spirv-fuzz: Account for differing signedness in WrapVectorSynonym (#4414)

Makes the fuzzer pass and transformation that wraps vector synonyms
aware of the fact that integer operations can have arguments that
differ in signedness, and that the result type of such an operation
can have different sign from the argument types.

Fixes #4413.
This commit is contained in:
Alastair Donaldson 2021-09-14 22:09:39 +01:00 коммит произвёл GitHub
Родитель 36ff135341
Коммит 9e65f054d1
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
11 изменённых файлов: 581 добавлений и 399 удалений

Просмотреть файл

@ -198,7 +198,7 @@ bool FuzzerPassApplyIdSynonyms::DataDescriptorsHaveCompatibleTypes(
GetIRContext(), base_object_type_id_2, dd2.index());
assert(type_id_1 && type_id_2 && "Data descriptors have invalid types");
return TransformationReplaceIdWithSynonym::TypesAreCompatible(
return fuzzerutil::TypesAreCompatible(
GetIRContext(), opcode, use_in_operand_index, type_id_1, type_id_2);
}

Просмотреть файл

@ -55,21 +55,12 @@ void FuzzerPassWrapVectorSynonym::Apply() {
SpvOpCompositeConstruct, instruction_iterator)) {
return;
}
// Get the scalar type represented by the targeted instruction id.
uint32_t operand_type_id = instruction_iterator->type_id();
// Get a random vector size from 2 to 4.
uint32_t vector_size = GetFuzzerContext()->GetWidthOfWrappingVector();
// Randomly choose a position that target ids should be placed at.
// The position is in range [0, n - 1], where n is the size of the
// vector.
uint32_t position =
GetFuzzerContext()->GetRandomIndexForWrappingVector(vector_size);
// Target ids are the two scalar ids from the original instruction.
uint32_t target_id1 = instruction_iterator->GetSingleWordInOperand(0);
uint32_t target_id2 = instruction_iterator->GetSingleWordInOperand(1);
// Get the scalar operands from the original instruction.
opt::Instruction* operand1 = GetIRContext()->get_def_use_mgr()->GetDef(
instruction_iterator->GetSingleWordInOperand(0));
opt::Instruction* operand2 = GetIRContext()->get_def_use_mgr()->GetDef(
instruction_iterator->GetSingleWordInOperand(1));
// We need to be able to make a synonym of the scalar operation's result
// id, as well as the operand ids (for example, they cannot be
@ -80,16 +71,23 @@ void FuzzerPassWrapVectorSynonym::Apply() {
return;
}
if (!fuzzerutil::CanMakeSynonymOf(
GetIRContext(), *GetTransformationContext(),
*GetIRContext()->get_def_use_mgr()->GetDef(target_id1))) {
GetIRContext(), *GetTransformationContext(), *operand1)) {
return;
}
if (!fuzzerutil::CanMakeSynonymOf(
GetIRContext(), *GetTransformationContext(),
*GetIRContext()->get_def_use_mgr()->GetDef(target_id2))) {
GetIRContext(), *GetTransformationContext(), *operand2)) {
return;
}
// Get a random vector size from 2 to 4.
uint32_t vector_size = GetFuzzerContext()->GetWidthOfWrappingVector();
// Randomly choose a position that target ids should be placed at.
// The position is in range [0, n - 1], where n is the size of the
// vector.
uint32_t position =
GetFuzzerContext()->GetRandomIndexForWrappingVector(vector_size);
// Stores the ids of scalar constants.
std::vector<uint32_t> vec1_components;
std::vector<uint32_t> vec2_components;
@ -97,33 +95,42 @@ void FuzzerPassWrapVectorSynonym::Apply() {
// Populate components based on vector type and size.
for (uint32_t i = 0; i < vector_size; ++i) {
if (i == position) {
vec1_components.emplace_back(target_id1);
vec2_components.emplace_back(target_id2);
vec1_components.emplace_back(operand1->result_id());
vec2_components.emplace_back(operand2->result_id());
} else {
vec1_components.emplace_back(
FindOrCreateZeroConstant(operand_type_id, true));
FindOrCreateZeroConstant(operand1->type_id(), true));
vec2_components.emplace_back(
FindOrCreateZeroConstant(operand_type_id, true));
FindOrCreateZeroConstant(operand2->type_id(), true));
}
}
// Add two OpCompositeConstruct to the module with result id returned.
const uint32_t vector_type_id =
FindOrCreateVectorType(operand_type_id, vector_size);
// The added vectors may have different types, for instance if the
// scalar instruction operates on integers with differing sign.
// Add the first OpCompositeConstruct that wraps the id of the first
// operand.
uint32_t result_id1 = GetFuzzerContext()->GetFreshId();
ApplyTransformation(TransformationCompositeConstruct(
vector_type_id, vec1_components, instruction_descriptor,
result_id1));
FindOrCreateVectorType(operand1->type_id(), vector_size),
vec1_components, instruction_descriptor, result_id1));
// Add the second OpCompositeConstruct that wraps the id of the second
// operand.
uint32_t result_id2 = GetFuzzerContext()->GetFreshId();
ApplyTransformation(TransformationCompositeConstruct(
vector_type_id, vec2_components, instruction_descriptor,
result_id2));
FindOrCreateVectorType(operand2->type_id(), vector_size),
vec2_components, instruction_descriptor, result_id2));
// The result of the vector instruction that
// TransformationWrapVectorSynonym will create should be a vector of the
// right size, with the scalar instruction's result type as its element
// type. This can be distinct from the types of the operands, if the
// scalar instruction adds two signed integers and stores the result in
// an unsigned id, for example. A transformation is applied to add the
// right type to the module.
FindOrCreateVectorType(instruction_iterator->type_id(), vector_size);
// Apply transformation to do vector operation and add synonym between
// the result vector id and the id of the original instruction.

Просмотреть файл

@ -2018,6 +2018,93 @@ opt::Module::iterator GetFunctionIterator(opt::IRContext* ir_context,
});
}
// TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3582): Add all
// opcodes that are agnostic to signedness of operands to function.
// This is not exhaustive yet.
bool IsAgnosticToSignednessOfOperand(SpvOp opcode,
uint32_t use_in_operand_index) {
switch (opcode) {
case SpvOpSNegate:
case SpvOpNot:
case SpvOpIAdd:
case SpvOpISub:
case SpvOpIMul:
case SpvOpSDiv:
case SpvOpSRem:
case SpvOpSMod:
case SpvOpShiftRightLogical:
case SpvOpShiftRightArithmetic:
case SpvOpShiftLeftLogical:
case SpvOpBitwiseOr:
case SpvOpBitwiseXor:
case SpvOpBitwiseAnd:
case SpvOpIEqual:
case SpvOpINotEqual:
case SpvOpULessThan:
case SpvOpSLessThan:
case SpvOpUGreaterThan:
case SpvOpSGreaterThan:
case SpvOpULessThanEqual:
case SpvOpSLessThanEqual:
case SpvOpUGreaterThanEqual:
case SpvOpSGreaterThanEqual:
return true;
case SpvOpAtomicStore:
case SpvOpAtomicExchange:
case SpvOpAtomicIAdd:
case SpvOpAtomicISub:
case SpvOpAtomicSMin:
case SpvOpAtomicUMin:
case SpvOpAtomicSMax:
case SpvOpAtomicUMax:
case SpvOpAtomicAnd:
case SpvOpAtomicOr:
case SpvOpAtomicXor:
case SpvOpAtomicFAddEXT: // Capability AtomicFloat32AddEXT,
// AtomicFloat64AddEXT.
assert(use_in_operand_index != 0 &&
"Signedness check should not occur on a pointer operand.");
return use_in_operand_index == 1 || use_in_operand_index == 2;
case SpvOpAtomicCompareExchange:
case SpvOpAtomicCompareExchangeWeak: // Capability Kernel.
assert(use_in_operand_index != 0 &&
"Signedness check should not occur on a pointer operand.");
return use_in_operand_index >= 1 && use_in_operand_index <= 3;
case SpvOpAtomicLoad:
case SpvOpAtomicIIncrement:
case SpvOpAtomicIDecrement:
case SpvOpAtomicFlagTestAndSet: // Capability Kernel.
case SpvOpAtomicFlagClear: // Capability Kernel.
assert(use_in_operand_index != 0 &&
"Signedness check should not occur on a pointer operand.");
return use_in_operand_index >= 1;
case SpvOpAccessChain:
// The signedness of indices does not matter.
return use_in_operand_index > 0;
default:
// Conservatively assume that the id cannot be swapped in other
// instructions.
return false;
}
}
bool TypesAreCompatible(opt::IRContext* ir_context, SpvOp opcode,
uint32_t use_in_operand_index, uint32_t type_id_1,
uint32_t type_id_2) {
assert(ir_context->get_type_mgr()->GetType(type_id_1) &&
ir_context->get_type_mgr()->GetType(type_id_2) &&
"Type ids are invalid");
return type_id_1 == type_id_2 ||
(IsAgnosticToSignednessOfOperand(opcode, use_in_operand_index) &&
fuzzerutil::TypesAreEqualUpToSign(ir_context, type_id_1, type_id_2));
}
} // namespace fuzzerutil
} // namespace fuzz
} // namespace spvtools

Просмотреть файл

@ -604,6 +604,21 @@ bool NewTerminatorPreservesDominationRules(opt::IRContext* ir_context,
opt::Module::iterator GetFunctionIterator(opt::IRContext* ir_context,
uint32_t function_id);
// Returns true if the instruction with opcode |opcode| does not change its
// behaviour depending on the signedness of the operand at
// |use_in_operand_index|.
// Assumes that the operand must be the id of an integer scalar or vector.
bool IsAgnosticToSignednessOfOperand(SpvOp opcode,
uint32_t use_in_operand_index);
// Returns true if |type_id_1| and |type_id_2| represent compatible types
// given the context of the instruction with |opcode| (i.e. we can replace
// an operand of |opcode| of the first type with an id of the second type
// and vice-versa).
bool TypesAreCompatible(opt::IRContext* ir_context, SpvOp opcode,
uint32_t use_in_operand_index, uint32_t type_id_1,
uint32_t type_id_2);
} // namespace fuzzerutil
} // namespace fuzz
} // namespace spvtools

Просмотреть файл

