Updated spirv-tools.

This commit is contained in:
Бранимир Караџић
2019-06-22 07:30:33 -07:00
parent 5de3e44573
commit 7a2e51d748
54 changed files with 5910 additions and 318 deletions

View File

@@ -1,174 +1,26 @@
use_relative_paths = True
vars = {
'chromium_git': 'https://chromium.googlesource.com',
'github': 'https://github.com',
'build_revision': '037f38ae0fe5e11b4f7c33b750fd7a1e9634a606',
'buildtools_revision': 'ab7b6a7b350dd15804c87c20ce78982811fdd76f',
'clang_revision': 'abe5e4f9dc0f1df848c7a0efa05256253e77a7b7',
'effcee_revision': '04b624799f5a9dbaf3fa1dbed2ba9dce2fc8dcf2',
'googletest_revision': '98a0d007d7092b72eea0e501bb9ad17908a1a036',
'testing_revision': '340252637e2e7c72c0901dcbeeacfff419e19b59',
're2_revision': '6cf8ccd82dbaab2668e9b13596c68183c9ecd13f',
'spirv_headers_revision': '8b911bd2ba37677037b38c9bd286c7c05701bcda',
'effcee_revision': 'b83b58d177b797edd1f94c5f10837f2cc2863f0a',
'googletest_revision': '2f42d769ad1b08742f7ccb5ad4dd357fc5ff248c',
're2_revision': '848dfb7e1d7ba641d598cb66f81590f3999a555a',
'spirv_headers_revision': 'de99d4d834aeb51dd9f099baa285bd44fd04bb3d',
}
deps = {
"build":
Var('chromium_git') + "/chromium/src/build.git@" + Var('build_revision'),
'buildtools':
Var('chromium_git') + '/chromium/buildtools.git@' +
Var('buildtools_revision'),
'external/spirv-headers':
Var('github') + '/KhronosGroup/SPIRV-Headers.git@' +
Var('spirv_headers_revision'),
'external/effcee':
Var('github') + '/google/effcee.git@' + Var('effcee_revision'),
'external/googletest':
Var('github') + '/google/googletest.git@' + Var('googletest_revision'),
'external/effcee':
Var('github') + '/google/effcee.git@' + Var('effcee_revision'),
'external/re2':
Var('github') + '/google/re2.git@' + Var('re2_revision'),
'testing':
Var('chromium_git') + '/chromium/src/testing@' +
Var('testing_revision'),
'tools/clang':
Var('chromium_git') + '/chromium/src/tools/clang@' + Var('clang_revision')
'external/spirv-headers':
Var('github') + '/KhronosGroup/SPIRV-Headers.git@' +
Var('spirv_headers_revision'),
}
recursedeps = [
# buildtools provides clang_format, libc++, and libc++api
'buildtools',
]
hooks = [
{
'name': 'gn_win',
'action': [ 'download_from_google_storage',
'--no_resume',
'--platform=win32',
'--no_auth',
'--bucket', 'chromium-gn',
'-s', 'SPIRV-Tools/buildtools/win/gn.exe.sha1',
],
},
{
'name': 'gn_mac',
'pattern': '.',
'action': [ 'download_from_google_storage',
'--no_resume',
'--platform=darwin',
'--no_auth',
'--bucket', 'chromium-gn',
'-s', 'SPIRV-Tools/buildtools/mac/gn.sha1',
],
},
{
'name': 'gn_linux64',
'pattern': '.',
'action': [ 'download_from_google_storage',
'--no_resume',
'--platform=linux*',
'--no_auth',
'--bucket', 'chromium-gn',
'-s', 'SPIRV-Tools/buildtools/linux64/gn.sha1',
],
},
# Pull clang-format binaries using checked-in hashes.
{
'name': 'clang_format_win',
'pattern': '.',
'action': [ 'download_from_google_storage',
'--no_resume',
'--platform=win32',
'--no_auth',
'--bucket', 'chromium-clang-format',
'-s', 'SPIRV-Tools/buildtools/win/clang-format.exe.sha1',
],
},
{
'name': 'clang_format_mac',
'pattern': '.',
'action': [ 'download_from_google_storage',
'--no_resume',
'--platform=darwin',
'--no_auth',
'--bucket', 'chromium-clang-format',
'-s', 'SPIRV-Tools/buildtools/mac/clang-format.sha1',
],
},
{
'name': 'clang_format_linux',
'pattern': '.',
'action': [ 'download_from_google_storage',
'--no_resume',
'--platform=linux*',
'--no_auth',
'--bucket', 'chromium-clang-format',
'-s', 'SPIRV-Tools/buildtools/linux64/clang-format.sha1',
],
},
{
# Pull clang
'name': 'clang',
'pattern': '.',
'action': ['python',
'SPIRV-Tools/tools/clang/scripts/update.py'
],
},
{
'name': 'sysroot_arm',
'pattern': '.',
'condition': 'checkout_linux and checkout_arm',
'action': ['python', 'SPIRV-Tools/build/linux/sysroot_scripts/install-sysroot.py',
'--arch=arm'],
},
{
'name': 'sysroot_arm64',
'pattern': '.',
'condition': 'checkout_linux and checkout_arm64',
'action': ['python', 'SPIRV-Tools/build/linux/sysroot_scripts/install-sysroot.py',
'--arch=arm64'],
},
{
'name': 'sysroot_x86',
'pattern': '.',
'condition': 'checkout_linux and (checkout_x86 or checkout_x64)',
'action': ['python', 'SPIRV-Tools/build/linux/sysroot_scripts/install-sysroot.py',
'--arch=x86'],
},
{
'name': 'sysroot_mips',
'pattern': '.',
'condition': 'checkout_linux and checkout_mips',
'action': ['python', 'SPIRV-Tools/build/linux/sysroot_scripts/install-sysroot.py',
'--arch=mips'],
},
{
'name': 'sysroot_x64',
'pattern': '.',
'condition': 'checkout_linux and checkout_x64',
'action': ['python', 'SPIRV-Tools/build/linux/sysroot_scripts/install-sysroot.py',
'--arch=x64'],
},
{
# Update the Windows toolchain if necessary.
'name': 'win_toolchain',
'pattern': '.',
'condition': 'checkout_win',
'action': ['python', 'SPIRV-Tools/build/vs_toolchain.py', 'update', '--force'],
},
{
# Update the Mac toolchain if necessary.
'name': 'mac_toolchain',
'pattern': '.',
'action': ['python', 'SPIRV-Tools/build/mac_toolchain.py'],
},
]

View File

@@ -270,6 +270,10 @@ mkdir build && cd build
cmake [-G <platform-generator>] <spirv-dir>
```
*Note*:
The script `utils/git-sync-deps` can be used to checkout and/or update the
contents of the repos under `external/` instead of manually maintaining them.
Once the build files have been generated, build using your preferred
development environment.
@@ -344,6 +348,13 @@ $ANDROID_NDK/ndk-build -C ../android_test \
NDK_APP_OUT=`pwd`/app
```
### Updating DEPS
Occasionally the entries in DEPS will need to be updated. This is done on demand
when there is a request to do this, often due to downstream breakages. There is
a script `utils/roll_deps.sh` provided, which will generate a patch with the
updated DEPS values. This will still need to be tested in your checkout to
confirm that there are no integration issues that need to be resolved.
## Library
### Usage

View File

@@ -1 +1 @@
"v2019.4-dev", "SPIRV-Tools v2019.4-dev v2019.3-47-g59983a60"
"v2019.4-dev", "SPIRV-Tools v2019.4-dev v2019.3-60-gdf86bb44"

View File

@@ -32,6 +32,7 @@ if(SPIRV_BUILD_FUZZER)
fuzzer_pass.h
fuzzer_pass_add_dead_breaks.h
fuzzer_pass_add_useful_constructs.h
fuzzer_pass_obfuscate_constants.h
fuzzer_pass_permute_blocks.h
fuzzer_pass_split_blocks.h
fuzzer_util.h
@@ -46,8 +47,10 @@ if(SPIRV_BUILD_FUZZER)
transformation_add_type_boolean.h
transformation_add_type_float.h
transformation_add_type_int.h
transformation_add_type_pointer.h
transformation_move_block_down.h
transformation_replace_boolean_constant_with_constant_binary.h
transformation_replace_constant_with_uniform.h
transformation_split_block.h
uniform_buffer_element_descriptor.h
${CMAKE_CURRENT_BINARY_DIR}/protobufs/spvtoolsfuzz.pb.h
@@ -58,6 +61,7 @@ if(SPIRV_BUILD_FUZZER)
fuzzer_pass.cpp
fuzzer_pass_add_dead_breaks.cpp
fuzzer_pass_add_useful_constructs.cpp
fuzzer_pass_obfuscate_constants.cpp
fuzzer_pass_permute_blocks.cpp
fuzzer_pass_split_blocks.cpp
fuzzer_util.cpp
@@ -71,8 +75,10 @@ if(SPIRV_BUILD_FUZZER)
transformation_add_type_boolean.cpp
transformation_add_type_float.cpp
transformation_add_type_int.cpp
transformation_add_type_pointer.cpp
transformation_move_block_down.cpp
transformation_replace_boolean_constant_with_constant_binary.cpp
transformation_replace_constant_with_uniform.cpp
transformation_split_block.cpp
uniform_buffer_element_descriptor.cpp
${CMAKE_CURRENT_BINARY_DIR}/protobufs/spvtoolsfuzz.pb.cc

View File

