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 visitLoop(glslang::TVisit, glslang::TIntermLoop*);
bool visitBranch(glslang::TVisit visit, glslang::TIntermBranch*); bool visitBranch(glslang::TVisit visit, glslang::TIntermBranch*);
void finishSpv(); void finishSpv(bool compileOnly);
void dumpSpv(std::vector<unsigned int>& out); void dumpSpv(std::vector<unsigned int>& out);
protected: protected:
@ -167,6 +167,7 @@ protected:
bool filterMember(const glslang::TType& member); bool filterMember(const glslang::TType& member);
spv::Id convertGlslangStructToSpvType(const glslang::TType&, const glslang::TTypeList* glslangStruct, spv::Id convertGlslangStructToSpvType(const glslang::TType&, const glslang::TTypeList* glslangStruct,
glslang::TLayoutPacking, const glslang::TQualifier&); glslang::TLayoutPacking, const glslang::TQualifier&);
spv::LinkageType convertGlslangLinkageToSpv(glslang::TLinkType glslangLinkType);
void decorateStructType(const glslang::TType&, const glslang::TTypeList* glslangStruct, glslang::TLayoutPacking, void decorateStructType(const glslang::TType&, const glslang::TTypeList* glslangStruct, glslang::TLayoutPacking,
const glslang::TQualifier&, spv::Id, const std::vector<spv::Id>& spvMembers); const glslang::TQualifier&, spv::Id, const std::vector<spv::Id>& spvMembers);
spv::Id makeArraySizeId(const glslang::TArraySizes&, int dim, bool allowZero = false); spv::Id makeArraySizeId(const glslang::TArraySizes&, int dim, bool allowZero = false);
@ -1588,8 +1589,12 @@ TGlslangToSpvTraverser::TGlslangToSpvTraverser(unsigned int spvVersion,
builder.addCapability(spv::CapabilityVariablePointers); builder.addCapability(spv::CapabilityVariablePointers);
} }
shaderEntry = builder.makeEntryPoint(glslangIntermediate->getEntryPointName().c_str()); // If not linking, there is no entry point
entryPoint = builder.addEntryPoint(executionModel, shaderEntry, glslangIntermediate->getEntryPointName().c_str()); if (!options.compileOnly) {
shaderEntry = builder.makeEntryPoint(glslangIntermediate->getEntryPointName().c_str());
entryPoint =
builder.addEntryPoint(executionModel, shaderEntry, glslangIntermediate->getEntryPointName().c_str());
}
// Add the source extensions // Add the source extensions
const auto& sourceExtensions = glslangIntermediate->getRequestedExtensions(); const auto& sourceExtensions = glslangIntermediate->getRequestedExtensions();
@ -1939,23 +1944,26 @@ TGlslangToSpvTraverser::TGlslangToSpvTraverser(unsigned int spvVersion,
} }
// Finish creating SPV, after the traversal is complete. // Finish creating SPV, after the traversal is complete.
void TGlslangToSpvTraverser::finishSpv() void TGlslangToSpvTraverser::finishSpv(bool compileOnly)
{ {
// Finish the entry point function // If not linking, an entry point is not expected
if (! entryPointTerminated) { if (!compileOnly) {
builder.setBuildPoint(shaderEntry->getLastBlock()); // Finish the entry point function
builder.leaveFunction(); if (!entryPointTerminated) {
} builder.setBuildPoint(shaderEntry->getLastBlock());
builder.leaveFunction();
}
// finish off the entry-point SPV instruction by adding the Input/Output <id> // finish off the entry-point SPV instruction by adding the Input/Output <id>
for (auto it = iOSet.cbegin(); it != iOSet.cend(); ++it) for (auto it = iOSet.cbegin(); it != iOSet.cend(); ++it)
entryPoint->addIdOperand(*it); entryPoint->addIdOperand(*it);
}
// Add capabilities, extensions, remove unneeded decorations, etc., // Add capabilities, extensions, remove unneeded decorations, etc.,
// based on the resulting SPIR-V. // based on the resulting SPIR-V.
// Note: WebGPU code generation must have the opportunity to aggressively // Note: WebGPU code generation must have the opportunity to aggressively
// prune unreachable merge blocks and continue targets. // prune unreachable merge blocks and continue targets.
builder.postProcess(); builder.postProcess(compileOnly);
} }
// Write the SPV into 'out'. // 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. // In all cases, still let the traverser visit the children for us.
makeFunctions(node->getAsAggregate()->getSequence()); makeFunctions(node->getAsAggregate()->getSequence());
// Also, we want all globals initializers to go into the beginning of the entry point, before // Global initializers is specific to the shader entry point, which does not exist in compile-only mode
// anything else gets there, so visit out of order, doing them all now. if (!options.compileOnly) {
makeGlobalInitializers(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());
}
//Pre process linker objects for ray tracing stages //Pre process linker objects for ray tracing stages
if (glslangIntermediate->isRayTracingStage()) if (glslangIntermediate->isRayTracingStage())
@ -4329,6 +4340,16 @@ spv::Id TGlslangToSpvTraverser::convertGlslangToSpvType(const glslang::TType& ty
return convertGlslangToSpvType(type, getExplicitLayout(type), type.getQualifier(), false, forwardReferenceOnly); 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. // 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. // explicitLayout can be kept the same throughout the hierarchical recursive walk.
// Mutually recursive with convertGlslangStructToSpvType(). // Mutually recursive with convertGlslangStructToSpvType().
@ -5396,10 +5417,10 @@ void TGlslangToSpvTraverser::makeFunctions(const glslang::TIntermSequence& glslF
} }
spv::Block* functionBlock; spv::Block* functionBlock;
spv::Function *function = builder.makeFunctionEntry(TranslatePrecisionDecoration(glslFunction->getType()), spv::Function* function = builder.makeFunctionEntry(
convertGlslangToSpvType(glslFunction->getType()), TranslatePrecisionDecoration(glslFunction->getType()), convertGlslangToSpvType(glslFunction->getType()),
glslFunction->getName().c_str(), paramTypes, paramNames, glslFunction->getName().c_str(), convertGlslangLinkageToSpv(glslFunction->getLinkType()), paramTypes,
paramDecorations, &functionBlock); paramNames, paramDecorations, &functionBlock);
if (implicitThis) if (implicitThis)
function->setImplicitThis(); function->setImplicitThis();
@ -10102,7 +10123,7 @@ void GlslangToSpv(const TIntermediate& intermediate, std::vector<unsigned int>&
TGlslangToSpvTraverser it(intermediate.getSpv().spv, &intermediate, logger, *options); TGlslangToSpvTraverser it(intermediate.getSpv().spv, &intermediate, logger, *options);
root->traverse(&it); root->traverse(&it);
it.finishSpv(); it.finishSpv(options->compileOnly);
it.dumpSpv(spirv); it.dumpSpv(spirv);
#if ENABLE_OPT #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. // 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) 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); Instruction* instr = new Instruction(OpExecutionMode);
instr->addIdOperand(entryPoint->getId()); instr->addIdOperand(entryPoint->getId());
instr->addImmediateOperand(mode); 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) 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); Instruction* instr = new Instruction(OpExecutionMode);
instr->addIdOperand(entryPoint->getId()); instr->addIdOperand(entryPoint->getId());
instr->addImmediateOperand(mode); 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) 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); Instruction* instr = new Instruction(OpExecutionModeId);
instr->addIdOperand(entryPoint->getId()); instr->addIdOperand(entryPoint->getId());
instr->addImmediateOperand(mode); 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)); 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) void Builder::addDecorationId(Id id, Decoration decoration, Id idDecoration)
{ {
if (decoration == spv::DecorationMax) if (decoration == spv::DecorationMax)
@ -2048,7 +2070,7 @@ Function* Builder::makeEntryPoint(const char* entryPoint)
emitNonSemanticShaderDebugInfo = false; emitNonSemanticShaderDebugInfo = false;
} }
entryPointFunction = makeFunctionEntry(NoPrecision, returnType, entryPoint, paramsTypes, paramNames, decorations, &entry); entryPointFunction = makeFunctionEntry(NoPrecision, returnType, entryPoint, LinkageTypeMax, paramsTypes, paramNames, decorations, &entry);
emitNonSemanticShaderDebugInfo = restoreNonSemanticShaderDebugInfo; emitNonSemanticShaderDebugInfo = restoreNonSemanticShaderDebugInfo;
@ -2056,7 +2078,7 @@ Function* Builder::makeEntryPoint(const char* entryPoint)
} }
// Comments in header // 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<Id>& paramTypes, const std::vector<char const*>& paramNames,
const std::vector<std::vector<Decoration>>& decorations, Block **entry) 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 typeId = makeFunctionType(returnType, paramTypes);
Id firstParamId = paramTypes.size() == 0 ? 0 : getUniqueIds((int)paramTypes.size()); Id firstParamId = paramTypes.size() == 0 ? 0 : getUniqueIds((int)paramTypes.size());
Id funcId = getUniqueId(); 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 // Set up the precisions
setPrecision(function->getId(), precision); setPrecision(function->getId(), precision);
@ -2234,6 +2256,12 @@ void Builder::enterFunction(Function const* function)
defInst->addIdOperand(funcId); defInst->addIdOperand(funcId);
buildPoint->addInstruction(std::unique_ptr<Instruction>(defInst)); 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 // Comments in header

