Added OpenCL ExtInst validation rules
authorAndrey Tuganov <andreyt@google.com>
Tue, 9 Jan 2018 15:58:22 +0000 (10:58 -0500)
committerDavid Neto <dneto@google.com>
Thu, 1 Feb 2018 19:14:13 +0000 (14:14 -0500)
source/validate_ext_inst.cpp
test/val/val_ext_inst_test.cpp

index e9b1b37..e74dbdc 100644 (file)
 
 namespace libspirv {
 
+namespace {
+
+uint32_t GetSizeTBitWidth(const ValidationState_t& _) {
+  if (_.addressing_model() == SpvAddressingModelPhysical32) return 32;
+
+  if (_.addressing_model() == SpvAddressingModelPhysical64) return 64;
+
+  return 0;
+}
+
+}  // anonymous namespace
+
 // Validates correctness of ExtInst instructions.
 spv_result_t ExtInstPass(ValidationState_t& _,
                          const spv_parsed_instruction_t* inst) {
@@ -743,8 +755,1219 @@ spv_result_t ExtInstPass(ValidationState_t& _,
       }
     }
   } else if (ext_inst_type == SPV_EXT_INST_TYPE_OPENCL_STD) {
-    // TODO(atgoo@github.com) Add validation rules for OpenCL extended
-    // instructions.
+    const OpenCLLIB::Entrypoints ext_inst_key =
+        OpenCLLIB::Entrypoints(ext_inst_index);
+    switch (ext_inst_key) {
+      case OpenCLLIB::Acos:
+      case OpenCLLIB::Acosh:
+      case OpenCLLIB::Acospi:
+      case OpenCLLIB::Asin:
+      case OpenCLLIB::Asinh:
+      case OpenCLLIB::Asinpi:
+      case OpenCLLIB::Atan:
+      case OpenCLLIB::Atan2:
+      case OpenCLLIB::Atanh:
+      case OpenCLLIB::Atanpi:
+      case OpenCLLIB::Atan2pi:
+      case OpenCLLIB::Cbrt:
+      case OpenCLLIB::Ceil:
+      case OpenCLLIB::Copysign:
+      case OpenCLLIB::Cos:
+      case OpenCLLIB::Cosh:
+      case OpenCLLIB::Cospi:
+      case OpenCLLIB::Erfc:
+      case OpenCLLIB::Erf:
+      case OpenCLLIB::Exp:
+      case OpenCLLIB::Exp2:
+      case OpenCLLIB::Exp10:
+      case OpenCLLIB::Expm1:
+      case OpenCLLIB::Fabs:
+      case OpenCLLIB::Fdim:
+      case OpenCLLIB::Floor:
+      case OpenCLLIB::Fma:
+      case OpenCLLIB::Fmax:
+      case OpenCLLIB::Fmin:
+      case OpenCLLIB::Fmod:
+      case OpenCLLIB::Hypot:
+      case OpenCLLIB::Lgamma:
+      case OpenCLLIB::Log:
+      case OpenCLLIB::Log2:
+      case OpenCLLIB::Log10:
+      case OpenCLLIB::Log1p:
+      case OpenCLLIB::Logb:
+      case OpenCLLIB::Mad:
+      case OpenCLLIB::Maxmag:
+      case OpenCLLIB::Minmag:
+      case OpenCLLIB::Nextafter:
+      case OpenCLLIB::Pow:
+      case OpenCLLIB::Powr:
+      case OpenCLLIB::Remainder:
+      case OpenCLLIB::Rint:
+      case OpenCLLIB::Round:
+      case OpenCLLIB::Rsqrt:
+      case OpenCLLIB::Sin:
+      case OpenCLLIB::Sinh:
+      case OpenCLLIB::Sinpi:
+      case OpenCLLIB::Sqrt:
+      case OpenCLLIB::Tan:
+      case OpenCLLIB::Tanh:
+      case OpenCLLIB::Tanpi:
+      case OpenCLLIB::Tgamma:
+      case OpenCLLIB::Trunc:
+      case OpenCLLIB::Half_cos:
+      case OpenCLLIB::Half_divide:
+      case OpenCLLIB::Half_exp:
+      case OpenCLLIB::Half_exp2:
+      case OpenCLLIB::Half_exp10:
+      case OpenCLLIB::Half_log:
+      case OpenCLLIB::Half_log2:
+      case OpenCLLIB::Half_log10:
+      case OpenCLLIB::Half_powr:
+      case OpenCLLIB::Half_recip:
+      case OpenCLLIB::Half_rsqrt:
+      case OpenCLLIB::Half_sin:
+      case OpenCLLIB::Half_sqrt:
+      case OpenCLLIB::Half_tan:
+      case OpenCLLIB::Native_cos:
+      case OpenCLLIB::Native_divide:
+      case OpenCLLIB::Native_exp:
+      case OpenCLLIB::Native_exp2:
+      case OpenCLLIB::Native_exp10:
+      case OpenCLLIB::Native_log:
+      case OpenCLLIB::Native_log2:
+      case OpenCLLIB::Native_log10:
+      case OpenCLLIB::Native_powr:
+      case OpenCLLIB::Native_recip:
+      case OpenCLLIB::Native_rsqrt:
+      case OpenCLLIB::Native_sin:
+      case OpenCLLIB::Native_sqrt:
+      case OpenCLLIB::Native_tan:
+      case OpenCLLIB::FClamp:
+      case OpenCLLIB::Degrees:
+      case OpenCLLIB::FMax_common:
+      case OpenCLLIB::FMin_common:
+      case OpenCLLIB::Mix:
+      case OpenCLLIB::Radians:
+      case OpenCLLIB::Step:
+      case OpenCLLIB::Smoothstep:
+      case OpenCLLIB::Sign: {
+        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 num_components = _.GetDimension(result_type);
+        if (num_components > 4 && num_components != 8 && num_components != 16) {
+          return _.diag(SPV_ERROR_INVALID_DATA)
+                 << ext_inst_name() << ": "
+                 << "expected Result Type to be a scalar or a vector with 2, "
+                    "3, 4, 8 or 16 components";
+        }
+
+        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 OpenCLLIB::Fract:
+      case OpenCLLIB::Modf:
+      case OpenCLLIB::Sincos:
+      case OpenCLLIB::Remquo: {
+        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 num_components = _.GetDimension(result_type);
+        if (num_components > 4 && num_components != 8 && num_components != 16) {
+          return _.diag(SPV_ERROR_INVALID_DATA)
+                 << ext_inst_name() << ": "
+                 << "expected Result Type to be a scalar or a vector with 2, "
+                    "3, 4, 8 or 16 components";
+        }
+
+        uint32_t operand_index = 4;
+        const uint32_t x_type = _.GetOperandTypeId(inst, operand_index++);
+        if (result_type != x_type) {
+          return _.diag(SPV_ERROR_INVALID_DATA)
+                 << ext_inst_name() << ": "
+                 << "expected type of operand X to be equal to Result Type";
+        }
+
+        if (ext_inst_key == OpenCLLIB::Remquo) {
+          const uint32_t y_type = _.GetOperandTypeId(inst, operand_index++);
+          if (result_type != y_type) {
+            return _.diag(SPV_ERROR_INVALID_DATA)
+                   << ext_inst_name() << ": "
+                   << "expected type of operand Y to be equal to Result Type";
+          }
+        }
+
+        const uint32_t p_type = _.GetOperandTypeId(inst, operand_index++);
+        uint32_t p_storage_class = 0;
+        uint32_t p_data_type = 0;
+        if (!_.GetPointerTypeInfo(p_type, &p_data_type, &p_storage_class)) {
+          return _.diag(SPV_ERROR_INVALID_DATA)
+                 << ext_inst_name() << ": "
+                 << "expected the last operand to be a pointer";
+        }
+
+        if (p_storage_class != SpvStorageClassGeneric &&
+            p_storage_class != SpvStorageClassCrossWorkgroup &&
+            p_storage_class != SpvStorageClassWorkgroup &&
+            p_storage_class != SpvStorageClassFunction) {
+          return _.diag(SPV_ERROR_INVALID_DATA)
+                 << ext_inst_name() << ": "
+                 << "expected storage class of the pointer to be Generic, "
+                    "CrossWorkgroup, Workgroup or Function";
+        }
+
+        if (result_type != p_data_type) {
+          return _.diag(SPV_ERROR_INVALID_DATA)
+                 << ext_inst_name() << ": "
+                 << "expected data type of the pointer to be equal to Result "
+                    "Type";
+        }
+        break;
+      }
+
+      case OpenCLLIB::Frexp:
+      case OpenCLLIB::Lgamma_r: {
+        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 num_components = _.GetDimension(result_type);
+        if (num_components > 4 && num_components != 8 && num_components != 16) {
+          return _.diag(SPV_ERROR_INVALID_DATA)
+                 << ext_inst_name() << ": "
+                 << "expected Result Type to be a scalar or a vector with 2, "
+                    "3, 4, 8 or 16 components";
+        }
+
+        const uint32_t x_type = _.GetOperandTypeId(inst, 4);
+        if (result_type != x_type) {
+          return _.diag(SPV_ERROR_INVALID_DATA)
+                 << ext_inst_name() << ": "
+                 << "expected type of operand X to be equal to Result Type";
+        }
+
+        const uint32_t p_type = _.GetOperandTypeId(inst, 5);
+        uint32_t p_storage_class = 0;
+        uint32_t p_data_type = 0;
+        if (!_.GetPointerTypeInfo(p_type, &p_data_type, &p_storage_class)) {
+          return _.diag(SPV_ERROR_INVALID_DATA)
+                 << ext_inst_name() << ": "
+                 << "expected the last operand to be a pointer";
+        }
+
+        if (p_storage_class != SpvStorageClassGeneric &&
+            p_storage_class != SpvStorageClassCrossWorkgroup &&
+            p_storage_class != SpvStorageClassWorkgroup &&
+            p_storage_class != SpvStorageClassFunction) {
+          return _.diag(SPV_ERROR_INVALID_DATA)
+                 << ext_inst_name() << ": "
+                 << "expected storage class of the pointer to be Generic, "
+                    "CrossWorkgroup, Workgroup or Function";
+        }
+
+        if (!_.IsIntScalarOrVectorType(p_data_type) ||
+            _.GetBitWidth(p_data_type) != 32) {
+          return _.diag(SPV_ERROR_INVALID_DATA)
+                 << ext_inst_name() << ": "
+                 << "expected data type of the pointer to be a 32-bit int "
+                    "scalar or vector type";
+        }
+
+        if (_.GetDimension(p_data_type) != num_components) {
+          return _.diag(SPV_ERROR_INVALID_DATA)
+                 << ext_inst_name() << ": "
+                 << "expected data type of the pointer to have the same number "
+                    "of components as Result Type";
+        }
+        break;
+      }
+
+      case OpenCLLIB::Ilogb: {
+        if (!_.IsIntScalarOrVectorType(result_type) ||
+            _.GetBitWidth(result_type) != 32) {
+          return _.diag(SPV_ERROR_INVALID_DATA)
+                 << ext_inst_name() << ": "
+                 << "expected Result Type to be a 32-bit int scalar or vector "
+                    "type";
+        }
+
+        const uint32_t num_components = _.GetDimension(result_type);
+        if (num_components > 4 && num_components != 8 && num_components != 16) {
+          return _.diag(SPV_ERROR_INVALID_DATA)
+                 << ext_inst_name() << ": "
+                 << "expected Result Type to be a scalar or a vector with 2, "
+                    "3, 4, 8 or 16 components";
+        }
+
+        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 a float scalar or vector";
+        }
+
+        if (_.GetDimension(x_type) != num_components) {
+          return _.diag(SPV_ERROR_INVALID_DATA)
+                 << ext_inst_name() << ": "
+                 << "expected operand X to have the same number of components "
+                    "as Result Type";
+        }
+        break;
+      }
+
+      case OpenCLLIB::Ldexp:
+      case OpenCLLIB::Pown:
+      case OpenCLLIB::Rootn: {
+        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 num_components = _.GetDimension(result_type);
+        if (num_components > 4 && num_components != 8 && num_components != 16) {
+          return _.diag(SPV_ERROR_INVALID_DATA)
+                 << ext_inst_name() << ": "
+                 << "expected Result Type to be a scalar or a vector with 2, "
+                    "3, 4, 8 or 16 components";
+        }
+
+        const uint32_t x_type = _.GetOperandTypeId(inst, 4);
+        if (result_type != x_type) {
+          return _.diag(SPV_ERROR_INVALID_DATA)
+                 << ext_inst_name() << ": "
+                 << "expected type of operand X to be equal to Result Type";
+        }
+
+        const uint32_t exp_type = _.GetOperandTypeId(inst, 5);
+        if (!_.IsIntScalarOrVectorType(exp_type) ||
+            _.GetBitWidth(exp_type) != 32) {
+          return _.diag(SPV_ERROR_INVALID_DATA)
+                 << ext_inst_name() << ": "
+                 << "expected the exponent to be a 32-bit int scalar or vector";
+        }
+
+        if (_.GetDimension(exp_type) != num_components) {
+          return _.diag(SPV_ERROR_INVALID_DATA)
+                 << ext_inst_name() << ": "
+                 << "expected the exponent to have the same number of "
+                    "components as Result Type";
+        }
+        break;
+      }
+
+      case OpenCLLIB::Nan: {
+        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 num_components = _.GetDimension(result_type);
+        if (num_components > 4 && num_components != 8 && num_components != 16) {
+          return _.diag(SPV_ERROR_INVALID_DATA)
+                 << ext_inst_name() << ": "
+                 << "expected Result Type to be a scalar or a vector with 2, "
+                    "3, 4, 8 or 16 components";
+        }
+
+        const uint32_t nancode_type = _.GetOperandTypeId(inst, 4);
+        if (!_.IsIntScalarOrVectorType(nancode_type)) {
+          return _.diag(SPV_ERROR_INVALID_DATA)
+                 << ext_inst_name() << ": "
+                 << "expected Nancode to be an int scalar or vector type";
+        }
+
+        if (_.GetDimension(nancode_type) != num_components) {
+          return _.diag(SPV_ERROR_INVALID_DATA)
+                 << ext_inst_name() << ": "
+                 << "expected Nancode to have the same number of components as "
+                    "Result Type";
+        }
+
+        if (_.GetBitWidth(result_type) != _.GetBitWidth(nancode_type)) {
+          return _.diag(SPV_ERROR_INVALID_DATA)
+                 << ext_inst_name() << ": "
+                 << "expected Nancode to have the same bit width as Result "
+                    "Type";
+        }
+        break;
+      }
+
+      case OpenCLLIB::SAbs:
+      case OpenCLLIB::SAbs_diff:
+      case OpenCLLIB::SAdd_sat:
+      case OpenCLLIB::UAdd_sat:
+      case OpenCLLIB::SHadd:
+      case OpenCLLIB::UHadd:
+      case OpenCLLIB::SRhadd:
+      case OpenCLLIB::URhadd:
+      case OpenCLLIB::SClamp:
+      case OpenCLLIB::UClamp:
+      case OpenCLLIB::Clz:
+      case OpenCLLIB::Ctz:
+      case OpenCLLIB::SMad_hi:
+      case OpenCLLIB::UMad_sat:
+      case OpenCLLIB::SMad_sat:
+      case OpenCLLIB::SMax:
+      case OpenCLLIB::UMax:
+      case OpenCLLIB::SMin:
+      case OpenCLLIB::UMin:
+      case OpenCLLIB::SMul_hi:
+      case OpenCLLIB::Rotate:
+      case OpenCLLIB::SSub_sat:
+      case OpenCLLIB::USub_sat:
+      case OpenCLLIB::Popcount:
+      case OpenCLLIB::UAbs:
+      case OpenCLLIB::UAbs_diff:
+      case OpenCLLIB::UMul_hi:
+      case OpenCLLIB::UMad_hi: {
+        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 num_components = _.GetDimension(result_type);
+        if (num_components > 4 && num_components != 8 && num_components != 16) {
+          return _.diag(SPV_ERROR_INVALID_DATA)
+                 << ext_inst_name() << ": "
+                 << "expected Result Type to be a scalar or a vector with 2, "
+                    "3, 4, 8 or 16 components";
+        }
+
+        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 OpenCLLIB::U_Upsample:
+      case OpenCLLIB::S_Upsample: {
+        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_num_components = _.GetDimension(result_type);
+        if (result_num_components > 4 && result_num_components != 8 &&
+            result_num_components != 16) {
+          return _.diag(SPV_ERROR_INVALID_DATA)
+                 << ext_inst_name() << ": "
+                 << "expected Result Type to be a scalar or a vector with 2, "
+                    "3, 4, 8 or 16 components";
+        }
+
+        const uint32_t result_bit_width = _.GetBitWidth(result_type);
+        if (result_bit_width != 16 && result_bit_width != 32 &&
+            result_bit_width != 64) {
+          return _.diag(SPV_ERROR_INVALID_DATA)
+                 << ext_inst_name() << ": "
+                 << "expected bit width of Result Type components to be 16, 32 "
+                    "or 64";
+        }
+
+        const uint32_t hi_type = _.GetOperandTypeId(inst, 4);
+        const uint32_t lo_type = _.GetOperandTypeId(inst, 5);
+
+        if (hi_type != lo_type) {
+          return _.diag(SPV_ERROR_INVALID_DATA)
+                 << ext_inst_name() << ": "
+                 << "expected Hi and Lo operands to have the same type";
+        }
+
+        if (result_num_components != _.GetDimension(hi_type)) {
+          return _.diag(SPV_ERROR_INVALID_DATA)
+                 << ext_inst_name() << ": "
+                 << "expected Hi and Lo operands to have the same number of "
+                    "components as Result Type";
+        }
+
+        if (result_bit_width != 2 * _.GetBitWidth(hi_type)) {
+          return _.diag(SPV_ERROR_INVALID_DATA)
+                 << ext_inst_name() << ": "
+                 << "expected bit width of components of Hi and Lo operands to "
+                    "be half of the bit width of components of Result Type";
+        }
+        break;
+      }
+
+      case OpenCLLIB::SMad24:
+      case OpenCLLIB::UMad24:
+      case OpenCLLIB::SMul24:
+      case OpenCLLIB::UMul24: {
+        if (!_.IsIntScalarOrVectorType(result_type) ||
+            _.GetBitWidth(result_type) != 32) {
+          return _.diag(SPV_ERROR_INVALID_DATA)
+                 << ext_inst_name() << ": "
+                 << "expected Result Type to be a 32-bit int scalar or vector "
+                    "type";
+        }
+
+        const uint32_t num_components = _.GetDimension(result_type);
+        if (num_components > 4 && num_components != 8 && num_components != 16) {
+          return _.diag(SPV_ERROR_INVALID_DATA)
+                 << ext_inst_name() << ": "
+                 << "expected Result Type to be a scalar or a vector with 2, "
+                    "3, 4, 8 or 16 components";
+        }
+
+        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 OpenCLLIB::Cross: {
+        if (!_.IsFloatVectorType(result_type)) {
+          return _.diag(SPV_ERROR_INVALID_DATA)
+                 << ext_inst_name() << ": "
+                 << "expected Result Type to be a float vector type";
+        }
+
+        const uint32_t num_components = _.GetDimension(result_type);
+        if (num_components != 3 && num_components != 4) {
+          return _.diag(SPV_ERROR_INVALID_DATA)
+                 << ext_inst_name() << ": "
+                 << "expected Result Type to have 3 or 4 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 OpenCLLIB::Distance:
+      case OpenCLLIB::Fast_distance: {
+        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";
+        }
+
+        const uint32_t num_components = _.GetDimension(p0_type);
+        if (num_components > 4) {
+          return _.diag(SPV_ERROR_INVALID_DATA)
+                 << ext_inst_name() << ": "
+                 << "expected operand P0 to have no more than 4 components";
+        }
+
+        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 (p0_type != p1_type) {
+          return _.diag(SPV_ERROR_INVALID_DATA)
+                 << ext_inst_name() << ": "
+                 << "expected operands P0 and P1 to be of the same type";
+        }
+        break;
+      }
+
+      case OpenCLLIB::Length:
+      case OpenCLLIB::Fast_length: {
+        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 p_type = _.GetOperandTypeId(inst, 4);
+        if (!_.IsFloatScalarOrVectorType(p_type)) {
+          return _.diag(SPV_ERROR_INVALID_DATA)
+                 << ext_inst_name() << ": "
+                 << "expected operand P to be a float scalar or vector";
+        }
+
+        const uint32_t num_components = _.GetDimension(p_type);
+        if (num_components > 4) {
+          return _.diag(SPV_ERROR_INVALID_DATA)
+                 << ext_inst_name() << ": "
+                 << "expected operand P to have no more than 4 components";
+        }
+
+        if (result_type != _.GetComponentType(p_type)) {
+          return _.diag(SPV_ERROR_INVALID_DATA)
+                 << ext_inst_name() << ": "
+                 << "expected operand P component type to be equal to Result "
+                    "Type";
+        }
+        break;
+      }
+
+      case OpenCLLIB::Normalize:
+      case OpenCLLIB::Fast_normalize: {
+        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 num_components = _.GetDimension(result_type);
+        if (num_components > 4) {
+          return _.diag(SPV_ERROR_INVALID_DATA)
+                 << ext_inst_name() << ": "
+                 << "expected Result Type to have no more than 4 components";
+        }
+
+        const uint32_t p_type = _.GetOperandTypeId(inst, 4);
+        if (p_type != result_type) {
+          return _.diag(SPV_ERROR_INVALID_DATA)
+                 << ext_inst_name() << ": "
+                 << "expected operand P type to be equal to Result Type";
+        }
+        break;
+      }
+
+      case OpenCLLIB::Bitselect: {
+        if (!_.IsFloatScalarOrVectorType(result_type) &&
+            !_.IsIntScalarOrVectorType(result_type)) {
+          return _.diag(SPV_ERROR_INVALID_DATA)
+                 << ext_inst_name() << ": "
+                 << "expected Result Type to be an int or float scalar or "
+                    "vector type";
+        }
+
+        const uint32_t num_components = _.GetDimension(result_type);
+        if (num_components > 4 && num_components != 8 && num_components != 16) {
+          return _.diag(SPV_ERROR_INVALID_DATA)
+                 << ext_inst_name() << ": "
+                 << "expected Result Type to be a scalar or a vector with 2, "
+                    "3, 4, 8 or 16 components";
+        }
+
+        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 OpenCLLIB::Select: {
+        if (!_.IsFloatScalarOrVectorType(result_type) &&
+            !_.IsIntScalarOrVectorType(result_type)) {
+          return _.diag(SPV_ERROR_INVALID_DATA)
+                 << ext_inst_name() << ": "
+                 << "expected Result Type to be an int or float scalar or "
+                    "vector type";
+        }
+
+        const uint32_t num_components = _.GetDimension(result_type);
+        if (num_components > 4 && num_components != 8 && num_components != 16) {
+          return _.diag(SPV_ERROR_INVALID_DATA)
+                 << ext_inst_name() << ": "
+                 << "expected Result Type to be a scalar or a vector with 2, "
+                    "3, 4, 8 or 16 components";
+        }
+
+        const uint32_t a_type = _.GetOperandTypeId(inst, 4);
+        const uint32_t b_type = _.GetOperandTypeId(inst, 5);
+        const uint32_t c_type = _.GetOperandTypeId(inst, 6);
+
+        if (result_type != a_type) {
+          return _.diag(SPV_ERROR_INVALID_DATA)
+                 << ext_inst_name() << ": "
+                 << "expected operand A type to be equal to Result Type";
+        }
+
+        if (result_type != b_type) {
+          return _.diag(SPV_ERROR_INVALID_DATA)
+                 << ext_inst_name() << ": "
+                 << "expected operand B type to be equal to Result Type";
+        }
+
+        if (!_.IsIntScalarOrVectorType(c_type)) {
+          return _.diag(SPV_ERROR_INVALID_DATA)
+                 << ext_inst_name() << ": "
+                 << "expected operand C to be an int scalar or vector";
+        }
+
+        if (num_components != _.GetDimension(c_type)) {
+          return _.diag(SPV_ERROR_INVALID_DATA)
+                 << ext_inst_name() << ": "
+                 << "expected operand C to have the same number of components "
+                    "as Result Type";
+        }
+
+        if (_.GetBitWidth(result_type) != _.GetBitWidth(c_type)) {
+          return _.diag(SPV_ERROR_INVALID_DATA)
+                 << ext_inst_name() << ": "
+                 << "expected operand C to have the same bit width as Result "
+                    "Type";
+        }
+        break;
+      }
+
+      case OpenCLLIB::Vloadn: {
+        if (!_.IsFloatVectorType(result_type) &&
+            !_.IsIntVectorType(result_type)) {
+          return _.diag(SPV_ERROR_INVALID_DATA)
+                 << ext_inst_name() << ": "
+                 << "expected Result Type to be an int or float vector type";
+        }
+
+        const uint32_t num_components = _.GetDimension(result_type);
+        if (num_components > 4 && num_components != 8 && num_components != 16) {
+          return _.diag(SPV_ERROR_INVALID_DATA)
+                 << ext_inst_name() << ": "
+                 << "expected Result Type to have 2, 3, 4, 8 or 16 components";
+        }
+
+        const uint32_t offset_type = _.GetOperandTypeId(inst, 4);
+        const uint32_t p_type = _.GetOperandTypeId(inst, 5);
+
+        const uint32_t size_t_bit_width = GetSizeTBitWidth(_);
+        if (!size_t_bit_width) {
+          return _.diag(SPV_ERROR_INVALID_DATA)
+                 << ext_inst_name()
+                 << " can only be used with physical addressing models";
+        }
+
+        if (!_.IsIntScalarType(offset_type) ||
+            _.GetBitWidth(offset_type) != size_t_bit_width) {
+          return _.diag(SPV_ERROR_INVALID_DATA)
+                 << ext_inst_name() << ": "
+                 << "expected operand Offset to be of type size_t ("
+                 << size_t_bit_width
+                 << "-bit integer for the addressing model used in the module)";
+        }
+
+        uint32_t p_storage_class = 0;
+        uint32_t p_data_type = 0;
+        if (!_.GetPointerTypeInfo(p_type, &p_data_type, &p_storage_class)) {
+          return _.diag(SPV_ERROR_INVALID_DATA)
+                 << ext_inst_name() << ": "
+                 << "expected operand P to be a pointer";
+        }
+
+        if (p_storage_class != SpvStorageClassUniformConstant &&
+            p_storage_class != SpvStorageClassGeneric) {
+          return _.diag(SPV_ERROR_INVALID_DATA)
+                 << ext_inst_name() << ": "
+                 << "expected operand P storage class to be UniformConstant or "
+                    "Generic";
+        }
+
+        if (_.GetComponentType(result_type) != p_data_type) {
+          return _.diag(SPV_ERROR_INVALID_DATA)
+                 << ext_inst_name() << ": "
+                 << "expected operand P data type to be equal to component "
+                    "type of Result Type";
+        }
+
+        const uint32_t n_value = inst->words[7];
+        if (num_components != n_value) {
+          return _.diag(SPV_ERROR_INVALID_DATA)
+                 << ext_inst_name() << ": "
+                 << "expected literal N to be equal to the number of "
+                    "components of Result Type";
+        }
+        break;
+      }
+
+      case OpenCLLIB::Vstoren: {
+        if (_.GetIdOpcode(result_type) != SpvOpTypeVoid) {
+          return _.diag(SPV_ERROR_INVALID_DATA)
+                 << ext_inst_name() << ": expected Result Type to be void";
+        }
+
+        const uint32_t data_type = _.GetOperandTypeId(inst, 4);
+        const uint32_t offset_type = _.GetOperandTypeId(inst, 5);
+        const uint32_t p_type = _.GetOperandTypeId(inst, 6);
+
+        if (!_.IsFloatVectorType(data_type) && !_.IsIntVectorType(data_type)) {
+          return _.diag(SPV_ERROR_INVALID_DATA)
+                 << ext_inst_name() << ": "
+                 << "expected Data to be an int or float vector";
+        }
+
+        const uint32_t num_components = _.GetDimension(data_type);
+        if (num_components > 4 && num_components != 8 && num_components != 16) {
+          return _.diag(SPV_ERROR_INVALID_DATA)
+                 << ext_inst_name() << ": "
+                 << "expected Data to have 2, 3, 4, 8 or 16 components";
+        }
+
+        const uint32_t size_t_bit_width = GetSizeTBitWidth(_);
+        if (!size_t_bit_width) {
+          return _.diag(SPV_ERROR_INVALID_DATA)
+                 << ext_inst_name()
+                 << " can only be used with physical addressing models";
+        }
+
+        if (!_.IsIntScalarType(offset_type) ||
+            _.GetBitWidth(offset_type) != size_t_bit_width) {
+          return _.diag(SPV_ERROR_INVALID_DATA)
+                 << ext_inst_name() << ": "
+                 << "expected operand Offset to be of type size_t ("
+                 << size_t_bit_width
+                 << "-bit integer for the addressing model used in the module)";
+        }
+
+        uint32_t p_storage_class = 0;
+        uint32_t p_data_type = 0;
+        if (!_.GetPointerTypeInfo(p_type, &p_data_type, &p_storage_class)) {
+          return _.diag(SPV_ERROR_INVALID_DATA)
+                 << ext_inst_name() << ": "
+                 << "expected operand P to be a pointer";
+        }
+
+        if (p_storage_class != SpvStorageClassGeneric) {
+          return _.diag(SPV_ERROR_INVALID_DATA)
+                 << ext_inst_name() << ": "
+                 << "expected operand P storage class to be Generic";
+        }
+
+        if (_.GetComponentType(data_type) != p_data_type) {
+          return _.diag(SPV_ERROR_INVALID_DATA)
+                 << ext_inst_name() << ": "
+                 << "expected operand P data type to be equal to the type of "
+                    "operand Data components";
+        }
+        break;
+      }
+
+      case OpenCLLIB::Vload_half: {
+        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 offset_type = _.GetOperandTypeId(inst, 4);
+        const uint32_t p_type = _.GetOperandTypeId(inst, 5);
+
+        const uint32_t size_t_bit_width = GetSizeTBitWidth(_);
+        if (!size_t_bit_width) {
+          return _.diag(SPV_ERROR_INVALID_DATA)
+                 << ext_inst_name()
+                 << " can only be used with physical addressing models";
+        }
+
+        if (!_.IsIntScalarType(offset_type) ||
+            _.GetBitWidth(offset_type) != size_t_bit_width) {
+          return _.diag(SPV_ERROR_INVALID_DATA)
+                 << ext_inst_name() << ": "
+                 << "expected operand Offset to be of type size_t ("
+                 << size_t_bit_width
+                 << "-bit integer for the addressing model used in the module)";
+        }
+
+        uint32_t p_storage_class = 0;
+        uint32_t p_data_type = 0;
+        if (!_.GetPointerTypeInfo(p_type, &p_data_type, &p_storage_class)) {
+          return _.diag(SPV_ERROR_INVALID_DATA)
+                 << ext_inst_name() << ": "
+                 << "expected operand P to be a pointer";
+        }
+
+        if (p_storage_class != SpvStorageClassUniformConstant &&
+            p_storage_class != SpvStorageClassGeneric &&
+            p_storage_class != SpvStorageClassCrossWorkgroup &&
+            p_storage_class != SpvStorageClassWorkgroup &&
+            p_storage_class != SpvStorageClassFunction) {
+          return _.diag(SPV_ERROR_INVALID_DATA)
+                 << ext_inst_name() << ": "
+                 << "expected operand P storage class to be UniformConstant, "
+                    "Generic, CrossWorkgroup, Workgroup or Function";
+        }
+
+        if (!_.IsFloatScalarType(p_data_type) ||
+            _.GetBitWidth(p_data_type) != 16) {
+          return _.diag(SPV_ERROR_INVALID_DATA)
+                 << ext_inst_name() << ": "
+                 << "expected operand P data type to be 16-bit float scalar";
+        }
+        break;
+      }
+
+      case OpenCLLIB::Vload_halfn:
+      case OpenCLLIB::Vloada_halfn: {
+        if (!_.IsFloatVectorType(result_type)) {
+          return _.diag(SPV_ERROR_INVALID_DATA)
+                 << ext_inst_name() << ": "
+                 << "expected Result Type to be a float vector type";
+        }
+
+        const uint32_t num_components = _.GetDimension(result_type);
+        if (num_components > 4 && num_components != 8 && num_components != 16) {
+          return _.diag(SPV_ERROR_INVALID_DATA)
+                 << ext_inst_name() << ": "
+                 << "expected Result Type to have 2, 3, 4, 8 or 16 components";
+        }
+
+        const uint32_t offset_type = _.GetOperandTypeId(inst, 4);
+        const uint32_t p_type = _.GetOperandTypeId(inst, 5);
+
+        const uint32_t size_t_bit_width = GetSizeTBitWidth(_);
+        if (!size_t_bit_width) {
+          return _.diag(SPV_ERROR_INVALID_DATA)
+                 << ext_inst_name()
+                 << " can only be used with physical addressing models";
+        }
+
+        if (!_.IsIntScalarType(offset_type) ||
+            _.GetBitWidth(offset_type) != size_t_bit_width) {
+          return _.diag(SPV_ERROR_INVALID_DATA)
+                 << ext_inst_name() << ": "
+                 << "expected operand Offset to be of type size_t ("
+                 << size_t_bit_width
+                 << "-bit integer for the addressing model used in the module)";
+        }
+
+        uint32_t p_storage_class = 0;
+        uint32_t p_data_type = 0;
+        if (!_.GetPointerTypeInfo(p_type, &p_data_type, &p_storage_class)) {
+          return _.diag(SPV_ERROR_INVALID_DATA)
+                 << ext_inst_name() << ": "
+                 << "expected operand P to be a pointer";
+        }
+
+        if (p_storage_class != SpvStorageClassUniformConstant &&
+            p_storage_class != SpvStorageClassGeneric &&
+            p_storage_class != SpvStorageClassCrossWorkgroup &&
+            p_storage_class != SpvStorageClassWorkgroup &&
+            p_storage_class != SpvStorageClassFunction) {
+          return _.diag(SPV_ERROR_INVALID_DATA)
+                 << ext_inst_name() << ": "
+                 << "expected operand P storage class to be UniformConstant, "
+                    "Generic, CrossWorkgroup, Workgroup or Function";
+        }
+
+        if (!_.IsFloatScalarType(p_data_type) ||
+            _.GetBitWidth(p_data_type) != 16) {
+          return _.diag(SPV_ERROR_INVALID_DATA)
+                 << ext_inst_name() << ": "
+                 << "expected operand P data type to be 16-bit float scalar";
+        }
+
+        const uint32_t n_value = inst->words[7];
+        if (num_components != n_value) {
+          return _.diag(SPV_ERROR_INVALID_DATA)
+                 << ext_inst_name() << ": "
+                 << "expected literal N to be equal to the number of "
+                    "components of Result Type";
+        }
+        break;
+      }
+
+      case OpenCLLIB::Vstore_half:
+      case OpenCLLIB::Vstore_half_r:
+      case OpenCLLIB::Vstore_halfn:
+      case OpenCLLIB::Vstore_halfn_r:
+      case OpenCLLIB::Vstorea_halfn:
+      case OpenCLLIB::Vstorea_halfn_r: {
+        if (_.GetIdOpcode(result_type) != SpvOpTypeVoid) {
+          return _.diag(SPV_ERROR_INVALID_DATA)
+                 << ext_inst_name() << ": expected Result Type to be void";
+        }
+
+        const uint32_t data_type = _.GetOperandTypeId(inst, 4);
+        const uint32_t offset_type = _.GetOperandTypeId(inst, 5);
+        const uint32_t p_type = _.GetOperandTypeId(inst, 6);
+        const uint32_t data_type_bit_width = _.GetBitWidth(data_type);
+
+        if (ext_inst_key == OpenCLLIB::Vstore_half ||
+            ext_inst_key == OpenCLLIB::Vstore_half_r) {
+          if (!_.IsFloatScalarType(data_type) ||
+              (data_type_bit_width != 32 && data_type_bit_width != 64)) {
+            return _.diag(SPV_ERROR_INVALID_DATA)
+                   << ext_inst_name() << ": "
+                   << "expected Data to be a 32 or 64-bit float scalar";
+          }
+        } else {
+          if (!_.IsFloatVectorType(data_type) ||
+              (data_type_bit_width != 32 && data_type_bit_width != 64)) {
+            return _.diag(SPV_ERROR_INVALID_DATA)
+                   << ext_inst_name() << ": "
+                   << "expected Data to be a 32 or 64-bit float vector";
+          }
+
+          const uint32_t num_components = _.GetDimension(data_type);
+          if (num_components > 4 && num_components != 8 &&
+              num_components != 16) {
+            return _.diag(SPV_ERROR_INVALID_DATA)
+                   << ext_inst_name() << ": "
+                   << "expected Data to have 2, 3, 4, 8 or 16 components";
+          }
+        }
+
+        const uint32_t size_t_bit_width = GetSizeTBitWidth(_);
+        if (!size_t_bit_width) {
+          return _.diag(SPV_ERROR_INVALID_DATA)
+                 << ext_inst_name()
+                 << " can only be used with physical addressing models";
+        }
+
+        if (!_.IsIntScalarType(offset_type) ||
+            _.GetBitWidth(offset_type) != size_t_bit_width) {
+          return _.diag(SPV_ERROR_INVALID_DATA)
+                 << ext_inst_name() << ": "
+                 << "expected operand Offset to be of type size_t ("
+                 << size_t_bit_width
+                 << "-bit integer for the addressing model used in the module)";
+        }
+
+        uint32_t p_storage_class = 0;
+        uint32_t p_data_type = 0;
+        if (!_.GetPointerTypeInfo(p_type, &p_data_type, &p_storage_class)) {
+          return _.diag(SPV_ERROR_INVALID_DATA)
+                 << ext_inst_name() << ": "
+                 << "expected operand P to be a pointer";
+        }
+
+        if (p_storage_class != SpvStorageClassGeneric &&
+            p_storage_class != SpvStorageClassCrossWorkgroup &&
+            p_storage_class != SpvStorageClassWorkgroup &&
+            p_storage_class != SpvStorageClassFunction) {
+          return _.diag(SPV_ERROR_INVALID_DATA)
+                 << ext_inst_name() << ": "
+                 << "expected operand P storage class to be Generic, "
+                    "CrossWorkgroup, Workgroup or Function";
+        }
+
+        if (!_.IsFloatScalarType(p_data_type) ||
+            _.GetBitWidth(p_data_type) != 16) {
+          return _.diag(SPV_ERROR_INVALID_DATA)
+                 << ext_inst_name() << ": "
+                 << "expected operand P data type to be 16-bit float scalar";
+        }
+
+        // Rounding mode enum is checked by assembler.
+        break;
+      }
+
+      case OpenCLLIB::Shuffle:
+      case OpenCLLIB::Shuffle2: {
+        if (!_.IsFloatVectorType(result_type) &&
+            !_.IsIntVectorType(result_type)) {
+          return _.diag(SPV_ERROR_INVALID_DATA)
+                 << ext_inst_name() << ": "
+                 << "expected Result Type to be an int or float vector type";
+        }
+
+        const uint32_t result_num_components = _.GetDimension(result_type);
+        if (result_num_components != 2 && result_num_components != 4 &&
+            result_num_components != 8 && result_num_components != 16) {
+          return _.diag(SPV_ERROR_INVALID_DATA)
+                 << ext_inst_name() << ": "
+                 << "expected Result Type to have 2, 4, 8 or 16 components";
+        }
+
+        uint32_t operand_index = 4;
+        const uint32_t x_type = _.GetOperandTypeId(inst, operand_index++);
+
+        if (ext_inst_key == OpenCLLIB::Shuffle2) {
+          const uint32_t y_type = _.GetOperandTypeId(inst, operand_index++);
+          if (x_type != y_type) {
+            return _.diag(SPV_ERROR_INVALID_DATA)
+                   << ext_inst_name() << ": "
+                   << "expected operands X and Y to be of the same type";
+          }
+        }
+
+        const uint32_t shuffle_mask_type =
+            _.GetOperandTypeId(inst, operand_index++);
+
+        if (!_.IsFloatVectorType(x_type) && !_.IsIntVectorType(x_type)) {
+          return _.diag(SPV_ERROR_INVALID_DATA)
+                 << ext_inst_name() << ": "
+                 << "expected operand X to be an int or float vector";
+        }
+
+        const uint32_t x_num_components = _.GetDimension(x_type);
+        if (x_num_components != 2 && x_num_components != 4 &&
+            x_num_components != 8 && x_num_components != 16) {
+          return _.diag(SPV_ERROR_INVALID_DATA)
+                 << ext_inst_name() << ": "
+                 << "expected operand X to have 2, 4, 8 or 16 components";
+        }
+
+        const uint32_t result_component_type = _.GetComponentType(result_type);
+
+        if (result_component_type != _.GetComponentType(x_type)) {
+          return _.diag(SPV_ERROR_INVALID_DATA)
+                 << ext_inst_name() << ": "
+                 << "expected operand X and Result Type to have equal "
+                    "component types";
+        }
+
+        if (!_.IsIntVectorType(shuffle_mask_type)) {
+          return _.diag(SPV_ERROR_INVALID_DATA)
+                 << ext_inst_name() << ": "
+                 << "expected operand Shuffle Mask to be an int vector";
+        }
+
+        if (result_num_components != _.GetDimension(shuffle_mask_type)) {
+          return _.diag(SPV_ERROR_INVALID_DATA)
+                 << ext_inst_name() << ": "
+                 << "expected operand Shuffle Mask to have the same number of "
+                    "components as Result Type";
+        }
+
+        if (_.GetBitWidth(result_component_type) !=
+            _.GetBitWidth(shuffle_mask_type)) {
+          return _.diag(SPV_ERROR_INVALID_DATA)
+                 << ext_inst_name() << ": "
+                 << "expected operand Shuffle Mask components to have the same "
+                    "bit width as Result Type components";
+        }
+        break;
+      }
+
+      case OpenCLLIB::Printf: {
+        if (!_.IsIntScalarType(result_type) ||
+            _.GetBitWidth(result_type) != 32) {
+          return _.diag(SPV_ERROR_INVALID_DATA)
+                 << ext_inst_name() << ": "
+                 << "expected Result Type to be a 32-bit int type";
+        }
+
+        const uint32_t format_type = _.GetOperandTypeId(inst, 4);
+        uint32_t format_storage_class = 0;
+        uint32_t format_data_type = 0;
+        if (!_.GetPointerTypeInfo(format_type, &format_data_type,
+                                  &format_storage_class)) {
+          return _.diag(SPV_ERROR_INVALID_DATA)
+                 << ext_inst_name() << ": "
+                 << "expected operand Format to be a pointer";
+        }
+
+        if (format_storage_class != SpvStorageClassUniformConstant) {
+          return _.diag(SPV_ERROR_INVALID_DATA)
+                 << ext_inst_name() << ": "
+                 << "expected Format storage class to be UniformConstant";
+        }
+
+        if (!_.IsIntScalarType(format_data_type) ||
+            _.GetBitWidth(format_data_type) != 8) {
+          return _.diag(SPV_ERROR_INVALID_DATA)
+                 << ext_inst_name() << ": "
+                 << "expected Format data type to be 8-bit int";
+        }
+        break;
+      }
+
+      case OpenCLLIB::Prefetch: {
+        if (_.GetIdOpcode(result_type) != SpvOpTypeVoid) {
+          return _.diag(SPV_ERROR_INVALID_DATA)
+                 << ext_inst_name() << ": expected Result Type to be void";
+        }
+
+        const uint32_t p_type = _.GetOperandTypeId(inst, 4);
+        const uint32_t num_elements_type = _.GetOperandTypeId(inst, 5);
+
+        uint32_t p_storage_class = 0;
+        uint32_t p_data_type = 0;
+        if (!_.GetPointerTypeInfo(p_type, &p_data_type, &p_storage_class)) {
+          return _.diag(SPV_ERROR_INVALID_DATA)
+                 << ext_inst_name() << ": "
+                 << "expected operand Ptr to be a pointer";
+        }
+
+        if (p_storage_class != SpvStorageClassCrossWorkgroup) {
+          return _.diag(SPV_ERROR_INVALID_DATA)
+                 << ext_inst_name() << ": "
+                 << "expected operand Ptr storage class to be CrossWorkgroup";
+        }
+
+        if (!_.IsFloatScalarOrVectorType(p_data_type) &&
+            !_.IsIntScalarOrVectorType(p_data_type)) {
+          return _.diag(SPV_ERROR_INVALID_DATA)
+                 << ext_inst_name() << ": "
+                 << "expected Ptr data type to be int or float scalar or "
+                    "vector";
+        }
+
+        const uint32_t num_components = _.GetDimension(p_data_type);
+        if (num_components > 4 && num_components != 8 && num_components != 16) {
+          return _.diag(SPV_ERROR_INVALID_DATA)
+                 << ext_inst_name() << ": "
+                 << "expected Result Type to be a scalar or a vector with 2, "
+                    "3, 4, 8 or 16 components";
+        }
+
+        const uint32_t size_t_bit_width = GetSizeTBitWidth(_);
+        if (!size_t_bit_width) {
+          return _.diag(SPV_ERROR_INVALID_DATA)
+                 << ext_inst_name()
+                 << " can only be used with physical addressing models";
+        }
+
+        if (!_.IsIntScalarType(num_elements_type) ||
+            _.GetBitWidth(num_elements_type) != size_t_bit_width) {
+          return _.diag(SPV_ERROR_INVALID_DATA)
+                 << ext_inst_name() << ": "
+                 << "expected operand Num Elements to be of type size_t ("
+                 << size_t_bit_width
+                 << "-bit integer for the addressing model used in the module)";
+        }
+        break;
+      }
+    }
   }
 
   return SPV_SUCCESS;
index ed5e7bf..ee304ba 100644 (file)
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+// Tests validation rules of GLSL.450.std and OpenCL.std extended instructions.
+// Doesn't test OpenCL.std vector size 2, 3, 4, 8 or 16 rules (not supported
+// by standard SPIR-V).
+
 #include <sstream>
 #include <string>
 
@@ -35,6 +39,23 @@ using ValidateGlslStd450SinLike = spvtest::ValidateBase<std::string>;
 using ValidateGlslStd450PowLike = spvtest::ValidateBase<std::string>;
 using ValidateGlslStd450Pack = spvtest::ValidateBase<std::string>;
 using ValidateGlslStd450Unpack = spvtest::ValidateBase<std::string>;
+using ValidateOpenCLStdSqrtLike = spvtest::ValidateBase<std::string>;
+using ValidateOpenCLStdFMinLike = spvtest::ValidateBase<std::string>;
+using ValidateOpenCLStdFClampLike = spvtest::ValidateBase<std::string>;
+using ValidateOpenCLStdSAbsLike = spvtest::ValidateBase<std::string>;
+using ValidateOpenCLStdUMinLike = spvtest::ValidateBase<std::string>;
+using ValidateOpenCLStdUClampLike = spvtest::ValidateBase<std::string>;
+using ValidateOpenCLStdUMul24Like = spvtest::ValidateBase<std::string>;
+using ValidateOpenCLStdUMad24Like = spvtest::ValidateBase<std::string>;
+using ValidateOpenCLStdLengthLike = spvtest::ValidateBase<std::string>;
+using ValidateOpenCLStdDistanceLike = spvtest::ValidateBase<std::string>;
+using ValidateOpenCLStdNormalizeLike = spvtest::ValidateBase<std::string>;
+using ValidateOpenCLStdVStoreHalfLike = spvtest::ValidateBase<std::string>;
+using ValidateOpenCLStdVLoadHalfLike = spvtest::ValidateBase<std::string>;
+using ValidateOpenCLStdFractLike = spvtest::ValidateBase<std::string>;
+using ValidateOpenCLStdFrexpLike = spvtest::ValidateBase<std::string>;
+using ValidateOpenCLStdLdexpLike = spvtest::ValidateBase<std::string>;
+using ValidateOpenCLStdUpsampleLike = spvtest::ValidateBase<std::string>;
 
 // Returns number of components in Pack/Unpack extended instructions.
 // |ext_inst_name| is expected to be of the format "PackHalf2x16".
@@ -218,6 +239,203 @@ OpFunctionEnd)";
   return ss.str();
 }
 
+std::string GenerateKernelCode(
+    const std::string& body,
+    const std::string& capabilities_and_extensions = "",
+    const std::string& memory_model = "Physical32") {
+  std::ostringstream ss;
+  ss << R"(
+OpCapability Addresses
+OpCapability Kernel
+OpCapability Linkage
+OpCapability GenericPointer
+OpCapability Int8
+OpCapability Int16
+OpCapability Int64
+OpCapability Float16
+OpCapability Float64
+OpCapability Vector16
+OpCapability Matrix
+)";
+
+  ss << capabilities_and_extensions;
+  ss << "%extinst = OpExtInstImport \"OpenCL.std\"\n";
+  ss << "OpMemoryModel " << memory_model << " OpenCL\n";
+
+  ss << R"(
+%void = OpTypeVoid
+%func = OpTypeFunction %void
+%bool = OpTypeBool
+%f16 = OpTypeFloat 16
+%f32 = OpTypeFloat 32
+%f64 = OpTypeFloat 64
+%u32 = OpTypeInt 32 0
+%u64 = OpTypeInt 64 0
+%u16 = OpTypeInt 16 0
+%u8 = OpTypeInt 8 0
+%f32vec2 = OpTypeVector %f32 2
+%f32vec3 = OpTypeVector %f32 3
+%f32vec4 = OpTypeVector %f32 4
+%f32vec8 = OpTypeVector %f32 8
+%f16vec8 = OpTypeVector %f16 8
+%f32vec16 = OpTypeVector %f32 16
+%f64vec2 = OpTypeVector %f64 2
+%f64vec3 = OpTypeVector %f64 3
+%f64vec4 = OpTypeVector %f64 4
+%u32vec2 = OpTypeVector %u32 2
+%u32vec3 = OpTypeVector %u32 3
+%u32vec4 = OpTypeVector %u32 4
+%u32vec8 = OpTypeVector %u32 8
+%u64vec2 = OpTypeVector %u64 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
+%f32vec8_01010101 = OpConstantComposite %f32vec8 %f32_0 %f32_1 %f32_0 %f32_1 %f32_0 %f32_1 %f32_0 %f32_1
+
+%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
+
+%u8_0 = OpConstant %u8 0
+%u8_1 = OpConstant %u8 1
+%u8_2 = OpConstant %u8 2
+%u8_3 = OpConstant %u8 3
+
+%u16_0 = OpConstant %u16 0
+%u16_1 = OpConstant %u16 1
+%u16_2 = OpConstant %u16 2
+%u16_3 = OpConstant %u16 3
+
+%u32_0 = OpConstant %u32 0
+%u32_1 = OpConstant %u32 1
+%u32_2 = OpConstant %u32 2
+%u32_3 = OpConstant %u32 3
+%u32_256 = OpConstant %u32 256
+
+%u64_0 = OpConstant %u64 0
+%u64_1 = OpConstant %u64 1
+%u64_2 = OpConstant %u64 2
+%u64_3 = OpConstant %u64 3
+%u64_256 = OpConstant %u64 256
+
+%u32vec2_01 = OpConstantComposite %u32vec2 %u32_0 %u32_1
+%u32vec2_12 = OpConstantComposite %u32vec2 %u32_1 %u32_2
+%u32vec3_012 = OpConstantComposite %u32vec3 %u32_0 %u32_1 %u32_2
+%u32vec4_0123 = OpConstantComposite %u32vec4 %u32_0 %u32_1 %u32_2 %u32_3
+
+%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
+
+%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
+
+%f16vec8_ptr_workgroup = OpTypePointer Workgroup %f16vec8
+%f16vec8_workgroup = OpVariable %f16vec8_ptr_workgroup Workgroup
+%f16_ptr_workgroup = OpTypePointer Workgroup %f16
+
+%u32vec8_ptr_workgroup = OpTypePointer Workgroup %u32vec8
+%u32vec8_workgroup = OpVariable %u32vec8_ptr_workgroup Workgroup
+%u32_ptr_workgroup = OpTypePointer Workgroup %u32
+
+%f32vec8_ptr_workgroup = OpTypePointer Workgroup %f32vec8
+%f32vec8_workgroup = OpVariable %f32vec8_ptr_workgroup Workgroup
+%f32_ptr_workgroup = OpTypePointer Workgroup %f32
+
+%u32arr = OpTypeArray %u32 %u32_256
+%u32arr_ptr_cross_workgroup = OpTypePointer CrossWorkgroup %u32arr
+%u32arr_cross_workgroup = OpVariable %u32arr_ptr_cross_workgroup CrossWorkgroup
+%u32_ptr_cross_workgroup = OpTypePointer CrossWorkgroup %u32
+
+%f32arr = OpTypeArray %f32 %u32_256
+%f32arr_ptr_cross_workgroup = OpTypePointer CrossWorkgroup %f32arr
+%f32arr_cross_workgroup = OpVariable %f32arr_ptr_cross_workgroup CrossWorkgroup
+%f32_ptr_cross_workgroup = OpTypePointer CrossWorkgroup %f32
+
+%f32vec2arr = OpTypeArray %f32vec2 %u32_256
+%f32vec2arr_ptr_cross_workgroup = OpTypePointer CrossWorkgroup %f32vec2arr
+%f32vec2arr_cross_workgroup = OpVariable %f32vec2arr_ptr_cross_workgroup CrossWorkgroup
+%f32vec2_ptr_cross_workgroup = OpTypePointer CrossWorkgroup %f32vec2
+
+%struct_arr = OpTypeArray %struct_f32_f32 %u32_256
+%struct_arr_ptr_cross_workgroup = OpTypePointer CrossWorkgroup %struct_arr
+%struct_arr_cross_workgroup = OpVariable %struct_arr_ptr_cross_workgroup CrossWorkgroup
+%struct_ptr_cross_workgroup = OpTypePointer CrossWorkgroup %struct_f32_f32
+
+%f16vec8_ptr_uniform_constant = OpTypePointer UniformConstant %f16vec8
+%f16vec8_uniform_constant = OpVariable %f16vec8_ptr_uniform_constant UniformConstant
+%f16_ptr_uniform_constant = OpTypePointer UniformConstant %f16
+
+%u32vec8_ptr_uniform_constant = OpTypePointer UniformConstant %u32vec8
+%u32vec8_uniform_constant = OpVariable %u32vec8_ptr_uniform_constant UniformConstant
+%u32_ptr_uniform_constant = OpTypePointer UniformConstant %u32
+
+%f32vec8_ptr_uniform_constant = OpTypePointer UniformConstant %f32vec8
+%f32vec8_uniform_constant = OpVariable %f32vec8_ptr_uniform_constant UniformConstant
+%f32_ptr_uniform_constant = OpTypePointer UniformConstant %f32
+
+%f16vec8_ptr_input = OpTypePointer Input %f16vec8
+%f16vec8_input = OpVariable %f16vec8_ptr_input Input
+%f16_ptr_input = OpTypePointer Input %f16
+
+%f32_ptr_generic = OpTypePointer Generic %f32
+%u32_ptr_generic = OpTypePointer Generic %u32
+
+%f32_ptr_function = OpTypePointer Function %f32
+%f32vec2_ptr_function = OpTypePointer Function %f32vec2
+%u32_ptr_function = OpTypePointer Function %u32
+%u64_ptr_function = OpTypePointer Function %u64
+%u32vec2_ptr_function = OpTypePointer Function %u32vec2
+
+%u8arr = OpTypeArray %u8 %u32_256
+%u8arr_ptr_uniform_constant = OpTypePointer UniformConstant %u8arr
+%u8arr_uniform_constant = OpVariable %u8arr_ptr_uniform_constant UniformConstant
+%u8_ptr_uniform_constant = OpTypePointer UniformConstant %u8
+%u8_ptr_generic = OpTypePointer Generic %u8
+
+%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;
@@ -2515,4 +2733,3029 @@ TEST_F(ValidateExtInst, GlslStd450InterpolateAtOffsetWrongExecutionModel) {
                         "Fragment execution model"));
 }
 
