| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987 |
- // 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.
- #include "source/opt/scalar_analysis.h"
- #include <functional>
- #include <string>
- #include <utility>
- #include "source/opt/ir_context.h"
- // Transforms a given scalar operation instruction into a DAG representation.
- //
- // 1. Take an instruction and traverse its operands until we reach a
- // constant node or an instruction which we do not know how to compute the
- // value, such as a load.
- //
- // 2. Create a new node for each instruction traversed and build the nodes for
- // the in operands of that instruction as well.
- //
- // 3. Add the operand nodes as children of the first and hash the node. Use the
- // hash to see if the node is already in the cache. We ensure the children are
- // always in sorted order so that two nodes with the same children but inserted
- // in a different order have the same hash and so that the overloaded operator==
- // will return true. If the node is already in the cache return the cached
- // version instead.
- //
- // 4. The created DAG can then be simplified by
- // ScalarAnalysis::SimplifyExpression, implemented in
- // scalar_analysis_simplification.cpp. See that file for further information on
- // the simplification process.
- //
- namespace spvtools {
- namespace opt {
- uint32_t SENode::NumberOfNodes = 0;
- ScalarEvolutionAnalysis::ScalarEvolutionAnalysis(IRContext* context)
- : context_(context), pretend_equal_{} {
- // Create and cached the CantComputeNode.
- cached_cant_compute_ =
- GetCachedOrAdd(std::unique_ptr<SECantCompute>(new SECantCompute(this)));
- }
- SENode* ScalarEvolutionAnalysis::CreateNegation(SENode* operand) {
- // If operand is can't compute then the whole graph is can't compute.
- if (operand->IsCantCompute()) return CreateCantComputeNode();
- if (operand->GetType() == SENode::Constant) {
- return CreateConstant(-operand->AsSEConstantNode()->FoldToSingleValue());
- }
- std::unique_ptr<SENode> negation_node{new SENegative(this)};
- negation_node->AddChild(operand);
- return GetCachedOrAdd(std::move(negation_node));
- }
- SENode* ScalarEvolutionAnalysis::CreateConstant(int64_t integer) {
- return GetCachedOrAdd(
- std::unique_ptr<SENode>(new SEConstantNode(this, integer)));
- }
- SENode* ScalarEvolutionAnalysis::CreateRecurrentExpression(
- const Loop* loop, SENode* offset, SENode* coefficient) {
- assert(loop && "Recurrent add expressions must have a valid loop.");
- // If operands are can't compute then the whole graph is can't compute.
- if (offset->IsCantCompute() || coefficient->IsCantCompute())
- return CreateCantComputeNode();
- const Loop* loop_to_use = nullptr;
- if (pretend_equal_[loop]) {
- loop_to_use = pretend_equal_[loop];
- } else {
- loop_to_use = loop;
- }
- std::unique_ptr<SERecurrentNode> phi_node{
- new SERecurrentNode(this, loop_to_use)};
- phi_node->AddOffset(offset);
- phi_node->AddCoefficient(coefficient);
- return GetCachedOrAdd(std::move(phi_node));
- }
- SENode* ScalarEvolutionAnalysis::AnalyzeMultiplyOp(
- const Instruction* multiply) {
- assert(multiply->opcode() == spv::Op::OpIMul &&
- "Multiply node did not come from a multiply instruction");
- analysis::DefUseManager* def_use = context_->get_def_use_mgr();
- SENode* op1 =
- AnalyzeInstruction(def_use->GetDef(multiply->GetSingleWordInOperand(0)));
- SENode* op2 =
- AnalyzeInstruction(def_use->GetDef(multiply->GetSingleWordInOperand(1)));
- return CreateMultiplyNode(op1, op2);
- }
- SENode* ScalarEvolutionAnalysis::CreateMultiplyNode(SENode* operand_1,
- SENode* operand_2) {
- // If operands are can't compute then the whole graph is can't compute.
- if (operand_1->IsCantCompute() || operand_2->IsCantCompute())
- return CreateCantComputeNode();
- if (operand_1->GetType() == SENode::Constant &&
- operand_2->GetType() == SENode::Constant) {
- return CreateConstant(operand_1->AsSEConstantNode()->FoldToSingleValue() *
- operand_2->AsSEConstantNode()->FoldToSingleValue());
- }
- std::unique_ptr<SENode> multiply_node{new SEMultiplyNode(this)};
- multiply_node->AddChild(operand_1);
- multiply_node->AddChild(operand_2);
- return GetCachedOrAdd(std::move(multiply_node));
- }
- SENode* ScalarEvolutionAnalysis::CreateSubtraction(SENode* operand_1,
- SENode* operand_2) {
- // Fold if both operands are constant.
- if (operand_1->GetType() == SENode::Constant &&
- operand_2->GetType() == SENode::Constant) {
- return CreateConstant(operand_1->AsSEConstantNode()->FoldToSingleValue() -
- operand_2->AsSEConstantNode()->FoldToSingleValue());
- }
- return CreateAddNode(operand_1, CreateNegation(operand_2));
- }
- SENode* ScalarEvolutionAnalysis::CreateAddNode(SENode* operand_1,
- SENode* operand_2) {
- // Fold if both operands are constant and the |simplify| flag is true.
- if (operand_1->GetType() == SENode::Constant &&
- operand_2->GetType() == SENode::Constant) {
- return CreateConstant(operand_1->AsSEConstantNode()->FoldToSingleValue() +
- operand_2->AsSEConstantNode()->FoldToSingleValue());
- }
- // If operands are can't compute then the whole graph is can't compute.
- if (operand_1->IsCantCompute() || operand_2->IsCantCompute())
- return CreateCantComputeNode();
- std::unique_ptr<SENode> add_node{new SEAddNode(this)};
- add_node->AddChild(operand_1);
- add_node->AddChild(operand_2);
- return GetCachedOrAdd(std::move(add_node));
- }
- SENode* ScalarEvolutionAnalysis::AnalyzeInstruction(const Instruction* inst) {
- auto itr = recurrent_node_map_.find(inst);
- if (itr != recurrent_node_map_.end()) return itr->second;
- SENode* output = nullptr;
- switch (inst->opcode()) {
- case spv::Op::OpPhi: {
- output = AnalyzePhiInstruction(inst);
- break;
- }
- case spv::Op::OpConstant:
- case spv::Op::OpConstantNull: {
- output = AnalyzeConstant(inst);
- break;
- }
- case spv::Op::OpISub:
- case spv::Op::OpIAdd: {
- output = AnalyzeAddOp(inst);
- break;
- }
- case spv::Op::OpIMul: {
- output = AnalyzeMultiplyOp(inst);
- break;
- }
- default: {
- output = CreateValueUnknownNode(inst);
- break;
- }
- }
- return output;
- }
- SENode* ScalarEvolutionAnalysis::AnalyzeConstant(const Instruction* inst) {
- if (inst->opcode() == spv::Op::OpConstantNull) return CreateConstant(0);
- assert(inst->opcode() == spv::Op::OpConstant);
- assert(inst->NumInOperands() == 1);
- int64_t value = 0;
- // Look up the instruction in the constant manager.
- const analysis::Constant* constant =
- context_->get_constant_mgr()->FindDeclaredConstant(inst->result_id());
- if (!constant) return CreateCantComputeNode();
- const analysis::IntConstant* int_constant = constant->AsIntConstant();
- // Exit out if it is a 64 bit integer.
- if (!int_constant || int_constant->words().size() != 1)
- return CreateCantComputeNode();
- if (int_constant->type()->AsInteger()->IsSigned()) {
- value = int_constant->GetS32BitValue();
- } else {
- value = int_constant->GetU32BitValue();
- }
- return CreateConstant(value);
- }
- // Handles both addition and subtraction. If the |sub| flag is set then the
- // addition will be op1+(-op2) otherwise op1+op2.
- SENode* ScalarEvolutionAnalysis::AnalyzeAddOp(const Instruction* inst) {
- assert((inst->opcode() == spv::Op::OpIAdd ||
- inst->opcode() == spv::Op::OpISub) &&
- "Add node must be created from a OpIAdd or OpISub instruction");
- analysis::DefUseManager* def_use = context_->get_def_use_mgr();
- SENode* op1 =
- AnalyzeInstruction(def_use->GetDef(inst->GetSingleWordInOperand(0)));
- SENode* op2 =
- AnalyzeInstruction(def_use->GetDef(inst->GetSingleWordInOperand(1)));
- // To handle subtraction we wrap the second operand in a unary negation node.
- if (inst->opcode() == spv::Op::OpISub) {
- op2 = CreateNegation(op2);
- }
- return CreateAddNode(op1, op2);
- }
- SENode* ScalarEvolutionAnalysis::AnalyzePhiInstruction(const Instruction* phi) {
- // The phi should only have two incoming value pairs.
- if (phi->NumInOperands() != 4) {
- return CreateCantComputeNode();
- }
- analysis::DefUseManager* def_use = context_->get_def_use_mgr();
- // Get the basic block this instruction belongs to.
- BasicBlock* basic_block =
- context_->get_instr_block(const_cast<Instruction*>(phi));
- // And then the function that the basic blocks belongs to.
- Function* function = basic_block->GetParent();
- // Use the function to get the loop descriptor.
- LoopDescriptor* loop_descriptor = context_->GetLoopDescriptor(function);
- // We only handle phis in loops at the moment.
- if (!loop_descriptor) return CreateCantComputeNode();
- // Get the innermost loop which this block belongs to.
- Loop* loop = (*loop_descriptor)[basic_block->id()];
- // If the loop doesn't exist or doesn't have a preheader or latch block, exit
- // out.
- if (!loop || !loop->GetLatchBlock() || !loop->GetPreHeaderBlock() ||
- loop->GetHeaderBlock() != basic_block)
- return recurrent_node_map_[phi] = CreateCantComputeNode();
- const Loop* loop_to_use = nullptr;
- if (pretend_equal_[loop]) {
- loop_to_use = pretend_equal_[loop];
- } else {
- loop_to_use = loop;
- }
- std::unique_ptr<SERecurrentNode> phi_node{
- new SERecurrentNode(this, loop_to_use)};
- // We add the node to this map to allow it to be returned before the node is
- // fully built. This is needed as the subsequent call to AnalyzeInstruction
- // could lead back to this |phi| instruction so we return the pointer
- // immediately in AnalyzeInstruction to break the recursion.
- recurrent_node_map_[phi] = phi_node.get();
- // Traverse the operands of the instruction an create new nodes for each one.
- for (uint32_t i = 0; i < phi->NumInOperands(); i += 2) {
- uint32_t value_id = phi->GetSingleWordInOperand(i);
- uint32_t incoming_label_id = phi->GetSingleWordInOperand(i + 1);
- Instruction* value_inst = def_use->GetDef(value_id);
- SENode* value_node = AnalyzeInstruction(value_inst);
- // If any operand is CantCompute then the whole graph is CantCompute.
- if (value_node->IsCantCompute())
- return recurrent_node_map_[phi] = CreateCantComputeNode();
- // If the value is coming from the preheader block then the value is the
- // initial value of the phi.
- if (incoming_label_id == loop->GetPreHeaderBlock()->id()) {
- phi_node->AddOffset(value_node);
- } else if (incoming_label_id == loop->GetLatchBlock()->id()) {
- // Assumed to be in the form of step + phi.
- if (value_node->GetType() != SENode::Add)
- return recurrent_node_map_[phi] = CreateCantComputeNode();
- SENode* step_node = nullptr;
- SENode* phi_operand = nullptr;
- SENode* operand_1 = value_node->GetChild(0);
- SENode* operand_2 = value_node->GetChild(1);
- // Find which node is the step term.
- if (!operand_1->AsSERecurrentNode())
- step_node = operand_1;
- else if (!operand_2->AsSERecurrentNode())
- step_node = operand_2;
- // Find which node is the recurrent expression.
- if (operand_1->AsSERecurrentNode())
- phi_operand = operand_1;
- else if (operand_2->AsSERecurrentNode())
- phi_operand = operand_2;
- // If it is not in the form step + phi exit out.
- if (!(step_node && phi_operand))
- return recurrent_node_map_[phi] = CreateCantComputeNode();
- // If the phi operand is not the same phi node exit out.
- if (phi_operand != phi_node.get())
- return recurrent_node_map_[phi] = CreateCantComputeNode();
- if (!IsLoopInvariant(loop, step_node))
- return recurrent_node_map_[phi] = CreateCantComputeNode();
- phi_node->AddCoefficient(step_node);
- }
- }
- // Once the node is fully built we update the map with the version from the
- // cache (if it has already been added to the cache).
- return recurrent_node_map_[phi] = GetCachedOrAdd(std::move(phi_node));
- }
- SENode* ScalarEvolutionAnalysis::CreateValueUnknownNode(
- const Instruction* inst) {
- std::unique_ptr<SEValueUnknown> load_node{
- new SEValueUnknown(this, inst->result_id())};
- return GetCachedOrAdd(std::move(load_node));
- }
- SENode* ScalarEvolutionAnalysis::CreateCantComputeNode() {
- return cached_cant_compute_;
- }
- // Add the created node into the cache of nodes. If it already exists return it.
- SENode* ScalarEvolutionAnalysis::GetCachedOrAdd(
- std::unique_ptr<SENode> prospective_node) {
- auto itr = node_cache_.find(prospective_node);
- if (itr != node_cache_.end()) {
- return (*itr).get();
- }
- SENode* raw_ptr_to_node = prospective_node.get();
- node_cache_.insert(std::move(prospective_node));
- return raw_ptr_to_node;
- }
- bool ScalarEvolutionAnalysis::IsLoopInvariant(const Loop* loop,
- const SENode* node) const {
- for (auto itr = node->graph_cbegin(); itr != node->graph_cend(); ++itr) {
- if (const SERecurrentNode* rec = itr->AsSERecurrentNode()) {
- const BasicBlock* header = rec->GetLoop()->GetHeaderBlock();
- // If the loop which the recurrent expression belongs to is either |loop
- // or a nested loop inside |loop| then we assume it is variant.
- if (loop->IsInsideLoop(header)) {
- return false;
- }
- } else if (const SEValueUnknown* unknown = itr->AsSEValueUnknown()) {
- // If the instruction is inside the loop we conservatively assume it is
- // loop variant.
- if (loop->IsInsideLoop(unknown->ResultId())) return false;
- }
- }
- return true;
- }
- SENode* ScalarEvolutionAnalysis::GetCoefficientFromRecurrentTerm(
- SENode* node, const Loop* loop) {
- // Traverse the DAG to find the recurrent expression belonging to |loop|.
- for (auto itr = node->graph_begin(); itr != node->graph_end(); ++itr) {
- SERecurrentNode* rec = itr->AsSERecurrentNode();
- if (rec && rec->GetLoop() == loop) {
- return rec->GetCoefficient();
- }
- }
- return CreateConstant(0);
- }
- SENode* ScalarEvolutionAnalysis::UpdateChildNode(SENode* parent,
- SENode* old_child,
- SENode* new_child) {
- // Only handles add.
- if (parent->GetType() != SENode::Add) return parent;
- std::vector<SENode*> new_children;
- for (SENode* child : *parent) {
- if (child == old_child) {
- new_children.push_back(new_child);
- } else {
- new_children.push_back(child);
- }
- }
- std::unique_ptr<SENode> add_node{new SEAddNode(this)};
- for (SENode* child : new_children) {
- add_node->AddChild(child);
- }
- return SimplifyExpression(GetCachedOrAdd(std::move(add_node)));
- }
- // Rebuild the |node| eliminating, if it exists, the recurrent term which
- // belongs to the |loop|.
- SENode* ScalarEvolutionAnalysis::BuildGraphWithoutRecurrentTerm(
- SENode* node, const Loop* loop) {
- // If the node is already a recurrent expression belonging to loop then just
- // return the offset.
- SERecurrentNode* recurrent = node->AsSERecurrentNode();
- if (recurrent) {
- if (recurrent->GetLoop() == loop) {
- return recurrent->GetOffset();
- } else {
- return node;
- }
- }
- std::vector<SENode*> new_children;
- // Otherwise find the recurrent node in the children of this node.
- for (auto itr : *node) {
- recurrent = itr->AsSERecurrentNode();
- if (recurrent && recurrent->GetLoop() == loop) {
- new_children.push_back(recurrent->GetOffset());
- } else {
- new_children.push_back(itr);
- }
- }
- std::unique_ptr<SENode> add_node{new SEAddNode(this)};
- for (SENode* child : new_children) {
- add_node->AddChild(child);
- }
- return SimplifyExpression(GetCachedOrAdd(std::move(add_node)));
- }
- // Return the recurrent term belonging to |loop| if it appears in the graph
- // starting at |node| or null if it doesn't.
- SERecurrentNode* ScalarEvolutionAnalysis::GetRecurrentTerm(SENode* node,
- const Loop* loop) {
- for (auto itr = node->graph_begin(); itr != node->graph_end(); ++itr) {
- SERecurrentNode* rec = itr->AsSERecurrentNode();
- if (rec && rec->GetLoop() == loop) {
- return rec;
- }
- }
- return nullptr;
- }
- std::string SENode::AsString() const {
- switch (GetType()) {
- case Constant:
- return "Constant";
- case RecurrentAddExpr:
- return "RecurrentAddExpr";
- case Add:
- return "Add";
- case Negative:
- return "Negative";
- case Multiply:
- return "Multiply";
- case ValueUnknown:
- return "Value Unknown";
- case CanNotCompute:
- return "Can not compute";
- }
- return "NULL";
- }
- bool SENode::operator==(const SENode& other) const {
- if (GetType() != other.GetType()) return false;
- if (other.GetChildren().size() != children_.size()) return false;
- const SERecurrentNode* this_as_recurrent = AsSERecurrentNode();
- // Check the children are the same, for SERecurrentNodes we need to check the
- // offset and coefficient manually as the child vector is sorted by ids so the
- // offset/coefficient information is lost.
- if (!this_as_recurrent) {
- for (size_t index = 0; index < children_.size(); ++index) {
- if (other.GetChildren()[index] != children_[index]) return false;
- }
- } else {
- const SERecurrentNode* other_as_recurrent = other.AsSERecurrentNode();
- // We've already checked the types are the same, this should not fail if
- // this->AsSERecurrentNode() succeeded.
- assert(other_as_recurrent);
- if (this_as_recurrent->GetCoefficient() !=
- other_as_recurrent->GetCoefficient())
- return false;
- if (this_as_recurrent->GetOffset() != other_as_recurrent->GetOffset())
- return false;
- if (this_as_recurrent->GetLoop() != other_as_recurrent->GetLoop())
- return false;
- }
- // If we're dealing with a value unknown node check both nodes were created by
- // the same instruction.
- if (GetType() == SENode::ValueUnknown) {
- if (AsSEValueUnknown()->ResultId() !=
- other.AsSEValueUnknown()->ResultId()) {
- return false;
- }
- }
- if (AsSEConstantNode()) {
- if (AsSEConstantNode()->FoldToSingleValue() !=
- other.AsSEConstantNode()->FoldToSingleValue())
- return false;
- }
- return true;
- }
- bool SENode::operator!=(const SENode& other) const { return !(*this == other); }
- namespace {
- // Helper functions to insert 32/64 bit values into the 32 bit hash string. This
- // allows us to add pointers to the string by reinterpreting the pointers as
- // uintptr_t. PushToString will deduce the type, call sizeof on it and use
- // that size to call into the correct PushToStringImpl functor depending on
- // whether it is 32 or 64 bit.
- template <typename T, size_t size_of_t>
- struct PushToStringImpl;
- template <typename T>
- struct PushToStringImpl<T, 8> {
- void operator()(T id, std::u32string* str) {
- str->push_back(static_cast<uint32_t>(id >> 32));
- str->push_back(static_cast<uint32_t>(id));
- }
- };
- template <typename T>
- struct PushToStringImpl<T, 4> {
- void operator()(T id, std::u32string* str) {
- str->push_back(static_cast<uint32_t>(id));
- }
- };
- template <typename T>
- void PushToString(T id, std::u32string* str) {
- PushToStringImpl<T, sizeof(T)>{}(id, str);
- }
- } // namespace
- // Implements the hashing of SENodes.
- size_t SENodeHash::operator()(const SENode* node) const {
- // Concatenate the terms into a string which we can hash.
- std::u32string hash_string{};
- // Hashing the type as a string is safer than hashing the enum as the enum is
- // very likely to collide with constants.
- for (char ch : node->AsString()) {
- hash_string.push_back(static_cast<char32_t>(ch));
- }
- // We just ignore the literal value unless it is a constant.
- if (node->GetType() == SENode::Constant)
- PushToString(node->AsSEConstantNode()->FoldToSingleValue(), &hash_string);
- const SERecurrentNode* recurrent = node->AsSERecurrentNode();
- // If we're dealing with a recurrent expression hash the loop as well so that
- // nested inductions like i=0,i++ and j=0,j++ correspond to different nodes.
- if (recurrent) {
- PushToString(reinterpret_cast<uintptr_t>(recurrent->GetLoop()),
- &hash_string);
- // Recurrent expressions can't be hashed using the normal method as the
- // order of coefficient and offset matters to the hash.
- PushToString(reinterpret_cast<uintptr_t>(recurrent->GetCoefficient()),
- &hash_string);
- PushToString(reinterpret_cast<uintptr_t>(recurrent->GetOffset()),
- &hash_string);
- return std::hash<std::u32string>{}(hash_string);
- }
- // Hash the result id of the original instruction which created this node if
- // it is a value unknown node.
- if (node->GetType() == SENode::ValueUnknown) {
- PushToString(node->AsSEValueUnknown()->ResultId(), &hash_string);
- }
- // Hash the pointers of the child nodes, each SENode has a unique pointer
- // associated with it.
- const std::vector<SENode*>& children = node->GetChildren();
- for (const SENode* child : children) {
- PushToString(reinterpret_cast<uintptr_t>(child), &hash_string);
- }
- return std::hash<std::u32string>{}(hash_string);
- }
- // This overload is the actual overload used by the node_cache_ set.
- size_t SENodeHash::operator()(const std::unique_ptr<SENode>& node) const {
- return this->operator()(node.get());
- }
- void SENode::DumpDot(std::ostream& out, bool recurse) const {
- size_t unique_id = std::hash<const SENode*>{}(this);
- out << unique_id << " [label=\"" << AsString() << " ";
- if (GetType() == SENode::Constant) {
- out << "\nwith value: " << this->AsSEConstantNode()->FoldToSingleValue();
- }
- out << "\"]\n";
- for (const SENode* child : children_) {
- size_t child_unique_id = std::hash<const SENode*>{}(child);
- out << unique_id << " -> " << child_unique_id << " \n";
- if (recurse) child->DumpDot(out, true);
- }
- }
- namespace {
- class IsGreaterThanZero {
- public:
- explicit IsGreaterThanZero(IRContext* context) : context_(context) {}
- // Determine if the value of |node| is always strictly greater than zero if
- // |or_equal_zero| is false or greater or equal to zero if |or_equal_zero| is
- // true. It returns true is the evaluation was able to conclude something, in
- // which case the result is stored in |result|.
- // The algorithm work by going through all the nodes and determine the
- // sign of each of them.
- bool Eval(const SENode* node, bool or_equal_zero, bool* result) {
- *result = false;
- switch (Visit(node)) {
- case Signedness::kPositiveOrNegative: {
- return false;
- }
- case Signedness::kStrictlyNegative: {
- *result = false;
- break;
- }
- case Signedness::kNegative: {
- if (!or_equal_zero) {
- return false;
- }
- *result = false;
- break;
- }
- case Signedness::kStrictlyPositive: {
- *result = true;
- break;
- }
- case Signedness::kPositive: {
- if (!or_equal_zero) {
- return false;
- }
- *result = true;
- break;
- }
- }
- return true;
- }
- private:
- enum class Signedness {
- kPositiveOrNegative, // Yield a value positive or negative.
- kStrictlyNegative, // Yield a value strictly less than 0.
- kNegative, // Yield a value less or equal to 0.
- kStrictlyPositive, // Yield a value strictly greater than 0.
- kPositive // Yield a value greater or equal to 0.
- };
- // Combine the signedness according to arithmetic rules of a given operator.
- using Combiner = std::function<Signedness(Signedness, Signedness)>;
- // Returns a functor to interpret the signedness of 2 expressions as if they
- // were added.
- Combiner GetAddCombiner() const {
- return [](Signedness lhs, Signedness rhs) {
- switch (lhs) {
- case Signedness::kPositiveOrNegative:
- break;
- case Signedness::kStrictlyNegative:
- if (rhs == Signedness::kStrictlyNegative ||
- rhs == Signedness::kNegative)
- return lhs;
- break;
- case Signedness::kNegative: {
- if (rhs == Signedness::kStrictlyNegative)
- return Signedness::kStrictlyNegative;
- if (rhs == Signedness::kNegative) return Signedness::kNegative;
- break;
- }
- case Signedness::kStrictlyPositive: {
- if (rhs == Signedness::kStrictlyPositive ||
- rhs == Signedness::kPositive) {
- return Signedness::kStrictlyPositive;
- }
- break;
- }
- case Signedness::kPositive: {
- if (rhs == Signedness::kStrictlyPositive)
- return Signedness::kStrictlyPositive;
- if (rhs == Signedness::kPositive) return Signedness::kPositive;
- break;
- }
- }
- return Signedness::kPositiveOrNegative;
- };
- }
- // Returns a functor to interpret the signedness of 2 expressions as if they
- // were multiplied.
- Combiner GetMulCombiner() const {
- return [](Signedness lhs, Signedness rhs) {
- switch (lhs) {
- case Signedness::kPositiveOrNegative:
- break;
- case Signedness::kStrictlyNegative: {
- switch (rhs) {
- case Signedness::kPositiveOrNegative: {
- break;
- }
- case Signedness::kStrictlyNegative: {
- return Signedness::kStrictlyPositive;
- }
- case Signedness::kNegative: {
- return Signedness::kPositive;
- }
- case Signedness::kStrictlyPositive: {
- return Signedness::kStrictlyNegative;
- }
- case Signedness::kPositive: {
- return Signedness::kNegative;
- }
- }
- break;
- }
- case Signedness::kNegative: {
- switch (rhs) {
- case Signedness::kPositiveOrNegative: {
- break;
- }
- case Signedness::kStrictlyNegative:
- case Signedness::kNegative: {
- return Signedness::kPositive;
- }
- case Signedness::kStrictlyPositive:
- case Signedness::kPositive: {
- return Signedness::kNegative;
- }
- }
- break;
- }
- case Signedness::kStrictlyPositive: {
- return rhs;
- }
- case Signedness::kPositive: {
- switch (rhs) {
- case Signedness::kPositiveOrNegative: {
- break;
- }
- case Signedness::kStrictlyNegative:
- case Signedness::kNegative: {
- return Signedness::kNegative;
- }
- case Signedness::kStrictlyPositive:
- case Signedness::kPositive: {
- return Signedness::kPositive;
- }
- }
- break;
- }
- }
- return Signedness::kPositiveOrNegative;
- };
- }
- Signedness Visit(const SENode* node) {
- switch (node->GetType()) {
- case SENode::Constant:
- return Visit(node->AsSEConstantNode());
- break;
- case SENode::RecurrentAddExpr:
- return Visit(node->AsSERecurrentNode());
- break;
- case SENode::Negative:
- return Visit(node->AsSENegative());
- break;
- case SENode::CanNotCompute:
- return Visit(node->AsSECantCompute());
- break;
- case SENode::ValueUnknown:
- return Visit(node->AsSEValueUnknown());
- break;
- case SENode::Add:
- return VisitExpr(node, GetAddCombiner());
- break;
- case SENode::Multiply:
- return VisitExpr(node, GetMulCombiner());
- break;
- }
- return Signedness::kPositiveOrNegative;
- }
- // Returns the signedness of a constant |node|.
- Signedness Visit(const SEConstantNode* node) {
- if (0 == node->FoldToSingleValue()) return Signedness::kPositive;
- if (0 < node->FoldToSingleValue()) return Signedness::kStrictlyPositive;
- if (0 > node->FoldToSingleValue()) return Signedness::kStrictlyNegative;
- return Signedness::kPositiveOrNegative;
- }
- // Returns the signedness of an unknown |node| based on its type.
- Signedness Visit(const SEValueUnknown* node) {
- Instruction* insn = context_->get_def_use_mgr()->GetDef(node->ResultId());
- analysis::Type* type = context_->get_type_mgr()->GetType(insn->type_id());
- assert(type && "Can't retrieve a type for the instruction");
- analysis::Integer* int_type = type->AsInteger();
- assert(type && "Can't retrieve an integer type for the instruction");
- return int_type->IsSigned() ? Signedness::kPositiveOrNegative
- : Signedness::kPositive;
- }
- // Returns the signedness of a recurring expression.
- Signedness Visit(const SERecurrentNode* node) {
- Signedness coeff_sign = Visit(node->GetCoefficient());
- // SERecurrentNode represent an affine expression in the range [0,
- // loop_bound], so the result cannot be strictly positive or negative.
- switch (coeff_sign) {
- default:
- break;
- case Signedness::kStrictlyNegative:
- coeff_sign = Signedness::kNegative;
- break;
- case Signedness::kStrictlyPositive:
- coeff_sign = Signedness::kPositive;
- break;
- }
- return GetAddCombiner()(coeff_sign, Visit(node->GetOffset()));
- }
- // Returns the signedness of a negation |node|.
- Signedness Visit(const SENegative* node) {
- switch (Visit(*node->begin())) {
- case Signedness::kPositiveOrNegative: {
- return Signedness::kPositiveOrNegative;
- }
- case Signedness::kStrictlyNegative: {
- return Signedness::kStrictlyPositive;
- }
- case Signedness::kNegative: {
- return Signedness::kPositive;
- }
- case Signedness::kStrictlyPositive: {
- return Signedness::kStrictlyNegative;
- }
- case Signedness::kPositive: {
- return Signedness::kNegative;
- }
- }
- return Signedness::kPositiveOrNegative;
- }
- Signedness Visit(const SECantCompute*) {
- return Signedness::kPositiveOrNegative;
- }
- // Returns the signedness of a binary expression by using the combiner
- // |reduce|.
- Signedness VisitExpr(
- const SENode* node,
- std::function<Signedness(Signedness, Signedness)> reduce) {
- Signedness result = Visit(*node->begin());
- for (const SENode* operand : make_range(++node->begin(), node->end())) {
- if (result == Signedness::kPositiveOrNegative) {
- return Signedness::kPositiveOrNegative;
- }
- result = reduce(result, Visit(operand));
- }
- return result;
- }
- IRContext* context_;
- };
- } // namespace
- bool ScalarEvolutionAnalysis::IsAlwaysGreaterThanZero(SENode* node,
- bool* is_gt_zero) const {
- return IsGreaterThanZero(context_).Eval(node, false, is_gt_zero);
- }
- bool ScalarEvolutionAnalysis::IsAlwaysGreaterOrEqualToZero(
- SENode* node, bool* is_ge_zero) const {
- return IsGreaterThanZero(context_).Eval(node, true, is_ge_zero);
- }
- namespace {
- // Remove |node| from the |mul| chain (of the form A * ... * |node| * ... * Z),
- // if |node| is not in the chain, returns the original chain.
- SENode* RemoveOneNodeFromMultiplyChain(SEMultiplyNode* mul,
- const SENode* node) {
- SENode* lhs = mul->GetChildren()[0];
- SENode* rhs = mul->GetChildren()[1];
- if (lhs == node) {
- return rhs;
- }
- if (rhs == node) {
- return lhs;
- }
- if (lhs->AsSEMultiplyNode()) {
- SENode* res = RemoveOneNodeFromMultiplyChain(lhs->AsSEMultiplyNode(), node);
- if (res != lhs)
- return mul->GetParentAnalysis()->CreateMultiplyNode(res, rhs);
- }
- if (rhs->AsSEMultiplyNode()) {
- SENode* res = RemoveOneNodeFromMultiplyChain(rhs->AsSEMultiplyNode(), node);
- if (res != rhs)
- return mul->GetParentAnalysis()->CreateMultiplyNode(res, rhs);
- }
- return mul;
- }
- } // namespace
- std::pair<SExpression, int64_t> SExpression::operator/(
- SExpression rhs_wrapper) const {
- SENode* lhs = node_;
- SENode* rhs = rhs_wrapper.node_;
- // Check for division by 0.
- if (rhs->AsSEConstantNode() &&
- !rhs->AsSEConstantNode()->FoldToSingleValue()) {
- return {scev_->CreateCantComputeNode(), 0};
- }
- // Trivial case.
- if (lhs->AsSEConstantNode() && rhs->AsSEConstantNode()) {
- int64_t lhs_value = lhs->AsSEConstantNode()->FoldToSingleValue();
- int64_t rhs_value = rhs->AsSEConstantNode()->FoldToSingleValue();
- return {scev_->CreateConstant(lhs_value / rhs_value),
- lhs_value % rhs_value};
- }
- // look for a "c U / U" pattern.
- if (lhs->AsSEMultiplyNode()) {
- assert(lhs->GetChildren().size() == 2 &&
- "More than 2 operand for a multiply node.");
- SENode* res = RemoveOneNodeFromMultiplyChain(lhs->AsSEMultiplyNode(), rhs);
- if (res != lhs) {
- return {res, 0};
- }
- }
- return {scev_->CreateCantComputeNode(), 0};
- }
- } // namespace opt
- } // namespace spvtools
|