Merge pull request #2625 from amdrexu/feature

Implement the extension GL_EXT_spirv_intrinsics
This commit is contained in:
Greg Fischer 2021-06-16 14:52:35 -06:00 committed by GitHub
commit 71612a7e5d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
40 changed files with 7337 additions and 3967 deletions

View file

@ -1092,12 +1092,31 @@ TFunction* TParseContext::handleFunctionDeclarator(const TSourceLoc& loc, TFunct
TSymbol* symbol = symbolTable.find(function.getMangledName(), &builtIn);
if (symbol && symbol->getAsFunction() && builtIn)
requireProfile(loc, ~EEsProfile, "redefinition of built-in function");
#ifndef GLSLANG_WEB
// Check the validity of using spirv_literal qualifier
for (int i = 0; i < function.getParamCount(); ++i) {
if (function[i].type->getQualifier().isSpirvLiteral() && function.getBuiltInOp() != EOpSpirvInst)
error(loc, "'spirv_literal' can only be used on functions defined with 'spirv_instruction' for argument",
function.getName().c_str(), "%d", i + 1);
}
// For function declaration with SPIR-V instruction qualifier, always ignore the built-in function and
// respect this redeclared one.
if (symbol && builtIn && function.getBuiltInOp() == EOpSpirvInst)
symbol = nullptr;
#endif
const TFunction* prevDec = symbol ? symbol->getAsFunction() : 0;
if (prevDec) {
if (prevDec->isPrototyped() && prototype)
profileRequires(loc, EEsProfile, 300, nullptr, "multiple prototypes for same function");
if (prevDec->getType() != function.getType())
error(loc, "overloaded functions must have the same return type", function.getName().c_str(), "");
#ifndef GLSLANG_WEB
if (prevDec->getSpirvInstruction() != function.getSpirvInstruction()) {
error(loc, "overloaded functions must have the same qualifiers", function.getName().c_str(),
"spirv_instruction");
}
#endif
for (int i = 0; i < prevDec->getParamCount(); ++i) {
if ((*prevDec)[i].type->getQualifier().storage != function[i].type->getQualifier().storage)
error(loc, "overloaded functions must have the same parameter storage qualifiers for argument", function[i].type->getStorageQualifierString(), "%d", i+1);
@ -1299,6 +1318,15 @@ TIntermTyped* TParseContext::handleFunctionCall(const TSourceLoc& loc, TFunction
if (lValueErrorCheck(arguments->getLoc(), "assign", arg->getAsTyped()))
error(arguments->getLoc(), "Non-L-value cannot be passed for 'out' or 'inout' parameters.", "out", "");
}
#ifndef GLSLANG_WEB
if (formalQualifier.isSpirvLiteral()) {
if (!arg->getAsTyped()->getQualifier().isFrontEndConstant()) {
error(arguments->getLoc(),
"Non front-end constant expressions cannot be passed for 'spirv_literal' parameters.",
"spirv_literal", "");
}
}
#endif
const TType& argType = arg->getAsTyped()->getType();
const TQualifier& argQualifier = argType.getQualifier();
if (argQualifier.isMemory() && (argType.containsOpaque() || argType.isReference())) {
@ -1353,6 +1381,11 @@ TIntermTyped* TParseContext::handleFunctionCall(const TSourceLoc& loc, TFunction
if (builtIn && fnCandidate->getBuiltInOp() != EOpNull) {
// A function call mapped to a built-in operation.
result = handleBuiltInFunctionCall(loc, arguments, *fnCandidate);
#ifndef GLSLANG_WEB
} else if (fnCandidate->getBuiltInOp() == EOpSpirvInst) {
// When SPIR-V instruction qualifier is specified, the function call is still mapped to a built-in operation.
result = handleBuiltInFunctionCall(loc, arguments, *fnCandidate);
#endif
} else {
// This is a function call not mapped to built-in operator.
// It could still be a built-in function, but only if PureOperatorBuiltins == false.
@ -1430,6 +1463,35 @@ TIntermTyped* TParseContext::handleBuiltInFunctionCall(TSourceLoc loc, TIntermNo
} else if (result->getAsOperator())
builtInOpCheck(loc, function, *result->getAsOperator());
#ifndef GLSLANG_WEB
// Special handling for function call with SPIR-V instruction qualifier specified
if (function.getBuiltInOp() == EOpSpirvInst) {
if (auto agg = result->getAsAggregate()) {
// Propogate spirv_by_reference/spirv_literal from parameters to arguments
auto& sequence = agg->getSequence();
for (unsigned i = 0; i < sequence.size(); ++i) {
if (function[i].type->getQualifier().isSpirvByReference())
sequence[i]->getAsTyped()->getQualifier().setSpirvByReference();
if (function[i].type->getQualifier().isSpirvLiteral())
sequence[i]->getAsTyped()->getQualifier().setSpirvLiteral();
}
// Attach the function call to SPIR-V intruction
agg->setSpirvInstruction(function.getSpirvInstruction());
} else if (auto unaryNode = result->getAsUnaryNode()) {
// Propogate spirv_by_reference/spirv_literal from parameters to arguments
if (function[0].type->getQualifier().isSpirvByReference())
unaryNode->getOperand()->getQualifier().setSpirvByReference();
if (function[0].type->getQualifier().isSpirvLiteral())
unaryNode->getOperand()->getQualifier().setSpirvLiteral();
// Attach the function call to SPIR-V intruction
unaryNode->setSpirvInstruction(function.getSpirvInstruction());
} else
assert(0);
}
#endif
return result;
}
@ -2931,7 +2993,8 @@ void TParseContext::reservedErrorCheck(const TSourceLoc& loc, const TString& ide
// "Identifiers starting with "gl_" are reserved for use by OpenGL, and may not be
// declared in a shader; this results in a compile-time error."
if (! symbolTable.atBuiltInLevel()) {
if (builtInName(identifier))
if (builtInName(identifier) && !extensionTurnedOn(E_GL_EXT_spirv_intrinsics))
// The extension GL_EXT_spirv_intrinsics allows us to declare identifiers starting with "gl_".
error(loc, "identifiers starting with \"gl_\" are reserved", identifier.c_str(), "");
// "__" are not supposed to be an error. ES 300 (and desktop) added the clarification:
@ -2939,7 +3002,8 @@ void TParseContext::reservedErrorCheck(const TSourceLoc& loc, const TString& ide
// reserved; using such a name does not itself result in an error, but may result
// in undefined behavior."
// however, before that, ES tests required an error.
if (identifier.find("__") != TString::npos) {
if (identifier.find("__") != TString::npos && !extensionTurnedOn(E_GL_EXT_spirv_intrinsics)) {
// The extension GL_EXT_spirv_intrinsics allows us to declare identifiers starting with "__".
if (isEsProfile() && version < 300)
error(loc, "identifiers containing consecutive underscores (\"__\") are reserved, and an error if version < 300", identifier.c_str(), "");
else
@ -2960,14 +3024,16 @@ void TParseContext::reservedPpErrorCheck(const TSourceLoc& loc, const char* iden
// single underscore) are also reserved, and defining such a name results in a
// compile-time error."
// however, before that, ES tests required an error.
if (strncmp(identifier, "GL_", 3) == 0)
if (strncmp(identifier, "GL_", 3) == 0 && !extensionTurnedOn(E_GL_EXT_spirv_intrinsics))
// The extension GL_EXT_spirv_intrinsics allows us to declare macros prefixed with "GL_".
ppError(loc, "names beginning with \"GL_\" can't be (un)defined:", op, identifier);
else if (strncmp(identifier, "defined", 8) == 0)
if (relaxedErrors())
ppWarn(loc, "\"defined\" is (un)defined:", op, identifier);
else
ppError(loc, "\"defined\" can't be (un)defined:", op, identifier);
else if (strstr(identifier, "__") != 0) {
else if (strstr(identifier, "__") != 0 && !extensionTurnedOn(E_GL_EXT_spirv_intrinsics)) {
// The extension GL_EXT_spirv_intrinsics allows us to declare macros prefixed with "__".
if (isEsProfile() && version >= 300 &&
(strcmp(identifier, "__LINE__") == 0 ||
strcmp(identifier, "__FILE__") == 0 ||
@ -3594,6 +3660,14 @@ void TParseContext::globalQualifierFixCheck(const TSourceLoc& loc, TQualifier& q
if (!nonuniformOkay && qualifier.isNonUniform())
error(loc, "for non-parameter, can only apply to 'in' or no storage qualifier", "nonuniformEXT", "");
#ifndef GLSLANG_WEB
if (qualifier.isSpirvByReference())
error(loc, "can only apply to parameter", "spirv_by_reference", "");
if (qualifier.isSpirvLiteral())
error(loc, "can only apply to parameter", "spirv_literal", "");
#endif
// Storage qualifier isn't ready for memberQualifierCheck, we should skip invariantCheck for it.
if (!isMemberCheck || structNestingLevel > 0)
invariantCheck(loc, qualifier);
@ -3855,6 +3929,41 @@ void TParseContext::mergeQualifiers(const TSourceLoc& loc, TQualifier& dst, cons
MERGE_SINGLETON(nonUniform);
#endif
#ifndef GLSLANG_WEB
// SPIR-V storage class qualifier (GL_EXT_spirv_intrinsics)
dst.spirvStorageClass = src.spirvStorageClass;
// SPIR-V decorate qualifiers (GL_EXT_spirv_intrinsics)
if (src.hasSprivDecorate()) {
if (dst.hasSprivDecorate()) {
const TSpirvDecorate& srcSpirvDecorate = src.getSpirvDecorate();
TSpirvDecorate& dstSpirvDecorate = dst.getSpirvDecorate();
for (auto& decorate : srcSpirvDecorate.decorates) {
if (dstSpirvDecorate.decorates.find(decorate.first) != dstSpirvDecorate.decorates.end())
error(loc, "too many SPIR-V decorate qualifiers", "spirv_decorate", "(decoration=%u)", decorate.first);
else
dstSpirvDecorate.decorates.insert(decorate);
}
for (auto& decorateId : srcSpirvDecorate.decorateIds) {
if (dstSpirvDecorate.decorateIds.find(decorateId.first) != dstSpirvDecorate.decorateIds.end())
error(loc, "too many SPIR-V decorate qualifiers", "spirv_decorate_id", "(decoration=%u)", decorateId.first);
else
dstSpirvDecorate.decorateIds.insert(decorateId);
}
for (auto& decorateString : srcSpirvDecorate.decorateStrings) {
if (dstSpirvDecorate.decorates.find(decorateString.first) != dstSpirvDecorate.decorates.end())
error(loc, "too many SPIR-V decorate qualifiers", "spirv_decorate_string", "(decoration=%u)", decorateString.first);
else
dstSpirvDecorate.decorates.insert(decorateString);
}
} else {
dst.spirvDecorate = src.spirvDecorate;
}
}
#endif
if (repeated)
error(loc, "replicated qualifiers", "", "");
}
@ -4818,6 +4927,17 @@ void TParseContext::paramCheckFix(const TSourceLoc& loc, const TQualifier& quali
}
if (qualifier.isNonUniform())
type.getQualifier().nonUniform = qualifier.nonUniform;
#ifndef GLSLANG_WEB
if (qualifier.isSpirvByReference())
type.getQualifier().setSpirvByReference();
if (qualifier.isSpirvLiteral()) {
if (type.getBasicType() == EbtFloat || type.getBasicType() == EbtInt || type.getBasicType() == EbtUint ||
type.getBasicType() == EbtBool)
type.getQualifier().setSpirvLiteral();
else
error(loc, "cannot use spirv_literal qualifier", type.getBasicTypeString().c_str(), "");
#endif
}
paramCheckFixStorage(loc, qualifier.storage, type);
}
@ -5885,6 +6005,9 @@ void TParseContext::layoutObjectCheck(const TSourceLoc& loc, const TSymbol& symb
case EvqVaryingIn:
case EvqVaryingOut:
if (!type.getQualifier().isTaskMemory() &&
#ifndef GLSLANG_WEB
!type.getQualifier().hasSprivDecorate() &&
#endif
(type.getBasicType() != EbtBlock ||
(!(*type.getStruct())[0].type->getQualifier().hasLocation() &&
(*type.getStruct())[0].type->getQualifier().builtIn == EbvNone)))
@ -5946,6 +6069,11 @@ void TParseContext::layoutMemberLocationArrayCheck(const TSourceLoc& loc, bool m
// Do layout error checking with respect to a type.
void TParseContext::layoutTypeCheck(const TSourceLoc& loc, const TType& type)
{
#ifndef GLSLANG_WEB
if (extensionTurnedOn(E_GL_EXT_spirv_intrinsics))
return; // Skip any check if GL_EXT_spirv_intrinsics is turned on
#endif
const TQualifier& qualifier = type.getQualifier();
// first, intra-layout qualifier-only error checking
@ -7952,6 +8080,10 @@ void TParseContext::declareBlock(const TSourceLoc& loc, TTypeList& typeList, con
memberQualifier.perViewNV = currentBlockQualifier.perViewNV;
if (currentBlockQualifier.perTaskNV)
memberQualifier.perTaskNV = currentBlockQualifier.perTaskNV;
if (memberQualifier.storage == EvqSpirvStorageClass)
error(memberLoc, "member cannot have a spirv_storage_class qualifier", memberType.getFieldName().c_str(), "");
if (memberQualifier.hasSprivDecorate() && !memberQualifier.getSpirvDecorate().decorateIds.empty())
error(memberLoc, "member cannot have a spirv_decorate_id qualifier", memberType.getFieldName().c_str(), "");
#endif
if ((currentBlockQualifier.storage == EvqUniform || currentBlockQualifier.storage == EvqBuffer) && (memberQualifier.isInterpolation() || memberQualifier.isAuxiliary()))
error(memberLoc, "member of uniform or buffer block cannot have an auxiliary or interpolation qualifier", memberType.getFieldName().c_str(), "");