SPIRV-Tools/source/fuzz/fuzzer_pass_add_useful_constructs.cpp
Alastair Donaldson 8d4261bc44
spirv-fuzz: Introduce TransformationContext (#3272)
Some transformations (e.g. TransformationAddFunction) rely on running
the validator to decide whether the transformation is applicable.  A
recent change allowed spirv-fuzz to take validator options, to cater
for the case where a module should be considered valid under
particular conditions.  However, validation during the checking of
transformations had no access to these validator options.

This change introduced TransformationContext, which currently consists
of a fact manager and a set of validator options, but could in the
future have other fields corresponding to other objects that it is
useful to have access to when applying transformations.  Now, instead
of checking and applying transformations in the context of a
FactManager, a TransformationContext is used.  This gives access to
the fact manager as before, and also access to the validator options
when they are needed.
2020-04-02 15:54:46 +01:00

223 lines
10 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 {
FuzzerPassAddUsefulConstructs::FuzzerPassAddUsefulConstructs(
opt::IRContext* ir_context, TransformationContext* transformation_context,
FuzzerContext* fuzzer_context,
protobufs::TransformationSequence* transformations)
: FuzzerPass(ir_context, transformation_context, 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(),
*GetTransformationContext()) &&
"Should be applicable by construction.");
add_constant_int.Apply(GetIRContext(), GetTransformationContext());
*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(),
*GetTransformationContext()) &&
"Should be applicable by construction.");
add_constant_float.Apply(GetIRContext(), GetTransformationContext());
*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(),
*GetTransformationContext()) &&
"Should be applicable by construction.");
add_type_boolean.Apply(GetIRContext(), GetTransformationContext());
*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(),
*GetTransformationContext()) &&
"Should be applicable by construction.");
add_type_int.Apply(GetIRContext(), GetTransformationContext());
*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(),
*GetTransformationContext()) &&
"Should be applicable by construction.");
add_type_float.Apply(GetIRContext(), GetTransformationContext());
*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(),
*GetTransformationContext()) &&
"Should be applicable by construction.");
add_constant_boolean.Apply(GetIRContext(), GetTransformationContext());
*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 : GetTransformationContext()
->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(),
*GetTransformationContext()) &&
"Should be applicable by construction.");
add_pointer.Apply(GetIRContext(), GetTransformationContext());
*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