Implement transformation to record synonymous constants. (#3494)

Adds a fact-only transformation that records that two constants in the module are synonymous.
This commit is contained in:
Stefano Milizia 2020-07-10 13:02:14 +00:00 committed by GitHub
parent 94667fbf66
commit 5f8cdd8b45
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 522 additions and 1 deletions

View File

@ -128,6 +128,7 @@ if(SPIRV_BUILD_FUZZER)
transformation_permute_function_parameters.h transformation_permute_function_parameters.h
transformation_permute_phi_operands.h transformation_permute_phi_operands.h
transformation_push_id_through_variable.h transformation_push_id_through_variable.h
transformation_record_synonymous_constants.h
transformation_replace_boolean_constant_with_constant_binary.h transformation_replace_boolean_constant_with_constant_binary.h
transformation_replace_constant_with_uniform.h transformation_replace_constant_with_uniform.h
transformation_replace_id_with_synonym.h transformation_replace_id_with_synonym.h
@ -243,6 +244,7 @@ if(SPIRV_BUILD_FUZZER)
transformation_permute_function_parameters.cpp transformation_permute_function_parameters.cpp
transformation_permute_phi_operands.cpp transformation_permute_phi_operands.cpp
transformation_push_id_through_variable.cpp transformation_push_id_through_variable.cpp
transformation_record_synonymous_constants.cpp
transformation_replace_boolean_constant_with_constant_binary.cpp transformation_replace_boolean_constant_with_constant_binary.cpp
transformation_replace_constant_with_uniform.cpp transformation_replace_constant_with_uniform.cpp
transformation_replace_id_with_synonym.cpp transformation_replace_id_with_synonym.cpp

View File

@ -385,6 +385,7 @@ message Transformation {
TransformationInvertComparisonOperator invert_comparison_operator = 54; TransformationInvertComparisonOperator invert_comparison_operator = 54;
TransformationAddImageSampleUnusedComponents add_image_sample_unused_components = 55; TransformationAddImageSampleUnusedComponents add_image_sample_unused_components = 55;
TransformationReplaceParameterWithGlobal replace_parameter_with_global = 56; TransformationReplaceParameterWithGlobal replace_parameter_with_global = 56;
TransformationRecordSynonymousConstants record_synonymous_constants = 57;
// Add additional option using the next available number. // Add additional option using the next available number.
} }
} }
@ -1123,6 +1124,26 @@ message TransformationPushIdThroughVariable {
} }
message TransformationRecordSynonymousConstants {
// A transformation that, given the IDs to two synonymous constants,
// records the fact that they are synonymous. The module is not changed.
// Two constants are synonymous if:
// - they have the same type (ignoring the presence of integer sign)
// - they have the same opcode (one of OpConstant, OpConstantTrue,
// OpConstantFalse, OpConstantNull)
// - they have the same value
// If the types are the same, OpConstantNull is equivalent to
// OpConstantFalse or OpConstant with value zero.
// The id of a constant
uint32 constant1_id = 1;
// The id of the synonym
uint32 constant2_id = 2;
}
message TransformationReplaceParameterWithGlobal { message TransformationReplaceParameterWithGlobal {
// Removes parameter with result id |parameter_id| from its function // Removes parameter with result id |parameter_id| from its function

View File

@ -58,6 +58,7 @@
#include "source/fuzz/transformation_permute_function_parameters.h" #include "source/fuzz/transformation_permute_function_parameters.h"
#include "source/fuzz/transformation_permute_phi_operands.h" #include "source/fuzz/transformation_permute_phi_operands.h"
#include "source/fuzz/transformation_push_id_through_variable.h" #include "source/fuzz/transformation_push_id_through_variable.h"
#include "source/fuzz/transformation_record_synonymous_constants.h"
#include "source/fuzz/transformation_replace_boolean_constant_with_constant_binary.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_constant_with_uniform.h"
#include "source/fuzz/transformation_replace_id_with_synonym.h" #include "source/fuzz/transformation_replace_id_with_synonym.h"
@ -194,6 +195,10 @@ std::unique_ptr<Transformation> Transformation::FromMessage(
case protobufs::Transformation::TransformationCase::kPushIdThroughVariable: case protobufs::Transformation::TransformationCase::kPushIdThroughVariable:
return MakeUnique<TransformationPushIdThroughVariable>( return MakeUnique<TransformationPushIdThroughVariable>(
message.push_id_through_variable()); message.push_id_through_variable());
case protobufs::Transformation::TransformationCase::
kRecordSynonymousConstants:
return MakeUnique<TransformationRecordSynonymousConstants>(
message.record_synonymous_constants());
case protobufs::Transformation::TransformationCase:: case protobufs::Transformation::TransformationCase::
kReplaceParameterWithGlobal: kReplaceParameterWithGlobal:
return MakeUnique<TransformationReplaceParameterWithGlobal>( return MakeUnique<TransformationReplaceParameterWithGlobal>(

View File

@ -0,0 +1,107 @@
// Copyright (c) 2020 Stefano Milizia
// 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 "transformation_record_synonymous_constants.h"
namespace spvtools {
namespace fuzz {
namespace {
bool IsScalarZeroConstant(const opt::analysis::Constant* constant) {
return constant->AsScalarConstant() && constant->IsZero();
}
} // namespace
TransformationRecordSynonymousConstants::
TransformationRecordSynonymousConstants(
const protobufs::TransformationRecordSynonymousConstants& message)
: message_(message) {}
TransformationRecordSynonymousConstants::
TransformationRecordSynonymousConstants(uint32_t constant1_id,
uint32_t constant2_id) {
message_.set_constant1_id(constant1_id);
message_.set_constant2_id(constant2_id);
}
bool TransformationRecordSynonymousConstants::IsApplicable(
opt::IRContext* ir_context,
const TransformationContext& /* unused */) const {
// The ids must be different
if (message_.constant1_id() == message_.constant2_id()) {
return false;
}
auto constant1 = ir_context->get_constant_mgr()->FindDeclaredConstant(
message_.constant1_id());
auto constant2 = ir_context->get_constant_mgr()->FindDeclaredConstant(
message_.constant2_id());
// The constants must exist
if (constant1 == nullptr || constant2 == nullptr) {
return false;
}
// If the constants are equal, then they are equivalent
if (constant1 == constant2) {
return true;
}
// If the constants are two integers (signed or unsigned), they are equal
// if they have the same width and the same data words.
if (constant1->AsIntConstant() && constant2->AsIntConstant() &&
constant1->type()->AsInteger()->width() ==
constant2->type()->AsInteger()->width() &&
constant1->AsIntConstant()->words() ==
constant2->AsIntConstant()->words()) {
return true;
}
// The types must be the same
if (!constant1->type()->IsSame(constant2->type())) {
return false;
}
// The constants are equivalent if one is null and the other is a static
// constant with value 0.
return (constant1->AsNullConstant() && IsScalarZeroConstant(constant2)) ||
(IsScalarZeroConstant(constant1) && constant2->AsNullConstant());
}
void TransformationRecordSynonymousConstants::Apply(
opt::IRContext* ir_context,
TransformationContext* transformation_context) const {
protobufs::FactDataSynonym fact_data_synonym;
// Define the two equivalent data descriptors (just containing the ids)
*fact_data_synonym.mutable_data1() =
MakeDataDescriptor(message_.constant1_id(), {});
*fact_data_synonym.mutable_data2() =
MakeDataDescriptor(message_.constant2_id(), {});
protobufs::Fact fact;
*fact.mutable_data_synonym_fact() = fact_data_synonym;
// Add the fact to the fact manager
transformation_context->GetFactManager()->AddFact(fact, ir_context);
}
protobufs::Transformation TransformationRecordSynonymousConstants::ToMessage()
const {
protobufs::Transformation result;
*result.mutable_record_synonymous_constants() = message_;
return result;
}
} // namespace fuzz
} // namespace spvtools

View File

@ -0,0 +1,59 @@
// Copyright (c) 2020 Stefano Milizia
// Copyright (c) 2020 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef SOURCE_FUZZ_TRANSFORMATION_RECORD_SYNONYMOUS_CONSTANTS_H
#define SOURCE_FUZZ_TRANSFORMATION_RECORD_SYNONYMOUS_CONSTANTS_H
#include "source/fuzz/transformation.h"
namespace spvtools {
namespace fuzz {
class TransformationRecordSynonymousConstants : public Transformation {
public:
explicit TransformationRecordSynonymousConstants(
const protobufs::TransformationRecordSynonymousConstants& message);
TransformationRecordSynonymousConstants(uint32_t constant1_id,
uint32_t constant2_id);
// - |message_.constant_id| and |message_.synonym_id| are distinct ids
// of constants
// - |message_.constant_id| and |message_.synonym_id| refer to constants
// that are equal or equivalent.
// Two integers with the same width and value are equal, even if one is
// signed and the other is not.
// Constants are equivalent if both of them represent zero-like scalar
// values of the same type (for example OpConstant of type int and value
// 0 and OpConstantNull of type int).
bool IsApplicable(
opt::IRContext* ir_context,
const TransformationContext& transformation_context) const override;
// Adds the fact that |message_.constant_id| and |message_.synonym_id|
// are synonyms to the fact manager. The module is not changed.
void Apply(opt::IRContext* ir_context,
TransformationContext* transformation_context) const override;
protobufs::Transformation ToMessage() const override;
private:
protobufs::TransformationRecordSynonymousConstants message_;
};
} // namespace fuzz
} // namespace spvtools
#endif // SOURCE_FUZZ_TRANSFORMATION_RECORD_SYNONYMOUS_CONSTANTS

View File

@ -78,6 +78,7 @@ if (${SPIRV_BUILD_FUZZER})
transformation_swap_commutable_operands_test.cpp transformation_swap_commutable_operands_test.cpp
transformation_swap_conditional_branch_operands_test.cpp transformation_swap_conditional_branch_operands_test.cpp
transformation_toggle_access_chain_instruction_test.cpp transformation_toggle_access_chain_instruction_test.cpp
transformation_record_synonymous_constants_test.cpp
transformation_vector_shuffle_test.cpp transformation_vector_shuffle_test.cpp
uniform_buffer_element_descriptor_test.cpp) uniform_buffer_element_descriptor_test.cpp)

View File

@ -0,0 +1,325 @@
// Copyright (c) 2020 Stefano Milizia
// 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_record_synonymous_constants.h"
#include "test/fuzz/fuzz_test_util.h"
namespace spvtools {
namespace fuzz {
namespace {
// Apply the TransformationRecordSynonymousConstants defined by the given
// constant1_id and constant2_id and check that the fact that the two
// constants are synonym is recorded.
void ApplyTransformationAndCheckFactManager(
uint32_t constant1_id, uint32_t constant2_id, opt::IRContext* ir_context,
TransformationContext* transformation_context) {
TransformationRecordSynonymousConstants(constant1_id, constant2_id)
.Apply(ir_context, transformation_context);
ASSERT_TRUE(transformation_context->GetFactManager()->IsSynonymous(
MakeDataDescriptor(constant1_id, {}),
MakeDataDescriptor(constant2_id, {})));
}
TEST(TransformationRecordSynonymousConstantsTest, IntConstants) {
std::string shader = R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %4 "main" %17
OpExecutionMode %4 OriginUpperLeft
OpSource ESSL 310
OpName %4 "main"
OpName %8 "a"
OpName %10 "b"
OpName %12 "c"
OpName %17 "color"
OpDecorate %8 RelaxedPrecision
OpDecorate %10 RelaxedPrecision
OpDecorate %12 RelaxedPrecision
OpDecorate %17 Location 0
%2 = OpTypeVoid
%3 = OpTypeFunction %2
%6 = OpTypeInt 32 0
%19 = OpTypeInt 32 1
%7 = OpTypePointer Function %6
%9 = OpConstant %6 0
%18 = OpConstant %6 0
%11 = OpConstantNull %6
%13 = OpConstant %6 1
%20 = OpConstant %19 1
%21 = OpConstant %19 -1
%22 = OpConstant %6 1
%14 = OpTypeFloat 32
%15 = OpTypeVector %14 4
%16 = OpTypePointer Output %15
%17 = OpVariable %16 Output
%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
OpStore %12 %13
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);
ASSERT_TRUE(IsValid(env, context.get()));
// %3 is not a constant declaration
ASSERT_FALSE(TransformationRecordSynonymousConstants(3, 9).IsApplicable(
context.get(), transformation_context));
// Swapping the ids gives the same result
ASSERT_FALSE(TransformationRecordSynonymousConstants(9, 3).IsApplicable(
context.get(), transformation_context));
// The two constants must be different
ASSERT_FALSE(TransformationRecordSynonymousConstants(9, 9).IsApplicable(
context.get(), transformation_context));
// %9 and %13 are not equivalent
ASSERT_FALSE(TransformationRecordSynonymousConstants(9, 13).IsApplicable(
context.get(), transformation_context));
// Swapping the ids gives the same result
ASSERT_FALSE(TransformationRecordSynonymousConstants(13, 9).IsApplicable(
context.get(), transformation_context));
// %11 and %13 are not equivalent
ASSERT_FALSE(TransformationRecordSynonymousConstants(11, 13).IsApplicable(
context.get(), transformation_context));
// Swapping the ids gives the same result
ASSERT_FALSE(TransformationRecordSynonymousConstants(13, 11).IsApplicable(
context.get(), transformation_context));
// %20 and %21 have different values
ASSERT_FALSE(TransformationRecordSynonymousConstants(20, 21).IsApplicable(
context.get(), transformation_context));
// %13 and %22 are equal and thus equivalent (having the same value and type)
ASSERT_TRUE(TransformationRecordSynonymousConstants(13, 22).IsApplicable(
context.get(), transformation_context));
ApplyTransformationAndCheckFactManager(13, 22, context.get(),
&transformation_context);
// %13 and %20 are equal even if %13 is signed and %20 is unsigned
ASSERT_TRUE(TransformationRecordSynonymousConstants(13, 20).IsApplicable(
context.get(), transformation_context));
ApplyTransformationAndCheckFactManager(13, 20, context.get(),
&transformation_context);
// %9 and %11 are equivalent (OpConstant with value 0 and OpConstantNull)
ASSERT_TRUE(TransformationRecordSynonymousConstants(9, 11).IsApplicable(
context.get(), transformation_context));
ApplyTransformationAndCheckFactManager(9, 11, context.get(),
&transformation_context);
// Swapping the ids gives the same result
ASSERT_TRUE(TransformationRecordSynonymousConstants(11, 9).IsApplicable(
context.get(), transformation_context));
ApplyTransformationAndCheckFactManager(11, 9, context.get(),
&transformation_context);
}
TEST(TransformationRecordSynonymousConstantsTest, BoolConstants) {
std::string shader = R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %4 "main" %19
OpExecutionMode %4 OriginUpperLeft
OpSource ESSL 310
OpName %4 "main"
OpName %8 "b"
OpName %19 "color"
OpDecorate %19 Location 0
%2 = OpTypeVoid
%3 = OpTypeFunction %2
%6 = OpTypeBool
%7 = OpTypePointer Function %6
%9 = OpConstantFalse %6
%20 = OpConstantNull %6
%11 = OpConstantTrue %6
%21 = OpConstantFalse %6
%22 = OpConstantTrue %6
%16 = OpTypeFloat 32
%17 = OpTypeVector %16 4
%18 = OpTypePointer Output %17
%19 = OpVariable %18 Output
%4 = OpFunction %2 None %3
%5 = OpLabel
%8 = OpVariable %7 Function
OpStore %8 %9
%10 = OpLoad %6 %8
%12 = OpLogicalEqual %6 %10 %11
OpSelectionMerge %14 None
OpBranchConditional %12 %13 %14
%13 = OpLabel
OpReturn
%14 = OpLabel
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);
ASSERT_TRUE(IsValid(env, context.get()));
// %9 and %11 are not equivalent
ASSERT_FALSE(TransformationRecordSynonymousConstants(9, 11).IsApplicable(
context.get(), transformation_context));
// %20 and %11 are not equivalent
ASSERT_FALSE(TransformationRecordSynonymousConstants(20, 11).IsApplicable(
context.get(), transformation_context));
// %9 and %21 are equivalent (both OpConstantFalse)
ASSERT_TRUE(TransformationRecordSynonymousConstants(9, 21).IsApplicable(
context.get(), transformation_context));
ApplyTransformationAndCheckFactManager(9, 21, context.get(),
&transformation_context);
// %11 and %22 are equivalent (both OpConstantTrue)
ASSERT_TRUE(TransformationRecordSynonymousConstants(11, 22).IsApplicable(
context.get(), transformation_context));
ApplyTransformationAndCheckFactManager(11, 22, context.get(),
&transformation_context);
// %9 and %20 are equivalent (OpConstantFalse and boolean OpConstantNull)
ASSERT_TRUE(TransformationRecordSynonymousConstants(9, 20).IsApplicable(
context.get(), transformation_context));
ApplyTransformationAndCheckFactManager(9, 20, context.get(),
&transformation_context);
}
TEST(TransformationRecordSynonymousConstantsTest, FloatConstants) {
std::string shader = R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %4 "main" %22
OpExecutionMode %4 OriginUpperLeft
OpSource ESSL 310
OpName %4 "main"
OpName %8 "a"
OpName %10 "b"
OpName %12 "c"
OpName %22 "color"
OpDecorate %22 Location 0
%2 = OpTypeVoid
%3 = OpTypeFunction %2
%6 = OpTypeFloat 32
%7 = OpTypePointer Function %6
%9 = OpConstant %6 0
%11 = OpConstantNull %6
%13 = OpConstant %6 2
%26 = OpConstant %6 2
%16 = OpTypeBool
%20 = OpTypeVector %6 4
%21 = OpTypePointer Output %20
%22 = OpVariable %21 Output
%23 = OpConstantComposite %20 %9 %11 %9 %11
%25 = OpConstantComposite %20 %11 %9 %9 %11
%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
OpStore %12 %13
%14 = OpLoad %6 %8
%15 = OpLoad %6 %10
%17 = OpFOrdEqual %16 %14 %15
OpSelectionMerge %19 None
OpBranchConditional %17 %18 %24
%18 = OpLabel
OpStore %22 %23
OpBranch %19
%24 = OpLabel
OpStore %22 %25
OpBranch %19
%19 = OpLabel
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);
ASSERT_TRUE(IsValid(env, context.get()));
// %9 and %13 are not equivalent
ASSERT_FALSE(TransformationRecordSynonymousConstants(9, 13).IsApplicable(
context.get(), transformation_context));
// %11 and %13 are not equivalent
ASSERT_FALSE(TransformationRecordSynonymousConstants(11, 13).IsApplicable(
context.get(), transformation_context));
// %13 and %23 are not equivalent
ASSERT_FALSE(TransformationRecordSynonymousConstants(13, 23).IsApplicable(
context.get(), transformation_context));
// %13 and %26 are identical float constants
ASSERT_TRUE(TransformationRecordSynonymousConstants(13, 26).IsApplicable(
context.get(), transformation_context));
ApplyTransformationAndCheckFactManager(13, 26, context.get(),
&transformation_context);
// %9 and %11 are equivalent ()
ASSERT_TRUE(TransformationRecordSynonymousConstants(9, 11).IsApplicable(
context.get(), transformation_context));
ApplyTransformationAndCheckFactManager(9, 11, context.get(),
&transformation_context);
}
} // namespace
} // namespace fuzz
} // namespace spvtools

View File

@ -35,7 +35,8 @@ AUTHORS = ['The Khronos Group Inc.',
'Samsung Inc', 'Samsung Inc',
'André Perez Maselco', 'André Perez Maselco',
'Vasyl Teliman', 'Vasyl Teliman',
'Advanced Micro Devices, Inc.'] 'Advanced Micro Devices, Inc.',
'Stefano Milizia']
CURRENT_YEAR='2020' CURRENT_YEAR='2020'
YEARS = '(2014-2016|2015-2016|2015-2020|2016|2016-2017|2017|2017-2019|2018|2019|2020)' YEARS = '(2014-2016|2015-2016|2015-2020|2016|2016-2017|2017|2017-2019|2018|2019|2020)'