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.
* Validate that if a construct contains a header and it's merge is
reachable, the construct also contains the merge
* updated block merging to not merge into the continue
* update inlining to mark the original block of a single block loop as
the continue
* updated some tests
* remove dead code
* rename kBlockTypeHeader to kBlockTypeSelection for clarity
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.
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.
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.
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.
A previous change that disabled long-running tests by default failed
to enable short-running tests when long-running tests are enabled.
This change fixes that problem.
To aid in debugging issues in spirv-fuzz, this change adds an option whereby the SPIR-V module is validated after each transformation is applied during replay. This can assist in finding a transformation that erroneously makes the module invalid, so that said transformation can be debugged.
spirv-fuzz has useful tests that run the fuzzer and shrinker, to give
the whole tool a good shake up, effectively "fuzzing the fuzzer". The
problems that this detects are sensitive to the source of randomness
that is used, which can change from test platform to test platform.
It is thus not a good idea to run these tests by default during
continuous integration - they may end up failing due to environtal
factors, making it look like an unrelated change has broken the fuzzer
when really the fuzzer has revealed an already-existing bug in itself.
This change makes the tests disabled by default; they can enabled
during dedicated testing of the fuzzer.
If the fuzzer's fact manager knows that ids A and B are synonymous, it
can replace a use of A with a use of B, so long as various conditions
hold (e.g. the definition of B must dominate the use of A, and it is
not legal to replace a use of an OpConstant in a struct's access chain
with a synonym that is not an OpConstant).
This change adds a fuzzer pass to sprinke such synonym replacements
through the module.
The implementation of these passes had overlooked the fact that adding
a new edge to a control flow graph can change dominance information.
Adding a dead break/continue risks causing uses to no longer be
dominated by their definitions. This change introduces various tests
to expose such scenarios, and augments the preconditions for these
transformations with checks to guard against the situation.
This transformation can introduce an instruction that uses
OpCopyObject to make a copy of some other result id. This change
introduces the transformation, but does not yet introduce a fuzzer
pass to actually apply it.
The recently added fuzzer_replayer and fuzzer_shrinker tests were
rather heavyweight and were leading to CI timeouts. This change
reduces the runtime of those tests by having them do fewer iterations.
Similar to the existing 'add dead breaks' pass, this adds a pass to
add dead continues to blocks in loops where such a transformation is
viable. Various functionality common to this new pass and 'add dead
breaks' has been factored into 'fuzzer_util', and some small
improvements to 'add dead breaks' that were identified while reviewing
that code again have been applied.
Fixes#2719.
Adds to spirv-fuzz the option to shrink a sequence of transformations
that lead to an interesting binary to be generated, to find a smaller
sub-sequence of transformations that still lead to an interesting (but
hopefully simpler) binary being generated. The notion of what counts
as "interesting" comes from a user-provided script, the
"interestingness function", similar to the way the spirv-reduce tool
works. The shrinking process will give up after a maximum number of
steps, which can be configured on the command line.
Tests for the combination of fuzzing and shrinking are included, using
a variety of interestingness functions.
Fixes#2695. Allowing unreachable blocks to be moved can lead to an
unreachable block A getting placed after an unreachable successor B,
which is a problem if B uses ids that A generates.
* Represent uniform facts via descriptor set and binding.
Previously uniform facts were expressed with resepect to the id of a
uniform variable. Describing them with respect to a descriptor set
and binding is more convenient from the point of view of expressing
facts about a shader without requiring analysis of its SPIR-V.
* Fix equality testing for uniform buffer element descriptors.
The equality test now checks that the lengths of the index vectors
match. Added a test that exposes the previous omission.