Add Vulkan-specific validation rules for atomics
authorAndrey Tuganov <andreyt@google.com>
Tue, 6 Feb 2018 17:10:11 +0000 (12:10 -0500)
committerDavid Neto <dneto@google.com>
Wed, 7 Feb 2018 18:31:35 +0000 (13:31 -0500)
Added atomic instructions validation rules from
https://www.khronos.org/registry/vulkan/specs/1.0/html/vkspec.html#spirvenv-module-validation

source/val/validation_state.cpp
source/val/validation_state.h
source/validate_atomics.cpp
source/validate_barriers.cpp
test/val/val_atomics_test.cpp

index b76620e..ed4cd70 100644 (file)
@@ -766,4 +766,22 @@ bool ValidationState_t::GetConstantValUint64(uint32_t id, uint64_t* val) const {
   return true;
 }
 
+std::tuple<bool, bool, uint32_t> ValidationState_t::EvalInt32IfConst(
+    uint32_t id) {
+  const Instruction* const inst = FindDef(id);
+  assert(inst);
+  const uint32_t type = inst->type_id();
+
+  if (!IsIntScalarType(type) || GetBitWidth(type) != 32) {
+    return std::make_tuple(false, false, 0);
+  }
+
+  if (inst->opcode() != SpvOpConstant && inst->opcode() != SpvOpSpecConstant) {
+    return std::make_tuple(true, false, 0);
+  }
+
+  assert(inst->words().size() == 4);
+  return std::make_tuple(true, true, inst->word(3));
+}
+
 }  // namespace libspirv
index 986d759..d63ac02 100644 (file)
@@ -18,6 +18,7 @@
 #include <deque>
 #include <set>
 #include <string>
+#include <tuple>
 #include <unordered_map>
 #include <unordered_set>
 #include <vector>
