#include "source/fuzz/transformation_set_loop_control.h"
namespace spvtools {
namespace fuzz {
TransformationSetLoopControl::TransformationSetLoopControl(
protobufs::TransformationSetLoopControl message)
: message_(std::move(message)) {}
TransformationSetLoopControl::TransformationSetLoopControl(
uint32_t block_id, uint32_t loop_control, uint32_t peel_count,
uint32_t partial_count) {
message_.set_block_id(block_id);
message_.set_loop_control(loop_control);
message_.set_peel_count(peel_count);
message_.set_partial_count(partial_count);
}
bool TransformationSetLoopControl::IsApplicable(
opt::IRContext* ir_context, const TransformationContext& ) const {
auto block = ir_context->get_instr_block(message_.block_id());
if (!block) {
return false;
}
auto merge_inst = block->GetMergeInst();
if (!merge_inst || merge_inst->opcode() != spv::Op::OpLoopMerge) {
return false;
}
uint32_t all_loop_control_mask_bits_set = uint32_t(
spv::LoopControlMask::Unroll | spv::LoopControlMask::DontUnroll |
spv::LoopControlMask::DependencyInfinite |
spv::LoopControlMask::DependencyLength |
spv::LoopControlMask::MinIterations |
spv::LoopControlMask::MaxIterations |
spv::LoopControlMask::IterationMultiple |
spv::LoopControlMask::PeelCount | spv::LoopControlMask::PartialCount);
(void)(all_loop_control_mask_bits_set);
assert(!(message_.loop_control() & ~all_loop_control_mask_bits_set));
auto existing_loop_control_mask =
merge_inst->GetSingleWordInOperand(kLoopControlMaskInOperandIndex);
for (spv::LoopControlMask mask : {spv::LoopControlMask::DependencyInfinite,
spv::LoopControlMask::DependencyLength,
spv::LoopControlMask::MinIterations,
spv::LoopControlMask::MaxIterations,
spv::LoopControlMask::IterationMultiple}) {
if (LoopControlBitIsAddedByTransformation(mask,
existing_loop_control_mask)) {
return false;
}
}
if ((message_.loop_control() & uint32_t(spv::LoopControlMask::PeelCount)) &&
!PeelCountIsSupported(ir_context)) {
return false;
}
if ((message_.loop_control() &
uint32_t(spv::LoopControlMask::PartialCount)) &&
!PartialCountIsSupported(ir_context)) {
return false;
}
if (message_.peel_count() > 0 &&
!(message_.loop_control() & uint32_t(spv::LoopControlMask::PeelCount))) {
return false;
}
if (message_.partial_count() > 0 &&
!(message_.loop_control() &
uint32_t(spv::LoopControlMask::PartialCount))) {
return false;
}
return !(
(message_.loop_control() & uint32_t(spv::LoopControlMask::DontUnroll)) &&
(message_.loop_control() & uint32_t(spv::LoopControlMask::PeelCount |
spv::LoopControlMask::PartialCount)));
}
void TransformationSetLoopControl::Apply(
opt::IRContext* ir_context, TransformationContext* ) const {
auto merge_inst =
ir_context->get_instr_block(message_.block_id())->GetMergeInst();
auto existing_loop_control_mask =
merge_inst->GetSingleWordInOperand(kLoopControlMaskInOperandIndex);
opt::Instruction::OperandList new_operands;
new_operands.push_back(merge_inst->GetInOperand(0));
new_operands.push_back(merge_inst->GetInOperand(1));
new_operands.push_back(
{SPV_OPERAND_TYPE_LOOP_CONTROL, {message_.loop_control()}});
uint32_t literal_index = 0;
for (spv::LoopControlMask mask : {spv::LoopControlMask::DependencyLength,
spv::LoopControlMask::MinIterations,
spv::LoopControlMask::MaxIterations,
spv::LoopControlMask::IterationMultiple}) {
if (existing_loop_control_mask & uint32_t(mask)) {
if (message_.loop_control() & uint32_t(mask)) {
new_operands.push_back(
{SPV_OPERAND_TYPE_LITERAL_INTEGER,
{merge_inst->GetSingleWordInOperand(
kLoopControlFirstLiteralInOperandIndex + literal_index)}});
}
literal_index++;
}
}
if (message_.loop_control() & uint32_t(spv::LoopControlMask::PeelCount)) {
new_operands.push_back(
{SPV_OPERAND_TYPE_LITERAL_INTEGER, {message_.peel_count()}});
}
if (message_.loop_control() & uint32_t(spv::LoopControlMask::PartialCount)) {
new_operands.push_back(
{SPV_OPERAND_TYPE_LITERAL_INTEGER, {message_.partial_count()}});
}
merge_inst->SetInOperands(std::move(new_operands));
}
protobufs::Transformation TransformationSetLoopControl::ToMessage() const {
protobufs::Transformation result;
*result.mutable_set_loop_control() = message_;
return result;
}
bool TransformationSetLoopControl::LoopControlBitIsAddedByTransformation(
spv::LoopControlMask loop_control_single_bit_mask,
uint32_t existing_loop_control_mask) const {
return !(uint32_t(loop_control_single_bit_mask) &
existing_loop_control_mask) &&
(uint32_t(loop_control_single_bit_mask) & message_.loop_control());
}
bool TransformationSetLoopControl::PartialCountIsSupported(
opt::IRContext* ir_context) {
switch (ir_context->grammar().target_env()) {
case SPV_ENV_UNIVERSAL_1_0:
case SPV_ENV_UNIVERSAL_1_1:
case SPV_ENV_UNIVERSAL_1_2:
case SPV_ENV_UNIVERSAL_1_3:
case SPV_ENV_VULKAN_1_0:
case SPV_ENV_VULKAN_1_1:
return false;
default:
return true;
}
}
bool TransformationSetLoopControl::PeelCountIsSupported(
opt::IRContext* ir_context) {
switch (ir_context->grammar().target_env()) {
case SPV_ENV_UNIVERSAL_1_0:
case SPV_ENV_UNIVERSAL_1_1:
case SPV_ENV_UNIVERSAL_1_2:
case SPV_ENV_UNIVERSAL_1_3:
case SPV_ENV_VULKAN_1_0:
case SPV_ENV_VULKAN_1_1:
return false;
default:
return true;
}
}
std::unordered_set<uint32_t> TransformationSetLoopControl::GetFreshIds() const {
return std::unordered_set<uint32_t>();
}
}
}