@ -65,9 +65,10 @@ bool TransformationReplaceIdWithSynonym::IsApplicable(
// If the id of interest and the synonym are scalar or vector integer
// constants with different signedness, their use can only be swapped if the
// instruction is agnostic to the signedness of the operand.
if (!TypesAreCompatible(ir_context, use_instruction->opcode(),
message_.id_use_descriptor().in_operand_index(),
type_id_of_interest, type_id_synonym)) {
if (!fuzzerutil::TypesAreCompatible(
ir_context, use_instruction->opcode(),
message_.id_use_descriptor().in_operand_index(), type_id_of_interest,
type_id_synonym)) {
return false;
}
@ -109,93 +110,6 @@ protobufs::Transformation TransformationReplaceIdWithSynonym::ToMessage()
return result;
}
// TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3582): Add all
// opcodes that are agnostic to signedness of operands to function.
// This is not exhaustive yet.
bool TransformationReplaceIdWithSynonym::IsAgnosticToSignednessOfOperand(
SpvOp opcode, uint32_t use_in_operand_index) {
switch (opcode) {
case SpvOpSNegate:
case SpvOpNot:
case SpvOpIAdd:
case SpvOpISub:
case SpvOpIMul:
case SpvOpSDiv:
case SpvOpSRem:
case SpvOpSMod:
case SpvOpShiftRightLogical:
case SpvOpShiftRightArithmetic:
case SpvOpShiftLeftLogical:
case SpvOpBitwiseOr:
case SpvOpBitwiseXor:
case SpvOpBitwiseAnd:
case SpvOpIEqual:
case SpvOpINotEqual:
case SpvOpULessThan:
case SpvOpSLessThan:
case SpvOpUGreaterThan:
case SpvOpSGreaterThan:
case SpvOpULessThanEqual:
case SpvOpSLessThanEqual:
case SpvOpUGreaterThanEqual:
case SpvOpSGreaterThanEqual:
return true;
case SpvOpAtomicStore:
case SpvOpAtomicExchange:
case SpvOpAtomicIAdd:
case SpvOpAtomicISub:
case SpvOpAtomicSMin:
case SpvOpAtomicUMin:
case SpvOpAtomicSMax:
case SpvOpAtomicUMax:
case SpvOpAtomicAnd:
case SpvOpAtomicOr:
case SpvOpAtomicXor:
case SpvOpAtomicFAddEXT: // Capability AtomicFloat32AddEXT,
// AtomicFloat64AddEXT.
assert(use_in_operand_index != 0 &&
"Signedness check should not occur on a pointer operand.");
return use_in_operand_index == 1 || use_in_operand_index == 2;
case SpvOpAtomicCompareExchange:
case SpvOpAtomicCompareExchangeWeak: // Capability Kernel.
assert(use_in_operand_index != 0 &&
"Signedness check should not occur on a pointer operand.");
return use_in_operand_index >= 1 && use_in_operand_index <= 3;
case SpvOpAtomicLoad:
case SpvOpAtomicIIncrement:
case SpvOpAtomicIDecrement:
case SpvOpAtomicFlagTestAndSet: // Capability Kernel.
case SpvOpAtomicFlagClear: // Capability Kernel.
assert(use_in_operand_index != 0 &&
"Signedness check should not occur on a pointer operand.");
return use_in_operand_index >= 1;
case SpvOpAccessChain:
// The signedness of indices does not matter.
return use_in_operand_index > 0;
default:
// Conservatively assume that the id cannot be swapped in other
// instructions.
return false;
}
}
bool TransformationReplaceIdWithSynonym::TypesAreCompatible(
opt::IRContext* ir_context, SpvOp opcode, uint32_t use_in_operand_index,
uint32_t type_id_1, uint32_t type_id_2) {
assert(ir_context->get_type_mgr()->GetType(type_id_1) &&
ir_context->get_type_mgr()->GetType(type_id_2) &&
"Type ids are invalid");
return type_id_1 == type_id_2 ||
(IsAgnosticToSignednessOfOperand(opcode, use_in_operand_index) &&
fuzzerutil::TypesAreEqualUpToSign(ir_context, type_id_1, type_id_2));
}
std::unordered_set<uint32_t> TransformationReplaceIdWithSynonym::GetFreshIds()
const {
return std::unordered_set<uint32_t>();

Просмотреть файл

@ -52,22 +52,7 @@ class TransformationReplaceIdWithSynonym : public Transformation {
protobufs::Transformation ToMessage() const override;
// Returns true if |type_id_1| and |type_id_2| represent compatible types
// given the context of the instruction with |opcode| (i.e. we can replace
// an operand of |opcode| of the first type with an id of the second type
// and vice-versa).
static bool TypesAreCompatible(opt::IRContext* ir_context, SpvOp opcode,
uint32_t use_in_operand_index,
uint32_t type_id_1, uint32_t type_id_2);
private:
// Returns true if the instruction with opcode |opcode| does not change its
// behaviour depending on the signedness of the operand at
// |use_in_operand_index|.
// Assumes that the operand must be the id of an integer scalar or vector.
static bool IsAgnosticToSignednessOfOperand(SpvOp opcode,
uint32_t use_in_operand_index);
protobufs::TransformationReplaceIdWithSynonym message_;
};

Просмотреть файл

@ -70,16 +70,29 @@ bool TransformationWrapVectorSynonym::IsApplicable(
return false;
}
// The 2 vectors must be the same valid vector type.
// The 2 vectors must have compatible vector types.
auto vec1_type_id = vec1->type_id();
auto vec2_type_id = vec2->type_id();
if (vec1_type_id != vec2_type_id) {
for (auto operand_index : {0, 1}) {
if (!fuzzerutil::TypesAreCompatible(ir_context, instruction->opcode(),
operand_index, vec1_type_id,
vec2_type_id)) {
return false;
}
}
auto vec1_type = ir_context->get_def_use_mgr()->GetDef(vec1_type_id);
if (vec1_type->opcode() != SpvOpTypeVector) {
return false;
}
if (ir_context->get_def_use_mgr()->GetDef(vec1_type_id)->opcode() !=
SpvOpTypeVector) {
// A suitable vector for the result type of the new vector instruction must
// exist in the module. This is a vector of the right length, whose element
// type matches the result type of the scalar instruction.
uint32_t vector_size = vec1_type->GetSingleWordInOperand(1);
if (!fuzzerutil::MaybeGetVectorType(ir_context, instruction->type_id(),
vector_size)) {
return false;
}
@ -124,9 +137,11 @@ void TransformationWrapVectorSynonym::Apply(
// Make a new arithmetic instruction: %fresh_id = OpXX %type_id %result_id1
// %result_id2.
auto vec_type_id = ir_context->get_def_use_mgr()
->GetDef(message_.vector_operand1())
->type_id();
auto vector_operand_type = ir_context->get_def_use_mgr()->GetDef(
fuzzerutil::GetTypeId(ir_context, message_.vector_operand1()));
uint32_t vector_size = vector_operand_type->GetSingleWordInOperand(1);
auto vec_type_id = fuzzerutil::MaybeGetVectorType(
ir_context, instruction->type_id(), vector_size);
auto new_instruction = MakeUnique<opt::Instruction>(
ir_context, instruction->opcode(), vec_type_id, message_.fresh_id(),
std::move(in_operands));

Просмотреть файл

@ -38,11 +38,14 @@ class TransformationWrapVectorSynonym : public Transformation {
// two vector operands.
// - |fresh_id| is an unused id that will be used as a result id of the
// created instruction.
// - |vector_operand1| and |vector_operand2| must have the same vector type
// that is supported by this transformation.
// - |vector_operand1| and |vector_operand2| must have compatible vector types
// that are supported by this transformation.
// - |pos| is an index of the operands of |instruction_id| in the
// |vector_operand1| and |vector_operand2|. It must be less than the size
// of those vector operands.
// - A vector type with the same width as the types of the vector operands,
// and element type matching the type of |instruction_id|, must exist in the
// module.
bool IsApplicable(
opt::IRContext* ir_context,
const TransformationContext& transformation_context) const override;

Просмотреть файл

@ -1549,6 +1549,259 @@ TEST(FuzzerutilTest, FuzzerUtilMaybeGetZeroConstantTest) {
float_ids.end());
}
TEST(FuzzerutilTest, TypesAreCompatible) {
const std::string shader = R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %4 "main"
OpExecutionMode %4 OriginUpperLeft
OpSource ESSL 320
%2 = OpTypeVoid
%3 = OpTypeFunction %2
%6 = OpTypeInt 32 1
%9 = OpTypeInt 32 0
%8 = OpTypeStruct %6
%10 = OpTypePointer StorageBuffer %8
%11 = OpVariable %10 StorageBuffer
%86 = OpTypeStruct %9
%87 = OpTypePointer Workgroup %86
%88 = OpVariable %87 Workgroup
%89 = OpTypePointer Workgroup %9
%19 = OpConstant %9 0
%18 = OpConstant %9 1
%12 = OpConstant %6 0
%13 = OpTypePointer StorageBuffer %6
%15 = OpConstant %6 2
%16 = OpConstant %6 7
%20 = OpConstant %9 64
%4 = OpFunction %2 None %3
%5 = OpLabel
%14 = OpAccessChain %13 %11 %12
%90 = OpAccessChain %89 %88 %19
%21 = OpAtomicLoad %6 %14 %15 %20
%22 = OpAtomicExchange %6 %14 %15 %20 %16
%23 = OpAtomicCompareExchange %6 %14 %15 %20 %12 %16 %15
%24 = OpAtomicIIncrement %6 %14 %15 %20
%25 = OpAtomicIDecrement %6 %14 %15 %20
%26 = OpAtomicIAdd %6 %14 %15 %20 %16
%27 = OpAtomicISub %6 %14 %15 %20 %16
%28 = OpAtomicSMin %6 %14 %15 %20 %16
%29 = OpAtomicUMin %9 %90 %15 %20 %18
%30 = OpAtomicSMax %6 %14 %15 %20 %15
%31 = OpAtomicUMax %9 %90 %15 %20 %18
%32 = OpAtomicAnd %6 %14 %15 %20 %16
%33 = OpAtomicOr %6 %14 %15 %20 %16
%34 = OpAtomicXor %6 %14 %15 %20 %16
OpAtomicStore %14 %15 %20 %16
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));
const uint32_t int_type = 6; // The id of OpTypeInt 32 1
const uint32_t uint_type = 9; // The id of OpTypeInt 32 0
// OpAtomicLoad
#ifndef NDEBUG
ASSERT_DEATH(fuzzerutil::TypesAreCompatible(context.get(), SpvOpAtomicLoad, 0,
int_type, uint_type),
"Signedness check should not occur on a pointer operand.");
#endif
ASSERT_TRUE(fuzzerutil::TypesAreCompatible(context.get(), SpvOpAtomicLoad, 1,
int_type, uint_type));
ASSERT_TRUE(fuzzerutil::TypesAreCompatible(context.get(), SpvOpAtomicLoad, 2,
int_type, uint_type));
// OpAtomicExchange
#ifndef NDEBUG
ASSERT_DEATH(fuzzerutil::TypesAreCompatible(
context.get(), SpvOpAtomicExchange, 0, int_type, uint_type),
"Signedness check should not occur on a pointer operand.");
#endif
ASSERT_TRUE(fuzzerutil::TypesAreCompatible(context.get(), SpvOpAtomicExchange,
1, int_type, uint_type));
ASSERT_TRUE(fuzzerutil::TypesAreCompatible(context.get(), SpvOpAtomicExchange,
2, int_type, uint_type));
ASSERT_FALSE(fuzzerutil::TypesAreCompatible(
context.get(), SpvOpAtomicExchange, 3, int_type, uint_type));
// OpAtomicStore
#ifndef NDEBUG
ASSERT_DEATH(fuzzerutil::TypesAreCompatible(context.get(), SpvOpAtomicStore,
0, int_type, uint_type),
"Signedness check should not occur on a pointer operand.");
#endif
ASSERT_TRUE(fuzzerutil::TypesAreCompatible(context.get(), SpvOpAtomicStore, 1,
int_type, uint_type));
ASSERT_TRUE(fuzzerutil::TypesAreCompatible(context.get(), SpvOpAtomicStore, 2,
int_type, uint_type));
ASSERT_FALSE(fuzzerutil::TypesAreCompatible(context.get(), SpvOpAtomicStore,
3, int_type, uint_type));
// OpAtomicCompareExchange
#ifndef NDEBUG
ASSERT_DEATH(
fuzzerutil::TypesAreCompatible(context.get(), SpvOpAtomicCompareExchange,
0, int_type, uint_type),
"Signedness check should not occur on a pointer operand.");
#endif
ASSERT_TRUE(fuzzerutil::TypesAreCompatible(
context.get(), SpvOpAtomicCompareExchange, 1, int_type, uint_type));
ASSERT_TRUE(fuzzerutil::TypesAreCompatible(
context.get(), SpvOpAtomicCompareExchange, 2, int_type, uint_type));
ASSERT_TRUE(fuzzerutil::TypesAreCompatible(
context.get(), SpvOpAtomicCompareExchange, 3, int_type, uint_type));
ASSERT_FALSE(fuzzerutil::TypesAreCompatible(
context.get(), SpvOpAtomicCompareExchange, 4, int_type, uint_type));
// OpAtomicIIncrement
#ifndef NDEBUG
ASSERT_DEATH(
fuzzerutil::TypesAreCompatible(context.get(), SpvOpAtomicIIncrement, 0,
int_type, uint_type),
"Signedness check should not occur on a pointer operand.");
#endif
ASSERT_TRUE(fuzzerutil::TypesAreCompatible(
context.get(), SpvOpAtomicIIncrement, 1, int_type, uint_type));
ASSERT_TRUE(fuzzerutil::TypesAreCompatible(
context.get(), SpvOpAtomicIIncrement, 2, int_type, uint_type));
// OpAtomicIDecrement
#ifndef NDEBUG
ASSERT_DEATH(fuzzerutil::TypesAreCompatible(context.get(), SpvOpAtomicStore,
0, int_type, uint_type),
"Signedness check should not occur on a pointer operand.");
#endif
ASSERT_TRUE(fuzzerutil::TypesAreCompatible(context.get(), SpvOpAtomicStore, 1,
int_type, uint_type));
ASSERT_TRUE(fuzzerutil::TypesAreCompatible(context.get(), SpvOpAtomicStore, 2,
int_type, uint_type));
// OpAtomicIAdd
#ifndef NDEBUG
ASSERT_DEATH(fuzzerutil::TypesAreCompatible(context.get(), SpvOpAtomicIAdd, 0,
int_type, uint_type),
"Signedness check should not occur on a pointer operand.");
#endif
ASSERT_TRUE(fuzzerutil::TypesAreCompatible(context.get(), SpvOpAtomicIAdd, 1,
int_type, uint_type));
ASSERT_TRUE(fuzzerutil::TypesAreCompatible(context.get(), SpvOpAtomicIAdd, 2,
int_type, uint_type));
ASSERT_FALSE(fuzzerutil::TypesAreCompatible(context.get(), SpvOpAtomicIAdd, 3,
int_type, uint_type));
// OpAtomicISub
#ifndef NDEBUG
ASSERT_DEATH(fuzzerutil::TypesAreCompatible(context.get(), SpvOpAtomicISub, 0,
int_type, uint_type),
"Signedness check should not occur on a pointer operand.");
#endif
ASSERT_TRUE(fuzzerutil::TypesAreCompatible(context.get(), SpvOpAtomicISub, 1,
int_type, uint_type));
ASSERT_TRUE(fuzzerutil::TypesAreCompatible(context.get(), SpvOpAtomicISub, 2,
int_type, uint_type));
ASSERT_FALSE(fuzzerutil::TypesAreCompatible(context.get(), SpvOpAtomicISub, 3,
int_type, uint_type));
// OpAtomicSMin
#ifndef NDEBUG
ASSERT_DEATH(fuzzerutil::TypesAreCompatible(context.get(), SpvOpAtomicSMin, 0,
int_type, uint_type),
"Signedness check should not occur on a pointer operand.");
#endif
ASSERT_TRUE(fuzzerutil::TypesAreCompatible(context.get(), SpvOpAtomicSMin, 1,
int_type, uint_type));
ASSERT_TRUE(fuzzerutil::TypesAreCompatible(context.get(), SpvOpAtomicSMin, 2,
int_type, uint_type));
ASSERT_FALSE(fuzzerutil::TypesAreCompatible(context.get(), SpvOpAtomicSMin, 3,
int_type, uint_type));
// OpAtomicUMin
#ifndef NDEBUG
ASSERT_DEATH(fuzzerutil::TypesAreCompatible(context.get(), SpvOpAtomicUMin, 0,
int_type, uint_type),
"Signedness check should not occur on a pointer operand.");
#endif
ASSERT_TRUE(fuzzerutil::TypesAreCompatible(context.get(), SpvOpAtomicUMin, 1,
int_type, uint_type));
ASSERT_TRUE(fuzzerutil::TypesAreCompatible(context.get(), SpvOpAtomicUMin, 2,
int_type, uint_type));
ASSERT_FALSE(fuzzerutil::TypesAreCompatible(context.get(), SpvOpAtomicUMin, 3,
int_type, uint_type));
// OpAtomicSMax
#ifndef NDEBUG
ASSERT_DEATH(fuzzerutil::TypesAreCompatible(context.get(), SpvOpAtomicSMax, 0,
int_type, uint_type),
"Signedness check should not occur on a pointer operand.");
#endif
ASSERT_TRUE(fuzzerutil::TypesAreCompatible(context.get(), SpvOpAtomicSMax, 1,
int_type, uint_type));
ASSERT_TRUE(fuzzerutil::TypesAreCompatible(context.get(), SpvOpAtomicSMax, 2,
int_type, uint_type));
ASSERT_FALSE(fuzzerutil::TypesAreCompatible(context.get(), SpvOpAtomicSMax, 3,
int_type, uint_type));
// OpAtomicUMax
#ifndef NDEBUG
ASSERT_DEATH(fuzzerutil::TypesAreCompatible(context.get(), SpvOpAtomicUMax, 0,
int_type, uint_type),
"Signedness check should not occur on a pointer operand.");
#endif
ASSERT_TRUE(fuzzerutil::TypesAreCompatible(context.get(), SpvOpAtomicUMax, 1,
int_type, uint_type));
ASSERT_TRUE(fuzzerutil::TypesAreCompatible(context.get(), SpvOpAtomicUMax, 2,
int_type, uint_type));
ASSERT_FALSE(fuzzerutil::TypesAreCompatible(context.get(), SpvOpAtomicUMax, 3,
int_type, uint_type));
// OpAtomicAnd
#ifndef NDEBUG
ASSERT_DEATH(fuzzerutil::TypesAreCompatible(context.get(), SpvOpAtomicAnd, 0,
int_type, uint_type),
"Signedness check should not occur on a pointer operand.");
#endif
ASSERT_TRUE(fuzzerutil::TypesAreCompatible(context.get(), SpvOpAtomicAnd, 1,
int_type, uint_type));
ASSERT_TRUE(fuzzerutil::TypesAreCompatible(context.get(), SpvOpAtomicAnd, 2,
int_type, uint_type));
ASSERT_FALSE(fuzzerutil::TypesAreCompatible(context.get(), SpvOpAtomicAnd, 3,
int_type, uint_type));
// OpAtomicOr
#ifndef NDEBUG
ASSERT_DEATH(fuzzerutil::TypesAreCompatible(context.get(), SpvOpAtomicOr, 0,
int_type, uint_type),
"Signedness check should not occur on a pointer operand.");
#endif
ASSERT_TRUE(fuzzerutil::TypesAreCompatible(context.get(), SpvOpAtomicOr, 1,
int_type, uint_type));
ASSERT_TRUE(fuzzerutil::TypesAreCompatible(context.get(), SpvOpAtomicOr, 2,
int_type, uint_type));
ASSERT_FALSE(fuzzerutil::TypesAreCompatible(context.get(), SpvOpAtomicOr, 3,
int_type, uint_type));
// OpAtomicXor
#ifndef NDEBUG
ASSERT_DEATH(fuzzerutil::TypesAreCompatible(context.get(), SpvOpAtomicXor, 0,
int_type, uint_type),
"Signedness check should not occur on a pointer operand.");
#endif
ASSERT_TRUE(fuzzerutil::TypesAreCompatible(context.get(), SpvOpAtomicXor, 1,
int_type, uint_type));
ASSERT_TRUE(fuzzerutil::TypesAreCompatible(context.get(), SpvOpAtomicXor, 2,
int_type, uint_type));
ASSERT_FALSE(fuzzerutil::TypesAreCompatible(context.get(), SpvOpAtomicXor, 3,
int_type, uint_type));
}
} // namespace
} // namespace fuzz
} // namespace spvtools

