spirv-fuzz: Do not add synonyms involving irrelevant ids (#3890)

Fixes #3886.
This commit is contained in:
Alastair Donaldson 2020-10-08 22:34:39 +01:00 committed by GitHub
parent d52f79122a
commit 3602287858
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
25 changed files with 1054 additions and 254 deletions

View File

@ -94,6 +94,9 @@ void TransformationAddBitInstructionSynonym::Apply(
auto bit_instruction =
ir_context->get_def_use_mgr()->GetDef(message_.instruction_result_id());
// Use an appropriate helper function to add the new instruction and new
// synonym fact. The helper function should take care of invalidating
// analyses before adding facts.
switch (bit_instruction->opcode()) {
case SpvOpBitwiseOr:
case SpvOpBitwiseXor:
@ -106,8 +109,6 @@ void TransformationAddBitInstructionSynonym::Apply(
assert(false && "Should be unreachable.");
break;
}
ir_context->InvalidateAnalysesExceptFor(opt::IRContext::kAnalysisNone);
}
protobufs::Transformation TransformationAddBitInstructionSynonym::ToMessage()
@ -223,11 +224,19 @@ void TransformationAddBitInstructionSynonym::AddOpBitwiseOrOpNotSynonym(
ir_context->InvalidateAnalysesExceptFor(opt::IRContext::kAnalysisNone);
// We only add a synonym fact if the bit instruction is not irrelevant, and if
// the new result id we would make it synonymous with is not irrelevant. (It
// could be irrelevant if we are in a dead block.)
if (!transformation_context->GetFactManager()->IdIsIrrelevant(
bit_instruction->result_id()) &&
!transformation_context->GetFactManager()->IdIsIrrelevant(
bit_insert.result_id())) {
// Adds the fact that the last |bit_insert| instruction is synonymous of
// |bit_instruction|.
transformation_context->GetFactManager()->AddFactDataSynonym(
MakeDataDescriptor(bit_insert.result_id(), {}),
MakeDataDescriptor(bit_instruction->result_id(), {}));
}
}
std::unordered_set<uint32_t>

View File

@ -53,8 +53,8 @@ bool TransformationAddLoopToCreateIntConstantSynonym::IsApplicable(
opt::IRContext* ir_context,
const TransformationContext& transformation_context) const {
// Check that |message_.constant_id|, |message_.initial_val_id| and
// |message_.step_val_id| are existing constants.
// |message_.step_val_id| are existing constants, and that their values are
// not irrelevant.
auto constant = ir_context->get_constant_mgr()->FindDeclaredConstant(
message_.constant_id());
auto initial_val = ir_context->get_constant_mgr()->FindDeclaredConstant(
@ -65,6 +65,14 @@ bool TransformationAddLoopToCreateIntConstantSynonym::IsApplicable(
if (!constant || !initial_val || !step_val) {
return false;
}
if (transformation_context.GetFactManager()->IdIsIrrelevant(
message_.constant_id()) ||
transformation_context.GetFactManager()->IdIsIrrelevant(
message_.initial_val_id()) ||
transformation_context.GetFactManager()->IdIsIrrelevant(
message_.step_val_id())) {
return false;
}
// Check that the type of |constant| is integer scalar or vector with integer
// components.
@ -101,12 +109,15 @@ bool TransformationAddLoopToCreateIntConstantSynonym::IsApplicable(
return false;
}
// |message_.num_iterations_id| is an integer constant with bit width 32.
// |message_.num_iterations_id| must be a non-irrelevant integer constant with
// bit width 32.
auto num_iterations = ir_context->get_constant_mgr()->FindDeclaredConstant(
message_.num_iterations_id());
if (!num_iterations || !num_iterations->AsIntConstant() ||
num_iterations->type()->AsInteger()->width() != 32) {
num_iterations->type()->AsInteger()->width() != 32 ||
transformation_context.GetFactManager()->IdIsIrrelevant(
message_.num_iterations_id())) {
return false;
}
@ -181,6 +192,13 @@ bool TransformationAddLoopToCreateIntConstantSynonym::IsApplicable(
return false;
}
// Check that the block is not dead. If it is then the new loop would be
// dead and the data it computes would be irrelevant, so we would not be able
// to make a synonym.
if (transformation_context.GetFactManager()->BlockIsDead(block->id())) {
return false;
}
// Check that the block is not a merge block.
if (ir_context->GetStructuredCFGAnalysis()->IsMergeBlock(block->id())) {
return false;

View File

@ -45,6 +45,7 @@ class TransformationAddLoopToCreateIntConstantSynonym : public Transformation {
// - |message_.block_after_loop_id| is the label of a block which has a single
// predecessor and which is not a merge block, a continue block or a loop
// header.
// - |message_.block_after_loop_id| must not be a dead block.
// - |message_.additional_block_id| is either 0 or a valid fresh id, distinct
// from the other fresh ids.
// - All of the other parameters are valid fresh ids.

View File

@ -34,9 +34,11 @@ TransformationAddOpPhiSynonym::TransformationAddOpPhiSynonym(
bool TransformationAddOpPhiSynonym::IsApplicable(
opt::IRContext* ir_context,
const TransformationContext& transformation_context) const {
// Check that |message_.block_id| is a block label id.
// Check that |message_.block_id| is a block label id, and that it is not
// dead.
auto block = fuzzerutil::MaybeFindBlock(ir_context, message_.block_id());
if (!block) {
if (!block ||
transformation_context.GetFactManager()->BlockIsDead(block->id())) {
return false;
}

View File

@ -30,6 +30,7 @@ class TransformationAddOpPhiSynonym : public Transformation {
// - |message_.block_id| is the label of a block with at least one
// predecessor.
// - |message_.block_id| must not be a dead block.
// - |message_.pred_to_id| contains a mapping from each of the predecessors of
// the block to an id that is available at the end of the predecessor.
// - All the ids corresponding to a predecessor in |message_.pred_to_id|:

View File

@ -40,8 +40,7 @@ TransformationCompositeConstruct::TransformationCompositeConstruct(
}
bool TransformationCompositeConstruct::IsApplicable(
opt::IRContext* ir_context,
const TransformationContext& transformation_context) const {
opt::IRContext* ir_context, const TransformationContext& /*unused*/) const {
if (!fuzzerutil::IsFreshId(ir_context, message_.fresh_id())) {
// We require the id for the composite constructor to be unused.
return false;
@ -94,14 +93,6 @@ bool TransformationCompositeConstruct::IsApplicable(
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;
@ -136,48 +127,7 @@ void TransformationCompositeConstruct::Apply(
fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_id());
ir_context->InvalidateAnalysesExceptFor(opt::IRContext::kAnalysisNone);
// Inform the fact manager that we now have new synonyms: every component of
// the composite is synonymous with the id used to construct that component,
// except in the case of a vector where a single vector id can span multiple
// components.
auto composite_type =
ir_context->get_type_mgr()->GetType(message_.composite_type_id());
uint32_t index = 0;
for (auto component : message_.component()) {
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()) {
// The case where the composite being constructed is a vector and the
// component provided for construction is also a vector is special. It
// requires adding a synonym fact relating each element of the sub-vector
// to the corresponding element of the composite being constructed.
assert(component_type->AsVector()->element_type() ==
composite_type->AsVector()->element_type());
assert(component_type->AsVector()->element_count() <
composite_type->AsVector()->element_count());
for (uint32_t subvector_index = 0;
subvector_index < component_type->AsVector()->element_count();
subvector_index++) {
if (!transformation_context->GetFactManager()->IdIsIrrelevant(
component)) {
transformation_context->GetFactManager()->AddFactDataSynonym(
MakeDataDescriptor(component, {subvector_index}),
MakeDataDescriptor(message_.fresh_id(), {index}));
}
index++;
}
} else {
// The other cases are simple: the component is made directly synonymous
// with the element of the composite being constructed.
if (!transformation_context->GetFactManager()->IdIsIrrelevant(
component)) {
transformation_context->GetFactManager()->AddFactDataSynonym(
MakeDataDescriptor(component, {}),
MakeDataDescriptor(message_.fresh_id(), {index}));
}
index++;
}
}
AddDataSynonymFacts(ir_context, transformation_context);
}
bool TransformationCompositeConstruct::ComponentsForArrayConstructionAreOK(
@ -315,5 +265,58 @@ std::unordered_set<uint32_t> TransformationCompositeConstruct::GetFreshIds()
return {message_.fresh_id()};
}
void TransformationCompositeConstruct::AddDataSynonymFacts(
opt::IRContext* ir_context,
TransformationContext* transformation_context) const {
// If the result id of the composite we are constructing is irrelevant (e.g.
// because it is in a dead block) then we do not make any synonyms.
if (transformation_context->GetFactManager()->IdIsIrrelevant(
message_.fresh_id())) {
return;
}
// Inform the fact manager that we now have new synonyms: every component of
// the composite is synonymous with the id used to construct that component
// (so long as it is legitimate to create a synonym from that id), except in
// the case of a vector where a single vector id can span multiple components.
auto composite_type =
ir_context->get_type_mgr()->GetType(message_.composite_type_id());
uint32_t index = 0;
for (auto component : message_.component()) {
if (!fuzzerutil::CanMakeSynonymOf(
ir_context, *transformation_context,
ir_context->get_def_use_mgr()->GetDef(component))) {
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()) {
// The case where the composite being constructed is a vector and the
// component provided for construction is also a vector is special. It
// requires adding a synonym fact relating each element of the sub-vector
// to the corresponding element of the composite being constructed.
assert(component_type->AsVector()->element_type() ==
composite_type->AsVector()->element_type());
assert(component_type->AsVector()->element_count() <
composite_type->AsVector()->element_count());
for (uint32_t subvector_index = 0;
subvector_index < component_type->AsVector()->element_count();
subvector_index++) {
transformation_context->GetFactManager()->AddFactDataSynonym(
MakeDataDescriptor(component, {subvector_index}),
MakeDataDescriptor(message_.fresh_id(), {index}));
index++;
}
} else {
// The other cases are simple: the component is made directly synonymous
// with the element of the composite being constructed.
transformation_context->GetFactManager()->AddFactDataSynonym(
MakeDataDescriptor(component, {}),
MakeDataDescriptor(message_.fresh_id(), {index}));
index++;
}
}
}
} // namespace fuzz
} // namespace spvtools

View File

@ -58,6 +58,10 @@ class TransformationCompositeConstruct : public Transformation {
// |message_.base_instruction_id| and |message_.offset|. The instruction
// creates a composite of type |message_.composite_type_id| using the ids of
// |message_.component|.
//
// Synonym facts are added between the elements of the resulting composite
// and the components used to construct it, as long as the associated ids
// support synonym creation.
void Apply(opt::IRContext* ir_context,
TransformationContext* transformation_context) const override;
@ -86,6 +90,11 @@ class TransformationCompositeConstruct : public Transformation {
opt::IRContext* ir_context,
const opt::analysis::Vector& vector_type) const;
// Helper method for adding data synonym facts when applying the
// transformation to |ir_context| and |transformation_context|.
void AddDataSynonymFacts(opt::IRContext* ir_context,
TransformationContext* transformation_context) const;
protobufs::TransformationCompositeConstruct message_;
};

View File

@ -41,8 +41,7 @@ TransformationCompositeExtract::TransformationCompositeExtract(
}
bool TransformationCompositeExtract::IsApplicable(
opt::IRContext* ir_context,
const TransformationContext& transformation_context) const {
opt::IRContext* ir_context, const TransformationContext& /*unused*/) const {
if (!fuzzerutil::IsFreshId(ir_context, message_.fresh_id())) {
return false;
}
@ -56,14 +55,6 @@ 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())
@ -113,6 +104,33 @@ void TransformationCompositeExtract::Apply(
ir_context->InvalidateAnalysesExceptFor(
opt::IRContext::Analysis::kAnalysisNone);
AddDataSynonymFacts(ir_context, transformation_context);
}
protobufs::Transformation TransformationCompositeExtract::ToMessage() const {
protobufs::Transformation result;
*result.mutable_composite_extract() = message_;
return result;
}
std::unordered_set<uint32_t> TransformationCompositeExtract::GetFreshIds()
const {
return {message_.fresh_id()};
}
void TransformationCompositeExtract::AddDataSynonymFacts(
opt::IRContext* ir_context,
TransformationContext* transformation_context) const {
// Don't add synonyms if the composite being extracted from is not suitable,
// or if the result id into which we are extracting is irrelevant.
if (!fuzzerutil::CanMakeSynonymOf(
ir_context, *transformation_context,
ir_context->get_def_use_mgr()->GetDef(message_.composite_id())) ||
transformation_context->GetFactManager()->IdIsIrrelevant(
message_.fresh_id())) {
return;
}
// Add the fact that the id storing the extracted element is synonymous with
// the index into the structure.
if (!transformation_context->GetFactManager()->IdIsIrrelevant(
@ -130,16 +148,5 @@ void TransformationCompositeExtract::Apply(
}
}
protobufs::Transformation TransformationCompositeExtract::ToMessage() const {
protobufs::Transformation result;
*result.mutable_composite_extract() = message_;
return result;
}
std::unordered_set<uint32_t> TransformationCompositeExtract::GetFreshIds()
const {
return {message_.fresh_id()};
}
} // namespace fuzz
} // namespace spvtools

View File

@ -49,9 +49,11 @@ 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|. If |composite_id| is not an irrelevant id,
// generates a data synonym fact relating
// |message_.fresh_id| to the extracted element.
// |message_.fresh_id|.
//
// Adds a synonym fact associating |message_.fresh_id| with the relevant
// element of |message_.composite_id|, as long as these ids support synonym
// creation.
void Apply(opt::IRContext* ir_context,
TransformationContext* transformation_context) const override;
@ -60,6 +62,11 @@ class TransformationCompositeExtract : public Transformation {
protobufs::Transformation ToMessage() const override;
private:
// Helper method for adding data synonym facts when applying the
// transformation to |ir_context| and |transformation_context|.
void AddDataSynonymFacts(opt::IRContext* ir_context,
TransformationContext* transformation_context) const;
protobufs::TransformationCompositeExtract message_;
};

View File

@ -134,54 +134,8 @@ void TransformationCompositeInsert::Apply(
// We have modified the module so most analyzes are now invalid.
ir_context->InvalidateAnalysesExceptFor(opt::IRContext::kAnalysisNone);
// Add facts about synonyms. Every element which hasn't been changed in
// the copy is synonymous to the corresponding element in the original
// composite which has id |message_.composite_id|. For every index that is a
// prefix of |index|, the components different from the one that
// contains the inserted object are synonymous with corresponding
// elements in the original composite.
// If |composite_id| is irrelevant then don't add any synonyms.
if (transformation_context->GetFactManager()->IdIsIrrelevant(
message_.composite_id())) {
return;
}
uint32_t current_node_type_id = composite_type_id;
std::vector<uint32_t> current_index;
for (uint32_t current_level = 0; current_level < index.size();
current_level++) {
auto current_node_type_inst =
ir_context->get_def_use_mgr()->GetDef(current_node_type_id);
uint32_t index_to_skip = index[current_level];
uint32_t num_of_components = fuzzerutil::GetBoundForCompositeIndex(
*current_node_type_inst, ir_context);
// Update the current_node_type_id.
current_node_type_id = fuzzerutil::WalkOneCompositeTypeIndex(
ir_context, current_node_type_id, index_to_skip);
for (uint32_t i = 0; i < num_of_components; i++) {
if (i == index_to_skip) {
continue;
}
current_index.push_back(i);
transformation_context->GetFactManager()->AddFactDataSynonym(
MakeDataDescriptor(message_.fresh_id(), current_index),
MakeDataDescriptor(message_.composite_id(), current_index));
current_index.pop_back();
}
// Store the prefix of the |index|.
current_index.push_back(index[current_level]);
}
// The element which has been changed is synonymous to the found object
// itself. Add this fact only if |object_id| is not irrelevant.
if (!transformation_context->GetFactManager()->IdIsIrrelevant(
message_.object_id())) {
transformation_context->GetFactManager()->AddFactDataSynonym(
MakeDataDescriptor(message_.object_id(), {}),
MakeDataDescriptor(message_.fresh_id(), index));
}
// Add data synonym facts that arise from the insertion.
AddDataSynonymFacts(ir_context, transformation_context);
}
protobufs::Transformation TransformationCompositeInsert::ToMessage() const {
@ -219,5 +173,69 @@ std::unordered_set<uint32_t> TransformationCompositeInsert::GetFreshIds()
return {message_.fresh_id()};
}
void TransformationCompositeInsert::AddDataSynonymFacts(
opt::IRContext* ir_context,
TransformationContext* transformation_context) const {
// If the result id arising from the insertion is irrelevant then do not add
// any data synonym facts. (The result id can be irrelevant if the insertion
// occurs in a dead block.)
if (transformation_context->GetFactManager()->IdIsIrrelevant(
message_.fresh_id())) {
return;
}
// So long as the |message_.composite_id| is suitable for participating in
// synonyms, every every element of the insertion result except for at the
// index being inserted into is synonymous with the corresponding element of
// |message_.composite_id|. In that case, for every index that is a prefix of
// |index|, the components different from the one that contains the inserted
// object are synonymous with corresponding elements in the original
// composite.
uint32_t current_node_type_id =
fuzzerutil::GetTypeId(ir_context, message_.composite_id());
std::vector<uint32_t> current_index;
std::vector<uint32_t> index =
fuzzerutil::RepeatedFieldToVector(message_.index());
for (uint32_t current_level : index) {
auto current_node_type_inst =
ir_context->get_def_use_mgr()->GetDef(current_node_type_id);
uint32_t index_to_skip = current_level;
uint32_t num_of_components = fuzzerutil::GetBoundForCompositeIndex(
*current_node_type_inst, ir_context);
// Update the current_node_type_id.
current_node_type_id = fuzzerutil::WalkOneCompositeTypeIndex(
ir_context, current_node_type_id, index_to_skip);
for (uint32_t i = 0; i < num_of_components; i++) {
if (i == index_to_skip) {
continue;
}
current_index.push_back(i);
if (fuzzerutil::CanMakeSynonymOf(
ir_context, *transformation_context,
ir_context->get_def_use_mgr()->GetDef(message_.composite_id()))) {
transformation_context->GetFactManager()->AddFactDataSynonym(
MakeDataDescriptor(message_.fresh_id(), current_index),
MakeDataDescriptor(message_.composite_id(), current_index));
}
current_index.pop_back();
}
// Store the prefix of the |index|.
current_index.push_back(current_level);
}
// If the object being inserted supports synonym creation then it is
// synonymous with the result of the insert instruction at the given index.
if (fuzzerutil::CanMakeSynonymOf(
ir_context, *transformation_context,
ir_context->get_def_use_mgr()->GetDef(message_.object_id()))) {
transformation_context->GetFactManager()->AddFactDataSynonym(
MakeDataDescriptor(message_.object_id(), {}),
MakeDataDescriptor(message_.fresh_id(), index));
}
}
} // namespace fuzz
} // namespace spvtools

View File

@ -65,6 +65,11 @@ class TransformationCompositeInsert : public Transformation {
opt::Instruction* instruction);
private:
// Helper method for adding data synonym facts when applying the
// transformation to |ir_context| and |transformation_context|.
void AddDataSynonymFacts(opt::IRContext* ir_context,
TransformationContext* transformation_context) const;
protobufs::TransformationCompositeInsert message_;
};

View File

@ -38,8 +38,7 @@ TransformationPushIdThroughVariable::TransformationPushIdThroughVariable(
}
bool TransformationPushIdThroughVariable::IsApplicable(
opt::IRContext* ir_context,
const TransformationContext& transformation_context) const {
opt::IRContext* ir_context, const TransformationContext& /*unused*/) 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())) {
@ -74,14 +73,6 @@ bool TransformationPushIdThroughVariable::IsApplicable(
return false;
}
// We should be able to create a synonym of |value_id| if it's not irrelevant.
if (!transformation_context.GetFactManager()->IdIsIrrelevant(
message_.value_id()) &&
!fuzzerutil::CanMakeSynonymOf(ir_context, transformation_context,
value_instruction)) {
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(),
@ -153,8 +144,11 @@ void TransformationPushIdThroughVariable::Apply(
ir_context->InvalidateAnalysesExceptFor(opt::IRContext::kAnalysisNone);
if (!transformation_context->GetFactManager()->IdIsIrrelevant(
message_.value_id())) {
// We should be able to create a synonym of |value_id| if it's not irrelevant.
if (fuzzerutil::CanMakeSynonymOf(ir_context, *transformation_context,
value_instruction) &&
!transformation_context->GetFactManager()->IdIsIrrelevant(
message_.value_synonym_id())) {
// Adds the fact that |message_.value_synonym_id|
// and |message_.value_id| are synonymous.
transformation_context->GetFactManager()->AddFactDataSynonym(

View File

@ -52,7 +52,7 @@ class TransformationPushIdThroughVariable : public Transformation {
// Stores |value_id| to |variable_id|, loads |variable_id| to
// |value_synonym_id|. Adds the fact that |value_synonym_id| and |value_id|
// are synonymous if |value_id| is not irrelevant.
// are synonymous if |value_id| and |value_synonym_id| are not irrelevant.
void Apply(opt::IRContext* ir_context,
TransformationContext* transformation_context) const override;

View File

@ -89,7 +89,7 @@ void TransformationReplaceCopyObjectWithStoreLoad::Apply(
copy_object_instruction->opcode() == SpvOpCopyObject &&
"The required OpCopyObject instruction must be defined.");
// Get id used as a source by the OpCopyObject instruction.
uint32_t src_operand = copy_object_instruction->GetSingleWordOperand(2);
uint32_t src_operand = copy_object_instruction->GetSingleWordInOperand(0);
// A pointer type instruction pointing to the value type must be defined.
auto pointer_type_id = fuzzerutil::MaybeGetPointerType(
ir_context, copy_object_instruction->type_id(),
@ -129,11 +129,15 @@ void TransformationReplaceCopyObjectWithStoreLoad::Apply(
ir_context->InvalidateAnalysesExceptFor(opt::IRContext::kAnalysisNone);
if (!transformation_context->GetFactManager()->IdIsIrrelevant(
message_.copy_object_result_id()) &&
!transformation_context->GetFactManager()->IdIsIrrelevant(src_operand)) {
// Adds the fact that |message_.copy_object_result_id|
// and src_operand (id used by OpCopyObject) are synonymous.
transformation_context->GetFactManager()->AddFactDataSynonym(
MakeDataDescriptor(message_.copy_object_result_id(), {}),
MakeDataDescriptor(src_operand, {}));
}
}
protobufs::Transformation

View File

@ -39,8 +39,7 @@ TransformationVectorShuffle::TransformationVectorShuffle(
}
bool TransformationVectorShuffle::IsApplicable(
opt::IRContext* ir_context,
const TransformationContext& transformation_context) const {
opt::IRContext* ir_context, const TransformationContext& /*unused*/) const {
// The fresh id must not already be in use.
if (!fuzzerutil::IsFreshId(ir_context, message_.fresh_id())) {
return false;
@ -57,26 +56,12 @@ 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.
@ -153,67 +138,7 @@ void TransformationVectorShuffle::Apply(
ir_context->InvalidateAnalysesExceptFor(
opt::IRContext::Analysis::kAnalysisNone);
// If the new instruction is irrelevant (because it is in a dead block), it
// cannot participate in any DataSynonym fact.
if (transformation_context->GetFactManager()->IdIsIrrelevant(
message_.fresh_id())) {
return;
}
// Add synonym facts relating the defined elements of the shuffle result to
// the vector components that they come from.
for (uint32_t component_index = 0;
component_index < static_cast<uint32_t>(message_.component_size());
component_index++) {
uint32_t component = message_.component(component_index);
if (component == 0xFFFFFFFF) {
// This component is undefined, so move on - but first note that the
// overall shuffle result cannot be synonymous with any vector.
continue;
}
// This describes the element of the result vector associated with
// |component_index|.
protobufs::DataDescriptor descriptor_for_result_component =
MakeDataDescriptor(message_.fresh_id(), {component_index});
protobufs::DataDescriptor descriptor_for_source_component;
// Get a data descriptor for the component of the input vector to which
// |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();
assert(
index_into_vector_2 <
GetVectorType(ir_context, message_.vector2())->element_count() &&
"Vector shuffle index is out of bounds.");
descriptor_for_source_component =
MakeDataDescriptor(message_.vector2(), {index_into_vector_2});
}
// Add a fact relating this input vector component with the associated
// result component.
transformation_context->GetFactManager()->AddFactDataSynonym(
descriptor_for_result_component, descriptor_for_source_component);
}
AddDataSynonymFacts(ir_context, transformation_context);
}
protobufs::Transformation TransformationVectorShuffle::ToMessage() const {
@ -240,5 +165,69 @@ std::unordered_set<uint32_t> TransformationVectorShuffle::GetFreshIds() const {
return {message_.fresh_id()};
}
void TransformationVectorShuffle::AddDataSynonymFacts(
opt::IRContext* ir_context,
TransformationContext* transformation_context) const {
// If the new instruction is irrelevant (because it is in a dead block), it
// cannot participate in any DataSynonym fact.
if (transformation_context->GetFactManager()->IdIsIrrelevant(
message_.fresh_id())) {
return;
}
// Add synonym facts relating the defined elements of the shuffle result to
// the vector components that they come from.
for (uint32_t component_index = 0;
component_index < static_cast<uint32_t>(message_.component_size());
component_index++) {
uint32_t component = message_.component(component_index);
if (component == 0xFFFFFFFF) {
// This component is undefined, we do not introduce a synonym.
continue;
}
// This describes the element of the result vector associated with
// |component_index|.
protobufs::DataDescriptor descriptor_for_result_component =
MakeDataDescriptor(message_.fresh_id(), {component_index});
protobufs::DataDescriptor descriptor_for_source_component;
// Get a data descriptor for the component of the input vector to which
// |component| refers.
if (component <
GetVectorType(ir_context, message_.vector1())->element_count()) {
// Check that the first vector can participate in data synonym facts.
if (!fuzzerutil::CanMakeSynonymOf(
ir_context, *transformation_context,
ir_context->get_def_use_mgr()->GetDef(message_.vector1()))) {
continue;
}
descriptor_for_source_component =
MakeDataDescriptor(message_.vector1(), {component});
} else {
// Check that the second vector can participate in data synonym facts.
if (!fuzzerutil::CanMakeSynonymOf(
ir_context, *transformation_context,
ir_context->get_def_use_mgr()->GetDef(message_.vector2()))) {
continue;
}
auto index_into_vector_2 =
component -
GetVectorType(ir_context, message_.vector1())->element_count();
assert(
index_into_vector_2 <
GetVectorType(ir_context, message_.vector2())->element_count() &&
"Vector shuffle index is out of bounds.");
descriptor_for_source_component =
MakeDataDescriptor(message_.vector2(), {index_into_vector_2});
}
// Add a fact relating this input vector component with the associated
// result component.
transformation_context->GetFactManager()->AddFactDataSynonym(
descriptor_for_result_component, descriptor_for_source_component);
}
}
} // namespace fuzz
} // namespace spvtools

View File

@ -52,15 +52,16 @@ class TransformationVectorShuffle : public Transformation {
// Inserts an OpVectorShuffle instruction before
// |message_.instruction_to_insert_before|, shuffles vectors
// |message_.vector1| and |message_.vector2| using the indices provided by
// |message_.component|, into |message_.fresh_id|. Adds a fact to the fact
// manager recording the fact each element of |message_.fresh_id| is
// |message_.component|, into |message_.fresh_id|.
//
// If |message_.fresh_id| is irrelevant (e.g. due to being in a dead block)
// of if one of |message_.vector1| or |message_.vector2| is irrelevant and the
// shuffle reads components from the irrelevant vector then no synonym facts
// are added.
//
// Otherwise, a fact is added recording that element of |message_.fresh_id| is
// synonymous with the element of |message_.vector1| or |message_.vector2|
// 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. 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|).
// from which it came (with undefined components being ignored).
void Apply(opt::IRContext* ir_context,
TransformationContext* transformation_context) const override;
@ -76,9 +77,15 @@ class TransformationVectorShuffle : public Transformation {
uint32_t GetResultTypeId(opt::IRContext* ir_context,
const opt::analysis::Type& element_type) const;
// Returns the type associated with |id_of_vector| in |ir_context|.
static opt::analysis::Vector* GetVectorType(opt::IRContext* ir_context,
uint32_t id_of_vector);
// Helper method for adding data synonym facts when applying the
// transformation to |ir_context| and |transformation_context|.
void AddDataSynonymFacts(opt::IRContext* ir_context,
TransformationContext* transformation_context) const;
protobufs::TransformationVectorShuffle message_;
};

View File

@ -721,6 +721,184 @@ TEST(TransformationAddBitInstructionSynonymTest, AddOpNotSynonym) {
ASSERT_TRUE(IsEqual(env, variant_shader, context.get()));
}
TEST(TransformationAddBitInstructionSynonymTest, NoSynonymWhenIdIsIrrelevant) {
std::string reference_shader = R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Vertex %37 "main"
; Types
%2 = OpTypeInt 32 0
%3 = OpTypeVoid
%4 = OpTypeFunction %3
; Constants
%5 = OpConstant %2 0
%6 = OpConstant %2 1
%7 = OpConstant %2 2
%8 = OpConstant %2 3
%9 = OpConstant %2 4
%10 = OpConstant %2 5
%11 = OpConstant %2 6
%12 = OpConstant %2 7
%13 = OpConstant %2 8
%14 = OpConstant %2 9
%15 = OpConstant %2 10
%16 = OpConstant %2 11
%17 = OpConstant %2 12
%18 = OpConstant %2 13
%19 = OpConstant %2 14
%20 = OpConstant %2 15
%21 = OpConstant %2 16
%22 = OpConstant %2 17
%23 = OpConstant %2 18
%24 = OpConstant %2 19
%25 = OpConstant %2 20
%26 = OpConstant %2 21
%27 = OpConstant %2 22
%28 = OpConstant %2 23
%29 = OpConstant %2 24
%30 = OpConstant %2 25
%31 = OpConstant %2 26
%32 = OpConstant %2 27
%33 = OpConstant %2 28
%34 = OpConstant %2 29
%35 = OpConstant %2 30
%36 = OpConstant %2 31
; main function
%37 = OpFunction %3 None %4
%38 = OpLabel
%39 = OpBitwiseOr %2 %5 %6 ; bit instruction
OpReturn
OpFunctionEnd
)";
const auto env = SPV_ENV_UNIVERSAL_1_5;
const auto consumer = nullptr;
const auto context =
BuildModule(env, consumer, reference_shader, kFuzzAssembleOption);
ASSERT_TRUE(IsValid(env, context.get()));
spvtools::ValidatorOptions validator_options;
TransformationContext transformation_context(
MakeUnique<FactManager>(context.get()), validator_options);
// Mark the result id of the bit instruction as irrelevant.
transformation_context.GetFactManager()->AddFactIdIsIrrelevant(39);
// Adds OpBitwiseOr synonym.
auto transformation = TransformationAddBitInstructionSynonym(
39, {40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52,
53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65,
66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78,
79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91,
92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104,
105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117,
118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130,
131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143,
144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156,
157, 158, 159, 160, 161, 162, 163, 164, 165, 166});
ASSERT_TRUE(
transformation.IsApplicable(context.get(), transformation_context));
ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context);
ASSERT_TRUE(IsValid(env, context.get()));
// No synonym should have been created, since the bit instruction is
// irrelevant.
ASSERT_FALSE(transformation_context.GetFactManager()->IsSynonymous(
MakeDataDescriptor(166, {}), MakeDataDescriptor(39, {})));
}
TEST(TransformationAddBitInstructionSynonymTest, NoSynonymWhenBlockIsDead) {
std::string reference_shader = R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Vertex %37 "main"
; Types
%2 = OpTypeInt 32 0
%3 = OpTypeVoid
%4 = OpTypeFunction %3
; Constants
%5 = OpConstant %2 0
%6 = OpConstant %2 1
%7 = OpConstant %2 2
%8 = OpConstant %2 3
%9 = OpConstant %2 4
%10 = OpConstant %2 5
%11 = OpConstant %2 6
%12 = OpConstant %2 7
%13 = OpConstant %2 8
%14 = OpConstant %2 9
%15 = OpConstant %2 10
%16 = OpConstant %2 11
%17 = OpConstant %2 12
%18 = OpConstant %2 13
%19 = OpConstant %2 14
%20 = OpConstant %2 15
%21 = OpConstant %2 16
%22 = OpConstant %2 17
%23 = OpConstant %2 18
%24 = OpConstant %2 19
%25 = OpConstant %2 20
%26 = OpConstant %2 21
%27 = OpConstant %2 22
%28 = OpConstant %2 23
%29 = OpConstant %2 24
%30 = OpConstant %2 25
%31 = OpConstant %2 26
%32 = OpConstant %2 27
%33 = OpConstant %2 28
%34 = OpConstant %2 29
%35 = OpConstant %2 30
%36 = OpConstant %2 31
; main function
%37 = OpFunction %3 None %4
%38 = OpLabel
%39 = OpBitwiseOr %2 %5 %6 ; bit instruction
OpReturn
OpFunctionEnd
)";
const auto env = SPV_ENV_UNIVERSAL_1_5;
const auto consumer = nullptr;
const auto context =
BuildModule(env, consumer, reference_shader, kFuzzAssembleOption);
ASSERT_TRUE(IsValid(env, context.get()));
spvtools::ValidatorOptions validator_options;
TransformationContext transformation_context(
MakeUnique<FactManager>(context.get()), validator_options);
// Mark the block where we will try to create the synonym as dead.
transformation_context.GetFactManager()->AddFactBlockIsDead(38);
// Adds OpBitwiseOr synonym.
auto transformation = TransformationAddBitInstructionSynonym(
39, {40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52,
53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65,
66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78,
79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91,
92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104,
105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117,
118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130,
131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143,
144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156,
157, 158, 159, 160, 161, 162, 163, 164, 165, 166});
ASSERT_TRUE(
transformation.IsApplicable(context.get(), transformation_context));
ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context);
ASSERT_TRUE(IsValid(env, context.get()));
// No synonym should have been created, since the bit instruction is
// irrelevant.
ASSERT_FALSE(transformation_context.GetFactManager()->IsSynonymous(
MakeDataDescriptor(166, {}), MakeDataDescriptor(39, {})));
}
} // namespace
} // namespace fuzz
} // namespace spvtools

View File

@ -948,6 +948,79 @@ TEST(TransformationAddLoopToCreateIntConstantSynonymTest, Underflow) {
ASSERT_TRUE(IsEqual(env, after_transformations, context.get()));
}
TEST(TransformationAddLoopToCreateIntConstantSynonymTest,
InapplicableDueToDeadBlockOrIrrelevantId) {
std::string shader = R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %2 "main"
OpExecutionMode %2 OriginUpperLeft
OpSource ESSL 310
%3 = OpTypeVoid
%4 = OpTypeFunction %3
%5 = OpTypeBool
%6 = OpConstantTrue %5
%7 = OpTypeInt 32 1
%8 = OpConstant %7 0
%9 = OpConstant %7 1
%10 = OpConstant %7 2
%11 = OpConstant %7 5
%12 = OpConstant %7 10
%13 = OpConstant %7 20
%1010 = OpConstant %7 2
%1011 = OpConstant %7 5
%1012 = OpConstant %7 10
%1013 = OpConstant %7 20
%2 = OpFunction %3 None %4
%14 = OpLabel
OpSelectionMerge %16 None
OpBranchConditional %6 %16 %15
%15 = OpLabel
OpBranch %16
%16 = OpLabel
OpBranch %17
%17 = OpLabel
OpReturn
OpFunctionEnd
)";
const auto env = SPV_ENV_UNIVERSAL_1_5;
const auto consumer = nullptr;
const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
ASSERT_TRUE(IsValid(env, context.get()));
spvtools::ValidatorOptions validator_options;
TransformationContext transformation_context(
MakeUnique<FactManager>(context.get()), validator_options);
transformation_context.GetFactManager()->AddFactBlockIsDead(15);
transformation_context.GetFactManager()->AddFactIdIsIrrelevant(1010);
transformation_context.GetFactManager()->AddFactIdIsIrrelevant(1011);
transformation_context.GetFactManager()->AddFactIdIsIrrelevant(1012);
transformation_context.GetFactManager()->AddFactIdIsIrrelevant(1013);
// Bad because the block before which the loop would be inserted is dead.
ASSERT_FALSE(TransformationAddLoopToCreateIntConstantSynonym(
12, 13, 11, 10, 15, 100, 101, 102, 103, 104, 105, 106, 0)
.IsApplicable(context.get(), transformation_context));
// OK
ASSERT_TRUE(TransformationAddLoopToCreateIntConstantSynonym(
12, 13, 11, 10, 17, 100, 101, 102, 103, 104, 105, 106, 0)
.IsApplicable(context.get(), transformation_context));
// Bad because in each case one of the constants involved is irrelevant.
ASSERT_FALSE(TransformationAddLoopToCreateIntConstantSynonym(
1012, 13, 11, 10, 17, 100, 101, 102, 103, 104, 105, 106, 0)
.IsApplicable(context.get(), transformation_context));
ASSERT_FALSE(TransformationAddLoopToCreateIntConstantSynonym(
12, 1013, 11, 10, 17, 100, 101, 102, 103, 104, 105, 106, 0)
.IsApplicable(context.get(), transformation_context));
ASSERT_FALSE(TransformationAddLoopToCreateIntConstantSynonym(
12, 13, 1011, 10, 17, 100, 101, 102, 103, 104, 105, 106, 0)
.IsApplicable(context.get(), transformation_context));
ASSERT_FALSE(TransformationAddLoopToCreateIntConstantSynonym(
12, 13, 11, 1010, 17, 100, 101, 102, 103, 104, 105, 106, 0)
.IsApplicable(context.get(), transformation_context));
}
} // namespace
} // namespace fuzz
} // namespace spvtools

View File

@ -418,6 +418,68 @@ TEST(TransformationAddOpPhiSynonymTest, VariablePointers) {
ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
}
TEST(TransformationAddOpPhiSynonymTest, DeadBlock) {
std::string shader = R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %4 "main"
OpExecutionMode %4 OriginUpperLeft
OpSource ESSL 320
%2 = OpTypeVoid
%3 = OpTypeFunction %2
%6 = OpTypeInt 32 1
%7 = OpTypePointer Function %6
%9 = OpConstant %6 2
%10 = OpTypeBool
%11 = OpConstantFalse %10
%15 = OpConstant %6 0
%50 = OpConstant %6 0
%4 = OpFunction %2 None %3
%5 = OpLabel
%8 = OpVariable %7 Function
OpStore %8 %9
OpSelectionMerge %13 None
OpBranchConditional %11 %12 %13
%12 = OpLabel
%14 = OpLoad %6 %8
%16 = OpIEqual %10 %14 %15
OpSelectionMerge %18 None
OpBranchConditional %16 %17 %40
%17 = OpLabel
OpBranch %18
%40 = OpLabel
OpBranch %18
%18 = OpLabel
OpBranch %13
%13 = OpLabel
OpReturn
OpFunctionEnd
)";
const auto env = SPV_ENV_UNIVERSAL_1_5;
const auto consumer = nullptr;
const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
ASSERT_TRUE(IsValid(env, context.get()));
spvtools::ValidatorOptions validator_options;
TransformationContext transformation_context(
MakeUnique<FactManager>(context.get()), validator_options);
// Dead blocks
transformation_context.GetFactManager()->AddFactBlockIsDead(12);
transformation_context.GetFactManager()->AddFactBlockIsDead(17);
transformation_context.GetFactManager()->AddFactBlockIsDead(18);
// Declare synonym
ASSERT_TRUE(transformation_context.GetFactManager()->MaybeAddFact(
MakeSynonymFact(15, 50)));
// Bad because the block 18 is dead.
ASSERT_FALSE(TransformationAddOpPhiSynonym(18, {{{17, 15}, {40, 50}}}, 100)
.IsApplicable(context.get(), transformation_context));
}
} // namespace
} // namespace fuzz
} // namespace spvtools