@@ -413,6 +414,10 @@ class ValidationState_t {
   bool GetPointerTypeInfo(uint32_t id, uint32_t* data_type,
                           uint32_t* storage_class) const;
 
+  // Tries to evaluate a 32-bit signed or unsigned scalar integer constant.
+  // Returns tuple <is_int32, is_const_int32, value>.
+  std::tuple<bool, bool, uint32_t> EvalInt32IfConst(uint32_t id);
+
  private:
   ValidationState_t(const ValidationState_t&);
 
index 1254b73..108784b 100644 (file)
 
 #include "diagnostic.h"
 #include "opcode.h"
+#include "spirv_target_env.h"
 #include "util/bitutils.h"
 #include "val/instruction.h"
 #include "val/validation_state.h"
 
 namespace libspirv {
 
+// Validates Memory Scope operand.
+spv_result_t ValidateMemoryScope(ValidationState_t& _,
+                                 const spv_parsed_instruction_t* inst,
+                                 uint32_t id) {
+  const SpvOp opcode = static_cast<SpvOp>(inst->opcode);
+  bool is_int32 = false, is_const_int32 = false;
+  uint32_t value = 0;
+  std::tie(is_int32, is_const_int32, value) = _.EvalInt32IfConst(id);
+
+  if (!is_int32) {
+    return _.diag(SPV_ERROR_INVALID_DATA)
+           << spvOpcodeString(opcode) << ": expected Scope to be 32-bit int";
+  }
+
+  if (!is_const_int32) {
+    return SPV_SUCCESS;
+  }
+
+  if (spvIsVulkanEnv(_.context()->target_env)) {
+    if (value != SpvScopeDevice && value != SpvScopeWorkgroup &&
+        value != SpvScopeInvocation) {
+      return _.diag(SPV_ERROR_INVALID_DATA)
+             << spvOpcodeString(opcode)
+             << ": in Vulkan environment memory scope is limited to Device, "
+                "Workgroup and Invocation";
+    }
+  }
+
+  // TODO(atgoo@github.com) Add checks for OpenCL and OpenGL environments.
+
+  return SPV_SUCCESS;
+}
+
 // Validates a Memory Semantics operand.
 spv_result_t ValidateMemorySemantics(ValidationState_t& _,
                                      const spv_parsed_instruction_t* inst,
                                      uint32_t operand_index) {
   const SpvOp opcode = static_cast<SpvOp>(inst->opcode);
-  const uint32_t memory_semantics =
+  bool is_int32 = false, is_const_int32 = false;
+  uint32_t flags = 0;
+  const uint32_t memory_semantics_id =
       inst->words[inst->operands[operand_index].offset];
-  const Instruction* const memory_semantics_inst = _.FindDef(memory_semantics);
-  assert(memory_semantics_inst);
-  const uint32_t memory_semantics_type = memory_semantics_inst->type_id();
+  std::tie(is_int32, is_const_int32, flags) =
+      _.EvalInt32IfConst(memory_semantics_id);
 
-  if (!_.IsIntScalarType(memory_semantics_type) ||
-      _.GetBitWidth(memory_semantics_type) != 32) {
+  if (!is_int32) {
     return _.diag(SPV_ERROR_INVALID_DATA)
            << spvOpcodeString(opcode)
            << ": expected Memory Semantics to be 32-bit int";
   }
 
-  if (memory_semantics_inst->opcode() != SpvOpConstant &&
-      memory_semantics_inst->opcode() != SpvOpSpecConstant) {
+  if (!is_const_int32) {
     return SPV_SUCCESS;
   }
 
-  assert(memory_semantics_inst->words().size() == 4);
-  const uint32_t flags = memory_semantics_inst->word(3);
-
   if (spvutils::CountSetBits(
           flags &
           (SpvMemorySemanticsAcquireMask | SpvMemorySemanticsReleaseMask |
@@ -94,6 +124,28 @@ spv_result_t ValidateMemorySemantics(ValidationState_t& _,
               "for operand Unequal";
   }
 
+  if (spvIsVulkanEnv(_.context()->target_env)) {
+    if (opcode == SpvOpAtomicLoad &&
+        (flags & SpvMemorySemanticsReleaseMask ||
+         flags & SpvMemorySemanticsAcquireReleaseMask ||
+         flags & SpvMemorySemanticsSequentiallyConsistentMask)) {
+      return _.diag(SPV_ERROR_INVALID_DATA)
+             << "Vulkan spec disallows OpAtomicLoad with Memory Semantics "
+                "Release, AcquireRelease and SequentiallyConsistent";
+    }
+
+    if (opcode == SpvOpAtomicStore &&
+        (flags & SpvMemorySemanticsAcquireMask ||
+         flags & SpvMemorySemanticsAcquireReleaseMask ||
+         flags & SpvMemorySemanticsSequentiallyConsistentMask)) {
+      return _.diag(SPV_ERROR_INVALID_DATA)
+             << "Vulkan spec disallows OpAtomicStore with Memory Semantics "
+                "Acquire, AcquireRelease and SequentiallyConsistent";
+    }
+  }
+
+  // TODO(atgoo@github.com) Add checks for OpenCL and OpenGL environments.
+
   return SPV_SUCCESS;
 }
 
@@ -199,11 +251,10 @@ spv_result_t AtomicsPass(ValidationState_t& _,
         }
       }
 
-      const uint32_t scope_type = _.GetOperandTypeId(inst, operand_index++);
-      if (!_.IsIntScalarType(scope_type) || _.GetBitWidth(scope_type) != 32) {
-        return _.diag(SPV_ERROR_INVALID_DATA)
-               << spvOpcodeString(opcode)
-               << ": expected Scope to be 32-bit int";
+      const uint32_t memory_scope =
+          inst->words[inst->operands[operand_index++].offset];
+      if (auto error = ValidateMemoryScope(_, inst, memory_scope)) {
+        return error;
       }
 
       if (auto error = ValidateMemorySemantics(_, inst, operand_index++))
index 3df5011..21834a2 100644 (file)
@@ -16,8 +16,6 @@
 
 #include "validate.h"
 
-#include <tuple>
-
 #include "diagnostic.h"
 #include "opcode.h"
 #include "spirv_target_env.h"
@@ -29,26 +27,6 @@ namespace libspirv {
 
 namespace {
 
-// Tries to evaluate a 32-bit signed or unsigned scalar integer constant.
-// Returns tuple <is_int32, is_const_int32, value>.
-std::tuple<bool, bool, uint32_t> EvalInt32IfConst(ValidationState_t& _,
-                                                  uint32_t id) {
-  const Instruction* const inst = _.FindDef(id);
-  assert(inst);
-  const uint32_t type = inst->type_id();
-
-  if (!_.IsIntScalarType(type) || _.GetBitWidth(type) != 32) {
-    return std::make_tuple(false, false, 0);
-  }
-
-  if (inst->opcode() != SpvOpConstant && inst->opcode() != SpvOpSpecConstant) {
-    return std::make_tuple(true, false, 0);
-  }
-
-  assert(inst->words().size() == 4);
-  return std::make_tuple(true, true, inst->word(3));
-}
-
 // Validates Execution Scope operand.
 spv_result_t ValidateExecutionScope(ValidationState_t& _,
                                     const spv_parsed_instruction_t* inst,
@@ -56,7 +34,7 @@ spv_result_t ValidateExecutionScope(ValidationState_t& _,
   const SpvOp opcode = static_cast<SpvOp>(inst->opcode);
   bool is_int32 = false, is_const_int32 = false;
   uint32_t value = 0;
-  std::tie(is_int32, is_const_int32, value) = EvalInt32IfConst(_, id);
+  std::tie(is_int32, is_const_int32, value) = _.EvalInt32IfConst(id);
 
   if (!is_int32) {
     return _.diag(SPV_ERROR_INVALID_DATA)
@@ -89,7 +67,7 @@ spv_result_t ValidateMemoryScope(ValidationState_t& _,
   const SpvOp opcode = static_cast<SpvOp>(inst->opcode);
   bool is_int32 = false, is_const_int32 = false;
   uint32_t value = 0;
-  std::tie(is_int32, is_const_int32, value) = EvalInt32IfConst(_, id);
+  std::tie(is_int32, is_const_int32, value) = _.EvalInt32IfConst(id);
 
   if (!is_int32) {
     return _.diag(SPV_ERROR_INVALID_DATA)
@@ -123,7 +101,7 @@ spv_result_t ValidateMemorySemantics(ValidationState_t& _,
   const SpvOp opcode = static_cast<SpvOp>(inst->opcode);
   bool is_int32 = false, is_const_int32 = false;
   uint32_t value = 0;
-  std::tie(is_int32, is_const_int32, value) = EvalInt32IfConst(_, id);
+  std::tie(is_int32, is_const_int32, value) = _.EvalInt32IfConst(id);
 
   if (!is_int32) {
     return _.diag(SPV_ERROR_INVALID_DATA)
index c2cf465..57b6d2f 100644 (file)
@@ -32,7 +32,6 @@ std::string GenerateShaderCode(
   std::ostringstream ss;
   ss << R"(
 OpCapability Shader
-OpCapability Linkage
 OpCapability Int64
 )";
 
@@ -55,7 +54,11 @@ OpEntryPoint Fragment %main "main"
 %u64_1 = OpConstant %u64 1
 %f32vec4_0000 = OpConstantComposite %f32vec4 %f32_0 %f32_0 %f32_0 %f32_0
 
-%scope = OpConstant %u32 1
+%cross_device = OpConstant %u32 0
+%device = OpConstant %u32 1
+%workgroup = OpConstant %u32 2
+%subgroup = OpConstant %u32 3
+%invocation = OpConstant %u32 4
 
 %relaxed = OpConstant %u32 0
 %acquire = OpConstant %u32 2
@@ -71,9 +74,6 @@ OpEntryPoint Fragment %main "main"
 %u32_ptr = OpTypePointer Workgroup %u32
 %u32_var = OpVariable %u32_ptr Workgroup
 
-%u64_ptr = OpTypePointer Workgroup %u64
-%u64_var = OpVariable %u64_ptr Workgroup
-
 %f32vec4_ptr = OpTypePointer Workgroup %f32vec4
 %f32vec4_var = OpVariable %f32vec4_ptr Workgroup
 
@@ -121,7 +121,11 @@ OpMemoryModel Physical32 OpenCL
 %u64_1 = OpConstant %u64 1
 %f32vec4_0000 = OpConstantComposite %f32vec4 %f32_0 %f32_0 %f32_0 %f32_0
 
-%scope = OpConstant %u32 1
+%cross_device = OpConstant %u32 0
+%device = OpConstant %u32 1
+%workgroup = OpConstant %u32 2
+%subgroup = OpConstant %u32 3
+%invocation = OpConstant %u32 4
 
 %relaxed = OpConstant %u32 0
 %acquire = OpConstant %u32 2
@@ -161,30 +165,93 @@ OpFunctionEnd)";
 
 TEST_F(ValidateAtomics, AtomicLoadShaderSuccess) {
   const std::string body = R"(
-%val1 = OpAtomicLoad %u32 %u32_var %scope %relaxed
+%val1 = OpAtomicLoad %u32 %u32_var %device %relaxed
+%val2 = OpAtomicLoad %u32 %u32_var %workgroup %acquire
+%val3 = OpAtomicLoad %u32 %u32_var %subgroup %sequentially_consistent
 )";
 
-  CompileSuccessfully(GenerateShaderCode(body).c_str());
+  CompileSuccessfully(GenerateShaderCode(body));
   ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
 }
 
 TEST_F(ValidateAtomics, AtomicLoadKernelSuccess) {
   const std::string body = R"(
-%val1 = OpAtomicLoad %f32 %f32_var %scope %relaxed
-%val2 = OpAtomicLoad %u32 %u32_var %scope %relaxed
-%val3 = OpAtomicLoad %u64 %u64_var %scope %acquire
+%val1 = OpAtomicLoad %f32 %f32_var %device %relaxed
+%val2 = OpAtomicLoad %u32 %u32_var %workgroup %sequentially_consistent
+%val3 = OpAtomicLoad %u64 %u64_var %subgroup %acquire
 )";
 
-  CompileSuccessfully(GenerateKernelCode(body).c_str());
+  CompileSuccessfully(GenerateKernelCode(body));
   ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
 }
 
+TEST_F(ValidateAtomics, AtomicLoadVulkanSuccess) {
+  const std::string body = R"(
+%val1 = OpAtomicLoad %u32 %u32_var %device %relaxed
+%val2 = OpAtomicLoad %u32 %u32_var %workgroup %acquire
+)";
+
+  CompileSuccessfully(GenerateShaderCode(body), SPV_ENV_VULKAN_1_0);
+  ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_0));
+}
+
+TEST_F(ValidateAtomics, AtomicLoadVulkanSubgroup) {
+  const std::string body = R"(
+%val1 = OpAtomicLoad %u32 %u32_var %subgroup %acquire
+)";
+
+  CompileSuccessfully(GenerateShaderCode(body), SPV_ENV_VULKAN_1_0);
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("AtomicLoad: in Vulkan environment memory scope is "
+                        "limited to Device, Workgroup and Invocation"));
+}
+
+TEST_F(ValidateAtomics, AtomicLoadVulkanRelease) {
+  const std::string body = R"(
+%val1 = OpAtomicLoad %u32 %u32_var %workgroup %release
+)";
+
+  CompileSuccessfully(GenerateShaderCode(body), SPV_ENV_VULKAN_1_0);
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0));
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("Vulkan spec disallows OpAtomicLoad with Memory Semantics "
+                "Release, AcquireRelease and SequentiallyConsistent"));
+}
+
+TEST_F(ValidateAtomics, AtomicLoadVulkanAcquireRelease) {
+  const std::string body = R"(
+%val1 = OpAtomicLoad %u32 %u32_var %workgroup %acquire_release
+)";
+
+  CompileSuccessfully(GenerateShaderCode(body), SPV_ENV_VULKAN_1_0);
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0));
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("Vulkan spec disallows OpAtomicLoad with Memory Semantics "
+                "Release, AcquireRelease and SequentiallyConsistent"));
+}
+
+TEST_F(ValidateAtomics, AtomicLoadVulkanSequentiallyConsistent) {
+  const std::string body = R"(
+%val1 = OpAtomicLoad %u32 %u32_var %workgroup %sequentially_consistent
+)";
+
+  CompileSuccessfully(GenerateShaderCode(body), SPV_ENV_VULKAN_1_0);
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0));
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("Vulkan spec disallows OpAtomicLoad with Memory Semantics "
+                "Release, AcquireRelease and SequentiallyConsistent"));
+}
+
 TEST_F(ValidateAtomics, AtomicLoadShaderFloat) {
   const std::string body = R"(
-%val1 = OpAtomicLoad %f32 %f32_var %scope %relaxed
+%val1 = OpAtomicLoad %f32 %f32_var %device %relaxed
 )";
 
