2017-07-27 19:30:12 +00:00
|
|
|
// Copyright (c) 2017 The Khronos Group Inc.
|
|
|
|
// Copyright (c) 2017 Valve Corporation
|
|
|
|
// Copyright (c) 2017 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.
|
|
|
|
|
2018-08-03 12:05:33 +00:00
|
|
|
#ifndef SOURCE_OPT_MEM_PASS_H_
|
|
|
|
#define SOURCE_OPT_MEM_PASS_H_
|
2017-07-27 19:30:12 +00:00
|
|
|
|
|
|
|
#include <algorithm>
|
2017-10-25 17:26:25 +00:00
|
|
|
#include <list>
|
2017-07-27 19:30:12 +00:00
|
|
|
#include <map>
|
|
|
|
#include <queue>
|
|
|
|
#include <unordered_map>
|
|
|
|
#include <unordered_set>
|
|
|
|
#include <utility>
|
|
|
|
|
2018-08-03 19:06:09 +00:00
|
|
|
#include "source/opt/basic_block.h"
|
|
|
|
#include "source/opt/def_use_manager.h"
|
|
|
|
#include "source/opt/dominator_analysis.h"
|
|
|
|
#include "source/opt/module.h"
|
|
|
|
#include "source/opt/pass.h"
|
2017-07-27 19:30:12 +00:00
|
|
|
|
|
|
|
namespace spvtools {
|
|
|
|
namespace opt {
|
|
|
|
|
|
|
|
// A common base class for mem2reg-type passes. Provides common
|
|
|
|
// utility functions and supporting state.
|
|
|
|
class MemPass : public Pass {
|
|
|
|
public:
|
|
|
|
virtual ~MemPass() = default;
|
|
|
|
|
SSA rewrite pass.
This pass replaces the load/store elimination passes. It implements the
SSA re-writing algorithm proposed in
Simple and Efficient Construction of Static Single Assignment Form.
Braun M., Buchwald S., Hack S., Leißa R., Mallon C., Zwinkau A. (2013)
In: Jhala R., De Bosschere K. (eds)
Compiler Construction. CC 2013.
Lecture Notes in Computer Science, vol 7791.
Springer, Berlin, Heidelberg
https://link.springer.com/chapter/10.1007/978-3-642-37051-9_6
In contrast to common eager algorithms based on dominance and dominance
frontier information, this algorithm works backwards from load operations.
When a target variable is loaded, it queries the variable's reaching
definition. If the reaching definition is unknown at the current location,
it searches backwards in the CFG, inserting Phi instructions at join points
in the CFG along the way until it finds the desired store instruction.
The algorithm avoids repeated lookups using memoization.
For reducible CFGs, which are a superset of the structured CFGs in SPIRV,
this algorithm is proven to produce minimal SSA. That is, it inserts the
minimal number of Phi instructions required to ensure the SSA property, but
some Phi instructions may be dead
(https://en.wikipedia.org/wiki/Static_single_assignment_form).
2018-02-22 21:18:29 +00:00
|
|
|
// Returns an undef value for the given |var_id|'s type.
|
|
|
|
uint32_t GetUndefVal(uint32_t var_id) {
|
|
|
|
return Type2Undef(GetPointeeTypeId(get_def_use_mgr()->GetDef(var_id)));
|
|
|
|
}
|
|
|
|
|
|
|
|
// Given a load or store |ip|, return the pointer instruction.
|
|
|
|
// Also return the base variable's id in |varId|. If no base variable is
|
|
|
|
// found, |varId| will be 0.
|
2018-07-12 19:14:43 +00:00
|
|
|
Instruction* GetPtr(Instruction* ip, uint32_t* varId);
|
SSA rewrite pass.
This pass replaces the load/store elimination passes. It implements the
SSA re-writing algorithm proposed in
Simple and Efficient Construction of Static Single Assignment Form.
Braun M., Buchwald S., Hack S., Leißa R., Mallon C., Zwinkau A. (2013)
In: Jhala R., De Bosschere K. (eds)
Compiler Construction. CC 2013.
Lecture Notes in Computer Science, vol 7791.
Springer, Berlin, Heidelberg
https://link.springer.com/chapter/10.1007/978-3-642-37051-9_6
In contrast to common eager algorithms based on dominance and dominance
frontier information, this algorithm works backwards from load operations.
When a target variable is loaded, it queries the variable's reaching
definition. If the reaching definition is unknown at the current location,
it searches backwards in the CFG, inserting Phi instructions at join points
in the CFG along the way until it finds the desired store instruction.
The algorithm avoids repeated lookups using memoization.
For reducible CFGs, which are a superset of the structured CFGs in SPIRV,
this algorithm is proven to produce minimal SSA. That is, it inserts the
minimal number of Phi instructions required to ensure the SSA property, but
some Phi instructions may be dead
(https://en.wikipedia.org/wiki/Static_single_assignment_form).
2018-02-22 21:18:29 +00:00
|
|
|
|
|
|
|
// Return true if |varId| is a previously identified target variable.
|
|
|
|
// Return false if |varId| is a previously identified non-target variable.
|
|
|
|
//
|
|
|
|
// Non-target variables are variable of function scope of a target type that
|
|
|
|
// are accessed with constant-index access chains. not accessed with
|
|
|
|
// non-constant-index access chains. Also cache non-target variables.
|
|
|
|
//
|
|
|
|
// If variable is not cached, return true if variable is a function scope
|
|
|
|
// variable of target type, false otherwise. Updates caches of target and
|
|
|
|
// non-target variables.
|
|
|
|
bool IsTargetVar(uint32_t varId);
|
|
|
|
|
|
|
|
// Collect target SSA variables. This traverses all the loads and stores in
|
|
|
|
// function |func| looking for variables that can be replaced with SSA IDs. It
|
2018-04-10 20:32:47 +00:00
|
|
|
// populates the sets |seen_target_vars_| and |seen_non_target_vars_|.
|
2018-07-12 19:14:43 +00:00
|
|
|
void CollectTargetVars(Function* func);
|
SSA rewrite pass.
This pass replaces the load/store elimination passes. It implements the
SSA re-writing algorithm proposed in
Simple and Efficient Construction of Static Single Assignment Form.
Braun M., Buchwald S., Hack S., Leißa R., Mallon C., Zwinkau A. (2013)
In: Jhala R., De Bosschere K. (eds)
Compiler Construction. CC 2013.
Lecture Notes in Computer Science, vol 7791.
Springer, Berlin, Heidelberg
https://link.springer.com/chapter/10.1007/978-3-642-37051-9_6
In contrast to common eager algorithms based on dominance and dominance
frontier information, this algorithm works backwards from load operations.
When a target variable is loaded, it queries the variable's reaching
definition. If the reaching definition is unknown at the current location,
it searches backwards in the CFG, inserting Phi instructions at join points
in the CFG along the way until it finds the desired store instruction.
The algorithm avoids repeated lookups using memoization.
For reducible CFGs, which are a superset of the structured CFGs in SPIRV,
this algorithm is proven to produce minimal SSA. That is, it inserts the
minimal number of Phi instructions required to ensure the SSA property, but
some Phi instructions may be dead
(https://en.wikipedia.org/wiki/Static_single_assignment_form).
2018-02-22 21:18:29 +00:00
|
|
|
|
2017-07-27 19:30:12 +00:00
|
|
|
protected:
|
2018-07-12 13:08:45 +00:00
|
|
|
MemPass();
|
|
|
|
|
2017-07-27 19:30:12 +00:00
|
|
|
// Returns true if |typeInst| is a scalar type
|
|
|
|
// or a vector or matrix
|
2018-07-12 19:14:43 +00:00
|
|
|
bool IsBaseTargetType(const Instruction* typeInst) const;
|
2017-07-27 19:30:12 +00:00
|
|
|
|
|
|
|
// Returns true if |typeInst| is a math type or a struct or array
|
|
|
|
// of a math type.
|
|
|
|
// TODO(): Add more complex types to convert
|
2018-07-12 19:14:43 +00:00
|
|
|
bool IsTargetType(const Instruction* typeInst) const;
|
2017-07-27 19:30:12 +00:00
|
|
|
|
|
|
|
// Returns true if |opcode| is a non-ptr access chain op
|
|
|
|
bool IsNonPtrAccessChain(const SpvOp opcode) const;
|
|
|
|
|
2017-08-04 21:04:37 +00:00
|
|
|
// Given the id |ptrId|, return true if the top-most non-CopyObj is
|
2017-08-10 22:42:16 +00:00
|
|
|
// a variable, a non-ptr access chain or a parameter of pointer type.
|
2017-08-04 21:04:37 +00:00
|
|
|
bool IsPtr(uint32_t ptrId);
|
|
|
|
|
|
|
|
// Given the id of a pointer |ptrId|, return the top-most non-CopyObj.
|
2017-12-11 18:10:24 +00:00
|
|
|
// Also return the base variable's id in |varId|. If no base variable is
|
|
|
|
// found, |varId| will be 0.
|
2018-07-12 19:14:43 +00:00
|
|
|
Instruction* GetPtr(uint32_t ptrId, uint32_t* varId);
|
2017-08-04 21:04:37 +00:00
|
|
|
|
2017-07-27 19:30:12 +00:00
|
|
|
// Return true if all uses of |id| are only name or decorate ops.
|
|
|
|
bool HasOnlyNamesAndDecorates(uint32_t id) const;
|
|
|
|
|
2018-01-04 22:04:03 +00:00
|
|
|
// Kill all instructions in block |bp|. Whether or not to kill the label is
|
|
|
|
// indicated by |killLabel|.
|
2018-07-12 19:14:43 +00:00
|
|
|
void KillAllInsts(BasicBlock* bp, bool killLabel = true);
|
2017-10-17 22:33:43 +00:00
|
|
|
|
2017-07-27 19:30:12 +00:00
|
|
|
// Return true if any instruction loads from |varId|
|
|
|
|
bool HasLoads(uint32_t varId) const;
|
|
|
|
|
|
|
|
// Return true if |varId| is not a function variable or if it has
|
|
|
|
// a load
|
|
|
|
bool IsLiveVar(uint32_t varId) const;
|
|
|
|
|
|
|
|
// Add stores using |ptr_id| to |insts|
|
2018-07-12 19:14:43 +00:00
|
|
|
void AddStores(uint32_t ptr_id, std::queue<Instruction*>* insts);
|
2017-07-27 19:30:12 +00:00
|
|
|
|
|
|
|
// Delete |inst| and iterate DCE on all its operands if they are now
|
|
|
|
// useless. If a load is deleted and its variable has no other loads,
|
|
|
|
// delete all its variable's stores.
|
2018-07-12 19:14:43 +00:00
|
|
|
void DCEInst(Instruction* inst, const std::function<void(Instruction*)>&);
|
2017-07-27 19:30:12 +00:00
|
|
|
|
2017-10-17 22:33:43 +00:00
|
|
|
// Call all the cleanup helper functions on |func|.
|
2018-07-12 19:14:43 +00:00
|
|
|
bool CFGCleanup(Function* func);
|
2017-10-17 22:33:43 +00:00
|
|
|
|
2017-07-27 19:30:12 +00:00
|
|
|
// Return true if |op| is supported decorate.
|
2017-08-04 18:00:30 +00:00
|
|
|
inline bool IsNonTypeDecorate(uint32_t op) const {
|
2017-07-27 19:30:12 +00:00
|
|
|
return (op == SpvOpDecorate || op == SpvOpDecorateId);
|
|
|
|
}
|
|
|
|
|
2019-09-10 13:38:23 +00:00
|
|
|
// Return the id of an undef value with type |type_id|. Create and insert an
|
|
|
|
// undef after the first non-variable in the function if it doesn't already
|
|
|
|
// exist. Add undef to function undef map. Returns 0 of the value does not
|
|
|
|
// exist, and cannot be created.
|
2017-10-25 17:26:25 +00:00
|
|
|
uint32_t Type2Undef(uint32_t type_id);
|
2017-07-27 19:30:12 +00:00
|
|
|
|
|
|
|
// Cache of verified target vars
|
|
|
|
std::unordered_set<uint32_t> seen_target_vars_;
|
|
|
|
|
|
|
|
// Cache of verified non-target vars
|
|
|
|
std::unordered_set<uint32_t> seen_non_target_vars_;
|
|
|
|
|
2017-10-25 17:26:25 +00:00
|
|
|
private:
|
|
|
|
// Return true if all uses of |varId| are only through supported reference
|
|
|
|
// operations ie. loads and store. Also cache in supported_ref_vars_.
|
|
|
|
// TODO(dnovillo): This function is replicated in other passes and it's
|
|
|
|
// slightly different in every pass. Is it possible to make one common
|
|
|
|
// implementation?
|
|
|
|
bool HasOnlySupportedRefs(uint32_t varId);
|
|
|
|
|
2017-10-17 22:33:43 +00:00
|
|
|
// Remove all the unreachable basic blocks in |func|.
|
2018-07-12 19:14:43 +00:00
|
|
|
bool RemoveUnreachableBlocks(Function* func);
|
2017-10-30 13:02:03 +00:00
|
|
|
|
2017-10-17 22:33:43 +00:00
|
|
|
// Remove the block pointed by the iterator |*bi|. This also removes
|
|
|
|
// all the instructions in the pointed-to block.
|
2018-07-12 19:14:43 +00:00
|
|
|
void RemoveBlock(Function::iterator* bi);
|
2017-10-17 22:33:43 +00:00
|
|
|
|
|
|
|
// Remove Phi operands in |phi| that are coming from blocks not in
|
|
|
|
// |reachable_blocks|.
|
2018-04-12 17:50:36 +00:00
|
|
|
void RemovePhiOperands(
|
2018-07-12 19:14:43 +00:00
|
|
|
Instruction* phi,
|
|
|
|
const std::unordered_set<BasicBlock*>& reachable_blocks);
|
2017-10-10 23:16:50 +00:00
|
|
|
|
2017-10-25 17:26:25 +00:00
|
|
|
// Map from type to undef
|
|
|
|
std::unordered_map<uint32_t, uint32_t> type2undefs_;
|
2017-07-27 19:30:12 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
} // namespace opt
|
|
|
|
} // namespace spvtools
|
|
|
|
|
2018-08-03 12:05:33 +00:00
|
|
|
#endif // SOURCE_OPT_MEM_PASS_H_
|