#include "source/fuzz/transformation_add_opphi_synonym.h"
#include "source/fuzz/fuzzer_util.h"
namespace spvtools {
namespace fuzz {
TransformationAddOpPhiSynonym::TransformationAddOpPhiSynonym(
protobufs::TransformationAddOpPhiSynonym message)
: message_(std::move(message)) {}
TransformationAddOpPhiSynonym::TransformationAddOpPhiSynonym(
uint32_t block_id, const std::map<uint32_t, uint32_t>& preds_to_ids,
uint32_t fresh_id) {
message_.set_block_id(block_id);
*message_.mutable_pred_to_id() =
fuzzerutil::MapToRepeatedUInt32Pair(preds_to_ids);
message_.set_fresh_id(fresh_id);
}
bool TransformationAddOpPhiSynonym::IsApplicable(
opt::IRContext* ir_context,
const TransformationContext& transformation_context) const {
auto block = fuzzerutil::MaybeFindBlock(ir_context, message_.block_id());
if (!block ||
transformation_context.GetFactManager()->BlockIsDead(block->id())) {
return false;
}
if (!fuzzerutil::IsFreshId(ir_context, message_.fresh_id())) {
return false;
}
std::vector<uint32_t> predecessors = ir_context->cfg()->preds(block->id());
if (predecessors.empty()) {
return false;
}
std::map<uint32_t, uint32_t> preds_to_ids =
fuzzerutil::RepeatedUInt32PairToMap(message_.pred_to_id());
if (preds_to_ids.size() != static_cast<size_t>(message_.pred_to_id_size())) {
return false;
}
for (uint32_t pred : predecessors) {
if (preds_to_ids.count(pred) == 0) {
return false;
}
if (!ir_context->get_def_use_mgr()->GetDef(preds_to_ids[pred])) {
return false;
}
}
uint32_t first_id = preds_to_ids[predecessors[0]];
uint32_t type_id = ir_context->get_def_use_mgr()->GetDef(first_id)->type_id();
if (!CheckTypeIsAllowed(ir_context, type_id)) {
return false;
}
for (uint32_t pred : predecessors) {
auto id = preds_to_ids[pred];
if (ir_context->get_def_use_mgr()->GetDef(id)->type_id() != type_id) {
return false;
}
if (id != first_id &&
!transformation_context.GetFactManager()->IsSynonymous(
MakeDataDescriptor(id, {}), MakeDataDescriptor(first_id, {}))) {
return false;
}
auto pred_block = ir_context->get_instr_block(pred);
assert(pred_block && "Could not find one of the predecessor blocks.");
if (!fuzzerutil::IdIsAvailableBeforeInstruction(
ir_context, pred_block->terminator(), id)) {
return false;
}
}
return true;
}
void TransformationAddOpPhiSynonym::Apply(
opt::IRContext* ir_context,
TransformationContext* transformation_context) const {
uint32_t first_id = message_.pred_to_id(0).second();
uint32_t type_id = ir_context->get_def_use_mgr()->GetDef(first_id)->type_id();
opt::Instruction::OperandList operand_list;
for (auto& pair : message_.pred_to_id()) {
operand_list.emplace_back(
opt::Operand{SPV_OPERAND_TYPE_ID, {pair.second()}});
operand_list.emplace_back(
opt::Operand{SPV_OPERAND_TYPE_ID, {pair.first()}});
}
ir_context->get_instr_block(message_.block_id())
->begin()
.InsertBefore(MakeUnique<opt::Instruction>(ir_context, spv::Op::OpPhi,
type_id, message_.fresh_id(),
std::move(operand_list)));
fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_id());
ir_context->InvalidateAnalysesExceptFor(
opt::IRContext::Analysis::kAnalysisNone);
transformation_context->GetFactManager()->AddFactDataSynonym(
MakeDataDescriptor(message_.fresh_id(), {}),
MakeDataDescriptor(first_id, {}));
}
protobufs::Transformation TransformationAddOpPhiSynonym::ToMessage() const {
protobufs::Transformation result;
*result.mutable_add_opphi_synonym() = message_;
return result;
}
bool TransformationAddOpPhiSynonym::CheckTypeIsAllowed(
opt::IRContext* ir_context, uint32_t type_id) {
auto type = ir_context->get_type_mgr()->GetType(type_id);
if (!type) {
return false;
}
if (type->AsBool() || type->AsInteger() || type->AsFloat() ||
type->AsVector() || type->AsMatrix() || type->AsArray() ||
type->AsStruct()) {
return true;
}
if (type->AsPointer()) {
auto storage_class = type->AsPointer()->storage_class();
return ir_context->get_feature_mgr()->HasCapability(
spv::Capability::VariablePointers) &&
(storage_class == spv::StorageClass::Workgroup ||
storage_class == spv::StorageClass::StorageBuffer);
}
return false;
}
std::unordered_set<uint32_t> TransformationAddOpPhiSynonym::GetFreshIds()
const {
return {message_.fresh_id()};
}
}
}