Commit f95bac4e by Sean Risser

Update SPIR-V Tools to a61d07a72

Changes: 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) Commands: ./third_party/update-spirvtools.sh Bug: b/123642959 Change-Id: I3c4f10f78f63a75dcec8036a6a9488a82aebf84c
parents ad54c716 d2283f8e
......@@ -176,12 +176,6 @@ void DataSynonymAndIdEquationFacts::AddEquationFactRecursive(
// 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])) {
......
......@@ -107,8 +107,6 @@ namespace fuzz {
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 @@ bool Fuzzer::ShouldContinueFuzzing() {
// 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;
......
......@@ -20,14 +20,19 @@ namespace spvtools {
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 @@ FuzzerContext::GetRandomSynonymType() {
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
......@@ -104,240 +104,271 @@ class FuzzerContext {
// 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() {
......
......@@ -35,6 +35,13 @@ void FuzzerPassAddBitInstructionSynonyms::Apply() {
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())) {
......
......@@ -91,35 +91,29 @@ void FuzzerPassAddCompositeExtract::Apply() {
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");
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;
const auto* type_inst =
GetIRContext()->get_def_use_mgr()->GetDef(type_id);
assert(type_inst && "Composite instruction has invalid type id");
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 @@ void FuzzerPassAddCompositeExtract::Apply() {
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()));
......
......@@ -71,7 +71,8 @@ void FuzzerPassFlattenConditionalBranches::Apply() {
// 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 @@ void FuzzerPassFlattenConditionalBranches::Apply() {
}
}
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
......
......@@ -53,6 +53,8 @@ FuzzerPass* RepeatedPassManagerRandomWithRecommendations::ChoosePass(
result = recommended_passes_.front();
recommended_passes_.pop_front();
}
assert(result != nullptr && "A pass must have been chosen.");
last_pass_choice_ = result;
return result;
}
......
......@@ -46,7 +46,8 @@ bool TransformationAddBitInstructionSynonym::IsApplicable(
// |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;
}
......
......@@ -82,65 +82,6 @@ bool TransformationFlattenConditionalBranch::IsApplicable(
}
}
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 @@ bool TransformationFlattenConditionalBranch::IsApplicable(
// 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 @@ bool TransformationFlattenConditionalBranch::IsApplicable(
}
}
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 @@ protobufs::Transformation TransformationFlattenConditionalBranch::ToMessage()
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 @@ bool TransformationFlattenConditionalBranch::
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 @@ bool TransformationFlattenConditionalBranch::
// - 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 @@ bool TransformationFlattenConditionalBranch::
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 @@ bool TransformationFlattenConditionalBranch::
// 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 @@ TransformationFlattenConditionalBranch::EncloseInstructionInConditional(
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 @@ bool TransformationFlattenConditionalBranch::OpSelectArgumentsAreRestricted(
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);
......
......@@ -72,6 +72,7 @@ class TransformationFlattenConditionalBranch : public Transformation {
// 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 @@ class TransformationFlattenConditionalBranch : public Transformation {
// |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 @@ class TransformationFlattenConditionalBranch : public Transformation {
// |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.
......
......@@ -384,7 +384,8 @@ bool DebugInfoManager::IsVariableDebugDeclared(uint32_t variable_id) {
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 @@ void DebugInfoManager::KillDebugDeclares(uint32_t variable_id) {
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 @@ Instruction* DebugInfoManager::AddDebugValueWithIndex(
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) {
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;
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 @@ void DebugInfoManager::AddDebugValueIfVarDeclIsVisible(
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,
......
......@@ -138,14 +138,15 @@ class DebugInfoManager {
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);
......
......@@ -148,16 +148,41 @@ bool LocalSingleStoreElimPass::ProcessVariable(Instruction* var_inst) {
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.
......
......@@ -94,6 +94,10 @@ class LocalSingleStoreElimPass : public Pass {
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_;
};
......
......@@ -161,6 +161,7 @@ Optimizer& Optimizer::RegisterPerformancePasses() {
.RegisterPass(CreateDeadBranchElimPass())
.RegisterPass(CreateMergeReturnPass())
.RegisterPass(CreateInlineExhaustivePass())
.RegisterPass(CreateEliminateDeadFunctionsPass())
.RegisterPass(CreateAggressiveDCEPass())
.RegisterPass(CreatePrivateToLocalPass())
.RegisterPass(CreateLocalSingleBlockLoadStoreElimPass())
......
......@@ -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 @@ void SSARewriter::ProcessStore(Instruction* inst, BasicBlock* bb) {
}
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 @@ bool SSARewriter::GenerateSSAReplacements(BasicBlock* bb) {
}
}
// 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 @@ bool SSARewriter::ApplyReplacements() {
}
// 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 @@ void SSARewriter::FinalizePhiCandidate(PhiCandidate* phi_candidate) {
// 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.
......
......@@ -2603,17 +2603,19 @@ spv_result_t BuiltInsValidator::ValidateLayerOrViewportIndexAtReference(
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 @@ spv_result_t BuiltInsValidator::ValidateLayerOrViewportIndexAtReference(
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));
}
......
......@@ -1377,6 +1377,10 @@ std::string ValidationState_t::VkErrorID(uint32_t id,
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 @@ std::string ValidationState_t::VkErrorID(uint32_t id,
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 @@ std::string ValidationState_t::VkErrorID(uint32_t id,
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
};
......
......@@ -30,12 +30,12 @@ TEST(TransformationAddBitInstructionSynonymTest, IsApplicable) {
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 @@ TEST(TransformationAddBitInstructionSynonymTest, IsApplicable) {
%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 @@ TEST(TransformationAddBitInstructionSynonymTest, IsApplicable) {
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 @@ TEST(TransformationAddBitInstructionSynonymTest, AddOpBitwiseOrSynonym) {
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 @@ TEST(TransformationAddBitInstructionSynonymTest, AddOpNotSynonym) {
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, {})));
......
......@@ -2041,6 +2041,100 @@ TEST(TransformationFlattenConditionalBranchTest,
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
......@@ -1211,6 +1211,237 @@ TEST_F(LocalSingleStoreElimTest, DebugValueTest) {
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
......
......@@ -3891,6 +3891,44 @@ TEST_F(LocalSSAElimTest, RemoveDebugDeclareWithoutLoads) {
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
......
......@@ -149,6 +149,7 @@ class TestPerformanceOptimizationPasses(expect.ValidObjectFile1_5,
'eliminate-dead-branches',
'merge-return',
'inline-entry-points-exhaustive',
'eliminate-dead-functions',
'eliminate-dead-code-aggressive',
'private-to-local',
'eliminate-local-single-block',
......
......@@ -168,6 +168,17 @@ MATCHER_P(AnyVUID, vuid_set, "VUID from the set is in error message") {
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 @@ INSTANTIATE_TEST_SUITE_P(
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 @@ INSTANTIATE_TEST_SUITE_P(
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 @@ INSTANTIATE_TEST_SUITE_P(
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 "
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment