| // Copyright (c) 2020 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_OVERFLOW_ID_SOURCE_H_ |
| #define SOURCE_FUZZ_OVERFLOW_ID_SOURCE_H_ |
| |
| #include <cstdint> |
| #include <unordered_set> |
| |
| namespace spvtools { |
| namespace fuzz { |
| |
| // An implementation of this interface can be used to provide fresh ids on |
| // demand when applying a transformation. |
| // |
| // During fuzzing this should never be required: a fuzzer pass should determine |
| // all the fresh ids it requires to apply a transformation. |
| // |
| // However, during shrinking we can have the situation where, after removing |
| // an early transformation, a later transformation needs more ids. |
| // |
| // As an example, suppose a SPIR-V function originally has this form: |
| // |
| // main() { |
| // stmt1; |
| // stmt2; |
| // stmt3; |
| // stmt4; |
| // } |
| // |
| // Now suppose two *outlining* transformations are applied. The first |
| // transformation, T1, outlines "stmt1; stmt2;" into a function foo, giving us: |
| // |
| // foo() { |
| // stmt1; |
| // stmt2; |
| // } |
| // |
| // main() { |
| // foo(); |
| // stmt3; |
| // stmt4; |
| // } |
| // |
| // The second transformation, T2, outlines "foo(); stmt3;" from main into a |
| // function bar, giving us: |
| // |
| // foo() { |
| // stmt1; |
| // stmt2; |
| // } |
| // |
| // bar() { |
| // foo(); |
| // stmt3; |
| // } |
| // |
| // main() { |
| // bar(); |
| // stmt4; |
| // } |
| // |
| // Suppose that T2 used a set of fresh ids, FRESH, in order to perform its |
| // outlining. |
| // |
| // Now suppose that during shrinking we remove T1, but still want to apply T2. |
| // The fresh ids used by T2 - FRESH - are sufficient to outline "foo(); stmt3;". |
| // However, because we did not apply T1, "foo();" does not exist and instead the |
| // task of T2 is to outline "stmt1; stmt2; stmt3;". The set FRESH contains |
| // *some* of the fresh ids required to do this (those for "stmt3;"), but not all |
| // of them (those for "stmt1; stmt2;" are missing). |
| // |
| // A source of overflow ids can be used to allow the shrinker to proceed |
| // nevertheless. |
| // |
| // It is desirable to use overflow ids only when needed. In our worked example, |
| // T2 should still use the ids from FRESH when handling "stmt3;", because later |
| // transformations might refer to those ids and will become inapplicable if |
| // overflow ids are used instead. |
| class OverflowIdSource { |
| public: |
| virtual ~OverflowIdSource(); |
| |
| // Returns true if and only if this source is capable of providing overflow |
| // ids. |
| virtual bool HasOverflowIds() const = 0; |
| |
| // Precondition: HasOverflowIds() must hold. Returns the next available |
| // overflow id. |
| virtual uint32_t GetNextOverflowId() = 0; |
| |
| // Returns the set of overflow ids from this source that have been previously |
| // issued via calls to GetNextOverflowId(). |
| virtual const std::unordered_set<uint32_t>& GetIssuedOverflowIds() const = 0; |
| }; |
| |
| } // namespace fuzz |
| } // namespace spvtools |
| |
| #endif // SOURCE_FUZZ_OVERFLOW_ID_SOURCE_H_ |