Просмотреть файл

@ -2173,262 +2173,6 @@ TEST(TransformationReplaceIdWithSynonymTest,
ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
}
// TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/4345): Improve this
// test so that it covers more atomic operations, and enable the test once the
// issue is fixed.
TEST(TransformationReplaceIdWithSynonymTest, TypesAreCompatible) {
const std::string shader = R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %4 "main"
OpExecutionMode %4 OriginUpperLeft
OpSource ESSL 320
%2 = OpTypeVoid
%3 = OpTypeFunction %2
%6 = OpTypeInt 32 1
%9 = OpTypeInt 32 0
%8 = OpTypeStruct %6
%10 = OpTypePointer StorageBuffer %8
%11 = OpVariable %10 StorageBuffer
%86 = OpTypeStruct %9
%87 = OpTypePointer Workgroup %86
%88 = OpVariable %87 Workgroup
%89 = OpTypePointer Workgroup %9
%19 = OpConstant %9 0
%18 = OpConstant %9 1
%12 = OpConstant %6 0
%13 = OpTypePointer StorageBuffer %6
%15 = OpConstant %6 2
%16 = OpConstant %6 7
%20 = OpConstant %9 64
%4 = OpFunction %2 None %3
%5 = OpLabel
%14 = OpAccessChain %13 %11 %12
%90 = OpAccessChain %89 %88 %19
%21 = OpAtomicLoad %6 %14 %15 %20
%22 = OpAtomicExchange %6 %14 %15 %20 %16
%23 = OpAtomicCompareExchange %6 %14 %15 %20 %12 %16 %15
%24 = OpAtomicIIncrement %6 %14 %15 %20
%25 = OpAtomicIDecrement %6 %14 %15 %20
%26 = OpAtomicIAdd %6 %14 %15 %20 %16
%27 = OpAtomicISub %6 %14 %15 %20 %16
%28 = OpAtomicSMin %6 %14 %15 %20 %16
%29 = OpAtomicUMin %9 %90 %15 %20 %18
%30 = OpAtomicSMax %6 %14 %15 %20 %15
%31 = OpAtomicUMax %9 %90 %15 %20 %18
%32 = OpAtomicAnd %6 %14 %15 %20 %16
%33 = OpAtomicOr %6 %14 %15 %20 %16
%34 = OpAtomicXor %6 %14 %15 %20 %16
OpAtomicStore %14 %15 %20 %16
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));
const uint32_t int_type = 6; // The id of OpTypeInt 32 1
const uint32_t uint_type = 9; // The id of OpTypeInt 32 0
// OpAtomicLoad
#ifndef NDEBUG
ASSERT_DEATH(TransformationReplaceIdWithSynonym::TypesAreCompatible(
context.get(), SpvOpAtomicLoad, 0, int_type, uint_type),
"Signedness check should not occur on a pointer operand.");
#endif
ASSERT_TRUE(TransformationReplaceIdWithSynonym::TypesAreCompatible(
context.get(), SpvOpAtomicLoad, 1, int_type, uint_type));
ASSERT_TRUE(TransformationReplaceIdWithSynonym::TypesAreCompatible(
context.get(), SpvOpAtomicLoad, 2, int_type, uint_type));
// OpAtomicExchange
#ifndef NDEBUG
ASSERT_DEATH(TransformationReplaceIdWithSynonym::TypesAreCompatible(
context.get(), SpvOpAtomicExchange, 0, int_type, uint_type),
"Signedness check should not occur on a pointer operand.");
#endif
ASSERT_TRUE(TransformationReplaceIdWithSynonym::TypesAreCompatible(
context.get(), SpvOpAtomicExchange, 1, int_type, uint_type));
ASSERT_TRUE(TransformationReplaceIdWithSynonym::TypesAreCompatible(
context.get(), SpvOpAtomicExchange, 2, int_type, uint_type));
ASSERT_FALSE(TransformationReplaceIdWithSynonym::TypesAreCompatible(
context.get(), SpvOpAtomicExchange, 3, int_type, uint_type));
// OpAtomicStore
#ifndef NDEBUG
ASSERT_DEATH(TransformationReplaceIdWithSynonym::TypesAreCompatible(
context.get(), SpvOpAtomicStore, 0, int_type, uint_type),
"Signedness check should not occur on a pointer operand.");
#endif
ASSERT_TRUE(TransformationReplaceIdWithSynonym::TypesAreCompatible(
context.get(), SpvOpAtomicStore, 1, int_type, uint_type));
ASSERT_TRUE(TransformationReplaceIdWithSynonym::TypesAreCompatible(
context.get(), SpvOpAtomicStore, 2, int_type, uint_type));
ASSERT_FALSE(TransformationReplaceIdWithSynonym::TypesAreCompatible(
context.get(), SpvOpAtomicStore, 3, int_type, uint_type));
// OpAtomicCompareExchange
#ifndef NDEBUG
ASSERT_DEATH(
TransformationReplaceIdWithSynonym::TypesAreCompatible(
context.get(), SpvOpAtomicCompareExchange, 0, int_type, uint_type),
"Signedness check should not occur on a pointer operand.");
#endif
ASSERT_TRUE(TransformationReplaceIdWithSynonym::TypesAreCompatible(
context.get(), SpvOpAtomicCompareExchange, 1, int_type, uint_type));
ASSERT_TRUE(TransformationReplaceIdWithSynonym::TypesAreCompatible(
context.get(), SpvOpAtomicCompareExchange, 2, int_type, uint_type));
ASSERT_TRUE(TransformationReplaceIdWithSynonym::TypesAreCompatible(
context.get(), SpvOpAtomicCompareExchange, 3, int_type, uint_type));
ASSERT_FALSE(TransformationReplaceIdWithSynonym::TypesAreCompatible(
context.get(), SpvOpAtomicCompareExchange, 4, int_type, uint_type));
// OpAtomicIIncrement
#ifndef NDEBUG
ASSERT_DEATH(
TransformationReplaceIdWithSynonym::TypesAreCompatible(
context.get(), SpvOpAtomicIIncrement, 0, int_type, uint_type),
"Signedness check should not occur on a pointer operand.");
#endif
ASSERT_TRUE(TransformationReplaceIdWithSynonym::TypesAreCompatible(
context.get(), SpvOpAtomicIIncrement, 1, int_type, uint_type));
ASSERT_TRUE(TransformationReplaceIdWithSynonym::TypesAreCompatible(
context.get(), SpvOpAtomicIIncrement, 2, int_type, uint_type));
// OpAtomicIDecrement
#ifndef NDEBUG
ASSERT_DEATH(TransformationReplaceIdWithSynonym::TypesAreCompatible(
context.get(), SpvOpAtomicStore, 0, int_type, uint_type),
"Signedness check should not occur on a pointer operand.");
#endif
ASSERT_TRUE(TransformationReplaceIdWithSynonym::TypesAreCompatible(
context.get(), SpvOpAtomicStore, 1, int_type, uint_type));
ASSERT_TRUE(TransformationReplaceIdWithSynonym::TypesAreCompatible(
context.get(), SpvOpAtomicStore, 2, int_type, uint_type));
// OpAtomicIAdd
#ifndef NDEBUG
ASSERT_DEATH(TransformationReplaceIdWithSynonym::TypesAreCompatible(
context.get(), SpvOpAtomicIAdd, 0, int_type, uint_type),
"Signedness check should not occur on a pointer operand.");
#endif
ASSERT_TRUE(TransformationReplaceIdWithSynonym::TypesAreCompatible(
context.get(), SpvOpAtomicIAdd, 1, int_type, uint_type));
ASSERT_TRUE(TransformationReplaceIdWithSynonym::TypesAreCompatible(
context.get(), SpvOpAtomicIAdd, 2, int_type, uint_type));
ASSERT_FALSE(TransformationReplaceIdWithSynonym::TypesAreCompatible(
context.get(), SpvOpAtomicIAdd, 3, int_type, uint_type));
// OpAtomicISub
#ifndef NDEBUG
ASSERT_DEATH(TransformationReplaceIdWithSynonym::TypesAreCompatible(
context.get(), SpvOpAtomicISub, 0, int_type, uint_type),
"Signedness check should not occur on a pointer operand.");
#endif
ASSERT_TRUE(TransformationReplaceIdWithSynonym::TypesAreCompatible(
context.get(), SpvOpAtomicISub, 1, int_type, uint_type));
ASSERT_TRUE(TransformationReplaceIdWithSynonym::TypesAreCompatible(
context.get(), SpvOpAtomicISub, 2, int_type, uint_type));
ASSERT_FALSE(TransformationReplaceIdWithSynonym::TypesAreCompatible(
context.get(), SpvOpAtomicISub, 3, int_type, uint_type));
// OpAtomicSMin
#ifndef NDEBUG
ASSERT_DEATH(TransformationReplaceIdWithSynonym::TypesAreCompatible(
context.get(), SpvOpAtomicSMin, 0, int_type, uint_type),
"Signedness check should not occur on a pointer operand.");
#endif
ASSERT_TRUE(TransformationReplaceIdWithSynonym::TypesAreCompatible(
context.get(), SpvOpAtomicSMin, 1, int_type, uint_type));
ASSERT_TRUE(TransformationReplaceIdWithSynonym::TypesAreCompatible(
context.get(), SpvOpAtomicSMin, 2, int_type, uint_type));
ASSERT_FALSE(TransformationReplaceIdWithSynonym::TypesAreCompatible(
context.get(), SpvOpAtomicSMin, 3, int_type, uint_type));
// OpAtomicUMin
#ifndef NDEBUG
ASSERT_DEATH(TransformationReplaceIdWithSynonym::TypesAreCompatible(
context.get(), SpvOpAtomicUMin, 0, int_type, uint_type),
"Signedness check should not occur on a pointer operand.");
#endif
ASSERT_TRUE(TransformationReplaceIdWithSynonym::TypesAreCompatible(
context.get(), SpvOpAtomicUMin, 1, int_type, uint_type));
ASSERT_TRUE(TransformationReplaceIdWithSynonym::TypesAreCompatible(
context.get(), SpvOpAtomicUMin, 2, int_type, uint_type));
ASSERT_FALSE(TransformationReplaceIdWithSynonym::TypesAreCompatible(
context.get(), SpvOpAtomicUMin, 3, int_type, uint_type));
// OpAtomicSMax
#ifndef NDEBUG
ASSERT_DEATH(TransformationReplaceIdWithSynonym::TypesAreCompatible(
context.get(), SpvOpAtomicSMax, 0, int_type, uint_type),
"Signedness check should not occur on a pointer operand.");
#endif
ASSERT_TRUE(TransformationReplaceIdWithSynonym::TypesAreCompatible(
context.get(), SpvOpAtomicSMax, 1, int_type, uint_type));
ASSERT_TRUE(TransformationReplaceIdWithSynonym::TypesAreCompatible(
context.get(), SpvOpAtomicSMax, 2, int_type, uint_type));
ASSERT_FALSE(TransformationReplaceIdWithSynonym::TypesAreCompatible(
context.get(), SpvOpAtomicSMax, 3, int_type, uint_type));
// OpAtomicUMax
#ifndef NDEBUG
ASSERT_DEATH(TransformationReplaceIdWithSynonym::TypesAreCompatible(
context.get(), SpvOpAtomicUMax, 0, int_type, uint_type),
"Signedness check should not occur on a pointer operand.");
#endif
ASSERT_TRUE(TransformationReplaceIdWithSynonym::TypesAreCompatible(
context.get(), SpvOpAtomicUMax, 1, int_type, uint_type));
ASSERT_TRUE(TransformationReplaceIdWithSynonym::TypesAreCompatible(
context.get(), SpvOpAtomicUMax, 2, int_type, uint_type));
ASSERT_FALSE(TransformationReplaceIdWithSynonym::TypesAreCompatible(
context.get(), SpvOpAtomicUMax, 3, int_type, uint_type));
// OpAtomicAnd
#ifndef NDEBUG
ASSERT_DEATH(TransformationReplaceIdWithSynonym::TypesAreCompatible(
context.get(), SpvOpAtomicAnd, 0, int_type, uint_type),
"Signedness check should not occur on a pointer operand.");
#endif
ASSERT_TRUE(TransformationReplaceIdWithSynonym::TypesAreCompatible(
context.get(), SpvOpAtomicAnd, 1, int_type, uint_type));
ASSERT_TRUE(TransformationReplaceIdWithSynonym::TypesAreCompatible(
context.get(), SpvOpAtomicAnd, 2, int_type, uint_type));
ASSERT_FALSE(TransformationReplaceIdWithSynonym::TypesAreCompatible(
context.get(), SpvOpAtomicAnd, 3, int_type, uint_type));
// OpAtomicOr
#ifndef NDEBUG
ASSERT_DEATH(TransformationReplaceIdWithSynonym::TypesAreCompatible(
context.get(), SpvOpAtomicOr, 0, int_type, uint_type),
"Signedness check should not occur on a pointer operand.");
#endif
ASSERT_TRUE(TransformationReplaceIdWithSynonym::TypesAreCompatible(
context.get(), SpvOpAtomicOr, 1, int_type, uint_type));
ASSERT_TRUE(TransformationReplaceIdWithSynonym::TypesAreCompatible(
context.get(), SpvOpAtomicOr, 2, int_type, uint_type));
ASSERT_FALSE(TransformationReplaceIdWithSynonym::TypesAreCompatible(
context.get(), SpvOpAtomicOr, 3, int_type, uint_type));
// OpAtomicXor
#ifndef NDEBUG
ASSERT_DEATH(TransformationReplaceIdWithSynonym::TypesAreCompatible(
context.get(), SpvOpAtomicXor, 0, int_type, uint_type),
"Signedness check should not occur on a pointer operand.");
#endif
ASSERT_TRUE(TransformationReplaceIdWithSynonym::TypesAreCompatible(
context.get(), SpvOpAtomicXor, 1, int_type, uint_type));
ASSERT_TRUE(TransformationReplaceIdWithSynonym::TypesAreCompatible(
context.get(), SpvOpAtomicXor, 2, int_type, uint_type));
ASSERT_FALSE(TransformationReplaceIdWithSynonym::TypesAreCompatible(
context.get(), SpvOpAtomicXor, 3, int_type, uint_type));
}
} // namespace
} // namespace fuzz
} // namespace spvtools