@@ -64,6 +64,11 @@ struct FactManager::ConstantUniformFacts {
const protobufs::FactConstantUniform& constant_uniform_fact,
uint32_t type_id) const;
// Checks that the width of a floating-point constant is supported, and that
// the constant is finite.
bool FloatingPointValueIsSuitable(const protobufs::FactConstantUniform& fact,
uint32_t width) const;
std::vector<std::pair<protobufs::FactConstantUniform, uint32_t>>
facts_and_type_ids;
};
@@ -169,22 +174,48 @@ FactManager::ConstantUniformFacts::GetTypesForWhichUniformValuesAreKnown()
return result;
}
bool FactManager::ConstantUniformFacts::FloatingPointValueIsSuitable(
const protobufs::FactConstantUniform& fact, uint32_t width) const {
const uint32_t kFloatWidth = 32;
const uint32_t kDoubleWidth = 64;
if (width != kFloatWidth && width != kDoubleWidth) {
// Only 32- and 64-bit floating-point types are handled.
return false;
}
std::vector<uint32_t> words = GetConstantWords(fact);
if (width == 32) {
float value;
memcpy(&value, words.data(), sizeof(float));
if (!std::isfinite(value)) {
return false;
}
} else {
double value;
memcpy(&value, words.data(), sizeof(double));
if (!std::isfinite(value)) {
return false;
}
}
return true;
}
bool FactManager::ConstantUniformFacts::AddFact(
const protobufs::FactConstantUniform& fact, opt::IRContext* context) {
auto should_be_uniform_variable = context->get_def_use_mgr()->GetDef(
fact.uniform_buffer_element_descriptor().uniform_variable_id());
if (!should_be_uniform_variable) {
return false;
}
if (SpvOpVariable != should_be_uniform_variable->opcode()) {
return false;
}
if (SpvStorageClassUniform !=
should_be_uniform_variable->GetSingleWordInOperand(0)) {
// Try to find a unique instruction that declares a variable such that the
// variable is decorated with the descriptor set and binding associated with
// the constant uniform fact.
opt::Instruction* uniform_variable = FindUniformVariable(
fact.uniform_buffer_element_descriptor(), context, true);
if (!uniform_variable) {
return false;
}
assert(SpvOpVariable == uniform_variable->opcode());
assert(SpvStorageClassUniform == uniform_variable->GetSingleWordInOperand(0));
auto should_be_uniform_pointer_type =
context->get_type_mgr()->GetType(should_be_uniform_variable->type_id());
context->get_type_mgr()->GetType(uniform_variable->type_id());
if (!should_be_uniform_pointer_type->AsPointer()) {
return false;
}
@@ -193,7 +224,7 @@ bool FactManager::ConstantUniformFacts::AddFact(
return false;
}
auto should_be_uniform_pointer_instruction =
context->get_def_use_mgr()->GetDef(should_be_uniform_variable->type_id());
context->get_def_use_mgr()->GetDef(uniform_variable->type_id());
auto element_type =
should_be_uniform_pointer_instruction->GetSingleWordInOperand(1);
@@ -236,6 +267,12 @@ bool FactManager::ConstantUniformFacts::AddFact(
auto width = final_element_type->AsFloat()
? final_element_type->AsFloat()->width()
: final_element_type->AsInteger()->width();
if (final_element_type->AsFloat() &&
!FloatingPointValueIsSuitable(fact, width)) {
return false;
}
auto required_words = (width + 32 - 1) / 32;
if (static_cast<uint32_t>(fact.constant_word().size()) != required_words) {
return false;

View File

@@ -21,6 +21,7 @@
#include "source/fuzz/fuzzer_context.h"
#include "source/fuzz/fuzzer_pass_add_dead_breaks.h"
#include "source/fuzz/fuzzer_pass_add_useful_constructs.h"
#include "source/fuzz/fuzzer_pass_obfuscate_constants.h"
#include "source/fuzz/fuzzer_pass_permute_blocks.h"
#include "source/fuzz/fuzzer_pass_split_blocks.h"
#include "source/fuzz/protobufs/spirvfuzz_protobufs.h"
@@ -113,8 +114,9 @@ Fuzzer::FuzzerResultStatus Fuzzer::Run(
FuzzerPassAddDeadBreaks(ir_context.get(), &fact_manager, &fuzzer_context,
transformation_sequence_out)
.Apply();
// TODO(afd) Various other passes will be added.
FuzzerPassObfuscateConstants(ir_context.get(), &fact_manager, &fuzzer_context,
transformation_sequence_out)
.Apply();
// Finally, give the blocks in the module a good shake-up.
FuzzerPassPermuteBlocks(ir_context.get(), &fact_manager, &fuzzer_context,

View File

@@ -14,6 +14,8 @@
#include "source/fuzz/fuzzer_context.h"
#include <cmath>
namespace spvtools {
namespace fuzz {
@@ -24,8 +26,19 @@ namespace {
const uint32_t kDefaultChanceOfAddingDeadBreak = 20;
const uint32_t kDefaultChanceOfMovingBlockDown = 25;
const uint32_t kDefaultChanceOfObfuscatingConstant = 20;
const uint32_t kDefaultChanceOfSplittingBlock = 20;
// Default functions for controlling how deep to go during recursive
// generation/transformation. Keep them in alphabetical order.
const std::function<bool(uint32_t, RandomGenerator*)>
kDefaultGoDeeperInConstantObfuscation =
[](uint32_t current_depth, RandomGenerator* random_generator) -> bool {
double chance = 1.0 / std::pow(3.0, static_cast<float>(current_depth + 1));
return random_generator->RandomDouble() < chance;
};
} // namespace
FuzzerContext::FuzzerContext(RandomGenerator* random_generator,
@@ -34,7 +47,10 @@ FuzzerContext::FuzzerContext(RandomGenerator* random_generator,
next_fresh_id_(min_fresh_id),
chance_of_adding_dead_break_(kDefaultChanceOfAddingDeadBreak),
chance_of_moving_block_down_(kDefaultChanceOfMovingBlockDown),
chance_of_splitting_block_(kDefaultChanceOfSplittingBlock) {}
chance_of_obfuscating_constant_(kDefaultChanceOfObfuscatingConstant),
chance_of_splitting_block_(kDefaultChanceOfSplittingBlock),
go_deeper_in_constant_obfuscation_(
kDefaultGoDeeperInConstantObfuscation) {}
FuzzerContext::~FuzzerContext() = default;

View File

@@ -15,6 +15,8 @@
#ifndef SOURCE_FUZZ_FUZZER_CONTEXT_H_
#define SOURCE_FUZZ_FUZZER_CONTEXT_H_
#include <functional>
#include "source/fuzz/random_generator.h"
#include "source/opt/function.h"
@@ -43,8 +45,18 @@ class FuzzerContext {
// Keep them in alphabetical order.
uint32_t GetChanceOfAddingDeadBreak() { return chance_of_adding_dead_break_; }
uint32_t GetChanceOfMovingBlockDown() { return chance_of_moving_block_down_; }
uint32_t GetChanceOfObfuscatingConstant() {
return chance_of_obfuscating_constant_;
}
uint32_t GetChanceOfSplittingBlock() { return chance_of_splitting_block_; }
// Probability distributions to control how deeply to recurse.
// Keep them in alphabetical order.
const std::function<bool(uint32_t, RandomGenerator*)>&
GoDeeperInConstantObfuscation() {
return go_deeper_in_constant_obfuscation_;
}
private:
// The source of randomness.
RandomGenerator* random_generator_;
@@ -55,7 +67,13 @@ class FuzzerContext {
// Keep them in alphabetical order.
uint32_t chance_of_adding_dead_break_;
uint32_t chance_of_moving_block_down_;
uint32_t chance_of_obfuscating_constant_;
uint32_t chance_of_splitting_block_;
// Functions to determine with what probability to go deeper when generating
// or mutating constructs recursively.
const std::function<bool(uint32_t, RandomGenerator*)>&
go_deeper_in_constant_obfuscation_;
};
} // namespace fuzz

View File

@@ -19,6 +19,7 @@
#include "source/fuzz/transformation_add_type_boolean.h"
#include "source/fuzz/transformation_add_type_float.h"
#include "source/fuzz/transformation_add_type_int.h"
#include "source/fuzz/transformation_add_type_pointer.h"
namespace spvtools {
namespace fuzz {
@@ -176,6 +177,55 @@ void FuzzerPassAddUsefulConstructs::Apply() {
for (unsigned int& datum : uint_data) {
MaybeAddFloatConstant(32, {datum});
}
// For every known-to-be-constant uniform, make sure we have instructions
// declaring:
// - a pointer type with uniform storage class, whose pointee type is the type
// of the element
// - a signed integer constant for each index required to access the element
// - a constant for the constant value itself
for (auto& fact_and_type_id :
GetFactManager()->GetConstantUniformFactsAndTypes()) {
uint32_t element_type_id = fact_and_type_id.second;
assert(element_type_id);
auto element_type =
GetIRContext()->get_type_mgr()->GetType(element_type_id);
assert(element_type &&
"If the constant uniform fact is well-formed, the module must "
"already have a declaration of the type for the uniform element.");
opt::analysis::Pointer uniform_pointer(element_type,
SpvStorageClassUniform);
if (!GetIRContext()->get_type_mgr()->GetId(&uniform_pointer)) {
auto add_pointer = transformation::MakeTransformationAddTypePointer(
GetFuzzerContext()->GetFreshId(), SpvStorageClassUniform,
element_type_id);
assert(transformation::IsApplicable(add_pointer, GetIRContext(),
*GetFactManager()) &&
"Should be applicable by construction.");
transformation::Apply(add_pointer, GetIRContext(), GetFactManager());
*GetTransformations()->add_transformation()->mutable_add_type_pointer() =
add_pointer;
}
std::vector<uint32_t> words;
for (auto word : fact_and_type_id.first.constant_word()) {
words.push_back(word);
}
// We get the element type again as the type manager may have been
// invalidated since we last retrieved it.
element_type = GetIRContext()->get_type_mgr()->GetType(element_type_id);
if (element_type->AsInteger()) {
MaybeAddIntConstant(element_type->AsInteger()->width(),
element_type->AsInteger()->IsSigned(), words);
} else {
assert(element_type->AsFloat() &&
"Known uniform values must be integer or floating-point.");
MaybeAddFloatConstant(element_type->AsFloat()->width(), words);
}
for (auto index :
fact_and_type_id.first.uniform_buffer_element_descriptor().index()) {
MaybeAddIntConstant(32, true, {index});
}
}
}
} // namespace fuzz

View File

@@ -0,0 +1,471 @@
// Copyright (c) 2019 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "source/fuzz/fuzzer_pass_obfuscate_constants.h"
#include <cmath>
#include "source/fuzz/transformation_replace_boolean_constant_with_constant_binary.h"
#include "source/fuzz/transformation_replace_constant_with_uniform.h"
#include "source/opt/ir_context.h"
namespace spvtools {
namespace fuzz {
FuzzerPassObfuscateConstants::FuzzerPassObfuscateConstants(
opt::IRContext* ir_context, FactManager* fact_manager,
FuzzerContext* fuzzer_context,
protobufs::TransformationSequence* transformations)
: FuzzerPass(ir_context, fact_manager, fuzzer_context, transformations) {}
FuzzerPassObfuscateConstants::~FuzzerPassObfuscateConstants() = default;
void FuzzerPassObfuscateConstants::ObfuscateBoolConstantViaConstantPair(
uint32_t depth, const protobufs::IdUseDescriptor& bool_constant_use,
const std::vector<SpvOp>& greater_than_opcodes,
const std::vector<SpvOp>& less_than_opcodes, uint32_t constant_id_1,
uint32_t constant_id_2, bool first_constant_is_larger) {
auto bool_constant_opcode = GetIRContext()
->get_def_use_mgr()
->GetDef(bool_constant_use.id_of_interest())
->opcode();
assert((bool_constant_opcode == SpvOpConstantFalse ||
bool_constant_opcode == SpvOpConstantTrue) &&
"Precondition: this must be a usage of a boolean constant.");
// Pick an opcode at random. First randomly decide whether to generate
// a 'greater than' or 'less than' kind of opcode, and then select a
// random opcode from the resulting subset.
SpvOp comparison_opcode;
if (GetFuzzerContext()->GetRandomGenerator()->RandomBool()) {
comparison_opcode = greater_than_opcodes
[GetFuzzerContext()->GetRandomGenerator()->RandomUint32(
static_cast<uint32_t>(greater_than_opcodes.size()))];
} else {
comparison_opcode = less_than_opcodes
[GetFuzzerContext()->GetRandomGenerator()->RandomUint32(
static_cast<uint32_t>(less_than_opcodes.size()))];
}
// We now need to decide how to order constant_id_1 and constant_id_2 such
// that 'constant_id_1 comparison_opcode constant_id_2' evaluates to the
// boolean constant.
const bool is_greater_than_opcode =
std::find(greater_than_opcodes.begin(), greater_than_opcodes.end(),
comparison_opcode) != greater_than_opcodes.end();
uint32_t lhs_id;
uint32_t rhs_id;
if ((bool_constant_opcode == SpvOpConstantTrue &&
first_constant_is_larger == is_greater_than_opcode) ||
(bool_constant_opcode == SpvOpConstantFalse &&
first_constant_is_larger != is_greater_than_opcode)) {
lhs_id = constant_id_1;
rhs_id = constant_id_2;
} else {
lhs_id = constant_id_2;
rhs_id = constant_id_1;
}
// We can now make a transformation that will replace |bool_constant_use|
// with an expression of the form (written using infix notation):
// |lhs_id| |comparison_opcode| |rhs_id|
auto transformation = transformation::
MakeTransformationReplaceBooleanConstantWithConstantBinary(
bool_constant_use, lhs_id, rhs_id, comparison_opcode,
GetFuzzerContext()->GetFreshId());
// The transformation should be applicable by construction.
assert(transformation::IsApplicable(transformation, GetIRContext(),
*GetFactManager()));
// Applying this transformation yields a pointer to the new instruction that
// computes the result of the binary expression.
auto binary_operator_instruction =
transformation::Apply(transformation, GetIRContext(), GetFactManager());
// Add this transformation to the sequence of transformations that have been
// applied.
*GetTransformations()
->add_transformation()
->mutable_replace_boolean_constant_with_constant_binary() =
transformation;
// Having made a binary expression, there may now be opportunities to further
// obfuscate the constants used as the LHS and RHS of the expression (e.g. by
// replacing them with loads from known uniforms).
//
// We thus consider operands 0 and 1 (LHS and RHS in turn).
for (uint32_t index : {0u, 1u}) {
// We randomly decide, based on the current depth of obfuscation, whether
// to further obfuscate this operand.
if (GetFuzzerContext()->GoDeeperInConstantObfuscation()(
depth, GetFuzzerContext()->GetRandomGenerator())) {
auto in_operand_use = transformation::MakeIdUseDescriptor(
binary_operator_instruction->GetSingleWordInOperand(index),
binary_operator_instruction->opcode(), index,
binary_operator_instruction->result_id(), 0);
ObfuscateConstant(depth + 1, in_operand_use);
}
}
}
void FuzzerPassObfuscateConstants::ObfuscateBoolConstantViaFloatConstantPair(
uint32_t depth, const protobufs::IdUseDescriptor& bool_constant_use,
uint32_t float_constant_id_1, uint32_t float_constant_id_2) {
auto float_constant_1 = GetIRContext()
->get_constant_mgr()
->FindDeclaredConstant(float_constant_id_1)
->AsFloatConstant();
auto float_constant_2 = GetIRContext()
->get_constant_mgr()
->FindDeclaredConstant(float_constant_id_2)
->AsFloatConstant();
assert(float_constant_1->words() != float_constant_2->words() &&
"The constants should not be identical.");
assert(std::isfinite(float_constant_1->GetValueAsDouble()) &&
"The constants must be finite numbers.");
assert(std::isfinite(float_constant_2->GetValueAsDouble()) &&
"The constants must be finite numbers.");
bool first_constant_is_larger;
assert(float_constant_1->type()->AsFloat()->width() ==
float_constant_2->type()->AsFloat()->width() &&
"First and second floating-point constants must have the same width.");
if (float_constant_1->type()->AsFloat()->width() == 32) {
first_constant_is_larger =
float_constant_1->GetFloat() > float_constant_2->GetFloat();
} else {
assert(float_constant_1->type()->AsFloat()->width() == 64 &&
"Supported floating-point widths are 32 and 64.");
first_constant_is_larger =
float_constant_1->GetDouble() > float_constant_2->GetDouble();
}
std::vector<SpvOp> greater_than_opcodes{
SpvOpFOrdGreaterThan, SpvOpFOrdGreaterThanEqual, SpvOpFUnordGreaterThan,
SpvOpFUnordGreaterThanEqual};
std::vector<SpvOp> less_than_opcodes{
SpvOpFOrdGreaterThan, SpvOpFOrdGreaterThanEqual, SpvOpFUnordGreaterThan,
SpvOpFUnordGreaterThanEqual};
ObfuscateBoolConstantViaConstantPair(
depth, bool_constant_use, greater_than_opcodes, less_than_opcodes,
float_constant_id_1, float_constant_id_2, first_constant_is_larger);
}
void FuzzerPassObfuscateConstants::
ObfuscateBoolConstantViaSignedIntConstantPair(
uint32_t depth, const protobufs::IdUseDescriptor& bool_constant_use,
uint32_t signed_int_constant_id_1, uint32_t signed_int_constant_id_2) {
auto signed_int_constant_1 =
GetIRContext()
->get_constant_mgr()
->FindDeclaredConstant(signed_int_constant_id_1)
->AsIntConstant();
auto signed_int_constant_2 =
GetIRContext()
->get_constant_mgr()
->FindDeclaredConstant(signed_int_constant_id_2)
->AsIntConstant();
assert(signed_int_constant_1->words() != signed_int_constant_2->words() &&
"The constants should not be identical.");
bool first_constant_is_larger;
assert(signed_int_constant_1->type()->AsInteger()->width() ==
signed_int_constant_2->type()->AsInteger()->width() &&
"First and second floating-point constants must have the same width.");
assert(signed_int_constant_1->type()->AsInteger()->IsSigned());
assert(signed_int_constant_2->type()->AsInteger()->IsSigned());
if (signed_int_constant_1->type()->AsFloat()->width() == 32) {
first_constant_is_larger =
signed_int_constant_1->GetS32() > signed_int_constant_2->GetS32();
} else {
assert(signed_int_constant_1->type()->AsFloat()->width() == 64 &&
"Supported integer widths are 32 and 64.");
first_constant_is_larger =
signed_int_constant_1->GetS64() > signed_int_constant_2->GetS64();
}
std::vector<SpvOp> greater_than_opcodes{SpvOpSGreaterThan,
SpvOpSGreaterThanEqual};
std::vector<SpvOp> less_than_opcodes{SpvOpSLessThan, SpvOpSLessThanEqual};
ObfuscateBoolConstantViaConstantPair(
depth, bool_constant_use, greater_than_opcodes, less_than_opcodes,
signed_int_constant_id_1, signed_int_constant_id_2,
first_constant_is_larger);
}
void FuzzerPassObfuscateConstants::
ObfuscateBoolConstantViaUnsignedIntConstantPair(
uint32_t depth, const protobufs::IdUseDescriptor& bool_constant_use,
uint32_t unsigned_int_constant_id_1,
uint32_t unsigned_int_constant_id_2) {
auto unsigned_int_constant_1 =
GetIRContext()
->get_constant_mgr()
->FindDeclaredConstant(unsigned_int_constant_id_1)
->AsIntConstant();
auto unsigned_int_constant_2 =
GetIRContext()
->get_constant_mgr()
->FindDeclaredConstant(unsigned_int_constant_id_2)
->AsIntConstant();
assert(unsigned_int_constant_1->words() != unsigned_int_constant_2->words() &&
"The constants should not be identical.");
bool first_constant_is_larger;
assert(unsigned_int_constant_1->type()->AsInteger()->width() ==
unsigned_int_constant_2->type()->AsInteger()->width() &&
"First and second floating-point constants must have the same width.");
assert(!unsigned_int_constant_1->type()->AsInteger()->IsSigned());
assert(!unsigned_int_constant_2->type()->AsInteger()->IsSigned());
if (unsigned_int_constant_1->type()->AsFloat()->width() == 32) {
first_constant_is_larger =
unsigned_int_constant_1->GetU32() > unsigned_int_constant_2->GetU32();
} else {
assert(unsigned_int_constant_1->type()->AsFloat()->width() == 64 &&
"Supported integer widths are 32 and 64.");
first_constant_is_larger =
unsigned_int_constant_1->GetU64() > unsigned_int_constant_2->GetU64();
}
std::vector<SpvOp> greater_than_opcodes{SpvOpUGreaterThan,
SpvOpUGreaterThanEqual};
std::vector<SpvOp> less_than_opcodes{SpvOpULessThan, SpvOpULessThanEqual};
ObfuscateBoolConstantViaConstantPair(
depth, bool_constant_use, greater_than_opcodes, less_than_opcodes,
unsigned_int_constant_id_1, unsigned_int_constant_id_2,
first_constant_is_larger);
}
void FuzzerPassObfuscateConstants::ObfuscateBoolConstant(
uint32_t depth, const protobufs::IdUseDescriptor& constant_use) {
// We want to replace the boolean constant use with a binary expression over
// scalar constants, but only if we can then potentially replace the constants
// with uniforms of the same value.
auto available_types_with_uniforms =
GetFactManager()->GetTypesForWhichUniformValuesAreKnown();
if (available_types_with_uniforms.empty()) {
// Do not try to obfuscate if we do not have access to any uniform
// elements with known values.
return;
}
auto chosen_type_id = available_types_with_uniforms
[GetFuzzerContext()->GetRandomGenerator()->RandomUint32(
static_cast<uint32_t>(available_types_with_uniforms.size()))];
auto available_constants =
GetFactManager()->GetConstantsAvailableFromUniformsForType(
GetIRContext(), chosen_type_id);
if (available_constants.size() == 1) {
// TODO(afd): for now we only obfuscate a boolean if there are at least
// two constants available from uniforms, so that we can do a
// comparison between them. It would be good to be able to do the
// obfuscation even if there is only one such constant, if there is
// also another regular constant available.
return;
}
// We know we have at least two known-to-be-constant uniforms of the chosen
// type. Pick one of them at random.
auto constant_index_1 =
GetFuzzerContext()->GetRandomGenerator()->RandomUint32(
static_cast<uint32_t>(available_constants.size()));
uint32_t constant_index_2;
// Now choose another one distinct from the first one.
do {
constant_index_2 = GetFuzzerContext()->GetRandomGenerator()->RandomUint32(
static_cast<uint32_t>(available_constants.size()));
} while (constant_index_1 == constant_index_2);
auto constant_id_1 = available_constants[constant_index_1];
auto constant_id_2 = available_constants[constant_index_2];
assert(constant_id_1 != 0 && constant_id_2 != 0 &&
"We should not find an available constant with an id of 0.");
// Now perform the obfuscation, according to whether the type of the constants
// is float, signed int, or unsigned int.
auto chosen_type = GetIRContext()->get_type_mgr()->GetType(chosen_type_id);
if (chosen_type->AsFloat()) {
ObfuscateBoolConstantViaFloatConstantPair(depth, constant_use,
constant_id_1, constant_id_2);
} else {
assert(chosen_type->AsInteger() &&
"We should only have uniform facts about ints and floats.");
if (chosen_type->AsInteger()->IsSigned()) {
ObfuscateBoolConstantViaSignedIntConstantPair(
depth, constant_use, constant_id_1, constant_id_2);
} else {
ObfuscateBoolConstantViaUnsignedIntConstantPair(
depth, constant_use, constant_id_1, constant_id_2);
}
}
}
void FuzzerPassObfuscateConstants::ObfuscateScalarConstant(
uint32_t /*depth*/, const protobufs::IdUseDescriptor& constant_use) {
// TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/2670): consider
// additional ways to obfuscate scalar constants.
// Check whether we know that any uniforms are guaranteed to be equal to the
// scalar constant associated with |constant_use|.
auto uniform_descriptors = GetFactManager()->GetUniformDescriptorsForConstant(
GetIRContext(), constant_use.id_of_interest());
if (uniform_descriptors.empty()) {
// No relevant uniforms, so do not obfuscate.
return;
}
// Choose a random available uniform known to be equal to the constant.
protobufs::UniformBufferElementDescriptor uniform_descriptor =
uniform_descriptors
[GetFuzzerContext()->GetRandomGenerator()->RandomUint32(
static_cast<uint32_t>(uniform_descriptors.size()))];
// Create, apply and record a transformation to replace the constant use with
// the result of a load from the chosen uniform.
auto transformation =
transformation::MakeTransformationReplaceConstantWithUniform(
constant_use, uniform_descriptor, GetFuzzerContext()->GetFreshId(),
GetFuzzerContext()->GetFreshId());
// Transformation should be applicable by construction.
assert(transformation::IsApplicable(transformation, GetIRContext(),
*GetFactManager()));
transformation::Apply(transformation, GetIRContext(), GetFactManager());
*GetTransformations()
->add_transformation()
->mutable_replace_constant_with_uniform() = transformation;
}
void FuzzerPassObfuscateConstants::ObfuscateConstant(
uint32_t depth, const protobufs::IdUseDescriptor& constant_use) {
switch (GetIRContext()
->get_def_use_mgr()
->GetDef(constant_use.id_of_interest())
->opcode()) {
case SpvOpConstantTrue:
case SpvOpConstantFalse:
ObfuscateBoolConstant(depth, constant_use);
break;
case SpvOpConstant:
ObfuscateScalarConstant(depth, constant_use);
break;
default:
assert(false && "The opcode should be one of the above.");
break;
}
}
void FuzzerPassObfuscateConstants::MaybeAddConstantIdUse(
const opt::Instruction& inst, uint32_t in_operand_index,
uint32_t base_instruction_result_id,
const std::map<SpvOp, uint32_t>& skipped_opcode_count,
std::vector<protobufs::IdUseDescriptor>* constant_uses) {
if (inst.GetInOperand(in_operand_index).type != SPV_OPERAND_TYPE_ID) {
// The operand is not an id, so it cannot be a constant id.
return;
}
auto operand_id = inst.GetSingleWordInOperand(in_operand_index);
auto operand_definition =
GetIRContext()->get_def_use_mgr()->GetDef(operand_id);
switch (operand_definition->opcode()) {
case SpvOpConstantFalse:
case SpvOpConstantTrue:
case SpvOpConstant: {
// The operand is a constant id, so make an id use descriptor and record
// it.
protobufs::IdUseDescriptor id_use_descriptor;
id_use_descriptor.set_id_of_interest(operand_id);
id_use_descriptor.set_target_instruction_opcode(inst.opcode());
id_use_descriptor.set_in_operand_index(in_operand_index);
id_use_descriptor.set_base_instruction_result_id(
base_instruction_result_id);
id_use_descriptor.set_num_opcodes_to_ignore(
skipped_opcode_count.find(inst.opcode()) == skipped_opcode_count.end()
? 0
: skipped_opcode_count.at(inst.opcode()));
constant_uses->push_back(id_use_descriptor);
} break;
default:
break;
}
}
void FuzzerPassObfuscateConstants::Apply() {
// First, gather up all the constant uses available in the module, by going
// through each block in each function.
std::vector<protobufs::IdUseDescriptor> constant_uses;
for (auto& function : *GetIRContext()->module()) {
for (auto& block : function) {
// For each constant use we encounter we are going to make an id use
// descriptor. An id use is described with respect to a base instruction;
// if there are instructions at the start of the block without result ids,
// the base instruction will have to be the block's label.
uint32_t base_instruction_result_id = block.id();
// An id use descriptor also records how many instructions of a particular
// opcode need to be skipped in order to find the instruction of interest
// from the base instruction. We maintain a mapping that records a skip
// count for each relevant opcode.
std::map<SpvOp, uint32_t> skipped_opcode_count;
// Go through each instruction in the block.
for (auto& inst : block) {
if (inst.HasResultId()) {
// The instruction has a result id, so can be used as the base
// instruction from now on, until another instruction with a result id
// is encountered.
base_instruction_result_id = inst.result_id();
// Opcode skip counts were with respect to the previous base
// instruction and are now irrelevant.
skipped_opcode_count.clear();
}
// Consider each operand of the instruction, and add a constant id use
// for the operand if relevant.
for (uint32_t in_operand_index = 0;
in_operand_index < inst.NumInOperands(); in_operand_index++) {
MaybeAddConstantIdUse(inst, in_operand_index,
base_instruction_result_id,
skipped_opcode_count, &constant_uses);
}
if (!inst.HasResultId()) {
// The instruction has no result id, so in order to identify future id
// uses for instructions with this opcode from the existing base
// instruction, we need to increase the skip count for this opcode.
skipped_opcode_count[inst.opcode()] =
skipped_opcode_count.find(inst.opcode()) ==
skipped_opcode_count.end()
? 1
: skipped_opcode_count[inst.opcode()] + 1;
}
}
}
}
// Go through the constant uses in a random order by repeatedly pulling out a
// constant use at a random index.
while (!constant_uses.empty()) {
auto index = GetFuzzerContext()->GetRandomGenerator()->RandomUint32(
static_cast<uint32_t>(constant_uses.size()));
auto constant_use = std::move(constant_uses[index]);
constant_uses.erase(constant_uses.begin() + index);
// Decide probabilistically whether to skip or obfuscate this constant use.
if (GetFuzzerContext()->GetRandomGenerator()->RandomPercentage() >
GetFuzzerContext()->GetChanceOfObfuscatingConstant()) {
continue;
}
ObfuscateConstant(0, constant_use);
}
}
} // namespace fuzz
} // namespace spvtools

View File

@@ -0,0 +1,107 @@
// Copyright (c) 2019 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef SOURCE_FUZZ_FUZZER_PASS_OBFUSCATE_CONSTANTS_
#define SOURCE_FUZZ_FUZZER_PASS_OBFUSCATE_CONSTANTS_
#include <vector>
#include "source/fuzz/fuzzer_pass.h"
namespace spvtools {
namespace fuzz {
// A fuzzer pass for turning uses of constants into more complex forms.
// Examples include replacing 'true' with '42 < 52', and replacing '42' with
// 'a.b.c' if 'a.b.c' is known to hold the value '42'.
class FuzzerPassObfuscateConstants : public FuzzerPass {
public:
FuzzerPassObfuscateConstants(
opt::IRContext* ir_context, FactManager* fact_manager,
FuzzerContext* fuzzer_context,
protobufs::TransformationSequence* transformations);
~FuzzerPassObfuscateConstants() override;
void Apply() override;
private:
// Applies 0 or more transformations to potentially obfuscate the constant
// use represented by |constant_use|. The |depth| parameter controls how
// deeply obfuscation can recurse.
void ObfuscateConstant(uint32_t depth,
const protobufs::IdUseDescriptor& constant_use);
// This method will try to turn |constant_use|, required to be a use of a
// boolean constant, into a binary expression on scalar constants, which may
// themselves be recursively obfuscated.
void ObfuscateBoolConstant(uint32_t depth,
const protobufs::IdUseDescriptor& constant_use);
// This method will try to turn |constant_use|, required to be a use of a
// scalar constant, into the value loaded from a uniform known to have the
// same value as the constant (if one exists).
void ObfuscateScalarConstant(uint32_t depth,
const protobufs::IdUseDescriptor& constant_use);
// Applies a transformation to replace the boolean constant usage represented
// by |bool_constant_use| with a binary expression involving
// |float_constant_id_1| and |float_constant_id_2|, which must not be equal
// to one another. Possibly further obfuscates the uses of these float
// constants. The |depth| parameter controls how deeply obfuscation can
// recurse.
void ObfuscateBoolConstantViaFloatConstantPair(
uint32_t depth, const protobufs::IdUseDescriptor& bool_constant_use,
uint32_t float_constant_id_1, uint32_t float_constant_id_2);
// Similar to the above, but for signed int constants.
void ObfuscateBoolConstantViaSignedIntConstantPair(
uint32_t depth, const protobufs::IdUseDescriptor& bool_constant_use,
uint32_t signed_int_constant_id_1, uint32_t signed_int_constant_id_2);
// Similar to the above, but for unsigned int constants.
void ObfuscateBoolConstantViaUnsignedIntConstantPair(
uint32_t depth, const protobufs::IdUseDescriptor& bool_constant_use,
uint32_t unsigned_int_constant_id_1, uint32_t unsigned_int_constant_id_2);
// A helper method to capture the common parts of the above methods.
// The method is used to obfuscate the boolean constant usage represented by
// |bool_constant_use| by replacing it with '|constant_id_1| OP
// |constant_id_2|', where 'OP' is chosen from either |greater_than_opcodes|
// or |less_than_opcodes|.
//
// The two constant ids must not represent the same value, and thus
// |greater_than_opcodes| may include 'greater than or equal' opcodes
// (similar for |less_than_opcodes|).
void ObfuscateBoolConstantViaConstantPair(
uint32_t depth, const protobufs::IdUseDescriptor& bool_constant_use,
const std::vector<SpvOp>& greater_than_opcodes,
const std::vector<SpvOp>& less_than_opcodes, uint32_t constant_id_1,
uint32_t constant_id_2, bool first_constant_is_larger);
// A helper method to determine whether input operand |in_operand_index| of
// |inst| is the id of a constant, and add an id use descriptor to
// |candidate_constant_uses| if so. The other parameters are used for id use
// descriptor construction.
void MaybeAddConstantIdUse(
const opt::Instruction& inst, uint32_t in_operand_index,
uint32_t base_instruction_result_id,
const std::map<SpvOp, uint32_t>& skipped_opcode_count,
std::vector<protobufs::IdUseDescriptor>* constant_uses);
};
} // namespace fuzz
} // namespace spvtools
#endif // #define SOURCE_FUZZ_FUZZER_PASS_OBFUSCATE_CONSTANTS_

View File

@@ -64,8 +64,9 @@ message UniformBufferElementDescriptor {
// is contained, and (b) a series of indices that need to be followed to get
// to the element (via fields and array/vector indices).
//
// Example: suppose %42 is the id of a uniform variable, and that the uniform
// variable has the following type (using GLSL-like syntax):
// Example: suppose there is a uniform variable with descriptor set 7 and
// binding 9, and that the uniform variable has the following type (using
// GLSL-like syntax):
//
// struct S {
// float f;
@@ -74,16 +75,17 @@ message UniformBufferElementDescriptor {
// };
//
// Then:
// - 42[0] describes the 'f' field.
// - 42[1,1] describes the y component of the 'g' field.
// - 42[2,7,3] describes the w component of element 7 of the 'h' field
// - (7, 9, [0]) describes the 'f' field.
// - (7, 9, [1,1]) describes the y component of the 'g' field.
// - (7, 9, [2,7,3]) describes the w component of element 7 of the 'h' field
// The result id of a uniform variable.
uint32 uniform_variable_id = 1;
// The descriptor set and binding associated with a uniform variable.
uint32 descriptor_set = 1;
uint32 binding = 2;
// An ordered sequence of indices through composite structures in the
// uniform buffer.
repeated uint32 index = 2;
repeated uint32 index = 3;
}
@@ -133,6 +135,8 @@ message Transformation {
TransformationAddTypeInt add_type_int = 7;
TransformationAddDeadBreak add_dead_break = 8;
TransformationReplaceBooleanConstantWithConstantBinary replace_boolean_constant_with_constant_binary = 9;
TransformationAddTypePointer add_type_pointer = 10;
TransformationReplaceConstantWithUniform replace_constant_with_uniform = 11;
// Add additional option using the next available number.
}
}
@@ -222,6 +226,22 @@ message TransformationAddTypeInt {
}
message TransformationAddTypePointer {
// Adds OpTypePointer to the module, with the given storage class and base
// type
// Id to be used for the type
uint32 fresh_id = 1;
// Pointer storage class
uint32 storage_class = 2;
// Id of the base type for the pointer
uint32 base_type_id = 3;
}
message TransformationMoveBlockDown {
// A transformation that moves a basic block to be one position lower in
@@ -229,6 +249,24 @@ message TransformationMoveBlockDown {
// The id of the block to move down.
uint32 block_id = 1;
}
message TransformationReplaceConstantWithUniform {
// Replaces a use of a constant id with the the result of a load from an
// element of uniform buffer known to hold the same value as the constant
// A descriptor for the id we would like to replace
IdUseDescriptor id_use_descriptor = 1;
// Uniform descriptor to identify which uniform value to choose
UniformBufferElementDescriptor uniform_descriptor = 2;
// Id that will store the result of an access chain
uint32 fresh_id_for_access_chain = 3;
// Id that will store the result of a load
uint32 fresh_id_for_load = 4;
}

View File

@@ -24,8 +24,10 @@
#include "source/fuzz/transformation_add_type_boolean.h"
#include "source/fuzz/transformation_add_type_float.h"
#include "source/fuzz/transformation_add_type_int.h"
#include "source/fuzz/transformation_add_type_pointer.h"
#include "source/fuzz/transformation_move_block_down.h"
#include "source/fuzz/transformation_replace_boolean_constant_with_constant_binary.h"
#include "source/fuzz/transformation_replace_constant_with_uniform.h"
#include "source/fuzz/transformation_split_block.h"
#include "source/opt/build_module.h"
#include "source/util/make_unique.h"
@@ -58,6 +60,9 @@ bool IsApplicable(const protobufs::Transformation& transformation,
case protobufs::Transformation::TransformationCase::kAddTypeInt:
return transformation::IsApplicable(transformation.add_type_int(),
context, fact_manager);
case protobufs::Transformation::TransformationCase::kAddTypePointer:
return transformation::IsApplicable(transformation.add_type_pointer(),
context, fact_manager);
case protobufs::Transformation::TransformationCase::kMoveBlockDown:
return transformation::IsApplicable(transformation.move_block_down(),
context, fact_manager);
@@ -66,6 +71,11 @@ bool IsApplicable(const protobufs::Transformation& transformation,
return transformation::IsApplicable(
transformation.replace_boolean_constant_with_constant_binary(),
context, fact_manager);
case protobufs::Transformation::TransformationCase::
kReplaceConstantWithUniform:
return transformation::IsApplicable(
transformation.replace_constant_with_uniform(), context,
fact_manager);
case protobufs::Transformation::TransformationCase::kSplitBlock:
return transformation::IsApplicable(transformation.split_block(), context,
fact_manager);
@@ -107,6 +117,10 @@ void Apply(const protobufs::Transformation& transformation,
transformation::Apply(transformation.add_type_int(), context,
fact_manager);
break;
case protobufs::Transformation::TransformationCase::kAddTypePointer:
transformation::Apply(transformation.add_type_pointer(), context,
fact_manager);
break;
case protobufs::Transformation::TransformationCase::kMoveBlockDown:
transformation::Apply(transformation.move_block_down(), context,
fact_manager);
@@ -117,6 +131,11 @@ void Apply(const protobufs::Transformation& transformation,
transformation.replace_boolean_constant_with_constant_binary(),
context, fact_manager);
break;
case protobufs::Transformation::TransformationCase::
kReplaceConstantWithUniform:
transformation::Apply(transformation.replace_constant_with_uniform(),
context, fact_manager);
break;
case protobufs::Transformation::TransformationCase::kSplitBlock:
transformation::Apply(transformation.split_block(), context,
fact_manager);

View File

@@ -0,0 +1,61 @@
// Copyright (c) 2019 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "source/fuzz/transformation_add_type_pointer.h"
#include "source/fuzz/fuzzer_util.h"
namespace spvtools {
namespace fuzz {
namespace transformation {
using opt::IRContext;
bool IsApplicable(const protobufs::TransformationAddTypePointer& message,
IRContext* context,
const spvtools::fuzz::FactManager& /*unused*/) {
// The id must be fresh.
if (!fuzzerutil::IsFreshId(context, message.fresh_id())) {
return false;
}
// The base type must be known.
return context->get_type_mgr()->GetType(message.base_type_id()) != nullptr;
}
void Apply(const protobufs::TransformationAddTypePointer& message,
IRContext* context, spvtools::fuzz::FactManager* /*unused*/) {
// Add the pointer type.
opt::Instruction::OperandList in_operands = {
{SPV_OPERAND_TYPE_STORAGE_CLASS, {message.storage_class()}},
{SPV_OPERAND_TYPE_ID, {message.base_type_id()}}};
context->module()->AddType(MakeUnique<opt::Instruction>(
context, SpvOpTypePointer, 0, message.fresh_id(), in_operands));
fuzzerutil::UpdateModuleIdBound(context, message.fresh_id());
// We have added an instruction to the module, so need to be careful about the
// validity of existing analyses.
context->InvalidateAnalysesExceptFor(IRContext::Analysis::kAnalysisNone);
}
protobufs::TransformationAddTypePointer MakeTransformationAddTypePointer(
uint32_t fresh_id, SpvStorageClass storage_class, uint32_t base_type_id) {
protobufs::TransformationAddTypePointer result;
result.set_fresh_id(fresh_id);
result.set_storage_class(storage_class);
result.set_base_type_id(base_type_id);
return result;
}
} // namespace transformation
} // namespace fuzz
} // namespace spvtools

View File

@@ -0,0 +1,44 @@
// Copyright (c) 2019 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef SOURCE_FUZZ_TRANSFORMATION_ADD_TYPE_POINTER_H_
#define SOURCE_FUZZ_TRANSFORMATION_ADD_TYPE_POINTER_H_
#include "source/fuzz/fact_manager.h"
#include "source/fuzz/protobufs/spirvfuzz_protobufs.h"
#include "source/opt/ir_context.h"
namespace spvtools {
namespace fuzz {
namespace transformation {
// - |message.fresh_id| must not be used by the module
// - |message.base_type_id| must be the result id of an OpType[...] instruction
bool IsApplicable(const protobufs::TransformationAddTypePointer& message,
opt::IRContext* context, const FactManager& fact_manager);
// Adds an OpTypePointer instruction with the given storage class and base type
// to the module.
void Apply(const protobufs::TransformationAddTypePointer& message,
opt::IRContext* context, FactManager* fact_manager);
// Helper factory to create a transformation message.
protobufs::TransformationAddTypePointer MakeTransformationAddTypePointer(
uint32_t fresh_id, SpvStorageClass storage_class, uint32_t base_type_id);
} // namespace transformation
} // namespace fuzz
} // namespace spvtools
#endif // SOURCE_FUZZ_TRANSFORMATION_ADD_TYPE_POINTER_H_

View File

@@ -0,0 +1,230 @@
// Copyright (c) 2019 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "source/fuzz/transformation_replace_constant_with_uniform.h"
#include "source/fuzz/fuzzer_util.h"
#include "source/fuzz/uniform_buffer_element_descriptor.h"
namespace spvtools {
namespace fuzz {
namespace transformation {
namespace {
std::unique_ptr<opt::Instruction> MakeAccessChainInstruction(
const protobufs::TransformationReplaceConstantWithUniform& message,
spvtools::opt::IRContext* context, uint32_t constant_type_id) {
// The input operands for the access chain.
opt::Instruction::OperandList operands_for_access_chain;
opt::Instruction* uniform_variable =
FindUniformVariable(message.uniform_descriptor(), context, false);
// The first input operand is the id of the uniform variable.
operands_for_access_chain.push_back(
{SPV_OPERAND_TYPE_ID, {uniform_variable->result_id()}});
// The other input operands are the ids of the constants used to index into
// the uniform. The uniform buffer descriptor specifies a series of literals;
// for each we find the id of the instruction that defines it, and add these
// instruction ids as operands.
opt::analysis::Integer int_type(32, true);
auto registered_int_type =
context->get_type_mgr()->GetRegisteredType(&int_type)->AsInteger();
auto int_type_id = context->get_type_mgr()->GetId(&int_type);
for (auto index : message.uniform_descriptor().index()) {
opt::analysis::IntConstant int_constant(registered_int_type, {index});
auto constant_id = context->get_constant_mgr()->FindDeclaredConstant(
&int_constant, int_type_id);
operands_for_access_chain.push_back({SPV_OPERAND_TYPE_ID, {constant_id}});
}
// The type id for the access chain is a uniform pointer with base type
// matching the given constant id type.
auto type_and_pointer_type = context->get_type_mgr()->GetTypeAndPointerType(
constant_type_id, SpvStorageClassUniform);
assert(type_and_pointer_type.first != nullptr);
assert(type_and_pointer_type.second != nullptr);
auto pointer_to_uniform_constant_type_id =
context->get_type_mgr()->GetId(type_and_pointer_type.second.get());
return MakeUnique<opt::Instruction>(
context, SpvOpAccessChain, pointer_to_uniform_constant_type_id,
message.fresh_id_for_access_chain(), operands_for_access_chain);
}
std::unique_ptr<opt::Instruction> MakeLoadInstruction(
const protobufs::TransformationReplaceConstantWithUniform& message,
spvtools::opt::IRContext* context, uint32_t constant_type_id) {
opt::Instruction::OperandList operands_for_load = {
{SPV_OPERAND_TYPE_ID, {message.fresh_id_for_access_chain()}}};
return MakeUnique<opt::Instruction>(context, SpvOpLoad, constant_type_id,
message.fresh_id_for_load(),
operands_for_load);
}
} // namespace
bool IsApplicable(
const protobufs::TransformationReplaceConstantWithUniform& message,
spvtools::opt::IRContext* context,
const spvtools::fuzz::FactManager& fact_manager) {
// The following is really an invariant of the transformation rather than
// merely a requirement of the precondition. We check it here since we cannot
// check it in the message constructor.
assert(message.fresh_id_for_access_chain() != message.fresh_id_for_load() &&
"Fresh ids for access chain and load result cannot be the same.");
// The ids for the access chain and load instructions must both be fresh.
if (!fuzzerutil::IsFreshId(context, message.fresh_id_for_access_chain())) {
return false;
}
if (!fuzzerutil::IsFreshId(context, message.fresh_id_for_load())) {
return false;
}
// The id specified in the id use descriptor must be that of a declared scalar
// constant.
auto declared_constant = context->get_constant_mgr()->FindDeclaredConstant(
message.id_use_descriptor().id_of_interest());
if (!declared_constant) {
return false;
}
if (!declared_constant->AsScalarConstant()) {
return false;
}
// The fact manager needs to believe that the uniform data element described
// by the uniform buffer element descriptor will hold a scalar value.
auto constant_id_associated_with_uniform =
fact_manager.GetConstantFromUniformDescriptor(
context, message.uniform_descriptor());
if (!constant_id_associated_with_uniform) {
return false;
}
auto constant_associated_with_uniform =
context->get_constant_mgr()->FindDeclaredConstant(
constant_id_associated_with_uniform);
assert(constant_associated_with_uniform &&
"The constant should be present in the module.");
if (!constant_associated_with_uniform->AsScalarConstant()) {
return false;
}
// The types and values of the scalar value held in the id specified by the id
// use descriptor and in the uniform data element specified by the uniform
// buffer element descriptor need to match on both type and value.
if (!declared_constant->type()->IsSame(
constant_associated_with_uniform->type())) {
return false;
}
if (declared_constant->AsScalarConstant()->words() !=
constant_associated_with_uniform->AsScalarConstant()->words()) {
return false;
}
// The id use descriptor must identify some instruction with respect to the
// module.
auto instruction_using_constant =
transformation::FindInstruction(message.id_use_descriptor(), context);
if (!instruction_using_constant) {
return false;
}
// The module needs to have a uniform pointer type suitable for indexing into
// the uniform variable, i.e. matching the type of the constant we wish to
// replace with a uniform.
opt::analysis::Pointer pointer_to_type_of_constant(declared_constant->type(),
SpvStorageClassUniform);
if (!context->get_type_mgr()->GetId(&pointer_to_type_of_constant)) {
return false;
}
// In order to index into the uniform, the module has got to contain the int32
// type, plus an OpConstant for each of the indices of interest.
opt::analysis::Integer int_type(32, true);
if (!context->get_type_mgr()->GetId(&int_type)) {
return false;
}
auto registered_int_type =
context->get_type_mgr()->GetRegisteredType(&int_type)->AsInteger();
auto int_type_id = context->get_type_mgr()->GetId(&int_type);
for (auto index : message.uniform_descriptor().index()) {
opt::analysis::IntConstant int_constant(registered_int_type, {index});
if (!context->get_constant_mgr()->FindDeclaredConstant(&int_constant,
int_type_id)) {
return false;
}
}
return true;
}
void Apply(const protobufs::TransformationReplaceConstantWithUniform& message,
spvtools::opt::IRContext* context,
spvtools::fuzz::FactManager* /*unused*/) {
// Get the instruction that contains the id use we wish to replace.
auto instruction_containing_constant_use =
transformation::FindInstruction(message.id_use_descriptor(), context);
assert(instruction_containing_constant_use &&
"Precondition requires that the id use can be found.");
assert(instruction_containing_constant_use->GetSingleWordInOperand(
message.id_use_descriptor().in_operand_index()) ==
message.id_use_descriptor().id_of_interest() &&
"Does not appear to be a usage of the desired id.");
// The id of the type for the constant whose use we wish to replace.
auto constant_type_id =
context->get_def_use_mgr()
->GetDef(message.id_use_descriptor().id_of_interest())
->type_id();
// Add an access chain instruction to target the uniform element.
instruction_containing_constant_use->InsertBefore(
MakeAccessChainInstruction(message, context, constant_type_id));
// Add a load from this access chain.
instruction_containing_constant_use->InsertBefore(
MakeLoadInstruction(message, context, constant_type_id));
// Adjust the instruction containing the usage of the constant so that this
// usage refers instead to the result of the load.
instruction_containing_constant_use->SetInOperand(
message.id_use_descriptor().in_operand_index(),
{message.fresh_id_for_load()});
// Update the module id bound to reflect the new instructions.
fuzzerutil::UpdateModuleIdBound(context, message.fresh_id_for_load());
fuzzerutil::UpdateModuleIdBound(context, message.fresh_id_for_access_chain());
context->InvalidateAnalysesExceptFor(opt::IRContext::Analysis::kAnalysisNone);
}
protobufs::TransformationReplaceConstantWithUniform
MakeTransformationReplaceConstantWithUniform(
protobufs::IdUseDescriptor id_use,
protobufs::UniformBufferElementDescriptor uniform_descriptor,
uint32_t fresh_id_for_access_chain, uint32_t fresh_id_for_load) {
protobufs::TransformationReplaceConstantWithUniform result;
*result.mutable_id_use_descriptor() = std::move(id_use);
*result.mutable_uniform_descriptor() = std::move(uniform_descriptor);
result.set_fresh_id_for_access_chain(fresh_id_for_access_chain);
result.set_fresh_id_for_load(fresh_id_for_load);
return result;
}
} // namespace transformation
} // namespace fuzz
} // namespace spvtools

View File

@@ -0,0 +1,73 @@
#include <utility>
// Copyright (c) 2019 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef SOURCE_FUZZ_TRANSFORMATION_REPLACE_CONSTANT_WITH_UNIFORM_H_
#define SOURCE_FUZZ_TRANSFORMATION_REPLACE_CONSTANT_WITH_UNIFORM_H_
#include "source/fuzz/fact_manager.h"
#include "source/fuzz/id_use_descriptor.h"
#include "source/fuzz/protobufs/spirvfuzz_protobufs.h"
#include "source/opt/ir_context.h"
namespace spvtools {
namespace fuzz {
namespace transformation {
// - |fresh_id_for_access_chain| and |fresh_id_for_load| must be distinct fresh
// ids.
// - |uniform_descriptor| specifies a result id and a list of integer literal
// indices.
// As an example, suppose |uniform_descriptor| is (18, [0, 1, 0])
// It is required that:
// - the result id (18 in our example) is the id of some uniform variable
// - the module contains an integer constant instruction corresponding to
// each of the literal indices; in our example there must thus be
// OpConstant instructions %A and %B say for each of 0 and 1
// - it is legitimate to index into the uniform variable using the
// sequence of indices; in our example this means indexing into %18 using
// the sequence %A %B %A
// - the module contains a uniform pointer type corresponding to the type
// of the uniform data element obtained by following these indices
// - |id_use_descriptor| identifies the use of some id %C. It is required that:
// - this use does indeed exist in the module
// - %C is an OpConstant
// - According to the fact manager, the uniform data element specified by
// |uniform_descriptor| holds a value with the same type and value as %C
bool IsApplicable(
const protobufs::TransformationReplaceConstantWithUniform& message,
opt::IRContext* context, const FactManager& fact_manager);
// - Introduces two new instructions:
// - An access chain targeting the uniform data element specified by
// |uniform_descriptor|, with result id |fresh_id_for_access_chain|
// - A load from this access chain, with id |fresh_id_for_load|
// - Replaces the id use specified by |id_use_descriptor| with
// |fresh_id_for_load|
void Apply(const protobufs::TransformationReplaceConstantWithUniform& message,
opt::IRContext* context, FactManager* fact_manager);
// Helper factory to create a transformation message.
protobufs::TransformationReplaceConstantWithUniform
MakeTransformationReplaceConstantWithUniform(
protobufs::IdUseDescriptor id_use,
protobufs::UniformBufferElementDescriptor uniform_descriptor,
uint32_t fresh_id_for_access_chain, uint32_t fresh_id_for_load);
} // namespace transformation
} // namespace fuzz
} // namespace spvtools
#endif // SOURCE_FUZZ_TRANSFORMATION_REPLACE_CONSTANT_WITH_UNIFORM_H_

View File

@@ -14,13 +14,17 @@
#include "source/fuzz/uniform_buffer_element_descriptor.h"
#include <source/opt/instruction.h>
namespace spvtools {
namespace fuzz {
protobufs::UniformBufferElementDescriptor MakeUniformBufferElementDescriptor(
uint32_t uniform_variable_id, std::vector<uint32_t>&& indices) {
uint32_t descriptor_set, uint32_t binding,
std::vector<uint32_t>&& indices) {
protobufs::UniformBufferElementDescriptor result;
result.set_uniform_variable_id(uniform_variable_id);
result.set_descriptor_set(descriptor_set);
result.set_binding(binding);
for (auto index : indices) {
result.add_index(index);
}
@@ -30,10 +34,85 @@ protobufs::UniformBufferElementDescriptor MakeUniformBufferElementDescriptor(
bool UniformBufferElementDescriptorEquals::operator()(
const protobufs::UniformBufferElementDescriptor* first,
const protobufs::UniformBufferElementDescriptor* second) const {
return first->uniform_variable_id() == second->uniform_variable_id() &&
return first->descriptor_set() == second->descriptor_set() &&
first->binding() == second->binding() &&
first->index().size() == second->index().size() &&
std::equal(first->index().begin(), first->index().end(),
second->index().begin());
}
opt::Instruction* FindUniformVariable(
const protobufs::UniformBufferElementDescriptor&
uniform_buffer_element_descriptor,
opt::IRContext* context, bool check_unique) {
opt::Instruction* result = nullptr;
for (auto& inst : context->types_values()) {
// Consider all global variables with uniform storage class.
if (inst.opcode() != SpvOpVariable) {
continue;
}
if (inst.GetSingleWordInOperand(0) != SpvStorageClassUniform) {
continue;
}
// Determine whether the variable is decorated with a descriptor set
// matching that in |uniform_buffer_element|.
bool descriptor_set_matches = false;
context->get_decoration_mgr()->ForEachDecoration(
inst.result_id(), SpvDecorationDescriptorSet,
[&descriptor_set_matches, &uniform_buffer_element_descriptor](
const opt::Instruction& decoration_inst) {
const uint32_t kDescriptorSetOperandIndex = 2;
if (decoration_inst.GetSingleWordInOperand(
kDescriptorSetOperandIndex) ==
uniform_buffer_element_descriptor.descriptor_set()) {
descriptor_set_matches = true;
}
});
if (!descriptor_set_matches) {
// Descriptor set does not match.
continue;
}
// Determine whether the variable is decorated with a binding matching that
// in |uniform_buffer_element|.
bool binding_matches = false;
context->get_decoration_mgr()->ForEachDecoration(
inst.result_id(), SpvDecorationBinding,
[&binding_matches, &uniform_buffer_element_descriptor](
const opt::Instruction& decoration_inst) {
const uint32_t kBindingOperandIndex = 2;
if (decoration_inst.GetSingleWordInOperand(kBindingOperandIndex) ==
uniform_buffer_element_descriptor.binding()) {
binding_matches = true;
}
});
if (!binding_matches) {
// Binding does not match.
continue;
}
// This instruction is a uniform variable with the right descriptor set and
// binding.
if (!check_unique) {
// If we aren't checking uniqueness, return it.
return &inst;
}
if (result) {
// More than one uniform variable is decorated with the given descriptor
// set and binding. This means the fact is ambiguous.
return nullptr;
}
result = &inst;
}
// We get here either if no match was found, or if |check_unique| holds and
// exactly one match was found.
assert(result == nullptr || check_unique);
return result;
}
} // namespace fuzz
} // namespace spvtools

View File

@@ -19,6 +19,8 @@
#include <vector>
#include "source/fuzz/protobufs/spirvfuzz_protobufs.h"
#include "source/opt/instruction.h"
#include "source/opt/ir_context.h"
namespace spvtools {
namespace fuzz {
@@ -26,7 +28,7 @@ namespace fuzz {
// Factory method to create a uniform buffer element descriptor message from an
// id and list of indices.
protobufs::UniformBufferElementDescriptor MakeUniformBufferElementDescriptor(
uint32_t uniform_variable_id, std::vector<uint32_t>&& indices);
uint32_t descriptor_set, uint32_t binding, std::vector<uint32_t>&& indices);
// Equality function for uniform buffer element descriptors.
struct UniformBufferElementDescriptorEquals {
@@ -35,6 +37,16 @@ struct UniformBufferElementDescriptorEquals {
const protobufs::UniformBufferElementDescriptor* second) const;
};
// Returns a pointer to an OpVariable in |context| that is decorated with the
// descriptor set and binding associated with |uniform_buffer_element|. Returns
// nullptr if no such variable exists. If multiple such variables exist, a
// pointer to an arbitrary one of the associated instructions is returned if
// |check_unique| is false, and nullptr is returned if |check_unique| is true.
opt::Instruction* FindUniformVariable(
const protobufs::UniformBufferElementDescriptor&
uniform_buffer_element_descriptor,
opt::IRContext* context, bool check_unique);
} // namespace fuzz
} // namespace spvtools

View File

@@ -868,6 +868,7 @@ void AggressiveDCEPass::InitExtensions() {
"SPV_AMD_gpu_shader_half_float_fetch",
"SPV_GOOGLE_decorate_string",
"SPV_GOOGLE_hlsl_functionality1",
"SPV_GOOGLE_user_type",
"SPV_NV_shader_subgroup_partitioned",
"SPV_EXT_descriptor_indexing",
"SPV_NV_fragment_shader_barycentric",

View File

@@ -579,6 +579,7 @@ void CommonUniformElimPass::InitExtensions() {
"SPV_AMD_gpu_shader_half_float_fetch",
"SPV_GOOGLE_decorate_string",
"SPV_GOOGLE_hlsl_functionality1",
"SPV_GOOGLE_user_type",
"SPV_NV_shader_subgroup_partitioned",
"SPV_EXT_descriptor_indexing",
"SPV_NV_fragment_shader_barycentric",

View File

@@ -343,6 +343,7 @@ void LocalAccessChainConvertPass::InitExtensions() {
"SPV_AMD_gpu_shader_half_float_fetch",
"SPV_GOOGLE_decorate_string",
"SPV_GOOGLE_hlsl_functionality1",
"SPV_GOOGLE_user_type",
"SPV_NV_shader_subgroup_partitioned",
"SPV_EXT_descriptor_indexing",
"SPV_NV_fragment_shader_barycentric",

View File

@@ -246,6 +246,7 @@ void LocalSingleBlockLoadStoreElimPass::InitExtensions() {
"SPV_AMD_gpu_shader_half_float_fetch",
"SPV_GOOGLE_decorate_string",
"SPV_GOOGLE_hlsl_functionality1",
"SPV_GOOGLE_user_type",
"SPV_NV_shader_subgroup_partitioned",
"SPV_EXT_descriptor_indexing",
"SPV_NV_fragment_shader_barycentric",

View File

@@ -95,6 +95,7 @@ void LocalMultiStoreElimPass::InitExtensions() {
"SPV_AMD_gpu_shader_half_float_fetch",
"SPV_GOOGLE_decorate_string",
"SPV_GOOGLE_hlsl_functionality1",
"SPV_GOOGLE_user_type",
"SPV_NV_shader_subgroup_partitioned",
"SPV_EXT_descriptor_indexing",
"SPV_NV_fragment_shader_barycentric",

View File

@@ -25,6 +25,12 @@ namespace spvtools {
namespace opt {
Pass::Status UpgradeMemoryModel::Process() {
// TODO: This pass needs changes to support cooperative matrices.
if (context()->get_feature_mgr()->HasCapability(
SpvCapabilityCooperativeMatrixNV)) {
return Pass::Status::SuccessWithoutChange;
}
// Only update Logical GLSL450 to Logical VulkanKHR.
Instruction* memory_model = get_module()->GetMemoryModel();
if (memory_model->GetSingleWordInOperand(0u) != SpvAddressingModelLogical ||
@@ -110,6 +116,12 @@ void UpgradeMemoryModel::UpgradeInstructions() {
}
});
}
UpgradeMemoryAndImages();
UpgradeAtomics();
}
void UpgradeMemoryModel::UpgradeMemoryAndImages() {
for (auto& func : *get_module()) {
func.ForEachInst([this](Instruction* inst) {
bool is_coherent = false;
@@ -239,6 +251,50 @@ void UpgradeMemoryModel::UpgradeInstructions() {
}
}
void UpgradeMemoryModel::UpgradeAtomics() {
for (auto& func : *get_module()) {
func.ForEachInst([this](Instruction* inst) {
if (spvOpcodeIsAtomicOp(inst->opcode())) {
bool unused_coherent = false;
bool is_volatile = false;
SpvScope unused_scope = SpvScopeQueueFamilyKHR;
std::tie(unused_coherent, is_volatile, unused_scope) =
GetInstructionAttributes(inst->GetSingleWordInOperand(0));
UpgradeSemantics(inst, 2u, is_volatile);
if (inst->opcode() == SpvOpAtomicCompareExchange ||
inst->opcode() == SpvOpAtomicCompareExchangeWeak) {
UpgradeSemantics(inst, 3u, is_volatile);
}
}
});
}
}
void UpgradeMemoryModel::UpgradeSemantics(Instruction* inst,
uint32_t in_operand,
bool is_volatile) {
if (!is_volatile) return;
uint32_t semantics_id = inst->GetSingleWordInOperand(in_operand);
const analysis::Constant* constant =
context()->get_constant_mgr()->FindDeclaredConstant(semantics_id);
const analysis::Integer* type = constant->type()->AsInteger();
assert(type && type->width() == 32);
uint32_t value = 0;
if (type->IsSigned()) {
value = static_cast<uint32_t>(constant->GetS32());
} else {
value = constant->GetU32();
}
value |= SpvMemorySemanticsVolatileMask;
auto new_constant = context()->get_constant_mgr()->GetConstant(type, {value});
auto new_semantics =
context()->get_constant_mgr()->GetDefiningInstruction(new_constant);
inst->SetInOperand(in_operand, {new_semantics->result_id()});
}
std::tuple<bool, bool, SpvScope> UpgradeMemoryModel::GetInstructionAttributes(
uint32_t id) {
// |id| is a pointer used in a memory/image instruction. Need to determine if

View File

@@ -57,12 +57,19 @@ class UpgradeMemoryModel : public Pass {
// capability and extension.
void UpgradeMemoryModelInstruction();
// Upgrades memory, image and barrier instructions.
// Upgrades memory, image and atomic instructions.
// Memory and image instructions convert coherent and volatile decorations
// into flags on the instruction. Barriers in tessellation shaders get the
// output storage semantic if appropriate.
// into flags on the instruction.
// Atomic memory semantics convert volatile decoration into flags on the
// instruction.
void UpgradeInstructions();
// Upgrades memory and image operands for instructions that have them.
void UpgradeMemoryAndImages();
// Adds the volatile memory semantic if necessary.
void UpgradeAtomics();
// Returns whether |id| is coherent and/or volatile.
std::tuple<bool, bool, SpvScope> GetInstructionAttributes(uint32_t id);
@@ -95,6 +102,11 @@ class UpgradeMemoryModel : public Pass {
bool is_volatile, OperationType operation_type,
InstructionType inst_type);
// Modifies the semantics at |in_operand| of |inst| to include the volatile
// bit if |is_volatile| is true.
void UpgradeSemantics(Instruction* inst, uint32_t in_operand,
bool is_volatile);
// Returns the result id for a constant for |scope|.
uint32_t GetScopeConstant(SpvScope scope);

View File

@@ -103,80 +103,45 @@ uint32_t spvVersionForTargetEnv(spv_target_env env) {
return SPV_SPIRV_VERSION_WORD(0, 0);
}
static const std::pair<const char*, spv_target_env> spvTargetEnvNameMap[] = {
{"vulkan1.1spv1.4", SPV_ENV_VULKAN_1_1_SPIRV_1_4},
{"vulkan1.0", SPV_ENV_VULKAN_1_0},
{"vulkan1.1", SPV_ENV_VULKAN_1_1},
{"spv1.0", SPV_ENV_UNIVERSAL_1_0},
{"spv1.1", SPV_ENV_UNIVERSAL_1_1},
{"spv1.2", SPV_ENV_UNIVERSAL_1_2},
{"spv1.3", SPV_ENV_UNIVERSAL_1_3},
{"spv1.4", SPV_ENV_UNIVERSAL_1_4},
{"opencl1.2embedded", SPV_ENV_OPENCL_EMBEDDED_1_2},
{"opencl1.2", SPV_ENV_OPENCL_1_2},
{"opencl2.0embedded", SPV_ENV_OPENCL_EMBEDDED_2_0},
{"opencl2.0", SPV_ENV_OPENCL_2_0},
{"opencl2.1embedded", SPV_ENV_OPENCL_EMBEDDED_2_1},
{"opencl2.1", SPV_ENV_OPENCL_2_1},
{"opencl2.2embedded", SPV_ENV_OPENCL_EMBEDDED_2_2},
{"opencl2.2", SPV_ENV_OPENCL_2_2},
{"opengl4.0", SPV_ENV_OPENGL_4_0},
{"opengl4.1", SPV_ENV_OPENGL_4_1},
{"opengl4.2", SPV_ENV_OPENGL_4_2},
{"opengl4.3", SPV_ENV_OPENGL_4_3},
{"opengl4.5", SPV_ENV_OPENGL_4_5},
{"webgpu0", SPV_ENV_WEBGPU_0},
};
bool spvParseTargetEnv(const char* s, spv_target_env* env) {
auto match = [s](const char* b) {
return s && (0 == strncmp(s, b, strlen(b)));
};
if (match("vulkan1.1spv1.4")) {
if (env) *env = SPV_ENV_VULKAN_1_1_SPIRV_1_4;
return true;
} else if (match("vulkan1.0")) {
if (env) *env = SPV_ENV_VULKAN_1_0;
return true;
} else if (match("vulkan1.1")) {
if (env) *env = SPV_ENV_VULKAN_1_1;
return true;
} else if (match("spv1.0")) {
if (env) *env = SPV_ENV_UNIVERSAL_1_0;
return true;
} else if (match("spv1.1")) {
if (env) *env = SPV_ENV_UNIVERSAL_1_1;
return true;
} else if (match("spv1.2")) {
if (env) *env = SPV_ENV_UNIVERSAL_1_2;
return true;
} else if (match("spv1.3")) {
if (env) *env = SPV_ENV_UNIVERSAL_1_3;
return true;
} else if (match("spv1.4")) {
if (env) *env = SPV_ENV_UNIVERSAL_1_4;
return true;
} else if (match("opencl1.2embedded")) {
if (env) *env = SPV_ENV_OPENCL_EMBEDDED_1_2;
return true;
} else if (match("opencl1.2")) {
if (env) *env = SPV_ENV_OPENCL_1_2;
return true;
} else if (match("opencl2.0embedded")) {
if (env) *env = SPV_ENV_OPENCL_EMBEDDED_2_0;
return true;
} else if (match("opencl2.0")) {
if (env) *env = SPV_ENV_OPENCL_2_0;
return true;
} else if (match("opencl2.1embedded")) {
if (env) *env = SPV_ENV_OPENCL_EMBEDDED_2_1;
return true;
} else if (match("opencl2.1")) {
if (env) *env = SPV_ENV_OPENCL_2_1;
return true;
} else if (match("opencl2.2embedded")) {
if (env) *env = SPV_ENV_OPENCL_EMBEDDED_2_2;
return true;
} else if (match("opencl2.2")) {
if (env) *env = SPV_ENV_OPENCL_2_2;
return true;
} else if (match("opengl4.0")) {
if (env) *env = SPV_ENV_OPENGL_4_0;
return true;
} else if (match("opengl4.1")) {
if (env) *env = SPV_ENV_OPENGL_4_1;
return true;
} else if (match("opengl4.2")) {
if (env) *env = SPV_ENV_OPENGL_4_2;
return true;
} else if (match("opengl4.3")) {
if (env) *env = SPV_ENV_OPENGL_4_3;
return true;
} else if (match("opengl4.5")) {
if (env) *env = SPV_ENV_OPENGL_4_5;
return true;
} else if (match("webgpu0")) {
if (env) *env = SPV_ENV_WEBGPU_0;
return true;
} else {
if (env) *env = SPV_ENV_UNIVERSAL_1_0;
return false;
for (auto& name_env : spvTargetEnvNameMap) {
if (match(name_env.first)) {
if (env) {
*env = name_env.second;
}
return true;
}
}
if (env) *env = SPV_ENV_UNIVERSAL_1_0;
return false;
}
bool spvIsVulkanEnv(spv_target_env env) {
@@ -310,3 +275,29 @@ std::string spvLogStringForEnv(spv_target_env env) {
}
return "Unknown";
}
std::string spvTargetEnvList(const int pad, const int wrap) {
std::string ret;
size_t max_line_len = wrap - pad; // The first line isn't padded
std::string line;
std::string sep = "";
for (auto& name_env : spvTargetEnvNameMap) {
std::string word = sep + name_env.first;
if (line.length() + word.length() > max_line_len) {
// Adding one word wouldn't fit, commit the line in progress and
// start a new one.
ret += line + "\n";
line.assign(pad, ' ');
// The first line is done. The max length now comprises the
// padding.
max_line_len = wrap;
}
line += word;
sep = "|";
}
ret += line;
return ret;
}

View File

@@ -38,4 +38,12 @@ uint32_t spvVersionForTargetEnv(spv_target_env env);
// environment, i.e. "Vulkan", "WebGPU", "OpenCL", etc.
std::string spvLogStringForEnv(spv_target_env env);
// Returns a formatted list of all SPIR-V target environment names that
// can be parsed by spvParseTargetEnv.
// |pad| is the number of space characters that the begining of each line
// except the first one will be padded with.
// |wrap| is the max length of lines the user desires. Word-wrapping will
// occur to satisfy this limit.
std::string spvTargetEnvList(const int pad, const int wrap);
#endif // SOURCE_SPIRV_TARGET_ENV_H_

View File

@@ -175,13 +175,37 @@ spv_result_t AtomicsPass(ValidationState_t& _, const Instruction* inst) {
return error;
}
if (auto error = ValidateMemorySemantics(_, inst, operand_index++))
const auto equal_semantics_index = operand_index++;
if (auto error = ValidateMemorySemantics(_, inst, equal_semantics_index))
return error;
if (opcode == SpvOpAtomicCompareExchange ||
opcode == SpvOpAtomicCompareExchangeWeak) {
if (auto error = ValidateMemorySemantics(_, inst, operand_index++))
const auto unequal_semantics_index = operand_index++;
if (auto error =
ValidateMemorySemantics(_, inst, unequal_semantics_index))
return error;
// Volatile bits must match for equal and unequal semantics. Previous
// checks guarantee they are 32-bit constants, but we need to recheck
// whether they are evaluatable constants.
bool is_int32 = false;
bool is_equal_const = false;
bool is_unequal_const = false;
uint32_t equal_value = 0;
uint32_t unequal_value = 0;
std::tie(is_int32, is_equal_const, equal_value) = _.EvalInt32IfConst(
inst->GetOperandAs<uint32_t>(equal_semantics_index));
std::tie(is_int32, is_unequal_const, unequal_value) =
_.EvalInt32IfConst(
inst->GetOperandAs<uint32_t>(unequal_semantics_index));
if (is_equal_const && is_unequal_const &&
((equal_value & SpvMemorySemanticsVolatileMask) ^
(unequal_value & SpvMemorySemanticsVolatileMask))) {
return _.diag(SPV_ERROR_INVALID_ID, inst)
<< "Volatile mask setting must match for Equal and Unequal "
"memory semantics";
}
}
if (opcode == SpvOpAtomicStore) {

View File

@@ -1393,6 +1393,81 @@ spv_result_t CheckIntegerWrapDecoration(ValidationState_t& vstate,
<< spvOpcodeString(inst.opcode());
}
// Returns SPV_SUCCESS if validation rules are satisfied for the Component
// decoration. Otherwise emits a diagnostic and returns something other than
// SPV_SUCCESS.
spv_result_t CheckComponentDecoration(ValidationState_t& vstate,
const Instruction& inst,
const Decoration& decoration) {
assert(inst.id() && "Parser ensures the target of the decoration has an ID");
uint32_t type_id;
if (decoration.struct_member_index() == Decoration::kInvalidMember) {
// The target must be a memory object declaration.
const auto opcode = inst.opcode();
if (opcode != SpvOpVariable && opcode != SpvOpFunctionParameter) {
return vstate.diag(SPV_ERROR_INVALID_ID, &inst)
<< "Target of Component decoration must be a memory object "
"declaration (a variable or a function parameter)";
}
// Only valid for the Input and Output Storage Classes.
const auto storage_class = opcode == SpvOpVariable
? inst.GetOperandAs<SpvStorageClass>(2)
: SpvStorageClassMax;
if (storage_class != SpvStorageClassInput &&
storage_class != SpvStorageClassOutput &&
storage_class != SpvStorageClassMax) {
return vstate.diag(SPV_ERROR_INVALID_ID, &inst)
<< "Target of Component decoration is invalid: must point to a "
"Storage Class of Input(1) or Output(3). Found Storage "
"Class "
<< storage_class;
}
type_id = inst.type_id();
if (vstate.IsPointerType(type_id)) {
const auto pointer = vstate.FindDef(type_id);
type_id = pointer->GetOperandAs<uint32_t>(2);
}
} else {
if (inst.opcode() != SpvOpTypeStruct) {
return vstate.diag(SPV_ERROR_INVALID_DATA, &inst)
<< "Attempted to get underlying data type via member index for "
"non-struct type.";
}
type_id = inst.word(decoration.struct_member_index() + 2);
}
if (spvIsVulkanEnv(vstate.context()->target_env)) {
if (!vstate.IsIntScalarOrVectorType(type_id) &&
!vstate.IsFloatScalarOrVectorType(type_id)) {
return vstate.diag(SPV_ERROR_INVALID_ID, &inst)
<< "Component decoration specified for type "
<< vstate.getIdName(type_id) << " that is not a scalar or vector";
}
// For 16-, and 32-bit types, it is invalid if this sequence of components
// gets larger than 3.
const auto bit_width = vstate.GetBitWidth(type_id);
if (bit_width == 16 || bit_width == 32) {
assert(decoration.params().size() == 1 &&
"Grammar ensures Component has one parameter");
const auto component = decoration.params()[0];
const auto last_component = component + vstate.GetDimension(type_id) - 1;
if (last_component > 3) {
return vstate.diag(SPV_ERROR_INVALID_ID, &inst)
<< "Sequence of components starting with " << component
<< " and ending with " << last_component
<< " gets larger than 3";
}
}
}
return SPV_SUCCESS;
}
#define PASS_OR_BAIL_AT_LINE(X, LINE) \
{ \
spv_result_t e##LINE = (X); \
@@ -1421,6 +1496,9 @@ spv_result_t CheckDecorationsFromDecoration(ValidationState_t& vstate) {
for (const auto& decoration : decorations) {
switch (decoration.dec_type()) {
case SpvDecorationComponent:
PASS_OR_BAIL(CheckComponentDecoration(vstate, *inst, decoration));
break;
case SpvDecorationFPRoundingMode:
if (is_shader)
PASS_OR_BAIL(CheckFPRoundingModeForShaders(vstate, *inst));

View File

@@ -12,8 +12,6 @@
// See the License for the specific language governing permissions and
// limitations under the License.
#include "source/val/validate.h"
#include <algorithm>
#include <string>
#include <vector>
@@ -21,6 +19,7 @@
#include "source/opcode.h"
#include "source/spirv_target_env.h"
#include "source/val/instruction.h"
#include "source/val/validate.h"
#include "source/val/validate_scopes.h"
#include "source/val/validation_state.h"
@@ -809,6 +808,25 @@ spv_result_t ValidateStore(ValidationState_t& _, const Instruction* inst) {
<< "OpStore Pointer <id> '" << _.getIdName(pointer_id)
<< "' storage class is read-only";
}
if (spvIsVulkanEnv(_.context()->target_env) &&
storage_class == SpvStorageClassUniform) {
auto base_ptr = _.TracePointer(pointer);
if (base_ptr->opcode() == SpvOpVariable) {
// If it's not a variable a different check should catch the problem.
auto base_type = _.FindDef(base_ptr->GetOperandAs<uint32_t>(0));
// Get the pointed-to type.
base_type = _.FindDef(base_type->GetOperandAs<uint32_t>(2u));
if (base_type->opcode() == SpvOpTypeArray ||
base_type->opcode() == SpvOpTypeRuntimeArray) {
base_type = _.FindDef(base_type->GetOperandAs<uint32_t>(1u));
}
if (_.HasDecoration(base_type->id(), SpvDecorationBlock)) {
return _.diag(SPV_ERROR_INVALID_ID, inst)
<< "In the Vulkan environment, cannot store to Uniform Blocks";
}
}
}
}
const auto object_index = 1;

View File

@@ -39,11 +39,20 @@ spv_result_t ValidateMemorySemantics(ValidationState_t& _,
}
if (!is_const_int32) {
if (_.HasCapability(SpvCapabilityShader)) {
if (_.HasCapability(SpvCapabilityShader) &&
!_.HasCapability(SpvCapabilityCooperativeMatrixNV)) {
return _.diag(SPV_ERROR_INVALID_DATA, inst)
<< "Memory Semantics ids must be OpConstant when Shader "
"capability is present";
}
if (_.HasCapability(SpvCapabilityShader) &&
_.HasCapability(SpvCapabilityCooperativeMatrixNV) &&
!spvOpcodeIsConstant(_.GetIdOpcode(id))) {
return _.diag(SPV_ERROR_INVALID_DATA, inst)
<< "Memory Semantics must be a constant instruction when "
"CooperativeMatrixNV capability is present";
}
return SPV_SUCCESS;
}
@@ -127,6 +136,21 @@ spv_result_t ValidateMemorySemantics(ValidationState_t& _,
<< "VulkanMemoryModelKHR";
}
if (value & SpvMemorySemanticsVolatileMask) {
if (!_.HasCapability(SpvCapabilityVulkanMemoryModelKHR)) {
return _.diag(SPV_ERROR_INVALID_DATA, inst)
<< spvOpcodeString(opcode)
<< ": Memory Semantics Volatile requires capability "
"VulkanMemoryModelKHR";
}
if (!spvOpcodeIsAtomicOp(inst->opcode())) {
return _.diag(SPV_ERROR_INVALID_DATA, inst)
<< "Memory Semantics Volatile can only be used with atomic "
"instructions";
}
}
if (value & SpvMemorySemanticsUniformMemoryMask &&
!_.HasCapability(SpvCapabilityShader)) {
return _.diag(SPV_ERROR_INVALID_DATA, inst)

View File

@@ -1213,5 +1213,18 @@ bool ValidationState_t::LogicallyMatch(const Instruction* lhs,
return false;
}
const Instruction* ValidationState_t::TracePointer(
const Instruction* inst) const {
auto base_ptr = inst;
while (base_ptr->opcode() == SpvOpAccessChain ||
base_ptr->opcode() == SpvOpInBoundsAccessChain ||
base_ptr->opcode() == SpvOpPtrAccessChain ||
base_ptr->opcode() == SpvOpInBoundsPtrAccessChain ||
base_ptr->opcode() == SpvOpCopyObject) {
base_ptr = FindDef(base_ptr->GetOperandAs<uint32_t>(2u));
}
return base_ptr;
}
} // namespace val
} // namespace spvtools

View File

@@ -680,6 +680,15 @@ class ValidationState_t {
bool LogicallyMatch(const Instruction* lhs, const Instruction* rhs,
bool check_decorations);
// Traces |inst| to find a single base pointer. Returns the base pointer.
// Will trace through the following instructions:
// * OpAccessChain
// * OpInBoundsAccessChain
// * OpPtrAccessChain
// * OpInBoundsPtrAccessChain
// * OpCopyObject
const Instruction* TracePointer(const Instruction* inst) const;
private:
ValidationState_t(const ValidationState_t&);

View File

@@ -20,15 +20,19 @@ if (${SPIRV_BUILD_FUZZER})
fuzzer_replayer_test.cpp
fact_manager_test.cpp
fuzz_test_util.cpp
fuzzer_pass_add_useful_constructs_test.cpp
transformation_add_constant_boolean_test.cpp
transformation_add_constant_scalar_test.cpp
transformation_add_dead_break_test.cpp
transformation_add_type_boolean_test.cpp
transformation_add_type_float_test.cpp
transformation_add_type_int_test.cpp
transformation_add_type_pointer_test.cpp
transformation_move_block_down_test.cpp
transformation_replace_boolean_constant_with_constant_binary_test.cpp
transformation_split_block_test.cpp)
transformation_replace_constant_with_uniform_test.cpp
transformation_split_block_test.cpp
uniform_buffer_element_descriptor_test.cpp)
add_spvtools_unittest(TARGET fuzz
SRCS ${SOURCES}

View File

@@ -12,6 +12,8 @@
// See the License for the specific language governing permissions and
// limitations under the License.
#include <limits>
#include "source/fuzz/fact_manager.h"
#include "source/fuzz/uniform_buffer_element_descriptor.h"
#include "test/fuzz/fuzz_test_util.h"
@@ -56,6 +58,44 @@ TEST(FactManagerTest, ConstantsAvailableViaUniforms) {
OpExecutionMode %4 OriginUpperLeft
OpSource GLSL 450
OpName %4 "main"
OpDecorate %100 DescriptorSet 0
OpDecorate %100 Binding 0
OpDecorate %200 DescriptorSet 0
OpDecorate %200 Binding 1
OpDecorate %300 DescriptorSet 0
OpDecorate %300 Binding 2
OpDecorate %400 DescriptorSet 0
OpDecorate %400 Binding 3
OpDecorate %500 DescriptorSet 0
OpDecorate %500 Binding 4
OpDecorate %600 DescriptorSet 0
OpDecorate %600 Binding 5
OpDecorate %700 DescriptorSet 0
OpDecorate %700 Binding 6
OpDecorate %800 DescriptorSet 1
OpDecorate %800 Binding 0
OpDecorate %900 DescriptorSet 1
OpDecorate %900 Binding 1
OpDecorate %1000 DescriptorSet 1
OpDecorate %1000 Binding 2
OpDecorate %1100 DescriptorSet 1
OpDecorate %1100 Binding 3
OpDecorate %1200 DescriptorSet 1
OpDecorate %1200 Binding 4
OpDecorate %1300 DescriptorSet 1
OpDecorate %1300 Binding 5
OpDecorate %1400 DescriptorSet 1
OpDecorate %1400 Binding 6
OpDecorate %1500 DescriptorSet 2
OpDecorate %1500 Binding 0
OpDecorate %1600 DescriptorSet 2
OpDecorate %1600 Binding 1
OpDecorate %1700 DescriptorSet 2
OpDecorate %1700 Binding 2
OpDecorate %1800 DescriptorSet 2
OpDecorate %1800 Binding 3
OpDecorate %1900 DescriptorSet 2
OpDecorate %1900 Binding 4
%2 = OpTypeVoid
%3 = OpTypeFunction %2
%10 = OpTypeInt 32 0
@@ -229,94 +269,98 @@ TEST(FactManagerTest, ConstantsAvailableViaUniforms) {
type_uint32_id)
.empty());
// In the comments that follow we write v[...][...] to refer to uniform
// variable v indexed with some given indices, when in practice v is
// identified via a (descriptor set, binding) pair.
// 100[2][3] == int(1)
ASSERT_TRUE(AddFactHelper(&fact_manager, context.get(), {1},
MakeUniformBufferElementDescriptor(100, {2, 3})));
MakeUniformBufferElementDescriptor(0, 0, {2, 3})));
// 200[1][2][3] == int(1)
ASSERT_TRUE(
AddFactHelper(&fact_manager, context.get(), {1},
MakeUniformBufferElementDescriptor(200, {1, 2, 3})));
MakeUniformBufferElementDescriptor(0, 1, {1, 2, 3})));
// 300[1][0][2][3] == int(1)
ASSERT_TRUE(
AddFactHelper(&fact_manager, context.get(), {1},
MakeUniformBufferElementDescriptor(300, {1, 0, 2, 3})));
MakeUniformBufferElementDescriptor(0, 2, {1, 0, 2, 3})));
// 400[2][3] = int32_min
ASSERT_TRUE(AddFactHelper(&fact_manager, context.get(), {buffer_int32_min[0]},
MakeUniformBufferElementDescriptor(400, {2, 3})));
MakeUniformBufferElementDescriptor(0, 3, {2, 3})));
// 500[1][2][3] = int32_min
ASSERT_TRUE(
AddFactHelper(&fact_manager, context.get(), {buffer_int32_min[0]},
MakeUniformBufferElementDescriptor(500, {1, 2, 3})));
MakeUniformBufferElementDescriptor(0, 4, {1, 2, 3})));
// 600[1][2][3] = int64_max
ASSERT_TRUE(AddFactHelper(
&fact_manager, context.get(), {buffer_int64_max[0], buffer_int64_max[1]},
MakeUniformBufferElementDescriptor(600, {1, 2, 3})));
MakeUniformBufferElementDescriptor(0, 5, {1, 2, 3})));
// 700[1][1] = int64_max
ASSERT_TRUE(AddFactHelper(&fact_manager, context.get(),
{buffer_int64_max[0], buffer_int64_max[1]},
MakeUniformBufferElementDescriptor(700, {1, 1})));
MakeUniformBufferElementDescriptor(0, 6, {1, 1})));
// 800[2][3] = uint(1)
ASSERT_TRUE(AddFactHelper(&fact_manager, context.get(), {1},
MakeUniformBufferElementDescriptor(800, {2, 3})));
MakeUniformBufferElementDescriptor(1, 0, {2, 3})));
// 900[1][2][3] = uint(1)
ASSERT_TRUE(
AddFactHelper(&fact_manager, context.get(), {1},
MakeUniformBufferElementDescriptor(900, {1, 2, 3})));
MakeUniformBufferElementDescriptor(1, 1, {1, 2, 3})));
// 1000[1][0][2][3] = uint(1)
ASSERT_TRUE(
AddFactHelper(&fact_manager, context.get(), {1},
MakeUniformBufferElementDescriptor(1000, {1, 0, 2, 3})));
MakeUniformBufferElementDescriptor(1, 2, {1, 0, 2, 3})));
// 1100[0] = uint64(1)
ASSERT_TRUE(AddFactHelper(&fact_manager, context.get(),
{buffer_uint64_1[0], buffer_uint64_1[1]},
MakeUniformBufferElementDescriptor(1100, {0})));
MakeUniformBufferElementDescriptor(1, 3, {0})));
// 1200[0][0] = uint64_max
ASSERT_TRUE(AddFactHelper(&fact_manager, context.get(),
{buffer_uint64_max[0], buffer_uint64_max[1]},
MakeUniformBufferElementDescriptor(1200, {0, 0})));
MakeUniformBufferElementDescriptor(1, 4, {0, 0})));
// 1300[1][0] = uint64_max
ASSERT_TRUE(AddFactHelper(&fact_manager, context.get(),
{buffer_uint64_max[0], buffer_uint64_max[1]},
MakeUniformBufferElementDescriptor(1300, {1, 0})));
MakeUniformBufferElementDescriptor(1, 5, {1, 0})));
// 1400[6] = float(10.0)
ASSERT_TRUE(AddFactHelper(&fact_manager, context.get(), {buffer_float_10[0]},
MakeUniformBufferElementDescriptor(1400, {6})));
MakeUniformBufferElementDescriptor(1, 6, {6})));
// 1500[7] = float(10.0)
ASSERT_TRUE(AddFactHelper(&fact_manager, context.get(), {buffer_float_10[0]},
MakeUniformBufferElementDescriptor(1500, {7})));
MakeUniformBufferElementDescriptor(2, 0, {7})));
// 1600[9][9] = float(10.0)
ASSERT_TRUE(AddFactHelper(&fact_manager, context.get(), {buffer_float_10[0]},
MakeUniformBufferElementDescriptor(1600, {9, 9})));
MakeUniformBufferElementDescriptor(2, 1, {9, 9})));
// 1700[9][9][1] = double(10.0)
ASSERT_TRUE(AddFactHelper(
&fact_manager, context.get(), {buffer_double_10[0], buffer_double_10[1]},
MakeUniformBufferElementDescriptor(1700, {9, 9, 1})));
MakeUniformBufferElementDescriptor(2, 2, {9, 9, 1})));
// 1800[9][9][2] = double(10.0)
ASSERT_TRUE(AddFactHelper(
&fact_manager, context.get(), {buffer_double_10[0], buffer_double_10[1]},
MakeUniformBufferElementDescriptor(1800, {9, 9, 2})));
MakeUniformBufferElementDescriptor(2, 3, {9, 9, 2})));
// 1900[0][0][0][0][0] = double(20.0)
ASSERT_TRUE(AddFactHelper(
&fact_manager, context.get(), {buffer_double_20[0], buffer_double_20[1]},
MakeUniformBufferElementDescriptor(1900, {0, 0, 0, 0, 0})));
MakeUniformBufferElementDescriptor(2, 4, {0, 0, 0, 0, 0})));
opt::Instruction::OperandList operands = {
{SPV_OPERAND_TYPE_LITERAL_INTEGER, {1}}};
@@ -427,12 +471,12 @@ TEST(FactManagerTest, ConstantsAvailableViaUniforms) {
context.get(), double_constant_ids[0]);
ASSERT_EQ(2, descriptors_for_double_10.size());
{
auto temp = MakeUniformBufferElementDescriptor(1700, {9, 9, 1});
auto temp = MakeUniformBufferElementDescriptor(2, 2, {9, 9, 1});
ASSERT_TRUE(UniformBufferElementDescriptorEquals()(
&temp, &descriptors_for_double_10[0]));
}
{
auto temp = MakeUniformBufferElementDescriptor(1800, {9, 9, 2});
auto temp = MakeUniformBufferElementDescriptor(2, 3, {9, 9, 2});
ASSERT_TRUE(UniformBufferElementDescriptorEquals()(
&temp, &descriptors_for_double_10[1]));
}
@@ -441,17 +485,17 @@ TEST(FactManagerTest, ConstantsAvailableViaUniforms) {
context.get(), double_constant_ids[1]);
ASSERT_EQ(1, descriptors_for_double_20.size());
{
auto temp = MakeUniformBufferElementDescriptor(1900, {0, 0, 0, 0, 0});
auto temp = MakeUniformBufferElementDescriptor(2, 4, {0, 0, 0, 0, 0});
ASSERT_TRUE(UniformBufferElementDescriptorEquals()(
&temp, &descriptors_for_double_20[0]));
}
auto constant_1_id = fact_manager.GetConstantFromUniformDescriptor(
context.get(), MakeUniformBufferElementDescriptor(1800, {9, 9, 2}));
context.get(), MakeUniformBufferElementDescriptor(2, 3, {9, 9, 2}));
ASSERT_TRUE(constant_1_id);
auto constant_2_id = fact_manager.GetConstantFromUniformDescriptor(
context.get(), MakeUniformBufferElementDescriptor(1900, {0, 0, 0, 0, 0}));
context.get(), MakeUniformBufferElementDescriptor(2, 4, {0, 0, 0, 0, 0}));
ASSERT_TRUE(constant_2_id);
ASSERT_EQ(double_constant_ids[0], constant_1_id);
@@ -503,9 +547,9 @@ TEST(FactManagerTest, TwoConstantsWithSameValue) {
FactManager fact_manager;
auto uniform_buffer_element_descriptor =
MakeUniformBufferElementDescriptor(12, {0});
MakeUniformBufferElementDescriptor(0, 0, {0});
// 12[0] = int(1)
// (0, 0, [0]) = int(1)
ASSERT_TRUE(AddFactHelper(&fact_manager, context.get(), {1},
uniform_buffer_element_descriptor));
auto constants =
@@ -529,6 +573,171 @@ TEST(FactManagerTest, TwoConstantsWithSameValue) {
}
}
TEST(FactManagerTest, NonFiniteFactsAreNotValid) {
std::string shader = R"(
OpCapability Shader
OpCapability Float64
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %4 "main"
OpExecutionMode %4 OriginUpperLeft
OpSource ESSL 310
OpName %4 "main"
OpName %7 "buf"
OpMemberName %7 0 "f"
OpMemberName %7 1 "d"
OpName %9 ""
OpMemberDecorate %7 0 Offset 0
OpMemberDecorate %7 1 Offset 8
OpDecorate %7 Block
OpDecorate %9 DescriptorSet 0
OpDecorate %9 Binding 0
%2 = OpTypeVoid
%3 = OpTypeFunction %2
%6 = OpTypeFloat 32
%10 = OpTypeFloat 64
%7 = OpTypeStruct %6 %10
%8 = OpTypePointer Uniform %7
%9 = OpVariable %8 Uniform
%4 = OpFunction %2 None %3
%5 = OpLabel
OpReturn
OpFunctionEnd
)";
const auto env = SPV_ENV_UNIVERSAL_1_3;
const auto consumer = nullptr;
const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
ASSERT_TRUE(IsValid(env, context.get()));
FactManager fact_manager;
auto uniform_buffer_element_descriptor_f =
MakeUniformBufferElementDescriptor(0, 0, {0});
auto uniform_buffer_element_descriptor_d =
MakeUniformBufferElementDescriptor(0, 0, {1});
if (std::numeric_limits<float>::has_infinity) {
// f == +inf
float positive_infinity_float = std::numeric_limits<float>::infinity();
uint32_t words[1];
memcpy(words, &positive_infinity_float, sizeof(float));
ASSERT_FALSE(AddFactHelper(&fact_manager, context.get(), {words[0]},
uniform_buffer_element_descriptor_f));
// f == -inf
float negative_infinity_float = std::numeric_limits<float>::infinity();
memcpy(words, &negative_infinity_float, sizeof(float));
ASSERT_FALSE(AddFactHelper(&fact_manager, context.get(), {words[0]},
uniform_buffer_element_descriptor_f));
}
if (std::numeric_limits<float>::has_quiet_NaN) {
// f == NaN
float quiet_nan_float = std::numeric_limits<float>::quiet_NaN();
uint32_t words[1];
memcpy(words, &quiet_nan_float, sizeof(float));
ASSERT_FALSE(AddFactHelper(&fact_manager, context.get(), {words[0]},
uniform_buffer_element_descriptor_f));
}
if (std::numeric_limits<double>::has_infinity) {
// d == +inf
double positive_infinity_double = std::numeric_limits<double>::infinity();
uint32_t words[2];
memcpy(words, &positive_infinity_double, sizeof(double));
ASSERT_FALSE(AddFactHelper(&fact_manager, context.get(),
{words[0], words[1]},
uniform_buffer_element_descriptor_d));
// d == -inf
double negative_infinity_double = -std::numeric_limits<double>::infinity();
memcpy(words, &negative_infinity_double, sizeof(double));
ASSERT_FALSE(AddFactHelper(&fact_manager, context.get(),
{words[0], words[1]},
uniform_buffer_element_descriptor_d));
}
if (std::numeric_limits<double>::has_quiet_NaN) {
// d == NaN
double quiet_nan_double = std::numeric_limits<double>::quiet_NaN();
uint32_t words[2];
memcpy(words, &quiet_nan_double, sizeof(double));
ASSERT_FALSE(AddFactHelper(&fact_manager, context.get(),
{words[0], words[1]},
uniform_buffer_element_descriptor_d));
}
}
TEST(FactManagerTest, AmbiguousFact) {
// This test came from the following GLSL:
//
// #version 310 es
//
// precision highp float;
//
// layout(set = 0, binding = 0) uniform buf {
// float f;
// };
//
// layout(set = 0, binding = 0) uniform buf2 {
// float g;
// };
//
// void main() {
//
// }
std::string shader = R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %4 "main"
OpExecutionMode %4 OriginUpperLeft
OpSource ESSL 310
OpName %4 "main"
OpName %7 "buf"
OpMemberName %7 0 "f"
OpName %9 ""
OpName %10 "buf2"
OpMemberName %10 0 "g"
OpName %12 ""
OpMemberDecorate %7 0 Offset 0
OpDecorate %7 Block
OpDecorate %9 DescriptorSet 0
OpDecorate %9 Binding 0
OpMemberDecorate %10 0 Offset 0
OpDecorate %10 Block
OpDecorate %12 DescriptorSet 0
OpDecorate %12 Binding 0
%2 = OpTypeVoid
%3 = OpTypeFunction %2
%6 = OpTypeFloat 32
%7 = OpTypeStruct %6
%8 = OpTypePointer Uniform %7
%9 = OpVariable %8 Uniform
%10 = OpTypeStruct %6
%11 = OpTypePointer Uniform %10
%12 = OpVariable %11 Uniform
%4 = OpFunction %2 None %3
%5 = OpLabel
OpReturn
OpFunctionEnd
)";
const auto env = SPV_ENV_UNIVERSAL_1_3;
const auto consumer = nullptr;
const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
ASSERT_TRUE(IsValid(env, context.get()));
FactManager fact_manager;
auto uniform_buffer_element_descriptor =
MakeUniformBufferElementDescriptor(0, 0, {0});
// The fact cannot be added because it is ambiguous: there are two uniforms
// with descriptor set 0 and binding 0.
ASSERT_FALSE(AddFactHelper(&fact_manager, context.get(), {1},
uniform_buffer_element_descriptor));
}
} // namespace
} // namespace fuzz
} // namespace spvtools

