mirror of
https://github.com/KhronosGroup/SPIRV-Tools
synced 2024-12-29 03:01:08 +00:00
26dba32c43
A new pass that allows the fuzzer to change the 'selection control' operand of OpSelectionControl instructions. Fixes #2937.
215 lines
9.5 KiB
C++
215 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 {
|
|
|
|
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
|