spirv-fuzz: Fix usages of irrelevant constants (#3566)

Part of #3177.
This commit is contained in:
Vasyl Teliman 2020-07-22 21:03:58 +03:00 committed by GitHub
parent a0d8dc2b44
commit bc2f78b7d9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
21 changed files with 197 additions and 53 deletions

View File

@ -53,11 +53,12 @@ void FuzzerPassAddSynonyms::Apply() {
// Select all instructions that can be used to create a synonym to.
auto available_instructions = FindAvailableInstructions(
function, block, inst_it,
[synonym_type](opt::IRContext* ir_context, opt::Instruction* inst) {
[synonym_type, this](opt::IRContext* ir_context,
opt::Instruction* inst) {
// Check that we can create a synonym to |inst| as described by
// the |synonym_type| and insert it before |inst_it|.
return TransformationAddSynonym::IsInstructionValid(
ir_context, inst, synonym_type);
ir_context, *GetTransformationContext(), inst, synonym_type);
});
if (available_instructions.empty()) {

View File

@ -60,10 +60,10 @@ void FuzzerPassAddVectorShuffleInstructions::Apply() {
std::vector<opt::Instruction*> vector_instructions =
FindAvailableInstructions(
function, block, instruction_iterator,
[instruction_descriptor](
[this, instruction_descriptor](
opt::IRContext* ir_context,
opt::Instruction* instruction) -> bool {
if (!instruction->type_id()) {
if (!instruction->result_id() || !instruction->type_id()) {
return false;
}
@ -73,6 +73,18 @@ void FuzzerPassAddVectorShuffleInstructions::Apply() {
return false;
}
if (!GetTransformationContext()
->GetFactManager()
->IdIsIrrelevant(instruction->result_id()) &&
!fuzzerutil::CanMakeSynonymOf(ir_context,
*GetTransformationContext(),
instruction)) {
// If the id is irrelevant, we can use it since it will not
// participate in DataSynonym fact. Otherwise, we should be
// able to produce a synonym out of the id.
return false;
}
return fuzzerutil::IdIsAvailableBeforeInstruction(
ir_context,
FindInstruction(instruction_descriptor, ir_context),

View File

@ -142,6 +142,9 @@ void FuzzerPassApplyIdSynonyms::Apply() {
: parent_block->terminator();
}
assert(!GetTransformationContext()->GetFactManager()->IdIsIrrelevant(
synonym_to_try->object()) &&
"Irrelevant ids can't participate in DataSynonym facts");
ApplyTransformation(TransformationCompositeExtract(
MakeInstructionDescriptor(GetIRContext(),
instruction_to_insert_before),

View File

@ -14,12 +14,10 @@
#include "source/fuzz/fuzzer_pass_construct_composites.h"
#include <cmath>
#include <memory>
#include "source/fuzz/fuzzer_util.h"
#include "source/fuzz/transformation_composite_construct.h"
#include "source/util/make_unique.h"
namespace spvtools {
namespace fuzz {
@ -67,8 +65,23 @@ void FuzzerPassConstructComposites::Apply() {
// program point) and suitable for making a synonym of, associate it
// with the id of its result type.
TypeIdToInstructions type_id_to_available_instructions;
for (auto instruction : FindAvailableInstructions(
function, block, inst_it, fuzzerutil::CanMakeSynonymOf)) {
auto available_instructions = FindAvailableInstructions(
function, block, inst_it,
[this](opt::IRContext* ir_context, opt::Instruction* inst) {
if (!inst->result_id() || !inst->type_id()) {
return false;
}
// If the id is irrelevant, we can use it since it will not
// participate in DataSynonym fact. Otherwise, we should be able
// to produce a synonym out of the id.
return GetTransformationContext()
->GetFactManager()
->IdIsIrrelevant(inst->result_id()) ||
fuzzerutil::CanMakeSynonymOf(
ir_context, *GetTransformationContext(), inst);
});
for (auto instruction : available_instructions) {
RecordAvailableInstruction(instruction,
&type_id_to_available_instructions);
}

View File

@ -55,8 +55,12 @@ void FuzzerPassCopyObjects::Apply() {
}
std::vector<opt::Instruction*> relevant_instructions =
FindAvailableInstructions(function, block, inst_it,
fuzzerutil::CanMakeSynonymOf);
FindAvailableInstructions(
function, block, inst_it,
[this](opt::IRContext* ir_context, opt::Instruction* inst) {
return fuzzerutil::CanMakeSynonymOf(
ir_context, *GetTransformationContext(), inst);
});
// At this point, |relevant_instructions| contains all the instructions
// we might think of copying.

View File

@ -14,6 +14,7 @@
// limitations under the License.
#include "source/fuzz/fuzzer_pass_interchange_zero_like_constants.h"
#include "source/fuzz/fuzzer_util.h"
#include "source/fuzz/id_use_descriptor.h"
#include "source/fuzz/transformation_record_synonymous_constants.h"
@ -83,13 +84,21 @@ void FuzzerPassInterchangeZeroLikeConstants::Apply() {
for (auto constant : GetIRContext()->GetConstants()) {
uint32_t constant_id = constant->result_id();
uint32_t toggled_id = FindOrCreateToggledConstant(constant);
if (GetTransformationContext()->GetFactManager()->IdIsIrrelevant(
constant_id)) {
continue;
}
uint32_t toggled_id = FindOrCreateToggledConstant(constant);
if (!toggled_id) {
// Not a zero-like constant
continue;
}
assert(!GetTransformationContext()->GetFactManager()->IdIsIrrelevant(
toggled_id) &&
"FindOrCreateToggledConstant can't produce an irrelevant id");
// Record synonymous constants
ApplyTransformation(
TransformationRecordSynonymousConstants(constant_id, toggled_id));

View File

@ -80,7 +80,7 @@ void FuzzerPassPushIdsThroughVariables::Apply() {
std::vector<opt::Instruction*> value_instructions =
FindAvailableInstructions(
function, block, instruction_iterator,
[basic_type_id, instruction_descriptor](
[this, basic_type_id, instruction_descriptor](
opt::IRContext* ir_context,
opt::Instruction* instruction) -> bool {
if (!instruction->result_id() || !instruction->type_id()) {
@ -91,6 +91,12 @@ void FuzzerPassPushIdsThroughVariables::Apply() {
return false;
}
if (!fuzzerutil::CanMakeSynonymOf(ir_context,
*GetTransformationContext(),
instruction)) {
return false;
}
return fuzzerutil::IdIsAvailableBeforeInstruction(
ir_context,
FindInstruction(instruction_descriptor, ir_context),

View File

@ -248,7 +248,9 @@ bool CanInsertOpcodeBeforeInstruction(
return opcode == SpvOpPhi || instruction_in_block->opcode() != SpvOpPhi;
}
bool CanMakeSynonymOf(opt::IRContext* ir_context, opt::Instruction* inst) {
bool CanMakeSynonymOf(opt::IRContext* ir_context,
const TransformationContext& transformation_context,
opt::Instruction* inst) {
if (inst->opcode() == SpvOpSampledImage) {
// The SPIR-V data rules say that only very specific instructions may
// may consume the result id of an OpSampledImage, and this excludes the
@ -259,6 +261,11 @@ bool CanMakeSynonymOf(opt::IRContext* ir_context, opt::Instruction* inst) {
// We can only make a synonym of an instruction that generates an id.
return false;
}
if (transformation_context.GetFactManager()->IdIsIrrelevant(
inst->result_id())) {
// An irrelevant id can't be a synonym of anything.
return false;
}
if (!inst->type_id()) {
// We can only make a synonym of an instruction that has a type.
return false;

View File

@ -93,7 +93,11 @@ bool CanInsertOpcodeBeforeInstruction(
SpvOp opcode, const opt::BasicBlock::iterator& instruction_in_block);
// Determines whether it is OK to make a synonym of |inst|.
bool CanMakeSynonymOf(opt::IRContext* ir_context, opt::Instruction* inst);
// |transformation_context| is used to verify that the result id of |inst|
// does not participate in IdIsIrrelevant fact.
bool CanMakeSynonymOf(opt::IRContext* ir_context,
const TransformationContext& transformation_context,
opt::Instruction* inst);
// Determines whether the given type is a composite; that is: an array, matrix,
// struct or vector.

View File

@ -56,7 +56,8 @@ bool TransformationAddSynonym::IsApplicable(
}
// Check that we can apply |synonym_type| to |result_id|.
if (!IsInstructionValid(ir_context, synonym, message_.synonym_type())) {
if (!IsInstructionValid(ir_context, transformation_context, synonym,
message_.synonym_type())) {
return false;
}
@ -124,7 +125,8 @@ protobufs::Transformation TransformationAddSynonym::ToMessage() const {
}
bool TransformationAddSynonym::IsInstructionValid(
opt::IRContext* ir_context, opt::Instruction* inst,
opt::IRContext* ir_context,
const TransformationContext& transformation_context, opt::Instruction* inst,
protobufs::TransformationAddSynonym::SynonymType synonym_type) {
// Instruction must have a result id, type id. We skip OpUndef and
// OpConstantNull.
@ -133,8 +135,10 @@ bool TransformationAddSynonym::IsInstructionValid(
return false;
}
// TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3177):
// We can't create a synonym of an irrelevant id.
if (!fuzzerutil::CanMakeSynonymOf(ir_context, transformation_context, inst)) {
return false;
}
switch (synonym_type) {
case protobufs::TransformationAddSynonym::ADD_ZERO:
case protobufs::TransformationAddSynonym::SUB_ZERO:
@ -151,7 +155,9 @@ bool TransformationAddSynonym::IsInstructionValid(
return type->AsInteger() || type->AsFloat();
}
case protobufs::TransformationAddSynonym::COPY_OBJECT:
return fuzzerutil::CanMakeSynonymOf(ir_context, inst);
// All checks for OpCopyObject are handled by
// fuzzerutil::CanMakeSynonymOf.
return true;
case protobufs::TransformationAddSynonym::LOGICAL_AND:
case protobufs::TransformationAddSynonym::LOGICAL_OR: {
// The instruction must be either a scalar or a vector of booleans.

View File

@ -35,6 +35,7 @@ class TransformationAddSynonym : public Transformation {
const protobufs::InstructionDescriptor& insert_before);
// - |result_id| must be a valid result id of some instruction in the module.
// - |result_id| may not be an irrelevant id.
// - |synonym_type| is a type of the synonymous instruction that will be
// created.
// - |synonym_fresh_id| is a fresh id.
@ -57,7 +58,9 @@ class TransformationAddSynonym : public Transformation {
// Returns true if we can create a synonym of |inst| according to the
// |synonym_type|.
static bool IsInstructionValid(
opt::IRContext* ir_context, opt::Instruction* inst,
opt::IRContext* ir_context,
const TransformationContext& transformation_context,
opt::Instruction* inst,
protobufs::TransformationAddSynonym::SynonymType synonym_type);
// Returns true if |synonym_type| requires an additional constant instruction

View File

@ -40,7 +40,8 @@ TransformationCompositeConstruct::TransformationCompositeConstruct(
}
bool TransformationCompositeConstruct::IsApplicable(
opt::IRContext* ir_context, const TransformationContext& /*unused*/) const {
opt::IRContext* ir_context,
const TransformationContext& transformation_context) const {
if (!fuzzerutil::IsFreshId(ir_context, message_.fresh_id())) {
// We require the id for the composite constructor to be unused.
return false;
@ -87,7 +88,20 @@ bool TransformationCompositeConstruct::IsApplicable(
// Now check whether every component being used to initialize the composite is
// available at the desired program point.
for (auto& component : message_.component()) {
for (auto component : message_.component()) {
auto* inst = ir_context->get_def_use_mgr()->GetDef(component);
if (!inst) {
return false;
}
// We should be able to create a synonym of |component| if it's not
// irrelevant.
if (!transformation_context.GetFactManager()->IdIsIrrelevant(component) &&
!fuzzerutil::CanMakeSynonymOf(ir_context, transformation_context,
inst)) {
return false;
}
if (!fuzzerutil::IdIsAvailableBeforeInstruction(ir_context, insert_before,
component)) {
return false;
@ -130,6 +144,11 @@ void TransformationCompositeConstruct::Apply(
ir_context->get_type_mgr()->GetType(message_.composite_type_id());
uint32_t index = 0;
for (auto component : message_.component()) {
if (transformation_context->GetFactManager()->IdIsIrrelevant(component)) {
// Irrelevant ids do not participate in DataSynonym facts.
continue;
}
auto component_type = ir_context->get_type_mgr()->GetType(
ir_context->get_def_use_mgr()->GetDef(component)->type_id());
if (composite_type->AsVector() && component_type->AsVector()) {

View File

@ -40,7 +40,8 @@ TransformationCompositeExtract::TransformationCompositeExtract(
}
bool TransformationCompositeExtract::IsApplicable(
opt::IRContext* ir_context, const TransformationContext& /*unused*/) const {
opt::IRContext* ir_context,
const TransformationContext& transformation_context) const {
if (!fuzzerutil::IsFreshId(ir_context, message_.fresh_id())) {
return false;
}
@ -54,6 +55,14 @@ bool TransformationCompositeExtract::IsApplicable(
if (!composite_instruction) {
return false;
}
if (!transformation_context.GetFactManager()->IdIsIrrelevant(
message_.composite_id()) &&
!fuzzerutil::CanMakeSynonymOf(ir_context, transformation_context,
composite_instruction)) {
// |composite_id| will participate in DataSynonym facts. Thus, it can't be
// an irrelevant id.
return false;
}
if (auto block = ir_context->get_instr_block(composite_instruction)) {
if (composite_instruction == instruction_to_insert_before ||
!ir_context->GetDominatorAnalysis(block->GetParent())
@ -105,17 +114,20 @@ void TransformationCompositeExtract::Apply(
// Add the fact that the id storing the extracted element is synonymous with
// the index into the structure.
std::vector<uint32_t> indices;
for (auto an_index : message_.index()) {
indices.push_back(an_index);
if (!transformation_context->GetFactManager()->IdIsIrrelevant(
message_.composite_id())) {
std::vector<uint32_t> indices;
for (auto an_index : message_.index()) {
indices.push_back(an_index);
}
protobufs::DataDescriptor data_descriptor_for_extracted_element =
MakeDataDescriptor(message_.composite_id(), std::move(indices));
protobufs::DataDescriptor data_descriptor_for_result_id =
MakeDataDescriptor(message_.fresh_id(), {});
transformation_context->GetFactManager()->AddFactDataSynonym(
data_descriptor_for_extracted_element, data_descriptor_for_result_id,
ir_context);
}
protobufs::DataDescriptor data_descriptor_for_extracted_element =
MakeDataDescriptor(message_.composite_id(), std::move(indices));
protobufs::DataDescriptor data_descriptor_for_result_id =
MakeDataDescriptor(message_.fresh_id(), {});
transformation_context->GetFactManager()->AddFactDataSynonym(
data_descriptor_for_extracted_element, data_descriptor_for_result_id,
ir_context);
}
protobufs::Transformation TransformationCompositeExtract::ToMessage() const {

View File

@ -48,7 +48,8 @@ class TransformationCompositeExtract : public Transformation {
// Adds an OpCompositeConstruct instruction before the instruction identified
// by |message_.instruction_to_insert_before|, that extracts from
// |message_.composite_id| via indices |message_.index| into
// |message_.fresh_id|. Generates a data synonym fact relating
// |message_.fresh_id|. If |composite_id| is not an irrelevant id,
// generates a data synonym fact relating
// |message_.fresh_id| to the extracted element.
void Apply(opt::IRContext* ir_context,
TransformationContext* transformation_context) const override;

View File

@ -37,7 +37,8 @@ TransformationEquationInstruction::TransformationEquationInstruction(
}
bool TransformationEquationInstruction::IsApplicable(
opt::IRContext* ir_context, const TransformationContext& /*unused*/) const {
opt::IRContext* ir_context,
const TransformationContext& transformation_context) const {
// The result id must be fresh.
if (!fuzzerutil::IsFreshId(ir_context, message_.fresh_id())) {
return false;
@ -59,6 +60,9 @@ bool TransformationEquationInstruction::IsApplicable(
if (inst->opcode() == SpvOpUndef) {
return false;
}
if (transformation_context.GetFactManager()->IdIsIrrelevant(id)) {
return false;
}
if (!fuzzerutil::IdIsAvailableBeforeInstruction(ir_context, insert_before,
id)) {
return false;

View File

@ -38,7 +38,8 @@ TransformationPushIdThroughVariable::TransformationPushIdThroughVariable(
}
bool TransformationPushIdThroughVariable::IsApplicable(
opt::IRContext* ir_context, const TransformationContext& /*unused*/) const {
opt::IRContext* ir_context,
const TransformationContext& transformation_context) const {
// |message_.value_synonym_id| and |message_.variable_id| must be fresh.
if (!fuzzerutil::IsFreshId(ir_context, message_.value_synonym_id()) ||
!fuzzerutil::IsFreshId(ir_context, message_.variable_id())) {
@ -73,6 +74,12 @@ bool TransformationPushIdThroughVariable::IsApplicable(
return false;
}
// |value_id| may not be an irrelevant id.
if (transformation_context.GetFactManager()->IdIsIrrelevant(
message_.value_id())) {
return false;
}
// A pointer type instruction pointing to the value type must be defined.
auto pointer_type_id = fuzzerutil::MaybeGetPointerType(
ir_context, value_instruction->type_id(),

View File

@ -36,6 +36,7 @@ class TransformationPushIdThroughVariable : public Transformation {
// - |message_.value_id| must be an instruction result id that has the same
// type as the pointee type of |message_.pointer_id|
// - |value_id| may not be an irrelevant id.
// - |message_.value_synonym_id| must be fresh
// - |message_.variable_id| must be fresh
// - |message_.variable_storage_class| must be either StorageClassPrivate or

View File

@ -32,12 +32,19 @@ TransformationRecordSynonymousConstants::
bool TransformationRecordSynonymousConstants::IsApplicable(
opt::IRContext* ir_context,
const TransformationContext& /* unused */) const {
const TransformationContext& transformation_context) const {
// The ids must be different
if (message_.constant1_id() == message_.constant2_id()) {
return false;
}
if (transformation_context.GetFactManager()->IdIsIrrelevant(
message_.constant1_id()) ||
transformation_context.GetFactManager()->IdIsIrrelevant(
message_.constant2_id())) {
return false;
}
return AreEquivalentConstants(ir_context, message_.constant1_id(),
message_.constant2_id());
}
@ -45,17 +52,10 @@ bool TransformationRecordSynonymousConstants::IsApplicable(
void TransformationRecordSynonymousConstants::Apply(
opt::IRContext* ir_context,
TransformationContext* transformation_context) const {
protobufs::FactDataSynonym fact_data_synonym;
// Define the two equivalent data descriptors (just containing the ids)
*fact_data_synonym.mutable_data1() =
MakeDataDescriptor(message_.constant1_id(), {});
*fact_data_synonym.mutable_data2() =
MakeDataDescriptor(message_.constant2_id(), {});
protobufs::Fact fact;
*fact.mutable_data_synonym_fact() = fact_data_synonym;
// Add the fact to the fact manager
transformation_context->GetFactManager()->AddFact(fact, ir_context);
transformation_context->GetFactManager()->AddFactDataSynonym(
MakeDataDescriptor(message_.constant1_id(), {}),
MakeDataDescriptor(message_.constant2_id(), {}), ir_context);
}
protobufs::Transformation TransformationRecordSynonymousConstants::ToMessage()
@ -67,11 +67,14 @@ protobufs::Transformation TransformationRecordSynonymousConstants::ToMessage()
bool TransformationRecordSynonymousConstants::AreEquivalentConstants(
opt::IRContext* ir_context, uint32_t constant_id1, uint32_t constant_id2) {
opt::Instruction* def_1 = ir_context->get_def_use_mgr()->GetDef(constant_id1);
opt::Instruction* def_2 = ir_context->get_def_use_mgr()->GetDef(constant_id2);
const auto* def_1 = ir_context->get_def_use_mgr()->GetDef(constant_id1);
const auto* def_2 = ir_context->get_def_use_mgr()->GetDef(constant_id2);
// Check that the definitions exist
assert(def_1 && def_2 && "The constant ids must exist in the module.");
if (!def_1 || !def_2) {
// We don't use an assertion since otherwise the shrinker fails.
return false;
}
// The type ids must be the same
// TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3536): Somehow

View File

@ -41,6 +41,7 @@ class TransformationRecordSynonymousConstants : public Transformation {
// - if both of them represent zero-like values of the same type
// - if they are composite constants with the same type and their
// components are pairwise equivalent.
// - |constant1_id| and |constant2_id| may not be irrelevant.
bool IsApplicable(
opt::IRContext* ir_context,
const TransformationContext& transformation_context) const override;

View File

@ -39,7 +39,8 @@ TransformationVectorShuffle::TransformationVectorShuffle(
}
bool TransformationVectorShuffle::IsApplicable(
opt::IRContext* ir_context, const TransformationContext& /*unused*/) const {
opt::IRContext* ir_context,
const TransformationContext& transformation_context) const {
// The fresh id must not already be in use.
if (!fuzzerutil::IsFreshId(ir_context, message_.fresh_id())) {
return false;
@ -56,12 +57,26 @@ bool TransformationVectorShuffle::IsApplicable(
if (!vector1_instruction || !vector1_instruction->type_id()) {
return false;
}
// We should be able to create a synonym of |vector1| if it's not irrelevant.
if (!transformation_context.GetFactManager()->IdIsIrrelevant(
message_.vector1()) &&
!fuzzerutil::CanMakeSynonymOf(ir_context, transformation_context,
vector1_instruction)) {
return false;
}
// The second vector must be an instruction with a type id
auto vector2_instruction =
ir_context->get_def_use_mgr()->GetDef(message_.vector2());
if (!vector2_instruction || !vector2_instruction->type_id()) {
return false;
}
// We should be able to create a synonym of |vector2| if it's not irrelevant.
if (!transformation_context.GetFactManager()->IdIsIrrelevant(
message_.vector2()) &&
!fuzzerutil::CanMakeSynonymOf(ir_context, transformation_context,
vector2_instruction)) {
return false;
}
auto vector1_type =
ir_context->get_type_mgr()->GetType(vector1_instruction->type_id());
// The first vector instruction's type must actually be a vector type.
@ -161,9 +176,21 @@ void TransformationVectorShuffle::Apply(
// |component| refers.
if (component <
GetVectorType(ir_context, message_.vector1())->element_count()) {
// Irrelevant id cannot participate in DataSynonym facts.
if (transformation_context->GetFactManager()->IdIsIrrelevant(
message_.vector1())) {
continue;
}
descriptor_for_source_component =
MakeDataDescriptor(message_.vector1(), {component});
} else {
// Irrelevant id cannot participate in DataSynonym facts.
if (transformation_context->GetFactManager()->IdIsIrrelevant(
message_.vector2())) {
continue;
}
auto index_into_vector_2 =
component -
GetVectorType(ir_context, message_.vector1())->element_count();

View File

@ -19,7 +19,6 @@
#include "source/fuzz/transformation.h"
#include "source/fuzz/transformation_context.h"
#include "source/opt/ir_context.h"
#include "source/opt/types.h"
namespace spvtools {
@ -59,7 +58,9 @@ class TransformationVectorShuffle : public Transformation {
// from which it came (with undefined components being ignored). If the
// result vector is a contiguous sub-range of one of the input vectors, a
// fact is added to record that |message_.fresh_id| is synonymous with this
// sub-range.
// sub-range. DataSynonym facts are added only for non-irrelevant vectors
// (e.g. if |vector1| is irrelevant but |vector2| is not, synonyms will be
// created for |vector1| but not |vector2|).
void Apply(opt::IRContext* ir_context,
TransformationContext* transformation_context) const override;