View File

@@ -0,0 +1,393 @@
// Copyright (c) 2019 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "source/fuzz/fuzzer_pass_add_useful_constructs.h"
#include "source/fuzz/pseudo_random_generator.h"
#include "source/fuzz/uniform_buffer_element_descriptor.h"
#include "test/fuzz/fuzz_test_util.h"
namespace spvtools {
namespace fuzz {
namespace {
bool AddFactHelper(
FactManager* fact_manager, opt::IRContext* context, uint32_t word,
const protobufs::UniformBufferElementDescriptor& descriptor) {
protobufs::FactConstantUniform constant_uniform_fact;
constant_uniform_fact.add_constant_word(word);
*constant_uniform_fact.mutable_uniform_buffer_element_descriptor() =
descriptor;
protobufs::Fact fact;
*fact.mutable_constant_uniform_fact() = constant_uniform_fact;
return fact_manager->AddFact(fact, context);
}
TEST(FuzzerPassAddUsefulConstructsTest, CheckBasicStuffIsAdded) {
// The SPIR-V came from the following empty GLSL shader:
//
// #version 450
//
// void main()
// {
// }
std::string shader = R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %4 "main"
OpExecutionMode %4 OriginUpperLeft
OpSource GLSL 450
OpName %4 "main"
%2 = OpTypeVoid
%3 = OpTypeFunction %2
%4 = OpFunction %2 None %3
%5 = OpLabel
OpReturn
OpFunctionEnd
)";
const auto env = SPV_ENV_UNIVERSAL_1_3;
const auto consumer = nullptr;
const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
ASSERT_TRUE(IsValid(env, context.get()));
FactManager fact_manager;
FuzzerContext fuzzer_context(MakeUnique<PseudoRandomGenerator>(0).get(), 100);
protobufs::TransformationSequence transformation_sequence;
FuzzerPassAddUsefulConstructs pass(context.get(), &fact_manager,
&fuzzer_context, &transformation_sequence);
pass.Apply();
ASSERT_TRUE(IsValid(env, context.get()));
std::string after = R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %4 "main"
OpExecutionMode %4 OriginUpperLeft
OpSource GLSL 450
OpName %4 "main"
%2 = OpTypeVoid
%3 = OpTypeFunction %2
%100 = OpTypeBool
%101 = OpTypeInt 32 1
%102 = OpTypeInt 32 0
%103 = OpTypeFloat 32
%104 = OpConstantTrue %100
%105 = OpConstantFalse %100
%106 = OpConstant %101 0
%107 = OpConstant %101 1
%108 = OpConstant %102 0
%109 = OpConstant %102 1
%110 = OpConstant %103 0
%111 = OpConstant %103 1
%4 = OpFunction %2 None %3
%5 = OpLabel
OpReturn
OpFunctionEnd
)";
ASSERT_TRUE(IsEqual(env, after, context.get()));
}
TEST(FuzzerPassAddUsefulConstructsTest,
CheckTypesIndicesAndConstantsAddedForUniformFacts) {
// The SPIR-V came from the following GLSL shader:
//
// #version 450
//
// struct S {
// int x;
// float y;
// int z;
// int w;
// };
//
// uniform buf {
// S s;
// uint w[10];
// };
//
// void main() {
// }
std::string shader = R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %4 "main"
OpExecutionMode %4 OriginUpperLeft
OpSource GLSL 450
OpName %4 "main"
OpName %8 "S"
OpMemberName %8 0 "x"
OpMemberName %8 1 "y"
OpMemberName %8 2 "z"
OpMemberName %8 3 "w"
OpName %12 "buf"
OpMemberName %12 0 "s"
OpMemberName %12 1 "w"
OpName %14 ""
OpMemberDecorate %8 0 Offset 0
OpMemberDecorate %8 1 Offset 4
OpMemberDecorate %8 2 Offset 8
OpMemberDecorate %8 3 Offset 12
OpDecorate %11 ArrayStride 16
OpMemberDecorate %12 0 Offset 0
OpMemberDecorate %12 1 Offset 16
OpDecorate %12 Block
OpDecorate %14 DescriptorSet 0
OpDecorate %14 Binding 0
%2 = OpTypeVoid
%3 = OpTypeFunction %2
%6 = OpTypeInt 32 1
%7 = OpTypeFloat 32
%8 = OpTypeStruct %6 %7 %6 %6
%9 = OpTypeInt 32 0
%10 = OpConstant %9 10
%11 = OpTypeArray %9 %10
%12 = OpTypeStruct %8 %11
%13 = OpTypePointer Uniform %12
%14 = OpVariable %13 Uniform
%4 = OpFunction %2 None %3
%5 = OpLabel
OpReturn
OpFunctionEnd
)";
const auto env = SPV_ENV_UNIVERSAL_1_3;
const auto consumer = nullptr;
const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
ASSERT_TRUE(IsValid(env, context.get()));
FactManager fact_manager;
FuzzerContext fuzzer_context(MakeUnique<PseudoRandomGenerator>(0).get(), 100);
protobufs::TransformationSequence transformation_sequence;
// Add some uniform facts.
// buf.s.x == 200
ASSERT_TRUE(AddFactHelper(&fact_manager, context.get(), 200,
MakeUniformBufferElementDescriptor(0, 0, {0, 0})));
// buf.s.y == 0.5
const float float_value = 0.5;
uint32_t float_value_as_uint;
memcpy(&float_value_as_uint, &float_value, sizeof(float_value));
ASSERT_TRUE(AddFactHelper(&fact_manager, context.get(), float_value_as_uint,
MakeUniformBufferElementDescriptor(0, 0, {0, 1})));
// buf.s.z == 300
ASSERT_TRUE(AddFactHelper(&fact_manager, context.get(), 300,
MakeUniformBufferElementDescriptor(0, 0, {0, 2})));
// buf.s.w == 400
ASSERT_TRUE(AddFactHelper(&fact_manager, context.get(), 400,
MakeUniformBufferElementDescriptor(0, 0, {0, 3})));
// buf.w[6] = 22
ASSERT_TRUE(AddFactHelper(&fact_manager, context.get(), 22,
MakeUniformBufferElementDescriptor(0, 0, {1, 6})));
// buf.w[8] = 23
ASSERT_TRUE(AddFactHelper(&fact_manager, context.get(), 23,
MakeUniformBufferElementDescriptor(0, 0, {1, 8})));
// Assert some things about the module that are not true prior to adding the
// pass
{
// No uniform int pointer
opt::analysis::Integer temp_type_signed_int(32, true);
opt::analysis::Integer* registered_type_signed_int =
context->get_type_mgr()
->GetRegisteredType(&temp_type_signed_int)
->AsInteger();
opt::analysis::Pointer type_pointer_uniform_signed_int(
registered_type_signed_int, SpvStorageClassUniform);
ASSERT_EQ(0,
context->get_type_mgr()->GetId(&type_pointer_uniform_signed_int));
// No uniform uint pointer
opt::analysis::Integer temp_type_unsigned_int(32, false);
opt::analysis::Integer* registered_type_unsigned_int =
context->get_type_mgr()
->GetRegisteredType(&temp_type_unsigned_int)
->AsInteger();
opt::analysis::Pointer type_pointer_uniform_unsigned_int(
registered_type_unsigned_int, SpvStorageClassUniform);
ASSERT_EQ(
0, context->get_type_mgr()->GetId(&type_pointer_uniform_unsigned_int));
// No uniform float pointer
opt::analysis::Float temp_type_float(32);
opt::analysis::Float* registered_type_float =
context->get_type_mgr()->GetRegisteredType(&temp_type_float)->AsFloat();
opt::analysis::Pointer type_pointer_uniform_float(registered_type_float,
SpvStorageClassUniform);
ASSERT_EQ(0, context->get_type_mgr()->GetId(&type_pointer_uniform_float));
// No int constants 200, 300 nor 400
opt::analysis::IntConstant int_constant_200(registered_type_signed_int,
{200});
opt::analysis::IntConstant int_constant_300(registered_type_signed_int,
{300});
opt::analysis::IntConstant int_constant_400(registered_type_signed_int,
{400});
ASSERT_EQ(nullptr,
context->get_constant_mgr()->FindConstant(&int_constant_200));
ASSERT_EQ(nullptr,
context->get_constant_mgr()->FindConstant(&int_constant_300));
ASSERT_EQ(nullptr,
context->get_constant_mgr()->FindConstant(&int_constant_400));
// No float constant 0.5
opt::analysis::FloatConstant float_constant_zero_point_five(
registered_type_float, {float_value_as_uint});
ASSERT_EQ(nullptr, context->get_constant_mgr()->FindConstant(
&float_constant_zero_point_five));
// No uint constant 22
opt::analysis::IntConstant uint_constant_22(registered_type_unsigned_int,
{22});
ASSERT_EQ(nullptr,
context->get_constant_mgr()->FindConstant(&uint_constant_22));
// No uint constant 23
opt::analysis::IntConstant uint_constant_23(registered_type_unsigned_int,
{23});
ASSERT_EQ(nullptr,
context->get_constant_mgr()->FindConstant(&uint_constant_23));
// No int constants 0, 1, 2, 3, 6, 8
opt::analysis::IntConstant int_constant_0(registered_type_signed_int, {0});
opt::analysis::IntConstant int_constant_1(registered_type_signed_int, {1});
opt::analysis::IntConstant int_constant_2(registered_type_signed_int, {2});
opt::analysis::IntConstant int_constant_3(registered_type_signed_int, {3});
opt::analysis::IntConstant int_constant_6(registered_type_signed_int, {6});
opt::analysis::IntConstant int_constant_8(registered_type_signed_int, {8});
ASSERT_EQ(nullptr,
context->get_constant_mgr()->FindConstant(&int_constant_0));
ASSERT_EQ(nullptr,
context->get_constant_mgr()->FindConstant(&int_constant_1));
ASSERT_EQ(nullptr,
context->get_constant_mgr()->FindConstant(&int_constant_2));
ASSERT_EQ(nullptr,
context->get_constant_mgr()->FindConstant(&int_constant_3));
ASSERT_EQ(nullptr,
context->get_constant_mgr()->FindConstant(&int_constant_6));
ASSERT_EQ(nullptr,
context->get_constant_mgr()->FindConstant(&int_constant_8));
}
FuzzerPassAddUsefulConstructs pass(context.get(), &fact_manager,
&fuzzer_context, &transformation_sequence);
pass.Apply();
ASSERT_TRUE(IsValid(env, context.get()));
// Now assert some things about the module that should be true following the
// pass.
// We reconstruct all necessary types and constants to guard against the type
// and constant managers for the module having been invalidated.
{
// Uniform int pointer now present
opt::analysis::Integer temp_type_signed_int(32, true);
opt::analysis::Integer* registered_type_signed_int =
context->get_type_mgr()
->GetRegisteredType(&temp_type_signed_int)
->AsInteger();
opt::analysis::Pointer type_pointer_uniform_signed_int(
registered_type_signed_int, SpvStorageClassUniform);
ASSERT_NE(0,
context->get_type_mgr()->GetId(&type_pointer_uniform_signed_int));
// Uniform uint pointer now present
opt::analysis::Integer temp_type_unsigned_int(32, false);
opt::analysis::Integer* registered_type_unsigned_int =
context->get_type_mgr()
->GetRegisteredType(&temp_type_unsigned_int)
->AsInteger();
opt::analysis::Pointer type_pointer_uniform_unsigned_int(
registered_type_unsigned_int, SpvStorageClassUniform);
ASSERT_NE(
0, context->get_type_mgr()->GetId(&type_pointer_uniform_unsigned_int));
// Uniform float pointer now present
opt::analysis::Float temp_type_float(32);
opt::analysis::Float* registered_type_float =
context->get_type_mgr()->GetRegisteredType(&temp_type_float)->AsFloat();
opt::analysis::Pointer type_pointer_uniform_float(registered_type_float,
SpvStorageClassUniform);
ASSERT_NE(0, context->get_type_mgr()->GetId(&type_pointer_uniform_float));
// int constants 200, 300, 400 now present
opt::analysis::IntConstant int_constant_200(registered_type_signed_int,
{200});
opt::analysis::IntConstant int_constant_300(registered_type_signed_int,
{300});
opt::analysis::IntConstant int_constant_400(registered_type_signed_int,
{400});
ASSERT_NE(nullptr,
context->get_constant_mgr()->FindConstant(&int_constant_200));
ASSERT_NE(nullptr,
context->get_constant_mgr()->FindConstant(&int_constant_300));
ASSERT_NE(nullptr,
context->get_constant_mgr()->FindConstant(&int_constant_400));
// float constant 0.5 now present
opt::analysis::FloatConstant float_constant_zero_point_five(
registered_type_float, {float_value_as_uint});
ASSERT_NE(nullptr, context->get_constant_mgr()->FindConstant(
&float_constant_zero_point_five));
// uint constant 22 now present
opt::analysis::IntConstant uint_constant_22(registered_type_unsigned_int,
{22});
ASSERT_NE(nullptr,
context->get_constant_mgr()->FindConstant(&uint_constant_22));
// uint constant 23 now present
opt::analysis::IntConstant uint_constant_23(registered_type_unsigned_int,
{23});
ASSERT_NE(nullptr,
context->get_constant_mgr()->FindConstant(&uint_constant_23));
// int constants 0, 1, 2, 3, 6, 8 now present
opt::analysis::IntConstant int_constant_0(registered_type_signed_int, {0});
opt::analysis::IntConstant int_constant_1(registered_type_signed_int, {1});
opt::analysis::IntConstant int_constant_2(registered_type_signed_int, {2});
opt::analysis::IntConstant int_constant_3(registered_type_signed_int, {3});
opt::analysis::IntConstant int_constant_6(registered_type_signed_int, {6});
opt::analysis::IntConstant int_constant_8(registered_type_signed_int, {8});
ASSERT_NE(nullptr,
context->get_constant_mgr()->FindConstant(&int_constant_0));
ASSERT_NE(nullptr,
context->get_constant_mgr()->FindConstant(&int_constant_1));
ASSERT_NE(nullptr,
context->get_constant_mgr()->FindConstant(&int_constant_2));
ASSERT_NE(nullptr,
context->get_constant_mgr()->FindConstant(&int_constant_3));
ASSERT_NE(nullptr,
context->get_constant_mgr()->FindConstant(&int_constant_6));
ASSERT_NE(nullptr,
context->get_constant_mgr()->FindConstant(&int_constant_8));
}
}
} // namespace
} // namespace fuzz
} // namespace spvtools

