SPIRV-Tools/source/opt/common_uniform_elim_pass.h
Steven Perron 65046eca7c Change IRContext::KillInst to delete instructions.
The current method of removing an instruction is to call ToNop.  The
problem with this is that it leaves around an instruction that later
passes will look at.  We should just delete the instruction.

In MemPass there is a utility routine called DCEInst.  It can delete
essentially any instruction, which can invalidate pointers now that they
are actually deleted.  The interface was changed to add a call back that
can be used to update any local data structures that contain
ir::Intruction*.
2017-12-04 11:07:45 -05:00

198 lines
7.7 KiB
C++

// Copyright (c) 2016 The Khronos Group Inc.
// Copyright (c) 2016 Valve Corporation
// Copyright (c) 2016 LunarG Inc.
//
// 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 LIBSPIRV_OPT_COMMON_UNIFORM_ELIM_PASS_H_
#define LIBSPIRV_OPT_COMMON_UNIFORM_ELIM_PASS_H_
#include <algorithm>
#include <map>
#include <queue>
#include <unordered_map>
#include <unordered_set>
#include <utility>
#include "basic_block.h"
#include "decoration_manager.h"
#include "def_use_manager.h"
#include "ir_context.h"
#include "module.h"
#include "pass.h"
namespace spvtools {
namespace opt {
// See optimizer.hpp for documentation.
class CommonUniformElimPass : public Pass {
using cbb_ptr = const ir::BasicBlock*;
public:
using GetBlocksFunction =
std::function<std::vector<ir::BasicBlock*>*(const ir::BasicBlock*)>;
CommonUniformElimPass();
const char* name() const override { return "eliminate-common-uniform"; }
Status Process(ir::IRContext*) override;
private:
// Returns true if |opcode| is a non-ptr access chain op
bool IsNonPtrAccessChain(const SpvOp opcode) const;
// Returns true if |typeInst| is a sampler or image type or a struct
// containing one, recursively.
bool IsSamplerOrImageType(const ir::Instruction* typeInst) const;
// Returns true if |varId| is a variable containing a sampler or image.
bool IsSamplerOrImageVar(uint32_t varId) const;
// Given a load or store pointed at by |ip|, return the top-most
// non-CopyObj in its pointer operand. Also return the base pointer
// in |objId|.
ir::Instruction* GetPtr(ir::Instruction* ip, uint32_t* objId);
// Return true if variable is uniform
bool IsUniformVar(uint32_t varId);
// Given the type id for a struct type, checks if the struct type
// or any struct member is volatile decorated
bool IsVolatileStruct(uint32_t type_id);
// Given an OpAccessChain instruction, return true
// if the accessed variable belongs to a volatile
// decorated object or member of a struct type
bool IsAccessChainToVolatileStructType(
const ir::Instruction& AccessChainInst);
// Given an OpLoad instruction, return true if
// OpLoad has a Volatile Memory Access flag or if
// the resulting type is a volatile decorated struct
bool IsVolatileLoad(const ir::Instruction& loadInst);
// Return true if any uses of |id| are decorate ops.
bool HasUnsupportedDecorates(uint32_t id) const;
// Return true if all uses of |id| are only name or decorate ops.
bool HasOnlyNamesAndDecorates(uint32_t id) const;
// Delete inst if it has no uses. Assumes inst has a resultId.
void DeleteIfUseless(ir::Instruction* inst);
// Replace all instances of load's id with replId and delete load
// and its access chain, if any
ir::Instruction* ReplaceAndDeleteLoad(ir::Instruction* loadInst,
uint32_t replId,
ir::Instruction* ptrInst);
// For the (constant index) access chain ptrInst, create an
// equivalent load and extract
void GenACLoadRepl(const ir::Instruction* ptrInst,
std::vector<std::unique_ptr<ir::Instruction>>* newInsts,
uint32_t* resultId);
// Return true if all indices are constant
bool IsConstantIndexAccessChain(ir::Instruction* acp);
// Convert all uniform access chain loads into load/extract.
bool UniformAccessChainConvert(ir::Function* func);
// Compute structured successors for function |func|.
// A block's structured successors are the blocks it branches to
// together with its declared merge block if it has one.
// When order matters, the merge block always appears first.
// This assures correct depth first search in the presence of early
// returns and kills. If the successor vector contain duplicates
// if the merge block, they are safely ignored by DFS.
//
// TODO(dnovillo): This pass computes structured successors slightly different
// than the implementation in class Pass. Can this be re-factored?
void ComputeStructuredSuccessors(ir::Function* func);
// Compute structured block order for |func| into |structuredOrder|. This
// order has the property that dominators come before all blocks they
// dominate and merge blocks come after all blocks that are in the control
// constructs of their header.
//
// TODO(dnovillo): This pass computes structured order slightly different
// than the implementation in class Pass. Can this be re-factored?
void ComputeStructuredOrder(ir::Function* func,
std::list<ir::BasicBlock*>* order);
// Eliminate loads of uniform variables which have previously been loaded.
// If first load is in control flow, move it to first block of function.
// Most effective if preceded by UniformAccessChainRemoval().
bool CommonUniformLoadElimination(ir::Function* func);
// Eliminate loads of uniform sampler and image variables which have
// previously
// been loaded in the same block for types whose loads cannot cross blocks.
bool CommonUniformLoadElimBlock(ir::Function* func);
// Eliminate duplicated extracts of same id. Extract may be moved to same
// block as the id definition. This is primarily intended for extracts
// from uniform loads. Most effective if preceded by
// CommonUniformLoadElimination().
bool CommonExtractElimination(ir::Function* func);
// For function |func|, first change all uniform constant index
// access chain loads into equivalent composite extracts. Then consolidate
// identical uniform loads into one uniform load. Finally, consolidate
// identical uniform extracts into one uniform extract. This may require
// moving a load or extract to a point which dominates all uses.
// Return true if func is modified.
//
// This pass requires the function to have structured control flow ie shader
// capability. It also requires logical addressing ie Addresses capability
// is not enabled. It also currently does not support any extensions.
//
// This function currently only optimizes loads with a single index.
bool EliminateCommonUniform(ir::Function* func);
// Initialize extensions whitelist
void InitExtensions();
// Return true if all extensions in this module are allowed by this pass.
bool AllExtensionsSupported() const;
// Return true if |op| is a decorate for non-type instruction
inline bool IsNonTypeDecorate(uint32_t op) const {
return (op == SpvOpDecorate || op == SpvOpDecorateId);
}
void Initialize(ir::IRContext* c);
Pass::Status ProcessImpl();
// Map from uniform variable id to its common load id
std::unordered_map<uint32_t, uint32_t> uniform2load_id_;
// Map of extract composite ids to map of indices to insts
// TODO(greg-lunarg): Consider std::vector.
std::unordered_map<uint32_t,
std::unordered_map<uint32_t, std::list<ir::Instruction*>>>
comp2idx2inst_;
// Extensions supported by this pass.
std::unordered_set<std::string> extensions_whitelist_;
// Map from block to its structured successor blocks. See
// ComputeStructuredSuccessors() for definition.
std::unordered_map<const ir::BasicBlock*, std::vector<ir::BasicBlock*>>
block2structured_succs_;
};
} // namespace opt
} // namespace spvtools
#endif // LIBSPIRV_OPT_SSAMEM_PASS_H_