Support SPV_KHR_vulkan_memory_model rev2

Support collapsed into one commit:
- Asm/Dis support for SPV_KHR_vulkan_memory_model
- Add Vulkan mem model image operands to switch
- Add TODO for source/validate_image.cpp
- val: Image operands NonPrivateTexelKHR, VolatileTexelKHR have no operands
  This is required for memory model tests to pass SPIR-V validation.
- Round trip tests: Test new flags on OpCopyMemory*
diff --git a/source/val/validate_image.cpp b/source/val/validate_image.cpp
index 2c020ed..b362292 100644
--- a/source/val/validate_image.cpp
+++ b/source/val/validate_image.cpp
@@ -55,6 +55,13 @@
     case SpvImageOperandsConstOffsetsMask:
     case SpvImageOperandsSampleMask:
     case SpvImageOperandsMinLodMask:
+
+    // TODO(dneto): Support image operands related to the Vulkan memory model.
+    // https://gitlab.khronos.org/spirv/spirv-tools/issues/32
+    case SpvImageOperandsMakeTexelAvailableKHRMask:
+    case SpvImageOperandsMakeTexelVisibleKHRMask:
+    case SpvImageOperandsNonPrivateTexelKHRMask:
+    case SpvImageOperandsVolatileTexelKHRMask:
       return true;
   }
   return false;
@@ -210,7 +217,12 @@
   const SpvOp opcode = inst->opcode();
   const size_t num_words = inst->words().size();
 
-  size_t expected_num_image_operand_words = spvtools::utils::CountSetBits(mask);
+  // NonPrivate and Volatile take no operand words.
+  const uint32_t mask_bits_having_operands =
+      mask & ~uint32_t(SpvImageOperandsNonPrivateTexelKHRMask |
+                       SpvImageOperandsVolatileTexelKHRMask);
+  size_t expected_num_image_operand_words =
+      spvtools::utils::CountSetBits(mask_bits_having_operands);
   if (mask & SpvImageOperandsGradMask) {
     // Grad uses two words.
     ++expected_num_image_operand_words;
diff --git a/test/binary_parse_test.cpp b/test/binary_parse_test.cpp
index 7d97001..8cf3043 100644
--- a/test/binary_parse_test.cpp
+++ b/test/binary_parse_test.cpp
@@ -890,8 +890,8 @@
          "Invalid function control operand: 31 has invalid mask component 16"},
         {"OpLoopMerge %1 %2 !1027",
          "Invalid loop control operand: 1027 has invalid mask component 1024"},
-        {"%2 = OpImageFetch %1 %image %coord !511",
-         "Invalid image operand: 511 has invalid mask component 256"},
+        {"%2 = OpImageFetch %1 %image %coord !32770",
+         "Invalid image operand: 32770 has invalid mask component 32768"},
         {"OpSelectionMerge %1 !7",
          "Invalid selection control operand: 7 has invalid mask component 4"},
     }), );
diff --git a/test/operand_capabilities_test.cpp b/test/operand_capabilities_test.cpp
index 0aeb505..152cf5b 100644
--- a/test/operand_capabilities_test.cpp
+++ b/test/operand_capabilities_test.cpp
@@ -604,16 +604,18 @@
             })), );
 
 // See SPIR-V Section 3.27 Scope <id>
-INSTANTIATE_TEST_CASE_P(Scope, EnumCapabilityTest,
-                        Combine(Values(SPV_ENV_UNIVERSAL_1_0,
-                                       SPV_ENV_UNIVERSAL_1_1),
-                                ValuesIn(std::vector<EnumCapabilityCase>{
-                                    CASE0(SCOPE_ID, ScopeCrossDevice),
-                                    CASE0(SCOPE_ID, ScopeDevice),
-                                    CASE0(SCOPE_ID, ScopeWorkgroup),
-                                    CASE0(SCOPE_ID, ScopeSubgroup),
-                                    CASE0(SCOPE_ID, ScopeInvocation),
-                                })), );
+INSTANTIATE_TEST_CASE_P(
+    Scope, EnumCapabilityTest,
+    Combine(Values(SPV_ENV_UNIVERSAL_1_0, SPV_ENV_UNIVERSAL_1_1,
+                   SPV_ENV_UNIVERSAL_1_2, SPV_ENV_UNIVERSAL_1_3),
+            ValuesIn(std::vector<EnumCapabilityCase>{
+                CASE0(SCOPE_ID, ScopeCrossDevice),
+                CASE0(SCOPE_ID, ScopeDevice),
+                CASE0(SCOPE_ID, ScopeWorkgroup),
+                CASE0(SCOPE_ID, ScopeSubgroup),
+                CASE0(SCOPE_ID, ScopeInvocation),
+                CASE1(SCOPE_ID, ScopeQueueFamilyKHR, VulkanMemoryModelKHR),
+            })), );
 
 // See SPIR-V Section 3.28 Group Operation
 INSTANTIATE_TEST_CASE_P(
diff --git a/test/operand_pattern_test.cpp b/test/operand_pattern_test.cpp
index b3e3024..851051b 100644
--- a/test/operand_pattern_test.cpp
+++ b/test/operand_pattern_test.cpp
@@ -89,9 +89,10 @@
     ::testing::ValuesIn(std::vector<MaskExpansionCase>{
         // No bits means no change.
         {SPV_OPERAND_TYPE_OPTIONAL_MEMORY_ACCESS, 0, {PREFIX0}, {PREFIX0}},
-        // Unknown bits means no change.
+        // Unknown bits means no change.  Use all bits that aren't in the grammar.
+	// The last mask enum is 0x20
         {SPV_OPERAND_TYPE_OPTIONAL_MEMORY_ACCESS,
-         0xfffffffc,
+         0xffffffc0,
          {PREFIX1},
          {PREFIX1}},
         // Volatile has no operands.
diff --git a/test/text_to_binary.extension_test.cpp b/test/text_to_binary.extension_test.cpp
index 0d8d324..7bed03d 100644
--- a/test/text_to_binary.extension_test.cpp
+++ b/test/text_to_binary.extension_test.cpp
@@ -510,6 +510,147 @@
                                  {SpvCapabilityVariablePointersStorageBuffer})},
             })), );
 
