SPIRV-Tools/source/val/validate_extensions.cpp
Greg Fischer 48007a5c7f
Add interpolate legalization pass (#4220)
This pass converts an internal form of GLSLstd450 Interpolate ops
to the externally valid form. The external form takes the lvalue
of the interpolant. The internal form can do a load of the interpolant.
The pass replaces the load with its pointer. The internal form is
generated by glslang and possibly other frontends for HLSL shaders.
The new pass is called as part of HLSL legalization after all
propagation is complete.

Also adds internal interpolate form to pre-legalization validation
2021-03-31 14:26:36 -04:00

3160 lines
123 KiB
C++

// Copyright (c) 2018 Google Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// Validates correctness of extension SPIR-V instructions.
#include <cstdlib>
#include <sstream>
#include <string>
#include <vector>
#include "spirv/unified1/NonSemanticClspvReflection.h"
#include "OpenCLDebugInfo100.h"
#include "source/diagnostic.h"
#include "source/enum_string_mapping.h"
#include "source/extensions.h"
#include "source/latest_version_glsl_std_450_header.h"
#include "source/latest_version_opencl_std_header.h"
#include "source/opcode.h"
#include "source/spirv_constant.h"
#include "source/spirv_target_env.h"
#include "source/val/instruction.h"
#include "source/val/validate.h"
#include "source/val/validation_state.h"
namespace spvtools {
namespace val {
namespace {
uint32_t GetSizeTBitWidth(const ValidationState_t& _) {
if (_.addressing_model() == SpvAddressingModelPhysical32) return 32;
if (_.addressing_model() == SpvAddressingModelPhysical64) return 64;
return 0;
}
// Check that the operand of a debug info instruction |inst| at |word_index|
// is a result id of an instruction with |expected_opcode|.
spv_result_t ValidateOperandForDebugInfo(
ValidationState_t& _, const std::string& operand_name,
SpvOp expected_opcode, const Instruction* inst, uint32_t word_index,
const std::function<std::string()>& ext_inst_name) {
auto* operand = _.FindDef(inst->word(word_index));
if (operand->opcode() != expected_opcode) {
spv_opcode_desc desc = nullptr;
if (_.grammar().lookupOpcode(expected_opcode, &desc) != SPV_SUCCESS ||
!desc) {
return _.diag(SPV_ERROR_INVALID_DATA, inst)
<< ext_inst_name() << ": "
<< "expected operand " << operand_name << " is invalid";
}
return _.diag(SPV_ERROR_INVALID_DATA, inst)
<< ext_inst_name() << ": "
<< "expected operand " << operand_name << " must be a result id of "
<< "Op" << desc->name;
}
return SPV_SUCCESS;
}
#define CHECK_OPERAND(NAME, opcode, index) \
do { \
auto result = ValidateOperandForDebugInfo(_, NAME, opcode, inst, index, \
ext_inst_name); \
if (result != SPV_SUCCESS) return result; \
} while (0)
// True if the operand of a debug info instruction |inst| at |word_index|
// satisifies |expectation| that is given as a function. Otherwise,
// returns false.
bool DoesDebugInfoOperandMatchExpectation(
const ValidationState_t& _,
const std::function<bool(OpenCLDebugInfo100Instructions)>& expectation,
const Instruction* inst, uint32_t word_index) {
if (inst->words().size() <= word_index) return false;
auto* debug_inst = _.FindDef(inst->word(word_index));
if (debug_inst->opcode() != SpvOpExtInst ||
debug_inst->ext_inst_type() != SPV_EXT_INST_TYPE_OPENCL_DEBUGINFO_100 ||
!expectation(OpenCLDebugInfo100Instructions(debug_inst->word(4)))) {
return false;
}
return true;
}
// Check that the operand of a debug info instruction |inst| at |word_index|
// is a result id of an debug info instruction whose debug instruction type
// is |expected_debug_inst|.
spv_result_t ValidateDebugInfoOperand(
ValidationState_t& _, const std::string& debug_inst_name,
OpenCLDebugInfo100Instructions expected_debug_inst, const Instruction* inst,
uint32_t word_index, const std::function<std::string()>& ext_inst_name) {
std::function<bool(OpenCLDebugInfo100Instructions)> expectation =
[expected_debug_inst](OpenCLDebugInfo100Instructions dbg_inst) {
return dbg_inst == expected_debug_inst;
};
if (DoesDebugInfoOperandMatchExpectation(_, expectation, inst, word_index))
return SPV_SUCCESS;
spv_ext_inst_desc desc = nullptr;
_.grammar().lookupExtInst(SPV_EXT_INST_TYPE_OPENCL_DEBUGINFO_100,
expected_debug_inst, &desc);
if (_.grammar().lookupExtInst(SPV_EXT_INST_TYPE_OPENCL_DEBUGINFO_100,
expected_debug_inst, &desc) != SPV_SUCCESS ||
!desc) {
return _.diag(SPV_ERROR_INVALID_DATA, inst)
<< ext_inst_name() << ": "
<< "expected operand " << debug_inst_name << " is invalid";
}
return _.diag(SPV_ERROR_INVALID_DATA, inst)
<< ext_inst_name() << ": "
<< "expected operand " << debug_inst_name << " must be a result id of "
<< desc->name;
}
#define CHECK_DEBUG_OPERAND(NAME, debug_opcode, index) \
do { \
auto result = ValidateDebugInfoOperand(_, NAME, debug_opcode, inst, index, \
ext_inst_name); \
if (result != SPV_SUCCESS) return result; \
} while (0)
// Check that the operand of a debug info instruction |inst| at |word_index|
// is a result id of an debug info instruction with DebugTypeBasic.
spv_result_t ValidateOperandBaseType(
ValidationState_t& _, const Instruction* inst, uint32_t word_index,
const std::function<std::string()>& ext_inst_name) {
return ValidateDebugInfoOperand(_, "Base Type",
OpenCLDebugInfo100DebugTypeBasic, inst,
word_index, ext_inst_name);
}
// Check that the operand of a debug info instruction |inst| at |word_index|
// is a result id of a debug lexical scope instruction which is one of
// DebugCompilationUnit, DebugFunction, DebugLexicalBlock, or
// DebugTypeComposite.
spv_result_t ValidateOperandLexicalScope(
ValidationState_t& _, const std::string& debug_inst_name,
const Instruction* inst, uint32_t word_index,
const std::function<std::string()>& ext_inst_name) {
std::function<bool(OpenCLDebugInfo100Instructions)> expectation =
[](OpenCLDebugInfo100Instructions dbg_inst) {
return dbg_inst == OpenCLDebugInfo100DebugCompilationUnit ||
dbg_inst == OpenCLDebugInfo100DebugFunction ||
dbg_inst == OpenCLDebugInfo100DebugLexicalBlock ||
dbg_inst == OpenCLDebugInfo100DebugTypeComposite;
};
if (DoesDebugInfoOperandMatchExpectation(_, expectation, inst, word_index))
return SPV_SUCCESS;
return _.diag(SPV_ERROR_INVALID_DATA, inst)
<< ext_inst_name() << ": "
<< "expected operand " << debug_inst_name
<< " must be a result id of a lexical scope";
}
// Check that the operand of a debug info instruction |inst| at |word_index|
// is a result id of a debug type instruction (See DebugTypeXXX in
// "4.3. Type instructions" section of OpenCL.DebugInfo.100 spec.
spv_result_t ValidateOperandDebugType(
ValidationState_t& _, const std::string& debug_inst_name,
const Instruction* inst, uint32_t word_index,
const std::function<std::string()>& ext_inst_name,
bool allow_template_param) {
std::function<bool(OpenCLDebugInfo100Instructions)> expectation =
[&allow_template_param](OpenCLDebugInfo100Instructions dbg_inst) {
if (allow_template_param &&
(dbg_inst == OpenCLDebugInfo100DebugTypeTemplateParameter ||
dbg_inst ==
OpenCLDebugInfo100DebugTypeTemplateTemplateParameter)) {
return true;
}
return OpenCLDebugInfo100DebugTypeBasic <= dbg_inst &&
dbg_inst <= OpenCLDebugInfo100DebugTypeTemplate;
};
if (DoesDebugInfoOperandMatchExpectation(_, expectation, inst, word_index))
return SPV_SUCCESS;
return _.diag(SPV_ERROR_INVALID_DATA, inst)
<< ext_inst_name() << ": "
<< "expected operand " << debug_inst_name
<< " is not a valid debug type";
}
bool IsUint32Constant(ValidationState_t& _, uint32_t id) {
auto inst = _.FindDef(id);
if (!inst || inst->opcode() != SpvOpConstant) {
return false;
}
auto type = _.FindDef(inst->type_id());
if (!type || type->opcode() != SpvOpTypeInt) {
return false;
}
if (type->GetOperandAs<uint32_t>(1) != 32) {
return false;
}
if (type->GetOperandAs<uint32_t>(2) != 0) {
return false;
}
return true;
}
spv_result_t ValidateClspvReflectionKernel(ValidationState_t& _,
const Instruction* inst) {
const auto kernel_id = inst->GetOperandAs<uint32_t>(4);
const auto kernel = _.FindDef(kernel_id);
if (kernel->opcode() != SpvOpFunction) {
return _.diag(SPV_ERROR_INVALID_ID, inst)
<< "Kernel does not reference a function";
}
bool found_kernel = false;
for (auto entry_point : _.entry_points()) {
if (entry_point == kernel_id) {
found_kernel = true;
break;
}
}
if (!found_kernel) {
return _.diag(SPV_ERROR_INVALID_ID, inst)
<< "Kernel does not reference an entry-point";
}
const auto* exec_models = _.GetExecutionModels(kernel_id);
if (!exec_models || exec_models->empty()) {
return _.diag(SPV_ERROR_INVALID_ID, inst)
<< "Kernel does not reference an entry-point";
}
for (auto exec_model : *exec_models) {
if (exec_model != SpvExecutionModelGLCompute) {
return _.diag(SPV_ERROR_INVALID_ID, inst)
<< "Kernel must refer only to GLCompute entry-points";
}
}
auto name = _.FindDef(inst->GetOperandAs<uint32_t>(5));
if (!name || name->opcode() != SpvOpString) {
return _.diag(SPV_ERROR_INVALID_ID, inst) << "Name must be an OpString";
}
const std::string name_str = reinterpret_cast<const char*>(
name->words().data() + name->operands()[1].offset);
bool found = false;
for (auto& desc : _.entry_point_descriptions(kernel_id)) {
if (name_str == desc.name) {
found = true;
break;
}
}
if (!found) {
return _.diag(SPV_ERROR_INVALID_ID, inst)
<< "Name must match an entry-point for Kernel";
}
return SPV_SUCCESS;
}
spv_result_t ValidateClspvReflectionArgumentInfo(ValidationState_t& _,
const Instruction* inst) {
const auto num_operands = inst->operands().size();
if (_.GetIdOpcode(inst->GetOperandAs<uint32_t>(4)) != SpvOpString) {
return _.diag(SPV_ERROR_INVALID_ID, inst) << "Name must be an OpString";
}
if (num_operands > 5) {
if (_.GetIdOpcode(inst->GetOperandAs<uint32_t>(5)) != SpvOpString) {
return _.diag(SPV_ERROR_INVALID_ID, inst)
<< "TypeName must be an OpString";
}
}
if (num_operands > 6) {
if (!IsUint32Constant(_, inst->GetOperandAs<uint32_t>(6))) {
return _.diag(SPV_ERROR_INVALID_ID, inst)
<< "AddressQualifier must be a 32-bit unsigned integer "
"OpConstant";
}
}
if (num_operands > 7) {
if (!IsUint32Constant(_, inst->GetOperandAs<uint32_t>(7))) {
return _.diag(SPV_ERROR_INVALID_ID, inst)
<< "AccessQualifier must be a 32-bit unsigned integer "
"OpConstant";
}
}
if (num_operands > 8) {
if (!IsUint32Constant(_, inst->GetOperandAs<uint32_t>(8))) {
return _.diag(SPV_ERROR_INVALID_ID, inst)
<< "TypeQualifier must be a 32-bit unsigned integer "
"OpConstant";
}
}
return SPV_SUCCESS;
}
spv_result_t ValidateKernelDecl(ValidationState_t& _, const Instruction* inst) {
const auto decl_id = inst->GetOperandAs<uint32_t>(4);
const auto decl = _.FindDef(decl_id);
if (!decl || decl->opcode() != SpvOpExtInst) {
return _.diag(SPV_ERROR_INVALID_ID, inst)
<< "Kernel must be a Kernel extended instruction";
}
if (decl->GetOperandAs<uint32_t>(2) != inst->GetOperandAs<uint32_t>(2)) {
return _.diag(SPV_ERROR_INVALID_ID, inst)
<< "Kernel must be from the same extended instruction import";
}
const auto ext_inst =
decl->GetOperandAs<NonSemanticClspvReflectionInstructions>(3);
if (ext_inst != NonSemanticClspvReflectionKernel) {
return _.diag(SPV_ERROR_INVALID_ID, inst)
<< "Kernel must be a Kernel extended instruction";
}
return SPV_SUCCESS;
}
spv_result_t ValidateArgInfo(ValidationState_t& _, const Instruction* inst,
uint32_t info_index) {
auto info = _.FindDef(inst->GetOperandAs<uint32_t>(info_index));
if (!info || info->opcode() != SpvOpExtInst) {
return _.diag(SPV_ERROR_INVALID_ID, inst)
<< "ArgInfo must be an ArgumentInfo extended instruction";
}
if (info->GetOperandAs<uint32_t>(2) != inst->GetOperandAs<uint32_t>(2)) {
return _.diag(SPV_ERROR_INVALID_ID, inst)
<< "ArgInfo must be from the same extended instruction import";
}
auto ext_inst = info->GetOperandAs<NonSemanticClspvReflectionInstructions>(3);
if (ext_inst != NonSemanticClspvReflectionArgumentInfo) {
return _.diag(SPV_ERROR_INVALID_ID, inst)
<< "ArgInfo must be an ArgumentInfo extended instruction";
}
return SPV_SUCCESS;
}
spv_result_t ValidateClspvReflectionArgumentBuffer(ValidationState_t& _,
const Instruction* inst) {
const auto num_operands = inst->operands().size();
if (auto error = ValidateKernelDecl(_, inst)) {
return error;
}
if (!IsUint32Constant(_, inst->GetOperandAs<uint32_t>(5))) {
return _.diag(SPV_ERROR_INVALID_ID, inst)
<< "Ordinal must be a 32-bit unsigned integer OpConstant";
}
if (!IsUint32Constant(_, inst->GetOperandAs<uint32_t>(6))) {
return _.diag(SPV_ERROR_INVALID_ID, inst)
<< "DescriptorSet must be a 32-bit unsigned integer OpConstant";
}
if (!IsUint32Constant(_, inst->GetOperandAs<uint32_t>(7))) {
return _.diag(SPV_ERROR_INVALID_ID, inst)
<< "Binding must be a 32-bit unsigned integer OpConstant";
}
if (num_operands == 9) {
if (auto error = ValidateArgInfo(_, inst, 8)) {
return error;
}
}
return SPV_SUCCESS;
}
spv_result_t ValidateClspvReflectionArgumentPodBuffer(ValidationState_t& _,
const Instruction* inst) {
const auto num_operands = inst->operands().size();
if (auto error = ValidateKernelDecl(_, inst)) {
return error;
}
if (!IsUint32Constant(_, inst->GetOperandAs<uint32_t>(5))) {
return _.diag(SPV_ERROR_INVALID_ID, inst)
<< "Ordinal must be a 32-bit unsigned integer OpConstant";
}
if (!IsUint32Constant(_, inst->GetOperandAs<uint32_t>(6))) {
return _.diag(SPV_ERROR_INVALID_ID, inst)
<< "DescriptorSet must be a 32-bit unsigned integer OpConstant";
}
if (!IsUint32Constant(_, inst->GetOperandAs<uint32_t>(7))) {
return _.diag(SPV_ERROR_INVALID_ID, inst)
<< "Binding must be a 32-bit unsigned integer OpConstant";
}
if (!IsUint32Constant(_, inst->GetOperandAs<uint32_t>(8))) {
return _.diag(SPV_ERROR_INVALID_ID, inst)
<< "Offset must be a 32-bit unsigned integer OpConstant";
}
if (!IsUint32Constant(_, inst->GetOperandAs<uint32_t>(9))) {
return _.diag(SPV_ERROR_INVALID_ID, inst)
<< "Size must be a 32-bit unsigned integer OpConstant";
}
if (num_operands == 11) {
if (auto error = ValidateArgInfo(_, inst, 10)) {
return error;
}
}
return SPV_SUCCESS;
}
spv_result_t ValidateClspvReflectionArgumentPodPushConstant(
ValidationState_t& _, const Instruction* inst) {
const auto num_operands = inst->operands().size();
if (auto error = ValidateKernelDecl(_, inst)) {
return error;
}
if (!IsUint32Constant(_, inst->GetOperandAs<uint32_t>(5))) {
return _.diag(SPV_ERROR_INVALID_ID, inst)
<< "Ordinal must be a 32-bit unsigned integer OpConstant";
}
if (!IsUint32Constant(_, inst->GetOperandAs<uint32_t>(6))) {
return _.diag(SPV_ERROR_INVALID_ID, inst)
<< "Offset must be a 32-bit unsigned integer OpConstant";
}
if (!IsUint32Constant(_, inst->GetOperandAs<uint32_t>(7))) {
return _.diag(SPV_ERROR_INVALID_ID, inst)
<< "Size must be a 32-bit unsigned integer OpConstant";
}
if (num_operands == 9) {
if (auto error = ValidateArgInfo(_, inst, 8)) {
return error;
}
}
return SPV_SUCCESS;
}
spv_result_t ValidateClspvReflectionArgumentWorkgroup(ValidationState_t& _,
const Instruction* inst) {
const auto num_operands = inst->operands().size();
if (auto error = ValidateKernelDecl(_, inst)) {
return error;
}
if (!IsUint32Constant(_, inst->GetOperandAs<uint32_t>(5))) {
return _.diag(SPV_ERROR_INVALID_ID, inst)
<< "Ordinal must be a 32-bit unsigned integer OpConstant";
}
if (!IsUint32Constant(_, inst->GetOperandAs<uint32_t>(6))) {
return _.diag(SPV_ERROR_INVALID_ID, inst)
<< "SpecId must be a 32-bit unsigned integer OpConstant";
}
if (!IsUint32Constant(_, inst->GetOperandAs<uint32_t>(7))) {
return _.diag(SPV_ERROR_INVALID_ID, inst)
<< "ElemSize must be a 32-bit unsigned integer OpConstant";
}
if (num_operands == 9) {
if (auto error = ValidateArgInfo(_, inst, 8)) {
return error;
}
}
return SPV_SUCCESS;
}
spv_result_t ValidateClspvReflectionSpecConstantTriple(
ValidationState_t& _, const Instruction* inst) {
if (!IsUint32Constant(_, inst->GetOperandAs<uint32_t>(4))) {
return _.diag(SPV_ERROR_INVALID_ID, inst)
<< "X must be a 32-bit unsigned integer OpConstant";
}
if (!IsUint32Constant(_, inst->GetOperandAs<uint32_t>(5))) {
return _.diag(SPV_ERROR_INVALID_ID, inst)
<< "Y must be a 32-bit unsigned integer OpConstant";
}
if (!IsUint32Constant(_, inst->GetOperandAs<uint32_t>(6))) {
return _.diag(SPV_ERROR_INVALID_ID, inst)
<< "Z must be a 32-bit unsigned integer OpConstant";
}
return SPV_SUCCESS;
}
spv_result_t ValidateClspvReflectionSpecConstantWorkDim(
ValidationState_t& _, const Instruction* inst) {
if (!IsUint32Constant(_, inst->GetOperandAs<uint32_t>(4))) {
return _.diag(SPV_ERROR_INVALID_ID, inst)
<< "Dim must be a 32-bit unsigned integer OpConstant";
}
return SPV_SUCCESS;
}
spv_result_t ValidateClspvReflectionPushConstant(ValidationState_t& _,
const Instruction* inst) {
if (!IsUint32Constant(_, inst->GetOperandAs<uint32_t>(4))) {
return _.diag(SPV_ERROR_INVALID_ID, inst)
<< "Offset must be a 32-bit unsigned integer OpConstant";
}
if (!IsUint32Constant(_, inst->GetOperandAs<uint32_t>(5))) {
return _.diag(SPV_ERROR_INVALID_ID, inst)
<< "Size must be a 32-bit unsigned integer OpConstant";
}
return SPV_SUCCESS;
}
spv_result_t ValidateClspvReflectionConstantData(ValidationState_t& _,
const Instruction* inst) {
if (!IsUint32Constant(_, inst->GetOperandAs<uint32_t>(4))) {
return _.diag(SPV_ERROR_INVALID_ID, inst)
<< "DescriptorSet must be a 32-bit unsigned integer OpConstant";
}
if (!IsUint32Constant(_, inst->GetOperandAs<uint32_t>(5))) {
return _.diag(SPV_ERROR_INVALID_ID, inst)
<< "Binding must be a 32-bit unsigned integer OpConstant";
}
if (_.GetIdOpcode(inst->GetOperandAs<uint32_t>(6)) != SpvOpString) {
return _.diag(SPV_ERROR_INVALID_ID, inst) << "Data must be an OpString";
}
return SPV_SUCCESS;
}
spv_result_t ValidateClspvReflectionSampler(ValidationState_t& _,
const Instruction* inst) {
if (!IsUint32Constant(_, inst->GetOperandAs<uint32_t>(4))) {
return _.diag(SPV_ERROR_INVALID_ID, inst)
<< "DescriptorSet must be a 32-bit unsigned integer OpConstant";
}
if (!IsUint32Constant(_, inst->GetOperandAs<uint32_t>(5))) {
return _.diag(SPV_ERROR_INVALID_ID, inst)
<< "Binding must be a 32-bit unsigned integer OpConstant";
}
if (!IsUint32Constant(_, inst->GetOperandAs<uint32_t>(6))) {
return _.diag(SPV_ERROR_INVALID_ID, inst)
<< "Mask must be a 32-bit unsigned integer OpConstant";
}
return SPV_SUCCESS;
}
spv_result_t ValidateClspvReflectionPropertyRequiredWorkgroupSize(
ValidationState_t& _, const Instruction* inst) {
if (auto error = ValidateKernelDecl(_, inst)) {
return error;
}
if (!IsUint32Constant(_, inst->GetOperandAs<uint32_t>(5))) {
return _.diag(SPV_ERROR_INVALID_ID, inst)
<< "X must be a 32-bit unsigned integer OpConstant";
}
if (!IsUint32Constant(_, inst->GetOperandAs<uint32_t>(6))) {
return _.diag(SPV_ERROR_INVALID_ID, inst)
<< "Y must be a 32-bit unsigned integer OpConstant";
}
if (!IsUint32Constant(_, inst->GetOperandAs<uint32_t>(7))) {
return _.diag(SPV_ERROR_INVALID_ID, inst)
<< "Z must be a 32-bit unsigned integer OpConstant";
}
return SPV_SUCCESS;
}
spv_result_t ValidateClspvReflectionInstruction(ValidationState_t& _,
const Instruction* inst,
uint32_t /*version*/) {
if (!_.IsVoidType(inst->type_id())) {
return _.diag(SPV_ERROR_INVALID_ID, inst)
<< "Return Type must be OpTypeVoid";
}
auto ext_inst = inst->GetOperandAs<NonSemanticClspvReflectionInstructions>(3);
switch (ext_inst) {
case NonSemanticClspvReflectionKernel:
return ValidateClspvReflectionKernel(_, inst);
case NonSemanticClspvReflectionArgumentInfo:
return ValidateClspvReflectionArgumentInfo(_, inst);
case NonSemanticClspvReflectionArgumentStorageBuffer:
case NonSemanticClspvReflectionArgumentUniform:
case NonSemanticClspvReflectionArgumentSampledImage:
case NonSemanticClspvReflectionArgumentStorageImage:
case NonSemanticClspvReflectionArgumentSampler:
return ValidateClspvReflectionArgumentBuffer(_, inst);
case NonSemanticClspvReflectionArgumentPodStorageBuffer:
case NonSemanticClspvReflectionArgumentPodUniform:
return ValidateClspvReflectionArgumentPodBuffer(_, inst);
case NonSemanticClspvReflectionArgumentPodPushConstant:
return ValidateClspvReflectionArgumentPodPushConstant(_, inst);
case NonSemanticClspvReflectionArgumentWorkgroup:
return ValidateClspvReflectionArgumentWorkgroup(_, inst);
case NonSemanticClspvReflectionSpecConstantWorkgroupSize:
case NonSemanticClspvReflectionSpecConstantGlobalOffset:
return ValidateClspvReflectionSpecConstantTriple(_, inst);
case NonSemanticClspvReflectionSpecConstantWorkDim:
return ValidateClspvReflectionSpecConstantWorkDim(_, inst);
case NonSemanticClspvReflectionPushConstantGlobalOffset:
case NonSemanticClspvReflectionPushConstantEnqueuedLocalSize:
case NonSemanticClspvReflectionPushConstantGlobalSize:
case NonSemanticClspvReflectionPushConstantRegionOffset:
case NonSemanticClspvReflectionPushConstantNumWorkgroups:
case NonSemanticClspvReflectionPushConstantRegionGroupOffset:
return ValidateClspvReflectionPushConstant(_, inst);
case NonSemanticClspvReflectionConstantDataStorageBuffer:
case NonSemanticClspvReflectionConstantDataUniform:
return ValidateClspvReflectionConstantData(_, inst);
case NonSemanticClspvReflectionLiteralSampler:
return ValidateClspvReflectionSampler(_, inst);
case NonSemanticClspvReflectionPropertyRequiredWorkgroupSize:
return ValidateClspvReflectionPropertyRequiredWorkgroupSize(_, inst);
default:
break;
}
return SPV_SUCCESS;
}
bool IsConstIntScalarTypeWith32Or64Bits(ValidationState_t& _,
Instruction* instr) {
if (instr->opcode() != SpvOpConstant) return false;
if (!_.IsIntScalarType(instr->type_id())) return false;
uint32_t size_in_bits = _.GetBitWidth(instr->type_id());
return size_in_bits == 32 || size_in_bits == 64;
}
bool IsConstWithIntScalarType(ValidationState_t& _, const Instruction* inst,
uint32_t word_index) {
auto* int_scalar_const = _.FindDef(inst->word(word_index));
if (int_scalar_const->opcode() == SpvOpConstant &&
_.IsIntScalarType(int_scalar_const->type_id())) {
return true;
}
return false;
}
bool IsDebugVariableWithIntScalarType(ValidationState_t& _,
const Instruction* inst,
uint32_t word_index) {
auto* dbg_int_scalar_var = _.FindDef(inst->word(word_index));
if (OpenCLDebugInfo100Instructions(dbg_int_scalar_var->word(4)) ==
OpenCLDebugInfo100DebugLocalVariable ||
OpenCLDebugInfo100Instructions(dbg_int_scalar_var->word(4)) ==
OpenCLDebugInfo100DebugGlobalVariable) {
auto* dbg_type = _.FindDef(dbg_int_scalar_var->word(6));
if (OpenCLDebugInfo100Instructions(dbg_type->word(4)) ==
OpenCLDebugInfo100DebugTypeBasic &&
(OpenCLDebugInfo100DebugBaseTypeAttributeEncoding(dbg_type->word(7)) ==
OpenCLDebugInfo100Signed ||
OpenCLDebugInfo100DebugBaseTypeAttributeEncoding(dbg_type->word(7)) ==
OpenCLDebugInfo100Unsigned)) {
return true;
}
}
return false;
}
} // anonymous namespace
spv_result_t ValidateExtension(ValidationState_t& _, const Instruction* inst) {
if (_.version() < SPV_SPIRV_VERSION_WORD(1, 4)) {
std::string extension = GetExtensionString(&(inst->c_inst()));
if (extension ==
ExtensionToString(kSPV_KHR_workgroup_memory_explicit_layout)) {
return _.diag(SPV_ERROR_WRONG_VERSION, inst)
<< "SPV_KHR_workgroup_memory_explicit_layout extension "
"requires SPIR-V version 1.4 or later.";
}
}
return SPV_SUCCESS;
}
spv_result_t ValidateExtInstImport(ValidationState_t& _,
const Instruction* inst) {
const auto name_id = 1;
if (!_.HasExtension(kSPV_KHR_non_semantic_info)) {
const std::string name(reinterpret_cast<const char*>(
inst->words().data() + inst->operands()[name_id].offset));
if (name.find("NonSemantic.") == 0) {
return _.diag(SPV_ERROR_INVALID_DATA, inst)
<< "NonSemantic extended instruction sets cannot be declared "
"without SPV_KHR_non_semantic_info.";
}
}
return SPV_SUCCESS;
}
spv_result_t ValidateExtInst(ValidationState_t& _, const Instruction* inst) {
const uint32_t result_type = inst->type_id();
const uint32_t num_operands = static_cast<uint32_t>(inst->operands().size());
const uint32_t ext_inst_set = inst->word(3);
const uint32_t ext_inst_index = inst->word(4);
const spv_ext_inst_type_t ext_inst_type =
spv_ext_inst_type_t(inst->ext_inst_type());
auto ext_inst_name = [&_, ext_inst_set, ext_inst_type, ext_inst_index]() {
spv_ext_inst_desc desc = nullptr;
if (_.grammar().lookupExtInst(ext_inst_type, ext_inst_index, &desc) !=
SPV_SUCCESS ||
!desc) {
return std::string("Unknown ExtInst");
}
auto* import_inst = _.FindDef(ext_inst_set);
assert(import_inst);
std::ostringstream ss;
ss << reinterpret_cast<const char*>(import_inst->words().data() + 2);
ss << " ";
ss << desc->name;
return ss.str();
};
if (ext_inst_type == SPV_EXT_INST_TYPE_GLSL_STD_450) {
const GLSLstd450 ext_inst_key = GLSLstd450(ext_inst_index);
switch (ext_inst_key) {
case GLSLstd450Round:
case GLSLstd450RoundEven:
case GLSLstd450FAbs:
case GLSLstd450Trunc:
case GLSLstd450FSign:
case GLSLstd450Floor:
case GLSLstd450Ceil:
case GLSLstd450Fract:
case GLSLstd450Sqrt:
case GLSLstd450InverseSqrt:
case GLSLstd450FMin:
case GLSLstd450FMax:
case GLSLstd450FClamp:
case GLSLstd450FMix:
case GLSLstd450Step:
case GLSLstd450SmoothStep:
case GLSLstd450Fma:
case GLSLstd450Normalize:
case GLSLstd450FaceForward:
case GLSLstd450Reflect:
case GLSLstd450NMin:
case GLSLstd450NMax:
case GLSLstd450NClamp: {
if (!_.IsFloatScalarOrVectorType(result_type)) {
return _.diag(SPV_ERROR_INVALID_DATA, inst)
<< ext_inst_name() << ": "
<< "expected Result Type to be a float scalar or vector type";
}
for (uint32_t operand_index = 4; operand_index < num_operands;
++operand_index) {
const uint32_t operand_type = _.GetOperandTypeId(inst, operand_index);
if (result_type != operand_type) {
return _.diag(SPV_ERROR_INVALID_DATA, inst)
<< ext_inst_name() << ": "
<< "expected types of all operands to be equal to Result "
"Type";
}
}
break;
}
case GLSLstd450SAbs:
case GLSLstd450SSign:
case GLSLstd450UMin:
case GLSLstd450SMin:
case GLSLstd450UMax:
case GLSLstd450SMax:
case GLSLstd450UClamp:
case GLSLstd450SClamp:
case GLSLstd450FindILsb:
case GLSLstd450FindUMsb:
case GLSLstd450FindSMsb: {
if (!_.IsIntScalarOrVectorType(result_type)) {
return _.diag(SPV_ERROR_INVALID_DATA, inst)
<< ext_inst_name() << ": "
<< "expected Result Type to be an int scalar or vector type";
}
const uint32_t result_type_bit_width = _.GetBitWidth(result_type);
const uint32_t result_type_dimension = _.GetDimension(result_type);
for (uint32_t operand_index = 4; operand_index < num_operands;
++operand_index) {
const uint32_t operand_type = _.GetOperandTypeId(inst, operand_index);
if (!_.IsIntScalarOrVectorType(operand_type)) {
return _.diag(SPV_ERROR_INVALID_DATA, inst)
<< ext_inst_name() << ": "
<< "expected all operands to be int scalars or vectors";
}
if (result_type_dimension != _.GetDimension(operand_type)) {
return _.diag(SPV_ERROR_INVALID_DATA, inst)
<< ext_inst_name() << ": "
<< "expected all operands to have the same dimension as "
<< "Result Type";
}
if (result_type_bit_width != _.GetBitWidth(operand_type)) {
return _.diag(SPV_ERROR_INVALID_DATA, inst)
<< ext_inst_name() << ": "
<< "expected all operands to have the same bit width as "
<< "Result Type";
}
if (ext_inst_key == GLSLstd450FindUMsb ||
ext_inst_key == GLSLstd450FindSMsb) {
if (result_type_bit_width != 32) {
return _.diag(SPV_ERROR_INVALID_DATA, inst)
<< ext_inst_name() << ": "
<< "this instruction is currently limited to 32-bit width "
<< "components";
}
}
}
break;
}
case GLSLstd450Radians:
case GLSLstd450Degrees:
case GLSLstd450Sin:
case GLSLstd450Cos:
case GLSLstd450Tan:
case GLSLstd450Asin:
case GLSLstd450Acos:
case GLSLstd450Atan:
case GLSLstd450Sinh:
case GLSLstd450Cosh:
case GLSLstd450Tanh:
case GLSLstd450Asinh:
case GLSLstd450Acosh:
case GLSLstd450Atanh:
case GLSLstd450Exp:
case GLSLstd450Exp2:
case GLSLstd450Log:
case GLSLstd450Log2:
case GLSLstd450Atan2:
case GLSLstd450Pow: {
if (!_.IsFloatScalarOrVectorType(result_type)) {
return _.diag(SPV_ERROR_INVALID_DATA, inst)
<< ext_inst_name() << ": "
<< "expected Result Type to be a 16 or 32-bit scalar or "
"vector float type";
}
const uint32_t result_type_bit_width = _.GetBitWidth(result_type);
if (result_type_bit_width != 16 && result_type_bit_width != 32) {
return _.diag(SPV_ERROR_INVALID_DATA, inst)
<< ext_inst_name() << ": "
<< "expected Result Type to be a 16 or 32-bit scalar or "
"vector float type";
}
for (uint32_t operand_index = 4; operand_index < num_operands;
++operand_index) {
const uint32_t operand_type = _.GetOperandTypeId(inst, operand_index);
if (result_type != operand_type) {
return _.diag(SPV_ERROR_INVALID_DATA, inst)
<< ext_inst_name() << ": "
<< "expected types of all operands to be equal to Result "
"Type";
}
}
break;
}
case GLSLstd450Determinant: {
const uint32_t x_type = _.GetOperandTypeId(inst, 4);
uint32_t num_rows = 0;
uint32_t num_cols = 0;
uint32_t col_type = 0;
uint32_t component_type = 0;
if (!_.GetMatrixTypeInfo(x_type, &num_rows, &num_cols, &col_type,
&component_type) ||
num_rows != num_cols) {
return _.diag(SPV_ERROR_INVALID_DATA, inst)
<< ext_inst_name() << ": "
<< "expected operand X to be a square matrix";
}
if (result_type != component_type) {
return _.diag(SPV_ERROR_INVALID_DATA, inst)
<< ext_inst_name() << ": "
<< "expected operand X component type to be equal to "
<< "Result Type";
}
break;
}
case GLSLstd450MatrixInverse: {
uint32_t num_rows = 0;
uint32_t num_cols = 0;
uint32_t col_type = 0;
uint32_t component_type = 0;
if (!_.GetMatrixTypeInfo(result_type, &num_rows, &num_cols, &col_type,
&component_type) ||
num_rows != num_cols) {
return _.diag(SPV_ERROR_INVALID_DATA, inst)
<< ext_inst_name() << ": "
<< "expected Result Type to be a square matrix";
}
const uint32_t x_type = _.GetOperandTypeId(inst, 4);
if (result_type != x_type) {
return _.diag(SPV_ERROR_INVALID_DATA, inst)
<< ext_inst_name() << ": "
<< "expected operand X type to be equal to Result Type";
}
break;
}
case GLSLstd450Modf: {
if (!_.IsFloatScalarOrVectorType(result_type)) {
return _.diag(SPV_ERROR_INVALID_DATA, inst)
<< ext_inst_name() << ": "
<< "expected Result Type to be a scalar or vector float type";
}
const uint32_t x_type = _.GetOperandTypeId(inst, 4);
const uint32_t i_type = _.GetOperandTypeId(inst, 5);
if (x_type != result_type) {
return _.diag(SPV_ERROR_INVALID_DATA, inst)
<< ext_inst_name() << ": "
<< "expected operand X type to be equal to Result Type";
}
uint32_t i_storage_class = 0;
uint32_t i_data_type = 0;
if (!_.GetPointerTypeInfo(i_type, &i_data_type, &i_storage_class)) {
return _.diag(SPV_ERROR_INVALID_DATA, inst)
<< ext_inst_name() << ": "
<< "expected operand I to be a pointer";
}
if (i_data_type != result_type) {
return _.diag(SPV_ERROR_INVALID_DATA, inst)
<< ext_inst_name() << ": "
<< "expected operand I data type to be equal to Result Type";
}
break;
}
case GLSLstd450ModfStruct: {
std::vector<uint32_t> result_types;
if (!_.GetStructMemberTypes(result_type, &result_types) ||
result_types.size() != 2 ||
!_.IsFloatScalarOrVectorType(result_types[0]) ||
result_types[1] != result_types[0]) {
return _.diag(SPV_ERROR_INVALID_DATA, inst)
<< ext_inst_name() << ": "
<< "expected Result Type to be a struct with two identical "
<< "scalar or vector float type members";
}
const uint32_t x_type = _.GetOperandTypeId(inst, 4);
if (x_type != result_types[0]) {
return _.diag(SPV_ERROR_INVALID_DATA, inst)
<< ext_inst_name() << ": "
<< "expected operand X type to be equal to members of "
<< "Result Type struct";
}
break;
}
case GLSLstd450Frexp: {
if (!_.IsFloatScalarOrVectorType(result_type)) {
return _.diag(SPV_ERROR_INVALID_DATA, inst)
<< ext_inst_name() << ": "
<< "expected Result Type to be a scalar or vector float type";
}
const uint32_t x_type = _.GetOperandTypeId(inst, 4);
const uint32_t exp_type = _.GetOperandTypeId(inst, 5);
if (x_type != result_type) {
return _.diag(SPV_ERROR_INVALID_DATA, inst)
<< ext_inst_name() << ": "
<< "expected operand X type to be equal to Result Type";
}
uint32_t exp_storage_class = 0;
uint32_t exp_data_type = 0;
if (!_.GetPointerTypeInfo(exp_type, &exp_data_type,
&exp_storage_class)) {
return _.diag(SPV_ERROR_INVALID_DATA, inst)
<< ext_inst_name() << ": "
<< "expected operand Exp to be a pointer";
}
if (!_.IsIntScalarOrVectorType(exp_data_type) ||
(!_.HasExtension(kSPV_AMD_gpu_shader_int16) &&
_.GetBitWidth(exp_data_type) != 32) ||
(_.HasExtension(kSPV_AMD_gpu_shader_int16) &&
_.GetBitWidth(exp_data_type) != 16 &&
_.GetBitWidth(exp_data_type) != 32)) {
return _.diag(SPV_ERROR_INVALID_DATA, inst)
<< ext_inst_name() << ": "
<< "expected operand Exp data type to be a "
<< (_.HasExtension(kSPV_AMD_gpu_shader_int16)
? "16-bit or 32-bit "
: "32-bit ")
<< "int scalar or vector type";
}
if (_.GetDimension(result_type) != _.GetDimension(exp_data_type)) {
return _.diag(SPV_ERROR_INVALID_DATA, inst)
<< ext_inst_name() << ": "
<< "expected operand Exp data type to have the same component "
<< "number as Result Type";
}
break;
}
case GLSLstd450Ldexp: {
if (!_.IsFloatScalarOrVectorType(result_type)) {
return _.diag(SPV_ERROR_INVALID_DATA, inst)
<< ext_inst_name() << ": "
<< "expected Result Type to be a scalar or vector float type";
}
const uint32_t x_type = _.GetOperandTypeId(inst, 4);
const uint32_t exp_type = _.GetOperandTypeId(inst, 5);
if (x_type != result_type) {
return _.diag(SPV_ERROR_INVALID_DATA, inst)
<< ext_inst_name() << ": "
<< "expected operand X type to be equal to Result Type";
}
if (!_.IsIntScalarOrVectorType(exp_type)) {
return _.diag(SPV_ERROR_INVALID_DATA, inst)
<< ext_inst_name() << ": "
<< "expected operand Exp to be a 32-bit int scalar "
<< "or vector type";
}
if (_.GetDimension(result_type) != _.GetDimension(exp_type)) {
return _.diag(SPV_ERROR_INVALID_DATA, inst)
<< ext_inst_name() << ": "
<< "expected operand Exp to have the same component "
<< "number as Result Type";
}
break;
}
case GLSLstd450FrexpStruct: {
std::vector<uint32_t> result_types;
if (!_.GetStructMemberTypes(result_type, &result_types) ||
result_types.size() != 2 ||
!_.IsFloatScalarOrVectorType(result_types[0]) ||
!_.IsIntScalarOrVectorType(result_types[1]) ||
(!_.HasExtension(kSPV_AMD_gpu_shader_int16) &&
_.GetBitWidth(result_types[1]) != 32) ||
(_.HasExtension(kSPV_AMD_gpu_shader_int16) &&
_.GetBitWidth(result_types[1]) != 16 &&
_.GetBitWidth(result_types[1]) != 32) ||
_.GetDimension(result_types[0]) !=
_.GetDimension(result_types[1])) {
return _.diag(SPV_ERROR_INVALID_DATA, inst)
<< ext_inst_name() << ": "
<< "expected Result Type to be a struct with two members, "
<< "first member a float scalar or vector, second member a "
<< (_.HasExtension(kSPV_AMD_gpu_shader_int16)
? "16-bit or 32-bit "
: "32-bit ")
<< "int scalar or vector with the same number of "
<< "components as the first member";
}
const uint32_t x_type = _.GetOperandTypeId(inst, 4);
if (x_type != result_types[0]) {
return _.diag(SPV_ERROR_INVALID_DATA, inst)
<< ext_inst_name() << ": "
<< "expected operand X type to be equal to the first member "
<< "of Result Type struct";
}
break;
}
case GLSLstd450PackSnorm4x8:
case GLSLstd450PackUnorm4x8: {
if (!_.IsIntScalarType(result_type) ||
_.GetBitWidth(result_type) != 32) {
return _.diag(SPV_ERROR_INVALID_DATA, inst)
<< ext_inst_name() << ": "
<< "expected Result Type to be 32-bit int scalar type";
}
const uint32_t v_type = _.GetOperandTypeId(inst, 4);
if (!_.IsFloatVectorType(v_type) || _.GetDimension(v_type) != 4 ||
_.GetBitWidth(v_type) != 32) {
return _.diag(SPV_ERROR_INVALID_DATA, inst)
<< ext_inst_name() << ": "
<< "expected operand V to be a 32-bit float vector of size 4";
}
break;
}
case GLSLstd450PackSnorm2x16:
case GLSLstd450PackUnorm2x16:
case GLSLstd450PackHalf2x16: {
if (!_.IsIntScalarType(result_type) ||
_.GetBitWidth(result_type) != 32) {
return _.diag(SPV_ERROR_INVALID_DATA, inst)
<< ext_inst_name() << ": "
<< "expected Result Type to be 32-bit int scalar type";
}
const uint32_t v_type = _.GetOperandTypeId(inst, 4);
if (!_.IsFloatVectorType(v_type) || _.GetDimension(v_type) != 2 ||
_.GetBitWidth(v_type) != 32) {
return _.diag(SPV_ERROR_INVALID_DATA, inst)
<< ext_inst_name() << ": "
<< "expected operand V to be a 32-bit float vector of size 2";
}
break;
}
case GLSLstd450PackDouble2x32: {
if (!_.IsFloatScalarType(result_type) ||
_.GetBitWidth(result_type) != 64) {
return _.diag(SPV_ERROR_INVALID_DATA, inst)
<< ext_inst_name() << ": "
<< "expected Result Type to be 64-bit float scalar type";
}
const uint32_t v_type = _.GetOperandTypeId(inst, 4);
if (!_.IsIntVectorType(v_type) || _.GetDimension(v_type) != 2 ||
_.GetBitWidth(v_type) != 32) {
return _.diag(SPV_ERROR_INVALID_DATA, inst)
<< ext_inst_name() << ": "
<< "expected operand V to be a 32-bit int vector of size 2";
}
break;
}
case GLSLstd450UnpackSnorm4x8:
case GLSLstd450UnpackUnorm4x8: {
if (!_.IsFloatVectorType(result_type) ||
_.GetDimension(result_type) != 4 ||
_.GetBitWidth(result_type) != 32) {
return _.diag(SPV_ERROR_INVALID_DATA, inst)
<< ext_inst_name() << ": "
<< "expected Result Type to be a 32-bit float vector of size "
"4";
}
const uint32_t v_type = _.GetOperandTypeId(inst, 4);
if (!_.IsIntScalarType(v_type) || _.GetBitWidth(v_type) != 32) {
return _.diag(SPV_ERROR_INVALID_DATA, inst)
<< ext_inst_name() << ": "
<< "expected operand P to be a 32-bit int scalar";
}
break;
}
case GLSLstd450UnpackSnorm2x16:
case GLSLstd450UnpackUnorm2x16:
case GLSLstd450UnpackHalf2x16: {
if (!_.IsFloatVectorType(result_type) ||
_.GetDimension(result_type) != 2 ||
_.GetBitWidth(result_type) != 32) {
return _.diag(SPV_ERROR_INVALID_DATA, inst)
<< ext_inst_name() << ": "
<< "expected Result Type to be a 32-bit float vector of size "
"2";
}
const uint32_t v_type = _.GetOperandTypeId(inst, 4);
if (!_.IsIntScalarType(v_type) || _.GetBitWidth(v_type) != 32) {
return _.diag(SPV_ERROR_INVALID_DATA, inst)
<< ext_inst_name() << ": "
<< "expected operand P to be a 32-bit int scalar";
}
break;
}
case GLSLstd450UnpackDouble2x32: {
if (!_.IsIntVectorType(result_type) ||
_.GetDimension(result_type) != 2 ||
_.GetBitWidth(result_type) != 32) {
return _.diag(SPV_ERROR_INVALID_DATA, inst)
<< ext_inst_name() << ": "
<< "expected Result Type to be a 32-bit int vector of size "
"2";
}
const uint32_t v_type = _.GetOperandTypeId(inst, 4);
if (!_.IsFloatScalarType(v_type) || _.GetBitWidth(v_type) != 64) {
return _.diag(SPV_ERROR_INVALID_DATA, inst)
<< ext_inst_name() << ": "
<< "expected operand V to be a 64-bit float scalar";
}
break;
}
case GLSLstd450Length: {
if (!_.IsFloatScalarType(result_type)) {
return _.diag(SPV_ERROR_INVALID_DATA, inst)
<< ext_inst_name() << ": "
<< "expected Result Type to be a float scalar type";
}
const uint32_t x_type = _.GetOperandTypeId(inst, 4);
if (!_.IsFloatScalarOrVectorType(x_type)) {
return _.diag(SPV_ERROR_INVALID_DATA, inst)
<< ext_inst_name() << ": "
<< "expected operand X to be of float scalar or vector type";
}
if (result_type != _.GetComponentType(x_type)) {
return _.diag(SPV_ERROR_INVALID_DATA, inst)
<< ext_inst_name() << ": "
<< "expected operand X component type to be equal to Result "
"Type";
}
break;
}
case GLSLstd450Distance: {
if (!_.IsFloatScalarType(result_type)) {
return _.diag(SPV_ERROR_INVALID_DATA, inst)
<< 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, inst)
<< ext_inst_name() << ": "
<< "expected operand P0 to be of float scalar or vector type";
}
if (result_type != _.GetComponentType(p0_type)) {
return _.diag(SPV_ERROR_INVALID_DATA, inst)
<< ext_inst_name() << ": "
<< "expected operand P0 component type to be equal to "
<< "Result Type";
}
const uint32_t p1_type = _.GetOperandTypeId(inst, 5);
if (!_.IsFloatScalarOrVectorType(p1_type)) {
return _.diag(SPV_ERROR_INVALID_DATA, inst)
<< ext_inst_name() << ": "
<< "expected operand P1 to be of float scalar or vector type";
}
if (result_type != _.GetComponentType(p1_type)) {
return _.diag(SPV_ERROR_INVALID_DATA, inst)
<< ext_inst_name() << ": "
<< "expected operand P1 component type to be equal to "
<< "Result Type";
}
if (_.GetDimension(p0_type) != _.GetDimension(p1_type)) {
return _.diag(SPV_ERROR_INVALID_DATA, inst)
<< ext_inst_name() << ": "
<< "expected operands P0 and P1 to have the same number of "
<< "components";
}
break;
}
case GLSLstd450Cross: {
if (!_.IsFloatVectorType(result_type)) {
return _.diag(SPV_ERROR_INVALID_DATA, inst)
<< ext_inst_name() << ": "
<< "expected Result Type to be a float vector type";
}
if (_.GetDimension(result_type) != 3) {
return _.diag(SPV_ERROR_INVALID_DATA, inst)
<< ext_inst_name() << ": "
<< "expected Result Type to have 3 components";
}
const uint32_t x_type = _.GetOperandTypeId(inst, 4);
const uint32_t y_type = _.GetOperandTypeId(inst, 5);
if (x_type != result_type) {
return _.diag(SPV_ERROR_INVALID_DATA, inst)
<< ext_inst_name() << ": "
<< "expected operand X type to be equal to Result Type";
}
if (y_type != result_type) {
return _.diag(SPV_ERROR_INVALID_DATA, inst)
<< ext_inst_name() << ": "
<< "expected operand Y type to be equal to Result Type";
}
break;
}
case GLSLstd450Refract: {
if (!_.IsFloatScalarOrVectorType(result_type)) {
return _.diag(SPV_ERROR_INVALID_DATA, inst)
<< ext_inst_name() << ": "
<< "expected Result Type to be a float scalar or vector type";
}
const uint32_t i_type = _.GetOperandTypeId(inst, 4);
const uint32_t n_type = _.GetOperandTypeId(inst, 5);
const uint32_t eta_type = _.GetOperandTypeId(inst, 6);
if (result_type != i_type) {
return _.diag(SPV_ERROR_INVALID_DATA, inst)
<< ext_inst_name() << ": "
<< "expected operand I to be of type equal to Result Type";
}
if (result_type != n_type) {
return _.diag(SPV_ERROR_INVALID_DATA, inst)
<< ext_inst_name() << ": "
<< "expected operand N to be of type equal to Result Type";
}
if (!_.IsFloatScalarType(eta_type)) {
return _.diag(SPV_ERROR_INVALID_DATA, inst)
<< ext_inst_name() << ": "
<< "expected operand Eta to be a float scalar";
}
break;
}
case GLSLstd450InterpolateAtCentroid:
case GLSLstd450InterpolateAtSample:
case GLSLstd450InterpolateAtOffset: {
if (!_.HasCapability(SpvCapabilityInterpolationFunction)) {
return _.diag(SPV_ERROR_INVALID_CAPABILITY, inst)
<< ext_inst_name()
<< " requires capability InterpolationFunction";
}
if (!_.IsFloatScalarOrVectorType(result_type) ||
_.GetBitWidth(result_type) != 32) {
return _.diag(SPV_ERROR_INVALID_DATA, inst)
<< ext_inst_name() << ": "
<< "expected Result Type to be a 32-bit float scalar "
<< "or vector type";
}
// If HLSL legalization and first operand is an OpLoad, use load
// pointer as the interpolant lvalue. Else use interpolate first
// operand.
uint32_t interp_id = inst->GetOperandAs<uint32_t>(4);
auto* interp_inst = _.FindDef(interp_id);
uint32_t interpolant_type = (_.options()->before_hlsl_legalization &&
interp_inst->opcode() == SpvOpLoad)
? _.GetOperandTypeId(interp_inst, 2)
: _.GetOperandTypeId(inst, 4);
uint32_t interpolant_storage_class = 0;
uint32_t interpolant_data_type = 0;
if (!_.GetPointerTypeInfo(interpolant_type, &interpolant_data_type,
&interpolant_storage_class)) {
return _.diag(SPV_ERROR_INVALID_DATA, inst)
<< ext_inst_name() << ": "
<< "expected Interpolant to be a pointer";
}
if (result_type != interpolant_data_type) {
return _.diag(SPV_ERROR_INVALID_DATA, inst)
<< ext_inst_name() << ": "
<< "expected Interpolant data type to be equal to Result Type";
}
if (interpolant_storage_class != SpvStorageClassInput) {
return _.diag(SPV_ERROR_INVALID_DATA, inst)
<< ext_inst_name() << ": "
<< "expected Interpolant storage class to be Input";
}
if (ext_inst_key == GLSLstd450InterpolateAtSample) {
const uint32_t sample_type = _.GetOperandTypeId(inst, 5);
if (!_.IsIntScalarType(sample_type) ||
_.GetBitWidth(sample_type) != 32) {
return _.diag(SPV_ERROR_INVALID_DATA, inst)
<< ext_inst_name() << ": "
<< "expected Sample to be 32-bit integer";
}
}
if (ext_inst_key == GLSLstd450InterpolateAtOffset) {
const uint32_t offset_type = _.GetOperandTypeId(inst, 5);
if (!_.IsFloatVectorType(offset_type) ||
_.GetDimension(offset_type) != 2 ||
_.GetBitWidth(offset_type) != 32) {
return _.diag(SPV_ERROR_INVALID_DATA, inst)
<< ext_inst_name() << ": "
<< "expected Offset to be a vector of 2 32-bit floats";
}
}
_.function(inst->function()->id())
->RegisterExecutionModelLimitation(
SpvExecutionModelFragment,
ext_inst_name() +
std::string(" requires Fragment execution model"));
break;
}
case GLSLstd450IMix: {
return _.diag(SPV_ERROR_INVALID_DATA, inst)
<< "Extended instruction GLSLstd450IMix is not supported";
}
case GLSLstd450Bad: {
return _.diag(SPV_ERROR_INVALID_DATA, inst)
<< "Encountered extended instruction GLSLstd450Bad";
}
case GLSLstd450Count: {
assert(0);
break;
}
}
} else if (ext_inst_type == SPV_EXT_INST_TYPE_OPENCL_STD) {
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, inst)
<< 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, inst)
<< 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, inst)
<< ext_inst_name() << ": "
<< "expected types of all operands to be equal to Result "
"Type";
}
}
break;
}
case OpenCLLIB::Fract:
case OpenCLLIB::Modf:
case OpenCLLIB::Sincos: {
if (!_.IsFloatScalarOrVectorType(result_type)) {
return _.diag(SPV_ERROR_INVALID_DATA, inst)
<< 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, inst)
<< 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, inst)
<< 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, inst)
<< 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, inst)
<< 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, inst)
<< ext_inst_name() << ": "
<< "expected data type of the pointer to be equal to Result "
"Type";
}
break;
}
case OpenCLLIB::Frexp:
case OpenCLLIB::Lgamma_r:
case OpenCLLIB::Remquo: {
if (!_.IsFloatScalarOrVectorType(result_type)) {
return _.diag(SPV_ERROR_INVALID_DATA, inst)
<< 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, inst)
<< 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, inst)
<< 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, inst)
<< 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, inst)
<< 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, inst)
<< 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, inst)
<< 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, inst)
<< 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, inst)
<< 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, inst)
<< 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, inst)
<< 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, inst)
<< 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, inst)
<< 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, inst)
<< 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, inst)
<< 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, inst)
<< 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, inst)
<< 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, inst)
<< 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, inst)
<< 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, inst)
<< 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, inst)
<< 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, inst)
<< 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, inst)
<< 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, inst)
<< 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, inst)
<< 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, inst)
<< 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, inst)
<< 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, inst)
<< 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, inst)
<< 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, inst)
<< 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, inst)
<< 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, inst)
<< 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, inst)
<< 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, inst)
<< 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, inst)
<< 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, inst)
<< 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, inst)
<< ext_inst_name() << ": "
<< "expected operand X type to be equal to Result Type";
}
if (y_type != result_type) {
return _.diag(SPV_ERROR_INVALID_DATA, inst)
<< 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, inst)
<< 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, inst)
<< 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, inst)
<< 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, inst)
<< 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, inst)
<< 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, inst)
<< 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, inst)
<< 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, inst)
<< 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, inst)
<< 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, inst)
<< 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, inst)
<< 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, inst)
<< 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, inst)
<< 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, inst)
<< 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, inst)
<< 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, inst)
<< 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, inst)
<< 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, inst)
<< ext_inst_name() << ": "
<< "expected operand A type to be equal to Result Type";
}
if (result_type != b_type) {
return _.diag(SPV_ERROR_INVALID_DATA, inst)
<< ext_inst_name() << ": "
<< "expected operand B type to be equal to Result Type";
}
if (!_.IsIntScalarOrVectorType(c_type)) {
return _.diag(SPV_ERROR_INVALID_DATA, inst)
<< 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, inst)
<< 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, inst)
<< 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, inst)
<< 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, inst)
<< 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, inst)
<< 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, inst)
<< 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, inst)
<< 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, inst)
<< ext_inst_name() << ": "
<< "expected operand P storage class to be UniformConstant, "
"Generic, CrossWorkgroup, Workgroup or Function";
}
if (_.GetComponentType(result_type) != p_data_type) {
return _.diag(SPV_ERROR_INVALID_DATA, inst)
<< ext_inst_name() << ": "
<< "expected operand P data type to be equal to component "
"type of Result Type";
}
const uint32_t n_value = inst->word(7);
if (num_components != n_value) {
return _.diag(SPV_ERROR_INVALID_DATA, inst)
<< 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, inst)
<< 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, inst)
<< 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, inst)
<< 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, inst)
<< 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, inst)
<< 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, inst)
<< 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, inst)
<< ext_inst_name() << ": "
<< "expected operand P storage class to be Generic, "
"CrossWorkgroup, Workgroup or Function";
}
if (_.GetComponentType(data_type) != p_data_type) {
return _.diag(SPV_ERROR_INVALID_DATA, inst)
<< 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, inst)
<< 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, inst)
<< 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, inst)
<< 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, inst)
<< 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, inst)
<< 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, inst)
<< 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, inst)
<< 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, inst)
<< 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, inst)
<< 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, inst)
<< 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, inst)
<< 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, inst)
<< 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, inst)
<< ext_inst_name() << ": "
<< "expected operand P data type to be 16-bit float scalar";
}
const uint32_t n_value = inst->word(7);
if (num_components != n_value) {
return _.diag(SPV_ERROR_INVALID_DATA, inst)
<< 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, inst)
<< 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, inst)
<< 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, inst)
<< 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, inst)
<< 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, inst)
<< 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, inst)
<< 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, inst)
<< 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, inst)
<< 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, inst)
<< 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, inst)
<< 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, inst)
<< 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, inst)
<< 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, inst)
<< 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, inst)
<< 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, inst)
<< 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, inst)
<< 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, inst)
<< 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, inst)
<< 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, inst)
<< 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, inst)
<< ext_inst_name() << ": "
<< "expected operand Format to be a pointer";
}
if (format_storage_class != SpvStorageClassUniformConstant) {
return _.diag(SPV_ERROR_INVALID_DATA, inst)
<< 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, inst)
<< 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, inst)
<< 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, inst)
<< ext_inst_name() << ": "
<< "expected operand Ptr to be a pointer";
}
if (p_storage_class != SpvStorageClassCrossWorkgroup) {
return _.diag(SPV_ERROR_INVALID_DATA, inst)
<< 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, inst)
<< 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, inst)
<< 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, inst)
<< 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, inst)
<< 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;
}
}
} else if (ext_inst_type == SPV_EXT_INST_TYPE_OPENCL_DEBUGINFO_100) {
if (!_.IsVoidType(result_type)) {
return _.diag(SPV_ERROR_INVALID_DATA, inst)
<< ext_inst_name() << ": "
<< "expected result type must be a result id of "
<< "OpTypeVoid";
}
auto num_words = inst->words().size();
const OpenCLDebugInfo100Instructions ext_inst_key =
OpenCLDebugInfo100Instructions(ext_inst_index);
switch (ext_inst_key) {
case OpenCLDebugInfo100DebugInfoNone:
case OpenCLDebugInfo100DebugNoScope:
case OpenCLDebugInfo100DebugOperation:
// The binary parser validates the opcode for DebugInfoNone,
// DebugNoScope, DebugOperation, and the literal values don't need
// further checks.
break;
case OpenCLDebugInfo100DebugCompilationUnit: {
CHECK_DEBUG_OPERAND("Source", OpenCLDebugInfo100DebugSource, 7);
break;
}
case OpenCLDebugInfo100DebugSource: {
CHECK_OPERAND("File", SpvOpString, 5);
if (num_words == 7) CHECK_OPERAND("Text", SpvOpString, 6);
break;
}
case OpenCLDebugInfo100DebugTypeBasic: {
CHECK_OPERAND("Name", SpvOpString, 5);
CHECK_OPERAND("Size", SpvOpConstant, 6);
// "Encoding" param is already validated by the binary parsing stage.
break;
}
case OpenCLDebugInfo100DebugTypePointer:
case OpenCLDebugInfo100DebugTypeQualifier: {
auto validate_base_type =
ValidateOperandBaseType(_, inst, 5, ext_inst_name);
if (validate_base_type != SPV_SUCCESS) return validate_base_type;
break;
}
case OpenCLDebugInfo100DebugTypeVector: {
auto validate_base_type =
ValidateOperandBaseType(_, inst, 5, ext_inst_name);
if (validate_base_type != SPV_SUCCESS) return validate_base_type;
uint32_t component_count = inst->word(6);
if (!component_count || component_count > 4) {
return _.diag(SPV_ERROR_INVALID_DATA, inst)
<< ext_inst_name() << ": Component Count must be positive "
<< "integer less than or equal to 4";
}
break;
}
case OpenCLDebugInfo100DebugTypeArray: {
auto validate_base_type = ValidateOperandDebugType(
_, "Base Type", inst, 5, ext_inst_name, false);
if (validate_base_type != SPV_SUCCESS) return validate_base_type;
for (uint32_t i = 6; i < num_words; ++i) {
bool invalid = false;
auto* component_count = _.FindDef(inst->word(i));
if (IsConstIntScalarTypeWith32Or64Bits(_, component_count)) {
// TODO: We need a spec discussion for the bindless array.
if (!component_count->word(3)) {
invalid = true;
}
} else if (component_count->words().size() > 6 &&
(OpenCLDebugInfo100Instructions(component_count->word(
4)) == OpenCLDebugInfo100DebugLocalVariable ||
OpenCLDebugInfo100Instructions(component_count->word(
4)) == OpenCLDebugInfo100DebugGlobalVariable)) {
auto* component_count_type = _.FindDef(component_count->word(6));
if (component_count_type->words().size() > 7) {
if (OpenCLDebugInfo100Instructions(component_count_type->word(
4)) != OpenCLDebugInfo100DebugTypeBasic ||
OpenCLDebugInfo100DebugBaseTypeAttributeEncoding(
component_count_type->word(7)) !=
OpenCLDebugInfo100Unsigned) {
invalid = true;
} else {
// DebugTypeBasic for DebugLocalVariable/DebugGlobalVariable
// must have Unsigned encoding and 32 or 64 as its size in bits.
Instruction* size_in_bits =
_.FindDef(component_count_type->word(6));
if (!_.IsIntScalarType(size_in_bits->type_id()) ||
(size_in_bits->word(3) != 32 &&
size_in_bits->word(3) != 64)) {
invalid = true;
}
}
} else {
invalid = true;
}
} else {
invalid = true;
}
if (invalid) {
return _.diag(SPV_ERROR_INVALID_DATA, inst)
<< ext_inst_name() << ": Component Count must be "
<< "OpConstant with a 32- or 64-bits integer scalar type or "
<< "DebugGlobalVariable or DebugLocalVariable with a 32- or "
<< "64-bits unsigned integer scalar type";
}
}
break;
}
case OpenCLDebugInfo100DebugTypedef: {
CHECK_OPERAND("Name", SpvOpString, 5);
auto validate_base_type =
ValidateOperandBaseType(_, inst, 6, ext_inst_name);
if (validate_base_type != SPV_SUCCESS) return validate_base_type;
CHECK_DEBUG_OPERAND("Source", OpenCLDebugInfo100DebugSource, 7);
auto validate_parent =
ValidateOperandLexicalScope(_, "Parent", inst, 10, ext_inst_name);
if (validate_parent != SPV_SUCCESS) return validate_parent;
break;
}
case OpenCLDebugInfo100DebugTypeFunction: {
auto* return_type = _.FindDef(inst->word(6));
// TODO: We need a spec discussion that we have to allow return and
// parameter types of a DebugTypeFunction to have template parameter.
if (return_type->opcode() != SpvOpTypeVoid) {
auto validate_return = ValidateOperandDebugType(
_, "Return Type", inst, 6, ext_inst_name, true);
if (validate_return != SPV_SUCCESS) return validate_return;
}
for (uint32_t word_index = 7; word_index < num_words; ++word_index) {
auto validate_param = ValidateOperandDebugType(
_, "Parameter Types", inst, word_index, ext_inst_name, true);
if (validate_param != SPV_SUCCESS) return validate_param;
}
break;
}
case OpenCLDebugInfo100DebugTypeEnum: {
CHECK_OPERAND("Name", SpvOpString, 5);
if (!DoesDebugInfoOperandMatchExpectation(
_,
[](OpenCLDebugInfo100Instructions dbg_inst) {
return dbg_inst == OpenCLDebugInfo100DebugInfoNone;
},
inst, 6)) {
auto validate_underlying_type = ValidateOperandDebugType(
_, "Underlying Types", inst, 6, ext_inst_name, false);
if (validate_underlying_type != SPV_SUCCESS)
return validate_underlying_type;
}
CHECK_DEBUG_OPERAND("Source", OpenCLDebugInfo100DebugSource, 7);
auto validate_parent =
ValidateOperandLexicalScope(_, "Parent", inst, 10, ext_inst_name);
if (validate_parent != SPV_SUCCESS) return validate_parent;
CHECK_OPERAND("Size", SpvOpConstant, 11);
auto* size = _.FindDef(inst->word(11));
if (!_.IsIntScalarType(size->type_id()) || !size->word(3)) {
return _.diag(SPV_ERROR_INVALID_DATA, inst)
<< ext_inst_name() << ": expected operand Size is a "
<< "positive integer";
}
for (uint32_t word_index = 13; word_index + 1 < num_words;
word_index += 2) {
CHECK_OPERAND("Value", SpvOpConstant, word_index);
CHECK_OPERAND("Name", SpvOpString, word_index + 1);
}
break;
}
case OpenCLDebugInfo100DebugTypeComposite: {
CHECK_OPERAND("Name", SpvOpString, 5);
CHECK_DEBUG_OPERAND("Source", OpenCLDebugInfo100DebugSource, 7);
auto validate_parent =
ValidateOperandLexicalScope(_, "Parent", inst, 10, ext_inst_name);
if (validate_parent != SPV_SUCCESS) return validate_parent;
CHECK_OPERAND("Linkage Name", SpvOpString, 11);
if (!DoesDebugInfoOperandMatchExpectation(
_,
[](OpenCLDebugInfo100Instructions dbg_inst) {
return dbg_inst == OpenCLDebugInfo100DebugInfoNone;
},
inst, 12)) {
CHECK_OPERAND("Size", SpvOpConstant, 12);
}
for (uint32_t word_index = 14; word_index < num_words; ++word_index) {
if (!DoesDebugInfoOperandMatchExpectation(
_,
[](OpenCLDebugInfo100Instructions dbg_inst) {
return dbg_inst == OpenCLDebugInfo100DebugTypeMember ||
dbg_inst == OpenCLDebugInfo100DebugFunction ||
dbg_inst == OpenCLDebugInfo100DebugTypeInheritance;
},
inst, word_index)) {
return _.diag(SPV_ERROR_INVALID_DATA, inst)
<< ext_inst_name() << ": "
<< "expected operand Members "
<< "must be DebugTypeMember, DebugFunction, or "
"DebugTypeInheritance";
}
}
break;
}
case OpenCLDebugInfo100DebugTypeMember: {
CHECK_OPERAND("Name", SpvOpString, 5);
// TODO: We need a spec discussion that we have to allow member types
// to have template parameter.
auto validate_type =
ValidateOperandDebugType(_, "Type", inst, 6, ext_inst_name, true);
if (validate_type != SPV_SUCCESS) return validate_type;
CHECK_DEBUG_OPERAND("Source", OpenCLDebugInfo100DebugSource, 7);
CHECK_DEBUG_OPERAND("Parent", OpenCLDebugInfo100DebugTypeComposite, 10);
CHECK_OPERAND("Offset", SpvOpConstant, 11);
CHECK_OPERAND("Size", SpvOpConstant, 12);
if (num_words == 15) CHECK_OPERAND("Value", SpvOpConstant, 14);
break;
}
case OpenCLDebugInfo100DebugTypeInheritance: {
CHECK_DEBUG_OPERAND("Child", OpenCLDebugInfo100DebugTypeComposite, 5);
auto* debug_inst = _.FindDef(inst->word(5));
auto composite_type =
OpenCLDebugInfo100DebugCompositeType(debug_inst->word(6));
if (composite_type != OpenCLDebugInfo100Class &&
composite_type != OpenCLDebugInfo100Structure) {
return _.diag(SPV_ERROR_INVALID_DATA, inst)
<< ext_inst_name() << ": "
<< "expected operand Child must be class or struct debug type";
}
CHECK_DEBUG_OPERAND("Parent", OpenCLDebugInfo100DebugTypeComposite, 6);
debug_inst = _.FindDef(inst->word(6));
composite_type =
OpenCLDebugInfo100DebugCompositeType(debug_inst->word(6));
if (composite_type != OpenCLDebugInfo100Class &&
composite_type != OpenCLDebugInfo100Structure) {
return _.diag(SPV_ERROR_INVALID_DATA, inst)
<< ext_inst_name() << ": "
<< "expected operand Parent must be class or struct debug "
"type";
}
CHECK_OPERAND("Offset", SpvOpConstant, 7);
CHECK_OPERAND("Size", SpvOpConstant, 8);
break;
}
case OpenCLDebugInfo100DebugFunction: {
CHECK_OPERAND("Name", SpvOpString, 5);
auto validate_type =
ValidateOperandDebugType(_, "Type", inst, 6, ext_inst_name, false);
if (validate_type != SPV_SUCCESS) return validate_type;
CHECK_DEBUG_OPERAND("Source", OpenCLDebugInfo100DebugSource, 7);
auto validate_parent =
ValidateOperandLexicalScope(_, "Parent", inst, 10, ext_inst_name);
if (validate_parent != SPV_SUCCESS) return validate_parent;
CHECK_OPERAND("Linkage Name", SpvOpString, 11);
if (!DoesDebugInfoOperandMatchExpectation(
_,
[](OpenCLDebugInfo100Instructions dbg_inst) {
return dbg_inst == OpenCLDebugInfo100DebugInfoNone;
},
inst, 14)) {
CHECK_OPERAND("Function", SpvOpFunction, 14);
}
if (num_words == 16) {
CHECK_DEBUG_OPERAND("Declaration",
OpenCLDebugInfo100DebugFunctionDeclaration, 15);
}
break;
}
case OpenCLDebugInfo100DebugFunctionDeclaration: {
CHECK_OPERAND("Name", SpvOpString, 5);
auto validate_type =
ValidateOperandDebugType(_, "Type", inst, 6, ext_inst_name, false);
if (validate_type != SPV_SUCCESS) return validate_type;
CHECK_DEBUG_OPERAND("Source", OpenCLDebugInfo100DebugSource, 7);
auto validate_parent =
ValidateOperandLexicalScope(_, "Parent", inst, 10, ext_inst_name);
if (validate_parent != SPV_SUCCESS) return validate_parent;
CHECK_OPERAND("Linkage Name", SpvOpString, 11);
break;
}
case OpenCLDebugInfo100DebugLexicalBlock: {
CHECK_DEBUG_OPERAND("Source", OpenCLDebugInfo100DebugSource, 5);
auto validate_parent =
ValidateOperandLexicalScope(_, "Parent", inst, 8, ext_inst_name);
if (validate_parent != SPV_SUCCESS) return validate_parent;
if (num_words == 10) CHECK_OPERAND("Name", SpvOpString, 9);
break;
}
case OpenCLDebugInfo100DebugScope: {
auto validate_scope =
ValidateOperandLexicalScope(_, "Scope", inst, 5, ext_inst_name);
if (validate_scope != SPV_SUCCESS) return validate_scope;
if (num_words == 7) {
CHECK_DEBUG_OPERAND("Inlined At", OpenCLDebugInfo100DebugInlinedAt,
6);
}
break;
}
case OpenCLDebugInfo100DebugLocalVariable: {
CHECK_OPERAND("Name", SpvOpString, 5);
// TODO: We need a spec discussion that we have to allow local variable
// types to have template parameter.
auto validate_type =
ValidateOperandDebugType(_, "Type", inst, 6, ext_inst_name, true);
if (validate_type != SPV_SUCCESS) return validate_type;
CHECK_DEBUG_OPERAND("Source", OpenCLDebugInfo100DebugSource, 7);
auto validate_parent =
ValidateOperandLexicalScope(_, "Parent", inst, 10, ext_inst_name);
if (validate_parent != SPV_SUCCESS) return validate_parent;
break;
}
case OpenCLDebugInfo100DebugDeclare: {
CHECK_DEBUG_OPERAND("Local Variable",
OpenCLDebugInfo100DebugLocalVariable, 5);
auto* operand = _.FindDef(inst->word(6));
if (operand->opcode() != SpvOpVariable &&
operand->opcode() != SpvOpFunctionParameter) {
return _.diag(SPV_ERROR_INVALID_DATA, inst)
<< ext_inst_name() << ": "
<< "expected operand Variable must be a result id of "
"OpVariable or OpFunctionParameter";
}
CHECK_DEBUG_OPERAND("Expression", OpenCLDebugInfo100DebugExpression, 7);
break;
}
case OpenCLDebugInfo100DebugExpression: {
for (uint32_t word_index = 5; word_index < num_words; ++word_index) {
CHECK_DEBUG_OPERAND("Operation", OpenCLDebugInfo100DebugOperation,
word_index);
}
break;
}
case OpenCLDebugInfo100DebugTypeTemplate: {
if (!DoesDebugInfoOperandMatchExpectation(
_,
[](OpenCLDebugInfo100Instructions dbg_inst) {
return dbg_inst == OpenCLDebugInfo100DebugTypeComposite ||
dbg_inst == OpenCLDebugInfo100DebugFunction;
},
inst, 5)) {
return _.diag(SPV_ERROR_INVALID_DATA, inst)
<< ext_inst_name() << ": "
<< "expected operand Target must be DebugTypeComposite "
<< "or DebugFunction";
}
for (uint32_t word_index = 6; word_index < num_words; ++word_index) {
if (!DoesDebugInfoOperandMatchExpectation(
_,
[](OpenCLDebugInfo100Instructions dbg_inst) {
return dbg_inst ==
OpenCLDebugInfo100DebugTypeTemplateParameter ||
dbg_inst ==
OpenCLDebugInfo100DebugTypeTemplateTemplateParameter;
},
inst, word_index)) {
return _.diag(SPV_ERROR_INVALID_DATA, inst)
<< ext_inst_name() << ": "
<< "expected operand Parameters must be "
<< "DebugTypeTemplateParameter or "
<< "DebugTypeTemplateTemplateParameter";
}
}
break;
}
case OpenCLDebugInfo100DebugTypeTemplateParameter: {
CHECK_OPERAND("Name", SpvOpString, 5);
auto validate_actual_type = ValidateOperandDebugType(
_, "Actual Type", inst, 6, ext_inst_name, false);
if (validate_actual_type != SPV_SUCCESS) return validate_actual_type;
if (!DoesDebugInfoOperandMatchExpectation(
_,
[](OpenCLDebugInfo100Instructions dbg_inst) {
return dbg_inst == OpenCLDebugInfo100DebugInfoNone;
},
inst, 7)) {
CHECK_OPERAND("Value", SpvOpConstant, 7);
}
CHECK_DEBUG_OPERAND("Source", OpenCLDebugInfo100DebugSource, 8);
break;
}
case OpenCLDebugInfo100DebugGlobalVariable: {
CHECK_OPERAND("Name", SpvOpString, 5);
auto validate_type =
ValidateOperandDebugType(_, "Type", inst, 6, ext_inst_name, false);
if (validate_type != SPV_SUCCESS) return validate_type;
CHECK_DEBUG_OPERAND("Source", OpenCLDebugInfo100DebugSource, 7);
auto validate_scope =
ValidateOperandLexicalScope(_, "Scope", inst, 10, ext_inst_name);
if (validate_scope != SPV_SUCCESS) return validate_scope;
CHECK_OPERAND("Linkage Name", SpvOpString, 11);
if (!DoesDebugInfoOperandMatchExpectation(
_,
[](OpenCLDebugInfo100Instructions dbg_inst) {
return dbg_inst == OpenCLDebugInfo100DebugInfoNone;
},
inst, 12)) {
auto* operand = _.FindDef(inst->word(12));
if (operand->opcode() != SpvOpVariable &&
operand->opcode() != SpvOpConstant) {
return _.diag(SPV_ERROR_INVALID_DATA, inst)
<< ext_inst_name() << ": "
<< "expected operand Variable must be a result id of "
"OpVariable or OpConstant or DebugInfoNone";
}
}
if (num_words == 15) {
CHECK_DEBUG_OPERAND("Static Member Declaration",
OpenCLDebugInfo100DebugTypeMember, 14);
}
break;
}
case OpenCLDebugInfo100DebugInlinedAt: {
auto validate_scope =
ValidateOperandLexicalScope(_, "Scope", inst, 6, ext_inst_name);
if (validate_scope != SPV_SUCCESS) return validate_scope;
if (num_words == 8) {
CHECK_DEBUG_OPERAND("Inlined", OpenCLDebugInfo100DebugInlinedAt, 7);
}
break;
}
case OpenCLDebugInfo100DebugValue: {
CHECK_DEBUG_OPERAND("Local Variable",
OpenCLDebugInfo100DebugLocalVariable, 5);
CHECK_DEBUG_OPERAND("Expression", OpenCLDebugInfo100DebugExpression, 7);
for (uint32_t word_index = 8; word_index < num_words; ++word_index) {
// TODO: The following code simply checks if it is a const int scalar
// or a DebugLocalVariable or DebugGlobalVariable, but we have to
// check it using the same validation for Indexes of OpAccessChain.
if (!IsConstWithIntScalarType(_, inst, word_index) &&
!IsDebugVariableWithIntScalarType(_, inst, word_index)) {
return _.diag(SPV_ERROR_INVALID_DATA, inst)
<< ext_inst_name() << ": expected operand Indexes is "
<< "OpConstant, DebugGlobalVariable, or "
<< "type is OpConstant with an integer scalar type";
}
}
break;
}
// TODO: Add validation rules for remaining cases as well.
case OpenCLDebugInfo100DebugTypePtrToMember:
case OpenCLDebugInfo100DebugTypeTemplateTemplateParameter:
case OpenCLDebugInfo100DebugTypeTemplateParameterPack:
case OpenCLDebugInfo100DebugLexicalBlockDiscriminator:
case OpenCLDebugInfo100DebugInlinedVariable:
case OpenCLDebugInfo100DebugMacroDef:
case OpenCLDebugInfo100DebugMacroUndef:
case OpenCLDebugInfo100DebugImportedEntity:
break;
case OpenCLDebugInfo100InstructionsMax:
assert(0);
break;
}
} else if (ext_inst_type == SPV_EXT_INST_TYPE_NONSEMANTIC_CLSPVREFLECTION) {
auto import_inst = _.FindDef(inst->GetOperandAs<uint32_t>(2));
const std::string name(reinterpret_cast<const char*>(
import_inst->words().data() + import_inst->operands()[1].offset));
const std::string reflection = "NonSemantic.ClspvReflection.";
char* end_ptr;
auto version_string = name.substr(reflection.size());
if (version_string.empty()) {
return _.diag(SPV_ERROR_INVALID_DATA, import_inst)
<< "Missing NonSemantic.ClspvReflection import version";
}
uint32_t version = static_cast<uint32_t>(
std::strtoul(version_string.c_str(), &end_ptr, 10));
if (end_ptr && *end_ptr != '\0') {
return _.diag(SPV_ERROR_INVALID_DATA, import_inst)
<< "NonSemantic.ClspvReflection import does not encode the "
"version correctly";
}
if (version == 0 || version > NonSemanticClspvReflectionRevision) {
return _.diag(SPV_ERROR_INVALID_DATA, import_inst)
<< "Unknown NonSemantic.ClspvReflection import version";
}
return ValidateClspvReflectionInstruction(_, inst, version);
}
return SPV_SUCCESS;
}
spv_result_t ExtensionPass(ValidationState_t& _, const Instruction* inst) {
const SpvOp opcode = inst->opcode();
if (opcode == SpvOpExtension) return ValidateExtension(_, inst);
if (opcode == SpvOpExtInstImport) return ValidateExtInstImport(_, inst);
if (opcode == SpvOpExtInst) return ValidateExtInst(_, inst);
return SPV_SUCCESS;
}
} // namespace val
} // namespace spvtools