#include "source/fuzz/transformation_flatten_conditional_branch.h"
#include "source/fuzz/fuzzer_util.h"
#include "source/fuzz/instruction_descriptor.h"
namespace spvtools {
namespace fuzz {
TransformationFlattenConditionalBranch::TransformationFlattenConditionalBranch(
protobufs::TransformationFlattenConditionalBranch message)
: message_(std::move(message)) {}
TransformationFlattenConditionalBranch::TransformationFlattenConditionalBranch(
uint32_t header_block_id, bool true_branch_first,
uint32_t fresh_id_for_bvec2_selector, uint32_t fresh_id_for_bvec3_selector,
uint32_t fresh_id_for_bvec4_selector,
const std::vector<protobufs::SideEffectWrapperInfo>&
side_effect_wrappers_info) {
message_.set_header_block_id(header_block_id);
message_.set_true_branch_first(true_branch_first);
message_.set_fresh_id_for_bvec2_selector(fresh_id_for_bvec2_selector);
message_.set_fresh_id_for_bvec3_selector(fresh_id_for_bvec3_selector);
message_.set_fresh_id_for_bvec4_selector(fresh_id_for_bvec4_selector);
for (auto const& side_effect_wrapper_info : side_effect_wrappers_info) {
*message_.add_side_effect_wrapper_info() = side_effect_wrapper_info;
}
}
bool TransformationFlattenConditionalBranch::IsApplicable(
opt::IRContext* ir_context,
const TransformationContext& transformation_context) const {
auto header_block =
fuzzerutil::MaybeFindBlock(ir_context, message_.header_block_id());
if (!header_block || !header_block->GetMergeInst() ||
header_block->GetMergeInst()->opcode() != spv::Op::OpSelectionMerge) {
return false;
}
if (header_block->terminator()->opcode() != spv::Op::OpBranchConditional) {
return false;
}
if (transformation_context.GetFactManager()->IdIsIrrelevant(
header_block->terminator()->GetSingleWordInOperand(0))) {
return false;
}
std::set<uint32_t> used_fresh_ids;
for (uint32_t fresh_id_for_bvec_selector :
{message_.fresh_id_for_bvec2_selector(),
message_.fresh_id_for_bvec3_selector(),
message_.fresh_id_for_bvec4_selector()}) {
if (fresh_id_for_bvec_selector != 0) {
if (!CheckIdIsFreshAndNotUsedByThisTransformation(
fresh_id_for_bvec_selector, ir_context, &used_fresh_ids)) {
return false;
}
}
}
std::set<opt::Instruction*> instructions_that_need_ids;
if (!GetProblematicInstructionsIfConditionalCanBeFlattened(
ir_context, header_block, transformation_context,
&instructions_that_need_ids)) {
return false;
}
auto insts_to_wrapper_info = GetInstructionsToWrapperInfo(ir_context);
{
for (const auto& inst_to_info : insts_to_wrapper_info) {
for (uint32_t id : {inst_to_info.second.merge_block_id(),
inst_to_info.second.execute_block_id()}) {
if (!id || !CheckIdIsFreshAndNotUsedByThisTransformation(
id, ir_context, &used_fresh_ids)) {
return false;
}
}
if (InstructionNeedsPlaceholder(ir_context, *inst_to_info.first)) {
for (uint32_t id : {inst_to_info.second.actual_result_id(),
inst_to_info.second.alternative_block_id(),
inst_to_info.second.placeholder_result_id()}) {
if (!id || !CheckIdIsFreshAndNotUsedByThisTransformation(
id, ir_context, &used_fresh_ids)) {
return false;
}
}
auto value_def = ir_context->get_def_use_mgr()->GetDef(
inst_to_info.second.value_to_copy_id());
if (!value_def ||
value_def->type_id() != inst_to_info.first->type_id() ||
!fuzzerutil::IdIsAvailableBeforeInstruction(
ir_context, inst_to_info.first,
inst_to_info.second.value_to_copy_id())) {
return false;
}
}
}
}
for (auto instruction : instructions_that_need_ids) {
if (insts_to_wrapper_info.count(instruction) == 0 &&
!transformation_context.GetOverflowIdSource()->HasOverflowIds()) {
return false;
}
}
if (OpSelectArgumentsAreRestricted(ir_context)) {
uint32_t convergence_block_id =
FindConvergenceBlock(ir_context, *header_block);
if (!ir_context->cfg()
->block(convergence_block_id)
->WhileEachPhiInst([this,
ir_context](opt::Instruction* inst) -> bool {
opt::Instruction* phi_result_type =
ir_context->get_def_use_mgr()->GetDef(inst->type_id());
switch (phi_result_type->opcode()) {
case spv::Op::OpTypeBool:
case spv::Op::OpTypeInt:
case spv::Op::OpTypeFloat:
case spv::Op::OpTypePointer:
return true;
case spv::Op::OpTypeVector: {
uint32_t bool_type_id =
fuzzerutil::MaybeGetBoolType(ir_context);
if (!bool_type_id) {
return false;
}
uint32_t dimension =
phi_result_type->GetSingleWordInOperand(1);
if (fuzzerutil::MaybeGetVectorType(ir_context, bool_type_id,
dimension) == 0) {
return false;
}
switch (dimension) {
case 2:
return message_.fresh_id_for_bvec2_selector() != 0;
case 3:
return message_.fresh_id_for_bvec3_selector() != 0;
default:
assert(dimension == 4 && "Invalid vector dimension.");
return message_.fresh_id_for_bvec4_selector() != 0;
}
}
default:
return false;
}
})) {
return false;
}
}
return true;
}
void TransformationFlattenConditionalBranch::Apply(
opt::IRContext* ir_context,
TransformationContext* transformation_context) const {
std::vector<uint32_t> branches = {2, 1};
if (!message_.true_branch_first()) {
branches = {1, 2};
}
auto header_block = ir_context->cfg()->block(message_.header_block_id());
auto branch_instruction = header_block->terminator();
uint32_t first_block_first_branch_id =
branch_instruction->GetSingleWordInOperand(branches[1]);
uint32_t first_block_last_branch_id =
branch_instruction->GetSingleWordInOperand(branches[0]);
uint32_t convergence_block_id =
FindConvergenceBlock(ir_context, *header_block);
if (first_block_first_branch_id != first_block_last_branch_id) {
RewriteOpPhiInstructionsAtConvergenceBlock(
*header_block, convergence_block_id, ir_context);
}
auto insts_to_info = GetInstructionsToWrapperInfo(ir_context);
opt::BasicBlock* last_block_first_branch = nullptr;
std::vector<uint32_t> dead_blocks;
std::vector<uint32_t> irrelevant_ids;
for (uint32_t branch : branches) {
auto current_block = header_block;
uint32_t next_block_id = branch_instruction->GetSingleWordInOperand(branch);
while (next_block_id != convergence_block_id) {
current_block->GetParent()->MoveBasicBlockToAfter(next_block_id,
current_block);
current_block = ir_context->cfg()->block(next_block_id);
std::vector<opt::Instruction*> problematic_instructions;
current_block->ForEachInst(
[&problematic_instructions](opt::Instruction* instruction) {
if (instruction->opcode() != spv::Op::OpLabel &&
instruction->opcode() != spv::Op::OpBranch &&
!fuzzerutil::InstructionHasNoSideEffects(*instruction)) {
problematic_instructions.push_back(instruction);
}
});
uint32_t condition_id =
header_block->terminator()->GetSingleWordInOperand(0);
for (auto instruction : problematic_instructions) {
protobufs::SideEffectWrapperInfo wrapper_info;
if (insts_to_info.count(instruction) != 0) {
wrapper_info = insts_to_info[instruction];
} else {
wrapper_info.set_merge_block_id(
transformation_context->GetOverflowIdSource()
->GetNextOverflowId());
wrapper_info.set_execute_block_id(
transformation_context->GetOverflowIdSource()
->GetNextOverflowId());
if (InstructionNeedsPlaceholder(ir_context, *instruction)) {
wrapper_info.set_actual_result_id(
transformation_context->GetOverflowIdSource()
->GetNextOverflowId());
wrapper_info.set_alternative_block_id(
transformation_context->GetOverflowIdSource()
->GetNextOverflowId());
wrapper_info.set_placeholder_result_id(
transformation_context->GetOverflowIdSource()
->GetNextOverflowId());
for (bool is_irrelevant : {true, false}) {
wrapper_info.set_value_to_copy_id(
fuzzerutil::MaybeGetZeroConstant(
ir_context, *transformation_context,
instruction->type_id(), is_irrelevant));
if (wrapper_info.value_to_copy_id()) {
break;
}
}
}
}
current_block = EncloseInstructionInConditional(
ir_context, *transformation_context, current_block, instruction,
wrapper_info, condition_id, branch == 1, &dead_blocks,
&irrelevant_ids);
}
next_block_id = current_block->terminator()->GetSingleWordInOperand(0);
if (next_block_id == convergence_block_id && branch == branches[1]) {
last_block_first_branch = current_block;
}
}
}
uint32_t after_header = first_block_first_branch_id != convergence_block_id
? first_block_first_branch_id
: first_block_last_branch_id;
auto merge_inst = header_block->GetMergeInst();
ir_context->KillInst(branch_instruction);
ir_context->KillInst(merge_inst);
header_block->AddInstruction(MakeUnique<opt::Instruction>(
ir_context, spv::Op::OpBranch, 0, 0,
opt::Instruction::OperandList{{SPV_OPERAND_TYPE_ID, {after_header}}}));
if (last_block_first_branch) {
last_block_first_branch->terminator()->SetInOperand(
0, {first_block_last_branch_id});
if (first_block_last_branch_id != convergence_block_id) {
ir_context->get_instr_block(first_block_last_branch_id)
->ForEachPhiInst(
[&last_block_first_branch](opt::Instruction* phi_inst) {
phi_inst->SetInOperand(1, {last_block_first_branch->id()});
});
}
}
ir_context->InvalidateAnalysesExceptFor(opt::IRContext::kAnalysisNone);
for (auto dead_block : dead_blocks) {
transformation_context->GetFactManager()->AddFactBlockIsDead(dead_block);
}
for (auto irrelevant_id : irrelevant_ids) {
transformation_context->GetFactManager()->AddFactIdIsIrrelevant(
irrelevant_id);
}
}
protobufs::Transformation TransformationFlattenConditionalBranch::ToMessage()
const {
protobufs::Transformation result;
*result.mutable_flatten_conditional_branch() = message_;
return result;
}
bool TransformationFlattenConditionalBranch::
GetProblematicInstructionsIfConditionalCanBeFlattened(
opt::IRContext* ir_context, opt::BasicBlock* header,
const TransformationContext& transformation_context,
std::set<opt::Instruction*>* instructions_that_need_ids) {
uint32_t merge_block_id = header->MergeBlockIdIfAny();
assert(merge_block_id &&
header->GetMergeInst()->opcode() == spv::Op::OpSelectionMerge &&
header->terminator()->opcode() == spv::Op::OpBranchConditional &&
"|header| must be the header of a conditional.");
if (!ir_context->IsReachable(*header)) {
return false;
}
auto enclosing_function = header->GetParent();
auto dominator_analysis =
ir_context->GetDominatorAnalysis(enclosing_function);
auto postdominator_analysis =
ir_context->GetPostDominatorAnalysis(enclosing_function);
if (!dominator_analysis->Dominates(header->id(), merge_block_id) ||
!postdominator_analysis->Dominates(merge_block_id, header->id())) {
return false;
}
std::queue<uint32_t> to_check;
header->ForEachSuccessorLabel(
[&to_check](uint32_t label) { to_check.push(label); });
auto* structured_cfg = ir_context->GetStructuredCFGAnalysis();
while (!to_check.empty()) {
uint32_t block_id = to_check.front();
to_check.pop();
if (structured_cfg->ContainingConstruct(block_id) != header->id() &&
block_id != merge_block_id) {
return false;
}
if (postdominator_analysis->Dominates(block_id, header->id())) {
continue;
}
if (!transformation_context.GetFactManager()->BlockIsDead(header->id()) &&
transformation_context.GetFactManager()->BlockIsDead(block_id)) {
return false;
}
auto block = ir_context->cfg()->block(block_id);
if (block->GetMergeInst()) {
return false;
}
if (block->terminator()->opcode() != spv::Op::OpBranch) {
return false;
}
std::unordered_set<uint32_t> synonym_base_objects;
for (auto* synonym :
transformation_context.GetFactManager()->GetAllSynonyms()) {
synonym_base_objects.insert(synonym->object());
}
bool all_instructions_compatible = block->WhileEachInst(
[ir_context, instructions_that_need_ids,
&synonym_base_objects](opt::Instruction* instruction) {
if (instruction->opcode() == spv::Op::OpLabel) {
return true;
}
if (instruction->result_id() &&
synonym_base_objects.count(instruction->result_id())) {
return false;
}
if (instruction->IsBranch()) {
return instruction->opcode() == spv::Op::OpBranch;
}
if (!InstructionCanBeHandled(ir_context, *instruction)) {
return false;
}
if (!fuzzerutil::InstructionHasNoSideEffects(*instruction)) {
instructions_that_need_ids->emplace(instruction);
}
return true;
});
if (!all_instructions_compatible) {
return false;
}
to_check.push(block->terminator()->GetSingleWordInOperand(0));
}
return true;
}
bool TransformationFlattenConditionalBranch::InstructionNeedsPlaceholder(
opt::IRContext* ir_context, const opt::Instruction& instruction) {
assert(!fuzzerutil::InstructionHasNoSideEffects(instruction) &&
InstructionCanBeHandled(ir_context, instruction) &&
"The instruction must have side effects and it must be possible to "
"enclose it inside a conditional.");
if (instruction.HasResultId()) {
auto type = ir_context->get_type_mgr()->GetType(instruction.type_id());
return type && !type->AsVoid();
}
return false;
}
std::unordered_map<opt::Instruction*, protobufs::SideEffectWrapperInfo>
TransformationFlattenConditionalBranch::GetInstructionsToWrapperInfo(
opt::IRContext* ir_context) const {
std::unordered_map<opt::Instruction*, protobufs::SideEffectWrapperInfo>
instructions_to_ids;
for (const auto& wrapper_info : message_.side_effect_wrapper_info()) {
auto instruction = FindInstruction(wrapper_info.instruction(), ir_context);
if (instruction) {
instructions_to_ids.emplace(instruction, wrapper_info);
}
}
return instructions_to_ids;
}
opt::BasicBlock*
TransformationFlattenConditionalBranch::EncloseInstructionInConditional(
opt::IRContext* ir_context,
const TransformationContext& transformation_context, opt::BasicBlock* block,
opt::Instruction* instruction,
const protobufs::SideEffectWrapperInfo& wrapper_info, uint32_t condition_id,
bool exec_if_cond_true, std::vector<uint32_t>* dead_blocks,
std::vector<uint32_t>* irrelevant_ids) {
auto next_instruction = instruction->NextNode();
for (uint32_t id :
{wrapper_info.merge_block_id(), wrapper_info.execute_block_id()}) {
fuzzerutil::UpdateModuleIdBound(ir_context, id);
}
auto execute_block = block->SplitBasicBlock(
ir_context, wrapper_info.execute_block_id(),
fuzzerutil::GetIteratorForInstruction(block, instruction));
auto merge_block = execute_block->SplitBasicBlock(
ir_context, wrapper_info.merge_block_id(),
fuzzerutil::GetIteratorForInstruction(execute_block, next_instruction));
if (transformation_context.GetFactManager()->BlockIsDead(block->id())) {
dead_blocks->emplace_back(execute_block->id());
dead_blocks->emplace_back(merge_block->id());
}
auto alternative_block = merge_block;
execute_block->AddInstruction(MakeUnique<opt::Instruction>(
ir_context, spv::Op::OpBranch, 0, 0,
opt::Instruction::OperandList{
{SPV_OPERAND_TYPE_ID, {merge_block->id()}}}));
if (InstructionNeedsPlaceholder(ir_context, *instruction)) {
for (uint32_t id :
{wrapper_info.actual_result_id(), wrapper_info.alternative_block_id(),
wrapper_info.placeholder_result_id()}) {
fuzzerutil::UpdateModuleIdBound(ir_context, id);
}
auto alternative_block_temp = MakeUnique<opt::BasicBlock>(
MakeUnique<opt::Instruction>(ir_context, spv::Op::OpLabel, 0,
wrapper_info.alternative_block_id(),
opt::Instruction::OperandList{}));
uint32_t original_result_id = instruction->result_id();
instruction->SetResultId(wrapper_info.actual_result_id());
if (wrapper_info.value_to_copy_id()) {
alternative_block_temp->AddInstruction(MakeUnique<opt::Instruction>(
ir_context, spv::Op::OpCopyObject, instruction->type_id(),
wrapper_info.placeholder_result_id(),
opt::Instruction::OperandList{
{SPV_OPERAND_TYPE_ID, {wrapper_info.value_to_copy_id()}}}));
} else {
alternative_block_temp->AddInstruction(MakeUnique<opt::Instruction>(
ir_context, spv::Op::OpUndef, instruction->type_id(),
wrapper_info.placeholder_result_id(),
opt::Instruction::OperandList{}));
}
irrelevant_ids->emplace_back(wrapper_info.placeholder_result_id());
alternative_block_temp->AddInstruction(MakeUnique<opt::Instruction>(
ir_context, spv::Op::OpBranch, 0, 0,
opt::Instruction::OperandList{
{SPV_OPERAND_TYPE_ID, {merge_block->id()}}}));
alternative_block = block->GetParent()->InsertBasicBlockBefore(
std::move(alternative_block_temp), merge_block);
merge_block->begin().InsertBefore(MakeUnique<opt::Instruction>(
ir_context, spv::Op::OpPhi, instruction->type_id(), original_result_id,
opt::Instruction::OperandList{
{SPV_OPERAND_TYPE_ID, {instruction->result_id()}},
{SPV_OPERAND_TYPE_ID, {execute_block->id()}},
{SPV_OPERAND_TYPE_ID, {wrapper_info.placeholder_result_id()}},
{SPV_OPERAND_TYPE_ID, {alternative_block->id()}}}));
if (transformation_context.GetFactManager()->BlockIsDead(block->id())) {
dead_blocks->emplace_back(alternative_block->id());
}
}
auto if_block_id = (exec_if_cond_true ? execute_block : alternative_block)
->GetLabel()
->result_id();
auto else_block_id = (exec_if_cond_true ? alternative_block : execute_block)
->GetLabel()
->result_id();
block->AddInstruction(MakeUnique<opt::Instruction>(
ir_context, spv::Op::OpSelectionMerge, 0, 0,
opt::Instruction::OperandList{
{SPV_OPERAND_TYPE_ID, {merge_block->id()}},
{SPV_OPERAND_TYPE_SELECTION_CONTROL,
{uint32_t(spv::SelectionControlMask::MaskNone)}}}));
block->AddInstruction(MakeUnique<opt::Instruction>(
ir_context, spv::Op::OpBranchConditional, 0, 0,
opt::Instruction::OperandList{{SPV_OPERAND_TYPE_ID, {condition_id}},
{SPV_OPERAND_TYPE_ID, {if_block_id}},
{SPV_OPERAND_TYPE_ID, {else_block_id}}}));
return merge_block;
}
bool TransformationFlattenConditionalBranch::InstructionCanBeHandled(
opt::IRContext* ir_context, const opt::Instruction& instruction) {
if (fuzzerutil::InstructionHasNoSideEffects(instruction)) {
return true;
}
if (instruction.opcode() == spv::Op::OpControlBarrier ||
instruction.opcode() == spv::Op::OpMemoryBarrier ||
instruction.opcode() == spv::Op::OpNamedBarrierInitialize ||
instruction.opcode() == spv::Op::OpMemoryNamedBarrier ||
instruction.opcode() == spv::Op::OpTypeNamedBarrier) {
return false;
}
if (instruction.opcode() == spv::Op::OpSampledImage) {
return false;
}
if (instruction.opcode() == spv::Op::OpLoad &&
ir_context->get_def_use_mgr()->GetDef(instruction.type_id())->opcode() ==
spv::Op::OpTypeSampledImage) {
return false;
}
if (instruction.HasResultId()) {
auto type = ir_context->get_type_mgr()->GetType(instruction.type_id());
assert(type && "The type should be found in the module");
if (type->AsVoid() &&
!ir_context->get_def_use_mgr()->WhileEachUse(
instruction.result_id(),
[](opt::Instruction* use_inst, uint32_t use_index) {
return use_index <
use_inst->NumOperands() - use_inst->NumInOperands();
})) {
return false;
}
}
return true;
}
std::unordered_set<uint32_t>
TransformationFlattenConditionalBranch::GetFreshIds() const {
std::unordered_set<uint32_t> result = {
message_.fresh_id_for_bvec2_selector(),
message_.fresh_id_for_bvec3_selector(),
message_.fresh_id_for_bvec4_selector()};
for (auto& side_effect_wrapper_info : message_.side_effect_wrapper_info()) {
result.insert(side_effect_wrapper_info.merge_block_id());
result.insert(side_effect_wrapper_info.execute_block_id());
result.insert(side_effect_wrapper_info.actual_result_id());
result.insert(side_effect_wrapper_info.alternative_block_id());
result.insert(side_effect_wrapper_info.placeholder_result_id());
}
return result;
}
uint32_t TransformationFlattenConditionalBranch::FindConvergenceBlock(
opt::IRContext* ir_context, const opt::BasicBlock& header_block) {
uint32_t result = header_block.terminator()->GetSingleWordInOperand(1);
auto postdominator_analysis =
ir_context->GetPostDominatorAnalysis(header_block.GetParent());
while (!postdominator_analysis->Dominates(result, header_block.id())) {
auto current_block = ir_context->get_instr_block(result);
result = current_block->terminator()->GetSingleWordInOperand(0);
}
return result;
}
bool TransformationFlattenConditionalBranch::OpSelectArgumentsAreRestricted(
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 true;
}
default:
return false;
}
}
void TransformationFlattenConditionalBranch::AddBooleanVectorConstructorToBlock(
uint32_t fresh_id, uint32_t dimension,
const opt::Operand& branch_condition_operand, opt::IRContext* ir_context,
opt::BasicBlock* block) {
opt::Instruction::OperandList in_operands;
for (uint32_t i = 0; i < dimension; i++) {
in_operands.emplace_back(branch_condition_operand);
}
block->begin()->InsertBefore(MakeUnique<opt::Instruction>(
ir_context, spv::Op::OpCompositeConstruct,
fuzzerutil::MaybeGetVectorType(
ir_context, fuzzerutil::MaybeGetBoolType(ir_context), dimension),
fresh_id, in_operands));
fuzzerutil::UpdateModuleIdBound(ir_context, fresh_id);
}
void TransformationFlattenConditionalBranch::
RewriteOpPhiInstructionsAtConvergenceBlock(
const opt::BasicBlock& header_block, uint32_t convergence_block_id,
opt::IRContext* ir_context) const {
const opt::Instruction& branch_instruction = *header_block.terminator();
const opt::Operand& branch_condition_operand =
branch_instruction.GetInOperand(0);
bool require_2d_boolean_vector = false;
bool require_3d_boolean_vector = false;
bool require_4d_boolean_vector = false;
opt::BasicBlock* convergence_block =
ir_context->get_instr_block(convergence_block_id);
convergence_block->ForEachPhiInst(
[this, &branch_condition_operand, branch_instruction,
convergence_block_id, &header_block, ir_context,
&require_2d_boolean_vector, &require_3d_boolean_vector,
&require_4d_boolean_vector](opt::Instruction* phi_inst) {
assert(phi_inst->NumInOperands() == 4 &&
"We are going to replace an OpPhi with an OpSelect. This "
"only makes sense if the block has two distinct "
"predecessors.");
opt::Operand selector_operand = branch_condition_operand;
opt::Instruction* type_inst =
ir_context->get_def_use_mgr()->GetDef(phi_inst->type_id());
if (type_inst->opcode() == spv::Op::OpTypeVector) {
uint32_t dimension = type_inst->GetSingleWordInOperand(1);
switch (dimension) {
case 2:
if (message_.fresh_id_for_bvec2_selector() != 0) {
selector_operand = {SPV_OPERAND_TYPE_ID,
{message_.fresh_id_for_bvec2_selector()}};
require_2d_boolean_vector = true;
}
break;
case 3:
if (message_.fresh_id_for_bvec3_selector() != 0) {
selector_operand = {SPV_OPERAND_TYPE_ID,
{message_.fresh_id_for_bvec3_selector()}};
require_3d_boolean_vector = true;
}
break;
case 4:
if (message_.fresh_id_for_bvec4_selector() != 0) {
selector_operand = {SPV_OPERAND_TYPE_ID,
{message_.fresh_id_for_bvec4_selector()}};
require_4d_boolean_vector = true;
}
break;
default:
assert(dimension == 4 && "Invalid vector dimension.");
break;
}
}
std::vector<opt::Operand> operands;
operands.emplace_back(selector_operand);
uint32_t branch_instruction_true_block_id =
branch_instruction.GetSingleWordInOperand(1);
uint32_t branch_instruction_false_block_id =
branch_instruction.GetSingleWordInOperand(2);
if (branch_instruction_true_block_id == convergence_block_id) {
assert(branch_instruction_false_block_id != convergence_block_id &&
"Control should not reach here if both branches target the "
"convergence block.");
if (phi_inst->GetSingleWordInOperand(1) ==
message_.header_block_id()) {
operands.emplace_back(phi_inst->GetInOperand(0));
operands.emplace_back(phi_inst->GetInOperand(2));
} else {
assert(phi_inst->GetSingleWordInOperand(3) ==
message_.header_block_id() &&
"Since the convergence block has the header block as one of "
"two predecessors, if it is not handled by the first pair "
"of operands of this OpPhi instruction it should be handled "
"by the second pair.");
operands.emplace_back(phi_inst->GetInOperand(2));
operands.emplace_back(phi_inst->GetInOperand(0));
}
} else if (branch_instruction_false_block_id == convergence_block_id) {
if (phi_inst->GetSingleWordInOperand(1) ==
message_.header_block_id()) {
operands.emplace_back(phi_inst->GetInOperand(2));
operands.emplace_back(phi_inst->GetInOperand(0));
} else {
assert(phi_inst->GetSingleWordInOperand(3) ==
message_.header_block_id() &&
"Since the convergence block has the header block as one of "
"two predecessors, if it is not handled by the first pair "
"of operands of this OpPhi instruction it should be handled "
"by the second pair.");
operands.emplace_back(phi_inst->GetInOperand(0));
operands.emplace_back(phi_inst->GetInOperand(2));
}
} else if (ir_context->GetDominatorAnalysis(header_block.GetParent())
->Dominates(branch_instruction_true_block_id,
phi_inst->GetSingleWordInOperand(1))) {
operands.emplace_back(phi_inst->GetInOperand(0));
operands.emplace_back(phi_inst->GetInOperand(2));
} else {
operands.emplace_back(phi_inst->GetInOperand(2));
operands.emplace_back(phi_inst->GetInOperand(0));
}
phi_inst->SetOpcode(spv::Op::OpSelect);
phi_inst->SetInOperands(std::move(operands));
});
if (require_2d_boolean_vector) {
AddBooleanVectorConstructorToBlock(message_.fresh_id_for_bvec2_selector(),
2, branch_condition_operand, ir_context,
convergence_block);
}
if (require_3d_boolean_vector) {
AddBooleanVectorConstructorToBlock(message_.fresh_id_for_bvec3_selector(),
3, branch_condition_operand, ir_context,
convergence_block);
}
if (require_4d_boolean_vector) {
AddBooleanVectorConstructorToBlock(message_.fresh_id_for_bvec4_selector(),
4, branch_condition_operand, ir_context,
convergence_block);
}
}
}
}