mirror of
https://github.com/KhronosGroup/SPIRV-Tools
synced 2024-11-30 15:00:06 +00:00
867451f49e
Adds a scalar replacement pass. The pass considers all function scope variables of composite type. If there are accesses to individual elements (and it is legal) the pass replaces the variable with a variable for each composite element and updates all the uses. Added the pass to -O Added NumUses and NumUsers to DefUseManager Added some helper methods for the inst to block mapping in context Added some helper methods for specific constant types No longer generate duplicate pointer types. * Now searches for an existing pointer of the appropriate type instead of failing validation * Fixed spec constant extracts * Addressed changes for review * Changed RunSinglePassAndMatch to be able to run validation * current users do not enable it Added handling of acceptable decorations. * Decorations are also transfered where appropriate Refactored extension checking into FeatureManager * Context now owns a feature manager * consciously NOT an analysis * added some test * fixed some minor issues related to decorates * added some decorate related tests for scalar replacement
205 lines
8.2 KiB
C++
205 lines
8.2 KiB
C++
// Copyright (c) 2017 Google 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_SCALAR_REPLACEMENT_PASS_H_
|
|
#define LIBSPIRV_OPT_SCALAR_REPLACEMENT_PASS_H_
|
|
|
|
#include "function.h"
|
|
#include "pass.h"
|
|
#include "type_manager.h"
|
|
|
|
#include <queue>
|
|
|
|
namespace spvtools {
|
|
namespace opt {
|
|
|
|
// Documented in optimizer.hpp
|
|
class ScalarReplacementPass : public Pass {
|
|
public:
|
|
ScalarReplacementPass() = default;
|
|
|
|
const char* name() const override { return "scalar-replacement"; }
|
|
|
|
// Attempts to scalarize all appropriate function scope variables. Returns
|
|
// SuccessWithChange if any change is made.
|
|
Status Process(ir::IRContext* c) override;
|
|
|
|
ir::IRContext::Analysis GetPreservedAnalyses() override {
|
|
return ir::IRContext::kAnalysisDefUse |
|
|
ir::IRContext::kAnalysisInstrToBlockMapping |
|
|
ir::IRContext::kAnalysisDecorations |
|
|
ir::IRContext::kAnalysisCombinators | ir::IRContext::kAnalysisCFG;
|
|
}
|
|
|
|
private:
|
|
// Small container for tracking statistics about variables.
|
|
//
|
|
// TODO(alanbaker): Develop some useful heuristics to tune this pass.
|
|
struct VariableStats {
|
|
uint32_t num_partial_accesses;
|
|
uint32_t num_full_accesses;
|
|
};
|
|
|
|
// Attempts to scalarize all appropriate function scope variables in
|
|
// |function|. Returns SuccessWithChange if any changes are mode.
|
|
Status ProcessFunction(ir::Function* function);
|
|
|
|
// Returns true if |varInst| can be scalarized.
|
|
//
|
|
// Examines the use chain of |varInst| to verify all uses are valid for
|
|
// scalarization.
|
|
bool CanReplaceVariable(const ir::Instruction* varInst) const;
|
|
|
|
// Returns true if |typeInst| is an acceptable type to scalarize.
|
|
//
|
|
// Allows all aggregate types except runtime arrays. Additionally, checks the
|
|
// that the number of elements that would be scalarized is within bounds.
|
|
bool CheckType(const ir::Instruction* typeInst) const;
|
|
|
|
// Returns true if all the decorations for |varInst| are acceptable for
|
|
// scalarization.
|
|
bool CheckAnnotations(const ir::Instruction* varInst) const;
|
|
|
|
// Returns true if all the decorations for |typeInst| are acceptable for
|
|
// scalarization.
|
|
bool CheckTypeAnnotations(const ir::Instruction* typeInst) const;
|
|
|
|
// Returns true if the uses of |inst| are acceptable for scalarization.
|
|
//
|
|
// Recursively checks all the uses of |inst|. For |inst| specifically, only
|
|
// allows SpvOpAccessChain, SpvOpInBoundsAccessChain, SpvOpLoad and
|
|
// SpvOpStore. Access chains must have the first index be a compile-time
|
|
// constant. Subsequent uses of access chains (including other access chains)
|
|
// are checked in a more relaxed manner.
|
|
bool CheckUses(const ir::Instruction* inst) const;
|
|
|
|
// Helper function for the above |CheckUses|.
|
|
//
|
|
// This version tracks some stats about the current OpVariable. These stats
|
|
// are used to drive heuristics about when to scalarize.
|
|
bool CheckUses(const ir::Instruction* inst, VariableStats* stats) const;
|
|
|
|
// Relaxed helper function for |CheckUses|.
|
|
bool CheckUsesRelaxed(const ir::Instruction* inst) const;
|
|
|
|
// Transfers appropriate decorations from |source| to |replacements|.
|
|
void TransferAnnotations(const ir::Instruction* source,
|
|
std::vector<ir::Instruction*>* replacements);
|
|
|
|
// Scalarizes |inst| and updates its uses.
|
|
//
|
|
// |inst| must be an OpVariable. It is replaced with an OpVariable for each
|
|
// for element of the composite type. Uses of |inst| are updated as
|
|
// appropriate. If the replacement variables are themselves scalarizable, they
|
|
// get added to |worklist| for further processing. If any replacement
|
|
// variable ends up with no uses it is erased. Returns false if any
|
|
// subsequent access chain is out of bounds.
|
|
bool ReplaceVariable(ir::Instruction* inst,
|
|
std::queue<ir::Instruction*>* worklist);
|
|
|
|
// Returns the underlying storage type for |inst|.
|
|
//
|
|
// |inst| must be an OpVariable. Returns the type that is pointed to by
|
|
// |inst|.
|
|
ir::Instruction* GetStorageType(const ir::Instruction* inst) const;
|
|
|
|
// Returns true if the load can be scalarized.
|
|
//
|
|
// |inst| must be an OpLoad. Returns true if |index| is the pointer operand of
|
|
// |inst| and the load is not from volatile memory.
|
|
bool CheckLoad(const ir::Instruction* inst, uint32_t index) const;
|
|
|
|
// Returns true if the store can be scalarized.
|
|
//
|
|
// |inst| must be an OpStore. Returns true if |index| is the pointer operand
|
|
// of |inst| and the store is not to volatile memory.
|
|
bool CheckStore(const ir::Instruction* inst, uint32_t index) const;
|
|
|
|
// Creates a variable of type |typeId| from the |index|'th element of
|
|
// |varInst|. The new variable is added to |replacements|.
|
|
void CreateVariable(uint32_t typeId, ir::Instruction* varInst, uint32_t index,
|
|
std::vector<ir::Instruction*>* replacements);
|
|
|
|
// Populates |replacements| with a new OpVariable for each element of |inst|.
|
|
//
|
|
// |inst| must be an OpVariable of a composite type. New variables are
|
|
// initialized the same as the corresponding index in |inst|. |replacements|
|
|
// will contain a variable for each element of the composite with matching
|
|
// indexes (i.e. the 0'th element of |inst| is the 0'th entry of
|
|
// |replacements|).
|
|
void CreateReplacementVariables(ir::Instruction* inst,
|
|
std::vector<ir::Instruction*>* replacements);
|
|
|
|
// Returns the value of an OpConstant of integer type.
|
|
//
|
|
// |constant| must use two or fewer words to generate the value.
|
|
size_t GetConstantInteger(const ir::Instruction* constant) const;
|
|
|
|
// Returns the integer literal for |op|.
|
|
size_t GetIntegerLiteral(const ir::Operand& op) const;
|
|
|
|
// Returns the array length for |arrayInst|.
|
|
size_t GetArrayLength(const ir::Instruction* arrayInst) const;
|
|
|
|
// Returns the number of elements in |type|.
|
|
//
|
|
// |type| must be a vector or matrix type.
|
|
size_t GetNumElements(const ir::Instruction* type) const;
|
|
|
|
// Returns an id for a pointer to |id|.
|
|
uint32_t GetOrCreatePointerType(uint32_t id);
|
|
|
|
// Creates the initial value for the |index| element of |source| in |newVar|.
|
|
//
|
|
// If there is an initial value for |source| for element |index|, it is
|
|
// appended as an operand on |newVar|. If the initial value is OpUndef, no
|
|
// initial value is added to |newVar|.
|
|
void GetOrCreateInitialValue(ir::Instruction* source, uint32_t index,
|
|
ir::Instruction* newVar);
|
|
|
|
// Replaces the load to the entire composite.
|
|
//
|
|
// Generates a load for each replacement variable and then creates a new
|
|
// composite by combining all of the loads.
|
|
//
|
|
// |load| must be a load.
|
|
void ReplaceWholeLoad(ir::Instruction* load,
|
|
const std::vector<ir::Instruction*>& replacements);
|
|
|
|
// Replaces the store to the entire composite.
|
|
//
|
|
// Generates a composite extract and store for each element in the scalarized
|
|
// variable from the original store data input.
|
|
void ReplaceWholeStore(ir::Instruction* store,
|
|
const std::vector<ir::Instruction*>& replacements);
|
|
|
|
// Replaces an access chain to the composite variable with either a direct use
|
|
// of the appropriate replacement variable or another access chain with the
|
|
// replacement variable as the base and one fewer indexes. Returns false if
|
|
// the chain has an out of bounds access.
|
|
bool ReplaceAccessChain(ir::Instruction* chain,
|
|
const std::vector<ir::Instruction*>& replacements);
|
|
|
|
// Maps storage type to a pointer type enclosing that type.
|
|
std::unordered_map<uint32_t, uint32_t> pointee_to_pointer_;
|
|
|
|
// Maps type id to OpConstantNull for that type.
|
|
std::unordered_map<uint32_t, uint32_t> type_to_null_;
|
|
};
|
|
|
|
} // namespace opt
|
|
} // namespace spvtools
|
|
|
|
#endif // LIBSPIRV_OPT_SCALAR_REPLACEMENT_PASS_H_
|