View File

@ -1517,10 +1517,64 @@ TEST(TransformationCompositeConstructTest, DontAddSynonymsForIrrelevantIds) {
ASSERT_TRUE(IsValid(env, context.get()));
ASSERT_FALSE(transformation_context.GetFactManager()->IsSynonymous(
MakeDataDescriptor(25, {}), MakeDataDescriptor(200, {0})));
ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
// Even though %28 is not irrelevant, we do not create a synonym because part
// of the new composite, %200, is tainted by the irrelevant id %25.
ASSERT_FALSE(transformation_context.GetFactManager()->IsSynonymous(
MakeDataDescriptor(28, {}), MakeDataDescriptor(200, {1})));
}
TEST(TransformationCompositeConstructTest, DontAddSynonymsInDeadBlock) {
std::string shader = R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %4 "main"
OpExecutionMode %4 OriginUpperLeft
OpSource ESSL 320
%2 = OpTypeVoid
%3 = OpTypeFunction %2
%6 = OpTypeInt 32 1
%7 = OpTypeVector %6 2
%8 = OpTypePointer Function %7
%10 = OpConstant %6 0
%11 = OpConstant %6 1
%12 = OpConstantComposite %7 %10 %11
%13 = OpTypeBool
%14 = OpConstantFalse %13
%4 = OpFunction %2 None %3
%5 = OpLabel
%9 = OpVariable %8 Function
OpStore %9 %12
OpSelectionMerge %16 None
OpBranchConditional %14 %15 %16
%15 = OpLabel
OpBranch %16
%16 = OpLabel
OpReturn
OpFunctionEnd
)";
const auto env = SPV_ENV_UNIVERSAL_1_3;
const auto consumer = nullptr;
const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
ASSERT_TRUE(IsValid(env, context.get()));
spvtools::ValidatorOptions validator_options;
TransformationContext transformation_context(
MakeUnique<FactManager>(context.get()), validator_options);
transformation_context.GetFactManager()->AddFactBlockIsDead(15);
TransformationCompositeConstruct transformation(
7, {10, 11}, MakeInstructionDescriptor(15, SpvOpBranch, 0), 100);
ASSERT_TRUE(
transformation.IsApplicable(context.get(), transformation_context));
ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context);
ASSERT_FALSE(transformation_context.GetFactManager()->IsSynonymous(
MakeDataDescriptor(7, {0}), MakeDataDescriptor(10, {})));
ASSERT_FALSE(transformation_context.GetFactManager()->IsSynonymous(
MakeDataDescriptor(7, {1}), MakeDataDescriptor(11, {})));
}
} // namespace
} // namespace fuzz
} // namespace spvtools

