Implement 4 AMD-specific extensions.

- Support GL_AMD_shader_ballot (SPV_AMD_shader_ballot).
- Support GL_AMD_shader_trinary_minmax (SPV_AMD_shader_trinary_minmax).
- Support GL_AMD_shader_explicit_vertex_parameter
  (SPV_AMD_shader_explicit_vertex_parameter).
- Support GL_AMD_gcn_shader (SPV_AMD_gcn_shader).
diff --git a/SPIRV/CMakeLists.txt b/SPIRV/CMakeLists.txt
index 5c169b1..9628a82 100755
--- a/SPIRV/CMakeLists.txt
+++ b/SPIRV/CMakeLists.txt
@@ -18,6 +18,11 @@
     doc.h
     disassemble.h)
 
+if(ENABLE_AMD_EXTENSIONS)
+    set(HEADERS
+        GLSL.ext.AMD.h)
+endif(ENABLE_AMD_EXTENSIONS)
+
 add_library(SPIRV STATIC ${SOURCES} ${HEADERS})
 set_property(TARGET SPIRV PROPERTY FOLDER glslang)
 
diff --git a/SPIRV/GLSL.ext.AMD.h b/SPIRV/GLSL.ext.AMD.h
new file mode 100644
index 0000000..633cea0
--- /dev/null
+++ b/SPIRV/GLSL.ext.AMD.h
@@ -0,0 +1,113 @@
+/*
+** Copyright (c) 2014-2016 The Khronos Group Inc.
+**
+** Permission is hereby granted, free of charge, to any person obtaining a copy
+** of this software and/or associated documentation files (the "Materials"),
+** to deal in the Materials without restriction, including without limitation
+** the rights to use, copy, modify, merge, publish, distribute, sublicense,
+** and/or sell copies of the Materials, and to permit persons to whom the
+** Materials are furnished to do so, subject to the following conditions:
+**
+** The above copyright notice and this permission notice shall be included in
+** all copies or substantial portions of the Materials.
+**
+** MODIFICATIONS TO THIS FILE MAY MEAN IT NO LONGER ACCURATELY REFLECTS KHRONOS
+** STANDARDS. THE UNMODIFIED, NORMATIVE VERSIONS OF KHRONOS SPECIFICATIONS AND
+** HEADER INFORMATION ARE LOCATED AT https://www.khronos.org/registry/
+**
+** THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+** OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+** THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+** FROM,OUT OF OR IN CONNECTION WITH THE MATERIALS OR THE USE OR OTHER DEALINGS
+** IN THE MATERIALS.
+*/
+
+#ifndef GLSLextAMD_H
+#define GLSLextAMD_H
+
+enum BuiltIn;
+enum Decoration;
+enum Op;
+
+static const int GLSLextAMDVersion = 100;
+static const int GLSLextAMDRevision = 1;
+
+// SPV_AMD_shader_ballot
+static const char* const E_SPV_AMD_shader_ballot = "SPV_AMD_shader_ballot";
+
+static const Op OpGroupIAddNonUniformAMD = static_cast<Op>(5000);
+static const Op OpGroupFAddNonUniformAMD = static_cast<Op>(5001);
+static const Op OpGroupFMinNonUniformAMD = static_cast<Op>(5002);
+static const Op OpGroupUMinNonUniformAMD = static_cast<Op>(5003);
+static const Op OpGroupSMinNonUniformAMD = static_cast<Op>(5004);
+static const Op OpGroupFMaxNonUniformAMD = static_cast<Op>(5005);
+static const Op OpGroupUMaxNonUniformAMD = static_cast<Op>(5006);
+static const Op OpGroupSMaxNonUniformAMD = static_cast<Op>(5007);
+
+enum ShaderBallotAMD {
+    ShaderBallotBadAMD = 0, // Don't use
+
+    SwizzleInvocationsAMD = 1,
+    SwizzleInvocationsMaskedAMD = 2,
+    WriteInvocationAMD = 3,
+    MbcntAMD = 4,
+
+    ShaderBallotCountAMD
+};
+
+// SPV_AMD_shader_trinary_minmax
+static const char* const E_SPV_AMD_shader_trinary_minmax = "SPV_AMD_shader_trinary_minmax";
+
+enum ShaderTrinaryMinMaxAMD {
+    ShaderTrinaryMinMaxBadAMD = 0, // Don't use
+
+    FMin3AMD = 1,
+    UMin3AMD = 2,
+    SMin3AMD = 3,
+    FMax3AMD = 4,
+    UMax3AMD = 5,
+    SMax3AMD = 6,
+    FMid3AMD = 7,
+    UMid3AMD = 8,
+    SMid3AMD = 9,
+
+    ShaderTrinaryMinMaxCountAMD
+};
+
+// SPV_AMD_shader_explicit_vertex_parameter
+static const char* const E_SPV_AMD_shader_explicit_vertex_parameter = "SPV_AMD_shader_explicit_vertex_parameter";
+
+static const BuiltIn BuiltInBaryCoordNoPerspAMD           = static_cast<BuiltIn>(4992);
+static const BuiltIn BuiltInBaryCoordNoPerspCentroidAMD   = static_cast<BuiltIn>(4993);
+static const BuiltIn BuiltInBaryCoordNoPerspSampleAMD     = static_cast<BuiltIn>(4994);
+static const BuiltIn BuiltInBaryCoordSmoothAMD            = static_cast<BuiltIn>(4995);
+static const BuiltIn BuiltInBaryCoordSmoothCentroidAMD    = static_cast<BuiltIn>(4996);
+static const BuiltIn BuiltInBaryCoordSmoothSampleAMD      = static_cast<BuiltIn>(4997);
+static const BuiltIn BuiltInBaryCoordPullModelAMD         = static_cast<BuiltIn>(4998);
+
+static const Decoration DecorationExplicitInterpAMD       = static_cast<Decoration>(4999);
+
+enum ShaderExplicitVertexParameterAMD {
+    ShaderExplicitVertexParameterBadAMD = 0, // Don't use
+
+    InterpolateAtVertexAMD = 1,
+
+    ShaderExplicitVertexParameterCountAMD
+};
+
+// SPV_AMD_gcn_shader
+static const char* const E_SPV_AMD_gcn_shader = "SPV_AMD_gcn_shader";
+
+enum GcnShaderAMD {
+    GcnShaderBadAMD = 0, // Don't use
+
+    CubeFaceIndexAMD = 1,
+    CubeFaceCoordAMD = 2,
+    TimeAMD = 3,
+
+    GcnShaderCountAMD
+};
+
+#endif  // #ifndef GLSLextAMD_H
diff --git a/SPIRV/GlslangToSpv.cpp b/SPIRV/GlslangToSpv.cpp
index dd11304..0fc5730 100755
--- a/SPIRV/GlslangToSpv.cpp
+++ b/SPIRV/GlslangToSpv.cpp
@@ -43,6 +43,9 @@
 #include "SpvBuilder.h"
 namespace spv {
    #include "GLSL.std.450.h"
+#ifdef AMD_EXTENSIONS
+   #include "GLSL.ext.AMD.h"
+#endif
 }
 
 // Glslang includes
@@ -147,9 +150,9 @@
     spv::Id createConversion(glslang::TOperator op, spv::Decoration precision, spv::Decoration noContraction, spv::Id destTypeId, spv::Id operand, glslang::TBasicType typeProxy);
     spv::Id makeSmearedConstant(spv::Id constant, int vectorSize);
     spv::Id createAtomicOperation(glslang::TOperator op, spv::Decoration precision, spv::Id typeId, std::vector<spv::Id>& operands, glslang::TBasicType typeProxy);
-    spv::Id createInvocationsOperation(glslang::TOperator, spv::Id typeId, spv::Id operand);
+    spv::Id createInvocationsOperation(glslang::TOperator, spv::Id typeId, spv::Id operand, glslang::TBasicType typeProxy);
     spv::Id createMiscOperation(glslang::TOperator op, spv::Decoration precision, spv::Id typeId, std::vector<spv::Id>& operands, glslang::TBasicType typeProxy);
-    spv::Id createNoArgOperation(glslang::TOperator op);
+    spv::Id createNoArgOperation(glslang::TOperator op, spv::Decoration precision, spv::Id typeId);
     spv::Id getSymbolId(const glslang::TIntermSymbol* node);
     void addDecoration(spv::Id id, spv::Decoration dec);
     void addDecoration(spv::Id id, spv::Decoration dec, unsigned value);
@@ -160,6 +163,7 @@
     bool isTrivialLeaf(const glslang::TIntermTyped* node);
     bool isTrivial(const glslang::TIntermTyped* node);
     spv::Id createShortCircuit(glslang::TOperator, glslang::TIntermTyped& left, glslang::TIntermTyped& right);
+    spv::Id getExtBuiltins(const char* name);
 
     spv::Function* shaderEntry;
     spv::Instruction* entryPoint;
@@ -175,6 +179,7 @@
     std::set<spv::Id> iOSet;           // all input/output variables from either static use or declaration of interface
     const glslang::TIntermediate* glslangIntermediate;
     spv::Id stdBuiltins;
+    std::unordered_map<const char*, spv::Id> extBuiltinMap;
 
     std::unordered_map<int, spv::Id> symbolValues;
     std::unordered_set<int> constReadOnlyParameters;  // set of formal function parameters that have glslang qualifier constReadOnly, so we know they are not local function "const" that are write-once
