Add --no-link option

Adds the --no-link option which outputs the compiled shader binaries
without linking them. This is a first step towards allowing users to
create SPIR-v binary, non-executable libraries.

When using the --no-link option, all functions are decorated with the
Export linkage attribute.
This commit is contained in:
Nathaniel Cesario 2023-08-17 13:49:18 -06:00 committed by arcady-lunarg
parent a4aceb57de
commit 4c57db1595
24 changed files with 1671 additions and 1454 deletions

View file

@ -101,6 +101,8 @@ namespace glslang {
if (name == "nonwritable") return EatNonWritable;
if (name == "nonreadable") return EatNonReadable;
if (name == "export") return EatExport;
} else if (nameSpace.size() > 0)
return EatNone;

View file

@ -226,6 +226,7 @@ typedef struct glslang_spv_options_s {
bool validate;
bool emit_nonsemantic_shader_debug_info;
bool emit_nonsemantic_shader_debug_source;
bool compile_only;
} glslang_spv_options_t;
#ifdef __cplusplus

View file

@ -1108,6 +1108,11 @@ enum TOperator {
EOpImageBlockMatchSSDQCOM,
};
enum TLinkType {
ELinkNone,
ELinkExport,
};
class TIntermTraverser;
class TIntermOperator;
class TIntermAggregate;
@ -1325,9 +1330,11 @@ public:
virtual const TString& getMethodName() const { return method; }
virtual TIntermTyped* getObject() const { return object; }
virtual void traverse(TIntermTraverser*);
void setExport() { linkType = ELinkExport; }
protected:
TIntermTyped* object;
TString method;
TLinkType linkType;
};
//
@ -1700,6 +1707,9 @@ public:
const TPragmaTable& getPragmaTable() const { return *pragmaTable; }
void setSpirvInstruction(const TSpirvInstruction& inst) { spirvInst = inst; }
const TSpirvInstruction& getSpirvInstruction() const { return spirvInst; }
void setLinkType(TLinkType l) { linkType = l; }
TLinkType getLinkType() const { return linkType; }
protected:
TIntermAggregate(const TIntermAggregate&); // disallow copy constructor
TIntermAggregate& operator=(const TIntermAggregate&); // disallow assignment operator
@ -1711,6 +1721,7 @@ protected:
bool debug;
TPragmaTable* pragmaTable;
TSpirvInstruction spirvInst;
TLinkType linkType = ELinkNone;
};
//

View file

@ -1243,6 +1243,8 @@ TIntermAggregate* TParseContext::handleFunctionDefinition(const TSourceLoc& loc,
error(loc, "function cannot take any parameter(s)", function.getName().c_str(), "");
if (function.getType().getBasicType() != EbtVoid)
error(loc, "", function.getType().getBasicTypeString().c_str(), "entry point cannot return a value");
if (function.getLinkType() != ELinkNone)
error(loc, "main function cannot be exported", "", "");
}
//
@ -1279,6 +1281,7 @@ TIntermAggregate* TParseContext::handleFunctionDefinition(const TSourceLoc& loc,
} else
paramNodes = intermediate.growAggregate(paramNodes, intermediate.addSymbol(*param.type, loc), loc);
}
paramNodes->setLinkType(function.getLinkType());
intermediate.setAggregateOperator(paramNodes, EOpParameters, TType(EbtVoid), loc);
loopNestingLevel = 0;
statementNestingLevel = 0;

View file

@ -196,6 +196,7 @@ public:
struct TPragma contextPragma;
int beginInvocationInterlockCount;
int endInvocationInterlockCount;
bool compileOnly = false;
protected:
TParseContextBase(TParseContextBase&);

View file

@ -796,7 +796,8 @@ bool ProcessDeferred(
bool requireNonempty,
TShader::Includer& includer,
const std::string sourceEntryPointName = "",
const TEnvironment* environment = nullptr) // optional way of fully setting all versions, overriding the above
const TEnvironment* environment = nullptr, // optional way of fully setting all versions, overriding the above
bool compileOnly = false)
{
// This must be undone (.pop()) by the caller, after it finishes consuming the created tree.
GetThreadPoolAllocator().push();
@ -942,6 +943,7 @@ bool ProcessDeferred(
std::unique_ptr<TParseContextBase> parseContext(CreateParseContext(*symbolTable, intermediate, version, profile, source,
stage, compiler->infoSink,
spvVersion, forwardCompatible, messages, false, sourceEntryPointName));
parseContext->compileOnly = compileOnly;
TPpContext ppContext(*parseContext, names[numPre] ? names[numPre] : "", includer);
// only GLSL (bison triggered, really) needs an externally set scan context
@ -1279,14 +1281,15 @@ bool CompileDeferred(
TIntermediate& intermediate,// returned tree, etc.
TShader::Includer& includer,
const std::string sourceEntryPointName = "",
TEnvironment* environment = nullptr)
TEnvironment* environment = nullptr,
bool compileOnly = false)
{
DoFullParse parser;
return ProcessDeferred(compiler, shaderStrings, numStrings, inputLengths, stringNames,
preamble, optLevel, resources, defaultVersion,
defaultProfile, forceDefaultVersionAndProfile, overrideVersion,
forwardCompatible, messages, intermediate, parser,
true, includer, sourceEntryPointName, environment);
true, includer, sourceEntryPointName, environment, compileOnly);
}
} // end anonymous namespace for local functions
@ -1867,7 +1870,7 @@ bool TShader::parse(const TBuiltInResource* builtInResources, int defaultVersion
preamble, EShOptNone, builtInResources, defaultVersion,
defaultProfile, forceDefaultVersionAndProfile, overrideVersion,
forwardCompatible, messages, *intermediate, includer, sourceEntryPointName,
&environment);
&environment, compileOnly);
}
// Fill in a string with the result of preprocessing ShaderStrings

