<NME> validate_atomics.cpp
<BEF> // Copyright (c) 2017 Google Inc.
// Modifications Copyright (C) 2020 Advanced Micro Devices, Inc. All rights
// reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// Validates correctness of atomic SPIR-V instructions.

#include "source/val/validate.h"

#include "source/diagnostic.h"
#include "source/opcode.h"
#include "source/spirv_target_env.h"
#include "source/util/bitutils.h"
#include "source/val/instruction.h"
#include "source/val/validate_memory_semantics.h"
#include "source/val/validate_scopes.h"
#include "source/val/validation_state.h"

namespace {

bool IsStorageClassAllowedByUniversalRules(uint32_t storage_class) {
  switch (storage_class) {
    case SpvStorageClassUniform:
  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";
  }

    case SpvStorageClassPhysicalStorageBuffer:
    case SpvStorageClassTaskPayloadWorkgroupEXT:
      return true;
      break;
    default:
      return false;
  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";
      return false;
      break;
    default:
      return true;
  }
}

bool HasOnlyFloatReturnType(uint32_t opcode) {
  switch (opcode) {
    case SpvOpAtomicFAddEXT:
    case SpvOpAtomicFMinEXT:
    case SpvOpAtomicFMaxEXT:
      return true;
      break;
    default:
      return false;
  }
}
      _.EvalInt32IfConst(memory_semantics_id);

  if (!is_int32) {
    return _.diag(SPV_ERROR_INVALID_DATA)
           << spvOpcodeString(opcode)
           << ": expected Memory Semantics to be 32-bit int";
  }
    case SpvOpAtomicIAdd:
    case SpvOpAtomicISub:
    case SpvOpAtomicSMin:
    case SpvOpAtomicUMin:
    case SpvOpAtomicSMax:
    case SpvOpAtomicUMax:
    case SpvOpAtomicAnd:
          (SpvMemorySemanticsAcquireMask | SpvMemorySemanticsReleaseMask |
           SpvMemorySemanticsAcquireReleaseMask |
           SpvMemorySemanticsSequentiallyConsistentMask)) > 1) {
    return _.diag(SPV_ERROR_INVALID_DATA)
           << spvOpcodeString(opcode)
           << ": no more than one of the following Memory Semantics bits can "
              "be set at the same time: Acquire, Release, AcquireRelease or "
}


  if (flags & SpvMemorySemanticsUniformMemoryMask &&
      !_.HasCapability(SpvCapabilityShader)) {
    return _.diag(SPV_ERROR_INVALID_DATA)
           << spvOpcodeString(opcode)
           << ": Memory Semantics UniformMemory requires capability Shader";
  }

  if (flags & SpvMemorySemanticsAtomicCounterMemoryMask &&
      !_.HasCapability(SpvCapabilityAtomicStorage)) {
    return _.diag(SPV_ERROR_INVALID_DATA)
           << spvOpcodeString(opcode)
           << ": Memory Semantics UniformMemory requires capability "
              "AtomicStorage";
      return true;
      break;
  if (opcode == SpvOpAtomicFlagClear &&
      (flags & SpvMemorySemanticsAcquireMask ||
       flags & SpvMemorySemanticsAcquireReleaseMask)) {
    return _.diag(SPV_ERROR_INVALID_DATA)
           << "Memory Semantics Acquire and AcquireRelease cannot be used with "
           << spvOpcodeString(opcode);
  }
namespace spvtools {
  if (opcode == SpvOpAtomicCompareExchange && operand_index == 5 &&
      (flags & SpvMemorySemanticsReleaseMask ||
       flags & SpvMemorySemanticsAcquireReleaseMask)) {
    return _.diag(SPV_ERROR_INVALID_DATA)
           << spvOpcodeString(opcode)
           << ": Memory Semantics Release and AcquireRelease cannot be used "
              "for operand Unequal";
    case SpvOpAtomicStore:
    case SpvOpAtomicExchange:
    case SpvOpAtomicFAddEXT:
    case SpvOpAtomicCompareExchange:
        (flags & SpvMemorySemanticsReleaseMask ||
         flags & SpvMemorySemanticsAcquireReleaseMask ||
         flags & SpvMemorySemanticsSequentiallyConsistentMask)) {
      return _.diag(SPV_ERROR_INVALID_DATA)
             << "Vulkan spec disallows OpAtomicLoad with Memory Semantics "
                "Release, AcquireRelease and SequentiallyConsistent";
    }
    case SpvOpAtomicFMinEXT:
    case SpvOpAtomicSMax:
        (flags & SpvMemorySemanticsAcquireMask ||
         flags & SpvMemorySemanticsAcquireReleaseMask ||
         flags & SpvMemorySemanticsSequentiallyConsistentMask)) {
      return _.diag(SPV_ERROR_INVALID_DATA)
             << "Vulkan spec disallows OpAtomicStore with Memory Semantics "
                "Acquire, AcquireRelease and SequentiallyConsistent";
    }
      const uint32_t result_type = inst->type_id();

      // All current atomics only are scalar result
      // Validate return type first so can just check if pointer type is same
      // (if applicable)
      if (HasReturnType(opcode)) {
        if (HasOnlyFloatReturnType(opcode) &&
            !_.IsFloatScalarType(result_type)) {
          return _.diag(SPV_ERROR_INVALID_DATA, inst)
                 << spvOpcodeString(opcode)
                 << ": expected Result Type to be float scalar type";
        } else if (HasOnlyIntReturnType(opcode) &&
                   !_.IsIntScalarType(result_type)) {
          return _.diag(SPV_ERROR_INVALID_DATA, inst)
                 << spvOpcodeString(opcode)
                 << ": expected Result Type to be integer scalar type";
        } else if (HasIntOrFloatReturnType(opcode) &&
                   !_.IsFloatScalarType(result_type) &&
                   !_.IsIntScalarType(result_type)) {
          return _.diag(SPV_ERROR_INVALID_DATA, inst)
                 << spvOpcodeString(opcode)
                 << ": expected Result Type to be integer or float scalar type";
        } else if (HasOnlyBoolReturnType(opcode) &&
                   !_.IsBoolScalarType(result_type)) {
          return _.diag(SPV_ERROR_INVALID_DATA, inst)
                 << spvOpcodeString(opcode)
                 << ": expected Result Type to be bool scalar type";
        }
      }

      uint32_t operand_index = HasReturnType(opcode) ? 2 : 0;
      const uint32_t pointer_type = _.GetOperandTypeId(inst, operand_index++);
      uint32_t data_type = 0;
           opcode == SpvOpAtomicCompareExchange)) {
        if (!_.IsFloatScalarType(result_type) &&
            !_.IsIntScalarType(result_type)) {
          return _.diag(SPV_ERROR_INVALID_DATA)
                 << spvOpcodeString(opcode)
                 << ": expected Result Type to be int or float scalar type";
        }
      } else if (opcode == SpvOpAtomicFlagTestAndSet) {
        if (!_.IsBoolScalarType(result_type)) {
          return _.diag(SPV_ERROR_INVALID_DATA)
                 << spvOpcodeString(opcode)
                 << ": expected Result Type to be bool scalar type";
        }
      }
        assert(result_type == 0);
      } else {
        if (!_.IsIntScalarType(result_type)) {
          return _.diag(SPV_ERROR_INVALID_DATA)
                 << spvOpcodeString(opcode)
                 << ": expected Result Type to be int scalar type";
        }
        if (spvIsVulkanEnv(_.context()->target_env) &&
            _.GetBitWidth(result_type) != 32) {
          return _.diag(SPV_ERROR_INVALID_DATA)
                 << spvOpcodeString(opcode)
                 << ": according to the Vulkan spec atomic Result Type needs "
                    "to be a 32-bit int scalar type";
              (storage_class != SpvStorageClassStorageBuffer) &&
              (storage_class != SpvStorageClassWorkgroup) &&
              (storage_class != SpvStorageClassImage) &&
              (storage_class != SpvStorageClassPhysicalStorageBuffer) &&
              (storage_class != SpvStorageClassTaskPayloadWorkgroupEXT)) {
            return _.diag(SPV_ERROR_INVALID_DATA, inst)
                   << _.VkErrorID(4686) << spvOpcodeString(opcode)
      uint32_t data_type = 0;
      uint32_t storage_class = 0;
      if (!_.GetPointerTypeInfo(pointer_type, &data_type, &storage_class)) {
        return _.diag(SPV_ERROR_INVALID_DATA)
               << spvOpcodeString(opcode)
               << ": expected Pointer to be of type OpTypePointer";
      }
                 << ": Function storage class forbidden when the Shader "
                    "capability is declared.";
        }

        if (opcode == SpvOpAtomicFAddEXT) {
          // result type being float checked already
          if ((_.GetBitWidth(result_type) == 16) &&
              (!_.HasCapability(SpvCapabilityAtomicFloat16AddEXT))) {
        case SpvStorageClassStorageBuffer:
          break;
        default:
          return _.diag(SPV_ERROR_INVALID_DATA)
                 << spvOpcodeString(opcode)
                 << ": expected Pointer Storage Class to be Uniform, "
                    "Workgroup, CrossWorkgroup, Generic, AtomicCounter, Image "
            return _.diag(SPV_ERROR_INVALID_DATA, inst)
                   << spvOpcodeString(opcode)
                   << ": float add atomics require the AtomicFloat32AddEXT "
      if (opcode == SpvOpAtomicFlagTestAndSet ||
          opcode == SpvOpAtomicFlagClear) {
        if (!_.IsIntScalarType(data_type) || _.GetBitWidth(data_type) != 32) {
          return _.diag(SPV_ERROR_INVALID_DATA)
                 << spvOpcodeString(opcode)
                 << ": expected Pointer to point to a value of 32-bit int type";
        }
      } else if (opcode == SpvOpAtomicStore) {
        if (!_.IsFloatScalarType(data_type) && !_.IsIntScalarType(data_type)) {
          return _.diag(SPV_ERROR_INVALID_DATA)
                 << spvOpcodeString(opcode)
                 << ": expected Pointer to be a pointer to int or float "
                 << "scalar type";
        }
      } else {
        if (data_type != result_type) {
          return _.diag(SPV_ERROR_INVALID_DATA)
                 << spvOpcodeString(opcode)
                 << ": expected Pointer to point to a value of type Result "
                    "Type";
            return _.diag(SPV_ERROR_INVALID_DATA, inst)
                   << spvOpcodeString(opcode)
                   << ": float min/max atomics require the "
                      "AtomicFloat32MinMaxEXT capability";
          }
          if ((_.GetBitWidth(result_type) == 64) &&
              (!_.HasCapability(SpvCapabilityAtomicFloat64MinMaxEXT))) {
            return _.diag(SPV_ERROR_INVALID_DATA, inst)
                   << spvOpcodeString(opcode)
                   << ": float min/max atomics require the "
                      "AtomicFloat64MinMaxEXT capability";
          }
        }
      }

      // And finally OpenCL environment rules
      if (spvIsOpenCLEnv(_.context()->target_env)) {
      if (opcode == SpvOpAtomicStore) {
        const uint32_t value_type = _.GetOperandTypeId(inst, 3);
        if (value_type != data_type) {
          return _.diag(SPV_ERROR_INVALID_DATA)
                 << spvOpcodeString(opcode)
                 << ": expected Value type and the type pointed to by Pointer "
                    "to"
                    "CrossWorkGroup or Generic in the OpenCL environment.";
        }

        if (_.context()->target_env == SPV_ENV_OPENCL_1_2) {
          if (storage_class == SpvStorageClassGeneric) {
                 opcode != SpvOpAtomicFlagClear) {
        const uint32_t value_type = _.GetOperandTypeId(inst, operand_index++);
        if (value_type != result_type) {
          return _.diag(SPV_ERROR_INVALID_DATA)
                 << spvOpcodeString(opcode)
                 << ": expected Value to be of type Result Type";
        }
      // If result and pointer type are different, need to do special check here
      if (opcode == SpvOpAtomicFlagTestAndSet ||
          opcode == SpvOpAtomicFlagClear) {
        if (!_.IsIntScalarType(data_type) || _.GetBitWidth(data_type) != 32) {
        const uint32_t comparator_type =
            _.GetOperandTypeId(inst, operand_index++);
        if (comparator_type != result_type) {
          return _.diag(SPV_ERROR_INVALID_DATA)
                 << spvOpcodeString(opcode)
                 << ": expected Comparator to be of type Result Type";
        }
          return _.diag(SPV_ERROR_INVALID_DATA, inst)
                 << spvOpcodeString(opcode)
                 << ": expected Pointer to be a pointer to integer or float "
                 << "scalar type";
        }
      } else if (data_type != result_type) {
        return _.diag(SPV_ERROR_INVALID_DATA, inst)
               << spvOpcodeString(opcode)
               << ": expected Pointer to point to a value of type Result "
                  "Type";
      }

      auto memory_scope = inst->GetOperandAs<const uint32_t>(operand_index++);
      if (auto error = ValidateMemoryScope(_, inst, memory_scope)) {
        return error;
      }

      const auto equal_semantics_index = operand_index++;
      if (auto error = ValidateMemorySemantics(_, inst, equal_semantics_index,
                                               memory_scope))
        return error;

      if (opcode == SpvOpAtomicCompareExchange ||
          opcode == SpvOpAtomicCompareExchangeWeak) {
        const auto unequal_semantics_index = operand_index++;
        if (auto error = ValidateMemorySemantics(
                _, inst, unequal_semantics_index, memory_scope))
          return error;

        // Volatile bits must match for equal and unequal semantics. Previous
        // checks guarantee they are 32-bit constants, but we need to recheck
        // whether they are evaluatable constants.
        bool is_int32 = false;
        bool is_equal_const = false;
        bool is_unequal_const = false;
        uint32_t equal_value = 0;
        uint32_t unequal_value = 0;
        std::tie(is_int32, is_equal_const, equal_value) = _.EvalInt32IfConst(
            inst->GetOperandAs<uint32_t>(equal_semantics_index));
        std::tie(is_int32, is_unequal_const, unequal_value) =
            _.EvalInt32IfConst(
                inst->GetOperandAs<uint32_t>(unequal_semantics_index));
        if (is_equal_const && is_unequal_const &&
            ((equal_value & SpvMemorySemanticsVolatileMask) ^
             (unequal_value & SpvMemorySemanticsVolatileMask))) {
          return _.diag(SPV_ERROR_INVALID_ID, inst)
                 << "Volatile mask setting must match for Equal and Unequal "
                    "memory semantics";
        }
      }

      if (opcode == SpvOpAtomicStore) {
        const uint32_t value_type = _.GetOperandTypeId(inst, 3);
        if (value_type != data_type) {
          return _.diag(SPV_ERROR_INVALID_DATA, inst)
                 << spvOpcodeString(opcode)
                 << ": expected Value type and the type pointed to by "
                    "Pointer to be the same";
        }
      } else if (opcode != SpvOpAtomicLoad && opcode != SpvOpAtomicIIncrement &&
                 opcode != SpvOpAtomicIDecrement &&
                 opcode != SpvOpAtomicFlagTestAndSet &&
                 opcode != SpvOpAtomicFlagClear) {
        const uint32_t value_type = _.GetOperandTypeId(inst, operand_index++);
        if (value_type != result_type) {
          return _.diag(SPV_ERROR_INVALID_DATA, inst)
                 << spvOpcodeString(opcode)
                 << ": expected Value to be of type Result Type";
        }
      }

      if (opcode == SpvOpAtomicCompareExchange ||
          opcode == SpvOpAtomicCompareExchangeWeak) {
        const uint32_t comparator_type =
            _.GetOperandTypeId(inst, operand_index++);
        if (comparator_type != result_type) {
          return _.diag(SPV_ERROR_INVALID_DATA, inst)
                 << spvOpcodeString(opcode)
                 << ": expected Comparator to be of type Result Type";
        }
      }

      break;
    }

    default:
      break;
  }

  return SPV_SUCCESS;
}

}  // namespace val
}  // namespace spvtools

<MSG> Update diag() in validate_atomics (#1753)

This CL updates validate_atomics to explicitly provide the instruction
when caling diag().
<DFF> @@ -35,7 +35,7 @@ spv_result_t ValidateMemoryScope(ValidationState_t& _, const Instruction* inst,
   std::tie(is_int32, is_const_int32, value) = _.EvalInt32IfConst(id);
 
   if (!is_int32) {
-    return _.diag(SPV_ERROR_INVALID_DATA)
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
            << spvOpcodeString(opcode) << ": expected Scope to be 32-bit int";
   }
 
@@ -48,7 +48,7 @@ spv_result_t ValidateMemoryScope(ValidationState_t& _, const Instruction* inst,
   if (spvIsVulkanEnv(_.context()->target_env)) {
     if (value != SpvScopeDevice && value != SpvScopeWorkgroup &&
         value != SpvScopeInvocation) {
-      return _.diag(SPV_ERROR_INVALID_DATA)
+      return _.diag(SPV_ERROR_INVALID_DATA, inst)
              << spvOpcodeString(opcode)
              << ": in Vulkan environment memory scope is limited to Device, "
                 "Workgroup and Invocation";
@@ -73,7 +73,7 @@ spv_result_t ValidateMemorySemantics(ValidationState_t& _,
       _.EvalInt32IfConst(memory_semantics_id);
 
   if (!is_int32) {
-    return _.diag(SPV_ERROR_INVALID_DATA)
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
            << spvOpcodeString(opcode)
            << ": expected Memory Semantics to be 32-bit int";
   }
@@ -87,7 +87,7 @@ spv_result_t ValidateMemorySemantics(ValidationState_t& _,
           (SpvMemorySemanticsAcquireMask | SpvMemorySemanticsReleaseMask |
            SpvMemorySemanticsAcquireReleaseMask |
            SpvMemorySemanticsSequentiallyConsistentMask)) > 1) {
-    return _.diag(SPV_ERROR_INVALID_DATA)
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
            << spvOpcodeString(opcode)
            << ": no more than one of the following Memory Semantics bits can "
               "be set at the same time: Acquire, Release, AcquireRelease or "
@@ -96,14 +96,14 @@ spv_result_t ValidateMemorySemantics(ValidationState_t& _,
 
   if (flags & SpvMemorySemanticsUniformMemoryMask &&
       !_.HasCapability(SpvCapabilityShader)) {
-    return _.diag(SPV_ERROR_INVALID_DATA)
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
            << spvOpcodeString(opcode)
            << ": Memory Semantics UniformMemory requires capability Shader";
   }
 
   if (flags & SpvMemorySemanticsAtomicCounterMemoryMask &&
       !_.HasCapability(SpvCapabilityAtomicStorage)) {
-    return _.diag(SPV_ERROR_INVALID_DATA)
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
            << spvOpcodeString(opcode)
            << ": Memory Semantics UniformMemory requires capability "
               "AtomicStorage";
@@ -112,7 +112,7 @@ spv_result_t ValidateMemorySemantics(ValidationState_t& _,
   if (opcode == SpvOpAtomicFlagClear &&
       (flags & SpvMemorySemanticsAcquireMask ||
        flags & SpvMemorySemanticsAcquireReleaseMask)) {
-    return _.diag(SPV_ERROR_INVALID_DATA)
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
            << "Memory Semantics Acquire and AcquireRelease cannot be used with "
            << spvOpcodeString(opcode);
   }
@@ -120,7 +120,7 @@ spv_result_t ValidateMemorySemantics(ValidationState_t& _,
   if (opcode == SpvOpAtomicCompareExchange && operand_index == 5 &&
       (flags & SpvMemorySemanticsReleaseMask ||
        flags & SpvMemorySemanticsAcquireReleaseMask)) {
-    return _.diag(SPV_ERROR_INVALID_DATA)
+    return _.diag(SPV_ERROR_INVALID_DATA, inst)
            << spvOpcodeString(opcode)
            << ": Memory Semantics Release and AcquireRelease cannot be used "
               "for operand Unequal";
@@ -131,7 +131,7 @@ spv_result_t ValidateMemorySemantics(ValidationState_t& _,
         (flags & SpvMemorySemanticsReleaseMask ||
          flags & SpvMemorySemanticsAcquireReleaseMask ||
          flags & SpvMemorySemanticsSequentiallyConsistentMask)) {
-      return _.diag(SPV_ERROR_INVALID_DATA)
+      return _.diag(SPV_ERROR_INVALID_DATA, inst)
              << "Vulkan spec disallows OpAtomicLoad with Memory Semantics "
                 "Release, AcquireRelease and SequentiallyConsistent";
     }
@@ -140,7 +140,7 @@ spv_result_t ValidateMemorySemantics(ValidationState_t& _,
         (flags & SpvMemorySemanticsAcquireMask ||
          flags & SpvMemorySemanticsAcquireReleaseMask ||
          flags & SpvMemorySemanticsSequentiallyConsistentMask)) {
-      return _.diag(SPV_ERROR_INVALID_DATA)
+      return _.diag(SPV_ERROR_INVALID_DATA, inst)
              << "Vulkan spec disallows OpAtomicStore with Memory Semantics "
                 "Acquire, AcquireRelease and SequentiallyConsistent";
     }
@@ -180,13 +180,13 @@ spv_result_t AtomicsPass(ValidationState_t& _, const Instruction* inst) {
            opcode == SpvOpAtomicCompareExchange)) {
         if (!_.IsFloatScalarType(result_type) &&
             !_.IsIntScalarType(result_type)) {
-          return _.diag(SPV_ERROR_INVALID_DATA)
+          return _.diag(SPV_ERROR_INVALID_DATA, inst)
                  << spvOpcodeString(opcode)
                  << ": expected Result Type to be int or float scalar type";
         }
       } else if (opcode == SpvOpAtomicFlagTestAndSet) {
         if (!_.IsBoolScalarType(result_type)) {
-          return _.diag(SPV_ERROR_INVALID_DATA)
+          return _.diag(SPV_ERROR_INVALID_DATA, inst)
                  << spvOpcodeString(opcode)
                  << ": expected Result Type to be bool scalar type";
         }
@@ -194,13 +194,13 @@ spv_result_t AtomicsPass(ValidationState_t& _, const Instruction* inst) {
         assert(result_type == 0);
       } else {
         if (!_.IsIntScalarType(result_type)) {
-          return _.diag(SPV_ERROR_INVALID_DATA)
+          return _.diag(SPV_ERROR_INVALID_DATA, inst)
                  << spvOpcodeString(opcode)
                  << ": expected Result Type to be int scalar type";
         }
         if (spvIsVulkanEnv(_.context()->target_env) &&
             _.GetBitWidth(result_type) != 32) {
-          return _.diag(SPV_ERROR_INVALID_DATA)
+          return _.diag(SPV_ERROR_INVALID_DATA, inst)
                  << spvOpcodeString(opcode)
                  << ": according to the Vulkan spec atomic Result Type needs "
                     "to be a 32-bit int scalar type";
@@ -214,7 +214,7 @@ spv_result_t AtomicsPass(ValidationState_t& _, const Instruction* inst) {
       uint32_t data_type = 0;
       uint32_t storage_class = 0;
       if (!_.GetPointerTypeInfo(pointer_type, &data_type, &storage_class)) {
-        return _.diag(SPV_ERROR_INVALID_DATA)
+        return _.diag(SPV_ERROR_INVALID_DATA, inst)
                << spvOpcodeString(opcode)
                << ": expected Pointer to be of type OpTypePointer";
       }
@@ -229,7 +229,7 @@ spv_result_t AtomicsPass(ValidationState_t& _, const Instruction* inst) {
         case SpvStorageClassStorageBuffer:
           break;
         default:
-          return _.diag(SPV_ERROR_INVALID_DATA)
+          return _.diag(SPV_ERROR_INVALID_DATA, inst)
                  << spvOpcodeString(opcode)
                  << ": expected Pointer Storage Class to be Uniform, "
                     "Workgroup, CrossWorkgroup, Generic, AtomicCounter, Image "
@@ -239,20 +239,20 @@ spv_result_t AtomicsPass(ValidationState_t& _, const Instruction* inst) {
       if (opcode == SpvOpAtomicFlagTestAndSet ||
           opcode == SpvOpAtomicFlagClear) {
         if (!_.IsIntScalarType(data_type) || _.GetBitWidth(data_type) != 32) {
-          return _.diag(SPV_ERROR_INVALID_DATA)
+          return _.diag(SPV_ERROR_INVALID_DATA, inst)
                  << spvOpcodeString(opcode)
                  << ": expected Pointer to point to a value of 32-bit int type";
         }
       } else if (opcode == SpvOpAtomicStore) {
         if (!_.IsFloatScalarType(data_type) && !_.IsIntScalarType(data_type)) {
-          return _.diag(SPV_ERROR_INVALID_DATA)
+          return _.diag(SPV_ERROR_INVALID_DATA, inst)
                  << spvOpcodeString(opcode)
                  << ": expected Pointer to be a pointer to int or float "
                  << "scalar type";
         }
       } else {
         if (data_type != result_type) {
-          return _.diag(SPV_ERROR_INVALID_DATA)
+          return _.diag(SPV_ERROR_INVALID_DATA, inst)
                  << spvOpcodeString(opcode)
                  << ": expected Pointer to point to a value of type Result "
                     "Type";
@@ -276,7 +276,7 @@ spv_result_t AtomicsPass(ValidationState_t& _, const Instruction* inst) {
       if (opcode == SpvOpAtomicStore) {
         const uint32_t value_type = _.GetOperandTypeId(inst, 3);
         if (value_type != data_type) {
-          return _.diag(SPV_ERROR_INVALID_DATA)
+          return _.diag(SPV_ERROR_INVALID_DATA, inst)
                  << spvOpcodeString(opcode)
                  << ": expected Value type and the type pointed to by Pointer "
                     "to"
@@ -288,7 +288,7 @@ spv_result_t AtomicsPass(ValidationState_t& _, const Instruction* inst) {
                  opcode != SpvOpAtomicFlagClear) {
         const uint32_t value_type = _.GetOperandTypeId(inst, operand_index++);
         if (value_type != result_type) {
-          return _.diag(SPV_ERROR_INVALID_DATA)
+          return _.diag(SPV_ERROR_INVALID_DATA, inst)
                  << spvOpcodeString(opcode)
                  << ": expected Value to be of type Result Type";
         }
@@ -299,7 +299,7 @@ spv_result_t AtomicsPass(ValidationState_t& _, const Instruction* inst) {
         const uint32_t comparator_type =
             _.GetOperandTypeId(inst, operand_index++);
         if (comparator_type != result_type) {
-          return _.diag(SPV_ERROR_INVALID_DATA)
+          return _.diag(SPV_ERROR_INVALID_DATA, inst)
                  << spvOpcodeString(opcode)
                  << ": expected Comparator to be of type Result Type";
         }

{'commit': 'diff --git a/source/val/validate_atomics.cpp b/source/val/validate_atomics.cpp\nindex e537c976..becb8720 100644\n--- a/source/val/validate_atomics.cpp\n+++ b/source/val/validate_atomics.cpp\n@@ -35,7 +35,7 @@ spv_result_t ValidateMemoryScope(ValidationState_t& _, const Instruction* inst,\n   std::tie(is_int32, is_const_int32, value) = _.EvalInt32IfConst(id);\n \n   if (!is_int32) {\n-    return _.diag(SPV_ERROR_INVALID_DATA)\n+    return _.diag(SPV_ERROR_INVALID_DATA, inst)\n            << spvOpcodeString(opcode) << ": expected Scope to be 32-bit int";\n   }\n \n@@ -48,7 +48,7 @@ spv_result_t ValidateMemoryScope(ValidationState_t& _, const Instruction* inst,\n   if (spvIsVulkanEnv(_.context()->target_env)) {\n     if (value != SpvScopeDevice && value != SpvScopeWorkgroup &&\n         value != SpvScopeInvocation) {\n-      return _.diag(SPV_ERROR_INVALID_DATA)\n+      return _.diag(SPV_ERROR_INVALID_DATA, inst)\n              << spvOpcodeString(opcode)\n              << ": in Vulkan environment memory scope is limited to Device, "\n                 "Workgroup and Invocation";\n@@ -73,7 +73,7 @@ spv_result_t ValidateMemorySemantics(ValidationState_t& _,\n       _.EvalInt32IfConst(memory_semantics_id);\n \n   if (!is_int32) {\n-    return _.diag(SPV_ERROR_INVALID_DATA)\n+    return _.diag(SPV_ERROR_INVALID_DATA, inst)\n            << spvOpcodeString(opcode)\n            << ": expected Memory Semantics to be 32-bit int";\n   }\n@@ -87,7 +87,7 @@ spv_result_t ValidateMemorySemantics(ValidationState_t& _,\n           (SpvMemorySemanticsAcquireMask | SpvMemorySemanticsReleaseMask |\n            SpvMemorySemanticsAcquireReleaseMask |\n            SpvMemorySemanticsSequentiallyConsistentMask)) > 1) {\n-    return _.diag(SPV_ERROR_INVALID_DATA)\n+    return _.diag(SPV_ERROR_INVALID_DATA, inst)\n            << spvOpcodeString(opcode)\n            << ": no more than one of the following Memory Semantics bits can "\n               "be set at the same time: Acquire, Release, AcquireRelease or "\n@@ -96,14 +96,14 @@ spv_result_t ValidateMemorySemantics(ValidationState_t& _,\n \n   if (flags & SpvMemorySemanticsUniformMemoryMask &&\n       !_.HasCapability(SpvCapabilityShader)) {\n-    return _.diag(SPV_ERROR_INVALID_DATA)\n+    return _.diag(SPV_ERROR_INVALID_DATA, inst)\n            << spvOpcodeString(opcode)\n            << ": Memory Semantics UniformMemory requires capability Shader";\n   }\n \n   if (flags & SpvMemorySemanticsAtomicCounterMemoryMask &&\n       !_.HasCapability(SpvCapabilityAtomicStorage)) {\n-    return _.diag(SPV_ERROR_INVALID_DATA)\n+    return _.diag(SPV_ERROR_INVALID_DATA, inst)\n            << spvOpcodeString(opcode)\n            << ": Memory Semantics UniformMemory requires capability "\n               "AtomicStorage";\n@@ -112,7 +112,7 @@ spv_result_t ValidateMemorySemantics(ValidationState_t& _,\n   if (opcode == SpvOpAtomicFlagClear &&\n       (flags & SpvMemorySemanticsAcquireMask ||\n        flags & SpvMemorySemanticsAcquireReleaseMask)) {\n-    return _.diag(SPV_ERROR_INVALID_DATA)\n+    return _.diag(SPV_ERROR_INVALID_DATA, inst)\n            << "Memory Semantics Acquire and AcquireRelease cannot be used with "\n            << spvOpcodeString(opcode);\n   }\n@@ -120,7 +120,7 @@ spv_result_t ValidateMemorySemantics(ValidationState_t& _,\n   if (opcode == SpvOpAtomicCompareExchange && operand_index == 5 &&\n       (flags & SpvMemorySemanticsReleaseMask ||\n        flags & SpvMemorySemanticsAcquireReleaseMask)) {\n-    return _.diag(SPV_ERROR_INVALID_DATA)\n+    return _.diag(SPV_ERROR_INVALID_DATA, inst)\n            << spvOpcodeString(opcode)\n            << ": Memory Semantics Release and AcquireRelease cannot be used "\n               "for operand Unequal";\n@@ -131,7 +131,7 @@ spv_result_t ValidateMemorySemantics(ValidationState_t& _,\n         (flags & SpvMemorySemanticsReleaseMask ||\n          flags & SpvMemorySemanticsAcquireReleaseMask ||\n          flags & SpvMemorySemanticsSequentiallyConsistentMask)) {\n-      return _.diag(SPV_ERROR_INVALID_DATA)\n+      return _.diag(SPV_ERROR_INVALID_DATA, inst)\n              << "Vulkan spec disallows OpAtomicLoad with Memory Semantics "\n                 "Release, AcquireRelease and SequentiallyConsistent";\n     }\n@@ -140,7 +140,7 @@ spv_result_t ValidateMemorySemantics(ValidationState_t& _,\n         (flags & SpvMemorySemanticsAcquireMask ||\n          flags & SpvMemorySemanticsAcquireReleaseMask ||\n          flags & SpvMemorySemanticsSequentiallyConsistentMask)) {\n-      return _.diag(SPV_ERROR_INVALID_DATA)\n+      return _.diag(SPV_ERROR_INVALID_DATA, inst)\n              << "Vulkan spec disallows OpAtomicStore with Memory Semantics "\n                 "Acquire, AcquireRelease and SequentiallyConsistent";\n     }\n@@ -180,13 +180,13 @@ spv_result_t AtomicsPass(ValidationState_t& _, const Instruction* inst) {\n            opcode == SpvOpAtomicCompareExchange)) {\n         if (!_.IsFloatScalarType(result_type) &&\n             !_.IsIntScalarType(result_type)) {\n-          return _.diag(SPV_ERROR_INVALID_DATA)\n+          return _.diag(SPV_ERROR_INVALID_DATA, inst)\n                  << spvOpcodeString(opcode)\n                  << ": expected Result Type to be int or float scalar type";\n         }\n       } else if (opcode == SpvOpAtomicFlagTestAndSet) {\n         if (!_.IsBoolScalarType(result_type)) {\n-          return _.diag(SPV_ERROR_INVALID_DATA)\n+          return _.diag(SPV_ERROR_INVALID_DATA, inst)\n                  << spvOpcodeString(opcode)\n                  << ": expected Result Type to be bool scalar type";\n         }\n@@ -194,13 +194,13 @@ spv_result_t AtomicsPass(ValidationState_t& _, const Instruction* inst) {\n         assert(result_type == 0);\n       } else {\n         if (!_.IsIntScalarType(result_type)) {\n-          return _.diag(SPV_ERROR_INVALID_DATA)\n+          return _.diag(SPV_ERROR_INVALID_DATA, inst)\n                  << spvOpcodeString(opcode)\n                  << ": expected Result Type to be int scalar type";\n         }\n         if (spvIsVulkanEnv(_.context()->target_env) &&\n             _.GetBitWidth(result_type) != 32) {\n-          return _.diag(SPV_ERROR_INVALID_DATA)\n+          return _.diag(SPV_ERROR_INVALID_DATA, inst)\n                  << spvOpcodeString(opcode)\n                  << ": according to the Vulkan spec atomic Result Type needs "\n                     "to be a 32-bit int scalar type";\n@@ -214,7 +214,7 @@ spv_result_t AtomicsPass(ValidationState_t& _, const Instruction* inst) {\n       uint32_t data_type = 0;\n       uint32_t storage_class = 0;\n       if (!_.GetPointerTypeInfo(pointer_type, &data_type, &storage_class)) {\n-        return _.diag(SPV_ERROR_INVALID_DATA)\n+        return _.diag(SPV_ERROR_INVALID_DATA, inst)\n                << spvOpcodeString(opcode)\n                << ": expected Pointer to be of type OpTypePointer";\n       }\n@@ -229,7 +229,7 @@ spv_result_t AtomicsPass(ValidationState_t& _, const Instruction* inst) {\n         case SpvStorageClassStorageBuffer:\n           break;\n         default:\n-          return _.diag(SPV_ERROR_INVALID_DATA)\n+          return _.diag(SPV_ERROR_INVALID_DATA, inst)\n                  << spvOpcodeString(opcode)\n                  << ": expected Pointer Storage Class to be Uniform, "\n                     "Workgroup, CrossWorkgroup, Generic, AtomicCounter, Image "\n@@ -239,20 +239,20 @@ spv_result_t AtomicsPass(ValidationState_t& _, const Instruction* inst) {\n       if (opcode == SpvOpAtomicFlagTestAndSet ||\n           opcode == SpvOpAtomicFlagClear) {\n         if (!_.IsIntScalarType(data_type) || _.GetBitWidth(data_type) != 32) {\n-          return _.diag(SPV_ERROR_INVALID_DATA)\n+          return _.diag(SPV_ERROR_INVALID_DATA, inst)\n                  << spvOpcodeString(opcode)\n                  << ": expected Pointer to point to a value of 32-bit int type";\n         }\n       } else if (opcode == SpvOpAtomicStore) {\n         if (!_.IsFloatScalarType(data_type) && !_.IsIntScalarType(data_type)) {\n-          return _.diag(SPV_ERROR_INVALID_DATA)\n+          return _.diag(SPV_ERROR_INVALID_DATA, inst)\n                  << spvOpcodeString(opcode)\n                  << ": expected Pointer to be a pointer to int or float "\n                  << "scalar type";\n         }\n       } else {\n         if (data_type != result_type) {\n-          return _.diag(SPV_ERROR_INVALID_DATA)\n+          return _.diag(SPV_ERROR_INVALID_DATA, inst)\n                  << spvOpcodeString(opcode)\n                  << ": expected Pointer to point to a value of type Result "\n                     "Type";\n@@ -276,7 +276,7 @@ spv_result_t AtomicsPass(ValidationState_t& _, const Instruction* inst) {\n       if (opcode == SpvOpAtomicStore) {\n         const uint32_t value_type = _.GetOperandTypeId(inst, 3);\n         if (value_type != data_type) {\n-          return _.diag(SPV_ERROR_INVALID_DATA)\n+          return _.diag(SPV_ERROR_INVALID_DATA, inst)\n                  << spvOpcodeString(opcode)\n                  << ": expected Value type and the type pointed to by Pointer "\n                     "to"\n@@ -288,7 +288,7 @@ spv_result_t AtomicsPass(ValidationState_t& _, const Instruction* inst) {\n                  opcode != SpvOpAtomicFlagClear) {\n         const uint32_t value_type = _.GetOperandTypeId(inst, operand_index++);\n         if (value_type != result_type) {\n-          return _.diag(SPV_ERROR_INVALID_DATA)\n+          return _.diag(SPV_ERROR_INVALID_DATA, inst)\n                  << spvOpcodeString(opcode)\n                  << ": expected Value to be of type Result Type";\n         }\n@@ -299,7 +299,7 @@ spv_result_t AtomicsPass(ValidationState_t& _, const Instruction* inst) {\n         const uint32_t comparator_type =\n             _.GetOperandTypeId(inst, operand_index++);\n         if (comparator_type != result_type) {\n-          return _.diag(SPV_ERROR_INVALID_DATA)\n+          return _.diag(SPV_ERROR_INVALID_DATA, inst)\n                  << spvOpcodeString(opcode)\n                  << ": expected Comparator to be of type Result Type";\n         }\n', 'commit_subject': 'Update diag() in validate_atomics (#1753)', 'repo_name': 'KhronosGroup/SPIRV-Tools', 'committer': {'name': 'GitHub', 'email': '1505422b2465e9a84f6fdfaa161078890c593f06@github.com', 'time_sec': '1533072043', 'tz_offset': '-240', 'date': {'seconds': '1533072043'}}, 'commit_tree': 'dee2731fa38ee1c430b391064605699c3edeabc5', 'commit_message': 'Update diag() in validate_atomics (#1753)\n\nThis CL updates validate_atomics to explicitly provide the instruction\r\nwhen caling diag().', 'language_name': 'HTML', 'license': 'apache-2.0', 'hunks': '@@ -35,7 +35,7 @@ spv_result_t ValidateMemoryScope(ValidationState_t& _, const Instruction* inst,\n   std::tie(is_int32, is_const_int32, value) = _.EvalInt32IfConst(id);\n \n   if (!is_int32) {\n-    return _.diag(SPV_ERROR_INVALID_DATA)\n+    return _.diag(SPV_ERROR_INVALID_DATA, inst)\n            << spvOpcodeString(opcode) << ": expected Scope to be 32-bit int";\n   }\n \n@@ -48,7 +48,7 @@ spv_result_t ValidateMemoryScope(ValidationState_t& _, const Instruction* inst,\n   if (spvIsVulkanEnv(_.context()->target_env)) {\n     if (value != SpvScopeDevice && value != SpvScopeWorkgroup &&\n         value != SpvScopeInvocation) {\n-      return _.diag(SPV_ERROR_INVALID_DATA)\n+      return _.diag(SPV_ERROR_INVALID_DATA, inst)\n              << spvOpcodeString(opcode)\n              << ": in Vulkan environment memory scope is limited to Device, "\n                 "Workgroup and Invocation";\n@@ -73,7 +73,7 @@ spv_result_t ValidateMemorySemantics(ValidationState_t& _,\n       _.EvalInt32IfConst(memory_semantics_id);\n \n   if (!is_int32) {\n-    return _.diag(SPV_ERROR_INVALID_DATA)\n+    return _.diag(SPV_ERROR_INVALID_DATA, inst)\n            << spvOpcodeString(opcode)\n            << ": expected Memory Semantics to be 32-bit int";\n   }\n@@ -87,7 +87,7 @@ spv_result_t ValidateMemorySemantics(ValidationState_t& _,\n           (SpvMemorySemanticsAcquireMask | SpvMemorySemanticsReleaseMask |\n            SpvMemorySemanticsAcquireReleaseMask |\n            SpvMemorySemanticsSequentiallyConsistentMask)) > 1) {\n-    return _.diag(SPV_ERROR_INVALID_DATA)\n+    return _.diag(SPV_ERROR_INVALID_DATA, inst)\n            << spvOpcodeString(opcode)\n            << ": no more than one of the following Memory Semantics bits can "\n               "be set at the same time: Acquire, Release, AcquireRelease or "\n@@ -96,14 +96,14 @@ spv_result_t ValidateMemorySemantics(ValidationState_t& _,\n \n   if (flags & SpvMemorySemanticsUniformMemoryMask &&\n       !_.HasCapability(SpvCapabilityShader)) {\n-    return _.diag(SPV_ERROR_INVALID_DATA)\n+    return _.diag(SPV_ERROR_INVALID_DATA, inst)\n            << spvOpcodeString(opcode)\n            << ": Memory Semantics UniformMemory requires capability Shader";\n   }\n \n   if (flags & SpvMemorySemanticsAtomicCounterMemoryMask &&\n       !_.HasCapability(SpvCapabilityAtomicStorage)) {\n-    return _.diag(SPV_ERROR_INVALID_DATA)\n+    return _.diag(SPV_ERROR_INVALID_DATA, inst)\n            << spvOpcodeString(opcode)\n            << ": Memory Semantics UniformMemory requires capability "\n               "AtomicStorage";\n@@ -112,7 +112,7 @@ spv_result_t ValidateMemorySemantics(ValidationState_t& _,\n   if (opcode == SpvOpAtomicFlagClear &&\n       (flags & SpvMemorySemanticsAcquireMask ||\n        flags & SpvMemorySemanticsAcquireReleaseMask)) {\n-    return _.diag(SPV_ERROR_INVALID_DATA)\n+    return _.diag(SPV_ERROR_INVALID_DATA, inst)\n            << "Memory Semantics Acquire and AcquireRelease cannot be used with "\n            << spvOpcodeString(opcode);\n   }\n@@ -120,7 +120,7 @@ spv_result_t ValidateMemorySemantics(ValidationState_t& _,\n   if (opcode == SpvOpAtomicCompareExchange && operand_index == 5 &&\n       (flags & SpvMemorySemanticsReleaseMask ||\n        flags & SpvMemorySemanticsAcquireReleaseMask)) {\n-    return _.diag(SPV_ERROR_INVALID_DATA)\n+    return _.diag(SPV_ERROR_INVALID_DATA, inst)\n            << spvOpcodeString(opcode)\n            << ": Memory Semantics Release and AcquireRelease cannot be used "\n               "for operand Unequal";\n@@ -131,7 +131,7 @@ spv_result_t ValidateMemorySemantics(ValidationState_t& _,\n         (flags & SpvMemorySemanticsReleaseMask ||\n          flags & SpvMemorySemanticsAcquireReleaseMask ||\n          flags & SpvMemorySemanticsSequentiallyConsistentMask)) {\n-      return _.diag(SPV_ERROR_INVALID_DATA)\n+      return _.diag(SPV_ERROR_INVALID_DATA, inst)\n              << "Vulkan spec disallows OpAtomicLoad with Memory Semantics "\n                 "Release, AcquireRelease and SequentiallyConsistent";\n     }\n@@ -140,7 +140,7 @@ spv_result_t ValidateMemorySemantics(ValidationState_t& _,\n         (flags & SpvMemorySemanticsAcquireMask ||\n          flags & SpvMemorySemanticsAcquireReleaseMask ||\n          flags & SpvMemorySemanticsSequentiallyConsistentMask)) {\n-      return _.diag(SPV_ERROR_INVALID_DATA)\n+      return _.diag(SPV_ERROR_INVALID_DATA, inst)\n              << "Vulkan spec disallows OpAtomicStore with Memory Semantics "\n                 "Acquire, AcquireRelease and SequentiallyConsistent";\n     }\n@@ -180,13 +180,13 @@ spv_result_t AtomicsPass(ValidationState_t& _, const Instruction* inst) {\n            opcode == SpvOpAtomicCompareExchange)) {\n         if (!_.IsFloatScalarType(result_type) &&\n             !_.IsIntScalarType(result_type)) {\n-          return _.diag(SPV_ERROR_INVALID_DATA)\n+          return _.diag(SPV_ERROR_INVALID_DATA, inst)\n                  << spvOpcodeString(opcode)\n                  << ": expected Result Type to be int or float scalar type";\n         }\n       } else if (opcode == SpvOpAtomicFlagTestAndSet) {\n         if (!_.IsBoolScalarType(result_type)) {\n-          return _.diag(SPV_ERROR_INVALID_DATA)\n+          return _.diag(SPV_ERROR_INVALID_DATA, inst)\n                  << spvOpcodeString(opcode)\n                  << ": expected Result Type to be bool scalar type";\n         }\n@@ -194,13 +194,13 @@ spv_result_t AtomicsPass(ValidationState_t& _, const Instruction* inst) {\n         assert(result_type == 0);\n       } else {\n         if (!_.IsIntScalarType(result_type)) {\n-          return _.diag(SPV_ERROR_INVALID_DATA)\n+          return _.diag(SPV_ERROR_INVALID_DATA, inst)\n                  << spvOpcodeString(opcode)\n                  << ": expected Result Type to be int scalar type";\n         }\n         if (spvIsVulkanEnv(_.context()->target_env) &&\n             _.GetBitWidth(result_type) != 32) {\n-          return _.diag(SPV_ERROR_INVALID_DATA)\n+          return _.diag(SPV_ERROR_INVALID_DATA, inst)\n                  << spvOpcodeString(opcode)\n                  << ": according to the Vulkan spec atomic Result Type needs "\n                     "to be a 32-bit int scalar type";\n@@ -214,7 +214,7 @@ spv_result_t AtomicsPass(ValidationState_t& _, const Instruction* inst) {\n       uint32_t data_type = 0;\n       uint32_t storage_class = 0;\n       if (!_.GetPointerTypeInfo(pointer_type, &data_type, &storage_class)) {\n-        return _.diag(SPV_ERROR_INVALID_DATA)\n+        return _.diag(SPV_ERROR_INVALID_DATA, inst)\n                << spvOpcodeString(opcode)\n                << ": expected Pointer to be of type OpTypePointer";\n       }\n@@ -229,7 +229,7 @@ spv_result_t AtomicsPass(ValidationState_t& _, const Instruction* inst) {\n         case SpvStorageClassStorageBuffer:\n           break;\n         default:\n-          return _.diag(SPV_ERROR_INVALID_DATA)\n+          return _.diag(SPV_ERROR_INVALID_DATA, inst)\n                  << spvOpcodeString(opcode)\n                  << ": expected Pointer Storage Class to be Uniform, "\n                     "Workgroup, CrossWorkgroup, Generic, AtomicCounter, Image "\n@@ -239,20 +239,20 @@ spv_result_t AtomicsPass(ValidationState_t& _, const Instruction* inst) {\n       if (opcode == SpvOpAtomicFlagTestAndSet ||\n           opcode == SpvOpAtomicFlagClear) {\n         if (!_.IsIntScalarType(data_type) || _.GetBitWidth(data_type) != 32) {\n-          return _.diag(SPV_ERROR_INVALID_DATA)\n+          return _.diag(SPV_ERROR_INVALID_DATA, inst)\n                  << spvOpcodeString(opcode)\n                  << ": expected Pointer to point to a value of 32-bit int type";\n         }\n       } else if (opcode == SpvOpAtomicStore) {\n         if (!_.IsFloatScalarType(data_type) && !_.IsIntScalarType(data_type)) {\n-          return _.diag(SPV_ERROR_INVALID_DATA)\n+          return _.diag(SPV_ERROR_INVALID_DATA, inst)\n                  << spvOpcodeString(opcode)\n                  << ": expected Pointer to be a pointer to int or float "\n                  << "scalar type";\n         }\n       } else {\n         if (data_type != result_type) {\n-          return _.diag(SPV_ERROR_INVALID_DATA)\n+          return _.diag(SPV_ERROR_INVALID_DATA, inst)\n                  << spvOpcodeString(opcode)\n                  << ": expected Pointer to point to a value of type Result "\n                     "Type";\n@@ -276,7 +276,7 @@ spv_result_t AtomicsPass(ValidationState_t& _, const Instruction* inst) {\n       if (opcode == SpvOpAtomicStore) {\n         const uint32_t value_type = _.GetOperandTypeId(inst, 3);\n         if (value_type != data_type) {\n-          return _.diag(SPV_ERROR_INVALID_DATA)\n+          return _.diag(SPV_ERROR_INVALID_DATA, inst)\n                  << spvOpcodeString(opcode)\n                  << ": expected Value type and the type pointed to by Pointer "\n                     "to"\n@@ -288,7 +288,7 @@ spv_result_t AtomicsPass(ValidationState_t& _, const Instruction* inst) {\n                  opcode != SpvOpAtomicFlagClear) {\n         const uint32_t value_type = _.GetOperandTypeId(inst, operand_index++);\n         if (value_type != result_type) {\n-          return _.diag(SPV_ERROR_INVALID_DATA)\n+          return _.diag(SPV_ERROR_INVALID_DATA, inst)\n                  << spvOpcodeString(opcode)\n                  << ": expected Value to be of type Result Type";\n         }\n@@ -299,7 +299,7 @@ spv_result_t AtomicsPass(ValidationState_t& _, const Instruction* inst) {\n         const uint32_t comparator_type =\n             _.GetOperandTypeId(inst, operand_index++);\n         if (comparator_type != result_type) {\n-          return _.diag(SPV_ERROR_INVALID_DATA)\n+          return _.diag(SPV_ERROR_INVALID_DATA, inst)\n                  << spvOpcodeString(opcode)\n                  << ": expected Comparator to be of type Result Type";\n         }\n', 'addition_count': 22, 'deletion_count': 22, 'src_file': 'a/source/val/validate_atomics.cpp', 'tgt_file': 'b/source/val/validate_atomics.cpp', 'file_extension': '.cpp', 'before_file': '// Copyright (c) 2017 Google Inc.\n// Modifications Copyright (C) 2020 Advanced Micro Devices, Inc. All rights\n// reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the "License");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an "AS IS" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n// Validates correctness of atomic SPIR-V instructions.\n\n#include "source/val/validate.h"\n\n#include "source/diagnostic.h"\n#include "source/opcode.h"\n#include "source/spirv_target_env.h"\n#include "source/util/bitutils.h"\n#include "source/val/instruction.h"\n#include "source/val/validate_memory_semantics.h"\n#include "source/val/validate_scopes.h"\n#include "source/val/validation_state.h"\n\nnamespace {\n\nbool IsStorageClassAllowedByUniversalRules(uint32_t storage_class) {\n  switch (storage_class) {\n    case SpvStorageClassUniform:\n  std::tie(is_int32, is_const_int32, value) = _.EvalInt32IfConst(id);\n\n  if (!is_int32) {\n    return _.diag(SPV_ERROR_INVALID_DATA)\n           << spvOpcodeString(opcode) << ": expected Scope to be 32-bit int";\n  }\n\n    case SpvStorageClassPhysicalStorageBuffer:\n    case SpvStorageClassTaskPayloadWorkgroupEXT:\n      return true;\n      break;\n    default:\n      return false;\n  if (spvIsVulkanEnv(_.context()->target_env)) {\n    if (value != SpvScopeDevice && value != SpvScopeWorkgroup &&\n        value != SpvScopeInvocation) {\n      return _.diag(SPV_ERROR_INVALID_DATA)\n             << spvOpcodeString(opcode)\n             << ": in Vulkan environment memory scope is limited to Device, "\n                "Workgroup and Invocation";\n      return false;\n      break;\n    default:\n      return true;\n  }\n}\n\nbool HasOnlyFloatReturnType(uint32_t opcode) {\n  switch (opcode) {\n    case SpvOpAtomicFAddEXT:\n    case SpvOpAtomicFMinEXT:\n    case SpvOpAtomicFMaxEXT:\n      return true;\n      break;\n    default:\n      return false;\n  }\n}\n      _.EvalInt32IfConst(memory_semantics_id);\n\n  if (!is_int32) {\n    return _.diag(SPV_ERROR_INVALID_DATA)\n           << spvOpcodeString(opcode)\n           << ": expected Memory Semantics to be 32-bit int";\n  }\n    case SpvOpAtomicIAdd:\n    case SpvOpAtomicISub:\n    case SpvOpAtomicSMin:\n    case SpvOpAtomicUMin:\n    case SpvOpAtomicSMax:\n    case SpvOpAtomicUMax:\n    case SpvOpAtomicAnd:\n          (SpvMemorySemanticsAcquireMask | SpvMemorySemanticsReleaseMask |\n           SpvMemorySemanticsAcquireReleaseMask |\n           SpvMemorySemanticsSequentiallyConsistentMask)) > 1) {\n    return _.diag(SPV_ERROR_INVALID_DATA)\n           << spvOpcodeString(opcode)\n           << ": no more than one of the following Memory Semantics bits can "\n              "be set at the same time: Acquire, Release, AcquireRelease or "\n}\n\n\n  if (flags & SpvMemorySemanticsUniformMemoryMask &&\n      !_.HasCapability(SpvCapabilityShader)) {\n    return _.diag(SPV_ERROR_INVALID_DATA)\n           << spvOpcodeString(opcode)\n           << ": Memory Semantics UniformMemory requires capability Shader";\n  }\n\n  if (flags & SpvMemorySemanticsAtomicCounterMemoryMask &&\n      !_.HasCapability(SpvCapabilityAtomicStorage)) {\n    return _.diag(SPV_ERROR_INVALID_DATA)\n           << spvOpcodeString(opcode)\n           << ": Memory Semantics UniformMemory requires capability "\n              "AtomicStorage";\n      return true;\n      break;\n  if (opcode == SpvOpAtomicFlagClear &&\n      (flags & SpvMemorySemanticsAcquireMask ||\n       flags & SpvMemorySemanticsAcquireReleaseMask)) {\n    return _.diag(SPV_ERROR_INVALID_DATA)\n           << "Memory Semantics Acquire and AcquireRelease cannot be used with "\n           << spvOpcodeString(opcode);\n  }\nnamespace spvtools {\n  if (opcode == SpvOpAtomicCompareExchange && operand_index == 5 &&\n      (flags & SpvMemorySemanticsReleaseMask ||\n       flags & SpvMemorySemanticsAcquireReleaseMask)) {\n    return _.diag(SPV_ERROR_INVALID_DATA)\n           << spvOpcodeString(opcode)\n           << ": Memory Semantics Release and AcquireRelease cannot be used "\n              "for operand Unequal";\n    case SpvOpAtomicStore:\n    case SpvOpAtomicExchange:\n    case SpvOpAtomicFAddEXT:\n    case SpvOpAtomicCompareExchange:\n        (flags & SpvMemorySemanticsReleaseMask ||\n         flags & SpvMemorySemanticsAcquireReleaseMask ||\n         flags & SpvMemorySemanticsSequentiallyConsistentMask)) {\n      return _.diag(SPV_ERROR_INVALID_DATA)\n             << "Vulkan spec disallows OpAtomicLoad with Memory Semantics "\n                "Release, AcquireRelease and SequentiallyConsistent";\n    }\n    case SpvOpAtomicFMinEXT:\n    case SpvOpAtomicSMax:\n        (flags & SpvMemorySemanticsAcquireMask ||\n         flags & SpvMemorySemanticsAcquireReleaseMask ||\n         flags & SpvMemorySemanticsSequentiallyConsistentMask)) {\n      return _.diag(SPV_ERROR_INVALID_DATA)\n             << "Vulkan spec disallows OpAtomicStore with Memory Semantics "\n                "Acquire, AcquireRelease and SequentiallyConsistent";\n    }\n      const uint32_t result_type = inst->type_id();\n\n      // All current atomics only are scalar result\n      // Validate return type first so can just check if pointer type is same\n      // (if applicable)\n      if (HasReturnType(opcode)) {\n        if (HasOnlyFloatReturnType(opcode) &&\n            !_.IsFloatScalarType(result_type)) {\n          return _.diag(SPV_ERROR_INVALID_DATA, inst)\n                 << spvOpcodeString(opcode)\n                 << ": expected Result Type to be float scalar type";\n        } else if (HasOnlyIntReturnType(opcode) &&\n                   !_.IsIntScalarType(result_type)) {\n          return _.diag(SPV_ERROR_INVALID_DATA, inst)\n                 << spvOpcodeString(opcode)\n                 << ": expected Result Type to be integer scalar type";\n        } else if (HasIntOrFloatReturnType(opcode) &&\n                   !_.IsFloatScalarType(result_type) &&\n                   !_.IsIntScalarType(result_type)) {\n          return _.diag(SPV_ERROR_INVALID_DATA, inst)\n                 << spvOpcodeString(opcode)\n                 << ": expected Result Type to be integer or float scalar type";\n        } else if (HasOnlyBoolReturnType(opcode) &&\n                   !_.IsBoolScalarType(result_type)) {\n          return _.diag(SPV_ERROR_INVALID_DATA, inst)\n                 << spvOpcodeString(opcode)\n                 << ": expected Result Type to be bool scalar type";\n        }\n      }\n\n      uint32_t operand_index = HasReturnType(opcode) ? 2 : 0;\n      const uint32_t pointer_type = _.GetOperandTypeId(inst, operand_index++);\n      uint32_t data_type = 0;\n           opcode == SpvOpAtomicCompareExchange)) {\n        if (!_.IsFloatScalarType(result_type) &&\n            !_.IsIntScalarType(result_type)) {\n          return _.diag(SPV_ERROR_INVALID_DATA)\n                 << spvOpcodeString(opcode)\n                 << ": expected Result Type to be int or float scalar type";\n        }\n      } else if (opcode == SpvOpAtomicFlagTestAndSet) {\n        if (!_.IsBoolScalarType(result_type)) {\n          return _.diag(SPV_ERROR_INVALID_DATA)\n                 << spvOpcodeString(opcode)\n                 << ": expected Result Type to be bool scalar type";\n        }\n      }\n        assert(result_type == 0);\n      } else {\n        if (!_.IsIntScalarType(result_type)) {\n          return _.diag(SPV_ERROR_INVALID_DATA)\n                 << spvOpcodeString(opcode)\n                 << ": expected Result Type to be int scalar type";\n        }\n        if (spvIsVulkanEnv(_.context()->target_env) &&\n            _.GetBitWidth(result_type) != 32) {\n          return _.diag(SPV_ERROR_INVALID_DATA)\n                 << spvOpcodeString(opcode)\n                 << ": according to the Vulkan spec atomic Result Type needs "\n                    "to be a 32-bit int scalar type";\n              (storage_class != SpvStorageClassStorageBuffer) &&\n              (storage_class != SpvStorageClassWorkgroup) &&\n              (storage_class != SpvStorageClassImage) &&\n              (storage_class != SpvStorageClassPhysicalStorageBuffer) &&\n              (storage_class != SpvStorageClassTaskPayloadWorkgroupEXT)) {\n            return _.diag(SPV_ERROR_INVALID_DATA, inst)\n                   << _.VkErrorID(4686) << spvOpcodeString(opcode)\n      uint32_t data_type = 0;\n      uint32_t storage_class = 0;\n      if (!_.GetPointerTypeInfo(pointer_type, &data_type, &storage_class)) {\n        return _.diag(SPV_ERROR_INVALID_DATA)\n               << spvOpcodeString(opcode)\n               << ": expected Pointer to be of type OpTypePointer";\n      }\n                 << ": Function storage class forbidden when the Shader "\n                    "capability is declared.";\n        }\n\n        if (opcode == SpvOpAtomicFAddEXT) {\n          // result type being float checked already\n          if ((_.GetBitWidth(result_type) == 16) &&\n              (!_.HasCapability(SpvCapabilityAtomicFloat16AddEXT))) {\n        case SpvStorageClassStorageBuffer:\n          break;\n        default:\n          return _.diag(SPV_ERROR_INVALID_DATA)\n                 << spvOpcodeString(opcode)\n                 << ": expected Pointer Storage Class to be Uniform, "\n                    "Workgroup, CrossWorkgroup, Generic, AtomicCounter, Image "\n            return _.diag(SPV_ERROR_INVALID_DATA, inst)\n                   << spvOpcodeString(opcode)\n                   << ": float add atomics require the AtomicFloat32AddEXT "\n      if (opcode == SpvOpAtomicFlagTestAndSet ||\n          opcode == SpvOpAtomicFlagClear) {\n        if (!_.IsIntScalarType(data_type) || _.GetBitWidth(data_type) != 32) {\n          return _.diag(SPV_ERROR_INVALID_DATA)\n                 << spvOpcodeString(opcode)\n                 << ": expected Pointer to point to a value of 32-bit int type";\n        }\n      } else if (opcode == SpvOpAtomicStore) {\n        if (!_.IsFloatScalarType(data_type) && !_.IsIntScalarType(data_type)) {\n          return _.diag(SPV_ERROR_INVALID_DATA)\n                 << spvOpcodeString(opcode)\n                 << ": expected Pointer to be a pointer to int or float "\n                 << "scalar type";\n        }\n      } else {\n        if (data_type != result_type) {\n          return _.diag(SPV_ERROR_INVALID_DATA)\n                 << spvOpcodeString(opcode)\n                 << ": expected Pointer to point to a value of type Result "\n                    "Type";\n            return _.diag(SPV_ERROR_INVALID_DATA, inst)\n                   << spvOpcodeString(opcode)\n                   << ": float min/max atomics require the "\n                      "AtomicFloat32MinMaxEXT capability";\n          }\n          if ((_.GetBitWidth(result_type) == 64) &&\n              (!_.HasCapability(SpvCapabilityAtomicFloat64MinMaxEXT))) {\n            return _.diag(SPV_ERROR_INVALID_DATA, inst)\n                   << spvOpcodeString(opcode)\n                   << ": float min/max atomics require the "\n                      "AtomicFloat64MinMaxEXT capability";\n          }\n        }\n      }\n\n      // And finally OpenCL environment rules\n      if (spvIsOpenCLEnv(_.context()->target_env)) {\n      if (opcode == SpvOpAtomicStore) {\n        const uint32_t value_type = _.GetOperandTypeId(inst, 3);\n        if (value_type != data_type) {\n          return _.diag(SPV_ERROR_INVALID_DATA)\n                 << spvOpcodeString(opcode)\n                 << ": expected Value type and the type pointed to by Pointer "\n                    "to"\n                    "CrossWorkGroup or Generic in the OpenCL environment.";\n        }\n\n        if (_.context()->target_env == SPV_ENV_OPENCL_1_2) {\n          if (storage_class == SpvStorageClassGeneric) {\n                 opcode != SpvOpAtomicFlagClear) {\n        const uint32_t value_type = _.GetOperandTypeId(inst, operand_index++);\n        if (value_type != result_type) {\n          return _.diag(SPV_ERROR_INVALID_DATA)\n                 << spvOpcodeString(opcode)\n                 << ": expected Value to be of type Result Type";\n        }\n      // If result and pointer type are different, need to do special check here\n      if (opcode == SpvOpAtomicFlagTestAndSet ||\n          opcode == SpvOpAtomicFlagClear) {\n        if (!_.IsIntScalarType(data_type) || _.GetBitWidth(data_type) != 32) {\n        const uint32_t comparator_type =\n            _.GetOperandTypeId(inst, operand_index++);\n        if (comparator_type != result_type) {\n          return _.diag(SPV_ERROR_INVALID_DATA)\n                 << spvOpcodeString(opcode)\n                 << ": expected Comparator to be of type Result Type";\n        }\n          return _.diag(SPV_ERROR_INVALID_DATA, inst)\n                 << spvOpcodeString(opcode)\n                 << ": expected Pointer to be a pointer to integer or float "\n                 << "scalar type";\n        }\n      } else if (data_type != result_type) {\n        return _.diag(SPV_ERROR_INVALID_DATA, inst)\n               << spvOpcodeString(opcode)\n               << ": expected Pointer to point to a value of type Result "\n                  "Type";\n      }\n\n      auto memory_scope = inst->GetOperandAs<const uint32_t>(operand_index++);\n      if (auto error = ValidateMemoryScope(_, inst, memory_scope)) {\n        return error;\n      }\n\n      const auto equal_semantics_index = operand_index++;\n      if (auto error = ValidateMemorySemantics(_, inst, equal_semantics_index,\n                                               memory_scope))\n        return error;\n\n      if (opcode == SpvOpAtomicCompareExchange ||\n          opcode == SpvOpAtomicCompareExchangeWeak) {\n        const auto unequal_semantics_index = operand_index++;\n        if (auto error = ValidateMemorySemantics(\n                _, inst, unequal_semantics_index, memory_scope))\n          return error;\n\n        // Volatile bits must match for equal and unequal semantics. Previous\n        // checks guarantee they are 32-bit constants, but we need to recheck\n        // whether they are evaluatable constants.\n        bool is_int32 = false;\n        bool is_equal_const = false;\n        bool is_unequal_const = false;\n        uint32_t equal_value = 0;\n        uint32_t unequal_value = 0;\n        std::tie(is_int32, is_equal_const, equal_value) = _.EvalInt32IfConst(\n            inst->GetOperandAs<uint32_t>(equal_semantics_index));\n        std::tie(is_int32, is_unequal_const, unequal_value) =\n            _.EvalInt32IfConst(\n                inst->GetOperandAs<uint32_t>(unequal_semantics_index));\n        if (is_equal_const && is_unequal_const &&\n            ((equal_value & SpvMemorySemanticsVolatileMask) ^\n             (unequal_value & SpvMemorySemanticsVolatileMask))) {\n          return _.diag(SPV_ERROR_INVALID_ID, inst)\n                 << "Volatile mask setting must match for Equal and Unequal "\n                    "memory semantics";\n        }\n      }\n\n      if (opcode == SpvOpAtomicStore) {\n        const uint32_t value_type = _.GetOperandTypeId(inst, 3);\n        if (value_type != data_type) {\n          return _.diag(SPV_ERROR_INVALID_DATA, inst)\n                 << spvOpcodeString(opcode)\n                 << ": expected Value type and the type pointed to by "\n                    "Pointer to be the same";\n        }\n      } else if (opcode != SpvOpAtomicLoad && opcode != SpvOpAtomicIIncrement &&\n                 opcode != SpvOpAtomicIDecrement &&\n                 opcode != SpvOpAtomicFlagTestAndSet &&\n                 opcode != SpvOpAtomicFlagClear) {\n        const uint32_t value_type = _.GetOperandTypeId(inst, operand_index++);\n        if (value_type != result_type) {\n          return _.diag(SPV_ERROR_INVALID_DATA, inst)\n                 << spvOpcodeString(opcode)\n                 << ": expected Value to be of type Result Type";\n        }\n      }\n\n      if (opcode == SpvOpAtomicCompareExchange ||\n          opcode == SpvOpAtomicCompareExchangeWeak) {\n        const uint32_t comparator_type =\n            _.GetOperandTypeId(inst, operand_index++);\n        if (comparator_type != result_type) {\n          return _.diag(SPV_ERROR_INVALID_DATA, inst)\n                 << spvOpcodeString(opcode)\n                 << ": expected Comparator to be of type Result Type";\n        }\n      }\n\n      break;\n    }\n\n    default:\n      break;\n  }\n\n  return SPV_SUCCESS;\n}\n\n}  // namespace val\n}  // namespace spvtools\n', 'message': 'Update diag() in validate_atomics (#1753)\n\nThis CL updates validate_atomics to explicitly provide the instruction\r\nwhen caling diag().', 'author_name': 'GitHub', 'lang': 'cpp'}