Implement extension GL_AMD_shader_image_load_store_lod
diff --git a/SPIRV/GLSL.ext.AMD.h b/SPIRV/GLSL.ext.AMD.h
index b5b8dd6..5121ed9 100644
--- a/SPIRV/GLSL.ext.AMD.h
+++ b/SPIRV/GLSL.ext.AMD.h
@@ -33,7 +33,7 @@
 enum Op;
 
 static const int GLSLextAMDVersion = 100;
-static const int GLSLextAMDRevision = 4;
+static const int GLSLextAMDRevision = 5;
 
 // SPV_AMD_shader_ballot
 static const char* const E_SPV_AMD_shader_ballot = "SPV_AMD_shader_ballot";
@@ -101,4 +101,9 @@
 // SPV_AMD_gpu_shader_int16
 static const char* const E_SPV_AMD_gpu_shader_int16 = "SPV_AMD_gpu_shader_int16";
 
+// SPV_AMD_shader_image_load_store_lod
+static const char* const E_SPV_AMD_shader_image_load_store_lod = "SPV_AMD_shader_image_load_store_lod";
+
+static const Capability CapabilityImageReadWriteLodAMD = static_cast<Capability>(5015);
+
 #endif  // #ifndef GLSLextAMD_H
diff --git a/SPIRV/GlslangToSpv.cpp b/SPIRV/GlslangToSpv.cpp
index a167702..78ec833 100755
--- a/SPIRV/GlslangToSpv.cpp
+++ b/SPIRV/GlslangToSpv.cpp
@@ -1452,7 +1452,11 @@
         builder.setAccessChainRValue(result);
 
         return false;
+#ifdef AMD_EXTENSIONS
+    } else if (node->getOp() == glslang::EOpImageStore || node->getOp() == glslang::EOpImageStoreLod) {
+#else
     } else if (node->getOp() == glslang::EOpImageStore) {
+#endif
         // "imageStore" is a special case, which has no result
         return false;
     }
@@ -3136,6 +3140,10 @@
             if (i == 4)
                 lvalue = true;
             break;
+        case glslang::EOpSparseImageLoadLod:
+            if (i == 3)
+                lvalue = true;
+            break;
 #endif
         default:
             break;
@@ -3238,26 +3246,55 @@
         }
 
         operands.push_back(*(opIt++));
+#ifdef AMD_EXTENSIONS
+        if (node->getOp() == glslang::EOpImageLoad || node->getOp() == glslang::EOpImageLoadLod) {
+#else
         if (node->getOp() == glslang::EOpImageLoad) {
+#endif
             if (sampler.ms) {
                 operands.push_back(spv::ImageOperandsSampleMask);
                 operands.push_back(*opIt);
+#ifdef AMD_EXTENSIONS
+            } else if (cracked.lod) {
+                builder.addExtension(spv::E_SPV_AMD_shader_image_load_store_lod);
+                builder.addCapability(spv::CapabilityImageReadWriteLodAMD);
+
+                operands.push_back(spv::ImageOperandsLodMask);
+                operands.push_back(*opIt);
+#endif
             }
             if (builder.getImageTypeFormat(builder.getImageType(operands.front())) == spv::ImageFormatUnknown)
                 builder.addCapability(spv::CapabilityStorageImageReadWithoutFormat);
             return builder.createOp(spv::OpImageRead, resultType(), operands);
+#ifdef AMD_EXTENSIONS
+        } else if (node->getOp() == glslang::EOpImageStore || node->getOp() == glslang::EOpImageStoreLod) {
+#else
         } else if (node->getOp() == glslang::EOpImageStore) {
+#endif
             if (sampler.ms) {
                 operands.push_back(*(opIt + 1));
                 operands.push_back(spv::ImageOperandsSampleMask);
                 operands.push_back(*opIt);
+#ifdef AMD_EXTENSIONS
+            } else if (cracked.lod) {
+                builder.addExtension(spv::E_SPV_AMD_shader_image_load_store_lod);
+                builder.addCapability(spv::CapabilityImageReadWriteLodAMD);
+
+                operands.push_back(*(opIt + 1));
+                operands.push_back(spv::ImageOperandsLodMask);
+                operands.push_back(*opIt);
+#endif
             } else
                 operands.push_back(*opIt);
             builder.createNoResultOp(spv::OpImageWrite, operands);
             if (builder.getImageTypeFormat(builder.getImageType(operands.front())) == spv::ImageFormatUnknown)
                 builder.addCapability(spv::CapabilityStorageImageWriteWithoutFormat);
             return spv::NoResult;
+#ifdef AMD_EXTENSIONS
+        } else if (node->getOp() == glslang::EOpSparseImageLoad || node->getOp() == glslang::EOpSparseImageLoadLod) {
+#else
         } else if (node->getOp() == glslang::EOpSparseImageLoad) {
+#endif
             builder.addCapability(spv::CapabilitySparseResidency);
             if (builder.getImageTypeFormat(builder.getImageType(operands.front())) == spv::ImageFormatUnknown)
                 builder.addCapability(spv::CapabilityStorageImageReadWithoutFormat);
@@ -3265,6 +3302,14 @@
             if (sampler.ms) {
                 operands.push_back(spv::ImageOperandsSampleMask);
                 operands.push_back(*opIt++);
+#ifdef AMD_EXTENSIONS
+            } else if (cracked.lod) {
+                builder.addExtension(spv::E_SPV_AMD_shader_image_load_store_lod);
+                builder.addCapability(spv::CapabilityImageReadWriteLodAMD);
+
+                operands.push_back(spv::ImageOperandsLodMask);
+                operands.push_back(*opIt++);
+#endif
             }
 
             // Create the return type that was a special structure
diff --git a/SPIRV/doc.cpp b/SPIRV/doc.cpp
index b47fa48..fa12ab0 100755
--- a/SPIRV/doc.cpp
+++ b/SPIRV/doc.cpp
@@ -844,6 +844,7 @@
 
 #ifdef AMD_EXTENSIONS
     case 5009: return "ImageGatherBiasLodAMD";
+    case 5015: return "ImageReadWriteLodAMD";
 #endif
 
     case 4445: return "AtomicStorageOps";
diff --git a/Test/baseResults/spv.imageLoadStoreLod.frag.out b/Test/baseResults/spv.imageLoadStoreLod.frag.out
new file mode 100644
index 0000000..0f16430
--- /dev/null
+++ b/Test/baseResults/spv.imageLoadStoreLod.frag.out
@@ -0,0 +1,135 @@
+spv.imageLoadStoreLod.frag
+// Module Version 10000
+// Generated by (magic number): 80001
+// Id's are bound by 82
+
+                              Capability Shader
+                              Capability ImageCubeArray
+                              Capability SparseResidency
+                              Capability Image1D
+                              Capability ImageReadWriteLodAMD
+                              Extension  "SPV_AMD_shader_image_load_store_lod"
+               1:             ExtInstImport  "GLSL.std.450"
+                              MemoryModel Logical GLSL450
+                              EntryPoint Fragment 4  "main" 77
+                              ExecutionMode 4 OriginUpperLeft
+                              Source GLSL 450
+                              SourceExtension  "GL_AMD_shader_image_load_store_lod"
+                              Name 4  "main"
+                              Name 9  "f4"
+                              Name 14  "i1D"
+                              Name 24  "i2D"
+                              Name 34  "i3D"
+                              Name 46  "iiCube"
+                              Name 53  "ii1DArray"
+                              Name 60  "ui2DArray"
+                              Name 64  "u4"
+                              Name 65  "ResType"
+                              Name 71  "uiCubeArray"
+                              Name 77  "fragColor"
+                              Decorate 14(i1D) DescriptorSet 0
+                              Decorate 14(i1D) Binding 0
+                              Decorate 24(i2D) DescriptorSet 0
+                              Decorate 24(i2D) Binding 1
+                              Decorate 34(i3D) DescriptorSet 0
+                              Decorate 34(i3D) Binding 2
+                              Decorate 46(iiCube) DescriptorSet 0
+                              Decorate 46(iiCube) Binding 3
+                              Decorate 53(ii1DArray) DescriptorSet 0
+                              Decorate 53(ii1DArray) Binding 4
+                              Decorate 60(ui2DArray) DescriptorSet 0
+                              Decorate 60(ui2DArray) Binding 5
+                              Decorate 71(uiCubeArray) DescriptorSet 0
+                              Decorate 71(uiCubeArray) Binding 6
+                              Decorate 77(fragColor) Location 0
+               2:             TypeVoid
+               3:             TypeFunction 2
+               6:             TypeFloat 32
+               7:             TypeVector 6(float) 4
+               8:             TypePointer Function 7(fvec4)
+              10:    6(float) Constant 0
+              11:    7(fvec4) ConstantComposite 10 10 10 10
+              12:             TypeImage 6(float) 1D nonsampled format:Rgba32f
+              13:             TypePointer UniformConstant 12
+         14(i1D):     13(ptr) Variable UniformConstant
+              16:             TypeInt 32 1
+              17:     16(int) Constant 1
+              18:     16(int) Constant 3
+              22:             TypeImage 6(float) 2D nonsampled format:Rgba32f
+              23:             TypePointer UniformConstant 22
+         24(i2D):     23(ptr) Variable UniformConstant
+              26:             TypeVector 16(int) 2
+              27:     16(int) Constant 2
+              28:   26(ivec2) ConstantComposite 27 18
+              32:             TypeImage 6(float) 3D nonsampled format:Rgba32f
+              33:             TypePointer UniformConstant 32
+         34(i3D):     33(ptr) Variable UniformConstant
+              36:             TypeVector 16(int) 3
+              37:     16(int) Constant 4
+              38:     16(int) Constant 5
+              39:     16(int) Constant 6
+              40:   36(ivec3) ConstantComposite 37 38 39
+              44:             TypeImage 16(int) Cube nonsampled format:Rgba32i
+              45:             TypePointer UniformConstant 44
+      46(iiCube):     45(ptr) Variable UniformConstant
+              49:             TypeVector 16(int) 4
+              51:             TypeImage 16(int) 1D array nonsampled format:Rgba32i
+              52:             TypePointer UniformConstant 51
+   53(ii1DArray):     52(ptr) Variable UniformConstant
+              57:             TypeInt 32 0
+              58:             TypeImage 57(int) 2D array nonsampled format:Rgba32ui
+              59:             TypePointer UniformConstant 58
+   60(ui2DArray):     59(ptr) Variable UniformConstant
+              62:             TypeVector 57(int) 4
+              63:             TypePointer Function 62(ivec4)
+     65(ResType):             TypeStruct 16(int) 62(ivec4)
+              69:             TypeImage 57(int) Cube array nonsampled format:Rgba32ui
+              70:             TypePointer UniformConstant 69
+ 71(uiCubeArray):     70(ptr) Variable UniformConstant
+              76:             TypePointer Output 7(fvec4)
+   77(fragColor):     76(ptr) Variable Output
+         4(main):           2 Function None 3
+               5:             Label
+           9(f4):      8(ptr) Variable Function
+          64(u4):     63(ptr) Variable Function
+                              Store 9(f4) 11
+              15:          12 Load 14(i1D)
+              19:    7(fvec4) ImageRead 15 17 Lod 18
+              20:    7(fvec4) Load 9(f4)
+              21:    7(fvec4) FAdd 20 19
+                              Store 9(f4) 21
+              25:          22 Load 24(i2D)
+              29:    7(fvec4) ImageRead 25 28 Lod 18
+              30:    7(fvec4) Load 9(f4)
+              31:    7(fvec4) FAdd 30 29
+                              Store 9(f4) 31
+              35:          32 Load 34(i3D)
+              41:    7(fvec4) ImageRead 35 40 Lod 18
+              42:    7(fvec4) Load 9(f4)
+              43:    7(fvec4) FAdd 42 41
+                              Store 9(f4) 43
+              47:          44 Load 46(iiCube)
+              48:    7(fvec4) Load 9(f4)
+              50:   49(ivec4) ConvertFToS 48
+                              ImageWrite 47 40 50 Lod 18
+              54:          51 Load 53(ii1DArray)
+              55:    7(fvec4) Load 9(f4)
+              56:   49(ivec4) ConvertFToS 55
+                              ImageWrite 54 28 56 Lod 18
+              61:          58 Load 60(ui2DArray)
+              66: 65(ResType) ImageSparseRead 61 40 Lod 18
+              67:   62(ivec4) CompositeExtract 66 1
+                              Store 64(u4) 67
+              68:     16(int) CompositeExtract 66 0
+              72:          69 Load 71(uiCubeArray)
+              73: 65(ResType) ImageSparseRead 72 40 Lod 18
+              74:   62(ivec4) CompositeExtract 73 1
+                              Store 64(u4) 74
+              75:     16(int) CompositeExtract 73 0
+              78:    7(fvec4) Load 9(f4)
+              79:   62(ivec4) Load 64(u4)
+              80:    7(fvec4) ConvertUToF 79
+              81:    7(fvec4) FAdd 78 80
+                              Store 77(fragColor) 81
+                              Return
+                              FunctionEnd
diff --git a/Test/spv.imageLoadStoreLod.frag b/Test/spv.imageLoadStoreLod.frag
new file mode 100644
index 0000000..0da1da1
--- /dev/null
+++ b/Test/spv.imageLoadStoreLod.frag
@@ -0,0 +1,36 @@
+#version 450 core

+

+#extension GL_AMD_shader_image_load_store_lod: enable

+

+layout(rgba32f,  binding = 0) uniform image1D         i1D;

+layout(rgba32f,  binding = 1) uniform image2D         i2D;

+layout(rgba32f,  binding = 2) uniform image3D         i3D;

+layout(rgba32i,  binding = 3) uniform iimageCube      iiCube;

+layout(rgba32i,  binding = 4) uniform iimage1DArray   ii1DArray;

+layout(rgba32ui, binding = 5) uniform uimage2DArray   ui2DArray;

+layout(rgba32ui, binding = 6) uniform uimageCubeArray uiCubeArray;

+

+layout(location = 0) out vec4 fragColor;

+

+void main()

+{

+    const int c1 = 1;

+    const ivec2 c2 = ivec2(2, 3);

+    const ivec3 c3 = ivec3(4, 5, 6);

+

+    const int lod = 3;

+

+    vec4 f4 = vec4(0.0);

+    f4 += imageLoadLodAMD(i1D, c1, lod);

+    f4 += imageLoadLodAMD(i2D, c2, lod);

+    f4 += imageLoadLodAMD(i3D, c3, lod);

+

+    imageStoreLodAMD(iiCube, c3, lod, ivec4(f4));

+    imageStoreLodAMD(ii1DArray, c2, lod, ivec4(f4));

+

+    uvec4 u4;

+    sparseImageLoadLodAMD(ui2DArray, c3, lod, u4);

+    sparseImageLoadLodAMD(uiCubeArray, c3, lod, u4);

+

+    fragColor = f4 + vec4(u4);

+}
\ No newline at end of file
diff --git a/glslang/Include/intermediate.h b/glslang/Include/intermediate.h
index e289efd..6149b19 100644
--- a/glslang/Include/intermediate.h
+++ b/glslang/Include/intermediate.h
@@ -593,6 +593,10 @@
     EOpImageQuerySamples,
     EOpImageLoad,
     EOpImageStore,
+#ifdef AMD_EXTENSIONS
+    EOpImageLoadLod,
+    EOpImageStoreLod,
+#endif
     EOpImageAtomicAdd,
     EOpImageAtomicMin,
     EOpImageAtomicMax,
@@ -605,6 +609,9 @@
     EOpSubpassLoad,
     EOpSubpassLoadMS,
     EOpSparseImageLoad,
+#ifdef AMD_EXTENSIONS
+    EOpSparseImageLoadLod,
+#endif
 
     EOpImageGuardEnd,
 
@@ -1198,6 +1205,11 @@
             cracked.offsets = true;
             cracked.lod     = true;
             break;
+        case EOpImageLoadLod:
+        case EOpImageStoreLod:
+        case EOpSparseImageLoadLod:
+            cracked.lod = true;
+            break;
 #endif
         case EOpSubpassLoad:
         case EOpSubpassLoadMS:
diff --git a/glslang/MachineIndependent/Initialize.cpp b/glslang/MachineIndependent/Initialize.cpp
index 1beff77..6e6660e 100644
--- a/glslang/MachineIndependent/Initialize.cpp
+++ b/glslang/MachineIndependent/Initialize.cpp
@@ -4238,6 +4238,43 @@
             }
         }
     }
+
+#ifdef AMD_EXTENSIONS
+    if (sampler.dim == EsdRect || sampler.dim == EsdBuffer || sampler.shadow || sampler.ms)
+        return;
+
+    if (profile == EEsProfile || version < 450)
+        return;
+
+    TString imageLodParams = typeName;
+    if (dims == 1)
+        imageLodParams.append(", int");
+    else {
+        imageLodParams.append(", ivec");
+        imageLodParams.append(postfixes[dims]);
+    }
+    imageLodParams.append(", int");
+
+    commonBuiltins.append(prefixes[sampler.type]);
+    commonBuiltins.append("vec4 imageLoadLodAMD(readonly volatile coherent ");
+    commonBuiltins.append(imageLodParams);
+    commonBuiltins.append(");\n");
+
+    commonBuiltins.append("void imageStoreLodAMD(writeonly volatile coherent ");
+    commonBuiltins.append(imageLodParams);
+    commonBuiltins.append(", ");
+    commonBuiltins.append(prefixes[sampler.type]);
+    commonBuiltins.append("vec4);\n");
+
+    if (sampler.dim != Esd1D) {
+        commonBuiltins.append("int sparseImageLoadLodAMD(readonly volatile coherent ");
+        commonBuiltins.append(imageLodParams);
+        commonBuiltins.append(", out ");
+        commonBuiltins.append(prefixes[sampler.type]);
+        commonBuiltins.append("vec4");
+        commonBuiltins.append(");\n");
+    }
+#endif
 }
 
 //
@@ -5710,6 +5747,13 @@
             symbolTable.setFunctionExtensions("sparseTextureGatherLodOffsetAMD",    1, &E_GL_AMD_texture_gather_bias_lod);
             symbolTable.setFunctionExtensions("sparseTextureGatherLodOffsetsAMD",   1, &E_GL_AMD_texture_gather_bias_lod);
         }
+
+        // E_GL_AMD_shader_image_load_store_lod
+        if (profile != EEsProfile) {
+            symbolTable.setFunctionExtensions("imageLoadLodAMD",        1, &E_GL_AMD_shader_image_load_store_lod);
+            symbolTable.setFunctionExtensions("imageStoreLodAMD",       1, &E_GL_AMD_shader_image_load_store_lod);
+            symbolTable.setFunctionExtensions("sparseImageLoadLodAMD",  1, &E_GL_AMD_shader_image_load_store_lod);
+        }
 #endif
 
         symbolTable.setVariableExtensions("gl_FragDepthEXT", 1, &E_GL_EXT_frag_depth);
@@ -6146,6 +6190,10 @@
             symbolTable.relateToOperator("sparseTextureGatherLodAMD",           EOpSparseTextureGatherLod);
             symbolTable.relateToOperator("sparseTextureGatherLodOffsetAMD",     EOpSparseTextureGatherLodOffset);
             symbolTable.relateToOperator("sparseTextureGatherLodOffsetsAMD",    EOpSparseTextureGatherLodOffsets);
+
+            symbolTable.relateToOperator("imageLoadLodAMD",                     EOpImageLoadLod);
+            symbolTable.relateToOperator("imageStoreLodAMD",                    EOpImageStoreLod);
+            symbolTable.relateToOperator("sparseImageLoadLodAMD",               EOpSparseImageLoadLod);
 #endif
         }
         if (profile == EEsProfile) {
diff --git a/glslang/MachineIndependent/ParseHelper.cpp b/glslang/MachineIndependent/ParseHelper.cpp
index 687c211..7902db8 100644
--- a/glslang/MachineIndependent/ParseHelper.cpp
+++ b/glslang/MachineIndependent/ParseHelper.cpp
@@ -1144,7 +1144,13 @@
             operationPrecision = std::max(operationPrecision, function[arg].type->getQualifier().precision);
         }
         // compute the result precision
+#ifdef AMD_EXTENSIONS
+        if (agg->isSampling() ||
+            agg->getOp() == EOpImageLoad || agg->getOp() == EOpImageStore ||
+            agg->getOp() == EOpImageLoadLod || agg->getOp() == EOpImageStoreLod)
+#else
         if (agg->isSampling() || agg->getOp() == EOpImageLoad || agg->getOp() == EOpImageStore)
+#endif
             resultPrecision = sequence[0]->getAsTyped()->getQualifier().precision;
         else if (function.getType().getBasicType() != EbtBool)
             resultPrecision = function.getType().getQualifier().precision == EpqNone ?
