blob: 65cf3d4304adbb3af4a8f1b19b262d034e315237 [file] [log] [blame]
// 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_replace_boolean_constant_with_constant_binary.h"
#include "gtest/gtest.h"
#include "source/fuzz/fuzzer_util.h"
#include "source/fuzz/id_use_descriptor.h"
#include "source/fuzz/instruction_descriptor.h"
#include "test/fuzz/fuzz_test_util.h"
namespace spvtools {
namespace fuzz {
namespace {
TEST(TransformationReplaceBooleanConstantWithConstantBinaryTest,
BasicReplacements) {
// The test came from the following pseudo-GLSL, where int64 and uint64 denote
// 64-bit integer types (they were replaced with int and uint during
// translation to SPIR-V, and the generated SPIR-V has been doctored to
// accommodate them).
//
// #version 450
//
// void main() {
// double d1, d2;
// d1 = 1.0;
// d2 = 2.0;
// float f1, f2;
// f1 = 4.0;
// f2 = 8.0;
// int i1, i2;
// i1 = 100;
// i2 = 200;
//
// uint u1, u2;
// u1 = 300u;
// u2 = 400u;
//
// int64 i64_1, i64_2;
// i64_1 = 500;
// i64_2 = 600;
//
// uint64 u64_1, u64_2;
// u64_1 = 700u;
// u64_2 = 800u;
//
// bool b, c, d, e;
// b = true;
// c = false;
// d = true || c;
// c = c && false;
// }
std::string shader = R"(
OpCapability Shader
OpCapability Float64
OpCapability Int64
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %4 "main"
OpExecutionMode %4 OriginUpperLeft
OpSource GLSL 450
OpName %4 "main"
OpName %8 "d1"
OpName %10 "d2"
OpName %14 "f1"
OpName %16 "f2"
OpName %20 "i1"
OpName %22 "i2"
OpName %26 "u1"
OpName %28 "u2"
OpName %30 "i64_1"
OpName %32 "i64_2"
OpName %34 "u64_1"
OpName %36 "u64_2"
OpName %40 "b"
OpName %42 "c"
OpName %44 "d"
%2 = OpTypeVoid
%3 = OpTypeFunction %2
%6 = OpTypeFloat 64
%7 = OpTypePointer Function %6
%9 = OpConstant %6 1
%11 = OpConstant %6 2
%12 = OpTypeFloat 32
%13 = OpTypePointer Function %12
%15 = OpConstant %12 4
%17 = OpConstant %12 8
%18 = OpTypeInt 32 1
%60 = OpTypeInt 64 1
%61 = OpTypePointer Function %60
%19 = OpTypePointer Function %18
%21 = OpConstant %18 -100
%23 = OpConstant %18 200
%24 = OpTypeInt 32 0
%62 = OpTypeInt 64 0
%63 = OpTypePointer Function %62
%25 = OpTypePointer Function %24
%27 = OpConstant %24 300
%29 = OpConstant %24 400
%31 = OpConstant %60 -600
%33 = OpConstant %60 -500
%35 = OpConstant %62 700
%37 = OpConstant %62 800
%38 = OpTypeBool
%39 = OpTypePointer Function %38
%41 = OpConstantTrue %38
%43 = OpConstantFalse %38
%4 = OpFunction %2 None %3
%5 = OpLabel
%8 = OpVariable %7 Function
%10 = OpVariable %7 Function
%14 = OpVariable %13 Function
%16 = OpVariable %13 Function
%20 = OpVariable %19 Function
%22 = OpVariable %19 Function
%26 = OpVariable %25 Function
%28 = OpVariable %25 Function
%30 = OpVariable %61 Function
%32 = OpVariable %61 Function
%34 = OpVariable %63 Function
%36 = OpVariable %63 Function
%40 = OpVariable %39 Function
%42 = OpVariable %39 Function
%44 = OpVariable %39 Function
OpStore %8 %9
OpStore %10 %11
OpStore %14 %15
OpStore %16 %17
OpStore %20 %21
OpStore %22 %23
OpStore %26 %27
OpStore %28 %29
OpStore %30 %31
OpStore %32 %33
OpStore %34 %35
OpStore %36 %37
OpStore %40 %41
OpStore %42 %43
%45 = OpLoad %38 %42
%46 = OpLogicalOr %38 %41 %45
OpStore %44 %46
%47 = OpLoad %38 %42
%48 = OpLogicalAnd %38 %47 %43
OpStore %42 %48
OpReturn
OpFunctionEnd
)";
const auto env = SPV_ENV_UNIVERSAL_1_3;
const auto consumer = nullptr;
const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
spvtools::ValidatorOptions validator_options;
ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
kConsoleMessageConsumer));
TransformationContext transformation_context(
MakeUnique<FactManager>(context.get()), validator_options);
std::vector<protobufs::IdUseDescriptor> uses_of_true = {
MakeIdUseDescriptor(
41, MakeInstructionDescriptor(44, spv::Op::OpStore, 12), 1),
MakeIdUseDescriptor(
41, MakeInstructionDescriptor(46, spv::Op::OpLogicalOr, 0), 0)};
std::vector<protobufs::IdUseDescriptor> uses_of_false = {
MakeIdUseDescriptor(
43, MakeInstructionDescriptor(44, spv::Op::OpStore, 13), 1),
MakeIdUseDescriptor(
43, MakeInstructionDescriptor(48, spv::Op::OpLogicalAnd, 0), 1)};
const uint32_t fresh_id = 100;
std::vector<spv::Op> fp_gt_opcodes = {
spv::Op::OpFOrdGreaterThan, spv::Op::OpFOrdGreaterThanEqual,
spv::Op::OpFUnordGreaterThan, spv::Op::OpFUnordGreaterThanEqual};
std::vector<spv::Op> fp_lt_opcodes = {
spv::Op::OpFOrdLessThan, spv::Op::OpFOrdLessThanEqual,
spv::Op::OpFUnordLessThan, spv::Op::OpFUnordLessThanEqual};
std::vector<spv::Op> int_gt_opcodes = {spv::Op::OpSGreaterThan,
spv::Op::OpSGreaterThanEqual};
std::vector<spv::Op> int_lt_opcodes = {spv::Op::OpSLessThan,
spv::Op::OpSLessThanEqual};
std::vector<spv::Op> uint_gt_opcodes = {spv::Op::OpUGreaterThan,
spv::Op::OpUGreaterThanEqual};
std::vector<spv::Op> uint_lt_opcodes = {spv::Op::OpULessThan,
spv::Op::OpULessThanEqual};
#define CHECK_OPERATOR(USE_DESCRIPTOR, LHS_ID, RHS_ID, OPCODE, FRESH_ID) \
ASSERT_TRUE(TransformationReplaceBooleanConstantWithConstantBinary( \
USE_DESCRIPTOR, LHS_ID, RHS_ID, OPCODE, FRESH_ID) \
.IsApplicable(context.get(), transformation_context)); \
ASSERT_FALSE(TransformationReplaceBooleanConstantWithConstantBinary( \
USE_DESCRIPTOR, RHS_ID, LHS_ID, OPCODE, FRESH_ID) \
.IsApplicable(context.get(), transformation_context));
#define CHECK_TRANSFORMATION_APPLICABILITY(GT_OPCODES, LT_OPCODES, SMALL_ID, \
LARGE_ID) \
for (auto gt_opcode : GT_OPCODES) { \
for (auto& true_use : uses_of_true) { \
CHECK_OPERATOR(true_use, LARGE_ID, SMALL_ID, gt_opcode, fresh_id); \
} \
for (auto& false_use : uses_of_false) { \
CHECK_OPERATOR(false_use, SMALL_ID, LARGE_ID, gt_opcode, fresh_id); \
} \
} \
for (auto lt_opcode : LT_OPCODES) { \
for (auto& true_use : uses_of_true) { \
CHECK_OPERATOR(true_use, SMALL_ID, LARGE_ID, lt_opcode, fresh_id); \
} \
for (auto& false_use : uses_of_false) { \
CHECK_OPERATOR(false_use, LARGE_ID, SMALL_ID, lt_opcode, fresh_id); \
} \
}
// Float
{ CHECK_TRANSFORMATION_APPLICABILITY(fp_gt_opcodes, fp_lt_opcodes, 15, 17); }
// Double
{ CHECK_TRANSFORMATION_APPLICABILITY(fp_gt_opcodes, fp_lt_opcodes, 9, 11); }
// Int32
{
CHECK_TRANSFORMATION_APPLICABILITY(int_gt_opcodes, int_lt_opcodes, 21, 23);
}
// Int64
{
CHECK_TRANSFORMATION_APPLICABILITY(int_gt_opcodes, int_lt_opcodes, 31, 33);
}
// Uint32
{
CHECK_TRANSFORMATION_APPLICABILITY(uint_gt_opcodes, uint_lt_opcodes, 27,
29);
}
// Uint64
{
CHECK_TRANSFORMATION_APPLICABILITY(uint_gt_opcodes, uint_lt_opcodes, 35,
37);
}
// Target id is not fresh
ASSERT_FALSE(TransformationReplaceBooleanConstantWithConstantBinary(
uses_of_true[0], 15, 17, spv::Op::OpFOrdLessThan, 15)
.IsApplicable(context.get(), transformation_context));
// LHS id does not exist
ASSERT_FALSE(TransformationReplaceBooleanConstantWithConstantBinary(
uses_of_true[0], 300, 17, spv::Op::OpFOrdLessThan, 200)
.IsApplicable(context.get(), transformation_context));
// RHS id does not exist
ASSERT_FALSE(TransformationReplaceBooleanConstantWithConstantBinary(
uses_of_true[0], 15, 300, spv::Op::OpFOrdLessThan, 200)
.IsApplicable(context.get(), transformation_context));
// LHS and RHS ids do not match type
ASSERT_FALSE(TransformationReplaceBooleanConstantWithConstantBinary(
uses_of_true[0], 11, 17, spv::Op::OpFOrdLessThan, 200)
.IsApplicable(context.get(), transformation_context));
// Opcode not appropriate
ASSERT_FALSE(TransformationReplaceBooleanConstantWithConstantBinary(
uses_of_true[0], 15, 17, spv::Op::OpFDiv, 200)
.IsApplicable(context.get(), transformation_context));
auto replace_true_with_double_comparison =
TransformationReplaceBooleanConstantWithConstantBinary(
uses_of_true[0], 11, 9, spv::Op::OpFUnordGreaterThan, 100);
auto replace_true_with_uint32_comparison =
TransformationReplaceBooleanConstantWithConstantBinary(
uses_of_true[1], 27, 29, spv::Op::OpULessThanEqual, 101);
auto replace_false_with_float_comparison =
TransformationReplaceBooleanConstantWithConstantBinary(
uses_of_false[0], 17, 15, spv::Op::OpFOrdLessThan, 102);
auto replace_false_with_sint64_comparison =
TransformationReplaceBooleanConstantWithConstantBinary(
uses_of_false[1], 33, 31, spv::Op::OpSLessThan, 103);
ASSERT_TRUE(replace_true_with_double_comparison.IsApplicable(
context.get(), transformation_context));
ApplyAndCheckFreshIds(replace_true_with_double_comparison, context.get(),
&transformation_context);
ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
kConsoleMessageConsumer));
ASSERT_TRUE(replace_true_with_uint32_comparison.IsApplicable(
context.get(), transformation_context));
ApplyAndCheckFreshIds(replace_true_with_uint32_comparison, context.get(),
&transformation_context);
ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
kConsoleMessageConsumer));
ASSERT_TRUE(replace_false_with_float_comparison.IsApplicable(
context.get(), transformation_context));
ApplyAndCheckFreshIds(replace_false_with_float_comparison, context.get(),
&transformation_context);
ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
kConsoleMessageConsumer));
ASSERT_TRUE(replace_false_with_sint64_comparison.IsApplicable(
context.get(), transformation_context));
ApplyAndCheckFreshIds(replace_false_with_sint64_comparison, context.get(),
&transformation_context);
ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
kConsoleMessageConsumer));
std::string after = R"(
OpCapability Shader
OpCapability Float64
OpCapability Int64
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %4 "main"
OpExecutionMode %4 OriginUpperLeft
OpSource GLSL 450
OpName %4 "main"
OpName %8 "d1"
OpName %10 "d2"
OpName %14 "f1"
OpName %16 "f2"
OpName %20 "i1"
OpName %22 "i2"
OpName %26 "u1"
OpName %28 "u2"
OpName %30 "i64_1"
OpName %32 "i64_2"
OpName %34 "u64_1"
OpName %36 "u64_2"
OpName %40 "b"
OpName %42 "c"
OpName %44 "d"
%2 = OpTypeVoid
%3 = OpTypeFunction %2
%6 = OpTypeFloat 64
%7 = OpTypePointer Function %6
%9 = OpConstant %6 1
%11 = OpConstant %6 2
%12 = OpTypeFloat 32
%13 = OpTypePointer Function %12
%15 = OpConstant %12 4
%17 = OpConstant %12 8
%18 = OpTypeInt 32 1
%60 = OpTypeInt 64 1
%61 = OpTypePointer Function %60
%19 = OpTypePointer Function %18
%21 = OpConstant %18 -100
%23 = OpConstant %18 200
%24 = OpTypeInt 32 0
%62 = OpTypeInt 64 0
%63 = OpTypePointer Function %62
%25 = OpTypePointer Function %24
%27 = OpConstant %24 300
%29 = OpConstant %24 400
%31 = OpConstant %60 -600
%33 = OpConstant %60 -500
%35 = OpConstant %62 700
%37 = OpConstant %62 800
%38 = OpTypeBool
%39 = OpTypePointer Function %38
%41 = OpConstantTrue %38
%43 = OpConstantFalse %38
%4 = OpFunction %2 None %3
%5 = OpLabel
%8 = OpVariable %7 Function
%10 = OpVariable %7 Function
%14 = OpVariable %13 Function
%16 = OpVariable %13 Function
%20 = OpVariable %19 Function
%22 = OpVariable %19 Function
%26 = OpVariable %25 Function
%28 = OpVariable %25 Function
%30 = OpVariable %61 Function
%32 = OpVariable %61 Function
%34 = OpVariable %63 Function
%36 = OpVariable %63 Function
%40 = OpVariable %39 Function
%42 = OpVariable %39 Function
%44 = OpVariable %39 Function
OpStore %8 %9
OpStore %10 %11
OpStore %14 %15
OpStore %16 %17
OpStore %20 %21
OpStore %22 %23
OpStore %26 %27
OpStore %28 %29
OpStore %30 %31
OpStore %32 %33
OpStore %34 %35
OpStore %36 %37
%100 = OpFUnordGreaterThan %38 %11 %9
OpStore %40 %100
%102 = OpFOrdLessThan %38 %17 %15
OpStore %42 %102
%45 = OpLoad %38 %42
%101 = OpULessThanEqual %38 %27 %29
%46 = OpLogicalOr %38 %101 %45
OpStore %44 %46
%47 = OpLoad %38 %42
%103 = OpSLessThan %38 %33 %31
%48 = OpLogicalAnd %38 %47 %103
OpStore %42 %48
OpReturn
OpFunctionEnd
)";
ASSERT_TRUE(IsEqual(env, after, context.get()));
if (std::numeric_limits<double>::has_quiet_NaN) {
double quiet_nan_double = std::numeric_limits<double>::quiet_NaN();
uint32_t words[2];
memcpy(words, &quiet_nan_double, sizeof(double));
opt::Instruction::OperandList operands = {
{SPV_OPERAND_TYPE_LITERAL_INTEGER, {words[0]}},
{SPV_OPERAND_TYPE_LITERAL_INTEGER, {words[1]}}};
context->module()->AddGlobalValue(MakeUnique<opt::Instruction>(
context.get(), spv::Op::OpConstant, 6, 200, operands));
fuzzerutil::UpdateModuleIdBound(context.get(), 200);
ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(
context.get(), validator_options, kConsoleMessageConsumer));
// The transformation is not applicable because %200 is NaN.
ASSERT_FALSE(TransformationReplaceBooleanConstantWithConstantBinary(
uses_of_true[0], 11, 200, spv::Op::OpFOrdLessThan, 300)
.IsApplicable(context.get(), transformation_context));
}
if (std::numeric_limits<double>::has_infinity) {
double positive_infinity_double = std::numeric_limits<double>::infinity();
uint32_t words[2];
memcpy(words, &positive_infinity_double, sizeof(double));
opt::Instruction::OperandList operands = {
{SPV_OPERAND_TYPE_LITERAL_INTEGER, {words[0]}},
{SPV_OPERAND_TYPE_LITERAL_INTEGER, {words[1]}}};
context->module()->AddGlobalValue(MakeUnique<opt::Instruction>(
context.get(), spv::Op::OpConstant, 6, 201, operands));
fuzzerutil::UpdateModuleIdBound(context.get(), 201);
ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(
context.get(), validator_options, kConsoleMessageConsumer));
// Even though the double constant %11 is less than the infinity %201, the
// transformation is restricted to only apply to finite values.
ASSERT_FALSE(TransformationReplaceBooleanConstantWithConstantBinary(
uses_of_true[0], 11, 201, spv::Op::OpFOrdLessThan, 300)
.IsApplicable(context.get(), transformation_context));
}
if (std::numeric_limits<float>::has_infinity) {
float positive_infinity_float = std::numeric_limits<float>::infinity();
float negative_infinity_float = -1 * positive_infinity_float;
uint32_t words_positive_infinity[1];
uint32_t words_negative_infinity[1];
memcpy(words_positive_infinity, &positive_infinity_float, sizeof(float));
memcpy(words_negative_infinity, &negative_infinity_float, sizeof(float));
opt::Instruction::OperandList operands_positive_infinity = {
{SPV_OPERAND_TYPE_LITERAL_INTEGER, {words_positive_infinity[0]}}};
context->module()->AddGlobalValue(
MakeUnique<opt::Instruction>(context.get(), spv::Op::OpConstant, 12,
202, operands_positive_infinity));
fuzzerutil::UpdateModuleIdBound(context.get(), 202);
opt::Instruction::OperandList operands = {
{SPV_OPERAND_TYPE_LITERAL_INTEGER, {words_negative_infinity[0]}}};
context->module()->AddGlobalValue(MakeUnique<opt::Instruction>(
context.get(), spv::Op::OpConstant, 12, 203, operands));
fuzzerutil::UpdateModuleIdBound(context.get(), 203);
ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(
context.get(), validator_options, kConsoleMessageConsumer));
// Even though the negative infinity at %203 is less than the positive
// infinity %202, the transformation is restricted to only apply to finite
// values.
ASSERT_FALSE(TransformationReplaceBooleanConstantWithConstantBinary(
uses_of_true[0], 203, 202, spv::Op::OpFOrdLessThan, 300)
.IsApplicable(context.get(), transformation_context));
}
}
TEST(TransformationReplaceBooleanConstantWithConstantBinaryTest,
MergeInstructions) {
// The test came from the following GLSL:
//
// void main() {
// int x = 1;
// int y = 2;
// if (true) {
// x = 2;
// }
// while(false) {
// y = 2;
// }
// }
std::string shader = R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %4 "main"
OpExecutionMode %4 OriginUpperLeft
OpSource GLSL 450
OpName %4 "main"
OpName %8 "x"
OpName %10 "y"
%2 = OpTypeVoid
%3 = OpTypeFunction %2
%6 = OpTypeInt 32 1
%7 = OpTypePointer Function %6
%9 = OpConstant %6 1
%11 = OpConstant %6 2
%12 = OpTypeBool
%13 = OpConstantTrue %12
%21 = OpConstantFalse %12
%4 = OpFunction %2 None %3
%5 = OpLabel
%8 = OpVariable %7 Function
%10 = OpVariable %7 Function
OpStore %8 %9
OpStore %10 %11
OpSelectionMerge %15 None
OpBranchConditional %13 %14 %15
%14 = OpLabel
OpStore %8 %11
OpBranch %15
%15 = OpLabel
OpBranch %16
%16 = OpLabel
OpLoopMerge %18 %19 None
OpBranchConditional %21 %17 %18
%17 = OpLabel
OpStore %10 %11
OpBranch %19
%19 = OpLabel
OpBranch %16
%18 = OpLabel
OpReturn
OpFunctionEnd
)";
const auto env = SPV_ENV_UNIVERSAL_1_3;
const auto consumer = nullptr;
const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
spvtools::ValidatorOptions validator_options;
ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
kConsoleMessageConsumer));
TransformationContext transformation_context(
MakeUnique<FactManager>(context.get()), validator_options);
auto use_of_true_in_if = MakeIdUseDescriptor(
13, MakeInstructionDescriptor(10, spv::Op::OpBranchConditional, 0), 0);
auto use_of_false_in_while = MakeIdUseDescriptor(
21, MakeInstructionDescriptor(16, spv::Op::OpBranchConditional, 0), 0);
auto replacement_1 = TransformationReplaceBooleanConstantWithConstantBinary(
use_of_true_in_if, 9, 11, spv::Op::OpSLessThan, 100);
auto replacement_2 = TransformationReplaceBooleanConstantWithConstantBinary(
use_of_false_in_while, 9, 11, spv::Op::OpSGreaterThanEqual, 101);
ASSERT_TRUE(
replacement_1.IsApplicable(context.get(), transformation_context));
ApplyAndCheckFreshIds(replacement_1, context.get(), &transformation_context);
ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
kConsoleMessageConsumer));
ASSERT_TRUE(
replacement_2.IsApplicable(context.get(), transformation_context));
ApplyAndCheckFreshIds(replacement_2, context.get(), &transformation_context);
ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
kConsoleMessageConsumer));
std::string after = R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %4 "main"
OpExecutionMode %4 OriginUpperLeft
OpSource GLSL 450
OpName %4 "main"
OpName %8 "x"
OpName %10 "y"
%2 = OpTypeVoid
%3 = OpTypeFunction %2
%6 = OpTypeInt 32 1
%7 = OpTypePointer Function %6
%9 = OpConstant %6 1
%11 = OpConstant %6 2
%12 = OpTypeBool
%13 = OpConstantTrue %12
%21 = OpConstantFalse %12
%4 = OpFunction %2 None %3
%5 = OpLabel
%8 = OpVariable %7 Function
%10 = OpVariable %7 Function
OpStore %8 %9
OpStore %10 %11
%100 = OpSLessThan %12 %9 %11
OpSelectionMerge %15 None
OpBranchConditional %100 %14 %15
%14 = OpLabel
OpStore %8 %11
OpBranch %15
%15 = OpLabel
OpBranch %16
%16 = OpLabel
%101 = OpSGreaterThanEqual %12 %9 %11
OpLoopMerge %18 %19 None
OpBranchConditional %101 %17 %18
%17 = OpLabel
OpStore %10 %11
OpBranch %19
%19 = OpLabel
OpBranch %16
%18 = OpLabel
OpReturn
OpFunctionEnd
)";
ASSERT_TRUE(IsEqual(env, after, context.get()));
}
TEST(TransformationReplaceBooleanConstantWithConstantBinaryTest, OpPhi) {
// Hand-written SPIR-V to check applicability of the transformation on an
// OpPhi argument.
std::string reference_shader = R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Vertex %10 "main"
; Types
%2 = OpTypeVoid
%3 = OpTypeFunction %2
%4 = OpTypeInt 32 0
%5 = OpTypeBool
; Constants
%6 = OpConstant %4 0
%7 = OpConstant %4 1
%8 = OpConstantTrue %5
%9 = OpConstantFalse %5
; main function
%10 = OpFunction %2 None %3
%11 = OpLabel
OpSelectionMerge %13 None
OpBranchConditional %8 %12 %13
%12 = OpLabel
OpBranch %13
%13 = OpLabel
%14 = OpPhi %5 %8 %11 %9 %12
OpReturn
OpFunctionEnd
)";
const auto env = SPV_ENV_UNIVERSAL_1_3;
const auto consumer = nullptr;
const auto context =
BuildModule(env, consumer, reference_shader, kFuzzAssembleOption);
spvtools::ValidatorOptions validator_options;
ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
kConsoleMessageConsumer));
TransformationContext transformation_context(
MakeUnique<FactManager>(context.get()), validator_options);
auto instruction_descriptor =
MakeInstructionDescriptor(14, spv::Op::OpPhi, 0);
auto id_use_descriptor = MakeIdUseDescriptor(8, instruction_descriptor, 0);
auto transformation = TransformationReplaceBooleanConstantWithConstantBinary(
id_use_descriptor, 6, 7, spv::Op::OpULessThan, 15);
ASSERT_TRUE(
transformation.IsApplicable(context.get(), transformation_context));
ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context);
std::string variant_shader = R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Vertex %10 "main"
; Types
%2 = OpTypeVoid
%3 = OpTypeFunction %2
%4 = OpTypeInt 32 0
%5 = OpTypeBool
; Constants
%6 = OpConstant %4 0
%7 = OpConstant %4 1
%8 = OpConstantTrue %5
%9 = OpConstantFalse %5
; main function
%10 = OpFunction %2 None %3
%11 = OpLabel
%15 = OpULessThan %5 %6 %7
OpSelectionMerge %13 None
OpBranchConditional %8 %12 %13
%12 = OpLabel
OpBranch %13
%13 = OpLabel
%14 = OpPhi %5 %15 %11 %9 %12
OpReturn
OpFunctionEnd
)";
ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
kConsoleMessageConsumer));
ASSERT_TRUE(IsEqual(env, variant_shader, context.get()));
}
TEST(TransformationReplaceBooleanConstantWithConstantBinaryTest,
DoNotReplaceVariableInitializer) {
std::string shader = R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %4 "main"
OpExecutionMode %4 OriginUpperLeft
OpSource ESSL 310
OpName %4 "main"
%2 = OpTypeVoid
%3 = OpTypeFunction %2
%6 = OpTypeBool
%7 = OpTypePointer Function %6
%9 = OpConstantTrue %6
%10 = OpTypeInt 32 1
%13 = OpConstant %10 0
%15 = OpConstant %10 1
%4 = OpFunction %2 None %3
%5 = OpLabel
%50 = OpVariable %7 Function %9
OpReturn
OpFunctionEnd
)";
const auto env = SPV_ENV_UNIVERSAL_1_3;
const auto consumer = nullptr;
const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
spvtools::ValidatorOptions validator_options;
ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
kConsoleMessageConsumer));
TransformationContext transformation_context(
MakeUnique<FactManager>(context.get()), validator_options);
ASSERT_FALSE(
TransformationReplaceBooleanConstantWithConstantBinary(
MakeIdUseDescriptor(
9, MakeInstructionDescriptor(50, spv::Op::OpVariable, 0), 1),
13, 15, spv::Op::OpSLessThan, 100)
.IsApplicable(context.get(), transformation_context));
}
} // namespace
} // namespace fuzz
} // namespace spvtools