Просмотреть файл

@ -1389,6 +1389,165 @@ TEST(TransformationWrapVectorSynonym, AdditionalWidthSupportTest) {
ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
}
TEST(TransformationWrapVectorSynonym, DifferentVectorSignedness) {
std::string shader = R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %4 "main"
OpExecutionMode %4 OriginUpperLeft
OpSource ESSL 320
%2 = OpTypeVoid
%3 = OpTypeFunction %2
%6 = OpTypeInt 32 1
%7 = OpTypeVector %6 2
%8 = OpTypePointer Function %7
%10 = OpConstant %6 1
%11 = OpConstant %6 0
%12 = OpConstantComposite %7 %10 %11
%14 = OpTypeInt 32 0
%15 = OpTypeVector %14 2
%18 = OpConstant %14 3
%19 = OpConstant %14 0
%20 = OpConstantComposite %15 %18 %19
%21 = OpConstantComposite %15 %19 %18
%4 = OpFunction %2 None %3
%5 = OpLabel
%100 = OpIAdd %14 %10 %18
%101 = OpIAdd %6 %10 %18
%102 = OpIAdd %6 %18 %19
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;
// Check context validity.
ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
kConsoleMessageConsumer));
TransformationContext transformation_context(
MakeUnique<FactManager>(context.get()), validator_options);
transformation_context.GetFactManager()->AddFactDataSynonym(
MakeDataDescriptor(10, {}), MakeDataDescriptor(12, {0}));
transformation_context.GetFactManager()->AddFactDataSynonym(
MakeDataDescriptor(18, {}), MakeDataDescriptor(20, {0}));
transformation_context.GetFactManager()->AddFactDataSynonym(
MakeDataDescriptor(19, {}), MakeDataDescriptor(21, {0}));
{
TransformationWrapVectorSynonym transformation1(100, 12, 20, 200, 0);
ASSERT_TRUE(
transformation1.IsApplicable(context.get(), transformation_context));
ApplyAndCheckFreshIds(transformation1, context.get(),
&transformation_context);
ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
MakeDataDescriptor(200, {0}), MakeDataDescriptor(100, {})));
}
{
TransformationWrapVectorSynonym transformation2(101, 12, 20, 201, 0);
ASSERT_TRUE(
transformation2.IsApplicable(context.get(), transformation_context));
ApplyAndCheckFreshIds(transformation2, context.get(),
&transformation_context);
ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
MakeDataDescriptor(201, {0}), MakeDataDescriptor(101, {})));
}
{
TransformationWrapVectorSynonym transformation3(102, 20, 21, 202, 0);
ASSERT_TRUE(
transformation3.IsApplicable(context.get(), transformation_context));
ApplyAndCheckFreshIds(transformation3, context.get(),
&transformation_context);
ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
MakeDataDescriptor(202, {0}), MakeDataDescriptor(102, {})));
}
std::string after_transformation = R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %4 "main"
OpExecutionMode %4 OriginUpperLeft
OpSource ESSL 320
%2 = OpTypeVoid
%3 = OpTypeFunction %2
%6 = OpTypeInt 32 1
%7 = OpTypeVector %6 2
%8 = OpTypePointer Function %7
%10 = OpConstant %6 1
%11 = OpConstant %6 0
%12 = OpConstantComposite %7 %10 %11
%14 = OpTypeInt 32 0
%15 = OpTypeVector %14 2
%18 = OpConstant %14 3
%19 = OpConstant %14 0
%20 = OpConstantComposite %15 %18 %19
%21 = OpConstantComposite %15 %19 %18
%4 = OpFunction %2 None %3
%5 = OpLabel
%200 = OpIAdd %15 %12 %20
%100 = OpIAdd %14 %10 %18
%201 = OpIAdd %7 %12 %20
%101 = OpIAdd %6 %10 %18
%202 = OpIAdd %7 %20 %21
%102 = OpIAdd %6 %18 %19
OpReturn
OpFunctionEnd
)";
ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
}
TEST(TransformationWrapVectorSynonym, SignednessDoesNotMatchResultType) {
std::string shader = R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %4 "main"
OpExecutionMode %4 OriginUpperLeft
OpSource ESSL 320
%2 = OpTypeVoid
%3 = OpTypeFunction %2
%6 = OpTypeInt 32 1
%7 = OpTypeVector %6 2
%8 = OpTypePointer Function %7
%10 = OpConstant %6 1
%11 = OpConstant %6 0
%12 = OpConstantComposite %7 %10 %11
%13 = OpConstantComposite %7 %11 %10
%14 = OpTypeInt 32 0
%4 = OpFunction %2 None %3
%5 = OpLabel
%100 = OpIAdd %14 %10 %11
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;
// Check context validity.
ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
kConsoleMessageConsumer));
TransformationContext transformation_context(
MakeUnique<FactManager>(context.get()), validator_options);
transformation_context.GetFactManager()->AddFactDataSynonym(
MakeDataDescriptor(10, {}), MakeDataDescriptor(12, {0}));
transformation_context.GetFactManager()->AddFactDataSynonym(
MakeDataDescriptor(11, {}), MakeDataDescriptor(13, {0}));
ASSERT_FALSE(TransformationWrapVectorSynonym(100, 12, 13, 200, 0)
.IsApplicable(context.get(), transformation_context));
}
} // namespace
} // namespace fuzz
} // namespace spvtools