@@ -371,6 +376,10 @@
         return spv::DecorationNoPerspective;
     else if (qualifier.flat)
         return spv::DecorationFlat;
+#ifdef AMD_EXTENSIONS
+    else if (qualifier.explicitInterp)
+        return spv::DecorationExplicitInterpAMD;
+#endif
     else
         return spv::DecorationMax;
 }
@@ -508,6 +517,15 @@
         // TODO: Add SPIR-V builtin ID.
         logger->missingFunctionality("shader ballot");
         return spv::BuiltInMax;
+#ifdef AMD_EXTENSIONS
+    case glslang::EbvBaryCoordNoPersp:          return spv::BuiltInBaryCoordNoPerspAMD;
+    case glslang::EbvBaryCoordNoPerspCentroid:  return spv::BuiltInBaryCoordNoPerspCentroidAMD;
+    case glslang::EbvBaryCoordNoPerspSample:    return spv::BuiltInBaryCoordNoPerspSampleAMD;
+    case glslang::EbvBaryCoordSmooth:           return spv::BuiltInBaryCoordSmoothAMD;
+    case glslang::EbvBaryCoordSmoothCentroid:   return spv::BuiltInBaryCoordSmoothCentroidAMD;
+    case glslang::EbvBaryCoordSmoothSample:     return spv::BuiltInBaryCoordSmoothSampleAMD;
+    case glslang::EbvBaryCoordPullModel:        return spv::BuiltInBaryCoordPullModelAMD;
+#endif
     default:                               return spv::BuiltInMax;
     }
 }
@@ -628,6 +646,10 @@
         child.invariant = true;
     if (parent.nopersp)
         child.nopersp = true;
+#ifdef AMD_EXTENSIONS
+    if (parent.explicitInterp)
+        child.explicitInterp = true;
+#endif
     if (parent.flat)
         child.flat = true;
     if (parent.centroid)
@@ -1484,6 +1506,9 @@
             break;
         case glslang::EOpInterpolateAtSample:
         case glslang::EOpInterpolateAtOffset:
+#ifdef AMD_EXTENSIONS
+        case glslang::EOpInterpolateAtVertex:
+#endif
             if (arg == 0)
                 lvalue = true;
             break;
