Commit ac2fc259 by Shahbaz Youssefi Committed by Angle LUCI CQ

Vulkan: SPIR-V Gen: Support function forward declaration

The generation of function-related ids is moved to visitFunctionPrototype to support forward declarations. The DeferGlobalInitializers AST transformation produces such code. Bug: angleproject:4889 Change-Id: Iefee534575092ca25ff86e416e35703d7022bc07 Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/2939332 Commit-Queue: Shahbaz Youssefi <syoussefi@chromium.org> Reviewed-by: 's avatarTim Van Patten <timvp@google.com> Reviewed-by: 's avatarJamie Madill <jmadill@chromium.org>
parent 013613ea
...@@ -111,6 +111,17 @@ struct NodeData ...@@ -111,6 +111,17 @@ struct NodeData
AccessChain accessChain; AccessChain accessChain;
}; };
struct FunctionIds
{
// Id of the function type, return type and parameter types.
spirv::IdRef functionTypeId;
spirv::IdRef returnTypeId;
spirv::IdRefList parameterTypeIds;
// Id of the function itself.
spirv::IdRef functionId;
};
bool IsAccessChainRValue(const AccessChain &accessChain) bool IsAccessChainRValue(const AccessChain &accessChain)
{ {
return accessChain.storageClass == spv::StorageClassMax; return accessChain.storageClass == spv::StorageClassMax;
...@@ -227,11 +238,13 @@ class OutputSPIRVTraverser : public TIntermTraverser ...@@ -227,11 +238,13 @@ class OutputSPIRVTraverser : public TIntermTraverser
// A map of TSymbol to its SPIR-V id. This could be a: // A map of TSymbol to its SPIR-V id. This could be a:
// //
// - TVariable, or // - TVariable, or
// - TFunction, or
// - TInterfaceBlock: because TIntermSymbols referencing a field of an unnamed interface block // - TInterfaceBlock: because TIntermSymbols referencing a field of an unnamed interface block
// don't reference the TVariable that defines the struct, but the TInterfaceBlock itself. // don't reference the TVariable that defines the struct, but the TInterfaceBlock itself.
angle::HashMap<const TSymbol *, spirv::IdRef> mSymbolIdMap; angle::HashMap<const TSymbol *, spirv::IdRef> mSymbolIdMap;
// A map of TFunction to its various SPIR-V ids.
angle::HashMap<const TFunction *, FunctionIds> mFunctionIdMap;
// Whether the current symbol being visited is being declared. // Whether the current symbol being visited is being declared.
bool mIsSymbolBeingDeclared = false; bool mIsSymbolBeingDeclared = false;
}; };
...@@ -1287,8 +1300,8 @@ spirv::IdRef OutputSPIRVTraverser::createFunctionCall(TIntermAggregate *node, ...@@ -1287,8 +1300,8 @@ spirv::IdRef OutputSPIRVTraverser::createFunctionCall(TIntermAggregate *node,
const TFunction *function = node->getFunction(); const TFunction *function = node->getFunction();
ASSERT(function); ASSERT(function);
ASSERT(mSymbolIdMap.count(function) > 0); ASSERT(mFunctionIdMap.count(function) > 0);
const spirv::IdRef functionId = mSymbolIdMap[function]; const spirv::IdRef functionId = mFunctionIdMap[function].functionId;
// Get the list of parameters passed to the function. The function parameters can only be // Get the list of parameters passed to the function. The function parameters can only be
// memory variables, or if the function argument is |const|, an rvalue. // memory variables, or if the function argument is |const|, an rvalue.
...@@ -2073,41 +2086,26 @@ bool OutputSPIRVTraverser::visitFunctionDefinition(Visit visit, TIntermFunctionD ...@@ -2073,41 +2086,26 @@ bool OutputSPIRVTraverser::visitFunctionDefinition(Visit visit, TIntermFunctionD
{ {
if (visit == PreVisit) if (visit == PreVisit)
{ {
const TFunction *function = node->getFunction(); return true;
}
// Declare the function type
const spirv::IdRef returnTypeId =
mBuilder.getTypeData(function->getReturnType(), EbsUnspecified).id;
spirv::IdRefList paramTypeIds;
for (size_t paramIndex = 0; paramIndex < function->getParamCount(); ++paramIndex)
{
const TType &paramType = function->getParam(paramIndex)->getType();
spirv::IdRef paramId = mBuilder.getTypeData(paramType, EbsUnspecified).id;
// const function parameters are intermediate values, while the rest are "variables"
// with the Function storage class.
if (paramType.getQualifier() != EvqConst)
{
paramId = mBuilder.getTypePointerId(paramId, spv::StorageClassFunction);
}
paramTypeIds.push_back(paramId); // After the prototype is visited, generate the initial code for the function.
} if (visit == InVisit)
{
const TFunction *function = node->getFunction();
const spirv::IdRef functionTypeId = mBuilder.getFunctionTypeId(returnTypeId, paramTypeIds); ASSERT(mFunctionIdMap.count(function) > 0);
const FunctionIds &ids = mFunctionIdMap[function];
// Declare the function itself // Declare the function.
const spirv::IdRef functionId = mBuilder.getNewId(); spirv::WriteFunction(mBuilder.getSpirvFunctions(), ids.returnTypeId, ids.functionId,
spirv::WriteFunction(mBuilder.getSpirvFunctions(), returnTypeId, functionId, spv::FunctionControlMaskNone, ids.functionTypeId);
spv::FunctionControlMaskNone, functionTypeId);
for (size_t paramIndex = 0; paramIndex < function->getParamCount(); ++paramIndex) for (size_t paramIndex = 0; paramIndex < function->getParamCount(); ++paramIndex)
{ {
const spirv::IdRef paramId = mBuilder.getNewId(); const spirv::IdRef paramId = mBuilder.getNewId();
spirv::WriteFunctionParameter(mBuilder.getSpirvFunctions(), paramTypeIds[paramIndex], spirv::WriteFunctionParameter(mBuilder.getSpirvFunctions(),
paramId); ids.parameterTypeIds[paramIndex], paramId);
// Remember the id of the variable for future look up. // Remember the id of the variable for future look up.
const TVariable *paramVariable = function->getParam(paramIndex); const TVariable *paramVariable = function->getParam(paramIndex);
...@@ -2115,38 +2113,25 @@ bool OutputSPIRVTraverser::visitFunctionDefinition(Visit visit, TIntermFunctionD ...@@ -2115,38 +2113,25 @@ bool OutputSPIRVTraverser::visitFunctionDefinition(Visit visit, TIntermFunctionD
mSymbolIdMap[paramVariable] = paramId; mSymbolIdMap[paramVariable] = paramId;
} }
// Remember the ID of main() for the sake of OpEntryPoint. mBuilder.startNewFunction(ids.functionId, mBuilder.hashFunctionName(function).data());
if (function->isMain())
{
mBuilder.setEntryPointId(functionId);
}
mBuilder.startNewFunction(functionId, mBuilder.hashFunctionName(function).data());
// Remember the id of the function for future look up.
ASSERT(mSymbolIdMap.count(function) == 0);
mSymbolIdMap[function] = functionId;
return true; return true;
} }
if (visit == PostVisit) // If no explicit return was specified, add one automatically here.
if (!mBuilder.isCurrentFunctionBlockTerminated())
{ {
// If no explicit return was specified, add one automatically here. // Only meaningful if the function returns void. Otherwise it must have had a return
if (!mBuilder.isCurrentFunctionBlockTerminated()) // value.
{ ASSERT(node->getFunction()->getReturnType().getBasicType() == EbtVoid);
// Only meaningful if the function returns void. Otherwise it must have had a return spirv::WriteReturn(mBuilder.getSpirvCurrentFunctionBlock());
// value. mBuilder.terminateCurrentFunctionBlock();
ASSERT(node->getFunction()->getReturnType().getBasicType() == EbtVoid); }
spirv::WriteReturn(mBuilder.getSpirvCurrentFunctionBlock());
mBuilder.terminateCurrentFunctionBlock();
}
mBuilder.assembleSpirvFunctionBlocks(); mBuilder.assembleSpirvFunctionBlocks();
// End the function // End the function
spirv::WriteFunctionEnd(mBuilder.getSpirvFunctions()); spirv::WriteFunctionEnd(mBuilder.getSpirvFunctions());
}
return true; return true;
} }
...@@ -2162,7 +2147,49 @@ bool OutputSPIRVTraverser::visitGlobalQualifierDeclaration(Visit visit, ...@@ -2162,7 +2147,49 @@ bool OutputSPIRVTraverser::visitGlobalQualifierDeclaration(Visit visit,
void OutputSPIRVTraverser::visitFunctionPrototype(TIntermFunctionPrototype *node) void OutputSPIRVTraverser::visitFunctionPrototype(TIntermFunctionPrototype *node)
{ {
// Nothing to do. The function type is declared together with its definition. const TFunction *function = node->getFunction();
// If the function was previously forward declared, skip this.
if (mFunctionIdMap.count(function) > 0)
{
return;
}
FunctionIds ids;
// Declare the function type
ids.returnTypeId = mBuilder.getTypeData(function->getReturnType(), EbsUnspecified).id;
spirv::IdRefList paramTypeIds;
for (size_t paramIndex = 0; paramIndex < function->getParamCount(); ++paramIndex)
{
const TType &paramType = function->getParam(paramIndex)->getType();
spirv::IdRef paramId = mBuilder.getTypeData(paramType, EbsUnspecified).id;
// const function parameters are intermediate values, while the rest are "variables"
// with the Function storage class.
if (paramType.getQualifier() != EvqConst)
{
paramId = mBuilder.getTypePointerId(paramId, spv::StorageClassFunction);
}
ids.parameterTypeIds.push_back(paramId);
}
ids.functionTypeId = mBuilder.getFunctionTypeId(ids.returnTypeId, ids.parameterTypeIds);
// Allocate an id for the function up-front.
ids.functionId = mBuilder.getNewId();
// Remember the ID of main() for the sake of OpEntryPoint.
if (function->isMain())
{
mBuilder.setEntryPointId(ids.functionId);
}
// Remember the id of the function for future look up.
mFunctionIdMap[function] = ids;
} }
bool OutputSPIRVTraverser::visitAggregate(Visit visit, TIntermAggregate *node) bool OutputSPIRVTraverser::visitAggregate(Visit visit, TIntermAggregate *node)
......
...@@ -10880,6 +10880,32 @@ void main() ...@@ -10880,6 +10880,32 @@ void main()
glDeleteShader(shader); glDeleteShader(shader);
} }
// Test that initializing global variables with non-constant values work
TEST_P(GLSLTest_ES3, InitGlobalNonConstant)
{
ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_EXT_shader_non_constant_global_initializers"));
constexpr char kVS[] = R"(#version 300 es
#extension GL_EXT_shader_non_constant_global_initializers : require
uniform U
{
vec4 u;
} u;
out vec4 color;
vec4 global1 = u.u;
vec4 global2 = u.u + vec4(1);
vec4 global3 = global1 * global2;
void main()
{
color = global3;
})";
GLuint shader = CompileShader(GL_VERTEX_SHADER, kVS);
EXPECT_NE(0u, shader);
glDeleteShader(shader);
}
} // anonymous namespace } // anonymous namespace
ANGLE_INSTANTIATE_TEST_ES2_AND_ES3(GLSLTest); ANGLE_INSTANTIATE_TEST_ES2_AND_ES3(GLSLTest);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment