SPV 1.4: Implement the 5 new loop controls.
diff --git a/SPIRV/GlslangToSpv.cpp b/SPIRV/GlslangToSpv.cpp
index d25ccd3..3d0f0c7 100644
--- a/SPIRV/GlslangToSpv.cpp
+++ b/SPIRV/GlslangToSpv.cpp
@@ -135,7 +135,7 @@
     spv::ImageFormat TranslateImageFormat(const glslang::TType& type);
     spv::SelectionControlMask TranslateSelectionControl(const glslang::TIntermSelection&) const;
     spv::SelectionControlMask TranslateSwitchControl(const glslang::TIntermSwitch&) const;
-    spv::LoopControlMask TranslateLoopControl(const glslang::TIntermLoop&, unsigned int& dependencyLength) const;
+    spv::LoopControlMask TranslateLoopControl(const glslang::TIntermLoop&, std::vector<unsigned int>& operands) const;
     spv::StorageClass TranslateStorageClass(const glslang::TType&);
     void addIndirectionIndexCapabilities(const glslang::TType& baseType, const glslang::TType& indexType);
     spv::Id createSpvVariable(const glslang::TIntermSymbol*);
@@ -1055,7 +1055,7 @@
 
 // return a non-0 dependency if the dependency argument must be set
 spv::LoopControlMask TGlslangToSpvTraverser::TranslateLoopControl(const glslang::TIntermLoop& loopNode,
-    unsigned int& dependencyLength) const
+    std::vector<unsigned int>& operands) const
 {
     spv::LoopControlMask control = spv::LoopControlMaskNone;
 
@@ -1067,7 +1067,29 @@
         control = control | spv::LoopControlDependencyInfiniteMask;
     else if (loopNode.getLoopDependency() > 0) {
         control = control | spv::LoopControlDependencyLengthMask;
-        dependencyLength = loopNode.getLoopDependency();
+        operands.push_back((unsigned int)loopNode.getLoopDependency());
+    }
+    if (glslangIntermediate->getSpv().spv >= glslang::EShTargetSpv_1_4) {
+        if (loopNode.getMinIterations() > 0) {
+            control = control | spv::LoopControlMinIterationsMask;
+            operands.push_back(loopNode.getMinIterations());
+        }
+        if (loopNode.getMaxIterations() < glslang::TIntermLoop::iterationsInfinite) {
+            control = control | spv::LoopControlMaxIterationsMask;
+            operands.push_back(loopNode.getMaxIterations());
+        }
+        if (loopNode.getIterationMultiple() > 1) {
+            control = control | spv::LoopControlIterationMultipleMask;
+            operands.push_back(loopNode.getIterationMultiple());
+        }
+        if (loopNode.getPeelCount() > 0) {
+            control = control | spv::LoopControlPeelCountMask;
+            operands.push_back(loopNode.getPeelCount());
+        }
+        if (loopNode.getPartialCount() > 0) {
+            control = control | spv::LoopControlPartialCountMask;
+            operands.push_back(loopNode.getPartialCount());
+        }
     }
 
     return control;
@@ -2841,8 +2863,8 @@
     builder.createBranch(&blocks.head);
 
     // Loop control:
-    unsigned int dependencyLength = glslang::TIntermLoop::dependencyInfinite;
-    const spv::LoopControlMask control = TranslateLoopControl(*node, dependencyLength);
+    std::vector<unsigned int> operands;
+    const spv::LoopControlMask control = TranslateLoopControl(*node, operands);
 
     // Spec requires back edges to target header blocks, and every header block
     // must dominate its merge block.  Make a header block first to ensure these
@@ -2852,7 +2874,7 @@
     // including merges of its own.
     builder.setLine(node->getLoc().line, node->getLoc().getFilename());
     builder.setBuildPoint(&blocks.head);
-    builder.createLoopMerge(&blocks.merge, &blocks.continue_target, control, dependencyLength);
+    builder.createLoopMerge(&blocks.merge, &blocks.continue_target, control, operands);
     if (node->testFirst() && node->getTest()) {
         spv::Block& test = builder.makeNewBlock();
         builder.createBranch(&test);
diff --git a/SPIRV/SpvBuilder.cpp b/SPIRV/SpvBuilder.cpp
index 138c41c..6eb18d0 100644
--- a/SPIRV/SpvBuilder.cpp
+++ b/SPIRV/SpvBuilder.cpp
@@ -2956,14 +2956,14 @@
 }
 
 void Builder::createLoopMerge(Block* mergeBlock, Block* continueBlock, unsigned int control,
-                              unsigned int dependencyLength)
+                              const std::vector<unsigned int>& operands)
 {
     Instruction* merge = new Instruction(OpLoopMerge);
     merge->addIdOperand(mergeBlock->getId());
     merge->addIdOperand(continueBlock->getId());
     merge->addImmediateOperand(control);
-    if ((control & LoopControlDependencyLengthMask) != 0)
-        merge->addImmediateOperand(dependencyLength);
+    for (int op = 0; op < (int)operands.size(); ++op)
+        merge->addImmediateOperand(operands[op]);
     buildPoint->addInstruction(std::unique_ptr<Instruction>(merge));
 }
 
diff --git a/SPIRV/SpvBuilder.h b/SPIRV/SpvBuilder.h
index 52f7fba..72018a3 100644
--- a/SPIRV/SpvBuilder.h
+++ b/SPIRV/SpvBuilder.h
@@ -662,7 +662,7 @@
 
     void createBranch(Block* block);
     void createConditionalBranch(Id condition, Block* thenBlock, Block* elseBlock);
-    void createLoopMerge(Block* mergeBlock, Block* continueBlock, unsigned int control, unsigned int dependencyLength);
+    void createLoopMerge(Block* mergeBlock, Block* continueBlock, unsigned int control, const std::vector<unsigned int>& operands);
 
     // Sets to generate opcode for specialization constants.
     void setToSpecConstCodeGenMode() { generatingOpCodeForSpecConst = true; }
diff --git a/SPIRV/doc.cpp b/SPIRV/doc.cpp
index 76e1df8..4c27843 100644
--- a/SPIRV/doc.cpp
+++ b/SPIRV/doc.cpp
@@ -674,15 +674,20 @@
     }
 }
 
-const int LoopControlCeiling = 4;
+const int LoopControlCeiling = LoopControlPartialCountShift + 1;
 
 const char* LoopControlString(int cont)
 {
     switch (cont) {
-    case 0:  return "Unroll";
-    case 1:  return "DontUnroll";
-    case 2:  return "DependencyInfinite";
-    case 3:  return "DependencyLength";
+    case LoopControlUnrollShift:             return "Unroll";
+    case LoopControlDontUnrollShift:         return "DontUnroll";
+    case LoopControlDependencyInfiniteShift: return "DependencyInfinite";
+    case LoopControlDependencyLengthShift:   return "DependencyLength";
+    case LoopControlMinIterationsShift:      return "MinIterations";
+    case LoopControlMaxIterationsShift:      return "MaxIterations";
+    case LoopControlIterationMultipleShift:  return "IterationMultiple";
+    case LoopControlPeelCountShift:          return "PeelCount";
+    case LoopControlPartialCountShift:       return "PartialCount";
 
     case LoopControlCeiling:
     default: return "Bad";
diff --git a/Test/baseResults/460.frag.out b/Test/baseResults/460.frag.out
index 90c4837..8670e6e 100644
--- a/Test/baseResults/460.frag.out
+++ b/Test/baseResults/460.frag.out
@@ -56,7 +56,7 @@
 0:28  Function Definition: attExt( ( global void)
 0:28    Function Parameters: 
 0:30    Sequence
-0:30      Loop with condition not tested first: Dependency -3
+0:30      Loop with condition not tested first
 0:30        Loop Condition
 0:30        Constant:
 0:30          true (const bool)
diff --git a/Test/baseResults/spv.1.4.LoopControl.frag.out b/Test/baseResults/spv.1.4.LoopControl.frag.out
new file mode 100644
index 0000000..608b4bf
--- /dev/null
+++ b/Test/baseResults/spv.1.4.LoopControl.frag.out
@@ -0,0 +1,110 @@
+spv.1.4.LoopControl.frag
+WARNING: 0:15: 'min_iterations' : expected a single integer argument 
+WARNING: 0:15: 'max_iterations' : expected a single integer argument 
+
+Validation failed
+// Module Version 10400
+// Generated by (magic number): 80007
+// Id's are bound by 54
+
+                              Capability Shader
+               1:             ExtInstImport  "GLSL.std.450"
+                              MemoryModel Logical GLSL450
+                              EntryPoint Fragment 4  "main" 53
+                              ExecutionMode 4 OriginUpperLeft
+                              Source GLSL 450
+                              SourceExtension  "GL_EXT_control_flow_attributes"
+                              Name 4  "main"
+                              Name 8  "i"
+                              Name 32  "i"
+                              Name 42  "i"
+                              Name 53  "cond"
+               2:             TypeVoid
+               3:             TypeFunction 2
+               6:             TypeInt 32 1
+               7:             TypePointer Function 6(int)
+               9:      6(int) Constant 0
+              16:      6(int) Constant 8
+              17:             TypeBool
+              20:      6(int) Constant 1
+              27:    17(bool) ConstantTrue
+              52:             TypePointer Private 17(bool)
+        53(cond):     52(ptr) Variable Private
+         4(main):           2 Function None 3
+               5:             Label
+            8(i):      7(ptr) Variable Function
+           32(i):      7(ptr) Variable Function
+           42(i):      7(ptr) Variable Function
+                              Store 8(i) 9
+                              Branch 10
+              10:             Label
+                              LoopMerge 12 13 MinIterations MaxIterations  3 7
+                              Branch 14
+              14:             Label
+              15:      6(int) Load 8(i)
+              18:    17(bool) SLessThan 15 16
+                              BranchConditional 18 11 12
+              11:               Label
+                                Branch 13
+              13:               Label
+              19:      6(int)   Load 8(i)
+              21:      6(int)   IAdd 19 20
+                                Store 8(i) 21
+                                Branch 10
+              12:             Label
+                              Branch 22
+              22:             Label
+                              LoopMerge 24 25 IterationMultiple  2
+                              Branch 26
+              26:             Label
+                              BranchConditional 27 23 24
+              23:               Label
+                                Branch 25
+              25:               Label
+                                Branch 22
+              24:             Label
+                              Branch 28
+              28:             Label
+                              LoopMerge 30 31 PeelCount  5
+                              Branch 29
+              29:             Label
+                              Branch 31
+              31:             Label
+                              BranchConditional 27 28 30
+              30:             Label
+                              Store 32(i) 9
+                              Branch 33
+              33:             Label
+                              LoopMerge 35 36 PartialCount  4
+                              Branch 37
+              37:             Label
+              38:      6(int) Load 32(i)
+              39:    17(bool) SLessThan 38 16
+                              BranchConditional 39 34 35
+              34:               Label
+                                Branch 36
+              36:               Label
+              40:      6(int)   Load 32(i)
+              41:      6(int)   IAdd 40 20
+                                Store 32(i) 41
+                                Branch 33
+              35:             Label
+                              Store 42(i) 9
+                              Branch 43
+              43:             Label
+                              LoopMerge 45 46 None
+                              Branch 47
+              47:             Label
+              48:      6(int) Load 42(i)
+              49:    17(bool) SLessThan 48 16
+                              BranchConditional 49 44 45
+              44:               Label
+                                Branch 46
+              46:               Label
+              50:      6(int)   Load 42(i)
+              51:      6(int)   IAdd 50 20
+                                Store 42(i) 51
+                                Branch 43
+              45:             Label
+                              Return
+                              FunctionEnd
diff --git a/Test/baseResults/spv.controlFlowAttributes.frag.out b/Test/baseResults/spv.controlFlowAttributes.frag.out
index 489522b..c708232 100644
--- a/Test/baseResults/spv.controlFlowAttributes.frag.out
+++ b/Test/baseResults/spv.controlFlowAttributes.frag.out
@@ -1,7 +1,7 @@
 spv.controlFlowAttributes.frag
-WARNING: 0:20: '' : attribute with arguments not recognized, skipping 
-WARNING: 0:21: '' : attribute with arguments not recognized, skipping 
-WARNING: 0:22: '' : attribute with arguments not recognized, skipping 
+WARNING: 0:20: 'unroll' : expected no arguments 
+WARNING: 0:21: 'dont_unroll' : expected no arguments 
+WARNING: 0:22: 'dependency_infinite' : expected no arguments 
 WARNING: 0:23: 'dependency_length' : expected a single integer argument 
 WARNING: 0:24: '' : attribute with arguments not recognized, skipping 
 WARNING: 0:25: '' : attribute with arguments not recognized, skipping 
diff --git a/Test/spv.1.4.LoopControl.frag b/Test/spv.1.4.LoopControl.frag
new file mode 100644
index 0000000..00392ae
--- /dev/null
+++ b/Test/spv.1.4.LoopControl.frag
@@ -0,0 +1,19 @@
+#version 450
+
+#extension GL_EXT_control_flow_attributes : enable
+
+bool cond;
+
+void main()
+{
+        [[min_iterations(3), max_iterations(7)]]   for (int i = 0; i < 8; ++i) { }
+        [[iteration_multiple(2)]]                  while(true) {  }
+        [[peel_count(5)]]                          do {  } while(true);
+        [[partial_count(4)]]                       for (int i = 0; i < 8; ++i) { }
+
+        // warnings on all these
+        [[min_iterations, max_iterations]]   for (int i = 0; i < 8; ++i) { }
+        //[[iteration_multiple(0)]]                  while(true) {  }
+        //[[peel_count]]                          do {  } while(true);
+        //[[partial_count]]                       for (int i = 0; i < 8; ++i) { }
+}
diff --git a/glslang/Include/intermediate.h b/glslang/Include/intermediate.h
index a204904..89d1954 100644
--- a/glslang/Include/intermediate.h
+++ b/glslang/Include/intermediate.h
@@ -1116,7 +1116,12 @@
         first(testFirst),
         unroll(false),
         dontUnroll(false),
-        dependency(0)
+        dependency(0),
+        minIterations(0),
+        maxIterations(iterationsInfinite),
+        iterationMultiple(1),
+        peelCount(0),
+        partialCount(0)
     { }
 
     virtual       TIntermLoop* getAsLoopNode() { return this; }
@@ -1128,14 +1133,36 @@
     bool testFirst() const { return first; }
 
     void setUnroll()     { unroll = true; }
-    void setDontUnroll() { dontUnroll = true; }
+    void setDontUnroll() {
+        dontUnroll = true;
+        peelCount = 0;
+        partialCount = 0;
+    }
     bool getUnroll()     const { return unroll; }
     bool getDontUnroll() const { return dontUnroll; }
 
     static const unsigned int dependencyInfinite = 0xFFFFFFFF;
+    static const unsigned int iterationsInfinite = 0xFFFFFFFF;
     void setLoopDependency(int d) { dependency = d; }
     int getLoopDependency() const { return dependency; }
 
+    void setMinIterations(unsigned int v) { minIterations = v; }
+    unsigned int getMinIterations() const { return minIterations; }
+    void setMaxIterations(unsigned int v) { maxIterations = v; }
+    unsigned int getMaxIterations() const { return maxIterations; }
+    void setIterationMultiple(unsigned int v) { iterationMultiple = v; }
+    unsigned int getIterationMultiple() const { return iterationMultiple; }
+    void setPeelCount(unsigned int v) {
+        peelCount = v;
+        dontUnroll = false;
+    }
+    unsigned int getPeelCount() const { return peelCount; }
+    void setPartialCount(unsigned int v) {
+        partialCount = v;
+        dontUnroll = false;
+    }
+    unsigned int getPartialCount() const { return partialCount; }
+
 protected:
     TIntermNode* body;       // code to loop over
     TIntermTyped* test;      // exit condition associated with loop, could be 0 for 'for' loops
@@ -1144,6 +1171,11 @@
     bool unroll;             // true if unroll requested
     bool dontUnroll;         // true if request to not unroll
     unsigned int dependency; // loop dependency hint; 0 means not set or unknown
+    unsigned int minIterations;      // as per the SPIR-V specification
+    unsigned int maxIterations;      // as per the SPIR-V specification
+    unsigned int iterationMultiple;  // as per the SPIR-V specification
+    unsigned int peelCount;          // as per the SPIR-V specification
+    unsigned int partialCount;       // as per the SPIR-V specification
 };
 
 //
diff --git a/glslang/MachineIndependent/attribute.cpp b/glslang/MachineIndependent/attribute.cpp
index bf960ff..d4a23f3 100644
--- a/glslang/MachineIndependent/attribute.cpp
+++ b/glslang/MachineIndependent/attribute.cpp
@@ -52,6 +52,7 @@
     return true;
 }
 
+
 // extract strings out of attribute arguments stored in attribute aggregate.
 // convert to lower case if converToLower is true (for case-insensitive compare convenience)
 bool TAttributeArgs::getString(TString& value, int argNum, bool convertToLower) const 
@@ -110,6 +111,16 @@
         return EatDependencyInfinite;
     else if (name == "dependency_length")
         return EatDependencyLength;
+    else if (name == "min_iterations")
+        return EatMinIterations;
+    else if (name == "max_iterations")
+        return EatMaxIterations;
+    else if (name == "iteration_multiple")
+        return EatIterationMultiple;
+    else if (name == "peel_count")
+        return EatPeelCount;
+    else if (name == "partial_count")
+        return EatPartialCount;
     else
         return EatNone;
 }
@@ -225,29 +236,101 @@
     }
 
     for (auto it = attributes.begin(); it != attributes.end(); ++it) {
-        if (it->name != EatDependencyLength && it->size() > 0) {
-            warn(node->getLoc(), "attribute with arguments not recognized, skipping", "", "");
-            continue;
-        }
 
-        int value;
+        const auto noArgument = [&](const char* feature) {
+            if (it->size() > 0) {
+                warn(node->getLoc(), "expected no arguments", feature, "");
+                return false;
+            }
+            return true;
+        };
+
+        const auto positiveSignedArgument = [&](const char* feature, int& value) {
+            if (it->size() == 1 && it->getInt(value)) {
+                if (value <= 0) {
+                    error(node->getLoc(), "must be positive", feature, "");
+                    return false;
+                }
+            } else {
+                warn(node->getLoc(), "expected a single integer argument", feature, "");
+                return false;
+            }
+            return true;
+        };
+
+        const auto unsignedArgument = [&](const char* feature, unsigned int& uiValue) {
+            int value;
+            if (!(it->size() == 1 && it->getInt(value))) {
+                warn(node->getLoc(), "expected a single integer argument", feature, "");
+                return false;
+            }
+            uiValue = (unsigned int)value;
+            return true;
+        };
+
+        const auto positiveUnsignedArgument = [&](const char* feature, unsigned int& uiValue) {
+            int value;
+            if (it->size() == 1 && it->getInt(value)) {
+                if (value == 0) {
+                    error(node->getLoc(), "must be greater than or equal to 1", feature, "");
+                    return false;
+                }
+            } else {
+                warn(node->getLoc(), "expected a single integer argument", feature, "");
+                return false;
+            }
+            uiValue = (unsigned int)value;
+            return true;
+        };
+
+        const auto spirv14 = [&](const char* feature) {
+            if (spvVersion.spv > 0 && spvVersion.spv < EShTargetSpv_1_4)
+                warn(node->getLoc(), "attribute requires a SPIR-V 1.4 target-env", feature, "");
+        };
+
+        int value = 0;
+        unsigned uiValue = 0;
         switch (it->name) {
         case EatUnroll:
-            loop->setUnroll();
+            if (noArgument("unroll"))
+                loop->setUnroll();
             break;
         case EatLoop:
-            loop->setDontUnroll();
+            if (noArgument("dont_unroll"))
+                loop->setDontUnroll();
             break;
         case EatDependencyInfinite:
-            loop->setLoopDependency(TIntermLoop::dependencyInfinite);
+            if (noArgument("dependency_infinite"))
+                loop->setLoopDependency(TIntermLoop::dependencyInfinite);
             break;
         case EatDependencyLength:
-            if (it->size() == 1 && it->getInt(value)) {
-                if (value <= 0)
-                    error(node->getLoc(), "must be positive", "dependency_length", "");
+            if (positiveSignedArgument("dependency_length", value))
                 loop->setLoopDependency(value);
-            } else
-                warn(node->getLoc(), "expected a single integer argument", "dependency_length", "");
+            break;
+        case EatMinIterations:
+            spirv14("min_iterations");
+            if (unsignedArgument("min_iterations", uiValue))
+                loop->setMinIterations(uiValue);
+            break;
+        case EatMaxIterations:
+            spirv14("max_iterations");
+            if (unsignedArgument("max_iterations", uiValue))
+                loop->setMaxIterations(uiValue);
+            break;
+        case EatIterationMultiple:
+            spirv14("iteration_multiple");
+            if (positiveUnsignedArgument("iteration_multiple", uiValue))
+                loop->setIterationMultiple(uiValue);
+            break;
+        case EatPeelCount:
+            spirv14("peel_count");
+            if (unsignedArgument("peel_count", uiValue))
+                loop->setPeelCount(uiValue);
+            break;
+        case EatPartialCount:
+            spirv14("partial_count");
+            if (unsignedArgument("partial_count", uiValue))
+                loop->setPartialCount(uiValue);
             break;
         default:
             warn(node->getLoc(), "attribute does not apply to a loop", "", "");
diff --git a/glslang/MachineIndependent/attribute.h b/glslang/MachineIndependent/attribute.h
index 8d0c5bc..844ce45 100644
--- a/glslang/MachineIndependent/attribute.h
+++ b/glslang/MachineIndependent/attribute.h
@@ -71,7 +71,12 @@
         EatPushConstant,
         EatConstantId,
         EatDependencyInfinite,
-        EatDependencyLength
+        EatDependencyLength,
+        EatMinIterations,
+        EatMaxIterations,
+        EatIterationMultiple,
+        EatPeelCount,
+        EatPartialCount
     };
 
     class TIntermAggregate;
diff --git a/gtests/Spv.FromFile.cpp b/gtests/Spv.FromFile.cpp
index b8427c8..5978959 100755
--- a/gtests/Spv.FromFile.cpp
+++ b/gtests/Spv.FromFile.cpp
@@ -467,6 +467,7 @@
     ::testing::ValuesIn(std::vector<std::string>({
         "spv.1.4.OpEntryPoint.frag",
         "spv.1.4.OpSelect.frag",
+        "spv.1.4.LoopControl.frag",
     })),
     FileNameAsCustomTestSuffix
 );