spirv-fuzz: Get order right for OpSelect arguments (#3974)

Fixes #3948.
This commit is contained in:
Alastair Donaldson 2020-10-22 16:05:51 +01:00 коммит произвёл GitHub
Родитель 88f7bcb6af
Коммит 4f5423187d
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
2 изменённых файлов: 205 добавлений и 15 удалений

Просмотреть файл

@ -844,8 +844,9 @@ void TransformationFlattenConditionalBranch::
opt::BasicBlock* convergence_block =
ir_context->get_instr_block(convergence_block_id);
convergence_block->ForEachPhiInst(
[this, &branch_condition_operand, branch_instruction, &header_block,
ir_context, &require_2d_boolean_vector, &require_3d_boolean_vector,
[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 "
@ -880,8 +881,7 @@ void TransformationFlattenConditionalBranch::
require_3d_boolean_vector = true;
}
break;
default:
assert(dimension == 4 && "Invalid vector dimension.");
case 4:
// Similar to the 2D case.
if (message_.fresh_id_for_bvec4_selector() != 0) {
selector_operand = {SPV_OPERAND_TYPE_ID,
@ -889,6 +889,9 @@ void TransformationFlattenConditionalBranch::
require_4d_boolean_vector = true;
}
break;
default:
assert(dimension == 4 && "Invalid vector dimension.");
break;
}
}
std::vector<opt::Operand> operands;
@ -896,26 +899,69 @@ void TransformationFlattenConditionalBranch::
uint32_t branch_instruction_true_block_id =
branch_instruction.GetSingleWordInOperand(1);
uint32_t branch_instruction_false_block_id =
branch_instruction.GetSingleWordInOperand(2);
// The OpPhi takes values from two distinct predecessors. One
// predecessor is associated with the "true" path of the conditional
// we are flattening, the other with the "false" path, but these
// predecessors can appear in either order as operands to the OpPhi
// instruction. We determine in which order the OpPhi inputs should
// appear as OpSelect arguments by checking dominance of the true and
// false immediate successors of the header block.
if (ir_context->GetDominatorAnalysis(header_block.GetParent())
->Dominates(branch_instruction_true_block_id,
phi_inst->GetSingleWordInOperand(1))) {
// The "true" branch is handled first in the OpPhi's operands; we
// thus provide operands to OpSelect in the same order that they
// appear in the OpPhi.
// appear as OpSelect arguments by first checking whether the
// convergence block is a direct successor of the selection header, and
// otherwise checking dominance of the true and false immediate
// successors of the header block.
if (branch_instruction_true_block_id == convergence_block_id) {
// The branch instruction's true block is the convergence block. This
// means that the OpPhi's value associated with the branch
// instruction's block should the "true" result of the OpSelect.
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) {
// The branch instruction's false block is the convergence block. This
// means that the OpPhi's value associated with the branch
// instruction's block should the "false" result of the OpSelect.
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))) {
// The "true" branch of the conditional is handled first in the
// OpPhi's operands; we thus provide operands to OpSelect in the same
// order that they appear in the OpPhi.
operands.emplace_back(phi_inst->GetInOperand(0));
operands.emplace_back(phi_inst->GetInOperand(2));
} else {
// The "false" branch is handled first in the OpPhi's operands; we
// thus provide operands to OpSelect in reverse of the order that
// they appear in the OpPhi.
// The "false" branch of the conditional is handled first in the
// OpPhi's operands; we thus provide operands to OpSelect in reverse
// of the order that they appear in the OpPhi.
operands.emplace_back(phi_inst->GetInOperand(2));
operands.emplace_back(phi_inst->GetInOperand(0));
}

Просмотреть файл

@ -1897,6 +1897,150 @@ TEST(TransformationFlattenConditionalBranchTest,
.IsApplicable(context.get(), transformation_context));
}
TEST(TransformationFlattenConditionalBranchTest,
OpPhiWhenTrueBranchIsConvergenceBlock) {
std::string shader = R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %4 "main"
OpExecutionMode %4 OriginUpperLeft
OpSource ESSL 320
OpName %4 "main"
%2 = OpTypeVoid
%3 = OpTypeFunction %2
%6 = OpTypeBool
%7 = OpConstantTrue %6
%4 = OpFunction %2 None %3
%5 = OpLabel
OpSelectionMerge %9 None
OpBranchConditional %7 %9 %8
%8 = OpLabel
%10 = OpCopyObject %6 %7
OpBranch %9
%9 = OpLabel
%11 = OpPhi %6 %10 %8 %7 %5
%12 = OpPhi %6 %7 %5 %10 %8
OpReturn
OpFunctionEnd
)";
const auto env = SPV_ENV_UNIVERSAL_1_3;
const auto consumer = nullptr;
const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
spvtools::ValidatorOptions validator_options;
ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
kConsoleMessageConsumer));
TransformationContext transformation_context(
MakeUnique<FactManager>(context.get()), validator_options);
TransformationFlattenConditionalBranch transformation(5, true, 0, 0, 0, {});
ASSERT_TRUE(
transformation.IsApplicable(context.get(), transformation_context));
ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context);
ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
kConsoleMessageConsumer));
std::string expected = R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %4 "main"
OpExecutionMode %4 OriginUpperLeft
OpSource ESSL 320
OpName %4 "main"
%2 = OpTypeVoid
%3 = OpTypeFunction %2
%6 = OpTypeBool
%7 = OpConstantTrue %6
%4 = OpFunction %2 None %3
%5 = OpLabel
OpBranch %8
%8 = OpLabel
%10 = OpCopyObject %6 %7
OpBranch %9
%9 = OpLabel
%11 = OpSelect %6 %7 %7 %10
%12 = OpSelect %6 %7 %7 %10
OpReturn
OpFunctionEnd
)";
ASSERT_TRUE(IsEqual(env, expected, context.get()));
}
TEST(TransformationFlattenConditionalBranchTest,
OpPhiWhenFalseBranchIsConvergenceBlock) {
std::string shader = R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %4 "main"
OpExecutionMode %4 OriginUpperLeft
OpSource ESSL 320
OpName %4 "main"
%2 = OpTypeVoid
%3 = OpTypeFunction %2
%6 = OpTypeBool
%7 = OpConstantTrue %6
%4 = OpFunction %2 None %3
%5 = OpLabel
OpSelectionMerge %9 None
OpBranchConditional %7 %8 %9
%8 = OpLabel
%10 = OpCopyObject %6 %7
OpBranch %9
%9 = OpLabel
%11 = OpPhi %6 %10 %8 %7 %5
%12 = OpPhi %6 %7 %5 %10 %8
OpReturn
OpFunctionEnd
)";
const auto env = SPV_ENV_UNIVERSAL_1_3;
const auto consumer = nullptr;
const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
spvtools::ValidatorOptions validator_options;
ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
kConsoleMessageConsumer));
TransformationContext transformation_context(
MakeUnique<FactManager>(context.get()), validator_options);
TransformationFlattenConditionalBranch transformation(5, true, 0, 0, 0, {});
ASSERT_TRUE(
transformation.IsApplicable(context.get(), transformation_context));
ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context);
ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
kConsoleMessageConsumer));
std::string expected = R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %4 "main"
OpExecutionMode %4 OriginUpperLeft
OpSource ESSL 320
OpName %4 "main"
%2 = OpTypeVoid
%3 = OpTypeFunction %2
%6 = OpTypeBool
%7 = OpConstantTrue %6
%4 = OpFunction %2 None %3
%5 = OpLabel
OpBranch %8
%8 = OpLabel
%10 = OpCopyObject %6 %7
OpBranch %9
%9 = OpLabel
%11 = OpSelect %6 %7 %10 %7
%12 = OpSelect %6 %7 %10 %7
OpReturn
OpFunctionEnd
)";
ASSERT_TRUE(IsEqual(env, expected, context.get()));
}
} // namespace
} // namespace fuzz
} // namespace spvtools