View File

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

View File

@ -483,9 +483,13 @@ void Builder::postProcessFeatures() {
} }
// comment in header // comment in header
void Builder::postProcess() { void Builder::postProcess(bool compileOnly)
postProcessCFG(); {
postProcessFeatures(); // 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 }; // end spv namespace

View File

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

View File

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

View File

@ -73,40 +73,41 @@ extern "C" {
} }
// Command-line options // Command-line options
enum TOptions { enum TOptions : uint64_t {
EOptionNone = 0, EOptionNone = 0,
EOptionIntermediate = (1 << 0), EOptionIntermediate = (1ull << 0),
EOptionSuppressInfolog = (1 << 1), EOptionSuppressInfolog = (1ull << 1),
EOptionMemoryLeakMode = (1 << 2), EOptionMemoryLeakMode = (1ull << 2),
EOptionRelaxedErrors = (1 << 3), EOptionRelaxedErrors = (1ull << 3),
EOptionGiveWarnings = (1 << 4), EOptionGiveWarnings = (1ull << 4),
EOptionLinkProgram = (1 << 5), EOptionLinkProgram = (1ull << 5),
EOptionMultiThreaded = (1 << 6), EOptionMultiThreaded = (1ull << 6),
EOptionDumpConfig = (1 << 7), EOptionDumpConfig = (1ull << 7),
EOptionDumpReflection = (1 << 8), EOptionDumpReflection = (1ull << 8),
EOptionSuppressWarnings = (1 << 9), EOptionSuppressWarnings = (1ull << 9),
EOptionDumpVersions = (1 << 10), EOptionDumpVersions = (1ull << 10),
EOptionSpv = (1 << 11), EOptionSpv = (1ull << 11),
EOptionHumanReadableSpv = (1 << 12), EOptionHumanReadableSpv = (1ull << 12),
EOptionVulkanRules = (1 << 13), EOptionVulkanRules = (1ull << 13),
EOptionDefaultDesktop = (1 << 14), EOptionDefaultDesktop = (1ull << 14),
EOptionOutputPreprocessed = (1 << 15), EOptionOutputPreprocessed = (1ull << 15),
EOptionOutputHexadecimal = (1 << 16), EOptionOutputHexadecimal = (1ull << 16),
EOptionReadHlsl = (1 << 17), EOptionReadHlsl = (1ull << 17),
EOptionCascadingErrors = (1 << 18), EOptionCascadingErrors = (1ull << 18),
EOptionAutoMapBindings = (1 << 19), EOptionAutoMapBindings = (1ull << 19),
EOptionFlattenUniformArrays = (1 << 20), EOptionFlattenUniformArrays = (1ull << 20),
EOptionNoStorageFormat = (1 << 21), EOptionNoStorageFormat = (1ull << 21),
EOptionKeepUncalled = (1 << 22), EOptionKeepUncalled = (1ull << 22),
EOptionHlslOffsets = (1 << 23), EOptionHlslOffsets = (1ull << 23),
EOptionHlslIoMapping = (1 << 24), EOptionHlslIoMapping = (1ull << 24),
EOptionAutoMapLocations = (1 << 25), EOptionAutoMapLocations = (1ull << 25),
EOptionDebug = (1 << 26), EOptionDebug = (1ull << 26),
EOptionStdin = (1 << 27), EOptionStdin = (1ull << 27),
EOptionOptimizeDisable = (1 << 28), EOptionOptimizeDisable = (1ull << 28),
EOptionOptimizeSize = (1 << 29), EOptionOptimizeSize = (1ull << 29),
EOptionInvertY = (1 << 30), EOptionInvertY = (1ull << 30),
EOptionDumpBareVersion = (1 << 31), EOptionDumpBareVersion = (1ull << 31),
EOptionCompileOnly = (1ull << 32),
}; };
bool targetHlslFunctionality1 = false; bool targetHlslFunctionality1 = false;
bool SpvToolsDisassembler = false; bool SpvToolsDisassembler = false;
@ -166,7 +167,7 @@ void ProcessConfigFile()
} }
int ReflectOptions = EShReflectionDefault; int ReflectOptions = EShReflectionDefault;
int Options = 0; std::underlying_type_t<TOptions> Options = EOptionNone;
const char* ExecutableName = nullptr; const char* ExecutableName = nullptr;
const char* binaryFileName = nullptr; const char* binaryFileName = nullptr;
const char* depencyFileName = nullptr; const char* depencyFileName = nullptr;
@ -889,6 +890,8 @@ void ProcessArguments(std::vector<std::unique_ptr<glslang::TWorkItem>>& workItem
bumpArg(); bumpArg();
} else if (lowerword == "version") { } else if (lowerword == "version") {
Options |= EOptionDumpVersions; Options |= EOptionDumpVersions;
} else if (lowerword == "no-link") {
Options |= EOptionCompileOnly;
} else if (lowerword == "help") { } else if (lowerword == "help") {
usage(); usage();
break; break;
@ -1310,6 +1313,7 @@ void CompileAndLinkShaderUnits(std::vector<ShaderCompUnit> compUnits)
// //
glslang::TProgram& program = *new glslang::TProgram; glslang::TProgram& program = *new glslang::TProgram;
const bool compileOnly = (Options & EOptionCompileOnly) != 0;
for (auto it = compUnits.cbegin(); it != compUnits.cend(); ++it) { for (auto it = compUnits.cbegin(); it != compUnits.cend(); ++it) {
const auto &compUnit = *it; const auto &compUnit = *it;
for (int i = 0; i < compUnit.count; i++) { for (int i = 0; i < compUnit.count; i++) {
@ -1326,6 +1330,9 @@ void CompileAndLinkShaderUnits(std::vector<ShaderCompUnit> compUnits)
shader->setSourceEntryPoint(sourceEntryPointName); shader->setSourceEntryPoint(sourceEntryPointName);
} }
if (compileOnly)
shader->setCompileOnly();
shader->setOverrideVersion(GlslVersion); shader->setOverrideVersion(GlslVersion);
std::string intrinsicString = getIntrinsic(compUnit.text, compUnit.count); std::string intrinsicString = getIntrinsic(compUnit.text, compUnit.count);
@ -1445,7 +1452,8 @@ void CompileAndLinkShaderUnits(std::vector<ShaderCompUnit> compUnits)
if (! shader->parse(GetResources(), defaultVersion, false, messages, includer)) if (! shader->parse(GetResources(), defaultVersion, false, messages, includer))
CompileFailed = true; CompileFailed = true;
program.addShader(shader); if (!compileOnly)
program.addShader(shader);
if (! (Options & EOptionSuppressInfolog) && if (! (Options & EOptionSuppressInfolog) &&
! (Options & EOptionMemoryLeakMode)) { ! (Options & EOptionMemoryLeakMode)) {
@ -1460,27 +1468,28 @@ void CompileAndLinkShaderUnits(std::vector<ShaderCompUnit> compUnits)
// Program-level processing... // Program-level processing...
// //
// Link if (!compileOnly) {
if (! (Options & EOptionOutputPreprocessed) && ! program.link(messages)) // Link
LinkFailed = true; if (!(Options & EOptionOutputPreprocessed) && !program.link(messages))
// Map IO
if (Options & EOptionSpv) {
if (!program.mapIO())
LinkFailed = true; LinkFailed = true;
}
// Report // Map IO
if (! (Options & EOptionSuppressInfolog) && if (Options & EOptionSpv) {
! (Options & EOptionMemoryLeakMode)) { if (!program.mapIO())
PutsIfNonEmpty(program.getInfoLog()); LinkFailed = true;
PutsIfNonEmpty(program.getInfoDebugLog()); }
}
// Reflect // Report
if (Options & EOptionDumpReflection) { if (!(Options & EOptionSuppressInfolog) && !(Options & EOptionMemoryLeakMode)) {
program.buildReflection(ReflectOptions); PutsIfNonEmpty(program.getInfoLog());
program.dumpReflection(); PutsIfNonEmpty(program.getInfoDebugLog());
}
// Reflect
if (Options & EOptionDumpReflection) {
program.buildReflection(ReflectOptions);
program.dumpReflection();
}
} }
std::vector<std::string> outputFiles; std::vector<std::string> outputFiles;
@ -1490,44 +1499,58 @@ void CompileAndLinkShaderUnits(std::vector<ShaderCompUnit> compUnits)
if (CompileFailed || LinkFailed) if (CompileFailed || LinkFailed)
printf("SPIR-V is not generated for failed compile or link\n"); printf("SPIR-V is not generated for failed compile or link\n");
else { else {
for (int stage = 0; stage < EShLangCount; ++stage) { std::vector<glslang::TIntermediate*> intermediates;
if (program.getIntermediate((EShLanguage)stage)) { if (!compileOnly) {
std::vector<unsigned int> spirv; for (int stage = 0; stage < EShLangCount; ++stage) {
spv::SpvBuildLogger logger; if (auto* i = program.getIntermediate((EShLanguage)stage)) {
glslang::SpvOptions spvOptions; intermediates.emplace_back(i);
if (Options & EOptionDebug) {
spvOptions.generateDebugInfo = true;
if (emitNonSemanticShaderDebugInfo) {
spvOptions.emitNonSemanticShaderDebugInfo = true;
if (emitNonSemanticShaderDebugSource) {
spvOptions.emitNonSemanticShaderDebugSource = true;
}
}
} else if (stripDebugInfo)
spvOptions.stripDebugInfo = true;
spvOptions.disableOptimizer = (Options & EOptionOptimizeDisable) != 0;
spvOptions.optimizeSize = (Options & EOptionOptimizeSize) != 0;
spvOptions.disassemble = SpvToolsDisassembler;
spvOptions.validate = SpvToolsValidate;
glslang::GlslangToSpv(*program.getIntermediate((EShLanguage)stage), spirv, &logger, &spvOptions);
// Dump the spv to a file or stdout, etc., but only if not doing
// memory/perf testing, as it's not internal to programmatic use.
if (! (Options & EOptionMemoryLeakMode)) {
printf("%s", logger.getAllMessages().c_str());
if (Options & EOptionOutputHexadecimal) {
if (!glslang::OutputSpvHex(spirv, GetBinaryName((EShLanguage)stage), variableName))
exit(EFailUsage);
} else {
if (!glslang::OutputSpvBin(spirv, GetBinaryName((EShLanguage)stage)))
exit(EFailUsage);
}
outputFiles.push_back(GetBinaryName((EShLanguage)stage));
if (!SpvToolsDisassembler && (Options & EOptionHumanReadableSpv))
spv::Disassemble(std::cout, spirv);
} }
} }
} else {
for (const auto* shader : shaders) {
if (auto* i = shader->getIntermediate()) {
intermediates.emplace_back(i);
}
}
}
for (auto* intermediate : intermediates) {
std::vector<unsigned int> spirv;
spv::SpvBuildLogger logger;
glslang::SpvOptions spvOptions;
if (Options & EOptionDebug) {
spvOptions.generateDebugInfo = true;
if (emitNonSemanticShaderDebugInfo) {
spvOptions.emitNonSemanticShaderDebugInfo = true;
if (emitNonSemanticShaderDebugSource) {
spvOptions.emitNonSemanticShaderDebugSource = true;
}
}
} else if (stripDebugInfo)
spvOptions.stripDebugInfo = true;
spvOptions.disableOptimizer = (Options & EOptionOptimizeDisable) != 0;
spvOptions.optimizeSize = (Options & EOptionOptimizeSize) != 0;
spvOptions.disassemble = SpvToolsDisassembler;
spvOptions.validate = SpvToolsValidate;
spvOptions.compileOnly = compileOnly;
glslang::GlslangToSpv(*intermediate, spirv, &logger, &spvOptions);
// Dump the spv to a file or stdout, etc., but only if not doing
// memory/perf testing, as it's not internal to programmatic use.
if (!(Options & EOptionMemoryLeakMode)) {
printf("%s", logger.getAllMessages().c_str());
const auto filename = GetBinaryName(intermediate->getStage());
if (Options & EOptionOutputHexadecimal) {
if (!glslang::OutputSpvHex(spirv, filename, variableName))
exit(EFailUsage);
} else {
if (!glslang::OutputSpvBin(spirv, filename))
exit(EFailUsage);
}
outputFiles.push_back(filename);
if (!SpvToolsDisassembler && (Options & EOptionHumanReadableSpv))
spv::Disassemble(std::cout, spirv);
}
} }
} }
} }
@ -2075,7 +2098,10 @@ void usage()
" --variable-name <name>\n" " --variable-name <name>\n"
" --vn <name> creates a C header file that contains a\n" " --vn <name> creates a C header file that contains a\n"
" uint32_t array named <name>\n" " uint32_t array named <name>\n"
" initialized with the shader binary code\n"); " initialized with the shader binary code\n"
" --no-link Only compile shader; do not link (GLSL-only)\n"
" NOTE: this option will set the export linkage\n"
" attribute on all functions\n");
exit(EFailUsage); exit(EFailUsage);
} }

