Skip to content

Commit

Permalink
Add --no-link option
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
ncesario-lunarg authored and arcady-lunarg committed Sep 18, 2023
1 parent a4aceb5 commit 4c57db1
Show file tree
Hide file tree
Showing 24 changed files with 1,671 additions and 1,454 deletions.
63 changes: 42 additions & 21 deletions SPIRV/GlslangToSpv.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,7 @@ class TGlslangToSpvTraverser : public glslang::TIntermTraverser {
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:
Expand Down Expand Up @@ -167,6 +167,7 @@ class TGlslangToSpvTraverser : public glslang::TIntermTraverser {
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);
Expand Down Expand Up @@ -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();
Expand Down Expand Up @@ -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'.
Expand Down Expand Up @@ -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())
Expand Down Expand Up @@ -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().
Expand Down Expand Up @@ -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();

Expand Down Expand Up @@ -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
Expand Down
34 changes: 31 additions & 3 deletions SPIRV/SpvBuilder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand All @@ -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);
Expand All @@ -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);
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -2048,23 +2070,23 @@ 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;

return entryPointFunction;
}

// 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)
{
// Make the function and initial instructions in it
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);
Expand Down Expand Up @@ -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
Expand Down
6 changes: 4 additions & 2 deletions SPIRV/SpvBuilder.h
Original file line number Diff line number Diff line change
Expand Up @@ -393,6 +393,7 @@ class Builder {
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);
Expand All @@ -417,7 +418,8 @@ class Builder {
// 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
Expand Down Expand Up @@ -828,7 +830,7 @@ class Builder {

// 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();
Expand Down
10 changes: 7 additions & 3 deletions SPIRV/SpvPostProcess.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
1 change: 1 addition & 0 deletions SPIRV/SpvTools.h
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ struct SpvOptions {
bool validate {false};
bool emitNonSemanticShaderDebugInfo {false};
bool emitNonSemanticShaderDebugSource{ false };
bool compileOnly{false};
};

#if ENABLE_OPT
Expand Down
17 changes: 14 additions & 3 deletions SPIRV/spvIR.h
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -402,6 +402,9 @@ class Function {
end.dump(out);
}

LinkageType getLinkType() const { return linkType; }
const char* getExportName() const { return exportName.c_str(); }

protected:
Function(const Function&);
Function& operator=(Function&);
Expand All @@ -414,6 +417,8 @@ class Function {
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;
};

//
Expand Down Expand Up @@ -473,10 +478,11 @@ class Module {
// 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);
Expand All @@ -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)
Expand Down
Loading

0 comments on commit 4c57db1

Please sign in to comment.