spirv-fuzz: Get order right for OpSelect arguments (#3974)
Fixes #3948.
This commit is contained in:
Родитель
88f7bcb6af
Коммит
4f5423187d
|
@ -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
|
||||
|
|
Загрузка…
Ссылка в новой задаче