spirv-fuzz: Create synonym of int constant using a loop (#3790)

This transformation, given a constant integer (scalar or vector) C,
constants I and S of compatible type and scalar 32-bit integer constant
N, such that C = I - S*N, adds a loop which subtracts S from I, N
times, creating a synonym for C.

The related fuzzer pass randomly chooses constants to which to add
synonyms using this transformation, and the location where they should
be added.

Fixes #3616.
This commit is contained in:
Stefano Milizia 2020-09-23 15:10:02 +02:00 committed by GitHub
parent 7cc4b4d2ca
commit 67525bded1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
18 changed files with 1952 additions and 2 deletions

View File

@ -63,15 +63,16 @@ if(SPIRV_BUILD_FUZZER)
fuzzer_pass_add_function_calls.h
fuzzer_pass_add_global_variables.h
fuzzer_pass_add_image_sample_unused_components.h
fuzzer_pass_add_synonyms.h
fuzzer_pass_add_loads.h
fuzzer_pass_add_local_variables.h
fuzzer_pass_add_loop_preheaders.h
fuzzer_pass_add_loops_to_create_int_constant_synonyms.h
fuzzer_pass_add_no_contraction_decorations.h
fuzzer_pass_add_opphi_synonyms.h
fuzzer_pass_add_parameters.h
fuzzer_pass_add_relaxed_decorations.h
fuzzer_pass_add_stores.h
fuzzer_pass_add_synonyms.h
fuzzer_pass_add_vector_shuffle_instructions.h
fuzzer_pass_adjust_branch_weights.h
fuzzer_pass_adjust_function_controls.h
@ -147,6 +148,7 @@ if(SPIRV_BUILD_FUZZER)
transformation_add_image_sample_unused_components.h
transformation_add_local_variable.h
transformation_add_loop_preheader.h
transformation_add_loop_to_create_int_constant_synonym.h
transformation_add_no_contraction_decoration.h
transformation_add_opphi_synonym.h
transformation_add_parameter.h
@ -237,15 +239,16 @@ if(SPIRV_BUILD_FUZZER)
fuzzer_pass_add_function_calls.cpp
fuzzer_pass_add_global_variables.cpp
fuzzer_pass_add_image_sample_unused_components.cpp
fuzzer_pass_add_synonyms.cpp
fuzzer_pass_add_loads.cpp
fuzzer_pass_add_local_variables.cpp
fuzzer_pass_add_loop_preheaders.cpp
fuzzer_pass_add_loops_to_create_int_constant_synonyms.cpp
fuzzer_pass_add_no_contraction_decorations.cpp
fuzzer_pass_add_opphi_synonyms.cpp
fuzzer_pass_add_parameters.cpp
fuzzer_pass_add_relaxed_decorations.cpp
fuzzer_pass_add_stores.cpp
fuzzer_pass_add_synonyms.cpp
fuzzer_pass_add_vector_shuffle_instructions.cpp
fuzzer_pass_adjust_branch_weights.cpp
fuzzer_pass_adjust_function_controls.cpp
@ -319,6 +322,7 @@ if(SPIRV_BUILD_FUZZER)
transformation_add_image_sample_unused_components.cpp
transformation_add_local_variable.cpp
transformation_add_loop_preheader.cpp
transformation_add_loop_to_create_int_constant_synonym.cpp
transformation_add_no_contraction_decoration.cpp
transformation_add_opphi_synonym.cpp
transformation_add_parameter.cpp

View File

@ -35,6 +35,7 @@
#include "source/fuzz/fuzzer_pass_add_loads.h"
#include "source/fuzz/fuzzer_pass_add_local_variables.h"
#include "source/fuzz/fuzzer_pass_add_loop_preheaders.h"
#include "source/fuzz/fuzzer_pass_add_loops_to_create_int_constant_synonyms.h"
#include "source/fuzz/fuzzer_pass_add_no_contraction_decorations.h"
#include "source/fuzz/fuzzer_pass_add_opphi_synonyms.h"
#include "source/fuzz/fuzzer_pass_add_parameters.h"
@ -238,6 +239,8 @@ Fuzzer::FuzzerResult Fuzzer::Run() {
MaybeAddRepeatedPass<FuzzerPassAddLoads>(&pass_instances);
MaybeAddRepeatedPass<FuzzerPassAddLocalVariables>(&pass_instances);
MaybeAddRepeatedPass<FuzzerPassAddLoopPreheaders>(&pass_instances);
MaybeAddRepeatedPass<FuzzerPassAddLoopsToCreateIntConstantSynonyms>(
&pass_instances);
MaybeAddRepeatedPass<FuzzerPassAddOpPhiSynonyms>(&pass_instances);
MaybeAddRepeatedPass<FuzzerPassAddParameters>(&pass_instances);
MaybeAddRepeatedPass<FuzzerPassAddRelaxedDecorations>(&pass_instances);

View File

@ -75,6 +75,8 @@ const std::pair<uint32_t, uint32_t> kChanceOfChoosingWorkgroupStorageClass = {
50, 50};
const std::pair<uint32_t, uint32_t> kChanceOfConstructingComposite = {20, 50};
const std::pair<uint32_t, uint32_t> kChanceOfCopyingObject = {20, 50};
const std::pair<uint32_t, uint32_t> kChanceOfCreatingIntSynonymsUsingLoops = {
5, 10};
const std::pair<uint32_t, uint32_t> kChanceOfDonatingAdditionalModule = {5, 50};
const std::pair<uint32_t, uint32_t> kChanceOfDuplicatingRegionWithSelection = {
20, 50};
@ -84,6 +86,8 @@ const std::pair<uint32_t, uint32_t> kChanceOfGoingDeeperToInsertInComposite = {
30, 70};
const std::pair<uint32_t, uint32_t> kChanceOfGoingDeeperWhenMakingAccessChain =
{50, 95};
const std::pair<uint32_t, uint32_t>
kChanceOfHavingTwoBlocksInLoopToCreateIntSynonym = {50, 80};
const std::pair<uint32_t, uint32_t> kChanceOfInliningFunction = {10, 90};
const std::pair<uint32_t, uint32_t> kChanceOfInterchangingZeroLikeConstants = {
10, 90};
@ -244,6 +248,8 @@ FuzzerContext::FuzzerContext(RandomGenerator* random_generator,
chance_of_constructing_composite_ =
ChooseBetweenMinAndMax(kChanceOfConstructingComposite);
chance_of_copying_object_ = ChooseBetweenMinAndMax(kChanceOfCopyingObject);
chance_of_creating_int_synonyms_using_loops_ =
ChooseBetweenMinAndMax(kChanceOfCreatingIntSynonymsUsingLoops);
chance_of_donating_additional_module_ =
ChooseBetweenMinAndMax(kChanceOfDonatingAdditionalModule);
chance_of_duplicating_region_with_selection_ =
@ -254,6 +260,8 @@ FuzzerContext::FuzzerContext(RandomGenerator* random_generator,
ChooseBetweenMinAndMax(kChanceOfGoingDeeperToInsertInComposite);
chance_of_going_deeper_when_making_access_chain_ =
ChooseBetweenMinAndMax(kChanceOfGoingDeeperWhenMakingAccessChain);
chance_of_having_two_blocks_in_loop_to_create_int_synonym_ =
ChooseBetweenMinAndMax(kChanceOfHavingTwoBlocksInLoopToCreateIntSynonym);
chance_of_inlining_function_ =
ChooseBetweenMinAndMax(kChanceOfInliningFunction);
chance_of_interchanging_signedness_of_integer_operands_ =

View File

@ -204,6 +204,9 @@ class FuzzerContext {
return chance_of_constructing_composite_;
}
uint32_t GetChanceOfCopyingObject() { return chance_of_copying_object_; }
uint32_t GetChanceOfCreatingIntSynonymsUsingLoops() {
return chance_of_creating_int_synonyms_using_loops_;
}
uint32_t GetChanceOfDonatingAdditionalModule() {
return chance_of_donating_additional_module_;
}
@ -219,6 +222,9 @@ class FuzzerContext {
uint32_t GetChanceOfGoingDeeperWhenMakingAccessChain() {
return chance_of_going_deeper_when_making_access_chain_;
}
uint32_t GetChanceOfHavingTwoBlocksInLoopToCreateIntSynonym() {
return chance_of_having_two_blocks_in_loop_to_create_int_synonym_;
}
uint32_t GetChanceOfInliningFunction() {
return chance_of_inlining_function_;
}
@ -342,6 +348,9 @@ class FuzzerContext {
uint32_t GetRandomIndexForCompositeInsert(uint32_t number_of_components) {
return random_generator_->RandomUint32(number_of_components);
}
int64_t GetRandomValueForStepConstantInLoop() {
return random_generator_->RandomUint64(UINT64_MAX);
}
uint32_t GetRandomLoopControlPartialCount() {
return random_generator_->RandomUint32(max_loop_control_partial_count_);
}
@ -351,6 +360,9 @@ class FuzzerContext {
uint32_t GetRandomLoopLimit() {
return random_generator_->RandomUint32(max_loop_limit_);
}
uint32_t GetRandomNumberOfLoopIterations(uint32_t max_num_iterations) {
return ChooseBetweenMinAndMax({1, max_num_iterations});
}
uint32_t GetRandomNumberOfNewParameters(uint32_t num_of_params) {
assert(num_of_params < GetMaximumNumberOfFunctionParameters());
return ChooseBetweenMinAndMax(
@ -423,11 +435,13 @@ class FuzzerContext {
uint32_t chance_of_choosing_workgroup_storage_class_;
uint32_t chance_of_constructing_composite_;
uint32_t chance_of_copying_object_;
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_flattening_conditional_branch_;
uint32_t chance_of_going_deeper_to_insert_in_composite_;
uint32_t chance_of_going_deeper_when_making_access_chain_;
uint32_t chance_of_having_two_blocks_in_loop_to_create_int_synonym_;
uint32_t chance_of_inlining_function_;
uint32_t chance_of_interchanging_signedness_of_integer_operands_;
uint32_t chance_of_interchanging_zero_like_constants_;

View File

@ -35,6 +35,7 @@
#include "source/fuzz/transformation_add_type_pointer.h"
#include "source/fuzz/transformation_add_type_struct.h"
#include "source/fuzz/transformation_add_type_vector.h"
#include "source/fuzz/transformation_split_block.h"
namespace spvtools {
namespace fuzz {
@ -608,6 +609,29 @@ opt::BasicBlock* FuzzerPass::GetOrCreateSimpleLoopPreheader(
return &*function->FindBlock(preheader_id);
}
opt::BasicBlock* FuzzerPass::SplitBlockAfterOpPhiOrOpVariable(
uint32_t block_id) {
auto block = fuzzerutil::MaybeFindBlock(GetIRContext(), block_id);
assert(block && "|block_id| must be a block label");
assert(!block->IsLoopHeader() && "|block_id| cannot be a loop header");
// Find the first non-OpPhi and non-OpVariable instruction.
auto non_phi_or_var_inst = &*block->begin();
while (non_phi_or_var_inst->opcode() == SpvOpPhi ||
non_phi_or_var_inst->opcode() == SpvOpVariable) {
non_phi_or_var_inst = non_phi_or_var_inst->NextNode();
}
// Split the block.
uint32_t new_block_id = GetFuzzerContext()->GetFreshId();
ApplyTransformation(TransformationSplitBlock(
MakeInstructionDescriptor(GetIRContext(), non_phi_or_var_inst),
new_block_id));
// We need to return the newly-created block.
return &*block->GetParent()->FindBlock(new_block_id);
}
uint32_t FuzzerPass::FindOrCreateLocalVariable(
uint32_t pointer_type_id, uint32_t function_id,
bool pointee_value_is_irrelevant) {

View File

@ -293,6 +293,11 @@ class FuzzerPass {
// reachable in the CFG (and thus has at least 2 predecessors).
opt::BasicBlock* GetOrCreateSimpleLoopPreheader(uint32_t header_id);
// Returns the second block in the pair obtained by splitting |block_id| just
// after the last OpPhi or OpVariable instruction in it. Assumes that the
// block is not a loop header.
opt::BasicBlock* SplitBlockAfterOpPhiOrOpVariable(uint32_t block_id);
// Returns the id of an available local variable (storage class Function) with
// the fact PointeeValueIsIrrelevant set according to
// |pointee_value_is_irrelevant|. If there is no such variable, it creates one

View File

@ -0,0 +1,247 @@
// Copyright (c) 2020 Google LLC
//
// 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_add_loops_to_create_int_constant_synonyms.h"
#include "source/fuzz/call_graph.h"
#include "source/fuzz/fuzzer_util.h"
#include "source/fuzz/transformation_add_loop_to_create_int_constant_synonym.h"
namespace spvtools {
namespace fuzz {
namespace {
uint32_t kMaxNestingDepth = 4;
} // namespace
FuzzerPassAddLoopsToCreateIntConstantSynonyms::
FuzzerPassAddLoopsToCreateIntConstantSynonyms(
opt::IRContext* ir_context,
TransformationContext* transformation_context,
FuzzerContext* fuzzer_context,
protobufs::TransformationSequence* transformations)
: FuzzerPass(ir_context, transformation_context, fuzzer_context,
transformations) {}
FuzzerPassAddLoopsToCreateIntConstantSynonyms::
~FuzzerPassAddLoopsToCreateIntConstantSynonyms() = default;
void FuzzerPassAddLoopsToCreateIntConstantSynonyms::Apply() {
std::vector<uint32_t> constants;
// Choose the constants for which to create synonyms.
for (auto constant_def : GetIRContext()->GetConstants()) {
// Randomly decide whether to consider this constant.
if (!GetFuzzerContext()->ChoosePercentage(
GetFuzzerContext()->GetChanceOfCreatingIntSynonymsUsingLoops())) {
continue;
}
auto constant = GetIRContext()->get_constant_mgr()->FindDeclaredConstant(
constant_def->result_id());
// We only consider integer constants (scalar or vector).
if (!constant->AsIntConstant() &&
!(constant->AsVectorConstant() &&
constant->AsVectorConstant()->component_type()->AsInteger())) {
continue;
}
constants.push_back(constant_def->result_id());
}
std::vector<uint32_t> blocks;
// Get a list of all the blocks before which we can add a loop creating a new
// synonym. We cannot apply the transformation while iterating over the
// module, because we are going to add new blocks.
for (auto& function : *GetIRContext()->module()) {
// Consider all blocks reachable from the first block of the function.
GetIRContext()->cfg()->ForEachBlockInPostOrder(
&*function.begin(),
[&blocks](opt::BasicBlock* block) { blocks.push_back(block->id()); });
}
// Make sure that the module has an OpTypeBool instruction, and 32-bit signed
// integer constants 0 and 1, adding them if necessary.
FindOrCreateBoolType();
FindOrCreateIntegerConstant({0}, 32, true, false);
FindOrCreateIntegerConstant({1}, 32, true, false);
// Compute the call graph. We can use this for any further computation, since
// we are not adding or removing functions or function calls.
auto call_graph = CallGraph(GetIRContext());
// Consider each constant and each block.
for (uint32_t constant_id : constants) {
// Choose one of the blocks.
uint32_t block_id = blocks[GetFuzzerContext()->RandomIndex(blocks)];
// Adjust the block so that the transformation can be applied.
auto block = GetIRContext()->get_instr_block(block_id);
// If the block is a loop header, add a simple preheader. We can do this
// because we have excluded all the non-reachable headers.
if (block->IsLoopHeader()) {
block = GetOrCreateSimpleLoopPreheader(block->id());
block_id = block->id();
}
assert(!block->IsLoopHeader() &&
"The block cannot be a loop header at this point.");
// If the block is a merge block, a continue block or it does not have
// exactly 1 predecessor, split it after any OpPhi or OpVariable
// instructions.
if (GetIRContext()->GetStructuredCFGAnalysis()->IsMergeBlock(block->id()) ||
GetIRContext()->GetStructuredCFGAnalysis()->IsContinueBlock(
block->id()) ||
GetIRContext()->cfg()->preds(block->id()).size() != 1) {
block = SplitBlockAfterOpPhiOrOpVariable(block->id());
block_id = block->id();
}
// Randomly decide the values for the number of iterations and the step
// value, and compute the initial value accordingly.
// The maximum number of iterations depends on the maximum possible loop
// nesting depth of the block, computed interprocedurally, i.e. also
// considering the possibility that the enclosing function is called inside
// a loop. It is:
// - 1 if the nesting depth is >= kMaxNestingDepth
// - 2^(kMaxNestingDepth - nesting_depth) otherwise
uint32_t max_nesting_depth =
call_graph.GetMaxCallNestingDepth(block->GetParent()->result_id()) +
GetIRContext()->GetStructuredCFGAnalysis()->LoopNestingDepth(
block->id());
uint32_t num_iterations =
max_nesting_depth >= kMaxNestingDepth
? 1
: GetFuzzerContext()->GetRandomNumberOfLoopIterations(
1u << (kMaxNestingDepth - max_nesting_depth));
// Find or create the corresponding constant containing the number of
// iterations.
uint32_t num_iterations_id =
FindOrCreateIntegerConstant({num_iterations}, 32, true, false);
// Find the other constants.
// We use 64-bit values and then use the bits that we need. We find the
// step value (S) randomly and then compute the initial value (I) using
// the equation I = C + S*N.
uint32_t initial_value_id = 0;
uint32_t step_value_id = 0;
// Get the content of the existing constant.
const auto constant =
GetIRContext()->get_constant_mgr()->FindDeclaredConstant(constant_id);
const auto constant_type_id =
GetIRContext()->get_def_use_mgr()->GetDef(constant_id)->type_id();
if (constant->AsIntConstant()) {
// The constant is a scalar integer.
std::tie(initial_value_id, step_value_id) =
FindSuitableStepAndInitialValueConstants(
constant->GetZeroExtendedValue(),
constant->type()->AsInteger()->width(),
constant->type()->AsInteger()->IsSigned(), num_iterations);
} else {
// The constant is a vector of integers.
assert(constant->AsVectorConstant() &&
constant->AsVectorConstant()->component_type()->AsInteger() &&
"If the program got here, the constant should be a vector of "
"integers.");
// Find a constant for each component of the initial value and the step
// values.
std::vector<uint32_t> initial_value_component_ids;
std::vector<uint32_t> step_value_component_ids;
// Get the value, width and signedness of the components.
std::vector<uint64_t> component_values;
for (auto component : constant->AsVectorConstant()->GetComponents()) {
component_values.push_back(component->GetZeroExtendedValue());
}
uint32_t bit_width =
constant->AsVectorConstant()->component_type()->AsInteger()->width();
uint32_t is_signed = constant->AsVectorConstant()
->component_type()
->AsInteger()
->IsSigned();
for (uint64_t component_val : component_values) {
uint32_t initial_val_id;
uint32_t step_val_id;
std::tie(initial_val_id, step_val_id) =
FindSuitableStepAndInitialValueConstants(component_val, bit_width,
is_signed, num_iterations);
initial_value_component_ids.push_back(initial_val_id);
step_value_component_ids.push_back(step_val_id);
}
// Find or create the vector constants.
initial_value_id = FindOrCreateCompositeConstant(
initial_value_component_ids, constant_type_id, false);
step_value_id = FindOrCreateCompositeConstant(step_value_component_ids,
constant_type_id, false);
}
assert(initial_value_id && step_value_id &&
"|initial_value_id| and |step_value_id| should have been defined.");
// Randomly decide whether to have two blocks (or just one) in the new
// loop.
uint32_t additional_block_id =
GetFuzzerContext()->ChoosePercentage(
GetFuzzerContext()
->GetChanceOfHavingTwoBlocksInLoopToCreateIntSynonym())
? GetFuzzerContext()->GetFreshId()
: 0;
// Add the loop and create the synonym.
ApplyTransformation(TransformationAddLoopToCreateIntConstantSynonym(
constant_id, initial_value_id, step_value_id, num_iterations_id,
block_id, GetFuzzerContext()->GetFreshId(),
GetFuzzerContext()->GetFreshId(), GetFuzzerContext()->GetFreshId(),
GetFuzzerContext()->GetFreshId(), GetFuzzerContext()->GetFreshId(),
GetFuzzerContext()->GetFreshId(), GetFuzzerContext()->GetFreshId(),
additional_block_id));
}
}
std::pair<uint32_t, uint32_t> FuzzerPassAddLoopsToCreateIntConstantSynonyms::
FindSuitableStepAndInitialValueConstants(uint64_t constant_val,
uint32_t bit_width, bool is_signed,
uint32_t num_iterations) {
// Choose the step value randomly and compute the initial value accordingly.
// The result of |initial_value| could overflow, but this is OK, since
// the transformation takes overflows into consideration (the equation still
// holds as long as the last |bit_width| bits of C and of (I-S*N) match).
uint64_t step_value =
GetFuzzerContext()->GetRandomValueForStepConstantInLoop();
uint64_t initial_value = constant_val + step_value * num_iterations;
uint32_t initial_val_id = FindOrCreateIntegerConstant(
fuzzerutil::IntToWords(initial_value, bit_width, is_signed), bit_width,
is_signed, false);
uint32_t step_val_id = FindOrCreateIntegerConstant(
fuzzerutil::IntToWords(step_value, bit_width, is_signed), bit_width,
is_signed, false);
return {initial_val_id, step_val_id};
}
} // namespace fuzz
} // namespace spvtools

View File

@ -0,0 +1,53 @@
// Copyright (c) 2020 Google LLC
//
// 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_ADD_LOOPS_TO_CREATE_INT_CONSTANT_SYNONYMS_H_
#define SOURCE_FUZZ_FUZZER_PASS_ADD_LOOPS_TO_CREATE_INT_CONSTANT_SYNONYMS_H_
#include "source/fuzz/fuzzer_pass.h"
namespace spvtools {
namespace fuzz {
// A fuzzer pass that adds synonyms for integer, scalar or vector, constants, by
// adding loops that compute the same value by subtracting a value S from an
// initial value I, and for N times, so that C = I - S*N.
class FuzzerPassAddLoopsToCreateIntConstantSynonyms : public FuzzerPass {
public:
FuzzerPassAddLoopsToCreateIntConstantSynonyms(
opt::IRContext* ir_context, TransformationContext* transformation_context,
FuzzerContext* fuzzer_context,
protobufs::TransformationSequence* transformations);
~FuzzerPassAddLoopsToCreateIntConstantSynonyms();
void Apply() override;
private:
// Returns a pair (initial_val_id, step_val_id) such that both ids are
// integer scalar constants of the same type as the scalar integer constant
// identified by the given |constant_val|, |bit_width| and signedness, and
// such that, if I is the value of initial_val_id, S is the value of
// step_val_id and C is the value of the constant, the equation (C = I - S *
// num_iterations) holds, (only considering the last |bit_width| bits of each
// constant).
std::pair<uint32_t, uint32_t> FindSuitableStepAndInitialValueConstants(
uint64_t constant_val, uint32_t bit_width, bool is_signed,
uint32_t num_iterations);
};
} // namespace fuzz
} // namespace spvtools
#endif // SOURCE_FUZZ_FUZZER_PASS_ADD_LOOPS_TO_CREATE_INT_CONSTANT_SYNONYMS_H_

View File

@ -1306,6 +1306,30 @@ void AddStructType(opt::IRContext* ir_context, uint32_t result_id,
UpdateModuleIdBound(ir_context, result_id);
}
std::vector<uint32_t> IntToWords(uint64_t value, uint32_t width,
bool is_signed) {
assert(width <= 64 && "The bit width should not be more than 64 bits");
// Sign-extend or zero-extend the last |width| bits of |value|, depending on
// |is_signed|.
if (is_signed) {
// Sign-extend by shifting left and then shifting right, interpreting the
// integer as signed.
value = static_cast<int64_t>(value << (64 - width)) >> (64 - width);
} else {
// Zero-extend by shifting left and then shifting right, interpreting the
// integer as unsigned.
value = (value << (64 - width)) >> (64 - width);
}
std::vector<uint32_t> result;
result.push_back(static_cast<uint32_t>(value));
if (width > 32) {
result.push_back(static_cast<uint32_t>(value >> 32));
}
return result;
}
bool TypesAreEqualUpToSign(opt::IRContext* ir_context, uint32_t type1_id,
uint32_t type2_id) {
if (type1_id == type2_id) {

View File

@ -474,6 +474,17 @@ void AddVectorType(opt::IRContext* ir_context, uint32_t result_id,
void AddStructType(opt::IRContext* ir_context, uint32_t result_id,
const std::vector<uint32_t>& component_type_ids);
// Returns a vector of words representing the integer |value|, only considering
// the last |width| bits. The last |width| bits are sign-extended if the value
// is signed, zero-extended if it is unsigned.
// |width| must be <= 64.
// If |width| <= 32, returns a vector containing one value. If |width| > 64,
// returns a vector containing two values, with the first one representing the
// lower-order word of the value and the second one representing the
// higher-order word.
std::vector<uint32_t> IntToWords(uint64_t value, uint32_t width,
bool is_signed);
// Returns a bit pattern that represents a floating-point |value|.
inline uint32_t FloatToWord(float value) {
uint32_t result;

View File

@ -30,6 +30,7 @@
#include "source/fuzz/fuzzer_pass_add_loads.h"
#include "source/fuzz/fuzzer_pass_add_local_variables.h"
#include "source/fuzz/fuzzer_pass_add_loop_preheaders.h"
#include "source/fuzz/fuzzer_pass_add_loops_to_create_int_constant_synonyms.h"
#include "source/fuzz/fuzzer_pass_add_opphi_synonyms.h"
#include "source/fuzz/fuzzer_pass_add_parameters.h"
#include "source/fuzz/fuzzer_pass_add_relaxed_decorations.h"
@ -118,6 +119,7 @@ class RepeatedPassInstances {
REPEATED_PASS_INSTANCE(AddLoads);
REPEATED_PASS_INSTANCE(AddLocalVariables);
REPEATED_PASS_INSTANCE(AddLoopPreheaders);
REPEATED_PASS_INSTANCE(AddLoopsToCreateIntConstantSynonyms);
REPEATED_PASS_INSTANCE(AddOpPhiSynonyms);
REPEATED_PASS_INSTANCE(AddParameters);
REPEATED_PASS_INSTANCE(AddRelaxedDecorations);

View File

@ -122,6 +122,10 @@ RepeatedPassRecommenderStandard::GetFuturePassRecommendations(
{pass_instances_->GetDuplicateRegionsWithSelections(),
pass_instances_->GetOutlineFunctions()});
}
if (&pass == pass_instances_->GetAddLoopsToCreateIntConstantSynonyms()) {
// - New synonyms can be applied
return RandomOrderAndNonNull({pass_instances_->GetApplyIdSynonyms()});
}
if (&pass == pass_instances_->GetAddOpPhiSynonyms()) {
// - New synonyms can be applied
// - If OpPhi synonyms are introduced for blocks with dead predecessors, the

View File

@ -497,6 +497,7 @@ message Transformation {
TransformationDuplicateRegionWithSelection duplicate_region_with_selection = 75;
TransformationFlattenConditionalBranch flatten_conditional_branch = 76;
TransformationAddBitInstructionSynonym add_bit_instruction_synonym = 77;
TransformationAddLoopToCreateIntConstantSynonym add_loop_to_create_int_constant_synonym = 78;
// Add additional option using the next available number.
}
}
@ -847,6 +848,97 @@ message TransformationAddLoopPreheader {
}
message TransformationAddLoopToCreateIntConstantSynonym {
// A transformation that uses a loop to create a synonym for an integer
// constant C (scalar or vector) using an initial value I, a step value S and
// a number of iterations N such that C = I - N * S. For each iteration, S is
// added to the total.
// The loop can be made up of one or two blocks, and it is inserted before a
// block with a single predecessor. In the one-block case, it is of the form:
//
// %loop_id = OpLabel
// %ctr_id = OpPhi %int %int_0 %pred %incremented_ctr_id %loop_id
// %temp_id = OpPhi %type_of_I %I %pred %eventual_syn_id %loop_id
// %eventual_syn_id = OpISub %type_of_I %temp_id %step_val_id
// %incremented_ctr_id = OpIAdd %int %ctr_id %int_1
// %cond_id = OpSLessThan %bool %incremented_ctr_id %num_iterations_id
// OpLoopMerge %block_after_loop_id %loop_id %none
// OpBranchConditional %cond_id %loop_id %block_after_loop_id
//
// A new OpPhi instruction is then added to %block_after_loop_id, as follows:
//
// %block_after_loop_id = OpLabel
// %syn_id = OpPhi %type_of_I %eventual_syn_id %loop_id
//
// This can be translated, assuming that N > 0, to:
// int syn = I;
// for (int ctr = 0; ctr < N; ctr++) syn = syn - S;
//
// All existing OpPhi instructions in %block_after_loop_id are also updated
// to reflect the fact that its predecessor is now %loop_id.
// The following are existing ids.
// The id of the integer constant C that we want a synonym of.
uint32 constant_id = 1;
// The id of the initial value integer constant I.
uint32 initial_val_id = 2;
// The id of the step value integer constant S.
uint32 step_val_id = 3;
// The id of the integer scalar constant, its value being the number of
// iterations N.
uint32 num_iterations_id = 4;
// The label id of the block before which the loop must be inserted.
uint32 block_after_loop_id = 5;
// The following are fresh ids.
// A fresh id for the synonym.
uint32 syn_id = 6;
// A fresh id for the label of the loop,
uint32 loop_id = 7;
// A fresh id for the counter.
uint32 ctr_id = 8;
// A fresh id taking the value I - S * ctr at the ctr-th iteration.
uint32 temp_id = 9;
// A fresh id taking the value I - S * (ctr + 1) at the ctr-th iteration, and
// thus I - S * N at the last iteration.
uint32 eventual_syn_id = 10;
// A fresh id for the incremented counter.
uint32 incremented_ctr_id = 11;
// A fresh id for the loop condition.
uint32 cond_id = 12;
// The instructions in the loop can also be laid out in two basic blocks, as follows:
//
// %loop_id = OpLabel
// %ctr_id = OpPhi %int %int_0 %pred %incremented_ctr_id %loop_id
// %temp_id = OpPhi %type_of_I %I %pred %eventual_syn_id %loop_id
// OpLoopMerge %block_after_loop_id %additional_block_id None
// OpBranch %additional_block_id
//
// %additional_block_id = OpLabel
// %eventual_syn_id = OpISub %type_of_I %temp_id %step_val_id
// %incremented_ctr_id = OpIAdd %int %ctr_id %int_1
// %cond_id = OpSLessThan %bool %incremented_ctr_id %num_iterations_id
// OpBranchConditional %cond_id %loop_id %block_after_loop_id
// A fresh id for the additional block. If this is 0, it means that only one
// block is to be created.
uint32 additional_block_id = 13;
}
message TransformationAddNoContractionDecoration {
// Applies OpDecorate NoContraction to the given result id

View File

@ -33,6 +33,7 @@
#include "source/fuzz/transformation_add_image_sample_unused_components.h"
#include "source/fuzz/transformation_add_local_variable.h"
#include "source/fuzz/transformation_add_loop_preheader.h"
#include "source/fuzz/transformation_add_loop_to_create_int_constant_synonym.h"
#include "source/fuzz/transformation_add_no_contraction_decoration.h"
#include "source/fuzz/transformation_add_opphi_synonym.h"
#include "source/fuzz/transformation_add_parameter.h"
@ -149,6 +150,10 @@ std::unique_ptr<Transformation> Transformation::FromMessage(
case protobufs::Transformation::TransformationCase::kAddLoopPreheader:
return MakeUnique<TransformationAddLoopPreheader>(
message.add_loop_preheader());
case protobufs::Transformation::TransformationCase::
kAddLoopToCreateIntConstantSynonym:
return MakeUnique<TransformationAddLoopToCreateIntConstantSynonym>(
message.add_loop_to_create_int_constant_synonym());
case protobufs::Transformation::TransformationCase::
kAddNoContractionDecoration:
return MakeUnique<TransformationAddNoContractionDecoration>(

View File

@ -0,0 +1,427 @@
// Copyright (c) 2020 Google LLC
//
// 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_add_loop_to_create_int_constant_synonym.h"
#include "source/fuzz/fuzzer_util.h"
namespace spvtools {
namespace fuzz {
namespace {
uint32_t kMaxNumOfIterations = 32;
}
TransformationAddLoopToCreateIntConstantSynonym::
TransformationAddLoopToCreateIntConstantSynonym(
const protobufs::TransformationAddLoopToCreateIntConstantSynonym&
message)
: message_(message) {}
TransformationAddLoopToCreateIntConstantSynonym::
TransformationAddLoopToCreateIntConstantSynonym(
uint32_t constant_id, uint32_t initial_val_id, uint32_t step_val_id,
uint32_t num_iterations_id, uint32_t block_after_loop_id,
uint32_t syn_id, uint32_t loop_id, uint32_t ctr_id, uint32_t temp_id,
uint32_t eventual_syn_id, uint32_t incremented_ctr_id, uint32_t cond_id,
uint32_t additional_block_id) {
message_.set_constant_id(constant_id);
message_.set_initial_val_id(initial_val_id);
message_.set_step_val_id(step_val_id);
message_.set_num_iterations_id(num_iterations_id);
message_.set_block_after_loop_id(block_after_loop_id);
message_.set_syn_id(syn_id);
message_.set_loop_id(loop_id);
message_.set_ctr_id(ctr_id);
message_.set_temp_id(temp_id);
message_.set_eventual_syn_id(eventual_syn_id);
message_.set_incremented_ctr_id(incremented_ctr_id);
message_.set_cond_id(cond_id);
message_.set_additional_block_id(additional_block_id);
}
bool TransformationAddLoopToCreateIntConstantSynonym::IsApplicable(
opt::IRContext* ir_context,
const TransformationContext& transformation_context) const {
// Check that |message_.constant_id|, |message_.initial_val_id| and
// |message_.step_val_id| are existing constants.
auto constant = ir_context->get_constant_mgr()->FindDeclaredConstant(
message_.constant_id());
auto initial_val = ir_context->get_constant_mgr()->FindDeclaredConstant(
message_.initial_val_id());
auto step_val = ir_context->get_constant_mgr()->FindDeclaredConstant(
message_.step_val_id());
if (!constant || !initial_val || !step_val) {
return false;
}
// Check that the type of |constant| is integer scalar or vector with integer
// components.
if (!constant->AsIntConstant() &&
(!constant->AsVectorConstant() ||
!constant->type()->AsVector()->element_type()->AsInteger())) {
return false;
}
// Check that the component bit width of |constant| is <= 64.
// Consider the width of the constant if it is an integer, of a single
// component if it is a vector.
uint32_t bit_width =
constant->AsIntConstant()
? constant->type()->AsInteger()->width()
: constant->type()->AsVector()->element_type()->AsInteger()->width();
if (bit_width > 64) {
return false;
}
auto constant_def =
ir_context->get_def_use_mgr()->GetDef(message_.constant_id());
auto initial_val_def =
ir_context->get_def_use_mgr()->GetDef(message_.initial_val_id());
auto step_val_def =
ir_context->get_def_use_mgr()->GetDef(message_.step_val_id());
// Check that |constant|, |initial_val| and |step_val| have the same type,
// with possibly different signedness.
if (!fuzzerutil::TypesAreEqualUpToSign(ir_context, constant_def->type_id(),
initial_val_def->type_id()) ||
!fuzzerutil::TypesAreEqualUpToSign(ir_context, constant_def->type_id(),
step_val_def->type_id())) {
return false;
}
// |message_.num_iterations_id| is an integer constant with bit width 32.
auto num_iterations = ir_context->get_constant_mgr()->FindDeclaredConstant(
message_.num_iterations_id());
if (!num_iterations || !num_iterations->AsIntConstant() ||
num_iterations->type()->AsInteger()->width() != 32) {
return false;
}
// Check that the number of iterations is > 0 and <= 32.
uint32_t num_iterations_value =
num_iterations->AsIntConstant()->GetU32BitValue();
if (num_iterations_value == 0 || num_iterations_value > kMaxNumOfIterations) {
return false;
}
// Check that the module contains 32-bit signed integer scalar constants of
// value 0 and 1.
if (!fuzzerutil::MaybeGetIntegerConstant(ir_context, transformation_context,
{0}, 32, true, false)) {
return false;
}
if (!fuzzerutil::MaybeGetIntegerConstant(ir_context, transformation_context,
{1}, 32, true, false)) {
return false;
}
// Check that the module contains the Bool type.
if (!fuzzerutil::MaybeGetBoolType(ir_context)) {
return false;
}
// Check that the equation C = I - S * N is satisfied.
// Collect the components in vectors (if the constants are scalars, these
// vectors will contain the constants themselves).
std::vector<const opt::analysis::Constant*> c_components;
std::vector<const opt::analysis::Constant*> i_components;
std::vector<const opt::analysis::Constant*> s_components;
if (constant->AsIntConstant()) {
c_components.emplace_back(constant);
i_components.emplace_back(initial_val);
s_components.emplace_back(step_val);
} else {
// It is a vector: get all the components.
c_components = constant->AsVectorConstant()->GetComponents();
i_components = initial_val->AsVectorConstant()->GetComponents();
s_components = step_val->AsVectorConstant()->GetComponents();
}
// Check the value of the components satisfy the equation.
for (uint32_t i = 0; i < c_components.size(); i++) {
// Use 64-bits integers to be able to handle constants of any width <= 64.
uint64_t c_value = c_components[i]->AsIntConstant()->GetZeroExtendedValue();
uint64_t i_value = i_components[i]->AsIntConstant()->GetZeroExtendedValue();
uint64_t s_value = s_components[i]->AsIntConstant()->GetZeroExtendedValue();
uint64_t result = i_value - s_value * num_iterations_value;
// Use bit shifts to ignore the first bits in excess (if there are any). By
// shifting left, we discard the first |64 - bit_width| bits. By shifting
// right, we move the bits back to their correct position.
result = (result << (64 - bit_width)) >> (64 - bit_width);
if (c_value != result) {
return false;
}
}
// Check that |message_.block_after_loop_id| is the label of a block.
auto block =
fuzzerutil::MaybeFindBlock(ir_context, message_.block_after_loop_id());
// Check that the block exists and has a single predecessor.
if (!block || ir_context->cfg()->preds(block->id()).size() != 1) {
return false;
}
// Check that the block is not a merge block.
if (ir_context->GetStructuredCFGAnalysis()->IsMergeBlock(block->id())) {
return false;
}
// Check that the block is not a continue block.
if (ir_context->GetStructuredCFGAnalysis()->IsContinueBlock(block->id())) {
return false;
}
// Check that the block is not a loop header.
if (block->IsLoopHeader()) {
return false;
}
// Check all the fresh ids.
std::set<uint32_t> fresh_ids_used;
for (uint32_t id : {message_.syn_id(), message_.loop_id(), message_.ctr_id(),
message_.temp_id(), message_.eventual_syn_id(),
message_.incremented_ctr_id(), message_.cond_id()}) {
if (!id || !CheckIdIsFreshAndNotUsedByThisTransformation(id, ir_context,
&fresh_ids_used)) {
return false;
}
}
// Check the additional block id if it is non-zero.
return !message_.additional_block_id() ||
CheckIdIsFreshAndNotUsedByThisTransformation(
message_.additional_block_id(), ir_context, &fresh_ids_used);
}
void TransformationAddLoopToCreateIntConstantSynonym::Apply(
opt::IRContext* ir_context,
TransformationContext* transformation_context) const {
// Find 32-bit signed integer constants 0 and 1.
uint32_t const_0_id = fuzzerutil::MaybeGetIntegerConstant(
ir_context, *transformation_context, {0}, 32, true, false);
auto const_0_def = ir_context->get_def_use_mgr()->GetDef(const_0_id);
uint32_t const_1_id = fuzzerutil::MaybeGetIntegerConstant(
ir_context, *transformation_context, {1}, 32, true, false);
// Retrieve the instruction defining the initial value constant.
auto initial_val_def =
ir_context->get_def_use_mgr()->GetDef(message_.initial_val_id());
// Retrieve the block before which we want to insert the loop.
auto block_after_loop =
ir_context->get_instr_block(message_.block_after_loop_id());
// Find the predecessor of the block.
uint32_t pred_id =
ir_context->cfg()->preds(message_.block_after_loop_id())[0];
// Get the id for the last block in the new loop. It will be
// |message_.additional_block_id| if this is non_zero, |message_.loop_id|
// otherwise.
uint32_t last_loop_block_id = message_.additional_block_id()
? message_.additional_block_id()
: message_.loop_id();
// Create the loop header block.
std::unique_ptr<opt::BasicBlock> loop_block =
MakeUnique<opt::BasicBlock>(MakeUnique<opt::Instruction>(
ir_context, SpvOpLabel, 0, message_.loop_id(),
opt::Instruction::OperandList{}));
// Add OpPhi instructions to retrieve the current value of the counter and of
// the temporary variable that will be decreased at each operation.
loop_block->AddInstruction(MakeUnique<opt::Instruction>(
ir_context, SpvOpPhi, const_0_def->type_id(), message_.ctr_id(),
opt::Instruction::OperandList{
{SPV_OPERAND_TYPE_ID, {const_0_id}},
{SPV_OPERAND_TYPE_ID, {pred_id}},
{SPV_OPERAND_TYPE_ID, {message_.incremented_ctr_id()}},
{SPV_OPERAND_TYPE_ID, {last_loop_block_id}}}));
loop_block->AddInstruction(MakeUnique<opt::Instruction>(
ir_context, SpvOpPhi, initial_val_def->type_id(), message_.temp_id(),
opt::Instruction::OperandList{
{SPV_OPERAND_TYPE_ID, {message_.initial_val_id()}},
{SPV_OPERAND_TYPE_ID, {pred_id}},
{SPV_OPERAND_TYPE_ID, {message_.eventual_syn_id()}},
{SPV_OPERAND_TYPE_ID, {last_loop_block_id}}}));
// Collect the other instructions in a list. These will be added to an
// additional block if |message_.additional_block_id| is defined, to the loop
// header otherwise.
std::vector<std::unique_ptr<opt::Instruction>> other_instructions;
// Add an instruction to subtract the step value from the temporary value.
// The value of this id will converge to the constant in the last iteration.
other_instructions.push_back(MakeUnique<opt::Instruction>(
ir_context, SpvOpISub, initial_val_def->type_id(),
message_.eventual_syn_id(),
opt::Instruction::OperandList{
{SPV_OPERAND_TYPE_ID, {message_.temp_id()}},
{SPV_OPERAND_TYPE_ID, {message_.step_val_id()}}}));
// Add an instruction to increment the counter.
other_instructions.push_back(MakeUnique<opt::Instruction>(
ir_context, SpvOpIAdd, const_0_def->type_id(),
message_.incremented_ctr_id(),
opt::Instruction::OperandList{{SPV_OPERAND_TYPE_ID, {message_.ctr_id()}},
{SPV_OPERAND_TYPE_ID, {const_1_id}}}));
// Add an instruction to decide whether the condition holds.
other_instructions.push_back(MakeUnique<opt::Instruction>(
ir_context, SpvOpSLessThan, fuzzerutil::MaybeGetBoolType(ir_context),
message_.cond_id(),
opt::Instruction::OperandList{
{SPV_OPERAND_TYPE_ID, {message_.incremented_ctr_id()}},
{SPV_OPERAND_TYPE_ID, {message_.num_iterations_id()}}}));
// Define the OpLoopMerge instruction for the loop header. The merge block is
// the existing block, the continue block is the last block in the loop
// (either the loop itself or the additional block).
std::unique_ptr<opt::Instruction> merge_inst = MakeUnique<opt::Instruction>(
ir_context, SpvOpLoopMerge, 0, 0,
opt::Instruction::OperandList{
{SPV_OPERAND_TYPE_ID, {message_.block_after_loop_id()}},
{SPV_OPERAND_TYPE_ID, {last_loop_block_id}},
{SPV_OPERAND_TYPE_LOOP_CONTROL, {SpvLoopControlMaskNone}}});
// Define a conditional branch instruction, branching to the loop header if
// the condition holds, and to the existing block otherwise. This instruction
// will be added to the last block in the loop.
std::unique_ptr<opt::Instruction> conditional_branch =
MakeUnique<opt::Instruction>(
ir_context, SpvOpBranchConditional, 0, 0,
opt::Instruction::OperandList{
{SPV_OPERAND_TYPE_ID, {message_.cond_id()}},
{SPV_OPERAND_TYPE_ID, {message_.loop_id()}},
{SPV_OPERAND_TYPE_ID, {message_.block_after_loop_id()}}});
if (message_.additional_block_id()) {
// If an id for the additional block is specified, create an additional
// block, containing the instructions in the list and a branching
// instruction.
std::unique_ptr<opt::BasicBlock> additional_block =
MakeUnique<opt::BasicBlock>(MakeUnique<opt::Instruction>(
ir_context, SpvOpLabel, 0, message_.additional_block_id(),
opt::Instruction::OperandList{}));
for (auto& instruction : other_instructions) {
additional_block->AddInstruction(std::move(instruction));
}
additional_block->AddInstruction(std::move(conditional_branch));
// Add the merge instruction to the header.
loop_block->AddInstruction(std::move(merge_inst));
// Add an unconditional branch from the header to the additional block.
loop_block->AddInstruction(MakeUnique<opt::Instruction>(
ir_context, SpvOpBranch, 0, 0,
opt::Instruction::OperandList{
{SPV_OPERAND_TYPE_ID, {message_.additional_block_id()}}}));
// Insert the two loop blocks before the existing block.
block_after_loop->GetParent()->InsertBasicBlockBefore(std::move(loop_block),
block_after_loop);
block_after_loop->GetParent()->InsertBasicBlockBefore(
std::move(additional_block), block_after_loop);
} else {
// If no id for an additional block is specified, the loop will only be made
// up of one block, so we need to add all the instructions to it.
for (auto& instruction : other_instructions) {
loop_block->AddInstruction(std::move(instruction));
}
// Add the merge and conditional branch instructions.
loop_block->AddInstruction(std::move(merge_inst));
loop_block->AddInstruction(std::move(conditional_branch));
// Insert the header before the existing block.
block_after_loop->GetParent()->InsertBasicBlockBefore(std::move(loop_block),
block_after_loop);
}
// Update the branching instructions leading to this block.
ir_context->get_def_use_mgr()->ForEachUse(
message_.block_after_loop_id(),
[this](opt::Instruction* instruction, uint32_t operand_index) {
assert(instruction->opcode() != SpvOpLoopMerge &&
instruction->opcode() != SpvOpSelectionMerge &&
instruction->opcode() != SpvOpSwitch &&
"The block should not be referenced by OpLoopMerge, "
"OpSelectionMerge or OpSwitch instructions, by construction.");
// Replace all uses of the label inside branch instructions.
if (instruction->opcode() == SpvOpBranch ||
instruction->opcode() == SpvOpBranchConditional) {
instruction->SetOperand(operand_index, {message_.loop_id()});
}
});
// Update all the OpPhi instructions in the block after the loop: its
// predecessor is now the last block in the loop.
block_after_loop->ForEachPhiInst(
[last_loop_block_id](opt::Instruction* phi_inst) {
// Since the block only had one predecessor, the id of the predecessor
// is input operand 1.
phi_inst->SetInOperand(1, {last_loop_block_id});
});
// Add a new OpPhi instruction at the beginning of the block after the loop,
// defining the synonym of the constant. The type id will be the same as
// |message_.initial_value_id|, since this is the value that is decremented in
// the loop.
block_after_loop->begin()->InsertBefore(MakeUnique<opt::Instruction>(
ir_context, SpvOpPhi, initial_val_def->type_id(), message_.syn_id(),
opt::Instruction::OperandList{
{SPV_OPERAND_TYPE_ID, {message_.eventual_syn_id()}},
{SPV_OPERAND_TYPE_ID, {last_loop_block_id}}}));
// Update the module id bound with all the fresh ids used.
for (uint32_t id : {message_.syn_id(), message_.loop_id(), message_.ctr_id(),
message_.temp_id(), message_.eventual_syn_id(),
message_.incremented_ctr_id(), message_.cond_id(),
message_.cond_id(), message_.additional_block_id()}) {
fuzzerutil::UpdateModuleIdBound(ir_context, id);
}
// Since we changed the structure of the module, we need to invalidate all the
// analyses.
ir_context->InvalidateAnalysesExceptFor(opt::IRContext::kAnalysisNone);
// Record that |message_.syn_id| is synonymous with |message_.constant_id|.
transformation_context->GetFactManager()->AddFactDataSynonym(
MakeDataDescriptor(message_.syn_id(), {}),
MakeDataDescriptor(message_.constant_id(), {}), ir_context);
}
protobufs::Transformation
TransformationAddLoopToCreateIntConstantSynonym::ToMessage() const {
protobufs::Transformation result;
*result.mutable_add_loop_to_create_int_constant_synonym() = message_;
return result;
}
} // namespace fuzz
} // namespace spvtools

View File

@ -0,0 +1,69 @@
// Copyright (c) 2020 Google LLC
//
// 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_ADD_LOOP_TO_CREATE_INT_CONSTANT_SYNONYM_H_
#define SOURCE_FUZZ_TRANSFORMATION_ADD_LOOP_TO_CREATE_INT_CONSTANT_SYNONYM_H_
#include "source/fuzz/transformation.h"
namespace spvtools {
namespace fuzz {
class TransformationAddLoopToCreateIntConstantSynonym : public Transformation {
public:
explicit TransformationAddLoopToCreateIntConstantSynonym(
const protobufs::TransformationAddLoopToCreateIntConstantSynonym&
message);
TransformationAddLoopToCreateIntConstantSynonym(
uint32_t constant_id, uint32_t initial_val_id, uint32_t step_val_id,
uint32_t num_iterations_id, uint32_t block_after_loop_id, uint32_t syn_id,
uint32_t loop_id, uint32_t ctr_id, uint32_t temp_id,
uint32_t eventual_syn_id, uint32_t incremented_ctr_id, uint32_t cond_id,
uint32_t additional_block_id);
// - |message_.constant_id|, |message_.initial_value_id|,
// |message_.step_val_id| are integer constants (scalar or vectors) with the
// same type (with possibly different signedness, but same bit width, which
// must be <= 64). Let their value be C, I, S respectively.
// - |message_.num_iterations_id| is a 32-bit integer scalar constant, with
// value N > 0 and N <= 32.
// - The module contains 32-bit signed integer scalar constants of values 0
// and 1.
// - The module contains the boolean type.
// - C = I - S * N
// - |message_.block_after_loop_id| is the label of a block which has a single
// predecessor and which is not a merge block, a continue block or a loop
// header.
// - |message_.additional_block_id| is either 0 or a valid fresh id, distinct
// from the other fresh ids.
// - All of the other parameters are valid fresh ids.
bool IsApplicable(
opt::IRContext* ir_context,
const TransformationContext& transformation_context) const override;
// Adds a loop to the module, defining a synonym of an integer (scalar or
// vector) constant. This id is marked as synonym with the original constant.
void Apply(opt::IRContext* ir_context,
TransformationContext* transformation_context) const override;
protobufs::Transformation ToMessage() const override;
private:
protobufs::TransformationAddLoopToCreateIntConstantSynonym message_;
};
} // namespace fuzz
} // namespace spvtools
#endif // SOURCE_FUZZ_TRANSFORMATION_ADD_LOOP_TO_CREATE_INT_CONSTANT_SYNONYM_H_

View File

@ -46,6 +46,7 @@ if (${SPIRV_BUILD_FUZZER})
transformation_add_image_sample_unused_components_test.cpp
transformation_add_local_variable_test.cpp
transformation_add_loop_preheader_test.cpp
transformation_add_loop_to_create_int_constant_synonym_test.cpp
transformation_add_no_contraction_decoration_test.cpp
transformation_add_opphi_synonym_test.cpp
transformation_add_parameter_test.cpp

View File

@ -0,0 +1,957 @@
// Copyright (c) 2020 Google LLC
//
// 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_add_loop_to_create_int_constant_synonym.h"
#include "test/fuzz/fuzz_test_util.h"
namespace spvtools {
namespace fuzz {
namespace {
TEST(TransformationAddLoopToCreateIntConstantSynonymTest,
ConstantsNotSuitable) {
std::string shader = R"(
OpCapability Shader
OpCapability Int64
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %2 "main"
OpExecutionMode %2 OriginUpperLeft
OpSource ESSL 310
OpName %2 "main"
%3 = OpTypeVoid
%4 = OpTypeFunction %3
%36 = OpTypeBool
%5 = OpTypeInt 32 1
%6 = OpConstant %5 -1
%7 = OpConstant %5 0
%8 = OpConstant %5 1
%9 = OpConstant %5 2
%10 = OpConstant %5 5
%11 = OpConstant %5 10
%12 = OpConstant %5 20
%13 = OpConstant %5 33
%14 = OpTypeVector %5 2
%15 = OpConstantComposite %14 %10 %11
%16 = OpConstantComposite %14 %12 %12
%17 = OpTypeVector %5 3
%18 = OpConstantComposite %17 %11 %7 %11
%19 = OpTypeInt 64 1
%20 = OpConstant %19 0
%21 = OpConstant %19 10
%22 = OpTypeVector %19 2
%23 = OpConstantComposite %22 %21 %20
%24 = OpTypeFloat 32
%25 = OpConstant %24 0
%26 = OpConstant %24 5
%27 = OpConstant %24 10
%28 = OpConstant %24 20
%29 = OpTypeVector %24 3
%30 = OpConstantComposite %29 %26 %27 %26
%31 = OpConstantComposite %29 %28 %28 %28
%32 = OpConstantComposite %29 %27 %25 %27
%2 = OpFunction %3 None %4
%33 = OpLabel
%34 = OpCopyObject %5 %11
OpBranch %35
%35 = OpLabel
OpReturn
OpFunctionEnd
)";
const auto env = SPV_ENV_UNIVERSAL_1_5;
const auto consumer = nullptr;
const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
ASSERT_TRUE(IsValid(env, context.get()));
FactManager fact_manager;
spvtools::ValidatorOptions validator_options;
TransformationContext transformation_context(&fact_manager,
validator_options);
// Reminder: the first four parameters of the constructor are the constants
// with values for C, I, S, N respectively.
// %70 does not correspond to an id in the module.
ASSERT_FALSE(TransformationAddLoopToCreateIntConstantSynonym(
70, 12, 10, 9, 35, 100, 101, 102, 103, 104, 105, 106, 107)
.IsApplicable(context.get(), transformation_context));
// %35 is not a constant.
ASSERT_FALSE(TransformationAddLoopToCreateIntConstantSynonym(
35, 12, 10, 9, 35, 100, 101, 102, 103, 104, 105, 106, 107)
.IsApplicable(context.get(), transformation_context));
// %27, %28 and %26 are not integer constants, but scalar floats.
ASSERT_FALSE(TransformationAddLoopToCreateIntConstantSynonym(
27, 28, 26, 9, 35, 100, 101, 102, 103, 104, 105, 106, 107)
.IsApplicable(context.get(), transformation_context));
// %32, %31 and %30 are not integer constants, but vector floats.
ASSERT_FALSE(TransformationAddLoopToCreateIntConstantSynonym(
32, 31, 30, 9, 35, 100, 101, 102, 103, 104, 105, 106, 107)
.IsApplicable(context.get(), transformation_context));
// %18=(10, 0, 10) has 3 components, while %16=(20, 20) and %15=(5, 10)
// have 2.
ASSERT_FALSE(TransformationAddLoopToCreateIntConstantSynonym(
18, 16, 15, 9, 35, 100, 101, 102, 103, 104, 105, 106, 107)
.IsApplicable(context.get(), transformation_context));
// %21 has bit width 64, while the width of %12 and %10 is 32.
ASSERT_FALSE(TransformationAddLoopToCreateIntConstantSynonym(
21, 12, 10, 9, 35, 100, 101, 102, 103, 104, 105, 106, 107)
.IsApplicable(context.get(), transformation_context));
// %13 has component width 64, while the component width of %16 and %15 is 32.
ASSERT_FALSE(TransformationAddLoopToCreateIntConstantSynonym(
13, 16, 15, 9, 35, 100, 101, 102, 103, 104, 105, 106, 107)
.IsApplicable(context.get(), transformation_context));
// %21 (N) is a 64-bit integer, not 32-bit.
ASSERT_FALSE(TransformationAddLoopToCreateIntConstantSynonym(
7, 7, 7, 21, 35, 100, 101, 102, 103, 104, 105, 106, 107)
.IsApplicable(context.get(), transformation_context));
// %7 (N) has value 0, so N <= 0.
ASSERT_FALSE(TransformationAddLoopToCreateIntConstantSynonym(
7, 7, 7, 7, 35, 100, 101, 102, 103, 104, 105, 106, 107)
.IsApplicable(context.get(), transformation_context));
// %6 (N) has value -1, so N <= 1.
ASSERT_FALSE(TransformationAddLoopToCreateIntConstantSynonym(
7, 7, 7, 6, 35, 100, 101, 102, 103, 104, 105, 106, 107)
.IsApplicable(context.get(), transformation_context));
// %13 (N) has value 33, so N > 32.
ASSERT_FALSE(TransformationAddLoopToCreateIntConstantSynonym(
7, 7, 7, 6, 13, 100, 101, 102, 103, 104, 105, 106, 107)
.IsApplicable(context.get(), transformation_context));
// C(%11)=10, I(%12)=20, S(%10)=5, N(%8)=1, so C=I-S*N does not hold, as
// 20-5*1=15.
ASSERT_FALSE(TransformationAddLoopToCreateIntConstantSynonym(
11, 12, 10, 8, 35, 100, 101, 102, 103, 104, 105, 106, 107)
.IsApplicable(context.get(), transformation_context));
// C(%15)=(5, 10), I(%16)=(20, 20), S(%15)=(5, 10), N(%8)=1, so C=I-S*N does
// not hold, as (20, 20)-1*(5, 10) = (15, 10).
ASSERT_FALSE(TransformationAddLoopToCreateIntConstantSynonym(
15, 16, 15, 8, 35, 100, 101, 102, 103, 104, 105, 106, 107)
.IsApplicable(context.get(), transformation_context));
}
TEST(TransformationAddLoopToCreateIntConstantSynonymTest,
MissingConstantsOrBoolType) {
{
// The shader is missing the boolean type.
std::string shader = R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %2 "main"
OpExecutionMode %2 OriginUpperLeft
OpSource ESSL 310
%3 = OpTypeVoid
%4 = OpTypeFunction %3
%5 = OpTypeInt 32 1
%20 = OpConstant %5 0
%6 = OpConstant %5 1
%7 = OpConstant %5 2
%8 = OpConstant %5 5
%9 = OpConstant %5 10
%10 = OpConstant %5 20
%2 = OpFunction %3 None %4
%11 = OpLabel
OpBranch %12
%12 = OpLabel
OpReturn
OpFunctionEnd
)";
const auto env = SPV_ENV_UNIVERSAL_1_5;
const auto consumer = nullptr;
const auto context =
BuildModule(env, consumer, shader, kFuzzAssembleOption);
ASSERT_TRUE(IsValid(env, context.get()));
FactManager fact_manager;
spvtools::ValidatorOptions validator_options;
TransformationContext transformation_context(&fact_manager,
validator_options);
ASSERT_FALSE(TransformationAddLoopToCreateIntConstantSynonym(
9, 10, 8, 7, 12, 100, 101, 102, 103, 104, 105, 106, 107)
.IsApplicable(context.get(), transformation_context));
}
{
// The shader is missing a 32-bit integer 0 constant.
std::string shader = R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %2 "main"
OpExecutionMode %2 OriginUpperLeft
OpSource ESSL 310
%3 = OpTypeVoid
%4 = OpTypeFunction %3
%20 = OpTypeBool
%5 = OpTypeInt 32 1
%6 = OpConstant %5 1
%7 = OpConstant %5 2
%8 = OpConstant %5 5
%9 = OpConstant %5 10
%10 = OpConstant %5 20
%2 = OpFunction %3 None %4
%11 = OpLabel
OpBranch %12
%12 = OpLabel
OpReturn
OpFunctionEnd
)";
const auto env = SPV_ENV_UNIVERSAL_1_5;
const auto consumer = nullptr;
const auto context =
BuildModule(env, consumer, shader, kFuzzAssembleOption);
ASSERT_TRUE(IsValid(env, context.get()));
FactManager fact_manager;
spvtools::ValidatorOptions validator_options;
TransformationContext transformation_context(&fact_manager,
validator_options);
ASSERT_FALSE(TransformationAddLoopToCreateIntConstantSynonym(
9, 10, 8, 7, 12, 100, 101, 102, 103, 104, 105, 106, 107)
.IsApplicable(context.get(), transformation_context));
}
{
// The shader is missing a 32-bit integer 1 constant.
std::string shader = R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %2 "main"
OpExecutionMode %2 OriginUpperLeft
OpSource ESSL 310
%3 = OpTypeVoid
%4 = OpTypeFunction %3
%20 = OpTypeBool
%5 = OpTypeInt 32 1
%6 = OpConstant %5 0
%7 = OpConstant %5 2
%8 = OpConstant %5 5
%9 = OpConstant %5 10
%10 = OpConstant %5 20
%2 = OpFunction %3 None %4
%11 = OpLabel
OpBranch %12
%12 = OpLabel
OpReturn
OpFunctionEnd
)";
const auto env = SPV_ENV_UNIVERSAL_1_5;
const auto consumer = nullptr;
const auto context =
BuildModule(env, consumer, shader, kFuzzAssembleOption);
ASSERT_TRUE(IsValid(env, context.get()));
FactManager fact_manager;
spvtools::ValidatorOptions validator_options;
TransformationContext transformation_context(&fact_manager,
validator_options);
ASSERT_FALSE(TransformationAddLoopToCreateIntConstantSynonym(
9, 10, 8, 7, 12, 100, 101, 102, 103, 104, 105, 106, 107)
.IsApplicable(context.get(), transformation_context));
}
}
TEST(TransformationAddLoopToCreateIntConstantSynonymTest, Simple) {
std::string shader = R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %2 "main"
OpExecutionMode %2 OriginUpperLeft
OpSource ESSL 310
%3 = OpTypeVoid
%4 = OpTypeFunction %3
%5 = OpTypeBool
%6 = OpConstantTrue %5
%7 = OpTypeInt 32 1
%8 = OpConstant %7 0
%9 = OpConstant %7 1
%10 = OpConstant %7 2
%11 = OpConstant %7 5
%12 = OpConstant %7 10
%13 = OpConstant %7 20
%2 = OpFunction %3 None %4
%14 = OpLabel
OpBranch %15
%15 = OpLabel
%22 = OpPhi %7 %12 %14
OpSelectionMerge %16 None
OpBranchConditional %6 %17 %18
%17 = OpLabel
%23 = OpPhi %7 %13 %15
OpBranch %18
%18 = OpLabel
OpBranch %16
%16 = OpLabel
OpBranch %19
%19 = OpLabel
OpLoopMerge %20 %19 None
OpBranchConditional %6 %20 %19
%20 = OpLabel
OpBranch %21
%21 = OpLabel
OpBranch %24
%24 = OpLabel
OpLoopMerge %27 %25 None
OpBranch %25
%25 = OpLabel
OpBranch %26
%26 = OpLabel
OpBranchConditional %6 %24 %27
%27 = OpLabel
OpReturn
OpFunctionEnd
)";
const auto env = SPV_ENV_UNIVERSAL_1_5;
const auto consumer = nullptr;
const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
ASSERT_TRUE(IsValid(env, context.get()));
FactManager fact_manager;
spvtools::ValidatorOptions validator_options;
TransformationContext transformation_context(&fact_manager,
validator_options);
// Block %14 has no predecessors.
ASSERT_FALSE(TransformationAddLoopToCreateIntConstantSynonym(
12, 13, 11, 10, 14, 100, 101, 102, 103, 104, 105, 106, 107)
.IsApplicable(context.get(), transformation_context));
// Block %18 has more than one predecessor.
ASSERT_FALSE(TransformationAddLoopToCreateIntConstantSynonym(
12, 13, 11, 10, 18, 100, 101, 102, 103, 104, 105, 106, 107)
.IsApplicable(context.get(), transformation_context));
// Block %16 is a merge block.
ASSERT_FALSE(TransformationAddLoopToCreateIntConstantSynonym(
12, 13, 11, 10, 16, 100, 101, 102, 103, 104, 105, 106, 107)
.IsApplicable(context.get(), transformation_context));
// Block %25 is a continue block.
ASSERT_FALSE(TransformationAddLoopToCreateIntConstantSynonym(
12, 13, 11, 10, 25, 100, 101, 102, 103, 104, 105, 106, 107)
.IsApplicable(context.get(), transformation_context));
// Block %19 has more than one predecessor.
ASSERT_FALSE(TransformationAddLoopToCreateIntConstantSynonym(
12, 13, 11, 10, 19, 100, 101, 102, 103, 104, 105, 106, 107)
.IsApplicable(context.get(), transformation_context));
// Block %20 is a merge block.
ASSERT_FALSE(TransformationAddLoopToCreateIntConstantSynonym(
12, 13, 11, 10, 20, 100, 101, 102, 103, 104, 105, 106, 107)
.IsApplicable(context.get(), transformation_context));
// Id %20 is supposed to be fresh, but it is not.
ASSERT_FALSE(TransformationAddLoopToCreateIntConstantSynonym(
12, 13, 11, 10, 15, 100, 20, 102, 103, 104, 105, 106, 107)
.IsApplicable(context.get(), transformation_context));
// Id %100 is used twice.
ASSERT_FALSE(TransformationAddLoopToCreateIntConstantSynonym(
12, 13, 11, 10, 15, 100, 100, 102, 103, 104, 105, 106, 107)
.IsApplicable(context.get(), transformation_context));
// Id %100 is used twice.
ASSERT_FALSE(TransformationAddLoopToCreateIntConstantSynonym(
12, 13, 11, 10, 15, 100, 101, 102, 103, 104, 105, 106, 100)
.IsApplicable(context.get(), transformation_context));
// Only the last id (for the additional block) is optional, so the other ones
// cannot be 0.
ASSERT_FALSE(TransformationAddLoopToCreateIntConstantSynonym(
12, 13, 11, 10, 15, 0, 101, 102, 103, 104, 105, 106, 100)
.IsApplicable(context.get(), transformation_context));
// This transformation will create a synonym of constant %12 from a 1-block
// loop.
auto transformation1 = TransformationAddLoopToCreateIntConstantSynonym(
12, 13, 11, 10, 15, 100, 101, 102, 103, 104, 105, 106, 0);
ASSERT_TRUE(
transformation1.IsApplicable(context.get(), transformation_context));
transformation1.Apply(context.get(), &transformation_context);
ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
MakeDataDescriptor(12, {}), MakeDataDescriptor(100, {})));
ASSERT_TRUE(IsValid(env, context.get()));
// This transformation will create a synonym of constant %12 from a 2-block
// loop.
auto transformation2 = TransformationAddLoopToCreateIntConstantSynonym(
12, 13, 11, 10, 17, 107, 108, 109, 110, 111, 112, 113, 114);
ASSERT_TRUE(
transformation2.IsApplicable(context.get(), transformation_context));
transformation2.Apply(context.get(), &transformation_context);
ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
MakeDataDescriptor(12, {}), MakeDataDescriptor(107, {})));
ASSERT_TRUE(IsValid(env, context.get()));
// This transformation will create a synonym of constant %12 from a 2-block
// loop.
auto transformation3 = TransformationAddLoopToCreateIntConstantSynonym(
12, 13, 11, 10, 26, 115, 116, 117, 118, 119, 120, 121, 0);
ASSERT_TRUE(
transformation3.IsApplicable(context.get(), transformation_context));
transformation3.Apply(context.get(), &transformation_context);
ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
MakeDataDescriptor(12, {}), MakeDataDescriptor(115, {})));
ASSERT_TRUE(IsValid(env, context.get()));
std::string after_transformations = R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %2 "main"
OpExecutionMode %2 OriginUpperLeft
OpSource ESSL 310
%3 = OpTypeVoid
%4 = OpTypeFunction %3
%5 = OpTypeBool
%6 = OpConstantTrue %5
%7 = OpTypeInt 32 1
%8 = OpConstant %7 0
%9 = OpConstant %7 1
%10 = OpConstant %7 2
%11 = OpConstant %7 5
%12 = OpConstant %7 10
%13 = OpConstant %7 20
%2 = OpFunction %3 None %4
%14 = OpLabel
OpBranch %101
%101 = OpLabel
%102 = OpPhi %7 %8 %14 %105 %101
%103 = OpPhi %7 %13 %14 %104 %101
%104 = OpISub %7 %103 %11
%105 = OpIAdd %7 %102 %9
%106 = OpSLessThan %5 %105 %10
OpLoopMerge %15 %101 None
OpBranchConditional %106 %101 %15
%15 = OpLabel
%100 = OpPhi %7 %104 %101
%22 = OpPhi %7 %12 %101
OpSelectionMerge %16 None
OpBranchConditional %6 %108 %18
%108 = OpLabel
%109 = OpPhi %7 %8 %15 %112 %114
%110 = OpPhi %7 %13 %15 %111 %114
OpLoopMerge %17 %114 None
OpBranch %114
%114 = OpLabel
%111 = OpISub %7 %110 %11
%112 = OpIAdd %7 %109 %9
%113 = OpSLessThan %5 %112 %10
OpBranchConditional %113 %108 %17
%17 = OpLabel
%107 = OpPhi %7 %111 %114
%23 = OpPhi %7 %13 %114
OpBranch %18
%18 = OpLabel
OpBranch %16
%16 = OpLabel
OpBranch %19
%19 = OpLabel
OpLoopMerge %20 %19 None
OpBranchConditional %6 %20 %19
%20 = OpLabel
OpBranch %21
%21 = OpLabel
OpBranch %24
%24 = OpLabel
OpLoopMerge %27 %25 None
OpBranch %25
%25 = OpLabel
OpBranch %116
%116 = OpLabel
%117 = OpPhi %7 %8 %25 %120 %116
%118 = OpPhi %7 %13 %25 %119 %116
%119 = OpISub %7 %118 %11
%120 = OpIAdd %7 %117 %9
%121 = OpSLessThan %5 %120 %10
OpLoopMerge %26 %116 None
OpBranchConditional %121 %116 %26
%26 = OpLabel
%115 = OpPhi %7 %119 %116
OpBranchConditional %6 %24 %27
%27 = OpLabel
OpReturn
OpFunctionEnd
)";
ASSERT_TRUE(IsEqual(env, after_transformations, context.get()));
}
TEST(TransformationAddLoopToCreateIntConstantSynonymTest,
DifferentSignednessAndVectors) {
std::string shader = R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %2 "main"
OpExecutionMode %2 OriginUpperLeft
OpSource ESSL 310
%3 = OpTypeVoid
%4 = OpTypeFunction %3
%5 = OpTypeBool
%6 = OpConstantTrue %5
%7 = OpTypeInt 32 1
%8 = OpConstant %7 0
%9 = OpConstant %7 1
%10 = OpConstant %7 2
%11 = OpConstant %7 5
%12 = OpConstant %7 10
%13 = OpConstant %7 20
%14 = OpTypeInt 32 0
%15 = OpConstant %14 0
%16 = OpConstant %14 5
%17 = OpConstant %14 10
%18 = OpConstant %14 20
%19 = OpTypeVector %7 2
%20 = OpTypeVector %14 2
%21 = OpConstantComposite %19 %12 %8
%22 = OpConstantComposite %20 %17 %15
%23 = OpConstantComposite %19 %13 %12
%24 = OpConstantComposite %19 %11 %11
%2 = OpFunction %3 None %4
%25 = OpLabel
OpBranch %26
%26 = OpLabel
OpBranch %27
%27 = OpLabel
OpBranch %28
%28 = OpLabel
OpBranch %29
%29 = OpLabel
OpBranch %30
%30 = OpLabel
OpReturn
OpFunctionEnd
)";
const auto env = SPV_ENV_UNIVERSAL_1_5;
const auto consumer = nullptr;
const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
ASSERT_TRUE(IsValid(env, context.get()));
FactManager fact_manager;
spvtools::ValidatorOptions validator_options;
TransformationContext transformation_context(&fact_manager,
validator_options);
// These tests check that the transformation is applicable and is applied
// correctly with integers, scalar and vectors, of different signedness.
// %12 is a signed integer, %18 and %16 are unsigned integers.
auto transformation1 = TransformationAddLoopToCreateIntConstantSynonym(
12, 18, 16, 10, 26, 100, 101, 102, 103, 104, 105, 106, 0);
ASSERT_TRUE(
transformation1.IsApplicable(context.get(), transformation_context));
transformation1.Apply(context.get(), &transformation_context);
ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
MakeDataDescriptor(12, {}), MakeDataDescriptor(100, {})));
ASSERT_TRUE(IsValid(env, context.get()));
// %12 and %11 are signed integers, %18 is an unsigned integer.
auto transformation2 = TransformationAddLoopToCreateIntConstantSynonym(
12, 18, 11, 10, 27, 108, 109, 110, 111, 112, 113, 114, 0);
ASSERT_TRUE(
transformation2.IsApplicable(context.get(), transformation_context));
transformation2.Apply(context.get(), &transformation_context);
ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
MakeDataDescriptor(12, {}), MakeDataDescriptor(108, {})));
ASSERT_TRUE(IsValid(env, context.get()));
// %17, %18 and %16 are all signed integers.
auto transformation3 = TransformationAddLoopToCreateIntConstantSynonym(
17, 18, 16, 10, 28, 115, 116, 117, 118, 119, 120, 121, 0);
ASSERT_TRUE(
transformation3.IsApplicable(context.get(), transformation_context));
transformation3.Apply(context.get(), &transformation_context);
ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
MakeDataDescriptor(17, {}), MakeDataDescriptor(115, {})));
ASSERT_TRUE(IsValid(env, context.get()));
// %22 is an unsigned integer vector, %23 and %24 are signed integer vectors.
auto transformation4 = TransformationAddLoopToCreateIntConstantSynonym(
22, 23, 24, 10, 29, 122, 123, 124, 125, 126, 127, 128, 0);
ASSERT_TRUE(
transformation4.IsApplicable(context.get(), transformation_context));
transformation4.Apply(context.get(), &transformation_context);
ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
MakeDataDescriptor(22, {}), MakeDataDescriptor(122, {})));
ASSERT_TRUE(IsValid(env, context.get()));
// %21, %23 and %24 are all signed integer vectors.
auto transformation5 = TransformationAddLoopToCreateIntConstantSynonym(
21, 23, 24, 10, 30, 129, 130, 131, 132, 133, 134, 135, 0);
ASSERT_TRUE(
transformation5.IsApplicable(context.get(), transformation_context));
transformation5.Apply(context.get(), &transformation_context);
ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
MakeDataDescriptor(21, {}), MakeDataDescriptor(129, {})));
ASSERT_TRUE(IsValid(env, context.get()));
std::string after_transformations = R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %2 "main"
OpExecutionMode %2 OriginUpperLeft
OpSource ESSL 310
%3 = OpTypeVoid
%4 = OpTypeFunction %3
%5 = OpTypeBool
%6 = OpConstantTrue %5
%7 = OpTypeInt 32 1
%8 = OpConstant %7 0
%9 = OpConstant %7 1
%10 = OpConstant %7 2
%11 = OpConstant %7 5
%12 = OpConstant %7 10
%13 = OpConstant %7 20
%14 = OpTypeInt 32 0
%15 = OpConstant %14 0
%16 = OpConstant %14 5
%17 = OpConstant %14 10
%18 = OpConstant %14 20
%19 = OpTypeVector %7 2
%20 = OpTypeVector %14 2
%21 = OpConstantComposite %19 %12 %8
%22 = OpConstantComposite %20 %17 %15
%23 = OpConstantComposite %19 %13 %12
%24 = OpConstantComposite %19 %11 %11
%2 = OpFunction %3 None %4
%25 = OpLabel
OpBranch %101
%101 = OpLabel
%102 = OpPhi %7 %8 %25 %105 %101
%103 = OpPhi %14 %18 %25 %104 %101
%104 = OpISub %14 %103 %16
%105 = OpIAdd %7 %102 %9
%106 = OpSLessThan %5 %105 %10
OpLoopMerge %26 %101 None
OpBranchConditional %106 %101 %26
%26 = OpLabel
%100 = OpPhi %14 %104 %101
OpBranch %109
%109 = OpLabel
%110 = OpPhi %7 %8 %26 %113 %109
%111 = OpPhi %14 %18 %26 %112 %109
%112 = OpISub %14 %111 %11
%113 = OpIAdd %7 %110 %9
%114 = OpSLessThan %5 %113 %10
OpLoopMerge %27 %109 None
OpBranchConditional %114 %109 %27
%27 = OpLabel
%108 = OpPhi %14 %112 %109
OpBranch %116
%116 = OpLabel
%117 = OpPhi %7 %8 %27 %120 %116
%118 = OpPhi %14 %18 %27 %119 %116
%119 = OpISub %14 %118 %16
%120 = OpIAdd %7 %117 %9
%121 = OpSLessThan %5 %120 %10
OpLoopMerge %28 %116 None
OpBranchConditional %121 %116 %28
%28 = OpLabel
%115 = OpPhi %14 %119 %116
OpBranch %123
%123 = OpLabel
%124 = OpPhi %7 %8 %28 %127 %123
%125 = OpPhi %19 %23 %28 %126 %123
%126 = OpISub %19 %125 %24
%127 = OpIAdd %7 %124 %9
%128 = OpSLessThan %5 %127 %10
OpLoopMerge %29 %123 None
OpBranchConditional %128 %123 %29
%29 = OpLabel
%122 = OpPhi %19 %126 %123
OpBranch %130
%130 = OpLabel
%131 = OpPhi %7 %8 %29 %134 %130
%132 = OpPhi %19 %23 %29 %133 %130
%133 = OpISub %19 %132 %24
%134 = OpIAdd %7 %131 %9
%135 = OpSLessThan %5 %134 %10
OpLoopMerge %30 %130 None
OpBranchConditional %135 %130 %30
%30 = OpLabel
%129 = OpPhi %19 %133 %130
OpReturn
OpFunctionEnd
)";
ASSERT_TRUE(IsEqual(env, after_transformations, context.get()));
}
TEST(TransformationAddLoopToCreateIntConstantSynonymTest, 64BitConstants) {
std::string shader = R"(
OpCapability Shader
OpCapability Int64
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %2 "main"
OpExecutionMode %2 OriginUpperLeft
OpSource ESSL 310
%3 = OpTypeVoid
%4 = OpTypeFunction %3
%5 = OpTypeBool
%6 = OpConstantTrue %5
%7 = OpTypeInt 32 1
%8 = OpConstant %7 0
%9 = OpConstant %7 1
%10 = OpConstant %7 2
%11 = OpTypeInt 64 1
%12 = OpConstant %11 5
%13 = OpConstant %11 10
%14 = OpConstant %11 20
%15 = OpTypeVector %11 2
%16 = OpConstantComposite %15 %13 %13
%17 = OpConstantComposite %15 %14 %14
%18 = OpConstantComposite %15 %12 %12
%2 = OpFunction %3 None %4
%19 = OpLabel
OpBranch %20
%20 = OpLabel
OpBranch %21
%21 = OpLabel
OpReturn
OpFunctionEnd
)";
const auto env = SPV_ENV_UNIVERSAL_1_5;
const auto consumer = nullptr;
const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
ASSERT_TRUE(IsValid(env, context.get()));
FactManager fact_manager;
spvtools::ValidatorOptions validator_options;
TransformationContext transformation_context(&fact_manager,
validator_options);
// These tests check that the transformation can be applied, and is applied
// correctly, to 64-bit integer (scalar and vector) constants.
// 64-bit scalar integers.
auto transformation1 = TransformationAddLoopToCreateIntConstantSynonym(
13, 14, 12, 10, 20, 100, 101, 102, 103, 104, 105, 106, 0);
ASSERT_TRUE(
transformation1.IsApplicable(context.get(), transformation_context));
transformation1.Apply(context.get(), &transformation_context);
ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
MakeDataDescriptor(13, {}), MakeDataDescriptor(100, {})));
ASSERT_TRUE(IsValid(env, context.get()));
// 64-bit vector integers.
auto transformation2 = TransformationAddLoopToCreateIntConstantSynonym(
16, 17, 18, 10, 21, 107, 108, 109, 110, 111, 112, 113, 0);
ASSERT_TRUE(
transformation2.IsApplicable(context.get(), transformation_context));
transformation2.Apply(context.get(), &transformation_context);
ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
MakeDataDescriptor(16, {}), MakeDataDescriptor(107, {})));
ASSERT_TRUE(IsValid(env, context.get()));
std::string after_transformations = R"(
OpCapability Shader
OpCapability Int64
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %2 "main"
OpExecutionMode %2 OriginUpperLeft
OpSource ESSL 310
%3 = OpTypeVoid
%4 = OpTypeFunction %3
%5 = OpTypeBool
%6 = OpConstantTrue %5
%7 = OpTypeInt 32 1
%8 = OpConstant %7 0
%9 = OpConstant %7 1
%10 = OpConstant %7 2
%11 = OpTypeInt 64 1
%12 = OpConstant %11 5
%13 = OpConstant %11 10
%14 = OpConstant %11 20
%15 = OpTypeVector %11 2
%16 = OpConstantComposite %15 %13 %13
%17 = OpConstantComposite %15 %14 %14
%18 = OpConstantComposite %15 %12 %12
%2 = OpFunction %3 None %4
%19 = OpLabel
OpBranch %101
%101 = OpLabel
%102 = OpPhi %7 %8 %19 %105 %101
%103 = OpPhi %11 %14 %19 %104 %101
%104 = OpISub %11 %103 %12
%105 = OpIAdd %7 %102 %9
%106 = OpSLessThan %5 %105 %10
OpLoopMerge %20 %101 None
OpBranchConditional %106 %101 %20
%20 = OpLabel
%100 = OpPhi %11 %104 %101
OpBranch %108
%108 = OpLabel
%109 = OpPhi %7 %8 %20 %112 %108
%110 = OpPhi %15 %17 %20 %111 %108
%111 = OpISub %15 %110 %18
%112 = OpIAdd %7 %109 %9
%113 = OpSLessThan %5 %112 %10
OpLoopMerge %21 %108 None
OpBranchConditional %113 %108 %21
%21 = OpLabel
%107 = OpPhi %15 %111 %108
OpReturn
OpFunctionEnd
)";
ASSERT_TRUE(IsEqual(env, after_transformations, context.get()));
}
TEST(TransformationAddLoopToCreateIntConstantSynonymTest, Underflow) {
std::string shader = R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %2 "main"
OpExecutionMode %2 OriginUpperLeft
OpSource ESSL 310
%3 = OpTypeVoid
%4 = OpTypeFunction %3
%5 = OpTypeBool
%6 = OpConstantTrue %5
%7 = OpTypeInt 32 1
%8 = OpConstant %7 0
%9 = OpConstant %7 1
%10 = OpConstant %7 2
%11 = OpConstant %7 20
%12 = OpConstant %7 -4
%13 = OpTypeInt 32 0
%14 = OpConstant %13 214748365
%15 = OpConstant %13 4294967256
%2 = OpFunction %3 None %4
%16 = OpLabel
OpBranch %17
%17 = OpLabel
OpBranch %18
%18 = OpLabel
OpReturn
OpFunctionEnd
)";
const auto env = SPV_ENV_UNIVERSAL_1_5;
const auto consumer = nullptr;
const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
ASSERT_TRUE(IsValid(env, context.get()));
FactManager fact_manager;
spvtools::ValidatorOptions validator_options;
TransformationContext transformation_context(&fact_manager,
validator_options);
// These tests check that underflows are taken into consideration when
// deciding if transformation is applicable.
// Subtracting 2147483648 20 times from 32-bit integer 0 underflows 2 times
// and the result is equivalent to -4.
auto transformation1 = TransformationAddLoopToCreateIntConstantSynonym(
12, 8, 14, 11, 17, 100, 101, 102, 103, 104, 105, 106, 0);
ASSERT_TRUE(
transformation1.IsApplicable(context.get(), transformation_context));
transformation1.Apply(context.get(), &transformation_context);
ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
MakeDataDescriptor(12, {}), MakeDataDescriptor(100, {})));
ASSERT_TRUE(IsValid(env, context.get()));
// Subtracting 20 twice from 0 underflows and gives the unsigned integer
// 4294967256.
auto transformation2 = TransformationAddLoopToCreateIntConstantSynonym(
15, 8, 11, 10, 18, 107, 108, 109, 110, 111, 112, 113, 0);
ASSERT_TRUE(
transformation2.IsApplicable(context.get(), transformation_context));
transformation2.Apply(context.get(), &transformation_context);
ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
MakeDataDescriptor(15, {}), MakeDataDescriptor(107, {})));
ASSERT_TRUE(IsValid(env, context.get()));
std::string after_transformations = R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %2 "main"
OpExecutionMode %2 OriginUpperLeft
OpSource ESSL 310
%3 = OpTypeVoid
%4 = OpTypeFunction %3
%5 = OpTypeBool
%6 = OpConstantTrue %5
%7 = OpTypeInt 32 1
%8 = OpConstant %7 0
%9 = OpConstant %7 1
%10 = OpConstant %7 2
%11 = OpConstant %7 20
%12 = OpConstant %7 -4
%13 = OpTypeInt 32 0
%14 = OpConstant %13 214748365
%15 = OpConstant %13 4294967256
%2 = OpFunction %3 None %4
%16 = OpLabel
OpBranch %101
%101 = OpLabel
%102 = OpPhi %7 %8 %16 %105 %101
%103 = OpPhi %7 %8 %16 %104 %101
%104 = OpISub %7 %103 %14
%105 = OpIAdd %7 %102 %9
%106 = OpSLessThan %5 %105 %11
OpLoopMerge %17 %101 None
OpBranchConditional %106 %101 %17
%17 = OpLabel
%100 = OpPhi %7 %104 %101
OpBranch %108
%108 = OpLabel
%109 = OpPhi %7 %8 %17 %112 %108
%110 = OpPhi %7 %8 %17 %111 %108
%111 = OpISub %7 %110 %11
%112 = OpIAdd %7 %109 %9
%113 = OpSLessThan %5 %112 %10
OpLoopMerge %18 %108 None
OpBranchConditional %113 %108 %18
%18 = OpLabel
%107 = OpPhi %7 %111 %108
OpReturn
OpFunctionEnd
)";
ASSERT_TRUE(IsEqual(env, after_transformations, context.get()));
}
} // namespace
} // namespace fuzz
} // namespace spvtools