SPV: Emit OpSelect when a selection node is simple enough.

Also, ensures it has a type, no disallowed side effects,
or performance trade offs.
This commit is contained in:
John Kessenich
2017-01-26 20:31:11 -07:00
parent 509177d136
commit 433e9ff896
6 changed files with 321 additions and 322 deletions

View File

@@ -1748,42 +1748,94 @@ bool TGlslangToSpvTraverser::visitAggregate(glslang::TVisit visit, glslang::TInt
}
}
// This path handles both if-then-else and ?:
// The if-then-else has a node type of void, while
// ?: has either a void or a non-void node type
//
// Leaving the result, when not void:
// GLSL only has r-values as the result of a :?, but
// if we have an l-value, that can be more efficient if it will
// become the base of a complex r-value expression, because the
// next layer copies r-values into memory to use the access-chain mechanism
bool TGlslangToSpvTraverser::visitSelection(glslang::TVisit /* visit */, glslang::TIntermSelection* node)
{
// This path handles both if-then-else and ?:
// The if-then-else has a node type of void, while
// ?: has a non-void node type
spv::Id result = 0;
if (node->getBasicType() != glslang::EbtVoid) {
// don't handle this as just on-the-fly temporaries, because there will be two names
// and better to leave SSA to later passes
result = builder.createVariable(spv::StorageClassFunction, convertGlslangToSpvType(node->getType()));
// See if it simple and safe to generate OpSelect instead of using control flow.
// Crucially, side effects must be avoided, and there are performance trade-offs.
// Return true if good idea (and safe) for OpSelect, false otherwise.
const auto selectPolicy = [&]() -> bool {
if (node->getBasicType() == glslang::EbtVoid)
return false;
if (node->getTrueBlock() == nullptr ||
node->getFalseBlock() == nullptr)
return false;
assert(node->getType() == node->getTrueBlock() ->getAsTyped()->getType() &&
node->getType() == node->getFalseBlock()->getAsTyped()->getType());
// return true if a single operand to ? : is okay for OpSelect
const auto operandOkay = [](glslang::TIntermTyped* node) {
return node->getAsSymbolNode() || node->getAsConstantUnion();
};
return operandOkay(node->getTrueBlock() ->getAsTyped()) &&
operandOkay(node->getFalseBlock()->getAsTyped());
};
// Emit OpSelect for this selection.
const auto handleAsOpSelect = [&]() {
node->getCondition()->traverse(this);
spv::Id condition = accessChainLoad(node->getCondition()->getType());
node->getTrueBlock()->traverse(this);
spv::Id trueValue = accessChainLoad(node->getTrueBlock()->getAsTyped()->getType());
node->getFalseBlock()->traverse(this);
spv::Id falseValue = accessChainLoad(node->getTrueBlock()->getAsTyped()->getType());
spv::Id select = builder.createTriOp(spv::OpSelect, convertGlslangToSpvType(node->getType()), condition, trueValue, falseValue);
builder.clearAccessChain();
builder.setAccessChainRValue(select);
};
// Try for OpSelect
if (selectPolicy()) {
handleAsOpSelect();
return false;
}
// Instead, emit control flow...
// Don't handle results as temporaries, because there will be two names
// and better to leave SSA to later passes.
spv::Id result = (node->getBasicType() == glslang::EbtVoid)
? spv::NoResult
: builder.createVariable(spv::StorageClassFunction, convertGlslangToSpvType(node->getType()));
// emit the condition before doing anything with selection
node->getCondition()->traverse(this);
// make an "if" based on the value created by the condition
spv::Builder::If ifBuilder(accessChainLoad(node->getCondition()->getType()), builder);
if (node->getTrueBlock()) {
// emit the "then" statement
// emit the "then" statement
if (node->getTrueBlock() != nullptr) {
node->getTrueBlock()->traverse(this);
if (result)
builder.createStore(accessChainLoad(node->getTrueBlock()->getAsTyped()->getType()), result);
if (result != spv::NoResult)
builder.createStore(accessChainLoad(node->getTrueBlock()->getAsTyped()->getType()), result);
}
if (node->getFalseBlock()) {
if (node->getFalseBlock() != nullptr) {
ifBuilder.makeBeginElse();
// emit the "else" statement
node->getFalseBlock()->traverse(this);
if (result)
if (result != spv::NoResult)
builder.createStore(accessChainLoad(node->getFalseBlock()->getAsTyped()->getType()), result);
}
// finish off the control flow
ifBuilder.makeEndIf();
if (result) {
if (result != spv::NoResult) {
// GLSL only has r-values as the result of a :?, but
// if we have an l-value, that can be more efficient if it will
// become the base of a complex r-value expression, because the