blob: 8f0937c24368b983a9a2b48894f7ce9059b7ca3c [file] [log] [blame]
// 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
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// See the License for the specific language governing permissions and
// limitations under the License.
#include "source/fuzz/transformation.h"
namespace spvtools {
namespace fuzz {
class TransformationMergeFunctionReturns : public Transformation {
explicit TransformationMergeFunctionReturns(
protobufs::TransformationMergeFunctionReturns message);
uint32_t function_id, uint32_t outer_header_id, uint32_t outer_return_id,
uint32_t return_val_id, uint32_t any_returnable_val_id,
const std::vector<protobufs::ReturnMergingInfo>& returns_merging_info);
// - |message_.function_id| is the id of a function.
// - The entry block of |message_.function_id| branches unconditionally to
// another block.
// - |message_.any_returnable_val_id| is an id whose type is the same as the
// return type of the function and which is available at the end of the
// entry block. If this id is not found in the module, the transformation
// will try to find a suitable one.
// If the function is void, or no loops in the function contain return
// statements, this id will be ignored.
// - Merge blocks of reachable loops that contain return statements only
// consist of OpLabel, OpPhi or OpBranch instructions.
// - The model contains OpConstantTrue and OpConstantFalse instructions.
// - For all merge blocks of reachable loops that contain return statements,
// either:
// - a mapping is provided in |message_.return_merging_info|, all of the
// corresponding fresh ids are valid and, for each OpPhi instruction in
// the block, there is a mapping to an available id of the same type in
// |opphi_to_suitable_id| or a suitable id, available at the end of the
// entry block, can be found in the module.
// - there is no mapping, but overflow ids are available and, for every
// OpPhi instruction in the merge blocks that need to be modified, a
// suitable id, available at the end of the entry block, can be found.
// - The addition of new predecessors to the relevant merge blocks does not
// cause any id use to be invalid (i.e. every id must dominate all its uses
// even after the transformation has added new branches).
// - All of the fresh ids that are provided and needed by the transformation
// are valid.
bool IsApplicable(
opt::IRContext* ir_context,
const TransformationContext& transformation_context) const override;
// Changes the function so that there is only one reachable return
// instruction. The function is enclosed by an outer loop, whose merge block
// is the new return block. All existing return statements are replaced by
// branch instructions to the merge block of the loop enclosing them, and
// OpPhi instructions are used to keep track of the return value and of
// whether the function is returning.
void Apply(opt::IRContext* ir_context,
TransformationContext* transformation_context) const override;
std::unordered_set<uint32_t> GetFreshIds() const override;
protobufs::Transformation ToMessage() const override;
// Returns a map from merge block ids to the corresponding info in
// |message_.return_merging_info|.
std::map<uint32_t, protobufs::ReturnMergingInfo>
GetMappingOfMergeBlocksToInfo() const;
// Returns a map from type ids to an id with that type and which is available
// at the end of the entry block of |message_.function_id|.
// Assumes that the function exists.
std::map<uint32_t, uint32_t> GetTypesToIdAvailableAfterEntryBlock(
opt::IRContext* ir_context) const;
// Returns true if adding new predecessors to the given loop merge blocks
// does not render any instructions invalid (each id definition must still
// dominate all of its uses). The loop merge blocks and corresponding new
// predecessors to consider are given in |merge_blocks_to_new_predecessors|.
// All of the new predecessors are assumed to be inside the loop associated
// with the corresponding loop merge block.
static bool CheckDefinitionsStillDominateUsesAfterAddingNewPredecessors(
opt::IRContext* ir_context, const opt::Function* function,
const std::map<uint32_t, std::set<uint32_t>>&
// Returns true if the required ids for |merge_block| are provided in the
// |merge_blocks_to_info| map, or if ids of the suitable type can be found.
static bool CheckThatTheCorrectIdsAreGivenForMergeBlock(
uint32_t merge_block,
const std::map<uint32_t, protobufs::ReturnMergingInfo>&
const std::map<uint32_t, uint32_t>& types_to_available_id,
bool function_is_void, opt::IRContext* ir_context,
const TransformationContext& transformation_context,
std::set<uint32_t>* used_fresh_ids);
protobufs::TransformationMergeFunctionReturns message_;
} // namespace fuzz
} // namespace spvtools