source/opt/cfg_cleanup_pass.cpp \
source/opt/compact_ids_pass.cpp \
source/opt/common_uniform_elim_pass.cpp \
+ source/opt/constants.cpp \
source/opt/dead_branch_elim_pass.cpp \
source/opt/dead_variable_elimination.cpp \
source/opt/decoration_manager.cpp \
cfg.cpp
common_uniform_elim_pass.cpp
compact_ids_pass.cpp
+ constants.cpp
dead_branch_elim_pass.cpp
dead_variable_elimination.cpp
decoration_manager.cpp
true);
if (modified)
- c->SetIdBound(static_cast<uint32_t>(result_id_mapping.size() + 1));
+ c->module()->SetIdBound(
+ static_cast<uint32_t>(result_id_mapping.size() + 1));
return modified ? Status::SuccessWithChange : Status::SuccessWithoutChange;
}
--- /dev/null
+// 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.
+
+#include "constants.h"
+#include "ir_context.h"
+
+#include <unordered_map>
+#include <vector>
+
+namespace spvtools {
+namespace opt {
+namespace analysis {
+
+analysis::Type* ConstantManager::GetType(const ir::Instruction* inst) const {
+ return context()->get_type_mgr()->GetType(inst->type_id());
+}
+
+uint32_t ConstantManager::FindRecordedConstant(
+ const analysis::Constant* c) const {
+ auto iter = const_val_to_id_.find(c);
+ if (iter == const_val_to_id_.end()) {
+ return 0;
+ } else {
+ return iter->second;
+ }
+}
+
+std::vector<const analysis::Constant*> ConstantManager::GetConstantsFromIds(
+ const std::vector<uint32_t>& ids) const {
+ std::vector<const analysis::Constant*> constants;
+ for (uint32_t id : ids) {
+ if (analysis::Constant* c = FindRecordedConstant(id)) {
+ constants.push_back(c);
+ } else {
+ return {};
+ }
+ }
+ return constants;
+}
+
+ir::Instruction* ConstantManager::BuildInstructionAndAddToModule(
+ std::unique_ptr<analysis::Constant> c, ir::Module::inst_iterator* pos) {
+ analysis::Constant* new_const = c.get();
+ uint32_t new_id = context()->TakeNextId();
+ const_val_to_id_[new_const] = new_id;
+ id_to_const_val_[new_id] = std::move(c);
+ auto new_inst = CreateInstruction(new_id, new_const);
+ if (!new_inst) return nullptr;
+ auto* new_inst_ptr = new_inst.get();
+ *pos = pos->InsertBefore(std::move(new_inst));
+ ++(*pos);
+ context()->get_def_use_mgr()->AnalyzeInstDefUse(new_inst_ptr);
+ return new_inst_ptr;
+}
+
+analysis::Constant* ConstantManager::FindRecordedConstant(uint32_t id) const {
+ auto iter = id_to_const_val_.find(id);
+ if (iter == id_to_const_val_.end()) {
+ return nullptr;
+ } else {
+ return iter->second.get();
+ }
+}
+
+std::unique_ptr<analysis::Constant> ConstantManager::CreateConstant(
+ const analysis::Type* type,
+ const std::vector<uint32_t>& literal_words_or_ids) const {
+ std::unique_ptr<analysis::Constant> new_const;
+ if (literal_words_or_ids.size() == 0) {
+ // Constant declared with OpConstantNull
+ return MakeUnique<analysis::NullConstant>(type);
+ } else if (auto* bt = type->AsBool()) {
+ assert(literal_words_or_ids.size() == 1 &&
+ "Bool constant should be declared with one operand");
+ return MakeUnique<analysis::BoolConstant>(bt, literal_words_or_ids.front());
+ } else if (auto* it = type->AsInteger()) {
+ return MakeUnique<analysis::IntConstant>(it, literal_words_or_ids);
+ } else if (auto* ft = type->AsFloat()) {
+ return MakeUnique<analysis::FloatConstant>(ft, literal_words_or_ids);
+ } else if (auto* vt = type->AsVector()) {
+ auto components = GetConstantsFromIds(literal_words_or_ids);
+ if (components.empty()) return nullptr;
+ // All components of VectorConstant must be of type Bool, Integer or Float.
+ if (!std::all_of(components.begin(), components.end(),
+ [](const analysis::Constant* c) {
+ if (c->type()->AsBool() || c->type()->AsInteger() ||
+ c->type()->AsFloat()) {
+ return true;
+ } else {
+ return false;
+ }
+ }))
+ return nullptr;
+ // All components of VectorConstant must be in the same type.
+ const auto* component_type = components.front()->type();
+ if (!std::all_of(components.begin(), components.end(),
+ [&component_type](const analysis::Constant* c) {
+ if (c->type() == component_type) return true;
+ return false;
+ }))
+ return nullptr;
+ return MakeUnique<analysis::VectorConstant>(vt, components);
+ } else if (auto* st = type->AsStruct()) {
+ auto components = GetConstantsFromIds(literal_words_or_ids);
+ if (components.empty()) return nullptr;
+ return MakeUnique<analysis::StructConstant>(st, components);
+ } else if (auto* at = type->AsArray()) {
+ auto components = GetConstantsFromIds(literal_words_or_ids);
+ if (components.empty()) return nullptr;
+ return MakeUnique<analysis::ArrayConstant>(at, components);
+ } else {
+ return nullptr;
+ }
+}
+
+std::unique_ptr<analysis::Constant> ConstantManager::CreateConstantFromInst(
+ ir::Instruction* inst) const {
+ std::vector<uint32_t> literal_words_or_ids;
+ std::unique_ptr<analysis::Constant> new_const;
+
+ // Collect the constant defining literals or component ids.
+ for (uint32_t i = 0; i < inst->NumInOperands(); i++) {
+ literal_words_or_ids.insert(literal_words_or_ids.end(),
+ inst->GetInOperand(i).words.begin(),
+ inst->GetInOperand(i).words.end());
+ }
+
+ switch (inst->opcode()) {
+ // OpConstant{True|Flase} have the value embedded in the opcode. So they
+ // are not handled by the for-loop above. Here we add the value explicitly.
+ case SpvOp::SpvOpConstantTrue:
+ literal_words_or_ids.push_back(true);
+ break;
+ case SpvOp::SpvOpConstantFalse:
+ literal_words_or_ids.push_back(false);
+ break;
+ case SpvOp::SpvOpConstantNull:
+ case SpvOp::SpvOpConstant:
+ case SpvOp::SpvOpConstantComposite:
+ case SpvOp::SpvOpSpecConstantComposite:
+ break;
+ default:
+ return nullptr;
+ }
+ return CreateConstant(GetType(inst), literal_words_or_ids);
+}
+
+std::unique_ptr<ir::Instruction> ConstantManager::CreateInstruction(
+ uint32_t id, analysis::Constant* c) const {
+ if (c->AsNullConstant()) {
+ return MakeUnique<ir::Instruction>(
+ context(), SpvOp::SpvOpConstantNull,
+ context()->get_type_mgr()->GetId(c->type()), id,
+ std::initializer_list<ir::Operand>{});
+ } else if (analysis::BoolConstant* bc = c->AsBoolConstant()) {
+ return MakeUnique<ir::Instruction>(
+ context(),
+ bc->value() ? SpvOp::SpvOpConstantTrue : SpvOp::SpvOpConstantFalse,
+ context()->get_type_mgr()->GetId(c->type()), id,
+ std::initializer_list<ir::Operand>{});
+ } else if (analysis::IntConstant* ic = c->AsIntConstant()) {
+ return MakeUnique<ir::Instruction>(
+ context(), SpvOp::SpvOpConstant,
+ context()->get_type_mgr()->GetId(c->type()), id,
+ std::initializer_list<ir::Operand>{ir::Operand(
+ spv_operand_type_t::SPV_OPERAND_TYPE_TYPED_LITERAL_NUMBER,
+ ic->words())});
+ } else if (analysis::FloatConstant* fc = c->AsFloatConstant()) {
+ return MakeUnique<ir::Instruction>(
+ context(), SpvOp::SpvOpConstant,
+ context()->get_type_mgr()->GetId(c->type()), id,
+ std::initializer_list<ir::Operand>{ir::Operand(
+ spv_operand_type_t::SPV_OPERAND_TYPE_TYPED_LITERAL_NUMBER,
+ fc->words())});
+ } else if (analysis::CompositeConstant* cc = c->AsCompositeConstant()) {
+ return CreateCompositeInstruction(id, cc);
+ } else {
+ return nullptr;
+ }
+}
+
+std::unique_ptr<ir::Instruction> ConstantManager::CreateCompositeInstruction(
+ uint32_t result_id, analysis::CompositeConstant* cc) const {
+ std::vector<ir::Operand> operands;
+ for (const analysis::Constant* component_const : cc->GetComponents()) {
+ uint32_t id = FindRecordedConstant(component_const);
+ if (id == 0) {
+ // Cannot get the id of the component constant, while all components
+ // should have been added to the module prior to the composite constant.
+ // Cannot create OpConstantComposite instruction in this case.
+ return nullptr;
+ }
+ operands.emplace_back(spv_operand_type_t::SPV_OPERAND_TYPE_ID,
+ std::initializer_list<uint32_t>{id});
+ }
+ return MakeUnique<ir::Instruction>(
+ context(), SpvOp::SpvOpConstantComposite,
+ context()->get_type_mgr()->GetId(cc->type()), result_id,
+ std::move(operands));
+}
+
+} // namespace analysis
+} // namespace opt
+} // namespace spvtools
#include <vector>
#include "make_unique.h"
+#include "module.h"
+#include "type_manager.h"
#include "types.h"
namespace spvtools {
}
};
+class IRContext;
+
+// This class represents a pool of constants.
+class ConstantManager {
+ public:
+ ConstantManager(ir::IRContext* ctx) : ctx_(ctx) {}
+
+ ir::IRContext* context() const { return ctx_; }
+
+ // Creates a Constant instance with the given type and a vector of constant
+ // defining words. Returns an unique pointer to the created Constant instance
+ // if the Constant instance can be created successfully. To create scalar
+ // type constants, the vector should contain the constant value in 32 bit
+ // words and the given type must be of type Bool, Integer or Float. To create
+ // composite type constants, the vector should contain the component ids, and
+ // those component ids should have been recorded before as Normal Constants.
+ // And the given type must be of type Struct, Vector or Array. When creating
+ // VectorType Constant instance, the components must be scalars of the same
+ // type, either Bool, Integer or Float. If any of the rules above failed, the
+ // creation will fail and nullptr will be returned. If the vector is empty,
+ // a NullConstant instance will be created with the given type.
+ std::unique_ptr<Constant> CreateConstant(
+ const Type* type,
+ const std::vector<uint32_t>& literal_words_or_ids) const;
+
+ // Creates a Constant instance to hold the constant value of the given
+ // instruction. If the given instruction defines a normal constants whose
+ // value is already known in the module, returns the unique pointer to the
+ // created Constant instance. Otherwise does not create anything and returns a
+ // nullptr.
+ std::unique_ptr<Constant> CreateConstantFromInst(ir::Instruction* inst) const;
+
+ // Creates a constant defining instruction for the given Constant instance
+ // and inserts the instruction at the position specified by the given
+ // instruction iterator. Returns a pointer to the created instruction if
+ // succeeded, otherwise returns a null pointer. The instruction iterator
+ // points to the same instruction before and after the insertion. This is the
+ // only method that actually manages id creation/assignment and instruction
+ // creation/insertion for a new Constant instance.
+ ir::Instruction* BuildInstructionAndAddToModule(
+ std::unique_ptr<analysis::Constant> c, ir::Module::inst_iterator* pos);
+
+ // Creates an instruction with the given result id to declare a constant
+ // represented by the given Constant instance. Returns an unique pointer to
+ // the created instruction if the instruction can be created successfully.
+ // Otherwise, returns a null pointer.
+ std::unique_ptr<ir::Instruction> CreateInstruction(
+ uint32_t result_id, analysis::Constant* c) const;
+
+ // Creates an OpConstantComposite instruction with the given result id and
+ // the CompositeConst instance which represents a composite constant. Returns
+ // an unique pointer to the created instruction if succeeded. Otherwise
+ // returns a null pointer.
+ std::unique_ptr<ir::Instruction> CreateCompositeInstruction(
+ uint32_t result_id, analysis::CompositeConstant* cc) const;
+
+ // A helper function to get the result type of the given instruction. Returns
+ // nullptr if the instruction does not have a type id (type id is 0).
+ analysis::Type* GetType(const ir::Instruction* inst) const;
+
+ // A helper function to get the collected normal constant with the given id.
+ // Returns the pointer to the Constant instance in case it is found.
+ // Otherwise, returns null pointer.
+ analysis::Constant* FindRecordedConstant(uint32_t id) const;
+
+ // A helper function to get the id of a collected constant with the pointer
+ // to the Constant instance. Returns 0 in case the constant is not found.
+ uint32_t FindRecordedConstant(const analysis::Constant* c) const;
+
+ // A helper function to get a vector of Constant instances with the specified
+ // ids. If can not find the Constant instance for any one of the ids, returns
+ // an empty vector.
+ std::vector<const analysis::Constant*> GetConstantsFromIds(
+ const std::vector<uint32_t>& ids) const;
+
+ // Records a new mapping between |inst| and |const_value|.
+ // This updates the two mappings |id_to_const_val_| and |const_val_to_id_|.
+ void MapConstantToInst(std::unique_ptr<analysis::Constant> const_value,
+ ir::Instruction* inst) {
+ const_val_to_id_[const_value.get()] = inst->result_id();
+ id_to_const_val_[inst->result_id()] = std::move(const_value);
+ }
+
+ private:
+ // IR context that owns this constant manager.
+ ir::IRContext* ctx_;
+
+ // A mapping from the result ids of Normal Constants to their
+ // analysis::Constant instances. All Normal Constants in the module, either
+ // existing ones before optimization or the newly generated ones, should have
+ // their Constant instance stored and their result id registered in this map.
+ std::unordered_map<uint32_t, std::unique_ptr<analysis::Constant>>
+ id_to_const_val_;
+
+ // A mapping from the analsis::Constant instance of Normal Contants to their
+ // result id in the module. This is a mirror map of id_to_const_val_. All
+ // Normal Constants that defining instructions in the module should have
+ // their analysis::Constant and their result id registered here.
+ std::unordered_map<const analysis::Constant*, uint32_t> const_val_to_id_;
+};
+
} // namespace analysis
} // namespace opt
} // namespace spvtools
namespace spvtools {
namespace opt {
-FoldSpecConstantOpAndCompositePass::FoldSpecConstantOpAndCompositePass()
- : max_id_(0), type_mgr_(nullptr), id_to_const_val_() {}
-
Pass::Status FoldSpecConstantOpAndCompositePass::Process(
ir::IRContext* irContext) {
Initialize(irContext);
void FoldSpecConstantOpAndCompositePass::Initialize(ir::IRContext* irContext) {
InitializeProcessing(irContext);
- type_mgr_.reset(new analysis::TypeManager(consumer(), *irContext->module()));
- for (const auto& id_def : get_def_use_mgr()->id_to_defs()) {
- max_id_ = std::max(max_id_, id_def.first);
- }
-};
+}
Pass::Status FoldSpecConstantOpAndCompositePass::ProcessImpl(
ir::IRContext* irContext) {
// used in OpSpecConstant{Composite|Op} instructions.
// TODO(qining): If the constant or its type has decoration, we may need
// to skip it.
- if (GetType(inst) && !GetType(inst)->decoration_empty()) continue;
+ if (context()->get_constant_mgr()->GetType(inst) &&
+ !context()->get_constant_mgr()->GetType(inst)->decoration_empty())
+ continue;
switch (SpvOp opcode = inst->opcode()) {
// Records the values of Normal Constants.
case SpvOp::SpvOpConstantTrue:
// Constant will be turned in to a Normal Constant. In that case, a
// Constant instance should also be created successfully and recorded
// in the id_to_const_val_ and const_val_to_id_ mapps.
- if (auto const_value = CreateConstFromInst(inst)) {
+ if (auto const_value =
+ context()->get_constant_mgr()->CreateConstantFromInst(inst)) {
// Need to replace the OpSpecConstantComposite instruction with a
// corresponding OpConstantComposite instruction.
if (opcode == SpvOp::SpvOpSpecConstantComposite) {
inst->SetOpcode(SpvOp::SpvOpConstantComposite);
modified = true;
}
- const_val_to_id_[const_value.get()] = inst->result_id();
- id_to_const_val_[inst->result_id()] = std::move(const_value);
+ context()->get_constant_mgr()->MapConstantToInst(
+ std::move(const_value), inst);
}
break;
}
// Note that for OpSpecConstantOp, the second in-operand is the first id
// operand. The first in-operand is the spec opcode.
analysis::Constant* first_operand_const =
- FindRecordedConst(inst->GetSingleWordInOperand(1));
+ context()->get_constant_mgr()->FindRecordedConstant(
+ inst->GetSingleWordInOperand(1));
if (!first_operand_const) return nullptr;
const analysis::Constant* current_const = first_operand_const;
// Case 2: current constant is a constant created with OpConstantNull.
// Because components of a NullConstant are always NullConstants, we can
// return early with a NullConstant in the result type.
- return BuildInstructionAndAddToModule(CreateConst(GetType(inst), {}),
- pos);
+ return context()->get_constant_mgr()->BuildInstructionAndAddToModule(
+ context()->get_constant_mgr()->CreateConstant(
+ context()->get_constant_mgr()->GetType(inst), {}),
+ pos);
} else {
// Dereferencing a non-composite constant. Invalid case.
return nullptr;
}
}
- return BuildInstructionAndAddToModule(current_const->Copy(), pos);
+ return context()->get_constant_mgr()->BuildInstructionAndAddToModule(
+ current_const->Copy(), pos);
}
ir::Instruction* FoldSpecConstantOpAndCompositePass::DoVectorShuffle(
ir::Module::inst_iterator* pos) {
ir::Instruction* inst = &**pos;
- analysis::Vector* result_vec_type = GetType(inst)->AsVector();
+ analysis::Vector* result_vec_type =
+ context()->get_constant_mgr()->GetType(inst)->AsVector();
assert(inst->NumInOperands() - 1 > 2 &&
"OpSpecConstantOp DoVectorShuffle instruction requires more than 2 "
"operands (2 vector ids and at least one literal operand");
assert(inst->GetInOperand(i).type == SPV_OPERAND_TYPE_ID &&
"The vector operand must have a SPV_OPERAND_TYPE_ID type");
uint32_t operand_id = inst->GetSingleWordInOperand(i);
- analysis::Constant* operand_const = FindRecordedConst(operand_id);
+ analysis::Constant* operand_const =
+ context()->get_constant_mgr()->FindRecordedConstant(operand_id);
if (!operand_const) return nullptr;
const analysis::Type* operand_type = operand_const->type();
assert(operand_type->AsVector() &&
if (!null_component_constants) {
const analysis::Type* component_type =
operand_type->AsVector()->element_type();
- null_component_constants = CreateConst(component_type, {});
+ null_component_constants =
+ context()->get_constant_mgr()->CreateConstant(component_type, {});
}
// Append the null scalar consts to the concatenated components
// vector.
// must be added to the module before the dependee composite constants to
// satisfy SSA def-use dominance.
if (null_component_constants) {
- BuildInstructionAndAddToModule(std::move(null_component_constants), pos);
+ context()->get_constant_mgr()->BuildInstructionAndAddToModule(
+ std::move(null_component_constants), pos);
}
// Create the new vector constant with the selected components.
std::vector<const analysis::Constant*> selected_components;
}
auto new_vec_const = MakeUnique<analysis::VectorConstant>(
result_vec_type, selected_components);
- return BuildInstructionAndAddToModule(std::move(new_vec_const), pos);
+ return context()->get_constant_mgr()->BuildInstructionAndAddToModule(
+ std::move(new_vec_const), pos);
}
namespace {
ir::Instruction* FoldSpecConstantOpAndCompositePass::DoComponentWiseOperation(
ir::Module::inst_iterator* pos) {
const ir::Instruction* inst = &**pos;
- const analysis::Type* result_type = GetType(inst);
+ const analysis::Type* result_type =
+ context()->get_constant_mgr()->GetType(inst);
SpvOp spec_opcode = static_cast<SpvOp>(inst->GetSingleWordInOperand(0));
// Check and collect operands.
std::vector<analysis::Constant*> operands;
- if (!std::all_of(inst->cbegin(), inst->cend(),
- [&operands, this](const ir::Operand& o) {
- // skip the operands that is not an id.
- if (o.type != spv_operand_type_t::SPV_OPERAND_TYPE_ID)
- return true;
- uint32_t id = o.words.front();
- if (analysis::Constant* c = FindRecordedConst(id)) {
- if (IsValidTypeForComponentWiseOperation(c->type())) {
- operands.push_back(c);
- return true;
- }
- }
- return false;
- }))
+ if (!std::all_of(
+ inst->cbegin(), inst->cend(),
+ [&operands, this](const ir::Operand& o) {
+ // skip the operands that is not an id.
+ if (o.type != spv_operand_type_t::SPV_OPERAND_TYPE_ID) return true;
+ uint32_t id = o.words.front();
+ if (analysis::Constant* c =
+ context()->get_constant_mgr()->FindRecordedConstant(id)) {
+ if (IsValidTypeForComponentWiseOperation(c->type())) {
+ operands.push_back(c);
+ return true;
+ }
+ }
+ return false;
+ }))
return nullptr;
if (result_type->AsInteger() || result_type->AsBool()) {
// Scalar operation
uint32_t result_val = FoldScalars(spec_opcode, operands);
- auto result_const = CreateConst(result_type, {result_val});
- return BuildInstructionAndAddToModule(std::move(result_const), pos);
+ auto result_const = context()->get_constant_mgr()->CreateConstant(
+ result_type, {result_val});
+ return context()->get_constant_mgr()->BuildInstructionAndAddToModule(
+ std::move(result_const), pos);
} else if (result_type->AsVector()) {
// Vector operation
const analysis::Type* element_type =
FoldVectors(spec_opcode, num_dims, operands);
std::vector<const analysis::Constant*> result_vector_components;
for (uint32_t r : result_vec) {
- if (auto rc = CreateConst(element_type, {r})) {
+ if (auto rc = context()->get_constant_mgr()->CreateConstant(element_type,
+ {r})) {
result_vector_components.push_back(rc.get());
- if (!BuildInstructionAndAddToModule(std::move(rc), pos)) {
+ if (!context()->get_constant_mgr()->BuildInstructionAndAddToModule(
+ std::move(rc), pos)) {
assert(false &&
"Failed to build and insert constant declaring instruction "
"for the given vector component constant");
}
auto new_vec_const = MakeUnique<analysis::VectorConstant>(
result_type->AsVector(), result_vector_components);
- return BuildInstructionAndAddToModule(std::move(new_vec_const), pos);
+ return context()->get_constant_mgr()->BuildInstructionAndAddToModule(
+ std::move(new_vec_const), pos);
} else {
// Cannot process invalid component wise operation. The result of component
// wise operation must be of integer or bool scalar or vector of
}
}
-ir::Instruction*
-FoldSpecConstantOpAndCompositePass::BuildInstructionAndAddToModule(
- std::unique_ptr<analysis::Constant> c, ir::Module::inst_iterator* pos) {
- analysis::Constant* new_const = c.get();
- uint32_t new_id = ++max_id_;
- get_module()->SetIdBound(new_id + 1);
- const_val_to_id_[new_const] = new_id;
- id_to_const_val_[new_id] = std::move(c);
- auto new_inst = CreateInstruction(new_id, new_const);
- if (!new_inst) return nullptr;
- auto* new_inst_ptr = new_inst.get();
- *pos = pos->InsertBefore(std::move(new_inst));
- ++(*pos);
- get_def_use_mgr()->AnalyzeInstDefUse(new_inst_ptr);
- return new_inst_ptr;
-}
-
-std::unique_ptr<analysis::Constant>
-FoldSpecConstantOpAndCompositePass::CreateConstFromInst(ir::Instruction* inst) {
- std::vector<uint32_t> literal_words_or_ids;
- std::unique_ptr<analysis::Constant> new_const;
- // Collect the constant defining literals or component ids.
- for (uint32_t i = 0; i < inst->NumInOperands(); i++) {
- literal_words_or_ids.insert(literal_words_or_ids.end(),
- inst->GetInOperand(i).words.begin(),
- inst->GetInOperand(i).words.end());
- }
- switch (inst->opcode()) {
- // OpConstant{True|Flase} have the value embedded in the opcode. So they
- // are not handled by the for-loop above. Here we add the value explicitly.
- case SpvOp::SpvOpConstantTrue:
- literal_words_or_ids.push_back(true);
- break;
- case SpvOp::SpvOpConstantFalse:
- literal_words_or_ids.push_back(false);
- break;
- case SpvOp::SpvOpConstantNull:
- case SpvOp::SpvOpConstant:
- case SpvOp::SpvOpConstantComposite:
- case SpvOp::SpvOpSpecConstantComposite:
- break;
- default:
- return nullptr;
- }
- return CreateConst(GetType(inst), literal_words_or_ids);
-}
-
-analysis::Constant* FoldSpecConstantOpAndCompositePass::FindRecordedConst(
- uint32_t id) {
- auto iter = id_to_const_val_.find(id);
- if (iter == id_to_const_val_.end()) {
- return nullptr;
- } else {
- return iter->second.get();
- }
-}
-
-uint32_t FoldSpecConstantOpAndCompositePass::FindRecordedConst(
- const analysis::Constant* c) {
- auto iter = const_val_to_id_.find(c);
- if (iter == const_val_to_id_.end()) {
- return 0;
- } else {
- return iter->second;
- }
-}
-
-std::vector<const analysis::Constant*>
-FoldSpecConstantOpAndCompositePass::GetConstsFromIds(
- const std::vector<uint32_t>& ids) {
- std::vector<const analysis::Constant*> constants;
- for (uint32_t id : ids) {
- if (analysis::Constant* c = FindRecordedConst(id)) {
- constants.push_back(c);
- } else {
- return {};
- }
- }
- return constants;
-}
-
-std::unique_ptr<analysis::Constant>
-FoldSpecConstantOpAndCompositePass::CreateConst(
- const analysis::Type* type,
- const std::vector<uint32_t>& literal_words_or_ids) {
- std::unique_ptr<analysis::Constant> new_const;
- if (literal_words_or_ids.size() == 0) {
- // Constant declared with OpConstantNull
- return MakeUnique<analysis::NullConstant>(type);
- } else if (auto* bt = type->AsBool()) {
- assert(literal_words_or_ids.size() == 1 &&
- "Bool constant should be declared with one operand");
- return MakeUnique<analysis::BoolConstant>(bt, literal_words_or_ids.front());
- } else if (auto* it = type->AsInteger()) {
- return MakeUnique<analysis::IntConstant>(it, literal_words_or_ids);
- } else if (auto* ft = type->AsFloat()) {
- return MakeUnique<analysis::FloatConstant>(ft, literal_words_or_ids);
- } else if (auto* vt = type->AsVector()) {
- auto components = GetConstsFromIds(literal_words_or_ids);
- if (components.empty()) return nullptr;
- // All components of VectorConstant must be of type Bool, Integer or Float.
- if (!std::all_of(components.begin(), components.end(),
- [](const analysis::Constant* c) {
- if (c->type()->AsBool() || c->type()->AsInteger() ||
- c->type()->AsFloat()) {
- return true;
- } else {
- return false;
- }
- }))
- return nullptr;
- // All components of VectorConstant must be in the same type.
- const auto* component_type = components.front()->type();
- if (!std::all_of(components.begin(), components.end(),
- [&component_type](const analysis::Constant* c) {
- if (c->type() == component_type) return true;
- return false;
- }))
- return nullptr;
- return MakeUnique<analysis::VectorConstant>(vt, components);
- } else if (auto* st = type->AsStruct()) {
- auto components = GetConstsFromIds(literal_words_or_ids);
- if (components.empty()) return nullptr;
- return MakeUnique<analysis::StructConstant>(st, components);
- } else if (auto* at = type->AsArray()) {
- auto components = GetConstsFromIds(literal_words_or_ids);
- if (components.empty()) return nullptr;
- return MakeUnique<analysis::ArrayConstant>(at, components);
- } else {
- return nullptr;
- }
-}
-
-std::vector<ir::Operand> BuildOperandsFromIds(
- const std::vector<uint32_t>& ids) {
- std::vector<ir::Operand> operands;
- for (uint32_t id : ids) {
- operands.emplace_back(spv_operand_type_t::SPV_OPERAND_TYPE_ID,
- std::initializer_list<uint32_t>{id});
- }
- return operands;
-}
-
-std::unique_ptr<ir::Instruction>
-FoldSpecConstantOpAndCompositePass::CreateInstruction(uint32_t id,
- analysis::Constant* c) {
- if (c->AsNullConstant()) {
- return MakeUnique<ir::Instruction>(context(), SpvOp::SpvOpConstantNull,
- type_mgr_->GetId(c->type()), id,
- std::initializer_list<ir::Operand>{});
- } else if (analysis::BoolConstant* bc = c->AsBoolConstant()) {
- return MakeUnique<ir::Instruction>(
- context(),
- bc->value() ? SpvOp::SpvOpConstantTrue : SpvOp::SpvOpConstantFalse,
- type_mgr_->GetId(c->type()), id, std::initializer_list<ir::Operand>{});
- } else if (analysis::IntConstant* ic = c->AsIntConstant()) {
- return MakeUnique<ir::Instruction>(
- context(), SpvOp::SpvOpConstant, type_mgr_->GetId(c->type()), id,
- std::initializer_list<ir::Operand>{ir::Operand(
- spv_operand_type_t::SPV_OPERAND_TYPE_TYPED_LITERAL_NUMBER,
- ic->words())});
- } else if (analysis::FloatConstant* fc = c->AsFloatConstant()) {
- return MakeUnique<ir::Instruction>(
- context(), SpvOp::SpvOpConstant, type_mgr_->GetId(c->type()), id,
- std::initializer_list<ir::Operand>{ir::Operand(
- spv_operand_type_t::SPV_OPERAND_TYPE_TYPED_LITERAL_NUMBER,
- fc->words())});
- } else if (analysis::CompositeConstant* cc = c->AsCompositeConstant()) {
- return CreateCompositeInstruction(id, cc);
- } else {
- return nullptr;
- }
-}
-
-std::unique_ptr<ir::Instruction>
-FoldSpecConstantOpAndCompositePass::CreateCompositeInstruction(
- uint32_t result_id, analysis::CompositeConstant* cc) {
- std::vector<ir::Operand> operands;
- for (const analysis::Constant* component_const : cc->GetComponents()) {
- uint32_t id = FindRecordedConst(component_const);
- if (id == 0) {
- // Cannot get the id of the component constant, while all components
- // should have been added to the module prior to the composite constant.
- // Cannot create OpConstantComposite instruction in this case.
- return nullptr;
- }
- operands.emplace_back(spv_operand_type_t::SPV_OPERAND_TYPE_ID,
- std::initializer_list<uint32_t>{id});
- }
- return MakeUnique<ir::Instruction>(context(), SpvOp::SpvOpConstantComposite,
- type_mgr_->GetId(cc->type()), result_id,
- std::move(operands));
-}
-
} // namespace opt
} // namespace spvtools
// See optimizer.hpp for documentation.
class FoldSpecConstantOpAndCompositePass : public Pass {
public:
- FoldSpecConstantOpAndCompositePass();
+ FoldSpecConstantOpAndCompositePass() = default;
const char* name() const override { return "fold-spec-const-op-composite"; }
// if succeeded, otherwise return nullptr.
ir::Instruction* DoComponentWiseOperation(
ir::Module::inst_iterator* inst_iter_ptr);
-
- // Creates a constant defining instruction for the given Constant instance
- // and inserts the instruction at the position specified by the given
- // instruction iterator. Returns a pointer to the created instruction if
- // succeeded, otherwise returns a null pointer. The instruction iterator
- // points to the same instruction before and after the insertion. This is the
- // only method that actually manages id creation/assignment and instruction
- // creation/insertion for a new Constant instance.
- ir::Instruction* BuildInstructionAndAddToModule(
- std::unique_ptr<analysis::Constant> c, ir::Module::inst_iterator* pos);
-
- // Creates a Constant instance to hold the constant value of the given
- // instruction. If the given instruction defines a normal constants whose
- // value is already known in the module, returns the unique pointer to the
- // created Constant instance. Otherwise does not create anything and returns a
- // nullptr.
- std::unique_ptr<analysis::Constant> CreateConstFromInst(
- ir::Instruction* inst);
-
- // Creates a Constant instance with the given type and a vector of constant
- // defining words. Returns an unique pointer to the created Constant instance
- // if the Constant instance can be created successfully. To create scalar
- // type constants, the vector should contain the constant value in 32 bit
- // words and the given type must be of type Bool, Integer or Float. To create
- // composite type constants, the vector should contain the component ids, and
- // those component ids should have been recorded before as Normal Constants.
- // And the given type must be of type Struct, Vector or Array. When creating
- // VectorType Constant instance, the components must be scalars of the same
- // type, either Bool, Integer or Float. If any of the rules above failed, the
- // creation will fail and nullptr will be returned. If the vector is empty,
- // a NullConstant instance will be created with the given type.
- std::unique_ptr<analysis::Constant> CreateConst(
- const analysis::Type* type,
- const std::vector<uint32_t>& literal_words_or_ids);
-
- // Creates an instruction with the given result id to declare a constant
- // represented by the given Constant instance. Returns an unique pointer to
- // the created instruction if the instruction can be created successfully.
- // Otherwise, returns a null pointer.
- std::unique_ptr<ir::Instruction> CreateInstruction(uint32_t result_id,
- analysis::Constant* c);
-
- // Creates an OpConstantComposite instruction with the given result id and
- // the CompositeConst instance which represents a composite constant. Returns
- // an unique pointer to the created instruction if succeeded. Otherwise
- // returns a null pointer.
- std::unique_ptr<ir::Instruction> CreateCompositeInstruction(
- uint32_t result_id, analysis::CompositeConstant* cc);
-
- // A helper function to get the collected normal constant with the given id.
- // Returns the pointer to the Constant instance in case it is found.
- // Otherwise, returns null pointer.
- analysis::Constant* FindRecordedConst(uint32_t id);
- // A helper function to get the id of a collected constant with the pointer
- // to the Constant instance. Returns 0 in case the constant is not found.
- uint32_t FindRecordedConst(const analysis::Constant* c);
-
- // A helper function to get a vector of Constant instances with the specified
- // ids. If can not find the Constant instance for any one of the ids, returns
- // an empty vector.
- std::vector<const analysis::Constant*> GetConstsFromIds(
- const std::vector<uint32_t>& ids);
-
- // A helper function to get the result type of the given instrution. Returns
- // nullptr if the instruction does not have a type id (type id is 0).
- analysis::Type* GetType(const ir::Instruction* inst) {
- return type_mgr_->GetType(inst->type_id());
- }
-
- // The maximum used ID.
- uint32_t max_id_;
-
- // Type manager
- std::unique_ptr<analysis::TypeManager> type_mgr_;
-
- // A mapping from the result ids of Normal Constants to their
- // analysis::Constant instances. All Normal Constants in the module, either
- // existing ones before optimization or the newly generated ones, should have
- // their Constant instance stored and their result id registered in this map.
- std::unordered_map<uint32_t, std::unique_ptr<analysis::Constant>>
- id_to_const_val_;
- // A mapping from the analsis::Constant instance of Normal Contants to their
- // result id in the module. This is a mirror map of id_to_const_val_. All
- // Normal Constants that defining instructions in the module should have
- // their analysis::Constant and their result id registered here.
- std::unordered_map<const analysis::Constant*, uint32_t> const_val_to_id_;
};
} // namespace opt
// limitations under the License.
#include "ir_context.h"
-#include <spirv/1.0/GLSL.std.450.h>
-#include <cstring>
#include "log.h"
#include "mem_pass.h"
+#include "spirv/1.0/GLSL.std.450.h"
+
+#include <cstring>
namespace spvtools {
namespace ir {
#define SPIRV_TOOLS_IR_CONTEXT_H
#include "cfg.h"
+#include "constants.h"
#include "decoration_manager.h"
#include "def_use_manager.h"
#include "dominator_analysis.h"
#include "module.h"
+#include "type_manager.h"
#include <algorithm>
#include <iostream>
friend inline Analysis operator<<(Analysis a, int shift);
friend inline Analysis& operator<<=(Analysis& a, int shift);
- // Create an |IRContext| that contains an owned |Module|
+ // Creates an |IRContext| that contains an owned |Module|
IRContext(spvtools::MessageConsumer c)
: unique_id_(0),
module_(new Module()),
consumer_(std::move(c)),
def_use_mgr_(nullptr),
- valid_analyses_(kAnalysisNone) {
+ valid_analyses_(kAnalysisNone),
+ constant_mgr_(nullptr),
+ type_mgr_(nullptr) {
module_->SetContext(this);
}
module_(std::move(m)),
consumer_(std::move(c)),
def_use_mgr_(nullptr),
- valid_analyses_(kAnalysisNone) {
+ valid_analyses_(kAnalysisNone),
+ constant_mgr_(nullptr),
+ type_mgr_(nullptr) {
module_->SetContext(this);
InitializeCombinators();
}
Module* module() const { return module_.get(); }
- inline void SetIdBound(uint32_t i);
- inline uint32_t IdBound() const;
-
// Returns a vector of pointers to constant-creation instructions in this
// context.
inline std::vector<Instruction*> GetConstants();
return decoration_mgr_.get();
};
+ // Returns a pointer to the constant manager. If no constant manager has been
+ // created yet, it creates one. NOTE: Once created, the constant manager
+ // remains active and it is never re-built.
+ opt::analysis::ConstantManager* get_constant_mgr() {
+ if (!constant_mgr_)
+ constant_mgr_.reset(new opt::analysis::ConstantManager(this));
+ return constant_mgr_.get();
+ }
+
+ // Returns a pointer to the type manager. If no type manager has been created
+ // yet, it creates one. NOTE: Once created, the type manager remains active it
+ // is never re-built.
+ opt::analysis::TypeManager* get_type_mgr() {
+ if (!type_mgr_)
+ type_mgr_.reset(new opt::analysis::TypeManager(consumer(), *module()));
+ return type_mgr_.get();
+ }
+
// Sets the message consumer to the given |consumer|. |consumer| which will be
// invoked every time there is a message to be communicated to the outside.
void SetMessageConsumer(spvtools::MessageConsumer c) {
post_dominator_trees_.erase(f);
}
+ // Return the next available SSA id and increment it.
+ inline uint32_t TakeNextId() { return module()->TakeNextIdBound(); }
+
private:
// Builds the def-use manager from scratch, even if it was already valid.
void BuildDefUseManager() {
// Add the combinator opcode for the given extension to combinator_ops_.
void AddCombinatorsForExtension(ir::Instruction* extension);
- // An unique identifier for this instruction. Can be used to order
+ // An unique identifier for instructions in |module_|. Can be used to order
// instructions in a container.
//
// This member is initialized to 0, but always issues this value plus one.
// Therefore, 0 is not a valid unique id for an instruction.
uint32_t unique_id_;
+ // The module being processed within this IR context.
std::unique_ptr<Module> module_;
+
+ // A message consumer for diagnostics.
spvtools::MessageConsumer consumer_;
+
+ // The def-use manager for |module_|.
std::unique_ptr<opt::analysis::DefUseManager> def_use_mgr_;
+
+ // The instruction decoration manager for |module_|.
std::unique_ptr<opt::analysis::DecorationManager> decoration_mgr_;
// A map from instructions the the basic block they belong to. This mapping is
std::map<const ir::Function*, opt::DominatorAnalysis> dominator_trees_;
std::map<const ir::Function*, opt::PostDominatorAnalysis>
post_dominator_trees_;
+
+ // Constant manager for |module_|.
+ std::unique_ptr<opt::analysis::ConstantManager> constant_mgr_;
+
+ // Type manager for |module_|.
+ std::unique_ptr<opt::analysis::TypeManager> type_mgr_;
};
inline ir::IRContext::Analysis operator|(ir::IRContext::Analysis lhs,
return a;
}
-void IRContext::SetIdBound(uint32_t i) { module_->SetIdBound(i); }
-
-uint32_t IRContext::IdBound() const { return module()->IdBound(); }
-
std::vector<Instruction*> spvtools::ir::IRContext::GetConstants() {
return module()->GetConstants();
}
// Sets the header to the given |header|.
void SetHeader(const ModuleHeader& header) { header_ = header; }
+
// Sets the Id bound.
void SetIdBound(uint32_t bound) { header_.bound = bound; }
+
// Returns the Id bound.
uint32_t IdBound() { return header_.bound; }
+
+ // Returns the current Id bound and increases it to the next available value.
+ uint32_t TakeNextIdBound() { return header_.bound++; }
+
// Appends a capability instruction to this module.
inline void AddCapability(std::unique_ptr<Instruction> c);
+
// Appends an extension instruction to this module.
inline void AddExtension(std::unique_ptr<Instruction> e);
+
// Appends an extended instruction set instruction to this module.
inline void AddExtInstImport(std::unique_ptr<Instruction> e);
+
// Set the memory model for this module.
inline void SetMemoryModel(std::unique_ptr<Instruction> m);
+
// Appends an entry point instruction to this module.
inline void AddEntryPoint(std::unique_ptr<Instruction> e);
+
// Appends an execution mode instruction to this module.
inline void AddExecutionMode(std::unique_ptr<Instruction> e);
+
// Appends a debug 1 instruction (excluding OpLine & OpNoLine) to this module.
// "debug 1" instructions are the ones in layout section 7.a), see section
// 2.4 Logical Layout of a Module from the SPIR-V specification.
inline void AddDebug1Inst(std::unique_ptr<Instruction> d);
+
// Appends a debug 2 instruction (excluding OpLine & OpNoLine) to this module.
// "debug 2" instructions are the ones in layout section 7.b), see section
// 2.4 Logical Layout of a Module from the SPIR-V specification.
inline void AddDebug2Inst(std::unique_ptr<Instruction> d);
+
// Appends a debug 3 instruction (OpModuleProcessed) to this module.
// This is due to decision by the SPIR Working Group, pending publication.
inline void AddDebug3Inst(std::unique_ptr<Instruction> d);
+
// Appends an annotation instruction to this module.
inline void AddAnnotationInst(std::unique_ptr<Instruction> a);
+
// Appends a type-declaration instruction to this module.
inline void AddType(std::unique_ptr<Instruction> t);
+
// Appends a constant, global variable, or OpUndef instruction to this module.
inline void AddGlobalValue(std::unique_ptr<Instruction> v);
+
// Appends a function to this module.
inline void AddFunction(std::unique_ptr<Function> f);
} // namespace
-Pass::Pass() : consumer_(nullptr), next_id_(0), context_(nullptr) {}
+Pass::Pass() : consumer_(nullptr), context_(nullptr) {}
void Pass::AddCalls(ir::Function* func, std::queue<uint32_t>* todo) {
for (auto bi = func->begin(); bi != func->end(); ++bi)
protected:
// Initialize basic data structures for the pass. This sets up the def-use
- // manager, module and other attributes. TODO(dnovillo): Some of this should
- // be done during pass instantiation. Other things should be outside the pass
- // altogether (e.g., def-use manager).
- virtual void InitializeProcessing(ir::IRContext* c) {
- context_ = c;
- next_id_ = context_->IdBound();
- }
+ // manager, module and other attributes.
+ virtual void InitializeProcessing(ir::IRContext* c) { context_ = c; }
// Processes the given |module|. Returns Status::Failure if errors occur when
// processing. Returns the corresponding Status::Success if processing is
// Return type id for |ptrInst|'s pointee
uint32_t GetPointeeTypeId(const ir::Instruction* ptrInst) const;
- // Return the next available Id and increment it.
- inline uint32_t TakeNextId() {
- assert(context_ && next_id_ > 0);
- uint32_t retval = next_id_++;
- context_->SetIdBound(next_id_);
- return retval;
- }
+ // Return the next available SSA id and increment it.
+ uint32_t TakeNextId() { return context_->TakeNextId(); }
private:
MessageConsumer consumer_; // Message consumer.
- // Next unused ID
- uint32_t next_id_;
-
// The context that this pass belongs to.
ir::IRContext* context_;
};
}
// Set the Id bound in the header in case a pass forgot to do so.
+ //
+ // TODO(dnovillo): This should be unnecessary and automatically maintained by
+ // the IRContext.
if (status == Pass::Status::SuccessWithChange) {
- context->SetIdBound(context->module()->ComputeIdBound());
+ context->module()->SetIdBound(context->module()->ComputeIdBound());
}
passes_.clear();
return status;
// Returns the number of forward pointer types hold in this manager.
size_t NumForwardPointers() const { return forward_pointers_.size(); }
+ // Analyzes the types and decorations on types in the given |module|.
+ // TODO(dnovillo): This should be private and the type manager should know how
+ // to update itself when new types are added
+ // (https://github.com/KhronosGroup/SPIRV-Tools/issues/1071).
+ void AnalyzeTypes(const spvtools::ir::Module& module);
+
private:
using TypeToIdMap = std::unordered_map<const Type*, uint32_t>;
using ForwardPointerVector = std::vector<std::unique_ptr<ForwardPointer>>;
- // Analyzes the types and decorations on types in the given |module|.
- void AnalyzeTypes(const spvtools::ir::Module& module);
-
// Creates and returns a type from the given SPIR-V |inst|. Returns nullptr if
// the given instruction is not for defining a type.
Type* RecordIfTypeDefinition(const spvtools::ir::Instruction& inst);