View File

@ -577,6 +577,55 @@ TEST(TransformationCompositeExtractTest, DontAddSynonymsForIrrelevantIds) {
MakeDataDescriptor(201, {}), MakeDataDescriptor(100, {2})));
}
TEST(TransformationCompositeExtractTest, DontAddSynonymInDeadBlock) {
std::string shader = R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %4 "main"
OpExecutionMode %4 OriginUpperLeft
OpSource ESSL 320
%2 = OpTypeVoid
%3 = OpTypeFunction %2
%6 = OpTypeInt 32 1
%7 = OpTypeVector %6 2
%8 = OpTypePointer Function %7
%10 = OpConstant %6 0
%11 = OpConstant %6 1
%12 = OpConstantComposite %7 %10 %11
%13 = OpTypeBool
%14 = OpConstantFalse %13
%4 = OpFunction %2 None %3
%5 = OpLabel
%9 = OpVariable %8 Function
OpStore %9 %12
OpSelectionMerge %16 None
OpBranchConditional %14 %15 %16
%15 = OpLabel
OpBranch %16
%16 = OpLabel
OpReturn
OpFunctionEnd
)";
const auto env = SPV_ENV_UNIVERSAL_1_4;
const auto consumer = nullptr;
const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
ASSERT_TRUE(IsValid(env, context.get()));
spvtools::ValidatorOptions validator_options;
TransformationContext transformation_context(
MakeUnique<FactManager>(context.get()), validator_options);
transformation_context.GetFactManager()->AddFactBlockIsDead(15);
TransformationCompositeExtract transformation(
MakeInstructionDescriptor(15, SpvOpBranch, 0), 100, 12, {0});
ASSERT_TRUE(
transformation.IsApplicable(context.get(), transformation_context));
ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context);
ASSERT_FALSE(transformation_context.GetFactManager()->IsSynonymous(
MakeDataDescriptor(100, {}), MakeDataDescriptor(12, {0})));
}
} // namespace
} // namespace fuzz
} // namespace spvtools

