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