From efc782d743372b374796d5df3cb233291b686d52 Mon Sep 17 00:00:00 2001 From: Umar Arshad Date: Wed, 13 Jul 2016 18:57:52 -0400 Subject: [PATCH] Check definitions appear in dominator of use Also address use and def of ID in same instruction --- CHANGES | 2 + source/validate.cpp | 1 + source/validate.h | 12 ++++ source/validate_id.cpp | 58 +++++++++++++-- test/Validate.CFG.cpp | 122 ++++++++++++++++---------------- test/Validate.SSA.cpp | 188 ++++++++++++++++++++++++++++++++++++++++++++++++- 6 files changed, 318 insertions(+), 65 deletions(-) diff --git a/CHANGES b/CHANGES index 0e76412..8e5ab77 100644 --- a/CHANGES +++ b/CHANGES @@ -2,6 +2,8 @@ Revision history for SPIRV-Tools v2016.2-dev 2016-07-19 - Start v2016.2 + - Validator is incomplete + - Checks ID use block is dominated by definition block v2016.1 2016-07-19 - Fix https://github.com/KhronosGroup/SPIRV-Tools/issues/261 diff --git a/source/validate.cpp b/source/validate.cpp index c5779a9..dc2173b 100644 --- a/source/validate.cpp +++ b/source/validate.cpp @@ -229,6 +229,7 @@ spv_result_t spvValidate(const spv_const_context context, // CFG checks are performed after the binary has been parsed // and the CFGPass has collected information about the control flow spvCheckReturn(PerformCfgChecks(vstate)); + spvCheckReturn(CheckIdDefinitionDominateUse(vstate)); // NOTE: Copy each instruction for easier processing std::vector instructions; diff --git a/source/validate.h b/source/validate.h index 7f41119..225d0c3 100644 --- a/source/validate.h +++ b/source/validate.h @@ -88,6 +88,18 @@ std::vector> CalculateDominators( /// @return SPV_SUCCESS if no errors are found. SPV_ERROR_INVALID_CFG otherwise spv_result_t PerformCfgChecks(ValidationState_t& _); +/// @brief This function checks all ID definitions dominate their use in the +/// CFG. +/// +/// This function will iterate over all ID definitions that are defined in the +/// functions of a module and make sure that the definitions appear in a +/// block that dominates their use. +/// +/// @param[in] _ the validation state of the module +/// +/// @return SPV_SUCCESS if no errors are found. SPV_ERROR_INVALID_ID otherwise +spv_result_t CheckIdDefinitionDominateUse(const ValidationState_t& _); + /// @brief Updates the immediate dominator for each of the block edges /// /// Updates the immediate dominator of the blocks for each of the edges diff --git a/source/validate_id.cpp b/source/validate_id.cpp index 78550d4..84ba404 100644 --- a/source/validate_id.cpp +++ b/source/validate_id.cpp @@ -2376,13 +2376,56 @@ function getCanBeForwardDeclaredFunction(SpvOp opcode) { namespace libspirv { +/// This function checks all ID definitions dominate their use in the CFG. +/// +/// This function will iterate over all ID definitions that are defined in the +/// functions of a module and make sure that the definitions appear in a +/// block that dominates their use. +/// +/// NOTE: This function does NOT check module scoped functions which are +/// checked during the initial binary parse in the IdPass below +spv_result_t CheckIdDefinitionDominateUse(const ValidationState_t& _) { + for (const auto& definition : _.all_definitions()) { + // Check only those blocks defined in a function + if (const Function* func = definition.second.defining_function()) { + if (const BasicBlock* block = definition.second.defining_block()) { + // If the Id is defined within a block then make sure all references to + // that Id appear in a blocks that are dominated by the defining block + for (auto use : definition.second.uses()) { + if (use->dom_end() == find(use->dom_begin(), use->dom_end(), block)) { + return _.diag(SPV_ERROR_INVALID_ID) + << "ID " << _.getIdName(definition.first) + << " defined in block " << _.getIdName(block->id()) + << " does not dominate its use in block " + << _.getIdName(use->id()); + } + } + } else { + // If the Ids defined within a function but not in a block(i.e. function + // parameters, block ids), then make sure all references to that Id + // appear within the same function + bool found = false; + for (auto use : definition.second.uses()) { + tie(ignore, found) = func->GetBlock(use->id()); + if (!found) { + return _.diag(SPV_ERROR_INVALID_ID) + << "ID " << _.getIdName(definition.first) + << " used in block " << _.getIdName(use->id()) + << " is used outside of it's defining function " + << _.getIdName(func->id()); + } + } + } + } + // NOTE: Ids defined outside of functions must appear before they are used + // This check is being performed in the IdPass function + } + return SPV_SUCCESS; +} + // Performs SSA validation on the IDs of an instruction. The // can_have_forward_declared_ids functor should return true if the // instruction operand's ID can be forward referenced. -// -// TODO(umar): Use dominators to correctly validate SSA. For example, the result -// id from a 'then' block cannot dominate its usage in the 'else' block. This -// is not yet performed by this function. spv_result_t IdPass(ValidationState_t& _, const spv_parsed_instruction_t* inst) { auto can_have_forward_declared_ids = @@ -2396,8 +2439,10 @@ spv_result_t IdPass(ValidationState_t& _, auto ret = SPV_ERROR_INTERNAL; switch (type) { case SPV_OPERAND_TYPE_RESULT_ID: + // NOTE: Multiple Id definitions are being checked by the binary parser + // NOTE: result Id is added *after* all of the other Ids have been + // checked to avoid premature use in the same instruction _.RemoveIfForwardDeclared(*operand_ptr); - _.AddId(*inst); ret = SPV_SUCCESS; break; case SPV_OPERAND_TYPE_ID: @@ -2423,6 +2468,9 @@ spv_result_t IdPass(ValidationState_t& _, return ret; } } + if (inst->result_id) { + _.AddId(*inst); + } return SPV_SUCCESS; } } // namespace libspirv diff --git a/test/Validate.CFG.cpp b/test/Validate.CFG.cpp index e9d7dad..255a290 100644 --- a/test/Validate.CFG.cpp +++ b/test/Validate.CFG.cpp @@ -91,8 +91,13 @@ class Block { : label_(label), body_(), type_(type), successors_() {} /// Sets the instructions which will appear in the body of the block - Block& setBody(std::string body) { - body_ = body; + Block& SetBody(std::string body) { + body_ = body; + return *this; + } + + Block& AppendBody(std::string body) { + body_ += body; return *this; } @@ -196,9 +201,9 @@ TEST_P(ValidateCFG, Simple) { Block cont("cont"); Block merge("merge", SpvOpReturn); - entry.setBody("%cond = OpSLessThan %intt %one %two\n"); + entry.SetBody("%cond = OpSLessThan %intt %one %two\n"); if (is_shader) { - loop.setBody("OpLoopMerge %merge %cont None\n"); + loop.SetBody("OpLoopMerge %merge %cont None\n"); } string str = header(GetParam()) + nameOps("loop", "entry", "cont", "merge", @@ -220,7 +225,7 @@ TEST_P(ValidateCFG, Variable) { Block cont("cont"); Block exit("exit", SpvOpReturn); - entry.setBody("%var = OpVariable %ptrt Function\n"); + entry.SetBody("%var = OpVariable %ptrt Function\n"); string str = header(GetParam()) + nameOps(make_pair("func", "Main")) + types_consts() + " %func = OpFunction %voidt None %funct\n"; @@ -239,7 +244,7 @@ TEST_P(ValidateCFG, VariableNotInFirstBlockBad) { Block exit("exit", SpvOpReturn); // This operation should only be performed in the entry block - cont.setBody("%var = OpVariable %ptrt Function\n"); + cont.SetBody("%var = OpVariable %ptrt Function\n"); string str = header(GetParam()) + nameOps(make_pair("func", "Main")) + types_consts() + " %func = OpFunction %voidt None %funct\n"; @@ -264,8 +269,8 @@ TEST_P(ValidateCFG, BlockAppearsBeforeDominatorBad) { Block branch("branch", SpvOpBranchConditional); Block merge("merge", SpvOpReturn); - entry.setBody("%cond = OpSLessThan %intt %one %two\n"); - if (is_shader) branch.setBody("OpSelectionMerge %merge None\n"); + entry.SetBody("%cond = OpSLessThan %intt %one %two\n"); + if (is_shader) branch.SetBody("OpSelectionMerge %merge None\n"); string str = header(GetParam()) + nameOps("cont", "branch", make_pair("func", "Main")) + @@ -291,11 +296,11 @@ TEST_P(ValidateCFG, MergeBlockTargetedByMultipleHeaderBlocksBad) { Block selection("selection", SpvOpBranchConditional); Block merge("merge", SpvOpReturn); - entry.setBody("%cond = OpSLessThan %intt %one %two\n"); - if (is_shader) loop.setBody(" OpLoopMerge %merge %loop None\n"); + entry.SetBody("%cond = OpSLessThan %intt %one %two\n"); + if (is_shader) loop.SetBody(" OpLoopMerge %merge %loop None\n"); // cannot share the same merge - if (is_shader) selection.setBody("OpSelectionMerge %merge None\n"); + if (is_shader) selection.SetBody("OpSelectionMerge %merge None\n"); string str = header(GetParam()) + nameOps("merge", make_pair("func", "Main")) + types_consts() + @@ -325,11 +330,11 @@ TEST_P(ValidateCFG, MergeBlockTargetedByMultipleHeaderBlocksSelectionBad) { Block selection("selection", SpvOpBranchConditional); Block merge("merge", SpvOpReturn); - entry.setBody("%cond = OpSLessThan %intt %one %two\n"); - if (is_shader) selection.setBody(" OpSelectionMerge %merge None\n"); + entry.SetBody("%cond = OpSLessThan %intt %one %two\n"); + if (is_shader) selection.SetBody(" OpSelectionMerge %merge None\n"); // cannot share the same merge - if (is_shader) loop.setBody(" OpLoopMerge %merge %loop None\n"); + if (is_shader) loop.SetBody(" OpLoopMerge %merge %loop None\n"); string str = header(GetParam()) + nameOps("merge", make_pair("func", "Main")) + types_consts() + @@ -377,8 +382,8 @@ TEST_P(ValidateCFG, BranchConditionalTrueTargetFirstBlockBad) { Block bad("bad", SpvOpBranchConditional); Block exit("exit", SpvOpReturn); - entry.setBody("%cond = OpSLessThan %intt %one %two\n"); - bad.setBody(" OpLoopMerge %entry %exit None\n"); + entry.SetBody("%cond = OpSLessThan %intt %one %two\n"); + bad.SetBody(" OpLoopMerge %entry %exit None\n"); string str = header(GetParam()) + nameOps("entry", "bad", make_pair("func", "Main")) + @@ -403,8 +408,8 @@ TEST_P(ValidateCFG, BranchConditionalFalseTargetFirstBlockBad) { Block merge("merge"); Block end("end", SpvOpReturn); - entry.setBody("%cond = OpSLessThan %intt %one %two\n"); - bad.setBody("OpLoopMerge %merge %cont None\n"); + entry.SetBody("%cond = OpSLessThan %intt %one %two\n"); + bad.SetBody("OpLoopMerge %merge %cont None\n"); string str = header(GetParam()) + nameOps("entry", "bad", make_pair("func", "Main")) + @@ -433,8 +438,8 @@ TEST_P(ValidateCFG, SwitchTargetFirstBlockBad) { Block merge("merge"); Block end("end", SpvOpReturn); - entry.setBody("%cond = OpSLessThan %intt %one %two\n"); - bad.setBody("OpSelectionMerge %merge None\n"); + entry.SetBody("%cond = OpSLessThan %intt %one %two\n"); + bad.SetBody("OpSelectionMerge %merge None\n"); string str = header(GetParam()) + nameOps("entry", "bad", make_pair("func", "Main")) + @@ -462,8 +467,8 @@ TEST_P(ValidateCFG, BranchToBlockInOtherFunctionBad) { Block middle("middle", SpvOpBranchConditional); Block end("end", SpvOpReturn); - entry.setBody("%cond = OpSLessThan %intt %one %two\n"); - middle.setBody("OpSelectionMerge %end None\n"); + entry.SetBody("%cond = OpSLessThan %intt %one %two\n"); + middle.SetBody("OpSelectionMerge %end None\n"); Block entry2("entry2"); Block middle2("middle2"); @@ -499,9 +504,9 @@ TEST_P(ValidateCFG, HeaderDoesntDominatesMergeBad) { Block f("f"); Block merge("merge", SpvOpReturn); - entry.setBody("%cond = OpSLessThan %intt %one %two\n"); + head.SetBody("%cond = OpSLessThan %intt %one %two\n"); - if (is_shader) head.setBody("OpSelectionMerge %merge None\n"); + if (is_shader) head.AppendBody("OpSelectionMerge %merge None\n"); string str = header(GetParam()) + nameOps("head", "merge", make_pair("func", "Main")) + @@ -514,7 +519,6 @@ TEST_P(ValidateCFG, HeaderDoesntDominatesMergeBad) { str += "OpFunctionEnd\n"; CompileSuccessfully(str); - if (is_shader) { ASSERT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions()); EXPECT_THAT( @@ -535,8 +539,8 @@ TEST_P(ValidateCFG, UnreachableMerge) { Block f("f", SpvOpReturn); Block merge("merge", SpvOpReturn); - entry.setBody("%cond = OpSLessThan %intt %one %two\n"); - if (is_shader) branch.setBody("OpSelectionMerge %merge None\n"); + entry.SetBody("%cond = OpSLessThan %intt %one %two\n"); + if (is_shader) branch.AppendBody("OpSelectionMerge %merge None\n"); string str = header(GetParam()) + nameOps("branch", "merge", make_pair("func", "Main")) + @@ -561,8 +565,8 @@ TEST_P(ValidateCFG, UnreachableMergeDefinedByOpUnreachable) { Block f("f", SpvOpReturn); Block merge("merge", SpvOpUnreachable); - entry.setBody("%cond = OpSLessThan %intt %one %two\n"); - if (is_shader) branch.setBody("OpSelectionMerge %merge None\n"); + entry.SetBody("%cond = OpSLessThan %intt %one %two\n"); + if (is_shader) branch.AppendBody("OpSelectionMerge %merge None\n"); string str = header(GetParam()) + nameOps("branch", "merge", make_pair("func", "Main")) + @@ -606,8 +610,8 @@ TEST_P(ValidateCFG, UnreachableBranch) { Block merge("merge"); Block exit("exit", SpvOpReturn); - entry.setBody("%cond = OpSLessThan %intt %one %two\n"); - if (is_shader) unreachable.setBody("OpSelectionMerge %merge None\n"); + unreachable.SetBody("%cond = OpSLessThan %intt %one %two\n"); + if (is_shader) unreachable.AppendBody("OpSelectionMerge %merge None\n"); string str = header(GetParam()) + nameOps("unreachable", "exit", make_pair("func", "Main")) + types_consts() + "%func = OpFunction %voidt None %funct\n"; @@ -638,8 +642,8 @@ TEST_P(ValidateCFG, SingleBlockLoop) { Block loop("loop", SpvOpBranchConditional); Block exit("exit", SpvOpReturn); - entry.setBody("%cond = OpSLessThan %intt %one %two\n"); - if (is_shader) loop.setBody("OpLoopMerge %exit %loop None\n"); + entry.SetBody("%cond = OpSLessThan %intt %one %two\n"); + if (is_shader) loop.AppendBody("OpLoopMerge %exit %loop None\n"); string str = header(GetParam()) + string(types_consts()) + "%func = OpFunction %voidt None %funct\n"; @@ -664,10 +668,10 @@ 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 %intt %one %two\n"); if (is_shader) { - loop1.setBody("OpLoopMerge %loop1_merge %loop2 None\n"); - loop2.setBody("OpLoopMerge %loop2_merge %loop2 None\n"); + loop1.SetBody("OpLoopMerge %loop1_merge %loop2 None\n"); + loop2.SetBody("OpLoopMerge %loop2_merge %loop2 None\n"); } string str = header(GetParam()) + nameOps("loop2", "loop2_merge") + @@ -694,11 +698,11 @@ TEST_P(ValidateCFG, NestedSelection) { vector merge_blocks; Block inner("inner"); - entry.setBody("%cond = OpSLessThan %intt %one %two\n"); + entry.SetBody("%cond = OpSLessThan %intt %one %two\n"); if_blocks.emplace_back("if0", SpvOpBranchConditional); - if (is_shader) if_blocks[0].setBody("OpSelectionMerge %if_merge0 None\n"); + if (is_shader) if_blocks[0].SetBody("OpSelectionMerge %if_merge0 None\n"); merge_blocks.emplace_back("if_merge0", SpvOpReturn); for (int i = 1; i < N; i++) { @@ -706,7 +710,7 @@ TEST_P(ValidateCFG, NestedSelection) { ss << i; if_blocks.emplace_back("if" + ss.str(), SpvOpBranchConditional); if (is_shader) - if_blocks[i].setBody("OpSelectionMerge %if_merge" + ss.str() + " None\n"); + if_blocks[i].SetBody("OpSelectionMerge %if_merge" + ss.str() + " None\n"); merge_blocks.emplace_back("if_merge" + ss.str(), SpvOpBranch); } string str = header(GetParam()) + string(types_consts()) + @@ -737,10 +741,10 @@ 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 %intt %one %two\n"); if (is_shader) { - loop1.setBody("OpLoopMerge %exit %loop2_merge None\n"); - loop2.setBody("OpLoopMerge %loop2_merge %loop2 None\n"); + loop1.SetBody("OpLoopMerge %exit %loop2_merge None\n"); + loop2.SetBody("OpLoopMerge %loop2_merge %loop2 None\n"); } string str = header(GetParam()) + @@ -775,8 +779,8 @@ TEST_P(ValidateCFG, BranchingToNonLoopHeaderBlockBad) { Block f("f"); Block exit("exit", SpvOpReturn); - entry.setBody("%cond = OpSLessThan %intt %one %two\n"); - if (is_shader) split.setBody("OpSelectionMerge %exit None\n"); + entry.SetBody("%cond = OpSLessThan %intt %one %two\n"); + if (is_shader) split.SetBody("OpSelectionMerge %exit None\n"); string str = header(GetParam()) + nameOps("split", "f") + types_consts() + "%func = OpFunction %voidt None %funct\n"; @@ -806,8 +810,8 @@ TEST_P(ValidateCFG, BranchingToSameNonLoopHeaderBlockBad) { Block split("split", SpvOpBranchConditional); Block exit("exit", SpvOpReturn); - entry.setBody("%cond = OpSLessThan %intt %one %two\n"); - if (is_shader) split.setBody("OpSelectionMerge %exit None\n"); + entry.SetBody("%cond = OpSLessThan %intt %one %two\n"); + if (is_shader) split.SetBody("OpSelectionMerge %exit None\n"); string str = header(GetParam()) + nameOps("split") + types_consts() + "%func = OpFunction %voidt None %funct\n"; @@ -836,8 +840,8 @@ TEST_P(ValidateCFG, MultipleBackEdgesToLoopHeaderBad) { Block cont("cont", SpvOpBranchConditional); Block merge("merge", SpvOpReturn); - entry.setBody("%cond = OpSLessThan %intt %one %two\n"); - if (is_shader) loop.setBody("OpLoopMerge %merge %loop None\n"); + entry.SetBody("%cond = OpSLessThan %intt %one %two\n"); + if (is_shader) loop.SetBody("OpLoopMerge %merge %loop None\n"); string str = header(GetParam()) + nameOps("cont", "loop") + types_consts() + "%func = OpFunction %voidt None %funct\n"; @@ -868,8 +872,8 @@ TEST_P(ValidateCFG, ContinueTargetMustBePostDominatedByBackEdge) { Block merge("merge", SpvOpReturn); Block exit("exit", SpvOpReturn); - entry.setBody("%cond = OpSLessThan %intt %one %two\n"); - if (is_shader) loop.setBody("OpLoopMerge %merge %cheader None\n"); + entry.SetBody("%cond = OpSLessThan %intt %one %two\n"); + if (is_shader) loop.SetBody("OpLoopMerge %merge %cheader None\n"); string str = header(GetParam()) + nameOps("cheader", "be_block") + types_consts() + "%func = OpFunction %voidt None %funct\n"; @@ -901,8 +905,8 @@ TEST_P(ValidateCFG, BranchOutOfConstructToMergeBad) { Block cont("cont", SpvOpBranchConditional); Block merge("merge", SpvOpReturn); - entry.setBody("%cond = OpSLessThan %intt %one %two\n"); - if (is_shader) loop.setBody("OpLoopMerge %merge %loop None\n"); + entry.SetBody("%cond = OpSLessThan %intt %one %two\n"); + if (is_shader) loop.SetBody("OpLoopMerge %merge %loop None\n"); string str = header(GetParam()) + nameOps("cont", "loop") + types_consts() + "%func = OpFunction %voidt None %funct\n"; @@ -933,8 +937,8 @@ TEST_P(ValidateCFG, BranchOutOfConstructBad) { Block merge("merge"); Block exit("exit", SpvOpReturn); - entry.setBody("%cond = OpSLessThan %intt %one %two\n"); - if (is_shader) loop.setBody("OpLoopMerge %merge %loop None\n"); + entry.SetBody("%cond = OpSLessThan %intt %one %two\n"); + if (is_shader) loop.SetBody("OpLoopMerge %merge %loop None\n"); string str = header(GetParam()) + nameOps("cont", "loop") + types_consts() + "%func = OpFunction %voidt None %funct\n"; @@ -987,11 +991,11 @@ OpDecorate %id BuiltIn GlobalInvocationId %main = OpFunction %void None %voidf )"; - entry.setBody( - "%idval = OpLoad %uvec3 %id\n" - "%x = OpCompositeExtract %u32 %idval 0\n" - "%selector = OpUMod %u32 %x %three\n" - "OpSelectionMerge %phi None\n"); + entry.SetBody( + "%idval = OpLoad %uvec3 %id\n" + "%x = OpCompositeExtract %u32 %idval 0\n" + "%selector = OpUMod %u32 %x %three\n" + "OpSelectionMerge %phi None\n"); str += entry >> vector({def, case0, case1, case2}); str += case1 >> phi; str += def; diff --git a/test/Validate.SSA.cpp b/test/Validate.SSA.cpp index 03d7940..3460d92 100644 --- a/test/Validate.SSA.cpp +++ b/test/Validate.SSA.cpp @@ -107,6 +107,49 @@ TEST_F(ValidateSSA, DominateUsageBad) { EXPECT_THAT(getDiagnosticString(), HasSubstr("not_dominant")); } +TEST_F(ValidateSSA, DominateUsageWithinBlockBad) { + char str[] = R"( + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpName %bad "bad" +%voidt = OpTypeVoid +%funct = OpTypeFunction %voidt +%uintt = OpTypeInt 32 0 +%one = OpConstant %uintt 1 +%func = OpFunction %voidt None %funct +%entry = OpLabel +%sum = OpIAdd %uintt %one %bad +%bad = OpCopyObject %uintt %sum + OpReturn + OpFunctionEnd +)"; + CompileSuccessfully(str); + ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + MatchesRegex("ID .\\[bad\\] has not been defined")); +} + +TEST_F(ValidateSSA, DominateUsageSameInstructionBad) { + char str[] = R"( + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpName %sum "sum" +%voidt = OpTypeVoid +%funct = OpTypeFunction %voidt +%uintt = OpTypeInt 32 0 +%one = OpConstant %uintt 1 +%func = OpFunction %voidt None %funct +%entry = OpLabel +%sum = OpIAdd %uintt %one %sum + OpReturn + OpFunctionEnd +)"; + CompileSuccessfully(str); + ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + MatchesRegex("ID .\\[sum\\] has not been defined")); +} + TEST_F(ValidateSSA, ForwardNameGood) { char str[] = R"( OpCapability Shader @@ -1014,5 +1057,148 @@ TEST_F(ValidateSSA, PhiMissingLabelBad) { EXPECT_THAT(getDiagnosticString(), HasSubstr("missing")); } -// TODO(umar): OpGroupMemberDecorate +TEST_F(ValidateSSA, IdDominatesItsUseGood) { + string str = kHeader + kBasicTypes + + R"( +%func = OpFunction %voidt None %vfunct +%entry = OpLabel +%cond = OpSLessThan %intt %one %ten +%eleven = OpIAdd %intt %one %ten + OpSelectionMerge %merge None + OpBranchConditional %cond %t %f +%t = OpLabel +%twelve = OpIAdd %intt %eleven %one + OpBranch %merge +%f = OpLabel +%twentytwo = OpIAdd %intt %eleven %ten + OpBranch %merge +%merge = OpLabel + OpReturn + OpFunctionEnd +)"; + + CompileSuccessfully(str); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateSSA, IdDoesNotDominateItsUseBad) { + string str = kHeader + + "OpName %eleven \"eleven\"\n" + "OpName %true_block \"true_block\"\n" + "OpName %false_block \"false_block\"" + + kBasicTypes + + R"( +%func = OpFunction %voidt None %vfunct +%entry = OpLabel +%cond = OpSLessThan %intt %one %ten + OpSelectionMerge %merge None + OpBranchConditional %cond %true_block %false_block +%true_block = OpLabel +%eleven = OpIAdd %intt %one %ten +%twelve = OpIAdd %intt %eleven %one + OpBranch %merge +%false_block = OpLabel +%twentytwo = OpIAdd %intt %eleven %ten + OpBranch %merge +%merge = OpLabel + OpReturn + OpFunctionEnd +)"; + CompileSuccessfully(str); + ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + MatchesRegex("ID .\\[eleven\\] defined in block .\\[true_block\\] does " + "not dominate its use in block .\\[false_block\\]")); +} + +TEST_F(ValidateSSA, PhiUseDoesntDominateDefinitionGood) { + string str = kHeader + kBasicTypes + + R"( +%func = OpFunction %voidt None %vfunct +%entry = OpLabel +%var_one = OpVariable %intptrt Function %one +%one_val = OpLoad %intt %var_one + OpBranch %loop +%loop = OpLabel +%i = OpPhi %intt %one_val %entry %inew %cont +%cond = OpSLessThan %intt %one %ten + OpLoopMerge %merge %cont None + OpBranchConditional %cond %body %merge +%body = OpLabel + OpBranch %cont +%cont = OpLabel +%inew = OpIAdd %intt %i %one + OpBranch %loop +%merge = OpLabel + OpReturn + OpFunctionEnd +)"; + + CompileSuccessfully(str); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); } + +TEST_F(ValidateSSA, PhiUseDoesntDominateUseOfPhiOperandUsedBeforeDefinitionBad) { + string str = kHeader + + "OpName %inew \"inew\"" + + kBasicTypes + + R"( +%func = OpFunction %voidt None %vfunct +%entry = OpLabel +%var_one = OpVariable %intptrt Function %one +%one_val = OpLoad %intt %var_one + OpBranch %loop +%loop = OpLabel +%i = OpPhi %intt %one_val %entry %inew %cont +%bad = OpIAdd %intt %inew %one +%cond = OpSLessThan %intt %one %ten + OpLoopMerge %merge %cont None + OpBranchConditional %cond %body %merge +%body = OpLabel + OpBranch %cont +%cont = OpLabel +%inew = OpIAdd %intt %i %one + OpBranch %loop +%merge = OpLabel + OpReturn + OpFunctionEnd +)"; + + CompileSuccessfully(str); + ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + MatchesRegex("ID .\\[inew\\] has not been defined")); +} + +TEST_F(ValidateSSA, UseFunctionParameterFromOtherFunctionBad) { + string str = kHeader + + "OpName %first \"first\"\n" + "OpName %entry2 \"entry2\"\n" + "OpName %func \"func\"\n" + + kBasicTypes + + R"( +%viifunct = OpTypeFunction %voidt %intt %intt +%func = OpFunction %voidt None %viifunct +%first = OpFunctionParameter %intt +%second = OpFunctionParameter %intt + OpFunctionEnd +%func2 = OpFunction %voidt None %viifunct +%first2 = OpFunctionParameter %intt +%second2 = OpFunctionParameter %intt +%entry2 = OpLabel +%baduse = OpIAdd %intt %first %first2 + OpReturn + OpFunctionEnd +)"; + + CompileSuccessfully(str); + ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + MatchesRegex("ID .\\[first\\] used in block .\\[entry2\\] is used " + "outside of it's defining function .\\[func\\]")); +} +// TODO(umar): OpGroupMemberDecorate +} // namespace -- 2.7.4