View File

@ -14,6 +14,7 @@
#include "source/fuzz/transformation_composite_insert.h"
#include "source/fuzz/data_descriptor.h"
#include "source/fuzz/instruction_descriptor.h"
#include "test/fuzz/fuzz_test_util.h"
@ -370,7 +371,8 @@ TEST(TransformationCompositeInsertTest, IrrelevantCompositeNoSynonyms) {
&transformation_context);
ASSERT_TRUE(IsValid(env, context.get()));
// No synonyms should have been added.
// No synonyms that involve the original object - %30 - should have been
// added.
ASSERT_FALSE(transformation_context.GetFactManager()->IsSynonymous(
MakeDataDescriptor(30, {0}), MakeDataDescriptor(50, {0})));
ASSERT_FALSE(transformation_context.GetFactManager()->IsSynonymous(
@ -379,15 +381,13 @@ TEST(TransformationCompositeInsertTest, IrrelevantCompositeNoSynonyms) {
MakeDataDescriptor(30, {1, 2}), MakeDataDescriptor(50, {1, 2})));
ASSERT_FALSE(transformation_context.GetFactManager()->IsSynonymous(
MakeDataDescriptor(30, {1, 0, 1}), MakeDataDescriptor(50, {1, 0, 1})));
ASSERT_FALSE(transformation_context.GetFactManager()->IsSynonymous(
// We *should* have a synonym between %11 and the component of %50 into which
// it has been inserted.
ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
MakeDataDescriptor(50, {1, 0, 0}), MakeDataDescriptor(11, {})));
}
TEST(TransformationCompositeInsertTest, IrrelevantObjectSomeSynonyms) {
// This test handles cases where |object| is irrelevant.
// The transformation should create some synonyms. It shouldn't create a
// synonym related to |object|. The member composite has a different number of
// elements than the parent composite.
TEST(TransformationCompositeInsertTest, IrrelevantObjectNoSynonyms) {
std::string shader = R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
@ -474,7 +474,8 @@ TEST(TransformationCompositeInsertTest, IrrelevantObjectSomeSynonyms) {
&transformation_context);
ASSERT_TRUE(IsValid(env, context.get()));
// These synonyms should have been added.
// Since %30 and %50 are not irrelevant, they should be synonymous at all
// indices unaffected by the insertion.
ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
MakeDataDescriptor(30, {0}), MakeDataDescriptor(50, {0})));
ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
@ -483,7 +484,8 @@ TEST(TransformationCompositeInsertTest, IrrelevantObjectSomeSynonyms) {
MakeDataDescriptor(30, {1, 2}), MakeDataDescriptor(50, {1, 2})));
ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
MakeDataDescriptor(30, {1, 0, 1}), MakeDataDescriptor(50, {1, 0, 1})));
// This synonym shouldn't have been added.
// Since %11 is irrelevant it should not be synonymous with the component into
// which it has been inserted.
ASSERT_FALSE(transformation_context.GetFactManager()->IsSynonymous(
MakeDataDescriptor(50, {1, 0, 0}), MakeDataDescriptor(11, {})));
}
@ -802,6 +804,106 @@ TEST(TransformationCompositeInsertTest, IdNotAvailableScenarios) {
ASSERT_FALSE(
transformation_bad_4.IsApplicable(context.get(), transformation_context));
}
TEST(TransformationCompositeInsertTest, CompositeInsertionWithIrrelevantIds) {
// This checks that we do *not* get data synonym facts when we do composite
// insertion using irrelevant ids or in dead blocks.
std::string shader = R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %12 "main"
OpExecutionMode %12 OriginUpperLeft
OpSource ESSL 310
%2 = OpTypeVoid
%3 = OpTypeFunction %2
%6 = OpTypeInt 32 1
%7 = OpTypeVector %6 2
%8 = OpConstant %6 0
%9 = OpConstantComposite %7 %8 %8
%10 = OpTypeBool
%11 = OpConstantFalse %10
%16 = OpConstant %6 0
%17 = OpConstant %6 1
%18 = OpConstantComposite %7 %8 %8
%12 = OpFunction %2 None %3
%13 = OpLabel
OpSelectionMerge %15 None
OpBranchConditional %11 %14 %15
%14 = OpLabel
OpBranch %15
%15 = OpLabel
OpReturn
OpFunctionEnd
)";
const auto env = SPV_ENV_UNIVERSAL_1_3;
const auto consumer = nullptr;
const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
ASSERT_TRUE(IsValid(env, context.get()));
spvtools::ValidatorOptions validator_options;
TransformationContext transformation_context(
MakeUnique<FactManager>(context.get()), validator_options);
transformation_context.GetFactManager()->AddFactBlockIsDead(14);
transformation_context.GetFactManager()->AddFactIdIsIrrelevant(16);
transformation_context.GetFactManager()->AddFactIdIsIrrelevant(18);
// Leads to synonyms - nothing is irrelevant.
auto transformation1 = TransformationCompositeInsert(
MakeInstructionDescriptor(13, SpvOpSelectionMerge, 0), 100, 9, 17, {0});
ASSERT_TRUE(
transformation1.IsApplicable(context.get(), transformation_context));
ApplyAndCheckFreshIds(transformation1, context.get(),
&transformation_context);
ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
MakeDataDescriptor(100, {0}), MakeDataDescriptor(17, {})));
ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
MakeDataDescriptor(100, {1}), MakeDataDescriptor(9, {1})));
// Because %16 is irrelevant, we don't get a synonym with the component to
// which it has been inserted (but we do for the other component).
auto transformation2 = TransformationCompositeInsert(
MakeInstructionDescriptor(13, SpvOpSelectionMerge, 0), 101, 9, 16, {0});
ASSERT_TRUE(
transformation2.IsApplicable(context.get(), transformation_context));
ApplyAndCheckFreshIds(transformation2, context.get(),
&transformation_context);
ASSERT_FALSE(transformation_context.GetFactManager()->IsSynonymous(
MakeDataDescriptor(101, {0}), MakeDataDescriptor(16, {})));
ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
MakeDataDescriptor(101, {1}), MakeDataDescriptor(9, {1})));
// Because %18 is irrelevant we only get a synonym for the component into
// which insertion has taken place.
auto transformation3 = TransformationCompositeInsert(
MakeInstructionDescriptor(13, SpvOpSelectionMerge, 0), 102, 18, 17, {0});
ASSERT_TRUE(
transformation3.IsApplicable(context.get(), transformation_context));
ApplyAndCheckFreshIds(transformation3, context.get(),
&transformation_context);
ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
MakeDataDescriptor(102, {0}), MakeDataDescriptor(17, {})));
ASSERT_FALSE(transformation_context.GetFactManager()->IsSynonymous(
MakeDataDescriptor(102, {1}), MakeDataDescriptor(18, {1})));
// Does not lead to synonyms as block %14 is dead.
auto transformation4 = TransformationCompositeInsert(
MakeInstructionDescriptor(14, SpvOpBranch, 0), 103, 9, 17, {0});
ASSERT_TRUE(
transformation4.IsApplicable(context.get(), transformation_context));
ApplyAndCheckFreshIds(transformation4, context.get(),
&transformation_context);
ASSERT_FALSE(transformation_context.GetFactManager()->IsSynonymous(
MakeDataDescriptor(103, {0}), MakeDataDescriptor(17, {})));
ASSERT_FALSE(transformation_context.GetFactManager()->IsSynonymous(
MakeDataDescriptor(103, {1}), MakeDataDescriptor(9, {1})));
ASSERT_TRUE(IsValid(env, context.get()));
}
} // namespace
} // namespace fuzz
} // namespace spvtools

View File

@ -694,6 +694,61 @@ TEST(TransformationPushIdThroughVariableTest, DontAddSynonymsForIrrelevantIds) {
MakeDataDescriptor(21, {}), MakeDataDescriptor(62, {})));
}
TEST(TransformationPushIdThroughVariableTest, DontAddSynonymsInDeadBlocks) {
std::string reference_shader = R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %4 "main"
OpExecutionMode %4 OriginUpperLeft
OpSource ESSL 320
%2 = OpTypeVoid
%3 = OpTypeFunction %2
%6 = OpTypeInt 32 1
%7 = OpTypeVector %6 2
%8 = OpTypePointer Function %7
%10 = OpConstant %6 0
%11 = OpConstant %6 1
%12 = OpConstantComposite %7 %10 %11
%13 = OpTypeBool
%50 = OpTypePointer Function %13
%14 = OpConstantFalse %13
%4 = OpFunction %2 None %3
%5 = OpLabel
%9 = OpVariable %8 Function
OpStore %9 %12
OpSelectionMerge %16 None
OpBranchConditional %14 %15 %16
%15 = OpLabel
OpBranch %16
%16 = OpLabel
OpReturn
OpFunctionEnd
)";
const auto env = SPV_ENV_UNIVERSAL_1_4;
const auto consumer = nullptr;
const auto context =
BuildModule(env, consumer, reference_shader, kFuzzAssembleOption);
spvtools::ValidatorOptions validator_options;
TransformationContext transformation_context(
MakeUnique<FactManager>(context.get()), validator_options);
// Tests the reference shader validity.
ASSERT_TRUE(IsValid(env, context.get()));
transformation_context.GetFactManager()->AddFactBlockIsDead(15);
auto transformation = TransformationPushIdThroughVariable(
14, 100, 101, SpvStorageClassFunction, 14,
MakeInstructionDescriptor(15, SpvOpBranch, 0));
ASSERT_TRUE(
transformation.IsApplicable(context.get(), transformation_context));
ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context);
ASSERT_TRUE(IsValid(env, context.get()));
ASSERT_FALSE(transformation_context.GetFactManager()->IsSynonymous(
MakeDataDescriptor(14, {}), MakeDataDescriptor(100, {})));
}
} // namespace
} // namespace fuzz
} // namespace spvtools

