1*03ce13f7SAndroid Build Coastguard Worker# Guide to writing a spirv-fuzz fuzzer pass 2*03ce13f7SAndroid Build Coastguard Worker 3*03ce13f7SAndroid Build Coastguard WorkerWriting a spirv-fuzz fuzzer pass usually requires two main contributions: 4*03ce13f7SAndroid Build Coastguard Worker 5*03ce13f7SAndroid Build Coastguard Worker- A *transformation*, capturing a small semantics-preserving change that can be made to a SPIR-V module. This requires adding a protobuf message representing the transformation, and a corresponding class that implements the `Transformation` interface. 6*03ce13f7SAndroid Build Coastguard Worker- A new *fuzzer pass* class, implementing the `FuzzerPass` interface, that knows how to walk a SPIR-V module and apply the new transformation in a randomized fashion. 7*03ce13f7SAndroid Build Coastguard Worker 8*03ce13f7SAndroid Build Coastguard WorkerIn some cases, more than one kind of transformation is required for a single fuzzer pass, and in some cases the transformations that a new fuzzer pass requires have already been introduced by existing passes. But the most common case is to introduce a transformation and fuzzer pass together. 9*03ce13f7SAndroid Build Coastguard Worker 10*03ce13f7SAndroid Build Coastguard WorkerAs an example, let's consider the `TransformationSetSelectionControl` transformation. In SPIR-V, an `OpSelectionMerge` instruction (which intuitively indicates the start of an `if` or `switch` statement in a function) has a *selection control* mask, that can be one of `None`, `Flatten` or `DontFlatten`. The details of these do not matter much for this little tutorial, but in brief, this parameter provides a hint to the shader compiler as to whether it would be profitable to attempt to flatten a piece of conditional code so that all of its statements are executed in a predicated fashion. 11*03ce13f7SAndroid Build Coastguard Worker 12*03ce13f7SAndroid Build Coastguard WorkerAs the selection control mask is just a hint, changing the value of this mask should have no semantic impact on the module. The `TransformationSelectionControl` transformation specifies a new value for a given selection control mask. 13*03ce13f7SAndroid Build Coastguard Worker 14*03ce13f7SAndroid Build Coastguard Worker## Adding a new protobuf message 15*03ce13f7SAndroid Build Coastguard Worker 16*03ce13f7SAndroid Build Coastguard WorkerTake a look at the `Transformation` message in `spvtoolsfuzz.proto`. This has a `oneof` field that can be any one of the different spirv-fuzz transformations. Observe that one of the options is `TransformationSetSelectionControl`. When adding a transformation you first need to add an option for your transformation to the end of the `oneof` declaration. 17*03ce13f7SAndroid Build Coastguard Worker 18*03ce13f7SAndroid Build Coastguard WorkerNow look at the `TransformationSetSelectionControl` message. If adding your own transformation you need to add a new message for your transformation, and it should be placed alphabetically with respect to other transformations. 19*03ce13f7SAndroid Build Coastguard Worker 20*03ce13f7SAndroid Build Coastguard WorkerThe fields of `TransformationSetSelectionControl` provide just enough information to (a) determine whether a given example of this transformation is actually applicable, and (b) apply the transformation in the case that it is applicable. The details of the transformation message will vary a lot between transformations. In this case, the message has a `block_id` field, specifying a block that must end with `OpSelectionMerge`, and a `selection_control` field, which is the new value for the selection control mask of the `OpSelectionMerge` instruction. 21*03ce13f7SAndroid Build Coastguard Worker 22*03ce13f7SAndroid Build Coastguard Worker## Adding a new transformation class 23*03ce13f7SAndroid Build Coastguard Worker 24*03ce13f7SAndroid Build Coastguard WorkerIf your transformation is called `TransformationSomeThing`, you need to add `transformation_some_thing.h` and `transformation_some_thing.cpp` to `source/fuzz` and the corresponding `CMakeLists.txt` file. So for `TransformationSetSelectionControl` we have `transformation_selection_control.h` and `transformation_selection_control.cpp`, and we will use this as an example to illustrate the expected contents of these files. 25*03ce13f7SAndroid Build Coastguard Worker 26*03ce13f7SAndroid Build Coastguard WorkerThe header file contains the specification of a class, `TransformationSetSelectionControl`, that implements the `Transformation` interface (from `transformation.h`). 27*03ce13f7SAndroid Build Coastguard Worker 28*03ce13f7SAndroid Build Coastguard WorkerA transformation class should always have a single field, which should be the associated protobuf message; in our case: 29*03ce13f7SAndroid Build Coastguard Worker 30*03ce13f7SAndroid Build Coastguard Worker``` 31*03ce13f7SAndroid Build Coastguard Worker private: 32*03ce13f7SAndroid Build Coastguard Worker protobufs::TransformationSetSelectionControl message_; 33*03ce13f7SAndroid Build Coastguard Worker``` 34*03ce13f7SAndroid Build Coastguard Worker 35*03ce13f7SAndroid Build Coastguard Workerand two public constructors, one that takes a protobuf message; in our case: 36*03ce13f7SAndroid Build Coastguard Worker 37*03ce13f7SAndroid Build Coastguard Worker``` 38*03ce13f7SAndroid Build Coastguard Worker explicit TransformationSetSelectionControl( 39*03ce13f7SAndroid Build Coastguard Worker const protobufs::TransformationSetSelectionControl& message); 40*03ce13f7SAndroid Build Coastguard Worker``` 41*03ce13f7SAndroid Build Coastguard Worker 42*03ce13f7SAndroid Build Coastguard Workerand one that takes a parameter for each protobuf message field; in our case: 43*03ce13f7SAndroid Build Coastguard Worker 44*03ce13f7SAndroid Build Coastguard Worker``` 45*03ce13f7SAndroid Build Coastguard Worker TransformationSetSelectionControl(uint32_t block_id); 46*03ce13f7SAndroid Build Coastguard Worker``` 47*03ce13f7SAndroid Build Coastguard Worker 48*03ce13f7SAndroid Build Coastguard WorkerThe first constructor allows an instance of the class to be created from a corresponding protobuf message. The second should provide the ingredients necessary to populate a protobuf message. 49*03ce13f7SAndroid Build Coastguard Worker 50*03ce13f7SAndroid Build Coastguard WorkerThe class should also override the `IsApplicable`, `Apply` and `ToMessage` methods from `Transformation`. 51*03ce13f7SAndroid Build Coastguard Worker 52*03ce13f7SAndroid Build Coastguard WorkerSee `transformation_set_selection_control.h` for an example. 53*03ce13f7SAndroid Build Coastguard Worker 54*03ce13f7SAndroid Build Coastguard WorkerThe `IsApplicable` method should have a comment in the header file describing the conditions for applicability in simple terms. These conditions should be implemented in the body of this method in the `.cpp` file. 55*03ce13f7SAndroid Build Coastguard Worker 56*03ce13f7SAndroid Build Coastguard WorkerIn the case of `TransformationSetSelectionControl`, `IsApplicable` involves checking that `block_id` is indeed the id of a block that has an `OpSelectionMerge` instruction, and that `selection_control` is a valid selection mask. 57*03ce13f7SAndroid Build Coastguard Worker 58*03ce13f7SAndroid Build Coastguard WorkerThe `Apply` method should have a comment in the header file summarising the result of applying the transformation. It should be implemented in the `.cpp` file, and you should assume that `IsApplicable` holds whenever `Apply` is invoked. 59*03ce13f7SAndroid Build Coastguard Worker 60*03ce13f7SAndroid Build Coastguard Worker## Writing tests for the transformation class 61*03ce13f7SAndroid Build Coastguard Worker 62*03ce13f7SAndroid Build Coastguard WorkerWhenever you add a transformation class, `TransformationSomeThing`, you should add an associated test file, `transformation_some_thing_test.cpp`, under `test/fuzz`, adding it to the associated `CMakeLists.txt` file. 63*03ce13f7SAndroid Build Coastguard Worker 64*03ce13f7SAndroid Build Coastguard WorkerFor example `test/fuzz/transformation_set_selection_control_test.cpp` contains tests for `TransformationSetSelectionControl`. Your tests should aim to cover one example from each scenario where the transformation is inapplicable, and check that it is indeed deemed inapplicable, and then check that the transformation does the right thing when applied in a few different ways. 65*03ce13f7SAndroid Build Coastguard Worker 66*03ce13f7SAndroid Build Coastguard WorkerFor example, the tests for `TransformationSetSelectionControl` check that a transformation of this kind is inapplicable if the `block_id` field of the transformation is not a block, or does not end in `OpSelectionMerge`, or if the `selection_control` mask has an illegal value. It also checks that applying a sequence of valid transformations to a SPIR-V shader leads to a shader with appropriately modified selection controls. 67*03ce13f7SAndroid Build Coastguard Worker 68*03ce13f7SAndroid Build Coastguard Worker## Adding a new fuzzer pass class 69*03ce13f7SAndroid Build Coastguard Worker 70*03ce13f7SAndroid Build Coastguard WorkerA *fuzzer pass* traverses a SPIR-V module looking for places to apply a certain kind of transformation, and randomly decides at which of these points to actually apply the transformation. It might be necessary to apply other transformations in order to apply a given transformation (for example, if a transformation requires a certain type to be present in the module, said type can be added if not already present via another transformation). 71*03ce13f7SAndroid Build Coastguard Worker 72*03ce13f7SAndroid Build Coastguard WorkerA fuzzer pass implements the `FuzzerPass` interface, and overrides its `Apply` method. If your fuzzer pass is named `FuzzerPassSomeThing` then it should be represented by `fuzzer_pass_some_thing.h` and `fuzzer_pass_some_thing.cpp`, under `source/fuzz`; these should be added to the associated `CMakeLists.txt` file. 73*03ce13f7SAndroid Build Coastguard Worker 74*03ce13f7SAndroid Build Coastguard WorkerHave a look at the source filed for `FuzzerPassAdjustSelectionControls`. This pass considers every block that ends with `OpSelectionMerge`. It decides randomly whether to adjust the selection control of this merge instruction via: 75*03ce13f7SAndroid Build Coastguard Worker 76*03ce13f7SAndroid Build Coastguard Worker``` 77*03ce13f7SAndroid Build Coastguard Workerif (!GetFuzzerContext()->ChoosePercentage( 78*03ce13f7SAndroid Build Coastguard Worker GetFuzzerContext()->GetChanceOfAdjustingSelectionControl())) { 79*03ce13f7SAndroid Build Coastguard Worker continue; 80*03ce13f7SAndroid Build Coastguard Worker} 81*03ce13f7SAndroid Build Coastguard Worker``` 82*03ce13f7SAndroid Build Coastguard Worker 83*03ce13f7SAndroid Build Coastguard WorkerThe `GetChanceOfAddingSelectionControl()` method has been added to `FuzzerContext` specifically to support this pass, and returns a percentage between 0 and 100. It returns the `chance_of_adjusting_selection_control_` of `FuzzerContext`, which is randomly initialized to lie with the interval defined by `kChanceOfAdjustingSelectionControl` in `fuzzer_context.cpp`. For any pass you write, you will need to add an analogous `GetChanceOf...` method to `FuzzerContext`, backed by an appropriate field, and you will need to decide on lower and upper bounds for this field and specify these via a `kChanceOf...` constant. 84