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

@ -133,7 +133,7 @@ public:
bool visitLoop(glslang::TVisit, glslang::TIntermLoop*);
bool visitBranch(glslang::TVisit visit, glslang::TIntermBranch*);
void finishSpv();
void finishSpv(bool compileOnly);
void dumpSpv(std::vector<unsigned int>& out);
protected:
@ -167,6 +167,7 @@ protected:
bool filterMember(const glslang::TType& member);
spv::Id convertGlslangStructToSpvType(const glslang::TType&, const glslang::TTypeList* glslangStruct,
glslang::TLayoutPacking, const glslang::TQualifier&);
spv::LinkageType convertGlslangLinkageToSpv(glslang::TLinkType glslangLinkType);
void decorateStructType(const glslang::TType&, const glslang::TTypeList* glslangStruct, glslang::TLayoutPacking,
const glslang::TQualifier&, spv::Id, const std::vector<spv::Id>& spvMembers);
spv::Id makeArraySizeId(const glslang::TArraySizes&, int dim, bool allowZero = false);
@ -1588,8 +1589,12 @@ TGlslangToSpvTraverser::TGlslangToSpvTraverser(unsigned int spvVersion,
builder.addCapability(spv::CapabilityVariablePointers);
}
shaderEntry = builder.makeEntryPoint(glslangIntermediate->getEntryPointName().c_str());
entryPoint = builder.addEntryPoint(executionModel, shaderEntry, glslangIntermediate->getEntryPointName().c_str());
// If not linking, there is no entry point
if (!options.compileOnly) {
shaderEntry = builder.makeEntryPoint(glslangIntermediate->getEntryPointName().c_str());
entryPoint =
builder.addEntryPoint(executionModel, shaderEntry, glslangIntermediate->getEntryPointName().c_str());
}
// Add the source extensions
const auto& sourceExtensions = glslangIntermediate->getRequestedExtensions();
@ -1939,23 +1944,26 @@ TGlslangToSpvTraverser::TGlslangToSpvTraverser(unsigned int spvVersion,
}
// Finish creating SPV, after the traversal is complete.
void TGlslangToSpvTraverser::finishSpv()
void TGlslangToSpvTraverser::finishSpv(bool compileOnly)
{
// Finish the entry point function
if (! entryPointTerminated) {
builder.setBuildPoint(shaderEntry->getLastBlock());
builder.leaveFunction();
}
// If not linking, an entry point is not expected
if (!compileOnly) {
// Finish the entry point function
if (!entryPointTerminated) {
builder.setBuildPoint(shaderEntry->getLastBlock());
builder.leaveFunction();
}
// finish off the entry-point SPV instruction by adding the Input/Output <id>
for (auto it = iOSet.cbegin(); it != iOSet.cend(); ++it)
entryPoint->addIdOperand(*it);
// finish off the entry-point SPV instruction by adding the Input/Output <id>
for (auto it = iOSet.cbegin(); it != iOSet.cend(); ++it)
entryPoint->addIdOperand(*it);
}
// Add capabilities, extensions, remove unneeded decorations, etc.,
// based on the resulting SPIR-V.
// Note: WebGPU code generation must have the opportunity to aggressively
// prune unreachable merge blocks and continue targets.
builder.postProcess();
builder.postProcess(compileOnly);
}
// Write the SPV into 'out'.
@ -2840,9 +2848,12 @@ bool TGlslangToSpvTraverser::visitAggregate(glslang::TVisit visit, glslang::TInt
// In all cases, still let the traverser visit the children for us.
makeFunctions(node->getAsAggregate()->getSequence());
// Also, we want all globals initializers to go into the beginning of the entry point, before
// anything else gets there, so visit out of order, doing them all now.
makeGlobalInitializers(node->getAsAggregate()->getSequence());
// Global initializers is specific to the shader entry point, which does not exist in compile-only mode
if (!options.compileOnly) {
// Also, we want all globals initializers to go into the beginning of the entry point, before
// anything else gets there, so visit out of order, doing them all now.
makeGlobalInitializers(node->getAsAggregate()->getSequence());
}
//Pre process linker objects for ray tracing stages
if (glslangIntermediate->isRayTracingStage())
@ -4329,6 +4340,16 @@ spv::Id TGlslangToSpvTraverser::convertGlslangToSpvType(const glslang::TType& ty
return convertGlslangToSpvType(type, getExplicitLayout(type), type.getQualifier(), false, forwardReferenceOnly);
}
spv::LinkageType TGlslangToSpvTraverser::convertGlslangLinkageToSpv(glslang::TLinkType linkType)
{
switch (linkType) {
case glslang::ELinkExport:
return spv::LinkageTypeExport;
default:
return spv::LinkageTypeMax;
}
}
// Do full recursive conversion of an arbitrary glslang type to a SPIR-V Id.
// explicitLayout can be kept the same throughout the hierarchical recursive walk.
// Mutually recursive with convertGlslangStructToSpvType().
@ -5396,10 +5417,10 @@ void TGlslangToSpvTraverser::makeFunctions(const glslang::TIntermSequence& glslF
}
spv::Block* functionBlock;
spv::Function *function = builder.makeFunctionEntry(TranslatePrecisionDecoration(glslFunction->getType()),
convertGlslangToSpvType(glslFunction->getType()),
glslFunction->getName().c_str(), paramTypes, paramNames,
paramDecorations, &functionBlock);
spv::Function* function = builder.makeFunctionEntry(
TranslatePrecisionDecoration(glslFunction->getType()), convertGlslangToSpvType(glslFunction->getType()),
glslFunction->getName().c_str(), convertGlslangLinkageToSpv(glslFunction->getLinkType()), paramTypes,
paramNames, paramDecorations, &functionBlock);
if (implicitThis)
function->setImplicitThis();
@ -10102,7 +10123,7 @@ void GlslangToSpv(const TIntermediate& intermediate, std::vector<unsigned int>&
TGlslangToSpvTraverser it(intermediate.getSpv().spv, &intermediate, logger, *options);
root->traverse(&it);
it.finishSpv();
it.finishSpv(options->compileOnly);
it.dumpSpv(spirv);
#if ENABLE_OPT

View file

@ -1835,6 +1835,10 @@ Instruction* Builder::addEntryPoint(ExecutionModel model, Function* function, co
// Currently relying on the fact that all 'value' of interest are small non-negative values.
void Builder::addExecutionMode(Function* entryPoint, ExecutionMode mode, int value1, int value2, int value3)
{
// entryPoint can be null if we are in compile-only mode
if (!entryPoint)
return;
Instruction* instr = new Instruction(OpExecutionMode);
instr->addIdOperand(entryPoint->getId());
instr->addImmediateOperand(mode);
@ -1850,6 +1854,10 @@ void Builder::addExecutionMode(Function* entryPoint, ExecutionMode mode, int val
void Builder::addExecutionMode(Function* entryPoint, ExecutionMode mode, const std::vector<unsigned>& literals)
{
// entryPoint can be null if we are in compile-only mode
if (!entryPoint)
return;
Instruction* instr = new Instruction(OpExecutionMode);
instr->addIdOperand(entryPoint->getId());
instr->addImmediateOperand(mode);
@ -1861,6 +1869,10 @@ void Builder::addExecutionMode(Function* entryPoint, ExecutionMode mode, const s
void Builder::addExecutionModeId(Function* entryPoint, ExecutionMode mode, const std::vector<Id>& operandIds)
{
// entryPoint can be null if we are in compile-only mode
if (!entryPoint)
return;
Instruction* instr = new Instruction(OpExecutionModeId);
instr->addIdOperand(entryPoint->getId());
instr->addImmediateOperand(mode);
@ -1944,6 +1956,16 @@ void Builder::addDecoration(Id id, Decoration decoration, const std::vector<cons
decorations.push_back(std::unique_ptr<Instruction>(dec));
}
void Builder::addLinkageDecoration(Id id, const char* name, spv::LinkageType linkType) {
Instruction* dec = new Instruction(OpDecorate);
dec->addIdOperand(id);
dec->addImmediateOperand(spv::DecorationLinkageAttributes);
dec->addStringOperand(name);
dec->addImmediateOperand(linkType);
decorations.push_back(std::unique_ptr<Instruction>(dec));
}
void Builder::addDecorationId(Id id, Decoration decoration, Id idDecoration)
{
if (decoration == spv::DecorationMax)
@ -2048,7 +2070,7 @@ Function* Builder::makeEntryPoint(const char* entryPoint)
emitNonSemanticShaderDebugInfo = false;
}
entryPointFunction = makeFunctionEntry(NoPrecision, returnType, entryPoint, paramsTypes, paramNames, decorations, &entry);
entryPointFunction = makeFunctionEntry(NoPrecision, returnType, entryPoint, LinkageTypeMax, paramsTypes, paramNames, decorations, &entry);
emitNonSemanticShaderDebugInfo = restoreNonSemanticShaderDebugInfo;
@ -2056,7 +2078,7 @@ Function* Builder::makeEntryPoint(const char* entryPoint)
}
// Comments in header
Function* Builder::makeFunctionEntry(Decoration precision, Id returnType, const char* name,
Function* Builder::makeFunctionEntry(Decoration precision, Id returnType, const char* name, LinkageType linkType,
const std::vector<Id>& paramTypes, const std::vector<char const*>& paramNames,
const std::vector<std::vector<Decoration>>& decorations, Block **entry)
{
@ -2064,7 +2086,7 @@ Function* Builder::makeFunctionEntry(Decoration precision, Id returnType, const
Id typeId = makeFunctionType(returnType, paramTypes);
Id firstParamId = paramTypes.size() == 0 ? 0 : getUniqueIds((int)paramTypes.size());
Id funcId = getUniqueId();
Function* function = new Function(funcId, returnType, typeId, firstParamId, module);
Function* function = new Function(funcId, returnType, typeId, firstParamId, linkType, name, module);
// Set up the precisions
setPrecision(function->getId(), precision);
@ -2234,6 +2256,12 @@ void Builder::enterFunction(Function const* function)
defInst->addIdOperand(funcId);
buildPoint->addInstruction(std::unique_ptr<Instruction>(defInst));
}
if (auto linkType = function->getLinkType(); linkType != LinkageTypeMax) {
Id funcId = function->getFuncId();
addCapability(CapabilityLinkage);
addLinkageDecoration(funcId, function->getExportName(), linkType);
}
}
// Comments in header

View file

@ -393,6 +393,7 @@ public:
void addDecoration(Id, Decoration, const char*);
void addDecoration(Id, Decoration, const std::vector<unsigned>& literals);
void addDecoration(Id, Decoration, const std::vector<const char*>& strings);
void addLinkageDecoration(Id id, const char* name, spv::LinkageType linkType);
void addDecorationId(Id id, Decoration, Id idDecoration);
void addDecorationId(Id id, Decoration, const std::vector<Id>& operandIds);
void addMemberDecoration(Id, unsigned int member, Decoration, int num = -1);
@ -417,7 +418,8 @@ public:
// Return the function, pass back the entry.
// The returned pointer is only valid for the lifetime of this builder.
Function* makeFunctionEntry(Decoration precision, Id returnType, const char* name,
const std::vector<Id>& paramTypes, const std::vector<char const*>& paramNames,
LinkageType linkType, const std::vector<Id>& paramTypes,
const std::vector<char const*>& paramNames,
const std::vector<std::vector<Decoration>>& precisions, Block **entry = nullptr);
// Create a return. An 'implicit' return is one not appearing in the source
@ -828,7 +830,7 @@ public:
// Add capabilities, extensions, remove unneeded decorations, etc.,
// based on the resulting SPIR-V.
void postProcess();
void postProcess(bool compileOnly);
// Prune unreachable blocks in the CFG and remove unneeded decorations.
void postProcessCFG();

View file

@ -483,9 +483,13 @@ void Builder::postProcessFeatures() {
}
// comment in header
void Builder::postProcess() {
postProcessCFG();
postProcessFeatures();
void Builder::postProcess(bool compileOnly)
{
// postProcessCFG needs an entrypoint to determine what is reachable, but if we are not creating an "executable" shader, we don't have an entrypoint
if (!compileOnly)
postProcessCFG();
postProcessFeatures();
}
}; // end spv namespace

View file

@ -61,6 +61,7 @@ struct SpvOptions {
bool validate {false};
bool emitNonSemanticShaderDebugInfo {false};
bool emitNonSemanticShaderDebugSource{ false };
bool compileOnly{false};
};
#if ENABLE_OPT

View file

@ -323,7 +323,7 @@ void inReadableOrder(Block* root, std::function<void(Block*, ReachReason, Block*
class Function {
public:
Function(Id id, Id resultType, Id functionType, Id firstParam, Module& parent);
Function(Id id, Id resultType, Id functionType, Id firstParam, LinkageType linkage, const std::string& name, Module& parent);
virtual ~Function()
{
for (int i = 0; i < (int)parameterInstructions.size(); ++i)
@ -402,6 +402,9 @@ public:
end.dump(out);
}
LinkageType getLinkType() const { return linkType; }
const char* getExportName() const { return exportName.c_str(); }
protected:
Function(const Function&);
Function& operator=(Function&);
@ -414,6 +417,8 @@ protected:
bool implicitThis; // true if this is a member function expecting to be passed a 'this' as the first argument
bool reducedPrecisionReturn;
std::set<int> reducedPrecisionParams; // list of parameter indexes that need a relaxed precision arg
LinkageType linkType;
std::string exportName;
};
//
@ -473,10 +478,11 @@ protected:
// Add both
// - the OpFunction instruction
// - all the OpFunctionParameter instructions
__inline Function::Function(Id id, Id resultType, Id functionType, Id firstParamId, Module& parent)
__inline Function::Function(Id id, Id resultType, Id functionType, Id firstParamId, LinkageType linkage, const std::string& name, Module& parent)
: parent(parent), lineInstruction(nullptr),
functionInstruction(id, resultType, OpFunction), implicitThis(false),
reducedPrecisionReturn(false)
reducedPrecisionReturn(false),
linkType(linkage)
{
// OpFunction
functionInstruction.addImmediateOperand(FunctionControlMaskNone);
@ -492,6 +498,11 @@ __inline Function::Function(Id id, Id resultType, Id functionType, Id firstParam
parent.mapInstruction(param);
parameterInstructions.push_back(param);
}
// If importing/exporting, save the function name (without the mangled parameters) for the linkage decoration
if (linkType != LinkageTypeMax) {
exportName = name.substr(0, name.find_first_of('('));
}
}
__inline void Function::addLocalVariable(std::unique_ptr<Instruction> inst)