Fix generation of conditionals with a struct result in SPIR-V

It is possible for the SPIR-V code generator to receive a conditional
where the two branches have the same shader language level type but
different SPIR-V types because of things like offset decorations. This
change modifies visitSelection() to handle this case by using either
multiTypeStore() or, if that is available, OpCopyLogical.

Fixes #3164
This commit is contained in:
Arcady Goldmints-Orlov 2023-03-31 14:57:49 -06:00 committed by arcady-lunarg
parent f8a2442a16
commit cdb350b356

View File

@ -3789,10 +3789,11 @@ bool TGlslangToSpvTraverser::visitSelection(glslang::TVisit /* visit */, glslang
// Find a way of executing both sides and selecting the right result. // Find a way of executing both sides and selecting the right result.
const auto executeBothSides = [&]() -> void { const auto executeBothSides = [&]() -> void {
// execute both sides // execute both sides
spv::Id resultType = convertGlslangToSpvType(node->getType());
node->getTrueBlock()->traverse(this); node->getTrueBlock()->traverse(this);
spv::Id trueValue = accessChainLoad(node->getTrueBlock()->getAsTyped()->getType()); spv::Id trueValue = accessChainLoad(node->getTrueBlock()->getAsTyped()->getType());
node->getFalseBlock()->traverse(this); node->getFalseBlock()->traverse(this);
spv::Id falseValue = accessChainLoad(node->getTrueBlock()->getAsTyped()->getType()); spv::Id falseValue = accessChainLoad(node->getFalseBlock()->getAsTyped()->getType());
builder.setLine(node->getLoc().line, node->getLoc().getFilename()); builder.setLine(node->getLoc().line, node->getLoc().getFilename());
@ -3801,8 +3802,8 @@ bool TGlslangToSpvTraverser::visitSelection(glslang::TVisit /* visit */, glslang
return; return;
// emit code to select between trueValue and falseValue // emit code to select between trueValue and falseValue
// see if OpSelect can handle the result type, and that the SPIR-V types
// see if OpSelect can handle it // of the inputs match the result type.
if (isOpSelectable()) { if (isOpSelectable()) {
// Emit OpSelect for this selection. // Emit OpSelect for this selection.
@ -3814,10 +3815,18 @@ bool TGlslangToSpvTraverser::visitSelection(glslang::TVisit /* visit */, glslang
builder.getNumComponents(trueValue))); builder.getNumComponents(trueValue)));
} }
// If the types do not match, it is because of mismatched decorations on aggregates.
// Since isOpSelectable only lets us get here for SPIR-V >= 1.4, we can use OpCopyObject
// to get matching types.
if (builder.getTypeId(trueValue) != resultType) {
trueValue = builder.createUnaryOp(spv::OpCopyLogical, resultType, trueValue);
}
if (builder.getTypeId(falseValue) != resultType) {
falseValue = builder.createUnaryOp(spv::OpCopyLogical, resultType, falseValue);
}
// OpSelect // OpSelect
result = builder.createTriOp(spv::OpSelect, result = builder.createTriOp(spv::OpSelect, resultType, condition, trueValue, falseValue);
convertGlslangToSpvType(node->getType()), condition,
trueValue, falseValue);
builder.clearAccessChain(); builder.clearAccessChain();
builder.setAccessChainRValue(result); builder.setAccessChainRValue(result);
@ -3825,7 +3834,7 @@ bool TGlslangToSpvTraverser::visitSelection(glslang::TVisit /* visit */, glslang
// We need control flow to select the result. // We need control flow to select the result.
// TODO: Once SPIR-V OpSelect allows arbitrary types, eliminate this path. // TODO: Once SPIR-V OpSelect allows arbitrary types, eliminate this path.
result = builder.createVariable(TranslatePrecisionDecoration(node->getType()), result = builder.createVariable(TranslatePrecisionDecoration(node->getType()),
spv::StorageClassFunction, convertGlslangToSpvType(node->getType())); spv::StorageClassFunction, resultType);
// Selection control: // Selection control:
const spv::SelectionControlMask control = TranslateSelectionControl(*node); const spv::SelectionControlMask control = TranslateSelectionControl(*node);
@ -3834,10 +3843,15 @@ bool TGlslangToSpvTraverser::visitSelection(glslang::TVisit /* visit */, glslang
spv::Builder::If ifBuilder(condition, control, builder); spv::Builder::If ifBuilder(condition, control, builder);
// emit the "then" statement // emit the "then" statement
builder.createStore(trueValue, result); builder.clearAccessChain();
builder.setAccessChainLValue(result);
multiTypeStore(node->getType(), trueValue);
ifBuilder.makeBeginElse(); ifBuilder.makeBeginElse();
// emit the "else" statement // emit the "else" statement
builder.createStore(falseValue, result); builder.clearAccessChain();
builder.setAccessChainLValue(result);
multiTypeStore(node->getType(), falseValue);
// finish off the control flow // finish off the control flow
ifBuilder.makeEndIf(); ifBuilder.makeEndIf();
@ -3864,16 +3878,26 @@ bool TGlslangToSpvTraverser::visitSelection(glslang::TVisit /* visit */, glslang
// emit the "then" statement // emit the "then" statement
if (node->getTrueBlock() != nullptr) { if (node->getTrueBlock() != nullptr) {
node->getTrueBlock()->traverse(this); node->getTrueBlock()->traverse(this);
if (result != spv::NoResult) if (result != spv::NoResult) {
builder.createStore(accessChainLoad(node->getTrueBlock()->getAsTyped()->getType()), result); spv::Id load = accessChainLoad(node->getTrueBlock()->getAsTyped()->getType());
builder.clearAccessChain();
builder.setAccessChainLValue(result);
multiTypeStore(node->getType(), load);
}
} }
if (node->getFalseBlock() != nullptr) { if (node->getFalseBlock() != nullptr) {
ifBuilder.makeBeginElse(); ifBuilder.makeBeginElse();
// emit the "else" statement // emit the "else" statement
node->getFalseBlock()->traverse(this); node->getFalseBlock()->traverse(this);
if (result != spv::NoResult) if (result != spv::NoResult) {
builder.createStore(accessChainLoad(node->getFalseBlock()->getAsTyped()->getType()), result); spv::Id load = accessChainLoad(node->getFalseBlock()->getAsTyped()->getType());
builder.clearAccessChain();
builder.setAccessChainLValue(result);
multiTypeStore(node->getType(), load);
}
} }
// finish off the control flow // finish off the control flow