+// SPV_KHR_vulkan_memory_model
+
+INSTANTIATE_TEST_CASE_P(
+    SPV_KHR_vulkan_memory_model, ExtensionRoundTripTest,
+    // We'll get coverage over operand tables by trying the universal
+    // environments, and at least one specific environment.
+    //
+    // Note: SPV_KHR_vulkan_memory_model adds scope enum value QueueFamilyKHR.
+    // Scope enums are used in ID defini
+    Combine(
+        Values(SPV_ENV_UNIVERSAL_1_0, SPV_ENV_UNIVERSAL_1_1,
+               SPV_ENV_UNIVERSAL_1_3, SPV_ENV_VULKAN_1_0, SPV_ENV_VULKAN_1_1),
+        ValuesIn(std::vector<AssemblyCase>{
+            {"OpCapability VulkanMemoryModelKHR\n",
+             MakeInstruction(SpvOpCapability,
+                             {SpvCapabilityVulkanMemoryModelKHR})},
+            {"OpCapability VulkanMemoryModelDeviceScopeKHR\n",
+             MakeInstruction(SpvOpCapability,
+                             {SpvCapabilityVulkanMemoryModelDeviceScopeKHR})},
+            {"OpMemoryModel Logical VulkanKHR\n",
+             MakeInstruction(SpvOpMemoryModel, {SpvAddressingModelLogical,
+                                                SpvMemoryModelVulkanKHR})},
+            {"OpStore %1 %2 MakePointerAvailableKHR %3\n",
+             MakeInstruction(SpvOpStore,
+                             {1, 2, SpvMemoryAccessMakePointerAvailableKHRMask,
+                              3})},
+            {"OpStore %1 %2 Volatile|MakePointerAvailableKHR %3\n",
+             MakeInstruction(SpvOpStore,
+                             {1, 2,
+                              int(SpvMemoryAccessMakePointerAvailableKHRMask) |
+                                  int(SpvMemoryAccessVolatileMask),
+                              3})},
+            {"OpStore %1 %2 Aligned|MakePointerAvailableKHR 4 %3\n",
+             MakeInstruction(SpvOpStore,
+                             {1, 2,
+                              int(SpvMemoryAccessMakePointerAvailableKHRMask) |
+                                  int(SpvMemoryAccessAlignedMask),
+                              4, 3})},
+            {"OpStore %1 %2 MakePointerAvailableKHR|NonPrivatePointerKHR %3\n",
+             MakeInstruction(SpvOpStore,
+                             {1, 2,
+                              int(SpvMemoryAccessMakePointerAvailableKHRMask) |
+                                  int(SpvMemoryAccessNonPrivatePointerKHRMask),
+                              3})},
+            {"%2 = OpLoad %1 %3 MakePointerVisibleKHR %4\n",
+             MakeInstruction(SpvOpLoad,
+                             {1, 2, 3, SpvMemoryAccessMakePointerVisibleKHRMask,
+                              4})},
+            {"%2 = OpLoad %1 %3 Volatile|MakePointerVisibleKHR %4\n",
+             MakeInstruction(SpvOpLoad,
+                             {1, 2, 3,
+                              int(SpvMemoryAccessMakePointerVisibleKHRMask) |
+                                  int(SpvMemoryAccessVolatileMask),
+                              4})},
+            {"%2 = OpLoad %1 %3 Aligned|MakePointerVisibleKHR 8 %4\n",
+             MakeInstruction(SpvOpLoad,
+                             {1, 2, 3,
+                              int(SpvMemoryAccessMakePointerVisibleKHRMask) |
+                                  int(SpvMemoryAccessAlignedMask),
+                              8, 4})},
+            {"%2 = OpLoad %1 %3 MakePointerVisibleKHR|NonPrivatePointerKHR "
+             "%4\n",
+             MakeInstruction(SpvOpLoad,
+                             {1, 2, 3,
+                              int(SpvMemoryAccessMakePointerVisibleKHRMask) |
+                                  int(SpvMemoryAccessNonPrivatePointerKHRMask),
+                              4})},
+            {"OpCopyMemory %1 %2 "
+             "MakePointerAvailableKHR|"
+             "MakePointerVisibleKHR|"
+             "NonPrivatePointerKHR "
+             "%3 %4\n",
+             MakeInstruction(SpvOpCopyMemory,
+                             {1, 2,
+                              (int(SpvMemoryAccessMakePointerVisibleKHRMask) |
+                               int(SpvMemoryAccessMakePointerAvailableKHRMask) |
+                               int(SpvMemoryAccessNonPrivatePointerKHRMask)),
+                              3, 4})},
+            {"OpCopyMemorySized %1 %2 %3 "
+             "MakePointerAvailableKHR|"
+             "MakePointerVisibleKHR|"
+             "NonPrivatePointerKHR "
+             "%4 %5\n",
+             MakeInstruction(SpvOpCopyMemorySized,
+                             {1, 2, 3,
+                              (int(SpvMemoryAccessMakePointerVisibleKHRMask) |
+                               int(SpvMemoryAccessMakePointerAvailableKHRMask) |
+                               int(SpvMemoryAccessNonPrivatePointerKHRMask)),
+                              4, 5})},
+            // Image operands
+            {"OpImageWrite %1 %2 %3 MakeTexelAvailableKHR "
+             "%4\n",
+             MakeInstruction(
+                 SpvOpImageWrite,
+                 {1, 2, 3, int(SpvImageOperandsMakeTexelAvailableKHRMask), 4})},
+            {"OpImageWrite %1 %2 %3 MakeTexelAvailableKHR|NonPrivateTexelKHR "
+             "%4\n",
+             MakeInstruction(SpvOpImageWrite,
+                             {1, 2, 3,
+                              int(SpvImageOperandsMakeTexelAvailableKHRMask) |
+                                  int(SpvImageOperandsNonPrivateTexelKHRMask),
+                              4})},
+            {"OpImageWrite %1 %2 %3 "
+             "MakeTexelAvailableKHR|NonPrivateTexelKHR|VolatileTexelKHR "
+             "%4\n",
+             MakeInstruction(SpvOpImageWrite,
+                             {1, 2, 3,
+                              int(SpvImageOperandsMakeTexelAvailableKHRMask) |
+                                  int(SpvImageOperandsNonPrivateTexelKHRMask) |
+                                  int(SpvImageOperandsVolatileTexelKHRMask),
+                              4})},
+            {"%2 = OpImageRead %1 %3 %4 MakeTexelVisibleKHR "
+             "%5\n",
+             MakeInstruction(SpvOpImageRead,
+                             {1, 2, 3, 4,
+                              int(SpvImageOperandsMakeTexelVisibleKHRMask),
+                              5})},
+            {"%2 = OpImageRead %1 %3 %4 "
+             "MakeTexelVisibleKHR|NonPrivateTexelKHR "
+             "%5\n",
+             MakeInstruction(SpvOpImageRead,
+                             {1, 2, 3, 4,
+                              int(SpvImageOperandsMakeTexelVisibleKHRMask) |
+                                  int(SpvImageOperandsNonPrivateTexelKHRMask),
+                              5})},
+            {"%2 = OpImageRead %1 %3 %4 "
+             "MakeTexelVisibleKHR|NonPrivateTexelKHR|VolatileTexelKHR "
+             "%5\n",
+             MakeInstruction(SpvOpImageRead,
+                             {1, 2, 3, 4,
+                              int(SpvImageOperandsMakeTexelVisibleKHRMask) |
+                                  int(SpvImageOperandsNonPrivateTexelKHRMask) |
+                                  int(SpvImageOperandsVolatileTexelKHRMask),
+                              5})},
+
+            // Memory semantics ID values are numbers put into a SPIR-V
+            // constant integer referenced by Id. There is no token for
+            // them, and so no assembler or disassembler support required.
+            // Similar for Scope ID.
+        })), );
+
 // SPV_GOOGLE_decorate_string
 
 INSTANTIATE_TEST_CASE_P(