+TEST_P(ValidateOpenCLStdSqrtLike, 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 %f32vec4 %extinst " << ext_inst_name
+     << " %f32vec4_0123\n";
+  ss << "%val4 = OpExtInst %f64 %extinst " << ext_inst_name << " %f64_0\n";
+  CompileSuccessfully(GenerateKernelCode(ss.str()));
+  ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+TEST_P(ValidateOpenCLStdSqrtLike, IntResultType) {
+  const std::string ext_inst_name = GetParam();
+  const std::string body =
+      "%val1 = OpExtInst %u32 %extinst " + ext_inst_name + " %f32_0\n";
+
+  CompileSuccessfully(GenerateKernelCode(body));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("OpenCL.std " + ext_inst_name +
+                        ": expected Result Type to be a float scalar "
+                        "or vector type"));
+}
+
+TEST_P(ValidateOpenCLStdSqrtLike, IntOperand) {
+  const std::string ext_inst_name = GetParam();
+  const std::string body =
+      "%val1 = OpExtInst %f32 %extinst " + ext_inst_name + " %u32_0\n";
+
+  CompileSuccessfully(GenerateKernelCode(body));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("OpenCL.std " + ext_inst_name +
+                        ": expected types of all operands to be equal to "
+                        "Result Type"));
+}
+
+INSTANTIATE_TEST_CASE_P(
+    AllSqrtLike, ValidateOpenCLStdSqrtLike,
+    ::testing::ValuesIn(std::vector<std::string>{
+        "acos",         "acosh",       "acospi",       "asin",
+        "asinh",        "asinpi",      "atan",         "atanh",
+        "atanpi",       "cbrt",        "ceil",         "cos",
+        "cosh",         "cospi",       "erfc",         "erf",
+        "exp",          "exp2",        "exp10",        "expm1",
+        "fabs",         "floor",       "log",          "log2",
+        "log10",        "log1p",       "logb",         "rint",
+        "round",        "rsqrt",       "sin",          "sinh",
+        "sinpi",        "sqrt",        "tan",          "tanh",
+        "tanpi",        "tgamma",      "trunc",        "half_cos",
+        "half_exp",     "half_exp2",   "half_exp10",   "half_log",
+        "half_log2",    "half_log10",  "half_recip",   "half_rsqrt",
+        "half_sin",     "half_sqrt",   "half_tan",     "lgamma",
+        "native_cos",   "native_exp",  "native_exp2",  "native_exp10",
+        "native_log",   "native_log2", "native_log10", "native_recip",
+        "native_rsqrt", "native_sin",  "native_sqrt",  "native_tan",
+        "degrees",      "radians",     "sign",
+    }), );
+
+TEST_P(ValidateOpenCLStdFMinLike, 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(GenerateKernelCode(ss.str()));
+  ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+TEST_P(ValidateOpenCLStdFMinLike, 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(GenerateKernelCode(body));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("OpenCL.std " + ext_inst_name +
+                        ": expected Result Type to be a float scalar "
+                        "or vector type"));
+}
+
+TEST_P(ValidateOpenCLStdFMinLike, 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(GenerateKernelCode(body));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("OpenCL.std " + ext_inst_name +
+                        ": expected types of all operands to be equal to "
+                        "Result Type"));
+}
+
+TEST_P(ValidateOpenCLStdFMinLike, 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(GenerateKernelCode(body));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("OpenCL.std " + ext_inst_name +
+                        ": expected types of all operands to be equal to "
+                        "Result Type"));
+}
+
+INSTANTIATE_TEST_CASE_P(AllFMinLike, ValidateOpenCLStdFMinLike,
+                        ::testing::ValuesIn(std::vector<std::string>{
+                            "atan2",     "atan2pi",       "copysign",
+                            "fdim",      "fmax",          "fmin",
+                            "fmod",      "maxmag",        "minmag",
+                            "hypot",     "nextafter",     "pow",
+                            "powr",      "remainder",     "half_divide",
+                            "half_powr", "native_divide", "native_powr",
+                            "step",      "fmax_common",   "fmin_common",
+                        }), );
+
+TEST_P(ValidateOpenCLStdFClampLike, 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(GenerateKernelCode(ss.str()));
+  ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+TEST_P(ValidateOpenCLStdFClampLike, 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(GenerateKernelCode(body));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("OpenCL.std " + ext_inst_name +
+                        ": expected Result Type to be a float scalar "
+                        "or vector type"));
+}
+
+TEST_P(ValidateOpenCLStdFClampLike, 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(GenerateKernelCode(body));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("OpenCL.std " + ext_inst_name +
+                        ": expected types of all operands to be equal to "
+                        "Result Type"));
+}
+
+TEST_P(ValidateOpenCLStdFClampLike, 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(GenerateKernelCode(body));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("OpenCL.std " + ext_inst_name +
+                        ": expected types of all operands to be equal to "
+                        "Result Type"));
+}
+
+TEST_P(ValidateOpenCLStdFClampLike, 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(GenerateKernelCode(body));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("OpenCL.std " + ext_inst_name +
+                        ": expected types of all operands to be equal to "
+                        "Result Type"));
+}
+
+INSTANTIATE_TEST_CASE_P(AllFClampLike, ValidateOpenCLStdFClampLike,
+                        ::testing::ValuesIn(std::vector<std::string>{
+                            "fma",
+                            "mad",
+                            "fclamp",
+                            "mix",
+                            "smoothstep",
+                        }), );
+
+TEST_P(ValidateOpenCLStdSAbsLike, Success) {
+  const std::string ext_inst_name = GetParam();
+  std::ostringstream ss;
+  ss << "%val1 = OpExtInst %u32 %extinst " << ext_inst_name << " %u32_1\n";
+  ss << "%val2 = OpExtInst %u32 %extinst " << ext_inst_name << " %u32_1\n";
+  ss << "%val3 = OpExtInst %u32 %extinst " << ext_inst_name << " %u32_1\n";
+  ss << "%val4 = OpExtInst %u32 %extinst " << ext_inst_name << " %u32_1\n";
+  ss << "%val5 = OpExtInst %u32vec2 %extinst " << ext_inst_name
+     << " %u32vec2_01\n";
+  ss << "%val6 = OpExtInst %u32vec2 %extinst " << ext_inst_name
+     << " %u32vec2_01\n";
+  ss << "%val7 = OpExtInst %u32vec2 %extinst " << ext_inst_name
+     << " %u32vec2_01\n";
+  ss << "%val8 = OpExtInst %u32vec2 %extinst " << ext_inst_name
+     << " %u32vec2_01\n";
+  CompileSuccessfully(GenerateKernelCode(ss.str()));
+  ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+TEST_P(ValidateOpenCLStdSAbsLike, FloatResultType) {
+  const std::string ext_inst_name = GetParam();
+  const std::string body =
+      "%val1 = OpExtInst %f32 %extinst " + ext_inst_name + " %u32_0\n";
+
+  CompileSuccessfully(GenerateKernelCode(body));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("OpenCL.std " + ext_inst_name +
+                        ": expected Result Type to be an int scalar "
+                        "or vector type"));
+}
+
+TEST_P(ValidateOpenCLStdSAbsLike, FloatOperand) {
+  const std::string ext_inst_name = GetParam();
+  const std::string body =
+      "%val1 = OpExtInst %u32 %extinst " + ext_inst_name + " %f32_0\n";
+
+  CompileSuccessfully(GenerateKernelCode(body));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("OpenCL.std " + ext_inst_name +
+                ": expected types of all operands to be equal to Result Type"));
+}
+
+TEST_P(ValidateOpenCLStdSAbsLike, U64Operand) {
+  const std::string ext_inst_name = GetParam();
+  const std::string body =
+      "%val1 = OpExtInst %u32 %extinst " + ext_inst_name + " %u64_0\n";
+
+  CompileSuccessfully(GenerateKernelCode(body));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("OpenCL.std " + ext_inst_name +
+                ": expected types of all operands to be equal to Result Type"));
+}
+
+INSTANTIATE_TEST_CASE_P(AllSAbsLike, ValidateOpenCLStdSAbsLike,
+                        ::testing::ValuesIn(std::vector<std::string>{
+                            "s_abs",
+                            "clz",
+                            "ctz",
+                            "popcount",
+                            "u_abs",
+                        }), );
+
+TEST_P(ValidateOpenCLStdUMinLike, Success) {
+  const std::string ext_inst_name = GetParam();
+  std::ostringstream ss;
+  ss << "%val1 = OpExtInst %u32 %extinst " << ext_inst_name
+     << " %u32_1 %u32_2\n";
+  ss << "%val2 = OpExtInst %u32 %extinst " << ext_inst_name
+     << " %u32_1 %u32_2\n";
+  ss << "%val3 = OpExtInst %u32 %extinst " << ext_inst_name
+     << " %u32_1 %u32_2\n";
+  ss << "%val4 = OpExtInst %u32 %extinst " << ext_inst_name
+     << " %u32_1 %u32_2\n";
+  ss << "%val5 = OpExtInst %u32vec2 %extinst " << ext_inst_name
+     << " %u32vec2_01 %u32vec2_01\n";
+  ss << "%val6 = OpExtInst %u32vec2 %extinst " << ext_inst_name
+     << " %u32vec2_01 %u32vec2_01\n";
+  ss << "%val7 = OpExtInst %u32vec2 %extinst " << ext_inst_name
+     << " %u32vec2_01 %u32vec2_01\n";
+  ss << "%val8 = OpExtInst %u32vec2 %extinst " << ext_inst_name
+     << " %u32vec2_01 %u32vec2_01\n";
+  ss << "%val9 = OpExtInst %u64 %extinst " << ext_inst_name
+     << " %u64_1 %u64_0\n";
+  CompileSuccessfully(GenerateKernelCode(ss.str()));
+  ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+TEST_P(ValidateOpenCLStdUMinLike, 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(GenerateKernelCode(body));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("OpenCL.std " + ext_inst_name +
+                        ": expected Result Type to be an int scalar "
+                        "or vector type"));
+}
+
+TEST_P(ValidateOpenCLStdUMinLike, FloatOperand1) {
+  const std::string ext_inst_name = GetParam();
+  const std::string body =
+      "%val1 = OpExtInst %u32 %extinst " + ext_inst_name + " %f32_0 %u32_0\n";
+
+  CompileSuccessfully(GenerateKernelCode(body));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("OpenCL.std " + ext_inst_name +
+                ": expected types of all operands to be equal to Result Type"));
+}
+
+TEST_P(ValidateOpenCLStdUMinLike, FloatOperand2) {
+  const std::string ext_inst_name = GetParam();
+  const std::string body =
+      "%val1 = OpExtInst %u32 %extinst " + ext_inst_name + " %u32_0 %f32_0\n";
+
+  CompileSuccessfully(GenerateKernelCode(body));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("OpenCL.std " + ext_inst_name +
+                ": expected types of all operands to be equal to Result Type"));
+}
+
+TEST_P(ValidateOpenCLStdUMinLike, U64Operand1) {
+  const std::string ext_inst_name = GetParam();
+  const std::string body =
+      "%val1 = OpExtInst %u32 %extinst " + ext_inst_name + " %u64_0 %u32_0\n";
+
+  CompileSuccessfully(GenerateKernelCode(body));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("OpenCL.std " + ext_inst_name +
+                ": expected types of all operands to be equal to Result Type"));
+}
+
+TEST_P(ValidateOpenCLStdUMinLike, U64Operand2) {
+  const std::string ext_inst_name = GetParam();
+  const std::string body =
+      "%val1 = OpExtInst %u32 %extinst " + ext_inst_name + " %u32_0 %u64_0\n";
+
+  CompileSuccessfully(GenerateKernelCode(body));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("OpenCL.std " + ext_inst_name +
+                ": expected types of all operands to be equal to Result Type"));
+}
+
+INSTANTIATE_TEST_CASE_P(AllUMinLike, ValidateOpenCLStdUMinLike,
+                        ::testing::ValuesIn(std::vector<std::string>{
+                            "s_max",
+                            "u_max",
+                            "s_min",
+                            "u_min",
+                            "s_abs_diff",
+                            "s_add_sat",
+                            "u_add_sat",
+                            "s_mul_hi",
+                            "rotate",
+                            "s_sub_sat",
+                            "u_sub_sat",
+                            "s_hadd",
+                            "u_hadd",
+                            "s_rhadd",
+                            "u_rhadd",
+                            "u_abs_diff",
+                            "u_mul_hi",
+                        }), );
+
+TEST_P(ValidateOpenCLStdUClampLike, Success) {
+  const std::string ext_inst_name = GetParam();
+  std::ostringstream ss;
+  ss << "%val1 = OpExtInst %u32 %extinst " << ext_inst_name
+     << " %u32_0 %u32_1 %u32_2\n";
+  ss << "%val2 = OpExtInst %u32 %extinst " << ext_inst_name
+     << " %u32_0 %u32_1 %u32_2\n";
+  ss << "%val3 = OpExtInst %u32 %extinst " << ext_inst_name
+     << " %u32_0 %u32_1 %u32_2\n";
+  ss << "%val4 = OpExtInst %u32 %extinst " << ext_inst_name
+     << " %u32_0 %u32_1 %u32_2\n";
+  ss << "%val5 = OpExtInst %u32vec2 %extinst " << ext_inst_name
+     << " %u32vec2_01 %u32vec2_01 %u32vec2_12\n";
+  ss << "%val6 = OpExtInst %u32vec2 %extinst " << ext_inst_name
+     << " %u32vec2_01 %u32vec2_01 %u32vec2_12\n";
+  ss << "%val7 = OpExtInst %u32vec2 %extinst " << ext_inst_name
+     << " %u32vec2_01 %u32vec2_01 %u32vec2_12\n";
+  ss << "%val8 = OpExtInst %u32vec2 %extinst " << ext_inst_name
+     << " %u32vec2_01 %u32vec2_01 %u32vec2_12\n";
+  ss << "%val9 = OpExtInst %u64 %extinst " << ext_inst_name
+     << " %u64_1 %u64_0 %u64_1\n";
+  CompileSuccessfully(GenerateKernelCode(ss.str()));
+  ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+TEST_P(ValidateOpenCLStdUClampLike, 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(GenerateKernelCode(body));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("OpenCL.std " + ext_inst_name +
+                        ": expected Result Type to be an int scalar "
+                        "or vector type"));
+}
+
+TEST_P(ValidateOpenCLStdUClampLike, FloatOperand1) {
+  const std::string ext_inst_name = GetParam();
+  const std::string body = "%val1 = OpExtInst %u32 %extinst " + ext_inst_name +
+                           " %f32_0 %u32_0 %u32_1\n";
+
+  CompileSuccessfully(GenerateKernelCode(body));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("OpenCL.std " + ext_inst_name +
+                ": expected types of all operands to be equal to Result Type"));
+}
+
+TEST_P(ValidateOpenCLStdUClampLike, FloatOperand2) {
+  const std::string ext_inst_name = GetParam();
+  const std::string body = "%val1 = OpExtInst %u32 %extinst " + ext_inst_name +
+                           " %u32_0 %f32_0 %u32_1\n";
+
+  CompileSuccessfully(GenerateKernelCode(body));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("OpenCL.std " + ext_inst_name +
+                ": expected types of all operands to be equal to Result Type"));
+}
+
+TEST_P(ValidateOpenCLStdUClampLike, FloatOperand3) {
+  const std::string ext_inst_name = GetParam();
+  const std::string body = "%val1 = OpExtInst %u32 %extinst " + ext_inst_name +
+                           " %u32_0 %u32_0 %f32_1\n";
+
+  CompileSuccessfully(GenerateKernelCode(body));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("OpenCL.std " + ext_inst_name +
+                ": expected types of all operands to be equal to Result Type"));
+}
+
+TEST_P(ValidateOpenCLStdUClampLike, U64Operand1) {
+  const std::string ext_inst_name = GetParam();
+  const std::string body = "%val1 = OpExtInst %u32 %extinst " + ext_inst_name +
+                           " %f32_0 %u32_0 %u64_1\n";
+
+  CompileSuccessfully(GenerateKernelCode(body));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("OpenCL.std " + ext_inst_name +
+                ": expected types of all operands to be equal to Result Type"));
+}
+
+TEST_P(ValidateOpenCLStdUClampLike, U64Operand2) {
+  const std::string ext_inst_name = GetParam();
+  const std::string body = "%val1 = OpExtInst %u32 %extinst " + ext_inst_name +
+                           " %u32_0 %f32_0 %u64_1\n";
+
+  CompileSuccessfully(GenerateKernelCode(body));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("OpenCL.std " + ext_inst_name +
+                ": expected types of all operands to be equal to Result Type"));
+}
+
+TEST_P(ValidateOpenCLStdUClampLike, U64Operand3) {
+  const std::string ext_inst_name = GetParam();
+  const std::string body = "%val1 = OpExtInst %u32 %extinst " + ext_inst_name +
+                           " %u32_0 %u32_0 %u64_1\n";
+
+  CompileSuccessfully(GenerateKernelCode(body));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("OpenCL.std " + ext_inst_name +
+                ": expected types of all operands to be equal to Result Type"));
+}
+
+INSTANTIATE_TEST_CASE_P(AllUClampLike, ValidateOpenCLStdUClampLike,
+                        ::testing::ValuesIn(std::vector<std::string>{
+                            "s_clamp",
+                            "u_clamp",
+                            "s_mad_hi",
+                            "u_mad_sat",
+                            "s_mad_sat",
+                            "u_mad_hi",
+                        }), );
+
+// -------------------------------------------------------------
+TEST_P(ValidateOpenCLStdUMul24Like, Success) {
+  const std::string ext_inst_name = GetParam();
+  std::ostringstream ss;
+  ss << "%val1 = OpExtInst %u32 %extinst " << ext_inst_name
+     << " %u32_1 %u32_2\n";
+  ss << "%val2 = OpExtInst %u32 %extinst " << ext_inst_name
+     << " %u32_1 %u32_2\n";
+  ss << "%val3 = OpExtInst %u32 %extinst " << ext_inst_name
+     << " %u32_1 %u32_2\n";
+  ss << "%val4 = OpExtInst %u32 %extinst " << ext_inst_name
+     << " %u32_1 %u32_2\n";
+  ss << "%val5 = OpExtInst %u32vec2 %extinst " << ext_inst_name
+     << " %u32vec2_01 %u32vec2_01\n";
+  ss << "%val6 = OpExtInst %u32vec2 %extinst " << ext_inst_name
+     << " %u32vec2_01 %u32vec2_01\n";
+  ss << "%val7 = OpExtInst %u32vec2 %extinst " << ext_inst_name
+     << " %u32vec2_01 %u32vec2_01\n";
+  ss << "%val8 = OpExtInst %u32vec2 %extinst " << ext_inst_name
+     << " %u32vec2_01 %u32vec2_01\n";
+  CompileSuccessfully(GenerateKernelCode(ss.str()));
+  ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+TEST_P(ValidateOpenCLStdUMul24Like, 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(GenerateKernelCode(body));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr(
+          "OpenCL.std " + ext_inst_name +
+          ": expected Result Type to be a 32-bit int scalar or vector type"));
+}
+
+TEST_P(ValidateOpenCLStdUMul24Like, U64ResultType) {
+  const std::string ext_inst_name = GetParam();
+  const std::string body =
+      "%val1 = OpExtInst %u64 %extinst " + ext_inst_name + " %u64_0 %u64\n";
+
+  CompileSuccessfully(GenerateKernelCode(body));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr(
+          "OpenCL.std " + ext_inst_name +
+          ": expected Result Type to be a 32-bit int scalar or vector type"));
+}
+
+TEST_P(ValidateOpenCLStdUMul24Like, FloatOperand1) {
+  const std::string ext_inst_name = GetParam();
+  const std::string body =
+      "%val1 = OpExtInst %u32 %extinst " + ext_inst_name + " %f32_0 %u32_0\n";
+
+  CompileSuccessfully(GenerateKernelCode(body));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("OpenCL.std " + ext_inst_name +
+                ": expected types of all operands to be equal to Result Type"));
+}
+
+TEST_P(ValidateOpenCLStdUMul24Like, FloatOperand2) {
+  const std::string ext_inst_name = GetParam();
+  const std::string body =
+      "%val1 = OpExtInst %u32 %extinst " + ext_inst_name + " %u32_0 %f32_0\n";
+
+  CompileSuccessfully(GenerateKernelCode(body));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("OpenCL.std " + ext_inst_name +
+                ": expected types of all operands to be equal to Result Type"));
+}
+
+TEST_P(ValidateOpenCLStdUMul24Like, U64Operand1) {
+  const std::string ext_inst_name = GetParam();
+  const std::string body =
+      "%val1 = OpExtInst %u32 %extinst " + ext_inst_name + " %u64_0 %u32_0\n";
+
+  CompileSuccessfully(GenerateKernelCode(body));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("OpenCL.std " + ext_inst_name +
+                ": expected types of all operands to be equal to Result Type"));
+}
+
+TEST_P(ValidateOpenCLStdUMul24Like, U64Operand2) {
+  const std::string ext_inst_name = GetParam();
+  const std::string body =
+      "%val1 = OpExtInst %u32 %extinst " + ext_inst_name + " %u32_0 %u64_0\n";
+
+  CompileSuccessfully(GenerateKernelCode(body));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("OpenCL.std " + ext_inst_name +
+                ": expected types of all operands to be equal to Result Type"));
+}
+
+INSTANTIATE_TEST_CASE_P(AllUMul24Like, ValidateOpenCLStdUMul24Like,
+                        ::testing::ValuesIn(std::vector<std::string>{
+                            "s_mul24",
+                            "u_mul24",
+                        }), );
+
+TEST_P(ValidateOpenCLStdUMad24Like, Success) {
+  const std::string ext_inst_name = GetParam();
+  std::ostringstream ss;
+  ss << "%val1 = OpExtInst %u32 %extinst " << ext_inst_name
+     << " %u32_0 %u32_1 %u32_2\n";
+  ss << "%val2 = OpExtInst %u32 %extinst " << ext_inst_name
+     << " %u32_0 %u32_1 %u32_2\n";
+  ss << "%val3 = OpExtInst %u32 %extinst " << ext_inst_name
+     << " %u32_0 %u32_1 %u32_2\n";
+  ss << "%val4 = OpExtInst %u32 %extinst " << ext_inst_name
+     << " %u32_0 %u32_1 %u32_2\n";
+  ss << "%val5 = OpExtInst %u32vec2 %extinst " << ext_inst_name
+     << " %u32vec2_01 %u32vec2_01 %u32vec2_12\n";
+  ss << "%val6 = OpExtInst %u32vec2 %extinst " << ext_inst_name
+     << " %u32vec2_01 %u32vec2_01 %u32vec2_12\n";
+  ss << "%val7 = OpExtInst %u32vec2 %extinst " << ext_inst_name
+     << " %u32vec2_01 %u32vec2_01 %u32vec2_12\n";
+  ss << "%val8 = OpExtInst %u32vec2 %extinst " << ext_inst_name
+     << " %u32vec2_01 %u32vec2_01 %u32vec2_12\n";
+  CompileSuccessfully(GenerateKernelCode(ss.str()));
+  ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+TEST_P(ValidateOpenCLStdUMad24Like, 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(GenerateKernelCode(body));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr(
+          "OpenCL.std " + ext_inst_name +
+          ": expected Result Type to be a 32-bit int scalar or vector type"));
+}
+
+TEST_P(ValidateOpenCLStdUMad24Like, U64ResultType) {
+  const std::string ext_inst_name = GetParam();
+  const std::string body = "%val1 = OpExtInst %u64 %extinst " + ext_inst_name +
+                           " %u64_0 %u64_0 %u64_1\n";
+
+  CompileSuccessfully(GenerateKernelCode(body));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr(
+          "OpenCL.std " + ext_inst_name +
+          ": expected Result Type to be a 32-bit int scalar or vector type"));
+}
+
+TEST_P(ValidateOpenCLStdUMad24Like, FloatOperand1) {
+  const std::string ext_inst_name = GetParam();
+  const std::string body = "%val1 = OpExtInst %u32 %extinst " + ext_inst_name +
+                           " %f32_0 %u32_0 %u32_1\n";
+
+  CompileSuccessfully(GenerateKernelCode(body));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("OpenCL.std " + ext_inst_name +
+                ": expected types of all operands to be equal to Result Type"));
+}
+
+TEST_P(ValidateOpenCLStdUMad24Like, FloatOperand2) {
+  const std::string ext_inst_name = GetParam();
+  const std::string body = "%val1 = OpExtInst %u32 %extinst " + ext_inst_name +
+                           " %u32_0 %f32_0 %u32_1\n";
+
+  CompileSuccessfully(GenerateKernelCode(body));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("OpenCL.std " + ext_inst_name +
+                ": expected types of all operands to be equal to Result Type"));
+}
+
+TEST_P(ValidateOpenCLStdUMad24Like, FloatOperand3) {
+  const std::string ext_inst_name = GetParam();
+  const std::string body = "%val1 = OpExtInst %u32 %extinst " + ext_inst_name +
+                           " %u32_0 %u32_0 %f32_1\n";
+
+  CompileSuccessfully(GenerateKernelCode(body));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("OpenCL.std " + ext_inst_name +
+                ": expected types of all operands to be equal to Result Type"));
+}
+
+TEST_P(ValidateOpenCLStdUMad24Like, U64Operand1) {
+  const std::string ext_inst_name = GetParam();
+  const std::string body = "%val1 = OpExtInst %u32 %extinst " + ext_inst_name +
+                           " %f32_0 %u32_0 %u64_1\n";
+
+  CompileSuccessfully(GenerateKernelCode(body));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("OpenCL.std " + ext_inst_name +
+                ": expected types of all operands to be equal to Result Type"));
+}
+
+TEST_P(ValidateOpenCLStdUMad24Like, U64Operand2) {
+  const std::string ext_inst_name = GetParam();
+  const std::string body = "%val1 = OpExtInst %u32 %extinst " + ext_inst_name +
+                           " %u32_0 %f32_0 %u64_1\n";
+
+  CompileSuccessfully(GenerateKernelCode(body));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("OpenCL.std " + ext_inst_name +
+                ": expected types of all operands to be equal to Result Type"));
+}
+
+TEST_P(ValidateOpenCLStdUMad24Like, U64Operand3) {
+  const std::string ext_inst_name = GetParam();
+  const std::string body = "%val1 = OpExtInst %u32 %extinst " + ext_inst_name +
+                           " %u32_0 %u32_0 %u64_1\n";
+
+  CompileSuccessfully(GenerateKernelCode(body));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("OpenCL.std " + ext_inst_name +
+                ": expected types of all operands to be equal to Result Type"));
+}
+
+INSTANTIATE_TEST_CASE_P(AllUMad24Like, ValidateOpenCLStdUMad24Like,
+                        ::testing::ValuesIn(std::vector<std::string>{
+                            "s_mad24",
+                            "u_mad24",
+                        }), );
+
+TEST_F(ValidateExtInst, OpenCLStdCrossSuccess) {
+  const std::string body = R"(
+%val1 = OpExtInst %f32vec3 %extinst cross %f32vec3_012 %f32vec3_123
+%val2 = OpExtInst %f32vec4 %extinst cross %f32vec4_0123 %f32vec4_0123
+)";
+
+  CompileSuccessfully(GenerateKernelCode(body));
+  ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+TEST_F(ValidateExtInst, OpenCLStdCrossIntVectorResultType) {
+  const std::string body = R"(
+%val1 = OpExtInst %u32vec3 %extinst cross %f32vec3_012 %f32vec3_123
+)";
+
+  CompileSuccessfully(GenerateKernelCode(body));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("OpenCL.std cross: "
+                        "expected Result Type to be a float vector type"));
+}
+
+TEST_F(ValidateExtInst, OpenCLStdCrossResultTypeWrongSize) {
+  const std::string body = R"(
+%val1 = OpExtInst %f32vec2 %extinst cross %f32vec3_012 %f32vec3_123
+)";
+
+  CompileSuccessfully(GenerateKernelCode(body));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("OpenCL.std cross: "
+                        "expected Result Type to have 3 or 4 components"));
+}
+
+TEST_F(ValidateExtInst, OpenCLStdCrossXWrongType) {
+  const std::string body = R"(
+%val1 = OpExtInst %f32vec3 %extinst cross %f64vec3_012 %f32vec3_123
+)";
+
+  CompileSuccessfully(GenerateKernelCode(body));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("OpenCL.std cross: "
+                        "expected operand X type to be equal to Result Type"));
+}
+
+TEST_F(ValidateExtInst, OpenCLStdCrossYWrongType) {
+  const std::string body = R"(
+%val1 = OpExtInst %f32vec3 %extinst cross %f32vec3_123 %f64vec3_012
+)";
+
+  CompileSuccessfully(GenerateKernelCode(body));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("OpenCL.std cross: "
+                        "expected operand Y type to be equal to Result Type"));
+}
+
+TEST_P(ValidateOpenCLStdLengthLike, Success) {
+  const std::string ext_inst_name = GetParam();
+  std::ostringstream ss;
+  ss << "%val1 = OpExtInst %f32 %extinst " << ext_inst_name << " %f32vec2_01\n";
+  ss << "%val2 = OpExtInst %f32 %extinst " << ext_inst_name
+     << " %f32vec4_0123\n";
+
+  CompileSuccessfully(GenerateKernelCode(ss.str()));
+  ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+TEST_P(ValidateOpenCLStdLengthLike, IntResultType) {
+  const std::string ext_inst_name = GetParam();
+  const std::string body =
+      "%val1 = OpExtInst %u32 %extinst " + ext_inst_name + " %f32vec2_01\n";
+
+  CompileSuccessfully(GenerateKernelCode(body));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("OpenCL.std " + ext_inst_name +
+                        ": "
+                        "expected Result Type to be a float scalar type"));
+}
+
+TEST_P(ValidateOpenCLStdLengthLike, IntX) {
+  const std::string ext_inst_name = GetParam();
+  const std::string body =
+      "%val1 = OpExtInst %f32 %extinst " + ext_inst_name + " %u32vec2_01\n";
+
+  CompileSuccessfully(GenerateKernelCode(body));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("OpenCL.std " + ext_inst_name +
+                        ": "
+                        "expected operand P to be a float scalar or vector"));
+}
+
+TEST_P(ValidateOpenCLStdLengthLike, VectorTooBig) {
+  const std::string ext_inst_name = GetParam();
+  const std::string body = "%val1 = OpExtInst %f32 %extinst " + ext_inst_name +
+                           " %f32vec8_01010101\n";
+
+  CompileSuccessfully(GenerateKernelCode(body));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("OpenCL.std " + ext_inst_name +
+                ": "
+                "expected operand P to have no more than 4 components"));
+}
+
+TEST_P(ValidateOpenCLStdLengthLike, DifferentType) {
+  const std::string ext_inst_name = GetParam();
+  const std::string body =
+      "%val1 = OpExtInst %f64 %extinst " + ext_inst_name + " %f32vec2_01\n";
+
+  CompileSuccessfully(GenerateKernelCode(body));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("OpenCL.std " + ext_inst_name +
+                        ": "
+                        "expected operand P component type to be equal to "
+                        "Result Type"));
+}
+
+INSTANTIATE_TEST_CASE_P(AllLengthLike, ValidateOpenCLStdLengthLike,
+                        ::testing::ValuesIn(std::vector<std::string>{
+                            "length",
+                            "fast_length",
+                        }), );
+
+TEST_P(ValidateOpenCLStdDistanceLike, Success) {
+  const std::string ext_inst_name = GetParam();
+  std::ostringstream ss;
+  ss << "%val1 = OpExtInst %f32 %extinst " << ext_inst_name
+     << " %f32vec2_01 %f32vec2_01\n";
+  ss << "%val2 = OpExtInst %f32 %extinst " << ext_inst_name
+     << " %f32vec4_0123 %f32vec4_1234\n";
+  ss << "%val3 = OpExtInst %f32 %extinst " << ext_inst_name
+     << " %f32_0 %f32_1\n";
+
+  CompileSuccessfully(GenerateKernelCode(ss.str()));
+  ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+TEST_P(ValidateOpenCLStdDistanceLike, IntResultType) {
+  const std::string ext_inst_name = GetParam();
+  const std::string body = "%val1 = OpExtInst %u32 %extinst " + ext_inst_name +
+                           " %f32vec2_01 %f32vec2_12\n";
+
+  CompileSuccessfully(GenerateKernelCode(body));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("OpenCL.std " + ext_inst_name +
+                        ": "
+                        "expected Result Type to be a float scalar type"));
+}
+
+TEST_P(ValidateOpenCLStdDistanceLike, IntP0) {
+  const std::string ext_inst_name = GetParam();
+  const std::string body = "%val1 = OpExtInst %f32 %extinst " + ext_inst_name +
+                           " %u32vec2_01 %f32vec2_12\n";
+
+  CompileSuccessfully(GenerateKernelCode(body));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("OpenCL.std " + ext_inst_name +
+                ": "
+                "expected operand P0 to be of float scalar or vector type"));
+}
+
+TEST_P(ValidateOpenCLStdDistanceLike, VectorTooBig) {
+  const std::string ext_inst_name = GetParam();
+  const std::string body = "%val1 = OpExtInst %f32 %extinst " + ext_inst_name +
+                           " %f32vec8_01010101 %f32vec8_01010101\n";
+
+  CompileSuccessfully(GenerateKernelCode(body));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("OpenCL.std " + ext_inst_name +
+                ": "
+                "expected operand P0 to have no more than 4 components"));
+}
+
+TEST_P(ValidateOpenCLStdDistanceLike, F64P0) {
+  const std::string ext_inst_name = GetParam();
+  const std::string body = "%val1 = OpExtInst %f32 %extinst " + ext_inst_name +
+                           " %f64vec2_01 %f32vec2_12\n";
+
+  CompileSuccessfully(GenerateKernelCode(body));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr(
+          "OpenCL.std " + ext_inst_name +
+          ": "
+          "expected operand P0 component type to be equal to Result Type"));
+}
+
+TEST_P(ValidateOpenCLStdDistanceLike, DifferentOperands) {
+  const std::string ext_inst_name = GetParam();
+  const std::string body = "%val1 = OpExtInst %f64 %extinst " + ext_inst_name +
+                           " %f64vec2_01 %f32vec2_12\n";
+
+  CompileSuccessfully(GenerateKernelCode(body));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("OpenCL.std " + ext_inst_name +
+                        ": "
+                        "expected operands P0 and P1 to be of the same type"));
+}
+
+INSTANTIATE_TEST_CASE_P(AllDistanceLike, ValidateOpenCLStdDistanceLike,
+                        ::testing::ValuesIn(std::vector<std::string>{
+                            "distance",
+                            "fast_distance",
+                        }), );
+
+TEST_P(ValidateOpenCLStdNormalizeLike, Success) {
+  const std::string ext_inst_name = GetParam();
+  std::ostringstream ss;
+  ss << "%val1 = OpExtInst %f32vec2 %extinst " << ext_inst_name
+     << " %f32vec2_01\n";
+  ss << "%val2 = OpExtInst %f32vec4 %extinst " << ext_inst_name
+     << " %f32vec4_0123\n";
+  ss << "%val3 = OpExtInst %f32 %extinst " << ext_inst_name << " %f32_2\n";
+
+  CompileSuccessfully(GenerateKernelCode(ss.str()));
+  ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+TEST_P(ValidateOpenCLStdNormalizeLike, IntResultType) {
+  const std::string ext_inst_name = GetParam();
+  const std::string body =
+      "%val1 = OpExtInst %u32 %extinst " + ext_inst_name + " %f32_2\n";
+
+  CompileSuccessfully(GenerateKernelCode(body));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("OpenCL.std " + ext_inst_name +
+                ": "
+                "expected Result Type to be a float scalar or vector type"));
+}
+
+TEST_P(ValidateOpenCLStdNormalizeLike, VectorTooBig) {
+  const std::string ext_inst_name = GetParam();
+  const std::string body = "%val1 = OpExtInst %f32vec8 %extinst " +
+                           ext_inst_name + " %f32vec8_01010101\n";
+
+  CompileSuccessfully(GenerateKernelCode(body));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("OpenCL.std " + ext_inst_name +
+                ": "
+                "expected Result Type to have no more than 4 components"));
+}
+
+TEST_P(ValidateOpenCLStdNormalizeLike, DifferentType) {
+  const std::string ext_inst_name = GetParam();
+  const std::string body =
+      "%val1 = OpExtInst %f64vec2 %extinst " + ext_inst_name + " %f32vec2_01\n";
+
+  CompileSuccessfully(GenerateKernelCode(body));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("OpenCL.std " + ext_inst_name +
+                        ": "
+                        "expected operand P type to be equal to Result Type"));
+}
+
+INSTANTIATE_TEST_CASE_P(AllNormalizeLike, ValidateOpenCLStdNormalizeLike,
+                        ::testing::ValuesIn(std::vector<std::string>{
+                            "normalize",
+                            "fast_normalize",
+                        }), );
+
+TEST_F(ValidateExtInst, OpenCLStdBitselectSuccess) {
+  const std::string body = R"(
+%val1 = OpExtInst %f32 %extinst bitselect %f32_2 %f32_1 %f32_1
+%val2 = OpExtInst %f32vec4 %extinst bitselect %f32vec4_0123 %f32vec4_1234 %f32vec4_0123
+%val3 = OpExtInst %u32 %extinst bitselect %u32_2 %u32_1 %u32_1
+%val4 = OpExtInst %u32vec4 %extinst bitselect %u32vec4_0123 %u32vec4_0123 %u32vec4_0123
+%val5 = OpExtInst %u64 %extinst bitselect %u64_2 %u64_1 %u64_1
+)";
+
+  CompileSuccessfully(GenerateKernelCode(body));
+  ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+TEST_F(ValidateExtInst, OpenCLStdBitselectWrongResultType) {
+  const std::string body = R"(
+%val3 = OpExtInst %struct_f32_f32 %extinst bitselect %u32_2 %u32_1 %u32_1
+)";
+
+  CompileSuccessfully(GenerateKernelCode(body));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr(
+          "OpenCL.std bitselect: "
+          "expected Result Type to be an int or float scalar or vector type"));
+}
+
+TEST_F(ValidateExtInst, OpenCLStdBitselectAWrongType) {
+  const std::string body = R"(
+%val3 = OpExtInst %u32 %extinst bitselect %f32_2 %u32_1 %u32_1
+)";
+
+  CompileSuccessfully(GenerateKernelCode(body));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("OpenCL.std bitselect: "
+                "expected types of all operands to be equal to Result Type"));
+}
+
+TEST_F(ValidateExtInst, OpenCLStdBitselectBWrongType) {
+  const std::string body = R"(
+%val3 = OpExtInst %u32 %extinst bitselect %u32_2 %f32_1 %u32_1
+)";
+
+  CompileSuccessfully(GenerateKernelCode(body));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("OpenCL.std bitselect: "
+                "expected types of all operands to be equal to Result Type"));
+}
+
+TEST_F(ValidateExtInst, OpenCLStdBitselectCWrongType) {
+  const std::string body = R"(
+%val3 = OpExtInst %u32 %extinst bitselect %u32_2 %u32_1 %f32_1
+)";
+
+  CompileSuccessfully(GenerateKernelCode(body));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("OpenCL.std bitselect: "
+                "expected types of all operands to be equal to Result Type"));
+}
+
+TEST_F(ValidateExtInst, OpenCLStdSelectSuccess) {
+  const std::string body = R"(
+%val1 = OpExtInst %f32 %extinst select %f32_2 %f32_1 %u32_1
+%val2 = OpExtInst %f32vec4 %extinst select %f32vec4_0123 %f32vec4_1234 %u32vec4_0123
+%val3 = OpExtInst %u32 %extinst select %u32_2 %u32_1 %u32_1
+%val4 = OpExtInst %u32vec4 %extinst select %u32vec4_0123 %u32vec4_0123 %u32vec4_0123
+%val5 = OpExtInst %u64 %extinst select %u64_2 %u64_1 %u64_1
+)";
+
+  CompileSuccessfully(GenerateKernelCode(body));
+  ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+TEST_F(ValidateExtInst, OpenCLStdSelectWrongResultType) {
+  const std::string body = R"(
+%val3 = OpExtInst %struct_f32_f32 %extinst select %u32_2 %u32_1 %u32_1
+)";
+
+  CompileSuccessfully(GenerateKernelCode(body));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr(
+          "OpenCL.std select: "
+          "expected Result Type to be an int or float scalar or vector type"));
+}
+
+TEST_F(ValidateExtInst, OpenCLStdSelectAWrongType) {
+  const std::string body = R"(
+%val3 = OpExtInst %u32 %extinst select %f32_2 %u32_1 %u32_1
+)";
+
+  CompileSuccessfully(GenerateKernelCode(body));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("OpenCL.std select: "
+                        "expected operand A type to be equal to Result Type"));
+}
+
+TEST_F(ValidateExtInst, OpenCLStdSelectBWrongType) {
+  const std::string body = R"(
+%val3 = OpExtInst %u32 %extinst select %u32_2 %f32_1 %u32_1
+)";
+
+  CompileSuccessfully(GenerateKernelCode(body));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("OpenCL.std select: "
+                        "expected operand B type to be equal to Result Type"));
+}
+
+TEST_F(ValidateExtInst, OpenCLStdSelectCWrongType) {
+  const std::string body = R"(
+%val3 = OpExtInst %f32 %extinst select %f32_2 %f32_1 %f32_1
+)";
+
+  CompileSuccessfully(GenerateKernelCode(body));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("OpenCL.std select: "
+                        "expected operand C to be an int scalar or vector"));
+}
+
+TEST_F(ValidateExtInst, OpenCLStdSelectCWrongComponentNumber) {
+  const std::string body = R"(
+%val3 = OpExtInst %f32vec2 %extinst select %f32vec2_12 %f32vec2_01 %u32_1
+)";
+
+  CompileSuccessfully(GenerateKernelCode(body));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("OpenCL.std select: "
+                        "expected operand C to have the same number of "
+                        "components as Result Type"));
+}
+
+TEST_F(ValidateExtInst, OpenCLStdSelectCWrongBitWidth) {
+  const std::string body = R"(
+%val3 = OpExtInst %f32vec2 %extinst select %f32vec2_12 %f32vec2_01 %u64vec2_01
+)";
+
+  CompileSuccessfully(GenerateKernelCode(body));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr(
+          "OpenCL.std select: "
+          "expected operand C to have the same bit width as Result Type"));
+}
+
+TEST_P(ValidateOpenCLStdVStoreHalfLike, SuccessPhysical32) {
+  const std::string ext_inst_name = GetParam();
+  const std::string rounding_mode =
+      ext_inst_name.substr(ext_inst_name.length() - 2) == "_r" ? " RTE" : "";
+
+  std::ostringstream ss;
+  ss << "%ptr = OpAccessChain %f16_ptr_workgroup %f16vec8_workgroup %u32_1\n";
+  if (std::string::npos == ext_inst_name.find("halfn")) {
+    ss << "%val1 = OpExtInst %void %extinst " << ext_inst_name
+       << " %f32_1 %u32_1 %ptr" << rounding_mode << "\n";
+    ss << "%val2 = OpExtInst %void %extinst " << ext_inst_name
+       << " %f64_0 %u32_2 %ptr" << rounding_mode << "\n";
+  } else {
+    ss << "%val1 = OpExtInst %void %extinst " << ext_inst_name
+       << " %f32vec2_01 %u32_1 %ptr" << rounding_mode << "\n";
+    ss << "%val2 = OpExtInst %void %extinst " << ext_inst_name
+       << " %f32vec4_0123 %u32_0 %ptr" << rounding_mode << "\n";
+    ss << "%val3 = OpExtInst %void %extinst " << ext_inst_name
+       << " %f64vec2_01 %u32_2 %ptr" << rounding_mode << "\n";
+  }
+
+  CompileSuccessfully(GenerateKernelCode(ss.str()));
+  ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+TEST_P(ValidateOpenCLStdVStoreHalfLike, SuccessPhysical64) {
+  const std::string ext_inst_name = GetParam();
+  const std::string rounding_mode =
+      ext_inst_name.substr(ext_inst_name.length() - 2) == "_r" ? " RTE" : "";
+
+  std::ostringstream ss;
+  ss << "%ptr = OpAccessChain %f16_ptr_workgroup %f16vec8_workgroup %u32_1\n";
+  if (std::string::npos == ext_inst_name.find("halfn")) {
+    ss << "%val1 = OpExtInst %void %extinst " << ext_inst_name
+       << " %f32_1 %u64_1 %ptr" << rounding_mode << "\n";
+    ss << "%val2 = OpExtInst %void %extinst " << ext_inst_name
+       << " %f64_0 %u64_2 %ptr" << rounding_mode << "\n";
+  } else {
+    ss << "%val1 = OpExtInst %void %extinst " << ext_inst_name
+       << " %f32vec2_01 %u64_1 %ptr" << rounding_mode << "\n";
+    ss << "%val2 = OpExtInst %void %extinst " << ext_inst_name
+       << " %f32vec4_0123 %u64_0 %ptr" << rounding_mode << "\n";
+    ss << "%val3 = OpExtInst %void %extinst " << ext_inst_name
+       << " %f64vec2_01 %u64_2 %ptr" << rounding_mode << "\n";
+  }
+
+  CompileSuccessfully(GenerateKernelCode(ss.str(), "", "Physical64"));
+  ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+TEST_P(ValidateOpenCLStdVStoreHalfLike, NonVoidResultType) {
+  const std::string ext_inst_name = GetParam();
+  const std::string rounding_mode =
+      ext_inst_name.substr(ext_inst_name.length() - 2) == "_r" ? " RTE" : "";
+
+  std::ostringstream ss;
+  ss << "%ptr = OpAccessChain %f16_ptr_workgroup %f16vec8_workgroup %u32_1\n";
+  if (std::string::npos == ext_inst_name.find("halfn")) {
+    ss << "%val1 = OpExtInst %f32 %extinst " << ext_inst_name
+       << " %f32_1 %u32_1 %ptr" << rounding_mode << "\n";
+  } else {
+    ss << "%val1 = OpExtInst %f32 %extinst " << ext_inst_name
+       << " %f32vec2_01 %u32_1 %ptr" << rounding_mode << "\n";
+  }
+
+  CompileSuccessfully(GenerateKernelCode(ss.str()));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("OpenCL.std " + ext_inst_name +
+                        ": expected Result Type to be void"));
+}
+
+TEST_P(ValidateOpenCLStdVStoreHalfLike, WrongDataType) {
+  const std::string ext_inst_name = GetParam();
+  const std::string rounding_mode =
+      ext_inst_name.substr(ext_inst_name.length() - 2) == "_r" ? " RTE" : "";
+
+  std::ostringstream ss;
+  ss << "%ptr = OpAccessChain %f16_ptr_workgroup %f16vec8_workgroup %u32_1\n";
+  if (std::string::npos == ext_inst_name.find("halfn")) {
+    ss << "%val1 = OpExtInst %void %extinst " << ext_inst_name
+       << " %f64vec2_01 %u32_1 %ptr" << rounding_mode << "\n";
+    CompileSuccessfully(GenerateKernelCode(ss.str()));
+    ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+    EXPECT_THAT(getDiagnosticString(),
+                HasSubstr("OpenCL.std " + ext_inst_name +
+                          ": expected Data to be a 32 or 64-bit float scalar"));
+  } else {
+    ss << "%val1 = OpExtInst %void %extinst " << ext_inst_name
+       << " %f64_0 %u32_1 %ptr" << rounding_mode << "\n";
+    CompileSuccessfully(GenerateKernelCode(ss.str()));
+    ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+    EXPECT_THAT(getDiagnosticString(),
+                HasSubstr("OpenCL.std " + ext_inst_name +
+                          ": expected Data to be a 32 or 64-bit float vector"));
+  }
+}
+
+TEST_P(ValidateOpenCLStdVStoreHalfLike, AddressingModelLogical) {
+  const std::string ext_inst_name = GetParam();
+  const std::string rounding_mode =
+      ext_inst_name.substr(ext_inst_name.length() - 2) == "_r" ? " RTE" : "";
+
+  std::ostringstream ss;
+  ss << "%ptr = OpAccessChain %f16_ptr_workgroup %f16vec8_workgroup %u32_1\n";
+  if (std::string::npos == ext_inst_name.find("halfn")) {
+    ss << "%val1 = OpExtInst %void %extinst " << ext_inst_name
+       << " %f32_0 %u32_1 %ptr" << rounding_mode << "\n";
+  } else {
+    ss << "%val1 = OpExtInst %void %extinst " << ext_inst_name
+       << " %f32vec2_01 %u32_1 %ptr" << rounding_mode << "\n";
+  }
+
+  CompileSuccessfully(GenerateKernelCode(ss.str(), "", "Logical"));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("OpenCL.std " + ext_inst_name +
+                        " can only be used with physical addressing models"));
+}
+
+TEST_P(ValidateOpenCLStdVStoreHalfLike, OffsetNotSizeT) {
+  const std::string ext_inst_name = GetParam();
+  const std::string rounding_mode =
+      ext_inst_name.substr(ext_inst_name.length() - 2) == "_r" ? " RTE" : "";
+
+  std::ostringstream ss;
+  ss << "%ptr = OpAccessChain %f16_ptr_workgroup %f16vec8_workgroup %u32_1\n";
+  if (std::string::npos == ext_inst_name.find("halfn")) {
+    ss << "%val1 = OpExtInst %void %extinst " << ext_inst_name
+       << " %f32_0 %u32_1 %ptr" << rounding_mode << "\n";
+  } else {
+    ss << "%val1 = OpExtInst %void %extinst " << ext_inst_name
+       << " %f32vec2_01 %u32_1 %ptr" << rounding_mode << "\n";
+  }
+
+  CompileSuccessfully(GenerateKernelCode(ss.str(), "", "Physical64"));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("OpenCL.std " + ext_inst_name +
+                ": "
+                "expected operand Offset to be of type size_t (64-bit integer "
+                "for the addressing model used in the module)"));
+}
+
+TEST_P(ValidateOpenCLStdVStoreHalfLike, PNotPointer) {
+  const std::string ext_inst_name = GetParam();
+  const std::string rounding_mode =
+      ext_inst_name.substr(ext_inst_name.length() - 2) == "_r" ? " RTE" : "";
+
+  std::ostringstream ss;
+  if (std::string::npos == ext_inst_name.find("halfn")) {
+    ss << "%val1 = OpExtInst %void %extinst " << ext_inst_name
+       << " %f32_0 %u32_1 %f16_ptr_workgroup" << rounding_mode << "\n";
+  } else {
+    ss << "%val1 = OpExtInst %void %extinst " << ext_inst_name
+       << " %f32vec2_01 %u32_1 %f16_ptr_workgroup" << rounding_mode << "\n";
+  }
+
+  CompileSuccessfully(GenerateKernelCode(ss.str()));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("OpenCL.std " + ext_inst_name +
+                        ": expected operand P to be a pointer"));
+}
+
+TEST_P(ValidateOpenCLStdVStoreHalfLike, ConstPointer) {
+  const std::string ext_inst_name = GetParam();
+  const std::string rounding_mode =
+      ext_inst_name.substr(ext_inst_name.length() - 2) == "_r" ? " RTE" : "";
+
+  std::ostringstream ss;
+  ss << "%ptr = OpAccessChain %f16_ptr_uniform_constant "
+        "%f16vec8_uniform_constant %u32_1\n";
+  if (std::string::npos == ext_inst_name.find("halfn")) {
+    ss << "%val1 = OpExtInst %void %extinst " << ext_inst_name
+       << " %f32_0 %u32_1 %ptr" << rounding_mode << "\n";
+  } else {
+    ss << "%val1 = OpExtInst %void %extinst " << ext_inst_name
+       << " %f32vec2_01 %u32_1 %ptr" << rounding_mode << "\n";
+  }
+
+  CompileSuccessfully(GenerateKernelCode(ss.str()));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("OpenCL.std " + ext_inst_name +
+                        ": expected operand P storage class to be Generic, "
+                        "CrossWorkgroup, Workgroup or Function"));
+}
+
+TEST_P(ValidateOpenCLStdVStoreHalfLike, PDataTypeInt) {
+  const std::string ext_inst_name = GetParam();
+  const std::string rounding_mode =
+      ext_inst_name.substr(ext_inst_name.length() - 2) == "_r" ? " RTE" : "";
+
+  std::ostringstream ss;
+  ss << "%ptr = OpAccessChain %u32_ptr_workgroup %u32vec8_workgroup %u32_1\n";
+  if (std::string::npos == ext_inst_name.find("halfn")) {
+    ss << "%val1 = OpExtInst %void %extinst " << ext_inst_name
+       << " %f32_0 %u32_1 %ptr" << rounding_mode << "\n";
+  } else {
+    ss << "%val1 = OpExtInst %void %extinst " << ext_inst_name
+       << " %f32vec2_01 %u32_1 %ptr" << rounding_mode << "\n";
+  }
+
+  CompileSuccessfully(GenerateKernelCode(ss.str()));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("OpenCL.std " + ext_inst_name +
+                ": expected operand P data type to be 16-bit float scalar"));
+}
+
+TEST_P(ValidateOpenCLStdVStoreHalfLike, PDataTypeFloat32) {
+  const std::string ext_inst_name = GetParam();
+  const std::string rounding_mode =
+      ext_inst_name.substr(ext_inst_name.length() - 2) == "_r" ? " RTE" : "";
+
+  std::ostringstream ss;
+  ss << "%ptr = OpAccessChain %f32_ptr_workgroup %f32vec8_workgroup %u32_1\n";
+  if (std::string::npos == ext_inst_name.find("halfn")) {
+    ss << "%val1 = OpExtInst %void %extinst " << ext_inst_name
+       << " %f32_0 %u32_1 %ptr" << rounding_mode << "\n";
+  } else {
+    ss << "%val1 = OpExtInst %void %extinst " << ext_inst_name
+       << " %f32vec2_01 %u32_1 %ptr" << rounding_mode << "\n";
+  }
+
+  CompileSuccessfully(GenerateKernelCode(ss.str()));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("OpenCL.std " + ext_inst_name +
+                ": expected operand P data type to be 16-bit float scalar"));
+}
+
+INSTANTIATE_TEST_CASE_P(AllVStoreHalfLike, ValidateOpenCLStdVStoreHalfLike,
+                        ::testing::ValuesIn(std::vector<std::string>{
+                            "vstore_half",
+                            "vstore_half_r",
+                            "vstore_halfn",
+                            "vstore_halfn_r",
+                            "vstorea_halfn",
+                            "vstorea_halfn_r",
+                        }), );
+
+TEST_P(ValidateOpenCLStdVLoadHalfLike, SuccessPhysical32) {
+  const std::string ext_inst_name = GetParam();
+
+  std::ostringstream ss;
+  ss << "%ptr = OpAccessChain %f16_ptr_workgroup %f16vec8_workgroup %u32_1\n";
+  ss << "%val1 = OpExtInst %f32vec2 %extinst " << ext_inst_name
+     << " %u32_1 %ptr 2\n";
+  ss << "%val2 = OpExtInst %f32vec3 %extinst " << ext_inst_name
+     << " %u32_1 %ptr 3\n";
+  ss << "%val3 = OpExtInst %f32vec4 %extinst " << ext_inst_name
+     << " %u32_1 %ptr 4\n";
+
+  CompileSuccessfully(GenerateKernelCode(ss.str()));
+  ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+TEST_P(ValidateOpenCLStdVLoadHalfLike, SuccessPhysical64) {
+  const std::string ext_inst_name = GetParam();
+
+  std::ostringstream ss;
+  ss << "%ptr = OpAccessChain %f16_ptr_workgroup %f16vec8_workgroup %u32_1\n";
+  ss << "%val1 = OpExtInst %f32vec2 %extinst " << ext_inst_name
+     << " %u64_1 %ptr 2\n";
+  ss << "%val2 = OpExtInst %f32vec3 %extinst " << ext_inst_name
+     << " %u64_1 %ptr 3\n";
+  ss << "%val3 = OpExtInst %f32vec4 %extinst " << ext_inst_name
+     << " %u64_1 %ptr 4\n";
+
+  CompileSuccessfully(GenerateKernelCode(ss.str(), "", "Physical64"));
+  ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+TEST_P(ValidateOpenCLStdVLoadHalfLike, ResultTypeNotFloatVector) {
+  const std::string ext_inst_name = GetParam();
+
+  std::ostringstream ss;
+  ss << "%ptr = OpAccessChain %f16_ptr_workgroup %f16vec8_workgroup %u32_1\n";
+  ss << "%val1 = OpExtInst %f32 %extinst " << ext_inst_name
+     << " %u32_1 %ptr 1\n";
+
+  CompileSuccessfully(GenerateKernelCode(ss.str()));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("OpenCL.std " + ext_inst_name +
+                        ": expected Result Type to be a float vector type"));
+}
+
+TEST_P(ValidateOpenCLStdVLoadHalfLike, AddressingModelLogical) {
+  const std::string ext_inst_name = GetParam();
+
+  std::ostringstream ss;
+  ss << "%ptr = OpAccessChain %f16_ptr_workgroup %f16vec8_workgroup %u32_1\n";
+  ss << "%val1 = OpExtInst %f32vec2 %extinst " << ext_inst_name
+     << " %u32_1 %ptr 2\n";
+
+  CompileSuccessfully(GenerateKernelCode(ss.str(), "", "Logical"));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("OpenCL.std " + ext_inst_name +
+                        " can only be used with physical addressing models"));
+}
+
+TEST_P(ValidateOpenCLStdVLoadHalfLike, OffsetNotSizeT) {
+  const std::string ext_inst_name = GetParam();
+
+  std::ostringstream ss;
+  ss << "%ptr = OpAccessChain %f16_ptr_workgroup %f16vec8_workgroup %u32_1\n";
+  ss << "%val1 = OpExtInst %f32vec2 %extinst " << ext_inst_name
+     << " %u64_1 %ptr 2\n";
+
+  CompileSuccessfully(GenerateKernelCode(ss.str()));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("OpenCL.std " + ext_inst_name +
+                ": expected operand Offset to be of type size_t (32-bit "
+                "integer for the addressing model used in the module)"));
+}
+
+TEST_P(ValidateOpenCLStdVLoadHalfLike, PNotPointer) {
+  const std::string ext_inst_name = GetParam();
+
+  std::ostringstream ss;
+  ss << "%val1 = OpExtInst %f32vec2 %extinst " << ext_inst_name
+     << " %u32_1 %f16_ptr_workgroup 2\n";
+
+  CompileSuccessfully(GenerateKernelCode(ss.str()));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("OpenCL.std " + ext_inst_name +
+                        ": expected operand P to be a pointer"));
+}
+
+TEST_P(ValidateOpenCLStdVLoadHalfLike, OffsetWrongStorageType) {
+  const std::string ext_inst_name = GetParam();
+
+  std::ostringstream ss;
+  ss << "%ptr = OpAccessChain %f16_ptr_input %f16vec8_input %u32_1\n";
+  ss << "%val1 = OpExtInst %f32vec2 %extinst " << ext_inst_name
+     << " %u32_1 %ptr 2\n";
+
+  CompileSuccessfully(GenerateKernelCode(ss.str()));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("OpenCL.std " + ext_inst_name +
+                ": expected operand P storage class to be UniformConstant, "
+                "Generic, CrossWorkgroup, Workgroup or Function"));
+}
+
+TEST_P(ValidateOpenCLStdVLoadHalfLike, PDataTypeInt) {
+  const std::string ext_inst_name = GetParam();
+
+  std::ostringstream ss;
+  ss << "%ptr = OpAccessChain %u32_ptr_workgroup %u32vec8_workgroup %u32_1\n";
+  ss << "%val1 = OpExtInst %f32vec2 %extinst " << ext_inst_name
+     << " %u32_1 %ptr 2\n";
+
+  CompileSuccessfully(GenerateKernelCode(ss.str()));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("OpenCL.std " + ext_inst_name +
+                ": expected operand P data type to be 16-bit float scalar"));
+}
+
+TEST_P(ValidateOpenCLStdVLoadHalfLike, PDataTypeFloat32) {
+  const std::string ext_inst_name = GetParam();
+
+  std::ostringstream ss;
+  ss << "%ptr = OpAccessChain %f32_ptr_workgroup %f32vec8_workgroup %u32_1\n";
+  ss << "%val1 = OpExtInst %f32vec2 %extinst " << ext_inst_name
+     << " %u32_1 %ptr 2\n";
+
+  CompileSuccessfully(GenerateKernelCode(ss.str()));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("OpenCL.std " + ext_inst_name +
+                ": expected operand P data type to be 16-bit float scalar"));
+}
+
+TEST_P(ValidateOpenCLStdVLoadHalfLike, WrongN) {
+  const std::string ext_inst_name = GetParam();
+
+  std::ostringstream ss;
+  ss << "%ptr = OpAccessChain %f16_ptr_workgroup %f16vec8_workgroup %u32_1\n";
+  ss << "%val1 = OpExtInst %f32vec2 %extinst " << ext_inst_name
+     << " %u32_1 %ptr 3\n";
+
+  CompileSuccessfully(GenerateKernelCode(ss.str()));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("OpenCL.std " + ext_inst_name +
+                        ": expected literal N to be equal to the number of "
+                        "components of Result Type"));
+}
+
+INSTANTIATE_TEST_CASE_P(AllVLoadHalfLike, ValidateOpenCLStdVLoadHalfLike,
+                        ::testing::ValuesIn(std::vector<std::string>{
+                            "vload_halfn",
+                            "vloada_halfn",
+                        }), );
+
+TEST_F(ValidateExtInst, VLoadNSuccessFloatPhysical32) {
+  std::ostringstream ss;
+  ss << "%ptr = OpAccessChain %f32_ptr_uniform_constant "
+        "%f32vec8_uniform_constant %u32_1\n";
+  ss << "%val1 = OpExtInst %f32vec2 %extinst vloadn %u32_1 %ptr 2\n";
+  ss << "%val2 = OpExtInst %f32vec3 %extinst vloadn %u32_1 %ptr 3\n";
+  ss << "%val3 = OpExtInst %f32vec4 %extinst vloadn %u32_1 %ptr 4\n";
+
+  CompileSuccessfully(GenerateKernelCode(ss.str()));
+  ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+TEST_F(ValidateExtInst, VLoadNSuccessIntPhysical32) {
+  std::ostringstream ss;
+  ss << "%ptr = OpAccessChain %u32_ptr_uniform_constant "
+        "%u32vec8_uniform_constant %u32_1\n";
+  ss << "%val1 = OpExtInst %u32vec2 %extinst vloadn %u32_1 %ptr 2\n";
+  ss << "%val2 = OpExtInst %u32vec3 %extinst vloadn %u32_1 %ptr 3\n";
+  ss << "%val3 = OpExtInst %u32vec4 %extinst vloadn %u32_1 %ptr 4\n";
+
+  CompileSuccessfully(GenerateKernelCode(ss.str()));
+  ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+TEST_F(ValidateExtInst, VLoadNSuccessFloatPhysical64) {
+  std::ostringstream ss;
+  ss << "%ptr = OpAccessChain %f32_ptr_uniform_constant "
+        "%f32vec8_uniform_constant %u32_1\n";
+  ss << "%val1 = OpExtInst %f32vec2 %extinst vloadn %u64_1 %ptr 2\n";
+  ss << "%val2 = OpExtInst %f32vec3 %extinst vloadn %u64_1 %ptr 3\n";
+  ss << "%val3 = OpExtInst %f32vec4 %extinst vloadn %u64_1 %ptr 4\n";
+
+  CompileSuccessfully(GenerateKernelCode(ss.str(), "", "Physical64"));
+  ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+TEST_F(ValidateExtInst, VLoadNSuccessIntPhysical64) {
+  std::ostringstream ss;
+  ss << "%ptr = OpAccessChain %u32_ptr_uniform_constant "
+        "%u32vec8_uniform_constant %u32_1\n";
+  ss << "%val1 = OpExtInst %u32vec2 %extinst vloadn %u64_1 %ptr 2\n";
+  ss << "%val2 = OpExtInst %u32vec3 %extinst vloadn %u64_1 %ptr 3\n";
+  ss << "%val3 = OpExtInst %u32vec4 %extinst vloadn %u64_1 %ptr 4\n";
+
+  CompileSuccessfully(GenerateKernelCode(ss.str(), "", "Physical64"));
+  ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+TEST_F(ValidateExtInst, VLoadNWrongResultType) {
+  std::ostringstream ss;
+  ss << "%ptr = OpAccessChain %f32_ptr_uniform_constant "
+        "%f32vec8_uniform_constant %u32_1\n";
+  ss << "%val1 = OpExtInst %f32 %extinst vloadn %u32_1 %ptr 2\n";
+
+  CompileSuccessfully(GenerateKernelCode(ss.str()));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("OpenCL.std vloadn: "
+                "expected Result Type to be an int or float vector type"));
+}
+
+TEST_F(ValidateExtInst, VLoadNAddressingModelLogical) {
+  std::ostringstream ss;
+  ss << "%ptr = OpAccessChain %f32_ptr_uniform_constant "
+        "%f32vec8_uniform_constant %u32_1\n";
+  ss << "%val1 = OpExtInst %f32vec2 %extinst vloadn %u32_1 %ptr 2\n";
+
+  CompileSuccessfully(GenerateKernelCode(ss.str(), "", "Logical"));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("OpenCL.std vloadn can only be used with physical "
+                        "addressing models"));
+}
+
+TEST_F(ValidateExtInst, VLoadNOffsetNotSizeT) {
+  std::ostringstream ss;
+  ss << "%ptr = OpAccessChain %f32_ptr_uniform_constant "
+        "%f32vec8_uniform_constant %u32_1\n";
+  ss << "%val1 = OpExtInst %f32vec2 %extinst vloadn %u64_1 %ptr 2\n";
+
+  CompileSuccessfully(GenerateKernelCode(ss.str()));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr(
+          "OpenCL.std vloadn: expected operand Offset to be of type size_t "
+          "(32-bit integer for the addressing model used in the module)"));
+}
+
+TEST_F(ValidateExtInst, VLoadNPNotPointer) {
+  std::ostringstream ss;
+  ss << "%val1 = OpExtInst %f32vec2 %extinst vloadn %u32_1 "
+        "%f32_ptr_uniform_constant 2\n";
+
+  CompileSuccessfully(GenerateKernelCode(ss.str()));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("OpenCL.std vloadn: expected operand P to be a pointer"));
+}
+
+TEST_F(ValidateExtInst, VLoadNWrongStorageClass) {
+  std::ostringstream ss;
+  ss << "%ptr = OpAccessChain %u32_ptr_workgroup %u32vec8_workgroup %u32_1\n";
+  ss << "%val1 = OpExtInst %u32vec2 %extinst vloadn %u32_1 %ptr 2\n";
+
+  CompileSuccessfully(GenerateKernelCode(ss.str()));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("OpenCL.std vloadn: expected operand P storage class "
+                        "to be UniformConstant or Generic"));
+}
+
+TEST_F(ValidateExtInst, VLoadNWrongComponentType) {
+  std::ostringstream ss;
+  ss << "%ptr = OpAccessChain %f32_ptr_uniform_constant "
+        "%f32vec8_uniform_constant %u32_1\n";
+  ss << "%val1 = OpExtInst %u32vec2 %extinst vloadn %u32_1 %ptr 2\n";
+
+  CompileSuccessfully(GenerateKernelCode(ss.str()));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("OpenCL.std vloadn: expected operand P data type to be "
+                        "equal to component type of Result Type"));
+}
+
+TEST_F(ValidateExtInst, VLoadNWrongN) {
+  std::ostringstream ss;
+  ss << "%ptr = OpAccessChain %f32_ptr_uniform_constant "
+        "%f32vec8_uniform_constant %u32_1\n";
+  ss << "%val1 = OpExtInst %f32vec2 %extinst vloadn %u32_1 %ptr 3\n";
+
+  CompileSuccessfully(GenerateKernelCode(ss.str()));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("OpenCL.std vloadn: expected literal N to be equal to "
+                        "the number of components of Result Type"));
+}
+
+TEST_F(ValidateExtInst, VLoadHalfSuccessPhysical32) {
+  std::ostringstream ss;
+  ss << "%ptr = OpAccessChain %f16_ptr_uniform_constant "
+        "%f16vec8_uniform_constant %u32_1\n";
+  ss << "%val1 = OpExtInst %f32 %extinst vload_half %u32_1 %ptr\n";
+  ss << "%val2 = OpExtInst %f64 %extinst vload_half %u32_1 %ptr\n";
+
+  CompileSuccessfully(GenerateKernelCode(ss.str()));
+  ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+TEST_F(ValidateExtInst, VLoadHalfSuccessPhysical64) {
+  std::ostringstream ss;
+  ss << "%ptr = OpAccessChain %f16_ptr_uniform_constant "
+        "%f16vec8_uniform_constant %u32_1\n";
+  ss << "%val1 = OpExtInst %f32 %extinst vload_half %u64_1 %ptr\n";
+  ss << "%val2 = OpExtInst %f64 %extinst vload_half %u64_1 %ptr\n";
+
+  CompileSuccessfully(GenerateKernelCode(ss.str(), "", "Physical64"));
+  ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+TEST_F(ValidateExtInst, VLoadHalfWrongResultType) {
+  std::ostringstream ss;
+  ss << "%ptr = OpAccessChain %f16_ptr_uniform_constant "
+        "%f16vec8_uniform_constant %u32_1\n";
+  ss << "%val1 = OpExtInst %u32 %extinst vload_half %u32_1 %ptr\n";
+
+  CompileSuccessfully(GenerateKernelCode(ss.str()));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("OpenCL.std vload_half: "
+                        "expected Result Type to be a float scalar type"));
+}
+
+TEST_F(ValidateExtInst, VLoadHalfAddressingModelLogical) {
+  std::ostringstream ss;
+  ss << "%ptr = OpAccessChain %f16_ptr_uniform_constant "
+        "%f16vec8_uniform_constant %u32_1\n";
+  ss << "%val1 = OpExtInst %f32 %extinst vload_half %u32_1 %ptr\n";
+
+  CompileSuccessfully(GenerateKernelCode(ss.str(), "", "Logical"));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("OpenCL.std vload_half can only be used with physical "
+                        "addressing models"));
+}
+
+TEST_F(ValidateExtInst, VLoadHalfOffsetNotSizeT) {
+  std::ostringstream ss;
+  ss << "%ptr = OpAccessChain %f16_ptr_uniform_constant "
+        "%f16vec8_uniform_constant %u32_1\n";
+  ss << "%val1 = OpExtInst %f32 %extinst vload_half %u64_1 %ptr\n";
+
+  CompileSuccessfully(GenerateKernelCode(ss.str()));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr(
+          "OpenCL.std vload_half: expected operand Offset to be of type size_t "
+          "(32-bit integer for the addressing model used in the module)"));
+}
+
+TEST_F(ValidateExtInst, VLoadHalfPNotPointer) {
+  std::ostringstream ss;
+  ss << "%val1 = OpExtInst %f32 %extinst vload_half %u32_1 "
+        "%f16_ptr_uniform_constant\n";
+
+  CompileSuccessfully(GenerateKernelCode(ss.str()));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("OpenCL.std vload_half: expected operand P to be a pointer"));
+}
+
+TEST_F(ValidateExtInst, VLoadHalfWrongStorageClass) {
+  std::ostringstream ss;
+  ss << "%ptr = OpAccessChain %f16_ptr_input %f16vec8_input %u32_1\n";
+  ss << "%val1 = OpExtInst %f32 %extinst vload_half %u32_1 %ptr\n";
+
+  CompileSuccessfully(GenerateKernelCode(ss.str()));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr(
+          "OpenCL.std vload_half: expected operand P storage class to be "
+          "UniformConstant, Generic, CrossWorkgroup, Workgroup or Function"));
+}
+
+TEST_F(ValidateExtInst, VLoadHalfPDataTypeInt) {
+  std::ostringstream ss;
+  ss << "%ptr = OpAccessChain %u32_ptr_uniform_constant "
+        "%u32vec8_uniform_constant %u32_1\n";
+  ss << "%val1 = OpExtInst %f32 %extinst vload_half %u32_1 %ptr\n";
+
+  CompileSuccessfully(GenerateKernelCode(ss.str()));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("OpenCL.std vload_half: expected operand P data type "
+                        "to be 16-bit float scalar"));
+}
+
+TEST_F(ValidateExtInst, VLoadHalfPDataTypeFloat32) {
+  std::ostringstream ss;
+  ss << "%ptr = OpAccessChain %f32_ptr_uniform_constant "
+        "%f32vec8_uniform_constant %u32_1\n";
+  ss << "%val1 = OpExtInst %f32 %extinst vload_half %u32_1 %ptr\n";
+
+  CompileSuccessfully(GenerateKernelCode(ss.str()));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("OpenCL.std vload_half: expected operand P data type "
+                        "to be 16-bit float scalar"));
+}
+
+TEST_F(ValidateExtInst, VStoreNSuccessFloatPhysical32) {
+  std::ostringstream ss;
+  ss << "%ptr_w = OpAccessChain %f32_ptr_workgroup %f32vec8_workgroup %u32_1\n";
+  ss << "%ptr_g = OpPtrCastToGeneric %f32_ptr_generic %ptr_w\n";
+  ss << "%val1 = OpExtInst %void %extinst vstoren %f32vec2_01 %u32_1 %ptr_g\n";
+  ss << "%val2 = OpExtInst %void %extinst vstoren %f32vec4_0123 %u32_1 "
+        "%ptr_g\n";
+
+  CompileSuccessfully(GenerateKernelCode(ss.str()));
+  ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+TEST_F(ValidateExtInst, VStoreNSuccessFloatPhysical64) {
+  std::ostringstream ss;
+  ss << "%ptr_w = OpAccessChain %f32_ptr_workgroup %f32vec8_workgroup %u32_1\n";
+  ss << "%ptr_g = OpPtrCastToGeneric %f32_ptr_generic %ptr_w\n";
+  ss << "%val1 = OpExtInst %void %extinst vstoren %f32vec2_01 %u64_1 %ptr_g\n";
+  ss << "%val2 = OpExtInst %void %extinst vstoren %f32vec4_0123 %u64_1 "
+        "%ptr_g\n";
+
+  CompileSuccessfully(GenerateKernelCode(ss.str(), "", "Physical64"));
+  ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+TEST_F(ValidateExtInst, VStoreNSuccessIntPhysical32) {
+  std::ostringstream ss;
+  ss << "%ptr_w = OpAccessChain %u32_ptr_workgroup %u32vec8_workgroup %u32_1\n";
+  ss << "%ptr_g = OpPtrCastToGeneric %u32_ptr_generic %ptr_w\n";
+  ss << "%val1 = OpExtInst %void %extinst vstoren %u32vec2_01 %u32_1 %ptr_g\n";
+  ss << "%val2 = OpExtInst %void %extinst vstoren %u32vec4_0123 %u32_1 "
+        "%ptr_g\n";
+
+  CompileSuccessfully(GenerateKernelCode(ss.str()));
+  ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+TEST_F(ValidateExtInst, VStoreNSuccessIntPhysical64) {
+  std::ostringstream ss;
+  ss << "%ptr_w = OpAccessChain %u32_ptr_workgroup %u32vec8_workgroup %u32_1\n";
+  ss << "%ptr_g = OpPtrCastToGeneric %u32_ptr_generic %ptr_w\n";
+  ss << "%val1 = OpExtInst %void %extinst vstoren %u32vec2_01 %u64_1 %ptr_g\n";
+  ss << "%val2 = OpExtInst %void %extinst vstoren %u32vec4_0123 %u64_1 "
+        "%ptr_g\n";
+
+  CompileSuccessfully(GenerateKernelCode(ss.str(), "", "Physical64"));
+  ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+TEST_F(ValidateExtInst, VStoreNResultTypeNotVoid) {
+  std::ostringstream ss;
+  ss << "%ptr_w = OpAccessChain %f32_ptr_workgroup %f32vec8_workgroup %u32_1\n";
+  ss << "%ptr_g = OpPtrCastToGeneric %f32_ptr_generic %ptr_w\n";
+  ss << "%val1 = OpExtInst %f32 %extinst vstoren %f32vec2_01 %u32_1 %ptr_g\n";
+
+  CompileSuccessfully(GenerateKernelCode(ss.str()));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("OpenCL.std vstoren: expected Result Type to be void"));
+}
+
+TEST_F(ValidateExtInst, VStoreNDataWrongType) {
+  std::ostringstream ss;
+  ss << "%ptr_w = OpAccessChain %f32_ptr_workgroup %f32vec8_workgroup %u32_1\n";
+  ss << "%ptr_g = OpPtrCastToGeneric %f32_ptr_generic %ptr_w\n";
+  ss << "%val1 = OpExtInst %void %extinst vstoren %f32_1 %u32_1 %ptr_g\n";
+
+  CompileSuccessfully(GenerateKernelCode(ss.str()));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr(
+          "OpenCL.std vstoren: expected Data to be an int or float vector"));
+}
+
+TEST_F(ValidateExtInst, VStoreNAddressingModelLogical) {
+  std::ostringstream ss;
+  ss << "%ptr_w = OpAccessChain %f32_ptr_workgroup %f32vec8_workgroup %u32_1\n";
+  ss << "%ptr_g = OpPtrCastToGeneric %f32_ptr_generic %ptr_w\n";
+  ss << "%val1 = OpExtInst %void %extinst vstoren %f32vec2_01 %u32_1 %ptr_g\n";
+
+  CompileSuccessfully(GenerateKernelCode(ss.str(), "", "Logical"));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("OpenCL.std vstoren can only be used with physical "
+                        "addressing models"));
+}
+
+TEST_F(ValidateExtInst, VStoreNOffsetNotSizeT) {
+  std::ostringstream ss;
+  ss << "%ptr_w = OpAccessChain %f32_ptr_workgroup %f32vec8_workgroup %u32_1\n";
+  ss << "%ptr_g = OpPtrCastToGeneric %f32_ptr_generic %ptr_w\n";
+  ss << "%val1 = OpExtInst %void %extinst vstoren %f32vec2_01 %u32_1 %ptr_g\n";
+
+  CompileSuccessfully(GenerateKernelCode(ss.str(), "", "Physical64"));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr(
+          "OpenCL.std vstoren: expected operand Offset to be of type size_t "
+          "(64-bit integer for the addressing model used in the module)"));
+}
+
+TEST_F(ValidateExtInst, VStoreNPNotPointer) {
+  std::ostringstream ss;
+  ss << "%val1 = OpExtInst %void %extinst vstoren %f32vec2_01 %u32_1 "
+        "%f32_ptr_generic\n";
+
+  CompileSuccessfully(GenerateKernelCode(ss.str()));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("OpenCL.std vstoren: expected operand P to be a pointer"));
+}
+
+TEST_F(ValidateExtInst, VStoreNPNotGeneric) {
+  std::ostringstream ss;
+  ss << "%ptr_w = OpAccessChain %f32_ptr_workgroup %f32vec8_workgroup %u32_1\n";
+  ss << "%val1 = OpExtInst %void %extinst vstoren %f32vec2_01 %u32_1 %ptr_w\n";
+
+  CompileSuccessfully(GenerateKernelCode(ss.str()));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("OpenCL.std vstoren: expected operand P storage class "
+                        "to be Generic"));
+}
+
+TEST_F(ValidateExtInst, VStorePWrongDataType) {
+  std::ostringstream ss;
+  ss << "%ptr_w = OpAccessChain %f32_ptr_workgroup %f32vec8_workgroup %u32_1\n";
+  ss << "%ptr_g = OpPtrCastToGeneric %f32_ptr_generic %ptr_w\n";
+  ss << "%val1 = OpExtInst %void %extinst vstoren %u32vec2_01 %u32_1 %ptr_g\n";
+
+  CompileSuccessfully(GenerateKernelCode(ss.str()));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("OpenCL.std vstoren: expected operand P data type to "
+                        "be equal to the type of operand Data components"));
+}
+
+TEST_F(ValidateExtInst, OpenCLStdShuffleSuccess) {
+  const std::string body = R"(
+%val1 = OpExtInst %f32vec2 %extinst shuffle %f32vec4_0123 %u32vec2_01
+%val2 = OpExtInst %f32vec4 %extinst shuffle %f32vec4_0123 %u32vec4_0123
+%val3 = OpExtInst %u32vec2 %extinst shuffle %u32vec4_0123 %u32vec2_01
+%val4 = OpExtInst %u32vec4 %extinst shuffle %u32vec4_0123 %u32vec4_0123
+)";
+
+  CompileSuccessfully(GenerateKernelCode(body));
+  ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+TEST_F(ValidateExtInst, OpenCLStdShuffleWrongResultType) {
+  const std::string body = R"(
+%val1 = OpExtInst %f32 %extinst shuffle %f32vec4_0123 %u32vec2_01
+)";
+
+  CompileSuccessfully(GenerateKernelCode(body));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("OpenCL.std shuffle: "
+                "expected Result Type to be an int or float vector type"));
+}
+
+TEST_F(ValidateExtInst, OpenCLStdShuffleResultTypeInvalidNumComponents) {
+  const std::string body = R"(
+%val1 = OpExtInst %f32vec3 %extinst shuffle %f32vec4_0123 %u32vec3_012
+)";
+
+  CompileSuccessfully(GenerateKernelCode(body));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("OpenCL.std shuffle: "
+                "expected Result Type to have 2, 4, 8 or 16 components"));
+}
+
+TEST_F(ValidateExtInst, OpenCLStdShuffleXWrongType) {
+  const std::string body = R"(
+%val1 = OpExtInst %f32vec2 %extinst shuffle %f32_0 %u32vec2_01
+)";
+
+  CompileSuccessfully(GenerateKernelCode(body));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("OpenCL.std shuffle: "
+                        "expected operand X to be an int or float vector"));
+}
+
+TEST_F(ValidateExtInst, OpenCLStdShuffleXInvalidNumComponents) {
+  const std::string body = R"(
+%val1 = OpExtInst %f32vec2 %extinst shuffle %f32vec3_012 %u32vec2_01
+)";
+
+  CompileSuccessfully(GenerateKernelCode(body));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("OpenCL.std shuffle: "
+                        "expected operand X to have 2, 4, 8 or 16 components"));
+}
+
+TEST_F(ValidateExtInst, OpenCLStdShuffleXInvalidComponentType) {
+  const std::string body = R"(
+%val1 = OpExtInst %f32vec2 %extinst shuffle %f64vec4_0123 %u32vec2_01
+)";
+
+  CompileSuccessfully(GenerateKernelCode(body));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr(
+          "OpenCL.std shuffle: "
+          "expected operand X and Result Type to have equal component types"));
+}
+
+TEST_F(ValidateExtInst, OpenCLStdShuffleShuffleMaskNotIntVector) {
+  const std::string body = R"(
+%val1 = OpExtInst %f32vec2 %extinst shuffle %f32vec4_0123 %f32vec2_01
+)";
+
+  CompileSuccessfully(GenerateKernelCode(body));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("OpenCL.std shuffle: "
+                        "expected operand Shuffle Mask to be an int vector"));
+}
+
+TEST_F(ValidateExtInst, OpenCLStdShuffleShuffleMaskInvalidNumComponents) {
+  const std::string body = R"(
+%val1 = OpExtInst %f32vec4 %extinst shuffle %f32vec4_0123 %u32vec2_01
+)";
+
+  CompileSuccessfully(GenerateKernelCode(body));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("OpenCL.std shuffle: "
+                        "expected operand Shuffle Mask to have the same number "
+                        "of components as Result Type"));
+}
+
+TEST_F(ValidateExtInst, OpenCLStdShuffleShuffleMaskInvalidBitWidth) {
+  const std::string body = R"(
+%val1 = OpExtInst %f64vec2 %extinst shuffle %f64vec4_0123 %u32vec2_01
+)";
+
+  CompileSuccessfully(GenerateKernelCode(body));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("OpenCL.std shuffle: "
+                        "expected operand Shuffle Mask components to have the "
+                        "same bit width as Result Type components"));
+}
+
+TEST_F(ValidateExtInst, OpenCLStdShuffle2Success) {
+  const std::string body = R"(
+%val1 = OpExtInst %f32vec2 %extinst shuffle2 %f32vec4_0123 %f32vec4_0123 %u32vec2_01
+%val2 = OpExtInst %f32vec4 %extinst shuffle2 %f32vec4_0123 %f32vec4_0123 %u32vec4_0123
+%val3 = OpExtInst %u32vec2 %extinst shuffle2 %u32vec4_0123 %u32vec4_0123 %u32vec2_01
+%val4 = OpExtInst %u32vec4 %extinst shuffle2 %u32vec4_0123 %u32vec4_0123 %u32vec4_0123
+)";
+
+  CompileSuccessfully(GenerateKernelCode(body));
+  ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+TEST_F(ValidateExtInst, OpenCLStdShuffle2WrongResultType) {
+  const std::string body = R"(
+%val1 = OpExtInst %f32 %extinst shuffle2 %f32vec4_0123 %f32vec4_0123 %u32vec2_01
+)";
+
+  CompileSuccessfully(GenerateKernelCode(body));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("OpenCL.std shuffle2: "
+                "expected Result Type to be an int or float vector type"));
+}
+
+TEST_F(ValidateExtInst, OpenCLStdShuffle2ResultTypeInvalidNumComponents) {
+  const std::string body = R"(
+%val1 = OpExtInst %f32vec3 %extinst shuffle2 %f32vec4_0123 %f32vec4_0123 %u32vec3_012
+)";
+
+  CompileSuccessfully(GenerateKernelCode(body));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("OpenCL.std shuffle2: "
+                "expected Result Type to have 2, 4, 8 or 16 components"));
+}
+
+TEST_F(ValidateExtInst, OpenCLStdShuffle2XWrongType) {
+  const std::string body = R"(
+%val1 = OpExtInst %f32vec2 %extinst shuffle2 %f32_0 %f32_0 %u32vec2_01
+)";
+
+  CompileSuccessfully(GenerateKernelCode(body));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("OpenCL.std shuffle2: "
+                        "expected operand X to be an int or float vector"));
+}
+
+TEST_F(ValidateExtInst, OpenCLStdShuffle2YTypeDifferentFromX) {
+  const std::string body = R"(
+%val1 = OpExtInst %f32vec2 %extinst shuffle2 %f32vec2_01 %f32vec4_0123 %u32vec2_01
+)";
+
+  CompileSuccessfully(GenerateKernelCode(body));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("OpenCL.std shuffle2: "
+                        "expected operands X and Y to be of the same type"));
+}
+
+TEST_F(ValidateExtInst, OpenCLStdShuffle2XInvalidNumComponents) {
+  const std::string body = R"(
+%val1 = OpExtInst %f32vec2 %extinst shuffle2 %f32vec3_012 %f32vec3_012 %u32vec2_01
+)";
+
+  CompileSuccessfully(GenerateKernelCode(body));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("OpenCL.std shuffle2: "
+                        "expected operand X to have 2, 4, 8 or 16 components"));
+}
+
+TEST_F(ValidateExtInst, OpenCLStdShuffle2XInvalidComponentType) {
+  const std::string body = R"(
+%val1 = OpExtInst %f32vec2 %extinst shuffle2 %f64vec4_0123 %f64vec4_0123 %u32vec2_01
+)";
+
+  CompileSuccessfully(GenerateKernelCode(body));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr(
+          "OpenCL.std shuffle2: "
+          "expected operand X and Result Type to have equal component types"));
+}
+
+TEST_F(ValidateExtInst, OpenCLStdShuffle2ShuffleMaskNotIntVector) {
+  const std::string body = R"(
+%val1 = OpExtInst %f32vec2 %extinst shuffle2 %f32vec4_0123 %f32vec4_0123 %f32vec2_01
+)";
+
+  CompileSuccessfully(GenerateKernelCode(body));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("OpenCL.std shuffle2: "
+                        "expected operand Shuffle Mask to be an int vector"));
+}
+
+TEST_F(ValidateExtInst, OpenCLStdShuffle2ShuffleMaskInvalidNumComponents) {
+  const std::string body = R"(
+%val1 = OpExtInst %f32vec4 %extinst shuffle2 %f32vec4_0123 %f32vec4_0123 %u32vec2_01
+)";
+
+  CompileSuccessfully(GenerateKernelCode(body));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("OpenCL.std shuffle2: "
+                        "expected operand Shuffle Mask to have the same number "
+                        "of components as Result Type"));
+}
+
+TEST_F(ValidateExtInst, OpenCLStdShuffle2ShuffleMaskInvalidBitWidth) {
+  const std::string body = R"(
+%val1 = OpExtInst %f64vec2 %extinst shuffle2 %f64vec4_0123 %f64vec4_0123 %u32vec2_01
+)";
+
+  CompileSuccessfully(GenerateKernelCode(body));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("OpenCL.std shuffle2: "
+                        "expected operand Shuffle Mask components to have the "
+                        "same bit width as Result Type components"));
+}
+
+TEST_F(ValidateExtInst, OpenCLStdPrintfSuccess) {
+  const std::string body = R"(
+%format = OpAccessChain %u8_ptr_uniform_constant %u8arr_uniform_constant %u32_0
+%val1 = OpExtInst %u32 %extinst printf %format %u32_0 %u32_1
+)";
+
+  CompileSuccessfully(GenerateKernelCode(body));
+  ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+TEST_F(ValidateExtInst, OpenCLStdPrintfBoolResultType) {
+  const std::string body = R"(
+%format = OpAccessChain %u8_ptr_uniform_constant %u8arr_uniform_constant %u32_0
+%val1 = OpExtInst %bool %extinst printf %format %u32_0 %u32_1
+)";
+
+  CompileSuccessfully(GenerateKernelCode(body));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr(
+          "OpenCL.std printf: expected Result Type to be a 32-bit int type"));
+}
+
+TEST_F(ValidateExtInst, OpenCLStdPrintfU64ResultType) {
+  const std::string body = R"(
+%format = OpAccessChain %u8_ptr_uniform_constant %u8arr_uniform_constant %u32_0
+%val1 = OpExtInst %u64 %extinst printf %format %u32_0 %u32_1
+)";
+
+  CompileSuccessfully(GenerateKernelCode(body));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr(
+          "OpenCL.std printf: expected Result Type to be a 32-bit int type"));
+}
+
+TEST_F(ValidateExtInst, OpenCLStdPrintfFormatNotPointer) {
+  const std::string body = R"(
+%val1 = OpExtInst %u32 %extinst printf %u8_ptr_uniform_constant %u32_0 %u32_1
+)";
+
+  CompileSuccessfully(GenerateKernelCode(body));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("OpenCL.std printf: expected operand Format to be a pointer"));
+}
+
+TEST_F(ValidateExtInst, OpenCLStdPrintfFormatNotUniformConstStorageClass) {
+  const std::string body = R"(
+%format_const = OpAccessChain %u8_ptr_uniform_constant %u8arr_uniform_constant %u32_0
+%format = OpBitcast %u8_ptr_generic %format_const
+%val1 = OpExtInst %u32 %extinst printf %format %u32_0 %u32_1
+)";
+
+  CompileSuccessfully(GenerateKernelCode(body));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("OpenCL.std printf: expected Format storage class to "
+                        "be UniformConstant"));
+}
+
+TEST_F(ValidateExtInst, OpenCLStdPrintfFormatNotU8Pointer) {
+  const std::string body = R"(
+%format = OpAccessChain %u32_ptr_uniform_constant %u32vec8_uniform_constant %u32_0
+%val1 = OpExtInst %u32 %extinst printf %format %u32_0 %u32_1
+)";
+
+  CompileSuccessfully(GenerateKernelCode(body));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr(
+          "OpenCL.std printf: expected Format data type to be 8-bit int"));
+}
+
+TEST_F(ValidateExtInst, OpenCLStdPrefetchU32Success) {
+  const std::string body = R"(
+%ptr = OpAccessChain %u32_ptr_cross_workgroup %u32arr_cross_workgroup %u32_0
+%val1 = OpExtInst %void %extinst prefetch %ptr %u32_256
+)";
+
+  CompileSuccessfully(GenerateKernelCode(body));
+  ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+TEST_F(ValidateExtInst, OpenCLStdPrefetchU32Physical64Success) {
+  const std::string body = R"(
+%ptr = OpAccessChain %u32_ptr_cross_workgroup %u32arr_cross_workgroup %u32_0
+%val1 = OpExtInst %void %extinst prefetch %ptr %u64_256
+)";
+
+  CompileSuccessfully(GenerateKernelCode(body, "", "Physical64"));
+  ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+TEST_F(ValidateExtInst, OpenCLStdPrefetchF32Success) {
+  const std::string body = R"(
+%ptr = OpAccessChain %f32_ptr_cross_workgroup %f32arr_cross_workgroup %u32_0
+%val1 = OpExtInst %void %extinst prefetch %ptr %u32_256
+)";
+
+  CompileSuccessfully(GenerateKernelCode(body));
+  ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+TEST_F(ValidateExtInst, OpenCLStdPrefetchF32Vec2Success) {
+  const std::string body = R"(
+%ptr = OpAccessChain %f32vec2_ptr_cross_workgroup %f32vec2arr_cross_workgroup %u32_0
+%val1 = OpExtInst %void %extinst prefetch %ptr %u32_256
+)";
+
+  CompileSuccessfully(GenerateKernelCode(body));
+  ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+TEST_F(ValidateExtInst, OpenCLStdPrefetchResultTypeNotVoid) {
+  const std::string body = R"(
+%ptr = OpAccessChain %u32_ptr_cross_workgroup %u32arr_cross_workgroup %u32_0
+%val1 = OpExtInst %u32 %extinst prefetch %ptr %u32_256
+)";
+
+  CompileSuccessfully(GenerateKernelCode(body));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("OpenCL.std prefetch: expected Result Type to be void"));
+}
+
+TEST_F(ValidateExtInst, OpenCLStdPrefetchPtrNotPointer) {
+  const std::string body = R"(
+%val1 = OpExtInst %void %extinst prefetch %u32_ptr_cross_workgroup %u32_256
+)";
+
+  CompileSuccessfully(GenerateKernelCode(body));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("OpenCL.std prefetch: expected operand Ptr to be a pointer"));
+}
+
+TEST_F(ValidateExtInst, OpenCLStdPrefetchPtrNotCrossWorkgroup) {
+  const std::string body = R"(
+%ptr = OpAccessChain %u8_ptr_uniform_constant %u8arr_uniform_constant %u32_0
+%val1 = OpExtInst %void %extinst prefetch %ptr %u32_256
+)";
+
+  CompileSuccessfully(GenerateKernelCode(body));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("OpenCL.std prefetch: expected operand Ptr storage "
+                        "class to be CrossWorkgroup"));
+}
+
+TEST_F(ValidateExtInst, OpenCLStdPrefetchInvalidDataType) {
+  const std::string body = R"(
+%ptr = OpAccessChain %struct_ptr_cross_workgroup %struct_arr_cross_workgroup %u32_0
+%val1 = OpExtInst %void %extinst prefetch %ptr %u32_256
+)";
+
+  CompileSuccessfully(GenerateKernelCode(body));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("OpenCL.std prefetch: expected Ptr data type to be int "
+                        "or float scalar or vector"));
+}
+
+TEST_F(ValidateExtInst, OpenCLStdPrefetchAddressingModelLogical) {
+  const std::string body = R"(
+%ptr = OpAccessChain %u32_ptr_cross_workgroup %u32arr_cross_workgroup %u32_0
+%val1 = OpExtInst %void %extinst prefetch %ptr %u32_256
+)";
+
+  CompileSuccessfully(GenerateKernelCode(body, "", "Logical"));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("OpenCL.std prefetch can only be used with physical "
+                        "addressing models"));
+}
+
+TEST_F(ValidateExtInst, OpenCLStdPrefetchNumElementsNotSizeT) {
+  const std::string body = R"(
+%ptr = OpAccessChain %f32_ptr_cross_workgroup %f32arr_cross_workgroup %u32_0
+%val1 = OpExtInst %void %extinst prefetch %ptr %u32_256
+)";
+
+  CompileSuccessfully(GenerateKernelCode(body, "", "Physical64"));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("OpenCL.std prefetch: expected operand Num Elements to "
+                        "be of type size_t (64-bit integer for the addressing "
+                        "model used in the module)"));
+}
+
+TEST_P(ValidateOpenCLStdFractLike, Success) {
+  const std::string ext_inst_name = GetParam();
+  std::ostringstream ss;
+  ss << "%var_f32 = OpVariable %f32_ptr_function Function\n";
+  ss << "%var_f32vec2 = OpVariable %f32vec2_ptr_function Function\n";
+  ss << "%val1 = OpExtInst %f32 %extinst " << ext_inst_name
+     << " %f32_0 %var_f32\n";
+  ss << "%val2 = OpExtInst %f32vec2 %extinst " << ext_inst_name
+     << " %f32vec2_01 %var_f32vec2\n";
+
+  CompileSuccessfully(GenerateKernelCode(ss.str()));
+  ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+TEST_P(ValidateOpenCLStdFractLike, IntResultType) {
+  const std::string ext_inst_name = GetParam();
+  std::ostringstream ss;
+  ss << "%var_f32 = OpVariable %f32_ptr_function Function\n";
+  ss << "%val1 = OpExtInst %u32 %extinst " << ext_inst_name
+     << " %f32_0 %var_f32\n";
+
+  CompileSuccessfully(GenerateKernelCode(ss.str()));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("OpenCL.std " + ext_inst_name +
+                ": expected Result Type to be a float scalar or vector type"));
+}
+
+TEST_P(ValidateOpenCLStdFractLike, XWrongType) {
+  const std::string ext_inst_name = GetParam();
+  std::ostringstream ss;
+  ss << "%var_f32 = OpVariable %f32_ptr_function Function\n";
+  ss << "%val1 = OpExtInst %f32 %extinst " << ext_inst_name
+     << " %f64_0 %var_f32\n";
+
+  CompileSuccessfully(GenerateKernelCode(ss.str()));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("OpenCL.std " + ext_inst_name +
+                ": expected type of operand X to be equal to Result Type"));
+}
+
+TEST_P(ValidateOpenCLStdFractLike, NotPointer) {
+  const std::string ext_inst_name = GetParam();
+  std::ostringstream ss;
+  ss << "%var_f32 = OpVariable %f32_ptr_function Function\n";
+  ss << "%val1 = OpExtInst %f32 %extinst " << ext_inst_name
+     << " %f32_0 %f32_1\n";
+
+  CompileSuccessfully(GenerateKernelCode(ss.str()));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("OpenCL.std " + ext_inst_name +
+                        ": expected the last operand to be a pointer"));
+}
+
+TEST_P(ValidateOpenCLStdFractLike, PointerInvalidStorageClass) {
+  const std::string ext_inst_name = GetParam();
+  std::ostringstream ss;
+  ss << "%ptr = OpAccessChain %f32_ptr_uniform_constant "
+        "%f32vec8_uniform_constant %u32_1\n";
+  ss << "%val1 = OpExtInst %f32 %extinst " << ext_inst_name << " %f32_0 %ptr\n";
+
+  CompileSuccessfully(GenerateKernelCode(ss.str()));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("OpenCL.std " + ext_inst_name +
+                        ": expected storage class of the pointer to be "
+                        "Generic, CrossWorkgroup, Workgroup or Function"));
+}
+
+TEST_P(ValidateOpenCLStdFractLike, PointerWrongDataType) {
+  const std::string ext_inst_name = GetParam();
+  std::ostringstream ss;
+  ss << "%var_u32 = OpVariable %u32_ptr_function Function\n";
+  ss << "%val1 = OpExtInst %f32 %extinst " << ext_inst_name
+     << " %f32_0 %var_u32\n";
+
+  CompileSuccessfully(GenerateKernelCode(ss.str()));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr(
+          "OpenCL.std " + ext_inst_name +
+          ": expected data type of the pointer to be equal to Result Type"));
+}
+
+INSTANTIATE_TEST_CASE_P(AllFractLike, ValidateOpenCLStdFractLike,
+                        ::testing::ValuesIn(std::vector<std::string>{
+                            "fract",
+                            "modf",
+                            "sincos",
+                        }), );
+
+TEST_F(ValidateExtInst, OpenCLStdRemquoSuccess) {
+  const std::string body = R"(
+%var_f32 = OpVariable %f32_ptr_function Function
+%var_f32vec2 = OpVariable %f32vec2_ptr_function Function
+%val1 = OpExtInst %f32 %extinst remquo %f32_3 %f32_2 %var_f32
+%val2 = OpExtInst %f32vec2 %extinst remquo %f32vec2_01 %f32vec2_12 %var_f32vec2
+)";
+
+  CompileSuccessfully(GenerateKernelCode(body));
+  ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+TEST_F(ValidateExtInst, OpenCLStdRemquoIntResultType) {
+  const std::string body = R"(
+%var_f32 = OpVariable %f32_ptr_function Function
+%val1 = OpExtInst %u32 %extinst remquo %f32_3 %f32_2 %var_f32
+)";
+
+  CompileSuccessfully(GenerateKernelCode(body));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("OpenCL.std remquo: "
+                "expected Result Type to be a float scalar or vector type"));
+}
+
+TEST_F(ValidateExtInst, OpenCLStdRemquoXWrongType) {
+  const std::string body = R"(
+%var_f32 = OpVariable %f32_ptr_function Function
+%val1 = OpExtInst %f32 %extinst remquo %u32_3 %f32_2 %var_f32
+)";
+
+  CompileSuccessfully(GenerateKernelCode(body));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("OpenCL.std remquo: "
+                "expected type of operand X to be equal to Result Type"));
+}
+
+TEST_F(ValidateExtInst, OpenCLStdRemquoYWrongType) {
+  const std::string body = R"(
+%var_f32 = OpVariable %f32_ptr_function Function
+%val1 = OpExtInst %f32 %extinst remquo %f32_3 %u32_2 %var_f32
+)";
+
+  CompileSuccessfully(GenerateKernelCode(body));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("OpenCL.std remquo: "
+                "expected type of operand Y to be equal to Result Type"));
+}
+
+TEST_F(ValidateExtInst, OpenCLStdRemquoNotPointer) {
+  const std::string body = R"(
+%val1 = OpExtInst %f32 %extinst remquo %f32_3 %f32_2 %f32_1
+)";
+
+  CompileSuccessfully(GenerateKernelCode(body));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("OpenCL.std remquo: "
+                        "expected the last operand to be a pointer"));
+}
+
+TEST_F(ValidateExtInst, OpenCLStdRemquoPointerWrongStorageClass) {
+  const std::string body = R"(
+%ptr = OpAccessChain %f32_ptr_uniform_constant %f32vec8_uniform_constant %u32_1
+%val1 = OpExtInst %f32 %extinst remquo %f32_3 %f32_2 %ptr
+)";
+
+  CompileSuccessfully(GenerateKernelCode(body));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("OpenCL.std remquo: "
+                        "expected storage class of the pointer to be Generic, "
+                        "CrossWorkgroup, Workgroup or Function"));
+}
+
+TEST_F(ValidateExtInst, OpenCLStdRemquoPointerWrongDataType) {
+  const std::string body = R"(
+%var_u32 = OpVariable %u32_ptr_function Function
+%val1 = OpExtInst %f32 %extinst remquo %f32_3 %f32_2 %var_u32
+)";
+
+  CompileSuccessfully(GenerateKernelCode(body));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr(
+          "OpenCL.std remquo: "
+          "expected data type of the pointer to be equal to Result Type"));
+}
+
+TEST_P(ValidateOpenCLStdFrexpLike, Success) {
+  const std::string ext_inst_name = GetParam();
+  std::ostringstream ss;
+  ss << "%var_u32 = OpVariable %u32_ptr_function Function\n";
+  ss << "%var_u32vec2 = OpVariable %u32vec2_ptr_function Function\n";
+  ss << "%val1 = OpExtInst %f32 %extinst " << ext_inst_name
+     << " %f32_0 %var_u32\n";
+  ss << "%val2 = OpExtInst %f32vec2 %extinst " << ext_inst_name
+     << " %f32vec2_01 %var_u32vec2\n";
+
+  CompileSuccessfully(GenerateKernelCode(ss.str()));
+  ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+TEST_P(ValidateOpenCLStdFrexpLike, IntResultType) {
+  const std::string ext_inst_name = GetParam();
+  std::ostringstream ss;
+  ss << "%var_u32 = OpVariable %u32_ptr_function Function\n";
+  ss << "%val1 = OpExtInst %u32 %extinst " << ext_inst_name
+     << " %f32_0 %var_u32\n";
+
+  CompileSuccessfully(GenerateKernelCode(ss.str()));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("OpenCL.std " + ext_inst_name +
+                ": expected Result Type to be a float scalar or vector type"));
+}
+
+TEST_P(ValidateOpenCLStdFrexpLike, XWrongType) {
+  const std::string ext_inst_name = GetParam();
+  std::ostringstream ss;
+  ss << "%var_u32 = OpVariable %u32_ptr_function Function\n";
+  ss << "%val1 = OpExtInst %f32 %extinst " << ext_inst_name
+     << " %f64_0 %var_u32\n";
+
+  CompileSuccessfully(GenerateKernelCode(ss.str()));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("OpenCL.std " + ext_inst_name +
+                ": expected type of operand X to be equal to Result Type"));
+}
+
+TEST_P(ValidateOpenCLStdFrexpLike, NotPointer) {
+  const std::string ext_inst_name = GetParam();
+  std::ostringstream ss;
+  ss << "%val1 = OpExtInst %f32 %extinst " << ext_inst_name
+     << " %f32_0 %u32_1\n";
+
+  CompileSuccessfully(GenerateKernelCode(ss.str()));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("OpenCL.std " + ext_inst_name +
+                        ": expected the last operand to be a pointer"));
+}
+
+TEST_P(ValidateOpenCLStdFrexpLike, PointerInvalidStorageClass) {
+  const std::string ext_inst_name = GetParam();
+  std::ostringstream ss;
+  ss << "%ptr = OpAccessChain %f32_ptr_uniform_constant "
+        "%f32vec8_uniform_constant %u32_1\n";
+  ss << "%val1 = OpExtInst %f32 %extinst " << ext_inst_name << " %f32_0 %ptr\n";
+
+  CompileSuccessfully(GenerateKernelCode(ss.str()));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("OpenCL.std " + ext_inst_name +
+                        ": expected storage class of the pointer to be "
+                        "Generic, CrossWorkgroup, Workgroup or Function"));
+}
+
+TEST_P(ValidateOpenCLStdFrexpLike, PointerDataTypeFloat) {
+  const std::string ext_inst_name = GetParam();
+  std::ostringstream ss;
+  ss << "%var_f32 = OpVariable %f32_ptr_function Function\n";
+  ss << "%val1 = OpExtInst %f32 %extinst " << ext_inst_name
+     << " %f32_0 %var_f32\n";
+
+  CompileSuccessfully(GenerateKernelCode(ss.str()));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("OpenCL.std " + ext_inst_name +
+                        ": expected data type of the pointer to be a 32-bit "
+                        "int scalar or vector type"));
+}
+
+TEST_P(ValidateOpenCLStdFrexpLike, PointerDataTypeU64) {
+  const std::string ext_inst_name = GetParam();
+  std::ostringstream ss;
+  ss << "%var_u64 = OpVariable %u64_ptr_function Function\n";
+  ss << "%val1 = OpExtInst %f32 %extinst " << ext_inst_name
+     << " %f32_0 %var_u64\n";
+
+  CompileSuccessfully(GenerateKernelCode(ss.str()));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("OpenCL.std " + ext_inst_name +
+                        ": expected data type of the pointer to be a 32-bit "
+                        "int scalar or vector type"));
+}
+
+TEST_P(ValidateOpenCLStdFrexpLike, PointerDataTypeDiffSize) {
+  const std::string ext_inst_name = GetParam();
+  std::ostringstream ss;
+  ss << "%var_u32 = OpVariable %u32_ptr_function Function\n";
+  ss << "%val1 = OpExtInst %f32vec2 %extinst " << ext_inst_name
+     << " %f32vec2_01 %var_u32\n";
+
+  CompileSuccessfully(GenerateKernelCode(ss.str()));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("OpenCL.std " + ext_inst_name +
+                        ": expected data type of the pointer to have the same "
+                        "number of components as Result Type"));
+}
+
+INSTANTIATE_TEST_CASE_P(AllFrexpLike, ValidateOpenCLStdFrexpLike,
+                        ::testing::ValuesIn(std::vector<std::string>{
+                            "frexp",
+                            "lgamma_r",
+                        }), );
+
+TEST_F(ValidateExtInst, OpenCLStdIlogbSuccess) {
+  const std::string body = R"(
+%val1 = OpExtInst %u32 %extinst ilogb %f32_3
+%val2 = OpExtInst %u32vec2 %extinst ilogb %f32vec2_12
+)";
+
+  CompileSuccessfully(GenerateKernelCode(body));
+  ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+TEST_F(ValidateExtInst, OpenCLStdIlogbFloatResultType) {
+  const std::string body = R"(
+%val1 = OpExtInst %f32 %extinst ilogb %f32_3
+)";
+
+  CompileSuccessfully(GenerateKernelCode(body));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr(
+          "OpenCL.std ilogb: "
+          "expected Result Type to be a 32-bit int scalar or vector type"));
+}
+
+TEST_F(ValidateExtInst, OpenCLStdIlogbIntX) {
+  const std::string body = R"(
+%val1 = OpExtInst %u32 %extinst ilogb %u32_3
+)";
+
+  CompileSuccessfully(GenerateKernelCode(body));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("OpenCL.std ilogb: "
+                        "expected operand X to be a float scalar or vector"));
+}
+
+TEST_F(ValidateExtInst, OpenCLStdIlogbDiffSize) {
+  const std::string body = R"(
+%val2 = OpExtInst %u32vec2 %extinst ilogb %f32_1
+)";
+
+  CompileSuccessfully(GenerateKernelCode(body));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("OpenCL.std ilogb: "
+                        "expected operand X to have the same number of "
+                        "components as Result Type"));
+}
+
+TEST_F(ValidateExtInst, OpenCLStdNanSuccess) {
+  const std::string body = R"(
+%val1 = OpExtInst %f32 %extinst nan %u32_3
+%val2 = OpExtInst %f32vec2 %extinst nan %u32vec2_12
+)";
+
+  CompileSuccessfully(GenerateKernelCode(body));
+  ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+TEST_F(ValidateExtInst, OpenCLStdNanIntResultType) {
+  const std::string body = R"(
+%val1 = OpExtInst %u32 %extinst nan %u32_3
+)";
+
+  CompileSuccessfully(GenerateKernelCode(body));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("OpenCL.std nan: "
+                "expected Result Type to be a float scalar or vector type"));
+}
+
+TEST_F(ValidateExtInst, OpenCLStdNanFloatNancode) {
+  const std::string body = R"(
+%val1 = OpExtInst %f32 %extinst nan %f32_3
+)";
+
+  CompileSuccessfully(GenerateKernelCode(body));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("OpenCL.std nan: "
+                        "expected Nancode to be an int scalar or vector type"));
+}
+
+TEST_F(ValidateExtInst, OpenCLStdNanFloatDiffSize) {
+  const std::string body = R"(
+%val1 = OpExtInst %f32 %extinst nan %u32vec2_12
+)";
+
+  CompileSuccessfully(GenerateKernelCode(body));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("OpenCL.std nan: "
+                        "expected Nancode to have the same number of "
+                        "components as Result Type"));
+}
+
+TEST_F(ValidateExtInst, OpenCLStdNanFloatDiffBitWidth) {
+  const std::string body = R"(
+%val1 = OpExtInst %f64 %extinst nan %u32_2
+)";
+
+  CompileSuccessfully(GenerateKernelCode(body));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("OpenCL.std nan: "
+                "expected Nancode to have the same bit width as Result Type"));
+}
+
+TEST_P(ValidateOpenCLStdLdexpLike, Success) {
+  const std::string ext_inst_name = GetParam();
+  std::ostringstream ss;
+  ss << "%val1 = OpExtInst %f32 %extinst " << ext_inst_name
+     << " %f32_0 %u32_1\n";
+  ss << "%val2 = OpExtInst %f32vec2 %extinst " << ext_inst_name
+     << " %f32vec2_12 %u32vec2_12\n";
+
+  CompileSuccessfully(GenerateKernelCode(ss.str()));
+  ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+TEST_P(ValidateOpenCLStdLdexpLike, IntResultType) {
+  const std::string ext_inst_name = GetParam();
+  std::ostringstream ss;
+  ss << "%val1 = OpExtInst %u32 %extinst " << ext_inst_name
+     << " %f32_0 %u32_1\n";
+
+  CompileSuccessfully(GenerateKernelCode(ss.str()));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("OpenCL.std " + ext_inst_name +
+                ": expected Result Type to be a float scalar or vector type"));
+}
+
+TEST_P(ValidateOpenCLStdLdexpLike, XWrongType) {
+  const std::string ext_inst_name = GetParam();
+  std::ostringstream ss;
+  ss << "%val1 = OpExtInst %f32 %extinst " << ext_inst_name
+     << " %u32_0 %u32_1\n";
+
+  CompileSuccessfully(GenerateKernelCode(ss.str()));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("OpenCL.std " + ext_inst_name +
+                ": expected type of operand X to be equal to Result Type"));
+}
+
+TEST_P(ValidateOpenCLStdLdexpLike, ExponentNotInt) {
+  const std::string ext_inst_name = GetParam();
+  std::ostringstream ss;
+  ss << "%val1 = OpExtInst %f32 %extinst " << ext_inst_name
+     << " %f32_0 %f32_1\n";
+
+  CompileSuccessfully(GenerateKernelCode(ss.str()));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("OpenCL.std " + ext_inst_name +
+                ": expected the exponent to be a 32-bit int scalar or vector"));
+}
+
+TEST_P(ValidateOpenCLStdLdexpLike, ExponentNotInt32) {
+  const std::string ext_inst_name = GetParam();
+  std::ostringstream ss;
+  ss << "%val1 = OpExtInst %f32 %extinst " << ext_inst_name
+     << " %f32_0 %u64_1\n";
+
+  CompileSuccessfully(GenerateKernelCode(ss.str()));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("OpenCL.std " + ext_inst_name +
+                ": expected the exponent to be a 32-bit int scalar or vector"));
+}
+
+TEST_P(ValidateOpenCLStdLdexpLike, ExponentWrongSize) {
+  const std::string ext_inst_name = GetParam();
+  std::ostringstream ss;
+  ss << "%val1 = OpExtInst %f32 %extinst " << ext_inst_name
+     << " %f32_0 %u32vec2_01\n";
+
+  CompileSuccessfully(GenerateKernelCode(ss.str()));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("OpenCL.std " + ext_inst_name +
+                        ": expected the exponent to have the same number of "
+                        "components as Result Type"));
+}
+
+INSTANTIATE_TEST_CASE_P(AllLdexpLike, ValidateOpenCLStdLdexpLike,
+                        ::testing::ValuesIn(std::vector<std::string>{
+                            "ldexp",
+                            "pown",
+                            "rootn",
+                        }), );
+
+TEST_P(ValidateOpenCLStdUpsampleLike, Success) {
+  const std::string ext_inst_name = GetParam();
+  std::ostringstream ss;
+  ss << "%val1 = OpExtInst %u16 %extinst " << ext_inst_name << " %u8_1 %u8_2\n";
+  ss << "%val2 = OpExtInst %u32 %extinst " << ext_inst_name
+     << " %u16_1 %u16_2\n";
+  ss << "%val3 = OpExtInst %u64 %extinst " << ext_inst_name
+     << " %u32_1 %u32_2\n";
+  ss << "%val4 = OpExtInst %u64vec2 %extinst " << ext_inst_name
+     << " %u32vec2_01 %u32vec2_01\n";
+
+  CompileSuccessfully(GenerateKernelCode(ss.str()));
+  ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+TEST_P(ValidateOpenCLStdUpsampleLike, FloatResultType) {
+  const std::string ext_inst_name = GetParam();
+  std::ostringstream ss;
+  ss << "%val1 = OpExtInst %f64 %extinst " << ext_inst_name
+     << " %u32_1 %u32_2\n";
+
+  CompileSuccessfully(GenerateKernelCode(ss.str()));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("OpenCL.std " + ext_inst_name +
+                ": expected Result Type to be an int scalar or vector type"));
+}
+
+TEST_P(ValidateOpenCLStdUpsampleLike, InvalidResultTypeBitWidth) {
+  const std::string ext_inst_name = GetParam();
+  std::ostringstream ss;
+  ss << "%val1 = OpExtInst %u8 %extinst " << ext_inst_name << " %u8_1 %u8_2\n";
+
+  CompileSuccessfully(GenerateKernelCode(ss.str()));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr(
+          "OpenCL.std " + ext_inst_name +
+          ": expected bit width of Result Type components to be 16, 32 or 64"));
+}
+
+TEST_P(ValidateOpenCLStdUpsampleLike, LoHiDiffType) {
+  const std::string ext_inst_name = GetParam();
+  std::ostringstream ss;
+  ss << "%val1 = OpExtInst %u64 %extinst " << ext_inst_name
+     << " %u32_1 %u16_2\n";
+
+  CompileSuccessfully(GenerateKernelCode(ss.str()));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("OpenCL.std " + ext_inst_name +
+                        ": expected Hi and Lo operands to have the same type"));
+}
+
+TEST_P(ValidateOpenCLStdUpsampleLike, DiffNumberOfComponents) {
+  const std::string ext_inst_name = GetParam();
+  std::ostringstream ss;
+  ss << "%val1 = OpExtInst %u64vec2 %extinst " << ext_inst_name
+     << " %u32_1 %u32_2\n";
+
+  CompileSuccessfully(GenerateKernelCode(ss.str()));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("OpenCL.std " + ext_inst_name +
+                        ": expected Hi and Lo operands to have the same number "
+                        "of components as Result Type"));
+}
+
+TEST_P(ValidateOpenCLStdUpsampleLike, HiLoWrongBitWidth) {
+  const std::string ext_inst_name = GetParam();
+  std::ostringstream ss;
+  ss << "%val1 = OpExtInst %u64 %extinst " << ext_inst_name
+     << " %u16_1 %u16_2\n";
+
+  CompileSuccessfully(GenerateKernelCode(ss.str()));
+  ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("OpenCL.std " + ext_inst_name +
+                ": expected bit width of components of Hi and Lo operands to "
+                "be half of the bit width of components of Result Type"));
+}
+
+INSTANTIATE_TEST_CASE_P(AllUpsampleLike, ValidateOpenCLStdUpsampleLike,
+                        ::testing::ValuesIn(std::vector<std::string>{
+                            "u_upsample",
+                            "s_upsample",
+                        }), );
+
 }  // anonymous namespace