View File

@@ -14,6 +14,7 @@
#include "source/fuzz/fuzzer.h"
#include "source/fuzz/replayer.h"
#include "source/fuzz/uniform_buffer_element_descriptor.h"
#include "test/fuzz/fuzz_test_util.h"
namespace spvtools {
@@ -25,8 +26,9 @@ namespace {
// the binary produced after each fuzzer run is valid, and that replaying
// the transformations that were applied during fuzzing leads to an
// identical binary.
void RunFuzzerAndReplayer(const std::string& shader, uint32_t initial_seed,
uint32_t num_runs) {
void RunFuzzerAndReplayer(const std::string& shader,
const protobufs::FactSequence& initial_facts,
uint32_t initial_seed, uint32_t num_runs) {
const auto env = SPV_ENV_UNIVERSAL_1_3;
std::vector<uint32_t> binary_in;
@@ -35,23 +37,27 @@ void RunFuzzerAndReplayer(const std::string& shader, uint32_t initial_seed,
ASSERT_TRUE(t.Validate(binary_in));
for (uint32_t seed = initial_seed; seed < initial_seed + num_runs; seed++) {
protobufs::FactSequence initial_facts;
std::vector<uint32_t> fuzzer_binary_out;
protobufs::TransformationSequence fuzzer_transformation_sequence_out;
spvtools::FuzzerOptions fuzzer_options;
spvFuzzerOptionsSetRandomSeed(fuzzer_options, seed);
Fuzzer fuzzer(env);
fuzzer.Run(binary_in, initial_facts, &fuzzer_binary_out,
&fuzzer_transformation_sequence_out, fuzzer_options);
auto fuzzer_result_status =
fuzzer.Run(binary_in, initial_facts, &fuzzer_binary_out,
&fuzzer_transformation_sequence_out, fuzzer_options);
ASSERT_EQ(Fuzzer::FuzzerResultStatus::kComplete, fuzzer_result_status);
ASSERT_TRUE(t.Validate(fuzzer_binary_out));
std::vector<uint32_t> replayer_binary_out;
protobufs::TransformationSequence replayer_transformation_sequence_out;
Replayer replayer(env);
replayer.Run(binary_in, initial_facts, fuzzer_transformation_sequence_out,
&replayer_binary_out, &replayer_transformation_sequence_out);
auto replayer_result_status = replayer.Run(
binary_in, initial_facts, fuzzer_transformation_sequence_out,
&replayer_binary_out, &replayer_transformation_sequence_out);
ASSERT_EQ(Replayer::ReplayerResultStatus::kComplete,
replayer_result_status);
// After replaying the transformations applied by the fuzzer, exactly those
// transformations should have been applied, and the binary resulting from
@@ -233,7 +239,7 @@ TEST(FuzzerReplayerTest, Miscellaneous1) {
// Do 10 fuzzer runs, starting from an initial seed of 0 (seed value chosen
// arbitrarily).
RunFuzzerAndReplayer(shader, 0, 10);
RunFuzzerAndReplayer(shader, protobufs::FactSequence(), 0, 10);
}
TEST(FuzzerReplayerTest, Miscellaneous2) {
@@ -478,7 +484,492 @@ TEST(FuzzerReplayerTest, Miscellaneous2) {
// Do 10 fuzzer runs, starting from an initial seed of 10 (seed value chosen
// arbitrarily).
RunFuzzerAndReplayer(shader, 10, 10);
RunFuzzerAndReplayer(shader, protobufs::FactSequence(), 10, 10);
}
TEST(FuzzerReplayerTest, Miscellaneous3) {
// The SPIR-V came from this GLSL, which was then optimized using spirv-opt
// with the -O argument:
//
// #version 310 es
//
// precision highp float;
//
// layout(location = 0) out vec4 _GLF_color;
//
// layout(set = 0, binding = 0) uniform buf0 {
// vec2 resolution;
// };
// void main(void)
// {
// float A[50];
// for(
// int i = 0;
// i < 200;
// i ++
// )
// {
// if(i >= int(resolution.x))
// {
// break;
// }
// if((4 * (i / 4)) == i)
// {
// A[i / 4] = float(i);
// }
// }
// for(
// int i = 0;
// i < 50;
// i ++
// )
// {
// if(i < int(gl_FragCoord.x))
// {
// break;
// }
// if(i > 0)
// {
// A[i] += A[i - 1];
// }
// }
// if(int(gl_FragCoord.x) < 20)
// {
// _GLF_color = vec4(A[0] / resolution.x, A[4] / resolution.y, 1.0, 1.0);
// }
// else
// if(int(gl_FragCoord.x) < 40)
// {
// _GLF_color = vec4(A[5] / resolution.x, A[9] / resolution.y, 1.0, 1.0);
// }
// else
// if(int(gl_FragCoord.x) < 60)
// {
// _GLF_color = vec4(A[10] / resolution.x, A[14] / resolution.y,
// 1.0, 1.0);
// }
// else
// if(int(gl_FragCoord.x) < 80)
// {
// _GLF_color = vec4(A[15] / resolution.x, A[19] / resolution.y,
// 1.0, 1.0);
// }
// else
// if(int(gl_FragCoord.x) < 100)
// {
// _GLF_color = vec4(A[20] / resolution.x, A[24] / resolution.y,
// 1.0, 1.0);
// }
// else
// if(int(gl_FragCoord.x) < 120)
// {
// _GLF_color = vec4(A[25] / resolution.x, A[29] / resolution.y,
// 1.0, 1.0);
// }
// else
// if(int(gl_FragCoord.x) < 140)
// {
// _GLF_color = vec4(A[30] / resolution.x, A[34] / resolution.y,
// 1.0, 1.0);
// }
// else
// if(int(gl_FragCoord.x) < 160)
// {
// _GLF_color = vec4(A[35] / resolution.x, A[39] /
// resolution.y, 1.0, 1.0);
// }
// else
// if(int(gl_FragCoord.x) < 180)
// {
// _GLF_color = vec4(A[40] / resolution.x, A[44] /
// resolution.y, 1.0, 1.0);
// }
// else
// if(int(gl_FragCoord.x) < 180)
// {
// _GLF_color = vec4(A[45] / resolution.x, A[49] /
// resolution.y, 1.0, 1.0);
// }
// else
// {
// discard;
// }
// }
std::string shader = R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %4 "main" %68 %100
OpExecutionMode %4 OriginUpperLeft
OpSource ESSL 310
OpName %4 "main"
OpName %22 "buf0"
OpMemberName %22 0 "resolution"
OpName %24 ""
OpName %46 "A"
OpName %68 "gl_FragCoord"
OpName %100 "_GLF_color"
OpMemberDecorate %22 0 Offset 0
OpDecorate %22 Block
OpDecorate %24 DescriptorSet 0
OpDecorate %24 Binding 0
OpDecorate %37 RelaxedPrecision
OpDecorate %38 RelaxedPrecision
OpDecorate %55 RelaxedPrecision
OpDecorate %68 BuiltIn FragCoord
OpDecorate %83 RelaxedPrecision
OpDecorate %91 RelaxedPrecision
OpDecorate %100 Location 0
OpDecorate %302 RelaxedPrecision
OpDecorate %304 RelaxedPrecision
%2 = OpTypeVoid
%3 = OpTypeFunction %2
%6 = OpTypeInt 32 1
%9 = OpConstant %6 0
%16 = OpConstant %6 200
%17 = OpTypeBool
%20 = OpTypeFloat 32
%21 = OpTypeVector %20 2
%22 = OpTypeStruct %21
%23 = OpTypePointer Uniform %22
%24 = OpVariable %23 Uniform
%25 = OpTypeInt 32 0
%26 = OpConstant %25 0
%27 = OpTypePointer Uniform %20
%35 = OpConstant %6 4
%43 = OpConstant %25 50
%44 = OpTypeArray %20 %43
%45 = OpTypePointer Function %44
%51 = OpTypePointer Function %20
%54 = OpConstant %6 1
%63 = OpConstant %6 50
%66 = OpTypeVector %20 4
%67 = OpTypePointer Input %66
%68 = OpVariable %67 Input
%69 = OpTypePointer Input %20
%95 = OpConstant %6 20
%99 = OpTypePointer Output %66
%100 = OpVariable %99 Output
%108 = OpConstant %25 1
%112 = OpConstant %20 1
%118 = OpConstant %6 40
%122 = OpConstant %6 5
%128 = OpConstant %6 9
%139 = OpConstant %6 60
%143 = OpConstant %6 10
%149 = OpConstant %6 14
%160 = OpConstant %6 80
%164 = OpConstant %6 15
%170 = OpConstant %6 19
%181 = OpConstant %6 100
%190 = OpConstant %6 24
%201 = OpConstant %6 120
%205 = OpConstant %6 25
%211 = OpConstant %6 29
%222 = OpConstant %6 140
%226 = OpConstant %6 30
%232 = OpConstant %6 34
%243 = OpConstant %6 160
%247 = OpConstant %6 35
%253 = OpConstant %6 39
%264 = OpConstant %6 180
%273 = OpConstant %6 44
%287 = OpConstant %6 45
%293 = OpConstant %6 49
%4 = OpFunction %2 None %3
%5 = OpLabel
%46 = OpVariable %45 Function
OpBranch %10
%10 = OpLabel
%302 = OpPhi %6 %9 %5 %55 %42
%18 = OpSLessThan %17 %302 %16
OpLoopMerge %12 %42 None
OpBranchConditional %18 %11 %12
%11 = OpLabel
%28 = OpAccessChain %27 %24 %9 %26
%29 = OpLoad %20 %28
%30 = OpConvertFToS %6 %29
%31 = OpSGreaterThanEqual %17 %302 %30
OpSelectionMerge %33 None
OpBranchConditional %31 %32 %33
%32 = OpLabel
OpBranch %12
%33 = OpLabel
%37 = OpSDiv %6 %302 %35
%38 = OpIMul %6 %35 %37
%40 = OpIEqual %17 %38 %302
OpSelectionMerge %42 None
OpBranchConditional %40 %41 %42
%41 = OpLabel
%50 = OpConvertSToF %20 %302
%52 = OpAccessChain %51 %46 %37
OpStore %52 %50
OpBranch %42
%42 = OpLabel
%55 = OpIAdd %6 %302 %54
OpBranch %10
%12 = OpLabel
OpBranch %57
%57 = OpLabel
%304 = OpPhi %6 %9 %12 %91 %80
%64 = OpSLessThan %17 %304 %63
OpLoopMerge %59 %80 None
OpBranchConditional %64 %58 %59
%58 = OpLabel
%70 = OpAccessChain %69 %68 %26
%71 = OpLoad %20 %70
%72 = OpConvertFToS %6 %71
%73 = OpSLessThan %17 %304 %72
OpSelectionMerge %75 None
OpBranchConditional %73 %74 %75
%74 = OpLabel
OpBranch %59
%75 = OpLabel
%78 = OpSGreaterThan %17 %304 %9
OpSelectionMerge %80 None
OpBranchConditional %78 %79 %80
%79 = OpLabel
%83 = OpISub %6 %304 %54
%84 = OpAccessChain %51 %46 %83
%85 = OpLoad %20 %84
%86 = OpAccessChain %51 %46 %304
%87 = OpLoad %20 %86
%88 = OpFAdd %20 %87 %85
OpStore %86 %88
OpBranch %80
%80 = OpLabel
%91 = OpIAdd %6 %304 %54
OpBranch %57
%59 = OpLabel
%92 = OpAccessChain %69 %68 %26
%93 = OpLoad %20 %92
%94 = OpConvertFToS %6 %93
%96 = OpSLessThan %17 %94 %95
OpSelectionMerge %98 None
OpBranchConditional %96 %97 %114
%97 = OpLabel
%101 = OpAccessChain %51 %46 %9
%102 = OpLoad %20 %101
%103 = OpAccessChain %27 %24 %9 %26
%104 = OpLoad %20 %103
%105 = OpFDiv %20 %102 %104
%106 = OpAccessChain %51 %46 %35
%107 = OpLoad %20 %106
%109 = OpAccessChain %27 %24 %9 %108
%110 = OpLoad %20 %109
%111 = OpFDiv %20 %107 %110
%113 = OpCompositeConstruct %66 %105 %111 %112 %112
OpStore %100 %113
OpBranch %98
%114 = OpLabel
%119 = OpSLessThan %17 %94 %118
OpSelectionMerge %121 None
OpBranchConditional %119 %120 %135
%120 = OpLabel
%123 = OpAccessChain %51 %46 %122
%124 = OpLoad %20 %123
%125 = OpAccessChain %27 %24 %9 %26
%126 = OpLoad %20 %125
%127 = OpFDiv %20 %124 %126
%129 = OpAccessChain %51 %46 %128
%130 = OpLoad %20 %129
%131 = OpAccessChain %27 %24 %9 %108
%132 = OpLoad %20 %131
%133 = OpFDiv %20 %130 %132
%134 = OpCompositeConstruct %66 %127 %133 %112 %112
OpStore %100 %134
OpBranch %121
%135 = OpLabel
%140 = OpSLessThan %17 %94 %139
OpSelectionMerge %142 None
OpBranchConditional %140 %141 %156
%141 = OpLabel
%144 = OpAccessChain %51 %46 %143
%145 = OpLoad %20 %144
%146 = OpAccessChain %27 %24 %9 %26
%147 = OpLoad %20 %146
%148 = OpFDiv %20 %145 %147
%150 = OpAccessChain %51 %46 %149
%151 = OpLoad %20 %150
%152 = OpAccessChain %27 %24 %9 %108
%153 = OpLoad %20 %152
%154 = OpFDiv %20 %151 %153
%155 = OpCompositeConstruct %66 %148 %154 %112 %112
OpStore %100 %155
OpBranch %142
%156 = OpLabel
%161 = OpSLessThan %17 %94 %160
OpSelectionMerge %163 None
OpBranchConditional %161 %162 %177
%162 = OpLabel
%165 = OpAccessChain %51 %46 %164
%166 = OpLoad %20 %165
%167 = OpAccessChain %27 %24 %9 %26
%168 = OpLoad %20 %167
%169 = OpFDiv %20 %166 %168
%171 = OpAccessChain %51 %46 %170
%172 = OpLoad %20 %171
%173 = OpAccessChain %27 %24 %9 %108
%174 = OpLoad %20 %173
%175 = OpFDiv %20 %172 %174
%176 = OpCompositeConstruct %66 %169 %175 %112 %112
OpStore %100 %176
OpBranch %163
%177 = OpLabel
%182 = OpSLessThan %17 %94 %181
OpSelectionMerge %184 None
OpBranchConditional %182 %183 %197
%183 = OpLabel
%185 = OpAccessChain %51 %46 %95
%186 = OpLoad %20 %185
%187 = OpAccessChain %27 %24 %9 %26
%188 = OpLoad %20 %187
%189 = OpFDiv %20 %186 %188
%191 = OpAccessChain %51 %46 %190
%192 = OpLoad %20 %191
%193 = OpAccessChain %27 %24 %9 %108
%194 = OpLoad %20 %193
%195 = OpFDiv %20 %192 %194
%196 = OpCompositeConstruct %66 %189 %195 %112 %112
OpStore %100 %196
OpBranch %184
%197 = OpLabel
%202 = OpSLessThan %17 %94 %201
OpSelectionMerge %204 None
OpBranchConditional %202 %203 %218
%203 = OpLabel
%206 = OpAccessChain %51 %46 %205
%207 = OpLoad %20 %206
%208 = OpAccessChain %27 %24 %9 %26
%209 = OpLoad %20 %208
%210 = OpFDiv %20 %207 %209
%212 = OpAccessChain %51 %46 %211
%213 = OpLoad %20 %212
%214 = OpAccessChain %27 %24 %9 %108
%215 = OpLoad %20 %214
%216 = OpFDiv %20 %213 %215
%217 = OpCompositeConstruct %66 %210 %216 %112 %112
OpStore %100 %217
OpBranch %204
%218 = OpLabel
%223 = OpSLessThan %17 %94 %222
OpSelectionMerge %225 None
OpBranchConditional %223 %224 %239
%224 = OpLabel
%227 = OpAccessChain %51 %46 %226
%228 = OpLoad %20 %227
%229 = OpAccessChain %27 %24 %9 %26
%230 = OpLoad %20 %229
%231 = OpFDiv %20 %228 %230
%233 = OpAccessChain %51 %46 %232
%234 = OpLoad %20 %233
%235 = OpAccessChain %27 %24 %9 %108
%236 = OpLoad %20 %235
%237 = OpFDiv %20 %234 %236
%238 = OpCompositeConstruct %66 %231 %237 %112 %112
OpStore %100 %238
OpBranch %225
%239 = OpLabel
%244 = OpSLessThan %17 %94 %243
OpSelectionMerge %246 None
OpBranchConditional %244 %245 %260
%245 = OpLabel
%248 = OpAccessChain %51 %46 %247
%249 = OpLoad %20 %248
%250 = OpAccessChain %27 %24 %9 %26
%251 = OpLoad %20 %250
%252 = OpFDiv %20 %249 %251
%254 = OpAccessChain %51 %46 %253
%255 = OpLoad %20 %254
%256 = OpAccessChain %27 %24 %9 %108
%257 = OpLoad %20 %256
%258 = OpFDiv %20 %255 %257
%259 = OpCompositeConstruct %66 %252 %258 %112 %112
OpStore %100 %259
OpBranch %246
%260 = OpLabel
%265 = OpSLessThan %17 %94 %264
OpSelectionMerge %267 None
OpBranchConditional %265 %266 %280
%266 = OpLabel
%268 = OpAccessChain %51 %46 %118
%269 = OpLoad %20 %268
%270 = OpAccessChain %27 %24 %9 %26
%271 = OpLoad %20 %270
%272 = OpFDiv %20 %269 %271
%274 = OpAccessChain %51 %46 %273
%275 = OpLoad %20 %274
%276 = OpAccessChain %27 %24 %9 %108
%277 = OpLoad %20 %276
%278 = OpFDiv %20 %275 %277
%279 = OpCompositeConstruct %66 %272 %278 %112 %112
OpStore %100 %279
OpBranch %267
%280 = OpLabel
OpSelectionMerge %285 None
OpBranchConditional %265 %285 %300
%285 = OpLabel
%288 = OpAccessChain %51 %46 %287
%289 = OpLoad %20 %288
%290 = OpAccessChain %27 %24 %9 %26
%291 = OpLoad %20 %290
%292 = OpFDiv %20 %289 %291
%294 = OpAccessChain %51 %46 %293
%295 = OpLoad %20 %294
%296 = OpAccessChain %27 %24 %9 %108
%297 = OpLoad %20 %296
%298 = OpFDiv %20 %295 %297
%299 = OpCompositeConstruct %66 %292 %298 %112 %112
OpStore %100 %299
OpBranch %267
%300 = OpLabel
OpKill
%267 = OpLabel
OpBranch %246
%246 = OpLabel
OpBranch %225
%225 = OpLabel
OpBranch %204
%204 = OpLabel
OpBranch %184
%184 = OpLabel
OpBranch %163
%163 = OpLabel
OpBranch %142
%142 = OpLabel
OpBranch %121
%121 = OpLabel
OpBranch %98
%98 = OpLabel
OpReturn
OpFunctionEnd
)";
// Add the facts "resolution.x == 250" and "resolution.y == 100".
protobufs::FactSequence facts;
{
protobufs::FactConstantUniform resolution_x_eq_250;
*resolution_x_eq_250.mutable_uniform_buffer_element_descriptor() =
MakeUniformBufferElementDescriptor(0, 0, {0, 0});
*resolution_x_eq_250.mutable_constant_word()->Add() = 250;
protobufs::Fact temp;
*temp.mutable_constant_uniform_fact() = resolution_x_eq_250;
*facts.mutable_fact()->Add() = temp;
}
{
protobufs::FactConstantUniform resolution_y_eq_100;
*resolution_y_eq_100.mutable_uniform_buffer_element_descriptor() =
MakeUniformBufferElementDescriptor(0, 0, {0, 1});
*resolution_y_eq_100.mutable_constant_word()->Add() = 100;
protobufs::Fact temp;
*temp.mutable_constant_uniform_fact() = resolution_y_eq_100;
*facts.mutable_fact()->Add() = temp;
}
// Do 10 fuzzer runs, starting from an initial seed of 94 (seed value chosen
// arbitrarily).
RunFuzzerAndReplayer(shader, facts, 94, 10);
}
} // namespace

View File

@@ -0,0 +1,218 @@
// Copyright (c) 2019 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "source/fuzz/transformation_add_type_pointer.h"
#include "test/fuzz/fuzz_test_util.h"
namespace spvtools {
namespace fuzz {
namespace {
TEST(TransformationAddTypePointerTest, BasicTest) {
// The SPIR-V was obtained from this GLSL:
//
// #version 450
//
// int x;
// float y;
// vec2 z;
//
// struct T {
// int a, b;
// };
//
// struct S {
// T t;
// int u;
// };
//
// void main() {
// S myS = S(T(1, 2), 3);
// myS.u = x;
// }
std::string shader = R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %4 "main"
OpExecutionMode %4 OriginUpperLeft
OpSource GLSL 450
OpName %4 "main"
OpName %7 "T"
OpMemberName %7 0 "a"
OpMemberName %7 1 "b"
OpName %8 "S"
OpMemberName %8 0 "t"
OpMemberName %8 1 "u"
OpName %10 "myS"
OpName %17 "x"
OpName %23 "y"
OpName %26 "z"
%2 = OpTypeVoid
%3 = OpTypeFunction %2
%6 = OpTypeInt 32 1
%7 = OpTypeStruct %6 %6
%8 = OpTypeStruct %7 %6
%9 = OpTypePointer Function %8
%11 = OpConstant %6 1
%12 = OpConstant %6 2
%13 = OpConstantComposite %7 %11 %12
%14 = OpConstant %6 3
%15 = OpConstantComposite %8 %13 %14
%16 = OpTypePointer Private %6
%17 = OpVariable %16 Private
%19 = OpTypePointer Function %6
%21 = OpTypeFloat 32
%22 = OpTypePointer Private %21
%23 = OpVariable %22 Private
%24 = OpTypeVector %21 2
%25 = OpTypePointer Private %24
%26 = OpVariable %25 Private
%4 = OpFunction %2 None %3
%5 = OpLabel
%10 = OpVariable %9 Function
OpStore %10 %15
%18 = OpLoad %6 %17
%20 = OpAccessChain %19 %10 %11
OpStore %20 %18
OpReturn
OpFunctionEnd
)";
const auto env = SPV_ENV_UNIVERSAL_1_3;
const auto consumer = nullptr;
const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
ASSERT_TRUE(IsValid(env, context.get()));
FactManager fact_manager;
auto bad_type_id_does_not_exist =
transformation::MakeTransformationAddTypePointer(
100, SpvStorageClassFunction, 101);
auto bad_type_id_is_not_type =
transformation::MakeTransformationAddTypePointer(
100, SpvStorageClassFunction, 23);
auto bad_result_id_is_not_fresh =
transformation::MakeTransformationAddTypePointer(
17, SpvStorageClassFunction, 21);
auto good_new_private_pointer_to_t =
transformation::MakeTransformationAddTypePointer(
101, SpvStorageClassPrivate, 7);
auto good_new_uniform_pointer_to_t =
transformation::MakeTransformationAddTypePointer(
102, SpvStorageClassUniform, 7);
auto good_another_function_pointer_to_s =
transformation::MakeTransformationAddTypePointer(
103, SpvStorageClassFunction, 8);
auto good_new_uniform_pointer_to_s =
transformation::MakeTransformationAddTypePointer(
104, SpvStorageClassUniform, 8);
auto good_another_private_pointer_to_float =
transformation::MakeTransformationAddTypePointer(
105, SpvStorageClassPrivate, 21);
auto good_new_private_pointer_to_private_pointer_to_float =
transformation::MakeTransformationAddTypePointer(
106, SpvStorageClassPrivate, 105);
auto good_new_uniform_pointer_to_vec2 =
transformation::MakeTransformationAddTypePointer(
107, SpvStorageClassUniform, 24);
auto good_new_private_pointer_to_uniform_pointer_to_vec2 =
transformation::MakeTransformationAddTypePointer(
108, SpvStorageClassPrivate, 107);
ASSERT_FALSE(transformation::IsApplicable(bad_type_id_does_not_exist,
context.get(), fact_manager));
ASSERT_FALSE(transformation::IsApplicable(bad_type_id_is_not_type,
context.get(), fact_manager));
ASSERT_FALSE(transformation::IsApplicable(bad_result_id_is_not_fresh,
context.get(), fact_manager));
for (auto& transformation :
{good_new_private_pointer_to_t, good_new_uniform_pointer_to_t,
good_another_function_pointer_to_s, good_new_uniform_pointer_to_s,
good_another_private_pointer_to_float,
good_new_private_pointer_to_private_pointer_to_float,
good_new_uniform_pointer_to_vec2,
good_new_private_pointer_to_uniform_pointer_to_vec2}) {
ASSERT_TRUE(transformation::IsApplicable(transformation, context.get(),
fact_manager));
transformation::Apply(transformation, context.get(), &fact_manager);
ASSERT_TRUE(IsValid(env, context.get()));
}
std::string after_transformation = R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %4 "main"
OpExecutionMode %4 OriginUpperLeft
OpSource GLSL 450
OpName %4 "main"
OpName %7 "T"
OpMemberName %7 0 "a"
OpMemberName %7 1 "b"
OpName %8 "S"
OpMemberName %8 0 "t"
OpMemberName %8 1 "u"
OpName %10 "myS"
OpName %17 "x"
OpName %23 "y"
OpName %26 "z"
%2 = OpTypeVoid
%3 = OpTypeFunction %2
%6 = OpTypeInt 32 1
%7 = OpTypeStruct %6 %6
%8 = OpTypeStruct %7 %6
%9 = OpTypePointer Function %8
%11 = OpConstant %6 1
%12 = OpConstant %6 2
%13 = OpConstantComposite %7 %11 %12
%14 = OpConstant %6 3
%15 = OpConstantComposite %8 %13 %14
%16 = OpTypePointer Private %6
%17 = OpVariable %16 Private
%19 = OpTypePointer Function %6
%21 = OpTypeFloat 32
%22 = OpTypePointer Private %21
%23 = OpVariable %22 Private
%24 = OpTypeVector %21 2
%25 = OpTypePointer Private %24
%26 = OpVariable %25 Private
%101 = OpTypePointer Private %7
%102 = OpTypePointer Uniform %7
%103 = OpTypePointer Function %8
%104 = OpTypePointer Uniform %8
%105 = OpTypePointer Private %21
%106 = OpTypePointer Private %105
%107 = OpTypePointer Uniform %24
%108 = OpTypePointer Private %107
%4 = OpFunction %2 None %3
%5 = OpLabel
%10 = OpVariable %9 Function
OpStore %10 %15
%18 = OpLoad %6 %17
%20 = OpAccessChain %19 %10 %11
OpStore %20 %18
OpReturn
OpFunctionEnd
)";
ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
}
} // namespace
} // namespace fuzz
} // namespace spvtools

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,84 @@
// Copyright (c) 2019 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "source/fuzz/uniform_buffer_element_descriptor.h"
#include "test/fuzz/fuzz_test_util.h"
namespace spvtools {
namespace fuzz {
namespace {
TEST(UniformBufferElementDescriptorTest, TestEquality) {
// Test that equality works as expected for various buffer element
// descriptors.
protobufs::UniformBufferElementDescriptor descriptor1 =
MakeUniformBufferElementDescriptor(0, 0, {1, 2, 3});
protobufs::UniformBufferElementDescriptor descriptor2 =
MakeUniformBufferElementDescriptor(0, 0, {1, 2, 3});
protobufs::UniformBufferElementDescriptor descriptor3 =
MakeUniformBufferElementDescriptor(0, 1, {1, 2, 3});
protobufs::UniformBufferElementDescriptor descriptor4 =
MakeUniformBufferElementDescriptor(1, 0, {1, 2, 3});
protobufs::UniformBufferElementDescriptor descriptor5 =
MakeUniformBufferElementDescriptor(1, 1, {1, 2, 3});
protobufs::UniformBufferElementDescriptor descriptor6 =
MakeUniformBufferElementDescriptor(0, 0, {1, 2, 4});
protobufs::UniformBufferElementDescriptor descriptor7 =
MakeUniformBufferElementDescriptor(0, 0, {1, 2});
ASSERT_TRUE(
UniformBufferElementDescriptorEquals()(&descriptor1, &descriptor1));
ASSERT_TRUE(
UniformBufferElementDescriptorEquals()(&descriptor1, &descriptor2));
ASSERT_TRUE(
UniformBufferElementDescriptorEquals()(&descriptor3, &descriptor3));
ASSERT_TRUE(
UniformBufferElementDescriptorEquals()(&descriptor4, &descriptor4));
ASSERT_TRUE(
UniformBufferElementDescriptorEquals()(&descriptor5, &descriptor5));
ASSERT_TRUE(
UniformBufferElementDescriptorEquals()(&descriptor6, &descriptor6));
ASSERT_TRUE(
UniformBufferElementDescriptorEquals()(&descriptor7, &descriptor7));
ASSERT_FALSE(
UniformBufferElementDescriptorEquals()(&descriptor1, &descriptor3));
ASSERT_FALSE(
UniformBufferElementDescriptorEquals()(&descriptor3, &descriptor1));
ASSERT_FALSE(
UniformBufferElementDescriptorEquals()(&descriptor1, &descriptor4));
ASSERT_FALSE(
UniformBufferElementDescriptorEquals()(&descriptor4, &descriptor1));
ASSERT_FALSE(
UniformBufferElementDescriptorEquals()(&descriptor1, &descriptor5));
ASSERT_FALSE(
UniformBufferElementDescriptorEquals()(&descriptor5, &descriptor1));
ASSERT_FALSE(
UniformBufferElementDescriptorEquals()(&descriptor1, &descriptor6));
ASSERT_FALSE(
UniformBufferElementDescriptorEquals()(&descriptor6, &descriptor1));
ASSERT_FALSE(
UniformBufferElementDescriptorEquals()(&descriptor1, &descriptor7));
ASSERT_FALSE(
UniformBufferElementDescriptorEquals()(&descriptor7, &descriptor1));
}
} // namespace
} // namespace fuzz
} // namespace spvtools

View File

@@ -2037,4 +2037,203 @@ OpFunctionEnd
SinglePassRunAndMatch<opt::UpgradeMemoryModel>(text, true);
}
TEST_F(UpgradeMemoryModelTest, VolatileAtomicLoad) {
const std::string text = R"(
; CHECK-NOT: OpDecorate {{.*}} Volatile
; CHECK: [[volatile:%[a-zA-Z0-9_]+]] = OpConstant [[int:%[a-zA-Z0-9_]+]] 32768
; CHECK: OpAtomicLoad [[int]] {{.*}} {{.*}} [[volatile]]
OpCapability Shader
OpCapability Linkage
OpMemoryModel Logical GLSL450
OpDecorate %ssbo_var Volatile
%void = OpTypeVoid
%int = OpTypeInt 32 0
%device = OpConstant %int 1
%relaxed = OpConstant %int 0
%ptr_ssbo_int = OpTypePointer StorageBuffer %int
%ssbo_var = OpVariable %ptr_ssbo_int StorageBuffer
%void_fn = OpTypeFunction %void
%func = OpFunction %void None %void_fn
%entry = OpLabel
%ld = OpAtomicLoad %int %ssbo_var %device %relaxed
OpReturn
OpFunctionEnd
)";
SinglePassRunAndMatch<opt::UpgradeMemoryModel>(text, true);
}
TEST_F(UpgradeMemoryModelTest, VolatileAtomicLoadPreviousFlags) {
const std::string text = R"(
; CHECK-NOT: OpDecorate {{.*}} Volatile
; CHECK: [[volatile:%[a-zA-Z0-9_]+]] = OpConstant [[int:%[a-zA-Z0-9_]+]] 32834
; CHECK: OpAtomicLoad [[int]] {{.*}} {{.*}} [[volatile]]
OpCapability Shader
OpCapability Linkage
OpMemoryModel Logical GLSL450
OpDecorate %ssbo_var Volatile
%void = OpTypeVoid
%int = OpTypeInt 32 0
%device = OpConstant %int 1
%acquire_ssbo = OpConstant %int 66
%ptr_ssbo_int = OpTypePointer StorageBuffer %int
%ssbo_var = OpVariable %ptr_ssbo_int StorageBuffer
%void_fn = OpTypeFunction %void
%func = OpFunction %void None %void_fn
%entry = OpLabel
%ld = OpAtomicLoad %int %ssbo_var %device %acquire_ssbo
OpReturn
OpFunctionEnd
)";
SinglePassRunAndMatch<opt::UpgradeMemoryModel>(text, true);
}
TEST_F(UpgradeMemoryModelTest, VolatileAtomicStore) {
const std::string text = R"(
; CHECK-NOT: OpDecorate {{.*}} Volatile
; CHECK: [[volatile:%[a-zA-Z0-9_]+]] = OpConstant {{.*}} 32768
; CHECK: OpAtomicStore {{.*}} {{.*}} [[volatile]]
OpCapability Shader
OpCapability Linkage
OpMemoryModel Logical GLSL450
OpDecorate %ssbo_var Volatile
%void = OpTypeVoid
%int = OpTypeInt 32 0
%int_0 = OpConstant %int 0
%device = OpConstant %int 1
%relaxed = OpConstant %int 0
%ptr_ssbo_int = OpTypePointer StorageBuffer %int
%ssbo_var = OpVariable %ptr_ssbo_int StorageBuffer
%void_fn = OpTypeFunction %void
%func = OpFunction %void None %void_fn
%entry = OpLabel
OpAtomicStore %ssbo_var %device %relaxed %int_0
OpReturn
OpFunctionEnd
)";
SinglePassRunAndMatch<opt::UpgradeMemoryModel>(text, true);
}
TEST_F(UpgradeMemoryModelTest, VolatileAtomicStorePreviousFlags) {
const std::string text = R"(
; CHECK-NOT: OpDecorate {{.*}} Volatile
; CHECK: [[volatile:%[a-zA-Z0-9_]+]] = OpConstant {{.*}} 32836
; CHECK: OpAtomicStore {{.*}} {{.*}} [[volatile]]
OpCapability Shader
OpCapability Linkage
OpMemoryModel Logical GLSL450
OpDecorate %ssbo_var Volatile
%void = OpTypeVoid
%int = OpTypeInt 32 0
%int_0 = OpConstant %int 0
%device = OpConstant %int 1
%release_ssbo = OpConstant %int 68
%ptr_ssbo_int = OpTypePointer StorageBuffer %int
%ssbo_var = OpVariable %ptr_ssbo_int StorageBuffer
%void_fn = OpTypeFunction %void
%func = OpFunction %void None %void_fn
%entry = OpLabel
OpAtomicStore %ssbo_var %device %release_ssbo %int_0
OpReturn
OpFunctionEnd
)";
SinglePassRunAndMatch<opt::UpgradeMemoryModel>(text, true);
}
TEST_F(UpgradeMemoryModelTest, VolatileAtomicCompareExchange) {
const std::string text = R"(
; CHECK-NOT: OpDecorate {{.*}} Volatile
; CHECK: [[volatile:%[a-zA-Z0-9_]+]] = OpConstant [[int:%[a-zA-Z0-9_]+]] 32768
; CHECK: OpAtomicCompareExchange [[int]] {{.*}} {{.*}} [[volatile]] [[volatile]]
OpCapability Shader
OpCapability Linkage
OpMemoryModel Logical GLSL450
OpDecorate %ssbo_var Volatile
%void = OpTypeVoid
%int = OpTypeInt 32 0
%int_0 = OpConstant %int 0
%int_1 = OpConstant %int 1
%device = OpConstant %int 1
%relaxed = OpConstant %int 0
%ptr_ssbo_int = OpTypePointer StorageBuffer %int
%ssbo_var = OpVariable %ptr_ssbo_int StorageBuffer
%void_fn = OpTypeFunction %void
%func = OpFunction %void None %void_fn
%entry = OpLabel
%ld = OpAtomicCompareExchange %int %ssbo_var %device %relaxed %relaxed %int_0 %int_1
OpReturn
OpFunctionEnd
)";
SinglePassRunAndMatch<opt::UpgradeMemoryModel>(text, true);
}
TEST_F(UpgradeMemoryModelTest, VolatileAtomicCompareExchangePreviousFlags) {
const std::string text = R"(
; CHECK-NOT: OpDecorate {{.*}} Volatile
; CHECK: [[volatile_acq_rel:%[a-zA-Z0-9_]+]] = OpConstant [[int:%[a-zA-Z0-9_]+]] 32840
; CHECK: [[volatile_acq:%[a-zA-Z0-9_]+]] = OpConstant [[int:%[a-zA-Z0-9_]+]] 32834
; CHECK: OpAtomicCompareExchange [[int]] {{.*}} {{.*}} [[volatile_acq_rel]] [[volatile_acq]]
OpCapability Shader
OpCapability Linkage
OpMemoryModel Logical GLSL450
OpDecorate %ssbo_var Volatile
%void = OpTypeVoid
%int = OpTypeInt 32 0
%int_0 = OpConstant %int 0
%int_1 = OpConstant %int 1
%device = OpConstant %int 1
%acq_ssbo = OpConstant %int 66
%acq_rel_ssbo = OpConstant %int 72
%ptr_ssbo_int = OpTypePointer StorageBuffer %int
%ssbo_var = OpVariable %ptr_ssbo_int StorageBuffer
%void_fn = OpTypeFunction %void
%func = OpFunction %void None %void_fn
%entry = OpLabel
%ld = OpAtomicCompareExchange %int %ssbo_var %device %acq_rel_ssbo %acq_ssbo %int_0 %int_1
OpReturn
OpFunctionEnd
)";
SinglePassRunAndMatch<opt::UpgradeMemoryModel>(text, true);
}
TEST_F(UpgradeMemoryModelTest, VolatileAtomicLoadMemberDecoration) {
const std::string text = R"(
; CHECK-NOT: OpMemberDecorate {{.*}} {{.*}} Volatile
; CHECK: [[relaxed:%[a-zA-Z0-9_]+]] = OpConstant {{.*}} 0
; CHECK: [[volatile:%[a-zA-Z0-9_]+]] = OpConstant [[int:%[a-zA-Z0-9_]+]] 32768
; CHECK: OpAtomicLoad [[int]] {{.*}} {{.*}} [[relaxed]]
; CHECK: OpAtomicLoad [[int]] {{.*}} {{.*}} [[volatile]]
OpCapability Shader
OpCapability Linkage
OpMemoryModel Logical GLSL450
OpMemberDecorate %struct 1 Volatile
%void = OpTypeVoid
%int = OpTypeInt 32 0
%device = OpConstant %int 1
%relaxed = OpConstant %int 0
%int_0 = OpConstant %int 0
%int_1 = OpConstant %int 1
%ptr_ssbo_int = OpTypePointer StorageBuffer %int
%struct = OpTypeStruct %int %int
%ptr_ssbo_struct = OpTypePointer StorageBuffer %struct
%ssbo_var = OpVariable %ptr_ssbo_struct StorageBuffer
%void_fn = OpTypeFunction %void
%func = OpFunction %void None %void_fn
%entry = OpLabel
%gep0 = OpAccessChain %ptr_ssbo_int %ssbo_var %int_0
%ld0 = OpAtomicLoad %int %gep0 %device %relaxed
%gep1 = OpAccessChain %ptr_ssbo_int %ssbo_var %int_1
%ld1 = OpAtomicLoad %int %gep1 %device %relaxed
OpReturn
OpFunctionEnd
)";
SinglePassRunAndMatch<opt::UpgradeMemoryModel>(text, true);
}
} // namespace

