spirv-fuzz: Add expand vector reduction transformation (#3869)
The following implementations are introduced:
- Transformation and fuzzer pass for expanding vector reduction.
- Unit tests to cover the instructions with different vector sizes.
Fixes #3768.
diff --git a/source/fuzz/CMakeLists.txt b/source/fuzz/CMakeLists.txt
index a0e7ed8..63a6625 100644
--- a/source/fuzz/CMakeLists.txt
+++ b/source/fuzz/CMakeLists.txt
@@ -86,6 +86,7 @@
fuzzer_pass_copy_objects.h
fuzzer_pass_donate_modules.h
fuzzer_pass_duplicate_regions_with_selections.h
+ fuzzer_pass_expand_vector_reductions.h
fuzzer_pass_flatten_conditional_branches.h
fuzzer_pass_inline_functions.h
fuzzer_pass_invert_comparison_operators.h
@@ -179,6 +180,7 @@
transformation_context.h
transformation_duplicate_region_with_selection.h
transformation_equation_instruction.h
+ transformation_expand_vector_reduction.h
transformation_flatten_conditional_branch.h
transformation_function_call.h
transformation_inline_function.h
@@ -274,6 +276,7 @@
fuzzer_pass_copy_objects.cpp
fuzzer_pass_donate_modules.cpp
fuzzer_pass_duplicate_regions_with_selections.cpp
+ fuzzer_pass_expand_vector_reductions.cpp
fuzzer_pass_flatten_conditional_branches.cpp
fuzzer_pass_inline_functions.cpp
fuzzer_pass_invert_comparison_operators.cpp
@@ -365,6 +368,7 @@
transformation_context.cpp
transformation_duplicate_region_with_selection.cpp
transformation_equation_instruction.cpp
+ transformation_expand_vector_reduction.cpp
transformation_flatten_conditional_branch.cpp
transformation_function_call.cpp
transformation_inline_function.cpp
diff --git a/source/fuzz/fuzzer.cpp b/source/fuzz/fuzzer.cpp
index f326656..4361283 100644
--- a/source/fuzz/fuzzer.cpp
+++ b/source/fuzz/fuzzer.cpp
@@ -55,6 +55,7 @@
#include "source/fuzz/fuzzer_pass_copy_objects.h"
#include "source/fuzz/fuzzer_pass_donate_modules.h"
#include "source/fuzz/fuzzer_pass_duplicate_regions_with_selections.h"
+#include "source/fuzz/fuzzer_pass_expand_vector_reductions.h"
#include "source/fuzz/fuzzer_pass_flatten_conditional_branches.h"
#include "source/fuzz/fuzzer_pass_inline_functions.h"
#include "source/fuzz/fuzzer_pass_interchange_signedness_of_integer_operands.h"
@@ -259,6 +260,7 @@
donor_suppliers_);
MaybeAddRepeatedPass<FuzzerPassDuplicateRegionsWithSelections>(
&pass_instances);
+ MaybeAddRepeatedPass<FuzzerPassExpandVectorReductions>(&pass_instances);
MaybeAddRepeatedPass<FuzzerPassFlattenConditionalBranches>(&pass_instances);
MaybeAddRepeatedPass<FuzzerPassInlineFunctions>(&pass_instances);
MaybeAddRepeatedPass<FuzzerPassInvertComparisonOperators>(&pass_instances);
diff --git a/source/fuzz/fuzzer_context.cpp b/source/fuzz/fuzzer_context.cpp
index 3a27d6f..65904dc 100644
--- a/source/fuzz/fuzzer_context.cpp
+++ b/source/fuzz/fuzzer_context.cpp
@@ -81,6 +81,8 @@
const std::pair<uint32_t, uint32_t> kChanceOfDonatingAdditionalModule = {5, 50};
const std::pair<uint32_t, uint32_t> kChanceOfDuplicatingRegionWithSelection = {
20, 50};
+const std::pair<uint32_t, uint32_t> kChanceOfExpandingVectorReduction = {20,
+ 90};
const std::pair<uint32_t, uint32_t> kChanceOfFlatteningConditionalBranch = {45,
95};
const std::pair<uint32_t, uint32_t> kChanceOfGoingDeeperToExtractComposite = {
@@ -266,6 +268,8 @@
ChooseBetweenMinAndMax(kChanceOfDonatingAdditionalModule);
chance_of_duplicating_region_with_selection_ =
ChooseBetweenMinAndMax(kChanceOfDuplicatingRegionWithSelection);
+ chance_of_expanding_vector_reduction_ =
+ ChooseBetweenMinAndMax(kChanceOfExpandingVectorReduction);
chance_of_flattening_conditional_branch_ =
ChooseBetweenMinAndMax(kChanceOfFlatteningConditionalBranch);
chance_of_going_deeper_to_extract_composite_ =
diff --git a/source/fuzz/fuzzer_context.h b/source/fuzz/fuzzer_context.h
index b230af3..9193dfc 100644
--- a/source/fuzz/fuzzer_context.h
+++ b/source/fuzz/fuzzer_context.h
@@ -216,6 +216,9 @@
uint32_t GetChanceOfDuplicatingRegionWithSelection() {
return chance_of_duplicating_region_with_selection_;
}
+ uint32_t GetChanceOfExpandingVectorReduction() {
+ return chance_of_expanding_vector_reduction_;
+ }
uint32_t GetChanceOfFlatteningConditionalBranch() {
return chance_of_flattening_conditional_branch_;
}
@@ -461,6 +464,7 @@
uint32_t chance_of_creating_int_synonyms_using_loops_;
uint32_t chance_of_donating_additional_module_;
uint32_t chance_of_duplicating_region_with_selection_;
+ uint32_t chance_of_expanding_vector_reduction_;
uint32_t chance_of_flattening_conditional_branch_;
uint32_t chance_of_going_deeper_to_extract_composite_;
uint32_t chance_of_going_deeper_to_insert_in_composite_;
diff --git a/source/fuzz/fuzzer_pass_expand_vector_reductions.cpp b/source/fuzz/fuzzer_pass_expand_vector_reductions.cpp
new file mode 100644
index 0000000..1416fe0
--- /dev/null
+++ b/source/fuzz/fuzzer_pass_expand_vector_reductions.cpp
@@ -0,0 +1,67 @@
+// Copyright (c) 2020 André Perez Maselco
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "source/fuzz/fuzzer_pass_expand_vector_reductions.h"
+
+#include "source/fuzz/fuzzer_util.h"
+#include "source/fuzz/instruction_descriptor.h"
+#include "source/fuzz/transformation_expand_vector_reduction.h"
+
+namespace spvtools {
+namespace fuzz {
+
+FuzzerPassExpandVectorReductions::FuzzerPassExpandVectorReductions(
+ opt::IRContext* ir_context, TransformationContext* transformation_context,
+ FuzzerContext* fuzzer_context,
+ protobufs::TransformationSequence* transformations)
+ : FuzzerPass(ir_context, transformation_context, fuzzer_context,
+ transformations) {}
+
+FuzzerPassExpandVectorReductions::~FuzzerPassExpandVectorReductions() = default;
+
+void FuzzerPassExpandVectorReductions::Apply() {
+ for (auto& function : *GetIRContext()->module()) {
+ for (auto& block : function) {
+ for (auto& instruction : block) {
+ // Randomly decides whether the transformation will be applied.
+ if (!GetFuzzerContext()->ChoosePercentage(
+ GetFuzzerContext()->GetChanceOfExpandingVectorReduction())) {
+ continue;
+ }
+
+ // |instruction| must be OpAny or OpAll.
+ if (instruction.opcode() != SpvOpAny &&
+ instruction.opcode() != SpvOpAll) {
+ continue;
+ }
+
+ // It must be able to make a synonym of |instruction|.
+ if (!fuzzerutil::CanMakeSynonymOf(
+ GetIRContext(), *GetTransformationContext(), &instruction)) {
+ continue;
+ }
+
+ // Applies the expand vector reduction transformation.
+ ApplyTransformation(TransformationExpandVectorReduction(
+ instruction.result_id(),
+ GetFuzzerContext()->GetFreshIds(
+ TransformationExpandVectorReduction::GetRequiredFreshIdCount(
+ GetIRContext(), &instruction))));
+ }
+ }
+ }
+}
+
+} // namespace fuzz
+} // namespace spvtools
diff --git a/source/fuzz/fuzzer_pass_expand_vector_reductions.h b/source/fuzz/fuzzer_pass_expand_vector_reductions.h
new file mode 100644
index 0000000..ae3238b
--- /dev/null
+++ b/source/fuzz/fuzzer_pass_expand_vector_reductions.h
@@ -0,0 +1,41 @@
+// Copyright (c) 2020 André Perez Maselco
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef SOURCE_FUZZ_FUZZER_PASS_EXPAND_VECTOR_REDUCTIONS_H_
+#define SOURCE_FUZZ_FUZZER_PASS_EXPAND_VECTOR_REDUCTIONS_H_
+
+#include "source/fuzz/fuzzer_pass.h"
+
+namespace spvtools {
+namespace fuzz {
+
+// This fuzzer pass adds synonyms for the OpAny and OpAll instructions. It
+// iterates over the module, checks if there are any OpAny or OpAll applicable
+// instructions and randomly applies the expand vector reduction transformation.
+class FuzzerPassExpandVectorReductions : public FuzzerPass {
+ public:
+ FuzzerPassExpandVectorReductions(
+ opt::IRContext* ir_context, TransformationContext* transformation_context,
+ FuzzerContext* fuzzer_context,
+ protobufs::TransformationSequence* transformations);
+
+ ~FuzzerPassExpandVectorReductions();
+
+ void Apply() override;
+};
+
+} // namespace fuzz
+} // namespace spvtools
+
+#endif // SOURCE_FUZZ_FUZZER_PASS_EXPAND_VECTOR_REDUCTIONS_H_
diff --git a/source/fuzz/pass_management/repeated_pass_instances.h b/source/fuzz/pass_management/repeated_pass_instances.h
index 5f479fb..80ac087 100644
--- a/source/fuzz/pass_management/repeated_pass_instances.h
+++ b/source/fuzz/pass_management/repeated_pass_instances.h
@@ -43,6 +43,7 @@
#include "source/fuzz/fuzzer_pass_copy_objects.h"
#include "source/fuzz/fuzzer_pass_donate_modules.h"
#include "source/fuzz/fuzzer_pass_duplicate_regions_with_selections.h"
+#include "source/fuzz/fuzzer_pass_expand_vector_reductions.h"
#include "source/fuzz/fuzzer_pass_flatten_conditional_branches.h"
#include "source/fuzz/fuzzer_pass_inline_functions.h"
#include "source/fuzz/fuzzer_pass_invert_comparison_operators.h"
@@ -137,6 +138,7 @@
REPEATED_PASS_INSTANCE(CopyObjects);
REPEATED_PASS_INSTANCE(DonateModules);
REPEATED_PASS_INSTANCE(DuplicateRegionsWithSelections);
+ REPEATED_PASS_INSTANCE(ExpandVectorReductions);
REPEATED_PASS_INSTANCE(FlattenConditionalBranches);
REPEATED_PASS_INSTANCE(InlineFunctions);
REPEATED_PASS_INSTANCE(InvertComparisonOperators);
diff --git a/source/fuzz/pass_management/repeated_pass_recommender_standard.cpp b/source/fuzz/pass_management/repeated_pass_recommender_standard.cpp
index 7121c33..a933848 100644
--- a/source/fuzz/pass_management/repeated_pass_recommender_standard.cpp
+++ b/source/fuzz/pass_management/repeated_pass_recommender_standard.cpp
@@ -206,6 +206,10 @@
// - Parts of duplicated regions can be outlined
return RandomOrderAndNonNull({pass_instances_->GetOutlineFunctions()});
}
+ if (&pass == pass_instances_->GetExpandVectorReductions()) {
+ // - Adding OpAny and OpAll synonyms creates opportunities to apply synonyms
+ return RandomOrderAndNonNull({pass_instances_->GetApplyIdSynonyms()});
+ }
if (&pass == pass_instances_->GetFlattenConditionalBranches()) {
// - Parts of flattened selections can be outlined
// - The flattening transformation introduces constants and irrelevant ids
diff --git a/source/fuzz/protobufs/spvtoolsfuzz.proto b/source/fuzz/protobufs/spvtoolsfuzz.proto
index 20116c7..770a2dd 100644
--- a/source/fuzz/protobufs/spvtoolsfuzz.proto
+++ b/source/fuzz/protobufs/spvtoolsfuzz.proto
@@ -556,6 +556,7 @@
TransformationReplaceBranchFromDeadBlockWithExit replace_branch_from_dead_block_with_exit = 82;
TransformationWrapEarlyTerminatorInFunction wrap_early_terminator_in_function = 83;
TransformationMergeFunctionReturns merge_function_returns = 84;
+ TransformationExpandVectorReduction expand_vector_reduction = 85;
// Add additional option using the next available number.
}
}
@@ -1417,6 +1418,21 @@
}
+message TransformationExpandVectorReduction {
+
+ // A transformation that adds synonyms for OpAny and OpAll instructions by
+ // evaluating each vector component with the corresponding logical operation.
+ // There is a SPIR-V code example in the header file of the transformation
+ // class that can help understand the transformation.
+
+ // The OpAny or OpAll instruction result id.
+ uint32 instruction_result_id = 1;
+
+ // The fresh ids required to apply the transformation.
+ repeated uint32 fresh_ids = 2;
+
+}
+
message TransformationFlattenConditionalBranch {
// A transformation that takes a selection construct with a header
diff --git a/source/fuzz/transformation.cpp b/source/fuzz/transformation.cpp
index c8bf879..ebbc393 100644
--- a/source/fuzz/transformation.cpp
+++ b/source/fuzz/transformation.cpp
@@ -57,6 +57,7 @@
#include "source/fuzz/transformation_compute_data_synonym_fact_closure.h"
#include "source/fuzz/transformation_duplicate_region_with_selection.h"
#include "source/fuzz/transformation_equation_instruction.h"
+#include "source/fuzz/transformation_expand_vector_reduction.h"
#include "source/fuzz/transformation_flatten_conditional_branch.h"
#include "source/fuzz/transformation_function_call.h"
#include "source/fuzz/transformation_inline_function.h"
@@ -225,6 +226,9 @@
case protobufs::Transformation::TransformationCase::kEquationInstruction:
return MakeUnique<TransformationEquationInstruction>(
message.equation_instruction());
+ case protobufs::Transformation::TransformationCase::kExpandVectorReduction:
+ return MakeUnique<TransformationExpandVectorReduction>(
+ message.expand_vector_reduction());
case protobufs::Transformation::TransformationCase::
kFlattenConditionalBranch:
return MakeUnique<TransformationFlattenConditionalBranch>(
diff --git a/source/fuzz/transformation_expand_vector_reduction.cpp b/source/fuzz/transformation_expand_vector_reduction.cpp
new file mode 100644
index 0000000..640aea2
--- /dev/null
+++ b/source/fuzz/transformation_expand_vector_reduction.cpp
@@ -0,0 +1,170 @@
+// Copyright (c) 2020 André Perez Maselco
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "source/fuzz/transformation_expand_vector_reduction.h"
+
+#include "source/fuzz/fuzzer_util.h"
+#include "source/fuzz/instruction_descriptor.h"
+
+namespace spvtools {
+namespace fuzz {
+
+TransformationExpandVectorReduction::TransformationExpandVectorReduction(
+ const spvtools::fuzz::protobufs::TransformationExpandVectorReduction&
+ message)
+ : message_(message) {}
+
+TransformationExpandVectorReduction::TransformationExpandVectorReduction(
+ const uint32_t instruction_result_id,
+ const std::vector<uint32_t>& fresh_ids) {
+ message_.set_instruction_result_id(instruction_result_id);
+ *message_.mutable_fresh_ids() =
+ google::protobuf::RepeatedField<google::protobuf::uint32>(
+ fresh_ids.begin(), fresh_ids.end());
+}
+
+bool TransformationExpandVectorReduction::IsApplicable(
+ opt::IRContext* ir_context, const TransformationContext& /*unused*/) const {
+ auto* instruction =
+ ir_context->get_def_use_mgr()->GetDef(message_.instruction_result_id());
+
+ // |instruction| must be defined.
+ if (!instruction) {
+ return false;
+ }
+
+ // |instruction| must be OpAny or OpAll.
+ if (instruction->opcode() != SpvOpAny && instruction->opcode() != SpvOpAll) {
+ return false;
+ }
+
+ // |message_.fresh_ids.size| must have the exact number of fresh ids required
+ // to apply the transformation.
+ if (static_cast<uint32_t>(message_.fresh_ids().size()) !=
+ GetRequiredFreshIdCount(ir_context, instruction)) {
+ return false;
+ }
+
+ std::set<uint32_t> ids_used_by_this_transformation;
+ for (uint32_t fresh_id : message_.fresh_ids()) {
+ // All ids in |message_.fresh_ids| must be fresh.
+ if (!fuzzerutil::IsFreshId(ir_context, fresh_id)) {
+ return false;
+ }
+
+ // All fresh ids need to be distinct.
+ if (!CheckIdIsFreshAndNotUsedByThisTransformation(
+ fresh_id, ir_context, &ids_used_by_this_transformation)) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+void TransformationExpandVectorReduction::Apply(
+ opt::IRContext* ir_context,
+ TransformationContext* transformation_context) const {
+ auto* instruction =
+ ir_context->get_def_use_mgr()->GetDef(message_.instruction_result_id());
+ auto* vector = ir_context->get_def_use_mgr()->GetDef(
+ instruction->GetSingleWordInOperand(0));
+ uint32_t vector_component_count = ir_context->get_type_mgr()
+ ->GetType(vector->type_id())
+ ->AsVector()
+ ->element_count();
+
+ // Fresh id iterator.
+ auto fresh_id = message_.fresh_ids().begin();
+
+ // |vector_components| are the ids of the extracted components from |vector|.
+ std::vector<uint32_t> vector_components;
+
+ for (uint32_t i = 0; i < vector_component_count; i++) {
+ // Extracts the i-th |vector| component.
+ auto vector_component = opt::Instruction(
+ ir_context, SpvOpCompositeExtract, instruction->type_id(), *fresh_id++,
+ {{SPV_OPERAND_TYPE_ID, {vector->result_id()}},
+ {SPV_OPERAND_TYPE_LITERAL_INTEGER, {i}}});
+ instruction->InsertBefore(MakeUnique<opt::Instruction>(vector_component));
+ fuzzerutil::UpdateModuleIdBound(ir_context, vector_component.result_id());
+ vector_components.push_back(vector_component.result_id());
+ }
+
+ // The first two |vector| components are used in the first logical operation.
+ auto logical_instruction = opt::Instruction(
+ ir_context,
+ instruction->opcode() == SpvOpAny ? SpvOpLogicalOr : SpvOpLogicalAnd,
+ instruction->type_id(), *fresh_id++,
+ {{SPV_OPERAND_TYPE_ID, {vector_components[0]}},
+ {SPV_OPERAND_TYPE_ID, {vector_components[1]}}});
+ instruction->InsertBefore(MakeUnique<opt::Instruction>(logical_instruction));
+ fuzzerutil::UpdateModuleIdBound(ir_context, logical_instruction.result_id());
+
+ // Evaluates the remaining components.
+ for (uint32_t i = 2; i < vector_components.size(); i++) {
+ logical_instruction = opt::Instruction(
+ ir_context, logical_instruction.opcode(), instruction->type_id(),
+ *fresh_id++,
+ {{SPV_OPERAND_TYPE_ID, {vector_components[i]}},
+ {SPV_OPERAND_TYPE_ID, {logical_instruction.result_id()}}});
+ instruction->InsertBefore(
+ MakeUnique<opt::Instruction>(logical_instruction));
+ fuzzerutil::UpdateModuleIdBound(ir_context,
+ logical_instruction.result_id());
+ }
+
+ ir_context->InvalidateAnalysesExceptFor(opt::IRContext::kAnalysisNone);
+
+ // If it's possible to make a synonym of |instruction|, then add the fact that
+ // the last |logical_instruction| is a synonym of |instruction|.
+ if (fuzzerutil::CanMakeSynonymOf(ir_context, *transformation_context,
+ instruction)) {
+ transformation_context->GetFactManager()->AddFactDataSynonym(
+ MakeDataDescriptor(logical_instruction.result_id(), {}),
+ MakeDataDescriptor(instruction->result_id(), {}));
+ }
+}
+
+protobufs::Transformation TransformationExpandVectorReduction::ToMessage()
+ const {
+ protobufs::Transformation result;
+ *result.mutable_expand_vector_reduction() = message_;
+ return result;
+}
+
+uint32_t TransformationExpandVectorReduction::GetRequiredFreshIdCount(
+ opt::IRContext* ir_context, opt::Instruction* instruction) {
+ // For each vector component, 1 OpCompositeExtract and 1 OpLogical* (except
+ // for the first component) instructions will be inserted.
+ return 2 * ir_context->get_type_mgr()
+ ->GetType(ir_context->get_def_use_mgr()
+ ->GetDef(instruction->GetSingleWordInOperand(0))
+ ->type_id())
+ ->AsVector()
+ ->element_count() -
+ 1;
+}
+
+std::unordered_set<uint32_t> TransformationExpandVectorReduction::GetFreshIds()
+ const {
+ std::unordered_set<uint32_t> result;
+ for (auto id : message_.fresh_ids()) {
+ result.insert(id);
+ }
+ return result;
+}
+
+} // namespace fuzz
+} // namespace spvtools
diff --git a/source/fuzz/transformation_expand_vector_reduction.h b/source/fuzz/transformation_expand_vector_reduction.h
new file mode 100644
index 0000000..e4cc953
--- /dev/null
+++ b/source/fuzz/transformation_expand_vector_reduction.h
@@ -0,0 +1,105 @@
+// Copyright (c) 2020 André Perez Maselco
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef SOURCE_FUZZ_TRANSFORMATION_EXPAND_VECTOR_REDUCTION_H_
+#define SOURCE_FUZZ_TRANSFORMATION_EXPAND_VECTOR_REDUCTION_H_
+
+#include "source/fuzz/protobufs/spirvfuzz_protobufs.h"
+#include "source/fuzz/transformation.h"
+#include "source/fuzz/transformation_context.h"
+#include "source/opt/ir_context.h"
+
+namespace spvtools {
+namespace fuzz {
+
+// clang-format off
+// SPIR-V code to help understand the transformation.
+//
+// -------------------------------------------------------------------------------
+// | Reference shader | Variant shader |
+// -------------------------------------------------------------------------------
+// | OpCapability Shader | OpCapability Shader |
+// | %1 = OpExtInstImport "GLSL.std.450" | %1 = OpExtInstImport "GLSL.std.450" |
+// | OpMemoryModel Logical GLSL450 | OpMemoryModel Logical GLSL450 |
+// | OpEntryPoint Vertex %9 "main" | OpEntryPoint Vertex %9 "main" |
+// | | |
+// | ; Types | ; Types |
+// | %2 = OpTypeBool | %2 = OpTypeBool |
+// | %3 = OpTypeVector %2 2 | %3 = OpTypeVector %2 2 |
+// | %4 = OpTypeVoid | %4 = OpTypeVoid |
+// | %5 = OpTypeFunction %4 | %5 = OpTypeFunction %4 |
+// | | |
+// | ; Constants | ; Constants |
+// | %6 = OpConstantTrue %2 | %6 = OpConstantTrue %2 |
+// | %7 = OpConstantFalse %2 | %7 = OpConstantFalse %2 |
+// | %8 = OpConstantComposite %3 %6 %7 | %8 = OpConstantComposite %3 %6 %7 |
+// | | |
+// | ; main function | ; main function |
+// | %9 = OpFunction %4 None %5 | %9 = OpFunction %4 None %5 |
+// | %10 = OpLabel | %10 = OpLabel |
+// | %11 = OpAny %2 %8 | |
+// | %12 = OpAll %2 %8 | ; Add OpAny synonym |
+// | OpReturn | %13 = OpCompositeExtract %2 %8 0 |
+// | OpFunctionEnd | %14 = OpCompositeExtract %2 %8 1 |
+// | | %15 = OpLogicalOr %2 %13 %14 |
+// | | %11 = OpAny %2 %8 |
+// | | |
+// | | ; Add OpAll synonym |
+// | | %16 = OpCompositeExtract %2 %8 0 |
+// | | %17 = OpCompositeExtract %2 %8 1 |
+// | | %18 = OpLogicalAnd %2 %16 %17 |
+// | | %12 = OpAll %2 %8 |
+// | | OpReturn |
+// | | OpFunctionEnd |
+// -------------------------------------------------------------------------------
+//
+// %11 and %15 are synonymous
+// %12 and %18 are synonymous
+// clang-format on
+class TransformationExpandVectorReduction : public Transformation {
+ public:
+ explicit TransformationExpandVectorReduction(
+ const protobufs::TransformationExpandVectorReduction& message);
+
+ TransformationExpandVectorReduction(const uint32_t instruction_result_id,
+ const std::vector<uint32_t>& fresh_ids);
+
+ // - |message_.instruction_result_id| must be OpAny or OpAll.
+ // - |message_.fresh_ids| must be fresh ids needed to apply the
+ // transformation.
+ bool IsApplicable(
+ opt::IRContext* ir_context,
+ const TransformationContext& transformation_context) const override;
+
+ // Adds synonyms for OpAny and OpAll instructions by evaluating each vector
+ // component with the corresponding logical operation.
+ void Apply(opt::IRContext* ir_context,
+ TransformationContext* transformation_context) const override;
+
+ std::unordered_set<uint32_t> GetFreshIds() const override;
+
+ protobufs::Transformation ToMessage() const override;
+
+ // Returns the number of fresh ids required to apply the transformation.
+ static uint32_t GetRequiredFreshIdCount(opt::IRContext* ir_context,
+ opt::Instruction* instruction);
+
+ private:
+ protobufs::TransformationExpandVectorReduction message_;
+};
+
+} // namespace fuzz
+} // namespace spvtools
+
+#endif // SOURCE_FUZZ_TRANSFORMATION_EXPAND_VECTOR_REDUCTION_H_
diff --git a/test/fuzz/CMakeLists.txt b/test/fuzz/CMakeLists.txt
index 866a924..2e93293 100644
--- a/test/fuzz/CMakeLists.txt
+++ b/test/fuzz/CMakeLists.txt
@@ -73,6 +73,7 @@
transformation_compute_data_synonym_fact_closure_test.cpp
transformation_duplicate_region_with_selection_test.cpp
transformation_equation_instruction_test.cpp
+ transformation_expand_vector_reduction_test.cpp
transformation_flatten_conditional_branch_test.cpp
transformation_function_call_test.cpp
transformation_inline_function_test.cpp
diff --git a/test/fuzz/transformation_expand_vector_reduction_test.cpp b/test/fuzz/transformation_expand_vector_reduction_test.cpp
new file mode 100644
index 0000000..ae5c4af
--- /dev/null
+++ b/test/fuzz/transformation_expand_vector_reduction_test.cpp
@@ -0,0 +1,284 @@
+// Copyright (c) 2020 André Perez Maselco
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "source/fuzz/transformation_expand_vector_reduction.h"
+
+#include "gtest/gtest.h"
+#include "source/fuzz/fuzzer_util.h"
+#include "source/fuzz/instruction_descriptor.h"
+#include "test/fuzz/fuzz_test_util.h"
+
+namespace spvtools {
+namespace fuzz {
+namespace {
+
+TEST(TransformationExpandVectorReductionTest, IsApplicable) {
+ std::string reference_shader = R"(
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Vertex %9 "main"
+
+ ; Types
+ %2 = OpTypeBool
+ %3 = OpTypeVector %2 2
+ %4 = OpTypeVoid
+ %5 = OpTypeFunction %4
+
+ ; Constants
+ %6 = OpConstantTrue %2
+ %7 = OpConstantFalse %2
+ %8 = OpConstantComposite %3 %6 %7
+
+ ; main function
+ %9 = OpFunction %4 None %5
+ %10 = OpLabel
+ %11 = OpAny %2 %8
+ %12 = OpAll %2 %8
+ OpReturn
+ OpFunctionEnd
+ )";
+
+ const auto env = SPV_ENV_UNIVERSAL_1_5;
+ const auto consumer = nullptr;
+ const auto context =
+ BuildModule(env, consumer, reference_shader, kFuzzAssembleOption);
+
+ spvtools::ValidatorOptions validator_options;
+ TransformationContext transformation_context(
+ MakeUnique<FactManager>(context.get()), validator_options);
+ ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
+ kConsoleMessageConsumer));
+
+ // Tests undefined instruction.
+ auto transformation = TransformationExpandVectorReduction(13, {14, 15, 16});
+ ASSERT_FALSE(
+ transformation.IsApplicable(context.get(), transformation_context));
+
+ // Tests non OpAny or OpAll instruction.
+ transformation = TransformationExpandVectorReduction(10, {13, 14, 15});
+ ASSERT_FALSE(
+ transformation.IsApplicable(context.get(), transformation_context));
+
+ // Tests the number of fresh ids being different than the necessary.
+ transformation = TransformationExpandVectorReduction(11, {13, 14});
+ ASSERT_FALSE(
+ transformation.IsApplicable(context.get(), transformation_context));
+
+ transformation = TransformationExpandVectorReduction(12, {13, 14, 15, 16});
+ ASSERT_FALSE(
+ transformation.IsApplicable(context.get(), transformation_context));
+
+ // Tests non-fresh ids.
+ transformation = TransformationExpandVectorReduction(11, {12, 13, 14});
+ ASSERT_FALSE(
+ transformation.IsApplicable(context.get(), transformation_context));
+
+ // Tests duplicated fresh ids.
+ transformation = TransformationExpandVectorReduction(11, {13, 13, 14});
+ ASSERT_FALSE(
+ transformation.IsApplicable(context.get(), transformation_context));
+
+ // Tests applicable transformations.
+ transformation = TransformationExpandVectorReduction(11, {13, 14, 15});
+ ASSERT_TRUE(
+ transformation.IsApplicable(context.get(), transformation_context));
+
+ transformation = TransformationExpandVectorReduction(12, {13, 14, 15});
+ ASSERT_TRUE(
+ transformation.IsApplicable(context.get(), transformation_context));
+}
+
+TEST(TransformationExpandVectorReductionTest, Apply) {
+ std::string reference_shader = R"(
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Vertex %13 "main"
+
+ ; Types
+ %2 = OpTypeBool
+ %3 = OpTypeVector %2 2
+ %4 = OpTypeVector %2 3
+ %5 = OpTypeVector %2 4
+ %6 = OpTypeVoid
+ %7 = OpTypeFunction %6
+
+ ; Constants
+ %8 = OpConstantTrue %2
+ %9 = OpConstantFalse %2
+ %10 = OpConstantComposite %3 %8 %9
+ %11 = OpConstantComposite %4 %8 %9 %8
+ %12 = OpConstantComposite %5 %8 %9 %8 %9
+
+ ; main function
+ %13 = OpFunction %6 None %7
+ %14 = OpLabel
+
+ ; OpAny for 2-dimensional vector
+ %15 = OpAny %2 %10
+
+ ; OpAny for 3-dimensional vector
+ %16 = OpAny %2 %11
+
+ ; OpAny for 4-dimensional vector
+ %17 = OpAny %2 %12
+
+ ; OpAll for 2-dimensional vector
+ %18 = OpAll %2 %10
+
+ ; OpAll for 3-dimensional vector
+ %19 = OpAll %2 %11
+
+ ; OpAll for 4-dimensional vector
+ %20 = OpAll %2 %12
+ OpReturn
+ OpFunctionEnd
+ )";
+
+ const auto env = SPV_ENV_UNIVERSAL_1_5;
+ const auto consumer = nullptr;
+ const auto context =
+ BuildModule(env, consumer, reference_shader, kFuzzAssembleOption);
+
+ spvtools::ValidatorOptions validator_options;
+ TransformationContext transformation_context(
+ MakeUnique<FactManager>(context.get()), validator_options);
+ ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
+ kConsoleMessageConsumer));
+
+ // Adds OpAny synonym for 2-dimensional vector.
+ auto transformation = TransformationExpandVectorReduction(15, {21, 22, 23});
+ ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context);
+ ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
+ MakeDataDescriptor(23, {}), MakeDataDescriptor(15, {})));
+
+ // Adds OpAny synonym for 3-dimensional vector.
+ transformation =
+ TransformationExpandVectorReduction(16, {24, 25, 26, 27, 28});
+ ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context);
+ ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
+ MakeDataDescriptor(28, {}), MakeDataDescriptor(16, {})));
+
+ // Adds OpAny synonym for 4-dimensional vector.
+ transformation =
+ TransformationExpandVectorReduction(17, {29, 30, 31, 32, 33, 34, 35});
+ ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context);
+ ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
+ MakeDataDescriptor(35, {}), MakeDataDescriptor(17, {})));
+
+ // Adds OpAll synonym for 2-dimensional vector.
+ transformation = TransformationExpandVectorReduction(18, {36, 37, 38});
+ ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context);
+ ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
+ MakeDataDescriptor(38, {}), MakeDataDescriptor(18, {})));
+
+ // Adds OpAll synonym for 3-dimensional vector.
+ transformation =
+ TransformationExpandVectorReduction(19, {39, 40, 41, 42, 43});
+ ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context);
+ ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
+ MakeDataDescriptor(43, {}), MakeDataDescriptor(19, {})));
+
+ // Adds OpAll synonym for 4-dimensional vector.
+ transformation =
+ TransformationExpandVectorReduction(20, {44, 45, 46, 47, 48, 49, 50});
+ ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context);
+ ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
+ MakeDataDescriptor(50, {}), MakeDataDescriptor(20, {})));
+
+ std::string variant_shader = R"(
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Vertex %13 "main"
+
+ ; Types
+ %2 = OpTypeBool
+ %3 = OpTypeVector %2 2
+ %4 = OpTypeVector %2 3
+ %5 = OpTypeVector %2 4
+ %6 = OpTypeVoid
+ %7 = OpTypeFunction %6
+
+ ; Constants
+ %8 = OpConstantTrue %2
+ %9 = OpConstantFalse %2
+ %10 = OpConstantComposite %3 %8 %9
+ %11 = OpConstantComposite %4 %8 %9 %8
+ %12 = OpConstantComposite %5 %8 %9 %8 %9
+
+ ; main function
+ %13 = OpFunction %6 None %7
+ %14 = OpLabel
+
+ ; Add OpAny synonym for 2-dimensional vector
+ %21 = OpCompositeExtract %2 %10 0
+ %22 = OpCompositeExtract %2 %10 1
+ %23 = OpLogicalOr %2 %21 %22
+ %15 = OpAny %2 %10
+
+ ; Add OpAny synonym for 3-dimensional vector
+ %24 = OpCompositeExtract %2 %11 0
+ %25 = OpCompositeExtract %2 %11 1
+ %26 = OpCompositeExtract %2 %11 2
+ %27 = OpLogicalOr %2 %24 %25
+ %28 = OpLogicalOr %2 %26 %27
+ %16 = OpAny %2 %11
+
+ ; Add OpAny synonym for 4-dimensional vector
+ %29 = OpCompositeExtract %2 %12 0
+ %30 = OpCompositeExtract %2 %12 1
+ %31 = OpCompositeExtract %2 %12 2
+ %32 = OpCompositeExtract %2 %12 3
+ %33 = OpLogicalOr %2 %29 %30
+ %34 = OpLogicalOr %2 %31 %33
+ %35 = OpLogicalOr %2 %32 %34
+ %17 = OpAny %2 %12
+
+ ; Add OpAll synonym for 2-dimensional vector
+ %36 = OpCompositeExtract %2 %10 0
+ %37 = OpCompositeExtract %2 %10 1
+ %38 = OpLogicalAnd %2 %36 %37
+ %18 = OpAll %2 %10
+
+ ; Add OpAll synonym for 3-dimensional vector
+ %39 = OpCompositeExtract %2 %11 0
+ %40 = OpCompositeExtract %2 %11 1
+ %41 = OpCompositeExtract %2 %11 2
+ %42 = OpLogicalAnd %2 %39 %40
+ %43 = OpLogicalAnd %2 %41 %42
+ %19 = OpAll %2 %11
+
+ ; Add OpAll synonym for 4-dimensional vector
+ %44 = OpCompositeExtract %2 %12 0
+ %45 = OpCompositeExtract %2 %12 1
+ %46 = OpCompositeExtract %2 %12 2
+ %47 = OpCompositeExtract %2 %12 3
+ %48 = OpLogicalAnd %2 %44 %45
+ %49 = OpLogicalAnd %2 %46 %48
+ %50 = OpLogicalAnd %2 %47 %49
+ %20 = OpAll %2 %12
+ OpReturn
+ OpFunctionEnd
+ )";
+
+ ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
+ kConsoleMessageConsumer));
+ ASSERT_TRUE(IsEqual(env, variant_shader, context.get()));
+}
+
+} // namespace
+} // namespace fuzz
+} // namespace spvtools