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