SPIRV-Tools/source/fuzz/fuzzer_pass_add_useful_constructs.cpp
Alastair Donaldson dfcb5a1e10
Refactor fuzzer transformations (#2694)
Introduced abstract class for transformations, and refactored all transformations to inherit from this abstract class.
2019-06-25 20:49:46 +01:00

217 lines
9.5 KiB
C++

// Copyright (c) 2019 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_useful_constructs.h"
#include "source/fuzz/transformation_add_constant_boolean.h"
#include "source/fuzz/transformation_add_constant_scalar.h"
#include "source/fuzz/transformation_add_type_boolean.h"
#include "source/fuzz/transformation_add_type_float.h"
#include "source/fuzz/transformation_add_type_int.h"
#include "source/fuzz/transformation_add_type_pointer.h"
namespace spvtools {
namespace fuzz {
using opt::IRContext;
FuzzerPassAddUsefulConstructs::FuzzerPassAddUsefulConstructs(
opt::IRContext* ir_context, FactManager* fact_manager,
FuzzerContext* fuzzer_context,
protobufs::TransformationSequence* transformations)
: FuzzerPass(ir_context, fact_manager, fuzzer_context, transformations){};
FuzzerPassAddUsefulConstructs::~FuzzerPassAddUsefulConstructs() = default;
void FuzzerPassAddUsefulConstructs::MaybeAddIntConstant(
uint32_t width, bool is_signed, std::vector<uint32_t> data) const {
opt::analysis::Integer temp_int_type(width, is_signed);
assert(GetIRContext()->get_type_mgr()->GetId(&temp_int_type) &&
"int type should already be registered.");
auto registered_int_type = GetIRContext()
->get_type_mgr()
->GetRegisteredType(&temp_int_type)
->AsInteger();
auto int_type_id = GetIRContext()->get_type_mgr()->GetId(registered_int_type);
assert(int_type_id &&
"The relevant int type should have been added to the module already.");
opt::analysis::IntConstant int_constant(registered_int_type, data);
if (!GetIRContext()->get_constant_mgr()->FindConstant(&int_constant)) {
TransformationAddConstantScalar add_constant_int =
TransformationAddConstantScalar(GetFuzzerContext()->GetFreshId(),
int_type_id, data);
assert(add_constant_int.IsApplicable(GetIRContext(), *GetFactManager()) &&
"Should be applicable by construction.");
add_constant_int.Apply(GetIRContext(), GetFactManager());
*GetTransformations()->add_transformation() = add_constant_int.ToMessage();
}
}
void FuzzerPassAddUsefulConstructs::MaybeAddFloatConstant(
uint32_t width, std::vector<uint32_t> data) const {
opt::analysis::Float temp_float_type(width);
assert(GetIRContext()->get_type_mgr()->GetId(&temp_float_type) &&
"float type should already be registered.");
auto registered_float_type = GetIRContext()
->get_type_mgr()
->GetRegisteredType(&temp_float_type)
->AsFloat();
auto float_type_id =
GetIRContext()->get_type_mgr()->GetId(registered_float_type);
assert(
float_type_id &&
"The relevant float type should have been added to the module already.");
opt::analysis::FloatConstant float_constant(registered_float_type, data);
if (!GetIRContext()->get_constant_mgr()->FindConstant(&float_constant)) {
TransformationAddConstantScalar add_constant_float =
TransformationAddConstantScalar(GetFuzzerContext()->GetFreshId(),
float_type_id, data);
assert(add_constant_float.IsApplicable(GetIRContext(), *GetFactManager()) &&
"Should be applicable by construction.");
add_constant_float.Apply(GetIRContext(), GetFactManager());
*GetTransformations()->add_transformation() =
add_constant_float.ToMessage();
}
}
void FuzzerPassAddUsefulConstructs::Apply() {
{
// Add boolean type if not present.
opt::analysis::Bool temp_bool_type;
if (!GetIRContext()->get_type_mgr()->GetId(&temp_bool_type)) {
auto add_type_boolean =
TransformationAddTypeBoolean(GetFuzzerContext()->GetFreshId());
assert(add_type_boolean.IsApplicable(GetIRContext(), *GetFactManager()) &&
"Should be applicable by construction.");
add_type_boolean.Apply(GetIRContext(), GetFactManager());
*GetTransformations()->add_transformation() =
add_type_boolean.ToMessage();
}
}
{
// Add signed and unsigned 32-bit integer types if not present.
for (auto is_signed : {true, false}) {
opt::analysis::Integer temp_int_type(32, is_signed);
if (!GetIRContext()->get_type_mgr()->GetId(&temp_int_type)) {
TransformationAddTypeInt add_type_int = TransformationAddTypeInt(
GetFuzzerContext()->GetFreshId(), 32, is_signed);
assert(add_type_int.IsApplicable(GetIRContext(), *GetFactManager()) &&
"Should be applicable by construction.");
add_type_int.Apply(GetIRContext(), GetFactManager());
*GetTransformations()->add_transformation() = add_type_int.ToMessage();
}
}
}
{
// Add 32-bit float type if not present.
opt::analysis::Float temp_float_type(32);
if (!GetIRContext()->get_type_mgr()->GetId(&temp_float_type)) {
TransformationAddTypeFloat add_type_float =
TransformationAddTypeFloat(GetFuzzerContext()->GetFreshId(), 32);
assert(add_type_float.IsApplicable(GetIRContext(), *GetFactManager()) &&
"Should be applicable by construction.");
add_type_float.Apply(GetIRContext(), GetFactManager());
*GetTransformations()->add_transformation() = add_type_float.ToMessage();
}
}
// Add boolean constants true and false if not present.
opt::analysis::Bool temp_bool_type;
auto bool_type = GetIRContext()
->get_type_mgr()
->GetRegisteredType(&temp_bool_type)
->AsBool();
for (auto boolean_value : {true, false}) {
// Add OpConstantTrue/False if not already there.
opt::analysis::BoolConstant bool_constant(bool_type, boolean_value);
if (!GetIRContext()->get_constant_mgr()->FindConstant(&bool_constant)) {
TransformationAddConstantBoolean add_constant_boolean(
GetFuzzerContext()->GetFreshId(), boolean_value);
assert(add_constant_boolean.IsApplicable(GetIRContext(),
*GetFactManager()) &&
"Should be applicable by construction.");
add_constant_boolean.Apply(GetIRContext(), GetFactManager());
*GetTransformations()->add_transformation() =
add_constant_boolean.ToMessage();
}
}
// Add signed and unsigned 32-bit integer constants 0 and 1 if not present.
for (auto is_signed : {true, false}) {
for (auto value : {0u, 1u}) {
MaybeAddIntConstant(32, is_signed, {value});
}
}
// Add 32-bit float constants 0.0 and 1.0 if not present.
uint32_t uint_data[2];
float float_data[2] = {0.0, 1.0};
memcpy(uint_data, float_data, sizeof(float_data));
for (unsigned int& datum : uint_data) {
MaybeAddFloatConstant(32, {datum});
}
// For every known-to-be-constant uniform, make sure we have instructions
// declaring:
// - a pointer type with uniform storage class, whose pointee type is the type
// of the element
// - a signed integer constant for each index required to access the element
// - a constant for the constant value itself
for (auto& fact_and_type_id :
GetFactManager()->GetConstantUniformFactsAndTypes()) {
uint32_t element_type_id = fact_and_type_id.second;
assert(element_type_id);
auto element_type =
GetIRContext()->get_type_mgr()->GetType(element_type_id);
assert(element_type &&
"If the constant uniform fact is well-formed, the module must "
"already have a declaration of the type for the uniform element.");
opt::analysis::Pointer uniform_pointer(element_type,
SpvStorageClassUniform);
if (!GetIRContext()->get_type_mgr()->GetId(&uniform_pointer)) {
auto add_pointer =
TransformationAddTypePointer(GetFuzzerContext()->GetFreshId(),
SpvStorageClassUniform, element_type_id);
assert(add_pointer.IsApplicable(GetIRContext(), *GetFactManager()) &&
"Should be applicable by construction.");
add_pointer.Apply(GetIRContext(), GetFactManager());
*GetTransformations()->add_transformation() = add_pointer.ToMessage();
}
std::vector<uint32_t> words;
for (auto word : fact_and_type_id.first.constant_word()) {
words.push_back(word);
}
// We get the element type again as the type manager may have been
// invalidated since we last retrieved it.
element_type = GetIRContext()->get_type_mgr()->GetType(element_type_id);
if (element_type->AsInteger()) {
MaybeAddIntConstant(element_type->AsInteger()->width(),
element_type->AsInteger()->IsSigned(), words);
} else {
assert(element_type->AsFloat() &&
"Known uniform values must be integer or floating-point.");
MaybeAddFloatConstant(element_type->AsFloat()->width(), words);
}
for (auto index :
fact_and_type_id.first.uniform_buffer_element_descriptor().index()) {
MaybeAddIntConstant(32, true, {index});
}
}
}
} // namespace fuzz
} // namespace spvtools