Squashed 'third_party/SPIRV-Tools/' changes from f7da52775..a61d07a72
a61d07a72 spirv-val: Fix SPV_KHR_fragment_shading_rate VUID label (#4014)
650acb575 spirv-val: Label Layer and ViewportIndex VUIDs (#4013)
0c036df28 Add dead function elimination to -O (#4015)
c2b2b5788 Add DebugValue for invisible store in single_store_elim (#4002)
c74d5523b Fix SSA re-writing in the presence of variable pointers. (#4010)
02195a029 spirv-fuzz: Fixes to pass management (#4011)
bcf5b211d spirv-fuzz: Add support for reining in rogue fuzzer passes (#3987)
7d250ed51 spirv-fuzz: Fix assertion failure in FuzzerPassAddCompositeExtract (#3995)
f9937bcc8 spirv-fuzz: Fix invalid equation facts (#4009)
aa6035f1c spirv-fuzz: Fix bugs in TransformationFlattenConditionalBranch (#4006)
5735576f8 spirv-fuzz: Fix bug related to transformation applicability (#3990)
git-subtree-dir: third_party/SPIRV-Tools
git-subtree-split: a61d07a72763c1eb200de0a2c316703643a0d1d9
diff --git a/source/fuzz/fact_manager/data_synonym_and_id_equation_facts.cpp b/source/fuzz/fact_manager/data_synonym_and_id_equation_facts.cpp
index ad4cd0c..a2c1f2c 100644
--- a/source/fuzz/fact_manager/data_synonym_and_id_equation_facts.cpp
+++ b/source/fuzz/fact_manager/data_synonym_and_id_equation_facts.cpp
@@ -176,12 +176,6 @@
// We can thus infer "a = d"
AddDataSynonymFactRecursive(lhs_dd, *equation.operands[0]);
}
- if (synonymous_.IsEquivalent(*equation.operands[0], *rhs_dds[1])) {
- // Equation form: "a = (c - e) + c"
- // We can thus infer "a = -e"
- AddEquationFactRecursive(lhs_dd, SpvOpSNegate,
- {equation.operands[1]});
- }
}
}
for (const auto& equation : GetEquations(rhs_dds[1])) {
diff --git a/source/fuzz/fuzzer.cpp b/source/fuzz/fuzzer.cpp
index 4361283..40da497 100644
--- a/source/fuzz/fuzzer.cpp
+++ b/source/fuzz/fuzzer.cpp
@@ -107,8 +107,6 @@
namespace {
const uint32_t kIdBoundGap = 100;
-const uint32_t kTransformationLimit = 2000;
-
} // namespace
Fuzzer::Fuzzer(spv_target_env target_env, MessageConsumer consumer,
@@ -367,21 +365,37 @@
// that fuzzing stops if the number of repeated passes hits the limit on the
// number of transformations that can be applied.
assert(
- num_repeated_passes_applied_ <= kTransformationLimit &&
+ num_repeated_passes_applied_ <=
+ fuzzer_context_->GetTransformationLimit() &&
"The number of repeated passes applied must not exceed its upper limit.");
- if (num_repeated_passes_applied_ == kTransformationLimit) {
+ if (ir_context_->module()->id_bound() >= fuzzer_context_->GetIdBoundLimit()) {
+ return false;
+ }
+ if (num_repeated_passes_applied_ ==
+ fuzzer_context_->GetTransformationLimit()) {
// Stop because fuzzing has got stuck.
return false;
}
auto transformations_applied_so_far =
static_cast<uint32_t>(transformation_sequence_out_.transformation_size());
- if (transformations_applied_so_far >= kTransformationLimit) {
+ if (transformations_applied_so_far >=
+ fuzzer_context_->GetTransformationLimit()) {
// Stop because we have reached the transformation limit.
return false;
}
+ // If we have applied T transformations so far, and the limit on the number of
+ // transformations to apply is L (where T < L), the chance that we will
+ // continue fuzzing is:
+ //
+ // 1 - T/(2*L)
+ //
+ // That is, the chance of continuing decreases as more transformations are
+ // applied. Using 2*L instead of L increases the number of transformations
+ // that are applied on average.
auto chance_of_continuing = static_cast<uint32_t>(
100.0 * (1.0 - (static_cast<double>(transformations_applied_so_far) /
- static_cast<double>(kTransformationLimit))));
+ (2.0 * static_cast<double>(
+ fuzzer_context_->GetTransformationLimit())))));
if (!fuzzer_context_->ChoosePercentage(chance_of_continuing)) {
// We have probabilistically decided to stop.
return false;
diff --git a/source/fuzz/fuzzer_context.cpp b/source/fuzz/fuzzer_context.cpp
index 65904dc..47bf4e2 100644
--- a/source/fuzz/fuzzer_context.cpp
+++ b/source/fuzz/fuzzer_context.cpp
@@ -20,14 +20,19 @@
namespace fuzz {
namespace {
+
+// Limits to help control the overall fuzzing process and rein in individual
+// fuzzer passes.
+const uint32_t kIdBoundLimit = 50000;
+const uint32_t kTransformationLimit = 2000;
+
// Default <minimum, maximum> pairs of probabilities for applying various
// transformations. All values are percentages. Keep them in alphabetical order.
-
const std::pair<uint32_t, uint32_t>
- kChanceOfAcceptingRepeatedPassRecommendation = {70, 100};
+ kChanceOfAcceptingRepeatedPassRecommendation = {50, 80};
const std::pair<uint32_t, uint32_t> kChanceOfAddingAccessChain = {5, 50};
-const std::pair<uint32_t, uint32_t> kChanceOfAddingAnotherPassToPassLoop = {85,
- 95};
+const std::pair<uint32_t, uint32_t> kChanceOfAddingAnotherPassToPassLoop = {50,
+ 90};
const std::pair<uint32_t, uint32_t> kChanceOfAddingAnotherStructField = {20,
90};
const std::pair<uint32_t, uint32_t> kChanceOfAddingArrayOrStructType = {20, 90};
@@ -392,5 +397,11 @@
return static_cast<protobufs::TransformationAddSynonym::SynonymType>(result);
}
+uint32_t FuzzerContext::GetIdBoundLimit() const { return kIdBoundLimit; }
+
+uint32_t FuzzerContext::GetTransformationLimit() const {
+ return kTransformationLimit;
+}
+
} // namespace fuzz
} // namespace spvtools
diff --git a/source/fuzz/fuzzer_context.h b/source/fuzz/fuzzer_context.h
index 9193dfc..8c51041 100644
--- a/source/fuzz/fuzzer_context.h
+++ b/source/fuzz/fuzzer_context.h
@@ -104,240 +104,271 @@
// Returns a vector of |count| fresh ids.
std::vector<uint32_t> GetFreshIds(const uint32_t count);
+ // A suggested limit on the id bound for the module being fuzzed. This is
+ // useful for deciding when to stop the overall fuzzing process. Furthermore,
+ // fuzzer passes that run the risk of spiralling out of control can
+ // periodically check this limit and terminate early if it has been reached.
+ uint32_t GetIdBoundLimit() const;
+
+ // A suggested limit on the number of transformations that should be applied.
+ // Also useful to control the overall fuzzing process and rein in individual
+ // fuzzer passes.
+ uint32_t GetTransformationLimit() const;
+
// Probabilities associated with applying various transformations.
// Keep them in alphabetical order.
- uint32_t GetChanceOfAcceptingRepeatedPassRecommendation() {
+ uint32_t GetChanceOfAcceptingRepeatedPassRecommendation() const {
return chance_of_accepting_repeated_pass_recommendation_;
}
- uint32_t GetChanceOfAddingAccessChain() {
+ uint32_t GetChanceOfAddingAccessChain() const {
return chance_of_adding_access_chain_;
}
- uint32_t GetChanceOfAddingAnotherPassToPassLoop() {
+ uint32_t GetChanceOfAddingAnotherPassToPassLoop() const {
return chance_of_adding_another_pass_to_pass_loop_;
}
- uint32_t GetChanceOfAddingAnotherStructField() {
+ uint32_t GetChanceOfAddingAnotherStructField() const {
return chance_of_adding_another_struct_field_;
}
- uint32_t GetChanceOfAddingArrayOrStructType() {
+ uint32_t GetChanceOfAddingArrayOrStructType() const {
return chance_of_adding_array_or_struct_type_;
}
- uint32_t GetChanceOfAddingBitInstructionSynonym() {
+ uint32_t GetChanceOfAddingBitInstructionSynonym() const {
return chance_of_adding_bit_instruction_synonym_;
}
- uint32_t GetChanceOfAddingBothBranchesWhenReplacingOpSelect() {
+ uint32_t GetChanceOfAddingBothBranchesWhenReplacingOpSelect() const {
return chance_of_adding_both_branches_when_replacing_opselect_;
}
- uint32_t GetChanceOfAddingCompositeExtract() {
+ uint32_t GetChanceOfAddingCompositeExtract() const {
return chance_of_adding_composite_extract_;
}
- uint32_t GetChanceOfAddingCompositeInsert() {
+ uint32_t GetChanceOfAddingCompositeInsert() const {
return chance_of_adding_composite_insert_;
}
- uint32_t GetChanceOfAddingCopyMemory() {
+ uint32_t GetChanceOfAddingCopyMemory() const {
return chance_of_adding_copy_memory_;
}
- uint32_t GetChanceOfAddingDeadBlock() { return chance_of_adding_dead_block_; }
- uint32_t GetChanceOfAddingDeadBreak() { return chance_of_adding_dead_break_; }
- uint32_t GetChanceOfAddingDeadContinue() {
+ uint32_t GetChanceOfAddingDeadBlock() const {
+ return chance_of_adding_dead_block_;
+ }
+ uint32_t GetChanceOfAddingDeadBreak() const {
+ return chance_of_adding_dead_break_;
+ }
+ uint32_t GetChanceOfAddingDeadContinue() const {
return chance_of_adding_dead_continue_;
}
- uint32_t GetChanceOfAddingEquationInstruction() {
+ uint32_t GetChanceOfAddingEquationInstruction() const {
return chance_of_adding_equation_instruction_;
}
- uint32_t GetChanceOfAddingGlobalVariable() {
+ uint32_t GetChanceOfAddingGlobalVariable() const {
return chance_of_adding_global_variable_;
}
- uint32_t GetChanceOfAddingImageSampleUnusedComponents() {
+ uint32_t GetChanceOfAddingImageSampleUnusedComponents() const {
return chance_of_adding_image_sample_unused_components_;
}
- uint32_t GetChanceOfAddingLoad() { return chance_of_adding_load_; }
- uint32_t GetChanceOfAddingLocalVariable() {
+ uint32_t GetChanceOfAddingLoad() const { return chance_of_adding_load_; }
+ uint32_t GetChanceOfAddingLocalVariable() const {
return chance_of_adding_local_variable_;
}
- uint32_t GetChanceOfAddingLoopPreheader() {
+ uint32_t GetChanceOfAddingLoopPreheader() const {
return chance_of_adding_loop_preheader_;
}
- uint32_t GetChanceOfAddingMatrixType() {
+ uint32_t GetChanceOfAddingMatrixType() const {
return chance_of_adding_matrix_type_;
}
- uint32_t GetChanceOfAddingNoContractionDecoration() {
+ uint32_t GetChanceOfAddingNoContractionDecoration() const {
return chance_of_adding_no_contraction_decoration_;
}
- uint32_t GetChanceOfAddingOpPhiSynonym() {
+ uint32_t GetChanceOfAddingOpPhiSynonym() const {
return chance_of_adding_opphi_synonym_;
}
- uint32_t GetChanceOfAddingParameters() { return chance_of_adding_parameters; }
- uint32_t GetChanceOfAddingRelaxedDecoration() {
+ uint32_t GetChanceOfAddingParameters() const {
+ return chance_of_adding_parameters;
+ }
+ uint32_t GetChanceOfAddingRelaxedDecoration() const {
return chance_of_adding_relaxed_decoration_;
}
- uint32_t GetChanceOfAddingStore() { return chance_of_adding_store_; }
- uint32_t GetChanceOfAddingSynonyms() { return chance_of_adding_synonyms_; }
- uint32_t GetChanceOfAddingTrueBranchWhenReplacingOpSelect() {
+ uint32_t GetChanceOfAddingStore() const { return chance_of_adding_store_; }
+ uint32_t GetChanceOfAddingSynonyms() const {
+ return chance_of_adding_synonyms_;
+ }
+ uint32_t GetChanceOfAddingTrueBranchWhenReplacingOpSelect() const {
return chance_of_adding_true_branch_when_replacing_opselect_;
}
- uint32_t GetChanceOfAddingVectorShuffle() {
+ uint32_t GetChanceOfAddingVectorShuffle() const {
return chance_of_adding_vector_shuffle_;
}
- uint32_t GetChanceOfAddingVectorType() {
+ uint32_t GetChanceOfAddingVectorType() const {
return chance_of_adding_vector_type_;
}
- uint32_t GetChanceOfAdjustingBranchWeights() {
+ uint32_t GetChanceOfAdjustingBranchWeights() const {
return chance_of_adjusting_branch_weights_;
}
- uint32_t GetChanceOfAdjustingFunctionControl() {
+ uint32_t GetChanceOfAdjustingFunctionControl() const {
return chance_of_adjusting_function_control_;
}
- uint32_t GetChanceOfAdjustingLoopControl() {
+ uint32_t GetChanceOfAdjustingLoopControl() const {
return chance_of_adjusting_loop_control_;
}
- uint32_t GetChanceOfAdjustingMemoryOperandsMask() {
+ uint32_t GetChanceOfAdjustingMemoryOperandsMask() const {
return chance_of_adjusting_memory_operands_mask_;
}
- uint32_t GetChanceOfAdjustingSelectionControl() {
+ uint32_t GetChanceOfAdjustingSelectionControl() const {
return chance_of_adjusting_selection_control_;
}
- uint32_t GetChanceOfCallingFunction() { return chance_of_calling_function_; }
- uint32_t GetChanceOfChoosingStructTypeVsArrayType() {
+ uint32_t GetChanceOfCallingFunction() const {
+ return chance_of_calling_function_;
+ }
+ uint32_t GetChanceOfChoosingStructTypeVsArrayType() const {
return chance_of_choosing_struct_type_vs_array_type_;
}
- uint32_t GetChanceOfChoosingWorkgroupStorageClass() {
+ uint32_t GetChanceOfChoosingWorkgroupStorageClass() const {
return chance_of_choosing_workgroup_storage_class_;
}
- uint32_t GetChanceOfConstructingComposite() {
+ uint32_t GetChanceOfConstructingComposite() const {
return chance_of_constructing_composite_;
}
- uint32_t GetChanceOfCopyingObject() { return chance_of_copying_object_; }
- uint32_t GetChanceOfCreatingIntSynonymsUsingLoops() {
+ uint32_t GetChanceOfCopyingObject() const {
+ return chance_of_copying_object_;
+ }
+ uint32_t GetChanceOfCreatingIntSynonymsUsingLoops() const {
return chance_of_creating_int_synonyms_using_loops_;
}
- uint32_t GetChanceOfDonatingAdditionalModule() {
+ uint32_t GetChanceOfDonatingAdditionalModule() const {
return chance_of_donating_additional_module_;
}
- uint32_t GetChanceOfDuplicatingRegionWithSelection() {
+ uint32_t GetChanceOfDuplicatingRegionWithSelection() const {
return chance_of_duplicating_region_with_selection_;
}
- uint32_t GetChanceOfExpandingVectorReduction() {
+ uint32_t GetChanceOfExpandingVectorReduction() const {
return chance_of_expanding_vector_reduction_;
}
- uint32_t GetChanceOfFlatteningConditionalBranch() {
+ uint32_t GetChanceOfFlatteningConditionalBranch() const {
return chance_of_flattening_conditional_branch_;
}
- uint32_t GetChanceOfGoingDeeperToExtractComposite() {
+ uint32_t GetChanceOfGoingDeeperToExtractComposite() const {
return chance_of_going_deeper_to_extract_composite_;
}
- uint32_t GetChanceOfGoingDeeperToInsertInComposite() {
+ uint32_t GetChanceOfGoingDeeperToInsertInComposite() const {
return chance_of_going_deeper_to_insert_in_composite_;
}
- uint32_t GetChanceOfGoingDeeperWhenMakingAccessChain() {
+ uint32_t GetChanceOfGoingDeeperWhenMakingAccessChain() const {
return chance_of_going_deeper_when_making_access_chain_;
}
- uint32_t GetChanceOfHavingTwoBlocksInLoopToCreateIntSynonym() {
+ uint32_t GetChanceOfHavingTwoBlocksInLoopToCreateIntSynonym() const {
return chance_of_having_two_blocks_in_loop_to_create_int_synonym_;
}
- uint32_t GetChanceOfInliningFunction() {
+ uint32_t GetChanceOfInliningFunction() const {
return chance_of_inlining_function_;
}
- uint32_t GetChanceOfInterchangingSignednessOfIntegerOperands() {
+ uint32_t GetChanceOfInterchangingSignednessOfIntegerOperands() const {
return chance_of_interchanging_signedness_of_integer_operands_;
}
- uint32_t GetChanceOfInterchangingZeroLikeConstants() {
+ uint32_t GetChanceOfInterchangingZeroLikeConstants() const {
return chance_of_interchanging_zero_like_constants_;
}
- uint32_t GetChanceOfInvertingComparisonOperators() {
+ uint32_t GetChanceOfInvertingComparisonOperators() const {
return chance_of_inverting_comparison_operators_;
}
- uint32_t ChanceOfMakingDonorLivesafe() {
+ uint32_t ChanceOfMakingDonorLivesafe() const {
return chance_of_making_donor_livesafe_;
}
- uint32_t GetChanceOfMakingVectorOperationDynamic() {
+ uint32_t GetChanceOfMakingVectorOperationDynamic() const {
return chance_of_making_vector_operation_dynamic_;
}
- uint32_t GetChanceOfMergingBlocks() { return chance_of_merging_blocks_; }
- uint32_t GetChanceOfMergingFunctionReturns() {
+ uint32_t GetChanceOfMergingBlocks() const {
+ return chance_of_merging_blocks_;
+ }
+ uint32_t GetChanceOfMergingFunctionReturns() const {
return chance_of_merging_function_returns_;
}
- uint32_t GetChanceOfMovingBlockDown() { return chance_of_moving_block_down_; }
- uint32_t GetChanceOfMutatingPointer() { return chance_of_mutating_pointer_; }
- uint32_t GetChanceOfObfuscatingConstant() {
+ uint32_t GetChanceOfMovingBlockDown() const {
+ return chance_of_moving_block_down_;
+ }
+ uint32_t GetChanceOfMutatingPointer() const {
+ return chance_of_mutating_pointer_;
+ }
+ uint32_t GetChanceOfObfuscatingConstant() const {
return chance_of_obfuscating_constant_;
}
- uint32_t GetChanceOfOutliningFunction() {
+ uint32_t GetChanceOfOutliningFunction() const {
return chance_of_outlining_function_;
}
- uint32_t GetChanceOfPermutingInstructions() {
+ uint32_t GetChanceOfPermutingInstructions() const {
return chance_of_permuting_instructions_;
}
- uint32_t GetChanceOfPermutingParameters() {
+ uint32_t GetChanceOfPermutingParameters() const {
return chance_of_permuting_parameters_;
}
- uint32_t GetChanceOfPermutingPhiOperands() {
+ uint32_t GetChanceOfPermutingPhiOperands() const {
return chance_of_permuting_phi_operands_;
}
- uint32_t GetChanceOfPropagatingInstructionsDown() {
+ uint32_t GetChanceOfPropagatingInstructionsDown() const {
return chance_of_propagating_instructions_down_;
}
- uint32_t GetChanceOfPropagatingInstructionsUp() {
+ uint32_t GetChanceOfPropagatingInstructionsUp() const {
return chance_of_propagating_instructions_up_;
}
- uint32_t GetChanceOfPushingIdThroughVariable() {
+ uint32_t GetChanceOfPushingIdThroughVariable() const {
return chance_of_pushing_id_through_variable_;
}
- uint32_t GetChanceOfReplacingAddSubMulWithCarryingExtended() {
+ uint32_t GetChanceOfReplacingAddSubMulWithCarryingExtended() const {
return chance_of_replacing_add_sub_mul_with_carrying_extended_;
}
- uint32_t GetChanceOfReplacingBranchFromDeadBlockWithExit() {
+ uint32_t GetChanceOfReplacingBranchFromDeadBlockWithExit() const {
return chance_of_replacing_branch_from_dead_block_with_exit_;
}
- uint32_t GetChanceOfReplacingCopyMemoryWithLoadStore() {
+ uint32_t GetChanceOfReplacingCopyMemoryWithLoadStore() const {
return chance_of_replacing_copy_memory_with_load_store_;
}
- uint32_t GetChanceOfReplacingCopyObjectWithStoreLoad() {
+ uint32_t GetChanceOfReplacingCopyObjectWithStoreLoad() const {
return chance_of_replacing_copyobject_with_store_load_;
}
- uint32_t GetChanceOfReplacingIdWithSynonym() {
+ uint32_t GetChanceOfReplacingIdWithSynonym() const {
return chance_of_replacing_id_with_synonym_;
}
- uint32_t GetChanceOfReplacingIrrelevantId() {
+ uint32_t GetChanceOfReplacingIrrelevantId() const {
return chance_of_replacing_irrelevant_id_;
}
- uint32_t GetChanceOfReplacingLinearAlgebraInstructions() {
+ uint32_t GetChanceOfReplacingLinearAlgebraInstructions() const {
return chance_of_replacing_linear_algebra_instructions_;
}
- uint32_t GetChanceOfReplacingLoadStoreWithCopyMemory() {
+ uint32_t GetChanceOfReplacingLoadStoreWithCopyMemory() const {
return chance_of_replacing_load_store_with_copy_memory_;
}
- uint32_t GetChanceOfReplacingOpPhiIdFromDeadPredecessor() {
+ uint32_t GetChanceOfReplacingOpPhiIdFromDeadPredecessor() const {
return chance_of_replacing_opphi_id_from_dead_predecessor_;
}
- uint32_t GetChanceOfReplacingOpselectWithConditionalBranch() {
+ uint32_t GetChanceOfReplacingOpselectWithConditionalBranch() const {
return chance_of_replacing_opselect_with_conditional_branch_;
}
- uint32_t GetChanceOfReplacingParametersWithGlobals() {
+ uint32_t GetChanceOfReplacingParametersWithGlobals() const {
return chance_of_replacing_parameters_with_globals_;
}
- uint32_t GetChanceOfReplacingParametersWithStruct() {
+ uint32_t GetChanceOfReplacingParametersWithStruct() const {
return chance_of_replacing_parameters_with_struct_;
}
- uint32_t GetChanceOfSplittingBlock() { return chance_of_splitting_block_; }
- uint32_t GetChanceOfSwappingConditionalBranchOperands() {
+ uint32_t GetChanceOfSplittingBlock() const {
+ return chance_of_splitting_block_;
+ }
+ uint32_t GetChanceOfSwappingConditionalBranchOperands() const {
return chance_of_swapping_conditional_branch_operands_;
}
- uint32_t GetChanceOfTogglingAccessChainInstruction() {
+ uint32_t GetChanceOfTogglingAccessChainInstruction() const {
return chance_of_toggling_access_chain_instruction_;
}
- uint32_t GetChanceOfWrappingRegionInSelection() {
+ uint32_t GetChanceOfWrappingRegionInSelection() const {
return chance_of_wrapping_region_in_selection_;
}
// Other functions to control transformations. Keep them in alphabetical
// order.
- uint32_t GetMaximumEquivalenceClassSizeForDataSynonymFactClosure() {
+ uint32_t GetMaximumEquivalenceClassSizeForDataSynonymFactClosure() const {
return max_equivalence_class_size_for_data_synonym_fact_closure_;
}
- uint32_t GetMaximumNumberOfFunctionParameters() {
+ uint32_t GetMaximumNumberOfFunctionParameters() const {
return max_number_of_function_parameters_;
}
- uint32_t GetMaximumNumberOfParametersReplacedWithStruct() {
+ uint32_t GetMaximumNumberOfParametersReplacedWithStruct() const {
return max_number_of_parameters_replaced_with_struct_;
}
std::pair<uint32_t, uint32_t> GetRandomBranchWeights() {
diff --git a/source/fuzz/fuzzer_pass_add_bit_instruction_synonyms.cpp b/source/fuzz/fuzzer_pass_add_bit_instruction_synonyms.cpp
index bd2dd3d..7b9ac4e 100644
--- a/source/fuzz/fuzzer_pass_add_bit_instruction_synonyms.cpp
+++ b/source/fuzz/fuzzer_pass_add_bit_instruction_synonyms.cpp
@@ -35,6 +35,13 @@
for (auto& function : *GetIRContext()->module()) {
for (auto& block : function) {
for (auto& instruction : block) {
+ // This fuzzer pass can add a *lot* of ids. We bail out early if we hit
+ // the recommended id limit.
+ if (GetIRContext()->module()->id_bound() >=
+ GetFuzzerContext()->GetIdBoundLimit()) {
+ return;
+ }
+
// Randomly decides whether the transformation will be applied.
if (!GetFuzzerContext()->ChoosePercentage(
GetFuzzerContext()->GetChanceOfAddingBitInstructionSynonym())) {
diff --git a/source/fuzz/fuzzer_pass_add_composite_extract.cpp b/source/fuzz/fuzzer_pass_add_composite_extract.cpp
index 3b40d5f..132a49d 100644
--- a/source/fuzz/fuzzer_pass_add_composite_extract.cpp
+++ b/source/fuzz/fuzzer_pass_add_composite_extract.cpp
@@ -91,35 +91,29 @@
available_composites)];
composite_id = inst->result_id();
- const auto* type =
- GetIRContext()->get_type_mgr()->GetType(inst->type_id());
- assert(type && "Composite instruction has invalid type id");
-
+ auto type_id = inst->type_id();
do {
uint32_t number_of_members = 0;
- if (const auto* array_type = type->AsArray()) {
- const auto* type_inst =
- GetIRContext()->get_def_use_mgr()->GetDef(inst->type_id());
- assert(type_inst && "Type instruction must exist");
+ const auto* type_inst =
+ GetIRContext()->get_def_use_mgr()->GetDef(type_id);
+ assert(type_inst && "Composite instruction has invalid type id");
- number_of_members =
- fuzzerutil::GetArraySize(*type_inst, GetIRContext());
- type = array_type->element_type();
- } else if (const auto* vector_type = type->AsVector()) {
- number_of_members = vector_type->element_count();
- type = vector_type->element_type();
- } else if (const auto* matrix_type = type->AsMatrix()) {
- number_of_members = matrix_type->element_count();
- type = matrix_type->element_type();
- } else if (const auto* struct_type = type->AsStruct()) {
- number_of_members =
- static_cast<uint32_t>(struct_type->element_types().size());
- // The next value of |type| will be assigned when we know the
- // index of the OpTypeStruct's operand.
- } else {
- assert(false && "|inst| is not a composite");
- return;
+ switch (type_inst->opcode()) {
+ case SpvOpTypeArray:
+ number_of_members =
+ fuzzerutil::GetArraySize(*type_inst, GetIRContext());
+ break;
+ case SpvOpTypeVector:
+ case SpvOpTypeMatrix:
+ number_of_members = type_inst->GetSingleWordInOperand(1);
+ break;
+ case SpvOpTypeStruct:
+ number_of_members = type_inst->NumInOperands();
+ break;
+ default:
+ assert(false && "|type_inst| is not a composite");
+ return;
}
if (number_of_members == 0) {
@@ -130,10 +124,21 @@
GetFuzzerContext()->GetRandomCompositeExtractIndex(
number_of_members));
- if (const auto* struct_type = type->AsStruct()) {
- type = struct_type->element_types()[indices.back()];
+ switch (type_inst->opcode()) {
+ case SpvOpTypeArray:
+ case SpvOpTypeVector:
+ case SpvOpTypeMatrix:
+ type_id = type_inst->GetSingleWordInOperand(0);
+ break;
+ case SpvOpTypeStruct:
+ type_id = type_inst->GetSingleWordInOperand(indices.back());
+ break;
+ default:
+ assert(false && "|type_inst| is not a composite");
+ return;
}
- } while (fuzzerutil::IsCompositeType(type) &&
+ } while (fuzzerutil::IsCompositeType(
+ GetIRContext()->get_type_mgr()->GetType(type_id)) &&
GetFuzzerContext()->ChoosePercentage(
GetFuzzerContext()
->GetChanceOfGoingDeeperToExtractComposite()));
diff --git a/source/fuzz/fuzzer_pass_flatten_conditional_branches.cpp b/source/fuzz/fuzzer_pass_flatten_conditional_branches.cpp
index c7c2933..1e21aa5 100644
--- a/source/fuzz/fuzzer_pass_flatten_conditional_branches.cpp
+++ b/source/fuzz/fuzzer_pass_flatten_conditional_branches.cpp
@@ -71,7 +71,8 @@
// Do not consider this header if the conditional cannot be flattened.
if (!TransformationFlattenConditionalBranch::
GetProblematicInstructionsIfConditionalCanBeFlattened(
- GetIRContext(), header, &instructions_that_need_ids)) {
+ GetIRContext(), header, *GetTransformationContext(),
+ &instructions_that_need_ids)) {
continue;
}
@@ -214,7 +215,7 @@
}
}
- wrappers_info.emplace_back(wrapper_info);
+ wrappers_info.push_back(std::move(wrapper_info));
}
// Apply the transformation, evenly choosing whether to lay out the true
diff --git a/source/fuzz/pass_management/repeated_pass_manager_random_with_recommendations.cpp b/source/fuzz/pass_management/repeated_pass_manager_random_with_recommendations.cpp
index 920d13f..6d77f25 100644
--- a/source/fuzz/pass_management/repeated_pass_manager_random_with_recommendations.cpp
+++ b/source/fuzz/pass_management/repeated_pass_manager_random_with_recommendations.cpp
@@ -53,6 +53,8 @@
result = recommended_passes_.front();
recommended_passes_.pop_front();
}
+ assert(result != nullptr && "A pass must have been chosen.");
+ last_pass_choice_ = result;
return result;
}
diff --git a/source/fuzz/transformation_add_bit_instruction_synonym.cpp b/source/fuzz/transformation_add_bit_instruction_synonym.cpp
index 9fdc37e..6cdfdfb 100644
--- a/source/fuzz/transformation_add_bit_instruction_synonym.cpp
+++ b/source/fuzz/transformation_add_bit_instruction_synonym.cpp
@@ -46,7 +46,8 @@
// |instruction| must be defined and must be a supported bit instruction.
if (!instruction || (instruction->opcode() != SpvOpBitwiseOr &&
instruction->opcode() != SpvOpBitwiseXor &&
- instruction->opcode() != SpvOpBitwiseAnd)) {
+ instruction->opcode() != SpvOpBitwiseAnd &&
+ instruction->opcode() != SpvOpNot)) {
return false;
}
diff --git a/source/fuzz/transformation_flatten_conditional_branch.cpp b/source/fuzz/transformation_flatten_conditional_branch.cpp
index bad4972..dec933c 100644
--- a/source/fuzz/transformation_flatten_conditional_branch.cpp
+++ b/source/fuzz/transformation_flatten_conditional_branch.cpp
@@ -82,65 +82,6 @@
}
}
- if (OpSelectArgumentsAreRestricted(ir_context)) {
- // OpPhi instructions at the convergence block for the selection are handled
- // by turning them into OpSelect instructions. As the SPIR-V version in use
- // has restrictions on the arguments that OpSelect can take, we must check
- // that any OpPhi instructions are compatible with these restrictions.
- uint32_t convergence_block_id =
- FindConvergenceBlock(ir_context, *header_block);
- // Consider every OpPhi instruction at the convergence block.
- if (!ir_context->cfg()
- ->block(convergence_block_id)
- ->WhileEachPhiInst([this,
- ir_context](opt::Instruction* inst) -> bool {
- // Decide whether the OpPhi can be handled based on its result
- // type.
- opt::Instruction* phi_result_type =
- ir_context->get_def_use_mgr()->GetDef(inst->type_id());
- switch (phi_result_type->opcode()) {
- case SpvOpTypeBool:
- case SpvOpTypeInt:
- case SpvOpTypeFloat:
- case SpvOpTypePointer:
- // Fine: OpSelect can work directly on scalar and pointer
- // types.
- return true;
- case SpvOpTypeVector: {
- // In its restricted form, OpSelect can only select between
- // vectors if the condition of the select is a boolean
- // boolean vector. We thus require the appropriate boolean
- // vector type to be present.
- uint32_t bool_type_id =
- fuzzerutil::MaybeGetBoolType(ir_context);
- uint32_t dimension =
- phi_result_type->GetSingleWordInOperand(1);
- if (fuzzerutil::MaybeGetVectorType(ir_context, bool_type_id,
- dimension) == 0) {
- // The required boolean vector type is not present.
- return false;
- }
- // The transformation needs to be equipped with a fresh id
- // in which to store the vectorized version of the selection
- // construct's condition.
- switch (dimension) {
- case 2:
- return message_.fresh_id_for_bvec2_selector() != 0;
- case 3:
- return message_.fresh_id_for_bvec3_selector() != 0;
- default:
- assert(dimension == 4 && "Invalid vector dimension.");
- return message_.fresh_id_for_bvec4_selector() != 0;
- }
- }
- default:
- return false;
- }
- })) {
- return false;
- }
- }
-
// Use a set to keep track of the instructions that require fresh ids.
std::set<opt::Instruction*> instructions_that_need_ids;
@@ -148,7 +89,8 @@
// if so, add all the problematic instructions that need to be enclosed inside
// conditionals to |instructions_that_need_ids|.
if (!GetProblematicInstructionsIfConditionalCanBeFlattened(
- ir_context, header_block, &instructions_that_need_ids)) {
+ ir_context, header_block, transformation_context,
+ &instructions_that_need_ids)) {
return false;
}
@@ -205,6 +147,69 @@
}
}
+ if (OpSelectArgumentsAreRestricted(ir_context)) {
+ // OpPhi instructions at the convergence block for the selection are handled
+ // by turning them into OpSelect instructions. As the SPIR-V version in use
+ // has restrictions on the arguments that OpSelect can take, we must check
+ // that any OpPhi instructions are compatible with these restrictions.
+ uint32_t convergence_block_id =
+ FindConvergenceBlock(ir_context, *header_block);
+ // Consider every OpPhi instruction at the convergence block.
+ if (!ir_context->cfg()
+ ->block(convergence_block_id)
+ ->WhileEachPhiInst([this,
+ ir_context](opt::Instruction* inst) -> bool {
+ // Decide whether the OpPhi can be handled based on its result
+ // type.
+ opt::Instruction* phi_result_type =
+ ir_context->get_def_use_mgr()->GetDef(inst->type_id());
+ switch (phi_result_type->opcode()) {
+ case SpvOpTypeBool:
+ case SpvOpTypeInt:
+ case SpvOpTypeFloat:
+ case SpvOpTypePointer:
+ // Fine: OpSelect can work directly on scalar and pointer
+ // types.
+ return true;
+ case SpvOpTypeVector: {
+ // In its restricted form, OpSelect can only select between
+ // vectors if the condition of the select is a boolean
+ // boolean vector. We thus require the appropriate boolean
+ // vector type to be present.
+ uint32_t bool_type_id =
+ fuzzerutil::MaybeGetBoolType(ir_context);
+ if (!bool_type_id) {
+ return false;
+ }
+
+ uint32_t dimension =
+ phi_result_type->GetSingleWordInOperand(1);
+ if (fuzzerutil::MaybeGetVectorType(ir_context, bool_type_id,
+ dimension) == 0) {
+ // The required boolean vector type is not present.
+ return false;
+ }
+ // The transformation needs to be equipped with a fresh id
+ // in which to store the vectorized version of the selection
+ // construct's condition.
+ switch (dimension) {
+ case 2:
+ return message_.fresh_id_for_bvec2_selector() != 0;
+ case 3:
+ return message_.fresh_id_for_bvec3_selector() != 0;
+ default:
+ assert(dimension == 4 && "Invalid vector dimension.");
+ return message_.fresh_id_for_bvec4_selector() != 0;
+ }
+ }
+ default:
+ return false;
+ }
+ })) {
+ return false;
+ }
+ }
+
// All checks were passed.
return true;
}
@@ -428,6 +433,7 @@
bool TransformationFlattenConditionalBranch::
GetProblematicInstructionsIfConditionalCanBeFlattened(
opt::IRContext* ir_context, opt::BasicBlock* header,
+ const TransformationContext& transformation_context,
std::set<opt::Instruction*>* instructions_that_need_ids) {
uint32_t merge_block_id = header->MergeBlockIdIfAny();
assert(merge_block_id &&
@@ -441,6 +447,11 @@
auto postdominator_analysis =
ir_context->GetPostDominatorAnalysis(enclosing_function);
+ // |header| must be reachable.
+ if (!dominator_analysis->IsReachable(header)) {
+ return false;
+ }
+
// Check that the header and the merge block describe a single-entry,
// single-exit region.
if (!dominator_analysis->Dominates(header->id(), merge_block_id) ||
@@ -454,13 +465,22 @@
// - they branch unconditionally to another block
// Add any side-effecting instruction, requiring fresh ids, to
// |instructions_that_need_ids|
- std::list<uint32_t> to_check;
+ std::queue<uint32_t> to_check;
header->ForEachSuccessorLabel(
- [&to_check](uint32_t label) { to_check.push_back(label); });
+ [&to_check](uint32_t label) { to_check.push(label); });
+ auto* structured_cfg = ir_context->GetStructuredCFGAnalysis();
while (!to_check.empty()) {
uint32_t block_id = to_check.front();
- to_check.pop_front();
+ to_check.pop();
+
+ if (structured_cfg->ContainingConstruct(block_id) != header->id() &&
+ block_id != merge_block_id) {
+ // This block can be reached from the |header| but doesn't belong to its
+ // selection construct. This might be a continue target of some loop -
+ // we can't flatten the |header|.
+ return false;
+ }
// If the block post-dominates the header, this is where flow converges, and
// we don't need to check this branch any further, because the
@@ -470,6 +490,15 @@
continue;
}
+ if (!transformation_context.GetFactManager()->BlockIsDead(header->id()) &&
+ transformation_context.GetFactManager()->BlockIsDead(block_id)) {
+ // The |header| is not dead but the |block_id| is. Since |block_id|
+ // doesn't postdominate the |header|, CFG hasn't converged yet. Thus, we
+ // don't flatten the construct to prevent |block_id| from becoming
+ // executable.
+ return false;
+ }
+
auto block = ir_context->cfg()->block(block_id);
// The block must not have a merge instruction, because inner constructs are
@@ -518,7 +547,7 @@
// Add the successor of this block to the list of blocks that need to be
// checked.
- to_check.push_back(block->terminator()->GetSingleWordInOperand(0));
+ to_check.push(block->terminator()->GetSingleWordInOperand(0));
}
// All the blocks are compatible with the transformation and this is indeed a
@@ -564,7 +593,7 @@
opt::Instruction* instruction,
const protobufs::SideEffectWrapperInfo& wrapper_info, uint32_t condition_id,
bool exec_if_cond_true, std::vector<uint32_t>* dead_blocks,
- std::vector<uint32_t>* irrelevant_ids) const {
+ std::vector<uint32_t>* irrelevant_ids) {
// Get the next instruction (it will be useful for splitting).
auto next_instruction = instruction->NextNode();
@@ -810,7 +839,7 @@
void TransformationFlattenConditionalBranch::AddBooleanVectorConstructorToBlock(
uint32_t fresh_id, uint32_t dimension,
const opt::Operand& branch_condition_operand, opt::IRContext* ir_context,
- opt::BasicBlock* block) const {
+ opt::BasicBlock* block) {
opt::Instruction::OperandList in_operands;
for (uint32_t i = 0; i < dimension; i++) {
in_operands.emplace_back(branch_condition_operand);
diff --git a/source/fuzz/transformation_flatten_conditional_branch.h b/source/fuzz/transformation_flatten_conditional_branch.h
index 2d5e8d7..e8cb414 100644
--- a/source/fuzz/transformation_flatten_conditional_branch.h
+++ b/source/fuzz/transformation_flatten_conditional_branch.h
@@ -72,6 +72,7 @@
// instructions are OpSelectionMerge and OpBranchConditional.
static bool GetProblematicInstructionsIfConditionalCanBeFlattened(
opt::IRContext* ir_context, opt::BasicBlock* header,
+ const TransformationContext& transformation_context,
std::set<opt::Instruction*>* instructions_that_need_ids);
// Returns true iff the given instruction needs a placeholder to be enclosed
@@ -117,14 +118,14 @@
// |dead_blocks| and |irrelevant_ids| are used to record the ids of blocks
// and instructions for which dead block and irrelevant id facts should
// ultimately be created.
- opt::BasicBlock* EncloseInstructionInConditional(
+ static opt::BasicBlock* EncloseInstructionInConditional(
opt::IRContext* ir_context,
const TransformationContext& transformation_context,
opt::BasicBlock* block, opt::Instruction* instruction,
const protobufs::SideEffectWrapperInfo& wrapper_info,
uint32_t condition_id, bool exec_if_cond_true,
std::vector<uint32_t>* dead_blocks,
- std::vector<uint32_t>* irrelevant_ids) const;
+ std::vector<uint32_t>* irrelevant_ids);
// Turns every OpPhi instruction of |convergence_block| -- the convergence
// block for |header_block| (both in |ir_context|) into an OpSelect
@@ -137,10 +138,10 @@
// |ir_context|, with result id given by |fresh_id|. The instruction will
// make a |dimension|-dimensional boolean vector with
// |branch_condition_operand| at every component.
- void AddBooleanVectorConstructorToBlock(
+ static void AddBooleanVectorConstructorToBlock(
uint32_t fresh_id, uint32_t dimension,
const opt::Operand& branch_condition_operand, opt::IRContext* ir_context,
- opt::BasicBlock* block) const;
+ opt::BasicBlock* block);
// Returns true if the given instruction either has no side effects or it can
// be handled by being enclosed in a conditional.
diff --git a/source/opt/debug_info_manager.cpp b/source/opt/debug_info_manager.cpp
index 48285bd..87ef655 100644
--- a/source/opt/debug_info_manager.cpp
+++ b/source/opt/debug_info_manager.cpp
@@ -384,7 +384,8 @@
return dbg_decl_itr != var_id_to_dbg_decl_.end();
}
-void DebugInfoManager::KillDebugDeclares(uint32_t variable_id) {
+bool DebugInfoManager::KillDebugDeclares(uint32_t variable_id) {
+ bool modified = false;
auto dbg_decl_itr = var_id_to_dbg_decl_.find(variable_id);
if (dbg_decl_itr != var_id_to_dbg_decl_.end()) {
// We intentionally copy the list of DebugDeclare instructions because
@@ -394,9 +395,11 @@
for (auto* dbg_decl : copy_dbg_decls) {
context()->KillInst(dbg_decl);
+ modified = true;
}
var_id_to_dbg_decl_.erase(dbg_decl_itr);
}
+ return modified;
}
uint32_t DebugInfoManager::GetParentScope(uint32_t child_scope) {
@@ -514,16 +517,18 @@
return added_dbg_value;
}
-void DebugInfoManager::AddDebugValueIfVarDeclIsVisible(
+bool DebugInfoManager::AddDebugValueIfVarDeclIsVisible(
Instruction* scope_and_line, uint32_t variable_id, uint32_t value_id,
Instruction* insert_pos,
std::unordered_set<Instruction*>* invisible_decls) {
- auto dbg_decl_itr = var_id_to_dbg_decl_.find(variable_id);
- if (dbg_decl_itr == var_id_to_dbg_decl_.end()) return;
+ assert(scope_and_line != nullptr);
+ auto dbg_decl_itr = var_id_to_dbg_decl_.find(variable_id);
+ if (dbg_decl_itr == var_id_to_dbg_decl_.end()) return false;
+
+ bool modified = false;
for (auto* dbg_decl_or_val : dbg_decl_itr->second) {
- if (scope_and_line &&
- !IsDeclareVisibleToInstr(dbg_decl_or_val, scope_and_line)) {
+ if (!IsDeclareVisibleToInstr(dbg_decl_or_val, scope_and_line)) {
if (invisible_decls) invisible_decls->insert(dbg_decl_or_val);
continue;
}
@@ -547,10 +552,11 @@
kDebugValueOperandLocalVariableIndex),
value_id, 0, index_id, insert_before);
assert(added_dbg_value != nullptr);
- added_dbg_value->UpdateDebugInfoFrom(scope_and_line ? scope_and_line
- : dbg_decl_or_val);
+ added_dbg_value->UpdateDebugInfoFrom(scope_and_line);
AnalyzeDebugInst(added_dbg_value);
+ modified = true;
}
+ return modified;
}
bool DebugInfoManager::AddDebugValueForDecl(Instruction* dbg_decl,
diff --git a/source/opt/debug_info_manager.h b/source/opt/debug_info_manager.h
index edb11b7..630be4f 100644
--- a/source/opt/debug_info_manager.h
+++ b/source/opt/debug_info_manager.h
@@ -138,14 +138,15 @@
bool IsVariableDebugDeclared(uint32_t variable_id);
// Kills all debug declaration instructions with Deref whose 'Local Variable'
- // operand is |variable_id|.
- void KillDebugDeclares(uint32_t variable_id);
+ // operand is |variable_id|. Returns whether it kills an instruction or not.
+ bool KillDebugDeclares(uint32_t variable_id);
// Generates a DebugValue instruction with value |value_id| for every local
// variable that is in the scope of |scope_and_line| and whose memory is
// |variable_id| and inserts it after the instruction |insert_pos|.
- // |invisible_decls| returns DebugDeclares invisible to |scope_and_line|.
- void AddDebugValueIfVarDeclIsVisible(
+ // Returns whether a DebugValue is added or not. |invisible_decls| returns
+ // DebugDeclares invisible to |scope_and_line|.
+ bool AddDebugValueIfVarDeclIsVisible(
Instruction* scope_and_line, uint32_t variable_id, uint32_t value_id,
Instruction* insert_pos,
std::unordered_set<Instruction*>* invisible_decls);
diff --git a/source/opt/local_single_store_elim_pass.cpp b/source/opt/local_single_store_elim_pass.cpp
index f1b8177..99c0fb2 100644
--- a/source/opt/local_single_store_elim_pass.cpp
+++ b/source/opt/local_single_store_elim_pass.cpp
@@ -148,16 +148,41 @@
context()->get_type_mgr()->GetType(var_inst->type_id());
const analysis::Type* store_type = var_type->AsPointer()->pointee_type();
if (!(store_type->AsStruct() || store_type->AsArray())) {
- context()->get_debug_info_mgr()->AddDebugValueIfVarDeclIsVisible(
- nullptr, var_id, store_inst->GetSingleWordInOperand(1), store_inst,
- nullptr);
- context()->get_debug_info_mgr()->KillDebugDeclares(var_id);
+ modified |= RewriteDebugDeclares(store_inst, var_id);
}
}
return modified;
}
+bool LocalSingleStoreElimPass::RewriteDebugDeclares(Instruction* store_inst,
+ uint32_t var_id) {
+ std::unordered_set<Instruction*> invisible_decls;
+ uint32_t value_id = store_inst->GetSingleWordInOperand(1);
+ bool modified =
+ context()->get_debug_info_mgr()->AddDebugValueIfVarDeclIsVisible(
+ store_inst, var_id, value_id, store_inst, &invisible_decls);
+
+ // For cases like the argument passing for an inlined function, the value
+ // assignment is out of DebugDeclare's scope, but we have to preserve the
+ // value assignment information using DebugValue. Generally, we need
+ // ssa-rewrite analysis to decide a proper value assignment but at this point
+ // we confirm that |var_id| has a single store. We can safely add DebugValue.
+ if (!invisible_decls.empty()) {
+ BasicBlock* store_block = context()->get_instr_block(store_inst);
+ DominatorAnalysis* dominator_analysis =
+ context()->GetDominatorAnalysis(store_block->GetParent());
+ for (auto* decl : invisible_decls) {
+ if (dominator_analysis->Dominates(store_inst, decl)) {
+ context()->get_debug_info_mgr()->AddDebugValueForDecl(decl, value_id);
+ modified = true;
+ }
+ }
+ }
+ modified |= context()->get_debug_info_mgr()->KillDebugDeclares(var_id);
+ return modified;
+}
+
Instruction* LocalSingleStoreElimPass::FindSingleStoreAndCheckUses(
Instruction* var_inst, const std::vector<Instruction*>& users) const {
// Make sure there is exactly 1 store.
diff --git a/source/opt/local_single_store_elim_pass.h b/source/opt/local_single_store_elim_pass.h
index d66a441..3aa0f02 100644
--- a/source/opt/local_single_store_elim_pass.h
+++ b/source/opt/local_single_store_elim_pass.h
@@ -94,6 +94,10 @@
bool RewriteLoads(Instruction* store_inst,
const std::vector<Instruction*>& uses, bool* all_rewritten);
+ // Replaces DebugDeclares of |var_id| with DebugValues using the value
+ // assignment of |store_inst|.
+ bool RewriteDebugDeclares(Instruction* store_inst, uint32_t var_id);
+
// Extensions supported by this pass.
std::unordered_set<std::string> extensions_allowlist_;
};
diff --git a/source/opt/optimizer.cpp b/source/opt/optimizer.cpp
index bc14411..1ded2ee 100644
--- a/source/opt/optimizer.cpp
+++ b/source/opt/optimizer.cpp
@@ -161,6 +161,7 @@
.RegisterPass(CreateDeadBranchElimPass())
.RegisterPass(CreateMergeReturnPass())
.RegisterPass(CreateInlineExhaustivePass())
+ .RegisterPass(CreateEliminateDeadFunctionsPass())
.RegisterPass(CreateAggressiveDCEPass())
.RegisterPass(CreatePrivateToLocalPass())
.RegisterPass(CreateLocalSingleBlockLoadStoreElimPass())
diff --git a/source/opt/ssa_rewrite_pass.cpp b/source/opt/ssa_rewrite_pass.cpp
index 5a56887..3ff0361 100644
--- a/source/opt/ssa_rewrite_pass.cpp
+++ b/source/opt/ssa_rewrite_pass.cpp
@@ -47,6 +47,7 @@
#include "source/opcode.h"
#include "source/opt/cfg.h"
#include "source/opt/mem_pass.h"
+#include "source/opt/types.h"
#include "source/util/make_unique.h"
// Debug logging (0: Off, 1-N: Verbosity level). Replace this with the
@@ -326,32 +327,94 @@
}
bool SSARewriter::ProcessLoad(Instruction* inst, BasicBlock* bb) {
+ // Get the pointer that we are using to load from.
uint32_t var_id = 0;
(void)pass_->GetPtr(inst, &var_id);
- if (pass_->IsTargetVar(var_id)) {
- // Get the immediate reaching definition for |var_id|.
- uint32_t val_id = GetReachingDef(var_id, bb);
+
+ // Get the immediate reaching definition for |var_id|.
+ //
+ // In the presence of variable pointers, the reaching definition may be
+ // another pointer. For example, the following fragment:
+ //
+ // %2 = OpVariable %_ptr_Input_float Input
+ // %11 = OpVariable %_ptr_Function__ptr_Input_float Function
+ // OpStore %11 %2
+ // %12 = OpLoad %_ptr_Input_float %11
+ // %13 = OpLoad %float %12
+ //
+ // corresponds to the pseudo-code:
+ //
+ // layout(location = 0) in flat float *%2
+ // float %13;
+ // float *%12;
+ // float **%11;
+ // *%11 = %2;
+ // %12 = *%11;
+ // %13 = *%12;
+ //
+ // which ultimately, should correspond to:
+ //
+ // %13 = *%2;
+ //
+ // During rewriting, the pointer %12 is found to be replaceable by %2 (i.e.,
+ // load_replacement_[12] is 2). However, when processing the load
+ // %13 = *%12, the type of %12's reaching definition is another float
+ // pointer (%2), instead of a float value.
+ //
+ // When this happens, we need to continue looking up the reaching definition
+ // chain until we get to a float value or a non-target var (i.e. a variable
+ // that cannot be SSA replaced, like %2 in this case since it is a function
+ // argument).
+ analysis::DefUseManager* def_use_mgr = pass_->context()->get_def_use_mgr();
+ analysis::TypeManager* type_mgr = pass_->context()->get_type_mgr();
+ analysis::Type* load_type = type_mgr->GetType(inst->type_id());
+ uint32_t val_id = 0;
+ bool found_reaching_def = false;
+ while (!found_reaching_def) {
+ if (!pass_->IsTargetVar(var_id)) {
+ // If the variable we are loading from is not an SSA target (globals,
+ // function parameters), do nothing.
+ return true;
+ }
+
+ val_id = GetReachingDef(var_id, bb);
if (val_id == 0) {
return false;
}
- // Schedule a replacement for the result of this load instruction with
- // |val_id|. After all the rewriting decisions are made, every use of
- // this load will be replaced with |val_id|.
- const uint32_t load_id = inst->result_id();
- assert(load_replacement_.count(load_id) == 0);
- load_replacement_[load_id] = val_id;
- PhiCandidate* defining_phi = GetPhiCandidate(val_id);
- if (defining_phi) {
- defining_phi->AddUser(load_id);
+ // If the reaching definition is a pointer type different than the type of
+ // the instruction we are analyzing, then it must be a reference to another
+ // pointer (otherwise, this would be invalid SPIRV). We continue
+ // de-referencing it by making |val_id| be |var_id|.
+ //
+ // NOTE: if there is no reaching definition instruction, it means |val_id|
+ // is an undef.
+ Instruction* reaching_def_inst = def_use_mgr->GetDef(val_id);
+ if (reaching_def_inst &&
+ !type_mgr->GetType(reaching_def_inst->type_id())->IsSame(load_type)) {
+ var_id = val_id;
+ } else {
+ found_reaching_def = true;
}
+ }
+
+ // Schedule a replacement for the result of this load instruction with
+ // |val_id|. After all the rewriting decisions are made, every use of
+ // this load will be replaced with |val_id|.
+ uint32_t load_id = inst->result_id();
+ assert(load_replacement_.count(load_id) == 0);
+ load_replacement_[load_id] = val_id;
+ PhiCandidate* defining_phi = GetPhiCandidate(val_id);
+ if (defining_phi) {
+ defining_phi->AddUser(load_id);
+ }
#if SSA_REWRITE_DEBUGGING_LEVEL > 1
- std::cerr << "\tFound load: "
- << inst->PrettyPrint(SPV_BINARY_TO_TEXT_OPTION_FRIENDLY_NAMES)
- << " (replacement for %" << load_id << " is %" << val_id << ")\n";
+ std::cerr << "\tFound load: "
+ << inst->PrettyPrint(SPV_BINARY_TO_TEXT_OPTION_FRIENDLY_NAMES)
+ << " (replacement for %" << load_id << " is %" << val_id << ")\n";
#endif
- }
+
return true;
}
@@ -390,8 +453,8 @@
}
}
- // Seal |bb|. This means that all the stores in it have been scanned and it's
- // ready to feed them into its successors.
+ // Seal |bb|. This means that all the stores in it have been scanned and
+ // it's ready to feed them into its successors.
SealBlock(bb);
#if SSA_REWRITE_DEBUGGING_LEVEL > 1
@@ -504,8 +567,8 @@
}
// Scan uses for all inserted Phi instructions. Do this separately from the
- // registration of the Phi instruction itself to avoid trying to analyze uses
- // of Phi instructions that have not been registered yet.
+ // registration of the Phi instruction itself to avoid trying to analyze
+ // uses of Phi instructions that have not been registered yet.
for (Instruction* phi_inst : generated_phis) {
pass_->get_def_use_mgr()->AnalyzeInstUse(&*phi_inst);
}
@@ -562,7 +625,8 @@
// This candidate is now completed.
phi_candidate->MarkComplete();
- // If |phi_candidate| is not trivial, add it to the list of Phis to generate.
+ // If |phi_candidate| is not trivial, add it to the list of Phis to
+ // generate.
if (TryRemoveTrivialPhi(phi_candidate) == phi_candidate->result_id()) {
// If we could not remove |phi_candidate|, it means that it is complete
// and not trivial. Add it to the list of Phis to generate.
diff --git a/source/val/validate_builtins.cpp b/source/val/validate_builtins.cpp
index 1d85f88..2c5604c 100644
--- a/source/val/validate_builtins.cpp
+++ b/source/val/validate_builtins.cpp
@@ -2603,17 +2603,19 @@
assert(function_id_ == 0);
for (const auto em :
{SpvExecutionModelVertex, SpvExecutionModelTessellationEvaluation,
- SpvExecutionModelGeometry}) {
+ SpvExecutionModelGeometry, SpvExecutionModelMeshNV}) {
id_to_at_reference_checks_[referenced_from_inst.id()].push_back(
- std::bind(&BuiltInsValidator::ValidateNotCalledWithExecutionModel,
- this,
- "Vulkan spec doesn't allow BuiltIn Layer and "
- "ViewportIndex to be "
- "used for variables with Input storage class if "
- "execution model is Vertex, TessellationEvaluation, or "
- "Geometry.",
- em, decoration, built_in_inst, referenced_from_inst,
- std::placeholders::_1));
+ std::bind(
+ &BuiltInsValidator::ValidateNotCalledWithExecutionModel, this,
+ std::string(
+ _.VkErrorID((operand == SpvBuiltInLayer) ? 4274 : 4406) +
+ "Vulkan spec doesn't allow BuiltIn Layer and "
+ "ViewportIndex to be "
+ "used for variables with Input storage class if "
+ "execution model is Vertex, TessellationEvaluation, "
+ "Geometry, or MeshNV."),
+ em, decoration, built_in_inst, referenced_from_inst,
+ std::placeholders::_1));
}
}
@@ -2621,11 +2623,12 @@
assert(function_id_ == 0);
id_to_at_reference_checks_[referenced_from_inst.id()].push_back(std::bind(
&BuiltInsValidator::ValidateNotCalledWithExecutionModel, this,
- "Vulkan spec doesn't allow BuiltIn Layer and "
- "ViewportIndex to be "
- "used for variables with Output storage class if "
- "execution model is "
- "Fragment.",
+ std::string(_.VkErrorID((operand == SpvBuiltInLayer) ? 4275 : 4407) +
+ "Vulkan spec doesn't allow BuiltIn Layer and "
+ "ViewportIndex to be "
+ "used for variables with Output storage class if "
+ "execution model is "
+ "Fragment."),
SpvExecutionModelFragment, decoration, built_in_inst,
referenced_from_inst, std::placeholders::_1));
}
diff --git a/source/val/validation_state.cpp b/source/val/validation_state.cpp
index e190b3c..cb69dda 100644
--- a/source/val/validation_state.cpp
+++ b/source/val/validation_state.cpp
@@ -1377,6 +1377,10 @@
return VUID_WRAP(VUID-InstanceIndex-InstanceIndex-04265);
case 4272:
return VUID_WRAP(VUID-Layer-Layer-04272);
+ case 4274:
+ return VUID_WRAP(VUID-Layer-Layer-04274);
+ case 4275:
+ return VUID_WRAP(VUID-Layer-Layer-04275);
case 4276:
return VUID_WRAP(VUID-Layer-Layer-04276);
case 4281:
@@ -1469,6 +1473,10 @@
return VUID_WRAP(VUID-ViewIndex-ViewIndex-04403);
case 4404:
return VUID_WRAP(VUID-ViewportIndex-ViewportIndex-04404);
+ case 4406:
+ return VUID_WRAP(VUID-ViewportIndex-ViewportIndex-04406);
+ case 4407:
+ return VUID_WRAP(VUID-ViewportIndex-ViewportIndex-04407);
case 4408:
return VUID_WRAP(VUID-ViewportIndex-ViewportIndex-04408);
case 4422:
@@ -1483,6 +1491,18 @@
return VUID_WRAP(VUID-WorkgroupSize-WorkgroupSize-04426);
case 4427:
return VUID_WRAP(VUID-WorkgroupSize-WorkgroupSize-04427);
+ case 4484:
+ return VUID_WRAP(VUID-PrimitiveShadingRateKHR-PrimitiveShadingRateKHR-04484);
+ case 4485:
+ return VUID_WRAP(VUID-PrimitiveShadingRateKHR-PrimitiveShadingRateKHR-04485);
+ case 4486:
+ return VUID_WRAP(VUID-PrimitiveShadingRateKHR-PrimitiveShadingRateKHR-04486);
+ case 4490:
+ return VUID_WRAP(VUID-ShadingRateKHR-ShadingRateKHR-04490);
+ case 4491:
+ return VUID_WRAP(VUID-ShadingRateKHR-ShadingRateKHR-04491);
+ case 4492:
+ return VUID_WRAP(VUID-ShadingRateKHR-ShadingRateKHR-04492);
default:
return ""; // unknown id
};
diff --git a/test/fuzz/transformation_add_bit_instruction_synonym_test.cpp b/test/fuzz/transformation_add_bit_instruction_synonym_test.cpp
index 9760ed3..fa8f7bf 100644
--- a/test/fuzz/transformation_add_bit_instruction_synonym_test.cpp
+++ b/test/fuzz/transformation_add_bit_instruction_synonym_test.cpp
@@ -30,12 +30,12 @@
OpMemoryModel Logical GLSL450
OpEntryPoint Vertex %37 "main"
-; Types
+ ; Types
%2 = OpTypeInt 32 0
%3 = OpTypeVoid
%4 = OpTypeFunction %3
-; Constants
+ ; Constants
%5 = OpConstant %2 0
%6 = OpConstant %2 1
%7 = OpConstant %2 2
@@ -69,10 +69,22 @@
%35 = OpConstant %2 30
%36 = OpConstant %2 31
-; main function
+ ; main function
%37 = OpFunction %3 None %4
%38 = OpLabel
+
+ ; Supported bit instructions
%39 = OpBitwiseOr %2 %5 %6
+ %40 = OpBitwiseXor %2 %7 %8
+ %41 = OpBitwiseAnd %2 %9 %10
+ %42 = OpNot %2 %11
+
+ ; Not yet supported bit instructions
+ %43 = OpShiftRightLogical %2 %12 %13
+ %44 = OpShiftRightArithmetic %2 %14 %15
+ %45 = OpShiftLeftLogical %2 %16 %17
+ %46 = OpBitReverse %2 %18
+ %47 = OpBitCount %2 %19
OpReturn
OpFunctionEnd
)";
@@ -86,78 +98,94 @@
kConsoleMessageConsumer));
TransformationContext transformation_context(
MakeUnique<FactManager>(context.get()), validator_options);
+
// Tests undefined bit instruction.
auto transformation = TransformationAddBitInstructionSynonym(
- 40, {41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53,
- 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66,
- 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79,
- 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92,
- 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105,
- 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118,
- 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131,
- 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144,
- 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157,
- 158, 159, 160, 161, 162, 163, 164, 165, 166, 167});
+ 48, {49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61,
+ 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74,
+ 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87,
+ 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100,
+ 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113,
+ 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126,
+ 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139,
+ 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152,
+ 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, 165,
+ 166, 167, 168, 169, 170, 171, 172, 173, 174, 175});
ASSERT_FALSE(
transformation.IsApplicable(context.get(), transformation_context));
// Tests false bit instruction.
transformation = TransformationAddBitInstructionSynonym(
- 38, {40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52,
- 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65,
- 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78,
- 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91,
- 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104,
- 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117,
- 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130,
- 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143,
- 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156,
- 157, 158, 159, 160, 161, 162, 163, 164, 165, 166});
+ 38, {48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60,
+ 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73,
+ 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86,
+ 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99,
+ 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112,
+ 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125,
+ 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138,
+ 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151,
+ 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164,
+ 165, 166, 167, 168, 169, 170, 171, 172, 173, 174});
ASSERT_FALSE(
transformation.IsApplicable(context.get(), transformation_context));
// Tests the number of fresh ids being different than the necessary.
transformation = TransformationAddBitInstructionSynonym(
39,
- {40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53,
- 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67,
- 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81,
- 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95,
- 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109,
- 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123,
- 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137,
- 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151,
- 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, 165});
+ {48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61,
+ 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75,
+ 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89,
+ 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103,
+ 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117,
+ 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131,
+ 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145,
+ 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159,
+ 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173});
ASSERT_FALSE(
transformation.IsApplicable(context.get(), transformation_context));
// Tests non-fresh ids.
transformation = TransformationAddBitInstructionSynonym(
- 39, {38, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51,
- 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64,
- 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77,
- 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90,
- 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103,
- 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116,
- 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129,
- 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142,
- 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155,
- 156, 157, 158, 159, 160, 161, 162, 163, 164, 165});
+ 40, {47, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60,
+ 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73,
+ 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86,
+ 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99,
+ 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112,
+ 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125,
+ 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138,
+ 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151,
+ 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164,
+ 165, 166, 167, 168, 169, 170, 171, 172, 173, 174});
ASSERT_FALSE(
transformation.IsApplicable(context.get(), transformation_context));
- // Tests applicable transformation.
+ // Tests unsupported transformation.
transformation = TransformationAddBitInstructionSynonym(
- 39, {40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52,
- 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65,
- 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78,
- 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91,
- 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104,
- 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117,
- 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130,
- 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143,
- 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156,
- 157, 158, 159, 160, 161, 162, 163, 164, 165, 166});
+ 43, {48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60,
+ 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73,
+ 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86,
+ 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99,
+ 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112,
+ 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125,
+ 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138,
+ 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151,
+ 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164,
+ 165, 166, 167, 168, 169, 170, 171, 172, 173, 174});
+ ASSERT_FALSE(
+ transformation.IsApplicable(context.get(), transformation_context));
+
+ // Tests supported transformation.
+ transformation = TransformationAddBitInstructionSynonym(
+ 41, {48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60,
+ 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73,
+ 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86,
+ 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99,
+ 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112,
+ 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125,
+ 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138,
+ 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151,
+ 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164,
+ 165, 166, 167, 168, 169, 170, 171, 172, 173, 174});
ASSERT_TRUE(
transformation.IsApplicable(context.get(), transformation_context));
}
@@ -238,6 +266,8 @@
131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143,
144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156,
157, 158, 159, 160, 161, 162, 163, 164, 165, 166});
+ ASSERT_TRUE(
+ transformation.IsApplicable(context.get(), transformation_context));
ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context);
ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
MakeDataDescriptor(166, {}), MakeDataDescriptor(39, {})));
@@ -534,6 +564,8 @@
96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109,
110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123,
124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134});
+ ASSERT_TRUE(
+ transformation.IsApplicable(context.get(), transformation_context));
ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context);
ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
MakeDataDescriptor(134, {}), MakeDataDescriptor(39, {})));
diff --git a/test/fuzz/transformation_flatten_conditional_branch_test.cpp b/test/fuzz/transformation_flatten_conditional_branch_test.cpp
index 0aaf20e..e0697d4 100644
--- a/test/fuzz/transformation_flatten_conditional_branch_test.cpp
+++ b/test/fuzz/transformation_flatten_conditional_branch_test.cpp
@@ -2041,6 +2041,100 @@
ASSERT_TRUE(IsEqual(env, expected, context.get()));
}
+TEST(TransformationFlattenConditionalBranchTest, ContainsDeadBlocksTest) {
+ std::string shader = R"(
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %4 "main"
+ OpExecutionMode %4 OriginUpperLeft
+ OpSource ESSL 320
+ OpName %4 "main"
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %6 = OpTypeBool
+ %7 = OpConstantFalse %6
+ %4 = OpFunction %2 None %3
+ %5 = OpLabel
+ OpSelectionMerge %9 None
+ OpBranchConditional %7 %8 %9
+ %8 = OpLabel
+ %10 = OpCopyObject %6 %7
+ OpBranch %9
+ %9 = OpLabel
+ %11 = OpPhi %6 %10 %8 %7 %5
+ %12 = OpPhi %6 %7 %5 %10 %8
+ OpReturn
+ OpFunctionEnd
+ )";
+
+ const auto env = SPV_ENV_UNIVERSAL_1_3;
+ const auto consumer = nullptr;
+ const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+ spvtools::ValidatorOptions validator_options;
+ ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
+ kConsoleMessageConsumer));
+ TransformationContext transformation_context(
+ MakeUnique<FactManager>(context.get()), validator_options);
+
+ TransformationFlattenConditionalBranch transformation(5, true, 0, 0, 0, {});
+ ASSERT_TRUE(
+ transformation.IsApplicable(context.get(), transformation_context));
+
+ transformation_context.GetFactManager()->AddFactBlockIsDead(8);
+
+ ASSERT_FALSE(
+ transformation.IsApplicable(context.get(), transformation_context));
+}
+
+TEST(TransformationFlattenConditionalBranchTest, ContainsContinueBlockTest) {
+ std::string shader = R"(
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %4 "main"
+ OpExecutionMode %4 OriginUpperLeft
+ OpSource ESSL 320
+ OpName %4 "main"
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %6 = OpTypeBool
+ %7 = OpConstantFalse %6
+ %4 = OpFunction %2 None %3
+ %12 = OpLabel
+ OpBranch %13
+ %13 = OpLabel
+ OpLoopMerge %15 %14 None
+ OpBranchConditional %7 %5 %15
+ %5 = OpLabel
+ OpSelectionMerge %11 None
+ OpBranchConditional %7 %9 %10
+ %9 = OpLabel
+ OpBranch %11
+ %10 = OpLabel
+ OpBranch %14
+ %11 = OpLabel
+ OpBranch %14
+ %14 = OpLabel
+ OpBranch %13
+ %15 = OpLabel
+ OpReturn
+ OpFunctionEnd
+ )";
+
+ const auto env = SPV_ENV_UNIVERSAL_1_3;
+ const auto consumer = nullptr;
+ const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+ spvtools::ValidatorOptions validator_options;
+ ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
+ kConsoleMessageConsumer));
+ TransformationContext transformation_context(
+ MakeUnique<FactManager>(context.get()), validator_options);
+
+ ASSERT_FALSE(TransformationFlattenConditionalBranch(5, true, 0, 0, 0, {})
+ .IsApplicable(context.get(), transformation_context));
+}
+
} // namespace
} // namespace fuzz
} // namespace spvtools
diff --git a/test/opt/local_single_store_elim_test.cpp b/test/opt/local_single_store_elim_test.cpp
index ebc89a6..d015dfb 100644
--- a/test/opt/local_single_store_elim_test.cpp
+++ b/test/opt/local_single_store_elim_test.cpp
@@ -1211,6 +1211,237 @@
SinglePassRunAndMatch<LocalSingleStoreElimPass>(text, false);
}
+TEST_F(LocalSingleStoreElimTest, UseStoreLineInfoForDebugValueLine) {
+ // When the store is in the scope of OpenCL.DebugInfo.100 DebugDeclare,
+ // the OpLine of the added OpenCL.DebugInfo.100 DebugValue must be the
+ // same with the OpLine of the store.
+ const std::string text = R"(
+ OpCapability Shader
+ %1 = OpExtInstImport "OpenCL.DebugInfo.100"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Vertex %main "main" %in_var_POSITION %in_var_COLOR %gl_Position %out_var_COLOR
+ %7 = OpString "simple.hlsl"
+ %8 = OpString "float"
+ %9 = OpString "VS_OUTPUT"
+ %10 = OpString "color"
+ %11 = OpString "pos"
+ %12 = OpString "main"
+ %13 = OpString ""
+ %14 = OpString "vout"
+ OpName %in_var_POSITION "in.var.POSITION"
+ OpName %in_var_COLOR "in.var.COLOR"
+ OpName %out_var_COLOR "out.var.COLOR"
+ OpName %main "main"
+ OpName %VS_OUTPUT "VS_OUTPUT"
+ OpMemberName %VS_OUTPUT 0 "pos"
+ OpMemberName %VS_OUTPUT 1 "color"
+ OpDecorate %gl_Position BuiltIn Position
+ OpDecorate %in_var_POSITION Location 0
+ OpDecorate %in_var_COLOR Location 1
+ OpDecorate %out_var_COLOR Location 0
+ %int = OpTypeInt 32 1
+ %int_0 = OpConstant %int 0
+ %int_1 = OpConstant %int 1
+ %uint = OpTypeInt 32 0
+ %uint_32 = OpConstant %uint 32
+ %float = OpTypeFloat 32
+ %v4float = OpTypeVector %float 4
+%_ptr_Input_v4float = OpTypePointer Input %v4float
+%_ptr_Output_v4float = OpTypePointer Output %v4float
+ %void = OpTypeVoid
+ %uint_256 = OpConstant %uint 256
+ %uint_128 = OpConstant %uint 128
+ %uint_0 = OpConstant %uint 0
+ %36 = OpTypeFunction %void
+%_ptr_Function_v4float = OpTypePointer Function %v4float
+ %VS_OUTPUT = OpTypeStruct %v4float %v4float
+%_ptr_Function_VS_OUTPUT = OpTypePointer Function %VS_OUTPUT
+%in_var_POSITION = OpVariable %_ptr_Input_v4float Input
+%in_var_COLOR = OpVariable %_ptr_Input_v4float Input
+%gl_Position = OpVariable %_ptr_Output_v4float Output
+%out_var_COLOR = OpVariable %_ptr_Output_v4float Output
+ %85 = OpExtInst %void %1 DebugOperation Deref
+ %81 = OpExtInst %void %1 DebugInfoNone
+ %52 = OpExtInst %void %1 DebugExpression
+ %40 = OpExtInst %void %1 DebugTypeBasic %8 %uint_32 Float
+ %41 = OpExtInst %void %1 DebugTypeVector %40 4
+ %42 = OpExtInst %void %1 DebugSource %7
+ %43 = OpExtInst %void %1 DebugCompilationUnit 1 4 %42 HLSL
+ %44 = OpExtInst %void %1 DebugTypeComposite %9 Structure %42 1 8 %43 %9 %uint_256 FlagIsProtected|FlagIsPrivate %45 %46
+ %46 = OpExtInst %void %1 DebugTypeMember %10 %41 %42 3 10 %44 %uint_128 %uint_128 FlagIsProtected|FlagIsPrivate
+ %45 = OpExtInst %void %1 DebugTypeMember %11 %41 %42 2 10 %44 %uint_0 %uint_128 FlagIsProtected|FlagIsPrivate
+ %47 = OpExtInst %void %1 DebugTypeFunction FlagIsProtected|FlagIsPrivate %44 %41 %41
+ %48 = OpExtInst %void %1 DebugFunction %12 %47 %42 6 1 %43 %13 FlagIsProtected|FlagIsPrivate 7 %81
+ %49 = OpExtInst %void %1 DebugLexicalBlock %42 7 38 %48
+ %50 = OpExtInst %void %1 DebugLocalVariable %14 %44 %42 8 13 %49 FlagIsLocal
+ %84 = OpExtInst %void %1 DebugExpression %85
+ %main = OpFunction %void None %36
+ %54 = OpLabel
+ %91 = OpExtInst %void %1 DebugScope %49
+ OpLine %7 7 23
+ %83 = OpVariable %_ptr_Function_v4float Function
+ OpLine %7 8 13
+ %87 = OpExtInst %void %1 DebugValue %50 %83 %84 %int_1
+ OpLine %7 7 23
+ %82 = OpVariable %_ptr_Function_v4float Function
+ OpLine %7 8 13
+ %86 = OpExtInst %void %1 DebugValue %50 %82 %84 %int_0
+ OpNoLine
+ %92 = OpExtInst %void %1 DebugNoScope
+ %55 = OpLoad %v4float %in_var_POSITION
+ %56 = OpLoad %v4float %in_var_COLOR
+;CHECK: [[pos:%\w+]] = OpLoad %v4float %in_var_POSITION
+;CHECK: [[color:%\w+]] = OpLoad %v4float %in_var_COLOR
+
+ %94 = OpExtInst %void %1 DebugScope %49
+ OpLine %7 9 3
+ OpStore %82 %55
+;CHECK: OpLine [[file:%\w+]] 9 3
+;CHECK: OpStore {{%\w+}} [[pos]]
+;CHECK: {{%\w+}} = OpExtInst %void {{%\w+}} DebugValue [[vout:%\w+]] [[pos]] [[empty_expr:%\w+]] %int_0
+;CHECK: OpLine [[file]] 10 3
+;CHECK: OpStore {{%\w+}} [[color]]
+;CHECK: {{%\w+}} = OpExtInst %void {{%\w+}} DebugValue [[vout]] [[color]] [[empty_expr]] %int_1
+
+ OpLine %7 10 3
+ OpStore %83 %56
+ OpLine %7 11 10
+ %90 = OpCompositeConstruct %VS_OUTPUT %55 %56
+ OpNoLine
+ %95 = OpExtInst %void %1 DebugNoScope
+ %58 = OpCompositeExtract %v4float %90 0
+ OpStore %gl_Position %58
+ %59 = OpCompositeExtract %v4float %90 1
+ OpStore %out_var_COLOR %59
+ OpReturn
+ OpFunctionEnd
+ )";
+
+ SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
+ SinglePassRunAndMatch<LocalSingleStoreElimPass>(text, false);
+}
+
+TEST_F(LocalSingleStoreElimTest, AddDebugValueforStoreOutOfDebugDeclareScope) {
+ const std::string text = R"(
+ OpCapability Shader
+ %1 = OpExtInstImport "OpenCL.DebugInfo.100"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Vertex %main "main" %in_var_POSITION %in_var_COLOR %gl_Position %out_var_COLOR
+ %7 = OpString "simple.hlsl"
+ %8 = OpString "float"
+ %9 = OpString "VS_OUTPUT"
+ %10 = OpString "color"
+ %11 = OpString "pos"
+ %12 = OpString "main"
+ %13 = OpString ""
+ %14 = OpString "vout"
+ OpName %in_var_POSITION "in.var.POSITION"
+ OpName %in_var_COLOR "in.var.COLOR"
+ OpName %out_var_COLOR "out.var.COLOR"
+ OpName %main "main"
+ OpName %VS_OUTPUT "VS_OUTPUT"
+ OpMemberName %VS_OUTPUT 0 "pos"
+ OpMemberName %VS_OUTPUT 1 "color"
+ OpDecorate %gl_Position BuiltIn Position
+ OpDecorate %in_var_POSITION Location 0
+ OpDecorate %in_var_COLOR Location 1
+ OpDecorate %out_var_COLOR Location 0
+ %int = OpTypeInt 32 1
+ %int_0 = OpConstant %int 0
+ %int_1 = OpConstant %int 1
+ %uint = OpTypeInt 32 0
+ %uint_32 = OpConstant %uint 32
+ %float = OpTypeFloat 32
+ %v4float = OpTypeVector %float 4
+%_ptr_Input_v4float = OpTypePointer Input %v4float
+%_ptr_Output_v4float = OpTypePointer Output %v4float
+ %void = OpTypeVoid
+ %uint_256 = OpConstant %uint 256
+ %uint_128 = OpConstant %uint 128
+ %uint_0 = OpConstant %uint 0
+ %36 = OpTypeFunction %void
+%_ptr_Function_v4float = OpTypePointer Function %v4float
+ %VS_OUTPUT = OpTypeStruct %v4float %v4float
+%_ptr_Function_VS_OUTPUT = OpTypePointer Function %VS_OUTPUT
+%in_var_POSITION = OpVariable %_ptr_Input_v4float Input
+%in_var_COLOR = OpVariable %_ptr_Input_v4float Input
+%gl_Position = OpVariable %_ptr_Output_v4float Output
+%out_var_COLOR = OpVariable %_ptr_Output_v4float Output
+ %85 = OpExtInst %void %1 DebugOperation Deref
+ %81 = OpExtInst %void %1 DebugInfoNone
+ %52 = OpExtInst %void %1 DebugExpression
+ %40 = OpExtInst %void %1 DebugTypeBasic %8 %uint_32 Float
+ %41 = OpExtInst %void %1 DebugTypeVector %40 4
+ %42 = OpExtInst %void %1 DebugSource %7
+ %43 = OpExtInst %void %1 DebugCompilationUnit 1 4 %42 HLSL
+ %44 = OpExtInst %void %1 DebugTypeComposite %9 Structure %42 1 8 %43 %9 %uint_256 FlagIsProtected|FlagIsPrivate %45 %46
+ %46 = OpExtInst %void %1 DebugTypeMember %10 %41 %42 3 10 %44 %uint_128 %uint_128 FlagIsProtected|FlagIsPrivate
+ %45 = OpExtInst %void %1 DebugTypeMember %11 %41 %42 2 10 %44 %uint_0 %uint_128 FlagIsProtected|FlagIsPrivate
+ %47 = OpExtInst %void %1 DebugTypeFunction FlagIsProtected|FlagIsPrivate %44 %41 %41
+ %48 = OpExtInst %void %1 DebugFunction %12 %47 %42 6 1 %43 %13 FlagIsProtected|FlagIsPrivate 7 %81
+ %49 = OpExtInst %void %1 DebugLexicalBlock %42 7 38 %48
+ %50 = OpExtInst %void %1 DebugLocalVariable %14 %44 %42 8 13 %49 FlagIsLocal
+ %51 = OpExtInst %void %1 DebugLocalVariable %10 %41 %42 7 23 %48 FlagIsLocal 2
+ %53 = OpExtInst %void %1 DebugLocalVariable %11 %41 %42 6 23 %48 FlagIsLocal 1
+;CHECK: [[dbg_color:%\w+]] = OpExtInst %void {{%\w+}} DebugLocalVariable {{%\w+}} {{%\w+}} {{%\w+}} 7 23 {{%\w+}} FlagIsLocal 2
+;CHECK: [[dbg_pos:%\w+]] = OpExtInst %void {{%\w+}} DebugLocalVariable {{%\w+}} {{%\w+}} {{%\w+}} 6 23 {{%\w+}} FlagIsLocal 1
+
+ %84 = OpExtInst %void %1 DebugExpression %85
+ %main = OpFunction %void None %36
+ %54 = OpLabel
+ %91 = OpExtInst %void %1 DebugScope %49
+ OpLine %7 7 23
+ %83 = OpVariable %_ptr_Function_v4float Function
+ OpLine %7 8 13
+ %87 = OpExtInst %void %1 DebugValue %50 %83 %84 %int_1
+ OpLine %7 7 23
+ %82 = OpVariable %_ptr_Function_v4float Function
+ OpLine %7 8 13
+ %86 = OpExtInst %void %1 DebugValue %50 %82 %84 %int_0
+ OpNoLine
+ %92 = OpExtInst %void %1 DebugNoScope
+%param_var_pos = OpVariable %_ptr_Function_v4float Function
+%param_var_color = OpVariable %_ptr_Function_v4float Function
+ %55 = OpLoad %v4float %in_var_POSITION
+ OpStore %param_var_pos %55
+ %56 = OpLoad %v4float %in_var_COLOR
+;CHECK: DebugNoScope
+;CHECK-NOT: OpLine
+;CHECK: [[pos:%\w+]] = OpLoad %v4float %in_var_POSITION
+;CHECK: [[color:%\w+]] = OpLoad %v4float %in_var_COLOR
+
+ OpStore %param_var_color %56
+ %93 = OpExtInst %void %1 DebugScope %48
+ OpLine %7 6 23
+ %73 = OpExtInst %void %1 DebugDeclare %53 %param_var_pos %52
+ OpLine %7 7 23
+ %74 = OpExtInst %void %1 DebugDeclare %51 %param_var_color %52
+;CHECK: OpLine [[file:%\w+]] 6 23
+;CHECK-NEXT: {{%\w+}} = OpExtInst %void {{%\w+}} DebugValue [[dbg_pos]] [[pos]] [[empty_expr:%\w+]]
+;CHECK: OpLine [[file]] 7 23
+;CHECK-NEXT: {{%\w+}} = OpExtInst %void {{%\w+}} DebugValue [[dbg_color]] [[color]] [[empty_expr]]
+
+ %94 = OpExtInst %void %1 DebugScope %49
+ OpLine %7 9 3
+ OpStore %82 %55
+ OpLine %7 10 3
+ OpStore %83 %56
+ OpLine %7 11 10
+ %90 = OpCompositeConstruct %VS_OUTPUT %55 %56
+ OpNoLine
+ %95 = OpExtInst %void %1 DebugNoScope
+ %58 = OpCompositeExtract %v4float %90 0
+ OpStore %gl_Position %58
+ %59 = OpCompositeExtract %v4float %90 1
+ OpStore %out_var_COLOR %59
+ OpReturn
+ OpFunctionEnd
+ )";
+
+ SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
+ SinglePassRunAndMatch<LocalSingleStoreElimPass>(text, false);
+}
+
// TODO(greg-lunarg): Add tests to verify handling of these cases:
//
// Other types
diff --git a/test/opt/local_ssa_elim_test.cpp b/test/opt/local_ssa_elim_test.cpp
index 2ecd238..ff42193 100644
--- a/test/opt/local_ssa_elim_test.cpp
+++ b/test/opt/local_ssa_elim_test.cpp
@@ -3891,6 +3891,44 @@
SinglePassRunAndMatch<SSARewritePass>(text, true);
}
+// Check support for pointer variables. When pointer variables are used, the
+// computation of reaching definitions may need to follow pointer chains.
+// See https://github.com/KhronosGroup/SPIRV-Tools/issues/3873 for details.
+TEST_F(LocalSSAElimTest, PointerVariables) {
+ const std::string text = R"(
+ OpCapability Shader
+ OpCapability VariablePointers
+ OpExtension "SPV_KHR_variable_pointers"
+ OpMemoryModel Logical Simple
+ OpEntryPoint Fragment %1 "main" %2 %3
+ OpExecutionMode %1 OriginUpperLeft
+ %float = OpTypeFloat 32
+ %void = OpTypeVoid
+ %6 = OpTypeFunction %void
+%_ptr_Input_float = OpTypePointer Input %float
+%_ptr_Output_float = OpTypePointer Output %float
+%_ptr_Function__ptr_Input_float = OpTypePointer Function %_ptr_Input_float
+ %2 = OpVariable %_ptr_Input_float Input
+ %3 = OpVariable %_ptr_Output_float Output
+ %1 = OpFunction %void None %6
+ %10 = OpLabel
+ %11 = OpVariable %_ptr_Function__ptr_Input_float Function
+ OpStore %11 %2
+
+; CHECK-NOT: %12 = OpLoad %_ptr_Input_float %11
+ %12 = OpLoad %_ptr_Input_float %11
+
+; CHECK: %13 = OpLoad %float %2
+ %13 = OpLoad %float %12
+
+ OpStore %3 %13
+ OpReturn
+ OpFunctionEnd
+)";
+
+ SinglePassRunAndMatch<SSARewritePass>(text, true);
+}
+
// TODO(greg-lunarg): Add tests to verify handling of these cases:
//
// No optimization in the presence of
diff --git a/test/tools/opt/flags.py b/test/tools/opt/flags.py
index f7dc64c..f8117d9 100644
--- a/test/tools/opt/flags.py
+++ b/test/tools/opt/flags.py
@@ -149,6 +149,7 @@
'eliminate-dead-branches',
'merge-return',
'inline-entry-points-exhaustive',
+ 'eliminate-dead-functions',
'eliminate-dead-code-aggressive',
'private-to-local',
'eliminate-local-single-block',
diff --git a/test/val/val_builtins_test.cpp b/test/val/val_builtins_test.cpp
index 6bfcca3..74d5c3a 100644
--- a/test/val/val_builtins_test.cpp
+++ b/test/val/val_builtins_test.cpp
@@ -168,6 +168,17 @@
std::string token;
std::string vuids = std::string(vuid_set);
size_t position;
+
+ // Catch case were someone accidentally left spaces by trimming string
+ // clang-format off
+ vuids.erase(std::find_if(vuids.rbegin(), vuids.rend(), [](unsigned char c) {
+ return (c != ' ');
+ }).base(), vuids.end());
+ vuids.erase(vuids.begin(), std::find_if(vuids.begin(), vuids.end(), [](unsigned char c) {
+ return (c != ' ');
+ }));
+ // clang-format on
+
do {
position = vuids.find(delimiter);
if (position != std::string::npos) {
@@ -1263,7 +1274,8 @@
ValidateVulkanCombineBuiltInExecutionModelDataTypeResult,
Combine(
Values("Layer", "ViewportIndex"), Values("Fragment"), Values("Output"),
- Values("%u32"), Values(nullptr),
+ Values("%u32"),
+ Values("VUID-Layer-Layer-04275 VUID-ViewportIndex-ViewportIndex-04407"),
Values(TestResult(SPV_ERROR_INVALID_DATA,
"Output storage class if execution model is Fragment",
"which is called with execution model Fragment"))));
@@ -1274,10 +1286,11 @@
Combine(
Values("Layer", "ViewportIndex"),
Values("Vertex", "TessellationEvaluation", "Geometry"), Values("Input"),
- Values("%u32"), Values(nullptr),
+ Values("%u32"),
+ Values("VUID-Layer-Layer-04274 VUID-ViewportIndex-ViewportIndex-04406"),
Values(TestResult(SPV_ERROR_INVALID_DATA,
"Input storage class if execution model is Vertex, "
- "TessellationEvaluation, or Geometry",
+ "TessellationEvaluation, Geometry, or MeshNV",
"which is called with execution model"))));
INSTANTIATE_TEST_SUITE_P(
@@ -3836,7 +3849,7 @@
Values("PrimitiveShadingRateKHR"), Values("Vertex"), Values("Output"),
Values("%f32"), Values("OpCapability FragmentShadingRateKHR\n"),
Values("OpExtension \"SPV_KHR_fragment_shading_rate\"\n"),
- Values("VUID-PrimitiveShadingRateKHR-PrimitiveShadingRateKHR-04485 "),
+ Values("VUID-PrimitiveShadingRateKHR-PrimitiveShadingRateKHR-04486 "),
Values(TestResult(
SPV_ERROR_INVALID_DATA,
"According to the Vulkan spec BuiltIn PrimitiveShadingRateKHR "