SPIRV-Tools/source/fuzz/transformation.h
Alastair Donaldson fcb22ecf0f
spirv-fuzz: Report fresh ids in transformations (#3856)
Adds a virtual method, GetFreshIds(), to Transformation. Every
transformation uses this to indicate which ids in its protobuf message
are fresh ids. This means that when replaying a sequence of
transformations the replayer can obtain a smallest id that is not in
use by the module already and that will not be used by any
transformation by necessity. Ids greater than or equal to this id
can be used as overflow ids.

Fixes #3851.
2020-09-29 22:12:49 +01:00

109 lines
5.0 KiB
C++

// Copyright (c) 2019 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_TRANSFORMATION_H_
#define SOURCE_FUZZ_TRANSFORMATION_H_
#include <memory>
#include <unordered_set>
#include "source/fuzz/protobufs/spirvfuzz_protobufs.h"
#include "source/fuzz/transformation_context.h"
#include "source/opt/ir_context.h"
namespace spvtools {
namespace fuzz {
// Rules for transformations
// -------------------------
//
// - Immutability: a transformation must be immutable.
// - Ability to copy and serialize: to ensure that a copy of a transformation,
// possibly saved out to disk and read back again, is indistinguishable
// from the original transformation, thus a transformation must depend
// only on well-defined pieces of state, such as instruction ids. It must
// not rely on state such as pointers to instructions and blocks.
// - Determinism: the effect of a transformation on a module be a deterministic
// function of the module and the transformation. Any randomization should
// be applied before creating the transformation, not during its
// application.
// - Well-defined and precondition: the 'IsApplicable' method should only
// return true if the transformation can be cleanly applied to the given
// module, to mutate it into a valid and semantically-equivalent module, as
// long as the module is initially valid.
// - Ability to test precondition on any valid module: 'IsApplicable' should be
// designed so that it is safe to ask whether a transformation is
// applicable to an arbitrary valid module. For example, if a
// transformation involves a block id, 'IsApplicable' should check whether
// the module indeed has a block with that id, and return false if not. It
// must not assume that there is such a block.
// - Documented precondition: while the implementation of 'IsApplicable' should
// should codify the precondition, the method should be commented in the
// header file for a transformation with a precise English description of
// the precondition.
// - Documented effect: while the implementation of 'Apply' should codify the
// effect of the transformation, the method should be commented in the
// header file for a transformation with a precise English description of
// the effect.
class Transformation {
public:
virtual ~Transformation();
// Factory method to obtain a transformation object from the protobuf
// representation of a transformation given by |message|.
static std::unique_ptr<Transformation> FromMessage(
const protobufs::Transformation& message);
// A precondition that determines whether the transformation can be cleanly
// applied in a semantics-preserving manner to the SPIR-V module given by
// |ir_context|, in the presence of facts and other contextual information
// captured by |transformation_context|.
//
// Preconditions for individual transformations must be documented in the
// associated header file using precise English. The transformation context
// provides access to facts about the module that are known to be true, on
// which the precondition may depend.
virtual bool IsApplicable(
opt::IRContext* ir_context,
const TransformationContext& transformation_context) const = 0;
// Requires that IsApplicable(ir_context, *transformation_context) holds.
// Applies the transformation, mutating |ir_context| and possibly updating
// |transformation_context| with new facts established by the transformation.
virtual void Apply(opt::IRContext* ir_context,
TransformationContext* transformation_context) const = 0;
// Returns the set of fresh ids that appear in the transformation's protobuf
// message.
virtual std::unordered_set<uint32_t> GetFreshIds() const = 0;
// Turns the transformation into a protobuf message for serialization.
virtual protobufs::Transformation ToMessage() const = 0;
// Helper that returns true if and only if (a) |id| is a fresh id for the
// module, and (b) |id| is not in |ids_used_by_this_transformation|, a set of
// ids already known to be in use by a transformation. This is useful when
// checking id freshness for a transformation that uses many ids, all of which
// must be distinct.
static bool CheckIdIsFreshAndNotUsedByThisTransformation(
uint32_t id, opt::IRContext* ir_context,
std::set<uint32_t>* ids_used_by_this_transformation);
};
} // namespace fuzz
} // namespace spvtools
#endif // SOURCE_FUZZ_TRANSFORMATION_H_