diff --git a/SPIRV/GlslangToSpv.cpp b/SPIRV/GlslangToSpv.cpp
index 966acef4941ca1b589fc5db26daafc979f6ecc96..e175bfc6516f055ac1080063047d9dce55588e17 100755
--- a/SPIRV/GlslangToSpv.cpp
+++ b/SPIRV/GlslangToSpv.cpp
@@ -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,11 @@ 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());
+        builder.setEmitOpLines();
+    }
     stdBuiltins = builder.import("GLSL.std.450");
     builder.setMemoryModel(spv::AddressingModelLogical, spv::MemoryModelGLSL450);
     shaderEntry = builder.makeEntryPoint(glslangIntermediate->getEntryPointName().c_str());
@@ -1069,6 +1078,8 @@ void TGlslangToSpvTraverser::visitSymbol(glslang::TIntermSymbol* symbol)
 
 bool TGlslangToSpvTraverser::visitBinary(glslang::TVisit /* visit */, glslang::TIntermBinary* node)
 {
+    builder.setLine(node->getLoc().line);
+
     SpecConstantOpModeGuard spec_constant_op_mode_setter(&builder);
     if (node->getType().getQualifier().isSpecConstant())
         spec_constant_op_mode_setter.turnOnSpecConstantOpMode();
@@ -1256,6 +1267,8 @@ bool TGlslangToSpvTraverser::visitBinary(glslang::TVisit /* visit */, glslang::T
 
 bool TGlslangToSpvTraverser::visitUnary(glslang::TVisit /* visit */, glslang::TIntermUnary* node)
 {
+    builder.setLine(node->getLoc().line);
+
     SpecConstantOpModeGuard spec_constant_op_mode_setter(&builder);
     if (node->getType().getQualifier().isSpecConstant())
         spec_constant_op_mode_setter.turnOnSpecConstantOpMode();
@@ -1499,6 +1512,7 @@ bool TGlslangToSpvTraverser::visitAggregate(glslang::TVisit visit, glslang::TInt
         return false;
     case glslang::EOpFunctionCall:
     {
+        builder.setLine(node->getLoc().line);
         if (node->isUserDefined())
             result = handleUserFunctionCall(node);
         // assert(result);  // this can happen for bad shaders because the call graph completeness checking is not yet done
@@ -1605,6 +1619,7 @@ bool TGlslangToSpvTraverser::visitAggregate(glslang::TVisit visit, glslang::TInt
     case glslang::EOpConstructStruct:
     case glslang::EOpConstructTextureSampler:
     {
+        builder.setLine(node->getLoc().line);
         std::vector<spv::Id> arguments;
         translateArguments(*node, arguments);
         spv::Id constructed;
@@ -1715,6 +1730,7 @@ bool TGlslangToSpvTraverser::visitAggregate(glslang::TVisit visit, glslang::TInt
         right->traverse(this);
         spv::Id rightId = accessChainLoad(right->getType());
 
+        builder.setLine(node->getLoc().line);
         result = createBinaryOperation(binOp, precision, TranslateNoContractionDecoration(node->getType().getQualifier()),
                                        resultType(), leftId, rightId,
                                        left->getType().getBasicType(), reduceComparison);
@@ -1787,10 +1803,13 @@ bool TGlslangToSpvTraverser::visitAggregate(glslang::TVisit visit, glslang::TInt
             glslangOperands[arg]->traverse(this);
         if (lvalue)
             operands.push_back(builder.accessChainGetLValue());
-        else
+        else {
+            builder.setLine(node->getLoc().line);
             operands.push_back(accessChainLoad(glslangOperands[arg]->getAsTyped()->getType()));
+        }
     }
 
+    builder.setLine(node->getLoc().line);
     if (atomic) {
         // Handle all atomics
         result = createAtomicOperation(node->getOp(), precision, resultType(), operands, node->getBasicType());
@@ -1872,6 +1891,8 @@ bool TGlslangToSpvTraverser::visitSelection(glslang::TVisit /* visit */, glslang
         node->getFalseBlock()->traverse(this);
         spv::Id falseValue = accessChainLoad(node->getTrueBlock()->getAsTyped()->getType());
 
+        builder.setLine(node->getLoc().line);
+
         // smear condition to vector, if necessary (AST is always scalar)
         if (builder.isVector(trueValue))
             condition = builder.smearScalar(spv::NoPrecision, condition, 
@@ -2014,6 +2035,7 @@ bool TGlslangToSpvTraverser::visitLoop(glslang::TVisit /* visit */, glslang::TIn
     // by a block-ending branch.  But we don't want to put any other body/test
     // instructions in it, since the body/test may have arbitrary instructions,
     // including merges of its own.
+    builder.setLine(node->getLoc().line);
     builder.setBuildPoint(&blocks.head);
     builder.createLoopMerge(&blocks.merge, &blocks.continue_target, control);
     if (node->testFirst() && node->getTest()) {
@@ -2022,8 +2044,7 @@ bool TGlslangToSpvTraverser::visitLoop(glslang::TVisit /* visit */, glslang::TIn
 
         builder.setBuildPoint(&test);
         node->getTest()->traverse(this);
-        spv::Id condition =
-            accessChainLoad(node->getTest()->getType());
+        spv::Id condition = accessChainLoad(node->getTest()->getType());
         builder.createConditionalBranch(condition, &blocks.body, &blocks.merge);
 
         builder.setBuildPoint(&blocks.body);
@@ -2038,6 +2059,7 @@ bool TGlslangToSpvTraverser::visitLoop(glslang::TVisit /* visit */, glslang::TIn
             node->getTerminal()->traverse(this);
         builder.createBranch(&blocks.head);
     } else {
+        builder.setLine(node->getLoc().line);
         builder.createBranch(&blocks.body);
 
         breakForLoop.push(true);
@@ -2072,6 +2094,8 @@ bool TGlslangToSpvTraverser::visitBranch(glslang::TVisit /* visit */, glslang::T
     if (node->getExpression())
         node->getExpression()->traverse(this);
 
+    builder.setLine(node->getLoc().line);
+
     switch (node->getFlowOp()) {
     case glslang::EOpKill:
         builder.makeDiscard();
@@ -3049,9 +3073,11 @@ void TGlslangToSpvTraverser::translateArguments(glslang::TIntermUnary& node, std
 
 spv::Id TGlslangToSpvTraverser::createImageTextureFunctionCall(glslang::TIntermOperator* node)
 {
-    if (! node->isImage() && ! node->isTexture()) {
+    if (! node->isImage() && ! node->isTexture())
         return spv::NoResult;
-    }
+
+    builder.setLine(node->getLoc().line);
+
     auto resultType = [&node,this]{ return convertGlslangToSpvType(node->getType()); };
 
     // Process a GLSL texturing op (will be SPV image)
@@ -5561,22 +5587,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);
diff --git a/SPIRV/GlslangToSpv.h b/SPIRV/GlslangToSpv.h
index aba48a383fc05846ac6db7e4c58914dfc67ae1f1..0dad4d219fc8f317add572bd06293a82e8187c84 100644
--- a/SPIRV/GlslangToSpv.h
+++ b/SPIRV/GlslangToSpv.h
@@ -47,9 +47,16 @@
 
 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);
 
diff --git a/SPIRV/SpvBuilder.cpp b/SPIRV/SpvBuilder.cpp
index 0add1db40d962eb133c6dcfe019a24afe01d271d..ffd17af1b72bc2cc9ad099d55e18478ad538e3b0 100644
--- a/SPIRV/SpvBuilder.cpp
+++ b/SPIRV/SpvBuilder.cpp
@@ -59,6 +59,9 @@ namespace spv {
 Builder::Builder(unsigned int magicNumber, SpvBuildLogger* buildLogger) :
     source(SourceLanguageUnknown),
     sourceVersion(0),
+    sourceFileStringId(NoResult),
+    currentLine(0),
+    emitOpLines(false),
     addressModel(AddressingModelLogical),
     memoryModel(MemoryModelGLSL450),
     builderNumber(magicNumber),
@@ -84,6 +87,26 @@ Id Builder::import(const char* name)
     return import->getResultId();
 }
 
+// Emit an OpLine if we've been asked to emit OpLines and the line number
+// has changed since the last time, and is a valid line number.
+void Builder::setLine(int lineNum)
+{
+    if (lineNum != 0 && lineNum != currentLine) {
+        currentLine = lineNum;
+        if (emitOpLines)
+            addLine(sourceFileStringId, currentLine, 0);
+    }
+}
+
+void Builder::addLine(Id fileName, int lineNum, int column)
+{
+    Instruction* line = new Instruction(OpLine);
+    line->addIdOperand(fileName);
+    line->addImmediateOperand(lineNum);
+    line->addImmediateOperand(column);
+    buildPoint->addInstruction(std::unique_ptr<Instruction>(line));
+}
+
 // For creating new groupedTypes (will return old type if the requested one was already made).
 Id Builder::makeVoidType()
 {
@@ -928,17 +951,6 @@ void Builder::addMemberName(Id id, int memberNumber, const char* string)
     names.push_back(std::unique_ptr<Instruction>(name));
 }
 
-void Builder::addLine(Id target, Id fileName, int lineNum, int column)
-{
-    Instruction* line = new Instruction(OpLine);
-    line->addIdOperand(target);
-    line->addIdOperand(fileName);
-    line->addImmediateOperand(lineNum);
-    line->addImmediateOperand(column);
-
-    lines.push_back(std::unique_ptr<Instruction>(line));
-}
-
 void Builder::addDecoration(Id id, Decoration decoration, int num)
 {
     if (decoration == spv::DecorationMax)
@@ -2411,12 +2423,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 +2582,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) {
diff --git a/SPIRV/SpvBuilder.h b/SPIRV/SpvBuilder.h
index d93174ed201598fc87ddc219d30c31a5eab0a088..60d97e9c81794e91c7c5d605dce10fa93072c55e 100755
--- a/SPIRV/SpvBuilder.h
+++ b/SPIRV/SpvBuilder.h
@@ -70,7 +70,16 @@ 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 setEmitOpLines() { emitOpLines = true; }
     void addExtension(const char* ext) { extensions.insert(ext); }
     Id import(const char*);
     void setMemoryModel(spv::AddressingModel addr, spv::MemoryModel mem)
@@ -92,6 +101,12 @@ public:
         return id;
     }
 
+    // Log the current line, and if different than the last one,
+    // issue a new OpLine, using the current file name.
+    void setLine(int line);
+    // Low-level OpLine. See setLine() for a layered helper.
+    void addLine(Id fileName, int line, int column);
+
     // For creating new types (will return old type if the requested one was already made).
     Id makeVoidType();
     Id makeBoolType();
@@ -213,7 +228,6 @@ public:
     void addExecutionMode(Function*, ExecutionMode mode, int value1 = -1, int value2 = -1, int value3 = -1);
     void addName(Id, const char* name);
     void addMemberName(Id, int member, const char* name);
-    void addLine(Id target, Id fileName, int line, int column);
     void addDecoration(Id, Decoration, int num = -1);
     void addMemberDecoration(Id, unsigned int member, Decoration, int num = -1);
 
@@ -561,10 +575,15 @@ 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;
+    int currentLine;
+    bool emitOpLines;
     std::set<std::string> extensions;
     std::vector<const char*> sourceExtensions;
     AddressingModel addressModel;
@@ -579,6 +598,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 +619,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
 
diff --git a/SPIRV/spvIR.h b/SPIRV/spvIR.h
index ce8b4b8a062e64f585cd577bdbe22493777977e6..087a53f2696af34e5061f907bf799292d52e9a62 100755
--- a/SPIRV/spvIR.h
+++ b/SPIRV/spvIR.h
@@ -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;
 };
 
diff --git a/StandAlone/StandAlone.cpp b/StandAlone/StandAlone.cpp
index 8a6b12dc456aa3437dcfb2b46e4edd44894d53cb..ef47662cd12f99064102e8bd4c98b442c3d122d2 100644
--- a/StandAlone/StandAlone.cpp
+++ b/StandAlone/StandAlone.cpp
@@ -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
index 0000000000000000000000000000000000000000..16f605bfa29c6d9b728140c1bad0d81d44d508ff
--- /dev/null
+++ b/Test/baseResults/spv.debugInfo.frag.out
@@ -0,0 +1,272 @@
+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 126
+
+                              Capability Shader
+               2:             ExtInstImport  "GLSL.std.450"
+                              MemoryModel Logical GLSL450
+                              EntryPoint Fragment 5  "main" 24 52
+                              ExecutionMode 5 OriginUpperLeft
+               1:             String  "spv.debugInfo.frag"
+                              Source GLSL 450 1  "#version 450
+
+struct S {
+    int a;
+};
+
+uniform ubuf {
+    S s;
+};
+
+uniform sampler2D s2d;
+
+layout(location = 0) in vec4 inv;
+layout(location = 0) out vec4 outv;
+
+vec4 foo(S s)
+{
+    vec4 r = s.a * inv;
+    ++r;
+    if (r.x > 3.0)
+        --r;
+    else
+        r *= 2;
+
+    return r;
+}
+
+void main()
+{
+    outv = foo(s);
+    outv += texture(s2d, vec2(0.5));
+
+    switch (s.a) {
+    case 10:
+        ++outv;
+        break;
+    case 20:
+        outv = 2 * outv;
+        ++outv;
+        break;
+    default:
+        --outv;
+        break;
+    }
+
+    for (int i = 0; i < 10; ++i)
+        outv *= 3.0;
+
+    outv.x < 10.0 ?
+        outv = sin(outv) :
+        outv = cos(outv);
+}"
+                              Name 5  "main"
+                              Name 8  "S"
+                              MemberName 8(S) 0  "a"
+                              Name 14  "foo(struct-S-i11;"
+                              Name 13  "s"
+                              Name 17  "r"
+                              Name 24  "inv"
+                              Name 52  "outv"
+                              Name 53  "S"
+                              MemberName 53(S) 0  "a"
+                              Name 54  "ubuf"
+                              MemberName 54(ubuf) 0  "s"
+                              Name 56  ""
+                              Name 57  "S"
+                              MemberName 57(S) 0  "a"
+                              Name 59  "param"
+                              Name 69  "s2d"
+                              Name 99  "i"
+                              Decorate 24(inv) Location 0
+                              Decorate 52(outv) Location 0
+                              MemberDecorate 53(S) 0 Offset 0
+                              MemberDecorate 54(ubuf) 0 Offset 0
+                              Decorate 54(ubuf) Block
+                              Decorate 56 DescriptorSet 0
+                              Decorate 69(s2d) DescriptorSet 0
+               3:             TypeVoid
+               4:             TypeFunction 3
+               7:             TypeInt 32 1
+            8(S):             TypeStruct 7(int)
+               9:             TypePointer Function 8(S)
+              10:             TypeFloat 32
+              11:             TypeVector 10(float) 4
+              12:             TypeFunction 11(fvec4) 9(ptr)
+              16:             TypePointer Function 11(fvec4)
+              18:      7(int) Constant 0
+              19:             TypePointer Function 7(int)
+              23:             TypePointer Input 11(fvec4)
+         24(inv):     23(ptr) Variable Input
+              28:   10(float) Constant 1065353216
+              31:             TypeInt 32 0
+              32:     31(int) Constant 0
+              33:             TypePointer Function 10(float)
+              36:   10(float) Constant 1077936128
+              37:             TypeBool
+              45:   10(float) Constant 1073741824
+              51:             TypePointer Output 11(fvec4)
+        52(outv):     51(ptr) Variable Output
+           53(S):             TypeStruct 7(int)
+        54(ubuf):             TypeStruct 53(S)
+              55:             TypePointer Uniform 54(ubuf)
+              56:     55(ptr) Variable Uniform
+           57(S):             TypeStruct 7(int)
+              58:             TypePointer Function 57(S)
+              60:             TypePointer Uniform 53(S)
+              66:             TypeImage 10(float) 2D sampled format:Unknown
+              67:             TypeSampledImage 66
+              68:             TypePointer UniformConstant 67
+         69(s2d):     68(ptr) Variable UniformConstant
+              71:             TypeVector 10(float) 2
+              72:   10(float) Constant 1056964608
+              73:   71(fvec2) ConstantComposite 72 72
+              77:             TypePointer Uniform 7(int)
+             106:      7(int) Constant 10
+             111:      7(int) Constant 1
+             114:             TypePointer Output 10(float)
+             117:   10(float) Constant 1092616192
+         5(main):           3 Function None 4
+               6:             Label
+       59(param):     58(ptr) Variable Function
+           99(i):     19(ptr) Variable Function
+             113:     16(ptr) Variable Function
+                              Line 1 30 0
+              61:     60(ptr) AccessChain 56 18
+              62:       53(S) Load 61
+              63:      7(int) CompositeExtract 62 0
+              64:     19(ptr) AccessChain 59(param) 18
+                              Store 64 63
+              65:   11(fvec4) FunctionCall 14(foo(struct-S-i11;) 59(param)
+                              Store 52(outv) 65
+                              Line 1 31 0
+              70:          67 Load 69(s2d)
+              74:   11(fvec4) ImageSampleImplicitLod 70 73
+              75:   11(fvec4) Load 52(outv)
+              76:   11(fvec4) FAdd 75 74
+                              Store 52(outv) 76
+                              Line 1 33 0
+              78:     77(ptr) AccessChain 56 18 18
+              79:      7(int) Load 78
+                              SelectionMerge 83 None
+                              Switch 79 82 
+                                     case 10: 80
+                                     case 20: 81
+              82:               Label
+                                Line 1 42 0
+              94:   11(fvec4)   Load 52(outv)
+              95:   11(fvec4)   CompositeConstruct 28 28 28 28
+              96:   11(fvec4)   FSub 94 95
+                                Store 52(outv) 96
+                                Line 1 43 0
+                                Branch 83
+              80:               Label
+                                Line 1 35 0
+              84:   11(fvec4)   Load 52(outv)
+              85:   11(fvec4)   CompositeConstruct 28 28 28 28
+              86:   11(fvec4)   FAdd 84 85
+                                Store 52(outv) 86
+                                Line 1 36 0
+                                Branch 83
+              81:               Label
+                                Line 1 38 0
+              88:   11(fvec4)   Load 52(outv)
+              89:   11(fvec4)   VectorTimesScalar 88 45
+                                Store 52(outv) 89
+                                Line 1 39 0
+              90:   11(fvec4)   Load 52(outv)
+              91:   11(fvec4)   CompositeConstruct 28 28 28 28
+              92:   11(fvec4)   FAdd 90 91
+                                Store 52(outv) 92
+                                Line 1 40 0
+                                Branch 83
+              83:             Label
+                              Line 1 46 0
+                              Store 99(i) 18
+                              Branch 100
+             100:             Label
+                              LoopMerge 102 103 None
+                              Branch 104
+             104:             Label
+             105:      7(int) Load 99(i)
+             107:    37(bool) SLessThan 105 106
+                              BranchConditional 107 101 102
+             101:               Label
+                                Line 1 47 0
+             108:   11(fvec4)   Load 52(outv)
+             109:   11(fvec4)   VectorTimesScalar 108 36
+                                Store 52(outv) 109
+                                Branch 103
+             103:               Label
+                                Line 1 46 0
+             110:      7(int)   Load 99(i)
+             112:      7(int)   IAdd 110 111
+                                Store 99(i) 112
+                                Branch 100
+             102:             Label
+                              Line 1 49 0
+             115:    114(ptr) AccessChain 52(outv) 32
+             116:   10(float) Load 115
+             118:    37(bool) FOrdLessThan 116 117
+                              SelectionMerge 120 None
+                              BranchConditional 118 119 123
+             119:               Label
+                                Line 1 50 0
+             121:   11(fvec4)   Load 52(outv)
+             122:   11(fvec4)   ExtInst 2(GLSL.std.450) 13(Sin) 121
+                                Store 52(outv) 122
+                                Store 113 122
+                                Branch 120
+             123:               Label
+                                Line 1 51 0
+             124:   11(fvec4)   Load 52(outv)
+             125:   11(fvec4)   ExtInst 2(GLSL.std.450) 14(Cos) 124
+                                Store 52(outv) 125
+                                Store 113 125
+                                Branch 120
+             120:             Label
+                              Return
+                              FunctionEnd
+14(foo(struct-S-i11;):   11(fvec4) Function None 12
+           13(s):      9(ptr) FunctionParameter
+              15:             Label
+           17(r):     16(ptr) Variable Function
+                              Line 1 18 0
+              20:     19(ptr) AccessChain 13(s) 18
+              21:      7(int) Load 20
+              22:   10(float) ConvertSToF 21
+              25:   11(fvec4) Load 24(inv)
+              26:   11(fvec4) VectorTimesScalar 25 22
+                              Store 17(r) 26
+                              Line 1 19 0
+              27:   11(fvec4) Load 17(r)
+              29:   11(fvec4) CompositeConstruct 28 28 28 28
+              30:   11(fvec4) FAdd 27 29
+                              Store 17(r) 30
+                              Line 1 20 0
+              34:     33(ptr) AccessChain 17(r) 32
+              35:   10(float) Load 34
+              38:    37(bool) FOrdGreaterThan 35 36
+                              SelectionMerge 40 None
+                              BranchConditional 38 39 44
+              39:               Label
+                                Line 1 21 0
+              41:   11(fvec4)   Load 17(r)
+              42:   11(fvec4)   CompositeConstruct 28 28 28 28
+              43:   11(fvec4)   FSub 41 42
+                                Store 17(r) 43
+                                Branch 40
+              44:               Label
+                                Line 1 23 0
+              46:   11(fvec4)   Load 17(r)
+              47:   11(fvec4)   VectorTimesScalar 46 45
+                                Store 17(r) 47
+                                Branch 40
+              40:             Label
+                              Line 1 25 0
+              48:   11(fvec4) Load 17(r)
+                              ReturnValue 48
+                              FunctionEnd
diff --git a/Test/runtests b/Test/runtests
index a3c89e045b6524a9eb0097e6a621cc61096029b1..ac117fb1d1e91e367416f193942a1b5b4c460833 100755
--- a/Test/runtests
+++ b/Test/runtests
@@ -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
index 0000000000000000000000000000000000000000..3b6cd27fead7be0d65f1758e3025a19103e5ed81
--- /dev/null
+++ b/Test/spv.debugInfo.frag
@@ -0,0 +1,52 @@
+#version 450
+
+struct S {
+    int a;
+};
+
+uniform ubuf {
+    S s;
+};
+
+uniform sampler2D s2d;
+
+layout(location = 0) in vec4 inv;
+layout(location = 0) out vec4 outv;
+
+vec4 foo(S s)
+{
+    vec4 r = s.a * inv;
+    ++r;
+    if (r.x > 3.0)
+        --r;
+    else
+        r *= 2;
+
+    return r;
+}
+
+void main()
+{
+    outv = foo(s);
+    outv += texture(s2d, vec2(0.5));
+
+    switch (s.a) {
+    case 10:
+        ++outv;
+        break;
+    case 20:
+        outv = 2 * outv;
+        ++outv;
+        break;
+    default:
+        --outv;
+        break;
+    }
+
+    for (int i = 0; i < 10; ++i)
+        outv *= 3.0;
+
+    outv.x < 10.0 ?
+        outv = sin(outv) :
+        outv = cos(outv);
+}
\ No newline at end of file
diff --git a/glslang/MachineIndependent/ShaderLang.cpp b/glslang/MachineIndependent/ShaderLang.cpp
index d563bc2cdb0b5ddb5167ed1a2ab39c7a155373fe..297edfd8841bc212007b01914ceab0d70126543f 100644
--- a/glslang/MachineIndependent/ShaderLang.cpp
+++ b/glslang/MachineIndependent/ShaderLang.cpp
@@ -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)]
diff --git a/glslang/MachineIndependent/localintermediate.h b/glslang/MachineIndependent/localintermediate.h
index 8f6fdffe4486ecbc7591ca693e538fd427acee75..56319dc6997aaa9bbd0f17fc2a146e5b50cab92f 100644
--- a/glslang/MachineIndependent/localintermediate.h
+++ b/glslang/MachineIndependent/localintermediate.h
@@ -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
 };
diff --git a/glslang/Public/ShaderLang.h b/glslang/Public/ShaderLang.h
index 9cd999ba29ef440db8a8de4b3249903bc31f8c66..2d366d1f6b7e8c69855cf321aee9b5ba239f72aa 100644
--- a/glslang/Public/ShaderLang.h
+++ b/glslang/Public/ShaderLang.h
@@ -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
 };
 
 //