View File

@ -195,6 +195,67 @@ TEST(TransformationReplaceCopyObjectWithStoreLoad, BasicScenarios) {
ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
}
TEST(TransformationReplaceCopyObjectWithStoreLoad, IrrelevantIdsAndDeadBlocks) {
std::string reference_shader = R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %4 "main"
OpExecutionMode %4 OriginUpperLeft
OpSource ESSL 320
%2 = OpTypeVoid
%3 = OpTypeFunction %2
%6 = OpTypeInt 32 1
%30 = OpTypePointer Function %6
%10 = OpConstant %6 0
%11 = OpConstant %6 1
%13 = OpTypeBool
%14 = OpConstantFalse %13
%4 = OpFunction %2 None %3
%5 = OpLabel
OpSelectionMerge %16 None
OpBranchConditional %14 %15 %16
%15 = OpLabel
%50 = OpCopyObject %6 %10
OpBranch %16
%16 = OpLabel
%51 = OpCopyObject %6 %11
OpReturn
OpFunctionEnd
)";
const auto env = SPV_ENV_UNIVERSAL_1_4;
const auto consumer = nullptr;
const auto context =
BuildModule(env, consumer, reference_shader, kFuzzAssembleOption);
spvtools::ValidatorOptions validator_options;
TransformationContext transformation_context(
MakeUnique<FactManager>(context.get()), validator_options);
ASSERT_TRUE(IsValid(env, context.get()));
transformation_context.GetFactManager()->AddFactBlockIsDead(15);
transformation_context.GetFactManager()->AddFactIdIsIrrelevant(11);
auto transformation_1 = TransformationReplaceCopyObjectWithStoreLoad(
50, 100, SpvStorageClassFunction, 10);
ASSERT_TRUE(
transformation_1.IsApplicable(context.get(), transformation_context));
ApplyAndCheckFreshIds(transformation_1, context.get(),
&transformation_context);
ASSERT_FALSE(transformation_context.GetFactManager()->IsSynonymous(
MakeDataDescriptor(100, {}), MakeDataDescriptor(50, {})));
auto transformation_2 = TransformationReplaceCopyObjectWithStoreLoad(
51, 101, SpvStorageClassFunction, 10);
ASSERT_TRUE(
transformation_2.IsApplicable(context.get(), transformation_context));
ApplyAndCheckFreshIds(transformation_2, context.get(),
&transformation_context);
ASSERT_FALSE(transformation_context.GetFactManager()->IsSynonymous(
MakeDataDescriptor(101, {}), MakeDataDescriptor(51, {})));
}
} // namespace
} // namespace fuzz
} // namespace spvtools