View File

@@ -1998,6 +1998,153 @@ TEST_F(ValidateAtomics, CompareExchangeWeakV14Bad) {
"AtomicCompareExchangeWeak requires SPIR-V version 1.3 or earlier"));
}
TEST_F(ValidateAtomics, CompareExchangeVolatileMatch) {
const std::string spirv = R"(
OpCapability Shader
OpCapability VulkanMemoryModelKHR
OpCapability Linkage
OpExtension "SPV_KHR_vulkan_memory_model"
OpMemoryModel Logical VulkanKHR
%void = OpTypeVoid
%int = OpTypeInt 32 0
%int_0 = OpConstant %int 0
%int_1 = OpConstant %int 1
%workgroup = OpConstant %int 2
%volatile = OpConstant %int 32768
%ptr_wg_int = OpTypePointer Workgroup %int
%wg_var = OpVariable %ptr_wg_int Workgroup
%void_fn = OpTypeFunction %void
%func = OpFunction %void None %void_fn
%entry = OpLabel
%cmp_ex = OpAtomicCompareExchange %int %wg_var %workgroup %volatile %volatile %int_0 %int_1
OpReturn
OpFunctionEnd
)";
CompileSuccessfully(spirv);
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
}
TEST_F(ValidateAtomics, CompareExchangeVolatileMismatch) {
const std::string spirv = R"(
OpCapability Shader
OpCapability VulkanMemoryModelKHR
OpCapability Linkage
OpExtension "SPV_KHR_vulkan_memory_model"
OpMemoryModel Logical VulkanKHR
%void = OpTypeVoid
%int = OpTypeInt 32 0
%int_0 = OpConstant %int 0
%int_1 = OpConstant %int 1
%workgroup = OpConstant %int 2
%volatile = OpConstant %int 32768
%non_volatile = OpConstant %int 0
%ptr_wg_int = OpTypePointer Workgroup %int
%wg_var = OpVariable %ptr_wg_int Workgroup
%void_fn = OpTypeFunction %void
%func = OpFunction %void None %void_fn
%entry = OpLabel
%cmp_ex = OpAtomicCompareExchange %int %wg_var %workgroup %non_volatile %volatile %int_0 %int_1
OpReturn
OpFunctionEnd
)";
CompileSuccessfully(spirv);
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
EXPECT_THAT(getDiagnosticString(),
HasSubstr("Volatile mask setting must match for Equal and "
"Unequal memory semantics"));
}
TEST_F(ValidateAtomics, CompareExchangeVolatileMismatchCooperativeMatrix) {
const std::string spirv = R"(
OpCapability Shader
OpCapability VulkanMemoryModelKHR
OpCapability Linkage
OpCapability CooperativeMatrixNV
OpExtension "SPV_KHR_vulkan_memory_model"
OpExtension "SPV_NV_cooperative_matrix"
OpMemoryModel Logical VulkanKHR
%void = OpTypeVoid
%int = OpTypeInt 32 0
%int_0 = OpConstant %int 0
%int_1 = OpConstant %int 1
%workgroup = OpConstant %int 2
%volatile = OpSpecConstant %int 32768
%non_volatile = OpSpecConstant %int 32768
%ptr_wg_int = OpTypePointer Workgroup %int
%wg_var = OpVariable %ptr_wg_int Workgroup
%void_fn = OpTypeFunction %void
%func = OpFunction %void None %void_fn
%entry = OpLabel
%cmp_ex = OpAtomicCompareExchange %int %wg_var %workgroup %volatile %non_volatile %int_0 %int_1
OpReturn
OpFunctionEnd
)";
// This is ok because we cannot evaluate the spec constant defaults.
CompileSuccessfully(spirv);
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
}
TEST_F(ValidateAtomics, VolatileRequiresVulkanMemoryModel) {
const std::string spirv = R"(
OpCapability Shader
OpCapability Linkage
OpMemoryModel Logical GLSL450
%void = OpTypeVoid
%int = OpTypeInt 32 0
%int_0 = OpConstant %int 0
%int_1 = OpConstant %int 1
%workgroup = OpConstant %int 2
%volatile = OpConstant %int 32768
%ptr_wg_int = OpTypePointer Workgroup %int
%wg_var = OpVariable %ptr_wg_int Workgroup
%void_fn = OpTypeFunction %void
%func = OpFunction %void None %void_fn
%entry = OpLabel
%ld = OpAtomicLoad %int %wg_var %workgroup %volatile
OpReturn
OpFunctionEnd
)";
CompileSuccessfully(spirv);
EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
EXPECT_THAT(getDiagnosticString(),
HasSubstr("Memory Semantics Volatile requires capability "
"VulkanMemoryModelKHR"));
}
TEST_F(ValidateAtomics, CooperativeMatrixSemanticsMustBeConstant) {
const std::string spirv = R"(
OpCapability Shader
OpCapability Linkage
OpCapability CooperativeMatrixNV
OpExtension "SPV_NV_cooperative_matrix"
OpMemoryModel Logical GLSL450
%void = OpTypeVoid
%int = OpTypeInt 32 0
%int_0 = OpConstant %int 0
%int_1 = OpConstant %int 1
%workgroup = OpConstant %int 2
%undef = OpUndef %int
%ptr_wg_int = OpTypePointer Workgroup %int
%wg_var = OpVariable %ptr_wg_int Workgroup
%void_fn = OpTypeFunction %void
%func = OpFunction %void None %void_fn
%entry = OpLabel
%ld = OpAtomicLoad %int %wg_var %workgroup %undef
OpReturn
OpFunctionEnd
)";
CompileSuccessfully(spirv);
EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
EXPECT_THAT(getDiagnosticString(),
HasSubstr("Memory Semantics must be a constant instruction when "
"CooperativeMatrixNV capability is present"));
}
} // namespace
} // namespace val
} // namespace spvtools

