| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229 |
- // 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_OUTLINE_FUNCTION_H_
- #define SOURCE_FUZZ_TRANSFORMATION_OUTLINE_FUNCTION_H_
- #include <map>
- #include <set>
- #include <vector>
- #include "source/fuzz/protobufs/spirvfuzz_protobufs.h"
- #include "source/fuzz/transformation.h"
- #include "source/fuzz/transformation_context.h"
- #include "source/opt/ir_context.h"
- namespace spvtools {
- namespace fuzz {
- class TransformationOutlineFunction : public Transformation {
- public:
- explicit TransformationOutlineFunction(
- protobufs::TransformationOutlineFunction message);
- TransformationOutlineFunction(
- uint32_t entry_block, uint32_t exit_block,
- uint32_t new_function_struct_return_type_id,
- uint32_t new_function_type_id, uint32_t new_function_id,
- uint32_t new_function_region_entry_block, uint32_t new_caller_result_id,
- uint32_t new_callee_result_id,
- const std::map<uint32_t, uint32_t>& input_id_to_fresh_id,
- const std::map<uint32_t, uint32_t>& output_id_to_fresh_id);
- // - All the fresh ids occurring in the transformation must be distinct and
- // fresh
- // - |message_.entry_block| and |message_.exit_block| must form a single-entry
- // single-exit control flow graph region
- // - |message_.entry_block| must not start with OpVariable
- // - |message_.entry_block| must not be a loop header
- // - |message_.exit_block| must not be a merge block or the continue target
- // of a loop
- // - A structured control flow construct must lie either completely within the
- // region or completely outside it
- // - |message.entry_block| must not start with OpPhi; this is to keep the
- // transformation simple - another transformation should be used to split
- // a desired entry block that starts with OpPhi if needed
- // - |message_.input_id_to_fresh_id| must contain an entry for every id
- // defined outside the region but used in the region
- // - |message_.output_id_to_fresh_id| must contain an entry for every id
- // defined in the region but used outside the region
- bool IsApplicable(
- opt::IRContext* ir_context,
- const TransformationContext& transformation_context) const override;
- // - A new function with id |message_.new_function_id| is added to the module.
- // - If the region generates output ids, the return type of this function is
- // a new struct type with one field per output id, and with type id
- // |message_.new_function_struct_return_type|, otherwise the function return
- // types is void and |message_.new_function_struct_return_type| is not used.
- // - If the region generates input ids, the new function has one parameter per
- // input id. Fresh ids for these parameters are provided by
- // |message_.input_id_to_fresh_id|.
- // - Unless the type required for the new function is already known,
- // |message_.new_function_type_id| is used as the type id for a new function
- // type, and the new function uses this type.
- // - The new function starts with a placeholder block with id
- // |message_.new_function_first_block|, which jumps straight to a successor
- // block, to avoid violating rules on what the first block in a function may
- // look like.
- // - The outlined region is replaced with a single block, with the same id
- // as |message_.entry_block|, and which calls the new function, passing the
- // region's input ids as parameters. The result is stored in
- // |message_.new_caller_result_id|, which has type
- // |message_.new_function_struct_return_type| (unless there are
- // no output ids, in which case the return type is void). The components
- // of this returned struct are then copied out into the region's output ids.
- // The block ends with the merge instruction (if any) and terminator of
- // |message_.exit_block|.
- // - The body of the new function is identical to the outlined region, except
- // that (a) the region's entry block has id
- // |message_.new_function_region_entry_block|, (b) input id uses are
- // replaced with parameter accesses, (c) and definitions of output ids are
- // replaced with definitions of corresponding fresh ids provided by
- // |message_.output_id_to_fresh_id|, and (d) the block of the function
- // ends by returning a composite of type
- // |message_.new_function_struct_return_type| comprised of all the fresh
- // output ids (unless the return type is void, in which case no value is
- // returned.
- 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 the set of blocks dominated by |entry_block| and post-dominated
- // by |exit_block|.
- static std::set<opt::BasicBlock*> GetRegionBlocks(
- opt::IRContext* ir_context, opt::BasicBlock* entry_block,
- opt::BasicBlock* exit_block);
- // Yields ids that are used in |region_set| and that are either parameters
- // to the function containing |region_set|, or are defined by blocks of this
- // function that are outside |region_set|.
- //
- // Special cases: OpPhi instructions in |region_entry_block| and the
- // terminator of |region_exit_block| do not get outlined, therefore
- // - id uses in OpPhi instructions in |region_entry_block| are ignored
- // - id uses in the terminator instruction of |region_exit_block| are ignored
- static std::vector<uint32_t> GetRegionInputIds(
- opt::IRContext* ir_context, const std::set<opt::BasicBlock*>& region_set,
- opt::BasicBlock* region_exit_block);
- // Yields all ids that are defined in |region_set| and used outside
- // |region_set|.
- //
- // Special cases: for similar reasons as for |GetRegionInputIds|,
- // - ids defined in the region and used in the terminator of
- // |region_exit_block| count as output ids
- static std::vector<uint32_t> GetRegionOutputIds(
- opt::IRContext* ir_context, const std::set<opt::BasicBlock*>& region_set,
- opt::BasicBlock* region_exit_block);
- private:
- // Ensures that the module's id bound is at least the maximum of any fresh id
- // associated with the transformation.
- void UpdateModuleIdBoundForFreshIds(
- opt::IRContext* ir_context,
- const std::map<uint32_t, uint32_t>& input_id_to_fresh_id_map,
- const std::map<uint32_t, uint32_t>& output_id_to_fresh_id_map) const;
- // Uses |input_id_to_fresh_id_map| and |output_id_to_fresh_id_map| to convert,
- // in the region to be outlined, all the input ids in |region_input_ids| and
- // the output ids in |region_output_ids| to their fresh counterparts.
- // Parameters |region_blocks| provides access to the blocks that must be
- // modified, and |original_region_exit_block| allows for some special cases
- // where ids should not be remapped.
- void RemapInputAndOutputIdsInRegion(
- opt::IRContext* ir_context,
- const opt::BasicBlock& original_region_exit_block,
- const std::set<opt::BasicBlock*>& region_blocks,
- const std::vector<uint32_t>& region_input_ids,
- const std::vector<uint32_t>& region_output_ids,
- const std::map<uint32_t, uint32_t>& input_id_to_fresh_id_map,
- const std::map<uint32_t, uint32_t>& output_id_to_fresh_id_map) const;
- // Produce a Function object that has the right function type and parameter
- // declarations. The function argument types and parameter ids are dictated
- // by |region_input_ids| and |input_id_to_fresh_id_map|. The function return
- // type is dictated by |region_output_ids|.
- //
- // A new struct type to represent the function return type, and a new function
- // type for the function, will be added to the module (unless suitable types
- // are already present).
- //
- // Facts about the function containing the outlined region that are relevant
- // to the new function are propagated via the vact manager in
- // |transformation_context|.
- std::unique_ptr<opt::Function> PrepareFunctionPrototype(
- const std::vector<uint32_t>& region_input_ids,
- const std::vector<uint32_t>& region_output_ids,
- const std::map<uint32_t, uint32_t>& input_id_to_fresh_id_map,
- opt::IRContext* ir_context,
- TransformationContext* transformation_context) const;
- // Creates the body of the outlined function by cloning blocks from the
- // original region, given by |region_blocks|, adapting the cloned version
- // of |original_region_exit_block| so that it returns something appropriate,
- // and patching up branches to |original_region_entry_block| to refer to its
- // clone. Parameters |region_output_ids| and |output_id_to_fresh_id_map| are
- // used to determine what the function should return. Parameter
- // |output_id_to_type_id| provides the type of each output id.
- //
- // The |transformation_context| argument allow facts about blocks being
- // outlined, e.g. whether they are dead blocks, to be asserted about blocks
- // that get created during outlining.
- void PopulateOutlinedFunction(
- const opt::BasicBlock& original_region_entry_block,
- const opt::BasicBlock& original_region_exit_block,
- const std::set<opt::BasicBlock*>& region_blocks,
- const std::vector<uint32_t>& region_output_ids,
- const std::map<uint32_t, uint32_t>& output_id_to_type_id,
- const std::map<uint32_t, uint32_t>& output_id_to_fresh_id_map,
- opt::IRContext* ir_context, opt::Function* outlined_function) const;
- // Shrinks the outlined region, given by |region_blocks|, down to the single
- // block |original_region_entry_block|. This block is itself shrunk to just
- // contain:
- // - any OpPhi instructions that were originally present
- // - a call to the outlined function, with parameters provided by
- // |region_input_ids|
- // - instructions to route components of the call's return value into
- // |region_output_ids|
- // - The merge instruction (if any) and terminator of the original region's
- // exit block, given by |cloned_exit_block_merge| and
- // |cloned_exit_block_terminator|
- // Parameters |output_id_to_type_id| and |return_type_id| provide the
- // provide types for the region's output ids, and the return type of the
- // outlined function: as the module is in an inconsistent state when this
- // function is called, this information cannot be gotten from the def-use
- // manager.
- void ShrinkOriginalRegion(
- opt::IRContext* ir_context,
- const std::set<opt::BasicBlock*>& region_blocks,
- const std::vector<uint32_t>& region_input_ids,
- const std::vector<uint32_t>& region_output_ids,
- const std::map<uint32_t, uint32_t>& output_id_to_type_id,
- uint32_t return_type_id,
- std::unique_ptr<opt::Instruction> cloned_exit_block_merge,
- std::unique_ptr<opt::Instruction> cloned_exit_block_terminator,
- opt::BasicBlock* original_region_entry_block) const;
- protobufs::TransformationOutlineFunction message_;
- };
- } // namespace fuzz
- } // namespace spvtools
- #endif // SOURCE_FUZZ_TRANSFORMATION_OUTLINE_FUNCTION_H_
|