Skip to content

Commit 8f279ba

Browse files
authored
spirv: get field index from SPIR-V type, not AST (#4806)
Before this commit, struct field indices were computed from the AST type. This was correct as SPIR-V types and AST types had the same structure. When adding bitfields, we started to squash some fields together, as long as some rules were respected (bitfield, same type, doesn't span over multiple type-sized words, etc). This means indices now diverge between SPIR-V types and AST types. This requires us to lower the AST type before the lowering pass, when generating instructions like OpAccessChain. A cleaner alternative would be to lower all types before instruction generation, and only operate on SPIR-V type. But cost of making this refactoring is large, and we don't believe it is worth it since we plan to upstream to clang (hence rewrite this code). note: this code prevents AST indices to diverge from SPIR-V indices when computed. This is just a safeguard until bitfields are in place in case I made a mistake with the layout rules, allowing us to catch those bugs faster. Signed-off-by: Nathan Gauër <[email protected]>
1 parent f12b050 commit 8f279ba

1 file changed

Lines changed: 77 additions & 17 deletions

File tree

tools/clang/lib/SPIRV/SpirvEmitter.cpp

Lines changed: 77 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515

1616
#include "AlignmentSizeCalculator.h"
1717
#include "InitListHandler.h"
18+
#include "LowerTypeVisitor.h"
1819
#include "RawBufferMethods.h"
1920
#include "dxc/DXIL/DxilConstants.h"
2021
#include "dxc/HlslIntrinsicOp.h"
@@ -542,6 +543,71 @@ bool isVkRawBufferLoadIntrinsic(const clang::FunctionDecl *FD) {
542543
return true;
543544
}
544545

546+
// Takes an AST member type, and determines its index in the equivalent SPIR-V
547+
// struct type. This is required as the struct layout might change between the
548+
// AST representation and SPIR-V representation.
549+
uint32_t getFieldIndexInStruct(const SpirvCodeGenOptions &spirvOptions,
550+
LowerTypeVisitor &lowerTypeVisitor,
551+
const MemberExpr *expr) {
552+
// If we are accessing a derived struct, we need to account for the number
553+
// of base structs, since they are placed as fields at the beginning of the
554+
// derived struct.
555+
auto baseType = expr->getBase()->getType();
556+
if (baseType->isPointerType()) {
557+
baseType = baseType->getPointeeType();
558+
}
559+
560+
const auto *fieldDecl =
561+
dynamic_cast<const FieldDecl *>(expr->getMemberDecl());
562+
assert(fieldDecl);
563+
const uint32_t indexAST =
564+
getNumBaseClasses(baseType) + fieldDecl->getFieldIndex();
565+
566+
// The AST type index is not representative of the SPIR-V type index
567+
// because we might squash some fields (bitfields by ex.).
568+
// What we need is to match each AST node with the squashed field and then,
569+
// determine the real index.
570+
const SpirvType *spvType = lowerTypeVisitor.lowerType(
571+
baseType, spirvOptions.sBufferLayoutRule, llvm::None, SourceLocation());
572+
assert(spvType);
573+
574+
const auto st = dynamic_cast<const StructType *>(spvType);
575+
assert(st != nullptr);
576+
const auto &fields = st->getFields();
577+
assert(indexAST <= fields.size());
578+
579+
// Some fields in SPIR-V share the same index (bitfields). Computing the final
580+
// index of the requested field.
581+
uint32_t indexSPV = 0;
582+
for (size_t i = 1; i <= indexAST; i++) {
583+
// Do not remove this condition. This is required to support inheritance:
584+
// 1. SPIR-V composite first element is the parent type:
585+
// by ex "OpTypeStruct %base_struct %float".
586+
// 2. if the parent type is an empty class, it's size it zero, hence
587+
// "%float" offset is also 0.
588+
//
589+
// A way to detect such cases is to check for type difference: fields cannot
590+
// be merged if the type is different.
591+
if (fields[i - 1].type != fields[i].type) {
592+
indexSPV++;
593+
continue;
594+
}
595+
596+
if (fields[i - 1].offset.getValueOr(0) != fields[i].offset.getValueOr(0)) {
597+
indexSPV++;
598+
continue;
599+
}
600+
}
601+
602+
// TODO(issue #4140): remove once bitfields are implemented.
603+
// This is just a safeguard until bitfield support is in. Before bitfields,
604+
// AST indices were always correct, so this function should not change that
605+
// behavior. Once the bitfield support is in, indices will start to diverge,
606+
// and this assert should be removed.
607+
assert(indexSPV == indexAST);
608+
return indexSPV;
609+
}
610+
545611
} // namespace
546612

547613
SpirvEmitter::SpirvEmitter(CompilerInstance &ci)
@@ -7614,23 +7680,17 @@ const Expr *SpirvEmitter::collectArrayStructIndices(
76147680
}
76157681
}
76167682

7617-
// Append the index of the current level
7618-
const auto *fieldDecl = cast<FieldDecl>(indexing->getMemberDecl());
7619-
assert(fieldDecl);
7620-
// If we are accessing a derived struct, we need to account for the number
7621-
// of base structs, since they are placed as fields at the beginning of the
7622-
// derived struct.
7623-
auto baseType = indexing->getBase()->getType();
7624-
if (baseType->isPointerType()) {
7625-
baseType = baseType->getPointeeType();
7626-
}
7627-
const uint32_t index =
7628-
getNumBaseClasses(baseType) + fieldDecl->getFieldIndex();
7629-
if (rawIndex) {
7630-
rawIndices->push_back(index);
7631-
} else {
7632-
indices->push_back(spvBuilder.getConstantInt(
7633-
astContext.IntTy, llvm::APInt(32, index, true)));
7683+
{
7684+
LowerTypeVisitor lowerTypeVisitor(astContext, spvContext, spirvOptions);
7685+
const uint32_t fieldIndex =
7686+
getFieldIndexInStruct(spirvOptions, lowerTypeVisitor, indexing);
7687+
7688+
if (rawIndex) {
7689+
rawIndices->push_back(fieldIndex);
7690+
} else {
7691+
indices->push_back(spvBuilder.getConstantInt(
7692+
astContext.IntTy, llvm::APInt(32, fieldIndex, true)));
7693+
}
76347694
}
76357695

76367696
return base;

0 commit comments

Comments
 (0)