View File

@@ -1361,6 +1361,115 @@ OpFunctionEnd
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
}
TEST_F(ValidateBarriers, VolatileMemoryBarrier) {
const std::string text = R"(
OpCapability Shader
OpCapability VulkanMemoryModelKHR
OpCapability VulkanMemoryModelDeviceScopeKHR
OpCapability Linkage
OpExtension "SPV_KHR_vulkan_memory_model"
OpMemoryModel Logical VulkanKHR
%void = OpTypeVoid
%int = OpTypeInt 32 0
%device = OpConstant %int 1
%semantics = OpConstant %int 32768
%functy = OpTypeFunction %void
%func = OpFunction %void None %functy
%1 = OpLabel
OpMemoryBarrier %device %semantics
OpReturn
OpFunctionEnd
)";
CompileSuccessfully(text);
EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
EXPECT_THAT(getDiagnosticString(),
HasSubstr("Memory Semantics Volatile can only be used with "
"atomic instructions"));
}
TEST_F(ValidateBarriers, VolatileControlBarrier) {
const std::string text = R"(
OpCapability Shader
OpCapability VulkanMemoryModelKHR
OpCapability VulkanMemoryModelDeviceScopeKHR
OpCapability Linkage
OpExtension "SPV_KHR_vulkan_memory_model"
OpMemoryModel Logical VulkanKHR
%void = OpTypeVoid
%int = OpTypeInt 32 0
%device = OpConstant %int 1
%semantics = OpConstant %int 32768
%functy = OpTypeFunction %void
%func = OpFunction %void None %functy
%1 = OpLabel
OpControlBarrier %device %device %semantics
OpReturn
OpFunctionEnd
)";
CompileSuccessfully(text);
EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
EXPECT_THAT(getDiagnosticString(),
HasSubstr("Memory Semantics Volatile can only be used with "
"atomic instructions"));
}
TEST_F(ValidateBarriers, CooperativeMatrixSpecConstantVolatile) {
const std::string text = R"(
OpCapability Shader
OpCapability VulkanMemoryModelKHR
OpCapability VulkanMemoryModelDeviceScopeKHR
OpCapability CooperativeMatrixNV
OpCapability Linkage
OpExtension "SPV_KHR_vulkan_memory_model"
OpExtension "SPV_NV_cooperative_matrix"
OpMemoryModel Logical VulkanKHR
%void = OpTypeVoid
%int = OpTypeInt 32 0
%device = OpConstant %int 1
%semantics = OpSpecConstant %int 32768
%functy = OpTypeFunction %void
%func = OpFunction %void None %functy
%1 = OpLabel
OpControlBarrier %device %device %semantics
OpReturn
OpFunctionEnd
)";
CompileSuccessfully(text);
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
}
TEST_F(ValidateBarriers, CooperativeMatrixNonConstantSemantics) {
const std::string text = R"(
OpCapability Shader
OpCapability VulkanMemoryModelKHR
OpCapability VulkanMemoryModelDeviceScopeKHR
OpCapability CooperativeMatrixNV
OpCapability Linkage
OpExtension "SPV_KHR_vulkan_memory_model"
OpExtension "SPV_NV_cooperative_matrix"
OpMemoryModel Logical VulkanKHR
%void = OpTypeVoid
%int = OpTypeInt 32 0
%device = OpConstant %int 1
%semantics = OpUndef %int
%functy = OpTypeFunction %void
%func = OpFunction %void None %functy
%1 = OpLabel
OpControlBarrier %device %device %semantics
OpReturn
OpFunctionEnd
)";
CompileSuccessfully(text);
EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
EXPECT_THAT(getDiagnosticString(),
HasSubstr("Memory Semantics must be a constant instruction when "
"CooperativeMatrixNV capability is present"));
}
} // namespace
} // namespace val
} // namespace spvtools