@@ -1524,7 +1549,7 @@
         // Pass through to generic operations.
         switch (glslangOperands.size()) {
         case 0:
-            result = createNoArgOperation(node->getOp());
+            result = createNoArgOperation(node->getOp(), precision, convertGlslangToSpvType(node->getType()));
             break;
         case 1:
             result = createUnaryOperation(
@@ -3169,6 +3194,7 @@
 spv::Id TGlslangToSpvTraverser::createUnaryOperation(glslang::TOperator op, spv::Decoration precision, spv::Decoration noContraction, spv::Id typeId, spv::Id operand, glslang::TBasicType typeProxy)
 {
     spv::Op unaryOp = spv::OpNop;
+    int extBuiltins = -1;
     int libCall = -1;
     bool isUnsigned = typeProxy == glslang::EbtUint || typeProxy == glslang::EbtUint64;
     bool isFloat = typeProxy == glslang::EbtFloat || typeProxy == glslang::EbtDouble;
@@ -3448,7 +3474,32 @@
     case glslang::EOpAnyInvocation:
     case glslang::EOpAllInvocations:
     case glslang::EOpAllInvocationsEqual:
-        return createInvocationsOperation(op, typeId, operand);
+#ifdef AMD_EXTENSIONS
+    case glslang::EOpMinInvocations:
+    case glslang::EOpMaxInvocations:
+    case glslang::EOpAddInvocations:
+    case glslang::EOpMinInvocationsNonUniform:
+    case glslang::EOpMaxInvocationsNonUniform:
+    case glslang::EOpAddInvocationsNonUniform:
+#endif
+        return createInvocationsOperation(op, typeId, operand, typeProxy);
+
+#ifdef AMD_EXTENSIONS
+    case glslang::EOpMbcnt:
+        extBuiltins = getExtBuiltins(spv::E_SPV_AMD_shader_ballot);
+        libCall = spv::MbcntAMD;
+        break;
+
+    case glslang::EOpCubeFaceIndex:
+        extBuiltins = getExtBuiltins(spv::E_SPV_AMD_gcn_shader);
+        libCall = spv::CubeFaceIndexAMD;
+        break;
+
+    case glslang::EOpCubeFaceCoord:
+        extBuiltins = getExtBuiltins(spv::E_SPV_AMD_gcn_shader);
+        libCall = spv::CubeFaceCoordAMD;
+        break;
+#endif
 
     default:
         return 0;
@@ -3458,7 +3509,7 @@
     if (libCall >= 0) {
         std::vector<spv::Id> args;
         args.push_back(operand);
-        id = builder.createBuiltinCall(typeId, stdBuiltins, libCall, args);
+        id = builder.createBuiltinCall(typeId, extBuiltins >= 0 ? extBuiltins : stdBuiltins, libCall, args);
     } else {
         id = builder.createUnaryOp(unaryOp, typeId, operand);
     }
@@ -3765,12 +3816,20 @@
 }
 
 // Create group invocation operations.
-spv::Id TGlslangToSpvTraverser::createInvocationsOperation(glslang::TOperator op, spv::Id typeId, spv::Id operand)
+spv::Id TGlslangToSpvTraverser::createInvocationsOperation(glslang::TOperator op, spv::Id typeId, spv::Id operand, glslang::TBasicType typeProxy)
 {
+    bool isUnsigned = typeProxy == glslang::EbtUint || typeProxy == glslang::EbtUint64;
+    bool isFloat = typeProxy == glslang::EbtFloat || typeProxy == glslang::EbtDouble;
+
     builder.addCapability(spv::CapabilityGroups);
 
     std::vector<spv::Id> operands;
     operands.push_back(builder.makeUintConstant(spv::ScopeSubgroup));
+#ifdef AMD_EXTENSIONS
+    if (op == glslang::EOpMinInvocations || op == glslang::EOpMaxInvocations || op == glslang::EOpAddInvocations ||
+        op == glslang::EOpMinInvocationsNonUniform || op == glslang::EOpMaxInvocationsNonUniform || op == glslang::EOpAddInvocationsNonUniform)
+        operands.push_back(spv::GroupOperationReduce);
+#endif
     operands.push_back(operand);
 
     switch (op) {
@@ -3786,6 +3845,74 @@
         return builder.createBinOp(spv::OpLogicalOr, typeId, groupAll,
                                    builder.createUnaryOp(spv::OpLogicalNot, typeId, groupAny));
     }
+#ifdef AMD_EXTENSIONS
+    case glslang::EOpMinInvocations:
+    case glslang::EOpMaxInvocations:
+    case glslang::EOpAddInvocations:
+    {
+        spv::Op spvOp = spv::OpNop;
+        if (op == glslang::EOpMinInvocations) {
+            if (isFloat)
+                spvOp = spv::OpGroupFMin;
+            else {
+                if (isUnsigned)
+                    spvOp = spv::OpGroupUMin;
+                else
+                    spvOp = spv::OpGroupSMin;
+            }
+        } else if (op == glslang::EOpMaxInvocations) {
+            if (isFloat)
+                spvOp = spv::OpGroupFMax;
+            else {
+                if (isUnsigned)
+                    spvOp = spv::OpGroupUMax;
+                else
+                    spvOp = spv::OpGroupSMax;
+            }
+        } else {
+            if (isFloat)
+                spvOp = spv::OpGroupFAdd;
+            else
+                spvOp = spv::OpGroupIAdd;
+        }
+
+        return builder.createOp(spvOp, typeId, operands);
+    }
+    case glslang::EOpMinInvocationsNonUniform:
+    case glslang::EOpMaxInvocationsNonUniform:
+    case glslang::EOpAddInvocationsNonUniform:
+    {
+        spv::Op spvOp = spv::OpNop;
+        if (op == glslang::EOpMinInvocationsNonUniform) {
+            if (isFloat)
+                spvOp = spv::OpGroupFMinNonUniformAMD;
+            else {
+                if (isUnsigned)
+                    spvOp = spv::OpGroupUMinNonUniformAMD;
+                else
+                    spvOp = spv::OpGroupSMinNonUniformAMD;
+            }
+        }
+        else if (op == glslang::EOpMaxInvocationsNonUniform) {
+            if (isFloat)
+                spvOp = spv::OpGroupFMaxNonUniformAMD;
+            else {
+                if (isUnsigned)
+                    spvOp = spv::OpGroupUMaxNonUniformAMD;
+                else
+                    spvOp = spv::OpGroupSMaxNonUniformAMD;
+            }
+        }
+        else {
+            if (isFloat)
+                spvOp = spv::OpGroupFAddNonUniformAMD;
+            else
+                spvOp = spv::OpGroupIAddNonUniformAMD;
+        }
+
+        return builder.createOp(spvOp, typeId, operands);
+    }
+#endif
     default:
         logger->missingFunctionality("invocation operation");
         return spv::NoResult;
@@ -3798,6 +3925,7 @@
     bool isFloat = typeProxy == glslang::EbtFloat || typeProxy == glslang::EbtDouble;
 
     spv::Op opCode = spv::OpNop;
+    int extBuiltins = -1;
     int libCall = -1;
     size_t consumedOperands = operands.size();
     spv::Id typeId0 = 0;
@@ -3941,6 +4069,60 @@
         libCall = spv::GLSLstd450Bad;
         break;
 
+#ifdef AMD_EXTENSIONS
+    case glslang::EOpSwizzleInvocations:
+        extBuiltins = getExtBuiltins(spv::E_SPV_AMD_shader_ballot);
+        libCall = spv::SwizzleInvocationsAMD;
+        break;
+    case glslang::EOpSwizzleInvocationsMasked:
+        extBuiltins = getExtBuiltins(spv::E_SPV_AMD_shader_ballot);
+        libCall = spv::SwizzleInvocationsMaskedAMD;
+        break;
+    case glslang::EOpWriteInvocation:
+        extBuiltins = getExtBuiltins(spv::E_SPV_AMD_shader_ballot);
+        libCall = spv::WriteInvocationAMD;
+        break;
+
+    case glslang::EOpMin3:
+        extBuiltins = getExtBuiltins(spv::E_SPV_AMD_shader_trinary_minmax);
+        if (isFloat)
+            libCall = spv::FMin3AMD;
+        else {
+            if (isUnsigned)
+                libCall = spv::UMin3AMD;
+            else
+                libCall = spv::SMin3AMD;
+        }
+        break;
+    case glslang::EOpMax3:
+        extBuiltins = getExtBuiltins(spv::E_SPV_AMD_shader_trinary_minmax);
+        if (isFloat)
+            libCall = spv::FMax3AMD;
+        else {
+            if (isUnsigned)
+                libCall = spv::UMax3AMD;
+            else
+                libCall = spv::SMax3AMD;
+        }
+        break;
+    case glslang::EOpMid3:
+        extBuiltins = getExtBuiltins(spv::E_SPV_AMD_shader_trinary_minmax);
+        if (isFloat)
+            libCall = spv::FMid3AMD;
+        else {
+            if (isUnsigned)
+                libCall = spv::UMid3AMD;
+            else
+                libCall = spv::SMid3AMD;
+        }
+        break;
+
+    case glslang::EOpInterpolateAtVertex:
+        extBuiltins = getExtBuiltins(spv::E_SPV_AMD_shader_explicit_vertex_parameter);
+        libCall = spv::InterpolateAtVertexAMD;
+        break;
+#endif
+
     default:
         return 0;
     }
@@ -3951,7 +4133,7 @@
         // Construct the call arguments, without modifying the original operands vector.
         // We might need the remaining arguments, e.g. in the EOpFrexp case.
         std::vector<spv::Id> callArguments(operands.begin(), operands.begin() + consumedOperands);
-        id = builder.createBuiltinCall(typeId, stdBuiltins, libCall, callArguments);
+        id = builder.createBuiltinCall(typeId, extBuiltins >= 0 ? extBuiltins : stdBuiltins, libCall, callArguments);
     } else {
         switch (consumedOperands) {
         case 0:
@@ -3997,8 +4179,8 @@
     return builder.setPrecision(id, precision);
 }
 
-// Intrinsics with no arguments, no return value, and no precision.
-spv::Id TGlslangToSpvTraverser::createNoArgOperation(glslang::TOperator op)
+// Intrinsics with no arguments (or no return value, and no precision).
+spv::Id TGlslangToSpvTraverser::createNoArgOperation(glslang::TOperator op, spv::Decoration precision, spv::Id typeId)
 {
     // TODO: get the barrier operands correct
 
@@ -4045,6 +4227,14 @@
         // Control barrier with non-"None" semantic is also a memory barrier.
         builder.createControlBarrier(spv::ScopeWorkgroup, spv::ScopeWorkgroup, spv::MemorySemanticsWorkgroupMemoryMask);
         return 0;
+#ifdef AMD_EXTENSIONS
+    case glslang::EOpTime:
+    {
+        std::vector<spv::Id> args; // Dummy arguments
+        spv::Id id = builder.createBuiltinCall(typeId, getExtBuiltins(spv::E_SPV_AMD_gcn_shader), spv::TimeAMD, args);
+        return builder.setPrecision(id, precision);
+    }
+#endif
     default:
         logger->missingFunctionality("unknown operation with no arguments");
         return 0;
@@ -4437,6 +4627,20 @@
     return builder.createOp(spv::OpPhi, boolTypeId, phiOperands);
 }
 
+// Return type Id of the imported set of extended instructions corresponds to the name.
+// Import this set if it has not been imported yet.
+spv::Id TGlslangToSpvTraverser::getExtBuiltins(const char* name)
+{
+    if (extBuiltinMap.find(name) != extBuiltinMap.end())
+        return extBuiltinMap[name];
+    else {
+        builder.addExtensions(name);
+        spv::Id extBuiltins = builder.import(name);
+        extBuiltinMap[name] = extBuiltins;
+        return extBuiltins;
+    }
+}
+
 };  // end anonymous namespace
 
 namespace glslang {
diff --git a/SPIRV/SpvBuilder.cpp b/SPIRV/SpvBuilder.cpp
index e5317ab..1411d16 100644
--- a/SPIRV/SpvBuilder.cpp
+++ b/SPIRV/SpvBuilder.cpp
@@ -2312,7 +2312,11 @@
         capInst.dump(out);
     }
 
-    // TBD: OpExtension ...
+    for (int e = 0; e < (int)extensions.size(); ++e) {
+        Instruction extInst(0, 0, OpExtension);
+        extInst.addStringOperand(extensions[e]);
+        extInst.dump(out);
+    }
 
     dumpInstructions(out, imports);
     Instruction memInst(0, 0, OpMemoryModel);
@@ -2331,10 +2335,10 @@
         sourceInst.addImmediateOperand(sourceVersion);
         sourceInst.dump(out);
     }
-    for (int e = 0; e < (int)extensions.size(); ++e) {
-        Instruction extInst(0, 0, OpSourceExtension);
-        extInst.addStringOperand(extensions[e]);
-        extInst.dump(out);
+    for (int e = 0; e < (int)sourceExtensions.size(); ++e) {
+        Instruction sourceExtInst(0, 0, OpSourceExtension);
+        sourceExtInst.addStringOperand(sourceExtensions[e]);
+        sourceExtInst.dump(out);
     }
     dumpInstructions(out, names);
     dumpInstructions(out, lines);
diff --git a/SPIRV/SpvBuilder.h b/SPIRV/SpvBuilder.h
index c7e3bcd..00e85b8 100755
--- a/SPIRV/SpvBuilder.h
+++ b/SPIRV/SpvBuilder.h
@@ -70,7 +70,8 @@
         source = lang;
         sourceVersion = version;
     }
-    void addSourceExtension(const char* ext) { extensions.push_back(ext); }
+    void addSourceExtension(const char* ext) { sourceExtensions.push_back(ext); }
+    void addExtensions(const char* ext) { extensions.push_back(ext); }
     Id import(const char*);
     void setMemoryModel(spv::AddressingModel addr, spv::MemoryModel mem)
     {
@@ -552,6 +553,7 @@
     SourceLanguage source;
     int sourceVersion;
     std::vector<const char*> extensions;
+    std::vector<const char*> sourceExtensions;
     AddressingModel addressModel;
     MemoryModel memoryModel;
     std::set<spv::Capability> capabilities;
diff --git a/SPIRV/disassemble.cpp b/SPIRV/disassemble.cpp
index 75688cb..b60b3ba 100644
--- a/SPIRV/disassemble.cpp
+++ b/SPIRV/disassemble.cpp
@@ -44,16 +44,25 @@
 #include <sstream>
 #include <cstring>
 
-namespace spv {
-    // Include C-based headers that don't have a namespace
-    #include "GLSL.std.450.h"
-}
-const char* GlslStd450DebugNames[spv::GLSLstd450Count];
-
 #include "disassemble.h"
 #include "doc.h"
 
 namespace spv {
+    extern "C" {
+        // Include C-based headers that don't have a namespace
+        #include "GLSL.std.450.h"
+#ifdef AMD_EXTENSIONS
+        #include "GLSL.ext.AMD.h"
+#endif
+    }
+}
+const char* GlslStd450DebugNames[spv::GLSLstd450Count];
+
+namespace spv {
+
+#ifdef AMD_EXTENSIONS
+static const char* GLSLextAMDGetDebugNames(const char*, unsigned);
+#endif
 
 static void Kill(std::ostream& out, const char* message)
 {
@@ -64,6 +73,9 @@
 // used to identify the extended instruction library imported when printing
 enum ExtInstSet {
     GLSL450Inst,
+#ifdef AMD_EXTENSIONS
+    GLSLextAMDInst,
+#endif
     OpenCLExtInst,
 };
 
@@ -446,14 +458,26 @@
             --numOperands;
             if (opCode == OpExtInst) {
                 ExtInstSet extInstSet = GLSL450Inst;
-                if (0 == memcmp("OpenCL", (const char*)(idDescriptor[stream[word-2]].c_str()), 6)) {
+                const char* name = idDescriptor[stream[word - 2]].c_str();
+                if (0 == memcmp("OpenCL", name, 6)) {
                     extInstSet = OpenCLExtInst;
+#ifdef AMD_EXTENSIONS
+                } else if (strcmp(spv::E_SPV_AMD_shader_ballot, name) == 0 ||
+                           strcmp(spv::E_SPV_AMD_shader_trinary_minmax, name) == 0 ||
+                           strcmp(spv::E_SPV_AMD_shader_explicit_vertex_parameter, name) == 0 ||
+                           strcmp(spv::E_SPV_AMD_gcn_shader, name) == 0) {
+                    extInstSet = GLSLextAMDInst;
+#endif
                 }
                 unsigned entrypoint = stream[word - 1];
                 if (extInstSet == GLSL450Inst) {
                     if (entrypoint < GLSLstd450Count) {
                         out << "(" << GlslStd450DebugNames[entrypoint] << ")";
                     }
+#ifdef AMD_EXTENSIONS
+                } else if (extInstSet == GLSLextAMDInst) {
+                    out << "(" << GLSLextAMDGetDebugNames(name, entrypoint) << ")";
+#endif
                 }
             }
             break;
@@ -561,6 +585,50 @@
     names[GLSLstd450InterpolateAtOffset]     = "InterpolateAtOffset";
 }
 
+#ifdef AMD_EXTENSIONS
+static const char* GLSLextAMDGetDebugNames(const char* name, unsigned entrypoint)
+{
+    if (strcmp(name, spv::E_SPV_AMD_shader_ballot) == 0) {
+        switch (entrypoint) {
+        case SwizzleInvocationsAMD:         return "SwizzleInvocationsAMD";
+        case SwizzleInvocationsMaskedAMD:   return "SwizzleInvocationsMaskedAMD";
+        case WriteInvocationAMD:            return "WriteInvocationAMD";
+        case MbcntAMD:                      return "MbcntAMD";
+        default:                            return "Bad";
+        }
+    } else if (strcmp(name, spv::E_SPV_AMD_shader_trinary_minmax) == 0) {
+        switch (entrypoint) {
+        case FMin3AMD:      return "FMin3AMD";
+        case UMin3AMD:      return "UMin3AMD";
+        case SMin3AMD:      return "SMin3AMD";
+        case FMax3AMD:      return "FMax3AMD";
+        case UMax3AMD:      return "UMax3AMD";
+        case SMax3AMD:      return "SMax3AMD";
+        case FMid3AMD:      return "FMid3AMD";
+        case UMid3AMD:      return "UMid3AMD";
+        case SMid3AMD:      return "SMid3AMD";
+        default:            return "Bad";
+        }
+    } else if (strcmp(name, spv::E_SPV_AMD_shader_explicit_vertex_parameter) == 0) {
+        switch (entrypoint) {
+        case InterpolateAtVertexAMD:    return "InterpolateAtVertexAMD";
+        default:                        return "Bad";
+        }
+    }
+    else if (strcmp(name, spv::E_SPV_AMD_gcn_shader) == 0) {
+        switch (entrypoint) {
+        case CubeFaceIndexAMD:      return "CubeFaceIndexAMD";
+        case CubeFaceCoordAMD:      return "CubeFaceCoordAMD";
+        case TimeAMD:               return "TimeAMD";
+        default:
+            break;
+        }
+    }
+
+    return "Bad";
+}
+#endif
+
 void Disassemble(std::ostream& out, const std::vector<unsigned int>& stream)
 {
     SpirvStream SpirvStream(out, stream);
diff --git a/SPIRV/doc.cpp b/SPIRV/doc.cpp
index fed3ec4..9b58b59 100755
--- a/SPIRV/doc.cpp
+++ b/SPIRV/doc.cpp
@@ -45,6 +45,15 @@
 #include <string.h>
 #include <algorithm>
 
+#ifdef AMD_EXTENSIONS
+namespace spv {
+    extern "C" {
+        // Include C-based headers that don't have a namespace
+        #include "GLSL.ext.AMD.h"
+    }
+}
+#endif
+
 namespace spv {
 
 //
@@ -243,6 +252,10 @@
 
     case DecorationCeiling:
     default:  return "Bad";
+
+#ifdef AMD_EXTENSIONS
+    case 4999: return "ExplicitInterpAMD";
+#endif
     }
 }
 
@@ -298,6 +311,16 @@
 
     case BuiltInCeiling:
     default: return "Bad";
+
+#ifdef AMD_EXTENSIONS
+    case 4992: return "BaryCoordNoPerspAMD";
+    case 4993: return "BaryCoordNoPerspCentroidAMD";
+    case 4994: return "BaryCoordNoPerspSampleAMD";
+    case 4995: return "BaryCoordSmoothAMD";
+    case 4996: return "BaryCoordSmoothCentroidAMD";
+    case 4997: return "BaryCoordSmoothSampleAMD";
+    case 4998: return "BaryCoordPullModelAMD";
+#endif
     }
 }
 
@@ -1107,12 +1130,27 @@
     case OpcodeCeiling:
     default:
         return "Bad";
+
+#ifdef AMD_EXTENSIONS
+    case 5000: return "OpGroupIAddNonUniformAMD";
+    case 5001: return "OpGroupFAddNonUniformAMD";
+    case 5002: return "OpGroupFMinNonUniformAMD";
+    case 5003: return "OpGroupUMinNonUniformAMD";
+    case 5004: return "OpGroupSMinNonUniformAMD";
+    case 5005: return "OpGroupFMaxNonUniformAMD";
+    case 5006: return "OpGroupUMaxNonUniformAMD";
+    case 5007: return "OpGroupSMaxNonUniformAMD";
+#endif
     }
 }
 
 // The set of objects that hold all the instruction/operand
 // parameterization information.
+#ifdef AMD_EXTENSIONS
+InstructionParameters InstructionDesc[OpCodeMask + 1];
+#else
 InstructionParameters InstructionDesc[OpcodeCeiling];
+#endif
 OperandParameters ExecutionModeOperands[ExecutionModeCeiling];
 OperandParameters DecorationOperands[DecorationCeiling];
 
@@ -2703,6 +2741,48 @@
     InstructionDesc[OpEnqueueMarker].operands.push(OperandId, "'Num Events'");
     InstructionDesc[OpEnqueueMarker].operands.push(OperandId, "'Wait Events'");
     InstructionDesc[OpEnqueueMarker].operands.push(OperandId, "'Ret Event'");
+
+#ifdef AMD_EXTENSIONS
+    InstructionDesc[OpGroupIAddNonUniformAMD].capabilities.push_back(CapabilityGroups);
+    InstructionDesc[OpGroupIAddNonUniformAMD].operands.push(OperandScope, "'Execution'");
+    InstructionDesc[OpGroupIAddNonUniformAMD].operands.push(OperandGroupOperation, "'Operation'");
+    InstructionDesc[OpGroupIAddNonUniformAMD].operands.push(OperandId, "'X'");
+
+    InstructionDesc[OpGroupFAddNonUniformAMD].capabilities.push_back(CapabilityGroups);
+    InstructionDesc[OpGroupFAddNonUniformAMD].operands.push(OperandScope, "'Execution'");
+    InstructionDesc[OpGroupFAddNonUniformAMD].operands.push(OperandGroupOperation, "'Operation'");
+    InstructionDesc[OpGroupFAddNonUniformAMD].operands.push(OperandId, "'X'");
+
+    InstructionDesc[OpGroupUMinNonUniformAMD].capabilities.push_back(CapabilityGroups);
+    InstructionDesc[OpGroupUMinNonUniformAMD].operands.push(OperandScope, "'Execution'");
+    InstructionDesc[OpGroupUMinNonUniformAMD].operands.push(OperandGroupOperation, "'Operation'");
+    InstructionDesc[OpGroupUMinNonUniformAMD].operands.push(OperandId, "'X'");
+
+    InstructionDesc[OpGroupSMinNonUniformAMD].capabilities.push_back(CapabilityGroups);
+    InstructionDesc[OpGroupSMinNonUniformAMD].operands.push(OperandScope, "'Execution'");
+    InstructionDesc[OpGroupSMinNonUniformAMD].operands.push(OperandGroupOperation, "'Operation'");
+    InstructionDesc[OpGroupSMinNonUniformAMD].operands.push(OperandId, "X");
+
+    InstructionDesc[OpGroupFMinNonUniformAMD].capabilities.push_back(CapabilityGroups);
+    InstructionDesc[OpGroupFMinNonUniformAMD].operands.push(OperandScope, "'Execution'");
+    InstructionDesc[OpGroupFMinNonUniformAMD].operands.push(OperandGroupOperation, "'Operation'");
+    InstructionDesc[OpGroupFMinNonUniformAMD].operands.push(OperandId, "X");
+
+    InstructionDesc[OpGroupUMaxNonUniformAMD].capabilities.push_back(CapabilityGroups);
+    InstructionDesc[OpGroupUMaxNonUniformAMD].operands.push(OperandScope, "'Execution'");
+    InstructionDesc[OpGroupUMaxNonUniformAMD].operands.push(OperandGroupOperation, "'Operation'");
+    InstructionDesc[OpGroupUMaxNonUniformAMD].operands.push(OperandId, "X");
+
+    InstructionDesc[OpGroupSMaxNonUniformAMD].capabilities.push_back(CapabilityGroups);
+    InstructionDesc[OpGroupSMaxNonUniformAMD].operands.push(OperandScope, "'Execution'");
+    InstructionDesc[OpGroupSMaxNonUniformAMD].operands.push(OperandGroupOperation, "'Operation'");
+    InstructionDesc[OpGroupSMaxNonUniformAMD].operands.push(OperandId, "X");
+
+    InstructionDesc[OpGroupFMaxNonUniformAMD].capabilities.push_back(CapabilityGroups);
+    InstructionDesc[OpGroupFMaxNonUniformAMD].operands.push(OperandScope, "'Execution'");
+    InstructionDesc[OpGroupFMaxNonUniformAMD].operands.push(OperandGroupOperation, "'Operation'");
+    InstructionDesc[OpGroupFMaxNonUniformAMD].operands.push(OperandId, "X");
+#endif
 }
 
 }; // end spv namespace