diff --git a/glslang/MachineIndependent/Versions.cpp b/glslang/MachineIndependent/Versions.cpp
index f24b563..531aa15 100644
--- a/glslang/MachineIndependent/Versions.cpp
+++ b/glslang/MachineIndependent/Versions.cpp
@@ -200,6 +200,7 @@
     extensionBehavior[E_GL_AMD_gpu_shader_half_float]                = EBhDisable;
     extensionBehavior[E_GL_AMD_texture_gather_bias_lod]              = EBhDisable;
     extensionBehavior[E_GL_AMD_gpu_shader_int16]                     = EBhDisable;
+    extensionBehavior[E_GL_AMD_shader_image_load_store_lod]          = EBhDisable;
 #endif
 
 #ifdef NV_EXTENSIONS
@@ -331,6 +332,7 @@
             "#define GL_AMD_gpu_shader_half_float 1\n"
             "#define GL_AMD_texture_gather_bias_lod 1\n"
             "#define GL_AMD_gpu_shader_int16 1\n"
+            "#define GL_AMD_shader_image_load_store_lod 1\n"
 #endif
 
 #ifdef NV_EXTENSIONS
diff --git a/glslang/MachineIndependent/Versions.h b/glslang/MachineIndependent/Versions.h
index 0f68492..78a9b28 100644
--- a/glslang/MachineIndependent/Versions.h
+++ b/glslang/MachineIndependent/Versions.h
@@ -170,6 +170,7 @@
 const char* const E_GL_AMD_gpu_shader_half_float                = "GL_AMD_gpu_shader_half_float";
 const char* const E_GL_AMD_texture_gather_bias_lod              = "GL_AMD_texture_gather_bias_lod";
 const char* const E_GL_AMD_gpu_shader_int16                     = "GL_AMD_gpu_shader_int16";
+const char* const E_GL_AMD_shader_image_load_store_lod          = "GL_AMD_shader_image_load_store_lod";
 #endif
 
 #ifdef NV_EXTENSIONS
diff --git a/glslang/MachineIndependent/intermOut.cpp b/glslang/MachineIndependent/intermOut.cpp
index 479f15d..30240fc 100644
--- a/glslang/MachineIndependent/intermOut.cpp
+++ b/glslang/MachineIndependent/intermOut.cpp
@@ -704,6 +704,10 @@
     case EOpImageAtomicXor:             out.debug << "imageAtomicXor";        break;
     case EOpImageAtomicExchange:        out.debug << "imageAtomicExchange";   break;
     case EOpImageAtomicCompSwap:        out.debug << "imageAtomicCompSwap";   break;
+#ifdef AMD_EXTENSIONS
+    case EOpImageLoadLod:               out.debug << "imageLoadLod";          break;
+    case EOpImageStoreLod:              out.debug << "imageStoreLod";         break;
+#endif
 
     case EOpTextureQuerySize:           out.debug << "textureSize";           break;
     case EOpTextureQueryLod:            out.debug << "textureQueryLod";       break;
@@ -756,6 +760,7 @@
     case EOpSparseTextureGatherLod:         out.debug << "sparseTextureGatherLod";          break;
     case EOpSparseTextureGatherLodOffset:   out.debug << "sparseTextureGatherLodOffset";    break;
     case EOpSparseTextureGatherLodOffsets:  out.debug << "sparseTextureGatherLodOffsets";   break;
+    case EOpSparseImageLoadLod:             out.debug << "sparseImageLoadLod";              break;
 #endif
 
     case EOpAddCarry:                   out.debug << "addCarry";              break;
diff --git a/gtests/Spv.FromFile.cpp b/gtests/Spv.FromFile.cpp
index 86d5bba..ef2ef34 100644
--- a/gtests/Spv.FromFile.cpp
+++ b/gtests/Spv.FromFile.cpp
@@ -406,6 +406,7 @@
     Glsl, CompileVulkanToSpirvTestAMD,
     ::testing::ValuesIn(std::vector<std::string>({
         "spv.float16.frag",
+        "spv.imageLoadStoreLod.frag",
         "spv.int16.frag",
         "spv.shaderBallotAMD.comp",
         "spv.textureGatherBiasLod.frag"