View File

@@ -1168,8 +1168,10 @@ std::make_pair(std::string(kOpenCLMemoryModel) +
ShaderDependencies()),
std::make_pair(std::string(kOpenCLMemoryModel) +
"OpEntryPoint Kernel %func \"compute\" \n"
"OpDecorate %intt Component 0\n"
"%intt = OpTypeInt 32 0\n" + std::string(kVoidFVoid),
"OpDecorate %var Component 0\n"
"%intt = OpTypeInt 32 0\n"
"%ptr = OpTypePointer Input %intt\n"
"%var = OpVariable %ptr Input\n" + std::string(kVoidFVoid),
ShaderDependencies()),
std::make_pair(std::string(kOpenCLMemoryModel) +
"OpEntryPoint Kernel %func \"compute\" \n"

View File

@@ -6400,6 +6400,314 @@ OpDecorate %1 BufferBlock
"requires SPIR-V version 1.3 or earlier"));
}
// Component
TEST_F(ValidateDecorations, ComponentDecorationBadTarget) {
std::string spirv = R"(
OpCapability Shader
OpMemoryModel Logical GLSL450
OpEntryPoint Vertex %main "main"
OpDecorate %t Component 0
%void = OpTypeVoid
%3 = OpTypeFunction %void
%float = OpTypeFloat 32
%t = OpTypeVector %float 2
%main = OpFunction %void None %3
%5 = OpLabel
OpReturn
OpFunctionEnd
)";
CompileSuccessfully(spirv);
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateAndRetrieveValidationState());
EXPECT_THAT(getDiagnosticString(),
HasSubstr("Target of Component decoration must be "
"a memory object declaration"));
}
TEST_F(ValidateDecorations, ComponentDecorationBadStorageClass) {
std::string spirv = R"(
OpCapability Shader
OpMemoryModel Logical GLSL450
OpEntryPoint Vertex %main "main"
OpDecorate %v Component 0
%void = OpTypeVoid
%3 = OpTypeFunction %void
%float = OpTypeFloat 32
%t = OpTypeVector %float 2
%ptr_private = OpTypePointer Private %t
%v = OpVariable %ptr_private Private
%main = OpFunction %void None %3
%5 = OpLabel
OpReturn
OpFunctionEnd
)";
CompileSuccessfully(spirv);
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateAndRetrieveValidationState());
EXPECT_THAT(getDiagnosticString(),
HasSubstr("Target of Component decoration is invalid: must "
"point to a Storage Class of Input(1) or Output(3)"));
}
TEST_F(ValidateDecorations, ComponentDecorationBadTypeVulkan) {
const spv_target_env env = SPV_ENV_VULKAN_1_0;
std::string spirv = R"(
OpCapability Shader
OpCapability Matrix
OpMemoryModel Logical GLSL450
OpEntryPoint Vertex %main "main"
OpDecorate %v Component 0
%void = OpTypeVoid
%3 = OpTypeFunction %void
%float = OpTypeFloat 32
%vtype = OpTypeVector %float 4
%t = OpTypeMatrix %vtype 4
%ptr_input = OpTypePointer Input %t
%v = OpVariable %ptr_input Input
%main = OpFunction %void None %3
%5 = OpLabel
OpReturn
OpFunctionEnd
)";
CompileSuccessfully(spirv, env);
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateAndRetrieveValidationState(env));
EXPECT_THAT(getDiagnosticString(),
HasSubstr("Component decoration specified for type"));
EXPECT_THAT(getDiagnosticString(), HasSubstr("is not a scalar or vector"));
}
std::string ShaderWithComponentDecoration(const std::string& type,
const std::string& decoration) {
return R"(
OpCapability Shader
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %main "main" %entryPointOutput
OpExecutionMode %main OriginUpperLeft
OpDecorate %entryPointOutput Location 0
OpDecorate %entryPointOutput )" +
decoration + R"(
%void = OpTypeVoid
%3 = OpTypeFunction %void
%float = OpTypeFloat 32
%vtype = )" + type + R"(
%float_0 = OpConstant %float 0
%_ptr_Output_vtype = OpTypePointer Output %vtype
%entryPointOutput = OpVariable %_ptr_Output_vtype Output
%main = OpFunction %void None %3
%5 = OpLabel
OpReturn
OpFunctionEnd
)";
}
TEST_F(ValidateDecorations, ComponentDecorationIntGood0Vulkan) {
const spv_target_env env = SPV_ENV_VULKAN_1_0;
std::string spirv =
ShaderWithComponentDecoration("OpTypeInt 32 0", "Component 0");
CompileSuccessfully(spirv, env);
EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState(env));
EXPECT_THAT(getDiagnosticString(), Eq(""));
}
TEST_F(ValidateDecorations, ComponentDecorationIntGood1Vulkan) {
const spv_target_env env = SPV_ENV_VULKAN_1_0;
std::string spirv =
ShaderWithComponentDecoration("OpTypeInt 32 0", "Component 1");
CompileSuccessfully(spirv, env);
EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState(env));
EXPECT_THAT(getDiagnosticString(), Eq(""));
}
TEST_F(ValidateDecorations, ComponentDecorationIntGood2Vulkan) {
const spv_target_env env = SPV_ENV_VULKAN_1_0;
std::string spirv =
ShaderWithComponentDecoration("OpTypeInt 32 0", "Component 2");
CompileSuccessfully(spirv, env);
EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState(env));
EXPECT_THAT(getDiagnosticString(), Eq(""));
}
TEST_F(ValidateDecorations, ComponentDecorationIntGood3Vulkan) {
const spv_target_env env = SPV_ENV_VULKAN_1_0;
std::string spirv =
ShaderWithComponentDecoration("OpTypeInt 32 0", "Component 3");
CompileSuccessfully(spirv, env);
EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState(env));
EXPECT_THAT(getDiagnosticString(), Eq(""));
}
TEST_F(ValidateDecorations, ComponentDecorationIntBad4Vulkan) {
const spv_target_env env = SPV_ENV_VULKAN_1_0;
std::string spirv =
ShaderWithComponentDecoration("OpTypeInt 32 0", "Component 4");
CompileSuccessfully(spirv, env);
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateAndRetrieveValidationState(env));
EXPECT_THAT(getDiagnosticString(),
HasSubstr("Sequence of components starting with 4 "
"and ending with 4 gets larger than 3"));
}
TEST_F(ValidateDecorations, ComponentDecorationVector3GoodVulkan) {
const spv_target_env env = SPV_ENV_VULKAN_1_0;
std::string spirv =
ShaderWithComponentDecoration("OpTypeVector %float 3", "Component 1");
CompileSuccessfully(spirv, env);
EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState(env));
EXPECT_THAT(getDiagnosticString(), Eq(""));
}
TEST_F(ValidateDecorations, ComponentDecorationVector4GoodVulkan) {
const spv_target_env env = SPV_ENV_VULKAN_1_0;
std::string spirv =
ShaderWithComponentDecoration("OpTypeVector %float 4", "Component 0");
CompileSuccessfully(spirv, env);
EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState(env));
EXPECT_THAT(getDiagnosticString(), Eq(""));
}
TEST_F(ValidateDecorations, ComponentDecorationVector4Bad1Vulkan) {
const spv_target_env env = SPV_ENV_VULKAN_1_0;
std::string spirv =
ShaderWithComponentDecoration("OpTypeVector %float 4", "Component 1");
CompileSuccessfully(spirv, env);
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateAndRetrieveValidationState(env));
EXPECT_THAT(getDiagnosticString(),
HasSubstr("Sequence of components starting with 1 "
"and ending with 4 gets larger than 3"));
}
TEST_F(ValidateDecorations, ComponentDecorationVector4Bad3Vulkan) {
const spv_target_env env = SPV_ENV_VULKAN_1_0;
std::string spirv =
ShaderWithComponentDecoration("OpTypeVector %float 4", "Component 3");
CompileSuccessfully(spirv, env);
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateAndRetrieveValidationState(env));
EXPECT_THAT(getDiagnosticString(),
HasSubstr("Sequence of components starting with 3 "
"and ending with 6 gets larger than 3"));
}
TEST_F(ValidateDecorations, ComponentDecorationBlockGood) {
std::string spirv = R"(
OpCapability Shader
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %4 "main" %9 %12
OpExecutionMode %4 OriginUpperLeft
OpDecorate %9 Location 0
OpMemberDecorate %block 0 Location 2
OpMemberDecorate %block 0 Component 1
OpDecorate %block Block
%2 = OpTypeVoid
%3 = OpTypeFunction %2
%float = OpTypeFloat 32
%vec3 = OpTypeVector %float 3
%8 = OpTypePointer Output %vec3
%9 = OpVariable %8 Output
%block = OpTypeStruct %vec3
%11 = OpTypePointer Input %block
%12 = OpVariable %11 Input
%int = OpTypeInt 32 1
%14 = OpConstant %int 0
%15 = OpTypePointer Input %vec3
%4 = OpFunction %2 None %3
%5 = OpLabel
%16 = OpAccessChain %15 %12 %14
%17 = OpLoad %vec3 %16
OpStore %9 %17
OpReturn
OpFunctionEnd
)";
CompileSuccessfully(spirv);
EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState());
EXPECT_THAT(getDiagnosticString(), Eq(""));
}
TEST_F(ValidateDecorations, ComponentDecorationBlockBadVulkan) {
const spv_target_env env = SPV_ENV_VULKAN_1_0;
std::string spirv = R"(
OpCapability Shader
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %4 "main" %9 %12
OpExecutionMode %4 OriginUpperLeft
OpDecorate %9 Location 0
OpMemberDecorate %block 0 Location 2
OpMemberDecorate %block 0 Component 2
OpDecorate %block Block
%2 = OpTypeVoid
%3 = OpTypeFunction %2
%float = OpTypeFloat 32
%vec3 = OpTypeVector %float 3
%8 = OpTypePointer Output %vec3
%9 = OpVariable %8 Output
%block = OpTypeStruct %vec3
%11 = OpTypePointer Input %block
%12 = OpVariable %11 Input
%int = OpTypeInt 32 1
%14 = OpConstant %int 0
%15 = OpTypePointer Input %vec3
%4 = OpFunction %2 None %3
%5 = OpLabel
%16 = OpAccessChain %15 %12 %14
%17 = OpLoad %vec3 %16
OpStore %9 %17
OpReturn
OpFunctionEnd
)";
CompileSuccessfully(spirv, env);
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateAndRetrieveValidationState(env));
EXPECT_THAT(getDiagnosticString(),
HasSubstr("Sequence of components starting with 2 "
"and ending with 4 gets larger than 3"));
}
TEST_F(ValidateDecorations, ComponentDecorationFunctionParameter) {
std::string spirv = R"(
OpCapability Shader
OpMemoryModel Logical GLSL450
OpEntryPoint Vertex %main "main"
OpDecorate %param_f Component 0
%void = OpTypeVoid
%void_fn = OpTypeFunction %void
%float = OpTypeFloat 32
%float_0 = OpConstant %float 0
%int = OpTypeInt 32 0
%int_2 = OpConstant %int 2
%struct_b = OpTypeStruct %float
%extra_fn = OpTypeFunction %void %float
%helper = OpFunction %void None %extra_fn
%param_f = OpFunctionParameter %float
%helper_label = OpLabel
OpReturn
OpFunctionEnd
%main = OpFunction %void None %void_fn
%label = OpLabel
OpReturn
OpFunctionEnd
)";
CompileSuccessfully(spirv);
EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState());
EXPECT_THAT(getDiagnosticString(), Eq(""));
}
} // namespace
} // namespace val
} // namespace spvtools

