1 //===- SPIRVConversion.h - SPIR-V Conversion Utilities ----------*- C++ -*-===// 2 // 3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4 // See https://llvm.org/LICENSE.txt for license information. 5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 // 7 //===----------------------------------------------------------------------===// 8 // 9 // Defines utilities to use while converting to the SPIR-V dialect. 10 // 11 //===----------------------------------------------------------------------===// 12 13 #ifndef MLIR_DIALECT_SPIRV_TRANSFORMS_SPIRVCONVERSION_H 14 #define MLIR_DIALECT_SPIRV_TRANSFORMS_SPIRVCONVERSION_H 15 16 #include "mlir/Dialect/SPIRV/IR/SPIRVAttributes.h" 17 #include "mlir/Dialect/SPIRV/IR/SPIRVTypes.h" 18 #include "mlir/Dialect/SPIRV/IR/TargetAndABI.h" 19 #include "mlir/Transforms/DialectConversion.h" 20 #include "llvm/ADT/SmallSet.h" 21 22 namespace mlir { 23 24 //===----------------------------------------------------------------------===// 25 // Type Converter 26 //===----------------------------------------------------------------------===// 27 28 /// Type conversion from builtin types to SPIR-V types for shader interface. 29 /// 30 /// For memref types, this converter additionally performs type wrapping to 31 /// satisfy shader interface requirements: shader interface types must be 32 /// pointers to structs. 33 class SPIRVTypeConverter : public TypeConverter { 34 public: 35 struct Options { 36 /// Whether to emulate non-32-bit scalar types with 32-bit scalar types if 37 /// no native support. 38 /// 39 /// Non-32-bit scalar types require special hardware support that may not 40 /// exist on all GPUs. This is reflected in SPIR-V as that non-32-bit scalar 41 /// types require special capabilities or extensions. This option controls 42 /// whether to use 32-bit types to emulate, if a scalar type of a certain 43 /// bitwidth is not supported in the target environment. This requires the 44 /// runtime to also feed in data with a matched bitwidth and layout for 45 /// interface types. The runtime can do that by inspecting the SPIR-V 46 /// module. 47 /// 48 /// If the original scalar type has less than 32-bit, a multiple of its 49 /// values will be packed into one 32-bit value to be memory efficient. 50 bool emulateNon32BitScalarTypes{true}; 51 52 /// Use 64-bit integers to convert index types. 53 bool use64bitIndex{false}; 54 55 /// The number of bits to store a boolean value. It is eight bits by 56 /// default. 57 unsigned boolNumBits{8}; 58 59 // Note: we need this instead of inline initializers because of 60 // https://bugs.llvm.org/show_bug.cgi?id=36684 OptionsOptions61 Options() 62 63 {} 64 }; 65 66 explicit SPIRVTypeConverter(spirv::TargetEnvAttr targetAttr, 67 Options options = {}); 68 69 /// Gets the SPIR-V correspondence for the standard index type. 70 Type getIndexType() const; 71 72 /// Returns the corresponding memory space for memref given a SPIR-V storage 73 /// class. 74 static unsigned getMemorySpaceForStorageClass(spirv::StorageClass); 75 76 /// Returns the SPIR-V storage class given a memory space for memref. Return 77 /// llvm::None if the memory space does not map to any SPIR-V storage class. 78 static Optional<spirv::StorageClass> 79 getStorageClassForMemorySpace(unsigned space); 80 81 /// Returns the options controlling the SPIR-V type converter. 82 const Options &getOptions() const; 83 84 private: 85 spirv::TargetEnv targetEnv; 86 Options options; 87 88 MLIRContext *getContext() const; 89 }; 90 91 //===----------------------------------------------------------------------===// 92 // Conversion Target 93 //===----------------------------------------------------------------------===// 94 95 // The default SPIR-V conversion target. 96 // 97 // It takes a SPIR-V target environment and controls operation legality based on 98 // the their availability in the target environment. 99 class SPIRVConversionTarget : public ConversionTarget { 100 public: 101 /// Creates a SPIR-V conversion target for the given target environment. 102 static std::unique_ptr<SPIRVConversionTarget> 103 get(spirv::TargetEnvAttr targetAttr); 104 105 private: 106 explicit SPIRVConversionTarget(spirv::TargetEnvAttr targetAttr); 107 108 // Be explicit that instance of this class cannot be copied or moved: there 109 // are lambdas capturing fields of the instance. 110 SPIRVConversionTarget(const SPIRVConversionTarget &) = delete; 111 SPIRVConversionTarget(SPIRVConversionTarget &&) = delete; 112 SPIRVConversionTarget &operator=(const SPIRVConversionTarget &) = delete; 113 SPIRVConversionTarget &operator=(SPIRVConversionTarget &&) = delete; 114 115 /// Returns true if the given `op` is legal to use under the current target 116 /// environment. 117 bool isLegalOp(Operation *op); 118 119 spirv::TargetEnv targetEnv; 120 }; 121 122 //===----------------------------------------------------------------------===// 123 // Patterns and Utility Functions 124 //===----------------------------------------------------------------------===// 125 126 /// Appends to a pattern list additional patterns for translating the builtin 127 /// `func` op to the SPIR-V dialect. These patterns do not handle shader 128 /// interface/ABI; they convert function parameters to be of SPIR-V allowed 129 /// types. 130 void populateBuiltinFuncToSPIRVPatterns(SPIRVTypeConverter &typeConverter, 131 RewritePatternSet &patterns); 132 133 namespace spirv { 134 class AccessChainOp; 135 136 /// Returns the value for the given `builtin` variable. This function gets or 137 /// inserts the global variable associated for the builtin within the nearest 138 /// symbol table enclosing `op`. Returns null Value on error. 139 Value getBuiltinVariableValue(Operation *op, BuiltIn builtin, Type integerType, 140 OpBuilder &builder); 141 142 /// Gets the value at the given `offset` of the push constant storage with a 143 /// total of `elementCount` `integerType` integers. A global variable will be 144 /// created in the nearest symbol table enclosing `op` for the push constant 145 /// storage if not existing. Load ops will be created via the given `builder` to 146 /// load values from the push constant. Returns null Value on error. 147 Value getPushConstantValue(Operation *op, unsigned elementCount, 148 unsigned offset, Type integerType, 149 OpBuilder &builder); 150 151 /// Generates IR to perform index linearization with the given `indices` and 152 /// their corresponding `strides`, adding an initial `offset`. 153 Value linearizeIndex(ValueRange indices, ArrayRef<int64_t> strides, 154 int64_t offset, Type integerType, Location loc, 155 OpBuilder &builder); 156 157 /// Performs the index computation to get to the element at `indices` of the 158 /// memory pointed to by `basePtr`, using the layout map of `baseType`. 159 /// Returns null if index computation cannot be performed. 160 161 // TODO: This method assumes that the `baseType` is a MemRefType with AffineMap 162 // that has static strides. Extend to handle dynamic strides. 163 spirv::AccessChainOp getElementPtr(SPIRVTypeConverter &typeConverter, 164 MemRefType baseType, Value basePtr, 165 ValueRange indices, Location loc, 166 OpBuilder &builder); 167 168 } // namespace spirv 169 } // namespace mlir 170 171 #endif // MLIR_DIALECT_SPIRV_TRANSFORMS_SPIRVCONVERSION_H 172