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:
@@ -145,7 +145,7 @@
|
||||
|
||||
|
||||
<!-- SECTION: SPIR-V FP Fast Math Mode Bit Reservations -->
|
||||
<!-- Reserve ranges of bits in the “FP Fast Math Mode” bitfield.
|
||||
<!-- Reserve ranges of bits in the "FP Fast Math Mode" bitfield.
|
||||
Each vendor determines the use of values in their own ranges.
|
||||
Vendors are not required to disclose those uses. If the use of a
|
||||
value is included in an extension that is adopted by a Khronos
|
||||
|
||||
@@ -1 +1 @@
|
||||
"v2020.4-dev", "SPIRV-Tools v2020.4-dev 685eeed2529a1671c875d204406decacdff43cb3"
|
||||
"v2020.4-dev", "SPIRV-Tools v2020.4-dev 0dd31c89ea3db9b4acd6c39bb6f31cc30adf7dc6"
|
||||
|
||||
12
3rdparty/spirv-tools/source/fuzz/CMakeLists.txt
vendored
12
3rdparty/spirv-tools/source/fuzz/CMakeLists.txt
vendored
@@ -48,6 +48,7 @@ if(SPIRV_BUILD_FUZZER)
|
||||
fuzzer_pass_add_loads.h
|
||||
fuzzer_pass_add_local_variables.h
|
||||
fuzzer_pass_add_no_contraction_decorations.h
|
||||
fuzzer_pass_add_parameters.h
|
||||
fuzzer_pass_add_stores.h
|
||||
fuzzer_pass_add_vector_shuffle_instructions.h
|
||||
fuzzer_pass_adjust_branch_weights.h
|
||||
@@ -64,10 +65,12 @@ if(SPIRV_BUILD_FUZZER)
|
||||
fuzzer_pass_outline_functions.h
|
||||
fuzzer_pass_permute_blocks.h
|
||||
fuzzer_pass_permute_function_parameters.h
|
||||
fuzzer_pass_permute_phi_operands.h
|
||||
fuzzer_pass_push_ids_through_variables.h
|
||||
fuzzer_pass_replace_linear_algebra_instructions.h
|
||||
fuzzer_pass_split_blocks.h
|
||||
fuzzer_pass_swap_commutable_operands.h
|
||||
fuzzer_pass_swap_conditional_branch_operands.h
|
||||
fuzzer_pass_toggle_access_chain_instruction.h
|
||||
fuzzer_util.h
|
||||
id_use_descriptor.h
|
||||
@@ -92,6 +95,7 @@ if(SPIRV_BUILD_FUZZER)
|
||||
transformation_add_global_variable.h
|
||||
transformation_add_local_variable.h
|
||||
transformation_add_no_contraction_decoration.h
|
||||
transformation_add_parameters.h
|
||||
transformation_add_spec_constant_op.h
|
||||
transformation_add_type_array.h
|
||||
transformation_add_type_boolean.h
|
||||
@@ -115,6 +119,7 @@ if(SPIRV_BUILD_FUZZER)
|
||||
transformation_move_block_down.h
|
||||
transformation_outline_function.h
|
||||
transformation_permute_function_parameters.h
|
||||
transformation_permute_phi_operands.h
|
||||
transformation_push_id_through_variable.h
|
||||
transformation_replace_boolean_constant_with_constant_binary.h
|
||||
transformation_replace_constant_with_uniform.h
|
||||
@@ -127,6 +132,7 @@ if(SPIRV_BUILD_FUZZER)
|
||||
transformation_split_block.h
|
||||
transformation_store.h
|
||||
transformation_swap_commutable_operands.h
|
||||
transformation_swap_conditional_branch_operands.h
|
||||
transformation_toggle_access_chain_instruction.h
|
||||
transformation_vector_shuffle.h
|
||||
uniform_buffer_element_descriptor.h
|
||||
@@ -150,6 +156,7 @@ if(SPIRV_BUILD_FUZZER)
|
||||
fuzzer_pass_add_loads.cpp
|
||||
fuzzer_pass_add_local_variables.cpp
|
||||
fuzzer_pass_add_no_contraction_decorations.cpp
|
||||
fuzzer_pass_add_parameters.cpp
|
||||
fuzzer_pass_add_stores.cpp
|
||||
fuzzer_pass_add_vector_shuffle_instructions.cpp
|
||||
fuzzer_pass_adjust_branch_weights.cpp
|
||||
@@ -166,10 +173,12 @@ if(SPIRV_BUILD_FUZZER)
|
||||
fuzzer_pass_outline_functions.cpp
|
||||
fuzzer_pass_permute_blocks.cpp
|
||||
fuzzer_pass_permute_function_parameters.cpp
|
||||
fuzzer_pass_permute_phi_operands.cpp
|
||||
fuzzer_pass_push_ids_through_variables.cpp
|
||||
fuzzer_pass_replace_linear_algebra_instructions.cpp
|
||||
fuzzer_pass_split_blocks.cpp
|
||||
fuzzer_pass_swap_commutable_operands.cpp
|
||||
fuzzer_pass_swap_conditional_branch_operands.cpp
|
||||
fuzzer_pass_toggle_access_chain_instruction.cpp
|
||||
fuzzer_util.cpp
|
||||
id_use_descriptor.cpp
|
||||
@@ -193,6 +202,7 @@ if(SPIRV_BUILD_FUZZER)
|
||||
transformation_add_global_variable.cpp
|
||||
transformation_add_local_variable.cpp
|
||||
transformation_add_no_contraction_decoration.cpp
|
||||
transformation_add_parameters.cpp
|
||||
transformation_add_spec_constant_op.cpp
|
||||
transformation_add_type_array.cpp
|
||||
transformation_add_type_boolean.cpp
|
||||
@@ -216,6 +226,7 @@ if(SPIRV_BUILD_FUZZER)
|
||||
transformation_move_block_down.cpp
|
||||
transformation_outline_function.cpp
|
||||
transformation_permute_function_parameters.cpp
|
||||
transformation_permute_phi_operands.cpp
|
||||
transformation_push_id_through_variable.cpp
|
||||
transformation_replace_boolean_constant_with_constant_binary.cpp
|
||||
transformation_replace_constant_with_uniform.cpp
|
||||
@@ -228,6 +239,7 @@ if(SPIRV_BUILD_FUZZER)
|
||||
transformation_split_block.cpp
|
||||
transformation_store.cpp
|
||||
transformation_swap_commutable_operands.cpp
|
||||
transformation_swap_conditional_branch_operands.cpp
|
||||
transformation_toggle_access_chain_instruction.cpp
|
||||
transformation_vector_shuffle.cpp
|
||||
uniform_buffer_element_descriptor.cpp
|
||||
|
||||
12
3rdparty/spirv-tools/source/fuzz/fuzzer.cpp
vendored
12
3rdparty/spirv-tools/source/fuzz/fuzzer.cpp
vendored
@@ -32,6 +32,7 @@
|
||||
#include "source/fuzz/fuzzer_pass_add_loads.h"
|
||||
#include "source/fuzz/fuzzer_pass_add_local_variables.h"
|
||||
#include "source/fuzz/fuzzer_pass_add_no_contraction_decorations.h"
|
||||
#include "source/fuzz/fuzzer_pass_add_parameters.h"
|
||||
#include "source/fuzz/fuzzer_pass_add_stores.h"
|
||||
#include "source/fuzz/fuzzer_pass_add_vector_shuffle_instructions.h"
|
||||
#include "source/fuzz/fuzzer_pass_adjust_branch_weights.h"
|
||||
@@ -47,10 +48,12 @@
|
||||
#include "source/fuzz/fuzzer_pass_outline_functions.h"
|
||||
#include "source/fuzz/fuzzer_pass_permute_blocks.h"
|
||||
#include "source/fuzz/fuzzer_pass_permute_function_parameters.h"
|
||||
#include "source/fuzz/fuzzer_pass_permute_phi_operands.h"
|
||||
#include "source/fuzz/fuzzer_pass_push_ids_through_variables.h"
|
||||
#include "source/fuzz/fuzzer_pass_replace_linear_algebra_instructions.h"
|
||||
#include "source/fuzz/fuzzer_pass_split_blocks.h"
|
||||
#include "source/fuzz/fuzzer_pass_swap_commutable_operands.h"
|
||||
#include "source/fuzz/fuzzer_pass_swap_conditional_branch_operands.h"
|
||||
#include "source/fuzz/fuzzer_pass_toggle_access_chain_instruction.h"
|
||||
#include "source/fuzz/protobufs/spirvfuzz_protobufs.h"
|
||||
#include "source/fuzz/pseudo_random_generator.h"
|
||||
@@ -222,6 +225,9 @@ Fuzzer::FuzzerResultStatus Fuzzer::Run(
|
||||
MaybeAddPass<FuzzerPassAddLocalVariables>(
|
||||
&passes, ir_context.get(), &transformation_context, &fuzzer_context,
|
||||
transformation_sequence_out);
|
||||
MaybeAddPass<FuzzerPassAddParameters>(
|
||||
&passes, ir_context.get(), &transformation_context, &fuzzer_context,
|
||||
transformation_sequence_out);
|
||||
MaybeAddPass<FuzzerPassAddStores>(&passes, ir_context.get(),
|
||||
&transformation_context, &fuzzer_context,
|
||||
transformation_sequence_out);
|
||||
@@ -264,6 +270,9 @@ Fuzzer::FuzzerResultStatus Fuzzer::Run(
|
||||
MaybeAddPass<FuzzerPassSplitBlocks>(
|
||||
&passes, ir_context.get(), &transformation_context, &fuzzer_context,
|
||||
transformation_sequence_out);
|
||||
MaybeAddPass<FuzzerPassSwapBranchConditionalOperands>(
|
||||
&passes, ir_context.get(), &transformation_context, &fuzzer_context,
|
||||
transformation_sequence_out);
|
||||
}
|
||||
|
||||
bool is_first = true;
|
||||
@@ -301,6 +310,9 @@ Fuzzer::FuzzerResultStatus Fuzzer::Run(
|
||||
MaybeAddPass<FuzzerPassAddNoContractionDecorations>(
|
||||
&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);
|
||||
MaybeAddPass<FuzzerPassSwapCommutableOperands>(
|
||||
&final_passes, ir_context.get(), &transformation_context, &fuzzer_context,
|
||||
transformation_sequence_out);
|
||||
|
||||
@@ -38,6 +38,7 @@ const std::pair<uint32_t, uint32_t> kChanceOfAddingLocalVariable = {20, 90};
|
||||
const std::pair<uint32_t, uint32_t> kChanceOfAddingMatrixType = {20, 70};
|
||||
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> kChanceOfAddingVectorType = {20, 70};
|
||||
const std::pair<uint32_t, uint32_t> kChanceOfAddingVectorShuffle = {20, 70};
|
||||
@@ -63,11 +64,14 @@ const std::pair<uint32_t, uint32_t> kChanceOfMovingBlockDown = {20, 50};
|
||||
const std::pair<uint32_t, uint32_t> kChanceOfObfuscatingConstant = {10, 90};
|
||||
const std::pair<uint32_t, uint32_t> kChanceOfOutliningFunction = {10, 90};
|
||||
const std::pair<uint32_t, uint32_t> kChanceOfPermutingParameters = {30, 90};
|
||||
const std::pair<uint32_t, uint32_t> kChanceOfPermutingPhiOperands = {30, 90};
|
||||
const std::pair<uint32_t, uint32_t> kChanceOfPushingIdThroughVariable = {5, 50};
|
||||
const std::pair<uint32_t, uint32_t> kChanceOfReplacingIdWithSynonym = {10, 90};
|
||||
const std::pair<uint32_t, uint32_t>
|
||||
kChanceOfReplacingLinearAlgebraInstructions = {10, 90};
|
||||
const std::pair<uint32_t, uint32_t> kChanceOfSplittingBlock = {40, 95};
|
||||
const std::pair<uint32_t, uint32_t> kChanceOfSwappingConditionalBranchOperands =
|
||||
{10, 70};
|
||||
const std::pair<uint32_t, uint32_t> kChanceOfTogglingAccessChainInstruction = {
|
||||
20, 90};
|
||||
|
||||
@@ -78,6 +82,10 @@ const uint32_t kDefaultMaxLoopControlPartialCount = 100;
|
||||
const uint32_t kDefaultMaxLoopControlPeelCount = 100;
|
||||
const uint32_t kDefaultMaxLoopLimit = 20;
|
||||
const uint32_t kDefaultMaxNewArraySizeLimit = 100;
|
||||
// TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3424):
|
||||
// think whether there is a better limit on the maximum number of parameters.
|
||||
const uint32_t kDefaultMaxNumberOfFunctionParameters = 128;
|
||||
const uint32_t kDefaultMaxNumberOfNewParameters = 15;
|
||||
|
||||
// Default functions for controlling how deep to go during recursive
|
||||
// generation/transformation. Keep them in alphabetical order.
|
||||
@@ -101,6 +109,8 @@ FuzzerContext::FuzzerContext(RandomGenerator* random_generator,
|
||||
max_loop_control_peel_count_(kDefaultMaxLoopControlPeelCount),
|
||||
max_loop_limit_(kDefaultMaxLoopLimit),
|
||||
max_new_array_size_limit_(kDefaultMaxNewArraySizeLimit),
|
||||
max_number_of_function_parameters_(kDefaultMaxNumberOfFunctionParameters),
|
||||
max_number_of_new_parameters_(kDefaultMaxNumberOfNewParameters),
|
||||
go_deeper_in_constant_obfuscation_(
|
||||
kDefaultGoDeeperInConstantObfuscation) {
|
||||
chance_of_adding_access_chain_ =
|
||||
@@ -126,6 +136,8 @@ FuzzerContext::FuzzerContext(RandomGenerator* random_generator,
|
||||
ChooseBetweenMinAndMax(kChanceOfAddingMatrixType);
|
||||
chance_of_adding_no_contraction_decoration_ =
|
||||
ChooseBetweenMinAndMax(kChanceOfAddingNoContractionDecoration);
|
||||
chance_of_adding_parameters =
|
||||
ChooseBetweenMinAndMax(kChanceOfAddingParameters);
|
||||
chance_of_adding_store_ = ChooseBetweenMinAndMax(kChanceOfAddingStore);
|
||||
chance_of_adding_vector_shuffle_ =
|
||||
ChooseBetweenMinAndMax(kChanceOfAddingVectorShuffle);
|
||||
@@ -163,6 +175,8 @@ FuzzerContext::FuzzerContext(RandomGenerator* random_generator,
|
||||
ChooseBetweenMinAndMax(kChanceOfOutliningFunction);
|
||||
chance_of_permuting_parameters_ =
|
||||
ChooseBetweenMinAndMax(kChanceOfPermutingParameters);
|
||||
chance_of_permuting_phi_operands_ =
|
||||
ChooseBetweenMinAndMax(kChanceOfPermutingPhiOperands);
|
||||
chance_of_pushing_id_through_variable_ =
|
||||
ChooseBetweenMinAndMax(kChanceOfPushingIdThroughVariable);
|
||||
chance_of_replacing_id_with_synonym_ =
|
||||
@@ -170,6 +184,8 @@ FuzzerContext::FuzzerContext(RandomGenerator* random_generator,
|
||||
chance_of_replacing_linear_algebra_instructions_ =
|
||||
ChooseBetweenMinAndMax(kChanceOfReplacingLinearAlgebraInstructions);
|
||||
chance_of_splitting_block_ = ChooseBetweenMinAndMax(kChanceOfSplittingBlock);
|
||||
chance_of_swapping_conditional_branch_operands_ =
|
||||
ChooseBetweenMinAndMax(kChanceOfSwappingConditionalBranchOperands);
|
||||
chance_of_toggling_access_chain_instruction_ =
|
||||
ChooseBetweenMinAndMax(kChanceOfTogglingAccessChainInstruction);
|
||||
}
|
||||
|
||||
@@ -135,6 +135,7 @@ class FuzzerContext {
|
||||
uint32_t GetChanceOfAddingNoContractionDecoration() {
|
||||
return chance_of_adding_no_contraction_decoration_;
|
||||
}
|
||||
uint32_t GetChanceOfAddingParameters() { return chance_of_adding_parameters; }
|
||||
uint32_t GetChanceOfAddingStore() { return chance_of_adding_store_; }
|
||||
uint32_t GetChanceOfAddingVectorShuffle() {
|
||||
return chance_of_adding_vector_shuffle_;
|
||||
@@ -185,6 +186,9 @@ class FuzzerContext {
|
||||
uint32_t GetChanceOfPermutingParameters() {
|
||||
return chance_of_permuting_parameters_;
|
||||
}
|
||||
uint32_t GetChanceOfPermutingPhiOperands() {
|
||||
return chance_of_permuting_phi_operands_;
|
||||
}
|
||||
uint32_t GetChanceOfPushingIdThroughVariable() {
|
||||
return chance_of_pushing_id_through_variable_;
|
||||
}
|
||||
@@ -195,6 +199,9 @@ class FuzzerContext {
|
||||
return chance_of_replacing_linear_algebra_instructions_;
|
||||
}
|
||||
uint32_t GetChanceOfSplittingBlock() { return chance_of_splitting_block_; }
|
||||
uint32_t GetChanceOfSwappingConditionalBranchOperands() {
|
||||
return chance_of_swapping_conditional_branch_operands_;
|
||||
}
|
||||
uint32_t GetChanceOfTogglingAccessChainInstruction() {
|
||||
return chance_of_toggling_access_chain_instruction_;
|
||||
}
|
||||
@@ -204,6 +211,9 @@ class FuzzerContext {
|
||||
uint32_t GetMaximumEquivalenceClassSizeForDataSynonymFactClosure() {
|
||||
return max_equivalence_class_size_for_data_synonym_fact_closure_;
|
||||
}
|
||||
uint32_t GetMaximumNumberOfFunctionParameters() {
|
||||
return max_number_of_function_parameters_;
|
||||
}
|
||||
std::pair<uint32_t, uint32_t> GetRandomBranchWeights() {
|
||||
std::pair<uint32_t, uint32_t> branch_weights = {0, 0};
|
||||
|
||||
@@ -239,6 +249,12 @@ class FuzzerContext {
|
||||
uint32_t GetRandomLoopLimit() {
|
||||
return random_generator_->RandomUint32(max_loop_limit_);
|
||||
}
|
||||
uint32_t GetRandomNumberOfNewParameters(uint32_t num_of_params) {
|
||||
assert(num_of_params < GetMaximumNumberOfFunctionParameters());
|
||||
return ChooseBetweenMinAndMax(
|
||||
{1, std::min(max_number_of_new_parameters_,
|
||||
GetMaximumNumberOfFunctionParameters() - num_of_params)});
|
||||
}
|
||||
uint32_t GetRandomSizeForNewArray() {
|
||||
// Ensure that the array size is non-zero.
|
||||
return random_generator_->RandomUint32(max_new_array_size_limit_ - 1) + 1;
|
||||
@@ -267,6 +283,7 @@ class FuzzerContext {
|
||||
uint32_t chance_of_adding_local_variable_;
|
||||
uint32_t chance_of_adding_matrix_type_;
|
||||
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_vector_shuffle_;
|
||||
uint32_t chance_of_adding_vector_type_;
|
||||
@@ -287,10 +304,12 @@ class FuzzerContext {
|
||||
uint32_t chance_of_obfuscating_constant_;
|
||||
uint32_t chance_of_outlining_function_;
|
||||
uint32_t chance_of_permuting_parameters_;
|
||||
uint32_t chance_of_permuting_phi_operands_;
|
||||
uint32_t chance_of_pushing_id_through_variable_;
|
||||
uint32_t chance_of_replacing_id_with_synonym_;
|
||||
uint32_t chance_of_replacing_linear_algebra_instructions_;
|
||||
uint32_t chance_of_splitting_block_;
|
||||
uint32_t chance_of_swapping_conditional_branch_operands_;
|
||||
uint32_t chance_of_toggling_access_chain_instruction_;
|
||||
|
||||
// Limits associated with various quantities for which random values are
|
||||
@@ -301,6 +320,8 @@ class FuzzerContext {
|
||||
uint32_t max_loop_control_peel_count_;
|
||||
uint32_t max_loop_limit_;
|
||||
uint32_t max_new_array_size_limit_;
|
||||
uint32_t max_number_of_function_parameters_;
|
||||
uint32_t max_number_of_new_parameters_;
|
||||
|
||||
// Functions to determine with what probability to go deeper when generating
|
||||
// or mutating constructs recursively.
|
||||
|
||||
143
3rdparty/spirv-tools/source/fuzz/fuzzer_pass_add_parameters.cpp
vendored
Normal file
143
3rdparty/spirv-tools/source/fuzz/fuzzer_pass_add_parameters.cpp
vendored
Normal file
@@ -0,0 +1,143 @@
|
||||
// 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_parameters.h"
|
||||
|
||||
#include "source/fuzz/fuzzer_context.h"
|
||||
#include "source/fuzz/fuzzer_util.h"
|
||||
#include "source/fuzz/instruction_descriptor.h"
|
||||
#include "source/fuzz/transformation_add_parameters.h"
|
||||
|
||||
namespace spvtools {
|
||||
namespace fuzz {
|
||||
|
||||
FuzzerPassAddParameters::FuzzerPassAddParameters(
|
||||
opt::IRContext* ir_context, TransformationContext* transformation_context,
|
||||
FuzzerContext* fuzzer_context,
|
||||
protobufs::TransformationSequence* transformations)
|
||||
: FuzzerPass(ir_context, transformation_context, fuzzer_context,
|
||||
transformations) {}
|
||||
|
||||
FuzzerPassAddParameters::~FuzzerPassAddParameters() = default;
|
||||
|
||||
void FuzzerPassAddParameters::Apply() {
|
||||
const auto& type_candidates = ComputeTypeCandidates();
|
||||
|
||||
if (type_candidates.empty()) {
|
||||
// The module contains no suitable types to use in new parameters.
|
||||
return;
|
||||
}
|
||||
|
||||
// Iterate over all functions in the module.
|
||||
for (const auto& function : *GetIRContext()->module()) {
|
||||
// Skip all entry-point functions - we don't want to change those.
|
||||
if (fuzzerutil::FunctionIsEntryPoint(GetIRContext(),
|
||||
function.result_id())) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (GetNumberOfParameters(function) >=
|
||||
GetFuzzerContext()->GetMaximumNumberOfFunctionParameters()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!GetFuzzerContext()->ChoosePercentage(
|
||||
GetFuzzerContext()->GetChanceOfAddingParameters())) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const auto* type_inst =
|
||||
fuzzerutil::GetFunctionType(GetIRContext(), &function);
|
||||
assert(type_inst);
|
||||
|
||||
// -1 because we don't take return type into account.
|
||||
auto num_old_parameters = type_inst->NumInOperands() - 1;
|
||||
auto num_new_parameters =
|
||||
GetFuzzerContext()->GetRandomNumberOfNewParameters(
|
||||
GetNumberOfParameters(function));
|
||||
|
||||
std::vector<uint32_t> all_types(num_old_parameters);
|
||||
std::vector<uint32_t> new_types(num_new_parameters);
|
||||
std::vector<uint32_t> parameter_ids(num_new_parameters);
|
||||
std::vector<uint32_t> constant_ids(num_new_parameters);
|
||||
|
||||
// Get type ids for old parameters.
|
||||
for (uint32_t i = 0; i < num_old_parameters; ++i) {
|
||||
// +1 since we don't take return type into account.
|
||||
all_types[i] = type_inst->GetSingleWordInOperand(i + 1);
|
||||
}
|
||||
|
||||
for (uint32_t i = 0; i < num_new_parameters; ++i) {
|
||||
// Get type ids for new parameters.
|
||||
new_types[i] =
|
||||
type_candidates[GetFuzzerContext()->RandomIndex(type_candidates)];
|
||||
|
||||
// Create constants to initialize new parameters from.
|
||||
constant_ids[i] = FindOrCreateZeroConstant(new_types[i]);
|
||||
}
|
||||
|
||||
// Append new parameters to the old ones.
|
||||
all_types.insert(all_types.end(), new_types.begin(), new_types.end());
|
||||
|
||||
// Generate result ids for new parameters.
|
||||
for (auto& id : parameter_ids) {
|
||||
id = GetFuzzerContext()->GetFreshId();
|
||||
}
|
||||
|
||||
auto result_type_id = type_inst->GetSingleWordInOperand(0);
|
||||
ApplyTransformation(TransformationAddParameters(
|
||||
function.result_id(),
|
||||
FindOrCreateFunctionType(result_type_id, all_types),
|
||||
std::move(new_types), std::move(parameter_ids),
|
||||
std::move(constant_ids)));
|
||||
}
|
||||
}
|
||||
|
||||
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(
|
||||
function.DefInst().GetSingleWordInOperand(1));
|
||||
assert(type && type->AsFunction());
|
||||
|
||||
return static_cast<uint32_t>(type->AsFunction()->param_types().size());
|
||||
}
|
||||
|
||||
} // namespace fuzz
|
||||
} // namespace spvtools
|
||||
51
3rdparty/spirv-tools/source/fuzz/fuzzer_pass_add_parameters.h
vendored
Normal file
51
3rdparty/spirv-tools/source/fuzz/fuzzer_pass_add_parameters.h
vendored
Normal file
@@ -0,0 +1,51 @@
|
||||
// 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_PARAMETERS_H_
|
||||
#define SOURCE_FUZZ_FUZZER_PASS_ADD_PARAMETERS_H_
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "source/fuzz/fuzzer_pass.h"
|
||||
|
||||
namespace spvtools {
|
||||
namespace fuzz {
|
||||
|
||||
// Randomly decides for each non-entry-point function in the module whether to
|
||||
// add new parameters to it. If so, randomly determines the number of parameters
|
||||
// to add, their type and creates constants used to initialize them.
|
||||
class FuzzerPassAddParameters : public FuzzerPass {
|
||||
public:
|
||||
FuzzerPassAddParameters(opt::IRContext* ir_context,
|
||||
TransformationContext* transformation_context,
|
||||
FuzzerContext* fuzzer_context,
|
||||
protobufs::TransformationSequence* transformations);
|
||||
|
||||
~FuzzerPassAddParameters() override;
|
||||
|
||||
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;
|
||||
};
|
||||
|
||||
} // namespace fuzz
|
||||
} // namespace spvtools
|
||||
|
||||
#endif // SOURCE_FUZZ_FUZZER_PASS_ADD_PARAMETERS_H_
|
||||
@@ -34,7 +34,7 @@ class FuzzerPassPermuteFunctionParameters : public FuzzerPass {
|
||||
FuzzerContext* fuzzer_context,
|
||||
protobufs::TransformationSequence* transformations);
|
||||
|
||||
~FuzzerPassPermuteFunctionParameters();
|
||||
~FuzzerPassPermuteFunctionParameters() override;
|
||||
|
||||
void Apply() override;
|
||||
};
|
||||
|
||||
64
3rdparty/spirv-tools/source/fuzz/fuzzer_pass_permute_phi_operands.cpp
vendored
Normal file
64
3rdparty/spirv-tools/source/fuzz/fuzzer_pass_permute_phi_operands.cpp
vendored
Normal file
@@ -0,0 +1,64 @@
|
||||
// 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 <numeric>
|
||||
#include <vector>
|
||||
|
||||
#include "source/fuzz/fuzzer_context.h"
|
||||
#include "source/fuzz/fuzzer_pass_permute_phi_operands.h"
|
||||
#include "source/fuzz/fuzzer_util.h"
|
||||
#include "source/fuzz/instruction_descriptor.h"
|
||||
#include "source/fuzz/transformation_permute_phi_operands.h"
|
||||
|
||||
namespace spvtools {
|
||||
namespace fuzz {
|
||||
|
||||
FuzzerPassPermutePhiOperands::FuzzerPassPermutePhiOperands(
|
||||
opt::IRContext* ir_context, TransformationContext* transformation_context,
|
||||
FuzzerContext* fuzzer_context,
|
||||
protobufs::TransformationSequence* transformations)
|
||||
: FuzzerPass(ir_context, transformation_context, fuzzer_context,
|
||||
transformations) {}
|
||||
|
||||
FuzzerPassPermutePhiOperands::~FuzzerPassPermutePhiOperands() = default;
|
||||
|
||||
void FuzzerPassPermutePhiOperands::Apply() {
|
||||
ForEachInstructionWithInstructionDescriptor(
|
||||
[this](opt::Function* /*unused*/, opt::BasicBlock* /*unused*/,
|
||||
opt::BasicBlock::iterator inst_it,
|
||||
const protobufs::InstructionDescriptor& /*unused*/) {
|
||||
const auto& inst = *inst_it;
|
||||
|
||||
if (inst.opcode() != SpvOpPhi) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!GetFuzzerContext()->ChoosePercentage(
|
||||
GetFuzzerContext()->GetChanceOfPermutingPhiOperands())) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Create a vector of indices for each pair of operands in the |inst|.
|
||||
// OpPhi always has an even number of operands.
|
||||
std::vector<uint32_t> permutation(inst.NumInOperands() / 2);
|
||||
std::iota(permutation.begin(), permutation.end(), 0);
|
||||
GetFuzzerContext()->Shuffle(&permutation);
|
||||
|
||||
ApplyTransformation(TransformationPermutePhiOperands(
|
||||
inst.result_id(), std::move(permutation)));
|
||||
});
|
||||
}
|
||||
|
||||
} // namespace fuzz
|
||||
} // namespace spvtools
|
||||
40
3rdparty/spirv-tools/source/fuzz/fuzzer_pass_permute_phi_operands.h
vendored
Normal file
40
3rdparty/spirv-tools/source/fuzz/fuzzer_pass_permute_phi_operands.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_PERMUTE_PHI_OPERANDS_H_
|
||||
#define SOURCE_FUZZ_FUZZER_PASS_PERMUTE_PHI_OPERANDS_H_
|
||||
|
||||
#include "source/fuzz/fuzzer_pass.h"
|
||||
|
||||
namespace spvtools {
|
||||
namespace fuzz {
|
||||
|
||||
// Iterates over all instructions in the module and randomly decides for each
|
||||
// OpPhi instruction whether to permute its operands.
|
||||
class FuzzerPassPermutePhiOperands : public FuzzerPass {
|
||||
public:
|
||||
FuzzerPassPermutePhiOperands(
|
||||
opt::IRContext* ir_context, TransformationContext* transformation_context,
|
||||
FuzzerContext* fuzzer_context,
|
||||
protobufs::TransformationSequence* transformations);
|
||||
|
||||
~FuzzerPassPermutePhiOperands() override;
|
||||
|
||||
void Apply() override;
|
||||
};
|
||||
|
||||
} // namespace fuzz
|
||||
} // namespace spvtools
|
||||
|
||||
#endif // SOURCE_FUZZ_FUZZER_PASS_PERMUTE_PHI_OPERANDS_H_
|
||||
@@ -104,13 +104,37 @@ void FuzzerPassPushIdsThroughVariables::Apply() {
|
||||
// If the pointer type does not exist, then create it.
|
||||
FindOrCreatePointerType(basic_type_id, variable_storage_class);
|
||||
|
||||
// TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3403):
|
||||
// type support here is limited by the FindOrCreateZeroConstant
|
||||
// function.
|
||||
const auto* type_inst =
|
||||
GetIRContext()->get_def_use_mgr()->GetDef(basic_type_id);
|
||||
assert(type_inst);
|
||||
switch (type_inst->opcode()) {
|
||||
case SpvOpTypeBool:
|
||||
case SpvOpTypeFloat:
|
||||
case SpvOpTypeInt:
|
||||
case SpvOpTypeArray:
|
||||
case SpvOpTypeMatrix:
|
||||
case SpvOpTypeVector:
|
||||
case SpvOpTypeStruct:
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
||||
// Create a constant to initialize the variable from. This might update
|
||||
// module's id bound so it must be done before any fresh ids are
|
||||
// computed.
|
||||
auto initializer_id = FindOrCreateZeroConstant(basic_type_id);
|
||||
|
||||
// Applies the push id through variable transformation.
|
||||
ApplyTransformation(TransformationPushIdThroughVariable(
|
||||
value_instructions[GetFuzzerContext()->RandomIndex(
|
||||
value_instructions)]
|
||||
->result_id(),
|
||||
GetFuzzerContext()->GetFreshId(), GetFuzzerContext()->GetFreshId(),
|
||||
variable_storage_class, instruction_descriptor));
|
||||
variable_storage_class, initializer_id, instruction_descriptor));
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -30,7 +30,7 @@ class FuzzerPassPushIdsThroughVariables : public FuzzerPass {
|
||||
FuzzerContext* fuzzer_context,
|
||||
protobufs::TransformationSequence* transformations);
|
||||
|
||||
~FuzzerPassPushIdsThroughVariables();
|
||||
~FuzzerPassPushIdsThroughVariables() override;
|
||||
|
||||
void Apply() override;
|
||||
};
|
||||
|
||||
@@ -42,6 +42,7 @@ void FuzzerPassReplaceLinearAlgebraInstructions::Apply() {
|
||||
// addressed the following conditional can use the function
|
||||
// |spvOpcodeIsLinearAlgebra|.
|
||||
if (instruction->opcode() != SpvOpVectorTimesScalar &&
|
||||
instruction->opcode() != SpvOpMatrixTimesScalar &&
|
||||
instruction->opcode() != SpvOpDot) {
|
||||
return;
|
||||
}
|
||||
|
||||
59
3rdparty/spirv-tools/source/fuzz/fuzzer_pass_swap_conditional_branch_operands.cpp
vendored
Normal file
59
3rdparty/spirv-tools/source/fuzz/fuzzer_pass_swap_conditional_branch_operands.cpp
vendored
Normal file
@@ -0,0 +1,59 @@
|
||||
// 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_swap_conditional_branch_operands.h"
|
||||
#include "source/fuzz/fuzzer_context.h"
|
||||
#include "source/fuzz/fuzzer_util.h"
|
||||
#include "source/fuzz/instruction_descriptor.h"
|
||||
#include "source/fuzz/transformation_swap_conditional_branch_operands.h"
|
||||
|
||||
namespace spvtools {
|
||||
namespace fuzz {
|
||||
|
||||
FuzzerPassSwapBranchConditionalOperands::
|
||||
FuzzerPassSwapBranchConditionalOperands(
|
||||
opt::IRContext* ir_context,
|
||||
TransformationContext* transformation_context,
|
||||
FuzzerContext* fuzzer_context,
|
||||
protobufs::TransformationSequence* transformations)
|
||||
: FuzzerPass(ir_context, transformation_context, fuzzer_context,
|
||||
transformations) {}
|
||||
|
||||
FuzzerPassSwapBranchConditionalOperands::
|
||||
~FuzzerPassSwapBranchConditionalOperands() = default;
|
||||
|
||||
void FuzzerPassSwapBranchConditionalOperands::Apply() {
|
||||
ForEachInstructionWithInstructionDescriptor(
|
||||
[this](opt::Function* /*unused*/, opt::BasicBlock* /*unused*/,
|
||||
opt::BasicBlock::iterator inst_it,
|
||||
const protobufs::InstructionDescriptor& instruction_descriptor) {
|
||||
const auto& inst = *inst_it;
|
||||
|
||||
if (inst.opcode() != SpvOpBranchConditional) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!GetFuzzerContext()->ChoosePercentage(
|
||||
GetFuzzerContext()
|
||||
->GetChanceOfSwappingConditionalBranchOperands())) {
|
||||
return;
|
||||
}
|
||||
|
||||
ApplyTransformation(TransformationSwapConditionalBranchOperands(
|
||||
instruction_descriptor, GetFuzzerContext()->GetFreshId()));
|
||||
});
|
||||
}
|
||||
|
||||
} // namespace fuzz
|
||||
} // namespace spvtools
|
||||
40
3rdparty/spirv-tools/source/fuzz/fuzzer_pass_swap_conditional_branch_operands.h
vendored
Normal file
40
3rdparty/spirv-tools/source/fuzz/fuzzer_pass_swap_conditional_branch_operands.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_SWAP_BRANCH_CONDITIONAL_OPERANDS_H_
|
||||
#define SOURCE_FUZZ_FUZZER_PASS_SWAP_BRANCH_CONDITIONAL_OPERANDS_H_
|
||||
|
||||
#include "source/fuzz/fuzzer_pass.h"
|
||||
|
||||
namespace spvtools {
|
||||
namespace fuzz {
|
||||
|
||||
// Randomly decides for each OpBranchConditional instruction in the module
|
||||
// whether to swap its operands or not.
|
||||
class FuzzerPassSwapBranchConditionalOperands : public FuzzerPass {
|
||||
public:
|
||||
FuzzerPassSwapBranchConditionalOperands(
|
||||
opt::IRContext* ir_context, TransformationContext* transformation_context,
|
||||
FuzzerContext* fuzzer_context,
|
||||
protobufs::TransformationSequence* transformations);
|
||||
|
||||
~FuzzerPassSwapBranchConditionalOperands() override;
|
||||
|
||||
void Apply() override;
|
||||
};
|
||||
|
||||
} // namespace fuzz
|
||||
} // namespace spvtools
|
||||
|
||||
#endif // SOURCE_FUZZ_FUZZER_PASS_SWAP_BRANCH_CONDITIONAL_OPERANDS_H_
|
||||
104
3rdparty/spirv-tools/source/fuzz/fuzzer_util.cpp
vendored
104
3rdparty/spirv-tools/source/fuzz/fuzzer_util.cpp
vendored
@@ -12,6 +12,9 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include <algorithm>
|
||||
#include <unordered_set>
|
||||
|
||||
#include "source/fuzz/fuzzer_util.h"
|
||||
|
||||
#include "source/opt/build_module.h"
|
||||
@@ -583,6 +586,107 @@ void AddVariableIdToEntryPointInterfaces(opt::IRContext* context, uint32_t id) {
|
||||
}
|
||||
}
|
||||
|
||||
void AddGlobalVariable(opt::IRContext* context, uint32_t result_id,
|
||||
uint32_t type_id, SpvStorageClass storage_class,
|
||||
uint32_t initializer_id) {
|
||||
// Check various preconditions.
|
||||
assert((storage_class == SpvStorageClassPrivate ||
|
||||
storage_class == SpvStorageClassWorkgroup) &&
|
||||
"Variable's storage class must be either Private or Workgroup");
|
||||
|
||||
auto* type_inst = context->get_def_use_mgr()->GetDef(type_id);
|
||||
(void)type_inst; // Variable becomes unused in release mode.
|
||||
assert(type_inst && type_inst->opcode() == SpvOpTypePointer &&
|
||||
GetStorageClassFromPointerType(type_inst) == storage_class &&
|
||||
"Variable's type is invalid");
|
||||
|
||||
if (storage_class == SpvStorageClassWorkgroup) {
|
||||
assert(initializer_id == 0);
|
||||
}
|
||||
|
||||
if (initializer_id != 0) {
|
||||
const auto* constant_inst =
|
||||
context->get_def_use_mgr()->GetDef(initializer_id);
|
||||
(void)constant_inst; // Variable becomes unused in release mode.
|
||||
assert(constant_inst && spvOpcodeIsConstant(constant_inst->opcode()) &&
|
||||
GetPointeeTypeIdFromPointerType(type_inst) ==
|
||||
constant_inst->type_id() &&
|
||||
"Initializer is invalid");
|
||||
}
|
||||
|
||||
opt::Instruction::OperandList operands = {
|
||||
{SPV_OPERAND_TYPE_STORAGE_CLASS, {static_cast<uint32_t>(storage_class)}}};
|
||||
|
||||
if (initializer_id) {
|
||||
operands.push_back({SPV_OPERAND_TYPE_ID, {initializer_id}});
|
||||
}
|
||||
|
||||
context->module()->AddGlobalValue(MakeUnique<opt::Instruction>(
|
||||
context, SpvOpVariable, type_id, result_id, std::move(operands)));
|
||||
|
||||
AddVariableIdToEntryPointInterfaces(context, result_id);
|
||||
}
|
||||
|
||||
void AddLocalVariable(opt::IRContext* context, uint32_t result_id,
|
||||
uint32_t type_id, uint32_t function_id,
|
||||
uint32_t initializer_id) {
|
||||
// Check various preconditions.
|
||||
auto* type_inst = context->get_def_use_mgr()->GetDef(type_id);
|
||||
(void)type_inst; // Variable becomes unused in release mode.
|
||||
assert(type_inst && type_inst->opcode() == SpvOpTypePointer &&
|
||||
GetStorageClassFromPointerType(type_inst) == SpvStorageClassFunction &&
|
||||
"Variable's type is invalid");
|
||||
|
||||
const auto* constant_inst =
|
||||
context->get_def_use_mgr()->GetDef(initializer_id);
|
||||
(void)constant_inst; // Variable becomes unused in release mode.
|
||||
assert(constant_inst && spvOpcodeIsConstant(constant_inst->opcode()) &&
|
||||
GetPointeeTypeIdFromPointerType(type_inst) ==
|
||||
constant_inst->type_id() &&
|
||||
"Initializer is invalid");
|
||||
|
||||
auto* function = FindFunction(context, function_id);
|
||||
assert(function && "Function id is invalid");
|
||||
|
||||
function->begin()->begin()->InsertBefore(MakeUnique<opt::Instruction>(
|
||||
context, SpvOpVariable, type_id, result_id,
|
||||
opt::Instruction::OperandList{
|
||||
{SPV_OPERAND_TYPE_STORAGE_CLASS, {SpvStorageClassFunction}},
|
||||
{SPV_OPERAND_TYPE_ID, {initializer_id}}}));
|
||||
}
|
||||
|
||||
bool HasDuplicates(const std::vector<uint32_t>& arr) {
|
||||
return std::unordered_set<uint32_t>(arr.begin(), arr.end()).size() !=
|
||||
arr.size();
|
||||
}
|
||||
|
||||
bool IsPermutationOfRange(const std::vector<uint32_t>& arr, uint32_t lo,
|
||||
uint32_t hi) {
|
||||
if (arr.empty()) {
|
||||
return lo > hi;
|
||||
}
|
||||
|
||||
if (HasDuplicates(arr)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
auto min_max = std::minmax_element(arr.begin(), arr.end());
|
||||
return arr.size() == hi - lo + 1 && *min_max.first == lo &&
|
||||
*min_max.second == hi;
|
||||
}
|
||||
|
||||
std::vector<opt::Instruction*> GetParameters(opt::IRContext* ir_context,
|
||||
uint32_t function_id) {
|
||||
auto* function = FindFunction(ir_context, function_id);
|
||||
assert(function && "|function_id| is invalid");
|
||||
|
||||
std::vector<opt::Instruction*> result;
|
||||
function->ForEachParam(
|
||||
[&result](opt::Instruction* inst) { result.push_back(inst); });
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
} // namespace fuzzerutil
|
||||
|
||||
} // namespace fuzz
|
||||
|
||||
40
3rdparty/spirv-tools/source/fuzz/fuzzer_util.h
vendored
40
3rdparty/spirv-tools/source/fuzz/fuzzer_util.h
vendored
@@ -226,6 +226,46 @@ bool GlobalVariablesMustBeDeclaredInEntryPointInterfaces(
|
||||
// from an entry point function, to be listed in that function's interface.
|
||||
void AddVariableIdToEntryPointInterfaces(opt::IRContext* context, uint32_t id);
|
||||
|
||||
// Adds a global variable with storage class |storage_class| to the module, with
|
||||
// type |type_id| and either no initializer or |initializer_id| as an
|
||||
// initializer, depending on whether |initializer_id| is 0. The global variable
|
||||
// has result id |result_id|.
|
||||
//
|
||||
// - |type_id| must be the id of a pointer type with the same storage class as
|
||||
// |storage_class|.
|
||||
// - |storage_class| must be Private or Workgroup.
|
||||
// - |initializer_id| must be 0 if |storage_class| is Workgroup, and otherwise
|
||||
// may either be 0 or the id of a constant whose type is the pointee type of
|
||||
// |type_id|.
|
||||
void AddGlobalVariable(opt::IRContext* context, uint32_t result_id,
|
||||
uint32_t type_id, SpvStorageClass storage_class,
|
||||
uint32_t initializer_id);
|
||||
|
||||
// Adds an instruction to the start of |function_id|, of the form:
|
||||
// |result_id| = OpVariable |type_id| Function |initializer_id|.
|
||||
//
|
||||
// - |type_id| must be the id of a pointer type with Function storage class.
|
||||
// - |initializer_id| must be the id of a constant with the same type as the
|
||||
// pointer's pointee type.
|
||||
// - |function_id| must be the id of a function.
|
||||
void AddLocalVariable(opt::IRContext* context, uint32_t result_id,
|
||||
uint32_t type_id, uint32_t function_id,
|
||||
uint32_t initializer_id);
|
||||
|
||||
// Returns true if the vector |arr| has duplicates.
|
||||
bool HasDuplicates(const std::vector<uint32_t>& arr);
|
||||
|
||||
// Checks that the given vector |arr| contains a permutation of a range
|
||||
// [lo, hi]. That being said, all elements in the range are present without
|
||||
// duplicates. If |arr| is empty, returns true iff |lo > hi|.
|
||||
bool IsPermutationOfRange(const std::vector<uint32_t>& arr, uint32_t lo,
|
||||
uint32_t hi);
|
||||
|
||||
// Returns OpFunctionParameter instructions corresponding to the function
|
||||
// with result id |function_id|.
|
||||
std::vector<opt::Instruction*> GetParameters(opt::IRContext* ir_context,
|
||||
uint32_t function_id);
|
||||
|
||||
} // namespace fuzzerutil
|
||||
|
||||
} // namespace fuzz
|
||||
|
||||
@@ -378,6 +378,9 @@ message Transformation {
|
||||
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;
|
||||
TransformationAddParameters add_parameters = 52;
|
||||
// Add additional option using the next available number.
|
||||
}
|
||||
}
|
||||
@@ -621,6 +624,27 @@ message TransformationAddNoContractionDecoration {
|
||||
|
||||
}
|
||||
|
||||
message TransformationAddParameters {
|
||||
|
||||
// Adds new parameters into the function.
|
||||
|
||||
// Result id of the function to add parameters to.
|
||||
uint32 function_id = 1;
|
||||
|
||||
// New type of the function.
|
||||
uint32 new_type_id = 2;
|
||||
|
||||
// Type ids of parameters to add to the function.
|
||||
repeated uint32 new_parameter_type = 3;
|
||||
|
||||
// Result ids for new parameters.
|
||||
repeated uint32 new_parameter_id = 4;
|
||||
|
||||
// Constants to initialize new parameters from.
|
||||
repeated uint32 constant_id = 5;
|
||||
|
||||
}
|
||||
|
||||
message TransformationAddSpecConstantOp {
|
||||
|
||||
// Adds OpSpecConstantOp into the module.
|
||||
@@ -1004,6 +1028,19 @@ message TransformationPermuteFunctionParameters {
|
||||
|
||||
}
|
||||
|
||||
message TransformationPermutePhiOperands {
|
||||
|
||||
// Permutes operands of some OpPhi instruction.
|
||||
|
||||
// Result id of the instruction to apply the transformation to.
|
||||
uint32 result_id = 1;
|
||||
|
||||
// A sequence of numbers in the range [0, n/2 - 1] where |n| is the number
|
||||
// of operands of the OpPhi instruction with |result_id|.
|
||||
repeated uint32 permutation = 2;
|
||||
|
||||
}
|
||||
|
||||
message TransformationPushIdThroughVariable {
|
||||
|
||||
// A transformation that makes |value_synonym_id| and |value_id| to be
|
||||
@@ -1019,12 +1056,15 @@ message TransformationPushIdThroughVariable {
|
||||
// A fresh id for the variable to be stored to.
|
||||
uint32 variable_id = 3;
|
||||
|
||||
// Constant to initialize the variable from.
|
||||
uint32 initializer_id = 4;
|
||||
|
||||
// The variable storage class (global or local).
|
||||
uint32 variable_storage_class = 4;
|
||||
uint32 variable_storage_class = 5;
|
||||
|
||||
// A descriptor for an instruction which the new OpStore
|
||||
// and OpLoad instructions might be inserted before.
|
||||
InstructionDescriptor instruction_descriptor = 5;
|
||||
InstructionDescriptor instruction_descriptor = 6;
|
||||
|
||||
}
|
||||
|
||||
@@ -1094,13 +1134,13 @@ message TransformationReplaceLinearAlgebraInstruction {
|
||||
// This transformation is only applicable if the described instruction has one of the following opcodes.
|
||||
// Supported:
|
||||
// OpVectorTimesScalar
|
||||
// OpMatrixTimesScalar
|
||||
// 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
|
||||
// OpMatrixTimesScalar
|
||||
// OpVectorTimesMatrix
|
||||
// OpMatrixTimesVector
|
||||
// OpMatrixTimesMatrix
|
||||
@@ -1226,6 +1266,21 @@ message TransformationSwapCommutableOperands {
|
||||
|
||||
}
|
||||
|
||||
message TransformationSwapConditionalBranchOperands {
|
||||
|
||||
// Swaps label ids in OpBranchConditional instruction.
|
||||
// Additionally, inverts the guard and swaps branch weights
|
||||
// if present.
|
||||
|
||||
// Descriptor of the instruction to swap operands of.
|
||||
InstructionDescriptor instruction_descriptor = 1;
|
||||
|
||||
// Fresh result id for the OpLogicalNot instruction, used
|
||||
// to invert the guard.
|
||||
uint32 fresh_id = 2;
|
||||
|
||||
}
|
||||
|
||||
message TransformationToggleAccessChainInstruction {
|
||||
|
||||
// A transformation that toggles an access chain instruction.
|
||||
|
||||
@@ -30,6 +30,7 @@
|
||||
#include "source/fuzz/transformation_add_global_variable.h"
|
||||
#include "source/fuzz/transformation_add_local_variable.h"
|
||||
#include "source/fuzz/transformation_add_no_contraction_decoration.h"
|
||||
#include "source/fuzz/transformation_add_parameters.h"
|
||||
#include "source/fuzz/transformation_add_spec_constant_op.h"
|
||||
#include "source/fuzz/transformation_add_type_array.h"
|
||||
#include "source/fuzz/transformation_add_type_boolean.h"
|
||||
@@ -52,6 +53,7 @@
|
||||
#include "source/fuzz/transformation_move_block_down.h"
|
||||
#include "source/fuzz/transformation_outline_function.h"
|
||||
#include "source/fuzz/transformation_permute_function_parameters.h"
|
||||
#include "source/fuzz/transformation_permute_phi_operands.h"
|
||||
#include "source/fuzz/transformation_push_id_through_variable.h"
|
||||
#include "source/fuzz/transformation_replace_boolean_constant_with_constant_binary.h"
|
||||
#include "source/fuzz/transformation_replace_constant_with_uniform.h"
|
||||
@@ -64,6 +66,7 @@
|
||||
#include "source/fuzz/transformation_split_block.h"
|
||||
#include "source/fuzz/transformation_store.h"
|
||||
#include "source/fuzz/transformation_swap_commutable_operands.h"
|
||||
#include "source/fuzz/transformation_swap_conditional_branch_operands.h"
|
||||
#include "source/fuzz/transformation_toggle_access_chain_instruction.h"
|
||||
#include "source/fuzz/transformation_vector_shuffle.h"
|
||||
#include "source/util/make_unique.h"
|
||||
@@ -112,6 +115,8 @@ std::unique_ptr<Transformation> Transformation::FromMessage(
|
||||
kAddNoContractionDecoration:
|
||||
return MakeUnique<TransformationAddNoContractionDecoration>(
|
||||
message.add_no_contraction_decoration());
|
||||
case protobufs::Transformation::TransformationCase::kAddParameters:
|
||||
return MakeUnique<TransformationAddParameters>(message.add_parameters());
|
||||
case protobufs::Transformation::TransformationCase::kAddSpecConstantOp:
|
||||
return MakeUnique<TransformationAddSpecConstantOp>(
|
||||
message.add_spec_constant_op());
|
||||
@@ -169,6 +174,9 @@ std::unique_ptr<Transformation> Transformation::FromMessage(
|
||||
kPermuteFunctionParameters:
|
||||
return MakeUnique<TransformationPermuteFunctionParameters>(
|
||||
message.permute_function_parameters());
|
||||
case protobufs::Transformation::TransformationCase::kPermutePhiOperands:
|
||||
return MakeUnique<TransformationPermutePhiOperands>(
|
||||
message.permute_phi_operands());
|
||||
case protobufs::Transformation::TransformationCase::kPushIdThroughVariable:
|
||||
return MakeUnique<TransformationPushIdThroughVariable>(
|
||||
message.push_id_through_variable());
|
||||
@@ -206,6 +214,10 @@ std::unique_ptr<Transformation> Transformation::FromMessage(
|
||||
case protobufs::Transformation::TransformationCase::kSwapCommutableOperands:
|
||||
return MakeUnique<TransformationSwapCommutableOperands>(
|
||||
message.swap_commutable_operands());
|
||||
case protobufs::Transformation::TransformationCase::
|
||||
kSwapConditionalBranchOperands:
|
||||
return MakeUnique<TransformationSwapConditionalBranchOperands>(
|
||||
message.swap_conditional_branch_operands());
|
||||
case protobufs::Transformation::TransformationCase::
|
||||
kToggleAccessChainInstruction:
|
||||
return MakeUnique<TransformationToggleAccessChainInstruction>(
|
||||
|
||||
@@ -93,20 +93,12 @@ bool TransformationAddGlobalVariable::IsApplicable(
|
||||
void TransformationAddGlobalVariable::Apply(
|
||||
opt::IRContext* ir_context,
|
||||
TransformationContext* transformation_context) const {
|
||||
opt::Instruction::OperandList input_operands;
|
||||
input_operands.push_back(
|
||||
{SPV_OPERAND_TYPE_STORAGE_CLASS, {message_.storage_class()}});
|
||||
if (message_.initializer_id()) {
|
||||
input_operands.push_back(
|
||||
{SPV_OPERAND_TYPE_ID, {message_.initializer_id()}});
|
||||
}
|
||||
ir_context->module()->AddGlobalValue(MakeUnique<opt::Instruction>(
|
||||
ir_context, SpvOpVariable, message_.type_id(), message_.fresh_id(),
|
||||
input_operands));
|
||||
fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_id());
|
||||
fuzzerutil::AddGlobalVariable(
|
||||
ir_context, message_.fresh_id(), message_.type_id(),
|
||||
static_cast<SpvStorageClass>(message_.storage_class()),
|
||||
message_.initializer_id());
|
||||
|
||||
fuzzerutil::AddVariableIdToEntryPointInterfaces(ir_context,
|
||||
message_.fresh_id());
|
||||
fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_id());
|
||||
|
||||
if (message_.value_is_irrelevant()) {
|
||||
transformation_context->GetFactManager()->AddFactValueOfPointeeIsIrrelevant(
|
||||
|
||||
@@ -70,18 +70,12 @@ bool TransformationAddLocalVariable::IsApplicable(
|
||||
void TransformationAddLocalVariable::Apply(
|
||||
opt::IRContext* ir_context,
|
||||
TransformationContext* transformation_context) const {
|
||||
fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_id());
|
||||
fuzzerutil::FindFunction(ir_context, message_.function_id())
|
||||
->begin()
|
||||
->begin()
|
||||
->InsertBefore(MakeUnique<opt::Instruction>(
|
||||
ir_context, SpvOpVariable, message_.type_id(), message_.fresh_id(),
|
||||
opt::Instruction::OperandList(
|
||||
{{SPV_OPERAND_TYPE_STORAGE_CLASS,
|
||||
{
|
||||
fuzzerutil::AddLocalVariable(ir_context, message_.fresh_id(),
|
||||
message_.type_id(), message_.function_id(),
|
||||
message_.initializer_id());
|
||||
|
||||
fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_id());
|
||||
|
||||
SpvStorageClassFunction}},
|
||||
{SPV_OPERAND_TYPE_ID, {message_.initializer_id()}}})));
|
||||
if (message_.value_is_irrelevant()) {
|
||||
transformation_context->GetFactManager()->AddFactValueOfPointeeIsIrrelevant(
|
||||
message_.fresh_id());
|
||||
|
||||
201
3rdparty/spirv-tools/source/fuzz/transformation_add_parameters.cpp
vendored
Normal file
201
3rdparty/spirv-tools/source/fuzz/transformation_add_parameters.cpp
vendored
Normal file
@@ -0,0 +1,201 @@
|
||||
// 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_parameters.h"
|
||||
|
||||
#include <source/spirv_constant.h>
|
||||
|
||||
#include "source/fuzz/fuzzer_util.h"
|
||||
|
||||
namespace spvtools {
|
||||
namespace fuzz {
|
||||
|
||||
TransformationAddParameters::TransformationAddParameters(
|
||||
const protobufs::TransformationAddParameters& message)
|
||||
: message_(message) {}
|
||||
|
||||
TransformationAddParameters::TransformationAddParameters(
|
||||
uint32_t function_id, uint32_t new_type_id,
|
||||
const std::vector<uint32_t>& new_parameter_type,
|
||||
const std::vector<uint32_t>& new_parameter_id,
|
||||
const std::vector<uint32_t>& constant_id) {
|
||||
message_.set_function_id(function_id);
|
||||
message_.set_new_type_id(new_type_id);
|
||||
|
||||
for (auto id : new_parameter_type) {
|
||||
message_.add_new_parameter_type(id);
|
||||
}
|
||||
|
||||
for (auto id : new_parameter_id) {
|
||||
message_.add_new_parameter_id(id);
|
||||
}
|
||||
|
||||
for (auto id : constant_id) {
|
||||
message_.add_constant_id(id);
|
||||
}
|
||||
}
|
||||
|
||||
bool TransformationAddParameters::IsApplicable(
|
||||
opt::IRContext* ir_context, const TransformationContext& /*unused*/) const {
|
||||
// Check that function exists
|
||||
const auto* function =
|
||||
fuzzerutil::FindFunction(ir_context, message_.function_id());
|
||||
if (!function ||
|
||||
fuzzerutil::FunctionIsEntryPoint(ir_context, function->result_id())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Validate new parameters.
|
||||
const auto& new_type_ids = message_.new_parameter_type();
|
||||
const auto& new_parameter_ids = message_.new_parameter_id();
|
||||
const auto& constant_ids = message_.constant_id();
|
||||
|
||||
// All three vectors must have the same size.
|
||||
if (new_type_ids.size() != new_parameter_ids.size() ||
|
||||
new_type_ids.size() != constant_ids.size()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Vectors must have at least one component.
|
||||
if (new_type_ids.empty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check that type ids exist in the module and are not OpTypeVoid.
|
||||
for (auto id : new_type_ids) {
|
||||
const auto* type = ir_context->get_type_mgr()->GetType(id);
|
||||
if (!type || type->AsVoid()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Check that all parameter ids are fresh.
|
||||
for (auto id : new_parameter_ids) {
|
||||
if (!fuzzerutil::IsFreshId(ir_context, id)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Check that constants exist and have valid type.
|
||||
for (int i = 0, n = constant_ids.size(); i < n; ++i) {
|
||||
const auto* inst = ir_context->get_def_use_mgr()->GetDef(constant_ids[i]);
|
||||
if (!inst || inst->type_id() != new_type_ids[i]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Validate new function type.
|
||||
const auto* old_type_inst = fuzzerutil::GetFunctionType(ir_context, function);
|
||||
const auto* new_type_inst =
|
||||
ir_context->get_def_use_mgr()->GetDef(message_.new_type_id());
|
||||
|
||||
// Both types must exist.
|
||||
assert(old_type_inst && old_type_inst->opcode() == SpvOpTypeFunction);
|
||||
if (!new_type_inst || new_type_inst->opcode() != SpvOpTypeFunction) {
|
||||
return false;
|
||||
}
|
||||
|
||||
auto num_old_parameters = old_type_inst->NumInOperands();
|
||||
auto num_new_parameters = new_type_ids.size();
|
||||
|
||||
// New function type has been added to the module which means that it's valid.
|
||||
// Thus, we don't need to check whether the limit on the number of arguments
|
||||
// is satisfied.
|
||||
|
||||
// New type = old type + new parameters.
|
||||
if (new_type_inst->NumInOperands() !=
|
||||
num_old_parameters + num_new_parameters) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check that old parameters and the return type are preserved.
|
||||
for (uint32_t i = 0; i < num_old_parameters; ++i) {
|
||||
if (new_type_inst->GetSingleWordInOperand(i) !=
|
||||
old_type_inst->GetSingleWordInOperand(i)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Check that new parameters have been appended.
|
||||
for (int i = 0; i < num_new_parameters; ++i) {
|
||||
if (new_type_inst->GetSingleWordInOperand(i + num_old_parameters) !=
|
||||
new_type_ids[i]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void TransformationAddParameters::Apply(
|
||||
opt::IRContext* ir_context, TransformationContext* /*unused*/) const {
|
||||
// Retrieve all data from the message
|
||||
auto function_id = message_.function_id();
|
||||
const auto& new_parameter_type = message_.new_parameter_type();
|
||||
const auto& new_parameter_id = message_.new_parameter_id();
|
||||
const auto& constant_id = message_.constant_id();
|
||||
|
||||
// Find the function that will be transformed
|
||||
auto* function = fuzzerutil::FindFunction(ir_context, function_id);
|
||||
assert(function && "Can't find the function");
|
||||
|
||||
// Change function's type
|
||||
function->DefInst().SetInOperand(1, {message_.new_type_id()});
|
||||
|
||||
// Add new parameters to the function.
|
||||
for (int i = 0, n = new_parameter_id.size(); i < n; ++i) {
|
||||
function->AddParameter(MakeUnique<opt::Instruction>(
|
||||
ir_context, SpvOpFunctionParameter, new_parameter_type[i],
|
||||
new_parameter_id[i], opt::Instruction::OperandList()));
|
||||
|
||||
// TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3403):
|
||||
// Add an PointeeValueIsIrrelevant fact if the parameter is a pointer.
|
||||
}
|
||||
|
||||
// Fix all OpFunctionCall instructions.
|
||||
ir_context->get_def_use_mgr()->ForEachUser(
|
||||
&function->DefInst(),
|
||||
[function_id, &constant_id](opt::Instruction* call) {
|
||||
if (call->opcode() != SpvOpFunctionCall ||
|
||||
call->GetSingleWordInOperand(0) != function_id) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (auto id : constant_id) {
|
||||
// TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3177):
|
||||
// it would be good to mark this usage of |id| as irrelevant, so that
|
||||
// we can perform some interesting transformations on it later.
|
||||
call->AddOperand({SPV_OPERAND_TYPE_ID, {id}});
|
||||
}
|
||||
});
|
||||
|
||||
// Update module's id bound. We can safely dereference the result of
|
||||
// max_element since |new_parameter_id| is guaranteed to have elements.
|
||||
fuzzerutil::UpdateModuleIdBound(
|
||||
ir_context,
|
||||
*std::max_element(new_parameter_id.begin(), new_parameter_id.end()));
|
||||
|
||||
// Make sure our changes are analyzed.
|
||||
ir_context->InvalidateAnalysesExceptFor(
|
||||
opt::IRContext::Analysis::kAnalysisNone);
|
||||
}
|
||||
|
||||
protobufs::Transformation TransformationAddParameters::ToMessage() const {
|
||||
protobufs::Transformation result;
|
||||
*result.mutable_add_parameters() = message_;
|
||||
return result;
|
||||
}
|
||||
|
||||
} // namespace fuzz
|
||||
} // namespace spvtools
|
||||
70
3rdparty/spirv-tools/source/fuzz/transformation_add_parameters.h
vendored
Normal file
70
3rdparty/spirv-tools/source/fuzz/transformation_add_parameters.h
vendored
Normal file
@@ -0,0 +1,70 @@
|
||||
// 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_PARAMETERS_H_
|
||||
#define SOURCE_FUZZ_TRANSFORMATION_ADD_PARAMETERS_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 TransformationAddParameters : public Transformation {
|
||||
public:
|
||||
explicit TransformationAddParameters(
|
||||
const protobufs::TransformationAddParameters& message);
|
||||
|
||||
TransformationAddParameters(uint32_t function_id, uint32_t new_type_id,
|
||||
const std::vector<uint32_t>& new_parameter_type,
|
||||
const std::vector<uint32_t>& new_parameter_id,
|
||||
const std::vector<uint32_t>& constant_id);
|
||||
|
||||
// - |function_id| must be a valid result id of some non-entry-point function
|
||||
// in the module.
|
||||
// - |new_type_id| must be a result id of OpTypeFunction instruction.
|
||||
// - New type of the function must have the same return type. New function
|
||||
// parameters must be appended to the old ones.
|
||||
// - |new_parameter_type| contains result ids of some OpType* instructions in
|
||||
// the module. It may not contain result ids of OpTypeVoid.
|
||||
// - |new_parameter_id| contains fresh ids.
|
||||
// - |constant_id| contains result ids used to initialize new parameters. Type
|
||||
// ids of these instructions must be the same as |new_parameter_type| (i.e.
|
||||
// |new_parameter_type[i] == GetDef(constant_id[i])->type_id()|).
|
||||
// - |new_parameter_id|, |new_parameter_type| and |constant_id| should all
|
||||
// have the same size and may not be empty.
|
||||
bool IsApplicable(
|
||||
opt::IRContext* ir_context,
|
||||
const TransformationContext& transformation_context) const override;
|
||||
|
||||
// - Creates new OpFunctionParameter instructions for the function with
|
||||
// |function_id|.
|
||||
// - Changes type of the function to |new_type_id|.
|
||||
// - Adds ids from |constant_id| to every OpFunctionCall instruction that
|
||||
// calls the function.
|
||||
void Apply(opt::IRContext* ir_context,
|
||||
TransformationContext* transformation_context) const override;
|
||||
|
||||
protobufs::Transformation ToMessage() const override;
|
||||
|
||||
private:
|
||||
protobufs::TransformationAddParameters message_;
|
||||
};
|
||||
|
||||
} // namespace fuzz
|
||||
} // namespace spvtools
|
||||
|
||||
#endif // SOURCE_FUZZ_TRANSFORMATION_ADD_PARAMETERS_H_
|
||||
@@ -12,7 +12,6 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include <unordered_set>
|
||||
#include <vector>
|
||||
|
||||
#include "source/fuzz/fuzzer_util.h"
|
||||
@@ -53,7 +52,8 @@ bool TransformationPermuteFunctionParameters::IsApplicable(
|
||||
const auto* function_type = fuzzerutil::GetFunctionType(ir_context, function);
|
||||
assert(function_type && "Function type is null");
|
||||
|
||||
const auto& permutation = message_.permutation();
|
||||
std::vector<uint32_t> permutation(message_.permutation().begin(),
|
||||
message_.permutation().end());
|
||||
|
||||
// Don't take return type into account
|
||||
auto arg_size = function_type->NumInOperands() - 1;
|
||||
@@ -63,21 +63,20 @@ bool TransformationPermuteFunctionParameters::IsApplicable(
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check that all indices are valid
|
||||
// and unique integers from the [0, n-1] set
|
||||
std::unordered_set<uint32_t> unique_indices;
|
||||
for (auto index : permutation) {
|
||||
// We don't compare |index| with 0 since it's an unsigned integer
|
||||
if (index >= arg_size) {
|
||||
return false;
|
||||
}
|
||||
// Check that permutation doesn't have duplicated values.
|
||||
assert(!fuzzerutil::HasDuplicates(permutation) &&
|
||||
"Permutation has duplicates");
|
||||
|
||||
unique_indices.insert(index);
|
||||
// Check that elements in permutation are in range [0, arg_size - 1].
|
||||
//
|
||||
// We must check whether the permutation is empty first because in that case
|
||||
// |arg_size - 1| will produce |std::numeric_limits<uint32_t>::max()| since
|
||||
// it's an unsigned integer.
|
||||
if (!permutation.empty() &&
|
||||
!fuzzerutil::IsPermutationOfRange(permutation, 0, arg_size - 1)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check that permutation doesn't have duplicated values
|
||||
assert(unique_indices.size() == arg_size && "Permutation has duplicates");
|
||||
|
||||
// Check that new function's type is valid:
|
||||
// - Has the same number of operands
|
||||
// - Has the same result type as the old one
|
||||
|
||||
94
3rdparty/spirv-tools/source/fuzz/transformation_permute_phi_operands.cpp
vendored
Normal file
94
3rdparty/spirv-tools/source/fuzz/transformation_permute_phi_operands.cpp
vendored
Normal file
@@ -0,0 +1,94 @@
|
||||
// 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 <vector>
|
||||
|
||||
#include "source/fuzz/fuzzer_util.h"
|
||||
#include "source/fuzz/transformation_permute_phi_operands.h"
|
||||
|
||||
namespace spvtools {
|
||||
namespace fuzz {
|
||||
|
||||
TransformationPermutePhiOperands::TransformationPermutePhiOperands(
|
||||
const spvtools::fuzz::protobufs::TransformationPermutePhiOperands& message)
|
||||
: message_(message) {}
|
||||
|
||||
TransformationPermutePhiOperands::TransformationPermutePhiOperands(
|
||||
uint32_t result_id, const std::vector<uint32_t>& permutation) {
|
||||
message_.set_result_id(result_id);
|
||||
|
||||
for (auto index : permutation) {
|
||||
message_.add_permutation(index);
|
||||
}
|
||||
}
|
||||
|
||||
bool TransformationPermutePhiOperands::IsApplicable(
|
||||
opt::IRContext* ir_context, const TransformationContext& /*unused*/) const {
|
||||
// Check that |message_.result_id| is valid.
|
||||
const auto* inst =
|
||||
ir_context->get_def_use_mgr()->GetDef(message_.result_id());
|
||||
if (!inst || inst->opcode() != SpvOpPhi) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check that |message_.permutation| has expected size.
|
||||
auto expected_permutation_size = inst->NumInOperands() / 2;
|
||||
if (static_cast<uint32_t>(message_.permutation().size()) !=
|
||||
expected_permutation_size) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check that |message_.permutation| has elements in range
|
||||
// [0, expected_permutation_size - 1].
|
||||
std::vector<uint32_t> permutation(message_.permutation().begin(),
|
||||
message_.permutation().end());
|
||||
assert(!fuzzerutil::HasDuplicates(permutation) &&
|
||||
"Permutation has duplicates");
|
||||
|
||||
// We must check whether the permutation is empty first because in that case
|
||||
// |expected_permutation_size - 1| will produce
|
||||
// |std::numeric_limits<uint32_t>::max()| since it's an unsigned integer.
|
||||
return permutation.empty() ||
|
||||
fuzzerutil::IsPermutationOfRange(permutation, 0,
|
||||
expected_permutation_size - 1);
|
||||
}
|
||||
|
||||
void TransformationPermutePhiOperands::Apply(
|
||||
opt::IRContext* ir_context, TransformationContext* /*unused*/) const {
|
||||
auto* inst = ir_context->get_def_use_mgr()->GetDef(message_.result_id());
|
||||
assert(inst);
|
||||
|
||||
opt::Instruction::OperandList permuted_operands;
|
||||
permuted_operands.reserve(inst->NumInOperands());
|
||||
|
||||
for (auto index : message_.permutation()) {
|
||||
permuted_operands.push_back(std::move(inst->GetInOperand(2 * index)));
|
||||
permuted_operands.push_back(std::move(inst->GetInOperand(2 * index + 1)));
|
||||
}
|
||||
|
||||
inst->SetInOperands(std::move(permuted_operands));
|
||||
|
||||
// Make sure our changes are analyzed
|
||||
ir_context->InvalidateAnalysesExceptFor(
|
||||
opt::IRContext::Analysis::kAnalysisNone);
|
||||
}
|
||||
|
||||
protobufs::Transformation TransformationPermutePhiOperands::ToMessage() const {
|
||||
protobufs::Transformation result;
|
||||
*result.mutable_permute_phi_operands() = message_;
|
||||
return result;
|
||||
}
|
||||
|
||||
} // namespace fuzz
|
||||
} // namespace spvtools
|
||||
56
3rdparty/spirv-tools/source/fuzz/transformation_permute_phi_operands.h
vendored
Normal file
56
3rdparty/spirv-tools/source/fuzz/transformation_permute_phi_operands.h
vendored
Normal file
@@ -0,0 +1,56 @@
|
||||
// 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_PERMUTE_PHI_OPERANDS_H_
|
||||
#define SOURCE_FUZZ_TRANSFORMATION_PERMUTE_PHI_OPERANDS_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 TransformationPermutePhiOperands : public Transformation {
|
||||
public:
|
||||
explicit TransformationPermutePhiOperands(
|
||||
const protobufs::TransformationPermutePhiOperands& message);
|
||||
|
||||
TransformationPermutePhiOperands(uint32_t result_id,
|
||||
const std::vector<uint32_t>& permutation);
|
||||
|
||||
// - |result_id| must be a valid id of some OpPhi instruction in the module.
|
||||
// - |permutation| must contain elements in the range [0, n/2 - 1] where |n|
|
||||
// is a number of operands to the instruction with |result_id|. All elements
|
||||
// must be unique (i.e. |permutation.size() == n / 2|).
|
||||
bool IsApplicable(
|
||||
opt::IRContext* ir_context,
|
||||
const TransformationContext& transformation_context) const override;
|
||||
|
||||
// Permutes operands of the OpPhi instruction with |result_id| according to
|
||||
// the elements in |permutation|.
|
||||
void Apply(opt::IRContext* ir_context,
|
||||
TransformationContext* transformation_context) const override;
|
||||
|
||||
protobufs::Transformation ToMessage() const override;
|
||||
|
||||
private:
|
||||
protobufs::TransformationPermutePhiOperands message_;
|
||||
};
|
||||
|
||||
} // namespace fuzz
|
||||
} // namespace spvtools
|
||||
|
||||
#endif // SOURCE_FUZZ_TRANSFORMATION_PERMUTE_PHI_OPERANDS_H_
|
||||
@@ -27,12 +27,13 @@ TransformationPushIdThroughVariable::TransformationPushIdThroughVariable(
|
||||
|
||||
TransformationPushIdThroughVariable::TransformationPushIdThroughVariable(
|
||||
uint32_t value_id, uint32_t value_synonym_id, uint32_t variable_id,
|
||||
uint32_t variable_storage_class,
|
||||
uint32_t variable_storage_class, uint32_t initializer_id,
|
||||
const protobufs::InstructionDescriptor& instruction_descriptor) {
|
||||
message_.set_value_id(value_id);
|
||||
message_.set_value_synonym_id(value_synonym_id);
|
||||
message_.set_variable_id(variable_id);
|
||||
message_.set_variable_storage_class(variable_storage_class);
|
||||
message_.set_initializer_id(initializer_id);
|
||||
*message_.mutable_instruction_descriptor() = instruction_descriptor;
|
||||
}
|
||||
|
||||
@@ -85,6 +86,14 @@ bool TransformationPushIdThroughVariable::IsApplicable(
|
||||
message_.variable_storage_class() == SpvStorageClassFunction) &&
|
||||
"The variable storage class must be private or function.");
|
||||
|
||||
// Check that initializer is valid.
|
||||
const auto* constant_inst =
|
||||
ir_context->get_def_use_mgr()->GetDef(message_.initializer_id());
|
||||
if (!constant_inst || !spvOpcodeIsConstant(constant_inst->opcode()) ||
|
||||
value_instruction->type_id() != constant_inst->type_id()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// |message_.value_id| must be available at the insertion point.
|
||||
return fuzzerutil::IdIsAvailableBeforeInstruction(
|
||||
ir_context, instruction_to_insert_before, message_.value_id());
|
||||
@@ -103,28 +112,23 @@ void TransformationPushIdThroughVariable::Apply(
|
||||
assert(pointer_type_id && "The required pointer type must be available.");
|
||||
|
||||
// Adds whether a global or local variable.
|
||||
fuzzerutil::UpdateModuleIdBound(ir_context, message_.variable_id());
|
||||
if (message_.variable_storage_class() == SpvStorageClassPrivate) {
|
||||
ir_context->module()->AddGlobalValue(MakeUnique<opt::Instruction>(
|
||||
ir_context, SpvOpVariable, pointer_type_id, message_.variable_id(),
|
||||
opt::Instruction::OperandList(
|
||||
{{SPV_OPERAND_TYPE_STORAGE_CLASS, {SpvStorageClassPrivate}}})));
|
||||
|
||||
fuzzerutil::AddVariableIdToEntryPointInterfaces(ir_context,
|
||||
message_.variable_id());
|
||||
fuzzerutil::AddGlobalVariable(ir_context, message_.variable_id(),
|
||||
pointer_type_id, SpvStorageClassPrivate,
|
||||
message_.initializer_id());
|
||||
} else {
|
||||
ir_context
|
||||
->get_instr_block(
|
||||
FindInstruction(message_.instruction_descriptor(), ir_context))
|
||||
->GetParent()
|
||||
->begin()
|
||||
->begin()
|
||||
->InsertBefore(MakeUnique<opt::Instruction>(
|
||||
ir_context, SpvOpVariable, pointer_type_id, message_.variable_id(),
|
||||
opt::Instruction::OperandList({{SPV_OPERAND_TYPE_STORAGE_CLASS,
|
||||
{SpvStorageClassFunction}}})));
|
||||
auto function_id = ir_context
|
||||
->get_instr_block(FindInstruction(
|
||||
message_.instruction_descriptor(), ir_context))
|
||||
->GetParent()
|
||||
->result_id();
|
||||
fuzzerutil::AddLocalVariable(ir_context, message_.variable_id(),
|
||||
pointer_type_id, function_id,
|
||||
message_.initializer_id());
|
||||
}
|
||||
|
||||
fuzzerutil::UpdateModuleIdBound(ir_context, message_.variable_id());
|
||||
|
||||
// Stores value id to variable id.
|
||||
FindInstruction(message_.instruction_descriptor(), ir_context)
|
||||
->InsertBefore(MakeUnique<opt::Instruction>(
|
||||
|
||||
@@ -31,6 +31,7 @@ class TransformationPushIdThroughVariable : public Transformation {
|
||||
TransformationPushIdThroughVariable(
|
||||
uint32_t value_id, uint32_t value_synonym_fresh_id,
|
||||
uint32_t variable_fresh_id, uint32_t variable_storage_class,
|
||||
uint32_t initializer_id,
|
||||
const protobufs::InstructionDescriptor& instruction_descriptor);
|
||||
|
||||
// - |message_.value_id| must be an instruction result id that has the same
|
||||
@@ -39,6 +40,9 @@ class TransformationPushIdThroughVariable : public Transformation {
|
||||
// - |message_.variable_id| must be fresh
|
||||
// - |message_.variable_storage_class| must be either StorageClassPrivate or
|
||||
// StorageClassFunction
|
||||
// - |message_.initializer_id| must be a result id of some constant in the
|
||||
// module. Its type must be equal to the pointee type of the variable that
|
||||
// will be created.
|
||||
// - |message_.instruction_descriptor| must identify an instruction
|
||||
// which it is valid to insert the OpStore and OpLoad instructions before it
|
||||
// and must be belongs to a reachable block.
|
||||
|
||||
@@ -46,6 +46,7 @@ bool TransformationReplaceLinearAlgebraInstruction::IsApplicable(
|
||||
// the following conditional can use the function |spvOpcodeIsLinearAlgebra|.
|
||||
// It must be a supported linear algebra instruction.
|
||||
if (instruction->opcode() != SpvOpVectorTimesScalar &&
|
||||
instruction->opcode() != SpvOpMatrixTimesScalar &&
|
||||
instruction->opcode() != SpvOpDot) {
|
||||
return false;
|
||||
}
|
||||
@@ -77,6 +78,9 @@ void TransformationReplaceLinearAlgebraInstruction::Apply(
|
||||
case SpvOpVectorTimesScalar:
|
||||
ReplaceOpVectorTimesScalar(ir_context, linear_algebra_instruction);
|
||||
break;
|
||||
case SpvOpMatrixTimesScalar:
|
||||
ReplaceOpMatrixTimesScalar(ir_context, linear_algebra_instruction);
|
||||
break;
|
||||
case SpvOpDot:
|
||||
ReplaceOpDot(ir_context, linear_algebra_instruction);
|
||||
break;
|
||||
@@ -110,7 +114,21 @@ uint32_t TransformationReplaceLinearAlgebraInstruction::GetRequiredFreshIdCount(
|
||||
->type_id())
|
||||
->AsVector()
|
||||
->element_count();
|
||||
case SpvOpDot: {
|
||||
case SpvOpMatrixTimesScalar: {
|
||||
// For each matrix column, |1 + column.size| OpCompositeExtract,
|
||||
// |column.size| OpFMul and 1 OpCompositeConstruct instructions will be
|
||||
// inserted.
|
||||
auto matrix_instruction = ir_context->get_def_use_mgr()->GetDef(
|
||||
instruction->GetSingleWordInOperand(0));
|
||||
auto matrix_type =
|
||||
ir_context->get_type_mgr()->GetType(matrix_instruction->type_id());
|
||||
return 2 * matrix_type->AsMatrix()->element_count() *
|
||||
(1 + matrix_type->AsMatrix()
|
||||
->element_type()
|
||||
->AsVector()
|
||||
->element_count());
|
||||
}
|
||||
case SpvOpDot:
|
||||
// For each pair of vector components, 2 OpCompositeExtract and 1 OpFMul
|
||||
// will be inserted. The first two OpFMul instructions will result the
|
||||
// first OpFAdd instruction to be inserted. For each remaining OpFMul, 1
|
||||
@@ -124,7 +142,6 @@ uint32_t TransformationReplaceLinearAlgebraInstruction::GetRequiredFreshIdCount(
|
||||
->AsVector()
|
||||
->element_count() -
|
||||
2;
|
||||
}
|
||||
default:
|
||||
assert(false && "Unsupported linear algebra instruction.");
|
||||
return 0;
|
||||
@@ -179,6 +196,90 @@ void TransformationReplaceLinearAlgebraInstruction::ReplaceOpVectorTimesScalar(
|
||||
}
|
||||
}
|
||||
|
||||
void TransformationReplaceLinearAlgebraInstruction::ReplaceOpMatrixTimesScalar(
|
||||
opt::IRContext* ir_context,
|
||||
opt::Instruction* linear_algebra_instruction) const {
|
||||
// Gets OpMatrixTimesScalar in operands.
|
||||
auto matrix_instruction = ir_context->get_def_use_mgr()->GetDef(
|
||||
linear_algebra_instruction->GetSingleWordInOperand(0));
|
||||
auto scalar_instruction = ir_context->get_def_use_mgr()->GetDef(
|
||||
linear_algebra_instruction->GetSingleWordInOperand(1));
|
||||
|
||||
// Gets matrix information.
|
||||
uint32_t matrix_column_count = ir_context->get_type_mgr()
|
||||
->GetType(matrix_instruction->type_id())
|
||||
->AsMatrix()
|
||||
->element_count();
|
||||
auto matrix_column_type = ir_context->get_type_mgr()
|
||||
->GetType(matrix_instruction->type_id())
|
||||
->AsMatrix()
|
||||
->element_type();
|
||||
uint32_t matrix_column_size = matrix_column_type->AsVector()->element_count();
|
||||
|
||||
std::vector<uint32_t> composite_construct_ids(matrix_column_count);
|
||||
uint32_t fresh_id_index = 0;
|
||||
|
||||
for (uint32_t i = 0; i < matrix_column_count; i++) {
|
||||
// Extracts |matrix| column.
|
||||
uint32_t matrix_extract_id = message_.fresh_ids(fresh_id_index++);
|
||||
fuzzerutil::UpdateModuleIdBound(ir_context, matrix_extract_id);
|
||||
linear_algebra_instruction->InsertBefore(MakeUnique<opt::Instruction>(
|
||||
ir_context, SpvOpCompositeExtract,
|
||||
ir_context->get_type_mgr()->GetId(matrix_column_type),
|
||||
matrix_extract_id,
|
||||
opt::Instruction::OperandList(
|
||||
{{SPV_OPERAND_TYPE_ID, {matrix_instruction->result_id()}},
|
||||
{SPV_OPERAND_TYPE_LITERAL_INTEGER, {i}}})));
|
||||
|
||||
std::vector<uint32_t> float_multiplication_ids(matrix_column_size);
|
||||
|
||||
for (uint32_t j = 0; j < matrix_column_size; j++) {
|
||||
// Extracts |column| component.
|
||||
uint32_t column_extract_id = message_.fresh_ids(fresh_id_index++);
|
||||
fuzzerutil::UpdateModuleIdBound(ir_context, column_extract_id);
|
||||
linear_algebra_instruction->InsertBefore(MakeUnique<opt::Instruction>(
|
||||
ir_context, SpvOpCompositeExtract, scalar_instruction->type_id(),
|
||||
column_extract_id,
|
||||
opt::Instruction::OperandList(
|
||||
{{SPV_OPERAND_TYPE_ID, {matrix_extract_id}},
|
||||
{SPV_OPERAND_TYPE_LITERAL_INTEGER, {j}}})));
|
||||
|
||||
// Multiplies the |column| component with the |scalar|.
|
||||
float_multiplication_ids[j] = message_.fresh_ids(fresh_id_index++);
|
||||
fuzzerutil::UpdateModuleIdBound(ir_context, float_multiplication_ids[j]);
|
||||
linear_algebra_instruction->InsertBefore(MakeUnique<opt::Instruction>(
|
||||
ir_context, SpvOpFMul, scalar_instruction->type_id(),
|
||||
float_multiplication_ids[j],
|
||||
opt::Instruction::OperandList(
|
||||
{{SPV_OPERAND_TYPE_ID, {column_extract_id}},
|
||||
{SPV_OPERAND_TYPE_ID, {scalar_instruction->result_id()}}})));
|
||||
}
|
||||
|
||||
// Constructs a new column multiplied by |scalar|.
|
||||
opt::Instruction::OperandList composite_construct_in_operands;
|
||||
for (uint32_t& float_multiplication_id : float_multiplication_ids) {
|
||||
composite_construct_in_operands.push_back(
|
||||
{SPV_OPERAND_TYPE_ID, {float_multiplication_id}});
|
||||
}
|
||||
composite_construct_ids[i] = message_.fresh_ids(fresh_id_index++);
|
||||
fuzzerutil::UpdateModuleIdBound(ir_context, composite_construct_ids[i]);
|
||||
linear_algebra_instruction->InsertBefore(MakeUnique<opt::Instruction>(
|
||||
ir_context, SpvOpCompositeConstruct,
|
||||
ir_context->get_type_mgr()->GetId(matrix_column_type),
|
||||
composite_construct_ids[i], composite_construct_in_operands));
|
||||
}
|
||||
|
||||
// The OpMatrixTimesScalar instruction is changed to an OpCompositeConstruct
|
||||
// instruction.
|
||||
linear_algebra_instruction->SetOpcode(SpvOpCompositeConstruct);
|
||||
linear_algebra_instruction->SetInOperand(0, {composite_construct_ids[0]});
|
||||
linear_algebra_instruction->SetInOperand(1, {composite_construct_ids[1]});
|
||||
for (uint32_t i = 2; i < composite_construct_ids.size(); i++) {
|
||||
linear_algebra_instruction->AddOperand(
|
||||
{SPV_OPERAND_TYPE_ID, {composite_construct_ids[i]}});
|
||||
}
|
||||
}
|
||||
|
||||
void TransformationReplaceLinearAlgebraInstruction::ReplaceOpDot(
|
||||
opt::IRContext* ir_context,
|
||||
opt::Instruction* linear_algebra_instruction) const {
|
||||
|
||||
@@ -56,6 +56,10 @@ class TransformationReplaceLinearAlgebraInstruction : public Transformation {
|
||||
void ReplaceOpVectorTimesScalar(opt::IRContext* ir_context,
|
||||
opt::Instruction* instruction) const;
|
||||
|
||||
// Replaces an OpMatrixTimesScalar instruction.
|
||||
void ReplaceOpMatrixTimesScalar(opt::IRContext* ir_context,
|
||||
opt::Instruction* instruction) const;
|
||||
|
||||
// Replaces an OpDot instruction.
|
||||
void ReplaceOpDot(opt::IRContext* ir_context,
|
||||
opt::Instruction* instruction) const;
|
||||
|
||||
104
3rdparty/spirv-tools/source/fuzz/transformation_swap_conditional_branch_operands.cpp
vendored
Normal file
104
3rdparty/spirv-tools/source/fuzz/transformation_swap_conditional_branch_operands.cpp
vendored
Normal file
@@ -0,0 +1,104 @@
|
||||
// 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_swap_conditional_branch_operands.h"
|
||||
#include "source/fuzz/fuzzer_util.h"
|
||||
#include "source/fuzz/instruction_descriptor.h"
|
||||
|
||||
namespace spvtools {
|
||||
namespace fuzz {
|
||||
|
||||
TransformationSwapConditionalBranchOperands::
|
||||
TransformationSwapConditionalBranchOperands(
|
||||
const spvtools::fuzz::protobufs::
|
||||
TransformationSwapConditionalBranchOperands& message)
|
||||
: message_(message) {}
|
||||
|
||||
TransformationSwapConditionalBranchOperands::
|
||||
TransformationSwapConditionalBranchOperands(
|
||||
const protobufs::InstructionDescriptor& instruction_descriptor,
|
||||
uint32_t fresh_id) {
|
||||
*message_.mutable_instruction_descriptor() = instruction_descriptor;
|
||||
message_.set_fresh_id(fresh_id);
|
||||
}
|
||||
|
||||
bool TransformationSwapConditionalBranchOperands::IsApplicable(
|
||||
opt::IRContext* ir_context, const TransformationContext& /*unused*/) const {
|
||||
const auto* inst =
|
||||
FindInstruction(message_.instruction_descriptor(), ir_context);
|
||||
return fuzzerutil::IsFreshId(ir_context, message_.fresh_id()) && inst &&
|
||||
inst->opcode() == SpvOpBranchConditional;
|
||||
}
|
||||
|
||||
void TransformationSwapConditionalBranchOperands::Apply(
|
||||
opt::IRContext* ir_context, TransformationContext* /*unused*/) const {
|
||||
auto* branch_inst =
|
||||
FindInstruction(message_.instruction_descriptor(), ir_context);
|
||||
assert(branch_inst);
|
||||
|
||||
auto* block = ir_context->get_instr_block(branch_inst);
|
||||
assert(block);
|
||||
|
||||
// Compute the last instruction in the |block| that allows us to insert
|
||||
// OpLogicalNot above it.
|
||||
auto iter = fuzzerutil::GetIteratorForInstruction(block, branch_inst);
|
||||
if (!fuzzerutil::CanInsertOpcodeBeforeInstruction(SpvOpLogicalNot, iter)) {
|
||||
// There might be a merge instruction before OpBranchConditional.
|
||||
--iter;
|
||||
}
|
||||
|
||||
assert(fuzzerutil::CanInsertOpcodeBeforeInstruction(SpvOpLogicalNot, iter) &&
|
||||
"We should now be able to insert SpvOpLogicalNot before |iter|");
|
||||
|
||||
// Get the instruction whose result is used as a condition for
|
||||
// OpBranchConditional.
|
||||
const auto* condition_inst = ir_context->get_def_use_mgr()->GetDef(
|
||||
branch_inst->GetSingleWordInOperand(0));
|
||||
assert(condition_inst);
|
||||
|
||||
// We are swapping the labels in OpBranchConditional. This means that we must
|
||||
// invert the guard as well. We are using OpLogicalNot for that purpose here.
|
||||
iter.InsertBefore(MakeUnique<opt::Instruction>(
|
||||
ir_context, SpvOpLogicalNot, condition_inst->type_id(),
|
||||
message_.fresh_id(),
|
||||
opt::Instruction::OperandList{
|
||||
{SPV_OPERAND_TYPE_ID, {condition_inst->result_id()}}}));
|
||||
|
||||
fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_id());
|
||||
|
||||
// Update OpBranchConditional condition operand.
|
||||
branch_inst->GetInOperand(0).words[0] = message_.fresh_id();
|
||||
|
||||
// Swap label operands.
|
||||
std::swap(branch_inst->GetInOperand(1), branch_inst->GetInOperand(2));
|
||||
|
||||
// Additionally, swap branch weights if present.
|
||||
if (branch_inst->NumInOperands() > 3) {
|
||||
std::swap(branch_inst->GetInOperand(3), branch_inst->GetInOperand(4));
|
||||
}
|
||||
|
||||
// Make sure the changes are analyzed.
|
||||
ir_context->InvalidateAnalysesExceptFor(
|
||||
opt::IRContext::Analysis::kAnalysisNone);
|
||||
}
|
||||
|
||||
protobufs::Transformation
|
||||
TransformationSwapConditionalBranchOperands::ToMessage() const {
|
||||
protobufs::Transformation result;
|
||||
*result.mutable_swap_conditional_branch_operands() = message_;
|
||||
return result;
|
||||
}
|
||||
|
||||
} // namespace fuzz
|
||||
} // namespace spvtools
|
||||
58
3rdparty/spirv-tools/source/fuzz/transformation_swap_conditional_branch_operands.h
vendored
Normal file
58
3rdparty/spirv-tools/source/fuzz/transformation_swap_conditional_branch_operands.h
vendored
Normal file
@@ -0,0 +1,58 @@
|
||||
// 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_SWAP_CONDITIONAL_BRANCH_OPERANDS_H_
|
||||
#define SOURCE_FUZZ_TRANSFORMATION_SWAP_CONDITIONAL_BRANCH_OPERANDS_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 TransformationSwapConditionalBranchOperands : public Transformation {
|
||||
public:
|
||||
explicit TransformationSwapConditionalBranchOperands(
|
||||
const protobufs::TransformationSwapConditionalBranchOperands& message);
|
||||
|
||||
TransformationSwapConditionalBranchOperands(
|
||||
const protobufs::InstructionDescriptor& instruction_descriptor,
|
||||
uint32_t fresh_id);
|
||||
|
||||
// - |message_.instruction_descriptor| must be a valid descriptor of some
|
||||
// OpBranchConditional instruction in the module.
|
||||
// - |message_.fresh_id| must be a fresh id.
|
||||
bool IsApplicable(
|
||||
opt::IRContext* ir_context,
|
||||
const TransformationContext& transformation_context) const override;
|
||||
|
||||
// Inserts |%fresh_id = OpLogicalNot %bool_type_id %cond_id| before
|
||||
// |OpBranchConditional %cond_id %branch_a %branch_b [%weight_a %weight_b]|.
|
||||
// Replaces %cond_id with %fresh_id and swaps %branch_* and %weight_*
|
||||
// operands.
|
||||
void Apply(opt::IRContext* ir_context,
|
||||
TransformationContext* transformation_context) const override;
|
||||
|
||||
protobufs::Transformation ToMessage() const override;
|
||||
|
||||
private:
|
||||
protobufs::TransformationSwapConditionalBranchOperands message_;
|
||||
};
|
||||
|
||||
} // namespace fuzz
|
||||
} // namespace spvtools
|
||||
|
||||
#endif // SOURCE_FUZZ_TRANSFORMATION_SWAP_CONDITIONAL_BRANCH_OPERANDS_H_
|
||||
1
3rdparty/spirv-tools/source/name_mapper.cpp
vendored
1
3rdparty/spirv-tools/source/name_mapper.cpp
vendored
@@ -153,6 +153,7 @@ void FriendlyNameMapper::SaveBuiltInName(uint32_t target_id,
|
||||
CASE(SubgroupLocalInvocationId)
|
||||
GLCASE(VertexIndex)
|
||||
GLCASE(InstanceIndex)
|
||||
GLCASE(BaseInstance)
|
||||
CASE(SubgroupEqMaskKHR)
|
||||
CASE(SubgroupGeMaskKHR)
|
||||
CASE(SubgroupGtMaskKHR)
|
||||
|
||||
@@ -24,7 +24,18 @@ static const uint32_t kOpLineOperandLineIndex = 1;
|
||||
static const uint32_t kLineOperandIndexDebugFunction = 7;
|
||||
static const uint32_t kLineOperandIndexDebugLexicalBlock = 5;
|
||||
static const uint32_t kDebugFunctionOperandFunctionIndex = 13;
|
||||
static const uint32_t kDebugFunctionOperandParentIndex = 9;
|
||||
static const uint32_t kDebugTypeCompositeOperandParentIndex = 9;
|
||||
static const uint32_t kDebugLexicalBlockOperandParentIndex = 7;
|
||||
static const uint32_t kDebugInlinedAtOperandInlinedIndex = 6;
|
||||
static const uint32_t kDebugExpressOperandOperationIndex = 4;
|
||||
static const uint32_t kDebugDeclareOperandLocalVariableIndex = 4;
|
||||
static const uint32_t kDebugDeclareOperandVariableIndex = 5;
|
||||
static const uint32_t kDebugValueOperandLocalVariableIndex = 4;
|
||||
static const uint32_t kDebugValueOperandExpressionIndex = 6;
|
||||
static const uint32_t kDebugOperationOperandOperationIndex = 4;
|
||||
static const uint32_t kOpVariableOperandStorageClassIndex = 2;
|
||||
static const uint32_t kDebugLocalVariableOperandParentIndex = 9;
|
||||
|
||||
namespace spvtools {
|
||||
namespace opt {
|
||||
@@ -36,7 +47,8 @@ void SetInlinedOperand(Instruction* dbg_inlined_at, uint32_t inlined_operand) {
|
||||
assert(dbg_inlined_at->GetOpenCL100DebugOpcode() ==
|
||||
OpenCLDebugInfo100DebugInlinedAt);
|
||||
if (dbg_inlined_at->NumOperands() <= kDebugInlinedAtOperandInlinedIndex) {
|
||||
dbg_inlined_at->AddOperand({SPV_OPERAND_TYPE_RESULT_ID, {inlined_operand}});
|
||||
dbg_inlined_at->AddOperand(
|
||||
{spv_operand_type_t::SPV_OPERAND_TYPE_ID, {inlined_operand}});
|
||||
} else {
|
||||
dbg_inlined_at->SetOperand(kDebugInlinedAtOperandInlinedIndex,
|
||||
{inlined_operand});
|
||||
@@ -53,6 +65,12 @@ uint32_t GetInlinedOperand(Instruction* dbg_inlined_at) {
|
||||
kDebugInlinedAtOperandInlinedIndex);
|
||||
}
|
||||
|
||||
bool IsEmptyDebugExpression(Instruction* instr) {
|
||||
return instr->GetOpenCL100DebugOpcode() ==
|
||||
OpenCLDebugInfo100DebugExpression &&
|
||||
instr->NumOperands() == kDebugExpressOperandOperationIndex;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
DebugInfoManager::DebugInfoManager(IRContext* c) : context_(c) {
|
||||
@@ -83,6 +101,20 @@ void DebugInfoManager::RegisterDbgFunction(Instruction* inst) {
|
||||
fn_id_to_dbg_fn_[fn_id] = inst;
|
||||
}
|
||||
|
||||
void DebugInfoManager::RegisterDbgDeclare(uint32_t var_id,
|
||||
Instruction* dbg_declare) {
|
||||
assert(dbg_declare->GetOpenCL100DebugOpcode() ==
|
||||
OpenCLDebugInfo100DebugDeclare ||
|
||||
dbg_declare->GetOpenCL100DebugOpcode() ==
|
||||
OpenCLDebugInfo100DebugValue);
|
||||
auto dbg_decl_itr = var_id_to_dbg_decl_.find(var_id);
|
||||
if (dbg_decl_itr == var_id_to_dbg_decl_.end()) {
|
||||
var_id_to_dbg_decl_[var_id] = {dbg_declare};
|
||||
} else {
|
||||
dbg_decl_itr->second.insert(dbg_declare);
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t DebugInfoManager::CreateDebugInlinedAt(const Instruction* line,
|
||||
const DebugScope& scope) {
|
||||
if (context()->get_feature_mgr()->GetExtInstImportId_OpenCL100DebugInfo() ==
|
||||
@@ -139,10 +171,12 @@ uint32_t DebugInfoManager::CreateDebugInlinedAt(const Instruction* line,
|
||||
// |scope| already has DebugInlinedAt. We put the existing DebugInlinedAt
|
||||
// into the Inlined operand of this new DebugInlinedAt.
|
||||
if (scope.GetInlinedAt() != kNoInlinedAt) {
|
||||
inlined_at->AddOperand({spv_operand_type_t::SPV_OPERAND_TYPE_RESULT_ID,
|
||||
{scope.GetInlinedAt()}});
|
||||
inlined_at->AddOperand(
|
||||
{spv_operand_type_t::SPV_OPERAND_TYPE_ID, {scope.GetInlinedAt()}});
|
||||
}
|
||||
RegisterDbgInst(inlined_at.get());
|
||||
if (context()->AreAnalysesValid(IRContext::Analysis::kAnalysisDefUse))
|
||||
context()->get_def_use_mgr()->AnalyzeInstDefUse(inlined_at.get());
|
||||
context()->module()->AddExtInstDebugInfo(std::move(inlined_at));
|
||||
return result_id;
|
||||
}
|
||||
@@ -218,11 +252,11 @@ Instruction* DebugInfoManager::GetDebugInfoNone() {
|
||||
context(), SpvOpExtInst, context()->get_type_mgr()->GetVoidTypeId(),
|
||||
result_id,
|
||||
{
|
||||
{SPV_OPERAND_TYPE_RESULT_ID,
|
||||
{spv_operand_type_t::SPV_OPERAND_TYPE_ID,
|
||||
{context()
|
||||
->get_feature_mgr()
|
||||
->GetExtInstImportId_OpenCL100DebugInfo()}},
|
||||
{SPV_OPERAND_TYPE_EXTENSION_INSTRUCTION_NUMBER,
|
||||
{spv_operand_type_t::SPV_OPERAND_TYPE_EXTENSION_INSTRUCTION_NUMBER,
|
||||
{static_cast<uint32_t>(OpenCLDebugInfo100DebugInfoNone)}},
|
||||
}));
|
||||
|
||||
@@ -232,9 +266,38 @@ Instruction* DebugInfoManager::GetDebugInfoNone() {
|
||||
std::move(dbg_info_none_inst));
|
||||
|
||||
RegisterDbgInst(debug_info_none_inst_);
|
||||
if (context()->AreAnalysesValid(IRContext::Analysis::kAnalysisDefUse))
|
||||
context()->get_def_use_mgr()->AnalyzeInstDefUse(debug_info_none_inst_);
|
||||
return debug_info_none_inst_;
|
||||
}
|
||||
|
||||
Instruction* DebugInfoManager::GetEmptyDebugExpression() {
|
||||
if (empty_debug_expr_inst_ != nullptr) return empty_debug_expr_inst_;
|
||||
|
||||
uint32_t result_id = context()->TakeNextId();
|
||||
std::unique_ptr<Instruction> empty_debug_expr(new Instruction(
|
||||
context(), SpvOpExtInst, context()->get_type_mgr()->GetVoidTypeId(),
|
||||
result_id,
|
||||
{
|
||||
{spv_operand_type_t::SPV_OPERAND_TYPE_ID,
|
||||
{context()
|
||||
->get_feature_mgr()
|
||||
->GetExtInstImportId_OpenCL100DebugInfo()}},
|
||||
{spv_operand_type_t::SPV_OPERAND_TYPE_EXTENSION_INSTRUCTION_NUMBER,
|
||||
{static_cast<uint32_t>(OpenCLDebugInfo100DebugExpression)}},
|
||||
}));
|
||||
|
||||
// Add to the front of |ext_inst_debuginfo_|.
|
||||
empty_debug_expr_inst_ =
|
||||
context()->module()->ext_inst_debuginfo_begin()->InsertBefore(
|
||||
std::move(empty_debug_expr));
|
||||
|
||||
RegisterDbgInst(empty_debug_expr_inst_);
|
||||
if (context()->AreAnalysesValid(IRContext::Analysis::kAnalysisDefUse))
|
||||
context()->get_def_use_mgr()->AnalyzeInstDefUse(empty_debug_expr_inst_);
|
||||
return empty_debug_expr_inst_;
|
||||
}
|
||||
|
||||
Instruction* DebugInfoManager::GetDebugInlinedAt(uint32_t dbg_inlined_at_id) {
|
||||
auto* inlined_at = GetDbgInst(dbg_inlined_at_id);
|
||||
if (inlined_at == nullptr) return nullptr;
|
||||
@@ -252,12 +315,162 @@ Instruction* DebugInfoManager::CloneDebugInlinedAt(uint32_t clone_inlined_at_id,
|
||||
std::unique_ptr<Instruction> new_inlined_at(inlined_at->Clone(context()));
|
||||
new_inlined_at->SetResultId(context()->TakeNextId());
|
||||
RegisterDbgInst(new_inlined_at.get());
|
||||
if (context()->AreAnalysesValid(IRContext::Analysis::kAnalysisDefUse))
|
||||
context()->get_def_use_mgr()->AnalyzeInstDefUse(new_inlined_at.get());
|
||||
if (insert_before != nullptr)
|
||||
return insert_before->InsertBefore(std::move(new_inlined_at));
|
||||
return context()->module()->ext_inst_debuginfo_end()->InsertBefore(
|
||||
std::move(new_inlined_at));
|
||||
}
|
||||
|
||||
uint32_t DebugInfoManager::GetParentScope(uint32_t child_scope) {
|
||||
auto dbg_scope_itr = id_to_dbg_inst_.find(child_scope);
|
||||
assert(dbg_scope_itr != id_to_dbg_inst_.end());
|
||||
OpenCLDebugInfo100Instructions debug_opcode =
|
||||
dbg_scope_itr->second->GetOpenCL100DebugOpcode();
|
||||
uint32_t parent_scope = kNoDebugScope;
|
||||
switch (debug_opcode) {
|
||||
case OpenCLDebugInfo100DebugFunction:
|
||||
parent_scope = dbg_scope_itr->second->GetSingleWordOperand(
|
||||
kDebugFunctionOperandParentIndex);
|
||||
break;
|
||||
case OpenCLDebugInfo100DebugLexicalBlock:
|
||||
parent_scope = dbg_scope_itr->second->GetSingleWordOperand(
|
||||
kDebugLexicalBlockOperandParentIndex);
|
||||
break;
|
||||
case OpenCLDebugInfo100DebugTypeComposite:
|
||||
parent_scope = dbg_scope_itr->second->GetSingleWordOperand(
|
||||
kDebugTypeCompositeOperandParentIndex);
|
||||
break;
|
||||
case OpenCLDebugInfo100DebugCompilationUnit:
|
||||
// DebugCompilationUnit does not have a parent scope.
|
||||
break;
|
||||
default:
|
||||
assert(false &&
|
||||
"Unreachable. A debug scope instruction must be "
|
||||
"DebugFunction, DebugTypeComposite, DebugLexicalBlock, "
|
||||
"or DebugCompilationUnit.");
|
||||
break;
|
||||
}
|
||||
return parent_scope;
|
||||
}
|
||||
|
||||
bool DebugInfoManager::IsAncestorOfScope(uint32_t scope, uint32_t ancestor) {
|
||||
uint32_t ancestor_scope_itr = scope;
|
||||
while (ancestor_scope_itr != kNoDebugScope) {
|
||||
if (ancestor == ancestor_scope_itr) return true;
|
||||
ancestor_scope_itr = GetParentScope(ancestor_scope_itr);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool DebugInfoManager::IsDeclareVisibleToInstr(Instruction* dbg_declare,
|
||||
uint32_t instr_scope_id) {
|
||||
if (instr_scope_id == kNoDebugScope) return false;
|
||||
|
||||
uint32_t dbg_local_var_id =
|
||||
dbg_declare->GetSingleWordOperand(kDebugDeclareOperandLocalVariableIndex);
|
||||
auto dbg_local_var_itr = id_to_dbg_inst_.find(dbg_local_var_id);
|
||||
assert(dbg_local_var_itr != id_to_dbg_inst_.end());
|
||||
uint32_t decl_scope_id = dbg_local_var_itr->second->GetSingleWordOperand(
|
||||
kDebugLocalVariableOperandParentIndex);
|
||||
|
||||
// If the scope of DebugDeclare is an ancestor scope of the instruction's
|
||||
// scope, the local variable is visible to the instruction.
|
||||
return IsAncestorOfScope(instr_scope_id, decl_scope_id);
|
||||
}
|
||||
|
||||
void DebugInfoManager::AddDebugValue(Instruction* scope_and_line,
|
||||
uint32_t variable_id, uint32_t value_id,
|
||||
Instruction* insert_pos) {
|
||||
auto dbg_decl_itr = var_id_to_dbg_decl_.find(variable_id);
|
||||
if (dbg_decl_itr == var_id_to_dbg_decl_.end()) return;
|
||||
|
||||
uint32_t instr_scope_id = scope_and_line->GetDebugScope().GetLexicalScope();
|
||||
for (auto* dbg_decl_or_val : dbg_decl_itr->second) {
|
||||
if (!IsDeclareVisibleToInstr(dbg_decl_or_val, instr_scope_id)) continue;
|
||||
|
||||
uint32_t result_id = context()->TakeNextId();
|
||||
std::unique_ptr<Instruction> new_dbg_value(new Instruction(
|
||||
context(), SpvOpExtInst, context()->get_type_mgr()->GetVoidTypeId(),
|
||||
result_id,
|
||||
{
|
||||
{spv_operand_type_t::SPV_OPERAND_TYPE_ID,
|
||||
{context()
|
||||
->get_feature_mgr()
|
||||
->GetExtInstImportId_OpenCL100DebugInfo()}},
|
||||
{spv_operand_type_t::SPV_OPERAND_TYPE_EXTENSION_INSTRUCTION_NUMBER,
|
||||
{static_cast<uint32_t>(OpenCLDebugInfo100DebugValue)}},
|
||||
{spv_operand_type_t::SPV_OPERAND_TYPE_ID,
|
||||
{dbg_decl_or_val->GetSingleWordOperand(
|
||||
kDebugValueOperandLocalVariableIndex)}},
|
||||
{spv_operand_type_t::SPV_OPERAND_TYPE_ID, {value_id}},
|
||||
{spv_operand_type_t::SPV_OPERAND_TYPE_ID,
|
||||
{GetEmptyDebugExpression()->result_id()}},
|
||||
}));
|
||||
|
||||
if (dbg_decl_or_val->NumOperands() >
|
||||
kDebugValueOperandExpressionIndex + 1) {
|
||||
assert(dbg_decl_or_val->GetOpenCL100DebugOpcode() ==
|
||||
OpenCLDebugInfo100DebugValue);
|
||||
for (uint32_t i = kDebugValueOperandExpressionIndex + 1;
|
||||
i < dbg_decl_or_val->NumOperands(); ++i) {
|
||||
new_dbg_value->AddOperand({spv_operand_type_t::SPV_OPERAND_TYPE_ID,
|
||||
{dbg_decl_or_val->GetSingleWordOperand(i)}});
|
||||
}
|
||||
}
|
||||
|
||||
// Avoid inserting the new DebugValue between OpPhi or OpVariable
|
||||
// instructions.
|
||||
Instruction* insert_before = insert_pos->NextNode();
|
||||
while (insert_before->opcode() == SpvOpPhi ||
|
||||
insert_before->opcode() == SpvOpVariable) {
|
||||
insert_before = insert_before->NextNode();
|
||||
}
|
||||
|
||||
Instruction* added_dbg_value =
|
||||
insert_before->InsertBefore(std::move(new_dbg_value));
|
||||
added_dbg_value->UpdateDebugInfo(scope_and_line);
|
||||
AnalyzeDebugInst(added_dbg_value);
|
||||
if (context()->AreAnalysesValid(IRContext::Analysis::kAnalysisDefUse))
|
||||
context()->get_def_use_mgr()->AnalyzeInstDefUse(added_dbg_value);
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t DebugInfoManager::GetVariableIdOfDebugValueUsedForDeclare(
|
||||
Instruction* inst) {
|
||||
if (inst->GetOpenCL100DebugOpcode() != OpenCLDebugInfo100DebugValue) return 0;
|
||||
|
||||
auto* expr =
|
||||
GetDbgInst(inst->GetSingleWordOperand(kDebugValueOperandExpressionIndex));
|
||||
if (expr == nullptr) return 0;
|
||||
if (expr->NumOperands() != kDebugExpressOperandOperationIndex + 1) return 0;
|
||||
|
||||
auto* operation = GetDbgInst(
|
||||
expr->GetSingleWordOperand(kDebugExpressOperandOperationIndex));
|
||||
if (operation == nullptr) return 0;
|
||||
if (operation->GetSingleWordOperand(kDebugOperationOperandOperationIndex) !=
|
||||
OpenCLDebugInfo100Deref) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint32_t var_id =
|
||||
inst->GetSingleWordOperand(kDebugDeclareOperandVariableIndex);
|
||||
if (!context()->AreAnalysesValid(IRContext::Analysis::kAnalysisDefUse)) {
|
||||
assert(false &&
|
||||
"Checking a DebugValue can be used for declare needs DefUseManager");
|
||||
return 0;
|
||||
}
|
||||
|
||||
auto* var = context()->get_def_use_mgr()->GetDef(var_id);
|
||||
if (var->opcode() == SpvOpVariable &&
|
||||
SpvStorageClass(var->GetSingleWordOperand(
|
||||
kOpVariableOperandStorageClassIndex)) == SpvStorageClassFunction) {
|
||||
return var_id;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void DebugInfoManager::AnalyzeDebugInst(Instruction* dbg_inst) {
|
||||
if (dbg_inst->GetOpenCL100DebugOpcode() == OpenCLDebugInfo100InstructionsMax)
|
||||
return;
|
||||
@@ -275,12 +488,37 @@ void DebugInfoManager::AnalyzeDebugInst(Instruction* dbg_inst) {
|
||||
dbg_inst->GetOpenCL100DebugOpcode() == OpenCLDebugInfo100DebugInfoNone) {
|
||||
debug_info_none_inst_ = dbg_inst;
|
||||
}
|
||||
|
||||
if (empty_debug_expr_inst_ == nullptr && IsEmptyDebugExpression(dbg_inst)) {
|
||||
empty_debug_expr_inst_ = dbg_inst;
|
||||
}
|
||||
|
||||
if (dbg_inst->GetOpenCL100DebugOpcode() == OpenCLDebugInfo100DebugDeclare) {
|
||||
uint32_t var_id =
|
||||
dbg_inst->GetSingleWordOperand(kDebugDeclareOperandVariableIndex);
|
||||
RegisterDbgDeclare(var_id, dbg_inst);
|
||||
}
|
||||
|
||||
if (uint32_t var_id = GetVariableIdOfDebugValueUsedForDeclare(dbg_inst)) {
|
||||
RegisterDbgDeclare(var_id, dbg_inst);
|
||||
}
|
||||
}
|
||||
|
||||
void DebugInfoManager::AnalyzeDebugInsts(Module& module) {
|
||||
debug_info_none_inst_ = nullptr;
|
||||
empty_debug_expr_inst_ = nullptr;
|
||||
module.ForEachInst([this](Instruction* cpi) { AnalyzeDebugInst(cpi); });
|
||||
|
||||
// Move |empty_debug_expr_inst_| to the beginning of the debug instruction
|
||||
// list.
|
||||
if (empty_debug_expr_inst_ != nullptr &&
|
||||
empty_debug_expr_inst_->PreviousNode() != nullptr &&
|
||||
empty_debug_expr_inst_->PreviousNode()->GetOpenCL100DebugOpcode() !=
|
||||
OpenCLDebugInfo100InstructionsMax) {
|
||||
empty_debug_expr_inst_->InsertBefore(
|
||||
&*context()->module()->ext_inst_debuginfo_begin());
|
||||
}
|
||||
|
||||
// Move |debug_info_none_inst_| to the beginning of the debug instruction
|
||||
// list.
|
||||
if (debug_info_none_inst_ != nullptr &&
|
||||
@@ -292,6 +530,55 @@ void DebugInfoManager::AnalyzeDebugInsts(Module& module) {
|
||||
}
|
||||
}
|
||||
|
||||
void DebugInfoManager::ClearDebugInfo(Instruction* instr) {
|
||||
if (instr == nullptr ||
|
||||
instr->GetOpenCL100DebugOpcode() == OpenCLDebugInfo100InstructionsMax) {
|
||||
return;
|
||||
}
|
||||
|
||||
id_to_dbg_inst_.erase(instr->result_id());
|
||||
|
||||
if (instr->GetOpenCL100DebugOpcode() == OpenCLDebugInfo100DebugFunction) {
|
||||
auto fn_id =
|
||||
instr->GetSingleWordOperand(kDebugFunctionOperandFunctionIndex);
|
||||
fn_id_to_dbg_fn_.erase(fn_id);
|
||||
}
|
||||
|
||||
if (instr->GetOpenCL100DebugOpcode() == OpenCLDebugInfo100DebugDeclare ||
|
||||
instr->GetOpenCL100DebugOpcode() == OpenCLDebugInfo100DebugValue) {
|
||||
auto var_or_value_id =
|
||||
instr->GetSingleWordOperand(kDebugDeclareOperandVariableIndex);
|
||||
auto dbg_decl_itr = var_id_to_dbg_decl_.find(var_or_value_id);
|
||||
if (dbg_decl_itr != var_id_to_dbg_decl_.end()) {
|
||||
dbg_decl_itr->second.erase(instr);
|
||||
}
|
||||
}
|
||||
|
||||
if (debug_info_none_inst_ == instr) {
|
||||
debug_info_none_inst_ = nullptr;
|
||||
for (auto dbg_instr_itr = context()->module()->ext_inst_debuginfo_begin();
|
||||
dbg_instr_itr != context()->module()->ext_inst_debuginfo_end();
|
||||
++dbg_instr_itr) {
|
||||
if (instr != &*dbg_instr_itr &&
|
||||
dbg_instr_itr->GetOpenCL100DebugOpcode() ==
|
||||
OpenCLDebugInfo100DebugInfoNone) {
|
||||
debug_info_none_inst_ = &*dbg_instr_itr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (empty_debug_expr_inst_ == instr) {
|
||||
empty_debug_expr_inst_ = nullptr;
|
||||
for (auto dbg_instr_itr = context()->module()->ext_inst_debuginfo_begin();
|
||||
dbg_instr_itr != context()->module()->ext_inst_debuginfo_end();
|
||||
++dbg_instr_itr) {
|
||||
if (instr != &*dbg_instr_itr && IsEmptyDebugExpression(&*dbg_instr_itr)) {
|
||||
empty_debug_expr_inst_ = &*dbg_instr_itr;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace analysis
|
||||
} // namespace opt
|
||||
} // namespace spvtools
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
#define SOURCE_OPT_DEBUG_INFO_MANAGER_H_
|
||||
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
|
||||
#include "source/opt/instruction.h"
|
||||
#include "source/opt/module.h"
|
||||
@@ -128,6 +129,15 @@ class DebugInfoManager {
|
||||
uint32_t BuildDebugInlinedAtChain(uint32_t callee_inlined_at,
|
||||
DebugInlinedAtContext* inlined_at_ctx);
|
||||
|
||||
// Generates a DebugValue instruction with value |value_id| for every local
|
||||
// variable that is in the scope of |scope_and_line| and whose memory is
|
||||
// |variable_id| and inserts it after the instruction |insert_pos|.
|
||||
void AddDebugValue(Instruction* scope_and_line, uint32_t variable_id,
|
||||
uint32_t value_id, Instruction* insert_pos);
|
||||
|
||||
// Erases |instr| from data structures of this class.
|
||||
void ClearDebugInfo(Instruction* instr);
|
||||
|
||||
private:
|
||||
IRContext* context() { return context_; }
|
||||
|
||||
@@ -147,6 +157,31 @@ class DebugInfoManager {
|
||||
// in |inst| must not already be registered.
|
||||
void RegisterDbgFunction(Instruction* inst);
|
||||
|
||||
// Register the DebugDeclare or DebugValue with Deref operation
|
||||
// |dbg_declare| into |var_id_to_dbg_decl_| using OpVariable id
|
||||
// |var_id| as a key.
|
||||
void RegisterDbgDeclare(uint32_t var_id, Instruction* dbg_declare);
|
||||
|
||||
// Returns a DebugExpression instruction without Operation operands.
|
||||
Instruction* GetEmptyDebugExpression();
|
||||
|
||||
// Returns the id of Value operand if |inst| is DebugValue who has Deref
|
||||
// operation and its Value operand is a result id of OpVariable with
|
||||
// Function storage class. Otherwise, returns 0.
|
||||
uint32_t GetVariableIdOfDebugValueUsedForDeclare(Instruction* inst);
|
||||
|
||||
// Returns true if a scope |ancestor| is |scope| or an ancestor scope
|
||||
// of |scope|.
|
||||
bool IsAncestorOfScope(uint32_t scope, uint32_t ancestor);
|
||||
|
||||
// Returns true if the declaration of a local variable |dbg_declare|
|
||||
// is visible in the scope of an instruction |instr_scope_id|.
|
||||
bool IsDeclareVisibleToInstr(Instruction* dbg_declare,
|
||||
uint32_t instr_scope_id);
|
||||
|
||||
// Returns the parent scope of the scope |child_scope|.
|
||||
uint32_t GetParentScope(uint32_t child_scope);
|
||||
|
||||
IRContext* context_;
|
||||
|
||||
// Mapping from ids of OpenCL.DebugInfo.100 extension instructions
|
||||
@@ -157,9 +192,19 @@ class DebugInfoManager {
|
||||
// operand is the function.
|
||||
std::unordered_map<uint32_t, Instruction*> fn_id_to_dbg_fn_;
|
||||
|
||||
// Mapping from variable or value ids to DebugDeclare or DebugValue
|
||||
// instructions whose operand is the variable or value.
|
||||
std::unordered_map<uint32_t, std::unordered_set<Instruction*>>
|
||||
var_id_to_dbg_decl_;
|
||||
|
||||
// DebugInfoNone instruction. We need only a single DebugInfoNone.
|
||||
// To reuse the existing one, we keep it using this member variable.
|
||||
Instruction* debug_info_none_inst_;
|
||||
|
||||
// DebugExpression instruction without Operation operands. We need only
|
||||
// a single DebugExpression without Operation operands. To reuse the
|
||||
// existing one, we keep it using this member variable.
|
||||
Instruction* empty_debug_expr_inst_;
|
||||
};
|
||||
|
||||
} // namespace analysis
|
||||
|
||||
233
3rdparty/spirv-tools/source/opt/desc_sroa.cpp
vendored
233
3rdparty/spirv-tools/source/opt/desc_sroa.cpp
vendored
@@ -56,7 +56,23 @@ bool DescriptorScalarReplacement::IsCandidate(Instruction* var) {
|
||||
uint32_t var_type_id = ptr_type_inst->GetSingleWordInOperand(1);
|
||||
Instruction* var_type_inst =
|
||||
context()->get_def_use_mgr()->GetDef(var_type_id);
|
||||
if (var_type_inst->opcode() != SpvOpTypeArray) {
|
||||
if (var_type_inst->opcode() != SpvOpTypeArray &&
|
||||
var_type_inst->opcode() != SpvOpTypeStruct) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// All structures with descriptor assignments must be replaced by variables,
|
||||
// one for each of their members - with the exceptions of buffers.
|
||||
// Buffers are represented as structures, but we shouldn't replace a buffer
|
||||
// with its elements. All buffers have offset decorations for members of their
|
||||
// structure types.
|
||||
bool has_offset_decoration = false;
|
||||
context()->get_decoration_mgr()->ForEachDecoration(
|
||||
var_type_inst->result_id(), SpvDecorationOffset,
|
||||
[&has_offset_decoration](const Instruction&) {
|
||||
has_offset_decoration = true;
|
||||
});
|
||||
if (has_offset_decoration) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -84,9 +100,11 @@ bool DescriptorScalarReplacement::IsCandidate(Instruction* var) {
|
||||
}
|
||||
|
||||
bool DescriptorScalarReplacement::ReplaceCandidate(Instruction* var) {
|
||||
std::vector<Instruction*> work_list;
|
||||
std::vector<Instruction*> access_chain_work_list;
|
||||
std::vector<Instruction*> load_work_list;
|
||||
bool failed = !get_def_use_mgr()->WhileEachUser(
|
||||
var->result_id(), [this, &work_list](Instruction* use) {
|
||||
var->result_id(),
|
||||
[this, &access_chain_work_list, &load_work_list](Instruction* use) {
|
||||
if (use->opcode() == SpvOpName) {
|
||||
return true;
|
||||
}
|
||||
@@ -98,7 +116,10 @@ bool DescriptorScalarReplacement::ReplaceCandidate(Instruction* var) {
|
||||
switch (use->opcode()) {
|
||||
case SpvOpAccessChain:
|
||||
case SpvOpInBoundsAccessChain:
|
||||
work_list.push_back(use);
|
||||
access_chain_work_list.push_back(use);
|
||||
return true;
|
||||
case SpvOpLoad:
|
||||
load_work_list.push_back(use);
|
||||
return true;
|
||||
default:
|
||||
context()->EmitErrorMessage(
|
||||
@@ -112,11 +133,16 @@ bool DescriptorScalarReplacement::ReplaceCandidate(Instruction* var) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (Instruction* use : work_list) {
|
||||
for (Instruction* use : access_chain_work_list) {
|
||||
if (!ReplaceAccessChain(var, use)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
for (Instruction* use : load_work_list) {
|
||||
if (!ReplaceLoadedValue(var, use)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -177,21 +203,36 @@ uint32_t DescriptorScalarReplacement::GetReplacementVariable(Instruction* var,
|
||||
uint32_t ptr_type_id = var->type_id();
|
||||
Instruction* ptr_type_inst = get_def_use_mgr()->GetDef(ptr_type_id);
|
||||
assert(ptr_type_inst->opcode() == SpvOpTypePointer &&
|
||||
"Variable should be a pointer to an array.");
|
||||
uint32_t arr_type_id = ptr_type_inst->GetSingleWordInOperand(1);
|
||||
Instruction* arr_type_inst = get_def_use_mgr()->GetDef(arr_type_id);
|
||||
assert(arr_type_inst->opcode() == SpvOpTypeArray &&
|
||||
"Variable should be a pointer to an array.");
|
||||
"Variable should be a pointer to an array or structure.");
|
||||
uint32_t pointee_type_id = ptr_type_inst->GetSingleWordInOperand(1);
|
||||
Instruction* pointee_type_inst = get_def_use_mgr()->GetDef(pointee_type_id);
|
||||
const bool is_array = pointee_type_inst->opcode() == SpvOpTypeArray;
|
||||
const bool is_struct = pointee_type_inst->opcode() == SpvOpTypeStruct;
|
||||
assert((is_array || is_struct) &&
|
||||
"Variable should be a pointer to an array or structure.");
|
||||
|
||||
uint32_t array_len_id = arr_type_inst->GetSingleWordInOperand(1);
|
||||
const analysis::Constant* array_len_const =
|
||||
context()->get_constant_mgr()->FindDeclaredConstant(array_len_id);
|
||||
assert(array_len_const != nullptr && "Array length must be a constant.");
|
||||
uint32_t array_len = array_len_const->GetU32();
|
||||
// For arrays, each array element should be replaced with a new replacement
|
||||
// variable
|
||||
if (is_array) {
|
||||
uint32_t array_len_id = pointee_type_inst->GetSingleWordInOperand(1);
|
||||
const analysis::Constant* array_len_const =
|
||||
context()->get_constant_mgr()->FindDeclaredConstant(array_len_id);
|
||||
assert(array_len_const != nullptr && "Array length must be a constant.");
|
||||
uint32_t array_len = array_len_const->GetU32();
|
||||
|
||||
replacement_vars = replacement_variables_
|
||||
.insert({var, std::vector<uint32_t>(array_len, 0)})
|
||||
.first;
|
||||
replacement_vars = replacement_variables_
|
||||
.insert({var, std::vector<uint32_t>(array_len, 0)})
|
||||
.first;
|
||||
}
|
||||
// For structures, each member should be replaced with a new replacement
|
||||
// variable
|
||||
if (is_struct) {
|
||||
const uint32_t num_members = pointee_type_inst->NumInOperands();
|
||||
replacement_vars =
|
||||
replacement_variables_
|
||||
.insert({var, std::vector<uint32_t>(num_members, 0)})
|
||||
.first;
|
||||
}
|
||||
}
|
||||
|
||||
if (replacement_vars->second[idx] == 0) {
|
||||
@@ -212,12 +253,17 @@ uint32_t DescriptorScalarReplacement::CreateReplacementVariable(
|
||||
uint32_t ptr_type_id = var->type_id();
|
||||
Instruction* ptr_type_inst = get_def_use_mgr()->GetDef(ptr_type_id);
|
||||
assert(ptr_type_inst->opcode() == SpvOpTypePointer &&
|
||||
"Variable should be a pointer to an array.");
|
||||
uint32_t arr_type_id = ptr_type_inst->GetSingleWordInOperand(1);
|
||||
Instruction* arr_type_inst = get_def_use_mgr()->GetDef(arr_type_id);
|
||||
assert(arr_type_inst->opcode() == SpvOpTypeArray &&
|
||||
"Variable should be a pointer to an array.");
|
||||
uint32_t element_type_id = arr_type_inst->GetSingleWordInOperand(0);
|
||||
"Variable should be a pointer to an array or structure.");
|
||||
uint32_t pointee_type_id = ptr_type_inst->GetSingleWordInOperand(1);
|
||||
Instruction* pointee_type_inst = get_def_use_mgr()->GetDef(pointee_type_id);
|
||||
const bool is_array = pointee_type_inst->opcode() == SpvOpTypeArray;
|
||||
const bool is_struct = pointee_type_inst->opcode() == SpvOpTypeStruct;
|
||||
assert((is_array || is_struct) &&
|
||||
"Variable should be a pointer to an array or structure.");
|
||||
|
||||
uint32_t element_type_id =
|
||||
is_array ? pointee_type_inst->GetSingleWordInOperand(0)
|
||||
: pointee_type_inst->GetSingleWordInOperand(idx);
|
||||
|
||||
uint32_t ptr_element_type_id = context()->get_type_mgr()->FindPointerToType(
|
||||
element_type_id, storage_class);
|
||||
@@ -242,19 +288,42 @@ uint32_t DescriptorScalarReplacement::CreateReplacementVariable(
|
||||
|
||||
uint32_t decoration = new_decoration->GetSingleWordInOperand(1u);
|
||||
if (decoration == SpvDecorationBinding) {
|
||||
uint32_t new_binding = new_decoration->GetSingleWordInOperand(2) + idx;
|
||||
uint32_t new_binding = new_decoration->GetSingleWordInOperand(2);
|
||||
if (is_array) {
|
||||
new_binding += idx * GetNumBindingsUsedByType(ptr_element_type_id);
|
||||
}
|
||||
if (is_struct) {
|
||||
// The binding offset that should be added is the sum of binding numbers
|
||||
// used by previous members of the current struct.
|
||||
for (uint32_t i = 0; i < idx; ++i) {
|
||||
new_binding += GetNumBindingsUsedByType(
|
||||
pointee_type_inst->GetSingleWordInOperand(i));
|
||||
}
|
||||
}
|
||||
new_decoration->SetInOperand(2, {new_binding});
|
||||
}
|
||||
context()->AddAnnotationInst(std::move(new_decoration));
|
||||
}
|
||||
|
||||
// Create a new OpName for the replacement variable.
|
||||
std::vector<std::unique_ptr<Instruction>> names_to_add;
|
||||
for (auto p : context()->GetNames(var->result_id())) {
|
||||
Instruction* name_inst = p.second;
|
||||
std::string name_str = utils::MakeString(name_inst->GetOperand(1).words);
|
||||
name_str += "[";
|
||||
name_str += utils::ToString(idx);
|
||||
name_str += "]";
|
||||
if (is_array) {
|
||||
name_str += "[" + utils::ToString(idx) + "]";
|
||||
}
|
||||
if (is_struct) {
|
||||
Instruction* member_name_inst =
|
||||
context()->GetMemberName(pointee_type_inst->result_id(), idx);
|
||||
name_str += ".";
|
||||
if (member_name_inst)
|
||||
name_str += utils::MakeString(member_name_inst->GetOperand(2).words);
|
||||
else
|
||||
// In case the member does not have a name assigned to it, use the
|
||||
// member index.
|
||||
name_str += utils::ToString(idx);
|
||||
}
|
||||
|
||||
std::unique_ptr<Instruction> new_name(new Instruction(
|
||||
context(), SpvOpName, 0, 0,
|
||||
@@ -262,12 +331,118 @@ uint32_t DescriptorScalarReplacement::CreateReplacementVariable(
|
||||
{SPV_OPERAND_TYPE_ID, {id}},
|
||||
{SPV_OPERAND_TYPE_LITERAL_STRING, utils::MakeVector(name_str)}}));
|
||||
Instruction* new_name_inst = new_name.get();
|
||||
context()->AddDebug2Inst(std::move(new_name));
|
||||
get_def_use_mgr()->AnalyzeInstDefUse(new_name_inst);
|
||||
names_to_add.push_back(std::move(new_name));
|
||||
}
|
||||
|
||||
// We shouldn't add the new names when we are iterating over name ranges
|
||||
// above. We can add all the new names now.
|
||||
for (auto& new_name : names_to_add)
|
||||
context()->AddDebug2Inst(std::move(new_name));
|
||||
|
||||
return id;
|
||||
}
|
||||
|
||||
uint32_t DescriptorScalarReplacement::GetNumBindingsUsedByType(
|
||||
uint32_t type_id) {
|
||||
Instruction* type_inst = get_def_use_mgr()->GetDef(type_id);
|
||||
|
||||
// If it's a pointer, look at the underlying type.
|
||||
if (type_inst->opcode() == SpvOpTypePointer) {
|
||||
type_id = type_inst->GetSingleWordInOperand(1);
|
||||
type_inst = get_def_use_mgr()->GetDef(type_id);
|
||||
}
|
||||
|
||||
// Arrays consume N*M binding numbers where N is the array length, and M is
|
||||
// the number of bindings used by each array element.
|
||||
if (type_inst->opcode() == SpvOpTypeArray) {
|
||||
uint32_t element_type_id = type_inst->GetSingleWordInOperand(0);
|
||||
uint32_t length_id = type_inst->GetSingleWordInOperand(1);
|
||||
const analysis::Constant* length_const =
|
||||
context()->get_constant_mgr()->FindDeclaredConstant(length_id);
|
||||
// OpTypeArray's length must always be a constant
|
||||
assert(length_const != nullptr);
|
||||
uint32_t num_elems = length_const->GetU32();
|
||||
return num_elems * GetNumBindingsUsedByType(element_type_id);
|
||||
}
|
||||
|
||||
// The number of bindings consumed by a structure is the sum of the bindings
|
||||
// used by its members.
|
||||
if (type_inst->opcode() == SpvOpTypeStruct) {
|
||||
uint32_t sum = 0;
|
||||
for (uint32_t i = 0; i < type_inst->NumInOperands(); i++)
|
||||
sum += GetNumBindingsUsedByType(type_inst->GetSingleWordInOperand(i));
|
||||
return sum;
|
||||
}
|
||||
|
||||
// All other types are considered to take up 1 binding number.
|
||||
return 1;
|
||||
}
|
||||
|
||||
bool DescriptorScalarReplacement::ReplaceLoadedValue(Instruction* var,
|
||||
Instruction* value) {
|
||||
// |var| is the global variable that has to be eliminated (OpVariable).
|
||||
// |value| is the OpLoad instruction that has loaded |var|.
|
||||
// The function expects all users of |value| to be OpCompositeExtract
|
||||
// instructions. Otherwise the function returns false with an error message.
|
||||
assert(value->opcode() == SpvOpLoad);
|
||||
assert(value->GetSingleWordInOperand(0) == var->result_id());
|
||||
std::vector<Instruction*> work_list;
|
||||
bool failed = !get_def_use_mgr()->WhileEachUser(
|
||||
value->result_id(), [this, &work_list](Instruction* use) {
|
||||
if (use->opcode() != SpvOpCompositeExtract) {
|
||||
context()->EmitErrorMessage(
|
||||
"Variable cannot be replaced: invalid instruction", use);
|
||||
return false;
|
||||
}
|
||||
work_list.push_back(use);
|
||||
return true;
|
||||
});
|
||||
|
||||
if (failed) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (Instruction* use : work_list) {
|
||||
if (!ReplaceCompositeExtract(var, use)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// All usages of the loaded value have been killed. We can kill the OpLoad.
|
||||
context()->KillInst(value);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DescriptorScalarReplacement::ReplaceCompositeExtract(
|
||||
Instruction* var, Instruction* extract) {
|
||||
assert(extract->opcode() == SpvOpCompositeExtract);
|
||||
// We're currently only supporting extractions of one index at a time. If we
|
||||
// need to, we can handle cases with multiple indexes in the future.
|
||||
if (extract->NumInOperands() != 2) {
|
||||
context()->EmitErrorMessage(
|
||||
"Variable cannot be replaced: invalid instruction", extract);
|
||||
return false;
|
||||
}
|
||||
|
||||
uint32_t replacement_var =
|
||||
GetReplacementVariable(var, extract->GetSingleWordInOperand(1));
|
||||
|
||||
// The result type of the OpLoad is the same as the result type of the
|
||||
// OpCompositeExtract.
|
||||
uint32_t load_id = TakeNextId();
|
||||
std::unique_ptr<Instruction> load(
|
||||
new Instruction(context(), SpvOpLoad, extract->type_id(), load_id,
|
||||
std::initializer_list<Operand>{
|
||||
{SPV_OPERAND_TYPE_ID, {replacement_var}}}));
|
||||
Instruction* load_instr = load.get();
|
||||
get_def_use_mgr()->AnalyzeInstDefUse(load_instr);
|
||||
context()->set_instr_block(load_instr, context()->get_instr_block(extract));
|
||||
extract->InsertBefore(std::move(load));
|
||||
context()->ReplaceAllUsesWith(extract->result_id(), load_id);
|
||||
context()->KillInst(extract);
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace opt
|
||||
} // namespace spvtools
|
||||
|
||||
23
3rdparty/spirv-tools/source/opt/desc_sroa.h
vendored
23
3rdparty/spirv-tools/source/opt/desc_sroa.h
vendored
@@ -61,6 +61,20 @@ class DescriptorScalarReplacement : public Pass {
|
||||
// |true| if successful.
|
||||
bool ReplaceAccessChain(Instruction* var, Instruction* use);
|
||||
|
||||
// Replaces the given compososite variable |var| loaded by OpLoad |value| with
|
||||
// replacement variables, one for each component that's accessed in the
|
||||
// shader. Assumes that |value| is only used by OpCompositeExtract
|
||||
// instructions, one index at a time. Returns true on success, and false
|
||||
// otherwise.
|
||||
bool ReplaceLoadedValue(Instruction* var, Instruction* value);
|
||||
|
||||
// Replaces the given OpCompositeExtract |extract| and all of its references
|
||||
// with an OpLoad of a replacement variable. |var| is the variable with
|
||||
// composite type whose value is being used by |extract|. Assumes that
|
||||
// |extract| is extracting one index only. Returns true on success, and false
|
||||
// otherwise.
|
||||
bool ReplaceCompositeExtract(Instruction* var, Instruction* extract);
|
||||
|
||||
// Returns the id of the variable that will be used to replace the |idx|th
|
||||
// element of |var|. The variable is created if it has not already been
|
||||
// created.
|
||||
@@ -70,6 +84,15 @@ class DescriptorScalarReplacement : public Pass {
|
||||
// element of |var|.
|
||||
uint32_t CreateReplacementVariable(Instruction* var, uint32_t idx);
|
||||
|
||||
// Returns the number of bindings used by the given |type_id|.
|
||||
// All types are considered to use 1 binding slot, except:
|
||||
// 1- A pointer type consumes as many binding numbers as its pointee.
|
||||
// 2- An array of size N consumes N*M binding numbers, where M is the number
|
||||
// of bindings used by each array element.
|
||||
// 3- The number of bindings consumed by a structure is the sum of the
|
||||
// bindings used by its members.
|
||||
uint32_t GetNumBindingsUsedByType(uint32_t type_id);
|
||||
|
||||
// A map from an OpVariable instruction to the set of variables that will be
|
||||
// used to replace it. The entry |replacement_variables_[var][i]| is the id of
|
||||
// a variable that will be used in the place of the the ith element of the
|
||||
|
||||
27
3rdparty/spirv-tools/source/opt/ir_context.cpp
vendored
27
3rdparty/spirv-tools/source/opt/ir_context.cpp
vendored
@@ -97,8 +97,9 @@ void IRContext::InvalidateAnalysesExceptFor(
|
||||
}
|
||||
|
||||
void IRContext::InvalidateAnalyses(IRContext::Analysis analyses_to_invalidate) {
|
||||
// The ConstantManager contains Type pointers. If the TypeManager goes
|
||||
// away, the ConstantManager has to go away.
|
||||
// The ConstantManager and DebugInfoManager contain Type pointers. If the
|
||||
// TypeManager goes away, the ConstantManager and DebugInfoManager have to
|
||||
// go away.
|
||||
if (analyses_to_invalidate & kAnalysisTypes) {
|
||||
analyses_to_invalidate |= kAnalysisConstants;
|
||||
analyses_to_invalidate |= kAnalysisDebugInfo;
|
||||
@@ -179,6 +180,9 @@ Instruction* IRContext::KillInst(Instruction* inst) {
|
||||
decoration_mgr_->RemoveDecoration(inst);
|
||||
}
|
||||
}
|
||||
if (AreAnalysesValid(kAnalysisDebugInfo)) {
|
||||
get_debug_info_mgr()->ClearDebugInfo(inst);
|
||||
}
|
||||
if (type_mgr_ && IsTypeInst(inst->opcode())) {
|
||||
type_mgr_->RemoveId(inst->result_id());
|
||||
}
|
||||
@@ -218,6 +222,13 @@ bool IRContext::KillDef(uint32_t id) {
|
||||
return false;
|
||||
}
|
||||
|
||||
void IRContext::KillDebugDeclareInsts(Function* fn) {
|
||||
fn->ForEachInst([this](Instruction* inst) {
|
||||
if (inst->GetOpenCL100DebugOpcode() == OpenCLDebugInfo100DebugDeclare)
|
||||
KillInst(inst);
|
||||
});
|
||||
}
|
||||
|
||||
bool IRContext::ReplaceAllUsesWith(uint32_t before, uint32_t after) {
|
||||
return ReplaceAllUsesWithPredicate(
|
||||
before, after, [](Instruction*, uint32_t) { return true; });
|
||||
@@ -344,6 +355,9 @@ void IRContext::ForgetUses(Instruction* inst) {
|
||||
get_decoration_mgr()->RemoveDecoration(inst);
|
||||
}
|
||||
}
|
||||
if (AreAnalysesValid(kAnalysisDebugInfo)) {
|
||||
get_debug_info_mgr()->ClearDebugInfo(inst);
|
||||
}
|
||||
RemoveFromIdToName(inst);
|
||||
}
|
||||
|
||||
@@ -356,6 +370,9 @@ void IRContext::AnalyzeUses(Instruction* inst) {
|
||||
get_decoration_mgr()->AddDecoration(inst);
|
||||
}
|
||||
}
|
||||
if (AreAnalysesValid(kAnalysisDebugInfo)) {
|
||||
get_debug_info_mgr()->AnalyzeDebugInst(inst);
|
||||
}
|
||||
if (id_to_name_ &&
|
||||
(inst->opcode() == SpvOpName || inst->opcode() == SpvOpMemberName)) {
|
||||
id_to_name_->insert({inst->GetSingleWordInOperand(0), inst});
|
||||
@@ -394,6 +411,7 @@ void IRContext::KillOperandFromDebugInstructions(Instruction* inst) {
|
||||
if (operand.words[0] == id) {
|
||||
operand.words[0] =
|
||||
get_debug_info_mgr()->GetDebugInfoNone()->result_id();
|
||||
get_def_use_mgr()->AnalyzeInstUse(&*it);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -408,13 +426,10 @@ void IRContext::KillOperandFromDebugInstructions(Instruction* inst) {
|
||||
if (operand.words[0] == id) {
|
||||
operand.words[0] =
|
||||
get_debug_info_mgr()->GetDebugInfoNone()->result_id();
|
||||
get_def_use_mgr()->AnalyzeInstUse(&*it);
|
||||
}
|
||||
}
|
||||
}
|
||||
// Notice that we do not need anythings to do for local variables.
|
||||
// DebugLocalVariable does not have an OpVariable operand. Instead,
|
||||
// DebugDeclare/DebugValue has an OpVariable operand for a local
|
||||
// variable. The function inlining pass handles it properly.
|
||||
}
|
||||
|
||||
void IRContext::AddCombinatorsForCapability(uint32_t capability) {
|
||||
|
||||
29
3rdparty/spirv-tools/source/opt/ir_context.h
vendored
29
3rdparty/spirv-tools/source/opt/ir_context.h
vendored
@@ -357,6 +357,13 @@ class IRContext {
|
||||
inline IteratorRange<std::multimap<uint32_t, Instruction*>::iterator>
|
||||
GetNames(uint32_t id);
|
||||
|
||||
// Returns an OpMemberName instruction that targets |struct_type_id| at
|
||||
// index |index|. Returns nullptr if no such instruction exists.
|
||||
// While the SPIR-V spec does not prohibit having multiple OpMemberName
|
||||
// instructions for the same structure member, it is hard to imagine a member
|
||||
// having more than one name. This method returns the first one it finds.
|
||||
inline Instruction* GetMemberName(uint32_t struct_type_id, uint32_t index);
|
||||
|
||||
// Sets the message consumer to the given |consumer|. |consumer| which will be
|
||||
// invoked every time there is a message to be communicated to the outside.
|
||||
void SetMessageConsumer(MessageConsumer c) { consumer_ = std::move(c); }
|
||||
@@ -396,6 +403,9 @@ class IRContext {
|
||||
// instruction exists.
|
||||
Instruction* KillInst(Instruction* inst);
|
||||
|
||||
// Deletes DebugDeclare instructions in the given function |fn|.
|
||||
void KillDebugDeclareInsts(Function* fn);
|
||||
|
||||
// Returns true if all of the given analyses are valid.
|
||||
bool AreAnalysesValid(Analysis set) { return (set & valid_analyses_) == set; }
|
||||
|
||||
@@ -1061,7 +1071,9 @@ void IRContext::AddDebug1Inst(std::unique_ptr<Instruction>&& d) {
|
||||
void IRContext::AddDebug2Inst(std::unique_ptr<Instruction>&& d) {
|
||||
if (AreAnalysesValid(kAnalysisNameMap)) {
|
||||
if (d->opcode() == SpvOpName || d->opcode() == SpvOpMemberName) {
|
||||
id_to_name_->insert({d->result_id(), d.get()});
|
||||
// OpName and OpMemberName do not have result-ids. The target of the
|
||||
// instruction is at InOperand index 0.
|
||||
id_to_name_->insert({d->GetSingleWordInOperand(0), d.get()});
|
||||
}
|
||||
}
|
||||
module()->AddDebug2Inst(std::move(d));
|
||||
@@ -1135,6 +1147,21 @@ IRContext::GetNames(uint32_t id) {
|
||||
return make_range(std::move(result.first), std::move(result.second));
|
||||
}
|
||||
|
||||
Instruction* IRContext::GetMemberName(uint32_t struct_type_id, uint32_t index) {
|
||||
if (!AreAnalysesValid(kAnalysisNameMap)) {
|
||||
BuildIdToNameMap();
|
||||
}
|
||||
auto result = id_to_name_->equal_range(struct_type_id);
|
||||
for (auto i = result.first; i != result.second; ++i) {
|
||||
auto* name_instr = i->second;
|
||||
if (name_instr->opcode() == SpvOpMemberName &&
|
||||
name_instr->GetSingleWordInOperand(1) == index) {
|
||||
return name_instr;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
} // namespace opt
|
||||
} // namespace spvtools
|
||||
|
||||
|
||||
6
3rdparty/spirv-tools/source/opt/mem_pass.cpp
vendored
6
3rdparty/spirv-tools/source/opt/mem_pass.cpp
vendored
@@ -20,6 +20,7 @@
|
||||
#include <set>
|
||||
#include <vector>
|
||||
|
||||
#include "OpenCLDebugInfo100.h"
|
||||
#include "source/cfa.h"
|
||||
#include "source/opt/basic_block.h"
|
||||
#include "source/opt/dominator_analysis.h"
|
||||
@@ -225,6 +226,11 @@ MemPass::MemPass() {}
|
||||
|
||||
bool MemPass::HasOnlySupportedRefs(uint32_t varId) {
|
||||
return get_def_use_mgr()->WhileEachUser(varId, [this](Instruction* user) {
|
||||
auto dbg_op = user->GetOpenCL100DebugOpcode();
|
||||
if (dbg_op == OpenCLDebugInfo100DebugDeclare ||
|
||||
dbg_op == OpenCLDebugInfo100DebugValue) {
|
||||
return true;
|
||||
}
|
||||
SpvOp op = user->opcode();
|
||||
if (op != SpvOpStore && op != SpvOpLoad && op != SpvOpName &&
|
||||
!IsNonTypeDecorate(op)) {
|
||||
|
||||
4
3rdparty/spirv-tools/source/opt/pass.h
vendored
4
3rdparty/spirv-tools/source/opt/pass.h
vendored
@@ -71,6 +71,10 @@ class Pass {
|
||||
return context()->get_def_use_mgr();
|
||||
}
|
||||
|
||||
analysis::DebugInfoManager* get_debug_info_mgr() const {
|
||||
return context()->get_debug_info_mgr();
|
||||
}
|
||||
|
||||
analysis::DecorationManager* get_decoration_mgr() const {
|
||||
return context()->get_decoration_mgr();
|
||||
}
|
||||
|
||||
@@ -307,6 +307,7 @@ void SSARewriter::ProcessStore(Instruction* inst, BasicBlock* bb) {
|
||||
}
|
||||
if (pass_->IsTargetVar(var_id)) {
|
||||
WriteVariable(var_id, bb, val_id);
|
||||
pass_->get_debug_info_mgr()->AddDebugValue(inst, var_id, val_id, inst);
|
||||
|
||||
#if SSA_REWRITE_DEBUGGING_LEVEL > 1
|
||||
std::cerr << "\tFound store '%" << var_id << " = %" << val_id << "': "
|
||||
@@ -437,6 +438,8 @@ bool SSARewriter::ApplyReplacements() {
|
||||
|
||||
// Add Phi instructions from completed Phi candidates.
|
||||
std::vector<Instruction*> generated_phis;
|
||||
// Add DebugValue instructions for Phi instructions.
|
||||
std::vector<Instruction*> dbg_values_for_phis;
|
||||
for (const PhiCandidate* phi_candidate : phis_to_generate_) {
|
||||
#if SSA_REWRITE_DEBUGGING_LEVEL > 2
|
||||
std::cerr << "Phi candidate: " << phi_candidate->PrettyPrint(pass_->cfg())
|
||||
@@ -447,9 +450,10 @@ bool SSARewriter::ApplyReplacements() {
|
||||
"Tried to instantiate a Phi instruction from an incomplete Phi "
|
||||
"candidate");
|
||||
|
||||
auto* local_var = pass_->get_def_use_mgr()->GetDef(phi_candidate->var_id());
|
||||
|
||||
// Build the vector of operands for the new OpPhi instruction.
|
||||
uint32_t type_id = pass_->GetPointeeTypeId(
|
||||
pass_->get_def_use_mgr()->GetDef(phi_candidate->var_id()));
|
||||
uint32_t type_id = pass_->GetPointeeTypeId(local_var);
|
||||
std::vector<Operand> phi_operands;
|
||||
uint32_t arg_ix = 0;
|
||||
std::unordered_map<uint32_t, uint32_t> already_seen;
|
||||
@@ -479,11 +483,17 @@ bool SSARewriter::ApplyReplacements() {
|
||||
pass_->get_def_use_mgr()->AnalyzeInstDef(&*phi_inst);
|
||||
pass_->context()->set_instr_block(&*phi_inst, phi_candidate->bb());
|
||||
auto insert_it = phi_candidate->bb()->begin();
|
||||
insert_it.InsertBefore(std::move(phi_inst));
|
||||
insert_it = insert_it.InsertBefore(std::move(phi_inst));
|
||||
pass_->context()->get_decoration_mgr()->CloneDecorations(
|
||||
phi_candidate->var_id(), phi_candidate->result_id(),
|
||||
{SpvDecorationRelaxedPrecision});
|
||||
|
||||
// Add DebugValue for the new OpPhi instruction.
|
||||
insert_it->SetDebugScope(local_var->GetDebugScope());
|
||||
pass_->get_debug_info_mgr()->AddDebugValue(
|
||||
&*insert_it, phi_candidate->var_id(), phi_candidate->result_id(),
|
||||
&*insert_it);
|
||||
|
||||
modified = true;
|
||||
}
|
||||
|
||||
@@ -604,6 +614,8 @@ Pass::Status SSARewriter::RewriteFunctionIntoSSA(Function* fp) {
|
||||
<< fp->PrettyPrint(0) << "\n";
|
||||
#endif
|
||||
|
||||
if (modified) pass_->context()->KillDebugDeclareInsts(fp);
|
||||
|
||||
return modified ? Pass::Status::SuccessWithChange
|
||||
: Pass::Status::SuccessWithoutChange;
|
||||
}
|
||||
|
||||
@@ -39,8 +39,7 @@ namespace opt {
|
||||
// (https://link.springer.com/chapter/10.1007/978-3-642-37051-9_6)
|
||||
class SSARewriter {
|
||||
public:
|
||||
SSARewriter(MemPass* pass)
|
||||
: pass_(pass), first_phi_id_(pass_->get_module()->IdBound()) {}
|
||||
SSARewriter(MemPass* pass) : pass_(pass) {}
|
||||
|
||||
// Rewrites SSA-target variables in function |fp| into SSA. This is the
|
||||
// entry point for the SSA rewrite algorithm. SSA-target variables are
|
||||
@@ -287,10 +286,6 @@ class SSARewriter {
|
||||
|
||||
// Memory pass requesting the SSA rewriter.
|
||||
MemPass* pass_;
|
||||
|
||||
// ID of the first Phi created by the SSA rewriter. During rewriting, any
|
||||
// ID bigger than this corresponds to a Phi candidate.
|
||||
uint32_t first_phi_id_;
|
||||
};
|
||||
|
||||
class SSARewritePass : public MemPass {
|
||||
|
||||
@@ -1540,6 +1540,21 @@ spv_result_t CheckBlockDecoration(ValidationState_t& vstate,
|
||||
return SPV_SUCCESS;
|
||||
}
|
||||
|
||||
spv_result_t CheckLocationDecoration(ValidationState_t& vstate,
|
||||
const Instruction& inst,
|
||||
const Decoration& decoration) {
|
||||
if (inst.opcode() == SpvOpVariable) return SPV_SUCCESS;
|
||||
|
||||
if (decoration.struct_member_index() != Decoration::kInvalidMember &&
|
||||
inst.opcode() == SpvOpTypeStruct) {
|
||||
return SPV_SUCCESS;
|
||||
}
|
||||
|
||||
return vstate.diag(SPV_ERROR_INVALID_ID, &inst)
|
||||
<< "Location decoration can only be applied to a variable or member "
|
||||
"of a structure type";
|
||||
}
|
||||
|
||||
#define PASS_OR_BAIL_AT_LINE(X, LINE) \
|
||||
{ \
|
||||
spv_result_t e##LINE = (X); \
|
||||
@@ -1590,6 +1605,9 @@ spv_result_t CheckDecorationsFromDecoration(ValidationState_t& vstate) {
|
||||
case SpvDecorationBufferBlock:
|
||||
PASS_OR_BAIL(CheckBlockDecoration(vstate, *inst, decoration));
|
||||
break;
|
||||
case SpvDecorationLocation:
|
||||
PASS_OR_BAIL(CheckLocationDecoration(vstate, *inst, decoration));
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -102,6 +102,373 @@ spv_result_t check_interface_variable(ValidationState_t& _,
|
||||
return SPV_SUCCESS;
|
||||
}
|
||||
|
||||
// This function assumes a base location has been determined already. As such
|
||||
// any further location decorations are invalid.
|
||||
// TODO: if this code turns out to be slow, there is an opportunity to cache
|
||||
// the result for a given type id.
|
||||
spv_result_t NumConsumedLocations(ValidationState_t& _, const Instruction* type,
|
||||
uint32_t* num_locations) {
|
||||
*num_locations = 0;
|
||||
switch (type->opcode()) {
|
||||
case SpvOpTypeInt:
|
||||
case SpvOpTypeFloat:
|
||||
// Scalars always consume a single location.
|
||||
*num_locations = 1;
|
||||
break;
|
||||
case SpvOpTypeVector:
|
||||
// 3- and 4-component 64-bit vectors consume two locations.
|
||||
if ((_.ContainsSizedIntOrFloatType(type->id(), SpvOpTypeInt, 64) ||
|
||||
_.ContainsSizedIntOrFloatType(type->id(), SpvOpTypeFloat, 64)) &&
|
||||
(type->GetOperandAs<uint32_t>(2) > 2)) {
|
||||
*num_locations = 2;
|
||||
} else {
|
||||
*num_locations = 1;
|
||||
}
|
||||
break;
|
||||
case SpvOpTypeMatrix:
|
||||
// Matrices consume locations equal to the underlying vector type for
|
||||
// each column.
|
||||
NumConsumedLocations(_, _.FindDef(type->GetOperandAs<uint32_t>(1)),
|
||||
num_locations);
|
||||
*num_locations *= type->GetOperandAs<uint32_t>(2);
|
||||
break;
|
||||
case SpvOpTypeArray: {
|
||||
// Arrays consume locations equal to the underlying type times the number
|
||||
// of elements in the vector.
|
||||
NumConsumedLocations(_, _.FindDef(type->GetOperandAs<uint32_t>(1)),
|
||||
num_locations);
|
||||
bool is_int = false;
|
||||
bool is_const = false;
|
||||
uint32_t value = 0;
|
||||
// Attempt to evaluate the number of array elements.
|
||||
std::tie(is_int, is_const, value) =
|
||||
_.EvalInt32IfConst(type->GetOperandAs<uint32_t>(2));
|
||||
if (is_int && is_const) *num_locations *= value;
|
||||
break;
|
||||
}
|
||||
case SpvOpTypeStruct: {
|
||||
// Members cannot have location decorations at this point.
|
||||
if (_.HasDecoration(type->id(), SpvDecorationLocation)) {
|
||||
return _.diag(SPV_ERROR_INVALID_DATA, type)
|
||||
<< "Members cannot be assigned a location";
|
||||
}
|
||||
|
||||
// Structs consume locations equal to the sum of the locations consumed
|
||||
// by the members.
|
||||
for (uint32_t i = 1; i < type->operands().size(); ++i) {
|
||||
uint32_t member_locations = 0;
|
||||
if (auto error = NumConsumedLocations(
|
||||
_, _.FindDef(type->GetOperandAs<uint32_t>(i)),
|
||||
&member_locations)) {
|
||||
return error;
|
||||
}
|
||||
*num_locations += member_locations;
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return SPV_SUCCESS;
|
||||
}
|
||||
|
||||
// Returns the number of components consumed by types that support a component
|
||||
// decoration.
|
||||
uint32_t NumConsumedComponents(ValidationState_t& _, const Instruction* type) {
|
||||
uint32_t num_components = 0;
|
||||
switch (type->opcode()) {
|
||||
case SpvOpTypeInt:
|
||||
case SpvOpTypeFloat:
|
||||
// 64-bit types consume two components.
|
||||
if (type->GetOperandAs<uint32_t>(1) == 64) {
|
||||
num_components = 2;
|
||||
} else {
|
||||
num_components = 1;
|
||||
}
|
||||
break;
|
||||
case SpvOpTypeVector:
|
||||
// Vectors consume components equal to the underlying type's consumption
|
||||
// times the number of elements in the vector. Note that 3- and 4-element
|
||||
// vectors cannot have a component decoration (i.e. assumed to be zero).
|
||||
num_components =
|
||||
NumConsumedComponents(_, _.FindDef(type->GetOperandAs<uint32_t>(1)));
|
||||
num_components *= type->GetOperandAs<uint32_t>(2);
|
||||
break;
|
||||
default:
|
||||
// This is an error that is validated elsewhere.
|
||||
break;
|
||||
}
|
||||
|
||||
return num_components;
|
||||
}
|
||||
|
||||
// Populates |locations| (and/or |output_index1_locations|) with the use
|
||||
// location and component coordinates for |variable|. Indices are calculated as
|
||||
// 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 bool is_fragment = entry_point->GetOperandAs<SpvExecutionModel>(0) ==
|
||||
SpvExecutionModelFragment;
|
||||
const bool is_output =
|
||||
variable->GetOperandAs<SpvStorageClass>(2) == SpvStorageClassOutput;
|
||||
auto ptr_type_id = variable->GetOperandAs<uint32_t>(0);
|
||||
auto ptr_type = _.FindDef(ptr_type_id);
|
||||
auto type_id = ptr_type->GetOperandAs<uint32_t>(2);
|
||||
auto type = _.FindDef(type_id);
|
||||
|
||||
// Check for Location, Component and Index decorations on the variable. The
|
||||
// validator allows duplicate decorations if the location/component/index are
|
||||
// equal. Also track Patch and PerTaskNV decorations.
|
||||
bool has_location = false;
|
||||
uint32_t location = 0;
|
||||
bool has_component = false;
|
||||
uint32_t component = 0;
|
||||
bool has_index = false;
|
||||
uint32_t index = 0;
|
||||
bool has_patch = false;
|
||||
bool has_per_task_nv = false;
|
||||
bool has_per_vertex_nv = false;
|
||||
for (auto& dec : _.id_decorations(variable->id())) {
|
||||
if (dec.dec_type() == SpvDecorationLocation) {
|
||||
if (has_location && dec.params()[0] != location) {
|
||||
return _.diag(SPV_ERROR_INVALID_DATA, variable)
|
||||
<< "Variable has conflicting location decorations";
|
||||
}
|
||||
has_location = true;
|
||||
location = dec.params()[0];
|
||||
} else if (dec.dec_type() == SpvDecorationComponent) {
|
||||
if (has_component && dec.params()[0] != component) {
|
||||
return _.diag(SPV_ERROR_INVALID_DATA, variable)
|
||||
<< "Variable has conflicting component decorations";
|
||||
}
|
||||
has_component = true;
|
||||
component = dec.params()[0];
|
||||
} else if (dec.dec_type() == SpvDecorationIndex) {
|
||||
if (!is_output || !is_fragment) {
|
||||
return _.diag(SPV_ERROR_INVALID_DATA, variable)
|
||||
<< "Index can only be applied to Fragment output variables";
|
||||
}
|
||||
if (has_index && dec.params()[0] != index) {
|
||||
return _.diag(SPV_ERROR_INVALID_DATA, variable)
|
||||
<< "Variable has conflicting index decorations";
|
||||
}
|
||||
has_index = true;
|
||||
index = dec.params()[0];
|
||||
} else if (dec.dec_type() == SpvDecorationBuiltIn) {
|
||||
// Don't check built-ins.
|
||||
return SPV_SUCCESS;
|
||||
} else if (dec.dec_type() == SpvDecorationPatch) {
|
||||
has_patch = true;
|
||||
} else if (dec.dec_type() == SpvDecorationPerTaskNV) {
|
||||
has_per_task_nv = true;
|
||||
} else if (dec.dec_type() == SpvDecorationPerVertexNV) {
|
||||
has_per_vertex_nv = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Vulkan 14.1.3: Tessellation control and mesh per-vertex outputs and
|
||||
// tessellation control, evaluation and geometry per-vertex inputs have a
|
||||
// layer of arraying that is not included in interface matching.
|
||||
bool is_arrayed = false;
|
||||
switch (entry_point->GetOperandAs<SpvExecutionModel>(0)) {
|
||||
case SpvExecutionModelTessellationControl:
|
||||
if (!has_patch) {
|
||||
is_arrayed = true;
|
||||
}
|
||||
break;
|
||||
case SpvExecutionModelTessellationEvaluation:
|
||||
if (!is_output && !has_patch) {
|
||||
is_arrayed = true;
|
||||
}
|
||||
break;
|
||||
case SpvExecutionModelGeometry:
|
||||
if (!is_output) {
|
||||
is_arrayed = true;
|
||||
}
|
||||
break;
|
||||
case SpvExecutionModelFragment:
|
||||
if (!is_output && has_per_vertex_nv) {
|
||||
is_arrayed = true;
|
||||
}
|
||||
break;
|
||||
case SpvExecutionModelMeshNV:
|
||||
if (is_output && !has_per_task_nv) {
|
||||
is_arrayed = true;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
// Unpack arrayness.
|
||||
if (is_arrayed && (type->opcode() == SpvOpTypeArray ||
|
||||
type->opcode() == SpvOpTypeRuntimeArray)) {
|
||||
type_id = type->GetOperandAs<uint32_t>(1);
|
||||
type = _.FindDef(type_id);
|
||||
}
|
||||
|
||||
if (type->opcode() == SpvOpTypeStruct) {
|
||||
// Don't check built-ins.
|
||||
if (_.HasDecoration(type_id, SpvDecorationBuiltIn)) return SPV_SUCCESS;
|
||||
}
|
||||
|
||||
// Only block-decorated structs don't need a location on the variable.
|
||||
const bool is_block = _.HasDecoration(type_id, SpvDecorationBlock);
|
||||
if (!has_location && !is_block) {
|
||||
return _.diag(SPV_ERROR_INVALID_DATA, variable)
|
||||
<< "Variable must be decorated with a location";
|
||||
}
|
||||
|
||||
const std::string storage_class = is_output ? "output" : "input";
|
||||
if (has_location) {
|
||||
auto sub_type = type;
|
||||
bool is_int = false;
|
||||
bool is_const = false;
|
||||
uint32_t array_size = 1;
|
||||
// If the variable is still arrayed, mark the locations/components per
|
||||
// index.
|
||||
if (type->opcode() == SpvOpTypeArray) {
|
||||
// Determine the array size if possible and get the element type.
|
||||
std::tie(is_int, is_const, array_size) =
|
||||
_.EvalInt32IfConst(type->GetOperandAs<uint32_t>(2));
|
||||
if (!is_int || !is_const) array_size = 1;
|
||||
auto sub_type_id = type->GetOperandAs<uint32_t>(1);
|
||||
sub_type = _.FindDef(sub_type_id);
|
||||
}
|
||||
|
||||
for (uint32_t array_idx = 0; array_idx < array_size; ++array_idx) {
|
||||
uint32_t num_locations = 0;
|
||||
if (auto error = NumConsumedLocations(_, sub_type, &num_locations))
|
||||
return error;
|
||||
|
||||
uint32_t num_components = NumConsumedComponents(_, sub_type);
|
||||
uint32_t array_location = location + (num_locations * array_idx);
|
||||
uint32_t start = array_location * 4;
|
||||
uint32_t end = (array_location + num_locations) * 4;
|
||||
if (num_components != 0) {
|
||||
start += component;
|
||||
end = array_location * 4 + component + num_components;
|
||||
}
|
||||
|
||||
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)) {
|
||||
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 {
|
||||
// For Block-decorated structs with no location assigned to the variable,
|
||||
// each member of the block must be assigned a location. Also record any
|
||||
// member component assignments. The validator allows duplicate decorations
|
||||
// if they agree on the location/component.
|
||||
std::unordered_map<uint32_t, uint32_t> member_locations;
|
||||
std::unordered_map<uint32_t, uint32_t> member_components;
|
||||
for (auto& dec : _.id_decorations(type_id)) {
|
||||
if (dec.dec_type() == SpvDecorationLocation) {
|
||||
auto where = member_locations.find(dec.struct_member_index());
|
||||
if (where == member_locations.end()) {
|
||||
member_locations[dec.struct_member_index()] = dec.params()[0];
|
||||
} else if (where->second != dec.params()[0]) {
|
||||
return _.diag(SPV_ERROR_INVALID_DATA, type)
|
||||
<< "Member index " << dec.struct_member_index()
|
||||
<< " has conflicting location assignments";
|
||||
}
|
||||
} else if (dec.dec_type() == SpvDecorationComponent) {
|
||||
auto where = member_components.find(dec.struct_member_index());
|
||||
if (where == member_components.end()) {
|
||||
member_components[dec.struct_member_index()] = dec.params()[0];
|
||||
} else if (where->second != dec.params()[0]) {
|
||||
return _.diag(SPV_ERROR_INVALID_DATA, type)
|
||||
<< "Member index " << dec.struct_member_index()
|
||||
<< " has conflicting component assignments";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (uint32_t i = 1; i < type->operands().size(); ++i) {
|
||||
auto where = member_locations.find(i - 1);
|
||||
if (where == member_locations.end()) {
|
||||
return _.diag(SPV_ERROR_INVALID_DATA, type)
|
||||
<< "Member index " << i - 1
|
||||
<< " is missing a location assignment";
|
||||
}
|
||||
|
||||
location = where->second;
|
||||
auto member = _.FindDef(type->GetOperandAs<uint32_t>(i));
|
||||
uint32_t num_locations = 0;
|
||||
if (auto error = NumConsumedLocations(_, member, &num_locations))
|
||||
return error;
|
||||
|
||||
// If the component is not specified, it is assumed to be zero.
|
||||
uint32_t num_components = NumConsumedComponents(_, member);
|
||||
component = 0;
|
||||
if (member_components.count(i - 1)) {
|
||||
component = member_components[i - 1];
|
||||
}
|
||||
|
||||
uint32_t start = location * 4;
|
||||
uint32_t end = (location + num_locations) * 4;
|
||||
if (num_components != 0) {
|
||||
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)) {
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return SPV_SUCCESS;
|
||||
}
|
||||
|
||||
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);
|
||||
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);
|
||||
auto storage_class = interface_var->GetOperandAs<SpvStorageClass>(2);
|
||||
if (storage_class != SpvStorageClassInput &&
|
||||
storage_class != SpvStorageClassOutput) {
|
||||
continue;
|
||||
}
|
||||
|
||||
auto locations = (storage_class == SpvStorageClassInput)
|
||||
? &input_locations
|
||||
: &output_locations_index0;
|
||||
if (auto error = GetLocationsForVariable(
|
||||
_, entry_point, interface_var, locations, &output_locations_index1))
|
||||
return error;
|
||||
}
|
||||
|
||||
return SPV_SUCCESS;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
spv_result_t ValidateInterfaces(ValidationState_t& _) {
|
||||
@@ -114,6 +481,17 @@ spv_result_t ValidateInterfaces(ValidationState_t& _) {
|
||||
}
|
||||
}
|
||||
|
||||
if (spvIsVulkanEnv(_.context()->target_env)) {
|
||||
for (auto& inst : _.ordered_instructions()) {
|
||||
if (inst.opcode() == SpvOpEntryPoint) {
|
||||
if (auto error = ValidateLocations(_, &inst)) {
|
||||
return error;
|
||||
}
|
||||
}
|
||||
if (inst.opcode() == SpvOpTypeVoid) break;
|
||||
}
|
||||
}
|
||||
|
||||
return SPV_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
31
3rdparty/spirv-tools/utils/roll_deps.sh
vendored
31
3rdparty/spirv-tools/utils/roll_deps.sh
vendored
@@ -13,19 +13,34 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
# Attempts to roll all entries in DEPS to origin/master and creates a
|
||||
# commit.
|
||||
# Attempts to roll all entries in DEPS to tip-of-tree and create a commit.
|
||||
#
|
||||
# Depends on roll-dep from depot_path being in PATH.
|
||||
|
||||
effcee_dir="third_party/effcee/"
|
||||
effcee_trunk="origin/main"
|
||||
googletest_dir="third_party/googletest/"
|
||||
googletest_trunk="origin/master"
|
||||
re2_dir="third_party/re2/"
|
||||
re2_trunk="origin/master"
|
||||
spirv_headers_dir="third_party/spirv-headers/"
|
||||
spirv_headers_trunk="origin/master"
|
||||
|
||||
# This script assumes it's parent directory is the repo root.
|
||||
repo_path=$(dirname "$0")/..
|
||||
|
||||
effcee_dir="external/effcee/"
|
||||
googletest_dir="external/googletest/"
|
||||
re2_dir="external/re2/"
|
||||
spirv_headers_dir="external/spirv-headers/"
|
||||
|
||||
cd "$repo_path"
|
||||
|
||||
roll-dep "$@" "${effcee_dir}" "${googletest_dir}" "${re2_dir}" "${spirv_headers_dir}"
|
||||
if [[ $(git diff --stat) != '' ]]; then
|
||||
echo "Working tree is dirty, commit changes before attempting to roll DEPS"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
old_head=$(git rev-parse HEAD)
|
||||
|
||||
roll-dep --ignore-dirty-tree --roll-to="${effcee_trunk}" "${effcee_dir}"
|
||||
roll-dep --ignore-dirty-tree --roll-to="${googletest_trunk}" "${googletest_dir}"
|
||||
roll-dep --ignore-dirty-tree --roll-to="${re2_trunk}" "${re2_dir}"
|
||||
roll-dep --ignore-dirty-tree --roll-to="${spirv_headers_trunk}" "${spirv_headers_dir}"
|
||||
|
||||
git rebase --interactive "${old_head}"
|
||||
|
||||
Reference in New Issue
Block a user