This change adds fuzzer passes that sprinkle loads and stores into a
module at random, with stores restricted to occur in either dead
blocks, or to use pointers for which it is known that the pointee
value does not influence the module's overall behaviour.
The change also generalises the VariableValueIsArbitrary fact to
PointeeValueIsIrrelevant, to allow stores through access chains or
object copies of variables whose values are known to be irrelevant.
The change includes some other minor refactorings.
Adds two new fuzzer passes to add variables to a module: one that adds
Private storage class global variables, another that adds Function
storage class local variables.
Adds a fuzzer pass that randomly adds vector and matrix types not
already present in the module, and randomly adds structs with random
field types and arrays with random base types and sizes. Other passes
will be able to create variables and ids using these types.
If the fuzzer object-copies a pointer we would like to be able to
perform loads from the copy (and stores to it, if its value is known
not to matter). Undefined and null pointers present a problem here,
so this change disallows copying them.
This change adds a new kind of fact to the fact manager, which records
when a variable (or pointer parameter) refers to an arbitrary value,
so that anything can be stored to it, without affecting the observable
behaviour of the module, and nothing can be guaranteed about values
loaded from it. Donated modules are the current source of such
variables, and other transformations, such as outlining, have been
adapted to propagate these facts appropriately.
This change allows the generator to (optionally and at random) make
the functions of a module "livesafe" during donation. This involves
introducing a loop limiter variable to each function and gating the
number of total loop iterations for the function using that variable.
It also involves eliminating OpKill and OpUnreachable instructions
(changing them to OpReturn/OpReturnValue), and clamping access chain
indices so that they are always in-bounds.
This change refactors some code for walking access chain indexes to
make it mirror the structure of other code (to improve readability in
the first instance and potentially enable a future refactoring to
extract common code), and fixes a problem related to module donation
and function types.
This adds a new kind of fact to the fact manager that knows whether a
block is dead - i.e. guaranteed to be statically unreachable - and a
new transformation for adding a selection construct to a CFG that
conditionally branches to a fresh, dead block, such that the branch
will never be dynamically taken. Transformations that may create new
blocks ('split block' and 'outline function') are updated to propagate
dead block facts to newly-created blocks where appropriate. A fuzzer
pass randomly adds dead blocks to the module.
Future transformations will be able to exploit the fact that such
blocks are known to be dead.
This change adds a fuzzer pass that allows code from other SPIR-V
modules to be donated into the module under transformation. It also
changes the command-line options of the tools so that, in fuzzing
mode, a file must be specified that contains the names of available
donor modules.
In the context of SPIR-V 1.4 or higher, global variables cannot be
used by an instruction unless they are listed in the interface of all
entry points that might invoke the instruction. This change
conservatively adds new global variables to the interfaces of all
entry points (if the SPIR-V version is 1.4 or higher).
Issue #3111 notes that a more rigorous approach to entry point
interfaces could be taken in spirv-fuzz, which would allow being less
conservative here.
This change prevents the spirv-fuzz function outliner from outlining a
region that uses the result of an OpAccessChain not defined inside the
region. Such accesses were turning into parameters to the outlined
function, and the result of an OpAccessChain cannot be passed as a
function parameter according to the SPIR-V specification.
A new transformation and associated fuzzer pass in spirv-fuzz that
selects single-entry single-exit control flow graph regions and for
each selected region outlines the region into a new function and
replaces the original region with a call to this function.
The passes that add dead breaks and continues suffer from the
challenge that a new control flow graph edge can change dominance
information, leading to the potenital for definitions to no longer
dominate their uses. The attempt at guarding against this was known
to be incomplete. This change calls on the SPIR-V validator to do the
necessary checking: in deciding whether adding such an edge would be
legitimate, we clone the module, add the edge, and use the validator
to check whether the transformed clone is valid.
This strategy is heavy-weight, and should be used sparingly, but seems
like a good option when the validity of transformations is intricate,
to avoid reimplementing swathes of validation logic in the fuzzer.
Fixes#2919.
Adds an option to run the validator on the SPIR-V binary after each
fuzzer pass has been applied, to help identify when the fuzzer has
made the module invalid. Also adds a helper method to allow dumping
of the sequence of transformations that have been applied to a JSON
file.
Prior to this change, TransformationReplaceIdWithSynonym was designed
to be able to replace an id with some synonymous data descriptor,
possibly necessitating extracting from a composite into a fresh id in
order to get at the synonymous data. This change simplifies things so
that TransformationReplaceIdWithSynonym just allows one id to be
replaced by another id. It is the responsibility of the associated
fuzzer pass - FuzzerPassApplyIdSynonyms - to perform the extraction
operations, using e.g. TransformationCompositeExtract.
Inroduces a new transformation that adds a vector shuffle instruction
to the module, with associated facts about how the result vector of
the shuffle relates to the input vectors.
A fuzzer pass to add such transformations is not yet in place.
When a data synonym fact about two composites is added, data synonym
facts between all sub-components of the composites are also added.
Furthermore, when data synonym facts been all sub-components of two
composites are known, a data synonym fact relating the two composites
is added. Identification of this case is done in a lazy manner, when
questions about data synonym facts are asked.
The change introduces helper methods to get the size of an array type
and the number of elements of a struct type, and fixes
TransformationCompositeExtract to invalidate analyses appropriately.
An equivalence relation is computed by traversing the tree of values
rooted at the class's representative. Children were represented by
unordered sets, meaning that the order of values in an equivalence
class could be nondeterministic. This change makes things
deterministic by representing children using a vector.
The path compression optimization employed in the implementation of
the underlying union-find data structure has the potential to change
the order in which elements appear in an equivalence class by changing
the structure of the tree, so the guarantee of determinism is limited
to being a deterministic function of the manner in which the
equivalence relation is updated and inspected.
This change fixes a bug in EquivalenceRelation, changes the interface
of EquivalenceRelation to avoid exposing (potentially
nondeterministic) unordered sets, and changes the interface of
FactManager to allow querying data synonyms directly. These interface
changes have required a lot of corresponding changes to client code
and tests.
At present, TransformationReplaceIdWithSynonym both extracts elements
from composite objects and replaces uses of ids with synonyms. This
new TransformationCompositeExtract class will allow that
transformation to be broken into smaller transformations.
Class TransformationConstructComposite has been renamed to
TransformationCompositeConstruct, to correspond to the name of the
SPIR-V instruction (as is done with e.g. TransformationCopyObject).
Running tests revealed an issue related to checking dominance in
TransformationReplaceIdWithSynonym, which is also fixed here.
This change uses the recently-added equivalence relation class to
re-work the way synonyms between data values are managed by the fact
manager.
The tests for 'transformation_replace_id_with_synonym' have been
temporarily removed. This is because those tests are going to be
split into a number of test classes in an upcoming PR, once some other
refactorings have been applied, and it would be burdensome to
temporarily refactor all the tests to be in a working state for this
intermediate change.
Adds a templated class for representing an equivalence relation on a
value data type. This will be used by spirv-fuzz for representing
sets of distinct pieces of data in a shader that are known to have
equal values.
A new pass that gives spirv-fuzz the ability to adjust the memory
operand masks associated with memory access instructions (such as
OpLoad and OpCopy Memory).
Fixes#2940.
Added exports for libraries. External libraries that themselves use
libraries require all dependencies have exports, so not having exports can
cause major problems when used within other projects.
Install paths for exports are now placed in the proper directories expected
by Windows and *nix systems. Config files are generated as well, which
should work with CMake's find_package() function once installed.
This change refactors the 'split blocks' transformation so that an
instruction is identified via a base, opcode, and number of those
opcodes to be skipped when searching from the base, as opposed to the
previous design which used a base and offset.
A refactoring that separates the identification of an instruction from
the identification of a use in an instruction, to enable the former to
be used independently of the latter.
A new pass that allows the fuzzer to change the 'loop control' operand
(and associated literal operands) of OpLoopMerge instructions.
Fixes#2938.
Fixes#2943.
Adds a fuzzer pass and transformation to create a composite (array,
matrix, struct or vector) from available constituent components, and
inform the fact manager that each component of the new composite is
synonymous with the id that was used to construct it. This allows the
"replace id with synonym" pass to then replace uses of said ids with
uses of elements extracted from the composite.
Fixes#2858.
Issue #2919 identifies a problem in spirv-fuzz's ability to determine
when it is safe to add a new control flow edge without breaking
dominance rules. This change adds a (currently disabled) test to
expose the issue, and a comment to document that the current solution
is incomplete.
Adds a spirv-fuzz option for converting a SPIR-V shader into a shader
that renders red, whilst containing the body of the original shader.
This is for aiding in compiler crash bug reporting.
Because dominance information becomes a bit unreliable when blocks are
unreachable, this change makes it so that the 'dead break'
transformation will not introduce a break to an unreachable block.
Fixes#2907.
The performance of the fuzzer was unacceptable in the 'permute blocks'
transformation, due to dominator analysis being repeatedly invalidated
and recomputed. This change preserves the dominator analysis,
together with the CFG analysis, when a block is moved down.
This change introduces a robust check for whether an index in an
access chain is indexing into a struct, in which case the index needs
to be an OpConstant and cannot be replaced with a synonym.
Fixes#2906.
Issues #2898 and #2900 identify some cases where adding a dead
continue would lead to an invalid module, and these turned out to be
due to the lack of sensible dominance information when a continue
target is unreachable. This change requires that the header of a loop
dominates the loop's continue target if a dead continue is to be
added.
Furthermore, issue #2905 identified a shortcoming in the algorithm
being used to identify when it is OK, from a dominance point of view,
to add a new break/continue edge to a control flow graph. This change
replaces that algorithm with a simpler and more obviously correct
algorithm (that incidentally does not require the new edge to be a
break/continue edge in particular).
Fixes#2898.
Fixes#2900.
Fixes#2905.
Before this change, spirv-fuzz would replace a pointer argument to a
function call with a synonym, which is problematic when the synonym is
not a memory object declaration, since function call arguments are
required to be memory object declarations. This change adds a check
to ensure that such a replacement is not made.
Fixes#2896.
Before this change, spirv-fuzz would replace a constant boolean
argument to an OpPhi with the result of a binary operation, inserting
the instruction to compute the binary operation right before the
OpPhi, leading to an invalid module. This change conservatively
disallows replacing OpPhi arguments. Issue #2902 notes that there is
scope for being less conservative.
Fixes#2897.
This change to spirv-fuzz uses ideas from "Swarm Testing" (Groce et al. 2012), so that a random subset of fuzzer passes are enabled. These passes are then applied repeatedly in a randomized fashion, with the aggression with which they are applied being randomly chosen per pass.
There is plenty of scope for refining the probabilities introduce in this change; this is just meant to be a reasonable first effort.