Add fuzzer pass to obfuscate constants. (#2671)

Adds a new transformation that can replace a constant with a uniform known to have the same value, and adds a fuzzer pass that (a) replaces a boolean with a comparison of literals (e.g. replacing "true" with "42 > 24"), and then (b) obfuscates the literals appearing in this comparison by replacing them with identically-valued uniforms, if available.

The fuzzer_replayer test file has also been updated to allow initial facts to be provided, and to do error checking of the status results returned by the fuzzer and replayer components.
This commit is contained in:
Alastair Donaldson 2019-06-18 18:41:08 +01:00 committed by GitHub
parent 2090d7a2d2
commit 001e823b65
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
20 changed files with 3866 additions and 12 deletions

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,6 +174,31 @@ 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(
@ -236,6 +266,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

@ -133,6 +133,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 +224,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 +247,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,227 @@
// 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"
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;
// The first input operand is the id of the uniform variable.
operands_for_access_chain.push_back(
{SPV_OPERAND_TYPE_ID,
{message.uniform_descriptor().uniform_variable_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

@ -20,14 +20,17 @@ 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_replace_constant_with_uniform_test.cpp
transformation_split_block_test.cpp)
add_spvtools_unittest(TARGET fuzz

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"
@ -529,6 +531,101 @@ 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(9, {0});
auto uniform_buffer_element_descriptor_d =
MakeUniformBufferElementDescriptor(9, {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));
}
}
} // 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(14, {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(14, {0, 1})));
// buf.s.z == 300
ASSERT_TRUE(AddFactHelper(&fact_manager, context.get(), 300,
MakeUniformBufferElementDescriptor(14, {0, 2})));
// buf.s.w == 400
ASSERT_TRUE(AddFactHelper(&fact_manager, context.get(), 400,
MakeUniformBufferElementDescriptor(14, {0, 3})));
// buf.w[6] = 22
ASSERT_TRUE(AddFactHelper(&fact_manager, context.get(), 22,
MakeUniformBufferElementDescriptor(14, {1, 6})));
// buf.w[8] = 23
ASSERT_TRUE(AddFactHelper(&fact_manager, context.get(), 23,
MakeUniformBufferElementDescriptor(14, {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(24, {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(24, {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