| // 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 |