spirv-fuzz: Fuzzer passes to add local and global variables (#3175)

Adds two new fuzzer passes to add variables to a module: one that adds
Private storage class global variables, another that adds Function
storage class local variables.
This commit is contained in:
Alastair Donaldson 2020-02-05 21:07:44 +00:00 committed by GitHub
parent 9e52bc0d0c
commit 1f03ac1027
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
18 changed files with 930 additions and 5 deletions

View File

@ -40,6 +40,8 @@ if(SPIRV_BUILD_FUZZER)
fuzzer_pass_add_dead_blocks.h
fuzzer_pass_add_dead_breaks.h
fuzzer_pass_add_dead_continues.h
fuzzer_pass_add_global_variables.h
fuzzer_pass_add_local_variables.h
fuzzer_pass_add_no_contraction_decorations.h
fuzzer_pass_add_useful_constructs.h
fuzzer_pass_adjust_function_controls.h
@ -74,6 +76,7 @@ if(SPIRV_BUILD_FUZZER)
transformation_add_function.h
transformation_add_global_undef.h
transformation_add_global_variable.h
transformation_add_local_variable.h
transformation_add_no_contraction_decoration.h
transformation_add_type_array.h
transformation_add_type_boolean.h
@ -112,6 +115,8 @@ if(SPIRV_BUILD_FUZZER)
fuzzer_pass_add_dead_blocks.cpp
fuzzer_pass_add_dead_breaks.cpp
fuzzer_pass_add_dead_continues.cpp
fuzzer_pass_add_global_variables.cpp
fuzzer_pass_add_local_variables.cpp
fuzzer_pass_add_no_contraction_decorations.cpp
fuzzer_pass_add_useful_constructs.cpp
fuzzer_pass_adjust_function_controls.cpp
@ -145,6 +150,7 @@ if(SPIRV_BUILD_FUZZER)
transformation_add_function.cpp
transformation_add_global_undef.cpp
transformation_add_global_variable.cpp
transformation_add_local_variable.cpp
transformation_add_no_contraction_decoration.cpp
transformation_add_type_array.cpp
transformation_add_type_boolean.cpp

View File

@ -25,6 +25,8 @@
#include "source/fuzz/fuzzer_pass_add_dead_blocks.h"
#include "source/fuzz/fuzzer_pass_add_dead_breaks.h"
#include "source/fuzz/fuzzer_pass_add_dead_continues.h"
#include "source/fuzz/fuzzer_pass_add_global_variables.h"
#include "source/fuzz/fuzzer_pass_add_local_variables.h"
#include "source/fuzz/fuzzer_pass_add_no_contraction_decorations.h"
#include "source/fuzz/fuzzer_pass_add_useful_constructs.h"
#include "source/fuzz/fuzzer_pass_adjust_function_controls.h"
@ -191,6 +193,12 @@ Fuzzer::FuzzerResultStatus Fuzzer::Run(
MaybeAddPass<FuzzerPassAddDeadContinues>(&passes, ir_context.get(),
&fact_manager, &fuzzer_context,
transformation_sequence_out);
MaybeAddPass<FuzzerPassAddGlobalVariables>(&passes, ir_context.get(),
&fact_manager, &fuzzer_context,
transformation_sequence_out);
MaybeAddPass<FuzzerPassAddLocalVariables>(&passes, ir_context.get(),
&fact_manager, &fuzzer_context,
transformation_sequence_out);
MaybeAddPass<FuzzerPassApplyIdSynonyms>(&passes, ir_context.get(),
&fact_manager, &fuzzer_context,
transformation_sequence_out);

View File

@ -29,6 +29,8 @@ const std::pair<uint32_t, uint32_t> kChanceOfAddingArrayOrStructType = {20, 90};
const std::pair<uint32_t, uint32_t> kChanceOfAddingDeadBlock = {20, 90};
const std::pair<uint32_t, uint32_t> kChanceOfAddingDeadBreak = {5, 80};
const std::pair<uint32_t, uint32_t> kChanceOfAddingDeadContinue = {5, 80};
const std::pair<uint32_t, uint32_t> kChanceOfAddingGlobalVariable = {20, 90};
const std::pair<uint32_t, uint32_t> kChanceOfAddingLocalVariable = {20, 90};
const std::pair<uint32_t, uint32_t> kChanceOfAddingMatrixType = {20, 70};
const std::pair<uint32_t, uint32_t> kChanceOfAddingNoContractionDecoration = {
5, 70};
@ -88,6 +90,10 @@ FuzzerContext::FuzzerContext(RandomGenerator* random_generator,
ChooseBetweenMinAndMax(kChanceOfAddingDeadBreak);
chance_of_adding_dead_continue_ =
ChooseBetweenMinAndMax(kChanceOfAddingDeadContinue);
chance_of_adding_global_variable_ =
ChooseBetweenMinAndMax(kChanceOfAddingGlobalVariable);
chance_of_adding_local_variable_ =
ChooseBetweenMinAndMax(kChanceOfAddingLocalVariable);
chance_of_adding_matrix_type_ =
ChooseBetweenMinAndMax(kChanceOfAddingMatrixType);
chance_of_adding_no_contraction_decoration_ =

View File

@ -69,6 +69,12 @@ class FuzzerContext {
uint32_t GetChanceOfAddingDeadContinue() {
return chance_of_adding_dead_continue_;
}
uint32_t GetChanceOfAddingGlobalVariable() {
return chance_of_adding_global_variable_;
}
uint32_t GetChanceOfAddingLocalVariable() {
return chance_of_adding_local_variable_;
}
uint32_t GetChanceOfAddingMatrixType() {
return chance_of_adding_matrix_type_;
}
@ -148,6 +154,8 @@ class FuzzerContext {
uint32_t chance_of_adding_dead_block_;
uint32_t chance_of_adding_dead_break_;
uint32_t chance_of_adding_dead_continue_;
uint32_t chance_of_adding_global_variable_;
uint32_t chance_of_adding_local_variable_;
uint32_t chance_of_adding_matrix_type_;
uint32_t chance_of_adding_no_contraction_decoration_;
uint32_t chance_of_adding_vector_type_;

View File

@ -14,7 +14,10 @@
#include "source/fuzz/fuzzer_pass.h"
#include "source/fuzz/fuzzer_util.h"
#include "source/fuzz/instruction_descriptor.h"
#include "source/fuzz/transformation_add_constant_boolean.h"
#include "source/fuzz/transformation_add_constant_composite.h"
#include "source/fuzz/transformation_add_constant_scalar.h"
#include "source/fuzz/transformation_add_global_undef.h"
#include "source/fuzz/transformation_add_type_boolean.h"
@ -243,6 +246,42 @@ uint32_t FuzzerPass::FindOrCreate32BitIntegerConstant(uint32_t word,
return result;
}
uint32_t FuzzerPass::FindOrCreate32BitFloatConstant(uint32_t word) {
auto float_type_id = FindOrCreate32BitFloatType();
opt::analysis::FloatConstant float_constant(
GetIRContext()->get_type_mgr()->GetType(float_type_id)->AsFloat(),
{word});
auto existing_constant =
GetIRContext()->get_constant_mgr()->FindConstant(&float_constant);
if (existing_constant) {
return GetIRContext()
->get_constant_mgr()
->GetDefiningInstruction(existing_constant)
->result_id();
}
auto result = GetFuzzerContext()->GetFreshId();
ApplyTransformation(
TransformationAddConstantScalar(result, float_type_id, {word}));
return result;
}
uint32_t FuzzerPass::FindOrCreateBoolConstant(bool value) {
auto bool_type_id = FindOrCreateBoolType();
opt::analysis::BoolConstant bool_constant(
GetIRContext()->get_type_mgr()->GetType(bool_type_id)->AsBool(), value);
auto existing_constant =
GetIRContext()->get_constant_mgr()->FindConstant(&bool_constant);
if (existing_constant) {
return GetIRContext()
->get_constant_mgr()
->GetDefiningInstruction(existing_constant)
->result_id();
}
auto result = GetFuzzerContext()->GetFreshId();
ApplyTransformation(TransformationAddConstantBoolean(result, value));
return result;
}
uint32_t FuzzerPass::FindOrCreateGlobalUndef(uint32_t type_id) {
for (auto& inst : GetIRContext()->types_values()) {
if (inst.opcode() == SpvOpUndef && inst.type_id() == type_id) {
@ -254,5 +293,147 @@ uint32_t FuzzerPass::FindOrCreateGlobalUndef(uint32_t type_id) {
return result;
}
std::pair<std::vector<uint32_t>, std::map<uint32_t, std::vector<uint32_t>>>
FuzzerPass::GetAvailableBaseTypesAndPointers(
SpvStorageClass storage_class) const {
// Records all of the base types available in the module.
std::vector<uint32_t> base_types;
// For each base type, records all the associated pointer types that target
// that base type and that have |storage_class| as their storage class.
std::map<uint32_t, std::vector<uint32_t>> base_type_to_pointers;
for (auto& inst : GetIRContext()->types_values()) {
switch (inst.opcode()) {
case SpvOpTypeArray:
case SpvOpTypeBool:
case SpvOpTypeFloat:
case SpvOpTypeInt:
case SpvOpTypeMatrix:
case SpvOpTypeStruct:
case SpvOpTypeVector:
// These types are suitable as pointer base types. Record the type,
// and the fact that we cannot yet have seen any pointers that use this
// as its base type.
base_types.push_back(inst.result_id());
base_type_to_pointers.insert({inst.result_id(), {}});
break;
case SpvOpTypePointer:
if (inst.GetSingleWordInOperand(0) == storage_class) {
// The pointer has the desired storage class, so we are interested in
// it. Associate it with its base type.
base_type_to_pointers.at(inst.GetSingleWordInOperand(1))
.push_back(inst.result_id());
}
break;
default:
break;
}
}
return {base_types, base_type_to_pointers};
}
uint32_t FuzzerPass::FindOrCreateZeroConstant(
uint32_t scalar_or_composite_type_id) {
auto type_instruction =
GetIRContext()->get_def_use_mgr()->GetDef(scalar_or_composite_type_id);
assert(type_instruction && "The type instruction must exist.");
switch (type_instruction->opcode()) {
case SpvOpTypeBool:
return FindOrCreateBoolConstant(false);
case SpvOpTypeFloat:
return FindOrCreate32BitFloatConstant(0);
case SpvOpTypeInt:
return FindOrCreate32BitIntegerConstant(
0, type_instruction->GetSingleWordInOperand(1) != 0);
case SpvOpTypeArray: {
return GetZeroConstantForHomogeneousComposite(
*type_instruction, type_instruction->GetSingleWordInOperand(0),
fuzzerutil::GetArraySize(*type_instruction, GetIRContext()));
}
case SpvOpTypeMatrix:
case SpvOpTypeVector: {
return GetZeroConstantForHomogeneousComposite(
*type_instruction, type_instruction->GetSingleWordInOperand(0),
type_instruction->GetSingleWordInOperand(1));
}
case SpvOpTypeStruct: {
std::vector<const opt::analysis::Constant*> field_zero_constants;
std::vector<uint32_t> field_zero_ids;
for (uint32_t index = 0; index < type_instruction->NumInOperands();
index++) {
uint32_t field_constant_id = FindOrCreateZeroConstant(
type_instruction->GetSingleWordInOperand(index));
field_zero_ids.push_back(field_constant_id);
field_zero_constants.push_back(
GetIRContext()->get_constant_mgr()->FindDeclaredConstant(
field_constant_id));
}
return FindOrCreateCompositeConstant(
*type_instruction, field_zero_constants, field_zero_ids);
}
default:
assert(false && "Unknown type.");
return 0;
}
}
uint32_t FuzzerPass::FindOrCreateCompositeConstant(
const opt::Instruction& composite_type_instruction,
const std::vector<const opt::analysis::Constant*>& constants,
const std::vector<uint32_t>& constant_ids) {
assert(constants.size() == constant_ids.size() &&
"Precondition: |constants| and |constant_ids| must be in "
"correspondence.");
opt::analysis::Type* composite_type = GetIRContext()->get_type_mgr()->GetType(
composite_type_instruction.result_id());
std::unique_ptr<opt::analysis::Constant> composite_constant;
if (composite_type->AsArray()) {
composite_constant = MakeUnique<opt::analysis::ArrayConstant>(
composite_type->AsArray(), constants);
} else if (composite_type->AsMatrix()) {
composite_constant = MakeUnique<opt::analysis::MatrixConstant>(
composite_type->AsMatrix(), constants);
} else if (composite_type->AsStruct()) {
composite_constant = MakeUnique<opt::analysis::StructConstant>(
composite_type->AsStruct(), constants);
} else if (composite_type->AsVector()) {
composite_constant = MakeUnique<opt::analysis::VectorConstant>(
composite_type->AsVector(), constants);
} else {
assert(false &&
"Precondition: |composite_type| must declare a composite type.");
return 0;
}
uint32_t existing_constant =
GetIRContext()->get_constant_mgr()->FindDeclaredConstant(
composite_constant.get(), composite_type_instruction.result_id());
if (existing_constant) {
return existing_constant;
}
uint32_t result = GetFuzzerContext()->GetFreshId();
ApplyTransformation(TransformationAddConstantComposite(
result, composite_type_instruction.result_id(), constant_ids));
return result;
}
uint32_t FuzzerPass::GetZeroConstantForHomogeneousComposite(
const opt::Instruction& composite_type_instruction,
uint32_t component_type_id, uint32_t num_components) {
std::vector<const opt::analysis::Constant*> zero_constants;
std::vector<uint32_t> zero_ids;
uint32_t zero_component = FindOrCreateZeroConstant(component_type_id);
const opt::analysis::Constant* registered_zero_component =
GetIRContext()->get_constant_mgr()->FindDeclaredConstant(zero_component);
for (uint32_t i = 0; i < num_components; i++) {
zero_constants.push_back(registered_zero_component);
zero_ids.push_back(zero_component);
}
return FindOrCreateCompositeConstant(composite_type_instruction,
zero_constants, zero_ids);
}
} // namespace fuzz
} // namespace spvtools

View File

@ -138,12 +138,85 @@ class FuzzerPass {
// applied to add them.
uint32_t FindOrCreate32BitIntegerConstant(uint32_t word, bool is_signed);
// Returns the id of an OpConstant instruction, with 32-bit floating-point
// type, with |word| as its value. If either the required floating-point type
// or the constant do not exist, transformations are applied to add them.
uint32_t FindOrCreate32BitFloatConstant(uint32_t word);
// Returns the id of an OpConstantTrue or OpConstantFalse instruction,
// according to |value|. If either the required instruction or the bool
// type do not exist, transformations are applied to add them.
uint32_t FindOrCreateBoolConstant(bool value);
// Returns the result id of an instruction of the form:
// %id = OpUndef %|type_id|
// If no such instruction exists, a transformation is applied to add it.
uint32_t FindOrCreateGlobalUndef(uint32_t type_id);
// Yields a pair, (base_type_ids, base_type_ids_to_pointers), such that:
// - base_type_ids captures every scalar or composite type declared in the
// module (i.e., all int, bool, float, vector, matrix, struct and array
// types
// - base_type_ids_to_pointers maps every such base type to the sequence
// of all pointer types that have storage class |storage_class| and the
// given base type as their pointee type. The sequence may be empty for
// some base types if no pointers to those types are defined for the given
// storage class, and the sequence will have multiple elements if there are
// repeated pointer declarations for the same base type and storage class.
std::pair<std::vector<uint32_t>, std::map<uint32_t, std::vector<uint32_t>>>
GetAvailableBaseTypesAndPointers(SpvStorageClass storage_class) const;
// Given a type id, |scalar_or_composite_type_id|, which must correspond to
// some scalar or composite type, returns the result id of an instruction
// defining a constant of the given type that is zero or false at everywhere.
// If such an instruction does not yet exist, transformations are applied to
// add it.
//
// Examples:
// --------------+-------------------------------
// TYPE | RESULT is id corresponding to
// --------------+-------------------------------
// bool | false
// --------------+-------------------------------
// bvec4 | (false, false, false, false)
// --------------+-------------------------------
// float | 0.0
// --------------+-------------------------------
// vec2 | (0.0, 0.0)
// --------------+-------------------------------
// int[3] | [0, 0, 0]
// --------------+-------------------------------
// struct S { |
// int i; | S(0, false, (0u, 0u))
// bool b; |
// uint2 u; |
// } |
// --------------+-------------------------------
uint32_t FindOrCreateZeroConstant(uint32_t scalar_or_composite_type_id);
private:
// Array, matrix and vector are *homogeneous* composite types in the sense
// that every component of one of these types has the same type. Given a
// homogeneous composite type instruction, |composite_type_instruction|,
// returns the id of a composite constant instruction for which every element
// is zero/false. If such an instruction does not yet exist, transformations
// are applied to add it.
uint32_t GetZeroConstantForHomogeneousComposite(
const opt::Instruction& composite_type_instruction,
uint32_t component_type_id, uint32_t num_components);
// Helper to find an existing composite constant instruction of the given
// composite type with the given constant components, or to apply
// transformations to create such an instruction if it does not yet exist.
// Parameter |composite_type_instruction| must be a composite type
// instruction. The parameters |constants| and |constant_ids| must have the
// same size, and it must be the case that for each i, |constant_ids[i]| is
// the result id of an instruction that defines |constants[i]|.
uint32_t FindOrCreateCompositeConstant(
const opt::Instruction& composite_type_instruction,
const std::vector<const opt::analysis::Constant*>& constants,
const std::vector<uint32_t>& constant_ids);
opt::IRContext* ir_context_;
FactManager* fact_manager_;
FuzzerContext* fuzzer_context_;

View File

@ -0,0 +1,75 @@
// 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_global_variables.h"
#include "source/fuzz/transformation_add_global_variable.h"
#include "source/fuzz/transformation_add_type_pointer.h"
namespace spvtools {
namespace fuzz {
FuzzerPassAddGlobalVariables::FuzzerPassAddGlobalVariables(
opt::IRContext* ir_context, FactManager* fact_manager,
FuzzerContext* fuzzer_context,
protobufs::TransformationSequence* transformations)
: FuzzerPass(ir_context, fact_manager, fuzzer_context, transformations) {}
FuzzerPassAddGlobalVariables::~FuzzerPassAddGlobalVariables() = default;
void FuzzerPassAddGlobalVariables::Apply() {
auto base_type_ids_and_pointers =
GetAvailableBaseTypesAndPointers(SpvStorageClassPrivate);
// These are the base types that are available to this fuzzer pass.
auto& base_types = base_type_ids_and_pointers.first;
// These are the pointers to those base types that are *initially* available
// to the fuzzer pass. The fuzzer pass might add pointer types in cases where
// none are available for a given base type.
auto& base_type_to_pointers = base_type_ids_and_pointers.second;
// Probabilistically keep adding global variables.
while (GetFuzzerContext()->ChoosePercentage(
GetFuzzerContext()->GetChanceOfAddingGlobalVariable())) {
// Choose a random base type; the new variable's type will be a pointer to
// this base type.
uint32_t base_type =
base_types[GetFuzzerContext()->RandomIndex(base_types)];
uint32_t pointer_type_id;
std::vector<uint32_t>& available_pointers_to_base_type =
base_type_to_pointers.at(base_type);
// Determine whether there is at least one pointer to this base type.
if (available_pointers_to_base_type.empty()) {
// There is not. Make one, to use here, and add it to the available
// pointers for the base type so that future variables can potentially
// use it.
pointer_type_id = GetFuzzerContext()->GetFreshId();
available_pointers_to_base_type.push_back(pointer_type_id);
ApplyTransformation(TransformationAddTypePointer(
pointer_type_id, SpvStorageClassPrivate, base_type));
} else {
// There is - grab one.
pointer_type_id =
available_pointers_to_base_type[GetFuzzerContext()->RandomIndex(
available_pointers_to_base_type)];
}
ApplyTransformation(TransformationAddGlobalVariable(
GetFuzzerContext()->GetFreshId(), pointer_type_id,
FindOrCreateZeroConstant(base_type), true));
}
}
} // namespace fuzz
} // namespace spvtools

View File

@ -0,0 +1,40 @@
// 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_GLOBAL_VARIABLES_H_
#define SOURCE_FUZZ_FUZZER_PASS_ADD_GLOBAL_VARIABLES_H_
#include "source/fuzz/fuzzer_pass.h"
namespace spvtools {
namespace fuzz {
// Fuzzer pass that randomly adds global variables, with Private storage class,
// to the module.
class FuzzerPassAddGlobalVariables : public FuzzerPass {
public:
FuzzerPassAddGlobalVariables(
opt::IRContext* ir_context, FactManager* fact_manager,
FuzzerContext* fuzzer_context,
protobufs::TransformationSequence* transformations);
~FuzzerPassAddGlobalVariables();
void Apply() override;
};
} // namespace fuzz
} // namespace spvtools
#endif // SOURCE_FUZZ_FUZZER_PASS_ADD_GLOBAL_VARIABLES_H_

View File

@ -0,0 +1,79 @@
// 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_local_variables.h"
#include "source/fuzz/fuzzer_util.h"
#include "source/fuzz/transformation_add_local_variable.h"
#include "source/fuzz/transformation_add_type_pointer.h"
namespace spvtools {
namespace fuzz {
FuzzerPassAddLocalVariables::FuzzerPassAddLocalVariables(
opt::IRContext* ir_context, FactManager* fact_manager,
FuzzerContext* fuzzer_context,
protobufs::TransformationSequence* transformations)
: FuzzerPass(ir_context, fact_manager, fuzzer_context, transformations) {}
FuzzerPassAddLocalVariables::~FuzzerPassAddLocalVariables() = default;
void FuzzerPassAddLocalVariables::Apply() {
auto base_type_ids_and_pointers =
GetAvailableBaseTypesAndPointers(SpvStorageClassFunction);
// These are the base types that are available to this fuzzer pass.
auto& base_types = base_type_ids_and_pointers.first;
// These are the pointers to those base types that are *initially* available
// to the fuzzer pass. The fuzzer pass might add pointer types in cases where
// none are available for a given base type.
auto& base_type_to_pointers = base_type_ids_and_pointers.second;
// Consider every function in the module.
for (auto& function : *GetIRContext()->module()) {
// Probabilistically keep adding random variables to this function.
while (GetFuzzerContext()->ChoosePercentage(
GetFuzzerContext()->GetChanceOfAddingLocalVariable())) {
// Choose a random base type; the new variable's type will be a pointer to
// this base type.
uint32_t base_type =
base_types[GetFuzzerContext()->RandomIndex(base_types)];
uint32_t pointer_type;
std::vector<uint32_t>& available_pointers_to_base_type =
base_type_to_pointers.at(base_type);
// Determine whether there is at least one pointer to this base type.
if (available_pointers_to_base_type.empty()) {
// There is not. Make one, to use here, and add it to the available
// pointers for the base type so that future variables can potentially
// use it.
pointer_type = GetFuzzerContext()->GetFreshId();
ApplyTransformation(TransformationAddTypePointer(
pointer_type, SpvStorageClassFunction, base_type));
available_pointers_to_base_type.push_back(pointer_type);
} else {
// There is - grab one.
pointer_type =
available_pointers_to_base_type[GetFuzzerContext()->RandomIndex(
available_pointers_to_base_type)];
}
ApplyTransformation(TransformationAddLocalVariable(
GetFuzzerContext()->GetFreshId(), pointer_type, function.result_id(),
FindOrCreateZeroConstant(base_type), true));
}
}
}
} // namespace fuzz
} // namespace spvtools

View File

@ -0,0 +1,43 @@
// 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_LOCAL_VARIABLES_H_
#define SOURCE_FUZZ_FUZZER_PASS_ADD_LOCAL_VARIABLES_H_
#include "source/fuzz/fuzzer_pass.h"
#include <utility>
#include <vector>
namespace spvtools {
namespace fuzz {
// Fuzzer pass that randomly adds local variables, with Function storage class,
// to the module.
class FuzzerPassAddLocalVariables : public FuzzerPass {
public:
FuzzerPassAddLocalVariables(
opt::IRContext* ir_context, FactManager* fact_manager,
FuzzerContext* fuzzer_context,
protobufs::TransformationSequence* transformations);
~FuzzerPassAddLocalVariables();
void Apply() override;
};
} // namespace fuzz
} // namespace spvtools
#endif // SOURCE_FUZZ_FUZZER_PASS_ADD_LOCAL_VARIABLES_H_

View File

@ -391,6 +391,15 @@ uint32_t FindFunctionType(opt::IRContext* ir_context,
return 0;
}
opt::Function* FindFunction(opt::IRContext* ir_context, uint32_t function_id) {
for (auto& function : *ir_context->module()) {
if (function.result_id() == function_id) {
return &function;
}
}
return nullptr;
}
} // namespace fuzzerutil
} // namespace fuzz

View File

@ -137,6 +137,10 @@ bool IsMergeOrContinue(opt::IRContext* ir_context, uint32_t block_id);
uint32_t FindFunctionType(opt::IRContext* ir_context,
const std::vector<uint32_t>& type_ids);
// Returns the function with result id |function_id|, or |nullptr| if no such
// function exists.
opt::Function* FindFunction(opt::IRContext* ir_context, uint32_t function_id);
} // namespace fuzzerutil
} // namespace fuzz

View File

@ -339,6 +339,7 @@ message Transformation {
TransformationAddGlobalUndef add_global_undef = 32;
TransformationAddFunction add_function = 33;
TransformationAddDeadBlock add_dead_block = 34;
TransformationAddLocalVariable add_local_variable = 35;
// Add additional option using the next available number.
}
}
@ -507,15 +508,38 @@ message TransformationAddGlobalVariable {
// Optional initializer; 0 if there is no initializer
uint32 initializer_id = 3;
// True if and only if the value of the variable should be regarded, in
// general, as totally unknown and subject to change (even if, due to an
// initializer, the original value is known). This is the case for variables
// added when a module is donated, for example, and means that stores to such
// variables can be performed in an arbitrary fashion.
// True if and only if the behaviour of the module should not depend on the
// value of the variable, in which case stores to the variable can be
// performed in an arbitrary fashion.
bool value_is_arbitrary = 4;
}
message TransformationAddLocalVariable {
// Adds a local variable of the given type (which must be a pointer with
// Function storage class) to the given function, initialized to the given
// id.
// Fresh id for the local variable
uint32 fresh_id = 1;
// The type of the local variable
uint32 type_id = 2;
// The id of the function to which the local variable should be added
uint32 function_id = 3;
// Initial value of the variable
uint32 initializer_id = 4;
// True if and only if the behaviour of the module should not depend on the
// value of the variable, in which case stores to the variable can be
// performed in an arbitrary fashion.
bool value_is_arbitrary = 5;
}
message TransformationAddNoContractionDecoration {
// Applies OpDecorate NoContraction to the given result id

View File

@ -26,6 +26,7 @@
#include "source/fuzz/transformation_add_function.h"
#include "source/fuzz/transformation_add_global_undef.h"
#include "source/fuzz/transformation_add_global_variable.h"
#include "source/fuzz/transformation_add_local_variable.h"
#include "source/fuzz/transformation_add_no_contraction_decoration.h"
#include "source/fuzz/transformation_add_type_array.h"
#include "source/fuzz/transformation_add_type_boolean.h"
@ -85,6 +86,9 @@ std::unique_ptr<Transformation> Transformation::FromMessage(
case protobufs::Transformation::TransformationCase::kAddGlobalVariable:
return MakeUnique<TransformationAddGlobalVariable>(
message.add_global_variable());
case protobufs::Transformation::TransformationCase::kAddLocalVariable:
return MakeUnique<TransformationAddLocalVariable>(
message.add_local_variable());
case protobufs::Transformation::TransformationCase::
kAddNoContractionDecoration:
return MakeUnique<TransformationAddNoContractionDecoration>(

View File

@ -0,0 +1,98 @@
// 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_local_variable.h"
#include "source/fuzz/fuzzer_util.h"
namespace spvtools {
namespace fuzz {
TransformationAddLocalVariable::TransformationAddLocalVariable(
const spvtools::fuzz::protobufs::TransformationAddLocalVariable& message)
: message_(message) {}
TransformationAddLocalVariable::TransformationAddLocalVariable(
uint32_t fresh_id, uint32_t type_id, uint32_t function_id,
uint32_t initializer_id, bool value_is_arbitrary) {
message_.set_fresh_id(fresh_id);
message_.set_type_id(type_id);
message_.set_function_id(function_id);
message_.set_initializer_id(initializer_id);
message_.set_value_is_arbitrary(value_is_arbitrary);
}
bool TransformationAddLocalVariable::IsApplicable(
opt::IRContext* context,
const spvtools::fuzz::FactManager& /*unused*/) const {
// The provided id must be fresh.
if (!fuzzerutil::IsFreshId(context, message_.fresh_id())) {
return false;
}
// The pointer type id must indeed correspond to a pointer, and it must have
// function storage class.
auto type_instruction =
context->get_def_use_mgr()->GetDef(message_.type_id());
if (!type_instruction || type_instruction->opcode() != SpvOpTypePointer ||
type_instruction->GetSingleWordInOperand(0) != SpvStorageClassFunction) {
return false;
}
// The initializer must...
auto initializer_instruction =
context->get_def_use_mgr()->GetDef(message_.initializer_id());
// ... exist, ...
if (!initializer_instruction) {
return false;
}
// ... be a constant, ...
if (!spvOpcodeIsConstant(initializer_instruction->opcode())) {
return false;
}
// ... and have the same type as the pointee type.
if (initializer_instruction->type_id() !=
type_instruction->GetSingleWordInOperand(1)) {
return false;
}
// The function to which the local variable is to be added must exist.
return fuzzerutil::FindFunction(context, message_.function_id());
}
void TransformationAddLocalVariable::Apply(
opt::IRContext* context, spvtools::fuzz::FactManager* fact_manager) const {
fuzzerutil::UpdateModuleIdBound(context, message_.fresh_id());
fuzzerutil::FindFunction(context, message_.function_id())
->begin()
->begin()
->InsertBefore(MakeUnique<opt::Instruction>(
context, SpvOpVariable, message_.type_id(), message_.fresh_id(),
opt::Instruction::OperandList(
{{SPV_OPERAND_TYPE_STORAGE_CLASS,
{
SpvStorageClassFunction}},
{SPV_OPERAND_TYPE_ID, {message_.initializer_id()}}})));
if (message_.value_is_arbitrary()) {
fact_manager->AddFactValueOfVariableIsArbitrary(message_.fresh_id());
}
context->InvalidateAnalysesExceptFor(opt::IRContext::kAnalysisNone);
}
protobufs::Transformation TransformationAddLocalVariable::ToMessage() const {
protobufs::Transformation result;
*result.mutable_add_local_variable() = message_;
return result;
}
} // namespace fuzz
} // namespace spvtools

View File

@ -0,0 +1,60 @@
// 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_LOCAL_VARIABLE_H_
#define SOURCE_FUZZ_TRANSFORMATION_ADD_LOCAL_VARIABLE_H_
#include "source/fuzz/fact_manager.h"
#include "source/fuzz/protobufs/spirvfuzz_protobufs.h"
#include "source/fuzz/transformation.h"
#include "source/opt/ir_context.h"
namespace spvtools {
namespace fuzz {
class TransformationAddLocalVariable : public Transformation {
public:
explicit TransformationAddLocalVariable(
const protobufs::TransformationAddLocalVariable& message);
TransformationAddLocalVariable(uint32_t fresh_id, uint32_t type_id,
uint32_t function_id, uint32_t initializer_id,
bool value_is_arbitrary);
// - |message_.fresh_id| must not be used by the module
// - |message_.type_id| must be the id of a pointer type with Function
// storage class
// - |message_.initializer_id| must be the id of a constant with the same
// type as the pointer's pointee type
// - |message_.function_id| must be the id of a function
bool IsApplicable(opt::IRContext* context,
const FactManager& fact_manager) const override;
// Adds an instruction to the start of |message_.function_id|, of the form:
// |message_.fresh_id| = OpVariable |message_.type_id| Function
// |message_.initializer_id|
// If |message_.value_is_arbitrary| holds, adds a corresponding fact to
// |fact_manager|.
void Apply(opt::IRContext* context, FactManager* fact_manager) const override;
protobufs::Transformation ToMessage() const override;
private:
protobufs::TransformationAddLocalVariable message_;
};
} // namespace fuzz
} // namespace spvtools
#endif // SOURCE_FUZZ_TRANSFORMATION_ADD_LOCAL_VARIABLE_H_

View File

@ -33,6 +33,7 @@ if (${SPIRV_BUILD_FUZZER})
transformation_add_function_test.cpp
transformation_add_global_undef_test.cpp
transformation_add_global_variable_test.cpp
transformation_add_local_variable_test.cpp
transformation_add_no_contraction_decoration_test.cpp
transformation_add_type_array_test.cpp
transformation_add_type_boolean_test.cpp

View File

@ -0,0 +1,206 @@
// 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_local_variable.h"
#include "test/fuzz/fuzz_test_util.h"
namespace spvtools {
namespace fuzz {
namespace {
TEST(TransformationAddLocalVariableTest, BasicTest) {
std::string shader = R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %4 "main"
OpExecutionMode %4 OriginUpperLeft
OpSource ESSL 310
%2 = OpTypeVoid
%3 = OpTypeFunction %2
%6 = OpTypeInt 32 1
%7 = OpTypeStruct %6 %6
%8 = OpTypePointer Function %7
%10 = OpConstant %6 1
%11 = OpConstant %6 2
%12 = OpConstantComposite %7 %10 %11
%13 = OpTypeFloat 32
%14 = OpTypeInt 32 0
%15 = OpConstant %14 3
%16 = OpTypeArray %13 %15
%17 = OpTypeBool
%18 = OpTypeStruct %16 %7 %17
%19 = OpTypePointer Function %18
%21 = OpConstant %13 1
%22 = OpConstant %13 2
%23 = OpConstant %13 4
%24 = OpConstantComposite %16 %21 %22 %23
%25 = OpConstant %6 5
%26 = OpConstant %6 6
%27 = OpConstantComposite %7 %25 %26
%28 = OpConstantFalse %17
%29 = OpConstantComposite %18 %24 %27 %28
%30 = OpTypeVector %13 2
%31 = OpTypePointer Function %30
%33 = OpConstantComposite %30 %21 %21
%34 = OpTypeVector %17 3
%35 = OpTypePointer Function %34
%37 = OpConstantTrue %17
%38 = OpConstantComposite %34 %37 %28 %28
%39 = OpTypeVector %13 4
%40 = OpTypeMatrix %39 3
%41 = OpTypePointer Function %40
%43 = OpConstantComposite %39 %21 %22 %23 %21
%44 = OpConstantComposite %39 %22 %23 %21 %22
%45 = OpConstantComposite %39 %23 %21 %22 %23
%46 = OpConstantComposite %40 %43 %44 %45
%50 = OpTypePointer Function %14
%51 = OpConstantNull %14
%4 = OpFunction %2 None %3
%5 = OpLabel
OpReturn
OpFunctionEnd
)";
const auto env = SPV_ENV_UNIVERSAL_1_4;
const auto consumer = nullptr;
const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
ASSERT_TRUE(IsValid(env, context.get()));
FactManager fact_manager;
// A few cases of inapplicable transformations:
// Id 4 is already in use
ASSERT_FALSE(TransformationAddLocalVariable(4, 50, 4, 51, true)
.IsApplicable(context.get(), fact_manager));
// Type mismatch between initializer and pointer
ASSERT_FALSE(TransformationAddLocalVariable(105, 46, 4, 51, true)
.IsApplicable(context.get(), fact_manager));
// Id 5 is not a function
ASSERT_FALSE(TransformationAddLocalVariable(105, 50, 5, 51, true)
.IsApplicable(context.get(), fact_manager));
// %105 = OpVariable %50 Function %51
{
TransformationAddLocalVariable transformation(105, 50, 4, 51, true);
ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager));
transformation.Apply(context.get(), &fact_manager);
}
// %104 = OpVariable %41 Function %46
{
TransformationAddLocalVariable transformation(104, 41, 4, 46, false);
ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager));
transformation.Apply(context.get(), &fact_manager);
}
// %103 = OpVariable %35 Function %38
{
TransformationAddLocalVariable transformation(103, 35, 4, 38, true);
ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager));
transformation.Apply(context.get(), &fact_manager);
}
// %102 = OpVariable %31 Function %33
{
TransformationAddLocalVariable transformation(102, 31, 4, 33, false);
ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager));
transformation.Apply(context.get(), &fact_manager);
}
// %101 = OpVariable %19 Function %29
{
TransformationAddLocalVariable transformation(101, 19, 4, 29, true);
ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager));
transformation.Apply(context.get(), &fact_manager);
}
// %100 = OpVariable %8 Function %12
{
TransformationAddLocalVariable transformation(100, 8, 4, 12, false);
ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager));
transformation.Apply(context.get(), &fact_manager);
}
ASSERT_FALSE(fact_manager.VariableValueIsArbitrary(100));
ASSERT_TRUE(fact_manager.VariableValueIsArbitrary(101));
ASSERT_FALSE(fact_manager.VariableValueIsArbitrary(102));
ASSERT_TRUE(fact_manager.VariableValueIsArbitrary(103));
ASSERT_FALSE(fact_manager.VariableValueIsArbitrary(104));
ASSERT_TRUE(fact_manager.VariableValueIsArbitrary(105));
std::string after_transformation = R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %4 "main"
OpExecutionMode %4 OriginUpperLeft
OpSource ESSL 310
%2 = OpTypeVoid
%3 = OpTypeFunction %2
%6 = OpTypeInt 32 1
%7 = OpTypeStruct %6 %6
%8 = OpTypePointer Function %7
%10 = OpConstant %6 1
%11 = OpConstant %6 2
%12 = OpConstantComposite %7 %10 %11
%13 = OpTypeFloat 32
%14 = OpTypeInt 32 0
%15 = OpConstant %14 3
%16 = OpTypeArray %13 %15
%17 = OpTypeBool
%18 = OpTypeStruct %16 %7 %17
%19 = OpTypePointer Function %18
%21 = OpConstant %13 1
%22 = OpConstant %13 2
%23 = OpConstant %13 4
%24 = OpConstantComposite %16 %21 %22 %23
%25 = OpConstant %6 5
%26 = OpConstant %6 6
%27 = OpConstantComposite %7 %25 %26
%28 = OpConstantFalse %17
%29 = OpConstantComposite %18 %24 %27 %28
%30 = OpTypeVector %13 2
%31 = OpTypePointer Function %30
%33 = OpConstantComposite %30 %21 %21
%34 = OpTypeVector %17 3
%35 = OpTypePointer Function %34
%37 = OpConstantTrue %17
%38 = OpConstantComposite %34 %37 %28 %28
%39 = OpTypeVector %13 4
%40 = OpTypeMatrix %39 3
%41 = OpTypePointer Function %40
%43 = OpConstantComposite %39 %21 %22 %23 %21
%44 = OpConstantComposite %39 %22 %23 %21 %22
%45 = OpConstantComposite %39 %23 %21 %22 %23
%46 = OpConstantComposite %40 %43 %44 %45
%50 = OpTypePointer Function %14
%51 = OpConstantNull %14
%4 = OpFunction %2 None %3
%5 = OpLabel
%100 = OpVariable %8 Function %12
%101 = OpVariable %19 Function %29
%102 = OpVariable %31 Function %33
%103 = OpVariable %35 Function %38
%104 = OpVariable %41 Function %46
%105 = OpVariable %50 Function %51
OpReturn
OpFunctionEnd
)";
ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
}
} // namespace
} // namespace fuzz
} // namespace spvtools