Updated spirv-tools.

This commit is contained in:
Бранимир Караџић
2020-07-10 16:24:17 -07:00
parent 466adb535d
commit 75bbd23e53
45 changed files with 2050 additions and 115 deletions

View File

@@ -1 +1 @@
"v2020.4-dev", "SPIRV-Tools v2020.4-dev 7efb218fd1272e44be3bc1e891ac153da59e9871"
"v2020.4-dev", "SPIRV-Tools v2020.4-dev 38bba44706260cfb807097c58ca926c64c3a13d2"

View File

@@ -20,4 +20,5 @@
{19, "Clay", "Clay Shader Compiler", "Clay Clay Shader Compiler"},
{20, "W3C WebGPU Group", "WHLSL Shader Translator", "W3C WebGPU Group WHLSL Shader Translator"},
{21, "Google", "Clspv", "Google Clspv"},
{22, "Google", "MLIR SPIR-V Serializer", "Google MLIR SPIR-V Serializer"},
{22, "Google", "MLIR SPIR-V Serializer", "Google MLIR SPIR-V Serializer"},
{23, "Google", "Tint Compiler", "Google Tint Compiler"},

View File

@@ -46,6 +46,7 @@ if(SPIRV_BUILD_FUZZER)
fuzzer_pass_add_equation_instructions.h
fuzzer_pass_add_function_calls.h
fuzzer_pass_add_global_variables.h
fuzzer_pass_add_image_sample_unused_components.h
fuzzer_pass_add_loads.h
fuzzer_pass_add_local_variables.h
fuzzer_pass_add_no_contraction_decorations.h
@@ -70,6 +71,7 @@ if(SPIRV_BUILD_FUZZER)
fuzzer_pass_permute_phi_operands.h
fuzzer_pass_push_ids_through_variables.h
fuzzer_pass_replace_linear_algebra_instructions.h
fuzzer_pass_replace_parameter_with_global.h
fuzzer_pass_split_blocks.h
fuzzer_pass_swap_commutable_operands.h
fuzzer_pass_swap_conditional_branch_operands.h
@@ -96,6 +98,7 @@ if(SPIRV_BUILD_FUZZER)
transformation_add_function.h
transformation_add_global_undef.h
transformation_add_global_variable.h
transformation_add_image_sample_unused_components.h
transformation_add_local_variable.h
transformation_add_no_contraction_decoration.h
transformation_add_parameter.h
@@ -125,10 +128,12 @@ if(SPIRV_BUILD_FUZZER)
transformation_permute_function_parameters.h
transformation_permute_phi_operands.h
transformation_push_id_through_variable.h
transformation_record_synonymous_constants.h
transformation_replace_boolean_constant_with_constant_binary.h
transformation_replace_constant_with_uniform.h
transformation_replace_id_with_synonym.h
transformation_replace_linear_algebra_instruction.h
transformation_replace_parameter_with_global.h
transformation_set_function_control.h
transformation_set_loop_control.h
transformation_set_memory_operands_mask.h
@@ -158,6 +163,7 @@ if(SPIRV_BUILD_FUZZER)
fuzzer_pass_add_equation_instructions.cpp
fuzzer_pass_add_function_calls.cpp
fuzzer_pass_add_global_variables.cpp
fuzzer_pass_add_image_sample_unused_components.cpp
fuzzer_pass_add_loads.cpp
fuzzer_pass_add_local_variables.cpp
fuzzer_pass_add_no_contraction_decorations.cpp
@@ -182,6 +188,7 @@ if(SPIRV_BUILD_FUZZER)
fuzzer_pass_permute_phi_operands.cpp
fuzzer_pass_push_ids_through_variables.cpp
fuzzer_pass_replace_linear_algebra_instructions.cpp
fuzzer_pass_replace_parameter_with_global.cpp
fuzzer_pass_split_blocks.cpp
fuzzer_pass_swap_commutable_operands.cpp
fuzzer_pass_swap_conditional_branch_operands.cpp
@@ -207,6 +214,7 @@ if(SPIRV_BUILD_FUZZER)
transformation_add_function.cpp
transformation_add_global_undef.cpp
transformation_add_global_variable.cpp
transformation_add_image_sample_unused_components.cpp
transformation_add_local_variable.cpp
transformation_add_no_contraction_decoration.cpp
transformation_add_parameter.cpp
@@ -236,10 +244,12 @@ if(SPIRV_BUILD_FUZZER)
transformation_permute_function_parameters.cpp
transformation_permute_phi_operands.cpp
transformation_push_id_through_variable.cpp
transformation_record_synonymous_constants.cpp
transformation_replace_boolean_constant_with_constant_binary.cpp
transformation_replace_constant_with_uniform.cpp
transformation_replace_id_with_synonym.cpp
transformation_replace_linear_algebra_instruction.cpp
transformation_replace_parameter_with_global.cpp
transformation_set_function_control.cpp
transformation_set_loop_control.cpp
transformation_set_memory_operands_mask.cpp

View File

@@ -30,6 +30,7 @@
#include "source/fuzz/fuzzer_pass_add_equation_instructions.h"
#include "source/fuzz/fuzzer_pass_add_function_calls.h"
#include "source/fuzz/fuzzer_pass_add_global_variables.h"
#include "source/fuzz/fuzzer_pass_add_image_sample_unused_components.h"
#include "source/fuzz/fuzzer_pass_add_loads.h"
#include "source/fuzz/fuzzer_pass_add_local_variables.h"
#include "source/fuzz/fuzzer_pass_add_no_contraction_decorations.h"
@@ -53,6 +54,7 @@
#include "source/fuzz/fuzzer_pass_permute_phi_operands.h"
#include "source/fuzz/fuzzer_pass_push_ids_through_variables.h"
#include "source/fuzz/fuzzer_pass_replace_linear_algebra_instructions.h"
#include "source/fuzz/fuzzer_pass_replace_parameter_with_global.h"
#include "source/fuzz/fuzzer_pass_split_blocks.h"
#include "source/fuzz/fuzzer_pass_swap_commutable_operands.h"
#include "source/fuzz/fuzzer_pass_swap_conditional_branch_operands.h"
@@ -224,6 +226,9 @@ Fuzzer::FuzzerResultStatus Fuzzer::Run(
MaybeAddPass<FuzzerPassAddGlobalVariables>(
&passes, ir_context.get(), &transformation_context, &fuzzer_context,
transformation_sequence_out);
MaybeAddPass<FuzzerPassAddImageSampleUnusedComponents>(
&passes, ir_context.get(), &transformation_context, &fuzzer_context,
transformation_sequence_out);
MaybeAddPass<FuzzerPassAddLoads>(&passes, ir_context.get(),
&transformation_context, &fuzzer_context,
transformation_sequence_out);
@@ -272,6 +277,9 @@ Fuzzer::FuzzerResultStatus Fuzzer::Run(
MaybeAddPass<FuzzerPassPushIdsThroughVariables>(
&passes, ir_context.get(), &transformation_context, &fuzzer_context,
transformation_sequence_out);
MaybeAddPass<FuzzerPassReplaceParameterWithGlobal>(
&passes, ir_context.get(), &transformation_context, &fuzzer_context,
transformation_sequence_out);
MaybeAddPass<FuzzerPassReplaceLinearAlgebraInstructions>(
&passes, ir_context.get(), &transformation_context, &fuzzer_context,
transformation_sequence_out);

View File

@@ -34,6 +34,8 @@ const std::pair<uint32_t, uint32_t> kChanceOfAddingDeadContinue = {5, 80};
const std::pair<uint32_t, uint32_t> kChanceOfAddingEquationInstruction = {5,
90};
const std::pair<uint32_t, uint32_t> kChanceOfAddingGlobalVariable = {20, 90};
const std::pair<uint32_t, uint32_t> kChanceOfAddingImageSampleUnusedComponents =
{20, 90};
const std::pair<uint32_t, uint32_t> kChanceOfAddingLoad = {5, 50};
const std::pair<uint32_t, uint32_t> kChanceOfAddingLocalVariable = {20, 90};
const std::pair<uint32_t, uint32_t> kChanceOfAddingMatrixType = {20, 70};
@@ -54,6 +56,8 @@ const std::pair<uint32_t, uint32_t> kChanceOfAdjustingSelectionControl = {20,
const std::pair<uint32_t, uint32_t> kChanceOfCallingFunction = {1, 10};
const std::pair<uint32_t, uint32_t> kChanceOfChoosingStructTypeVsArrayType = {
20, 80};
const std::pair<uint32_t, uint32_t> kChanceOfChoosingWorkgroupStorageClass = {
50, 50};
const std::pair<uint32_t, uint32_t> kChanceOfConstructingComposite = {20, 50};
const std::pair<uint32_t, uint32_t> kChanceOfCopyingObject = {20, 50};
const std::pair<uint32_t, uint32_t> kChanceOfDonatingAdditionalModule = {5, 50};
@@ -72,6 +76,8 @@ const std::pair<uint32_t, uint32_t> kChanceOfPushingIdThroughVariable = {5, 50};
const std::pair<uint32_t, uint32_t> kChanceOfReplacingIdWithSynonym = {10, 90};
const std::pair<uint32_t, uint32_t>
kChanceOfReplacingLinearAlgebraInstructions = {10, 90};
const std::pair<uint32_t, uint32_t> kChanceOfReplacingParametersWithGlobals = {
30, 70};
const std::pair<uint32_t, uint32_t> kChanceOfSplittingBlock = {40, 95};
const std::pair<uint32_t, uint32_t> kChanceOfSwappingConditionalBranchOperands =
{10, 70};
@@ -135,6 +141,8 @@ FuzzerContext::FuzzerContext(RandomGenerator* random_generator,
chance_of_adding_global_variable_ =
ChooseBetweenMinAndMax(kChanceOfAddingGlobalVariable);
chance_of_adding_load_ = ChooseBetweenMinAndMax(kChanceOfAddingLoad);
chance_of_adding_image_sample_unused_components_ =
ChooseBetweenMinAndMax(kChanceOfAddingImageSampleUnusedComponents);
chance_of_adding_local_variable_ =
ChooseBetweenMinAndMax(kChanceOfAddingLocalVariable);
chance_of_adding_matrix_type_ =
@@ -162,6 +170,8 @@ FuzzerContext::FuzzerContext(RandomGenerator* random_generator,
ChooseBetweenMinAndMax(kChanceOfCallingFunction);
chance_of_choosing_struct_type_vs_array_type_ =
ChooseBetweenMinAndMax(kChanceOfChoosingStructTypeVsArrayType);
chance_of_choosing_workgroup_storage_class_ =
ChooseBetweenMinAndMax(kChanceOfChoosingWorkgroupStorageClass);
chance_of_constructing_composite_ =
ChooseBetweenMinAndMax(kChanceOfConstructingComposite);
chance_of_copying_object_ = ChooseBetweenMinAndMax(kChanceOfCopyingObject);
@@ -190,6 +200,8 @@ FuzzerContext::FuzzerContext(RandomGenerator* random_generator,
ChooseBetweenMinAndMax(kChanceOfReplacingIdWithSynonym);
chance_of_replacing_linear_algebra_instructions_ =
ChooseBetweenMinAndMax(kChanceOfReplacingLinearAlgebraInstructions);
chance_of_replacing_parameters_with_globals_ =
ChooseBetweenMinAndMax(kChanceOfReplacingParametersWithGlobals);
chance_of_splitting_block_ = ChooseBetweenMinAndMax(kChanceOfSplittingBlock);
chance_of_swapping_conditional_branch_operands_ =
ChooseBetweenMinAndMax(kChanceOfSwappingConditionalBranchOperands);

View File

@@ -128,6 +128,9 @@ class FuzzerContext {
uint32_t GetChanceOfAddingGlobalVariable() {
return chance_of_adding_global_variable_;
}
uint32_t GetChanceOfAddingImageSampleUnusedComponents() {
return chance_of_adding_image_sample_unused_components_;
}
uint32_t GetChanceOfAddingLoad() { return chance_of_adding_load_; }
uint32_t GetChanceOfAddingLocalVariable() {
return chance_of_adding_local_variable_;
@@ -165,6 +168,9 @@ class FuzzerContext {
uint32_t GetChanceOfChoosingStructTypeVsArrayType() {
return chance_of_choosing_struct_type_vs_array_type_;
}
uint32_t GetChanceOfChoosingWorkgroupStorageClass() {
return chance_of_choosing_workgroup_storage_class_;
}
uint32_t GetChanceOfConstructingComposite() {
return chance_of_constructing_composite_;
}
@@ -204,6 +210,9 @@ class FuzzerContext {
uint32_t GetChanceOfReplacingLinearAlgebraInstructions() {
return chance_of_replacing_linear_algebra_instructions_;
}
uint32_t GetChanceOfReplacingParametersWithGlobals() {
return chance_of_replacing_parameters_with_globals_;
}
uint32_t GetChanceOfSplittingBlock() { return chance_of_splitting_block_; }
uint32_t GetChanceOfSwappingConditionalBranchOperands() {
return chance_of_swapping_conditional_branch_operands_;
@@ -265,6 +274,11 @@ class FuzzerContext {
// Ensure that the array size is non-zero.
return random_generator_->RandomUint32(max_new_array_size_limit_ - 1) + 1;
}
uint32_t GetRandomUnusedComponentCountForImageSample(
uint32_t max_unused_component_count) {
// Ensure that the number of unused components is non-zero.
return random_generator_->RandomUint32(max_unused_component_count) + 1;
}
bool GoDeeperInConstantObfuscation(uint32_t depth) {
return go_deeper_in_constant_obfuscation_(depth, random_generator_);
}
@@ -286,6 +300,7 @@ class FuzzerContext {
uint32_t chance_of_adding_dead_continue_;
uint32_t chance_of_adding_equation_instruction_;
uint32_t chance_of_adding_global_variable_;
uint32_t chance_of_adding_image_sample_unused_components_;
uint32_t chance_of_adding_load_;
uint32_t chance_of_adding_local_variable_;
uint32_t chance_of_adding_matrix_type_;
@@ -301,6 +316,7 @@ class FuzzerContext {
uint32_t chance_of_adjusting_selection_control_;
uint32_t chance_of_calling_function_;
uint32_t chance_of_choosing_struct_type_vs_array_type_;
uint32_t chance_of_choosing_workgroup_storage_class_;
uint32_t chance_of_constructing_composite_;
uint32_t chance_of_copying_object_;
uint32_t chance_of_donating_additional_module_;
@@ -316,6 +332,7 @@ class FuzzerContext {
uint32_t chance_of_pushing_id_through_variable_;
uint32_t chance_of_replacing_id_with_synonym_;
uint32_t chance_of_replacing_linear_algebra_instructions_;
uint32_t chance_of_replacing_parameters_with_globals_;
uint32_t chance_of_splitting_block_;
uint32_t chance_of_swapping_conditional_branch_operands_;
uint32_t chance_of_toggling_access_chain_instruction_;

View File

@@ -30,8 +30,22 @@ FuzzerPassAddGlobalVariables::FuzzerPassAddGlobalVariables(
FuzzerPassAddGlobalVariables::~FuzzerPassAddGlobalVariables() = default;
void FuzzerPassAddGlobalVariables::Apply() {
SpvStorageClass variable_storage_class = SpvStorageClassPrivate;
for (auto& entry_point : GetIRContext()->module()->entry_points()) {
// If the execution model of some entry point is GLCompute,
// then the variable storage class may be Workgroup.
if (entry_point.GetSingleWordInOperand(0) == SpvExecutionModelGLCompute) {
variable_storage_class =
GetFuzzerContext()->ChoosePercentage(
GetFuzzerContext()->GetChanceOfChoosingWorkgroupStorageClass())
? SpvStorageClassWorkgroup
: SpvStorageClassPrivate;
break;
}
}
auto basic_type_ids_and_pointers =
GetAvailableBasicTypesAndPointers(SpvStorageClassPrivate);
GetAvailableBasicTypesAndPointers(variable_storage_class);
// These are the basic types that are available to this fuzzer pass.
auto& basic_types = basic_type_ids_and_pointers.first;
@@ -59,18 +73,21 @@ void FuzzerPassAddGlobalVariables::Apply() {
pointer_type_id = GetFuzzerContext()->GetFreshId();
available_pointers_to_basic_type.push_back(pointer_type_id);
ApplyTransformation(TransformationAddTypePointer(
pointer_type_id, SpvStorageClassPrivate, basic_type));
pointer_type_id, variable_storage_class, basic_type));
} else {
// There is - grab one.
pointer_type_id =
available_pointers_to_basic_type[GetFuzzerContext()->RandomIndex(
available_pointers_to_basic_type)];
}
// TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3274): We could
// add new variables with Workgroup storage class in compute shaders.
ApplyTransformation(TransformationAddGlobalVariable(
GetFuzzerContext()->GetFreshId(), pointer_type_id,
SpvStorageClassPrivate, FindOrCreateZeroConstant(basic_type), true));
variable_storage_class,
variable_storage_class == SpvStorageClassPrivate
? FindOrCreateZeroConstant(basic_type)
: 0,
true));
}
}

View File

@@ -0,0 +1,200 @@
// Copyright (c) 2020 André Perez Maselco
//
// 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_add_image_sample_unused_components.h"
#include "source/fuzz/fuzzer_util.h"
#include "source/fuzz/instruction_descriptor.h"
#include "source/fuzz/transformation_add_image_sample_unused_components.h"
#include "source/fuzz/transformation_composite_construct.h"
namespace spvtools {
namespace fuzz {
FuzzerPassAddImageSampleUnusedComponents::
FuzzerPassAddImageSampleUnusedComponents(
opt::IRContext* ir_context,
TransformationContext* transformation_context,
FuzzerContext* fuzzer_context,
protobufs::TransformationSequence* transformations)
: FuzzerPass(ir_context, transformation_context, fuzzer_context,
transformations) {}
FuzzerPassAddImageSampleUnusedComponents::
~FuzzerPassAddImageSampleUnusedComponents() = default;
void FuzzerPassAddImageSampleUnusedComponents::Apply() {
// SPIR-V module to help understand the transformation.
//
// OpCapability Shader
// %1 = OpExtInstImport "GLSL.std.450"
// OpMemoryModel Logical GLSL450
// OpEntryPoint Fragment %15 "main" %12 %14
// OpExecutionMode %15 OriginUpperLeft
//
// ; Decorations
// OpDecorate %12 Location 0 ; Input color variable location
// OpDecorate %13 DescriptorSet 0 ; Image coordinate variable
// descriptor set OpDecorate %13 Binding 0 ; Image coordinate
// variable binding OpDecorate %14 Location 0 ; Fragment color
// variable location
//
// ; Types
// %2 = OpTypeVoid
// %3 = OpTypeFunction %2
// %4 = OpTypeFloat 32
// %5 = OpTypeVector %4 2
// %6 = OpTypeVector %4 4
// %7 = OpTypeImage %4 2D 0 0 0 1 Rgba32f
// %8 = OpTypeSampledImage %7
// %9 = OpTypePointer Input %5
// %10 = OpTypePointer UniformConstant %8
// %11 = OpTypePointer Output %6
//
// ; Variables
// %12 = OpVariable %9 Input ; Input image coordinate variable
// %13 = OpVariable %10 UniformConstant ; Image variable
// %14 = OpVariable %11 Output ; Fragment color variable
//
// ; main function
// %15 = OpFunction %2 None %3
// %16 = OpLabel
// %17 = OpLoad %5 %12
// %18 = OpLoad %8 %13
// %19 = OpImageSampleImplicitLod %6 %18 %17
// OpStore %14 %19
// OpReturn
// OpFunctionEnd
GetIRContext()->module()->ForEachInst([this](opt::Instruction* instruction) {
// |instruction| %19 = OpImageSampleImplicitLod %6 %18 %17
if (!spvOpcodeIsImageSample(instruction->opcode())) {
return;
}
if (!GetFuzzerContext()->ChoosePercentage(
GetFuzzerContext()
->GetChanceOfAddingImageSampleUnusedComponents())) {
return;
}
// Gets image sample coordinate information.
// |coordinate_instruction| %17 = OpLoad %5 %12
uint32_t coordinate_id = instruction->GetSingleWordInOperand(1);
auto coordinate_instruction =
GetIRContext()->get_def_use_mgr()->GetDef(coordinate_id);
auto coordinate_type = GetIRContext()->get_type_mgr()->GetType(
coordinate_instruction->type_id());
// If the coordinate is a 4-dimensional vector, then no unused components
// may be added.
if (coordinate_type->AsVector() &&
coordinate_type->AsVector()->element_count() == 4) {
return;
}
// If the coordinate is a scalar, then at most 3 unused components may be
// added. If the coordinate is a vector, then the maximum number of unused
// components depends on the vector size.
// For the sample module, the coordinate type instruction is %5 =
// OpTypeVector %4 2, thus |max_unused_component_count| = 4 - 2 = 2.
uint32_t max_unused_component_count =
coordinate_type->AsInteger() || coordinate_type->AsFloat()
? 3
: 4 - coordinate_type->AsVector()->element_count();
// |unused_component_count| may be 1 or 2.
uint32_t unused_component_count =
GetFuzzerContext()->GetRandomUnusedComponentCountForImageSample(
max_unused_component_count);
// Gets a type for the zero-unused components.
uint32_t zero_constant_type_id;
switch (unused_component_count) {
case 1:
// If the coordinate is an integer or float, then the unused components
// type is the same as the coordinate. If the coordinate is a vector,
// then the unused components type is the same as the vector components
// type.
zero_constant_type_id =
coordinate_type->AsInteger() || coordinate_type->AsFloat()
? coordinate_instruction->type_id()
: GetIRContext()->get_type_mgr()->GetId(
coordinate_type->AsVector()->element_type());
break;
case 2:
case 3:
// If the coordinate is an integer or float, then the unused components
// type is the same as the coordinate. If the coordinate is a vector,
// then the unused components type is the same as the coordinate
// components type.
// |zero_constant_type_id| %5 = OpTypeVector %4 2
zero_constant_type_id =
coordinate_type->AsInteger() || coordinate_type->AsFloat()
? FindOrCreateVectorType(coordinate_instruction->type_id(),
unused_component_count)
: FindOrCreateVectorType(
GetIRContext()->get_type_mgr()->GetId(
coordinate_type->AsVector()->element_type()),
unused_component_count);
break;
default:
assert(false && "Should be unreachable.");
zero_constant_type_id = 0;
break;
}
// Gets |coordinate_type| again because the module may have changed due to
// the use of FindOrCreateVectorType above.
coordinate_type = GetIRContext()->get_type_mgr()->GetType(
coordinate_instruction->type_id());
// If the new vector type with unused components does not exist, then create
// it. |coordinate_with_unused_components_type_id| %6 = OpTypeVector %4 4
uint32_t coordinate_with_unused_components_type_id =
coordinate_type->AsInteger() || coordinate_type->AsFloat()
? FindOrCreateVectorType(coordinate_instruction->type_id(),
1 + unused_component_count)
: FindOrCreateVectorType(
GetIRContext()->get_type_mgr()->GetId(
coordinate_type->AsVector()->element_type()),
coordinate_type->AsVector()->element_count() +
unused_component_count);
// Inserts an OpCompositeConstruct instruction which
// represents the coordinate with unused components.
// |coordinate_with_unused_components_id|
// %22 = OpCompositeConstruct %6 %17 %21
uint32_t coordinate_with_unused_components_id =
GetFuzzerContext()->GetFreshId();
ApplyTransformation(TransformationCompositeConstruct(
coordinate_with_unused_components_type_id,
{coordinate_instruction->result_id(),
// FindOrCreateZeroConstant
// %20 = OpConstant %4 0
// %21 = OpConstantComposite %5 %20 %20
FindOrCreateZeroConstant(zero_constant_type_id)},
MakeInstructionDescriptor(GetIRContext(), instruction),
coordinate_with_unused_components_id));
// Tries to add unused components to the image sample coordinate.
// %19 = OpImageSampleImplicitLod %6 %18 %22
ApplyTransformation(TransformationAddImageSampleUnusedComponents(
coordinate_with_unused_components_id,
MakeInstructionDescriptor(GetIRContext(), instruction)));
});
}
} // namespace fuzz
} // namespace spvtools

View File

@@ -0,0 +1,41 @@
// Copyright (c) 2020 André Perez Maselco
//
// 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_FUZZER_PASS_ADD_IMAGE_SAMPLE_UNUSED_COMPONENTS_H_
#define SOURCE_FUZZ_FUZZER_PASS_ADD_IMAGE_SAMPLE_UNUSED_COMPONENTS_H_
#include "source/fuzz/fuzzer_pass.h"
namespace spvtools {
namespace fuzz {
// This fuzzer pass searches for image sample instructions in the module and
// randomly applies the transformation to add unused components to the image
// sample coordinate.
class FuzzerPassAddImageSampleUnusedComponents : public FuzzerPass {
public:
FuzzerPassAddImageSampleUnusedComponents(
opt::IRContext* ir_context, TransformationContext* transformation_context,
FuzzerContext* fuzzer_context,
protobufs::TransformationSequence* transformations);
~FuzzerPassAddImageSampleUnusedComponents();
void Apply() override;
};
} // namespace fuzz
} // namespace spvtools
#endif // SOURCE_FUZZ_FUZZER_PASS_ADD_IMAGE_SAMPLE_UNUSED_COMPONENTS_H_

View File

@@ -43,6 +43,8 @@ void FuzzerPassReplaceLinearAlgebraInstructions::Apply() {
// |spvOpcodeIsLinearAlgebra|.
if (instruction->opcode() != SpvOpVectorTimesScalar &&
instruction->opcode() != SpvOpMatrixTimesScalar &&
instruction->opcode() != SpvOpVectorTimesMatrix &&
instruction->opcode() != SpvOpMatrixTimesVector &&
instruction->opcode() != SpvOpDot) {
return;
}

View File

@@ -0,0 +1,92 @@
// Copyright (c) 2020 Vasyl Teliman
//
// 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_parameter_with_global.h"
#include <numeric>
#include <vector>
#include "source/fuzz/fuzzer_context.h"
#include "source/fuzz/fuzzer_util.h"
#include "source/fuzz/transformation_replace_parameter_with_global.h"
namespace spvtools {
namespace fuzz {
FuzzerPassReplaceParameterWithGlobal::FuzzerPassReplaceParameterWithGlobal(
opt::IRContext* ir_context, TransformationContext* transformation_context,
FuzzerContext* fuzzer_context,
protobufs::TransformationSequence* transformations)
: FuzzerPass(ir_context, transformation_context, fuzzer_context,
transformations) {}
FuzzerPassReplaceParameterWithGlobal::~FuzzerPassReplaceParameterWithGlobal() =
default;
void FuzzerPassReplaceParameterWithGlobal::Apply() {
for (const auto& function : *GetIRContext()->module()) {
if (fuzzerutil::FunctionIsEntryPoint(GetIRContext(),
function.result_id())) {
continue;
}
if (!GetFuzzerContext()->ChoosePercentage(
GetFuzzerContext()->GetChanceOfReplacingParametersWithGlobals())) {
continue;
}
auto params =
fuzzerutil::GetParameters(GetIRContext(), function.result_id());
// Make sure at least one parameter can be replaced. Also checks that the
// function has at least one parameter.
if (std::none_of(params.begin(), params.end(),
[this](const opt::Instruction* param) {
const auto* param_type =
GetIRContext()->get_type_mgr()->GetType(
param->type_id());
assert(param_type && "Parameter has invalid type");
return TransformationReplaceParameterWithGlobal::
CanReplaceFunctionParameterType(*param_type);
})) {
continue;
}
// Select id of a parameter to replace.
const opt::Instruction* replaced_param = nullptr;
const opt::analysis::Type* param_type = nullptr;
do {
replaced_param = GetFuzzerContext()->RemoveAtRandomIndex(&params);
param_type =
GetIRContext()->get_type_mgr()->GetType(replaced_param->type_id());
assert(param_type && "Parameter has invalid type");
} while (!TransformationReplaceParameterWithGlobal::
CanReplaceFunctionParameterType(*param_type));
assert(replaced_param && "Unable to find a parameter to replace");
// Make sure type id for the global variable exists in the module.
FindOrCreatePointerType(replaced_param->type_id(), SpvStorageClassPrivate);
// Make sure initializer for the global variable exists in the module.
FindOrCreateZeroConstant(replaced_param->type_id());
ApplyTransformation(TransformationReplaceParameterWithGlobal(
GetFuzzerContext()->GetFreshId(), replaced_param->result_id(),
GetFuzzerContext()->GetFreshId()));
}
}
} // namespace fuzz
} // namespace spvtools

View File

@@ -0,0 +1,40 @@
// Copyright (c) 2020 Vasyl Teliman
//
// 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_FUZZER_PASS_REPLACE_PARAMETER_WITH_GLOBAL_H_
#define SOURCE_FUZZ_FUZZER_PASS_REPLACE_PARAMETER_WITH_GLOBAL_H_
#include "source/fuzz/fuzzer_pass.h"
namespace spvtools {
namespace fuzz {
// Iterates over all non-entry-point functions in the module and randomly
// replaces a parameter with a global variable.
class FuzzerPassReplaceParameterWithGlobal : public FuzzerPass {
public:
FuzzerPassReplaceParameterWithGlobal(
opt::IRContext* ir_context, TransformationContext* transformation_context,
FuzzerContext* fuzzer_context,
protobufs::TransformationSequence* transformations);
~FuzzerPassReplaceParameterWithGlobal() override;
void Apply() override;
};
} // namespace fuzz
} // namespace spvtools
#endif // SOURCE_FUZZ_FUZZER_PASS_REPLACE_PARAMETER_WITH_GLOBAL_H_

View File

@@ -112,19 +112,6 @@ bool PhiIdsOkForNewEdge(
return true;
}
uint32_t MaybeGetBoolConstantId(opt::IRContext* context, bool value) {
opt::analysis::Bool bool_type;
auto registered_bool_type =
context->get_type_mgr()->GetRegisteredType(&bool_type);
if (!registered_bool_type) {
return 0;
}
opt::analysis::BoolConstant bool_constant(registered_bool_type->AsBool(),
value);
return context->get_constant_mgr()->FindDeclaredConstant(
&bool_constant, context->get_type_mgr()->GetId(&bool_type));
}
void AddUnreachableEdgeAndUpdateOpPhis(
opt::IRContext* context, opt::BasicBlock* bb_from, opt::BasicBlock* bb_to,
bool condition_value,
@@ -135,7 +122,7 @@ void AddUnreachableEdgeAndUpdateOpPhis(
"Precondition on terminator of bb_from is not satisfied");
// Get the id of the boolean constant to be used as the condition.
uint32_t bool_id = MaybeGetBoolConstantId(context, condition_value);
uint32_t bool_id = MaybeGetBoolConstant(context, condition_value);
assert(
bool_id &&
"Precondition that condition value must be available is not satisfied");
@@ -627,6 +614,7 @@ void AddGlobalVariable(opt::IRContext* context, uint32_t result_id,
context, SpvOpVariable, type_id, result_id, std::move(operands)));
AddVariableIdToEntryPointInterfaces(context, result_id);
UpdateModuleIdBound(context, result_id);
}
void AddLocalVariable(opt::IRContext* context, uint32_t result_id,
@@ -657,6 +645,8 @@ void AddLocalVariable(opt::IRContext* context, uint32_t result_id,
opt::Instruction::OperandList{
{SPV_OPERAND_TYPE_STORAGE_CLASS, {SpvStorageClassFunction}},
{SPV_OPERAND_TYPE_ID, {initializer_id}}}));
UpdateModuleIdBound(context, result_id);
}
bool HasDuplicates(const std::vector<uint32_t>& arr) {
@@ -771,6 +761,199 @@ uint32_t MaybeGetStructType(opt::IRContext* ir_context,
return ir_context->get_type_mgr()->GetId(&type);
}
uint32_t MaybeGetZeroConstant(opt::IRContext* ir_context,
uint32_t scalar_or_composite_type_id) {
const auto* type =
ir_context->get_type_mgr()->GetType(scalar_or_composite_type_id);
assert(type && "|scalar_or_composite_type_id| is invalid");
switch (type->kind()) {
case opt::analysis::Type::kBool:
return MaybeGetBoolConstant(ir_context, false);
case opt::analysis::Type::kFloat:
case opt::analysis::Type::kInteger: {
std::vector<uint32_t> words = {0};
if ((type->AsInteger() && type->AsInteger()->width() > 32) ||
(type->AsFloat() && type->AsFloat()->width() > 32)) {
words.push_back(0);
}
return MaybeGetScalarConstant(ir_context, words,
scalar_or_composite_type_id);
}
case opt::analysis::Type::kStruct: {
std::vector<uint32_t> component_ids;
for (const auto* component_type : type->AsStruct()->element_types()) {
auto component_type_id =
ir_context->get_type_mgr()->GetId(component_type);
assert(component_type_id && "Component type is invalid");
auto component_id = MaybeGetZeroConstant(ir_context, component_type_id);
if (component_id == 0) {
return 0;
}
component_ids.push_back(component_id);
}
return MaybeGetCompositeConstant(ir_context, component_ids,
scalar_or_composite_type_id);
}
case opt::analysis::Type::kMatrix:
case opt::analysis::Type::kVector: {
const auto* component_type = type->AsVector()
? type->AsVector()->element_type()
: type->AsMatrix()->element_type();
auto component_type_id =
ir_context->get_type_mgr()->GetId(component_type);
assert(component_type_id && "Component type is invalid");
if (auto component_id =
MaybeGetZeroConstant(ir_context, component_type_id)) {
auto component_count = type->AsVector()
? type->AsVector()->element_count()
: type->AsMatrix()->element_count();
return MaybeGetCompositeConstant(
ir_context, std::vector<uint32_t>(component_count, component_id),
scalar_or_composite_type_id);
}
return 0;
}
case opt::analysis::Type::kArray: {
auto component_type_id =
ir_context->get_type_mgr()->GetId(type->AsArray()->element_type());
assert(component_type_id && "Component type is invalid");
if (auto component_id =
MaybeGetZeroConstant(ir_context, component_type_id)) {
auto type_id = ir_context->get_type_mgr()->GetId(type);
assert(type_id && "|type| is invalid");
const auto* type_inst = ir_context->get_def_use_mgr()->GetDef(type_id);
assert(type_inst && "Array's type id is invalid");
return MaybeGetCompositeConstant(
ir_context,
std::vector<uint32_t>(GetArraySize(*type_inst, ir_context),
component_id),
scalar_or_composite_type_id);
}
return 0;
}
default:
assert(false && "Type is not supported");
return 0;
}
}
uint32_t MaybeGetScalarConstant(opt::IRContext* ir_context,
const std::vector<uint32_t>& words,
uint32_t scalar_type_id) {
const auto* type = ir_context->get_type_mgr()->GetType(scalar_type_id);
assert(type && "|scalar_type_id| is invalid");
if (const auto* int_type = type->AsInteger()) {
return MaybeGetIntegerConstant(ir_context, words, int_type->width(),
int_type->IsSigned());
} else if (const auto* float_type = type->AsFloat()) {
return MaybeGetFloatConstant(ir_context, words, float_type->width());
} else {
assert(type->AsBool() && words.size() == 1 &&
"|scalar_type_id| doesn't represent a scalar type");
return MaybeGetBoolConstant(ir_context, words[0]);
}
}
uint32_t MaybeGetCompositeConstant(opt::IRContext* ir_context,
const std::vector<uint32_t>& component_ids,
uint32_t composite_type_id) {
std::vector<const opt::analysis::Constant*> constants;
for (auto id : component_ids) {
const auto* component_constant =
ir_context->get_constant_mgr()->FindDeclaredConstant(id);
assert(component_constant && "|id| is invalid");
constants.push_back(component_constant);
}
const auto* type = ir_context->get_type_mgr()->GetType(composite_type_id);
assert(type && "|composite_type_id| is invalid");
std::unique_ptr<opt::analysis::Constant> composite_constant;
switch (type->kind()) {
case opt::analysis::Type::kStruct:
composite_constant = MakeUnique<opt::analysis::StructConstant>(
type->AsStruct(), std::move(constants));
break;
case opt::analysis::Type::kVector:
composite_constant = MakeUnique<opt::analysis::VectorConstant>(
type->AsVector(), std::move(constants));
break;
case opt::analysis::Type::kMatrix:
composite_constant = MakeUnique<opt::analysis::MatrixConstant>(
type->AsMatrix(), std::move(constants));
break;
case opt::analysis::Type::kArray:
composite_constant = MakeUnique<opt::analysis::ArrayConstant>(
type->AsArray(), std::move(constants));
break;
default:
assert(false &&
"|composite_type_id| is not a result id of a composite type");
return 0;
}
return ir_context->get_constant_mgr()->FindDeclaredConstant(
composite_constant.get(), composite_type_id);
}
uint32_t MaybeGetIntegerConstant(opt::IRContext* ir_context,
const std::vector<uint32_t>& words,
uint32_t width, bool is_signed) {
auto type_id = MaybeGetIntegerType(ir_context, width, is_signed);
if (!type_id) {
return 0;
}
const auto* type = ir_context->get_type_mgr()->GetType(type_id);
assert(type && "|type_id| is invalid");
opt::analysis::IntConstant constant(type->AsInteger(), words);
return ir_context->get_constant_mgr()->FindDeclaredConstant(&constant,
type_id);
}
uint32_t MaybeGetFloatConstant(opt::IRContext* ir_context,
const std::vector<uint32_t>& words,
uint32_t width) {
auto type_id = MaybeGetFloatType(ir_context, width);
if (!type_id) {
return 0;
}
const auto* type = ir_context->get_type_mgr()->GetType(type_id);
assert(type && "|type_id| is invalid");
opt::analysis::FloatConstant constant(type->AsFloat(), words);
return ir_context->get_constant_mgr()->FindDeclaredConstant(&constant,
type_id);
}
uint32_t MaybeGetBoolConstant(opt::IRContext* context, bool value) {
opt::analysis::Bool bool_type;
auto registered_bool_type =
context->get_type_mgr()->GetRegisteredType(&bool_type);
if (!registered_bool_type) {
return 0;
}
opt::analysis::BoolConstant bool_constant(registered_bool_type->AsBool(),
value);
return context->get_constant_mgr()->FindDeclaredConstant(
&bool_constant, context->get_type_mgr()->GetId(&bool_type));
}
void AddIntegerType(opt::IRContext* ir_context, uint32_t result_id,
uint32_t width, bool is_signed) {
ir_context->module()->AddType(MakeUnique<opt::Instruction>(

View File

@@ -53,10 +53,6 @@ bool PhiIdsOkForNewEdge(
opt::IRContext* context, opt::BasicBlock* bb_from, opt::BasicBlock* bb_to,
const google::protobuf::RepeatedField<google::protobuf::uint32>& phi_ids);
// Returns the id of a boolean constant with value |value| if it exists in the
// module, or 0 otherwise.
uint32_t MaybeGetBoolConstantId(opt::IRContext* context, bool value);
// Requires that a boolean constant with value |condition_value| is available,
// that PhiIdsOkForNewEdge(context, bb_from, bb_to, phi_ids) holds, and that
// bb_from ends with "OpBranch %some_block". Turns OpBranch into
@@ -229,7 +225,8 @@ void AddVariableIdToEntryPointInterfaces(opt::IRContext* context, uint32_t id);
// Adds a global variable with storage class |storage_class| to the module, with
// type |type_id| and either no initializer or |initializer_id| as an
// initializer, depending on whether |initializer_id| is 0. The global variable
// has result id |result_id|.
// has result id |result_id|. Updates module's id bound to accommodate for
// |result_id|.
//
// - |type_id| must be the id of a pointer type with the same storage class as
// |storage_class|.
@@ -243,6 +240,7 @@ void AddGlobalVariable(opt::IRContext* context, uint32_t result_id,
// Adds an instruction to the start of |function_id|, of the form:
// |result_id| = OpVariable |type_id| Function |initializer_id|.
// Updates module's id bound to accommodate for |result_id|.
//
// - |type_id| must be the id of a pointer type with Function storage class.
// - |initializer_id| must be the id of a constant with the same type as the
@@ -306,6 +304,48 @@ uint32_t MaybeGetVectorType(opt::IRContext* ir_context,
uint32_t MaybeGetStructType(opt::IRContext* ir_context,
const std::vector<uint32_t>& component_type_ids);
// Recursive definition is the following:
// - if |scalar_or_composite_type_id| is a result id of a scalar type - returns
// a result id of the following constants (depending on the type): int -> 0,
// float -> 0.0, bool -> false.
// - otherwise, returns a result id of an OpConstantComposite instruction.
// Every component of the composite constant is looked up by calling this
// function with the type id of that component.
// Returns 0 if no such instruction is present in the module.
uint32_t MaybeGetZeroConstant(opt::IRContext* ir_context,
uint32_t scalar_or_composite_type_id);
// Returns the result id of an OpConstant instruction. |scalar_type_id| must be
// a result id of a scalar type (i.e. int, float or bool). Returns 0 if no such
// instruction is present in the module.
uint32_t MaybeGetScalarConstant(opt::IRContext* ir_context,
const std::vector<uint32_t>& words,
uint32_t scalar_type_id);
// Returns the result id of an OpConstantComposite instruction.
// |composite_type_id| must be a result id of a composite type (i.e. vector,
// matrix, struct or array). Returns 0 if no such instruction is present in the
// module.
uint32_t MaybeGetCompositeConstant(opt::IRContext* ir_context,
const std::vector<uint32_t>& component_ids,
uint32_t composite_type_id);
// Returns the result id of an OpConstant instruction of integral type.
// Returns 0 if no such instruction or type is present in the module.
uint32_t MaybeGetIntegerConstant(opt::IRContext* ir_context,
const std::vector<uint32_t>& words,
uint32_t width, bool is_signed);
// Returns the result id of an OpConstant instruction of floating-point type.
// Returns 0 if no such instruction or type is present in the module.
uint32_t MaybeGetFloatConstant(opt::IRContext* ir_context,
const std::vector<uint32_t>& words,
uint32_t width);
// Returns the id of a boolean constant with value |value| if it exists in the
// module, or 0 otherwise.
uint32_t MaybeGetBoolConstant(opt::IRContext* context, bool value);
// Creates a new OpTypeInt instruction in the module. Updates module's id bound
// to accommodate for |result_id|.
void AddIntegerType(opt::IRContext* ir_context, uint32_t result_id,

View File

@@ -383,6 +383,9 @@ message Transformation {
TransformationAddParameter add_parameter = 52;
TransformationAddCopyMemory add_copy_memory = 53;
TransformationInvertComparisonOperator invert_comparison_operator = 54;
TransformationAddImageSampleUnusedComponents add_image_sample_unused_components = 55;
TransformationReplaceParameterWithGlobal replace_parameter_with_global = 56;
TransformationRecordSynonymousConstants record_synonymous_constants = 57;
// Add additional option using the next available number.
}
}
@@ -616,6 +619,18 @@ message TransformationAddGlobalVariable {
}
message TransformationAddImageSampleUnusedComponents {
// A transformation that adds unused components to an image sample coordinate.
// An vector id with the original coordinate and the unused components.
uint32 coordinate_with_unused_components_id = 1;
// A descriptor for an image sample instruction.
InstructionDescriptor instruction_descriptor = 2;
}
message TransformationAddLocalVariable {
// Adds a local variable of the given type (which must be a pointer with
@@ -1109,6 +1124,43 @@ 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 {
// Removes parameter with result id |parameter_id| from its function
// and creates a global variable to pass its value to the function instead.
// Fresh id for a new function type. This might not be used if a required
// function type already exists or if we can change the old function type.
uint32 function_type_fresh_id = 2;
// Result id of the OpFunctionParameter instruction to remove.
uint32 parameter_id = 3;
// Fresh id of a global variable used to pass parameter's value to the function.
uint32 global_variable_fresh_id = 4;
}
message TransformationReplaceBooleanConstantWithConstantBinary {
// A transformation to capture replacing a use of a boolean constant with
@@ -1176,14 +1228,14 @@ message TransformationReplaceLinearAlgebraInstruction {
// Supported:
// OpVectorTimesScalar
// OpMatrixTimesScalar
// OpVectorTimesMatrix
// OpMatrixTimesVector
// OpDot
// TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3354):
// Right now we only support certain operations. When this issue is addressed
// the supporting comments can be removed.
// To be supported in the future:
// OpTranspose
// OpVectorTimesMatrix
// OpMatrixTimesVector
// OpMatrixTimesMatrix
// OpOuterProduct
InstructionDescriptor instruction_descriptor = 2;

View File

@@ -29,6 +29,7 @@
#include "source/fuzz/transformation_add_function.h"
#include "source/fuzz/transformation_add_global_undef.h"
#include "source/fuzz/transformation_add_global_variable.h"
#include "source/fuzz/transformation_add_image_sample_unused_components.h"
#include "source/fuzz/transformation_add_local_variable.h"
#include "source/fuzz/transformation_add_no_contraction_decoration.h"
#include "source/fuzz/transformation_add_parameter.h"
@@ -57,10 +58,12 @@
#include "source/fuzz/transformation_permute_function_parameters.h"
#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_boolean_constant_with_constant_binary.h"
#include "source/fuzz/transformation_replace_constant_with_uniform.h"
#include "source/fuzz/transformation_replace_id_with_synonym.h"
#include "source/fuzz/transformation_replace_linear_algebra_instruction.h"
#include "source/fuzz/transformation_replace_parameter_with_global.h"
#include "source/fuzz/transformation_set_function_control.h"
#include "source/fuzz/transformation_set_loop_control.h"
#include "source/fuzz/transformation_set_memory_operands_mask.h"
@@ -112,6 +115,10 @@ std::unique_ptr<Transformation> Transformation::FromMessage(
case protobufs::Transformation::TransformationCase::kAddGlobalVariable:
return MakeUnique<TransformationAddGlobalVariable>(
message.add_global_variable());
case protobufs::Transformation::TransformationCase::
kAddImageSampleUnusedComponents:
return MakeUnique<TransformationAddImageSampleUnusedComponents>(
message.add_image_sample_unused_components());
case protobufs::Transformation::TransformationCase::kAddLocalVariable:
return MakeUnique<TransformationAddLocalVariable>(
message.add_local_variable());
@@ -188,6 +195,14 @@ std::unique_ptr<Transformation> Transformation::FromMessage(
case protobufs::Transformation::TransformationCase::kPushIdThroughVariable:
return MakeUnique<TransformationPushIdThroughVariable>(
message.push_id_through_variable());
case protobufs::Transformation::TransformationCase::
kRecordSynonymousConstants:
return MakeUnique<TransformationRecordSynonymousConstants>(
message.record_synonymous_constants());
case protobufs::Transformation::TransformationCase::
kReplaceParameterWithGlobal:
return MakeUnique<TransformationReplaceParameterWithGlobal>(
message.replace_parameter_with_global());
case protobufs::Transformation::TransformationCase::
kReplaceBooleanConstantWithConstantBinary:
return MakeUnique<TransformationReplaceBooleanConstantWithConstantBinary>(

View File

@@ -40,8 +40,8 @@ bool TransformationAddDeadBlock::IsApplicable(
// First, we check that a constant with the same value as
// |message_.condition_value| is present.
if (!fuzzerutil::MaybeGetBoolConstantId(ir_context,
message_.condition_value())) {
if (!fuzzerutil::MaybeGetBoolConstant(ir_context,
message_.condition_value())) {
// The required constant is not present, so the transformation cannot be
// applied.
return false;
@@ -92,8 +92,8 @@ void TransformationAddDeadBlock::Apply(
existing_block->terminator()->GetSingleWordInOperand(0);
// Get the id of the boolean value that will be used as the branch condition.
auto bool_id = fuzzerutil::MaybeGetBoolConstantId(ir_context,
message_.condition_value());
auto bool_id =
fuzzerutil::MaybeGetBoolConstant(ir_context, message_.condition_value());
// Make a new block that unconditionally branches to the original successor
// block.

View File

@@ -112,8 +112,8 @@ bool TransformationAddDeadBreak::IsApplicable(
const TransformationContext& transformation_context) const {
// First, we check that a constant with the same value as
// |message_.break_condition_value| is present.
if (!fuzzerutil::MaybeGetBoolConstantId(ir_context,
message_.break_condition_value())) {
if (!fuzzerutil::MaybeGetBoolConstant(ir_context,
message_.break_condition_value())) {
// The required constant is not present, so the transformation cannot be
// applied.
return false;

View File

@@ -38,8 +38,8 @@ bool TransformationAddDeadContinue::IsApplicable(
const TransformationContext& transformation_context) const {
// First, we check that a constant with the same value as
// |message_.continue_condition_value| is present.
if (!fuzzerutil::MaybeGetBoolConstantId(
ir_context, message_.continue_condition_value())) {
if (!fuzzerutil::MaybeGetBoolConstant(ir_context,
message_.continue_condition_value())) {
// The required constant is not present, so the transformation cannot be
// applied.
return false;

View File

@@ -98,8 +98,6 @@ void TransformationAddGlobalVariable::Apply(
static_cast<SpvStorageClass>(message_.storage_class()),
message_.initializer_id());
fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_id());
if (message_.value_is_irrelevant()) {
transformation_context->GetFactManager()->AddFactValueOfPointeeIsIrrelevant(
message_.fresh_id());

View File

@@ -0,0 +1,117 @@
// Copyright (c) 2020 André Perez Maselco
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "source/fuzz/transformation_add_image_sample_unused_components.h"
#include "source/fuzz/fuzzer_util.h"
#include "source/fuzz/instruction_descriptor.h"
namespace spvtools {
namespace fuzz {
TransformationAddImageSampleUnusedComponents::
TransformationAddImageSampleUnusedComponents(
const spvtools::fuzz::protobufs::
TransformationAddImageSampleUnusedComponents& message)
: message_(message) {}
TransformationAddImageSampleUnusedComponents::
TransformationAddImageSampleUnusedComponents(
uint32_t coordinate_with_unused_components_id,
const protobufs::InstructionDescriptor& instruction_descriptor) {
message_.set_coordinate_with_unused_components_id(
coordinate_with_unused_components_id);
*message_.mutable_instruction_descriptor() = instruction_descriptor;
}
bool TransformationAddImageSampleUnusedComponents::IsApplicable(
opt::IRContext* ir_context, const TransformationContext& /*unused*/) const {
auto image_sample_instruction =
FindInstruction(message_.instruction_descriptor(), ir_context);
// The image sample instruction must be defined.
if (image_sample_instruction == nullptr) {
return false;
}
// The instruction must be an image sample instruction.
if (!spvOpcodeIsImageSample(image_sample_instruction->opcode())) {
return false;
}
uint32_t coordinate_id = image_sample_instruction->GetSingleWordInOperand(1);
auto coordinate_instruction =
ir_context->get_def_use_mgr()->GetDef(coordinate_id);
auto coordinate_type =
ir_context->get_type_mgr()->GetType(coordinate_instruction->type_id());
// It must be possible to add unused components.
if (coordinate_type->AsVector() &&
coordinate_type->AsVector()->element_count() == 4) {
return false;
}
auto coordinate_with_unused_components_instruction =
ir_context->get_def_use_mgr()->GetDef(
message_.coordinate_with_unused_components_id());
// The coordinate with unused components instruction must be defined.
if (coordinate_with_unused_components_instruction == nullptr) {
return false;
}
// It must be an OpCompositeConstruct instruction such that it can be checked
// that the original components are present.
if (coordinate_with_unused_components_instruction->opcode() !=
SpvOpCompositeConstruct) {
return false;
}
// The first constituent must be the original coordinate.
if (coordinate_with_unused_components_instruction->GetSingleWordInOperand(
0) != coordinate_id) {
return false;
}
auto coordinate_with_unused_components_type =
ir_context->get_type_mgr()->GetType(
coordinate_with_unused_components_instruction->type_id());
// |coordinate_with_unused_components_type| must be a vector.
if (!coordinate_with_unused_components_type->AsVector()) {
return false;
}
return true;
}
void TransformationAddImageSampleUnusedComponents::Apply(
opt::IRContext* ir_context, TransformationContext* /*unused*/) const {
// Sets the coordinate operand.
auto image_sample_instruction =
FindInstruction(message_.instruction_descriptor(), ir_context);
image_sample_instruction->SetInOperand(
1, {message_.coordinate_with_unused_components_id()});
ir_context->InvalidateAnalysesExceptFor(opt::IRContext::kAnalysisNone);
}
protobufs::Transformation
TransformationAddImageSampleUnusedComponents::ToMessage() const {
protobufs::Transformation result;
*result.mutable_add_image_sample_unused_components() = message_;
return result;
}
} // namespace fuzz
} // namespace spvtools

View File

@@ -0,0 +1,57 @@
// Copyright (c) 2020 André Perez Maselco
//
// 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_ADD_IMAGE_SAMPLE_UNUSED_COMPONENTS_H_
#define SOURCE_FUZZ_TRANSFORMATION_ADD_IMAGE_SAMPLE_UNUSED_COMPONENTS_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 TransformationAddImageSampleUnusedComponents : public Transformation {
public:
explicit TransformationAddImageSampleUnusedComponents(
const protobufs::TransformationAddImageSampleUnusedComponents& message);
TransformationAddImageSampleUnusedComponents(
uint32_t coordinate_with_unused_components_id,
const protobufs::InstructionDescriptor& instruction_descriptor);
// - |coordinate_with_unused_components_id| must identify a vector such that
// the first components match the components of the image sample coordinate.
// - |message_.instruction_descriptor| must identify an image sample
// instruction
bool IsApplicable(
opt::IRContext* ir_context,
const TransformationContext& transformation_context) const override;
// Add unused components to an image sample coordinate by replacing the
// coordinate with |coordinate_with_unused_components_id|.
void Apply(opt::IRContext* ir_context,
TransformationContext* transformation_context) const override;
protobufs::Transformation ToMessage() const override;
private:
protobufs::TransformationAddImageSampleUnusedComponents message_;
};
} // namespace fuzz
} // namespace spvtools
#endif // SOURCE_FUZZ_TRANSFORMATION_ADD_IMAGE_SAMPLE_UNUSED_COMPONENTS_H_

View File

@@ -74,8 +74,6 @@ void TransformationAddLocalVariable::Apply(
message_.type_id(), message_.function_id(),
message_.initializer_id());
fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_id());
if (message_.value_is_irrelevant()) {
transformation_context->GetFactManager()->AddFactValueOfPointeeIsIrrelevant(
message_.fresh_id());

View File

@@ -127,8 +127,6 @@ void TransformationPushIdThroughVariable::Apply(
message_.initializer_id());
}
fuzzerutil::UpdateModuleIdBound(ir_context, message_.variable_id());
// Stores value id to variable id.
FindInstruction(message_.instruction_descriptor(), ir_context)
->InsertBefore(MakeUnique<opt::Instruction>(

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

@@ -47,6 +47,8 @@ bool TransformationReplaceLinearAlgebraInstruction::IsApplicable(
// It must be a supported linear algebra instruction.
if (instruction->opcode() != SpvOpVectorTimesScalar &&
instruction->opcode() != SpvOpMatrixTimesScalar &&
instruction->opcode() != SpvOpVectorTimesMatrix &&
instruction->opcode() != SpvOpMatrixTimesVector &&
instruction->opcode() != SpvOpDot) {
return false;
}
@@ -59,9 +61,8 @@ bool TransformationReplaceLinearAlgebraInstruction::IsApplicable(
}
// All ids in |message_.fresh_ids| must be fresh.
for (uint32_t i = 0; i < static_cast<uint32_t>(message_.fresh_ids().size());
i++) {
if (!fuzzerutil::IsFreshId(ir_context, message_.fresh_ids(i))) {
for (uint32_t fresh_id : message_.fresh_ids()) {
if (!fuzzerutil::IsFreshId(ir_context, fresh_id)) {
return false;
}
}
@@ -81,6 +82,12 @@ void TransformationReplaceLinearAlgebraInstruction::Apply(
case SpvOpMatrixTimesScalar:
ReplaceOpMatrixTimesScalar(ir_context, linear_algebra_instruction);
break;
case SpvOpVectorTimesMatrix:
ReplaceOpVectorTimesMatrix(ir_context, linear_algebra_instruction);
break;
case SpvOpMatrixTimesVector:
ReplaceOpMatrixTimesVector(ir_context, linear_algebra_instruction);
break;
case SpvOpDot:
ReplaceOpDot(ir_context, linear_algebra_instruction);
break;
@@ -128,6 +135,49 @@ uint32_t TransformationReplaceLinearAlgebraInstruction::GetRequiredFreshIdCount(
->AsVector()
->element_count());
}
case SpvOpVectorTimesMatrix: {
// For each vector component, 1 OpCompositeExtract instruction will be
// inserted. For each matrix column, |1 + vector_component_count|
// OpCompositeExtract, |vector_component_count| OpFMul and
// |vector_component_count - 1| OpFAdd instructions will be inserted.
auto vector_instruction = ir_context->get_def_use_mgr()->GetDef(
instruction->GetSingleWordInOperand(0));
auto matrix_instruction = ir_context->get_def_use_mgr()->GetDef(
instruction->GetSingleWordInOperand(1));
uint32_t vector_component_count =
ir_context->get_type_mgr()
->GetType(vector_instruction->type_id())
->AsVector()
->element_count();
uint32_t matrix_column_count =
ir_context->get_type_mgr()
->GetType(matrix_instruction->type_id())
->AsMatrix()
->element_count();
return vector_component_count * (3 * matrix_column_count + 1);
}
case SpvOpMatrixTimesVector: {
// For each matrix column, |1 + matrix_row_count| OpCompositeExtract
// will be inserted. For each matrix row, |matrix_column_count| OpFMul and
// |matrix_column_count - 1| OpFAdd instructions will be inserted. For
// each vector component, 1 OpCompositeExtract instruction will be
// inserted.
auto matrix_instruction = ir_context->get_def_use_mgr()->GetDef(
instruction->GetSingleWordInOperand(0));
uint32_t matrix_column_count =
ir_context->get_type_mgr()
->GetType(matrix_instruction->type_id())
->AsMatrix()
->element_count();
uint32_t matrix_row_count = ir_context->get_type_mgr()
->GetType(matrix_instruction->type_id())
->AsMatrix()
->element_type()
->AsVector()
->element_count();
return 3 * matrix_column_count * matrix_row_count +
2 * matrix_column_count - matrix_row_count;
}
case SpvOpDot:
// For each pair of vector components, 2 OpCompositeExtract and 1 OpFMul
// will be inserted. The first two OpFMul instructions will result the
@@ -280,6 +330,236 @@ void TransformationReplaceLinearAlgebraInstruction::ReplaceOpMatrixTimesScalar(
}
}
void TransformationReplaceLinearAlgebraInstruction::ReplaceOpVectorTimesMatrix(
opt::IRContext* ir_context,
opt::Instruction* linear_algebra_instruction) const {
// Gets vector information.
auto vector_instruction = ir_context->get_def_use_mgr()->GetDef(
linear_algebra_instruction->GetSingleWordInOperand(0));
uint32_t vector_component_count = ir_context->get_type_mgr()
->GetType(vector_instruction->type_id())
->AsVector()
->element_count();
auto vector_component_type = ir_context->get_type_mgr()
->GetType(vector_instruction->type_id())
->AsVector()
->element_type();
// Extracts vector components.
uint32_t fresh_id_index = 0;
std::vector<uint32_t> vector_component_ids(vector_component_count);
for (uint32_t i = 0; i < vector_component_count; i++) {
vector_component_ids[i] = message_.fresh_ids(fresh_id_index++);
linear_algebra_instruction->InsertBefore(MakeUnique<opt::Instruction>(
ir_context, SpvOpCompositeExtract,
ir_context->get_type_mgr()->GetId(vector_component_type),
vector_component_ids[i],
opt::Instruction::OperandList(
{{SPV_OPERAND_TYPE_ID, {vector_instruction->result_id()}},
{SPV_OPERAND_TYPE_LITERAL_INTEGER, {i}}})));
}
// Gets matrix information.
auto matrix_instruction = ir_context->get_def_use_mgr()->GetDef(
linear_algebra_instruction->GetSingleWordInOperand(1));
uint32_t matrix_column_count = ir_context->get_type_mgr()
->GetType(matrix_instruction->type_id())
->AsMatrix()
->element_count();
auto matrix_column_type = ir_context->get_type_mgr()
->GetType(matrix_instruction->type_id())
->AsMatrix()
->element_type();
std::vector<uint32_t> result_component_ids(matrix_column_count);
for (uint32_t i = 0; i < matrix_column_count; i++) {
// Extracts matrix column.
uint32_t matrix_extract_id = message_.fresh_ids(fresh_id_index++);
linear_algebra_instruction->InsertBefore(MakeUnique<opt::Instruction>(
ir_context, SpvOpCompositeExtract,
ir_context->get_type_mgr()->GetId(matrix_column_type),
matrix_extract_id,
opt::Instruction::OperandList(
{{SPV_OPERAND_TYPE_ID, {matrix_instruction->result_id()}},
{SPV_OPERAND_TYPE_LITERAL_INTEGER, {i}}})));
std::vector<uint32_t> float_multiplication_ids(vector_component_count);
for (uint32_t j = 0; j < vector_component_count; j++) {
// Extracts column component.
uint32_t column_extract_id = message_.fresh_ids(fresh_id_index++);
linear_algebra_instruction->InsertBefore(MakeUnique<opt::Instruction>(
ir_context, SpvOpCompositeExtract,
ir_context->get_type_mgr()->GetId(vector_component_type),
column_extract_id,
opt::Instruction::OperandList(
{{SPV_OPERAND_TYPE_ID, {matrix_extract_id}},
{SPV_OPERAND_TYPE_LITERAL_INTEGER, {j}}})));
// Multiplies corresponding vector and column components.
float_multiplication_ids[j] = message_.fresh_ids(fresh_id_index++);
linear_algebra_instruction->InsertBefore(MakeUnique<opt::Instruction>(
ir_context, SpvOpFMul,
ir_context->get_type_mgr()->GetId(vector_component_type),
float_multiplication_ids[j],
opt::Instruction::OperandList(
{{SPV_OPERAND_TYPE_ID, {vector_component_ids[j]}},
{SPV_OPERAND_TYPE_ID, {column_extract_id}}})));
}
// Adds the multiplication results.
std::vector<uint32_t> float_add_ids;
uint32_t float_add_id = message_.fresh_ids(fresh_id_index++);
float_add_ids.push_back(float_add_id);
linear_algebra_instruction->InsertBefore(MakeUnique<opt::Instruction>(
ir_context, SpvOpFAdd,
ir_context->get_type_mgr()->GetId(vector_component_type), float_add_id,
opt::Instruction::OperandList(
{{SPV_OPERAND_TYPE_ID, {float_multiplication_ids[0]}},
{SPV_OPERAND_TYPE_ID, {float_multiplication_ids[1]}}})));
for (uint32_t j = 2; j < float_multiplication_ids.size(); j++) {
float_add_id = message_.fresh_ids(fresh_id_index++);
float_add_ids.push_back(float_add_id);
linear_algebra_instruction->InsertBefore(MakeUnique<opt::Instruction>(
ir_context, SpvOpFAdd,
ir_context->get_type_mgr()->GetId(vector_component_type),
float_add_id,
opt::Instruction::OperandList(
{{SPV_OPERAND_TYPE_ID, {float_multiplication_ids[j]}},
{SPV_OPERAND_TYPE_ID, {float_add_ids[j - 2]}}})));
}
result_component_ids[i] = float_add_ids.back();
}
// The OpVectorTimesMatrix instruction is changed to an OpCompositeConstruct
// instruction.
linear_algebra_instruction->SetOpcode(SpvOpCompositeConstruct);
linear_algebra_instruction->SetInOperand(0, {result_component_ids[0]});
linear_algebra_instruction->SetInOperand(1, {result_component_ids[1]});
for (uint32_t i = 2; i < result_component_ids.size(); i++) {
linear_algebra_instruction->AddOperand(
{SPV_OPERAND_TYPE_ID, {result_component_ids[i]}});
}
fuzzerutil::UpdateModuleIdBound(
ir_context, message_.fresh_ids(message_.fresh_ids().size() - 1));
}
void TransformationReplaceLinearAlgebraInstruction::ReplaceOpMatrixTimesVector(
opt::IRContext* ir_context,
opt::Instruction* linear_algebra_instruction) const {
// Gets matrix information.
auto matrix_instruction = ir_context->get_def_use_mgr()->GetDef(
linear_algebra_instruction->GetSingleWordInOperand(0));
uint32_t matrix_column_count = ir_context->get_type_mgr()
->GetType(matrix_instruction->type_id())
->AsMatrix()
->element_count();
auto matrix_column_type = ir_context->get_type_mgr()
->GetType(matrix_instruction->type_id())
->AsMatrix()
->element_type();
uint32_t matrix_row_count = matrix_column_type->AsVector()->element_count();
// Extracts matrix columns.
uint32_t fresh_id_index = 0;
std::vector<uint32_t> matrix_column_ids(matrix_column_count);
for (uint32_t i = 0; i < matrix_column_count; i++) {
matrix_column_ids[i] = message_.fresh_ids(fresh_id_index++);
linear_algebra_instruction->InsertBefore(MakeUnique<opt::Instruction>(
ir_context, SpvOpCompositeExtract,
ir_context->get_type_mgr()->GetId(matrix_column_type),
matrix_column_ids[i],
opt::Instruction::OperandList(
{{SPV_OPERAND_TYPE_ID, {matrix_instruction->result_id()}},
{SPV_OPERAND_TYPE_LITERAL_INTEGER, {i}}})));
}
// Gets vector information.
auto vector_instruction = ir_context->get_def_use_mgr()->GetDef(
linear_algebra_instruction->GetSingleWordInOperand(1));
auto vector_component_type = ir_context->get_type_mgr()
->GetType(vector_instruction->type_id())
->AsVector()
->element_type();
// Extracts vector components.
std::vector<uint32_t> vector_component_ids(matrix_column_count);
for (uint32_t i = 0; i < matrix_column_count; i++) {
vector_component_ids[i] = message_.fresh_ids(fresh_id_index++);
linear_algebra_instruction->InsertBefore(MakeUnique<opt::Instruction>(
ir_context, SpvOpCompositeExtract,
ir_context->get_type_mgr()->GetId(vector_component_type),
vector_component_ids[i],
opt::Instruction::OperandList(
{{SPV_OPERAND_TYPE_ID, {vector_instruction->result_id()}},
{SPV_OPERAND_TYPE_LITERAL_INTEGER, {i}}})));
}
std::vector<uint32_t> result_component_ids(matrix_row_count);
for (uint32_t i = 0; i < matrix_row_count; i++) {
std::vector<uint32_t> float_multiplication_ids(matrix_column_count);
for (uint32_t j = 0; j < matrix_column_count; j++) {
// Extracts column component.
uint32_t column_extract_id = message_.fresh_ids(fresh_id_index++);
linear_algebra_instruction->InsertBefore(MakeUnique<opt::Instruction>(
ir_context, SpvOpCompositeExtract,
ir_context->get_type_mgr()->GetId(vector_component_type),
column_extract_id,
opt::Instruction::OperandList(
{{SPV_OPERAND_TYPE_ID, {matrix_column_ids[j]}},
{SPV_OPERAND_TYPE_LITERAL_INTEGER, {i}}})));
// Multiplies corresponding vector and column components.
float_multiplication_ids[j] = message_.fresh_ids(fresh_id_index++);
linear_algebra_instruction->InsertBefore(MakeUnique<opt::Instruction>(
ir_context, SpvOpFMul,
ir_context->get_type_mgr()->GetId(vector_component_type),
float_multiplication_ids[j],
opt::Instruction::OperandList(
{{SPV_OPERAND_TYPE_ID, {column_extract_id}},
{SPV_OPERAND_TYPE_ID, {vector_component_ids[j]}}})));
}
// Adds the multiplication results.
std::vector<uint32_t> float_add_ids;
uint32_t float_add_id = message_.fresh_ids(fresh_id_index++);
float_add_ids.push_back(float_add_id);
linear_algebra_instruction->InsertBefore(MakeUnique<opt::Instruction>(
ir_context, SpvOpFAdd,
ir_context->get_type_mgr()->GetId(vector_component_type), float_add_id,
opt::Instruction::OperandList(
{{SPV_OPERAND_TYPE_ID, {float_multiplication_ids[0]}},
{SPV_OPERAND_TYPE_ID, {float_multiplication_ids[1]}}})));
for (uint32_t j = 2; j < float_multiplication_ids.size(); j++) {
float_add_id = message_.fresh_ids(fresh_id_index++);
float_add_ids.push_back(float_add_id);
linear_algebra_instruction->InsertBefore(MakeUnique<opt::Instruction>(
ir_context, SpvOpFAdd,
ir_context->get_type_mgr()->GetId(vector_component_type),
float_add_id,
opt::Instruction::OperandList(
{{SPV_OPERAND_TYPE_ID, {float_multiplication_ids[j]}},
{SPV_OPERAND_TYPE_ID, {float_add_ids[j - 2]}}})));
}
result_component_ids[i] = float_add_ids.back();
}
// The OpMatrixTimesVector instruction is changed to an OpCompositeConstruct
// instruction.
linear_algebra_instruction->SetOpcode(SpvOpCompositeConstruct);
linear_algebra_instruction->SetInOperand(0, {result_component_ids[0]});
linear_algebra_instruction->SetInOperand(1, {result_component_ids[1]});
for (uint32_t i = 2; i < result_component_ids.size(); i++) {
linear_algebra_instruction->AddOperand(
{SPV_OPERAND_TYPE_ID, {result_component_ids[i]}});
}
fuzzerutil::UpdateModuleIdBound(
ir_context, message_.fresh_ids(message_.fresh_ids().size() - 1));
}
void TransformationReplaceLinearAlgebraInstruction::ReplaceOpDot(
opt::IRContext* ir_context,
opt::Instruction* linear_algebra_instruction) const {

View File

@@ -60,6 +60,14 @@ class TransformationReplaceLinearAlgebraInstruction : public Transformation {
void ReplaceOpMatrixTimesScalar(opt::IRContext* ir_context,
opt::Instruction* instruction) const;
// Replaces an OpVectorTimesMatrix instruction.
void ReplaceOpVectorTimesMatrix(opt::IRContext* ir_context,
opt::Instruction* instruction) const;
// Replaces an OpMatrixTimesVector instruction.
void ReplaceOpMatrixTimesVector(opt::IRContext* ir_context,
opt::Instruction* instruction) const;
// Replaces an OpDot instruction.
void ReplaceOpDot(opt::IRContext* ir_context,
opt::Instruction* instruction) const;

View File

@@ -0,0 +1,245 @@
// Copyright (c) 2020 Vasyl Teliman
//
// 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_parameter_with_global.h"
#include <vector>
#include "source/fuzz/fuzzer_util.h"
namespace spvtools {
namespace fuzz {
namespace {
opt::Function* GetFunctionFromParameterId(opt::IRContext* ir_context,
uint32_t param_id) {
auto* param_inst = ir_context->get_def_use_mgr()->GetDef(param_id);
assert(param_inst && "Parameter id is invalid");
for (auto& function : *ir_context->module()) {
if (fuzzerutil::InstructionIsFunctionParameter(param_inst, &function)) {
return &function;
}
}
return nullptr;
}
} // namespace
TransformationReplaceParameterWithGlobal::
TransformationReplaceParameterWithGlobal(
const protobufs::TransformationReplaceParameterWithGlobal& message)
: message_(message) {}
TransformationReplaceParameterWithGlobal::
TransformationReplaceParameterWithGlobal(
uint32_t function_type_fresh_id, uint32_t parameter_id,
uint32_t global_variable_fresh_id) {
message_.set_function_type_fresh_id(function_type_fresh_id);
message_.set_parameter_id(parameter_id);
message_.set_global_variable_fresh_id(global_variable_fresh_id);
}
bool TransformationReplaceParameterWithGlobal::IsApplicable(
opt::IRContext* ir_context, const TransformationContext& /*unused*/) const {
// Check that |parameter_id| is valid.
const auto* param_inst =
ir_context->get_def_use_mgr()->GetDef(message_.parameter_id());
if (!param_inst || param_inst->opcode() != SpvOpFunctionParameter) {
return false;
}
// Check that function exists and is not an entry point.
const auto* function =
GetFunctionFromParameterId(ir_context, message_.parameter_id());
if (!function ||
fuzzerutil::FunctionIsEntryPoint(ir_context, function->result_id())) {
return false;
}
// We already know that the function has at least one parameter -
// |parameter_id|.
// Check that replaced parameter has valid type.
const auto* param_type =
ir_context->get_type_mgr()->GetType(param_inst->type_id());
assert(param_type && "Parameter has invalid type");
if (!CanReplaceFunctionParameterType(*param_type)) {
return false;
}
// Check that initializer for the global variable exists in the module.
if (fuzzerutil::MaybeGetZeroConstant(ir_context, param_inst->type_id()) ==
0) {
return false;
}
// Check that pointer type for the global variable exists in the module.
if (!fuzzerutil::MaybeGetPointerType(ir_context, param_inst->type_id(),
SpvStorageClassPrivate)) {
return false;
}
return fuzzerutil::IsFreshId(ir_context, message_.function_type_fresh_id()) &&
fuzzerutil::IsFreshId(ir_context,
message_.global_variable_fresh_id()) &&
message_.function_type_fresh_id() !=
message_.global_variable_fresh_id();
}
void TransformationReplaceParameterWithGlobal::Apply(
opt::IRContext* ir_context, TransformationContext* /*unused*/) const {
const auto* param_inst =
ir_context->get_def_use_mgr()->GetDef(message_.parameter_id());
assert(param_inst && "Parameter must exist");
// Create global variable to store parameter's value.
//
// TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3177):
// Mark the global variable's pointee as irrelevant if replaced parameter is
// irrelevant.
fuzzerutil::AddGlobalVariable(
ir_context, message_.global_variable_fresh_id(),
fuzzerutil::MaybeGetPointerType(ir_context, param_inst->type_id(),
SpvStorageClassPrivate),
SpvStorageClassPrivate,
fuzzerutil::MaybeGetZeroConstant(ir_context, param_inst->type_id()));
auto* function =
GetFunctionFromParameterId(ir_context, message_.parameter_id());
assert(function && "Function must exist");
// Insert an OpLoad instruction right after OpVariable instructions.
auto it = function->begin()->begin();
while (it != function->begin()->end() &&
!fuzzerutil::CanInsertOpcodeBeforeInstruction(SpvOpLoad, it)) {
++it;
}
assert(fuzzerutil::CanInsertOpcodeBeforeInstruction(SpvOpLoad, it) &&
"Can't insert OpLoad or OpCopyMemory into the first basic block of "
"the function");
it.InsertBefore(MakeUnique<opt::Instruction>(
ir_context, SpvOpLoad, param_inst->type_id(), param_inst->result_id(),
opt::Instruction::OperandList{
{SPV_OPERAND_TYPE_ID, {message_.global_variable_fresh_id()}}}));
// Calculate the index of the replaced parameter (we need to know this to
// remove operands from the OpFunctionCall).
auto params = fuzzerutil::GetParameters(ir_context, function->result_id());
auto parameter_index = static_cast<uint32_t>(params.size());
for (uint32_t i = 0, n = static_cast<uint32_t>(params.size()); i < n; ++i) {
if (params[i]->result_id() == message_.parameter_id()) {
parameter_index = i;
break;
}
}
assert(parameter_index != params.size() &&
"Parameter must exist in the function");
// Update all relevant OpFunctionCall instructions.
ir_context->get_def_use_mgr()->ForEachUser(
function->result_id(),
[ir_context, parameter_index, this](opt::Instruction* inst) {
if (inst->opcode() != SpvOpFunctionCall) {
return;
}
assert(fuzzerutil::CanInsertOpcodeBeforeInstruction(SpvOpStore, inst) &&
"Can't insert OpStore right before the function call");
// Insert an OpStore before the OpFunctionCall. +1 since the first
// operand of OpFunctionCall is an id of the function.
inst->InsertBefore(MakeUnique<opt::Instruction>(
ir_context, SpvOpStore, 0, 0,
opt::Instruction::OperandList{
{SPV_OPERAND_TYPE_ID, {message_.global_variable_fresh_id()}},
{SPV_OPERAND_TYPE_ID,
{inst->GetSingleWordInOperand(parameter_index + 1)}}}));
// +1 since the first operand of OpFunctionCall is an id of the
// function.
inst->RemoveInOperand(parameter_index + 1);
});
// Remove the parameter from the function.
function->RemoveParameter(message_.parameter_id());
// Update function's type.
auto* old_function_type = fuzzerutil::GetFunctionType(ir_context, function);
assert(old_function_type && "Function has invalid type");
// Preemptively add function's return type id.
std::vector<uint32_t> type_ids = {
old_function_type->GetSingleWordInOperand(0)};
// +1 and -1 since the first operand is the return type id.
for (uint32_t i = 1; i < old_function_type->NumInOperands(); ++i) {
if (i - 1 != parameter_index) {
type_ids.push_back(old_function_type->GetSingleWordInOperand(i));
}
}
if (ir_context->get_def_use_mgr()->NumUsers(old_function_type) == 1) {
// Change the old type in place. +1 since the first operand is the result
// type id of the function.
old_function_type->RemoveInOperand(parameter_index + 1);
} else {
// Find an existing or create a new function type.
function->DefInst().SetInOperand(
1, {fuzzerutil::FindOrCreateFunctionType(
ir_context, message_.function_type_fresh_id(), type_ids)});
}
// Make sure our changes are analyzed
ir_context->InvalidateAnalysesExceptFor(
opt::IRContext::Analysis::kAnalysisNone);
}
protobufs::Transformation TransformationReplaceParameterWithGlobal::ToMessage()
const {
protobufs::Transformation result;
*result.mutable_replace_parameter_with_global() = message_;
return result;
}
bool TransformationReplaceParameterWithGlobal::CanReplaceFunctionParameterType(
const opt::analysis::Type& type) {
// TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3403):
// Think about other type instructions we can add here.
switch (type.kind()) {
case opt::analysis::Type::kBool:
case opt::analysis::Type::kInteger:
case opt::analysis::Type::kFloat:
case opt::analysis::Type::kArray:
case opt::analysis::Type::kMatrix:
case opt::analysis::Type::kVector:
return true;
case opt::analysis::Type::kStruct:
return std::all_of(
type.AsStruct()->element_types().begin(),
type.AsStruct()->element_types().end(),
[](const opt::analysis::Type* element_type) {
return CanReplaceFunctionParameterType(*element_type);
});
default:
return false;
}
}
} // namespace fuzz
} // namespace spvtools

View File

@@ -0,0 +1,67 @@
// Copyright (c) 2020 Vasyl Teliman
//
// 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_REPLACE_PARAMETER_WITH_GLOBAL_H_
#define SOURCE_FUZZ_TRANSFORMATION_REPLACE_PARAMETER_WITH_GLOBAL_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 TransformationReplaceParameterWithGlobal : public Transformation {
public:
explicit TransformationReplaceParameterWithGlobal(
const protobufs::TransformationReplaceParameterWithGlobal& message);
TransformationReplaceParameterWithGlobal(uint32_t function_type_fresh_id,
uint32_t parameter_id,
uint32_t global_variable_fresh_id);
// - |function_type_fresh_id| is a fresh id.
// - |parameter_id| is the result id of the parameter to replace.
// - |global_variable_fresh_id| is a fresh id.
// - |function_type_fresh_id| is not equal to |global_variable_fresh_id|.
// - the function that contains |parameter_id| may not be an entry-point
// function.
bool IsApplicable(
opt::IRContext* ir_context,
const TransformationContext& transformation_context) const override;
// - Removes parameter with result id |parameter_id| from its function
// - Adds a global variable to store the value for the parameter
// - Add an OpStore instruction before each function call to
// store parameter's value into the variable
// - Adds OpLoad at the beginning of the function to load the
// value from the variable into the old parameter's id
void Apply(opt::IRContext* ir_context,
TransformationContext* transformation_context) const override;
protobufs::Transformation ToMessage() const override;
// Returns true if the type of the parameter is supported by this
// transformation.
static bool CanReplaceFunctionParameterType(const opt::analysis::Type& type);
private:
protobufs::TransformationReplaceParameterWithGlobal message_;
};
} // namespace fuzz
} // namespace spvtools
#endif // SOURCE_FUZZ_TRANSFORMATION_REPLACE_PARAMETER_WITH_GLOBAL_H_

View File

@@ -666,6 +666,26 @@ bool spvOpcodeIsLinearAlgebra(SpvOp opcode) {
}
}
bool spvOpcodeIsImageSample(const SpvOp opcode) {
switch (opcode) {
case SpvOpImageSampleImplicitLod:
case SpvOpImageSampleExplicitLod:
case SpvOpImageSampleDrefImplicitLod:
case SpvOpImageSampleDrefExplicitLod:
case SpvOpImageSampleProjImplicitLod:
case SpvOpImageSampleProjExplicitLod:
case SpvOpImageSampleProjDrefImplicitLod:
case SpvOpImageSampleProjDrefExplicitLod:
case SpvOpImageSparseSampleImplicitLod:
case SpvOpImageSparseSampleExplicitLod:
case SpvOpImageSparseSampleDrefImplicitLod:
case SpvOpImageSparseSampleDrefExplicitLod:
return true;
default:
return false;
}
}
std::vector<uint32_t> spvOpcodeMemorySemanticsOperandIndices(SpvOp opcode) {
switch (opcode) {
case SpvOpMemoryBarrier:

View File

@@ -137,6 +137,9 @@ bool spvOpcodeIsCommutativeBinaryOperator(SpvOp opcode);
// Returns true for opcodes that represents linear algebra instructions.
bool spvOpcodeIsLinearAlgebra(SpvOp opcode);
// Returns true for opcodes that represents an image sample instruction.
bool spvOpcodeIsImageSample(SpvOp opcode);
// Returns a vector containing the indices of the memory semantics <id>
// operands for |opcode|.
std::vector<uint32_t> spvOpcodeMemorySemanticsOperandIndices(SpvOp opcode);

View File

@@ -29,6 +29,12 @@ const uint32_t kCompositeExtractObjectInOperand = 0;
const uint32_t kTypePointerStorageClassInIdx = 0;
const uint32_t kTypePointerPointeeInIdx = 1;
bool IsOpenCL100DebugDeclareOrValue(Instruction* di) {
auto dbg_opcode = di->GetOpenCL100DebugOpcode();
return dbg_opcode == OpenCLDebugInfo100DebugDeclare ||
dbg_opcode == OpenCLDebugInfo100DebugValue;
}
} // namespace
Pass::Status CopyPropagateArrays::Process() {
@@ -188,6 +194,8 @@ bool CopyPropagateArrays::HasValidReferencesOnly(Instruction* ptr_inst,
return ptr_inst->opcode() == SpvOpVariable &&
store_inst->GetSingleWordInOperand(kStorePointerInOperand) ==
ptr_inst->result_id();
} else if (IsOpenCL100DebugDeclareOrValue(use)) {
return true;
}
// Some other instruction. Be conservative.
return false;
@@ -492,6 +500,8 @@ bool CopyPropagateArrays::CanUpdateUses(Instruction* original_ptr_inst,
const_mgr,
type](Instruction* use,
uint32_t) {
if (IsOpenCL100DebugDeclareOrValue(use)) return true;
switch (use->opcode()) {
case SpvOpLoad: {
analysis::Pointer* pointer_type = type->AsPointer();
@@ -565,6 +575,7 @@ bool CopyPropagateArrays::CanUpdateUses(Instruction* original_ptr_inst,
}
});
}
void CopyPropagateArrays::UpdateUses(Instruction* original_ptr_inst,
Instruction* new_ptr_inst) {
analysis::TypeManager* type_mgr = context()->get_type_mgr();
@@ -580,6 +591,52 @@ void CopyPropagateArrays::UpdateUses(Instruction* original_ptr_inst,
for (auto pair : uses) {
Instruction* use = pair.first;
uint32_t index = pair.second;
if (use->IsOpenCL100DebugInstr()) {
switch (use->GetOpenCL100DebugOpcode()) {
case OpenCLDebugInfo100DebugDeclare: {
if (new_ptr_inst->opcode() == SpvOpVariable ||
new_ptr_inst->opcode() == SpvOpFunctionParameter) {
context()->ForgetUses(use);
use->SetOperand(index, {new_ptr_inst->result_id()});
context()->AnalyzeUses(use);
} else {
// Based on the spec, we cannot use a pointer other than OpVariable
// or OpFunctionParameter for DebugDeclare. We have to use
// DebugValue with Deref.
context()->ForgetUses(use);
// Change DebugDeclare to DebugValue.
use->SetOperand(
index - 2,
{static_cast<uint32_t>(OpenCLDebugInfo100DebugValue)});
use->SetOperand(index, {new_ptr_inst->result_id()});
// Add Deref operation.
Instruction* dbg_expr =
def_use_mgr->GetDef(use->GetSingleWordOperand(index + 1));
auto* deref_expr_instr =
context()->get_debug_info_mgr()->DerefDebugExpression(dbg_expr);
use->SetOperand(index + 1, {deref_expr_instr->result_id()});
context()->AnalyzeUses(deref_expr_instr);
context()->AnalyzeUses(use);
}
break;
}
case OpenCLDebugInfo100DebugValue:
context()->ForgetUses(use);
use->SetOperand(index, {new_ptr_inst->result_id()});
context()->AnalyzeUses(use);
break;
default:
assert(false && "Don't know how to rewrite instruction");
break;
}
continue;
}
switch (use->opcode()) {
case SpvOpLoad: {
// Replace the actual use.

View File

@@ -36,6 +36,7 @@ static const uint32_t kDebugValueOperandExpressionIndex = 6;
static const uint32_t kDebugOperationOperandOperationIndex = 4;
static const uint32_t kOpVariableOperandStorageClassIndex = 2;
static const uint32_t kDebugLocalVariableOperandParentIndex = 9;
static const uint32_t kDebugOperationOperandOpCodeIndex = 4;
namespace spvtools {
namespace opt {
@@ -244,6 +245,51 @@ uint32_t DebugInfoManager::BuildDebugInlinedAtChain(
return chain_head_id;
}
Instruction* DebugInfoManager::GetDebugOperationWithDeref() {
if (deref_operation_ != nullptr) return deref_operation_;
uint32_t result_id = context()->TakeNextId();
std::unique_ptr<Instruction> deref_operation(new Instruction(
context(), SpvOpExtInst, context()->get_type_mgr()->GetVoidTypeId(),
result_id,
{
{SPV_OPERAND_TYPE_ID,
{context()
->get_feature_mgr()
->GetExtInstImportId_OpenCL100DebugInfo()}},
{SPV_OPERAND_TYPE_EXTENSION_INSTRUCTION_NUMBER,
{static_cast<uint32_t>(OpenCLDebugInfo100DebugOperation)}},
{SPV_OPERAND_TYPE_CLDEBUG100_DEBUG_OPERATION,
{static_cast<uint32_t>(OpenCLDebugInfo100Deref)}},
}));
// Add to the front of |ext_inst_debuginfo_|.
deref_operation_ =
context()->module()->ext_inst_debuginfo_begin()->InsertBefore(
std::move(deref_operation));
RegisterDbgInst(deref_operation_);
if (context()->AreAnalysesValid(IRContext::Analysis::kAnalysisDefUse))
context()->get_def_use_mgr()->AnalyzeInstDefUse(deref_operation_);
return deref_operation_;
}
Instruction* DebugInfoManager::DerefDebugExpression(Instruction* dbg_expr) {
assert(dbg_expr->GetOpenCL100DebugOpcode() ==
OpenCLDebugInfo100DebugExpression);
std::unique_ptr<Instruction> deref_expr(dbg_expr->Clone(context()));
deref_expr->SetResultId(context()->TakeNextId());
deref_expr->InsertOperand(
kDebugExpressOperandOperationIndex,
{SPV_OPERAND_TYPE_ID, {GetDebugOperationWithDeref()->result_id()}});
auto* deref_expr_instr =
context()->ext_inst_debuginfo_end()->InsertBefore(std::move(deref_expr));
AnalyzeDebugInst(deref_expr_instr);
if (context()->AreAnalysesValid(IRContext::Analysis::kAnalysisDefUse))
context()->get_def_use_mgr()->AnalyzeInstDefUse(deref_expr_instr);
return deref_expr_instr;
}
Instruction* DebugInfoManager::GetDebugInfoNone() {
if (debug_info_none_inst_ != nullptr) return debug_info_none_inst_;
@@ -323,6 +369,29 @@ Instruction* DebugInfoManager::CloneDebugInlinedAt(uint32_t clone_inlined_at_id,
std::move(new_inlined_at));
}
bool DebugInfoManager::IsDebugDeclared(uint32_t variable_id) {
auto dbg_decl_itr = var_id_to_dbg_decl_.find(variable_id);
return dbg_decl_itr != var_id_to_dbg_decl_.end();
}
void DebugInfoManager::KillDebugDeclares(uint32_t variable_id) {
bool done = false;
while (!done) {
Instruction* kill_inst = nullptr;
auto dbg_decl_itr = var_id_to_dbg_decl_.find(variable_id);
if (dbg_decl_itr != var_id_to_dbg_decl_.end()) {
for (auto dbg_decl : dbg_decl_itr->second) {
kill_inst = dbg_decl;
break;
}
}
if (kill_inst)
context()->KillInst(kill_inst);
else
done = true;
}
}
uint32_t DebugInfoManager::GetParentScope(uint32_t child_scope) {
auto dbg_scope_itr = id_to_dbg_inst_.find(child_scope);
assert(dbg_scope_itr != id_to_dbg_inst_.end());
@@ -434,6 +503,11 @@ void DebugInfoManager::AddDebugValue(Instruction* scope_and_line,
AnalyzeDebugInst(added_dbg_value);
if (context()->AreAnalysesValid(IRContext::Analysis::kAnalysisDefUse))
context()->get_def_use_mgr()->AnalyzeInstDefUse(added_dbg_value);
if (context()->AreAnalysesValid(
IRContext::Analysis::kAnalysisInstrToBlockMapping)) {
auto insert_blk = context()->get_instr_block(insert_before);
context()->set_instr_block(added_dbg_value, insert_blk);
}
}
}
@@ -472,8 +546,7 @@ uint32_t DebugInfoManager::GetVariableIdOfDebugValueUsedForDeclare(
}
void DebugInfoManager::AnalyzeDebugInst(Instruction* dbg_inst) {
if (dbg_inst->GetOpenCL100DebugOpcode() == OpenCLDebugInfo100InstructionsMax)
return;
if (!dbg_inst->IsOpenCL100DebugInstr()) return;
RegisterDbgInst(dbg_inst);
@@ -484,6 +557,13 @@ void DebugInfoManager::AnalyzeDebugInst(Instruction* dbg_inst) {
RegisterDbgFunction(dbg_inst);
}
if (deref_operation_ == nullptr &&
dbg_inst->GetOpenCL100DebugOpcode() == OpenCLDebugInfo100DebugOperation &&
dbg_inst->GetSingleWordOperand(kDebugOperationOperandOpCodeIndex) ==
OpenCLDebugInfo100Deref) {
deref_operation_ = dbg_inst;
}
if (debug_info_none_inst_ == nullptr &&
dbg_inst->GetOpenCL100DebugOpcode() == OpenCLDebugInfo100DebugInfoNone) {
debug_info_none_inst_ = dbg_inst;
@@ -505,6 +585,7 @@ void DebugInfoManager::AnalyzeDebugInst(Instruction* dbg_inst) {
}
void DebugInfoManager::AnalyzeDebugInsts(Module& module) {
deref_operation_ = nullptr;
debug_info_none_inst_ = nullptr;
empty_debug_expr_inst_ = nullptr;
module.ForEachInst([this](Instruction* cpi) { AnalyzeDebugInst(cpi); });
@@ -513,8 +594,7 @@ void DebugInfoManager::AnalyzeDebugInsts(Module& module) {
// list.
if (empty_debug_expr_inst_ != nullptr &&
empty_debug_expr_inst_->PreviousNode() != nullptr &&
empty_debug_expr_inst_->PreviousNode()->GetOpenCL100DebugOpcode() !=
OpenCLDebugInfo100InstructionsMax) {
empty_debug_expr_inst_->PreviousNode()->IsOpenCL100DebugInstr()) {
empty_debug_expr_inst_->InsertBefore(
&*context()->module()->ext_inst_debuginfo_begin());
}
@@ -523,16 +603,14 @@ void DebugInfoManager::AnalyzeDebugInsts(Module& module) {
// list.
if (debug_info_none_inst_ != nullptr &&
debug_info_none_inst_->PreviousNode() != nullptr &&
debug_info_none_inst_->PreviousNode()->GetOpenCL100DebugOpcode() !=
OpenCLDebugInfo100InstructionsMax) {
debug_info_none_inst_->PreviousNode()->IsOpenCL100DebugInstr()) {
debug_info_none_inst_->InsertBefore(
&*context()->module()->ext_inst_debuginfo_begin());
}
}
void DebugInfoManager::ClearDebugInfo(Instruction* instr) {
if (instr == nullptr ||
instr->GetOpenCL100DebugOpcode() == OpenCLDebugInfo100InstructionsMax) {
if (instr == nullptr || !instr->IsOpenCL100DebugInstr()) {
return;
}
@@ -554,6 +632,22 @@ void DebugInfoManager::ClearDebugInfo(Instruction* instr) {
}
}
if (deref_operation_ == instr) {
deref_operation_ = nullptr;
for (auto dbg_instr_itr = context()->module()->ext_inst_debuginfo_begin();
dbg_instr_itr != context()->module()->ext_inst_debuginfo_end();
++dbg_instr_itr) {
if (instr != &*dbg_instr_itr &&
dbg_instr_itr->GetOpenCL100DebugOpcode() ==
OpenCLDebugInfo100DebugOperation &&
dbg_instr_itr->GetSingleWordOperand(
kDebugOperationOperandOpCodeIndex) == OpenCLDebugInfo100Deref) {
deref_operation_ = &*dbg_instr_itr;
break;
}
}
}
if (debug_info_none_inst_ == instr) {
debug_info_none_inst_ = nullptr;
for (auto dbg_instr_itr = context()->module()->ext_inst_debuginfo_begin();
@@ -563,6 +657,7 @@ void DebugInfoManager::ClearDebugInfo(Instruction* instr) {
dbg_instr_itr->GetOpenCL100DebugOpcode() ==
OpenCLDebugInfo100DebugInfoNone) {
debug_info_none_inst_ = &*dbg_instr_itr;
break;
}
}
}
@@ -574,6 +669,7 @@ void DebugInfoManager::ClearDebugInfo(Instruction* instr) {
++dbg_instr_itr) {
if (instr != &*dbg_instr_itr && IsEmptyDebugExpression(&*dbg_instr_itr)) {
empty_debug_expr_inst_ = &*dbg_instr_itr;
break;
}
}
}

View File

@@ -95,6 +95,10 @@ class DebugInfoManager {
uint32_t CreateDebugInlinedAt(const Instruction* line,
const DebugScope& scope);
// Clones DebugExpress instruction |dbg_expr| and add Deref Operation
// in the front of the Operation list of |dbg_expr|.
Instruction* DerefDebugExpression(Instruction* dbg_expr);
// Returns a DebugInfoNone instruction.
Instruction* GetDebugInfoNone();
@@ -129,6 +133,12 @@ class DebugInfoManager {
uint32_t BuildDebugInlinedAtChain(uint32_t callee_inlined_at,
DebugInlinedAtContext* inlined_at_ctx);
// Return true if |variable_id| has DebugDeclare or DebugVal.
bool IsDebugDeclared(uint32_t variable_id);
// Kill all DebugDeclares for |variable_id|
void KillDebugDeclares(uint32_t variable_id);
// Generates a DebugValue instruction with value |value_id| for every local
// variable that is in the scope of |scope_and_line| and whose memory is
// |variable_id| and inserts it after the instruction |insert_pos|.
@@ -149,6 +159,9 @@ class DebugInfoManager {
// does not exists.
Instruction* GetDbgInst(uint32_t id);
// Returns a DebugOperation instruction with OpCode Deref.
Instruction* GetDebugOperationWithDeref();
// Registers the debug instruction |inst| into |id_to_dbg_inst_| using id of
// |inst| as a key.
void RegisterDbgInst(Instruction* inst);
@@ -197,6 +210,9 @@ class DebugInfoManager {
std::unordered_map<uint32_t, std::unordered_set<Instruction*>>
var_id_to_dbg_decl_;
// DebugOperation whose OpCode is OpenCLDebugInfo100Deref.
Instruction* deref_operation_;
// DebugInfoNone instruction. We need only a single DebugInfoNone.
// To reuse the existing one, we keep it using this member variable.
Instruction* debug_info_none_inst_;

View File

@@ -306,6 +306,10 @@ class Instruction : public utils::IntrusiveNodeBase<Instruction> {
void RemoveOperand(uint32_t index) {
operands_.erase(operands_.begin() + index);
}
// Insert an operand before the |index|-th operand
void InsertOperand(uint32_t index, Operand&& operand) {
operands_.insert(operands_.begin() + index, operand);
}
// The following methods are similar to the above, but are for in operands.
uint32_t NumInOperands() const {
@@ -535,6 +539,11 @@ class Instruction : public utils::IntrusiveNodeBase<Instruction> {
// OpenCLDebugInfo100InstructionsMax.
OpenCLDebugInfo100Instructions GetOpenCL100DebugOpcode() const;
// Returns true if it is an OpenCL.DebugInfo.100 instruction.
bool IsOpenCL100DebugInstr() const {
return GetOpenCL100DebugOpcode() != OpenCLDebugInfo100InstructionsMax;
}
// Dump this instruction on stderr. Useful when running interactive
// debuggers.
void Dump() const;

View File

@@ -31,6 +31,11 @@ const uint32_t kStoreValIdInIdx = 1;
bool LocalSingleBlockLoadStoreElimPass::HasOnlySupportedRefs(uint32_t ptrId) {
if (supported_ref_ptrs_.find(ptrId) != supported_ref_ptrs_.end()) return true;
if (get_def_use_mgr()->WhileEachUser(ptrId, [this](Instruction* user) {
auto dbg_op = user->GetOpenCL100DebugOpcode();
if (dbg_op == OpenCLDebugInfo100DebugDeclare ||
dbg_op == OpenCLDebugInfo100DebugValue) {
return true;
}
SpvOp op = user->opcode();
if (IsNonPtrAccessChain(op) || op == SpvOpCopyObject) {
if (!HasOnlySupportedRefs(user->result_id())) {
@@ -73,10 +78,12 @@ bool LocalSingleBlockLoadStoreElimPass::LocalSingleBlockLoadStoreElim(
// variable.
if (ptrInst->opcode() == SpvOpVariable) {
// If a previous store to same variable, mark the store
// for deletion if not still used.
// for deletion if not still used. Don't delete store
// if debugging; let ssa-rewrite and DCE handle it
auto prev_store = var2store_.find(varId);
if (prev_store != var2store_.end() &&
instructions_to_save.count(prev_store->second) == 0) {
instructions_to_save.count(prev_store->second) == 0 &&
!context()->get_debug_info_mgr()->IsDebugDeclared(varId)) {
instructions_to_kill.push_back(prev_store->second);
modified = true;
}

View File

@@ -133,7 +133,27 @@ bool LocalSingleStoreElimPass::ProcessVariable(Instruction* var_inst) {
return false;
}
return RewriteLoads(store_inst, users);
bool all_rewritten;
bool modified = RewriteLoads(store_inst, users, &all_rewritten);
// If all uses are rewritten and the variable has a DebugDeclare and the
// variable is not an aggregate, add a DebugValue after the store and remove
// the DebugDeclare.
uint32_t var_id = var_inst->result_id();
if (all_rewritten &&
context()->get_debug_info_mgr()->IsDebugDeclared(var_id)) {
const analysis::Type* var_type =
context()->get_type_mgr()->GetType(var_inst->type_id());
const analysis::Type* store_type = var_type->AsPointer()->pointee_type();
if (!(store_type->AsStruct() || store_type->AsArray())) {
context()->get_debug_info_mgr()->AddDebugValue(
store_inst, var_id, store_inst->GetSingleWordInOperand(1),
store_inst);
context()->get_debug_info_mgr()->KillDebugDeclares(var_id);
}
}
return modified;
}
Instruction* LocalSingleStoreElimPass::FindSingleStoreAndCheckUses(
@@ -172,6 +192,14 @@ Instruction* LocalSingleStoreElimPass::FindSingleStoreAndCheckUses(
case SpvOpName:
case SpvOpCopyObject:
break;
case SpvOpExtInst: {
auto dbg_op = user->GetOpenCL100DebugOpcode();
if (dbg_op == OpenCLDebugInfo100DebugDeclare ||
dbg_op == OpenCLDebugInfo100DebugValue) {
break;
}
return nullptr;
}
default:
if (!user->IsDecoration()) {
// Don't know if this instruction modifies the variable.
@@ -218,7 +246,8 @@ bool LocalSingleStoreElimPass::FeedsAStore(Instruction* inst) const {
}
bool LocalSingleStoreElimPass::RewriteLoads(
Instruction* store_inst, const std::vector<Instruction*>& uses) {
Instruction* store_inst, const std::vector<Instruction*>& uses,
bool* all_rewritten) {
BasicBlock* store_block = context()->get_instr_block(store_inst);
DominatorAnalysis* dominator_analysis =
context()->GetDominatorAnalysis(store_block->GetParent());
@@ -229,16 +258,22 @@ bool LocalSingleStoreElimPass::RewriteLoads(
else
stored_id = store_inst->GetSingleWordInOperand(kVariableInitIdInIdx);
std::vector<Instruction*> uses_in_store_block;
*all_rewritten = true;
bool modified = false;
for (Instruction* use : uses) {
if (use->opcode() == SpvOpLoad) {
if (dominator_analysis->Dominates(store_inst, use)) {
modified = true;
context()->KillNamesAndDecorates(use->result_id());
context()->ReplaceAllUsesWith(use->result_id(), stored_id);
context()->KillInst(use);
}
if (use->opcode() == SpvOpStore) continue;
auto dbg_op = use->GetOpenCL100DebugOpcode();
if (dbg_op == OpenCLDebugInfo100DebugDeclare ||
dbg_op == OpenCLDebugInfo100DebugValue)
continue;
if (use->opcode() == SpvOpLoad &&
dominator_analysis->Dominates(store_inst, use)) {
modified = true;
context()->KillNamesAndDecorates(use->result_id());
context()->ReplaceAllUsesWith(use->result_id(), stored_id);
context()->KillInst(use);
} else {
*all_rewritten = false;
}
}

View File

@@ -89,9 +89,10 @@ class LocalSingleStoreElimPass : public Pass {
bool FeedsAStore(Instruction* inst) const;
// Replaces all of the loads in |uses| by the value stored in |store_inst|.
// The load instructions are then killed.
// The load instructions are then killed. |all_rewritten| is true iff all
// uses have been rewritten.
bool RewriteLoads(Instruction* store_inst,
const std::vector<Instruction*>& uses);
const std::vector<Instruction*>& uses, bool* all_rewritten);
// Extensions supported by this pass.
std::unordered_set<std::string> extensions_allowlist_;

View File

@@ -71,10 +71,6 @@ class Pass {
return context()->get_def_use_mgr();
}
analysis::DebugInfoManager* get_debug_info_mgr() const {
return context()->get_debug_info_mgr();
}
analysis::DecorationManager* get_decoration_mgr() const {
return context()->get_decoration_mgr();
}

View File

@@ -112,10 +112,10 @@ bool ReduceLoadSize::ReplaceExtract(Instruction* inst) {
Instruction* new_access_chain = ir_builder.AddAccessChain(
pointer_to_result_type_id,
composite_inst->GetSingleWordInOperand(kLoadPointerInIdx), ids);
Instruction* new_laod =
Instruction* new_load =
ir_builder.AddLoad(inst->type_id(), new_access_chain->result_id());
context()->ReplaceAllUsesWith(inst->result_id(), new_laod->result_id());
context()->ReplaceAllUsesWith(inst->result_id(), new_load->result_id());
context()->KillInst(inst);
return true;
}
@@ -139,6 +139,7 @@ bool ReduceLoadSize::ShouldReplaceExtract(Instruction* inst) {
all_elements_used =
!def_use_mgr->WhileEachUser(op_inst, [&elements_used](Instruction* use) {
if (use->IsOpenCL100DebugInstr()) return true;
if (use->opcode() != SpvOpCompositeExtract ||
use->NumInOperands() == 1) {
return false;

View File

@@ -307,7 +307,8 @@ void SSARewriter::ProcessStore(Instruction* inst, BasicBlock* bb) {
}
if (pass_->IsTargetVar(var_id)) {
WriteVariable(var_id, bb, val_id);
pass_->get_debug_info_mgr()->AddDebugValue(inst, var_id, val_id, inst);
pass_->context()->get_debug_info_mgr()->AddDebugValue(inst, var_id, val_id,
inst);
#if SSA_REWRITE_DEBUGGING_LEVEL > 1
std::cerr << "\tFound store '%" << var_id << " = %" << val_id << "': "
@@ -490,7 +491,7 @@ bool SSARewriter::ApplyReplacements() {
// Add DebugValue for the new OpPhi instruction.
insert_it->SetDebugScope(local_var->GetDebugScope());
pass_->get_debug_info_mgr()->AddDebugValue(
pass_->context()->get_debug_info_mgr()->AddDebugValue(
&*insert_it, phi_candidate->var_id(), phi_candidate->result_id(),
&*insert_it);

View File

@@ -52,6 +52,9 @@ void VectorDCE::FindLiveComponents(Function* function,
// components are live because of arbitrary nesting of structs.
function->ForEachInst(
[&work_list, this, live_components](Instruction* current_inst) {
if (current_inst->IsOpenCL100DebugInstr()) {
return;
}
if (!HasVectorOrScalarResult(current_inst) ||
!context()->IsCombinatorInstruction(current_inst)) {
MarkUsesAsLive(current_inst, all_components_live_, live_components,
@@ -297,51 +300,60 @@ bool VectorDCE::HasScalarResult(const Instruction* inst) const {
bool VectorDCE::RewriteInstructions(
Function* function, const VectorDCE::LiveComponentMap& live_components) {
bool modified = false;
function->ForEachInst(
[&modified, this, live_components](Instruction* current_inst) {
if (!context()->IsCombinatorInstruction(current_inst)) {
return;
}
auto live_component = live_components.find(current_inst->result_id());
if (live_component == live_components.end()) {
// If this instruction is not in live_components then it does not
// produce a vector, or it is never referenced and ADCE will remove
// it. No point in trying to differentiate.
return;
}
// Kill DebugValue in the middle of the instruction iteration will result
// in accessing a dangling pointer. We keep dead DebugValue instructions
// in |dead_dbg_value| to kill them once after the iteration.
std::vector<Instruction*> dead_dbg_value;
// If no element in the current instruction is used replace it with an
// OpUndef.
if (live_component->second.Empty()) {
modified = true;
uint32_t undef_id = this->Type2Undef(current_inst->type_id());
context()->KillNamesAndDecorates(current_inst);
context()->ReplaceAllUsesWith(current_inst->result_id(), undef_id);
context()->KillInst(current_inst);
return;
}
function->ForEachInst([&modified, this, live_components,
&dead_dbg_value](Instruction* current_inst) {
if (!context()->IsCombinatorInstruction(current_inst)) {
return;
}
switch (current_inst->opcode()) {
case SpvOpCompositeInsert:
modified |=
RewriteInsertInstruction(current_inst, live_component->second);
break;
case SpvOpCompositeConstruct:
// TODO: The members that are not live can be replaced by an undef
// or constant. This will remove uses of those values, and possibly
// create opportunities for ADCE.
break;
default:
// Do nothing.
break;
}
});
auto live_component = live_components.find(current_inst->result_id());
if (live_component == live_components.end()) {
// If this instruction is not in live_components then it does not
// produce a vector, or it is never referenced and ADCE will remove
// it. No point in trying to differentiate.
return;
}
// If no element in the current instruction is used replace it with an
// OpUndef.
if (live_component->second.Empty()) {
modified = true;
MarkDebugValueUsesAsDead(current_inst, &dead_dbg_value);
uint32_t undef_id = this->Type2Undef(current_inst->type_id());
context()->KillNamesAndDecorates(current_inst);
context()->ReplaceAllUsesWith(current_inst->result_id(), undef_id);
context()->KillInst(current_inst);
return;
}
switch (current_inst->opcode()) {
case SpvOpCompositeInsert:
modified |= RewriteInsertInstruction(
current_inst, live_component->second, &dead_dbg_value);
break;
case SpvOpCompositeConstruct:
// TODO: The members that are not live can be replaced by an undef
// or constant. This will remove uses of those values, and possibly
// create opportunities for ADCE.
break;
default:
// Do nothing.
break;
}
});
for (auto* i : dead_dbg_value) context()->KillInst(i);
return modified;
}
bool VectorDCE::RewriteInsertInstruction(
Instruction* current_inst, const utils::BitVector& live_components) {
Instruction* current_inst, const utils::BitVector& live_components,
std::vector<Instruction*>* dead_dbg_value) {
// If the value being inserted is not live, then we can skip the insert.
if (current_inst->NumInOperands() == 2) {
@@ -355,6 +367,7 @@ bool VectorDCE::RewriteInsertInstruction(
uint32_t insert_index = current_inst->GetSingleWordInOperand(2);
if (!live_components.Get(insert_index)) {
MarkDebugValueUsesAsDead(current_inst, dead_dbg_value);
context()->KillNamesAndDecorates(current_inst->result_id());
uint32_t composite_id =
current_inst->GetSingleWordInOperand(kInsertCompositeIdInIdx);
@@ -377,6 +390,15 @@ bool VectorDCE::RewriteInsertInstruction(
return false;
}
void VectorDCE::MarkDebugValueUsesAsDead(
Instruction* composite, std::vector<Instruction*>* dead_dbg_value) {
context()->get_def_use_mgr()->ForEachUser(
composite, [&dead_dbg_value](Instruction* use) {
if (use->GetOpenCL100DebugOpcode() == OpenCLDebugInfo100DebugValue)
dead_dbg_value->push_back(use);
});
}
void VectorDCE::AddItemToWorkListIfNeeded(
WorkListItem work_item, VectorDCE::LiveComponentMap* live_components,
std::vector<WorkListItem>* work_list) {

View File

@@ -73,6 +73,11 @@ class VectorDCE : public MemPass {
bool RewriteInstructions(Function* function,
const LiveComponentMap& live_components);
// Makrs all DebugValue instructions that use |composite| for their values as
// dead instructions by putting them into |dead_dbg_value|.
void MarkDebugValueUsesAsDead(Instruction* composite,
std::vector<Instruction*>* dead_dbg_value);
// Rewrites the OpCompositeInsert instruction |current_inst| to avoid
// unnecessary computes given that the only components of the result that are
// live are |live_components|.
@@ -83,7 +88,8 @@ class VectorDCE : public MemPass {
// If the composite input to |current_inst| is not live, then it is replaced
// by and OpUndef in |current_inst|.
bool RewriteInsertInstruction(Instruction* current_inst,
const utils::BitVector& live_components);
const utils::BitVector& live_components,
std::vector<Instruction*>* dead_dbg_value);
// Returns true if the result of |inst| is a vector or a scalar.
bool HasVectorOrScalarResult(const Instruction* inst) const;

View File

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