// Copyright (c) 2016 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 "source/opt/function.h" #include #include #include "function.h" #include "ir_context.h" #include "source/util/bit_vector.h" namespace spvtools { namespace opt { Function* Function::Clone(IRContext* ctx) const { Function* clone = new Function(std::unique_ptr(DefInst().Clone(ctx))); clone->params_.reserve(params_.size()); ForEachParam( [clone, ctx](const Instruction* inst) { clone->AddParameter(std::unique_ptr(inst->Clone(ctx))); }, true); for (const auto& i : debug_insts_in_header_) { clone->AddDebugInstructionInHeader( std::unique_ptr(i.Clone(ctx))); } clone->blocks_.reserve(blocks_.size()); for (const auto& b : blocks_) { std::unique_ptr bb(b->Clone(ctx)); bb->SetParent(clone); clone->AddBasicBlock(std::move(bb)); } clone->SetFunctionEnd(std::unique_ptr(EndInst()->Clone(ctx))); return clone; } void Function::ForEachInst(const std::function& f, bool run_on_debug_line_insts) { WhileEachInst( [&f](Instruction* inst) { f(inst); return true; }, run_on_debug_line_insts); } void Function::ForEachInst(const std::function& f, bool run_on_debug_line_insts) const { WhileEachInst( [&f](const Instruction* inst) { f(inst); return true; }, run_on_debug_line_insts); } bool Function::WhileEachInst(const std::function& f, bool run_on_debug_line_insts) { if (def_inst_) { if (!def_inst_->WhileEachInst(f, run_on_debug_line_insts)) { return false; } } for (auto& param : params_) { if (!param->WhileEachInst(f, run_on_debug_line_insts)) { return false; } } if (!debug_insts_in_header_.empty()) { Instruction* di = &debug_insts_in_header_.front(); while (di != nullptr) { Instruction* next_instruction = di->NextNode(); if (!di->WhileEachInst(f, run_on_debug_line_insts)) return false; di = next_instruction; } } for (auto& bb : blocks_) { if (!bb->WhileEachInst(f, run_on_debug_line_insts)) { return false; } } if (end_inst_) return end_inst_->WhileEachInst(f, run_on_debug_line_insts); return true; } bool Function::WhileEachInst(const std::function& f, bool run_on_debug_line_insts) const { if (def_inst_) { if (!static_cast(def_inst_.get()) ->WhileEachInst(f, run_on_debug_line_insts)) { return false; } } for (const auto& param : params_) { if (!static_cast(param.get()) ->WhileEachInst(f, run_on_debug_line_insts)) { return false; } } for (const auto& di : debug_insts_in_header_) { if (!static_cast(&di)->WhileEachInst( f, run_on_debug_line_insts)) return false; } for (const auto& bb : blocks_) { if (!static_cast(bb.get())->WhileEachInst( f, run_on_debug_line_insts)) { return false; } } if (end_inst_) return static_cast(end_inst_.get()) ->WhileEachInst(f, run_on_debug_line_insts); return true; } void Function::ForEachParam(const std::function& f, bool run_on_debug_line_insts) { for (auto& param : params_) static_cast(param.get()) ->ForEachInst(f, run_on_debug_line_insts); } void Function::ForEachParam(const std::function& f, bool run_on_debug_line_insts) const { for (const auto& param : params_) static_cast(param.get()) ->ForEachInst(f, run_on_debug_line_insts); } void Function::ForEachDebugInstructionsInHeader( const std::function& f) { if (debug_insts_in_header_.empty()) return; Instruction* di = &debug_insts_in_header_.front(); while (di != nullptr) { Instruction* next_instruction = di->NextNode(); di->ForEachInst(f); di = next_instruction; } } BasicBlock* Function::InsertBasicBlockAfter( std::unique_ptr&& new_block, BasicBlock* position) { for (auto bb_iter = begin(); bb_iter != end(); ++bb_iter) { if (&*bb_iter == position) { new_block->SetParent(this); ++bb_iter; bb_iter = bb_iter.InsertBefore(std::move(new_block)); return &*bb_iter; } } assert(false && "Could not find insertion point."); return nullptr; } BasicBlock* Function::InsertBasicBlockBefore( std::unique_ptr&& new_block, BasicBlock* position) { for (auto bb_iter = begin(); bb_iter != end(); ++bb_iter) { if (&*bb_iter == position) { new_block->SetParent(this); bb_iter = bb_iter.InsertBefore(std::move(new_block)); return &*bb_iter; } } assert(false && "Could not find insertion point."); return nullptr; } bool Function::IsRecursive() const { IRContext* ctx = blocks_.front()->GetLabel()->context(); IRContext::ProcessFunction mark_visited = [this](Function* fp) { return fp == this; }; // Process the call tree from all of the function called by |this|. If it get // back to |this|, then we have a recursive function. std::queue roots; ctx->AddCalls(this, &roots); return ctx->ProcessCallTreeFromRoots(mark_visited, &roots); } std::ostream& operator<<(std::ostream& str, const Function& func) { str << func.PrettyPrint(); return str; } void Function::Dump() const { std::cerr << "Function #" << result_id() << "\n" << *this << "\n"; } std::string Function::PrettyPrint(uint32_t options) const { std::ostringstream str; ForEachInst([&str, options](const Instruction* inst) { str << inst->PrettyPrint(options); if (inst->opcode() != SpvOpFunctionEnd) { str << std::endl; } }); return str.str(); } } // namespace opt } // namespace spvtools