spirv-fuzz: Wrap OpKill and similar in function calls (#3884)
Part of #3717.
This commit is contained in:
Родитель
11d5924227
Коммит
e022659922
|
@ -220,6 +220,7 @@ if(SPIRV_BUILD_FUZZER)
|
||||||
transformation_swap_conditional_branch_operands.h
|
transformation_swap_conditional_branch_operands.h
|
||||||
transformation_toggle_access_chain_instruction.h
|
transformation_toggle_access_chain_instruction.h
|
||||||
transformation_vector_shuffle.h
|
transformation_vector_shuffle.h
|
||||||
|
transformation_wrap_early_terminator_in_function.h
|
||||||
transformation_wrap_region_in_selection.h
|
transformation_wrap_region_in_selection.h
|
||||||
uniform_buffer_element_descriptor.h
|
uniform_buffer_element_descriptor.h
|
||||||
${CMAKE_CURRENT_BINARY_DIR}/protobufs/spvtoolsfuzz.pb.h
|
${CMAKE_CURRENT_BINARY_DIR}/protobufs/spvtoolsfuzz.pb.h
|
||||||
|
@ -404,6 +405,7 @@ if(SPIRV_BUILD_FUZZER)
|
||||||
transformation_swap_conditional_branch_operands.cpp
|
transformation_swap_conditional_branch_operands.cpp
|
||||||
transformation_toggle_access_chain_instruction.cpp
|
transformation_toggle_access_chain_instruction.cpp
|
||||||
transformation_vector_shuffle.cpp
|
transformation_vector_shuffle.cpp
|
||||||
|
transformation_wrap_early_terminator_in_function.cpp
|
||||||
transformation_wrap_region_in_selection.cpp
|
transformation_wrap_region_in_selection.cpp
|
||||||
uniform_buffer_element_descriptor.cpp
|
uniform_buffer_element_descriptor.cpp
|
||||||
${CMAKE_CURRENT_BINARY_DIR}/protobufs/spvtoolsfuzz.pb.cc
|
${CMAKE_CURRENT_BINARY_DIR}/protobufs/spvtoolsfuzz.pb.cc
|
||||||
|
|
|
@ -554,6 +554,7 @@ message Transformation {
|
||||||
TransformationAddEarlyTerminatorWrapper add_early_terminator_wrapper = 80;
|
TransformationAddEarlyTerminatorWrapper add_early_terminator_wrapper = 80;
|
||||||
TransformationPropagateInstructionDown propagate_instruction_down = 81;
|
TransformationPropagateInstructionDown propagate_instruction_down = 81;
|
||||||
TransformationReplaceBranchFromDeadBlockWithExit replace_branch_from_dead_block_with_exit = 82;
|
TransformationReplaceBranchFromDeadBlockWithExit replace_branch_from_dead_block_with_exit = 82;
|
||||||
|
TransformationWrapEarlyTerminatorInFunction wrap_early_terminator_in_function = 83;
|
||||||
// Add additional option using the next available number.
|
// Add additional option using the next available number.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2260,6 +2261,26 @@ message TransformationVectorShuffle {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
message TransformationWrapEarlyTerminatorInFunction {
|
||||||
|
|
||||||
|
// Replaces an early terminator - OpKill, OpReachable or OpTerminateInvocation
|
||||||
|
// - with a call to a wrapper function for the terminator.
|
||||||
|
|
||||||
|
// A fresh id for a new OpFunctionCall instruction.
|
||||||
|
uint32 fresh_id = 1;
|
||||||
|
|
||||||
|
// A descriptor for an OpKill, OpUnreachable or OpTerminateInvocation
|
||||||
|
// instruction.
|
||||||
|
InstructionDescriptor early_terminator_instruction = 2;
|
||||||
|
|
||||||
|
// An id with the same type as the enclosing function's return type that is
|
||||||
|
// available at the early terminator. This is used to change the terminator
|
||||||
|
// to OpReturnValue. Ignored if the enclosing function has void return type,
|
||||||
|
// in which case OpReturn can be used as the new terminator.
|
||||||
|
uint32 returned_value_id = 3;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
message TransformationWrapRegionInSelection {
|
message TransformationWrapRegionInSelection {
|
||||||
|
|
||||||
// Transforms a single-entry-single-exit region R into
|
// Transforms a single-entry-single-exit region R into
|
||||||
|
|
|
@ -98,6 +98,7 @@
|
||||||
#include "source/fuzz/transformation_swap_conditional_branch_operands.h"
|
#include "source/fuzz/transformation_swap_conditional_branch_operands.h"
|
||||||
#include "source/fuzz/transformation_toggle_access_chain_instruction.h"
|
#include "source/fuzz/transformation_toggle_access_chain_instruction.h"
|
||||||
#include "source/fuzz/transformation_vector_shuffle.h"
|
#include "source/fuzz/transformation_vector_shuffle.h"
|
||||||
|
#include "source/fuzz/transformation_wrap_early_terminator_in_function.h"
|
||||||
#include "source/fuzz/transformation_wrap_region_in_selection.h"
|
#include "source/fuzz/transformation_wrap_region_in_selection.h"
|
||||||
#include "source/util/make_unique.h"
|
#include "source/util/make_unique.h"
|
||||||
|
|
||||||
|
@ -358,6 +359,10 @@ std::unique_ptr<Transformation> Transformation::FromMessage(
|
||||||
message.toggle_access_chain_instruction());
|
message.toggle_access_chain_instruction());
|
||||||
case protobufs::Transformation::TransformationCase::kVectorShuffle:
|
case protobufs::Transformation::TransformationCase::kVectorShuffle:
|
||||||
return MakeUnique<TransformationVectorShuffle>(message.vector_shuffle());
|
return MakeUnique<TransformationVectorShuffle>(message.vector_shuffle());
|
||||||
|
case protobufs::Transformation::TransformationCase::
|
||||||
|
kWrapEarlyTerminatorInFunction:
|
||||||
|
return MakeUnique<TransformationWrapEarlyTerminatorInFunction>(
|
||||||
|
message.wrap_early_terminator_in_function());
|
||||||
case protobufs::Transformation::TransformationCase::kWrapRegionInSelection:
|
case protobufs::Transformation::TransformationCase::kWrapRegionInSelection:
|
||||||
return MakeUnique<TransformationWrapRegionInSelection>(
|
return MakeUnique<TransformationWrapRegionInSelection>(
|
||||||
message.wrap_region_in_selection());
|
message.wrap_region_in_selection());
|
||||||
|
|
|
@ -0,0 +1,183 @@
|
||||||
|
// Copyright (c) 2020 Google LLC
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
#include "source/fuzz/transformation_wrap_early_terminator_in_function.h"
|
||||||
|
|
||||||
|
#include "source/fuzz/fuzzer_util.h"
|
||||||
|
#include "source/fuzz/instruction_descriptor.h"
|
||||||
|
#include "source/util/make_unique.h"
|
||||||
|
|
||||||
|
namespace spvtools {
|
||||||
|
namespace fuzz {
|
||||||
|
|
||||||
|
TransformationWrapEarlyTerminatorInFunction::
|
||||||
|
TransformationWrapEarlyTerminatorInFunction(
|
||||||
|
const spvtools::fuzz::protobufs::
|
||||||
|
TransformationWrapEarlyTerminatorInFunction& message)
|
||||||
|
: message_(message) {}
|
||||||
|
|
||||||
|
TransformationWrapEarlyTerminatorInFunction::
|
||||||
|
TransformationWrapEarlyTerminatorInFunction(
|
||||||
|
uint32_t fresh_id,
|
||||||
|
const protobufs::InstructionDescriptor& early_terminator_instruction,
|
||||||
|
uint32_t returned_value_id) {
|
||||||
|
message_.set_fresh_id(fresh_id);
|
||||||
|
*message_.mutable_early_terminator_instruction() =
|
||||||
|
early_terminator_instruction;
|
||||||
|
message_.set_returned_value_id(returned_value_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool TransformationWrapEarlyTerminatorInFunction::IsApplicable(
|
||||||
|
opt::IRContext* ir_context, const TransformationContext& /*unused*/) const {
|
||||||
|
// The given id must be fresh.
|
||||||
|
if (!fuzzerutil::IsFreshId(ir_context, message_.fresh_id())) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// |message_.early_terminator_instruction| must identify an instruciton, and
|
||||||
|
// the instruction must indeed be an early terminator.
|
||||||
|
auto early_terminator =
|
||||||
|
FindInstruction(message_.early_terminator_instruction(), ir_context);
|
||||||
|
if (!early_terminator) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
switch (early_terminator->opcode()) {
|
||||||
|
case SpvOpKill:
|
||||||
|
case SpvOpUnreachable:
|
||||||
|
case SpvOpTerminateInvocation:
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// A wrapper function for the early terminator must exist.
|
||||||
|
auto wrapper_function =
|
||||||
|
MaybeGetWrapperFunction(ir_context, early_terminator->opcode());
|
||||||
|
if (wrapper_function == nullptr) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
auto enclosing_function =
|
||||||
|
ir_context->get_instr_block(early_terminator)->GetParent();
|
||||||
|
// The wrapper function cannot be the function containing the instruction we
|
||||||
|
// would like to wrap.
|
||||||
|
if (wrapper_function->result_id() == enclosing_function->result_id()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!ir_context->get_type_mgr()
|
||||||
|
->GetType(enclosing_function->type_id())
|
||||||
|
->AsVoid()) {
|
||||||
|
// The enclosing function has non-void return type. We thus need to make
|
||||||
|
// sure that |message_.returned_value_instruction| provides a suitable
|
||||||
|
// result id to use in an OpReturnValue instruction.
|
||||||
|
auto returned_value_instruction =
|
||||||
|
ir_context->get_def_use_mgr()->GetDef(message_.returned_value_id());
|
||||||
|
if (!returned_value_instruction || !returned_value_instruction->type_id() ||
|
||||||
|
returned_value_instruction->type_id() !=
|
||||||
|
enclosing_function->type_id()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!fuzzerutil::IdIsAvailableBeforeInstruction(
|
||||||
|
ir_context, early_terminator, message_.returned_value_id())) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void TransformationWrapEarlyTerminatorInFunction::Apply(
|
||||||
|
opt::IRContext* ir_context, TransformationContext* /*unused*/) const {
|
||||||
|
fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_id());
|
||||||
|
auto early_terminator =
|
||||||
|
FindInstruction(message_.early_terminator_instruction(), ir_context);
|
||||||
|
auto enclosing_block = ir_context->get_instr_block(early_terminator);
|
||||||
|
auto enclosing_function = enclosing_block->GetParent();
|
||||||
|
|
||||||
|
// We would like to add an OpFunctionCall before the block's terminator
|
||||||
|
// instruction, and then change the block's terminator to OpReturn or
|
||||||
|
// OpReturnValue.
|
||||||
|
|
||||||
|
// We get an iterator to the instruction we would like to insert the function
|
||||||
|
// call before. It will be an iterator to the final instruction in the block
|
||||||
|
// unless the block is a merge block in which case it will be to the
|
||||||
|
// penultimate instruction (because we cannot insert an OpFunctionCall after
|
||||||
|
// a merge instruction).
|
||||||
|
auto iterator = enclosing_block->tail();
|
||||||
|
if (enclosing_block->MergeBlockIdIfAny()) {
|
||||||
|
--iterator;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto wrapper_function =
|
||||||
|
MaybeGetWrapperFunction(ir_context, early_terminator->opcode());
|
||||||
|
|
||||||
|
iterator->InsertBefore(MakeUnique<opt::Instruction>(
|
||||||
|
ir_context, SpvOpFunctionCall, wrapper_function->type_id(),
|
||||||
|
message_.fresh_id(),
|
||||||
|
opt::Instruction::OperandList(
|
||||||
|
{{SPV_OPERAND_TYPE_ID, {wrapper_function->result_id()}}})));
|
||||||
|
|
||||||
|
opt::Instruction::OperandList new_in_operands;
|
||||||
|
if (!ir_context->get_type_mgr()
|
||||||
|
->GetType(enclosing_function->type_id())
|
||||||
|
->AsVoid()) {
|
||||||
|
new_in_operands.push_back(
|
||||||
|
{SPV_OPERAND_TYPE_ID, {message_.returned_value_id()}});
|
||||||
|
early_terminator->SetOpcode(SpvOpReturnValue);
|
||||||
|
} else {
|
||||||
|
early_terminator->SetOpcode(SpvOpReturn);
|
||||||
|
}
|
||||||
|
early_terminator->SetInOperands(std::move(new_in_operands));
|
||||||
|
|
||||||
|
ir_context->InvalidateAnalysesExceptFor(opt::IRContext::kAnalysisNone);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::unordered_set<uint32_t>
|
||||||
|
TransformationWrapEarlyTerminatorInFunction::GetFreshIds() const {
|
||||||
|
return std::unordered_set<uint32_t>({message_.fresh_id()});
|
||||||
|
}
|
||||||
|
|
||||||
|
protobufs::Transformation
|
||||||
|
TransformationWrapEarlyTerminatorInFunction::ToMessage() const {
|
||||||
|
protobufs::Transformation result;
|
||||||
|
*result.mutable_wrap_early_terminator_in_function() = message_;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
opt::Function*
|
||||||
|
TransformationWrapEarlyTerminatorInFunction::MaybeGetWrapperFunction(
|
||||||
|
opt::IRContext* ir_context, SpvOp early_terminator_opcode) {
|
||||||
|
assert((early_terminator_opcode == SpvOpKill ||
|
||||||
|
early_terminator_opcode == SpvOpUnreachable ||
|
||||||
|
early_terminator_opcode == SpvOpTerminateInvocation) &&
|
||||||
|
"Invalid opcode.");
|
||||||
|
auto void_type_id = fuzzerutil::MaybeGetVoidType(ir_context);
|
||||||
|
if (!void_type_id) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
auto void_function_type_id =
|
||||||
|
fuzzerutil::FindFunctionType(ir_context, {void_type_id});
|
||||||
|
if (!void_function_type_id) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
for (auto& function : *ir_context->module()) {
|
||||||
|
if (function.DefInst().GetSingleWordInOperand(1) != void_function_type_id) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (function.begin()->begin()->opcode() == early_terminator_opcode) {
|
||||||
|
return &function;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace fuzz
|
||||||
|
} // namespace spvtools
|
|
@ -0,0 +1,59 @@
|
||||||
|
// Copyright (c) 2020 Google LLC
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
#ifndef SOURCE_FUZZ_TRANSFORMATION_WRAP_EARLY_TERMINATOR_IN_FUNCTION_H_
|
||||||
|
#define SOURCE_FUZZ_TRANSFORMATION_WRAP_EARLY_TERMINATOR_IN_FUNCTION_H_
|
||||||
|
|
||||||
|
#include "source/fuzz/protobufs/spirvfuzz_protobufs.h"
|
||||||
|
#include "source/fuzz/transformation.h"
|
||||||
|
#include "source/fuzz/transformation_context.h"
|
||||||
|
#include "source/opt/ir_context.h"
|
||||||
|
|
||||||
|
namespace spvtools {
|
||||||
|
namespace fuzz {
|
||||||
|
|
||||||
|
class TransformationWrapEarlyTerminatorInFunction : public Transformation {
|
||||||
|
public:
|
||||||
|
explicit TransformationWrapEarlyTerminatorInFunction(
|
||||||
|
const protobufs::TransformationWrapEarlyTerminatorInFunction& message);
|
||||||
|
|
||||||
|
TransformationWrapEarlyTerminatorInFunction(
|
||||||
|
uint32_t fresh_id,
|
||||||
|
const protobufs::InstructionDescriptor& early_terminator_instruction,
|
||||||
|
uint32_t returned_value_id);
|
||||||
|
|
||||||
|
// TODO comment
|
||||||
|
bool IsApplicable(
|
||||||
|
opt::IRContext* ir_context,
|
||||||
|
const TransformationContext& transformation_context) const override;
|
||||||
|
|
||||||
|
// TODO comment
|
||||||
|
void Apply(opt::IRContext* ir_context,
|
||||||
|
TransformationContext* transformation_context) const override;
|
||||||
|
|
||||||
|
std::unordered_set<uint32_t> GetFreshIds() const override;
|
||||||
|
|
||||||
|
protobufs::Transformation ToMessage() const override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
static opt::Function* MaybeGetWrapperFunction(opt::IRContext* ir_context,
|
||||||
|
SpvOp early_terminator_opcode);
|
||||||
|
|
||||||
|
protobufs::TransformationWrapEarlyTerminatorInFunction message_;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace fuzz
|
||||||
|
} // namespace spvtools
|
||||||
|
|
||||||
|
#endif // SOURCE_FUZZ_TRANSFORMATION_WRAP_EARLY_TERMINATOR_IN_FUNCTION_H_
|
|
@ -115,6 +115,7 @@ if (${SPIRV_BUILD_FUZZER})
|
||||||
transformation_toggle_access_chain_instruction_test.cpp
|
transformation_toggle_access_chain_instruction_test.cpp
|
||||||
transformation_record_synonymous_constants_test.cpp
|
transformation_record_synonymous_constants_test.cpp
|
||||||
transformation_vector_shuffle_test.cpp
|
transformation_vector_shuffle_test.cpp
|
||||||
|
transformation_wrap_early_terminator_in_function_test.cpp
|
||||||
transformation_wrap_region_in_selection_test.cpp
|
transformation_wrap_region_in_selection_test.cpp
|
||||||
uniform_buffer_element_descriptor_test.cpp)
|
uniform_buffer_element_descriptor_test.cpp)
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,315 @@
|
||||||
|
// Copyright (c) 2020 Google LLC
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
#include "source/fuzz/transformation_wrap_early_terminator_in_function.h"
|
||||||
|
|
||||||
|
#include "source/fuzz/instruction_descriptor.h"
|
||||||
|
#include "test/fuzz/fuzz_test_util.h"
|
||||||
|
|
||||||
|
namespace spvtools {
|
||||||
|
namespace fuzz {
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
TEST(TransformationWrapEarlyTerminatorInFunctionTest, IsApplicable) {
|
||||||
|
std::string shader = R"(
|
||||||
|
OpCapability Shader
|
||||||
|
OpExtension "SPV_KHR_terminate_invocation"
|
||||||
|
%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 = OpTypeInt 32 1
|
||||||
|
%7 = OpConstant %6 0
|
||||||
|
%90 = OpTypeBool
|
||||||
|
%91 = OpConstantFalse %90
|
||||||
|
|
||||||
|
%20 = OpTypeFunction %2 %6
|
||||||
|
%21 = OpTypeFunction %6
|
||||||
|
|
||||||
|
%4 = OpFunction %2 None %3
|
||||||
|
%5 = OpLabel
|
||||||
|
OpSelectionMerge %11 None
|
||||||
|
OpSwitch %7 %11 0 %8 1 %9 2 %10
|
||||||
|
%8 = OpLabel
|
||||||
|
OpKill
|
||||||
|
%9 = OpLabel
|
||||||
|
OpUnreachable
|
||||||
|
%10 = OpLabel
|
||||||
|
OpTerminateInvocation
|
||||||
|
%11 = OpLabel
|
||||||
|
OpReturn
|
||||||
|
OpFunctionEnd
|
||||||
|
|
||||||
|
%30 = OpFunction %2 None %3
|
||||||
|
%31 = OpLabel
|
||||||
|
OpKill
|
||||||
|
OpFunctionEnd
|
||||||
|
|
||||||
|
%50 = OpFunction %2 None %3
|
||||||
|
%51 = OpLabel
|
||||||
|
OpTerminateInvocation
|
||||||
|
OpFunctionEnd
|
||||||
|
|
||||||
|
%60 = OpFunction %6 None %21
|
||||||
|
%61 = OpLabel
|
||||||
|
OpBranch %62
|
||||||
|
%62 = OpLabel
|
||||||
|
OpKill
|
||||||
|
OpFunctionEnd
|
||||||
|
|
||||||
|
%70 = OpFunction %6 None %21
|
||||||
|
%71 = OpLabel
|
||||||
|
OpUnreachable
|
||||||
|
OpFunctionEnd
|
||||||
|
|
||||||
|
%80 = OpFunction %2 None %20
|
||||||
|
%81 = OpFunctionParameter %6
|
||||||
|
%82 = OpLabel
|
||||||
|
OpTerminateInvocation
|
||||||
|
OpFunctionEnd
|
||||||
|
|
||||||
|
)";
|
||||||
|
|
||||||
|
const auto env = SPV_ENV_UNIVERSAL_1_4;
|
||||||
|
const auto consumer = nullptr;
|
||||||
|
const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
|
||||||
|
ASSERT_TRUE(IsValid(env, context.get()));
|
||||||
|
|
||||||
|
spvtools::ValidatorOptions validator_options;
|
||||||
|
TransformationContext transformation_context(
|
||||||
|
MakeUnique<FactManager>(context.get()), validator_options);
|
||||||
|
|
||||||
|
// Bad: id is not fresh
|
||||||
|
ASSERT_FALSE(TransformationWrapEarlyTerminatorInFunction(
|
||||||
|
61, MakeInstructionDescriptor(8, SpvOpKill, 0), 0)
|
||||||
|
.IsApplicable(context.get(), transformation_context));
|
||||||
|
|
||||||
|
// Bad: early terminator instruction descriptor does not exist
|
||||||
|
ASSERT_FALSE(TransformationWrapEarlyTerminatorInFunction(
|
||||||
|
100, MakeInstructionDescriptor(82, SpvOpKill, 0), 0)
|
||||||
|
.IsApplicable(context.get(), transformation_context));
|
||||||
|
|
||||||
|
// Bad: early terminator instruction does not identify an early terminator
|
||||||
|
ASSERT_FALSE(TransformationWrapEarlyTerminatorInFunction(
|
||||||
|
100, MakeInstructionDescriptor(5, SpvOpSelectionMerge, 0), 0)
|
||||||
|
.IsApplicable(context.get(), transformation_context));
|
||||||
|
|
||||||
|
// Bad: no wrapper function is available
|
||||||
|
ASSERT_FALSE(TransformationWrapEarlyTerminatorInFunction(
|
||||||
|
100, MakeInstructionDescriptor(9, SpvOpUnreachable, 0), 0)
|
||||||
|
.IsApplicable(context.get(), transformation_context));
|
||||||
|
|
||||||
|
// Bad: returned value does not exist
|
||||||
|
ASSERT_FALSE(TransformationWrapEarlyTerminatorInFunction(
|
||||||
|
100, MakeInstructionDescriptor(62, SpvOpKill, 0), 1000)
|
||||||
|
.IsApplicable(context.get(), transformation_context));
|
||||||
|
|
||||||
|
// Bad: returned value does not have a type
|
||||||
|
ASSERT_FALSE(TransformationWrapEarlyTerminatorInFunction(
|
||||||
|
100, MakeInstructionDescriptor(62, SpvOpKill, 0), 61)
|
||||||
|
.IsApplicable(context.get(), transformation_context));
|
||||||
|
|
||||||
|
// Bad: returned value type does not match
|
||||||
|
ASSERT_FALSE(TransformationWrapEarlyTerminatorInFunction(
|
||||||
|
100, MakeInstructionDescriptor(62, SpvOpKill, 0), 91)
|
||||||
|
.IsApplicable(context.get(), transformation_context));
|
||||||
|
|
||||||
|
// Bad: returned value is not available
|
||||||
|
ASSERT_FALSE(TransformationWrapEarlyTerminatorInFunction(
|
||||||
|
100, MakeInstructionDescriptor(62, SpvOpKill, 0), 81)
|
||||||
|
.IsApplicable(context.get(), transformation_context));
|
||||||
|
|
||||||
|
// Bad: the OpKill being targeted is in the only available wrapper; we cannot
|
||||||
|
// have the wrapper call itself.
|
||||||
|
ASSERT_FALSE(TransformationWrapEarlyTerminatorInFunction(
|
||||||
|
100, MakeInstructionDescriptor(31, SpvOpKill, 0), 0)
|
||||||
|
.IsApplicable(context.get(), transformation_context));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(TransformationWrapEarlyTerminatorInFunctionTest, Apply) {
|
||||||
|
std::string shader = R"(
|
||||||
|
OpCapability Shader
|
||||||
|
OpExtension "SPV_KHR_terminate_invocation"
|
||||||
|
%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 = OpTypeInt 32 1
|
||||||
|
%7 = OpConstant %6 0
|
||||||
|
|
||||||
|
%20 = OpTypeFunction %2 %6
|
||||||
|
%21 = OpTypeFunction %6
|
||||||
|
|
||||||
|
%4 = OpFunction %2 None %3
|
||||||
|
%5 = OpLabel
|
||||||
|
OpSelectionMerge %11 None
|
||||||
|
OpSwitch %7 %11 0 %8 1 %9 2 %10
|
||||||
|
%8 = OpLabel
|
||||||
|
OpKill
|
||||||
|
%9 = OpLabel
|
||||||
|
OpUnreachable
|
||||||
|
%10 = OpLabel
|
||||||
|
OpTerminateInvocation
|
||||||
|
%11 = OpLabel
|
||||||
|
OpReturn
|
||||||
|
OpFunctionEnd
|
||||||
|
|
||||||
|
%30 = OpFunction %2 None %3
|
||||||
|
%31 = OpLabel
|
||||||
|
OpKill
|
||||||
|
OpFunctionEnd
|
||||||
|
|
||||||
|
%40 = OpFunction %2 None %3
|
||||||
|
%41 = OpLabel
|
||||||
|
OpUnreachable
|
||||||
|
OpFunctionEnd
|
||||||
|
|
||||||
|
%50 = OpFunction %2 None %3
|
||||||
|
%51 = OpLabel
|
||||||
|
OpTerminateInvocation
|
||||||
|
OpFunctionEnd
|
||||||
|
|
||||||
|
%60 = OpFunction %2 None %3
|
||||||
|
%61 = OpLabel
|
||||||
|
OpBranch %62
|
||||||
|
%62 = OpLabel
|
||||||
|
OpKill
|
||||||
|
OpFunctionEnd
|
||||||
|
|
||||||
|
%70 = OpFunction %6 None %21
|
||||||
|
%71 = OpLabel
|
||||||
|
OpUnreachable
|
||||||
|
OpFunctionEnd
|
||||||
|
|
||||||
|
%80 = OpFunction %2 None %20
|
||||||
|
%81 = OpFunctionParameter %6
|
||||||
|
%82 = OpLabel
|
||||||
|
OpTerminateInvocation
|
||||||
|
OpFunctionEnd
|
||||||
|
)";
|
||||||
|
|
||||||
|
const auto env = SPV_ENV_UNIVERSAL_1_4;
|
||||||
|
const auto consumer = nullptr;
|
||||||
|
const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
|
||||||
|
ASSERT_TRUE(IsValid(env, context.get()));
|
||||||
|
|
||||||
|
spvtools::ValidatorOptions validator_options;
|
||||||
|
TransformationContext transformation_context(
|
||||||
|
MakeUnique<FactManager>(context.get()), validator_options);
|
||||||
|
|
||||||
|
for (auto& transformation :
|
||||||
|
{TransformationWrapEarlyTerminatorInFunction(
|
||||||
|
100, MakeInstructionDescriptor(8, SpvOpKill, 0), 0),
|
||||||
|
TransformationWrapEarlyTerminatorInFunction(
|
||||||
|
101, MakeInstructionDescriptor(9, SpvOpUnreachable, 0), 0),
|
||||||
|
TransformationWrapEarlyTerminatorInFunction(
|
||||||
|
102, MakeInstructionDescriptor(10, SpvOpTerminateInvocation, 0), 0),
|
||||||
|
TransformationWrapEarlyTerminatorInFunction(
|
||||||
|
103, MakeInstructionDescriptor(62, SpvOpKill, 0), 0),
|
||||||
|
TransformationWrapEarlyTerminatorInFunction(
|
||||||
|
104, MakeInstructionDescriptor(71, SpvOpUnreachable, 0), 7),
|
||||||
|
TransformationWrapEarlyTerminatorInFunction(
|
||||||
|
105, MakeInstructionDescriptor(82, SpvOpTerminateInvocation, 0),
|
||||||
|
0)}) {
|
||||||
|
ASSERT_TRUE(
|
||||||
|
transformation.IsApplicable(context.get(), transformation_context));
|
||||||
|
ApplyAndCheckFreshIds(transformation, context.get(),
|
||||||
|
&transformation_context);
|
||||||
|
}
|
||||||
|
ASSERT_TRUE(IsValid(env, context.get()));
|
||||||
|
|
||||||
|
std::string after_transformation = R"(
|
||||||
|
OpCapability Shader
|
||||||
|
OpExtension "SPV_KHR_terminate_invocation"
|
||||||
|
%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 = OpTypeInt 32 1
|
||||||
|
%7 = OpConstant %6 0
|
||||||
|
|
||||||
|
%20 = OpTypeFunction %2 %6
|
||||||
|
%21 = OpTypeFunction %6
|
||||||
|
|
||||||
|
%4 = OpFunction %2 None %3
|
||||||
|
%5 = OpLabel
|
||||||
|
OpSelectionMerge %11 None
|
||||||
|
OpSwitch %7 %11 0 %8 1 %9 2 %10
|
||||||
|
%8 = OpLabel
|
||||||
|
%100 = OpFunctionCall %2 %30
|
||||||
|
OpReturn
|
||||||
|
%9 = OpLabel
|
||||||
|
%101 = OpFunctionCall %2 %40
|
||||||
|
OpReturn
|
||||||
|
%10 = OpLabel
|
||||||
|
%102 = OpFunctionCall %2 %50
|
||||||
|
OpReturn
|
||||||
|
%11 = OpLabel
|
||||||
|
OpReturn
|
||||||
|
OpFunctionEnd
|
||||||
|
|
||||||
|
%30 = OpFunction %2 None %3
|
||||||
|
%31 = OpLabel
|
||||||
|
OpKill
|
||||||
|
OpFunctionEnd
|
||||||
|
|
||||||
|
%40 = OpFunction %2 None %3
|
||||||
|
%41 = OpLabel
|
||||||
|
OpUnreachable
|
||||||
|
OpFunctionEnd
|
||||||
|
|
||||||
|
%50 = OpFunction %2 None %3
|
||||||
|
%51 = OpLabel
|
||||||
|
OpTerminateInvocation
|
||||||
|
OpFunctionEnd
|
||||||
|
|
||||||
|
%60 = OpFunction %2 None %3
|
||||||
|
%61 = OpLabel
|
||||||
|
OpBranch %62
|
||||||
|
%62 = OpLabel
|
||||||
|
%103 = OpFunctionCall %2 %30
|
||||||
|
OpReturn
|
||||||
|
OpFunctionEnd
|
||||||
|
|
||||||
|
%70 = OpFunction %6 None %21
|
||||||
|
%71 = OpLabel
|
||||||
|
%104 = OpFunctionCall %2 %40
|
||||||
|
OpReturnValue %7
|
||||||
|
OpFunctionEnd
|
||||||
|
|
||||||
|
%80 = OpFunction %2 None %20
|
||||||
|
%81 = OpFunctionParameter %6
|
||||||
|
%82 = OpLabel
|
||||||
|
%105 = OpFunctionCall %2 %50
|
||||||
|
OpReturn
|
||||||
|
OpFunctionEnd
|
||||||
|
)";
|
||||||
|
ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
} // namespace fuzz
|
||||||
|
} // namespace spvtools
|
Загрузка…
Ссылка в новой задаче