Add a new constant manager class.

This patch adds a new constant manager class to interface with
analysis::Constant.  The new constant manager lives in ir::IRContext
together with the type manager (analysis::TypeManager).

The new analysis::ConstantManager is used by the spec constant folder
and the constant propagator (in progress).

Another cleanup introduced by this patch removes the ID management from
the fold spec constant pass, and ir::IRContext and moves it to
ir::Module. SSA IDs were maintained by IRContext and Module.  That's
pointless and leads to mismatch IDs. Fixed by moving all the bookkeeping
to ir::Module.
This commit is contained in:
Diego Novillo 2017-12-07 16:42:27 -05:00
parent 5d602abd66
commit 241dcacc04
14 changed files with 456 additions and 358 deletions

View File

@ -58,6 +58,7 @@ SPVTOOLS_OPT_SRC_FILES := \
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 \

View File

@ -75,6 +75,7 @@ add_library(SPIRV-Tools-opt
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

View File

@ -64,7 +64,8 @@ Pass::Status CompactIdsPass::Process(ir::IRContext* c) {
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;
}

215
source/opt/constants.cpp Normal file
View File

@ -0,0 +1,215 @@
// 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

View File

@ -20,6 +20,8 @@
#include <vector>
#include "make_unique.h"
#include "module.h"
#include "type_manager.h"
#include "types.h"
namespace spvtools {
@ -284,6 +286,107 @@ class NullConstant : public Constant {
}
};
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

View File

@ -26,9 +26,6 @@
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);
@ -37,11 +34,7 @@ Pass::Status FoldSpecConstantOpAndCompositePass::Process(
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) {
@ -80,7 +73,9 @@ Pass::Status FoldSpecConstantOpAndCompositePass::ProcessImpl(
// 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:
@ -96,15 +91,16 @@ Pass::Status FoldSpecConstantOpAndCompositePass::ProcessImpl(
// 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;
}
@ -179,7 +175,8 @@ ir::Instruction* FoldSpecConstantOpAndCompositePass::DoCompositeExtract(
// 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;
@ -195,20 +192,24 @@ ir::Instruction* FoldSpecConstantOpAndCompositePass::DoCompositeExtract(
// 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");
@ -229,7 +230,8 @@ ir::Instruction* FoldSpecConstantOpAndCompositePass::DoVectorShuffle(
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() &&
@ -246,7 +248,8 @@ ir::Instruction* FoldSpecConstantOpAndCompositePass::DoVectorShuffle(
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.
@ -262,7 +265,8 @@ ir::Instruction* FoldSpecConstantOpAndCompositePass::DoVectorShuffle(
// 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;
@ -276,7 +280,8 @@ ir::Instruction* FoldSpecConstantOpAndCompositePass::DoVectorShuffle(
}
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 {
@ -306,32 +311,36 @@ bool IsValidTypeForComponentWiseOperation(const analysis::Type* type) {
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 =
@ -341,9 +350,11 @@ ir::Instruction* FoldSpecConstantOpAndCompositePass::DoComponentWiseOperation(
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");
@ -354,7 +365,8 @@ ir::Instruction* FoldSpecConstantOpAndCompositePass::DoComponentWiseOperation(
}
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
@ -363,199 +375,5 @@ ir::Instruction* FoldSpecConstantOpAndCompositePass::DoComponentWiseOperation(
}
}
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

View File

@ -32,7 +32,7 @@ namespace opt {
// See optimizer.hpp for documentation.
class FoldSpecConstantOpAndCompositePass : public Pass {
public:
FoldSpecConstantOpAndCompositePass();
FoldSpecConstantOpAndCompositePass() = default;
const char* name() const override { return "fold-spec-const-op-composite"; }
@ -79,92 +79,6 @@ class FoldSpecConstantOpAndCompositePass : public Pass {
// 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

View File

@ -13,10 +13,11 @@
// 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 {

View File

@ -16,10 +16,12 @@
#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>
@ -59,13 +61,15 @@ class IRContext {
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);
}
@ -74,15 +78,14 @@ class IRContext {
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();
@ -204,6 +207,24 @@ class IRContext {
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) {
@ -327,6 +348,9 @@ class IRContext {
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() {
@ -367,16 +391,23 @@ class IRContext {
// 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
@ -401,6 +432,12 @@ class IRContext {
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,
@ -427,10 +464,6 @@ inline ir::IRContext::Analysis& operator<<=(ir::IRContext::Analysis& a,
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();
}

View File

@ -52,39 +52,57 @@ class Module {
// 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);

View File

@ -28,7 +28,7 @@ const uint32_t kTypePointerTypeIdInIdx = 1;
} // 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)

View File

@ -128,13 +128,8 @@ class Pass {
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
@ -144,20 +139,12 @@ class Pass {
// 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_;
};

View File

@ -27,8 +27,11 @@ Pass::Status PassManager::Run(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;

View File

@ -62,13 +62,16 @@ class TypeManager {
// 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);