spirv-fuzz: TransformationReplaceAddSubMulWithCarryingExtended (#3598)

Replaces OpIAdd with OpIAddCarry, OpISub with OpISubBorrow, OpIMul with
OpUMulExtended or OpSMulExtended and stores the result into a fresh_id
representing a structure. Extracts the first element of the result into
the original result_id. This value is the same as the result of the
original instruction.

Fixes #3577
This commit is contained in:
Antoni Karpiński 2020-08-06 16:30:34 +00:00 коммит произвёл GitHub
Родитель 6d7f34fbfe
Коммит 7b2dd11dda
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
11 изменённых файлов: 1070 добавлений и 4 удалений

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

@ -76,6 +76,7 @@ if(SPIRV_BUILD_FUZZER)
fuzzer_pass_permute_instructions.h
fuzzer_pass_permute_phi_operands.h
fuzzer_pass_push_ids_through_variables.h
fuzzer_pass_replace_adds_subs_muls_with_carrying_extended.h
fuzzer_pass_replace_copy_memories_with_loads_stores.h
fuzzer_pass_replace_copy_objects_with_stores_loads.h
fuzzer_pass_replace_linear_algebra_instructions.h
@ -143,6 +144,7 @@ if(SPIRV_BUILD_FUZZER)
transformation_permute_phi_operands.h
transformation_push_id_through_variable.h
transformation_record_synonymous_constants.h
transformation_replace_add_sub_mul_with_carrying_extended.h
transformation_replace_boolean_constant_with_constant_binary.h
transformation_replace_constant_with_uniform.h
transformation_replace_copy_memory_with_load_store.h
@ -211,6 +213,7 @@ if(SPIRV_BUILD_FUZZER)
fuzzer_pass_permute_instructions.cpp
fuzzer_pass_permute_phi_operands.cpp
fuzzer_pass_push_ids_through_variables.cpp
fuzzer_pass_replace_adds_subs_muls_with_carrying_extended.cpp
fuzzer_pass_replace_copy_memories_with_loads_stores.cpp
fuzzer_pass_replace_copy_objects_with_stores_loads.cpp
fuzzer_pass_replace_linear_algebra_instructions.cpp
@ -277,6 +280,7 @@ if(SPIRV_BUILD_FUZZER)
transformation_permute_phi_operands.cpp
transformation_push_id_through_variable.cpp
transformation_record_synonymous_constants.cpp
transformation_replace_add_sub_mul_with_carrying_extended.cpp
transformation_replace_boolean_constant_with_constant_binary.cpp
transformation_replace_constant_with_uniform.cpp
transformation_replace_copy_memory_with_load_store.cpp

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

@ -82,6 +82,8 @@ const std::pair<uint32_t, uint32_t> kChanceOfPermutingInstructions = {20, 70};
const std::pair<uint32_t, uint32_t> kChanceOfPermutingParameters = {30, 90};
const std::pair<uint32_t, uint32_t> kChanceOfPermutingPhiOperands = {30, 90};
const std::pair<uint32_t, uint32_t> kChanceOfPushingIdThroughVariable = {5, 50};
const std::pair<uint32_t, uint32_t>
kChanceOfReplacingAddSubMulWithCarryingExtended = {20, 90};
const std::pair<uint32_t, uint32_t> kChanceOfReplacingCopyMemoryWithLoadStore =
{20, 90};
const std::pair<uint32_t, uint32_t> kChanceOfReplacingCopyObjectWithStoreLoad =
@ -227,6 +229,8 @@ FuzzerContext::FuzzerContext(RandomGenerator* random_generator,
ChooseBetweenMinAndMax(kChanceOfPermutingPhiOperands);
chance_of_pushing_id_through_variable_ =
ChooseBetweenMinAndMax(kChanceOfPushingIdThroughVariable);
chance_of_replacing_add_sub_mul_with_carrying_extended_ =
ChooseBetweenMinAndMax(kChanceOfReplacingAddSubMulWithCarryingExtended);
chance_of_replacing_copy_memory_with_load_store_ =
ChooseBetweenMinAndMax(kChanceOfReplacingCopyMemoryWithLoadStore);
chance_of_replacing_copyobject_with_store_load_ =

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

@ -221,6 +221,9 @@ class FuzzerContext {
uint32_t GetChanceOfPushingIdThroughVariable() {
return chance_of_pushing_id_through_variable_;
}
uint32_t GetChanceOfReplacingAddSubMulWithCarryingExtended() {
return chance_of_replacing_add_sub_mul_with_carrying_extended_;
}
uint32_t GetChanceOfReplacingCopyMemoryWithLoadStore() {
return chance_of_replacing_copy_memory_with_load_store_;
}
@ -375,6 +378,7 @@ class FuzzerContext {
uint32_t chance_of_permuting_parameters_;
uint32_t chance_of_permuting_phi_operands_;
uint32_t chance_of_pushing_id_through_variable_;
uint32_t chance_of_replacing_add_sub_mul_with_carrying_extended_;
uint32_t chance_of_replacing_copy_memory_with_load_store_;
uint32_t chance_of_replacing_copyobject_with_store_load_;
uint32_t chance_of_replacing_id_with_synonym_;

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

@ -0,0 +1,76 @@
// Copyright (c) 2020 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "source/fuzz/fuzzer_pass_replace_adds_subs_muls_with_carrying_extended.h"
#include "source/fuzz/fuzzer_util.h"
#include "source/fuzz/transformation_replace_add_sub_mul_with_carrying_extended.h"
namespace spvtools {
namespace fuzz {
namespace {
const uint32_t kArithmeticInstructionIndexLeftInOperand = 0;
} // namespace
FuzzerPassReplaceAddsSubsMulsWithCarryingExtended::
FuzzerPassReplaceAddsSubsMulsWithCarryingExtended(
opt::IRContext* ir_context,
TransformationContext* transformation_context,
FuzzerContext* fuzzer_context,
protobufs::TransformationSequence* transformations)
: FuzzerPass(ir_context, transformation_context, fuzzer_context,
transformations) {}
FuzzerPassReplaceAddsSubsMulsWithCarryingExtended::
~FuzzerPassReplaceAddsSubsMulsWithCarryingExtended() = default;
void FuzzerPassReplaceAddsSubsMulsWithCarryingExtended::Apply() {
for (auto& function : *GetIRContext()->module()) {
for (auto& block : function) {
for (auto& instruction : block) {
// Randomly decide whether to apply the transformation.
if (!GetFuzzerContext()->ChoosePercentage(
GetFuzzerContext()
->GetChanceOfReplacingAddSubMulWithCarryingExtended())) {
continue;
}
// Check if the transformation can be applied to this instruction.
if (!TransformationReplaceAddSubMulWithCarryingExtended::
IsInstructionSuitable(GetIRContext(), instruction)) {
continue;
}
// Get the operand type id. We know that both operands have the same
// type.
uint32_t operand_type_id =
GetIRContext()
->get_def_use_mgr()
->GetDef(instruction.GetSingleWordInOperand(
kArithmeticInstructionIndexLeftInOperand))
->type_id();
// Ensure the required struct type exists. The struct type is based on
// the operand type.
FindOrCreateStructType({operand_type_id, operand_type_id});
ApplyTransformation(TransformationReplaceAddSubMulWithCarryingExtended(
GetFuzzerContext()->GetFreshId(), instruction.result_id()));
}
}
}
}
} // namespace fuzz
} // namespace spvtools

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

@ -0,0 +1,42 @@
// Copyright (c) 2020 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef SPIRV_TOOLS_FUZZER_PASS_REPLACE_ADDS_SUBS_MULS_WITH_CARRYING_EXTENDED_H
#define SPIRV_TOOLS_FUZZER_PASS_REPLACE_ADDS_SUBS_MULS_WITH_CARRYING_EXTENDED_H
#include "source/fuzz/fuzzer_pass.h"
namespace spvtools {
namespace fuzz {
// A fuzzer pass that replaces instructions OpIAdd, OpISub, OpIMul with pairs of
// instructions. The first one (OpIAddCarry, OpISubBorrow, OpUMulExtended,
// OpSMulExtended) computes the result into a struct. The second one extracts
// the appropriate component from the struct to yield the original result.
class FuzzerPassReplaceAddsSubsMulsWithCarryingExtended : public FuzzerPass {
public:
FuzzerPassReplaceAddsSubsMulsWithCarryingExtended(
opt::IRContext* ir_context, TransformationContext* transformation_context,
FuzzerContext* fuzzer_context,
protobufs::TransformationSequence* transformations);
~FuzzerPassReplaceAddsSubsMulsWithCarryingExtended() override;
void Apply() override;
};
} // namespace fuzz
} // namespace spvtools
#endif // SPIRV_TOOLS_FUZZER_PASS_REPLACE_ADDS_SUBS_MULS_WITH_CARRYING_EXTENDED_H

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

@ -410,6 +410,7 @@ message Transformation {
TransformationAddLoopPreheader add_loop_preheader = 63;
TransformationMoveInstructionDown move_instruction_down = 64;
TransformationMakeVectorOperationDynamic make_vector_operation_dynamic = 65;
TransformationReplaceAddSubMulWithCarryingExtended replace_add_sub_mul_with_carrying_extended = 66;
// Add additional option using the next available number.
}
}
@ -1285,6 +1286,24 @@ message TransformationRecordSynonymousConstants {
}
message TransformationReplaceAddSubMulWithCarryingExtended {
// Replaces OpIAdd with OpIAddCarry, OpISub with OpISubBorrow, OpIMul
// with OpUMulExtended or OpSMulExtended (depending on the signedness
// of the operands) and stores the result into a |struct_fresh_id|.
// In the original instruction the result type id and the type ids of
// the operands must be the same. Then the transformation extracts
// the first element of the result into the original |result_id|.
// This value is the same as the result of the original instruction.
// The fresh id of the intermediate result.
uint32 struct_fresh_id = 1;
// The result id of the original instruction.
uint32 result_id = 2;
}
message TransformationReplaceParameterWithGlobal {
// Removes parameter with result id |parameter_id| from its function

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

@ -63,6 +63,7 @@
#include "source/fuzz/transformation_permute_phi_operands.h"
#include "source/fuzz/transformation_push_id_through_variable.h"
#include "source/fuzz/transformation_record_synonymous_constants.h"
#include "source/fuzz/transformation_replace_add_sub_mul_with_carrying_extended.h"
#include "source/fuzz/transformation_replace_boolean_constant_with_constant_binary.h"
#include "source/fuzz/transformation_replace_constant_with_uniform.h"
#include "source/fuzz/transformation_replace_copy_memory_with_load_store.h"
@ -221,9 +222,9 @@ std::unique_ptr<Transformation> Transformation::FromMessage(
return MakeUnique<TransformationRecordSynonymousConstants>(
message.record_synonymous_constants());
case protobufs::Transformation::TransformationCase::
kReplaceParameterWithGlobal:
return MakeUnique<TransformationReplaceParameterWithGlobal>(
message.replace_parameter_with_global());
kReplaceAddSubMulWithCarryingExtended:
return MakeUnique<TransformationReplaceAddSubMulWithCarryingExtended>(
message.replace_add_sub_mul_with_carrying_extended());
case protobufs::Transformation::TransformationCase::
kReplaceBooleanConstantWithConstantBinary:
return MakeUnique<TransformationReplaceBooleanConstantWithConstantBinary>(
@ -251,6 +252,10 @@ std::unique_ptr<Transformation> Transformation::FromMessage(
kReplaceLoadStoreWithCopyMemory:
return MakeUnique<TransformationReplaceLoadStoreWithCopyMemory>(
message.replace_load_store_with_copy_memory());
case protobufs::Transformation::TransformationCase::
kReplaceParameterWithGlobal:
return MakeUnique<TransformationReplaceParameterWithGlobal>(
message.replace_parameter_with_global());
case protobufs::Transformation::TransformationCase::
kReplaceParamsWithStruct:
return MakeUnique<TransformationReplaceParamsWithStruct>(

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

@ -0,0 +1,232 @@
// Copyright (c) 2020 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "source/fuzz/transformation_replace_add_sub_mul_with_carrying_extended.h"
#include "source/fuzz/fuzzer_util.h"
namespace spvtools {
namespace fuzz {
namespace {
const uint32_t kOpCompositeExtractIndexLowOrderBits = 0;
const uint32_t kArithmeticInstructionIndexLeftInOperand = 0;
const uint32_t kArithmeticInstructionIndexRightInOperand = 1;
} // namespace
TransformationReplaceAddSubMulWithCarryingExtended::
TransformationReplaceAddSubMulWithCarryingExtended(
const spvtools::fuzz::protobufs::
TransformationReplaceAddSubMulWithCarryingExtended& message)
: message_(message) {}
TransformationReplaceAddSubMulWithCarryingExtended::
TransformationReplaceAddSubMulWithCarryingExtended(uint32_t struct_fresh_id,
uint32_t result_id) {
message_.set_struct_fresh_id(struct_fresh_id);
message_.set_result_id(result_id);
}
bool TransformationReplaceAddSubMulWithCarryingExtended::IsApplicable(
opt::IRContext* ir_context, const TransformationContext& /*unused*/) const {
// |message_.struct_fresh_id| must be fresh.
if (!fuzzerutil::IsFreshId(ir_context, message_.struct_fresh_id())) {
return false;
}
// |message_.result_id| must refer to a suitable OpIAdd, OpISub or OpIMul
// instruction. The instruction must be defined.
auto instruction =
ir_context->get_def_use_mgr()->GetDef(message_.result_id());
if (instruction == nullptr) {
return false;
}
if (!TransformationReplaceAddSubMulWithCarryingExtended::
IsInstructionSuitable(ir_context, *instruction)) {
return false;
}
// The struct type for holding the intermediate result must exist in the
// module. The struct type is based on the operand type.
uint32_t operand_type_id = ir_context->get_def_use_mgr()
->GetDef(instruction->GetSingleWordInOperand(
kArithmeticInstructionIndexLeftInOperand))
->type_id();
uint32_t struct_type_id = fuzzerutil::MaybeGetStructType(
ir_context, {operand_type_id, operand_type_id});
if (struct_type_id == 0) {
return false;
}
return true;
}
void TransformationReplaceAddSubMulWithCarryingExtended::Apply(
opt::IRContext* ir_context, TransformationContext* /*unused*/) const {
// |message_.struct_fresh_id| must be fresh.
assert(fuzzerutil::IsFreshId(ir_context, message_.struct_fresh_id()) &&
"|message_.struct_fresh_id| must be fresh");
// Get the signedness of an operand if it is an int or the signedness of a
// component if it is a vector.
auto type_id =
ir_context->get_def_use_mgr()->GetDef(message_.result_id())->type_id();
auto type = ir_context->get_type_mgr()->GetType(type_id);
bool operand_is_signed;
if (type->kind() == opt::analysis::Type::kVector) {
auto operand_type = type->AsVector()->element_type();
operand_is_signed = operand_type->AsInteger()->IsSigned();
} else {
operand_is_signed = type->AsInteger()->IsSigned();
}
auto original_instruction =
ir_context->get_def_use_mgr()->GetDef(message_.result_id());
fuzzerutil::UpdateModuleIdBound(ir_context, message_.struct_fresh_id());
// Determine the opcode of the new instruction that computes the result into a
// struct.
SpvOp new_instruction_opcode;
switch (original_instruction->opcode()) {
case SpvOpIAdd:
new_instruction_opcode = SpvOpIAddCarry;
break;
case SpvOpISub:
new_instruction_opcode = SpvOpISubBorrow;
break;
case SpvOpIMul:
if (!operand_is_signed) {
new_instruction_opcode = SpvOpUMulExtended;
} else {
new_instruction_opcode = SpvOpSMulExtended;
}
break;
default:
assert(false && "The instruction has an unsupported opcode.");
return;
}
// Get the type of struct type id holding the intermediate result based on the
// operand type.
uint32_t operand_type_id =
ir_context->get_def_use_mgr()
->GetDef(original_instruction->GetSingleWordInOperand(
kArithmeticInstructionIndexLeftInOperand))
->type_id();
uint32_t struct_type_id = fuzzerutil::MaybeGetStructType(
ir_context, {operand_type_id, operand_type_id});
// Avoid unused variables in release mode.
(void)struct_type_id;
assert(struct_type_id && "The struct type must exist in the module.");
// Insert the new instruction that computes the result into a struct before
// the |original_instruction|.
original_instruction->InsertBefore(MakeUnique<opt::Instruction>(
ir_context, new_instruction_opcode, struct_type_id,
message_.struct_fresh_id(),
opt::Instruction::OperandList(
{{SPV_OPERAND_TYPE_ID,
{original_instruction->GetSingleWordInOperand(
kArithmeticInstructionIndexLeftInOperand)}},
{SPV_OPERAND_TYPE_ID,
{original_instruction->GetSingleWordInOperand(
kArithmeticInstructionIndexRightInOperand)}}})));
// Insert the OpCompositeExtract after the added instruction. This instruction
// takes the first component of the struct which represents low-order bits of
// the operation. This is the original result.
original_instruction->InsertBefore(MakeUnique<opt::Instruction>(
ir_context, SpvOpCompositeExtract, original_instruction->type_id(),
message_.result_id(),
opt::Instruction::OperandList(
{{SPV_OPERAND_TYPE_ID, {message_.struct_fresh_id()}},
{SPV_OPERAND_TYPE_LITERAL_INTEGER,
{kOpCompositeExtractIndexLowOrderBits}}})));
// Remove the original instruction.
ir_context->KillInst(original_instruction);
// We have modified the module so most analyzes are now invalid.
ir_context->InvalidateAnalysesExceptFor(opt::IRContext::kAnalysisNone);
}
bool TransformationReplaceAddSubMulWithCarryingExtended::IsInstructionSuitable(
opt::IRContext* ir_context, const opt::Instruction& instruction) {
auto instruction_opcode = instruction.opcode();
// Only instructions OpIAdd, OpISub, OpIMul are supported.
switch (instruction_opcode) {
case SpvOpIAdd:
case SpvOpISub:
case SpvOpIMul:
break;
default:
return false;
}
uint32_t operand_1_type_id =
ir_context->get_def_use_mgr()
->GetDef(instruction.GetSingleWordInOperand(
kArithmeticInstructionIndexLeftInOperand))
->type_id();
uint32_t operand_2_type_id =
ir_context->get_def_use_mgr()
->GetDef(instruction.GetSingleWordInOperand(
kArithmeticInstructionIndexRightInOperand))
->type_id();
uint32_t result_type_id = instruction.type_id();
// Both type ids of the operands and the result type ids must be equal.
if (operand_1_type_id != operand_2_type_id) {
return false;
}
if (operand_2_type_id != result_type_id) {
return false;
}
// In case of OpIAdd and OpISub, the type must be unsigned.
auto type = ir_context->get_type_mgr()->GetType(instruction.type_id());
switch (instruction_opcode) {
case SpvOpIAdd:
case SpvOpISub: {
// In case of OpIAdd and OpISub if the operand is a vector, the component
// type must be unsigned. Otherwise (if the operand is an int), the
// operand must be unsigned.
bool operand_is_signed =
type->AsVector()
? type->AsVector()->element_type()->AsInteger()->IsSigned()
: type->AsInteger()->IsSigned();
if (operand_is_signed) {
return false;
}
} break;
default:
break;
}
return true;
}
protobufs::Transformation
TransformationReplaceAddSubMulWithCarryingExtended::ToMessage() const {
protobufs::Transformation result;
*result.mutable_replace_add_sub_mul_with_carrying_extended() = message_;
return result;
}
} // namespace fuzz
} // namespace spvtools

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

@ -0,0 +1,69 @@
// Copyright (c) 2020 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef SPIRV_TOOLS_TRANSFORMATION_REPLACE_ADD_SUB_MUL_WITH_CARRYING_EXTENDED_H
#define SPIRV_TOOLS_TRANSFORMATION_REPLACE_ADD_SUB_MUL_WITH_CARRYING_EXTENDED_H
#include "source/fuzz/protobufs/spirvfuzz_protobufs.h"
#include "source/fuzz/transformation.h"
#include "source/fuzz/transformation_context.h"
#include "source/opt/ir_context.h"
namespace spvtools {
namespace fuzz {
class TransformationReplaceAddSubMulWithCarryingExtended
: public Transformation {
public:
explicit TransformationReplaceAddSubMulWithCarryingExtended(
const protobufs::TransformationReplaceAddSubMulWithCarryingExtended&
message);
explicit TransformationReplaceAddSubMulWithCarryingExtended(
uint32_t struct_fresh_id, uint32_t result_id);
// - |message_.struct_fresh_id| must be fresh.
// - |message_.result_id| must refer to an OpIAdd or OpISub or OpIMul
// instruction. In this instruction the result type id and the type ids of
// the operands must be the same.
// - The type of struct holding the intermediate result must exists in the
// module.
// - For OpIAdd, OpISub both operands must be unsigned.
bool IsApplicable(
opt::IRContext* ir_context,
const TransformationContext& transformation_context) const override;
// A transformation that replaces instructions OpIAdd, OpISub, OpIMul with
// pairs of instructions. The first one (OpIAddCarry, OpISubBorrow,
// OpUMulExtended, OpSMulExtended) computes the result into a struct. The
// second one extracts the appropriate component from the struct to yield the
// original result.
void Apply(opt::IRContext* ir_context,
TransformationContext* transformation_context) const override;
protobufs::Transformation ToMessage() const override;
// Checks if an OpIAdd, OpISub or OpIMul instruction can be used by the
// transformation.
bool static IsInstructionSuitable(opt::IRContext* ir_context,
const opt::Instruction& instruction);
private:
protobufs::TransformationReplaceAddSubMulWithCarryingExtended message_;
};
} // namespace fuzz
} // namespace spvtools
#endif // SPIRV_TOOLS_TRANSFORMATION_REPLACE_ADD_SUB_MUL_WITH_CARRYING_EXTENDED_H

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

@ -70,7 +70,7 @@ if (${SPIRV_BUILD_FUZZER})
transformation_permute_function_parameters_test.cpp
transformation_permute_phi_operands_test.cpp
transformation_push_id_through_variable_test.cpp
transformation_replace_parameter_with_global_test.cpp
transformation_replace_add_sub_mul_with_carrying_extended_test.cpp
transformation_replace_boolean_constant_with_constant_binary_test.cpp
transformation_replace_copy_object_with_store_load_test.cpp
transformation_replace_constant_with_uniform_test.cpp
@ -78,6 +78,7 @@ if (${SPIRV_BUILD_FUZZER})
transformation_replace_id_with_synonym_test.cpp
transformation_replace_linear_algebra_instruction_test.cpp
transformation_replace_load_store_with_copy_memory_test.cpp
transformation_replace_parameter_with_global_test.cpp
transformation_replace_params_with_struct_test.cpp
transformation_set_function_control_test.cpp
transformation_set_loop_control_test.cpp

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

@ -0,0 +1,610 @@
// Copyright (c) 2020 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "source/fuzz/transformation_replace_add_sub_mul_with_carrying_extended.h"
#include "source/fuzz/fuzzer_util.h"
#include "test/fuzz/fuzz_test_util.h"
namespace spvtools {
namespace fuzz {
namespace {
TEST(TransformationReplaceAddSubMulWithCarryingExtendedTest,
NotApplicableBasicChecks) {
// First conditions in IsApplicable() are checked.
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"
OpName %8 "i1"
OpName %10 "i2"
OpName %12 "i3"
%2 = OpTypeVoid
%3 = OpTypeFunction %2
%6 = OpTypeInt 32 1
%7 = OpTypePointer Function %6
%9 = OpConstant %6 2
%11 = OpConstant %6 3
%4 = OpFunction %2 None %3
%5 = OpLabel
%8 = OpVariable %7 Function
%10 = OpVariable %7 Function
%12 = OpVariable %7 Function
OpStore %8 %9
OpStore %10 %11
%13 = OpLoad %6 %10
%14 = OpLoad %6 %8
%15 = OpSDiv %6 %13 %14
OpStore %12 %15
OpReturn
OpFunctionEnd
)";
const auto env = SPV_ENV_UNIVERSAL_1_4;
const auto consumer = nullptr;
const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
FactManager fact_manager;
spvtools::ValidatorOptions validator_options;
TransformationContext transformation_context(&fact_manager,
validator_options);
// Bad: |struct_fresh_id| must be fresh.
auto transformation_bad_1 =
TransformationReplaceAddSubMulWithCarryingExtended(14, 15);
ASSERT_FALSE(
transformation_bad_1.IsApplicable(context.get(), transformation_context));
// Bad: The transformation cannot be applied to an instruction OpSDiv.
auto transformation_bad_2 =
TransformationReplaceAddSubMulWithCarryingExtended(20, 15);
ASSERT_FALSE(
transformation_bad_2.IsApplicable(context.get(), transformation_context));
// Bad: The transformation cannot be applied to an nonexistent instruction.
auto transformation_bad_3 =
TransformationReplaceAddSubMulWithCarryingExtended(20, 21);
ASSERT_FALSE(
transformation_bad_3.IsApplicable(context.get(), transformation_context));
}
TEST(TransformationReplaceAddSubMulWithCarryingExtendedTest,
NotApplicableDifferingSignedTypes) {
// Operand types and result types do not match. Not applicable to an operation
// on vectors with signed integers and operation on signed integers.
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"
OpName %8 "i1"
OpName %10 "i2"
OpName %16 "v1"
OpName %20 "v2"
OpName %25 "v3"
OpName %31 "u1"
%2 = OpTypeVoid
%3 = OpTypeFunction %2
%6 = OpTypeInt 32 1
%7 = OpTypePointer Function %6
%9 = OpConstant %6 1
%14 = OpTypeVector %6 3
%15 = OpTypePointer Function %14
%17 = OpConstant %6 0
%18 = OpConstant %6 2
%19 = OpConstantComposite %14 %17 %9 %18
%21 = OpConstant %6 3
%22 = OpConstant %6 4
%23 = OpConstant %6 5
%24 = OpConstantComposite %14 %21 %22 %23
%29 = OpTypeInt 32 0
%30 = OpTypePointer Function %29
%32 = OpConstant %29 2
%4 = OpFunction %2 None %3
%5 = OpLabel
%8 = OpVariable %7 Function
%10 = OpVariable %7 Function
%16 = OpVariable %15 Function
%20 = OpVariable %15 Function
%25 = OpVariable %15 Function
%31 = OpVariable %30 Function
OpStore %8 %9
%11 = OpLoad %6 %8
%12 = OpLoad %6 %8
%13 = OpISub %6 %11 %12
OpStore %10 %13
OpStore %16 %19
OpStore %20 %24
%26 = OpLoad %14 %16
%27 = OpLoad %14 %20
%28 = OpIAdd %14 %26 %27
OpStore %25 %28
OpStore %31 %32
%40 = OpIMul %6 %32 %18
%41 = OpIAdd %6 %32 %32
OpReturn
OpFunctionEnd
)";
const auto env = SPV_ENV_UNIVERSAL_1_4;
const auto consumer = nullptr;
const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
FactManager fact_manager;
spvtools::ValidatorOptions validator_options;
TransformationContext transformation_context(&fact_manager,
validator_options);
// Bad: The transformation cannot be applied to an instruction OpIMul that has
// different signedness of the types of operands.
auto transformation_bad_1 =
TransformationReplaceAddSubMulWithCarryingExtended(50, 40);
ASSERT_FALSE(
transformation_bad_1.IsApplicable(context.get(), transformation_context));
// Bad: The transformation cannot be applied to an instruction OpIAdd that has
// different signedness of the result type than the signedness of the types of
// the operands.
auto transformation_bad_2 =
TransformationReplaceAddSubMulWithCarryingExtended(50, 41);
ASSERT_FALSE(
transformation_bad_2.IsApplicable(context.get(), transformation_context));
// Bad: The transformation cannot be applied to the instruction OpIAdd of two
// vectors that have signed components.
auto transformation_bad_3 =
TransformationReplaceAddSubMulWithCarryingExtended(50, 28);
ASSERT_FALSE(
transformation_bad_3.IsApplicable(context.get(), transformation_context));
// Bad: The transformation cannot be applied to the instruction OpISub of two
// signed integers
auto transformation_bad_4 =
TransformationReplaceAddSubMulWithCarryingExtended(50, 13);
ASSERT_FALSE(
transformation_bad_4.IsApplicable(context.get(), transformation_context));
}
TEST(TransformationReplaceAddSubMulWithCarryingExtendedTest,
NotApplicableMissingStructTypes) {
// In all cases the required struct types are missing.
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"
OpName %8 "u1"
OpName %10 "u2"
OpName %12 "u3"
OpName %24 "i1"
OpName %26 "i2"
OpName %28 "i3"
%2 = OpTypeVoid
%3 = OpTypeFunction %2
%6 = OpTypeInt 32 0
%7 = OpTypePointer Function %6
%9 = OpConstant %6 1
%11 = OpConstant %6 2
%22 = OpTypeInt 32 1
%23 = OpTypePointer Function %22
%25 = OpConstant %22 1
%27 = OpConstant %22 2
%4 = OpFunction %2 None %3
%5 = OpLabel
%8 = OpVariable %7 Function
%10 = OpVariable %7 Function
%12 = OpVariable %7 Function
%24 = OpVariable %23 Function
%26 = OpVariable %23 Function
%28 = OpVariable %23 Function
OpStore %8 %9
OpStore %10 %11
%13 = OpLoad %6 %8
%14 = OpLoad %6 %10
%15 = OpIAdd %6 %13 %14
OpStore %12 %15
%16 = OpLoad %6 %8
%17 = OpLoad %6 %10
%18 = OpISub %6 %16 %17
OpStore %12 %18
%19 = OpLoad %6 %8
%20 = OpLoad %6 %10
%21 = OpIMul %6 %19 %20
OpStore %12 %21
OpStore %24 %25
OpStore %26 %27
%29 = OpLoad %22 %24
%30 = OpLoad %22 %26
%31 = OpIMul %22 %29 %30
OpStore %28 %31
OpReturn
OpFunctionEnd
)";
const auto env = SPV_ENV_UNIVERSAL_1_4;
const auto consumer = nullptr;
const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
FactManager fact_manager;
spvtools::ValidatorOptions validator_options;
TransformationContext transformation_context(&fact_manager,
validator_options);
auto transformation_bad_1 =
TransformationReplaceAddSubMulWithCarryingExtended(50, 15);
ASSERT_FALSE(
transformation_bad_1.IsApplicable(context.get(), transformation_context));
auto transformation_bad_2 =
TransformationReplaceAddSubMulWithCarryingExtended(50, 18);
ASSERT_FALSE(
transformation_bad_2.IsApplicable(context.get(), transformation_context));
// Bad: The transformation cannot be applied to the instruction OpIAdd of two
// vectors that have signed components.
auto transformation_bad_3 =
TransformationReplaceAddSubMulWithCarryingExtended(50, 21);
ASSERT_FALSE(
transformation_bad_3.IsApplicable(context.get(), transformation_context));
// Bad: The transformation cannot be applied to the instruction OpISub of two
// signed integers
auto transformation_bad_4 =
TransformationReplaceAddSubMulWithCarryingExtended(50, 31);
ASSERT_FALSE(
transformation_bad_4.IsApplicable(context.get(), transformation_context));
}
TEST(TransformationReplaceAddSubMulWithCarryingExtendedTest,
ApplicableScenarios) {
// In this test all of the transformations can be applied. The required struct
// types are provided.
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"
OpName %8 "u1"
OpName %10 "u2"
OpName %12 "u3"
OpName %24 "i1"
OpName %26 "i2"
OpName %28 "i3"
OpName %34 "uv1"
OpName %36 "uv2"
OpName %39 "uv3"
OpName %51 "v1"
OpName %53 "v2"
OpName %56 "v3"
OpName %60 "pair_uint"
OpMemberName %60 0 "u_1"
OpMemberName %60 1 "u_2"
OpName %62 "p_uint"
OpName %63 "pair_uvec2"
OpMemberName %63 0 "uv_1"
OpMemberName %63 1 "uv_2"
OpName %65 "p_uvec2"
OpName %66 "pair_ivec2"
OpMemberName %66 0 "v_1"
OpMemberName %66 1 "v_2"
OpName %68 "p_ivec2"
OpName %69 "pair_int"
OpMemberName %69 0 "i_1"
OpMemberName %69 1 "i_2"
OpName %71 "p_int"
%2 = OpTypeVoid
%3 = OpTypeFunction %2
%6 = OpTypeInt 32 0
%7 = OpTypePointer Function %6
%9 = OpConstant %6 1
%11 = OpConstant %6 2
%22 = OpTypeInt 32 1
%23 = OpTypePointer Function %22
%25 = OpConstant %22 1
%27 = OpConstant %22 2
%32 = OpTypeVector %6 2
%33 = OpTypePointer Function %32
%35 = OpConstantComposite %32 %9 %11
%37 = OpConstant %6 3
%38 = OpConstantComposite %32 %11 %37
%49 = OpTypeVector %22 2
%50 = OpTypePointer Function %49
%52 = OpConstantComposite %49 %25 %27
%54 = OpConstant %22 3
%55 = OpConstantComposite %49 %27 %54
%60 = OpTypeStruct %6 %6
%61 = OpTypePointer Private %60
%62 = OpVariable %61 Private
%63 = OpTypeStruct %32 %32
%64 = OpTypePointer Private %63
%65 = OpVariable %64 Private
%66 = OpTypeStruct %49 %49
%67 = OpTypePointer Private %66
%68 = OpVariable %67 Private
%69 = OpTypeStruct %22 %22
%70 = OpTypePointer Private %69
%71 = OpVariable %70 Private
%4 = OpFunction %2 None %3
%5 = OpLabel
%8 = OpVariable %7 Function
%10 = OpVariable %7 Function
%12 = OpVariable %7 Function
%24 = OpVariable %23 Function
%26 = OpVariable %23 Function
%28 = OpVariable %23 Function
%34 = OpVariable %33 Function
%36 = OpVariable %33 Function
%39 = OpVariable %33 Function
%51 = OpVariable %50 Function
%53 = OpVariable %50 Function
%56 = OpVariable %50 Function
OpStore %8 %9
OpStore %10 %11
%13 = OpLoad %6 %8
%14 = OpLoad %6 %10
%15 = OpIAdd %6 %13 %14
OpStore %12 %15
%16 = OpLoad %6 %8
%17 = OpLoad %6 %10
%18 = OpISub %6 %16 %17
OpStore %12 %18
%19 = OpLoad %6 %8
%20 = OpLoad %6 %10
%21 = OpIMul %6 %19 %20
OpStore %12 %21
OpStore %24 %25
OpStore %26 %27
%29 = OpLoad %22 %24
%30 = OpLoad %22 %26
%31 = OpIMul %22 %29 %30
OpStore %28 %31
OpStore %34 %35
OpStore %36 %38
%40 = OpLoad %32 %34
%41 = OpLoad %32 %36
%42 = OpIAdd %32 %40 %41
OpStore %39 %42
%43 = OpLoad %32 %34
%44 = OpLoad %32 %36
%45 = OpISub %32 %43 %44
OpStore %39 %45
%46 = OpLoad %32 %34
%47 = OpLoad %32 %36
%48 = OpIMul %32 %46 %47
OpStore %39 %48
OpStore %51 %52
OpStore %53 %55
%57 = OpLoad %49 %51
%58 = OpLoad %49 %53
%59 = OpIMul %49 %57 %58
OpStore %56 %59
OpReturn
OpFunctionEnd
)";
const auto env = SPV_ENV_UNIVERSAL_1_4;
const auto consumer = nullptr;
const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
FactManager fact_manager;
spvtools::ValidatorOptions validator_options;
TransformationContext transformation_context(&fact_manager,
validator_options);
auto transformation_good_1 =
TransformationReplaceAddSubMulWithCarryingExtended(80, 15);
ASSERT_TRUE(transformation_good_1.IsApplicable(context.get(),
transformation_context));
transformation_good_1.Apply(context.get(), &transformation_context);
ASSERT_TRUE(IsValid(env, context.get()));
auto transformation_good_2 =
TransformationReplaceAddSubMulWithCarryingExtended(81, 18);
ASSERT_TRUE(transformation_good_2.IsApplicable(context.get(),
transformation_context));
transformation_good_2.Apply(context.get(), &transformation_context);
ASSERT_TRUE(IsValid(env, context.get()));
auto transformation_good_3 =
TransformationReplaceAddSubMulWithCarryingExtended(82, 21);
ASSERT_TRUE(transformation_good_3.IsApplicable(context.get(),
transformation_context));
transformation_good_3.Apply(context.get(), &transformation_context);
ASSERT_TRUE(IsValid(env, context.get()));
auto transformation_good_4 =
TransformationReplaceAddSubMulWithCarryingExtended(83, 31);
ASSERT_TRUE(transformation_good_4.IsApplicable(context.get(),
transformation_context));
transformation_good_4.Apply(context.get(), &transformation_context);
ASSERT_TRUE(IsValid(env, context.get()));
auto transformation_good_5 =
TransformationReplaceAddSubMulWithCarryingExtended(84, 42);
ASSERT_TRUE(transformation_good_5.IsApplicable(context.get(),
transformation_context));
transformation_good_5.Apply(context.get(), &transformation_context);
ASSERT_TRUE(IsValid(env, context.get()));
auto transformation_good_6 =
TransformationReplaceAddSubMulWithCarryingExtended(85, 45);
ASSERT_TRUE(transformation_good_6.IsApplicable(context.get(),
transformation_context));
transformation_good_6.Apply(context.get(), &transformation_context);
ASSERT_TRUE(IsValid(env, context.get()));
auto transformation_good_7 =
TransformationReplaceAddSubMulWithCarryingExtended(86, 48);
ASSERT_TRUE(transformation_good_7.IsApplicable(context.get(),
transformation_context));
transformation_good_7.Apply(context.get(), &transformation_context);
ASSERT_TRUE(IsValid(env, context.get()));
auto transformation_good_8 =
TransformationReplaceAddSubMulWithCarryingExtended(87, 59);
ASSERT_TRUE(transformation_good_8.IsApplicable(context.get(),
transformation_context));
transformation_good_8.Apply(context.get(), &transformation_context);
ASSERT_TRUE(IsValid(env, context.get()));
std::string after_transformation = R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %4 "main"
OpExecutionMode %4 OriginUpperLeft
OpSource ESSL 310
OpName %4 "main"
OpName %8 "u1"
OpName %10 "u2"
OpName %12 "u3"
OpName %24 "i1"
OpName %26 "i2"
OpName %28 "i3"
OpName %34 "uv1"
OpName %36 "uv2"
OpName %39 "uv3"
OpName %51 "v1"
OpName %53 "v2"
OpName %56 "v3"
OpName %60 "pair_uint"
OpMemberName %60 0 "u_1"
OpMemberName %60 1 "u_2"
OpName %62 "p_uint"
OpName %63 "pair_uvec2"
OpMemberName %63 0 "uv_1"
OpMemberName %63 1 "uv_2"
OpName %65 "p_uvec2"
OpName %66 "pair_ivec2"
OpMemberName %66 0 "v_1"
OpMemberName %66 1 "v_2"
OpName %68 "p_ivec2"
OpName %69 "pair_int"
OpMemberName %69 0 "i_1"
OpMemberName %69 1 "i_2"
OpName %71 "p_int"
%2 = OpTypeVoid
%3 = OpTypeFunction %2
%6 = OpTypeInt 32 0
%7 = OpTypePointer Function %6
%9 = OpConstant %6 1
%11 = OpConstant %6 2
%22 = OpTypeInt 32 1
%23 = OpTypePointer Function %22
%25 = OpConstant %22 1
%27 = OpConstant %22 2
%32 = OpTypeVector %6 2
%33 = OpTypePointer Function %32
%35 = OpConstantComposite %32 %9 %11
%37 = OpConstant %6 3
%38 = OpConstantComposite %32 %11 %37
%49 = OpTypeVector %22 2
%50 = OpTypePointer Function %49
%52 = OpConstantComposite %49 %25 %27
%54 = OpConstant %22 3
%55 = OpConstantComposite %49 %27 %54
%60 = OpTypeStruct %6 %6
%61 = OpTypePointer Private %60
%62 = OpVariable %61 Private
%63 = OpTypeStruct %32 %32
%64 = OpTypePointer Private %63
%65 = OpVariable %64 Private
%66 = OpTypeStruct %49 %49
%67 = OpTypePointer Private %66
%68 = OpVariable %67 Private
%69 = OpTypeStruct %22 %22
%70 = OpTypePointer Private %69
%71 = OpVariable %70 Private
%4 = OpFunction %2 None %3
%5 = OpLabel
%8 = OpVariable %7 Function
%10 = OpVariable %7 Function
%12 = OpVariable %7 Function
%24 = OpVariable %23 Function
%26 = OpVariable %23 Function
%28 = OpVariable %23 Function
%34 = OpVariable %33 Function
%36 = OpVariable %33 Function
%39 = OpVariable %33 Function
%51 = OpVariable %50 Function
%53 = OpVariable %50 Function
%56 = OpVariable %50 Function
OpStore %8 %9
OpStore %10 %11
%13 = OpLoad %6 %8
%14 = OpLoad %6 %10
%80 = OpIAddCarry %60 %13 %14
%15 = OpCompositeExtract %6 %80 0
OpStore %12 %15
%16 = OpLoad %6 %8
%17 = OpLoad %6 %10
%81 = OpISubBorrow %60 %16 %17
%18 = OpCompositeExtract %6 %81 0
OpStore %12 %18
%19 = OpLoad %6 %8
%20 = OpLoad %6 %10
%82 = OpUMulExtended %60 %19 %20
%21 = OpCompositeExtract %6 %82 0
OpStore %12 %21
OpStore %24 %25
OpStore %26 %27
%29 = OpLoad %22 %24
%30 = OpLoad %22 %26
%83 = OpSMulExtended %69 %29 %30
%31 = OpCompositeExtract %22 %83 0
OpStore %28 %31
OpStore %34 %35
OpStore %36 %38
%40 = OpLoad %32 %34
%41 = OpLoad %32 %36
%84 = OpIAddCarry %63 %40 %41
%42 = OpCompositeExtract %32 %84 0
OpStore %39 %42
%43 = OpLoad %32 %34
%44 = OpLoad %32 %36
%85 = OpISubBorrow %63 %43 %44
%45 = OpCompositeExtract %32 %85 0
OpStore %39 %45
%46 = OpLoad %32 %34
%47 = OpLoad %32 %36
%86 = OpUMulExtended %63 %46 %47
%48 = OpCompositeExtract %32 %86 0
OpStore %39 %48
OpStore %51 %52
OpStore %53 %55
%57 = OpLoad %49 %51
%58 = OpLoad %49 %53
%87 = OpSMulExtended %66 %57 %58
%59 = OpCompositeExtract %49 %87 0
OpStore %56 %59
OpReturn
OpFunctionEnd
)";
ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
}
} // namespace
} // namespace fuzz
} // namespace spvtools