mirror of
https://gitee.com/openharmony/third_party_spirv-tools
synced 2024-11-27 17:40:28 +00:00
d8ca09821d
The first implementation of MemroyObject, which is used in copy propagate arrays, forced the access chain to be like the access chains in OpCompositeExtract. This excluded the possibility of the memory object from representing an array element that was extracted with a variable index. Looking at the code, that restriction is not neccessary. I also see some opportunities for doing this in some real shaders. Contributes to #1430.
219 lines
9.7 KiB
C++
219 lines
9.7 KiB
C++
// Copyright (c) 2018 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 LIBSPIRV_OPT_COPY_PROP_H_
|
|
#define LIBSPIRV_OPT_COPY_PROP_H_
|
|
|
|
#include "mem_pass.h"
|
|
|
|
namespace spvtools {
|
|
namespace opt {
|
|
|
|
// This pass implements a simple array copy propagation. It does not do a full
|
|
// array data flow. It looks for simple cases that meet the following
|
|
// conditions:
|
|
//
|
|
// 1) The source must never be stored to.
|
|
// 2) The target must be stored to exactly once.
|
|
// 3) The store to the target must be a store to the entire array, and be a
|
|
// copy of the entire source.
|
|
// 4) All loads of the target must be dominated by the store.
|
|
//
|
|
// The hard part is keeping all of the types correct. We do not want to
|
|
// have to do too large a search to update everything, which may not be
|
|
// possible, do we give up if we see any instruction that might be hard to
|
|
// update.
|
|
|
|
class CopyPropagateArrays : public MemPass {
|
|
public:
|
|
const char* name() const override { return "copy-propagate-arrays"; }
|
|
Status Process(ir::IRContext*) override;
|
|
|
|
ir::IRContext::Analysis GetPreservedAnalyses() override {
|
|
return ir::IRContext::kAnalysisDefUse | ir::IRContext::kAnalysisCFG |
|
|
ir::IRContext::kAnalysisInstrToBlockMapping |
|
|
ir::IRContext::kAnalysisLoopAnalysis |
|
|
ir::IRContext::kAnalysisDecorations |
|
|
ir::IRContext::kAnalysisDominatorAnalysis |
|
|
ir::IRContext::kAnalysisNameMap;
|
|
}
|
|
|
|
private:
|
|
// The class used to identify a particular memory object. This memory object
|
|
// will be owned by a particular variable, meaning that the memory is part of
|
|
// that variable. It could be the entire variable or a member of the
|
|
// variable.
|
|
class MemoryObject {
|
|
public:
|
|
// Construction a memory object that is owned by |var_inst|. The iterator
|
|
// |begin| and |end| traverse a container of integers that identify which
|
|
// member of |var_inst| this memory object will represent. These integers
|
|
// are interpreted the same way they would be in an |OpAccessChain|
|
|
// instruction.
|
|
template <class iterator>
|
|
MemoryObject(ir::Instruction* var_inst, iterator begin, iterator end);
|
|
|
|
// Change |this| to now point to the member identified by |access_chain|
|
|
// (starting from the current member). The elements in |access_chain| are
|
|
// interpreted the same as the indices in the |OpAccessChain|
|
|
// instruction.
|
|
void GetMember(const std::vector<uint32_t>& access_chain);
|
|
|
|
// Change |this| to now represent the first enclosing object to which it
|
|
// belongs. (Remove the last element off the access_chain). It is invalid
|
|
// to call this function if |this| does not represent a member of its owner.
|
|
void GetParent() {
|
|
assert(IsMember());
|
|
access_chain_.pop_back();
|
|
}
|
|
|
|
// Returns true if |this| represents a member of its owner, and not the
|
|
// entire variable.
|
|
bool IsMember() const { return !access_chain_.empty(); }
|
|
|
|
// Returns the number of members in the object represented by |this|. If
|
|
// |this| does not represent a composite type, the return value will be 0.
|
|
uint32_t GetNumberOfMembers();
|
|
|
|
// Returns the owning variable that the memory object is contained in.
|
|
ir::Instruction* GetVariable() const { return variable_inst_; }
|
|
|
|
// Returns a vector of integers that can be used to access the specific
|
|
// member that |this| represents starting from the owning variable. These
|
|
// values are to be interpreted the same way the indices are in an
|
|
// |OpAccessChain| instruction.
|
|
const std::vector<uint32_t>& AccessChain() const { return access_chain_; }
|
|
|
|
// Returns the type id of the pointer type that can be used to point to this
|
|
// memory object.
|
|
uint32_t GetPointerTypeId() const {
|
|
analysis::TypeManager* type_mgr =
|
|
GetVariable()->context()->get_type_mgr();
|
|
const analysis::Pointer* pointer_type =
|
|
type_mgr->GetType(GetVariable()->type_id())->AsPointer();
|
|
const analysis::Type* var_type = pointer_type->pointee_type();
|
|
const analysis::Type* member_type =
|
|
type_mgr->GetMemberType(var_type, GetAccessIds());
|
|
uint32_t member_type_id = type_mgr->GetId(member_type);
|
|
assert(member_type != 0);
|
|
uint32_t member_pointer_type_id = type_mgr->FindPointerToType(
|
|
member_type_id, pointer_type->storage_class());
|
|
return member_pointer_type_id;
|
|
}
|
|
|
|
// Returns the storage class of the memory object.
|
|
SpvStorageClass GetStorageClass() const {
|
|
analysis::TypeManager* type_mgr =
|
|
GetVariable()->context()->get_type_mgr();
|
|
const analysis::Pointer* pointer_type =
|
|
type_mgr->GetType(GetVariable()->type_id())->AsPointer();
|
|
return pointer_type->storage_class();
|
|
}
|
|
|
|
private:
|
|
// The variable that owns this memory object.
|
|
ir::Instruction* variable_inst_;
|
|
|
|
// The access chain to reach the particular member the memory object
|
|
// represents. It should be interpreted the same way the indices in an
|
|
// |OpAccessChain| are interpreted.
|
|
std::vector<uint32_t> access_chain_;
|
|
std::vector<uint32_t> GetAccessIds() const;
|
|
};
|
|
|
|
// Returns the memory object being stored to |var_inst| in the store
|
|
// instruction |store_inst|, if one exists, that can be used in place of
|
|
// |var_inst| in all of the loads of |var_inst|. This code is conservative
|
|
// and only identifies very simple cases. If no such memory object can be
|
|
// found, the return value is |nullptr|.
|
|
std::unique_ptr<CopyPropagateArrays::MemoryObject> FindSourceObjectIfPossible(
|
|
ir::Instruction* var_inst, ir::Instruction* store_inst);
|
|
|
|
// Replaces all loads of |var_inst| with a load from |source| instead.
|
|
// |insertion_pos| is a position where it is possible to construct the
|
|
// address of |source| and also dominates all of the loads of |var_inst|.
|
|
void PropagateObject(ir::Instruction* var_inst, MemoryObject* source,
|
|
ir::Instruction* insertion_pos);
|
|
|
|
// Returns true if all of the references to |ptr_inst| can be rewritten and
|
|
// are dominated by |store_inst|.
|
|
bool HasValidReferencesOnly(ir::Instruction* ptr_inst,
|
|
ir::Instruction* store_inst);
|
|
|
|
// Returns a memory object that at one time was equivalent to the value in
|
|
// |result|. If no such memory object exists, the return value is |nullptr|.
|
|
std::unique_ptr<MemoryObject> GetSourceObjectIfAny(uint32_t result);
|
|
|
|
// Returns the memory object that is loaded by |load_inst|. If a memory
|
|
// object cannot be identified, the return value is |nullptr|. The opcode of
|
|
// |load_inst| must be |OpLoad|.
|
|
std::unique_ptr<MemoryObject> BuildMemoryObjectFromLoad(
|
|
ir::Instruction* load_inst);
|
|
|
|
// Returns the memory object that at some point was equivalent to the result
|
|
// of |extract_inst|. If a memory object cannot be identified, the return
|
|
// value is |nullptr|. The opcode of |extract_inst| must be
|
|
// |OpCompositeExtract|.
|
|
std::unique_ptr<MemoryObject> BuildMemoryObjectFromExtract(
|
|
ir::Instruction* extract_inst);
|
|
|
|
// Returns the memory object that at some point was equivalent to the result
|
|
// of |construct_inst|. If a memory object cannot be identified, the return
|
|
// value is |nullptr|. The opcode of |extract_inst| must be
|
|
// |OpCompositeConstruct|.
|
|
std::unique_ptr<MemoryObject> BuildMemoryObjectFromCompositeConstruct(
|
|
ir::Instruction* conststruct_inst);
|
|
|
|
// Return true if |type_id| is a pointer type whose pointee type is an array.
|
|
bool IsPointerToArrayType(uint32_t type_id);
|
|
|
|
// Returns true of there are not stores using |ptr_inst| or something derived
|
|
// from it.
|
|
bool HasNoStores(ir::Instruction* ptr_inst);
|
|
|
|
// Creates an |OpAccessChain| instruction whose result is a pointer the memory
|
|
// represented by |source|. The new instruction will be placed before
|
|
// |insertion_point|. |insertion_point| must be part of a function. Returns
|
|
// the new instruction.
|
|
ir::Instruction* BuildNewAccessChain(ir::Instruction* insertion_point,
|
|
MemoryObject* source) const;
|
|
|
|
// Rewrites all uses of |original_ptr| to use |new_pointer_inst| updating
|
|
// types of other instructions as needed. This function should not be called
|
|
// if |CanUpdateUses(original_ptr_inst, new_pointer_inst->type_id())| returns
|
|
// false.
|
|
void UpdateUses(ir::Instruction* original_ptr_inst,
|
|
ir::Instruction* new_pointer_inst);
|
|
|
|
// Return true if |UpdateUses| is able to change all of the uses of
|
|
// |original_ptr_inst| to |type_id| and still have valid code.
|
|
bool CanUpdateUses(ir::Instruction* original_ptr_inst, uint32_t type_id);
|
|
|
|
// Returns the id whose value is the same as |object_to_copy| except its type
|
|
// is |new_type_id|. Any instructions need to generate this value will be
|
|
// inserted before |insertion_position|.
|
|
uint32_t GenerateCopy(ir::Instruction* object_to_copy, uint32_t new_type_id,
|
|
ir::Instruction* insertion_position);
|
|
|
|
// Returns a store to |var_inst| that writes to the entire variable, and is
|
|
// the only store that does so. Note it does not look through OpAccessChain
|
|
// instruction, so partial stores are not considered.
|
|
ir::Instruction* FindStoreInstruction(const ir::Instruction* var_inst) const;
|
|
};
|
|
|
|
} // namespace opt
|
|
} // namespace spvtools
|
|
|
|
#endif // LIBSPIRV_OPT_COPY_PROP_H_
|