//===- LLVMDialect.cpp - MLIR SPIR-V dialect ------------------------------===//
//
-// Copyright 2019 The MLIR Authors.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
-// 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 defines the SPIR-V dialect in MLIR.
//
//===----------------------------------------------------------------------===//
#include "mlir/SPIRV/SPIRVDialect.h"
-
#include "mlir/IR/MLIRContext.h"
#include "mlir/IR/StandardTypes.h"
#include "mlir/Parser.h"
#include "llvm/ADT/StringSwitch.h"
#include "llvm/Support/raw_ostream.h"
-#include <type_traits>
-
using namespace mlir;
using namespace mlir::spirv;
SPIRVDialect::SPIRVDialect(MLIRContext *context)
: Dialect(getDialectNamespace(), context) {
- addTypes<ArrayType, ImageType, PointerType, RuntimeArrayType>();
+ addTypes<ArrayType, ImageType, PointerType, RuntimeArrayType, StructType>();
addOperations<
#define GET_OP_LIST
return true;
}
-static Type parseAndVerifyTypeImpl(SPIRVDialect const &dialect, Location loc,
- StringRef spec) {
+static Type parseAndVerifyType(SPIRVDialect const &dialect, StringRef spec,
+ Location loc) {
+ spec = spec.trim();
auto *context = dialect.getContext();
auto type = mlir::parseType(spec.trim(), context);
if (!type) {
return type;
}
-Type SPIRVDialect::parseAndVerifyType(StringRef spec, Location loc) const {
- return parseAndVerifyTypeImpl(*this, loc, spec);
-}
-
// element-type ::= integer-type
// | floating-point-type
// | vector-type
// | spirv-type
//
// array-type ::= `!spv.array<` integer-literal `x` element-type `>`
-Type SPIRVDialect::parseArrayType(StringRef spec, Location loc) const {
+static Type parseArrayType(SPIRVDialect const &dialect, StringRef spec,
+ Location loc) {
if (!spec.consume_front("array<") || !spec.consume_back(">")) {
emitError(loc, "spv.array delimiter <...> mismatch");
return Type();
return Type();
}
- Type elementType = parseAndVerifyType(spec, loc);
+ Type elementType = parseAndVerifyType(dialect, spec, loc);
if (!elementType)
return Type();
return ArrayType::get(elementType, count);
}
+// TODO(ravishankarm) : Reorder methods to be utilities first and parse*Type
+// methods in alphabetical order
+//
// storage-class ::= `UniformConstant`
// | `Uniform`
// | `Workgroup`
// | <and other storage classes...>
//
// pointer-type ::= `!spv.ptr<` element-type `,` storage-class `>`
-Type SPIRVDialect::parsePointerType(StringRef spec, Location loc) const {
+static Type parsePointerType(SPIRVDialect const &dialect, StringRef spec,
+ Location loc) {
if (!spec.consume_front("ptr<") || !spec.consume_back(">")) {
emitError(loc, "spv.ptr delimiter <...> mismatch");
return Type();
return Type();
}
- auto pointeeType = parseAndVerifyType(ptSpec, loc);
+ auto pointeeType = parseAndVerifyType(dialect, ptSpec, loc);
if (!pointeeType)
return Type();
}
// runtime-array-type ::= `!spv.rtarray<` element-type `>`
-Type SPIRVDialect::parseRuntimeArrayType(StringRef spec, Location loc) const {
+static Type parseRuntimeArrayType(SPIRVDialect const &dialect, StringRef spec,
+ Location loc) {
if (!spec.consume_front("rtarray<") || !spec.consume_back(">")) {
emitError(loc, "spv.rtarray delimiter <...> mismatch");
return Type();
return Type();
}
- Type elementType = parseAndVerifyType(spec, loc);
+ Type elementType = parseAndVerifyType(dialect, spec, loc);
if (!elementType)
return Type();
// Specialize this function to parse each of the parameters that define an
// ImageType
template <typename ValTy>
-Optional<ValTy> parseAndVerify(SPIRVDialect const &dialect, Location loc,
- StringRef spec) {
+static Optional<ValTy> parseAndVerify(SPIRVDialect const &dialect, Location loc,
+ StringRef spec) {
emitError(loc, "unexpected parameter while parsing '") << spec << "'";
return llvm::None;
}
Optional<Type> parseAndVerify<Type>(SPIRVDialect const &dialect, Location loc,
StringRef spec) {
// TODO(ravishankarm): Further verify that the element type can be sampled
- return parseAndVerifyTypeImpl(dialect, loc, spec);
+ auto ty = parseAndVerifyType(dialect, spec, loc);
+ if (!ty) {
+ return llvm::None;
+ }
+ return ty;
}
template <>
Optional<Dim> parseAndVerify<Dim>(SPIRVDialect const &dialect, Location loc,
StringRef spec) {
auto dim = symbolizeDim(spec);
- if (!dim)
+ if (!dim) {
emitError(loc, "unknown Dim in Image type: '") << spec << "'";
+ }
return dim;
}
parseAndVerify<ImageDepthInfo>(SPIRVDialect const &dialect, Location loc,
StringRef spec) {
auto depth = symbolizeImageDepthInfo(spec);
- if (!depth)
+ if (!depth) {
emitError(loc, "unknown ImageDepthInfo in Image type: '") << spec << "'";
+ }
return depth;
}
parseAndVerify<ImageArrayedInfo>(SPIRVDialect const &dialect, Location loc,
StringRef spec) {
auto arrayedInfo = symbolizeImageArrayedInfo(spec);
- if (!arrayedInfo)
+ if (!arrayedInfo) {
emitError(loc, "unknown ImageArrayedInfo in Image type: '") << spec << "'";
+ }
return arrayedInfo;
}
parseAndVerify<ImageSamplingInfo>(SPIRVDialect const &dialect, Location loc,
StringRef spec) {
auto samplingInfo = symbolizeImageSamplingInfo(spec);
- if (!samplingInfo)
+ if (!samplingInfo) {
emitError(loc, "unknown ImageSamplingInfo in Image type: '") << spec << "'";
+ }
return samplingInfo;
}
parseAndVerify<ImageSamplerUseInfo>(SPIRVDialect const &dialect, Location loc,
StringRef spec) {
auto samplerUseInfo = symbolizeImageSamplerUseInfo(spec);
- if (!samplerUseInfo)
+ if (!samplerUseInfo) {
emitError(loc, "unknown ImageSamplerUseInfo in Image type: '")
<< spec << "'";
+ }
return samplerUseInfo;
}
Location loc,
StringRef spec) {
auto format = symbolizeImageFormat(spec);
- if (!format)
+ if (!format) {
emitError(loc, "unknown ImageFormat in Image type: '") << spec << "'";
+ }
return format;
}
+template <>
+Optional<spirv::StructType::LayoutInfo>
+parseAndVerify(SPIRVDialect const &dialect, Location loc, StringRef spec) {
+ uint64_t offsetVal = std::numeric_limits<uint64_t>::max();
+ if (!spec.consume_front("[")) {
+ emitError(loc, "expected '[' while parsing layout specification in '")
+ << spec << "'";
+ return llvm::None;
+ }
+ if (spec.consumeInteger(10, offsetVal)) {
+ emitError(
+ loc,
+ "expected unsigned integer to specify offset of member in struct: '")
+ << spec << "'";
+ return llvm::None;
+ }
+ spec = spec.trim();
+ if (!spec.consume_front("]")) {
+ emitError(loc, "missing ']' in decorations spec: '") << spec << "'";
+ return llvm::None;
+ }
+ if (spec != "") {
+ emitError(loc, "unexpected extra tokens in layout information: '")
+ << spec << "'";
+ return llvm::None;
+ }
+ return spirv::StructType::LayoutInfo{offsetVal};
+}
+
// Functor object to parse a comma separated list of specs. The function
// parseAndVerify does the actual parsing and verification of individual
// elements. This is a functor since parsing the last element of the list
// image-type ::= `!spv.image<` element-type `,` dim `,` depth-info `,`
// arrayed-info `,` sampling-info `,`
// sampler-use-info `,` format `>`
-Type SPIRVDialect::parseImageType(StringRef spec, Location loc) const {
+static Type parseImageType(SPIRVDialect const &dialect, StringRef spec,
+ Location loc) {
if (!spec.consume_front("image<") || !spec.consume_back(">")) {
emitError(loc, "spv.image delimiter <...> mismatch");
return Type();
auto value =
parseCommaSeparatedList<Type, Dim, ImageDepthInfo, ImageArrayedInfo,
ImageSamplingInfo, ImageSamplerUseInfo,
- ImageFormat>{}(*this, loc, spec);
+ ImageFormat>{}(dialect, loc, spec);
if (!value) {
return Type();
}
return ImageType::get(value.getValue());
}
+// Method to parse one member of a struct (including Layout information)
+static ParseResult
+parseStructElement(SPIRVDialect const &dialect, StringRef spec, Location loc,
+ SmallVectorImpl<Type> &memberTypes,
+ SmallVectorImpl<StructType::LayoutInfo> &layoutInfo) {
+ // Check for a '[' <layoutInfo> ']'
+ auto lastLSquare = spec.rfind('[');
+ auto typeSpec = spec.substr(0, lastLSquare);
+ auto layoutSpec = (lastLSquare == StringRef::npos ? StringRef("")
+ : spec.substr(lastLSquare));
+ auto type = parseAndVerify<Type>(dialect, loc, typeSpec);
+ if (!type) {
+ return failure();
+ }
+ memberTypes.push_back(type.getValue());
+ if (layoutSpec.empty()) {
+ return success();
+ }
+ if (layoutInfo.size() != memberTypes.size() - 1) {
+ emitError(loc, "layout specification must be given for all members");
+ return failure();
+ }
+ auto layout =
+ parseAndVerify<StructType::LayoutInfo>(dialect, loc, layoutSpec);
+ if (!layout) {
+ return failure();
+ }
+ layoutInfo.push_back(layout.getValue());
+ return success();
+}
+
+// Helper method to record the position of the corresponding '>' for every '<'
+// encountered when parsing the string left to right. The relative position of
+// '>' w.r.t to the '<' is recorded.
+static bool
+computeMatchingRAngles(Location loc, StringRef const &spec,
+ SmallVectorImpl<size_t> &matchingRAngleOffset) {
+ SmallVector<size_t, 4> openBrackets;
+ for (size_t i = 0, e = spec.size(); i != e; ++i) {
+ if (spec[i] == '<') {
+ openBrackets.push_back(i);
+ } else if (spec[i] == '>') {
+ if (openBrackets.empty()) {
+ emitError(loc, "unbalanced '<' in '") << spec << "'";
+ return false;
+ }
+ matchingRAngleOffset.push_back(i - openBrackets.pop_back_val());
+ }
+ }
+ return true;
+}
+
+static ParseResult
+parseStructHelper(SPIRVDialect const &dialect, StringRef spec, Location loc,
+ ArrayRef<size_t> matchingRAngleOffset,
+ SmallVectorImpl<Type> &memberTypes,
+ SmallVectorImpl<StructType::LayoutInfo> &layoutInfo) {
+ // Check if the occurrence of ',' or '<' is before. If former, split using
+ // ','. If latter, split using matching '>' to get the entire type
+ // description
+ auto firstComma = spec.find(',');
+ auto firstLAngle = spec.find('<');
+ if (firstLAngle == StringRef::npos && firstComma == StringRef::npos) {
+ return parseStructElement(dialect, spec, loc, memberTypes, layoutInfo);
+ }
+ if (firstLAngle == StringRef::npos || firstComma < firstLAngle) {
+ // Parse the type before the ','
+ if (parseStructElement(dialect, spec.substr(0, firstComma), loc,
+ memberTypes, layoutInfo)) {
+ return failure();
+ }
+ return parseStructHelper(dialect, spec.substr(firstComma + 1).ltrim(), loc,
+ matchingRAngleOffset, memberTypes, layoutInfo);
+ }
+ auto matchingRAngle = matchingRAngleOffset.front() + firstLAngle;
+ // Find the next ',' or '>'
+ auto endLoc = std::min(spec.find(',', matchingRAngle + 1), spec.size());
+ if (parseStructElement(dialect, spec.substr(0, endLoc), loc, memberTypes,
+ layoutInfo)) {
+ return failure();
+ }
+ auto rest = spec.substr(endLoc + 1).ltrim();
+ if (rest.empty()) {
+ return success();
+ }
+ if (rest.front() == ',') {
+ return parseStructHelper(
+ dialect, rest.drop_front().trim(), loc,
+ ArrayRef<size_t>(std::next(matchingRAngleOffset.begin()),
+ matchingRAngleOffset.end()),
+ memberTypes, layoutInfo);
+ }
+ emitError(loc, "unexpected string : '") << rest << "'";
+ return failure();
+}
+
+// struct-type ::= `!spv.struct<` spirv-type (` [` integer-literal `]`)?
+// (`, ` spirv-type ( ` [` integer-literal `] ` )? )*
+static Type parseStructType(SPIRVDialect const &dialect, StringRef spec,
+ Location loc) {
+ if (!spec.consume_front("struct<") || !spec.consume_back(">")) {
+ emitError(loc, "spv.struct delimiter <...> mismatch");
+ return Type();
+ }
+
+ if (spec.trim().empty()) {
+ emitError(loc, "expected SPIR-V type");
+ return Type();
+ }
+
+ SmallVector<Type, 4> memberTypes;
+ SmallVector<StructType::LayoutInfo, 4> layoutInfo;
+ SmallVector<size_t, 4> matchingRAngleOffset;
+ if (!computeMatchingRAngles(loc, spec, matchingRAngleOffset) ||
+ parseStructHelper(dialect, spec, loc, matchingRAngleOffset, memberTypes,
+ layoutInfo)) {
+ return Type();
+ }
+ if (layoutInfo.empty()) {
+ return StructType::get(memberTypes);
+ }
+ if (memberTypes.size() != layoutInfo.size()) {
+ emitError(loc, "layout specification must be given for all members");
+ return Type();
+ }
+ return StructType::get(memberTypes, layoutInfo);
+}
+
+// spirv-type ::= array-type
+// | element-type
+// | image-type
+// | pointer-type
+// | runtime-array-type
+// | struct-type
Type SPIRVDialect::parseType(StringRef spec, Location loc) const {
if (spec.startswith("array"))
- return parseArrayType(spec, loc);
+ return parseArrayType(*this, spec, loc);
if (spec.startswith("image"))
- return parseImageType(spec, loc);
+ return parseImageType(*this, spec, loc);
if (spec.startswith("ptr"))
- return parsePointerType(spec, loc);
+ return parsePointerType(*this, spec, loc);
if (spec.startswith("rtarray"))
- return parseRuntimeArrayType(spec, loc);
+ return parseRuntimeArrayType(*this, spec, loc);
+ if (spec.startswith("struct"))
+ return parseStructType(*this, spec, loc);
emitError(loc, "unknown SPIR-V type: ") << spec;
return Type();
<< stringifyImageFormat(type.getImageFormat()) << ">";
}
+static void print(StructType type, llvm::raw_ostream &os) {
+ os << "struct<";
+ std::string sep = "";
+ for (size_t i = 0, e = type.getNumMembers(); i != e; ++i) {
+ os << sep << type.getMemberType(i);
+ if (type.hasLayout()) {
+ os << " [" << type.getOffset(i) << "]";
+ }
+ sep = ", ";
+ }
+ os << ">";
+}
+
void SPIRVDialect::printType(Type type, llvm::raw_ostream &os) const {
switch (type.getKind()) {
case TypeKind::Array:
case TypeKind::RuntimeArray:
print(type.cast<RuntimeArrayType>(), os);
return;
- case TypeKind::ImageType:
+ case TypeKind::Image:
print(type.cast<ImageType>(), os);
return;
+ case TypeKind::Struct:
+ print(type.cast<StructType>(), os);
+ return;
default:
llvm_unreachable("unhandled SPIR-V type");
}
elementCount);
}
-Type ArrayType::getElementType() { return getImpl()->elementType; }
+Type ArrayType::getElementType() const { return getImpl()->elementType; }
-int64_t ArrayType::getElementCount() { return getImpl()->elementCount; }
+int64_t ArrayType::getElementCount() const { return getImpl()->elementCount; }
//===----------------------------------------------------------------------===//
// ImageType
ImageType::get(std::tuple<Type, Dim, ImageDepthInfo, ImageArrayedInfo,
ImageSamplingInfo, ImageSamplerUseInfo, ImageFormat>
value) {
- return Base::get(std::get<0>(value).getContext(), TypeKind::ImageType, value);
+ return Base::get(std::get<0>(value).getContext(), TypeKind::Image, value);
}
-Type ImageType::getElementType() { return getImpl()->elementType; }
+Type ImageType::getElementType() const { return getImpl()->elementType; }
-Dim ImageType::getDim() { return getImpl()->getDim(); }
+Dim ImageType::getDim() const { return getImpl()->getDim(); }
-ImageDepthInfo ImageType::getDepthInfo() { return getImpl()->getDepthInfo(); }
+ImageDepthInfo ImageType::getDepthInfo() const {
+ return getImpl()->getDepthInfo();
+}
-ImageArrayedInfo ImageType::getArrayedInfo() {
+ImageArrayedInfo ImageType::getArrayedInfo() const {
return getImpl()->getArrayedInfo();
}
-ImageSamplingInfo ImageType::getSamplingInfo() {
+ImageSamplingInfo ImageType::getSamplingInfo() const {
return getImpl()->getSamplingInfo();
}
-ImageSamplerUseInfo ImageType::getSamplerUseInfo() {
+ImageSamplerUseInfo ImageType::getSamplerUseInfo() const {
return getImpl()->getSamplerUseInfo();
}
-ImageFormat ImageType::getImageFormat() { return getImpl()->getImageFormat(); }
+ImageFormat ImageType::getImageFormat() const {
+ return getImpl()->getImageFormat();
+}
//===----------------------------------------------------------------------===//
// PointerType
storageClass);
}
-Type PointerType::getPointeeType() { return getImpl()->pointeeType; }
+Type PointerType::getPointeeType() const { return getImpl()->pointeeType; }
-StorageClass PointerType::getStorageClass() {
+StorageClass PointerType::getStorageClass() const {
return getImpl()->getStorageClass();
}
+StringRef PointerType::getStorageClassStr() const {
+ return stringifyStorageClass(getStorageClass());
+}
+
//===----------------------------------------------------------------------===//
// RuntimeArrayType
//===----------------------------------------------------------------------===//
elementType);
}
-Type RuntimeArrayType::getElementType() { return getImpl()->elementType; }
+Type RuntimeArrayType::getElementType() const { return getImpl()->elementType; }
+
+//===----------------------------------------------------------------------===//
+// StructType
+//===----------------------------------------------------------------------===//
+
+struct spirv::detail::StructTypeStorage : public TypeStorage {
+ StructTypeStorage(unsigned numMembers, Type const *memberTypes,
+ StructType::LayoutInfo const *layoutInfo)
+ : TypeStorage(numMembers), memberTypes(memberTypes),
+ layoutInfo(layoutInfo) {}
+
+ using KeyTy = std::pair<ArrayRef<Type>, ArrayRef<StructType::LayoutInfo>>;
+ bool operator==(const KeyTy &key) const {
+ return key == KeyTy(getMemberTypes(), getLayoutInfo());
+ }
+
+ static StructTypeStorage *construct(TypeStorageAllocator &allocator,
+ const KeyTy &key) {
+ ArrayRef<Type> keyTypes = key.first;
+
+ // Copy the member type and layout information into the bump pointer
+ auto typesList = allocator.copyInto(keyTypes).data();
+
+ const StructType::LayoutInfo *layoutInfoList = nullptr;
+ if (!key.second.empty()) {
+ ArrayRef<StructType::LayoutInfo> keyLayoutInfo = key.second;
+ assert(keyLayoutInfo.size() == keyTypes.size() &&
+ "size of layout information must be same as the size of number of "
+ "elements");
+ layoutInfoList = allocator.copyInto(keyLayoutInfo).data();
+ }
+
+ return new (allocator.allocate<StructTypeStorage>())
+ StructTypeStorage(keyTypes.size(), typesList, layoutInfoList);
+ }
+
+ ArrayRef<Type> getMemberTypes() const {
+ return ArrayRef<Type>(memberTypes, getSubclassData());
+ }
+
+ ArrayRef<StructType::LayoutInfo> getLayoutInfo() const {
+ if (layoutInfo) {
+ return ArrayRef<StructType::LayoutInfo>(layoutInfo, getSubclassData());
+ }
+ return ArrayRef<StructType::LayoutInfo>(nullptr, size_t(0));
+ }
+
+ Type const *memberTypes;
+ StructType::LayoutInfo const *layoutInfo;
+};
+
+StructType StructType::get(ArrayRef<Type> memberTypes) {
+ assert(!memberTypes.empty() && "Struct needs at least one member type");
+ ArrayRef<StructType::LayoutInfo> noLayout(nullptr, size_t(0));
+ return Base::get(memberTypes[0].getContext(), TypeKind::Struct, memberTypes,
+ noLayout);
+}
+
+StructType StructType::get(ArrayRef<Type> memberTypes,
+ ArrayRef<StructType::LayoutInfo> layoutInfo) {
+ assert(!memberTypes.empty() && "Struct needs at least one member type");
+ return Base::get(memberTypes.vec().front().getContext(), TypeKind::Struct,
+ memberTypes, layoutInfo);
+}
+
+size_t StructType::getNumMembers() const {
+ return getImpl()->getSubclassData();
+}
+
+Type StructType::getMemberType(size_t i) const {
+ assert(
+ getNumMembers() > i &&
+ "element index is more than number of members of the SPIR-V StructType");
+ return getImpl()->memberTypes[i];
+}
+
+bool StructType::hasLayout() const { return getImpl()->layoutInfo; }
+
+uint64_t StructType::getOffset(size_t i) const {
+ assert(
+ getNumMembers() > i &&
+ "element index is more than number of members of the SPIR-V StructType");
+ return getImpl()->layoutInfo[i];
+}