spirv-fuzz: Handle OpPhis in TransformationInlineFunction (#3833)
Fixes #3829.
diff --git a/source/fuzz/transformation_inline_function.cpp b/source/fuzz/transformation_inline_function.cpp
index f244f0d..dfa66f8 100644
--- a/source/fuzz/transformation_inline_function.cpp
+++ b/source/fuzz/transformation_inline_function.cpp
@@ -211,31 +211,46 @@
const auto result_id_map =
fuzzerutil::RepeatedUInt32PairToMap(message_.result_id_map());
- // Replaces the operand ids with their mapped result ids.
- instruction_to_be_inlined->ForEachInId([called_function,
- function_call_instruction,
- &result_id_map](uint32_t* id) {
- // If |id| is mapped, then set it to its mapped value.
- if (result_id_map.count(*id)) {
- *id = result_id_map.at(*id);
- return;
- }
+ const auto* function_call_block =
+ ir_context->get_instr_block(function_call_instruction);
+ assert(function_call_block && "OpFunctionCall must belong to some block");
- uint32_t parameter_index = 0;
- called_function->ForEachParam(
- [id, function_call_instruction,
- ¶meter_index](opt::Instruction* parameter_instruction) {
- // If the id is a function parameter, then set it to the
- // parameter value passed in the function call instruction.
- if (*id == parameter_instruction->result_id()) {
- // We do + 1 because the first in-operand for OpFunctionCall is
- // the function id that is being called.
- *id = function_call_instruction->GetSingleWordInOperand(
- parameter_index + 1);
- }
- parameter_index++;
- });
- });
+ // Replaces the operand ids with their mapped result ids.
+ instruction_to_be_inlined->ForEachInId(
+ [called_function, function_call_instruction, &result_id_map,
+ function_call_block](uint32_t* id) {
+ // We are not inlining the entry block of the |called_function|.
+ //
+ // We must check this condition first since we can't use the fresh id
+ // from |result_id_map| even if it has one. This is because that fresh
+ // id will never be added to the module since entry blocks are not
+ // inlined.
+ if (*id == called_function->entry()->id()) {
+ *id = function_call_block->id();
+ return;
+ }
+
+ // If |id| is mapped, then set it to its mapped value.
+ if (result_id_map.count(*id)) {
+ *id = result_id_map.at(*id);
+ return;
+ }
+
+ uint32_t parameter_index = 0;
+ called_function->ForEachParam(
+ [id, function_call_instruction,
+ ¶meter_index](opt::Instruction* parameter_instruction) {
+ // If the id is a function parameter, then set it to the
+ // parameter value passed in the function call instruction.
+ if (*id == parameter_instruction->result_id()) {
+ // We do + 1 because the first in-operand for OpFunctionCall is
+ // the function id that is being called.
+ *id = function_call_instruction->GetSingleWordInOperand(
+ parameter_index + 1);
+ }
+ parameter_index++;
+ });
+ });
// If |instruction_to_be_inlined| has result id, then set it to its mapped
// value.
diff --git a/test/fuzz/transformation_inline_function_test.cpp b/test/fuzz/transformation_inline_function_test.cpp
index 694f19c..1d3b049 100644
--- a/test/fuzz/transformation_inline_function_test.cpp
+++ b/test/fuzz/transformation_inline_function_test.cpp
@@ -13,6 +13,7 @@
// limitations under the License.
#include "source/fuzz/transformation_inline_function.h"
+
#include "source/fuzz/instruction_descriptor.h"
#include "test/fuzz/fuzz_test_util.h"
@@ -751,6 +752,81 @@
ASSERT_TRUE(IsEqual(env, variant_shader, context.get()));
}
+TEST(TransformationInlineFunctionTest, HandlesOpPhisInTheSecondBlock) {
+ std::string shader = R"(
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %4 "main"
+ OpExecutionMode %4 OriginUpperLeft
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %10 = OpTypeInt 32 0
+ %11 = OpUndef %10
+ %4 = OpFunction %2 None %3
+ %5 = OpLabel
+ %6 = OpFunctionCall %2 %7
+ OpBranch %14
+ %14 = OpLabel
+ OpReturn
+ OpFunctionEnd
+ %7 = OpFunction %2 None %3
+ %8 = OpLabel
+ OpBranch %13
+ %13 = OpLabel
+ %12 = OpPhi %10 %11 %8
+ OpReturn
+ OpFunctionEnd
+ )";
+
+ const auto env = SPV_ENV_UNIVERSAL_1_3;
+ const auto consumer = nullptr;
+ const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+ ASSERT_TRUE(IsValid(env, context.get()));
+
+ FactManager fact_manager;
+ spvtools::ValidatorOptions validator_options;
+ TransformationContext transformation_context(&fact_manager,
+ validator_options);
+
+ TransformationInlineFunction transformation(6,
+ {{{8, 20}, {13, 21}, {12, 22}}});
+ ASSERT_TRUE(
+ transformation.IsApplicable(context.get(), transformation_context));
+ transformation.Apply(context.get(), &transformation_context);
+ ASSERT_TRUE(IsValid(env, context.get()));
+
+ std::string after_transformation = R"(
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %4 "main"
+ OpExecutionMode %4 OriginUpperLeft
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %10 = OpTypeInt 32 0
+ %11 = OpUndef %10
+ %4 = OpFunction %2 None %3
+ %5 = OpLabel
+ OpBranch %21
+ %21 = OpLabel
+ %22 = OpPhi %10 %11 %5
+ OpBranch %14
+ %14 = OpLabel
+ OpReturn
+ OpFunctionEnd
+ %7 = OpFunction %2 None %3
+ %8 = OpLabel
+ OpBranch %13
+ %13 = OpLabel
+ %12 = OpPhi %10 %11 %8
+ OpReturn
+ OpFunctionEnd
+ )";
+
+ ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
+}
+
} // namespace
} // namespace fuzz
} // namespace spvtools