diff --git a/source/fuzz/fuzzer_pass.cpp b/source/fuzz/fuzzer_pass.cpp index a8cebe559..c44a4eaa7 100644 --- a/source/fuzz/fuzzer_pass.cpp +++ b/source/fuzz/fuzzer_pass.cpp @@ -28,6 +28,7 @@ #include "source/fuzz/transformation_add_type_int.h" #include "source/fuzz/transformation_add_type_matrix.h" #include "source/fuzz/transformation_add_type_pointer.h" +#include "source/fuzz/transformation_add_type_struct.h" #include "source/fuzz/transformation_add_type_vector.h" namespace spvtools { @@ -162,9 +163,8 @@ uint32_t FuzzerPass::FindOrCreateBoolType() { } uint32_t FuzzerPass::FindOrCreateIntegerType(uint32_t width, bool is_signed) { - opt::analysis::Integer int_type(width, is_signed); - auto existing_id = GetIRContext()->get_type_mgr()->GetId(&int_type); - if (existing_id) { + if (auto existing_id = + fuzzerutil::MaybeGetIntegerType(GetIRContext(), width, is_signed)) { return existing_id; } auto result = GetFuzzerContext()->GetFreshId(); @@ -173,9 +173,7 @@ uint32_t FuzzerPass::FindOrCreateIntegerType(uint32_t width, bool is_signed) { } uint32_t FuzzerPass::FindOrCreateFloatType(uint32_t width) { - opt::analysis::Float float_type(width); - auto existing_id = GetIRContext()->get_type_mgr()->GetId(&float_type); - if (existing_id) { + if (auto existing_id = fuzzerutil::MaybeGetFloatType(GetIRContext(), width)) { return existing_id; } auto result = GetFuzzerContext()->GetFreshId(); @@ -205,14 +203,8 @@ uint32_t FuzzerPass::FindOrCreateFunctionType( uint32_t FuzzerPass::FindOrCreateVectorType(uint32_t component_type_id, uint32_t component_count) { - assert(component_count >= 2 && component_count <= 4 && - "Precondition: component count must be in range [2, 4]."); - opt::analysis::Type* component_type = - GetIRContext()->get_type_mgr()->GetType(component_type_id); - assert(component_type && "Precondition: the component type must exist."); - opt::analysis::Vector vector_type(component_type, component_count); - auto existing_id = GetIRContext()->get_type_mgr()->GetId(&vector_type); - if (existing_id) { + if (auto existing_id = fuzzerutil::MaybeGetVectorType( + GetIRContext(), component_type_id, component_count)) { return existing_id; } auto result = GetFuzzerContext()->GetFreshId(); @@ -242,6 +234,17 @@ uint32_t FuzzerPass::FindOrCreateMatrixType(uint32_t column_count, return result; } +uint32_t FuzzerPass::FindOrCreateStructType( + const std::vector& component_type_ids) { + if (auto existing_id = + fuzzerutil::MaybeGetStructType(GetIRContext(), component_type_ids)) { + return existing_id; + } + auto new_id = GetFuzzerContext()->GetFreshId(); + ApplyTransformation(TransformationAddTypeStruct(new_id, component_type_ids)); + return new_id; +} + uint32_t FuzzerPass::FindOrCreatePointerType(uint32_t base_type_id, SpvStorageClass storage_class) { // We do not use the type manager here, due to problems related to isomorphic diff --git a/source/fuzz/fuzzer_pass.h b/source/fuzz/fuzzer_pass.h index fae222c99..f1f68ed07 100644 --- a/source/fuzz/fuzzer_pass.h +++ b/source/fuzz/fuzzer_pass.h @@ -136,6 +136,13 @@ class FuzzerPass { // type itself do not exist, transformations are applied to add them. uint32_t FindOrCreateMatrixType(uint32_t column_count, uint32_t row_count); + // Returns the id of an OpTypeStruct instruction with |component_type_ids| as + // type ids for struct's components. If no such a struct type exists, + // transformations are applied to add it. |component_type_ids| may not contain + // a result id of an OpTypeFunction. + uint32_t FindOrCreateStructType( + const std::vector& component_type_ids); + // Returns the id of a pointer type with base type |base_type_id| (which must // already exist) and storage class |storage_class|. A transformation is // applied to add the pointer if it does not already exist. diff --git a/source/fuzz/fuzzer_util.cpp b/source/fuzz/fuzzer_util.cpp index 7b137cf30..80dff2d10 100644 --- a/source/fuzz/fuzzer_util.cpp +++ b/source/fuzz/fuzzer_util.cpp @@ -729,6 +729,146 @@ uint32_t FindOrCreateFunctionType(opt::IRContext* ir_context, return result_id; } +uint32_t MaybeGetIntegerType(opt::IRContext* ir_context, uint32_t width, + bool is_signed) { + opt::analysis::Integer type(width, is_signed); + return ir_context->get_type_mgr()->GetId(&type); +} + +uint32_t MaybeGetFloatType(opt::IRContext* ir_context, uint32_t width) { + opt::analysis::Float type(width); + return ir_context->get_type_mgr()->GetId(&type); +} + +uint32_t MaybeGetVectorType(opt::IRContext* ir_context, + uint32_t component_type_id, + uint32_t element_count) { + const auto* component_type = + ir_context->get_type_mgr()->GetType(component_type_id); + assert(component_type && + (component_type->AsInteger() || component_type->AsFloat() || + component_type->AsBool()) && + "|component_type_id| is invalid"); + assert(element_count >= 2 && element_count <= 4 && + "Precondition: component count must be in range [2, 4]."); + opt::analysis::Vector type(component_type, element_count); + return ir_context->get_type_mgr()->GetId(&type); +} + +uint32_t MaybeGetStructType(opt::IRContext* ir_context, + const std::vector& component_type_ids) { + std::vector component_types; + component_types.reserve(component_type_ids.size()); + + for (auto type_id : component_type_ids) { + const auto* component_type = ir_context->get_type_mgr()->GetType(type_id); + assert(component_type && !component_type->AsFunction() && + "Component type is invalid"); + component_types.push_back(component_type); + } + + opt::analysis::Struct type(component_types); + return ir_context->get_type_mgr()->GetId(&type); +} + +void AddIntegerType(opt::IRContext* ir_context, uint32_t result_id, + uint32_t width, bool is_signed) { + ir_context->module()->AddType(MakeUnique( + ir_context, SpvOpTypeInt, 0, result_id, + opt::Instruction::OperandList{ + {SPV_OPERAND_TYPE_LITERAL_INTEGER, {width}}, + {SPV_OPERAND_TYPE_LITERAL_INTEGER, {is_signed ? 1u : 0u}}})); + + UpdateModuleIdBound(ir_context, result_id); +} + +void AddFloatType(opt::IRContext* ir_context, uint32_t result_id, + uint32_t width) { + ir_context->module()->AddType(MakeUnique( + ir_context, SpvOpTypeFloat, 0, result_id, + opt::Instruction::OperandList{ + {SPV_OPERAND_TYPE_LITERAL_INTEGER, {width}}})); + + UpdateModuleIdBound(ir_context, result_id); +} + +void AddVectorType(opt::IRContext* ir_context, uint32_t result_id, + uint32_t component_type_id, uint32_t element_count) { + const auto* component_type = + ir_context->get_type_mgr()->GetType(component_type_id); + (void)component_type; // Make compiler happy in release mode. + assert(component_type && + (component_type->AsInteger() || component_type->AsFloat() || + component_type->AsBool()) && + "|component_type_id| is invalid"); + assert(element_count >= 2 && element_count <= 4 && + "Precondition: component count must be in range [2, 4]."); + ir_context->module()->AddType(MakeUnique( + ir_context, SpvOpTypeVector, 0, result_id, + opt::Instruction::OperandList{ + {SPV_OPERAND_TYPE_ID, {component_type_id}}, + {SPV_OPERAND_TYPE_LITERAL_INTEGER, {element_count}}})); + + UpdateModuleIdBound(ir_context, result_id); +} + +void AddStructType(opt::IRContext* ir_context, uint32_t result_id, + const std::vector& component_type_ids) { + opt::Instruction::OperandList operands; + operands.reserve(component_type_ids.size()); + + for (auto type_id : component_type_ids) { + const auto* type = ir_context->get_type_mgr()->GetType(type_id); + (void)type; // Make compiler happy in release mode. + assert(type && !type->AsFunction() && "Component's type id is invalid"); + operands.push_back({SPV_OPERAND_TYPE_ID, {type_id}}); + } + + ir_context->AddType(MakeUnique( + ir_context, SpvOpTypeStruct, 0, result_id, std::move(operands))); + + UpdateModuleIdBound(ir_context, result_id); +} + +uint32_t FindOrCreateIntegerType(opt::IRContext* ir_context, uint32_t result_id, + uint32_t width, bool is_signed) { + if (auto existing_id = MaybeGetIntegerType(ir_context, width, is_signed)) { + return existing_id; + } + AddIntegerType(ir_context, result_id, width, is_signed); + return result_id; +} + +uint32_t FindOrCreateFloatType(opt::IRContext* ir_context, uint32_t result_id, + uint32_t width) { + if (auto existing_id = MaybeGetFloatType(ir_context, width)) { + return existing_id; + } + AddFloatType(ir_context, result_id, width); + return result_id; +} + +uint32_t FindOrCreateVectorType(opt::IRContext* ir_context, uint32_t result_id, + uint32_t component_type_id, + uint32_t element_count) { + if (auto existing_id = + MaybeGetVectorType(ir_context, component_type_id, element_count)) { + return existing_id; + } + AddVectorType(ir_context, result_id, component_type_id, element_count); + return result_id; +} + +uint32_t FindOrCreateStructType( + opt::IRContext* ir_context, uint32_t result_id, + const std::vector& component_type_ids) { + if (auto existing_id = MaybeGetStructType(ir_context, component_type_ids)) { + return existing_id; + } + AddStructType(ir_context, result_id, component_type_ids); + return result_id; +} + } // namespace fuzzerutil } // namespace fuzz diff --git a/source/fuzz/fuzzer_util.h b/source/fuzz/fuzzer_util.h index 7928df054..5b471bd34 100644 --- a/source/fuzz/fuzzer_util.h +++ b/source/fuzz/fuzzer_util.h @@ -284,6 +284,84 @@ uint32_t FindOrCreateFunctionType(opt::IRContext* ir_context, uint32_t result_id, const std::vector& type_ids); +// Returns a result id of an OpTypeInt instruction if present. Returns 0 +// otherwise. +uint32_t MaybeGetIntegerType(opt::IRContext* ir_context, uint32_t width, + bool is_signed); + +// Returns a result id of an OpTypeFloat instruction if present. Returns 0 +// otherwise. +uint32_t MaybeGetFloatType(opt::IRContext* ir_context, uint32_t width); + +// Returns a result id of an OpTypeVector instruction if present. Returns 0 +// otherwise. |component_type_id| must be a valid result id of an OpTypeInt, +// OpTypeFloat or OpTypeBool instruction in the module. |element_count| must be +// in the range [2, 4]. +uint32_t MaybeGetVectorType(opt::IRContext* ir_context, + uint32_t component_type_id, uint32_t element_count); + +// Returns a result id of an OpTypeStruct instruction if present. Returns 0 +// otherwise. |component_type_ids| may not contain a result id of an +// OpTypeFunction. +uint32_t MaybeGetStructType(opt::IRContext* ir_context, + const std::vector& component_type_ids); + +// Creates a new OpTypeInt instruction in the module. Updates module's id bound +// to accommodate for |result_id|. +void AddIntegerType(opt::IRContext* ir_context, uint32_t result_id, + uint32_t width, bool is_signed); + +// Creates a new OpTypeFloat instruction in the module. Updates module's id +// bound to accommodate for |result_id|. +void AddFloatType(opt::IRContext* ir_context, uint32_t result_id, + uint32_t width); + +// Creates a new OpTypeVector instruction in the module. |component_type_id| +// must be a valid result id of an OpTypeInt, OpTypeFloat or OpTypeBool +// instruction in the module. |element_count| must be in the range [2, 4]. +// Updates module's id bound to accommodate for |result_id|. +void AddVectorType(opt::IRContext* ir_context, uint32_t result_id, + uint32_t component_type_id, uint32_t element_count); + +// Creates a new OpTypeStruct instruction in the module. Updates module's id +// bound to accommodate for |result_id|. |component_type_ids| may not contain +// a result id of an OpTypeFunction. +void AddStructType(opt::IRContext* ir_context, uint32_t result_id, + const std::vector& component_type_ids); + +// Returns a result id of an OpTypeInt instruction in the module. Creates a new +// instruction with |result_id|, if no required OpTypeInt is present in the +// module, and returns |result_id|. Updates module's id bound to accommodate for +// |result_id|. +uint32_t FindOrCreateIntegerType(opt::IRContext* ir_context, uint32_t result_id, + uint32_t width, bool is_signed); + +// Returns a result id of an OpTypeFloat instruction in the module. Creates a +// new instruction with |result_id|, if no required OpTypeFloat is present in +// the module, and returns |result_id|. Updates module's id bound +// to accommodate for |result_id|. +uint32_t FindOrCreateFloatType(opt::IRContext* ir_context, uint32_t result_id, + uint32_t width); + +// Returns a result id of an OpTypeVector instruction in the module. Creates a +// new instruction with |result_id|, if no required OpTypeVector is present in +// the module, and returns |result_id|. |component_type_id| must be a valid +// result id of an OpTypeInt, OpTypeFloat or OpTypeBool instruction in the +// module. |element_count| must be in the range [2, 4]. Updates module's id +// bound to accommodate for |result_id|. +uint32_t FindOrCreateVectorType(opt::IRContext* ir_context, uint32_t result_id, + uint32_t component_type_id, + uint32_t element_count); + +// Returns a result id of an OpTypeStruct instruction in the module. Creates a +// new instruction with |result_id|, if no required OpTypeStruct is present in +// the module, and returns |result_id|. Updates module's id bound +// to accommodate for |result_id|. |component_type_ids| may not contain a result +// id of an OpTypeFunction. +uint32_t FindOrCreateStructType( + opt::IRContext* ir_context, uint32_t result_id, + const std::vector& component_type_ids); + } // namespace fuzzerutil } // namespace fuzz diff --git a/source/fuzz/transformation_add_parameter.cpp b/source/fuzz/transformation_add_parameter.cpp index c6ddc1c65..e1d159720 100644 --- a/source/fuzz/transformation_add_parameter.cpp +++ b/source/fuzz/transformation_add_parameter.cpp @@ -73,6 +73,7 @@ void TransformationAddParameter::Apply( auto parameter_type_id = fuzzerutil::GetTypeId(ir_context, message_.initializer_id()); + assert(parameter_type_id != 0 && "Initializer has invalid type"); // Add new parameters to the function. function->AddParameter(MakeUnique( diff --git a/source/fuzz/transformation_add_type_float.cpp b/source/fuzz/transformation_add_type_float.cpp index 80716e14f..c0c434bb9 100644 --- a/source/fuzz/transformation_add_type_float.cpp +++ b/source/fuzz/transformation_add_type_float.cpp @@ -38,17 +38,12 @@ bool TransformationAddTypeFloat::IsApplicable( // Applicable if there is no float type with this width already declared in // the module. - opt::analysis::Float float_type(message_.width()); - return ir_context->get_type_mgr()->GetId(&float_type) == 0; + return fuzzerutil::MaybeGetFloatType(ir_context, message_.width()) == 0; } void TransformationAddTypeFloat::Apply( opt::IRContext* ir_context, TransformationContext* /*unused*/) const { - opt::Instruction::OperandList width = { - {SPV_OPERAND_TYPE_LITERAL_INTEGER, {message_.width()}}}; - ir_context->module()->AddType(MakeUnique( - ir_context, SpvOpTypeFloat, 0, message_.fresh_id(), width)); - fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_id()); + fuzzerutil::AddFloatType(ir_context, message_.fresh_id(), message_.width()); // We have added an instruction to the module, so need to be careful about the // validity of existing analyses. ir_context->InvalidateAnalysesExceptFor( diff --git a/source/fuzz/transformation_add_type_int.cpp b/source/fuzz/transformation_add_type_int.cpp index a932a5f96..20759fc84 100644 --- a/source/fuzz/transformation_add_type_int.cpp +++ b/source/fuzz/transformation_add_type_int.cpp @@ -40,18 +40,14 @@ bool TransformationAddTypeInt::IsApplicable( // Applicable if there is no int type with this width and signedness already // declared in the module. - opt::analysis::Integer int_type(message_.width(), message_.is_signed()); - return ir_context->get_type_mgr()->GetId(&int_type) == 0; + return fuzzerutil::MaybeGetIntegerType(ir_context, message_.width(), + message_.is_signed()) == 0; } void TransformationAddTypeInt::Apply(opt::IRContext* ir_context, TransformationContext* /*unused*/) const { - opt::Instruction::OperandList in_operands = { - {SPV_OPERAND_TYPE_LITERAL_INTEGER, {message_.width()}}, - {SPV_OPERAND_TYPE_LITERAL_INTEGER, {message_.is_signed() ? 1u : 0u}}}; - ir_context->module()->AddType(MakeUnique( - ir_context, SpvOpTypeInt, 0, message_.fresh_id(), in_operands)); - fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_id()); + fuzzerutil::AddIntegerType(ir_context, message_.fresh_id(), message_.width(), + message_.is_signed()); // We have added an instruction to the module, so need to be careful about the // validity of existing analyses. ir_context->InvalidateAnalysesExceptFor( diff --git a/source/fuzz/transformation_add_type_struct.cpp b/source/fuzz/transformation_add_type_struct.cpp index 6ce5ea11c..a7345a17a 100644 --- a/source/fuzz/transformation_add_type_struct.cpp +++ b/source/fuzz/transformation_add_type_struct.cpp @@ -50,13 +50,10 @@ bool TransformationAddTypeStruct::IsApplicable( void TransformationAddTypeStruct::Apply( opt::IRContext* ir_context, TransformationContext* /*unused*/) const { - opt::Instruction::OperandList in_operands; - for (auto member_type : message_.member_type_id()) { - in_operands.push_back({SPV_OPERAND_TYPE_ID, {member_type}}); - } - ir_context->module()->AddType(MakeUnique( - ir_context, SpvOpTypeStruct, 0, message_.fresh_id(), in_operands)); - fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_id()); + fuzzerutil::AddStructType( + ir_context, message_.fresh_id(), + std::vector(message_.member_type_id().begin(), + message_.member_type_id().end())); // We have added an instruction to the module, so need to be careful about the // validity of existing analyses. ir_context->InvalidateAnalysesExceptFor( diff --git a/source/fuzz/transformation_add_type_vector.cpp b/source/fuzz/transformation_add_type_vector.cpp index f7b2fb59f..10a622412 100644 --- a/source/fuzz/transformation_add_type_vector.cpp +++ b/source/fuzz/transformation_add_type_vector.cpp @@ -46,13 +46,9 @@ bool TransformationAddTypeVector::IsApplicable( void TransformationAddTypeVector::Apply( opt::IRContext* ir_context, TransformationContext* /*unused*/) const { - opt::Instruction::OperandList in_operands; - in_operands.push_back({SPV_OPERAND_TYPE_ID, {message_.component_type_id()}}); - in_operands.push_back( - {SPV_OPERAND_TYPE_LITERAL_INTEGER, {message_.component_count()}}); - ir_context->module()->AddType(MakeUnique( - ir_context, SpvOpTypeVector, 0, message_.fresh_id(), in_operands)); - fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_id()); + fuzzerutil::AddVectorType(ir_context, message_.fresh_id(), + message_.component_type_id(), + message_.component_count()); // We have added an instruction to the module, so need to be careful about the // validity of existing analyses. ir_context->InvalidateAnalysesExceptFor(