-
Notifications
You must be signed in to change notification settings - Fork 12.8k
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
[mlir][spirv] Add a generic convert-to-spirv
pass
#95942
Conversation
@llvm/pr-subscribers-mlir-spirv @llvm/pr-subscribers-mlir Author: Angel Zhang (angelz913) ChangesThis PR implements a MVP version of an MLIR lowering pipeline to SPIR-V. The goal of adding this pipeline is to have a better test coverage of SPIR-V compilation upstream, and enable writing simple kernels by hand. The dialects supported in this version include Relevant links Future plans
Patch is 43.88 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/95942.diff 13 Files Affected:
diff --git a/mlir/include/mlir/Conversion/ConvertToSPIRV/ConvertToSPIRV.h b/mlir/include/mlir/Conversion/ConvertToSPIRV/ConvertToSPIRV.h
new file mode 100644
index 0000000000000..b539f5059b871
--- /dev/null
+++ b/mlir/include/mlir/Conversion/ConvertToSPIRV/ConvertToSPIRV.h
@@ -0,0 +1,26 @@
+//===- ConvertToSPIRV.h - Conversion to SPIR-V pass ---*- 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
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef MLIR_CONVERSION_CONVERTTOSPIRV_CONVERTTOSPIRV_H
+#define MLIR_CONVERSION_CONVERTTOSPIRV_CONVERTTOSPIRV_H
+
+#include <memory>
+
+#include "mlir/Pass/Pass.h"
+
+#define GEN_PASS_DECL_CONVERTTOSPIRVPASS
+#include "mlir/Conversion/Passes.h.inc"
+
+namespace mlir {
+
+/// Create a pass that performs dialect conversion to SPIR-V for all dialects
+std::unique_ptr<OperationPass<>> createConvertToSPIRVPass();
+
+} // namespace mlir
+
+#endif // MLIR_CONVERSION_CONVERTTOSPIRV_CONVERTTOSPIRV_H
diff --git a/mlir/include/mlir/Conversion/Passes.h b/mlir/include/mlir/Conversion/Passes.h
index 7700299b3a4f3..81daec6fd0138 100644
--- a/mlir/include/mlir/Conversion/Passes.h
+++ b/mlir/include/mlir/Conversion/Passes.h
@@ -30,6 +30,7 @@
#include "mlir/Conversion/ControlFlowToSPIRV/ControlFlowToSPIRV.h"
#include "mlir/Conversion/ControlFlowToSPIRV/ControlFlowToSPIRVPass.h"
#include "mlir/Conversion/ConvertToLLVM/ToLLVMPass.h"
+#include "mlir/Conversion/ConvertToSPIRV/ConvertToSPIRV.h"
#include "mlir/Conversion/FuncToEmitC/FuncToEmitCPass.h"
#include "mlir/Conversion/FuncToLLVM/ConvertFuncToLLVMPass.h"
#include "mlir/Conversion/FuncToSPIRV/FuncToSPIRVPass.h"
diff --git a/mlir/include/mlir/Conversion/Passes.td b/mlir/include/mlir/Conversion/Passes.td
index db67d6a5ff128..5747b8d38001d 100644
--- a/mlir/include/mlir/Conversion/Passes.td
+++ b/mlir/include/mlir/Conversion/Passes.td
@@ -11,7 +11,6 @@
include "mlir/Pass/PassBase.td"
-
//===----------------------------------------------------------------------===//
// ToLLVM
//===----------------------------------------------------------------------===//
@@ -31,6 +30,20 @@ def ConvertToLLVMPass : Pass<"convert-to-llvm"> {
];
}
+//===----------------------------------------------------------------------===//
+// ToSPIRV
+//===----------------------------------------------------------------------===//
+
+def ConvertToSPIRVPass : Pass<"convert-to-spirv"> {
+ let summary = "Convert to SPIR-V";
+ let description = [{
+ This is a generic pass to convert to SPIR-V.
+ }];
+
+ let constructor = "mlir::createConvertToSPIRVPass()";
+ let options = [];
+}
+
//===----------------------------------------------------------------------===//
// AffineToStandard
//===----------------------------------------------------------------------===//
diff --git a/mlir/lib/Conversion/CMakeLists.txt b/mlir/lib/Conversion/CMakeLists.txt
index 0a03a2e133db1..e107738a4c50c 100644
--- a/mlir/lib/Conversion/CMakeLists.txt
+++ b/mlir/lib/Conversion/CMakeLists.txt
@@ -19,6 +19,7 @@ add_subdirectory(ControlFlowToLLVM)
add_subdirectory(ControlFlowToSCF)
add_subdirectory(ControlFlowToSPIRV)
add_subdirectory(ConvertToLLVM)
+add_subdirectory(ConvertToSPIRV)
add_subdirectory(FuncToEmitC)
add_subdirectory(FuncToLLVM)
add_subdirectory(FuncToSPIRV)
diff --git a/mlir/lib/Conversion/ConvertToSPIRV/CMakeLists.txt b/mlir/lib/Conversion/ConvertToSPIRV/CMakeLists.txt
new file mode 100644
index 0000000000000..9a93301f09b48
--- /dev/null
+++ b/mlir/lib/Conversion/ConvertToSPIRV/CMakeLists.txt
@@ -0,0 +1,22 @@
+set(LLVM_OPTIONAL_SOURCES
+ ConvertToSPIRVPass.cpp
+)
+
+add_mlir_conversion_library(MLIRConvertToSPIRVPass
+ ConvertToSPIRVPass.cpp
+
+ ADDITIONAL_HEADER_DIRS
+ ${MLIR_MAIN_INCLUDE_DIR}/mlir/Conversion/ConvertToSPIRV
+
+ DEPENDS
+ MLIRConversionPassIncGen
+
+ LINK_LIBS PUBLIC
+ MLIRIR
+ MLIRPass
+ MLIRRewrite
+ MLIRSPIRVConversion
+ MLIRSPIRVDialect
+ MLIRSupport
+ MLIRTransformUtils
+ )
diff --git a/mlir/lib/Conversion/ConvertToSPIRV/ConvertToSPIRVPass.cpp b/mlir/lib/Conversion/ConvertToSPIRV/ConvertToSPIRVPass.cpp
new file mode 100644
index 0000000000000..b755e7d7ffe13
--- /dev/null
+++ b/mlir/lib/Conversion/ConvertToSPIRV/ConvertToSPIRVPass.cpp
@@ -0,0 +1,86 @@
+//===- ConvertToSPIRVPass.cpp - MLIR SPIR-V Conversion
+//--------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#include "mlir/Conversion/ArithToSPIRV/ArithToSPIRV.h"
+#include "mlir/Conversion/ConvertToSPIRV/ConvertToSPIRV.h"
+#include "mlir/Conversion/FuncToSPIRV/FuncToSPIRV.h"
+#include "mlir/Conversion/IndexToSPIRV/IndexToSPIRV.h"
+#include "mlir/Conversion/SCFToSPIRV/SCFToSPIRV.h"
+#include "mlir/Conversion/UBToSPIRV/UBToSPIRV.h"
+#include "mlir/Conversion/VectorToSPIRV/VectorToSPIRV.h"
+#include "mlir/Dialect/Arith/Transforms/Passes.h"
+#include "mlir/Dialect/SPIRV/IR/SPIRVDialect.h"
+#include "mlir/Dialect/SPIRV/Transforms/SPIRVConversion.h"
+#include "mlir/Dialect/Vector/Transforms/VectorRewritePatterns.h"
+#include "mlir/IR/PatternMatch.h"
+#include "mlir/Pass/Pass.h"
+#include "mlir/Rewrite/FrozenRewritePatternSet.h"
+#include "mlir/Transforms/DialectConversion.h"
+#include "mlir/Transforms/GreedyPatternRewriteDriver.h"
+#include <memory>
+
+#define DEBUG_TYPE "convert-to-spirv"
+
+namespace mlir {
+#define GEN_PASS_DEF_CONVERTTOSPIRVPASS
+#include "mlir/Conversion/Passes.h.inc"
+} // namespace mlir
+
+using namespace mlir;
+
+namespace {
+
+/// A pass to perform the SPIR-V conversion.
+class ConvertToSPIRVPass
+ : public impl::ConvertToSPIRVPassBase<ConvertToSPIRVPass> {
+
+public:
+ using impl::ConvertToSPIRVPassBase<
+ ConvertToSPIRVPass>::ConvertToSPIRVPassBase;
+
+ // Register dependent dialects for the current pass
+ void getDependentDialects(DialectRegistry ®istry) const override {
+ registry.insert<spirv::SPIRVDialect>();
+ }
+
+ void runOnOperation() final {
+ MLIRContext *context = &getContext();
+ Operation *op = getOperation();
+
+ auto targetAttr = spirv::lookupTargetEnvOrDefault(op);
+ SPIRVTypeConverter typeConverter(targetAttr);
+
+ RewritePatternSet patterns(context);
+ ScfToSPIRVContext scfToSPIRVContext;
+
+ // Populate patterns.
+ arith::populateCeilFloorDivExpandOpsPatterns(patterns);
+ arith::populateArithToSPIRVPatterns(typeConverter, patterns);
+ populateBuiltinFuncToSPIRVPatterns(typeConverter, patterns);
+ populateFuncToSPIRVPatterns(typeConverter, patterns);
+ index::populateIndexToSPIRVPatterns(typeConverter, patterns);
+ populateVectorToSPIRVPatterns(typeConverter, patterns);
+ populateSCFToSPIRVPatterns(typeConverter, scfToSPIRVContext, patterns);
+ ub::populateUBToSPIRVConversionPatterns(typeConverter, patterns);
+
+ std::unique_ptr<ConversionTarget> target =
+ SPIRVConversionTarget::get(targetAttr);
+
+ FrozenRewritePatternSet frozenPatterns(std::move(patterns));
+ if (failed(applyPartialConversion(op, *target, frozenPatterns))) {
+ return signalPassFailure();
+ }
+ }
+};
+
+} // namespace
+
+std::unique_ptr<OperationPass<>> mlir::createConvertToSPIRVPass() {
+ return std::make_unique<ConvertToSPIRVPass>();
+}
diff --git a/mlir/test/Conversion/ConvertToSPIRV/arith.mlir b/mlir/test/Conversion/ConvertToSPIRV/arith.mlir
new file mode 100644
index 0000000000000..823e9b4a6c3ab
--- /dev/null
+++ b/mlir/test/Conversion/ConvertToSPIRV/arith.mlir
@@ -0,0 +1,276 @@
+// RUN: mlir-opt -convert-to-spirv -split-input-file %s | FileCheck %s
+
+//===----------------------------------------------------------------------===//
+// arithmetic ops
+//===----------------------------------------------------------------------===//
+
+// CHECK-LABEL: @int32_scalar
+func.func @int32_scalar(%lhs: i32, %rhs: i32) {
+ // CHECK: spirv.IAdd %{{.*}}, %{{.*}}: i32
+ %0 = arith.addi %lhs, %rhs: i32
+ // CHECK: spirv.ISub %{{.*}}, %{{.*}}: i32
+ %1 = arith.subi %lhs, %rhs: i32
+ // CHECK: spirv.IMul %{{.*}}, %{{.*}}: i32
+ %2 = arith.muli %lhs, %rhs: i32
+ // CHECK: spirv.SDiv %{{.*}}, %{{.*}}: i32
+ %3 = arith.divsi %lhs, %rhs: i32
+ // CHECK: spirv.UDiv %{{.*}}, %{{.*}}: i32
+ %4 = arith.divui %lhs, %rhs: i32
+ // CHECK: spirv.UMod %{{.*}}, %{{.*}}: i32
+ %5 = arith.remui %lhs, %rhs: i32
+ return
+}
+
+// CHECK-LABEL: @int32_scalar_srem
+// CHECK-SAME: (%[[LHS:.+]]: i32, %[[RHS:.+]]: i32)
+func.func @int32_scalar_srem(%lhs: i32, %rhs: i32) {
+ // CHECK: %[[LABS:.+]] = spirv.GL.SAbs %[[LHS]] : i32
+ // CHECK: %[[RABS:.+]] = spirv.GL.SAbs %[[RHS]] : i32
+ // CHECK: %[[ABS:.+]] = spirv.UMod %[[LABS]], %[[RABS]] : i32
+ // CHECK: %[[POS:.+]] = spirv.IEqual %[[LHS]], %[[LABS]] : i32
+ // CHECK: %[[NEG:.+]] = spirv.SNegate %[[ABS]] : i32
+ // CHECK: %{{.+}} = spirv.Select %[[POS]], %[[ABS]], %[[NEG]] : i1, i32
+ %0 = arith.remsi %lhs, %rhs: i32
+ return
+}
+
+// -----
+
+//===----------------------------------------------------------------------===//
+// std bit ops
+//===----------------------------------------------------------------------===//
+
+// CHECK-LABEL: @bitwise_scalar
+func.func @bitwise_scalar(%arg0 : i32, %arg1 : i32) {
+ // CHECK: spirv.BitwiseAnd
+ %0 = arith.andi %arg0, %arg1 : i32
+ // CHECK: spirv.BitwiseOr
+ %1 = arith.ori %arg0, %arg1 : i32
+ // CHECK: spirv.BitwiseXor
+ %2 = arith.xori %arg0, %arg1 : i32
+ return
+}
+
+// CHECK-LABEL: @bitwise_vector
+func.func @bitwise_vector(%arg0 : vector<4xi32>, %arg1 : vector<4xi32>) {
+ // CHECK: spirv.BitwiseAnd
+ %0 = arith.andi %arg0, %arg1 : vector<4xi32>
+ // CHECK: spirv.BitwiseOr
+ %1 = arith.ori %arg0, %arg1 : vector<4xi32>
+ // CHECK: spirv.BitwiseXor
+ %2 = arith.xori %arg0, %arg1 : vector<4xi32>
+ return
+}
+
+// CHECK-LABEL: @logical_scalar
+func.func @logical_scalar(%arg0 : i1, %arg1 : i1) {
+ // CHECK: spirv.LogicalAnd
+ %0 = arith.andi %arg0, %arg1 : i1
+ // CHECK: spirv.LogicalOr
+ %1 = arith.ori %arg0, %arg1 : i1
+ // CHECK: spirv.LogicalNotEqual
+ %2 = arith.xori %arg0, %arg1 : i1
+ return
+}
+
+// CHECK-LABEL: @logical_vector
+func.func @logical_vector(%arg0 : vector<4xi1>, %arg1 : vector<4xi1>) {
+ // CHECK: spirv.LogicalAnd
+ %0 = arith.andi %arg0, %arg1 : vector<4xi1>
+ // CHECK: spirv.LogicalOr
+ %1 = arith.ori %arg0, %arg1 : vector<4xi1>
+ // CHECK: spirv.LogicalNotEqual
+ %2 = arith.xori %arg0, %arg1 : vector<4xi1>
+ return
+}
+
+// CHECK-LABEL: @shift_scalar
+func.func @shift_scalar(%arg0 : i32, %arg1 : i32) {
+ // CHECK: spirv.ShiftLeftLogical
+ %0 = arith.shli %arg0, %arg1 : i32
+ // CHECK: spirv.ShiftRightArithmetic
+ %1 = arith.shrsi %arg0, %arg1 : i32
+ // CHECK: spirv.ShiftRightLogical
+ %2 = arith.shrui %arg0, %arg1 : i32
+ return
+}
+
+// CHECK-LABEL: @shift_vector
+func.func @shift_vector(%arg0 : vector<4xi32>, %arg1 : vector<4xi32>) {
+ // CHECK: spirv.ShiftLeftLogical
+ %0 = arith.shli %arg0, %arg1 : vector<4xi32>
+ // CHECK: spirv.ShiftRightArithmetic
+ %1 = arith.shrsi %arg0, %arg1 : vector<4xi32>
+ // CHECK: spirv.ShiftRightLogical
+ %2 = arith.shrui %arg0, %arg1 : vector<4xi32>
+ return
+}
+
+// -----
+
+//===----------------------------------------------------------------------===//
+// arith.cmpf
+//===----------------------------------------------------------------------===//
+
+// CHECK-LABEL: @cmpf
+func.func @cmpf(%arg0 : f32, %arg1 : f32) {
+ // CHECK: spirv.FOrdEqual
+ %1 = arith.cmpf oeq, %arg0, %arg1 : f32
+ // CHECK: spirv.FOrdGreaterThan
+ %2 = arith.cmpf ogt, %arg0, %arg1 : f32
+ // CHECK: spirv.FOrdGreaterThanEqual
+ %3 = arith.cmpf oge, %arg0, %arg1 : f32
+ // CHECK: spirv.FOrdLessThan
+ %4 = arith.cmpf olt, %arg0, %arg1 : f32
+ // CHECK: spirv.FOrdLessThanEqual
+ %5 = arith.cmpf ole, %arg0, %arg1 : f32
+ // CHECK: spirv.FOrdNotEqual
+ %6 = arith.cmpf one, %arg0, %arg1 : f32
+ // CHECK: spirv.FUnordEqual
+ %7 = arith.cmpf ueq, %arg0, %arg1 : f32
+ // CHECK: spirv.FUnordGreaterThan
+ %8 = arith.cmpf ugt, %arg0, %arg1 : f32
+ // CHECK: spirv.FUnordGreaterThanEqual
+ %9 = arith.cmpf uge, %arg0, %arg1 : f32
+ // CHECK: spirv.FUnordLessThan
+ %10 = arith.cmpf ult, %arg0, %arg1 : f32
+ // CHECK: FUnordLessThanEqual
+ %11 = arith.cmpf ule, %arg0, %arg1 : f32
+ // CHECK: spirv.FUnordNotEqual
+ %12 = arith.cmpf une, %arg0, %arg1 : f32
+ return
+}
+
+// CHECK-LABEL: @vec1cmpf
+func.func @vec1cmpf(%arg0 : vector<1xf32>, %arg1 : vector<1xf32>) {
+ // CHECK: spirv.FOrdGreaterThan
+ %0 = arith.cmpf ogt, %arg0, %arg1 : vector<1xf32>
+ // CHECK: spirv.FUnordLessThan
+ %1 = arith.cmpf ult, %arg0, %arg1 : vector<1xf32>
+ return
+}
+
+// -----
+
+//===----------------------------------------------------------------------===//
+// arith.cmpi
+//===----------------------------------------------------------------------===//
+
+// CHECK-LABEL: @cmpi
+func.func @cmpi(%arg0 : i32, %arg1 : i32) {
+ // CHECK: spirv.IEqual
+ %0 = arith.cmpi eq, %arg0, %arg1 : i32
+ // CHECK: spirv.INotEqual
+ %1 = arith.cmpi ne, %arg0, %arg1 : i32
+ // CHECK: spirv.SLessThan
+ %2 = arith.cmpi slt, %arg0, %arg1 : i32
+ // CHECK: spirv.SLessThanEqual
+ %3 = arith.cmpi sle, %arg0, %arg1 : i32
+ // CHECK: spirv.SGreaterThan
+ %4 = arith.cmpi sgt, %arg0, %arg1 : i32
+ // CHECK: spirv.SGreaterThanEqual
+ %5 = arith.cmpi sge, %arg0, %arg1 : i32
+ // CHECK: spirv.ULessThan
+ %6 = arith.cmpi ult, %arg0, %arg1 : i32
+ // CHECK: spirv.ULessThanEqual
+ %7 = arith.cmpi ule, %arg0, %arg1 : i32
+ // CHECK: spirv.UGreaterThan
+ %8 = arith.cmpi ugt, %arg0, %arg1 : i32
+ // CHECK: spirv.UGreaterThanEqual
+ %9 = arith.cmpi uge, %arg0, %arg1 : i32
+ return
+}
+
+// CHECK-LABEL: @indexcmpi
+func.func @indexcmpi(%arg0 : index, %arg1 : index) {
+ // CHECK: spirv.IEqual
+ %0 = arith.cmpi eq, %arg0, %arg1 : index
+ // CHECK: spirv.INotEqual
+ %1 = arith.cmpi ne, %arg0, %arg1 : index
+ // CHECK: spirv.SLessThan
+ %2 = arith.cmpi slt, %arg0, %arg1 : index
+ // CHECK: spirv.SLessThanEqual
+ %3 = arith.cmpi sle, %arg0, %arg1 : index
+ // CHECK: spirv.SGreaterThan
+ %4 = arith.cmpi sgt, %arg0, %arg1 : index
+ // CHECK: spirv.SGreaterThanEqual
+ %5 = arith.cmpi sge, %arg0, %arg1 : index
+ // CHECK: spirv.ULessThan
+ %6 = arith.cmpi ult, %arg0, %arg1 : index
+ // CHECK: spirv.ULessThanEqual
+ %7 = arith.cmpi ule, %arg0, %arg1 : index
+ // CHECK: spirv.UGreaterThan
+ %8 = arith.cmpi ugt, %arg0, %arg1 : index
+ // CHECK: spirv.UGreaterThanEqual
+ %9 = arith.cmpi uge, %arg0, %arg1 : index
+ return
+}
+
+// CHECK-LABEL: @vec1cmpi
+func.func @vec1cmpi(%arg0 : vector<1xi32>, %arg1 : vector<1xi32>) {
+ // CHECK: spirv.ULessThan
+ %0 = arith.cmpi ult, %arg0, %arg1 : vector<1xi32>
+ // CHECK: spirv.SGreaterThan
+ %1 = arith.cmpi sgt, %arg0, %arg1 : vector<1xi32>
+ return
+}
+
+// CHECK-LABEL: @boolcmpi_equality
+func.func @boolcmpi_equality(%arg0 : i1, %arg1 : i1) {
+ // CHECK: spirv.LogicalEqual
+ %0 = arith.cmpi eq, %arg0, %arg1 : i1
+ // CHECK: spirv.LogicalNotEqual
+ %1 = arith.cmpi ne, %arg0, %arg1 : i1
+ return
+}
+
+// CHECK-LABEL: @boolcmpi_unsigned
+func.func @boolcmpi_unsigned(%arg0 : i1, %arg1 : i1) {
+ // CHECK-COUNT-2: spirv.Select
+ // CHECK: spirv.UGreaterThanEqual
+ %0 = arith.cmpi uge, %arg0, %arg1 : i1
+ // CHECK-COUNT-2: spirv.Select
+ // CHECK: spirv.ULessThan
+ %1 = arith.cmpi ult, %arg0, %arg1 : i1
+ return
+}
+
+// CHECK-LABEL: @vec1boolcmpi_equality
+func.func @vec1boolcmpi_equality(%arg0 : vector<1xi1>, %arg1 : vector<1xi1>) {
+ // CHECK: spirv.LogicalEqual
+ %0 = arith.cmpi eq, %arg0, %arg1 : vector<1xi1>
+ // CHECK: spirv.LogicalNotEqual
+ %1 = arith.cmpi ne, %arg0, %arg1 : vector<1xi1>
+ return
+}
+
+// CHECK-LABEL: @vec1boolcmpi_unsigned
+func.func @vec1boolcmpi_unsigned(%arg0 : vector<1xi1>, %arg1 : vector<1xi1>) {
+ // CHECK-COUNT-2: spirv.Select
+ // CHECK: spirv.UGreaterThanEqual
+ %0 = arith.cmpi uge, %arg0, %arg1 : vector<1xi1>
+ // CHECK-COUNT-2: spirv.Select
+ // CHECK: spirv.ULessThan
+ %1 = arith.cmpi ult, %arg0, %arg1 : vector<1xi1>
+ return
+}
+
+// CHECK-LABEL: @vecboolcmpi_equality
+func.func @vecboolcmpi_equality(%arg0 : vector<4xi1>, %arg1 : vector<4xi1>) {
+ // CHECK: spirv.LogicalEqual
+ %0 = arith.cmpi eq, %arg0, %arg1 : vector<4xi1>
+ // CHECK: spirv.LogicalNotEqual
+ %1 = arith.cmpi ne, %arg0, %arg1 : vector<4xi1>
+ return
+}
+
+// CHECK-LABEL: @vecboolcmpi_unsigned
+func.func @vecboolcmpi_unsigned(%arg0 : vector<3xi1>, %arg1 : vector<3xi1>) {
+ // CHECK-COUNT-2: spirv.Select
+ // CHECK: spirv.UGreaterThanEqual
+ %0 = arith.cmpi uge, %arg0, %arg1 : vector<3xi1>
+ // CHECK-COUNT-2: spirv.Select
+ // CHECK: spirv.ULessThan
+ %1 = arith.cmpi ult, %arg0, %arg1 : vector<3xi1>
+ return
+}
diff --git a/mlir/test/Conversion/ConvertToSPIRV/combined.mlir b/mlir/test/Conversion/ConvertToSPIRV/combined.mlir
new file mode 100644
index 0000000000000..9e908465cb142
--- /dev/null
+++ b/mlir/test/Conversion/ConvertToSPIRV/combined.mlir
@@ -0,0 +1,47 @@
+// RUN: mlir-opt -convert-to-spirv %s | FileCheck %s
+
+// CHECK-LABEL: @combined
+// CHECK: %[[C0_F32:.*]] = spirv.Constant 0.000000e+00 : f32
+// CHECK: %[[C1_F32:.*]] = spirv.Constant 1.000000e+00 : f32
+// CHECK: %[[C0_I32:.*]] = spirv.Constant 0 : i32
+// CHECK: %[[C4_I32:.*]] = spirv.Constant 4 : i32
+// CHECK: %[[C0_I32_0:.*]] = spirv.Constant 0 : i32
+// CHECK: %[[C4_I32_0:.*]] = spirv.Constant 4 : i32
+// CHECK: %[[C1_I32:.*]] = spirv.Constant 1 : i32
+// CHECK: %[[VEC:.*]] = spirv.Constant dense<1.000000e+00> : vector<4xf32>
+// CHECK: %[[VARIABLE:.*]] = spirv.Variable : !spirv.ptr<f32, Function>
+// CHECK: spirv.mlir.loop {
+// CHECK: spirv.Branch ^[[HEADER:.*]](%[[C0_I32_0]], %[[C0_F32]] : i32, f32)
+// CHECK: ^[[HEADER]](%[[INDVAR_0:.*]]: i32, %[[INDVAR_1:.*]]: f32):
+// CHECK: %[[SLESSTHAN:.*]] = spirv.SLessThan %[[INDVAR_0]], %[[C4_I32_0]] : i32
+// CHECK: spirv.BranchConditional %[[SLESSTHAN]], ^[[BODY:.*]], ^[[MERGE:.*]]
+// CHECK: ^[[BODY]]:
+// CHECK: %[[FADD:.*]] = spirv.FAdd %[[INDVAR_1]], %[[C1_F32]] : f32
+// CHECK: %[[INSERT:.*]] = spirv.CompositeInsert %[[FADD]], %[[VEC]][0 : i32] : f32 into vector<4xf32>
+// CHECK: spirv.Store "Function" %[[VARIABLE]], %[[FADD]] : f32
+// CHECK: %[[IADD:.*]] = spirv.IAdd %[[INDVAR_0]], %[[C1_I32]] : i32
+// CHECK: spirv.Branch ^[[HEADER]](%[[IADD]], %[[FADD]] : i32, f32)
+// CHECK: ^[[MERGE]]:
+// CHECK: spirv.mlir.merge
+// CHECK: }
+// CHECK: %[[LOAD:.*]] = spirv.Load "Function" %[[VARIABLE]] : f32
+// CHECK: %[[UNDEF:.*]] = spirv.Undef : f32
+// CHECK: spirv.ReturnValue %[[UNDEF]] : f32
+func.func @combined() -> f32 {
+ %c0_f32 = arith.constant 0.0 : f32
+ %c1_f32 = arith.constant 1.0 : f32
+ %c0_i32 = arith.constant 0 : i32
+ %c4_i32 = arith.constant 4 : i32
+ %lb = index.casts %c0_i32 : i32 to index
+ %ub = index.casts %c4_i32 : i32 to index
+ %step = arith.constant 1 : index
+ %buf = vector.broadcast %c1_f32 : f32 to vector<4xf32>
+ scf.for %iv = %lb to %ub step %step iter_args(%sum_iter = %c0_f32) -> f32 {
+ %t = vector.extract %buf[0] : f32 from vector<4xf32>
+ %sum_next = arith.addf %sum_iter, %t : f32
+ vector.insert %sum_next, %buf[0] : f32 into vector<4xf32>
+ scf.yield %sum_next : f32
+ }
+ %ret = ub.poison : f32
+ return %ret : f32
+}
diff --git a/mlir/test/Conversion/ConvertToSPIRV/index.mlir b/mlir/test/Conversion/ConvertToSPIRV/index.mlir
new file mode 100644
index 0000000000000..5ad2217add0d4
--- /dev/null
+++ b/mlir/test/Conversion/ConvertToSPIRV/index.mlir
@@ -0,0 +1,104 @@
+// RUN: mlir-opt %s -convert-to-spirv | FileCheck %s
+
+// CHECK-LABEL: @basic
+func.func @basic(%a: index, %b: index) {
+ // CHECK: spirv.IAdd
+ %0 = index.add %a, %b
+ // CHECK: spirv.ISub
+ %1 = index.sub %a, %b
+ // CHECK: spirv.IMul
+ %2 = index.mul %a, %b
+ // CHECK: spirv.SDiv
+ %3 = index.divs %a, %b
+ // CHECK: spirv.UDiv
+ %4 = index.divu %a, %b
+ // CHECK: spirv.SRem
+ %5 = index.rems %a, %b
+ // CHECK: spirv.UMod
+ %6 = index.remu %a, %b
+ // CHECK: spirv.GL.SMax
+ %7 = index.maxs %a, %b
+ // CHECK: spirv.GL.UMax
+ %8 = index.maxu...
[truncated]
|
convert-to-spirv
pass
464e201
to
6c19ecd
Compare
6d104c2
to
cc9eb3a
Compare
cc9eb3a
to
2b57c1b
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM but please wait for at least one more approval before merging.
This commit implements a MVP version of an MLIR lowering pipeline to SPIR-V. The goal is to have a better test coverage of SPIR-V compilation upstream, and enable writing simple kernels by hand. The dialects supported in this version include arith, vector (only 1-D vectors with size 2,3,4,8 or 16), scf, ub, index, func and math.
2b57c1b
to
d34055f
Compare
LLVM Buildbot has detected a new failure on builder Full details are available at: https://lab.llvm.org/buildbot/#/builders/89/builds/547 Here is the relevant piece of the build log for the reference:
|
LLVM Buildbot has detected a new failure on builder Full details are available at: https://lab.llvm.org/buildbot/#/builders/138/builds/309 Here is the relevant piece of the build log for the reference:
|
…)" This reverts commit 6a69cfb.
This PR implements a MVP version of an MLIR lowering pipeline to SPIR-V. The goal of adding this pipeline is to have a better test coverage of SPIR-V compilation upstream, and enable writing simple kernels by hand. The dialects supported in this version include `arith`, `vector` (only 1-D vectors with size 2,3,4,8 or 16), `scf`, `ub`, `index`, `func` and `math`. New test cases for the pass are also included in this PR. **Relevant links** - [Open MLIR Meeting - YouTube Video](https://www.youtube.com/watch?v=csWPOQfgLMo) - [Discussion on LLVM Forum](https://discourse.llvm.org/t/open-mlir-meeting-12-14-2023-discussion-on-improving-handling-of-unit-dimensions-in-the-vector-dialect/75683) **Future plans** - Add conversion patterns for other dialects, e.g. `gpu`, `tensor`, etc. - Include vector transformation to unroll vectors to 1-D, and handle those with unsupported sizes. - Implement multiple-return. SPIR-V does not support multiple return values since a `spirv.func` can only return zero or one values. It might be possible to wrap the return values in a `spirv.struct`. - Add a conversion for `scf.parallel`.
This PR implements a MVP version of an MLIR lowering pipeline to SPIR-V. The goal of adding this pipeline is to have a better test coverage of SPIR-V compilation upstream, and enable writing simple kernels by hand. The dialects supported in this version include `arith`, `vector` (only 1-D vectors with size 2,3,4,8 or 16), `scf`, `ub`, `index`, `func` and `math`. New test cases for the pass are also included in this PR. **Relevant links** - [Open MLIR Meeting - YouTube Video](https://www.youtube.com/watch?v=csWPOQfgLMo) - [Discussion on LLVM Forum](https://discourse.llvm.org/t/open-mlir-meeting-12-14-2023-discussion-on-improving-handling-of-unit-dimensions-in-the-vector-dialect/75683) **Future plans** - Add conversion patterns for other dialects, e.g. `gpu`, `tensor`, etc. - Include vector transformation to unroll vectors to 1-D, and handle those with unsupported sizes. - Implement multiple-return. SPIR-V does not support multiple return values since a `spirv.func` can only return zero or one values. It might be possible to wrap the return values in a `spirv.struct`. - Add a conversion for `scf.parallel`.
) Reverts llvm#95942 due to link failures.
Original change was reverted Reverts llvm#96334
This PR relands llvm#95942, which was reverted in llvm#96332 due to link failures. It fixes the issue by updating CMake dependencies. The bazel support, originally introduced in llvm#96334, is also included in this PR. --------- Co-authored-by: Keith Smiley <keithbsmiley@gmail.com>
This PR adds conversion patterns for MemRef to the `convert-to-spirv` pass, introduced in #95942. Conversions from MemRef memory space to SPIR-V storage class were also included, and would run before the final dialect conversion phase. **Future Plans** - Add tests for ops other than `memref.load` and `memref.store` --------- Co-authored-by: Jakub Kuderski <kubakuderski@gmail.com>
This PR adds conversion patterns for GPU to the `convert-to-spirv` pass, introduced in #95942. Now the pass is able to convert each `gpu.module` and its ops within a `builtin.module` into a `spirv.module`. **Future Plans** - Use `gpu.launch_func` to invoke kernel from host functions - Potentially integrate into the `mlir-vulkan-runner` for e2e testing
def ConvertToSPIRVPass : Pass<"convert-to-spirv"> { | ||
let summary = "Convert to SPIR-V"; | ||
let description = [{ | ||
This is a generic pass to convert to SPIR-V. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This claims to be a "generic" pass but it seems to me instead to be a "monolithic" pass.
Why didn't we align this on the convert-to-llvm
pass design instead?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@angelz913 talked about this in https://youtu.be/-qoMMrlYvGs?t=436 (starts around the 7m 15s mark). The TL;DR is we didn't know what interfaces to have, and decided to start with a monolithic multi-stage v0 implementation, with the plan to add make it interface-based when gain confidence in this design.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The TL;DR is we didn't know what interfaces to have
I don't quite understand? Why isn't it just copy the convert-to-llvm one and rename it?
I have strong concerns with in-tree monolithic passes like this: what is the timeline to remove this and migrate to a pluggable one?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree that in-tree monolithic passes arent great, but the change to use interfaces and go to more LLVM based approach is more involved (I dont think it is as simple as "copy the convert-to-llvm and rename it". Angel and Jakub can fill in more details here). I think timeline will depend on how much community involvement we get here. Jakub and Angel are trying to get upstream support flushed out more. So this is a strict improvement anyway. Its probably better to iterate on this in a bit.
FWIW, for conversion to LLVM in IREE we just throw all the conversion patterns into one pass and run them together. I personally find the split of conversion from each dialect to LLVM kind of artificial. Everything needs to be translated to LLVM. Running multiple passes that walk the IR multiple times seems like a waste. But that is a downstream decision and not having monoliths in upstream MLIR is useful.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think timeline will depend on how much community involvement we get here.
In this case I would want to see this pass moved to the test folder: I'm not comfortable with monolithic passes alongside the rest of the transformations right now.
FWIW, for conversion to LLVM in IREE we just throw all the conversion patterns into one pass and run them together. I personally find the split of conversion from each dialect to LLVM kind of artificial
I suspect you missed the intent of upstream design: these individual passes are all made that way upstream for testing, the intention has always been that downstream projects create a monolithic pass for their own purpose and don't use the upstream passes as-is (which is why the populatePatterns method are exposed): that's exactly "work as intended".
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
but the dependency on the spirv conversion code will have to move over there in some form.
Why would it have to be moved to the runner? The current pattern is that the transformations are made with mlir-opt
ahead of invoking the runner (hence why we ended up with a "pure" mlir-cpu-runner
).
So we can drop the runner, but it will just make the situation worse).
I think we're talking about different things maybe?
You seem to argue about the need for running some of these tests, while I'm just describing the system architecture of the project, which does not impact in any way which tests we're running.
That is, from a very high-level, the specific runner tool goes away and you do instead something like mlir-opt -pass-pipeline="builtin.module(convert-to-spirv)" %s | mlir-cpu-runner --shared-libs=mlir_vulkan_runtime.so
.
(this is a transition we made for the mlir-cuda-runner
~3 years ago, and the Vulkan runner has been a TODO ever since IIUC)
Note that in this model, the test pass is available for the opt tool, the runner does not need to know about the test passes (or any pass).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the clarification, Mehdi.
You seem to argue about the need for running some of these tests, while I'm just describing the system architecture of the project, which does not impact in any way which tests we're running.
Right, that's a good way to put it. From my point of view, what I mostly care about is that we do keep these tests (as in, both the implementation of convert-to-spirv and the e2e .mlir tests) while the cleanup in this area (both gpu-to-spirv and the runner implementation). I think we all aggrege on the end state, so I just want to make sure that we can find a path that allows us to make incremental improvements to get there.
From your perspective, would it be an option to make this a test pass and temporarily have it registered it in the vulkan runner (similar to how they are manually listed in mlir-opt.cpp
)? Maybe that's a middle ground, although I'm not sure of how much difference to a pass being a test or not there practically is for other users of mlir.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
From your perspective, would it be an option to make this a test pass and temporarily have it registered it in the vulkan runner (similar to how they are manually listed in mlir-opt.cpp)?
The problem is that we optionally compile the test passes I think? (With -DMLIR_INCLUDE_TESTS=ON
).
It may be easier to just keep the pass as-is until we migrate tests from mlir-vulkan-runner to mlir-cpu-runner?
The only thing that makes be nervous is that unless there is a timeline with people making progress on it, we'll still be adding mlir-vulkan-runner based tests multiple years from now. What I'm trying to see is some sort of a "gradient" on the progression of all this (and some timeline), rather than a quick immediate solution.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
OK, let me check internally if we can commit to something more concrete along this axis. Overall, I think we did make a lot of recent progress on the spirv conversion test coverage and general cleanup in this area, but I understand why you'd want to prioritize the runner cleanup next.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@joker-eph I checked and we will have @andfau-amd work on the mlir runner migration, starting from ~next week.
#106426) This PR adds an integration test for an argmax kernel with `mlir-vulkan-runner`. This test exercises the `convert-to-spirv` pass (landed in #95942) and demonstrates that we can use SPIR-V ops as "intrinsics" among higher-level dialects. The support for `index` dialect in `mlir-vulkan-runner` is also added.
This PR implements a MVP version of an MLIR lowering pipeline to SPIR-V. The goal of adding this pipeline is to have a better test coverage of SPIR-V compilation upstream, and enable writing simple kernels by hand. The dialects supported in this version include
arith
,vector
(only 1-D vectors with size 2,3,4,8 or 16),scf
,ub
,index
,func
andmath
. New test cases for the pass are also included in this PR.Relevant links
Future plans
gpu
,tensor
, etc.spirv.func
can only return zero or one values. It might be possible to wrap the return values in aspirv.struct
.scf.parallel
.