Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 20 additions & 8 deletions tools/clang/lib/SPIRV/SpirvEmitter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3951,8 +3951,16 @@ SpirvEmitter::processFlatConversion(const QualType type,
initInstr->setAstResultType(astContext.UnsignedLongLongTy);
}

// Decompose `initInstr`.
std::vector<SpirvInstruction *> flatValues = decomposeToScalars(initInstr);
QualType sourceType = initInstr->getAstResultType();
if (hlsl::IsHLSLResourceType(sourceType))
sourceType = hlsl::GetHLSLResourceResultType(sourceType);

// Converting the same AST type between layouts preserves its physical field
// sequence. Shape-changing flat conversions operate on AST fields.
Comment on lines +3958 to +3959

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This comment confused me as it states the truth, but doesn't mention how that should impact merged bitfields. The PR comment has a good description that I've slightly expanded.

Suggested change
// Converting the same AST type between layouts preserves its physical field
// sequence. Shape-changing flat conversions operate on AST fields.
// A same-type conversion emits one scalar per SPIR-V field, but a cast that
// changes the shape, keeps one scalar (potentially with merged bitfields) per
// AST field. includeMergedBitfields determines which indexing is used.

const bool includeMergedBitfields =
!astContext.hasSameUnqualifiedType(type, sourceType);
std::vector<SpirvInstruction *> flatValues =
decomposeToScalars(initInstr, includeMergedBitfields);

if (flatValues.size() == 1) {
return splatScalarToGenerate(type, flatValues[0], SpirvLayoutRule::Void);
Expand Down Expand Up @@ -16783,7 +16791,8 @@ SpirvEmitter::doUnaryExprOrTypeTraitExpr(const UnaryExprOrTypeTraitExpr *expr) {
}

std::vector<SpirvInstruction *>
SpirvEmitter::decomposeToScalars(SpirvInstruction *inst) {
SpirvEmitter::decomposeToScalars(SpirvInstruction *inst,
bool includeMergedBitfields) {
QualType elementType;
uint32_t elementCount = 0;
uint32_t numOfRows = 0;
Expand Down Expand Up @@ -16828,7 +16837,8 @@ SpirvEmitter::decomposeToScalars(SpirvInstruction *inst) {
auto *element = spvBuilder.createCompositeExtract(
elementType, inst, {i}, inst->getSourceLocation());
element->setLayoutRule(inst->getLayoutRule());
auto decomposedElement = decomposeToScalars(element);
auto decomposedElement =
decomposeToScalars(element, includeMergedBitfields);

// See how we can improve the performance by avoiding this copy.
result.insert(result.end(), decomposedElement.begin(),
Expand All @@ -16848,20 +16858,22 @@ SpirvEmitter::decomposeToScalars(SpirvInstruction *inst) {

forEachSpirvField(
recordType, dyn_cast<StructType>(type),
[this, inst, &result](size_t spirvFieldIndex, const QualType &fieldType,
const StructType::FieldInfo &fieldInfo) {
[this, inst, &result, includeMergedBitfields](
size_t spirvFieldIndex, const QualType &fieldType,
const StructType::FieldInfo &fieldInfo) {
auto *field = spvBuilder.createCompositeExtract(
fieldType, inst, {fieldInfo.fieldIndex},
inst->getSourceLocation());
field->setLayoutRule(inst->getLayoutRule());
auto decomposedField = decomposeToScalars(field);
auto decomposedField =
decomposeToScalars(field, includeMergedBitfields);

// See how we can improve the performance by avoiding this copy.
result.insert(result.end(), decomposedField.begin(),
decomposedField.end());
return true;
},
true);
includeMergedBitfields);
return result;
}

Expand Down
6 changes: 4 additions & 2 deletions tools/clang/lib/SPIRV/SpirvEmitter.h
Original file line number Diff line number Diff line change
Expand Up @@ -1404,8 +1404,10 @@ class SpirvEmitter : public ASTConsumer {

/// Returns a vector of SpirvInstruction that is the decompostion of `inst`
/// into scalars. This is recursive. For example, a struct of a 4 element
/// vector will return 4 scalars.
std::vector<SpirvInstruction *> decomposeToScalars(SpirvInstruction *inst);
/// vector will return 4 scalars. If `includeMergedBitfields` is false,
/// fields that share the same SPIR-V storage field produce one scalar.
std::vector<SpirvInstruction *>
decomposeToScalars(SpirvInstruction *inst, bool includeMergedBitfields);

/// Returns a spirv instruction with the value of the given type and layout
/// rule that is obtained by assigning each scalar in `type` to corresponding
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
// RUN: %dxc -T ps_6_0 -E main -HV 2021 -fcgl %s -spirv | FileCheck %s

struct MyData {
uint a : 16;
uint b : 16;
uint c;
};

ConstantBuffer<MyData> input;

uint main() : SV_Target {
// CHECK: [[SOURCE:%[0-9]+]] = OpLoad {{%[^ ]+}} %input
// CHECK-NEXT: [[BITFIELDS:%[0-9]+]] = OpCompositeExtract %uint [[SOURCE]] 0
// CHECK-NEXT: [[C:%[0-9]+]] = OpCompositeExtract %uint [[SOURCE]] 1
// CHECK-NEXT: [[VALUE:%[0-9]+]] = OpCompositeConstruct {{%[^ ]+}} [[BITFIELDS]] [[C]]
// CHECK-NEXT: OpStore %local [[VALUE]]
MyData local = input;

// CHECK: [[C_PTR:%[0-9]+]] = OpAccessChain %_ptr_Function_uint %local %int_1
// CHECK: [[C_VALUE:%[0-9]+]] = OpLoad %uint [[C_PTR]]
// CHECK: OpReturnValue [[C_VALUE]]
return local.c;
}
Loading