Allow specification of decorators on SPIR-V StructType members.

Allow specification of decorators on SPIR-V StructType members. If the
struct has layout information, these decorations are to be specified
after the offset specification of the member. These decorations are
emitted as OpMemberDecorate instructions on the struct <id>. Update
(de)serialization to handle these decorations.

PiperOrigin-RevId: 270130136
This commit is contained in:
Mahesh Ravishankar 2019-09-19 14:49:29 -07:00 committed by A. Unique TensorFlower
parent 2df646bef6
commit 9a4f5d2ee3
8 changed files with 287 additions and 114 deletions

View File

@ -179,8 +179,9 @@ For example,
This corresponds to SPIR-V [struct type][StructType]. Its syntax is
``` {.ebnf}
struct-type ::= `!spv.struct<` spirv-type (` [` integer-literal `]` )?
(`, ` spirv-type ( ` [` integer-literal `] ` )? )* `>`
struct-member-decoration ::= integer-literal? spirv-decoration*
struct-type ::= `!spv.struct<` spirv-type (`[` struct-member-decoration `]`)?
(`, ` spirv-type (`[` struct-member-decoration `]`)?
```
For Example,

View File

@ -174,12 +174,13 @@ public:
// types
using LayoutInfo = uint64_t;
using MemberDecorationInfo = std::pair<uint32_t, spirv::Decoration>;
static bool kindof(unsigned kind) { return kind == TypeKind::Struct; }
static StructType get(ArrayRef<Type> memberTypes);
static StructType get(ArrayRef<Type> memberTypes,
ArrayRef<LayoutInfo> layoutInfo);
ArrayRef<LayoutInfo> layoutInfo = {},
ArrayRef<MemberDecorationInfo> memberDecorations = {});
unsigned getNumElements() const;
@ -188,6 +189,16 @@ public:
bool hasLayout() const;
uint64_t getOffset(unsigned) const;
// Returns in `allMemberDecorations` the spirv::Decorations (apart from
// Offset) associated with all members of the StructType.
void getMemberDecorations(SmallVectorImpl<StructType::MemberDecorationInfo>
&allMemberDecorations) const;
// Returns in `memberDecorations` all the spirv::Decorations (apart from
// Offset) associated with the `i`-th member of the StructType.
void getMemberDecorations(
unsigned i, SmallVectorImpl<spirv::Decoration> &memberDecorations) const;
};
} // end namespace spirv

View File

