mirror of
https://github.com/bkaradzic/bgfx.git
synced 2026-02-17 20:52:36 +01:00
Updated spirv-tools.
This commit is contained in:
@@ -1 +1 @@
|
||||
"v2020.4-dev", "SPIRV-Tools v2020.4-dev 38bba44706260cfb807097c58ca926c64c3a13d2"
|
||||
"v2020.4-dev", "SPIRV-Tools v2020.4-dev 8d5c7eae3a89ee8b898c9771119abb8a3f977898"
|
||||
|
||||
@@ -110,6 +110,16 @@ static const int kInstRayTracingOutLaunchIdX = kInstCommonOutCnt;
|
||||
static const int kInstRayTracingOutLaunchIdY = kInstCommonOutCnt + 1;
|
||||
static const int kInstRayTracingOutLaunchIdZ = kInstCommonOutCnt + 2;
|
||||
|
||||
// Mesh Shader Output Record Offsets
|
||||
static const int kInstMeshOutGlobalInvocationIdX = kInstCommonOutCnt;
|
||||
static const int kInstMeshOutGlobalInvocationIdY = kInstCommonOutCnt + 1;
|
||||
static const int kInstMeshOutGlobalInvocationIdZ = kInstCommonOutCnt + 2;
|
||||
|
||||
// Task Shader Output Record Offsets
|
||||
static const int kInstTaskOutGlobalInvocationIdX = kInstCommonOutCnt;
|
||||
static const int kInstTaskOutGlobalInvocationIdY = kInstCommonOutCnt + 1;
|
||||
static const int kInstTaskOutGlobalInvocationIdZ = kInstCommonOutCnt + 2;
|
||||
|
||||
// Size of Common and Stage-specific Members
|
||||
static const int kInstStageOutCnt = kInstCommonOutCnt + 3;
|
||||
|
||||
|
||||
@@ -677,6 +677,12 @@ SPIRV_TOOLS_EXPORT void spvFuzzerOptionsEnableReplayValidation(
|
||||
SPIRV_TOOLS_EXPORT void spvFuzzerOptionsSetRandomSeed(
|
||||
spv_fuzzer_options options, uint32_t seed);
|
||||
|
||||
// Sets the range of transformations that should be applied during replay: 0
|
||||
// means all transformations, +N means the first N transformations, -N means all
|
||||
// except the final N transformations.
|
||||
SPIRV_TOOLS_EXPORT void spvFuzzerOptionsSetReplayRange(
|
||||
spv_fuzzer_options options, int32_t replay_range);
|
||||
|
||||
// Sets the maximum number of steps that the shrinker should take before giving
|
||||
// up.
|
||||
SPIRV_TOOLS_EXPORT void spvFuzzerOptionsSetShrinkerStepLimit(
|
||||
|
||||
@@ -227,6 +227,11 @@ class FuzzerOptions {
|
||||
spvFuzzerOptionsSetRandomSeed(options_, seed);
|
||||
}
|
||||
|
||||
// See spvFuzzerOptionsSetReplayRange.
|
||||
void set_replay_range(int32_t replay_range) {
|
||||
spvFuzzerOptionsSetReplayRange(options_, replay_range);
|
||||
}
|
||||
|
||||
// See spvFuzzerOptionsSetShrinkerStepLimit.
|
||||
void set_shrinker_step_limit(uint32_t shrinker_step_limit) {
|
||||
spvFuzzerOptionsSetShrinkerStepLimit(options_, shrinker_step_limit);
|
||||
|
||||
@@ -47,6 +47,7 @@ if(SPIRV_BUILD_FUZZER)
|
||||
fuzzer_pass_add_function_calls.h
|
||||
fuzzer_pass_add_global_variables.h
|
||||
fuzzer_pass_add_image_sample_unused_components.h
|
||||
fuzzer_pass_add_synonyms.h
|
||||
fuzzer_pass_add_loads.h
|
||||
fuzzer_pass_add_local_variables.h
|
||||
fuzzer_pass_add_no_contraction_decorations.h
|
||||
@@ -63,6 +64,7 @@ if(SPIRV_BUILD_FUZZER)
|
||||
fuzzer_pass_copy_objects.h
|
||||
fuzzer_pass_donate_modules.h
|
||||
fuzzer_pass_invert_comparison_operators.h
|
||||
fuzzer_pass_interchange_zero_like_constants.h
|
||||
fuzzer_pass_merge_blocks.h
|
||||
fuzzer_pass_obfuscate_constants.h
|
||||
fuzzer_pass_outline_functions.h
|
||||
@@ -103,6 +105,7 @@ if(SPIRV_BUILD_FUZZER)
|
||||
transformation_add_no_contraction_decoration.h
|
||||
transformation_add_parameter.h
|
||||
transformation_add_spec_constant_op.h
|
||||
transformation_add_synonym.h
|
||||
transformation_add_type_array.h
|
||||
transformation_add_type_boolean.h
|
||||
transformation_add_type_float.h
|
||||
@@ -117,7 +120,6 @@ if(SPIRV_BUILD_FUZZER)
|
||||
transformation_composite_extract.h
|
||||
transformation_compute_data_synonym_fact_closure.h
|
||||
transformation_context.h
|
||||
transformation_copy_object.h
|
||||
transformation_equation_instruction.h
|
||||
transformation_function_call.h
|
||||
transformation_invert_comparison_operator.h
|
||||
@@ -164,6 +166,7 @@ if(SPIRV_BUILD_FUZZER)
|
||||
fuzzer_pass_add_function_calls.cpp
|
||||
fuzzer_pass_add_global_variables.cpp
|
||||
fuzzer_pass_add_image_sample_unused_components.cpp
|
||||
fuzzer_pass_add_synonyms.cpp
|
||||
fuzzer_pass_add_loads.cpp
|
||||
fuzzer_pass_add_local_variables.cpp
|
||||
fuzzer_pass_add_no_contraction_decorations.cpp
|
||||
@@ -180,6 +183,7 @@ if(SPIRV_BUILD_FUZZER)
|
||||
fuzzer_pass_copy_objects.cpp
|
||||
fuzzer_pass_donate_modules.cpp
|
||||
fuzzer_pass_invert_comparison_operators.cpp
|
||||
fuzzer_pass_interchange_zero_like_constants.cpp
|
||||
fuzzer_pass_merge_blocks.cpp
|
||||
fuzzer_pass_obfuscate_constants.cpp
|
||||
fuzzer_pass_outline_functions.cpp
|
||||
@@ -219,6 +223,7 @@ if(SPIRV_BUILD_FUZZER)
|
||||
transformation_add_no_contraction_decoration.cpp
|
||||
transformation_add_parameter.cpp
|
||||
transformation_add_spec_constant_op.cpp
|
||||
transformation_add_synonym.cpp
|
||||
transformation_add_type_array.cpp
|
||||
transformation_add_type_boolean.cpp
|
||||
transformation_add_type_float.cpp
|
||||
@@ -233,7 +238,6 @@ if(SPIRV_BUILD_FUZZER)
|
||||
transformation_composite_extract.cpp
|
||||
transformation_compute_data_synonym_fact_closure.cpp
|
||||
transformation_context.cpp
|
||||
transformation_copy_object.cpp
|
||||
transformation_equation_instruction.cpp
|
||||
transformation_function_call.cpp
|
||||
transformation_invert_comparison_operator.cpp
|
||||
|
||||
@@ -457,6 +457,18 @@ class FactManager::DataSynonymAndIdEquationFacts {
|
||||
const protobufs::DataDescriptor& dd2,
|
||||
opt::IRContext* context);
|
||||
|
||||
// Computes various corollary facts from the data descriptor |dd| if members
|
||||
// of its equivalence class participate in equation facts with OpConvert*
|
||||
// opcodes. The descriptor should be registered in the equivalence relation.
|
||||
void ComputeConversionDataSynonymFacts(const protobufs::DataDescriptor& dd,
|
||||
opt::IRContext* context);
|
||||
|
||||
// Recurses into sub-components of the data descriptors, if they are
|
||||
// composites, to record that their components are pairwise-synonymous.
|
||||
void ComputeCompositeDataSynonymFacts(const protobufs::DataDescriptor& dd1,
|
||||
const protobufs::DataDescriptor& dd2,
|
||||
opt::IRContext* context);
|
||||
|
||||
// Records the fact that |dd1| and |dd2| are equivalent, and merges the sets
|
||||
// of equations that are known about them.
|
||||
void MakeEquivalent(const protobufs::DataDescriptor& dd1,
|
||||
@@ -588,9 +600,13 @@ void FactManager::DataSynonymAndIdEquationFacts::AddEquationFactRecursive(
|
||||
// Now try to work out corollaries implied by the new equation and existing
|
||||
// facts.
|
||||
switch (opcode) {
|
||||
case SpvOpConvertSToF:
|
||||
case SpvOpConvertUToF:
|
||||
ComputeConversionDataSynonymFacts(*rhs_dds[0], context);
|
||||
break;
|
||||
case SpvOpIAdd: {
|
||||
// Equation form: "a = b + c"
|
||||
for (auto equation : GetEquations(rhs_dds[0])) {
|
||||
for (const auto& equation : GetEquations(rhs_dds[0])) {
|
||||
if (equation.opcode == SpvOpISub) {
|
||||
// Equation form: "a = (d - e) + c"
|
||||
if (synonymous_.IsEquivalent(*equation.operands[1], *rhs_dds[1])) {
|
||||
@@ -606,7 +622,7 @@ void FactManager::DataSynonymAndIdEquationFacts::AddEquationFactRecursive(
|
||||
}
|
||||
}
|
||||
}
|
||||
for (auto equation : GetEquations(rhs_dds[1])) {
|
||||
for (const auto& equation : GetEquations(rhs_dds[1])) {
|
||||
if (equation.opcode == SpvOpISub) {
|
||||
// Equation form: "a = b + (d - e)"
|
||||
if (synonymous_.IsEquivalent(*equation.operands[1], *rhs_dds[0])) {
|
||||
@@ -620,7 +636,7 @@ void FactManager::DataSynonymAndIdEquationFacts::AddEquationFactRecursive(
|
||||
}
|
||||
case SpvOpISub: {
|
||||
// Equation form: "a = b - c"
|
||||
for (auto equation : GetEquations(rhs_dds[0])) {
|
||||
for (const auto& equation : GetEquations(rhs_dds[0])) {
|
||||
if (equation.opcode == SpvOpIAdd) {
|
||||
// Equation form: "a = (d + e) - c"
|
||||
if (synonymous_.IsEquivalent(*equation.operands[0], *rhs_dds[1])) {
|
||||
@@ -646,7 +662,7 @@ void FactManager::DataSynonymAndIdEquationFacts::AddEquationFactRecursive(
|
||||
}
|
||||
}
|
||||
|
||||
for (auto equation : GetEquations(rhs_dds[1])) {
|
||||
for (const auto& equation : GetEquations(rhs_dds[1])) {
|
||||
if (equation.opcode == SpvOpIAdd) {
|
||||
// Equation form: "a = b - (d + e)"
|
||||
if (synonymous_.IsEquivalent(*equation.operands[0], *rhs_dds[0])) {
|
||||
@@ -676,7 +692,7 @@ void FactManager::DataSynonymAndIdEquationFacts::AddEquationFactRecursive(
|
||||
case SpvOpLogicalNot:
|
||||
case SpvOpSNegate: {
|
||||
// Equation form: "a = !b" or "a = -b"
|
||||
for (auto equation : GetEquations(rhs_dds[0])) {
|
||||
for (const auto& equation : GetEquations(rhs_dds[0])) {
|
||||
if (equation.opcode == opcode) {
|
||||
// Equation form: "a = !!b" or "a = -(-b)"
|
||||
// We can thus infer "a = b"
|
||||
@@ -698,7 +714,69 @@ void FactManager::DataSynonymAndIdEquationFacts::AddDataSynonymFactRecursive(
|
||||
// Record that the data descriptors provided in the fact are equivalent.
|
||||
MakeEquivalent(dd1, dd2);
|
||||
|
||||
// We now check whether this is a synonym about composite objects. If it is,
|
||||
// Compute various corollary facts.
|
||||
ComputeConversionDataSynonymFacts(dd1, context);
|
||||
ComputeCompositeDataSynonymFacts(dd1, dd2, context);
|
||||
}
|
||||
|
||||
void FactManager::DataSynonymAndIdEquationFacts::
|
||||
ComputeConversionDataSynonymFacts(const protobufs::DataDescriptor& dd,
|
||||
opt::IRContext* context) {
|
||||
assert(synonymous_.Exists(dd) &&
|
||||
"|dd| should've been registered in the equivalence relation");
|
||||
|
||||
const auto* representative = synonymous_.Find(&dd);
|
||||
assert(representative &&
|
||||
"Representative can't be null for a registered descriptor");
|
||||
|
||||
const auto* type =
|
||||
context->get_type_mgr()->GetType(fuzzerutil::WalkCompositeTypeIndices(
|
||||
context, fuzzerutil::GetTypeId(context, representative->object()),
|
||||
representative->index()));
|
||||
assert(type && "Data descriptor has invalid type");
|
||||
|
||||
if ((type->AsVector() && type->AsVector()->element_type()->AsInteger()) ||
|
||||
type->AsInteger()) {
|
||||
// If there exist equation facts of the form |%a = opcode %representative|
|
||||
// and |%b = opcode %representative| where |opcode| is either OpConvertSToF
|
||||
// or OpConvertUToF, then |a| and |b| are synonymous.
|
||||
std::vector<const protobufs::DataDescriptor*> convert_s_to_f_lhs;
|
||||
std::vector<const protobufs::DataDescriptor*> convert_u_to_f_lhs;
|
||||
|
||||
for (const auto& fact : id_equations_) {
|
||||
for (const auto& equation : fact.second) {
|
||||
if (synonymous_.IsEquivalent(*equation.operands[0], *representative)) {
|
||||
if (equation.opcode == SpvOpConvertSToF) {
|
||||
convert_s_to_f_lhs.push_back(fact.first);
|
||||
} else if (equation.opcode == SpvOpConvertUToF) {
|
||||
convert_u_to_f_lhs.push_back(fact.first);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (const auto& synonyms :
|
||||
{std::move(convert_s_to_f_lhs), std::move(convert_u_to_f_lhs)}) {
|
||||
for (const auto* synonym_a : synonyms) {
|
||||
for (const auto* synonym_b : synonyms) {
|
||||
if (!synonymous_.IsEquivalent(*synonym_a, *synonym_b) &&
|
||||
DataDescriptorsAreWellFormedAndComparable(context, *synonym_a,
|
||||
*synonym_b)) {
|
||||
// |synonym_a| and |synonym_b| have compatible types - they are
|
||||
// synonymous.
|
||||
AddDataSynonymFactRecursive(*synonym_a, *synonym_b, context);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void FactManager::DataSynonymAndIdEquationFacts::
|
||||
ComputeCompositeDataSynonymFacts(const protobufs::DataDescriptor& dd1,
|
||||
const protobufs::DataDescriptor& dd2,
|
||||
opt::IRContext* context) {
|
||||
// Check whether this is a synonym about composite objects. If it is,
|
||||
// we can recursively add synonym facts about their associated sub-components.
|
||||
|
||||
// Get the type of the object referred to by the first data descriptor in the
|
||||
|
||||
10
3rdparty/spirv-tools/source/fuzz/fuzzer.cpp
vendored
10
3rdparty/spirv-tools/source/fuzz/fuzzer.cpp
vendored
@@ -18,7 +18,6 @@
|
||||
#include <memory>
|
||||
#include <sstream>
|
||||
|
||||
#include "fuzzer_pass_adjust_memory_operands_masks.h"
|
||||
#include "source/fuzz/fact_manager.h"
|
||||
#include "source/fuzz/fuzzer_context.h"
|
||||
#include "source/fuzz/fuzzer_pass_add_access_chains.h"
|
||||
@@ -36,15 +35,18 @@
|
||||
#include "source/fuzz/fuzzer_pass_add_no_contraction_decorations.h"
|
||||
#include "source/fuzz/fuzzer_pass_add_parameters.h"
|
||||
#include "source/fuzz/fuzzer_pass_add_stores.h"
|
||||
#include "source/fuzz/fuzzer_pass_add_synonyms.h"
|
||||
#include "source/fuzz/fuzzer_pass_add_vector_shuffle_instructions.h"
|
||||
#include "source/fuzz/fuzzer_pass_adjust_branch_weights.h"
|
||||
#include "source/fuzz/fuzzer_pass_adjust_function_controls.h"
|
||||
#include "source/fuzz/fuzzer_pass_adjust_loop_controls.h"
|
||||
#include "source/fuzz/fuzzer_pass_adjust_memory_operands_masks.h"
|
||||
#include "source/fuzz/fuzzer_pass_adjust_selection_controls.h"
|
||||
#include "source/fuzz/fuzzer_pass_apply_id_synonyms.h"
|
||||
#include "source/fuzz/fuzzer_pass_construct_composites.h"
|
||||
#include "source/fuzz/fuzzer_pass_copy_objects.h"
|
||||
#include "source/fuzz/fuzzer_pass_donate_modules.h"
|
||||
#include "source/fuzz/fuzzer_pass_interchange_zero_like_constants.h"
|
||||
#include "source/fuzz/fuzzer_pass_invert_comparison_operators.h"
|
||||
#include "source/fuzz/fuzzer_pass_merge_blocks.h"
|
||||
#include "source/fuzz/fuzzer_pass_obfuscate_constants.h"
|
||||
@@ -241,6 +243,9 @@ Fuzzer::FuzzerResultStatus Fuzzer::Run(
|
||||
MaybeAddPass<FuzzerPassAddStores>(&passes, ir_context.get(),
|
||||
&transformation_context, &fuzzer_context,
|
||||
transformation_sequence_out);
|
||||
MaybeAddPass<FuzzerPassAddSynonyms>(
|
||||
&passes, ir_context.get(), &transformation_context, &fuzzer_context,
|
||||
transformation_sequence_out);
|
||||
MaybeAddPass<FuzzerPassAddVectorShuffleInstructions>(
|
||||
&passes, ir_context.get(), &transformation_context, &fuzzer_context,
|
||||
transformation_sequence_out);
|
||||
@@ -326,6 +331,9 @@ Fuzzer::FuzzerResultStatus Fuzzer::Run(
|
||||
MaybeAddPass<FuzzerPassAddNoContractionDecorations>(
|
||||
&final_passes, ir_context.get(), &transformation_context, &fuzzer_context,
|
||||
transformation_sequence_out);
|
||||
MaybeAddPass<FuzzerPassInterchangeZeroLikeConstants>(
|
||||
&final_passes, ir_context.get(), &transformation_context, &fuzzer_context,
|
||||
transformation_sequence_out);
|
||||
MaybeAddPass<FuzzerPassPermutePhiOperands>(
|
||||
&final_passes, ir_context.get(), &transformation_context, &fuzzer_context,
|
||||
transformation_sequence_out);
|
||||
|
||||
@@ -43,6 +43,7 @@ const std::pair<uint32_t, uint32_t> kChanceOfAddingNoContractionDecoration = {
|
||||
5, 70};
|
||||
const std::pair<uint32_t, uint32_t> kChanceOfAddingParameters = {5, 70};
|
||||
const std::pair<uint32_t, uint32_t> kChanceOfAddingStore = {5, 50};
|
||||
const std::pair<uint32_t, uint32_t> kChanceOfAddingSynonyms = {20, 50};
|
||||
const std::pair<uint32_t, uint32_t> kChanceOfAddingVectorType = {20, 70};
|
||||
const std::pair<uint32_t, uint32_t> kChanceOfAddingVectorShuffle = {20, 70};
|
||||
const std::pair<uint32_t, uint32_t> kChanceOfAdjustingBranchWeights = {20, 90};
|
||||
@@ -63,6 +64,8 @@ const std::pair<uint32_t, uint32_t> kChanceOfCopyingObject = {20, 50};
|
||||
const std::pair<uint32_t, uint32_t> kChanceOfDonatingAdditionalModule = {5, 50};
|
||||
const std::pair<uint32_t, uint32_t> kChanceOfGoingDeeperWhenMakingAccessChain =
|
||||
{50, 95};
|
||||
const std::pair<uint32_t, uint32_t> kChanceOfInterchangingZeroLikeConstants = {
|
||||
10, 90};
|
||||
const std::pair<uint32_t, uint32_t> kChanceOfInvertingComparisonOperators = {
|
||||
20, 50};
|
||||
const std::pair<uint32_t, uint32_t> kChanceOfMakingDonorLivesafe = {40, 60};
|
||||
@@ -160,6 +163,7 @@ FuzzerContext::FuzzerContext(RandomGenerator* random_generator,
|
||||
ChooseBetweenMinAndMax(kChanceOfAdjustingBranchWeights);
|
||||
chance_of_adjusting_function_control_ =
|
||||
ChooseBetweenMinAndMax(kChanceOfAdjustingFunctionControl);
|
||||
chance_of_adding_synonyms_ = ChooseBetweenMinAndMax(kChanceOfAddingSynonyms);
|
||||
chance_of_adjusting_loop_control_ =
|
||||
ChooseBetweenMinAndMax(kChanceOfAdjustingLoopControl);
|
||||
chance_of_adjusting_memory_operands_mask_ =
|
||||
@@ -179,6 +183,8 @@ FuzzerContext::FuzzerContext(RandomGenerator* random_generator,
|
||||
ChooseBetweenMinAndMax(kChanceOfDonatingAdditionalModule);
|
||||
chance_of_going_deeper_when_making_access_chain_ =
|
||||
ChooseBetweenMinAndMax(kChanceOfGoingDeeperWhenMakingAccessChain);
|
||||
chance_of_interchanging_zero_like_constants_ =
|
||||
ChooseBetweenMinAndMax(kChanceOfInterchangingZeroLikeConstants);
|
||||
chance_of_inverting_comparison_operators_ =
|
||||
ChooseBetweenMinAndMax(kChanceOfInvertingComparisonOperators);
|
||||
chance_of_making_donor_livesafe_ =
|
||||
@@ -237,5 +243,21 @@ uint32_t FuzzerContext::ChooseBetweenMinAndMax(
|
||||
random_generator_->RandomUint32(min_max.second - min_max.first + 1);
|
||||
}
|
||||
|
||||
protobufs::TransformationAddSynonym::SynonymType
|
||||
FuzzerContext::GetRandomSynonymType() {
|
||||
// value_count method is guaranteed to return a value greater than 0.
|
||||
auto result_index = ChooseBetweenMinAndMax(
|
||||
{0, static_cast<uint32_t>(
|
||||
protobufs::TransformationAddSynonym::SynonymType_descriptor()
|
||||
->value_count() -
|
||||
1)});
|
||||
auto result = protobufs::TransformationAddSynonym::SynonymType_descriptor()
|
||||
->value(result_index)
|
||||
->number();
|
||||
assert(protobufs::TransformationAddSynonym::SynonymType_IsValid(result) &&
|
||||
"|result| is not a value of SynonymType");
|
||||
return static_cast<protobufs::TransformationAddSynonym::SynonymType>(result);
|
||||
}
|
||||
|
||||
} // namespace fuzz
|
||||
} // namespace spvtools
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
#include <functional>
|
||||
#include <utility>
|
||||
|
||||
#include "source/fuzz/protobufs/spirvfuzz_protobufs.h"
|
||||
#include "source/fuzz/random_generator.h"
|
||||
#include "source/opt/function.h"
|
||||
|
||||
@@ -143,6 +144,7 @@ class FuzzerContext {
|
||||
}
|
||||
uint32_t GetChanceOfAddingParameters() { return chance_of_adding_parameters; }
|
||||
uint32_t GetChanceOfAddingStore() { return chance_of_adding_store_; }
|
||||
uint32_t GetChanceOfAddingSynonyms() { return chance_of_adding_synonyms_; }
|
||||
uint32_t GetChanceOfAddingVectorShuffle() {
|
||||
return chance_of_adding_vector_shuffle_;
|
||||
}
|
||||
@@ -181,6 +183,9 @@ class FuzzerContext {
|
||||
uint32_t GetChanceOfGoingDeeperWhenMakingAccessChain() {
|
||||
return chance_of_going_deeper_when_making_access_chain_;
|
||||
}
|
||||
uint32_t GetChanceOfInterchangingZeroLikeConstants() {
|
||||
return chance_of_interchanging_zero_like_constants_;
|
||||
}
|
||||
uint32_t GetChanceOfInvertingComparisonOperators() {
|
||||
return chance_of_inverting_comparison_operators_;
|
||||
}
|
||||
@@ -274,6 +279,7 @@ class FuzzerContext {
|
||||
// Ensure that the array size is non-zero.
|
||||
return random_generator_->RandomUint32(max_new_array_size_limit_ - 1) + 1;
|
||||
}
|
||||
protobufs::TransformationAddSynonym::SynonymType GetRandomSynonymType();
|
||||
uint32_t GetRandomUnusedComponentCountForImageSample(
|
||||
uint32_t max_unused_component_count) {
|
||||
// Ensure that the number of unused components is non-zero.
|
||||
@@ -307,6 +313,7 @@ class FuzzerContext {
|
||||
uint32_t chance_of_adding_no_contraction_decoration_;
|
||||
uint32_t chance_of_adding_parameters;
|
||||
uint32_t chance_of_adding_store_;
|
||||
uint32_t chance_of_adding_synonyms_;
|
||||
uint32_t chance_of_adding_vector_shuffle_;
|
||||
uint32_t chance_of_adding_vector_type_;
|
||||
uint32_t chance_of_adjusting_branch_weights_;
|
||||
@@ -321,6 +328,7 @@ class FuzzerContext {
|
||||
uint32_t chance_of_copying_object_;
|
||||
uint32_t chance_of_donating_additional_module_;
|
||||
uint32_t chance_of_going_deeper_when_making_access_chain_;
|
||||
uint32_t chance_of_interchanging_zero_like_constants_;
|
||||
uint32_t chance_of_inverting_comparison_operators_;
|
||||
uint32_t chance_of_making_donor_livesafe_;
|
||||
uint32_t chance_of_merging_blocks_;
|
||||
|
||||
59
3rdparty/spirv-tools/source/fuzz/fuzzer_pass.cpp
vendored
59
3rdparty/spirv-tools/source/fuzz/fuzzer_pass.cpp
vendored
@@ -20,6 +20,7 @@
|
||||
#include "source/fuzz/instruction_descriptor.h"
|
||||
#include "source/fuzz/transformation_add_constant_boolean.h"
|
||||
#include "source/fuzz/transformation_add_constant_composite.h"
|
||||
#include "source/fuzz/transformation_add_constant_null.h"
|
||||
#include "source/fuzz/transformation_add_constant_scalar.h"
|
||||
#include "source/fuzz/transformation_add_global_undef.h"
|
||||
#include "source/fuzz/transformation_add_type_boolean.h"
|
||||
@@ -163,8 +164,9 @@ uint32_t FuzzerPass::FindOrCreateBoolType() {
|
||||
}
|
||||
|
||||
uint32_t FuzzerPass::FindOrCreateIntegerType(uint32_t width, bool is_signed) {
|
||||
if (auto existing_id =
|
||||
fuzzerutil::MaybeGetIntegerType(GetIRContext(), width, is_signed)) {
|
||||
opt::analysis::Integer int_type(width, is_signed);
|
||||
auto existing_id = GetIRContext()->get_type_mgr()->GetId(&int_type);
|
||||
if (existing_id) {
|
||||
return existing_id;
|
||||
}
|
||||
auto result = GetFuzzerContext()->GetFreshId();
|
||||
@@ -173,7 +175,9 @@ uint32_t FuzzerPass::FindOrCreateIntegerType(uint32_t width, bool is_signed) {
|
||||
}
|
||||
|
||||
uint32_t FuzzerPass::FindOrCreateFloatType(uint32_t width) {
|
||||
if (auto existing_id = fuzzerutil::MaybeGetFloatType(GetIRContext(), width)) {
|
||||
opt::analysis::Float float_type(width);
|
||||
auto existing_id = GetIRContext()->get_type_mgr()->GetId(&float_type);
|
||||
if (existing_id) {
|
||||
return existing_id;
|
||||
}
|
||||
auto result = GetFuzzerContext()->GetFreshId();
|
||||
@@ -203,8 +207,14 @@ uint32_t FuzzerPass::FindOrCreateFunctionType(
|
||||
|
||||
uint32_t FuzzerPass::FindOrCreateVectorType(uint32_t component_type_id,
|
||||
uint32_t component_count) {
|
||||
if (auto existing_id = fuzzerutil::MaybeGetVectorType(
|
||||
GetIRContext(), component_type_id, component_count)) {
|
||||
assert(component_count >= 2 && component_count <= 4 &&
|
||||
"Precondition: component count must be in range [2, 4].");
|
||||
opt::analysis::Type* component_type =
|
||||
GetIRContext()->get_type_mgr()->GetType(component_type_id);
|
||||
assert(component_type && "Precondition: the component type must exist.");
|
||||
opt::analysis::Vector vector_type(component_type, component_count);
|
||||
auto existing_id = GetIRContext()->get_type_mgr()->GetId(&vector_type);
|
||||
if (existing_id) {
|
||||
return existing_id;
|
||||
}
|
||||
auto result = GetFuzzerContext()->GetFreshId();
|
||||
@@ -344,6 +354,24 @@ uint32_t FuzzerPass::FindOrCreateConstant(const std::vector<uint32_t>& words,
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint32_t FuzzerPass::FindOrCreateCompositeConstant(
|
||||
const std::vector<uint32_t>& component_ids, uint32_t type_id) {
|
||||
assert(type_id && "|type_id| can't be 0");
|
||||
const auto* type_inst = GetIRContext()->get_def_use_mgr()->GetDef(type_id);
|
||||
assert(type_inst && "|type_id| is invalid");
|
||||
|
||||
std::vector<const opt::analysis::Constant*> constants;
|
||||
for (auto id : component_ids) {
|
||||
assert(id && "Component's id can't be 0");
|
||||
const auto* constant =
|
||||
GetIRContext()->get_constant_mgr()->FindDeclaredConstant(id);
|
||||
assert(constant && "Component's id is invalid");
|
||||
constants.push_back(constant);
|
||||
}
|
||||
|
||||
return FindOrCreateCompositeConstant(*type_inst, constants, component_ids);
|
||||
}
|
||||
|
||||
uint32_t FuzzerPass::FindOrCreateGlobalUndef(uint32_t type_id) {
|
||||
for (auto& inst : GetIRContext()->types_values()) {
|
||||
if (inst.opcode() == SpvOpUndef && inst.type_id() == type_id) {
|
||||
@@ -355,6 +383,27 @@ uint32_t FuzzerPass::FindOrCreateGlobalUndef(uint32_t type_id) {
|
||||
return result;
|
||||
}
|
||||
|
||||
uint32_t FuzzerPass::FindOrCreateNullConstant(uint32_t type_id) {
|
||||
// Find existing declaration
|
||||
opt::analysis::NullConstant null_constant(
|
||||
GetIRContext()->get_type_mgr()->GetType(type_id));
|
||||
auto existing_constant =
|
||||
GetIRContext()->get_constant_mgr()->FindConstant(&null_constant);
|
||||
|
||||
// Return if found
|
||||
if (existing_constant) {
|
||||
return GetIRContext()
|
||||
->get_constant_mgr()
|
||||
->GetDefiningInstruction(existing_constant)
|
||||
->result_id();
|
||||
}
|
||||
|
||||
// Create new if not found
|
||||
auto result = GetFuzzerContext()->GetFreshId();
|
||||
ApplyTransformation(TransformationAddConstantNull(result, type_id));
|
||||
return result;
|
||||
}
|
||||
|
||||
std::pair<std::vector<uint32_t>, std::map<uint32_t, std::vector<uint32_t>>>
|
||||
FuzzerPass::GetAvailableBasicTypesAndPointers(
|
||||
SpvStorageClass storage_class) const {
|
||||
|
||||
24
3rdparty/spirv-tools/source/fuzz/fuzzer_pass.h
vendored
24
3rdparty/spirv-tools/source/fuzz/fuzzer_pass.h
vendored
@@ -103,6 +103,20 @@ class FuzzerPass {
|
||||
*GetTransformations()->add_transformation() = transformation.ToMessage();
|
||||
}
|
||||
|
||||
// A generic helper for applying a transformation only if it is applicable.
|
||||
// If it is applicable, the transformation is applied and then added to the
|
||||
// sequence of applied transformations and the function returns true.
|
||||
// Otherwise, the function returns false.
|
||||
bool MaybeApplyTransformation(const Transformation& transformation) {
|
||||
if (transformation.IsApplicable(GetIRContext(),
|
||||
*GetTransformationContext())) {
|
||||
transformation.Apply(GetIRContext(), GetTransformationContext());
|
||||
*GetTransformations()->add_transformation() = transformation.ToMessage();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Returns the id of an OpTypeBool instruction. If such an instruction does
|
||||
// not exist, a transformation is applied to add it.
|
||||
uint32_t FindOrCreateBoolType();
|
||||
@@ -183,11 +197,21 @@ class FuzzerPass {
|
||||
uint32_t FindOrCreateConstant(const std::vector<uint32_t>& words,
|
||||
uint32_t type_id);
|
||||
|
||||
// Returns the id of an OpConstantComposite
|
||||
uint32_t FindOrCreateCompositeConstant(
|
||||
const std::vector<uint32_t>& component_ids, uint32_t type_id);
|
||||
|
||||
// Returns the result id of an instruction of the form:
|
||||
// %id = OpUndef %|type_id|
|
||||
// If no such instruction exists, a transformation is applied to add it.
|
||||
uint32_t FindOrCreateGlobalUndef(uint32_t type_id);
|
||||
|
||||
// Returns the id of an OpNullConstant instruction of type |type_id|. If
|
||||
// that instruction doesn't exist, it is added through a transformation.
|
||||
// |type_id| must be a valid result id of an OpType* instruction that exists
|
||||
// in the module.
|
||||
uint32_t FindOrCreateNullConstant(uint32_t type_id);
|
||||
|
||||
// Define a *basic type* to be an integer, boolean or floating-point type,
|
||||
// or a matrix, vector, struct or fixed-size array built from basic types. In
|
||||
// particular, a basic type cannot contain an opaque type (such as an image),
|
||||
|
||||
@@ -59,11 +59,7 @@ void FuzzerPassAddDeadBlocks::Apply() {
|
||||
}
|
||||
// Apply all those transformations that are in fact applicable.
|
||||
for (auto& transformation : candidate_transformations) {
|
||||
if (transformation.IsApplicable(GetIRContext(),
|
||||
*GetTransformationContext())) {
|
||||
transformation.Apply(GetIRContext(), GetTransformationContext());
|
||||
*GetTransformations()->add_transformation() = transformation.ToMessage();
|
||||
}
|
||||
MaybeApplyTransformation(transformation);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -114,12 +114,9 @@ void FuzzerPassAddDeadBreaks::Apply() {
|
||||
candidate_transformations.erase(candidate_transformations.begin() + index);
|
||||
// Probabilistically decide whether to try to apply it vs. ignore it, in the
|
||||
// case that it is applicable.
|
||||
if (transformation.IsApplicable(GetIRContext(),
|
||||
*GetTransformationContext()) &&
|
||||
GetFuzzerContext()->ChoosePercentage(
|
||||
if (GetFuzzerContext()->ChoosePercentage(
|
||||
GetFuzzerContext()->GetChanceOfAddingDeadBreak())) {
|
||||
transformation.Apply(GetIRContext(), GetTransformationContext());
|
||||
*GetTransformations()->add_transformation() = transformation.ToMessage();
|
||||
MaybeApplyTransformation(transformation);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -80,14 +80,9 @@ void FuzzerPassAddDeadContinues::Apply() {
|
||||
block.id(), condition_value, std::move(phi_ids));
|
||||
// Probabilistically decide whether to apply the transformation in the
|
||||
// case that it is applicable.
|
||||
if (candidate_transformation.IsApplicable(GetIRContext(),
|
||||
*GetTransformationContext()) &&
|
||||
GetFuzzerContext()->ChoosePercentage(
|
||||
if (GetFuzzerContext()->ChoosePercentage(
|
||||
GetFuzzerContext()->GetChanceOfAddingDeadContinue())) {
|
||||
candidate_transformation.Apply(GetIRContext(),
|
||||
GetTransformationContext());
|
||||
*GetTransformations()->add_transformation() =
|
||||
candidate_transformation.ToMessage();
|
||||
MaybeApplyTransformation(candidate_transformation);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -64,12 +64,106 @@ void FuzzerPassAddEquationInstructions::Apply() {
|
||||
|
||||
// Try the opcodes for which we know how to make ids at random until
|
||||
// something works.
|
||||
std::vector<SpvOp> candidate_opcodes = {SpvOpIAdd, SpvOpISub,
|
||||
SpvOpLogicalNot, SpvOpSNegate};
|
||||
std::vector<SpvOp> candidate_opcodes = {
|
||||
SpvOpIAdd, SpvOpISub, SpvOpLogicalNot, SpvOpSNegate,
|
||||
SpvOpConvertUToF, SpvOpConvertSToF, SpvOpBitcast};
|
||||
do {
|
||||
auto opcode =
|
||||
GetFuzzerContext()->RemoveAtRandomIndex(&candidate_opcodes);
|
||||
switch (opcode) {
|
||||
case SpvOpConvertSToF:
|
||||
case SpvOpConvertUToF: {
|
||||
auto candidate_instructions =
|
||||
GetIntegerInstructions(available_instructions);
|
||||
|
||||
if (candidate_instructions.empty()) {
|
||||
break;
|
||||
}
|
||||
|
||||
const auto* operand =
|
||||
candidate_instructions[GetFuzzerContext()->RandomIndex(
|
||||
candidate_instructions)];
|
||||
|
||||
const auto* type =
|
||||
GetIRContext()->get_type_mgr()->GetType(operand->type_id());
|
||||
assert(type && "Operand has invalid type");
|
||||
|
||||
// Make sure a result type exists in the module.
|
||||
if (const auto* vector = type->AsVector()) {
|
||||
FindOrCreateVectorType(
|
||||
FindOrCreateFloatType(
|
||||
vector->element_type()->AsInteger()->width()),
|
||||
vector->element_count());
|
||||
} else {
|
||||
FindOrCreateFloatType(type->AsInteger()->width());
|
||||
}
|
||||
|
||||
ApplyTransformation(TransformationEquationInstruction(
|
||||
GetFuzzerContext()->GetFreshId(), opcode,
|
||||
{operand->result_id()}, instruction_descriptor));
|
||||
return;
|
||||
}
|
||||
case SpvOpBitcast: {
|
||||
std::vector<const opt::Instruction*> candidate_instructions;
|
||||
for (const auto* inst : available_instructions) {
|
||||
const auto* type =
|
||||
GetIRContext()->get_type_mgr()->GetType(inst->type_id());
|
||||
assert(type && "Instruction has invalid type");
|
||||
if ((type->AsVector() &&
|
||||
(type->AsVector()->element_type()->AsInteger() ||
|
||||
type->AsVector()->element_type()->AsFloat())) ||
|
||||
type->AsInteger() || type->AsFloat()) {
|
||||
// We support OpBitcast for only scalars or vectors of
|
||||
// numerical type.
|
||||
candidate_instructions.push_back(inst);
|
||||
}
|
||||
}
|
||||
|
||||
if (!candidate_instructions.empty()) {
|
||||
const auto* operand_inst =
|
||||
candidate_instructions[GetFuzzerContext()->RandomIndex(
|
||||
candidate_instructions)];
|
||||
const auto* operand_type =
|
||||
GetIRContext()->get_type_mgr()->GetType(
|
||||
operand_inst->type_id());
|
||||
assert(operand_type && "Operand instruction has invalid type");
|
||||
|
||||
// Make sure a result type exists in the module.
|
||||
//
|
||||
// TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3539):
|
||||
// The only constraint on the types of OpBitcast's parameters
|
||||
// is that they must have the same number of bits. Consider
|
||||
// improving the code below to support this in full.
|
||||
if (const auto* vector = operand_type->AsVector()) {
|
||||
uint32_t element_type_id;
|
||||
if (const auto* int_type =
|
||||
vector->element_type()->AsInteger()) {
|
||||
element_type_id = FindOrCreateFloatType(int_type->width());
|
||||
} else {
|
||||
assert(vector->element_type()->AsFloat() &&
|
||||
"Vector must have numerical elements");
|
||||
element_type_id = FindOrCreateIntegerType(
|
||||
vector->element_type()->AsFloat()->width(),
|
||||
GetFuzzerContext()->ChooseEven());
|
||||
}
|
||||
|
||||
FindOrCreateVectorType(element_type_id,
|
||||
vector->element_count());
|
||||
} else if (const auto* int_type = operand_type->AsInteger()) {
|
||||
FindOrCreateFloatType(int_type->width());
|
||||
} else {
|
||||
assert(operand_type->AsFloat() &&
|
||||
"Operand is not a scalar of numerical type");
|
||||
FindOrCreateIntegerType(operand_type->AsFloat()->width(),
|
||||
GetFuzzerContext()->ChooseEven());
|
||||
}
|
||||
|
||||
ApplyTransformation(TransformationEquationInstruction(
|
||||
GetFuzzerContext()->GetFreshId(), opcode,
|
||||
{operand_inst->result_id()}, instruction_descriptor));
|
||||
return;
|
||||
}
|
||||
} break;
|
||||
case SpvOpIAdd:
|
||||
case SpvOpISub: {
|
||||
// Instructions of integer (scalar or vector) result type are
|
||||
@@ -181,6 +275,20 @@ FuzzerPassAddEquationInstructions::GetIntegerInstructions(
|
||||
return result;
|
||||
}
|
||||
|
||||
std::vector<opt::Instruction*>
|
||||
FuzzerPassAddEquationInstructions::GetFloatInstructions(
|
||||
const std::vector<opt::Instruction*>& instructions) const {
|
||||
std::vector<opt::Instruction*> result;
|
||||
for (auto& inst : instructions) {
|
||||
auto type = GetIRContext()->get_type_mgr()->GetType(inst->type_id());
|
||||
if (type->AsFloat() ||
|
||||
(type->AsVector() && type->AsVector()->element_type()->AsFloat())) {
|
||||
result.push_back(inst);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
std::vector<opt::Instruction*>
|
||||
FuzzerPassAddEquationInstructions::GetBooleanInstructions(
|
||||
const std::vector<opt::Instruction*>& instructions) const {
|
||||
@@ -225,10 +333,11 @@ FuzzerPassAddEquationInstructions::RestrictToElementBitWidth(
|
||||
if (type->AsVector()) {
|
||||
type = type->AsVector()->element_type();
|
||||
}
|
||||
assert(type->AsInteger() &&
|
||||
assert((type->AsInteger() || type->AsFloat()) &&
|
||||
"Precondition: all input instructions must "
|
||||
"have integer scalar or vector type.");
|
||||
if (type->AsInteger()->width() == bit_width) {
|
||||
"have integer or float scalar or vector type.");
|
||||
if ((type->AsInteger() && type->AsInteger()->width() == bit_width) ||
|
||||
(type->AsFloat() && type->AsFloat()->width() == bit_width)) {
|
||||
result.push_back(inst);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -41,6 +41,11 @@ class FuzzerPassAddEquationInstructions : public FuzzerPass {
|
||||
std::vector<opt::Instruction*> GetIntegerInstructions(
|
||||
const std::vector<opt::Instruction*>& instructions) const;
|
||||
|
||||
// Returns only instructions, that have either a scalar floating-point or a
|
||||
// vector type.
|
||||
std::vector<opt::Instruction*> GetFloatInstructions(
|
||||
const std::vector<opt::Instruction*>& instructions) const;
|
||||
|
||||
// Yields those instructions in |instructions| that have boolean scalar or
|
||||
// vector result type.
|
||||
std::vector<opt::Instruction*> GetBooleanInstructions(
|
||||
@@ -53,9 +58,9 @@ class FuzzerPassAddEquationInstructions : public FuzzerPass {
|
||||
const std::vector<opt::Instruction*>& instructions,
|
||||
uint32_t vector_width) const;
|
||||
|
||||
// Requires that |instructions| are integer scalars or vectors. Returns only
|
||||
// those instructions for which the bit-width of the underlying integer type
|
||||
// is |bit_width|.
|
||||
// Requires that |instructions| are integer or float scalars or vectors.
|
||||
// Returns only those instructions for which the bit-width of the underlying
|
||||
// integer or floating-point type is |bit_width|.
|
||||
std::vector<opt::Instruction*> RestrictToElementBitWidth(
|
||||
const std::vector<opt::Instruction*>& instructions,
|
||||
uint32_t bit_width) const;
|
||||
|
||||
@@ -32,7 +32,16 @@ FuzzerPassAddParameters::FuzzerPassAddParameters(
|
||||
FuzzerPassAddParameters::~FuzzerPassAddParameters() = default;
|
||||
|
||||
void FuzzerPassAddParameters::Apply() {
|
||||
const auto& type_candidates = ComputeTypeCandidates();
|
||||
// Compute type candidates for the new parameter.
|
||||
std::vector<uint32_t> type_candidates;
|
||||
for (const auto& type_inst : GetIRContext()->module()->GetTypes()) {
|
||||
const auto* type =
|
||||
GetIRContext()->get_type_mgr()->GetType(type_inst->result_id());
|
||||
assert(type && "Type instruction is not registered in the type manager");
|
||||
if (TransformationAddParameter::IsParameterTypeSupported(*type)) {
|
||||
type_candidates.push_back(type_inst->result_id());
|
||||
}
|
||||
}
|
||||
|
||||
if (type_candidates.empty()) {
|
||||
// The module contains no suitable types to use in new parameters.
|
||||
@@ -71,32 +80,6 @@ void FuzzerPassAddParameters::Apply() {
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<uint32_t> FuzzerPassAddParameters::ComputeTypeCandidates() const {
|
||||
std::vector<uint32_t> result;
|
||||
|
||||
for (const auto* type_inst : GetIRContext()->module()->GetTypes()) {
|
||||
// TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3403):
|
||||
// the number of types we support here is limited by the number of types
|
||||
// supported by |FindOrCreateZeroConstant|.
|
||||
switch (type_inst->opcode()) {
|
||||
case SpvOpTypeBool:
|
||||
case SpvOpTypeInt:
|
||||
case SpvOpTypeFloat:
|
||||
case SpvOpTypeArray:
|
||||
case SpvOpTypeMatrix:
|
||||
case SpvOpTypeVector:
|
||||
case SpvOpTypeStruct: {
|
||||
result.push_back(type_inst->result_id());
|
||||
} break;
|
||||
default:
|
||||
// Ignore other types.
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
uint32_t FuzzerPassAddParameters::GetNumberOfParameters(
|
||||
const opt::Function& function) const {
|
||||
const auto* type = GetIRContext()->get_type_mgr()->GetType(
|
||||
|
||||
@@ -37,10 +37,6 @@ class FuzzerPassAddParameters : public FuzzerPass {
|
||||
void Apply() override;
|
||||
|
||||
private:
|
||||
// Uses types, defined in the module, to compute a vector of their ids, which
|
||||
// will be used as type ids of new parameters.
|
||||
std::vector<uint32_t> ComputeTypeCandidates() const;
|
||||
|
||||
// Returns number of parameters of |function|.
|
||||
uint32_t GetNumberOfParameters(const opt::Function& function) const;
|
||||
};
|
||||
|
||||
126
3rdparty/spirv-tools/source/fuzz/fuzzer_pass_add_synonyms.cpp
vendored
Normal file
126
3rdparty/spirv-tools/source/fuzz/fuzzer_pass_add_synonyms.cpp
vendored
Normal file
@@ -0,0 +1,126 @@
|
||||
// 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_add_synonyms.h"
|
||||
|
||||
#include "source/fuzz/fuzzer_context.h"
|
||||
#include "source/fuzz/fuzzer_util.h"
|
||||
#include "source/fuzz/instruction_descriptor.h"
|
||||
#include "source/fuzz/transformation_add_synonym.h"
|
||||
|
||||
namespace spvtools {
|
||||
namespace fuzz {
|
||||
|
||||
FuzzerPassAddSynonyms::FuzzerPassAddSynonyms(
|
||||
opt::IRContext* ir_context, TransformationContext* transformation_context,
|
||||
FuzzerContext* fuzzer_context,
|
||||
protobufs::TransformationSequence* transformations)
|
||||
: FuzzerPass(ir_context, transformation_context, fuzzer_context,
|
||||
transformations) {}
|
||||
|
||||
FuzzerPassAddSynonyms::~FuzzerPassAddSynonyms() = default;
|
||||
|
||||
void FuzzerPassAddSynonyms::Apply() {
|
||||
ForEachInstructionWithInstructionDescriptor(
|
||||
[this](opt::Function* function, opt::BasicBlock* block,
|
||||
opt::BasicBlock::iterator inst_it,
|
||||
const protobufs::InstructionDescriptor& instruction_descriptor) {
|
||||
// Skip |inst_it| if we can't insert anything above it. OpIAdd is just
|
||||
// a representative of some instruction that might be produced by the
|
||||
// transformation.
|
||||
if (!fuzzerutil::CanInsertOpcodeBeforeInstruction(SpvOpIAdd, inst_it)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!GetFuzzerContext()->ChoosePercentage(
|
||||
GetFuzzerContext()->GetChanceOfAddingSynonyms())) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto synonym_type = GetFuzzerContext()->GetRandomSynonymType();
|
||||
|
||||
// Select all instructions that can be used to create a synonym to.
|
||||
auto available_instructions = FindAvailableInstructions(
|
||||
function, block, inst_it,
|
||||
[synonym_type](opt::IRContext* ir_context, opt::Instruction* inst) {
|
||||
// Check that we can create a synonym to |inst| as described by
|
||||
// the |synonym_type| and insert it before |inst_it|.
|
||||
return TransformationAddSynonym::IsInstructionValid(
|
||||
ir_context, inst, synonym_type);
|
||||
});
|
||||
|
||||
if (available_instructions.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
const auto* existing_synonym =
|
||||
available_instructions[GetFuzzerContext()->RandomIndex(
|
||||
available_instructions)];
|
||||
|
||||
// Make sure the module contains all instructions required to apply the
|
||||
// transformation.
|
||||
switch (synonym_type) {
|
||||
case protobufs::TransformationAddSynonym::ADD_ZERO:
|
||||
case protobufs::TransformationAddSynonym::SUB_ZERO:
|
||||
case protobufs::TransformationAddSynonym::LOGICAL_OR:
|
||||
// Create a zero constant to be used as an operand of the synonymous
|
||||
// instruction.
|
||||
FindOrCreateZeroConstant(existing_synonym->type_id());
|
||||
break;
|
||||
case protobufs::TransformationAddSynonym::MUL_ONE:
|
||||
case protobufs::TransformationAddSynonym::LOGICAL_AND: {
|
||||
const auto* existing_synonym_type =
|
||||
GetIRContext()->get_type_mgr()->GetType(
|
||||
existing_synonym->type_id());
|
||||
assert(existing_synonym_type && "Instruction has invalid type");
|
||||
|
||||
if (const auto* vector = existing_synonym_type->AsVector()) {
|
||||
auto element_type_id =
|
||||
GetIRContext()->get_type_mgr()->GetId(vector->element_type());
|
||||
assert(element_type_id && "Vector's element type is invalid");
|
||||
|
||||
auto one_word = vector->element_type()->AsFloat()
|
||||
? fuzzerutil::FloatToWord(1)
|
||||
: 1u;
|
||||
FindOrCreateCompositeConstant(
|
||||
std::vector<uint32_t>(
|
||||
vector->element_count(),
|
||||
FindOrCreateConstant({one_word}, element_type_id)),
|
||||
existing_synonym->type_id());
|
||||
} else {
|
||||
FindOrCreateConstant(
|
||||
{existing_synonym_type->AsFloat() ? fuzzerutil::FloatToWord(1)
|
||||
: 1u},
|
||||
existing_synonym->type_id());
|
||||
}
|
||||
} break;
|
||||
default:
|
||||
// This assertion will fail if some SynonymType is missing from the
|
||||
// switch statement.
|
||||
assert(
|
||||
!TransformationAddSynonym::IsAdditionalConstantRequired(
|
||||
synonym_type) &&
|
||||
"|synonym_type| requires an additional constant to be present "
|
||||
"in the module");
|
||||
break;
|
||||
}
|
||||
|
||||
ApplyTransformation(TransformationAddSynonym(
|
||||
existing_synonym->result_id(), synonym_type,
|
||||
GetFuzzerContext()->GetFreshId(), instruction_descriptor));
|
||||
});
|
||||
}
|
||||
|
||||
} // namespace fuzz
|
||||
} // namespace spvtools
|
||||
40
3rdparty/spirv-tools/source/fuzz/fuzzer_pass_add_synonyms.h
vendored
Normal file
40
3rdparty/spirv-tools/source/fuzz/fuzzer_pass_add_synonyms.h
vendored
Normal 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_ADD_SYNONYMS_H_
|
||||
#define SOURCE_FUZZ_FUZZER_PASS_ADD_SYNONYMS_H_
|
||||
|
||||
#include "source/fuzz/fuzzer_pass.h"
|
||||
|
||||
namespace spvtools {
|
||||
namespace fuzz {
|
||||
|
||||
// Sprinkles instructions through the module that produce ids, synonymous to
|
||||
// some other instructions.
|
||||
class FuzzerPassAddSynonyms : public FuzzerPass {
|
||||
public:
|
||||
FuzzerPassAddSynonyms(opt::IRContext* ir_context,
|
||||
TransformationContext* transformation_context,
|
||||
FuzzerContext* fuzzer_context,
|
||||
protobufs::TransformationSequence* transformations);
|
||||
|
||||
~FuzzerPassAddSynonyms() override;
|
||||
|
||||
void Apply() override;
|
||||
};
|
||||
|
||||
} // namespace fuzz
|
||||
} // namespace spvtools
|
||||
|
||||
#endif // SOURCE_FUZZ_FUZZER_PASS_ADD_SYNONYMS_H_
|
||||
@@ -73,10 +73,9 @@ void FuzzerPassApplyIdSynonyms::Apply() {
|
||||
continue;
|
||||
}
|
||||
// |use_index| is the absolute index of the operand. We require
|
||||
// the index of the operand restricted to input operands only, so
|
||||
// we subtract the number of non-input operands from |use_index|.
|
||||
// the index of the operand restricted to input operands only.
|
||||
uint32_t use_in_operand_index =
|
||||
use_index - use_inst->NumOperands() + use_inst->NumInOperands();
|
||||
fuzzerutil::InOperandIndexFromOperandIndex(*use_inst, use_index);
|
||||
if (!TransformationReplaceIdWithSynonym::UseCanBeReplacedWithSynonym(
|
||||
GetIRContext(), use_inst, use_in_operand_index)) {
|
||||
continue;
|
||||
|
||||
@@ -15,7 +15,8 @@
|
||||
#include "source/fuzz/fuzzer_pass_copy_objects.h"
|
||||
|
||||
#include "source/fuzz/fuzzer_util.h"
|
||||
#include "source/fuzz/transformation_copy_object.h"
|
||||
#include "source/fuzz/protobufs/spirvfuzz_protobufs.h"
|
||||
#include "source/fuzz/transformation_add_synonym.h"
|
||||
|
||||
namespace spvtools {
|
||||
namespace fuzz {
|
||||
@@ -65,11 +66,12 @@ void FuzzerPassCopyObjects::Apply() {
|
||||
|
||||
// Choose a copyable instruction at random, and create and apply an
|
||||
// object copying transformation based on it.
|
||||
ApplyTransformation(TransformationCopyObject(
|
||||
ApplyTransformation(TransformationAddSynonym(
|
||||
relevant_instructions[GetFuzzerContext()->RandomIndex(
|
||||
relevant_instructions)]
|
||||
->result_id(),
|
||||
instruction_descriptor, GetFuzzerContext()->GetFreshId()));
|
||||
protobufs::TransformationAddSynonym::COPY_OBJECT,
|
||||
GetFuzzerContext()->GetFreshId(), instruction_descriptor));
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
119
3rdparty/spirv-tools/source/fuzz/fuzzer_pass_interchange_zero_like_constants.cpp
vendored
Normal file
119
3rdparty/spirv-tools/source/fuzz/fuzzer_pass_interchange_zero_like_constants.cpp
vendored
Normal file
@@ -0,0 +1,119 @@
|
||||
// Copyright (c) 2020 Stefano Milizia
|
||||
// Copyright (c) 2020 Google LLC
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "source/fuzz/fuzzer_pass_interchange_zero_like_constants.h"
|
||||
#include "source/fuzz/fuzzer_util.h"
|
||||
#include "source/fuzz/id_use_descriptor.h"
|
||||
#include "source/fuzz/transformation_record_synonymous_constants.h"
|
||||
#include "source/fuzz/transformation_replace_id_with_synonym.h"
|
||||
|
||||
namespace spvtools {
|
||||
namespace fuzz {
|
||||
FuzzerPassInterchangeZeroLikeConstants::FuzzerPassInterchangeZeroLikeConstants(
|
||||
opt::IRContext* ir_context, TransformationContext* transformation_context,
|
||||
FuzzerContext* fuzzer_context,
|
||||
protobufs::TransformationSequence* transformations)
|
||||
: FuzzerPass(ir_context, transformation_context, fuzzer_context,
|
||||
transformations) {}
|
||||
|
||||
FuzzerPassInterchangeZeroLikeConstants::
|
||||
~FuzzerPassInterchangeZeroLikeConstants() = default;
|
||||
|
||||
uint32_t FuzzerPassInterchangeZeroLikeConstants::FindOrCreateToggledConstant(
|
||||
opt::Instruction* declaration) {
|
||||
auto constant = GetIRContext()->get_constant_mgr()->FindDeclaredConstant(
|
||||
declaration->result_id());
|
||||
|
||||
// This pass only toggles zero-like constants
|
||||
if (!constant->IsZero()) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (constant->AsScalarConstant()) {
|
||||
return FindOrCreateNullConstant(declaration->type_id());
|
||||
} else if (constant->AsNullConstant()) {
|
||||
// Add declaration of equivalent scalar constant
|
||||
auto kind = constant->type()->kind();
|
||||
if (kind == opt::analysis::Type::kBool ||
|
||||
kind == opt::analysis::Type::kInteger ||
|
||||
kind == opt::analysis::Type::kFloat) {
|
||||
return FindOrCreateZeroConstant(declaration->type_id());
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void FuzzerPassInterchangeZeroLikeConstants::MaybeAddUseToReplace(
|
||||
opt::Instruction* use_inst, uint32_t use_index, uint32_t replacement_id,
|
||||
std::vector<std::pair<protobufs::IdUseDescriptor, uint32_t>>*
|
||||
uses_to_replace) {
|
||||
// Only consider this use if it is in a block
|
||||
if (!GetIRContext()->get_instr_block(use_inst)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Get the index of the operand restricted to input operands.
|
||||
uint32_t in_operand_index =
|
||||
fuzzerutil::InOperandIndexFromOperandIndex(*use_inst, use_index);
|
||||
auto id_use_descriptor =
|
||||
MakeIdUseDescriptorFromUse(GetIRContext(), use_inst, in_operand_index);
|
||||
uses_to_replace->emplace_back(
|
||||
std::make_pair(id_use_descriptor, replacement_id));
|
||||
}
|
||||
|
||||
void FuzzerPassInterchangeZeroLikeConstants::Apply() {
|
||||
// Make vector keeping track of all the uses we want to replace.
|
||||
// This is a vector of pairs, where the first element is an id use descriptor
|
||||
// identifying the use of a constant id and the second is the id that should
|
||||
// be used to replace it.
|
||||
std::vector<std::pair<protobufs::IdUseDescriptor, uint32_t>> uses_to_replace;
|
||||
|
||||
for (auto constant : GetIRContext()->GetConstants()) {
|
||||
uint32_t constant_id = constant->result_id();
|
||||
uint32_t toggled_id = FindOrCreateToggledConstant(constant);
|
||||
|
||||
if (!toggled_id) {
|
||||
// Not a zero-like constant
|
||||
continue;
|
||||
}
|
||||
|
||||
// Record synonymous constants
|
||||
ApplyTransformation(
|
||||
TransformationRecordSynonymousConstants(constant_id, toggled_id));
|
||||
|
||||
// Find all the uses of the constant and, for each, probabilistically
|
||||
// decide whether to replace it.
|
||||
GetIRContext()->get_def_use_mgr()->ForEachUse(
|
||||
constant_id,
|
||||
[this, toggled_id, &uses_to_replace](opt::Instruction* use_inst,
|
||||
uint32_t use_index) -> void {
|
||||
if (GetFuzzerContext()->ChoosePercentage(
|
||||
GetFuzzerContext()
|
||||
->GetChanceOfInterchangingZeroLikeConstants())) {
|
||||
MaybeAddUseToReplace(use_inst, use_index, toggled_id,
|
||||
&uses_to_replace);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Replace the ids
|
||||
for (auto use_to_replace : uses_to_replace) {
|
||||
MaybeApplyTransformation(TransformationReplaceIdWithSynonym(
|
||||
use_to_replace.first, use_to_replace.second));
|
||||
}
|
||||
}
|
||||
} // namespace fuzz
|
||||
} // namespace spvtools
|
||||
63
3rdparty/spirv-tools/source/fuzz/fuzzer_pass_interchange_zero_like_constants.h
vendored
Normal file
63
3rdparty/spirv-tools/source/fuzz/fuzzer_pass_interchange_zero_like_constants.h
vendored
Normal file
@@ -0,0 +1,63 @@
|
||||
// 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_FUZZER_PASS_INTERCHANGE_ZERO_LIKE_CONSTANTS_
|
||||
#define SOURCE_FUZZ_FUZZER_PASS_INTERCHANGE_ZERO_LIKE_CONSTANTS_
|
||||
|
||||
#include "source/fuzz/fuzzer_pass.h"
|
||||
|
||||
namespace spvtools {
|
||||
namespace fuzz {
|
||||
|
||||
// A pass that:
|
||||
// - Finds all the zero-like constant definitions in the module and adds the
|
||||
// definitions of the corresponding synonym, recording the fact that they
|
||||
// are synonymous. If the synonym is already in the module, it does not
|
||||
// add a new one.
|
||||
// - For each use of a zero-like constant, decides whether to change it to the
|
||||
// id of the toggled constant.
|
||||
class FuzzerPassInterchangeZeroLikeConstants : public FuzzerPass {
|
||||
public:
|
||||
FuzzerPassInterchangeZeroLikeConstants(
|
||||
opt::IRContext* ir_context, TransformationContext* transformation_context,
|
||||
FuzzerContext* fuzzer_context,
|
||||
protobufs::TransformationSequence* transformations);
|
||||
|
||||
~FuzzerPassInterchangeZeroLikeConstants() override;
|
||||
|
||||
void Apply() override;
|
||||
|
||||
private:
|
||||
// Given the declaration of a zero-like constant, it finds or creates the
|
||||
// corresponding toggled constant (a scalar constant of value 0 becomes a
|
||||
// null constant of the same type and vice versa).
|
||||
// Returns the id of the toggled instruction if the constant is zero-like,
|
||||
// 0 otherwise.
|
||||
uint32_t FindOrCreateToggledConstant(opt::Instruction* declaration);
|
||||
|
||||
// Given an id use (described by an instruction and an index) and an id with
|
||||
// which the original one should be replaced, adds a pair (with the elements
|
||||
// being the corresponding id use descriptor and the replacement id) to
|
||||
// |uses_to_replace| if the use is in an instruction block, otherwise does
|
||||
// nothing.
|
||||
void MaybeAddUseToReplace(
|
||||
opt::Instruction* use_inst, uint32_t use_index, uint32_t replacement_id,
|
||||
std::vector<std::pair<protobufs::IdUseDescriptor, uint32_t>>*
|
||||
uses_to_replace);
|
||||
};
|
||||
|
||||
} // namespace fuzz
|
||||
} // namespace spvtools
|
||||
#endif // SOURCE_FUZZ_FUZZER_PASS_INTERCHANGE_ZERO_LIKE_CONSTANTS_
|
||||
@@ -56,11 +56,7 @@ void FuzzerPassMergeBlocks::Apply() {
|
||||
uint32_t index = GetFuzzerContext()->RandomIndex(potential_transformations);
|
||||
auto transformation = potential_transformations.at(index);
|
||||
potential_transformations.erase(potential_transformations.begin() + index);
|
||||
if (transformation.IsApplicable(GetIRContext(),
|
||||
*GetTransformationContext())) {
|
||||
transformation.Apply(GetIRContext(), GetTransformationContext());
|
||||
*GetTransformations()->add_transformation() = transformation.ToMessage();
|
||||
}
|
||||
MaybeApplyTransformation(transformation);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -477,28 +477,18 @@ void FuzzerPassObfuscateConstants::Apply() {
|
||||
skipped_opcode_count.clear();
|
||||
}
|
||||
|
||||
switch (inst.opcode()) {
|
||||
case SpvOpPhi:
|
||||
// The instruction must not be an OpPhi, as we cannot insert
|
||||
// instructions before an OpPhi.
|
||||
// TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/2902):
|
||||
// there is scope for being less conservative.
|
||||
break;
|
||||
case SpvOpVariable:
|
||||
// The instruction must not be an OpVariable, the only id that an
|
||||
// OpVariable uses is an initializer id, which has to remain
|
||||
// constant.
|
||||
break;
|
||||
default:
|
||||
// Consider each operand of the instruction, and add a constant id
|
||||
// use for the operand if relevant.
|
||||
for (uint32_t in_operand_index = 0;
|
||||
in_operand_index < inst.NumInOperands(); in_operand_index++) {
|
||||
MaybeAddConstantIdUse(inst, in_operand_index,
|
||||
base_instruction_result_id,
|
||||
skipped_opcode_count, &constant_uses);
|
||||
}
|
||||
break;
|
||||
// The instruction must not be an OpVariable, the only id that an
|
||||
// OpVariable uses is an initializer id, which has to remain
|
||||
// constant.
|
||||
if (inst.opcode() != SpvOpVariable) {
|
||||
// Consider each operand of the instruction, and add a constant id
|
||||
// use for the operand if relevant.
|
||||
for (uint32_t in_operand_index = 0;
|
||||
in_operand_index < inst.NumInOperands(); in_operand_index++) {
|
||||
MaybeAddConstantIdUse(inst, in_operand_index,
|
||||
base_instruction_result_id,
|
||||
skipped_opcode_count, &constant_uses);
|
||||
}
|
||||
}
|
||||
|
||||
if (!inst.HasResultId()) {
|
||||
|
||||
@@ -89,11 +89,7 @@ void FuzzerPassOutlineFunctions::Apply() {
|
||||
/*new_callee_result_id*/ GetFuzzerContext()->GetFreshId(),
|
||||
/*input_id_to_fresh_id*/ std::move(input_id_to_fresh_id),
|
||||
/*output_id_to_fresh_id*/ std::move(output_id_to_fresh_id));
|
||||
if (transformation.IsApplicable(GetIRContext(),
|
||||
*GetTransformationContext())) {
|
||||
transformation.Apply(GetIRContext(), GetTransformationContext());
|
||||
*GetTransformations()->add_transformation() = transformation.ToMessage();
|
||||
}
|
||||
MaybeApplyTransformation(transformation);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -67,12 +67,7 @@ void FuzzerPassPermuteBlocks::Apply() {
|
||||
// down indefinitely.
|
||||
while (true) {
|
||||
TransformationMoveBlockDown transformation(*id);
|
||||
if (transformation.IsApplicable(GetIRContext(),
|
||||
*GetTransformationContext())) {
|
||||
transformation.Apply(GetIRContext(), GetTransformationContext());
|
||||
*GetTransformations()->add_transformation() =
|
||||
transformation.ToMessage();
|
||||
} else {
|
||||
if (!MaybeApplyTransformation(transformation)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -45,6 +45,7 @@ void FuzzerPassReplaceLinearAlgebraInstructions::Apply() {
|
||||
instruction->opcode() != SpvOpMatrixTimesScalar &&
|
||||
instruction->opcode() != SpvOpVectorTimesMatrix &&
|
||||
instruction->opcode() != SpvOpMatrixTimesVector &&
|
||||
instruction->opcode() != SpvOpMatrixTimesMatrix &&
|
||||
instruction->opcode() != SpvOpDot) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -58,7 +58,7 @@ void FuzzerPassReplaceParameterWithGlobal::Apply() {
|
||||
param->type_id());
|
||||
assert(param_type && "Parameter has invalid type");
|
||||
return TransformationReplaceParameterWithGlobal::
|
||||
CanReplaceFunctionParameterType(*param_type);
|
||||
IsParameterTypeSupported(*param_type);
|
||||
})) {
|
||||
continue;
|
||||
}
|
||||
@@ -71,8 +71,9 @@ void FuzzerPassReplaceParameterWithGlobal::Apply() {
|
||||
param_type =
|
||||
GetIRContext()->get_type_mgr()->GetType(replaced_param->type_id());
|
||||
assert(param_type && "Parameter has invalid type");
|
||||
} while (!TransformationReplaceParameterWithGlobal::
|
||||
CanReplaceFunctionParameterType(*param_type));
|
||||
} while (
|
||||
!TransformationReplaceParameterWithGlobal::IsParameterTypeSupported(
|
||||
*param_type));
|
||||
|
||||
assert(replaced_param && "Unable to find a parameter to replace");
|
||||
|
||||
|
||||
@@ -96,11 +96,7 @@ void FuzzerPassSplitBlocks::Apply() {
|
||||
// If the position we have chosen turns out to be a valid place to split
|
||||
// the block, we apply the split. Otherwise the block just doesn't get
|
||||
// split.
|
||||
if (transformation.IsApplicable(GetIRContext(),
|
||||
*GetTransformationContext())) {
|
||||
transformation.Apply(GetIRContext(), GetTransformationContext());
|
||||
*GetTransformations()->add_transformation() = transformation.ToMessage();
|
||||
}
|
||||
MaybeApplyTransformation(transformation);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
64
3rdparty/spirv-tools/source/fuzz/fuzzer_util.cpp
vendored
64
3rdparty/spirv-tools/source/fuzz/fuzzer_util.cpp
vendored
@@ -156,6 +156,25 @@ void AddUnreachableEdgeAndUpdateOpPhis(
|
||||
}
|
||||
}
|
||||
|
||||
bool BlockIsBackEdge(opt::IRContext* context, uint32_t block_id,
|
||||
uint32_t loop_header_id) {
|
||||
auto block = context->cfg()->block(block_id);
|
||||
auto loop_header = context->cfg()->block(loop_header_id);
|
||||
|
||||
// |block| and |loop_header| must be defined, |loop_header| must be in fact
|
||||
// loop header and |block| must branch to it.
|
||||
if (!(block && loop_header && loop_header->IsLoopHeader() &&
|
||||
block->IsSuccessor(loop_header))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// |block_id| must be reachable and be dominated by |loop_header|.
|
||||
opt::DominatorAnalysis* dominator_analysis =
|
||||
context->GetDominatorAnalysis(loop_header->GetParent());
|
||||
return dominator_analysis->IsReachable(block_id) &&
|
||||
dominator_analysis->Dominates(loop_header_id, block_id);
|
||||
}
|
||||
|
||||
bool BlockIsInLoopContinueConstruct(opt::IRContext* context, uint32_t block_id,
|
||||
uint32_t maybe_loop_header_id) {
|
||||
// We deem a block to be part of a loop's continue construct if the loop's
|
||||
@@ -533,6 +552,12 @@ uint32_t MaybeGetPointerType(opt::IRContext* context, uint32_t pointee_type_id,
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint32_t InOperandIndexFromOperandIndex(const opt::Instruction& inst,
|
||||
uint32_t absolute_index) {
|
||||
// Subtract the number of non-input operands from the index
|
||||
return absolute_index - inst.NumOperands() + inst.NumInOperands();
|
||||
}
|
||||
|
||||
bool IsNullConstantSupported(const opt::analysis::Type& type) {
|
||||
return type.AsBool() || type.AsInteger() || type.AsFloat() ||
|
||||
type.AsMatrix() || type.AsVector() || type.AsArray() ||
|
||||
@@ -1013,45 +1038,6 @@ void AddStructType(opt::IRContext* ir_context, uint32_t result_id,
|
||||
UpdateModuleIdBound(ir_context, result_id);
|
||||
}
|
||||
|
||||
uint32_t FindOrCreateIntegerType(opt::IRContext* ir_context, uint32_t result_id,
|
||||
uint32_t width, bool is_signed) {
|
||||
if (auto existing_id = MaybeGetIntegerType(ir_context, width, is_signed)) {
|
||||
return existing_id;
|
||||
}
|
||||
AddIntegerType(ir_context, result_id, width, is_signed);
|
||||
return result_id;
|
||||
}
|
||||
|
||||
uint32_t FindOrCreateFloatType(opt::IRContext* ir_context, uint32_t result_id,
|
||||
uint32_t width) {
|
||||
if (auto existing_id = MaybeGetFloatType(ir_context, width)) {
|
||||
return existing_id;
|
||||
}
|
||||
AddFloatType(ir_context, result_id, width);
|
||||
return result_id;
|
||||
}
|
||||
|
||||
uint32_t FindOrCreateVectorType(opt::IRContext* ir_context, uint32_t result_id,
|
||||
uint32_t component_type_id,
|
||||
uint32_t element_count) {
|
||||
if (auto existing_id =
|
||||
MaybeGetVectorType(ir_context, component_type_id, element_count)) {
|
||||
return existing_id;
|
||||
}
|
||||
AddVectorType(ir_context, result_id, component_type_id, element_count);
|
||||
return result_id;
|
||||
}
|
||||
|
||||
uint32_t FindOrCreateStructType(
|
||||
opt::IRContext* ir_context, uint32_t result_id,
|
||||
const std::vector<uint32_t>& component_type_ids) {
|
||||
if (auto existing_id = MaybeGetStructType(ir_context, component_type_ids)) {
|
||||
return existing_id;
|
||||
}
|
||||
AddStructType(ir_context, result_id, component_type_ids);
|
||||
return result_id;
|
||||
}
|
||||
|
||||
} // namespace fuzzerutil
|
||||
|
||||
} // namespace fuzz
|
||||
|
||||
49
3rdparty/spirv-tools/source/fuzz/fuzzer_util.h
vendored
49
3rdparty/spirv-tools/source/fuzz/fuzzer_util.h
vendored
@@ -64,6 +64,12 @@ void AddUnreachableEdgeAndUpdateOpPhis(
|
||||
bool condition_value,
|
||||
const google::protobuf::RepeatedField<google::protobuf::uint32>& phi_ids);
|
||||
|
||||
// Returns true if and only if |loop_header_id| is a loop header and
|
||||
// |block_id| is a reachable block branching to and dominated by
|
||||
// |loop_header_id|.
|
||||
bool BlockIsBackEdge(opt::IRContext* context, uint32_t block_id,
|
||||
uint32_t loop_header_id);
|
||||
|
||||
// Returns true if and only if |maybe_loop_header_id| is a loop header and
|
||||
// |block_id| is in the continue construct of the associated loop.
|
||||
bool BlockIsInLoopContinueConstruct(opt::IRContext* context, uint32_t block_id,
|
||||
@@ -207,6 +213,11 @@ SpvStorageClass GetStorageClassFromPointerType(opt::IRContext* context,
|
||||
uint32_t MaybeGetPointerType(opt::IRContext* context, uint32_t pointee_type_id,
|
||||
SpvStorageClass storage_class);
|
||||
|
||||
// Given an instruction |inst| and an operand absolute index |absolute_index|,
|
||||
// returns the index of the operand restricted to the input operands.
|
||||
uint32_t InOperandIndexFromOperandIndex(const opt::Instruction& inst,
|
||||
uint32_t absolute_index);
|
||||
|
||||
// Returns true if and only if |type| is one of the types for which it is legal
|
||||
// to have an OpConstantNull value.
|
||||
bool IsNullConstantSupported(const opt::analysis::Type& type);
|
||||
@@ -369,38 +380,12 @@ void AddVectorType(opt::IRContext* ir_context, uint32_t result_id,
|
||||
void AddStructType(opt::IRContext* ir_context, uint32_t result_id,
|
||||
const std::vector<uint32_t>& component_type_ids);
|
||||
|
||||
// Returns a result id of an OpTypeInt instruction in the module. Creates a new
|
||||
// instruction with |result_id|, if no required OpTypeInt is present in the
|
||||
// module, and returns |result_id|. Updates module's id bound to accommodate for
|
||||
// |result_id|.
|
||||
uint32_t FindOrCreateIntegerType(opt::IRContext* ir_context, uint32_t result_id,
|
||||
uint32_t width, bool is_signed);
|
||||
|
||||
// Returns a result id of an OpTypeFloat instruction in the module. Creates a
|
||||
// new instruction with |result_id|, if no required OpTypeFloat is present in
|
||||
// the module, and returns |result_id|. Updates module's id bound
|
||||
// to accommodate for |result_id|.
|
||||
uint32_t FindOrCreateFloatType(opt::IRContext* ir_context, uint32_t result_id,
|
||||
uint32_t width);
|
||||
|
||||
// Returns a result id of an OpTypeVector instruction in the module. Creates a
|
||||
// new instruction with |result_id|, if no required OpTypeVector is present in
|
||||
// the module, and returns |result_id|. |component_type_id| must be a valid
|
||||
// result id of an OpTypeInt, OpTypeFloat or OpTypeBool instruction in the
|
||||
// module. |element_count| must be in the range [2, 4]. Updates module's id
|
||||
// bound to accommodate for |result_id|.
|
||||
uint32_t FindOrCreateVectorType(opt::IRContext* ir_context, uint32_t result_id,
|
||||
uint32_t component_type_id,
|
||||
uint32_t element_count);
|
||||
|
||||
// Returns a result id of an OpTypeStruct instruction in the module. Creates a
|
||||
// new instruction with |result_id|, if no required OpTypeStruct is present in
|
||||
// the module, and returns |result_id|. Updates module's id bound
|
||||
// to accommodate for |result_id|. |component_type_ids| may not contain a result
|
||||
// id of an OpTypeFunction.
|
||||
uint32_t FindOrCreateStructType(
|
||||
opt::IRContext* ir_context, uint32_t result_id,
|
||||
const std::vector<uint32_t>& component_type_ids);
|
||||
// Returns a bit pattern that represents a floating-point |value|.
|
||||
inline uint32_t FloatToWord(float value) {
|
||||
uint32_t result;
|
||||
memcpy(&result, &value, sizeof(uint32_t));
|
||||
return result;
|
||||
}
|
||||
|
||||
} // namespace fuzzerutil
|
||||
|
||||
|
||||
@@ -39,6 +39,7 @@
|
||||
// where warnings are ignored.
|
||||
|
||||
#include "google/protobuf/util/json_util.h"
|
||||
#include "google/protobuf/util/message_differencer.h"
|
||||
#include "source/fuzz/protobufs/spvtoolsfuzz.pb.h"
|
||||
|
||||
#if defined(__clang__)
|
||||
|
||||
@@ -341,51 +341,51 @@ message Transformation {
|
||||
TransformationAddTypePointer add_type_pointer = 10;
|
||||
TransformationReplaceConstantWithUniform replace_constant_with_uniform = 11;
|
||||
TransformationAddDeadContinue add_dead_continue = 12;
|
||||
TransformationCopyObject copy_object = 13;
|
||||
TransformationReplaceIdWithSynonym replace_id_with_synonym = 14;
|
||||
TransformationSetSelectionControl set_selection_control = 15;
|
||||
TransformationCompositeConstruct composite_construct = 16;
|
||||
TransformationSetLoopControl set_loop_control = 17;
|
||||
TransformationSetFunctionControl set_function_control = 18;
|
||||
TransformationAddNoContractionDecoration add_no_contraction_decoration = 19;
|
||||
TransformationSetMemoryOperandsMask set_memory_operands_mask = 20;
|
||||
TransformationCompositeExtract composite_extract = 21;
|
||||
TransformationVectorShuffle vector_shuffle = 22;
|
||||
TransformationOutlineFunction outline_function = 23;
|
||||
TransformationMergeBlocks merge_blocks = 24;
|
||||
TransformationAddTypeVector add_type_vector = 25;
|
||||
TransformationAddTypeArray add_type_array = 26;
|
||||
TransformationAddTypeMatrix add_type_matrix = 27;
|
||||
TransformationAddTypeStruct add_type_struct = 28;
|
||||
TransformationAddTypeFunction add_type_function = 29;
|
||||
TransformationAddConstantComposite add_constant_composite = 30;
|
||||
TransformationAddGlobalVariable add_global_variable = 31;
|
||||
TransformationAddGlobalUndef add_global_undef = 32;
|
||||
TransformationAddFunction add_function = 33;
|
||||
TransformationAddDeadBlock add_dead_block = 34;
|
||||
TransformationAddLocalVariable add_local_variable = 35;
|
||||
TransformationLoad load = 36;
|
||||
TransformationStore store = 37;
|
||||
TransformationFunctionCall function_call = 38;
|
||||
TransformationAccessChain access_chain = 39;
|
||||
TransformationEquationInstruction equation_instruction = 40;
|
||||
TransformationSwapCommutableOperands swap_commutable_operands = 41;
|
||||
TransformationPermuteFunctionParameters permute_function_parameters = 42;
|
||||
TransformationToggleAccessChainInstruction toggle_access_chain_instruction = 43;
|
||||
TransformationAddConstantNull add_constant_null = 44;
|
||||
TransformationComputeDataSynonymFactClosure compute_data_synonym_fact_closure = 45;
|
||||
TransformationAdjustBranchWeights adjust_branch_weights = 46;
|
||||
TransformationPushIdThroughVariable push_id_through_variable = 47;
|
||||
TransformationAddSpecConstantOp add_spec_constant_op = 48;
|
||||
TransformationReplaceLinearAlgebraInstruction replace_linear_algebra_instruction = 49;
|
||||
TransformationSwapConditionalBranchOperands swap_conditional_branch_operands = 50;
|
||||
TransformationPermutePhiOperands permute_phi_operands = 51;
|
||||
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;
|
||||
TransformationReplaceIdWithSynonym replace_id_with_synonym = 13;
|
||||
TransformationSetSelectionControl set_selection_control = 14;
|
||||
TransformationCompositeConstruct composite_construct = 15;
|
||||
TransformationSetLoopControl set_loop_control = 16;
|
||||
TransformationSetFunctionControl set_function_control = 17;
|
||||
TransformationAddNoContractionDecoration add_no_contraction_decoration = 18;
|
||||
TransformationSetMemoryOperandsMask set_memory_operands_mask = 19;
|
||||
TransformationCompositeExtract composite_extract = 20;
|
||||
TransformationVectorShuffle vector_shuffle = 21;
|
||||
TransformationOutlineFunction outline_function = 22;
|
||||
TransformationMergeBlocks merge_blocks = 23;
|
||||
TransformationAddTypeVector add_type_vector = 24;
|
||||
TransformationAddTypeArray add_type_array = 25;
|
||||
TransformationAddTypeMatrix add_type_matrix = 26;
|
||||
TransformationAddTypeStruct add_type_struct = 27;
|
||||
TransformationAddTypeFunction add_type_function = 28;
|
||||
TransformationAddConstantComposite add_constant_composite = 29;
|
||||
TransformationAddGlobalVariable add_global_variable = 30;
|
||||
TransformationAddGlobalUndef add_global_undef = 31;
|
||||
TransformationAddFunction add_function = 32;
|
||||
TransformationAddDeadBlock add_dead_block = 33;
|
||||
TransformationAddLocalVariable add_local_variable = 34;
|
||||
TransformationLoad load = 35;
|
||||
TransformationStore store = 36;
|
||||
TransformationFunctionCall function_call = 37;
|
||||
TransformationAccessChain access_chain = 38;
|
||||
TransformationEquationInstruction equation_instruction = 39;
|
||||
TransformationSwapCommutableOperands swap_commutable_operands = 40;
|
||||
TransformationPermuteFunctionParameters permute_function_parameters = 41;
|
||||
TransformationToggleAccessChainInstruction toggle_access_chain_instruction = 42;
|
||||
TransformationAddConstantNull add_constant_null = 43;
|
||||
TransformationComputeDataSynonymFactClosure compute_data_synonym_fact_closure = 44;
|
||||
TransformationAdjustBranchWeights adjust_branch_weights = 45;
|
||||
TransformationPushIdThroughVariable push_id_through_variable = 46;
|
||||
TransformationAddSpecConstantOp add_spec_constant_op = 47;
|
||||
TransformationReplaceLinearAlgebraInstruction replace_linear_algebra_instruction = 48;
|
||||
TransformationSwapConditionalBranchOperands swap_conditional_branch_operands = 49;
|
||||
TransformationPermutePhiOperands permute_phi_operands = 50;
|
||||
TransformationAddParameter add_parameter = 51;
|
||||
TransformationAddCopyMemory add_copy_memory = 52;
|
||||
TransformationInvertComparisonOperator invert_comparison_operator = 53;
|
||||
TransformationAddImageSampleUnusedComponents add_image_sample_unused_components = 54;
|
||||
TransformationReplaceParameterWithGlobal replace_parameter_with_global = 55;
|
||||
TransformationRecordSynonymousConstants record_synonymous_constants = 56;
|
||||
TransformationAddSynonym add_synonym = 57;
|
||||
// Add additional option using the next available number.
|
||||
}
|
||||
}
|
||||
@@ -705,6 +705,54 @@ message TransformationAddSpecConstantOp {
|
||||
|
||||
}
|
||||
|
||||
message TransformationAddSynonym {
|
||||
|
||||
// Adds a |synonymous_instruction| before |insert_before| instruction with
|
||||
// and creates a fact that |result_id| and the result id of |synonymous_instruction|
|
||||
// are synonymous.
|
||||
|
||||
// Result id of the first synonym.
|
||||
uint32 result_id = 1;
|
||||
|
||||
// Type of the synonym to apply. Some types might produce instructions
|
||||
// with commutative operands. Such types do not specify the order of the
|
||||
// operands since we have a special transformation to swap commutable operands.
|
||||
//
|
||||
// TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3499):
|
||||
// Consider adding more types here.
|
||||
enum SynonymType {
|
||||
// New synonym is derived by adding zero to the |result_id|.
|
||||
ADD_ZERO = 0;
|
||||
|
||||
// New synonym is derived by subtracting zero from the |result_id|.
|
||||
SUB_ZERO = 1;
|
||||
|
||||
// New synonym is derived by multiplying |result_id| by one.
|
||||
MUL_ONE = 2;
|
||||
|
||||
// New synonym is derived by applying OpCopyObject instruction to |result_id|.
|
||||
COPY_OBJECT = 3;
|
||||
|
||||
// New synonym is derived by applying OpLogicalOr to |result_id| with the second
|
||||
// operand being 'false'.
|
||||
LOGICAL_OR = 4;
|
||||
|
||||
// New synonym is derived by applying OpLogicalAnd to |result_id| with the second
|
||||
// operand being 'true'.
|
||||
LOGICAL_AND = 5;
|
||||
}
|
||||
|
||||
// Type of the synonym to create. See SynonymType for more details.
|
||||
SynonymType synonym_type = 2;
|
||||
|
||||
// Fresh result id for a created synonym.
|
||||
uint32 synonym_fresh_id = 3;
|
||||
|
||||
// An instruction to insert a new synonym before.
|
||||
InstructionDescriptor insert_before = 4;
|
||||
|
||||
}
|
||||
|
||||
message TransformationAddTypeArray {
|
||||
|
||||
// Adds an array type of the given element type and size to the module
|
||||
@@ -896,23 +944,6 @@ message TransformationComputeDataSynonymFactClosure {
|
||||
|
||||
}
|
||||
|
||||
message TransformationCopyObject {
|
||||
|
||||
// A transformation that introduces an OpCopyObject instruction to make a
|
||||
// copy of an object.
|
||||
|
||||
// Id of the object to be copied
|
||||
uint32 object = 1;
|
||||
|
||||
// A descriptor for an instruction in a block before which the new
|
||||
// OpCopyObject instruction should be inserted
|
||||
InstructionDescriptor instruction_to_insert_before = 2;
|
||||
|
||||
// A fresh id for the copied object
|
||||
uint32 fresh_id = 3;
|
||||
|
||||
}
|
||||
|
||||
message TransformationEquationInstruction {
|
||||
|
||||
// A transformation that adds an instruction to the module that defines an
|
||||
@@ -1230,13 +1261,13 @@ message TransformationReplaceLinearAlgebraInstruction {
|
||||
// OpMatrixTimesScalar
|
||||
// OpVectorTimesMatrix
|
||||
// OpMatrixTimesVector
|
||||
// OpMatrixTimesMatrix
|
||||
// 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
|
||||
// OpMatrixTimesMatrix
|
||||
// OpOuterProduct
|
||||
InstructionDescriptor instruction_descriptor = 2;
|
||||
|
||||
|
||||
16
3rdparty/spirv-tools/source/fuzz/replayer.cpp
vendored
16
3rdparty/spirv-tools/source/fuzz/replayer.cpp
vendored
@@ -65,12 +65,20 @@ Replayer::ReplayerResultStatus Replayer::Run(
|
||||
const std::vector<uint32_t>& binary_in,
|
||||
const protobufs::FactSequence& initial_facts,
|
||||
const protobufs::TransformationSequence& transformation_sequence_in,
|
||||
std::vector<uint32_t>* binary_out,
|
||||
uint32_t num_transformations_to_apply, std::vector<uint32_t>* binary_out,
|
||||
protobufs::TransformationSequence* transformation_sequence_out) const {
|
||||
// Check compatibility between the library version being linked with and the
|
||||
// header files being used.
|
||||
GOOGLE_PROTOBUF_VERIFY_VERSION;
|
||||
|
||||
if (num_transformations_to_apply >
|
||||
static_cast<uint32_t>(transformation_sequence_in.transformation_size())) {
|
||||
impl_->consumer(SPV_MSG_ERROR, nullptr, {},
|
||||
"The number of transformations to be replayed must not "
|
||||
"exceed the size of the transformation sequence.");
|
||||
return Replayer::ReplayerResultStatus::kTooManyTransformationsRequested;
|
||||
}
|
||||
|
||||
spvtools::SpirvTools tools(impl_->target_env);
|
||||
if (!tools.IsValid()) {
|
||||
impl_->consumer(SPV_MSG_ERROR, nullptr, {},
|
||||
@@ -104,7 +112,13 @@ Replayer::ReplayerResultStatus Replayer::Run(
|
||||
impl_->validator_options);
|
||||
|
||||
// Consider the transformation proto messages in turn.
|
||||
uint32_t counter = 0;
|
||||
for (auto& message : transformation_sequence_in.transformation()) {
|
||||
if (counter >= num_transformations_to_apply) {
|
||||
break;
|
||||
}
|
||||
counter++;
|
||||
|
||||
auto transformation = Transformation::FromMessage(message);
|
||||
|
||||
// Check whether the transformation can be applied.
|
||||
|
||||
14
3rdparty/spirv-tools/source/fuzz/replayer.h
vendored
14
3rdparty/spirv-tools/source/fuzz/replayer.h
vendored
@@ -34,6 +34,7 @@ class Replayer {
|
||||
kFailedToCreateSpirvToolsInterface,
|
||||
kInitialBinaryInvalid,
|
||||
kReplayValidationFailure,
|
||||
kTooManyTransformationsRequested,
|
||||
};
|
||||
|
||||
// Constructs a replayer from the given target environment.
|
||||
@@ -52,16 +53,17 @@ class Replayer {
|
||||
// invoked once for each message communicated from the library.
|
||||
void SetMessageConsumer(MessageConsumer consumer);
|
||||
|
||||
// Transforms |binary_in| to |binary_out| by attempting to apply the
|
||||
// transformations from |transformation_sequence_in|. Initial facts about the
|
||||
// input binary and the context in which it will execute are provided via
|
||||
// |initial_facts|. The transformations that were successfully applied are
|
||||
// returned via |transformation_sequence_out|.
|
||||
// Transforms |binary_in| to |binary_out| by attempting to apply the first
|
||||
// |num_transformations_to_apply| transformations from
|
||||
// |transformation_sequence_in|. Initial facts about the input binary and the
|
||||
// context in which it will execute are provided via |initial_facts|. The
|
||||
// transformations that were successfully applied are returned via
|
||||
// |transformation_sequence_out|.
|
||||
ReplayerResultStatus Run(
|
||||
const std::vector<uint32_t>& binary_in,
|
||||
const protobufs::FactSequence& initial_facts,
|
||||
const protobufs::TransformationSequence& transformation_sequence_in,
|
||||
std::vector<uint32_t>* binary_out,
|
||||
uint32_t num_transformations_to_apply, std::vector<uint32_t>* binary_out,
|
||||
protobufs::TransformationSequence* transformation_sequence_out) const;
|
||||
|
||||
private:
|
||||
|
||||
@@ -124,6 +124,7 @@ Shrinker::ShrinkerResultStatus Shrinker::Run(
|
||||
if (Replayer(impl_->target_env, impl_->validate_during_replay,
|
||||
impl_->validator_options)
|
||||
.Run(binary_in, initial_facts, transformation_sequence_in,
|
||||
transformation_sequence_in.transformation_size(),
|
||||
¤t_best_binary, ¤t_best_transformations) !=
|
||||
Replayer::ReplayerResultStatus::kComplete) {
|
||||
return ShrinkerResultStatus::kReplayFailed;
|
||||
@@ -196,6 +197,7 @@ Shrinker::ShrinkerResultStatus Shrinker::Run(
|
||||
if (Replayer(impl_->target_env, impl_->validate_during_replay,
|
||||
impl_->validator_options)
|
||||
.Run(binary_in, initial_facts, transformations_with_chunk_removed,
|
||||
transformations_with_chunk_removed.transformation_size(),
|
||||
&next_binary, &next_transformation_sequence) !=
|
||||
Replayer::ReplayerResultStatus::kComplete) {
|
||||
// Replay should not fail; if it does, we need to abort shrinking.
|
||||
|
||||
@@ -34,6 +34,7 @@
|
||||
#include "source/fuzz/transformation_add_no_contraction_decoration.h"
|
||||
#include "source/fuzz/transformation_add_parameter.h"
|
||||
#include "source/fuzz/transformation_add_spec_constant_op.h"
|
||||
#include "source/fuzz/transformation_add_synonym.h"
|
||||
#include "source/fuzz/transformation_add_type_array.h"
|
||||
#include "source/fuzz/transformation_add_type_boolean.h"
|
||||
#include "source/fuzz/transformation_add_type_float.h"
|
||||
@@ -47,7 +48,6 @@
|
||||
#include "source/fuzz/transformation_composite_construct.h"
|
||||
#include "source/fuzz/transformation_composite_extract.h"
|
||||
#include "source/fuzz/transformation_compute_data_synonym_fact_closure.h"
|
||||
#include "source/fuzz/transformation_copy_object.h"
|
||||
#include "source/fuzz/transformation_equation_instruction.h"
|
||||
#include "source/fuzz/transformation_function_call.h"
|
||||
#include "source/fuzz/transformation_invert_comparison_operator.h"
|
||||
@@ -131,6 +131,8 @@ std::unique_ptr<Transformation> Transformation::FromMessage(
|
||||
case protobufs::Transformation::TransformationCase::kAddSpecConstantOp:
|
||||
return MakeUnique<TransformationAddSpecConstantOp>(
|
||||
message.add_spec_constant_op());
|
||||
case protobufs::Transformation::TransformationCase::kAddSynonym:
|
||||
return MakeUnique<TransformationAddSynonym>(message.add_synonym());
|
||||
case protobufs::Transformation::TransformationCase::kAddTypeArray:
|
||||
return MakeUnique<TransformationAddTypeArray>(message.add_type_array());
|
||||
case protobufs::Transformation::TransformationCase::kAddTypeBoolean:
|
||||
@@ -165,8 +167,6 @@ std::unique_ptr<Transformation> Transformation::FromMessage(
|
||||
kComputeDataSynonymFactClosure:
|
||||
return MakeUnique<TransformationComputeDataSynonymFactClosure>(
|
||||
message.compute_data_synonym_fact_closure());
|
||||
case protobufs::Transformation::TransformationCase::kCopyObject:
|
||||
return MakeUnique<TransformationCopyObject>(message.copy_object());
|
||||
case protobufs::Transformation::TransformationCase::kEquationInstruction:
|
||||
return MakeUnique<TransformationEquationInstruction>(
|
||||
message.equation_instruction());
|
||||
|
||||
@@ -74,9 +74,9 @@ bool TransformationAccessChain::IsApplicable(
|
||||
switch (pointer->opcode()) {
|
||||
case SpvOpConstantNull:
|
||||
case SpvOpUndef:
|
||||
// TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3185): When
|
||||
// fuzzing for real we would like an 'assert(false)' here. But we also
|
||||
// want to be able to write negative unit tests.
|
||||
assert(
|
||||
false &&
|
||||
"Access chains should not be created from null/undefined pointers");
|
||||
return false;
|
||||
default:
|
||||
break;
|
||||
|
||||
@@ -85,12 +85,10 @@ bool TransformationAddDeadBreak::AddingBreakRespectsStructuredControlFlow(
|
||||
// loop as part of the loop, but it is not legal to jump from a loop's
|
||||
// continue construct to the loop's merge (except from the back-edge block),
|
||||
// so we need to check for this case.
|
||||
//
|
||||
// TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/2577): We do not
|
||||
// currently allow a dead break from a back edge block, but we could and
|
||||
// ultimately should.
|
||||
return !fuzzerutil::BlockIsInLoopContinueConstruct(
|
||||
ir_context, message_.from_block(), containing_construct);
|
||||
ir_context, message_.from_block(), containing_construct) ||
|
||||
fuzzerutil::BlockIsBackEdge(ir_context, message_.from_block(),
|
||||
containing_construct);
|
||||
}
|
||||
|
||||
// Case (3) holds if and only if |to_block| is the merge block for this
|
||||
@@ -102,7 +100,9 @@ bool TransformationAddDeadBreak::AddingBreakRespectsStructuredControlFlow(
|
||||
message_.to_block() ==
|
||||
ir_context->cfg()->block(containing_loop_header)->MergeBlockId()) {
|
||||
return !fuzzerutil::BlockIsInLoopContinueConstruct(
|
||||
ir_context, message_.from_block(), containing_loop_header);
|
||||
ir_context, message_.from_block(), containing_loop_header) ||
|
||||
fuzzerutil::BlockIsBackEdge(ir_context, message_.from_block(),
|
||||
containing_loop_header);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -56,7 +56,7 @@ bool TransformationAddParameter::IsApplicable(
|
||||
const auto* initializer_type =
|
||||
ir_context->get_type_mgr()->GetType(initializer_inst->type_id());
|
||||
|
||||
if (!initializer_type || initializer_type->AsVoid()) {
|
||||
if (!initializer_type || !IsParameterTypeSupported(*initializer_type)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -139,5 +139,28 @@ protobufs::Transformation TransformationAddParameter::ToMessage() const {
|
||||
return result;
|
||||
}
|
||||
|
||||
bool TransformationAddParameter::IsParameterTypeSupported(
|
||||
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 IsParameterTypeSupported(*element_type);
|
||||
});
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace fuzz
|
||||
} // namespace spvtools
|
||||
|
||||
@@ -35,7 +35,8 @@ class TransformationAddParameter : public Transformation {
|
||||
// - |function_id| must be a valid result id of some non-entry-point function
|
||||
// in the module.
|
||||
// - |initializer_id| must be a valid result id of some instruction in the
|
||||
// module. Instruction's type may not be OpTypeVoid.
|
||||
// module. Instruction's type must be supported by this transformation
|
||||
// as specified by IsParameterTypeSupported function.
|
||||
// - |parameter_fresh_id| and |function_type_fresh_id| are fresh ids and are
|
||||
// not equal.
|
||||
bool IsApplicable(
|
||||
@@ -52,6 +53,10 @@ class TransformationAddParameter : public Transformation {
|
||||
|
||||
protobufs::Transformation ToMessage() const override;
|
||||
|
||||
// Returns true if the type of the parameter is supported by this
|
||||
// transformation.
|
||||
static bool IsParameterTypeSupported(const opt::analysis::Type& type);
|
||||
|
||||
private:
|
||||
protobufs::TransformationAddParameter message_;
|
||||
};
|
||||
|
||||
299
3rdparty/spirv-tools/source/fuzz/transformation_add_synonym.cpp
vendored
Normal file
299
3rdparty/spirv-tools/source/fuzz/transformation_add_synonym.cpp
vendored
Normal file
@@ -0,0 +1,299 @@
|
||||
// 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_add_synonym.h"
|
||||
|
||||
#include <utility>
|
||||
|
||||
#include "source/fuzz/fuzzer_util.h"
|
||||
#include "source/fuzz/instruction_descriptor.h"
|
||||
|
||||
namespace spvtools {
|
||||
namespace fuzz {
|
||||
|
||||
TransformationAddSynonym::TransformationAddSynonym(
|
||||
protobufs::TransformationAddSynonym message)
|
||||
: message_(std::move(message)) {}
|
||||
|
||||
TransformationAddSynonym::TransformationAddSynonym(
|
||||
uint32_t result_id,
|
||||
protobufs::TransformationAddSynonym::SynonymType synonym_type,
|
||||
uint32_t synonym_fresh_id,
|
||||
const protobufs::InstructionDescriptor& insert_before) {
|
||||
message_.set_result_id(result_id);
|
||||
message_.set_synonym_type(synonym_type);
|
||||
message_.set_synonym_fresh_id(synonym_fresh_id);
|
||||
*message_.mutable_insert_before() = insert_before;
|
||||
}
|
||||
|
||||
bool TransformationAddSynonym::IsApplicable(
|
||||
opt::IRContext* ir_context, const TransformationContext& /*unused*/) const {
|
||||
assert(protobufs::TransformationAddSynonym::SynonymType_IsValid(
|
||||
message_.synonym_type()) &&
|
||||
"Synonym type is invalid");
|
||||
|
||||
// |synonym_fresh_id| must be fresh.
|
||||
if (!fuzzerutil::IsFreshId(ir_context, message_.synonym_fresh_id())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check that |message_.result_id| is valid.
|
||||
auto* synonym = ir_context->get_def_use_mgr()->GetDef(message_.result_id());
|
||||
if (!synonym) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check that we can apply |synonym_type| to |result_id|.
|
||||
if (!IsInstructionValid(ir_context, synonym, message_.synonym_type())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check that |insert_before| is valid.
|
||||
auto* insert_before_inst =
|
||||
FindInstruction(message_.insert_before(), ir_context);
|
||||
if (!insert_before_inst) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check that we can insert |message._synonymous_instruction| before
|
||||
// |message_.insert_before| instruction. We use OpIAdd to represent some
|
||||
// instruction that can produce a synonym.
|
||||
if (!fuzzerutil::CanInsertOpcodeBeforeInstruction(SpvOpIAdd,
|
||||
insert_before_inst)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// A constant instruction must be present in the module if required.
|
||||
if (IsAdditionalConstantRequired(message_.synonym_type()) &&
|
||||
MaybeGetConstantId(ir_context) == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Domination rules must be satisfied.
|
||||
return fuzzerutil::IdIsAvailableBeforeInstruction(
|
||||
ir_context, insert_before_inst, message_.result_id());
|
||||
}
|
||||
|
||||
void TransformationAddSynonym::Apply(
|
||||
opt::IRContext* ir_context,
|
||||
TransformationContext* transformation_context) const {
|
||||
// Add a synonymous instruction.
|
||||
FindInstruction(message_.insert_before(), ir_context)
|
||||
->InsertBefore(MakeSynonymousInstruction(ir_context));
|
||||
|
||||
fuzzerutil::UpdateModuleIdBound(ir_context, message_.synonym_fresh_id());
|
||||
|
||||
ir_context->InvalidateAnalysesExceptFor(
|
||||
opt::IRContext::Analysis::kAnalysisNone);
|
||||
|
||||
// Propagate PointeeValueIsIrrelevant fact.
|
||||
const auto* new_synonym_type = ir_context->get_type_mgr()->GetType(
|
||||
fuzzerutil::GetTypeId(ir_context, message_.synonym_fresh_id()));
|
||||
assert(new_synonym_type && "New synonym should have a valid type");
|
||||
|
||||
if (transformation_context->GetFactManager()->PointeeValueIsIrrelevant(
|
||||
message_.result_id()) &&
|
||||
new_synonym_type->AsPointer()) {
|
||||
transformation_context->GetFactManager()->AddFactValueOfPointeeIsIrrelevant(
|
||||
message_.synonym_fresh_id());
|
||||
}
|
||||
|
||||
// Mark two ids as synonymous.
|
||||
transformation_context->GetFactManager()->AddFactDataSynonym(
|
||||
MakeDataDescriptor(message_.result_id(), {}),
|
||||
MakeDataDescriptor(message_.synonym_fresh_id(), {}), ir_context);
|
||||
}
|
||||
|
||||
protobufs::Transformation TransformationAddSynonym::ToMessage() const {
|
||||
protobufs::Transformation result;
|
||||
*result.mutable_add_synonym() = message_;
|
||||
return result;
|
||||
}
|
||||
|
||||
bool TransformationAddSynonym::IsInstructionValid(
|
||||
opt::IRContext* ir_context, opt::Instruction* inst,
|
||||
protobufs::TransformationAddSynonym::SynonymType synonym_type) {
|
||||
// Instruction must have a result id, type id. We skip OpUndef and
|
||||
// OpConstantNull.
|
||||
if (!inst || !inst->result_id() || !inst->type_id() ||
|
||||
inst->opcode() == SpvOpUndef || inst->opcode() == SpvOpConstantNull) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3177):
|
||||
// We can't create a synonym of an irrelevant id.
|
||||
switch (synonym_type) {
|
||||
case protobufs::TransformationAddSynonym::ADD_ZERO:
|
||||
case protobufs::TransformationAddSynonym::SUB_ZERO:
|
||||
case protobufs::TransformationAddSynonym::MUL_ONE: {
|
||||
// The instruction must be either scalar or vector of integers or floats.
|
||||
const auto* type = ir_context->get_type_mgr()->GetType(inst->type_id());
|
||||
assert(type && "Instruction's result id is invalid");
|
||||
|
||||
if (const auto* vector = type->AsVector()) {
|
||||
return vector->element_type()->AsInteger() ||
|
||||
vector->element_type()->AsFloat();
|
||||
}
|
||||
|
||||
return type->AsInteger() || type->AsFloat();
|
||||
}
|
||||
case protobufs::TransformationAddSynonym::COPY_OBJECT:
|
||||
return fuzzerutil::CanMakeSynonymOf(ir_context, inst);
|
||||
case protobufs::TransformationAddSynonym::LOGICAL_AND:
|
||||
case protobufs::TransformationAddSynonym::LOGICAL_OR: {
|
||||
// The instruction must be either a scalar or a vector of booleans.
|
||||
const auto* type = ir_context->get_type_mgr()->GetType(inst->type_id());
|
||||
assert(type && "Instruction's result id is invalid");
|
||||
return (type->AsVector() && type->AsVector()->element_type()->AsBool()) ||
|
||||
type->AsBool();
|
||||
}
|
||||
default:
|
||||
assert(false && "Synonym type is not supported");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
std::unique_ptr<opt::Instruction>
|
||||
TransformationAddSynonym::MakeSynonymousInstruction(
|
||||
opt::IRContext* ir_context) const {
|
||||
auto synonym_type_id =
|
||||
fuzzerutil::GetTypeId(ir_context, message_.result_id());
|
||||
assert(synonym_type_id && "Synonym has invalid type id");
|
||||
|
||||
switch (message_.synonym_type()) {
|
||||
case protobufs::TransformationAddSynonym::SUB_ZERO:
|
||||
case protobufs::TransformationAddSynonym::MUL_ONE:
|
||||
case protobufs::TransformationAddSynonym::ADD_ZERO: {
|
||||
const auto* synonym_type =
|
||||
ir_context->get_type_mgr()->GetType(synonym_type_id);
|
||||
assert(synonym_type && "Synonym has invalid type");
|
||||
|
||||
// Compute instruction's opcode based on the type of the operand.
|
||||
// We have already checked that the operand is either a scalar or a vector
|
||||
// of either integers or floats.
|
||||
auto is_integral =
|
||||
(synonym_type->AsVector() &&
|
||||
synonym_type->AsVector()->element_type()->AsInteger()) ||
|
||||
synonym_type->AsInteger();
|
||||
auto opcode = SpvOpNop;
|
||||
switch (message_.synonym_type()) {
|
||||
case protobufs::TransformationAddSynonym::SUB_ZERO:
|
||||
opcode = is_integral ? SpvOpISub : SpvOpFSub;
|
||||
break;
|
||||
case protobufs::TransformationAddSynonym::MUL_ONE:
|
||||
opcode = is_integral ? SpvOpIMul : SpvOpFMul;
|
||||
break;
|
||||
case protobufs::TransformationAddSynonym::ADD_ZERO:
|
||||
opcode = is_integral ? SpvOpIAdd : SpvOpFAdd;
|
||||
break;
|
||||
default:
|
||||
assert(false && "Unreachable");
|
||||
break;
|
||||
}
|
||||
|
||||
return MakeUnique<opt::Instruction>(
|
||||
ir_context, opcode, synonym_type_id, message_.synonym_fresh_id(),
|
||||
opt::Instruction::OperandList{
|
||||
{SPV_OPERAND_TYPE_ID, {message_.result_id()}},
|
||||
{SPV_OPERAND_TYPE_ID, {MaybeGetConstantId(ir_context)}}});
|
||||
}
|
||||
case protobufs::TransformationAddSynonym::COPY_OBJECT:
|
||||
return MakeUnique<opt::Instruction>(
|
||||
ir_context, SpvOpCopyObject, synonym_type_id,
|
||||
message_.synonym_fresh_id(),
|
||||
opt::Instruction::OperandList{
|
||||
{SPV_OPERAND_TYPE_ID, {message_.result_id()}}});
|
||||
case protobufs::TransformationAddSynonym::LOGICAL_OR:
|
||||
case protobufs::TransformationAddSynonym::LOGICAL_AND: {
|
||||
auto opcode = message_.synonym_type() ==
|
||||
protobufs::TransformationAddSynonym::LOGICAL_OR
|
||||
? SpvOpLogicalOr
|
||||
: SpvOpLogicalAnd;
|
||||
return MakeUnique<opt::Instruction>(
|
||||
ir_context, opcode, synonym_type_id, message_.synonym_fresh_id(),
|
||||
opt::Instruction::OperandList{
|
||||
{SPV_OPERAND_TYPE_ID, {message_.result_id()}},
|
||||
{SPV_OPERAND_TYPE_ID, {MaybeGetConstantId(ir_context)}}});
|
||||
}
|
||||
default:
|
||||
assert(false && "Unhandled synonym type");
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t TransformationAddSynonym::MaybeGetConstantId(
|
||||
opt::IRContext* ir_context) const {
|
||||
assert(IsAdditionalConstantRequired(message_.synonym_type()) &&
|
||||
"Synonym type doesn't require an additional constant");
|
||||
|
||||
auto synonym_type_id =
|
||||
fuzzerutil::GetTypeId(ir_context, message_.result_id());
|
||||
assert(synonym_type_id && "Synonym has invalid type id");
|
||||
|
||||
switch (message_.synonym_type()) {
|
||||
case protobufs::TransformationAddSynonym::ADD_ZERO:
|
||||
case protobufs::TransformationAddSynonym::SUB_ZERO:
|
||||
case protobufs::TransformationAddSynonym::LOGICAL_OR:
|
||||
return fuzzerutil::MaybeGetZeroConstant(ir_context, synonym_type_id);
|
||||
case protobufs::TransformationAddSynonym::MUL_ONE:
|
||||
case protobufs::TransformationAddSynonym::LOGICAL_AND: {
|
||||
auto synonym_type = ir_context->get_type_mgr()->GetType(synonym_type_id);
|
||||
assert(synonym_type && "Synonym has invalid type");
|
||||
|
||||
if (const auto* vector = synonym_type->AsVector()) {
|
||||
auto element_type_id =
|
||||
ir_context->get_type_mgr()->GetId(vector->element_type());
|
||||
assert(element_type_id && "Vector's element type is invalid");
|
||||
|
||||
auto one_word =
|
||||
vector->element_type()->AsFloat() ? fuzzerutil::FloatToWord(1) : 1u;
|
||||
if (auto scalar_one_id = fuzzerutil::MaybeGetScalarConstant(
|
||||
ir_context, {one_word}, element_type_id)) {
|
||||
return fuzzerutil::MaybeGetCompositeConstant(
|
||||
ir_context,
|
||||
std::vector<uint32_t>(vector->element_count(), scalar_one_id),
|
||||
synonym_type_id);
|
||||
}
|
||||
|
||||
return 0;
|
||||
} else {
|
||||
return fuzzerutil::MaybeGetScalarConstant(
|
||||
ir_context,
|
||||
{synonym_type->AsFloat() ? fuzzerutil::FloatToWord(1) : 1u},
|
||||
synonym_type_id);
|
||||
}
|
||||
}
|
||||
default:
|
||||
// The assertion at the beginning of the function will fail in the debug
|
||||
// mode.
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
bool TransformationAddSynonym::IsAdditionalConstantRequired(
|
||||
protobufs::TransformationAddSynonym::SynonymType synonym_type) {
|
||||
switch (synonym_type) {
|
||||
case protobufs::TransformationAddSynonym::ADD_ZERO:
|
||||
case protobufs::TransformationAddSynonym::SUB_ZERO:
|
||||
case protobufs::TransformationAddSynonym::LOGICAL_OR:
|
||||
case protobufs::TransformationAddSynonym::MUL_ONE:
|
||||
case protobufs::TransformationAddSynonym::LOGICAL_AND:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace fuzz
|
||||
} // namespace spvtools
|
||||
86
3rdparty/spirv-tools/source/fuzz/transformation_add_synonym.h
vendored
Normal file
86
3rdparty/spirv-tools/source/fuzz/transformation_add_synonym.h
vendored
Normal file
@@ -0,0 +1,86 @@
|
||||
// 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_ADD_SYNONYM_H_
|
||||
#define SOURCE_FUZZ_TRANSFORMATION_ADD_SYNONYM_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 TransformationAddSynonym : public Transformation {
|
||||
public:
|
||||
explicit TransformationAddSynonym(
|
||||
protobufs::TransformationAddSynonym message);
|
||||
|
||||
TransformationAddSynonym(
|
||||
uint32_t result_id,
|
||||
protobufs::TransformationAddSynonym::SynonymType synonym_type,
|
||||
uint32_t synonym_fresh_id,
|
||||
const protobufs::InstructionDescriptor& insert_before);
|
||||
|
||||
// - |result_id| must be a valid result id of some instruction in the module.
|
||||
// - |synonym_type| is a type of the synonymous instruction that will be
|
||||
// created.
|
||||
// - |synonym_fresh_id| is a fresh id.
|
||||
// - |insert_before| must be a valid instruction descriptor and we must be
|
||||
// able to insert a new synonymous instruction before |insert_before|.
|
||||
// - |result_id| must be available before |insert_before|.
|
||||
bool IsApplicable(
|
||||
opt::IRContext* ir_context,
|
||||
const TransformationContext& transformation_context) const override;
|
||||
|
||||
// Creates a new synonymous instruction according to the |synonym_type| with
|
||||
// result id |synonym_fresh_id|.
|
||||
// Inserts that instruction before |insert_before| and creates a fact
|
||||
// that the |synonym_fresh_id| and the |result_id| are synonymous.
|
||||
void Apply(opt::IRContext* ir_context,
|
||||
TransformationContext* transformation_context) const override;
|
||||
|
||||
protobufs::Transformation ToMessage() const override;
|
||||
|
||||
// Returns true if we can create a synonym of |inst| according to the
|
||||
// |synonym_type|.
|
||||
static bool IsInstructionValid(
|
||||
opt::IRContext* ir_context, opt::Instruction* inst,
|
||||
protobufs::TransformationAddSynonym::SynonymType synonym_type);
|
||||
|
||||
// Returns true if |synonym_type| requires an additional constant instruction
|
||||
// to be present in the module.
|
||||
static bool IsAdditionalConstantRequired(
|
||||
protobufs::TransformationAddSynonym::SynonymType synonym_type);
|
||||
|
||||
private:
|
||||
// Returns a new instruction which is synonymous to |message_.result_id|.
|
||||
std::unique_ptr<opt::Instruction> MakeSynonymousInstruction(
|
||||
opt::IRContext* ir_context) const;
|
||||
|
||||
// Returns a result id of a constant instruction that is required to be
|
||||
// present in some synonym types (e.g. returns a result id of a zero constant
|
||||
// for ADD_ZERO synonym type). Returns 0 if no such instruction is present in
|
||||
// the module. This method should only be called when
|
||||
// IsAdditionalConstantRequired returns true.
|
||||
uint32_t MaybeGetConstantId(opt::IRContext* ir_context) const;
|
||||
|
||||
protobufs::TransformationAddSynonym message_;
|
||||
};
|
||||
|
||||
} // namespace fuzz
|
||||
} // namespace spvtools
|
||||
|
||||
#endif // SOURCE_FUZZ_TRANSFORMATION_ADD_SYNONYM_H_
|
||||
@@ -18,10 +18,8 @@ namespace spvtools {
|
||||
namespace fuzz {
|
||||
|
||||
TransformationContext::TransformationContext(
|
||||
FactManager* transformation_context,
|
||||
spv_validator_options validator_options)
|
||||
: fact_manager_(transformation_context),
|
||||
validator_options_(validator_options) {}
|
||||
FactManager* fact_manager, spv_validator_options validator_options)
|
||||
: fact_manager_(fact_manager), validator_options_(validator_options) {}
|
||||
|
||||
TransformationContext::~TransformationContext() = default;
|
||||
|
||||
|
||||
@@ -1,115 +0,0 @@
|
||||
// Copyright (c) 2019 Google LLC
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "source/fuzz/transformation_copy_object.h"
|
||||
|
||||
#include "source/fuzz/data_descriptor.h"
|
||||
#include "source/fuzz/fuzzer_util.h"
|
||||
#include "source/fuzz/instruction_descriptor.h"
|
||||
#include "source/opt/instruction.h"
|
||||
#include "source/util/make_unique.h"
|
||||
|
||||
namespace spvtools {
|
||||
namespace fuzz {
|
||||
|
||||
TransformationCopyObject::TransformationCopyObject(
|
||||
const protobufs::TransformationCopyObject& message)
|
||||
: message_(message) {}
|
||||
|
||||
TransformationCopyObject::TransformationCopyObject(
|
||||
uint32_t object,
|
||||
const protobufs::InstructionDescriptor& instruction_to_insert_before,
|
||||
uint32_t fresh_id) {
|
||||
message_.set_object(object);
|
||||
*message_.mutable_instruction_to_insert_before() =
|
||||
instruction_to_insert_before;
|
||||
message_.set_fresh_id(fresh_id);
|
||||
}
|
||||
|
||||
bool TransformationCopyObject::IsApplicable(
|
||||
opt::IRContext* ir_context, const TransformationContext& /*unused*/) const {
|
||||
if (!fuzzerutil::IsFreshId(ir_context, message_.fresh_id())) {
|
||||
// We require the id for the object copy to be unused.
|
||||
return false;
|
||||
}
|
||||
// The id of the object to be copied must exist
|
||||
auto object_inst = ir_context->get_def_use_mgr()->GetDef(message_.object());
|
||||
if (!object_inst) {
|
||||
return false;
|
||||
}
|
||||
if (!fuzzerutil::CanMakeSynonymOf(ir_context, object_inst)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
auto insert_before =
|
||||
FindInstruction(message_.instruction_to_insert_before(), ir_context);
|
||||
if (!insert_before) {
|
||||
// The instruction before which the copy should be inserted was not found.
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!fuzzerutil::CanInsertOpcodeBeforeInstruction(SpvOpCopyObject,
|
||||
insert_before)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// |message_object| must be available directly before the point where we want
|
||||
// to add the copy.
|
||||
return fuzzerutil::IdIsAvailableBeforeInstruction(ir_context, insert_before,
|
||||
message_.object());
|
||||
}
|
||||
|
||||
void TransformationCopyObject::Apply(
|
||||
opt::IRContext* ir_context,
|
||||
TransformationContext* transformation_context) const {
|
||||
auto object_inst = ir_context->get_def_use_mgr()->GetDef(message_.object());
|
||||
assert(object_inst && "The object to be copied must exist.");
|
||||
auto insert_before_inst =
|
||||
FindInstruction(message_.instruction_to_insert_before(), ir_context);
|
||||
auto destination_block = ir_context->get_instr_block(insert_before_inst);
|
||||
assert(destination_block && "The base instruction must be in a block.");
|
||||
auto insert_before = fuzzerutil::GetIteratorForInstruction(
|
||||
destination_block, insert_before_inst);
|
||||
assert(insert_before != destination_block->end() &&
|
||||
"There must be an instruction before which the copy can be inserted.");
|
||||
|
||||
opt::Instruction::OperandList operands = {
|
||||
{SPV_OPERAND_TYPE_ID, {message_.object()}}};
|
||||
insert_before->InsertBefore(MakeUnique<opt::Instruction>(
|
||||
ir_context, SpvOp::SpvOpCopyObject, object_inst->type_id(),
|
||||
message_.fresh_id(), operands));
|
||||
|
||||
fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_id());
|
||||
ir_context->InvalidateAnalysesExceptFor(
|
||||
opt::IRContext::Analysis::kAnalysisNone);
|
||||
|
||||
transformation_context->GetFactManager()->AddFactDataSynonym(
|
||||
MakeDataDescriptor(message_.object(), {}),
|
||||
MakeDataDescriptor(message_.fresh_id(), {}), ir_context);
|
||||
|
||||
if (transformation_context->GetFactManager()->PointeeValueIsIrrelevant(
|
||||
message_.object())) {
|
||||
transformation_context->GetFactManager()->AddFactValueOfPointeeIsIrrelevant(
|
||||
message_.fresh_id());
|
||||
}
|
||||
}
|
||||
|
||||
protobufs::Transformation TransformationCopyObject::ToMessage() const {
|
||||
protobufs::Transformation result;
|
||||
*result.mutable_copy_object() = message_;
|
||||
return result;
|
||||
}
|
||||
|
||||
} // namespace fuzz
|
||||
} // namespace spvtools
|
||||
@@ -1,77 +0,0 @@
|
||||
// Copyright (c) 2019 Google LLC
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#ifndef SOURCE_FUZZ_TRANSFORMATION_COPY_OBJECT_H_
|
||||
#define SOURCE_FUZZ_TRANSFORMATION_COPY_OBJECT_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 TransformationCopyObject : public Transformation {
|
||||
public:
|
||||
explicit TransformationCopyObject(
|
||||
const protobufs::TransformationCopyObject& message);
|
||||
|
||||
TransformationCopyObject(
|
||||
uint32_t object,
|
||||
const protobufs::InstructionDescriptor& instruction_to_insert_before,
|
||||
uint32_t fresh_id);
|
||||
|
||||
// - |message_.fresh_id| must not be used by the module.
|
||||
// - |message_.object| must be a result id that is a legitimate operand for
|
||||
// OpCopyObject. In particular, it must be the id of an instruction that
|
||||
// has a result type
|
||||
// - |message_.object| must not be the target of any decoration.
|
||||
// TODO(afd): consider copying decorations along with objects.
|
||||
// - |message_.base_instruction_id| must be the result id of an instruction
|
||||
// 'base' in some block 'blk'.
|
||||
// - 'blk' must contain an instruction 'inst' located |message_.offset|
|
||||
// instructions after 'base' (if |message_.offset| = 0 then 'inst' =
|
||||
// 'base').
|
||||
// - It must be legal to insert an OpCopyObject instruction directly
|
||||
// before 'inst'.
|
||||
// - |message_.object| must be available directly before 'inst'.
|
||||
// - |message_.object| must not be a null pointer or undefined pointer (so as
|
||||
// to make it legal to load from copied pointers).
|
||||
bool IsApplicable(
|
||||
opt::IRContext* ir_context,
|
||||
const TransformationContext& transformation_context) const override;
|
||||
|
||||
// - A new instruction,
|
||||
// %|message_.fresh_id| = OpCopyObject %ty %|message_.object|
|
||||
// is added directly before the instruction at |message_.insert_after_id| +
|
||||
// |message_|.offset, where %ty is the type of |message_.object|.
|
||||
// - The fact that |message_.fresh_id| and |message_.object| are synonyms
|
||||
// is added to the fact manager in |transformation_context|.
|
||||
// - If |message_.object| is a pointer whose pointee value is known to be
|
||||
// irrelevant, the analogous fact is added to the fact manager in
|
||||
// |transformation_context| about |message_.fresh_id|.
|
||||
void Apply(opt::IRContext* ir_context,
|
||||
TransformationContext* transformation_context) const override;
|
||||
|
||||
protobufs::Transformation ToMessage() const override;
|
||||
|
||||
private:
|
||||
protobufs::TransformationCopyObject message_;
|
||||
};
|
||||
|
||||
} // namespace fuzz
|
||||
} // namespace spvtools
|
||||
|
||||
#endif // SOURCE_FUZZ_TRANSFORMATION_COPY_OBJECT_H_
|
||||
@@ -42,6 +42,7 @@ bool TransformationEquationInstruction::IsApplicable(
|
||||
if (!fuzzerutil::IsFreshId(ir_context, message_.fresh_id())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// The instruction to insert before must exist.
|
||||
auto insert_before =
|
||||
FindInstruction(message_.instruction_to_insert_before(), ir_context);
|
||||
@@ -64,7 +65,7 @@ bool TransformationEquationInstruction::IsApplicable(
|
||||
}
|
||||
}
|
||||
|
||||
return MaybeGetResultType(ir_context) != 0;
|
||||
return MaybeGetResultTypeId(ir_context) != 0;
|
||||
}
|
||||
|
||||
void TransformationEquationInstruction::Apply(
|
||||
@@ -82,7 +83,8 @@ void TransformationEquationInstruction::Apply(
|
||||
FindInstruction(message_.instruction_to_insert_before(), ir_context)
|
||||
->InsertBefore(MakeUnique<opt::Instruction>(
|
||||
ir_context, static_cast<SpvOp>(message_.opcode()),
|
||||
MaybeGetResultType(ir_context), message_.fresh_id(), in_operands));
|
||||
MaybeGetResultTypeId(ir_context), message_.fresh_id(),
|
||||
std::move(in_operands)));
|
||||
|
||||
ir_context->InvalidateAnalysesExceptFor(opt::IRContext::kAnalysisNone);
|
||||
|
||||
@@ -97,12 +99,108 @@ protobufs::Transformation TransformationEquationInstruction::ToMessage() const {
|
||||
return result;
|
||||
}
|
||||
|
||||
uint32_t TransformationEquationInstruction::MaybeGetResultType(
|
||||
uint32_t TransformationEquationInstruction::MaybeGetResultTypeId(
|
||||
opt::IRContext* ir_context) const {
|
||||
switch (static_cast<SpvOp>(message_.opcode())) {
|
||||
auto opcode = static_cast<SpvOp>(message_.opcode());
|
||||
switch (opcode) {
|
||||
case SpvOpConvertUToF:
|
||||
case SpvOpConvertSToF: {
|
||||
if (message_.in_operand_id_size() != 1) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
const auto* type = ir_context->get_type_mgr()->GetType(
|
||||
fuzzerutil::GetTypeId(ir_context, message_.in_operand_id(0)));
|
||||
if (!type) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (const auto* vector = type->AsVector()) {
|
||||
if (!vector->element_type()->AsInteger()) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (auto element_type_id = fuzzerutil::MaybeGetFloatType(
|
||||
ir_context, vector->element_type()->AsInteger()->width())) {
|
||||
return fuzzerutil::MaybeGetVectorType(ir_context, element_type_id,
|
||||
vector->element_count());
|
||||
}
|
||||
|
||||
return 0;
|
||||
} else {
|
||||
if (!type->AsInteger()) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return fuzzerutil::MaybeGetFloatType(ir_context,
|
||||
type->AsInteger()->width());
|
||||
}
|
||||
}
|
||||
case SpvOpBitcast: {
|
||||
if (message_.in_operand_id_size() != 1) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
const auto* operand_inst =
|
||||
ir_context->get_def_use_mgr()->GetDef(message_.in_operand_id(0));
|
||||
if (!operand_inst) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
const auto* operand_type =
|
||||
ir_context->get_type_mgr()->GetType(operand_inst->type_id());
|
||||
if (!operand_type) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3539):
|
||||
// The only constraint on the types of OpBitcast's parameters is that
|
||||
// they must have the same number of bits. Consider improving the code
|
||||
// below to support this in full.
|
||||
if (const auto* vector = operand_type->AsVector()) {
|
||||
uint32_t component_type_id;
|
||||
if (const auto* int_type = vector->element_type()->AsInteger()) {
|
||||
component_type_id =
|
||||
fuzzerutil::MaybeGetFloatType(ir_context, int_type->width());
|
||||
} else if (const auto* float_type = vector->element_type()->AsFloat()) {
|
||||
component_type_id = fuzzerutil::MaybeGetIntegerType(
|
||||
ir_context, float_type->width(), true);
|
||||
if (component_type_id == 0 ||
|
||||
fuzzerutil::MaybeGetVectorType(ir_context, component_type_id,
|
||||
vector->element_count()) == 0) {
|
||||
component_type_id = fuzzerutil::MaybeGetIntegerType(
|
||||
ir_context, float_type->width(), false);
|
||||
}
|
||||
} else {
|
||||
assert(false && "Only vectors of numerical components are supported");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (component_type_id == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return fuzzerutil::MaybeGetVectorType(ir_context, component_type_id,
|
||||
vector->element_count());
|
||||
} else if (const auto* int_type = operand_type->AsInteger()) {
|
||||
return fuzzerutil::MaybeGetFloatType(ir_context, int_type->width());
|
||||
} else if (const auto* float_type = operand_type->AsFloat()) {
|
||||
if (auto existing_id = fuzzerutil::MaybeGetIntegerType(
|
||||
ir_context, float_type->width(), true)) {
|
||||
return existing_id;
|
||||
}
|
||||
|
||||
return fuzzerutil::MaybeGetIntegerType(ir_context, float_type->width(),
|
||||
false);
|
||||
} else {
|
||||
assert(false &&
|
||||
"Operand is not a scalar or a vector of numerical type");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
case SpvOpIAdd:
|
||||
case SpvOpISub: {
|
||||
if (message_.in_operand_id().size() != 2) {
|
||||
if (message_.in_operand_id_size() != 2) {
|
||||
return 0;
|
||||
}
|
||||
uint32_t first_operand_width = 0;
|
||||
@@ -175,7 +273,6 @@ uint32_t TransformationEquationInstruction::MaybeGetResultType(
|
||||
}
|
||||
return operand_inst->type_id();
|
||||
}
|
||||
|
||||
default:
|
||||
assert(false && "Inappropriate opcode for equation instruction.");
|
||||
return 0;
|
||||
|
||||
@@ -63,11 +63,9 @@ class TransformationEquationInstruction : public Transformation {
|
||||
protobufs::Transformation ToMessage() const override;
|
||||
|
||||
private:
|
||||
// A helper that, in one fell swoop, checks that |message_.opcode| and the ids
|
||||
// in |message_.in_operand_id| are compatible, and that the module contains
|
||||
// an appropriate result type id. If all is well, the result type id is
|
||||
// returned. Otherwise, 0 is returned.
|
||||
uint32_t MaybeGetResultType(opt::IRContext* ir_context) const;
|
||||
// Returns type id for the equation instruction. Returns 0 if result type does
|
||||
// not exist.
|
||||
uint32_t MaybeGetResultTypeId(opt::IRContext* ir_context) const;
|
||||
|
||||
protobufs::TransformationEquationInstruction message_;
|
||||
};
|
||||
|
||||
@@ -94,6 +94,18 @@ bool TransformationInvertComparisonOperator::IsInversionSupported(
|
||||
case SpvOpULessThanEqual:
|
||||
case SpvOpIEqual:
|
||||
case SpvOpINotEqual:
|
||||
case SpvOpFOrdEqual:
|
||||
case SpvOpFUnordEqual:
|
||||
case SpvOpFOrdNotEqual:
|
||||
case SpvOpFUnordNotEqual:
|
||||
case SpvOpFOrdLessThan:
|
||||
case SpvOpFUnordLessThan:
|
||||
case SpvOpFOrdLessThanEqual:
|
||||
case SpvOpFUnordLessThanEqual:
|
||||
case SpvOpFOrdGreaterThan:
|
||||
case SpvOpFUnordGreaterThan:
|
||||
case SpvOpFOrdGreaterThanEqual:
|
||||
case SpvOpFUnordGreaterThanEqual:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
@@ -124,6 +136,30 @@ SpvOp TransformationInvertComparisonOperator::InvertOpcode(SpvOp opcode) {
|
||||
return SpvOpINotEqual;
|
||||
case SpvOpINotEqual:
|
||||
return SpvOpIEqual;
|
||||
case SpvOpFOrdEqual:
|
||||
return SpvOpFUnordNotEqual;
|
||||
case SpvOpFUnordEqual:
|
||||
return SpvOpFOrdNotEqual;
|
||||
case SpvOpFOrdNotEqual:
|
||||
return SpvOpFUnordEqual;
|
||||
case SpvOpFUnordNotEqual:
|
||||
return SpvOpFOrdEqual;
|
||||
case SpvOpFOrdLessThan:
|
||||
return SpvOpFUnordGreaterThanEqual;
|
||||
case SpvOpFUnordLessThan:
|
||||
return SpvOpFOrdGreaterThanEqual;
|
||||
case SpvOpFOrdLessThanEqual:
|
||||
return SpvOpFUnordGreaterThan;
|
||||
case SpvOpFUnordLessThanEqual:
|
||||
return SpvOpFOrdGreaterThan;
|
||||
case SpvOpFOrdGreaterThan:
|
||||
return SpvOpFUnordLessThanEqual;
|
||||
case SpvOpFUnordGreaterThan:
|
||||
return SpvOpFOrdLessThanEqual;
|
||||
case SpvOpFOrdGreaterThanEqual:
|
||||
return SpvOpFUnordLessThan;
|
||||
case SpvOpFUnordGreaterThanEqual:
|
||||
return SpvOpFOrdLessThan;
|
||||
default:
|
||||
// The program will fail in the debug mode because of the assertion
|
||||
// at the beginning of the function.
|
||||
|
||||
@@ -127,22 +127,20 @@ void TransformationPushIdThroughVariable::Apply(
|
||||
message_.initializer_id());
|
||||
}
|
||||
|
||||
// Stores value id to variable id.
|
||||
FindInstruction(message_.instruction_descriptor(), ir_context)
|
||||
->InsertBefore(MakeUnique<opt::Instruction>(
|
||||
ir_context, SpvOpStore, 0, 0,
|
||||
opt::Instruction::OperandList(
|
||||
{{SPV_OPERAND_TYPE_ID, {message_.variable_id()}},
|
||||
{SPV_OPERAND_TYPE_ID, {message_.value_id()}}})));
|
||||
|
||||
// Loads variable id to value synonym id.
|
||||
// First, insert the OpLoad instruction before |instruction_descriptor| and
|
||||
// then insert the OpStore instruction before the OpLoad instruction.
|
||||
fuzzerutil::UpdateModuleIdBound(ir_context, message_.value_synonym_id());
|
||||
FindInstruction(message_.instruction_descriptor(), ir_context)
|
||||
->InsertBefore(MakeUnique<opt::Instruction>(
|
||||
ir_context, SpvOpLoad, value_instruction->type_id(),
|
||||
message_.value_synonym_id(),
|
||||
opt::Instruction::OperandList(
|
||||
{{SPV_OPERAND_TYPE_ID, {message_.variable_id()}}})));
|
||||
{{SPV_OPERAND_TYPE_ID, {message_.variable_id()}}})))
|
||||
->InsertBefore(MakeUnique<opt::Instruction>(
|
||||
ir_context, SpvOpStore, 0, 0,
|
||||
opt::Instruction::OperandList(
|
||||
{{SPV_OPERAND_TYPE_ID, {message_.variable_id()}},
|
||||
{SPV_OPERAND_TYPE_ID, {message_.value_id()}}})));
|
||||
|
||||
ir_context->InvalidateAnalysesExceptFor(opt::IRContext::kAnalysisNone);
|
||||
|
||||
|
||||
@@ -243,22 +243,15 @@ bool TransformationReplaceBooleanConstantWithConstantBinary::IsApplicable(
|
||||
return false;
|
||||
}
|
||||
|
||||
switch (instruction->opcode()) {
|
||||
case SpvOpPhi:
|
||||
// The instruction must not be an OpPhi, as we cannot insert a binary
|
||||
// operator instruction before an OpPhi.
|
||||
// TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/2902): there is
|
||||
// scope for being less conservative.
|
||||
return false;
|
||||
case SpvOpVariable:
|
||||
// The instruction must not be an OpVariable, because (a) we cannot insert
|
||||
// a binary operator before an OpVariable, but in any case (b) the
|
||||
// constant we would be replacing is the initializer constant of the
|
||||
// OpVariable, and this cannot be the result of a binary operation.
|
||||
return false;
|
||||
default:
|
||||
return true;
|
||||
// The instruction must not be an OpVariable, because (a) we cannot insert
|
||||
// a binary operator before an OpVariable, but in any case (b) the
|
||||
// constant we would be replacing is the initializer constant of the
|
||||
// OpVariable, and this cannot be the result of a binary operation.
|
||||
if (instruction->opcode() == SpvOpVariable) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void TransformationReplaceBooleanConstantWithConstantBinary::Apply(
|
||||
@@ -281,11 +274,22 @@ TransformationReplaceBooleanConstantWithConstantBinary::ApplyWithResult(
|
||||
opt::Instruction* result = binary_instruction.get();
|
||||
auto instruction_containing_constant_use =
|
||||
FindInstructionContainingUse(message_.id_use_descriptor(), ir_context);
|
||||
auto instruction_before_which_to_insert = instruction_containing_constant_use;
|
||||
|
||||
// If |instruction_before_which_to_insert| is an OpPhi instruction,
|
||||
// then |binary_instruction| will be inserted into the parent block associated
|
||||
// with the OpPhi variable operand.
|
||||
if (instruction_containing_constant_use->opcode() == SpvOpPhi) {
|
||||
instruction_before_which_to_insert =
|
||||
ir_context->cfg()
|
||||
->block(instruction_containing_constant_use->GetSingleWordInOperand(
|
||||
message_.id_use_descriptor().in_operand_index() + 1))
|
||||
->terminator();
|
||||
}
|
||||
|
||||
// We want to insert the new instruction before the instruction that contains
|
||||
// the use of the boolean, but we need to go backwards one more instruction if
|
||||
// the using instruction is preceded by a merge instruction.
|
||||
auto instruction_before_which_to_insert = instruction_containing_constant_use;
|
||||
{
|
||||
opt::Instruction* previous_node =
|
||||
instruction_before_which_to_insert->PreviousNode();
|
||||
@@ -294,6 +298,7 @@ TransformationReplaceBooleanConstantWithConstantBinary::ApplyWithResult(
|
||||
instruction_before_which_to_insert = previous_node;
|
||||
}
|
||||
}
|
||||
|
||||
instruction_before_which_to_insert->InsertBefore(
|
||||
std::move(binary_instruction));
|
||||
instruction_containing_constant_use->SetInOperand(
|
||||
|
||||
@@ -49,6 +49,7 @@ bool TransformationReplaceLinearAlgebraInstruction::IsApplicable(
|
||||
instruction->opcode() != SpvOpMatrixTimesScalar &&
|
||||
instruction->opcode() != SpvOpVectorTimesMatrix &&
|
||||
instruction->opcode() != SpvOpMatrixTimesVector &&
|
||||
instruction->opcode() != SpvOpMatrixTimesMatrix &&
|
||||
instruction->opcode() != SpvOpDot) {
|
||||
return false;
|
||||
}
|
||||
@@ -88,6 +89,9 @@ void TransformationReplaceLinearAlgebraInstruction::Apply(
|
||||
case SpvOpMatrixTimesVector:
|
||||
ReplaceOpMatrixTimesVector(ir_context, linear_algebra_instruction);
|
||||
break;
|
||||
case SpvOpMatrixTimesMatrix:
|
||||
ReplaceOpMatrixTimesMatrix(ir_context, linear_algebra_instruction);
|
||||
break;
|
||||
case SpvOpDot:
|
||||
ReplaceOpDot(ir_context, linear_algebra_instruction);
|
||||
break;
|
||||
@@ -178,6 +182,37 @@ uint32_t TransformationReplaceLinearAlgebraInstruction::GetRequiredFreshIdCount(
|
||||
return 3 * matrix_column_count * matrix_row_count +
|
||||
2 * matrix_column_count - matrix_row_count;
|
||||
}
|
||||
case SpvOpMatrixTimesMatrix: {
|
||||
// For each matrix 2 column, 1 OpCompositeExtract, 1 OpCompositeConstruct,
|
||||
// |3 * matrix_1_row_count * matrix_1_column_count| OpCompositeExtract,
|
||||
// |matrix_1_row_count * matrix_1_column_count| OpFMul,
|
||||
// |matrix_1_row_count * (matrix_1_column_count - 1)| OpFAdd instructions
|
||||
// will be inserted.
|
||||
auto matrix_1_instruction = ir_context->get_def_use_mgr()->GetDef(
|
||||
instruction->GetSingleWordInOperand(0));
|
||||
uint32_t matrix_1_column_count =
|
||||
ir_context->get_type_mgr()
|
||||
->GetType(matrix_1_instruction->type_id())
|
||||
->AsMatrix()
|
||||
->element_count();
|
||||
uint32_t matrix_1_row_count =
|
||||
ir_context->get_type_mgr()
|
||||
->GetType(matrix_1_instruction->type_id())
|
||||
->AsMatrix()
|
||||
->element_type()
|
||||
->AsVector()
|
||||
->element_count();
|
||||
|
||||
auto matrix_2_instruction = ir_context->get_def_use_mgr()->GetDef(
|
||||
instruction->GetSingleWordInOperand(1));
|
||||
uint32_t matrix_2_column_count =
|
||||
ir_context->get_type_mgr()
|
||||
->GetType(matrix_2_instruction->type_id())
|
||||
->AsMatrix()
|
||||
->element_count();
|
||||
return matrix_2_column_count *
|
||||
(2 + matrix_1_row_count * (5 * matrix_1_column_count - 1));
|
||||
}
|
||||
case SpvOpDot:
|
||||
// For each pair of vector components, 2 OpCompositeExtract and 1 OpFMul
|
||||
// will be inserted. The first two OpFMul instructions will result the
|
||||
@@ -560,6 +595,151 @@ void TransformationReplaceLinearAlgebraInstruction::ReplaceOpMatrixTimesVector(
|
||||
ir_context, message_.fresh_ids(message_.fresh_ids().size() - 1));
|
||||
}
|
||||
|
||||
void TransformationReplaceLinearAlgebraInstruction::ReplaceOpMatrixTimesMatrix(
|
||||
opt::IRContext* ir_context,
|
||||
opt::Instruction* linear_algebra_instruction) const {
|
||||
// Gets matrix 1 information.
|
||||
auto matrix_1_instruction = ir_context->get_def_use_mgr()->GetDef(
|
||||
linear_algebra_instruction->GetSingleWordInOperand(0));
|
||||
uint32_t matrix_1_column_count =
|
||||
ir_context->get_type_mgr()
|
||||
->GetType(matrix_1_instruction->type_id())
|
||||
->AsMatrix()
|
||||
->element_count();
|
||||
auto matrix_1_column_type = ir_context->get_type_mgr()
|
||||
->GetType(matrix_1_instruction->type_id())
|
||||
->AsMatrix()
|
||||
->element_type();
|
||||
auto matrix_1_column_component_type =
|
||||
matrix_1_column_type->AsVector()->element_type();
|
||||
uint32_t matrix_1_row_count =
|
||||
matrix_1_column_type->AsVector()->element_count();
|
||||
|
||||
// Gets matrix 2 information.
|
||||
auto matrix_2_instruction = ir_context->get_def_use_mgr()->GetDef(
|
||||
linear_algebra_instruction->GetSingleWordInOperand(1));
|
||||
uint32_t matrix_2_column_count =
|
||||
ir_context->get_type_mgr()
|
||||
->GetType(matrix_2_instruction->type_id())
|
||||
->AsMatrix()
|
||||
->element_count();
|
||||
auto matrix_2_column_type = ir_context->get_type_mgr()
|
||||
->GetType(matrix_2_instruction->type_id())
|
||||
->AsMatrix()
|
||||
->element_type();
|
||||
|
||||
uint32_t fresh_id_index = 0;
|
||||
std::vector<uint32_t> result_column_ids(matrix_2_column_count);
|
||||
for (uint32_t i = 0; i < matrix_2_column_count; i++) {
|
||||
// Extracts matrix 2 column.
|
||||
uint32_t matrix_2_column_id = message_.fresh_ids(fresh_id_index++);
|
||||
linear_algebra_instruction->InsertBefore(MakeUnique<opt::Instruction>(
|
||||
ir_context, SpvOpCompositeExtract,
|
||||
ir_context->get_type_mgr()->GetId(matrix_2_column_type),
|
||||
matrix_2_column_id,
|
||||
opt::Instruction::OperandList(
|
||||
{{SPV_OPERAND_TYPE_ID, {matrix_2_instruction->result_id()}},
|
||||
{SPV_OPERAND_TYPE_LITERAL_INTEGER, {i}}})));
|
||||
|
||||
std::vector<uint32_t> column_component_ids(matrix_1_row_count);
|
||||
for (uint32_t j = 0; j < matrix_1_row_count; j++) {
|
||||
std::vector<uint32_t> float_multiplication_ids(matrix_1_column_count);
|
||||
for (uint32_t k = 0; k < matrix_1_column_count; k++) {
|
||||
// Extracts matrix 1 column.
|
||||
uint32_t matrix_1_column_id = message_.fresh_ids(fresh_id_index++);
|
||||
linear_algebra_instruction->InsertBefore(MakeUnique<opt::Instruction>(
|
||||
ir_context, SpvOpCompositeExtract,
|
||||
ir_context->get_type_mgr()->GetId(matrix_1_column_type),
|
||||
matrix_1_column_id,
|
||||
opt::Instruction::OperandList(
|
||||
{{SPV_OPERAND_TYPE_ID, {matrix_1_instruction->result_id()}},
|
||||
{SPV_OPERAND_TYPE_LITERAL_INTEGER, {k}}})));
|
||||
|
||||
// Extracts matrix 1 column component.
|
||||
uint32_t matrix_1_column_component_id =
|
||||
message_.fresh_ids(fresh_id_index++);
|
||||
linear_algebra_instruction->InsertBefore(MakeUnique<opt::Instruction>(
|
||||
ir_context, SpvOpCompositeExtract,
|
||||
ir_context->get_type_mgr()->GetId(matrix_1_column_component_type),
|
||||
matrix_1_column_component_id,
|
||||
opt::Instruction::OperandList(
|
||||
{{SPV_OPERAND_TYPE_ID, {matrix_1_column_id}},
|
||||
{SPV_OPERAND_TYPE_LITERAL_INTEGER, {j}}})));
|
||||
|
||||
// Extracts matrix 2 column component.
|
||||
uint32_t matrix_2_column_component_id =
|
||||
message_.fresh_ids(fresh_id_index++);
|
||||
linear_algebra_instruction->InsertBefore(MakeUnique<opt::Instruction>(
|
||||
ir_context, SpvOpCompositeExtract,
|
||||
ir_context->get_type_mgr()->GetId(matrix_1_column_component_type),
|
||||
matrix_2_column_component_id,
|
||||
opt::Instruction::OperandList(
|
||||
{{SPV_OPERAND_TYPE_ID, {matrix_2_column_id}},
|
||||
{SPV_OPERAND_TYPE_LITERAL_INTEGER, {k}}})));
|
||||
|
||||
// Multiplies corresponding matrix 1 and matrix 2 column components.
|
||||
float_multiplication_ids[k] = message_.fresh_ids(fresh_id_index++);
|
||||
linear_algebra_instruction->InsertBefore(MakeUnique<opt::Instruction>(
|
||||
ir_context, SpvOpFMul,
|
||||
ir_context->get_type_mgr()->GetId(matrix_1_column_component_type),
|
||||
float_multiplication_ids[k],
|
||||
opt::Instruction::OperandList(
|
||||
{{SPV_OPERAND_TYPE_ID, {matrix_1_column_component_id}},
|
||||
{SPV_OPERAND_TYPE_ID, {matrix_2_column_component_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(matrix_1_column_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 k = 2; k < float_multiplication_ids.size(); k++) {
|
||||
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(matrix_1_column_component_type),
|
||||
float_add_id,
|
||||
opt::Instruction::OperandList(
|
||||
{{SPV_OPERAND_TYPE_ID, {float_multiplication_ids[k]}},
|
||||
{SPV_OPERAND_TYPE_ID, {float_add_ids[k - 2]}}})));
|
||||
}
|
||||
|
||||
column_component_ids[j] = float_add_ids.back();
|
||||
}
|
||||
|
||||
// Inserts the resulting matrix column.
|
||||
opt::Instruction::OperandList in_operands;
|
||||
for (auto& column_component_id : column_component_ids) {
|
||||
in_operands.push_back({SPV_OPERAND_TYPE_ID, {column_component_id}});
|
||||
}
|
||||
result_column_ids[i] = message_.fresh_ids(fresh_id_index++);
|
||||
linear_algebra_instruction->InsertBefore(MakeUnique<opt::Instruction>(
|
||||
ir_context, SpvOpCompositeConstruct,
|
||||
ir_context->get_type_mgr()->GetId(matrix_1_column_type),
|
||||
result_column_ids[i], opt::Instruction::OperandList(in_operands)));
|
||||
}
|
||||
|
||||
// The OpMatrixTimesMatrix instruction is changed to an OpCompositeConstruct
|
||||
// instruction.
|
||||
linear_algebra_instruction->SetOpcode(SpvOpCompositeConstruct);
|
||||
linear_algebra_instruction->SetInOperand(0, {result_column_ids[0]});
|
||||
linear_algebra_instruction->SetInOperand(1, {result_column_ids[1]});
|
||||
for (uint32_t i = 2; i < result_column_ids.size(); i++) {
|
||||
linear_algebra_instruction->AddOperand(
|
||||
{SPV_OPERAND_TYPE_ID, {result_column_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 {
|
||||
|
||||
@@ -68,6 +68,10 @@ class TransformationReplaceLinearAlgebraInstruction : public Transformation {
|
||||
void ReplaceOpMatrixTimesVector(opt::IRContext* ir_context,
|
||||
opt::Instruction* instruction) const;
|
||||
|
||||
// Replaces an OpMatrixTimesMatrix instruction.
|
||||
void ReplaceOpMatrixTimesMatrix(opt::IRContext* ir_context,
|
||||
opt::Instruction* instruction) const;
|
||||
|
||||
// Replaces an OpDot instruction.
|
||||
void ReplaceOpDot(opt::IRContext* ir_context,
|
||||
opt::Instruction* instruction) const;
|
||||
|
||||
@@ -76,7 +76,7 @@ bool TransformationReplaceParameterWithGlobal::IsApplicable(
|
||||
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)) {
|
||||
if (!IsParameterTypeSupported(*param_type)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -217,7 +217,7 @@ protobufs::Transformation TransformationReplaceParameterWithGlobal::ToMessage()
|
||||
return result;
|
||||
}
|
||||
|
||||
bool TransformationReplaceParameterWithGlobal::CanReplaceFunctionParameterType(
|
||||
bool TransformationReplaceParameterWithGlobal::IsParameterTypeSupported(
|
||||
const opt::analysis::Type& type) {
|
||||
// TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3403):
|
||||
// Think about other type instructions we can add here.
|
||||
@@ -230,12 +230,11 @@ bool TransformationReplaceParameterWithGlobal::CanReplaceFunctionParameterType(
|
||||
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);
|
||||
});
|
||||
return std::all_of(type.AsStruct()->element_types().begin(),
|
||||
type.AsStruct()->element_types().end(),
|
||||
[](const opt::analysis::Type* element_type) {
|
||||
return IsParameterTypeSupported(*element_type);
|
||||
});
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -55,7 +55,7 @@ class TransformationReplaceParameterWithGlobal : public Transformation {
|
||||
|
||||
// Returns true if the type of the parameter is supported by this
|
||||
// transformation.
|
||||
static bool CanReplaceFunctionParameterType(const opt::analysis::Type& type);
|
||||
static bool IsParameterTypeSupported(const opt::analysis::Type& type);
|
||||
|
||||
private:
|
||||
protobufs::TransformationReplaceParameterWithGlobal message_;
|
||||
|
||||
@@ -375,20 +375,17 @@ bool DebugInfoManager::IsDebugDeclared(uint32_t variable_id) {
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
auto dbg_decl_itr = var_id_to_dbg_decl_.find(variable_id);
|
||||
if (dbg_decl_itr != var_id_to_dbg_decl_.end()) {
|
||||
// We intentionally copy the list of DebugDeclare instructions because
|
||||
// context()->KillInst(dbg_decl) will update |var_id_to_dbg_decl_|. If we
|
||||
// directly use |dbg_decl_itr->second|, it accesses a dangling pointer.
|
||||
auto copy_dbg_decls = dbg_decl_itr->second;
|
||||
|
||||
for (auto* dbg_decl : copy_dbg_decls) {
|
||||
context()->KillInst(dbg_decl);
|
||||
}
|
||||
if (kill_inst)
|
||||
context()->KillInst(kill_inst);
|
||||
else
|
||||
done = true;
|
||||
var_id_to_dbg_decl_.erase(dbg_decl_itr);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -55,12 +55,25 @@ bool DominatorAnalysisBase::Dominates(Instruction* a, Instruction* b) const {
|
||||
return tree_.Dominates(bb_a, bb_b);
|
||||
}
|
||||
|
||||
Instruction* current_inst = a;
|
||||
while ((current_inst = current_inst->NextNode())) {
|
||||
if (current_inst == b) {
|
||||
const Instruction* current = a;
|
||||
const Instruction* other = b;
|
||||
|
||||
if (tree_.IsPostDominator()) {
|
||||
std::swap(current, other);
|
||||
}
|
||||
|
||||
// We handle OpLabel instructions explicitly since they are not stored in the
|
||||
// instruction list.
|
||||
if (current->opcode() == SpvOpLabel) {
|
||||
return true;
|
||||
}
|
||||
|
||||
while ((current = current->NextNode())) {
|
||||
if (current == other) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -174,7 +174,9 @@ void InstrumentPass::GenStageStreamWriteCode(uint32_t stage_idx,
|
||||
context()->GetBuiltinInputVarId(SpvBuiltInInstanceIndex),
|
||||
kInstVertOutInstanceIndex, base_offset_id, builder);
|
||||
} break;
|
||||
case SpvExecutionModelGLCompute: {
|
||||
case SpvExecutionModelGLCompute:
|
||||
case SpvExecutionModelTaskNV:
|
||||
case SpvExecutionModelMeshNV: {
|
||||
// Load and store GlobalInvocationId.
|
||||
uint32_t load_id = GenVarLoad(
|
||||
context()->GetBuiltinInputVarId(SpvBuiltInGlobalInvocationId),
|
||||
@@ -914,6 +916,7 @@ bool InstrumentPass::InstProcessEntryPointCallTree(InstProcessFunction& pfn) {
|
||||
stage != SpvExecutionModelGLCompute &&
|
||||
stage != SpvExecutionModelTessellationControl &&
|
||||
stage != SpvExecutionModelTessellationEvaluation &&
|
||||
stage != SpvExecutionModelTaskNV && stage != SpvExecutionModelMeshNV &&
|
||||
stage != SpvExecutionModelRayGenerationNV &&
|
||||
stage != SpvExecutionModelIntersectionNV &&
|
||||
stage != SpvExecutionModelAnyHitNV &&
|
||||
|
||||
@@ -22,6 +22,7 @@ const uint32_t kDefaultStepLimit = 250;
|
||||
spv_fuzzer_options_t::spv_fuzzer_options_t()
|
||||
: has_random_seed(false),
|
||||
random_seed(0),
|
||||
replay_range(0),
|
||||
replay_validation_enabled(false),
|
||||
shrinker_step_limit(kDefaultStepLimit),
|
||||
fuzzer_pass_validation_enabled(false) {}
|
||||
@@ -45,6 +46,11 @@ SPIRV_TOOLS_EXPORT void spvFuzzerOptionsSetRandomSeed(
|
||||
options->random_seed = seed;
|
||||
}
|
||||
|
||||
SPIRV_TOOLS_EXPORT void spvFuzzerOptionsSetReplayRange(
|
||||
spv_fuzzer_options options, int32_t replay_range) {
|
||||
options->replay_range = replay_range;
|
||||
}
|
||||
|
||||
SPIRV_TOOLS_EXPORT void spvFuzzerOptionsSetShrinkerStepLimit(
|
||||
spv_fuzzer_options options, uint32_t shrinker_step_limit) {
|
||||
options->shrinker_step_limit = shrinker_step_limit;
|
||||
|
||||
@@ -29,6 +29,9 @@ struct spv_fuzzer_options_t {
|
||||
bool has_random_seed;
|
||||
uint32_t random_seed;
|
||||
|
||||
// See spvFuzzerOptionsSetReplayRange.
|
||||
int32_t replay_range;
|
||||
|
||||
// See spvFuzzerOptionsEnableReplayValidation.
|
||||
bool replay_validation_enabled;
|
||||
|
||||
|
||||
@@ -58,15 +58,9 @@ void BasicBlock::RegisterSuccessors(
|
||||
for (auto& block : next_blocks) {
|
||||
block->predecessors_.push_back(this);
|
||||
successors_.push_back(block);
|
||||
if (block->reachable_ == false) block->set_reachable(reachable_);
|
||||
}
|
||||
}
|
||||
|
||||
void BasicBlock::RegisterBranchInstruction(SpvOp branch_instruction) {
|
||||
if (branch_instruction == SpvOpUnreachable) reachable_ = false;
|
||||
return;
|
||||
}
|
||||
|
||||
bool BasicBlock::dominates(const BasicBlock& other) const {
|
||||
return (this == &other) ||
|
||||
!(other.dom_end() ==
|
||||
|
||||
@@ -106,9 +106,6 @@ class BasicBlock {
|
||||
/// Returns the immedate post dominator of this basic block
|
||||
const BasicBlock* immediate_post_dominator() const;
|
||||
|
||||
/// Ends the block without a successor
|
||||
void RegisterBranchInstruction(SpvOp branch_instruction);
|
||||
|
||||
/// Returns the label instruction for the block, or nullptr if not set.
|
||||
const Instruction* label() const { return label_; }
|
||||
|
||||
|
||||
5
3rdparty/spirv-tools/source/val/function.cpp
vendored
5
3rdparty/spirv-tools/source/val/function.cpp
vendored
@@ -130,7 +130,6 @@ spv_result_t Function::RegisterBlock(uint32_t block_id, bool is_definition) {
|
||||
undefined_blocks_.erase(block_id);
|
||||
current_block_ = &inserted_block->second;
|
||||
ordered_blocks_.push_back(current_block_);
|
||||
if (IsFirstBlock(block_id)) current_block_->set_reachable(true);
|
||||
} else if (success) { // Block doesn't exsist but this is not a definition
|
||||
undefined_blocks_.insert(block_id);
|
||||
}
|
||||
@@ -138,8 +137,7 @@ spv_result_t Function::RegisterBlock(uint32_t block_id, bool is_definition) {
|
||||
return SPV_SUCCESS;
|
||||
}
|
||||
|
||||
void Function::RegisterBlockEnd(std::vector<uint32_t> next_list,
|
||||
SpvOp branch_instruction) {
|
||||
void Function::RegisterBlockEnd(std::vector<uint32_t> next_list) {
|
||||
assert(
|
||||
current_block_ &&
|
||||
"RegisterBlockEnd can only be called when parsing a binary in a block");
|
||||
@@ -174,7 +172,6 @@ void Function::RegisterBlockEnd(std::vector<uint32_t> next_list,
|
||||
}
|
||||
}
|
||||
|
||||
current_block_->RegisterBranchInstruction(branch_instruction);
|
||||
current_block_->RegisterSuccessors(next_blocks);
|
||||
current_block_ = nullptr;
|
||||
return;
|
||||
|
||||
4
3rdparty/spirv-tools/source/val/function.h
vendored
4
3rdparty/spirv-tools/source/val/function.h
vendored
@@ -97,9 +97,7 @@ class Function {
|
||||
/// Registers the end of the block
|
||||
///
|
||||
/// @param[in] successors_list A list of ids to the block's successors
|
||||
/// @param[in] branch_instruction the branch instruction that ended the block
|
||||
void RegisterBlockEnd(std::vector<uint32_t> successors_list,
|
||||
SpvOp branch_instruction);
|
||||
void RegisterBlockEnd(std::vector<uint32_t> successors_list);
|
||||
|
||||
/// Registers the end of the function. This is idempotent.
|
||||
void RegisterFunctionEnd();
|
||||
|
||||
4
3rdparty/spirv-tools/source/val/validate.cpp
vendored
4
3rdparty/spirv-tools/source/val/validate.cpp
vendored
@@ -368,6 +368,10 @@ spv_result_t ValidateBinaryUsingContextAndValidationState(
|
||||
// Catch undefined forward references before performing further checks.
|
||||
if (auto error = ValidateForwardDecls(*vstate)) return error;
|
||||
|
||||
// Calculate reachability after all the blocks are parsed, but early that it
|
||||
// can be relied on in subsequent pases.
|
||||
ReachabilityPass(*vstate);
|
||||
|
||||
// ID usage needs be handled in its own iteration of the instructions,
|
||||
// between the two others. It depends on the first loop to have been
|
||||
// finished, so that all instructions have been registered. And the following
|
||||
|
||||
3
3rdparty/spirv-tools/source/val/validate.h
vendored
3
3rdparty/spirv-tools/source/val/validate.h
vendored
@@ -197,6 +197,9 @@ spv_result_t FunctionPass(ValidationState_t& _, const Instruction* inst);
|
||||
/// Validates correctness of miscellaneous instructions.
|
||||
spv_result_t MiscPass(ValidationState_t& _, const Instruction* inst);
|
||||
|
||||
/// Calculates the reachability of basic blocks.
|
||||
void ReachabilityPass(ValidationState_t& _);
|
||||
|
||||
/// Validates execution limitations.
|
||||
///
|
||||
/// Verifies execution models are allowed for all functionality they contain.
|
||||
|
||||
31
3rdparty/spirv-tools/source/val/validate_cfg.cpp
vendored
31
3rdparty/spirv-tools/source/val/validate_cfg.cpp
vendored
@@ -1062,7 +1062,7 @@ spv_result_t CfgPass(ValidationState_t& _, const Instruction* inst) {
|
||||
uint32_t target = inst->GetOperandAs<uint32_t>(0);
|
||||
CFG_ASSERT(FirstBlockAssert, target);
|
||||
|
||||
_.current_function().RegisterBlockEnd({target}, opcode);
|
||||
_.current_function().RegisterBlockEnd({target});
|
||||
} break;
|
||||
case SpvOpBranchConditional: {
|
||||
uint32_t tlabel = inst->GetOperandAs<uint32_t>(1);
|
||||
@@ -1070,7 +1070,7 @@ spv_result_t CfgPass(ValidationState_t& _, const Instruction* inst) {
|
||||
CFG_ASSERT(FirstBlockAssert, tlabel);
|
||||
CFG_ASSERT(FirstBlockAssert, flabel);
|
||||
|
||||
_.current_function().RegisterBlockEnd({tlabel, flabel}, opcode);
|
||||
_.current_function().RegisterBlockEnd({tlabel, flabel});
|
||||
} break;
|
||||
|
||||
case SpvOpSwitch: {
|
||||
@@ -1080,7 +1080,7 @@ spv_result_t CfgPass(ValidationState_t& _, const Instruction* inst) {
|
||||
CFG_ASSERT(FirstBlockAssert, target);
|
||||
cases.push_back(target);
|
||||
}
|
||||
_.current_function().RegisterBlockEnd({cases}, opcode);
|
||||
_.current_function().RegisterBlockEnd({cases});
|
||||
} break;
|
||||
case SpvOpReturn: {
|
||||
const uint32_t return_type = _.current_function().GetResultTypeId();
|
||||
@@ -1090,13 +1090,13 @@ spv_result_t CfgPass(ValidationState_t& _, const Instruction* inst) {
|
||||
return _.diag(SPV_ERROR_INVALID_CFG, inst)
|
||||
<< "OpReturn can only be called from a function with void "
|
||||
<< "return type.";
|
||||
_.current_function().RegisterBlockEnd(std::vector<uint32_t>(), opcode);
|
||||
_.current_function().RegisterBlockEnd(std::vector<uint32_t>());
|
||||
break;
|
||||
}
|
||||
case SpvOpKill:
|
||||
case SpvOpReturnValue:
|
||||
case SpvOpUnreachable:
|
||||
_.current_function().RegisterBlockEnd(std::vector<uint32_t>(), opcode);
|
||||
_.current_function().RegisterBlockEnd(std::vector<uint32_t>());
|
||||
if (opcode == SpvOpKill) {
|
||||
_.current_function().RegisterExecutionModelLimitation(
|
||||
SpvExecutionModelFragment,
|
||||
@@ -1109,6 +1109,27 @@ spv_result_t CfgPass(ValidationState_t& _, const Instruction* inst) {
|
||||
return SPV_SUCCESS;
|
||||
}
|
||||
|
||||
void ReachabilityPass(ValidationState_t& _) {
|
||||
for (auto& f : _.functions()) {
|
||||
std::vector<BasicBlock*> stack;
|
||||
auto entry = f.first_block();
|
||||
// Skip function declarations.
|
||||
if (entry) stack.push_back(entry);
|
||||
|
||||
while (!stack.empty()) {
|
||||
auto block = stack.back();
|
||||
stack.pop_back();
|
||||
|
||||
if (block->reachable()) continue;
|
||||
|
||||
block->set_reachable(true);
|
||||
for (auto succ : *block->successors()) {
|
||||
stack.push_back(succ);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
spv_result_t ControlFlowPass(ValidationState_t& _, const Instruction* inst) {
|
||||
switch (inst->opcode()) {
|
||||
case SpvOpPhi:
|
||||
|
||||
@@ -208,8 +208,8 @@ uint32_t NumConsumedComponents(ValidationState_t& _, const Instruction* type) {
|
||||
// 4 * location + component.
|
||||
spv_result_t GetLocationsForVariable(
|
||||
ValidationState_t& _, const Instruction* entry_point,
|
||||
const Instruction* variable, std::vector<bool>* locations,
|
||||
std::vector<bool>* output_index1_locations) {
|
||||
const Instruction* variable, std::unordered_set<uint32_t>* locations,
|
||||
std::unordered_set<uint32_t>* output_index1_locations) {
|
||||
const bool is_fragment = entry_point->GetOperandAs<SpvExecutionModel>(0) ==
|
||||
SpvExecutionModelFragment;
|
||||
const bool is_output =
|
||||
@@ -356,17 +356,13 @@ spv_result_t GetLocationsForVariable(
|
||||
auto locs = locations;
|
||||
if (has_index && index == 1) locs = output_index1_locations;
|
||||
|
||||
if (end > locs->size()) {
|
||||
locs->resize(end, false);
|
||||
}
|
||||
for (uint32_t i = start; i < end; ++i) {
|
||||
if (locs->at(i)) {
|
||||
if (!locs->insert(i).second) {
|
||||
return _.diag(SPV_ERROR_INVALID_DATA, entry_point)
|
||||
<< "Entry-point has conflicting " << storage_class
|
||||
<< " location assignment at location " << i / 4
|
||||
<< ", component " << i % 4;
|
||||
}
|
||||
(*locs)[i] = true;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@@ -425,17 +421,13 @@ spv_result_t GetLocationsForVariable(
|
||||
start += component;
|
||||
end = location * 4 + component + num_components;
|
||||
}
|
||||
if (end > locations->size()) {
|
||||
locations->resize(end, false);
|
||||
}
|
||||
for (uint32_t l = start; l < end; ++l) {
|
||||
if (locations->at(l)) {
|
||||
if (!locations->insert(l).second) {
|
||||
return _.diag(SPV_ERROR_INVALID_DATA, entry_point)
|
||||
<< "Entry-point has conflicting " << storage_class
|
||||
<< " location assignment at location " << l / 4
|
||||
<< ", component " << l % 4;
|
||||
}
|
||||
(*locations)[l] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -445,10 +437,10 @@ spv_result_t GetLocationsForVariable(
|
||||
|
||||
spv_result_t ValidateLocations(ValidationState_t& _,
|
||||
const Instruction* entry_point) {
|
||||
// Reserve space for 16 locations with 4 components each.
|
||||
std::vector<bool> input_locations(16 * 4, false);
|
||||
std::vector<bool> output_locations_index0(16 * 4, false);
|
||||
std::vector<bool> output_locations_index1(16 * 4, false);
|
||||
// Locations are stored as a combined location and component values.
|
||||
std::unordered_set<uint32_t> input_locations;
|
||||
std::unordered_set<uint32_t> output_locations_index0;
|
||||
std::unordered_set<uint32_t> output_locations_index1;
|
||||
for (uint32_t i = 3; i < entry_point->operands().size(); ++i) {
|
||||
auto interface_id = entry_point->GetOperandAs<uint32_t>(i);
|
||||
auto interface_var = _.FindDef(interface_id);
|
||||
|
||||
@@ -501,10 +501,6 @@ spv_result_t ValidateMemoryModel(ValidationState_t& _,
|
||||
return _.diag(SPV_ERROR_INVALID_DATA, inst)
|
||||
<< "Addressing model must be Logical for WebGPU environment.";
|
||||
}
|
||||
if (_.memory_model() != SpvMemoryModelVulkanKHR) {
|
||||
return _.diag(SPV_ERROR_INVALID_DATA, inst)
|
||||
<< "Memory model must be VulkanKHR for WebGPU environment.";
|
||||
}
|
||||
}
|
||||
|
||||
if (spvIsOpenCLEnv(_.context()->target_env)) {
|
||||
|
||||
Reference in New Issue
Block a user