Add ExtInst validation pass (GLSL only for now)
authorAndrey Tuganov <andreyt@google.com>
Wed, 13 Dec 2017 16:56:09 +0000 (11:56 -0500)
committerDavid Neto <dneto@google.com>
Tue, 2 Jan 2018 21:53:25 +0000 (16:53 -0500)
Validates all GLSL.std.450 extended instructions.

Android.mk
source/CMakeLists.txt
source/validate.cpp
source/validate.h
source/validate_ext_inst.cpp [new file with mode: 0644]
test/val/CMakeLists.txt
test/val/val_ext_inst_test.cpp [new file with mode: 0644]

index 47a3004d3ceee2b0dcbfb7f778e6cb485a4d7876..48fb0874a84982e5808ad4cf320cc944e4e582bb 100644 (file)
@@ -43,6 +43,7 @@ SPVTOOLS_SRC_FILES := \
                source/validate_datarules.cpp \
                source/validate_decorations.cpp \
                source/validate_derivatives.cpp \
+               source/validate_ext_inst.cpp \
                source/validate_id.cpp \
                source/validate_image.cpp \
                source/validate_instruction.cpp \
index ce0bf7b7bb2586979d413cbf29c5d0dd0d7713fb..a306730a2908ea97848692fd688e5f0385ebd520 100644 (file)
@@ -290,6 +290,7 @@ set(SPIRV_SOURCES
   ${CMAKE_CURRENT_SOURCE_DIR}/validate_datarules.cpp
   ${CMAKE_CURRENT_SOURCE_DIR}/validate_decorations.cpp
   ${CMAKE_CURRENT_SOURCE_DIR}/validate_derivatives.cpp
+  ${CMAKE_CURRENT_SOURCE_DIR}/validate_ext_inst.cpp
   ${CMAKE_CURRENT_SOURCE_DIR}/validate_id.cpp
   ${CMAKE_CURRENT_SOURCE_DIR}/validate_image.cpp
   ${CMAKE_CURRENT_SOURCE_DIR}/validate_instruction.cpp
index 1b4a7c6de90cfb6e2927bd95c2a73938e78f782a..6bfabe1649d4a4c70fb099a2ba4e9b0b80fd63bf 100644 (file)
@@ -186,6 +186,7 @@ spv_result_t ProcessInstruction(void* user_data,
   if (auto error = DerivativesPass(_, inst)) return error;
   if (auto error = LogicalsPass(_, inst)) return error;
   if (auto error = BitwisePass(_, inst)) return error;
+  if (auto error = ExtInstPass(_, inst)) return error;
   if (auto error = ImagePass(_, inst)) return error;
   if (auto error = AtomicsPass(_, inst)) return error;
   if (auto error = PrimitivesPass(_, inst)) return error;
index db0e5f130b3047f9d9ba2e0d2e1da52cb0a2fb93..a1ea297c2993c6a4227d9846b5012c0f118c09f5 100644 (file)
@@ -147,6 +147,10 @@ spv_result_t AtomicsPass(ValidationState_t& _,
 spv_result_t LiteralsPass(ValidationState_t& _,
                           const spv_parsed_instruction_t* inst);
 
+/// Validates correctness of ExtInst instructions.
+spv_result_t ExtInstPass(ValidationState_t& _,
+                         const spv_parsed_instruction_t* inst);
+
 // Validates that capability declarations use operands allowed in the current
 // context.
 spv_result_t CapabilityPass(ValidationState_t& _,
diff --git a/source/validate_ext_inst.cpp b/source/validate_ext_inst.cpp
new file mode 100644 (file)
index 0000000..e75d886
--- /dev/null
@@ -0,0 +1,753 @@
+// Copyright (c) 2017 Google Inc.
+//
+// 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 ExtInst SPIR-V instructions.
+
+#include "validate.h"
+
+#include <sstream>
+
+#include "latest_version_glsl_std_450_header.h"
+#include "latest_version_opencl_std_header.h"
+
+#include "diagnostic.h"
+#include "opcode.h"
+#include "val/instruction.h"
+#include "val/validation_state.h"
+
+namespace libspirv {
+
+// Validates correctness of ExtInst instructions.
+spv_result_t ExtInstPass(ValidationState_t& _,
+                         const spv_parsed_instruction_t* inst) {
+  const SpvOp opcode = static_cast<SpvOp>(inst->opcode);
+  const uint32_t result_type = inst->type_id;
+  const uint32_t num_operands = inst->num_operands;
+
+  if (opcode != SpvOpExtInst) return SPV_SUCCESS;
+
+  const uint32_t ext_inst_set = inst->words[3];
+  const uint32_t ext_inst_index = inst->words[4];
+  const spv_ext_inst_type_t ext_inst_type =
+      spv_ext_inst_type_t(inst->ext_inst_type);
+
+  auto ext_inst_name = [&_, ext_inst_set, ext_inst_type, ext_inst_index]() {
+    spv_ext_inst_desc desc = nullptr;
+    if (_.grammar().lookupExtInst(ext_inst_type, ext_inst_index, &desc) !=
+            SPV_SUCCESS ||
+        !desc) {
+      return std::string("Unknown ExtInst");
+    }
+
+    auto* import_inst = _.FindDef(ext_inst_set);
+    assert(import_inst);
+
+    std::ostringstream ss;
+    ss << reinterpret_cast<const char*>(import_inst->words().data() + 2);
+    ss << " ";
+    ss << desc->name;
+
+    return ss.str();
+  };
+
+  if (ext_inst_type == SPV_EXT_INST_TYPE_GLSL_STD_450) {
+    const GLSLstd450 ext_inst_key = GLSLstd450(ext_inst_index);
+    switch (ext_inst_key) {
+      case GLSLstd450Round:
+      case GLSLstd450RoundEven:
+      case GLSLstd450FAbs:
+      case GLSLstd450Trunc:
+      case GLSLstd450FSign:
+      case GLSLstd450Floor:
+      case GLSLstd450Ceil:
+      case GLSLstd450Fract:
+      case GLSLstd450Sqrt:
+      case GLSLstd450InverseSqrt:
+      case GLSLstd450FMin:
+      case GLSLstd450FMax:
+      case GLSLstd450FClamp:
+      case GLSLstd450FMix:
+      case GLSLstd450Step:
+      case GLSLstd450SmoothStep:
+      case GLSLstd450Fma:
+      case GLSLstd450Normalize:
+      case GLSLstd450FaceForward:
+      case GLSLstd450Reflect:
+      case GLSLstd450NMin:
+      case GLSLstd450NMax:
+      case GLSLstd450NClamp: {
+        if (!_.IsFloatScalarOrVectorType(result_type)) {
+          return _.diag(SPV_ERROR_INVALID_DATA)
+                 << ext_inst_name() << ": "
+                 << "expected Result Type to be a float scalar or vector type";
+        }
+
+        for (uint32_t operand_index = 4; operand_index < num_operands;
+             ++operand_index) {
+          const uint32_t operand_type = _.GetOperandTypeId(inst, operand_index);
+          if (result_type != operand_type) {
+            return _.diag(SPV_ERROR_INVALID_DATA)
+                   << ext_inst_name() << ": "
+                   << "expected types of all operands to be equal to Result "
+                      "Type";
+          }
+        }
+        break;
+      }
+
+      case GLSLstd450SAbs:
+      case GLSLstd450SSign:
+      case GLSLstd450UMin:
+      case GLSLstd450SMin:
+      case GLSLstd450UMax:
+      case GLSLstd450SMax:
+      case GLSLstd450UClamp:
+      case GLSLstd450SClamp:
+      case GLSLstd450FindILsb:
+      case GLSLstd450FindUMsb:
+      case GLSLstd450FindSMsb: {
+        if (!_.IsIntScalarOrVectorType(result_type)) {
+          return _.diag(SPV_ERROR_INVALID_DATA)
+                 << ext_inst_name() << ": "
+                 << "expected Result Type to be an int scalar or vector type";
+        }
+
+        const uint32_t result_type_bit_width = _.GetBitWidth(result_type);
+        const uint32_t result_type_dimension = _.GetDimension(result_type);
+
+        for (uint32_t operand_index = 4; operand_index < num_operands;
+             ++operand_index) {
+          const uint32_t operand_type = _.GetOperandTypeId(inst, operand_index);
+          if (!_.IsIntScalarOrVectorType(operand_type)) {
+            return _.diag(SPV_ERROR_INVALID_DATA)
+                   << ext_inst_name() << ": "
+                   << "expected all operands to be int scalars or vectors";
+          }
+
+          if (result_type_dimension != _.GetDimension(operand_type)) {
+            return _.diag(SPV_ERROR_INVALID_DATA)
+                   << ext_inst_name() << ": "
+                   << "expected all operands to have the same dimension as "
+                   << "Result Type";
+          }
+
+          if (result_type_bit_width != _.GetBitWidth(operand_type)) {
+            return _.diag(SPV_ERROR_INVALID_DATA)
+                   << ext_inst_name() << ": "
+                   << "expected all operands to have the same bit width as "
+                   << "Result Type";
+          }
+
+          if (ext_inst_key == GLSLstd450FindUMsb ||
+              ext_inst_key == GLSLstd450FindSMsb) {
+            if (result_type_bit_width != 32) {
+              return _.diag(SPV_ERROR_INVALID_DATA)
+                     << ext_inst_name() << ": "
+                     << "this instruction is currently limited to 32-bit width "
+                     << "components";
+            }
+          }
+        }
+        break;
+      }
+
+      case GLSLstd450Radians:
+      case GLSLstd450Degrees:
+      case GLSLstd450Sin:
+      case GLSLstd450Cos:
+      case GLSLstd450Tan:
+      case GLSLstd450Asin:
+      case GLSLstd450Acos:
+      case GLSLstd450Atan:
+      case GLSLstd450Sinh:
+      case GLSLstd450Cosh:
+      case GLSLstd450Tanh:
+      case GLSLstd450Asinh:
+      case GLSLstd450Acosh:
+      case GLSLstd450Atanh:
+      case GLSLstd450Exp:
+      case GLSLstd450Exp2:
+      case GLSLstd450Log:
+      case GLSLstd450Log2:
+      case GLSLstd450Atan2:
+      case GLSLstd450Pow: {
+        if (!_.IsFloatScalarOrVectorType(result_type)) {
+          return _.diag(SPV_ERROR_INVALID_DATA)
+                 << ext_inst_name() << ": "
+                 << "expected Result Type to be a 16 or 32-bit scalar or "
+                    "vector float type";
+        }
+
+        const uint32_t result_type_bit_width = _.GetBitWidth(result_type);
+        if (result_type_bit_width != 16 && result_type_bit_width != 32) {
+          return _.diag(SPV_ERROR_INVALID_DATA)
+                 << ext_inst_name() << ": "
+                 << "expected Result Type to be a 16 or 32-bit scalar or "
+                    "vector float type";
+        }
+
+        for (uint32_t operand_index = 4; operand_index < num_operands;
+             ++operand_index) {
+          const uint32_t operand_type = _.GetOperandTypeId(inst, operand_index);
+          if (result_type != operand_type) {
+            return _.diag(SPV_ERROR_INVALID_DATA)
+                   << ext_inst_name() << ": "
+                   << "expected types of all operands to be equal to Result "
+                      "Type";
+          }
+        }
+        break;
+      }
+
+      case GLSLstd450Determinant: {
+        const uint32_t x_type = _.GetOperandTypeId(inst, 4);
+        uint32_t num_rows = 0;
+        uint32_t num_cols = 0;
+        uint32_t col_type = 0;
+        uint32_t component_type = 0;
+        if (!_.GetMatrixTypeInfo(x_type, &num_rows, &num_cols, &col_type,
+                                 &component_type) ||
+            num_rows != num_cols) {
+          return _.diag(SPV_ERROR_INVALID_DATA)
+                 << ext_inst_name() << ": "
+                 << "expected operand X to be a square matrix";
+        }
+
+        if (result_type != component_type) {
+          return _.diag(SPV_ERROR_INVALID_DATA)
+                 << ext_inst_name() << ": "
+                 << "expected operand X component type to be equal to "
+                 << "Result Type";
+        }
+        break;
+      }
+
+      case GLSLstd450MatrixInverse: {
+        uint32_t num_rows = 0;
+        uint32_t num_cols = 0;
+        uint32_t col_type = 0;
+        uint32_t component_type = 0;
+        if (!_.GetMatrixTypeInfo(result_type, &num_rows, &num_cols, &col_type,
+                                 &component_type) ||
+            num_rows != num_cols) {
+          return _.diag(SPV_ERROR_INVALID_DATA)
+                 << ext_inst_name() << ": "
+                 << "expected Result Type to be a square matrix";
+        }
+
+        const uint32_t x_type = _.GetOperandTypeId(inst, 4);
+        if (result_type != x_type) {
+          return _.diag(SPV_ERROR_INVALID_DATA)
+                 << ext_inst_name() << ": "
+                 << "expected operand X type to be equal to Result Type";
+        }
+        break;
+      }
+
+      case GLSLstd450Modf: {
+        if (!_.IsFloatScalarOrVectorType(result_type)) {
+          return _.diag(SPV_ERROR_INVALID_DATA)
+                 << ext_inst_name() << ": "
+                 << "expected Result Type to be a scalar or vector float type";
+        }
+
+        const uint32_t x_type = _.GetOperandTypeId(inst, 4);
+        const uint32_t i_type = _.GetOperandTypeId(inst, 5);
+
+        if (x_type != result_type) {
+          return _.diag(SPV_ERROR_INVALID_DATA)
+                 << ext_inst_name() << ": "
+                 << "expected operand X type to be equal to Result Type";
+        }
+
+        uint32_t i_storage_class = 0;
+        uint32_t i_data_type = 0;
+        if (!_.GetPointerTypeInfo(i_type, &i_data_type, &i_storage_class)) {
+          return _.diag(SPV_ERROR_INVALID_DATA)
+                 << ext_inst_name() << ": "
+                 << "expected operand I to be a pointer";
+        }
+
+        if (i_data_type != result_type) {
+          return _.diag(SPV_ERROR_INVALID_DATA)
+                 << ext_inst_name() << ": "
+                 << "expected operand I data type to be equal to Result Type";
+        }
+
+        break;
+      }
+
+      case GLSLstd450ModfStruct: {
+        std::vector<uint32_t> result_types;
+        if (!_.GetStructMemberTypes(result_type, &result_types) ||
+            result_types.size() != 2 ||
+            !_.IsFloatScalarOrVectorType(result_types[0]) ||
+            result_types[1] != result_types[0]) {
+          return _.diag(SPV_ERROR_INVALID_DATA)
+                 << ext_inst_name() << ": "
+                 << "expected Result Type to be a struct with two identical "
+                 << "scalar or vector float type members";
+        }
+
+        const uint32_t x_type = _.GetOperandTypeId(inst, 4);
+        if (x_type != result_types[0]) {
+          return _.diag(SPV_ERROR_INVALID_DATA)
+                 << ext_inst_name() << ": "
+                 << "expected operand X type to be equal to members of "
+                 << "Result Type struct";
+        }
+        break;
+      }
+
+      case GLSLstd450Frexp: {
+        if (!_.IsFloatScalarOrVectorType(result_type)) {
+          return _.diag(SPV_ERROR_INVALID_DATA)
+                 << ext_inst_name() << ": "
+                 << "expected Result Type to be a scalar or vector float type";
+        }
+
+        const uint32_t x_type = _.GetOperandTypeId(inst, 4);
+        const uint32_t exp_type = _.GetOperandTypeId(inst, 5);
+
+        if (x_type != result_type) {
+          return _.diag(SPV_ERROR_INVALID_DATA)
+                 << ext_inst_name() << ": "
+                 << "expected operand X type to be equal to Result Type";
+        }
+
+        uint32_t exp_storage_class = 0;
+        uint32_t exp_data_type = 0;
+        if (!_.GetPointerTypeInfo(exp_type, &exp_data_type,
+                                  &exp_storage_class)) {
+          return _.diag(SPV_ERROR_INVALID_DATA)
+                 << ext_inst_name() << ": "
+                 << "expected operand Exp to be a pointer";
+        }
+
+        if (!_.IsIntScalarOrVectorType(exp_data_type) ||
+            _.GetBitWidth(exp_data_type) != 32) {
+          return _.diag(SPV_ERROR_INVALID_DATA)
+                 << ext_inst_name() << ": "
+                 << "expected operand Exp data type to be a 32-bit int scalar "
+                 << "or vector type";
+        }
+
+        if (_.GetDimension(result_type) != _.GetDimension(exp_data_type)) {
+          return _.diag(SPV_ERROR_INVALID_DATA)
+                 << ext_inst_name() << ": "
+                 << "expected operand Exp data type to have the same component "
+                 << "number as Result Type";
+        }
+
+        break;
+      }
+
+      case GLSLstd450Ldexp: {
+        if (!_.IsFloatScalarOrVectorType(result_type)) {
+          return _.diag(SPV_ERROR_INVALID_DATA)
+                 << ext_inst_name() << ": "
+                 << "expected Result Type to be a scalar or vector float type";
+        }
+
+        const uint32_t x_type = _.GetOperandTypeId(inst, 4);
+        const uint32_t exp_type = _.GetOperandTypeId(inst, 5);
+
+        if (x_type != result_type) {
+          return _.diag(SPV_ERROR_INVALID_DATA)
+                 << ext_inst_name() << ": "
+                 << "expected operand X type to be equal to Result Type";
+        }
+
+        if (!_.IsIntScalarOrVectorType(exp_type)) {
+          return _.diag(SPV_ERROR_INVALID_DATA)
+                 << ext_inst_name() << ": "
+                 << "expected operand Exp to be a 32-bit int scalar "
+                 << "or vector type";
+        }
+
+        if (_.GetDimension(result_type) != _.GetDimension(exp_type)) {
+          return _.diag(SPV_ERROR_INVALID_DATA)
+                 << ext_inst_name() << ": "
+                 << "expected operand Exp to have the same component "
+                 << "number as Result Type";
+        }
+
+        break;
+      }
+
+      case GLSLstd450FrexpStruct: {
+        std::vector<uint32_t> result_types;
+        if (!_.GetStructMemberTypes(result_type, &result_types) ||
+            result_types.size() != 2 ||
+            !_.IsFloatScalarOrVectorType(result_types[0]) ||
+            !_.IsIntScalarOrVectorType(result_types[1]) ||
+            _.GetBitWidth(result_types[1]) != 32 ||
+            _.GetDimension(result_types[0]) !=
+                _.GetDimension(result_types[1])) {
+          return _.diag(SPV_ERROR_INVALID_DATA)
+                 << ext_inst_name() << ": "
+                 << "expected Result Type to be a struct with two members, "
+                 << "first member a float scalar or vector, second member "
+                 << "a 32-bit int scalar or vector with the same number of "
+                 << "components as the first member";
+        }
+
+        const uint32_t x_type = _.GetOperandTypeId(inst, 4);
+        if (x_type != result_types[0]) {
+          return _.diag(SPV_ERROR_INVALID_DATA)
+                 << ext_inst_name() << ": "
+                 << "expected operand X type to be equal to the first member "
+                 << "of Result Type struct";
+        }
+        break;
+      }
+
+      case GLSLstd450PackSnorm4x8:
+      case GLSLstd450PackUnorm4x8: {
+        if (!_.IsIntScalarType(result_type) ||
+            _.GetBitWidth(result_type) != 32) {
+          return _.diag(SPV_ERROR_INVALID_DATA)
+                 << ext_inst_name() << ": "
+                 << "expected Result Type to be 32-bit int scalar type";
+        }
+
+        const uint32_t v_type = _.GetOperandTypeId(inst, 4);
+        if (!_.IsFloatVectorType(v_type) || _.GetDimension(v_type) != 4 ||
+            _.GetBitWidth(v_type) != 32) {
+          return _.diag(SPV_ERROR_INVALID_DATA)
+                 << ext_inst_name() << ": "
+                 << "expected operand V to be a 32-bit float vector of size 4";
+        }
+        break;
+      }
+
+      case GLSLstd450PackSnorm2x16:
+      case GLSLstd450PackUnorm2x16:
+      case GLSLstd450PackHalf2x16: {
+        if (!_.IsIntScalarType(result_type) ||
+            _.GetBitWidth(result_type) != 32) {
+          return _.diag(SPV_ERROR_INVALID_DATA)
+                 << ext_inst_name() << ": "
+                 << "expected Result Type to be 32-bit int scalar type";
+        }
+
+        const uint32_t v_type = _.GetOperandTypeId(inst, 4);
+        if (!_.IsFloatVectorType(v_type) || _.GetDimension(v_type) != 2 ||
+            _.GetBitWidth(v_type) != 32) {
+          return _.diag(SPV_ERROR_INVALID_DATA)
+                 << ext_inst_name() << ": "
+                 << "expected operand V to be a 32-bit float vector of size 2";
+        }
+        break;
+      }
+
+      case GLSLstd450PackDouble2x32: {
+        if (!_.IsFloatScalarType(result_type) ||
+            _.GetBitWidth(result_type) != 64) {
+          return _.diag(SPV_ERROR_INVALID_DATA)
+                 << ext_inst_name() << ": "
+                 << "expected Result Type to be 64-bit float scalar type";
+        }
+
+        const uint32_t v_type = _.GetOperandTypeId(inst, 4);
+        if (!_.IsFloatVectorType(v_type) || _.GetDimension(v_type) != 2 ||
+            _.GetBitWidth(v_type) != 32) {
+          return _.diag(SPV_ERROR_INVALID_DATA)
+                 << ext_inst_name() << ": "
+                 << "expected operand V to be a 32-bit float vector of size 2";
+        }
+        break;
+      }
+
+      case GLSLstd450UnpackSnorm4x8:
+      case GLSLstd450UnpackUnorm4x8: {
+        if (!_.IsFloatVectorType(result_type) ||
+            _.GetDimension(result_type) != 4 ||
+            _.GetBitWidth(result_type) != 32) {
+          return _.diag(SPV_ERROR_INVALID_DATA)
+                 << ext_inst_name() << ": "
+                 << "expected Result Type to be a 32-bit float vector of size "
+                    "4";
+        }
+
+        const uint32_t v_type = _.GetOperandTypeId(inst, 4);
+        if (!_.IsIntScalarType(v_type) || _.GetBitWidth(v_type) != 32) {
+          return _.diag(SPV_ERROR_INVALID_DATA)
+                 << ext_inst_name() << ": "
+                 << "expected operand P to be a 32-bit int scalar";
+        }
+        break;
+      }
+
+      case GLSLstd450UnpackSnorm2x16:
+      case GLSLstd450UnpackUnorm2x16:
+      case GLSLstd450UnpackHalf2x16: {
+        if (!_.IsFloatVectorType(result_type) ||
+            _.GetDimension(result_type) != 2 ||
+            _.GetBitWidth(result_type) != 32) {
+          return _.diag(SPV_ERROR_INVALID_DATA)
+                 << ext_inst_name() << ": "
+                 << "expected Result Type to be a 32-bit float vector of size "
+                    "2";
+        }
+
+        const uint32_t v_type = _.GetOperandTypeId(inst, 4);
+        if (!_.IsIntScalarType(v_type) || _.GetBitWidth(v_type) != 32) {
+          return _.diag(SPV_ERROR_INVALID_DATA)
+                 << ext_inst_name() << ": "
+                 << "expected operand P to be a 32-bit int scalar";
+        }
+        break;
+      }
+
+      case GLSLstd450UnpackDouble2x32: {
+        if (!_.IsFloatVectorType(result_type) ||
+            _.GetDimension(result_type) != 2 ||
+            _.GetBitWidth(result_type) != 32) {
+          return _.diag(SPV_ERROR_INVALID_DATA)
+                 << ext_inst_name() << ": "
+                 << "expected Result Type to be a 32-bit float vector of size "
+                    "2";
+        }
+
+        const uint32_t v_type = _.GetOperandTypeId(inst, 4);
+        if (!_.IsFloatScalarType(v_type) || _.GetBitWidth(v_type) != 64) {
+          return _.diag(SPV_ERROR_INVALID_DATA)
+                 << ext_inst_name() << ": "
+                 << "expected operand V to be a 64-bit float scalar";
+        }
+        break;
+      }
+
+      case GLSLstd450Length: {
+        if (!_.IsFloatScalarType(result_type)) {
+          return _.diag(SPV_ERROR_INVALID_DATA)
+                 << ext_inst_name() << ": "
+                 << "expected Result Type to be a float scalar type";
+        }
+
+        const uint32_t x_type = _.GetOperandTypeId(inst, 4);
+        if (!_.IsFloatScalarOrVectorType(x_type)) {
+          return _.diag(SPV_ERROR_INVALID_DATA)
+                 << ext_inst_name() << ": "
+                 << "expected operand X to be of float scalar or vector type";
+        }
+
+        if (result_type != _.GetComponentType(x_type)) {
+          return _.diag(SPV_ERROR_INVALID_DATA)
+                 << ext_inst_name() << ": "
+                 << "expected operand X component type to be equal to Result "
+                    "Type";
+        }
+        break;
+      }
+
+      case GLSLstd450Distance: {
+        if (!_.IsFloatScalarType(result_type)) {
+          return _.diag(SPV_ERROR_INVALID_DATA)
+                 << ext_inst_name() << ": "
+                 << "expected Result Type to be a float scalar type";
+        }
+
+        const uint32_t p0_type = _.GetOperandTypeId(inst, 4);
+        if (!_.IsFloatScalarOrVectorType(p0_type)) {
+          return _.diag(SPV_ERROR_INVALID_DATA)
+                 << ext_inst_name() << ": "
+                 << "expected operand P0 to be of float scalar or vector type";
+        }
+
+        if (result_type != _.GetComponentType(p0_type)) {
+          return _.diag(SPV_ERROR_INVALID_DATA)
+                 << ext_inst_name() << ": "
+                 << "expected operand P0 component type to be equal to "
+                 << "Result Type";
+        }
+
+        const uint32_t p1_type = _.GetOperandTypeId(inst, 5);
+        if (!_.IsFloatScalarOrVectorType(p1_type)) {
+          return _.diag(SPV_ERROR_INVALID_DATA)
+                 << ext_inst_name() << ": "
+                 << "expected operand P1 to be of float scalar or vector type";
+        }
+
+        if (result_type != _.GetComponentType(p1_type)) {
+          return _.diag(SPV_ERROR_INVALID_DATA)
+                 << ext_inst_name() << ": "
+                 << "expected operand P1 component type to be equal to "
+                 << "Result Type";
+        }
+
+        if (_.GetDimension(p0_type) != _.GetDimension(p1_type)) {
+          return _.diag(SPV_ERROR_INVALID_DATA)
+                 << ext_inst_name() << ": "
+                 << "expected operands P0 and P1 to have the same number of "
+                 << "components";
+        }
+        break;
+      }
+
+      case GLSLstd450Cross: {
+        if (!_.IsFloatVectorType(result_type)) {
+          return _.diag(SPV_ERROR_INVALID_DATA)
+                 << ext_inst_name() << ": "
+                 << "expected Result Type to be a float vector type";
+        }
+
+        if (_.GetDimension(result_type) != 3) {
+          return _.diag(SPV_ERROR_INVALID_DATA)
+                 << ext_inst_name() << ": "
+                 << "expected Result Type to have 3 components";
+        }
+
+        const uint32_t x_type = _.GetOperandTypeId(inst, 4);
+        const uint32_t y_type = _.GetOperandTypeId(inst, 5);
+
+        if (x_type != result_type) {
+          return _.diag(SPV_ERROR_INVALID_DATA)
+                 << ext_inst_name() << ": "
+                 << "expected operand X type to be equal to Result Type";
+        }
+
+        if (y_type != result_type) {
+          return _.diag(SPV_ERROR_INVALID_DATA)
+                 << ext_inst_name() << ": "
+                 << "expected operand Y type to be equal to Result Type";
+        }
+        break;
+      }
+
+      case GLSLstd450Refract: {
+        if (!_.IsFloatScalarOrVectorType(result_type)) {
+          return _.diag(SPV_ERROR_INVALID_DATA)
+                 << ext_inst_name() << ": "
+                 << "expected Result Type to be a float scalar or vector type";
+        }
+
+        const uint32_t i_type = _.GetOperandTypeId(inst, 4);
+        const uint32_t n_type = _.GetOperandTypeId(inst, 5);
+        const uint32_t eta_type = _.GetOperandTypeId(inst, 6);
+
+        if (result_type != i_type) {
+          return _.diag(SPV_ERROR_INVALID_DATA)
+                 << ext_inst_name() << ": "
+                 << "expected operand I to be of type equal to Result Type";
+        }
+
+        if (result_type != n_type) {
+          return _.diag(SPV_ERROR_INVALID_DATA)
+                 << ext_inst_name() << ": "
+                 << "expected operand N to be of type equal to Result Type";
+        }
+
+        const uint32_t eta_type_bit_width = _.GetBitWidth(eta_type);
+        if (!_.IsFloatScalarType(eta_type) ||
+            (eta_type_bit_width != 16 && eta_type_bit_width != 32)) {
+          return _.diag(SPV_ERROR_INVALID_DATA)
+                 << ext_inst_name() << ": "
+                 << "expected operand Eta to be a 16 or 32-bit float scalar";
+        }
+        break;
+      }
+
+      case GLSLstd450InterpolateAtCentroid:
+      case GLSLstd450InterpolateAtSample:
+      case GLSLstd450InterpolateAtOffset: {
+        if (!_.HasCapability(SpvCapabilityInterpolationFunction)) {
+          return _.diag(SPV_ERROR_INVALID_CAPABILITY)
+                 << ext_inst_name()
+                 << " requires capability InterpolationFunction";
+        }
+
+        if (!_.IsFloatScalarOrVectorType(result_type) ||
+            _.GetBitWidth(result_type) != 32) {
+          return _.diag(SPV_ERROR_INVALID_DATA)
+                 << ext_inst_name() << ": "
+                 << "expected Result Type to be a 32-bit float scalar "
+                 << "or vector type";
+        }
+
+        const uint32_t interpolant_type = _.GetOperandTypeId(inst, 4);
+        uint32_t interpolant_storage_class = 0;
+        uint32_t interpolant_data_type = 0;
+        if (!_.GetPointerTypeInfo(interpolant_type, &interpolant_data_type,
+                                  &interpolant_storage_class)) {
+          return _.diag(SPV_ERROR_INVALID_DATA)
+                 << ext_inst_name() << ": "
+                 << "expected Interpolant to be a pointer";
+        }
+
+        if (result_type != interpolant_data_type) {
+          return _.diag(SPV_ERROR_INVALID_DATA)
+                 << ext_inst_name() << ": "
+                 << "expected Interpolant data type to be equal to Result Type";
+        }
+
+        if (interpolant_storage_class != SpvStorageClassInput) {
+          return _.diag(SPV_ERROR_INVALID_DATA)
+                 << ext_inst_name() << ": "
+                 << "expected Interpolant storage class to be Input";
+        }
+
+        if (ext_inst_key == GLSLstd450InterpolateAtSample) {
+          const uint32_t sample_type = _.GetOperandTypeId(inst, 5);
+          if (!_.IsIntScalarType(sample_type) ||
+              _.GetBitWidth(sample_type) != 32) {
+            return _.diag(SPV_ERROR_INVALID_DATA)
+                   << ext_inst_name() << ": "
+                   << "expected Sample to be 32-bit integer";
+          }
+        }
+
+        if (ext_inst_key == GLSLstd450InterpolateAtOffset) {
+          const uint32_t offset_type = _.GetOperandTypeId(inst, 5);
+          if (!_.IsFloatVectorType(offset_type) ||
+              _.GetDimension(offset_type) != 2 ||
+              _.GetBitWidth(offset_type) != 32) {
+            return _.diag(SPV_ERROR_INVALID_DATA)
+                   << ext_inst_name() << ": "
+                   << "expected Offset to be a vector of 2 32-bit floats";
+          }
+        }
+
+        _.current_function().RegisterExecutionModelLimitation(
+            SpvExecutionModelFragment,
+            ext_inst_name() +
+                std::string(" requires Fragment execution model"));
+        break;
+      }
+
+      case GLSLstd450IMix: {
+        return _.diag(SPV_ERROR_INVALID_DATA)
+               << "Extended instruction GLSLstd450IMix is not supported";
+      }
+
+      case GLSLstd450Bad: {
+        return _.diag(SPV_ERROR_INVALID_DATA)
+               << "Encountered extended instruction GLSLstd450Bad";
+      }
+
+      case GLSLstd450Count: {
+        assert(0);
+        break;
+      }
+    }
+  } else if (ext_inst_type == SPV_EXT_INST_TYPE_OPENCL_STD) {
+    // TODO(atgoo@github.com) Add validation rules for OpenCL extended
+    // instructions.
+  }
+
+  return SPV_SUCCESS;
+}
+
+}  // namespace libspirv
index 6e941f3c71aff2f38959cfc9b59f71e84904326f..c290755c66a1c470bf4863389a1bcdfe4142dff8 100644 (file)
@@ -128,6 +128,12 @@ add_spvtools_unittest(TARGET val_primitives
   LIBS ${SPIRV_TOOLS}
 )
 
+add_spvtools_unittest(TARGET val_ext_inst
+       SRCS val_ext_inst_test.cpp
+       ${VAL_TEST_COMMON_SRCS}
+  LIBS ${SPIRV_TOOLS}
+)
+
 add_spvtools_unittest(TARGET val_limits
        SRCS val_limits_test.cpp
        ${VAL_TEST_COMMON_SRCS}
diff --git a/test/val/val_ext_inst_test.cpp b/test/val/val_ext_inst_test.cpp
new file mode 100644 (file)
index 0000000..38f1696
--- /dev/null
@@ -0,0 +1,2518 @@
+// Copyright (c) 2017 Google Inc.
+//
+// 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.
+
+#include <sstream>
+#include <string>
+
+#include "gmock/gmock.h"
+#include "unit_spirv.h"
+#include "val_fixtures.h"
+
+namespace {
+
+using ::testing::HasSubstr;
+using ::testing::Not;
+
+using ValidateExtInst = spvtest::ValidateBase<bool>;
+using ValidateGlslStd450SqrtLike = spvtest::ValidateBase<std::string>;
+using ValidateGlslStd450FMinLike = spvtest::ValidateBase<std::string>;
+using ValidateGlslStd450FClampLike = spvtest::ValidateBase<std::string>;
+using ValidateGlslStd450SAbsLike = spvtest::ValidateBase<std::string>;
+using ValidateGlslStd450UMinLike = spvtest::ValidateBase<std::string>;
+using ValidateGlslStd450UClampLike = spvtest::ValidateBase<std::string>;
+using ValidateGlslStd450SinLike = spvtest::ValidateBase<std::string>;
+using ValidateGlslStd450PowLike = spvtest::ValidateBase<std::string>;
+using ValidateGlslStd450Pack = spvtest::ValidateBase<std::string>;
+using ValidateGlslStd450Unpack = spvtest::ValidateBase<std::string>;
+
+// Returns number of components in Pack/Unpack extended instructions.
+// |ext_inst_name| is expected to be of the format "PackHalf2x16".
+// Number of components is assumed to be single-digit.
+uint32_t GetPackedNumComponents(const std::string& ext_inst_name) {
+  const size_t x_index = ext_inst_name.find_last_of('x');
+  const std::string num_components_str =
+      ext_inst_name.substr(x_index - 1, x_index);
+  return uint32_t(std::stoul(num_components_str));
+}
+
+// Returns packed bit width in Pack/Unpack extended instructions.
+// |ext_inst_name| is expected to be of the format "PackHalf2x16".
+uint32_t GetPackedBitWidth(const std::string& ext_inst_name) {
+  const size_t x_index = ext_inst_name.find_last_of('x');
+  const std::string packed_bit_width_str = ext_inst_name.substr(x_index + 1);
+  return uint32_t(std::stoul(packed_bit_width_str));
+}
+
+std::string GenerateShaderCode(
+    const std::string& body,
+    const std::string& capabilities_and_extensions = "",
+    const std::string& execution_model = "Fragment") {
+  std::ostringstream ss;
+  ss << R"(
+OpCapability Shader
+OpCapability Float16
+OpCapability Float64
+OpCapability Int16
+OpCapability Int64
+)";
+
+  ss << capabilities_and_extensions;
+  ss << "%extinst = OpExtInstImport \"GLSL.std.450\"\n";
+  ss << "OpMemoryModel Logical GLSL450\n";
+  ss << "OpEntryPoint " << execution_model << " %main \"main\"\n";
+
+  ss << R"(
+%void = OpTypeVoid
+%func = OpTypeFunction %void
+%bool = OpTypeBool
+%f16 = OpTypeFloat 16
+%f32 = OpTypeFloat 32
+%f64 = OpTypeFloat 64
+%u32 = OpTypeInt 32 0
+%s32 = OpTypeInt 32 1
+%u64 = OpTypeInt 64 0
+%s64 = OpTypeInt 64 1
+%u16 = OpTypeInt 16 0
+%s16 = OpTypeInt 16 1
+%f32vec2 = OpTypeVector %f32 2
+%f32vec3 = OpTypeVector %f32 3
+%f32vec4 = OpTypeVector %f32 4
+%f64vec2 = OpTypeVector %f64 2
+%f64vec3 = OpTypeVector %f64 3
+%f64vec4 = OpTypeVector %f64 4
+%u32vec2 = OpTypeVector %u32 2
+%u32vec3 = OpTypeVector %u32 3
+%s32vec2 = OpTypeVector %s32 2
+%u32vec4 = OpTypeVector %u32 4
+%s32vec4 = OpTypeVector %s32 4
+%u64vec2 = OpTypeVector %u64 2
+%s64vec2 = OpTypeVector %s64 2
+%f64mat22 = OpTypeMatrix %f64vec2 2
+%f32mat22 = OpTypeMatrix %f32vec2 2
+%f32mat23 = OpTypeMatrix %f32vec2 3
+%f32mat32 = OpTypeMatrix %f32vec3 2
+%f32mat33 = OpTypeMatrix %f32vec3 3
+
+%f32_0 = OpConstant %f32 0
+%f32_1 = OpConstant %f32 1
+%f32_2 = OpConstant %f32 2
+%f32_3 = OpConstant %f32 3
+%f32_4 = OpConstant %f32 4
+%f32_h = OpConstant %f32 0.5
+%f32vec2_01 = OpConstantComposite %f32vec2 %f32_0 %f32_1
+%f32vec2_12 = OpConstantComposite %f32vec2 %f32_1 %f32_2
+%f32vec3_012 = OpConstantComposite %f32vec3 %f32_0 %f32_1 %f32_2
+%f32vec3_123 = OpConstantComposite %f32vec3 %f32_1 %f32_2 %f32_3
+%f32vec4_0123 = OpConstantComposite %f32vec4 %f32_0 %f32_1 %f32_2 %f32_3
+%f32vec4_1234 = OpConstantComposite %f32vec4 %f32_1 %f32_2 %f32_3 %f32_4
+
+%f64_0 = OpConstant %f64 0
+%f64_1 = OpConstant %f64 1
+%f64_2 = OpConstant %f64 2
+%f64_3 = OpConstant %f64 3
+%f64vec2_01 = OpConstantComposite %f64vec2 %f64_0 %f64_1
+%f64vec3_012 = OpConstantComposite %f64vec3 %f64_0 %f64_1 %f64_2
+%f64vec4_0123 = OpConstantComposite %f64vec4 %f64_0 %f64_1 %f64_2 %f64_3
+
+%f16_0 = OpConstant %f16 0
+%f16_1 = OpConstant %f16 1
+
+%u32_0 = OpConstant %u32 0
+%u32_1 = OpConstant %u32 1
+%u32_2 = OpConstant %u32 2
+%u32_3 = OpConstant %u32 3
+
+%s32_0 = OpConstant %s32 0
+%s32_1 = OpConstant %s32 1
+%s32_2 = OpConstant %s32 2
+%s32_3 = OpConstant %s32 3
+
+%u64_0 = OpConstant %u64 0
+%u64_1 = OpConstant %u64 1
+%u64_2 = OpConstant %u64 2
+%u64_3 = OpConstant %u64 3
+
+%s64_0 = OpConstant %s64 0
+%s64_1 = OpConstant %s64 1
+%s64_2 = OpConstant %s64 2
+%s64_3 = OpConstant %s64 3
+
+%s32vec2_01 = OpConstantComposite %s32vec2 %s32_0 %s32_1
+%u32vec2_01 = OpConstantComposite %u32vec2 %u32_0 %u32_1
+
+%s32vec2_12 = OpConstantComposite %s32vec2 %s32_1 %s32_2
+%u32vec2_12 = OpConstantComposite %u32vec2 %u32_1 %u32_2
+
+%s32vec4_0123 = OpConstantComposite %s32vec4 %s32_0 %s32_1 %s32_2 %s32_3
+%u32vec4_0123 = OpConstantComposite %u32vec4 %u32_0 %u32_1 %u32_2 %u32_3
+
+%s64vec2_01 = OpConstantComposite %s64vec2 %s64_0 %s64_1
+%u64vec2_01 = OpConstantComposite %u64vec2 %u64_0 %u64_1
+
+%f32mat22_1212 = OpConstantComposite %f32mat22 %f32vec2_12 %f32vec2_12
+%f32mat23_121212 = OpConstantComposite %f32mat23 %f32vec2_12 %f32vec2_12 %f32vec2_12
+
+%f32_ptr_output = OpTypePointer Output %f32
+%f32vec2_ptr_output = OpTypePointer Output %f32vec2
+
+%u32_ptr_output = OpTypePointer Output %u32
+%u32vec2_ptr_output = OpTypePointer Output %u32vec2
+
+%u64_ptr_output = OpTypePointer Output %u64
+
+%f32_output = OpVariable %f32_ptr_output Output
+%f32vec2_output = OpVariable %f32vec2_ptr_output Output
+
+%u32_output = OpVariable %u32_ptr_output Output
+%u32vec2_output = OpVariable %u32vec2_ptr_output Output
+
+%u64_output = OpVariable %u64_ptr_output Output
+
+%f32_ptr_input = OpTypePointer Input %f32
+%f32vec2_ptr_input = OpTypePointer Input %f32vec2
+
+%u32_ptr_input = OpTypePointer Input %u32
+%u32vec2_ptr_input = OpTypePointer Input %u32vec2
+
+%u64_ptr_input = OpTypePointer Input %u64
+
+%f32_input = OpVariable %f32_ptr_input Input
+%f32vec2_input = OpVariable %f32vec2_ptr_input Input
+
+%u32_input = OpVariable %u32_ptr_input Input
+%u32vec2_input = OpVariable %u32vec2_ptr_input Input
+
+%u64_input = OpVariable %u64_ptr_input Input
+
+%struct_f32_f32 = OpTypeStruct %f32 %f32
+%struct_f32_f32_f32 = OpTypeStruct %f32 %f32 %f32
+%struct_f32_u32 = OpTypeStruct %f32 %u32
+%struct_f32_u32_f32 = OpTypeStruct %f32 %u32 %f32
+%struct_u32_f32 = OpTypeStruct %u32 %f32
+%struct_u32_u32 = OpTypeStruct %u32 %u32
+%struct_f32_f64 = OpTypeStruct %f32 %f64
+%struct_f32vec2_f32vec2 = OpTypeStruct %f32vec2 %f32vec2
+%struct_f32vec2_u32vec2 = OpTypeStruct %f32vec2 %u32vec2
+
+%main = OpFunction %void None %func
+%main_entry = OpLabel
+)";
+
+  ss << body;
+
+  ss << R"(
+OpReturn
+OpFunctionEnd)";
+
+  return ss.str();
+}
+
+TEST_P(ValidateGlslStd450SqrtLike, Success) {
+  const std::string ext_inst_name = GetParam();
+  std::ostringstream ss;
+  ss << "%val1 = OpExtInst %f32 %extinst " << ext_inst_name << " %f32_0\n";
+  ss << "%val2 = OpExtInst %f32vec2 %extinst " << ext_inst_name
+     << " %f32vec2_01\n";
+  ss << "%val3 = OpExtInst %f64 %extinst " << ext_inst_name << " %f64_0\n";
+  CompileSuccessfully(GenerateShaderCode(ss.str()));
+  ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+TEST_P(ValidateGlslStd450SqrtLike, IntResultType) {
+  const std::string ext_inst_name = GetParam();
+  const std::string body =
+      "%val1 = OpExtInst %u32 %extinst " + ext_inst_name + " %f32_0\n";
+
+  CompileSuccessfully(GenerateShaderCode(body));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("GLSL.std.450 " + ext_inst_name +
+                        ": expected Result Type to be a float scalar "
+                        "or vector type"));
+}
+
+TEST_P(ValidateGlslStd450SqrtLike, IntOperand) {
+  const std::string ext_inst_name = GetParam();
+  const std::string body =
+      "%val1 = OpExtInst %f32 %extinst " + ext_inst_name + " %u32_0\n";
+
+  CompileSuccessfully(GenerateShaderCode(body));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("GLSL.std.450 " + ext_inst_name +
+                        ": expected types of all operands to be equal to "
+                        "Result Type"));
+}
+
+INSTANTIATE_TEST_CASE_P(AllSqrtLike, ValidateGlslStd450SqrtLike,
+                        ::testing::ValuesIn(std::vector<std::string>{
+                            "Round",
+                            "RoundEven",
+                            "FAbs",
+                            "Trunc",
+                            "FSign",
+                            "Floor",
+                            "Ceil",
+                            "Fract",
+                            "Sqrt",
+                            "InverseSqrt",
+                            "Normalize",
+                        }), );
+
+TEST_P(ValidateGlslStd450FMinLike, Success) {
+  const std::string ext_inst_name = GetParam();
+  std::ostringstream ss;
+  ss << "%val1 = OpExtInst %f32 %extinst " << ext_inst_name
+     << " %f32_0 %f32_1\n";
+  ss << "%val2 = OpExtInst %f32vec2 %extinst " << ext_inst_name
+     << " %f32vec2_01 %f32vec2_12\n";
+  ss << "%val3 = OpExtInst %f64 %extinst " << ext_inst_name
+     << " %f64_0 %f64_0\n";
+  CompileSuccessfully(GenerateShaderCode(ss.str()));
+  ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+TEST_P(ValidateGlslStd450FMinLike, IntResultType) {
+  const std::string ext_inst_name = GetParam();
+  const std::string body =
+      "%val1 = OpExtInst %u32 %extinst " + ext_inst_name + " %f32_0 %f32_1\n";
+
+  CompileSuccessfully(GenerateShaderCode(body));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("GLSL.std.450 " + ext_inst_name +
+                        ": expected Result Type to be a float scalar "
+                        "or vector type"));
+}
+
+TEST_P(ValidateGlslStd450FMinLike, IntOperand1) {
+  const std::string ext_inst_name = GetParam();
+  const std::string body =
+      "%val1 = OpExtInst %f32 %extinst " + ext_inst_name + " %u32_0 %f32_1\n";
+
+  CompileSuccessfully(GenerateShaderCode(body));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("GLSL.std.450 " + ext_inst_name +
+                        ": expected types of all operands to be equal to "
+                        "Result Type"));
+}
+
+TEST_P(ValidateGlslStd450FMinLike, IntOperand2) {
+  const std::string ext_inst_name = GetParam();
+  const std::string body =
+      "%val1 = OpExtInst %f32 %extinst " + ext_inst_name + " %f32_0 %u32_1\n";
+
+  CompileSuccessfully(GenerateShaderCode(body));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("GLSL.std.450 " + ext_inst_name +
+                        ": expected types of all operands to be equal to "
+                        "Result Type"));
+}
+
+INSTANTIATE_TEST_CASE_P(AllFMinLike, ValidateGlslStd450FMinLike,
+                        ::testing::ValuesIn(std::vector<std::string>{
+                            "FMin",
+                            "FMax",
+                            "Step",
+                            "Reflect",
+                            "NMin",
+                            "NMax",
+                        }), );
+
+TEST_P(ValidateGlslStd450FClampLike, Success) {
+  const std::string ext_inst_name = GetParam();
+  std::ostringstream ss;
+  ss << "%val1 = OpExtInst %f32 %extinst " << ext_inst_name
+     << " %f32_0 %f32_1 %f32_2\n";
+  ss << "%val2 = OpExtInst %f32vec2 %extinst " << ext_inst_name
+     << " %f32vec2_01 %f32vec2_01 %f32vec2_12\n";
+  ss << "%val3 = OpExtInst %f64 %extinst " << ext_inst_name
+     << " %f64_0 %f64_0 %f64_1\n";
+  CompileSuccessfully(GenerateShaderCode(ss.str()));
+  ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+TEST_P(ValidateGlslStd450FClampLike, IntResultType) {
+  const std::string ext_inst_name = GetParam();
+  const std::string body = "%val1 = OpExtInst %u32 %extinst " + ext_inst_name +
+                           " %f32_0 %f32_1 %f32_2\n";
+
+  CompileSuccessfully(GenerateShaderCode(body));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("GLSL.std.450 " + ext_inst_name +
+                        ": expected Result Type to be a float scalar "
+                        "or vector type"));
+}
+
+TEST_P(ValidateGlslStd450FClampLike, IntOperand1) {
+  const std::string ext_inst_name = GetParam();
+  const std::string body = "%val1 = OpExtInst %f32 %extinst " + ext_inst_name +
+                           " %u32_0 %f32_0 %f32_1\n";
+
+  CompileSuccessfully(GenerateShaderCode(body));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("GLSL.std.450 " + ext_inst_name +
+                        ": expected types of all operands to be equal to "
+                        "Result Type"));
+}
+
+TEST_P(ValidateGlslStd450FClampLike, IntOperand2) {
+  const std::string ext_inst_name = GetParam();
+  const std::string body = "%val1 = OpExtInst %f32 %extinst " + ext_inst_name +
+                           " %f32_0 %u32_0 %f32_1\n";
+
+  CompileSuccessfully(GenerateShaderCode(body));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("GLSL.std.450 " + ext_inst_name +
+                        ": expected types of all operands to be equal to "
+                        "Result Type"));
+}
+
+TEST_P(ValidateGlslStd450FClampLike, IntOperand3) {
+  const std::string ext_inst_name = GetParam();
+  const std::string body = "%val1 = OpExtInst %f32 %extinst " + ext_inst_name +
+                           " %f32_1 %f32_0 %u32_2\n";
+
+  CompileSuccessfully(GenerateShaderCode(body));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("GLSL.std.450 " + ext_inst_name +
+                        ": expected types of all operands to be equal to "
+                        "Result Type"));
+}
+
+INSTANTIATE_TEST_CASE_P(AllFClampLike, ValidateGlslStd450FClampLike,
+                        ::testing::ValuesIn(std::vector<std::string>{
+                            "FClamp",
+                            "FMix",
+                            "SmoothStep",
+                            "Fma",
+                            "FaceForward",
+                            "NClamp",
+                        }), );
+
+TEST_P(ValidateGlslStd450SAbsLike, Success) {
+  const std::string ext_inst_name = GetParam();
+  std::ostringstream ss;
+  ss << "%val1 = OpExtInst %s32 %extinst " << ext_inst_name << " %u32_1\n";
+  ss << "%val2 = OpExtInst %s32 %extinst " << ext_inst_name << " %s32_1\n";
+  ss << "%val3 = OpExtInst %u32 %extinst " << ext_inst_name << " %u32_1\n";
+  ss << "%val4 = OpExtInst %u32 %extinst " << ext_inst_name << " %s32_1\n";
+  ss << "%val5 = OpExtInst %s32vec2 %extinst " << ext_inst_name
+     << " %s32vec2_01\n";
+  ss << "%val6 = OpExtInst %u32vec2 %extinst " << ext_inst_name
+     << " %u32vec2_01\n";
+  ss << "%val7 = OpExtInst %u32vec2 %extinst " << ext_inst_name
+     << " %s32vec2_01\n";
+  ss << "%val8 = OpExtInst %s32vec2 %extinst " << ext_inst_name
+     << " %u32vec2_01\n";
+  CompileSuccessfully(GenerateShaderCode(ss.str()));
+  ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+TEST_P(ValidateGlslStd450SAbsLike, FloatResultType) {
+  const std::string ext_inst_name = GetParam();
+  const std::string body =
+      "%val1 = OpExtInst %f32 %extinst " + ext_inst_name + " %u32_0\n";
+
+  CompileSuccessfully(GenerateShaderCode(body));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("GLSL.std.450 " + ext_inst_name +
+                        ": expected Result Type to be an int scalar "
+                        "or vector type"));
+}
+
+TEST_P(ValidateGlslStd450SAbsLike, FloatOperand) {
+  const std::string ext_inst_name = GetParam();
+  const std::string body =
+      "%val1 = OpExtInst %s32 %extinst " + ext_inst_name + " %f32_0\n";
+
+  CompileSuccessfully(GenerateShaderCode(body));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("GLSL.std.450 " + ext_inst_name +
+                        ": expected all operands to be int scalars or "
+                        "vectors"));
+}
+
+TEST_P(ValidateGlslStd450SAbsLike, WrongDimOperand) {
+  const std::string ext_inst_name = GetParam();
+  const std::string body =
+      "%val1 = OpExtInst %s32 %extinst " + ext_inst_name + " %s32vec2_01\n";
+
+  CompileSuccessfully(GenerateShaderCode(body));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("GLSL.std.450 " + ext_inst_name +
+                        ": expected all operands to have the same dimension as "
+                        "Result Type"));
+}
+
+TEST_P(ValidateGlslStd450SAbsLike, WrongBitWidthOperand) {
+  const std::string ext_inst_name = GetParam();
+  const std::string body =
+      "%val1 = OpExtInst %s64 %extinst " + ext_inst_name + " %s32_0\n";
+
+  CompileSuccessfully(GenerateShaderCode(body));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("GLSL.std.450 " + ext_inst_name +
+                        ": expected all operands to have the same bit width as "
+                        "Result Type"));
+}
+
+INSTANTIATE_TEST_CASE_P(AllSAbsLike, ValidateGlslStd450SAbsLike,
+                        ::testing::ValuesIn(std::vector<std::string>{
+                            "SAbs",
+                            "SSign",
+                            "FindILsb",
+                            "FindUMsb",
+                            "FindSMsb",
+                        }), );
+
+TEST_F(ValidateExtInst, FindUMsbNot32Bit) {
+  const std::string body = R"(
+%val1 = OpExtInst %s64 %extinst FindUMsb %u64_1
+)";
+
+  CompileSuccessfully(GenerateShaderCode(body));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("GLSL.std.450 FindUMsb: this instruction is currently "
+                        "limited to 32-bit width components"));
+}
+
+TEST_F(ValidateExtInst, FindSMsbNot32Bit) {
+  const std::string body = R"(
+%val1 = OpExtInst %s64 %extinst FindSMsb %u64_1
+)";
+
+  CompileSuccessfully(GenerateShaderCode(body));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("GLSL.std.450 FindSMsb: this instruction is currently "
+                        "limited to 32-bit width components"));
+}
+
+TEST_P(ValidateGlslStd450UMinLike, Success) {
+  const std::string ext_inst_name = GetParam();
+  std::ostringstream ss;
+  ss << "%val1 = OpExtInst %s32 %extinst " << ext_inst_name
+     << " %u32_1 %s32_2\n";
+  ss << "%val2 = OpExtInst %s32 %extinst " << ext_inst_name
+     << " %s32_1 %u32_2\n";
+  ss << "%val3 = OpExtInst %u32 %extinst " << ext_inst_name
+     << " %u32_1 %s32_2\n";
+  ss << "%val4 = OpExtInst %u32 %extinst " << ext_inst_name
+     << " %s32_1 %u32_2\n";
+  ss << "%val5 = OpExtInst %s32vec2 %extinst " << ext_inst_name
+     << " %s32vec2_01 %u32vec2_01\n";
+  ss << "%val6 = OpExtInst %u32vec2 %extinst " << ext_inst_name
+     << " %u32vec2_01 %s32vec2_01\n";
+  ss << "%val7 = OpExtInst %u32vec2 %extinst " << ext_inst_name
+     << " %s32vec2_01 %u32vec2_01\n";
+  ss << "%val8 = OpExtInst %s32vec2 %extinst " << ext_inst_name
+     << " %u32vec2_01 %s32vec2_01\n";
+  ss << "%val9 = OpExtInst %s64 %extinst " << ext_inst_name
+     << " %u64_1 %s64_0\n";
+  CompileSuccessfully(GenerateShaderCode(ss.str()));
+  ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+TEST_P(ValidateGlslStd450UMinLike, FloatResultType) {
+  const std::string ext_inst_name = GetParam();
+  const std::string body =
+      "%val1 = OpExtInst %f32 %extinst " + ext_inst_name + " %u32_0 %u32_0\n";
+
+  CompileSuccessfully(GenerateShaderCode(body));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("GLSL.std.450 " + ext_inst_name +
+                        ": expected Result Type to be an int scalar "
+                        "or vector type"));
+}
+
+TEST_P(ValidateGlslStd450UMinLike, FloatOperand1) {
+  const std::string ext_inst_name = GetParam();
+  const std::string body =
+      "%val1 = OpExtInst %s32 %extinst " + ext_inst_name + " %f32_0 %u32_0\n";
+
+  CompileSuccessfully(GenerateShaderCode(body));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("GLSL.std.450 " + ext_inst_name +
+                        ": expected all operands to be int scalars or "
+                        "vectors"));
+}
+
+TEST_P(ValidateGlslStd450UMinLike, FloatOperand2) {
+  const std::string ext_inst_name = GetParam();
+  const std::string body =
+      "%val1 = OpExtInst %s32 %extinst " + ext_inst_name + " %u32_0 %f32_0\n";
+
+  CompileSuccessfully(GenerateShaderCode(body));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("GLSL.std.450 " + ext_inst_name +
+                        ": expected all operands to be int scalars or "
+                        "vectors"));
+}
+
+TEST_P(ValidateGlslStd450UMinLike, WrongDimOperand1) {
+  const std::string ext_inst_name = GetParam();
+  const std::string body = "%val1 = OpExtInst %s32 %extinst " + ext_inst_name +
+                           " %s32vec2_01 %s32_0\n";
+
+  CompileSuccessfully(GenerateShaderCode(body));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("GLSL.std.450 " + ext_inst_name +
+                        ": expected all operands to have the same dimension as "
+                        "Result Type"));
+}
+
+TEST_P(ValidateGlslStd450UMinLike, WrongDimOperand2) {
+  const std::string ext_inst_name = GetParam();
+  const std::string body = "%val1 = OpExtInst %s32 %extinst " + ext_inst_name +
+                           " %s32_0 %s32vec2_01\n";
+
+  CompileSuccessfully(GenerateShaderCode(body));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("GLSL.std.450 " + ext_inst_name +
+                        ": expected all operands to have the same dimension as "
+                        "Result Type"));
+}
+
+TEST_P(ValidateGlslStd450UMinLike, WrongBitWidthOperand1) {
+  const std::string ext_inst_name = GetParam();
+  const std::string body =
+      "%val1 = OpExtInst %s64 %extinst " + ext_inst_name + " %s32_0 %s64_0\n";
+
+  CompileSuccessfully(GenerateShaderCode(body));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("GLSL.std.450 " + ext_inst_name +
+                        ": expected all operands to have the same bit width as "
+                        "Result Type"));
+}
+
+TEST_P(ValidateGlslStd450UMinLike, WrongBitWidthOperand2) {
+  const std::string ext_inst_name = GetParam();
+  const std::string body =
+      "%val1 = OpExtInst %s64 %extinst " + ext_inst_name + " %s64_0 %s32_0\n";
+
+  CompileSuccessfully(GenerateShaderCode(body));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("GLSL.std.450 " + ext_inst_name +
+                        ": expected all operands to have the same bit width as "
+                        "Result Type"));
+}
+
+INSTANTIATE_TEST_CASE_P(AllUMinLike, ValidateGlslStd450UMinLike,
+                        ::testing::ValuesIn(std::vector<std::string>{
+                            "UMin",
+                            "SMin",
+                            "UMax",
+                            "SMax",
+                        }), );
+
+TEST_P(ValidateGlslStd450UClampLike, Success) {
+  const std::string ext_inst_name = GetParam();
+  std::ostringstream ss;
+  ss << "%val1 = OpExtInst %s32 %extinst " << ext_inst_name
+     << " %s32_0 %u32_1 %s32_2\n";
+  ss << "%val2 = OpExtInst %s32 %extinst " << ext_inst_name
+     << " %u32_0 %s32_1 %u32_2\n";
+  ss << "%val3 = OpExtInst %u32 %extinst " << ext_inst_name
+     << " %s32_0 %u32_1 %s32_2\n";
+  ss << "%val4 = OpExtInst %u32 %extinst " << ext_inst_name
+     << " %u32_0 %s32_1 %u32_2\n";
+  ss << "%val5 = OpExtInst %s32vec2 %extinst " << ext_inst_name
+     << " %s32vec2_01 %u32vec2_01 %u32vec2_12\n";
+  ss << "%val6 = OpExtInst %u32vec2 %extinst " << ext_inst_name
+     << " %u32vec2_01 %s32vec2_01 %s32vec2_12\n";
+  ss << "%val7 = OpExtInst %u32vec2 %extinst " << ext_inst_name
+     << " %s32vec2_01 %u32vec2_01 %u32vec2_12\n";
+  ss << "%val8 = OpExtInst %s32vec2 %extinst " << ext_inst_name
+     << " %u32vec2_01 %s32vec2_01 %s32vec2_12\n";
+  ss << "%val9 = OpExtInst %s64 %extinst " << ext_inst_name
+     << " %u64_1 %s64_0 %s64_1\n";
+  CompileSuccessfully(GenerateShaderCode(ss.str()));
+  ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+TEST_P(ValidateGlslStd450UClampLike, FloatResultType) {
+  const std::string ext_inst_name = GetParam();
+  const std::string body = "%val1 = OpExtInst %f32 %extinst " + ext_inst_name +
+                           " %u32_0 %u32_0 %u32_1\n";
+
+  CompileSuccessfully(GenerateShaderCode(body));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("GLSL.std.450 " + ext_inst_name +
+                        ": expected Result Type to be an int scalar "
+                        "or vector type"));
+}
+
+TEST_P(ValidateGlslStd450UClampLike, FloatOperand1) {
+  const std::string ext_inst_name = GetParam();
+  const std::string body = "%val1 = OpExtInst %s32 %extinst " + ext_inst_name +
+                           " %f32_0 %u32_0 %u32_1\n";
+
+  CompileSuccessfully(GenerateShaderCode(body));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("GLSL.std.450 " + ext_inst_name +
+                        ": expected all operands to be int scalars or "
+                        "vectors"));
+}
+
+TEST_P(ValidateGlslStd450UClampLike, FloatOperand2) {
+  const std::string ext_inst_name = GetParam();
+  const std::string body = "%val1 = OpExtInst %s32 %extinst " + ext_inst_name +
+                           " %u32_0 %f32_0 %u32_1\n";
+
+  CompileSuccessfully(GenerateShaderCode(body));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("GLSL.std.450 " + ext_inst_name +
+                        ": expected all operands to be int scalars or "
+                        "vectors"));
+}
+
+TEST_P(ValidateGlslStd450UClampLike, FloatOperand3) {
+  const std::string ext_inst_name = GetParam();
+  const std::string body = "%val1 = OpExtInst %s32 %extinst " + ext_inst_name +
+                           " %u32_0 %u32_0 %f32_1\n";
+
+  CompileSuccessfully(GenerateShaderCode(body));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("GLSL.std.450 " + ext_inst_name +
+                        ": expected all operands to be int scalars or "
+                        "vectors"));
+}
+
+TEST_P(ValidateGlslStd450UClampLike, WrongDimOperand1) {
+  const std::string ext_inst_name = GetParam();
+  const std::string body = "%val1 = OpExtInst %s32 %extinst " + ext_inst_name +
+                           " %s32vec2_01 %s32_0 %u32_1\n";
+
+  CompileSuccessfully(GenerateShaderCode(body));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("GLSL.std.450 " + ext_inst_name +
+                        ": expected all operands to have the same dimension as "
+                        "Result Type"));
+}
+
+TEST_P(ValidateGlslStd450UClampLike, WrongDimOperand2) {
+  const std::string ext_inst_name = GetParam();
+  const std::string body = "%val1 = OpExtInst %s32 %extinst " + ext_inst_name +
+                           " %s32_0 %s32vec2_01 %u32_1\n";
+
+  CompileSuccessfully(GenerateShaderCode(body));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("GLSL.std.450 " + ext_inst_name +
+                        ": expected all operands to have the same dimension as "
+                        "Result Type"));
+}
+
+TEST_P(ValidateGlslStd450UClampLike, WrongDimOperand3) {
+  const std::string ext_inst_name = GetParam();
+  const std::string body = "%val1 = OpExtInst %s32 %extinst " + ext_inst_name +
+                           " %s32_0 %u32_1 %s32vec2_01\n";
+
+  CompileSuccessfully(GenerateShaderCode(body));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("GLSL.std.450 " + ext_inst_name +
+                        ": expected all operands to have the same dimension as "
+                        "Result Type"));
+}
+
+TEST_P(ValidateGlslStd450UClampLike, WrongBitWidthOperand1) {
+  const std::string ext_inst_name = GetParam();
+  const std::string body = "%val1 = OpExtInst %s64 %extinst " + ext_inst_name +
+                           " %s32_0 %s64_0 %s64_1\n";
+
+  CompileSuccessfully(GenerateShaderCode(body));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("GLSL.std.450 " + ext_inst_name +
+                        ": expected all operands to have the same bit width as "
+                        "Result Type"));
+}
+
+TEST_P(ValidateGlslStd450UClampLike, WrongBitWidthOperand2) {
+  const std::string ext_inst_name = GetParam();
+  const std::string body = "%val1 = OpExtInst %s64 %extinst " + ext_inst_name +
+                           " %s64_0 %s32_0 %s64_1\n";
+
+  CompileSuccessfully(GenerateShaderCode(body));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("GLSL.std.450 " + ext_inst_name +
+                        ": expected all operands to have the same bit width as "
+                        "Result Type"));
+}
+
+TEST_P(ValidateGlslStd450UClampLike, WrongBitWidthOperand3) {
+  const std::string ext_inst_name = GetParam();
+  const std::string body = "%val1 = OpExtInst %s64 %extinst " + ext_inst_name +
+                           " %s64_0 %s64_0 %s32_1\n";
+
+  CompileSuccessfully(GenerateShaderCode(body));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("GLSL.std.450 " + ext_inst_name +
+                        ": expected all operands to have the same bit width as "
+                        "Result Type"));
+}
+
+INSTANTIATE_TEST_CASE_P(AllUClampLike, ValidateGlslStd450UClampLike,
+                        ::testing::ValuesIn(std::vector<std::string>{
+                            "UClamp",
+                            "SClamp",
+                        }), );
+
+TEST_P(ValidateGlslStd450SinLike, Success) {
+  const std::string ext_inst_name = GetParam();
+  std::ostringstream ss;
+  ss << "%val1 = OpExtInst %f32 %extinst " << ext_inst_name << " %f32_0\n";
+  ss << "%val2 = OpExtInst %f32vec2 %extinst " << ext_inst_name
+     << " %f32vec2_01\n";
+  CompileSuccessfully(GenerateShaderCode(ss.str()));
+  ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+TEST_P(ValidateGlslStd450SinLike, IntResultType) {
+  const std::string ext_inst_name = GetParam();
+  const std::string body =
+      "%val1 = OpExtInst %u32 %extinst " + ext_inst_name + " %f32_0\n";
+
+  CompileSuccessfully(GenerateShaderCode(body));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("GLSL.std.450 " + ext_inst_name +
+                        ": expected Result Type to be a 16 or 32-bit scalar "
+                        "or vector float type"));
+}
+
+TEST_P(ValidateGlslStd450SinLike, F64ResultType) {
+  const std::string ext_inst_name = GetParam();
+  const std::string body =
+      "%val1 = OpExtInst %f64 %extinst " + ext_inst_name + " %f32_0\n";
+
+  CompileSuccessfully(GenerateShaderCode(body));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("GLSL.std.450 " + ext_inst_name +
+                        ": expected Result Type to be a 16 or 32-bit scalar "
+                        "or vector float type"));
+}
+
+TEST_P(ValidateGlslStd450SinLike, IntOperand) {
+  const std::string ext_inst_name = GetParam();
+  const std::string body =
+      "%val1 = OpExtInst %f32 %extinst " + ext_inst_name + " %u32_0\n";
+
+  CompileSuccessfully(GenerateShaderCode(body));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("GLSL.std.450 " + ext_inst_name +
+                        ": expected types of all operands to be equal to "
+                        "Result Type"));
+}
+
+INSTANTIATE_TEST_CASE_P(AllSinLike, ValidateGlslStd450SinLike,
+                        ::testing::ValuesIn(std::vector<std::string>{
+                            "Radians",
+                            "Degrees",
+                            "Sin",
+                            "Cos",
+                            "Tan",
+                            "Asin",
+                            "Acos",
+                            "Atan",
+                            "Sinh",
+                            "Cosh",
+                            "Tanh",
+                            "Asinh",
+                            "Acosh",
+                            "Atanh",
+                            "Exp",
+                            "Exp2",
+                            "Log",
+                            "Log2",
+                        }), );
+
+TEST_P(ValidateGlslStd450PowLike, Success) {
+  const std::string ext_inst_name = GetParam();
+  std::ostringstream ss;
+  ss << "%val1 = OpExtInst %f32 %extinst " << ext_inst_name
+     << " %f32_1 %f32_1\n";
+  ss << "%val2 = OpExtInst %f32vec2 %extinst " << ext_inst_name
+     << " %f32vec2_01 %f32vec2_12\n";
+  CompileSuccessfully(GenerateShaderCode(ss.str()));
+  ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+TEST_P(ValidateGlslStd450PowLike, IntResultType) {
+  const std::string ext_inst_name = GetParam();
+  const std::string body =
+      "%val1 = OpExtInst %u32 %extinst " + ext_inst_name + " %f32_1 %f32_0\n";
+
+  CompileSuccessfully(GenerateShaderCode(body));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("GLSL.std.450 " + ext_inst_name +
+                        ": expected Result Type to be a 16 or 32-bit scalar "
+                        "or vector float type"));
+}
+
+TEST_P(ValidateGlslStd450PowLike, F64ResultType) {
+  const std::string ext_inst_name = GetParam();
+  const std::string body =
+      "%val1 = OpExtInst %f64 %extinst " + ext_inst_name + " %f32_1 %f32_0\n";
+
+  CompileSuccessfully(GenerateShaderCode(body));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("GLSL.std.450 " + ext_inst_name +
+                        ": expected Result Type to be a 16 or 32-bit scalar "
+                        "or vector float type"));
+}
+
+TEST_P(ValidateGlslStd450PowLike, IntOperand1) {
+  const std::string ext_inst_name = GetParam();
+  const std::string body =
+      "%val1 = OpExtInst %f32 %extinst " + ext_inst_name + " %u32_0 %f32_1\n";
+
+  CompileSuccessfully(GenerateShaderCode(body));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("GLSL.std.450 " + ext_inst_name +
+                        ": expected types of all operands to be equal to "
+                        "Result Type"));
+}
+
+TEST_P(ValidateGlslStd450PowLike, IntOperand2) {
+  const std::string ext_inst_name = GetParam();
+  const std::string body =
+      "%val1 = OpExtInst %f32 %extinst " + ext_inst_name + " %f32_0 %u32_1\n";
+
+  CompileSuccessfully(GenerateShaderCode(body));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("GLSL.std.450 " + ext_inst_name +
+                        ": expected types of all operands to be equal to "
+                        "Result Type"));
+}
+
+INSTANTIATE_TEST_CASE_P(AllPowLike, ValidateGlslStd450PowLike,
+                        ::testing::ValuesIn(std::vector<std::string>{
+                            "Atan2",
+                            "Pow",
+                        }), );
+
+TEST_F(ValidateExtInst, GlslStd450DeterminantSuccess) {
+  const std::string body = R"(
+%val1 = OpExtInst %f32 %extinst Determinant %f32mat22_1212
+)";
+
+  CompileSuccessfully(GenerateShaderCode(body));
+  ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+TEST_F(ValidateExtInst, GlslStd450DeterminantIncompatibleResultType) {
+  const std::string body = R"(
+%val1 = OpExtInst %f64 %extinst Determinant %f32mat22_1212
+)";
+
+  CompileSuccessfully(GenerateShaderCode(body));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("GLSL.std.450 Determinant: "
+                        "expected operand X component type to be equal to "
+                        "Result Type"));
+}
+
+TEST_F(ValidateExtInst, GlslStd450DeterminantNotMatrix) {
+  const std::string body = R"(
+%val1 = OpExtInst %f32 %extinst Determinant %f32_1
+)";
+
+  CompileSuccessfully(GenerateShaderCode(body));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("GLSL.std.450 Determinant: "
+                        "expected operand X to be a square matrix"));
+}
+
+TEST_F(ValidateExtInst, GlslStd450DeterminantMatrixNotSquare) {
+  const std::string body = R"(
+%val1 = OpExtInst %f32 %extinst Determinant %f32mat23_121212
+)";
+
+  CompileSuccessfully(GenerateShaderCode(body));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("GLSL.std.450 Determinant: "
+                        "expected operand X to be a square matrix"));
+}
+
+TEST_F(ValidateExtInst, GlslStd450MatrixInverseSuccess) {
+  const std::string body = R"(
+%val1 = OpExtInst %f32mat22 %extinst MatrixInverse %f32mat22_1212
+)";
+
+  CompileSuccessfully(GenerateShaderCode(body));
+  ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+TEST_F(ValidateExtInst, GlslStd450MatrixInverseIncompatibleResultType) {
+  const std::string body = R"(
+%val1 = OpExtInst %f32mat33 %extinst MatrixInverse %f32mat22_1212
+)";
+
+  CompileSuccessfully(GenerateShaderCode(body));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("GLSL.std.450 MatrixInverse: "
+                        "expected operand X type to be equal to "
+                        "Result Type"));
+}
+
+TEST_F(ValidateExtInst, GlslStd450MatrixInverseNotMatrix) {
+  const std::string body = R"(
+%val1 = OpExtInst %f32 %extinst MatrixInverse %f32mat22_1212
+)";
+
+  CompileSuccessfully(GenerateShaderCode(body));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("GLSL.std.450 MatrixInverse: "
+                        "expected Result Type to be a square matrix"));
+}
+
+TEST_F(ValidateExtInst, GlslStd450MatrixInverseMatrixNotSquare) {
+  const std::string body = R"(
+%val1 = OpExtInst %f32mat23 %extinst MatrixInverse %f32mat23_121212
+)";
+
+  CompileSuccessfully(GenerateShaderCode(body));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("GLSL.std.450 MatrixInverse: "
+                        "expected Result Type to be a square matrix"));
+}
+
+TEST_F(ValidateExtInst, GlslStd450ModfSuccess) {
+  const std::string body = R"(
+%val1 = OpExtInst %f32 %extinst Modf %f32_h %f32_output
+%val2 = OpExtInst %f32vec2 %extinst Modf %f32vec2_01 %f32vec2_output
+)";
+
+  CompileSuccessfully(GenerateShaderCode(body));
+  ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+TEST_F(ValidateExtInst, GlslStd450ModfIntResultType) {
+  const std::string body = R"(
+%val1 = OpExtInst %u32 %extinst Modf %f32_h %f32_output
+)";
+
+  CompileSuccessfully(GenerateShaderCode(body));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("GLSL.std.450 Modf: "
+                        "expected Result Type to be a scalar or vector "
+                        "float type"));
+}
+
+TEST_F(ValidateExtInst, GlslStd450ModfXNotOfResultType) {
+  const std::string body = R"(
+%val1 = OpExtInst %f32 %extinst Modf %f64_0 %f32_output
+)";
+
+  CompileSuccessfully(GenerateShaderCode(body));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("GLSL.std.450 Modf: "
+                        "expected operand X type to be equal to Result Type"));
+}
+
+TEST_F(ValidateExtInst, GlslStd450ModfINotPointer) {
+  const std::string body = R"(
+%val1 = OpExtInst %f32 %extinst Modf %f32_h %f32_1
+)";
+
+  CompileSuccessfully(GenerateShaderCode(body));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("GLSL.std.450 Modf: "
+                        "expected operand I to be a pointer"));
+}
+
+TEST_F(ValidateExtInst, GlslStd450ModfIDataNotOfResultType) {
+  const std::string body = R"(
+%val1 = OpExtInst %f32 %extinst Modf %f32_h %f32vec2_output
+)";
+
+  CompileSuccessfully(GenerateShaderCode(body));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("GLSL.std.450 Modf: "
+                        "expected operand I data type to be equal to "
+                        "Result Type"));
+}
+
+TEST_F(ValidateExtInst, GlslStd450ModfStructSuccess) {
+  const std::string body = R"(
+%val1 = OpExtInst %struct_f32_f32 %extinst ModfStruct %f32_h
+%val2 = OpExtInst %struct_f32vec2_f32vec2 %extinst ModfStruct %f32vec2_01
+)";
+
+  CompileSuccessfully(GenerateShaderCode(body));
+  ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+TEST_F(ValidateExtInst, GlslStd450ModfStructResultTypeNotStruct) {
+  const std::string body = R"(
+%val1 = OpExtInst %f32 %extinst ModfStruct %f32_h
+)";
+
+  CompileSuccessfully(GenerateShaderCode(body));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("GLSL.std.450 ModfStruct: "
+                        "expected Result Type to be a struct with two "
+                        "identical scalar or vector float type members"));
+}
+
+TEST_F(ValidateExtInst, GlslStd450ModfStructResultTypeStructWrongSize) {
+  const std::string body = R"(
+%val1 = OpExtInst %struct_f32_f32_f32 %extinst ModfStruct %f32_h
+)";
+
+  CompileSuccessfully(GenerateShaderCode(body));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("GLSL.std.450 ModfStruct: "
+                        "expected Result Type to be a struct with two "
+                        "identical scalar or vector float type members"));
+}
+
+TEST_F(ValidateExtInst, GlslStd450ModfStructResultTypeStructWrongFirstMember) {
+  const std::string body = R"(
+%val1 = OpExtInst %struct_u32_f32 %extinst ModfStruct %f32_h
+)";
+
+  CompileSuccessfully(GenerateShaderCode(body));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("GLSL.std.450 ModfStruct: "
+                        "expected Result Type to be a struct with two "
+                        "identical scalar or vector float type members"));
+}
+
+TEST_F(ValidateExtInst, GlslStd450ModfStructResultTypeStructMembersNotEqual) {
+  const std::string body = R"(
+%val1 = OpExtInst %struct_f32_f64 %extinst ModfStruct %f32_h
+)";
+
+  CompileSuccessfully(GenerateShaderCode(body));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("GLSL.std.450 ModfStruct: "
+                        "expected Result Type to be a struct with two "
+                        "identical scalar or vector float type members"));
+}
+
+TEST_F(ValidateExtInst, GlslStd450ModfStructXWrongType) {
+  const std::string body = R"(
+%val1 = OpExtInst %struct_f32_f32 %extinst ModfStruct %f64_0
+)";
+
+  CompileSuccessfully(GenerateShaderCode(body));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("GLSL.std.450 ModfStruct: "
+                        "expected operand X type to be equal to members of "
+                        "Result Type struct"));
+}
+
+TEST_F(ValidateExtInst, GlslStd450FrexpSuccess) {
+  const std::string body = R"(
+%val1 = OpExtInst %f32 %extinst Frexp %f32_h %u32_output
+%val2 = OpExtInst %f32vec2 %extinst Frexp %f32vec2_01 %u32vec2_output
+)";
+
+  CompileSuccessfully(GenerateShaderCode(body));
+  ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+TEST_F(ValidateExtInst, GlslStd450FrexpIntResultType) {
+  const std::string body = R"(
+%val1 = OpExtInst %u32 %extinst Frexp %f32_h %u32_output
+)";
+
+  CompileSuccessfully(GenerateShaderCode(body));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("GLSL.std.450 Frexp: "
+                        "expected Result Type to be a scalar or vector "
+                        "float type"));
+}
+
+TEST_F(ValidateExtInst, GlslStd450FrexpWrongXType) {
+  const std::string body = R"(
+%val1 = OpExtInst %f32 %extinst Frexp %u32_1 %u32_output
+)";
+
+  CompileSuccessfully(GenerateShaderCode(body));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("GLSL.std.450 Frexp: "
+                        "expected operand X type to be equal to Result Type"));
+}
+
+TEST_F(ValidateExtInst, GlslStd450FrexpExpNotPointer) {
+  const std::string body = R"(
+%val1 = OpExtInst %f32 %extinst Frexp %f32_1 %u32_1
+)";
+
+  CompileSuccessfully(GenerateShaderCode(body));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("GLSL.std.450 Frexp: "
+                        "expected operand Exp to be a pointer"));
+}
+
+TEST_F(ValidateExtInst, GlslStd450FrexpExpNotInt32Pointer) {
+  const std::string body = R"(
+%val1 = OpExtInst %f32 %extinst Frexp %f32_1 %f32_output
+)";
+
+  CompileSuccessfully(GenerateShaderCode(body));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("GLSL.std.450 Frexp: "
+                        "expected operand Exp data type to be a 32-bit int "
+                        "scalar or vector type"));
+}
+
+TEST_F(ValidateExtInst, GlslStd450FrexpExpWrongComponentNumber) {
+  const std::string body = R"(
+%val1 = OpExtInst %f32vec2 %extinst Frexp %f32vec2_01 %u32_output
+)";
+
+  CompileSuccessfully(GenerateShaderCode(body));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("GLSL.std.450 Frexp: "
+                        "expected operand Exp data type to have the same "
+                        "component number as Result Type"));
+}
+
+TEST_F(ValidateExtInst, GlslStd450LdexpSuccess) {
+  const std::string body = R"(
+%val1 = OpExtInst %f32 %extinst Ldexp %f32_h %u32_2
+%val2 = OpExtInst %f32vec2 %extinst Ldexp %f32vec2_01 %u32vec2_12
+%val3 = OpExtInst %f32 %extinst Ldexp %f32_h %u64_1
+)";
+
+  CompileSuccessfully(GenerateShaderCode(body));
+  ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+TEST_F(ValidateExtInst, GlslStd450LdexpIntResultType) {
+  const std::string body = R"(
+%val1 = OpExtInst %u32 %extinst Ldexp %f32_h %u32_2
+)";
+
+  CompileSuccessfully(GenerateShaderCode(body));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("GLSL.std.450 Ldexp: "
+                        "expected Result Type to be a scalar or vector "
+                        "float type"));
+}
+
+TEST_F(ValidateExtInst, GlslStd450LdexpWrongXType) {
+  const std::string body = R"(
+%val1 = OpExtInst %f32 %extinst Ldexp %u32_1 %u32_2
+)";
+
+  CompileSuccessfully(GenerateShaderCode(body));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("GLSL.std.450 Ldexp: "
+                        "expected operand X type to be equal to Result Type"));
+}
+
+TEST_F(ValidateExtInst, GlslStd450LdexpFloatExp) {
+  const std::string body = R"(
+%val1 = OpExtInst %f32 %extinst Ldexp %f32_1 %f32_2
+)";
+
+  CompileSuccessfully(GenerateShaderCode(body));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("GLSL.std.450 Ldexp: "
+                        "expected operand Exp to be a 32-bit int scalar "
+                        "or vector type"));
+}
+
+TEST_F(ValidateExtInst, GlslStd450LdexpExpWrongSize) {
+  const std::string body = R"(
+%val1 = OpExtInst %f32vec2 %extinst Ldexp %f32vec2_12 %u32_2
+)";
+
+  CompileSuccessfully(GenerateShaderCode(body));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("GLSL.std.450 Ldexp: "
+                        "expected operand Exp to have the same component "
+                        "number as Result Type"));
+}
+
+TEST_F(ValidateExtInst, GlslStd450FrexpStructSuccess) {
+  const std::string body = R"(
+%val1 = OpExtInst %struct_f32_u32 %extinst FrexpStruct %f32_h
+%val2 = OpExtInst %struct_f32vec2_u32vec2 %extinst FrexpStruct %f32vec2_01
+)";
+
+  CompileSuccessfully(GenerateShaderCode(body));
+  ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+TEST_F(ValidateExtInst, GlslStd450FrexpStructResultTypeNotStruct) {
+  const std::string body = R"(
+%val1 = OpExtInst %f32 %extinst FrexpStruct %f32_h
+)";
+
+  CompileSuccessfully(GenerateShaderCode(body));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("GLSL.std.450 FrexpStruct: "
+                        "expected Result Type to be a struct with two members, "
+                        "first member a float scalar or vector, second member "
+                        "a 32-bit int scalar or vector with the same number of "
+                        "components as the first member"));
+}
+
+TEST_F(ValidateExtInst, GlslStd450FrexpStructResultTypeStructWrongSize) {
+  const std::string body = R"(
+%val1 = OpExtInst %struct_f32_u32_f32 %extinst FrexpStruct %f32_h
+)";
+
+  CompileSuccessfully(GenerateShaderCode(body));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("GLSL.std.450 FrexpStruct: "
+                        "expected Result Type to be a struct with two members, "
+                        "first member a float scalar or vector, second member "
+                        "a 32-bit int scalar or vector with the same number of "
+                        "components as the first member"));
+}
+
+TEST_F(ValidateExtInst, GlslStd450FrexpStructResultTypeStructWrongMember1) {
+  const std::string body = R"(
+%val1 = OpExtInst %struct_u32_u32 %extinst FrexpStruct %f32_h
+)";
+
+  CompileSuccessfully(GenerateShaderCode(body));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("GLSL.std.450 FrexpStruct: "
+                        "expected Result Type to be a struct with two members, "
+                        "first member a float scalar or vector, second member "
+                        "a 32-bit int scalar or vector with the same number of "
+                        "components as the first member"));
+}
+
+TEST_F(ValidateExtInst, GlslStd450FrexpStructResultTypeStructWrongMember2) {
+  const std::string body = R"(
+%val1 = OpExtInst %struct_f32_f32 %extinst FrexpStruct %f32_h
+)";
+
+  CompileSuccessfully(GenerateShaderCode(body));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("GLSL.std.450 FrexpStruct: "
+                        "expected Result Type to be a struct with two members, "
+                        "first member a float scalar or vector, second member "
+                        "a 32-bit int scalar or vector with the same number of "
+                        "components as the first member"));
+}
+
+TEST_F(ValidateExtInst, GlslStd450FrexpStructXWrongType) {
+  const std::string body = R"(
+%val1 = OpExtInst %struct_f32_u32 %extinst FrexpStruct %f64_0
+)";
+
+  CompileSuccessfully(GenerateShaderCode(body));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("GLSL.std.450 FrexpStruct: "
+                        "expected operand X type to be equal to the first "
+                        "member of Result Type struct"));
+}
+
+TEST_P(ValidateGlslStd450Pack, Success) {
+  const std::string ext_inst_name = GetParam();
+  const uint32_t num_components = GetPackedNumComponents(ext_inst_name);
+  const uint32_t packed_bit_width = GetPackedBitWidth(ext_inst_name);
+  const uint32_t total_bit_width = num_components * packed_bit_width;
+  const std::string vec_str =
+      num_components == 2 ? " %f32vec2_01\n" : " %f32vec4_0123\n";
+
+  std::ostringstream body;
+  body << "%val1 = OpExtInst %u" << total_bit_width << " %extinst "
+       << ext_inst_name << vec_str;
+  body << "%val2 = OpExtInst %s" << total_bit_width << " %extinst "
+       << ext_inst_name << vec_str;
+  CompileSuccessfully(GenerateShaderCode(body.str()));
+  ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+TEST_P(ValidateGlslStd450Pack, Float32ResultType) {
+  const std::string ext_inst_name = GetParam();
+  const uint32_t num_components = GetPackedNumComponents(ext_inst_name);
+  const uint32_t packed_bit_width = GetPackedBitWidth(ext_inst_name);
+  const uint32_t total_bit_width = num_components * packed_bit_width;
+  const std::string vec_str =
+      num_components == 2 ? " %f32vec2_01\n" : " %f32vec4_0123\n";
+
+  std::ostringstream body;
+  body << "%val1 = OpExtInst %f" << total_bit_width << " %extinst "
+       << ext_inst_name << vec_str;
+
+  std::ostringstream expected;
+  expected << "GLSL.std.450 " << ext_inst_name
+           << ": expected Result Type to be " << total_bit_width
+           << "-bit int scalar type";
+
+  CompileSuccessfully(GenerateShaderCode(body.str()));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(), HasSubstr(expected.str()));
+}
+
+TEST_P(ValidateGlslStd450Pack, Int16ResultType) {
+  const std::string ext_inst_name = GetParam();
+  const uint32_t num_components = GetPackedNumComponents(ext_inst_name);
+  const uint32_t packed_bit_width = GetPackedBitWidth(ext_inst_name);
+  const uint32_t total_bit_width = num_components * packed_bit_width;
+  const std::string vec_str =
+      num_components == 2 ? " %f32vec2_01\n" : " %f32vec4_0123\n";
+
+  std::ostringstream body;
+  body << "%val1 = OpExtInst %u16 %extinst " << ext_inst_name << vec_str;
+
+  std::ostringstream expected;
+  expected << "GLSL.std.450 " << ext_inst_name
+           << ": expected Result Type to be " << total_bit_width
+           << "-bit int scalar type";
+
+  CompileSuccessfully(GenerateShaderCode(body.str()));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(), HasSubstr(expected.str()));
+}
+
+TEST_P(ValidateGlslStd450Pack, VNotVector) {
+  const std::string ext_inst_name = GetParam();
+  const uint32_t num_components = GetPackedNumComponents(ext_inst_name);
+  const uint32_t packed_bit_width = GetPackedBitWidth(ext_inst_name);
+  const uint32_t total_bit_width = num_components * packed_bit_width;
+
+  std::ostringstream body;
+  body << "%val1 = OpExtInst %u" << total_bit_width << " %extinst "
+       << ext_inst_name << " %f32_1\n";
+
+  std::ostringstream expected;
+  expected << "GLSL.std.450 " << ext_inst_name
+           << ": expected operand V to be a 32-bit float vector of size "
+           << num_components;
+
+  CompileSuccessfully(GenerateShaderCode(body.str()));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(), HasSubstr(expected.str()));
+}
+
+TEST_P(ValidateGlslStd450Pack, VNotFloatVector) {
+  const std::string ext_inst_name = GetParam();
+  const uint32_t num_components = GetPackedNumComponents(ext_inst_name);
+  const uint32_t packed_bit_width = GetPackedBitWidth(ext_inst_name);
+  const uint32_t total_bit_width = num_components * packed_bit_width;
+  const std::string vec_str =
+      num_components == 2 ? " %u32vec2_01\n" : " %u32vec4_0123\n";
+
+  std::ostringstream body;
+  body << "%val1 = OpExtInst %u" << total_bit_width << " %extinst "
+       << ext_inst_name << vec_str;
+
+  std::ostringstream expected;
+  expected << "GLSL.std.450 " << ext_inst_name
+           << ": expected operand V to be a 32-bit float vector of size "
+           << num_components;
+
+  CompileSuccessfully(GenerateShaderCode(body.str()));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(), HasSubstr(expected.str()));
+}
+
+TEST_P(ValidateGlslStd450Pack, VNotFloat32Vector) {
+  const std::string ext_inst_name = GetParam();
+  const uint32_t num_components = GetPackedNumComponents(ext_inst_name);
+  const uint32_t packed_bit_width = GetPackedBitWidth(ext_inst_name);
+  const uint32_t total_bit_width = num_components * packed_bit_width;
+  const std::string vec_str =
+      num_components == 2 ? " %f64vec2_01\n" : " %f64vec4_0123\n";
+
+  std::ostringstream body;
+  body << "%val1 = OpExtInst %u" << total_bit_width << " %extinst "
+       << ext_inst_name << vec_str;
+
+  std::ostringstream expected;
+  expected << "GLSL.std.450 " << ext_inst_name
+           << ": expected operand V to be a 32-bit float vector of size "
+           << num_components;
+
+  CompileSuccessfully(GenerateShaderCode(body.str()));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(), HasSubstr(expected.str()));
+}
+
+TEST_P(ValidateGlslStd450Pack, VWrongSizeVector) {
+  const std::string ext_inst_name = GetParam();
+  const uint32_t num_components = GetPackedNumComponents(ext_inst_name);
+  const uint32_t packed_bit_width = GetPackedBitWidth(ext_inst_name);
+  const uint32_t total_bit_width = num_components * packed_bit_width;
+  const std::string vec_str =
+      num_components == 4 ? " %f32vec2_01\n" : " %f32vec4_0123\n";
+
+  std::ostringstream body;
+  body << "%val1 = OpExtInst %u" << total_bit_width << " %extinst "
+       << ext_inst_name << vec_str;
+
+  std::ostringstream expected;
+  expected << "GLSL.std.450 " << ext_inst_name
+           << ": expected operand V to be a 32-bit float vector of size "
+           << num_components;
+
+  CompileSuccessfully(GenerateShaderCode(body.str()));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(), HasSubstr(expected.str()));
+}
+
+INSTANTIATE_TEST_CASE_P(AllPack, ValidateGlslStd450Pack,
+                        ::testing::ValuesIn(std::vector<std::string>{
+                            "PackSnorm4x8",
+                            "PackUnorm4x8",
+                            "PackSnorm2x16",
+                            "PackUnorm2x16",
+                            "PackHalf2x16",
+                        }), );
+
+TEST_F(ValidateExtInst, PackDouble2x32Success) {
+  const std::string body = R"(
+%val1 = OpExtInst %f64 %extinst PackDouble2x32 %f32vec2_01
+)";
+
+  CompileSuccessfully(GenerateShaderCode(body));
+  ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+TEST_F(ValidateExtInst, PackDouble2x32Float32ResultType) {
+  const std::string body = R"(
+%val1 = OpExtInst %f32 %extinst PackDouble2x32 %f32vec2_01
+)";
+
+  CompileSuccessfully(GenerateShaderCode(body));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("GLSL.std.450 PackDouble2x32: expected Result Type to "
+                        "be 64-bit float scalar type"));
+}
+
+TEST_F(ValidateExtInst, PackDouble2x32Int64ResultType) {
+  const std::string body = R"(
+%val1 = OpExtInst %u64 %extinst PackDouble2x32 %f32vec2_01
+)";
+
+  CompileSuccessfully(GenerateShaderCode(body));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("GLSL.std.450 PackDouble2x32: expected Result Type to "
+                        "be 64-bit float scalar type"));
+}
+
+TEST_F(ValidateExtInst, PackDouble2x32VNotVector) {
+  const std::string body = R"(
+%val1 = OpExtInst %f64 %extinst PackDouble2x32 %f64_1
+)";
+
+  CompileSuccessfully(GenerateShaderCode(body));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("GLSL.std.450 PackDouble2x32: expected operand V to be "
+                        "a 32-bit float vector of size 2"));
+}
+
+TEST_F(ValidateExtInst, PackDouble2x32VNotFloatVector) {
+  const std::string body = R"(
+%val1 = OpExtInst %f64 %extinst PackDouble2x32 %u32vec2_01
+)";
+
+  CompileSuccessfully(GenerateShaderCode(body));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("GLSL.std.450 PackDouble2x32: expected operand V to be "
+                        "a 32-bit float vector of size 2"));
+}
+
+TEST_F(ValidateExtInst, PackDouble2x32VNotFloat32Vector) {
+  const std::string body = R"(
+%val1 = OpExtInst %f64 %extinst PackDouble2x32 %f64vec2_01
+)";
+
+  CompileSuccessfully(GenerateShaderCode(body));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("GLSL.std.450 PackDouble2x32: expected operand V to be "
+                        "a 32-bit float vector of size 2"));
+}
+
+TEST_F(ValidateExtInst, PackDouble2x32VWrongSize) {
+  const std::string body = R"(
+%val1 = OpExtInst %f64 %extinst PackDouble2x32 %f32vec4_0123
+)";
+
+  CompileSuccessfully(GenerateShaderCode(body));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("GLSL.std.450 PackDouble2x32: expected operand V to be "
+                        "a 32-bit float vector of size 2"));
+}
+
+TEST_P(ValidateGlslStd450Unpack, Success) {
+  const std::string ext_inst_name = GetParam();
+  const uint32_t num_components = GetPackedNumComponents(ext_inst_name);
+  const uint32_t packed_bit_width = GetPackedBitWidth(ext_inst_name);
+  const uint32_t total_bit_width = num_components * packed_bit_width;
+  const std::string result_type_str =
+      num_components == 2 ? "%f32vec2" : " %f32vec4";
+
+  std::ostringstream body;
+  body << "%val1 = OpExtInst " << result_type_str << " %extinst "
+       << ext_inst_name << " %u" << total_bit_width << "_1\n";
+  body << "%val2 = OpExtInst " << result_type_str << " %extinst "
+       << ext_inst_name << " %s" << total_bit_width << "_1\n";
+  CompileSuccessfully(GenerateShaderCode(body.str()));
+  ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+TEST_P(ValidateGlslStd450Unpack, ResultTypeNotVector) {
+  const std::string ext_inst_name = GetParam();
+  const uint32_t num_components = GetPackedNumComponents(ext_inst_name);
+  const uint32_t packed_bit_width = GetPackedBitWidth(ext_inst_name);
+  const uint32_t total_bit_width = num_components * packed_bit_width;
+  const std::string result_type_str = "%f32";
+
+  std::ostringstream body;
+  body << "%val1 = OpExtInst " << result_type_str << " %extinst "
+       << ext_inst_name << " %u" << total_bit_width << "_1\n";
+
+  std::ostringstream expected;
+  expected << "GLSL.std.450 " << ext_inst_name
+           << ": expected Result Type to be a 32-bit float vector of size "
+           << num_components;
+
+  CompileSuccessfully(GenerateShaderCode(body.str()));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(), HasSubstr(expected.str()));
+}
+
+TEST_P(ValidateGlslStd450Unpack, ResultTypeNotFloatVector) {
+  const std::string ext_inst_name = GetParam();
+  const uint32_t num_components = GetPackedNumComponents(ext_inst_name);
+  const uint32_t packed_bit_width = GetPackedBitWidth(ext_inst_name);
+  const uint32_t total_bit_width = num_components * packed_bit_width;
+  const std::string result_type_str =
+      num_components == 2 ? "%u32vec2" : " %u32vec4";
+
+  std::ostringstream body;
+  body << "%val1 = OpExtInst " << result_type_str << " %extinst "
+       << ext_inst_name << " %u" << total_bit_width << "_1\n";
+
+  std::ostringstream expected;
+  expected << "GLSL.std.450 " << ext_inst_name
+           << ": expected Result Type to be a 32-bit float vector of size "
+           << num_components;
+
+  CompileSuccessfully(GenerateShaderCode(body.str()));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(), HasSubstr(expected.str()));
+}
+
+TEST_P(ValidateGlslStd450Unpack, ResultTypeNotFloat32Vector) {
+  const std::string ext_inst_name = GetParam();
+  const uint32_t num_components = GetPackedNumComponents(ext_inst_name);
+  const uint32_t packed_bit_width = GetPackedBitWidth(ext_inst_name);
+  const uint32_t total_bit_width = num_components * packed_bit_width;
+  const std::string result_type_str =
+      num_components == 2 ? "%f64vec2" : " %f64vec4";
+
+  std::ostringstream body;
+  body << "%val1 = OpExtInst " << result_type_str << " %extinst "
+       << ext_inst_name << " %u" << total_bit_width << "_1\n";
+
+  std::ostringstream expected;
+  expected << "GLSL.std.450 " << ext_inst_name
+           << ": expected Result Type to be a 32-bit float vector of size "
+           << num_components;
+
+  CompileSuccessfully(GenerateShaderCode(body.str()));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(), HasSubstr(expected.str()));
+}
+
+TEST_P(ValidateGlslStd450Unpack, ResultTypeWrongSize) {
+  const std::string ext_inst_name = GetParam();
+  const uint32_t num_components = GetPackedNumComponents(ext_inst_name);
+  const uint32_t packed_bit_width = GetPackedBitWidth(ext_inst_name);
+  const uint32_t total_bit_width = num_components * packed_bit_width;
+  const std::string result_type_str =
+      num_components == 4 ? "%f32vec2" : " %f32vec4";
+
+  std::ostringstream body;
+  body << "%val1 = OpExtInst " << result_type_str << " %extinst "
+       << ext_inst_name << " %u" << total_bit_width << "_1\n";
+
+  std::ostringstream expected;
+  expected << "GLSL.std.450 " << ext_inst_name
+           << ": expected Result Type to be a 32-bit float vector of size "
+           << num_components;
+
+  CompileSuccessfully(GenerateShaderCode(body.str()));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(), HasSubstr(expected.str()));
+}
+
+TEST_P(ValidateGlslStd450Unpack, ResultPNotInt) {
+  const std::string ext_inst_name = GetParam();
+  const uint32_t num_components = GetPackedNumComponents(ext_inst_name);
+  const uint32_t packed_bit_width = GetPackedBitWidth(ext_inst_name);
+  const uint32_t total_bit_width = num_components * packed_bit_width;
+  const std::string result_type_str =
+      num_components == 2 ? "%f32vec2" : " %f32vec4";
+
+  std::ostringstream body;
+  body << "%val1 = OpExtInst " << result_type_str << " %extinst "
+       << ext_inst_name << " %f" << total_bit_width << "_1\n";
+
+  std::ostringstream expected;
+  expected << "GLSL.std.450 " << ext_inst_name
+           << ": expected operand P to be a " << total_bit_width
+           << "-bit int scalar";
+
+  CompileSuccessfully(GenerateShaderCode(body.str()));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(), HasSubstr(expected.str()));
+}
+
+TEST_P(ValidateGlslStd450Unpack, ResultPWrongBitWidth) {
+  const std::string ext_inst_name = GetParam();
+  const uint32_t num_components = GetPackedNumComponents(ext_inst_name);
+  const uint32_t packed_bit_width = GetPackedBitWidth(ext_inst_name);
+  const uint32_t total_bit_width = num_components * packed_bit_width;
+  const uint32_t wrong_bit_width = total_bit_width == 32 ? 64 : 32;
+  const std::string result_type_str =
+      num_components == 2 ? "%f32vec2" : " %f32vec4";
+
+  std::ostringstream body;
+  body << "%val1 = OpExtInst " << result_type_str << " %extinst "
+       << ext_inst_name << " %u" << wrong_bit_width << "_1\n";
+
+  std::ostringstream expected;
+  expected << "GLSL.std.450 " << ext_inst_name
+           << ": expected operand P to be a " << total_bit_width
+           << "-bit int scalar";
+
+  CompileSuccessfully(GenerateShaderCode(body.str()));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(), HasSubstr(expected.str()));
+}
+
+INSTANTIATE_TEST_CASE_P(AllUnpack, ValidateGlslStd450Unpack,
+                        ::testing::ValuesIn(std::vector<std::string>{
+                            "UnpackSnorm4x8",
+                            "UnpackUnorm4x8",
+                            "UnpackSnorm2x16",
+                            "UnpackUnorm2x16",
+                            "UnpackHalf2x16",
+                        }), );
+
+TEST_F(ValidateExtInst, UnpackDouble2x32Success) {
+  const std::string body = R"(
+%val1 = OpExtInst %f32vec2 %extinst UnpackDouble2x32 %f64_1
+)";
+
+  CompileSuccessfully(GenerateShaderCode(body));
+  ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+TEST_F(ValidateExtInst, UnpackDouble2x32ResultTypeNotVector) {
+  const std::string body = R"(
+%val1 = OpExtInst %f64 %extinst UnpackDouble2x32 %f64_1
+)";
+
+  CompileSuccessfully(GenerateShaderCode(body));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("GLSL.std.450 UnpackDouble2x32: expected Result Type "
+                        "to be a 32-bit float vector of size 2"));
+}
+
+TEST_F(ValidateExtInst, UnpackDouble2x32ResultTypeNotFloatVector) {
+  const std::string body = R"(
+%val1 = OpExtInst %u32vec2 %extinst UnpackDouble2x32 %f64_1
+)";
+
+  CompileSuccessfully(GenerateShaderCode(body));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("GLSL.std.450 UnpackDouble2x32: expected Result Type "
+                        "to be a 32-bit float vector of size 2"));
+}
+
+TEST_F(ValidateExtInst, UnpackDouble2x32ResultTypeNotFloat32Vector) {
+  const std::string body = R"(
+%val1 = OpExtInst %f64vec2 %extinst UnpackDouble2x32 %f64_1
+)";
+
+  CompileSuccessfully(GenerateShaderCode(body));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("GLSL.std.450 UnpackDouble2x32: expected Result Type "
+                        "to be a 32-bit float vector of size 2"));
+}
+
+TEST_F(ValidateExtInst, UnpackDouble2x32ResultTypeWrongSize) {
+  const std::string body = R"(
+%val1 = OpExtInst %f32vec4 %extinst UnpackDouble2x32 %f64_1
+)";
+
+  CompileSuccessfully(GenerateShaderCode(body));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("GLSL.std.450 UnpackDouble2x32: expected Result Type "
+                        "to be a 32-bit float vector of size 2"));
+}
+
+TEST_F(ValidateExtInst, UnpackDouble2x32VNotFloat) {
+  const std::string body = R"(
+%val1 = OpExtInst %f32vec2 %extinst UnpackDouble2x32 %u64_1
+)";
+
+  CompileSuccessfully(GenerateShaderCode(body));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("GLSL.std.450 UnpackDouble2x32: expected operand V to "
+                        "be a 64-bit float scalar"));
+}
+
+TEST_F(ValidateExtInst, UnpackDouble2x32VNotFloat64) {
+  const std::string body = R"(
+%val1 = OpExtInst %f32vec2 %extinst UnpackDouble2x32 %f32_1
+)";
+
+  CompileSuccessfully(GenerateShaderCode(body));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("GLSL.std.450 UnpackDouble2x32: expected operand V to "
+                        "be a 64-bit float scalar"));
+}
+
+TEST_F(ValidateExtInst, GlslStd450LengthSuccess) {
+  const std::string body = R"(
+%val1 = OpExtInst %f32 %extinst Length %f32_1
+%val2 = OpExtInst %f32 %extinst Length %f32vec2_01
+%val3 = OpExtInst %f32 %extinst Length %f32vec4_0123
+)";
+
+  CompileSuccessfully(GenerateShaderCode(body));
+  ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+TEST_F(ValidateExtInst, GlslStd450LengthIntResultType) {
+  const std::string body = R"(
+%val1 = OpExtInst %u32 %extinst Length %f32vec2_01
+)";
+
+  CompileSuccessfully(GenerateShaderCode(body));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("GLSL.std.450 Length: "
+                        "expected Result Type to be a float scalar type"));
+}
+
+TEST_F(ValidateExtInst, GlslStd450LengthIntX) {
+  const std::string body = R"(
+%val1 = OpExtInst %f32 %extinst Length %u32vec2_01
+)";
+
+  CompileSuccessfully(GenerateShaderCode(body));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("GLSL.std.450 Length: "
+                        "expected operand X to be of float scalar or "
+                        "vector type"));
+}
+
+TEST_F(ValidateExtInst, GlslStd450LengthDifferentType) {
+  const std::string body = R"(
+%val1 = OpExtInst %f64 %extinst Length %f32vec2_01
+)";
+
+  CompileSuccessfully(GenerateShaderCode(body));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("GLSL.std.450 Length: "
+                        "expected operand X component type to be equal to "
+                        "Result Type"));
+}
+
+TEST_F(ValidateExtInst, GlslStd450DistanceSuccess) {
+  const std::string body = R"(
+%val1 = OpExtInst %f32 %extinst Distance %f32_0 %f32_1
+%val2 = OpExtInst %f32 %extinst Distance %f32vec2_01 %f32vec2_12
+%val3 = OpExtInst %f32 %extinst Distance %f32vec4_0123 %f32vec4_1234
+)";
+
+  CompileSuccessfully(GenerateShaderCode(body));
+  ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+TEST_F(ValidateExtInst, GlslStd450DistanceIntResultType) {
+  const std::string body = R"(
+%val1 = OpExtInst %u32 %extinst Distance %f32vec2_01 %f32vec2_12
+)";
+
+  CompileSuccessfully(GenerateShaderCode(body));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("GLSL.std.450 Distance: "
+                        "expected Result Type to be a float scalar type"));
+}
+
+TEST_F(ValidateExtInst, GlslStd450DistanceIntP0) {
+  const std::string body = R"(
+%val1 = OpExtInst %f32 %extinst Distance %u32_0 %f32_1
+)";
+
+  CompileSuccessfully(GenerateShaderCode(body));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("GLSL.std.450 Distance: "
+                        "expected operand P0 to be of float scalar or "
+                        "vector type"));
+}
+
+TEST_F(ValidateExtInst, GlslStd450DistanceF64VectorP0) {
+  const std::string body = R"(
+%val1 = OpExtInst %f32 %extinst Distance %f64vec2_01 %f32vec2_12
+)";
+
+  CompileSuccessfully(GenerateShaderCode(body));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("GLSL.std.450 Distance: "
+                        "expected operand P0 component type to be equal to "
+                        "Result Type"));
+}
+
+TEST_F(ValidateExtInst, GlslStd450DistanceIntP1) {
+  const std::string body = R"(
+%val1 = OpExtInst %f32 %extinst Distance %f32_0 %u32_1
+)";
+
+  CompileSuccessfully(GenerateShaderCode(body));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("GLSL.std.450 Distance: "
+                        "expected operand P1 to be of float scalar or "
+                        "vector type"));
+}
+
+TEST_F(ValidateExtInst, GlslStd450DistanceF64VectorP1) {
+  const std::string body = R"(
+%val1 = OpExtInst %f32 %extinst Distance %f32vec2_12 %f64vec2_01
+)";
+
+  CompileSuccessfully(GenerateShaderCode(body));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("GLSL.std.450 Distance: "
+                        "expected operand P1 component type to be equal to "
+                        "Result Type"));
+}
+
+TEST_F(ValidateExtInst, GlslStd450DistanceDifferentSize) {
+  const std::string body = R"(
+%val1 = OpExtInst %f32 %extinst Distance %f32vec2_01 %f32vec4_0123
+)";
+
+  CompileSuccessfully(GenerateShaderCode(body));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("GLSL.std.450 Distance: "
+                        "expected operands P0 and P1 to have the same number "
+                        "of components"));
+}
+
+TEST_F(ValidateExtInst, GlslStd450CrossSuccess) {
+  const std::string body = R"(
+%val1 = OpExtInst %f32vec3 %extinst Cross %f32vec3_012 %f32vec3_123
+)";
+
+  CompileSuccessfully(GenerateShaderCode(body));
+  ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+TEST_F(ValidateExtInst, GlslStd450CrossIntVectorResultType) {
+  const std::string body = R"(
+%val1 = OpExtInst %u32vec3 %extinst Cross %f32vec3_012 %f32vec3_123
+)";
+
+  CompileSuccessfully(GenerateShaderCode(body));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("GLSL.std.450 Cross: "
+                        "expected Result Type to be a float vector type"));
+}
+
+TEST_F(ValidateExtInst, GlslStd450CrossResultTypeWrongSize) {
+  const std::string body = R"(
+%val1 = OpExtInst %f32vec2 %extinst Cross %f32vec3_012 %f32vec3_123
+)";
+
+  CompileSuccessfully(GenerateShaderCode(body));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("GLSL.std.450 Cross: "
+                        "expected Result Type to have 3 components"));
+}
+
+TEST_F(ValidateExtInst, GlslStd450CrossXWrongType) {
+  const std::string body = R"(
+%val1 = OpExtInst %f32vec3 %extinst Cross %f64vec3_012 %f32vec3_123
+)";
+
+  CompileSuccessfully(GenerateShaderCode(body));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("GLSL.std.450 Cross: "
+                        "expected operand X type to be equal to Result Type"));
+}
+
+TEST_F(ValidateExtInst, GlslStd450CrossYWrongType) {
+  const std::string body = R"(
+%val1 = OpExtInst %f32vec3 %extinst Cross %f32vec3_123 %f64vec3_012
+)";
+
+  CompileSuccessfully(GenerateShaderCode(body));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("GLSL.std.450 Cross: "
+                        "expected operand Y type to be equal to Result Type"));
+}
+
+TEST_F(ValidateExtInst, GlslStd450RefractSuccess) {
+  const std::string body = R"(
+%val1 = OpExtInst %f32 %extinst Refract %f32_1 %f32_1 %f32_1
+%val2 = OpExtInst %f32vec2 %extinst Refract %f32vec2_01 %f32vec2_01 %f16_1
+)";
+
+  CompileSuccessfully(GenerateShaderCode(body));
+  ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+TEST_F(ValidateExtInst, GlslStd450RefractIntVectorResultType) {
+  const std::string body = R"(
+%val1 = OpExtInst %u32vec2 %extinst Refract %f32vec2_01 %f32vec2_01 %f32_1
+)";
+
+  CompileSuccessfully(GenerateShaderCode(body));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("GLSL.std.450 Refract: "
+                        "expected Result Type to be a float scalar or "
+                        "vector type"));
+}
+
+TEST_F(ValidateExtInst, GlslStd450RefractIntVectorI) {
+  const std::string body = R"(
+%val1 = OpExtInst %f32vec2 %extinst Refract %u32vec2_01 %f32vec2_01 %f32_1
+)";
+
+  CompileSuccessfully(GenerateShaderCode(body));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("GLSL.std.450 Refract: "
+                        "expected operand I to be of type equal to "
+                        "Result Type"));
+}
+
+TEST_F(ValidateExtInst, GlslStd450RefractIntVectorN) {
+  const std::string body = R"(
+%val1 = OpExtInst %f32vec2 %extinst Refract %f32vec2_01 %u32vec2_01 %f32_1
+)";
+
+  CompileSuccessfully(GenerateShaderCode(body));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("GLSL.std.450 Refract: "
+                        "expected operand N to be of type equal to "
+                        "Result Type"));
+}
+
+TEST_F(ValidateExtInst, GlslStd450RefractIntEta) {
+  const std::string body = R"(
+%val1 = OpExtInst %f32vec2 %extinst Refract %f32vec2_01 %f32vec2_01 %u32_1
+)";
+
+  CompileSuccessfully(GenerateShaderCode(body));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("GLSL.std.450 Refract: "
+                        "expected operand Eta to be a 16 or 32-bit "
+                        "float scalar"));
+}
+
+TEST_F(ValidateExtInst, GlslStd450RefractFloat64Eta) {
+  const std::string body = R"(
+%val1 = OpExtInst %f32vec2 %extinst Refract %f32vec2_01 %f32vec2_01 %f64_1
+)";
+
+  CompileSuccessfully(GenerateShaderCode(body));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("GLSL.std.450 Refract: "
+                        "expected operand Eta to be a 16 or 32-bit "
+                        "float scalar"));
+}
+
+TEST_F(ValidateExtInst, GlslStd450InterpolateAtCentroidSuccess) {
+  const std::string body = R"(
+%val1 = OpExtInst %f32 %extinst InterpolateAtCentroid %f32_input
+%val2 = OpExtInst %f32vec2 %extinst InterpolateAtCentroid %f32vec2_input
+)";
+
+  CompileSuccessfully(
+      GenerateShaderCode(body, "OpCapability InterpolationFunction\n"));
+  ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+TEST_F(ValidateExtInst, GlslStd450InterpolateAtCentroidNoCapability) {
+  const std::string body = R"(
+%val1 = OpExtInst %f32 %extinst InterpolateAtCentroid %f32_input
+)";
+
+  CompileSuccessfully(GenerateShaderCode(body));
+  ASSERT_EQ(SPV_ERROR_INVALID_CAPABILITY, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("GLSL.std.450 InterpolateAtCentroid requires "
+                        "capability InterpolationFunction"));
+}
+
+TEST_F(ValidateExtInst, GlslStd450InterpolateAtCentroidIntResultType) {
+  const std::string body = R"(
+%val1 = OpExtInst %u32 %extinst InterpolateAtCentroid %f32_input
+)";
+
+  CompileSuccessfully(
+      GenerateShaderCode(body, "OpCapability InterpolationFunction\n"));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("GLSL.std.450 InterpolateAtCentroid: "
+                        "expected Result Type to be a 32-bit float scalar "
+                        "or vector type"));
+}
+
+TEST_F(ValidateExtInst, GlslStd450InterpolateAtCentroidF64ResultType) {
+  const std::string body = R"(
+%val1 = OpExtInst %f64 %extinst InterpolateAtCentroid %f32_input
+)";
+
+  CompileSuccessfully(
+      GenerateShaderCode(body, "OpCapability InterpolationFunction\n"));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("GLSL.std.450 InterpolateAtCentroid: "
+                        "expected Result Type to be a 32-bit float scalar "
+                        "or vector type"));
+}
+
+TEST_F(ValidateExtInst, GlslStd450InterpolateAtCentroidNotPointer) {
+  const std::string body = R"(
+%val1 = OpExtInst %f32 %extinst InterpolateAtCentroid %f32_1
+)";
+
+  CompileSuccessfully(
+      GenerateShaderCode(body, "OpCapability InterpolationFunction\n"));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("GLSL.std.450 InterpolateAtCentroid: "
+                        "expected Interpolant to be a pointer"));
+}
+
+TEST_F(ValidateExtInst, GlslStd450InterpolateAtCentroidWrongDataType) {
+  const std::string body = R"(
+%val1 = OpExtInst %f32 %extinst InterpolateAtCentroid %f32vec2_input
+)";
+
+  CompileSuccessfully(
+      GenerateShaderCode(body, "OpCapability InterpolationFunction\n"));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("GLSL.std.450 InterpolateAtCentroid: "
+                        "expected Interpolant data type to be equal to "
+                        "Result Type"));
+}
+
+TEST_F(ValidateExtInst, GlslStd450InterpolateAtCentroidWrongStorageClass) {
+  const std::string body = R"(
+%val1 = OpExtInst %f32 %extinst InterpolateAtCentroid %f32_output
+)";
+
+  CompileSuccessfully(
+      GenerateShaderCode(body, "OpCapability InterpolationFunction\n"));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("GLSL.std.450 InterpolateAtCentroid: "
+                        "expected Interpolant storage class to be Input"));
+}
+
+TEST_F(ValidateExtInst, GlslStd450InterpolateAtCentroidWrongExecutionModel) {
+  const std::string body = R"(
+%val1 = OpExtInst %f32 %extinst InterpolateAtCentroid %f32_input
+)";
+
+  CompileSuccessfully(GenerateShaderCode(
+      body, "OpCapability InterpolationFunction\n", "Vertex"));
+  ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("GLSL.std.450 InterpolateAtCentroid requires "
+                        "Fragment execution model"));
+}
+
+TEST_F(ValidateExtInst, GlslStd450InterpolateAtSampleSuccess) {
+  const std::string body = R"(
+%val1 = OpExtInst %f32 %extinst InterpolateAtSample %f32_input %u32_1
+%val2 = OpExtInst %f32vec2 %extinst InterpolateAtSample %f32vec2_input %u32_1
+)";
+
+  CompileSuccessfully(
+      GenerateShaderCode(body, "OpCapability InterpolationFunction\n"));
+  ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+TEST_F(ValidateExtInst, GlslStd450InterpolateAtSampleNoCapability) {
+  const std::string body = R"(
+%val1 = OpExtInst %f32 %extinst InterpolateAtSample %f32_input %u32_1
+)";
+
+  CompileSuccessfully(GenerateShaderCode(body));
+  ASSERT_EQ(SPV_ERROR_INVALID_CAPABILITY, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("GLSL.std.450 InterpolateAtSample requires "
+                        "capability InterpolationFunction"));
+}
+
+TEST_F(ValidateExtInst, GlslStd450InterpolateAtSampleIntResultType) {
+  const std::string body = R"(
+%val1 = OpExtInst %u32 %extinst InterpolateAtSample %f32_input %u32_1
+)";
+
+  CompileSuccessfully(
+      GenerateShaderCode(body, "OpCapability InterpolationFunction\n"));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("GLSL.std.450 InterpolateAtSample: "
+                        "expected Result Type to be a 32-bit float scalar "
+                        "or vector type"));
+}
+
+TEST_F(ValidateExtInst, GlslStd450InterpolateAtSampleF64ResultType) {
+  const std::string body = R"(
+%val1 = OpExtInst %f64 %extinst InterpolateAtSample %f32_input %u32_1
+)";
+
+  CompileSuccessfully(
+      GenerateShaderCode(body, "OpCapability InterpolationFunction\n"));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("GLSL.std.450 InterpolateAtSample: "
+                        "expected Result Type to be a 32-bit float scalar "
+                        "or vector type"));
+}
+
+TEST_F(ValidateExtInst, GlslStd450InterpolateAtSampleNotPointer) {
+  const std::string body = R"(
+%val1 = OpExtInst %f32 %extinst InterpolateAtSample %f32_1 %u32_1
+)";
+
+  CompileSuccessfully(
+      GenerateShaderCode(body, "OpCapability InterpolationFunction\n"));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("GLSL.std.450 InterpolateAtSample: "
+                        "expected Interpolant to be a pointer"));
+}
+
+TEST_F(ValidateExtInst, GlslStd450InterpolateAtSampleWrongDataType) {
+  const std::string body = R"(
+%val1 = OpExtInst %f32 %extinst InterpolateAtSample %f32vec2_input %u32_1
+)";
+
+  CompileSuccessfully(
+      GenerateShaderCode(body, "OpCapability InterpolationFunction\n"));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("GLSL.std.450 InterpolateAtSample: "
+                        "expected Interpolant data type to be equal to "
+                        "Result Type"));
+}
+
+TEST_F(ValidateExtInst, GlslStd450InterpolateAtSampleWrongStorageClass) {
+  const std::string body = R"(
+%val1 = OpExtInst %f32 %extinst InterpolateAtSample %f32_output %u32_1
+)";
+
+  CompileSuccessfully(
+      GenerateShaderCode(body, "OpCapability InterpolationFunction\n"));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("GLSL.std.450 InterpolateAtSample: "
+                        "expected Interpolant storage class to be Input"));
+}
+
+TEST_F(ValidateExtInst, GlslStd450InterpolateAtSampleFloatSample) {
+  const std::string body = R"(
+%val1 = OpExtInst %f32 %extinst InterpolateAtSample %f32_input %f32_1
+)";
+
+  CompileSuccessfully(
+      GenerateShaderCode(body, "OpCapability InterpolationFunction\n"));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("GLSL.std.450 InterpolateAtSample: "
+                        "expected Sample to be 32-bit integer"));
+}
+
+TEST_F(ValidateExtInst, GlslStd450InterpolateAtSampleU64Sample) {
+  const std::string body = R"(
+%val1 = OpExtInst %f32 %extinst InterpolateAtSample %f32_input %u64_1
+)";
+
+  CompileSuccessfully(
+      GenerateShaderCode(body, "OpCapability InterpolationFunction\n"));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("GLSL.std.450 InterpolateAtSample: "
+                        "expected Sample to be 32-bit integer"));
+}
+
+TEST_F(ValidateExtInst, GlslStd450InterpolateAtSampleWrongExecutionModel) {
+  const std::string body = R"(
+%val1 = OpExtInst %f32 %extinst InterpolateAtSample %f32_input %u32_1
+)";
+
+  CompileSuccessfully(GenerateShaderCode(
+      body, "OpCapability InterpolationFunction\n", "Vertex"));
+  ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("GLSL.std.450 InterpolateAtSample requires "
+                        "Fragment execution model"));
+}
+
+TEST_F(ValidateExtInst, GlslStd450InterpolateAtOffsetSuccess) {
+  const std::string body = R"(
+%val1 = OpExtInst %f32 %extinst InterpolateAtOffset %f32_input %f32vec2_01
+%val2 = OpExtInst %f32vec2 %extinst InterpolateAtOffset %f32vec2_input %f32vec2_01
+)";
+
+  CompileSuccessfully(
+      GenerateShaderCode(body, "OpCapability InterpolationFunction\n"));
+  ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+TEST_F(ValidateExtInst, GlslStd450InterpolateAtOffsetNoCapability) {
+  const std::string body = R"(
+%val1 = OpExtInst %f32 %extinst InterpolateAtOffset %f32_input %f32vec2_01
+)";
+
+  CompileSuccessfully(GenerateShaderCode(body));
+  ASSERT_EQ(SPV_ERROR_INVALID_CAPABILITY, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("GLSL.std.450 InterpolateAtOffset requires "
+                        "capability InterpolationFunction"));
+}
+
+TEST_F(ValidateExtInst, GlslStd450InterpolateAtOffsetIntResultType) {
+  const std::string body = R"(
+%val1 = OpExtInst %u32 %extinst InterpolateAtOffset %f32_input %f32vec2_01
+)";
+
+  CompileSuccessfully(
+      GenerateShaderCode(body, "OpCapability InterpolationFunction\n"));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("GLSL.std.450 InterpolateAtOffset: "
+                        "expected Result Type to be a 32-bit float scalar "
+                        "or vector type"));
+}
+
+TEST_F(ValidateExtInst, GlslStd450InterpolateAtOffsetF64ResultType) {
+  const std::string body = R"(
+%val1 = OpExtInst %f64 %extinst InterpolateAtOffset %f32_input %f32vec2_01
+)";
+
+  CompileSuccessfully(
+      GenerateShaderCode(body, "OpCapability InterpolationFunction\n"));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("GLSL.std.450 InterpolateAtOffset: "
+                        "expected Result Type to be a 32-bit float scalar "
+                        "or vector type"));
+}
+
+TEST_F(ValidateExtInst, GlslStd450InterpolateAtOffsetNotPointer) {
+  const std::string body = R"(
+%val1 = OpExtInst %f32 %extinst InterpolateAtOffset %f32_1 %f32vec2_01
+)";
+
+  CompileSuccessfully(
+      GenerateShaderCode(body, "OpCapability InterpolationFunction\n"));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("GLSL.std.450 InterpolateAtOffset: "
+                        "expected Interpolant to be a pointer"));
+}
+
+TEST_F(ValidateExtInst, GlslStd450InterpolateAtOffsetWrongDataType) {
+  const std::string body = R"(
+%val1 = OpExtInst %f32 %extinst InterpolateAtOffset %f32vec2_input %f32vec2_01
+)";
+
+  CompileSuccessfully(
+      GenerateShaderCode(body, "OpCapability InterpolationFunction\n"));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("GLSL.std.450 InterpolateAtOffset: "
+                        "expected Interpolant data type to be equal to "
+                        "Result Type"));
+}
+
+TEST_F(ValidateExtInst, GlslStd450InterpolateAtOffsetWrongStorageClass) {
+  const std::string body = R"(
+%val1 = OpExtInst %f32 %extinst InterpolateAtOffset %f32_output %f32vec2_01
+)";
+
+  CompileSuccessfully(
+      GenerateShaderCode(body, "OpCapability InterpolationFunction\n"));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("GLSL.std.450 InterpolateAtOffset: "
+                        "expected Interpolant storage class to be Input"));
+}
+
+TEST_F(ValidateExtInst, GlslStd450InterpolateAtOffsetOffsetNotVector) {
+  const std::string body = R"(
+%val1 = OpExtInst %f32 %extinst InterpolateAtOffset %f32_input %f32_0
+)";
+
+  CompileSuccessfully(
+      GenerateShaderCode(body, "OpCapability InterpolationFunction\n"));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("GLSL.std.450 InterpolateAtOffset: "
+                        "expected Offset to be a vector of 2 32-bit floats"));
+}
+
+TEST_F(ValidateExtInst, GlslStd450InterpolateAtOffsetOffsetNotVector2) {
+  const std::string body = R"(
+%val1 = OpExtInst %f32 %extinst InterpolateAtOffset %f32_input %f32vec3_012
+)";
+
+  CompileSuccessfully(
+      GenerateShaderCode(body, "OpCapability InterpolationFunction\n"));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("GLSL.std.450 InterpolateAtOffset: "
+                        "expected Offset to be a vector of 2 32-bit floats"));
+}
+
+TEST_F(ValidateExtInst, GlslStd450InterpolateAtOffsetOffsetNotFloatVector) {
+  const std::string body = R"(
+%val1 = OpExtInst %f32 %extinst InterpolateAtOffset %f32_input %u32vec2_01
+)";
+
+  CompileSuccessfully(
+      GenerateShaderCode(body, "OpCapability InterpolationFunction\n"));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("GLSL.std.450 InterpolateAtOffset: "
+                        "expected Offset to be a vector of 2 32-bit floats"));
+}
+
+TEST_F(ValidateExtInst, GlslStd450InterpolateAtOffsetOffsetNotFloat32Vector) {
+  const std::string body = R"(
+%val1 = OpExtInst %f32 %extinst InterpolateAtOffset %f32_input %f64vec2_01
+)";
+
+  CompileSuccessfully(
+      GenerateShaderCode(body, "OpCapability InterpolationFunction\n"));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("GLSL.std.450 InterpolateAtOffset: "
+                        "expected Offset to be a vector of 2 32-bit floats"));
+}
+
+TEST_F(ValidateExtInst, GlslStd450InterpolateAtOffsetWrongExecutionModel) {
+  const std::string body = R"(
+%val1 = OpExtInst %f32 %extinst InterpolateAtOffset %f32_input %f32vec2_01
+)";
+
+  CompileSuccessfully(GenerateShaderCode(
+      body, "OpCapability InterpolationFunction\n", "Vertex"));
+  ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("GLSL.std.450 InterpolateAtOffset requires "
+                        "Fragment execution model"));
+}
+
+}  // anonymous namespace