From d21ba951de62baf463097bfbe3fbba9dea9cf91a Mon Sep 17 00:00:00 2001 From: Denis Khalikov Date: Tue, 8 Oct 2019 16:42:38 -0700 Subject: [PATCH] [spirv] Add a pass to decorate the composite types with layout info. Add a pass to decorate the composite types used by composite objects in the StorageBuffer, PhysicalStorageBuffer, Uniform, and PushConstant storage classes with layout information. Closes tensorflow/mlir#156 COPYBARA_INTEGRATE_REVIEW=https://github.com/tensorflow/mlir/pull/156 from denis0x0D:sandbox/layout_info_decoration 7c50840fd38ca169a2da7ce9886b52b50c868b84 PiperOrigin-RevId: 273634140 --- mlir/include/mlir/Dialect/SPIRV/Passes.h | 9 + mlir/include/mlir/Dialect/SPIRV/SPIRVDialect.h | 3 + .../mlir/Dialect/SPIRV/SPIRVStructureOps.td | 8 + mlir/lib/Dialect/SPIRV/CMakeLists.txt | 1 + mlir/lib/Dialect/SPIRV/SPIRVDialect.cpp | 7 +- mlir/lib/Dialect/SPIRV/Transforms/CMakeLists.txt | 8 + .../DecorateSPIRVCompositeTypeLayoutPass.cpp | 321 +++++++++++++++++++++ .../SPIRV/Transforms/layout-decoration.mlir | 99 +++++++ mlir/tools/mlir-opt/CMakeLists.txt | 1 + 9 files changed, 454 insertions(+), 3 deletions(-) create mode 100644 mlir/lib/Dialect/SPIRV/Transforms/CMakeLists.txt create mode 100644 mlir/lib/Dialect/SPIRV/Transforms/DecorateSPIRVCompositeTypeLayoutPass.cpp create mode 100644 mlir/test/Dialect/SPIRV/Transforms/layout-decoration.mlir diff --git a/mlir/include/mlir/Dialect/SPIRV/Passes.h b/mlir/include/mlir/Dialect/SPIRV/Passes.h index ce4c19b..4969935 100644 --- a/mlir/include/mlir/Dialect/SPIRV/Passes.h +++ b/mlir/include/mlir/Dialect/SPIRV/Passes.h @@ -27,8 +27,17 @@ namespace mlir { namespace spirv { +// Creates a module pass that converts standard ops to SPIR-V ops. std::unique_ptr> createConvertStandardToSPIRVPass(); +// Creates a module pass that converts composite types used by objects in the +// StorageBuffer, PhysicalStorageBuffer, Uniform, and PushConstant storage +// classes with layout information. +// +// Right now this pass only supports Vulkan layout rules. +std::unique_ptr> +createDecorateSPIRVCompositeTypeLayoutPass(); + } // namespace spirv } // namespace mlir diff --git a/mlir/include/mlir/Dialect/SPIRV/SPIRVDialect.h b/mlir/include/mlir/Dialect/SPIRV/SPIRVDialect.h index a2e6981..8e98270 100644 --- a/mlir/include/mlir/Dialect/SPIRV/SPIRVDialect.h +++ b/mlir/include/mlir/Dialect/SPIRV/SPIRVDialect.h @@ -38,6 +38,9 @@ public: /// Checks if the given `type` is valid in SPIR-V dialect. static bool isValidType(Type type); + /// Checks if the given `scalar type` is valid in SPIR-V dialect. + static bool isValidScalarType(Type type); + /// Returns the attribute name to use when specifying decorations on results /// of operations. static std::string getAttributeName(Decoration decoration); diff --git a/mlir/include/mlir/Dialect/SPIRV/SPIRVStructureOps.td b/mlir/include/mlir/Dialect/SPIRV/SPIRVStructureOps.td index ba53cf2..e54ea5f 100644 --- a/mlir/include/mlir/Dialect/SPIRV/SPIRVStructureOps.td +++ b/mlir/include/mlir/Dialect/SPIRV/SPIRVStructureOps.td @@ -242,6 +242,14 @@ def SPV_GlobalVariableOp : SPV_Op<"globalVariable", [InModuleScope]> { OptionalAttr:$initializer ); + let builders = [ + OpBuilder<"Builder *builder, OperationState &state, " + "TypeAttr type, ArrayRef namedAttrs", [{ + state.addAttribute("type", type); + state.addAttributes(namedAttrs); + }]> + ]; + let results = (outs); let hasOpcode = 0; diff --git a/mlir/lib/Dialect/SPIRV/CMakeLists.txt b/mlir/lib/Dialect/SPIRV/CMakeLists.txt index 05f09d2..04bdc73 100644 --- a/mlir/lib/Dialect/SPIRV/CMakeLists.txt +++ b/mlir/lib/Dialect/SPIRV/CMakeLists.txt @@ -20,3 +20,4 @@ target_link_libraries(MLIRSPIRV MLIRSupport) add_subdirectory(Serialization) +add_subdirectory(Transforms) diff --git a/mlir/lib/Dialect/SPIRV/SPIRVDialect.cpp b/mlir/lib/Dialect/SPIRV/SPIRVDialect.cpp index 86585c2..af50c8e 100644 --- a/mlir/lib/Dialect/SPIRV/SPIRVDialect.cpp +++ b/mlir/lib/Dialect/SPIRV/SPIRVDialect.cpp @@ -96,7 +96,7 @@ static bool isValidSPIRVIntType(IntegerType type) { type.getWidth()); } -static bool isValidSPIRVScalarType(Type type) { +bool SPIRVDialect::isValidScalarType(Type type) { if (type.isa()) { return !type.isBF16(); } @@ -107,7 +107,8 @@ static bool isValidSPIRVScalarType(Type type) { } static bool isValidSPIRVVectorType(VectorType type) { - return type.getRank() == 1 && isValidSPIRVScalarType(type.getElementType()) && + return type.getRank() == 1 && + SPIRVDialect::isValidScalarType(type.getElementType()) && type.getNumElements() >= 2 && type.getNumElements() <= 4; } @@ -117,7 +118,7 @@ bool SPIRVDialect::isValidType(Type type) { type.getKind() <= TypeKind::LAST_SPIRV_TYPE) { return true; } - if (isValidSPIRVScalarType(type)) { + if (SPIRVDialect::isValidScalarType(type)) { return true; } if (auto vectorType = type.dyn_cast()) { diff --git a/mlir/lib/Dialect/SPIRV/Transforms/CMakeLists.txt b/mlir/lib/Dialect/SPIRV/Transforms/CMakeLists.txt new file mode 100644 index 0000000..42be4f8 --- /dev/null +++ b/mlir/lib/Dialect/SPIRV/Transforms/CMakeLists.txt @@ -0,0 +1,8 @@ +add_llvm_library(MLIRSPIRVTransforms + DecorateSPIRVCompositeTypeLayoutPass.cpp + ) + +target_link_libraries(MLIRSPIRVTransforms + MLIRPass + MLIRSPIRV + ) diff --git a/mlir/lib/Dialect/SPIRV/Transforms/DecorateSPIRVCompositeTypeLayoutPass.cpp b/mlir/lib/Dialect/SPIRV/Transforms/DecorateSPIRVCompositeTypeLayoutPass.cpp new file mode 100644 index 0000000..d2693f2 --- /dev/null +++ b/mlir/lib/Dialect/SPIRV/Transforms/DecorateSPIRVCompositeTypeLayoutPass.cpp @@ -0,0 +1,321 @@ +//===- DecorateSPIRVCompositeTypeLayoutPass.cpp - Decorate composite type -===// +// +// Copyright 2019 The MLIR Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// ============================================================================= +// +// This file implements a pass to decorate the composite types used by +// composite objects in the StorageBuffer, PhysicalStorageBuffer, Uniform, and +// PushConstant storage classes with layout information. See SPIR-V spec +// "2.16.2. Validation Rules for Shader Capabilities" for more details. +// +//===----------------------------------------------------------------------===// + +#include "mlir/Dialect/SPIRV/Passes.h" +#include "mlir/Dialect/SPIRV/SPIRVDialect.h" +#include "mlir/Dialect/SPIRV/SPIRVOps.h" +#include "mlir/Transforms/DialectConversion.h" + +using namespace mlir; + +/// According to the Vulkan spec "14.5.4. Offset and Stride Assignment": +/// "There are different alignment requirements depending on the specific +/// resources and on the features enabled on the device." +/// +/// There are 3 types of alignment: scalar, base, extended. +/// See the spec for details. +/// +/// Note: Even if scalar alignment is supported, it is generally more +/// performant to use the base alignment. So here the calculation is based on +/// base alignment. +/// +/// The memory layout must obey the following rules: +/// 1. The Offset decoration of any member must be a multiple of its alignment. +/// 2. Any ArrayStride or MatrixStride decoration must be a multiple of the +/// alignment of the array or matrix as defined above. +/// +/// According to the SPIR-V spec: +/// "The ArrayStride, MatrixStride, and Offset decorations must be large +/// enough to hold the size of the objects they affect (that is, specifying +/// overlap is invalid)." +namespace { +class VulkanLayoutUtils { +public: + using Alignment = uint64_t; + + /// Returns a new type with layout info. Assigns the type size in bytes to the + /// `size`. Assigns the type alignment in bytes to the `alignment`. + static Type decorateType(spirv::StructType structType, + spirv::StructType::LayoutInfo &size, + Alignment &alignment); + /// Checks whether a type is legal in terms of Vulkan layout info + /// decoration. A type is dynamically illegal if it's a composite type in the + /// StorageBuffer, PhysicalStorageBuffer, Uniform, and PushConstant Storage + /// Classes without layout informtation. + static bool isLegalType(Type type); + +private: + static Type decorateType(Type type, spirv::StructType::LayoutInfo &size, + Alignment &alignment); + static Type decorateType(VectorType vectorType, + spirv::StructType::LayoutInfo &size, + Alignment &alignment); + static Type decorateType(spirv::ArrayType arrayType, + spirv::StructType::LayoutInfo &size, + Alignment &alignment); + /// Calculates the alignment for the given scalar type. + static Alignment getScalarTypeAlignment(Type scalarType); +}; + +Type VulkanLayoutUtils::decorateType(spirv::StructType structType, + spirv::StructType::LayoutInfo &size, + VulkanLayoutUtils::Alignment &alignment) { + if (structType.getNumElements() == 0) { + return structType; + } + + llvm::SmallVector memberTypes; + llvm::SmallVector layoutInfo; + llvm::SmallVector + memberDecorations; + + spirv::StructType::LayoutInfo structMemberOffset = 0; + VulkanLayoutUtils::Alignment maxMemberAlignment = 1; + + for (uint32_t i = 0, e = structType.getNumElements(); i < e; ++i) { + spirv::StructType::LayoutInfo memberSize = 0; + VulkanLayoutUtils::Alignment memberAlignment = 1; + + auto memberType = VulkanLayoutUtils::decorateType( + structType.getElementType(i), memberSize, memberAlignment); + structMemberOffset = llvm::alignTo(structMemberOffset, memberAlignment); + memberTypes.push_back(memberType); + layoutInfo.push_back(structMemberOffset); + // According to the Vulkan spec: + // "A structure has a base alignment equal to the largest base alignment of + // any of its members." + structMemberOffset += memberSize; + maxMemberAlignment = std::max(maxMemberAlignment, memberAlignment); + } + + // According to the Vulkan spec: + // "The Offset decoration of a member must not place it between the end of a + // structure or an array and the next multiple of the alignment of that + // structure or array." + size = llvm::alignTo(structMemberOffset, maxMemberAlignment); + alignment = maxMemberAlignment; + structType.getMemberDecorations(memberDecorations); + return spirv::StructType::get(memberTypes, layoutInfo, memberDecorations); +} + +Type VulkanLayoutUtils::decorateType(Type type, + spirv::StructType::LayoutInfo &size, + VulkanLayoutUtils::Alignment &alignment) { + if (spirv::SPIRVDialect::isValidScalarType(type)) { + alignment = VulkanLayoutUtils::getScalarTypeAlignment(type); + // Vulkan spec does not specify any padding for a scalar type. + size = alignment; + return type; + } + + switch (type.getKind()) { + case spirv::TypeKind::Struct: + return VulkanLayoutUtils::decorateType(type.cast(), size, + alignment); + case spirv::TypeKind::Array: + return VulkanLayoutUtils::decorateType(type.cast(), size, + alignment); + case StandardTypes::Vector: + return VulkanLayoutUtils::decorateType(type.cast(), size, + alignment); + default: + llvm_unreachable("unhandled SPIR-V type"); + } +} + +Type VulkanLayoutUtils::decorateType(VectorType vectorType, + spirv::StructType::LayoutInfo &size, + VulkanLayoutUtils::Alignment &alignment) { + const auto numElements = vectorType.getNumElements(); + auto elementType = vectorType.getElementType(); + spirv::StructType::LayoutInfo elementSize = 0; + VulkanLayoutUtils::Alignment elementAlignment = 1; + + auto memberType = VulkanLayoutUtils::decorateType(elementType, elementSize, + elementAlignment); + // According to the Vulkan spec: + // 1. "A two-component vector has a base alignment equal to twice its scalar + // alignment." + // 2. "A three- or four-component vector has a base alignment equal to four + // times its scalar alignment." + size = elementSize * numElements; + alignment = numElements == 2 ? elementAlignment * 2 : elementAlignment * 4; + return VectorType::get(numElements, memberType); +} + +Type VulkanLayoutUtils::decorateType(spirv::ArrayType arrayType, + spirv::StructType::LayoutInfo &size, + VulkanLayoutUtils::Alignment &alignment) { + const auto numElements = arrayType.getNumElements(); + auto elementType = arrayType.getElementType(); + spirv::ArrayType::LayoutInfo elementSize = 0; + VulkanLayoutUtils::Alignment elementAlignment = 1; + + auto memberType = VulkanLayoutUtils::decorateType(elementType, elementSize, + elementAlignment); + // According to the Vulkan spec: + // "An array has a base alignment equal to the base alignment of its element + // type." + size = elementSize * numElements; + alignment = elementAlignment; + return spirv::ArrayType::get(memberType, numElements, elementSize); +} + +VulkanLayoutUtils::Alignment +VulkanLayoutUtils::getScalarTypeAlignment(Type scalarType) { + // According to the Vulkan spec: + // 1. "A scalar of size N has a scalar alignment of N." + // 2. "A scalar has a base alignment equal to its scalar alignment." + // 3. "A scalar, vector or matrix type has an extended alignment equal to its + // base alignment." + auto bitWidth = scalarType.getIntOrFloatBitWidth(); + if (bitWidth == 1) + return 1; + return bitWidth / 8; +} + +bool VulkanLayoutUtils::isLegalType(Type type) { + auto ptrType = type.dyn_cast(); + if (!ptrType) { + return true; + } + + auto storageClass = ptrType.getStorageClass(); + auto structType = ptrType.getPointeeType().dyn_cast(); + if (!structType) { + return true; + } + + switch (storageClass) { + case spirv::StorageClass::Uniform: + case spirv::StorageClass::StorageBuffer: + case spirv::StorageClass::PushConstant: + case spirv::StorageClass::PhysicalStorageBuffer: + return structType.hasLayout() || !structType.getNumElements(); + default: + return true; + } +} +} // namespace + +namespace { +class SPIRVGlobalVariableOpLayoutInfoDecoration + : public OpRewritePattern { +public: + using OpRewritePattern::OpRewritePattern; + + PatternMatchResult matchAndRewrite(spirv::GlobalVariableOp op, + PatternRewriter &rewriter) const override { + spirv::StructType::LayoutInfo structSize = 0; + VulkanLayoutUtils::Alignment structAlignment = 1; + SmallVector globalVarAttrs; + + auto ptrType = op.type().cast(); + auto structType = VulkanLayoutUtils::decorateType( + ptrType.getPointeeType().cast(), structSize, + structAlignment); + auto decoratedType = + spirv::PointerType::get(structType, ptrType.getStorageClass()); + + // Save all named attributes except "type" attribute. + for (const auto &attr : op.getAttrs()) { + if (attr.first == "type") { + continue; + } + globalVarAttrs.push_back(attr); + } + + rewriter.replaceOpWithNewOp( + op, rewriter.getTypeAttr(decoratedType), globalVarAttrs); + return matchSuccess(); + } +}; + +class SPIRVAddressOfOpLayoutInfoDecoration + : public OpRewritePattern { +public: + using OpRewritePattern::OpRewritePattern; + + PatternMatchResult matchAndRewrite(spirv::AddressOfOp op, + PatternRewriter &rewriter) const override { + auto spirvModule = op.getParentOfType(); + auto varName = op.variable(); + auto varOp = spirvModule.lookupSymbol(varName); + + rewriter.replaceOpWithNewOp( + op, varOp.type(), rewriter.getSymbolRefAttr(varName)); + return matchSuccess(); + } +}; +} // namespace + +static void populateSPIRVLayoutInfoPatterns(OwningRewritePatternList &patterns, + MLIRContext *ctx) { + patterns.insert(ctx); +} + +namespace { +class DecorateSPIRVCompositeTypeLayoutPass + : public ModulePass { +private: + void runOnModule() override; +}; + +void DecorateSPIRVCompositeTypeLayoutPass::runOnModule() { + auto module = getModule(); + OwningRewritePatternList patterns; + populateSPIRVLayoutInfoPatterns(patterns, module.getContext()); + ConversionTarget target(*(module.getContext())); + target.addLegalDialect(); + target.addLegalOp(); + target.addDynamicallyLegalOp( + [](spirv::GlobalVariableOp op) { + return VulkanLayoutUtils::isLegalType(op.type()); + }); + + // Change the type for the direct users. + target.addDynamicallyLegalOp([](spirv::AddressOfOp op) { + return VulkanLayoutUtils::isLegalType(op.pointer()->getType()); + }); + + // TODO: Change the type for the indirect users such as spv.Load, spv.Store, + // spv.FunctionCall and so on. + + for (auto spirvModule : module.getOps()) { + if (failed(applyFullConversion(spirvModule, target, patterns))) { + signalPassFailure(); + } + } +} +} // namespace + +std::unique_ptr> +mlir::spirv::createDecorateSPIRVCompositeTypeLayoutPass() { + return std::make_unique(); +} + +static PassRegistration + pass("decorate-spirv-composite-type-layout", + "Decorate SPIR-V composite type with layout info"); diff --git a/mlir/test/Dialect/SPIRV/Transforms/layout-decoration.mlir b/mlir/test/Dialect/SPIRV/Transforms/layout-decoration.mlir new file mode 100644 index 0000000..0a019b1 --- /dev/null +++ b/mlir/test/Dialect/SPIRV/Transforms/layout-decoration.mlir @@ -0,0 +1,99 @@ +// RUN: mlir-opt -decorate-spirv-composite-type-layout -split-input-file -verify-diagnostics %s -o - | FileCheck %s + +spv.module "Logical" "GLSL450" { + // CHECK: spv.globalVariable @var0 bind(0, 1) : !spv.ptr [4], f32 [12]>, Uniform> + spv.globalVariable @var0 bind(0,1) : !spv.ptr, f32>, Uniform> + + // CHECK: spv.globalVariable @var1 bind(0, 2) : !spv.ptr [0], f32 [256]>, StorageBuffer> + spv.globalVariable @var1 bind(0,2) : !spv.ptr, f32>, StorageBuffer> + + // CHECK: spv.globalVariable @var2 bind(1, 0) : !spv.ptr [0], f32 [256]> [0], i32 [260]>, StorageBuffer> + spv.globalVariable @var2 bind(1,0) : !spv.ptr, f32>, i32>, StorageBuffer> + + // CHECK: spv.globalVariable @var3 : !spv.ptr [8]> [72]> [0], f32 [1152]>, StorageBuffer> + spv.globalVariable @var3 : !spv.ptr>>, f32>, StorageBuffer> + + // CHECK: spv.globalVariable @var4 bind(1, 2) : !spv.ptr [0], f32 [16], i1 [20]> [0], i1 [24]>, StorageBuffer> + spv.globalVariable @var4 bind(1,2) : !spv.ptr, f32, i1>, i1>, StorageBuffer> + + // CHECK: spv.globalVariable @var5 bind(1, 3) : !spv.ptr [0]>, StorageBuffer> + spv.globalVariable @var5 bind(1,3) : !spv.ptr>, StorageBuffer> + + func @kernel() -> () { + %c0 = spv.constant 0 : i32 + // CHECK: {{%.*}} = spv._address_of @var0 : !spv.ptr [4], f32 [12]>, Uniform> + %0 = spv._address_of @var0 : !spv.ptr, f32>, Uniform> + // CHECK: {{%.*}} = spv.AccessChain {{%.*}}[{{%.*}}] : !spv.ptr [4], f32 [12]>, Uniform> + %1 = spv.AccessChain %0[%c0] : !spv.ptr, f32>, Uniform> + spv.Return + } +} + +// ----- + +spv.module "Logical" "GLSL450" { + // CHECK: spv.globalVariable @var0 : !spv.ptr [0], i1 [16]> [0], i1 [24]> [0], i1 [32]> [0], i1 [40]>, Uniform> + spv.globalVariable @var0 : !spv.ptr, i1>, i1>, i1>, i1>, Uniform> + + // CHECK: spv.globalVariable @var1 : !spv.ptr [8], f32 [24]> [0], f32 [32]>, Uniform> + spv.globalVariable @var1 : !spv.ptr, f32>, f32>, Uniform> + + // CHECK: spv.globalVariable @var2 : !spv.ptr [128]> [8]> [8], f32 [2064]> [0], f32 [2072]>, Uniform> + spv.globalVariable @var2 : !spv.ptr>>, f32>, f32>, Uniform> + + // CHECK: spv.globalVariable @var3 : !spv.ptr [0], i1 [512]> [0], i1 [520]>, Uniform> + spv.globalVariable @var3 : !spv.ptr, i1>, i1>, Uniform> + + // CHECK: spv.globalVariable @var4 : !spv.ptr [8], i1 [24]>, Uniform> + spv.globalVariable @var4 : !spv.ptr, i1>, Uniform> + + // CHECK: spv.globalVariable @var5 : !spv.ptr [8], i1 [24]>, Uniform> + spv.globalVariable @var5 : !spv.ptr, i1>, Uniform> + + // CHECK: spv.globalVariable @var6 : !spv.ptr [8], i1 [24]>, Uniform> + spv.globalVariable @var6 : !spv.ptr, i1>, Uniform> + + // CHECK: spv.globalVariable @var7 : !spv.ptr [0], i1 [16]> [8], i1 [32]>, Uniform> + spv.globalVariable @var7 : !spv.ptr, i1>, i1>, Uniform> +} + +// ----- + +spv.module "Logical" "GLSL450" { + // CHECK: spv.globalVariable @var0 : !spv.ptr [0], f32 [8]>, StorageBuffer> + spv.globalVariable @var0 : !spv.ptr, f32>, StorageBuffer> + + // CHECK: spv.globalVariable @var1 : !spv.ptr [0], f32 [12]>, StorageBuffer> + spv.globalVariable @var1 : !spv.ptr, f32>, StorageBuffer> + + // CHECK: spv.globalVariable @var2 : !spv.ptr [0], f32 [16]>, StorageBuffer> + spv.globalVariable @var2 : !spv.ptr, f32>, StorageBuffer> +} + +// ----- + +spv.module "Logical" "GLSL450" { + // CHECK: spv.globalVariable @emptyStructAsMember : !spv.ptr [0]>, StorageBuffer> + spv.globalVariable @emptyStructAsMember : !spv.ptr>, StorageBuffer> + + // CHECK: spv.globalVariable @arrayType : !spv.ptr>, StorageBuffer> + spv.globalVariable @arrayType : !spv.ptr>, StorageBuffer> + + // CHECK: spv.globalVariable @InputStorage : !spv.ptr>, Input> + spv.globalVariable @InputStorage : !spv.ptr>, Input> + + // CHECK: spv.globalVariable @customLayout : !spv.ptr, Uniform> + spv.globalVariable @customLayout : !spv.ptr, Uniform> + + // CHECK: spv.globalVariable @emptyStruct : !spv.ptr, Uniform> + spv.globalVariable @emptyStruct : !spv.ptr, Uniform> +} + +// ----- + +spv.module "Logical" "GLSL450" { + // CHECK: spv.globalVariable @var0 : !spv.ptr, PushConstant> + spv.globalVariable @var0 : !spv.ptr, PushConstant> + // CHECK: spv.globalVariable @var1 : !spv.ptr, PhysicalStorageBuffer> + spv.globalVariable @var1 : !spv.ptr, PhysicalStorageBuffer> +} diff --git a/mlir/tools/mlir-opt/CMakeLists.txt b/mlir/tools/mlir-opt/CMakeLists.txt index 6b8a3ae..a279b0f 100644 --- a/mlir/tools/mlir-opt/CMakeLists.txt +++ b/mlir/tools/mlir-opt/CMakeLists.txt @@ -38,6 +38,7 @@ set(LIBS MLIRROCDLIR MLIRSPIRV MLIRSPIRVConversion + MLIRSPIRVTransforms MLIRStandardOps MLIRStandardToLLVM MLIRTransforms -- 2.7.4