View file

@ -246,7 +246,8 @@ public:
TSymbol(name),
mangledName(*name + '('),
op(tOp),
defined(false), prototyped(false), implicitThis(false), illegalImplicitThis(false), defaultParamCount(0)
defined(false), prototyped(false), implicitThis(false), illegalImplicitThis(false), defaultParamCount(0),
linkType(ELinkNone)
{
returnType.shallowCopy(retType);
declaredBuiltIn = retType.getQualifier().builtIn;
@ -326,6 +327,9 @@ public:
virtual void dump(TInfoSink& infoSink, bool complete = false) const override;
void setExport() { linkType = ELinkExport; }
TLinkType getLinkType() const { return linkType; }
protected:
explicit TFunction(const TFunction&);
TFunction& operator=(const TFunction&);
@ -347,6 +351,7 @@ protected:
int defaultParamCount;
TSpirvInstruction spirvInst; // SPIR-V instruction qualifiers
TLinkType linkType;
};
//

View file

@ -123,6 +123,8 @@ TAttributeType TParseContext::attributeFromName(const TString& name) const
return EatPartialCount;
else if (name == "subgroup_uniform_control_flow")
return EatSubgroupUniformControlFlow;
else if (name == "export")
return EatExport;
else
return EatNone;
}
@ -355,6 +357,7 @@ void TParseContext::handleFunctionAttributes(const TSourceLoc& loc, const TAttri
switch (it->name) {
case EatSubgroupUniformControlFlow:
requireExtensions(loc, 1, &E_GL_EXT_subgroup_uniform_control_flow, "attribute");
intermediate.setSubgroupUniformControlFlow();
break;
default:

View file

@ -120,6 +120,7 @@ namespace glslang {
EatNonWritable,
EatNonReadable,
EatSubgroupUniformControlFlow,
EatExport,
};
class TIntermAggregate;

View file

@ -941,24 +941,25 @@ identifier_list
function_prototype
: function_declarator RIGHT_PAREN {
$$.function = $1;
if (parseContext.compileOnly) $$.function->setExport();
$$.loc = $2.loc;
}
| function_declarator RIGHT_PAREN attribute {
$$.function = $1;
if (parseContext.compileOnly) $$.function->setExport();
$$.loc = $2.loc;
parseContext.requireExtensions($2.loc, 1, &E_GL_EXT_subgroup_uniform_control_flow, "attribute");
parseContext.handleFunctionAttributes($2.loc, *$3);
}
| attribute function_declarator RIGHT_PAREN {
$$.function = $2;
if (parseContext.compileOnly) $$.function->setExport();
$$.loc = $3.loc;
parseContext.requireExtensions($3.loc, 1, &E_GL_EXT_subgroup_uniform_control_flow, "attribute");
parseContext.handleFunctionAttributes($3.loc, *$1);
}
| attribute function_declarator RIGHT_PAREN attribute {
$$.function = $2;
if (parseContext.compileOnly) $$.function->setExport();
$$.loc = $3.loc;
parseContext.requireExtensions($3.loc, 1, &E_GL_EXT_subgroup_uniform_control_flow, "attribute");
parseContext.handleFunctionAttributes($3.loc, *$1);
parseContext.handleFunctionAttributes($3.loc, *$4);
}
@ -4088,6 +4089,7 @@ function_definition
parseContext.error($1.loc, "function does not return a value:", "", $1.function->getName().c_str());
parseContext.symbolTable.pop(&parseContext.defaultPrecision[0]);
$$ = parseContext.intermediate.growAggregate($1.intermNode, $3);
$$->getAsAggregate()->setLinkType($1.function->getLinkType());
parseContext.intermediate.setAggregateOperator($$, EOpFunction, $1.function->getType(), $1.loc);
$$->getAsAggregate()->setName($1.function->getMangledName().c_str());

File diff suppressed because it is too large Load diff

View file

@ -524,7 +524,7 @@ extern int yydebug;
#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED
union YYSTYPE
{
#line 97 "MachineIndependent/glslang.y"
#line 72 "MachineIndependent/glslang.y"
struct {
glslang::TSourceLoc loc;

View file

@ -573,6 +573,9 @@ public:
void setEnvInputVulkanRulesRelaxed() { environment.input.vulkanRulesRelaxed = true; }
bool getEnvInputVulkanRulesRelaxed() const { return environment.input.vulkanRulesRelaxed; }
void setCompileOnly() { compileOnly = true; }
bool getCompileOnly() const { return compileOnly; }
// Interface to #include handlers.
//
// To support #include, a client of Glslang does the following:
@ -722,6 +725,9 @@ protected:
TEnvironment environment;
// Indicates this shader is meant to be used without linking
bool compileOnly = false;
friend class TProgram;
private: