Support GL_ARB_fragment_shader_interlock
diff --git a/SPIRV/GLSL.ext.KHR.h b/SPIRV/GLSL.ext.KHR.h
index 333442b..bcc6087 100644
--- a/SPIRV/GLSL.ext.KHR.h
+++ b/SPIRV/GLSL.ext.KHR.h
@@ -41,5 +41,6 @@
 static const char* const E_SPV_KHR_post_depth_coverage          = "SPV_KHR_post_depth_coverage";
 static const char* const E_SPV_KHR_vulkan_memory_model          = "SPV_KHR_vulkan_memory_model";
 static const char* const E_SPV_EXT_physical_storage_buffer      = "SPV_EXT_physical_storage_buffer";
+static const char* const E_SPV_EXT_fragment_shader_interlock    = "SPV_EXT_fragment_shader_interlock";
 
 #endif  // #ifndef GLSLextKHR_H
diff --git a/SPIRV/GlslangToSpv.cpp b/SPIRV/GlslangToSpv.cpp
index c513cd1..7eba1b6 100644
--- a/SPIRV/GlslangToSpv.cpp
+++ b/SPIRV/GlslangToSpv.cpp
@@ -1469,6 +1469,30 @@
 
         if (glslangIntermediate->getDepth() != glslang::EldUnchanged && glslangIntermediate->isDepthReplacing())
             builder.addExecutionMode(shaderEntry, spv::ExecutionModeDepthReplacing);
+
+        switch (glslangIntermediate->getInterlockOrdering()) {
+        case glslang::EioPixelInterlockOrdered:         mode = spv::ExecutionModePixelInterlockOrderedEXT;          break;
+        case glslang::EioPixelInterlockUnordered:       mode = spv::ExecutionModePixelInterlockUnorderedEXT;          break;
+        case glslang::EioSampleInterlockOrdered:        mode = spv::ExecutionModeSampleInterlockOrderedEXT;          break;
+        case glslang::EioSampleInterlockUnordered:      mode = spv::ExecutionModeSampleInterlockUnorderedEXT;          break;
+        case glslang::EioShadingRateInterlockOrdered:   mode = spv::ExecutionModeShadingRateInterlockOrderedEXT;          break;
+        case glslang::EioShadingRateInterlockUnordered: mode = spv::ExecutionModeShadingRateInterlockUnorderedEXT;          break;
+        default:                                        mode = spv::ExecutionModeMax;                   break;
+        }
+        if (mode != spv::ExecutionModeMax) {
+            builder.addExecutionMode(shaderEntry, (spv::ExecutionMode)mode);
+            if (mode == spv::ExecutionModeShadingRateInterlockOrderedEXT ||
+                mode == spv::ExecutionModeShadingRateInterlockUnorderedEXT) {
+                builder.addCapability(spv::CapabilityFragmentShaderShadingRateInterlockEXT);
+            } else if (mode == spv::ExecutionModePixelInterlockOrderedEXT ||
+                       mode == spv::ExecutionModePixelInterlockUnorderedEXT) {
+                builder.addCapability(spv::CapabilityFragmentShaderPixelInterlockEXT);
+            } else {
+                builder.addCapability(spv::CapabilityFragmentShaderSampleInterlockEXT);
+            }
+            builder.addExtension(spv::E_SPV_EXT_fragment_shader_interlock);
+        }
+
         break;
 
     case EShLangCompute:
@@ -2396,6 +2420,11 @@
     case glslang::EOpCooperativeMatrixStore:
         noReturnValue = true;
         break;
+    case glslang::EOpBeginInvocationInterlock:
+    case glslang::EOpEndInvocationInterlock:
+        builder.addExtension(spv::E_SPV_EXT_fragment_shader_interlock);
+        noReturnValue = true;
+        break;
 
     default:
         break;
@@ -7429,6 +7458,14 @@
         builder.createNoResultOp(spv::OpTerminateRayNV);
         return 0;
 #endif
+
+    case glslang::EOpBeginInvocationInterlock:
+        builder.createNoResultOp(spv::OpBeginInvocationInterlockEXT);
+        return 0;
+    case glslang::EOpEndInvocationInterlock:
+        builder.createNoResultOp(spv::OpEndInvocationInterlockEXT);
+        return 0;
+
     default:
         logger->missingFunctionality("unknown operation with no arguments");
         return 0;
diff --git a/SPIRV/doc.cpp b/SPIRV/doc.cpp
index a0c384a..da9494d 100644
--- a/SPIRV/doc.cpp
+++ b/SPIRV/doc.cpp
@@ -191,6 +191,13 @@
     case ExecutionModeDerivativeGroupLinearNV:  return "DerivativeGroupLinearNV";
 #endif
 
+    case ExecutionModePixelInterlockOrderedEXT:         return "PixelInterlockOrderedEXT";
+    case ExecutionModePixelInterlockUnorderedEXT:       return "PixelInterlockUnorderedEXT";
+    case ExecutionModeSampleInterlockOrderedEXT:        return "SampleInterlockOrderedEXT";
+    case ExecutionModeSampleInterlockUnorderedEXT:      return "SampleInterlockUnorderedEXT";
+    case ExecutionModeShadingRateInterlockOrderedEXT:   return "ShadingRateInterlockOrderedEXT";
+    case ExecutionModeShadingRateInterlockUnorderedEXT: return "ShadingRateInterlockUnorderedEXT";
+
     case ExecutionModeCeiling:
     default: return "Bad";
     }
@@ -942,6 +949,10 @@
 
     case CapabilityCooperativeMatrixNV:     return "CooperativeMatrixNV";
 
+    case CapabilityFragmentShaderSampleInterlockEXT:        return "CapabilityFragmentShaderSampleInterlockEXT";
+    case CapabilityFragmentShaderPixelInterlockEXT:         return "CapabilityFragmentShaderPixelInterlockEXT";
+    case CapabilityFragmentShaderShadingRateInterlockEXT:   return "CapabilityFragmentShaderShadingRateInterlockEXT";
+
     default: return "Bad";
     }
 }
@@ -1352,6 +1363,9 @@
     case OpCooperativeMatrixMulAddNV:       return "OpCooperativeMatrixMulAddNV";
     case OpCooperativeMatrixLengthNV:       return "OpCooperativeMatrixLengthNV";
 
+    case OpBeginInvocationInterlockEXT:     return "OpBeginInvocationInterlockEXT";
+    case OpEndInvocationInterlockEXT:       return "OpEndInvocationInterlockEXT";
+
     default:
         return "Bad";
     }
@@ -1465,6 +1479,8 @@
     InstructionDesc[OpModuleProcessed].setResultAndType(false, false);
     InstructionDesc[OpTypeCooperativeMatrixNV].setResultAndType(true, false);
     InstructionDesc[OpCooperativeMatrixStoreNV].setResultAndType(false, false);
+    InstructionDesc[OpBeginInvocationInterlockEXT].setResultAndType(false, false);
+    InstructionDesc[OpEndInvocationInterlockEXT].setResultAndType(false, false);
 
     // Specific additional context-dependent operands
 
diff --git a/SPIRV/spirv.hpp b/SPIRV/spirv.hpp
index 5297fd3..03ba767 100644
--- a/SPIRV/spirv.hpp
+++ b/SPIRV/spirv.hpp
@@ -154,6 +154,12 @@
     ExecutionModeDerivativeGroupQuadsNV = 5289,
     ExecutionModeDerivativeGroupLinearNV = 5290,
     ExecutionModeOutputTrianglesNV = 5298,
+    ExecutionModePixelInterlockOrderedEXT = 5366,
+    ExecutionModePixelInterlockUnorderedEXT = 5367,
+    ExecutionModeSampleInterlockOrderedEXT = 5368,
+    ExecutionModeSampleInterlockUnorderedEXT = 5369,
+    ExecutionModeShadingRateInterlockOrderedEXT = 5370,
+    ExecutionModeShadingRateInterlockUnorderedEXT = 5371,
     ExecutionModeMax = 0x7fffffff,
 };
 
@@ -551,6 +557,10 @@
     BuiltInHitTNV = 5332,
     BuiltInHitKindNV = 5333,
     BuiltInIncomingRayFlagsNV = 5351,
+    BuiltInWarpsPerSMNV = 5374,
+    BuiltInSMCountNV = 5375,
+    BuiltInWarpIDNV = 5376,
+    BuiltInSMIDNV = 5377,
     BuiltInMax = 0x7fffffff,
 };
 
@@ -829,10 +839,15 @@
     CapabilityPhysicalStorageBufferAddressesEXT = 5347,
     CapabilityComputeDerivativeGroupLinearNV = 5350,
     CapabilityCooperativeMatrixNV = 5357,
+    CapabilityFragmentShaderSampleInterlockEXT = 5363,
+    CapabilityFragmentShaderShadingRateInterlockEXT = 5372,
+    CapabilityShaderSMBuiltinsNV = 5373,
+    CapabilityFragmentShaderPixelInterlockEXT = 5378,
     CapabilitySubgroupShuffleINTEL = 5568,
     CapabilitySubgroupBufferBlockIOINTEL = 5569,
     CapabilitySubgroupImageBlockIOINTEL = 5570,
     CapabilitySubgroupImageMediaBlockIOINTEL = 5579,
+    CapabilityIntegerFunctions2INTEL = 5584,
     CapabilitySubgroupAvcMotionEstimationINTEL = 5696,
     CapabilitySubgroupAvcMotionEstimationIntraINTEL = 5697,
     CapabilitySubgroupAvcMotionEstimationChromaINTEL = 5698,
@@ -1214,6 +1229,8 @@
     OpCooperativeMatrixStoreNV = 5360,
     OpCooperativeMatrixMulAddNV = 5361,
     OpCooperativeMatrixLengthNV = 5362,
+    OpBeginInvocationInterlockEXT = 5364,
+    OpEndInvocationInterlockEXT = 5365,
     OpSubgroupShuffleINTEL = 5571,
     OpSubgroupShuffleDownINTEL = 5572,
     OpSubgroupShuffleUpINTEL = 5573,
@@ -1224,6 +1241,20 @@
     OpSubgroupImageBlockWriteINTEL = 5578,
     OpSubgroupImageMediaBlockReadINTEL = 5580,
     OpSubgroupImageMediaBlockWriteINTEL = 5581,
+    OpUCountLeadingZerosINTEL = 5585,
+    OpUCountTrailingZerosINTEL = 5586,
+    OpAbsISubINTEL = 5587,
+    OpAbsUSubINTEL = 5588,
+    OpIAddSatINTEL = 5589,
+    OpUAddSatINTEL = 5590,
+    OpIAverageINTEL = 5591,
+    OpUAverageINTEL = 5592,
+    OpIAverageRoundedINTEL = 5593,
+    OpUAverageRoundedINTEL = 5594,
+    OpISubSatINTEL = 5595,
+    OpUSubSatINTEL = 5596,
+    OpIMul32x16INTEL = 5597,
+    OpUMul32x16INTEL = 5598,
     OpDecorateString = 5632,
     OpDecorateStringGOOGLE = 5632,
     OpMemberDecorateString = 5633,
@@ -1738,10 +1769,22 @@
     case OpSubgroupImageBlockWriteINTEL: *hasResult = false; *hasResultType = false; break;
     case OpSubgroupImageMediaBlockReadINTEL: *hasResult = true; *hasResultType = true; break;
     case OpSubgroupImageMediaBlockWriteINTEL: *hasResult = false; *hasResultType = false; break;
+    case OpUCountLeadingZerosINTEL: *hasResult = true; *hasResultType = true; break;
+    case OpUCountTrailingZerosINTEL: *hasResult = true; *hasResultType = true; break;
+    case OpAbsISubINTEL: *hasResult = true; *hasResultType = true; break;
+    case OpAbsUSubINTEL: *hasResult = true; *hasResultType = true; break;
+    case OpIAddSatINTEL: *hasResult = true; *hasResultType = true; break;
+    case OpUAddSatINTEL: *hasResult = true; *hasResultType = true; break;
+    case OpIAverageINTEL: *hasResult = true; *hasResultType = true; break;
+    case OpUAverageINTEL: *hasResult = true; *hasResultType = true; break;
+    case OpIAverageRoundedINTEL: *hasResult = true; *hasResultType = true; break;
+    case OpUAverageRoundedINTEL: *hasResult = true; *hasResultType = true; break;
+    case OpISubSatINTEL: *hasResult = true; *hasResultType = true; break;
+    case OpUSubSatINTEL: *hasResult = true; *hasResultType = true; break;
+    case OpIMul32x16INTEL: *hasResult = true; *hasResultType = true; break;
+    case OpUMul32x16INTEL: *hasResult = true; *hasResultType = true; break;
     case OpDecorateString: *hasResult = false; *hasResultType = false; break;
-    case OpDecorateStringGOOGLE: *hasResult = false; *hasResultType = false; break;
     case OpMemberDecorateString: *hasResult = false; *hasResultType = false; break;
-    case OpMemberDecorateStringGOOGLE: *hasResult = false; *hasResultType = false; break;
     case OpVmeImageINTEL: *hasResult = true; *hasResultType = true; break;
     case OpTypeVmeImageINTEL: *hasResult = true; *hasResultType = false; break;
     case OpTypeAvcImePayloadINTEL: *hasResult = true; *hasResultType = false; break;
@@ -1860,6 +1903,8 @@
     case OpSubgroupAvcSicGetPackedSkcLumaCountThresholdINTEL: *hasResult = true; *hasResultType = true; break;
     case OpSubgroupAvcSicGetPackedSkcLumaSumThresholdINTEL: *hasResult = true; *hasResultType = true; break;
     case OpSubgroupAvcSicGetInterRawSadsINTEL: *hasResult = true; *hasResultType = true; break;
+    case OpBeginInvocationInterlockEXT: *hasResult = false; *hasResultType = false; break;
+    case OpEndInvocationInterlockEXT: *hasResult = false; *hasResultType = false; break;
     }
 }
 #endif /* SPV_ENABLE_UTILITY_CODE */
diff --git a/Test/baseResults/spv.fsi.frag.out b/Test/baseResults/spv.fsi.frag.out
new file mode 100644
index 0000000..51fb068
--- /dev/null
+++ b/Test/baseResults/spv.fsi.frag.out
@@ -0,0 +1,56 @@
+spv.fsi.frag
+// Module Version 10000
+// Generated by (magic number): 80007
+// Id's are bound by 24
+
+                              Capability Shader
+                              Capability CapabilityFragmentShaderSampleInterlockEXT
+                              Extension  "SPV_EXT_fragment_shader_interlock"
+               1:             ExtInstImport  "GLSL.std.450"
+                              MemoryModel Logical GLSL450
+                              EntryPoint Fragment 4  "main"
+                              ExecutionMode 4 OriginUpperLeft
+                              ExecutionMode 4 SampleInterlockOrderedEXT
+                              Source GLSL 450
+                              SourceExtension  "GL_ARB_fragment_shader_interlock"
+                              Name 4  "main"
+                              Name 7  "B1"
+                              MemberName 7(B1) 0  "x"
+                              Name 9  "b1"
+                              Name 17  "im"
+                              MemberDecorate 7(B1) 0 Coherent
+                              MemberDecorate 7(B1) 0 Offset 0
+                              Decorate 7(B1) BufferBlock
+                              Decorate 9(b1) DescriptorSet 0
+                              Decorate 9(b1) Binding 0
+                              Decorate 17(im) DescriptorSet 0
+                              Decorate 17(im) Binding 1
+                              Decorate 17(im) Coherent
+               2:             TypeVoid
+               3:             TypeFunction 2
+               6:             TypeInt 32 1
+           7(B1):             TypeStruct 6(int)
+               8:             TypePointer Uniform 7(B1)
+           9(b1):      8(ptr) Variable Uniform
+              10:      6(int) Constant 0
+              11:      6(int) Constant 1
+              12:             TypePointer Uniform 6(int)
+              14:             TypeFloat 32
+              15:             TypeImage 14(float) 2D nonsampled format:Rgba32f
+              16:             TypePointer UniformConstant 15
+          17(im):     16(ptr) Variable UniformConstant
+              19:             TypeVector 6(int) 2
+              20:   19(ivec2) ConstantComposite 10 10
+              21:             TypeVector 14(float) 4
+              22:   14(float) Constant 0
+              23:   21(fvec4) ConstantComposite 22 22 22 22
+         4(main):           2 Function None 3
+               5:             Label
+                              BeginInvocationInterlockEXT
+              13:     12(ptr) AccessChain 9(b1) 10
+                              Store 13 11
+              18:          15 Load 17(im)
+                              ImageWrite 18 20 23
+                              EndInvocationInterlockEXT
+                              Return
+                              FunctionEnd
diff --git a/Test/baseResults/spv.fsi_Error.frag.out b/Test/baseResults/spv.fsi_Error.frag.out
new file mode 100644
index 0000000..4a03436
--- /dev/null
+++ b/Test/baseResults/spv.fsi_Error.frag.out
@@ -0,0 +1,14 @@
+spv.fsi_Error.frag
+ERROR: 0:4: 'pixel_interlock_ordered' : can only apply to 'in' 
+ERROR: 0:7: 'pixel_interlock_unordered' : cannot change previously set fragment shader interlock ordering 
+ERROR: 0:11: '' : beginInvocationInterlockARB() must be in main() 
+ERROR: 0:12: '' : endInvocationInterlockARB() must be in main() 
+ERROR: 0:17: '' : endInvocationInterlockARB() must only be called once 
+ERROR: 0:18: '' : beginInvocationInterlockARB() must only be called once 
+ERROR: 0:18: '' : beginInvocationInterlockARB() must be called before endInvocationInterlockARB() 
+ERROR: 0:22: '' : endInvocationInterlockARB() cannot be placed after a return from main() 
+ERROR: 0:22: '' : endInvocationInterlockARB() must only be called once 
+ERROR: 9 compilation errors.  No code generated.
+
+
+SPIR-V is not generated for failed compile or link
diff --git a/Test/spv.fsi.frag b/Test/spv.fsi.frag
new file mode 100644
index 0000000..5e6fb5f
--- /dev/null
+++ b/Test/spv.fsi.frag
@@ -0,0 +1,20 @@
+#version 450

+#extension GL_ARB_fragment_shader_interlock : enable

+

+layout(set = 0, binding = 0) coherent buffer B1 {

+    layout(offset = 0)  int x;

+} b1;

+

+layout(set = 0, binding = 1, rgba32f) coherent uniform image2D im;

+

+layout(sample_interlock_ordered) in;

+

+void main() {

+

+    beginInvocationInterlockARB();

+

+    b1.x = 1;

+    imageStore(im, ivec2(0,0), vec4(0));

+

+    endInvocationInterlockARB();

+}

diff --git a/Test/spv.fsi_Error.frag b/Test/spv.fsi_Error.frag
new file mode 100644
index 0000000..adddf3e
--- /dev/null
+++ b/Test/spv.fsi_Error.frag
@@ -0,0 +1,23 @@
+#version 450

+#extension GL_ARB_fragment_shader_interlock : enable

+

+layout(pixel_interlock_ordered) out;

+

+layout(pixel_interlock_ordered) in;

+layout(pixel_interlock_unordered) in;

+

+void foo()

+{

+    beginInvocationInterlockARB();

+    endInvocationInterlockARB();

+}

+

+void main() {

+

+    endInvocationInterlockARB();

+    beginInvocationInterlockARB();

+

+    return;

+

+    endInvocationInterlockARB();

+}

diff --git a/glslang/Include/Types.h b/glslang/Include/Types.h
index 90341dc..1cbf4db 100644
--- a/glslang/Include/Types.h
+++ b/glslang/Include/Types.h
@@ -422,6 +422,18 @@
     EBlendCount
 };
 
+enum TInterlockOrdering {
+    EioNone,
+    EioPixelInterlockOrdered,
+    EioPixelInterlockUnordered,
+    EioSampleInterlockOrdered,
+    EioSampleInterlockUnordered,
+    EioShadingRateInterlockOrdered,
+    EioShadingRateInterlockUnordered,
+
+    EioCount,
+};
+
 class TQualifier {
 public:
     static const int layoutNotSet = -1;
@@ -1110,6 +1122,18 @@
         default:                    return 0;
         }
     }
+    static const char* getInterlockOrderingString(TInterlockOrdering order)
+    {
+        switch (order) {
+        case EioPixelInterlockOrdered:          return "pixel_interlock_ordered";
+        case EioPixelInterlockUnordered:        return "pixel_interlock_unordered";
+        case EioSampleInterlockOrdered:         return "sample_interlock_ordered";
+        case EioSampleInterlockUnordered:       return "sample_interlock_unordered";
+        case EioShadingRateInterlockOrdered:    return "shading_rate_interlock_ordered";
+        case EioShadingRateInterlockUnordered:  return "shading_rate_interlock_unordered";
+        default:                                return "none";
+        }
+    }
 };
 
 // Qualifiers that don't need to be keep per object.  They have shader scope, not object scope.
@@ -1130,6 +1154,7 @@
     TLayoutDepth layoutDepth;
     bool blendEquation;       // true if any blend equation was specified
     int numViews;             // multiview extenstions
+    TInterlockOrdering interlockOrdering;
 
 #ifdef NV_EXTENSIONS
     bool layoutOverrideCoverage;        // true if layout override_coverage set
@@ -1165,6 +1190,7 @@
         layoutDerivativeGroupLinear = false;
         primitives                  = TQualifier::layoutNotSet;
 #endif
+        interlockOrdering = EioNone;
     }
 
     // Merge in characteristics from the 'src' qualifier.  They can override when
@@ -1215,6 +1241,9 @@
         if (src.primitives != TQualifier::layoutNotSet)
             primitives = src.primitives;
 #endif
+
+        if (src.interlockOrdering != EioNone)
+            interlockOrdering = src.interlockOrdering;
     }
 };
 
diff --git a/glslang/Include/intermediate.h b/glslang/Include/intermediate.h
index 89d1954..f7424e8 100644
--- a/glslang/Include/intermediate.h
+++ b/glslang/Include/intermediate.h
@@ -621,6 +621,9 @@
     EOpCooperativeMatrixStore,
     EOpCooperativeMatrixMulAdd,
 
+    EOpBeginInvocationInterlock, // Fragment only
+    EOpEndInvocationInterlock, // Fragment only
+
     //
     // Branch
     //
diff --git a/glslang/MachineIndependent/Initialize.cpp b/glslang/MachineIndependent/Initialize.cpp
index 16a2e1a..ecaf755 100644
--- a/glslang/MachineIndependent/Initialize.cpp
+++ b/glslang/MachineIndependent/Initialize.cpp
@@ -5030,6 +5030,10 @@
             "\n");
     }
 
+    stageBuiltins[EShLangFragment].append(
+        "void beginInvocationInterlockARB(void);"
+        "void endInvocationInterlockARB(void);");
+
 #ifdef AMD_EXTENSIONS
     // GL_AMD_shader_explicit_vertex_parameter
     if (profile != EEsProfile && version >= 450) {
@@ -9539,6 +9543,10 @@
         if (profile != EEsProfile)
             symbolTable.relateToOperator("interpolateAtVertexAMD", EOpInterpolateAtVertex);
 #endif
+
+        symbolTable.relateToOperator("beginInvocationInterlockARB", EOpBeginInvocationInterlock);
+        symbolTable.relateToOperator("endInvocationInterlockARB",   EOpEndInvocationInterlock);
+
         break;
 
     case EShLangCompute:
diff --git a/glslang/MachineIndependent/ParseHelper.cpp b/glslang/MachineIndependent/ParseHelper.cpp
index 8384eb9..569c928 100644
--- a/glslang/MachineIndependent/ParseHelper.cpp
+++ b/glslang/MachineIndependent/ParseHelper.cpp
@@ -1410,6 +1410,44 @@
                 error(loc, "tessellation control barrier() cannot be placed after a return from main()", "", "");
         }
         break;
+    case EOpBeginInvocationInterlock:
+        if (language != EShLangFragment)
+            error(loc, "beginInvocationInterlockARB() must be in a fragment shader", "", "");
+        if (! inMain)
+            error(loc, "beginInvocationInterlockARB() must be in main()", "", "");
+        else if (postEntryPointReturn)
+            error(loc, "beginInvocationInterlockARB() cannot be placed after a return from main()", "", "");
+        if (controlFlowNestingLevel > 0)
+            error(loc, "beginInvocationInterlockARB() cannot be placed within flow control", "", "");
+
+        if (beginInvocationInterlockCount > 0)
+            error(loc, "beginInvocationInterlockARB() must only be called once", "", "");
+        if (endInvocationInterlockCount > 0)
+            error(loc, "beginInvocationInterlockARB() must be called before endInvocationInterlockARB()", "", "");
+
+        beginInvocationInterlockCount++;
+
+        // default to pixel_interlock_ordered
+        if (intermediate.getInterlockOrdering() == EioNone)
+            intermediate.setInterlockOrdering(EioPixelInterlockOrdered);
+        break;
+    case EOpEndInvocationInterlock:
+        if (language != EShLangFragment)
+            error(loc, "endInvocationInterlockARB() must be in a fragment shader", "", "");
+        if (! inMain)
+            error(loc, "endInvocationInterlockARB() must be in main()", "", "");
+        else if (postEntryPointReturn)
+            error(loc, "endInvocationInterlockARB() cannot be placed after a return from main()", "", "");
+        if (controlFlowNestingLevel > 0)
+            error(loc, "endInvocationInterlockARB() cannot be placed within flow control", "", "");
+
+        if (endInvocationInterlockCount > 0)
+            error(loc, "endInvocationInterlockARB() must only be called once", "", "");
+        if (beginInvocationInterlockCount == 0)
+            error(loc, "beginInvocationInterlockARB() must be called before endInvocationInterlockARB()", "", "");
+
+        endInvocationInterlockCount++;
+        break;
     default:
         break;
     }
@@ -4945,6 +4983,17 @@
                 return;
             }
         }
+        for (TInterlockOrdering order = (TInterlockOrdering)(EioNone + 1); order < EioCount; order = (TInterlockOrdering)(order+1)) {
+            if (id == TQualifier::getInterlockOrderingString(order)) {
+                requireProfile(loc, ECoreProfile | ECompatibilityProfile, "fragment shader interlock layout qualifier");
+                profileRequires(loc, ECoreProfile | ECompatibilityProfile, 450, nullptr, "fragment shader interlock layout qualifier");
+                requireExtensions(loc, 1, &E_GL_ARB_fragment_shader_interlock, TQualifier::getInterlockOrderingString(order));
+                if (order == EioShadingRateInterlockOrdered || order == EioShadingRateInterlockUnordered)
+                    requireExtensions(loc, 1, &E_GL_NV_shading_rate_image, TQualifier::getInterlockOrderingString(order));
+                publicType.shaderQualifiers.interlockOrdering = order;
+                return;
+            }
+        }
         if (id.compare(0, 13, "blend_support") == 0) {
             bool found = false;
             for (TBlendEquationShift be = (TBlendEquationShift)0; be < EBlendCount; be = (TBlendEquationShift)(be + 1)) {
@@ -5945,6 +5994,8 @@
         error(loc, message, "blend equation", "");
     if (shaderQualifiers.numViews != TQualifier::layoutNotSet)
         error(loc, message, "num_views", "");
+    if (shaderQualifiers.interlockOrdering != EioNone)
+        error(loc, message, TQualifier::getInterlockOrderingString(shaderQualifiers.interlockOrdering), "");
 }
 
 // Correct and/or advance an object's offset layout qualifier.
@@ -7874,6 +7925,14 @@
         if (publicType.qualifier.storage != EvqVaryingOut)
             error(loc, "can only apply to 'out'", "blend equation", "");
     }
+    if (publicType.shaderQualifiers.interlockOrdering) {
+        if (publicType.qualifier.storage == EvqVaryingIn) {
+            if (!intermediate.setInterlockOrdering(publicType.shaderQualifiers.interlockOrdering))
+                error(loc, "cannot change previously set fragment shader interlock ordering", TQualifier::getInterlockOrderingString(publicType.shaderQualifiers.interlockOrdering), "");
+        }
+        else
+            error(loc, "can only apply to 'in'", TQualifier::getInterlockOrderingString(publicType.shaderQualifiers.interlockOrdering), "");
+    }
 
 #ifdef NV_EXTENSIONS
     if (publicType.shaderQualifiers.layoutDerivativeGroupQuads &&
diff --git a/glslang/MachineIndependent/ParseHelper.h b/glslang/MachineIndependent/ParseHelper.h
index a1ffe64..04ede1e 100644
--- a/glslang/MachineIndependent/ParseHelper.h
+++ b/glslang/MachineIndependent/ParseHelper.h
@@ -85,6 +85,7 @@
             statementNestingLevel(0), loopNestingLevel(0), structNestingLevel(0), controlFlowNestingLevel(0),
             postEntryPointReturn(false),
             contextPragma(true, false),
+            beginInvocationInterlockCount(0), endInvocationInterlockCount(0),
             parsingBuiltins(parsingBuiltins), scanContext(nullptr), ppContext(nullptr),
             limits(resources.limits),
             globalUniformBlock(nullptr),
@@ -182,6 +183,8 @@
     // the statementNestingLevel the current switch statement is at, which must match the level of its case statements
     TList<int> switchLevel;
     struct TPragma contextPragma;
+    int beginInvocationInterlockCount;
+    int endInvocationInterlockCount;
 
 protected:
     TParseContextBase(TParseContextBase&);
diff --git a/glslang/MachineIndependent/Versions.cpp b/glslang/MachineIndependent/Versions.cpp
index f19c385..6c97da1 100644
--- a/glslang/MachineIndependent/Versions.cpp
+++ b/glslang/MachineIndependent/Versions.cpp
@@ -187,6 +187,7 @@
 //    extensionBehavior[E_GL_ARB_cull_distance]                = EBhDisable;    // present for 4.5, but need extension control over block members
     extensionBehavior[E_GL_ARB_post_depth_coverage]          = EBhDisable;
     extensionBehavior[E_GL_ARB_shader_viewport_layer_array]  = EBhDisable;
+    extensionBehavior[E_GL_ARB_fragment_shader_interlock]    = EBhDisable;
 
     extensionBehavior[E_GL_KHR_shader_subgroup_basic]            = EBhDisable;
     extensionBehavior[E_GL_KHR_shader_subgroup_vote]             = EBhDisable;
@@ -379,6 +380,7 @@
             "#define GL_ARB_shader_stencil_export 1\n"
 //            "#define GL_ARB_cull_distance 1\n"    // present for 4.5, but need extension control over block members
             "#define GL_ARB_post_depth_coverage 1\n"
+            "#define GL_ARB_fragment_shader_interlock 1\n"
             "#define GL_EXT_shader_non_constant_global_initializers 1\n"
             "#define GL_EXT_shader_image_load_formatted 1\n"
             "#define GL_EXT_post_depth_coverage 1\n"
diff --git a/glslang/MachineIndependent/Versions.h b/glslang/MachineIndependent/Versions.h
index bff0827..953d33a 100644
--- a/glslang/MachineIndependent/Versions.h
+++ b/glslang/MachineIndependent/Versions.h
@@ -141,6 +141,7 @@
 // const char* const E_GL_ARB_cull_distance            = "GL_ARB_cull_distance";  // present for 4.5, but need extension control over block members
 const char* const E_GL_ARB_post_depth_coverage          = "GL_ARB_post_depth_coverage";
 const char* const E_GL_ARB_shader_viewport_layer_array  = "GL_ARB_shader_viewport_layer_array";
+const char* const E_GL_ARB_fragment_shader_interlock    = "GL_ARB_fragment_shader_interlock";
 
 const char* const E_GL_KHR_shader_subgroup_basic            = "GL_KHR_shader_subgroup_basic";
 const char* const E_GL_KHR_shader_subgroup_vote             = "GL_KHR_shader_subgroup_vote";
diff --git a/glslang/MachineIndependent/intermOut.cpp b/glslang/MachineIndependent/intermOut.cpp
index f9f285e..abf5681 100644
--- a/glslang/MachineIndependent/intermOut.cpp
+++ b/glslang/MachineIndependent/intermOut.cpp
@@ -1502,6 +1502,8 @@
             }
             infoSink.debug << "\n";
         }
+        if (interlockOrdering != EioNone)
+            infoSink.debug << "interlock ordering = " << TQualifier::getInterlockOrderingString(interlockOrdering) << "\n";
         break;
 
 #ifdef NV_EXTENSIONS
diff --git a/glslang/MachineIndependent/localintermediate.h b/glslang/MachineIndependent/localintermediate.h
index f29c93c..30ba98d 100644
--- a/glslang/MachineIndependent/localintermediate.h
+++ b/glslang/MachineIndependent/localintermediate.h
@@ -240,7 +240,7 @@
         invocations(TQualifier::layoutNotSet), vertices(TQualifier::layoutNotSet),
         inputPrimitive(ElgNone), outputPrimitive(ElgNone),
         pixelCenterInteger(false), originUpperLeft(false),
-        vertexSpacing(EvsNone), vertexOrder(EvoNone), pointMode(false), earlyFragmentTests(false),
+        vertexSpacing(EvsNone), vertexOrder(EvoNone), interlockOrdering(EioNone), pointMode(false), earlyFragmentTests(false),
         postDepthCoverage(false), depthLayout(EldNone), depthReplacing(false),
         hlslFunctionality1(false),
         blendEquations(0), xfbMode(false), multiStream(false),
@@ -608,6 +608,15 @@
     void setPointMode() { pointMode = true; }
     bool getPointMode() const { return pointMode; }
 
+    bool setInterlockOrdering(TInterlockOrdering o)
+    {
+        if (interlockOrdering != EioNone)
+            return interlockOrdering == o;
+        interlockOrdering = o;
+        return true;
+    }
+    TInterlockOrdering getInterlockOrdering() const { return interlockOrdering; }
+
     bool setLocalSize(int dim, int size)
     {
         if (localSize[dim] > 1)
@@ -826,6 +835,7 @@
     bool originUpperLeft;
     TVertexSpacing vertexSpacing;
     TVertexOrder vertexOrder;
+    TInterlockOrdering interlockOrdering;
     bool pointMode;
     int localSize[3];
     int localSizeSpecId[3];
diff --git a/gtests/Spv.FromFile.cpp b/gtests/Spv.FromFile.cpp
index 5a126f1..ead5e6d 100644
--- a/gtests/Spv.FromFile.cpp
+++ b/gtests/Spv.FromFile.cpp
@@ -319,6 +319,8 @@
         "spv.fragmentDensity.vert",
         "spv.fragmentDensity-es.frag",
         "spv.fragmentDensity-neg.frag",
+        "spv.fsi.frag",
+        "spv.fsi_Error.frag",
         "spv.fullyCovered.frag",
         "spv.functionCall.frag",
         "spv.functionNestedOpaque.vert",
diff --git a/known_good.json b/known_good.json
index 95f1f0a..337a573 100644
--- a/known_good.json
+++ b/known_good.json
@@ -5,14 +5,14 @@
       "site" : "github",
       "subrepo" : "KhronosGroup/SPIRV-Tools",
       "subdir" : "External/spirv-tools",
-      "commit" : "9dfd4b8358077bdbe8e2f9388572b5376c370f5d"
+      "commit" : "2c0111e6eba779cf30e8c7f5a733ea0762895ba0"
     },
     {
       "name" : "spirv-tools/external/spirv-headers",
       "site" : "github",
       "subrepo" : "KhronosGroup/SPIRV-Headers",
       "subdir" : "External/spirv-tools/external/spirv-headers",
-      "commit" : "4b0985f29277a81f5ce88feb0502cc44d6d5e7c3"
+      "commit" : "8b911bd2ba37677037b38c9bd286c7c05701bcda"
     }
   ]
 }