summaryrefslogtreecommitdiffstatshomepage
path: root/3rdparty/bgfx/3rdparty/spirv-tools/source/fuzz/transformation_replace_id_with_synonym.cpp
diff options
context:
space:
mode:
Diffstat (limited to '3rdparty/bgfx/3rdparty/spirv-tools/source/fuzz/transformation_replace_id_with_synonym.cpp')
-rw-r--r--3rdparty/bgfx/3rdparty/spirv-tools/source/fuzz/transformation_replace_id_with_synonym.cpp146
1 files changed, 118 insertions, 28 deletions
diff --git a/3rdparty/bgfx/3rdparty/spirv-tools/source/fuzz/transformation_replace_id_with_synonym.cpp b/3rdparty/bgfx/3rdparty/spirv-tools/source/fuzz/transformation_replace_id_with_synonym.cpp
index 0f28540b399..0e8b89124a3 100644
--- a/3rdparty/bgfx/3rdparty/spirv-tools/source/fuzz/transformation_replace_id_with_synonym.cpp
+++ b/3rdparty/bgfx/3rdparty/spirv-tools/source/fuzz/transformation_replace_id_with_synonym.cpp
@@ -20,6 +20,7 @@
#include "source/fuzz/fuzzer_util.h"
#include "source/fuzz/id_use_descriptor.h"
#include "source/opt/types.h"
+#include "source/util/make_unique.h"
namespace spvtools {
namespace fuzz {
@@ -33,9 +34,7 @@ TransformationReplaceIdWithSynonym::TransformationReplaceIdWithSynonym(
protobufs::IdUseDescriptor id_use_descriptor,
protobufs::DataDescriptor data_descriptor,
uint32_t fresh_id_for_temporary) {
- assert(fresh_id_for_temporary == 0 && data_descriptor.index().size() == 0 &&
- "At present we do not support making an id that is synonymous with an "
- "index into a composite.");
+ assert((fresh_id_for_temporary == 0) == (data_descriptor.index().empty()));
*message_.mutable_id_use_descriptor() = std::move(id_use_descriptor);
*message_.mutable_data_descriptor() = std::move(data_descriptor);
message_.set_fresh_id_for_temporary(fresh_id_for_temporary);
@@ -47,26 +46,30 @@ bool TransformationReplaceIdWithSynonym::IsApplicable(
auto id_of_interest = message_.id_use_descriptor().id_of_interest();
// Does the fact manager know about the synonym?
- if (fact_manager.GetIdsForWhichSynonymsAreKnown().count(id_of_interest) ==
- 0) {
+ auto ids_with_known_synonyms = fact_manager.GetIdsForWhichSynonymsAreKnown();
+ if (std::find(ids_with_known_synonyms.begin(), ids_with_known_synonyms.end(),
+ id_of_interest) == ids_with_known_synonyms.end()) {
return false;
}
auto available_synonyms = fact_manager.GetSynonymsForId(id_of_interest);
if (std::find_if(available_synonyms.begin(), available_synonyms.end(),
- [this](protobufs::DataDescriptor dd) -> bool {
- return DataDescriptorEquals()(&dd,
+ [this](const protobufs::DataDescriptor* dd) -> bool {
+ return DataDescriptorEquals()(dd,
&message_.data_descriptor());
}) == available_synonyms.end()) {
return false;
}
+ // Does the id use descriptor in the transformation identify an instruction?
auto use_instruction =
- transformation::FindInstruction(message_.id_use_descriptor(), context);
+ FindInstructionContainingUse(message_.id_use_descriptor(), context);
if (!use_instruction) {
return false;
}
+ // Is it legitimate to replace the use identified by the id use descriptor
+ // with a synonym?
if (!ReplacingUseWithSynonymIsOk(
context, use_instruction,
message_.id_use_descriptor().in_operand_index(),
@@ -74,8 +77,23 @@ bool TransformationReplaceIdWithSynonym::IsApplicable(
return false;
}
- assert(message_.fresh_id_for_temporary() == 0);
- assert(message_.data_descriptor().index().empty());
+ if (message_.fresh_id_for_temporary() == 0) {
+ if (!message_.data_descriptor().index().empty()) {
+ // If we have no id to use as a temporary variable, we should not have any
+ // indices to extract from.
+ return false;
+ }
+ } else {
+ if (!fuzzerutil::IsFreshId(context, message_.fresh_id_for_temporary())) {
+ // The id to be used as a temporary needs to be fresh.
+ return false;
+ }
+ if (message_.data_descriptor().index_size() != 1) {
+ // At present we support just a single index to allow extracting directly
+ // from a composite.
+ return false;
+ }
+ }
return true;
}
@@ -83,12 +101,79 @@ bool TransformationReplaceIdWithSynonym::IsApplicable(
void TransformationReplaceIdWithSynonym::Apply(
spvtools::opt::IRContext* context,
spvtools::fuzz::FactManager* /*unused*/) const {
- assert(message_.data_descriptor().index().empty());
auto instruction_to_change =
- transformation::FindInstruction(message_.id_use_descriptor(), context);
+ FindInstructionContainingUse(message_.id_use_descriptor(), context);
+
+ // Ultimately we are going to replace the id use identified in the
+ // transformation with |replacement_id|, which will either be the synonym's
+ // id, or the id of a temporary used to extract the synonym from a composite.
+ uint32_t replacement_id;
+
+ if (message_.fresh_id_for_temporary()) {
+ // The transformation having a temporary variable means that we need to
+ // extract the synonym from a composite.
+
+ uint32_t type_id_of_id_to_be_replaced =
+ context->get_def_use_mgr()
+ ->GetDef(message_.id_use_descriptor().id_of_interest())
+ ->type_id();
+ opt::analysis::Type* type_of_id_to_be_replaced =
+ context->get_type_mgr()->GetType(type_id_of_id_to_be_replaced);
+ opt::analysis::Type* type_of_composite = context->get_type_mgr()->GetType(
+ context->get_def_use_mgr()
+ ->GetDef(message_.data_descriptor().object())
+ ->type_id());
+
+ // Intuitively we want to make an OpCompositeExtract instruction, to get the
+ // synonym out of the composite. But in the case of a vector, the synonym
+ // might involve multiple vector indices; e.g. the y and z components of a
+ // vec4 might be synonymous with a vec2, and in that case OpCompositeExtract
+ // doesn't give us what we want; we need to use OpVectorShuffle instead.
+ std::unique_ptr<opt::Instruction> new_instruction;
+ if (type_of_composite->AsVector() &&
+ type_of_composite->AsVector()->element_type() !=
+ type_of_id_to_be_replaced) {
+ // We need to extract a vector from inside a vector, so we will need to
+ // use OpVectorShuffle.
+
+ assert(type_of_id_to_be_replaced->AsVector());
+ assert(type_of_id_to_be_replaced->AsVector()->element_type() ==
+ type_of_composite->AsVector()->element_type());
+ opt::Instruction::OperandList shuffle_operands = {
+ {SPV_OPERAND_TYPE_ID, {message_.data_descriptor().object()}},
+ {SPV_OPERAND_TYPE_ID, {message_.data_descriptor().object()}}};
+ for (uint32_t i = 0;
+ i < type_of_id_to_be_replaced->AsVector()->element_count(); i++) {
+ shuffle_operands.push_back({SPV_OPERAND_TYPE_LITERAL_INTEGER,
+ {message_.data_descriptor().index(0) + i}});
+ }
+ new_instruction = MakeUnique<opt::Instruction>(
+ context, SpvOpVectorShuffle, type_id_of_id_to_be_replaced,
+ message_.fresh_id_for_temporary(), shuffle_operands);
+ } else {
+ // We are either extracting from a non-vector, or extracting a scalar from
+ // a vector, so we can use OpCompositeExtract.
+ opt::Instruction::OperandList extract_operands = {
+ {SPV_OPERAND_TYPE_ID, {message_.data_descriptor().object()}},
+ {SPV_OPERAND_TYPE_LITERAL_INTEGER,
+ {message_.data_descriptor().index(0)}}};
+ new_instruction = MakeUnique<opt::Instruction>(
+ context, SpvOpCompositeExtract, type_id_of_id_to_be_replaced,
+ message_.fresh_id_for_temporary(), extract_operands);
+ }
+ instruction_to_change->InsertBefore(std::move(new_instruction));
+
+ // The replacement id is the temporary variable we used to extract the
+ // synonym from a composite.
+ replacement_id = message_.fresh_id_for_temporary();
+ fuzzerutil::UpdateModuleIdBound(context, replacement_id);
+ } else {
+ // The replacement id is the synonym's id.
+ replacement_id = message_.data_descriptor().object();
+ }
+
instruction_to_change->SetInOperand(
- message_.id_use_descriptor().in_operand_index(),
- {message_.data_descriptor().object()});
+ message_.id_use_descriptor().in_operand_index(), {replacement_id});
context->InvalidateAnalysesExceptFor(opt::IRContext::Analysis::kAnalysisNone);
}
@@ -191,22 +276,27 @@ bool TransformationReplaceIdWithSynonym::ReplacingUseWithSynonymIsOk(
// We now need to check that replacing the use with the synonym will respect
// dominance rules - i.e. the synonym needs to dominate the use.
- auto dominator_analysis = context->GetDominatorAnalysis(
- context->get_instr_block(use_instruction)->GetParent());
- if (use_instruction->opcode() == SpvOpPhi) {
- // In the case where the use is an operand to OpPhi, it is actually the
- // *parent* block associated with the operand that must be dominated by the
- // synonym.
- auto parent_block =
- use_instruction->GetSingleWordInOperand(use_in_operand_index + 1);
- if (!dominator_analysis->Dominates(
- context->get_instr_block(defining_instruction)->id(),
- parent_block)) {
+ // This is only relevant if the defining instruction is in a block; if it is
+ // not in a block then it is at global scope, and so replacing the use with it
+ // is fine.
+ if (context->get_instr_block(defining_instruction)) {
+ auto dominator_analysis = context->GetDominatorAnalysis(
+ context->get_instr_block(use_instruction)->GetParent());
+ if (use_instruction->opcode() == SpvOpPhi) {
+ // In the case where the use is an operand to OpPhi, it is actually the
+ // *parent* block associated with the operand that must be dominated by
+ // the synonym.
+ auto parent_block =
+ use_instruction->GetSingleWordInOperand(use_in_operand_index + 1);
+ if (!dominator_analysis->Dominates(
+ context->get_instr_block(defining_instruction)->id(),
+ parent_block)) {
+ return false;
+ }
+ } else if (!dominator_analysis->Dominates(defining_instruction,
+ use_instruction)) {
return false;
}
- } else if (!dominator_analysis->Dominates(defining_instruction,
- use_instruction)) {
- return false;
}
return true;
}