From cf85ad1429de560eb1569cf6b36ba5a4ae5ff4be Mon Sep 17 00:00:00 2001 From: Andrey Tuganov Date: Thu, 7 Sep 2017 17:27:57 -0400 Subject: [PATCH] Add validate logicals pass to the validator New pass checks operands of all instructions listed under 3.32.15. Relational and Logical Instructions --- source/CMakeLists.txt | 5 +- source/validate.cpp | 1 + source/validate.h | 4 + source/validate_logicals.cpp | 287 ++++++++++++++ test/val/CMakeLists.txt | 6 + test/val/val_cfg_test.cpp | 56 +-- test/val/val_id_test.cpp | 16 +- test/val/val_logicals_test.cpp | 851 +++++++++++++++++++++++++++++++++++++++++ test/val/val_ssa_test.cpp | 8 +- 9 files changed, 1196 insertions(+), 38 deletions(-) create mode 100644 source/validate_logicals.cpp create mode 100644 test/val/val_logicals_test.cpp diff --git a/source/CMakeLists.txt b/source/CMakeLists.txt index 04f23a3..9816810 100644 --- a/source/CMakeLists.txt +++ b/source/CMakeLists.txt @@ -255,11 +255,12 @@ set(SPIRV_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/validate_arithmetics.cpp ${CMAKE_CURRENT_SOURCE_DIR}/validate_cfg.cpp ${CMAKE_CURRENT_SOURCE_DIR}/validate_capability.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/validate_id.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/validate_instruction.cpp ${CMAKE_CURRENT_SOURCE_DIR}/validate_datarules.cpp ${CMAKE_CURRENT_SOURCE_DIR}/validate_decorations.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/validate_id.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/validate_instruction.cpp ${CMAKE_CURRENT_SOURCE_DIR}/validate_layout.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/validate_logicals.cpp ${CMAKE_CURRENT_SOURCE_DIR}/validate_type_unique.cpp ${CMAKE_CURRENT_SOURCE_DIR}/val/decoration.h ${CMAKE_CURRENT_SOURCE_DIR}/val/basic_block.cpp diff --git a/source/validate.cpp b/source/validate.cpp index 661da12..bea8bf1 100644 --- a/source/validate.cpp +++ b/source/validate.cpp @@ -181,6 +181,7 @@ spv_result_t ProcessInstruction(void* user_data, if (auto error = InstructionPass(_, inst)) return error; if (auto error = TypeUniquePass(_, inst)) return error; if (auto error = ArithmeticsPass(_, inst)) return error; + if (auto error = LogicalsPass(_, inst)) return error; return SPV_SUCCESS; } diff --git a/source/validate.h b/source/validate.h index 7d0466e..e5c47d5 100644 --- a/source/validate.h +++ b/source/validate.h @@ -115,6 +115,10 @@ spv_result_t TypeUniquePass(ValidationState_t& _, spv_result_t ArithmeticsPass(ValidationState_t& _, const spv_parsed_instruction_t* inst); +/// Validates correctness of logical instructions. +spv_result_t LogicalsPass(ValidationState_t& _, + const spv_parsed_instruction_t* inst); + // Validates that capability declarations use operands allowed in the current // context. spv_result_t CapabilityPass(ValidationState_t& _, diff --git a/source/validate_logicals.cpp b/source/validate_logicals.cpp new file mode 100644 index 0000000..de08b2b --- /dev/null +++ b/source/validate_logicals.cpp @@ -0,0 +1,287 @@ +// Copyright (c) 2017 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Validates correctness of logical SPIR-V instructions. + +#include "validate.h" + +#include "diagnostic.h" +#include "opcode.h" +#include "val/instruction.h" +#include "val/validation_state.h" + +namespace libspirv { + +namespace { + +// Returns operand word for given instruction and operand index. +// The operand is expected to only have one word. +inline uint32_t GetOperandWord(const spv_parsed_instruction_t* inst, + size_t operand_index) { + assert(operand_index < inst->num_operands); + const spv_parsed_operand_t& operand = inst->operands[operand_index]; + assert(operand.num_words == 1); + return inst->words[operand.offset]; +} + +// Returns the type id of instruction operand at |operand_index|. +// The operand is expected to be an id. +inline uint32_t GetOperandTypeId(ValidationState_t& _, + const spv_parsed_instruction_t* inst, + size_t operand_index) { + return _.GetTypeId(GetOperandWord(inst, operand_index)); +} + +} + +// Validates correctness of logical instructions. +spv_result_t LogicalsPass(ValidationState_t& _, + const spv_parsed_instruction_t* inst) { + const SpvOp opcode = static_cast(inst->opcode); + const uint32_t result_type = inst->type_id; + + switch (opcode) { + case SpvOpAny: + case SpvOpAll: { + if (!_.IsBoolScalarType(result_type)) + return _.diag(SPV_ERROR_INVALID_DATA) + << "Expected bool scalar type as Result Type: " + << spvOpcodeString(opcode); + + const uint32_t vector_type = GetOperandTypeId(_, inst, 2); + if (!vector_type || !_.IsBoolVectorType(vector_type)) + return _.diag(SPV_ERROR_INVALID_DATA) + << "Expected operand to be vector bool: " + << spvOpcodeString(opcode); + + break; + } + + case SpvOpIsNan: + case SpvOpIsInf: + case SpvOpIsFinite: + case SpvOpIsNormal: + case SpvOpSignBitSet: { + if (!_.IsBoolScalarType(result_type) && + !_.IsBoolVectorType(result_type)) + return _.diag(SPV_ERROR_INVALID_DATA) + << "Expected bool scalar or vector type as Result Type: " + << spvOpcodeString(opcode); + + const uint32_t operand_type = GetOperandTypeId(_, inst, 2); + if (!operand_type || (!_.IsFloatScalarType(operand_type) && + !_.IsFloatVectorType(operand_type))) + return _.diag(SPV_ERROR_INVALID_DATA) + << "Expected operand to be scalar or vector float: " + << spvOpcodeString(opcode); + + if (_.GetDimension(result_type) != _.GetDimension(operand_type)) + return _.diag(SPV_ERROR_INVALID_DATA) + << "Expected vector sizes of Result Type and the operand to be equal: " + << spvOpcodeString(opcode); + + break; + } + + + case SpvOpFOrdEqual: + case SpvOpFUnordEqual: + case SpvOpFOrdNotEqual: + case SpvOpFUnordNotEqual: + case SpvOpFOrdLessThan: + case SpvOpFUnordLessThan: + case SpvOpFOrdGreaterThan: + case SpvOpFUnordGreaterThan: + case SpvOpFOrdLessThanEqual: + case SpvOpFUnordLessThanEqual: + case SpvOpFOrdGreaterThanEqual: + case SpvOpFUnordGreaterThanEqual: + case SpvOpLessOrGreater: + case SpvOpOrdered: + case SpvOpUnordered: { + if (!_.IsBoolScalarType(result_type) && + !_.IsBoolVectorType(result_type)) + return _.diag(SPV_ERROR_INVALID_DATA) + << "Expected bool scalar or vector type as Result Type: " + << spvOpcodeString(opcode); + + const uint32_t left_operand_type = GetOperandTypeId(_, inst, 2); + if (!left_operand_type || (!_.IsFloatScalarType(left_operand_type) && + !_.IsFloatVectorType(left_operand_type))) + return _.diag(SPV_ERROR_INVALID_DATA) + << "Expected operands to be scalar or vector float: " + << spvOpcodeString(opcode); + + if (_.GetDimension(result_type) != _.GetDimension(left_operand_type)) + return _.diag(SPV_ERROR_INVALID_DATA) + << "Expected vector sizes of Result Type and the operands to be equal: " + << spvOpcodeString(opcode); + + if (left_operand_type != GetOperandTypeId(_, inst, 3)) + return _.diag(SPV_ERROR_INVALID_DATA) + << "Expected left and right operands to have the same type: " + << spvOpcodeString(opcode); + + break; + } + + case SpvOpLogicalEqual: + case SpvOpLogicalNotEqual: + case SpvOpLogicalOr: + case SpvOpLogicalAnd: { + if (!_.IsBoolScalarType(result_type) && + !_.IsBoolVectorType(result_type)) + return _.diag(SPV_ERROR_INVALID_DATA) + << "Expected bool scalar or vector type as Result Type: " + << spvOpcodeString(opcode); + + if (result_type != GetOperandTypeId(_, inst, 2) || + result_type != GetOperandTypeId(_, inst, 3)) + return _.diag(SPV_ERROR_INVALID_DATA) + << "Expected both operands to be of Result Type: " + << spvOpcodeString(opcode); + + break; + } + + case SpvOpLogicalNot: { + if (!_.IsBoolScalarType(result_type) && + !_.IsBoolVectorType(result_type)) + return _.diag(SPV_ERROR_INVALID_DATA) + << "Expected bool scalar or vector type as Result Type: " + << spvOpcodeString(opcode); + + if (result_type != GetOperandTypeId(_, inst, 2)) + return _.diag(SPV_ERROR_INVALID_DATA) + << "Expected operand to be of Result Type: " + << spvOpcodeString(opcode); + + break; + } + + case SpvOpSelect: { + uint32_t dimension = 1; + { + const Instruction* type_inst = _.FindDef(result_type); + assert(type_inst); + + const SpvOp type_opcode = type_inst->opcode(); + switch (type_opcode) { + case SpvOpTypePointer: { + if (!_.features().variable_pointers) + return _.diag(SPV_ERROR_INVALID_DATA) + << "Using pointers with OpSelect requires capability " + << "VariablePointers"; + break; + } + + case SpvOpTypeVector: { + dimension = type_inst->word(3); + break; + } + + case SpvOpTypeBool: + case SpvOpTypeInt: + case SpvOpTypeFloat: { + break; + } + + default: { + return _.diag(SPV_ERROR_INVALID_DATA) + << "Expected scalar or vector type as Result Type: " + << spvOpcodeString(opcode); + } + } + } + + const uint32_t condition_type = GetOperandTypeId(_, inst, 2); + const uint32_t left_type = GetOperandTypeId(_, inst, 3); + const uint32_t right_type = GetOperandTypeId(_, inst, 4); + + if (!condition_type || (!_.IsBoolScalarType(condition_type) && + !_.IsBoolVectorType(condition_type))) + return _.diag(SPV_ERROR_INVALID_DATA) + << "Expected bool scalar or vector type as condition: " + << spvOpcodeString(opcode); + + if (_.GetDimension(condition_type) != dimension) + return _.diag(SPV_ERROR_INVALID_DATA) + << "Expected vector sizes of Result Type and the condition to be" + << " equal: " << spvOpcodeString(opcode); + + if (result_type != left_type || result_type != right_type) + return _.diag(SPV_ERROR_INVALID_DATA) + << "Expected both objects to be of Result Type: " + << spvOpcodeString(opcode); + + break; + } + + case SpvOpIEqual: + case SpvOpINotEqual: + case SpvOpUGreaterThan: + case SpvOpUGreaterThanEqual: + case SpvOpULessThan: + case SpvOpULessThanEqual: + case SpvOpSGreaterThan: + case SpvOpSGreaterThanEqual: + case SpvOpSLessThan: + case SpvOpSLessThanEqual: { + if (!_.IsBoolScalarType(result_type) && + !_.IsBoolVectorType(result_type)) + return _.diag(SPV_ERROR_INVALID_DATA) + << "Expected bool scalar or vector type as Result Type: " + << spvOpcodeString(opcode); + + const uint32_t left_type = GetOperandTypeId(_, inst, 2); + const uint32_t right_type = GetOperandTypeId(_, inst, 3); + + if (!left_type || (!_.IsIntScalarType(left_type) && + !_.IsIntVectorType(left_type))) + return _.diag(SPV_ERROR_INVALID_DATA) + << "Expected operands to be scalar or vector int: " + << spvOpcodeString(opcode); + + if (_.GetDimension(result_type) != _.GetDimension(left_type)) + return _.diag(SPV_ERROR_INVALID_DATA) + << "Expected vector sizes of Result Type and the operands to be" + << " equal: " << spvOpcodeString(opcode); + + if (!right_type || (!_.IsIntScalarType(right_type) && + !_.IsIntVectorType(right_type))) + return _.diag(SPV_ERROR_INVALID_DATA) + << "Expected operands to be scalar or vector int: " + << spvOpcodeString(opcode); + + if (_.GetDimension(result_type) != _.GetDimension(right_type)) + return _.diag(SPV_ERROR_INVALID_DATA) + << "Expected vector sizes of Result Type and the operands to be" + << " equal: " << spvOpcodeString(opcode); + + if (_.GetBitWidth(left_type) != _.GetBitWidth(right_type)) + return _.diag(SPV_ERROR_INVALID_DATA) + << "Expected both operands to have the same component bit width: " + << spvOpcodeString(opcode); + + break; + } + + default: + break; + } + + return SPV_SUCCESS; +} + +} // namespace libspirv diff --git a/test/val/CMakeLists.txt b/test/val/CMakeLists.txt index 457b766..c9aba40 100644 --- a/test/val/CMakeLists.txt +++ b/test/val/CMakeLists.txt @@ -81,6 +81,12 @@ add_spvtools_unittest(TARGET val_arithmetics LIBS ${SPIRV_TOOLS} ) +add_spvtools_unittest(TARGET val_logicals + SRCS val_logicals_test.cpp + ${VAL_TEST_COMMON_SRCS} + LIBS ${SPIRV_TOOLS} +) + add_spvtools_unittest(TARGET val_limits SRCS val_limits_test.cpp ${VAL_TEST_COMMON_SRCS} diff --git a/test/val/val_cfg_test.cpp b/test/val/val_cfg_test.cpp index 01d92fa..45dac44 100644 --- a/test/val/val_cfg_test.cpp +++ b/test/val/val_cfg_test.cpp @@ -273,7 +273,7 @@ TEST_P(ValidateCFG, Simple) { Block cont("cont"); Block merge("merge", SpvOpReturn); - entry.SetBody("%cond = OpSLessThan %intt %one %two\n"); + entry.SetBody("%cond = OpSLessThan %boolt %one %two\n"); if (is_shader) { loop.SetBody("OpLoopMerge %merge %cont None\n"); } @@ -340,7 +340,7 @@ TEST_P(ValidateCFG, BlockSelfLoopIsOk) { Block loop("loop", SpvOpBranchConditional); Block merge("merge", SpvOpReturn); - entry.SetBody("%cond = OpSLessThan %intt %one %two\n"); + entry.SetBody("%cond = OpSLessThan %boolt %one %two\n"); if (is_shader) loop.SetBody("OpLoopMerge %merge %loop None\n"); string str = header(GetParam()) + @@ -364,7 +364,7 @@ TEST_P(ValidateCFG, BlockAppearsBeforeDominatorBad) { Block branch("branch", SpvOpBranchConditional); Block merge("merge", SpvOpReturn); - entry.SetBody("%cond = OpSLessThan %intt %one %two\n"); + entry.SetBody("%cond = OpSLessThan %boolt %one %two\n"); if (is_shader) branch.SetBody("OpSelectionMerge %merge None\n"); string str = header(GetParam()) + @@ -391,7 +391,7 @@ TEST_P(ValidateCFG, MergeBlockTargetedByMultipleHeaderBlocksBad) { Block selection("selection", SpvOpBranchConditional); Block merge("merge", SpvOpReturn); - entry.SetBody("%cond = OpSLessThan %intt %one %two\n"); + entry.SetBody("%cond = OpSLessThan %boolt %one %two\n"); if (is_shader) loop.SetBody(" OpLoopMerge %merge %loop None\n"); // cannot share the same merge @@ -425,7 +425,7 @@ TEST_P(ValidateCFG, MergeBlockTargetedByMultipleHeaderBlocksSelectionBad) { Block selection("selection", SpvOpBranchConditional); Block merge("merge", SpvOpReturn); - entry.SetBody("%cond = OpSLessThan %intt %one %two\n"); + entry.SetBody("%cond = OpSLessThan %boolt %one %two\n"); if (is_shader) selection.SetBody(" OpSelectionMerge %merge None\n"); // cannot share the same merge @@ -477,7 +477,7 @@ TEST_P(ValidateCFG, BranchConditionalTrueTargetFirstBlockBad) { Block bad("bad", SpvOpBranchConditional); Block exit("exit", SpvOpReturn); - entry.SetBody("%cond = OpSLessThan %intt %one %two\n"); + entry.SetBody("%cond = OpSLessThan %boolt %one %two\n"); bad.SetBody(" OpLoopMerge %entry %exit None\n"); string str = header(GetParam()) + @@ -503,7 +503,7 @@ TEST_P(ValidateCFG, BranchConditionalFalseTargetFirstBlockBad) { Block merge("merge"); Block end("end", SpvOpReturn); - entry.SetBody("%cond = OpSLessThan %intt %one %two\n"); + entry.SetBody("%cond = OpSLessThan %boolt %one %two\n"); bad.SetBody("OpLoopMerge %merge %cont None\n"); string str = header(GetParam()) + @@ -533,7 +533,7 @@ TEST_P(ValidateCFG, SwitchTargetFirstBlockBad) { Block merge("merge"); Block end("end", SpvOpReturn); - entry.SetBody("%cond = OpSLessThan %intt %one %two\n"); + entry.SetBody("%cond = OpSLessThan %boolt %one %two\n"); bad.SetBody("OpSelectionMerge %merge None\n"); string str = header(GetParam()) + @@ -562,7 +562,7 @@ TEST_P(ValidateCFG, BranchToBlockInOtherFunctionBad) { Block middle("middle", SpvOpBranchConditional); Block end("end", SpvOpReturn); - entry.SetBody("%cond = OpSLessThan %intt %one %two\n"); + entry.SetBody("%cond = OpSLessThan %boolt %one %two\n"); middle.SetBody("OpSelectionMerge %end None\n"); Block entry2("entry2"); @@ -599,7 +599,7 @@ TEST_P(ValidateCFG, HeaderDoesntDominatesMergeBad) { Block f("f"); Block merge("merge", SpvOpReturn); - head.SetBody("%cond = OpSLessThan %intt %one %two\n"); + head.SetBody("%cond = OpSLessThan %boolt %one %two\n"); if (is_shader) head.AppendBody("OpSelectionMerge %merge None\n"); @@ -633,7 +633,7 @@ TEST_P(ValidateCFG, HeaderDoesntStrictlyDominateMergeBad) { Block head("head", SpvOpBranchConditional); Block exit("exit", SpvOpReturn); - head.SetBody("%cond = OpSLessThan %intt %one %two\n"); + head.SetBody("%cond = OpSLessThan %boolt %one %two\n"); if (is_shader) head.AppendBody("OpSelectionMerge %head None\n"); @@ -666,7 +666,7 @@ TEST_P(ValidateCFG, UnreachableMerge) { Block f("f", SpvOpReturn); Block merge("merge", SpvOpReturn); - entry.SetBody("%cond = OpSLessThan %intt %one %two\n"); + entry.SetBody("%cond = OpSLessThan %boolt %one %two\n"); if (is_shader) branch.AppendBody("OpSelectionMerge %merge None\n"); string str = header(GetParam()) + @@ -692,7 +692,7 @@ TEST_P(ValidateCFG, UnreachableMergeDefinedByOpUnreachable) { Block f("f", SpvOpReturn); Block merge("merge", SpvOpUnreachable); - entry.SetBody("%cond = OpSLessThan %intt %one %two\n"); + entry.SetBody("%cond = OpSLessThan %boolt %one %two\n"); if (is_shader) branch.AppendBody("OpSelectionMerge %merge None\n"); string str = header(GetParam()) + @@ -737,7 +737,7 @@ TEST_P(ValidateCFG, UnreachableBranch) { Block merge("merge"); Block exit("exit", SpvOpReturn); - unreachable.SetBody("%cond = OpSLessThan %intt %one %two\n"); + unreachable.SetBody("%cond = OpSLessThan %boolt %one %two\n"); if (is_shader) unreachable.AppendBody("OpSelectionMerge %merge None\n"); string str = header(GetParam()) + nameOps("unreachable", "exit", make_pair("func", "Main")) + @@ -772,7 +772,7 @@ TEST_P(ValidateCFG, SingleBlockLoop) { Block loop("loop", SpvOpBranchConditional); Block exit("exit", SpvOpReturn); - entry.SetBody("%cond = OpSLessThan %intt %one %two\n"); + entry.SetBody("%cond = OpSLessThan %boolt %one %two\n"); if (is_shader) loop.AppendBody("OpLoopMerge %exit %loop None\n"); string str = header(GetParam()) + string(types_consts()) + @@ -798,7 +798,7 @@ TEST_P(ValidateCFG, NestedLoops) { Block loop1_merge("loop1_merge"); Block exit("exit", SpvOpReturn); - entry.SetBody("%cond = OpSLessThan %intt %one %two\n"); + entry.SetBody("%cond = OpSLessThan %boolt %one %two\n"); if (is_shader) { loop1.SetBody("OpLoopMerge %loop1_merge %loop2 None\n"); loop2.SetBody("OpLoopMerge %loop2_merge %loop2 None\n"); @@ -828,7 +828,7 @@ TEST_P(ValidateCFG, NestedSelection) { vector merge_blocks; Block inner("inner"); - entry.SetBody("%cond = OpSLessThan %intt %one %two\n"); + entry.SetBody("%cond = OpSLessThan %boolt %one %two\n"); if_blocks.emplace_back("if0", SpvOpBranchConditional); @@ -871,7 +871,7 @@ TEST_P(ValidateCFG, BackEdgeBlockDoesntPostDominateContinueTargetBad) { Block be_block("be_block"); Block exit("exit", SpvOpReturn); - entry.SetBody("%cond = OpSLessThan %intt %one %two\n"); + entry.SetBody("%cond = OpSLessThan %boolt %one %two\n"); if (is_shader) { loop1.SetBody("OpLoopMerge %exit %loop2_merge None\n"); loop2.SetBody("OpLoopMerge %loop2_merge %loop2 None\n"); @@ -909,7 +909,7 @@ TEST_P(ValidateCFG, BranchingToNonLoopHeaderBlockBad) { Block f("f"); Block exit("exit", SpvOpReturn); - entry.SetBody("%cond = OpSLessThan %intt %one %two\n"); + entry.SetBody("%cond = OpSLessThan %boolt %one %two\n"); if (is_shader) split.SetBody("OpSelectionMerge %exit None\n"); string str = header(GetParam()) + nameOps("split", "f") + types_consts() + @@ -940,7 +940,7 @@ TEST_P(ValidateCFG, BranchingToSameNonLoopHeaderBlockBad) { Block split("split", SpvOpBranchConditional); Block exit("exit", SpvOpReturn); - entry.SetBody("%cond = OpSLessThan %intt %one %two\n"); + entry.SetBody("%cond = OpSLessThan %boolt %one %two\n"); if (is_shader) split.SetBody("OpSelectionMerge %exit None\n"); string str = header(GetParam()) + nameOps("split") + types_consts() + @@ -971,7 +971,7 @@ TEST_P(ValidateCFG, MultipleBackEdgeBlocksToLoopHeaderBad) { Block back1("back1"); Block merge("merge", SpvOpReturn); - entry.SetBody("%cond = OpSLessThan %intt %one %two\n"); + entry.SetBody("%cond = OpSLessThan %boolt %one %two\n"); if (is_shader) loop.SetBody("OpLoopMerge %merge %back0 None\n"); string str = header(GetParam()) + nameOps("loop", "back0", "back1") + @@ -1006,7 +1006,7 @@ TEST_P(ValidateCFG, ContinueTargetMustBePostDominatedByBackEdge) { Block merge("merge", SpvOpReturn); Block exit("exit", SpvOpReturn); - entry.SetBody("%cond = OpSLessThan %intt %one %two\n"); + entry.SetBody("%cond = OpSLessThan %boolt %one %two\n"); if (is_shader) loop.SetBody("OpLoopMerge %merge %cheader None\n"); string str = header(GetParam()) + nameOps("cheader", "be_block") + @@ -1039,7 +1039,7 @@ TEST_P(ValidateCFG, BranchOutOfConstructToMergeBad) { Block cont("cont", SpvOpBranchConditional); Block merge("merge", SpvOpReturn); - entry.SetBody("%cond = OpSLessThan %intt %one %two\n"); + entry.SetBody("%cond = OpSLessThan %boolt %one %two\n"); if (is_shader) loop.SetBody("OpLoopMerge %merge %loop None\n"); string str = header(GetParam()) + nameOps("cont", "loop") + types_consts() + @@ -1072,7 +1072,7 @@ TEST_P(ValidateCFG, BranchOutOfConstructBad) { Block merge("merge"); Block exit("exit", SpvOpReturn); - entry.SetBody("%cond = OpSLessThan %intt %one %two\n"); + entry.SetBody("%cond = OpSLessThan %boolt %one %two\n"); if (is_shader) loop.SetBody("OpLoopMerge %merge %loop None\n"); string str = header(GetParam()) + nameOps("cont", "loop") + types_consts() + @@ -1216,7 +1216,7 @@ TEST_P(ValidateCFG, Block inner_merge("inner_merge"); Block exit("exit", SpvOpReturn); - entry.SetBody("%cond = OpSLessThan %intt %one %two\n"); + entry.SetBody("%cond = OpSLessThan %boolt %one %two\n"); if (is_shader) { entry.AppendBody("OpSelectionMerge %exit None\n"); inner_head.SetBody("OpSelectionMerge %inner_merge None\n"); @@ -1252,7 +1252,7 @@ TEST_P(ValidateCFG, ContinueTargetCanBeMergeBlockForNestedStructureGood) { Block if_merge("if_merge", SpvOpBranchConditional); Block merge("merge", SpvOpReturn); - entry.SetBody("%cond = OpSLessThan %intt %one %two\n"); + entry.SetBody("%cond = OpSLessThan %boolt %one %two\n"); if (is_shader) { loop.SetBody("OpLoopMerge %merge %if_merge None\n"); if_head.SetBody("OpSelectionMerge %if_merge None\n"); @@ -1283,7 +1283,7 @@ TEST_P(ValidateCFG, SingleLatchBlockMultipleBranchesToLoopHeader) { Block latch("latch", SpvOpBranchConditional); Block merge("merge", SpvOpReturn); - entry.SetBody("%cond = OpSLessThan %intt %one %two\n"); + entry.SetBody("%cond = OpSLessThan %boolt %one %two\n"); if (is_shader) { loop.SetBody("OpLoopMerge %merge %latch None\n"); } @@ -1315,7 +1315,7 @@ TEST_P(ValidateCFG, SingleLatchBlockHeaderContinueTargetIsItselfGood) { Block latch("latch"); Block merge("merge", SpvOpReturn); - entry.SetBody("%cond = OpSLessThan %intt %one %two\n"); + entry.SetBody("%cond = OpSLessThan %boolt %one %two\n"); if (is_shader) { loop.SetBody("OpLoopMerge %merge %loop None\n"); } diff --git a/test/val/val_id_test.cpp b/test/val/val_id_test.cpp index e1bd577..f241c52 100644 --- a/test/val/val_id_test.cpp +++ b/test/val/val_id_test.cpp @@ -1878,7 +1878,9 @@ TEST_F(ValidateIdWithMessage, OpLoadVarPtrOpSelectGood) { // Without the VariablePointers Capability, OpLoad will not allow loading // through a variable pointer. -TEST_F(ValidateIdWithMessage, OpLoadVarPtrOpSelectBad) { +// Disabled since using OpSelect with pointers without VariablePointers will +// fail LogicalsPass. +TEST_F(ValidateIdWithMessage, DISABLED_OpLoadVarPtrOpSelectBad) { std::string result_strategy = R"( %isneg = OpSLessThan %bool %i %zero %varptr = OpSelect %f32ptr %isneg %ptr1 %ptr2 @@ -2086,7 +2088,9 @@ TEST_F(ValidateIdWithMessage, OpStoreLogicalPointerBad) { // Without the VariablePointer Capability, OpStore should may not store // through a variable pointer. -TEST_F(ValidateIdWithMessage, OpStoreVarPtrBad) { +// Disabled since using OpSelect with pointers without VariablePointers will +// fail LogicalsPass. +TEST_F(ValidateIdWithMessage, DISABLED_OpStoreVarPtrBad) { std::string result_strategy = R"( %isneg = OpSLessThan %bool %i %zero %varptr = OpSelect %f32ptr %isneg %ptr1 %ptr2 @@ -3134,7 +3138,9 @@ OpFunctionEnd)"; // However, the OpSelect validation does not catch this today. Therefore, it is // caught by the OpSampledImage validation. If the OpSelect validation code is // updated, the error message for this test may change. -TEST_F(ValidateIdWithMessage, OpSampledImageUsedInOpSelectBad) { +// +// Disabled since OpSelect catches this now. +TEST_F(ValidateIdWithMessage, DISABLED_OpSampledImageUsedInOpSelectBad) { string spirv = kGLSL450MemoryModel + sampledImageSetup + R"( %smpld_img = OpSampledImage %sampled_image_type %image_inst %sampler_inst %select_img = OpSelect %sampled_image_type %spec_true %smpld_img %smpld_img @@ -3820,7 +3826,9 @@ TEST_F(ValidateIdWithMessage, OpReturnValueVarPtrGood) { // Without the VariablePointer Capability, the return value of a function is // *not* allowed to be a pointer. -TEST_F(ValidateIdWithMessage, OpReturnValueVarPtrBad) { +// Disabled since using OpSelect with pointers without VariablePointers will +// fail LogicalsPass. +TEST_F(ValidateIdWithMessage, DISABLED_OpReturnValueVarPtrBad) { std::ostringstream spirv; createVariablePointerSpirvProgram(&spirv, "" /* Instructions to add to "main" */, diff --git a/test/val/val_logicals_test.cpp b/test/val/val_logicals_test.cpp new file mode 100644 index 0000000..bf1ceae --- /dev/null +++ b/test/val/val_logicals_test.cpp @@ -0,0 +1,851 @@ +// Copyright (c) 2017 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Tests for unique type declaration rules validator. + +#include + +#include "gmock/gmock.h" +#include "unit_spirv.h" +#include "val_fixtures.h" + +namespace { + +using ::testing::HasSubstr; +using ::testing::Not; + +using ValidateLogicals = spvtest::ValidateBase; + +std::string GenerateShaderCode( + const std::string& body, + const std::string& capabilities_and_extensions = "") { + const std::string capabilities = +R"( +OpCapability Shader +OpCapability Int64 +OpCapability Float64)"; + + const std::string after_extension_before_body = +R"( +%ext_inst = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" +%void = OpTypeVoid +%func = OpTypeFunction %void +%bool = OpTypeBool +%f32 = OpTypeFloat 32 +%u32 = OpTypeInt 32 0 +%s32 = OpTypeInt 32 1 +%f64 = OpTypeFloat 64 +%u64 = OpTypeInt 64 0 +%s64 = OpTypeInt 64 1 +%boolvec2 = OpTypeVector %bool 2 +%s32vec2 = OpTypeVector %s32 2 +%u32vec2 = OpTypeVector %u32 2 +%u64vec2 = OpTypeVector %u64 2 +%f32vec2 = OpTypeVector %f32 2 +%f64vec2 = OpTypeVector %f64 2 +%boolvec3 = OpTypeVector %bool 3 +%u32vec3 = OpTypeVector %u32 3 +%u64vec3 = OpTypeVector %u64 3 +%s32vec3 = OpTypeVector %s32 3 +%f32vec3 = OpTypeVector %f32 3 +%f64vec3 = OpTypeVector %f64 3 +%boolvec4 = OpTypeVector %bool 4 +%u32vec4 = OpTypeVector %u32 4 +%u64vec4 = OpTypeVector %u64 4 +%s32vec4 = OpTypeVector %s32 4 +%f32vec4 = OpTypeVector %f32 4 +%f64vec4 = OpTypeVector %f64 4 + +%f32_0 = OpConstant %f32 0 +%f32_1 = OpConstant %f32 1 +%f32_2 = OpConstant %f32 2 +%f32_3 = OpConstant %f32 3 +%f32_4 = OpConstant %f32 4 + +%s32_0 = OpConstant %s32 0 +%s32_1 = OpConstant %s32 1 +%s32_2 = OpConstant %s32 2 +%s32_3 = OpConstant %s32 3 +%s32_4 = OpConstant %s32 4 +%s32_m1 = OpConstant %s32 -1 + +%u32_0 = OpConstant %u32 0 +%u32_1 = OpConstant %u32 1 +%u32_2 = OpConstant %u32 2 +%u32_3 = OpConstant %u32 3 +%u32_4 = OpConstant %u32 4 + +%f64_0 = OpConstant %f64 0 +%f64_1 = OpConstant %f64 1 +%f64_2 = OpConstant %f64 2 +%f64_3 = OpConstant %f64 3 +%f64_4 = OpConstant %f64 4 + +%s64_0 = OpConstant %s64 0 +%s64_1 = OpConstant %s64 1 +%s64_2 = OpConstant %s64 2 +%s64_3 = OpConstant %s64 3 +%s64_4 = OpConstant %s64 4 +%s64_m1 = OpConstant %s64 -1 + +%u64_0 = OpConstant %u64 0 +%u64_1 = OpConstant %u64 1 +%u64_2 = OpConstant %u64 2 +%u64_3 = OpConstant %u64 3 +%u64_4 = OpConstant %u64 4 + +%u32vec2_01 = OpConstantComposite %u32vec2 %u32_0 %u32_1 +%u32vec2_12 = OpConstantComposite %u32vec2 %u32_1 %u32_2 +%u32vec3_012 = OpConstantComposite %u32vec3 %u32_0 %u32_1 %u32_2 +%u32vec3_123 = OpConstantComposite %u32vec3 %u32_1 %u32_2 %u32_3 +%u32vec4_0123 = OpConstantComposite %u32vec4 %u32_0 %u32_1 %u32_2 %u32_3 +%u32vec4_1234 = OpConstantComposite %u32vec4 %u32_1 %u32_2 %u32_3 %u32_4 + +%s32vec2_01 = OpConstantComposite %s32vec2 %s32_0 %s32_1 +%s32vec2_12 = OpConstantComposite %s32vec2 %s32_1 %s32_2 +%s32vec3_012 = OpConstantComposite %s32vec3 %s32_0 %s32_1 %s32_2 +%s32vec3_123 = OpConstantComposite %s32vec3 %s32_1 %s32_2 %s32_3 +%s32vec4_0123 = OpConstantComposite %s32vec4 %s32_0 %s32_1 %s32_2 %s32_3 +%s32vec4_1234 = OpConstantComposite %s32vec4 %s32_1 %s32_2 %s32_3 %s32_4 + +%f32vec2_01 = OpConstantComposite %f32vec2 %f32_0 %f32_1 +%f32vec2_12 = OpConstantComposite %f32vec2 %f32_1 %f32_2 +%f32vec3_012 = OpConstantComposite %f32vec3 %f32_0 %f32_1 %f32_2 +%f32vec3_123 = OpConstantComposite %f32vec3 %f32_1 %f32_2 %f32_3 +%f32vec4_0123 = OpConstantComposite %f32vec4 %f32_0 %f32_1 %f32_2 %f32_3 +%f32vec4_1234 = OpConstantComposite %f32vec4 %f32_1 %f32_2 %f32_3 %f32_4 + +%f64vec2_01 = OpConstantComposite %f64vec2 %f64_0 %f64_1 +%f64vec2_12 = OpConstantComposite %f64vec2 %f64_1 %f64_2 +%f64vec3_012 = OpConstantComposite %f64vec3 %f64_0 %f64_1 %f64_2 +%f64vec3_123 = OpConstantComposite %f64vec3 %f64_1 %f64_2 %f64_3 +%f64vec4_0123 = OpConstantComposite %f64vec4 %f64_0 %f64_1 %f64_2 %f64_3 +%f64vec4_1234 = OpConstantComposite %f64vec4 %f64_1 %f64_2 %f64_3 %f64_4 + +%true = OpConstantTrue %bool +%false = OpConstantFalse %bool +%boolvec2_tf = OpConstantComposite %boolvec2 %true %false +%boolvec3_tft = OpConstantComposite %boolvec3 %true %false %true +%boolvec4_tftf = OpConstantComposite %boolvec4 %true %false %true %false + +%f32vec4ptr = OpTypePointer Function %f32vec4 + +%main = OpFunction %void None %func +%main_entry = OpLabel)"; + + const std::string after_body = +R"( +OpReturn +OpFunctionEnd)"; + + return capabilities + capabilities_and_extensions + + after_extension_before_body + body + after_body; +} + +std::string GenerateKernelCode( + const std::string& body, + const std::string& capabilities_and_extensions = "") { + const std::string capabilities = +R"( +OpCapability Addresses +OpCapability Kernel +OpCapability Linkage +OpCapability Int64 +OpCapability Float64)"; + + const std::string after_extension_before_body = +R"( +OpMemoryModel Physical32 OpenCL +%void = OpTypeVoid +%func = OpTypeFunction %void +%bool = OpTypeBool +%f32 = OpTypeFloat 32 +%u32 = OpTypeInt 32 0 +%f64 = OpTypeFloat 64 +%u64 = OpTypeInt 64 0 +%boolvec2 = OpTypeVector %bool 2 +%u32vec2 = OpTypeVector %u32 2 +%u64vec2 = OpTypeVector %u64 2 +%f32vec2 = OpTypeVector %f32 2 +%f64vec2 = OpTypeVector %f64 2 +%boolvec3 = OpTypeVector %bool 3 +%u32vec3 = OpTypeVector %u32 3 +%u64vec3 = OpTypeVector %u64 3 +%f32vec3 = OpTypeVector %f32 3 +%f64vec3 = OpTypeVector %f64 3 +%boolvec4 = OpTypeVector %bool 4 +%u32vec4 = OpTypeVector %u32 4 +%u64vec4 = OpTypeVector %u64 4 +%f32vec4 = OpTypeVector %f32 4 +%f64vec4 = OpTypeVector %f64 4 + +%f32_0 = OpConstant %f32 0 +%f32_1 = OpConstant %f32 1 +%f32_2 = OpConstant %f32 2 +%f32_3 = OpConstant %f32 3 +%f32_4 = OpConstant %f32 4 + +%u32_0 = OpConstant %u32 0 +%u32_1 = OpConstant %u32 1 +%u32_2 = OpConstant %u32 2 +%u32_3 = OpConstant %u32 3 +%u32_4 = OpConstant %u32 4 + +%f64_0 = OpConstant %f64 0 +%f64_1 = OpConstant %f64 1 +%f64_2 = OpConstant %f64 2 +%f64_3 = OpConstant %f64 3 +%f64_4 = OpConstant %f64 4 + +%u64_0 = OpConstant %u64 0 +%u64_1 = OpConstant %u64 1 +%u64_2 = OpConstant %u64 2 +%u64_3 = OpConstant %u64 3 +%u64_4 = OpConstant %u64 4 + +%u32vec2_01 = OpConstantComposite %u32vec2 %u32_0 %u32_1 +%u32vec2_12 = OpConstantComposite %u32vec2 %u32_1 %u32_2 +%u32vec3_012 = OpConstantComposite %u32vec3 %u32_0 %u32_1 %u32_2 +%u32vec3_123 = OpConstantComposite %u32vec3 %u32_1 %u32_2 %u32_3 +%u32vec4_0123 = OpConstantComposite %u32vec4 %u32_0 %u32_1 %u32_2 %u32_3 +%u32vec4_1234 = OpConstantComposite %u32vec4 %u32_1 %u32_2 %u32_3 %u32_4 + +%f32vec2_01 = OpConstantComposite %f32vec2 %f32_0 %f32_1 +%f32vec2_12 = OpConstantComposite %f32vec2 %f32_1 %f32_2 +%f32vec3_012 = OpConstantComposite %f32vec3 %f32_0 %f32_1 %f32_2 +%f32vec3_123 = OpConstantComposite %f32vec3 %f32_1 %f32_2 %f32_3 +%f32vec4_0123 = OpConstantComposite %f32vec4 %f32_0 %f32_1 %f32_2 %f32_3 +%f32vec4_1234 = OpConstantComposite %f32vec4 %f32_1 %f32_2 %f32_3 %f32_4 + +%f64vec2_01 = OpConstantComposite %f64vec2 %f64_0 %f64_1 +%f64vec2_12 = OpConstantComposite %f64vec2 %f64_1 %f64_2 +%f64vec3_012 = OpConstantComposite %f64vec3 %f64_0 %f64_1 %f64_2 +%f64vec3_123 = OpConstantComposite %f64vec3 %f64_1 %f64_2 %f64_3 +%f64vec4_0123 = OpConstantComposite %f64vec4 %f64_0 %f64_1 %f64_2 %f64_3 +%f64vec4_1234 = OpConstantComposite %f64vec4 %f64_1 %f64_2 %f64_3 %f64_4 + +%true = OpConstantTrue %bool +%false = OpConstantFalse %bool +%boolvec2_tf = OpConstantComposite %boolvec2 %true %false +%boolvec3_tft = OpConstantComposite %boolvec3 %true %false %true +%boolvec4_tftf = OpConstantComposite %boolvec4 %true %false %true %false + +%f32vec4ptr = OpTypePointer Function %f32vec4 + +%main = OpFunction %void None %func +%main_entry = OpLabel)"; + + const std::string after_body = +R"( +OpReturn +OpFunctionEnd)"; + + return capabilities + capabilities_and_extensions + + after_extension_before_body + body + after_body; +} + +TEST_F(ValidateLogicals, OpAnySuccess) { + const std::string body = R"( +%val1 = OpAny %bool %boolvec2_tf +%val2 = OpAny %bool %boolvec3_tft +%val3 = OpAny %bool %boolvec4_tftf +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateLogicals, OpAnyWrongTypeId) { + const std::string body = R"( +%val = OpAny %u32 %boolvec2_tf +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), HasSubstr( + "Expected bool scalar type as Result Type: Any")); +} + +TEST_F(ValidateLogicals, OpAnyWrongOperand) { + const std::string body = R"( +%val = OpAny %bool %u32vec3_123 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), HasSubstr( + "Expected operand to be vector bool: Any")); +} + +TEST_F(ValidateLogicals, OpIsNanSuccess) { + const std::string body = R"( +%val1 = OpIsNan %bool %f32_1 +%val2 = OpIsNan %bool %f64_0 +%val3 = OpIsNan %boolvec2 %f32vec2_12 +%val4 = OpIsNan %boolvec3 %f32vec3_123 +%val5 = OpIsNan %boolvec4 %f32vec4_1234 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateLogicals, OpIsNanWrongTypeId) { + const std::string body = R"( +%val1 = OpIsNan %u32 %f32_1 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), HasSubstr( + "Expected bool scalar or vector type as Result Type: IsNan")); +} + +TEST_F(ValidateLogicals, OpIsNanOperandNotFloat) { + const std::string body = R"( +%val1 = OpIsNan %bool %u32_1 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), HasSubstr( + "Expected operand to be scalar or vector float: IsNan")); +} + +TEST_F(ValidateLogicals, OpIsNanOperandWrongSize) { + const std::string body = R"( +%val1 = OpIsNan %bool %f32vec2_12 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), HasSubstr( + "Expected vector sizes of Result Type and the operand to be equal: " + "IsNan")); +} + +TEST_F(ValidateLogicals, OpLessOrGreaterSuccess) { + const std::string body = R"( +%val1 = OpLessOrGreater %bool %f32_0 %f32_1 +%val2 = OpLessOrGreater %bool %f64_0 %f64_0 +%val3 = OpLessOrGreater %boolvec2 %f32vec2_12 %f32vec2_12 +%val4 = OpLessOrGreater %boolvec3 %f32vec3_123 %f32vec3_123 +%val5 = OpLessOrGreater %boolvec4 %f32vec4_1234 %f32vec4_1234 +)"; + + CompileSuccessfully(GenerateKernelCode(body).c_str()); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateLogicals, OpLessOrGreaterWrongTypeId) { + const std::string body = R"( +%val1 = OpLessOrGreater %u32 %f32_1 %f32_1 +)"; + + CompileSuccessfully(GenerateKernelCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), HasSubstr( + "Expected bool scalar or vector type as Result Type: LessOrGreater")); +} + +TEST_F(ValidateLogicals, OpLessOrGreaterLeftOperandNotFloat) { + const std::string body = R"( +%val1 = OpLessOrGreater %bool %u32_1 %f32_1 +)"; + + CompileSuccessfully(GenerateKernelCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), HasSubstr( + "Expected operands to be scalar or vector float: LessOrGreater")); +} + +TEST_F(ValidateLogicals, OpLessOrGreaterLeftOperandWrongSize) { + const std::string body = R"( +%val1 = OpLessOrGreater %bool %f32vec2_12 %f32_1 +)"; + + CompileSuccessfully(GenerateKernelCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), HasSubstr( + "Expected vector sizes of Result Type and the operands to be equal: " + "LessOrGreater")); +} + +TEST_F(ValidateLogicals, OpLessOrGreaterOperandsDifferentType) { + const std::string body = R"( +%val1 = OpLessOrGreater %bool %f32_1 %f64_1 +)"; + + CompileSuccessfully(GenerateKernelCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), HasSubstr( + "Expected left and right operands to have the same type: " + "LessOrGreater")); +} + +TEST_F(ValidateLogicals, OpFOrdEqualSuccess) { + const std::string body = R"( +%val1 = OpFOrdEqual %bool %f32_0 %f32_1 +%val2 = OpFOrdEqual %bool %f64_0 %f64_0 +%val3 = OpFOrdEqual %boolvec2 %f32vec2_12 %f32vec2_12 +%val4 = OpFOrdEqual %boolvec3 %f32vec3_123 %f32vec3_123 +%val5 = OpFOrdEqual %boolvec4 %f32vec4_1234 %f32vec4_1234 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateLogicals, OpFOrdEqualWrongTypeId) { + const std::string body = R"( +%val1 = OpFOrdEqual %u32 %f32_1 %f32_1 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), HasSubstr( + "Expected bool scalar or vector type as Result Type: FOrdEqual")); +} + +TEST_F(ValidateLogicals, OpFOrdEqualLeftOperandNotFloat) { + const std::string body = R"( +%val1 = OpFOrdEqual %bool %u32_1 %f32_1 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), HasSubstr( + "Expected operands to be scalar or vector float: FOrdEqual")); +} + +TEST_F(ValidateLogicals, OpFOrdEqualLeftOperandWrongSize) { + const std::string body = R"( +%val1 = OpFOrdEqual %bool %f32vec2_12 %f32_1 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), HasSubstr( + "Expected vector sizes of Result Type and the operands to be equal: " + "FOrdEqual")); +} + +TEST_F(ValidateLogicals, OpFOrdEqualOperandsDifferentType) { + const std::string body = R"( +%val1 = OpFOrdEqual %bool %f32_1 %f64_1 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), HasSubstr( + "Expected left and right operands to have the same type: " + "FOrdEqual")); +} + +TEST_F(ValidateLogicals, OpLogicalEqualSuccess) { + const std::string body = R"( +%val1 = OpLogicalEqual %bool %true %false +%val2 = OpLogicalEqual %boolvec2 %boolvec2_tf %boolvec2_tf +%val3 = OpLogicalEqual %boolvec3 %boolvec3_tft %boolvec3_tft +%val4 = OpLogicalEqual %boolvec4 %boolvec4_tftf %boolvec4_tftf +)"; + + CompileSuccessfully(GenerateKernelCode(body).c_str()); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateLogicals, OpLogicalEqualWrongTypeId) { + const std::string body = R"( +%val1 = OpLogicalEqual %u32 %true %false +)"; + + CompileSuccessfully(GenerateKernelCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), HasSubstr( + "Expected bool scalar or vector type as Result Type: LogicalEqual")); +} + +TEST_F(ValidateLogicals, OpLogicalEqualWrongLeftOperand) { + const std::string body = R"( +%val1 = OpLogicalEqual %bool %boolvec2_tf %false +)"; + + CompileSuccessfully(GenerateKernelCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), HasSubstr( + "Expected both operands to be of Result Type: LogicalEqual")); +} + +TEST_F(ValidateLogicals, OpLogicalEqualWrongRightOperand) { + const std::string body = R"( +%val1 = OpLogicalEqual %boolvec2 %boolvec2_tf %false +)"; + + CompileSuccessfully(GenerateKernelCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), HasSubstr( + "Expected both operands to be of Result Type: LogicalEqual")); +} + +TEST_F(ValidateLogicals, OpLogicalNotSuccess) { + const std::string body = R"( +%val1 = OpLogicalNot %bool %true +%val2 = OpLogicalNot %boolvec2 %boolvec2_tf +%val3 = OpLogicalNot %boolvec3 %boolvec3_tft +%val4 = OpLogicalNot %boolvec4 %boolvec4_tftf +)"; + + CompileSuccessfully(GenerateKernelCode(body).c_str()); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateLogicals, OpLogicalNotWrongTypeId) { + const std::string body = R"( +%val1 = OpLogicalNot %u32 %true +)"; + + CompileSuccessfully(GenerateKernelCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), HasSubstr( + "Expected bool scalar or vector type as Result Type: LogicalNot")); +} + +TEST_F(ValidateLogicals, OpLogicalNotWrongOperand) { + const std::string body = R"( +%val1 = OpLogicalNot %bool %boolvec2_tf +)"; + + CompileSuccessfully(GenerateKernelCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), HasSubstr( + "Expected operand to be of Result Type: LogicalNot")); +} + +TEST_F(ValidateLogicals, OpSelectSuccess) { + const std::string body = R"( +%val1 = OpSelect %u32 %true %u32_0 %u32_1 +%val2 = OpSelect %f32 %true %f32_0 %f32_1 +%val3 = OpSelect %f64 %true %f64_0 %f64_1 +%val4 = OpSelect %f32vec2 %boolvec2_tf %f32vec2_01 %f32vec2_12 +%val5 = OpSelect %f32vec4 %boolvec4_tftf %f32vec4_0123 %f32vec4_1234 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateLogicals, OpSelectWrongTypeId) { + const std::string body = R"( +%val1 = OpSelect %void %true %u32_0 %u32_1 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), HasSubstr( + "Expected scalar or vector type as Result Type: Select")); +} + +TEST_F(ValidateLogicals, OpSelectPointerNoCapability) { + const std::string body = R"( +%x = OpVariable %f32vec4ptr Function +%y = OpVariable %f32vec4ptr Function +OpStore %x %f32vec4_0123 +OpStore %y %f32vec4_1234 +%val1 = OpSelect %f32vec4ptr %true %x %y +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), HasSubstr( + "Using pointers with OpSelect requires capability VariablePointers")); +} + +TEST_F(ValidateLogicals, OpSelectPointerWithCapability) { + const std::string body = R"( +%x = OpVariable %f32vec4ptr Function +%y = OpVariable %f32vec4ptr Function +OpStore %x %f32vec4_0123 +OpStore %y %f32vec4_1234 +%val1 = OpSelect %f32vec4ptr %true %x %y +)"; + + const std::string extra_cap_ext = R"( +OpCapability VariablePointers +OpExtension "SPV_KHR_variable_pointers" +)"; + + CompileSuccessfully(GenerateShaderCode(body, extra_cap_ext).c_str()); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateLogicals, OpSelectWrongCondition) { + const std::string body = R"( +%val1 = OpSelect %u32 %u32_1 %u32_0 %u32_1 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), HasSubstr( + "Expected bool scalar or vector type as condition: Select")); +} + +TEST_F(ValidateLogicals, OpSelectWrongConditionDimension) { + const std::string body = R"( +%val1 = OpSelect %u32vec2 %true %u32vec2_01 %u32vec2_12 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), HasSubstr( + "Expected vector sizes of Result Type and the condition to be equal: " + "Select")); +} + +TEST_F(ValidateLogicals, OpSelectWrongLeftObject) { + const std::string body = R"( +%val1 = OpSelect %bool %true %u32vec2_01 %u32_1 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), HasSubstr( + "Expected both objects to be of Result Type: Select")); +} + +TEST_F(ValidateLogicals, OpSelectWrongRightObject) { + const std::string body = R"( +%val1 = OpSelect %bool %true %u32_1 %u32vec2_01 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), HasSubstr( + "Expected both objects to be of Result Type: Select")); +} + +TEST_F(ValidateLogicals, OpIEqualSuccess) { + const std::string body = R"( +%val1 = OpIEqual %bool %u32_0 %s32_1 +%val2 = OpIEqual %bool %s64_0 %u64_0 +%val3 = OpIEqual %boolvec2 %s32vec2_12 %u32vec2_12 +%val4 = OpIEqual %boolvec3 %s32vec3_123 %u32vec3_123 +%val5 = OpIEqual %boolvec4 %s32vec4_1234 %u32vec4_1234 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateLogicals, OpIEqualWrongTypeId) { + const std::string body = R"( +%val1 = OpIEqual %u32 %s32_1 %s32_1 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), HasSubstr( + "Expected bool scalar or vector type as Result Type: IEqual")); +} + +TEST_F(ValidateLogicals, OpIEqualLeftOperandNotInt) { + const std::string body = R"( +%val1 = OpIEqual %bool %f32_1 %s32_1 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), HasSubstr( + "Expected operands to be scalar or vector int: IEqual")); +} + +TEST_F(ValidateLogicals, OpIEqualLeftOperandWrongSize) { + const std::string body = R"( +%val1 = OpIEqual %bool %s32vec2_12 %s32_1 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), HasSubstr( + "Expected vector sizes of Result Type and the operands to be equal: " + "IEqual")); +} + +TEST_F(ValidateLogicals, OpIEqualRightOperandNotInt) { + const std::string body = R"( +%val1 = OpIEqual %bool %u32_1 %f32_1 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), HasSubstr( + "Expected operands to be scalar or vector int: IEqual")); +} + +TEST_F(ValidateLogicals, OpIEqualDifferentBitWidth) { + const std::string body = R"( +%val1 = OpIEqual %bool %u32_1 %u64_1 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), HasSubstr( + "Expected both operands to have the same component bit width: IEqual")); +} + +TEST_F(ValidateLogicals, OpUGreaterThanSuccess) { + const std::string body = R"( +%val1 = OpUGreaterThan %bool %u32_0 %u32_1 +%val2 = OpUGreaterThan %bool %s32_0 %u32_1 +%val3 = OpUGreaterThan %bool %u64_0 %u64_0 +%val4 = OpUGreaterThan %bool %u64_0 %s64_0 +%val5 = OpUGreaterThan %boolvec2 %u32vec2_12 %u32vec2_12 +%val6 = OpUGreaterThan %boolvec3 %s32vec3_123 %u32vec3_123 +%val7 = OpUGreaterThan %boolvec4 %u32vec4_1234 %u32vec4_1234 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateLogicals, OpUGreaterThanWrongTypeId) { + const std::string body = R"( +%val1 = OpUGreaterThan %u32 %u32_1 %u32_1 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), HasSubstr( + "Expected bool scalar or vector type as Result Type: UGreaterThan")); +} + +TEST_F(ValidateLogicals, OpUGreaterThanLeftOperandNotInt) { + const std::string body = R"( +%val1 = OpUGreaterThan %bool %f32_1 %u32_1 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), HasSubstr( + "Expected operands to be scalar or vector int: UGreaterThan")); +} + +TEST_F(ValidateLogicals, OpUGreaterThanLeftOperandWrongSize) { + const std::string body = R"( +%val1 = OpUGreaterThan %bool %u32vec2_12 %u32_1 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), HasSubstr( + "Expected vector sizes of Result Type and the operands to be equal: " + "UGreaterThan")); +} + +TEST_F(ValidateLogicals, OpUGreaterThanRightOperandNotInt) { + const std::string body = R"( +%val1 = OpUGreaterThan %bool %u32_1 %f32_1 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), HasSubstr( + "Expected operands to be scalar or vector int: UGreaterThan")); +} + +TEST_F(ValidateLogicals, OpUGreaterThanDifferentBitWidth) { + const std::string body = R"( +%val1 = OpUGreaterThan %bool %u32_1 %u64_1 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), HasSubstr( + "Expected both operands to have the same component bit width: " + "UGreaterThan")); +} + +TEST_F(ValidateLogicals, OpSGreaterThanSuccess) { + const std::string body = R"( +%val1 = OpSGreaterThan %bool %s32_0 %s32_1 +%val2 = OpSGreaterThan %bool %u32_0 %s32_1 +%val3 = OpSGreaterThan %bool %s64_0 %s64_0 +%val4 = OpSGreaterThan %bool %s64_0 %u64_0 +%val5 = OpSGreaterThan %boolvec2 %s32vec2_12 %s32vec2_12 +%val6 = OpSGreaterThan %boolvec3 %s32vec3_123 %u32vec3_123 +%val7 = OpSGreaterThan %boolvec4 %s32vec4_1234 %s32vec4_1234 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateLogicals, OpSGreaterThanWrongTypeId) { + const std::string body = R"( +%val1 = OpSGreaterThan %s32 %s32_1 %s32_1 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), HasSubstr( + "Expected bool scalar or vector type as Result Type: SGreaterThan")); +} + +TEST_F(ValidateLogicals, OpSGreaterThanLeftOperandNotInt) { + const std::string body = R"( +%val1 = OpSGreaterThan %bool %f32_1 %s32_1 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), HasSubstr( + "Expected operands to be scalar or vector int: SGreaterThan")); +} + +TEST_F(ValidateLogicals, OpSGreaterThanLeftOperandWrongSize) { + const std::string body = R"( +%val1 = OpSGreaterThan %bool %s32vec2_12 %s32_1 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), HasSubstr( + "Expected vector sizes of Result Type and the operands to be equal: " + "SGreaterThan")); +} + +TEST_F(ValidateLogicals, OpSGreaterThanRightOperandNotInt) { + const std::string body = R"( +%val1 = OpSGreaterThan %bool %s32_1 %f32_1 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), HasSubstr( + "Expected operands to be scalar or vector int: SGreaterThan")); +} + +TEST_F(ValidateLogicals, OpSGreaterThanDifferentBitWidth) { + const std::string body = R"( +%val1 = OpSGreaterThan %bool %s32_1 %s64_1 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), HasSubstr( + "Expected both operands to have the same component bit width: SGreaterThan")); +} + +} // anonymous namespace diff --git a/test/val/val_ssa_test.cpp b/test/val/val_ssa_test.cpp index b2074b5..22e94f2 100644 --- a/test/val/val_ssa_test.cpp +++ b/test/val/val_ssa_test.cpp @@ -1080,7 +1080,7 @@ TEST_F(ValidateSSA, IdDominatesItsUseGood) { R"( %func = OpFunction %voidt None %vfunct %entry = OpLabel -%cond = OpSLessThan %uintt %one %ten +%cond = OpSLessThan %boolt %one %ten %eleven = OpIAdd %uintt %one %ten OpSelectionMerge %merge None OpBranchConditional %cond %t %f @@ -1108,7 +1108,7 @@ TEST_F(ValidateSSA, IdDoesNotDominateItsUseBad) { R"( %func = OpFunction %voidt None %vfunct %entry = OpLabel -%cond = OpSLessThan %uintt %one %ten +%cond = OpSLessThan %boolt %one %ten OpSelectionMerge %merge None OpBranchConditional %cond %true_block %false_block %true_block = OpLabel @@ -1140,7 +1140,7 @@ TEST_F(ValidateSSA, PhiUseDoesntDominateDefinitionGood) { OpBranch %loop %loop = OpLabel %i = OpPhi %uintt %one_val %entry %inew %cont -%cond = OpSLessThan %uintt %one %ten +%cond = OpSLessThan %boolt %one %ten OpLoopMerge %merge %cont None OpBranchConditional %cond %body %merge %body = OpLabel @@ -1169,7 +1169,7 @@ TEST_F(ValidateSSA, %loop = OpLabel %i = OpPhi %uintt %one_val %entry %inew %cont %bad = OpIAdd %uintt %inew %one -%cond = OpSLessThan %uintt %one %ten +%cond = OpSLessThan %boolt %one %ten OpLoopMerge %merge %cont None OpBranchConditional %cond %body %merge %body = OpLabel -- 2.7.4