-  CompileSuccessfully(GenerateShaderCode(body).c_str());
+  CompileSuccessfully(GenerateShaderCode(body));
   ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
   EXPECT_THAT(getDiagnosticString(),
               HasSubstr("AtomicLoad: "
@@ -193,10 +260,10 @@ TEST_F(ValidateAtomics, AtomicLoadShaderFloat) {
 
 TEST_F(ValidateAtomics, AtomicLoadWrongResultType) {
   const std::string body = R"(
-%val1 = OpAtomicLoad %f32vec4 %f32vec4_var %scope %relaxed
+%val1 = OpAtomicLoad %f32vec4 %f32vec4_var %device %relaxed
 )";
 
-  CompileSuccessfully(GenerateKernelCode(body).c_str());
+  CompileSuccessfully(GenerateKernelCode(body));
   ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
   EXPECT_THAT(getDiagnosticString(),
               HasSubstr("AtomicLoad: "
@@ -205,10 +272,10 @@ TEST_F(ValidateAtomics, AtomicLoadWrongResultType) {
 
 TEST_F(ValidateAtomics, AtomicLoadWrongPointerType) {
   const std::string body = R"(
-%val1 = OpAtomicLoad %f32 %f32_ptr %scope %relaxed
+%val1 = OpAtomicLoad %f32 %f32_ptr %device %relaxed
 )";
 
-  CompileSuccessfully(GenerateKernelCode(body).c_str());
+  CompileSuccessfully(GenerateKernelCode(body));
   ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
   EXPECT_THAT(
       getDiagnosticString(),
@@ -217,10 +284,10 @@ TEST_F(ValidateAtomics, AtomicLoadWrongPointerType) {
 
 TEST_F(ValidateAtomics, AtomicLoadWrongPointerDataType) {
   const std::string body = R"(
-%val1 = OpAtomicLoad %u32 %f32_var %scope %relaxed
+%val1 = OpAtomicLoad %u32 %f32_var %device %relaxed
 )";
 
-  CompileSuccessfully(GenerateKernelCode(body).c_str());
+  CompileSuccessfully(GenerateKernelCode(body));
   ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
   EXPECT_THAT(
       getDiagnosticString(),
@@ -233,7 +300,7 @@ TEST_F(ValidateAtomics, AtomicLoadWrongScopeType) {
 %val1 = OpAtomicLoad %f32 %f32_var %f32_1 %relaxed
 )";
 
-  CompileSuccessfully(GenerateKernelCode(body).c_str());
+  CompileSuccessfully(GenerateKernelCode(body));
   ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
   EXPECT_THAT(getDiagnosticString(),
               HasSubstr("AtomicLoad: expected Scope to be 32-bit int"));
@@ -241,32 +308,90 @@ TEST_F(ValidateAtomics, AtomicLoadWrongScopeType) {
 
 TEST_F(ValidateAtomics, AtomicLoadWrongMemorySemanticsType) {
   const std::string body = R"(
-%val1 = OpAtomicLoad %f32 %f32_var %scope %u64_1
+%val1 = OpAtomicLoad %f32 %f32_var %device %u64_1
 )";
 
-  CompileSuccessfully(GenerateKernelCode(body).c_str());
+  CompileSuccessfully(GenerateKernelCode(body));
   ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
   EXPECT_THAT(
       getDiagnosticString(),
       HasSubstr("AtomicLoad: expected Memory Semantics to be 32-bit int"));
 }
 
-TEST_F(ValidateAtomics, AtomicStoreSuccess) {
+TEST_F(ValidateAtomics, AtomicStoreKernelSuccess) {
   const std::string body = R"(
-OpAtomicStore %f32_var %scope %relaxed %f32_1
-%val2 = OpAtomicStore %u32_var %scope %relaxed %u32_1
+OpAtomicStore %f32_var %device %relaxed %f32_1
+OpAtomicStore %u32_var %subgroup %release %u32_1
 )";
 
-  CompileSuccessfully(GenerateKernelCode(body).c_str());
+  CompileSuccessfully(GenerateKernelCode(body));
   ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
 }
 
+TEST_F(ValidateAtomics, AtomicStoreShaderSuccess) {
+  const std::string body = R"(
+OpAtomicStore %u32_var %device %release %u32_1
+OpAtomicStore %u32_var %subgroup %sequentially_consistent %u32_1
+)";
+
+  CompileSuccessfully(GenerateShaderCode(body));
+  ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+TEST_F(ValidateAtomics, AtomicStoreVulkanSuccess) {
+  const std::string body = R"(
+OpAtomicStore %u32_var %device %release %u32_1
+)";
+
+  CompileSuccessfully(GenerateShaderCode(body), SPV_ENV_VULKAN_1_0);
+  ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_0));
+}
+
+TEST_F(ValidateAtomics, AtomicStoreVulkanAcquire) {
+  const std::string body = R"(
+OpAtomicStore %u32_var %device %acquire %u32_1
+)";
+
+  CompileSuccessfully(GenerateShaderCode(body), SPV_ENV_VULKAN_1_0);
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0));
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("Vulkan spec disallows OpAtomicStore with Memory Semantics "
+                "Acquire, AcquireRelease and SequentiallyConsistent"));
+}
+
+TEST_F(ValidateAtomics, AtomicStoreVulkanAcquireRelease) {
+  const std::string body = R"(
+OpAtomicStore %u32_var %device %acquire_release %u32_1
+)";
+
+  CompileSuccessfully(GenerateShaderCode(body), SPV_ENV_VULKAN_1_0);
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0));
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("Vulkan spec disallows OpAtomicStore with Memory Semantics "
+                "Acquire, AcquireRelease and SequentiallyConsistent"));
+}
+
+TEST_F(ValidateAtomics, AtomicStoreVulkanSequentiallyConsistent) {
+  const std::string body = R"(
+OpAtomicStore %u32_var %device %sequentially_consistent %u32_1
+)";
+
+  CompileSuccessfully(GenerateShaderCode(body), SPV_ENV_VULKAN_1_0);
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0));
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("Vulkan spec disallows OpAtomicStore with Memory Semantics "
+                "Acquire, AcquireRelease and SequentiallyConsistent"));
+}
+
 TEST_F(ValidateAtomics, AtomicStoreWrongPointerType) {
   const std::string body = R"(
-OpAtomicStore %f32_1 %scope %relaxed %f32_1
+OpAtomicStore %f32_1 %device %relaxed %f32_1
 )";
 