View File

@ -0,0 +1,36 @@
spv.exportFunctions.comp
// Module Version 10000
// Generated by (magic number): 8000b
// Id's are bound by 22
Capability Shader
Capability Linkage
1: ExtInstImport "GLSL.std.450"
MemoryModel Logical GLSL450
Source GLSL 450
Name 7 "add(f1;f1;"
Name 5 "a"
Name 6 "b"
Name 11 "foo("
Decorate 7(add(f1;f1;) Linkage Attributes 6579297 0
Decorate 11(foo() Linkage Attributes 7303014 0
2: TypeFloat 32
3: TypePointer Function 2(float)
4: TypeFunction 2(float) 3(ptr) 3(ptr)
9: TypeInt 32 1
10: TypeFunction 9(int)
17: TypeVoid
19: 9(int) Constant 0
7(add(f1;f1;): 2(float) Function None 4
5(a): 3(ptr) FunctionParameter
6(b): 3(ptr) FunctionParameter
8: Label
13: 2(float) Load 5(a)
14: 2(float) Load 6(b)
15: 2(float) FAdd 13 14
ReturnValue 15
FunctionEnd
11(foo(): 9(int) Function None 10
12: Label
ReturnValue 19
FunctionEnd

View File

@ -0,0 +1,9 @@
#version 450
float add(float a, float b) {
return a + b;
}
int foo() {
return 0;
}

View File

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

View File

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

View File

@ -1108,6 +1108,11 @@ enum TOperator {
EOpImageBlockMatchSSDQCOM, EOpImageBlockMatchSSDQCOM,
}; };
enum TLinkType {
ELinkNone,
ELinkExport,
};
class TIntermTraverser; class TIntermTraverser;
class TIntermOperator; class TIntermOperator;
class TIntermAggregate; class TIntermAggregate;
@ -1325,9 +1330,11 @@ public:
virtual const TString& getMethodName() const { return method; } virtual const TString& getMethodName() const { return method; }
virtual TIntermTyped* getObject() const { return object; } virtual TIntermTyped* getObject() const { return object; }
virtual void traverse(TIntermTraverser*); virtual void traverse(TIntermTraverser*);
void setExport() { linkType = ELinkExport; }
protected: protected:
TIntermTyped* object; TIntermTyped* object;
TString method; TString method;
TLinkType linkType;
}; };
// //
@ -1700,6 +1707,9 @@ public:
const TPragmaTable& getPragmaTable() const { return *pragmaTable; } const TPragmaTable& getPragmaTable() const { return *pragmaTable; }
void setSpirvInstruction(const TSpirvInstruction& inst) { spirvInst = inst; } void setSpirvInstruction(const TSpirvInstruction& inst) { spirvInst = inst; }
const TSpirvInstruction& getSpirvInstruction() const { return spirvInst; } const TSpirvInstruction& getSpirvInstruction() const { return spirvInst; }
void setLinkType(TLinkType l) { linkType = l; }
TLinkType getLinkType() const { return linkType; }
protected: protected:
TIntermAggregate(const TIntermAggregate&); // disallow copy constructor TIntermAggregate(const TIntermAggregate&); // disallow copy constructor
TIntermAggregate& operator=(const TIntermAggregate&); // disallow assignment operator TIntermAggregate& operator=(const TIntermAggregate&); // disallow assignment operator
@ -1711,6 +1721,7 @@ protected:
bool debug; bool debug;
TPragmaTable* pragmaTable; TPragmaTable* pragmaTable;
TSpirvInstruction spirvInst; 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(), ""); error(loc, "function cannot take any parameter(s)", function.getName().c_str(), "");
if (function.getType().getBasicType() != EbtVoid) if (function.getType().getBasicType() != EbtVoid)
error(loc, "", function.getType().getBasicTypeString().c_str(), "entry point cannot return a value"); 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 } else
paramNodes = intermediate.growAggregate(paramNodes, intermediate.addSymbol(*param.type, loc), loc); paramNodes = intermediate.growAggregate(paramNodes, intermediate.addSymbol(*param.type, loc), loc);
} }
paramNodes->setLinkType(function.getLinkType());
intermediate.setAggregateOperator(paramNodes, EOpParameters, TType(EbtVoid), loc); intermediate.setAggregateOperator(paramNodes, EOpParameters, TType(EbtVoid), loc);
loopNestingLevel = 0; loopNestingLevel = 0;
statementNestingLevel = 0; statementNestingLevel = 0;

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -941,24 +941,25 @@ identifier_list
function_prototype function_prototype
: function_declarator RIGHT_PAREN { : function_declarator RIGHT_PAREN {
$$.function = $1; $$.function = $1;
if (parseContext.compileOnly) $$.function->setExport();
$$.loc = $2.loc; $$.loc = $2.loc;
} }
| function_declarator RIGHT_PAREN attribute { | function_declarator RIGHT_PAREN attribute {
$$.function = $1; $$.function = $1;
if (parseContext.compileOnly) $$.function->setExport();
$$.loc = $2.loc; $$.loc = $2.loc;
parseContext.requireExtensions($2.loc, 1, &E_GL_EXT_subgroup_uniform_control_flow, "attribute");
parseContext.handleFunctionAttributes($2.loc, *$3); parseContext.handleFunctionAttributes($2.loc, *$3);
} }
| attribute function_declarator RIGHT_PAREN { | attribute function_declarator RIGHT_PAREN {
$$.function = $2; $$.function = $2;
if (parseContext.compileOnly) $$.function->setExport();
$$.loc = $3.loc; $$.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, *$1);
} }
| attribute function_declarator RIGHT_PAREN attribute { | attribute function_declarator RIGHT_PAREN attribute {
$$.function = $2; $$.function = $2;
if (parseContext.compileOnly) $$.function->setExport();
$$.loc = $3.loc; $$.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, *$1);
parseContext.handleFunctionAttributes($3.loc, *$4); 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.error($1.loc, "function does not return a value:", "", $1.function->getName().c_str());
parseContext.symbolTable.pop(&parseContext.defaultPrecision[0]); parseContext.symbolTable.pop(&parseContext.defaultPrecision[0]);
$$ = parseContext.intermediate.growAggregate($1.intermNode, $3); $$ = parseContext.intermediate.growAggregate($1.intermNode, $3);
$$->getAsAggregate()->setLinkType($1.function->getLinkType());
parseContext.intermediate.setAggregateOperator($$, EOpFunction, $1.function->getType(), $1.loc); parseContext.intermediate.setAggregateOperator($$, EOpFunction, $1.function->getType(), $1.loc);
$$->getAsAggregate()->setName($1.function->getMangledName().c_str()); $$->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 #if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED
union YYSTYPE union YYSTYPE
{ {
#line 97 "MachineIndependent/glslang.y" #line 72 "MachineIndependent/glslang.y"
struct { struct {
glslang::TSourceLoc loc; glslang::TSourceLoc loc;

View File

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

View File

@ -65,6 +65,7 @@ std::string FileNameAsCustomTestSuffixIoMap(
} }
using CompileVulkanToSpirvTest = GlslangTest<::testing::TestWithParam<std::string>>; using CompileVulkanToSpirvTest = GlslangTest<::testing::TestWithParam<std::string>>;
using CompileVulkanToSpirvTestNoLink = GlslangTest<::testing::TestWithParam<std::string>>;
using CompileVulkanToSpirvDeadCodeElimTest = GlslangTest<::testing::TestWithParam<std::string>>; using CompileVulkanToSpirvDeadCodeElimTest = GlslangTest<::testing::TestWithParam<std::string>>;
using CompileVulkanToDebugSpirvTest = GlslangTest<::testing::TestWithParam<std::string>>; using CompileVulkanToDebugSpirvTest = GlslangTest<::testing::TestWithParam<std::string>>;
using CompileVulkan1_1ToSpirvTest = GlslangTest<::testing::TestWithParam<std::string>>; using CompileVulkan1_1ToSpirvTest = GlslangTest<::testing::TestWithParam<std::string>>;
@ -92,6 +93,16 @@ TEST_P(CompileVulkanToSpirvTest, FromFile)
Target::Spv); Target::Spv);
} }
// Compiling GLSL to SPIR-V under Vulkan semantics without linking. Expected to successfully generate SPIR-V.
TEST_P(CompileVulkanToSpirvTestNoLink, FromFile)
{
options().compileOnly = true;
// NOTE: Vulkan 1.3 is currently required to use the linkage capability
// TODO(ncesario) make sure this is actually necessary
loadFileCompileAndCheck(GlobalTestSettings.testRoot, GetParam(), Source::GLSL, Semantics::Vulkan,
glslang::EShTargetVulkan_1_3, glslang::EShTargetSpv_1_0, Target::Spv);
}
TEST_P(CompileVulkanToSpirvDeadCodeElimTest, FromFile) TEST_P(CompileVulkanToSpirvDeadCodeElimTest, FromFile)
{ {
loadFileCompileAndCheck(GlobalTestSettings.testRoot, GetParam(), loadFileCompileAndCheck(GlobalTestSettings.testRoot, GetParam(),
@ -534,6 +545,14 @@ INSTANTIATE_TEST_SUITE_P(
FileNameAsCustomTestSuffix FileNameAsCustomTestSuffix
); );
INSTANTIATE_TEST_SUITE_P(
Glsl, CompileVulkanToSpirvTestNoLink,
::testing::ValuesIn(std::vector<std::string>({
"spv.exportFunctions.comp",
})),
FileNameAsCustomTestSuffix
);
// Cases with deliberately unreachable code. // Cases with deliberately unreachable code.
// By default the compiler will aggressively eliminate // By default the compiler will aggressively eliminate
// unreachable merges and continues. // unreachable merges and continues.

View File

@ -248,36 +248,58 @@ public:
} }
} }
if (options().compileOnly)
shader.setCompileOnly();
bool success = compile( bool success = compile(
&shader, code, entryPointName, controls, nullptr, &shaderName); &shader, code, entryPointName, controls, nullptr, &shaderName);
glslang::TProgram program; glslang::TProgram program;
program.addShader(&shader); spv::SpvBuildLogger logger;
success &= program.link(controls); std::vector<uint32_t> spirv_binary;
if (success)
program.mapIO();
if (success && (controls & EShMsgSpvRules)) { if (!options().compileOnly) {
spv::SpvBuildLogger logger; program.addShader(&shader);
std::vector<uint32_t> spirv_binary; success &= program.link(controls);
if (success)
program.mapIO();
if (success && (controls & EShMsgSpvRules)) {
options().disableOptimizer = !enableOptimizer;
options().generateDebugInfo = enableDebug;
options().emitNonSemanticShaderDebugInfo = enableNonSemanticShaderDebugInfo;
options().emitNonSemanticShaderDebugSource = enableNonSemanticShaderDebugInfo;
glslang::GlslangToSpv(*program.getIntermediate(stage), spirv_binary, &logger, &options());
} else {
return {{
{shaderName, shader.getInfoLog(), shader.getInfoDebugLog()},
},
program.getInfoLog(),
program.getInfoDebugLog(),
true,
"",
""};
}
} else {
options().disableOptimizer = !enableOptimizer; options().disableOptimizer = !enableOptimizer;
options().generateDebugInfo = enableDebug; options().generateDebugInfo = enableDebug;
options().emitNonSemanticShaderDebugInfo = enableNonSemanticShaderDebugInfo; options().emitNonSemanticShaderDebugInfo = enableNonSemanticShaderDebugInfo;
options().emitNonSemanticShaderDebugSource = enableNonSemanticShaderDebugInfo; options().emitNonSemanticShaderDebugSource = enableNonSemanticShaderDebugInfo;
glslang::GlslangToSpv(*program.getIntermediate(stage), glslang::GlslangToSpv(*shader.getIntermediate(), spirv_binary, &logger, &options());
spirv_binary, &logger, &options());
std::ostringstream disassembly_stream;
spv::Parameterize();
spv::Disassemble(disassembly_stream, spirv_binary);
bool validation_result = !options().validate || logger.getAllMessages().empty();
return {{{shaderName, shader.getInfoLog(), shader.getInfoDebugLog()},},
program.getInfoLog(), program.getInfoDebugLog(),
validation_result, logger.getAllMessages(), disassembly_stream.str()};
} else {
return {{{shaderName, shader.getInfoLog(), shader.getInfoDebugLog()},},
program.getInfoLog(), program.getInfoDebugLog(), true, "", ""};
} }
std::ostringstream disassembly_stream;
spv::Parameterize();
spv::Disassemble(disassembly_stream, spirv_binary);
bool validation_result = !options().validate || logger.getAllMessages().empty();
return {{
{shaderName, shader.getInfoLog(), shader.getInfoDebugLog()},
},
program.getInfoLog(),
program.getInfoDebugLog(),
validation_result,
logger.getAllMessages(),
disassembly_stream.str()};
} }
// Compiles and links the given source |code| of the given shader // Compiles and links the given source |code| of the given shader