mirror of
https://github.com/KhronosGroup/SPIRV-Tools
synced 2024-10-19 03:20:14 +00:00
521223b70a
This change allows the generator to (optionally and at random) make the functions of a module "livesafe" during donation. This involves introducing a loop limiter variable to each function and gating the number of total loop iterations for the function using that variable. It also involves eliminating OpKill and OpUnreachable instructions (changing them to OpReturn/OpReturnValue), and clamping access chain indices so that they are always in-bounds.
2748 lines
101 KiB
C++
2748 lines
101 KiB
C++
// Copyright (c) 2019 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_add_function.h"
|
|
#include "source/fuzz/instruction_message.h"
|
|
#include "test/fuzz/fuzz_test_util.h"
|
|
|
|
namespace spvtools {
|
|
namespace fuzz {
|
|
namespace {
|
|
|
|
protobufs::AccessChainClampingInfo MakeAccessClampingInfo(
|
|
uint32_t access_chain_id,
|
|
const std::vector<std::pair<uint32_t, uint32_t>>& compare_and_select_ids) {
|
|
protobufs::AccessChainClampingInfo result;
|
|
result.set_access_chain_id(access_chain_id);
|
|
for (auto& compare_and_select_id : compare_and_select_ids) {
|
|
auto pair = result.add_compare_and_select_ids();
|
|
pair->set_first(compare_and_select_id.first);
|
|
pair->set_second(compare_and_select_id.second);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
std::vector<protobufs::Instruction> GetInstructionsForFunction(
|
|
spv_target_env env, const MessageConsumer& consumer,
|
|
const std::string& donor, uint32_t function_id) {
|
|
std::vector<protobufs::Instruction> result;
|
|
const auto donor_context =
|
|
BuildModule(env, consumer, donor, kFuzzAssembleOption);
|
|
assert(IsValid(env, donor_context.get()) && "The given donor must be valid.");
|
|
for (auto& function : *donor_context->module()) {
|
|
if (function.result_id() == function_id) {
|
|
function.ForEachInst([&result](opt::Instruction* inst) {
|
|
opt::Instruction::OperandList input_operands;
|
|
for (uint32_t i = 0; i < inst->NumInOperands(); i++) {
|
|
input_operands.push_back(inst->GetInOperand(i));
|
|
}
|
|
result.push_back(MakeInstructionMessage(inst->opcode(), inst->type_id(),
|
|
inst->result_id(),
|
|
input_operands));
|
|
});
|
|
break;
|
|
}
|
|
}
|
|
assert(!result.empty() && "The required function should have been found.");
|
|
return result;
|
|
}
|
|
|
|
TEST(TransformationAddFunctionTest, BasicTest) {
|
|
std::string shader = R"(
|
|
OpCapability Shader
|
|
%1 = OpExtInstImport "GLSL.std.450"
|
|
OpMemoryModel Logical GLSL450
|
|
OpEntryPoint Fragment %4 "main"
|
|
OpExecutionMode %4 OriginUpperLeft
|
|
OpSource ESSL 310
|
|
%2 = OpTypeVoid
|
|
%3 = OpTypeFunction %2
|
|
%6 = OpTypeInt 32 1
|
|
%7 = OpTypePointer Function %6
|
|
%8 = OpTypeFloat 32
|
|
%9 = OpTypePointer Function %8
|
|
%10 = OpTypeFunction %8 %7 %9
|
|
%18 = OpConstant %8 0
|
|
%20 = OpConstant %6 0
|
|
%28 = OpTypeBool
|
|
%37 = OpConstant %6 1
|
|
%42 = OpTypePointer Private %8
|
|
%43 = OpVariable %42 Private
|
|
%47 = OpConstant %8 1
|
|
%4 = OpFunction %2 None %3
|
|
%5 = OpLabel
|
|
OpReturn
|
|
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()));
|
|
|
|
FactManager fact_manager;
|
|
|
|
TransformationAddFunction transformation1(std::vector<protobufs::Instruction>(
|
|
{MakeInstructionMessage(
|
|
SpvOpFunction, 8, 13,
|
|
{{SPV_OPERAND_TYPE_FUNCTION_CONTROL, {SpvFunctionControlMaskNone}},
|
|
{SPV_OPERAND_TYPE_ID, {10}}}),
|
|
MakeInstructionMessage(SpvOpFunctionParameter, 7, 11, {}),
|
|
MakeInstructionMessage(SpvOpFunctionParameter, 9, 12, {}),
|
|
MakeInstructionMessage(SpvOpLabel, 0, 14, {}),
|
|
MakeInstructionMessage(
|
|
SpvOpVariable, 9, 17,
|
|
{{SPV_OPERAND_TYPE_STORAGE_CLASS, {SpvStorageClassFunction}}}),
|
|
MakeInstructionMessage(
|
|
SpvOpVariable, 7, 19,
|
|
{{SPV_OPERAND_TYPE_STORAGE_CLASS, {SpvStorageClassFunction}}}),
|
|
MakeInstructionMessage(
|
|
SpvOpStore, 0, 0,
|
|
{{SPV_OPERAND_TYPE_ID, {17}}, {SPV_OPERAND_TYPE_ID, {18}}}),
|
|
MakeInstructionMessage(
|
|
SpvOpStore, 0, 0,
|
|
{{SPV_OPERAND_TYPE_ID, {19}}, {SPV_OPERAND_TYPE_ID, {20}}}),
|
|
MakeInstructionMessage(SpvOpBranch, 0, 0, {{SPV_OPERAND_TYPE_ID, {21}}}),
|
|
MakeInstructionMessage(SpvOpLabel, 0, 21, {}),
|
|
MakeInstructionMessage(
|
|
SpvOpLoopMerge, 0, 0,
|
|
{{SPV_OPERAND_TYPE_ID, {23}},
|
|
{SPV_OPERAND_TYPE_ID, {24}},
|
|
{SPV_OPERAND_TYPE_LOOP_CONTROL, {SpvLoopControlMaskNone}}}),
|
|
MakeInstructionMessage(SpvOpBranch, 0, 0, {{SPV_OPERAND_TYPE_ID, {25}}}),
|
|
MakeInstructionMessage(SpvOpLabel, 0, 25, {}),
|
|
MakeInstructionMessage(SpvOpLoad, 6, 26, {{SPV_OPERAND_TYPE_ID, {19}}}),
|
|
MakeInstructionMessage(SpvOpLoad, 6, 27, {{SPV_OPERAND_TYPE_ID, {11}}}),
|
|
MakeInstructionMessage(
|
|
SpvOpSLessThan, 28, 29,
|
|
{{SPV_OPERAND_TYPE_ID, {26}}, {SPV_OPERAND_TYPE_ID, {27}}}),
|
|
MakeInstructionMessage(SpvOpBranchConditional, 0, 0,
|
|
{{SPV_OPERAND_TYPE_ID, {29}},
|
|
{SPV_OPERAND_TYPE_ID, {22}},
|
|
{SPV_OPERAND_TYPE_ID, {23}}}),
|
|
MakeInstructionMessage(SpvOpLabel, 0, 22, {}),
|
|
MakeInstructionMessage(SpvOpLoad, 8, 30, {{SPV_OPERAND_TYPE_ID, {12}}}),
|
|
MakeInstructionMessage(SpvOpLoad, 6, 31, {{SPV_OPERAND_TYPE_ID, {19}}}),
|
|
MakeInstructionMessage(SpvOpConvertSToF, 8, 32,
|
|
{{SPV_OPERAND_TYPE_ID, {31}}}),
|
|
MakeInstructionMessage(
|
|
SpvOpFMul, 8, 33,
|
|
{{SPV_OPERAND_TYPE_ID, {30}}, {SPV_OPERAND_TYPE_ID, {32}}}),
|
|
MakeInstructionMessage(SpvOpLoad, 8, 34, {{SPV_OPERAND_TYPE_ID, {17}}}),
|
|
MakeInstructionMessage(
|
|
SpvOpFAdd, 8, 35,
|
|
{{SPV_OPERAND_TYPE_ID, {34}}, {SPV_OPERAND_TYPE_ID, {33}}}),
|
|
MakeInstructionMessage(
|
|
SpvOpStore, 0, 0,
|
|
{{SPV_OPERAND_TYPE_ID, {17}}, {SPV_OPERAND_TYPE_ID, {35}}}),
|
|
MakeInstructionMessage(SpvOpBranch, 0, 0, {{SPV_OPERAND_TYPE_ID, {24}}}),
|
|
MakeInstructionMessage(SpvOpLabel, 0, 24, {}),
|
|
MakeInstructionMessage(SpvOpLoad, 6, 36, {{SPV_OPERAND_TYPE_ID, {19}}}),
|
|
MakeInstructionMessage(
|
|
SpvOpIAdd, 6, 38,
|
|
{{SPV_OPERAND_TYPE_ID, {36}}, {SPV_OPERAND_TYPE_ID, {37}}}),
|
|
MakeInstructionMessage(
|
|
SpvOpStore, 0, 0,
|
|
{{SPV_OPERAND_TYPE_ID, {19}}, {SPV_OPERAND_TYPE_ID, {38}}}),
|
|
MakeInstructionMessage(SpvOpBranch, 0, 0, {{SPV_OPERAND_TYPE_ID, {21}}}),
|
|
MakeInstructionMessage(SpvOpLabel, 0, 23, {}),
|
|
MakeInstructionMessage(SpvOpLoad, 8, 39, {{SPV_OPERAND_TYPE_ID, {17}}}),
|
|
MakeInstructionMessage(SpvOpReturnValue, 0, 0,
|
|
{{SPV_OPERAND_TYPE_ID, {39}}}),
|
|
MakeInstructionMessage(SpvOpFunctionEnd, 0, 0, {})}));
|
|
|
|
ASSERT_TRUE(transformation1.IsApplicable(context.get(), fact_manager));
|
|
transformation1.Apply(context.get(), &fact_manager);
|
|
ASSERT_TRUE(IsValid(env, context.get()));
|
|
|
|
std::string after_transformation1 = R"(
|
|
OpCapability Shader
|
|
%1 = OpExtInstImport "GLSL.std.450"
|
|
OpMemoryModel Logical GLSL450
|
|
OpEntryPoint Fragment %4 "main"
|
|
OpExecutionMode %4 OriginUpperLeft
|
|
OpSource ESSL 310
|
|
%2 = OpTypeVoid
|
|
%3 = OpTypeFunction %2
|
|
%6 = OpTypeInt 32 1
|
|
%7 = OpTypePointer Function %6
|
|
%8 = OpTypeFloat 32
|
|
%9 = OpTypePointer Function %8
|
|
%10 = OpTypeFunction %8 %7 %9
|
|
%18 = OpConstant %8 0
|
|
%20 = OpConstant %6 0
|
|
%28 = OpTypeBool
|
|
%37 = OpConstant %6 1
|
|
%42 = OpTypePointer Private %8
|
|
%43 = OpVariable %42 Private
|
|
%47 = OpConstant %8 1
|
|
%4 = OpFunction %2 None %3
|
|
%5 = OpLabel
|
|
OpReturn
|
|
OpFunctionEnd
|
|
%13 = OpFunction %8 None %10
|
|
%11 = OpFunctionParameter %7
|
|
%12 = OpFunctionParameter %9
|
|
%14 = OpLabel
|
|
%17 = OpVariable %9 Function
|
|
%19 = OpVariable %7 Function
|
|
OpStore %17 %18
|
|
OpStore %19 %20
|
|
OpBranch %21
|
|
%21 = OpLabel
|
|
OpLoopMerge %23 %24 None
|
|
OpBranch %25
|
|
%25 = OpLabel
|
|
%26 = OpLoad %6 %19
|
|
%27 = OpLoad %6 %11
|
|
%29 = OpSLessThan %28 %26 %27
|
|
OpBranchConditional %29 %22 %23
|
|
%22 = OpLabel
|
|
%30 = OpLoad %8 %12
|
|
%31 = OpLoad %6 %19
|
|
%32 = OpConvertSToF %8 %31
|
|
%33 = OpFMul %8 %30 %32
|
|
%34 = OpLoad %8 %17
|
|
%35 = OpFAdd %8 %34 %33
|
|
OpStore %17 %35
|
|
OpBranch %24
|
|
%24 = OpLabel
|
|
%36 = OpLoad %6 %19
|
|
%38 = OpIAdd %6 %36 %37
|
|
OpStore %19 %38
|
|
OpBranch %21
|
|
%23 = OpLabel
|
|
%39 = OpLoad %8 %17
|
|
OpReturnValue %39
|
|
OpFunctionEnd
|
|
)";
|
|
ASSERT_TRUE(IsEqual(env, after_transformation1, context.get()));
|
|
ASSERT_TRUE(fact_manager.BlockIsDead(14));
|
|
ASSERT_TRUE(fact_manager.BlockIsDead(21));
|
|
ASSERT_TRUE(fact_manager.BlockIsDead(22));
|
|
ASSERT_TRUE(fact_manager.BlockIsDead(23));
|
|
ASSERT_TRUE(fact_manager.BlockIsDead(24));
|
|
ASSERT_TRUE(fact_manager.BlockIsDead(25));
|
|
|
|
TransformationAddFunction transformation2(std::vector<protobufs::Instruction>(
|
|
{MakeInstructionMessage(
|
|
SpvOpFunction, 2, 15,
|
|
{{SPV_OPERAND_TYPE_FUNCTION_CONTROL, {SpvFunctionControlMaskNone}},
|
|
{SPV_OPERAND_TYPE_ID, {3}}}),
|
|
MakeInstructionMessage(SpvOpLabel, 0, 16, {}),
|
|
MakeInstructionMessage(
|
|
SpvOpVariable, 7, 44,
|
|
{{SPV_OPERAND_TYPE_STORAGE_CLASS, {SpvStorageClassFunction}}}),
|
|
MakeInstructionMessage(
|
|
SpvOpVariable, 9, 45,
|
|
{{SPV_OPERAND_TYPE_STORAGE_CLASS, {SpvStorageClassFunction}}}),
|
|
MakeInstructionMessage(
|
|
SpvOpVariable, 7, 48,
|
|
{{SPV_OPERAND_TYPE_STORAGE_CLASS, {SpvStorageClassFunction}}}),
|
|
MakeInstructionMessage(
|
|
SpvOpVariable, 9, 49,
|
|
{{SPV_OPERAND_TYPE_STORAGE_CLASS, {SpvStorageClassFunction}}}),
|
|
MakeInstructionMessage(
|
|
SpvOpStore, 0, 0,
|
|
{{SPV_OPERAND_TYPE_ID, {44}}, {SPV_OPERAND_TYPE_ID, {20}}}),
|
|
MakeInstructionMessage(
|
|
SpvOpStore, 0, 0,
|
|
{{SPV_OPERAND_TYPE_ID, {45}}, {SPV_OPERAND_TYPE_ID, {18}}}),
|
|
MakeInstructionMessage(SpvOpFunctionCall, 8, 46,
|
|
{{SPV_OPERAND_TYPE_ID, {13}},
|
|
{SPV_OPERAND_TYPE_ID, {44}},
|
|
{SPV_OPERAND_TYPE_ID, {45}}}),
|
|
MakeInstructionMessage(
|
|
SpvOpStore, 0, 0,
|
|
{{SPV_OPERAND_TYPE_ID, {48}}, {SPV_OPERAND_TYPE_ID, {37}}}),
|
|
MakeInstructionMessage(
|
|
SpvOpStore, 0, 0,
|
|
{{SPV_OPERAND_TYPE_ID, {49}}, {SPV_OPERAND_TYPE_ID, {47}}}),
|
|
MakeInstructionMessage(SpvOpFunctionCall, 8, 50,
|
|
{{SPV_OPERAND_TYPE_ID, {13}},
|
|
{SPV_OPERAND_TYPE_ID, {48}},
|
|
{SPV_OPERAND_TYPE_ID, {49}}}),
|
|
MakeInstructionMessage(
|
|
SpvOpFAdd, 8, 51,
|
|
{{SPV_OPERAND_TYPE_ID, {46}}, {SPV_OPERAND_TYPE_ID, {50}}}),
|
|
MakeInstructionMessage(
|
|
SpvOpStore, 0, 0,
|
|
{{SPV_OPERAND_TYPE_ID, {43}}, {SPV_OPERAND_TYPE_ID, {51}}}),
|
|
MakeInstructionMessage(SpvOpReturn, 0, 0, {}),
|
|
MakeInstructionMessage(SpvOpFunctionEnd, 0, 0, {})}));
|
|
|
|
ASSERT_TRUE(transformation2.IsApplicable(context.get(), fact_manager));
|
|
transformation2.Apply(context.get(), &fact_manager);
|
|
ASSERT_TRUE(IsValid(env, context.get()));
|
|
|
|
std::string after_transformation2 = R"(
|
|
OpCapability Shader
|
|
%1 = OpExtInstImport "GLSL.std.450"
|
|
OpMemoryModel Logical GLSL450
|
|
OpEntryPoint Fragment %4 "main"
|
|
OpExecutionMode %4 OriginUpperLeft
|
|
OpSource ESSL 310
|
|
%2 = OpTypeVoid
|
|
%3 = OpTypeFunction %2
|
|
%6 = OpTypeInt 32 1
|
|
%7 = OpTypePointer Function %6
|
|
%8 = OpTypeFloat 32
|
|
%9 = OpTypePointer Function %8
|
|
%10 = OpTypeFunction %8 %7 %9
|
|
%18 = OpConstant %8 0
|
|
%20 = OpConstant %6 0
|
|
%28 = OpTypeBool
|
|
%37 = OpConstant %6 1
|
|
%42 = OpTypePointer Private %8
|
|
%43 = OpVariable %42 Private
|
|
%47 = OpConstant %8 1
|
|
%4 = OpFunction %2 None %3
|
|
%5 = OpLabel
|
|
OpReturn
|
|
OpFunctionEnd
|
|
%13 = OpFunction %8 None %10
|
|
%11 = OpFunctionParameter %7
|
|
%12 = OpFunctionParameter %9
|
|
%14 = OpLabel
|
|
%17 = OpVariable %9 Function
|
|
%19 = OpVariable %7 Function
|
|
OpStore %17 %18
|
|
OpStore %19 %20
|
|
OpBranch %21
|
|
%21 = OpLabel
|
|
OpLoopMerge %23 %24 None
|
|
OpBranch %25
|
|
%25 = OpLabel
|
|
%26 = OpLoad %6 %19
|
|
%27 = OpLoad %6 %11
|
|
%29 = OpSLessThan %28 %26 %27
|
|
OpBranchConditional %29 %22 %23
|
|
%22 = OpLabel
|
|
%30 = OpLoad %8 %12
|
|
%31 = OpLoad %6 %19
|
|
%32 = OpConvertSToF %8 %31
|
|
%33 = OpFMul %8 %30 %32
|
|
%34 = OpLoad %8 %17
|
|
%35 = OpFAdd %8 %34 %33
|
|
OpStore %17 %35
|
|
OpBranch %24
|
|
%24 = OpLabel
|
|
%36 = OpLoad %6 %19
|
|
%38 = OpIAdd %6 %36 %37
|
|
OpStore %19 %38
|
|
OpBranch %21
|
|
%23 = OpLabel
|
|
%39 = OpLoad %8 %17
|
|
OpReturnValue %39
|
|
OpFunctionEnd
|
|
%15 = OpFunction %2 None %3
|
|
%16 = OpLabel
|
|
%44 = OpVariable %7 Function
|
|
%45 = OpVariable %9 Function
|
|
%48 = OpVariable %7 Function
|
|
%49 = OpVariable %9 Function
|
|
OpStore %44 %20
|
|
OpStore %45 %18
|
|
%46 = OpFunctionCall %8 %13 %44 %45
|
|
OpStore %48 %37
|
|
OpStore %49 %47
|
|
%50 = OpFunctionCall %8 %13 %48 %49
|
|
%51 = OpFAdd %8 %46 %50
|
|
OpStore %43 %51
|
|
OpReturn
|
|
OpFunctionEnd
|
|
)";
|
|
ASSERT_TRUE(IsEqual(env, after_transformation2, context.get()));
|
|
ASSERT_TRUE(fact_manager.BlockIsDead(16));
|
|
}
|
|
|
|
TEST(TransformationAddFunctionTest, InapplicableTransformations) {
|
|
std::string shader = R"(
|
|
OpCapability Shader
|
|
%1 = OpExtInstImport "GLSL.std.450"
|
|
OpMemoryModel Logical GLSL450
|
|
OpEntryPoint Fragment %4 "main"
|
|
OpExecutionMode %4 OriginUpperLeft
|
|
OpSource ESSL 310
|
|
%2 = OpTypeVoid
|
|
%3 = OpTypeFunction %2
|
|
%6 = OpTypeInt 32 1
|
|
%7 = OpTypePointer Function %6
|
|
%8 = OpTypeFloat 32
|
|
%9 = OpTypePointer Function %8
|
|
%10 = OpTypeFunction %8 %7 %9
|
|
%18 = OpConstant %8 0
|
|
%20 = OpConstant %6 0
|
|
%28 = OpTypeBool
|
|
%37 = OpConstant %6 1
|
|
%42 = OpTypePointer Private %8
|
|
%43 = OpVariable %42 Private
|
|
%47 = OpConstant %8 1
|
|
%4 = OpFunction %2 None %3
|
|
%5 = OpLabel
|
|
OpReturn
|
|
OpFunctionEnd
|
|
%13 = OpFunction %8 None %10
|
|
%11 = OpFunctionParameter %7
|
|
%12 = OpFunctionParameter %9
|
|
%14 = OpLabel
|
|
%17 = OpVariable %9 Function
|
|
%19 = OpVariable %7 Function
|
|
OpStore %17 %18
|
|
OpStore %19 %20
|
|
OpBranch %21
|
|
%21 = OpLabel
|
|
OpLoopMerge %23 %24 None
|
|
OpBranch %25
|
|
%25 = OpLabel
|
|
%26 = OpLoad %6 %19
|
|
%27 = OpLoad %6 %11
|
|
%29 = OpSLessThan %28 %26 %27
|
|
OpBranchConditional %29 %22 %23
|
|
%22 = OpLabel
|
|
%30 = OpLoad %8 %12
|
|
%31 = OpLoad %6 %19
|
|
%32 = OpConvertSToF %8 %31
|
|
%33 = OpFMul %8 %30 %32
|
|
%34 = OpLoad %8 %17
|
|
%35 = OpFAdd %8 %34 %33
|
|
OpStore %17 %35
|
|
OpBranch %24
|
|
%24 = OpLabel
|
|
%36 = OpLoad %6 %19
|
|
%38 = OpIAdd %6 %36 %37
|
|
OpStore %19 %38
|
|
OpBranch %21
|
|
%23 = OpLabel
|
|
%39 = OpLoad %8 %17
|
|
OpReturnValue %39
|
|
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()));
|
|
|
|
FactManager fact_manager;
|
|
|
|
// No instructions
|
|
ASSERT_FALSE(
|
|
TransformationAddFunction(std::vector<protobufs::Instruction>({}))
|
|
.IsApplicable(context.get(), fact_manager));
|
|
|
|
// No function begin
|
|
ASSERT_FALSE(
|
|
TransformationAddFunction(
|
|
std::vector<protobufs::Instruction>(
|
|
{MakeInstructionMessage(SpvOpFunctionParameter, 7, 11, {}),
|
|
MakeInstructionMessage(SpvOpFunctionParameter, 9, 12, {}),
|
|
MakeInstructionMessage(SpvOpLabel, 0, 14, {})}))
|
|
.IsApplicable(context.get(), fact_manager));
|
|
|
|
// No OpLabel
|
|
ASSERT_FALSE(
|
|
TransformationAddFunction(
|
|
std::vector<protobufs::Instruction>(
|
|
{MakeInstructionMessage(SpvOpFunction, 8, 13,
|
|
{{SPV_OPERAND_TYPE_FUNCTION_CONTROL,
|
|
{SpvFunctionControlMaskNone}},
|
|
{SPV_OPERAND_TYPE_ID, {10}}}),
|
|
MakeInstructionMessage(SpvOpReturnValue, 0, 0,
|
|
{{SPV_OPERAND_TYPE_ID, {39}}}),
|
|
MakeInstructionMessage(SpvOpFunctionEnd, 0, 0, {})}))
|
|
.IsApplicable(context.get(), fact_manager));
|
|
|
|
// Abrupt end of instructions
|
|
ASSERT_FALSE(TransformationAddFunction(
|
|
std::vector<protobufs::Instruction>({MakeInstructionMessage(
|
|
SpvOpFunction, 8, 13,
|
|
{{SPV_OPERAND_TYPE_FUNCTION_CONTROL,
|
|
{SpvFunctionControlMaskNone}},
|
|
{SPV_OPERAND_TYPE_ID, {10}}})}))
|
|
.IsApplicable(context.get(), fact_manager));
|
|
|
|
// No function end
|
|
ASSERT_FALSE(
|
|
TransformationAddFunction(
|
|
std::vector<protobufs::Instruction>(
|
|
{MakeInstructionMessage(SpvOpFunction, 8, 13,
|
|
{{SPV_OPERAND_TYPE_FUNCTION_CONTROL,
|
|
{SpvFunctionControlMaskNone}},
|
|
{SPV_OPERAND_TYPE_ID, {10}}}),
|
|
MakeInstructionMessage(SpvOpLabel, 0, 14, {}),
|
|
MakeInstructionMessage(SpvOpReturnValue, 0, 0,
|
|
{{SPV_OPERAND_TYPE_ID, {39}}})}))
|
|
.IsApplicable(context.get(), fact_manager));
|
|
}
|
|
|
|
TEST(TransformationAddFunctionTest, LoopLimiters) {
|
|
std::string shader = R"(
|
|
OpCapability Shader
|
|
%1 = OpExtInstImport "GLSL.std.450"
|
|
OpMemoryModel Logical GLSL450
|
|
OpEntryPoint Fragment %4 "main"
|
|
OpExecutionMode %4 OriginUpperLeft
|
|
OpSource ESSL 310
|
|
%2 = OpTypeVoid
|
|
%3 = OpTypeFunction %2
|
|
%6 = OpTypeInt 32 0
|
|
%7 = OpTypePointer Function %6
|
|
%8 = OpConstant %6 0
|
|
%9 = OpConstant %6 1
|
|
%10 = OpConstant %6 5
|
|
%11 = OpTypeBool
|
|
%12 = OpConstantTrue %11
|
|
%4 = OpFunction %2 None %3
|
|
%5 = OpLabel
|
|
OpReturn
|
|
OpFunctionEnd
|
|
)";
|
|
|
|
const auto env = SPV_ENV_UNIVERSAL_1_4;
|
|
const auto consumer = nullptr;
|
|
|
|
std::vector<protobufs::Instruction> instructions;
|
|
instructions.push_back(MakeInstructionMessage(
|
|
SpvOpFunction, 2, 30,
|
|
{{SPV_OPERAND_TYPE_FUNCTION_CONTROL, {SpvFunctionControlMaskNone}},
|
|
{SPV_OPERAND_TYPE_TYPE_ID, {3}}}));
|
|
instructions.push_back(MakeInstructionMessage(SpvOpLabel, 0, 31, {}));
|
|
instructions.push_back(
|
|
MakeInstructionMessage(SpvOpBranch, 0, 0, {{SPV_OPERAND_TYPE_ID, {20}}}));
|
|
instructions.push_back(MakeInstructionMessage(SpvOpLabel, 0, 20, {}));
|
|
instructions.push_back(MakeInstructionMessage(
|
|
SpvOpLoopMerge, 0, 0,
|
|
{{SPV_OPERAND_TYPE_ID, {21}},
|
|
{SPV_OPERAND_TYPE_ID, {22}},
|
|
{SPV_OPERAND_TYPE_LOOP_CONTROL, {SpvLoopControlMaskNone}}}));
|
|
instructions.push_back(MakeInstructionMessage(SpvOpBranchConditional, 0, 0,
|
|
{{SPV_OPERAND_TYPE_ID, {12}},
|
|
{SPV_OPERAND_TYPE_ID, {23}},
|
|
{SPV_OPERAND_TYPE_ID, {21}}}));
|
|
instructions.push_back(MakeInstructionMessage(SpvOpLabel, 0, 23, {}));
|
|
instructions.push_back(MakeInstructionMessage(
|
|
SpvOpLoopMerge, 0, 0,
|
|
{{SPV_OPERAND_TYPE_ID, {25}},
|
|
{SPV_OPERAND_TYPE_ID, {26}},
|
|
{SPV_OPERAND_TYPE_LOOP_CONTROL, {SpvLoopControlMaskNone}}}));
|
|
instructions.push_back(
|
|
MakeInstructionMessage(SpvOpBranch, 0, 0, {{SPV_OPERAND_TYPE_ID, {28}}}));
|
|
instructions.push_back(MakeInstructionMessage(SpvOpLabel, 0, 28, {}));
|
|
instructions.push_back(MakeInstructionMessage(SpvOpBranchConditional, 0, 0,
|
|
{{SPV_OPERAND_TYPE_ID, {12}},
|
|
{SPV_OPERAND_TYPE_ID, {26}},
|
|
{SPV_OPERAND_TYPE_ID, {25}}}));
|
|
instructions.push_back(MakeInstructionMessage(SpvOpLabel, 0, 26, {}));
|
|
instructions.push_back(
|
|
MakeInstructionMessage(SpvOpBranch, 0, 0, {{SPV_OPERAND_TYPE_ID, {23}}}));
|
|
instructions.push_back(MakeInstructionMessage(SpvOpLabel, 0, 25, {}));
|
|
instructions.push_back(MakeInstructionMessage(
|
|
SpvOpLoopMerge, 0, 0,
|
|
{{SPV_OPERAND_TYPE_ID, {24}},
|
|
{SPV_OPERAND_TYPE_ID, {27}},
|
|
{SPV_OPERAND_TYPE_LOOP_CONTROL, {SpvLoopControlMaskNone}}}));
|
|
instructions.push_back(MakeInstructionMessage(SpvOpBranchConditional, 0, 0,
|
|
{{SPV_OPERAND_TYPE_ID, {12}},
|
|
{SPV_OPERAND_TYPE_ID, {24}},
|
|
{SPV_OPERAND_TYPE_ID, {27}}}));
|
|
instructions.push_back(MakeInstructionMessage(SpvOpLabel, 0, 27, {}));
|
|
instructions.push_back(
|
|
MakeInstructionMessage(SpvOpBranch, 0, 0, {{SPV_OPERAND_TYPE_ID, {25}}}));
|
|
instructions.push_back(MakeInstructionMessage(SpvOpLabel, 0, 24, {}));
|
|
instructions.push_back(
|
|
MakeInstructionMessage(SpvOpBranch, 0, 0, {{SPV_OPERAND_TYPE_ID, {22}}}));
|
|
instructions.push_back(MakeInstructionMessage(SpvOpLabel, 0, 22, {}));
|
|
instructions.push_back(
|
|
MakeInstructionMessage(SpvOpBranch, 0, 0, {{SPV_OPERAND_TYPE_ID, {20}}}));
|
|
instructions.push_back(MakeInstructionMessage(SpvOpLabel, 0, 21, {}));
|
|
instructions.push_back(MakeInstructionMessage(SpvOpReturn, 0, 0, {}));
|
|
instructions.push_back(MakeInstructionMessage(SpvOpFunctionEnd, 0, 0, {}));
|
|
|
|
FactManager fact_manager;
|
|
|
|
const auto context1 = BuildModule(env, consumer, shader, kFuzzAssembleOption);
|
|
const auto context2 = BuildModule(env, consumer, shader, kFuzzAssembleOption);
|
|
ASSERT_TRUE(IsValid(env, context1.get()));
|
|
|
|
TransformationAddFunction add_dead_function(instructions);
|
|
ASSERT_TRUE(add_dead_function.IsApplicable(context1.get(), fact_manager));
|
|
add_dead_function.Apply(context1.get(), &fact_manager);
|
|
ASSERT_TRUE(IsValid(env, context1.get()));
|
|
// The added function should not be deemed livesafe.
|
|
ASSERT_FALSE(fact_manager.FunctionIsLivesafe(30));
|
|
|
|
std::string added_as_dead_code = R"(
|
|
OpCapability Shader
|
|
%1 = OpExtInstImport "GLSL.std.450"
|
|
OpMemoryModel Logical GLSL450
|
|
OpEntryPoint Fragment %4 "main"
|
|
OpExecutionMode %4 OriginUpperLeft
|
|
OpSource ESSL 310
|
|
%2 = OpTypeVoid
|
|
%3 = OpTypeFunction %2
|
|
%6 = OpTypeInt 32 0
|
|
%7 = OpTypePointer Function %6
|
|
%8 = OpConstant %6 0
|
|
%9 = OpConstant %6 1
|
|
%10 = OpConstant %6 5
|
|
%11 = OpTypeBool
|
|
%12 = OpConstantTrue %11
|
|
%4 = OpFunction %2 None %3
|
|
%5 = OpLabel
|
|
OpReturn
|
|
OpFunctionEnd
|
|
%30 = OpFunction %2 None %3
|
|
%31 = OpLabel
|
|
OpBranch %20
|
|
%20 = OpLabel
|
|
OpLoopMerge %21 %22 None
|
|
OpBranchConditional %12 %23 %21
|
|
%23 = OpLabel
|
|
OpLoopMerge %25 %26 None
|
|
OpBranch %28
|
|
%28 = OpLabel
|
|
OpBranchConditional %12 %26 %25
|
|
%26 = OpLabel
|
|
OpBranch %23
|
|
%25 = OpLabel
|
|
OpLoopMerge %24 %27 None
|
|
OpBranchConditional %12 %24 %27
|
|
%27 = OpLabel
|
|
OpBranch %25
|
|
%24 = OpLabel
|
|
OpBranch %22
|
|
%22 = OpLabel
|
|
OpBranch %20
|
|
%21 = OpLabel
|
|
OpReturn
|
|
OpFunctionEnd
|
|
)";
|
|
ASSERT_TRUE(IsEqual(env, added_as_dead_code, context1.get()));
|
|
|
|
protobufs::LoopLimiterInfo loop_limiter1;
|
|
loop_limiter1.set_loop_header_id(20);
|
|
loop_limiter1.set_load_id(101);
|
|
loop_limiter1.set_increment_id(102);
|
|
loop_limiter1.set_compare_id(103);
|
|
loop_limiter1.set_logical_op_id(104);
|
|
|
|
protobufs::LoopLimiterInfo loop_limiter2;
|
|
loop_limiter2.set_loop_header_id(23);
|
|
loop_limiter2.set_load_id(105);
|
|
loop_limiter2.set_increment_id(106);
|
|
loop_limiter2.set_compare_id(107);
|
|
loop_limiter2.set_logical_op_id(108);
|
|
|
|
protobufs::LoopLimiterInfo loop_limiter3;
|
|
loop_limiter3.set_loop_header_id(25);
|
|
loop_limiter3.set_load_id(109);
|
|
loop_limiter3.set_increment_id(110);
|
|
loop_limiter3.set_compare_id(111);
|
|
loop_limiter3.set_logical_op_id(112);
|
|
|
|
std::vector<protobufs::LoopLimiterInfo> loop_limiters = {
|
|
loop_limiter1, loop_limiter2, loop_limiter3};
|
|
|
|
TransformationAddFunction add_livesafe_function(instructions, 100, 10,
|
|
loop_limiters, 0, {});
|
|
ASSERT_TRUE(add_livesafe_function.IsApplicable(context2.get(), fact_manager));
|
|
add_livesafe_function.Apply(context2.get(), &fact_manager);
|
|
ASSERT_TRUE(IsValid(env, context2.get()));
|
|
// The added function should indeed be deemed livesafe.
|
|
ASSERT_TRUE(fact_manager.FunctionIsLivesafe(30));
|
|
std::string added_as_livesafe_code = R"(
|
|
OpCapability Shader
|
|
%1 = OpExtInstImport "GLSL.std.450"
|
|
OpMemoryModel Logical GLSL450
|
|
OpEntryPoint Fragment %4 "main"
|
|
OpExecutionMode %4 OriginUpperLeft
|
|
OpSource ESSL 310
|
|
%2 = OpTypeVoid
|
|
%3 = OpTypeFunction %2
|
|
%6 = OpTypeInt 32 0
|
|
%7 = OpTypePointer Function %6
|
|
%8 = OpConstant %6 0
|
|
%9 = OpConstant %6 1
|
|
%10 = OpConstant %6 5
|
|
%11 = OpTypeBool
|
|
%12 = OpConstantTrue %11
|
|
%4 = OpFunction %2 None %3
|
|
%5 = OpLabel
|
|
OpReturn
|
|
OpFunctionEnd
|
|
%30 = OpFunction %2 None %3
|
|
%31 = OpLabel
|
|
%100 = OpVariable %7 Function %8
|
|
OpBranch %20
|
|
%20 = OpLabel
|
|
OpLoopMerge %21 %22 None
|
|
OpBranchConditional %12 %23 %21
|
|
%23 = OpLabel
|
|
OpLoopMerge %25 %26 None
|
|
OpBranch %28
|
|
%28 = OpLabel
|
|
OpBranchConditional %12 %26 %25
|
|
%26 = OpLabel
|
|
%105 = OpLoad %6 %100
|
|
%106 = OpIAdd %6 %105 %9
|
|
OpStore %100 %106
|
|
%107 = OpUGreaterThanEqual %11 %105 %10
|
|
OpBranchConditional %107 %25 %23
|
|
%25 = OpLabel
|
|
OpLoopMerge %24 %27 None
|
|
OpBranchConditional %12 %24 %27
|
|
%27 = OpLabel
|
|
%109 = OpLoad %6 %100
|
|
%110 = OpIAdd %6 %109 %9
|
|
OpStore %100 %110
|
|
%111 = OpUGreaterThanEqual %11 %109 %10
|
|
OpBranchConditional %111 %24 %25
|
|
%24 = OpLabel
|
|
OpBranch %22
|
|
%22 = OpLabel
|
|
%101 = OpLoad %6 %100
|
|
%102 = OpIAdd %6 %101 %9
|
|
OpStore %100 %102
|
|
%103 = OpUGreaterThanEqual %11 %101 %10
|
|
OpBranchConditional %103 %21 %20
|
|
%21 = OpLabel
|
|
OpReturn
|
|
OpFunctionEnd
|
|
)";
|
|
ASSERT_TRUE(IsEqual(env, added_as_livesafe_code, context2.get()));
|
|
}
|
|
|
|
TEST(TransformationAddFunctionTest, KillAndUnreachableInVoidFunction) {
|
|
std::string shader = R"(
|
|
OpCapability Shader
|
|
%1 = OpExtInstImport "GLSL.std.450"
|
|
OpMemoryModel Logical GLSL450
|
|
OpEntryPoint Fragment %4 "main"
|
|
OpExecutionMode %4 OriginUpperLeft
|
|
OpSource ESSL 310
|
|
%2 = OpTypeVoid
|
|
%3 = OpTypeFunction %2
|
|
%6 = OpTypeInt 32 1
|
|
%7 = OpTypePointer Function %6
|
|
%8 = OpTypeFunction %2 %7
|
|
%13 = OpConstant %6 2
|
|
%14 = OpTypeBool
|
|
%4 = OpFunction %2 None %3
|
|
%5 = OpLabel
|
|
OpReturn
|
|
OpFunctionEnd
|
|
)";
|
|
|
|
const auto env = SPV_ENV_UNIVERSAL_1_4;
|
|
const auto consumer = nullptr;
|
|
|
|
std::vector<protobufs::Instruction> instructions;
|
|
|
|
instructions.push_back(MakeInstructionMessage(
|
|
SpvOpFunction, 2, 10,
|
|
{{SPV_OPERAND_TYPE_FUNCTION_CONTROL, {SpvFunctionControlMaskNone}},
|
|
{SPV_OPERAND_TYPE_TYPE_ID, {8}}}));
|
|
instructions.push_back(
|
|
MakeInstructionMessage(SpvOpFunctionParameter, 7, 9, {}));
|
|
instructions.push_back(MakeInstructionMessage(SpvOpLabel, 0, 11, {}));
|
|
instructions.push_back(
|
|
MakeInstructionMessage(SpvOpLoad, 6, 12, {{SPV_OPERAND_TYPE_ID, {9}}}));
|
|
instructions.push_back(MakeInstructionMessage(
|
|
SpvOpIEqual, 14, 15,
|
|
{{SPV_OPERAND_TYPE_ID, {12}}, {SPV_OPERAND_TYPE_ID, {13}}}));
|
|
instructions.push_back(MakeInstructionMessage(
|
|
SpvOpSelectionMerge, 0, 0,
|
|
{{SPV_OPERAND_TYPE_ID, {17}},
|
|
{SPV_OPERAND_TYPE_SELECTION_CONTROL, {SpvSelectionControlMaskNone}}}));
|
|
instructions.push_back(MakeInstructionMessage(SpvOpBranchConditional, 0, 0,
|
|
{{SPV_OPERAND_TYPE_ID, {15}},
|
|
{SPV_OPERAND_TYPE_ID, {16}},
|
|
{SPV_OPERAND_TYPE_ID, {17}}}));
|
|
instructions.push_back(MakeInstructionMessage(SpvOpLabel, 0, 16, {}));
|
|
instructions.push_back(MakeInstructionMessage(SpvOpUnreachable, 0, 0, {}));
|
|
instructions.push_back(MakeInstructionMessage(SpvOpLabel, 0, 17, {}));
|
|
instructions.push_back(MakeInstructionMessage(SpvOpKill, 0, 0, {}));
|
|
instructions.push_back(MakeInstructionMessage(SpvOpFunctionEnd, 0, 0, {}));
|
|
|
|
FactManager fact_manager;
|
|
|
|
const auto context1 = BuildModule(env, consumer, shader, kFuzzAssembleOption);
|
|
const auto context2 = BuildModule(env, consumer, shader, kFuzzAssembleOption);
|
|
ASSERT_TRUE(IsValid(env, context1.get()));
|
|
|
|
TransformationAddFunction add_dead_function(instructions);
|
|
ASSERT_TRUE(add_dead_function.IsApplicable(context1.get(), fact_manager));
|
|
add_dead_function.Apply(context1.get(), &fact_manager);
|
|
ASSERT_TRUE(IsValid(env, context1.get()));
|
|
// The added function should not be deemed livesafe.
|
|
ASSERT_FALSE(fact_manager.FunctionIsLivesafe(10));
|
|
|
|
std::string added_as_dead_code = R"(
|
|
OpCapability Shader
|
|
%1 = OpExtInstImport "GLSL.std.450"
|
|
OpMemoryModel Logical GLSL450
|
|
OpEntryPoint Fragment %4 "main"
|
|
OpExecutionMode %4 OriginUpperLeft
|
|
OpSource ESSL 310
|
|
%2 = OpTypeVoid
|
|
%3 = OpTypeFunction %2
|
|
%6 = OpTypeInt 32 1
|
|
%7 = OpTypePointer Function %6
|
|
%8 = OpTypeFunction %2 %7
|
|
%13 = OpConstant %6 2
|
|
%14 = OpTypeBool
|
|
%4 = OpFunction %2 None %3
|
|
%5 = OpLabel
|
|
OpReturn
|
|
OpFunctionEnd
|
|
%10 = OpFunction %2 None %8
|
|
%9 = OpFunctionParameter %7
|
|
%11 = OpLabel
|
|
%12 = OpLoad %6 %9
|
|
%15 = OpIEqual %14 %12 %13
|
|
OpSelectionMerge %17 None
|
|
OpBranchConditional %15 %16 %17
|
|
%16 = OpLabel
|
|
OpUnreachable
|
|
%17 = OpLabel
|
|
OpKill
|
|
OpFunctionEnd
|
|
)";
|
|
ASSERT_TRUE(IsEqual(env, added_as_dead_code, context1.get()));
|
|
|
|
TransformationAddFunction add_livesafe_function(instructions, 0, 0, {}, 0,
|
|
{});
|
|
ASSERT_TRUE(add_livesafe_function.IsApplicable(context2.get(), fact_manager));
|
|
add_livesafe_function.Apply(context2.get(), &fact_manager);
|
|
ASSERT_TRUE(IsValid(env, context2.get()));
|
|
// The added function should indeed be deemed livesafe.
|
|
ASSERT_TRUE(fact_manager.FunctionIsLivesafe(10));
|
|
std::string added_as_livesafe_code = R"(
|
|
OpCapability Shader
|
|
%1 = OpExtInstImport "GLSL.std.450"
|
|
OpMemoryModel Logical GLSL450
|
|
OpEntryPoint Fragment %4 "main"
|
|
OpExecutionMode %4 OriginUpperLeft
|
|
OpSource ESSL 310
|
|
%2 = OpTypeVoid
|
|
%3 = OpTypeFunction %2
|
|
%6 = OpTypeInt 32 1
|
|
%7 = OpTypePointer Function %6
|
|
%8 = OpTypeFunction %2 %7
|
|
%13 = OpConstant %6 2
|
|
%14 = OpTypeBool
|
|
%4 = OpFunction %2 None %3
|
|
%5 = OpLabel
|
|
OpReturn
|
|
OpFunctionEnd
|
|
%10 = OpFunction %2 None %8
|
|
%9 = OpFunctionParameter %7
|
|
%11 = OpLabel
|
|
%12 = OpLoad %6 %9
|
|
%15 = OpIEqual %14 %12 %13
|
|
OpSelectionMerge %17 None
|
|
OpBranchConditional %15 %16 %17
|
|
%16 = OpLabel
|
|
OpReturn
|
|
%17 = OpLabel
|
|
OpReturn
|
|
OpFunctionEnd
|
|
)";
|
|
ASSERT_TRUE(IsEqual(env, added_as_livesafe_code, context2.get()));
|
|
}
|
|
|
|
TEST(TransformationAddFunctionTest, KillAndUnreachableInNonVoidFunction) {
|
|
std::string shader = R"(
|
|
OpCapability Shader
|
|
%1 = OpExtInstImport "GLSL.std.450"
|
|
OpMemoryModel Logical GLSL450
|
|
OpEntryPoint Fragment %4 "main"
|
|
OpExecutionMode %4 OriginUpperLeft
|
|
OpSource ESSL 310
|
|
%2 = OpTypeVoid
|
|
%3 = OpTypeFunction %2
|
|
%6 = OpTypeInt 32 1
|
|
%7 = OpTypePointer Function %6
|
|
%8 = OpTypeFunction %2 %7
|
|
%50 = OpTypeFunction %6 %7
|
|
%13 = OpConstant %6 2
|
|
%14 = OpTypeBool
|
|
%4 = OpFunction %2 None %3
|
|
%5 = OpLabel
|
|
OpReturn
|
|
OpFunctionEnd
|
|
)";
|
|
|
|
const auto env = SPV_ENV_UNIVERSAL_1_4;
|
|
const auto consumer = nullptr;
|
|
|
|
std::vector<protobufs::Instruction> instructions;
|
|
|
|
instructions.push_back(MakeInstructionMessage(
|
|
SpvOpFunction, 6, 10,
|
|
{{SPV_OPERAND_TYPE_FUNCTION_CONTROL, {SpvFunctionControlMaskNone}},
|
|
{SPV_OPERAND_TYPE_TYPE_ID, {50}}}));
|
|
instructions.push_back(
|
|
MakeInstructionMessage(SpvOpFunctionParameter, 7, 9, {}));
|
|
instructions.push_back(MakeInstructionMessage(SpvOpLabel, 0, 11, {}));
|
|
instructions.push_back(
|
|
MakeInstructionMessage(SpvOpLoad, 6, 12, {{SPV_OPERAND_TYPE_ID, {9}}}));
|
|
instructions.push_back(MakeInstructionMessage(
|
|
SpvOpIEqual, 14, 15,
|
|
{{SPV_OPERAND_TYPE_ID, {12}}, {SPV_OPERAND_TYPE_ID, {13}}}));
|
|
instructions.push_back(MakeInstructionMessage(
|
|
SpvOpSelectionMerge, 0, 0,
|
|
{{SPV_OPERAND_TYPE_ID, {17}},
|
|
{SPV_OPERAND_TYPE_SELECTION_CONTROL, {SpvSelectionControlMaskNone}}}));
|
|
instructions.push_back(MakeInstructionMessage(SpvOpBranchConditional, 0, 0,
|
|
{{SPV_OPERAND_TYPE_ID, {15}},
|
|
{SPV_OPERAND_TYPE_ID, {16}},
|
|
{SPV_OPERAND_TYPE_ID, {17}}}));
|
|
instructions.push_back(MakeInstructionMessage(SpvOpLabel, 0, 16, {}));
|
|
instructions.push_back(MakeInstructionMessage(SpvOpUnreachable, 0, 0, {}));
|
|
instructions.push_back(MakeInstructionMessage(SpvOpLabel, 0, 17, {}));
|
|
instructions.push_back(MakeInstructionMessage(SpvOpKill, 0, 0, {}));
|
|
instructions.push_back(MakeInstructionMessage(SpvOpFunctionEnd, 0, 0, {}));
|
|
|
|
FactManager fact_manager;
|
|
|
|
const auto context1 = BuildModule(env, consumer, shader, kFuzzAssembleOption);
|
|
const auto context2 = BuildModule(env, consumer, shader, kFuzzAssembleOption);
|
|
ASSERT_TRUE(IsValid(env, context1.get()));
|
|
|
|
TransformationAddFunction add_dead_function(instructions);
|
|
ASSERT_TRUE(add_dead_function.IsApplicable(context1.get(), fact_manager));
|
|
add_dead_function.Apply(context1.get(), &fact_manager);
|
|
ASSERT_TRUE(IsValid(env, context1.get()));
|
|
// The added function should not be deemed livesafe.
|
|
ASSERT_FALSE(fact_manager.FunctionIsLivesafe(10));
|
|
|
|
std::string added_as_dead_code = R"(
|
|
OpCapability Shader
|
|
%1 = OpExtInstImport "GLSL.std.450"
|
|
OpMemoryModel Logical GLSL450
|
|
OpEntryPoint Fragment %4 "main"
|
|
OpExecutionMode %4 OriginUpperLeft
|
|
OpSource ESSL 310
|
|
%2 = OpTypeVoid
|
|
%3 = OpTypeFunction %2
|
|
%6 = OpTypeInt 32 1
|
|
%7 = OpTypePointer Function %6
|
|
%8 = OpTypeFunction %2 %7
|
|
%50 = OpTypeFunction %6 %7
|
|
%13 = OpConstant %6 2
|
|
%14 = OpTypeBool
|
|
%4 = OpFunction %2 None %3
|
|
%5 = OpLabel
|
|
OpReturn
|
|
OpFunctionEnd
|
|
%10 = OpFunction %6 None %50
|
|
%9 = OpFunctionParameter %7
|
|
%11 = OpLabel
|
|
%12 = OpLoad %6 %9
|
|
%15 = OpIEqual %14 %12 %13
|
|
OpSelectionMerge %17 None
|
|
OpBranchConditional %15 %16 %17
|
|
%16 = OpLabel
|
|
OpUnreachable
|
|
%17 = OpLabel
|
|
OpKill
|
|
OpFunctionEnd
|
|
)";
|
|
ASSERT_TRUE(IsEqual(env, added_as_dead_code, context1.get()));
|
|
|
|
TransformationAddFunction add_livesafe_function(instructions, 0, 0, {}, 13,
|
|
{});
|
|
ASSERT_TRUE(add_livesafe_function.IsApplicable(context2.get(), fact_manager));
|
|
add_livesafe_function.Apply(context2.get(), &fact_manager);
|
|
ASSERT_TRUE(IsValid(env, context2.get()));
|
|
// The added function should indeed be deemed livesafe.
|
|
ASSERT_TRUE(fact_manager.FunctionIsLivesafe(10));
|
|
std::string added_as_livesafe_code = R"(
|
|
OpCapability Shader
|
|
%1 = OpExtInstImport "GLSL.std.450"
|
|
OpMemoryModel Logical GLSL450
|
|
OpEntryPoint Fragment %4 "main"
|
|
OpExecutionMode %4 OriginUpperLeft
|
|
OpSource ESSL 310
|
|
%2 = OpTypeVoid
|
|
%3 = OpTypeFunction %2
|
|
%6 = OpTypeInt 32 1
|
|
%7 = OpTypePointer Function %6
|
|
%8 = OpTypeFunction %2 %7
|
|
%50 = OpTypeFunction %6 %7
|
|
%13 = OpConstant %6 2
|
|
%14 = OpTypeBool
|
|
%4 = OpFunction %2 None %3
|
|
%5 = OpLabel
|
|
OpReturn
|
|
OpFunctionEnd
|
|
%10 = OpFunction %6 None %50
|
|
%9 = OpFunctionParameter %7
|
|
%11 = OpLabel
|
|
%12 = OpLoad %6 %9
|
|
%15 = OpIEqual %14 %12 %13
|
|
OpSelectionMerge %17 None
|
|
OpBranchConditional %15 %16 %17
|
|
%16 = OpLabel
|
|
OpReturnValue %13
|
|
%17 = OpLabel
|
|
OpReturnValue %13
|
|
OpFunctionEnd
|
|
)";
|
|
ASSERT_TRUE(IsEqual(env, added_as_livesafe_code, context2.get()));
|
|
}
|
|
|
|
TEST(TransformationAddFunctionTest, ClampedAccessChains) {
|
|
std::string shader = R"(
|
|
OpCapability Shader
|
|
%1 = OpExtInstImport "GLSL.std.450"
|
|
OpMemoryModel Logical GLSL450
|
|
OpEntryPoint Fragment %4 "main"
|
|
OpExecutionMode %4 OriginUpperLeft
|
|
OpSource ESSL 310
|
|
%2 = OpTypeVoid
|
|
%100 = OpTypeBool
|
|
%3 = OpTypeFunction %2
|
|
%6 = OpTypeInt 32 1
|
|
%7 = OpTypePointer Function %6
|
|
%15 = OpTypeInt 32 0
|
|
%102 = OpTypePointer Function %15
|
|
%8 = OpTypeFunction %2 %7 %102 %7
|
|
%16 = OpConstant %15 5
|
|
%17 = OpTypeArray %6 %16
|
|
%18 = OpTypeArray %17 %16
|
|
%19 = OpTypePointer Private %18
|
|
%20 = OpVariable %19 Private
|
|
%21 = OpConstant %6 0
|
|
%23 = OpTypePointer Private %6
|
|
%26 = OpTypePointer Function %17
|
|
%29 = OpTypePointer Private %17
|
|
%33 = OpConstant %6 4
|
|
%200 = OpConstant %15 4
|
|
%35 = OpConstant %15 10
|
|
%36 = OpTypeArray %6 %35
|
|
%37 = OpTypePointer Private %36
|
|
%38 = OpVariable %37 Private
|
|
%54 = OpTypeFloat 32
|
|
%55 = OpTypeVector %54 4
|
|
%56 = OpTypePointer Private %55
|
|
%57 = OpVariable %56 Private
|
|
%59 = OpTypeVector %54 3
|
|
%60 = OpTypeMatrix %59 2
|
|
%61 = OpTypePointer Private %60
|
|
%62 = OpVariable %61 Private
|
|
%64 = OpTypePointer Private %54
|
|
%69 = OpConstant %54 2
|
|
%71 = OpConstant %6 1
|
|
%72 = OpConstant %6 2
|
|
%201 = OpConstant %15 2
|
|
%73 = OpConstant %6 3
|
|
%202 = OpConstant %15 3
|
|
%203 = OpConstant %6 1
|
|
%204 = OpConstant %6 9
|
|
%4 = OpFunction %2 None %3
|
|
%5 = OpLabel
|
|
OpReturn
|
|
OpFunctionEnd
|
|
)";
|
|
|
|
const auto env = SPV_ENV_UNIVERSAL_1_4;
|
|
const auto consumer = nullptr;
|
|
|
|
std::vector<protobufs::Instruction> instructions;
|
|
|
|
instructions.push_back(MakeInstructionMessage(
|
|
SpvOpFunction, 2, 12,
|
|
{{SPV_OPERAND_TYPE_FUNCTION_CONTROL, {SpvFunctionControlMaskNone}},
|
|
{SPV_OPERAND_TYPE_TYPE_ID, {8}}}));
|
|
instructions.push_back(
|
|
MakeInstructionMessage(SpvOpFunctionParameter, 7, 9, {}));
|
|
instructions.push_back(
|
|
MakeInstructionMessage(SpvOpFunctionParameter, 102, 10, {}));
|
|
instructions.push_back(
|
|
MakeInstructionMessage(SpvOpFunctionParameter, 7, 11, {}));
|
|
instructions.push_back(MakeInstructionMessage(SpvOpLabel, 0, 13, {}));
|
|
|
|
instructions.push_back(MakeInstructionMessage(
|
|
SpvOpVariable, 7, 14,
|
|
{{SPV_OPERAND_TYPE_STORAGE_CLASS, {SpvStorageClassFunction}}}));
|
|
instructions.push_back(MakeInstructionMessage(
|
|
SpvOpVariable, 26, 27,
|
|
{{SPV_OPERAND_TYPE_STORAGE_CLASS, {SpvStorageClassFunction}}}));
|
|
instructions.push_back(
|
|
MakeInstructionMessage(SpvOpLoad, 6, 22, {{SPV_OPERAND_TYPE_ID, {11}}}));
|
|
instructions.push_back(MakeInstructionMessage(SpvOpAccessChain, 23, 24,
|
|
{{SPV_OPERAND_TYPE_ID, {20}},
|
|
{SPV_OPERAND_TYPE_ID, {21}},
|
|
{SPV_OPERAND_TYPE_ID, {22}}}));
|
|
instructions.push_back(
|
|
MakeInstructionMessage(SpvOpLoad, 6, 25, {{SPV_OPERAND_TYPE_ID, {24}}}));
|
|
instructions.push_back(MakeInstructionMessage(
|
|
SpvOpStore, 0, 0,
|
|
{{SPV_OPERAND_TYPE_ID, {14}}, {SPV_OPERAND_TYPE_ID, {25}}}));
|
|
instructions.push_back(
|
|
MakeInstructionMessage(SpvOpLoad, 15, 28, {{SPV_OPERAND_TYPE_ID, {10}}}));
|
|
instructions.push_back(MakeInstructionMessage(
|
|
SpvOpAccessChain, 29, 30,
|
|
{{SPV_OPERAND_TYPE_ID, {20}}, {SPV_OPERAND_TYPE_ID, {28}}}));
|
|
instructions.push_back(
|
|
MakeInstructionMessage(SpvOpLoad, 17, 31, {{SPV_OPERAND_TYPE_ID, {30}}}));
|
|
instructions.push_back(MakeInstructionMessage(
|
|
SpvOpStore, 0, 0,
|
|
{{SPV_OPERAND_TYPE_ID, {27}}, {SPV_OPERAND_TYPE_ID, {31}}}));
|
|
instructions.push_back(
|
|
MakeInstructionMessage(SpvOpLoad, 6, 32, {{SPV_OPERAND_TYPE_ID, {9}}}));
|
|
instructions.push_back(MakeInstructionMessage(
|
|
SpvOpInBoundsAccessChain, 7, 34,
|
|
{{SPV_OPERAND_TYPE_ID, {27}}, {SPV_OPERAND_TYPE_ID, {32}}}));
|
|
instructions.push_back(MakeInstructionMessage(
|
|
SpvOpStore, 0, 0,
|
|
{{SPV_OPERAND_TYPE_ID, {34}}, {SPV_OPERAND_TYPE_ID, {33}}}));
|
|
instructions.push_back(
|
|
MakeInstructionMessage(SpvOpLoad, 6, 39, {{SPV_OPERAND_TYPE_ID, {9}}}));
|
|
instructions.push_back(MakeInstructionMessage(
|
|
SpvOpAccessChain, 23, 40,
|
|
{{SPV_OPERAND_TYPE_ID, {38}}, {SPV_OPERAND_TYPE_ID, {33}}}));
|
|
instructions.push_back(
|
|
MakeInstructionMessage(SpvOpLoad, 6, 41, {{SPV_OPERAND_TYPE_ID, {40}}}));
|
|
instructions.push_back(MakeInstructionMessage(
|
|
SpvOpInBoundsAccessChain, 23, 42,
|
|
{{SPV_OPERAND_TYPE_ID, {38}}, {SPV_OPERAND_TYPE_ID, {39}}}));
|
|
instructions.push_back(MakeInstructionMessage(
|
|
SpvOpStore, 0, 0,
|
|
{{SPV_OPERAND_TYPE_ID, {42}}, {SPV_OPERAND_TYPE_ID, {41}}}));
|
|
instructions.push_back(
|
|
MakeInstructionMessage(SpvOpLoad, 15, 43, {{SPV_OPERAND_TYPE_ID, {10}}}));
|
|
instructions.push_back(
|
|
MakeInstructionMessage(SpvOpLoad, 6, 44, {{SPV_OPERAND_TYPE_ID, {11}}}));
|
|
instructions.push_back(
|
|
MakeInstructionMessage(SpvOpLoad, 6, 45, {{SPV_OPERAND_TYPE_ID, {9}}}));
|
|
instructions.push_back(
|
|
MakeInstructionMessage(SpvOpLoad, 15, 46, {{SPV_OPERAND_TYPE_ID, {10}}}));
|
|
instructions.push_back(MakeInstructionMessage(
|
|
SpvOpIAdd, 6, 47,
|
|
{{SPV_OPERAND_TYPE_ID, {45}}, {SPV_OPERAND_TYPE_ID, {46}}}));
|
|
instructions.push_back(MakeInstructionMessage(
|
|
SpvOpAccessChain, 23, 48,
|
|
{{SPV_OPERAND_TYPE_ID, {38}}, {SPV_OPERAND_TYPE_ID, {47}}}));
|
|
instructions.push_back(
|
|
MakeInstructionMessage(SpvOpLoad, 6, 49, {{SPV_OPERAND_TYPE_ID, {48}}}));
|
|
instructions.push_back(MakeInstructionMessage(SpvOpInBoundsAccessChain, 23,
|
|
50,
|
|
{{SPV_OPERAND_TYPE_ID, {20}},
|
|
{SPV_OPERAND_TYPE_ID, {43}},
|
|
{SPV_OPERAND_TYPE_ID, {44}}}));
|
|
instructions.push_back(
|
|
MakeInstructionMessage(SpvOpLoad, 6, 51, {{SPV_OPERAND_TYPE_ID, {50}}}));
|
|
instructions.push_back(MakeInstructionMessage(
|
|
SpvOpIAdd, 6, 52,
|
|
{{SPV_OPERAND_TYPE_ID, {51}}, {SPV_OPERAND_TYPE_ID, {49}}}));
|
|
instructions.push_back(MakeInstructionMessage(SpvOpAccessChain, 23, 53,
|
|
{{SPV_OPERAND_TYPE_ID, {20}},
|
|
{SPV_OPERAND_TYPE_ID, {43}},
|
|
{SPV_OPERAND_TYPE_ID, {44}}}));
|
|
instructions.push_back(MakeInstructionMessage(
|
|
SpvOpStore, 0, 0,
|
|
{{SPV_OPERAND_TYPE_ID, {53}}, {SPV_OPERAND_TYPE_ID, {52}}}));
|
|
instructions.push_back(
|
|
MakeInstructionMessage(SpvOpLoad, 15, 58, {{SPV_OPERAND_TYPE_ID, {10}}}));
|
|
instructions.push_back(
|
|
MakeInstructionMessage(SpvOpLoad, 6, 63, {{SPV_OPERAND_TYPE_ID, {11}}}));
|
|
instructions.push_back(MakeInstructionMessage(SpvOpAccessChain, 64, 65,
|
|
{{SPV_OPERAND_TYPE_ID, {62}},
|
|
{SPV_OPERAND_TYPE_ID, {21}},
|
|
{SPV_OPERAND_TYPE_ID, {63}}}));
|
|
instructions.push_back(MakeInstructionMessage(SpvOpAccessChain, 64, 101,
|
|
{{SPV_OPERAND_TYPE_ID, {62}},
|
|
{SPV_OPERAND_TYPE_ID, {45}},
|
|
{SPV_OPERAND_TYPE_ID, {46}}}));
|
|
instructions.push_back(
|
|
MakeInstructionMessage(SpvOpLoad, 54, 66, {{SPV_OPERAND_TYPE_ID, {65}}}));
|
|
instructions.push_back(MakeInstructionMessage(
|
|
SpvOpAccessChain, 64, 67,
|
|
{{SPV_OPERAND_TYPE_ID, {57}}, {SPV_OPERAND_TYPE_ID, {58}}}));
|
|
instructions.push_back(MakeInstructionMessage(
|
|
SpvOpStore, 0, 0,
|
|
{{SPV_OPERAND_TYPE_ID, {67}}, {SPV_OPERAND_TYPE_ID, {66}}}));
|
|
instructions.push_back(
|
|
MakeInstructionMessage(SpvOpLoad, 6, 68, {{SPV_OPERAND_TYPE_ID, {9}}}));
|
|
instructions.push_back(MakeInstructionMessage(
|
|
SpvOpInBoundsAccessChain, 64, 70,
|
|
{{SPV_OPERAND_TYPE_ID, {57}}, {SPV_OPERAND_TYPE_ID, {68}}}));
|
|
instructions.push_back(MakeInstructionMessage(
|
|
SpvOpStore, 0, 0,
|
|
{{SPV_OPERAND_TYPE_ID, {70}}, {SPV_OPERAND_TYPE_ID, {69}}}));
|
|
instructions.push_back(MakeInstructionMessage(SpvOpReturn, 0, 0, {}));
|
|
instructions.push_back(MakeInstructionMessage(SpvOpFunctionEnd, 0, 0, {}));
|
|
|
|
FactManager fact_manager;
|
|
|
|
const auto context1 = BuildModule(env, consumer, shader, kFuzzAssembleOption);
|
|
const auto context2 = BuildModule(env, consumer, shader, kFuzzAssembleOption);
|
|
ASSERT_TRUE(IsValid(env, context1.get()));
|
|
|
|
TransformationAddFunction add_dead_function(instructions);
|
|
ASSERT_TRUE(add_dead_function.IsApplicable(context1.get(), fact_manager));
|
|
add_dead_function.Apply(context1.get(), &fact_manager);
|
|
ASSERT_TRUE(IsValid(env, context1.get()));
|
|
|
|
std::string added_as_dead_code = R"(
|
|
OpCapability Shader
|
|
%1 = OpExtInstImport "GLSL.std.450"
|
|
OpMemoryModel Logical GLSL450
|
|
OpEntryPoint Fragment %4 "main"
|
|
OpExecutionMode %4 OriginUpperLeft
|
|
OpSource ESSL 310
|
|
%2 = OpTypeVoid
|
|
%100 = OpTypeBool
|
|
%3 = OpTypeFunction %2
|
|
%6 = OpTypeInt 32 1
|
|
%7 = OpTypePointer Function %6
|
|
%15 = OpTypeInt 32 0
|
|
%102 = OpTypePointer Function %15
|
|
%8 = OpTypeFunction %2 %7 %102 %7
|
|
%16 = OpConstant %15 5
|
|
%17 = OpTypeArray %6 %16
|
|
%18 = OpTypeArray %17 %16
|
|
%19 = OpTypePointer Private %18
|
|
%20 = OpVariable %19 Private
|
|
%21 = OpConstant %6 0
|
|
%23 = OpTypePointer Private %6
|
|
%26 = OpTypePointer Function %17
|
|
%29 = OpTypePointer Private %17
|
|
%33 = OpConstant %6 4
|
|
%200 = OpConstant %15 4
|
|
%35 = OpConstant %15 10
|
|
%36 = OpTypeArray %6 %35
|
|
%37 = OpTypePointer Private %36
|
|
%38 = OpVariable %37 Private
|
|
%54 = OpTypeFloat 32
|
|
%55 = OpTypeVector %54 4
|
|
%56 = OpTypePointer Private %55
|
|
%57 = OpVariable %56 Private
|
|
%59 = OpTypeVector %54 3
|
|
%60 = OpTypeMatrix %59 2
|
|
%61 = OpTypePointer Private %60
|
|
%62 = OpVariable %61 Private
|
|
%64 = OpTypePointer Private %54
|
|
%69 = OpConstant %54 2
|
|
%71 = OpConstant %6 1
|
|
%72 = OpConstant %6 2
|
|
%201 = OpConstant %15 2
|
|
%73 = OpConstant %6 3
|
|
%202 = OpConstant %15 3
|
|
%203 = OpConstant %6 1
|
|
%204 = OpConstant %6 9
|
|
%4 = OpFunction %2 None %3
|
|
%5 = OpLabel
|
|
OpReturn
|
|
OpFunctionEnd
|
|
%12 = OpFunction %2 None %8
|
|
%9 = OpFunctionParameter %7
|
|
%10 = OpFunctionParameter %102
|
|
%11 = OpFunctionParameter %7
|
|
%13 = OpLabel
|
|
%14 = OpVariable %7 Function
|
|
%27 = OpVariable %26 Function
|
|
%22 = OpLoad %6 %11
|
|
%24 = OpAccessChain %23 %20 %21 %22
|
|
%25 = OpLoad %6 %24
|
|
OpStore %14 %25
|
|
%28 = OpLoad %15 %10
|
|
%30 = OpAccessChain %29 %20 %28
|
|
%31 = OpLoad %17 %30
|
|
OpStore %27 %31
|
|
%32 = OpLoad %6 %9
|
|
%34 = OpInBoundsAccessChain %7 %27 %32
|
|
OpStore %34 %33
|
|
%39 = OpLoad %6 %9
|
|
%40 = OpAccessChain %23 %38 %33
|
|
%41 = OpLoad %6 %40
|
|
%42 = OpInBoundsAccessChain %23 %38 %39
|
|
OpStore %42 %41
|
|
%43 = OpLoad %15 %10
|
|
%44 = OpLoad %6 %11
|
|
%45 = OpLoad %6 %9
|
|
%46 = OpLoad %15 %10
|
|
%47 = OpIAdd %6 %45 %46
|
|
%48 = OpAccessChain %23 %38 %47
|
|
%49 = OpLoad %6 %48
|
|
%50 = OpInBoundsAccessChain %23 %20 %43 %44
|
|
%51 = OpLoad %6 %50
|
|
%52 = OpIAdd %6 %51 %49
|
|
%53 = OpAccessChain %23 %20 %43 %44
|
|
OpStore %53 %52
|
|
%58 = OpLoad %15 %10
|
|
%63 = OpLoad %6 %11
|
|
%65 = OpAccessChain %64 %62 %21 %63
|
|
%101 = OpAccessChain %64 %62 %45 %46
|
|
%66 = OpLoad %54 %65
|
|
%67 = OpAccessChain %64 %57 %58
|
|
OpStore %67 %66
|
|
%68 = OpLoad %6 %9
|
|
%70 = OpInBoundsAccessChain %64 %57 %68
|
|
OpStore %70 %69
|
|
OpReturn
|
|
OpFunctionEnd
|
|
)";
|
|
ASSERT_TRUE(IsEqual(env, added_as_dead_code, context1.get()));
|
|
|
|
std::vector<protobufs::AccessChainClampingInfo> access_chain_clamping_info;
|
|
access_chain_clamping_info.push_back(
|
|
MakeAccessClampingInfo(24, {{1001, 2001}, {1002, 2002}}));
|
|
access_chain_clamping_info.push_back(
|
|
MakeAccessClampingInfo(30, {{1003, 2003}}));
|
|
access_chain_clamping_info.push_back(
|
|
MakeAccessClampingInfo(34, {{1004, 2004}}));
|
|
access_chain_clamping_info.push_back(
|
|
MakeAccessClampingInfo(40, {{1005, 2005}}));
|
|
access_chain_clamping_info.push_back(
|
|
MakeAccessClampingInfo(42, {{1006, 2006}}));
|
|
access_chain_clamping_info.push_back(
|
|
MakeAccessClampingInfo(48, {{1007, 2007}}));
|
|
access_chain_clamping_info.push_back(
|
|
MakeAccessClampingInfo(50, {{1008, 2008}, {1009, 2009}}));
|
|
access_chain_clamping_info.push_back(
|
|
MakeAccessClampingInfo(53, {{1010, 2010}, {1011, 2011}}));
|
|
access_chain_clamping_info.push_back(
|
|
MakeAccessClampingInfo(65, {{1012, 2012}, {1013, 2013}}));
|
|
access_chain_clamping_info.push_back(
|
|
MakeAccessClampingInfo(101, {{1014, 2014}, {1015, 2015}}));
|
|
access_chain_clamping_info.push_back(
|
|
MakeAccessClampingInfo(67, {{1016, 2016}}));
|
|
access_chain_clamping_info.push_back(
|
|
MakeAccessClampingInfo(70, {{1017, 2017}}));
|
|
|
|
TransformationAddFunction add_livesafe_function(instructions, 0, 0, {}, 13,
|
|
access_chain_clamping_info);
|
|
ASSERT_TRUE(add_livesafe_function.IsApplicable(context2.get(), fact_manager));
|
|
add_livesafe_function.Apply(context2.get(), &fact_manager);
|
|
ASSERT_TRUE(IsValid(env, context2.get()));
|
|
std::string added_as_livesafe_code = R"(
|
|
OpCapability Shader
|
|
%1 = OpExtInstImport "GLSL.std.450"
|
|
OpMemoryModel Logical GLSL450
|
|
OpEntryPoint Fragment %4 "main"
|
|
OpExecutionMode %4 OriginUpperLeft
|
|
OpSource ESSL 310
|
|
%2 = OpTypeVoid
|
|
%100 = OpTypeBool
|
|
%3 = OpTypeFunction %2
|
|
%6 = OpTypeInt 32 1
|
|
%7 = OpTypePointer Function %6
|
|
%15 = OpTypeInt 32 0
|
|
%102 = OpTypePointer Function %15
|
|
%8 = OpTypeFunction %2 %7 %102 %7
|
|
%16 = OpConstant %15 5
|
|
%17 = OpTypeArray %6 %16
|
|
%18 = OpTypeArray %17 %16
|
|
%19 = OpTypePointer Private %18
|
|
%20 = OpVariable %19 Private
|
|
%21 = OpConstant %6 0
|
|
%23 = OpTypePointer Private %6
|
|
%26 = OpTypePointer Function %17
|
|
%29 = OpTypePointer Private %17
|
|
%33 = OpConstant %6 4
|
|
%200 = OpConstant %15 4
|
|
%35 = OpConstant %15 10
|
|
%36 = OpTypeArray %6 %35
|
|
%37 = OpTypePointer Private %36
|
|
%38 = OpVariable %37 Private
|
|
%54 = OpTypeFloat 32
|
|
%55 = OpTypeVector %54 4
|
|
%56 = OpTypePointer Private %55
|
|
%57 = OpVariable %56 Private
|
|
%59 = OpTypeVector %54 3
|
|
%60 = OpTypeMatrix %59 2
|
|
%61 = OpTypePointer Private %60
|
|
%62 = OpVariable %61 Private
|
|
%64 = OpTypePointer Private %54
|
|
%69 = OpConstant %54 2
|
|
%71 = OpConstant %6 1
|
|
%72 = OpConstant %6 2
|
|
%201 = OpConstant %15 2
|
|
%73 = OpConstant %6 3
|
|
%202 = OpConstant %15 3
|
|
%203 = OpConstant %6 1
|
|
%204 = OpConstant %6 9
|
|
%4 = OpFunction %2 None %3
|
|
%5 = OpLabel
|
|
OpReturn
|
|
OpFunctionEnd
|
|
%12 = OpFunction %2 None %8
|
|
%9 = OpFunctionParameter %7
|
|
%10 = OpFunctionParameter %102
|
|
%11 = OpFunctionParameter %7
|
|
%13 = OpLabel
|
|
%14 = OpVariable %7 Function
|
|
%27 = OpVariable %26 Function
|
|
%22 = OpLoad %6 %11
|
|
%1002 = OpULessThanEqual %100 %22 %33
|
|
%2002 = OpSelect %6 %1002 %22 %33
|
|
%24 = OpAccessChain %23 %20 %21 %2002
|
|
%25 = OpLoad %6 %24
|
|
OpStore %14 %25
|
|
%28 = OpLoad %15 %10
|
|
%1003 = OpULessThanEqual %100 %28 %200
|
|
%2003 = OpSelect %15 %1003 %28 %200
|
|
%30 = OpAccessChain %29 %20 %2003
|
|
%31 = OpLoad %17 %30
|
|
OpStore %27 %31
|
|
%32 = OpLoad %6 %9
|
|
%1004 = OpULessThanEqual %100 %32 %33
|
|
%2004 = OpSelect %6 %1004 %32 %33
|
|
%34 = OpInBoundsAccessChain %7 %27 %2004
|
|
OpStore %34 %33
|
|
%39 = OpLoad %6 %9
|
|
%40 = OpAccessChain %23 %38 %33
|
|
%41 = OpLoad %6 %40
|
|
%1006 = OpULessThanEqual %100 %39 %204
|
|
%2006 = OpSelect %6 %1006 %39 %204
|
|
%42 = OpInBoundsAccessChain %23 %38 %2006
|
|
OpStore %42 %41
|
|
%43 = OpLoad %15 %10
|
|
%44 = OpLoad %6 %11
|
|
%45 = OpLoad %6 %9
|
|
%46 = OpLoad %15 %10
|
|
%47 = OpIAdd %6 %45 %46
|
|
%1007 = OpULessThanEqual %100 %47 %204
|
|
%2007 = OpSelect %6 %1007 %47 %204
|
|
%48 = OpAccessChain %23 %38 %2007
|
|
%49 = OpLoad %6 %48
|
|
%1008 = OpULessThanEqual %100 %43 %200
|
|
%2008 = OpSelect %15 %1008 %43 %200
|
|
%1009 = OpULessThanEqual %100 %44 %33
|
|
%2009 = OpSelect %6 %1009 %44 %33
|
|
%50 = OpInBoundsAccessChain %23 %20 %2008 %2009
|
|
%51 = OpLoad %6 %50
|
|
%52 = OpIAdd %6 %51 %49
|
|
%1010 = OpULessThanEqual %100 %43 %200
|
|
%2010 = OpSelect %15 %1010 %43 %200
|
|
%1011 = OpULessThanEqual %100 %44 %33
|
|
%2011 = OpSelect %6 %1011 %44 %33
|
|
%53 = OpAccessChain %23 %20 %2010 %2011
|
|
OpStore %53 %52
|
|
%58 = OpLoad %15 %10
|
|
%63 = OpLoad %6 %11
|
|
%1013 = OpULessThanEqual %100 %63 %72
|
|
%2013 = OpSelect %6 %1013 %63 %72
|
|
%65 = OpAccessChain %64 %62 %21 %2013
|
|
%1014 = OpULessThanEqual %100 %45 %71
|
|
%2014 = OpSelect %6 %1014 %45 %71
|
|
%1015 = OpULessThanEqual %100 %46 %201
|
|
%2015 = OpSelect %15 %1015 %46 %201
|
|
%101 = OpAccessChain %64 %62 %2014 %2015
|
|
%66 = OpLoad %54 %65
|
|
%1016 = OpULessThanEqual %100 %58 %202
|
|
%2016 = OpSelect %15 %1016 %58 %202
|
|
%67 = OpAccessChain %64 %57 %2016
|
|
OpStore %67 %66
|
|
%68 = OpLoad %6 %9
|
|
%1017 = OpULessThanEqual %100 %68 %73
|
|
%2017 = OpSelect %6 %1017 %68 %73
|
|
%70 = OpInBoundsAccessChain %64 %57 %2017
|
|
OpStore %70 %69
|
|
OpReturn
|
|
OpFunctionEnd
|
|
)";
|
|
ASSERT_TRUE(IsEqual(env, added_as_livesafe_code, context2.get()));
|
|
}
|
|
|
|
TEST(TransformationAddFunctionTest, LivesafeCanCallLivesafe) {
|
|
std::string shader = R"(
|
|
OpCapability Shader
|
|
%1 = OpExtInstImport "GLSL.std.450"
|
|
OpMemoryModel Logical GLSL450
|
|
OpEntryPoint Fragment %4 "main"
|
|
OpExecutionMode %4 OriginUpperLeft
|
|
OpSource ESSL 310
|
|
%2 = OpTypeVoid
|
|
%3 = OpTypeFunction %2
|
|
%4 = OpFunction %2 None %3
|
|
%5 = OpLabel
|
|
OpReturn
|
|
OpFunctionEnd
|
|
%6 = OpFunction %2 None %3
|
|
%7 = OpLabel
|
|
OpReturn
|
|
OpFunctionEnd
|
|
)";
|
|
|
|
const auto env = SPV_ENV_UNIVERSAL_1_4;
|
|
const auto consumer = nullptr;
|
|
|
|
std::vector<protobufs::Instruction> instructions;
|
|
|
|
instructions.push_back(MakeInstructionMessage(
|
|
SpvOpFunction, 2, 8,
|
|
{{SPV_OPERAND_TYPE_FUNCTION_CONTROL, {SpvFunctionControlMaskNone}},
|
|
{SPV_OPERAND_TYPE_TYPE_ID, {3}}}));
|
|
instructions.push_back(MakeInstructionMessage(SpvOpLabel, 0, 9, {}));
|
|
instructions.push_back(MakeInstructionMessage(SpvOpFunctionCall, 2, 11,
|
|
{{SPV_OPERAND_TYPE_ID, {6}}}));
|
|
instructions.push_back(MakeInstructionMessage(SpvOpReturn, 0, 0, {}));
|
|
instructions.push_back(MakeInstructionMessage(SpvOpFunctionEnd, 0, 0, {}));
|
|
|
|
FactManager fact_manager1;
|
|
FactManager fact_manager2;
|
|
|
|
// Mark function 6 as livesafe.
|
|
fact_manager2.AddFactFunctionIsLivesafe(6);
|
|
|
|
const auto context1 = BuildModule(env, consumer, shader, kFuzzAssembleOption);
|
|
const auto context2 = BuildModule(env, consumer, shader, kFuzzAssembleOption);
|
|
ASSERT_TRUE(IsValid(env, context1.get()));
|
|
|
|
TransformationAddFunction add_dead_function(instructions);
|
|
ASSERT_TRUE(add_dead_function.IsApplicable(context1.get(), fact_manager1));
|
|
add_dead_function.Apply(context1.get(), &fact_manager1);
|
|
ASSERT_TRUE(IsValid(env, context1.get()));
|
|
|
|
std::string added_as_live_or_dead_code = R"(
|
|
OpCapability Shader
|
|
%1 = OpExtInstImport "GLSL.std.450"
|
|
OpMemoryModel Logical GLSL450
|
|
OpEntryPoint Fragment %4 "main"
|
|
OpExecutionMode %4 OriginUpperLeft
|
|
OpSource ESSL 310
|
|
%2 = OpTypeVoid
|
|
%3 = OpTypeFunction %2
|
|
%4 = OpFunction %2 None %3
|
|
%5 = OpLabel
|
|
OpReturn
|
|
OpFunctionEnd
|
|
%6 = OpFunction %2 None %3
|
|
%7 = OpLabel
|
|
OpReturn
|
|
OpFunctionEnd
|
|
%8 = OpFunction %2 None %3
|
|
%9 = OpLabel
|
|
%11 = OpFunctionCall %2 %6
|
|
OpReturn
|
|
OpFunctionEnd
|
|
)";
|
|
ASSERT_TRUE(IsEqual(env, added_as_live_or_dead_code, context1.get()));
|
|
|
|
TransformationAddFunction add_livesafe_function(instructions, 0, 0, {}, 0,
|
|
{});
|
|
ASSERT_TRUE(
|
|
add_livesafe_function.IsApplicable(context2.get(), fact_manager2));
|
|
add_livesafe_function.Apply(context2.get(), &fact_manager2);
|
|
ASSERT_TRUE(IsValid(env, context2.get()));
|
|
ASSERT_TRUE(IsEqual(env, added_as_live_or_dead_code, context2.get()));
|
|
}
|
|
|
|
TEST(TransformationAddFunctionTest, LivesafeOnlyCallsLivesafe) {
|
|
std::string shader = R"(
|
|
OpCapability Shader
|
|
%1 = OpExtInstImport "GLSL.std.450"
|
|
OpMemoryModel Logical GLSL450
|
|
OpEntryPoint Fragment %4 "main"
|
|
OpExecutionMode %4 OriginUpperLeft
|
|
OpSource ESSL 310
|
|
%2 = OpTypeVoid
|
|
%3 = OpTypeFunction %2
|
|
%4 = OpFunction %2 None %3
|
|
%5 = OpLabel
|
|
OpReturn
|
|
OpFunctionEnd
|
|
%6 = OpFunction %2 None %3
|
|
%7 = OpLabel
|
|
OpKill
|
|
OpFunctionEnd
|
|
)";
|
|
|
|
const auto env = SPV_ENV_UNIVERSAL_1_4;
|
|
const auto consumer = nullptr;
|
|
|
|
std::vector<protobufs::Instruction> instructions;
|
|
|
|
instructions.push_back(MakeInstructionMessage(
|
|
SpvOpFunction, 2, 8,
|
|
{{SPV_OPERAND_TYPE_FUNCTION_CONTROL, {SpvFunctionControlMaskNone}},
|
|
{SPV_OPERAND_TYPE_TYPE_ID, {3}}}));
|
|
instructions.push_back(MakeInstructionMessage(SpvOpLabel, 0, 9, {}));
|
|
instructions.push_back(MakeInstructionMessage(SpvOpFunctionCall, 2, 11,
|
|
{{SPV_OPERAND_TYPE_ID, {6}}}));
|
|
instructions.push_back(MakeInstructionMessage(SpvOpReturn, 0, 0, {}));
|
|
instructions.push_back(MakeInstructionMessage(SpvOpFunctionEnd, 0, 0, {}));
|
|
|
|
FactManager fact_manager;
|
|
|
|
const auto context1 = BuildModule(env, consumer, shader, kFuzzAssembleOption);
|
|
const auto context2 = BuildModule(env, consumer, shader, kFuzzAssembleOption);
|
|
ASSERT_TRUE(IsValid(env, context1.get()));
|
|
|
|
TransformationAddFunction add_dead_function(instructions);
|
|
ASSERT_TRUE(add_dead_function.IsApplicable(context1.get(), fact_manager));
|
|
add_dead_function.Apply(context1.get(), &fact_manager);
|
|
ASSERT_TRUE(IsValid(env, context1.get()));
|
|
|
|
std::string added_as_dead_code = R"(
|
|
OpCapability Shader
|
|
%1 = OpExtInstImport "GLSL.std.450"
|
|
OpMemoryModel Logical GLSL450
|
|
OpEntryPoint Fragment %4 "main"
|
|
OpExecutionMode %4 OriginUpperLeft
|
|
OpSource ESSL 310
|
|
%2 = OpTypeVoid
|
|
%3 = OpTypeFunction %2
|
|
%4 = OpFunction %2 None %3
|
|
%5 = OpLabel
|
|
OpReturn
|
|
OpFunctionEnd
|
|
%6 = OpFunction %2 None %3
|
|
%7 = OpLabel
|
|
OpKill
|
|
OpFunctionEnd
|
|
%8 = OpFunction %2 None %3
|
|
%9 = OpLabel
|
|
%11 = OpFunctionCall %2 %6
|
|
OpReturn
|
|
OpFunctionEnd
|
|
)";
|
|
ASSERT_TRUE(IsEqual(env, added_as_dead_code, context1.get()));
|
|
|
|
TransformationAddFunction add_livesafe_function(instructions, 0, 0, {}, 0,
|
|
{});
|
|
ASSERT_FALSE(
|
|
add_livesafe_function.IsApplicable(context2.get(), fact_manager));
|
|
}
|
|
|
|
TEST(TransformationAddFunctionTest,
|
|
LoopLimitersBackEdgeBlockEndsWithConditional1) {
|
|
std::string shader = R"(
|
|
OpCapability Shader
|
|
%1 = OpExtInstImport "GLSL.std.450"
|
|
OpMemoryModel Logical GLSL450
|
|
OpEntryPoint Fragment %4 "main"
|
|
OpExecutionMode %4 OriginUpperLeft
|
|
OpSource ESSL 310
|
|
%2 = OpTypeVoid
|
|
%3 = OpTypeFunction %2
|
|
%8 = OpTypeInt 32 1
|
|
%9 = OpTypePointer Function %8
|
|
%11 = OpConstant %8 0
|
|
%18 = OpConstant %8 10
|
|
%19 = OpTypeBool
|
|
%26 = OpConstantTrue %19
|
|
%27 = OpConstantFalse %19
|
|
%28 = OpTypeInt 32 0
|
|
%29 = OpTypePointer Function %28
|
|
%30 = OpConstant %28 0
|
|
%31 = OpConstant %28 1
|
|
%32 = OpConstant %28 5
|
|
%22 = OpConstant %8 1
|
|
%4 = OpFunction %2 None %3
|
|
%5 = OpLabel
|
|
OpReturn
|
|
OpFunctionEnd
|
|
)";
|
|
|
|
std::string donor = R"(
|
|
OpCapability Shader
|
|
%1 = OpExtInstImport "GLSL.std.450"
|
|
OpMemoryModel Logical GLSL450
|
|
OpEntryPoint Fragment %4 "main"
|
|
OpExecutionMode %4 OriginUpperLeft
|
|
OpSource ESSL 310
|
|
%2 = OpTypeVoid
|
|
%3 = OpTypeFunction %2
|
|
%8 = OpTypeInt 32 1
|
|
%9 = OpTypePointer Function %8
|
|
%11 = OpConstant %8 0
|
|
%18 = OpConstant %8 10
|
|
%19 = OpTypeBool
|
|
%26 = OpConstantTrue %19
|
|
%27 = OpConstantFalse %19
|
|
%28 = OpTypeInt 32 0
|
|
%29 = OpTypePointer Function %28
|
|
%30 = OpConstant %28 0
|
|
%31 = OpConstant %28 1
|
|
%32 = OpConstant %28 5
|
|
%22 = OpConstant %8 1
|
|
%4 = OpFunction %2 None %3
|
|
%5 = OpLabel
|
|
OpReturn
|
|
OpFunctionEnd
|
|
%6 = OpFunction %2 None %3
|
|
%7 = OpLabel
|
|
%10 = OpVariable %9 Function
|
|
OpStore %10 %11
|
|
OpBranch %12
|
|
%12 = OpLabel
|
|
OpLoopMerge %14 %15 None
|
|
OpBranch %15
|
|
%15 = OpLabel
|
|
%17 = OpLoad %8 %10
|
|
%20 = OpSLessThan %19 %17 %18
|
|
%21 = OpLoad %8 %10
|
|
%23 = OpIAdd %8 %21 %22
|
|
OpStore %10 %23
|
|
OpBranchConditional %20 %12 %14
|
|
%14 = OpLabel
|
|
OpReturn
|
|
OpFunctionEnd
|
|
)";
|
|
const auto env = SPV_ENV_UNIVERSAL_1_4;
|
|
const auto consumer = nullptr;
|
|
|
|
FactManager fact_manager;
|
|
|
|
const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
|
|
ASSERT_TRUE(IsValid(env, context.get()));
|
|
|
|
// Make a sequence of instruction messages corresponding to function %8 in
|
|
// |donor|.
|
|
std::vector<protobufs::Instruction> instructions =
|
|
GetInstructionsForFunction(env, consumer, donor, 6);
|
|
|
|
protobufs::LoopLimiterInfo loop_limiter_info;
|
|
loop_limiter_info.set_loop_header_id(12);
|
|
loop_limiter_info.set_load_id(102);
|
|
loop_limiter_info.set_increment_id(103);
|
|
loop_limiter_info.set_compare_id(104);
|
|
loop_limiter_info.set_logical_op_id(105);
|
|
TransformationAddFunction add_livesafe_function(instructions, 100, 32,
|
|
{loop_limiter_info}, 0, {});
|
|
ASSERT_TRUE(add_livesafe_function.IsApplicable(context.get(), fact_manager));
|
|
add_livesafe_function.Apply(context.get(), &fact_manager);
|
|
ASSERT_TRUE(IsValid(env, context.get()));
|
|
std::string expected = R"(
|
|
OpCapability Shader
|
|
%1 = OpExtInstImport "GLSL.std.450"
|
|
OpMemoryModel Logical GLSL450
|
|
OpEntryPoint Fragment %4 "main"
|
|
OpExecutionMode %4 OriginUpperLeft
|
|
OpSource ESSL 310
|
|
%2 = OpTypeVoid
|
|
%3 = OpTypeFunction %2
|
|
%8 = OpTypeInt 32 1
|
|
%9 = OpTypePointer Function %8
|
|
%11 = OpConstant %8 0
|
|
%18 = OpConstant %8 10
|
|
%19 = OpTypeBool
|
|
%26 = OpConstantTrue %19
|
|
%27 = OpConstantFalse %19
|
|
%28 = OpTypeInt 32 0
|
|
%29 = OpTypePointer Function %28
|
|
%30 = OpConstant %28 0
|
|
%31 = OpConstant %28 1
|
|
%32 = OpConstant %28 5
|
|
%22 = OpConstant %8 1
|
|
%4 = OpFunction %2 None %3
|
|
%5 = OpLabel
|
|
OpReturn
|
|
OpFunctionEnd
|
|
%6 = OpFunction %2 None %3
|
|
%7 = OpLabel
|
|
%100 = OpVariable %29 Function %30
|
|
%10 = OpVariable %9 Function
|
|
OpStore %10 %11
|
|
OpBranch %12
|
|
%12 = OpLabel
|
|
OpLoopMerge %14 %15 None
|
|
OpBranch %15
|
|
%15 = OpLabel
|
|
%17 = OpLoad %8 %10
|
|
%20 = OpSLessThan %19 %17 %18
|
|
%21 = OpLoad %8 %10
|
|
%23 = OpIAdd %8 %21 %22
|
|
OpStore %10 %23
|
|
%102 = OpLoad %28 %100
|
|
%103 = OpIAdd %28 %102 %31
|
|
OpStore %100 %103
|
|
%104 = OpULessThan %19 %102 %32
|
|
%105 = OpLogicalAnd %19 %20 %104
|
|
OpBranchConditional %105 %12 %14
|
|
%14 = OpLabel
|
|
OpReturn
|
|
OpFunctionEnd
|
|
)";
|
|
ASSERT_TRUE(IsEqual(env, expected, context.get()));
|
|
}
|
|
|
|
TEST(TransformationAddFunctionTest,
|
|
LoopLimitersBackEdgeBlockEndsWithConditional2) {
|
|
std::string shader = R"(
|
|
OpCapability Shader
|
|
%1 = OpExtInstImport "GLSL.std.450"
|
|
OpMemoryModel Logical GLSL450
|
|
OpEntryPoint Fragment %4 "main"
|
|
OpExecutionMode %4 OriginUpperLeft
|
|
OpSource ESSL 310
|
|
%2 = OpTypeVoid
|
|
%3 = OpTypeFunction %2
|
|
%8 = OpTypeInt 32 1
|
|
%9 = OpTypePointer Function %8
|
|
%11 = OpConstant %8 0
|
|
%18 = OpConstant %8 10
|
|
%19 = OpTypeBool
|
|
%26 = OpConstantTrue %19
|
|
%27 = OpConstantFalse %19
|
|
%28 = OpTypeInt 32 0
|
|
%29 = OpTypePointer Function %28
|
|
%30 = OpConstant %28 0
|
|
%31 = OpConstant %28 1
|
|
%32 = OpConstant %28 5
|
|
%22 = OpConstant %8 1
|
|
%4 = OpFunction %2 None %3
|
|
%5 = OpLabel
|
|
OpReturn
|
|
OpFunctionEnd
|
|
)";
|
|
|
|
std::string donor = R"(
|
|
OpCapability Shader
|
|
%1 = OpExtInstImport "GLSL.std.450"
|
|
OpMemoryModel Logical GLSL450
|
|
OpEntryPoint Fragment %4 "main"
|
|
OpExecutionMode %4 OriginUpperLeft
|
|
OpSource ESSL 310
|
|
%2 = OpTypeVoid
|
|
%3 = OpTypeFunction %2
|
|
%8 = OpTypeInt 32 1
|
|
%9 = OpTypePointer Function %8
|
|
%11 = OpConstant %8 0
|
|
%18 = OpConstant %8 10
|
|
%19 = OpTypeBool
|
|
%26 = OpConstantTrue %19
|
|
%27 = OpConstantFalse %19
|
|
%28 = OpTypeInt 32 0
|
|
%29 = OpTypePointer Function %28
|
|
%30 = OpConstant %28 0
|
|
%31 = OpConstant %28 1
|
|
%32 = OpConstant %28 5
|
|
%22 = OpConstant %8 1
|
|
%4 = OpFunction %2 None %3
|
|
%5 = OpLabel
|
|
OpReturn
|
|
OpFunctionEnd
|
|
%6 = OpFunction %2 None %3
|
|
%7 = OpLabel
|
|
%10 = OpVariable %9 Function
|
|
OpStore %10 %11
|
|
OpBranch %12
|
|
%12 = OpLabel
|
|
OpLoopMerge %14 %15 None
|
|
OpBranch %15
|
|
%15 = OpLabel
|
|
%17 = OpLoad %8 %10
|
|
%20 = OpSLessThan %19 %17 %18
|
|
%21 = OpLoad %8 %10
|
|
%23 = OpIAdd %8 %21 %22
|
|
OpStore %10 %23
|
|
%50 = OpLogicalNot %19 %20
|
|
OpBranchConditional %50 %14 %12
|
|
%14 = OpLabel
|
|
OpReturn
|
|
OpFunctionEnd
|
|
)";
|
|
const auto env = SPV_ENV_UNIVERSAL_1_4;
|
|
const auto consumer = nullptr;
|
|
|
|
FactManager fact_manager;
|
|
|
|
const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
|
|
ASSERT_TRUE(IsValid(env, context.get()));
|
|
|
|
// Make a sequence of instruction messages corresponding to function %8 in
|
|
// |donor|.
|
|
std::vector<protobufs::Instruction> instructions =
|
|
GetInstructionsForFunction(env, consumer, donor, 6);
|
|
|
|
protobufs::LoopLimiterInfo loop_limiter_info;
|
|
loop_limiter_info.set_loop_header_id(12);
|
|
loop_limiter_info.set_load_id(102);
|
|
loop_limiter_info.set_increment_id(103);
|
|
loop_limiter_info.set_compare_id(104);
|
|
loop_limiter_info.set_logical_op_id(105);
|
|
TransformationAddFunction add_livesafe_function(instructions, 100, 32,
|
|
{loop_limiter_info}, 0, {});
|
|
ASSERT_TRUE(add_livesafe_function.IsApplicable(context.get(), fact_manager));
|
|
add_livesafe_function.Apply(context.get(), &fact_manager);
|
|
ASSERT_TRUE(IsValid(env, context.get()));
|
|
std::string expected = R"(
|
|
OpCapability Shader
|
|
%1 = OpExtInstImport "GLSL.std.450"
|
|
OpMemoryModel Logical GLSL450
|
|
OpEntryPoint Fragment %4 "main"
|
|
OpExecutionMode %4 OriginUpperLeft
|
|
OpSource ESSL 310
|
|
%2 = OpTypeVoid
|
|
%3 = OpTypeFunction %2
|
|
%8 = OpTypeInt 32 1
|
|
%9 = OpTypePointer Function %8
|
|
%11 = OpConstant %8 0
|
|
%18 = OpConstant %8 10
|
|
%19 = OpTypeBool
|
|
%26 = OpConstantTrue %19
|
|
%27 = OpConstantFalse %19
|
|
%28 = OpTypeInt 32 0
|
|
%29 = OpTypePointer Function %28
|
|
%30 = OpConstant %28 0
|
|
%31 = OpConstant %28 1
|
|
%32 = OpConstant %28 5
|
|
%22 = OpConstant %8 1
|
|
%4 = OpFunction %2 None %3
|
|
%5 = OpLabel
|
|
OpReturn
|
|
OpFunctionEnd
|
|
%6 = OpFunction %2 None %3
|
|
%7 = OpLabel
|
|
%100 = OpVariable %29 Function %30
|
|
%10 = OpVariable %9 Function
|
|
OpStore %10 %11
|
|
OpBranch %12
|
|
%12 = OpLabel
|
|
OpLoopMerge %14 %15 None
|
|
OpBranch %15
|
|
%15 = OpLabel
|
|
%17 = OpLoad %8 %10
|
|
%20 = OpSLessThan %19 %17 %18
|
|
%21 = OpLoad %8 %10
|
|
%23 = OpIAdd %8 %21 %22
|
|
OpStore %10 %23
|
|
%50 = OpLogicalNot %19 %20
|
|
%102 = OpLoad %28 %100
|
|
%103 = OpIAdd %28 %102 %31
|
|
OpStore %100 %103
|
|
%104 = OpUGreaterThanEqual %19 %102 %32
|
|
%105 = OpLogicalOr %19 %50 %104
|
|
OpBranchConditional %105 %14 %12
|
|
%14 = OpLabel
|
|
OpReturn
|
|
OpFunctionEnd
|
|
)";
|
|
ASSERT_TRUE(IsEqual(env, expected, context.get()));
|
|
}
|
|
|
|
TEST(TransformationAddFunctionTest, LoopLimitersHeaderIsBackEdgeBlock) {
|
|
std::string shader = R"(
|
|
OpCapability Shader
|
|
%1 = OpExtInstImport "GLSL.std.450"
|
|
OpMemoryModel Logical GLSL450
|
|
OpEntryPoint Fragment %4 "main"
|
|
OpExecutionMode %4 OriginUpperLeft
|
|
OpSource ESSL 310
|
|
%2 = OpTypeVoid
|
|
%3 = OpTypeFunction %2
|
|
%8 = OpTypeInt 32 1
|
|
%9 = OpTypePointer Function %8
|
|
%11 = OpConstant %8 0
|
|
%18 = OpConstant %8 10
|
|
%19 = OpTypeBool
|
|
%26 = OpConstantTrue %19
|
|
%27 = OpConstantFalse %19
|
|
%28 = OpTypeInt 32 0
|
|
%29 = OpTypePointer Function %28
|
|
%30 = OpConstant %28 0
|
|
%31 = OpConstant %28 1
|
|
%32 = OpConstant %28 5
|
|
%22 = OpConstant %8 1
|
|
%4 = OpFunction %2 None %3
|
|
%5 = OpLabel
|
|
OpReturn
|
|
OpFunctionEnd
|
|
)";
|
|
|
|
std::string donor = R"(
|
|
OpCapability Shader
|
|
%1 = OpExtInstImport "GLSL.std.450"
|
|
OpMemoryModel Logical GLSL450
|
|
OpEntryPoint Fragment %4 "main"
|
|
OpExecutionMode %4 OriginUpperLeft
|
|
OpSource ESSL 310
|
|
%2 = OpTypeVoid
|
|
%3 = OpTypeFunction %2
|
|
%8 = OpTypeInt 32 1
|
|
%9 = OpTypePointer Function %8
|
|
%11 = OpConstant %8 0
|
|
%18 = OpConstant %8 10
|
|
%19 = OpTypeBool
|
|
%26 = OpConstantTrue %19
|
|
%27 = OpConstantFalse %19
|
|
%28 = OpTypeInt 32 0
|
|
%29 = OpTypePointer Function %28
|
|
%30 = OpConstant %28 0
|
|
%31 = OpConstant %28 1
|
|
%32 = OpConstant %28 5
|
|
%22 = OpConstant %8 1
|
|
%4 = OpFunction %2 None %3
|
|
%5 = OpLabel
|
|
OpReturn
|
|
OpFunctionEnd
|
|
%6 = OpFunction %2 None %3
|
|
%7 = OpLabel
|
|
%10 = OpVariable %9 Function
|
|
OpStore %10 %11
|
|
OpBranch %12
|
|
%12 = OpLabel
|
|
%17 = OpLoad %8 %10
|
|
%20 = OpSLessThan %19 %17 %18
|
|
%21 = OpLoad %8 %10
|
|
%23 = OpIAdd %8 %21 %22
|
|
OpStore %10 %23
|
|
%50 = OpLogicalNot %19 %20
|
|
OpLoopMerge %14 %12 None
|
|
OpBranchConditional %50 %14 %12
|
|
%14 = OpLabel
|
|
OpReturn
|
|
OpFunctionEnd
|
|
)";
|
|
const auto env = SPV_ENV_UNIVERSAL_1_4;
|
|
const auto consumer = nullptr;
|
|
|
|
FactManager fact_manager;
|
|
|
|
const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
|
|
ASSERT_TRUE(IsValid(env, context.get()));
|
|
|
|
// Make a sequence of instruction messages corresponding to function %8 in
|
|
// |donor|.
|
|
std::vector<protobufs::Instruction> instructions =
|
|
GetInstructionsForFunction(env, consumer, donor, 6);
|
|
|
|
protobufs::LoopLimiterInfo loop_limiter_info;
|
|
loop_limiter_info.set_loop_header_id(12);
|
|
loop_limiter_info.set_load_id(102);
|
|
loop_limiter_info.set_increment_id(103);
|
|
loop_limiter_info.set_compare_id(104);
|
|
loop_limiter_info.set_logical_op_id(105);
|
|
TransformationAddFunction add_livesafe_function(instructions, 100, 32,
|
|
{loop_limiter_info}, 0, {});
|
|
ASSERT_TRUE(add_livesafe_function.IsApplicable(context.get(), fact_manager));
|
|
add_livesafe_function.Apply(context.get(), &fact_manager);
|
|
ASSERT_TRUE(IsValid(env, context.get()));
|
|
std::string expected = R"(
|
|
OpCapability Shader
|
|
%1 = OpExtInstImport "GLSL.std.450"
|
|
OpMemoryModel Logical GLSL450
|
|
OpEntryPoint Fragment %4 "main"
|
|
OpExecutionMode %4 OriginUpperLeft
|
|
OpSource ESSL 310
|
|
%2 = OpTypeVoid
|
|
%3 = OpTypeFunction %2
|
|
%8 = OpTypeInt 32 1
|
|
%9 = OpTypePointer Function %8
|
|
%11 = OpConstant %8 0
|
|
%18 = OpConstant %8 10
|
|
%19 = OpTypeBool
|
|
%26 = OpConstantTrue %19
|
|
%27 = OpConstantFalse %19
|
|
%28 = OpTypeInt 32 0
|
|
%29 = OpTypePointer Function %28
|
|
%30 = OpConstant %28 0
|
|
%31 = OpConstant %28 1
|
|
%32 = OpConstant %28 5
|
|
%22 = OpConstant %8 1
|
|
%4 = OpFunction %2 None %3
|
|
%5 = OpLabel
|
|
OpReturn
|
|
OpFunctionEnd
|
|
%6 = OpFunction %2 None %3
|
|
%7 = OpLabel
|
|
%100 = OpVariable %29 Function %30
|
|
%10 = OpVariable %9 Function
|
|
OpStore %10 %11
|
|
OpBranch %12
|
|
%12 = OpLabel
|
|
%17 = OpLoad %8 %10
|
|
%20 = OpSLessThan %19 %17 %18
|
|
%21 = OpLoad %8 %10
|
|
%23 = OpIAdd %8 %21 %22
|
|
OpStore %10 %23
|
|
%50 = OpLogicalNot %19 %20
|
|
%102 = OpLoad %28 %100
|
|
%103 = OpIAdd %28 %102 %31
|
|
OpStore %100 %103
|
|
%104 = OpUGreaterThanEqual %19 %102 %32
|
|
%105 = OpLogicalOr %19 %50 %104
|
|
OpLoopMerge %14 %12 None
|
|
OpBranchConditional %105 %14 %12
|
|
%14 = OpLabel
|
|
OpReturn
|
|
OpFunctionEnd
|
|
)";
|
|
ASSERT_TRUE(IsEqual(env, expected, context.get()));
|
|
}
|
|
|
|
TEST(TransformationAddFunctionTest, InfiniteLoop1) {
|
|
std::string shader = R"(
|
|
OpCapability Shader
|
|
%1 = OpExtInstImport "GLSL.std.450"
|
|
OpMemoryModel Logical GLSL450
|
|
OpEntryPoint Fragment %4 "main"
|
|
OpExecutionMode %4 OriginUpperLeft
|
|
OpSource ESSL 310
|
|
%2 = OpTypeVoid
|
|
%3 = OpTypeFunction %2
|
|
%8 = OpTypeInt 32 1
|
|
%9 = OpTypePointer Function %8
|
|
%11 = OpConstant %8 0
|
|
%18 = OpConstant %8 10
|
|
%19 = OpTypeBool
|
|
%26 = OpConstantTrue %19
|
|
%27 = OpConstantFalse %19
|
|
%28 = OpTypeInt 32 0
|
|
%29 = OpTypePointer Function %28
|
|
%30 = OpConstant %28 0
|
|
%31 = OpConstant %28 1
|
|
%32 = OpConstant %28 5
|
|
%22 = OpConstant %8 1
|
|
%4 = OpFunction %2 None %3
|
|
%5 = OpLabel
|
|
OpReturn
|
|
OpFunctionEnd
|
|
)";
|
|
|
|
std::string donor = R"(
|
|
OpCapability Shader
|
|
%1 = OpExtInstImport "GLSL.std.450"
|
|
OpMemoryModel Logical GLSL450
|
|
OpEntryPoint Fragment %4 "main"
|
|
OpExecutionMode %4 OriginUpperLeft
|
|
OpSource ESSL 310
|
|
%2 = OpTypeVoid
|
|
%3 = OpTypeFunction %2
|
|
%8 = OpTypeInt 32 1
|
|
%9 = OpTypePointer Function %8
|
|
%11 = OpConstant %8 0
|
|
%18 = OpConstant %8 10
|
|
%19 = OpTypeBool
|
|
%26 = OpConstantTrue %19
|
|
%27 = OpConstantFalse %19
|
|
%28 = OpTypeInt 32 0
|
|
%29 = OpTypePointer Function %28
|
|
%30 = OpConstant %28 0
|
|
%31 = OpConstant %28 1
|
|
%32 = OpConstant %28 5
|
|
%22 = OpConstant %8 1
|
|
%4 = OpFunction %2 None %3
|
|
%5 = OpLabel
|
|
OpReturn
|
|
OpFunctionEnd
|
|
%6 = OpFunction %2 None %3
|
|
%7 = OpLabel
|
|
%10 = OpVariable %9 Function
|
|
OpStore %10 %11
|
|
OpBranch %12
|
|
%12 = OpLabel
|
|
OpLoopMerge %14 %12 None
|
|
OpBranch %12
|
|
%14 = OpLabel
|
|
OpReturn
|
|
OpFunctionEnd
|
|
)";
|
|
const auto env = SPV_ENV_UNIVERSAL_1_4;
|
|
const auto consumer = nullptr;
|
|
|
|
FactManager fact_manager;
|
|
|
|
const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
|
|
ASSERT_TRUE(IsValid(env, context.get()));
|
|
|
|
// Make a sequence of instruction messages corresponding to function %8 in
|
|
// |donor|.
|
|
std::vector<protobufs::Instruction> instructions =
|
|
GetInstructionsForFunction(env, consumer, donor, 6);
|
|
|
|
protobufs::LoopLimiterInfo loop_limiter_info;
|
|
loop_limiter_info.set_loop_header_id(12);
|
|
loop_limiter_info.set_load_id(102);
|
|
loop_limiter_info.set_increment_id(103);
|
|
loop_limiter_info.set_compare_id(104);
|
|
loop_limiter_info.set_logical_op_id(105);
|
|
TransformationAddFunction add_livesafe_function(instructions, 100, 32,
|
|
{loop_limiter_info}, 0, {});
|
|
ASSERT_TRUE(add_livesafe_function.IsApplicable(context.get(), fact_manager));
|
|
add_livesafe_function.Apply(context.get(), &fact_manager);
|
|
ASSERT_TRUE(IsValid(env, context.get()));
|
|
std::string expected = R"(
|
|
OpCapability Shader
|
|
%1 = OpExtInstImport "GLSL.std.450"
|
|
OpMemoryModel Logical GLSL450
|
|
OpEntryPoint Fragment %4 "main"
|
|
OpExecutionMode %4 OriginUpperLeft
|
|
OpSource ESSL 310
|
|
%2 = OpTypeVoid
|
|
%3 = OpTypeFunction %2
|
|
%8 = OpTypeInt 32 1
|
|
%9 = OpTypePointer Function %8
|
|
%11 = OpConstant %8 0
|
|
%18 = OpConstant %8 10
|
|
%19 = OpTypeBool
|
|
%26 = OpConstantTrue %19
|
|
%27 = OpConstantFalse %19
|
|
%28 = OpTypeInt 32 0
|
|
%29 = OpTypePointer Function %28
|
|
%30 = OpConstant %28 0
|
|
%31 = OpConstant %28 1
|
|
%32 = OpConstant %28 5
|
|
%22 = OpConstant %8 1
|
|
%4 = OpFunction %2 None %3
|
|
%5 = OpLabel
|
|
OpReturn
|
|
OpFunctionEnd
|
|
%6 = OpFunction %2 None %3
|
|
%7 = OpLabel
|
|
%100 = OpVariable %29 Function %30
|
|
%10 = OpVariable %9 Function
|
|
OpStore %10 %11
|
|
OpBranch %12
|
|
%12 = OpLabel
|
|
%102 = OpLoad %28 %100
|
|
%103 = OpIAdd %28 %102 %31
|
|
OpStore %100 %103
|
|
%104 = OpUGreaterThanEqual %19 %102 %32
|
|
OpLoopMerge %14 %12 None
|
|
OpBranchConditional %104 %14 %12
|
|
%14 = OpLabel
|
|
OpReturn
|
|
OpFunctionEnd
|
|
)";
|
|
ASSERT_TRUE(IsEqual(env, expected, context.get()));
|
|
}
|
|
|
|
TEST(TransformationAddFunctionTest, UnreachableContinueConstruct) {
|
|
// This captures the case where the loop's continue construct is statically
|
|
// unreachable. In this case the loop cannot iterate and so we do not add
|
|
// a loop limiter. (The reason we do not just add one anyway is that
|
|
// detecting which block would be the back-edge block is difficult in the
|
|
// absence of reliable dominance information.)
|
|
std::string shader = R"(
|
|
OpCapability Shader
|
|
%1 = OpExtInstImport "GLSL.std.450"
|
|
OpMemoryModel Logical GLSL450
|
|
OpEntryPoint Fragment %4 "main"
|
|
OpExecutionMode %4 OriginUpperLeft
|
|
OpSource ESSL 310
|
|
%2 = OpTypeVoid
|
|
%3 = OpTypeFunction %2
|
|
%8 = OpTypeInt 32 1
|
|
%9 = OpTypePointer Function %8
|
|
%11 = OpConstant %8 0
|
|
%18 = OpConstant %8 10
|
|
%19 = OpTypeBool
|
|
%23 = OpConstant %8 1
|
|
%26 = OpConstantTrue %19
|
|
%27 = OpConstantFalse %19
|
|
%28 = OpTypeInt 32 0
|
|
%29 = OpTypePointer Function %28
|
|
%30 = OpConstant %28 0
|
|
%31 = OpConstant %28 1
|
|
%32 = OpConstant %28 5
|
|
%4 = OpFunction %2 None %3
|
|
%5 = OpLabel
|
|
OpReturn
|
|
OpFunctionEnd
|
|
)";
|
|
|
|
std::string donor = R"(
|
|
OpCapability Shader
|
|
%1 = OpExtInstImport "GLSL.std.450"
|
|
OpMemoryModel Logical GLSL450
|
|
OpEntryPoint Fragment %4 "main"
|
|
OpExecutionMode %4 OriginUpperLeft
|
|
OpSource ESSL 310
|
|
%2 = OpTypeVoid
|
|
%3 = OpTypeFunction %2
|
|
%8 = OpTypeInt 32 1
|
|
%9 = OpTypePointer Function %8
|
|
%11 = OpConstant %8 0
|
|
%18 = OpConstant %8 10
|
|
%19 = OpTypeBool
|
|
%23 = OpConstant %8 1
|
|
%26 = OpConstantTrue %19
|
|
%27 = OpConstantFalse %19
|
|
%28 = OpTypeInt 32 0
|
|
%29 = OpTypePointer Function %28
|
|
%30 = OpConstant %28 0
|
|
%31 = OpConstant %28 1
|
|
%32 = OpConstant %28 5
|
|
%4 = OpFunction %2 None %3
|
|
%5 = OpLabel
|
|
OpReturn
|
|
OpFunctionEnd
|
|
%6 = OpFunction %2 None %3
|
|
%7 = OpLabel
|
|
%10 = OpVariable %9 Function
|
|
OpStore %10 %11
|
|
OpBranch %12
|
|
%12 = OpLabel
|
|
OpLoopMerge %14 %15 None
|
|
OpBranch %16
|
|
%16 = OpLabel
|
|
%17 = OpLoad %8 %10
|
|
%20 = OpSLessThan %19 %17 %18
|
|
OpBranchConditional %20 %13 %14
|
|
%13 = OpLabel
|
|
OpBranch %14
|
|
%15 = OpLabel
|
|
%22 = OpLoad %8 %10
|
|
%24 = OpIAdd %8 %22 %23
|
|
OpStore %10 %24
|
|
OpBranch %12
|
|
%14 = OpLabel
|
|
OpReturn
|
|
OpFunctionEnd
|
|
)";
|
|
|
|
const auto env = SPV_ENV_UNIVERSAL_1_4;
|
|
const auto consumer = nullptr;
|
|
|
|
FactManager fact_manager;
|
|
|
|
const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
|
|
ASSERT_TRUE(IsValid(env, context.get()));
|
|
|
|
// Make a sequence of instruction messages corresponding to function %8 in
|
|
// |donor|.
|
|
std::vector<protobufs::Instruction> instructions =
|
|
GetInstructionsForFunction(env, consumer, donor, 6);
|
|
|
|
protobufs::LoopLimiterInfo loop_limiter_info;
|
|
loop_limiter_info.set_loop_header_id(12);
|
|
loop_limiter_info.set_load_id(102);
|
|
loop_limiter_info.set_increment_id(103);
|
|
loop_limiter_info.set_compare_id(104);
|
|
loop_limiter_info.set_logical_op_id(105);
|
|
TransformationAddFunction add_livesafe_function(instructions, 100, 32,
|
|
{loop_limiter_info}, 0, {});
|
|
ASSERT_TRUE(add_livesafe_function.IsApplicable(context.get(), fact_manager));
|
|
add_livesafe_function.Apply(context.get(), &fact_manager);
|
|
ASSERT_TRUE(IsValid(env, context.get()));
|
|
std::string expected = R"(
|
|
OpCapability Shader
|
|
%1 = OpExtInstImport "GLSL.std.450"
|
|
OpMemoryModel Logical GLSL450
|
|
OpEntryPoint Fragment %4 "main"
|
|
OpExecutionMode %4 OriginUpperLeft
|
|
OpSource ESSL 310
|
|
%2 = OpTypeVoid
|
|
%3 = OpTypeFunction %2
|
|
%8 = OpTypeInt 32 1
|
|
%9 = OpTypePointer Function %8
|
|
%11 = OpConstant %8 0
|
|
%18 = OpConstant %8 10
|
|
%19 = OpTypeBool
|
|
%23 = OpConstant %8 1
|
|
%26 = OpConstantTrue %19
|
|
%27 = OpConstantFalse %19
|
|
%28 = OpTypeInt 32 0
|
|
%29 = OpTypePointer Function %28
|
|
%30 = OpConstant %28 0
|
|
%31 = OpConstant %28 1
|
|
%32 = OpConstant %28 5
|
|
%4 = OpFunction %2 None %3
|
|
%5 = OpLabel
|
|
OpReturn
|
|
OpFunctionEnd
|
|
%6 = OpFunction %2 None %3
|
|
%7 = OpLabel
|
|
%100 = OpVariable %29 Function %30
|
|
%10 = OpVariable %9 Function
|
|
OpStore %10 %11
|
|
OpBranch %12
|
|
%12 = OpLabel
|
|
OpLoopMerge %14 %15 None
|
|
OpBranch %16
|
|
%16 = OpLabel
|
|
%17 = OpLoad %8 %10
|
|
%20 = OpSLessThan %19 %17 %18
|
|
OpBranchConditional %20 %13 %14
|
|
%13 = OpLabel
|
|
OpBranch %14
|
|
%15 = OpLabel
|
|
%22 = OpLoad %8 %10
|
|
%24 = OpIAdd %8 %22 %23
|
|
OpStore %10 %24
|
|
OpBranch %12
|
|
%14 = OpLabel
|
|
OpReturn
|
|
OpFunctionEnd
|
|
)";
|
|
ASSERT_TRUE(IsEqual(env, expected, context.get()));
|
|
}
|
|
|
|
TEST(TransformationAddFunctionTest, LoopLimitersAndOpPhi1) {
|
|
// This captures the scenario where breaking a loop due to a loop limiter
|
|
// requires patching up OpPhi instructions occurring at the loop merge block.
|
|
|
|
std::string shader = R"(
|
|
OpCapability Shader
|
|
%1 = OpExtInstImport "GLSL.std.450"
|
|
OpMemoryModel Logical GLSL450
|
|
OpEntryPoint Fragment %4 "main"
|
|
OpExecutionMode %4 OriginUpperLeft
|
|
OpSource ESSL 310
|
|
%2 = OpTypeVoid
|
|
%3 = OpTypeFunction %2
|
|
%6 = OpTypeInt 32 1
|
|
%50 = OpTypeInt 32 0
|
|
%51 = OpConstant %50 0
|
|
%52 = OpConstant %50 1
|
|
%53 = OpTypePointer Function %50
|
|
%7 = OpTypeFunction %6
|
|
%10 = OpTypePointer Function %6
|
|
%12 = OpConstant %6 0
|
|
%19 = OpConstant %6 100
|
|
%20 = OpTypeBool
|
|
%23 = OpConstant %6 20
|
|
%28 = OpConstant %6 1
|
|
%4 = OpFunction %2 None %3
|
|
%5 = OpLabel
|
|
OpReturn
|
|
OpFunctionEnd
|
|
)";
|
|
|
|
std::string donor = R"(
|
|
OpCapability Shader
|
|
%1 = OpExtInstImport "GLSL.std.450"
|
|
OpMemoryModel Logical GLSL450
|
|
OpEntryPoint Fragment %4 "main"
|
|
OpExecutionMode %4 OriginUpperLeft
|
|
OpSource ESSL 310
|
|
%2 = OpTypeVoid
|
|
%3 = OpTypeFunction %2
|
|
%6 = OpTypeInt 32 1
|
|
%7 = OpTypeFunction %6
|
|
%10 = OpTypePointer Function %6
|
|
%12 = OpConstant %6 0
|
|
%19 = OpConstant %6 100
|
|
%20 = OpTypeBool
|
|
%23 = OpConstant %6 20
|
|
%28 = OpConstant %6 1
|
|
%4 = OpFunction %2 None %3
|
|
%5 = OpLabel
|
|
%36 = OpFunctionCall %6 %8
|
|
OpReturn
|
|
OpFunctionEnd
|
|
%8 = OpFunction %6 None %7
|
|
%9 = OpLabel
|
|
%11 = OpVariable %10 Function
|
|
OpStore %11 %12
|
|
OpBranch %13
|
|
%13 = OpLabel
|
|
%37 = OpPhi %6 %12 %9 %32 %16
|
|
OpLoopMerge %15 %16 None
|
|
OpBranch %17
|
|
%17 = OpLabel
|
|
%21 = OpSLessThan %20 %37 %19
|
|
OpBranchConditional %21 %14 %15
|
|
%14 = OpLabel
|
|
%24 = OpSGreaterThan %20 %37 %23
|
|
OpSelectionMerge %26 None
|
|
OpBranchConditional %24 %25 %26
|
|
%25 = OpLabel
|
|
%29 = OpIAdd %6 %37 %28
|
|
OpStore %11 %29
|
|
OpBranch %15
|
|
%26 = OpLabel
|
|
OpBranch %16
|
|
%16 = OpLabel
|
|
%32 = OpIAdd %6 %37 %28
|
|
OpStore %11 %32
|
|
OpBranch %13
|
|
%15 = OpLabel
|
|
%38 = OpPhi %6 %37 %17 %29 %25
|
|
OpReturnValue %38
|
|
OpFunctionEnd
|
|
)";
|
|
|
|
const auto env = SPV_ENV_UNIVERSAL_1_4;
|
|
const auto consumer = nullptr;
|
|
|
|
FactManager fact_manager;
|
|
|
|
const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
|
|
ASSERT_TRUE(IsValid(env, context.get()));
|
|
|
|
// Make a sequence of instruction messages corresponding to function %8 in
|
|
// |donor|.
|
|
std::vector<protobufs::Instruction> instructions =
|
|
GetInstructionsForFunction(env, consumer, donor, 8);
|
|
|
|
protobufs::LoopLimiterInfo loop_limiter_info;
|
|
loop_limiter_info.set_loop_header_id(13);
|
|
loop_limiter_info.set_load_id(102);
|
|
loop_limiter_info.set_increment_id(103);
|
|
loop_limiter_info.set_compare_id(104);
|
|
loop_limiter_info.set_logical_op_id(105);
|
|
|
|
TransformationAddFunction no_op_phi_data(instructions, 100, 28,
|
|
{loop_limiter_info}, 0, {});
|
|
// The loop limiter info is not good enough; it does not include ids to patch
|
|
// up the OpPhi at the loop merge.
|
|
ASSERT_FALSE(no_op_phi_data.IsApplicable(context.get(), fact_manager));
|
|
|
|
// Add a phi id for the new edge from the loop back edge block to the loop
|
|
// merge.
|
|
loop_limiter_info.add_phi_id(28);
|
|
TransformationAddFunction with_op_phi_data(instructions, 100, 28,
|
|
{loop_limiter_info}, 0, {});
|
|
ASSERT_TRUE(with_op_phi_data.IsApplicable(context.get(), fact_manager));
|
|
with_op_phi_data.Apply(context.get(), &fact_manager);
|
|
ASSERT_TRUE(IsValid(env, context.get()));
|
|
std::string expected = R"(
|
|
OpCapability Shader
|
|
%1 = OpExtInstImport "GLSL.std.450"
|
|
OpMemoryModel Logical GLSL450
|
|
OpEntryPoint Fragment %4 "main"
|
|
OpExecutionMode %4 OriginUpperLeft
|
|
OpSource ESSL 310
|
|
%2 = OpTypeVoid
|
|
%3 = OpTypeFunction %2
|
|
%6 = OpTypeInt 32 1
|
|
%50 = OpTypeInt 32 0
|
|
%51 = OpConstant %50 0
|
|
%52 = OpConstant %50 1
|
|
%53 = OpTypePointer Function %50
|
|
%7 = OpTypeFunction %6
|
|
%10 = OpTypePointer Function %6
|
|
%12 = OpConstant %6 0
|
|
%19 = OpConstant %6 100
|
|
%20 = OpTypeBool
|
|
%23 = OpConstant %6 20
|
|
%28 = OpConstant %6 1
|
|
%4 = OpFunction %2 None %3
|
|
%5 = OpLabel
|
|
OpReturn
|
|
OpFunctionEnd
|
|
%8 = OpFunction %6 None %7
|
|
%9 = OpLabel
|
|
%100 = OpVariable %53 Function %51
|
|
%11 = OpVariable %10 Function
|
|
OpStore %11 %12
|
|
OpBranch %13
|
|
%13 = OpLabel
|
|
%37 = OpPhi %6 %12 %9 %32 %16
|
|
OpLoopMerge %15 %16 None
|
|
OpBranch %17
|
|
%17 = OpLabel
|
|
%21 = OpSLessThan %20 %37 %19
|
|
OpBranchConditional %21 %14 %15
|
|
%14 = OpLabel
|
|
%24 = OpSGreaterThan %20 %37 %23
|
|
OpSelectionMerge %26 None
|
|
OpBranchConditional %24 %25 %26
|
|
%25 = OpLabel
|
|
%29 = OpIAdd %6 %37 %28
|
|
OpStore %11 %29
|
|
OpBranch %15
|
|
%26 = OpLabel
|
|
OpBranch %16
|
|
%16 = OpLabel
|
|
%32 = OpIAdd %6 %37 %28
|
|
OpStore %11 %32
|
|
%102 = OpLoad %50 %100
|
|
%103 = OpIAdd %50 %102 %52
|
|
OpStore %100 %103
|
|
%104 = OpUGreaterThanEqual %20 %102 %28
|
|
OpBranchConditional %104 %15 %13
|
|
%15 = OpLabel
|
|
%38 = OpPhi %6 %37 %17 %29 %25 %28 %16
|
|
OpReturnValue %38
|
|
OpFunctionEnd
|
|
)";
|
|
ASSERT_TRUE(IsEqual(env, expected, context.get()));
|
|
}
|
|
|
|
TEST(TransformationAddFunctionTest, LoopLimitersAndOpPhi2) {
|
|
// This captures the scenario where the loop merge block already has an OpPhi
|
|
// with the loop back edge block as a predecessor.
|
|
|
|
std::string shader = R"(
|
|
OpCapability Shader
|
|
%1 = OpExtInstImport "GLSL.std.450"
|
|
OpMemoryModel Logical GLSL450
|
|
OpEntryPoint Fragment %4 "main"
|
|
OpExecutionMode %4 OriginUpperLeft
|
|
OpSource ESSL 310
|
|
%2 = OpTypeVoid
|
|
%3 = OpTypeFunction %2
|
|
%6 = OpTypeInt 32 1
|
|
%50 = OpTypeInt 32 0
|
|
%51 = OpConstant %50 0
|
|
%52 = OpConstant %50 1
|
|
%53 = OpTypePointer Function %50
|
|
%7 = OpTypeFunction %6
|
|
%10 = OpTypePointer Function %6
|
|
%12 = OpConstant %6 0
|
|
%19 = OpConstant %6 100
|
|
%20 = OpTypeBool
|
|
%60 = OpConstantTrue %20
|
|
%23 = OpConstant %6 20
|
|
%28 = OpConstant %6 1
|
|
%4 = OpFunction %2 None %3
|
|
%5 = OpLabel
|
|
OpReturn
|
|
OpFunctionEnd
|
|
)";
|
|
|
|
std::string donor = R"(
|
|
OpCapability Shader
|
|
%1 = OpExtInstImport "GLSL.std.450"
|
|
OpMemoryModel Logical GLSL450
|
|
OpEntryPoint Fragment %4 "main"
|
|
OpExecutionMode %4 OriginUpperLeft
|
|
OpSource ESSL 310
|
|
%2 = OpTypeVoid
|
|
%3 = OpTypeFunction %2
|
|
%6 = OpTypeInt 32 1
|
|
%50 = OpTypeInt 32 0
|
|
%51 = OpConstant %50 0
|
|
%52 = OpConstant %50 1
|
|
%53 = OpTypePointer Function %50
|
|
%7 = OpTypeFunction %6
|
|
%10 = OpTypePointer Function %6
|
|
%12 = OpConstant %6 0
|
|
%19 = OpConstant %6 100
|
|
%20 = OpTypeBool
|
|
%60 = OpConstantTrue %20
|
|
%23 = OpConstant %6 20
|
|
%28 = OpConstant %6 1
|
|
%4 = OpFunction %2 None %3
|
|
%5 = OpLabel
|
|
OpReturn
|
|
OpFunctionEnd
|
|
%8 = OpFunction %6 None %7
|
|
%9 = OpLabel
|
|
%11 = OpVariable %10 Function
|
|
OpStore %11 %12
|
|
OpBranch %13
|
|
%13 = OpLabel
|
|
%37 = OpPhi %6 %12 %9 %32 %16
|
|
OpLoopMerge %15 %16 None
|
|
OpBranch %17
|
|
%17 = OpLabel
|
|
%21 = OpSLessThan %20 %37 %19
|
|
OpBranchConditional %21 %14 %15
|
|
%14 = OpLabel
|
|
%24 = OpSGreaterThan %20 %37 %23
|
|
OpSelectionMerge %26 None
|
|
OpBranchConditional %24 %25 %26
|
|
%25 = OpLabel
|
|
%29 = OpIAdd %6 %37 %28
|
|
OpStore %11 %29
|
|
OpBranch %15
|
|
%26 = OpLabel
|
|
OpBranch %16
|
|
%16 = OpLabel
|
|
%32 = OpIAdd %6 %37 %28
|
|
OpStore %11 %32
|
|
OpBranchConditional %60 %15 %13
|
|
%15 = OpLabel
|
|
%38 = OpPhi %6 %37 %17 %29 %25 %23 %16
|
|
OpReturnValue %38
|
|
OpFunctionEnd
|
|
)";
|
|
|
|
const auto env = SPV_ENV_UNIVERSAL_1_4;
|
|
const auto consumer = nullptr;
|
|
|
|
FactManager fact_manager;
|
|
|
|
const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
|
|
ASSERT_TRUE(IsValid(env, context.get()));
|
|
|
|
// Make a sequence of instruction messages corresponding to function %8 in
|
|
// |donor|.
|
|
std::vector<protobufs::Instruction> instructions =
|
|
GetInstructionsForFunction(env, consumer, donor, 8);
|
|
|
|
protobufs::LoopLimiterInfo loop_limiter_info;
|
|
loop_limiter_info.set_loop_header_id(13);
|
|
loop_limiter_info.set_load_id(102);
|
|
loop_limiter_info.set_increment_id(103);
|
|
loop_limiter_info.set_compare_id(104);
|
|
loop_limiter_info.set_logical_op_id(105);
|
|
|
|
TransformationAddFunction transformation(instructions, 100, 28,
|
|
{loop_limiter_info}, 0, {});
|
|
ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager));
|
|
transformation.Apply(context.get(), &fact_manager);
|
|
ASSERT_TRUE(IsValid(env, context.get()));
|
|
std::string expected = R"(
|
|
OpCapability Shader
|
|
%1 = OpExtInstImport "GLSL.std.450"
|
|
OpMemoryModel Logical GLSL450
|
|
OpEntryPoint Fragment %4 "main"
|
|
OpExecutionMode %4 OriginUpperLeft
|
|
OpSource ESSL 310
|
|
%2 = OpTypeVoid
|
|
%3 = OpTypeFunction %2
|
|
%6 = OpTypeInt 32 1
|
|
%50 = OpTypeInt 32 0
|
|
%51 = OpConstant %50 0
|
|
%52 = OpConstant %50 1
|
|
%53 = OpTypePointer Function %50
|
|
%7 = OpTypeFunction %6
|
|
%10 = OpTypePointer Function %6
|
|
%12 = OpConstant %6 0
|
|
%19 = OpConstant %6 100
|
|
%20 = OpTypeBool
|
|
%60 = OpConstantTrue %20
|
|
%23 = OpConstant %6 20
|
|
%28 = OpConstant %6 1
|
|
%4 = OpFunction %2 None %3
|
|
%5 = OpLabel
|
|
OpReturn
|
|
OpFunctionEnd
|
|
%8 = OpFunction %6 None %7
|
|
%9 = OpLabel
|
|
%100 = OpVariable %53 Function %51
|
|
%11 = OpVariable %10 Function
|
|
OpStore %11 %12
|
|
OpBranch %13
|
|
%13 = OpLabel
|
|
%37 = OpPhi %6 %12 %9 %32 %16
|
|
OpLoopMerge %15 %16 None
|
|
OpBranch %17
|
|
%17 = OpLabel
|
|
%21 = OpSLessThan %20 %37 %19
|
|
OpBranchConditional %21 %14 %15
|
|
%14 = OpLabel
|
|
%24 = OpSGreaterThan %20 %37 %23
|
|
OpSelectionMerge %26 None
|
|
OpBranchConditional %24 %25 %26
|
|
%25 = OpLabel
|
|
%29 = OpIAdd %6 %37 %28
|
|
OpStore %11 %29
|
|
OpBranch %15
|
|
%26 = OpLabel
|
|
OpBranch %16
|
|
%16 = OpLabel
|
|
%32 = OpIAdd %6 %37 %28
|
|
OpStore %11 %32
|
|
%102 = OpLoad %50 %100
|
|
%103 = OpIAdd %50 %102 %52
|
|
OpStore %100 %103
|
|
%104 = OpUGreaterThanEqual %20 %102 %28
|
|
%105 = OpLogicalOr %20 %60 %104
|
|
OpBranchConditional %105 %15 %13
|
|
%15 = OpLabel
|
|
%38 = OpPhi %6 %37 %17 %29 %25 %23 %16
|
|
OpReturnValue %38
|
|
OpFunctionEnd
|
|
)";
|
|
ASSERT_TRUE(IsEqual(env, expected, context.get()));
|
|
}
|
|
|
|
} // namespace
|
|
} // namespace fuzz
|
|
} // namespace spvtools
|