View File

@@ -3566,6 +3566,272 @@ OpMemoryModel Logical GLSL450
"the Result Type"));
}
TEST_F(ValidateMemory, StoreToUniformBlock) {
const std::string spirv = R"(
OpCapability Shader
OpMemoryModel Logical GLSL450
OpEntryPoint GLCompute %main "main"
OpExecutionMode %main LocalSize 1 1 1
OpDecorate %struct Block
OpMemberDecorate %struct 0 Offset 0
OpDecorate %var DescriptorSet 0
OpDecorate %var Binding 0
%void = OpTypeVoid
%int = OpTypeInt 32 0
%int_0 = OpConstant %int 0
%int4 = OpTypeVector %int 4
%struct = OpTypeStruct %int4
%ptr_uniform_struct = OpTypePointer Uniform %struct
%ptr_uniform_int4 = OpTypePointer Uniform %int4
%ptr_uniform_int = OpTypePointer Uniform %int
%var = OpVariable %ptr_uniform_struct Uniform
%void_fn = OpTypeFunction %void
%main = OpFunction %void None %void_fn
%entry = OpLabel
%gep1 = OpAccessChain %ptr_uniform_int4 %var %int_0
%gep2 = OpAccessChain %ptr_uniform_int %gep1 %int_0
OpStore %gep2 %int_0
OpReturn
OpFunctionEnd
)";
CompileSuccessfully(spirv);
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
}
TEST_F(ValidateMemory, StoreToUniformBlockVulkan) {
const std::string spirv = R"(
OpCapability Shader
OpMemoryModel Logical GLSL450
OpEntryPoint GLCompute %main "main"
OpExecutionMode %main LocalSize 1 1 1
OpDecorate %struct Block
OpMemberDecorate %struct 0 Offset 0
OpDecorate %var DescriptorSet 0
OpDecorate %var Binding 0
%void = OpTypeVoid
%int = OpTypeInt 32 0
%int_0 = OpConstant %int 0
%int4 = OpTypeVector %int 4
%struct = OpTypeStruct %int4
%ptr_uniform_struct = OpTypePointer Uniform %struct
%ptr_uniform_int4 = OpTypePointer Uniform %int4
%ptr_uniform_int = OpTypePointer Uniform %int
%var = OpVariable %ptr_uniform_struct Uniform
%void_fn = OpTypeFunction %void
%main = OpFunction %void None %void_fn
%entry = OpLabel
%gep1 = OpAccessChain %ptr_uniform_int4 %var %int_0
%gep2 = OpAccessChain %ptr_uniform_int %gep1 %int_0
OpStore %gep2 %int_0
OpReturn
OpFunctionEnd
)";
CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_1);
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_1));
EXPECT_THAT(
getDiagnosticString(),
HasSubstr("In the Vulkan environment, cannot store to Uniform Blocks"));
}
// This test requires that the struct is not id 2.
TEST_F(ValidateMemory, StoreToUniformBlockVulkan2) {
const std::string spirv = R"(
OpCapability Shader
OpMemoryModel Logical GLSL450
OpEntryPoint GLCompute %main "main" %gid_var
OpExecutionMode %main LocalSize 1 1 1
OpDecorate %3 Block
OpMemberDecorate %3 0 Offset 0
OpDecorate %var DescriptorSet 0
OpDecorate %var Binding 0
OpDecorate %gid_var BuiltIn GlobalInvocationId
%void = OpTypeVoid
%int = OpTypeInt 32 0
%int_0 = OpConstant %int 0
%int3 = OpTypeVector %int 3
%int4 = OpTypeVector %int 4
%3 = OpTypeStruct %int4
%ptr_uniform_struct = OpTypePointer Uniform %3
%ptr_uniform_int4 = OpTypePointer Uniform %int4
%ptr_uniform_int = OpTypePointer Uniform %int
%var = OpVariable %ptr_uniform_struct Uniform
%ptr_input_int3 = OpTypePointer Input %int3
%gid_var = OpVariable %ptr_input_int3 Input
%void_fn = OpTypeFunction %void
%main = OpFunction %void None %void_fn
%entry = OpLabel
%gep1 = OpAccessChain %ptr_uniform_int4 %var %int_0
%gep2 = OpAccessChain %ptr_uniform_int %gep1 %int_0
OpStore %gep2 %int_0
OpReturn
OpFunctionEnd
)";
CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_1);
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_1));
EXPECT_THAT(
getDiagnosticString(),
HasSubstr("In the Vulkan environment, cannot store to Uniform Blocks"));
}
TEST_F(ValidateMemory, StoreToUniformBufferBlockVulkan) {
const std::string spirv = R"(
OpCapability Shader
OpMemoryModel Logical GLSL450
OpEntryPoint GLCompute %main "main"
OpExecutionMode %main LocalSize 1 1 1
OpDecorate %struct BufferBlock
OpMemberDecorate %struct 0 Offset 0
OpDecorate %var DescriptorSet 0
OpDecorate %var Binding 0
%void = OpTypeVoid
%int = OpTypeInt 32 0
%int_0 = OpConstant %int 0
%int4 = OpTypeVector %int 4
%struct = OpTypeStruct %int4
%ptr_uniform_struct = OpTypePointer Uniform %struct
%ptr_uniform_int4 = OpTypePointer Uniform %int4
%ptr_uniform_int = OpTypePointer Uniform %int
%var = OpVariable %ptr_uniform_struct Uniform
%void_fn = OpTypeFunction %void
%main = OpFunction %void None %void_fn
%entry = OpLabel
%gep1 = OpAccessChain %ptr_uniform_int4 %var %int_0
%gep2 = OpAccessChain %ptr_uniform_int %gep1 %int_0
OpStore %gep2 %int_0
OpReturn
OpFunctionEnd
)";
CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_1);
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_1));
}
TEST_F(ValidateMemory, StoreToUniformBlockVulkanArray) {
const std::string spirv = R"(
OpCapability Shader
OpMemoryModel Logical GLSL450
OpEntryPoint GLCompute %main "main"
OpExecutionMode %main LocalSize 1 1 1
OpDecorate %struct Block
OpMemberDecorate %struct 0 Offset 0
OpDecorate %var DescriptorSet 0
OpDecorate %var Binding 0
%void = OpTypeVoid
%int = OpTypeInt 32 0
%int_0 = OpConstant %int 0
%int_1 = OpConstant %int 1
%int4 = OpTypeVector %int 4
%struct = OpTypeStruct %int4
%array_struct = OpTypeArray %struct %int_1
%ptr_uniform_array = OpTypePointer Uniform %array_struct
%ptr_uniform_struct = OpTypePointer Uniform %struct
%ptr_uniform_int4 = OpTypePointer Uniform %int4
%ptr_uniform_int = OpTypePointer Uniform %int
%var = OpVariable %ptr_uniform_array Uniform
%void_fn = OpTypeFunction %void
%main = OpFunction %void None %void_fn
%entry = OpLabel
%gep1 = OpAccessChain %ptr_uniform_int %var %int_0 %int_0 %int_0
%gep2 = OpCopyObject %ptr_uniform_int %gep1
OpStore %gep2 %int_0
OpReturn
OpFunctionEnd
)";
CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_1);
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_1));
EXPECT_THAT(
getDiagnosticString(),
HasSubstr("In the Vulkan environment, cannot store to Uniform Blocks"));
}
// This test requires that the struct is not id 2.
TEST_F(ValidateMemory, StoreToUniformBlockVulkanArray2) {
const std::string spirv = R"(
OpCapability Shader
OpMemoryModel Logical GLSL450
OpEntryPoint GLCompute %main "main" %gid_var
OpExecutionMode %main LocalSize 1 1 1
OpDecorate %struct Block
OpMemberDecorate %struct 0 Offset 0
OpDecorate %var DescriptorSet 0
OpDecorate %var Binding 0
OpDecorate %gid_var BuiltIn GlobalInvocationId
%void = OpTypeVoid
%int = OpTypeInt 32 0
%int_0 = OpConstant %int 0
%int_1 = OpConstant %int 1
%int3 = OpTypeVector %int 3
%int4 = OpTypeVector %int 4
%struct = OpTypeStruct %int4
%array_struct = OpTypeArray %struct %int_1
%ptr_uniform_array = OpTypePointer Uniform %array_struct
%ptr_uniform_struct = OpTypePointer Uniform %struct
%ptr_uniform_int4 = OpTypePointer Uniform %int4
%ptr_uniform_int = OpTypePointer Uniform %int
%var = OpVariable %ptr_uniform_array Uniform
%ptr_input_int3 = OpTypePointer Input %int3
%gid_var = OpVariable %ptr_input_int3 Input
%void_fn = OpTypeFunction %void
%main = OpFunction %void None %void_fn
%entry = OpLabel
%gep1 = OpAccessChain %ptr_uniform_int %var %int_0 %int_0 %int_0
%gep2 = OpCopyObject %ptr_uniform_int %gep1
OpStore %gep2 %int_0
OpReturn
OpFunctionEnd
)";
CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_1);
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_1));
EXPECT_THAT(
getDiagnosticString(),
HasSubstr("In the Vulkan environment, cannot store to Uniform Blocks"));
}
TEST_F(ValidateMemory, StoreToUniformBlockVulkanRuntimeArray) {
const std::string spirv = R"(
OpCapability Shader
OpCapability RuntimeDescriptorArrayEXT
OpExtension "SPV_EXT_descriptor_indexing"
OpMemoryModel Logical GLSL450
OpEntryPoint GLCompute %main "main"
OpExecutionMode %main LocalSize 1 1 1
OpDecorate %struct Block
OpMemberDecorate %struct 0 Offset 0
OpDecorate %var DescriptorSet 0
OpDecorate %var Binding 0
%void = OpTypeVoid
%int = OpTypeInt 32 0
%int_0 = OpConstant %int 0
%int4 = OpTypeVector %int 4
%struct = OpTypeStruct %int4
%array_struct = OpTypeRuntimeArray %struct
%ptr_uniform_array = OpTypePointer Uniform %array_struct
%ptr_uniform_struct = OpTypePointer Uniform %struct
%ptr_uniform_int4 = OpTypePointer Uniform %int4
%ptr_uniform_int = OpTypePointer Uniform %int
%var = OpVariable %ptr_uniform_array Uniform
%void_fn = OpTypeFunction %void
%main = OpFunction %void None %void_fn
%entry = OpLabel
%gep1 = OpAccessChain %ptr_uniform_int4 %var %int_0 %int_0
%gep2 = OpInBoundsAccessChain %ptr_uniform_int %gep1 %int_0
OpStore %gep2 %int_0
OpReturn
OpFunctionEnd
)";
CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_1);
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_1));
EXPECT_THAT(
getDiagnosticString(),
HasSubstr("In the Vulkan environment, cannot store to Uniform Blocks"));
}
} // namespace
} // namespace val
} // namespace spvtools

View File

@@ -21,6 +21,7 @@
#include "tools/io.h"
void print_usage(char* argv0) {
std::string target_env_list = spvTargetEnvList(19, 80);
printf(
R"(%s - Create a SPIR-V binary module from SPIR-V assembly text
@@ -41,11 +42,10 @@ Options:
Numeric IDs in the binary will have the same values as in the
source. Non-numeric IDs are allocated by filling in the gaps,
starting with 1 and going up.
--target-env {vulkan1.0|vulkan1.1|spv1.0|spv1.1|spv1.2|spv1.3|spv1.4}
Use Vulkan 1.0, Vulkan 1.1, SPIR-V 1.0, SPIR-V 1.1,
SPIR-V 1.2, SPIR-V 1.3, or SPIR-V 1.4
--target-env {%s}
Use specified environment.
)",
argv0, argv0);
argv0, argv0, target_env_list.c_str());
}
static const auto kDefaultEnvironment = SPV_ENV_UNIVERSAL_1_4;

View File

@@ -213,7 +213,7 @@ bool Replay(const spv_target_env& target_env,
bool Fuzz(const spv_target_env& target_env,
const spvtools::FuzzerOptions& fuzzer_options,
const std::vector<uint>& binary_in,
const std::vector<uint32_t>& binary_in,
const spvtools::fuzz::protobufs::FactSequence& initial_facts,
std::vector<uint32_t>* binary_out,
spvtools::fuzz::protobufs::TransformationSequence*

View File

@@ -23,6 +23,7 @@
#include "tools/io.h"
void print_usage(char* argv0) {
std::string target_env_list = spvTargetEnvList(27, 95);
printf(
R"(%s - Link SPIR-V binary files together.
@@ -39,11 +40,10 @@ Options:
--allow-partial-linkage Allow partial linkage by accepting imported symbols to be unresolved.
--verify-ids Verify that IDs in the resulting modules are truly unique.
--version Display linker version information
--target-env {vulkan1.0|vulkan1.1|spv1.0|spv1.1|spv1.2|spv1.3|spv1.4|opencl2.1|opencl2.2}
Use Vulkan 1.0, Vulkan 1.1, SPIR-V 1.0, SPIR-V 1.1, SPIR-V 1.2, SPIR-V 1.3,
SPIR-V1.4, OpenCL 2.1, OpenCL 2.2 validation rules.
--target-env {%s}
Use validation rules from the specified environment.
)",
argv0, argv0);
argv0, argv0, target_env_list.c_str());
}
int main(int argc, char** argv) {

View File

@@ -92,6 +92,7 @@ std::string GetWebGPUToVulkanPasses() {
}
void PrintUsage(const char* program) {
std::string target_env_list = spvTargetEnvList(16, 80);
// NOTE: Please maintain flags in lexicographical order.
printf(
R"(%s - Optimize a SPIR-V binary file.
@@ -436,9 +437,9 @@ Options (in lexicographical order):)",
printf(R"(
--target-env=<env>
Set the target environment. Without this flag the target
enviroment defaults to spv1.3.
<env> must be one of vulkan1.0, vulkan1.1, opencl2.2, spv1.0,
spv1.1, spv1.2, spv1.3, or webgpu0.)");
enviroment defaults to spv1.3. <env> must be one of
{%s})",
target_env_list.c_str());
printf(R"(
--time-report
Print the resource utilization of each pass (e.g., CPU time,

View File

@@ -25,6 +25,7 @@
#include "tools/util/cli_consumer.h"
void print_usage(char* argv0) {
std::string target_env_list = spvTargetEnvList(36, 105);
printf(
R"(%s - Validate a SPIR-V binary file.
@@ -65,13 +66,10 @@ Options:
--before-hlsl-legalization Allows code patterns that are intended to be
fixed by spirv-opt's legalization passes.
--version Display validator version information.
--target-env {vulkan1.0|vulkan1.1|vulkan1.1spv1.4|opencl2.2|spv1.0|spv1.1|
spv1.2|spv1.3|spv1.4|webgpu0}
Use Vulkan 1.0, Vulkan 1.1, Vulkan 1.1 with SPIR-V 1.4,
OpenCL 2.2, SPIR-V 1.0, SPIR-V 1.1, SPIR-V 1.2, SPIR-V 1.3,
SPIR-V 1.4, or WIP WebGPU validation rules.
--target-env {%s}
Use validation rules from the specified environment.
)",
argv0, argv0);
argv0, argv0, target_env_list.c_str());
}
int main(int argc, char** argv) {

282
3rdparty/spirv-tools/utils/git-sync-deps vendored Executable file
View File

@@ -0,0 +1,282 @@
#!/usr/bin/env python
# Copyright 2014 Google Inc.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are
# met:
#
# * Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above
# copyright notice, this list of conditions and the following disclaimer
# in the documentation and/or other materials provided with the
# distribution.
# * Neither the name of Google Inc. nor the names of its
# contributors may be used to endorse or promote products derived from
# this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
"""Parse a DEPS file and git checkout all of the dependencies.
Args:
An optional list of deps_os values.
Environment Variables:
GIT_EXECUTABLE: path to "git" binary; if unset, will look for one of
['git', 'git.exe', 'git.bat'] in your default path.
GIT_SYNC_DEPS_PATH: file to get the dependency list from; if unset,
will use the file ../DEPS relative to this script's directory.
GIT_SYNC_DEPS_QUIET: if set to non-empty string, suppress messages.
Git Config:
To disable syncing of a single repository:
cd path/to/repository
git config sync-deps.disable true
To re-enable sync:
cd path/to/repository
git config --unset sync-deps.disable
"""
import os
import re
import subprocess
import sys
import threading
from builtins import bytes
def git_executable():
"""Find the git executable.
Returns:
A string suitable for passing to subprocess functions, or None.
"""
envgit = os.environ.get('GIT_EXECUTABLE')
searchlist = ['git', 'git.exe', 'git.bat']
if envgit:
searchlist.insert(0, envgit)
with open(os.devnull, 'w') as devnull:
for git in searchlist:
try:
subprocess.call([git, '--version'], stdout=devnull)
except (OSError,):
continue
return git
return None
DEFAULT_DEPS_PATH = os.path.normpath(
os.path.join(os.path.dirname(__file__), os.pardir, 'DEPS'))
def usage(deps_file_path = None):
sys.stderr.write(
'Usage: run to grab dependencies, with optional platform support:\n')
sys.stderr.write(' %s %s' % (sys.executable, __file__))
if deps_file_path:
parsed_deps = parse_file_to_dict(deps_file_path)
if 'deps_os' in parsed_deps:
for deps_os in parsed_deps['deps_os']:
sys.stderr.write(' [%s]' % deps_os)
sys.stderr.write('\n\n')
sys.stderr.write(__doc__)
def git_repository_sync_is_disabled(git, directory):
try:
disable = subprocess.check_output(
[git, 'config', 'sync-deps.disable'], cwd=directory)
return disable.lower().strip() in ['true', '1', 'yes', 'on']
except subprocess.CalledProcessError:
return False
def is_git_toplevel(git, directory):
"""Return true iff the directory is the top level of a Git repository.
Args:
git (string) the git executable
directory (string) the path into which the repository
is expected to be checked out.
"""
try:
toplevel = subprocess.check_output(
[git, 'rev-parse', '--show-toplevel'], cwd=directory).strip()
return os.path.realpath(bytes(directory, 'utf8')) == os.path.realpath(toplevel)
except subprocess.CalledProcessError:
return False
def status(directory, checkoutable):
def truncate(s, length):
return s if len(s) <= length else s[:(length - 3)] + '...'
dlen = 36
directory = truncate(directory, dlen)
checkoutable = truncate(checkoutable, 40)
sys.stdout.write('%-*s @ %s\n' % (dlen, directory, checkoutable))
def git_checkout_to_directory(git, repo, checkoutable, directory, verbose):
"""Checkout (and clone if needed) a Git repository.
Args:
git (string) the git executable
repo (string) the location of the repository, suitable
for passing to `git clone`.
checkoutable (string) a tag, branch, or commit, suitable for
passing to `git checkout`
directory (string) the path into which the repository
should be checked out.
verbose (boolean)
Raises an exception if any calls to git fail.
"""
if not os.path.isdir(directory):
subprocess.check_call(
[git, 'clone', '--quiet', repo, directory])
if not is_git_toplevel(git, directory):
# if the directory exists, but isn't a git repo, you will modify
# the parent repostory, which isn't what you want.
sys.stdout.write('%s\n IS NOT TOP-LEVEL GIT DIRECTORY.\n' % directory)
return
# Check to see if this repo is disabled. Quick return.
if git_repository_sync_is_disabled(git, directory):
sys.stdout.write('%s\n SYNC IS DISABLED.\n' % directory)
return
with open(os.devnull, 'w') as devnull:
# If this fails, we will fetch before trying again. Don't spam user
# with error infomation.
if 0 == subprocess.call([git, 'checkout', '--quiet', checkoutable],
cwd=directory, stderr=devnull):
# if this succeeds, skip slow `git fetch`.
if verbose:
status(directory, checkoutable) # Success.
return
# If the repo has changed, always force use of the correct repo.
# If origin already points to repo, this is a quick no-op.
subprocess.check_call(
[git, 'remote', 'set-url', 'origin', repo], cwd=directory)
subprocess.check_call([git, 'fetch', '--quiet'], cwd=directory)
subprocess.check_call([git, 'checkout', '--quiet', checkoutable], cwd=directory)
if verbose:
status(directory, checkoutable) # Success.
def parse_file_to_dict(path):
dictionary = {}
contents = open(path).read()
# Need to convert Var() to vars[], so that the DEPS is actually Python. Var()
# comes from Autoroller using gclient which has a slightly different DEPS
# format.
contents = re.sub(r"Var\((.*?)\)", r"vars[\1]", contents)
exec(contents, dictionary)
return dictionary
def git_sync_deps(deps_file_path, command_line_os_requests, verbose):
"""Grab dependencies, with optional platform support.
Args:
deps_file_path (string) Path to the DEPS file.
command_line_os_requests (list of strings) Can be empty list.
List of strings that should each be a key in the deps_os
dictionary in the DEPS file.
Raises git Exceptions.
"""
git = git_executable()
assert git
deps_file_directory = os.path.dirname(deps_file_path)
deps_file = parse_file_to_dict(deps_file_path)
dependencies = deps_file['deps'].copy()
os_specific_dependencies = deps_file.get('deps_os', dict())
if 'all' in command_line_os_requests:
for value in list(os_specific_dependencies.values()):
dependencies.update(value)
else:
for os_name in command_line_os_requests:
# Add OS-specific dependencies
if os_name in os_specific_dependencies:
dependencies.update(os_specific_dependencies[os_name])
for directory in dependencies:
for other_dir in dependencies:
if directory.startswith(other_dir + '/'):
raise Exception('%r is parent of %r' % (other_dir, directory))
list_of_arg_lists = []
for directory in sorted(dependencies):
if '@' in dependencies[directory]:
repo, checkoutable = dependencies[directory].split('@', 1)
else:
raise Exception("please specify commit or tag")
relative_directory = os.path.join(deps_file_directory, directory)
list_of_arg_lists.append(
(git, repo, checkoutable, relative_directory, verbose))
multithread(git_checkout_to_directory, list_of_arg_lists)
for directory in deps_file.get('recursedeps', []):
recursive_path = os.path.join(deps_file_directory, directory, 'DEPS')
git_sync_deps(recursive_path, command_line_os_requests, verbose)
def multithread(function, list_of_arg_lists):
# for args in list_of_arg_lists:
# function(*args)
# return
threads = []
for args in list_of_arg_lists:
thread = threading.Thread(None, function, None, args)
thread.start()
threads.append(thread)
for thread in threads:
thread.join()
def main(argv):
deps_file_path = os.environ.get('GIT_SYNC_DEPS_PATH', DEFAULT_DEPS_PATH)
verbose = not bool(os.environ.get('GIT_SYNC_DEPS_QUIET', False))
if '--help' in argv or '-h' in argv:
usage(deps_file_path)
return 1
git_sync_deps(deps_file_path, argv, verbose)
# subprocess.check_call(
# [sys.executable,
# os.path.join(os.path.dirname(deps_file_path), 'bin', 'fetch-gn')])
return 0
if __name__ == '__main__':
exit(main(sys.argv[1:]))

31
3rdparty/spirv-tools/utils/roll_deps.sh vendored Executable file
View File

@@ -0,0 +1,31 @@
#!/usr/bin/env bash
# Copyright (c) 2019 Google Inc.
#
# 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.
# Attempts to roll all entries in DEPS to origin/master and creates a
# commit.
#
# Depends on roll-dep from depot_path being in PATH.
# 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}"