View File

@ -21,7 +21,7 @@ namespace spvtools {
namespace fuzz {
namespace {
TEST(TransformationVectorShuffle, BasicTest) {
TEST(TransformationVectorShuffleTest, BasicTest) {
std::string shader = R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
@ -544,7 +544,7 @@ TEST(TransformationVectorShuffleTest, IllegalInsertionPoints) {
.IsApplicable(context.get(), transformation_context));
}
TEST(TransformationVectorShuffle, HandlesIrrelevantIds1) {
TEST(TransformationVectorShuffleTest, HandlesIrrelevantIds1) {
std::string shader = R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
@ -624,7 +624,7 @@ TEST(TransformationVectorShuffle, HandlesIrrelevantIds1) {
MakeDataDescriptor(112, {0}), MakeDataDescriptor(200, {0})));
}
TEST(TransformationVectorShuffle, HandlesIrrelevantIds2) {
TEST(TransformationVectorShuffleTest, HandlesIrrelevantIds2) {
std::string shader = R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
@ -699,12 +699,104 @@ TEST(TransformationVectorShuffle, HandlesIrrelevantIds2) {
transformation.IsApplicable(context.get(), transformation_context));
ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context);
ASSERT_TRUE(IsValid(env, context.get()));
// Because %12 is not irrelevant, we get a synonym between it and %200[1].
ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
MakeDataDescriptor(12, {0}), MakeDataDescriptor(200, {1})));
// Because %112 is irrelevant, we do not get a synonym between it and %200[0].
ASSERT_FALSE(transformation_context.GetFactManager()->IsSynonymous(
MakeDataDescriptor(112, {0}), MakeDataDescriptor(200, {0})));
}
TEST(TransformationVectorShuffleTest, HandlesIrrelevantIds3) {
std::string shader = R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %4 "main"
OpExecutionMode %4 OriginUpperLeft
OpSource ESSL 320
%2 = OpTypeVoid
%3 = OpTypeFunction %2
%6 = OpTypeInt 32 1
%7 = OpTypeVector %6 2
%8 = OpTypePointer Function %7
%10 = OpConstant %6 0
%11 = OpConstant %6 1
%12 = OpConstantComposite %7 %10 %11
%40 = OpConstantComposite %7 %10 %11
%13 = OpTypeBool
%14 = OpConstantFalse %13
%4 = OpFunction %2 None %3
%5 = OpLabel
%9 = OpVariable %8 Function
OpStore %9 %12
OpSelectionMerge %16 None
OpBranchConditional %14 %15 %16
%15 = OpLabel
OpBranch %16
%16 = OpLabel
OpReturn
OpFunctionEnd
)";
const auto env = SPV_ENV_UNIVERSAL_1_4;
const auto consumer = nullptr;
const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
ASSERT_TRUE(IsValid(env, context.get()));
spvtools::ValidatorOptions validator_options;
TransformationContext transformation_context(
MakeUnique<FactManager>(context.get()), validator_options);
transformation_context.GetFactManager()->AddFactIdIsIrrelevant(40);
transformation_context.GetFactManager()->AddFactBlockIsDead(15);
TransformationVectorShuffle transformation1(
MakeInstructionDescriptor(15, SpvOpBranch, 0), 200, 12, 12, {0, 3});
ASSERT_TRUE(
transformation1.IsApplicable(context.get(), transformation_context));
ApplyAndCheckFreshIds(transformation1, context.get(),
&transformation_context);
ASSERT_FALSE(transformation_context.GetFactManager()->IsSynonymous(
MakeDataDescriptor(200, {0}), MakeDataDescriptor(12, {0})));
ASSERT_FALSE(transformation_context.GetFactManager()->IsSynonymous(
MakeDataDescriptor(200, {1}), MakeDataDescriptor(12, {1})));
TransformationVectorShuffle transformation2(
MakeInstructionDescriptor(16, SpvOpReturn, 0), 201, 12, 40, {0, 1});
ASSERT_TRUE(
transformation2.IsApplicable(context.get(), transformation_context));
ApplyAndCheckFreshIds(transformation2, context.get(),
&transformation_context);
ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
MakeDataDescriptor(201, {0}), MakeDataDescriptor(12, {0})));
ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
MakeDataDescriptor(201, {1}), MakeDataDescriptor(12, {1})));
TransformationVectorShuffle transformation3(
MakeInstructionDescriptor(16, SpvOpReturn, 0), 202, 40, 12, {2, 3});
ASSERT_TRUE(
transformation3.IsApplicable(context.get(), transformation_context));
ApplyAndCheckFreshIds(transformation3, context.get(),
&transformation_context);
ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
MakeDataDescriptor(202, {0}), MakeDataDescriptor(12, {0})));
ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
MakeDataDescriptor(202, {1}), MakeDataDescriptor(12, {1})));
TransformationVectorShuffle transformation4(
MakeInstructionDescriptor(16, SpvOpReturn, 0), 203, 40, 12, {0, 3});
ASSERT_TRUE(
transformation4.IsApplicable(context.get(), transformation_context));
ApplyAndCheckFreshIds(transformation4, context.get(),
&transformation_context);
// Because %40 is irrelevant we do not get a synonym between it and %203[0].
ASSERT_FALSE(transformation_context.GetFactManager()->IsSynonymous(
MakeDataDescriptor(203, {0}), MakeDataDescriptor(40, {0})));
// Because %12 is *not* irrelevant we do get a synonym between it and %203[1].
ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
MakeDataDescriptor(203, {1}), MakeDataDescriptor(12, {1})));
}
} // namespace
} // namespace fuzz
} // namespace spvtools