-  CompileSuccessfully(GenerateKernelCode(body).c_str());
+  CompileSuccessfully(GenerateKernelCode(body));
   ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
   EXPECT_THAT(
       getDiagnosticString(),
@@ -275,10 +400,10 @@ OpAtomicStore %f32_1 %scope %relaxed %f32_1
 
 TEST_F(ValidateAtomics, AtomicStoreWrongPointerDataType) {
   const std::string body = R"(
-OpAtomicStore %f32vec4_var %scope %relaxed %f32_1
+OpAtomicStore %f32vec4_var %device %relaxed %f32_1
 )";
 
-  CompileSuccessfully(GenerateKernelCode(body).c_str());
+  CompileSuccessfully(GenerateKernelCode(body));
   ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
   EXPECT_THAT(
       getDiagnosticString(),
@@ -290,10 +415,10 @@ OpAtomicStore %f32vec4_var %scope %relaxed %f32_1
 TEST_F(ValidateAtomics, AtomicStoreWrongPointerStorageType) {
   const std::string body = R"(
 %f32_var_function = OpVariable %f32_ptr_function Function
-OpAtomicStore %f32_var_function %scope %relaxed %f32_1
+OpAtomicStore %f32_var_function %device %relaxed %f32_1
 )";
 
-  CompileSuccessfully(GenerateKernelCode(body).c_str());
+  CompileSuccessfully(GenerateKernelCode(body));
   ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
   EXPECT_THAT(
       getDiagnosticString(),
@@ -307,7 +432,7 @@ TEST_F(ValidateAtomics, AtomicStoreWrongScopeType) {
 OpAtomicStore %f32_var %f32_1 %relaxed %f32_1
 )";
 
-  CompileSuccessfully(GenerateKernelCode(body).c_str());
+  CompileSuccessfully(GenerateKernelCode(body));
   ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
   EXPECT_THAT(getDiagnosticString(),
               HasSubstr("AtomicStore: expected Scope to be 32-bit int"));
@@ -315,10 +440,10 @@ OpAtomicStore %f32_var %f32_1 %relaxed %f32_1
 
 TEST_F(ValidateAtomics, AtomicStoreWrongMemorySemanticsType) {
   const std::string body = R"(
-OpAtomicStore %f32_var %scope %f32_1 %f32_1
+OpAtomicStore %f32_var %device %f32_1 %f32_1
 )";
 
-  CompileSuccessfully(GenerateKernelCode(body).c_str());
+  CompileSuccessfully(GenerateKernelCode(body));
   ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
   EXPECT_THAT(
       getDiagnosticString(),
@@ -327,10 +452,10 @@ OpAtomicStore %f32_var %scope %f32_1 %f32_1
 
 TEST_F(ValidateAtomics, AtomicStoreWrongValueType) {
   const std::string body = R"(
-OpAtomicStore %f32_var %scope %relaxed %u32_1
+OpAtomicStore %f32_var %device %relaxed %u32_1
 )";
 
-  CompileSuccessfully(GenerateKernelCode(body).c_str());
+  CompileSuccessfully(GenerateKernelCode(body));
   ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
   EXPECT_THAT(
       getDiagnosticString(),
@@ -341,33 +466,33 @@ OpAtomicStore %f32_var %scope %relaxed %u32_1
 
 TEST_F(ValidateAtomics, AtomicExchangeShaderSuccess) {
   const std::string body = R"(
-%val1 = OpAtomicStore %u32_var %scope %relaxed %u32_1
-%val2 = OpAtomicExchange %u32 %u32_var %scope %relaxed %u32_0
+%val1 = OpAtomicStore %u32_var %device %relaxed %u32_1
+%val2 = OpAtomicExchange %u32 %u32_var %device %relaxed %u32_0
 )";
 
-  CompileSuccessfully(GenerateShaderCode(body).c_str());
+  CompileSuccessfully(GenerateShaderCode(body));
   ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
 }
 
 TEST_F(ValidateAtomics, AtomicExchangeKernelSuccess) {
   const std::string body = R"(
-OpAtomicStore %f32_var %scope %relaxed %f32_1
-%val2 = OpAtomicExchange %f32 %f32_var %scope %relaxed %f32_0
-%val3 = OpAtomicStore %u32_var %scope %relaxed %u32_1
-%val4 = OpAtomicExchange %u32 %u32_var %scope %relaxed %u32_0
+OpAtomicStore %f32_var %device %relaxed %f32_1
+%val2 = OpAtomicExchange %f32 %f32_var %device %relaxed %f32_0
+%val3 = OpAtomicStore %u32_var %device %relaxed %u32_1
+%val4 = OpAtomicExchange %u32 %u32_var %device %relaxed %u32_0
 )";
 
-  CompileSuccessfully(GenerateKernelCode(body).c_str());
+  CompileSuccessfully(GenerateKernelCode(body));
   ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
 }
 
 TEST_F(ValidateAtomics, AtomicExchangeShaderFloat) {
   const std::string body = R"(
-OpAtomicStore %f32_var %scope %relaxed %f32_1
-%val2 = OpAtomicExchange %f32 %f32_var %scope %relaxed %f32_0
+OpAtomicStore %f32_var %device %relaxed %f32_1
+%val2 = OpAtomicExchange %f32 %f32_var %device %relaxed %f32_0
 )";
 
-  CompileSuccessfully(GenerateShaderCode(body).c_str());
+  CompileSuccessfully(GenerateShaderCode(body));
   ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
   EXPECT_THAT(getDiagnosticString(),
               HasSubstr("AtomicExchange: "
@@ -377,10 +502,10 @@ OpAtomicStore %f32_var %scope %relaxed %f32_1
 TEST_F(ValidateAtomics, AtomicExchangeWrongResultType) {
   const std::string body = R"(
 %val1 = OpStore %f32vec4_var %f32vec4_0000
-%val2 = OpAtomicExchange %f32vec4 %f32vec4_var %scope %relaxed %f32vec4_0000
+%val2 = OpAtomicExchange %f32vec4 %f32vec4_var %device %relaxed %f32vec4_0000
 )";
 
-  CompileSuccessfully(GenerateKernelCode(body).c_str());
+  CompileSuccessfully(GenerateKernelCode(body));
   ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
   EXPECT_THAT(getDiagnosticString(),
               HasSubstr("AtomicExchange: "
@@ -389,10 +514,10 @@ TEST_F(ValidateAtomics, AtomicExchangeWrongResultType) {
 
 TEST_F(ValidateAtomics, AtomicExchangeWrongPointerType) {
   const std::string body = R"(
-%val2 = OpAtomicExchange %f32 %f32vec4_ptr %scope %relaxed %f32vec4_0000
+%val2 = OpAtomicExchange %f32 %f32vec4_ptr %device %relaxed %f32vec4_0000
 )";
 
-  CompileSuccessfully(GenerateKernelCode(body).c_str());
+  CompileSuccessfully(GenerateKernelCode(body));
   ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
   EXPECT_THAT(
       getDiagnosticString(),
@@ -403,10 +528,10 @@ TEST_F(ValidateAtomics, AtomicExchangeWrongPointerType) {
 TEST_F(ValidateAtomics, AtomicExchangeWrongPointerDataType) {
   const std::string body = R"(
 %val1 = OpStore %f32vec4_var %f32vec4_0000
-%val2 = OpAtomicExchange %f32 %f32vec4_var %scope %relaxed %f32vec4_0000
+%val2 = OpAtomicExchange %f32 %f32vec4_var %device %relaxed %f32vec4_0000
 )";
 
-  CompileSuccessfully(GenerateKernelCode(body).c_str());
+  CompileSuccessfully(GenerateKernelCode(body));
   ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
   EXPECT_THAT(
       getDiagnosticString(),
@@ -416,11 +541,11 @@ TEST_F(ValidateAtomics, AtomicExchangeWrongPointerDataType) {
 
 TEST_F(ValidateAtomics, AtomicExchangeWrongScopeType) {
   const std::string body = R"(
-OpAtomicStore %f32_var %scope %relaxed %f32_1
+OpAtomicStore %f32_var %device %relaxed %f32_1
 %val2 = OpAtomicExchange %f32 %f32_var %f32_1 %relaxed %f32_0
 )";
 
-  CompileSuccessfully(GenerateKernelCode(body).c_str());
+  CompileSuccessfully(GenerateKernelCode(body));
   ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
   EXPECT_THAT(getDiagnosticString(),
               HasSubstr("AtomicExchange: expected Scope to be 32-bit int"));
@@ -428,11 +553,11 @@ OpAtomicStore %f32_var %scope %relaxed %f32_1
 
 TEST_F(ValidateAtomics, AtomicExchangeWrongMemorySemanticsType) {
   const std::string body = R"(
-OpAtomicStore %f32_var %scope %relaxed %f32_1
-%val2 = OpAtomicExchange %f32 %f32_var %scope %f32_1 %f32_0
+OpAtomicStore %f32_var %device %relaxed %f32_1
+%val2 = OpAtomicExchange %f32 %f32_var %device %f32_1 %f32_0
 )";
 
-  CompileSuccessfully(GenerateKernelCode(body).c_str());
+  CompileSuccessfully(GenerateKernelCode(body));
   ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
   EXPECT_THAT(
       getDiagnosticString(),
@@ -441,11 +566,11 @@ OpAtomicStore %f32_var %scope %relaxed %f32_1
 
 TEST_F(ValidateAtomics, AtomicExchangeWrongValueType) {
   const std::string body = R"(
-OpAtomicStore %f32_var %scope %relaxed %f32_1
-%val2 = OpAtomicExchange %f32 %f32_var %scope %relaxed %u32_0
+OpAtomicStore %f32_var %device %relaxed %f32_1
+%val2 = OpAtomicExchange %f32 %f32_var %device %relaxed %u32_0
 )";
 
-  CompileSuccessfully(GenerateKernelCode(body).c_str());
+  CompileSuccessfully(GenerateKernelCode(body));
   ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
   EXPECT_THAT(getDiagnosticString(),
               HasSubstr("AtomicExchange: "
@@ -454,33 +579,33 @@ OpAtomicStore %f32_var %scope %relaxed %f32_1
 
 TEST_F(ValidateAtomics, AtomicCompareExchangeShaderSuccess) {
   const std::string body = R"(
-%val1 = OpAtomicStore %u32_var %scope %relaxed %u32_1
-%val2 = OpAtomicCompareExchange %u32 %u32_var %scope %relaxed %relaxed %u32_0 %u32_0
+%val1 = OpAtomicStore %u32_var %device %relaxed %u32_1
+%val2 = OpAtomicCompareExchange %u32 %u32_var %device %relaxed %relaxed %u32_0 %u32_0
 )";
 
-  CompileSuccessfully(GenerateShaderCode(body).c_str());
+  CompileSuccessfully(GenerateShaderCode(body));
   ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
 }
 
 TEST_F(ValidateAtomics, AtomicCompareExchangeKernelSuccess) {
   const std::string body = R"(
-OpAtomicStore %f32_var %scope %relaxed %f32_1
-%val2 = OpAtomicCompareExchange %f32 %f32_var %scope %relaxed %relaxed %f32_0 %f32_1
-%val3 = OpAtomicStore %u32_var %scope %relaxed %u32_1
-%val4 = OpAtomicCompareExchange %u32 %u32_var %scope %relaxed %relaxed %u32_0 %u32_0
+OpAtomicStore %f32_var %device %relaxed %f32_1
+%val2 = OpAtomicCompareExchange %f32 %f32_var %device %relaxed %relaxed %f32_0 %f32_1
+%val3 = OpAtomicStore %u32_var %device %relaxed %u32_1
+%val4 = OpAtomicCompareExchange %u32 %u32_var %device %relaxed %relaxed %u32_0 %u32_0
 )";
 
-  CompileSuccessfully(GenerateKernelCode(body).c_str());
+  CompileSuccessfully(GenerateKernelCode(body));
   ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
 }
 
 TEST_F(ValidateAtomics, AtomicCompareExchangeShaderFloat) {
   const std::string body = R"(
-OpAtomicStore %f32_var %scope %relaxed %f32_1
-%val1 = OpAtomicCompareExchange %f32 %f32_var %scope %relaxed %relaxed %f32_0 %f32_1
+OpAtomicStore %f32_var %device %relaxed %f32_1
+%val1 = OpAtomicCompareExchange %f32 %f32_var %device %relaxed %relaxed %f32_0 %f32_1
 )";
 
-  CompileSuccessfully(GenerateShaderCode(body).c_str());
+  CompileSuccessfully(GenerateShaderCode(body));
   ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
   EXPECT_THAT(getDiagnosticString(),
               HasSubstr("AtomicCompareExchange: "
@@ -490,10 +615,10 @@ OpAtomicStore %f32_var %scope %relaxed %f32_1
 TEST_F(ValidateAtomics, AtomicCompareExchangeWrongResultType) {
   const std::string body = R"(
 %val1 = OpStore %f32vec4_var %f32vec4_0000
-%val2 = OpAtomicCompareExchange %f32vec4 %f32vec4_var %scope %relaxed %relaxed %f32vec4_0000 %f32vec4_0000
+%val2 = OpAtomicCompareExchange %f32vec4 %f32vec4_var %device %relaxed %relaxed %f32vec4_0000 %f32vec4_0000
 )";
 
-  CompileSuccessfully(GenerateKernelCode(body).c_str());
+  CompileSuccessfully(GenerateKernelCode(body));
   ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
   EXPECT_THAT(getDiagnosticString(),
               HasSubstr("AtomicCompareExchange: "
@@ -502,10 +627,10 @@ TEST_F(ValidateAtomics, AtomicCompareExchangeWrongResultType) {
 
 TEST_F(ValidateAtomics, AtomicCompareExchangeWrongPointerType) {
   const std::string body = R"(
-%val2 = OpAtomicCompareExchange %f32 %f32vec4_ptr %scope %relaxed %relaxed %f32vec4_0000 %f32vec4_0000
+%val2 = OpAtomicCompareExchange %f32 %f32vec4_ptr %device %relaxed %relaxed %f32vec4_0000 %f32vec4_0000
 )";
 
-  CompileSuccessfully(GenerateKernelCode(body).c_str());
+  CompileSuccessfully(GenerateKernelCode(body));
   ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
   EXPECT_THAT(getDiagnosticString(),
               HasSubstr("AtomicCompareExchange: expected Pointer to be of type "
@@ -515,10 +640,10 @@ TEST_F(ValidateAtomics, AtomicCompareExchangeWrongPointerType) {
 TEST_F(ValidateAtomics, AtomicCompareExchangeWrongPointerDataType) {
   const std::string body = R"(
 %val1 = OpStore %f32vec4_var %f32vec4_0000
-%val2 = OpAtomicCompareExchange %f32 %f32vec4_var %scope %relaxed %relaxed %f32_0 %f32_1
+%val2 = OpAtomicCompareExchange %f32 %f32vec4_var %device %relaxed %relaxed %f32_0 %f32_1
 )";
 
-  CompileSuccessfully(GenerateKernelCode(body).c_str());
+  CompileSuccessfully(GenerateKernelCode(body));
   ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
   EXPECT_THAT(
       getDiagnosticString(),
@@ -528,11 +653,11 @@ TEST_F(ValidateAtomics, AtomicCompareExchangeWrongPointerDataType) {
 
 TEST_F(ValidateAtomics, AtomicCompareExchangeWrongScopeType) {
   const std::string body = R"(
-OpAtomicStore %f32_var %scope %relaxed %f32_1
+OpAtomicStore %f32_var %device %relaxed %f32_1
 %val2 = OpAtomicCompareExchange %f32 %f32_var %f32_1 %relaxed %relaxed %f32_0 %f32_0
 )";
 
-  CompileSuccessfully(GenerateKernelCode(body).c_str());
+  CompileSuccessfully(GenerateKernelCode(body));
   ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
   EXPECT_THAT(
       getDiagnosticString(),
@@ -541,11 +666,11 @@ OpAtomicStore %f32_var %scope %relaxed %f32_1
 
 TEST_F(ValidateAtomics, AtomicCompareExchangeWrongMemorySemanticsType1) {
   const std::string body = R"(
-OpAtomicStore %f32_var %scope %relaxed %f32_1
-%val2 = OpAtomicCompareExchange %f32 %f32_var %scope %f32_1 %relaxed %f32_0 %f32_0
+OpAtomicStore %f32_var %device %relaxed %f32_1
+%val2 = OpAtomicCompareExchange %f32 %f32_var %device %f32_1 %relaxed %f32_0 %f32_0
 )";
 
-  CompileSuccessfully(GenerateKernelCode(body).c_str());
+  CompileSuccessfully(GenerateKernelCode(body));
   ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
   EXPECT_THAT(
       getDiagnosticString(),
@@ -555,11 +680,11 @@ OpAtomicStore %f32_var %scope %relaxed %f32_1
 
 TEST_F(ValidateAtomics, AtomicCompareExchangeWrongMemorySemanticsType2) {
   const std::string body = R"(
-OpAtomicStore %f32_var %scope %relaxed %f32_1
-%val2 = OpAtomicCompareExchange %f32 %f32_var %scope %relaxed %f32_1 %f32_0 %f32_0
+OpAtomicStore %f32_var %device %relaxed %f32_1
+%val2 = OpAtomicCompareExchange %f32 %f32_var %device %relaxed %f32_1 %f32_0 %f32_0
 )";
 
-  CompileSuccessfully(GenerateKernelCode(body).c_str());
+  CompileSuccessfully(GenerateKernelCode(body));
   ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
   EXPECT_THAT(
       getDiagnosticString(),
@@ -569,11 +694,11 @@ OpAtomicStore %f32_var %scope %relaxed %f32_1
 
 TEST_F(ValidateAtomics, AtomicCompareExchangeUnequalRelease) {
   const std::string body = R"(
-OpAtomicStore %f32_var %scope %relaxed %f32_1
-%val2 = OpAtomicCompareExchange %f32 %f32_var %scope %relaxed %release %f32_0 %f32_0
+OpAtomicStore %f32_var %device %relaxed %f32_1
+%val2 = OpAtomicCompareExchange %f32 %f32_var %device %relaxed %release %f32_0 %f32_0
 )";
 
-  CompileSuccessfully(GenerateKernelCode(body).c_str());
+  CompileSuccessfully(GenerateKernelCode(body));
   ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
   EXPECT_THAT(getDiagnosticString(),
               HasSubstr("AtomicCompareExchange: Memory Semantics Release and "
@@ -582,11 +707,11 @@ OpAtomicStore %f32_var %scope %relaxed %f32_1
 
 TEST_F(ValidateAtomics, AtomicCompareExchangeWrongValueType) {
   const std::string body = R"(
-OpAtomicStore %f32_var %scope %relaxed %f32_1
-%val2 = OpAtomicCompareExchange %f32 %f32_var %scope %relaxed %relaxed %u32_0 %f32_1
+OpAtomicStore %f32_var %device %relaxed %f32_1
+%val2 = OpAtomicCompareExchange %f32 %f32_var %device %relaxed %relaxed %u32_0 %f32_1
 )";
 
-  CompileSuccessfully(GenerateKernelCode(body).c_str());
+  CompileSuccessfully(GenerateKernelCode(body));
   ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
   EXPECT_THAT(getDiagnosticString(),
               HasSubstr("AtomicCompareExchange: "
@@ -595,11 +720,11 @@ OpAtomicStore %f32_var %scope %relaxed %f32_1
 
 TEST_F(ValidateAtomics, AtomicCompareExchangeWrongComparatorType) {
   const std::string body = R"(
-OpAtomicStore %f32_var %scope %relaxed %f32_1
-%val2 = OpAtomicCompareExchange %f32 %f32_var %scope %relaxed %relaxed %f32_0 %u32_1
+OpAtomicStore %f32_var %device %relaxed %f32_1
+%val2 = OpAtomicCompareExchange %f32 %f32_var %device %relaxed %relaxed %f32_0 %u32_1
 )";
 
-  CompileSuccessfully(GenerateKernelCode(body).c_str());
+  CompileSuccessfully(GenerateKernelCode(body));
   ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
   EXPECT_THAT(getDiagnosticString(),
               HasSubstr("AtomicCompareExchange: "
@@ -608,21 +733,21 @@ OpAtomicStore %f32_var %scope %relaxed %f32_1
 
 TEST_F(ValidateAtomics, AtomicCompareExchangeWeakSuccess) {
   const std::string body = R"(
-%val3 = OpAtomicStore %u32_var %scope %relaxed %u32_1
-%val4 = OpAtomicCompareExchangeWeak %u32 %u32_var %scope %relaxed %relaxed %u32_0 %u32_0
+%val3 = OpAtomicStore %u32_var %device %relaxed %u32_1
+%val4 = OpAtomicCompareExchangeWeak %u32 %u32_var %device %relaxed %relaxed %u32_0 %u32_0
 )";
 
-  CompileSuccessfully(GenerateKernelCode(body).c_str());
+  CompileSuccessfully(GenerateKernelCode(body));
   ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
 }
 
 TEST_F(ValidateAtomics, AtomicCompareExchangeWeakWrongResultType) {
   const std::string body = R"(
-OpAtomicStore %f32_var %scope %relaxed %f32_1
-%val2 = OpAtomicCompareExchangeWeak %f32 %f32_var %scope %relaxed %relaxed %f32_0 %f32_1
+OpAtomicStore %f32_var %device %relaxed %f32_1
+%val2 = OpAtomicCompareExchangeWeak %f32 %f32_var %device %relaxed %relaxed %f32_0 %f32_1
 )";
 
-  CompileSuccessfully(GenerateKernelCode(body).c_str());
+  CompileSuccessfully(GenerateKernelCode(body));
   ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
   EXPECT_THAT(getDiagnosticString(),
               HasSubstr("AtomicCompareExchangeWeak: "
@@ -631,40 +756,40 @@ OpAtomicStore %f32_var %scope %relaxed %f32_1
 
 TEST_F(ValidateAtomics, AtomicArithmeticsSuccess) {
   const std::string body = R"(
-OpAtomicStore %u32_var %scope %relaxed %u32_1
-%val1 = OpAtomicIIncrement %u32 %u32_var %scope %acquire_release
-%val2 = OpAtomicIDecrement %u32 %u32_var %scope %acquire_release
-%val3 = OpAtomicIAdd %u32 %u32_var %scope %acquire_release %u32_1
-%val4 = OpAtomicISub %u32 %u32_var %scope %acquire_release %u32_1
-%val5 = OpAtomicUMin %u32 %u32_var %scope %acquire_release %u32_1
-%val6 = OpAtomicUMax %u32 %u32_var %scope %acquire_release %u32_1
-%val7 = OpAtomicSMin %u32 %u32_var %scope %sequentially_consistent %u32_1
-%val8 = OpAtomicSMax %u32 %u32_var %scope %sequentially_consistent %u32_1
-%val9 = OpAtomicAnd %u32 %u32_var %scope %sequentially_consistent %u32_1
-%val10 = OpAtomicOr %u32 %u32_var %scope %sequentially_consistent %u32_1
-%val11 = OpAtomicXor %u32 %u32_var %scope %sequentially_consistent %u32_1
-)";
-
-  CompileSuccessfully(GenerateKernelCode(body).c_str());
+OpAtomicStore %u32_var %device %relaxed %u32_1
+%val1 = OpAtomicIIncrement %u32 %u32_var %device %acquire_release
+%val2 = OpAtomicIDecrement %u32 %u32_var %device %acquire_release
+%val3 = OpAtomicIAdd %u32 %u32_var %device %acquire_release %u32_1
+%val4 = OpAtomicISub %u32 %u32_var %device %acquire_release %u32_1
+%val5 = OpAtomicUMin %u32 %u32_var %device %acquire_release %u32_1
+%val6 = OpAtomicUMax %u32 %u32_var %device %acquire_release %u32_1
+%val7 = OpAtomicSMin %u32 %u32_var %device %sequentially_consistent %u32_1
+%val8 = OpAtomicSMax %u32 %u32_var %device %sequentially_consistent %u32_1
+%val9 = OpAtomicAnd %u32 %u32_var %device %sequentially_consistent %u32_1
+%val10 = OpAtomicOr %u32 %u32_var %device %sequentially_consistent %u32_1
+%val11 = OpAtomicXor %u32 %u32_var %device %sequentially_consistent %u32_1
+)";
+
+  CompileSuccessfully(GenerateKernelCode(body));
   ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
 }
 
 TEST_F(ValidateAtomics, AtomicFlagsSuccess) {
   const std::string body = R"(
-OpAtomicFlagClear %u32_var %scope %release
-%val1 = OpAtomicFlagTestAndSet %bool %u32_var %scope %relaxed
+OpAtomicFlagClear %u32_var %device %release
+%val1 = OpAtomicFlagTestAndSet %bool %u32_var %device %relaxed
 )";
 
-  CompileSuccessfully(GenerateKernelCode(body).c_str());
+  CompileSuccessfully(GenerateKernelCode(body));
   ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
 }
 
 TEST_F(ValidateAtomics, AtomicFlagTestAndSetWrongResultType) {
   const std::string body = R"(
-%val1 = OpAtomicFlagTestAndSet %u32 %u32_var %scope %relaxed
+%val1 = OpAtomicFlagTestAndSet %u32 %u32_var %device %relaxed
 )";
 
-  CompileSuccessfully(GenerateKernelCode(body).c_str());
+  CompileSuccessfully(GenerateKernelCode(body));
   ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
   EXPECT_THAT(getDiagnosticString(),
               HasSubstr("AtomicFlagTestAndSet: "
@@ -673,10 +798,10 @@ TEST_F(ValidateAtomics, AtomicFlagTestAndSetWrongResultType) {
 
 TEST_F(ValidateAtomics, AtomicFlagTestAndSetNotPointer) {
   const std::string body = R"(
-%val1 = OpAtomicFlagTestAndSet %bool %u32_1 %scope %relaxed
+%val1 = OpAtomicFlagTestAndSet %bool %u32_1 %device %relaxed
 )";
 
-  CompileSuccessfully(GenerateKernelCode(body).c_str());
+  CompileSuccessfully(GenerateKernelCode(body));
   ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
   EXPECT_THAT(getDiagnosticString(),
               HasSubstr("AtomicFlagTestAndSet: "
@@ -685,10 +810,10 @@ TEST_F(ValidateAtomics, AtomicFlagTestAndSetNotPointer) {
 
 TEST_F(ValidateAtomics, AtomicFlagTestAndSetNotIntPointer) {
   const std::string body = R"(
-%val1 = OpAtomicFlagTestAndSet %bool %f32_var %scope %relaxed
+%val1 = OpAtomicFlagTestAndSet %bool %f32_var %device %relaxed
 )";
 
-  CompileSuccessfully(GenerateKernelCode(body).c_str());
+  CompileSuccessfully(GenerateKernelCode(body));
   ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
   EXPECT_THAT(
       getDiagnosticString(),
@@ -698,10 +823,10 @@ TEST_F(ValidateAtomics, AtomicFlagTestAndSetNotIntPointer) {
 
 TEST_F(ValidateAtomics, AtomicFlagTestAndSetNotInt32Pointer) {
   const std::string body = R"(
-%val1 = OpAtomicFlagTestAndSet %bool %u64_var %scope %relaxed
+%val1 = OpAtomicFlagTestAndSet %bool %u64_var %device %relaxed
 )";
 
-  CompileSuccessfully(GenerateKernelCode(body).c_str());
+  CompileSuccessfully(GenerateKernelCode(body));
   ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
   EXPECT_THAT(
       getDiagnosticString(),
@@ -714,7 +839,7 @@ TEST_F(ValidateAtomics, AtomicFlagTestAndSetWrongScopeType) {
 %val1 = OpAtomicFlagTestAndSet %bool %u32_var %u64_1 %relaxed
 )";
 
-  CompileSuccessfully(GenerateKernelCode(body).c_str());
+  CompileSuccessfully(GenerateKernelCode(body));
   ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
   EXPECT_THAT(getDiagnosticString(),
               HasSubstr("AtomicFlagTestAndSet: "
@@ -723,10 +848,10 @@ TEST_F(ValidateAtomics, AtomicFlagTestAndSetWrongScopeType) {
 
 TEST_F(ValidateAtomics, AtomicFlagTestAndSetWrongMemorySemanticsType) {
   const std::string body = R"(
-%val1 = OpAtomicFlagTestAndSet %bool %u32_var %scope %u64_1
+%val1 = OpAtomicFlagTestAndSet %bool %u32_var %device %u64_1
 )";
 
-  CompileSuccessfully(GenerateKernelCode(body).c_str());
+  CompileSuccessfully(GenerateKernelCode(body));
   ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
   EXPECT_THAT(getDiagnosticString(),
               HasSubstr("AtomicFlagTestAndSet: "
@@ -735,10 +860,10 @@ TEST_F(ValidateAtomics, AtomicFlagTestAndSetWrongMemorySemanticsType) {
 
 TEST_F(ValidateAtomics, AtomicFlagClearAcquire) {
   const std::string body = R"(
-OpAtomicFlagClear %u32_var %scope %acquire
+OpAtomicFlagClear %u32_var %device %acquire
 )";
 
-  CompileSuccessfully(GenerateKernelCode(body).c_str());
+  CompileSuccessfully(GenerateKernelCode(body));
   ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
   EXPECT_THAT(getDiagnosticString(),
               HasSubstr("Memory Semantics Acquire and AcquireRelease cannot be "
@@ -747,10 +872,10 @@ OpAtomicFlagClear %u32_var %scope %acquire
 
 TEST_F(ValidateAtomics, AtomicFlagClearNotPointer) {
   const std::string body = R"(
-OpAtomicFlagClear %u32_1 %scope %relaxed
+OpAtomicFlagClear %u32_1 %device %relaxed
 )";
 
-  CompileSuccessfully(GenerateKernelCode(body).c_str());
+  CompileSuccessfully(GenerateKernelCode(body));
   ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
   EXPECT_THAT(getDiagnosticString(),
               HasSubstr("AtomicFlagClear: "
@@ -759,10 +884,10 @@ OpAtomicFlagClear %u32_1 %scope %relaxed
 
 TEST_F(ValidateAtomics, AtomicFlagClearNotIntPointer) {
   const std::string body = R"(
-OpAtomicFlagClear %f32_var %scope %relaxed
+OpAtomicFlagClear %f32_var %device %relaxed
 )";
 
-  CompileSuccessfully(GenerateKernelCode(body).c_str());
+  CompileSuccessfully(GenerateKernelCode(body));
   ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
   EXPECT_THAT(
       getDiagnosticString(),
@@ -772,10 +897,10 @@ OpAtomicFlagClear %f32_var %scope %relaxed
 
 TEST_F(ValidateAtomics, AtomicFlagClearNotInt32Pointer) {
   const std::string body = R"(
-OpAtomicFlagClear %u64_var %scope %relaxed
+OpAtomicFlagClear %u64_var %device %relaxed
 )";
 
-  CompileSuccessfully(GenerateKernelCode(body).c_str());
+  CompileSuccessfully(GenerateKernelCode(body));
   ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
   EXPECT_THAT(
       getDiagnosticString(),
@@ -788,7 +913,7 @@ TEST_F(ValidateAtomics, AtomicFlagClearWrongScopeType) {
 OpAtomicFlagClear %u32_var %u64_1 %relaxed
 )";
 
-  CompileSuccessfully(GenerateKernelCode(body).c_str());
+  CompileSuccessfully(GenerateKernelCode(body));
   ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
   EXPECT_THAT(getDiagnosticString(),
               HasSubstr("AtomicFlagClear: expected Scope to be 32-bit int"));
@@ -796,10 +921,10 @@ OpAtomicFlagClear %u32_var %u64_1 %relaxed
 
 TEST_F(ValidateAtomics, AtomicFlagClearWrongMemorySemanticsType) {
   const std::string body = R"(
-OpAtomicFlagClear %u32_var %scope %u64_1
+OpAtomicFlagClear %u32_var %device %u64_1
 )";
 
-  CompileSuccessfully(GenerateKernelCode(body).c_str());
+  CompileSuccessfully(GenerateKernelCode(body));
   ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
   EXPECT_THAT(
       getDiagnosticString(),
@@ -808,8 +933,8 @@ OpAtomicFlagClear %u32_var %scope %u64_1
 
 TEST_F(ValidateAtomics, AtomicIIncrementAcquireAndRelease) {
   const std::string body = R"(
-OpAtomicStore %u32_var %scope %relaxed %u32_1
-%val1 = OpAtomicIIncrement %u32 %u32_var %scope %acquire_and_release
+OpAtomicStore %u32_var %device %relaxed %u32_1
+%val1 = OpAtomicIIncrement %u32 %u32_var %device %acquire_and_release
 )";
 
   CompileSuccessfully(GenerateKernelCode(body));
@@ -823,8 +948,8 @@ OpAtomicStore %u32_var %scope %relaxed %u32_1
 
 TEST_F(ValidateAtomics, AtomicUniformMemorySemanticsShader) {
   const std::string body = R"(
-OpAtomicStore %u32_var %scope %relaxed %u32_1
-%val1 = OpAtomicIIncrement %u32 %u32_var %scope %acquire_release_uniform_workgroup
+OpAtomicStore %u32_var %device %relaxed %u32_1
+%val1 = OpAtomicIIncrement %u32 %u32_var %device %acquire_release_uniform_workgroup
 )";
 
   CompileSuccessfully(GenerateShaderCode(body));
@@ -833,8 +958,8 @@ OpAtomicStore %u32_var %scope %relaxed %u32_1
 
 TEST_F(ValidateAtomics, AtomicUniformMemorySemanticsKernel) {
   const std::string body = R"(
-OpAtomicStore %u32_var %scope %relaxed %u32_1
-%val1 = OpAtomicIIncrement %u32 %u32_var %scope %acquire_release_uniform_workgroup
+OpAtomicStore %u32_var %device %relaxed %u32_1
+%val1 = OpAtomicIIncrement %u32 %u32_var %device %acquire_release_uniform_workgroup
 )";
 
   CompileSuccessfully(GenerateKernelCode(body));
@@ -846,8 +971,8 @@ OpAtomicStore %u32_var %scope %relaxed %u32_1
 
 TEST_F(ValidateAtomics, AtomicCounterMemorySemanticsNoCapability) {
   const std::string body = R"(
-OpAtomicStore %u32_var %scope %relaxed %u32_1
-%val1 = OpAtomicIIncrement %u32 %u32_var %scope %acquire_release_atomic_counter_workgroup
+OpAtomicStore %u32_var %device %relaxed %u32_1
+%val1 = OpAtomicIIncrement %u32 %u32_var %device %acquire_release_atomic_counter_workgroup
 )";
 
   CompileSuccessfully(GenerateKernelCode(body));
@@ -859,8 +984,8 @@ OpAtomicStore %u32_var %scope %relaxed %u32_1
 
 TEST_F(ValidateAtomics, AtomicCounterMemorySemanticsWithCapability) {
   const std::string body = R"(
-OpAtomicStore %u32_var %scope %relaxed %u32_1
-%val1 = OpAtomicIIncrement %u32 %u32_var %scope %acquire_release_atomic_counter_workgroup
+OpAtomicStore %u32_var %device %relaxed %u32_1
+%val1 = OpAtomicIIncrement %u32 %u32_var %device %acquire_release_atomic_counter_workgroup
 )";
 
   CompileSuccessfully(GenerateKernelCode(body, "OpCapability AtomicStorage\n"));