@ -64,8 +64,8 @@ Optional<Type> parseAndVerify<Type>(SPIRVDialect const &dialect, Location loc,
StringRef spec);
template <>
Optional<uint64_t> parseAndVerify(SPIRVDialect const &dialect, Location loc,
StringRef spec);
Optional<uint64_t> parseAndVerify<uint64_t>(SPIRVDialect const &dialect,
Location loc, StringRef spec);
// Parses "<number> x" from the beginning of `spec`.
static bool parseNumberX(StringRef &spec, int64_t &number) {
@ -206,6 +206,13 @@ static Type parseArrayType(SPIRVDialect const &dialect, StringRef spec,
if (lastLSquare != StringRef::npos) {
auto layoutSpec = spec.substr(lastLSquare);
layoutSpec = layoutSpec.trim();
if (!layoutSpec.consume_front("[") || !layoutSpec.consume_back("]")) {
emitError(loc, "expected array stride within '[' ']' in '")
<< layoutSpec << "'";
return Type();
}
layoutSpec = layoutSpec.trim();
auto layout =
parseAndVerify<ArrayType::LayoutInfo>(dialect, loc, layoutSpec);
if (!layout) {
@ -216,6 +223,7 @@ static Type parseArrayType(SPIRVDialect const &dialect, StringRef spec,
emitError(loc, "ArrayStride must be greater than zero");
return Type();
}
spec = spec.substr(0, lastLSquare);
}
@ -314,34 +322,23 @@ Optional<Type> parseAndVerify<Type>(SPIRVDialect const &dialect, Location loc,
return ty;
}
template <>
Optional<uint64_t> 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;
}
template <typename IntTy>
static Optional<IntTy> parseAndVerifyInteger(SPIRVDialect const &dialect,
Location loc, StringRef spec) {
IntTy offsetVal = std::numeric_limits<IntTy>::max();
spec = spec.trim();
if (spec.consumeInteger(10, offsetVal)) {
emitError(loc, "expected unsigned integer to specify layout information: '")
<< 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 offsetVal;
}
template <>
Optional<uint64_t> parseAndVerify<uint64_t>(SPIRVDialect const &dialect,
Location loc, StringRef spec) {
return parseAndVerifyInteger<uint64_t>(dialect, loc, spec);
}
// 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
@ -423,33 +420,62 @@ static Type parseImageType(SPIRVDialect const &dialect, StringRef spec,
}
// 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> ']'
static ParseResult parseStructElement(
SPIRVDialect const &dialect, StringRef spec, Location loc,
SmallVectorImpl<Type> &memberTypes,
SmallVectorImpl<StructType::LayoutInfo> &layoutInfo,
SmallVectorImpl<StructType::MemberDecorationInfo> &memberDecorationInfo) {
// Check for '[' integer-literal spirv-decoration* ']'
auto lastLSquare = spec.rfind('[');
auto typeSpec = spec.substr(0, lastLSquare);
auto layoutSpec = (lastLSquare == StringRef::npos ? StringRef("")
: spec.substr(lastLSquare));
auto memberDecorationSpec =
(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()) {
if (memberDecorationSpec.empty()) {
return success();
}
if (layoutInfo.size() != memberTypes.size() - 1) {
emitError(loc, "layout specification must be given for all members");
memberDecorationSpec = memberDecorationSpec.trim();
if (!memberDecorationSpec.consume_front("[") ||
!memberDecorationSpec.consume_back("]")) {
emitError(loc,
"expected struct member offset/decoration within '[' ']' in '")
<< spec << "'";
return failure();
}
memberDecorationSpec = memberDecorationSpec.trim();
auto memberInfo = memberDecorationSpec.split(' ');
// Check if the first element is offset.
auto layout =
parseAndVerify<StructType::LayoutInfo>(dialect, loc, layoutSpec);
if (!layout) {
return failure();
parseAndVerify<StructType::LayoutInfo>(dialect, loc, memberInfo.first);
if (layout) {
if (layoutInfo.size() != memberTypes.size() - 1) {
emitError(loc, "layout specification must be given for all members");
return failure();
}
layoutInfo.push_back(layout.getValue());
memberDecorationSpec = memberInfo.second.trim();
}
// Check for spirv::Decorations.
while (!memberDecorationSpec.empty()) {
memberInfo = memberDecorationSpec.split(' ');
auto memberDecoration =
parseAndVerify<spirv::Decoration>(dialect, loc, memberInfo.first);
if (!memberDecoration) {
return failure();
}
memberDecorationInfo.emplace_back(
static_cast<uint32_t>(memberTypes.size() - 1),
memberDecoration.getValue());
memberDecorationSpec = memberInfo.second.trim();
}
layoutInfo.push_back(layout.getValue());
return success();
}
@ -474,33 +500,35 @@ computeMatchingRAngles(Location loc, StringRef const &spec,
return true;
}
static ParseResult
parseStructHelper(SPIRVDialect const &dialect, StringRef spec, Location loc,
ArrayRef<size_t> matchingRAngleOffset,
SmallVectorImpl<Type> &memberTypes,
SmallVectorImpl<StructType::LayoutInfo> &layoutInfo) {
static ParseResult parseStructHelper(
SPIRVDialect const &dialect, StringRef spec, Location loc,
ArrayRef<size_t> matchingRAngleOffset, SmallVectorImpl<Type> &memberTypes,
SmallVectorImpl<StructType::LayoutInfo> &layoutInfo,
SmallVectorImpl<StructType::MemberDecorationInfo> &memberDecorationsInfo) {
// 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);
return parseStructElement(dialect, spec, loc, memberTypes, layoutInfo,
memberDecorationsInfo);
}
if (firstLAngle == StringRef::npos || firstComma < firstLAngle) {
// Parse the type before the ','
if (parseStructElement(dialect, spec.substr(0, firstComma), loc,
memberTypes, layoutInfo)) {
memberTypes, layoutInfo, memberDecorationsInfo)) {
return failure();
}
return parseStructHelper(dialect, spec.substr(firstComma + 1).ltrim(), loc,
matchingRAngleOffset, memberTypes, layoutInfo);
matchingRAngleOffset, memberTypes, layoutInfo,
memberDecorationsInfo);
}
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)) {
layoutInfo, memberDecorationsInfo)) {
return failure();
}
auto rest = spec.substr(endLoc + 1).ltrim();
@ -512,14 +540,15 @@ parseStructHelper(SPIRVDialect const &dialect, StringRef spec, Location loc,
dialect, rest.drop_front().trim(), loc,
ArrayRef<size_t>(std::next(matchingRAngleOffset.begin()),
matchingRAngleOffset.end()),
memberTypes, layoutInfo);
memberTypes, layoutInfo, memberDecorationsInfo);
}
emitError(loc, "unexpected string : '") << rest << "'";
return failure();
}
// struct-type ::= `!spv.struct<` spirv-type (` [` integer-literal `]`)?
// (`, ` spirv-type ( ` [` integer-literal `] ` )? )*
// struct-member-decoration ::= integer-literal? spirv-decoration*
// struct-type ::= `!spv.struct<` spirv-type (`[` struct-member-decoration `]`)?
// (`, ` spirv-type (`[` struct-member-decoration `]`)?
static Type parseStructType(SPIRVDialect const &dialect, StringRef spec,
Location loc) {
if (!spec.consume_front("struct<") || !spec.consume_back(">")) {
@ -534,20 +563,18 @@ static Type parseStructType(SPIRVDialect const &dialect, StringRef spec,
SmallVector<Type, 4> memberTypes;
SmallVector<StructType::LayoutInfo, 4> layoutInfo;
SmallVector<StructType::MemberDecorationInfo, 4> memberDecorationsInfo;
SmallVector<size_t, 4> matchingRAngleOffset;
if (!computeMatchingRAngles(loc, spec, matchingRAngleOffset) ||
parseStructHelper(dialect, spec, loc, matchingRAngleOffset, memberTypes,
layoutInfo)) {
layoutInfo, memberDecorationsInfo)) {
return Type();
}
if (layoutInfo.empty()) {
return StructType::get(memberTypes);
}
if (memberTypes.size() != layoutInfo.size()) {
if (!layoutInfo.empty() && memberTypes.size() != layoutInfo.size()) {
emitError(loc, "layout specification must be given for all members");
return Type();
}
return StructType::get(memberTypes, layoutInfo);
return StructType::get(memberTypes, layoutInfo, memberDecorationsInfo);
}
// spirv-type ::= array-type
@ -606,8 +633,22 @@ static void print(StructType type, llvm::raw_ostream &os) {
os << "struct<";
auto printMember = [&](unsigned i) {
os << type.getElementType(i);
if (type.hasLayout()) {
os << " [" << type.getOffset(i) << "]";
SmallVector<spirv::Decoration, 0> decorations;
type.getMemberDecorations(i, decorations);
if (type.hasLayout() || !decorations.empty()) {
os << " [";
if (type.hasLayout()) {
os << type.getOffset(i);
if (!decorations.empty())
os << " ";
}
auto between_fn = [&os]() { os << " "; };
auto each_fn = [&os](spirv::Decoration decoration) {
os << stringifyDecoration(decoration);
};
mlir::interleave(decorations.begin(), decorations.end(), each_fn,
between_fn);
os << "]";
}
};
mlir::interleaveComma(llvm::seq<unsigned>(0, type.getNumElements()), os,

View File

@ -363,34 +363,47 @@ Type RuntimeArrayType::getElementType() const { return getImpl()->elementType; }
//===----------------------------------------------------------------------===//
struct spirv::detail::StructTypeStorage : public TypeStorage {
StructTypeStorage(unsigned numMembers, Type const *memberTypes,
StructType::LayoutInfo const *layoutInfo)
StructTypeStorage(
unsigned numMembers, Type const *memberTypes,
StructType::LayoutInfo const *layoutInfo, unsigned numMemberDecorations,
StructType::MemberDecorationInfo const *memberDecorationsInfo)
: TypeStorage(numMembers), memberTypes(memberTypes),
layoutInfo(layoutInfo) {}
layoutInfo(layoutInfo), numMemberDecorations(numMemberDecorations),
memberDecorationsInfo(memberDecorationsInfo) {}
using KeyTy = std::pair<ArrayRef<Type>, ArrayRef<StructType::LayoutInfo>>;
using KeyTy = std::tuple<ArrayRef<Type>, ArrayRef<StructType::LayoutInfo>,
ArrayRef<StructType::MemberDecorationInfo>>;
bool operator==(const KeyTy &key) const {
return key == KeyTy(getMemberTypes(), getLayoutInfo());
return key ==
KeyTy(getMemberTypes(), getLayoutInfo(), getMemberDecorationsInfo());
}
static StructTypeStorage *construct(TypeStorageAllocator &allocator,
const KeyTy &key) {
ArrayRef<Type> keyTypes = key.first;
ArrayRef<Type> keyTypes = std::get<0>(key);
// 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;
if (!std::get<1>(key).empty()) {
ArrayRef<StructType::LayoutInfo> keyLayoutInfo = std::get<1>(key);
assert(keyLayoutInfo.size() == keyTypes.size() &&
"size of layout information must be same as the size of number of "
"elements");
layoutInfoList = allocator.copyInto(keyLayoutInfo).data();
}
const StructType::MemberDecorationInfo *memberDecorationList = nullptr;
unsigned numMemberDecorations = 0;
if (!std::get<2>(key).empty()) {
auto keyMemberDecorations = std::get<2>(key);
numMemberDecorations = keyMemberDecorations.size();
memberDecorationList = allocator.copyInto(keyMemberDecorations).data();
}
return new (allocator.allocate<StructTypeStorage>())
StructTypeStorage(keyTypes.size(), typesList, layoutInfoList);
StructTypeStorage(keyTypes.size(), typesList, layoutInfoList,
numMemberDecorations, memberDecorationList);
}
ArrayRef<Type> getMemberTypes() const {
@ -401,25 +414,34 @@ struct spirv::detail::StructTypeStorage : public TypeStorage {
if (layoutInfo) {
return ArrayRef<StructType::LayoutInfo>(layoutInfo, getSubclassData());
}
return ArrayRef<StructType::LayoutInfo>(nullptr, size_t(0));
return {};
}
ArrayRef<StructType::MemberDecorationInfo> getMemberDecorationsInfo() const {
if (memberDecorationsInfo) {
return ArrayRef<StructType::MemberDecorationInfo>(memberDecorationsInfo,
numMemberDecorations);
}
return {};
}
Type const *memberTypes;
StructType::LayoutInfo const *layoutInfo;
unsigned numMemberDecorations;
StructType::MemberDecorationInfo const *memberDecorationsInfo;
};
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) {
StructType
StructType::get(ArrayRef<Type> memberTypes,
ArrayRef<StructType::LayoutInfo> layoutInfo,
ArrayRef<StructType::MemberDecorationInfo> memberDecorations) {
assert(!memberTypes.empty() && "Struct needs at least one member type");
// Sort the decorations.
SmallVector<StructType::MemberDecorationInfo, 4> sortedDecorations(
memberDecorations.begin(), memberDecorations.end());
llvm::array_pod_sort(sortedDecorations.begin(), sortedDecorations.end());
return Base::get(memberTypes.vec().front().getContext(), TypeKind::Struct,
memberTypes, layoutInfo);
memberTypes, layoutInfo, sortedDecorations);
}
unsigned StructType::getNumElements() const {
@ -441,3 +463,28 @@ uint64_t StructType::getOffset(unsigned index) const {
"element index is more than number of members of the SPIR-V StructType");
return getImpl()->layoutInfo[index];
}
void StructType::getMemberDecorations(
SmallVectorImpl<StructType::MemberDecorationInfo> &memberDecorations)
const {
memberDecorations.clear();
auto implMemberDecorations = getImpl()->getMemberDecorationsInfo();
memberDecorations.append(implMemberDecorations.begin(),
implMemberDecorations.end());
}
void StructType::getMemberDecorations(
unsigned index, SmallVectorImpl<spirv::Decoration> &decorations) const {
assert(getNumElements() > index && "member index out of range");
auto memberDecorations = getImpl()->getMemberDecorationsInfo();
decorations.clear();
for (auto &memberDecoration : memberDecorations) {
if (memberDecoration.first == index) {
decorations.push_back(memberDecoration.second);
}
if (memberDecoration.first > index) {
// Early exit since the decorations are stored sorted.
return;
}
}
}

View File

@ -399,7 +399,11 @@ private:
DenseMap<uint32_t, uint32_t> typeDecorations;
// Result <id> to member decorations.
DenseMap<uint32_t, DenseMap<uint32_t, uint32_t>> memberDecorationMap;
// decorated-struct-type-<id> ->
// (struct-member-index -> (decoration -> decoration-operands))
DenseMap<uint32_t,
DenseMap<uint32_t, DenseMap<spirv::Decoration, ArrayRef<uint32_t>>>>
memberDecorationMap;
// Result <id> to extended instruction set name.
DenseMap<uint32_t, StringRef> extendedInstSets;
@ -622,18 +626,22 @@ LogicalResult Deserializer::processDecoration(ArrayRef<uint32_t> words) {
LogicalResult Deserializer::processMemberDecoration(ArrayRef<uint32_t> words) {
// The binary layout of OpMemberDecorate is different comparing to OpDecorate
if (words.size() != 4) {
return emitError(unknownLoc, "OpMemberDecorate must have 4 operands");
if (words.size() < 3) {
return emitError(unknownLoc,
"OpMemberDecorate must have at least 3 operands");
}
switch (static_cast<spirv::Decoration>(words[2])) {
case spirv::Decoration::Offset:
memberDecorationMap[words[0]][words[1]] = words[3];
break;
default:
return emitError(unknownLoc, "unhandled OpMemberDecoration case: ")
<< words[2];
auto decoration = static_cast<spirv::Decoration>(words[2]);
if (decoration == spirv::Decoration::Offset && words.size() != 4) {
return emitError(unknownLoc,
" missing offset specification in OpMemberDecorate with "
"Offset decoration");
}
ArrayRef<uint32_t> decorationOperands;
if (words.size() > 3) {
decorationOperands = words.slice(3);
}
memberDecorationMap[words[0]][words[1]][decoration] = decorationOperands;
return success();
}
@ -1098,25 +1106,35 @@ LogicalResult Deserializer::processStructType(ArrayRef<uint32_t> operands) {
}
SmallVector<spirv::StructType::LayoutInfo, 0> layoutInfo;
// Check for layoutinfo
auto memberDecorationIt = memberDecorationMap.find(operands[0]);
if (memberDecorationIt != memberDecorationMap.end()) {
// Each member must have an offset
const auto &offsetDecorationMap = memberDecorationIt->second;
auto offsetDecorationMapEnd = offsetDecorationMap.end();
SmallVector<spirv::StructType::MemberDecorationInfo, 0> memberDecorationsInfo;
if (memberDecorationMap.count(operands[0])) {
auto &allMemberDecorations = memberDecorationMap[operands[0]];
for (auto memberIndex : llvm::seq<uint32_t>(0, memberTypes.size())) {
// Check that specific member has an offset
auto offsetIt = offsetDecorationMap.find(memberIndex);
if (offsetIt == offsetDecorationMapEnd) {
return emitError(unknownLoc, "OpTypeStruct with <id> ")
<< operands[0] << " must have an offset for " << memberIndex
<< "-th member";
if (allMemberDecorations.count(memberIndex)) {
for (auto &memberDecoration : allMemberDecorations[memberIndex]) {
// Check for offset.
if (memberDecoration.first == spirv::Decoration::Offset) {
// If layoutInfo is empty, resize to the number of members;
if (layoutInfo.empty()) {
layoutInfo.resize(memberTypes.size());
}
layoutInfo[memberIndex] = memberDecoration.second[0];
} else {
if (!memberDecoration.second.empty()) {
return emitError(unknownLoc,
"unhandled OpMemberDecoration with decoration ")
<< stringifyDecoration(memberDecoration.first)
<< " which has additional operands";
}
memberDecorationsInfo.emplace_back(memberIndex,
memberDecoration.first);
}
}
}
layoutInfo.push_back(
static_cast<spirv::StructType::LayoutInfo>(offsetIt->second));
}
}
typeMap[operands[0]] = spirv::StructType::get(memberTypes, layoutInfo);
typeMap[operands[0]] =
spirv::StructType::get(memberTypes, layoutInfo, memberDecorationsInfo);
return success();
}

View File

@ -167,7 +167,7 @@ private:
/// Process member decoration
LogicalResult processMemberDecoration(uint32_t structID, uint32_t memberNum,
spirv::Decoration decorationType,
uint32_t value);
ArrayRef<uint32_t> values = {});
//===--------------------------------------------------------------------===//
// Types
@ -532,9 +532,12 @@ LogicalResult Serializer::processTypeDecoration<spirv::ArrayType>(
LogicalResult
Serializer::processMemberDecoration(uint32_t structID, uint32_t memberIndex,
spirv::Decoration decorationType,
uint32_t value) {
ArrayRef<uint32_t> values) {
SmallVector<uint32_t, 4> args(
{structID, memberIndex, static_cast<uint32_t>(decorationType), value});
{structID, memberIndex, static_cast<uint32_t>(decorationType)});
if (!values.empty()) {
args.append(values.begin(), values.end());
}
return encodeInstructionInto(decorations, spirv::Opcode::OpMemberDecorate,
args);
}
@ -793,11 +796,21 @@ Serializer::prepareBasicType(Location loc, Type type, uint32_t resultID,
resultID, elementIndex, spirv::Decoration::Offset,
static_cast<uint32_t>(structType.getOffset(elementIndex))))) {
return emitError(loc, "cannot decorate ")
<< elementIndex << "-th member of : " << structType
<< "with its offset";
<< elementIndex << "-th member of " << structType
<< " with its offset";
}
}
}
SmallVector<spirv::StructType::MemberDecorationInfo, 4> memberDecorations;
structType.getMemberDecorations(memberDecorations);
for (auto &memberDecoration : memberDecorations) {
if (failed(processMemberDecoration(resultID, memberDecoration.first,
memberDecoration.second))) {
return emitError(loc, "cannot decorate ")
<< memberDecoration.first << "-th member of " << structType
<< " with " << stringifyDecoration(memberDecoration.second);
}
}
typeEnum = spirv::Opcode::OpTypeStruct;
return success();
}

View File

@ -13,6 +13,15 @@ spv.module "Logical" "GLSL450" {
// CHECK: !spv.ptr<!spv.struct<!spv.array<128 x !spv.struct<!spv.array<128 x f32 [4]> [0]> [4]> [0]>, StorageBuffer>
spv.globalVariable @var3 : !spv.ptr<!spv.struct<!spv.array<128 x !spv.struct<!spv.array<128 x f32 [4]> [0]> [4]> [0]>, StorageBuffer>
// CHECK: !spv.ptr<!spv.struct<f32 [0 NonWritable], i32 [4]>, StorageBuffer>
spv.globalVariable @var4 : !spv.ptr<!spv.struct<f32 [0 NonWritable], i32 [4]>, StorageBuffer>
// CHECK: !spv.ptr<!spv.struct<f32 [NonWritable], i32 [NonWritable NonReadable]>, StorageBuffer>
spv.globalVariable @var5 : !spv.ptr<!spv.struct<f32 [NonWritable], i32 [NonWritable NonReadable]>, StorageBuffer>
// CHECK: !spv.ptr<!spv.struct<f32 [0 NonWritable], i32 [4 NonWritable NonReadable]>, StorageBuffer>
spv.globalVariable @var6 : !spv.ptr<!spv.struct<f32 [0 NonWritable], i32 [4 NonWritable NonReadable]>, StorageBuffer>
// CHECK: !spv.ptr<!spv.struct<!spv.array<128 x f32 [4]> [0]>, Input>,
// CHECK-SAME: !spv.ptr<!spv.struct<!spv.array<128 x f32 [4]> [0]>, Output>
func @kernel_1(%arg0: !spv.ptr<!spv.struct<!spv.array<128 x f32 [4]> [0]>, Input>, %arg1: !spv.ptr<!spv.struct<!spv.array<128 x f32 [4]> [0]>, Output>) -> () {

View File

@ -235,6 +235,24 @@ func @nested_struct(!spv.struct<f32, !spv.struct<f32, i32>>)
// CHECK: func @nested_struct_with_offset(!spv.struct<f32 [0], !spv.struct<f32 [0], i32 [4]> [4]>)
func @nested_struct_with_offset(!spv.struct<f32 [0], !spv.struct<f32 [0], i32 [4]> [4]>)
// CHECK: func @struct_type_with_decoration(!spv.struct<f32 [NonWritable]>)
func @struct_type_with_decoration(!spv.struct<f32 [NonWritable]>)
// CHECK: func @struct_type_with_decoration_and_offset(!spv.struct<f32 [0 NonWritable]>)
func @struct_type_with_decoration_and_offset(!spv.struct<f32 [0 NonWritable]>)
// CHECK: func @struct_type_with_decoration2(!spv.struct<f32 [NonWritable], i32 [NonReadable]>)
func @struct_type_with_decoration2(!spv.struct<f32 [NonWritable], i32 [NonReadable]>)
// CHECK: func @struct_type_with_decoration3(!spv.struct<f32, i32 [NonReadable]>)
func @struct_type_with_decoration3(!spv.struct<f32, i32 [NonReadable]>)
// CHECK: func @struct_type_with_decoration4(!spv.struct<f32 [0], i32 [4 NonReadable]>)
func @struct_type_with_decoration4(!spv.struct<f32 [0], i32 [4 NonReadable]>)
// CHECK: func @struct_type_with_decoration5(!spv.struct<f32 [NonWritable NonReadable]>)
func @struct_type_with_decoration5(!spv.struct<f32 [NonWritable NonReadable]>)
// -----
// expected-error @+1 {{layout specification must be given for all members}}
@ -252,10 +270,25 @@ func @struct_type_missing_comma1(!spv.struct<f32 i32>) -> ()
// -----
// expected-error @+1 {{unexpected extra tokens in layout information: ' i32'}}
// expected-error @+1 {{expected struct member offset/decoration within '[' ']' in 'f32 [0] i32'}}
func @struct_type_missing_comma2(!spv.struct<f32 [0] i32>) -> ()
// -----
// expected-error @+1 {{expected unsigned integer to specify layout info}}
// expected-error @+1 {{unknown attribute: '-1'}}
func @struct_type_neg_offset(!spv.struct<f32 [-1]>) -> ()
// -----
// expected-error @+1 {{unbalanced '>' character in pretty dialect name}}
func @struct_type_neg_offset(!spv.struct<f32 [0>) -> ()
// -----
// expected-error @+1 {{unbalanced ']' character in pretty dialect name}}
func @struct_type_neg_offset(!spv.struct<f32 0]>) -> ()
// -----
// expected-error @+1 {{unknown attribute: '0'}}
func @struct_type_neg_offset(!spv.struct<f32 [NonWritable 0]>) -> ()