SPV: Add OpSource shader source code and file name.
authorJohn Kessenich <cepheus@frii.com>
Wed, 31 May 2017 23:11:16 +0000 (17:11 -0600)
committerJohn Kessenich <cepheus@frii.com>
Wed, 31 May 2017 23:14:15 +0000 (17:14 -0600)
12 files changed:
SPIRV/GlslangToSpv.cpp
SPIRV/GlslangToSpv.h
SPIRV/SpvBuilder.cpp
SPIRV/SpvBuilder.h
SPIRV/spvIR.h
StandAlone/StandAlone.cpp
Test/baseResults/spv.debugInfo.frag.out [new file with mode: 0644]
Test/runtests
Test/spv.debugInfo.frag [new file with mode: 0644]
glslang/MachineIndependent/ShaderLang.cpp
glslang/MachineIndependent/localintermediate.h
glslang/Public/ShaderLang.h

index 966acef4941ca1b589fc5db26daafc979f6ecc96..a2bd8484f85458bdf293e1854951ba1e1f356105 100755 (executable)
@@ -101,7 +101,7 @@ private:
 //
 class TGlslangToSpvTraverser : public glslang::TIntermTraverser {
 public:
-    TGlslangToSpvTraverser(const glslang::TIntermediate*, spv::SpvBuildLogger* logger);
+    TGlslangToSpvTraverser(const glslang::TIntermediate*, spv::SpvBuildLogger* logger, glslang::SpvOptions& options);
     virtual ~TGlslangToSpvTraverser() { }
 
     bool visitAggregate(glslang::TVisit, glslang::TIntermAggregate*);
@@ -179,6 +179,7 @@ protected:
     spv::Id createShortCircuit(glslang::TOperator, glslang::TIntermTyped& left, glslang::TIntermTyped& right);
     spv::Id getExtBuiltins(const char* name);
 
+    glslang::SpvOptions& options;
     spv::Function* shaderEntry;
     spv::Function* currentFunction;
     spv::Instruction* entryPoint;
@@ -851,8 +852,11 @@ bool HasNonLayoutQualifiers(const glslang::TType& type, const glslang::TQualifie
 // Implement the TGlslangToSpvTraverser class.
 //
 
-TGlslangToSpvTraverser::TGlslangToSpvTraverser(const glslang::TIntermediate* glslangIntermediate, spv::SpvBuildLogger* buildLogger)
-    : TIntermTraverser(true, false, true), shaderEntry(nullptr), currentFunction(nullptr),
+TGlslangToSpvTraverser::TGlslangToSpvTraverser(const glslang::TIntermediate* glslangIntermediate,
+                                               spv::SpvBuildLogger* buildLogger, glslang::SpvOptions& options)
+    : TIntermTraverser(true, false, true),
+      options(options),
+      shaderEntry(nullptr), currentFunction(nullptr),
       sequenceDepth(0), logger(buildLogger),
       builder((glslang::GetKhronosToolId() << 16) | GeneratorVersion, logger),
       inEntryPoint(false), entryPointTerminated(false), linkageOnly(false),
@@ -862,6 +866,10 @@ TGlslangToSpvTraverser::TGlslangToSpvTraverser(const glslang::TIntermediate* gls
 
     builder.clearAccessChain();
     builder.setSource(TranslateSourceLanguage(glslangIntermediate->getSource(), glslangIntermediate->getProfile()), glslangIntermediate->getVersion());
+    if (options.generateDebugInfo) {
+        builder.setSourceFile(glslangIntermediate->getSourceFile());
+        builder.setSourceText(glslangIntermediate->getSourceText());
+    }
     stdBuiltins = builder.import("GLSL.std.450");
     builder.setMemoryModel(spv::AddressingModelLogical, spv::MemoryModelGLSL450);
     shaderEntry = builder.makeEntryPoint(glslangIntermediate->getEntryPointName().c_str());
@@ -5561,22 +5569,27 @@ void OutputSpvHex(const std::vector<unsigned int>& spirv, const char* baseName,
 //
 // Set up the glslang traversal
 //
-void GlslangToSpv(const glslang::TIntermediate& intermediate, std::vector<unsigned int>& spirv)
+void GlslangToSpv(const glslang::TIntermediate& intermediate, std::vector<unsigned int>& spirv, SpvOptions* options)
 {
     spv::SpvBuildLogger logger;
-    GlslangToSpv(intermediate, spirv, &logger);
+    GlslangToSpv(intermediate, spirv, &logger, options);
 }
 
-void GlslangToSpv(const glslang::TIntermediate& intermediate, std::vector<unsigned int>& spirv, spv::SpvBuildLogger* logger)
+void GlslangToSpv(const glslang::TIntermediate& intermediate, std::vector<unsigned int>& spirv,
+                  spv::SpvBuildLogger* logger, SpvOptions* options)
 {
     TIntermNode* root = intermediate.getTreeRoot();
 
     if (root == 0)
         return;
 
+    glslang::SpvOptions defaultOptions;
+    if (options == nullptr)
+        options = &defaultOptions;
+
     glslang::GetThreadPoolAllocator().push();
 
-    TGlslangToSpvTraverser it(&intermediate, logger);
+    TGlslangToSpvTraverser it(&intermediate, logger, *options);
     root->traverse(&it);
     it.finishSpv();
     it.dumpSpv(spirv);
index aba48a383fc05846ac6db7e4c58914dfc67ae1f1..0dad4d219fc8f317add572bd06293a82e8187c84 100644 (file)
 
 namespace glslang {
 
+struct SpvOptions {
+    SpvOptions() : generateDebugInfo(false) { }
+    bool generateDebugInfo;
+};
+
 void GetSpirvVersion(std::string&);
-void GlslangToSpv(const glslang::TIntermediate& intermediate, std::vector<unsigned int>& spirv);
-void GlslangToSpv(const glslang::TIntermediate& intermediate, std::vector<unsigned int>& spirv, spv::SpvBuildLogger* logger);
+void GlslangToSpv(const glslang::TIntermediate& intermediate, std::vector<unsigned int>& spirv,
+                  SpvOptions* options = nullptr);
+void GlslangToSpv(const glslang::TIntermediate& intermediate, std::vector<unsigned int>& spirv,
+                  spv::SpvBuildLogger* logger, SpvOptions* options = nullptr);
 void OutputSpvBin(const std::vector<unsigned int>& spirv, const char* baseName);
 void OutputSpvHex(const std::vector<unsigned int>& spirv, const char* baseName, const char* varName);
 
index 0add1db40d962eb133c6dcfe019a24afe01d271d..37c4cb5b1d86f47b59a1aa326fa441cfc63b1601 100644 (file)
@@ -59,6 +59,7 @@ namespace spv {
 Builder::Builder(unsigned int magicNumber, SpvBuildLogger* buildLogger) :
     source(SourceLanguageUnknown),
     sourceVersion(0),
+    sourceFileStringId(NoResult),
     addressModel(AddressingModelLogical),
     memoryModel(MemoryModelGLSL450),
     builderNumber(magicNumber),
@@ -2411,12 +2412,8 @@ void Builder::dump(std::vector<unsigned int>& out) const
     dumpInstructions(out, executionModes);
 
     // Debug instructions
-    if (source != SourceLanguageUnknown) {
-        Instruction sourceInst(0, 0, OpSource);
-        sourceInst.addImmediateOperand(source);
-        sourceInst.addImmediateOperand(sourceVersion);
-        sourceInst.dump(out);
-    }
+    dumpInstructions(out, strings);
+    dumpSourceInstructions(out);
     for (int e = 0; e < (int)sourceExtensions.size(); ++e) {
         Instruction sourceExtInst(0, 0, OpSourceExtension);
         sourceExtInst.addStringOperand(sourceExtensions[e]);
@@ -2574,6 +2571,48 @@ void Builder::createConditionalBranch(Id condition, Block* thenBlock, Block* els
     elseBlock->addPredecessor(buildPoint);
 }
 
+// OpSource
+// [OpSourceContinued]
+// ...
+void Builder::dumpSourceInstructions(std::vector<unsigned int>& out) const
+{
+    const int maxWordCount = 0xFFFF;
+    const int opSourceWordCount = 4;
+    const int nonNullBytesPerInstruction = 4 * (maxWordCount - opSourceWordCount) - 1;
+
+    if (source != SourceLanguageUnknown) {
+        // OpSource Language Version File Source
+        Instruction sourceInst(NoResult, NoType, OpSource);
+        sourceInst.addImmediateOperand(source);
+        sourceInst.addImmediateOperand(sourceVersion);
+        // File operand
+        if (sourceFileStringId != NoResult) {
+            sourceInst.addIdOperand(sourceFileStringId);
+            // Source operand
+            if (sourceText.size() > 0) {
+                int nextByte = 0;
+                std::string subString;
+                while ((int)sourceText.size() - nextByte > 0) {
+                    subString = sourceText.substr(nextByte, nonNullBytesPerInstruction);
+                    if (nextByte == 0) {
+                        // OpSource
+                        sourceInst.addStringOperand(subString.c_str());
+                        sourceInst.dump(out);
+                    } else {
+                        // OpSourcContinued
+                        Instruction sourceContinuedInst(OpSourceContinued);
+                        sourceContinuedInst.addStringOperand(subString.c_str());
+                        sourceContinuedInst.dump(out);
+                    }
+                    nextByte += nonNullBytesPerInstruction;
+                }
+            } else
+                sourceInst.dump(out);
+        } else
+            sourceInst.dump(out);
+    }
+}
+
 void Builder::dumpInstructions(std::vector<unsigned int>& out, const std::vector<std::unique_ptr<Instruction> >& instructions) const
 {
     for (int i = 0; i < (int)instructions.size(); ++i) {
index d93174ed201598fc87ddc219d30c31a5eab0a088..42c18b7d868b023921f050af1b75149fde13f57f 100755 (executable)
@@ -70,6 +70,14 @@ public:
         source = lang;
         sourceVersion = version;
     }
+    void setSourceFile(const std::string& file)
+    {
+        Instruction* fileString = new Instruction(getUniqueId(), NoType, OpString);
+        fileString->addStringOperand(file.c_str());
+        sourceFileStringId = fileString->getResultId();
+        strings.push_back(std::unique_ptr<Instruction>(fileString));
+    }
+    void setSourceText(const std::string& text) { sourceText = text; }
     void addSourceExtension(const char* ext) { sourceExtensions.push_back(ext); }
     void addExtension(const char* ext) { extensions.insert(ext); }
     Id import(const char*);
@@ -561,10 +569,13 @@ public:
     void simplifyAccessChainSwizzle();
     void createAndSetNoPredecessorBlock(const char*);
     void createSelectionMerge(Block* mergeBlock, unsigned int control);
+    void dumpSourceInstructions(std::vector<unsigned int>&) const;
     void dumpInstructions(std::vector<unsigned int>&, const std::vector<std::unique_ptr<Instruction> >&) const;
 
     SourceLanguage source;
     int sourceVersion;
+    spv::Id sourceFileStringId;
+    std::string sourceText;
     std::set<std::string> extensions;
     std::vector<const char*> sourceExtensions;
     AddressingModel addressModel;
@@ -579,6 +590,7 @@ public:
     AccessChain accessChain;
 
     // special blocks of instructions for output
+    std::vector<std::unique_ptr<Instruction> > strings;
     std::vector<std::unique_ptr<Instruction> > imports;
     std::vector<std::unique_ptr<Instruction> > entryPoints;
     std::vector<std::unique_ptr<Instruction> > executionModes;
@@ -599,7 +611,7 @@ public:
     // Our loop stack.
     std::stack<LoopBlocks> loops;
 
-    // The stream for outputing warnings and errors.
+    // The stream for outputting warnings and errors.
     SpvBuildLogger* logger;
 };  // end Builder class
 
index ce8b4b8a062e64f585cd577bdbe22493777977e6..087a53f2696af34e5061f907bf799292d52e9a62 100755 (executable)
@@ -87,7 +87,6 @@ public:
     void addImmediateOperand(unsigned int immediate) { operands.push_back(immediate); }
     void addStringOperand(const char* str)
     {
-        originalString = str;
         unsigned int word;
         char* wordString = (char*)&word;
         char* wordPtr = wordString;
@@ -120,7 +119,6 @@ public:
     Id getTypeId() const { return typeId; }
     Id getIdOperand(int op) const { return operands[op]; }
     unsigned int getImmediateOperand(int op) const { return operands[op]; }
-    const char* getStringOperand() const { return originalString.c_str(); }
 
     // Write out the binary form.
     void dump(std::vector<unsigned int>& out) const
@@ -151,7 +149,6 @@ protected:
     Id typeId;
     Op opCode;
     std::vector<Id> operands;
-    std::string originalString;        // could be optimized away; convenience for getting string operand
     Block* block;
 };
 
index 8a6b12dc456aa3437dcfb2b46e4edd44894d53cb..ef47662cd12f99064102e8bd4c98b442c3d122d2 100644 (file)
@@ -91,6 +91,7 @@ enum TOptions {
     EOptionHlslOffsets          = (1 << 23),
     EOptionHlslIoMapping        = (1 << 24),
     EOptionAutoMapLocations     = (1 << 25),
+    EOptionDebug                = (1 << 26),
 };
 
 //
@@ -448,6 +449,9 @@ void ProcessArguments(std::vector<std::unique_ptr<glslang::TWorkItem>>& workItem
                 } else
                     Error("no <entry-point> provided for -e");
                 break;
+            case 'g':
+                Options |= EOptionDebug;
+                break;
             case 'h':
                 usage();
                 break;
@@ -539,6 +543,8 @@ void SetMessageOptions(EShMessages& messages)
         messages = (EShMessages)(messages | EShMsgKeepUncalled);
     if (Options & EOptionHlslOffsets)
         messages = (EShMessages)(messages | EShMsgHlslOffsets);
+    if (Options & EOptionDebug)
+        messages = (EShMessages)(messages | EShMsgDebugInfo);
 }
 
 //
@@ -722,7 +728,10 @@ void CompileAndLinkShaderUnits(std::vector<ShaderCompUnit> compUnits)
                     std::vector<unsigned int> spirv;
                     std::string warningsErrors;
                     spv::SpvBuildLogger logger;
-                    glslang::GlslangToSpv(*program.getIntermediate((EShLanguage)stage), spirv, &logger);
+                    glslang::SpvOptions spvOptions;
+                    if (Options & EOptionDebug)
+                        spvOptions.generateDebugInfo = true;
+                    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.
@@ -1031,6 +1040,7 @@ void usage()
            "              (default is ES version 100)\n"
            "  -D          input is HLSL\n"
            "  -e          specify entry-point name\n"
+           "  -g          generate debug information\n"
            "  -h          print this usage message\n"
            "  -i          intermediate tree (glslang AST) is printed out\n"
            "  -l          link all input files together to form a single module\n"
diff --git a/Test/baseResults/spv.debugInfo.frag.out b/Test/baseResults/spv.debugInfo.frag.out
new file mode 100644 (file)
index 0000000..6e9e208
--- /dev/null
@@ -0,0 +1,99 @@
+spv.debugInfo.frag
+Warning, version 450 is not yet complete; most version-specific features are present, but some are missing.
+
+// Module Version 10000
+// Generated by (magic number): 80001
+// Id's are bound by 40
+
+                              Capability Shader
+               2:             ExtInstImport  "GLSL.std.450"
+                              MemoryModel Logical GLSL450
+                              EntryPoint Fragment 5  "main" 17 24
+                              ExecutionMode 5 OriginUpperLeft
+               1:             String  "spv.debugInfo.frag"
+                              Source GLSL 450 1  "#version 450
+
+struct S {
+    int a;
+};
+
+uniform ubuf {
+    S s;
+};
+
+layout(location = 0) in vec4 inv;
+layout(location = 0) out vec4 outv;
+
+void foo(S s)
+{
+    outv = s.a * inv;
+}
+
+void main()
+{
+    foo(s);
+}"
+                              Name 5  "main"
+                              Name 8  "S"
+                              MemberName 8(S) 0  "a"
+                              Name 12  "foo(struct-S-i11;"
+                              Name 11  "s"
+                              Name 17  "outv"
+                              Name 24  "inv"
+                              Name 27  "S"
+                              MemberName 27(S) 0  "a"
+                              Name 28  "ubuf"
+                              MemberName 28(ubuf) 0  "s"
+                              Name 30  ""
+                              Name 31  "S"
+                              MemberName 31(S) 0  "a"
+                              Name 33  "param"
+                              Decorate 17(outv) Location 0
+                              Decorate 24(inv) Location 0
+                              MemberDecorate 27(S) 0 Offset 0
+                              MemberDecorate 28(ubuf) 0 Offset 0
+                              Decorate 28(ubuf) Block
+                              Decorate 30 DescriptorSet 0
+               3:             TypeVoid
+               4:             TypeFunction 3
+               7:             TypeInt 32 1
+            8(S):             TypeStruct 7(int)
+               9:             TypePointer Function 8(S)
+              10:             TypeFunction 3 9(ptr)
+              14:             TypeFloat 32
+              15:             TypeVector 14(float) 4
+              16:             TypePointer Output 15(fvec4)
+        17(outv):     16(ptr) Variable Output
+              18:      7(int) Constant 0
+              19:             TypePointer Function 7(int)
+              23:             TypePointer Input 15(fvec4)
+         24(inv):     23(ptr) Variable Input
+           27(S):             TypeStruct 7(int)
+        28(ubuf):             TypeStruct 27(S)
+              29:             TypePointer Uniform 28(ubuf)
+              30:     29(ptr) Variable Uniform
+           31(S):             TypeStruct 7(int)
+              32:             TypePointer Function 31(S)
+              34:             TypePointer Uniform 27(S)
+         5(main):           3 Function None 4
+               6:             Label
+       33(param):     32(ptr) Variable Function
+              35:     34(ptr) AccessChain 30 18
+              36:       27(S) Load 35
+              37:      7(int) CompositeExtract 36 0
+              38:     19(ptr) AccessChain 33(param) 18
+                              Store 38 37
+              39:           3 FunctionCall 12(foo(struct-S-i11;) 33(param)
+                              Return
+                              FunctionEnd
+12(foo(struct-S-i11;):           3 Function None 10
+           11(s):      9(ptr) FunctionParameter
+              13:             Label
+              20:     19(ptr) AccessChain 11(s) 18
+              21:      7(int) Load 20
+              22:   14(float) ConvertSToF 21
+              25:   15(fvec4) Load 24(inv)
+              26:   15(fvec4) VectorTimesScalar 25 22
+                              Store 17(outv) 26
+                              Return
+                              FunctionEnd
index a3c89e045b6524a9eb0097e6a621cc61096029b1..ac117fb1d1e91e367416f193942a1b5b4c460833 100755 (executable)
@@ -90,14 +90,21 @@ diff -b $BASEDIR/hlsl.hlslOffset.vert.out $TARGETDIR/hlsl.hlslOffset.vert.out ||
 #
 echo Configuring HLSL descriptor set and binding number manually
 $EXE -V -D -e main -H hlsl.multiDescriptorSet.frag --rsb frag t0 0 0 t1 1 0 s0 0 1 s1 1 1 b0 2 0 b1 2 1 b2 2 2 > $TARGETDIR/hlsl.multiDescriptorSet.frag.out
-diff -b $BASEDIR/hlsl.multiDescriptorSet.frag.out $TARGETDIR/hlsl.multiDescriptorSet.frag.out
+diff -b $BASEDIR/hlsl.multiDescriptorSet.frag.out $TARGETDIR/hlsl.multiDescriptorSet.frag.out || HASERROR=1
 
 #
 # Testing location error
 #
 echo Testing SPV no location
 $EXE -V -C spv.noLocation.vert > $TARGETDIR/spv.noLocation.vert.out
-diff -b $BASEDIR/spv.noLocation.vert.out $TARGETDIR/spv.noLocation.vert.out
+diff -b $BASEDIR/spv.noLocation.vert.out $TARGETDIR/spv.noLocation.vert.out || HASERROR=1
+
+#
+# Testing debug information
+#
+echo Testing SPV Debug Information
+$EXE -g -H spv.debugInfo.frag > $TARGETDIR/spv.debugInfo.frag.out
+diff -b $BASEDIR/spv.debugInfo.frag.out $TARGETDIR/spv.debugInfo.frag.out || HASERROR=1
 
 #
 # Final checking
diff --git a/Test/spv.debugInfo.frag b/Test/spv.debugInfo.frag
new file mode 100644 (file)
index 0000000..e2fc257
--- /dev/null
@@ -0,0 +1,22 @@
+#version 450\r
+\r
+struct S {\r
+    int a;\r
+};\r
+\r
+uniform ubuf {\r
+    S s;\r
+};\r
+\r
+layout(location = 0) in vec4 inv;\r
+layout(location = 0) out vec4 outv;\r
+\r
+void foo(S s)\r
+{\r
+    outv = s.a * inv;\r
+}\r
+\r
+void main()\r
+{\r
+    foo(s);\r
+}
\ No newline at end of file
index d563bc2cdb0b5ddb5167ed1a2ab39c7a155373fe..297edfd8841bc212007b01914ceab0d70126543f 100644 (file)
@@ -728,6 +728,11 @@ bool ProcessDeferred(
         intermediate.setOriginUpperLeft();
     if ((messages & EShMsgHlslOffsets) || (messages & EShMsgReadHlsl))
         intermediate.setHlslOffsets();
+    if (messages & EShMsgDebugInfo) {
+        intermediate.setSourceFile(names[numPre]);
+        for (int s = 0; s < numStrings; ++s)
+            intermediate.addSourceText(strings[numPre]);
+    }
     SetupBuiltinSymbolTable(version, profile, spvVersion, source);
 
     TSymbolTable* cachedTable = SharedSymbolTables[MapVersionToIndex(version)]
index 8f6fdffe4486ecbc7591ca693e538fd427acee75..56319dc6997aaa9bbd0f17fc2a146e5b50cab92f 100644 (file)
@@ -451,6 +451,11 @@ public:
         return semanticNameSet.insert(name).first->c_str();
     }
 
+    void setSourceFile(const char* file) { sourceFile = file; }
+    const std::string& getSourceFile() const { return sourceFile; }
+    void addSourceText(const char* text) { sourceText = sourceText + text; }
+    const std::string& getSourceText() const { return sourceText; }
+
     const char* const implicitThisName = "@this";
 
 protected:
@@ -541,6 +546,10 @@ protected:
 
     EShTextureSamplerTransformMode textureSamplerTransformMode;
 
+    // source code of shader, useful as part of debug information
+    std::string sourceFile;
+    std::string sourceText;
+
 private:
     void operator=(TIntermediate&); // prevent assignments
 };
index 9cd999ba29ef440db8a8de4b3249903bc31f8c66..2d366d1f6b7e8c69855cf321aee9b5ba239f72aa 100644 (file)
@@ -157,6 +157,7 @@ enum EShMessages {
     EShMsgCascadingErrors  = (1 << 7),  // get cascading errors; risks error-recovery issues, instead of an early exit
     EShMsgKeepUncalled     = (1 << 8),  // for testing, don't eliminate uncalled functions
     EShMsgHlslOffsets      = (1 << 9),  // allow block offsets to follow HLSL rules instead of GLSL rules
+    EShMsgDebugInfo        = (1 << 10), // save debug information
 };
 
 //