@@ -830,6 +830,194 @@ OpFunctionEnd)";
830830 EXPECT_FALSE (ctx->AreAnalysesValid (IRContext::kAnalysisDominatorAnalysis ));
831831}
832832
833+ TEST_F (IRContextTest, InvalidateCFGInvalidatesAll) {
834+ const std::string text = R"(
835+ OpCapability Shader
836+ OpCapability Linkage
837+ OpMemoryModel Logical GLSL450
838+ %1 = OpTypeVoid
839+ %2 = OpTypeFunction %1
840+ %3 = OpFunction %1 None %2
841+ %4 = OpLabel
842+ OpReturn
843+ OpFunctionEnd)" ;
844+
845+ std::unique_ptr<IRContext> ctx =
846+ BuildModule (SPV_ENV_UNIVERSAL_1_1 , nullptr , text,
847+ SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS );
848+
849+ ctx->BuildInvalidAnalyses (
850+ IRContext::kAnalysisCFG | IRContext::kAnalysisDominatorAnalysis |
851+ IRContext::kAnalysisStructuredCFG | IRContext::kAnalysisLoopAnalysis |
852+ IRContext::kAnalysisScalarEvolution | IRContext::kAnalysisLiveness );
853+
854+ ctx->InvalidateAnalyses (IRContext::kAnalysisCFG );
855+ EXPECT_FALSE (ctx->AreAnalysesValid (IRContext::kAnalysisCFG ));
856+ EXPECT_FALSE (ctx->AreAnalysesValid (IRContext::kAnalysisDominatorAnalysis ));
857+ EXPECT_FALSE (ctx->AreAnalysesValid (IRContext::kAnalysisStructuredCFG ));
858+ }
859+
860+ TEST_F (IRContextTest, InvalidateDominatorInvalidatesDownstreamOnly) {
861+ const std::string text = R"(
862+ OpCapability Shader
863+ OpCapability Linkage
864+ OpMemoryModel Logical GLSL450
865+ %1 = OpTypeVoid
866+ %2 = OpTypeFunction %1
867+ %3 = OpFunction %1 None %2
868+ %4 = OpLabel
869+ OpReturn
870+ OpFunctionEnd)" ;
871+
872+ std::unique_ptr<IRContext> ctx =
873+ BuildModule (SPV_ENV_UNIVERSAL_1_1 , nullptr , text,
874+ SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS );
875+
876+ ctx->BuildInvalidAnalyses (
877+ IRContext::kAnalysisCFG | IRContext::kAnalysisDominatorAnalysis |
878+ IRContext::kAnalysisStructuredCFG | IRContext::kAnalysisLoopAnalysis |
879+ IRContext::kAnalysisScalarEvolution | IRContext::kAnalysisLiveness );
880+
881+ ctx->InvalidateAnalyses (IRContext::kAnalysisDominatorAnalysis );
882+ EXPECT_TRUE (ctx->AreAnalysesValid (IRContext::kAnalysisCFG ));
883+ EXPECT_TRUE (ctx->AreAnalysesValid (IRContext::kAnalysisStructuredCFG ));
884+ EXPECT_FALSE (ctx->AreAnalysesValid (IRContext::kAnalysisDominatorAnalysis ));
885+ }
886+
887+ TEST_F (IRContextTest, InvalidateLoopInvalidatesDownstreamOnly) {
888+ const std::string text = R"(
889+ OpCapability Shader
890+ OpCapability Linkage
891+ OpMemoryModel Logical GLSL450
892+ %1 = OpTypeVoid
893+ %2 = OpTypeFunction %1
894+ %3 = OpFunction %1 None %2
895+ %4 = OpLabel
896+ OpReturn
897+ OpFunctionEnd)" ;
898+
899+ std::unique_ptr<IRContext> ctx =
900+ BuildModule (SPV_ENV_UNIVERSAL_1_1 , nullptr , text,
901+ SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS );
902+
903+ ctx->BuildInvalidAnalyses (
904+ IRContext::kAnalysisCFG | IRContext::kAnalysisDominatorAnalysis |
905+ IRContext::kAnalysisStructuredCFG | IRContext::kAnalysisLoopAnalysis |
906+ IRContext::kAnalysisScalarEvolution | IRContext::kAnalysisLiveness );
907+
908+ ctx->InvalidateAnalyses (IRContext::kAnalysisLoopAnalysis );
909+ EXPECT_TRUE (ctx->AreAnalysesValid (IRContext::kAnalysisCFG ));
910+ EXPECT_TRUE (ctx->AreAnalysesValid (IRContext::kAnalysisDominatorAnalysis ));
911+ EXPECT_TRUE (ctx->AreAnalysesValid (IRContext::kAnalysisStructuredCFG ));
912+ EXPECT_FALSE (ctx->AreAnalysesValid (IRContext::kAnalysisLoopAnalysis ));
913+ EXPECT_FALSE (ctx->AreAnalysesValid (IRContext::kAnalysisScalarEvolution ));
914+ EXPECT_FALSE (ctx->AreAnalysesValid (IRContext::kAnalysisLiveness ));
915+ }
916+
917+ #ifdef SPIRV_CHECK_CONTEXT
918+ TEST_F (IRContextTest, IsConsistentCatchesInconsistentDominator) {
919+ const std::string text = R"(
920+ OpCapability Shader
921+ OpCapability Linkage
922+ OpMemoryModel Logical GLSL450
923+ %1 = OpTypeVoid
924+ %2 = OpTypeFunction %1
925+ %3 = OpFunction %1 None %2
926+ %4 = OpLabel
927+ OpReturn
928+ OpFunctionEnd)" ;
929+
930+ std::unique_ptr<IRContext> ctx =
931+ BuildModule (SPV_ENV_UNIVERSAL_1_1 , nullptr , text,
932+ SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS );
933+
934+ ASSERT_TRUE (ctx->module ()->begin () != ctx->module ()->end ());
935+ Function* func = &*ctx->module ()->begin ();
936+
937+ // Computes and caches dominator analysis
938+ auto * dom = ctx->GetDominatorAnalysis (func);
939+ EXPECT_TRUE (ctx->IsConsistent ());
940+
941+ // Mess up the cached tree
942+ dom->GetDomTree ().ClearTree ();
943+
944+ // It should catch the inconsistency
945+ EXPECT_FALSE (ctx->IsConsistent ());
946+ }
947+
948+ TEST_F (IRContextTest, IsConsistentCatchesInconsistentStructuredCFG) {
949+ const std::string text = R"(
950+ OpCapability Shader
951+ %1 = OpExtInstImport "GLSL.std.450"
952+ OpMemoryModel Logical GLSL450
953+ OpEntryPoint Fragment %4 "main"
954+ OpExecutionMode %4 OriginUpperLeft
955+ %2 = OpTypeVoid
956+ %3 = OpTypeFunction %2
957+ %11 = OpTypeBool
958+ %true = OpConstantTrue %11
959+ %4 = OpFunction %2 None %3
960+ %5 = OpLabel
961+ OpSelectionMerge %10 None
962+ OpBranchConditional %true %8 %9
963+ %8 = OpLabel
964+ OpBranch %10
965+ %9 = OpLabel
966+ OpBranch %10
967+ %10 = OpLabel
968+ OpReturn
969+ OpFunctionEnd
970+ )" ;
971+
972+ std::unique_ptr<IRContext> ctx =
973+ BuildModule (SPV_ENV_UNIVERSAL_1_3 , nullptr , text,
974+ SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS );
975+
976+ // Build and cache structured CFG analysis
977+ ctx->BuildInvalidAnalyses (IRContext::kAnalysisStructuredCFG );
978+ EXPECT_TRUE (ctx->IsConsistent ());
979+
980+ // Modify the merge block operand of the selection merge instruction without
981+ // invalidating the analysis
982+ Instruction* merge_inst = ctx->cfg ()->block (5 )->GetMergeInst ();
983+ ASSERT_NE (merge_inst, nullptr );
984+ // Change merge block target from %10 to %8 (operand 0 is %10)
985+ merge_inst->SetInOperand (0 , {8 });
986+
987+ // It should catch the inconsistency
988+ EXPECT_FALSE (ctx->IsConsistent ());
989+ }
990+ #else
991+ TEST_F (IRContextTest, IsConsistentAlwaysTrueWithoutMacro) {
992+ const std::string text = R"(
993+ OpCapability Shader
994+ OpCapability Linkage
995+ OpMemoryModel Logical GLSL450
996+ %1 = OpTypeVoid
997+ %2 = OpTypeFunction %1
998+ %3 = OpFunction %1 None %2
999+ %4 = OpLabel
1000+ OpReturn
1001+ OpFunctionEnd)" ;
1002+
1003+ std::unique_ptr<IRContext> ctx =
1004+ BuildModule (SPV_ENV_UNIVERSAL_1_1 , nullptr , text,
1005+ SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS );
1006+
1007+ ASSERT_TRUE (ctx->module ()->begin () != ctx->module ()->end ());
1008+ Function* func = &*ctx->module ()->begin ();
1009+
1010+ auto * dom = ctx->GetDominatorAnalysis (func);
1011+ EXPECT_TRUE (ctx->IsConsistent ());
1012+
1013+ // Mess up the cached tree
1014+ dom->GetDomTree ().ClearTree ();
1015+
1016+ // Without SPIRV_CHECK_CONTEXT it should still return true
1017+ EXPECT_TRUE (ctx->IsConsistent ());
1018+ }
1019+ #endif
1020+
8331021TEST_F (IRContextTest, AsanErrorTest) {
8341022 std::string shader = R"(
8351023 OpCapability Shader
0 commit comments