1 // Copyright (c) 2019 Google LLC 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 #ifndef SOURCE_FUZZ_TRANSFORMATION_H_ 16 #define SOURCE_FUZZ_TRANSFORMATION_H_ 17 18 #include <memory> 19 #include <unordered_set> 20 21 #include "source/fuzz/protobufs/spirvfuzz_protobufs.h" 22 #include "source/fuzz/transformation_context.h" 23 #include "source/opt/ir_context.h" 24 25 namespace spvtools { 26 namespace fuzz { 27 28 // Rules for transformations 29 // ------------------------- 30 // 31 // - Immutability: a transformation must be immutable. 32 // - Ability to copy and serialize: to ensure that a copy of a transformation, 33 // possibly saved out to disk and read back again, is indistinguishable 34 // from the original transformation, thus a transformation must depend 35 // only on well-defined pieces of state, such as instruction ids. It must 36 // not rely on state such as pointers to instructions and blocks. 37 // - Determinism: the effect of a transformation on a module be a deterministic 38 // function of the module and the transformation. Any randomization should 39 // be applied before creating the transformation, not during its 40 // application. 41 // - Well-defined and precondition: the 'IsApplicable' method should only 42 // return true if the transformation can be cleanly applied to the given 43 // module, to mutate it into a valid and semantically-equivalent module, as 44 // long as the module is initially valid. 45 // - Ability to test precondition on any valid module: 'IsApplicable' should be 46 // designed so that it is safe to ask whether a transformation is 47 // applicable to an arbitrary valid module. For example, if a 48 // transformation involves a block id, 'IsApplicable' should check whether 49 // the module indeed has a block with that id, and return false if not. It 50 // must not assume that there is such a block. 51 // - Documented precondition: while the implementation of 'IsApplicable' should 52 // should codify the precondition, the method should be commented in the 53 // header file for a transformation with a precise English description of 54 // the precondition. 55 // - Documented effect: while the implementation of 'Apply' should codify the 56 // effect of the transformation, the method should be commented in the 57 // header file for a transformation with a precise English description of 58 // the effect. 59 60 class Transformation { 61 public: 62 virtual ~Transformation(); 63 64 // Factory method to obtain a transformation object from the protobuf 65 // representation of a transformation given by |message|. 66 static std::unique_ptr<Transformation> FromMessage( 67 const protobufs::Transformation& message); 68 69 // A precondition that determines whether the transformation can be cleanly 70 // applied in a semantics-preserving manner to the SPIR-V module given by 71 // |ir_context|, in the presence of facts and other contextual information 72 // captured by |transformation_context|. 73 // 74 // Preconditions for individual transformations must be documented in the 75 // associated header file using precise English. The transformation context 76 // provides access to facts about the module that are known to be true, on 77 // which the precondition may depend. 78 virtual bool IsApplicable( 79 opt::IRContext* ir_context, 80 const TransformationContext& transformation_context) const = 0; 81 82 // Requires that IsApplicable(ir_context, *transformation_context) holds. 83 // Applies the transformation, mutating |ir_context| and possibly updating 84 // |transformation_context| with new facts established by the transformation. 85 virtual void Apply(opt::IRContext* ir_context, 86 TransformationContext* transformation_context) const = 0; 87 88 // Returns the set of fresh ids that appear in the transformation's protobuf 89 // message. 90 virtual std::unordered_set<uint32_t> GetFreshIds() const = 0; 91 92 // Turns the transformation into a protobuf message for serialization. 93 virtual protobufs::Transformation ToMessage() const = 0; 94 95 // Helper that returns true if and only if (a) |id| is a fresh id for the 96 // module, and (b) |id| is not in |ids_used_by_this_transformation|, a set of 97 // ids already known to be in use by a transformation. This is useful when 98 // checking id freshness for a transformation that uses many ids, all of which 99 // must be distinct. 100 static bool CheckIdIsFreshAndNotUsedByThisTransformation( 101 uint32_t id, opt::IRContext* ir_context, 102 std::set<uint32_t>* ids_used_by_this_transformation); 103 }; 104 105 } // namespace fuzz 106 } // namespace spvtools 107 108 #endif // SOURCE_FUZZ_TRANSFORMATION_H_ 109