diff --git a/svf/lib/AE/Core/AbstractState.cpp b/svf/lib/AE/Core/AbstractState.cpp deleted file mode 100644 index 639cc366a..000000000 --- a/svf/lib/AE/Core/AbstractState.cpp +++ /dev/null @@ -1,216 +0,0 @@ -//===- IntervalExeState.cpp----Interval Domain-------------------------// -// -// SVF: Static Value-Flow Analysis -// -// Copyright (C) <2013-2022> -// - -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU Affero General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Affero General Public License for more details. - -// You should have received a copy of the GNU Affero General Public License -// along with this program. If not, see . -// -//===----------------------------------------------------------------------===// -/* - * AbstractExeState.cpp - * - * Created on: Jul 9, 2022 - * Author: Xiao Cheng, Jiawei Wang - * - */ - -#include "AE/Core/AbstractState.h" -#include "Util/SVFUtil.h" - -using namespace SVF; -using namespace SVFUtil; - -bool AbstractState::equals(const AbstractState&other) const -{ - return *this == other; -} - -u32_t AbstractState::hash() const -{ - size_t h = getVarToVal().size() * 2; - Hash hf; - for (const auto &t: getVarToVal()) - { - h ^= hf(t.first) + 0x9e3779b9 + (h << 6) + (h >> 2); - } - size_t h2 = getLocToVal().size() * 2; - for (const auto &t: getLocToVal()) - { - h2 ^= hf(t.first) + 0x9e3779b9 + (h2 << 6) + (h2 >> 2); - } - Hash> pairH; - return pairH({h, h2}); -} - -AbstractState AbstractState::widening(const AbstractState& other) -{ - // widen interval - AbstractState es = *this; - for (auto it = es._varToAbsVal.begin(); it != es._varToAbsVal.end(); ++it) - { - auto key = it->first; - if (other._varToAbsVal.find(key) != other._varToAbsVal.end()) - if (it->second.isInterval() && other._varToAbsVal.at(key).isInterval()) - it->second.getInterval().widen_with(other._varToAbsVal.at(key).getInterval()); - } - for (auto it = es._addrToAbsVal.begin(); it != es._addrToAbsVal.end(); ++it) - { - auto key = it->first; - if (other._addrToAbsVal.find(key) != other._addrToAbsVal.end()) - if (it->second.isInterval() && other._addrToAbsVal.at(key).isInterval()) - it->second.getInterval().widen_with(other._addrToAbsVal.at(key).getInterval()); - } - return es; -} - -AbstractState AbstractState::narrowing(const AbstractState& other) -{ - AbstractState es = *this; - for (auto it = es._varToAbsVal.begin(); it != es._varToAbsVal.end(); ++it) - { - auto key = it->first; - if (other._varToAbsVal.find(key) != other._varToAbsVal.end()) - if (it->second.isInterval() && other._varToAbsVal.at(key).isInterval()) - it->second.getInterval().narrow_with(other._varToAbsVal.at(key).getInterval()); - } - for (auto it = es._addrToAbsVal.begin(); it != es._addrToAbsVal.end(); ++it) - { - auto key = it->first; - if (other._addrToAbsVal.find(key) != other._addrToAbsVal.end()) - if (it->second.isInterval() && other._addrToAbsVal.at(key).isInterval()) - it->second.getInterval().narrow_with(other._addrToAbsVal.at(key).getInterval()); - } - return es; - -} - -/// domain widen with other, important! other widen this. -void AbstractState::widenWith(const AbstractState& other) -{ - for (auto it = _varToAbsVal.begin(); it != _varToAbsVal.end(); ++it) - { - auto key = it->first; - if (other.getVarToVal().find(key) != other.getVarToVal().end()) - if (it->second.isInterval() && other._varToAbsVal.at(key).isInterval()) - it->second.getInterval().widen_with(other._varToAbsVal.at(key).getInterval()); - } - for (auto it = _addrToAbsVal.begin(); it != _addrToAbsVal.end(); ++it) - { - auto key = it->first; - if (other._addrToAbsVal.find(key) != other._addrToAbsVal.end()) - if (it->second.isInterval() && other._varToAbsVal.at(key).isInterval()) - it->second.getInterval().widen_with(other._addrToAbsVal.at(key).getInterval()); - } -} - -/// domain join with other, important! other widen this. -void AbstractState::joinWith(const AbstractState& other) -{ - for (auto it = other._varToAbsVal.begin(); it != other._varToAbsVal.end(); ++it) - { - auto key = it->first; - auto oit = _varToAbsVal.find(key); - if (oit != _varToAbsVal.end()) - { - oit->second.join_with(it->second); - } - else - { - _varToAbsVal.emplace(key, it->second); - } - } - for (auto it = other._addrToAbsVal.begin(); it != other._addrToAbsVal.end(); ++it) - { - auto key = it->first; - auto oit = _addrToAbsVal.find(key); - if (oit != _addrToAbsVal.end()) - { - oit->second.join_with(it->second); - } - else - { - _addrToAbsVal.emplace(key, it->second); - } - } -} - -/// domain narrow with other, important! other widen this. -void AbstractState::narrowWith(const AbstractState& other) -{ - for (auto it = _varToAbsVal.begin(); it != _varToAbsVal.end(); ++it) - { - auto key = it->first; - auto oit = other.getVarToVal().find(key); - if (oit != other.getVarToVal().end()) - if (it->second.isInterval() && oit->second.isInterval()) - it->second.getInterval().narrow_with(oit->second.getInterval()); - } - for (auto it = _addrToAbsVal.begin(); it != _addrToAbsVal.end(); ++it) - { - auto key = it->first; - auto oit = other._addrToAbsVal.find(key); - if (oit != other._addrToAbsVal.end()) - if (it->second.isInterval() && oit->second.isInterval()) - it->second.getInterval().narrow_with(oit->second.getInterval()); - } -} - -/// domain meet with other, important! other widen this. -void AbstractState::meetWith(const AbstractState& other) -{ - for (auto it = other._varToAbsVal.begin(); it != other._varToAbsVal.end(); ++it) - { - auto key = it->first; - auto oit = _varToAbsVal.find(key); - if (oit != _varToAbsVal.end()) - { - oit->second.meet_with(it->second); - } - } - for (auto it = other._addrToAbsVal.begin(); it != other._addrToAbsVal.end(); ++it) - { - auto key = it->first; - auto oit = _addrToAbsVal.find(key); - if (oit != _addrToAbsVal.end()) - { - oit->second.meet_with(it->second); - } - } -} - -/// Print values of all expressions -void AbstractState::printExprValues(std::ostream &oss) const -{ - oss << "-----------Var and Value-----------\n"; - printTable(_varToAbsVal, oss); - printTable(_addrToAbsVal, oss); - oss << "-----------------------------------------\n"; -} - -void AbstractState::printTable(const VarToAbsValMap&table, std::ostream &oss) const -{ - oss.flags(std::ios::left); - std::set ordered; - for (const auto &item: table) - { - ordered.insert(item.first); - } - for (const auto &item: ordered) - { - oss << "Var" << std::to_string(item); - oss << "\t Value: " << table.at(item).toString() << "\n"; - } -} diff --git a/svf/lib/AE/Core/RelExeState.cpp b/svf/lib/AE/Core/RelExeState.cpp deleted file mode 100644 index 4035a041d..000000000 --- a/svf/lib/AE/Core/RelExeState.cpp +++ /dev/null @@ -1,216 +0,0 @@ -//===- RelExeState.cpp ----Relation Execution States for Interval Domains-------// -// -// SVF: Static Value-Flow Analysis -// -// Copyright (C) <2013-2022> -// - -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU Affero General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Affero General Public License for more details. - -// You should have received a copy of the GNU Affero General Public License -// along with this program. If not, see . -// -//===----------------------------------------------------------------------===// -/* - * RelExeState.cpp - * - * Created on: Aug 15, 2022 - * Author: Jiawei Ren, Xiao Cheng - * - */ - -#include "AE/Core/RelExeState.h" -#include "Util/GeneralType.h" -#include "Util/SVFUtil.h" -#include - -using namespace SVF; -using namespace SVFUtil; - -/*! - * Extract sub SVFVar IDs of a Z3Expr - * - * e.g., Given an expr "a+b", return {a.id, b.id} - * @param expr - * @param res - */ -void RelExeState::extractSubVars(const Z3Expr &expr, Set &res) -{ - if (expr.getExpr().num_args() == 0) - if (!expr.getExpr().is_true() && !expr.getExpr().is_false() && !expr.is_numeral()) - { - const std::string &exprStr = expr.to_string(); - res.insert(std::stoi(exprStr.substr(1, exprStr.size() - 1))); - } - for (u32_t i = 0; i < expr.getExpr().num_args(); ++i) - { - const z3::expr &e = expr.getExpr().arg(i); - extractSubVars(e, res); - } -} - -/*! - * Extract all related SVFVar IDs based on compare expr - * - * @param expr - * @param res - */ -void RelExeState::extractCmpVars(const Z3Expr &expr, Set &res) -{ - Set r; - extractSubVars(expr, r); - res.insert(r.begin(), r.end()); - assert(!r.empty() && "symbol not init?"); - if (r.size() == 1 && eq(expr, toZ3Expr(*r.begin()))) - { - return; - } - for (const auto &id: r) - { - extractCmpVars((*this)[id], res); - } -} - -/*! - * Build relational Z3Expr - * @param cmp - * @param succ - * @param vars return all the relational vars of a given variable - * @param initVars the vars on the right hand side of cmp statement, e.g., {a} for "cmp = a > 1" - * @return - */ -Z3Expr RelExeState::buildRelZ3Expr(u32_t cmp, s32_t succ, Set &vars, Set &initVars) -{ - Z3Expr res = (getZ3Expr(cmp) == succ).simplify(); - extractSubVars(res, initVars); - extractCmpVars(res, vars); - for (const auto &id: vars) - { - res = (res && toZ3Expr(id) == getZ3Expr(id)).simplify(); - } - res = (res && (toZ3Expr(cmp) == getZ3Expr(cmp))).simplify(); - vars.insert(cmp); - return res; -} - -RelExeState &RelExeState::operator=(const RelExeState &rhs) -{ - if (*this != rhs) - { - _varToVal = rhs.getVarToVal(); - _addrToVal = rhs.getLocToVal(); - } - return *this; -} - -/*! - * Overloading Operator== - * @param rhs - * @return - */ -bool RelExeState::operator==(const RelExeState &rhs) const -{ - return eqVarToValMap(_varToVal, rhs.getVarToVal()) && - eqVarToValMap(_addrToVal, rhs.getLocToVal()); -} - -/*! - * Overloading Operator< - * @param rhs - * @return - */ -bool RelExeState::operator<(const RelExeState &rhs) const -{ - return lessThanVarToValMap(_varToVal, rhs.getVarToVal()) || - lessThanVarToValMap(_addrToVal, rhs.getLocToVal()); -} - -bool RelExeState::eqVarToValMap(const VarToValMap &lhs, const VarToValMap &rhs) const -{ - if (lhs.size() != rhs.size()) return false; - for (const auto &item: lhs) - { - auto it = rhs.find(item.first); - // return false if SVFVar not exists in rhs or z3Expr not equal - if (it == rhs.end() || !eq(item.second, it->second)) - return false; - } - return true; -} - -bool RelExeState::lessThanVarToValMap(const VarToValMap &lhs, const VarToValMap &rhs) const -{ - if (lhs.size() != rhs.size()) return lhs.size() < rhs.size(); - for (const auto &item: lhs) - { - auto it = rhs.find(item.first); - // lhs > rhs if SVFVar not exists in rhs - if (it == rhs.end()) - return false; - // judge from expr id - if (!eq(item.second, it->second)) - return item.second.id() < it->second.id(); - } - return false; -} - -/*! - * Store value to location - * @param loc location, e.g., int_val(0x7f..01) - * @param value - */ -void RelExeState::store(const Z3Expr &loc, const Z3Expr &value) -{ - assert(loc.is_numeral() && "location must be numeral"); - s32_t virAddr = z3Expr2NumValue(loc); - assert(isVirtualMemAddress(virAddr) && "Pointer operand is not a physical address?"); - store(getInternalID(virAddr), value); -} - -/*! - * Load value at location - * @param loc location, e.g., int_val(0x7f..01) - * @return - */ -Z3Expr &RelExeState::load(const Z3Expr &loc) -{ - assert(loc.is_numeral() && "location must be numeral"); - s32_t virAddr = z3Expr2NumValue(loc); - assert(isVirtualMemAddress(virAddr) && "Pointer operand is not a physical address?"); - u32_t objId = getInternalID(virAddr); - assert(getInternalID(objId) == objId && "SVFVar idx overflow > 0x7f000000?"); - return load(objId); -} - -/*! - * Print values of all expressions - */ -void RelExeState::printExprValues() -{ - std::cout.flags(std::ios::left); - std::cout << "-----------Var and Value-----------\n"; - for (const auto &item: getVarToVal()) - { - std::stringstream exprName; - exprName << "Var" << item.first; - std::cout << std::setw(25) << exprName.str(); - const Z3Expr &sim = item.second.simplify(); - if (sim.is_numeral() && isVirtualMemAddress(z3Expr2NumValue(sim))) - { - std::cout << "\t Value: " << std::hex << "0x" << z3Expr2NumValue(sim) << "\n"; - } - else - { - std::cout << "\t Value: " << std::dec << sim << "\n"; - } - } - std::cout << "-----------------------------------------\n"; -} \ No newline at end of file diff --git a/svf/lib/AE/Core/RelationSolver.cpp b/svf/lib/AE/Core/RelationSolver.cpp deleted file mode 100644 index a5cc9dd9d..000000000 --- a/svf/lib/AE/Core/RelationSolver.cpp +++ /dev/null @@ -1,431 +0,0 @@ -//===- RelationSolver.cpp ----Relation Solver for Interval Domains-----------// -// -// SVF: Static Value-Flow Analysis -// -// Copyright (C) <2013-2022> -// - -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU Affero General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Affero General Public License for more details. - -// You should have received a copy of the GNU Affero General Public License -// along with this program. If not, see . -// -//===----------------------------------------------------------------------===// -/* - * RelationSolver.cpp - * - * Created on: Aug 4, 2022 - * Author: Jiawei Ren - * - */ -#include "AE/Core/RelationSolver.h" -#include -#include "Util/Options.h" - -using namespace SVF; -using namespace SVFUtil; - -AbstractState RelationSolver::bilateral(const AbstractState&domain, const Z3Expr& phi, - u32_t descend_check) -{ - /// init variables - AbstractState upper = domain.top(); - AbstractState lower = domain.bottom(); - u32_t meets_in_a_row = 0; - z3::solver solver = Z3Expr::getSolver(); - z3::params p(Z3Expr::getContext()); - /// TODO: add option for timeout - p.set(":timeout", static_cast(600)); // in milliseconds - solver.set(p); - AbstractState consequence; - - /// start processing - while (lower != upper) - { - if (meets_in_a_row == descend_check) - { - consequence = lower; - } - else - { - consequence = abstract_consequence(lower, upper, domain); - } - /// compute domain.model_and(phi, domain.logic_not(domain.gamma_hat(consequence))) - Z3Expr rhs = !(gamma_hat(consequence, domain)); - solver.push(); - solver.add(phi.getExpr() && rhs.getExpr()); - Map solution; - z3::check_result checkRes = solver.check(); - /// find any solution, which is sat - if (checkRes == z3::sat) - { - z3::model m = solver.get_model(); - for (u32_t i = 0; i < m.size(); i++) - { - z3::func_decl v = m[i]; - // assert(v.arity() == 0); - if (v.arity() != 0) - continue; - solution.emplace(std::stoi(v.name().str()), - m.get_const_interp(v).get_numeral_int()); - } - for (const auto& item : domain.getVarToVal()) - { - if (solution.find(item.first) == solution.end()) - { - solution.emplace(item.first, 0); - } - } - solver.pop(); - AbstractState newLower = domain.bottom(); - newLower.joinWith(lower); - AbstractState rhs = beta(solution, domain); - newLower.joinWith(rhs); - lower = newLower; - meets_in_a_row = 0; - } - else /// unknown or unsat - { - solver.pop(); - if (checkRes == z3::unknown) - { - /// for timeout reason return upper - if (solver.reason_unknown() == "timeout") - return upper; - } - AbstractState newUpper = domain.top(); - newUpper.meetWith(upper); - newUpper.meetWith(consequence); - upper = newUpper; - meets_in_a_row += 1; - } - } - return upper; -} - -AbstractState RelationSolver::RSY(const AbstractState& domain, const Z3Expr& phi) -{ - AbstractState lower = domain.bottom(); - z3::solver& solver = Z3Expr::getSolver(); - z3::params p(Z3Expr::getContext()); - /// TODO: add option for timeout - p.set(":timeout", static_cast(600)); // in milliseconds - solver.set(p); - while (1) - { - Z3Expr rhs = !(gamma_hat(lower, domain)); - solver.push(); - solver.add(phi.getExpr() && rhs.getExpr()); - Map solution; - z3::check_result checkRes = solver.check(); - /// find any solution, which is sat - if (checkRes == z3::sat) - { - z3::model m = solver.get_model(); - for (u32_t i = 0; i < m.size(); i++) - { - z3::func_decl v = m[i]; - if (v.arity() != 0) - continue; - - solution.emplace(std::stoi(v.name().str()), - m.get_const_interp(v).get_numeral_int()); - } - for (const auto& item : domain.getVarToVal()) - { - if (solution.find(item.first) == solution.end()) - { - solution.emplace(item.first, 0); - } - } - solver.pop(); - AbstractState newLower = domain.bottom(); - newLower.joinWith(lower); - newLower.joinWith(beta(solution, domain)); - lower = newLower; - } - else /// unknown or unsat - { - solver.pop(); - if (checkRes == z3::unknown) - { - /// for timeout reason return upper - if (solver.reason_unknown() == "timeout") - return domain.top(); - } - break; - } - } - return lower; -} - -AbstractState RelationSolver::abstract_consequence( - const AbstractState& lower, const AbstractState& upper, const AbstractState& domain) const -{ - /*Returns the "abstract consequence" of lower and upper. - - The abstract consequence must be a superset of lower and *NOT* a - superset of upper. - - Note that this is a fairly "simple" abstract consequence, in that it - sets only one variable to a non-top interval. This improves performance - of the SMT solver in many cases. In certain cases, other choices for - the abstract consequence will lead to better algorithm performance.*/ - - for (auto it = domain.getVarToVal().begin(); - it != domain.getVarToVal().end(); ++it) - /// for variable in self.variables: - { - AbstractState proposed = domain.top(); /// proposed = self.top.copy() - proposed[it->first] = lower[it->first].getInterval(); - /// proposed.set_interval(variable, lower.interval_of(variable)) - /// proposed._locToItvVal - if (!(proposed >= upper)) /// if not proposed >= upper: - { - return proposed; /// return proposed - } - } - return lower; /// return lower.copy() -} - -Z3Expr RelationSolver::gamma_hat(const AbstractState& exeState) const -{ - Z3Expr res(Z3Expr::getContext().bool_val(true)); - for (auto& item : exeState.getVarToVal()) - { - IntervalValue interval = item.second.getInterval(); - if (interval.isBottom()) - return Z3Expr::getContext().bool_val(false); - if (interval.isTop()) - continue; - Z3Expr v = toIntZ3Expr(item.first); - res = (res && v >= (int)interval.lb().getNumeral() && - v <= (int)interval.ub().getNumeral()).simplify(); - } - return res; -} - -Z3Expr RelationSolver::gamma_hat(const AbstractState& alpha, - const AbstractState& exeState) const -{ - Z3Expr res(Z3Expr::getContext().bool_val(true)); - for (auto& item : exeState.getVarToVal()) - { - IntervalValue interval = alpha[item.first].getInterval(); - if (interval.isBottom()) - return Z3Expr::getContext().bool_val(false); - if (interval.isTop()) - continue; - Z3Expr v = toIntZ3Expr(item.first); - res = (res && v >= (int)interval.lb().getNumeral() && - v <= (int)interval.ub().getNumeral()).simplify(); - } - return res; -} - -Z3Expr RelationSolver::gamma_hat(u32_t id, const AbstractState& exeState) const -{ - auto it = exeState.getVarToVal().find(id); - assert(it != exeState.getVarToVal().end() && "id not in varToVal?"); - Z3Expr v = toIntZ3Expr(id); - // Z3Expr v = Z3Expr::getContext().int_const(std::to_string(id).c_str()); - Z3Expr res = (v >= (int)it->second.getInterval().lb().getNumeral() && - v <= (int)it->second.getInterval().ub().getNumeral()); - return res; -} - -AbstractState RelationSolver::beta(const Map& sigma, - const AbstractState& exeState) const -{ - AbstractState res; - for (const auto& item : exeState.getVarToVal()) - { - res[item.first] = IntervalValue( - sigma.at(item.first), sigma.at(item.first)); - } - return res; -} - -void RelationSolver::updateMap(Map& map, u32_t key, const s32_t& value) -{ - auto it = map.find(key); - if (it == map.end()) - { - map.emplace(key, value); - } - else - { - it->second = value; - } -} - -AbstractState RelationSolver::BS(const AbstractState& domain, const Z3Expr &phi) -{ - /// because key of _varToItvVal is u32_t, -key may out of range for int - /// so we do key + bias for -key - u32_t bias = 0; - s32_t infinity = INT32_MAX/2 - 1; - - // int infinity = (INT32_MAX) - 1; - // int infinity = 20; - Map ret; - Map low_values, high_values; - Z3Expr new_phi = phi; - /// init low, ret, high - for (const auto& item: domain.getVarToVal()) - { - IntervalValue interval = item.second.getInterval(); - updateMap(ret, item.first, interval.ub().getIntNumeral()); - if (interval.lb().is_minus_infinity()) - updateMap(low_values, item.first, -infinity); - else - updateMap(low_values, item.first, interval.lb().getIntNumeral()); - if (interval.ub().is_plus_infinity()) - updateMap(high_values, item.first, infinity); - else - updateMap(high_values, item.first, interval.ub().getIntNumeral()); - if (item.first > bias) - bias = item.first + 1; - } - for (const auto& item: domain.getVarToVal()) - { - /// init objects -x - IntervalValue interval = item.second.getInterval(); - u32_t reverse_key = item.first + bias; - updateMap(ret, reverse_key, -interval.lb().getIntNumeral()); - if (interval.ub().is_plus_infinity()) - updateMap(low_values, reverse_key, -infinity); - else - updateMap(low_values, reverse_key, -interval.ub().getIntNumeral()); - if (interval.lb().is_minus_infinity()) - updateMap(high_values, reverse_key, infinity); - else - updateMap(high_values, reverse_key, -interval.lb().getIntNumeral()); - /// add a relation that x == -(x+bias) - new_phi = (new_phi && (toIntZ3Expr(reverse_key) == -1 * toIntZ3Expr(item.first))); - } - /// optimize each object - BoxedOptSolver(new_phi.simplify(), ret, low_values, high_values); - /// fill in the return values - AbstractState retInv; - for (const auto& item: ret) - { - if (item.first >= bias) - { - if (!retInv.inVarToValTable(item.first-bias)) - retInv[item.first-bias] = IntervalValue::top(); - - if (item.second == (infinity)) - retInv[item.first - bias] = IntervalValue(BoundedInt::minus_infinity(), - retInv[item.first - bias].getInterval().ub()); - else - retInv[item.first - bias] = IntervalValue(float(-item.second), retInv[item.first - bias].getInterval().ub()); - - } - else - { - if (item.second == (infinity)) - retInv[item.first] = IntervalValue(retInv[item.first].getInterval().lb(), - BoundedInt::plus_infinity()); - else - retInv[item.first] = IntervalValue(retInv[item.first].getInterval().lb(), float(item.second)); - } - } - return retInv; -} - -Map RelationSolver::BoxedOptSolver(const Z3Expr& phi, Map& ret, Map& low_values, Map& high_values) -{ - /// this is the S in the original paper - Map L_phi; - Map mid_values; - while (1) - { - L_phi.clear(); - for (const auto& item : ret) - { - Z3Expr v = toIntZ3Expr(item.first); - if (low_values.at(item.first) <= (high_values.at(item.first))) - { - s32_t mid = (low_values.at(item.first) + (high_values.at(item.first) - low_values.at(item.first)) / 2); - updateMap(mid_values, item.first, mid); - Z3Expr expr = (toIntVal(mid) <= v && v <= toIntVal(high_values.at(item.first))); - L_phi[item.first] = expr; - } - } - if (L_phi.empty()) - break; - else - decide_cpa_ext(phi, L_phi, mid_values, ret, low_values, high_values); - } - return ret; -} - - -void RelationSolver::decide_cpa_ext(const Z3Expr& phi, - Map& L_phi, - Map& mid_values, - Map& ret, - Map& low_values, - Map& high_values) -{ - while (1) - { - Z3Expr join_expr(Z3Expr::getContext().bool_val(false)); - for (const auto& item : L_phi) - join_expr = (join_expr || item.second); - join_expr = (join_expr && phi).simplify(); - z3::solver& solver = Z3Expr::getSolver(); - solver.push(); - solver.add(join_expr.getExpr()); - Map solution; - z3::check_result checkRes = solver.check(); - /// find any solution, which is sat - if (checkRes == z3::sat) - { - z3::model m = solver.get_model(); - solver.pop(); - for(const auto & item : L_phi) - { - u32_t id = item.first; - int value = m.eval(toIntZ3Expr(id).getExpr()).get_numeral_int(); - // int value = m.eval(Z3Expr::getContext().int_const(std::to_string(id).c_str())).get_numeral_int(); - /// id is the var id, value is the solution found for var_id - /// add a relation to check if the solution meets phi_id - Z3Expr expr = (item.second && toIntZ3Expr(id) == value); - solver.push(); - solver.add(expr.getExpr()); - // solution meets phi_id - if (solver.check() == z3::sat) - { - updateMap(ret, id, (value)); - updateMap(low_values, id, ret.at(id) + 1); - - s32_t mid = (low_values.at(id) + high_values.at(id) + 1) / 2; - updateMap(mid_values, id, mid); - Z3Expr v = toIntZ3Expr(id); - // Z3Expr v = Z3Expr::getContext().int_const(std::to_string(id).c_str()); - Z3Expr expr = (toIntVal(mid_values.at(id)) <= v && v <= toIntVal(high_values.at(id))); - L_phi[id] = expr; - } - solver.pop(); - } - } - else /// unknown or unsat, we consider unknown as unsat - { - solver.pop(); - for (const auto& item : L_phi) - high_values.at(item.first) = mid_values.at(item.first) - 1; - return; - } - } - -} \ No newline at end of file diff --git a/svf/lib/AE/Svfexe/AbstractInterpretation.cpp b/svf/lib/AE/Svfexe/AbstractInterpretation.cpp deleted file mode 100644 index be4c16aa1..000000000 --- a/svf/lib/AE/Svfexe/AbstractInterpretation.cpp +++ /dev/null @@ -1,1757 +0,0 @@ -//===- AbstractExecution.cpp -- Abstract Execution---------------------------------// -// -// SVF: Static Value-Flow Analysis -// -// Copyright (C) <2013-> -// - -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU Affero General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Affero General Public License for more details. - -// You should have received a copy of the GNU Affero General Public License -// along with this program. If not, see . -// -//===----------------------------------------------------------------------===// - - -// -// Created by Jiawei Wang on 2024/1/10. -// - -#include "AE/Svfexe/AbstractInterpretation.h" -#include "SVFIR/SVFIR.h" -#include "Util/Options.h" -#include "Util/WorkList.h" -#include - -using namespace SVF; -using namespace SVFUtil; -using namespace z3; - -// according to varieties of cmp insts, -// maybe var X var, var X const, const X var, const X const -// we accept 'var X const' 'var X var' 'const X const' -// if 'const X var', we need to reverse op0 op1 and its predicate 'var X' const' -// X' is reverse predicate of X -// == -> !=, != -> ==, > -> <=, >= -> <, < -> >=, <= -> > - -Map _reverse_predicate = -{ - {CmpStmt::Predicate::FCMP_OEQ, CmpStmt::Predicate::FCMP_ONE}, // == -> != - {CmpStmt::Predicate::FCMP_UEQ, CmpStmt::Predicate::FCMP_UNE}, // == -> != - {CmpStmt::Predicate::FCMP_OGT, CmpStmt::Predicate::FCMP_OLE}, // > -> <= - {CmpStmt::Predicate::FCMP_OGE, CmpStmt::Predicate::FCMP_OLT}, // >= -> < - {CmpStmt::Predicate::FCMP_OLT, CmpStmt::Predicate::FCMP_OGE}, // < -> >= - {CmpStmt::Predicate::FCMP_OLE, CmpStmt::Predicate::FCMP_OGT}, // <= -> > - {CmpStmt::Predicate::FCMP_ONE, CmpStmt::Predicate::FCMP_OEQ}, // != -> == - {CmpStmt::Predicate::FCMP_UNE, CmpStmt::Predicate::FCMP_UEQ}, // != -> == - {CmpStmt::Predicate::ICMP_EQ, CmpStmt::Predicate::ICMP_NE}, // == -> != - {CmpStmt::Predicate::ICMP_NE, CmpStmt::Predicate::ICMP_EQ}, // != -> == - {CmpStmt::Predicate::ICMP_UGT, CmpStmt::Predicate::ICMP_ULE}, // > -> <= - {CmpStmt::Predicate::ICMP_ULT, CmpStmt::Predicate::ICMP_UGE}, // < -> >= - {CmpStmt::Predicate::ICMP_UGE, CmpStmt::Predicate::ICMP_ULT}, // >= -> < - {CmpStmt::Predicate::ICMP_SGT, CmpStmt::Predicate::ICMP_SLE}, // > -> <= - {CmpStmt::Predicate::ICMP_SLT, CmpStmt::Predicate::ICMP_SGE}, // < -> >= - {CmpStmt::Predicate::ICMP_SGE, CmpStmt::Predicate::ICMP_SLT}, // >= -> < -}; - - -Map _switch_lhsrhs_predicate = -{ - {CmpStmt::Predicate::FCMP_OEQ, CmpStmt::Predicate::FCMP_OEQ}, // == -> == - {CmpStmt::Predicate::FCMP_UEQ, CmpStmt::Predicate::FCMP_UEQ}, // == -> == - {CmpStmt::Predicate::FCMP_OGT, CmpStmt::Predicate::FCMP_OLT}, // > -> < - {CmpStmt::Predicate::FCMP_OGE, CmpStmt::Predicate::FCMP_OLE}, // >= -> <= - {CmpStmt::Predicate::FCMP_OLT, CmpStmt::Predicate::FCMP_OGT}, // < -> > - {CmpStmt::Predicate::FCMP_OLE, CmpStmt::Predicate::FCMP_OGE}, // <= -> >= - {CmpStmt::Predicate::FCMP_ONE, CmpStmt::Predicate::FCMP_ONE}, // != -> != - {CmpStmt::Predicate::FCMP_UNE, CmpStmt::Predicate::FCMP_UNE}, // != -> != - {CmpStmt::Predicate::ICMP_EQ, CmpStmt::Predicate::ICMP_EQ}, // == -> == - {CmpStmt::Predicate::ICMP_NE, CmpStmt::Predicate::ICMP_NE}, // != -> != - {CmpStmt::Predicate::ICMP_UGT, CmpStmt::Predicate::ICMP_ULT}, // > -> < - {CmpStmt::Predicate::ICMP_ULT, CmpStmt::Predicate::ICMP_UGT}, // < -> > - {CmpStmt::Predicate::ICMP_UGE, CmpStmt::Predicate::ICMP_ULE}, // >= -> <= - {CmpStmt::Predicate::ICMP_SGT, CmpStmt::Predicate::ICMP_SLT}, // > -> < - {CmpStmt::Predicate::ICMP_SLT, CmpStmt::Predicate::ICMP_SGT}, // < -> > - {CmpStmt::Predicate::ICMP_SGE, CmpStmt::Predicate::ICMP_SLE}, // >= -> <= -}; - - -void AbstractInterpretation::runOnModule(ICFG *icfg) -{ - // 1. Start clock - _stat->startClk(); - _icfg = icfg; - _svfir = PAG::getPAG(); - _ander = AndersenWaveDiff::createAndersenWaveDiff(_svfir); - // init SVF Execution States - _svfir2AbsState = new SVFIR2AbsState(_svfir); - - // init SSE External API Handler - _callgraph = _ander->getPTACallGraph(); - - /// collect checkpoint - collectCheckPoint(); - - /// if function contains callInst that call itself, it is a recursive function. - markRecursiveFuns(); - for (const SVFFunction* fun: _svfir->getModule()->getFunctionSet()) - { - auto *wto = new ICFGWTO(_icfg, _icfg->getFunEntryICFGNode(fun)); - wto->init(); - _funcToWTO[fun] = wto; - } - analyse(); - checkPointAllSet(); - // 5. Stop clock and report bugs - _stat->endClk(); - _stat->finializeStat(); - if (Options::PStat()) - { - _stat->performStat(); - } - _stat->reportBug(); -} - -AbstractInterpretation::AbstractInterpretation() -{ - _stat = new AEStat(this); - initExtFunMap(); -} -/// Destructor -AbstractInterpretation::~AbstractInterpretation() -{ - delete _stat; - delete _svfir2AbsState; - for (auto it: _funcToWTO) - delete it.second; - -} - -void AbstractInterpretation::markRecursiveFuns() -{ - // detect if callgraph has cycle - CallGraphSCC* _callGraphScc = _ander->getCallGraphSCC(); - _callGraphScc->find(); - - for (auto it = _callgraph->begin(); it != _callgraph->end(); it++) - { - if (_callGraphScc->isInCycle(it->second->getId())) - _recursiveFuns.insert(it->second->getFunction()); - } -} - -/// Program entry -void AbstractInterpretation::analyse() -{ - // handle Global ICFGNode of SVFModule - handleGlobalNode(); - getAbsState(_icfg->getGlobalICFGNode())[PAG::getPAG()->getBlkPtr()] = IntervalValue::top(); - if (const SVFFunction* fun = _svfir->getModule()->getSVFFunction("main")) - { - handleFunc(fun); - } -} - -/// handle global node -void AbstractInterpretation::handleGlobalNode() -{ - AbstractState as; - const ICFGNode* node = _icfg->getGlobalICFGNode(); - _postAbsTrace[node] = _preAbsTrace[node]; - _postAbsTrace[node][SymbolTableInfo::NullPtr] = AddressValue(); - // Global Node, we just need to handle addr, load, store, copy and gep - for (const SVFStmt *stmt: node->getSVFStmts()) - { - handleSVFStatement(stmt); - } -} - -/// get execution state by merging states of predecessor blocks -/// Scenario 1: preblock -----(intraEdge)----> block, join the preES of inEdges -/// Scenario 2: preblock -----(callEdge)----> block -bool AbstractInterpretation::propagateStateIfFeasible(const ICFGNode *block) -{ - AbstractState as; - u32_t inEdgeNum = 0; - for (auto& edge: block->getInEdges()) - { - if (_postAbsTrace.find(edge->getSrcNode()) != _postAbsTrace.end()) - { - const IntraCFGEdge *intraCfgEdge = SVFUtil::dyn_cast(edge); - if (intraCfgEdge && intraCfgEdge->getCondition()) - { - AbstractState tmpEs = _postAbsTrace[edge->getSrcNode()]; - if (isBranchFeasible(intraCfgEdge, tmpEs)) - { - as.joinWith(tmpEs); - inEdgeNum++; - } - else - { - // do nothing - } - } - else - { - as.joinWith(_postAbsTrace[edge->getSrcNode()]); - inEdgeNum++; - } - } - else - { - - } - } - if (inEdgeNum == 0) - { - return false; - } - else - { - _preAbsTrace[block] = as; - return true; - } - assert(false && "implement this part"); -} - - -bool AbstractInterpretation::isCmpBranchFeasible(const CmpStmt* cmpStmt, s64_t succ, - AbstractState& as) -{ - AbstractState new_es = as; - // get cmp stmt's op0, op1, and predicate - NodeID op0 = cmpStmt->getOpVarID(0); - NodeID op1 = cmpStmt->getOpVarID(1); - NodeID res_id = cmpStmt->getResID(); - s32_t predicate = cmpStmt->getPredicate(); - - // if op0 or op1 is undefined, return; - // skip address compare - if (new_es.inVarToAddrsTable(op0) || new_es.inVarToAddrsTable(op1)) - { - as = new_es; - return true; - } - const LoadStmt *load_op0 = nullptr; - const LoadStmt *load_op1 = nullptr; - // get '%1 = load i32 s', and load inst may not exist - SVFVar* loadVar0 = _svfir->getGNode(op0); - if (!loadVar0->getInEdges().empty()) - { - SVFStmt *loadVar0InStmt = *loadVar0->getInEdges().begin(); - if (const LoadStmt *loadStmt = SVFUtil::dyn_cast(loadVar0InStmt)) - { - load_op0 = loadStmt; - } - else if (const CopyStmt *copyStmt = SVFUtil::dyn_cast(loadVar0InStmt)) - { - loadVar0 = _svfir->getGNode(copyStmt->getRHSVarID()); - if (!loadVar0->getInEdges().empty()) - { - SVFStmt *loadVar0InStmt2 = *loadVar0->getInEdges().begin(); - if (const LoadStmt *loadStmt = SVFUtil::dyn_cast(loadVar0InStmt2)) - { - load_op0 = loadStmt; - } - } - } - } - - SVFVar* loadVar1 = _svfir->getGNode(op1); - if (!loadVar1->getInEdges().empty()) - { - SVFStmt *loadVar1InStmt = *loadVar1->getInEdges().begin(); - if (const LoadStmt *loadStmt = SVFUtil::dyn_cast(loadVar1InStmt)) - { - load_op1 = loadStmt; - } - else if (const CopyStmt *copyStmt = SVFUtil::dyn_cast(loadVar1InStmt)) - { - loadVar1 = _svfir->getGNode(copyStmt->getRHSVarID()); - if (!loadVar1->getInEdges().empty()) - { - SVFStmt *loadVar1InStmt2 = *loadVar1->getInEdges().begin(); - if (const LoadStmt *loadStmt = SVFUtil::dyn_cast(loadVar1InStmt2)) - { - load_op1 = loadStmt; - } - } - } - } - // for const X const, we may get concrete resVal instantly - // for var X const, we may get [0,1] if the intersection of var and const is not empty set - IntervalValue resVal = new_es[res_id].getInterval(); - resVal.meet_with(IntervalValue((s64_t) succ, succ)); - // If Var X const generates bottom value, it means this branch path is not feasible. - if (resVal.isBottom()) - { - return false; - } - - bool b0 = new_es[op0].getInterval().is_numeral(); - bool b1 = new_es[op1].getInterval().is_numeral(); - - // if const X var, we should reverse op0 and op1. - if (b0 && !b1) - { - std::swap(op0, op1); - std::swap(load_op0, load_op1); - predicate = _switch_lhsrhs_predicate[predicate]; - } - else - { - // if var X var, we cannot preset the branch condition to infer the intervals of var0,var1 - if (!b0 && !b1) - { - as = new_es; - return true; - } - // if const X const, we can instantly get the resVal - else if (b0 && b1) - { - as = new_es; - return true; - } - } - // if cmp is 'var X const == false', we should reverse predicate 'var X' const == true' - // X' is reverse predicate of X - if (succ == 0) - { - predicate = _reverse_predicate[predicate]; - } - else {} - // change interval range according to the compare predicate - AddressValue addrs; - if(load_op0 && new_es.inVarToAddrsTable(load_op0->getRHSVarID())) - addrs = new_es[load_op0->getRHSVarID()].getAddrs(); - - IntervalValue &lhs = new_es[op0].getInterval(), &rhs = new_es[op1].getInterval(); - switch (predicate) - { - case CmpStmt::Predicate::ICMP_EQ: - case CmpStmt::Predicate::FCMP_OEQ: - case CmpStmt::Predicate::FCMP_UEQ: - { - // Var == Const, so [var.lb, var.ub].meet_with(const) - lhs.meet_with(rhs); - // if lhs is register value, we should also change its mem obj - for (const auto &addr: addrs) - { - NodeID objId = new_es.getInternalID(addr); - if (new_es.inAddrToValTable(objId)) - { - new_es.load(addr).meet_with(rhs); - } - } - break; - } - case CmpStmt::Predicate::ICMP_NE: - case CmpStmt::Predicate::FCMP_ONE: - case CmpStmt::Predicate::FCMP_UNE: - // Compliment set - break; - case CmpStmt::Predicate::ICMP_UGT: - case CmpStmt::Predicate::ICMP_SGT: - case CmpStmt::Predicate::FCMP_OGT: - case CmpStmt::Predicate::FCMP_UGT: - // Var > Const, so [var.lb, var.ub].meet_with([Const+1, +INF]) - lhs.meet_with(IntervalValue(rhs.lb() + 1, IntervalValue::plus_infinity())); - // if lhs is register value, we should also change its mem obj - for (const auto &addr: addrs) - { - NodeID objId = new_es.getInternalID(addr); - if (new_es.inAddrToValTable(objId)) - { - new_es.load(addr).meet_with( - IntervalValue(rhs.lb() + 1, IntervalValue::plus_infinity())); - } - } - break; - case CmpStmt::Predicate::ICMP_UGE: - case CmpStmt::Predicate::ICMP_SGE: - case CmpStmt::Predicate::FCMP_OGE: - case CmpStmt::Predicate::FCMP_UGE: - { - // Var >= Const, so [var.lb, var.ub].meet_with([Const, +INF]) - lhs.meet_with(IntervalValue(rhs.lb(), IntervalValue::plus_infinity())); - // if lhs is register value, we should also change its mem obj - for (const auto &addr: addrs) - { - NodeID objId = new_es.getInternalID(addr); - if (new_es.inAddrToValTable(objId)) - { - new_es.load(addr).meet_with( - IntervalValue(rhs.lb(), IntervalValue::plus_infinity())); - } - } - break; - } - case CmpStmt::Predicate::ICMP_ULT: - case CmpStmt::Predicate::ICMP_SLT: - case CmpStmt::Predicate::FCMP_OLT: - case CmpStmt::Predicate::FCMP_ULT: - { - // Var < Const, so [var.lb, var.ub].meet_with([-INF, const.ub-1]) - lhs.meet_with(IntervalValue(IntervalValue::minus_infinity(), rhs.ub() - 1)); - // if lhs is register value, we should also change its mem obj - for (const auto &addr: addrs) - { - NodeID objId = new_es.getInternalID(addr); - if (new_es.inAddrToValTable(objId)) - { - new_es.load(addr).meet_with( - IntervalValue(IntervalValue::minus_infinity(), rhs.ub() - 1)); - } - } - break; - } - case CmpStmt::Predicate::ICMP_ULE: - case CmpStmt::Predicate::ICMP_SLE: - case CmpStmt::Predicate::FCMP_OLE: - case CmpStmt::Predicate::FCMP_ULE: - { - // Var <= Const, so [var.lb, var.ub].meet_with([-INF, const.ub]) - lhs.meet_with(IntervalValue(IntervalValue::minus_infinity(), rhs.ub())); - // if lhs is register value, we should also change its mem obj - for (const auto &addr: addrs) - { - NodeID objId = new_es.getInternalID(addr); - if (new_es.inAddrToValTable(objId)) - { - new_es.load(addr).meet_with( - IntervalValue(IntervalValue::minus_infinity(), rhs.ub())); - } - } - break; - } - case CmpStmt::Predicate::FCMP_FALSE: - break; - case CmpStmt::Predicate::FCMP_TRUE: - break; - default: - assert(false && "implement this part"); - abort(); - } - as = new_es; - return true; -} - -bool AbstractInterpretation::isSwitchBranchFeasible(const SVFVar* var, s64_t succ, - AbstractState& as) -{ - AbstractState new_es = as; - IntervalValue& switch_cond = new_es[var->getId()].getInterval(); - s64_t value = succ; - FIFOWorkList workList; - for (SVFStmt *cmpVarInStmt: var->getInEdges()) - { - workList.push(cmpVarInStmt); - } - switch_cond.meet_with(IntervalValue(value, value)); - if (switch_cond.isBottom()) - { - return false; - } - while(!workList.empty()) - { - const SVFStmt* stmt = workList.pop(); - if (SVFUtil::isa(stmt)) - { - IntervalValue& copy_cond = new_es[var->getId()].getInterval(); - copy_cond.meet_with(IntervalValue(value, value)); - } - else if (const LoadStmt* load = SVFUtil::dyn_cast(stmt)) - { - if (new_es.inVarToAddrsTable(load->getRHSVarID())) - { - AddressValue &addrs = new_es[load->getRHSVarID()].getAddrs(); - for (const auto &addr: addrs) - { - NodeID objId = new_es.getInternalID(addr); - if (new_es.inAddrToValTable(objId)) - { - new_es.load(addr).meet_with(switch_cond); - } - } - } - } - } - as = new_es; - return true; -} - -bool AbstractInterpretation::isBranchFeasible(const IntraCFGEdge* intraEdge, - AbstractState& as) -{ - const SVFValue *cond = intraEdge->getCondition(); - NodeID cmpID = _svfir->getValueNode(cond); - SVFVar *cmpVar = _svfir->getGNode(cmpID); - if (cmpVar->getInEdges().empty()) - { - return isSwitchBranchFeasible(cmpVar, - intraEdge->getSuccessorCondValue(), as); - } - else - { - assert(!cmpVar->getInEdges().empty() && - "no in edges?"); - SVFStmt *cmpVarInStmt = *cmpVar->getInEdges().begin(); - if (const CmpStmt *cmpStmt = SVFUtil::dyn_cast(cmpVarInStmt)) - { - return isCmpBranchFeasible(cmpStmt, - intraEdge->getSuccessorCondValue(), as); - } - else - { - return isSwitchBranchFeasible( - cmpVar, intraEdge->getSuccessorCondValue(), as); - } - } - return true; -} -/// handle instructions in svf basic blocks -void AbstractInterpretation::handleWTONode(const ICFGSingletonWTO *icfgSingletonWto) -{ - const ICFGNode* node = icfgSingletonWto->node(); - _stat->getBlockTrace()++; - // Get execution states from in edges - if (!propagateStateIfFeasible(node)) - { - // No ES on the in edges - Infeasible block - return; - } - else - { - // Has ES on the in edges - Feasible block - // Get execution state from in edges - _postAbsTrace[node] = _preAbsTrace[node]; - } - - std::deque worklist; - - const std::vector& worklist_vec = _icfg->getSubNodes(node); - for (auto it = worklist_vec.begin(); it != worklist_vec.end(); ++it) - { - const ICFGNode* curNode = *it; - handleICFGNode(curNode); - } -} - -void AbstractInterpretation::handleCallSite(const ICFGNode* node) -{ - if (const CallICFGNode* callNode = SVFUtil::dyn_cast(node)) - { - if (isExtCall(callNode)) - { - extCallPass(callNode); - } - else if (isRecursiveCall(callNode)) - { - recursiveCallPass(callNode); - } - else if (isDirectCall(callNode)) - { - directCallFunPass(callNode); - } - else if (isIndirectCall(callNode)) - { - indirectCallFunPass(callNode); - } - else - { - assert(false && "implement this part"); - } - } - else - { - assert (false && "it is not call node"); - } -} - -bool AbstractInterpretation::isExtCall(const SVF::CallICFGNode *callNode) -{ - const SVFFunction *callfun = SVFUtil::getCallee(callNode->getCallSite()); - return SVFUtil::isExtCall(callfun); -} - -void AbstractInterpretation::extCallPass(const SVF::CallICFGNode *callNode) -{ - _callSiteStack.push_back(callNode); - handleExtAPI(callNode); - _callSiteStack.pop_back(); -} - -bool AbstractInterpretation::isRecursiveCall(const SVF::CallICFGNode *callNode) -{ - const SVFFunction *callfun = SVFUtil::getCallee(callNode->getCallSite()); - return _recursiveFuns.find(callfun) != _recursiveFuns.end(); -} - -void AbstractInterpretation::recursiveCallPass(const SVF::CallICFGNode *callNode) -{ - AbstractState& as = getAbsState(callNode); - SkipRecursiveCall(callNode); - const RetICFGNode *retNode = callNode->getRetICFGNode(); - if (retNode->getSVFStmts().size() > 0) - { - if (const RetPE *retPE = SVFUtil::dyn_cast(*retNode->getSVFStmts().begin())) - { - if (!retPE->getLHSVar()->isPointer() && - !retPE->getLHSVar()->isConstDataOrAggDataButNotNullPtr()) - { - as[retPE->getLHSVarID()] = IntervalValue::top(); - } - } - } - _postAbsTrace[retNode] = as; -} - -bool AbstractInterpretation::isDirectCall(const SVF::CallICFGNode *callNode) -{ - const SVFFunction *callfun = SVFUtil::getCallee(callNode->getCallSite()); - return _funcToWTO.find(callfun) != _funcToWTO.end(); -} -void AbstractInterpretation::directCallFunPass(const SVF::CallICFGNode *callNode) -{ - AbstractState& as = getAbsState(callNode); - const SVFFunction *callfun = SVFUtil::getCallee(callNode->getCallSite()); - _callSiteStack.push_back(callNode); - - _postAbsTrace[callNode] = as; - - handleFunc(callfun); - _callSiteStack.pop_back(); - // handle Ret node - const RetICFGNode *retNode = callNode->getRetICFGNode(); - // resume ES to callnode - _postAbsTrace[retNode] = _postAbsTrace[callNode]; -} - -bool AbstractInterpretation::isIndirectCall(const SVF::CallICFGNode *callNode) -{ - const auto callsiteMaps = _svfir->getIndirectCallsites(); - return callsiteMaps.find(callNode) != callsiteMaps.end(); -} - -void AbstractInterpretation::indirectCallFunPass(const SVF::CallICFGNode *callNode) -{ - AbstractState& as = getAbsState(callNode); - const auto callsiteMaps = _svfir->getIndirectCallsites(); - NodeID call_id = callsiteMaps.at(callNode); - if (!as.inVarToAddrsTable(call_id)) - { - return; - } - AbstractValue Addrs = - _svfir2AbsState->getAddrs(as, call_id); //_svfir2ExeState->getEs() - NodeID addr = *Addrs.getAddrs().begin(); - SVFVar *func_var = _svfir->getGNode(AbstractState::getInternalID(addr)); - const SVFFunction *callfun = SVFUtil::dyn_cast(func_var->getValue()); - if (callfun) - { - _callSiteStack.push_back(callNode); - _postAbsTrace[callNode] = as; - - handleFunc(callfun); - _callSiteStack.pop_back(); - // handle Ret node - const RetICFGNode *retNode = callNode->getRetICFGNode(); - _postAbsTrace[retNode] = _postAbsTrace[callNode]; - } -} - - - -void AbstractInterpretation::handleICFGNode(const ICFGNode *curICFGNode) -{ - _stat->getICFGNodeTrace()++; - // handle SVF Stmt - for (const SVFStmt *stmt: curICFGNode->getSVFStmts()) - { - handleSVFStatement(stmt); - } - // inlining the callee by calling handleFunc for the callee function - if (const CallICFGNode* callnode = SVFUtil::dyn_cast(curICFGNode)) - { - handleCallSite(callnode); - } - else - { - - } - _stat->countStateSize(); -} - -/// handle wto cycle (loop) -void AbstractInterpretation::handleCycle(const ICFGCycleWTO*cycle) -{ - // Get execution states from in edges - if (!propagateStateIfFeasible(cycle->head()->node())) - { - // No ES on the in edges - Infeasible block - return; - } - AbstractState pre_es = _preAbsTrace[cycle->head()->node()]; - // set -widen-delay - s32_t widen_delay = Options::WidenDelay(); - bool incresing = true; - for (int i = 0; ; i++) - { - handleWTONode(cycle->head()); - if (i < widen_delay) - { - if (i> 0 && pre_es >= _postAbsTrace[cycle->head()->node()]) - { - break; - } - pre_es = _postAbsTrace[cycle->head()->node()]; - } - else - { - if (i >= widen_delay) - { - if (incresing) - { - bool is_fixpoint = - isFixPointAfterWidening(cycle->head()->node(), pre_es); - if (is_fixpoint) - { - incresing = false; - continue; - } - } - else if (!incresing) - { - bool is_fixpoint = - isFixPointAfterNarrowing(cycle->head()->node(), pre_es); - if (is_fixpoint) - break; - } - } - } - for (auto it = cycle->begin(); it != cycle->end(); ++it) - { - const ICFGWTOComp* cur = *it; - if (const ICFGSingletonWTO* vertex = SVFUtil::dyn_cast(cur)) - { - handleWTONode(vertex); - } - else if (const ICFGCycleWTO* cycle2 = SVFUtil::dyn_cast(cur)) - { - handleCycle(cycle2); - } - else - { - assert(false && "unknown WTO type!"); - } - } - } -} - -bool AbstractInterpretation::isFixPointAfterWidening(const ICFGNode* cycle_head, - AbstractState& pre_as) -{ - // increasing iterations - AbstractState new_pre_as = pre_as.widening(_postAbsTrace[cycle_head]); - AbstractState new_pre_vaddr_as = new_pre_as; - //_svfir2AbsState->widenAddrs(getCurState(), new_pre_es, _postAbsTrace[cycle_head]); - - if (pre_as >= new_pre_as) - { - // increasing iterations - fixpoint reached - pre_as = new_pre_as; - _postAbsTrace[cycle_head] = pre_as; - return true; - } - else - { - pre_as = new_pre_as; - _postAbsTrace[cycle_head] = pre_as; - return false; - } -} - -bool AbstractInterpretation::isFixPointAfterNarrowing( - const SVF::ICFGNode* cycle_head, SVF::AbstractState& pre_as) -{ - // decreasing iterations - AbstractState new_pre_as = pre_as.narrowing(_postAbsTrace[cycle_head]); - AbstractState new_pre_vaddr_as = new_pre_as; - //_svfir2AbsState->narrowAddrs(getCurState(), new_pre_es, _postAbsTrace[cycle_head]); - if (new_pre_as >= pre_as) - { - // decreasing iterations - fixpoint reached - pre_as = new_pre_as; - _postAbsTrace[cycle_head] = pre_as; - return true; - } - else - { - pre_as = new_pre_as; - _postAbsTrace[cycle_head] = pre_as; - return false; - } -} - - - -/// handle user defined function, ext function is not included. -void AbstractInterpretation::handleFunc(const SVFFunction *func) -{ - _stat->getFunctionTrace()++; - ICFGWTO* wto = _funcToWTO[func]; - // set function entry ES - for (auto it = wto->begin(); it!= wto->end(); ++it) - { - const ICFGWTOComp* cur = *it; - if (const ICFGSingletonWTO* vertex = SVFUtil::dyn_cast(cur)) - { - handleWTONode(vertex); - } - else if (const ICFGCycleWTO* cycle = SVFUtil::dyn_cast(cur)) - { - handleCycle(cycle); - } - else - { - assert(false && "unknown WTO type!"); - } - } -} - -void AbstractInterpretation::handleSVFStatement(const SVFStmt *stmt) -{ - AbstractState& as = getAbsState(stmt->getICFGNode()); - if (const AddrStmt *addr = SVFUtil::dyn_cast(stmt)) - { - _svfir2AbsState->handleAddr(as, addr); - } - else if (const BinaryOPStmt *binary = SVFUtil::dyn_cast(stmt)) - { - _svfir2AbsState->handleBinary(as, binary); - } - else if (const CmpStmt *cmp = SVFUtil::dyn_cast(stmt)) - { - _svfir2AbsState->handleCmp(as, cmp); - } - else if (SVFUtil::isa(stmt)) - { - } - else if (SVFUtil::isa(stmt)) - { - // branch stmt is handled in hasBranchES - } - else if (const LoadStmt *load = SVFUtil::dyn_cast(stmt)) - { - _svfir2AbsState->handleLoad(as, load); - } - else if (const StoreStmt *store = SVFUtil::dyn_cast(stmt)) - { - _svfir2AbsState->handleStore(as, store); - } - else if (const CopyStmt *copy = SVFUtil::dyn_cast(stmt)) - { - _svfir2AbsState->handleCopy(as, copy); - } - else if (const GepStmt *gep = SVFUtil::dyn_cast(stmt)) - { - _svfir2AbsState->handleGep(as, gep); - } - else if (const SelectStmt *select = SVFUtil::dyn_cast(stmt)) - { - _svfir2AbsState->handleSelect(as, select); - } - else if (const PhiStmt *phi = SVFUtil::dyn_cast(stmt)) - { - _svfir2AbsState->handlePhi(as, phi); - } - else if (const CallPE *callPE = SVFUtil::dyn_cast(stmt)) - { - // To handle Call Edge - _svfir2AbsState->handleCall(as, callPE); - } - else if (const RetPE *retPE = SVFUtil::dyn_cast(stmt)) - { - _svfir2AbsState->handleRet(as, retPE); - } - else - assert(false && "implement this part"); -} - - -void AbstractInterpretation::SkipRecursiveCall(const CallICFGNode *callNode) -{ - AbstractState& as = getAbsState(callNode); - const SVFFunction *callfun = SVFUtil::getCallee(callNode->getCallSite()); - const RetICFGNode *retNode = callNode->getRetICFGNode(); - if (retNode->getSVFStmts().size() > 0) - { - if (const RetPE *retPE = SVFUtil::dyn_cast(*retNode->getSVFStmts().begin())) - { - AbstractState as; - if (!retPE->getLHSVar()->isPointer() && !retPE->getLHSVar()->isConstDataOrAggDataButNotNullPtr()) - as[retPE->getLHSVarID()] = IntervalValue::top(); - } - } - if (!retNode->getOutEdges().empty()) - { - if (retNode->getOutEdges().size() == 1) - { - - } - else - { - return; - } - } - FIFOWorkList blkWorkList; - FIFOWorkList instWorklist; - for (const SVFBasicBlock * bb: callfun->getReachableBBs()) - { - for (const SVFInstruction* inst: bb->getInstructionList()) - { - const ICFGNode* node = _icfg->getICFGNode(inst); - for (const SVFStmt *stmt: node->getSVFStmts()) - { - if (const StoreStmt *store = SVFUtil::dyn_cast(stmt)) - { - const SVFVar *rhsVar = store->getRHSVar(); - u32_t lhs = store->getLHSVarID(); - if (as.inVarToAddrsTable(lhs)) - { - if (!rhsVar->isPointer() && !rhsVar->isConstDataOrAggDataButNotNullPtr()) - { - const AbstractValue &addrs = as[lhs]; - for (const auto &addr: addrs.getAddrs()) - { - as.store(addr, IntervalValue::top()); - } - } - } - } - } - } - } -} - -// count the size of memory map -void AEStat::countStateSize() -{ - if (count == 0) - { - generalNumMap["ES_Var_AVG_Num"] = 0; - generalNumMap["ES_Loc_AVG_Num"] = 0; - generalNumMap["ES_Var_Addr_AVG_Num"] = 0; - generalNumMap["ES_Loc_Addr_AVG_Num"] = 0; - } - ++count; -// generalNumMap["ES_Var_AVG_Num"] += -// _ae->getCurState().getVarToVal().size(); -// generalNumMap["ES_Loc_AVG_Num"] += -// _ae->getCurState().getLocToVal().size(); -} - -void AEStat::finializeStat() -{ - memUsage = getMemUsage(); - if (count > 0) - { - generalNumMap["ES_Var_AVG_Num"] /= count; - generalNumMap["ES_Loc_AVG_Num"] /= count; - generalNumMap["ES_Var_Addr_AVG_Num"] /= count; - generalNumMap["ES_Loc_Addr_AVG_Num"] /= count; - } - generalNumMap["SVF_STMT_NUM"] = count; - generalNumMap["ICFG_Node_Num"] = _ae->_svfir->getICFG()->nodeNum; - u32_t callSiteNum = 0; - u32_t extCallSiteNum = 0; - Set funs; - for (const auto &it: *_ae->_svfir->getICFG()) - { - if (it.second->getFun()) - { - funs.insert(it.second->getFun()); - } - if (const CallICFGNode *callNode = dyn_cast(it.second)) - { - if (!isExtCall(callNode->getCallSite())) - { - callSiteNum++; - } - else - { - extCallSiteNum++; - } - } - } - generalNumMap["Func_Num"] = funs.size(); - generalNumMap["EXT_CallSite_Num"] = extCallSiteNum; - generalNumMap["NonEXT_CallSite_Num"] = callSiteNum; - generalNumMap["Bug_Num"] = _ae->_nodeToBugInfo.size(); - timeStatMap["Total_Time(sec)"] = (double)(endTime - startTime) / TIMEINTERVAL; - -} - -void AEStat::performStat() -{ - std::string fullName(_ae->_moduleName); - std::string name; - std::string moduleName; - if (fullName.find('/') == std::string::npos) - { - std::string name = fullName; - moduleName = name.substr(0, fullName.find('.')); - } - else - { - std::string name = fullName.substr(fullName.find('/'), fullName.size()); - moduleName = name.substr(0, fullName.find('.')); - } - - SVFUtil::outs() << "\n************************\n"; - SVFUtil::outs() << "################ (program : " << moduleName << ")###############\n"; - SVFUtil::outs().flags(std::ios::left); - unsigned field_width = 30; - for (NUMStatMap::iterator it = generalNumMap.begin(), eit = generalNumMap.end(); it != eit; ++it) - { - // format out put with width 20 space - std::cout << std::setw(field_width) << it->first << it->second << "\n"; - } - SVFUtil::outs() << "-------------------------------------------------------\n"; - for (TIMEStatMap::iterator it = timeStatMap.begin(), eit = timeStatMap.end(); it != eit; ++it) - { - // format out put with width 20 space - SVFUtil::outs() << std::setw(field_width) << it->first << it->second << "\n"; - } - SVFUtil::outs() << "Memory usage: " << memUsage << "\n"; - - SVFUtil::outs() << "#######################################################" << std::endl; - SVFUtil::outs().flush(); -} - -void AEStat::reportBug() -{ - - std::ofstream f; - if (Options::OutputName().size() == 0) - { - f.open("/dev/null"); - } - else - { - f.open(Options::OutputName()); - } - - std::cerr << "######################Full Overflow (" + std::to_string(_ae->_nodeToBugInfo.size()) + " found)######################\n"; - f << "######################Full Overflow (" + std::to_string(_ae->_nodeToBugInfo.size()) + " found)######################\n"; - std::cerr << "---------------------------------------------\n"; - f << "---------------------------------------------\n"; - for (auto& it: _ae->_nodeToBugInfo) - { - std::cerr << it.second << "\n---------------------------------------------\n"; - f << it.second << "\n---------------------------------------------\n"; - } -} - -void AbstractInterpretation::initExtFunMap() -{ -#define SSE_FUNC_PROCESS(LLVM_NAME ,FUNC_NAME) \ - auto sse_##FUNC_NAME = [this](const CallSite &cs) { \ - /* run real ext function */ \ - const CallICFGNode* callNode = SVFUtil::dyn_cast(_svfir->getICFG()->getICFGNode(cs.getInstruction())); \ - AbstractState& as = getAbsState(callNode); \ - u32_t rhs_id = _svfir->getValueNode(cs.getArgument(0)); \ - if (!as.inVarToValTable(rhs_id)) return; \ - u32_t rhs = as[rhs_id].getInterval().lb().getIntNumeral(); \ - s32_t res = FUNC_NAME(rhs); \ - u32_t lhsId = _svfir->getValueNode(cs.getInstruction()); \ - as[lhsId] = IntervalValue(res); \ - return; \ - }; \ - _func_map[#FUNC_NAME] = sse_##FUNC_NAME; - - SSE_FUNC_PROCESS(isalnum, isalnum); - SSE_FUNC_PROCESS(isalpha, isalpha); - SSE_FUNC_PROCESS(isblank, isblank); - SSE_FUNC_PROCESS(iscntrl, iscntrl); - SSE_FUNC_PROCESS(isdigit, isdigit); - SSE_FUNC_PROCESS(isgraph, isgraph); - SSE_FUNC_PROCESS(isprint, isprint); - SSE_FUNC_PROCESS(ispunct, ispunct); - SSE_FUNC_PROCESS(isspace, isspace); - SSE_FUNC_PROCESS(isupper, isupper); - SSE_FUNC_PROCESS(isxdigit, isxdigit); - SSE_FUNC_PROCESS(llvm.sin.f64, sin); - SSE_FUNC_PROCESS(llvm.cos.f64, cos); - SSE_FUNC_PROCESS(llvm.tan.f64, tan); - SSE_FUNC_PROCESS(llvm.log.f64, log); - SSE_FUNC_PROCESS(sinh, sinh); - SSE_FUNC_PROCESS(cosh, cosh); - SSE_FUNC_PROCESS(tanh, tanh); - - auto sse_svf_assert = [this](const CallSite &cs) - { - const CallICFGNode* callNode = SVFUtil::dyn_cast(_svfir->getICFG()->getICFGNode(cs.getInstruction())); - _checkpoints.erase(callNode); - u32_t arg0 = _svfir->getValueNode(cs.getArgument(0)); - AbstractState&as = getAbsState(callNode); - as[arg0].getInterval().meet_with(IntervalValue(1, 1)); - if (as[arg0].getInterval().equals(IntervalValue(1, 1))) - { - SVFUtil::errs() << SVFUtil::sucMsg("The assertion is successfully verified!!\n"); - } - else - { - SVFUtil::errs() <<"svf_assert Fail. " << cs.getInstruction()->toString() << "\n"; - assert(false); - } - return; - }; - _func_map["svf_assert"] = sse_svf_assert; - - auto svf_print = [&](const CallSite &cs) - { - if (cs.arg_size() < 2) return; - const CallICFGNode* callNode = SVFUtil::dyn_cast(_svfir->getICFG()->getICFGNode(cs.getInstruction())); - AbstractState&as = getAbsState(callNode); - u32_t num_id = _svfir->getValueNode(cs.getArgument(0)); - std::string text = strRead(as, cs.getArgument(1)); - assert(as.inVarToValTable(num_id) && "print() should pass integer"); - IntervalValue itv = as[num_id].getInterval(); - std::cout << "Text: " << text <<", Value: " << cs.getArgument(0)->toString() << ", PrintVal: " << itv.toString() << std::endl; - return; - }; - _func_map["svf_print"] = svf_print; - - // init _checkpoint_names - _checkpoint_names.insert("svf_assert"); -}; - -std::string AbstractInterpretation::strRead(AbstractState& as, const SVFValue* rhs) -{ - // sse read string nodeID->string - std::string str0; - - for (u32_t index = 0; index < Options::MaxFieldLimit(); index++) - { - // dead loop for string and break if there's a \0. If no \0, it will throw err. - if (!as.inVarToAddrsTable(_svfir->getValueNode(rhs))) continue; - AbstractValue expr0 = - _svfir2AbsState->getGepObjAddress(as, _svfir->getValueNode(rhs), index); - AbstractValue val; - for (const auto &addr: expr0.getAddrs()) - { - val.join_with(as.load(addr)); - } - if (!val.getInterval().is_numeral()) - { - break; - } - if ((char) val.getInterval().getIntNumeral() == '\0') - { - break; - } - str0.push_back((char) val.getInterval().getIntNumeral()); - } - return str0; -} - -void AbstractInterpretation::handleExtAPI(const CallICFGNode *call) -{ - AbstractState& as = getAbsState(call); - const SVFFunction *fun = SVFUtil::getCallee(call->getCallSite()); - assert(fun && "SVFFunction* is nullptr"); - CallSite cs = SVFUtil::getSVFCallSite(call->getCallSite()); - ExtAPIType extType = UNCLASSIFIED; - // get type of mem api - for (const std::string &annotation: fun->getAnnotations()) - { - if (annotation.find("MEMCPY") != std::string::npos) - extType = MEMCPY; - if (annotation.find("MEMSET") != std::string::npos) - extType = MEMSET; - if (annotation.find("STRCPY") != std::string::npos) - extType = STRCPY; - if (annotation.find("STRCAT") != std::string::npos) - extType = STRCAT; - } - if (extType == UNCLASSIFIED) - { - if (_func_map.find(fun->getName()) != _func_map.end()) - { - _func_map[fun->getName()](cs); - } - else - { - u32_t lhsId = _svfir->getValueNode(SVFUtil::getSVFCallSite(call->getCallSite()).getInstruction()); - if (as.inVarToAddrsTable(lhsId)) - { - - } - else - { - as[lhsId] = IntervalValue(); - } - return; - } - } - // 1. memcpy functions like memcpy_chk, strncpy, annotate("MEMCPY"), annotate("BUF_CHECK:Arg0, Arg2"), annotate("BUF_CHECK:Arg1, Arg2") - else if (extType == MEMCPY) - { - IntervalValue len = as[_svfir->getValueNode(cs.getArgument(2))].getInterval(); - handleMemcpy(as, cs.getArgument(0), cs.getArgument(1), len, 0); - } - else if (extType == MEMSET) - { - // memset dst is arg0, elem is arg1, size is arg2 - IntervalValue len = as[_svfir->getValueNode(cs.getArgument(2))].getInterval(); - IntervalValue elem = as[_svfir->getValueNode(cs.getArgument(1))].getInterval(); - handleMemset(as,cs.getArgument(0), elem, len); - } - else if (extType == STRCPY) - { - handleStrcpy(call); - } - else if (extType == STRCAT) - { - handleStrcat(call); - } - else - { - - } - return; -} - -void AbstractInterpretation::collectCheckPoint() -{ - // traverse every ICFGNode - for (auto it = _svfir->getICFG()->begin(); it != _svfir->getICFG()->end(); ++it) - { - const ICFGNode* node = it->second; - if (const CallICFGNode *call = SVFUtil::dyn_cast(node)) - { - if (const SVFFunction *fun = SVFUtil::getCallee(call->getCallSite())) - { - if (_checkpoint_names.find(fun->getName()) != _checkpoint_names.end()) - { - _checkpoints.insert(call); - } - } - } - } -} - -void AbstractInterpretation::checkPointAllSet() -{ - if (_checkpoints.size() == 0) - { - return; - } - else - { - SVFUtil::errs() << SVFUtil::errMsg("At least one svf_assert has not been checked!!") << "\n"; - for (const CallICFGNode* call: _checkpoints) - { - SVFUtil::errs() << call->toString() + "\n"; - } - assert(false); - } - -} - - -void AbstractInterpretation::handleStrcpy(const CallICFGNode *call) -{ - // strcpy, __strcpy_chk, stpcpy , wcscpy, __wcscpy_chk - // get the dst and src - AbstractState& as = getAbsState(call); - CallSite cs = SVFUtil::getSVFCallSite(call->getCallSite()); - const SVFValue* arg0Val = cs.getArgument(0); - const SVFValue* arg1Val = cs.getArgument(1); - IntervalValue strLen = getStrlen(as, arg1Val); - // no need to -1, since it has \0 as the last byte - handleMemcpy(as, arg0Val, arg1Val, strLen, strLen.lb().getIntNumeral()); -} - -u32_t AbstractInterpretation::getAllocaInstByteSize(AbstractState& as, const AddrStmt *addr) -{ - if (const ObjVar* objvar = SVFUtil::dyn_cast(addr->getRHSVar())) - { - objvar->getType(); - if (objvar->getMemObj()->isConstantByteSize()) - { - u32_t sz = objvar->getMemObj()->getByteSizeOfObj(); - return sz; - } - - else - { - const std::vector& sizes = addr->getArrSize(); - // Default element size is set to 1. - u32_t elementSize = 1; - u64_t res = elementSize; - for (const SVFValue* value: sizes) - { - if (!_svfir2AbsState->inVarToValTable(as, _svfir->getValueNode(value))) - { - as[_svfir->getValueNode(value)] = IntervalValue(Options::MaxFieldLimit()); - } - IntervalValue itv = - as[_svfir->getValueNode(value)].getInterval(); - res = res * itv.ub().getIntNumeral() > Options::MaxFieldLimit()? Options::MaxFieldLimit(): res * itv.ub().getIntNumeral(); - } - return (u32_t)res; - } - } - assert (false && "Addr rhs value is not ObjVar"); - abort(); -} - -IntervalValue AbstractInterpretation::traceMemoryAllocationSize(AbstractState& as, const SVFValue *value) -{ - /// Usually called by a GepStmt overflow check, or external API (like memcpy) overflow check - /// Defitions of Terms: - /// source node: malloc or gepStmt(array), sink node: gepStmt or external API (like memcpy) - /// it tracks the value flow from sink to source, and accumulates offset - /// then compare the accumulated offset and malloc size (or gepStmt array size) - SVF::FILOWorkList worklist; - Set visited; - visited.insert(value); - Map gep_offsets; - worklist.push(value); - IntervalValue total_bytes(0); - while (!worklist.empty()) - { - value = worklist.pop(); - if (const SVFInstruction* ins = SVFUtil::dyn_cast(value)) - { - const ICFGNode* node = _svfir->getICFG()->getICFGNode(ins); - /// CallNode means Source Node - if (const CallICFGNode* callnode = SVFUtil::dyn_cast(node)) - { - //to handle Ret PE - AccessMemoryViaRetNode(callnode, worklist, visited); - } - for (const SVFStmt *stmt: node->getSVFStmts()) - { - if (const CopyStmt *copy = SVFUtil::dyn_cast(stmt)) - { - // Copy Stmt, forward to lhs - AccessMemoryViaCopyStmt(copy, worklist, visited); - } - else if (const LoadStmt *load = SVFUtil::dyn_cast(stmt)) - { - // Load Stmt, forward to the Var from last Store Stmt - AccessMemoryViaLoadStmt(as, load, worklist, visited); - } - else if (const GepStmt *gep = SVFUtil::dyn_cast(stmt)) - { - // there are 3 type of gepStmt - // 1. ptr get offset - // 2. struct get field - // 3. array get element - // for array gep, there are two kind of overflow checking - // Arr [Struct.C * 10] arr, Struct.C {i32 a, i32 b} - // arr[11].a = **, it is "lhs = gep *arr, 0 (ptr), 11 (arrIdx), 0 (ptr), 0(struct field)" - // 1) in this case arrIdx 11 is overflow. - // Other case, - // Struct.C {i32 a, [i32*10] b, i32 c}, C.b[11] = 1 - // it is "lhs - gep *C, 0(ptr), 1(struct field), 0(ptr), 11(arrIdx)" - // 2) in this case arrIdx 11 is larger than its getOffsetVar.Type Array([i32*10]) - - // therefore, if last getOffsetVar.Type is not the Array, just check the overall offset and its - // gep source type size (together with totalOffset along the value flow). - // Alloc Size: TBD, but totalOffset + current Gep offset - - // otherwise, if last getOffsetVar.Type is the Array, check the last idx and array. (just offset, - // not with totalOffset during check) - // Alloc Size: getOffsetVar.TypeByteSize() - - // make sure it has OffsetVarAndGepType Pair - if (gep->getOffsetVarAndGepTypePairVec().size() > 0) - { - // check if last OffsetVarAndGepType Pair is Array - const SVFType* gepType = gep->getOffsetVarAndGepTypePairVec().back().second; - // if its array - if (gepType->isArrayTy()) - { - u32_t rhs_type_bytes = gepType->getByteSize(); - // if gepStmt's base var is Array, compares offset with the arraysize - return IntervalValue(rhs_type_bytes); - } - else - { - IntervalValue byteOffset = _svfir2AbsState->getByteOffset(as, gep); - // for variable offset, join with accumulate gep offset - gep_offsets[gep->getICFGNode()] = byteOffset; - total_bytes = total_bytes + byteOffset; - } - } - if (!visited.count(gep->getRHSVar()->getValue())) - { - visited.insert(gep->getRHSVar()->getValue()); - worklist.push(gep->getRHSVar()->getValue()); - } - } - else if (const AddrStmt *addr = SVFUtil::dyn_cast(stmt)) - { - // addrStmt is source node. - u32_t arr_type_size = getAllocaInstByteSize(as, addr); - return IntervalValue(arr_type_size) - total_bytes; - } - } - } - else if (const SVF::SVFGlobalValue* gvalue = SVFUtil::dyn_cast(value)) - { - u32_t arr_type_size = 0; - const SVFType* svftype = gvalue->getType(); - if (SVFUtil::isa(svftype)) - { - if(const SVFArrayType* ptrArrType = SVFUtil::dyn_cast(getPointeeElement(as, _svfir->getValueNode(value)))) - arr_type_size = ptrArrType->getByteSize(); - else - arr_type_size = svftype->getByteSize(); - } - else - arr_type_size = svftype->getByteSize(); - return IntervalValue(arr_type_size) - total_bytes; - } - else if (const SVF::SVFArgument* arg = SVFUtil::dyn_cast(value)) - { - // to handle call PE - AccessMemoryViaCallArgs(arg, worklist, visited); - } - else - { - // maybe SVFConstant - return IntervalValue(0); - } - } - return IntervalValue(0); -} - - -IntervalValue AbstractInterpretation::getStrlen(AbstractState& as, const SVF::SVFValue *strValue) -{ - IntervalValue dst_size = traceMemoryAllocationSize(as, strValue); - u32_t len = 0; - NodeID dstid = _svfir->getValueNode(strValue); - u32_t elemSize = 1; - if (_svfir2AbsState->inVarToAddrsTable(as, dstid)) - { - for (u32_t index = 0; index < dst_size.lb().getIntNumeral(); index++) - { - AbstractValue expr0 = - _svfir2AbsState->getGepObjAddress(as, dstid, index); - AbstractValue val; - for (const auto &addr: expr0.getAddrs()) - { - val.join_with(as.load(addr)); - } - if (val.getInterval().is_numeral() && (char) val.getInterval().getIntNumeral() == '\0') - { - break; - } - ++len; - } - if (strValue->getType()->isArrayTy()) - { - elemSize = SVFUtil::dyn_cast(strValue->getType())->getTypeOfElement()->getByteSize(); - } - else if (strValue->getType()->isPointerTy()) - { - if (const SVFType* elemType = getPointeeElement(as, _svfir->getValueNode(strValue))) - { - elemSize = elemType->getByteSize(); - } - else - { - elemSize = 1; - } - } - else - { - assert(false && "we cannot support this type"); - } - } - if (len == 0) - { - return IntervalValue((s64_t)0, (s64_t)Options::MaxFieldLimit()); - } - else - { - return IntervalValue(len * elemSize); - } -} - - -void AbstractInterpretation::handleStrcat(const SVF::CallICFGNode *call) -{ - // __strcat_chk, strcat, __wcscat_chk, wcscat, __strncat_chk, strncat, __wcsncat_chk, wcsncat - // to check it is strcat group or strncat group - AbstractState& as = getAbsState(call); - const SVFFunction *fun = SVFUtil::getCallee(call->getCallSite()); - const std::vector strcatGroup = {"__strcat_chk", "strcat", "__wcscat_chk", "wcscat"}; - const std::vector strncatGroup = {"__strncat_chk", "strncat", "__wcsncat_chk", "wcsncat"}; - if (std::find(strcatGroup.begin(), strcatGroup.end(), fun->getName()) != strcatGroup.end()) - { - CallSite cs = SVFUtil::getSVFCallSite(call->getCallSite()); - const SVFValue* arg0Val = cs.getArgument(0); - const SVFValue* arg1Val = cs.getArgument(1); - IntervalValue strLen0 = getStrlen(as, arg0Val); - IntervalValue strLen1 = getStrlen(as, arg1Val); - IntervalValue totalLen = strLen0 + strLen1; - handleMemcpy(as, arg0Val, arg1Val, strLen1, strLen0.lb().getIntNumeral()); - // do memcpy - } - else if (std::find(strncatGroup.begin(), strncatGroup.end(), fun->getName()) != strncatGroup.end()) - { - CallSite cs = SVFUtil::getSVFCallSite(call->getCallSite()); - const SVFValue* arg0Val = cs.getArgument(0); - const SVFValue* arg1Val = cs.getArgument(1); - const SVFValue* arg2Val = cs.getArgument(2); - IntervalValue arg2Num = as[_svfir->getValueNode(arg2Val)].getInterval(); - IntervalValue strLen0 = getStrlen(as, arg0Val); - IntervalValue totalLen = strLen0 + arg2Num; - handleMemcpy(as, arg0Val, arg1Val, arg2Num, strLen0.lb().getIntNumeral()); - // do memcpy - } - else - { - assert(false && "unknown strcat function, please add it to strcatGroup or strncatGroup"); - } -} - -void AbstractInterpretation::handleMemcpy(AbstractState& as, const SVF::SVFValue *dst, const SVF::SVFValue *src, IntervalValue len, u32_t start_idx) -{ - u32_t dstId = _svfir->getValueNode(dst); // pts(dstId) = {objid} objbar objtypeinfo->getType(). - u32_t srcId = _svfir->getValueNode(src); - u32_t elemSize = 1; - if (dst->getType()->isArrayTy()) - { - elemSize = SVFUtil::dyn_cast(dst->getType())->getTypeOfElement()->getByteSize(); - } - // memcpy(i32*, i32*, 40) - else if (dst->getType()->isPointerTy()) - { - if (const SVFType* elemType = getPointeeElement(as, _svfir->getValueNode(dst))) - { - if (elemType->isArrayTy()) - elemSize = SVFUtil::dyn_cast(elemType)->getTypeOfElement()->getByteSize(); - else - elemSize = elemType->getByteSize(); - } - else - { - elemSize = 1; - } - } - else - { - assert(false && "we cannot support this type"); - } - u32_t size = std::min((u32_t)Options::MaxFieldLimit(), (u32_t) len.lb().getIntNumeral()); - u32_t range_val = size / elemSize; - if (_svfir2AbsState->inVarToAddrsTable(as, srcId) && - _svfir2AbsState->inVarToAddrsTable(as, dstId)) - { - for (u32_t index = 0; index < range_val; index++) - { - // dead loop for string and break if there's a \0. If no \0, it will throw err. - AbstractValue expr_src = - _svfir2AbsState->getGepObjAddress(as, srcId, index); - AbstractValue expr_dst = - _svfir2AbsState->getGepObjAddress(as, dstId, index + start_idx); - for (const auto &dst: expr_dst.getAddrs()) - { - for (const auto &src: expr_src.getAddrs()) - { - u32_t objId = AbstractState::getInternalID(src); - if (as.inAddrToValTable(objId)) - { - as.store(dst, as.load(src)); - } - else if (as.inAddrToAddrsTable(objId)) - { - as.store(dst, as.load(src)); - } - } - } - } - } -} - -const SVFType* AbstractInterpretation::getPointeeElement(AbstractState& as, NodeID id) -{ - if (_svfir2AbsState->inVarToAddrsTable(as, id)) - { - const AbstractValue& addrs = _svfir2AbsState->getAddrs(as, id); - for (auto addr: addrs.getAddrs()) - { - NodeID addr_id = AbstractState::getInternalID(addr); - if (addr_id == 0) // nullptr has no memobj, skip - continue; - return SVFUtil::dyn_cast(_svfir->getGNode(addr_id))->getMemObj()->getType(); - } - } - else - { - // do nothing if no record in addrs table. - } - return nullptr; -} - -void AbstractInterpretation::handleMemset(AbstractState& as, const SVF::SVFValue *dst, IntervalValue elem, IntervalValue len) -{ - u32_t dstId = _svfir->getValueNode(dst); - u32_t size = std::min((u32_t)Options::MaxFieldLimit(), (u32_t) len.lb().getIntNumeral()); - u32_t elemSize = 1; - if (dst->getType()->isArrayTy()) - { - elemSize = SVFUtil::dyn_cast(dst->getType())->getTypeOfElement()->getByteSize(); - } - else if (dst->getType()->isPointerTy()) - { - if (const SVFType* elemType = getPointeeElement(as, _svfir->getValueNode(dst))) - { - elemSize = elemType->getByteSize(); - } - else - { - elemSize = 1; - } - } - else - { - assert(false && "we cannot support this type"); - } - - u32_t range_val = size / elemSize; - for (u32_t index = 0; index < range_val; index++) - { - // dead loop for string and break if there's a \0. If no \0, it will throw err. - if (_svfir2AbsState->inVarToAddrsTable(as, dstId)) - { - AbstractValue lhs_gep = - _svfir2AbsState->getGepObjAddress(as, dstId, index); - for (const auto &addr: lhs_gep.getAddrs()) - { - u32_t objId = AbstractState::getInternalID(addr); - if (as.inAddrToValTable(objId)) - { - AbstractValue tmp = as.load(addr); - tmp.join_with(elem); - as.store(addr, tmp); - } - else - { - as.store(addr, elem); - } - } - } - else - break; - } -} - - - -void AbstractInterpretation::AccessMemoryViaRetNode(const CallICFGNode *callnode, SVF::FILOWorkList& worklist, Set& visited) -{ - if (callnode->getRetICFGNode()->getSVFStmts().size() > 0) - { - const RetPE *ret = SVFUtil::dyn_cast(*callnode->getRetICFGNode()->getSVFStmts().begin()); - SVF::ValVar *ret_gnode = SVFUtil::dyn_cast(_svfir->getGNode(ret->getRHSVar()->getId())); - if (ret_gnode->hasIncomingEdges(SVFStmt::PEDGEK::Phi)) - { - const SVFStmt::SVFStmtSetTy &stmt_set = ret_gnode->getIncomingEdges(SVFStmt::PEDGEK::Phi); - for (auto it = stmt_set.begin(); it != stmt_set.end(); ++it) - { - const SVFStmt *stmt = *it; - if (const PhiStmt *phi = SVFUtil::dyn_cast(stmt)) - { - if (!visited.count(phi->getOpVar(0)->getValue())) - { - worklist.push(phi->getOpVar(0)->getValue()); - visited.insert(phi->getOpVar(0)->getValue()); - } - } - } - } - } -} - -void AbstractInterpretation::AccessMemoryViaCopyStmt(const CopyStmt *copy, SVF::FILOWorkList& worklist, Set& visited) -{ - if (!visited.count(copy->getRHSVar()->getValue())) - { - visited.insert(copy->getRHSVar()->getValue()); - worklist.push(copy->getRHSVar()->getValue()); - } -} - -void AbstractInterpretation::AccessMemoryViaLoadStmt(AbstractState& as, const LoadStmt *load, SVF::FILOWorkList& worklist, Set& visited) -{ - if (_svfir2AbsState->inVarToAddrsTable(as, load->getLHSVarID())) - { - const AbstractValue &Addrs = - _svfir2AbsState->getAddrs(as, load->getLHSVarID()); - for (auto vaddr: Addrs.getAddrs()) - { - NodeID id = AbstractState::getInternalID(vaddr); - if (id == 0) // nullptr has no memobj, skip - continue; - const auto *val = _svfir->getGNode(id); - if (!visited.count(val->getValue())) - { - visited.insert(val->getValue()); - worklist.push(val->getValue()); - } - } - } -} - -void AbstractInterpretation::AccessMemoryViaCallArgs(const SVF::SVFArgument *arg, - SVF::FILOWorkList &worklist, - Set &visited) -{ - std::vector callstack = _callSiteStack; - SVF::ValVar *arg_gnode = SVFUtil::cast(_svfir->getGNode(_svfir->getValueNode(arg))); - if (arg_gnode->hasIncomingEdges(SVFStmt::PEDGEK::Call)) - { - while (!callstack.empty()) - { - const CallICFGNode *cur_call = callstack.back(); - callstack.pop_back(); - for (const SVFStmt *stmt: cur_call->getSVFStmts()) - { - if (const CallPE *callPE = SVFUtil::dyn_cast(stmt)) - { - if (callPE->getLHSVarID() == _svfir->getValueNode(arg)) - { - if (!SVFUtil::isa(callPE->getRHSVar()) && - !SVFUtil::isa(callPE->getRHSVar())) - { - if (!visited.count(callPE->getRHSVar()->getValue())) - { - visited.insert(callPE->getRHSVar()->getValue()); - worklist.push(callPE->getRHSVar()->getValue()); - break; - } - } - } - } - } - } - } -} - diff --git a/svf/lib/AE/Svfexe/BufOverflowChecker.cpp b/svf/lib/AE/Svfexe/BufOverflowChecker.cpp deleted file mode 100644 index e115e8f0c..000000000 --- a/svf/lib/AE/Svfexe/BufOverflowChecker.cpp +++ /dev/null @@ -1,839 +0,0 @@ -//===- BufOverflowChecker.cpp -- BufOVerflowChecker Client for Abstract Execution---// -// -// SVF: Static Value-Flow Analysis -// -// Copyright (C) <2013-> -// - -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU Affero General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Affero General Public License for more details. - -// You should have received a copy of the GNU Affero General Public License -// along with this program. If not, see . -// -//===----------------------------------------------------------------------===// - - -// -// Created by Jiawei Wang on 2024/1/12. -// - -#include "AE/Svfexe/BufOverflowChecker.h" -#include "Util/WorkList.h" -#include "SVFIR/SVFType.h" -#include "Util/Options.h" -#include - -namespace SVF -{ - -std::string IntervalToIntStr(const IntervalValue& inv) -{ - if (inv.is_infinite()) - { - return inv.toString(); - } - else - { - s64_t lb_val = inv.lb().getNumeral(); - s64_t ub_val = inv.ub().getNumeral(); - - // check lb - s32_t lb_s32 = (lb_val < static_cast(INT_MIN)) ? INT_MIN : - (lb_val > static_cast(INT_MAX)) ? INT_MAX : - static_cast(lb_val); - - // check ub - s32_t ub_s32 = (ub_val < static_cast(INT_MIN)) ? INT_MIN : - (ub_val > static_cast(INT_MAX)) ? INT_MAX : - static_cast(ub_val); - - return "[" + std::to_string(lb_s32) + ", " + std::to_string(ub_s32) + "]"; - } -} - -void BufOverflowChecker::handleSVFStatement(const SVFStmt *stmt) -{ - AbstractInterpretation::handleSVFStatement(stmt); - AbstractState& as = getAbsState(stmt->getICFGNode()); - // for gep stmt, add the gep stmt to the addrToGep map - if (const GepStmt *gep = SVFUtil::dyn_cast(stmt)) - { - for (NodeID addrID: - _svfir2AbsState->getAddrs(as, gep->getLHSVarID()).getAddrs()) - { - NodeID objId = AbstractState::getInternalID(addrID); - _addrToGep[objId] = gep; - } - } -} - -void BufOverflowChecker::initExtAPIBufOverflowCheckRules() -{ - //void llvm_memcpy_p0i8_p0i8_i64(char* dst, char* src, int sz, int flag){} - _extAPIBufOverflowCheckRules["llvm_memcpy_p0i8_p0i8_i64"] = {{0, 2}, {1,2}}; - //void llvm_memcpy_p0_p0_i64(char* dst, char* src, int sz, int flag){} - _extAPIBufOverflowCheckRules["llvm_memcpy_p0_p0_i64"] = {{0, 2}, {1,2}}; - //void llvm_memcpy_p0i8_p0i8_i32(char* dst, char* src, int sz, int flag){} - _extAPIBufOverflowCheckRules["llvm_memcpy_p0i8_p0i8_i32"] = {{0, 2}, {1,2}}; - //void llvm_memcpy(char* dst, char* src, int sz, int flag){} - _extAPIBufOverflowCheckRules["llvm_memcpy"] = {{0, 2}, {1,2}}; - //void llvm_memmove(char* dst, char* src, int sz, int flag){} - _extAPIBufOverflowCheckRules["llvm_memmove"] = {{0, 2}, {1,2}}; - //void llvm_memmove_p0i8_p0i8_i64(char* dst, char* src, int sz, int flag){} - _extAPIBufOverflowCheckRules["llvm_memmove_p0i8_p0i8_i64"] = {{0, 2}, {1,2}}; - //void llvm_memmove_p0_p0_i64(char* dst, char* src, int sz, int flag){} - _extAPIBufOverflowCheckRules["llvm_memmove_p0_p0_i64"] = {{0, 2}, {1,2}}; - //void llvm_memmove_p0i8_p0i8_i32(char* dst, char* src, int sz, int flag){} - _extAPIBufOverflowCheckRules["llvm_memmove_p0i8_p0i8_i32"] = {{0, 2}, {1,2}}; - //void __memcpy_chk(char* dst, char* src, int sz, int flag){} - _extAPIBufOverflowCheckRules["__memcpy_chk"] = {{0, 2}, {1,2}}; - //void *memmove(void *str1, const void *str2, unsigned long n) - _extAPIBufOverflowCheckRules["memmove"] = {{0, 2}, {1,2}}; - //void bcopy(const void *s1, void *s2, unsigned long n){} - _extAPIBufOverflowCheckRules["bcopy"] = {{0, 2}, {1,2}}; - //void *memccpy( void * restrict dest, const void * restrict src, int c, unsigned long count) - _extAPIBufOverflowCheckRules["memccpy"] = {{0, 3}, {1,3}}; - //void __memmove_chk(char* dst, char* src, int sz){} - _extAPIBufOverflowCheckRules["__memmove_chk"] = {{0, 2}, {1,2}}; - //void llvm_memset(char* dst, char elem, int sz, int flag){} - _extAPIBufOverflowCheckRules["llvm_memset"] = {{0, 2}}; - //void llvm_memset_p0i8_i32(char* dst, char elem, int sz, int flag){} - _extAPIBufOverflowCheckRules["llvm_memset_p0i8_i32"] = {{0, 2}}; - //void llvm_memset_p0i8_i64(char* dst, char elem, int sz, int flag){} - _extAPIBufOverflowCheckRules["llvm_memset_p0i8_i64"] = {{0, 2}}; - //void llvm_memset_p0_i64(char* dst, char elem, int sz, int flag){} - _extAPIBufOverflowCheckRules["llvm_memset_p0_i64"] = {{0, 2}}; - //char *__memset_chk(char * dest, int c, unsigned long destlen, int flag) - _extAPIBufOverflowCheckRules["__memset_chk"] = {{0, 2}}; - //char *wmemset(wchar_t * dst, wchar_t elem, int sz, int flag) { - _extAPIBufOverflowCheckRules["wmemset"] = {{0, 2}}; - //char *strncpy(char *dest, const char *src, unsigned long n) - _extAPIBufOverflowCheckRules["strncpy"] = {{0, 2}, {1,2}}; - //unsigned long iconv(void* cd, char **restrict inbuf, unsigned long *restrict inbytesleft, char **restrict outbuf, unsigned long *restrict outbytesleft) - _extAPIBufOverflowCheckRules["iconv"] = {{1, 2}, {3, 4}}; -} - - -bool BufOverflowChecker::detectStrcpy(const CallICFGNode *call) -{ - AbstractState& as = getAbsState(call); - CallSite cs = SVFUtil::getSVFCallSite(call->getCallSite()); - const SVFValue* arg0Val = cs.getArgument(0); - const SVFValue* arg1Val = cs.getArgument(1); - IntervalValue strLen = getStrlen(as, arg1Val); - // no need to -1, since it has \0 as the last byte - return canSafelyAccessMemory(arg0Val, strLen, call); -} - -void BufOverflowChecker::initExtFunMap() -{ - - auto sse_scanf = [&](const CallSite &cs) - { - const CallICFGNode* callNode = SVFUtil::dyn_cast(_svfir->getICFG()->getICFGNode(cs.getInstruction())); - AbstractState& as = getAbsState(callNode); - //scanf("%d", &data); - if (cs.arg_size() < 2) return; - - u32_t dst_id = _svfir->getValueNode(cs.getArgument(1)); - if (!_svfir2AbsState->inVarToAddrsTable(as, dst_id)) - { - BufOverflowException bug("scanf may cause buffer overflow.\n", 0, 0, 0, 0, cs.getArgument(1)); - addBugToRecoder(bug, _svfir->getICFG()->getICFGNode(cs.getInstruction())); - return; - } - else - { - AbstractValue Addrs = _svfir2AbsState->getAddrs(as, dst_id); - for (auto vaddr: Addrs.getAddrs()) - { - u32_t objId = AbstractState::getInternalID(vaddr); - AbstractValue range = _svfir2AbsState->getRangeLimitFromType(_svfir->getGNode(objId)->getType()); - as.store(vaddr, range); - } - } - }; - auto sse_fscanf = [&](const CallSite &cs) - { - //fscanf(stdin, "%d", &data); - if (cs.arg_size() < 3) return; - const CallICFGNode* callNode = SVFUtil::dyn_cast(_svfir->getICFG()->getICFGNode(cs.getInstruction())); - AbstractState& as = getAbsState(callNode); - u32_t dst_id = _svfir->getValueNode(cs.getArgument(2)); - if (!_svfir2AbsState->inVarToAddrsTable(as, dst_id)) - { - BufOverflowException bug("scanf may cause buffer overflow.\n", 0, 0, 0, 0, cs.getArgument(2)); - addBugToRecoder(bug, _svfir->getICFG()->getICFGNode(cs.getInstruction())); - return; - } - else - { - AbstractValue Addrs = _svfir2AbsState->getAddrs(as, dst_id); - for (auto vaddr: Addrs.getAddrs()) - { - u32_t objId = AbstractState::getInternalID(vaddr); - AbstractValue range = _svfir2AbsState->getRangeLimitFromType(_svfir->getGNode(objId)->getType()); - as.store(vaddr, range); - } - } - }; - - _func_map["__isoc99_fscanf"] = sse_fscanf; - _func_map["__isoc99_scanf"] = sse_scanf; - _func_map["__isoc99_vscanf"] = sse_scanf; - _func_map["fscanf"] = sse_fscanf; - _func_map["scanf"] = sse_scanf; - _func_map["sscanf"] = sse_scanf; - _func_map["__isoc99_sscanf"] = sse_scanf; - _func_map["vscanf"] = sse_scanf; - - auto sse_fread = [&](const CallSite &cs) - { - if (cs.arg_size() < 3) return; - const CallICFGNode* callNode = SVFUtil::dyn_cast(_svfir->getICFG()->getICFGNode(cs.getInstruction())); - AbstractState&as = getAbsState(callNode); - u32_t block_count_id = _svfir->getValueNode(cs.getArgument(2)); - u32_t block_size_id = _svfir->getValueNode(cs.getArgument(1)); - IntervalValue block_count = as[block_count_id].getInterval(); - IntervalValue block_size = as[block_size_id].getInterval(); - IntervalValue block_byte = block_count * block_size; - canSafelyAccessMemory(cs.getArgument(0), block_byte, _svfir->getICFG()->getICFGNode(cs.getInstruction())); - }; - _func_map["fread"] = sse_fread; - - auto sse_sprintf = [&](const CallSite &cs) - { - // printf is difficult to predict since it has no byte size arguments - }; - - auto sse_snprintf = [&](const CallSite &cs) - { - if (cs.arg_size() < 2) return; - const CallICFGNode* callNode = SVFUtil::dyn_cast(_svfir->getICFG()->getICFGNode(cs.getInstruction())); - AbstractState&as = getAbsState(callNode); - u32_t size_id = _svfir->getValueNode(cs.getArgument(1)); - u32_t dst_id = _svfir->getValueNode(cs.getArgument(0)); - // get elem size of arg2 - u32_t elemSize = 1; - if (cs.getArgument(2)->getType()->isArrayTy()) - { - elemSize = SVFUtil::dyn_cast(cs.getArgument(2)->getType())->getTypeOfElement()->getByteSize(); - } - else if (cs.getArgument(2)->getType()->isPointerTy()) - { - elemSize = getPointeeElement(as, _svfir->getValueNode(cs.getArgument(2)))->getByteSize(); - } - else - { - return; - // assert(false && "we cannot support this type"); - } - IntervalValue size = as[size_id].getInterval() * IntervalValue(elemSize) - IntervalValue(1); - if (!as.inVarToAddrsTable(dst_id)) - { - if (Options::BufferOverflowCheck()) - { - BufOverflowException bug( - "snprintf dst_id or dst is not defined nor initializesd.\n", - 0, 0, 0, 0, cs.getArgument(0)); - addBugToRecoder(bug, _svfir->getICFG()->getICFGNode(cs.getInstruction())); - return; - } - } - canSafelyAccessMemory(cs.getArgument(0), size, _svfir->getICFG()->getICFGNode(cs.getInstruction())); - }; - _func_map["__snprintf_chk"] = sse_snprintf; - _func_map["__vsprintf_chk"] = sse_sprintf; - _func_map["__sprintf_chk"] = sse_sprintf; - _func_map["snprintf"] = sse_snprintf; - _func_map["sprintf"] = sse_sprintf; - _func_map["vsprintf"] = sse_sprintf; - _func_map["vsnprintf"] = sse_snprintf; - _func_map["__vsnprintf_chk"] = sse_snprintf; - _func_map["swprintf"] = sse_snprintf; - _func_map["_snwprintf"] = sse_snprintf; - - - auto sse_itoa = [&](const CallSite &cs) - { - // itoa(num, ch, 10); - // num: int, ch: char*, 10 is decimal - if (cs.arg_size() < 3) return; - const CallICFGNode* callNode = SVFUtil::dyn_cast(_svfir->getICFG()->getICFGNode(cs.getInstruction())); - AbstractState&as = getAbsState(callNode); - u32_t num_id = _svfir->getValueNode(cs.getArgument(0)); - - u32_t num = (u32_t) as[num_id].getInterval().getNumeral(); - std::string snum = std::to_string(num); - canSafelyAccessMemory(cs.getArgument(1), IntervalValue((s32_t)snum.size()), _svfir->getICFG()->getICFGNode(cs.getInstruction())); - }; - _func_map["itoa"] = sse_itoa; - - - auto sse_strlen = [&](const CallSite &cs) - { - // check the arg size - if (cs.arg_size() < 1) return; - const SVFValue* strValue = cs.getArgument(0); - const CallICFGNode* callNode = SVFUtil::dyn_cast(_svfir->getICFG()->getICFGNode(cs.getInstruction())); - AbstractState& as = getAbsState(callNode); - IntervalValue dst_size = getStrlen(as, strValue); - u32_t elemSize = 1; - if (strValue->getType()->isArrayTy()) - { - elemSize = SVFUtil::dyn_cast(strValue->getType())->getTypeOfElement()->getByteSize(); - } - else if (strValue->getType()->isPointerTy()) - { - if (const SVFType* pointee = getPointeeElement(as, _svfir->getValueNode(strValue))) - elemSize = pointee->getByteSize(); - else - elemSize = 1; - } - u32_t lhsId = _svfir->getValueNode(cs.getInstruction()); - as[lhsId] = dst_size / IntervalValue(elemSize); - }; - _func_map["strlen"] = sse_strlen; - _func_map["wcslen"] = sse_strlen; - - auto sse_recv = [&](const CallSite &cs) - { - // recv(sockfd, buf, len, flags); - if (cs.arg_size() < 4) return; - const CallICFGNode* callNode = SVFUtil::dyn_cast(_svfir->getICFG()->getICFGNode(cs.getInstruction())); - AbstractState&as = getAbsState(callNode); - u32_t len_id = _svfir->getValueNode(cs.getArgument(2)); - IntervalValue len = as[len_id].getInterval() - IntervalValue(1); - u32_t lhsId = _svfir->getValueNode(cs.getInstruction()); - as[lhsId] = len; - canSafelyAccessMemory(cs.getArgument(1), len, _svfir->getICFG()->getICFGNode(cs.getInstruction()));; - }; - _func_map["recv"] = sse_recv; - _func_map["__recv"] = sse_recv; - auto safe_bufaccess = [&](const CallSite &cs) - { - const CallICFGNode* callNode = SVFUtil::dyn_cast(_svfir->getICFG()->getICFGNode(cs.getInstruction())); - _checkpoints.erase(callNode); - //void SAFE_BUFACCESS(void* data, int size); - if (cs.arg_size() < 2) return; - AbstractState&as = getAbsState(callNode); - u32_t size_id = _svfir->getValueNode(cs.getArgument(1)); - IntervalValue val = as[size_id].getInterval(); - if (val.isBottom()) - { - val = IntervalValue(0); - assert(false && "SAFE_BUFACCESS size is bottom"); - } - bool isSafe = canSafelyAccessMemory(cs.getArgument(0), val, _svfir->getICFG()->getICFGNode(cs.getInstruction())); - if (isSafe) - { - std::cout << "safe buffer access success\n"; - return; - } - else - { - std::string err_msg = "this SAFE_BUFACCESS should be a safe access but detected buffer overflow. Pos: "; - err_msg += cs.getInstruction()->getSourceLoc(); - std::cerr << err_msg << std::endl; - assert(false); - } - }; - _func_map["SAFE_BUFACCESS"] = safe_bufaccess; - - auto unsafe_bufaccess = [&](const CallSite &cs) - { - const CallICFGNode* callNode = SVFUtil::dyn_cast(_svfir->getICFG()->getICFGNode(cs.getInstruction())); - _checkpoints.erase(callNode); - //void UNSAFE_BUFACCESS(void* data, int size); - if (cs.arg_size() < 2) return; - AbstractState&as = getAbsState(callNode); - u32_t size_id = _svfir->getValueNode(cs.getArgument(1)); - IntervalValue val = as[size_id].getInterval(); - if (val.isBottom()) - { - assert(false && "UNSAFE_BUFACCESS size is bottom"); - } - bool isSafe = canSafelyAccessMemory(cs.getArgument(0), val, _svfir->getICFG()->getICFGNode(cs.getInstruction())); - if (!isSafe) - { - std::cout << "detect buffer overflow success\n"; - return; - } - else - { - // if it is safe, it means it is wrongly labeled, assert false. - std::string err_msg = "this UNSAFE_BUFACCESS should be a buffer overflow but not detected. Pos: "; - err_msg += cs.getInstruction()->getSourceLoc(); - std::cerr << err_msg << std::endl; - assert(false); - } - }; - _func_map["UNSAFE_BUFACCESS"] = unsafe_bufaccess; - - // init _checkpoint_names - _checkpoint_names.insert("SAFE_BUFACCESS"); - _checkpoint_names.insert("UNSAFE_BUFACCESS"); -} - -bool BufOverflowChecker::detectStrcat(const CallICFGNode *call) -{ - AbstractState& as = getAbsState(call); - const SVFFunction *fun = SVFUtil::getCallee(call->getCallSite()); - // check the arg size - // if it is strcat group, we need to check the length of string, - // e.g. strcat(str1, str2); which checks AllocSize(str1) >= Strlen(str1) + Strlen(str2); - // if it is strncat group, we do not need to check the length of string, - // e.g. strncat(str1, str2, n); which checks AllocSize(str1) >= Strlen(str1) + n; - - const std::vector strcatGroup = {"__strcat_chk", "strcat", "__wcscat_chk", "wcscat"}; - const std::vector strncatGroup = {"__strncat_chk", "strncat", "__wcsncat_chk", "wcsncat"}; - if (std::find(strcatGroup.begin(), strcatGroup.end(), fun->getName()) != strcatGroup.end()) - { - CallSite cs = SVFUtil::getSVFCallSite(call->getCallSite()); - const SVFValue* arg0Val = cs.getArgument(0); - const SVFValue* arg1Val = cs.getArgument(1); - IntervalValue strLen0 = getStrlen(as, arg0Val); - IntervalValue strLen1 = getStrlen(as, arg1Val); - IntervalValue totalLen = strLen0 + strLen1; - return canSafelyAccessMemory(arg0Val, totalLen, call); - } - else if (std::find(strncatGroup.begin(), strncatGroup.end(), fun->getName()) != strncatGroup.end()) - { - CallSite cs = SVFUtil::getSVFCallSite(call->getCallSite()); - const SVFValue* arg0Val = cs.getArgument(0); - const SVFValue* arg2Val = cs.getArgument(2); - IntervalValue arg2Num = as[_svfir->getValueNode(arg2Val)].getInterval(); - IntervalValue strLen0 = getStrlen(as, arg0Val); - IntervalValue totalLen = strLen0 + arg2Num; - return canSafelyAccessMemory(arg0Val, totalLen, call); - } - else - { - assert(false && "unknown strcat function, please add it to strcatGroup or strncatGroup"); - abort(); - } -} - -void BufOverflowChecker::handleExtAPI(const CallICFGNode *call) -{ - AbstractState& as = getAbsState(call); - AbstractInterpretation::handleExtAPI(call); - const SVFFunction *fun = SVFUtil::getCallee(call->getCallSite()); - assert(fun && "SVFFunction* is nullptr"); - CallSite cs = SVFUtil::getSVFCallSite(call->getCallSite()); - // check the type of mem api, - // MEMCPY: like memcpy, memcpy_chk, llvm.memcpy etc. - // MEMSET: like memset, memset_chk, llvm.memset etc. - // STRCPY: like strcpy, strcpy_chk, wcscpy etc. - // STRCAT: like strcat, strcat_chk, wcscat etc. - // for other ext api like printf, scanf, etc., they have their own handlers - ExtAPIType extType = UNCLASSIFIED; - // get type of mem api - for (const std::string &annotation: fun->getAnnotations()) - { - if (annotation.find("MEMCPY") != std::string::npos) - extType = MEMCPY; - if (annotation.find("MEMSET") != std::string::npos) - extType = MEMSET; - if (annotation.find("STRCPY") != std::string::npos) - extType = STRCPY; - if (annotation.find("STRCAT") != std::string::npos) - extType = STRCAT; - } - // 1. memcpy functions like memcpy_chk, strncpy, annotate("MEMCPY"), annotate("BUF_CHECK:Arg0, Arg2"), annotate("BUF_CHECK:Arg1, Arg2") - if (extType == MEMCPY) - { - if (_extAPIBufOverflowCheckRules.count(fun->getName()) == 0) - { - // if it is not in the rules, we do not check it - SVFUtil::errs() << "Warning: " << fun->getName() << " is not in the rules, please implement it\n"; - return; - } - // call parseMemcpyBufferCheckArgs to parse the BUF_CHECK annotation - std::vector> args = _extAPIBufOverflowCheckRules.at(fun->getName()); - // loop the args and check the offset - for (auto arg: args) - { - IntervalValue offset = as[_svfir->getValueNode(cs.getArgument(arg.second))].getInterval() - IntervalValue(1); - canSafelyAccessMemory(cs.getArgument(arg.first), offset, call); - } - } - // 2. memset functions like memset, memset_chk, annotate("MEMSET"), annotate("BUF_CHECK:Arg0, Arg2") - else if (extType == MEMSET) - { - if (_extAPIBufOverflowCheckRules.count(fun->getName()) == 0) - { - // if it is not in the rules, we do not check it - SVFUtil::errs() << "Warning: " << fun->getName() << " is not in the rules, please implement it\n"; - return; - } - std::vector> args = _extAPIBufOverflowCheckRules.at(fun->getName()); - // loop the args and check the offset - for (auto arg: args) - { - IntervalValue offset = as[_svfir->getValueNode(cs.getArgument(arg.second))].getInterval() - IntervalValue(1); - canSafelyAccessMemory(cs.getArgument(arg.first), offset, call); - } - } - else if (extType == STRCPY) - { - detectStrcpy(call); - } - else if (extType == STRCAT) - { - detectStrcat(call); - } - else - { - - } - return; -} - -bool BufOverflowChecker::canSafelyAccessMemory(const SVFValue *value, const IntervalValue &len, const ICFGNode *curNode) -{ - AbstractState& as = getAbsState(curNode); - const SVFValue *firstValue = value; - /// Usually called by a GepStmt overflow check, or external API (like memcpy) overflow check - /// Defitions of Terms: - /// source node: malloc or gepStmt(array), sink node: gepStmt or external API (like memcpy) - /// e.g. 1) a = malloc(10), a[11] = 10, a[11] is the sink node, a is the source node (malloc) - /// 2) A = struct {int a[10];}, A.a[11] = 10, A.a[11] is the sink, A.a is the source node (gepStmt(array)) - - /// it tracks the value flow from sink to source, and accumulates offset - /// then compare the accumulated offset and malloc size (or gepStmt array size) - SVF::FILOWorkList worklist; - Set visited; - visited.insert(value); - Map gep_offsets; - IntervalValue total_bytes = len; - worklist.push(value); - std::vector callstack = _callSiteStack; - while (!worklist.empty()) - { - value = worklist.pop(); - if (const SVFInstruction *ins = SVFUtil::dyn_cast(value)) - { - const ICFGNode *node = _svfir->getICFG()->getICFGNode(ins); - if (const CallICFGNode *callnode = SVFUtil::dyn_cast(node)) - { - AccessMemoryViaRetNode(callnode, worklist, visited); - } - for (const SVFStmt *stmt: node->getSVFStmts()) - { - if (const CopyStmt *copy = SVFUtil::dyn_cast(stmt)) - { - AccessMemoryViaCopyStmt(copy, worklist, visited); - } - else if (const LoadStmt *load = SVFUtil::dyn_cast(stmt)) - { - AccessMemoryViaLoadStmt(as, load, worklist, visited); - } - else if (const GepStmt *gep = SVFUtil::dyn_cast(stmt)) - { - // there are 3 type of gepStmt - // 1. ptr get offset - // 2. struct get field - // 3. array get element - // for array gep, there are two kind of overflow checking - // Arr [Struct.C * 10] arr, Struct.C {i32 a, i32 b} - // arr[11].a = **, it is "lhs = gep *arr, 0 (ptr), 11 (arrIdx), 0 (ptr), 0(struct field)" - // 1) in this case arrIdx 11 is overflow. - // Other case, - // Struct.C {i32 a, [i32*10] b, i32 c}, C.b[11] = 1 - // it is "lhs - gep *C, 0(ptr), 1(struct field), 0(ptr), 11(arrIdx)" - // 2) in this case arrIdx 11 is larger than its getOffsetVar.Type Array([i32*10]) - - // therefore, if last getOffsetVar.Type is not the Array, just check the overall offset and its - // gep source type size (together with totalOffset along the value flow). - // so if curgepOffset + totalOffset >= gepSrc (overflow) - // else totalOffset += curgepOffset - - // otherwise, if last getOffsetVar.Type is the Array, check the last idx and array. (just offset, - // not with totalOffset during check) - // so if getOffsetVarVal > getOffsetVar.TypeSize (overflow) - // else safe and return. - IntervalValue byteOffset; - byteOffset = _svfir2AbsState->getByteOffset(as, gep); - // for variable offset, join with accumulate gep offset - gep_offsets[gep->getICFGNode()] = byteOffset; - if (byteOffset.ub().getNumeral() >= Options::MaxFieldLimit() && Options::GepUnknownIdx()) - { - return true; - } - - if (gep->getOffsetVarAndGepTypePairVec().size() > 0) - { - const SVFVar *gepVal = gep->getOffsetVarAndGepTypePairVec().back().first; - const SVFType *gepType = gep->getOffsetVarAndGepTypePairVec().back().second; - - if (gepType->isArrayTy()) - { - const SVFArrayType *gepArrType = SVFUtil::dyn_cast(gepType); - IntervalValue gepArrTotalByte(0); - const SVFValue *idxValue = gepVal->getValue(); - u32_t arrElemSize = gepArrType->getTypeOfElement()->getByteSize(); - if (const SVFConstantInt *op = SVFUtil::dyn_cast(idxValue)) - { - u32_t lb = (double) Options::MaxFieldLimit() / arrElemSize >= op->getSExtValue() ? - op->getSExtValue() * arrElemSize : Options::MaxFieldLimit(); - gepArrTotalByte = gepArrTotalByte + IntervalValue(lb, lb); - } - else - { - u32_t idx = _svfir->getValueNode(idxValue); - IntervalValue idxVal = as[idx].getInterval(); - if (idxVal.isBottom()) - { - gepArrTotalByte = gepArrTotalByte + IntervalValue(0, 0); - } - else - { - u32_t ub = (idxVal.ub().getNumeral() < 0) ? 0 : - (double) Options::MaxFieldLimit() / arrElemSize >= - idxVal.ub().getNumeral() ? - arrElemSize * idxVal.ub().getNumeral() : Options::MaxFieldLimit(); - u32_t lb = (idxVal.lb().getNumeral() < 0) ? 0 : - ((double) Options::MaxFieldLimit() / arrElemSize >= - idxVal.lb().getNumeral()) ? - arrElemSize * idxVal.lb().getNumeral() : Options::MaxFieldLimit(); - gepArrTotalByte = gepArrTotalByte + IntervalValue(lb, ub); - } - } - total_bytes = total_bytes + gepArrTotalByte; - if (total_bytes.ub().getNumeral() >= gepArrType->getByteSize()) - { - std::string msg = - "Buffer overflow!! Accessing buffer range: " + - IntervalToIntStr(total_bytes) + - "\nAllocated Gep buffer size: " + - std::to_string(gepArrType->getByteSize()) + "\n"; - msg += "Position: " + firstValue->toString() + "\n"; - msg += " The following is the value flow. [[\n"; - for (auto it = gep_offsets.begin(); it != gep_offsets.end(); ++it) - { - msg += it->first->toString() + ", Offset: " + IntervalToIntStr(it->second) + - "\n"; - } - msg += "]].\nAlloc Site: " + gep->toString() + "\n"; - - BufOverflowException bug(SVFUtil::errMsg(msg), gepArrType->getByteSize(), - gepArrType->getByteSize(), - total_bytes.lb().getNumeral(), total_bytes.ub().getNumeral(), - firstValue); - addBugToRecoder(bug, curNode); - return false; - } - else - { - // for gep last index's type is arr, stop here. - return true; - } - } - else - { - total_bytes = total_bytes + byteOffset; - } - - } - if (!visited.count(gep->getRHSVar()->getValue())) - { - visited.insert(gep->getRHSVar()->getValue()); - worklist.push(gep->getRHSVar()->getValue()); - } - } - else if (const AddrStmt *addr = SVFUtil::dyn_cast(stmt)) - { - // addrStmt is source node. - u32_t arr_type_size = getAllocaInstByteSize(as, addr); - if (total_bytes.ub().getNumeral() >= arr_type_size || - total_bytes.lb().getNumeral() < 0) - { - std::string msg = - "Buffer overflow!! Accessing buffer range: " + IntervalToIntStr(total_bytes) + - "\nAllocated buffer size: " + std::to_string(arr_type_size) + "\n"; - msg += "Position: " + firstValue->toString() + "\n"; - msg += " The following is the value flow. [[\n"; - for (auto it = gep_offsets.begin(); it != gep_offsets.end(); ++it) - { - msg += it->first->toString() + ", Offset: " + IntervalToIntStr(it->second) + "\n"; - } - msg += "]].\n Alloc Site: " + addr->toString() + "\n"; - BufOverflowException bug(SVFUtil::wrnMsg(msg), arr_type_size, arr_type_size, - total_bytes.lb().getNumeral(), total_bytes.ub().getNumeral(), - firstValue); - addBugToRecoder(bug, curNode); - return false; - } - else - { - - return true; - } - } - } - } - else if (const SVF::SVFGlobalValue *gvalue = SVFUtil::dyn_cast(value)) - { - u32_t arr_type_size = 0; - const SVFType *svftype = gvalue->getType(); - if (SVFUtil::isa(svftype)) - { - if (const SVFArrayType *ptrArrType = SVFUtil::dyn_cast( - getPointeeElement(as, _svfir->getValueNode(gvalue)))) - arr_type_size = ptrArrType->getByteSize(); - else - arr_type_size = svftype->getByteSize(); - } - else - arr_type_size = svftype->getByteSize(); - - if (total_bytes.ub().getNumeral() >= arr_type_size || total_bytes.lb().getNumeral() < 0) - { - std::string msg = "Buffer overflow!! Accessing buffer range: " + IntervalToIntStr(total_bytes) + - "\nAllocated buffer size: " + std::to_string(arr_type_size) + "\n"; - msg += "Position: " + firstValue->toString() + "\n"; - msg += " The following is the value flow.\n[["; - for (auto it = gep_offsets.begin(); it != gep_offsets.end(); ++it) - { - msg += it->first->toString() + ", Offset: " + IntervalToIntStr(it->second) + "\n"; - } - msg += "]]. \nAlloc Site: " + gvalue->toString() + "\n"; - - BufOverflowException bug(SVFUtil::wrnMsg(msg), arr_type_size, arr_type_size, - total_bytes.lb().getNumeral(), total_bytes.ub().getNumeral(), firstValue); - addBugToRecoder(bug, curNode); - return false; - } - else - { - return true; - } - } - else if (const SVF::SVFArgument *arg = SVFUtil::dyn_cast(value)) - { - AccessMemoryViaCallArgs(arg, worklist, visited); - } - else - { - // maybe SVFConstant - // it may be cannot find the source, maybe we start from non-main function, - // therefore it loses the value flow track - return true; - } - } - // it may be cannot find the source, maybe we start from non-main function, - // therefore it loses the value flow track - return true; -} - - - -void BufOverflowChecker::handleICFGNode(const SVF::ICFGNode *node) -{ - AbstractInterpretation::handleICFGNode(node); - detectBufOverflow(node); -} - -// -bool BufOverflowChecker::detectBufOverflow(const ICFGNode *node) -{ - AbstractState &as = getAbsState(node); - for (auto* stmt: node->getSVFStmts()) - { - if (const GepStmt *gep = SVFUtil::dyn_cast(stmt)) - { - const SVFVar* gepRhs = gep->getRHSVar(); - if (const SVFInstruction* inst = SVFUtil::dyn_cast(gepRhs->getValue())) - { - const ICFGNode* icfgNode = _svfir->getICFG()->getICFGNode(inst); - for (const SVFStmt* stmt2: icfgNode->getSVFStmts()) - { - if (const GepStmt *gep2 = SVFUtil::dyn_cast(stmt2)) - { - return canSafelyAccessMemory(gep2->getLHSVar()->getValue(), IntervalValue(0, 0), node); - } - } - } - } - else if (const LoadStmt* load = SVFUtil::dyn_cast(stmt)) - { - if (_svfir2AbsState->inVarToAddrsTable(as, load->getRHSVarID())) - { - AbstractValue Addrs = - _svfir2AbsState->getAddrs(as, load->getRHSVarID()); - for (auto vaddr: Addrs.getAddrs()) - { - u32_t objId = AbstractState::getInternalID(vaddr); - if (_addrToGep.find(objId) != _addrToGep.end()) - { - const GepStmt* gep = _addrToGep.at(objId); - return canSafelyAccessMemory(gep->getLHSVar()->getValue(), IntervalValue(0, 0), node); - } - } - } - } - else if (const StoreStmt* store = SVFUtil::dyn_cast(stmt)) - { - if (_svfir2AbsState->inVarToAddrsTable(as, store->getLHSVarID())) - { - AbstractValue Addrs = - _svfir2AbsState->getAddrs(as, store->getLHSVarID()); - for (auto vaddr: Addrs.getAddrs()) - { - u32_t objId = AbstractState::getInternalID(vaddr); - if (_addrToGep.find(objId) != _addrToGep.end()) - { - const GepStmt* gep = _addrToGep.at(objId); - return canSafelyAccessMemory(gep->getLHSVar()->getValue(), IntervalValue(0, 0), node); - } - } - } - } - } - return true; -} - -void BufOverflowChecker::addBugToRecoder(const BufOverflowException& e, const ICFGNode* node) -{ - const SVFInstruction* inst = nullptr; - if (const CallICFGNode* call = SVFUtil::dyn_cast(node)) - { - inst = call->getCallSite(); - } - else - { - inst = node->getSVFStmts().back()->getInst(); - } - GenericBug::EventStack eventStack; - SVFBugEvent sourceInstEvent(SVFBugEvent::EventType::SourceInst, inst); - for (const auto &callsite: _callSiteStack) - { - SVFBugEvent callSiteEvent(SVFBugEvent::EventType::CallSite, callsite->getCallSite()); - eventStack.push_back(callSiteEvent); - } - eventStack.push_back(sourceInstEvent); - if (eventStack.size() == 0) return; - std::string loc = eventStack.back().getEventLoc(); - if (_bugLoc.find(loc) != _bugLoc.end()) - { - return; - } - else - { - _bugLoc.insert(loc); - } - _recoder.addAbsExecBug(GenericBug::FULLBUFOVERFLOW, eventStack, e.getAllocLb(), e.getAllocUb(), e.getAccessLb(), - e.getAccessUb()); - _nodeToBugInfo[node] = e.what(); -} - -} diff --git a/svf/lib/AE/Svfexe/ICFGSimplification.cpp b/svf/lib/AE/Svfexe/ICFGSimplification.cpp deleted file mode 100644 index 253cf8d78..000000000 --- a/svf/lib/AE/Svfexe/ICFGSimplification.cpp +++ /dev/null @@ -1,172 +0,0 @@ -//===- ICFGSimplification.h -- Simplify ICFG----------------------------------// -// -// SVF: Static Value-Flow Analysis -// -// Copyright (C) <2013-> -// - -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU Affero General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Affero General Public License for more details. - -// You should have received a copy of the GNU Affero General Public License -// along with this program. If not, see . -// -// The implementation is based on -// Xiao Cheng, Jiawei Wang and Yulei Sui. Precise Sparse Abstract Execution via Cross-Domain Interaction. -// 46th International Conference on Software Engineering. (ICSE24) -//===----------------------------------------------------------------------===// - - -// -// Created by Jiawei Wang on 2024/2/25. -// -#include "AE/Svfexe/ICFGSimplification.h" - -namespace SVF -{ -void ICFGSimplification::mergeAdjacentNodes(ICFG* icfg) -{ - Map> bbToNodes; - Set subNodes; - for (const auto& func : *PAG::getPAG()->getModule()) - { - for (const auto& bb : *func) - { - for (const auto& inst : *bb) - { - if (SVFUtil::isIntrinsicInst(inst)) - continue; - const ICFGNode* icfgNode = icfg->getICFGNode(inst); - // e.g. - // block: entry - // insts: call = i32 %fun(i32 %arg) - // We put the callnode in an icfgnode alone, and the retnode is similar. - if (const CallICFGNode* callNode = - SVFUtil::dyn_cast(icfgNode)) - { - bbToNodes[bb].push_back(callNode); - subNodes.insert(callNode); - - const RetICFGNode* retNode = callNode->getRetICFGNode(); - bbToNodes[bb].push_back(retNode); - subNodes.insert(retNode); - } - else - { - // For ordinary instructions, we put multiple instructions in an ICFGNode - /// e.g. - /// entry: - /// %add = add i32 %a, %b ----> goto branch 1 - /// %sub = sub i32 %add, %c -----> goto branch 2 - /// call void @fun() ----> call and ret has been handled - /// %mul = mul i32 %sub, %d -----> goto branch 3 - if (bbToNodes.find(bb) == bbToNodes.end()) - { - // branch 1, for the first node of basic block - bbToNodes[bb] = {icfgNode}; - subNodes.insert(icfgNode); - } - else - { - const ICFGNode* pNode = bbToNodes[bb].back(); - if (!SVFUtil::isa(pNode)) - { - // branch 2, for the middle node of basic block - // do nothing if it is not ret node - } - else - { - // branch 3, for the node after ret node - bbToNodes[bb].push_back(icfgNode); - subNodes.insert(icfgNode); - } - } - } - } - } - - if (const FunEntryICFGNode* funEntryNode = icfg->getFunEntryICFGNode(func)) - { - if (const SVFBasicBlock* bb = funEntryNode->getBB()) - { - std::vector& nodes = bbToNodes[bb]; - nodes.insert(nodes.begin(), funEntryNode); - subNodes.insert(funEntryNode); - } - } - if (const FunExitICFGNode* funExitNode = icfg->getFunExitICFGNode(func)) - { - if (const SVFBasicBlock* bb = funExitNode->getBB()) - { - std::vector& nodes = bbToNodes[bb]; - nodes.push_back(funExitNode); - subNodes.insert(funExitNode); - } - } - } - - for (auto &it: subNodes) - { - ICFGNode* head = const_cast(it); - if (head->getOutEdges().size() != 1) - { - // if head has more than one out edges, we don't merge any following nodes. - continue; - } - else - { - ICFGNode* next = (*head->getOutEdges().begin())->getDstNode(); - // merge the following nodes, until the next subnode - while (subNodes.find(next) == subNodes.end()) - { - assert(icfg->getRepNode(next) != head && "should not find a circle here"); - icfg->removeICFGEdge(*head->getOutEdges().begin()); - std::vector rm_edges; - // Step 1: merge the out edges of next to head - for (ICFGEdge* outEdge: next->getOutEdges()) - { - rm_edges.push_back(outEdge); - ICFGNode* post = outEdge->getDstNode(); - if (outEdge->isIntraCFGEdge()) - { - IntraCFGEdge* intraEdge = SVFUtil::dyn_cast(outEdge); - if (intraEdge->getCondition()) - { - icfg->addConditionalIntraEdge(head, post, intraEdge->getCondition(), intraEdge->getSuccessorCondValue()); - } - else - { - icfg->addIntraEdge(head, post); - } - } - } - // Step 2: update the sub node map and rep node map - icfg->updateSubAndRep(next, head); - if (next->getOutEdges().size() == 1) - { - // Step 3: remove the edges from next to its next, since next has been merged to head - // if only one out edge, we may continue to merge the next node if it is not a subnode - next = (*next->getOutEdges().begin())->getDstNode(); - for (ICFGEdge* edge: rm_edges) - icfg->removeICFGEdge(edge); - - } - else - { - // if more than one out edges, we don't merge any following nodes. - for (ICFGEdge* edge: rm_edges) - icfg->removeICFGEdge(edge); - break; - } - } - } - } -} -} \ No newline at end of file diff --git a/svf/lib/AE/Svfexe/SVFIR2AbsState.cpp b/svf/lib/AE/Svfexe/SVFIR2AbsState.cpp deleted file mode 100644 index 10f2686c0..000000000 --- a/svf/lib/AE/Svfexe/SVFIR2AbsState.cpp +++ /dev/null @@ -1,957 +0,0 @@ -//===- SVFIR2AbsState.cpp -- SVF IR Translation to Interval Domain-----// -// -// SVF: Static Value-Flow Analysis -// -// Copyright (C) <2013-2022> -// - -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU Affero General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Affero General Public License for more details. - -// You should have received a copy of the GNU Affero General Public License -// along with this program. If not, see . -// -//===----------------------------------------------------------------------===// -/* - * SVFIR2AbsState.cpp - * - * Created on: Aug 7, 2022 - * Author: Jiawei Wang, Xiao Cheng - * - */ - -#include "AE/Svfexe/SVFIR2AbsState.h" -#include "Util/Options.h" - -using namespace SVF; -using namespace SVFUtil; - -AbstractValue SVF::SVFIR2AbsState::globalNulladdrs = AddressValue(); - -/** - * This function, getRangeLimitFromType, calculates the lower and upper bounds of - * a numeric range for a given SVFType. It is used to determine the possible value - * range of integer types. If the type is an SVFIntegerType, it calculates the bounds - * based on the size and signedness of the type. The calculated bounds are returned - * as an IntervalValue representing the lower (lb) and upper (ub) limits of the range. - * - * @param type The SVFType for which to calculate the value range. - * - * @return An IntervalValue representing the lower and upper bounds of the range. - */ -IntervalValue SVFIR2AbsState::getRangeLimitFromType(const SVFType* type) -{ - if (const SVFIntegerType* intType = SVFUtil::dyn_cast(type)) - { - u32_t bits = type->getByteSize() * 8; - s64_t ub = 0; - s64_t lb = 0; - if (bits >= 32) - { - if (intType->isSigned()) - { - ub = static_cast(std::numeric_limits::max()); - lb = static_cast(std::numeric_limits::min()); - } - else - { - ub = static_cast(std::numeric_limits::max()); - lb = static_cast(std::numeric_limits::min()); - } - } - else if (bits == 16) - { - if (intType->isSigned()) - { - ub = static_cast(std::numeric_limits::max()); - lb = static_cast(std::numeric_limits::min()); - } - else - { - ub = static_cast(std::numeric_limits::max()); - lb = static_cast(std::numeric_limits::min()); - } - } - else if (bits == 8) - { - if (intType->isSigned()) - { - ub = static_cast(std::numeric_limits::max()); - lb = static_cast(std::numeric_limits::min()); - } - else - { - ub = static_cast(std::numeric_limits::max()); - lb = static_cast(std::numeric_limits::min()); - } - } - return IntervalValue(lb, ub); - } - else if (SVFUtil::isa(type)) - { - // handle other type like float double, set s32_t as the range - s64_t ub = static_cast(std::numeric_limits::max()); - s64_t lb = static_cast(std::numeric_limits::min()); - return IntervalValue(lb, ub); - } - else - { - return IntervalValue::top(); - // other types, return top interval - } -} - -IntervalValue SVFIR2AbsState::getZExtValue(const AbstractState& as, const SVFVar* var) -{ - const SVFType* type = var->getType(); - if (SVFUtil::isa(type)) - { - u32_t bits = type->getByteSize() * 8; - if (as[var->getId()].getInterval().is_numeral()) - { - if (bits == 8) - { - int8_t signed_i8_value = as[var->getId()].getInterval().getIntNumeral(); - u32_t unsigned_value = static_cast(signed_i8_value); - return IntervalValue(unsigned_value, unsigned_value); - } - else if (bits == 16) - { - s16_t signed_i16_value = as[var->getId()].getInterval().getIntNumeral(); - u32_t unsigned_value = static_cast(signed_i16_value); - return IntervalValue(unsigned_value, unsigned_value); - } - else if (bits == 32) - { - s32_t signed_i32_value = as[var->getId()].getInterval().getIntNumeral(); - u32_t unsigned_value = static_cast(signed_i32_value); - return IntervalValue(unsigned_value, unsigned_value); - } - else if (bits == 64) - { - s64_t signed_i64_value = as[var->getId()].getInterval().getIntNumeral(); - return IntervalValue((s64_t)signed_i64_value, (s64_t)signed_i64_value); - // we only support i64 at most - } - else - { - assert(false && "cannot support int type other than u8/16/32/64"); - } - } - else - { - return IntervalValue::top(); // TODO: may have better solution - } - } - return IntervalValue::top(); // TODO: may have better solution -} - -IntervalValue SVFIR2AbsState::getSExtValue(const AbstractState& as, const SVFVar* var) -{ - return as[var->getId()].getInterval(); -} - -IntervalValue SVFIR2AbsState::getFPToSIntValue(const AbstractState& as, const SVF::SVFVar* var) -{ - // IntervalValue are BoundedInt, so we can directly return the value - return getSExtValue(as, var); -} - -IntervalValue SVFIR2AbsState::getFPToUIntValue(const AbstractState& as, const SVF::SVFVar* var) -{ - // IntervalValue are BoundedInt, so we can directly return the value - return getZExtValue(as, var); -} - -IntervalValue SVFIR2AbsState::getSIntToFPValue(const AbstractState& as, const SVF::SVFVar* var) -{ - // IntervalValue are BoundedInt, so we can directly return the value - return getSExtValue(as, var); -} - -IntervalValue SVFIR2AbsState::getUIntToFPValue(const AbstractState& as, const SVF::SVFVar* var) -{ - // IntervalValue are BoundedInt, so we can directly return the value - return getZExtValue(as, var); -} - -IntervalValue SVFIR2AbsState::getTruncValue(const AbstractState& as, const SVF::SVFVar* var, const SVFType* dstType) -{ - const IntervalValue& itv = as[var->getId()].getInterval(); - if(itv.isBottom()) return itv; - // get the value of ub and lb - s64_t int_lb = itv.lb().getIntNumeral(); - s64_t int_ub = itv.ub().getIntNumeral(); - // get dst type - u32_t dst_bits = dstType->getByteSize() * 8; - if (dst_bits == 8) - { - // get the signed value of ub and lb - int8_t s8_lb = static_cast(int_lb); - int8_t s8_ub = static_cast(int_ub); - if (s8_lb > s8_ub) - { - // return range of s8 - return IntervalValue::top(); - } - return IntervalValue(s8_lb, s8_ub); - } - else if (dst_bits == 16) - { - // get the signed value of ub and lb - s16_t s16_lb = static_cast(int_lb); - s16_t s16_ub = static_cast(int_ub); - if (s16_lb > s16_ub) - { - // return range of s16 - return IntervalValue::top(); - } - return IntervalValue(s16_lb, s16_ub); - } - else if (dst_bits == 32) - { - // get the signed value of ub and lb - s32_t s32_lb = static_cast(int_lb); - s32_t s32_ub = static_cast(int_ub); - if (s32_lb > s32_ub) - { - // return range of s32 - return IntervalValue::top(); - } - return IntervalValue(s32_lb, s32_ub); - } - else - { - assert(false && "cannot support dst int type other than u8/16/32"); - } -} - -IntervalValue SVFIR2AbsState::getFPTruncValue(const AbstractState& as, const SVF::SVFVar* var, const SVFType* dstType) -{ - // TODO: now we do not really handle fptrunc - return as[var->getId()].getInterval(); -} - -void SVFIR2AbsState::widenAddrs(AbstractState& as, AbstractState&lhs, const AbstractState&rhs) -{ - for (const auto &rhsItem: rhs._varToAbsVal) - { - auto lhsIter = lhs._varToAbsVal.find(rhsItem.first); - if (lhsIter != lhs._varToAbsVal.end()) - { - if (rhsItem.second.isAddr()) - { - for (const auto &addr: rhsItem.second.getAddrs()) - { - if (!lhsIter->second.getAddrs().contains(addr)) - { - for (s32_t i = 0; i < (s32_t) Options::MaxFieldLimit(); i++) - { - lhsIter->second.join_with(getGepObjAddress(as, getInternalID(addr), i)); - } - } - } - } - } - } - for (const auto &rhsItem: rhs._addrToAbsVal) - { - auto lhsIter = lhs._addrToAbsVal.find(rhsItem.first); - if (lhsIter != lhs._addrToAbsVal.end()) - { - if (rhsItem.second.isAddr()) - { - for (const auto& addr : rhsItem.second.getAddrs()) - { - if (!lhsIter->second.getAddrs().contains(addr)) - { - for (s32_t i = 0; i < (s32_t)Options::MaxFieldLimit(); - i++) - { - lhsIter->second.join_with( - getGepObjAddress(as, getInternalID(addr), i)); - } - } - } - } - } - } -} - -void SVFIR2AbsState::narrowAddrs(AbstractState& as, AbstractState&lhs, const AbstractState&rhs) -{ - for (const auto &rhsItem: rhs._varToAbsVal) - { - auto lhsIter = lhs._varToAbsVal.find(rhsItem.first); - if (lhsIter != lhs._varToAbsVal.end()) - { - if (lhsIter->second.isAddr()) - { - for (const auto &addr: lhsIter->second.getAddrs()) - { - if (!rhsItem.second.getAddrs().contains(addr)) - { - lhsIter->second = rhsItem.second; - break; - } - } - } - } - } - for (const auto &rhsItem: rhs._addrToAbsVal) - { - auto lhsIter = lhs._addrToAbsVal.find(rhsItem.first); - if (lhsIter != lhs._addrToAbsVal.end()) - { - if (lhsIter->second.isAddr()) - { - for (const auto& addr : lhsIter->second.getAddrs()) - { - if (!rhsItem.second.getAddrs().contains(addr)) - { - lhsIter->second = rhsItem.second; - break; - } - } - } - } - } -} - -AddressValue SVFIR2AbsState::getGepObjAddress(AbstractState& as, u32_t pointer, APOffset offset) -{ - AbstractValue addrs = getAddrs(as, pointer); - AddressValue ret = AddressValue(); - for (const auto &addr: addrs.getAddrs()) - { - s64_t baseObj = getInternalID(addr); - assert(SVFUtil::isa(_svfir->getGNode(baseObj)) && "Fail to get the base object address!"); - NodeID gepObj = _svfir->getGepObjVar(baseObj, offset); - as[gepObj] = AddressValue(AbstractState::getVirtualMemAddress(gepObj)); - ret.insert(AbstractState::getVirtualMemAddress(gepObj)); - } - return ret; -} - -/** - * This function, getByteOffset, calculates the byte offset for a given GepStmt. - * - * @param gep The GepStmt representing the GetElementPtr instruction. - * - * @return The calculated byte offset as an IntervalValue. - * - * It is byte offset rather than flatten index. - * e.g. %var2 = getelementptr inbounds %struct.OuterStruct, %struct.OuterStruct* %var0, i64 0, i32 2, i32 0, i64 %var1 -* %struct.OuterStruct = type { i32, i32, %struct.InnerStruct } -* %struct.InnerStruct = type { [2 x i32] } -* there are 4 GepTypePairs (<0, %struct.OuterStruct*>, <2, %struct.OuterStruct>, <0, %struct.InnerStruct>, <%var1, [2xi32]>) -* this function process arr/ptr subtype by calculating elemByteSize * indexOperand - * and process struct subtype by calculating the byte offset from beginning to the field of struct -* e.g. for 0th pair <0, %struct.OuterStruct*>, it is 0* ptrSize(%struct.OuterStruct*) = 0 bytes -* for 1st pair <2, %struct.OuterStruct>, it is 2nd field in %struct.OuterStruct = 8 bytes -* for 2nd pair <0, %struct.InnerStruct>, it is 0th field in %struct.InnerStruct = 0 bytes -* for 3rd pair <%var1, [2xi32]>, it is %var1'th element in array [2xi32] = 4bytes * %var1 -* ---- -* Therefore the final byteoffset is [8+4*var1.lb(), 8+4*var1.ub()] - * - */ -IntervalValue SVFIR2AbsState::getByteOffset(const AbstractState& as, const GepStmt *gep) -{ - IntervalValue byte_res = IntervalValue(0); - for (int i = gep->getOffsetVarAndGepTypePairVec().size() - 1; i >= 0; i--) - { - AccessPath::IdxOperandPair IdxVarAndType = - gep->getOffsetVarAndGepTypePairVec()[i]; - const SVFValue *value = - gep->getOffsetVarAndGepTypePairVec()[i].first->getValue(); - const SVFType *type = IdxVarAndType.second; - s64_t idxLb = 0; - s64_t idxUb = 0; - s64_t byteLb = 0; - s64_t byteUb = 0; - // get lb and ub of the index value - if (const SVFConstantInt* constInt = SVFUtil::dyn_cast(value)) - idxLb = idxUb = constInt->getSExtValue(); - else - { - IntervalValue idxItv = as[_svfir->getValueNode(value)].getInterval(); - if (idxItv.isBottom()) - idxLb = idxUb = 0; - else - { - idxLb = idxItv.lb().getIntNumeral(); - idxUb = idxItv.ub().getIntNumeral(); - } - } - // for pointer type, elemByteSize * indexOperand - if (SVFUtil::isa(type)) - { - u32_t elemByte = gep->getAccessPath().gepSrcPointeeType()->getByteSize(); - byteLb = (double)Options::MaxFieldLimit() / elemByte < idxLb? Options::MaxFieldLimit(): idxLb * elemByte; - byteUb = (double)Options::MaxFieldLimit() / elemByte < idxUb? Options::MaxFieldLimit(): idxUb * elemByte; - - } - // for array or struct, get flattened index from SymbolTable Info - // and then calculate the byte offset from beginning to the field of struct - else - { - if(Options::ModelArrays()) - { - const std::vector& so = SymbolTableInfo::SymbolInfo() - ->getTypeInfo(type) - ->getFlattenedElemIdxVec(); - if (so.empty() || idxUb >= (APOffset)so.size() || idxLb < 0) - { - byteLb = byteUb = 0; - } - else - { - idxLb = SymbolTableInfo::SymbolInfo()->getFlattenedElemIdx( - type, idxLb); - idxUb = SymbolTableInfo::SymbolInfo()->getFlattenedElemIdx( - type, idxUb); - for (u32_t idx = 0; idx < idxLb; ++idx) - { - s64_t byte = SymbolTableInfo::SymbolInfo()->getFlatternedElemType(type, idx)->getByteSize(); - byteLb += byte; - } - byteUb = byteLb; - for (u32_t idx = idxLb; idx < idxUb; ++idx) - { - s64_t byte = SymbolTableInfo::SymbolInfo()->getFlatternedElemType(type, idx)->getByteSize(); - byteUb += byte; - } - } - } - else - { - byteLb = byteUb = 0; - } - - } - byte_res = byte_res + IntervalValue(byteLb, byteUb); - } - - if (byte_res.isBottom()) - { - byte_res = IntervalValue(0); - } - return byte_res; -} - -/** - * This function, getElementIndex, calculates the offset range as a pair - * of APOffset values for a given GepStmt. - * - * @param gep The GepStmt representing the GetElementPtr instruction. - * - * @return A pair of APOffset values representing the offset range. - */ -IntervalValue SVFIR2AbsState::getElementIndex(const AbstractState& as, const GepStmt *gep) -{ - if (gep->isConstantOffset()) - return IntervalValue((s64_t)gep->accumulateConstantOffset()); - IntervalValue res = IntervalValue(0); - for (int i = gep->getOffsetVarAndGepTypePairVec().size() - 1; i >= 0; i--) - { - AccessPath::IdxOperandPair IdxVarAndType = - gep->getOffsetVarAndGepTypePairVec()[i]; - const SVFValue *value = - gep->getOffsetVarAndGepTypePairVec()[i].first->getValue(); - const SVFType *type = IdxVarAndType.second; - // idxLb/Ub is the flattened offset generated by the current OffsetVarAndGepTypePair - s64_t idxLb; - s64_t idxUb; - // get lb and ub of the index value - if (const SVFConstantInt* constInt = SVFUtil::dyn_cast(value)) - idxLb = idxUb = constInt->getSExtValue(); - else - { - IntervalValue idxItv = as[_svfir->getValueNode(value)].getInterval(); - if (idxItv.isBottom()) - idxLb = idxUb = 0; - else - { - idxLb = idxItv.lb().getIntNumeral(); - idxUb = idxItv.ub().getIntNumeral(); - } - } - // for pointer type, flattened index = elemNum * idx - if (SVFUtil::isa(type)) - { - u32_t elemNum = gep->getAccessPath().getElementNum(gep->getAccessPath().gepSrcPointeeType()); - idxLb = (double)Options::MaxFieldLimit() / elemNum < idxLb? Options::MaxFieldLimit(): idxLb * elemNum; - idxUb = (double)Options::MaxFieldLimit() / elemNum < idxUb? Options::MaxFieldLimit(): idxUb * elemNum; - } - // for array or struct, get flattened index from SymbolTable Info - else - { - if(Options::ModelArrays()) - { - const std::vector& so = SymbolTableInfo::SymbolInfo() - ->getTypeInfo(type) - ->getFlattenedElemIdxVec(); - if (so.empty() || idxUb >= (APOffset)so.size() || idxLb < 0) - { - idxLb = idxUb = 0; - } - else - { - idxLb = SymbolTableInfo::SymbolInfo()->getFlattenedElemIdx( - type, idxLb); - idxUb = SymbolTableInfo::SymbolInfo()->getFlattenedElemIdx( - type, idxUb); - } - } - else - idxLb = idxUb = 0; - } - res = res + IntervalValue(idxLb, idxUb); - } - res.meet_with(IntervalValue((s64_t)0, (s64_t)Options::MaxFieldLimit())); - if (res.isBottom()) - { - res = IntervalValue(0); - } - return res; -} - - -void SVFIR2AbsState::initObjVar(AbstractState& as, const ObjVar* var) -{ - NodeID varId = var->getId(); - if (var->hasValue()) - { - const MemObj *obj = var->getMemObj(); - /// constant data - if (obj->isConstDataOrConstGlobal() || obj->isConstantArray() || obj->isConstantStruct()) - { - if (const SVFConstantInt *consInt = SVFUtil::dyn_cast(obj->getValue())) - { - s64_t numeral = consInt->getSExtValue(); - as[varId] = IntervalValue(numeral, numeral); - } - else if (const SVFConstantFP* consFP = SVFUtil::dyn_cast(obj->getValue())) - as[varId] = IntervalValue(consFP->getFPValue(), consFP->getFPValue()); - else if (SVFUtil::isa(obj->getValue())) - as[varId] = IntervalValue(0, 0); - else if (SVFUtil::isa(obj->getValue())) - { - as[varId] = AddressValue(getVirtualMemAddress(varId)); - } - - else if (obj->isConstantArray() || obj->isConstantStruct()) - as[varId] = IntervalValue::top(); - else - as[varId] = IntervalValue::top(); - } - else - as[varId] = AddressValue(getVirtualMemAddress(varId)); - } - else - as[varId] = AddressValue(getVirtualMemAddress(varId)); -} - - -void SVFIR2AbsState::handleAddr(AbstractState& as, const AddrStmt *addr) -{ - initObjVar(as, SVFUtil::cast(addr->getRHSVar())); - if (addr->getRHSVar()->getType()->getKind() == SVFType::SVFIntegerTy) - as[addr->getRHSVarID()].getInterval().meet_with(getRangeLimitFromType(addr->getRHSVar()->getType())); - as[addr->getLHSVarID()] = as[addr->getRHSVarID()]; -} - - -void SVFIR2AbsState::handleBinary(AbstractState& as, const BinaryOPStmt *binary) -{ - u32_t op0 = binary->getOpVarID(0); - u32_t op1 = binary->getOpVarID(1); - u32_t res = binary->getResID(); - if (!inVarToValTable(as, op0)) as[op0] = IntervalValue::top(); - if (!inVarToValTable(as, op1)) as[op1] = IntervalValue::top(); - IntervalValue &lhs = as[op0].getInterval(), &rhs = as[op1].getInterval(); - IntervalValue resVal; - switch (binary->getOpcode()) - { - case BinaryOPStmt::Add: - case BinaryOPStmt::FAdd: - resVal = (lhs + rhs); - break; - case BinaryOPStmt::Sub: - case BinaryOPStmt::FSub: - resVal = (lhs - rhs); - break; - case BinaryOPStmt::Mul: - case BinaryOPStmt::FMul: - resVal = (lhs * rhs); - break; - case BinaryOPStmt::SDiv: - case BinaryOPStmt::FDiv: - case BinaryOPStmt::UDiv: - resVal = (lhs / rhs); - break; - case BinaryOPStmt::SRem: - case BinaryOPStmt::FRem: - case BinaryOPStmt::URem: - resVal = (lhs % rhs); - break; - case BinaryOPStmt::Xor: - resVal = (lhs ^ rhs); - break; - case BinaryOPStmt::And: - resVal = (lhs & rhs); - break; - case BinaryOPStmt::Or: - resVal = (lhs | rhs); - break; - case BinaryOPStmt::AShr: - resVal = (lhs >> rhs); - break; - case BinaryOPStmt::Shl: - resVal = (lhs << rhs); - break; - case BinaryOPStmt::LShr: - resVal = (lhs >> rhs); - break; - default: - assert(false && "undefined binary: "); - } - as[res] = resVal; -} - -void SVFIR2AbsState::handleCmp(AbstractState& as, const CmpStmt *cmp) -{ - u32_t op0 = cmp->getOpVarID(0); - u32_t op1 = cmp->getOpVarID(1); - if (!inVarToValTable(as, op0)) as[op0] = IntervalValue::top(); - if (!inVarToValTable(as, op1)) as[op1] = IntervalValue::top(); - u32_t res = cmp->getResID(); - if (inVarToValTable(as, op0) && inVarToValTable(as, op1)) - { - IntervalValue resVal; - IntervalValue &lhs = as[op0].getInterval(), &rhs = as[op1].getInterval(); - //AbstractValue - auto predicate = cmp->getPredicate(); - switch (predicate) - { - case CmpStmt::ICMP_EQ: - case CmpStmt::FCMP_OEQ: - case CmpStmt::FCMP_UEQ: - resVal = (lhs == rhs); - // resVal = (lhs.getInterval() == rhs.getInterval()); - break; - case CmpStmt::ICMP_NE: - case CmpStmt::FCMP_ONE: - case CmpStmt::FCMP_UNE: - resVal = (lhs != rhs); - break; - case CmpStmt::ICMP_UGT: - case CmpStmt::ICMP_SGT: - case CmpStmt::FCMP_OGT: - case CmpStmt::FCMP_UGT: - resVal = (lhs > rhs); - break; - case CmpStmt::ICMP_UGE: - case CmpStmt::ICMP_SGE: - case CmpStmt::FCMP_OGE: - case CmpStmt::FCMP_UGE: - resVal = (lhs >= rhs); - break; - case CmpStmt::ICMP_ULT: - case CmpStmt::ICMP_SLT: - case CmpStmt::FCMP_OLT: - case CmpStmt::FCMP_ULT: - resVal = (lhs < rhs); - break; - case CmpStmt::ICMP_ULE: - case CmpStmt::ICMP_SLE: - case CmpStmt::FCMP_OLE: - case CmpStmt::FCMP_ULE: - resVal = (lhs <= rhs); - break; - case CmpStmt::FCMP_FALSE: - resVal = IntervalValue(0, 0); - break; - case CmpStmt::FCMP_TRUE: - resVal = IntervalValue(1, 1); - break; - default: - { - assert(false && "undefined compare: "); - } - } - as[res] = resVal; - } - else if (inVarToAddrsTable(as, op0) && inVarToAddrsTable(as, op1)) - { - IntervalValue resVal; - AbstractValue &lhs = getAddrs(as, op0), &rhs = getAddrs(as, op1); - auto predicate = cmp->getPredicate(); - switch (predicate) - { - case CmpStmt::ICMP_EQ: - case CmpStmt::FCMP_OEQ: - case CmpStmt::FCMP_UEQ: - { - if (lhs.getAddrs().hasIntersect(rhs.getAddrs())) - { - resVal = IntervalValue(0, 1); - } - else if (lhs.getAddrs().empty() && rhs.getAddrs().empty()) - { - resVal = IntervalValue(1, 1); - } - else - { - resVal = IntervalValue(0, 0); - } - break; - } - case CmpStmt::ICMP_NE: - case CmpStmt::FCMP_ONE: - case CmpStmt::FCMP_UNE: - { - if (lhs.getAddrs().hasIntersect(rhs.getAddrs())) - { - resVal = IntervalValue(0, 1); - } - else if (lhs.getAddrs().empty() && rhs.getAddrs().empty()) - { - resVal = IntervalValue(0, 0); - } - else - { - resVal = IntervalValue(1, 1); - } - break; - } - case CmpStmt::ICMP_UGT: - case CmpStmt::ICMP_SGT: - case CmpStmt::FCMP_OGT: - case CmpStmt::FCMP_UGT: - { - if (lhs.getAddrs().size() == 1 && rhs.getAddrs().size() == 1) - { - resVal = IntervalValue(*lhs.getAddrs().begin() > *rhs.getAddrs().begin()); - } - else - { - resVal = IntervalValue(0, 1); - } - break; - } - case CmpStmt::ICMP_UGE: - case CmpStmt::ICMP_SGE: - case CmpStmt::FCMP_OGE: - case CmpStmt::FCMP_UGE: - { - if (lhs.getAddrs().size() == 1 && rhs.getAddrs().size() == 1) - { - resVal = IntervalValue(*lhs.getAddrs().begin() >= *rhs.getAddrs().begin()); - } - else - { - resVal = IntervalValue(0, 1); - } - break; - } - case CmpStmt::ICMP_ULT: - case CmpStmt::ICMP_SLT: - case CmpStmt::FCMP_OLT: - case CmpStmt::FCMP_ULT: - { - if (lhs.getAddrs().size() == 1 && rhs.getAddrs().size() == 1) - { - resVal = IntervalValue(*lhs.getAddrs().begin() < *rhs.getAddrs().begin()); - } - else - { - resVal = IntervalValue(0, 1); - } - break; - } - case CmpStmt::ICMP_ULE: - case CmpStmt::ICMP_SLE: - case CmpStmt::FCMP_OLE: - case CmpStmt::FCMP_ULE: - { - if (lhs.getAddrs().size() == 1 && rhs.getAddrs().size() == 1) - { - resVal = IntervalValue(*lhs.getAddrs().begin() <= *rhs.getAddrs().begin()); - } - else - { - resVal = IntervalValue(0, 1); - } - break; - } - case CmpStmt::FCMP_FALSE: - resVal = IntervalValue(0, 0); - break; - case CmpStmt::FCMP_TRUE: - resVal = IntervalValue(1, 1); - break; - default: - { - assert(false && "undefined compare: "); - } - } - as[res] = resVal; - } -} - -void SVFIR2AbsState::handleLoad(AbstractState& as, const LoadStmt *load) -{ - u32_t rhs = load->getRHSVarID(); - u32_t lhs = load->getLHSVarID(); - AbstractValue &addrs = as[rhs]; - AbstractValue rhsVal; // interval::bottom Address::bottom - // AbstractValue absRhs - for (const auto &addr: addrs.getAddrs()) - rhsVal.join_with(as.load(addr)); - as[lhs] = rhsVal; -} - -void SVFIR2AbsState::handleStore(AbstractState& as, const StoreStmt *store) -{ - u32_t rhs = store->getRHSVarID(); - u32_t lhs = store->getLHSVarID(); - - for (const auto &addr: as[lhs].getAddrs()) - { - as.store(addr, as[rhs]); - } -} - -void SVFIR2AbsState::handleCopy(AbstractState& as, const CopyStmt *copy) -{ - u32_t lhs = copy->getLHSVarID(); - u32_t rhs = copy->getRHSVarID(); - - if (copy->getCopyKind() == CopyStmt::COPYVAL) - { - as[lhs] = as[rhs]; - } - else if (copy->getCopyKind() == CopyStmt::ZEXT) - { - as[lhs] = getZExtValue(as, copy->getRHSVar()); - } - else if (copy->getCopyKind() == CopyStmt::SEXT) - { - as[lhs] = getSExtValue(as, copy->getRHSVar()); - } - else if (copy->getCopyKind() == CopyStmt::FPTOSI) - { - as[lhs] = getFPToSIntValue(as, copy->getRHSVar()); - } - else if (copy->getCopyKind() == CopyStmt::FPTOUI) - { - as[lhs] = getFPToUIntValue(as, copy->getRHSVar()); - } - else if (copy->getCopyKind() == CopyStmt::SITOFP) - { - as[lhs] = getSIntToFPValue(as, copy->getRHSVar()); - } - else if (copy->getCopyKind() == CopyStmt::UITOFP) - { - as[lhs] = getUIntToFPValue(as, copy->getRHSVar()); - } - else if (copy->getCopyKind() == CopyStmt::TRUNC) - { - as[lhs] = getTruncValue(as, copy->getRHSVar(), copy->getLHSVar()->getType()); - } - else if (copy->getCopyKind() == CopyStmt::FPTRUNC) - { - as[lhs] = getFPTruncValue(as, copy->getRHSVar(), copy->getLHSVar()->getType()); - } - else if (copy->getCopyKind() == CopyStmt::INTTOPTR) - { - //insert nullptr - } - else if (copy->getCopyKind() == CopyStmt::PTRTOINT) - { - as[lhs] = IntervalValue::top(); - } - else if (copy->getCopyKind() == CopyStmt::BITCAST) - { - if (as[rhs].isAddr()) - { - as[lhs] = as[rhs]; - } - else - { - // do nothing - } - } - else - { - assert(false && "undefined copy kind"); - abort(); - } -} - -void SVFIR2AbsState::handleGep(AbstractState& as, const GepStmt *gep) -{ - u32_t rhs = gep->getRHSVarID(); - u32_t lhs = gep->getLHSVarID(); - IntervalValue offsetPair = getElementIndex(as, gep); - AbstractValue gepAddrs; - APOffset lb = offsetPair.lb().getIntNumeral() < Options::MaxFieldLimit()? - offsetPair.lb().getIntNumeral(): Options::MaxFieldLimit(); - APOffset ub = offsetPair.ub().getIntNumeral() < Options::MaxFieldLimit()? - offsetPair.ub().getIntNumeral(): Options::MaxFieldLimit(); - for (APOffset i = lb; i <= ub; i++) - gepAddrs.join_with(getGepObjAddress(as, rhs, i)); - as[lhs] = gepAddrs; -} - -void SVFIR2AbsState::handleSelect(AbstractState& as, const SelectStmt *select) -{ - u32_t res = select->getResID(); - u32_t tval = select->getTrueValue()->getId(); - u32_t fval = select->getFalseValue()->getId(); - u32_t cond = select->getCondition()->getId(); - if (as[cond].getInterval().is_numeral()) - { - as[res] = as[cond].getInterval().is_zero() ? as[fval] : as[tval]; - } - else - { - as[res] = as[tval]; - as[res].join_with(as[fval]); - } -} - -void SVFIR2AbsState::handlePhi(AbstractState& as, const PhiStmt *phi) -{ - u32_t res = phi->getResID(); - AbstractValue rhs; - for (u32_t i = 0; i < phi->getOpVarNum(); i++) - { - NodeID curId = phi->getOpVarID(i); - rhs.join_with(as[curId]); - } - as[res] = rhs; -} - - -void SVFIR2AbsState::handleCall(AbstractState& as, const CallPE *callPE) -{ - NodeID lhs = callPE->getLHSVarID(); - NodeID rhs = callPE->getRHSVarID(); - as[lhs] = as[rhs]; -} - -void SVFIR2AbsState::handleRet(AbstractState& as, const RetPE *retPE) -{ - NodeID lhs = retPE->getLHSVarID(); - NodeID rhs = retPE->getRHSVarID(); - as[lhs] = as[rhs]; -} diff --git a/svf/lib/CFL/CFGNormalizer.cpp b/svf/lib/CFL/CFGNormalizer.cpp deleted file mode 100644 index b07c30e19..000000000 --- a/svf/lib/CFL/CFGNormalizer.cpp +++ /dev/null @@ -1,512 +0,0 @@ -//===----- CFGNormalizer.cpp -- Context Free Grammar Normalizer--------------// -// -// SVF: Static Value-Flow Analysis -// -// Copyright (C) <2013-> -// - -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU Affero General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Affero General Public License for more details. - -// You should have received a copy of the GNU Affero General Public License -// along with this program. If not, see . -// -//===----------------------------------------------------------------------===// - -/* - * CFGNormalizer.cpp - * - * Created on: April 19 , 2022 - * Author: Pei Xu - */ - -#include "CFL/CFGNormalizer.h" -#include "Util/SVFUtil.h" -#include "Util/WorkList.h" -#include "SVFIR/SVFValue.h" -#include -#include -#include -#include -#include - -using namespace SVF; - -CFGrammar* CFGNormalizer::normalize(GrammarBase *generalGrammar) -{ - CFGrammar *grammar = new CFGrammar(); - grammar->setStartKind(generalGrammar->getStartKind()); - grammar->setTerminals(generalGrammar->getTerminals()); - grammar->setNonterminals(generalGrammar->getNonterminals()); - grammar->setEBNFSigns(generalGrammar->getEBNFSigns()); - grammar->setTotalKind(generalGrammar->getTotalKind()); - grammar->setAttributeKinds(generalGrammar->getAttrSyms()); - grammar->setKindToAttrsMap(generalGrammar->getKindToAttrsMap()); - grammar->setRawProductions(generalGrammar->getRawProductions()); - barReplace(grammar); - ebnfSignReplace('*', grammar); - ebnfSignReplace('?', grammar); - ebnf_bin(grammar); - fillAttribute(grammar, grammar->getKindToAttrsMap()); - return grammar; -} - - -CFGrammar* CFGNormalizer::fillAttribute(CFGrammar *grammar, const Map>& kindToAttrsMap) -{ - NodeSet nodeSet = {}; - for (auto pair: kindToAttrsMap) - { - for (auto attri: pair.second) - { - nodeSet.insert(attri); - } - } - for(auto symProdsPair: grammar->getRawProductions()) - { - for(auto prod: symProdsPair.second) - { - /// rawProductions production does not include lhs - /// so append to the begin of the production - GrammarBase::Production tempP = prod; - tempP.insert(tempP.begin(), symProdsPair.first); - GrammarBase::Productions normalProds; - getFilledProductions(tempP, nodeSet, grammar, normalProds); - for (auto filledProd : normalProds) - { - insertToCFLGrammar(grammar, filledProd); - } - } - } - - return grammar; -} - -void CFGNormalizer::ebnf_bin(CFGrammar *grammar) -{ - GrammarBase::SymbolMap new_grammar = {}; - std::string tempStr = ""; - removeFirstSymbol(grammar); - - auto rawProductions = grammar->getRawProductions(); - - for(auto itr : rawProductions) - { - auto head = *(grammar->getRawProductions().find(itr.first)); - for(auto rule: head.second) - { - if (rule.size() < 3) continue; - - GrammarBase::Symbol first = rule[0]; - GrammarBase::Production long_run(rule.begin() + 1, rule.end()); - auto it = grammar->getRawProductions()[head.first].find(rule); - grammar->getRawProductions()[head.first].erase(it); - GrammarBase::Symbol X = check_head(new_grammar, long_run); - if (X == u32_t(-1)) - { - X = check_head(grammar->getRawProductions(), long_run); - } - if ((X == u32_t(-1)) == false) - { - rule = {first, X}; - grammar->getRawProductions()[head.first].insert(rule); - } - else - { - tempStr = "X"; - std::ostringstream ss; - ss << grammar->num_generator(); - tempStr.append(ss.str()); - /// Assign _attribute - /// if target portion of the production contain more than 1 variable then - /// X add no variable attribute - /// if target only contain one variable attribute X share the same variable attribute - Set variableAttributeSet = {}; - for (unsigned i = 0; i < long_run.size(); i++) - { - GrammarBase::VariableAttribute variableAttribute = long_run[i].variableAttribute; - if ( variableAttribute != 0) - { - variableAttributeSet.insert(variableAttribute); - } - } - if ( variableAttributeSet.size() == 1) - { - tempStr += "_"; - tempStr += char(*variableAttributeSet.begin()); - } - GrammarBase::Symbol tempSym = grammar->insertNonTerminalSymbol(tempStr); - rule = {first, tempSym}; - grammar->getRawProductions()[head.first].insert(rule); - X = tempSym; - } - new_grammar[X] = {}; - GrammarBase::Production temp_p = long_run; - GrammarBase::Symbol RHX; - if (long_run.size() ==2) - { - new_grammar[X].insert(temp_p); - long_run.clear(); - } - else - { - new_grammar[X].insert(long_run); - RHX = X; - } - while (long_run.size() > 2) - { - first = long_run[0]; - GrammarBase::Production prev_rule = long_run; - long_run.erase(long_run.begin()); - - X = RHX; - temp_p = long_run; - - RHX = check_head(new_grammar, long_run); - if (RHX == u32_t(-1)) - { - RHX = check_head(grammar->getRawProductions(), long_run); - } - if(RHX == u32_t(-1)) - { - tempStr = "X"; - std::ostringstream ss; - ss << grammar->num_generator(); - tempStr.append(ss.str()); - Set variableAttributeSet = {}; - for (unsigned i = 0; i < long_run.size(); i++) - { - GrammarBase::VariableAttribute variableAttribute = long_run[i].variableAttribute; - if ( variableAttribute != 0) - { - variableAttributeSet.insert(variableAttribute); - } - } - if ( variableAttributeSet.size() == 1) - { - tempStr += "_"; - tempStr += char(*variableAttributeSet.begin()); - } - GrammarBase::Symbol tempSym = grammar->insertNonTerminalSymbol(tempStr); - auto it = new_grammar[X].find(prev_rule); - new_grammar[X].erase(it); - new_grammar[X].insert({first, tempSym}); - new_grammar[tempSym].insert(long_run); - RHX = tempSym; - } - } - } - } - for (auto new_head : new_grammar) - { - for (auto prod : new_head.second) - { - auto it = grammar->getRawProductions()[new_head.first].find(prod); - if (it == grammar->getRawProductions()[new_head.first].end()) - { - grammar->getRawProductions()[new_head.first].insert(prod); - } - } - } -} - -///Loop through provided production based on existence of attribute of attribute variable -///and expand to productions set -///e.g Xi -> Y Zi with Xi i = 0, 1, Yi i = 0,2 -///Will get {X0 -> Y Z0, X1 -> Y Z1, X2 -> Y Z2} -void CFGNormalizer::getFilledProductions(GrammarBase::Production &prod, const NodeSet& nodeSet, CFGrammar *grammar, GrammarBase::Productions& normalProds) -{ - normalProds.clear(); - CFLFIFOWorkList worklist; - worklist.push(prod); - while( worklist.empty() == false ) - { - GrammarBase::Production currentProduction = worklist.pop(); - /// Get the first encounter variable attribute to expand - GrammarBase::VariableAttribute currentVariableAttribute = 0; - // GrammarBase::Kind baseKind; - for ( GrammarBase::Symbol &symbol : currentProduction ) - { - if ( currentVariableAttribute == 0 ) - { - currentVariableAttribute = symbol.variableAttribute; - // baseKind = symbol.kind; - } - } - if ( currentVariableAttribute == 0) - { - normalProds.insert(currentProduction); - continue; - } - //*(kindToAttriMap.find(baseKind)); - //for (auto attribute : nodeSet.second) - for (auto attribute : nodeSet) - { - GrammarBase::Production fillingProduction = currentProduction; - for ( GrammarBase::Symbol &symbol : fillingProduction ) - { - if ( symbol.variableAttribute == currentVariableAttribute) - { - symbol.attribute = attribute; - symbol.variableAttribute = 0; - } - } - /// Check whether all symbol expanded - bool continueToFill = false; - for ( GrammarBase::Symbol &symbol : fillingProduction ) - { - if ( symbol.variableAttribute != 0 ) - { - continueToFill = true; - } - } - if ( continueToFill == false) - { - normalProds.insert(fillingProduction); - } - else - { - worklist.push(fillingProduction); - } - } - } -} - -int CFGNormalizer::ebnfBracketMatch(GrammarBase::Production &prod, int i, CFGrammar *grammar) -{ - int index = i; - while (index >= 0) - { - if (grammar->kindToStr(prod[index].kind) == "(") - { - return index; - } - index--; - } - return 0; -} - -void CFGNormalizer::barReplace(CFGrammar *grammar) -{ - for (auto &symbolToProductionsPair : grammar->getRawProductions()) - { - GrammarBase::Productions productions; - //GrammarBase::Productions Originalproductions = symbolToProductionsPair.second; - for (auto ebnfProduction : symbolToProductionsPair.second) - { - size_t i = 1; - size_t j = 1; - while (i < ebnfProduction.size()) - { - if (grammar->kindToStr(ebnfProduction[i].kind) == "|") - { - GrammarBase::Production tempPro(ebnfProduction.begin()+j, ebnfProduction.begin()+i); - tempPro.insert(tempPro.begin(), symbolToProductionsPair.first ); - productions.insert(tempPro); - j = i+1; - } - i++; - } - GrammarBase::Production tempPro(ebnfProduction.begin()+j, ebnfProduction.begin()+i); - tempPro.insert(tempPro.begin(), symbolToProductionsPair.first ); - productions.insert(tempPro); - } - symbolToProductionsPair.second.clear(); - symbolToProductionsPair.second = productions; - } -} - -void CFGNormalizer::ebnfSignReplace(char sign, CFGrammar *grammar) -{ - /// Replace Sign Group With tempNonterminal 'X' - /// And load the replace in newProductions - SVF::Map newProductions; - std::string tempNonterminal = "X"; - - for (auto &symbolToProductionsPair : grammar->getRawProductions()) - { - GrammarBase::Productions productions = symbolToProductionsPair.second; - for (auto ebnfProduction : symbolToProductionsPair.second) - { - size_t i = 1; - while (i < ebnfProduction.size()) - { - s32_t signGroupStart = -1; - if (grammar->kindToStr(ebnfProduction[i].kind) == std::string(1, sign)) - { - /// If sign associate without group e.i with single symbol - assert(i != 1 && "sign in grammar associate with no symbol"); - if (grammar->kindToStr(ebnfProduction[i - 1].kind) != std::string(1, ')')) - { - signGroupStart = i - 1; - } - /// sign associate with group of symbol by brace pair - else - { - signGroupStart = ebnfBracketMatch(ebnfProduction, i, grammar); - } - std::string groupString = ""; - for (size_t j = signGroupStart; j < i; j++) - { - groupString.append(grammar->kindToStr(ebnfProduction[j].kind)); - groupString.append(" "); - } - groupString.append(grammar->kindToStr(ebnfProduction[i].kind)); - if (newProductions.find(groupString) != newProductions.end()) - { - productions.erase(ebnfProduction); - ebnfProduction.erase(ebnfProduction.begin() + signGroupStart, ebnfProduction.begin() + i + 1); - ebnfProduction.insert(ebnfProduction.begin() + signGroupStart, grammar->strToSymbol(newProductions[groupString])); - productions.insert(ebnfProduction); - } - else if ( (signGroupStart == 1) && (i == ebnfProduction.size() -1)) - { - newProductions[groupString] = grammar->kindToStr(ebnfProduction[0].kind); - productions.erase(ebnfProduction); - ebnfProduction.erase(ebnfProduction.begin() + signGroupStart, ebnfProduction.begin() + i + 1); - - } - else - { - tempNonterminal = "X"; - std::ostringstream ss; - ss << grammar->num_generator(); - tempNonterminal.append(ss.str()); - GrammarBase::Symbol tempSym = grammar->insertNonTerminalSymbol(tempNonterminal); - productions.erase(ebnfProduction); - ebnfProduction.erase(ebnfProduction.begin() + signGroupStart, ebnfProduction.begin() + i + 1); - ebnfProduction.insert(ebnfProduction.begin() + signGroupStart, tempSym); - newProductions[groupString] = tempNonterminal; - productions.insert(ebnfProduction); - } - - i = signGroupStart; - } - i++; - } - } - symbolToProductionsPair.second = productions; - } - for(auto rep: newProductions) - { - /// For Both * and ? need to insert epsilon rule - std::string new_nonterminal = rep.second; - GrammarBase::Production temp_list = {grammar->strToSymbol(new_nonterminal), grammar->strToSymbol("epsilon")}; - grammar->getRawProductions()[grammar->strToSymbol(new_nonterminal)].insert(temp_list); - /// insert second rule for '*' X -> X E for '+' X -> E - temp_list = {grammar->strToSymbol(new_nonterminal)}; - if (sign == '*' || sign == '?') - { - /// Insert Back the Group - GrammarBase::Production normalProd; - strTrans(rep.first, grammar, normalProd); - GrammarBase::Production withoutSign = {}; - if (sign == '*') - { - for (auto &word : normalProd) - { - if (word != grammar->strToSymbol("*") && word != grammar->strToSymbol("(") && word != grammar->strToSymbol(")")) - { - withoutSign.push_back(word); - } - } - withoutSign.push_back(grammar->strToSymbol(rep.second)); - } - if (sign == '?') - { - for (auto &word : normalProd) - { - if (word != grammar->strToSymbol("?") && word != grammar->strToSymbol("(") && word != grammar->strToSymbol(")")) - { - withoutSign.push_back(word); - } - } - } - temp_list.insert(temp_list.end(), withoutSign.begin(), withoutSign.end()); - } - grammar->getRawProductions()[grammar->strToSymbol(new_nonterminal)].insert(temp_list); - } -} - -void CFGNormalizer::strTrans(std::string LHS, CFGrammar *grammar, GrammarBase::Production& normalProd) -{ - std::smatch matches; - std::regex LHSReg("\\s*(.*)"); - std::string delimiter; - size_t pos; - std::string word; - std::regex_search(LHS, matches, LHSReg); - LHS = matches.str(1); - delimiter = " "; - while ((pos = LHS.find(delimiter)) != std::string::npos) - { - word = LHS.substr(0, pos); - LHS.erase(0, pos + delimiter.length()); - normalProd.push_back(grammar->strToSymbol(word)); - } - normalProd.push_back(grammar->strToSymbol(LHS)); -} - -GrammarBase::Symbol CFGNormalizer::check_head(GrammarBase::SymbolMap &grammar, GrammarBase::Production &rule) -{ - for(auto symProdPair: grammar) - { - for(auto prod: symProdPair.second) - { - if (rule == prod) - { - return symProdPair.first; - } - } - } - GrammarBase::Symbol symbol = u32_t(-1); - return symbol; -} - -/// Based on prod size to add on suitable member field of grammar -void CFGNormalizer::insertToCFLGrammar(CFGrammar *grammar, GrammarBase::Production &prod) -{ - if (prod.size() == 2) - { - if ((std::find(prod.begin(), prod.end(), grammar->strToKind("epsilon")) != prod.end())) - { - if (std::find(grammar->getEpsilonProds().begin(), grammar->getEpsilonProds().end(), prod) == grammar->getEpsilonProds().end()) - { - grammar->getEpsilonProds().insert(prod); - } - } - else - { - grammar->getSingleRHSToProds()[prod[1]].insert(prod); - } - } - if (prod.size() == 3) - { - grammar->getFirstRHSToProds()[prod[1]].insert(prod); - grammar->getSecondRHSToProds()[prod[2]].insert(prod); - } -} - -void CFGNormalizer::removeFirstSymbol(CFGrammar *grammar) -{ - // Remove First Terminal - for(auto head : grammar->getRawProductions()) - { - for(auto rule: head.second) - { - - GrammarBase::Production long_run = rule; - long_run.erase(long_run.begin()); - auto it = grammar->getRawProductions().at(head.first).find(rule); - grammar->getRawProductions().at(head.first).erase(it); - grammar->getRawProductions()[head.first].insert(long_run); - } - } -} diff --git a/svf/lib/CFL/CFGrammar.cpp b/svf/lib/CFL/CFGrammar.cpp deleted file mode 100644 index 5764bc543..000000000 --- a/svf/lib/CFL/CFGrammar.cpp +++ /dev/null @@ -1,441 +0,0 @@ -//===----- CFGrammar.cpp -- Context-free grammar --------------------------// -// -// SVF: Static Value-Flow Analysis -// -// Copyright (C) <2013-> -// - -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU Affero General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Affero General Public License for more details. - -// You should have received a copy of the GNU Affero General Public License -// along with this program. If not, see . -// -//===----------------------------------------------------------------------===// - -/* - * CFGrammar.cpp - * - * Created on: March 5, 2022 - * Author: Yulei Sui - */ - -#include "CFL/CFGrammar.h" -#include "Util/SVFUtil.h" -#include -#include -#include -#include - -using namespace SVF; - - -void GrammarBase::setRawProductions(SymbolMap& rawProductions) -{ - this->rawProductions = rawProductions; -} - -void GrammarBase::setKindToAttrsMap(const Map>& kindToAttrsMap) -{ - this->kindToAttrsMap = kindToAttrsMap; -} - -void GrammarBase::setAttributeKinds(const Set& attributeKinds) -{ - this->attributeKinds = attributeKinds; -} - -GrammarBase::Kind GrammarBase::strToKind(std::string str) const -{ - - auto tit = terminals.find(str); - if(tit!=terminals.end()) - return tit->second; - - auto nit = nonterminals.find(str); - if(nit!=nonterminals.end()) - return nit->second; - - auto sit = EBNFSigns.find(str); - if(sit!=EBNFSigns.end()) - return sit->second; - - assert(false && "kind not found!"); - abort(); -} - -GrammarBase::Symbol GrammarBase::strToSymbol(const std::string str) const -{ - Symbol symbol; - std::string attributeStr = extractAttributeStrFromSymbolStr(str); - std::string kindStr = extractKindStrFromSymbolStr(str); - symbol.kind = strToKind(kindStr); - - if ( attributeStr == "") return symbol; - - if ( (attributeStr.size() == 1) && (std::isalpha(attributeStr[attributeStr.size()-1])) ) - { - symbol.variableAttribute = (u32_t)attributeStr[attributeStr.size()-1]; - } - else - { - for( char &c : attributeStr) - { - if ( std::isdigit(c) == false ) - { - SVFUtil::errs() << SVFUtil::errMsg("\t Symbol Attribute Parse Failure :") << str - << " Attribute:" << attributeStr << " (only number or single alphabet.)"; - assert(false && "grammar loading failed!"); - } - } - symbol.attribute = std::stoi(attributeStr); - } - return symbol; -} - -std::string GrammarBase::kindToStr(Kind kind) const -{ - - std::string key = ""; - for (auto &i : terminals) - { - if (i.second == kind) - { - key = i.first; - return key; - } - } - - std::string nkey = ""; - for (auto &ni : nonterminals) - { - if (ni.second == kind) - { - nkey = ni.first; - return nkey; - } - } - - std::string signs = ""; - for (auto &i : EBNFSigns) - { - if (i.second == kind) - { - signs = i.first; - return signs; - } - } - - return ""; -} - -std::string GrammarBase::symToStrDump(Symbol sym) const -{ - Kind kind = sym.kind; - Attribute attribute = sym.attribute; - - std::string key = ""; - for (auto &i : terminals) - { - if (i.second == kind) - { - key = i.first; - if(attribute != 0) - { - key.append("_"); - key.append(std::to_string(attribute)); - } - return key; - } - } - - std::string nkey = ""; - for (auto &ni : nonterminals) - { - if (ni.second == kind) - { - nkey = ni.first; - if(attribute != 0) - { - nkey.append("_"); - nkey.append(std::to_string(attribute)); - } - return nkey; - } - } - - return ""; -} - -GrammarBase::Kind GrammarBase::insertTerminalKind(std::string kindStr) -{ - Kind kind; - if (terminals.find(kindStr) == terminals.end()) - { - kind = totalKind++; - terminals.insert({kindStr, kind}); - } - else - { - kind = strToKind(kindStr); - } - return kind; -} - -inline GrammarBase::Kind GrammarBase::insertNonterminalKind(std::string const kindStr) -{ - Kind kind; - if (nonterminals.find(kindStr) == nonterminals.end()) - { - kind = totalKind++; - nonterminals.insert({kindStr, kind}); - } - else - { - kind = strToKind(kindStr); - } - return kind; -} - -std::string GrammarBase::extractKindStrFromSymbolStr(const std::string& symbolStr) const -{ - std::string kindStr; - // symbolStr end with '_', the whole symbolStr treat as kind, not with attribute. - auto underscorePosition = symbolStr.find_last_of("_", symbolStr.size()-1); - if (underscorePosition == std::string::npos) - { - return symbolStr; - } - return symbolStr.substr(0, underscorePosition); -} - -std::string GrammarBase::extractAttributeStrFromSymbolStr(const std::string& symbolStr) const -{ - std::string attributeStr; - // symbolStr end with '_', the whole symbolStr treat as kind, not with attribute. - auto underscorePosition = symbolStr.find_last_of("_", symbolStr.size()-1); - if (underscorePosition == std::string::npos) - { - return ""; - } - return symbolStr.substr(underscorePosition+1); -} - -GrammarBase::Symbol GrammarBase::insertSymbol(std::string symbolStr) -{ - Symbol symbol; - if ((symbolStr.size() == 1) && (!isalpha(symbolStr[0]))) - { - symbol = insertEBNFSigns(symbolStr); - } - else if (isupper(symbolStr[0])) - { - symbol = insertNonTerminalSymbol(symbolStr); - } - else - { - symbol = insertTerminalSymbol(symbolStr); - } - return symbol; -} - -GrammarBase::Symbol GrammarBase::insertNonTerminalSymbol(std::string symbolStr) -{ - Symbol symbol; - std::string kindStr = extractKindStrFromSymbolStr(symbolStr); - std::string attributeStr = extractAttributeStrFromSymbolStr(symbolStr); - symbol.kind = insertNonterminalKind(kindStr); - - if ( attributeStr == "") return symbol; - - if ( (attributeStr.size() == 1) && (std::isalpha(attributeStr[attributeStr.size()-1])) ) - { - attributeKinds.insert(symbol.kind); - symbol.variableAttribute = (u32_t)attributeStr[attributeStr.size()-1]; - } - else - { - for( char &c : attributeStr) - { - if ( std::isdigit(c) == false ) - { - SVFUtil::errs() << SVFUtil::errMsg("\t Symbol Attribute Parse Failure :") << symbolStr - << " Attribute:" << attributeStr << " (only number or single alphabet.)"; - assert(false && "grammar loading failed!"); - } - } - attributeKinds.insert(symbol.kind); - symbol.attribute = std::stoi(attributeStr); - } - return symbol; -} - -GrammarBase::Symbol GrammarBase::insertTerminalSymbol(std::string symbolStr) -{ - Symbol symbol; - std::string kindStr = extractKindStrFromSymbolStr(symbolStr); - std::string attributeStr = extractAttributeStrFromSymbolStr(symbolStr); - symbol.kind = insertTerminalKind(kindStr); - - if ( attributeStr == "") return symbol; - - if ( (attributeStr.size() == 1) && (std::isalpha(attributeStr[attributeStr.size()-1])) ) - { - attributeKinds.insert(symbol.kind); - symbol.variableAttribute = (u32_t)attributeStr[attributeStr.size()-1]; - } - else - { - for( char &c : attributeStr) - { - if ( std::isdigit(c) == false ) - { - SVFUtil::errs() << SVFUtil::errMsg("\t Symbol Attribute Parse Failure :") << symbolStr - << " Attribute:" << attributeStr << " (only number or single alphabet.)"; - assert(false && "grammar loading failed!"); - } - } - attributeKinds.insert(symbol.kind); - symbol.attribute = std::stoi(attributeStr); - } - return symbol; -} - -GrammarBase::Symbol GrammarBase::insertEBNFSigns(std::string symbolStr) -{ - Symbol sign; - if (EBNFSigns.find(symbolStr) == EBNFSigns.end()) - { - sign = totalKind++; - EBNFSigns.insert({symbolStr, sign}); - } - else - { - sign = strToKind(symbolStr); - } - return sign; - -} - -void GrammarBase::insertAttribute(Kind kind, Attribute attribute) -{ - attributeKinds.insert(kind); - if (kindToAttrsMap.find(kind)!= kindToAttrsMap.end()) - { - kindToAttrsMap[kind].insert(attribute); - } - else - { - Set attrs {attribute}; - kindToAttrsMap.insert(make_pair(kind, attrs)); - } -} - -CFGrammar::CFGrammar() -{ - newTerminalSubscript = 0; -} - -void CFGrammar::dump() const -{ - dump("Normailized_Grammar.txt"); -} - -void CFGrammar::dump(std::string fileName) const -{ - std::ofstream gramFile(fileName); - gramFile << "Start Kind:\n"; - gramFile << '\t' << kindToStr(startKind) << '(' << startKind << ')' << ' ' << "\n\n"; - - gramFile << "Epsilon Production:\n"; - std::vector strV; - for (auto pro: this->epsilonProds) - { - std::stringstream ss; - for (auto sym : pro) - { - if (sym == pro[1]) - { - ss << "-> "; - } - ss << symToStrDump(sym) << '(' << sym.kind << ')' << ' '; - } - strV.insert(strV.begin(), ss.str()); - } - std::sort(strV.begin(), strV.end()); - for (auto pro: strV) - { - gramFile << '\t'; - gramFile << pro; - gramFile << '\n'; - } - gramFile << '\n'; - - gramFile << "Single Production:\n"; - strV = {}; - for (auto symProPair: singleRHSToProds) - { - for (auto pro: symProPair.second) - { - std::stringstream ss; - int i = 0; - for (auto sym : pro) - { - i++; - if (i == 2) - { - ss << "-> "; - } - ss << symToStrDump(sym) << '(' << sym.kind << ')' << ' '; - } - strV.insert(strV.begin(), ss.str()); - } - } - std::sort(strV.begin(), strV.end()); - for (auto pro: strV) - { - gramFile << '\t'; - gramFile << pro; - gramFile << '\n'; - } - gramFile << '\n'; - - gramFile << "Binary Production:\n"; - strV = {}; - for (auto symProPair: firstRHSToProds) - { - for (auto pro: symProPair.second) - { - std::stringstream ss; - int i = 0; - for (auto sym : pro) - { - i++; - - if (i == 2) - { - ss << "-> "; - } - ss << symToStrDump(sym) << '(' << sym.kind << ')' << ' '; - } - strV.insert(strV.begin(), ss.str()); - } - } - std::sort(strV.begin(), strV.end()); - for (auto pro: strV) - { - gramFile << '\t'; - gramFile << pro; - gramFile << '\n'; - } - gramFile << '\n'; - - gramFile.close(); -} diff --git a/svf/lib/CFL/CFLAlias.cpp b/svf/lib/CFL/CFLAlias.cpp deleted file mode 100644 index 4a97ba8ff..000000000 --- a/svf/lib/CFL/CFLAlias.cpp +++ /dev/null @@ -1,255 +0,0 @@ -//===----- CFLAlias.cpp -- CFL Alias Analysis Client--------------// -// -// SVF: Static Value-Flow Analysis -// -// Copyright (C) <2013-> -// - -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU Affero General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Affero General Public License for more details. - -// You should have received a copy of the GNU Affero General Public License -// along with this program. If not, see . -// -//===----------------------------------------------------------------------===// - -/* - * CFLAlias.cpp - * - * Created on: June 27 , 2022 - * Author: Pei Xu - */ - -#include "CFL/CFLAlias.h" -using namespace SVF; -using namespace SVFUtil; - -/*! - * On the fly call graph construction - * callsites is candidate indirect callsites need to be analyzed based on points-to results - * newEdges is the new indirect call edges discovered - */ -void CFLAlias::onTheFlyCallGraphSolve(const CallSiteToFunPtrMap& callsites, CallEdgeMap& newEdges) -{ - for(CallSiteToFunPtrMap::const_iterator iter = callsites.begin(), eiter = callsites.end(); iter!=eiter; ++iter) - { - const CallICFGNode* cs = iter->first; - - if (SVFUtil::getSVFCallSite(cs->getCallSite()).isVirtualCall()) - { - const SVFValue* vtbl = SVFUtil::getSVFCallSite(cs->getCallSite()).getVtablePtr(); - assert(pag->hasValueNode(vtbl)); - NodeID vtblId = pag->getValueNode(vtbl); - resolveCPPIndCalls(cs, getCFLPts(vtblId), newEdges); - } - else - resolveIndCalls(iter->first,getCFLPts(iter->second),newEdges); - } -} - -/*! - * Connect formal and actual parameters for indirect callsites - */ - -void CFLAlias::connectCaller2CalleeParams(CallSite cs, const SVFFunction* F) -{ - assert(F); - - DBOUT(DAndersen, outs() << "connect parameters from indirect callsite " << cs.getInstruction()->toString() << " to callee " << *F << "\n"); - - CallICFGNode* callBlockNode = svfir->getICFG()->getCallICFGNode(cs.getInstruction()); - RetICFGNode* retBlockNode = svfir->getICFG()->getRetICFGNode(cs.getInstruction()); - - if(SVFUtil::isHeapAllocExtFunViaRet(F) && svfir->callsiteHasRet(retBlockNode)) - { - heapAllocatorViaIndCall(cs); - } - - if (svfir->funHasRet(F) && svfir->callsiteHasRet(retBlockNode)) - { - const PAGNode* cs_return = svfir->getCallSiteRet(retBlockNode); - const PAGNode* fun_return = svfir->getFunRet(F); - if (cs_return->isPointer() && fun_return->isPointer()) - { - NodeID dstrec = cs_return->getId(); - NodeID srcret = fun_return->getId(); - addCopyEdge(srcret, dstrec); - } - else - { - DBOUT(DAndersen, outs() << "not a pointer ignored\n"); - } - } - - if (svfir->hasCallSiteArgsMap(callBlockNode) && svfir->hasFunArgsList(F)) - { - - // connect actual and formal param - const SVFIR::SVFVarList& csArgList = svfir->getCallSiteArgsList(callBlockNode); - const SVFIR::SVFVarList& funArgList = svfir->getFunArgsList(F); - //Go through the fixed parameters. - DBOUT(DPAGBuild, outs() << " args:"); - SVFIR::SVFVarList::const_iterator funArgIt = funArgList.begin(), funArgEit = funArgList.end(); - SVFIR::SVFVarList::const_iterator csArgIt = csArgList.begin(), csArgEit = csArgList.end(); - for (; funArgIt != funArgEit; ++csArgIt, ++funArgIt) - { - //Some programs (e.g. Linux kernel) leave unneeded parameters empty. - if (csArgIt == csArgEit) - { - DBOUT(DAndersen, outs() << " !! not enough args\n"); - break; - } - const PAGNode *cs_arg = *csArgIt ; - const PAGNode *fun_arg = *funArgIt; - - if (cs_arg->isPointer() && fun_arg->isPointer()) - { - DBOUT(DAndersen, outs() << "process actual parm " << cs_arg->toString() << " \n"); - NodeID srcAA = cs_arg->getId(); - NodeID dstFA = fun_arg->getId(); - addCopyEdge(srcAA, dstFA); - } - } - - //Any remaining actual args must be varargs. - if (F->isVarArg()) - { - NodeID vaF = svfir->getVarargNode(F); - DBOUT(DPAGBuild, outs() << "\n varargs:"); - for (; csArgIt != csArgEit; ++csArgIt) - { - const PAGNode *cs_arg = *csArgIt; - if (cs_arg->isPointer()) - { - NodeID vnAA = cs_arg->getId(); - addCopyEdge(vnAA,vaF); - } - } - } - if(csArgIt != csArgEit) - { - writeWrnMsg("too many args to non-vararg func."); - writeWrnMsg("(" + cs.getInstruction()->getSourceLoc() + ")"); - } - } -} - -void CFLAlias::heapAllocatorViaIndCall(CallSite cs) -{ - assert(SVFUtil::getCallee(cs) == nullptr && "not an indirect callsite?"); - RetICFGNode* retBlockNode = svfir->getICFG()->getRetICFGNode(cs.getInstruction()); - const PAGNode* cs_return = svfir->getCallSiteRet(retBlockNode); - NodeID srcret; - CallSite2DummyValPN::const_iterator it = callsite2DummyValPN.find(cs); - if(it != callsite2DummyValPN.end()) - { - srcret = it->second; - } - else - { - NodeID valNode = svfir->addDummyValNode(); - NodeID objNode = svfir->addDummyObjNode(cs.getType()); - callsite2DummyValPN.insert(std::make_pair(cs,valNode)); - graph->addCFLNode(valNode, new CFLNode(valNode)); - graph->addCFLNode(objNode, new CFLNode(objNode)); - srcret = valNode; - } - - NodeID dstrec = cs_return->getId(); - addCopyEdge(srcret, dstrec); -} - -/*! - * Update call graph for the input indirect callsites - */ -bool CFLAlias::updateCallGraph(const CallSiteToFunPtrMap& callsites) -{ - CallEdgeMap newEdges; - onTheFlyCallGraphSolve(callsites,newEdges); - for(CallEdgeMap::iterator it = newEdges.begin(), eit = newEdges.end(); it!=eit; ++it ) - { - CallSite cs = SVFUtil::getSVFCallSite(it->first->getCallSite()); - for(FunctionSet::iterator cit = it->second.begin(), ecit = it->second.end(); cit!=ecit; ++cit) - { - connectCaller2CalleeParams(cs,*cit); - } - } - - return (!solver->isWorklistEmpty()); -} - -void CFLAlias::initialize() -{ - stat = new CFLStat(this); - - // Parameter Checking - checkParameter(); - - // Build CFL Grammar - buildCFLGrammar(); - - // Build CFL Graph - buildCFLGraph(); - - // Normalize CFL Grammar - normalizeCFLGrammar(); - - // Initialize solver - initializeSolver(); -} - -void CFLAlias::initializeSolver() -{ - solver = new CFLSolver(graph, grammar); -} - -void CFLAlias::finalize() -{ - numOfChecks = solver->numOfChecks; - - if(Options::PrintCFL() == true) - { - if (Options::CFLGraph().empty()) - svfir->dump("IR"); - grammar->dump("Grammar"); - graph->dump("CFLGraph"); - } - if (Options::CFLGraph().empty()) - PointerAnalysis::finalize(); -} - -void CFLAlias::solve() -{ - // Start solving - double start = stat->getClk(true); - - solver->solve(); - if (Options::CFLGraph().empty()) - { - while (updateCallGraph(svfir->getIndirectCallsites())) - { - numOfIteration++; - solver->solve(); - } - } // Only cflgraph built from bc could reanalyze by update call graph - - double end = stat->getClk(true); - timeOfSolving += (end - start) / TIMEINTERVAL; -} - -void POCRAlias::initializeSolver() -{ - solver = new POCRSolver(graph, grammar); -} - -void POCRHybrid::initializeSolver() -{ - solver = new POCRHybridSolver(graph, grammar); -} diff --git a/svf/lib/CFL/CFLBase.cpp b/svf/lib/CFL/CFLBase.cpp deleted file mode 100644 index 70f634b9f..000000000 --- a/svf/lib/CFL/CFLBase.cpp +++ /dev/null @@ -1,160 +0,0 @@ -//===----- CFLBase.cpp -- CFL Analysis Client Base--------------// -// -// SVF: Static Value-Flow Analysis -// -// Copyright (C) <2013-> -// - -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU Affero General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Affero General Public License for more details. - -// You should have received a copy of the GNU Affero General Public License -// along with this program. If not, see . -// -//===----------------------------------------------------------------------===// - -/* - * CFLBase.cpp - * - * Created on: Oct 13, 2022 - * Author: Pei Xu - */ - - -#include "CFL/CFLBase.h" - -namespace SVF -{ - -class CFLStat; - -double CFLBase::timeOfBuildCFLGrammar = 0; -double CFLBase::timeOfNormalizeGrammar = 0; -double CFLBase::timeOfBuildCFLGraph = 0; -double CFLBase::timeOfSolving = 0; -double CFLBase::numOfTerminalEdges = 0; -double CFLBase::numOfTemporaryNonterminalEdges = 0; -double CFLBase::numOfNonterminalEdges = 0; -double CFLBase::numOfStartEdges = 0; -double CFLBase::numOfIteration = 1; -double CFLBase::numOfChecks = 1; - -void CFLBase::checkParameter() -{ - // Check for valid grammar file before parsing other options - std::string filename = Options::GrammarFilename(); - bool pagfile = (filename.rfind("PAGGrammar.txt") == filename.length() - std::string("PAGGrammar.txt").length()); - bool pegfile = (filename.rfind("PEGGrammar.txt") == filename.length() - std::string("PEGGrammar.txt").length()); - bool vfgfile = (filename.rfind("VFGGrammar.txt") == filename.length() - std::string("VFGGrammar.txt").length()); - if (!Options::Customized() && !(pagfile || pegfile || vfgfile)) - { - SVFUtil::errs() << "Invalid alias grammar file: " << Options::GrammarFilename() << "\n" - << "Please use a file that ends with either 'CFGrammar.txt' or 'PEGGrammar.txt', " - << "or use the -customized flag to allow custom grammar files.\n"; - assert(false && "grammar loading failed!"); // exit with error - } -} - -void CFLBase::buildCFLGrammar() -{ - // Start building grammar - double start = stat->getClk(true); - - GrammarBuilder grammarBuilder = GrammarBuilder(Options::GrammarFilename()); - grammarBase = grammarBuilder.build(); - - // Get time of build grammar - double end = stat->getClk(true); - timeOfBuildCFLGrammar += (end - start) / TIMEINTERVAL; -} - -void CFLBase::buildCFLGraph() -{ - // Start building CFLGraph - double start = stat->getClk(true); - - AliasCFLGraphBuilder cflGraphBuilder = AliasCFLGraphBuilder(); - if (Options::CFLGraph().empty()) // built from svfir - { - PointerAnalysis::initialize(); - ConstraintGraph *consCG = new ConstraintGraph(svfir); - if (Options::PEGTransfer()) - graph = cflGraphBuilder.buildBiPEGgraph(consCG, grammarBase->getStartKind(), grammarBase, svfir); - else - graph = cflGraphBuilder.buildBigraph(consCG, grammarBase->getStartKind(), grammarBase); - delete consCG; - } - else - graph = cflGraphBuilder.build(Options::CFLGraph(), grammarBase); - // Check CFL Graph and Grammar are accordance with grammar - CFLGramGraphChecker cflChecker = CFLGramGraphChecker(); - cflChecker.check(grammarBase, &cflGraphBuilder, graph); - - // Get time of build graph - double end = stat->getClk(true); - timeOfBuildCFLGraph += (end - start) / TIMEINTERVAL; -} - -void CFLBase::normalizeCFLGrammar() -{ - // Start normalize grammar - double start = stat->getClk(true); - - CFGNormalizer normalizer = CFGNormalizer(); - grammar = normalizer.normalize(grammarBase); - - // Get time of normalize grammar - double end = stat->getClk(true); - timeOfNormalizeGrammar += (end - start) / TIMEINTERVAL; -} - -void CFLBase::solve() -{ - // Start solving - double start = stat->getClk(true); - - solver->solve(); - - double end = stat->getClk(true); - timeOfSolving += (end - start) / TIMEINTERVAL; -} - -void CFLBase::finalize() -{ - numOfChecks = solver->numOfChecks; - - BVDataPTAImpl::finalize(); -} - -void CFLBase::analyze() -{ - initialize(); - - solve(); - - finalize(); -} - -CFLGraph* CFLBase::getCFLGraph() -{ - return graph; -} - -void CFLBase::countSumEdges() -{ - numOfStartEdges = 0; - for(auto it = getCFLGraph()->getCFLEdges().begin(); it != getCFLGraph()->getCFLEdges().end(); it++ ) - { - if ((*it)->getEdgeKind() == grammar->getStartKind()) - numOfStartEdges++; - } -} - -} // End namespace SVF diff --git a/svf/lib/CFL/CFLGraphBuilder.cpp b/svf/lib/CFL/CFLGraphBuilder.cpp deleted file mode 100644 index 7774f8188..000000000 --- a/svf/lib/CFL/CFLGraphBuilder.cpp +++ /dev/null @@ -1,546 +0,0 @@ -//===----- CFLGraphBuilder.h -- CFL Graph Builder--------------// -// -// SVF: Static Value-Flow Analysis -// -// Copyright (C) <2013-> -// - -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU Affero General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Affero General Public License for more details. - -// You should have received a copy of the GNU Affero General Public License -// along with this program. If not, see . -// -//===----------------------------------------------------------------------===// - -/* - * CFLGraphBuilder.h - * - * Created on: May 22, 2022 - * Author: Pei Xu - */ - -#include "CFL/CFLGraphBuilder.h" -#include "Util/Options.h" -#include "SVFIR/SVFValue.h" - -namespace SVF -{ -/// Add attribute to kindToAttribute Map -void CFLGraphBuilder::addAttribute(CFGrammar::Kind kind, CFGrammar::Attribute attribute) -{ - if (kindToAttrsMap.find(kind) == kindToAttrsMap.end()) - { - Set attrs{attribute}; - kindToAttrsMap.insert(make_pair(kind, attrs)); - } - else - { - if (kindToAttrsMap[kind].find(attribute) == kindToAttrsMap[kind].end()) - { - kindToAttrsMap[kind].insert(attribute); - } - } -} - -/// build label and kind connect from the grammar -void CFLGraphBuilder::buildlabelToKindMap(GrammarBase *grammar) -{ - for(auto pairV : grammar->getTerminals()) - { - if(labelToKindMap.find(pairV.first) == labelToKindMap.end()) - { - labelToKindMap.insert(pairV); - } - if(kindToLabelMap.find(pairV.second) == kindToLabelMap.end()) - { - kindToLabelMap.insert(make_pair(pairV.second, pairV.first)); - } - } - - for(auto pairV : grammar->getNonterminals()) - { - if(labelToKindMap.find(pairV.first) == labelToKindMap.end()) - { - labelToKindMap.insert(pairV); - } - if(kindToLabelMap.find(pairV.second) == kindToLabelMap.end()) - { - kindToLabelMap.insert(make_pair(pairV.second, pairV.first)); - } - } -} - -/// add src and dst node from file -CFLNode* CFLGraphBuilder::addGNode(u32_t NodeID) -{ - CFLNode* cflNode; - if (cflGraph->hasGNode(NodeID)==false) - { - cflNode = new CFLNode(NodeID); - cflGraph->addCFLNode(NodeID, cflNode); - } - else - { - cflNode = cflGraph->getGNode(NodeID); - } - return cflNode; -} - - -/// Method to build a bidirectional CFL graph by copying nodes and edges -/// from any graph inherited from GenericGraph -template -CFLGraph* CFLGraphBuilder::build(GenericGraph* graph, GrammarBase *grammar, BuildDirection direction) -{ - cflGraph = new CFLGraph(grammar->getStartKind()); - // buildlabelToKindMap(grammar); - for(auto it = graph->begin(); it!= graph->end(); it++) - { - CFLNode* node = new CFLNode((*it).first); - cflGraph->addCFLNode((*it).first, node); - } - for(auto it = graph->begin(); it!= graph->end(); it++) - { - N* node = (*it).second; - for(E* edge : node->getOutEdges()) - { - CFGrammar::Kind edgeLabel = edge->getEdgeKind(); - cflGraph->addCFLEdge(cflGraph->getGNode(edge->getSrcID()), cflGraph->getGNode(edge->getDstID()), edgeLabel); - if (direction == BuildDirection::bidirection) - { - std::string label = grammar->kindToStr(edge); - label.append("bar"); - cflGraph->addCFLEdge(cflGraph->getGNode(edge->getDstID()), cflGraph->getGNode(edge->getSrcID()), grammar->strToKind(label)); - } - } - } - return cflGraph; -} - -CFLGraph* CFLGraphBuilder::build(std::string fileName, GrammarBase *grammar, BuildDirection direction) -{ - bool isDot = (fileName.rfind(".dot") == fileName.length() - std::string(".dot").length()); - if (isDot) - return buildFromDot(fileName, grammar, direction); - - bool isJson = (fileName.rfind(".json") == fileName.length() - std::string(".json").length()); - if (isJson) - return buildFromJson(fileName, grammar, direction); - - return buildFromText(fileName, grammar, direction); -} - -//// Build graph from text file -CFLGraph* CFLGraphBuilder::buildFromText(std::string fileName, GrammarBase *grammar, BuildDirection direction) -{ - buildlabelToKindMap(grammar); - cflGraph = new CFLGraph(grammar->getStartKind()); - - std::cout << "Building CFL Graph from text file: " << fileName << "..\n"; - std::string lineString; - std::ifstream inputFile(fileName); - - if (!inputFile.is_open()) - { - SVFUtil::errs() << "Error opening " << fileName << std::endl; - abort(); - } - - std::string line; - current = labelToKindMap.size(); - u32_t lineNum = 0 ; - - while (getline(inputFile, line)) - { - std::vector vec = SVFUtil::split(line, '\t'); - if (vec.empty()) - continue; - lineNum += 1; - NodeID srcID = std::stoi(vec[0]); - NodeID dstID = std::stoi(vec[1]); - CFLNode *src = addGNode(srcID); - CFLNode *dst = addGNode(dstID); - std::string label = vec[2]; - if (labelToKindMap.find(label) != labelToKindMap.end()) - cflGraph->addCFLEdge(src, dst, labelToKindMap[label]); - else - { - if(Options::FlexSymMap() == true) - { - labelToKindMap.insert({label, current++}); - cflGraph->addCFLEdge(src, dst, labelToKindMap[label]); - } - else - { - std::string msg = "In line " + std::to_string(lineNum) + - " sym can not find in grammar, please correct the input dot or set --flexsymmap."; - SVFUtil::errMsg(msg); - std::cout << msg; - abort(); - } - } - } - - inputFile.close(); - return cflGraph; -} - -CFLGraph * CFLGraphBuilder::buildFromDot(std::string fileName, GrammarBase *grammar, BuildDirection direction) -{ - buildlabelToKindMap(grammar); - cflGraph = new CFLGraph(grammar->getStartKind()); - std::string lineString; - std::ifstream inputFile(fileName); - std::cout << "Building CFL Graph from dot file: " << fileName << "..\n"; - std::regex reg("Node(\\w+)\\s*->\\s*Node(\\w+)\\s*\\[.*label=(.*)\\]"); - std::cout << std::boolalpha; - u32_t lineNum = 0 ; - current = labelToKindMap.size(); - - while (getline(inputFile, lineString)) - { - lineNum += 1; - std::smatch matches; - if (std::regex_search(lineString, matches, reg)) - { - u32_t srcID = std::stoul(matches.str(1), nullptr, 16); - u32_t dstID = std::stoul(matches.str(2), nullptr, 16); - std::string label = matches.str(3); - CFLNode *src = addGNode(srcID); - CFLNode *dst = addGNode(dstID); - if (labelToKindMap.find(label) != labelToKindMap.end()) - cflGraph->addCFLEdge(src, dst, labelToKindMap[label]); - else - { - if(Options::FlexSymMap() == true) - { - labelToKindMap.insert({label, current++}); - cflGraph->addCFLEdge(src, dst, labelToKindMap[label]); - } - else - { - std::string msg = "In line " + std::to_string(lineNum) + - " sym can not find in grammar, please correct the input dot or set --flexsymmap."; - SVFUtil::errMsg(msg); - std::cout << msg; - abort(); - } - } - } - } - inputFile.close(); - return cflGraph; -} - -//// Build graph from json file -CFLGraph* CFLGraphBuilder::buildFromJson(std::string fileName, GrammarBase *grammar, BuildDirection direction) -{ - cflGraph = new CFLGraph(grammar->getStartKind()); - return cflGraph; -} - - -CFLGraph* AliasCFLGraphBuilder::buildBigraph(ConstraintGraph *graph, Kind startKind, GrammarBase *grammar) -{ - cflGraph = new CFLGraph(startKind); - - buildlabelToKindMap(grammar); - for(auto it = graph->begin(); it!= graph->end(); it++) - { - CFLNode* node = new CFLNode((*it).first); - cflGraph->addCFLNode((*it).first, node); - } - for(auto it = graph->begin(); it!= graph->end(); it++) - { - ConstraintNode* node = (*it).second; - for(ConstraintEdge* edge : node->getOutEdges()) - { - CFGrammar::Kind edgeLabel = edge->getEdgeKind(); - // Need to get the offset from the Const Edge - // The offset present edge is only from Normal Gep CG at moment - if(NormalGepCGEdge::classof(edge)) - { - NormalGepCGEdge *nGepEdge = SVFUtil::dyn_cast(edge); - CFGrammar::Attribute attr = nGepEdge->getConstantFieldIdx(); - addAttribute(edgeLabel, attr); - edgeLabel = CFGrammar::getAttributedKind(attr, edgeLabel); - cflGraph->addCFLEdge(cflGraph->getGNode(edge->getSrcID()), cflGraph->getGNode(edge->getDstID()), edgeLabel); - std::string label = kindToLabelMap[edge->getEdgeKind()]; - label.append("bar"); // for example Gep_i should be Gepbar_i, not Gep_ibar - cflGraph->addCFLEdge(cflGraph->getGNode(edge->getDstID()), cflGraph->getGNode(edge->getSrcID()), CFGrammar::getAttributedKind(attr, labelToKindMap[label])); - addAttribute(labelToKindMap[label], attr); - } - else - { - cflGraph->addCFLEdge(cflGraph->getGNode(edge->getSrcID()), cflGraph->getGNode(edge->getDstID()), edgeLabel); - std::string label = kindToLabelMap[edge->getEdgeKind()]; - label.append("bar"); - cflGraph->addCFLEdge(cflGraph->getGNode(edge->getDstID()), cflGraph->getGNode(edge->getSrcID()), labelToKindMap[label]); - } - } - } - return cflGraph; -} - -CFLGraph* AliasCFLGraphBuilder::buildBiPEGgraph(ConstraintGraph *graph, Kind startKind, GrammarBase *grammar, SVFIR* pag) -{ - cflGraph = new CFLGraph(startKind); - - buildlabelToKindMap(grammar); - for(auto it = graph->begin(); it!= graph->end(); it++) - { - CFLNode* node = new CFLNode((*it).first); - cflGraph->addCFLNode((*it).first, node); - } - for(auto it = graph->begin(); it!= graph->end(); it++) - { - ConstraintNode* node = (*it).second; - for(ConstraintEdge* edge : node->getOutEdges()) - { - /// Process Store - if (edge->getEdgeKind() == ConstraintEdge::Store) - { - if (pag->isNullPtr(edge->getSrcID())) - continue; - /// Check Dst of Store Dereference Node - ConstraintNode* Dst = edge->getDstNode(); - ConstraintNode* DerefNode = nullptr; - CFLNode* CFLDerefNode = nullptr; - for (ConstraintEdge* DstInEdge : Dst->getInEdges()) - { - if (DstInEdge->getEdgeKind() == ConstraintEdge::Addr) - { - DerefNode = DstInEdge->getSrcNode(); - CFLDerefNode = cflGraph->getGNode(DstInEdge->getSrcID()); - break; - } - } - if (DerefNode == nullptr) - { - - NodeID refId = pag->addDummyValNode(); - CFLDerefNode = new CFLNode(refId); - cflGraph->addCFLNode(refId, CFLDerefNode); - /// Add Addr Edge - cflGraph->addCFLEdge(CFLDerefNode, cflGraph->getGNode(edge->getDstID()), ConstraintEdge::Addr); - std::string label = kindToLabelMap[ConstraintEdge::Addr]; - label.append("bar"); - cflGraph->addCFLEdge(cflGraph->getGNode(edge->getDstID()), CFLDerefNode, labelToKindMap[label]); - } - /// Add Copy Edge - cflGraph->addCFLEdge(cflGraph->getGNode(edge->getSrcID()), cflGraph->getGNode(CFLDerefNode->getId()), ConstraintEdge::Copy); - std::string label = kindToLabelMap[ConstraintEdge::Copy]; - label.append("bar"); - cflGraph->addCFLEdge(cflGraph->getGNode(CFLDerefNode->getId()), cflGraph->getGNode(edge->getSrcID()), labelToKindMap[label]); - } - /// Process Load - else if ( edge->getEdgeKind() == ConstraintEdge::Load) - { - /// Check Src of Load Dereference Node - ConstraintNode* Src = edge->getSrcNode(); - ConstraintNode* DerefNode = nullptr; - CFLNode* CFLDerefNode = nullptr; - for (ConstraintEdge* SrcInEdge : Src->getInEdges()) - { - if (SrcInEdge->getEdgeKind() == ConstraintEdge::Addr) - { - DerefNode = SrcInEdge->getSrcNode(); - CFLDerefNode = cflGraph->getGNode(SrcInEdge->getSrcID()); - } - } - if (DerefNode == nullptr) - { - NodeID refId = pag->addDummyValNode(); - CFLDerefNode = new CFLNode(refId); - cflGraph->addCFLNode(refId, CFLDerefNode); - /// Add Addr Edge - cflGraph->addCFLEdge(CFLDerefNode, cflGraph->getGNode(edge->getSrcID()), ConstraintEdge::Addr); - std::string label = kindToLabelMap[ConstraintEdge::Addr]; - label.append("bar"); - cflGraph->addCFLEdge(cflGraph->getGNode(edge->getSrcID()), CFLDerefNode, labelToKindMap[label]); - } - /// Add Copy Edge - cflGraph->addCFLEdge(cflGraph->getGNode(CFLDerefNode->getId()), cflGraph->getGNode(edge->getDstID()), ConstraintEdge::Copy); - std::string label = kindToLabelMap[ConstraintEdge::Copy]; - label.append("bar"); - cflGraph->addCFLEdge(cflGraph->getGNode(edge->getDstID()), cflGraph->getGNode(CFLDerefNode->getId()), labelToKindMap[label]); - } - else if ( edge->getEdgeKind() == ConstraintEdge::VariantGep) - { - /// Handle VGep normalize to Normal Gep by connecting all geps' srcs to vgep dest - /// Example: In Test Case: Ctest field-ptr-arith-varIdx.c.bc - /// BFS Search the 8 LEVEL up to find the ValueNode, and the number of level search is arbitrary - /// the more the level search the more valueNode and the Vgep Dst will possibly connect - connectVGep(cflGraph, graph, edge->getSrcNode(), edge->getDstNode(), 8, pag); - } - else - { - CFGrammar::Kind edgeLabel = edge->getEdgeKind(); - // Need to get the offset from the Const Edge - // The offset present edge is only from Normal Gep CG at moment - if(NormalGepCGEdge::classof(edge)) - { - NormalGepCGEdge *nGepEdge = SVFUtil::dyn_cast(edge); - CFGrammar::Attribute attr = nGepEdge->getConstantFieldIdx(); - addAttribute(edgeLabel, attr); - edgeLabel = CFGrammar::getAttributedKind(attr, edgeLabel); - cflGraph->addCFLEdge(cflGraph->getGNode(edge->getSrcID()), cflGraph->getGNode(edge->getDstID()), edgeLabel); - std::string label = kindToLabelMap[edge->getEdgeKind()]; - label.append("bar"); // for example Gep_i should be Gepbar_i, not Gep_ibar - cflGraph->addCFLEdge(cflGraph->getGNode(edge->getDstID()), cflGraph->getGNode(edge->getSrcID()), CFGrammar::getAttributedKind(attr, labelToKindMap[label])); - addAttribute(labelToKindMap[label], attr); - } - else - { - cflGraph->addCFLEdge(cflGraph->getGNode(edge->getSrcID()), cflGraph->getGNode(edge->getDstID()), edgeLabel); - std::string label = kindToLabelMap[edge->getEdgeKind()]; - label.append("bar"); - cflGraph->addCFLEdge(cflGraph->getGNode(edge->getDstID()), cflGraph->getGNode(edge->getSrcID()), labelToKindMap[label]); - } - } - } - } - return cflGraph; -} - -void AliasCFLGraphBuilder::AliasCFLGraphBuilder::connectVGep(CFLGraph *cflGraph, ConstraintGraph *graph, ConstraintNode *src, ConstraintNode *dst, u32_t level, SVFIR* pag) -{ - if (level == 0) return; - level -= 1; - for (auto eit = src->getAddrInEdges().begin(); eit != src->getAddrInEdges().end(); eit++) - { - const MemObj* mem = pag->getBaseObj((*eit)->getSrcID()); - for (u32_t maxField = 0 ; maxField < mem->getNumOfElements(); maxField++) - { - addBiGepCFLEdge(cflGraph, (*eit)->getDstNode(), dst, maxField); - } - } - for (auto eit = src->getInEdges().begin(); eit != src->getInEdges().end() && level != 0; eit++) - { - connectVGep(cflGraph, graph, (*eit)->getSrcNode(), dst, level, pag); - } - return; -} - -void AliasCFLGraphBuilder::addBiCFLEdge(CFLGraph *cflGraph, ConstraintNode* src, ConstraintNode* dst, CFGrammar::Kind kind) -{ - cflGraph->addCFLEdge(cflGraph->getGNode(src->getId()), cflGraph->getGNode(dst->getId()), kind); - std::string label = kindToLabelMap[kind]; - label.append("bar"); - cflGraph->addCFLEdge(cflGraph->getGNode(dst->getId()), cflGraph->getGNode(src->getId()), labelToKindMap[label]); - return; -} - -void AliasCFLGraphBuilder::addBiGepCFLEdge(CFLGraph *cflGraph, ConstraintNode* src, ConstraintNode* dst, CFGrammar::Attribute attri) -{ - CFLEdge::GEdgeFlag edgeLabel = CFGrammar::getAttributedKind(attri, ConstraintEdge::NormalGep); - cflGraph->addCFLEdge(cflGraph->getGNode(src->getId()), cflGraph->getGNode(dst->getId()), edgeLabel); - std::string label = kindToLabelMap[ConstraintEdge::NormalGep]; - label.append("bar"); - cflGraph->addCFLEdge(cflGraph->getGNode(dst->getId()), cflGraph->getGNode(src->getId()), CFGrammar::getAttributedKind(attri, labelToKindMap[label])); - return; -} - -CFLGraph* VFCFLGraphBuilder::buildBigraph(SVFG *graph, Kind startKind, GrammarBase *grammar) -{ - cflGraph = new CFLGraph(startKind); - - buildlabelToKindMap(grammar); - for(auto it = graph->begin(); it!= graph->end(); it++) - { - CFLNode* node = new CFLNode((*it).first); - cflGraph->addCFLNode((*it).first, node); - } - for(auto it = graph->begin(); it!= graph->end(); it++) - { - VFGNode* node = (*it).second; - for(VFGEdge* edge : node->getOutEdges()) - { - CFGrammar::Kind edgeLabel; - // Get 'a' edge : IntraDirectVF || IntraIndirectVF - if (edge->getEdgeKind() == VFGEdge::IntraDirectVF || edge->getEdgeKind() == VFGEdge::IntraIndirectVF || edge->getEdgeKind() == VFGEdge::TheadMHPIndirectVF ) - { - edgeLabel = 0; - cflGraph->addCFLEdge(cflGraph->getGNode(edge->getSrcID()), cflGraph->getGNode(edge->getDstID()), edgeLabel); - cflGraph->addCFLEdge(cflGraph->getGNode(edge->getSrcID()), cflGraph->getGNode(edge->getDstID()), edgeLabel); - std::string label = kindToLabelMap[edge->getEdgeKind()]; - label.append("bar"); - cflGraph->addCFLEdge(cflGraph->getGNode(edge->getDstID()), cflGraph->getGNode(edge->getSrcID()), labelToKindMap[label]); - } - // Get 'call' edge : CallDirVF || CallIndVF - else if ( edge->getEdgeKind() == VFGEdge::CallDirVF ) - { - edgeLabel = 1; - CallDirSVFGEdge *attributedEdge = SVFUtil::dyn_cast(edge); - CFGrammar::Attribute attr = attributedEdge->getCallSiteId(); - addAttribute(edgeLabel, attr); - edgeLabel = CFGrammar::getAttributedKind(attr, edgeLabel); - cflGraph->addCFLEdge(cflGraph->getGNode(edge->getSrcID()), cflGraph->getGNode(edge->getDstID()), edgeLabel); - std::string label = kindToLabelMap[edgeLabel]; - label.append("bar"); // for example Gep_i should be Gepbar_i, not Gep_ibar - cflGraph->addCFLEdge(cflGraph->getGNode(edge->getDstID()), cflGraph->getGNode(edge->getSrcID()), CFGrammar::getAttributedKind(attr, labelToKindMap[label])); - addAttribute(labelToKindMap[label], attr); - } - // Get 'call' edge : CallIndVF - else if ( edge->getEdgeKind() == VFGEdge::CallIndVF ) - { - edgeLabel = 1; - CallIndSVFGEdge *attributedEdge = SVFUtil::dyn_cast(edge); - CFGrammar::Attribute attr = attributedEdge->getCallSiteId(); - addAttribute(edgeLabel, attr); - edgeLabel = CFGrammar::getAttributedKind(attr, edgeLabel); - cflGraph->addCFLEdge(cflGraph->getGNode(edge->getSrcID()), cflGraph->getGNode(edge->getDstID()), edgeLabel); - std::string label = kindToLabelMap[edgeLabel]; - label.append("bar"); // for example Gep_i should be Gepbar_i, not Gep_ibar - cflGraph->addCFLEdge(cflGraph->getGNode(edge->getDstID()), cflGraph->getGNode(edge->getSrcID()), CFGrammar::getAttributedKind(attr, labelToKindMap[label])); - addAttribute(labelToKindMap[label], attr); - } - // Get 'ret' edge : RetDirVF - else if ( edge->getEdgeKind() == VFGEdge::RetDirVF ) - { - edgeLabel = 2; - RetDirSVFGEdge *attributedEdge = SVFUtil::dyn_cast(edge); - CFGrammar::Attribute attr = attributedEdge->getCallSiteId(); - addAttribute(edgeLabel, attr); - edgeLabel = CFGrammar::getAttributedKind(attr, edgeLabel); - cflGraph->addCFLEdge(cflGraph->getGNode(edge->getSrcID()), cflGraph->getGNode(edge->getDstID()), edgeLabel); - std::string label = kindToLabelMap[edgeLabel]; - label.append("bar"); // for example Gep_i should be Gepbar_i, not Gep_ibar - cflGraph->addCFLEdge(cflGraph->getGNode(edge->getDstID()), cflGraph->getGNode(edge->getSrcID()), CFGrammar::getAttributedKind(attr, labelToKindMap[label])); - addAttribute(labelToKindMap[label], attr); - } - // Get 'ret' edge : RetIndVF - else if ( edge->getEdgeKind() == VFGEdge::RetIndVF ) - { - edgeLabel = 2; - RetIndSVFGEdge *attributedEdge = SVFUtil::dyn_cast(edge); - CFGrammar::Attribute attr = attributedEdge->getCallSiteId(); - addAttribute(edgeLabel, attr); - edgeLabel = CFGrammar::getAttributedKind(attr, edgeLabel); - cflGraph->addCFLEdge(cflGraph->getGNode(edge->getSrcID()), cflGraph->getGNode(edge->getDstID()), edgeLabel); - std::string label = kindToLabelMap[edgeLabel]; - label.append("bar"); // for example Gep_i should be Gepbar_i, not Gep_ibar - cflGraph->addCFLEdge(cflGraph->getGNode(edge->getDstID()), cflGraph->getGNode(edge->getSrcID()), CFGrammar::getAttributedKind(attr, labelToKindMap[label])); - addAttribute(labelToKindMap[label], attr); - } - } - } - return cflGraph; -} - -CFLGraph* VFCFLGraphBuilder::buildBiPEGgraph(ConstraintGraph *graph, Kind startKind, GrammarBase *grammar, SVFIR* pag) -{ - CFLGraph *cflGraph = new CFLGraph(startKind); - return cflGraph; -} - - -} // end of SVF namespace diff --git a/svf/lib/CFL/CFLSVFGBuilder.cpp b/svf/lib/CFL/CFLSVFGBuilder.cpp deleted file mode 100644 index c52231d3d..000000000 --- a/svf/lib/CFL/CFLSVFGBuilder.cpp +++ /dev/null @@ -1,100 +0,0 @@ -//===- CFLSVFGBuilder.cpp -- Building SVFG for CFL--------------------------// -// -// SVF: Static Value-Flow Analysis -// -// Copyright (C) <2013-> -// - -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU Affero General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Affero General Public License for more details. - -// You should have received a copy of the GNU Affero General Public License -// along with this program. If not, see . -// -//===----------------------------------------------------------------------===// - -// -// Created by Xiao on 30/12/23. -// -#include "MemoryModel/PointerAnalysisImpl.h" -#include "Graphs/SVFG.h" -#include "Util/Options.h" -#include "CFL/CFLSVFGBuilder.h" - - -using namespace SVF; -using namespace SVFUtil; - -void CFLSVFGBuilder::buildSVFG() -{ - - MemSSA* mssa = svfg->getMSSA(); - svfg->buildSVFG(); - BVDataPTAImpl* pta = mssa->getPTA(); - DBOUT(DGENERAL, outs() << pasMsg("\tCollect Global Variables\n")); - - collectGlobals(pta); - - DBOUT(DGENERAL, outs() << pasMsg("\tRemove Dereference Direct SVFG Edge\n")); - - rmDerefDirSVFGEdges(pta); - - rmIncomingEdgeForSUStore(pta); - - DBOUT(DGENERAL, outs() << pasMsg("\tAdd Sink SVFG Nodes\n")); - - AddExtActualParmSVFGNodes(pta->getPTACallGraph()); - - if(pta->printStat()) - svfg->performStat(); -} - - -/*! - * Remove Incoming Edge for strong-update (SU) store instruction - * Because the SU node does not receive indirect value - * - * e.g., - * L1: *p = O; (singleton) - * L2: *p = _; (SU here) - * We should remove the indirect value flow L1 -> L2 - * Because the points-to set of O from L1 does not pass to that after L2 - */ -void CFLSVFGBuilder::rmIncomingEdgeForSUStore(BVDataPTAImpl* pta) -{ - - for(SVFG::iterator it = svfg->begin(), eit = svfg->end(); it!=eit; ++it) - { - const SVFGNode* node = it->second; - - if(const StoreSVFGNode* stmtNode = SVFUtil::dyn_cast(node)) - { - if(SVFUtil::isa(stmtNode->getPAGEdge())) - { - NodeID singleton; - if(isStrongUpdate(node, singleton, pta)) - { - Set toRemove; - for (SVFGNode::const_iterator it2 = node->InEdgeBegin(), eit2 = node->InEdgeEnd(); it2 != eit2; ++it2) - { - if ((*it2)->isIndirectVFGEdge()) - { - toRemove.insert(*it2); - } - } - for (SVFGEdge* edge: toRemove) - { - svfg->removeSVFGEdge(edge); - } - } - } - } - } -} \ No newline at end of file diff --git a/svf/lib/CFL/CFLSolver.cpp b/svf/lib/CFL/CFLSolver.cpp deleted file mode 100644 index c9ba1355d..000000000 --- a/svf/lib/CFL/CFLSolver.cpp +++ /dev/null @@ -1,381 +0,0 @@ -//===----- CFLSolver.cpp -- Context-free language reachability solver--------------// -// -// SVF: Static Value-Flow Analysis -// -// Copyright (C) <2013-> -// - -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU Affero General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Affero General Public License for more details. - -// You should have received a copy of the GNU Affero General Public License -// along with this program. If not, see . -// -//===----------------------------------------------------------------------===// - -/* - * CFLSolver.cpp - * - * Created on: March 5, 2022 - * Author: Yulei Sui - */ - -#include "CFL/CFLSolver.h" - -using namespace SVF; - -double CFLSolver::numOfChecks = 0; - -void CFLSolver::initialize() -{ - for(auto it = graph->begin(); it!= graph->end(); it++) - { - for(const CFLEdge* edge : (*it).second->getOutEdges()) - { - pushIntoWorklist(edge); - } - } - - /// Foreach production X -> epsilon - /// add X(i,i) if not exist to E and to worklist - for(const Production& prod : grammar->getEpsilonProds()) - { - for(auto it = graph->begin(); it!= graph->end(); it++) - { - Symbol X = grammar->getLHSSymbol(prod); - CFLNode* i = (*it).second; - if(const CFLEdge* edge = graph->addCFLEdge(i, i, X)) - { - pushIntoWorklist(edge); - } - } - } -} - -void CFLSolver::processCFLEdge(const CFLEdge* Y_edge) -{ - CFLNode* i = Y_edge->getSrcNode(); - CFLNode* j = Y_edge->getDstNode(); - - /// For each production X -> Y - /// add X(i,j) if not exist to E and to worklist - Symbol Y = Y_edge->getEdgeKind(); - if (grammar->hasProdsFromSingleRHS(Y)) - for(const Production& prod : grammar->getProdsFromSingleRHS(Y)) - { - Symbol X = grammar->getLHSSymbol(prod); - numOfChecks++; - if(const CFLEdge* newEdge = graph->addCFLEdge(i, j, X)) - { - pushIntoWorklist(newEdge); - } - } - - /// For each production X -> Y Z - /// Foreach outgoing edge Z(j,k) from node j do - /// add X(i,k) if not exist to E and to worklist - if (grammar->hasProdsFromFirstRHS(Y)) - for(const Production& prod : grammar->getProdsFromFirstRHS(Y)) - { - Symbol X = grammar->getLHSSymbol(prod); - for(const CFLEdge* Z_edge : j->getOutEdgeWithTy(grammar->getSecondRHSSymbol(prod))) - { - CFLNode* k = Z_edge->getDstNode(); - numOfChecks++; - if(const CFLEdge* newEdge = graph->addCFLEdge(i, k, X)) - { - pushIntoWorklist(newEdge); - } - } - } - - /// For each production X -> Z Y - /// Foreach incoming edge Z(k,i) to node i do - /// add X(k,j) if not exist to E and to worklist - if(grammar->hasProdsFromSecondRHS(Y)) - for(const Production& prod : grammar->getProdsFromSecondRHS(Y)) - { - Symbol X = grammar->getLHSSymbol(prod); - for(const CFLEdge* Z_edge : i->getInEdgeWithTy(grammar->getFirstRHSSymbol(prod))) - { - CFLNode* k = Z_edge->getSrcNode(); - numOfChecks++; - if(const CFLEdge* newEdge = graph->addCFLEdge(k, j, X)) - { - pushIntoWorklist(newEdge); - } - } - } -} - - -void CFLSolver::solve() -{ - /// initial worklist - initialize(); - - while(!isWorklistEmpty()) - { - /// Select and remove an edge Y(i,j) from worklist - const CFLEdge* Y_edge = popFromWorklist(); - processCFLEdge(Y_edge); - } -} - -void POCRSolver::buildCFLData() -{ - for (CFLEdge* edge: graph->getCFLEdges()) - addEdge(edge->getSrcID(), edge->getDstID(), edge->getEdgeKind()); -} - -void POCRSolver::processCFLEdge(const CFLEdge* Y_edge) -{ - CFLNode* i = Y_edge->getSrcNode(); - CFLNode* j = Y_edge->getDstNode(); - - /// For each production X -> Y - /// add X(i,j) if not exist to E and to worklist - Symbol Y = Y_edge->getEdgeKind(); - if (grammar->hasProdsFromSingleRHS(Y)) - for(const Production& prod : grammar->getProdsFromSingleRHS(Y)) - { - Symbol X = grammar->getLHSSymbol(prod); - numOfChecks++; - if (addEdge(i->getId(), j->getId(), X)) - { - const CFLEdge* newEdge = graph->addCFLEdge(Y_edge->getSrcNode(), Y_edge->getDstNode(), X); - pushIntoWorklist(newEdge); - } - - } - - /// For each production X -> Y Z - /// Foreach outgoing edge Z(j,k) from node j do - /// add X(i,k) if not exist to E and to worklist - if (grammar->hasProdsFromFirstRHS(Y)) - for(const Production& prod : grammar->getProdsFromFirstRHS(Y)) - { - Symbol X = grammar->getLHSSymbol(prod); - NodeBS diffDsts = addEdges(i->getId(), getSuccMap(j->getId())[grammar->getSecondRHSSymbol(prod)], X); - numOfChecks += getSuccMap(j->getId())[grammar->getSecondRHSSymbol(prod)].count(); - for (NodeID diffDst: diffDsts) - { - const CFLEdge* newEdge = graph->addCFLEdge(i, graph->getGNode(diffDst), X); - pushIntoWorklist(newEdge); - } - } - - /// For each production X -> Z Y - /// Foreach incoming edge Z(k,i) to node i do - /// add X(k,j) if not exist to E and to worklist - if(grammar->hasProdsFromSecondRHS(Y)) - for(const Production& prod : grammar->getProdsFromSecondRHS(Y)) - { - Symbol X = grammar->getLHSSymbol(prod); - NodeBS diffSrcs = addEdges(getPredMap(i->getId())[grammar->getFirstRHSSymbol(prod)], j->getId(), X); - numOfChecks += getPredMap(i->getId())[grammar->getFirstRHSSymbol(prod)].count(); - for (NodeID diffSrc: diffSrcs) - { - const CFLEdge* newEdge = graph->addCFLEdge(graph->getGNode(diffSrc), j, X); - pushIntoWorklist(newEdge); - } - } -} - -void POCRSolver::initialize() -{ - for(auto edge : graph->getCFLEdges()) - { - pushIntoWorklist(edge); - } - - /// Foreach production X -> epsilon - /// add X(i,i) if not exist to E and to worklist - for(const Production& prod : grammar->getEpsilonProds()) - { - for(auto IDMap : getSuccMap()) - { - Symbol X = grammar->getLHSSymbol(prod); - if (addEdge(IDMap.first, IDMap.first, X)) - { - CFLNode* i = graph->getGNode(IDMap.first); - const CFLEdge* newEdge = graph->addCFLEdge(i, i, X); - pushIntoWorklist(newEdge); - } - } - } -} - -void POCRHybridSolver::processCFLEdge(const CFLEdge* Y_edge) -{ - CFLNode* i = Y_edge->getSrcNode(); - CFLNode* j = Y_edge->getDstNode(); - - /// For each production X -> Y - /// add X(i,j) if not exist to E and to worklist - Symbol Y = Y_edge->getEdgeKind(); - if (grammar->hasProdsFromSingleRHS(Y)) - for(const Production& prod : grammar->getProdsFromSingleRHS(Y)) - { - Symbol X = grammar->getLHSSymbol(prod); - numOfChecks++; - if (addEdge(i->getId(), j->getId(), X)) - { - const CFLEdge* newEdge = graph->addCFLEdge(Y_edge->getSrcNode(), Y_edge->getDstNode(), X); - pushIntoWorklist(newEdge); - } - - } - - /// For each production X -> Y Z - /// Foreach outgoing edge Z(j,k) from node j do - /// add X(i,k) if not exist to E and to worklist - if (grammar->hasProdsFromFirstRHS(Y)) - for(const Production& prod : grammar->getProdsFromFirstRHS(Y)) - { - if ((grammar->getLHSSymbol(prod) == grammar->strToSymbol("F")) && (Y == grammar->strToSymbol("F")) && (grammar->getSecondRHSSymbol(prod) == grammar->strToSymbol("F"))) - { - addArc(i->getId(), j->getId()); - } - else - { - Symbol X = grammar->getLHSSymbol(prod); - NodeBS diffDsts = addEdges(i->getId(), getSuccMap(j->getId())[grammar->getSecondRHSSymbol(prod)], X); - numOfChecks += getSuccMap(j->getId())[grammar->getSecondRHSSymbol(prod)].count(); - for (NodeID diffDst: diffDsts) - { - const CFLEdge* newEdge = graph->addCFLEdge(i, graph->getGNode(diffDst), X); - pushIntoWorklist(newEdge); - } - } - } - - /// For each production X -> Z Y - /// Foreach incoming edge Z(k,i) to node i do - /// add X(k,j) if not exist to E and to worklist - if(grammar->hasProdsFromSecondRHS(Y)) - for(const Production& prod : grammar->getProdsFromSecondRHS(Y)) - { - if ((grammar->getLHSSymbol(prod) == grammar->strToSymbol("F")) && (Y == grammar->strToSymbol("F")) && (grammar->getFirstRHSSymbol(prod) == grammar->strToSymbol("F"))) - { - addArc(i->getId(), j->getId()); - } - else - { - Symbol X = grammar->getLHSSymbol(prod); - NodeBS diffSrcs = addEdges(getPredMap(i->getId())[grammar->getFirstRHSSymbol(prod)], j->getId(), X); - numOfChecks += getPredMap(i->getId())[grammar->getFirstRHSSymbol(prod)].count(); - for (NodeID diffSrc: diffSrcs) - { - const CFLEdge* newEdge = graph->addCFLEdge(graph->getGNode(diffSrc), j, X); - pushIntoWorklist(newEdge); - } - } - } -} - -void POCRHybridSolver::initialize() -{ - for(auto edge : graph->getCFLEdges()) - { - pushIntoWorklist(edge); - } - - // init hybrid dataset - for (auto it = graph->begin(); it != graph->end(); ++it) - { - NodeID nId = it->first; - addInd_h(nId, nId); - } - - /// add X(i,i) if not exist to E and to worklist - for(const Production& prod : grammar->getEpsilonProds()) - { - for(auto IDMap : getSuccMap()) - { - Symbol X = grammar->getLHSSymbol(prod); - if (addEdge(IDMap.first, IDMap.first, X)) - { - CFLNode* i = graph->getGNode(IDMap.first); - const CFLEdge* newEdge = graph->addCFLEdge(i, i, X); - pushIntoWorklist(newEdge); - } - } - } -} - -void POCRHybridSolver::addArc(NodeID src, NodeID dst) -{ - if(hasEdge(src, dst, grammar->strToSymbol("F"))) - return; - - for (auto& iter: indMap[src]) - { - meld(iter.first, getNode_h(iter.first, src), getNode_h(dst, dst)); - } -} - - -void POCRHybridSolver::meld(NodeID x, TreeNode* uNode, TreeNode* vNode) -{ - numOfChecks++; - - TreeNode* newVNode = addInd_h(x, vNode->id); - if (!newVNode) - return; - - insertEdge_h(uNode, newVNode); - for (TreeNode* vChild: vNode->children) - { - meld_h(x, newVNode, vChild); - } -} - -bool POCRHybridSolver::hasInd_h(NodeID src, NodeID dst) -{ - auto it = indMap.find(dst); - if (it == indMap.end()) - return false; - return (it->second.find(src) != it->second.end()); -} - -POCRHybridSolver::TreeNode* POCRHybridSolver::addInd_h(NodeID src, NodeID dst) -{ - TreeNode* newNode = new TreeNode(dst); - auto resIns = indMap[dst].insert(std::make_pair(src, newNode)); - if (resIns.second) - return resIns.first->second; - delete newNode; - return nullptr; -} - -void POCRHybridSolver::addArc_h(NodeID src, NodeID dst) -{ - if (!hasInd_h(src, dst)) - { - for (auto iter: indMap[src]) - { - meld_h(iter.first, getNode_h(iter.first, src), getNode_h(dst, dst)); - } - } -} - -void POCRHybridSolver::meld_h(NodeID x, TreeNode* uNode, TreeNode* vNode) -{ - TreeNode* newVNode = addInd_h(x, vNode->id); - if (!newVNode) - return; - - insertEdge_h(uNode, newVNode); - for (TreeNode* vChild: vNode->children) - { - meld_h(x, newVNode, vChild); - } -} \ No newline at end of file diff --git a/svf/lib/CFL/CFLStat.cpp b/svf/lib/CFL/CFLStat.cpp deleted file mode 100644 index c4d3ac11d..000000000 --- a/svf/lib/CFL/CFLStat.cpp +++ /dev/null @@ -1,97 +0,0 @@ -//===- CFLStat.cpp -- Statistics of CFL Reachability's analysis------------------// -// -// SVF: Static Value-Flow Analysis -// -// Copyright (C) <2013-2017> -// - -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU Affero General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Affero General Public License for more details. - -// You should have received a copy of the GNU Affero General Public License -// along with this program. If not, see . -// -//===----------------------------------------------------------------------===// - -/* - * CFLStat.cpp - * - * Created on: 17 Sep, 2022 - * Author: Pei Xu - */ - -#include "CFL/CFLStat.h" - -using namespace SVF; -using namespace SVFUtil; -using namespace std; - -/*! - * Constructor - */ -CFLStat::CFLStat(CFLBase* p): PTAStat(p),pta(p) -{ - startClk(); -} - -/*! - * Collect CFLGraph information - */ -void CFLStat::CFLGraphStat() -{ - pta->countSumEdges(); - CFLGraph* CFLGraph = pta->getCFLGraph(); - - timeStatMap["BuildingTime"] = pta->timeOfBuildCFLGraph; - PTNumStatMap["NumOfNodes"] = CFLGraph->getTotalNodeNum(); - PTNumStatMap["NumOfEdges"] = CFLGraph->getCFLEdges().size(); - - PTAStat::printStat("CFLGraph Stats"); -} - -void CFLStat::CFLGrammarStat() -{ - timeStatMap["BuildingTime"] = pta->timeOfBuildCFLGrammar; - timeStatMap["NormalizationTime"] = pta->timeOfNormalizeGrammar; - - PTAStat::printStat("CFLGrammar Stats"); -} - -void CFLStat::CFLSolverStat() -{ - timeStatMap["AnalysisTime"] = pta->timeOfSolving; - PTNumStatMap["numOfChecks"] = pta->numOfChecks; - PTNumStatMap["numOfIteration"] = pta->numOfIteration; - PTNumStatMap["SumEdges"] = pta->numOfStartEdges; - - PTAStat::printStat("CFL-reachability Solver Stats"); -} - -/*! - * Start here - */ -void CFLStat::performStat() -{ - assert((SVFUtil::isa(pta)) && "not an CFLAlias pass!! what else??"); - endClk(); - - // Grammar stat - CFLGrammarStat(); - - // CFLGraph stat - CFLGraphStat(); - - // Solver stat - CFLSolverStat(); - - // Stat about Call graph and General stat - PTAStat::performStat(); -} - diff --git a/svf/lib/CFL/CFLVF.cpp b/svf/lib/CFL/CFLVF.cpp deleted file mode 100644 index 5af9ec1fd..000000000 --- a/svf/lib/CFL/CFLVF.cpp +++ /dev/null @@ -1,96 +0,0 @@ -//===----- CFLVF.cpp -- CFL Value Flow Client--------------// -// -// SVF: Static Value-Flow Analysis -// -// Copyright (C) <2013-> -// - -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU Affero General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Affero General Public License for more details. - -// You should have received a copy of the GNU Affero General Public License -// along with this program. If not, see . -// -//===----------------------------------------------------------------------===// - -/* - * CFLAlias.cpp - * - * Created on: September 7 , 2022 - * Author: Pei Xu - */ - -#include "CFL/CFLVF.h" - -using namespace SVF; -using namespace SVFUtil; - -void CFLVF::buildCFLGraph() -{ - // Build CFL Graph - VFCFLGraphBuilder cflGraphBuilder = VFCFLGraphBuilder(); - if (Options::CFLGraph().empty()) // built from svfir - { - PointerAnalysis::initialize(); - AndersenWaveDiff* ander = AndersenWaveDiff::createAndersenWaveDiff(pag); - svfg = memSSA.buildFullSVFG(ander); - graph = cflGraphBuilder.buildBigraph(svfg, grammarBase->getStartKind(), grammarBase); - } - else - graph = cflGraphBuilder.build(Options::CFLGraph(), grammarBase); - - // Check CFL Graph and Grammar are accordance with grammar - CFLGramGraphChecker cflChecker = CFLGramGraphChecker(); - cflChecker.check(grammarBase, &cflGraphBuilder, graph); -} - -void CFLVF::initialize() -{ - // Parameter Checking - checkParameter(); - - // Build CFL Grammar - buildCFLGrammar(); - - // Build CFL Graph - buildCFLGraph(); - - // Normalize grammar - normalizeCFLGrammar(); - - // Initialize solver - solver = new CFLSolver(graph, grammar); -} - -void CFLVF::checkParameter() -{ - // Check for valid grammar file before parsing other options - std::string filename = Options::GrammarFilename(); - bool vfgfile = (filename.rfind("VFGGrammar.txt") == filename.length() - std::string("VFGGrammar.txt").length()); - if (!Options::Customized() && !vfgfile) - { - SVFUtil::errs() << "Invalid VFG grammar file: " << Options::GrammarFilename() << "\n" - << "Please use a file that ends with 'VFG.txt', " - << "or use the -customized flag to allow custom grammar files.\n"; - assert(false && "grammar loading failed!"); // exit with error - } -} - - -void CFLVF::finalize() -{ - if(Options::PrintCFL()) - { - if (Options::CFLGraph().empty()) - svfir->dump("IR"); - grammar->dump("Grammar"); - graph->dump("CFLGraph"); - } -} diff --git a/svf/lib/CFL/GrammarBuilder.cpp b/svf/lib/CFL/GrammarBuilder.cpp deleted file mode 100644 index a59d247be..000000000 --- a/svf/lib/CFL/GrammarBuilder.cpp +++ /dev/null @@ -1,152 +0,0 @@ -//===----- GrammarBuilder.cpp -- Grammar Builder--------------// -// -// SVF: Static Value-Flow Analysis -// -// Copyright (C) <2013-> -// - -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU Affero General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Affero General Public License for more details. - -// You should have received a copy of the GNU Affero General Public License -// along with this program. If not, see . -// -//===----------------------------------------------------------------------===// - -/* - * GrammarBuilder.h - * - * Created on: April 27, 2022 - * Author: Pei Xu - */ - -#include -#include -#include -#include -#include -#include "CFL/GrammarBuilder.h" - -namespace SVF -{ -const inline std::string GrammarBuilder::parseProductionsString() const -{ - std::ifstream textFile(fileName); - if (!textFile.is_open()) - { - std::cerr << "Can't open CFL grammar file `" << fileName << "`" << std::endl; - abort(); - } - std::string lineString; - std::string lines = ""; - std::string startString; - std::string symbolString; - const std::string WHITESPACE = " \n\r\t\f\v"; - int lineNum = 0; - while (getline(textFile, lineString)) - { - if(lineNum == 1) - { - startString = stripSpace(lineString); - } - if(lineNum == 3) - { - symbolString = lineString.substr(lineString.find_first_not_of(WHITESPACE), lineString.find_last_not_of(WHITESPACE)+1); - } - - lines.append(lineString.substr(lineString.find_first_not_of(WHITESPACE), lineString.find_last_not_of(WHITESPACE)+1)); - lineNum++; - } - - std::regex reg("Start:([\\s\\S]*)Terminal:(.*)Productions:([\\s\\S]*)"); - std::smatch matches; - if (std::regex_search(lines, matches, reg)) - { - lines = matches.str(3); - } - std::string sString; - size_t pos = 0; - while ((pos = symbolString.find(" ")) != std::string::npos) - { - sString = stripSpace(symbolString.substr(0, pos)); - symbolString.erase(0, pos + 1); //Capital is Nonterminal, Otherwise is terminal - grammar->insertSymbol(sString); - } - grammar->insertSymbol(symbolString); - grammar->setStartKind(grammar->insertSymbol(startString)); - grammar->insertTerminalKind("epsilon"); - - return lines; -} - -const inline std::vector GrammarBuilder::loadWordProductions() const -{ - size_t pos = 0; - std::string lines = parseProductionsString(); - std::string word = ""; - std::vector wordProds; - std::string delimiter = ";"; - while ((pos = lines.find(";")) != std::string::npos) - { - word = lines.substr(0, pos); - wordProds.push_back(word); - lines.erase(0, pos + delimiter.length()); - } - return wordProds; -} - -const inline std::string GrammarBuilder::stripSpace(std::string s) const -{ - std::smatch matches; - std::regex stripReg("\\s*(\\S*)\\s*"); - std::regex_search(s, matches, stripReg); - return matches.str(1); -} - - -/// build grammarbase from textfile -GrammarBase* GrammarBuilder::build() const -{ - std::smatch matches; - std::string delimiter = " "; - std::string delimiter1 = "->"; - std::string word = ""; - size_t pos; - GrammarBase::Production prod; - std::vector wordProdVec = loadWordProductions(); - - for (auto wordProd : wordProdVec) - { - if ((pos = wordProd.find(delimiter1)) != std::string::npos) - { - std::string RHS = stripSpace(wordProd.substr(0, pos)); - std::string LHS = wordProd.substr(pos + delimiter1.size(), wordProd.size() - 1); - GrammarBase::Symbol RHSSymbol = grammar->insertSymbol(RHS); - prod.push_back(RHSSymbol); - if (grammar->getRawProductions().find(RHSSymbol) == grammar->getRawProductions().end()) grammar->getRawProductions().insert({RHSSymbol, {}}); - std::regex LHSRegEx("\\s*(.*)"); - std::regex_search(LHS, matches, LHSRegEx); - LHS = matches.str(1); - while ((pos = LHS.find(delimiter)) != std::string::npos) - { - word = LHS.substr(0, pos); - LHS.erase(0, pos + delimiter.length()); //Capital is Nonterminal, Otherwise is terminal - prod.push_back(grammar->insertSymbol(word)); - } - prod.push_back(grammar->insertSymbol(LHS)); - grammar->getRawProductions().at(RHSSymbol).insert(prod); - prod = {}; - } - } - - return grammar; -}; - -} diff --git a/svf/lib/CMakeLists.txt b/svf/lib/CMakeLists.txt new file mode 100644 index 000000000..eabf63349 --- /dev/null +++ b/svf/lib/CMakeLists.txt @@ -0,0 +1,35 @@ +add_subdirectory(CUDD) + +# Due to a mutual dependencies, all the sub projects of the SVG are merged here +# Otherwise it is impossible to load the dependencies in opt +# NOTE: if the SVF should be linked into opt, we should probably use the individual sub projects here, rather than the combined project + +if (LLVM_LINK_LLVM_DYLIB) + set(llvm_libs LLVM) +else() + llvm_map_components_to_libnames(llvm_libs bitwriter core ipo irreader instcombine instrumentation target linker analysis scalaropts support transformutils) +endif() + +add_compile_definitions(MEMVIEW_WITH_SVF) + +file (GLOB SOURCES + SVF-FE/*.cpp + Graphs/*.cpp + Util/*.cpp + MemoryModel/*.cpp + MSSA/*.cpp + WPA/*.cpp + MTA/*.cpp + SABER/*.cpp + DDA/*.cpp + MemoryViewSwitcher/InsertSwitch/src/lib/*.cpp) +add_llvm_library(Svf STATIC ${SOURCES} LINK_LIBS Cudd ${Z3_LIBRARIES}) +add_llvm_library(SvfShared SHARED ${SOURCES} LINK_LIBS Cudd ${Z3_LIBRARIES}) + + +link_directories(${CMAKE_BINARY_DIR}/lib/Cudd) + + +if(DEFINED IN_SOURCE_BUILD) + add_dependencies(Svf intrinsics_gen) +endif() diff --git a/svf/lib/CUDD/CMakeLists.txt b/svf/lib/CUDD/CMakeLists.txt new file mode 100644 index 000000000..083d92657 --- /dev/null +++ b/svf/lib/CUDD/CMakeLists.txt @@ -0,0 +1,16 @@ +set(SOURCES + epd.c + st.c + util.c + mtr.c + cuddInt.c +) + +add_llvm_library(Cudd ${SOURCES} LINK_LIBS m) + + +set_target_properties(Cudd PROPERTIES COMPILE_FLAGS "-Wno-format -Wno-int-to-pointer-cast -Wno-pointer-to-int-cast -DHAVE_IEEE_754 -DSIZEOF_VOID_P=8 -DSIZEOF_LONG=8") + +if(DEFINED IN_SOURCE_BUILD) + add_dependencies(Cudd intrinsics_gen) +endif() diff --git a/svf/lib/CUDD/cuddInt.c b/svf/lib/CUDD/cuddInt.c new file mode 100644 index 000000000..420d12064 --- /dev/null +++ b/svf/lib/CUDD/cuddInt.c @@ -0,0 +1,33645 @@ +/**CFile*********************************************************************** + + FileName [cuddAddIte.c] + + PackageName [cudd] + + Synopsis [ADD ITE function and satellites.] + + Description [External procedures included in this module: +
    +
  • Cudd_addIte() +
  • Cudd_addIteConstant() +
  • Cudd_addEvalConst() +
  • Cudd_addCmpl() +
  • Cudd_addLeq() +
+ Internal procedures included in this module: +
    +
  • cuddAddIteRecur() +
  • cuddAddCmplRecur() +
+ Static procedures included in this module: +
    +
  • addVarToConst() +
] + + Author [Fabio Somenzi] + + Copyright [Copyright (c) 1995-2012, Regents of the University of Colorado + + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + Neither the name of the University of Colorado nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE.] + +******************************************************************************/ + +#include "CUDD/util.h" +#include "CUDD/cuddInt.h" + +/*---------------------------------------------------------------------------*/ +/* Constant declarations */ +/*---------------------------------------------------------------------------*/ + + +/*---------------------------------------------------------------------------*/ +/* Stucture declarations */ +/*---------------------------------------------------------------------------*/ + + +/*---------------------------------------------------------------------------*/ +/* Type declarations */ +/*---------------------------------------------------------------------------*/ + + +/*---------------------------------------------------------------------------*/ +/* Variable declarations */ +/*---------------------------------------------------------------------------*/ + +#ifndef lint +static char rcsid[] DD_UNUSED = "$Id: cuddAddIte.c,v 1.16 2012/02/05 01:07:18 fabio Exp $"; +#endif + + +/*---------------------------------------------------------------------------*/ +/* Macro declarations */ +/*---------------------------------------------------------------------------*/ + + +/**AutomaticStart*************************************************************/ + +/*---------------------------------------------------------------------------*/ +/* Static function prototypes */ +/*---------------------------------------------------------------------------*/ + +static void addVarToConst (DdNode *f, DdNode **gp, DdNode **hp, DdNode *one, DdNode *zero); + +/**AutomaticEnd***************************************************************/ + + +/*---------------------------------------------------------------------------*/ +/* Definition of exported functions */ +/*---------------------------------------------------------------------------*/ + + +/**Function******************************************************************** + + Synopsis [Implements ITEconstant for ADDs.] + + Description [Implements ITEconstant for ADDs. f must be a 0-1 ADD. + Returns a pointer to the resulting ADD (which may or may not be + constant) or DD_NON_CONSTANT. No new nodes are created. This function + can be used, for instance, to check that g has a constant value + (specified by h) whenever f is 1. If the constant value is unknown, + then one should use Cudd_addEvalConst.] + + SideEffects [None] + + SeeAlso [Cudd_addIte Cudd_addEvalConst Cudd_bddIteConstant] + +******************************************************************************/ +DdNode * +Cudd_addIteConstant( + DdManager * dd, + DdNode * f, + DdNode * g, + DdNode * h) +{ + DdNode *one,*zero; + DdNode *Fv,*Fnv,*Gv,*Gnv,*Hv,*Hnv,*r,*t,*e; + unsigned int topf,topg,toph,v; + + statLine(dd); + /* Trivial cases. */ + if (f == (one = DD_ONE(dd))) { /* ITE(1,G,H) = G */ + return(g); + } + if (f == (zero = DD_ZERO(dd))) { /* ITE(0,G,H) = H */ + return(h); + } + + /* From now on, f is known not to be a constant. */ + addVarToConst(f,&g,&h,one,zero); + + /* Check remaining one variable cases. */ + if (g == h) { /* ITE(F,G,G) = G */ + return(g); + } + if (cuddIsConstant(g) && cuddIsConstant(h)) { + return(DD_NON_CONSTANT); + } + + topf = cuddI(dd,f->index); + topg = cuddI(dd,g->index); + toph = cuddI(dd,h->index); + v = ddMin(topg,toph); + + /* ITE(F,G,H) = (x,G,H) (non constant) if F = (x,1,0), x < top(G,H). */ + if (topf < v && cuddIsConstant(cuddT(f)) && cuddIsConstant(cuddE(f))) { + return(DD_NON_CONSTANT); + } + + /* Check cache. */ + r = cuddConstantLookup(dd,DD_ADD_ITE_CONSTANT_TAG,f,g,h); + if (r != NULL) { + return(r); + } + + /* Compute cofactors. */ + if (topf <= v) { + v = ddMin(topf,v); /* v = top_var(F,G,H) */ + Fv = cuddT(f); Fnv = cuddE(f); + } else { + Fv = Fnv = f; + } + if (topg == v) { + Gv = cuddT(g); Gnv = cuddE(g); + } else { + Gv = Gnv = g; + } + if (toph == v) { + Hv = cuddT(h); Hnv = cuddE(h); + } else { + Hv = Hnv = h; + } + + /* Recursive step. */ + t = Cudd_addIteConstant(dd,Fv,Gv,Hv); + if (t == DD_NON_CONSTANT || !cuddIsConstant(t)) { + cuddCacheInsert(dd, DD_ADD_ITE_CONSTANT_TAG, f, g, h, DD_NON_CONSTANT); + return(DD_NON_CONSTANT); + } + e = Cudd_addIteConstant(dd,Fnv,Gnv,Hnv); + if (e == DD_NON_CONSTANT || !cuddIsConstant(e) || t != e) { + cuddCacheInsert(dd, DD_ADD_ITE_CONSTANT_TAG, f, g, h, DD_NON_CONSTANT); + return(DD_NON_CONSTANT); + } + cuddCacheInsert(dd, DD_ADD_ITE_CONSTANT_TAG, f, g, h, t); + return(t); + +} /* end of Cudd_addIteConstant */ + + +/**Function******************************************************************** + + Synopsis [Checks whether ADD g is constant whenever ADD f is 1.] + + Description [Checks whether ADD g is constant whenever ADD f is 1. f + must be a 0-1 ADD. Returns a pointer to the resulting ADD (which may + or may not be constant) or DD_NON_CONSTANT. If f is identically 0, + the check is assumed to be successful, and the background value is + returned. No new nodes are created.] + + SideEffects [None] + + SeeAlso [Cudd_addIteConstant Cudd_addLeq] + +******************************************************************************/ +DdNode * +Cudd_addEvalConst( + DdManager * dd, + DdNode * f, + DdNode * g) +{ + DdNode *zero; + DdNode *Fv,*Fnv,*Gv,*Gnv,*r,*t,*e; + unsigned int topf,topg; + +#ifdef DD_DEBUG + assert(!Cudd_IsComplement(f)); +#endif + + statLine(dd); + /* Terminal cases. */ + if (f == DD_ONE(dd) || cuddIsConstant(g)) { + return(g); + } + if (f == (zero = DD_ZERO(dd))) { + return(dd->background); + } + +#ifdef DD_DEBUG + assert(!cuddIsConstant(f)); +#endif + /* From now on, f and g are known not to be constants. */ + + topf = cuddI(dd,f->index); + topg = cuddI(dd,g->index); + + /* Check cache. */ + r = cuddConstantLookup(dd,DD_ADD_EVAL_CONST_TAG,f,g,g); + if (r != NULL) { + return(r); + } + + /* Compute cofactors. */ + if (topf <= topg) { + Fv = cuddT(f); Fnv = cuddE(f); + } else { + Fv = Fnv = f; + } + if (topg <= topf) { + Gv = cuddT(g); Gnv = cuddE(g); + } else { + Gv = Gnv = g; + } + + /* Recursive step. */ + if (Fv != zero) { + t = Cudd_addEvalConst(dd,Fv,Gv); + if (t == DD_NON_CONSTANT || !cuddIsConstant(t)) { + cuddCacheInsert2(dd, Cudd_addEvalConst, f, g, DD_NON_CONSTANT); + return(DD_NON_CONSTANT); + } + if (Fnv != zero) { + e = Cudd_addEvalConst(dd,Fnv,Gnv); + if (e == DD_NON_CONSTANT || !cuddIsConstant(e) || t != e) { + cuddCacheInsert2(dd, Cudd_addEvalConst, f, g, DD_NON_CONSTANT); + return(DD_NON_CONSTANT); + } + } + cuddCacheInsert2(dd,Cudd_addEvalConst,f,g,t); + return(t); + } else { /* Fnv must be != zero */ + e = Cudd_addEvalConst(dd,Fnv,Gnv); + cuddCacheInsert2(dd, Cudd_addEvalConst, f, g, e); + return(e); + } + +} /* end of Cudd_addEvalConst */ + + +/**Function******************************************************************** + + Synopsis [Computes the complement of an ADD a la C language.] + + Description [Computes the complement of an ADD a la C language: The + complement of 0 is 1 and the complement of everything else is 0. + Returns a pointer to the resulting ADD if successful; NULL otherwise.] + + SideEffects [None] + + SeeAlso [Cudd_addNegate] + +******************************************************************************/ +DdNode * +Cudd_addCmpl( + DdManager * dd, + DdNode * f) +{ + DdNode *res; + + do { + dd->reordered = 0; + res = cuddAddCmplRecur(dd,f); + } while (dd->reordered == 1); + return(res); + +} /* end of Cudd_addCmpl */ + + +/**Function******************************************************************** + + Synopsis [Determines whether f is less than or equal to g.] + + Description [Returns 1 if f is less than or equal to g; 0 otherwise. + No new nodes are created. This procedure works for arbitrary ADDs. + For 0-1 ADDs Cudd_addEvalConst is more efficient.] + + SideEffects [None] + + SeeAlso [Cudd_addIteConstant Cudd_addEvalConst Cudd_bddLeq] + +******************************************************************************/ +int +Cudd_addLeq( + DdManager * dd, + DdNode * f, + DdNode * g) +{ + DdNode *tmp, *fv, *fvn, *gv, *gvn; + unsigned int topf, topg, res; + + /* Terminal cases. */ + if (f == g) return(1); + + statLine(dd); + if (cuddIsConstant(f)) { + if (cuddIsConstant(g)) return(cuddV(f) <= cuddV(g)); + if (f == DD_MINUS_INFINITY(dd)) return(1); + if (f == DD_PLUS_INFINITY(dd)) return(0); /* since f != g */ + } + if (g == DD_PLUS_INFINITY(dd)) return(1); + if (g == DD_MINUS_INFINITY(dd)) return(0); /* since f != g */ + + /* Check cache. */ + tmp = cuddCacheLookup2(dd,(DD_CTFP)Cudd_addLeq,f,g); + if (tmp != NULL) { + return(tmp == DD_ONE(dd)); + } + + /* Compute cofactors. One of f and g is not constant. */ + topf = cuddI(dd,f->index); + topg = cuddI(dd,g->index); + if (topf <= topg) { + fv = cuddT(f); fvn = cuddE(f); + } else { + fv = fvn = f; + } + if (topg <= topf) { + gv = cuddT(g); gvn = cuddE(g); + } else { + gv = gvn = g; + } + + res = Cudd_addLeq(dd,fvn,gvn) && Cudd_addLeq(dd,fv,gv); + + /* Store result in cache and return. */ + cuddCacheInsert2(dd,(DD_CTFP) Cudd_addLeq,f,g, + Cudd_NotCond(DD_ONE(dd),res==0)); + return(res); + +} /* end of Cudd_addLeq */ + + +/*---------------------------------------------------------------------------*/ +/* Definition of internal functions */ +/*---------------------------------------------------------------------------*/ + + +/**Function******************************************************************** + + Synopsis [Implements the recursive step of Cudd_addIte(f,g,h).] + + Description [Implements the recursive step of Cudd_addIte(f,g,h). + Returns a pointer to the resulting ADD if successful; NULL + otherwise.] + + SideEffects [None] + + SeeAlso [Cudd_addIte] + +******************************************************************************/ +DdNode * +cuddAddIteRecur( + DdManager * dd, + DdNode * f, + DdNode * g, + DdNode * h) +{ + DdNode *one,*zero; + DdNode *r,*Fv,*Fnv,*Gv,*Gnv,*Hv,*Hnv,*t,*e; + unsigned int topf,topg,toph,v; + int index; + + statLine(dd); + /* Trivial cases. */ + + /* One variable cases. */ + if (f == (one = DD_ONE(dd))) { /* ITE(1,G,H) = G */ + return(g); + } + if (f == (zero = DD_ZERO(dd))) { /* ITE(0,G,H) = H */ + return(h); + } + + /* From now on, f is known to not be a constant. */ + addVarToConst(f,&g,&h,one,zero); + + /* Check remaining one variable cases. */ + if (g == h) { /* ITE(F,G,G) = G */ + return(g); + } + + if (g == one) { /* ITE(F,1,0) = F */ + if (h == zero) return(f); + } + + topf = cuddI(dd,f->index); + topg = cuddI(dd,g->index); + toph = cuddI(dd,h->index); + v = ddMin(topg,toph); + + /* A shortcut: ITE(F,G,H) = (x,G,H) if F=(x,1,0), x < top(G,H). */ + if (topf < v && cuddT(f) == one && cuddE(f) == zero) { + r = cuddUniqueInter(dd,(int)f->index,g,h); + return(r); + } + if (topf < v && cuddT(f) == zero && cuddE(f) == one) { + r = cuddUniqueInter(dd,(int)f->index,h,g); + return(r); + } + + /* Check cache. */ + r = cuddCacheLookup(dd,DD_ADD_ITE_TAG,f,g,h); + if (r != NULL) { + return(r); + } + + /* Compute cofactors. */ + if (topf <= v) { + v = ddMin(topf,v); /* v = top_var(F,G,H) */ + index = f->index; + Fv = cuddT(f); Fnv = cuddE(f); + } else { + Fv = Fnv = f; + } + if (topg == v) { + index = g->index; + Gv = cuddT(g); Gnv = cuddE(g); + } else { + Gv = Gnv = g; + } + if (toph == v) { + index = h->index; + Hv = cuddT(h); Hnv = cuddE(h); + } else { + Hv = Hnv = h; + } + + /* Recursive step. */ + t = cuddAddIteRecur(dd,Fv,Gv,Hv); + if (t == NULL) return(NULL); + cuddRef(t); + + e = cuddAddIteRecur(dd,Fnv,Gnv,Hnv); + if (e == NULL) { + Cudd_RecursiveDeref(dd,t); + return(NULL); + } + cuddRef(e); + + r = (t == e) ? t : cuddUniqueInter(dd,index,t,e); + if (r == NULL) { + Cudd_RecursiveDeref(dd,t); + Cudd_RecursiveDeref(dd,e); + return(NULL); + } + cuddDeref(t); + cuddDeref(e); + + cuddCacheInsert(dd,DD_ADD_ITE_TAG,f,g,h,r); + + return(r); + +} /* end of cuddAddIteRecur */ + + +/**Function******************************************************************** + + Synopsis [Performs the recursive step of Cudd_addCmpl.] + + Description [Performs the recursive step of Cudd_addCmpl. Returns a + pointer to the resulting ADD if successful; NULL otherwise.] + + SideEffects [None] + + SeeAlso [Cudd_addCmpl] + +******************************************************************************/ +DdNode * +cuddAddCmplRecur( + DdManager * dd, + DdNode * f) +{ + DdNode *one,*zero; + DdNode *r,*Fv,*Fnv,*t,*e; + + statLine(dd); + one = DD_ONE(dd); + zero = DD_ZERO(dd); + + if (cuddIsConstant(f)) { + if (f == zero) { + return(one); + } else { + return(zero); + } + } + r = cuddCacheLookup1(dd,Cudd_addCmpl,f); + if (r != NULL) { + return(r); + } + Fv = cuddT(f); + Fnv = cuddE(f); + t = cuddAddCmplRecur(dd,Fv); + if (t == NULL) return(NULL); + cuddRef(t); + e = cuddAddCmplRecur(dd,Fnv); + if (e == NULL) { + Cudd_RecursiveDeref(dd,t); + return(NULL); + } + cuddRef(e); + r = (t == e) ? t : cuddUniqueInter(dd,(int)f->index,t,e); + if (r == NULL) { + Cudd_RecursiveDeref(dd, t); + Cudd_RecursiveDeref(dd, e); + return(NULL); + } + cuddDeref(t); + cuddDeref(e); + cuddCacheInsert1(dd,Cudd_addCmpl,f,r); + return(r); + +} /* end of cuddAddCmplRecur */ + + +/*---------------------------------------------------------------------------*/ +/* Definition of static functions */ +/*---------------------------------------------------------------------------*/ + + +/**Function******************************************************************** + + Synopsis [Replaces variables with constants if possible (part of + canonical form).] + + Description [] + + SideEffects [None] + +******************************************************************************/ +static void +addVarToConst( + DdNode * f, + DdNode ** gp, + DdNode ** hp, + DdNode * one, + DdNode * zero) +{ + DdNode *g = *gp; + DdNode *h = *hp; + + if (f == g) { /* ITE(F,F,H) = ITE(F,1,H) = F + H */ + *gp = one; + } + + if (f == h) { /* ITE(F,G,F) = ITE(F,G,0) = F * G */ + *hp = zero; + } + +} /* end of addVarToConst */ + + +/**CFile*********************************************************************** + + FileName [cuddAnneal.c] + + PackageName [cudd] + + Synopsis [Reordering of DDs based on simulated annealing] + + Description [Internal procedures included in this file: +
    +
  • cuddAnnealing() +
+ Static procedures included in this file: +
    +
  • stopping_criterion() +
  • random_generator() +
  • ddExchange() +
  • ddJumpingAux() +
  • ddJumpingUp() +
  • ddJumpingDown() +
  • siftBackwardProb() +
  • copyOrder() +
  • restoreOrder() +
+ ] + + SeeAlso [] + + Author [Jae-Young Jang, Jorgen Sivesind] + + Copyright [Copyright (c) 1995-2012, Regents of the University of Colorado + + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + Neither the name of the University of Colorado nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE.] + +******************************************************************************/ + +/*---------------------------------------------------------------------------*/ +/* Constant declarations */ +/*---------------------------------------------------------------------------*/ + +/* Annealing parameters */ +#define BETA 0.6 +#define ALPHA 0.90 +#define EXC_PROB 0.4 +#define JUMP_UP_PROB 0.36 +#define MAXGEN_RATIO 15.0 +#define STOP_TEMP 1.0 + +/*---------------------------------------------------------------------------*/ +/* Stucture declarations */ +/*---------------------------------------------------------------------------*/ + + +/*---------------------------------------------------------------------------*/ +/* Type declarations */ +/*---------------------------------------------------------------------------*/ + + +/*---------------------------------------------------------------------------*/ +/* Variable declarations */ +/*---------------------------------------------------------------------------*/ + +//#ifndef lint +//static char rcsid[] DD_UNUSED = "$Id: cuddAnneal.c,v 1.15 2012/02/05 01:07:18 fabio Exp $"; +//#endif + +#ifdef DD_STATS +extern int ddTotalNumberSwapping; +extern int ddTotalNISwaps; +static int tosses; +static int acceptances; +#endif + +/*---------------------------------------------------------------------------*/ +/* Macro declarations */ +/*---------------------------------------------------------------------------*/ + + +/**AutomaticStart*************************************************************/ + +/*---------------------------------------------------------------------------*/ +/* Static function prototypes */ +/*---------------------------------------------------------------------------*/ + +static int stopping_criterion (int c1, int c2, int c3, int c4, double temp); +static double random_generator (void); +static int ddExchange (DdManager *table, int x, int y, double temp); +static int ddJumpingAux (DdManager *table, int x, int x_low, int x_high, double temp); +static Move * ddJumpingUp (DdManager *table, int x, int x_low, int initial_size); +static Move * ddJumpingDown (DdManager *table, int x, int x_high, int initial_size); +static int siftBackwardProb (DdManager *table, Move *moves, int size, double temp); +static void copyOrder (DdManager *table, int *array, int lower, int upper); +static int restoreOrder (DdManager *table, int *array, int lower, int upper); + +/**AutomaticEnd***************************************************************/ + + +/*---------------------------------------------------------------------------*/ +/* Definition of exported functions */ +/*---------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------*/ +/* Definition of internal functions */ +/*---------------------------------------------------------------------------*/ + + +/**Function******************************************************************** + + Synopsis [Get new variable-order by simulated annealing algorithm.] + + Description [Get x, y by random selection. Choose either + exchange or jump randomly. In case of jump, choose between jump_up + and jump_down randomly. Do exchange or jump and get optimal case. + Loop until there is no improvement or temperature reaches + minimum. Returns 1 in case of success; 0 otherwise.] + + SideEffects [None] + + SeeAlso [] + +******************************************************************************/ +int +cuddAnnealing( + DdManager * table, + int lower, + int upper) +{ + int nvars; + int size; + int x,y; + int result; + int c1, c2, c3, c4; + int BestCost; + int *BestOrder; + double NewTemp, temp; + double rand1; + int innerloop, maxGen; + int ecount, ucount, dcount; + + nvars = upper - lower + 1; + + result = cuddSifting(table,lower,upper); +#ifdef DD_STATS + (void) fprintf(table->out,"\n"); +#endif + if (result == 0) return(0); + + size = table->keys - table->isolated; + + /* Keep track of the best order. */ + BestCost = size; + BestOrder = ALLOC(int,nvars); + if (BestOrder == NULL) { + table->errorCode = CUDD_MEMORY_OUT; + return(0); + } + copyOrder(table,BestOrder,lower,upper); + + temp = BETA * size; + maxGen = (int) (MAXGEN_RATIO * nvars); + + c1 = size + 10; + c2 = c1 + 10; + c3 = size; + c4 = c2 + 10; + ecount = ucount = dcount = 0; + + while (!stopping_criterion(c1, c2, c3, c4, temp)) { +#ifdef DD_STATS + (void) fprintf(table->out,"temp=%f\tsize=%d\tgen=%d\t", + temp,size,maxGen); + tosses = acceptances = 0; +#endif + for (innerloop = 0; innerloop < maxGen; innerloop++) { + /* Choose x, y randomly. */ + x = (int) Cudd_Random() % nvars; + do { + y = (int) Cudd_Random() % nvars; + } while (x == y); + x += lower; + y += lower; + if (x > y) { + int tmp = x; + x = y; + y = tmp; + } + + /* Choose move with roulette wheel. */ + rand1 = random_generator(); + if (rand1 < EXC_PROB) { + result = ddExchange(table,x,y,temp); /* exchange */ + ecount++; +#if 0 + (void) fprintf(table->out, + "Exchange of %d and %d: size = %d\n", + x,y,table->keys - table->isolated); +#endif + } else if (rand1 < EXC_PROB + JUMP_UP_PROB) { + result = ddJumpingAux(table,y,x,y,temp); /* jumping_up */ + ucount++; +#if 0 + (void) fprintf(table->out, + "Jump up of %d to %d: size = %d\n", + y,x,table->keys - table->isolated); +#endif + } else { + result = ddJumpingAux(table,x,x,y,temp); /* jumping_down */ + dcount++; +#if 0 + (void) fprintf(table->out, + "Jump down of %d to %d: size = %d\n", + x,y,table->keys - table->isolated); +#endif + } + + if (!result) { + FREE(BestOrder); + return(0); + } + + size = table->keys - table->isolated; /* keep current size */ + if (size < BestCost) { /* update best order */ + BestCost = size; + copyOrder(table,BestOrder,lower,upper); + } + } + c1 = c2; + c2 = c3; + c3 = c4; + c4 = size; + NewTemp = ALPHA * temp; + if (NewTemp >= 1.0) { + maxGen = (int)(log(NewTemp) / log(temp) * maxGen); + } + temp = NewTemp; /* control variable */ +#ifdef DD_STATS + (void) fprintf(table->out,"uphill = %d\taccepted = %d\n", + tosses,acceptances); + fflush(table->out); +#endif + } + + result = restoreOrder(table,BestOrder,lower,upper); + FREE(BestOrder); + if (!result) return(0); +#ifdef DD_STATS + fprintf(table->out,"#:N_EXCHANGE %8d : total exchanges\n",ecount); + fprintf(table->out,"#:N_JUMPUP %8d : total jumps up\n",ucount); + fprintf(table->out,"#:N_JUMPDOWN %8d : total jumps down",dcount); +#endif + return(1); + +} /* end of cuddAnnealing */ + + +/*---------------------------------------------------------------------------*/ +/* Definition of static functions */ +/*---------------------------------------------------------------------------*/ + +/**Function******************************************************************** + + Synopsis [Checks termination condition.] + + Description [If temperature is STOP_TEMP or there is no improvement + then terminates. Returns 1 if the termination criterion is met; 0 + otherwise.] + + SideEffects [None] + + SeeAlso [] + +******************************************************************************/ +static int +stopping_criterion( + int c1, + int c2, + int c3, + int c4, + double temp) +{ + if (STOP_TEMP < temp) { + return(0); + } else if ((c1 == c2) && (c1 == c3) && (c1 == c4)) { + return(1); + } else { + return(0); + } + +} /* end of stopping_criterion */ + + +/**Function******************************************************************** + + Synopsis [Random number generator.] + + Description [Returns a double precision value between 0.0 and 1.0.] + + SideEffects [None] + + SeeAlso [] + +******************************************************************************/ +static double +random_generator(void) +{ + return((double)(Cudd_Random() / 2147483561.0)); + +} /* end of random_generator */ + + +/**Function******************************************************************** + + Synopsis [This function is for exchanging two variables, x and y.] + + Description [This is the same funcion as ddSwapping except for + comparison expression. Use probability function, exp(-size_change/temp).] + + SideEffects [None] + + SeeAlso [] + +******************************************************************************/ +static int +ddExchange( + DdManager * table, + int x, + int y, + double temp) +{ + Move *move,*moves; + int tmp; + int x_ref,y_ref; + int x_next,y_next; + int size, result; + int initial_size, limit_size; + + x_ref = x; + y_ref = y; + + x_next = cuddNextHigh(table,x); + y_next = cuddNextLow(table,y); + moves = NULL; + initial_size = limit_size = table->keys - table->isolated; + + for (;;) { + if (x_next == y_next) { + size = cuddSwapInPlace(table,x,x_next); + if (size == 0) goto ddExchangeOutOfMem; + move = (Move *)cuddDynamicAllocNode(table); + if (move == NULL) goto ddExchangeOutOfMem; + move->x = x; + move->y = x_next; + move->size = size; + move->next = moves; + moves = move; + size = cuddSwapInPlace(table,y_next,y); + if (size == 0) goto ddExchangeOutOfMem; + move = (Move *)cuddDynamicAllocNode(table); + if (move == NULL) goto ddExchangeOutOfMem; + move->x = y_next; + move->y = y; + move->size = size; + move->next = moves; + moves = move; + size = cuddSwapInPlace(table,x,x_next); + if (size == 0) goto ddExchangeOutOfMem; + move = (Move *)cuddDynamicAllocNode(table); + if (move == NULL) goto ddExchangeOutOfMem; + move->x = x; + move->y = x_next; + move->size = size; + move->next = moves; + moves = move; + + tmp = x; + x = y; + y = tmp; + } else if (x == y_next) { + size = cuddSwapInPlace(table,x,x_next); + if (size == 0) goto ddExchangeOutOfMem; + move = (Move *)cuddDynamicAllocNode(table); + if (move == NULL) goto ddExchangeOutOfMem; + move->x = x; + move->y = x_next; + move->size = size; + move->next = moves; + moves = move; + tmp = x; + x = y; + y = tmp; + } else { + size = cuddSwapInPlace(table,x,x_next); + if (size == 0) goto ddExchangeOutOfMem; + move = (Move *)cuddDynamicAllocNode(table); + if (move == NULL) goto ddExchangeOutOfMem; + move->x = x; + move->y = x_next; + move->size = size; + move->next = moves; + moves = move; + size = cuddSwapInPlace(table,y_next,y); + if (size == 0) goto ddExchangeOutOfMem; + move = (Move *)cuddDynamicAllocNode(table); + if (move == NULL) goto ddExchangeOutOfMem; + move->x = y_next; + move->y = y; + move->size = size; + move->next = moves; + moves = move; + x = x_next; + y = y_next; + } + + x_next = cuddNextHigh(table,x); + y_next = cuddNextLow(table,y); + if (x_next > y_ref) break; + + if ((double) size > DD_MAX_REORDER_GROWTH * (double) limit_size) { + break; + } else if (size < limit_size) { + limit_size = size; + } + } + + if (y_next>=x_ref) { + size = cuddSwapInPlace(table,y_next,y); + if (size == 0) goto ddExchangeOutOfMem; + move = (Move *)cuddDynamicAllocNode(table); + if (move == NULL) goto ddExchangeOutOfMem; + move->x = y_next; + move->y = y; + move->size = size; + move->next = moves; + moves = move; + } + + /* move backward and stop at best position or accept uphill move */ + result = siftBackwardProb(table,moves,initial_size,temp); + if (!result) goto ddExchangeOutOfMem; + + while (moves != NULL) { + move = moves->next; + cuddDeallocMove(table, moves); + moves = move; + } + return(1); + + ddExchangeOutOfMem: + while (moves != NULL) { + move = moves->next; + cuddDeallocMove(table, moves); + moves = move; + } + return(0); + +} /* end of ddExchange */ + + +/**Function******************************************************************** + + Synopsis [Moves a variable to a specified position.] + + Description [If x==x_low, it executes jumping_down. If x==x_high, it + executes jumping_up. This funcion is similar to ddSiftingAux. Returns + 1 in case of success; 0 otherwise.] + + SideEffects [None] + + SeeAlso [] + +******************************************************************************/ +static int +ddJumpingAux( + DdManager * table, + int x, + int x_low, + int x_high, + double temp) +{ + Move *move; + Move *moves; /* list of moves */ + int initial_size; + int result; + + initial_size = table->keys - table->isolated; + +#ifdef DD_DEBUG + assert(table->subtables[x].keys > 0); +#endif + + moves = NULL; + + if (cuddNextLow(table,x) < x_low) { + if (cuddNextHigh(table,x) > x_high) return(1); + moves = ddJumpingDown(table,x,x_high,initial_size); + /* after that point x --> x_high unless early termination */ + if (moves == NULL) goto ddJumpingAuxOutOfMem; + /* move backward and stop at best position or accept uphill move */ + result = siftBackwardProb(table,moves,initial_size,temp); + if (!result) goto ddJumpingAuxOutOfMem; + } else if (cuddNextHigh(table,x) > x_high) { + moves = ddJumpingUp(table,x,x_low,initial_size); + /* after that point x --> x_low unless early termination */ + if (moves == NULL) goto ddJumpingAuxOutOfMem; + /* move backward and stop at best position or accept uphill move */ + result = siftBackwardProb(table,moves,initial_size,temp); + if (!result) goto ddJumpingAuxOutOfMem; + } else { + (void) fprintf(table->err,"Unexpected condition in ddJumping\n"); + goto ddJumpingAuxOutOfMem; + } + while (moves != NULL) { + move = moves->next; + cuddDeallocMove(table, moves); + moves = move; + } + return(1); + + ddJumpingAuxOutOfMem: + while (moves != NULL) { + move = moves->next; + cuddDeallocMove(table, moves); + moves = move; + } + return(0); + +} /* end of ddJumpingAux */ + + +/**Function******************************************************************** + + Synopsis [This function is for jumping up.] + + Description [This is a simplified version of ddSiftingUp. It does not + use lower bounding. Returns the set of moves in case of success; NULL + if memory is full.] + + SideEffects [None] + + SeeAlso [] + +******************************************************************************/ +static Move * +ddJumpingUp( + DdManager * table, + int x, + int x_low, + int initial_size) +{ + Move *moves; + Move *move; + int y; + int size; + int limit_size = initial_size; + + moves = NULL; + y = cuddNextLow(table,x); + while (y >= x_low) { + size = cuddSwapInPlace(table,y,x); + if (size == 0) goto ddJumpingUpOutOfMem; + move = (Move *)cuddDynamicAllocNode(table); + if (move == NULL) goto ddJumpingUpOutOfMem; + move->x = y; + move->y = x; + move->size = size; + move->next = moves; + moves = move; + if ((double) size > table->maxGrowth * (double) limit_size) { + break; + } else if (size < limit_size) { + limit_size = size; + } + x = y; + y = cuddNextLow(table,x); + } + return(moves); + + ddJumpingUpOutOfMem: + while (moves != NULL) { + move = moves->next; + cuddDeallocMove(table, moves); + moves = move; + } + return(NULL); + +} /* end of ddJumpingUp */ + + +/**Function******************************************************************** + + Synopsis [This function is for jumping down.] + + Description [This is a simplified version of ddSiftingDown. It does not + use lower bounding. Returns the set of moves in case of success; NULL + if memory is full.] + + SideEffects [None] + + SeeAlso [] + +******************************************************************************/ +static Move * +ddJumpingDown( + DdManager * table, + int x, + int x_high, + int initial_size) +{ + Move *moves; + Move *move; + int y; + int size; + int limit_size = initial_size; + + moves = NULL; + y = cuddNextHigh(table,x); + while (y <= x_high) { + size = cuddSwapInPlace(table,x,y); + if (size == 0) goto ddJumpingDownOutOfMem; + move = (Move *)cuddDynamicAllocNode(table); + if (move == NULL) goto ddJumpingDownOutOfMem; + move->x = x; + move->y = y; + move->size = size; + move->next = moves; + moves = move; + if ((double) size > table->maxGrowth * (double) limit_size) { + break; + } else if (size < limit_size) { + limit_size = size; + } + x = y; + y = cuddNextHigh(table,x); + } + return(moves); + + ddJumpingDownOutOfMem: + while (moves != NULL) { + move = moves->next; + cuddDeallocMove(table, moves); + moves = move; + } + return(NULL); + +} /* end of ddJumpingDown */ + + +/**Function******************************************************************** + + Synopsis [Returns the DD to the best position encountered during + sifting if there was improvement.] + + Description [Otherwise, "tosses a coin" to decide whether to keep + the current configuration or return the DD to the original + one. Returns 1 in case of success; 0 otherwise.] + + SideEffects [None] + + SeeAlso [] + +******************************************************************************/ +static int +siftBackwardProb( + DdManager * table, + Move * moves, + int size, + double temp) +{ + Move *move; + int res; + int best_size = size; + double coin, threshold; + + /* Look for best size during the last sifting */ + for (move = moves; move != NULL; move = move->next) { + if (move->size < best_size) { + best_size = move->size; + } + } + + /* If best_size equals size, the last sifting did not produce any + ** improvement. We now toss a coin to decide whether to retain + ** this change or not. + */ + if (best_size == size) { + coin = random_generator(); +#ifdef DD_STATS + tosses++; +#endif + threshold = exp(-((double)(table->keys - table->isolated - size))/temp); + if (coin < threshold) { +#ifdef DD_STATS + acceptances++; +#endif + return(1); + } + } + + /* Either there was improvement, or we have decided not to + ** accept the uphill move. Go to best position. + */ + res = table->keys - table->isolated; + for (move = moves; move != NULL; move = move->next) { + if (res == best_size) return(1); + res = cuddSwapInPlace(table,(int)move->x,(int)move->y); + if (!res) return(0); + } + + return(1); + +} /* end of sift_backward_prob */ + + +/**Function******************************************************************** + + Synopsis [Copies the current variable order to array.] + + Description [Copies the current variable order to array. + At the same time inverts the permutation.] + + SideEffects [None] + + SeeAlso [] + +******************************************************************************/ +static void +copyOrder( + DdManager * table, + int * array, + int lower, + int upper) +{ + int i; + int nvars; + + nvars = upper - lower + 1; + for (i = 0; i < nvars; i++) { + array[i] = table->invperm[i+lower]; + } + +} /* end of copyOrder */ + + +/**Function******************************************************************** + + Synopsis [Restores the variable order in array by a series of sifts up.] + + Description [Restores the variable order in array by a series of sifts up. + Returns 1 in case of success; 0 otherwise.] + + SideEffects [None] + + SeeAlso [] + +******************************************************************************/ +static int +restoreOrder( + DdManager * table, + int * array, + int lower, + int upper) +{ + int i, x, y, size; + int nvars = upper - lower + 1; + + for (i = 0; i < nvars; i++) { + x = table->perm[array[i]]; +#ifdef DD_DEBUG + assert(x >= lower && x <= upper); +#endif + y = cuddNextLow(table,x); + while (y >= i + lower) { + size = cuddSwapInPlace(table,y,x); + if (size == 0) return(0); + x = y; + y = cuddNextLow(table,x); + } + } + + return(1); + +} /* end of restoreOrder */ + +/**CFile*********************************************************************** + + FileName [cuddAPI.c] + + PackageName [cudd] + + Synopsis [Application interface functions.] + + Description [External procedures included in this module: +
    +
  • Cudd_addNewVar() +
  • Cudd_addNewVarAtLevel() +
  • Cudd_bddNewVar() +
  • Cudd_bddNewVarAtLevel() +
  • Cudd_addIthVar() +
  • Cudd_bddIthVar() +
  • Cudd_zddIthVar() +
  • Cudd_zddVarsFromBddVars() +
  • Cudd_addConst() +
  • Cudd_IsNonConstant() +
  • Cudd_ReadStartTime() +
  • Cudd_ReadElapsedTime() +
  • Cudd_SetStartTime() +
  • Cudd_ResetStartTime() +
  • Cudd_ReadTimeLimit() +
  • Cudd_SetTimeLimit() +
  • Cudd_UpdateTimeLimit() +
  • Cudd_IncreaseTimeLimit() +
  • Cudd_UnsetTimeLimit() +
  • Cudd_TimeLimited() +
  • Cudd_AutodynEnable() +
  • Cudd_AutodynDisable() +
  • Cudd_ReorderingStatus() +
  • Cudd_AutodynEnableZdd() +
  • Cudd_AutodynDisableZdd() +
  • Cudd_ReorderingStatusZdd() +
  • Cudd_zddRealignmentEnabled() +
  • Cudd_zddRealignEnable() +
  • Cudd_zddRealignDisable() +
  • Cudd_bddRealignmentEnabled() +
  • Cudd_bddRealignEnable() +
  • Cudd_bddRealignDisable() +
  • Cudd_ReadOne() +
  • Cudd_ReadZddOne() +
  • Cudd_ReadZero() +
  • Cudd_ReadLogicZero() +
  • Cudd_ReadPlusInfinity() +
  • Cudd_ReadMinusInfinity() +
  • Cudd_ReadBackground() +
  • Cudd_SetBackground() +
  • Cudd_ReadCacheSlots() +
  • Cudd_ReadCacheUsedSlots() +
  • Cudd_ReadCacheLookUps() +
  • Cudd_ReadCacheHits() +
  • Cudd_ReadMinHit() +
  • Cudd_SetMinHit() +
  • Cudd_ReadLooseUpTo() +
  • Cudd_SetLooseUpTo() +
  • Cudd_ReadMaxCache() +
  • Cudd_ReadMaxCacheHard() +
  • Cudd_SetMaxCacheHard() +
  • Cudd_ReadSize() +
  • Cudd_ReadSlots() +
  • Cudd_ReadUsedSlots() +
  • Cudd_ExpectedUsedSlots() +
  • Cudd_ReadKeys() +
  • Cudd_ReadDead() +
  • Cudd_ReadMinDead() +
  • Cudd_ReadReorderings() +
  • Cudd_ReadMaxReorderings() +
  • Cudd_SetMaxReorderings() +
  • Cudd_ReadReorderingTime() +
  • Cudd_ReadGarbageCollections() +
  • Cudd_ReadGarbageCollectionTime() +
  • Cudd_ReadNodesFreed() +
  • Cudd_ReadNodesDropped() +
  • Cudd_ReadUniqueLookUps() +
  • Cudd_ReadUniqueLinks() +
  • Cudd_ReadSiftMaxVar() +
  • Cudd_SetSiftMaxVar() +
  • Cudd_ReadMaxGrowth() +
  • Cudd_SetMaxGrowth() +
  • Cudd_ReadMaxGrowthAlternate() +
  • Cudd_SetMaxGrowthAlternate() +
  • Cudd_ReadReorderingCycle() +
  • Cudd_SetReorderingCycle() +
  • Cudd_ReadTree() +
  • Cudd_SetTree() +
  • Cudd_FreeTree() +
  • Cudd_ReadZddTree() +
  • Cudd_SetZddTree() +
  • Cudd_FreeZddTree() +
  • Cudd_NodeReadIndex() +
  • Cudd_ReadPerm() +
  • Cudd_ReadInvPerm() +
  • Cudd_ReadVars() +
  • Cudd_ReadEpsilon() +
  • Cudd_SetEpsilon() +
  • Cudd_ReadGroupCheck() +
  • Cudd_SetGroupcheck() +
  • Cudd_GarbageCollectionEnabled() +
  • Cudd_EnableGarbageCollection() +
  • Cudd_DisableGarbageCollection() +
  • Cudd_DeadAreCounted() +
  • Cudd_TurnOnCountDead() +
  • Cudd_TurnOffCountDead() +
  • Cudd_ReadRecomb() +
  • Cudd_SetRecomb() +
  • Cudd_ReadSymmviolation() +
  • Cudd_SetSymmviolation() +
  • Cudd_ReadArcviolation() +
  • Cudd_SetArcviolation() +
  • Cudd_ReadPopulationSize() +
  • Cudd_SetPopulationSize() +
  • Cudd_ReadNumberXovers() +
  • Cudd_SetNumberXovers() +
  • Cudd_ReadOrderRandomization() +
  • Cudd_SetOrderRandomization() +
  • Cudd_ReadMemoryInUse() +
  • Cudd_PrintInfo() +
  • Cudd_ReadPeakNodeCount() +
  • Cudd_ReadPeakLiveNodeCount() +
  • Cudd_ReadNodeCount() +
  • Cudd_zddReadNodeCount() +
  • Cudd_AddHook() +
  • Cudd_RemoveHook() +
  • Cudd_IsInHook() +
  • Cudd_StdPreReordHook() +
  • Cudd_StdPostReordHook() +
  • Cudd_EnableReorderingReporting() +
  • Cudd_DisableReorderingReporting() +
  • Cudd_ReorderingReporting() +
  • Cudd_PrintGroupedOrder() +
  • Cudd_EnableOrderingMonitoring() +
  • Cudd_DisableOrderingMonitoring() +
  • Cudd_OrderingMonitoring() +
  • Cudd_ReadErrorCode() +
  • Cudd_ClearErrorCode() +
  • Cudd_ReadStdout() +
  • Cudd_SetStdout() +
  • Cudd_ReadStderr() +
  • Cudd_SetStderr() +
  • Cudd_ReadNextReordering() +
  • Cudd_SetNextReordering() +
  • Cudd_ReadSwapSteps() +
  • Cudd_ReadMaxLive() +
  • Cudd_SetMaxLive() +
  • Cudd_ReadMaxMemory() +
  • Cudd_SetMaxMemory() +
  • Cudd_bddBindVar() +
  • Cudd_bddUnbindVar() +
  • Cudd_bddVarIsBound() +
  • Cudd_bddSetPiVar() +
  • Cudd_bddSetPsVar() +
  • Cudd_bddSetNsVar() +
  • Cudd_bddIsPiVar() +
  • Cudd_bddIsPsVar() +
  • Cudd_bddIsNsVar() +
  • Cudd_bddSetPairIndex() +
  • Cudd_bddReadPairIndex() +
  • Cudd_bddSetVarToBeGrouped() +
  • Cudd_bddSetVarHardGroup() +
  • Cudd_bddResetVarToBeGrouped() +
  • Cudd_bddIsVarToBeGrouped() +
  • Cudd_bddSetVarToBeUngrouped() +
  • Cudd_bddIsVarToBeUngrouped() +
  • Cudd_bddIsVarHardGroup() +
+ Static procedures included in this module: +
    +
  • fixVarTree() +
] + + SeeAlso [] + + Author [Fabio Somenzi] + + Copyright [Copyright (c) 1995-2012, Regents of the University of Colorado + + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + Neither the name of the University of Colorado nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE.] + +******************************************************************************/ + + +/*---------------------------------------------------------------------------*/ +/* Constant declarations */ +/*---------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------*/ +/* Stucture declarations */ +/*---------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------*/ +/* Type declarations */ +/*---------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------*/ +/* Variable declarations */ +/*---------------------------------------------------------------------------*/ + +//#ifndef lint +//static char rcsid[] DD_UNUSED = "$Id: cuddAPI.c,v 1.64 2012/02/05 01:07:18 fabio Exp $"; +//#endif + +/*---------------------------------------------------------------------------*/ +/* Macro declarations */ +/*---------------------------------------------------------------------------*/ + +/**AutomaticStart*************************************************************/ + +/*---------------------------------------------------------------------------*/ +/* Static function prototypes */ +/*---------------------------------------------------------------------------*/ + +static void fixVarTree (MtrNode *treenode, int *perm, int size); +static int addMultiplicityGroups (DdManager *dd, MtrNode *treenode, int multiplicity, char *vmask, char *lmask); + +/**AutomaticEnd***************************************************************/ + + +/**Function******************************************************************** + + Synopsis [Returns the BDD variable with index i.] + + Description [Retrieves the BDD variable with index i if it already + exists, or creates a new BDD variable. Returns a pointer to the + variable if successful; NULL otherwise.] + + SideEffects [None] + + SeeAlso [Cudd_bddNewVar Cudd_addIthVar Cudd_bddNewVarAtLevel + Cudd_ReadVars] + +******************************************************************************/ +DdNode * +Cudd_bddIthVar( + DdManager * dd, + int i) +{ + DdNode *res; + + if ((unsigned int) i >= CUDD_MAXINDEX - 1) return(NULL); + if (i < dd->size) { + res = dd->vars[i]; + } else { + res = cuddUniqueInter(dd,i,dd->one,Cudd_Not(dd->one)); + } + + return(res); + +} /* end of Cudd_bddIthVar */ + + +/**Function******************************************************************** + + Synopsis [Returns the one constant of the manager.] + + Description [Returns the one constant of the manager. The one + constant is common to ADDs and BDDs.] + + SideEffects [None] + + SeeAlso [Cudd_ReadZero Cudd_ReadLogicZero Cudd_ReadZddOne] + +******************************************************************************/ +DdNode * +Cudd_ReadOne( + DdManager * dd) +{ + return(dd->one); + +} /* end of Cudd_ReadOne */ + + +/**Function******************************************************************** + + Synopsis [Returns the logic zero constant of the manager.] + + Description [Returns the zero constant of the manager. The logic zero + constant is the complement of the one constant, and is distinct from + the arithmetic zero.] + + SideEffects [None] + + SeeAlso [Cudd_ReadOne Cudd_ReadZero] + +******************************************************************************/ +DdNode * +Cudd_ReadLogicZero( + DdManager * dd) +{ + return(Cudd_Not(DD_ONE(dd))); + +} /* end of Cudd_ReadLogicZero */ + + + +/**Function******************************************************************** + + Synopsis [Sets the hit rate that causes resizinig of the computed + table.] + + Description [Sets the minHit parameter of the manager. This + parameter controls the resizing of the computed table. If the hit + rate is larger than the specified value, and the cache is not + already too large, then its size is doubled.] + + SideEffects [None] + + SeeAlso [Cudd_ReadMinHit] + +******************************************************************************/ +void +Cudd_SetMinHit( + DdManager * dd, + unsigned int hr) +{ + /* Internally, the package manipulates the ratio of hits to + ** misses instead of the ratio of hits to accesses. */ + dd->minHit = (double) hr / (100.0 - (double) hr); + +} /* end of Cudd_SetMinHit */ + + + +/**Function******************************************************************** + + Synopsis [Returns the number of BDD variables in existance.] + + Description [] + + SideEffects [None] + + SeeAlso [Cudd_ReadZddSize] + +******************************************************************************/ +int +Cudd_ReadSize( + DdManager * dd) +{ + return(dd->size); + +} /* end of Cudd_ReadSize */ + + +/**Function******************************************************************** + + Synopsis [Frees the variable group tree of the manager.] + + Description [] + + SideEffects [None] + + SeeAlso [Cudd_SetTree Cudd_ReadTree Cudd_FreeZddTree] + +******************************************************************************/ +void +Cudd_FreeTree( + DdManager * dd) +{ + if (dd->tree != NULL) { + Mtr_FreeTree(dd->tree); + dd->tree = NULL; + } + return; + +} /* end of Cudd_FreeTree */ + + +/**Function******************************************************************** + + Synopsis [Frees the variable group tree of the manager.] + + Description [] + + SideEffects [None] + + SeeAlso [Cudd_SetZddTree Cudd_ReadZddTree Cudd_FreeTree] + +******************************************************************************/ +void +Cudd_FreeZddTree( + DdManager * dd) +{ + if (dd->treeZ != NULL) { + Mtr_FreeTree(dd->treeZ); + dd->treeZ = NULL; + } + return; + +} /* end of Cudd_FreeZddTree */ + + +/**Function******************************************************************** + + Synopsis [Returns the current position of the i-th variable in the + order.] + + Description [Returns the current position of the i-th variable in + the order. If the index is CUDD_CONST_INDEX, returns + CUDD_CONST_INDEX; otherwise, if the index is out of bounds returns + -1.] + + SideEffects [None] + + SeeAlso [Cudd_ReadInvPerm Cudd_ReadPermZdd] + +******************************************************************************/ +int +Cudd_ReadPerm( + DdManager * dd, + int i) +{ + if (i == CUDD_CONST_INDEX) return(CUDD_CONST_INDEX); + if (i < 0 || i >= dd->size) return(-1); + return(dd->perm[i]); + +} /* end of Cudd_ReadPerm */ + + +/**Function******************************************************************** + + Synopsis [Reads the epsilon parameter of the manager.] + + Description [Reads the epsilon parameter of the manager. The epsilon + parameter control the comparison between floating point numbers.] + + SideEffects [None] + + SeeAlso [Cudd_SetEpsilon] + +******************************************************************************/ +CUDD_VALUE_TYPE +Cudd_ReadEpsilon( + DdManager * dd) +{ + return(dd->epsilon); + +} /* end of Cudd_ReadEpsilon */ + + +/**Function******************************************************************** + + Synopsis [Sets the epsilon parameter of the manager to ep.] + + Description [Sets the epsilon parameter of the manager to ep. The epsilon + parameter control the comparison between floating point numbers.] + + SideEffects [None] + + SeeAlso [Cudd_ReadEpsilon] + +******************************************************************************/ +void +Cudd_SetEpsilon( + DdManager * dd, + CUDD_VALUE_TYPE ep) +{ + dd->epsilon = ep; + +} /* end of Cudd_SetEpsilon */ + + +/**Function******************************************************************** + + Synopsis [Returns the memory in use by the manager measured in bytes.] + + Description [] + + SideEffects [None] + + SeeAlso [] + +******************************************************************************/ +unsigned long +Cudd_ReadMemoryInUse( + DdManager * dd) +{ + return(dd->memused); + +} /* end of Cudd_ReadMemoryInUse */ + + +/**Function******************************************************************** + + Synopsis [Reports the number of nodes in BDDs and ADDs.] + + Description [Reports the number of live nodes in BDDs and ADDs. This + number does not include the isolated projection functions and the + unused constants. These nodes that are not counted are not part of + the DDs manipulated by the application.] + + SideEffects [None] + + SeeAlso [Cudd_ReadPeakNodeCount Cudd_zddReadNodeCount] + +******************************************************************************/ +long +Cudd_ReadNodeCount( + DdManager * dd) +{ + long count; + int i; + +#ifndef DD_NO_DEATH_ROW + cuddClearDeathRow(dd); +#endif + + count = (long) (dd->keys - dd->dead); + + /* Count isolated projection functions. Their number is subtracted + ** from the node count because they are not part of the BDDs. + */ + for (i=0; i < dd->size; i++) { + if (dd->vars[i]->ref == 1) count--; + } + /* Subtract from the count the unused constants. */ + if (DD_ZERO(dd)->ref == 1) count--; + if (DD_PLUS_INFINITY(dd)->ref == 1) count--; + if (DD_MINUS_INFINITY(dd)->ref == 1) count--; + + return(count); + +} /* end of Cudd_ReadNodeCount */ + + + +/**Function******************************************************************** + + Synopsis [Removes a function from a hook.] + + Description [Removes a function from a hook. A hook is a list of + application-provided functions called on certain occasions by the + package. Returns 1 if successful; 0 the function was not in the list.] + + SideEffects [None] + + SeeAlso [Cudd_AddHook] + +******************************************************************************/ +int +Cudd_RemoveHook( + DdManager * dd, + DD_HFP f, + Cudd_HookType where) +{ + DdHook **hook, *nextHook; + + switch (where) { + case CUDD_PRE_GC_HOOK: + hook = &(dd->preGCHook); + break; + case CUDD_POST_GC_HOOK: + hook = &(dd->postGCHook); + break; + case CUDD_PRE_REORDERING_HOOK: + hook = &(dd->preReorderingHook); + break; + case CUDD_POST_REORDERING_HOOK: + hook = &(dd->postReorderingHook); + break; + default: + return(0); + } + nextHook = *hook; + while (nextHook != NULL) { + if (nextHook->f == f) { + *hook = nextHook->next; + FREE(nextHook); + return(1); + } + hook = &(nextHook->next); + nextHook = nextHook->next; + } + + return(0); + +} /* end of Cudd_RemoveHook */ + + +/**Function******************************************************************** + + Synopsis [Reads a corresponding pair index for a given index.] + + Description [Reads a corresponding pair index for a given index. + These pair indices are present and next state variable. Returns the + corresponding variable index if the variable exists; -1 otherwise.] + + SideEffects [modifies the manager] + + SeeAlso [Cudd_bddSetPairIndex] + +******************************************************************************/ +int +Cudd_bddReadPairIndex( + DdManager *dd, + int index) +{ + if (index >= dd->size || index < 0) return -1; + return dd->subtables[dd->perm[index]].pairIndex; + +} /* end of Cudd_bddReadPairIndex */ + + +/**Function******************************************************************** + + Synopsis [Checks whether a variable is set to be grouped.] + + Description [Checks whether a variable is set to be grouped. This + function is used for lazy sifting.] + + SideEffects [none] + + SeeAlso [] + +******************************************************************************/ +int +Cudd_bddIsVarToBeGrouped( + DdManager *dd, + int index) +{ + if (index >= dd->size || index < 0) return(-1); + if (dd->subtables[dd->perm[index]].varToBeGrouped == CUDD_LAZY_UNGROUP) + return(0); + else + return(dd->subtables[dd->perm[index]].varToBeGrouped); + +} /* end of Cudd_bddIsVarToBeGrouped */ + + + +/**Function******************************************************************** + + Synopsis [Checks whether a variable is set to be ungrouped.] + + Description [Checks whether a variable is set to be ungrouped. This + function is used for lazy sifting. Returns 1 if the variable is marked + to be ungrouped; 0 if the variable exists, but it is not marked to be + ungrouped; -1 if the variable does not exist.] + + SideEffects [none] + + SeeAlso [Cudd_bddSetVarToBeUngrouped] + +******************************************************************************/ +int +Cudd_bddIsVarToBeUngrouped( + DdManager *dd, + int index) +{ + if (index >= dd->size || index < 0) return(-1); + return dd->subtables[dd->perm[index]].varToBeGrouped == CUDD_LAZY_UNGROUP; + +} /* end of Cudd_bddIsVarToBeGrouped */ + +/*---------------------------------------------------------------------------*/ +/* Definition of internal functions */ +/*---------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------*/ +/* Definition of static functions */ +/*---------------------------------------------------------------------------*/ + + +/**Function******************************************************************** + + Synopsis [Fixes a variable group tree.] + + Description [] + + SideEffects [Changes the variable group tree.] + + SeeAlso [] + +******************************************************************************/ +static void +fixVarTree( + MtrNode * treenode, + int * perm, + int size) +{ + treenode->index = treenode->low; + treenode->low = ((int) treenode->index < size) ? + perm[treenode->index] : treenode->index; + if (treenode->child != NULL) + fixVarTree(treenode->child, perm, size); + if (treenode->younger != NULL) + fixVarTree(treenode->younger, perm, size); + return; + +} /* end of fixVarTree */ + + +/**Function******************************************************************** + + Synopsis [Adds multiplicity groups to a ZDD variable group tree.] + + Description [Adds multiplicity groups to a ZDD variable group tree. + Returns 1 if successful; 0 otherwise. This function creates the groups + for set of ZDD variables (whose cardinality is given by parameter + multiplicity) that are created for each BDD variable in + Cudd_zddVarsFromBddVars. The crux of the matter is to determine the index + each new group. (The index of the first variable in the group.) + We first build all the groups for the children of a node, and then deal + with the ZDD variables that are directly attached to the node. The problem + for these is that the tree itself does not provide information on their + position inside the group. While we deal with the children of the node, + therefore, we keep track of all the positions they occupy. The remaining + positions in the tree can be freely used. Also, we keep track of all the + variables placed in the children. All the remaining variables are directly + attached to the group. We can then place any pair of variables not yet + grouped in any pair of available positions in the node.] + + SideEffects [Changes the variable group tree.] + + SeeAlso [Cudd_zddVarsFromBddVars] + +******************************************************************************/ +static int +addMultiplicityGroups( + DdManager *dd /* manager */, + MtrNode *treenode /* current tree node */, + int multiplicity /* how many ZDD vars per BDD var */, + char *vmask /* variable pairs for which a group has been already built */, + char *lmask /* levels for which a group has already been built*/) +{ + int startV, stopV, startL; + int i, j; + MtrNode *auxnode = treenode; + + while (auxnode != NULL) { + if (auxnode->child != NULL) { + addMultiplicityGroups(dd,auxnode->child,multiplicity,vmask,lmask); + } + /* Build remaining groups. */ + startV = dd->permZ[auxnode->index] / multiplicity; + startL = auxnode->low / multiplicity; + stopV = startV + auxnode->size / multiplicity; + /* Walk down vmask starting at startV and build missing groups. */ + for (i = startV, j = startL; i < stopV; i++) { + if (vmask[i] == 0) { + MtrNode *node; + while (lmask[j] == 1) j++; + node = Mtr_MakeGroup(auxnode, j * multiplicity, multiplicity, + MTR_FIXED); + if (node == NULL) { + return(0); + } + node->index = dd->invpermZ[i * multiplicity]; + vmask[i] = 1; + lmask[j] = 1; + } + } + auxnode = auxnode->younger; + } + return(1); + +} /* end of addMultiplicityGroups */ + +/**CFile*********************************************************************** + + FileName [cuddBddAbs.c] + + PackageName [cudd] + + Synopsis [Quantification functions for BDDs.] + + Description [External procedures included in this module: +
    +
  • Cudd_bddExistAbstract() +
  • Cudd_bddExistAbstractLimit() +
  • Cudd_bddXorExistAbstract() +
  • Cudd_bddUnivAbstract() +
  • Cudd_bddBooleanDiff() +
  • Cudd_bddVarIsDependent() +
+ Internal procedures included in this module: +
    +
  • cuddBddExistAbstractRecur() +
  • cuddBddXorExistAbstractRecur() +
  • cuddBddBooleanDiffRecur() +
+ Static procedures included in this module: +
    +
  • bddCheckPositiveCube() +
+ ] + + Author [Fabio Somenzi] + + Copyright [Copyright (c) 1995-2012, Regents of the University of Colorado + + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + Neither the name of the University of Colorado nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE.] + +******************************************************************************/ + +/*---------------------------------------------------------------------------*/ +/* Constant declarations */ +/*---------------------------------------------------------------------------*/ + + +/*---------------------------------------------------------------------------*/ +/* Stucture declarations */ +/*---------------------------------------------------------------------------*/ + + +/*---------------------------------------------------------------------------*/ +/* Type declarations */ +/*---------------------------------------------------------------------------*/ + + +/*---------------------------------------------------------------------------*/ +/* Variable declarations */ +/*---------------------------------------------------------------------------*/ + +//#ifndef lint +//static char rcsid[] DD_UNUSED = "$Id: cuddBddAbs.c,v 1.28 2012/02/05 01:07:18 fabio Exp $"; +//#endif + +/*---------------------------------------------------------------------------*/ +/* Macro declarations */ +/*---------------------------------------------------------------------------*/ + + +/**AutomaticStart*************************************************************/ + +/*---------------------------------------------------------------------------*/ +/* Static function prototypes */ +/*---------------------------------------------------------------------------*/ + +static int bddCheckPositiveCube (DdManager *manager, DdNode *cube); + +/**AutomaticEnd***************************************************************/ + + +/*---------------------------------------------------------------------------*/ +/* Definition of exported functions */ +/*---------------------------------------------------------------------------*/ + + +/**Function******************************************************************** + + Synopsis [Existentially abstracts all the variables in cube from f.] + + Description [Existentially abstracts all the variables in cube from f. + Returns the abstracted BDD if successful; NULL otherwise.] + + SideEffects [None] + + SeeAlso [Cudd_bddUnivAbstract Cudd_addExistAbstract] + +******************************************************************************/ +DdNode * +Cudd_bddExistAbstract( + DdManager * manager, + DdNode * f, + DdNode * cube) +{ + DdNode *res; + + if (bddCheckPositiveCube(manager, cube) == 0) { + (void) fprintf(manager->err, + "Error: Can only abstract positive cubes\n"); + manager->errorCode = CUDD_INVALID_ARG; + return(NULL); + } + + do { + manager->reordered = 0; + res = cuddBddExistAbstractRecur(manager, f, cube); + } while (manager->reordered == 1); + + return(res); + +} /* end of Cudd_bddExistAbstract */ + +/**Function******************************************************************** + + Synopsis [Checks whether a variable is dependent on others in a + function.] + + Description [Checks whether a variable is dependent on others in a + function. Returns 1 if the variable is dependent; 0 otherwise. No + new nodes are created.] + + SideEffects [None] + + SeeAlso [] + +******************************************************************************/ +int +Cudd_bddVarIsDependent( + DdManager *dd, /* manager */ + DdNode *f, /* function */ + DdNode *var /* variable */) +{ + DdNode *F, *res, *zero, *ft, *fe; + unsigned topf, level; + DD_CTFP cacheOp; + int retval; + + zero = Cudd_Not(DD_ONE(dd)); + if (Cudd_IsConstant(f)) return(f == zero); + + /* From now on f is not constant. */ + F = Cudd_Regular(f); + topf = (unsigned) dd->perm[F->index]; + level = (unsigned) dd->perm[var->index]; + + /* Check terminal case. If topf > index of var, f does not depend on var. + ** Therefore, var is not dependent in f. */ + if (topf > level) { + return(0); + } + + cacheOp = (DD_CTFP) Cudd_bddVarIsDependent; + res = cuddCacheLookup2(dd,cacheOp,f,var); + if (res != NULL) { + return(res != zero); + } + + /* Compute cofactors. */ + ft = Cudd_NotCond(cuddT(F), f != F); + fe = Cudd_NotCond(cuddE(F), f != F); + + if (topf == level) { + retval = Cudd_bddLeq(dd,ft,Cudd_Not(fe)); + } else { + retval = Cudd_bddVarIsDependent(dd,ft,var) && + Cudd_bddVarIsDependent(dd,fe,var); + } + + cuddCacheInsert2(dd,cacheOp,f,var,Cudd_NotCond(zero,retval)); + + return(retval); + +} /* Cudd_bddVarIsDependent */ + + +/*---------------------------------------------------------------------------*/ +/* Definition of internal functions */ +/*---------------------------------------------------------------------------*/ + + +/**Function******************************************************************** + + Synopsis [Performs the recursive steps of Cudd_bddExistAbstract.] + + Description [Performs the recursive steps of Cudd_bddExistAbstract. + Returns the BDD obtained by abstracting the variables + of cube from f if successful; NULL otherwise. It is also used by + Cudd_bddUnivAbstract.] + + SideEffects [None] + + SeeAlso [Cudd_bddExistAbstract Cudd_bddUnivAbstract] + +******************************************************************************/ +DdNode * +cuddBddExistAbstractRecur( + DdManager * manager, + DdNode * f, + DdNode * cube) +{ + DdNode *F, *T, *E, *res, *res1, *res2, *one; + + statLine(manager); + one = DD_ONE(manager); + F = Cudd_Regular(f); + + /* Cube is guaranteed to be a cube at this point. */ + if (cube == one || F == one) { + return(f); + } + /* From now on, f and cube are non-constant. */ + + /* Abstract a variable that does not appear in f. */ + while (manager->perm[F->index] > manager->perm[cube->index]) { + cube = cuddT(cube); + if (cube == one) return(f); + } + + /* Check the cache. */ + if (F->ref != 1 && (res = cuddCacheLookup2(manager, Cudd_bddExistAbstract, f, cube)) != NULL) { + return(res); + } + + /* Compute the cofactors of f. */ + T = cuddT(F); E = cuddE(F); + if (f != F) { + T = Cudd_Not(T); E = Cudd_Not(E); + } + + /* If the two indices are the same, so are their levels. */ + if (F->index == cube->index) { + if (T == one || E == one || T == Cudd_Not(E)) { + return(one); + } + res1 = cuddBddExistAbstractRecur(manager, T, cuddT(cube)); + if (res1 == NULL) return(NULL); + if (res1 == one) { + if (F->ref != 1) + cuddCacheInsert2(manager, Cudd_bddExistAbstract, f, cube, one); + return(one); + } + cuddRef(res1); + res2 = cuddBddExistAbstractRecur(manager, E, cuddT(cube)); + if (res2 == NULL) { + Cudd_IterDerefBdd(manager,res1); + return(NULL); + } + cuddRef(res2); + res = cuddBddAndRecur(manager, Cudd_Not(res1), Cudd_Not(res2)); + if (res == NULL) { + Cudd_IterDerefBdd(manager, res1); + Cudd_IterDerefBdd(manager, res2); + return(NULL); + } + res = Cudd_Not(res); + cuddRef(res); + Cudd_IterDerefBdd(manager, res1); + Cudd_IterDerefBdd(manager, res2); + if (F->ref != 1) + cuddCacheInsert2(manager, Cudd_bddExistAbstract, f, cube, res); + cuddDeref(res); + return(res); + } else { /* if (cuddI(manager,F->index) < cuddI(manager,cube->index)) */ + res1 = cuddBddExistAbstractRecur(manager, T, cube); + if (res1 == NULL) return(NULL); + cuddRef(res1); + res2 = cuddBddExistAbstractRecur(manager, E, cube); + if (res2 == NULL) { + Cudd_IterDerefBdd(manager, res1); + return(NULL); + } + cuddRef(res2); + /* ITE takes care of possible complementation of res1 and of the + ** case in which res1 == res2. */ + res = cuddBddIteRecur(manager, manager->vars[F->index], res1, res2); + if (res == NULL) { + Cudd_IterDerefBdd(manager, res1); + Cudd_IterDerefBdd(manager, res2); + return(NULL); + } + cuddDeref(res1); + cuddDeref(res2); + if (F->ref != 1) + cuddCacheInsert2(manager, Cudd_bddExistAbstract, f, cube, res); + return(res); + } + +} /* end of cuddBddExistAbstractRecur */ + + +/**Function******************************************************************** + + Synopsis [Takes the exclusive OR of two BDDs and simultaneously abstracts the + variables in cube.] + + Description [Takes the exclusive OR of two BDDs and simultaneously abstracts + the variables in cube. The variables are existentially abstracted. Returns a + pointer to the result is successful; NULL otherwise.] + + SideEffects [None] + + SeeAlso [Cudd_bddAndAbstract] + +******************************************************************************/ +DdNode * +cuddBddXorExistAbstractRecur( + DdManager * manager, + DdNode * f, + DdNode * g, + DdNode * cube) +{ + DdNode *F, *fv, *fnv, *G, *gv, *gnv; + DdNode *one, *zero, *r, *t, *e, *Cube; + unsigned int topf, topg, topcube, top, index; + + statLine(manager); + one = DD_ONE(manager); + zero = Cudd_Not(one); + + /* Terminal cases. */ + if (f == g) { + return(zero); + } + if (f == Cudd_Not(g)) { + return(one); + } + if (cube == one) { + return(cuddBddXorRecur(manager, f, g)); + } + if (f == one) { + return(cuddBddExistAbstractRecur(manager, Cudd_Not(g), cube)); + } + if (g == one) { + return(cuddBddExistAbstractRecur(manager, Cudd_Not(f), cube)); + } + if (f == zero) { + return(cuddBddExistAbstractRecur(manager, g, cube)); + } + if (g == zero) { + return(cuddBddExistAbstractRecur(manager, f, cube)); + } + + /* At this point f, g, and cube are not constant. */ + + if (f > g) { /* Try to increase cache efficiency. */ + DdNode *tmp = f; + f = g; + g = tmp; + } + + /* Check cache. */ + r = cuddCacheLookup(manager, DD_BDD_XOR_EXIST_ABSTRACT_TAG, f, g, cube); + if (r != NULL) { + return(r); + } + + /* Here we can skip the use of cuddI, because the operands are known + ** to be non-constant. + */ + F = Cudd_Regular(f); + topf = manager->perm[F->index]; + G = Cudd_Regular(g); + topg = manager->perm[G->index]; + top = ddMin(topf, topg); + topcube = manager->perm[cube->index]; + + if (topcube < top) { + return(cuddBddXorExistAbstractRecur(manager, f, g, cuddT(cube))); + } + /* Now, topcube >= top. */ + + if (topf == top) { + index = F->index; + fv = cuddT(F); + fnv = cuddE(F); + if (Cudd_IsComplement(f)) { + fv = Cudd_Not(fv); + fnv = Cudd_Not(fnv); + } + } else { + index = G->index; + fv = fnv = f; + } + + if (topg == top) { + gv = cuddT(G); + gnv = cuddE(G); + if (Cudd_IsComplement(g)) { + gv = Cudd_Not(gv); + gnv = Cudd_Not(gnv); + } + } else { + gv = gnv = g; + } + + if (topcube == top) { + Cube = cuddT(cube); + } else { + Cube = cube; + } + + t = cuddBddXorExistAbstractRecur(manager, fv, gv, Cube); + if (t == NULL) return(NULL); + + /* Special case: 1 OR anything = 1. Hence, no need to compute + ** the else branch if t is 1. + */ + if (t == one && topcube == top) { + cuddCacheInsert(manager, DD_BDD_XOR_EXIST_ABSTRACT_TAG, f, g, cube, one); + return(one); + } + cuddRef(t); + + e = cuddBddXorExistAbstractRecur(manager, fnv, gnv, Cube); + if (e == NULL) { + Cudd_IterDerefBdd(manager, t); + return(NULL); + } + cuddRef(e); + + if (topcube == top) { /* abstract */ + r = cuddBddAndRecur(manager, Cudd_Not(t), Cudd_Not(e)); + if (r == NULL) { + Cudd_IterDerefBdd(manager, t); + Cudd_IterDerefBdd(manager, e); + return(NULL); + } + r = Cudd_Not(r); + cuddRef(r); + Cudd_IterDerefBdd(manager, t); + Cudd_IterDerefBdd(manager, e); + cuddDeref(r); + } else if (t == e) { + r = t; + cuddDeref(t); + cuddDeref(e); + } else { + if (Cudd_IsComplement(t)) { + r = cuddUniqueInter(manager,(int)index,Cudd_Not(t),Cudd_Not(e)); + if (r == NULL) { + Cudd_IterDerefBdd(manager, t); + Cudd_IterDerefBdd(manager, e); + return(NULL); + } + r = Cudd_Not(r); + } else { + r = cuddUniqueInter(manager,(int)index,t,e); + if (r == NULL) { + Cudd_IterDerefBdd(manager, t); + Cudd_IterDerefBdd(manager, e); + return(NULL); + } + } + cuddDeref(e); + cuddDeref(t); + } + cuddCacheInsert(manager, DD_BDD_XOR_EXIST_ABSTRACT_TAG, f, g, cube, r); + return (r); + +} /* end of cuddBddXorExistAbstractRecur */ + + +/**Function******************************************************************** + + Synopsis [Performs the recursive steps of Cudd_bddBoleanDiff.] + + Description [Performs the recursive steps of Cudd_bddBoleanDiff. + Returns the BDD obtained by XORing the cofactors of f with respect to + var if successful; NULL otherwise. Exploits the fact that dF/dx = + dF'/dx.] + + SideEffects [None] + + SeeAlso [] + +******************************************************************************/ +DdNode * +cuddBddBooleanDiffRecur( + DdManager * manager, + DdNode * f, + DdNode * var) +{ + DdNode *T, *E, *res, *res1, *res2; + + statLine(manager); + if (cuddI(manager,f->index) > manager->perm[var->index]) { + /* f does not depend on var. */ + return(Cudd_Not(DD_ONE(manager))); + } + + /* From now on, f is non-constant. */ + + /* If the two indices are the same, so are their levels. */ + if (f->index == var->index) { + res = cuddBddXorRecur(manager, cuddT(f), cuddE(f)); + return(res); + } + + /* From now on, cuddI(manager,f->index) < cuddI(manager,cube->index). */ + + /* Check the cache. */ + res = cuddCacheLookup2(manager, cuddBddBooleanDiffRecur, f, var); + if (res != NULL) { + return(res); + } + + /* Compute the cofactors of f. */ + T = cuddT(f); E = cuddE(f); + + res1 = cuddBddBooleanDiffRecur(manager, T, var); + if (res1 == NULL) return(NULL); + cuddRef(res1); + res2 = cuddBddBooleanDiffRecur(manager, Cudd_Regular(E), var); + if (res2 == NULL) { + Cudd_IterDerefBdd(manager, res1); + return(NULL); + } + cuddRef(res2); + /* ITE takes care of possible complementation of res1 and of the + ** case in which res1 == res2. */ + res = cuddBddIteRecur(manager, manager->vars[f->index], res1, res2); + if (res == NULL) { + Cudd_IterDerefBdd(manager, res1); + Cudd_IterDerefBdd(manager, res2); + return(NULL); + } + cuddDeref(res1); + cuddDeref(res2); + cuddCacheInsert2(manager, cuddBddBooleanDiffRecur, f, var, res); + return(res); + +} /* end of cuddBddBooleanDiffRecur */ + + +/*---------------------------------------------------------------------------*/ +/* Definition of static functions */ +/*---------------------------------------------------------------------------*/ + +/**Function******************************************************************** + + Synopsis [Checks whether cube is an BDD representing the product of + positive literals.] + + Description [Returns 1 in case of success; 0 otherwise.] + + SideEffects [None] + +******************************************************************************/ +static int +bddCheckPositiveCube( + DdManager * manager, + DdNode * cube) +{ + if (Cudd_IsComplement(cube)) return(0); + if (cube == DD_ONE(manager)) return(1); + if (cuddIsConstant(cube)) return(0); + if (cuddE(cube) == Cudd_Not(DD_ONE(manager))) { + return(bddCheckPositiveCube(manager, cuddT(cube))); + } + return(0); + +} /* end of bddCheckPositiveCube */ + +/**CFile*********************************************************************** + + FileName [cuddBddIte.c] + + PackageName [cudd] + + Synopsis [BDD ITE function and satellites.] + + Description [External procedures included in this module: +
    +
  • Cudd_bddIte() +
  • Cudd_bddIteLimit() +
  • Cudd_bddIteConstant() +
  • Cudd_bddIntersect() +
  • Cudd_bddAnd() +
  • Cudd_bddAndLimit() +
  • Cudd_bddOr() +
  • Cudd_bddOrLimit() +
  • Cudd_bddNand() +
  • Cudd_bddNor() +
  • Cudd_bddXor() +
  • Cudd_bddXnor() +
  • Cudd_bddXnorLimit() +
  • Cudd_bddLeq() +
+ Internal procedures included in this module: +
    +
  • cuddBddIteRecur() +
  • cuddBddIntersectRecur() +
  • cuddBddAndRecur() +
  • cuddBddXorRecur() +
+ Static procedures included in this module: +
    +
  • bddVarToConst() +
  • bddVarToCanonical() +
  • bddVarToCanonicalSimple() +
] + + SeeAlso [] + + Author [Fabio Somenzi] + + Copyright [Copyright (c) 1995-2012, Regents of the University of Colorado + + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + Neither the name of the University of Colorado nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE.] + +******************************************************************************/ + +/*---------------------------------------------------------------------------*/ +/* Constant declarations */ +/*---------------------------------------------------------------------------*/ + + +/*---------------------------------------------------------------------------*/ +/* Stucture declarations */ +/*---------------------------------------------------------------------------*/ + + +/*---------------------------------------------------------------------------*/ +/* Type declarations */ +/*---------------------------------------------------------------------------*/ + + +/*---------------------------------------------------------------------------*/ +/* Variable declarations */ +/*---------------------------------------------------------------------------*/ + +//#ifndef lint +//static char rcsid[] DD_UNUSED = "$Id: cuddBddIte.c,v 1.26 2012/02/05 01:07:18 fabio Exp $"; +//#endif + +/*---------------------------------------------------------------------------*/ +/* Macro declarations */ +/*---------------------------------------------------------------------------*/ + + +/**AutomaticStart*************************************************************/ + +/*---------------------------------------------------------------------------*/ +/* Static function prototypes */ +/*---------------------------------------------------------------------------*/ + +static void bddVarToConst (DdNode *f, DdNode **gp, DdNode **hp, DdNode *one); +static int bddVarToCanonical (DdManager *dd, DdNode **fp, DdNode **gp, DdNode **hp, unsigned int *topfp, unsigned int *topgp, unsigned int *tophp); +static int bddVarToCanonicalSimple (DdManager *dd, DdNode **fp, DdNode **gp, DdNode **hp, unsigned int *topfp, unsigned int *topgp, unsigned int *tophp); + +/**AutomaticEnd***************************************************************/ + + +/*---------------------------------------------------------------------------*/ +/* Definition of exported functions */ +/*---------------------------------------------------------------------------*/ + + +/**Function******************************************************************** + + Synopsis [Implements ITE(f,g,h).] + + Description [Implements ITE(f,g,h). Returns a pointer to the + resulting BDD if successful; NULL if the intermediate result blows + up.] + + SideEffects [None] + + SeeAlso [Cudd_addIte Cudd_bddIteConstant Cudd_bddIntersect] + +******************************************************************************/ +DdNode * +Cudd_bddIte( + DdManager * dd, + DdNode * f, + DdNode * g, + DdNode * h) +{ + DdNode *res; + + do { + dd->reordered = 0; + res = cuddBddIteRecur(dd,f,g,h); + } while (dd->reordered == 1); + return(res); + +} /* end of Cudd_bddIte */ + + + +/**Function******************************************************************** + + Synopsis [Implements ITEconstant(f,g,h).] + + Description [Implements ITEconstant(f,g,h). Returns a pointer to the + resulting BDD (which may or may not be constant) or DD_NON_CONSTANT. + No new nodes are created.] + + SideEffects [None] + + SeeAlso [Cudd_bddIte Cudd_bddIntersect Cudd_bddLeq Cudd_addIteConstant] + +******************************************************************************/ +DdNode * +Cudd_bddIteConstant( + DdManager * dd, + DdNode * f, + DdNode * g, + DdNode * h) +{ + DdNode *r, *Fv, *Fnv, *Gv, *Gnv, *H, *Hv, *Hnv, *t, *e; + DdNode *one = DD_ONE(dd); + DdNode *zero = Cudd_Not(one); + int comple; + unsigned int topf, topg, toph, v; + + statLine(dd); + /* Trivial cases. */ + if (f == one) /* ITE(1,G,H) => G */ + return(g); + + if (f == zero) /* ITE(0,G,H) => H */ + return(h); + + /* f now not a constant. */ + bddVarToConst(f, &g, &h, one); /* possibly convert g or h */ + /* to constants */ + + if (g == h) /* ITE(F,G,G) => G */ + return(g); + + if (Cudd_IsConstant(g) && Cudd_IsConstant(h)) + return(DD_NON_CONSTANT); /* ITE(F,1,0) or ITE(F,0,1) */ + /* => DD_NON_CONSTANT */ + + if (g == Cudd_Not(h)) + return(DD_NON_CONSTANT); /* ITE(F,G,G') => DD_NON_CONSTANT */ + /* if F != G and F != G' */ + + comple = bddVarToCanonical(dd, &f, &g, &h, &topf, &topg, &toph); + + /* Cache lookup. */ + r = cuddConstantLookup(dd, DD_BDD_ITE_CONSTANT_TAG, f, g, h); + if (r != NULL) { + return(Cudd_NotCond(r,comple && r != DD_NON_CONSTANT)); + } + + v = ddMin(topg, toph); + + /* ITE(F,G,H) = (v,G,H) (non constant) if F = (v,1,0), v < top(G,H). */ + if (topf < v && cuddT(f) == one && cuddE(f) == zero) { + return(DD_NON_CONSTANT); + } + + /* Compute cofactors. */ + if (topf <= v) { + v = ddMin(topf, v); /* v = top_var(F,G,H) */ + Fv = cuddT(f); Fnv = cuddE(f); + } else { + Fv = Fnv = f; + } + + if (topg == v) { + Gv = cuddT(g); Gnv = cuddE(g); + } else { + Gv = Gnv = g; + } + + if (toph == v) { + H = Cudd_Regular(h); + Hv = cuddT(H); Hnv = cuddE(H); + if (Cudd_IsComplement(h)) { + Hv = Cudd_Not(Hv); + Hnv = Cudd_Not(Hnv); + } + } else { + Hv = Hnv = h; + } + + /* Recursion. */ + t = Cudd_bddIteConstant(dd, Fv, Gv, Hv); + if (t == DD_NON_CONSTANT || !Cudd_IsConstant(t)) { + cuddCacheInsert(dd, DD_BDD_ITE_CONSTANT_TAG, f, g, h, DD_NON_CONSTANT); + return(DD_NON_CONSTANT); + } + e = Cudd_bddIteConstant(dd, Fnv, Gnv, Hnv); + if (e == DD_NON_CONSTANT || !Cudd_IsConstant(e) || t != e) { + cuddCacheInsert(dd, DD_BDD_ITE_CONSTANT_TAG, f, g, h, DD_NON_CONSTANT); + return(DD_NON_CONSTANT); + } + cuddCacheInsert(dd, DD_BDD_ITE_CONSTANT_TAG, f, g, h, t); + return(Cudd_NotCond(t,comple)); + +} /* end of Cudd_bddIteConstant */ + + +/**Function******************************************************************** + + Synopsis [Returns a function included in the intersection of f and g.] + + Description [Computes a function included in the intersection of f and + g. (That is, a witness that the intersection is not empty.) + Cudd_bddIntersect tries to build as few new nodes as possible. If the + only result of interest is whether f and g intersect, + Cudd_bddLeq should be used instead.] + + SideEffects [None] + + SeeAlso [Cudd_bddLeq Cudd_bddIteConstant] + +******************************************************************************/ +DdNode * +Cudd_bddIntersect( + DdManager * dd /* manager */, + DdNode * f /* first operand */, + DdNode * g /* second operand */) +{ + DdNode *res; + + do { + dd->reordered = 0; + res = cuddBddIntersectRecur(dd,f,g); + } while (dd->reordered == 1); + + return(res); + +} /* end of Cudd_bddIntersect */ + + +/**Function******************************************************************** + + Synopsis [Computes the conjunction of two BDDs f and g.] + + Description [Computes the conjunction of two BDDs f and g. Returns a + pointer to the resulting BDD if successful; NULL if the intermediate + result blows up.] + + SideEffects [None] + + SeeAlso [Cudd_bddIte Cudd_addApply Cudd_bddAndAbstract Cudd_bddIntersect + Cudd_bddOr Cudd_bddNand Cudd_bddNor Cudd_bddXor Cudd_bddXnor] + +******************************************************************************/ +DdNode * +Cudd_bddAnd( + DdManager * dd, + DdNode * f, + DdNode * g) +{ + DdNode *res; + + do { + dd->reordered = 0; + res = cuddBddAndRecur(dd,f,g); + } while (dd->reordered == 1); + return(res); + +} /* end of Cudd_bddAnd */ + + +/**Function******************************************************************** + + Synopsis [Computes the conjunction of two BDDs f and g. Returns + NULL if too many nodes are required.] + + Description [Computes the conjunction of two BDDs f and g. Returns a + pointer to the resulting BDD if successful; NULL if the intermediate + result blows up or more new nodes than limit are + required.] + + SideEffects [None] + + SeeAlso [Cudd_bddAnd] + +******************************************************************************/ +DdNode * +Cudd_bddAndLimit( + DdManager * dd, + DdNode * f, + DdNode * g, + unsigned int limit) +{ + DdNode *res; + unsigned int saveLimit = dd->maxLive; + + dd->maxLive = (dd->keys - dd->dead) + (dd->keysZ - dd->deadZ) + limit; + do { + dd->reordered = 0; + res = cuddBddAndRecur(dd,f,g); + } while (dd->reordered == 1); + dd->maxLive = saveLimit; + return(res); + +} /* end of Cudd_bddAndLimit */ + + +/**Function******************************************************************** + + Synopsis [Computes the disjunction of two BDDs f and g.] + + Description [Computes the disjunction of two BDDs f and g. Returns a + pointer to the resulting BDD if successful; NULL if the intermediate + result blows up.] + + SideEffects [None] + + SeeAlso [Cudd_bddIte Cudd_addApply Cudd_bddAnd Cudd_bddNand Cudd_bddNor + Cudd_bddXor Cudd_bddXnor] + +******************************************************************************/ +DdNode * +Cudd_bddOr( + DdManager * dd, + DdNode * f, + DdNode * g) +{ + DdNode *res; + + do { + dd->reordered = 0; + res = cuddBddAndRecur(dd,Cudd_Not(f),Cudd_Not(g)); + } while (dd->reordered == 1); + res = Cudd_NotCond(res,res != NULL); + return(res); + +} /* end of Cudd_bddOr */ + + +/**Function******************************************************************** + + Synopsis [Computes the disjunction of two BDDs f and g. Returns + NULL if too many nodes are required.] + + Description [Computes the disjunction of two BDDs f and g. Returns a + pointer to the resulting BDD if successful; NULL if the intermediate + result blows up or more new nodes than limit are + required.] + + SideEffects [None] + + SeeAlso [Cudd_bddOr] + +******************************************************************************/ +DdNode * +Cudd_bddOrLimit( + DdManager * dd, + DdNode * f, + DdNode * g, + unsigned int limit) +{ + DdNode *res; + unsigned int saveLimit = dd->maxLive; + + dd->maxLive = (dd->keys - dd->dead) + (dd->keysZ - dd->deadZ) + limit; + do { + dd->reordered = 0; + res = cuddBddAndRecur(dd,Cudd_Not(f),Cudd_Not(g)); + } while (dd->reordered == 1); + dd->maxLive = saveLimit; + res = Cudd_NotCond(res,res != NULL); + return(res); + +} /* end of Cudd_bddOrLimit */ + + +/* end of Cudd_bddNand */ + + +/**Function******************************************************************** + + Synopsis [Computes the exclusive OR of two BDDs f and g.] + + Description [Computes the exclusive OR of two BDDs f and g. Returns a + pointer to the resulting BDD if successful; NULL if the intermediate + result blows up.] + + SideEffects [None] + + SeeAlso [Cudd_bddIte Cudd_addApply Cudd_bddAnd Cudd_bddOr + Cudd_bddNand Cudd_bddNor Cudd_bddXnor] + +******************************************************************************/ +DdNode * +Cudd_bddXor( + DdManager * dd, + DdNode * f, + DdNode * g) +{ + DdNode *res; + + do { + dd->reordered = 0; + res = cuddBddXorRecur(dd,f,g); + } while (dd->reordered == 1); + return(res); + +} /* end of Cudd_bddXor */ + + + +/**Function******************************************************************** + + Synopsis [Determines whether f is less than or equal to g.] + + Description [Returns 1 if f is less than or equal to g; 0 otherwise. + No new nodes are created.] + + SideEffects [None] + + SeeAlso [Cudd_bddIteConstant Cudd_addEvalConst] + +******************************************************************************/ +int +Cudd_bddLeq( + DdManager * dd, + DdNode * f, + DdNode * g) +{ + DdNode *one, *zero, *tmp, *F, *fv, *fvn, *gv, *gvn; + unsigned int topf, topg, res; + + statLine(dd); + /* Terminal cases and normalization. */ + if (f == g) return(1); + + if (Cudd_IsComplement(g)) { + /* Special case: if f is regular and g is complemented, + ** f(1,...,1) = 1 > 0 = g(1,...,1). + */ + if (!Cudd_IsComplement(f)) return(0); + /* Both are complemented: Swap and complement because + ** f <= g <=> g' <= f' and we want the second argument to be regular. + */ + tmp = g; + g = Cudd_Not(f); + f = Cudd_Not(tmp); + } else if (Cudd_IsComplement(f) && g < f) { + tmp = g; + g = Cudd_Not(f); + f = Cudd_Not(tmp); + } + + /* Now g is regular and, if f is not regular, f < g. */ + one = DD_ONE(dd); + if (g == one) return(1); /* no need to test against zero */ + if (f == one) return(0); /* since at this point g != one */ + if (Cudd_Not(f) == g) return(0); /* because neither is constant */ + zero = Cudd_Not(one); + if (f == zero) return(1); + + /* Here neither f nor g is constant. */ + + /* Check cache. */ + tmp = cuddCacheLookup2(dd,(DD_CTFP)Cudd_bddLeq,f,g); + if (tmp != NULL) { + return(tmp == one); + } + + /* Compute cofactors. */ + F = Cudd_Regular(f); + topf = dd->perm[F->index]; + topg = dd->perm[g->index]; + if (topf <= topg) { + fv = cuddT(F); fvn = cuddE(F); + if (f != F) { + fv = Cudd_Not(fv); + fvn = Cudd_Not(fvn); + } + } else { + fv = fvn = f; + } + if (topg <= topf) { + gv = cuddT(g); gvn = cuddE(g); + } else { + gv = gvn = g; + } + + /* Recursive calls. Since we want to maximize the probability of + ** the special case f(1,...,1) > g(1,...,1), we consider the negative + ** cofactors first. Indeed, the complementation parity of the positive + ** cofactors is the same as the one of the parent functions. + */ + res = Cudd_bddLeq(dd,fvn,gvn) && Cudd_bddLeq(dd,fv,gv); + + /* Store result in cache and return. */ + cuddCacheInsert2(dd,(DD_CTFP)Cudd_bddLeq,f,g,(res ? one : zero)); + return(res); + +} /* end of Cudd_bddLeq */ + + +/*---------------------------------------------------------------------------*/ +/* Definition of internal functions */ +/*---------------------------------------------------------------------------*/ + + +/**Function******************************************************************** + + Synopsis [Implements the recursive step of Cudd_bddIte.] + + Description [Implements the recursive step of Cudd_bddIte. Returns a + pointer to the resulting BDD. NULL if the intermediate result blows + up or if reordering occurs.] + + SideEffects [None] + + SeeAlso [] + +******************************************************************************/ +DdNode * +cuddBddIteRecur( + DdManager * dd, + DdNode * f, + DdNode * g, + DdNode * h) +{ + DdNode *one, *zero, *res; + DdNode *r, *Fv, *Fnv, *Gv, *Gnv, *H, *Hv, *Hnv, *t, *e; + unsigned int topf, topg, toph, v; + int index; + int comple; + + statLine(dd); + /* Terminal cases. */ + + /* One variable cases. */ + if (f == (one = DD_ONE(dd))) /* ITE(1,G,H) = G */ + return(g); + + if (f == (zero = Cudd_Not(one))) /* ITE(0,G,H) = H */ + return(h); + + /* From now on, f is known not to be a constant. */ + if (g == one || f == g) { /* ITE(F,F,H) = ITE(F,1,H) = F + H */ + if (h == zero) { /* ITE(F,1,0) = F */ + return(f); + } else { + res = cuddBddAndRecur(dd,Cudd_Not(f),Cudd_Not(h)); + return(Cudd_NotCond(res,res != NULL)); + } + } else if (g == zero || f == Cudd_Not(g)) { /* ITE(F,!F,H) = ITE(F,0,H) = !F * H */ + if (h == one) { /* ITE(F,0,1) = !F */ + return(Cudd_Not(f)); + } else { + res = cuddBddAndRecur(dd,Cudd_Not(f),h); + return(res); + } + } + if (h == zero || f == h) { /* ITE(F,G,F) = ITE(F,G,0) = F * G */ + res = cuddBddAndRecur(dd,f,g); + return(res); + } else if (h == one || f == Cudd_Not(h)) { /* ITE(F,G,!F) = ITE(F,G,1) = !F + G */ + res = cuddBddAndRecur(dd,f,Cudd_Not(g)); + return(Cudd_NotCond(res,res != NULL)); + } + + /* Check remaining one variable case. */ + if (g == h) { /* ITE(F,G,G) = G */ + return(g); + } else if (g == Cudd_Not(h)) { /* ITE(F,G,!G) = F <-> G */ + res = cuddBddXorRecur(dd,f,h); + return(res); + } + + /* From here, there are no constants. */ + comple = bddVarToCanonicalSimple(dd, &f, &g, &h, &topf, &topg, &toph); + + /* f & g are now regular pointers */ + + v = ddMin(topg, toph); + + /* A shortcut: ITE(F,G,H) = (v,G,H) if F = (v,1,0), v < top(G,H). */ + if (topf < v && cuddT(f) == one && cuddE(f) == zero) { + r = cuddUniqueInter(dd, (int) f->index, g, h); + return(Cudd_NotCond(r,comple && r != NULL)); + } + + /* Check cache. */ + r = cuddCacheLookup(dd, DD_BDD_ITE_TAG, f, g, h); + if (r != NULL) { + return(Cudd_NotCond(r,comple)); + } + + /* Compute cofactors. */ + if (topf <= v) { + v = ddMin(topf, v); /* v = top_var(F,G,H) */ + index = f->index; + Fv = cuddT(f); Fnv = cuddE(f); + } else { + Fv = Fnv = f; + } + if (topg == v) { + index = g->index; + Gv = cuddT(g); Gnv = cuddE(g); + } else { + Gv = Gnv = g; + } + if (toph == v) { + H = Cudd_Regular(h); + index = H->index; + Hv = cuddT(H); Hnv = cuddE(H); + if (Cudd_IsComplement(h)) { + Hv = Cudd_Not(Hv); + Hnv = Cudd_Not(Hnv); + } + } else { + Hv = Hnv = h; + } + + /* Recursive step. */ + t = cuddBddIteRecur(dd,Fv,Gv,Hv); + if (t == NULL) return(NULL); + cuddRef(t); + + e = cuddBddIteRecur(dd,Fnv,Gnv,Hnv); + if (e == NULL) { + Cudd_IterDerefBdd(dd,t); + return(NULL); + } + cuddRef(e); + + r = (t == e) ? t : cuddUniqueInter(dd,index,t,e); + if (r == NULL) { + Cudd_IterDerefBdd(dd,t); + Cudd_IterDerefBdd(dd,e); + return(NULL); + } + cuddDeref(t); + cuddDeref(e); + + cuddCacheInsert(dd, DD_BDD_ITE_TAG, f, g, h, r); + return(Cudd_NotCond(r,comple)); + +} /* end of cuddBddIteRecur */ + + +/**Function******************************************************************** + + Synopsis [Implements the recursive step of Cudd_bddIntersect.] + + Description [] + + SideEffects [None] + + SeeAlso [Cudd_bddIntersect] + +******************************************************************************/ +DdNode * +cuddBddIntersectRecur( + DdManager * dd, + DdNode * f, + DdNode * g) +{ + DdNode *res; + DdNode *F, *G, *t, *e; + DdNode *fv, *fnv, *gv, *gnv; + DdNode *one, *zero; + unsigned int index, topf, topg; + + statLine(dd); + one = DD_ONE(dd); + zero = Cudd_Not(one); + + /* Terminal cases. */ + if (f == zero || g == zero || f == Cudd_Not(g)) return(zero); + if (f == g || g == one) return(f); + if (f == one) return(g); + + /* At this point f and g are not constant. */ + if (f > g) { DdNode *tmp = f; f = g; g = tmp; } + res = cuddCacheLookup2(dd,Cudd_bddIntersect,f,g); + if (res != NULL) return(res); + + /* Find splitting variable. Here we can skip the use of cuddI, + ** because the operands are known to be non-constant. + */ + F = Cudd_Regular(f); + topf = dd->perm[F->index]; + G = Cudd_Regular(g); + topg = dd->perm[G->index]; + + /* Compute cofactors. */ + if (topf <= topg) { + index = F->index; + fv = cuddT(F); + fnv = cuddE(F); + if (Cudd_IsComplement(f)) { + fv = Cudd_Not(fv); + fnv = Cudd_Not(fnv); + } + } else { + index = G->index; + fv = fnv = f; + } + + if (topg <= topf) { + gv = cuddT(G); + gnv = cuddE(G); + if (Cudd_IsComplement(g)) { + gv = Cudd_Not(gv); + gnv = Cudd_Not(gnv); + } + } else { + gv = gnv = g; + } + + /* Compute partial results. */ + t = cuddBddIntersectRecur(dd,fv,gv); + if (t == NULL) return(NULL); + cuddRef(t); + if (t != zero) { + e = zero; + } else { + e = cuddBddIntersectRecur(dd,fnv,gnv); + if (e == NULL) { + Cudd_IterDerefBdd(dd, t); + return(NULL); + } + } + cuddRef(e); + + if (t == e) { /* both equal zero */ + res = t; + } else if (Cudd_IsComplement(t)) { + res = cuddUniqueInter(dd,(int)index,Cudd_Not(t),Cudd_Not(e)); + if (res == NULL) { + Cudd_IterDerefBdd(dd, t); + Cudd_IterDerefBdd(dd, e); + return(NULL); + } + res = Cudd_Not(res); + } else { + res = cuddUniqueInter(dd,(int)index,t,e); + if (res == NULL) { + Cudd_IterDerefBdd(dd, t); + Cudd_IterDerefBdd(dd, e); + return(NULL); + } + } + cuddDeref(e); + cuddDeref(t); + + cuddCacheInsert2(dd,Cudd_bddIntersect,f,g,res); + + return(res); + +} /* end of cuddBddIntersectRecur */ + + +/**Function******************************************************************** + + Synopsis [Implements the recursive step of Cudd_bddAnd.] + + Description [Implements the recursive step of Cudd_bddAnd by taking + the conjunction of two BDDs. Returns a pointer to the result is + successful; NULL otherwise.] + + SideEffects [None] + + SeeAlso [Cudd_bddAnd] + +******************************************************************************/ +DdNode * +cuddBddAndRecur( + DdManager * manager, + DdNode * f, + DdNode * g) +{ + DdNode *F, *fv, *fnv, *G, *gv, *gnv; + DdNode *one, *r, *t, *e; + unsigned int topf, topg, index; + + statLine(manager); + one = DD_ONE(manager); + + /* Terminal cases. */ + F = Cudd_Regular(f); + G = Cudd_Regular(g); + if (F == G) { + if (f == g) return(f); + else return(Cudd_Not(one)); + } + if (F == one) { + if (f == one) return(g); + else return(f); + } + if (G == one) { + if (g == one) return(f); + else return(g); + } + + /* At this point f and g are not constant. */ + if (f > g) { /* Try to increase cache efficiency. */ + DdNode *tmp = f; + f = g; + g = tmp; + F = Cudd_Regular(f); + G = Cudd_Regular(g); + } + + /* Check cache. */ + if (F->ref != 1 || G->ref != 1) { + r = cuddCacheLookup2(manager, Cudd_bddAnd, f, g); + if (r != NULL) return(r); + } + + /* Here we can skip the use of cuddI, because the operands are known + ** to be non-constant. + */ + topf = manager->perm[F->index]; + topg = manager->perm[G->index]; + + /* Compute cofactors. */ + if (topf <= topg) { + index = F->index; + fv = cuddT(F); + fnv = cuddE(F); + if (Cudd_IsComplement(f)) { + fv = Cudd_Not(fv); + fnv = Cudd_Not(fnv); + } + } else { + index = G->index; + fv = fnv = f; + } + + if (topg <= topf) { + gv = cuddT(G); + gnv = cuddE(G); + if (Cudd_IsComplement(g)) { + gv = Cudd_Not(gv); + gnv = Cudd_Not(gnv); + } + } else { + gv = gnv = g; + } + + t = cuddBddAndRecur(manager, fv, gv); + if (t == NULL) return(NULL); + cuddRef(t); + + e = cuddBddAndRecur(manager, fnv, gnv); + if (e == NULL) { + Cudd_IterDerefBdd(manager, t); + return(NULL); + } + cuddRef(e); + + if (t == e) { + r = t; + } else { + if (Cudd_IsComplement(t)) { + r = cuddUniqueInter(manager,(int)index,Cudd_Not(t),Cudd_Not(e)); + if (r == NULL) { + Cudd_IterDerefBdd(manager, t); + Cudd_IterDerefBdd(manager, e); + return(NULL); + } + r = Cudd_Not(r); + } else { + r = cuddUniqueInter(manager,(int)index,t,e); + if (r == NULL) { + Cudd_IterDerefBdd(manager, t); + Cudd_IterDerefBdd(manager, e); + return(NULL); + } + } + } + cuddDeref(e); + cuddDeref(t); + if (F->ref != 1 || G->ref != 1) + cuddCacheInsert2(manager, Cudd_bddAnd, f, g, r); + return(r); + +} /* end of cuddBddAndRecur */ + + +/**Function******************************************************************** + + Synopsis [Implements the recursive step of Cudd_bddXor.] + + Description [Implements the recursive step of Cudd_bddXor by taking + the exclusive OR of two BDDs. Returns a pointer to the result is + successful; NULL otherwise.] + + SideEffects [None] + + SeeAlso [Cudd_bddXor] + +******************************************************************************/ +DdNode * +cuddBddXorRecur( + DdManager * manager, + DdNode * f, + DdNode * g) +{ + DdNode *fv, *fnv, *G, *gv, *gnv; + DdNode *one, *zero, *r, *t, *e; + unsigned int topf, topg, index; + + statLine(manager); + one = DD_ONE(manager); + zero = Cudd_Not(one); + + /* Terminal cases. */ + if (f == g) return(zero); + if (f == Cudd_Not(g)) return(one); + if (f > g) { /* Try to increase cache efficiency and simplify tests. */ + DdNode *tmp = f; + f = g; + g = tmp; + } + if (g == zero) return(f); + if (g == one) return(Cudd_Not(f)); + if (Cudd_IsComplement(f)) { + f = Cudd_Not(f); + g = Cudd_Not(g); + } + /* Now the first argument is regular. */ + if (f == one) return(Cudd_Not(g)); + + /* At this point f and g are not constant. */ + + /* Check cache. */ + r = cuddCacheLookup2(manager, Cudd_bddXor, f, g); + if (r != NULL) return(r); + + /* Here we can skip the use of cuddI, because the operands are known + ** to be non-constant. + */ + topf = manager->perm[f->index]; + G = Cudd_Regular(g); + topg = manager->perm[G->index]; + + /* Compute cofactors. */ + if (topf <= topg) { + index = f->index; + fv = cuddT(f); + fnv = cuddE(f); + } else { + index = G->index; + fv = fnv = f; + } + + if (topg <= topf) { + gv = cuddT(G); + gnv = cuddE(G); + if (Cudd_IsComplement(g)) { + gv = Cudd_Not(gv); + gnv = Cudd_Not(gnv); + } + } else { + gv = gnv = g; + } + + t = cuddBddXorRecur(manager, fv, gv); + if (t == NULL) return(NULL); + cuddRef(t); + + e = cuddBddXorRecur(manager, fnv, gnv); + if (e == NULL) { + Cudd_IterDerefBdd(manager, t); + return(NULL); + } + cuddRef(e); + + if (t == e) { + r = t; + } else { + if (Cudd_IsComplement(t)) { + r = cuddUniqueInter(manager,(int)index,Cudd_Not(t),Cudd_Not(e)); + if (r == NULL) { + Cudd_IterDerefBdd(manager, t); + Cudd_IterDerefBdd(manager, e); + return(NULL); + } + r = Cudd_Not(r); + } else { + r = cuddUniqueInter(manager,(int)index,t,e); + if (r == NULL) { + Cudd_IterDerefBdd(manager, t); + Cudd_IterDerefBdd(manager, e); + return(NULL); + } + } + } + cuddDeref(e); + cuddDeref(t); + cuddCacheInsert2(manager, Cudd_bddXor, f, g, r); + return(r); + +} /* end of cuddBddXorRecur */ + + +/*---------------------------------------------------------------------------*/ +/* Definition of static functions */ +/*---------------------------------------------------------------------------*/ + + +/**Function******************************************************************** + + Synopsis [Replaces variables with constants if possible.] + + Description [This function performs part of the transformation to + standard form by replacing variables with constants if possible.] + + SideEffects [None] + + SeeAlso [bddVarToCanonical bddVarToCanonicalSimple] + +******************************************************************************/ +static void +bddVarToConst( + DdNode * f, + DdNode ** gp, + DdNode ** hp, + DdNode * one) +{ + DdNode *g = *gp; + DdNode *h = *hp; + + if (f == g) { /* ITE(F,F,H) = ITE(F,1,H) = F + H */ + *gp = one; + } else if (f == Cudd_Not(g)) { /* ITE(F,!F,H) = ITE(F,0,H) = !F * H */ + *gp = Cudd_Not(one); + } + if (f == h) { /* ITE(F,G,F) = ITE(F,G,0) = F * G */ + *hp = Cudd_Not(one); + } else if (f == Cudd_Not(h)) { /* ITE(F,G,!F) = ITE(F,G,1) = !F + G */ + *hp = one; + } + +} /* end of bddVarToConst */ + + +/**Function******************************************************************** + + Synopsis [Picks unique member from equiv expressions.] + + Description [Reduces 2 variable expressions to canonical form.] + + SideEffects [None] + + SeeAlso [bddVarToConst bddVarToCanonicalSimple] + +******************************************************************************/ +static int +bddVarToCanonical( + DdManager * dd, + DdNode ** fp, + DdNode ** gp, + DdNode ** hp, + unsigned int * topfp, + unsigned int * topgp, + unsigned int * tophp) +{ + register DdNode *F, *G, *H, *r, *f, *g, *h; + register unsigned int topf, topg, toph; + DdNode *one = dd->one; + int comple, change; + + f = *fp; + g = *gp; + h = *hp; + F = Cudd_Regular(f); + G = Cudd_Regular(g); + H = Cudd_Regular(h); + topf = cuddI(dd,F->index); + topg = cuddI(dd,G->index); + toph = cuddI(dd,H->index); + + change = 0; + + if (G == one) { /* ITE(F,c,H) */ + if ((topf > toph) || (topf == toph && f > h)) { + r = h; + h = f; + f = r; /* ITE(F,1,H) = ITE(H,1,F) */ + if (g != one) { /* g == zero */ + f = Cudd_Not(f); /* ITE(F,0,H) = ITE(!H,0,!F) */ + h = Cudd_Not(h); + } + change = 1; + } + } else if (H == one) { /* ITE(F,G,c) */ + if ((topf > topg) || (topf == topg && f > g)) { + r = g; + g = f; + f = r; /* ITE(F,G,0) = ITE(G,F,0) */ + if (h == one) { + f = Cudd_Not(f); /* ITE(F,G,1) = ITE(!G,!F,1) */ + g = Cudd_Not(g); + } + change = 1; + } + } else if (g == Cudd_Not(h)) { /* ITE(F,G,!G) = ITE(G,F,!F) */ + if ((topf > topg) || (topf == topg && f > g)) { + r = f; + f = g; + g = r; + h = Cudd_Not(r); + change = 1; + } + } + /* adjust pointers so that the first 2 arguments to ITE are regular */ + if (Cudd_IsComplement(f) != 0) { /* ITE(!F,G,H) = ITE(F,H,G) */ + f = Cudd_Not(f); + r = g; + g = h; + h = r; + change = 1; + } + comple = 0; + if (Cudd_IsComplement(g) != 0) { /* ITE(F,!G,H) = !ITE(F,G,!H) */ + g = Cudd_Not(g); + h = Cudd_Not(h); + change = 1; + comple = 1; + } + if (change != 0) { + *fp = f; + *gp = g; + *hp = h; + } + *topfp = cuddI(dd,f->index); + *topgp = cuddI(dd,g->index); + *tophp = cuddI(dd,Cudd_Regular(h)->index); + + return(comple); + +} /* end of bddVarToCanonical */ + + +/**Function******************************************************************** + + Synopsis [Picks unique member from equiv expressions.] + + Description [Makes sure the first two pointers are regular. This + mat require the complementation of the result, which is signaled by + returning 1 instead of 0. This function is simpler than the general + case because it assumes that no two arguments are the same or + complementary, and no argument is constant.] + + SideEffects [None] + + SeeAlso [bddVarToConst bddVarToCanonical] + +******************************************************************************/ +static int +bddVarToCanonicalSimple( + DdManager * dd, + DdNode ** fp, + DdNode ** gp, + DdNode ** hp, + unsigned int * topfp, + unsigned int * topgp, + unsigned int * tophp) +{ + register DdNode *r, *f, *g, *h; + int comple, change; + + f = *fp; + g = *gp; + h = *hp; + + change = 0; + + /* adjust pointers so that the first 2 arguments to ITE are regular */ + if (Cudd_IsComplement(f)) { /* ITE(!F,G,H) = ITE(F,H,G) */ + f = Cudd_Not(f); + r = g; + g = h; + h = r; + change = 1; + } + comple = 0; + if (Cudd_IsComplement(g)) { /* ITE(F,!G,H) = !ITE(F,G,!H) */ + g = Cudd_Not(g); + h = Cudd_Not(h); + change = 1; + comple = 1; + } + if (change) { + *fp = f; + *gp = g; + *hp = h; + } + + /* Here we can skip the use of cuddI, because the operands are known + ** to be non-constant. + */ + *topfp = dd->perm[f->index]; + *topgp = dd->perm[g->index]; + *tophp = dd->perm[Cudd_Regular(h)->index]; + + return(comple); + +} /* end of bddVarToCanonicalSimple */ + +/**CFile*********************************************************************** + + FileName [cuddCache.c] + + PackageName [cudd] + + Synopsis [Functions for cache insertion and lookup.] + + Description [Internal procedures included in this module: +
    +
  • cuddInitCache() +
  • cuddCacheInsert() +
  • cuddCacheInsert2() +
  • cuddCacheLookup() +
  • cuddCacheLookupZdd() +
  • cuddCacheLookup2() +
  • cuddCacheLookup2Zdd() +
  • cuddConstantLookup() +
  • cuddCacheProfile() +
  • cuddCacheResize() +
  • cuddCacheFlush() +
  • cuddComputeFloorLog2() +
+ Static procedures included in this module: +
    +
] + + SeeAlso [] + + Author [Fabio Somenzi] + + Copyright [Copyright (c) 1995-2012, Regents of the University of Colorado + + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + Neither the name of the University of Colorado nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE.] + +******************************************************************************/ + + +/*---------------------------------------------------------------------------*/ +/* Constant declarations */ +/*---------------------------------------------------------------------------*/ + +#ifdef DD_CACHE_PROFILE +#define DD_HYSTO_BINS 8 +#endif + +/*---------------------------------------------------------------------------*/ +/* Stucture declarations */ +/*---------------------------------------------------------------------------*/ + + +/*---------------------------------------------------------------------------*/ +/* Type declarations */ +/*---------------------------------------------------------------------------*/ + + +/*---------------------------------------------------------------------------*/ +/* Variable declarations */ +/*---------------------------------------------------------------------------*/ + +//#ifndef lint +//static char rcsid[] DD_UNUSED = "$Id: cuddCache.c,v 1.36 2012/02/05 01:07:18 fabio Exp $"; +//#endif + +/*---------------------------------------------------------------------------*/ +/* Macro declarations */ +/*---------------------------------------------------------------------------*/ + + +/**AutomaticStart*************************************************************/ + +/*---------------------------------------------------------------------------*/ +/* Static function prototypes */ +/*---------------------------------------------------------------------------*/ + + +/**AutomaticEnd***************************************************************/ + + +/*---------------------------------------------------------------------------*/ +/* Definition of exported functions */ +/*---------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------*/ +/* Definition of internal functions */ +/*---------------------------------------------------------------------------*/ + + +/**Function******************************************************************** + + Synopsis [Initializes the computed table.] + + Description [Initializes the computed table. It is called by + Cudd_Init. Returns 1 in case of success; 0 otherwise.] + + SideEffects [None] + + SeeAlso [Cudd_Init] + +******************************************************************************/ +int +cuddInitCache( + DdManager * unique /* unique table */, + unsigned int cacheSize /* initial size of the cache */, + unsigned int maxCacheSize /* cache size beyond which no resizing occurs */) +{ + int i; + unsigned int logSize; +#ifndef DD_CACHE_PROFILE + DdNodePtr *mem; + ptruint offset; +#endif + + /* Round cacheSize to largest power of 2 not greater than the requested + ** initial cache size. */ + logSize = cuddComputeFloorLog2(ddMax(cacheSize,unique->slots/2)); + cacheSize = 1 << logSize; + unique->acache = ALLOC(DdCache,cacheSize+1); + if (unique->acache == NULL) { + unique->errorCode = CUDD_MEMORY_OUT; + return(0); + } + /* If the size of the cache entry is a power of 2, we want to + ** enforce alignment to that power of two. This happens when + ** DD_CACHE_PROFILE is not defined. */ +#ifdef DD_CACHE_PROFILE + unique->cache = unique->acache; + unique->memused += (cacheSize) * sizeof(DdCache); +#else + mem = (DdNodePtr *) unique->acache; + offset = (ptruint) mem & (sizeof(DdCache) - 1); + mem += (sizeof(DdCache) - offset) / sizeof(DdNodePtr); + unique->cache = (DdCache *) mem; + assert(((ptruint) unique->cache & (sizeof(DdCache) - 1)) == 0); + unique->memused += (cacheSize+1) * sizeof(DdCache); +#endif + unique->cacheSlots = cacheSize; + unique->cacheShift = sizeof(int) * 8 - logSize; + unique->maxCacheHard = maxCacheSize; + /* If cacheSlack is non-negative, we can resize. */ + unique->cacheSlack = (int) ddMin(maxCacheSize, + DD_MAX_CACHE_TO_SLOTS_RATIO*unique->slots) - + 2 * (int) cacheSize; + Cudd_SetMinHit(unique,DD_MIN_HIT); + /* Initialize to avoid division by 0 and immediate resizing. */ + unique->cacheMisses = (double) (int) (cacheSize * unique->minHit + 1); + unique->cacheHits = 0; + unique->totCachehits = 0; + /* The sum of cacheMisses and totCacheMisses is always correct, + ** even though cacheMisses is larger than it should for the reasons + ** explained above. */ + unique->totCacheMisses = -unique->cacheMisses; + unique->cachecollisions = 0; + unique->cacheinserts = 0; + unique->cacheLastInserts = 0; + unique->cachedeletions = 0; + + /* Initialize the cache */ + for (i = 0; (unsigned) i < cacheSize; i++) { + unique->cache[i].h = 0; /* unused slots */ + unique->cache[i].data = NULL; /* invalid entry */ +#ifdef DD_CACHE_PROFILE + unique->cache[i].count = 0; +#endif + } + + return(1); + +} /* end of cuddInitCache */ + + +/**Function******************************************************************** + + Synopsis [Inserts a result in the cache for a function with three + operands.] + + Description [Inserts a result in the cache for a function with three + operands. The operator tag (see CUDD/cuddInt.h for details) is split and stored + into unused bits of the first two pointers.] + + SideEffects [None] + + SeeAlso [cuddCacheInsert2 cuddCacheInsert1] + +******************************************************************************/ +void +cuddCacheInsert( + DdManager * table, + ptruint op, + DdNode * f, + DdNode * g, + DdNode * h, + DdNode * data) +{ + int posn; + register DdCache *entry; + ptruint uf, ug, uh; + + uf = (ptruint) f | (op & 0xe); + ug = (ptruint) g | (op >> 4); + uh = (ptruint) h; + + posn = ddCHash2(uh,uf,ug,table->cacheShift); + entry = &table->cache[posn]; + + table->cachecollisions += entry->data != NULL; + table->cacheinserts++; + + entry->f = (DdNode *) uf; + entry->g = (DdNode *) ug; + entry->h = uh; + entry->data = data; +#ifdef DD_CACHE_PROFILE + entry->count++; +#endif + +} /* end of cuddCacheInsert */ + + +/**Function******************************************************************** + + Synopsis [Inserts a result in the cache for a function with two + operands.] + + Description [] + + SideEffects [None] + + SeeAlso [cuddCacheInsert cuddCacheInsert1] + +******************************************************************************/ +void +cuddCacheInsert2( + DdManager * table, + DD_CTFP op, + DdNode * f, + DdNode * g, + DdNode * data) +{ + int posn; + register DdCache *entry; + + posn = ddCHash2(op,f,g,table->cacheShift); + entry = &table->cache[posn]; + + if (entry->data != NULL) { + table->cachecollisions++; + } + table->cacheinserts++; + + entry->f = f; + entry->g = g; + entry->h = (ptruint) op; + entry->data = data; +#ifdef DD_CACHE_PROFILE + entry->count++; +#endif + +} /* end of cuddCacheInsert2 */ + + +/**Function******************************************************************** + + Synopsis [Inserts a result in the cache for a function with two + operands.] + + Description [] + + SideEffects [None] + + SeeAlso [cuddCacheInsert cuddCacheInsert2] + +******************************************************************************/ +void +cuddCacheInsert1( + DdManager * table, + DD_CTFP1 op, + DdNode * f, + DdNode * data) +{ + int posn; + register DdCache *entry; + + posn = ddCHash2(op,f,f,table->cacheShift); + entry = &table->cache[posn]; + + if (entry->data != NULL) { + table->cachecollisions++; + } + table->cacheinserts++; + + entry->f = f; + entry->g = f; + entry->h = (ptruint) op; + entry->data = data; +#ifdef DD_CACHE_PROFILE + entry->count++; +#endif + +} /* end of cuddCacheInsert1 */ + + +/**Function******************************************************************** + + Synopsis [Looks up in the cache for the result of op applied to f, + g, and h.] + + Description [Returns the result if found; it returns NULL if no + result is found.] + + SideEffects [None] + + SeeAlso [cuddCacheLookup2 cuddCacheLookup1] + +******************************************************************************/ +DdNode * +cuddCacheLookup( + DdManager * table, + ptruint op, + DdNode * f, + DdNode * g, + DdNode * h) +{ + int posn; + DdCache *en,*cache; + DdNode *data; + ptruint uf, ug, uh; + + uf = (ptruint) f | (op & 0xe); + ug = (ptruint) g | (op >> 4); + uh = (ptruint) h; + + cache = table->cache; +#ifdef DD_DEBUG + if (cache == NULL) { + return(NULL); + } +#endif + + posn = ddCHash2(uh,uf,ug,table->cacheShift); + en = &cache[posn]; + if (en->data != NULL && en->f==(DdNodePtr)uf && en->g==(DdNodePtr)ug && + en->h==uh) { + data = Cudd_Regular(en->data); + table->cacheHits++; + if (data->ref == 0) { + cuddReclaim(table,data); + } + return(en->data); + } + + /* Cache miss: decide whether to resize. */ + table->cacheMisses++; + + if (table->cacheSlack >= 0 && + table->cacheHits > table->cacheMisses * table->minHit) { + cuddCacheResize(table); + } + + return(NULL); + +} /* end of cuddCacheLookup */ + + +/**Function******************************************************************** + + Synopsis [Looks up in the cache for the result of op applied to f, + g, and h.] + + Description [Returns the result if found; it returns NULL if no + result is found.] + + SideEffects [None] + + SeeAlso [cuddCacheLookup2Zdd cuddCacheLookup1Zdd] + +******************************************************************************/ +DdNode * +cuddCacheLookupZdd( + DdManager * table, + ptruint op, + DdNode * f, + DdNode * g, + DdNode * h) +{ + int posn; + DdCache *en,*cache; + DdNode *data; + ptruint uf, ug, uh; + + uf = (ptruint) f | (op & 0xe); + ug = (ptruint) g | (op >> 4); + uh = (ptruint) h; + + cache = table->cache; +#ifdef DD_DEBUG + if (cache == NULL) { + return(NULL); + } +#endif + + posn = ddCHash2(uh,uf,ug,table->cacheShift); + en = &cache[posn]; + if (en->data != NULL && en->f==(DdNodePtr)uf && en->g==(DdNodePtr)ug && + en->h==uh) { + data = Cudd_Regular(en->data); + table->cacheHits++; + if (data->ref == 0) { + cuddReclaimZdd(table,data); + } + return(en->data); + } + + /* Cache miss: decide whether to resize. */ + table->cacheMisses++; + + if (table->cacheSlack >= 0 && + table->cacheHits > table->cacheMisses * table->minHit) { + cuddCacheResize(table); + } + + return(NULL); + +} /* end of cuddCacheLookupZdd */ + + +/**Function******************************************************************** + + Synopsis [Looks up in the cache for the result of op applied to f + and g.] + + Description [Returns the result if found; it returns NULL if no + result is found.] + + SideEffects [None] + + SeeAlso [cuddCacheLookup cuddCacheLookup1] + +******************************************************************************/ +DdNode * +cuddCacheLookup2( + DdManager * table, + DD_CTFP op, + DdNode * f, + DdNode * g) +{ + int posn; + DdCache *en,*cache; + DdNode *data; + + cache = table->cache; +#ifdef DD_DEBUG + if (cache == NULL) { + return(NULL); + } +#endif + + posn = ddCHash2(op,f,g,table->cacheShift); + en = &cache[posn]; + if (en->data != NULL && en->f==f && en->g==g && en->h==(ptruint)op) { + data = Cudd_Regular(en->data); + table->cacheHits++; + if (data->ref == 0) { + cuddReclaim(table,data); + } + return(en->data); + } + + /* Cache miss: decide whether to resize. */ + table->cacheMisses++; + + if (table->cacheSlack >= 0 && + table->cacheHits > table->cacheMisses * table->minHit) { + cuddCacheResize(table); + } + + return(NULL); + +} /* end of cuddCacheLookup2 */ + + +/**Function******************************************************************** + + Synopsis [Looks up in the cache for the result of op applied to f.] + + Description [Returns the result if found; it returns NULL if no + result is found.] + + SideEffects [None] + + SeeAlso [cuddCacheLookup cuddCacheLookup2] + +******************************************************************************/ +DdNode * +cuddCacheLookup1( + DdManager * table, + DD_CTFP1 op, + DdNode * f) +{ + int posn; + DdCache *en,*cache; + DdNode *data; + + cache = table->cache; +#ifdef DD_DEBUG + if (cache == NULL) { + return(NULL); + } +#endif + + posn = ddCHash2(op,f,f,table->cacheShift); + en = &cache[posn]; + if (en->data != NULL && en->f==f && en->h==(ptruint)op) { + data = Cudd_Regular(en->data); + table->cacheHits++; + if (data->ref == 0) { + cuddReclaim(table,data); + } + return(en->data); + } + + /* Cache miss: decide whether to resize. */ + table->cacheMisses++; + + if (table->cacheSlack >= 0 && + table->cacheHits > table->cacheMisses * table->minHit) { + cuddCacheResize(table); + } + + return(NULL); + +} /* end of cuddCacheLookup1 */ + + +/**Function******************************************************************** + + Synopsis [Looks up in the cache for the result of op applied to f + and g.] + + Description [Returns the result if found; it returns NULL if no + result is found.] + + SideEffects [None] + + SeeAlso [cuddCacheLookupZdd cuddCacheLookup1Zdd] + +******************************************************************************/ +DdNode * +cuddCacheLookup2Zdd( + DdManager * table, + DD_CTFP op, + DdNode * f, + DdNode * g) +{ + int posn; + DdCache *en,*cache; + DdNode *data; + + cache = table->cache; +#ifdef DD_DEBUG + if (cache == NULL) { + return(NULL); + } +#endif + + posn = ddCHash2(op,f,g,table->cacheShift); + en = &cache[posn]; + if (en->data != NULL && en->f==f && en->g==g && en->h==(ptruint)op) { + data = Cudd_Regular(en->data); + table->cacheHits++; + if (data->ref == 0) { + cuddReclaimZdd(table,data); + } + return(en->data); + } + + /* Cache miss: decide whether to resize. */ + table->cacheMisses++; + + if (table->cacheSlack >= 0 && + table->cacheHits > table->cacheMisses * table->minHit) { + cuddCacheResize(table); + } + + return(NULL); + +} /* end of cuddCacheLookup2Zdd */ + + +/**Function******************************************************************** + + Synopsis [Looks up in the cache for the result of op applied to f.] + + Description [Returns the result if found; it returns NULL if no + result is found.] + + SideEffects [None] + + SeeAlso [cuddCacheLookupZdd cuddCacheLookup2Zdd] + +******************************************************************************/ +DdNode * +cuddCacheLookup1Zdd( + DdManager * table, + DD_CTFP1 op, + DdNode * f) +{ + int posn; + DdCache *en,*cache; + DdNode *data; + + cache = table->cache; +#ifdef DD_DEBUG + if (cache == NULL) { + return(NULL); + } +#endif + + posn = ddCHash2(op,f,f,table->cacheShift); + en = &cache[posn]; + if (en->data != NULL && en->f==f && en->h==(ptruint)op) { + data = Cudd_Regular(en->data); + table->cacheHits++; + if (data->ref == 0) { + cuddReclaimZdd(table,data); + } + return(en->data); + } + + /* Cache miss: decide whether to resize. */ + table->cacheMisses++; + + if (table->cacheSlack >= 0 && + table->cacheHits > table->cacheMisses * table->minHit) { + cuddCacheResize(table); + } + + return(NULL); + +} /* end of cuddCacheLookup1Zdd */ + + +/**Function******************************************************************** + + Synopsis [Looks up in the cache for the result of op applied to f, + g, and h.] + + Description [Looks up in the cache for the result of op applied to f, + g, and h. Assumes that the calling procedure (e.g., + Cudd_bddIteConstant) is only interested in whether the result is + constant or not. Returns the result if found (possibly + DD_NON_CONSTANT); otherwise it returns NULL.] + + SideEffects [None] + + SeeAlso [cuddCacheLookup] + +******************************************************************************/ +DdNode * +cuddConstantLookup( + DdManager * table, + ptruint op, + DdNode * f, + DdNode * g, + DdNode * h) +{ + int posn; + DdCache *en,*cache; + ptruint uf, ug, uh; + + uf = (ptruint) f | (op & 0xe); + ug = (ptruint) g | (op >> 4); + uh = (ptruint) h; + + cache = table->cache; +#ifdef DD_DEBUG + if (cache == NULL) { + return(NULL); + } +#endif + posn = ddCHash2(uh,uf,ug,table->cacheShift); + en = &cache[posn]; + + /* We do not reclaim here because the result should not be + * referenced, but only tested for being a constant. + */ + if (en->data != NULL && + en->f == (DdNodePtr)uf && en->g == (DdNodePtr)ug && en->h == uh) { + table->cacheHits++; + return(en->data); + } + + /* Cache miss: decide whether to resize. */ + table->cacheMisses++; + + if (table->cacheSlack >= 0 && + table->cacheHits > table->cacheMisses * table->minHit) { + cuddCacheResize(table); + } + + return(NULL); + +} /* end of cuddConstantLookup */ + + +/**Function******************************************************************** + + Synopsis [Computes and prints a profile of the cache usage.] + + Description [Computes and prints a profile of the cache usage. + Returns 1 if successful; 0 otherwise.] + + SideEffects [None] + + SeeAlso [] + +******************************************************************************/ +int +cuddCacheProfile( + DdManager * table, + FILE * fp) +{ + DdCache *cache = table->cache; + int slots = table->cacheSlots; + int nzeroes = 0; + int i, retval; + double exUsed; + +#ifdef DD_CACHE_PROFILE + double count, mean, meansq, stddev, expected; + long max, min; + int imax, imin; + double *hystogramQ, *hystogramR; /* histograms by quotient and remainder */ + int nbins = DD_HYSTO_BINS; + int bin; + long thiscount; + double totalcount, exStddev; + + meansq = mean = expected = 0.0; + max = min = (long) cache[0].count; + imax = imin = 0; + totalcount = 0.0; + + hystogramQ = ALLOC(double, nbins); + if (hystogramQ == NULL) { + table->errorCode = CUDD_MEMORY_OUT; + return(0); + } + hystogramR = ALLOC(double, nbins); + if (hystogramR == NULL) { + FREE(hystogramQ); + table->errorCode = CUDD_MEMORY_OUT; + return(0); + } + for (i = 0; i < nbins; i++) { + hystogramQ[i] = 0; + hystogramR[i] = 0; + } + + for (i = 0; i < slots; i++) { + thiscount = (long) cache[i].count; + if (thiscount > max) { + max = thiscount; + imax = i; + } + if (thiscount < min) { + min = thiscount; + imin = i; + } + if (thiscount == 0) { + nzeroes++; + } + count = (double) thiscount; + mean += count; + meansq += count * count; + totalcount += count; + expected += count * (double) i; + bin = (i * nbins) / slots; + hystogramQ[bin] += (double) thiscount; + bin = i % nbins; + hystogramR[bin] += (double) thiscount; + } + mean /= (double) slots; + meansq /= (double) slots; + + /* Compute the standard deviation from both the data and the + ** theoretical model for a random distribution. */ + stddev = sqrt(meansq - mean*mean); + exStddev = sqrt((1 - 1/(double) slots) * totalcount / (double) slots); + + retval = fprintf(fp,"Cache average accesses = %g\n", mean); + if (retval == EOF) return(0); + retval = fprintf(fp,"Cache access standard deviation = %g ", stddev); + if (retval == EOF) return(0); + retval = fprintf(fp,"(expected = %g)\n", exStddev); + if (retval == EOF) return(0); + retval = fprintf(fp,"Cache max accesses = %ld for slot %d\n", max, imax); + if (retval == EOF) return(0); + retval = fprintf(fp,"Cache min accesses = %ld for slot %d\n", min, imin); + if (retval == EOF) return(0); + exUsed = 100.0 * (1.0 - exp(-totalcount / (double) slots)); + retval = fprintf(fp,"Cache used slots = %.2f%% (expected %.2f%%)\n", + 100.0 - (double) nzeroes * 100.0 / (double) slots, + exUsed); + if (retval == EOF) return(0); + + if (totalcount > 0) { + expected /= totalcount; + retval = fprintf(fp,"Cache access hystogram for %d bins", nbins); + if (retval == EOF) return(0); + retval = fprintf(fp," (expected bin value = %g)\nBy quotient:", + expected); + if (retval == EOF) return(0); + for (i = nbins - 1; i>=0; i--) { + retval = fprintf(fp," %.0f", hystogramQ[i]); + if (retval == EOF) return(0); + } + retval = fprintf(fp,"\nBy residue: "); + if (retval == EOF) return(0); + for (i = nbins - 1; i>=0; i--) { + retval = fprintf(fp," %.0f", hystogramR[i]); + if (retval == EOF) return(0); + } + retval = fprintf(fp,"\n"); + if (retval == EOF) return(0); + } + + FREE(hystogramQ); + FREE(hystogramR); +#else + for (i = 0; i < slots; i++) { + nzeroes += cache[i].h == 0; + } + exUsed = 100.0 * + (1.0 - exp(-(table->cacheinserts - table->cacheLastInserts) / + (double) slots)); + retval = fprintf(fp,"Cache used slots = %.2f%% (expected %.2f%%)\n", + 100.0 - (double) nzeroes * 100.0 / (double) slots, + exUsed); + if (retval == EOF) return(0); +#endif + return(1); + +} /* end of cuddCacheProfile */ + + +/**Function******************************************************************** + + Synopsis [Resizes the cache.] + + Description [] + + SideEffects [None] + + SeeAlso [] + +******************************************************************************/ +void +cuddCacheResize( + DdManager * table) +{ + DdCache *cache, *oldcache, *oldacache, *entry, *old; + int i; + int posn, shift; + unsigned int slots, oldslots; + double offset; + int moved = 0; + extern DD_OOMFP MMoutOfMemory; + DD_OOMFP saveHandler; +#ifndef DD_CACHE_PROFILE + ptruint misalignment; + DdNodePtr *mem; +#endif + + oldcache = table->cache; + oldacache = table->acache; + oldslots = table->cacheSlots; + slots = table->cacheSlots = oldslots << 1; + +#ifdef DD_VERBOSE + (void) fprintf(table->err,"Resizing the cache from %d to %d entries\n", + oldslots, slots); + (void) fprintf(table->err, + "\thits = %g\tmisses = %g\thit ratio = %5.3f\n", + table->cacheHits, table->cacheMisses, + table->cacheHits / (table->cacheHits + table->cacheMisses)); +#endif + + saveHandler = MMoutOfMemory; + MMoutOfMemory = Cudd_OutOfMem; + table->acache = cache = ALLOC(DdCache,slots+1); + MMoutOfMemory = saveHandler; + /* If we fail to allocate the new table we just give up. */ + if (cache == NULL) { +#ifdef DD_VERBOSE + (void) fprintf(table->err,"Resizing failed. Giving up.\n"); +#endif + table->cacheSlots = oldslots; + table->acache = oldacache; + /* Do not try to resize again. */ + table->maxCacheHard = oldslots - 1; + table->cacheSlack = - (int) (oldslots + 1); + return; + } + /* If the size of the cache entry is a power of 2, we want to + ** enforce alignment to that power of two. This happens when + ** DD_CACHE_PROFILE is not defined. */ +#ifdef DD_CACHE_PROFILE + table->cache = cache; +#else + mem = (DdNodePtr *) cache; + misalignment = (ptruint) mem & (sizeof(DdCache) - 1); + mem += (sizeof(DdCache) - misalignment) / sizeof(DdNodePtr); + table->cache = cache = (DdCache *) mem; + assert(((ptruint) table->cache & (sizeof(DdCache) - 1)) == 0); +#endif + shift = --(table->cacheShift); + table->memused += (slots - oldslots) * sizeof(DdCache); + table->cacheSlack -= slots; /* need these many slots to double again */ + + /* Clear new cache. */ + for (i = 0; (unsigned) i < slots; i++) { + cache[i].data = NULL; + cache[i].h = 0; +#ifdef DD_CACHE_PROFILE + cache[i].count = 0; +#endif + } + + /* Copy from old cache to new one. */ + for (i = 0; (unsigned) i < oldslots; i++) { + old = &oldcache[i]; + if (old->data != NULL) { + posn = ddCHash2(old->h,old->f,old->g,shift); + entry = &cache[posn]; + entry->f = old->f; + entry->g = old->g; + entry->h = old->h; + entry->data = old->data; +#ifdef DD_CACHE_PROFILE + entry->count = 1; +#endif + moved++; + } + } + + FREE(oldacache); + + /* Reinitialize measurements so as to avoid division by 0 and + ** immediate resizing. + */ + offset = (double) (int) (slots * table->minHit + 1); + table->totCacheMisses += table->cacheMisses - offset; + table->cacheMisses = offset; + table->totCachehits += table->cacheHits; + table->cacheHits = 0; + table->cacheLastInserts = table->cacheinserts - (double) moved; + +} /* end of cuddCacheResize */ + + +/**Function******************************************************************** + + Synopsis [Flushes the cache.] + + Description [] + + SideEffects [None] + + SeeAlso [] + +******************************************************************************/ +void +cuddCacheFlush( + DdManager * table) +{ + int i, slots; + DdCache *cache; + + slots = table->cacheSlots; + cache = table->cache; + for (i = 0; i < slots; i++) { + table->cachedeletions += cache[i].data != NULL; + cache[i].data = NULL; + } + table->cacheLastInserts = table->cacheinserts; + + return; + +} /* end of cuddCacheFlush */ + + +/**Function******************************************************************** + + Synopsis [Returns the floor of the logarithm to the base 2.] + + Description [Returns the floor of the logarithm to the base 2. + The input value is assumed to be greater than 0.] + + SideEffects [None] + + SeeAlso [] + +******************************************************************************/ +int +cuddComputeFloorLog2( + unsigned int value) +{ + int floorLog = 0; +#ifdef DD_DEBUG + assert(value > 0); +#endif + while (value > 1) { + floorLog++; + value >>= 1; + } + return(floorLog); + +} /* end of cuddComputeFloorLog2 */ + +/*---------------------------------------------------------------------------*/ +/* Definition of static functions */ +/*---------------------------------------------------------------------------*/ +/**CFile*********************************************************************** + + FileName [cuddCheck.c] + + PackageName [cudd] + + Synopsis [Functions to check consistency of data structures.] + + Description [External procedures included in this module: +
    +
  • Cudd_DebugCheck() +
  • Cudd_CheckKeys() +
+ Internal procedures included in this module: +
    +
  • cuddHeapProfile() +
  • cuddPrintNode() +
  • cuddPrintVarGroups() +
+ Static procedures included in this module: +
    +
  • debugFindParent() +
+ ] + + SeeAlso [] + + Author [Fabio Somenzi] + + Copyright [Copyright (c) 1995-2012, Regents of the University of Colorado + + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + Neither the name of the University of Colorado nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE.] + +******************************************************************************/ + + +/*---------------------------------------------------------------------------*/ +/* Constant declarations */ +/*---------------------------------------------------------------------------*/ + + +/*---------------------------------------------------------------------------*/ +/* Stucture declarations */ +/*---------------------------------------------------------------------------*/ + + +/*---------------------------------------------------------------------------*/ +/* Type declarations */ +/*---------------------------------------------------------------------------*/ + + +/*---------------------------------------------------------------------------*/ +/* Variable declarations */ +/*---------------------------------------------------------------------------*/ + +//#ifndef lint +//static char rcsid[] DD_UNUSED = "$Id: cuddCheck.c,v 1.37 2012/02/05 01:07:18 fabio Exp $"; +//#endif + +/*---------------------------------------------------------------------------*/ +/* Macro declarations */ +/*---------------------------------------------------------------------------*/ + + +/**AutomaticStart*************************************************************/ + +/*---------------------------------------------------------------------------*/ +/* Static function prototypes */ +/*---------------------------------------------------------------------------*/ + +static void debugFindParent (DdManager *table, DdNode *node); +#if 0 +static void debugCheckParent (DdManager *table, DdNode *node); +#endif + +/**AutomaticEnd***************************************************************/ + + +/*---------------------------------------------------------------------------*/ +/* Definition of exported functions */ +/*---------------------------------------------------------------------------*/ + + +/**Function******************************************************************** + + Synopsis [Checks for inconsistencies in the DD heap.] + + Description [Checks for inconsistencies in the DD heap: +
    +
  • node has illegal index +
  • live node has dead children +
  • node has illegal Then or Else pointers +
  • BDD/ADD node has identical children +
  • ZDD node has zero then child +
  • wrong number of total nodes +
  • wrong number of dead nodes +
  • ref count error at node +
+ Returns 0 if no inconsistencies are found; DD_OUT_OF_MEM if there is + not enough memory; 1 otherwise.] + + SideEffects [None] + + SeeAlso [Cudd_CheckKeys] + +******************************************************************************/ +int +Cudd_DebugCheck( + DdManager * table) +{ + unsigned int i; + int j,count; + int slots; + DdNodePtr *nodelist; + DdNode *f; + DdNode *sentinel = &(table->sentinel); + st_table *edgeTable; /* stores internal ref count for each node */ + st_generator *gen; + int flag = 0; + int totalNode; + int deadNode; + int index; + int shift; + + edgeTable = st_init_table(st_ptrcmp,st_ptrhash); + if (edgeTable == NULL) return(CUDD_OUT_OF_MEM); + + /* Check the BDD/ADD subtables. */ + for (i = 0; i < (unsigned) table->size; i++) { + index = table->invperm[i]; + if (i != (unsigned) table->perm[index]) { + (void) fprintf(table->err, + "Permutation corrupted: invperm[%u] = %d\t perm[%d] = %d\n", + i, index, index, table->perm[index]); + } + nodelist = table->subtables[i].nodelist; + slots = table->subtables[i].slots; + shift = table->subtables[i].shift; + + totalNode = 0; + deadNode = 0; + for (j = 0; j < slots; j++) { /* for each subtable slot */ + f = nodelist[j]; + while (f != sentinel) { + totalNode++; + if (cuddT(f) != NULL && cuddE(f) != NULL && f->ref != 0) { + if ((int) f->index != index) { + (void) fprintf(table->err, + "Error: node has illegal index\n"); + cuddPrintNode(f,table->err); + flag = 1; + } + if ((unsigned) cuddI(table,cuddT(f)->index) <= i || + (unsigned) cuddI(table,Cudd_Regular(cuddE(f))->index) + <= i) { + (void) fprintf(table->err, + "Error: node has illegal children\n"); + cuddPrintNode(f,table->err); + flag = 1; + } + if (Cudd_Regular(cuddT(f)) != cuddT(f)) { + (void) fprintf(table->err, + "Error: node has illegal form\n"); + cuddPrintNode(f,table->err); + flag = 1; + } + if (cuddT(f) == cuddE(f)) { + (void) fprintf(table->err, + "Error: node has identical children\n"); + cuddPrintNode(f,table->err); + flag = 1; + } + if (cuddT(f)->ref == 0 || Cudd_Regular(cuddE(f))->ref == 0) { + (void) fprintf(table->err, + "Error: live node has dead children\n"); + cuddPrintNode(f,table->err); + flag =1; + } + if (ddHash(cuddT(f),cuddE(f),shift) != j) { + (void) fprintf(table->err, "Error: misplaced node\n"); + cuddPrintNode(f,table->err); + flag =1; + } + /* Increment the internal reference count for the + ** then child of the current node. + */ + if (st_lookup_int(edgeTable,(char *)cuddT(f),&count)) { + count++; + } else { + count = 1; + } + if (st_insert(edgeTable,(char *)cuddT(f), + (char *)(long)count) == ST_OUT_OF_MEM) { + st_free_table(edgeTable); + return(CUDD_OUT_OF_MEM); + } + + /* Increment the internal reference count for the + ** else child of the current node. + */ + if (st_lookup_int(edgeTable,(char *)Cudd_Regular(cuddE(f)), + &count)) { + count++; + } else { + count = 1; + } + if (st_insert(edgeTable,(char *)Cudd_Regular(cuddE(f)), + (char *)(long)count) == ST_OUT_OF_MEM) { + st_free_table(edgeTable); + return(CUDD_OUT_OF_MEM); + } + } else if (cuddT(f) != NULL && cuddE(f) != NULL && f->ref == 0) { + deadNode++; +#if 0 + debugCheckParent(table,f); +#endif + } else { + fprintf(table->err, + "Error: node has illegal Then or Else pointers\n"); + cuddPrintNode(f,table->err); + flag = 1; + } + + f = f->next; + } /* for each element of the collision list */ + } /* for each subtable slot */ + + if ((unsigned) totalNode != table->subtables[i].keys) { + fprintf(table->err,"Error: wrong number of total nodes\n"); + flag = 1; + } + if ((unsigned) deadNode != table->subtables[i].dead) { + fprintf(table->err,"Error: wrong number of dead nodes\n"); + flag = 1; + } + } /* for each BDD/ADD subtable */ + + /* Check the ZDD subtables. */ + for (i = 0; i < (unsigned) table->sizeZ; i++) { + index = table->invpermZ[i]; + if (i != (unsigned) table->permZ[index]) { + (void) fprintf(table->err, + "Permutation corrupted: invpermZ[%u] = %d\t permZ[%d] = %d in ZDD\n", + i, index, index, table->permZ[index]); + } + nodelist = table->subtableZ[i].nodelist; + slots = table->subtableZ[i].slots; + + totalNode = 0; + deadNode = 0; + for (j = 0; j < slots; j++) { /* for each subtable slot */ + f = nodelist[j]; + while (f != NULL) { + totalNode++; + if (cuddT(f) != NULL && cuddE(f) != NULL && f->ref != 0) { + if ((int) f->index != index) { + (void) fprintf(table->err, + "Error: ZDD node has illegal index\n"); + cuddPrintNode(f,table->err); + flag = 1; + } + if (Cudd_IsComplement(cuddT(f)) || + Cudd_IsComplement(cuddE(f))) { + (void) fprintf(table->err, + "Error: ZDD node has complemented children\n"); + cuddPrintNode(f,table->err); + flag = 1; + } + if ((unsigned) cuddIZ(table,cuddT(f)->index) <= i || + (unsigned) cuddIZ(table,cuddE(f)->index) <= i) { + (void) fprintf(table->err, + "Error: ZDD node has illegal children\n"); + cuddPrintNode(f,table->err); + cuddPrintNode(cuddT(f),table->err); + cuddPrintNode(cuddE(f),table->err); + flag = 1; + } + if (cuddT(f) == DD_ZERO(table)) { + (void) fprintf(table->err, + "Error: ZDD node has zero then child\n"); + cuddPrintNode(f,table->err); + flag = 1; + } + if (cuddT(f)->ref == 0 || cuddE(f)->ref == 0) { + (void) fprintf(table->err, + "Error: ZDD live node has dead children\n"); + cuddPrintNode(f,table->err); + flag =1; + } + /* Increment the internal reference count for the + ** then child of the current node. + */ + if (st_lookup_int(edgeTable,(char *)cuddT(f),&count)) { + count++; + } else { + count = 1; + } + if (st_insert(edgeTable,(char *)cuddT(f), + (char *)(long)count) == ST_OUT_OF_MEM) { + st_free_table(edgeTable); + return(CUDD_OUT_OF_MEM); + } + + /* Increment the internal reference count for the + ** else child of the current node. + */ + if (st_lookup_int(edgeTable,(char *)cuddE(f),&count)) { + count++; + } else { + count = 1; + } + if (st_insert(edgeTable,(char *)cuddE(f), + (char *)(long)count) == ST_OUT_OF_MEM) { + st_free_table(edgeTable); + table->errorCode = CUDD_MEMORY_OUT; + return(CUDD_OUT_OF_MEM); + } + } else if (cuddT(f) != NULL && cuddE(f) != NULL && f->ref == 0) { + deadNode++; +#if 0 + debugCheckParent(table,f); +#endif + } else { + fprintf(table->err, + "Error: ZDD node has illegal Then or Else pointers\n"); + cuddPrintNode(f,table->err); + flag = 1; + } + + f = f->next; + } /* for each element of the collision list */ + } /* for each subtable slot */ + + if ((unsigned) totalNode != table->subtableZ[i].keys) { + fprintf(table->err, + "Error: wrong number of total nodes in ZDD\n"); + flag = 1; + } + if ((unsigned) deadNode != table->subtableZ[i].dead) { + fprintf(table->err, + "Error: wrong number of dead nodes in ZDD\n"); + flag = 1; + } + } /* for each ZDD subtable */ + + /* Check the constant table. */ + nodelist = table->constants.nodelist; + slots = table->constants.slots; + + totalNode = 0; + deadNode = 0; + for (j = 0; j < slots; j++) { + f = nodelist[j]; + while (f != NULL) { + totalNode++; + if (f->ref != 0) { + if (f->index != CUDD_CONST_INDEX) { + fprintf(table->err,"Error: node has illegal index\n"); +#if SIZEOF_VOID_P == 8 + fprintf(table->err, + " node 0x%lx, id = %u, ref = %u, value = %g\n", + (ptruint)f,f->index,f->ref,cuddV(f)); +#else + fprintf(table->err, + " node 0x%x, id = %hu, ref = %hu, value = %g\n", + (ptruint)f,f->index,f->ref,cuddV(f)); +#endif + flag = 1; + } + } else { + deadNode++; + } + f = f->next; + } + } + if ((unsigned) totalNode != table->constants.keys) { + (void) fprintf(table->err, + "Error: wrong number of total nodes in constants\n"); + flag = 1; + } + if ((unsigned) deadNode != table->constants.dead) { + (void) fprintf(table->err, + "Error: wrong number of dead nodes in constants\n"); + flag = 1; + } + gen = st_init_gen(edgeTable); + while (st_gen(gen, &f, &count)) { + if (count > (int)(f->ref) && f->ref != DD_MAXREF) { +#if SIZEOF_VOID_P == 8 + fprintf(table->err,"ref count error at node 0x%lx, count = %d, id = %u, ref = %u, then = 0x%lx, else = 0x%lx\n",(ptruint)f,count,f->index,f->ref,(ptruint)cuddT(f),(ptruint)cuddE(f)); +#else + fprintf(table->err,"ref count error at node 0x%x, count = %d, id = %hu, ref = %hu, then = 0x%x, else = 0x%x\n",(ptruint)f,count,f->index,f->ref,(ptruint)cuddT(f),(ptruint)cuddE(f)); +#endif + debugFindParent(table,f); + flag = 1; + } + } + st_free_gen(gen); + st_free_table(edgeTable); + + return (flag); + +} /* end of Cudd_DebugCheck */ + + +/**Function******************************************************************** + + Synopsis [Checks for several conditions that should not occur.] + + Description [Checks for the following conditions: +
    +
  • Wrong sizes of subtables. +
  • Wrong number of keys found in unique subtable. +
  • Wrong number of dead found in unique subtable. +
  • Wrong number of keys found in the constant table +
  • Wrong number of dead found in the constant table +
  • Wrong number of total slots found +
  • Wrong number of maximum keys found +
  • Wrong number of total dead found +
+ Reports the average length of non-empty lists. Returns the number of + subtables for which the number of keys is wrong.] + + SideEffects [None] + + SeeAlso [Cudd_DebugCheck] + +******************************************************************************/ +int +Cudd_CheckKeys( + DdManager * table) +{ + int size; + int i,j; + DdNodePtr *nodelist; + DdNode *node; + DdNode *sentinel = &(table->sentinel); + DdSubtable *subtable; + int keys; + int dead; + int count = 0; + int totalKeys = 0; + int totalSlots = 0; + int totalDead = 0; + int nonEmpty = 0; + unsigned int slots; + int logSlots; + int shift; + + size = table->size; + + for (i = 0; i < size; i++) { + subtable = &(table->subtables[i]); + nodelist = subtable->nodelist; + keys = subtable->keys; + dead = subtable->dead; + totalKeys += keys; + slots = subtable->slots; + shift = subtable->shift; + logSlots = sizeof(int) * 8 - shift; + if (((slots >> logSlots) << logSlots) != slots) { + (void) fprintf(table->err, + "Unique table %d is not the right power of 2\n", i); + (void) fprintf(table->err, + " slots = %u shift = %d\n", slots, shift); + } + totalSlots += slots; + totalDead += dead; + for (j = 0; (unsigned) j < slots; j++) { + node = nodelist[j]; + if (node != sentinel) { + nonEmpty++; + } + while (node != sentinel) { + keys--; + if (node->ref == 0) { + dead--; + } + node = node->next; + } + } + if (keys != 0) { + (void) fprintf(table->err, "Wrong number of keys found \ +in unique table %d (difference=%d)\n", i, keys); + count++; + } + if (dead != 0) { + (void) fprintf(table->err, "Wrong number of dead found \ +in unique table no. %d (difference=%d)\n", i, dead); + } + } /* for each BDD/ADD subtable */ + + /* Check the ZDD subtables. */ + size = table->sizeZ; + + for (i = 0; i < size; i++) { + subtable = &(table->subtableZ[i]); + nodelist = subtable->nodelist; + keys = subtable->keys; + dead = subtable->dead; + totalKeys += keys; + totalSlots += subtable->slots; + totalDead += dead; + for (j = 0; (unsigned) j < subtable->slots; j++) { + node = nodelist[j]; + if (node != NULL) { + nonEmpty++; + } + while (node != NULL) { + keys--; + if (node->ref == 0) { + dead--; + } + node = node->next; + } + } + if (keys != 0) { + (void) fprintf(table->err, "Wrong number of keys found \ +in ZDD unique table no. %d (difference=%d)\n", i, keys); + count++; + } + if (dead != 0) { + (void) fprintf(table->err, "Wrong number of dead found \ +in ZDD unique table no. %d (difference=%d)\n", i, dead); + } + } /* for each ZDD subtable */ + + /* Check the constant table. */ + subtable = &(table->constants); + nodelist = subtable->nodelist; + keys = subtable->keys; + dead = subtable->dead; + totalKeys += keys; + totalSlots += subtable->slots; + totalDead += dead; + for (j = 0; (unsigned) j < subtable->slots; j++) { + node = nodelist[j]; + if (node != NULL) { + nonEmpty++; + } + while (node != NULL) { + keys--; + if (node->ref == 0) { + dead--; + } + node = node->next; + } + } + if (keys != 0) { + (void) fprintf(table->err, "Wrong number of keys found \ +in the constant table (difference=%d)\n", keys); + count++; + } + if (dead != 0) { + (void) fprintf(table->err, "Wrong number of dead found \ +in the constant table (difference=%d)\n", dead); + } + if ((unsigned) totalKeys != table->keys + table->keysZ) { + (void) fprintf(table->err, "Wrong number of total keys found \ +(difference=%d)\n", (int) (totalKeys-table->keys)); + } + if ((unsigned) totalSlots != table->slots) { + (void) fprintf(table->err, "Wrong number of total slots found \ +(difference=%d)\n", (int) (totalSlots-table->slots)); + } + if (table->minDead != (unsigned) (table->gcFrac * table->slots)) { + (void) fprintf(table->err, "Wrong number of minimum dead found \ +(%u vs. %u)\n", table->minDead, + (unsigned) (table->gcFrac * (double) table->slots)); + } + if ((unsigned) totalDead != table->dead + table->deadZ) { + (void) fprintf(table->err, "Wrong number of total dead found \ +(difference=%d)\n", (int) (totalDead-table->dead)); + } + (void) fprintf(table->out,"Average length of non-empty lists = %g\n", + (double) table->keys / (double) nonEmpty); + + return(count); + +} /* end of Cudd_CheckKeys */ + + +/*---------------------------------------------------------------------------*/ +/* Definition of internal functions */ +/*---------------------------------------------------------------------------*/ + + +/**Function******************************************************************** + + Synopsis [Prints information about the heap.] + + Description [Prints to the manager's stdout the number of live nodes for each + level of the DD heap that contains at least one live node. It also + prints a summary containing: +
    +
  • total number of tables; +
  • number of tables with live nodes; +
  • table with the largest number of live nodes; +
  • number of nodes in that table. +
+ If more than one table contains the maximum number of live nodes, + only the one of lowest index is reported. Returns 1 in case of success + and 0 otherwise.] + + SideEffects [None] + + SeeAlso [] + +******************************************************************************/ +int +cuddHeapProfile( + DdManager * dd) +{ + int ntables = dd->size; + DdSubtable *subtables = dd->subtables; + int i, /* loop index */ + nodes, /* live nodes in i-th layer */ + retval, /* return value of fprintf */ + largest = -1, /* index of the table with most live nodes */ + maxnodes = -1, /* maximum number of live nodes in a table */ + nonempty = 0; /* number of tables with live nodes */ + + /* Print header. */ +#if SIZEOF_VOID_P == 8 + retval = fprintf(dd->out,"*** DD heap profile for 0x%lx ***\n", + (ptruint) dd); +#else + retval = fprintf(dd->out,"*** DD heap profile for 0x%x ***\n", + (ptruint) dd); +#endif + if (retval == EOF) return 0; + + /* Print number of live nodes for each nonempty table. */ + for (i=0; iout,"%5d: %5d nodes\n", i, nodes); + if (retval == EOF) return 0; + if (nodes > maxnodes) { + maxnodes = nodes; + largest = i; + } + } + } + + nodes = dd->constants.keys - dd->constants.dead; + if (nodes) { + nonempty++; + retval = fprintf(dd->out,"const: %5d nodes\n", nodes); + if (retval == EOF) return 0; + if (nodes > maxnodes) { + maxnodes = nodes; + largest = CUDD_CONST_INDEX; + } + } + + /* Print summary. */ + retval = fprintf(dd->out,"Summary: %d tables, %d non-empty, largest: %d ", + ntables+1, nonempty, largest); + if (retval == EOF) return 0; + retval = fprintf(dd->out,"(with %d nodes)\n", maxnodes); + if (retval == EOF) return 0; + + return(1); + +} /* end of cuddHeapProfile */ + + +/**Function******************************************************************** + + Synopsis [Prints out information on a node.] + + Description [Prints out information on a node.] + + SideEffects [None] + + SeeAlso [] + +******************************************************************************/ +void +cuddPrintNode( + DdNode * f, + FILE *fp) +{ + f = Cudd_Regular(f); +#if SIZEOF_VOID_P == 8 + (void) fprintf(fp," node 0x%lx, id = %u, ref = %u, then = 0x%lx, else = 0x%lx\n",(ptruint)f,f->index,f->ref,(ptruint)cuddT(f),(ptruint)cuddE(f)); +#else + (void) fprintf(fp," node 0x%x, id = %hu, ref = %hu, then = 0x%x, else = 0x%x\n",(ptruint)f,f->index,f->ref,(ptruint)cuddT(f),(ptruint)cuddE(f)); +#endif + +} /* end of cuddPrintNode */ + + + +/**Function******************************************************************** + + Synopsis [Prints the variable groups as a parenthesized list.] + + Description [Prints the variable groups as a parenthesized list. + For each group the level range that it represents is printed. After + each group, the group's flags are printed, preceded by a `|'. For + each flag (except MTR_TERMINAL) a character is printed. +
    +
  • F: MTR_FIXED +
  • N: MTR_NEWNODE +
  • S: MTR_SOFT +
+ The second argument, silent, if different from 0, causes + Cudd_PrintVarGroups to only check the syntax of the group tree.] + + SideEffects [None] + + SeeAlso [] + +******************************************************************************/ +void +cuddPrintVarGroups( + DdManager * dd /* manager */, + MtrNode * root /* root of the group tree */, + int zdd /* 0: BDD; 1: ZDD */, + int silent /* flag to check tree syntax only */) +{ + MtrNode *node; + int level; + + assert(root != NULL); + assert(root->younger == NULL || root->younger->elder == root); + assert(root->elder == NULL || root->elder->younger == root); + if (zdd) { + level = dd->permZ[root->index]; + } else { + level = dd->perm[root->index]; + } + if (!silent) (void) printf("(%d",level); + if (MTR_TEST(root,MTR_TERMINAL) || root->child == NULL) { + if (!silent) (void) printf(","); + } else { + node = root->child; + while (node != NULL) { + assert(node->low >= root->low && (int) (node->low + node->size) <= (int) (root->low + root->size)); + assert(node->parent == root); + cuddPrintVarGroups(dd,node,zdd,silent); + node = node->younger; + } + } + if (!silent) { + (void) printf("%d", (int) (level + root->size - 1)); + if (root->flags != MTR_DEFAULT) { + (void) printf("|"); + if (MTR_TEST(root,MTR_FIXED)) (void) printf("F"); + if (MTR_TEST(root,MTR_NEWNODE)) (void) printf("N"); + if (MTR_TEST(root,MTR_SOFT)) (void) printf("S"); + } + (void) printf(")"); + if (root->parent == NULL) (void) printf("\n"); + } + assert((root->flags &~(MTR_TERMINAL | MTR_SOFT | MTR_FIXED | MTR_NEWNODE)) == 0); + return; + +} /* end of cuddPrintVarGroups */ + + +/*---------------------------------------------------------------------------*/ +/* Definition of static functions */ +/*---------------------------------------------------------------------------*/ + + +/**Function******************************************************************** + + Synopsis [Searches the subtables above node for its parents.] + + Description [] + + SideEffects [None] + + SeeAlso [] + +******************************************************************************/ +static void +debugFindParent( + DdManager * table, + DdNode * node) +{ + int i,j; + int slots; + DdNodePtr *nodelist; + DdNode *f; + + for (i = 0; i < cuddI(table,node->index); i++) { + nodelist = table->subtables[i].nodelist; + slots = table->subtables[i].slots; + + for (j=0;jout,"parent is at 0x%lx, id = %u, ref = %u, then = 0x%lx, else = 0x%lx\n", + (ptruint)f,f->index,f->ref,(ptruint)cuddT(f),(ptruint)cuddE(f)); +#else + (void) fprintf(table->out,"parent is at 0x%x, id = %hu, ref = %hu, then = 0x%x, else = 0x%x\n", + (ptruint)f,f->index,f->ref,(ptruint)cuddT(f),(ptruint)cuddE(f)); +#endif + } + f = f->next; + } + } + } + +} /* end of debugFindParent */ + + +#if 0 +/**Function******************************************************************** + + Synopsis [Reports an error if a (dead) node has a non-dead parent.] + + Description [Searches all the subtables above node. Very expensive. + The same check is now implemented more efficiently in ddDebugCheck.] + + SideEffects [None] + + SeeAlso [debugFindParent] + +******************************************************************************/ +static void +debugCheckParent( + DdManager * table, + DdNode * node) +{ + int i,j; + int slots; + DdNode **nodelist,*f; + + for (i = 0; i < cuddI(table,node->index); i++) { + nodelist = table->subtables[i].nodelist; + slots = table->subtables[i].slots; + + for (j=0;jref != 0) { + (void) fprintf(table->err, + "error with zero ref count\n"); + (void) fprintf(table->err,"parent is 0x%x, id = %u, ref = %u, then = 0x%x, else = 0x%x\n",f,f->index,f->ref,cuddT(f),cuddE(f)); + (void) fprintf(table->err,"child is 0x%x, id = %u, ref = %u, then = 0x%x, else = 0x%x\n",node,node->index,node->ref,cuddT(node),cuddE(node)); + } + f = f->next; + } + } + } +} +#endif +/**CFile*********************************************************************** + + FileName [cuddCof.c] + + PackageName [cudd] + + Synopsis [Cofactoring functions.] + + Description [External procedures included in this module: +
    +
  • Cudd_Cofactor() +
  • Cudd_CheckCube() +
+ Internal procedures included in this module: +
    +
  • cuddGetBranches() +
  • cuddCofactorRecur() +
+ ] + + SeeAlso [] + + Author [Fabio Somenzi] + + Copyright [Copyright (c) 1995-2012, Regents of the University of Colorado + + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + Neither the name of the University of Colorado nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE.] + +******************************************************************************/ + + +/*---------------------------------------------------------------------------*/ +/* Constant declarations */ +/*---------------------------------------------------------------------------*/ + + +/*---------------------------------------------------------------------------*/ +/* Stucture declarations */ +/*---------------------------------------------------------------------------*/ + + +/*---------------------------------------------------------------------------*/ +/* Type declarations */ +/*---------------------------------------------------------------------------*/ + + +/*---------------------------------------------------------------------------*/ +/* Variable declarations */ +/*---------------------------------------------------------------------------*/ + +//#ifndef lint +//static char rcsid[] DD_UNUSED = "$Id: cuddCof.c,v 1.11 2012/02/05 01:07:18 fabio Exp $"; +//#endif + +/*---------------------------------------------------------------------------*/ +/* Macro declarations */ +/*---------------------------------------------------------------------------*/ + + +/**AutomaticStart*************************************************************/ + +/*---------------------------------------------------------------------------*/ +/* Static function prototypes */ +/*---------------------------------------------------------------------------*/ + + +/**AutomaticEnd***************************************************************/ + + +/*---------------------------------------------------------------------------*/ +/* Definition of exported functions */ +/*---------------------------------------------------------------------------*/ + + +/**Function******************************************************************** + + Synopsis [Computes the cofactor of f with respect to g.] + + Description [Computes the cofactor of f with respect to g; g must be + the BDD or the ADD of a cube. Returns a pointer to the cofactor if + successful; NULL otherwise.] + + SideEffects [None] + + SeeAlso [Cudd_bddConstrain Cudd_bddRestrict] + +******************************************************************************/ +DdNode * +Cudd_Cofactor( + DdManager * dd, + DdNode * f, + DdNode * g) +{ + DdNode *res,*zero; + + zero = Cudd_Not(DD_ONE(dd)); + if (g == zero || g == DD_ZERO(dd)) { + (void) fprintf(dd->err,"Cudd_Cofactor: Invalid restriction 1\n"); + dd->errorCode = CUDD_INVALID_ARG; + return(NULL); + } + do { + dd->reordered = 0; + res = cuddCofactorRecur(dd,f,g); + } while (dd->reordered == 1); + return(res); + +} /* end of Cudd_Cofactor */ + + +/**Function******************************************************************** + + Synopsis [Checks whether g is the BDD of a cube.] + + Description [Checks whether g is the BDD of a cube. Returns 1 in case + of success; 0 otherwise. The constant 1 is a valid cube, but all other + constant functions cause cuddCheckCube to return 0.] + + SideEffects [None] + + SeeAlso [] + +******************************************************************************/ +int +Cudd_CheckCube( + DdManager * dd, + DdNode * g) +{ + DdNode *g1,*g0,*one,*zero; + + one = DD_ONE(dd); + if (g == one) return(1); + if (Cudd_IsConstant(g)) return(0); + + zero = Cudd_Not(one); + cuddGetBranches(g,&g1,&g0); + + if (g0 == zero) { + return(Cudd_CheckCube(dd, g1)); + } + if (g1 == zero) { + return(Cudd_CheckCube(dd, g0)); + } + return(0); + +} /* end of Cudd_CheckCube */ + + +/*---------------------------------------------------------------------------*/ +/* Definition of internal functions */ +/*---------------------------------------------------------------------------*/ + + +/**Function******************************************************************** + + Synopsis [Computes the children of g.] + + Description [] + + SideEffects [None] + + SeeAlso [] + +******************************************************************************/ +void +cuddGetBranches( + DdNode * g, + DdNode ** g1, + DdNode ** g0) +{ + DdNode *G = Cudd_Regular(g); + + *g1 = cuddT(G); + *g0 = cuddE(G); + if (Cudd_IsComplement(g)) { + *g1 = Cudd_Not(*g1); + *g0 = Cudd_Not(*g0); + } + +} /* end of cuddGetBranches */ + + +/**Function******************************************************************** + + Synopsis [Performs the recursive step of Cudd_Cofactor.] + + Description [Performs the recursive step of Cudd_Cofactor. Returns a + pointer to the cofactor if successful; NULL otherwise.] + + SideEffects [None] + + SeeAlso [Cudd_Cofactor] + +******************************************************************************/ +DdNode * +cuddCofactorRecur( + DdManager * dd, + DdNode * f, + DdNode * g) +{ + DdNode *one,*zero,*F,*G,*g1,*g0,*f1,*f0,*t,*e,*r; + unsigned int topf,topg; + int comple; + + statLine(dd); + F = Cudd_Regular(f); + if (cuddIsConstant(F)) return(f); + + one = DD_ONE(dd); + + /* The invariant g != 0 is true on entry to this procedure and is + ** recursively maintained by it. Therefore it suffices to test g + ** against one to make sure it is not constant. + */ + if (g == one) return(f); + /* From now on, f and g are known not to be constants. */ + + comple = f != F; + r = cuddCacheLookup2(dd,Cudd_Cofactor,F,g); + if (r != NULL) { + return(Cudd_NotCond(r,comple)); + } + + topf = dd->perm[F->index]; + G = Cudd_Regular(g); + topg = dd->perm[G->index]; + + /* We take the cofactors of F because we are going to rely on + ** the fact that the cofactors of the complement are the complements + ** of the cofactors to better utilize the cache. Variable comple + ** remembers whether we have to complement the result or not. + */ + if (topf <= topg) { + f1 = cuddT(F); f0 = cuddE(F); + } else { + f1 = f0 = F; + } + if (topg <= topf) { + g1 = cuddT(G); g0 = cuddE(G); + if (g != G) { g1 = Cudd_Not(g1); g0 = Cudd_Not(g0); } + } else { + g1 = g0 = g; + } + + zero = Cudd_Not(one); + if (topf >= topg) { + if (g0 == zero || g0 == DD_ZERO(dd)) { + r = cuddCofactorRecur(dd, f1, g1); + } else if (g1 == zero || g1 == DD_ZERO(dd)) { + r = cuddCofactorRecur(dd, f0, g0); + } else { + (void) fprintf(dd->out, + "Cudd_Cofactor: Invalid restriction 2\n"); + dd->errorCode = CUDD_INVALID_ARG; + return(NULL); + } + if (r == NULL) return(NULL); + } else /* if (topf < topg) */ { + t = cuddCofactorRecur(dd, f1, g); + if (t == NULL) return(NULL); + cuddRef(t); + e = cuddCofactorRecur(dd, f0, g); + if (e == NULL) { + Cudd_RecursiveDeref(dd, t); + return(NULL); + } + cuddRef(e); + + if (t == e) { + r = t; + } else if (Cudd_IsComplement(t)) { + r = cuddUniqueInter(dd,(int)F->index,Cudd_Not(t),Cudd_Not(e)); + if (r != NULL) + r = Cudd_Not(r); + } else { + r = cuddUniqueInter(dd,(int)F->index,t,e); + } + if (r == NULL) { + Cudd_RecursiveDeref(dd ,e); + Cudd_RecursiveDeref(dd ,t); + return(NULL); + } + cuddDeref(t); + cuddDeref(e); + } + + cuddCacheInsert2(dd,Cudd_Cofactor,F,g,r); + + return(Cudd_NotCond(r,comple)); + +} /* end of cuddCofactorRecur */ + + +/*---------------------------------------------------------------------------*/ +/* Definition of static functions */ +/*---------------------------------------------------------------------------*/ + +/**CFile*********************************************************************** + + FileName [cuddExact.c] + + PackageName [cudd] + + Synopsis [Functions for exact variable reordering.] + + Description [External procedures included in this file: +
    +
+ Internal procedures included in this module: +
    +
  • cuddExact() +
+ Static procedures included in this module: +
    +
  • getMaxBinomial() +
  • gcd() +
  • getMatrix() +
  • freeMatrix() +
  • getLevelKeys() +
  • ddShuffle() +
  • ddSiftUp() +
  • updateUB() +
  • ddCountRoots() +
  • ddClearGlobal() +
  • computeLB() +
  • updateEntry() +
  • pushDown() +
  • initSymmInfo() +
] + + Author [Cheng Hua, Fabio Somenzi] + + Copyright [Copyright (c) 1995-2012, Regents of the University of Colorado + + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + Neither the name of the University of Colorado nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE.] + +******************************************************************************/ + + +/*---------------------------------------------------------------------------*/ +/* Constant declarations */ +/*---------------------------------------------------------------------------*/ + + +/*---------------------------------------------------------------------------*/ +/* Stucture declarations */ +/*---------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------*/ +/* Type declarations */ +/*---------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------*/ +/* Variable declarations */ +/*---------------------------------------------------------------------------*/ + +//#ifndef lint +//static char rcsid[] DD_UNUSED = "$Id: cuddExact.c,v 1.30 2012/02/05 01:07:18 fabio Exp $"; +//#endif + +#ifdef DD_STATS +static int ddTotalShuffles; +#endif + +/*---------------------------------------------------------------------------*/ +/* Macro declarations */ +/*---------------------------------------------------------------------------*/ + +/**AutomaticStart*************************************************************/ + +/*---------------------------------------------------------------------------*/ +/* Static function prototypes */ +/*---------------------------------------------------------------------------*/ + +static int getMaxBinomial (int n); +static DdHalfWord ** getMatrix (int rows, int cols); +static void freeMatrix (DdHalfWord **matrix); +static int getLevelKeys (DdManager *table, int l); +static int ddShuffle (DdManager *table, DdHalfWord *permutation, int lower, int upper); +static int ddSiftUp (DdManager *table, int x, int xLow); +static int updateUB (DdManager *table, int oldBound, DdHalfWord *bestOrder, int lower, int upper); +static int ddCountRoots (DdManager *table, int lower, int upper); +static void ddClearGlobal (DdManager *table, int lower, int maxlevel); +static int computeLB (DdManager *table, DdHalfWord *order, int roots, int cost, int lower, int upper, int level); +static int updateEntry (DdManager *table, DdHalfWord *order, int level, int cost, DdHalfWord **orders, int *costs, int subsets, char *mask, int lower, int upper); +static void pushDown (DdHalfWord *order, int j, int level); +static DdHalfWord * initSymmInfo (DdManager *table, int lower, int upper); +static int checkSymmInfo (DdManager *table, DdHalfWord *symmInfo, int index, int level); + +/**AutomaticEnd***************************************************************/ + + +/*---------------------------------------------------------------------------*/ +/* Definition of exported functions */ +/*---------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------*/ +/* Definition of internal functions */ +/*---------------------------------------------------------------------------*/ + + +/**Function******************************************************************** + + Synopsis [Exact variable ordering algorithm.] + + Description [Exact variable ordering algorithm. Finds an optimum + order for the variables between lower and upper. Returns 1 if + successful; 0 otherwise.] + + SideEffects [None] + + SeeAlso [] + +******************************************************************************/ +int +cuddExact( + DdManager * table, + int lower, + int upper) +{ + int k, i, j; + int maxBinomial, oldSubsets, newSubsets; + int subsetCost; + int size; /* number of variables to be reordered */ + int unused, nvars, level, result; + int upperBound, lowerBound, cost; + int roots; + char *mask = NULL; + DdHalfWord *symmInfo = NULL; + DdHalfWord **newOrder = NULL; + DdHalfWord **oldOrder = NULL; + int *newCost = NULL; + int *oldCost = NULL; + DdHalfWord **tmpOrder; + int *tmpCost; + DdHalfWord *bestOrder = NULL; + DdHalfWord *order; +#ifdef DD_STATS + int ddTotalSubsets; +#endif + + /* Restrict the range to be reordered by excluding unused variables + ** at the two ends. */ + while (table->subtables[lower].keys == 1 && + table->vars[table->invperm[lower]]->ref == 1 && + lower < upper) + lower++; + while (table->subtables[upper].keys == 1 && + table->vars[table->invperm[upper]]->ref == 1 && + lower < upper) + upper--; + if (lower == upper) return(1); /* trivial problem */ + + /* Apply symmetric sifting to get a good upper bound and to extract + ** symmetry information. */ + result = cuddSymmSiftingConv(table,lower,upper); + if (result == 0) goto cuddExactOutOfMem; + +#ifdef DD_STATS + (void) fprintf(table->out,"\n"); + ddTotalShuffles = 0; + ddTotalSubsets = 0; +#endif + + /* Initialization. */ + nvars = table->size; + size = upper - lower + 1; + /* Count unused variable among those to be reordered. This is only + ** used to compute maxBinomial. */ + unused = 0; + for (i = lower + 1; i < upper; i++) { + if (table->subtables[i].keys == 1 && + table->vars[table->invperm[i]]->ref == 1) + unused++; + } + + /* Find the maximum number of subsets we may have to store. */ + maxBinomial = getMaxBinomial(size - unused); + if (maxBinomial == -1) goto cuddExactOutOfMem; + + newOrder = getMatrix(maxBinomial, size); + if (newOrder == NULL) goto cuddExactOutOfMem; + + newCost = ALLOC(int, maxBinomial); + if (newCost == NULL) goto cuddExactOutOfMem; + + oldOrder = getMatrix(maxBinomial, size); + if (oldOrder == NULL) goto cuddExactOutOfMem; + + oldCost = ALLOC(int, maxBinomial); + if (oldCost == NULL) goto cuddExactOutOfMem; + + bestOrder = ALLOC(DdHalfWord, size); + if (bestOrder == NULL) goto cuddExactOutOfMem; + + mask = ALLOC(char, nvars); + if (mask == NULL) goto cuddExactOutOfMem; + + symmInfo = initSymmInfo(table, lower, upper); + if (symmInfo == NULL) goto cuddExactOutOfMem; + + roots = ddCountRoots(table, lower, upper); + + /* Initialize the old order matrix for the empty subset and the best + ** order to the current order. The cost for the empty subset includes + ** the cost of the levels between upper and the constants. These levels + ** are not going to change. Hence, we count them only once. + */ + oldSubsets = 1; + for (i = 0; i < size; i++) { + oldOrder[0][i] = bestOrder[i] = (DdHalfWord) table->invperm[i+lower]; + } + subsetCost = table->constants.keys; + for (i = upper + 1; i < nvars; i++) + subsetCost += getLevelKeys(table,i); + oldCost[0] = subsetCost; + /* The upper bound is initialized to the current size of the BDDs. */ + upperBound = table->keys - table->isolated; + + /* Now consider subsets of increasing size. */ + for (k = 1; k <= size; k++) { +#ifdef DD_STATS + (void) fprintf(table->out,"Processing subsets of size %d\n", k); + fflush(table->out); +#endif + newSubsets = 0; + level = size - k; /* offset of first bottom variable */ + + for (i = 0; i < oldSubsets; i++) { /* for each subset of size k-1 */ + order = oldOrder[i]; + cost = oldCost[i]; + lowerBound = computeLB(table, order, roots, cost, lower, upper, + level); + if (lowerBound >= upperBound) + continue; + /* Impose new order. */ + result = ddShuffle(table, order, lower, upper); + if (result == 0) goto cuddExactOutOfMem; + upperBound = updateUB(table,upperBound,bestOrder,lower,upper); + /* For each top bottom variable. */ + for (j = level; j >= 0; j--) { + /* Skip unused variables. */ + if (table->subtables[j+lower-1].keys == 1 && + table->vars[table->invperm[j+lower-1]]->ref == 1) continue; + /* Find cost under this order. */ + subsetCost = cost + getLevelKeys(table, lower + level); + newSubsets = updateEntry(table, order, level, subsetCost, + newOrder, newCost, newSubsets, mask, + lower, upper); + if (j == 0) + break; + if (checkSymmInfo(table, symmInfo, order[j-1], level) == 0) + continue; + pushDown(order,j-1,level); + /* Impose new order. */ + result = ddShuffle(table, order, lower, upper); + if (result == 0) goto cuddExactOutOfMem; + upperBound = updateUB(table,upperBound,bestOrder,lower,upper); + } /* for each bottom variable */ + } /* for each subset of size k */ + + /* New orders become old orders in preparation for next iteration. */ + tmpOrder = oldOrder; tmpCost = oldCost; + oldOrder = newOrder; oldCost = newCost; + newOrder = tmpOrder; newCost = tmpCost; +#ifdef DD_STATS + ddTotalSubsets += newSubsets; +#endif + oldSubsets = newSubsets; + } + result = ddShuffle(table, bestOrder, lower, upper); + if (result == 0) goto cuddExactOutOfMem; +#ifdef DD_STATS + #ifdef DD_VERBOSE + (void) fprintf(table->out,"\n"); +#endif + (void) fprintf(table->out,"#:S_EXACT %8d: total subsets\n", + ddTotalSubsets); + (void) fprintf(table->out,"#:H_EXACT %8d: total shuffles", + ddTotalShuffles); +#endif + + freeMatrix(newOrder); + freeMatrix(oldOrder); + FREE(bestOrder); + FREE(oldCost); + FREE(newCost); + FREE(symmInfo); + FREE(mask); + return(1); + + cuddExactOutOfMem: + + if (newOrder != NULL) freeMatrix(newOrder); + if (oldOrder != NULL) freeMatrix(oldOrder); + if (bestOrder != NULL) FREE(bestOrder); + if (oldCost != NULL) FREE(oldCost); + if (newCost != NULL) FREE(newCost); + if (symmInfo != NULL) FREE(symmInfo); + if (mask != NULL) FREE(mask); + table->errorCode = CUDD_MEMORY_OUT; + return(0); + +} /* end of cuddExact */ + + +/**Function******************************************************************** + + Synopsis [Returns the maximum value of (n choose k) for a given n.] + + Description [Computes the maximum value of (n choose k) for a given + n. The maximum value occurs for k = n/2 when n is even, or k = + (n-1)/2 when n is odd. The algorithm used in this procedure avoids + intermediate overflow problems. It is based on the identity +
+    binomial(n,k) = n/k * binomial(n-1,k-1).
+  
+ Returns the computed value if successful; -1 if out of range.] + + SideEffects [None] + + SeeAlso [] + +******************************************************************************/ +static int +getMaxBinomial( + int n) +{ + double i, j, result; + + if (n < 0 || n > 33) return(-1); /* error */ + if (n < 2) return(1); + + for (result = (double)((n+3)/2), i = result+1, j=2; i <= n; i++, j++) { + result *= i; + result /= j; + } + + return((int)result); + +} /* end of getMaxBinomial */ + + +#if 0 +/**Function******************************************************************** + + Synopsis [Returns the gcd of two integers.] + + Description [Returns the gcd of two integers. Uses the binary GCD + algorithm described in Cormen, Leiserson, and Rivest.] + + SideEffects [None] + + SeeAlso [] + +******************************************************************************/ +static int +gcd( + int x, + int y) +{ + int a; + int b; + int lsbMask; + + /* GCD(n,0) = n. */ + if (x == 0) return(y); + if (y == 0) return(x); + + a = x; b = y; lsbMask = 1; + + /* Here both a and b are != 0. The iteration maintains this invariant. + ** Hence, we only need to check for when they become equal. + */ + while (a != b) { + if (a & lsbMask) { + if (b & lsbMask) { /* both odd */ + if (a < b) { + b = (b - a) >> 1; + } else { + a = (a - b) >> 1; + } + } else { /* a odd, b even */ + b >>= 1; + } + } else { + if (b & lsbMask) { /* a even, b odd */ + a >>= 1; + } else { /* both even */ + lsbMask <<= 1; + } + } + } + + return(a); + +} /* end of gcd */ +#endif + + +/**Function******************************************************************** + + Synopsis [Allocates a two-dimensional matrix of ints.] + + Description [Allocates a two-dimensional matrix of ints. + Returns the pointer to the matrix if successful; NULL otherwise.] + + SideEffects [None] + + SeeAlso [freeMatrix] + +******************************************************************************/ +static DdHalfWord ** +getMatrix( + int rows /* number of rows */, + int cols /* number of columns */) +{ + DdHalfWord **matrix; + int i; + + if (cols*rows == 0) return(NULL); + matrix = ALLOC(DdHalfWord *, rows); + if (matrix == NULL) return(NULL); + matrix[0] = ALLOC(DdHalfWord, cols*rows); + if (matrix[0] == NULL) { + FREE(matrix); + return(NULL); + } + for (i = 1; i < rows; i++) { + matrix[i] = matrix[i-1] + cols; + } + return(matrix); + +} /* end of getMatrix */ + + +/**Function******************************************************************** + + Synopsis [Frees a two-dimensional matrix allocated by getMatrix.] + + Description [] + + SideEffects [None] + + SeeAlso [getMatrix] + +******************************************************************************/ +static void +freeMatrix( + DdHalfWord ** matrix) +{ + FREE(matrix[0]); + FREE(matrix); + return; + +} /* end of freeMatrix */ + + +/**Function******************************************************************** + + Synopsis [Returns the number of nodes at one level of a unique table.] + + Description [Returns the number of nodes at one level of a unique table. + The projection function, if isolated, is not counted.] + + SideEffects [None] + + SeeAlso [] + +******************************************************************************/ +static int +getLevelKeys( + DdManager * table, + int l) +{ + int isolated; + int x; /* x is an index */ + + x = table->invperm[l]; + isolated = table->vars[x]->ref == 1; + + return(table->subtables[l].keys - isolated); + +} /* end of getLevelKeys */ + + +/**Function******************************************************************** + + Synopsis [Reorders variables according to a given permutation.] + + Description [Reorders variables according to a given permutation. + The i-th permutation array contains the index of the variable that + should be brought to the i-th level. ddShuffle assumes that no + dead nodes are present and that the interaction matrix is properly + initialized. The reordering is achieved by a series of upward sifts. + Returns 1 if successful; 0 otherwise.] + + SideEffects [None] + + SeeAlso [] + +******************************************************************************/ +static int +ddShuffle( + DdManager * table, + DdHalfWord * permutation, + int lower, + int upper) +{ + DdHalfWord index; + int level; + int position; +#if 0 + int numvars; +#endif + int result; +#ifdef DD_STATS + unsigned long localTime; + int initialSize; +#ifdef DD_VERBOSE + int finalSize; +#endif + int previousSize; +#endif + +#ifdef DD_STATS + localTime = util_cpu_time(); + initialSize = table->keys - table->isolated; +#endif + +#if 0 + numvars = table->size; + + (void) fprintf(table->out,"%d:", ddTotalShuffles); + for (level = 0; level < numvars; level++) { + (void) fprintf(table->out," %d", table->invperm[level]); + } + (void) fprintf(table->out,"\n"); +#endif + + for (level = 0; level <= upper - lower; level++) { + index = permutation[level]; + position = table->perm[index]; +#ifdef DD_STATS + previousSize = table->keys - table->isolated; +#endif + result = ddSiftUp(table,position,level+lower); + if (!result) return(0); + } + +#ifdef DD_STATS + ddTotalShuffles++; +#ifdef DD_VERBOSE + finalSize = table->keys - table->isolated; + if (finalSize < initialSize) { + (void) fprintf(table->out,"-"); + } else if (finalSize > initialSize) { + (void) fprintf(table->out,"+"); + } else { + (void) fprintf(table->out,"="); + } + if ((ddTotalShuffles & 63) == 0) (void) fprintf(table->out,"\n"); + fflush(table->out); +#endif +#endif + + return(1); + +} /* end of ddShuffle */ + + +/**Function******************************************************************** + + Synopsis [Moves one variable up.] + + Description [Takes a variable from position x and sifts it up to + position xLow; xLow should be less than or equal to x. + Returns 1 if successful; 0 otherwise] + + SideEffects [None] + + SeeAlso [] + +******************************************************************************/ +static int +ddSiftUp( + DdManager * table, + int x, + int xLow) +{ + int y; + int size; + + y = cuddNextLow(table,x); + while (y >= xLow) { + size = cuddSwapInPlace(table,y,x); + if (size == 0) { + return(0); + } + x = y; + y = cuddNextLow(table,x); + } + return(1); + +} /* end of ddSiftUp */ + + +/**Function******************************************************************** + + Synopsis [Updates the upper bound and saves the best order seen so far.] + + Description [Updates the upper bound and saves the best order seen so far. + Returns the current value of the upper bound.] + + SideEffects [None] + + SeeAlso [] + +******************************************************************************/ +static int +updateUB( + DdManager * table, + int oldBound, + DdHalfWord * bestOrder, + int lower, + int upper) +{ + int i; + int newBound = table->keys - table->isolated; + + if (newBound < oldBound) { +#ifdef DD_STATS + (void) fprintf(table->out,"New upper bound = %d\n", newBound); + fflush(table->out); +#endif + for (i = lower; i <= upper; i++) + bestOrder[i-lower] = (DdHalfWord) table->invperm[i]; + return(newBound); + } else { + return(oldBound); + } + +} /* end of updateUB */ + + +/**Function******************************************************************** + + Synopsis [Counts the number of roots.] + + Description [Counts the number of roots at the levels between lower and + upper. The computation is based on breadth-first search. + A node is a root if it is not reachable from any previously visited node. + (All the nodes at level lower are therefore considered roots.) + The visited flag uses the LSB of the next pointer. Returns the root + count. The roots that are constant nodes are always ignored.] + + SideEffects [None] + + SeeAlso [ddClearGlobal] + +******************************************************************************/ +static int +ddCountRoots( + DdManager * table, + int lower, + int upper) +{ + int i,j; + DdNode *f; + DdNodePtr *nodelist; + DdNode *sentinel = &(table->sentinel); + int slots; + int roots = 0; + int maxlevel = lower; + + for (i = lower; i <= upper; i++) { + nodelist = table->subtables[i].nodelist; + slots = table->subtables[i].slots; + for (j = 0; j < slots; j++) { + f = nodelist[j]; + while (f != sentinel) { + /* A node is a root of the DAG if it cannot be + ** reached by nodes above it. If a node was never + ** reached during the previous depth-first searches, + ** then it is a root, and we start a new depth-first + ** search from it. + */ + if (!Cudd_IsComplement(f->next)) { + if (f != table->vars[f->index]) { + roots++; + } + } + if (!Cudd_IsConstant(cuddT(f))) { + cuddT(f)->next = Cudd_Complement(cuddT(f)->next); + if (table->perm[cuddT(f)->index] > maxlevel) + maxlevel = table->perm[cuddT(f)->index]; + } + if (!Cudd_IsConstant(cuddE(f))) { + Cudd_Regular(cuddE(f))->next = + Cudd_Complement(Cudd_Regular(cuddE(f))->next); + if (table->perm[Cudd_Regular(cuddE(f))->index] > maxlevel) + maxlevel = table->perm[Cudd_Regular(cuddE(f))->index]; + } + f = Cudd_Regular(f->next); + } + } + } + ddClearGlobal(table, lower, maxlevel); + + return(roots); + +} /* end of ddCountRoots */ + + +/**Function******************************************************************** + + Synopsis [Scans the DD and clears the LSB of the next pointers.] + + Description [Scans the DD and clears the LSB of the next pointers. + The LSB of the next pointers are used as markers to tell whether a + node was reached. Once the roots are counted, these flags are + reset.] + + SideEffects [None] + + SeeAlso [ddCountRoots] + +******************************************************************************/ +static void +ddClearGlobal( + DdManager * table, + int lower, + int maxlevel) +{ + int i,j; + DdNode *f; + DdNodePtr *nodelist; + DdNode *sentinel = &(table->sentinel); + int slots; + + for (i = lower; i <= maxlevel; i++) { + nodelist = table->subtables[i].nodelist; + slots = table->subtables[i].slots; + for (j = 0; j < slots; j++) { + f = nodelist[j]; + while (f != sentinel) { + f->next = Cudd_Regular(f->next); + f = f->next; + } + } + } + +} /* end of ddClearGlobal */ + + +/**Function******************************************************************** + + Synopsis [Computes a lower bound on the size of a BDD.] + + Description [Computes a lower bound on the size of a BDD from the + following factors: +
    +
  • size of the lower part of it; +
  • size of the part of the upper part not subjected to reordering; +
  • number of roots in the part of the BDD subjected to reordering; +
  • variable in the support of the roots in the upper part of the + BDD subjected to reordering. +
      ] + + SideEffects [None] + + SeeAlso [] + +******************************************************************************/ +static int +computeLB( + DdManager * table /* manager */, + DdHalfWord * order /* optimal order for the subset */, + int roots /* roots between lower and upper */, + int cost /* minimum cost for the subset */, + int lower /* lower level to be reordered */, + int upper /* upper level to be reordered */, + int level /* offset for the current top bottom var */ +) +{ + int i; + int lb = cost; + int lb1 = 0; + int lb2; + int support; + DdHalfWord ref; + + /* The levels not involved in reordering are not going to change. + ** Add their sizes to the lower bound. + */ + for (i = 0; i < lower; i++) { + lb += getLevelKeys(table,i); + } + /* If a variable is in the support, then there is going + ** to be at least one node labeled by that variable. + */ + for (i = lower; i <= lower+level; i++) { + support = table->subtables[i].keys > 1 || + table->vars[order[i-lower]]->ref > 1; + lb1 += support; + } + + /* Estimate the number of nodes required to connect the roots to + ** the nodes in the bottom part. */ + if (lower+level+1 < table->size) { + if (lower+level < upper) + ref = table->vars[order[level+1]]->ref; + else + ref = table->vars[table->invperm[upper+1]]->ref; + lb2 = table->subtables[lower+level+1].keys - + (ref > (DdHalfWord) 1) - roots; + } else { + lb2 = 0; + } + + lb += lb1 > lb2 ? lb1 : lb2; + + return(lb); + +} /* end of computeLB */ + + +/**Function******************************************************************** + + Synopsis [Updates entry for a subset.] + + Description [Updates entry for a subset. Finds the subset, if it exists. + If the new order for the subset has lower cost, or if the subset did not + exist, it stores the new order and cost. Returns the number of subsets + currently in the table.] + + SideEffects [None] + + SeeAlso [] + +******************************************************************************/ +static int +updateEntry( + DdManager * table, + DdHalfWord * order, + int level, + int cost, + DdHalfWord ** orders, + int * costs, + int subsets, + char * mask, + int lower, + int upper) +{ + int i, j; + int size = upper - lower + 1; + + /* Build a mask that says what variables are in this subset. */ + for (i = lower; i <= upper; i++) + mask[table->invperm[i]] = 0; + for (i = level; i < size; i++) + mask[order[i]] = 1; + + /* Check each subset until a match is found or all subsets are examined. */ + for (i = 0; i < subsets; i++) { + DdHalfWord *subset = orders[i]; + for (j = level; j < size; j++) { + if (mask[subset[j]] == 0) + break; + } + if (j == size) /* no mismatches: success */ + break; + } + if (i == subsets || cost < costs[i]) { /* add or replace */ + for (j = 0; j < size; j++) + orders[i][j] = order[j]; + costs[i] = cost; + subsets += (i == subsets); + } + return(subsets); + +} /* end of updateEntry */ + + +/**Function******************************************************************** + + Synopsis [Pushes a variable in the order down to position "level."] + + Description [] + + SideEffects [None] + + SeeAlso [] + +******************************************************************************/ +static void +pushDown( + DdHalfWord * order, + int j, + int level) +{ + int i; + DdHalfWord tmp; + + tmp = order[j]; + for (i = j; i < level; i++) { + order[i] = order[i+1]; + } + order[level] = tmp; + return; + +} /* end of pushDown */ + + +/**Function******************************************************************** + + Synopsis [Gathers symmetry information.] + + Description [Translates the symmetry information stored in the next + field of each subtable from level to indices. This procedure is called + immediately after symmetric sifting, so that the next fields are correct. + By translating this informaton in terms of indices, we make it independent + of subsequent reorderings. The format used is that of the next fields: + a circular list where each variable points to the next variable in the + same symmetry group. Only the entries between lower and upper are + considered. The procedure returns a pointer to an array + holding the symmetry information if successful; NULL otherwise.] + + SideEffects [None] + + SeeAlso [checkSymmInfo] + +******************************************************************************/ +static DdHalfWord * +initSymmInfo( + DdManager * table, + int lower, + int upper) +{ + int level, index, next, nextindex; + DdHalfWord *symmInfo; + + symmInfo = ALLOC(DdHalfWord, table->size); + if (symmInfo == NULL) return(NULL); + + for (level = lower; level <= upper; level++) { + index = table->invperm[level]; + next = table->subtables[level].next; + nextindex = table->invperm[next]; + symmInfo[index] = nextindex; + } + return(symmInfo); + +} /* end of initSymmInfo */ + + +/**Function******************************************************************** + + Synopsis [Check symmetry condition.] + + Description [Returns 1 if a variable is the one with the highest index + among those belonging to a symmetry group that are in the top part of + the BDD. The top part is given by level.] + + SideEffects [None] + + SeeAlso [initSymmInfo] + +******************************************************************************/ +static int +checkSymmInfo( + DdManager * table, + DdHalfWord * symmInfo, + int index, + int level) +{ + int i; + + i = symmInfo[index]; + while (i != index) { + if (index < i && table->perm[i] <= level) + return(0); + i = symmInfo[i]; + } + return(1); + +} /* end of checkSymmInfo */ + + +/**CFile*********************************************************************** + + FileName [cuddGenetic.c] + + PackageName [cudd] + + Synopsis [Genetic algorithm for variable reordering.] + + Description [Internal procedures included in this file: +
        +
      • cuddGa() +
      + Static procedures included in this module: +
        +
      • make_random() +
      • sift_up() +
      • build_dd() +
      • largest() +
      • rand_int() +
      • array_hash() +
      • array_compare() +
      • find_best() +
      • find_average_fitness() +
      • PMX() +
      • roulette() +
      + + The genetic algorithm implemented here is as follows. We start with + the current DD order. We sift this order and use this as the + reference DD. We only keep 1 DD around for the entire process and + simply rearrange the order of this DD, storing the various orders + and their corresponding DD sizes. We generate more random orders to + build an initial population. This initial population is 3 times the + number of variables, with a maximum of 120. Each random order is + built (from the reference DD) and its size stored. Each random + order is also sifted to keep the DD sizes fairly small. Then a + crossover is performed between two orders (picked randomly) and the + two resulting DDs are built and sifted. For each new order, if its + size is smaller than any DD in the population, it is inserted into + the population and the DD with the largest number of nodes is thrown + out. The crossover process happens up to 50 times, and at this point + the DD in the population with the smallest size is chosen as the + result. This DD must then be built from the reference DD.] + + SeeAlso [] + + Author [Curt Musfeldt, Alan Shuler, Fabio Somenzi] + + Copyright [Copyright (c) 1995-2012, Regents of the University of Colorado + + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + Neither the name of the University of Colorado nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE.] + +******************************************************************************/ + + +/*---------------------------------------------------------------------------*/ +/* Constant declarations */ +/*---------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------*/ +/* Stucture declarations */ +/*---------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------*/ +/* Type declarations */ +/*---------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------*/ +/* Variable declarations */ +/*---------------------------------------------------------------------------*/ + +//#ifndef lint +//static char rcsid[] DD_UNUSED = "$Id: cuddGenetic.c,v 1.30 2012/02/05 01:07:18 fabio Exp $"; +//#endif + +static int popsize; /* the size of the population */ +static int numvars; /* the number of input variables in the ckt. */ +/* storedd stores the population orders and sizes. This table has two +** extra rows and one extras column. The two extra rows are used for the +** offspring produced by a crossover. Each row stores one order and its +** size. The order is stored by storing the indices of variables in the +** order in which they appear in the order. The table is in reality a +** one-dimensional array which is accessed via a macro to give the illusion +** it is a two-dimensional structure. +*/ +static int *storedd; +static st_table *computed; /* hash table to identify existing orders */ +static int *repeat; /* how many times an order is present */ +static int large; /* stores the index of the population with + ** the largest number of nodes in the DD */ +static int result; +static int cross; /* the number of crossovers to perform */ + +/*---------------------------------------------------------------------------*/ +/* Macro declarations */ +/*---------------------------------------------------------------------------*/ + +/* macro used to access the population table as if it were a +** two-dimensional structure. +*/ +#define STOREDD(i,j) storedd[(i)*(numvars+1)+(j)] + +#ifdef __cplusplus +extern "C" { +#endif + +/**AutomaticStart*************************************************************/ + +/*---------------------------------------------------------------------------*/ +/* Static function prototypes */ +/*---------------------------------------------------------------------------*/ + +static int make_random (DdManager *table, int lower); +static int sift_up (DdManager *table, int x, int x_low); +static int build_dd (DdManager *table, int num, int lower, int upper); +static int largest (void); +static int rand_int (int a); +static int array_hash (char *array, int modulus); +static int array_compare (const char *array1, const char *array2); +static int find_best (void); +#ifdef DD_STATS +static double find_average_fitness (void); +#endif +static int PMX (int maxvar); +static int roulette (int *p1, int *p2); + +/**AutomaticEnd***************************************************************/ + +#ifdef __cplusplus +} +#endif + +/*---------------------------------------------------------------------------*/ +/* Definition of exported functions */ +/*---------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------*/ +/* Definition of internal functions */ +/*---------------------------------------------------------------------------*/ + + +/**Function******************************************************************** + + Synopsis [Genetic algorithm for DD reordering.] + + Description [Genetic algorithm for DD reordering. + The two children of a crossover will be stored in + storedd[popsize] and storedd[popsize+1] --- the last two slots in the + storedd array. (This will make comparisons and replacement easy.) + Returns 1 in case of success; 0 otherwise.] + + SideEffects [None] + + SeeAlso [] + +******************************************************************************/ +int +cuddGa( + DdManager * table /* manager */, + int lower /* lowest level to be reordered */, + int upper /* highest level to be reorderded */) +{ + int i,n,m; /* dummy/loop vars */ + int index; +#ifdef DD_STATS + double average_fitness; +#endif + int small; /* index of smallest DD in population */ + + /* Do an initial sifting to produce at least one reasonable individual. */ + if (!cuddSifting(table,lower,upper)) return(0); + + /* Get the initial values. */ + numvars = upper - lower + 1; /* number of variables to be reordered */ + if (table->populationSize == 0) { + popsize = 3 * numvars; /* population size is 3 times # of vars */ + if (popsize > 120) { + popsize = 120; /* Maximum population size is 120 */ + } + } else { + popsize = table->populationSize; /* user specified value */ + } + if (popsize < 4) popsize = 4; /* enforce minimum population size */ + + /* Allocate population table. */ + storedd = ALLOC(int,(popsize+2)*(numvars+1)); + if (storedd == NULL) { + table->errorCode = CUDD_MEMORY_OUT; + return(0); + } + + /* Initialize the computed table. This table is made up of two data + ** structures: A hash table with the key given by the order, which says + ** if a given order is present in the population; and the repeat + ** vector, which says how many copies of a given order are stored in + ** the population table. If there are multiple copies of an order, only + ** one has a repeat count greater than 1. This copy is the one pointed + ** by the computed table. + */ + repeat = ALLOC(int,popsize); + if (repeat == NULL) { + table->errorCode = CUDD_MEMORY_OUT; + FREE(storedd); + return(0); + } + for (i = 0; i < popsize; i++) { + repeat[i] = 0; + } + computed = st_init_table(array_compare,array_hash); + if (computed == NULL) { + table->errorCode = CUDD_MEMORY_OUT; + FREE(storedd); + FREE(repeat); + return(0); + } + + /* Copy the current DD and its size to the population table. */ + for (i = 0; i < numvars; i++) { + STOREDD(0,i) = table->invperm[i+lower]; /* order of initial DD */ + } + STOREDD(0,numvars) = table->keys - table->isolated; /* size of initial DD */ + + /* Store the initial order in the computed table. */ + if (st_insert(computed,(char *)storedd,(char *) 0) == ST_OUT_OF_MEM) { + FREE(storedd); + FREE(repeat); + st_free_table(computed); + return(0); + } + repeat[0]++; + + /* Insert the reverse order as second element of the population. */ + for (i = 0; i < numvars; i++) { + STOREDD(1,numvars-1-i) = table->invperm[i+lower]; /* reverse order */ + } + + /* Now create the random orders. make_random fills the population + ** table with random permutations. The successive loop builds and sifts + ** the DDs for the reverse order and each random permutation, and stores + ** the results in the computed table. + */ + if (!make_random(table,lower)) { + table->errorCode = CUDD_MEMORY_OUT; + FREE(storedd); + FREE(repeat); + st_free_table(computed); + return(0); + } + for (i = 1; i < popsize; i++) { + result = build_dd(table,i,lower,upper); /* build and sift order */ + if (!result) { + FREE(storedd); + FREE(repeat); + st_free_table(computed); + return(0); + } + if (st_lookup_int(computed,(char *)&STOREDD(i,0),&index)) { + repeat[index]++; + } else { + if (st_insert(computed,(char *)&STOREDD(i,0),(char *)(long)i) == + ST_OUT_OF_MEM) { + FREE(storedd); + FREE(repeat); + st_free_table(computed); + return(0); + } + repeat[i]++; + } + } + +#if 0 + #ifdef DD_STATS + /* Print the initial population. */ + (void) fprintf(table->out,"Initial population after sifting\n"); + for (m = 0; m < popsize; m++) { + for (i = 0; i < numvars; i++) { + (void) fprintf(table->out," %2d",STOREDD(m,i)); + } + (void) fprintf(table->out," : %3d (%d)\n", + STOREDD(m,numvars),repeat[m]); + } +#endif +#endif + + small = find_best(); +#ifdef DD_STATS + average_fitness = find_average_fitness(); + (void) fprintf(table->out,"\nInitial population: best fitness = %d, average fitness %8.3f",STOREDD(small,numvars),average_fitness); +#endif + + /* Decide how many crossovers should be tried. */ + if (table->numberXovers == 0) { + cross = 3*numvars; + if (cross > 60) { /* do a maximum of 50 crossovers */ + cross = 60; + } + } else { + cross = table->numberXovers; /* use user specified value */ + } + if (cross >= popsize) { + cross = popsize; + } + + /* Perform the crossovers to get the best order. */ + for (m = 0; m < cross; m++) { + if (!PMX(table->size)) { /* perform one crossover */ + table->errorCode = CUDD_MEMORY_OUT; + FREE(storedd); + FREE(repeat); + st_free_table(computed); + return(0); + } + /* The offsprings are left in the last two entries of the + ** population table. These are now considered in turn. + */ + for (i = popsize; i <= popsize+1; i++) { + result = build_dd(table,i,lower,upper); /* build and sift child */ + if (!result) { + FREE(storedd); + FREE(repeat); + st_free_table(computed); + return(0); + } + large = largest(); /* find the largest DD in population */ + + /* If the new child is smaller than the largest DD in the current + ** population, enter it into the population in place of the + ** largest DD. + */ + if (STOREDD(i,numvars) < STOREDD(large,numvars)) { + /* Look up the largest DD in the computed table. + ** Decrease its repetition count. If the repetition count + ** goes to 0, remove the largest DD from the computed table. + */ + result = st_lookup_int(computed,(char *)&STOREDD(large,0), + &index); + if (!result) { + FREE(storedd); + FREE(repeat); + st_free_table(computed); + return(0); + } + repeat[index]--; + if (repeat[index] == 0) { + int *pointer = &STOREDD(index,0); + result = st_delete(computed, &pointer, NULL); + if (!result) { + FREE(storedd); + FREE(repeat); + st_free_table(computed); + return(0); + } + } + /* Copy the new individual to the entry of the + ** population table just made available and update the + ** computed table. + */ + for (n = 0; n <= numvars; n++) { + STOREDD(large,n) = STOREDD(i,n); + } + if (st_lookup_int(computed,(char *)&STOREDD(large,0), + &index)) { + repeat[index]++; + } else { + if (st_insert(computed,(char *)&STOREDD(large,0), + (char *)(long)large) == ST_OUT_OF_MEM) { + FREE(storedd); + FREE(repeat); + st_free_table(computed); + return(0); + } + repeat[large]++; + } + } + } + } + + /* Find the smallest DD in the population and build it; + ** that will be the result. + */ + small = find_best(); + + /* Print stats on the final population. */ +#ifdef DD_STATS + average_fitness = find_average_fitness(); + (void) fprintf(table->out,"\nFinal population: best fitness = %d, average fitness %8.3f",STOREDD(small,numvars),average_fitness); +#endif + + /* Clean up, build the result DD, and return. */ + st_free_table(computed); + computed = NULL; + result = build_dd(table,small,lower,upper); + FREE(storedd); + FREE(repeat); + return(result); + +} /* end of cuddGa */ + + +/*---------------------------------------------------------------------------*/ +/* Definition of static functions */ +/*---------------------------------------------------------------------------*/ + +/**Function******************************************************************** + + Synopsis [Generates the random sequences for the initial population.] + + Description [Generates the random sequences for the initial population. + The sequences are permutations of the indices between lower and + upper in the current order.] + + SideEffects [None] + + SeeAlso [] + +******************************************************************************/ +static int +make_random( + DdManager * table, + int lower) +{ + int i,j; /* loop variables */ + int *used; /* is a number already in a permutation */ + int next; /* next random number without repetitions */ + + used = ALLOC(int,numvars); + if (used == NULL) { + table->errorCode = CUDD_MEMORY_OUT; + return(0); + } +#if 0 + #ifdef DD_STATS + (void) fprintf(table->out,"Initial population before sifting\n"); + for (i = 0; i < 2; i++) { + for (j = 0; j < numvars; j++) { + (void) fprintf(table->out," %2d",STOREDD(i,j)); + } + (void) fprintf(table->out,"\n"); + } +#endif +#endif + for (i = 2; i < popsize; i++) { + for (j = 0; j < numvars; j++) { + used[j] = 0; + } + /* Generate a permutation of {0...numvars-1} and use it to + ** permute the variables in the layesr from lower to upper. + */ + for (j = 0; j < numvars; j++) { + do { + next = rand_int(numvars-1); + } while (used[next] != 0); + used[next] = 1; + STOREDD(i,j) = table->invperm[next+lower]; + } +#if 0 + #ifdef DD_STATS + /* Print the order just generated. */ + for (j = 0; j < numvars; j++) { + (void) fprintf(table->out," %2d",STOREDD(i,j)); + } + (void) fprintf(table->out,"\n"); +#endif +#endif + } + FREE(used); + return(1); + +} /* end of make_random */ + + +/**Function******************************************************************** + + Synopsis [Moves one variable up.] + + Description [Takes a variable from position x and sifts it up to + position x_low; x_low should be less than x. Returns 1 if successful; + 0 otherwise] + + SideEffects [None] + + SeeAlso [] + +******************************************************************************/ +static int +sift_up( + DdManager * table, + int x, + int x_low) +{ + int y; + int size; + + y = cuddNextLow(table,x); + while (y >= x_low) { + size = cuddSwapInPlace(table,y,x); + if (size == 0) { + return(0); + } + x = y; + y = cuddNextLow(table,x); + } + return(1); + +} /* end of sift_up */ + + +/**Function******************************************************************** + + Synopsis [Builds a DD from a given order.] + + Description [Builds a DD from a given order. This procedure also + sifts the final order and inserts into the array the size in nodes + of the result. Returns 1 if successful; 0 otherwise.] + + SideEffects [None] + + SeeAlso [] + +******************************************************************************/ +static int +build_dd( + DdManager * table, + int num /* the index of the individual to be built */, + int lower, + int upper) +{ + int i,j; /* loop vars */ + int position; + int index; + int limit; /* how large the DD for this order can grow */ + int size; + + /* Check the computed table. If the order already exists, it + ** suffices to copy the size from the existing entry. + */ + if (computed && st_lookup_int(computed,(char *)&STOREDD(num,0),&index)) { + STOREDD(num,numvars) = STOREDD(index,numvars); +#ifdef DD_STATS + (void) fprintf(table->out,"\nCache hit for index %d", index); +#endif + return(1); + } + + /* Stop if the DD grows 20 times larges than the reference size. */ + limit = 20 * STOREDD(0,numvars); + + /* Sift up the variables so as to build the desired permutation. + ** First the variable that has to be on top is sifted to the top. + ** Then the variable that has to occupy the secon position is sifted + ** up to the second position, and so on. + */ + for (j = 0; j < numvars; j++) { + i = STOREDD(num,j); + position = table->perm[i]; + result = sift_up(table,position,j+lower); + if (!result) return(0); + size = table->keys - table->isolated; + if (size > limit) break; + } + + /* Sift the DD just built. */ +#ifdef DD_STATS + (void) fprintf(table->out,"\n"); +#endif + result = cuddSifting(table,lower,upper); + if (!result) return(0); + + /* Copy order and size to table. */ + for (j = 0; j < numvars; j++) { + STOREDD(num,j) = table->invperm[lower+j]; + } + STOREDD(num,numvars) = table->keys - table->isolated; /* size of new DD */ + return(1); + +} /* end of build_dd */ + + +/**Function******************************************************************** + + Synopsis [Finds the largest DD in the population.] + + Description [Finds the largest DD in the population. If an order is + repeated, it avoids choosing the copy that is in the computed table + (it has repeat[i] > 1).] + + SideEffects [None] + + SeeAlso [] + +******************************************************************************/ +static int +largest(void) +{ + int i; /* loop var */ + int big; /* temporary holder to return result */ + + big = 0; + while (repeat[big] > 1) big++; + for (i = big + 1; i < popsize; i++) { + if (STOREDD(i,numvars) >= STOREDD(big,numvars) && repeat[i] <= 1) { + big = i; + } + } + return(big); + +} /* end of largest */ + + +/**Function******************************************************************** + + Synopsis [Generates a random number between 0 and the integer a.] + + Description [] + + SideEffects [None] + + SeeAlso [] + +******************************************************************************/ +static int +rand_int( + int a) +{ + return(Cudd_Random() % (a+1)); + +} /* end of rand_int */ + + +/**Function******************************************************************** + + Synopsis [Hash function for the computed table.] + + Description [Hash function for the computed table. Returns the bucket + number.] + + SideEffects [None] + + SeeAlso [] + +******************************************************************************/ +static int +array_hash( + char * array, + int modulus) +{ + int val = 0; + int i; + int *intarray; + + intarray = (int *) array; + + for (i = 0; i < numvars; i++) { + val = val * 997 + intarray[i]; + } + + return ((val < 0) ? -val : val) % modulus; + +} /* end of array_hash */ + + +/**Function******************************************************************** + + Synopsis [Comparison function for the computed table.] + + Description [Comparison function for the computed table. Returns 0 if + the two arrays are equal; 1 otherwise.] + + SideEffects [None] + + SeeAlso [] + +******************************************************************************/ +static int +array_compare( + const char * array1, + const char * array2) +{ + int i; + int *intarray1, *intarray2; + + intarray1 = (int *) array1; + intarray2 = (int *) array2; + + for (i = 0; i < numvars; i++) { + if (intarray1[i] != intarray2[i]) return(1); + } + return(0); + +} /* end of array_compare */ + + +/**Function******************************************************************** + + Synopsis [Returns the index of the fittest individual.] + + Description [] + + SideEffects [None] + + SeeAlso [] + +******************************************************************************/ +static int +find_best(void) +{ + int i,small; + + small = 0; + for (i = 1; i < popsize; i++) { + if (STOREDD(i,numvars) < STOREDD(small,numvars)) { + small = i; + } + } + return(small); + +} /* end of find_best */ + + +/**Function******************************************************************** + + Synopsis [Returns the average fitness of the population.] + + Description [] + + SideEffects [None] + + SeeAlso [] + +******************************************************************************/ +#ifdef DD_STATS +static double +find_average_fitness(void) +{ + int i; + int total_fitness = 0; + double average_fitness; + + for (i = 0; i < popsize; i++) { + total_fitness += STOREDD(i,numvars); + } + average_fitness = (double) total_fitness / (double) popsize; + return(average_fitness); + +} /* end of find_average_fitness */ +#endif + + +/**Function******************************************************************** + + Synopsis [Performs the crossover between two parents.] + + Description [Performs the crossover between two randomly chosen + parents, and creates two children, x1 and x2. Uses the Partially + Matched Crossover operator.] + + SideEffects [None] + + SeeAlso [] + +******************************************************************************/ +static int +PMX( + int maxvar) +{ + int cut1,cut2; /* the two cut positions (random) */ + int mom,dad; /* the two randomly chosen parents */ + int *inv1; /* inverse permutations for repair algo */ + int *inv2; + int i; /* loop vars */ + int u,v; /* aux vars */ + + inv1 = ALLOC(int,maxvar); + if (inv1 == NULL) { + return(0); + } + inv2 = ALLOC(int,maxvar); + if (inv2 == NULL) { + FREE(inv1); + return(0); + } + + /* Choose two orders from the population using roulette wheel. */ + if (!roulette(&mom,&dad)) { + FREE(inv1); + FREE(inv2); + return(0); + } + + /* Choose two random cut positions. A cut in position i means that + ** the cut immediately precedes position i. If cut1 < cut2, we + ** exchange the middle of the two orderings; otherwise, we + ** exchange the beginnings and the ends. + */ + cut1 = rand_int(numvars-1); + do { + cut2 = rand_int(numvars-1); + } while (cut1 == cut2); + +#if 0 + /* Print out the parents. */ + (void) fprintf(table->out, + "Crossover of %d (mom) and %d (dad) between %d and %d\n", + mom,dad,cut1,cut2); + for (i = 0; i < numvars; i++) { + if (i == cut1 || i == cut2) (void) fprintf(table->out,"|"); + (void) fprintf(table->out,"%2d ",STOREDD(mom,i)); + } + (void) fprintf(table->out,"\n"); + for (i = 0; i < numvars; i++) { + if (i == cut1 || i == cut2) (void) fprintf(table->out,"|"); + (void) fprintf(table->out,"%2d ",STOREDD(dad,i)); + } + (void) fprintf(table->out,"\n"); +#endif + + /* Initialize the inverse permutations: -1 means yet undetermined. */ + for (i = 0; i < maxvar; i++) { + inv1[i] = -1; + inv2[i] = -1; + } + + /* Copy the portions whithin the cuts. */ + for (i = cut1; i != cut2; i = (i == numvars-1) ? 0 : i+1) { + STOREDD(popsize,i) = STOREDD(dad,i); + inv1[STOREDD(popsize,i)] = i; + STOREDD(popsize+1,i) = STOREDD(mom,i); + inv2[STOREDD(popsize+1,i)] = i; + } + + /* Now apply the repair algorithm outside the cuts. */ + for (i = cut2; i != cut1; i = (i == numvars-1 ) ? 0 : i+1) { + v = i; + do { + u = STOREDD(mom,v); + v = inv1[u]; + } while (v != -1); + STOREDD(popsize,i) = u; + inv1[u] = i; + v = i; + do { + u = STOREDD(dad,v); + v = inv2[u]; + } while (v != -1); + STOREDD(popsize+1,i) = u; + inv2[u] = i; + } + +#if 0 + /* Print the results of crossover. */ + for (i = 0; i < numvars; i++) { + if (i == cut1 || i == cut2) (void) fprintf(table->out,"|"); + (void) fprintf(table->out,"%2d ",STOREDD(popsize,i)); + } + (void) fprintf(table->out,"\n"); + for (i = 0; i < numvars; i++) { + if (i == cut1 || i == cut2) (void) fprintf(table->out,"|"); + (void) fprintf(table->out,"%2d ",STOREDD(popsize+1,i)); + } + (void) fprintf(table->out,"\n"); +#endif + + FREE(inv1); + FREE(inv2); + return(1); + +} /* end of PMX */ + + +/**Function******************************************************************** + + Synopsis [Selects two parents with the roulette wheel method.] + + Description [Selects two distinct parents with the roulette wheel method.] + + SideEffects [The indices of the selected parents are returned as side + effects.] + + SeeAlso [] + +******************************************************************************/ +static int +roulette( + int * p1, + int * p2) +{ + double *wheel; + double spin; + int i; + + wheel = ALLOC(double,popsize); + if (wheel == NULL) { + return(0); + } + + /* The fitness of an individual is the reciprocal of its size. */ + wheel[0] = 1.0 / (double) STOREDD(0,numvars); + + for (i = 1; i < popsize; i++) { + wheel[i] = wheel[i-1] + 1.0 / (double) STOREDD(i,numvars); + } + + /* Get a random number between 0 and wheel[popsize-1] (that is, + ** the sum of all fitness values. 2147483561 is the largest number + ** returned by Cudd_Random. + */ + spin = wheel[numvars-1] * (double) Cudd_Random() / 2147483561.0; + + /* Find the lucky element by scanning the wheel. */ + for (i = 0; i < popsize; i++) { + if (spin <= wheel[i]) break; + } + *p1 = i; + + /* Repeat the process for the second parent, making sure it is + ** distinct from the first. + */ + do { + spin = wheel[popsize-1] * (double) Cudd_Random() / 2147483561.0; + for (i = 0; i < popsize; i++) { + if (spin <= wheel[i]) break; + } + } while (i == *p1); + *p2 = i; + + FREE(wheel); + return(1); + +} /* end of roulette */ + +/**CFile*********************************************************************** + + FileName [cuddGroup.c] + + PackageName [cudd] + + Synopsis [Functions for group sifting.] + + Description [External procedures included in this file: +
        +
      • Cudd_MakeTreeNode() +
      + Internal procedures included in this file: +
        +
      • cuddTreeSifting() +
      + Static procedures included in this module: +
        +
      • ddTreeSiftingAux() +
      • ddCountInternalMtrNodes() +
      • ddReorderChildren() +
      • ddFindNodeHiLo() +
      • ddUniqueCompareGroup() +
      • ddGroupSifting() +
      • ddCreateGroup() +
      • ddGroupSiftingAux() +
      • ddGroupSiftingUp() +
      • ddGroupSiftingDown() +
      • ddGroupMove() +
      • ddGroupMoveBackward() +
      • ddGroupSiftingBackward() +
      • ddMergeGroups() +
      • ddDissolveGroup() +
      • ddNoCheck() +
      • ddSecDiffCheck() +
      • ddExtSymmCheck() +
      • ddVarGroupCheck() +
      • ddSetVarHandled() +
      • ddResetVarHandled() +
      • ddIsVarHandled() +
      • ddFixTree() +
      ] + + Author [Shipra Panda, Fabio Somenzi] + + Copyright [Copyright (c) 1995-2012, Regents of the University of Colorado + + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + Neither the name of the University of Colorado nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE.] + +******************************************************************************/ + + +/*---------------------------------------------------------------------------*/ +/* Constant declarations */ +/*---------------------------------------------------------------------------*/ + +/* Constants for lazy sifting */ +#define DD_NORMAL_SIFT 0 +#define DD_LAZY_SIFT 1 + +/* Constants for sifting up and down */ +#define DD_SIFT_DOWN 0 +#define DD_SIFT_UP 1 + +/*---------------------------------------------------------------------------*/ +/* Stucture declarations */ +/*---------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------*/ +/* Type declarations */ +/*---------------------------------------------------------------------------*/ + +#ifdef __cplusplus +extern "C" { +#endif +typedef int (*DD_CHKFP)(DdManager *, int, int); +#ifdef __cplusplus +} +#endif + +/*---------------------------------------------------------------------------*/ +/* Variable declarations */ +/*---------------------------------------------------------------------------*/ + +//#ifndef lint +//static char rcsid[] DD_UNUSED = "$Id: cuddGroup.c,v 1.49 2012/02/05 01:07:18 fabio Exp $"; +//#endif + +static int *entry; +extern int ddTotalNumberSwapping; +#ifdef DD_STATS +extern int ddTotalNISwaps; +static int extsymmcalls; +static int extsymm; +static int secdiffcalls; +static int secdiff; +static int secdiffmisfire; +#endif +#ifdef DD_DEBUG +static int pr = 0; /* flag to enable printing while debugging */ + /* by depositing a 1 into it */ +#endif +static unsigned int originalSize; + +/*---------------------------------------------------------------------------*/ +/* Macro declarations */ +/*---------------------------------------------------------------------------*/ + +#ifdef __cplusplus +extern "C" { +#endif + +/**AutomaticStart*************************************************************/ + +/*---------------------------------------------------------------------------*/ +/* Static function prototypes */ +/*---------------------------------------------------------------------------*/ + +static int ddTreeSiftingAux (DdManager *table, MtrNode *treenode, Cudd_ReorderingType method); +#ifdef DD_STATS +static int ddCountInternalMtrNodes (DdManager *table, MtrNode *treenode); +#endif +static int ddReorderChildren (DdManager *table, MtrNode *treenode, Cudd_ReorderingType method); +static void ddFindNodeHiLo (DdManager *table, MtrNode *treenode, int *lower, int *upper); +static int ddUniqueCompareGroup (int *ptrX, int *ptrY); +static int ddGroupSifting (DdManager *table, int lower, int upper, DD_CHKFP checkFunction, int lazyFlag); +static void ddCreateGroup (DdManager *table, int x, int y); +static int ddGroupSiftingAux (DdManager *table, int x, int xLow, int xHigh, DD_CHKFP checkFunction, int lazyFlag); +static int ddGroupSiftingUp (DdManager *table, int y, int xLow, DD_CHKFP checkFunction, Move **moves); +static int ddGroupSiftingDown (DdManager *table, int x, int xHigh, DD_CHKFP checkFunction, Move **moves); +static int ddGroupMove (DdManager *table, int x, int y, Move **moves); +static int ddGroupMoveBackward (DdManager *table, int x, int y); +static int ddGroupSiftingBackward (DdManager *table, Move *moves, int size, int upFlag, int lazyFlag); +static void ddMergeGroups (DdManager *table, MtrNode *treenode, int low, int high); +static void ddDissolveGroup (DdManager *table, int x, int y); +static int ddNoCheck (DdManager *table, int x, int y); +static int ddSecDiffCheck (DdManager *table, int x, int y); +static int ddExtSymmCheck (DdManager *table, int x, int y); +static int ddVarGroupCheck (DdManager * table, int x, int y); +static int ddSetVarHandled (DdManager *dd, int index); +static int ddResetVarHandled (DdManager *dd, int index); +static int ddIsVarHandled (DdManager *dd, int index); + +/**AutomaticEnd***************************************************************/ + +#ifdef __cplusplus +} +#endif + +/*---------------------------------------------------------------------------*/ +/* Definition of exported functions */ +/*---------------------------------------------------------------------------*/ + + + +/*---------------------------------------------------------------------------*/ +/* Definition of internal functions */ +/*---------------------------------------------------------------------------*/ + + +/**Function******************************************************************** + + Synopsis [Tree sifting algorithm.] + + Description [Tree sifting algorithm. Assumes that a tree representing + a group hierarchy is passed as a parameter. It then reorders each + group in postorder fashion by calling ddTreeSiftingAux. Assumes that + no dead nodes are present. Returns 1 if successful; 0 otherwise.] + + SideEffects [None] + +******************************************************************************/ +int +cuddTreeSifting( + DdManager * table /* DD table */, + Cudd_ReorderingType method /* reordering method for the groups of leaves */) +{ + int i; + int nvars; + int result; + int tempTree; + + /* If no tree is provided we create a temporary one in which all + ** variables are in a single group. After reordering this tree is + ** destroyed. + */ + tempTree = table->tree == NULL; + if (tempTree) { + table->tree = Mtr_InitGroupTree(0,table->size); + table->tree->index = table->invperm[0]; + } + nvars = table->size; + +#ifdef DD_DEBUG + if (pr > 0 && !tempTree) (void) fprintf(table->out,"cuddTreeSifting:"); + Mtr_PrintGroups(table->tree,pr <= 0); +#endif + +#ifdef DD_STATS + extsymmcalls = 0; + extsymm = 0; + secdiffcalls = 0; + secdiff = 0; + secdiffmisfire = 0; + + (void) fprintf(table->out,"\n"); + if (!tempTree) + (void) fprintf(table->out,"#:IM_NODES %8d: group tree nodes\n", + ddCountInternalMtrNodes(table,table->tree)); +#endif + + /* Initialize the group of each subtable to itself. Initially + ** there are no groups. Groups are created according to the tree + ** structure in postorder fashion. + */ + for (i = 0; i < nvars; i++) + table->subtables[i].next = i; + + + /* Reorder. */ + result = ddTreeSiftingAux(table, table->tree, method); + +#ifdef DD_STATS /* print stats */ + if (!tempTree && method == CUDD_REORDER_GROUP_SIFT && + (table->groupcheck == CUDD_GROUP_CHECK7 || + table->groupcheck == CUDD_GROUP_CHECK5)) { + (void) fprintf(table->out,"\nextsymmcalls = %d\n",extsymmcalls); + (void) fprintf(table->out,"extsymm = %d",extsymm); + } + if (!tempTree && method == CUDD_REORDER_GROUP_SIFT && + table->groupcheck == CUDD_GROUP_CHECK7) { + (void) fprintf(table->out,"\nsecdiffcalls = %d\n",secdiffcalls); + (void) fprintf(table->out,"secdiff = %d\n",secdiff); + (void) fprintf(table->out,"secdiffmisfire = %d",secdiffmisfire); + } +#endif + + if (tempTree) + Cudd_FreeTree(table); + else + Mtr_ReorderGroups(table->tree, table->perm); + + return(result); + +} /* end of cuddTreeSifting */ + + +/*---------------------------------------------------------------------------*/ +/* Definition of static functions */ +/*---------------------------------------------------------------------------*/ + + +/**Function******************************************************************** + + Synopsis [Visits the group tree and reorders each group.] + + Description [Recursively visits the group tree and reorders each + group in postorder fashion. Returns 1 if successful; 0 otherwise.] + + SideEffects [None] + +******************************************************************************/ +static int +ddTreeSiftingAux( + DdManager * table, + MtrNode * treenode, + Cudd_ReorderingType method) +{ + MtrNode *auxnode; + int res; + Cudd_AggregationType saveCheck; + +#ifdef DD_DEBUG + Mtr_PrintGroups(treenode,1); +#endif + + auxnode = treenode; + while (auxnode != NULL) { + if (auxnode->child != NULL) { + if (!ddTreeSiftingAux(table, auxnode->child, method)) + return(0); + saveCheck = table->groupcheck; + table->groupcheck = CUDD_NO_CHECK; + if (method != CUDD_REORDER_LAZY_SIFT) + res = ddReorderChildren(table, auxnode, CUDD_REORDER_GROUP_SIFT); + else + res = ddReorderChildren(table, auxnode, CUDD_REORDER_LAZY_SIFT); + table->groupcheck = saveCheck; + + if (res == 0) + return(0); + } else if (auxnode->size > 1) { + if (!ddReorderChildren(table, auxnode, method)) + return(0); + } + auxnode = auxnode->younger; + } + + return(1); + +} /* end of ddTreeSiftingAux */ + + +#ifdef DD_STATS +/**Function******************************************************************** + + Synopsis [Counts the number of internal nodes of the group tree.] + + Description [Counts the number of internal nodes of the group tree. + Returns the count.] + + SideEffects [None] + +******************************************************************************/ +static int +ddCountInternalMtrNodes( + DdManager * table, + MtrNode * treenode) +{ + MtrNode *auxnode; + int count,nodeCount; + + + nodeCount = 0; + auxnode = treenode; + while (auxnode != NULL) { + if (!(MTR_TEST(auxnode,MTR_TERMINAL))) { + nodeCount++; + count = ddCountInternalMtrNodes(table,auxnode->child); + nodeCount += count; + } + auxnode = auxnode->younger; + } + + return(nodeCount); + +} /* end of ddCountInternalMtrNodes */ +#endif + + +/**Function******************************************************************** + + Synopsis [Reorders the children of a group tree node according to + the options.] + + Description [Reorders the children of a group tree node according to + the options. After reordering puts all the variables in the group + and/or its descendents in a single group. This allows hierarchical + reordering. If the variables in the group do not exist yet, simply + does nothing. Returns 1 if successful; 0 otherwise.] + + SideEffects [None] + +******************************************************************************/ +static int +ddReorderChildren( + DdManager * table, + MtrNode * treenode, + Cudd_ReorderingType method) +{ + int lower; + int upper; + int result; + unsigned int initialSize; + + ddFindNodeHiLo(table,treenode,&lower,&upper); + /* If upper == -1 these variables do not exist yet. */ + if (upper == -1) + return(1); + + if (treenode->flags == MTR_FIXED) { + result = 1; + } else { +#ifdef DD_STATS + (void) fprintf(table->out," "); +#endif + switch (method) { + case CUDD_REORDER_RANDOM: + case CUDD_REORDER_RANDOM_PIVOT: + result = cuddSwapping(table,lower,upper,method); + break; + case CUDD_REORDER_SIFT: + result = cuddSifting(table,lower,upper); + break; + case CUDD_REORDER_SIFT_CONVERGE: + do { + initialSize = table->keys - table->isolated; + result = cuddSifting(table,lower,upper); + if (initialSize <= table->keys - table->isolated) + break; +#ifdef DD_STATS + else + (void) fprintf(table->out,"\n"); +#endif + } while (result != 0); + break; + case CUDD_REORDER_SYMM_SIFT: + result = cuddSymmSifting(table,lower,upper); + break; + case CUDD_REORDER_SYMM_SIFT_CONV: + result = cuddSymmSiftingConv(table,lower,upper); + break; + case CUDD_REORDER_GROUP_SIFT: + if (table->groupcheck == CUDD_NO_CHECK) { + result = ddGroupSifting(table,lower,upper,ddNoCheck, + DD_NORMAL_SIFT); + } else if (table->groupcheck == CUDD_GROUP_CHECK5) { + result = ddGroupSifting(table,lower,upper,ddExtSymmCheck, + DD_NORMAL_SIFT); + } else if (table->groupcheck == CUDD_GROUP_CHECK7) { + result = ddGroupSifting(table,lower,upper,ddExtSymmCheck, + DD_NORMAL_SIFT); + } else { + (void) fprintf(table->err, + "Unknown group ckecking method\n"); + result = 0; + } + break; + case CUDD_REORDER_GROUP_SIFT_CONV: + do { + initialSize = table->keys - table->isolated; + if (table->groupcheck == CUDD_NO_CHECK) { + result = ddGroupSifting(table,lower,upper,ddNoCheck, + DD_NORMAL_SIFT); + } else if (table->groupcheck == CUDD_GROUP_CHECK5) { + result = ddGroupSifting(table,lower,upper,ddExtSymmCheck, + DD_NORMAL_SIFT); + } else if (table->groupcheck == CUDD_GROUP_CHECK7) { + result = ddGroupSifting(table,lower,upper,ddExtSymmCheck, + DD_NORMAL_SIFT); + } else { + (void) fprintf(table->err, + "Unknown group ckecking method\n"); + result = 0; + } +#ifdef DD_STATS + (void) fprintf(table->out,"\n"); +#endif + result = cuddWindowReorder(table,lower,upper, + CUDD_REORDER_WINDOW4); + if (initialSize <= table->keys - table->isolated) + break; +#ifdef DD_STATS + else + (void) fprintf(table->out,"\n"); +#endif + } while (result != 0); + break; + case CUDD_REORDER_WINDOW2: + case CUDD_REORDER_WINDOW3: + case CUDD_REORDER_WINDOW4: + case CUDD_REORDER_WINDOW2_CONV: + case CUDD_REORDER_WINDOW3_CONV: + case CUDD_REORDER_WINDOW4_CONV: + result = cuddWindowReorder(table,lower,upper,method); + break; + case CUDD_REORDER_ANNEALING: + result = cuddAnnealing(table,lower,upper); + break; + case CUDD_REORDER_GENETIC: + result = cuddGa(table,lower,upper); + break; + case CUDD_REORDER_LINEAR: + result = cuddLinearAndSifting(table,lower,upper); + break; + case CUDD_REORDER_LINEAR_CONVERGE: + do { + initialSize = table->keys - table->isolated; + result = cuddLinearAndSifting(table,lower,upper); + if (initialSize <= table->keys - table->isolated) + break; +#ifdef DD_STATS + else + (void) fprintf(table->out,"\n"); +#endif + } while (result != 0); + break; + case CUDD_REORDER_EXACT: + result = cuddExact(table,lower,upper); + break; + case CUDD_REORDER_LAZY_SIFT: + result = ddGroupSifting(table,lower,upper,ddVarGroupCheck, + DD_LAZY_SIFT); + break; + default: + return(0); + } + } + + /* Create a single group for all the variables that were sifted, + ** so that they will be treated as a single block by successive + ** invocations of ddGroupSifting. + */ + ddMergeGroups(table,treenode,lower,upper); + +#ifdef DD_DEBUG + if (pr > 0) (void) fprintf(table->out,"ddReorderChildren:"); +#endif + + return(result); + +} /* end of ddReorderChildren */ + + +/**Function******************************************************************** + + Synopsis [Finds the lower and upper bounds of the group represented + by treenode.] + + Description [Finds the lower and upper bounds of the group + represented by treenode. From the index and size fields we need to + derive the current positions, and find maximum and minimum.] + + SideEffects [The bounds are returned as side effects.] + + SeeAlso [] + +******************************************************************************/ +static void +ddFindNodeHiLo( + DdManager * table, + MtrNode * treenode, + int * lower, + int * upper) +{ + int low; + int high; + + /* Check whether no variables in this group already exist. + ** If so, return immediately. The calling procedure will know from + ** the values of upper that no reordering is needed. + */ + if ((int) treenode->low >= table->size) { + *lower = table->size; + *upper = -1; + return; + } + + *lower = low = (unsigned int) table->perm[treenode->index]; + high = (int) (low + treenode->size - 1); + + if (high >= table->size) { + /* This is the case of a partially existing group. The aim is to + ** reorder as many variables as safely possible. If the tree + ** node is terminal, we just reorder the subset of the group + ** that is currently in existence. If the group has + ** subgroups, then we only reorder those subgroups that are + ** fully instantiated. This way we avoid breaking up a group. + */ + MtrNode *auxnode = treenode->child; + if (auxnode == NULL) { + *upper = (unsigned int) table->size - 1; + } else { + /* Search the subgroup that strands the table->size line. + ** If the first group starts at 0 and goes past table->size + ** upper will get -1, thus correctly signaling that no reordering + ** should take place. + */ + while (auxnode != NULL) { + int thisLower = table->perm[auxnode->low]; + int thisUpper = thisLower + auxnode->size - 1; + if (thisUpper >= table->size && thisLower < table->size) + *upper = (unsigned int) thisLower - 1; + auxnode = auxnode->younger; + } + } + } else { + /* Normal case: All the variables of the group exist. */ + *upper = (unsigned int) high; + } + +#ifdef DD_DEBUG + /* Make sure that all variables in group are contiguous. */ + assert(treenode->size >= *upper - *lower + 1); +#endif + + return; + +} /* end of ddFindNodeHiLo */ + + +/**Function******************************************************************** + + Synopsis [Comparison function used by qsort.] + + Description [Comparison function used by qsort to order the variables + according to the number of keys in the subtables. Returns the + difference in number of keys between the two variables being + compared.] + + SideEffects [None] + +******************************************************************************/ +static int +ddUniqueCompareGroup( + int * ptrX, + int * ptrY) +{ +#if 0 + if (entry[*ptrY] == entry[*ptrX]) { + return((*ptrX) - (*ptrY)); + } +#endif + return(entry[*ptrY] - entry[*ptrX]); + +} /* end of ddUniqueCompareGroup */ + + +/**Function******************************************************************** + + Synopsis [Sifts from treenode->low to treenode->high.] + + Description [Sifts from treenode->low to treenode->high. If + croupcheck == CUDD_GROUP_CHECK7, it checks for group creation at the + end of the initial sifting. If a group is created, it is then sifted + again. After sifting one variable, the group that contains it is + dissolved. Returns 1 in case of success; 0 otherwise.] + + SideEffects [None] + +******************************************************************************/ +static int +ddGroupSifting( + DdManager * table, + int lower, + int upper, + DD_CHKFP checkFunction, + int lazyFlag) +{ + int *var; + int i,j,x,xInit; + int nvars; + int classes; + int result; + int *sifted; + int merged; + int dissolve; +#ifdef DD_STATS + unsigned previousSize; +#endif + int xindex; + + nvars = table->size; + + /* Order variables to sift. */ + entry = NULL; + sifted = NULL; + var = ALLOC(int,nvars); + if (var == NULL) { + table->errorCode = CUDD_MEMORY_OUT; + goto ddGroupSiftingOutOfMem; + } + entry = ALLOC(int,nvars); + if (entry == NULL) { + table->errorCode = CUDD_MEMORY_OUT; + goto ddGroupSiftingOutOfMem; + } + sifted = ALLOC(int,nvars); + if (sifted == NULL) { + table->errorCode = CUDD_MEMORY_OUT; + goto ddGroupSiftingOutOfMem; + } + + /* Here we consider only one representative for each group. */ + for (i = 0, classes = 0; i < nvars; i++) { + sifted[i] = 0; + x = table->perm[i]; + if ((unsigned) x >= table->subtables[x].next) { + entry[i] = table->subtables[x].keys; + var[classes] = i; + classes++; + } + } + + qsort((void *)var,classes,sizeof(int), + (DD_QSFP) ddUniqueCompareGroup); + + if (lazyFlag) { + for (i = 0; i < nvars; i ++) { + ddResetVarHandled(table, i); + } + } + + /* Now sift. */ + for (i = 0; i < ddMin(table->siftMaxVar,classes); i++) { + if (ddTotalNumberSwapping >= table->siftMaxSwap) + break; + if (util_cpu_time() - table->startTime + table->reordTime + > table->timeLimit) { + table->autoDyn = 0; /* prevent further reordering */ + break; + } + xindex = var[i]; + if (sifted[xindex] == 1) /* variable already sifted as part of group */ + continue; + x = table->perm[xindex]; /* find current level of this variable */ + + if (x < lower || x > upper || table->subtables[x].bindVar == 1) + continue; +#ifdef DD_STATS + previousSize = table->keys - table->isolated; +#endif +#ifdef DD_DEBUG + /* x is bottom of group */ + assert((unsigned) x >= table->subtables[x].next); +#endif + if ((unsigned) x == table->subtables[x].next) { + dissolve = 1; + result = ddGroupSiftingAux(table,x,lower,upper,checkFunction, + lazyFlag); + } else { + dissolve = 0; + result = ddGroupSiftingAux(table,x,lower,upper,ddNoCheck,lazyFlag); + } + if (!result) goto ddGroupSiftingOutOfMem; + + /* check for aggregation */ + merged = 0; + if (lazyFlag == 0 && table->groupcheck == CUDD_GROUP_CHECK7) { + x = table->perm[xindex]; /* find current level */ + if ((unsigned) x == table->subtables[x].next) { /* not part of a group */ + if (x != upper && sifted[table->invperm[x+1]] == 0 && + (unsigned) x+1 == table->subtables[x+1].next) { + if (ddSecDiffCheck(table,x,x+1)) { + merged =1; + ddCreateGroup(table,x,x+1); + } + } + if (x != lower && sifted[table->invperm[x-1]] == 0 && + (unsigned) x-1 == table->subtables[x-1].next) { + if (ddSecDiffCheck(table,x-1,x)) { + merged =1; + ddCreateGroup(table,x-1,x); + } + } + } + } + + if (merged) { /* a group was created */ + /* move x to bottom of group */ + while ((unsigned) x < table->subtables[x].next) + x = table->subtables[x].next; + /* sift */ + result = ddGroupSiftingAux(table,x,lower,upper,ddNoCheck,lazyFlag); + if (!result) goto ddGroupSiftingOutOfMem; +#ifdef DD_STATS + if (table->keys < previousSize + table->isolated) { + (void) fprintf(table->out,"_"); + } else if (table->keys > previousSize + table->isolated) { + (void) fprintf(table->out,"^"); + } else { + (void) fprintf(table->out,"*"); + } + fflush(table->out); + } else { + if (table->keys < previousSize + table->isolated) { + (void) fprintf(table->out,"-"); + } else if (table->keys > previousSize + table->isolated) { + (void) fprintf(table->out,"+"); + } else { + (void) fprintf(table->out,"="); + } + fflush(table->out); +#endif + } + + /* Mark variables in the group just sifted. */ + x = table->perm[xindex]; + if ((unsigned) x != table->subtables[x].next) { + xInit = x; + do { + j = table->invperm[x]; + sifted[j] = 1; + x = table->subtables[x].next; + } while (x != xInit); + + /* Dissolve the group if it was created. */ + if (lazyFlag == 0 && dissolve) { + do { + j = table->subtables[x].next; + table->subtables[x].next = x; + x = j; + } while (x != xInit); + } + } + +#ifdef DD_DEBUG + if (pr > 0) (void) fprintf(table->out,"ddGroupSifting:"); +#endif + + if (lazyFlag) ddSetVarHandled(table, xindex); + } /* for */ + + FREE(sifted); + FREE(var); + FREE(entry); + + return(1); + + ddGroupSiftingOutOfMem: + if (entry != NULL) FREE(entry); + if (var != NULL) FREE(var); + if (sifted != NULL) FREE(sifted); + + return(0); + +} /* end of ddGroupSifting */ + + +/**Function******************************************************************** + + Synopsis [Creates a group encompassing variables from x to y in the + DD table.] + + Description [Creates a group encompassing variables from x to y in the + DD table. In the current implementation it must be y == x+1. + Returns 1 in case of success; 0 otherwise.] + + SideEffects [None] + +******************************************************************************/ +static void +ddCreateGroup( + DdManager * table, + int x, + int y) +{ + int gybot; + +#ifdef DD_DEBUG + assert(y == x+1); +#endif + + /* Find bottom of second group. */ + gybot = y; + while ((unsigned) gybot < table->subtables[gybot].next) + gybot = table->subtables[gybot].next; + + /* Link groups. */ + table->subtables[x].next = y; + table->subtables[gybot].next = x; + + return; + +} /* ddCreateGroup */ + + +/**Function******************************************************************** + + Synopsis [Sifts one variable up and down until it has taken all + positions. Checks for aggregation.] + + Description [Sifts one variable up and down until it has taken all + positions. Checks for aggregation. There may be at most two sweeps, + even if the group grows. Assumes that x is either an isolated + variable, or it is the bottom of a group. All groups may not have + been found. The variable being moved is returned to the best position + seen during sifting. Returns 1 in case of success; 0 otherwise.] + + SideEffects [None] + +******************************************************************************/ +static int +ddGroupSiftingAux( + DdManager * table, + int x, + int xLow, + int xHigh, + DD_CHKFP checkFunction, + int lazyFlag) +{ + Move *move; + Move *moves; /* list of moves */ + int initialSize; + int result; + int y; + int topbot; + +#ifdef DD_DEBUG + if (pr > 0) (void) fprintf(table->out, + "ddGroupSiftingAux from %d to %d\n",xLow,xHigh); + assert((unsigned) x >= table->subtables[x].next); /* x is bottom of group */ +#endif + + initialSize = table->keys - table->isolated; + moves = NULL; + + originalSize = initialSize; /* for lazy sifting */ + + /* If we have a singleton, we check for aggregation in both + ** directions before we sift. + */ + if ((unsigned) x == table->subtables[x].next) { + /* Will go down first, unless x == xHigh: + ** Look for aggregation above x. + */ + for (y = x; y > xLow; y--) { + if (!checkFunction(table,y-1,y)) + break; + topbot = table->subtables[y-1].next; /* find top of y-1's group */ + table->subtables[y-1].next = y; + table->subtables[x].next = topbot; /* x is bottom of group so its */ + /* next is top of y-1's group */ + y = topbot + 1; /* add 1 for y--; new y is top of group */ + } + /* Will go up first unless x == xlow: + ** Look for aggregation below x. + */ + for (y = x; y < xHigh; y++) { + if (!checkFunction(table,y,y+1)) + break; + /* find bottom of y+1's group */ + topbot = y + 1; + while ((unsigned) topbot < table->subtables[topbot].next) { + topbot = table->subtables[topbot].next; + } + table->subtables[topbot].next = table->subtables[y].next; + table->subtables[y].next = y + 1; + y = topbot - 1; /* subtract 1 for y++; new y is bottom of group */ + } + } + + /* Now x may be in the middle of a group. + ** Find bottom of x's group. + */ + while ((unsigned) x < table->subtables[x].next) + x = table->subtables[x].next; + + if (x == xLow) { /* Sift down */ +#ifdef DD_DEBUG + /* x must be a singleton */ + assert((unsigned) x == table->subtables[x].next); +#endif + if (x == xHigh) return(1); /* just one variable */ + + if (!ddGroupSiftingDown(table,x,xHigh,checkFunction,&moves)) + goto ddGroupSiftingAuxOutOfMem; + /* at this point x == xHigh, unless early term */ + + /* move backward and stop at best position */ + result = ddGroupSiftingBackward(table,moves,initialSize, + DD_SIFT_DOWN,lazyFlag); +#ifdef DD_DEBUG + assert(table->keys - table->isolated <= (unsigned) initialSize); +#endif + if (!result) goto ddGroupSiftingAuxOutOfMem; + + } else if (cuddNextHigh(table,x) > xHigh) { /* Sift up */ +#ifdef DD_DEBUG + /* x is bottom of group */ + assert((unsigned) x >= table->subtables[x].next); +#endif + /* Find top of x's group */ + x = table->subtables[x].next; + + if (!ddGroupSiftingUp(table,x,xLow,checkFunction,&moves)) + goto ddGroupSiftingAuxOutOfMem; + /* at this point x == xLow, unless early term */ + + /* move backward and stop at best position */ + result = ddGroupSiftingBackward(table,moves,initialSize, + DD_SIFT_UP,lazyFlag); +#ifdef DD_DEBUG + assert(table->keys - table->isolated <= (unsigned) initialSize); +#endif + if (!result) goto ddGroupSiftingAuxOutOfMem; + + } else if (x - xLow > xHigh - x) { /* must go down first: shorter */ + if (!ddGroupSiftingDown(table,x,xHigh,checkFunction,&moves)) + goto ddGroupSiftingAuxOutOfMem; + /* at this point x == xHigh, unless early term */ + + /* Find top of group */ + if (moves) { + x = moves->y; + } + while ((unsigned) x < table->subtables[x].next) + x = table->subtables[x].next; + x = table->subtables[x].next; +#ifdef DD_DEBUG + /* x should be the top of a group */ + assert((unsigned) x <= table->subtables[x].next); +#endif + + if (!ddGroupSiftingUp(table,x,xLow,checkFunction,&moves)) + goto ddGroupSiftingAuxOutOfMem; + + /* move backward and stop at best position */ + result = ddGroupSiftingBackward(table,moves,initialSize, + DD_SIFT_UP,lazyFlag); +#ifdef DD_DEBUG + assert(table->keys - table->isolated <= (unsigned) initialSize); +#endif + if (!result) goto ddGroupSiftingAuxOutOfMem; + + } else { /* moving up first: shorter */ + /* Find top of x's group */ + x = table->subtables[x].next; + + if (!ddGroupSiftingUp(table,x,xLow,checkFunction,&moves)) + goto ddGroupSiftingAuxOutOfMem; + /* at this point x == xHigh, unless early term */ + + if (moves) { + x = moves->x; + } + while ((unsigned) x < table->subtables[x].next) + x = table->subtables[x].next; +#ifdef DD_DEBUG + /* x is bottom of a group */ + assert((unsigned) x >= table->subtables[x].next); +#endif + + if (!ddGroupSiftingDown(table,x,xHigh,checkFunction,&moves)) + goto ddGroupSiftingAuxOutOfMem; + + /* move backward and stop at best position */ + result = ddGroupSiftingBackward(table,moves,initialSize, + DD_SIFT_DOWN,lazyFlag); +#ifdef DD_DEBUG + assert(table->keys - table->isolated <= (unsigned) initialSize); +#endif + if (!result) goto ddGroupSiftingAuxOutOfMem; + } + + while (moves != NULL) { + move = moves->next; + cuddDeallocMove(table, moves); + moves = move; + } + + return(1); + + ddGroupSiftingAuxOutOfMem: + while (moves != NULL) { + move = moves->next; + cuddDeallocMove(table, moves); + moves = move; + } + + return(0); + +} /* end of ddGroupSiftingAux */ + + +/**Function******************************************************************** + + Synopsis [Sifts up a variable until either it reaches position xLow + or the size of the DD heap increases too much.] + + Description [Sifts up a variable until either it reaches position + xLow or the size of the DD heap increases too much. Assumes that y is + the top of a group (or a singleton). Checks y for aggregation to the + adjacent variables. Records all the moves that are appended to the + list of moves received as input and returned as a side effect. + Returns 1 in case of success; 0 otherwise.] + + SideEffects [None] + +******************************************************************************/ +static int +ddGroupSiftingUp( + DdManager * table, + int y, + int xLow, + DD_CHKFP checkFunction, + Move ** moves) +{ + Move *move; + int x; + int size; + int i; + int gxtop,gybot; + int limitSize; + int xindex, yindex; + int zindex; + int z; + int isolated; + int L; /* lower bound on DD size */ +#ifdef DD_DEBUG + int checkL; +#endif + + yindex = table->invperm[y]; + + /* Initialize the lower bound. + ** The part of the DD below the bottom of y's group will not change. + ** The part of the DD above y that does not interact with any + ** variable of y's group will not change. + ** The rest may vanish in the best case, except for + ** the nodes at level xLow, which will not vanish, regardless. + ** What we use here is not really a lower bound, because we ignore + ** the interactions with all variables except y. + */ + limitSize = L = table->keys - table->isolated; + gybot = y; + while ((unsigned) gybot < table->subtables[gybot].next) + gybot = table->subtables[gybot].next; + for (z = xLow + 1; z <= gybot; z++) { + zindex = table->invperm[z]; + if (zindex == yindex || cuddTestInteract(table,zindex,yindex)) { + isolated = table->vars[zindex]->ref == 1; + L -= table->subtables[z].keys - isolated; + } + } + + x = cuddNextLow(table,y); + while (x >= xLow && L <= limitSize) { +#ifdef DD_DEBUG + gybot = y; + while ((unsigned) gybot < table->subtables[gybot].next) + gybot = table->subtables[gybot].next; + checkL = table->keys - table->isolated; + for (z = xLow + 1; z <= gybot; z++) { + zindex = table->invperm[z]; + if (zindex == yindex || cuddTestInteract(table,zindex,yindex)) { + isolated = table->vars[zindex]->ref == 1; + checkL -= table->subtables[z].keys - isolated; + } + } + if (pr > 0 && L != checkL) { + (void) fprintf(table->out, + "Inaccurate lower bound: L = %d checkL = %d\n", + L, checkL); + } +#endif + gxtop = table->subtables[x].next; + if (checkFunction(table,x,y)) { + /* Group found, attach groups */ + table->subtables[x].next = y; + i = table->subtables[y].next; + while (table->subtables[i].next != (unsigned) y) + i = table->subtables[i].next; + table->subtables[i].next = gxtop; + move = (Move *)cuddDynamicAllocNode(table); + if (move == NULL) goto ddGroupSiftingUpOutOfMem; + move->x = x; + move->y = y; + move->flags = MTR_NEWNODE; + move->size = table->keys - table->isolated; + move->next = *moves; + *moves = move; + } else if (table->subtables[x].next == (unsigned) x && + table->subtables[y].next == (unsigned) y) { + /* x and y are self groups */ + xindex = table->invperm[x]; + size = cuddSwapInPlace(table,x,y); +#ifdef DD_DEBUG + assert(table->subtables[x].next == (unsigned) x); + assert(table->subtables[y].next == (unsigned) y); +#endif + if (size == 0) goto ddGroupSiftingUpOutOfMem; + /* Update the lower bound. */ + if (cuddTestInteract(table,xindex,yindex)) { + isolated = table->vars[xindex]->ref == 1; + L += table->subtables[y].keys - isolated; + } + move = (Move *)cuddDynamicAllocNode(table); + if (move == NULL) goto ddGroupSiftingUpOutOfMem; + move->x = x; + move->y = y; + move->flags = MTR_DEFAULT; + move->size = size; + move->next = *moves; + *moves = move; + +#ifdef DD_DEBUG + if (pr > 0) (void) fprintf(table->out, + "ddGroupSiftingUp (2 single groups):\n"); +#endif + if ((double) size > (double) limitSize * table->maxGrowth) + return(1); + if (size < limitSize) limitSize = size; + } else { /* Group move */ + size = ddGroupMove(table,x,y,moves); + if (size == 0) goto ddGroupSiftingUpOutOfMem; + /* Update the lower bound. */ + z = (*moves)->y; + do { + zindex = table->invperm[z]; + if (cuddTestInteract(table,zindex,yindex)) { + isolated = table->vars[zindex]->ref == 1; + L += table->subtables[z].keys - isolated; + } + z = table->subtables[z].next; + } while (z != (int) (*moves)->y); + if ((double) size > (double) limitSize * table->maxGrowth) + return(1); + if (size < limitSize) limitSize = size; + } + y = gxtop; + x = cuddNextLow(table,y); + } + + return(1); + + ddGroupSiftingUpOutOfMem: + while (*moves != NULL) { + move = (*moves)->next; + cuddDeallocMove(table, *moves); + *moves = move; + } + return(0); + +} /* end of ddGroupSiftingUp */ + + +/**Function******************************************************************** + + Synopsis [Sifts down a variable until it reaches position xHigh.] + + Description [Sifts down a variable until it reaches position xHigh. + Assumes that x is the bottom of a group (or a singleton). Records + all the moves. Returns 1 in case of success; 0 otherwise.] + + SideEffects [None] + +******************************************************************************/ +static int +ddGroupSiftingDown( + DdManager * table, + int x, + int xHigh, + DD_CHKFP checkFunction, + Move ** moves) +{ + Move *move; + int y; + int size; + int limitSize; + int gxtop,gybot; + int R; /* upper bound on node decrease */ + int xindex, yindex; + int isolated, allVars; + int z; + int zindex; +#ifdef DD_DEBUG + int checkR; +#endif + + /* If the group consists of simple variables, there is no point in + ** sifting it down. This check is redundant if the projection functions + ** do not have external references, because the computation of the + ** lower bound takes care of the problem. It is necessary otherwise to + ** prevent the sifting down of simple variables. */ + y = x; + allVars = 1; + do { + if (table->subtables[y].keys != 1) { + allVars = 0; + break; + } + y = table->subtables[y].next; + } while (table->subtables[y].next != (unsigned) x); + if (allVars) + return(1); + + /* Initialize R. */ + xindex = table->invperm[x]; + gxtop = table->subtables[x].next; + limitSize = size = table->keys - table->isolated; + R = 0; + for (z = xHigh; z > gxtop; z--) { + zindex = table->invperm[z]; + if (zindex == xindex || cuddTestInteract(table,xindex,zindex)) { + isolated = table->vars[zindex]->ref == 1; + R += table->subtables[z].keys - isolated; + } + } + + y = cuddNextHigh(table,x); + while (y <= xHigh && size - R < limitSize) { +#ifdef DD_DEBUG + gxtop = table->subtables[x].next; + checkR = 0; + for (z = xHigh; z > gxtop; z--) { + zindex = table->invperm[z]; + if (zindex == xindex || cuddTestInteract(table,xindex,zindex)) { + isolated = table->vars[zindex]->ref == 1; + checkR += table->subtables[z].keys - isolated; + } + } + assert(R >= checkR); +#endif + /* Find bottom of y group. */ + gybot = table->subtables[y].next; + while (table->subtables[gybot].next != (unsigned) y) + gybot = table->subtables[gybot].next; + + if (checkFunction(table,x,y)) { + /* Group found: attach groups and record move. */ + gxtop = table->subtables[x].next; + table->subtables[x].next = y; + table->subtables[gybot].next = gxtop; + move = (Move *)cuddDynamicAllocNode(table); + if (move == NULL) goto ddGroupSiftingDownOutOfMem; + move->x = x; + move->y = y; + move->flags = MTR_NEWNODE; + move->size = table->keys - table->isolated; + move->next = *moves; + *moves = move; + } else if (table->subtables[x].next == (unsigned) x && + table->subtables[y].next == (unsigned) y) { + /* x and y are self groups */ + /* Update upper bound on node decrease. */ + yindex = table->invperm[y]; + if (cuddTestInteract(table,xindex,yindex)) { + isolated = table->vars[yindex]->ref == 1; + R -= table->subtables[y].keys - isolated; + } + size = cuddSwapInPlace(table,x,y); +#ifdef DD_DEBUG + assert(table->subtables[x].next == (unsigned) x); + assert(table->subtables[y].next == (unsigned) y); +#endif + if (size == 0) goto ddGroupSiftingDownOutOfMem; + + /* Record move. */ + move = (Move *) cuddDynamicAllocNode(table); + if (move == NULL) goto ddGroupSiftingDownOutOfMem; + move->x = x; + move->y = y; + move->flags = MTR_DEFAULT; + move->size = size; + move->next = *moves; + *moves = move; + +#ifdef DD_DEBUG + if (pr > 0) (void) fprintf(table->out, + "ddGroupSiftingDown (2 single groups):\n"); +#endif + if ((double) size > (double) limitSize * table->maxGrowth) + return(1); + if (size < limitSize) limitSize = size; + + x = y; + y = cuddNextHigh(table,x); + } else { /* Group move */ + /* Update upper bound on node decrease: first phase. */ + gxtop = table->subtables[x].next; + z = gxtop + 1; + do { + zindex = table->invperm[z]; + if (zindex == xindex || cuddTestInteract(table,xindex,zindex)) { + isolated = table->vars[zindex]->ref == 1; + R -= table->subtables[z].keys - isolated; + } + z++; + } while (z <= gybot); + size = ddGroupMove(table,x,y,moves); + if (size == 0) goto ddGroupSiftingDownOutOfMem; + if ((double) size > (double) limitSize * table->maxGrowth) + return(1); + if (size < limitSize) limitSize = size; + + /* Update upper bound on node decrease: second phase. */ + gxtop = table->subtables[gybot].next; + for (z = gxtop + 1; z <= gybot; z++) { + zindex = table->invperm[z]; + if (zindex == xindex || cuddTestInteract(table,xindex,zindex)) { + isolated = table->vars[zindex]->ref == 1; + R += table->subtables[z].keys - isolated; + } + } + } + x = gybot; + y = cuddNextHigh(table,x); + } + + return(1); + + ddGroupSiftingDownOutOfMem: + while (*moves != NULL) { + move = (*moves)->next; + cuddDeallocMove(table, *moves); + *moves = move; + } + + return(0); + +} /* end of ddGroupSiftingDown */ + + +/**Function******************************************************************** + + Synopsis [Swaps two groups and records the move.] + + Description [Swaps two groups and records the move. Returns the + number of keys in the DD table in case of success; 0 otherwise.] + + SideEffects [None] + +******************************************************************************/ +static int +ddGroupMove( + DdManager * table, + int x, + int y, + Move ** moves) +{ + Move *move; + int size; + int i,j,xtop,xbot,xsize,ytop,ybot,ysize,newxtop; + int swapx,swapy; +#if defined(DD_DEBUG) && defined(DD_VERBOSE) + int initialSize,bestSize; +#endif + +#ifdef DD_DEBUG + /* We assume that x < y */ + assert(x < y); +#endif + /* Find top, bottom, and size for the two groups. */ + xbot = x; + xtop = table->subtables[x].next; + xsize = xbot - xtop + 1; + ybot = y; + while ((unsigned) ybot < table->subtables[ybot].next) + ybot = table->subtables[ybot].next; + ytop = y; + ysize = ybot - ytop + 1; + +#if defined(DD_DEBUG) && defined(DD_VERBOSE) + initialSize = bestSize = table->keys - table->isolated; +#endif + /* Sift the variables of the second group up through the first group */ + for (i = 1; i <= ysize; i++) { + for (j = 1; j <= xsize; j++) { + size = cuddSwapInPlace(table,x,y); + if (size == 0) goto ddGroupMoveOutOfMem; +#if defined(DD_DEBUG) && defined(DD_VERBOSE) + if (size < bestSize) + bestSize = size; +#endif + swapx = x; swapy = y; + y = x; + x = cuddNextLow(table,y); + } + y = ytop + i; + x = cuddNextLow(table,y); + } +#if defined(DD_DEBUG) && defined(DD_VERBOSE) + if ((bestSize < initialSize) && (bestSize < size)) + (void) fprintf(table->out,"Missed local minimum: initialSize:%d bestSize:%d finalSize:%d\n",initialSize,bestSize,size); +#endif + + /* fix groups */ + y = xtop; /* ytop is now where xtop used to be */ + for (i = 0; i < ysize - 1; i++) { + table->subtables[y].next = cuddNextHigh(table,y); + y = cuddNextHigh(table,y); + } + table->subtables[y].next = xtop; /* y is bottom of its group, join */ + /* it to top of its group */ + x = cuddNextHigh(table,y); + newxtop = x; + for (i = 0; i < xsize - 1; i++) { + table->subtables[x].next = cuddNextHigh(table,x); + x = cuddNextHigh(table,x); + } + table->subtables[x].next = newxtop; /* x is bottom of its group, join */ + /* it to top of its group */ +#ifdef DD_DEBUG + if (pr > 0) (void) fprintf(table->out,"ddGroupMove:\n"); +#endif + + /* Store group move */ + move = (Move *) cuddDynamicAllocNode(table); + if (move == NULL) goto ddGroupMoveOutOfMem; + move->x = swapx; + move->y = swapy; + move->flags = MTR_DEFAULT; + move->size = table->keys - table->isolated; + move->next = *moves; + *moves = move; + + return(table->keys - table->isolated); + + ddGroupMoveOutOfMem: + while (*moves != NULL) { + move = (*moves)->next; + cuddDeallocMove(table, *moves); + *moves = move; + } + return(0); + +} /* end of ddGroupMove */ + + +/**Function******************************************************************** + + Synopsis [Undoes the swap two groups.] + + Description [Undoes the swap two groups. Returns 1 in case of + success; 0 otherwise.] + + SideEffects [None] + +******************************************************************************/ +static int +ddGroupMoveBackward( + DdManager * table, + int x, + int y) +{ + int size; + int i,j,xtop,xbot,xsize,ytop,ybot,ysize,newxtop; + + +#ifdef DD_DEBUG + /* We assume that x < y */ + assert(x < y); +#endif + + /* Find top, bottom, and size for the two groups. */ + xbot = x; + xtop = table->subtables[x].next; + xsize = xbot - xtop + 1; + ybot = y; + while ((unsigned) ybot < table->subtables[ybot].next) + ybot = table->subtables[ybot].next; + ytop = y; + ysize = ybot - ytop + 1; + + /* Sift the variables of the second group up through the first group */ + for (i = 1; i <= ysize; i++) { + for (j = 1; j <= xsize; j++) { + size = cuddSwapInPlace(table,x,y); + if (size == 0) + return(0); + y = x; + x = cuddNextLow(table,y); + } + y = ytop + i; + x = cuddNextLow(table,y); + } + + /* fix groups */ + y = xtop; + for (i = 0; i < ysize - 1; i++) { + table->subtables[y].next = cuddNextHigh(table,y); + y = cuddNextHigh(table,y); + } + table->subtables[y].next = xtop; /* y is bottom of its group, join */ + /* to its top */ + x = cuddNextHigh(table,y); + newxtop = x; + for (i = 0; i < xsize - 1; i++) { + table->subtables[x].next = cuddNextHigh(table,x); + x = cuddNextHigh(table,x); + } + table->subtables[x].next = newxtop; /* x is bottom of its group, join */ + /* to its top */ +#ifdef DD_DEBUG + if (pr > 0) (void) fprintf(table->out,"ddGroupMoveBackward:\n"); +#endif + + return(1); + +} /* end of ddGroupMoveBackward */ + + +/**Function******************************************************************** + + Synopsis [Determines the best position for a variables and returns + it there.] + + Description [Determines the best position for a variables and returns + it there. Returns 1 in case of success; 0 otherwise.] + + SideEffects [None] + +******************************************************************************/ +static int +ddGroupSiftingBackward( + DdManager * table, + Move * moves, + int size, + int upFlag, + int lazyFlag) +{ + Move *move; + int res; + Move *end_move; + int diff, tmp_diff; + int index; + unsigned int pairlev; + + if (lazyFlag) { + end_move = NULL; + + /* Find the minimum size, and the earliest position at which it + ** was achieved. */ + for (move = moves; move != NULL; move = move->next) { + if (move->size < size) { + size = move->size; + end_move = move; + } else if (move->size == size) { + if (end_move == NULL) end_move = move; + } + } + + /* Find among the moves that give minimum size the one that + ** minimizes the distance from the corresponding variable. */ + if (moves != NULL) { + diff = Cudd_ReadSize(table) + 1; + index = (upFlag == 1) ? + table->invperm[moves->x] : table->invperm[moves->y]; + pairlev = + (unsigned) table->perm[Cudd_bddReadPairIndex(table, index)]; + + for (move = moves; move != NULL; move = move->next) { + if (move->size == size) { + if (upFlag == 1) { + tmp_diff = (move->x > pairlev) ? + move->x - pairlev : pairlev - move->x; + } else { + tmp_diff = (move->y > pairlev) ? + move->y - pairlev : pairlev - move->y; + } + if (tmp_diff < diff) { + diff = tmp_diff; + end_move = move; + } + } + } + } + } else { + /* Find the minimum size. */ + for (move = moves; move != NULL; move = move->next) { + if (move->size < size) { + size = move->size; + } + } + } + + /* In case of lazy sifting, end_move identifies the position at + ** which we want to stop. Otherwise, we stop as soon as we meet + ** the minimum size. */ + for (move = moves; move != NULL; move = move->next) { + if (lazyFlag) { + if (move == end_move) return(1); + } else { + if (move->size == size) return(1); + } + if ((table->subtables[move->x].next == move->x) && + (table->subtables[move->y].next == move->y)) { + res = cuddSwapInPlace(table,(int)move->x,(int)move->y); + if (!res) return(0); +#ifdef DD_DEBUG + if (pr > 0) (void) fprintf(table->out,"ddGroupSiftingBackward:\n"); + assert(table->subtables[move->x].next == move->x); + assert(table->subtables[move->y].next == move->y); +#endif + } else { /* Group move necessary */ + if (move->flags == MTR_NEWNODE) { + ddDissolveGroup(table,(int)move->x,(int)move->y); + } else { + res = ddGroupMoveBackward(table,(int)move->x,(int)move->y); + if (!res) return(0); + } + } + + } + + return(1); + +} /* end of ddGroupSiftingBackward */ + + +/**Function******************************************************************** + + Synopsis [Merges groups in the DD table.] + + Description [Creates a single group from low to high and adjusts the + index field of the tree node.] + + SideEffects [None] + +******************************************************************************/ +static void +ddMergeGroups( + DdManager * table, + MtrNode * treenode, + int low, + int high) +{ + int i; + MtrNode *auxnode; + int saveindex; + int newindex; + + /* Merge all variables from low to high in one group, unless + ** this is the topmost group. In such a case we do not merge lest + ** we lose the symmetry information. */ + if (treenode != table->tree) { + for (i = low; i < high; i++) + table->subtables[i].next = i+1; + table->subtables[high].next = low; + } + + /* Adjust the index fields of the tree nodes. If a node is the + ** first child of its parent, then the parent may also need adjustment. */ + saveindex = treenode->index; + newindex = table->invperm[low]; + auxnode = treenode; + do { + auxnode->index = newindex; + if (auxnode->parent == NULL || + (int) auxnode->parent->index != saveindex) + break; + auxnode = auxnode->parent; + } while (1); + return; + +} /* end of ddMergeGroups */ + + +/**Function******************************************************************** + + Synopsis [Dissolves a group in the DD table.] + + Description [x and y are variables in a group to be cut in two. The cut + is to pass between x and y.] + + SideEffects [None] + +******************************************************************************/ +static void +ddDissolveGroup( + DdManager * table, + int x, + int y) +{ + int topx; + int boty; + + /* find top and bottom of the two groups */ + boty = y; + while ((unsigned) boty < table->subtables[boty].next) + boty = table->subtables[boty].next; + + topx = table->subtables[boty].next; + + table->subtables[boty].next = y; + table->subtables[x].next = topx; + + return; + +} /* end of ddDissolveGroup */ + + +/**Function******************************************************************** + + Synopsis [Pretends to check two variables for aggregation.] + + Description [Pretends to check two variables for aggregation. Always + returns 0.] + + SideEffects [None] + +******************************************************************************/ +static int +ddNoCheck( + DdManager * table, + int x, + int y) +{ + return(0); + +} /* end of ddNoCheck */ + + +/**Function******************************************************************** + + Synopsis [Checks two variables for aggregation.] + + Description [Checks two variables for aggregation. The check is based + on the second difference of the number of nodes as a function of the + layer. If the second difference is lower than a given threshold + (typically negative) then the two variables should be aggregated. + Returns 1 if the two variables pass the test; 0 otherwise.] + + SideEffects [None] + +******************************************************************************/ +static int +ddSecDiffCheck( + DdManager * table, + int x, + int y) +{ + double Nx,Nx_1; + double Sx; + double threshold; + int xindex,yindex; + + if (x==0) return(0); + +#ifdef DD_STATS + secdiffcalls++; +#endif + Nx = (double) table->subtables[x].keys; + Nx_1 = (double) table->subtables[x-1].keys; + Sx = (table->subtables[y].keys/Nx) - (Nx/Nx_1); + + threshold = table->recomb / 100.0; + if (Sx < threshold) { + xindex = table->invperm[x]; + yindex = table->invperm[y]; + if (cuddTestInteract(table,xindex,yindex)) { +#if defined(DD_DEBUG) && defined(DD_VERBOSE) + (void) fprintf(table->out, + "Second difference for %d = %g Pos(%d)\n", + table->invperm[x],Sx,x); +#endif +#ifdef DD_STATS + secdiff++; +#endif + return(1); + } else { +#ifdef DD_STATS + secdiffmisfire++; +#endif + return(0); + } + + } + return(0); + +} /* end of ddSecDiffCheck */ + + +/**Function******************************************************************** + + Synopsis [Checks for extended symmetry of x and y.] + + Description [Checks for extended symmetry of x and y. Returns 1 in + case of extended symmetry; 0 otherwise.] + + SideEffects [None] + +******************************************************************************/ +static int +ddExtSymmCheck( + DdManager * table, + int x, + int y) +{ + DdNode *f,*f0,*f1,*f01,*f00,*f11,*f10; + DdNode *one; + unsigned comple; /* f0 is complemented */ + int notproj; /* f is not a projection function */ + int arccount; /* number of arcs from layer x to layer y */ + int TotalRefCount; /* total reference count of layer y minus 1 */ + int counter; /* number of nodes of layer x that are allowed */ + /* to violate extended symmetry conditions */ + int arccounter; /* number of arcs into layer y that are allowed */ + /* to come from layers other than x */ + int i; + int xindex; + int yindex; + int res; + int slots; + DdNodePtr *list; + DdNode *sentinel = &(table->sentinel); + + xindex = table->invperm[x]; + yindex = table->invperm[y]; + + /* If the two variables do not interact, we do not want to merge them. */ + if (!cuddTestInteract(table,xindex,yindex)) + return(0); + +#ifdef DD_DEBUG + /* Checks that x and y do not contain just the projection functions. + ** With the test on interaction, these test become redundant, + ** because an isolated projection function does not interact with + ** any other variable. + */ + if (table->subtables[x].keys == 1) { + assert(table->vars[xindex]->ref != 1); + } + if (table->subtables[y].keys == 1) { + assert(table->vars[yindex]->ref != 1); + } +#endif + +#ifdef DD_STATS + extsymmcalls++; +#endif + + arccount = 0; + counter = (int) (table->subtables[x].keys * + (table->symmviolation/100.0) + 0.5); + one = DD_ONE(table); + + slots = table->subtables[x].slots; + list = table->subtables[x].nodelist; + for (i = 0; i < slots; i++) { + f = list[i]; + while (f != sentinel) { + /* Find f1, f0, f11, f10, f01, f00. */ + f1 = cuddT(f); + f0 = Cudd_Regular(cuddE(f)); + comple = Cudd_IsComplement(cuddE(f)); + notproj = f1 != one || f0 != one || f->ref != (DdHalfWord) 1; + if (f1->index == (unsigned) yindex) { + arccount++; + f11 = cuddT(f1); f10 = cuddE(f1); + } else { + if ((int) f0->index != yindex) { + /* If f is an isolated projection function it is + ** allowed to bypass layer y. + */ + if (notproj) { + if (counter == 0) + return(0); + counter--; /* f bypasses layer y */ + } + } + f11 = f10 = f1; + } + if ((int) f0->index == yindex) { + arccount++; + f01 = cuddT(f0); f00 = cuddE(f0); + } else { + f01 = f00 = f0; + } + if (comple) { + f01 = Cudd_Not(f01); + f00 = Cudd_Not(f00); + } + + /* Unless we are looking at a projection function + ** without external references except the one from the + ** table, we insist that f01 == f10 or f11 == f00 + */ + if (notproj) { + if (f01 != f10 && f11 != f00) { + if (counter == 0) + return(0); + counter--; + } + } + + f = f->next; + } /* while */ + } /* for */ + + /* Calculate the total reference counts of y */ + TotalRefCount = -1; /* -1 for projection function */ + slots = table->subtables[y].slots; + list = table->subtables[y].nodelist; + for (i = 0; i < slots; i++) { + f = list[i]; + while (f != sentinel) { + TotalRefCount += f->ref; + f = f->next; + } + } + + arccounter = (int) (table->subtables[y].keys * + (table->arcviolation/100.0) + 0.5); + res = arccount >= TotalRefCount - arccounter; + +#if defined(DD_DEBUG) && defined(DD_VERBOSE) + if (res) { + (void) fprintf(table->out, + "Found extended symmetry! x = %d\ty = %d\tPos(%d,%d)\n", + xindex,yindex,x,y); + } +#endif + +#ifdef DD_STATS + if (res) + extsymm++; +#endif + return(res); + +} /* end ddExtSymmCheck */ + + +/**Function******************************************************************** + + Synopsis [Checks for grouping of x and y.] + + Description [Checks for grouping of x and y. Returns 1 in + case of grouping; 0 otherwise. This function is used for lazy sifting.] + + SideEffects [None] + +******************************************************************************/ +static int +ddVarGroupCheck( + DdManager * table, + int x, + int y) +{ + int xindex = table->invperm[x]; + int yindex = table->invperm[y]; + + if (Cudd_bddIsVarToBeUngrouped(table, xindex)) return(0); + + if (Cudd_bddReadPairIndex(table, xindex) == yindex) { + if (ddIsVarHandled(table, xindex) || + ddIsVarHandled(table, yindex)) { + if (Cudd_bddIsVarToBeGrouped(table, xindex) || + Cudd_bddIsVarToBeGrouped(table, yindex) ) { + if (table->keys - table->isolated <= originalSize) { + return(1); + } + } + } + } + + return(0); + +} /* end of ddVarGroupCheck */ + + +/**Function******************************************************************** + + Synopsis [Sets a variable to already handled.] + + Description [Sets a variable to already handled. This function is used + for lazy sifting.] + + SideEffects [none] + + SeeAlso [] + +******************************************************************************/ +static int +ddSetVarHandled( + DdManager *dd, + int index) +{ + if (index >= dd->size || index < 0) return(0); + dd->subtables[dd->perm[index]].varHandled = 1; + return(1); + +} /* end of ddSetVarHandled */ + + +/**Function******************************************************************** + + Synopsis [Resets a variable to be processed.] + + Description [Resets a variable to be processed. This function is used + for lazy sifting.] + + SideEffects [none] + + SeeAlso [] + +******************************************************************************/ +static int +ddResetVarHandled( + DdManager *dd, + int index) +{ + if (index >= dd->size || index < 0) return(0); + dd->subtables[dd->perm[index]].varHandled = 0; + return(1); + +} /* end of ddResetVarHandled */ + + +/**Function******************************************************************** + + Synopsis [Checks whether a variables is already handled.] + + Description [Checks whether a variables is already handled. This + function is used for lazy sifting.] + + SideEffects [none] + + SeeAlso [] + +******************************************************************************/ +static int +ddIsVarHandled( + DdManager *dd, + int index) +{ + if (index >= dd->size || index < 0) return(-1); + return dd->subtables[dd->perm[index]].varHandled; + +} /* end of ddIsVarHandled */ + +/**CFile*********************************************************************** + + FileName [cuddInit.c] + + PackageName [cudd] + + Synopsis [Functions to initialize and shut down the DD manager.] + + Description [External procedures included in this module: +
        +
      • Cudd_Init() +
      • Cudd_Quit() +
      + Internal procedures included in this module: +
        +
      • cuddZddInitUniv() +
      • cuddZddFreeUniv() +
      + ] + + SeeAlso [] + + Author [Fabio Somenzi] + + Copyright [Copyright (c) 1995-2012, Regents of the University of Colorado + + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + Neither the name of the University of Colorado nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE.] + +******************************************************************************/ + + +/*---------------------------------------------------------------------------*/ +/* Constant declarations */ +/*---------------------------------------------------------------------------*/ + + +/*---------------------------------------------------------------------------*/ +/* Stucture declarations */ +/*---------------------------------------------------------------------------*/ + + +/*---------------------------------------------------------------------------*/ +/* Type declarations */ +/*---------------------------------------------------------------------------*/ + + +/*---------------------------------------------------------------------------*/ +/* Variable declarations */ +/*---------------------------------------------------------------------------*/ + +//#ifndef lint +//static char rcsid[] DD_UNUSED = "$Id: cuddInit.c,v 1.34 2012/02/05 01:07:19 fabio Exp $"; +//#endif + +/*---------------------------------------------------------------------------*/ +/* Macro declarations */ +/*---------------------------------------------------------------------------*/ + + +/**AutomaticStart*************************************************************/ + +/*---------------------------------------------------------------------------*/ +/* Static function prototypes */ +/*---------------------------------------------------------------------------*/ + + +/**AutomaticEnd***************************************************************/ + + +/*---------------------------------------------------------------------------*/ +/* Definition of exported functions */ +/*---------------------------------------------------------------------------*/ + +/**Function******************************************************************** + + Synopsis [Creates a new DD manager.] + + Description [Creates a new DD manager, initializes the table, the + basic constants and the projection functions. If maxMemory is 0, + Cudd_Init decides suitable values for the maximum size of the cache + and for the limit for fast unique table growth based on the available + memory. Returns a pointer to the manager if successful; NULL + otherwise.] + + SideEffects [None] + + SeeAlso [Cudd_Quit] + +******************************************************************************/ +DdManager * +Cudd_Init( + unsigned int numVars /* initial number of BDD variables (i.e., subtables) */, + unsigned int numVarsZ /* initial number of ZDD variables (i.e., subtables) */, + unsigned int numSlots /* initial size of the unique tables */, + unsigned int cacheSize /* initial size of the cache */, + unsigned long maxMemory /* target maximum memory occupation */) +{ + DdManager *unique; + int i,result; + DdNode *one, *zero; + unsigned int maxCacheSize; + unsigned int looseUpTo; + extern DD_OOMFP MMoutOfMemory; + DD_OOMFP saveHandler; + + if (maxMemory == 0) { + maxMemory = getSoftDataLimit(); + } + looseUpTo = (unsigned int) ((maxMemory / sizeof(DdNode)) / + DD_MAX_LOOSE_FRACTION); + unique = cuddInitTable(numVars,numVarsZ,numSlots,looseUpTo); + if (unique == NULL) return(NULL); + unique->maxmem = (unsigned long) maxMemory / 10 * 9; + maxCacheSize = (unsigned int) ((maxMemory / sizeof(DdCache)) / + DD_MAX_CACHE_FRACTION); + result = cuddInitCache(unique,cacheSize,maxCacheSize); + if (result == 0) return(NULL); + + saveHandler = MMoutOfMemory; + MMoutOfMemory = Cudd_OutOfMem; + unique->stash = ALLOC(char,(maxMemory / DD_STASH_FRACTION) + 4); + MMoutOfMemory = saveHandler; + if (unique->stash == NULL) { + (void) fprintf(unique->err,"Unable to set aside memory\n"); + } + + /* Initialize constants. */ + unique->one = cuddUniqueConst(unique,1.0); + if (unique->one == NULL) return(0); + cuddRef(unique->one); + unique->zero = cuddUniqueConst(unique,0.0); + if (unique->zero == NULL) return(0); + cuddRef(unique->zero); +#ifdef HAVE_IEEE_754 + if (DD_PLUS_INF_VAL != DD_PLUS_INF_VAL * 3 || + DD_PLUS_INF_VAL != DD_PLUS_INF_VAL / 3) { + (void) fprintf(unique->err,"Warning: Crippled infinite values\n"); + (void) fprintf(unique->err,"Recompile without -DHAVE_IEEE_754\n"); + } +#endif + unique->plusinfinity = cuddUniqueConst(unique,DD_PLUS_INF_VAL); + if (unique->plusinfinity == NULL) return(0); + cuddRef(unique->plusinfinity); + unique->minusinfinity = cuddUniqueConst(unique,DD_MINUS_INF_VAL); + if (unique->minusinfinity == NULL) return(0); + cuddRef(unique->minusinfinity); + unique->background = unique->zero; + + /* The logical zero is different from the CUDD_VALUE_TYPE zero! */ + one = unique->one; + zero = Cudd_Not(one); + /* Create the projection functions. */ + unique->vars = ALLOC(DdNodePtr,unique->maxSize); + if (unique->vars == NULL) { + unique->errorCode = CUDD_MEMORY_OUT; + return(NULL); + } + for (i = 0; i < unique->size; i++) { + unique->vars[i] = cuddUniqueInter(unique,i,one,zero); + if (unique->vars[i] == NULL) return(0); + cuddRef(unique->vars[i]); + } + + if (unique->sizeZ) + cuddZddInitUniv(unique); + + unique->memused += sizeof(DdNode *) * unique->maxSize; + + return(unique); + +} /* end of Cudd_Init */ + + +/**Function******************************************************************** + + Synopsis [Deletes resources associated with a DD manager.] + + Description [Deletes resources associated with a DD manager and + resets the global statistical counters. (Otherwise, another manaqger + subsequently created would inherit the stats of this one.)] + + SideEffects [None] + + SeeAlso [Cudd_Init] + +******************************************************************************/ +void +Cudd_Quit( + DdManager * unique) +{ + if (unique->stash != NULL) FREE(unique->stash); + cuddFreeTable(unique); + +} /* end of Cudd_Quit */ + + +/*---------------------------------------------------------------------------*/ +/* Definition of internal functions */ +/*---------------------------------------------------------------------------*/ + + +/**Function******************************************************************** + + Synopsis [Initializes the ZDD universe.] + + Description [Initializes the ZDD universe. Returns 1 if successful; 0 + otherwise.] + + SideEffects [None] + + SeeAlso [cuddZddFreeUniv] + +******************************************************************************/ +int +cuddZddInitUniv( + DdManager * zdd) +{ + DdNode *p, *res; + int i; + + zdd->univ = ALLOC(DdNodePtr, zdd->sizeZ); + if (zdd->univ == NULL) { + zdd->errorCode = CUDD_MEMORY_OUT; + return(0); + } + + res = DD_ONE(zdd); + cuddRef(res); + for (i = zdd->sizeZ - 1; i >= 0; i--) { + unsigned int index = zdd->invpermZ[i]; + p = res; + res = cuddUniqueInterZdd(zdd, index, p, p); + if (res == NULL) { + Cudd_RecursiveDerefZdd(zdd,p); + FREE(zdd->univ); + return(0); + } + cuddRef(res); + cuddDeref(p); + zdd->univ[i] = res; + } + +#ifdef DD_VERBOSE + cuddZddP(zdd, zdd->univ[0]); +#endif + + return(1); + +} /* end of cuddZddInitUniv */ + + +/**Function******************************************************************** + + Synopsis [Frees the ZDD universe.] + + Description [Frees the ZDD universe.] + + SideEffects [None] + + SeeAlso [cuddZddInitUniv] + +******************************************************************************/ +void +cuddZddFreeUniv( + DdManager * zdd) +{ + if (zdd->univ) { + Cudd_RecursiveDerefZdd(zdd, zdd->univ[0]); + FREE(zdd->univ); + } + +} /* end of cuddZddFreeUniv */ + + +/*---------------------------------------------------------------------------*/ +/* Definition of static functions */ +/*---------------------------------------------------------------------------*/ + +/**CFile*********************************************************************** + + FileName [cuddInteract.c] + + PackageName [cudd] + + Synopsis [Functions to manipulate the variable interaction matrix.] + + Description [Internal procedures included in this file: +
        +
      • cuddSetInteract() +
      • cuddTestInteract() +
      • cuddInitInteract() +
      + Static procedures included in this file: +
        +
      • ddSuppInteract() +
      • ddClearLocal() +
      • ddUpdateInteract() +
      • ddClearGlobal() +
      + The interaction matrix tells whether two variables are + both in the support of some function of the DD. The main use of the + interaction matrix is in the in-place swapping. Indeed, if two + variables do not interact, there is no arc connecting the two layers; + therefore, the swap can be performed in constant time, without + scanning the subtables. Another use of the interaction matrix is in + the computation of the lower bounds for sifting. Finally, the + interaction matrix can be used to speed up aggregation checks in + symmetric and group sifting.

      + The computation of the interaction matrix is done with a series of + depth-first searches. The searches start from those nodes that have + only external references. The matrix is stored as a packed array of bits; + since it is symmetric, only the upper triangle is kept in memory. + As a final remark, we note that there may be variables that do + interact, but that for a given variable order have no arc connecting + their layers when they are adjacent. For instance, in ite(a,b,c) with + the order asize, + sets the corresponding bit of the interaction matrix to 1.] + + SideEffects [None] + + SeeAlso [] + +******************************************************************************/ +void +cuddSetInteract( + DdManager * table, + int x, + int y) +{ + int posn, word, bit; + +#ifdef DD_DEBUG + assert(x < y); + assert(y < table->size); + assert(x >= 0); +#endif + + posn = ((((table->size << 1) - x - 3) * x) >> 1) + y - 1; + word = posn >> LOGBPL; + bit = posn & (BPL-1); + table->interact[word] |= 1L << bit; + +} /* end of cuddSetInteract */ + + +/**Function******************************************************************** + + Synopsis [Test interaction matrix entries.] + + Description [Given a pair of variables 0 <= x < y < table->size, + tests whether the corresponding bit of the interaction matrix is 1. + Returns the value of the bit.] + + SideEffects [None] + + SeeAlso [] + +******************************************************************************/ +int +cuddTestInteract( + DdManager * table, + int x, + int y) +{ + int posn, word, bit, result; + + if (x > y) { + int tmp = x; + x = y; + y = tmp; + } +#ifdef DD_DEBUG + assert(x < y); + assert(y < table->size); + assert(x >= 0); +#endif + + posn = ((((table->size << 1) - x - 3) * x) >> 1) + y - 1; + word = posn >> LOGBPL; + bit = posn & (BPL-1); + result = (table->interact[word] >> bit) & 1L; + return(result); + +} /* end of cuddTestInteract */ + + +/**Function******************************************************************** + + Synopsis [Initializes the interaction matrix.] + + Description [Initializes the interaction matrix. The interaction + matrix is implemented as a bit vector storing the upper triangle of + the symmetric interaction matrix. The bit vector is kept in an array + of long integers. The computation is based on a series of depth-first + searches, one for each root of the DAG. Two flags are needed: The + local visited flag uses the LSB of the then pointer. The global + visited flag uses the LSB of the next pointer. + Returns 1 if successful; 0 otherwise.] + + SideEffects [None] + + SeeAlso [] + +******************************************************************************/ +int +cuddInitInteract( + DdManager * table) +{ + int i,j; + unsigned long words; + long *interact; + char *support; + DdNode *f; + DdNode *sentinel = &(table->sentinel); + DdNodePtr *nodelist; + int slots; + unsigned long n = (unsigned long) table->size; + + words = ((n * (n-1)) >> (1 + LOGBPL)) + 1; + table->interact = interact = ALLOC(long,words); + if (interact == NULL) { + table->errorCode = CUDD_MEMORY_OUT; + return(0); + } + for (i = 0; i < words; i++) { + interact[i] = 0; + } + + support = ALLOC(char,n); + if (support == NULL) { + table->errorCode = CUDD_MEMORY_OUT; + FREE(interact); + return(0); + } + for (i = 0; i < n; i++) { + support[i] = 0; + } + + for (i = 0; i < n; i++) { + nodelist = table->subtables[i].nodelist; + slots = table->subtables[i].slots; + for (j = 0; j < slots; j++) { + f = nodelist[j]; + while (f != sentinel) { + /* A node is a root of the DAG if it cannot be + ** reached by nodes above it. If a node was never + ** reached during the previous depth-first searches, + ** then it is a root, and we start a new depth-first + ** search from it. + */ + if (!Cudd_IsComplement(f->next)) { + ddSuppInteract(f,support); + ddClearLocal(f); + ddUpdateInteract(table,support); + } + f = Cudd_Regular(f->next); + } + } + } + ddClearGlobal2(table); + + FREE(support); + return(1); + +} /* end of cuddInitInteract */ + + +/*---------------------------------------------------------------------------*/ +/* Definition of static functions */ +/*---------------------------------------------------------------------------*/ + + +/**Function******************************************************************** + + Synopsis [Find the support of f.] + + Description [Performs a DFS from f. Uses the LSB of the then pointer + as visited flag.] + + SideEffects [Accumulates in support the variables on which f depends.] + + SeeAlso [] + +******************************************************************************/ +static void +ddSuppInteract( + DdNode * f, + char * support) +{ + if (cuddIsConstant(f) || Cudd_IsComplement(cuddT(f))) { + return; + } + + support[f->index] = 1; + ddSuppInteract(cuddT(f),support); + ddSuppInteract(Cudd_Regular(cuddE(f)),support); + /* mark as visited */ + cuddT(f) = Cudd_Complement(cuddT(f)); + f->next = Cudd_Complement(f->next); + return; + +} /* end of ddSuppInteract */ + + +/**Function******************************************************************** + + Synopsis [Performs a DFS from f, clearing the LSB of the then pointers.] + + Description [] + + SideEffects [None] + + SeeAlso [] + +******************************************************************************/ +static void +ddClearLocal( + DdNode * f) +{ + if (cuddIsConstant(f) || !Cudd_IsComplement(cuddT(f))) { + return; + } + /* clear visited flag */ + cuddT(f) = Cudd_Regular(cuddT(f)); + ddClearLocal(cuddT(f)); + ddClearLocal(Cudd_Regular(cuddE(f))); + return; + +} /* end of ddClearLocal */ + + +/**Function******************************************************************** + + Synopsis [Marks as interacting all pairs of variables that appear in + support.] + + Description [If support[i] == support[j] == 1, sets the (i,j) entry + of the interaction matrix to 1.] + + SideEffects [Clears support.] + + SeeAlso [] + +******************************************************************************/ +static void +ddUpdateInteract( + DdManager * table, + char * support) +{ + int i,j; + int n = table->size; + + for (i = 0; i < n-1; i++) { + if (support[i] == 1) { + support[i] = 0; + for (j = i+1; j < n; j++) { + if (support[j] == 1) { + cuddSetInteract(table,i,j); + } + } + } + } + support[n-1] = 0; + +} /* end of ddUpdateInteract */ + + +/**Function******************************************************************** + + Synopsis [Scans the DD and clears the LSB of the next pointers.] + + Description [The LSB of the next pointers are used as markers to tell + whether a node was reached by at least one DFS. Once the interaction + matrix is built, these flags are reset.] + + SideEffects [None] + + SeeAlso [] + +******************************************************************************/ +static void +ddClearGlobal2( + DdManager * table) +{ + int i,j; + DdNode *f; + DdNode *sentinel = &(table->sentinel); + DdNodePtr *nodelist; + int slots; + + for (i = 0; i < table->size; i++) { + nodelist = table->subtables[i].nodelist; + slots = table->subtables[i].slots; + for (j = 0; j < slots; j++) { + f = nodelist[j]; + while (f != sentinel) { + f->next = Cudd_Regular(f->next); + f = f->next; + } + } + } + +} /* end of ddClearGlobal */ + +/**CFile*********************************************************************** + + FileName [cuddLCache.c] + + PackageName [cudd] + + Synopsis [Functions for local caches.] + + Description [Internal procedures included in this module: +

        +
      • cuddLocalCacheInit() +
      • cuddLocalCacheQuit() +
      • cuddLocalCacheInsert() +
      • cuddLocalCacheLookup() +
      • cuddLocalCacheClearDead() +
      • cuddLocalCacheClearAll() +
      • cuddLocalCacheProfile() +
      • cuddHashTableInit() +
      • cuddHashTableQuit() +
      • cuddHashTableGenericQuit() +
      • cuddHashTableInsert() +
      • cuddHashTableLookup() +
      • cuddHashTableGenericInsert() +
      • cuddHashTableGenericLookup() +
      • cuddHashTableInsert2() +
      • cuddHashTableLookup2() +
      • cuddHashTableInsert3() +
      • cuddHashTableLookup3() +
      + Static procedures included in this module: +
        +
      • cuddLocalCacheResize() +
      • ddLCHash() +
      • cuddLocalCacheAddToList() +
      • cuddLocalCacheRemoveFromList() +
      • cuddHashTableResize() +
      • cuddHashTableAlloc() +
      ] + + SeeAlso [] + + Author [Fabio Somenzi] + + Copyright [Copyright (c) 1995-2012, Regents of the University of Colorado + + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + Neither the name of the University of Colorado nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE.] + +******************************************************************************/ + + +/*---------------------------------------------------------------------------*/ +/* Constant declarations */ +/*---------------------------------------------------------------------------*/ + +#define DD_MAX_HASHTABLE_DENSITY 2 /* tells when to resize a table */ + +/*---------------------------------------------------------------------------*/ +/* Stucture declarations */ +/*---------------------------------------------------------------------------*/ + + +/*---------------------------------------------------------------------------*/ +/* Type declarations */ +/*---------------------------------------------------------------------------*/ + + +/*---------------------------------------------------------------------------*/ +/* Variable declarations */ +/*---------------------------------------------------------------------------*/ + +//#ifndef lint +//static char rcsid[] DD_UNUSED = "$Id: cuddLCache.c,v 1.27 2012/02/05 01:07:19 fabio Exp $"; +//#endif + +/*---------------------------------------------------------------------------*/ +/* Macro declarations */ +/*---------------------------------------------------------------------------*/ + +/**Macro*********************************************************************** + + Synopsis [Computes hash function for keys of one operand.] + + Description [] + + SideEffects [None] + + SeeAlso [ddLCHash3 ddLCHash] + +******************************************************************************/ +#if SIZEOF_VOID_P == 8 && SIZEOF_INT == 4 +#define ddLCHash1(f,shift) \ +(((unsigned)(ptruint)(f) * DD_P1) >> (shift)) +#else +#define ddLCHash1(f,shift) \ +(((unsigned)(f) * DD_P1) >> (shift)) +#endif + + +/**Macro*********************************************************************** + + Synopsis [Computes hash function for keys of two operands.] + + Description [] + + SideEffects [None] + + SeeAlso [ddLCHash3 ddLCHash] + +******************************************************************************/ +#if SIZEOF_VOID_P == 8 && SIZEOF_INT == 4 +#define ddLCHash2(f,g,shift) \ +((((unsigned)(ptruint)(f) * DD_P1 + \ + (unsigned)(ptruint)(g)) * DD_P2) >> (shift)) +#else +#define ddLCHash2(f,g,shift) \ +((((unsigned)(f) * DD_P1 + (unsigned)(g)) * DD_P2) >> (shift)) +#endif + + +/**Macro*********************************************************************** + + Synopsis [Computes hash function for keys of three operands.] + + Description [] + + SideEffects [None] + + SeeAlso [ddLCHash2 ddLCHash] + +******************************************************************************/ +#define ddLCHash3(f,g,h,shift) ddCHash2(f,g,h,shift) + + +/**AutomaticStart*************************************************************/ + +/*---------------------------------------------------------------------------*/ +/* Static function prototypes */ +/*---------------------------------------------------------------------------*/ + +static void cuddLocalCacheResize (DdLocalCache *cache); +DD_INLINE static unsigned int ddLCHash (DdNodePtr *key, unsigned int keysize, int shift); +static void cuddLocalCacheAddToList (DdLocalCache *cache); +static void cuddLocalCacheRemoveFromList (DdLocalCache *cache); +static int cuddHashTableResize (DdHashTable *hash); +DD_INLINE static DdHashItem * cuddHashTableAlloc (DdHashTable *hash); + +/**AutomaticEnd***************************************************************/ + + +/*---------------------------------------------------------------------------*/ +/* Definition of exported functions */ +/*---------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------*/ +/* Definition of internal functions */ +/*---------------------------------------------------------------------------*/ + + + +/**Function******************************************************************** + + Synopsis [Clears the dead entries of the local caches of a manager.] + + Description [Clears the dead entries of the local caches of a manager. + Used during garbage collection.] + + SideEffects [None] + + SeeAlso [] + +******************************************************************************/ +void +cuddLocalCacheClearDead( + DdManager * manager) +{ + DdLocalCache *cache = manager->localCaches; + unsigned int keysize; + unsigned int itemsize; + unsigned int slots; + DdLocalCacheItem *item; + DdNodePtr *key; + unsigned int i, j; + + while (cache != NULL) { + keysize = cache->keysize; + itemsize = cache->itemsize; + slots = cache->slots; + item = cache->item; + for (i = 0; i < slots; i++) { + if (item->value != NULL) { + if (Cudd_Regular(item->value)->ref == 0) { + item->value = NULL; + } else { + key = item->key; + for (j = 0; j < keysize; j++) { + if (Cudd_Regular(key[j])->ref == 0) { + item->value = NULL; + break; + } + } + } + } + item = (DdLocalCacheItem *) ((char *) item + itemsize); + } + cache = cache->next; + } + return; + +} /* end of cuddLocalCacheClearDead */ + + +/**Function******************************************************************** + + Synopsis [Clears the local caches of a manager.] + + Description [Clears the local caches of a manager. + Used before reordering.] + + SideEffects [None] + + SeeAlso [] + +******************************************************************************/ +void +cuddLocalCacheClearAll( + DdManager * manager) +{ + DdLocalCache *cache = manager->localCaches; + + while (cache != NULL) { + memset(cache->item, 0, cache->slots * cache->itemsize); + cache = cache->next; + } + return; + +} /* end of cuddLocalCacheClearAll */ + + +#ifdef DD_CACHE_PROFILE + +#define DD_HYSTO_BINS 8 + +/**Function******************************************************************** + + Synopsis [Computes and prints a profile of a local cache usage.] + + Description [Computes and prints a profile of a local cache usage. + Returns 1 if successful; 0 otherwise.] + + SideEffects [None] + + SeeAlso [] + +******************************************************************************/ +int +cuddLocalCacheProfile( + DdLocalCache * cache) +{ + double count, mean, meansq, stddev, expected; + long max, min; + int imax, imin; + int i, retval, slots; + long *hystogram; + int nbins = DD_HYSTO_BINS; + int bin; + long thiscount; + double totalcount; + int nzeroes; + DdLocalCacheItem *entry; + FILE *fp = cache->manager->out; + + slots = cache->slots; + + meansq = mean = expected = 0.0; + max = min = (long) cache->item[0].count; + imax = imin = nzeroes = 0; + totalcount = 0.0; + + hystogram = ALLOC(long, nbins); + if (hystogram == NULL) { + return(0); + } + for (i = 0; i < nbins; i++) { + hystogram[i] = 0; + } + + for (i = 0; i < slots; i++) { + entry = (DdLocalCacheItem *) ((char *) cache->item + + i * cache->itemsize); + thiscount = (long) entry->count; + if (thiscount > max) { + max = thiscount; + imax = i; + } + if (thiscount < min) { + min = thiscount; + imin = i; + } + if (thiscount == 0) { + nzeroes++; + } + count = (double) thiscount; + mean += count; + meansq += count * count; + totalcount += count; + expected += count * (double) i; + bin = (i * nbins) / slots; + hystogram[bin] += thiscount; + } + mean /= (double) slots; + meansq /= (double) slots; + stddev = sqrt(meansq - mean*mean); + + retval = fprintf(fp,"Cache stats: slots = %d average = %g ", slots, mean); + if (retval == EOF) return(0); + retval = fprintf(fp,"standard deviation = %g\n", stddev); + if (retval == EOF) return(0); + retval = fprintf(fp,"Cache max accesses = %ld for slot %d\n", max, imax); + if (retval == EOF) return(0); + retval = fprintf(fp,"Cache min accesses = %ld for slot %d\n", min, imin); + if (retval == EOF) return(0); + retval = fprintf(fp,"Cache unused slots = %d\n", nzeroes); + if (retval == EOF) return(0); + + if (totalcount) { + expected /= totalcount; + retval = fprintf(fp,"Cache access hystogram for %d bins", nbins); + if (retval == EOF) return(0); + retval = fprintf(fp," (expected bin value = %g)\n# ", expected); + if (retval == EOF) return(0); + for (i = nbins - 1; i>=0; i--) { + retval = fprintf(fp,"%ld ", hystogram[i]); + if (retval == EOF) return(0); + } + retval = fprintf(fp,"\n"); + if (retval == EOF) return(0); + } + + FREE(hystogram); + return(1); + +} /* end of cuddLocalCacheProfile */ +#endif + + +/**Function******************************************************************** + + Synopsis [Initializes a hash table.] + + Description [Initializes a hash table. Returns a pointer to the new + table if successful; NULL otherwise.] + + SideEffects [None] + + SeeAlso [cuddHashTableQuit] + +******************************************************************************/ +DdHashTable * +cuddHashTableInit( + DdManager * manager, + unsigned int keySize, + unsigned int initSize) +{ + DdHashTable *hash; + int logSize; + + hash = ALLOC(DdHashTable, 1); + if (hash == NULL) { + manager->errorCode = CUDD_MEMORY_OUT; + return(NULL); + } + hash->keysize = keySize; + hash->manager = manager; + hash->memoryList = NULL; + hash->nextFree = NULL; + hash->itemsize = (keySize + 1) * sizeof(DdNode *) + + sizeof(ptrint) + sizeof(DdHashItem *); + /* We have to guarantee that the shift be < 32. */ + if (initSize < 2) initSize = 2; + logSize = cuddComputeFloorLog2(initSize); + hash->numBuckets = 1 << logSize; + hash->shift = sizeof(int) * 8 - logSize; + hash->bucket = ALLOC(DdHashItem *, hash->numBuckets); + if (hash->bucket == NULL) { + manager->errorCode = CUDD_MEMORY_OUT; + FREE(hash); + return(NULL); + } + memset(hash->bucket, 0, hash->numBuckets * sizeof(DdHashItem *)); + hash->size = 0; + hash->maxsize = hash->numBuckets * DD_MAX_HASHTABLE_DENSITY; + return(hash); + +} /* end of cuddHashTableInit */ + + +/**Function******************************************************************** + + Synopsis [Shuts down a hash table.] + + Description [Shuts down a hash table, dereferencing all the values.] + + SideEffects [None] + + SeeAlso [cuddHashTableInit] + +******************************************************************************/ +void +cuddHashTableQuit( + DdHashTable * hash) +{ + unsigned int i; + DdManager *dd = hash->manager; + DdHashItem *bucket; + DdHashItem **memlist, **nextmem; + unsigned int numBuckets = hash->numBuckets; + + for (i = 0; i < numBuckets; i++) { + bucket = hash->bucket[i]; + while (bucket != NULL) { + Cudd_RecursiveDeref(dd, bucket->value); + bucket = bucket->next; + } + } + + memlist = hash->memoryList; + while (memlist != NULL) { + nextmem = (DdHashItem **) memlist[0]; + FREE(memlist); + memlist = nextmem; + } + + FREE(hash->bucket); + FREE(hash); + + return; + +} /* end of cuddHashTableQuit */ + + +/**Function******************************************************************** + + Synopsis [Shuts down a hash table.] + + Description [Shuts down a hash table, when the values are not DdNode + pointers.] + + SideEffects [None] + + SeeAlso [cuddHashTableInit] + +******************************************************************************/ +void +cuddHashTableGenericQuit( + DdHashTable * hash) +{ +#ifdef __osf__ + #pragma pointer_size save +#pragma pointer_size short +#endif + DdHashItem **memlist, **nextmem; + + memlist = hash->memoryList; + while (memlist != NULL) { + nextmem = (DdHashItem **) memlist[0]; + FREE(memlist); + memlist = nextmem; + } + + FREE(hash->bucket); + FREE(hash); +#ifdef __osf__ +#pragma pointer_size restore +#endif + + return; + +} /* end of cuddHashTableGenericQuit */ + + +/**Function******************************************************************** + + Synopsis [Inserts an item in a hash table.] + + Description [Inserts an item in a hash table when the key has more than + three pointers. Returns 1 if successful; 0 otherwise.] + + SideEffects [None] + + SeeAlso [[cuddHashTableInsert1 cuddHashTableInsert2 cuddHashTableInsert3 + cuddHashTableLookup] + +******************************************************************************/ +int +cuddHashTableInsert( + DdHashTable * hash, + DdNodePtr * key, + DdNode * value, + ptrint count) +{ + int result; + unsigned int posn; + DdHashItem *item; + unsigned int i; + +#ifdef DD_DEBUG + assert(hash->keysize > 3); +#endif + + if (hash->size > hash->maxsize) { + result = cuddHashTableResize(hash); + if (result == 0) return(0); + } + item = cuddHashTableAlloc(hash); + if (item == NULL) return(0); + hash->size++; + item->value = value; + cuddRef(value); + item->count = count; + for (i = 0; i < hash->keysize; i++) { + item->key[i] = key[i]; + } + posn = ddLCHash(key,hash->keysize,hash->shift); + item->next = hash->bucket[posn]; + hash->bucket[posn] = item; + + return(1); + +} /* end of cuddHashTableInsert */ + + +/**Function******************************************************************** + + Synopsis [Looks up a key in a hash table.] + + Description [Looks up a key consisting of more than three pointers + in a hash table. Returns the value associated to the key if there + is an entry for the given key in the table; NULL otherwise. If the + entry is present, its reference counter is decremented if not + saturated. If the counter reaches 0, the value of the entry is + dereferenced, and the entry is returned to the free list.] + + SideEffects [None] + + SeeAlso [cuddHashTableLookup1 cuddHashTableLookup2 cuddHashTableLookup3 + cuddHashTableInsert] + +******************************************************************************/ +DdNode * +cuddHashTableLookup( + DdHashTable * hash, + DdNodePtr * key) +{ + unsigned int posn; + DdHashItem *item, *prev; + unsigned int i, keysize; + +#ifdef DD_DEBUG + assert(hash->keysize > 3); +#endif + + posn = ddLCHash(key,hash->keysize,hash->shift); + item = hash->bucket[posn]; + prev = NULL; + + keysize = hash->keysize; + while (item != NULL) { + DdNodePtr *key2 = item->key; + int equal = 1; + for (i = 0; i < keysize; i++) { + if (key[i] != key2[i]) { + equal = 0; + break; + } + } + if (equal) { + DdNode *value = item->value; + cuddSatDec(item->count); + if (item->count == 0) { + cuddDeref(value); + if (prev == NULL) { + hash->bucket[posn] = item->next; + } else { + prev->next = item->next; + } + item->next = hash->nextFree; + hash->nextFree = item; + hash->size--; + } + return(value); + } + prev = item; + item = item->next; + } + return(NULL); + +} /* end of cuddHashTableLookup */ + + +/**Function******************************************************************** + + Synopsis [Inserts an item in a hash table.] + + Description [Inserts an item in a hash table when the key is one pointer. + Returns 1 if successful; 0 otherwise.] + + SideEffects [None] + + SeeAlso [cuddHashTableInsert cuddHashTableInsert2 cuddHashTableInsert3 + cuddHashTableLookup1] + +******************************************************************************/ +int +cuddHashTableInsert1( + DdHashTable * hash, + DdNode * f, + DdNode * value, + ptrint count) +{ + int result; + unsigned int posn; + DdHashItem *item; + +#ifdef DD_DEBUG + assert(hash->keysize == 1); +#endif + + if (hash->size > hash->maxsize) { + result = cuddHashTableResize(hash); + if (result == 0) return(0); + } + item = cuddHashTableAlloc(hash); + if (item == NULL) return(0); + hash->size++; + item->value = value; + cuddRef(value); + item->count = count; + item->key[0] = f; + posn = ddLCHash1(f,hash->shift); + item->next = hash->bucket[posn]; + hash->bucket[posn] = item; + + return(1); + +} /* end of cuddHashTableInsert1 */ + + +/**Function******************************************************************** + + Synopsis [Looks up a key consisting of one pointer in a hash table.] + + Description [Looks up a key consisting of one pointer in a hash table. + Returns the value associated to the key if there is an entry for the given + key in the table; NULL otherwise. If the entry is present, its reference + counter is decremented if not saturated. If the counter reaches 0, the + value of the entry is dereferenced, and the entry is returned to the free + list.] + + SideEffects [None] + + SeeAlso [cuddHashTableLookup cuddHashTableLookup2 cuddHashTableLookup3 + cuddHashTableInsert1] + +******************************************************************************/ +DdNode * +cuddHashTableLookup1( + DdHashTable * hash, + DdNode * f) +{ + unsigned int posn; + DdHashItem *item, *prev; + +#ifdef DD_DEBUG + assert(hash->keysize == 1); +#endif + + posn = ddLCHash1(f,hash->shift); + item = hash->bucket[posn]; + prev = NULL; + + while (item != NULL) { + DdNodePtr *key = item->key; + if (f == key[0]) { + DdNode *value = item->value; + cuddSatDec(item->count); + if (item->count == 0) { + cuddDeref(value); + if (prev == NULL) { + hash->bucket[posn] = item->next; + } else { + prev->next = item->next; + } + item->next = hash->nextFree; + hash->nextFree = item; + hash->size--; + } + return(value); + } + prev = item; + item = item->next; + } + return(NULL); + +} /* end of cuddHashTableLookup1 */ + + +/**Function******************************************************************** + + Synopsis [Inserts an item in a hash table.] + + Description [Inserts an item in a hash table when the key is one + pointer and the value is not a DdNode pointer. Returns 1 if + successful; 0 otherwise.] + + SideEffects [None] + + SeeAlso [cuddHashTableInsert1 cuddHashTableGenericLookup] + +******************************************************************************/ +int +cuddHashTableGenericInsert( + DdHashTable * hash, + DdNode * f, + void * value) +{ + int result; + unsigned int posn; + DdHashItem *item; + +#ifdef DD_DEBUG + assert(hash->keysize == 1); +#endif + + if (hash->size > hash->maxsize) { + result = cuddHashTableResize(hash); + if (result == 0) return(0); + } + item = cuddHashTableAlloc(hash); + if (item == NULL) return(0); + hash->size++; + item->value = (DdNode *) value; + item->count = 0; + item->key[0] = f; + posn = ddLCHash1(f,hash->shift); + item->next = hash->bucket[posn]; + hash->bucket[posn] = item; + + return(1); + +} /* end of cuddHashTableGenericInsert */ + + +/**Function******************************************************************** + + Synopsis [Looks up a key consisting of one pointer in a hash table.] + + Description [Looks up a key consisting of one pointer in a hash + table when the value is not a DdNode pointer. Returns the value + associated to the key if there is an entry for the given key in the + table; NULL otherwise.] + + SideEffects [None] + + SeeAlso [cuddHashTableLookup1 cuddHashTableGenericInsert] + +******************************************************************************/ +void * +cuddHashTableGenericLookup( + DdHashTable * hash, + DdNode * f) +{ + unsigned int posn; + DdHashItem *item; + +#ifdef DD_DEBUG + assert(hash->keysize == 1); +#endif + + posn = ddLCHash1(f,hash->shift); + item = hash->bucket[posn]; + + while (item != NULL) { + if (f == item->key[0]) { + return ((void *) item->value); + } + item = item->next; + } + return(NULL); + +} /* end of cuddHashTableGenericLookup */ + + +/**Function******************************************************************** + + Synopsis [Inserts an item in a hash table.] + + Description [Inserts an item in a hash table when the key is + composed of two pointers. Returns 1 if successful; 0 otherwise.] + + SideEffects [None] + + SeeAlso [cuddHashTableInsert cuddHashTableInsert1 cuddHashTableInsert3 + cuddHashTableLookup2] + +******************************************************************************/ +int +cuddHashTableInsert2( + DdHashTable * hash, + DdNode * f, + DdNode * g, + DdNode * value, + ptrint count) +{ + int result; + unsigned int posn; + DdHashItem *item; + +#ifdef DD_DEBUG + assert(hash->keysize == 2); +#endif + + if (hash->size > hash->maxsize) { + result = cuddHashTableResize(hash); + if (result == 0) return(0); + } + item = cuddHashTableAlloc(hash); + if (item == NULL) return(0); + hash->size++; + item->value = value; + cuddRef(value); + item->count = count; + item->key[0] = f; + item->key[1] = g; + posn = ddLCHash2(f,g,hash->shift); + item->next = hash->bucket[posn]; + hash->bucket[posn] = item; + + return(1); + +} /* end of cuddHashTableInsert2 */ + + +/**Function******************************************************************** + + Synopsis [Looks up a key consisting of two pointers in a hash table.] + + Description [Looks up a key consisting of two pointer in a hash table. + Returns the value associated to the key if there is an entry for the given + key in the table; NULL otherwise. If the entry is present, its reference + counter is decremented if not saturated. If the counter reaches 0, the + value of the entry is dereferenced, and the entry is returned to the free + list.] + + SideEffects [None] + + SeeAlso [cuddHashTableLookup cuddHashTableLookup1 cuddHashTableLookup3 + cuddHashTableInsert2] + +******************************************************************************/ +DdNode * +cuddHashTableLookup2( + DdHashTable * hash, + DdNode * f, + DdNode * g) +{ + unsigned int posn; + DdHashItem *item, *prev; + +#ifdef DD_DEBUG + assert(hash->keysize == 2); +#endif + + posn = ddLCHash2(f,g,hash->shift); + item = hash->bucket[posn]; + prev = NULL; + + while (item != NULL) { + DdNodePtr *key = item->key; + if ((f == key[0]) && (g == key[1])) { + DdNode *value = item->value; + cuddSatDec(item->count); + if (item->count == 0) { + cuddDeref(value); + if (prev == NULL) { + hash->bucket[posn] = item->next; + } else { + prev->next = item->next; + } + item->next = hash->nextFree; + hash->nextFree = item; + hash->size--; + } + return(value); + } + prev = item; + item = item->next; + } + return(NULL); + +} /* end of cuddHashTableLookup2 */ + + +/**Function******************************************************************** + + Synopsis [Inserts an item in a hash table.] + + Description [Inserts an item in a hash table when the key is + composed of three pointers. Returns 1 if successful; 0 otherwise.] + + SideEffects [None] + + SeeAlso [cuddHashTableInsert cuddHashTableInsert1 cuddHashTableInsert2 + cuddHashTableLookup3] + +******************************************************************************/ +int +cuddHashTableInsert3( + DdHashTable * hash, + DdNode * f, + DdNode * g, + DdNode * h, + DdNode * value, + ptrint count) +{ + int result; + unsigned int posn; + DdHashItem *item; + +#ifdef DD_DEBUG + assert(hash->keysize == 3); +#endif + + if (hash->size > hash->maxsize) { + result = cuddHashTableResize(hash); + if (result == 0) return(0); + } + item = cuddHashTableAlloc(hash); + if (item == NULL) return(0); + hash->size++; + item->value = value; + cuddRef(value); + item->count = count; + item->key[0] = f; + item->key[1] = g; + item->key[2] = h; + posn = ddLCHash3(f,g,h,hash->shift); + item->next = hash->bucket[posn]; + hash->bucket[posn] = item; + + return(1); + +} /* end of cuddHashTableInsert3 */ + + +/**Function******************************************************************** + + Synopsis [Looks up a key consisting of three pointers in a hash table.] + + Description [Looks up a key consisting of three pointers in a hash table. + Returns the value associated to the key if there is an entry for the given + key in the table; NULL otherwise. If the entry is present, its reference + counter is decremented if not saturated. If the counter reaches 0, the + value of the entry is dereferenced, and the entry is returned to the free + list.] + + SideEffects [None] + + SeeAlso [cuddHashTableLookup cuddHashTableLookup1 cuddHashTableLookup2 + cuddHashTableInsert3] + +******************************************************************************/ +DdNode * +cuddHashTableLookup3( + DdHashTable * hash, + DdNode * f, + DdNode * g, + DdNode * h) +{ + unsigned int posn; + DdHashItem *item, *prev; + +#ifdef DD_DEBUG + assert(hash->keysize == 3); +#endif + + posn = ddLCHash3(f,g,h,hash->shift); + item = hash->bucket[posn]; + prev = NULL; + + while (item != NULL) { + DdNodePtr *key = item->key; + if ((f == key[0]) && (g == key[1]) && (h == key[2])) { + DdNode *value = item->value; + cuddSatDec(item->count); + if (item->count == 0) { + cuddDeref(value); + if (prev == NULL) { + hash->bucket[posn] = item->next; + } else { + prev->next = item->next; + } + item->next = hash->nextFree; + hash->nextFree = item; + hash->size--; + } + return(value); + } + prev = item; + item = item->next; + } + return(NULL); + +} /* end of cuddHashTableLookup3 */ + + +/*---------------------------------------------------------------------------*/ +/* Definition of static functions */ +/*---------------------------------------------------------------------------*/ + + +/**Function******************************************************************** + + Synopsis [Resizes a local cache.] + + Description [] + + SideEffects [None] + + SeeAlso [] + +******************************************************************************/ +static void +cuddLocalCacheResize( + DdLocalCache * cache) +{ + DdLocalCacheItem *item, *olditem, *entry, *old; + int i, shift; + unsigned int posn; + unsigned int slots, oldslots; + extern DD_OOMFP MMoutOfMemory; + DD_OOMFP saveHandler; + + olditem = cache->item; + oldslots = cache->slots; + slots = cache->slots = oldslots << 1; + +#ifdef DD_VERBOSE + (void) fprintf(cache->manager->err, + "Resizing local cache from %d to %d entries\n", + oldslots, slots); + (void) fprintf(cache->manager->err, + "\thits = %.0f\tlookups = %.0f\thit ratio = %5.3f\n", + cache->hits, cache->lookUps, cache->hits / cache->lookUps); +#endif + + saveHandler = MMoutOfMemory; + MMoutOfMemory = Cudd_OutOfMem; + cache->item = item = + (DdLocalCacheItem *) ALLOC(char, slots * cache->itemsize); + MMoutOfMemory = saveHandler; + /* If we fail to allocate the new table we just give up. */ + if (item == NULL) { +#ifdef DD_VERBOSE + (void) fprintf(cache->manager->err,"Resizing failed. Giving up.\n"); +#endif + cache->slots = oldslots; + cache->item = olditem; + /* Do not try to resize again. */ + cache->maxslots = oldslots - 1; + return; + } + shift = --(cache->shift); + cache->manager->memused += (slots - oldslots) * cache->itemsize; + + /* Clear new cache. */ + memset(item, 0, slots * cache->itemsize); + + /* Copy from old cache to new one. */ + for (i = 0; (unsigned) i < oldslots; i++) { + old = (DdLocalCacheItem *) ((char *) olditem + i * cache->itemsize); + if (old->value != NULL) { + posn = ddLCHash(old->key,cache->keysize,shift); + entry = (DdLocalCacheItem *) ((char *) item + + posn * cache->itemsize); + memcpy(entry->key,old->key,cache->keysize*sizeof(DdNode *)); + entry->value = old->value; + } + } + + FREE(olditem); + + /* Reinitialize measurements so as to avoid division by 0 and + ** immediate resizing. + */ + cache->lookUps = (double) (int) (slots * cache->minHit + 1); + cache->hits = 0; + +} /* end of cuddLocalCacheResize */ + + +/**Function******************************************************************** + + Synopsis [Computes the hash value for a local cache.] + + Description [Computes the hash value for a local cache. Returns the + bucket index.] + + SideEffects [None] + + SeeAlso [] + +******************************************************************************/ +DD_INLINE +static unsigned int +ddLCHash( + DdNodePtr * key, + unsigned int keysize, + int shift) +{ + unsigned int val = (unsigned int) (ptrint) key[0] * DD_P2; + unsigned int i; + + for (i = 1; i < keysize; i++) { + val = val * DD_P1 + (int) (ptrint) key[i]; + } + + return(val >> shift); + +} /* end of ddLCHash */ + + +/**Function******************************************************************** + + Synopsis [Inserts a local cache in the manager list.] + + Description [] + + SideEffects [None] + + SeeAlso [] + +******************************************************************************/ +static void +cuddLocalCacheAddToList( + DdLocalCache * cache) +{ + DdManager *manager = cache->manager; + + cache->next = manager->localCaches; + manager->localCaches = cache; + return; + +} /* end of cuddLocalCacheAddToList */ + + +/**Function******************************************************************** + + Synopsis [Removes a local cache from the manager list.] + + Description [] + + SideEffects [None] + + SeeAlso [] + +******************************************************************************/ +static void +cuddLocalCacheRemoveFromList( + DdLocalCache * cache) +{ + DdManager *manager = cache->manager; + DdLocalCache **prevCache, *nextCache; + + prevCache = &(manager->localCaches); + nextCache = manager->localCaches; + + while (nextCache != NULL) { + if (nextCache == cache) { + *prevCache = nextCache->next; + return; + } + prevCache = &(nextCache->next); + nextCache = nextCache->next; + } + return; /* should never get here */ + +} /* end of cuddLocalCacheRemoveFromList */ + + +/**Function******************************************************************** + + Synopsis [Resizes a hash table.] + + Description [Resizes a hash table. Returns 1 if successful; 0 + otherwise.] + + SideEffects [None] + + SeeAlso [cuddHashTableInsert] + +******************************************************************************/ +static int +cuddHashTableResize( + DdHashTable * hash) +{ + int j; + unsigned int posn; + DdHashItem *item; + DdHashItem *next; + DdNode **key; + int numBuckets; + DdHashItem **buckets; + DdHashItem **oldBuckets = hash->bucket; + int shift; + int oldNumBuckets = hash->numBuckets; + extern DD_OOMFP MMoutOfMemory; + DD_OOMFP saveHandler; + + /* Compute the new size of the table. */ + numBuckets = oldNumBuckets << 1; + saveHandler = MMoutOfMemory; + MMoutOfMemory = Cudd_OutOfMem; + buckets = ALLOC(DdHashItem *, numBuckets); + MMoutOfMemory = saveHandler; + if (buckets == NULL) { + hash->maxsize <<= 1; + return(1); + } + + hash->bucket = buckets; + hash->numBuckets = numBuckets; + shift = --(hash->shift); + hash->maxsize <<= 1; + memset(buckets, 0, numBuckets * sizeof(DdHashItem *)); + if (hash->keysize == 1) { + for (j = 0; j < oldNumBuckets; j++) { + item = oldBuckets[j]; + while (item != NULL) { + next = item->next; + key = item->key; + posn = ddLCHash2(key[0], key[0], shift); + item->next = buckets[posn]; + buckets[posn] = item; + item = next; + } + } + } else if (hash->keysize == 2) { + for (j = 0; j < oldNumBuckets; j++) { + item = oldBuckets[j]; + while (item != NULL) { + next = item->next; + key = item->key; + posn = ddLCHash2(key[0], key[1], shift); + item->next = buckets[posn]; + buckets[posn] = item; + item = next; + } + } + } else if (hash->keysize == 3) { + for (j = 0; j < oldNumBuckets; j++) { + item = oldBuckets[j]; + while (item != NULL) { + next = item->next; + key = item->key; + posn = ddLCHash3(key[0], key[1], key[2], shift); + item->next = buckets[posn]; + buckets[posn] = item; + item = next; + } + } + } else { + for (j = 0; j < oldNumBuckets; j++) { + item = oldBuckets[j]; + while (item != NULL) { + next = item->next; + posn = ddLCHash(item->key, hash->keysize, shift); + item->next = buckets[posn]; + buckets[posn] = item; + item = next; + } + } + } + FREE(oldBuckets); + return(1); + +} /* end of cuddHashTableResize */ + + +/**Function******************************************************************** + + Synopsis [Fast storage allocation for items in a hash table.] + + Description [Fast storage allocation for items in a hash table. The + first 4 bytes of a chunk contain a pointer to the next block; the + rest contains DD_MEM_CHUNK spaces for hash items. Returns a pointer to + a new item if successful; NULL is memory is full.] + + SideEffects [None] + + SeeAlso [cuddAllocNode cuddDynamicAllocNode] + +******************************************************************************/ +DD_INLINE +static DdHashItem * +cuddHashTableAlloc( + DdHashTable * hash) +{ +int i; +unsigned int itemsize = hash->itemsize; +extern DD_OOMFP MMoutOfMemory; +DD_OOMFP saveHandler; +DdHashItem **mem, *thisOne, *next, *item; + +if (hash->nextFree == NULL) { +saveHandler = MMoutOfMemory; +MMoutOfMemory = Cudd_OutOfMem; +mem = (DdHashItem **) ALLOC(char,(DD_MEM_CHUNK+1) * itemsize); +MMoutOfMemory = saveHandler; +if (mem == NULL) { +if (hash->manager->stash != NULL) { +FREE(hash->manager->stash); +hash->manager->stash = NULL; +/* Inhibit resizing of tables. */ +hash->manager->maxCacheHard = hash->manager->cacheSlots - 1; +hash->manager->cacheSlack = - (int) (hash->manager->cacheSlots + 1); +for (i = 0; i < hash->manager->size; i++) { +hash->manager->subtables[i].maxKeys <<= 2; +} +hash->manager->gcFrac = 0.2; +hash->manager->minDead = +(unsigned) (0.2 * (double) hash->manager->slots); +mem = (DdHashItem **) ALLOC(char,(DD_MEM_CHUNK+1) * itemsize); +} +if (mem == NULL) { +(*MMoutOfMemory)((long)((DD_MEM_CHUNK + 1) * itemsize)); +hash->manager->errorCode = CUDD_MEMORY_OUT; +return(NULL); +} +} + +mem[0] = (DdHashItem *) hash->memoryList; +hash->memoryList = mem; + +thisOne = (DdHashItem *) ((char *) mem + itemsize); +hash->nextFree = thisOne; +for (i = 1; i < DD_MEM_CHUNK; i++) { +next = (DdHashItem *) ((char *) thisOne + itemsize); +thisOne->next = next; +thisOne = next; +} + +thisOne->next = NULL; + +} +item = hash->nextFree; +hash->nextFree = item->next; +return(item); + +} /* end of cuddHashTableAlloc */ +/**CFile*********************************************************************** + + FileName [cuddLinear.c] + + PackageName [cudd] + + Synopsis [Functions for DD reduction by linear transformations.] + + Description [ Internal procedures included in this module: +
        +
      • cuddLinearAndSifting() +
      • cuddLinearInPlace() +
      • cuddUpdateInteractionMatrix() +
      • cuddInitLinear() +
      • cuddResizeLinear() +
      + Static procedures included in this module: +
        +
      • ddLinearUniqueCompare() +
      • ddLinearAndSiftingAux() +
      • ddLinearAndSiftingUp() +
      • ddLinearAndSiftingDown() +
      • ddLinearAndSiftingBackward() +
      • ddUndoMoves() +
      • cuddXorLinear() +
      ] + + Author [Fabio Somenzi] + + Copyright [Copyright (c) 1995-2012, Regents of the University of Colorado + + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + Neither the name of the University of Colorado nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE.] + +******************************************************************************/ + + +/*---------------------------------------------------------------------------*/ +/* Constant declarations */ +/*---------------------------------------------------------------------------*/ + +#define CUDD_SWAP_MOVE 0 +#define CUDD_LINEAR_TRANSFORM_MOVE 1 +#define CUDD_INVERSE_TRANSFORM_MOVE 2 +#if SIZEOF_LONG == 8 +#define BPL 64 +#define LOGBPL 6 +#else +#define BPL 32 +#define LOGBPL 5 +#endif + +/*---------------------------------------------------------------------------*/ +/* Stucture declarations */ +/*---------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------*/ +/* Type declarations */ +/*---------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------*/ +/* Variable declarations */ +/*---------------------------------------------------------------------------*/ + +//#ifndef lint +//static char rcsid[] DD_UNUSED = "$Id: cuddLinear.c,v 1.29 2012/02/05 01:07:19 fabio Exp $"; +//#endif + +static int *entry; + +#ifdef DD_STATS +extern int ddTotalNumberSwapping; +extern int ddTotalNISwaps; +static int ddTotalNumberLinearTr; +#endif + +#ifdef DD_DEBUG +static int zero = 0; +#endif + +/*---------------------------------------------------------------------------*/ +/* Macro declarations */ +/*---------------------------------------------------------------------------*/ + +/**AutomaticStart*************************************************************/ + +/*---------------------------------------------------------------------------*/ +/* Static function prototypes */ +/*---------------------------------------------------------------------------*/ + +static int ddLinearUniqueCompare (int *ptrX, int *ptrY); +static int ddLinearAndSiftingAux (DdManager *table, int x, int xLow, int xHigh); +static Move * ddLinearAndSiftingUp (DdManager *table, int y, int xLow, Move *prevMoves); +static Move * ddLinearAndSiftingDown (DdManager *table, int x, int xHigh, Move *prevMoves); +static int ddLinearAndSiftingBackward (DdManager *table, int size, Move *moves); +static Move* ddUndoMoves (DdManager *table, Move *moves); +static void cuddXorLinear (DdManager *table, int x, int y); + +/**AutomaticEnd***************************************************************/ + + +/*---------------------------------------------------------------------------*/ +/* Definition of exported functions */ +/*---------------------------------------------------------------------------*/ + + +/**Function******************************************************************** + + Synopsis [Prints the linear transform matrix.] + + Description [Prints the linear transform matrix. Returns 1 in case of + success; 0 otherwise.] + + SideEffects [none] + + SeeAlso [] + +******************************************************************************/ +int +Cudd_PrintLinear( + DdManager * table) +{ + int i,j,k; + int retval; + int nvars = table->linearSize; + int wordsPerRow = ((nvars - 1) >> LOGBPL) + 1; + long word; + + for (i = 0; i < nvars; i++) { + for (j = 0; j < wordsPerRow; j++) { + word = table->linear[i*wordsPerRow + j]; + for (k = 0; k < BPL; k++) { + retval = fprintf(table->out,"%ld",word & 1); + if (retval == 0) return(0); + word >>= 1; + } + } + retval = fprintf(table->out,"\n"); + if (retval == 0) return(0); + } + return(1); + +} /* end of Cudd_PrintLinear */ + + + +/*---------------------------------------------------------------------------*/ +/* Definition of internal functions */ +/*---------------------------------------------------------------------------*/ + + +/**Function******************************************************************** + + Synopsis [BDD reduction based on combination of sifting and linear + transformations.] + + Description [BDD reduction based on combination of sifting and linear + transformations. Assumes that no dead nodes are present. +
        +
      1. Order all the variables according to the number of entries + in each unique table. +
      2. Sift the variable up and down, remembering each time the + total size of the DD heap. At each position, linear transformation + of the two adjacent variables is tried and is accepted if it reduces + the size of the DD. +
      3. Select the best permutation. +
      4. Repeat 3 and 4 for all variables. +
      + Returns 1 if successful; 0 otherwise.] + + SideEffects [None] + +******************************************************************************/ +int +cuddLinearAndSifting( + DdManager * table, + int lower, + int upper) +{ + int i; + int *var; + int size; + int x; + int result; +#ifdef DD_STATS + int previousSize; +#endif + +#ifdef DD_STATS + ddTotalNumberLinearTr = 0; +#endif + + size = table->size; + + var = NULL; + entry = NULL; + if (table->linear == NULL) { + result = cuddInitLinear(table); + if (result == 0) goto cuddLinearAndSiftingOutOfMem; +#if 0 + (void) fprintf(table->out,"\n"); + result = Cudd_PrintLinear(table); + if (result == 0) goto cuddLinearAndSiftingOutOfMem; +#endif + } else if (table->size != table->linearSize) { + result = cuddResizeLinear(table); + if (result == 0) goto cuddLinearAndSiftingOutOfMem; +#if 0 + (void) fprintf(table->out,"\n"); + result = Cudd_PrintLinear(table); + if (result == 0) goto cuddLinearAndSiftingOutOfMem; +#endif + } + + /* Find order in which to sift variables. */ + entry = ALLOC(int,size); + if (entry == NULL) { + table->errorCode = CUDD_MEMORY_OUT; + goto cuddLinearAndSiftingOutOfMem; + } + var = ALLOC(int,size); + if (var == NULL) { + table->errorCode = CUDD_MEMORY_OUT; + goto cuddLinearAndSiftingOutOfMem; + } + + for (i = 0; i < size; i++) { + x = table->perm[i]; + entry[i] = table->subtables[x].keys; + var[i] = i; + } + + qsort((void *)var,size,sizeof(int),(DD_QSFP)ddLinearUniqueCompare); + + /* Now sift. */ + for (i = 0; i < ddMin(table->siftMaxVar,size); i++) { + x = table->perm[var[i]]; + if (x < lower || x > upper) continue; +#ifdef DD_STATS + previousSize = table->keys - table->isolated; +#endif + result = ddLinearAndSiftingAux(table,x,lower,upper); + if (!result) goto cuddLinearAndSiftingOutOfMem; +#ifdef DD_STATS + if (table->keys < (unsigned) previousSize + table->isolated) { + (void) fprintf(table->out,"-"); + } else if (table->keys > (unsigned) previousSize + table->isolated) { + (void) fprintf(table->out,"+"); /* should never happen */ + (void) fprintf(table->out,"\nSize increased from %d to %d while sifting variable %d\n", previousSize, table->keys - table->isolated, var[i]); + } else { + (void) fprintf(table->out,"="); + } + fflush(table->out); +#endif +#ifdef DD_DEBUG + (void) Cudd_DebugCheck(table); +#endif + } + + FREE(var); + FREE(entry); + +#ifdef DD_STATS + (void) fprintf(table->out,"\n#:L_LINSIFT %8d: linear trans.", + ddTotalNumberLinearTr); +#endif + + return(1); + + cuddLinearAndSiftingOutOfMem: + + if (entry != NULL) FREE(entry); + if (var != NULL) FREE(var); + + return(0); + +} /* end of cuddLinearAndSifting */ + + +/**Function******************************************************************** + + Synopsis [Linearly combines two adjacent variables.] + + Description [Linearly combines two adjacent variables. Specifically, + replaces the top variable with the exclusive nor of the two variables. + It assumes that no dead nodes are present on entry to this + procedure. The procedure then guarantees that no dead nodes will be + present when it terminates. cuddLinearInPlace assumes that x < + y. Returns the number of keys in the table if successful; 0 + otherwise.] + + SideEffects [The two subtables corrresponding to variables x and y are + modified. The global counters of the unique table are also affected.] + + SeeAlso [cuddSwapInPlace] + +******************************************************************************/ +int +cuddLinearInPlace( + DdManager * table, + int x, + int y) +{ + DdNodePtr *xlist, *ylist; + int xindex, yindex; + int xslots, yslots; + int xshift, yshift; + int oldxkeys, oldykeys; + int newxkeys, newykeys; + int comple, newcomplement; + int i; + int posn; + int isolated; + DdNode *f,*f0,*f1,*f01,*f00,*f11,*f10,*newf1,*newf0; + DdNode *g,*next,*last; + DdNodePtr *previousP; + DdNode *tmp; + DdNode *sentinel = &(table->sentinel); +#ifdef DD_DEBUG + int count, idcheck; +#endif + +#ifdef DD_DEBUG + assert(x < y); + assert(cuddNextHigh(table,x) == y); + assert(table->subtables[x].keys != 0); + assert(table->subtables[y].keys != 0); + assert(table->subtables[x].dead == 0); + assert(table->subtables[y].dead == 0); +#endif + + xindex = table->invperm[x]; + yindex = table->invperm[y]; + + if (cuddTestInteract(table,xindex,yindex)) { +#ifdef DD_STATS + ddTotalNumberLinearTr++; +#endif + /* Get parameters of x subtable. */ + xlist = table->subtables[x].nodelist; + oldxkeys = table->subtables[x].keys; + xslots = table->subtables[x].slots; + xshift = table->subtables[x].shift; + + /* Get parameters of y subtable. */ + ylist = table->subtables[y].nodelist; + oldykeys = table->subtables[y].keys; + yslots = table->subtables[y].slots; + yshift = table->subtables[y].shift; + + newxkeys = 0; + newykeys = oldykeys; + + /* Check whether the two projection functions involved in this + ** swap are isolated. At the end, we'll be able to tell how many + ** isolated projection functions are there by checking only these + ** two functions again. This is done to eliminate the isolated + ** projection functions from the node count. + */ + isolated = - ((table->vars[xindex]->ref == 1) + + (table->vars[yindex]->ref == 1)); + + /* The nodes in the x layer are put in a chain. + ** The chain is handled as a FIFO; g points to the beginning and + ** last points to the end. + */ + g = NULL; +#ifdef DD_DEBUG + last = NULL; +#endif + for (i = 0; i < xslots; i++) { + f = xlist[i]; + if (f == sentinel) continue; + xlist[i] = sentinel; + if (g == NULL) { + g = f; + } else { + last->next = f; + } + while ((next = f->next) != sentinel) { + f = next; + } /* while there are elements in the collision chain */ + last = f; + } /* for each slot of the x subtable */ +#ifdef DD_DEBUG + /* last is always assigned in the for loop because there is at + ** least one key */ + assert(last != NULL); +#endif + last->next = NULL; + +#ifdef DD_COUNT + table->swapSteps += oldxkeys; +#endif + /* Take care of the x nodes that must be re-expressed. + ** They form a linked list pointed by g. + */ + f = g; + while (f != NULL) { + next = f->next; + /* Find f1, f0, f11, f10, f01, f00. */ + f1 = cuddT(f); +#ifdef DD_DEBUG + assert(!(Cudd_IsComplement(f1))); +#endif + if ((int) f1->index == yindex) { + f11 = cuddT(f1); f10 = cuddE(f1); + } else { + f11 = f10 = f1; + } +#ifdef DD_DEBUG + assert(!(Cudd_IsComplement(f11))); +#endif + f0 = cuddE(f); + comple = Cudd_IsComplement(f0); + f0 = Cudd_Regular(f0); + if ((int) f0->index == yindex) { + f01 = cuddT(f0); f00 = cuddE(f0); + } else { + f01 = f00 = f0; + } + if (comple) { + f01 = Cudd_Not(f01); + f00 = Cudd_Not(f00); + } + /* Decrease ref count of f1. */ + cuddSatDec(f1->ref); + /* Create the new T child. */ + if (f11 == f00) { + newf1 = f11; + cuddSatInc(newf1->ref); + } else { + /* Check ylist for triple (yindex,f11,f00). */ + posn = ddHash(f11, f00, yshift); + /* For each element newf1 in collision list ylist[posn]. */ + previousP = &(ylist[posn]); + newf1 = *previousP; + while (f11 < cuddT(newf1)) { + previousP = &(newf1->next); + newf1 = *previousP; + } + while (f11 == cuddT(newf1) && f00 < cuddE(newf1)) { + previousP = &(newf1->next); + newf1 = *previousP; + } + if (cuddT(newf1) == f11 && cuddE(newf1) == f00) { + cuddSatInc(newf1->ref); + } else { /* no match */ + newf1 = cuddDynamicAllocNode(table); + if (newf1 == NULL) + goto cuddLinearOutOfMem; + newf1->index = yindex; newf1->ref = 1; + cuddT(newf1) = f11; + cuddE(newf1) = f00; + /* Insert newf1 in the collision list ylist[posn]; + ** increase the ref counts of f11 and f00. + */ + newykeys++; + newf1->next = *previousP; + *previousP = newf1; + cuddSatInc(f11->ref); + tmp = Cudd_Regular(f00); + cuddSatInc(tmp->ref); + } + } + cuddT(f) = newf1; +#ifdef DD_DEBUG + assert(!(Cudd_IsComplement(newf1))); +#endif + + /* Do the same for f0, keeping complement dots into account. */ + /* decrease ref count of f0 */ + tmp = Cudd_Regular(f0); + cuddSatDec(tmp->ref); + /* create the new E child */ + if (f01 == f10) { + newf0 = f01; + tmp = Cudd_Regular(newf0); + cuddSatInc(tmp->ref); + } else { + /* make sure f01 is regular */ + newcomplement = Cudd_IsComplement(f01); + if (newcomplement) { + f01 = Cudd_Not(f01); + f10 = Cudd_Not(f10); + } + /* Check ylist for triple (yindex,f01,f10). */ + posn = ddHash(f01, f10, yshift); + /* For each element newf0 in collision list ylist[posn]. */ + previousP = &(ylist[posn]); + newf0 = *previousP; + while (f01 < cuddT(newf0)) { + previousP = &(newf0->next); + newf0 = *previousP; + } + while (f01 == cuddT(newf0) && f10 < cuddE(newf0)) { + previousP = &(newf0->next); + newf0 = *previousP; + } + if (cuddT(newf0) == f01 && cuddE(newf0) == f10) { + cuddSatInc(newf0->ref); + } else { /* no match */ + newf0 = cuddDynamicAllocNode(table); + if (newf0 == NULL) + goto cuddLinearOutOfMem; + newf0->index = yindex; newf0->ref = 1; + cuddT(newf0) = f01; + cuddE(newf0) = f10; + /* Insert newf0 in the collision list ylist[posn]; + ** increase the ref counts of f01 and f10. + */ + newykeys++; + newf0->next = *previousP; + *previousP = newf0; + cuddSatInc(f01->ref); + tmp = Cudd_Regular(f10); + cuddSatInc(tmp->ref); + } + if (newcomplement) { + newf0 = Cudd_Not(newf0); + } + } + cuddE(f) = newf0; + + /* Re-insert the modified f in xlist. + ** The modified f does not already exists in xlist. + ** (Because of the uniqueness of the cofactors.) + */ + posn = ddHash(newf1, newf0, xshift); + newxkeys++; + previousP = &(xlist[posn]); + tmp = *previousP; + while (newf1 < cuddT(tmp)) { + previousP = &(tmp->next); + tmp = *previousP; + } + while (newf1 == cuddT(tmp) && newf0 < cuddE(tmp)) { + previousP = &(tmp->next); + tmp = *previousP; + } + f->next = *previousP; + *previousP = f; + f = next; + } /* while f != NULL */ + + /* GC the y layer. */ + + /* For each node f in ylist. */ + for (i = 0; i < yslots; i++) { + previousP = &(ylist[i]); + f = *previousP; + while (f != sentinel) { + next = f->next; + if (f->ref == 0) { + tmp = cuddT(f); + cuddSatDec(tmp->ref); + tmp = Cudd_Regular(cuddE(f)); + cuddSatDec(tmp->ref); + cuddDeallocNode(table,f); + newykeys--; + } else { + *previousP = f; + previousP = &(f->next); + } + f = next; + } /* while f */ + *previousP = sentinel; + } /* for every collision list */ + +#ifdef DD_DEBUG + #if 0 + (void) fprintf(table->out,"Linearly combining %d and %d\n",x,y); +#endif + count = 0; + idcheck = 0; + for (i = 0; i < yslots; i++) { + f = ylist[i]; + while (f != sentinel) { + count++; + if (f->index != (DdHalfWord) yindex) + idcheck++; + f = f->next; + } + } + if (count != newykeys) { + fprintf(table->err,"Error in finding newykeys\toldykeys = %d\tnewykeys = %d\tactual = %d\n",oldykeys,newykeys,count); + } + if (idcheck != 0) + fprintf(table->err,"Error in id's of ylist\twrong id's = %d\n",idcheck); + count = 0; + idcheck = 0; + for (i = 0; i < xslots; i++) { + f = xlist[i]; + while (f != sentinel) { + count++; + if (f->index != (DdHalfWord) xindex) + idcheck++; + f = f->next; + } + } + if (count != newxkeys || newxkeys != oldxkeys) { + fprintf(table->err,"Error in finding newxkeys\toldxkeys = %d \tnewxkeys = %d \tactual = %d\n",oldxkeys,newxkeys,count); + } + if (idcheck != 0) + fprintf(table->err,"Error in id's of xlist\twrong id's = %d\n",idcheck); +#endif + + isolated += (table->vars[xindex]->ref == 1) + + (table->vars[yindex]->ref == 1); + table->isolated += isolated; + + /* Set the appropriate fields in table. */ + table->subtables[y].keys = newykeys; + + /* Here we should update the linear combination table + ** to record that x <- x EXNOR y. This is done by complementing + ** the (x,y) entry of the table. + */ + + table->keys += newykeys - oldykeys; + + cuddXorLinear(table,xindex,yindex); + } + +#ifdef DD_DEBUG + if (zero) { + (void) Cudd_DebugCheck(table); + } +#endif + + return(table->keys - table->isolated); + + cuddLinearOutOfMem: + (void) fprintf(table->err,"Error: cuddLinearInPlace out of memory\n"); + + return (0); + +} /* end of cuddLinearInPlace */ + + +/**Function******************************************************************** + + Synopsis [Updates the interaction matrix.] + + Description [] + + SideEffects [none] + + SeeAlso [] + +******************************************************************************/ +void +cuddUpdateInteractionMatrix( + DdManager * table, + int xindex, + int yindex) +{ + int i; + for (i = 0; i < yindex; i++) { + if (i != xindex && cuddTestInteract(table,i,yindex)) { + if (i < xindex) { + cuddSetInteract(table,i,xindex); + } else { + cuddSetInteract(table,xindex,i); + } + } + } + for (i = yindex+1; i < table->size; i++) { + if (i != xindex && cuddTestInteract(table,yindex,i)) { + if (i < xindex) { + cuddSetInteract(table,i,xindex); + } else { + cuddSetInteract(table,xindex,i); + } + } + } + +} /* end of cuddUpdateInteractionMatrix */ + + +/**Function******************************************************************** + + Synopsis [Initializes the linear transform matrix.] + + Description [Initializes the linear transform matrix. Returns 1 if + successful; 0 otherwise.] + + SideEffects [none] + + SeeAlso [] + +******************************************************************************/ +int +cuddInitLinear( + DdManager * table) +{ + int words; + int wordsPerRow; + int nvars; + int word; + int bit; + int i; + long *linear; + + nvars = table->size; + wordsPerRow = ((nvars - 1) >> LOGBPL) + 1; + words = wordsPerRow * nvars; + table->linear = linear = ALLOC(long,words); + if (linear == NULL) { + table->errorCode = CUDD_MEMORY_OUT; + return(0); + } + table->memused += words * sizeof(long); + table->linearSize = nvars; + for (i = 0; i < words; i++) linear[i] = 0; + for (i = 0; i < nvars; i++) { + word = wordsPerRow * i + (i >> LOGBPL); + bit = i & (BPL-1); + linear[word] = 1 << bit; + } + return(1); + +} /* end of cuddInitLinear */ + + +/**Function******************************************************************** + + Synopsis [Resizes the linear transform matrix.] + + Description [Resizes the linear transform matrix. Returns 1 if + successful; 0 otherwise.] + + SideEffects [none] + + SeeAlso [] + +******************************************************************************/ +int +cuddResizeLinear( + DdManager * table) +{ + int words,oldWords; + int wordsPerRow,oldWordsPerRow; + int nvars,oldNvars; + int word,oldWord; + int bit; + int i,j; + long *linear,*oldLinear; + + oldNvars = table->linearSize; + oldWordsPerRow = ((oldNvars - 1) >> LOGBPL) + 1; + oldWords = oldWordsPerRow * oldNvars; + oldLinear = table->linear; + + nvars = table->size; + wordsPerRow = ((nvars - 1) >> LOGBPL) + 1; + words = wordsPerRow * nvars; + table->linear = linear = ALLOC(long,words); + if (linear == NULL) { + table->errorCode = CUDD_MEMORY_OUT; + return(0); + } + table->memused += (words - oldWords) * sizeof(long); + for (i = 0; i < words; i++) linear[i] = 0; + + /* Copy old matrix. */ + for (i = 0; i < oldNvars; i++) { + for (j = 0; j < oldWordsPerRow; j++) { + oldWord = oldWordsPerRow * i + j; + word = wordsPerRow * i + j; + linear[word] = oldLinear[oldWord]; + } + } + FREE(oldLinear); + + /* Add elements to the diagonal. */ + for (i = oldNvars; i < nvars; i++) { + word = wordsPerRow * i + (i >> LOGBPL); + bit = i & (BPL-1); + linear[word] = 1 << bit; + } + table->linearSize = nvars; + + return(1); + +} /* end of cuddResizeLinear */ + + +/*---------------------------------------------------------------------------*/ +/* Definition of static functions */ +/*---------------------------------------------------------------------------*/ + + +/**Function******************************************************************** + + Synopsis [Comparison function used by qsort.] + + Description [Comparison function used by qsort to order the + variables according to the number of keys in the subtables. + Returns the difference in number of keys between the two + variables being compared.] + + SideEffects [None] + +******************************************************************************/ +static int +ddLinearUniqueCompare( + int * ptrX, + int * ptrY) +{ +#if 0 + if (entry[*ptrY] == entry[*ptrX]) { + return((*ptrX) - (*ptrY)); + } +#endif + return(entry[*ptrY] - entry[*ptrX]); + +} /* end of ddLinearUniqueCompare */ + + +/**Function******************************************************************** + + Synopsis [Given xLow <= x <= xHigh moves x up and down between the + boundaries.] + + Description [Given xLow <= x <= xHigh moves x up and down between the + boundaries. At each step a linear transformation is tried, and, if it + decreases the size of the DD, it is accepted. Finds the best position + and does the required changes. Returns 1 if successful; 0 otherwise.] + + SideEffects [None] + +******************************************************************************/ +static int +ddLinearAndSiftingAux( + DdManager * table, + int x, + int xLow, + int xHigh) +{ + + Move *move; + Move *moveUp; /* list of up moves */ + Move *moveDown; /* list of down moves */ + int initialSize; + int result; + + initialSize = table->keys - table->isolated; + + moveDown = NULL; + moveUp = NULL; + + if (x == xLow) { + moveDown = ddLinearAndSiftingDown(table,x,xHigh,NULL); + /* At this point x --> xHigh unless bounding occurred. */ + if (moveDown == (Move *) CUDD_OUT_OF_MEM) goto ddLinearAndSiftingAuxOutOfMem; + /* Move backward and stop at best position. */ + result = ddLinearAndSiftingBackward(table,initialSize,moveDown); + if (!result) goto ddLinearAndSiftingAuxOutOfMem; + + } else if (x == xHigh) { + moveUp = ddLinearAndSiftingUp(table,x,xLow,NULL); + /* At this point x --> xLow unless bounding occurred. */ + if (moveUp == (Move *) CUDD_OUT_OF_MEM) goto ddLinearAndSiftingAuxOutOfMem; + /* Move backward and stop at best position. */ + result = ddLinearAndSiftingBackward(table,initialSize,moveUp); + if (!result) goto ddLinearAndSiftingAuxOutOfMem; + + } else if ((x - xLow) > (xHigh - x)) { /* must go down first: shorter */ + moveDown = ddLinearAndSiftingDown(table,x,xHigh,NULL); + /* At this point x --> xHigh unless bounding occurred. */ + if (moveDown == (Move *) CUDD_OUT_OF_MEM) goto ddLinearAndSiftingAuxOutOfMem; + moveUp = ddUndoMoves(table,moveDown); +#ifdef DD_DEBUG + assert(moveUp == NULL || moveUp->x == x); +#endif + moveUp = ddLinearAndSiftingUp(table,x,xLow,moveUp); + if (moveUp == (Move *) CUDD_OUT_OF_MEM) goto ddLinearAndSiftingAuxOutOfMem; + /* Move backward and stop at best position. */ + result = ddLinearAndSiftingBackward(table,initialSize,moveUp); + if (!result) goto ddLinearAndSiftingAuxOutOfMem; + + } else { /* must go up first: shorter */ + moveUp = ddLinearAndSiftingUp(table,x,xLow,NULL); + /* At this point x --> xLow unless bounding occurred. */ + if (moveUp == (Move *) CUDD_OUT_OF_MEM) goto ddLinearAndSiftingAuxOutOfMem; + moveDown = ddUndoMoves(table,moveUp); +#ifdef DD_DEBUG + assert(moveDown == NULL || moveDown->y == x); +#endif + moveDown = ddLinearAndSiftingDown(table,x,xHigh,moveDown); + if (moveDown == (Move *) CUDD_OUT_OF_MEM) goto ddLinearAndSiftingAuxOutOfMem; + /* Move backward and stop at best position. */ + result = ddLinearAndSiftingBackward(table,initialSize,moveDown); + if (!result) goto ddLinearAndSiftingAuxOutOfMem; + } + + while (moveDown != NULL) { + move = moveDown->next; + cuddDeallocMove(table, moveDown); + moveDown = move; + } + while (moveUp != NULL) { + move = moveUp->next; + cuddDeallocMove(table, moveUp); + moveUp = move; + } + + return(1); + + ddLinearAndSiftingAuxOutOfMem: + while (moveDown != NULL) { + move = moveDown->next; + cuddDeallocMove(table, moveDown); + moveDown = move; + } + while (moveUp != NULL) { + move = moveUp->next; + cuddDeallocMove(table, moveUp); + moveUp = move; + } + + return(0); + +} /* end of ddLinearAndSiftingAux */ + + +/**Function******************************************************************** + + Synopsis [Sifts a variable up and applies linear transformations.] + + Description [Sifts a variable up and applies linear transformations. + Moves y up until either it reaches the bound (xLow) or the size of + the DD heap increases too much. Returns the set of moves in case of + success; NULL if memory is full.] + + SideEffects [None] + +******************************************************************************/ +static Move * +ddLinearAndSiftingUp( + DdManager * table, + int y, + int xLow, + Move * prevMoves) +{ + Move *moves; + Move *move; + int x; + int size, newsize; + int limitSize; + int xindex, yindex; + int isolated; + int L; /* lower bound on DD size */ +#ifdef DD_DEBUG + int checkL; + int z; + int zindex; +#endif + + moves = prevMoves; + yindex = table->invperm[y]; + + /* Initialize the lower bound. + ** The part of the DD below y will not change. + ** The part of the DD above y that does not interact with y will not + ** change. The rest may vanish in the best case, except for + ** the nodes at level xLow, which will not vanish, regardless. + */ + limitSize = L = table->keys - table->isolated; + for (x = xLow + 1; x < y; x++) { + xindex = table->invperm[x]; + if (cuddTestInteract(table,xindex,yindex)) { + isolated = table->vars[xindex]->ref == 1; + L -= table->subtables[x].keys - isolated; + } + } + isolated = table->vars[yindex]->ref == 1; + L -= table->subtables[y].keys - isolated; + + x = cuddNextLow(table,y); + while (x >= xLow && L <= limitSize) { + xindex = table->invperm[x]; +#ifdef DD_DEBUG + checkL = table->keys - table->isolated; + for (z = xLow + 1; z < y; z++) { + zindex = table->invperm[z]; + if (cuddTestInteract(table,zindex,yindex)) { + isolated = table->vars[zindex]->ref == 1; + checkL -= table->subtables[z].keys - isolated; + } + } + isolated = table->vars[yindex]->ref == 1; + checkL -= table->subtables[y].keys - isolated; + if (L != checkL) { + (void) fprintf(table->out, "checkL(%d) != L(%d)\n",checkL,L); + } +#endif + size = cuddSwapInPlace(table,x,y); + if (size == 0) goto ddLinearAndSiftingUpOutOfMem; + newsize = cuddLinearInPlace(table,x,y); + if (newsize == 0) goto ddLinearAndSiftingUpOutOfMem; + move = (Move *) cuddDynamicAllocNode(table); + if (move == NULL) goto ddLinearAndSiftingUpOutOfMem; + move->x = x; + move->y = y; + move->next = moves; + moves = move; + move->flags = CUDD_SWAP_MOVE; + if (newsize >= size) { + /* Undo transformation. The transformation we apply is + ** its own inverse. Hence, we just apply the transformation + ** again. + */ + newsize = cuddLinearInPlace(table,x,y); + if (newsize == 0) goto ddLinearAndSiftingUpOutOfMem; +#ifdef DD_DEBUG + if (newsize != size) { + (void) fprintf(table->out,"Change in size after identity transformation! From %d to %d\n",size,newsize); + } +#endif + } else if (cuddTestInteract(table,xindex,yindex)) { + size = newsize; + move->flags = CUDD_LINEAR_TRANSFORM_MOVE; + cuddUpdateInteractionMatrix(table,xindex,yindex); + } + move->size = size; + /* Update the lower bound. */ + if (cuddTestInteract(table,xindex,yindex)) { + isolated = table->vars[xindex]->ref == 1; + L += table->subtables[y].keys - isolated; + } + if ((double) size > (double) limitSize * table->maxGrowth) break; + if (size < limitSize) limitSize = size; + y = x; + x = cuddNextLow(table,y); + } + return(moves); + + ddLinearAndSiftingUpOutOfMem: + while (moves != NULL) { + move = moves->next; + cuddDeallocMove(table, moves); + moves = move; + } + return((Move *) CUDD_OUT_OF_MEM); + +} /* end of ddLinearAndSiftingUp */ + + +/**Function******************************************************************** + + Synopsis [Sifts a variable down and applies linear transformations.] + + Description [Sifts a variable down and applies linear + transformations. Moves x down until either it reaches the bound + (xHigh) or the size of the DD heap increases too much. Returns the + set of moves in case of success; NULL if memory is full.] + + SideEffects [None] + +******************************************************************************/ +static Move * +ddLinearAndSiftingDown( + DdManager * table, + int x, + int xHigh, + Move * prevMoves) +{ + Move *moves; + Move *move; + int y; + int size, newsize; + int R; /* upper bound on node decrease */ + int limitSize; + int xindex, yindex; + int isolated; +#ifdef DD_DEBUG + int checkR; + int z; + int zindex; +#endif + + moves = prevMoves; + /* Initialize R */ + xindex = table->invperm[x]; + limitSize = size = table->keys - table->isolated; + R = 0; + for (y = xHigh; y > x; y--) { + yindex = table->invperm[y]; + if (cuddTestInteract(table,xindex,yindex)) { + isolated = table->vars[yindex]->ref == 1; + R += table->subtables[y].keys - isolated; + } + } + + y = cuddNextHigh(table,x); + while (y <= xHigh && size - R < limitSize) { +#ifdef DD_DEBUG + checkR = 0; + for (z = xHigh; z > x; z--) { + zindex = table->invperm[z]; + if (cuddTestInteract(table,xindex,zindex)) { + isolated = table->vars[zindex]->ref == 1; + checkR += table->subtables[z].keys - isolated; + } + } + if (R != checkR) { + (void) fprintf(table->out, "checkR(%d) != R(%d)\n",checkR,R); + } +#endif + /* Update upper bound on node decrease. */ + yindex = table->invperm[y]; + if (cuddTestInteract(table,xindex,yindex)) { + isolated = table->vars[yindex]->ref == 1; + R -= table->subtables[y].keys - isolated; + } + size = cuddSwapInPlace(table,x,y); + if (size == 0) goto ddLinearAndSiftingDownOutOfMem; + newsize = cuddLinearInPlace(table,x,y); + if (newsize == 0) goto ddLinearAndSiftingDownOutOfMem; + move = (Move *) cuddDynamicAllocNode(table); + if (move == NULL) goto ddLinearAndSiftingDownOutOfMem; + move->x = x; + move->y = y; + move->next = moves; + moves = move; + move->flags = CUDD_SWAP_MOVE; + if (newsize >= size) { + /* Undo transformation. The transformation we apply is + ** its own inverse. Hence, we just apply the transformation + ** again. + */ + newsize = cuddLinearInPlace(table,x,y); + if (newsize == 0) goto ddLinearAndSiftingDownOutOfMem; + if (newsize != size) { + (void) fprintf(table->out,"Change in size after identity transformation! From %d to %d\n",size,newsize); + } + } else if (cuddTestInteract(table,xindex,yindex)) { + size = newsize; + move->flags = CUDD_LINEAR_TRANSFORM_MOVE; + cuddUpdateInteractionMatrix(table,xindex,yindex); + } + move->size = size; + if ((double) size > (double) limitSize * table->maxGrowth) break; + if (size < limitSize) limitSize = size; + x = y; + y = cuddNextHigh(table,x); + } + return(moves); + + ddLinearAndSiftingDownOutOfMem: + while (moves != NULL) { + move = moves->next; + cuddDeallocMove(table, moves); + moves = move; + } + return((Move *) CUDD_OUT_OF_MEM); + +} /* end of ddLinearAndSiftingDown */ + + +/**Function******************************************************************** + + Synopsis [Given a set of moves, returns the DD heap to the order + giving the minimum size.] + + Description [Given a set of moves, returns the DD heap to the + position giving the minimum size. In case of ties, returns to the + closest position giving the minimum size. Returns 1 in case of + success; 0 otherwise.] + + SideEffects [None] + +******************************************************************************/ +static int +ddLinearAndSiftingBackward( + DdManager * table, + int size, + Move * moves) +{ + Move *move; + int res; + + for (move = moves; move != NULL; move = move->next) { + if (move->size < size) { + size = move->size; + } + } + + for (move = moves; move != NULL; move = move->next) { + if (move->size == size) return(1); + if (move->flags == CUDD_LINEAR_TRANSFORM_MOVE) { + res = cuddLinearInPlace(table,(int)move->x,(int)move->y); + if (!res) return(0); + } + res = cuddSwapInPlace(table,(int)move->x,(int)move->y); + if (!res) return(0); + if (move->flags == CUDD_INVERSE_TRANSFORM_MOVE) { + res = cuddLinearInPlace(table,(int)move->x,(int)move->y); + if (!res) return(0); + } + } + + return(1); + +} /* end of ddLinearAndSiftingBackward */ + + +/**Function******************************************************************** + + Synopsis [Given a set of moves, returns the DD heap to the order + in effect before the moves.] + + Description [Given a set of moves, returns the DD heap to the + order in effect before the moves. Returns 1 in case of success; + 0 otherwise.] + + SideEffects [None] + +******************************************************************************/ +static Move* +ddUndoMoves( + DdManager * table, + Move * moves) +{ + Move *invmoves = NULL; + Move *move; + Move *invmove; + int size; + + for (move = moves; move != NULL; move = move->next) { + invmove = (Move *) cuddDynamicAllocNode(table); + if (invmove == NULL) goto ddUndoMovesOutOfMem; + invmove->x = move->x; + invmove->y = move->y; + invmove->next = invmoves; + invmoves = invmove; + if (move->flags == CUDD_SWAP_MOVE) { + invmove->flags = CUDD_SWAP_MOVE; + size = cuddSwapInPlace(table,(int)move->x,(int)move->y); + if (!size) goto ddUndoMovesOutOfMem; + } else if (move->flags == CUDD_LINEAR_TRANSFORM_MOVE) { + invmove->flags = CUDD_INVERSE_TRANSFORM_MOVE; + size = cuddLinearInPlace(table,(int)move->x,(int)move->y); + if (!size) goto ddUndoMovesOutOfMem; + size = cuddSwapInPlace(table,(int)move->x,(int)move->y); + if (!size) goto ddUndoMovesOutOfMem; + } else { /* must be CUDD_INVERSE_TRANSFORM_MOVE */ +#ifdef DD_DEBUG + (void) fprintf(table->err,"Unforseen event in ddUndoMoves!\n"); +#endif + invmove->flags = CUDD_LINEAR_TRANSFORM_MOVE; + size = cuddSwapInPlace(table,(int)move->x,(int)move->y); + if (!size) goto ddUndoMovesOutOfMem; + size = cuddLinearInPlace(table,(int)move->x,(int)move->y); + if (!size) goto ddUndoMovesOutOfMem; + } + invmove->size = size; + } + + return(invmoves); + + ddUndoMovesOutOfMem: + while (invmoves != NULL) { + move = invmoves->next; + cuddDeallocMove(table, invmoves); + invmoves = move; + } + return((Move *) CUDD_OUT_OF_MEM); + +} /* end of ddUndoMoves */ + + +/**Function******************************************************************** + + Synopsis [XORs two rows of the linear transform matrix.] + + Description [XORs two rows of the linear transform matrix and replaces + the first row with the result.] + + SideEffects [none] + + SeeAlso [] + +******************************************************************************/ +static void +cuddXorLinear( + DdManager * table, + int x, + int y) +{ + int i; + int nvars = table->size; + int wordsPerRow = ((nvars - 1) >> LOGBPL) + 1; + int xstart = wordsPerRow * x; + int ystart = wordsPerRow * y; + long *linear = table->linear; + + for (i = 0; i < wordsPerRow; i++) { + linear[xstart+i] ^= linear[ystart+i]; + } + +} /* end of cuddXorLinear */ +/**CFile*********************************************************************** + + FileName [cuddRef.c] + + PackageName [cudd] + + Synopsis [Functions that manipulate the reference counts.] + + Description [External procedures included in this module: +
        +
      • Cudd_Ref() +
      • Cudd_RecursiveDeref() +
      • Cudd_IterDerefBdd() +
      • Cudd_DelayedDerefBdd() +
      • Cudd_RecursiveDerefZdd() +
      • Cudd_Deref() +
      • Cudd_CheckZeroRef() +
      + Internal procedures included in this module: +
        +
      • cuddReclaim() +
      • cuddReclaimZdd() +
      • cuddClearDeathRow() +
      • cuddShrinkDeathRow() +
      • cuddIsInDeathRow() +
      • cuddTimesInDeathRow() +
      + ] + + SeeAlso [] + + Author [Fabio Somenzi] + + Copyright [Copyright (c) 1995-2012, Regents of the University of Colorado + + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + Neither the name of the University of Colorado nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE.] + +******************************************************************************/ + + +/*---------------------------------------------------------------------------*/ +/* Constant declarations */ +/*---------------------------------------------------------------------------*/ + + +/*---------------------------------------------------------------------------*/ +/* Stucture declarations */ +/*---------------------------------------------------------------------------*/ + + +/*---------------------------------------------------------------------------*/ +/* Type declarations */ +/*---------------------------------------------------------------------------*/ + + +/*---------------------------------------------------------------------------*/ +/* Variable declarations */ +/*---------------------------------------------------------------------------*/ + +//#ifndef lint +//static char rcsid[] DD_UNUSED = "$Id: cuddRef.c,v 1.29 2012/02/05 01:07:19 fabio Exp $"; +//#endif + +/*---------------------------------------------------------------------------*/ +/* Macro declarations */ +/*---------------------------------------------------------------------------*/ + + +/**AutomaticStart*************************************************************/ + +/*---------------------------------------------------------------------------*/ +/* Static function prototypes */ +/*---------------------------------------------------------------------------*/ + +/**AutomaticEnd***************************************************************/ + + +/*---------------------------------------------------------------------------*/ +/* Definition of exported functions */ +/*---------------------------------------------------------------------------*/ + +/**Function******************************************************************** + + Synopsis [Increases the reference count of a node, if it is not + saturated.] + + Description [] + + SideEffects [None] + + SeeAlso [Cudd_RecursiveDeref Cudd_Deref] + +******************************************************************************/ +void +Cudd_Ref( + DdNode * n) +{ + + n = Cudd_Regular(n); + + cuddSatInc(n->ref); + +} /* end of Cudd_Ref */ + + +/**Function******************************************************************** + + Synopsis [Decreases the reference count of node n.] + + Description [Decreases the reference count of node n. If n dies, + recursively decreases the reference counts of its children. It is + used to dispose of a DD that is no longer needed.] + + SideEffects [None] + + SeeAlso [Cudd_Deref Cudd_Ref Cudd_RecursiveDerefZdd] + +******************************************************************************/ +void +Cudd_RecursiveDeref( + DdManager * table, + DdNode * n) +{ + DdNode *N; + int ord; + DdNodePtr *stack = table->stack; + int SP = 1; + + unsigned int live = table->keys - table->dead; + if (live > table->peakLiveNodes) { + table->peakLiveNodes = live; + } + + N = Cudd_Regular(n); + + do { +#ifdef DD_DEBUG + assert(N->ref != 0); +#endif + + if (N->ref == 1) { + N->ref = 0; + table->dead++; +#ifdef DD_STATS + table->nodesDropped++; +#endif + if (cuddIsConstant(N)) { + table->constants.dead++; + N = stack[--SP]; + } else { + ord = table->perm[N->index]; + stack[SP++] = Cudd_Regular(cuddE(N)); + table->subtables[ord].dead++; + N = cuddT(N); + } + } else { + cuddSatDec(N->ref); + N = stack[--SP]; + } + } while (SP != 0); + +} /* end of Cudd_RecursiveDeref */ + + +/**Function******************************************************************** + + Synopsis [Decreases the reference count of BDD node n.] + + Description [Decreases the reference count of node n. If n dies, + recursively decreases the reference counts of its children. It is + used to dispose of a BDD that is no longer needed. It is more + efficient than Cudd_RecursiveDeref, but it cannot be used on + ADDs. The greater efficiency comes from being able to assume that no + constant node will ever die as a result of a call to this + procedure.] + + SideEffects [None] + + SeeAlso [Cudd_RecursiveDeref Cudd_DelayedDerefBdd] + +******************************************************************************/ +void +Cudd_IterDerefBdd( + DdManager * table, + DdNode * n) +{ + DdNode *N; + int ord; + DdNodePtr *stack = table->stack; + int SP = 1; + + unsigned int live = table->keys - table->dead; + if (live > table->peakLiveNodes) { + table->peakLiveNodes = live; + } + + N = Cudd_Regular(n); + + do { +#ifdef DD_DEBUG + assert(N->ref != 0); +#endif + + if (N->ref == 1) { + N->ref = 0; + table->dead++; +#ifdef DD_STATS + table->nodesDropped++; +#endif + ord = table->perm[N->index]; + stack[SP++] = Cudd_Regular(cuddE(N)); + table->subtables[ord].dead++; + N = cuddT(N); + } else { + cuddSatDec(N->ref); + N = stack[--SP]; + } + } while (SP != 0); + +} /* end of Cudd_IterDerefBdd */ + + + +/**Function******************************************************************** + + Synopsis [Decreases the reference count of ZDD node n.] + + Description [Decreases the reference count of ZDD node n. If n dies, + recursively decreases the reference counts of its children. It is + used to dispose of a ZDD that is no longer needed.] + + SideEffects [None] + + SeeAlso [Cudd_Deref Cudd_Ref Cudd_RecursiveDeref] + +******************************************************************************/ +void +Cudd_RecursiveDerefZdd( + DdManager * table, + DdNode * n) +{ + DdNode *N; + int ord; + DdNodePtr *stack = table->stack; + int SP = 1; + + N = n; + + do { +#ifdef DD_DEBUG + assert(N->ref != 0); +#endif + + cuddSatDec(N->ref); + + if (N->ref == 0) { + table->deadZ++; +#ifdef DD_STATS + table->nodesDropped++; +#endif +#ifdef DD_DEBUG + assert(!cuddIsConstant(N)); +#endif + ord = table->permZ[N->index]; + stack[SP++] = cuddE(N); + table->subtableZ[ord].dead++; + N = cuddT(N); + } else { + N = stack[--SP]; + } + } while (SP != 0); + +} /* end of Cudd_RecursiveDerefZdd */ + + +/**Function******************************************************************** + + Synopsis [Decreases the reference count of node.] + + Description [Decreases the reference count of node. It is primarily + used in recursive procedures to decrease the ref count of a result + node before returning it. This accomplishes the goal of removing the + protection applied by a previous Cudd_Ref.] + + SideEffects [None] + + SeeAlso [Cudd_RecursiveDeref Cudd_RecursiveDerefZdd Cudd_Ref] + +******************************************************************************/ +void +Cudd_Deref( + DdNode * node) +{ + node = Cudd_Regular(node); + cuddSatDec(node->ref); + +} /* end of Cudd_Deref */ + +/*---------------------------------------------------------------------------*/ +/* Definition of internal functions */ +/*---------------------------------------------------------------------------*/ + + +/**Function******************************************************************** + + Synopsis [Brings children of a dead node back.] + + Description [] + + SideEffects [None] + + SeeAlso [cuddReclaimZdd] + +******************************************************************************/ +void +cuddReclaim( + DdManager * table, + DdNode * n) +{ + DdNode *N; + int ord; + DdNodePtr *stack = table->stack; + int SP = 1; + double initialDead = table->dead; + + N = Cudd_Regular(n); + +#ifdef DD_DEBUG + assert(N->ref == 0); +#endif + + do { + if (N->ref == 0) { + N->ref = 1; + table->dead--; + if (cuddIsConstant(N)) { + table->constants.dead--; + N = stack[--SP]; + } else { + ord = table->perm[N->index]; + stack[SP++] = Cudd_Regular(cuddE(N)); + table->subtables[ord].dead--; + N = cuddT(N); + } + } else { + cuddSatInc(N->ref); + N = stack[--SP]; + } + } while (SP != 0); + + N = Cudd_Regular(n); + cuddSatDec(N->ref); + table->reclaimed += initialDead - table->dead; + +} /* end of cuddReclaim */ + + +/**Function******************************************************************** + + Synopsis [Brings children of a dead ZDD node back.] + + Description [] + + SideEffects [None] + + SeeAlso [cuddReclaim] + +******************************************************************************/ +void +cuddReclaimZdd( + DdManager * table, + DdNode * n) +{ + DdNode *N; + int ord; + DdNodePtr *stack = table->stack; + int SP = 1; + + N = n; + +#ifdef DD_DEBUG + assert(N->ref == 0); +#endif + + do { + cuddSatInc(N->ref); + + if (N->ref == 1) { + table->deadZ--; + table->reclaimed++; +#ifdef DD_DEBUG + assert(!cuddIsConstant(N)); +#endif + ord = table->permZ[N->index]; + stack[SP++] = cuddE(N); + table->subtableZ[ord].dead--; + N = cuddT(N); + } else { + N = stack[--SP]; + } + } while (SP != 0); + + cuddSatDec(n->ref); + +} /* end of cuddReclaimZdd */ + + +/**Function******************************************************************** + + Synopsis [Shrinks the death row.] + + Description [Shrinks the death row by a factor of four.] + + SideEffects [None] + + SeeAlso [cuddClearDeathRow] + +******************************************************************************/ +void +cuddShrinkDeathRow( + DdManager *table) +{ +#ifndef DD_NO_DEATH_ROW + int i; + + if (table->deathRowDepth > 3) { + for (i = table->deathRowDepth/4; i < table->deathRowDepth; i++) { + if (table->deathRow[i] == NULL) break; + Cudd_IterDerefBdd(table,table->deathRow[i]); + table->deathRow[i] = NULL; + } + table->deathRowDepth /= 4; + table->deadMask = table->deathRowDepth - 1; + if ((unsigned) table->nextDead > table->deadMask) { + table->nextDead = 0; + } + table->deathRow = REALLOC(DdNodePtr, table->deathRow, + table->deathRowDepth); + } +#endif + +} /* end of cuddShrinkDeathRow */ + + +/**Function******************************************************************** + + Synopsis [Clears the death row.] + + Description [] + + SideEffects [None] + + SeeAlso [Cudd_DelayedDerefBdd Cudd_IterDerefBdd Cudd_CheckZeroRef + cuddGarbageCollect] + +******************************************************************************/ +void +cuddClearDeathRow( + DdManager *table) +{ +#ifndef DD_NO_DEATH_ROW + int i; + + for (i = 0; i < table->deathRowDepth; i++) { + if (table->deathRow[i] == NULL) break; + Cudd_IterDerefBdd(table,table->deathRow[i]); + table->deathRow[i] = NULL; + } +#ifdef DD_DEBUG + for (; i < table->deathRowDepth; i++) { + assert(table->deathRow[i] == NULL); + } +#endif + table->nextDead = 0; +#endif + +} /* end of cuddClearDeathRow */ + + +/*---------------------------------------------------------------------------*/ +/* Definition of static functions */ +/*---------------------------------------------------------------------------*/ +/**CFile*********************************************************************** + + FileName [cuddReorder.c] + + PackageName [cudd] + + Synopsis [Functions for dynamic variable reordering.] + + Description [External procedures included in this file: +
        +
      • Cudd_ReduceHeap() +
      • Cudd_ShuffleHeap() +
      + Internal procedures included in this module: +
        +
      • cuddDynamicAllocNode() +
      • cuddSifting() +
      • cuddSwapping() +
      • cuddNextHigh() +
      • cuddNextLow() +
      • cuddSwapInPlace() +
      • cuddBddAlignToZdd() +
      + Static procedures included in this module: +
        +
      • ddUniqueCompare() +
      • ddSwapAny() +
      • ddSiftingAux() +
      • ddSiftingUp() +
      • ddSiftingDown() +
      • ddSiftingBackward() +
      • ddReorderPreprocess() +
      • ddReorderPostprocess() +
      • ddShuffle() +
      • ddSiftUp() +
      • bddFixTree() +
      ] + + Author [Shipra Panda, Bernard Plessier, Fabio Somenzi] + + Copyright [Copyright (c) 1995-2012, Regents of the University of Colorado + + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + Neither the name of the University of Colorado nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE.] + +******************************************************************************/ + + +/*---------------------------------------------------------------------------*/ +/* Constant declarations */ +/*---------------------------------------------------------------------------*/ + +#define DD_MAX_SUBTABLE_SPARSITY 8 + +/*---------------------------------------------------------------------------*/ +/* Stucture declarations */ +/*---------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------*/ +/* Type declarations */ +/*---------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------*/ +/* Variable declarations */ +/*---------------------------------------------------------------------------*/ + +//#ifndef lint +//static char rcsid[] DD_UNUSED = "$Id: cuddReorder.c,v 1.71 2012/02/05 01:07:19 fabio Exp $"; +//#endif + +static int *entry; + +int ddTotalNumberSwapping; +#ifdef DD_STATS +int ddTotalNISwaps; +#endif + +/*---------------------------------------------------------------------------*/ +/* Macro declarations */ +/*---------------------------------------------------------------------------*/ + +/**AutomaticStart*************************************************************/ + +/*---------------------------------------------------------------------------*/ +/* Static function prototypes */ +/*---------------------------------------------------------------------------*/ + +static int ddUniqueCompare (int *ptrX, int *ptrY); +static Move * ddSwapAny (DdManager *table, int x, int y); +static int ddSiftingAux (DdManager *table, int x, int xLow, int xHigh); +static Move * ddSiftingUp (DdManager *table, int y, int xLow); +static Move * ddSiftingDown (DdManager *table, int x, int xHigh); +static int ddSiftingBackward (DdManager *table, int size, Move *moves); +static int ddReorderPreprocess (DdManager *table); +static int ddReorderPostprocess (DdManager *table); +static int ddShuffle2 (DdManager *table, int *permutation); +static int ddSiftUp2 (DdManager *table, int x, int xLow); +static void bddFixTree (DdManager *table, MtrNode *treenode); +static int ddUpdateMtrTree (DdManager *table, MtrNode *treenode, int *perm, int *invperm); +static int ddCheckPermuation (DdManager *table, MtrNode *treenode, int *perm, int *invperm); + +/**AutomaticEnd***************************************************************/ + + +/*---------------------------------------------------------------------------*/ +/* Definition of exported functions */ +/*---------------------------------------------------------------------------*/ + + +/**Function******************************************************************** + + Synopsis [Main dynamic reordering routine.] + + Description [Main dynamic reordering routine. + Calls one of the possible reordering procedures: +
        +
      • Swapping +
      • Sifting +
      • Symmetric Sifting +
      • Group Sifting +
      • Window Permutation +
      • Simulated Annealing +
      • Genetic Algorithm +
      • Dynamic Programming (exact) +
      + + For sifting, symmetric sifting, group sifting, and window + permutation it is possible to request reordering to convergence.

      + + The core of all methods is the reordering procedure + cuddSwapInPlace() which swaps two adjacent variables and is based + on Rudell's paper. + Returns 1 in case of success; 0 otherwise. In the case of symmetric + sifting (with and without convergence) returns 1 plus the number of + symmetric variables, in case of success.] + + SideEffects [Changes the variable order for all diagrams and clears + the cache.] + +******************************************************************************/ +int +Cudd_ReduceHeap( + DdManager * table /* DD manager */, + Cudd_ReorderingType heuristic /* method used for reordering */, + int minsize /* bound below which no reordering occurs */) +{ + DdHook *hook; + int result; + unsigned int nextDyn; +#ifdef DD_STATS + unsigned int initialSize; + unsigned int finalSize; +#endif + unsigned long localTime; + + /* Don't reorder if there are too many dead nodes. */ + if (table->keys - table->dead < (unsigned) minsize) + return(1); + + if (heuristic == CUDD_REORDER_SAME) { + heuristic = table->autoMethod; + } + if (heuristic == CUDD_REORDER_NONE) { + return(1); + } + + /* This call to Cudd_ReduceHeap does initiate reordering. Therefore + ** we count it. + */ + table->reorderings++; + + localTime = util_cpu_time(); + + /* Run the hook functions. */ + hook = table->preReorderingHook; + while (hook != NULL) { + int res = (hook->f)(table, "BDD", (void *)heuristic); + if (res == 0) return(0); + hook = hook->next; + } + + if (!ddReorderPreprocess(table)) return(0); + ddTotalNumberSwapping = 0; + + if (table->keys > table->peakLiveNodes) { + table->peakLiveNodes = table->keys; + } +#ifdef DD_STATS + initialSize = table->keys - table->isolated; + ddTotalNISwaps = 0; + + switch(heuristic) { + case CUDD_REORDER_RANDOM: + case CUDD_REORDER_RANDOM_PIVOT: + (void) fprintf(table->out,"#:I_RANDOM "); + break; + case CUDD_REORDER_SIFT: + case CUDD_REORDER_SIFT_CONVERGE: + case CUDD_REORDER_SYMM_SIFT: + case CUDD_REORDER_SYMM_SIFT_CONV: + case CUDD_REORDER_GROUP_SIFT: + case CUDD_REORDER_GROUP_SIFT_CONV: + (void) fprintf(table->out,"#:I_SIFTING "); + break; + case CUDD_REORDER_WINDOW2: + case CUDD_REORDER_WINDOW3: + case CUDD_REORDER_WINDOW4: + case CUDD_REORDER_WINDOW2_CONV: + case CUDD_REORDER_WINDOW3_CONV: + case CUDD_REORDER_WINDOW4_CONV: + (void) fprintf(table->out,"#:I_WINDOW "); + break; + case CUDD_REORDER_ANNEALING: + (void) fprintf(table->out,"#:I_ANNEAL "); + break; + case CUDD_REORDER_GENETIC: + (void) fprintf(table->out,"#:I_GENETIC "); + break; + case CUDD_REORDER_LINEAR: + case CUDD_REORDER_LINEAR_CONVERGE: + (void) fprintf(table->out,"#:I_LINSIFT "); + break; + case CUDD_REORDER_EXACT: + (void) fprintf(table->out,"#:I_EXACT "); + break; + default: + return(0); + } + (void) fprintf(table->out,"%8d: initial size",initialSize); +#endif + + /* See if we should use alternate threshold for maximum growth. */ + if (table->reordCycle && table->reorderings % table->reordCycle == 0) { + double saveGrowth = table->maxGrowth; + table->maxGrowth = table->maxGrowthAlt; + result = cuddTreeSifting(table,heuristic); + table->maxGrowth = saveGrowth; + } else { + result = cuddTreeSifting(table,heuristic); + } + +#ifdef DD_STATS + (void) fprintf(table->out,"\n"); + finalSize = table->keys - table->isolated; + (void) fprintf(table->out,"#:F_REORDER %8d: final size\n",finalSize); + (void) fprintf(table->out,"#:T_REORDER %8g: total time (sec)\n", + ((double)(util_cpu_time() - localTime)/1000.0)); + (void) fprintf(table->out,"#:N_REORDER %8d: total swaps\n", + ddTotalNumberSwapping); + (void) fprintf(table->out,"#:M_REORDER %8d: NI swaps\n",ddTotalNISwaps); +#endif + + if (result == 0) + return(0); + + if (!ddReorderPostprocess(table)) + return(0); + + if (table->realign) { + if (!cuddZddAlignToBdd(table)) + return(0); + } + + nextDyn = (table->keys - table->constants.keys + 1) * + DD_DYN_RATIO + table->constants.keys; + if (table->reorderings < 20 || nextDyn > table->nextDyn) + table->nextDyn = nextDyn; + else + table->nextDyn += 20; + if (table->randomizeOrder != 0) { + table->nextDyn += Cudd_Random() & table->randomizeOrder; + } + table->reordered = 1; + + /* Run hook functions. */ + hook = table->postReorderingHook; + while (hook != NULL) { + int res = (hook->f)(table, "BDD", (void *)localTime); + if (res == 0) return(0); + hook = hook->next; + } + /* Update cumulative reordering time. */ + table->reordTime += util_cpu_time() - localTime; + + return(result); + +} /* end of Cudd_ReduceHeap */ + + +/*---------------------------------------------------------------------------*/ +/* Definition of internal functions */ +/*---------------------------------------------------------------------------*/ + + +/**Function******************************************************************** + + Synopsis [Dynamically allocates a Node.] + + Description [Dynamically allocates a Node. This procedure is similar + to cuddAllocNode in Cudd_Table.c, but it does not attempt garbage + collection, because during reordering there are no dead nodes. + Returns a pointer to a new node if successful; NULL is memory is + full.] + + SideEffects [None] + + SeeAlso [cuddAllocNode] + +******************************************************************************/ +DdNode * +cuddDynamicAllocNode( + DdManager * table) +{ + int i; + DdNodePtr *mem; + DdNode *list, *node; + extern DD_OOMFP MMoutOfMemory; + DD_OOMFP saveHandler; + + if (table->nextFree == NULL) { /* free list is empty */ + /* Try to allocate a new block. */ + saveHandler = MMoutOfMemory; + MMoutOfMemory = Cudd_OutOfMem; + mem = (DdNodePtr *) ALLOC(DdNode, DD_MEM_CHUNK + 1); + MMoutOfMemory = saveHandler; + if (mem == NULL && table->stash != NULL) { + FREE(table->stash); + table->stash = NULL; + /* Inhibit resizing of tables. */ + table->maxCacheHard = table->cacheSlots - 1; + table->cacheSlack = - (int) (table->cacheSlots + 1); + for (i = 0; i < table->size; i++) { + table->subtables[i].maxKeys <<= 2; + } + mem = (DdNodePtr *) ALLOC(DdNode,DD_MEM_CHUNK + 1); + } + if (mem == NULL) { + /* Out of luck. Call the default handler to do + ** whatever it specifies for a failed malloc. If this + ** handler returns, then set error code, print + ** warning, and return. */ + (*MMoutOfMemory)(sizeof(DdNode)*(DD_MEM_CHUNK + 1)); + table->errorCode = CUDD_MEMORY_OUT; +#ifdef DD_VERBOSE + (void) fprintf(table->err, + "cuddDynamicAllocNode: out of memory"); + (void) fprintf(table->err,"Memory in use = %lu\n", + table->memused); +#endif + return(NULL); + } else { /* successful allocation; slice memory */ + unsigned long offset; + table->memused += (DD_MEM_CHUNK + 1) * sizeof(DdNode); + mem[0] = (DdNode *) table->memoryList; + table->memoryList = mem; + + /* Here we rely on the fact that the size of a DdNode is a + ** power of 2 and a multiple of the size of a pointer. + ** If we align one node, all the others will be aligned + ** as well. */ + offset = (unsigned long) mem & (sizeof(DdNode) - 1); + mem += (sizeof(DdNode) - offset) / sizeof(DdNodePtr); +#ifdef DD_DEBUG + assert(((unsigned long) mem & (sizeof(DdNode) - 1)) == 0); +#endif + list = (DdNode *) mem; + + i = 1; + do { + list[i - 1].ref = 0; + list[i - 1].next = &list[i]; + } while (++i < DD_MEM_CHUNK); + + list[DD_MEM_CHUNK-1].ref = 0; + list[DD_MEM_CHUNK - 1].next = NULL; + + table->nextFree = &list[0]; + } + } /* if free list empty */ + + node = table->nextFree; + table->nextFree = node->next; + return (node); + +} /* end of cuddDynamicAllocNode */ + + +/**Function******************************************************************** + + Synopsis [Implementation of Rudell's sifting algorithm.] + + Description [Implementation of Rudell's sifting algorithm. + Assumes that no dead nodes are present. +

        +
      1. Order all the variables according to the number of entries + in each unique table. +
      2. Sift the variable up and down, remembering each time the + total size of the DD heap. +
      3. Select the best permutation. +
      4. Repeat 3 and 4 for all variables. +
      + Returns 1 if successful; 0 otherwise.] + + SideEffects [None] + +******************************************************************************/ +int +cuddSifting( + DdManager * table, + int lower, + int upper) +{ + int i; + int *var; + int size; + int x; + int result; +#ifdef DD_STATS + int previousSize; +#endif + + size = table->size; + + /* Find order in which to sift variables. */ + var = NULL; + entry = ALLOC(int,size); + if (entry == NULL) { + table->errorCode = CUDD_MEMORY_OUT; + goto cuddSiftingOutOfMem; + } + var = ALLOC(int,size); + if (var == NULL) { + table->errorCode = CUDD_MEMORY_OUT; + goto cuddSiftingOutOfMem; + } + + for (i = 0; i < size; i++) { + x = table->perm[i]; + entry[i] = table->subtables[x].keys; + var[i] = i; + } + + qsort((void *)var,size,sizeof(int),(DD_QSFP)ddUniqueCompare); + + /* Now sift. */ + for (i = 0; i < ddMin(table->siftMaxVar,size); i++) { + if (ddTotalNumberSwapping >= table->siftMaxSwap) + break; + if (util_cpu_time() - table->startTime + table->reordTime + > table->timeLimit) { + table->autoDyn = 0; /* prevent further reordering */ + break; + } + x = table->perm[var[i]]; + + if (x < lower || x > upper || table->subtables[x].bindVar == 1) + continue; +#ifdef DD_STATS + previousSize = table->keys - table->isolated; +#endif + result = ddSiftingAux(table, x, lower, upper); + if (!result) goto cuddSiftingOutOfMem; +#ifdef DD_STATS + if (table->keys < (unsigned) previousSize + table->isolated) { + (void) fprintf(table->out,"-"); + } else if (table->keys > (unsigned) previousSize + table->isolated) { + (void) fprintf(table->out,"+"); /* should never happen */ + (void) fprintf(table->err,"\nSize increased from %d to %d while sifting variable %d\n", previousSize, table->keys - table->isolated, var[i]); + } else { + (void) fprintf(table->out,"="); + } + fflush(table->out); +#endif + } + + FREE(var); + FREE(entry); + + return(1); + + cuddSiftingOutOfMem: + + if (entry != NULL) FREE(entry); + if (var != NULL) FREE(var); + + return(0); + +} /* end of cuddSifting */ + + +/**Function******************************************************************** + + Synopsis [Reorders variables by a sequence of (non-adjacent) swaps.] + + Description [Implementation of Plessier's algorithm that reorders + variables by a sequence of (non-adjacent) swaps. +
        +
      1. Select two variables (RANDOM or HEURISTIC). +
      2. Permute these variables. +
      3. If the nodes have decreased accept the permutation. +
      4. Otherwise reconstruct the original heap. +
      5. Loop. +
      + Returns 1 in case of success; 0 otherwise.] + + SideEffects [None] + +******************************************************************************/ +int +cuddSwapping( + DdManager * table, + int lower, + int upper, + Cudd_ReorderingType heuristic) +{ + int i, j; + int max, keys; + int nvars; + int x, y; + int iterate; + int previousSize; + Move *moves, *move; + int pivot; + int modulo; + int result; + +#ifdef DD_DEBUG + /* Sanity check */ + assert(lower >= 0 && upper < table->size && lower <= upper); +#endif + + nvars = upper - lower + 1; + iterate = nvars; + + for (i = 0; i < iterate; i++) { + if (ddTotalNumberSwapping >= table->siftMaxSwap) + break; + if (heuristic == CUDD_REORDER_RANDOM_PIVOT) { + max = -1; + for (j = lower; j <= upper; j++) { + if ((keys = table->subtables[j].keys) > max) { + max = keys; + pivot = j; + } + } + + modulo = upper - pivot; + if (modulo == 0) { + y = pivot; + } else{ + y = pivot + 1 + ((int) Cudd_Random() % modulo); + } + + modulo = pivot - lower - 1; + if (modulo < 1) { + x = lower; + } else{ + do { + x = (int) Cudd_Random() % modulo; + } while (x == y); + } + } else { + x = ((int) Cudd_Random() % nvars) + lower; + do { + y = ((int) Cudd_Random() % nvars) + lower; + } while (x == y); + } + previousSize = table->keys - table->isolated; + moves = ddSwapAny(table,x,y); + if (moves == NULL) goto cuddSwappingOutOfMem; + result = ddSiftingBackward(table,previousSize,moves); + if (!result) goto cuddSwappingOutOfMem; + while (moves != NULL) { + move = moves->next; + cuddDeallocMove(table, moves); + moves = move; + } +#ifdef DD_STATS + if (table->keys < (unsigned) previousSize + table->isolated) { + (void) fprintf(table->out,"-"); + } else if (table->keys > (unsigned) previousSize + table->isolated) { + (void) fprintf(table->out,"+"); /* should never happen */ + } else { + (void) fprintf(table->out,"="); + } + fflush(table->out); +#endif +#if 0 + (void) fprintf(table->out,"#:t_SWAPPING %8d: tmp size\n", + table->keys - table->isolated); +#endif + } + + return(1); + + cuddSwappingOutOfMem: + while (moves != NULL) { + move = moves->next; + cuddDeallocMove(table, moves); + moves = move; + } + + return(0); + +} /* end of cuddSwapping */ + + +/**Function******************************************************************** + + Synopsis [Finds the next subtable with a larger index.] + + Description [Finds the next subtable with a larger index. Returns the + index.] + + SideEffects [None] + + SeeAlso [cuddNextLow] + +******************************************************************************/ +int +cuddNextHigh( + DdManager * table, + int x) +{ + return(x+1); + +} /* end of cuddNextHigh */ + + +/**Function******************************************************************** + + Synopsis [Finds the next subtable with a smaller index.] + + Description [Finds the next subtable with a smaller index. Returns the + index.] + + SideEffects [None] + + SeeAlso [cuddNextHigh] + +******************************************************************************/ +int +cuddNextLow( + DdManager * table, + int x) +{ + return(x-1); + +} /* end of cuddNextLow */ + + +/**Function******************************************************************** + + Synopsis [Swaps two adjacent variables.] + + Description [Swaps two adjacent variables. It assumes that no dead + nodes are present on entry to this procedure. The procedure then + guarantees that no dead nodes will be present when it terminates. + cuddSwapInPlace assumes that x < y. Returns the number of keys in + the table if successful; 0 otherwise.] + + SideEffects [None] + +******************************************************************************/ +int +cuddSwapInPlace( + DdManager * table, + int x, + int y) +{ + DdNodePtr *xlist, *ylist; + int xindex, yindex; + int xslots, yslots; + int xshift, yshift; + int oldxkeys, oldykeys; + int newxkeys, newykeys; + int comple, newcomplement; + int i; + Cudd_VariableType varType; + Cudd_LazyGroupType groupType; + int posn; + int isolated; + DdNode *f,*f0,*f1,*f01,*f00,*f11,*f10,*newf1,*newf0; + DdNode *g,*next; + DdNodePtr *previousP; + DdNode *tmp; + DdNode *sentinel = &(table->sentinel); + extern DD_OOMFP MMoutOfMemory; + DD_OOMFP saveHandler; + +#ifdef DD_DEBUG + int count,idcheck; +#endif + +#ifdef DD_DEBUG + assert(x < y); + assert(cuddNextHigh(table,x) == y); + assert(table->subtables[x].keys != 0); + assert(table->subtables[y].keys != 0); + assert(table->subtables[x].dead == 0); + assert(table->subtables[y].dead == 0); +#endif + + ddTotalNumberSwapping++; + + /* Get parameters of x subtable. */ + xindex = table->invperm[x]; + xlist = table->subtables[x].nodelist; + oldxkeys = table->subtables[x].keys; + xslots = table->subtables[x].slots; + xshift = table->subtables[x].shift; + + /* Get parameters of y subtable. */ + yindex = table->invperm[y]; + ylist = table->subtables[y].nodelist; + oldykeys = table->subtables[y].keys; + yslots = table->subtables[y].slots; + yshift = table->subtables[y].shift; + + if (!cuddTestInteract(table,xindex,yindex)) { +#ifdef DD_STATS + ddTotalNISwaps++; +#endif + newxkeys = oldxkeys; + newykeys = oldykeys; + } else { + newxkeys = 0; + newykeys = oldykeys; + + /* Check whether the two projection functions involved in this + ** swap are isolated. At the end, we'll be able to tell how many + ** isolated projection functions are there by checking only these + ** two functions again. This is done to eliminate the isolated + ** projection functions from the node count. + */ + isolated = - ((table->vars[xindex]->ref == 1) + + (table->vars[yindex]->ref == 1)); + + /* The nodes in the x layer that do not depend on + ** y will stay there; the others are put in a chain. + ** The chain is handled as a LIFO; g points to the beginning. + */ + g = NULL; + if ((oldxkeys >= xslots || (unsigned) xslots == table->initSlots) && + oldxkeys <= DD_MAX_SUBTABLE_DENSITY * xslots) { + for (i = 0; i < xslots; i++) { + previousP = &(xlist[i]); + f = *previousP; + while (f != sentinel) { + next = f->next; + f1 = cuddT(f); f0 = cuddE(f); + if (f1->index != (DdHalfWord) yindex && + Cudd_Regular(f0)->index != (DdHalfWord) yindex) { + /* stays */ + newxkeys++; + *previousP = f; + previousP = &(f->next); + } else { + f->index = yindex; + f->next = g; + g = f; + } + f = next; + } /* while there are elements in the collision chain */ + *previousP = sentinel; + } /* for each slot of the x subtable */ + } else { /* resize xlist */ + DdNode *h = NULL; + DdNodePtr *newxlist; + unsigned int newxslots; + int newxshift; + /* Empty current xlist. Nodes that stay go to list h; + ** nodes that move go to list g. */ + for (i = 0; i < xslots; i++) { + f = xlist[i]; + while (f != sentinel) { + next = f->next; + f1 = cuddT(f); f0 = cuddE(f); + if (f1->index != (DdHalfWord) yindex && + Cudd_Regular(f0)->index != (DdHalfWord) yindex) { + /* stays */ + f->next = h; + h = f; + newxkeys++; + } else { + f->index = yindex; + f->next = g; + g = f; + } + f = next; + } /* while there are elements in the collision chain */ + } /* for each slot of the x subtable */ + /* Decide size of new subtable. */ + newxshift = xshift; + newxslots = xslots; + while ((unsigned) oldxkeys > DD_MAX_SUBTABLE_DENSITY * newxslots) { + newxshift--; + newxslots <<= 1; + } + while ((unsigned) oldxkeys < newxslots && + newxslots > table->initSlots) { + newxshift++; + newxslots >>= 1; + } + /* Try to allocate new table. Be ready to back off. */ + saveHandler = MMoutOfMemory; + MMoutOfMemory = Cudd_OutOfMem; + newxlist = ALLOC(DdNodePtr, newxslots); + MMoutOfMemory = saveHandler; + if (newxlist == NULL) { + (void) fprintf(table->err, "Unable to resize subtable %d for lack of memory\n", i); + newxlist = xlist; + newxslots = xslots; + newxshift = xshift; + } else { + table->slots += ((int) newxslots - xslots); + table->minDead = (unsigned) + (table->gcFrac * (double) table->slots); + table->cacheSlack = (int) + ddMin(table->maxCacheHard, DD_MAX_CACHE_TO_SLOTS_RATIO + * table->slots) - 2 * (int) table->cacheSlots; + table->memused += + ((int) newxslots - xslots) * sizeof(DdNodePtr); + FREE(xlist); + xslots = newxslots; + xshift = newxshift; + xlist = newxlist; + } + /* Initialize new subtable. */ + for (i = 0; i < xslots; i++) { + xlist[i] = sentinel; + } + /* Move nodes that were parked in list h to their new home. */ + f = h; + while (f != NULL) { + next = f->next; + f1 = cuddT(f); + f0 = cuddE(f); + /* Check xlist for pair (f11,f01). */ + posn = ddHash(f1, f0, xshift); + /* For each element tmp in collision list xlist[posn]. */ + previousP = &(xlist[posn]); + tmp = *previousP; + while (f1 < cuddT(tmp)) { + previousP = &(tmp->next); + tmp = *previousP; + } + while (f1 == cuddT(tmp) && f0 < cuddE(tmp)) { + previousP = &(tmp->next); + tmp = *previousP; + } + f->next = *previousP; + *previousP = f; + f = next; + } + } + +#ifdef DD_COUNT + table->swapSteps += oldxkeys - newxkeys; +#endif + /* Take care of the x nodes that must be re-expressed. + ** They form a linked list pointed by g. Their index has been + ** already changed to yindex. + */ + f = g; + while (f != NULL) { + next = f->next; + /* Find f1, f0, f11, f10, f01, f00. */ + f1 = cuddT(f); +#ifdef DD_DEBUG + assert(!(Cudd_IsComplement(f1))); +#endif + if ((int) f1->index == yindex) { + f11 = cuddT(f1); f10 = cuddE(f1); + } else { + f11 = f10 = f1; + } +#ifdef DD_DEBUG + assert(!(Cudd_IsComplement(f11))); +#endif + f0 = cuddE(f); + comple = Cudd_IsComplement(f0); + f0 = Cudd_Regular(f0); + if ((int) f0->index == yindex) { + f01 = cuddT(f0); f00 = cuddE(f0); + } else { + f01 = f00 = f0; + } + if (comple) { + f01 = Cudd_Not(f01); + f00 = Cudd_Not(f00); + } + /* Decrease ref count of f1. */ + cuddSatDec(f1->ref); + /* Create the new T child. */ + if (f11 == f01) { + newf1 = f11; + cuddSatInc(newf1->ref); + } else { + /* Check xlist for triple (xindex,f11,f01). */ + posn = ddHash(f11, f01, xshift); + /* For each element newf1 in collision list xlist[posn]. */ + previousP = &(xlist[posn]); + newf1 = *previousP; + while (f11 < cuddT(newf1)) { + previousP = &(newf1->next); + newf1 = *previousP; + } + while (f11 == cuddT(newf1) && f01 < cuddE(newf1)) { + previousP = &(newf1->next); + newf1 = *previousP; + } + if (cuddT(newf1) == f11 && cuddE(newf1) == f01) { + cuddSatInc(newf1->ref); + } else { /* no match */ + newf1 = cuddDynamicAllocNode(table); + if (newf1 == NULL) + goto cuddSwapOutOfMem; + newf1->index = xindex; newf1->ref = 1; + cuddT(newf1) = f11; + cuddE(newf1) = f01; + /* Insert newf1 in the collision list xlist[posn]; + ** increase the ref counts of f11 and f01. + */ + newxkeys++; + newf1->next = *previousP; + *previousP = newf1; + cuddSatInc(f11->ref); + tmp = Cudd_Regular(f01); + cuddSatInc(tmp->ref); + } + } + cuddT(f) = newf1; +#ifdef DD_DEBUG + assert(!(Cudd_IsComplement(newf1))); +#endif + + /* Do the same for f0, keeping complement dots into account. */ + /* Decrease ref count of f0. */ + tmp = Cudd_Regular(f0); + cuddSatDec(tmp->ref); + /* Create the new E child. */ + if (f10 == f00) { + newf0 = f00; + tmp = Cudd_Regular(newf0); + cuddSatInc(tmp->ref); + } else { + /* make sure f10 is regular */ + newcomplement = Cudd_IsComplement(f10); + if (newcomplement) { + f10 = Cudd_Not(f10); + f00 = Cudd_Not(f00); + } + /* Check xlist for triple (xindex,f10,f00). */ + posn = ddHash(f10, f00, xshift); + /* For each element newf0 in collision list xlist[posn]. */ + previousP = &(xlist[posn]); + newf0 = *previousP; + while (f10 < cuddT(newf0)) { + previousP = &(newf0->next); + newf0 = *previousP; + } + while (f10 == cuddT(newf0) && f00 < cuddE(newf0)) { + previousP = &(newf0->next); + newf0 = *previousP; + } + if (cuddT(newf0) == f10 && cuddE(newf0) == f00) { + cuddSatInc(newf0->ref); + } else { /* no match */ + newf0 = cuddDynamicAllocNode(table); + if (newf0 == NULL) + goto cuddSwapOutOfMem; + newf0->index = xindex; newf0->ref = 1; + cuddT(newf0) = f10; + cuddE(newf0) = f00; + /* Insert newf0 in the collision list xlist[posn]; + ** increase the ref counts of f10 and f00. + */ + newxkeys++; + newf0->next = *previousP; + *previousP = newf0; + cuddSatInc(f10->ref); + tmp = Cudd_Regular(f00); + cuddSatInc(tmp->ref); + } + if (newcomplement) { + newf0 = Cudd_Not(newf0); + } + } + cuddE(f) = newf0; + + /* Insert the modified f in ylist. + ** The modified f does not already exists in ylist. + ** (Because of the uniqueness of the cofactors.) + */ + posn = ddHash(newf1, newf0, yshift); + newykeys++; + previousP = &(ylist[posn]); + tmp = *previousP; + while (newf1 < cuddT(tmp)) { + previousP = &(tmp->next); + tmp = *previousP; + } + while (newf1 == cuddT(tmp) && newf0 < cuddE(tmp)) { + previousP = &(tmp->next); + tmp = *previousP; + } + f->next = *previousP; + *previousP = f; + f = next; + } /* while f != NULL */ + + /* GC the y layer. */ + + /* For each node f in ylist. */ + for (i = 0; i < yslots; i++) { + previousP = &(ylist[i]); + f = *previousP; + while (f != sentinel) { + next = f->next; + if (f->ref == 0) { + tmp = cuddT(f); + cuddSatDec(tmp->ref); + tmp = Cudd_Regular(cuddE(f)); + cuddSatDec(tmp->ref); + cuddDeallocNode(table,f); + newykeys--; + } else { + *previousP = f; + previousP = &(f->next); + } + f = next; + } /* while f */ + *previousP = sentinel; + } /* for i */ + +#ifdef DD_DEBUG + #if 0 + (void) fprintf(table->out,"Swapping %d and %d\n",x,y); +#endif + count = 0; + idcheck = 0; + for (i = 0; i < yslots; i++) { + f = ylist[i]; + while (f != sentinel) { + count++; + if (f->index != (DdHalfWord) yindex) + idcheck++; + f = f->next; + } + } + if (count != newykeys) { + (void) fprintf(table->out, + "Error in finding newykeys\toldykeys = %d\tnewykeys = %d\tactual = %d\n", + oldykeys,newykeys,count); + } + if (idcheck != 0) + (void) fprintf(table->out, + "Error in id's of ylist\twrong id's = %d\n", + idcheck); + count = 0; + idcheck = 0; + for (i = 0; i < xslots; i++) { + f = xlist[i]; + while (f != sentinel) { + count++; + if (f->index != (DdHalfWord) xindex) + idcheck++; + f = f->next; + } + } + if (count != newxkeys) { + (void) fprintf(table->out, + "Error in finding newxkeys\toldxkeys = %d \tnewxkeys = %d \tactual = %d\n", + oldxkeys,newxkeys,count); + } + if (idcheck != 0) + (void) fprintf(table->out, + "Error in id's of xlist\twrong id's = %d\n", + idcheck); +#endif + + isolated += (table->vars[xindex]->ref == 1) + + (table->vars[yindex]->ref == 1); + table->isolated += isolated; + } + + /* Set the appropriate fields in table. */ + table->subtables[x].nodelist = ylist; + table->subtables[x].slots = yslots; + table->subtables[x].shift = yshift; + table->subtables[x].keys = newykeys; + table->subtables[x].maxKeys = yslots * DD_MAX_SUBTABLE_DENSITY; + i = table->subtables[x].bindVar; + table->subtables[x].bindVar = table->subtables[y].bindVar; + table->subtables[y].bindVar = i; + /* Adjust filds for lazy sifting. */ + varType = table->subtables[x].varType; + table->subtables[x].varType = table->subtables[y].varType; + table->subtables[y].varType = varType; + i = table->subtables[x].pairIndex; + table->subtables[x].pairIndex = table->subtables[y].pairIndex; + table->subtables[y].pairIndex = i; + i = table->subtables[x].varHandled; + table->subtables[x].varHandled = table->subtables[y].varHandled; + table->subtables[y].varHandled = i; + groupType = table->subtables[x].varToBeGrouped; + table->subtables[x].varToBeGrouped = table->subtables[y].varToBeGrouped; + table->subtables[y].varToBeGrouped = groupType; + + table->subtables[y].nodelist = xlist; + table->subtables[y].slots = xslots; + table->subtables[y].shift = xshift; + table->subtables[y].keys = newxkeys; + table->subtables[y].maxKeys = xslots * DD_MAX_SUBTABLE_DENSITY; + + table->perm[xindex] = y; table->perm[yindex] = x; + table->invperm[x] = yindex; table->invperm[y] = xindex; + + table->keys += newxkeys + newykeys - oldxkeys - oldykeys; + + return(table->keys - table->isolated); + + cuddSwapOutOfMem: + (void) fprintf(table->err,"Error: cuddSwapInPlace out of memory\n"); + + return (0); + +} /* end of cuddSwapInPlace */ + + +/**Function******************************************************************** + + Synopsis [Reorders BDD variables according to the order of the ZDD + variables.] + + Description [Reorders BDD variables according to the order of the + ZDD variables. This function can be called at the end of ZDD + reordering to insure that the order of the BDD variables is + consistent with the order of the ZDD variables. The number of ZDD + variables must be a multiple of the number of BDD variables. Let + M be the ratio of the two numbers. cuddBddAlignToZdd + then considers the ZDD variables from M*i to + (M+1)*i-1 as corresponding to BDD variable + i. This function should be normally called from + Cudd_zddReduceHeap, which clears the cache. Returns 1 in case of + success; 0 otherwise.] + + SideEffects [Changes the BDD variable order for all diagrams and performs + garbage collection of the BDD unique table.] + + SeeAlso [Cudd_ShuffleHeap Cudd_zddReduceHeap] + +******************************************************************************/ +int +cuddBddAlignToZdd( + DdManager * table /* DD manager */) +{ + int *invperm; /* permutation array */ + int M; /* ratio of ZDD variables to BDD variables */ + int i; /* loop index */ + int result; /* return value */ + + /* We assume that a ratio of 0 is OK. */ + if (table->size == 0) + return(1); + + M = table->sizeZ / table->size; + /* Check whether the number of ZDD variables is a multiple of the + ** number of BDD variables. + */ + if (M * table->size != table->sizeZ) + return(0); + /* Create and initialize the inverse permutation array. */ + invperm = ALLOC(int,table->size); + if (invperm == NULL) { + table->errorCode = CUDD_MEMORY_OUT; + return(0); + } + for (i = 0; i < table->sizeZ; i += M) { + int indexZ = table->invpermZ[i]; + int index = indexZ / M; + invperm[i / M] = index; + } + /* Eliminate dead nodes. Do not scan the cache again, because we + ** assume that Cudd_zddReduceHeap has already cleared it. + */ + cuddGarbageCollect(table,0); + + /* Initialize number of isolated projection functions. */ + table->isolated = 0; + for (i = 0; i < table->size; i++) { + if (table->vars[i]->ref == 1) table->isolated++; + } + + /* Initialize the interaction matrix. */ + result = cuddInitInteract(table); + if (result == 0) return(0); + + result = ddShuffle2(table, invperm); + FREE(invperm); + /* Free interaction matrix. */ + FREE(table->interact); + /* Fix the BDD variable group tree. */ + bddFixTree(table,table->tree); + return(result); + +} /* end of cuddBddAlignToZdd */ + +/*---------------------------------------------------------------------------*/ +/* Definition of static functions */ +/*---------------------------------------------------------------------------*/ + + +/**Function******************************************************************** + + Synopsis [Comparison function used by qsort.] + + Description [Comparison function used by qsort to order the + variables according to the number of keys in the subtables. + Returns the difference in number of keys between the two + variables being compared.] + + SideEffects [None] + +******************************************************************************/ +static int +ddUniqueCompare( + int * ptrX, + int * ptrY) +{ +#if 0 + if (entry[*ptrY] == entry[*ptrX]) { + return((*ptrX) - (*ptrY)); + } +#endif + return(entry[*ptrY] - entry[*ptrX]); + +} /* end of ddUniqueCompare */ + + +/**Function******************************************************************** + + Synopsis [Swaps any two variables.] + + Description [Swaps any two variables. Returns the set of moves.] + + SideEffects [None] + +******************************************************************************/ +static Move * +ddSwapAny( + DdManager * table, + int x, + int y) +{ + Move *move, *moves; + int xRef,yRef; + int xNext,yNext; + int size; + int limitSize; + int tmp; + + if (x >y) { + tmp = x; x = y; y = tmp; + } + + xRef = x; yRef = y; + + xNext = cuddNextHigh(table,x); + yNext = cuddNextLow(table,y); + moves = NULL; + limitSize = table->keys - table->isolated; + + for (;;) { + if ( xNext == yNext) { + size = cuddSwapInPlace(table,x,xNext); + if (size == 0) goto ddSwapAnyOutOfMem; + move = (Move *) cuddDynamicAllocNode(table); + if (move == NULL) goto ddSwapAnyOutOfMem; + move->x = x; + move->y = xNext; + move->size = size; + move->next = moves; + moves = move; + + size = cuddSwapInPlace(table,yNext,y); + if (size == 0) goto ddSwapAnyOutOfMem; + move = (Move *) cuddDynamicAllocNode(table); + if (move == NULL) goto ddSwapAnyOutOfMem; + move->x = yNext; + move->y = y; + move->size = size; + move->next = moves; + moves = move; + + size = cuddSwapInPlace(table,x,xNext); + if (size == 0) goto ddSwapAnyOutOfMem; + move = (Move *) cuddDynamicAllocNode(table); + if (move == NULL) goto ddSwapAnyOutOfMem; + move->x = x; + move->y = xNext; + move->size = size; + move->next = moves; + moves = move; + + tmp = x; x = y; y = tmp; + + } else if (x == yNext) { + + size = cuddSwapInPlace(table,x,xNext); + if (size == 0) goto ddSwapAnyOutOfMem; + move = (Move *) cuddDynamicAllocNode(table); + if (move == NULL) goto ddSwapAnyOutOfMem; + move->x = x; + move->y = xNext; + move->size = size; + move->next = moves; + moves = move; + + tmp = x; x = y; y = tmp; + + } else { + size = cuddSwapInPlace(table,x,xNext); + if (size == 0) goto ddSwapAnyOutOfMem; + move = (Move *) cuddDynamicAllocNode(table); + if (move == NULL) goto ddSwapAnyOutOfMem; + move->x = x; + move->y = xNext; + move->size = size; + move->next = moves; + moves = move; + + size = cuddSwapInPlace(table,yNext,y); + if (size == 0) goto ddSwapAnyOutOfMem; + move = (Move *) cuddDynamicAllocNode(table); + if (move == NULL) goto ddSwapAnyOutOfMem; + move->x = yNext; + move->y = y; + move->size = size; + move->next = moves; + moves = move; + + x = xNext; + y = yNext; + } + + xNext = cuddNextHigh(table,x); + yNext = cuddNextLow(table,y); + if (xNext > yRef) break; + + if ((double) size > table->maxGrowth * (double) limitSize) break; + if (size < limitSize) limitSize = size; + } + if (yNext>=xRef) { + size = cuddSwapInPlace(table,yNext,y); + if (size == 0) goto ddSwapAnyOutOfMem; + move = (Move *) cuddDynamicAllocNode(table); + if (move == NULL) goto ddSwapAnyOutOfMem; + move->x = yNext; + move->y = y; + move->size = size; + move->next = moves; + moves = move; + } + + return(moves); + + ddSwapAnyOutOfMem: + while (moves != NULL) { + move = moves->next; + cuddDeallocMove(table, moves); + moves = move; + } + return(NULL); + +} /* end of ddSwapAny */ + + +/**Function******************************************************************** + + Synopsis [Given xLow <= x <= xHigh moves x up and down between the + boundaries.] + + Description [Given xLow <= x <= xHigh moves x up and down between the + boundaries. Finds the best position and does the required changes. + Returns 1 if successful; 0 otherwise.] + + SideEffects [None] + +******************************************************************************/ +static int +ddSiftingAux( + DdManager * table, + int x, + int xLow, + int xHigh) +{ + + Move *move; + Move *moveUp; /* list of up moves */ + Move *moveDown; /* list of down moves */ + int initialSize; + int result; + + initialSize = table->keys - table->isolated; + + moveDown = NULL; + moveUp = NULL; + + if (x == xLow) { + moveDown = ddSiftingDown(table,x,xHigh); + /* At this point x --> xHigh unless bounding occurred. */ + if (moveDown == (Move *) CUDD_OUT_OF_MEM) goto ddSiftingAuxOutOfMem; + /* Move backward and stop at best position. */ + result = ddSiftingBackward(table,initialSize,moveDown); + if (!result) goto ddSiftingAuxOutOfMem; + + } else if (x == xHigh) { + moveUp = ddSiftingUp(table,x,xLow); + /* At this point x --> xLow unless bounding occurred. */ + if (moveUp == (Move *) CUDD_OUT_OF_MEM) goto ddSiftingAuxOutOfMem; + /* Move backward and stop at best position. */ + result = ddSiftingBackward(table,initialSize,moveUp); + if (!result) goto ddSiftingAuxOutOfMem; + + } else if ((x - xLow) > (xHigh - x)) { /* must go down first: shorter */ + moveDown = ddSiftingDown(table,x,xHigh); + /* At this point x --> xHigh unless bounding occurred. */ + if (moveDown == (Move *) CUDD_OUT_OF_MEM) goto ddSiftingAuxOutOfMem; + if (moveDown != NULL) { + x = moveDown->y; + } + moveUp = ddSiftingUp(table,x,xLow); + if (moveUp == (Move *) CUDD_OUT_OF_MEM) goto ddSiftingAuxOutOfMem; + /* Move backward and stop at best position */ + result = ddSiftingBackward(table,initialSize,moveUp); + if (!result) goto ddSiftingAuxOutOfMem; + + } else { /* must go up first: shorter */ + moveUp = ddSiftingUp(table,x,xLow); + /* At this point x --> xLow unless bounding occurred. */ + if (moveUp == (Move *) CUDD_OUT_OF_MEM) goto ddSiftingAuxOutOfMem; + if (moveUp != NULL) { + x = moveUp->x; + } + moveDown = ddSiftingDown(table,x,xHigh); + if (moveDown == (Move *) CUDD_OUT_OF_MEM) goto ddSiftingAuxOutOfMem; + /* Move backward and stop at best position. */ + result = ddSiftingBackward(table,initialSize,moveDown); + if (!result) goto ddSiftingAuxOutOfMem; + } + + while (moveDown != NULL) { + move = moveDown->next; + cuddDeallocMove(table, moveDown); + moveDown = move; + } + while (moveUp != NULL) { + move = moveUp->next; + cuddDeallocMove(table, moveUp); + moveUp = move; + } + + return(1); + + ddSiftingAuxOutOfMem: + if (moveDown != (Move *) CUDD_OUT_OF_MEM) { + while (moveDown != NULL) { + move = moveDown->next; + cuddDeallocMove(table, moveDown); + moveDown = move; + } + } + if (moveUp != (Move *) CUDD_OUT_OF_MEM) { + while (moveUp != NULL) { + move = moveUp->next; + cuddDeallocMove(table, moveUp); + moveUp = move; + } + } + + return(0); + +} /* end of ddSiftingAux */ + + +/**Function******************************************************************** + + Synopsis [Sifts a variable up.] + + Description [Sifts a variable up. Moves y up until either it reaches + the bound (xLow) or the size of the DD heap increases too much. + Returns the set of moves in case of success; NULL if memory is full.] + + SideEffects [None] + +******************************************************************************/ +static Move * +ddSiftingUp( + DdManager * table, + int y, + int xLow) +{ + Move *moves; + Move *move; + int x; + int size; + int limitSize; + int xindex, yindex; + int isolated; + int L; /* lower bound on DD size */ +#ifdef DD_DEBUG + int checkL; + int z; + int zindex; +#endif + + moves = NULL; + yindex = table->invperm[y]; + + /* Initialize the lower bound. + ** The part of the DD below y will not change. + ** The part of the DD above y that does not interact with y will not + ** change. The rest may vanish in the best case, except for + ** the nodes at level xLow, which will not vanish, regardless. + */ + limitSize = L = table->keys - table->isolated; + for (x = xLow + 1; x < y; x++) { + xindex = table->invperm[x]; + if (cuddTestInteract(table,xindex,yindex)) { + isolated = table->vars[xindex]->ref == 1; + L -= table->subtables[x].keys - isolated; + } + } + isolated = table->vars[yindex]->ref == 1; + L -= table->subtables[y].keys - isolated; + + x = cuddNextLow(table,y); + while (x >= xLow && L <= limitSize) { + xindex = table->invperm[x]; +#ifdef DD_DEBUG + checkL = table->keys - table->isolated; + for (z = xLow + 1; z < y; z++) { + zindex = table->invperm[z]; + if (cuddTestInteract(table,zindex,yindex)) { + isolated = table->vars[zindex]->ref == 1; + checkL -= table->subtables[z].keys - isolated; + } + } + isolated = table->vars[yindex]->ref == 1; + checkL -= table->subtables[y].keys - isolated; + assert(L == checkL); +#endif + size = cuddSwapInPlace(table,x,y); + if (size == 0) goto ddSiftingUpOutOfMem; + /* Update the lower bound. */ + if (cuddTestInteract(table,xindex,yindex)) { + isolated = table->vars[xindex]->ref == 1; + L += table->subtables[y].keys - isolated; + } + move = (Move *) cuddDynamicAllocNode(table); + if (move == NULL) goto ddSiftingUpOutOfMem; + move->x = x; + move->y = y; + move->size = size; + move->next = moves; + moves = move; + if ((double) size > (double) limitSize * table->maxGrowth) break; + if (size < limitSize) limitSize = size; + y = x; + x = cuddNextLow(table,y); + } + return(moves); + + ddSiftingUpOutOfMem: + while (moves != NULL) { + move = moves->next; + cuddDeallocMove(table, moves); + moves = move; + } + return((Move *) CUDD_OUT_OF_MEM); + +} /* end of ddSiftingUp */ + + +/**Function******************************************************************** + + Synopsis [Sifts a variable down.] + + Description [Sifts a variable down. Moves x down until either it + reaches the bound (xHigh) or the size of the DD heap increases too + much. Returns the set of moves in case of success; NULL if memory is + full.] + + SideEffects [None] + +******************************************************************************/ +static Move * +ddSiftingDown( + DdManager * table, + int x, + int xHigh) +{ + Move *moves; + Move *move; + int y; + int size; + int R; /* upper bound on node decrease */ + int limitSize; + int xindex, yindex; + int isolated; +#ifdef DD_DEBUG + int checkR; + int z; + int zindex; +#endif + + moves = NULL; + /* Initialize R */ + xindex = table->invperm[x]; + limitSize = size = table->keys - table->isolated; + R = 0; + for (y = xHigh; y > x; y--) { + yindex = table->invperm[y]; + if (cuddTestInteract(table,xindex,yindex)) { + isolated = table->vars[yindex]->ref == 1; + R += table->subtables[y].keys - isolated; + } + } + + y = cuddNextHigh(table,x); + while (y <= xHigh && size - R < limitSize) { +#ifdef DD_DEBUG + checkR = 0; + for (z = xHigh; z > x; z--) { + zindex = table->invperm[z]; + if (cuddTestInteract(table,xindex,zindex)) { + isolated = table->vars[zindex]->ref == 1; + checkR += table->subtables[z].keys - isolated; + } + } + assert(R == checkR); +#endif + /* Update upper bound on node decrease. */ + yindex = table->invperm[y]; + if (cuddTestInteract(table,xindex,yindex)) { + isolated = table->vars[yindex]->ref == 1; + R -= table->subtables[y].keys - isolated; + } + size = cuddSwapInPlace(table,x,y); + if (size == 0) goto ddSiftingDownOutOfMem; + move = (Move *) cuddDynamicAllocNode(table); + if (move == NULL) goto ddSiftingDownOutOfMem; + move->x = x; + move->y = y; + move->size = size; + move->next = moves; + moves = move; + if ((double) size > (double) limitSize * table->maxGrowth) break; + if (size < limitSize) limitSize = size; + x = y; + y = cuddNextHigh(table,x); + } + return(moves); + + ddSiftingDownOutOfMem: + while (moves != NULL) { + move = moves->next; + cuddDeallocMove(table, moves); + moves = move; + } + return((Move *) CUDD_OUT_OF_MEM); + +} /* end of ddSiftingDown */ + + +/**Function******************************************************************** + + Synopsis [Given a set of moves, returns the DD heap to the position + giving the minimum size.] + + Description [Given a set of moves, returns the DD heap to the + position giving the minimum size. In case of ties, returns to the + closest position giving the minimum size. Returns 1 in case of + success; 0 otherwise.] + + SideEffects [None] + +******************************************************************************/ +static int +ddSiftingBackward( + DdManager * table, + int size, + Move * moves) +{ + Move *move; + int res; + + for (move = moves; move != NULL; move = move->next) { + if (move->size < size) { + size = move->size; + } + } + + for (move = moves; move != NULL; move = move->next) { + if (move->size == size) return(1); + res = cuddSwapInPlace(table,(int)move->x,(int)move->y); + if (!res) return(0); + } + + return(1); + +} /* end of ddSiftingBackward */ + + +/**Function******************************************************************** + + Synopsis [Prepares the DD heap for dynamic reordering.] + + Description [Prepares the DD heap for dynamic reordering. Does + garbage collection, to guarantee that there are no dead nodes; + clears the cache, which is invalidated by dynamic reordering; initializes + the number of isolated projection functions; and initializes the + interaction matrix. Returns 1 in case of success; 0 otherwise.] + + SideEffects [None] + +******************************************************************************/ +static int +ddReorderPreprocess( + DdManager * table) +{ + int i; + int res; + + /* Clear the cache. */ + cuddCacheFlush(table); + cuddLocalCacheClearAll(table); + + /* Eliminate dead nodes. Do not scan the cache again. */ + cuddGarbageCollect(table,0); + + /* Initialize number of isolated projection functions. */ + table->isolated = 0; + for (i = 0; i < table->size; i++) { + if (table->vars[i]->ref == 1) table->isolated++; + } + + /* Initialize the interaction matrix. */ + res = cuddInitInteract(table); + if (res == 0) return(0); + + return(1); + +} /* end of ddReorderPreprocess */ + + +/**Function******************************************************************** + + Synopsis [Cleans up at the end of reordering.] + + Description [] + + SideEffects [None] + +******************************************************************************/ +static int +ddReorderPostprocess( + DdManager * table) +{ + +#ifdef DD_VERBOSE + (void) fflush(table->out); +#endif + + /* Free interaction matrix. */ + FREE(table->interact); + + return(1); + +} /* end of ddReorderPostprocess */ + + +/**Function******************************************************************** + + Synopsis [Reorders variables according to a given permutation.] + + Description [Reorders variables according to a given permutation. + The i-th permutation array contains the index of the variable that + should be brought to the i-th level. ddShuffle assumes that no + dead nodes are present and that the interaction matrix is properly + initialized. The reordering is achieved by a series of upward sifts. + Returns 1 if successful; 0 otherwise.] + + SideEffects [None] + + SeeAlso [] + +******************************************************************************/ +static int +ddShuffle2( + DdManager * table, + int * permutation) +{ + int index; + int level; + int position; + int numvars; + int result; +#ifdef DD_STATS + unsigned long localTime; + int initialSize; + int finalSize; + int previousSize; +#endif + + ddTotalNumberSwapping = 0; +#ifdef DD_STATS + localTime = util_cpu_time(); + initialSize = table->keys - table->isolated; + (void) fprintf(table->out,"#:I_SHUFFLE %8d: initial size\n", + initialSize); + ddTotalNISwaps = 0; +#endif + + numvars = table->size; + + for (level = 0; level < numvars; level++) { + index = permutation[level]; + position = table->perm[index]; +#ifdef DD_STATS + previousSize = table->keys - table->isolated; +#endif + result = ddSiftUp2(table,position,level); + if (!result) return(0); +#ifdef DD_STATS + if (table->keys < (unsigned) previousSize + table->isolated) { + (void) fprintf(table->out,"-"); + } else if (table->keys > (unsigned) previousSize + table->isolated) { + (void) fprintf(table->out,"+"); /* should never happen */ + } else { + (void) fprintf(table->out,"="); + } + fflush(table->out); +#endif + } + +#ifdef DD_STATS + (void) fprintf(table->out,"\n"); + finalSize = table->keys - table->isolated; + (void) fprintf(table->out,"#:F_SHUFFLE %8d: final size\n",finalSize); + (void) fprintf(table->out,"#:T_SHUFFLE %8g: total time (sec)\n", + ((double)(util_cpu_time() - localTime)/1000.0)); + (void) fprintf(table->out,"#:N_SHUFFLE %8d: total swaps\n", + ddTotalNumberSwapping); + (void) fprintf(table->out,"#:M_SHUFFLE %8d: NI swaps\n",ddTotalNISwaps); +#endif + + return(1); + +} /* end of ddShuffle */ + + +/**Function******************************************************************** + + Synopsis [Moves one variable up.] + + Description [Takes a variable from position x and sifts it up to + position xLow; xLow should be less than or equal to x. + Returns 1 if successful; 0 otherwise] + + SideEffects [None] + + SeeAlso [] + +******************************************************************************/ +static int +ddSiftUp2( + DdManager * table, + int x, + int xLow) +{ + int y; + int size; + + y = cuddNextLow(table,x); + while (y >= xLow) { + size = cuddSwapInPlace(table,y,x); + if (size == 0) { + return(0); + } + x = y; + y = cuddNextLow(table,x); + } + return(1); + +} /* end of ddSiftUp */ + + +/**Function******************************************************************** + + Synopsis [Fixes the BDD variable group tree after a shuffle.] + + Description [Fixes the BDD variable group tree after a + shuffle. Assumes that the order of the variables in a terminal node + has not been changed.] + + SideEffects [Changes the BDD variable group tree.] + + SeeAlso [] + +******************************************************************************/ +static void +bddFixTree( + DdManager * table, + MtrNode * treenode) +{ + if (treenode == NULL) return; + treenode->low = ((int) treenode->index < table->size) ? + table->perm[treenode->index] : treenode->index; + if (treenode->child != NULL) { + bddFixTree(table, treenode->child); + } + if (treenode->younger != NULL) + bddFixTree(table, treenode->younger); + if (treenode->parent != NULL && treenode->low < treenode->parent->low) { + treenode->parent->low = treenode->low; + treenode->parent->index = treenode->index; + } + return; + +} /* end of bddFixTree */ + + +/**Function******************************************************************** + + Synopsis [Updates the BDD variable group tree before a shuffle.] + + Description [Updates the BDD variable group tree before a shuffle. + Returns 1 if successful; 0 otherwise.] + + SideEffects [Changes the BDD variable group tree.] + + SeeAlso [] + +******************************************************************************/ +static int +ddUpdateMtrTree( + DdManager * table, + MtrNode * treenode, + int * perm, + int * invperm) +{ + unsigned int i, size; + int index, level, minLevel, maxLevel, minIndex; + + if (treenode == NULL) return(1); + + minLevel = CUDD_MAXINDEX; + maxLevel = 0; + minIndex = -1; + /* i : level */ + for (i = treenode->low; i < treenode->low + treenode->size; i++) { + index = table->invperm[i]; + level = perm[index]; + if (level < minLevel) { + minLevel = level; + minIndex = index; + } + if (level > maxLevel) + maxLevel = level; + } + size = maxLevel - minLevel + 1; + if (minIndex == -1) return(0); + if (size == treenode->size) { + treenode->low = minLevel; + treenode->index = minIndex; + } else { + return(0); + } + + if (treenode->child != NULL) { + if (!ddUpdateMtrTree(table, treenode->child, perm, invperm)) + return(0); + } + if (treenode->younger != NULL) { + if (!ddUpdateMtrTree(table, treenode->younger, perm, invperm)) + return(0); + } + return(1); +} + + +/**Function******************************************************************** + + Synopsis [Checks the BDD variable group tree before a shuffle.] + + Description [Checks the BDD variable group tree before a shuffle. + Returns 1 if successful; 0 otherwise.] + + SideEffects [Changes the BDD variable group tree.] + + SeeAlso [] + +******************************************************************************/ +static int +ddCheckPermuation( + DdManager * table, + MtrNode * treenode, + int * perm, + int * invperm) +{ + unsigned int i, size; + int index, level, minLevel, maxLevel; + + if (treenode == NULL) return(1); + + minLevel = table->size; + maxLevel = 0; + /* i : level */ + for (i = treenode->low; i < treenode->low + treenode->size; i++) { + index = table->invperm[i]; + level = perm[index]; + if (level < minLevel) + minLevel = level; + if (level > maxLevel) + maxLevel = level; + } + size = maxLevel - minLevel + 1; + if (size != treenode->size) + return(0); + + if (treenode->child != NULL) { + if (!ddCheckPermuation(table, treenode->child, perm, invperm)) + return(0); + } + if (treenode->younger != NULL) { + if (!ddCheckPermuation(table, treenode->younger, perm, invperm)) + return(0); + } + return(1); +} +/**CFile*********************************************************************** + + FileName [cuddSat.c] + + PackageName [cudd] + + Synopsis [Functions for the solution of satisfiability related problems.] + + Description [External procedures included in this file: +
        +
      • Cudd_Eval() +
      • Cudd_ShortestPath() +
      • Cudd_LargestCube() +
      • Cudd_ShortestLength() +
      • Cudd_Decreasing() +
      • Cudd_Increasing() +
      • Cudd_EquivDC() +
      • Cudd_bddLeqUnless() +
      • Cudd_EqualSupNorm() +
      • Cudd_bddMakePrime() +
      • Cudd_bddMaximallyExpand() +
      • Cudd_bddLargestPrimeUnate() +
      + Internal procedures included in this module: +
        +
      • cuddBddMakePrime() +
      + Static procedures included in this module: +
        +
      • freePathPair() +
      • getShortest() +
      • getPath() +
      • getLargest() +
      • getCube() +
      • ddBddMaximallyExpand() +
      • ddShortestPathUnate() +
      ] + + Author [Seh-Woong Jeong, Fabio Somenzi] + + Copyright [Copyright (c) 1995-2012, Regents of the University of Colorado + + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + Neither the name of the University of Colorado nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE.] + +******************************************************************************/ + + +/*---------------------------------------------------------------------------*/ +/* Constant declarations */ +/*---------------------------------------------------------------------------*/ + +#define DD_BIGGY 100000000 + +/*---------------------------------------------------------------------------*/ +/* Stucture declarations */ +/*---------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------*/ +/* Type declarations */ +/*---------------------------------------------------------------------------*/ + + typedef struct cuddPathPair { + int pos; + int neg; + } cuddPathPair; + +/*---------------------------------------------------------------------------*/ +/* Variable declarations */ +/*---------------------------------------------------------------------------*/ + +//#ifndef lint +//static char rcsid[] DD_UNUSED = "$Id: cuddSat.c,v 1.39 2012/02/05 01:07:19 fabio Exp $"; +//#endif + + static DdNode *one, *zero; + +/*---------------------------------------------------------------------------*/ +/* Macro declarations */ +/*---------------------------------------------------------------------------*/ + +#define WEIGHT(weight, col) ((weight) == NULL ? 1 : weight[col]) + +#ifdef __cplusplus + extern "C" { +#endif + +/**AutomaticStart*************************************************************/ + +/*---------------------------------------------------------------------------*/ +/* Static function prototypes */ +/*---------------------------------------------------------------------------*/ + + static enum st_retval freePathPair (char *key, char *value, char *arg); + static cuddPathPair getShortest (DdNode *root, int *cost, int *support, st_table *visited); + + static cuddPathPair getLargest (DdNode *root, st_table *visited); + static DdNode * getCube (DdManager *manager, st_table *visited, DdNode *f, int cost); + static DdNode * ddBddMaximallyExpand(DdManager *dd, DdNode *lb, DdNode *ub, DdNode *f); + static int ddBddShortestPathUnate(DdManager *dd, DdNode *f, int *phases, st_table *table); + +/**AutomaticEnd***************************************************************/ + +#ifdef __cplusplus + } +#endif + +/*---------------------------------------------------------------------------*/ +/* Definition of exported functions */ +/*---------------------------------------------------------------------------*/ + +/**Function******************************************************************** + + Synopsis [Finds a largest cube in a DD.] + + Description [Finds a largest cube in a DD. f is the DD we want to + get the largest cube for. The problem is translated into the one of + finding a shortest path in f, when both THEN and ELSE arcs are assumed to + have unit length. This yields a largest cube in the disjoint cover + corresponding to the DD. Therefore, it is not necessarily the largest + implicant of f. Returns the largest cube as a BDD.] + + SideEffects [The number of literals of the cube is returned in the location + pointed by length if it is non-null.] + + SeeAlso [Cudd_ShortestPath] + +******************************************************************************/ + DdNode * + Cudd_LargestCube( + DdManager * manager, + DdNode * f, + int * length) + { + register DdNode *F; + st_table *visited; + DdNode *sol; + cuddPathPair *rootPair; + int complement, cost; + + one = DD_ONE(manager); + zero = DD_ZERO(manager); + + if (f == Cudd_Not(one) || f == zero) { + if (length != NULL) { + *length = DD_BIGGY; + } + return(Cudd_Not(one)); + } + /* From this point on, a path exists. */ + + do { + manager->reordered = 0; + + /* Initialize visited table. */ + visited = st_init_table(st_ptrcmp, st_ptrhash); + + /* Now get the length of the shortest path(s) from f to 1. */ + (void) getLargest(f, visited); + + complement = Cudd_IsComplement(f); + + F = Cudd_Regular(f); + + if (!st_lookup(visited, F, &rootPair)) return(NULL); + + if (complement) { + cost = rootPair->neg; + } else { + cost = rootPair->pos; + } + + /* Recover an actual shortest path. */ + sol = getCube(manager,visited,f,cost); + + st_foreach(visited, freePathPair, NULL); + st_free_table(visited); + + } while (manager->reordered == 1); + + if (length != NULL) { + *length = cost; + } + return(sol); + + } /* end of Cudd_LargestCube */ + + +/**Function******************************************************************** + + Synopsis [Determines whether a BDD is negative unate in a + variable.] + + Description [Determines whether the function represented by BDD f is + negative unate (monotonic decreasing) in variable i. Returns the + constant one is f is unate and the (logical) constant zero if it is not. + This function does not generate any new nodes.] + + SideEffects [None] + + SeeAlso [Cudd_Increasing] + +******************************************************************************/ + DdNode * + Cudd_Decreasing( + DdManager * dd, + DdNode * f, + int i) + { + unsigned int topf, level; + DdNode *F, *fv, *fvn, *res; + DD_CTFP cacheOp; + + statLine(dd); +#ifdef DD_DEBUG + assert(0 <= i && i < dd->size); +#endif + + F = Cudd_Regular(f); + topf = cuddI(dd,F->index); + + /* Check terminal case. If topf > i, f does not depend on var. + ** Therefore, f is unate in i. + */ + level = (unsigned) dd->perm[i]; + if (topf > level) { + return(DD_ONE(dd)); + } + + /* From now on, f is not constant. */ + + /* Check cache. */ + cacheOp = (DD_CTFP) Cudd_Decreasing; + res = cuddCacheLookup2(dd,cacheOp,f,dd->vars[i]); + if (res != NULL) { + return(res); + } + + /* Compute cofactors. */ + fv = cuddT(F); fvn = cuddE(F); + if (F != f) { + fv = Cudd_Not(fv); + fvn = Cudd_Not(fvn); + } + + if (topf == (unsigned) level) { + /* Special case: if fv is regular, fv(1,...,1) = 1; + ** If in addition fvn is complemented, fvn(1,...,1) = 0. + ** But then f(1,1,...,1) > f(0,1,...,1). Hence f is not + ** monotonic decreasing in i. + */ + if (!Cudd_IsComplement(fv) && Cudd_IsComplement(fvn)) { + return(Cudd_Not(DD_ONE(dd))); + } + res = Cudd_bddLeq(dd,fv,fvn) ? DD_ONE(dd) : Cudd_Not(DD_ONE(dd)); + } else { + res = Cudd_Decreasing(dd,fv,i); + if (res == DD_ONE(dd)) { + res = Cudd_Decreasing(dd,fvn,i); + } + } + + cuddCacheInsert2(dd,cacheOp,f,dd->vars[i],res); + return(res); + + } /* end of Cudd_Decreasing */ + + +/**Function******************************************************************** + + Synopsis [Tells whether F and G are identical wherever D is 0.] + + Description [Tells whether F and G are identical wherever D is 0. F + and G are either two ADDs or two BDDs. D is either a 0-1 ADD or a + BDD. The function returns 1 if F and G are equivalent, and 0 + otherwise. No new nodes are created.] + + SideEffects [None] + + SeeAlso [Cudd_bddLeqUnless] + +******************************************************************************/ + int + Cudd_EquivDC( + DdManager * dd, + DdNode * F, + DdNode * G, + DdNode * D) + { + DdNode *tmp, *One, *Gr, *Dr; + DdNode *Fv, *Fvn, *Gv, *Gvn, *Dv, *Dvn; + int res; + unsigned int flevel, glevel, dlevel, top; + + One = DD_ONE(dd); + + statLine(dd); + /* Check terminal cases. */ + if (D == One || F == G) return(1); + if (D == Cudd_Not(One) || D == DD_ZERO(dd) || F == Cudd_Not(G)) return(0); + + /* From now on, D is non-constant. */ + + /* Normalize call to increase cache efficiency. */ + if (F > G) { + tmp = F; + F = G; + G = tmp; + } + if (Cudd_IsComplement(F)) { + F = Cudd_Not(F); + G = Cudd_Not(G); + } + + /* From now on, F is regular. */ + + /* Check cache. */ + tmp = cuddCacheLookup(dd,DD_EQUIV_DC_TAG,F,G,D); + if (tmp != NULL) return(tmp == One); + + /* Find splitting variable. */ + flevel = cuddI(dd,F->index); + Gr = Cudd_Regular(G); + glevel = cuddI(dd,Gr->index); + top = ddMin(flevel,glevel); + Dr = Cudd_Regular(D); + dlevel = dd->perm[Dr->index]; + top = ddMin(top,dlevel); + + /* Compute cofactors. */ + if (top == flevel) { + Fv = cuddT(F); + Fvn = cuddE(F); + } else { + Fv = Fvn = F; + } + if (top == glevel) { + Gv = cuddT(Gr); + Gvn = cuddE(Gr); + if (G != Gr) { + Gv = Cudd_Not(Gv); + Gvn = Cudd_Not(Gvn); + } + } else { + Gv = Gvn = G; + } + if (top == dlevel) { + Dv = cuddT(Dr); + Dvn = cuddE(Dr); + if (D != Dr) { + Dv = Cudd_Not(Dv); + Dvn = Cudd_Not(Dvn); + } + } else { + Dv = Dvn = D; + } + + /* Solve recursively. */ + res = Cudd_EquivDC(dd,Fv,Gv,Dv); + if (res != 0) { + res = Cudd_EquivDC(dd,Fvn,Gvn,Dvn); + } + cuddCacheInsert(dd,DD_EQUIV_DC_TAG,F,G,D,(res) ? One : Cudd_Not(One)); + + return(res); + + } /* end of Cudd_EquivDC */ + + +/**Function******************************************************************** + + Synopsis [Tells whether f is less than of equal to G unless D is 1.] + + Description [Tells whether f is less than of equal to G unless D is + 1. f, g, and D are BDDs. The function returns 1 if f is less than + of equal to G, and 0 otherwise. No new nodes are created.] + + SideEffects [None] + + SeeAlso [Cudd_EquivDC Cudd_bddLeq Cudd_bddIteConstant] + +******************************************************************************/ + int + Cudd_bddLeqUnless( + DdManager *dd, + DdNode *f, + DdNode *g, + DdNode *D) + { + DdNode *tmp, *One, *F, *G; + DdNode *Ft, *Fe, *Gt, *Ge, *Dt, *De; + int res; + unsigned int flevel, glevel, dlevel, top; + + statLine(dd); + + One = DD_ONE(dd); + + /* Check terminal cases. */ + if (f == g || g == One || f == Cudd_Not(One) || D == One || + D == f || D == Cudd_Not(g)) return(1); + /* Check for two-operand cases. */ + if (D == Cudd_Not(One) || D == g || D == Cudd_Not(f)) + return(Cudd_bddLeq(dd,f,g)); + if (g == Cudd_Not(One) || g == Cudd_Not(f)) return(Cudd_bddLeq(dd,f,D)); + if (f == One) return(Cudd_bddLeq(dd,Cudd_Not(g),D)); + + /* From now on, f, g, and D are non-constant, distinct, and + ** non-complementary. */ + + /* Normalize call to increase cache efficiency. We rely on the + ** fact that f <= g unless D is equivalent to not(g) <= not(f) + ** unless D and to f <= D unless g. We make sure that D is + ** regular, and that at most one of f and g is complemented. We also + ** ensure that when two operands can be swapped, the one with the + ** lowest address comes first. */ + + if (Cudd_IsComplement(D)) { + if (Cudd_IsComplement(g)) { + /* Special case: if f is regular and g is complemented, + ** f(1,...,1) = 1 > 0 = g(1,...,1). If D(1,...,1) = 0, return 0. + */ + if (!Cudd_IsComplement(f)) return(0); + /* !g <= D unless !f or !D <= g unless !f */ + tmp = D; + D = Cudd_Not(f); + if (g < tmp) { + f = Cudd_Not(g); + g = tmp; + } else { + f = Cudd_Not(tmp); + } + } else { + if (Cudd_IsComplement(f)) { + /* !D <= !f unless g or !D <= g unless !f */ + tmp = f; + f = Cudd_Not(D); + if (tmp < g) { + D = g; + g = Cudd_Not(tmp); + } else { + D = Cudd_Not(tmp); + } + } else { + /* f <= D unless g or !D <= !f unless g */ + tmp = D; + D = g; + if (tmp < f) { + g = Cudd_Not(f); + f = Cudd_Not(tmp); + } else { + g = tmp; + } + } + } + } else { + if (Cudd_IsComplement(g)) { + if (Cudd_IsComplement(f)) { + /* !g <= !f unless D or !g <= D unless !f */ + tmp = f; + f = Cudd_Not(g); + if (D < tmp) { + g = D; + D = Cudd_Not(tmp); + } else { + g = Cudd_Not(tmp); + } + } else { + /* f <= g unless D or !g <= !f unless D */ + if (g < f) { + tmp = g; + g = Cudd_Not(f); + f = Cudd_Not(tmp); + } + } + } else { + /* f <= g unless D or f <= D unless g */ + if (D < g) { + tmp = D; + D = g; + g = tmp; + } + } + } + + /* From now on, D is regular. */ + + /* Check cache. */ + tmp = cuddCacheLookup(dd,DD_BDD_LEQ_UNLESS_TAG,f,g,D); + if (tmp != NULL) return(tmp == One); + + /* Find splitting variable. */ + F = Cudd_Regular(f); + flevel = dd->perm[F->index]; + G = Cudd_Regular(g); + glevel = dd->perm[G->index]; + top = ddMin(flevel,glevel); + dlevel = dd->perm[D->index]; + top = ddMin(top,dlevel); + + /* Compute cofactors. */ + if (top == flevel) { + Ft = cuddT(F); + Fe = cuddE(F); + if (F != f) { + Ft = Cudd_Not(Ft); + Fe = Cudd_Not(Fe); + } + } else { + Ft = Fe = f; + } + if (top == glevel) { + Gt = cuddT(G); + Ge = cuddE(G); + if (G != g) { + Gt = Cudd_Not(Gt); + Ge = Cudd_Not(Ge); + } + } else { + Gt = Ge = g; + } + if (top == dlevel) { + Dt = cuddT(D); + De = cuddE(D); + } else { + Dt = De = D; + } + + /* Solve recursively. */ + res = Cudd_bddLeqUnless(dd,Ft,Gt,Dt); + if (res != 0) { + res = Cudd_bddLeqUnless(dd,Fe,Ge,De); + } + cuddCacheInsert(dd,DD_BDD_LEQ_UNLESS_TAG,f,g,D,Cudd_NotCond(One,!res)); + + return(res); + + } /* end of Cudd_bddLeqUnless */ + + +/**Function******************************************************************** + + Synopsis [Compares two ADDs for equality within tolerance.] + + Description [Compares two ADDs for equality within tolerance. Two + ADDs are reported to be equal if the maximum difference between them + (the sup norm of their difference) is less than or equal to the + tolerance parameter. Returns 1 if the two ADDs are equal (within + tolerance); 0 otherwise. If parameter pr is positive + the first failure is reported to the standard output.] + + SideEffects [None] + + SeeAlso [] + +******************************************************************************/ + int + Cudd_EqualSupNorm( + DdManager * dd /* manager */, + DdNode * f /* first ADD */, + DdNode * g /* second ADD */, + CUDD_VALUE_TYPE tolerance /* maximum allowed difference */, + int pr /* verbosity level */) + { + DdNode *fv, *fvn, *gv, *gvn, *r; + unsigned int topf, topg; + + statLine(dd); + /* Check terminal cases. */ + if (f == g) return(1); + if (Cudd_IsConstant(f) && Cudd_IsConstant(g)) { + if (ddEqualVal(cuddV(f),cuddV(g),tolerance)) { + return(1); + } else { + if (pr>0) { + (void) fprintf(dd->out,"Offending nodes:\n"); + (void) fprintf(dd->out, + "f: address = %p\t value = %40.30f\n", + (void *) f, cuddV(f)); + (void) fprintf(dd->out, + "g: address = %p\t value = %40.30f\n", + (void *) g, cuddV(g)); + } + return(0); + } + } + + /* We only insert the result in the cache if the comparison is + ** successful. Therefore, if we hit we return 1. */ + r = cuddCacheLookup2(dd,(DD_CTFP)Cudd_EqualSupNorm,f,g); + if (r != NULL) { + return(1); + } + + /* Compute the cofactors and solve the recursive subproblems. */ + topf = cuddI(dd,f->index); + topg = cuddI(dd,g->index); + + if (topf <= topg) {fv = cuddT(f); fvn = cuddE(f);} else {fv = fvn = f;} + if (topg <= topf) {gv = cuddT(g); gvn = cuddE(g);} else {gv = gvn = g;} + + if (!Cudd_EqualSupNorm(dd,fv,gv,tolerance,pr)) return(0); + if (!Cudd_EqualSupNorm(dd,fvn,gvn,tolerance,pr)) return(0); + + cuddCacheInsert2(dd,(DD_CTFP)Cudd_EqualSupNorm,f,g,DD_ONE(dd)); + + return(1); + + } /* end of Cudd_EqualSupNorm */ + + +/**Function******************************************************************** + + Synopsis [Expands cube to a prime implicant of f.] + + Description [Expands cube to a prime implicant of f. Returns the prime + if successful; NULL otherwise. In particular, NULL is returned if cube + is not a real cube or is not an implicant of f.] + + SideEffects [None] + + SeeAlso [Cudd_bddMaximallyExpand] + +******************************************************************************/ + DdNode * + Cudd_bddMakePrime( + DdManager *dd /* manager */, + DdNode *cube /* cube to be expanded */, + DdNode *f /* function of which the cube is to be made a prime */) + { + DdNode *res; + + if (!Cudd_bddLeq(dd,cube,f)) return(NULL); + + do { + dd->reordered = 0; + res = cuddBddMakePrime(dd,cube,f); + } while (dd->reordered == 1); + return(res); + + } /* end of Cudd_bddMakePrime */ + + +/*---------------------------------------------------------------------------*/ +/* Definition of internal functions */ +/*---------------------------------------------------------------------------*/ + + +/**Function******************************************************************** + + Synopsis [Performs the recursive step of Cudd_bddMakePrime.] + + Description [Performs the recursive step of Cudd_bddMakePrime. + Returns the prime if successful; NULL otherwise.] + + SideEffects [None] + + SeeAlso [] + +******************************************************************************/ + DdNode * + cuddBddMakePrime( + DdManager *dd /* manager */, + DdNode *cube /* cube to be expanded */, + DdNode *f /* function of which the cube is to be made a prime */) + { + DdNode *scan; + DdNode *t, *e; + DdNode *res = cube; + DdNode *zero = Cudd_Not(DD_ONE(dd)); + + Cudd_Ref(res); + scan = cube; + while (!Cudd_IsConstant(scan)) { + DdNode *reg = Cudd_Regular(scan); + DdNode *var = dd->vars[reg->index]; + DdNode *expanded = Cudd_bddExistAbstract(dd,res,var); + if (expanded == NULL) { + Cudd_RecursiveDeref(dd,res); + return(NULL); + } + Cudd_Ref(expanded); + if (Cudd_bddLeq(dd,expanded,f)) { + Cudd_RecursiveDeref(dd,res); + res = expanded; + } else { + Cudd_RecursiveDeref(dd,expanded); + } + cuddGetBranches(scan,&t,&e); + if (t == zero) { + scan = e; + } else if (e == zero) { + scan = t; + } else { + Cudd_RecursiveDeref(dd,res); + return(NULL); /* cube is not a cube */ + } + } + + if (scan == DD_ONE(dd)) { + Cudd_Deref(res); + return(res); + } else { + Cudd_RecursiveDeref(dd,res); + return(NULL); + } + + } /* end of cuddBddMakePrime */ + + +/*---------------------------------------------------------------------------*/ +/* Definition of static functions */ +/*---------------------------------------------------------------------------*/ + + +/**Function******************************************************************** + + Synopsis [Frees the entries of the visited symbol table.] + + Description [Frees the entries of the visited symbol table. Returns + ST_CONTINUE.] + + SideEffects [None] + +******************************************************************************/ + static enum st_retval + freePathPair( + char * key, + char * value, + char * arg) + { + cuddPathPair *pair; + + pair = (cuddPathPair *) value; + FREE(pair); + return(ST_CONTINUE); + + } /* end of freePathPair */ + + +/**Function******************************************************************** + + Synopsis [Finds the length of the shortest path(s) in a DD.] + + Description [Finds the length of the shortest path(s) in a DD. + Uses a local symbol table to store the lengths for each + node. Only the lengths for the regular nodes are entered in the table, + because those for the complement nodes are simply obtained by swapping + the two lenghts. + Returns a pair of lengths: the length of the shortest path to 1; + and the length of the shortest path to 0. This is done so as to take + complement arcs into account.] + + SideEffects [Accumulates the support of the DD in support.] + + SeeAlso [] + +******************************************************************************/ + static cuddPathPair + getShortest( + DdNode * root, + int * cost, + int * support, + st_table * visited) + { + cuddPathPair *my_pair, res_pair, pair_T, pair_E; + DdNode *my_root, *T, *E; + int weight; + + my_root = Cudd_Regular(root); + + if (st_lookup(visited, my_root, &my_pair)) { + if (Cudd_IsComplement(root)) { + res_pair.pos = my_pair->neg; + res_pair.neg = my_pair->pos; + } else { + res_pair.pos = my_pair->pos; + res_pair.neg = my_pair->neg; + } + return(res_pair); + } + + /* In the case of a BDD the following test is equivalent to + ** testing whether the BDD is the constant 1. This formulation, + ** however, works for ADDs as well, by assuming the usual + ** dichotomy of 0 and != 0. + */ + if (cuddIsConstant(my_root)) { + if (my_root != zero) { + res_pair.pos = 0; + res_pair.neg = DD_BIGGY; + } else { + res_pair.pos = DD_BIGGY; + res_pair.neg = 0; + } + } else { + T = cuddT(my_root); + E = cuddE(my_root); + + pair_T = getShortest(T, cost, support, visited); + pair_E = getShortest(E, cost, support, visited); + weight = WEIGHT(cost, my_root->index); + res_pair.pos = ddMin(pair_T.pos+weight, pair_E.pos); + res_pair.neg = ddMin(pair_T.neg+weight, pair_E.neg); + + /* Update support. */ + if (support != NULL) { + support[my_root->index] = 1; + } + } + + my_pair = ALLOC(cuddPathPair, 1); + if (my_pair == NULL) { + if (Cudd_IsComplement(root)) { + int tmp = res_pair.pos; + res_pair.pos = res_pair.neg; + res_pair.neg = tmp; + } + return(res_pair); + } + my_pair->pos = res_pair.pos; + my_pair->neg = res_pair.neg; + + st_insert(visited, (char *)my_root, (char *)my_pair); + if (Cudd_IsComplement(root)) { + res_pair.pos = my_pair->neg; + res_pair.neg = my_pair->pos; + } else { + res_pair.pos = my_pair->pos; + res_pair.neg = my_pair->neg; + } + return(res_pair); + + } /* end of getShortest */ + + +/* end of getPath */ + + +/**Function******************************************************************** + + Synopsis [Finds the size of the largest cube(s) in a DD.] + + Description [Finds the size of the largest cube(s) in a DD. + This problem is translated into finding the shortest paths from a node + when both THEN and ELSE arcs have unit lengths. + Uses a local symbol table to store the lengths for each + node. Only the lengths for the regular nodes are entered in the table, + because those for the complement nodes are simply obtained by swapping + the two lenghts. + Returns a pair of lengths: the length of the shortest path to 1; + and the length of the shortest path to 0. This is done so as to take + complement arcs into account.] + + SideEffects [none] + + SeeAlso [] + +******************************************************************************/ + static cuddPathPair + getLargest( + DdNode * root, + st_table * visited) + { + cuddPathPair *my_pair, res_pair, pair_T, pair_E; + DdNode *my_root, *T, *E; + + my_root = Cudd_Regular(root); + + if (st_lookup(visited, my_root, &my_pair)) { + if (Cudd_IsComplement(root)) { + res_pair.pos = my_pair->neg; + res_pair.neg = my_pair->pos; + } else { + res_pair.pos = my_pair->pos; + res_pair.neg = my_pair->neg; + } + return(res_pair); + } + + /* In the case of a BDD the following test is equivalent to + ** testing whether the BDD is the constant 1. This formulation, + ** however, works for ADDs as well, by assuming the usual + ** dichotomy of 0 and != 0. + */ + if (cuddIsConstant(my_root)) { + if (my_root != zero) { + res_pair.pos = 0; + res_pair.neg = DD_BIGGY; + } else { + res_pair.pos = DD_BIGGY; + res_pair.neg = 0; + } + } else { + T = cuddT(my_root); + E = cuddE(my_root); + + pair_T = getLargest(T, visited); + pair_E = getLargest(E, visited); + res_pair.pos = ddMin(pair_T.pos, pair_E.pos) + 1; + res_pair.neg = ddMin(pair_T.neg, pair_E.neg) + 1; + } + + my_pair = ALLOC(cuddPathPair, 1); + if (my_pair == NULL) { /* simply do not cache this result */ + if (Cudd_IsComplement(root)) { + int tmp = res_pair.pos; + res_pair.pos = res_pair.neg; + res_pair.neg = tmp; + } + return(res_pair); + } + my_pair->pos = res_pair.pos; + my_pair->neg = res_pair.neg; + + /* Caching may fail without affecting correctness. */ + st_insert(visited, (char *)my_root, (char *)my_pair); + if (Cudd_IsComplement(root)) { + res_pair.pos = my_pair->neg; + res_pair.neg = my_pair->pos; + } else { + res_pair.pos = my_pair->pos; + res_pair.neg = my_pair->neg; + } + return(res_pair); + + } /* end of getLargest */ + + +/**Function******************************************************************** + + Synopsis [Build a BDD for a largest cube of f.] + + Description [Build a BDD for a largest cube of f. + Given the minimum length from the root, and the minimum + lengths for each node (in visited), apply triangulation at each node. + Of the two children of each node on a shortest path, at least one is + on a shortest path. In case of ties the procedure chooses the THEN + children. + Returns a pointer to the cube BDD representing the path if + successful; NULL otherwise.] + + SideEffects [None] + + SeeAlso [] + +******************************************************************************/ + static DdNode * + getCube( + DdManager * manager, + st_table * visited, + DdNode * f, + int cost) + { + DdNode *sol, *tmp; + DdNode *my_dd, *T, *E; + cuddPathPair *T_pair, *E_pair; + int Tcost, Ecost; + int complement; + + my_dd = Cudd_Regular(f); + complement = Cudd_IsComplement(f); + + sol = one; + cuddRef(sol); + + while (!cuddIsConstant(my_dd)) { + Tcost = cost - 1; + Ecost = cost - 1; + + T = cuddT(my_dd); + E = cuddE(my_dd); + + if (complement) {T = Cudd_Not(T); E = Cudd_Not(E);} + + if (!st_lookup(visited, Cudd_Regular(T), &T_pair)) return(NULL); + if ((Cudd_IsComplement(T) && T_pair->neg == Tcost) || + (!Cudd_IsComplement(T) && T_pair->pos == Tcost)) { + tmp = cuddBddAndRecur(manager,manager->vars[my_dd->index],sol); + if (tmp == NULL) { + Cudd_RecursiveDeref(manager,sol); + return(NULL); + } + cuddRef(tmp); + Cudd_RecursiveDeref(manager,sol); + sol = tmp; + + complement = Cudd_IsComplement(T); + my_dd = Cudd_Regular(T); + cost = Tcost; + continue; + } + if (!st_lookup(visited, Cudd_Regular(E), &E_pair)) return(NULL); + if ((Cudd_IsComplement(E) && E_pair->neg == Ecost) || + (!Cudd_IsComplement(E) && E_pair->pos == Ecost)) { + tmp = cuddBddAndRecur(manager,Cudd_Not(manager->vars[my_dd->index]),sol); + if (tmp == NULL) { + Cudd_RecursiveDeref(manager,sol); + return(NULL); + } + cuddRef(tmp); + Cudd_RecursiveDeref(manager,sol); + sol = tmp; + complement = Cudd_IsComplement(E); + my_dd = Cudd_Regular(E); + cost = Ecost; + continue; + } + (void) fprintf(manager->err,"We shouldn't be here!\n"); + manager->errorCode = CUDD_INTERNAL_ERROR; + return(NULL); + } + + cuddDeref(sol); + return(sol); + + } /* end of getCube */ + + +/**Function******************************************************************** + + Synopsis [Performs the recursive step of Cudd_bddMaximallyExpand.] + + Description [Performs the recursive step of Cudd_bddMaximallyExpand. + Returns set of primes or zero BDD if successful; NULL otherwise. On entry + to this function, ub and lb should be different from the zero BDD. The + function then maintains this invariant.] + + SideEffects [None] + + SeeAlso [] + +******************************************************************************/ + static DdNode * + ddBddMaximallyExpand( + DdManager *dd /* manager */, + DdNode *lb /* cube to be expanded */, + DdNode *ub /* upper bound cube */, + DdNode *f /* function against which to expand */) + { + DdNode *one, *zero, *lbv, *lbvn, *lbnx, *ubv, *ubvn, *fv, *fvn, *res; + DdNode *F, *UB, *LB, *t, *e; + unsigned int top, toplb, topub, topf, index; + + statLine(dd); + /* Terminal cases. */ + one = DD_ONE(dd); + zero = Cudd_Not(one); + assert(ub != zero && lb != zero); + /** There are three major terminal cases in theory: + ** ub -> f : return ub + ** lb == f : return lb + ** not(lb -> f): return zero + ** Only the second case can be checked exactly in constant time. + ** For the others, we check for sufficient conditions. + */ + if (ub == f || f == one) return(ub); + if (lb == f) return(lb); + if (f == zero || ub == Cudd_Not(f) || lb == one || lb == Cudd_Not(f)) + return(zero); + if (!Cudd_IsComplement(lb) && Cudd_IsComplement(f)) return(zero); + + /* Here lb and f are not constant. */ + + /* Check cache. Since lb and ub are cubes, their local reference counts + ** are always 1. Hence, we only check the reference count of f. + */ + F = Cudd_Regular(f); + if (F->ref != 1) { + DdNode *tmp = cuddCacheLookup(dd, DD_BDD_MAX_EXP_TAG, lb, ub, f); + if (tmp != NULL) { + return(tmp); + } + } + + /* Compute cofactors. For lb we use the non-zero one in + ** both branches of the recursion. + */ + LB = Cudd_Regular(lb); + UB = Cudd_Regular(ub); + topf = dd->perm[F->index]; + toplb = dd->perm[LB->index]; + topub = (ub == one) ? CUDD_CONST_INDEX : dd->perm[UB->index]; + assert(toplb <= topub); + top = ddMin(topf,toplb); + if (toplb == top) { + index = LB->index; + lbv = cuddT(LB); + lbvn = cuddE(LB); + if (lb != LB) { + lbv = Cudd_Not(lbv); + lbvn = Cudd_Not(lbvn); + } + if (lbv == zero) { + lbnx = lbvn; + } else { + lbnx = lbv; + } + } else { + index = F->index; + lbnx = lbv = lbvn = lb; + } + if (topub == top) { + ubv = cuddT(UB); + ubvn = cuddE(UB); + if (ub != UB) { + ubv = Cudd_Not(ubv); + ubvn = Cudd_Not(ubvn); + } + } else { + ubv = ubvn = ub; + } + if (topf == top) { + fv = cuddT(F); + fvn = cuddE(F); + if (f != F) { + fv = Cudd_Not(fv); + fvn = Cudd_Not(fvn); + } + } else { + fv = fvn = f; + } + + /* Recursive calls. */ + if (ubv != zero) { + t = ddBddMaximallyExpand(dd, lbnx, ubv, fv); + if (t == NULL) return(NULL); + } else { + assert(topub == toplb && topub == top && lbv == zero); + t = zero; + } + cuddRef(t); + + /* If the top variable appears only in lb, the positive and negative + ** cofactors of each operand are the same. We want to avoid a + ** needless recursive call, which would force us to give up the + ** cache optimization trick based on reference counts. + */ + if (ubv == ubvn && fv == fvn) { + res = t; + } else { + if (ubvn != zero) { + e = ddBddMaximallyExpand(dd, lbnx, ubvn, fvn); + if (e == NULL) { + Cudd_IterDerefBdd(dd,t); + return(NULL); + } + } else { + assert(topub == toplb && topub == top && lbvn == zero); + e = zero; + } + + if (t == e) { + res = t; + } else { + cuddRef(e); + + if (toplb == top) { + if (lbv == zero) { + /* Top variable appears in negative phase. */ + if (t != one) { + DdNode *newT; + if (Cudd_IsComplement(t)) { + newT = cuddUniqueInter(dd, index, Cudd_Not(t), zero); + if (newT == NULL) { + Cudd_IterDerefBdd(dd,t); + Cudd_IterDerefBdd(dd,e); + return(NULL); + } + newT = Cudd_Not(newT); + } else { + newT = cuddUniqueInter(dd, index, t, one); + if (newT == NULL) { + Cudd_IterDerefBdd(dd,t); + Cudd_IterDerefBdd(dd,e); + return(NULL); + } + } + cuddRef(newT); + cuddDeref(t); + t = newT; + } + } else if (lbvn == zero) { + /* Top variable appears in positive phase. */ + if (e != one) { + DdNode *newE; + newE = cuddUniqueInter(dd, index, one, e); + if (newE == NULL) { + Cudd_IterDerefBdd(dd,t); + Cudd_IterDerefBdd(dd,e); + return(NULL); + } + cuddRef(newE); + cuddDeref(e); + e = newE; + } + } else { + /* Not a cube. */ + Cudd_IterDerefBdd(dd,t); + Cudd_IterDerefBdd(dd,e); + return(NULL); + } + } + + /* Combine results. */ + res = cuddBddAndRecur(dd, t, e); + if (res == NULL) { + Cudd_IterDerefBdd(dd,t); + Cudd_IterDerefBdd(dd,e); + return(NULL); + } + cuddRef(res); + Cudd_IterDerefBdd(dd,t); + Cudd_IterDerefBdd(dd,e); + } + } + + /* Cache result and return. */ + if (F->ref != 1) { + cuddCacheInsert(dd, DD_BDD_MAX_EXP_TAG, lb, ub, f, res); + } + cuddDeref(res); + return(res); + + } /* end of ddBddMaximallyExpand */ + + +/**Function******************************************************************** + + Synopsis [Performs shortest path computation on a unate function.] + + Description [Performs shortest path computation on a unate function. + Returns the length of the shortest path to one if successful; + CUDD_OUT_OF_MEM otherwise. This function is based on the observation + that in the BDD of a unate function no node except the constant is + reachable from the root via paths of different parity.] + + SideEffects [None] + + SeeAlso [getShortest] + +******************************************************************************/ + static int + ddBddShortestPathUnate( + DdManager *dd, + DdNode *f, + int *phases, + st_table *table) + { + int positive, l, lT, lE; + DdNode *one = DD_ONE(dd); + DdNode *zero = Cudd_Not(one); + DdNode *F, *fv, *fvn; + + if (st_lookup_int(table, f, &l)) { + return(l); + } + if (f == one) { + l = 0; + } else if (f == zero) { + l = DD_BIGGY; + } else { + F = Cudd_Regular(f); + fv = cuddT(F); + fvn = cuddE(F); + if (f != F) { + fv = Cudd_Not(fv); + fvn = Cudd_Not(fvn); + } + lT = ddBddShortestPathUnate(dd, fv, phases, table); + lE = ddBddShortestPathUnate(dd, fvn, phases, table); + positive = phases[F->index]; + l = positive ? ddMin(lT+1, lE) : ddMin(lT, lE+1); + } + if (st_insert(table, f, (void *)(ptrint) l) == ST_OUT_OF_MEM) { + return(CUDD_OUT_OF_MEM); + } + return(l); + + } /* end of ddShortestPathUnate */ + + +/* end of ddGetLargestCubeUnate */ +/**CFile*********************************************************************** + + FileName [cuddSymmetry.c] + + PackageName [cudd] + + Synopsis [Functions for symmetry-based variable reordering.] + + Description [External procedures included in this file: +
        +
      • Cudd_SymmProfile() +
      + Internal procedures included in this module: +
        +
      • cuddSymmCheck() +
      • cuddSymmSifting() +
      • cuddSymmSiftingConv() +
      + Static procedures included in this module: +
        +
      • ddSymmUniqueCompare() +
      • ddSymmSiftingAux() +
      • ddSymmSiftingConvAux() +
      • ddSymmSiftingUp() +
      • ddSymmSiftingDown() +
      • ddSymmGroupMove() +
      • ddSymmGroupMoveBackward() +
      • ddSymmSiftingBackward() +
      • ddSymmSummary() +
      ] + + Author [Shipra Panda, Fabio Somenzi] + + Copyright [Copyright (c) 1995-2012, Regents of the University of Colorado + + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + Neither the name of the University of Colorado nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE.] + +******************************************************************************/ + + +/*---------------------------------------------------------------------------*/ +/* Constant declarations */ +/*---------------------------------------------------------------------------*/ + +#define MV_OOM (Move *)1 + +/*---------------------------------------------------------------------------*/ +/* Stucture declarations */ +/*---------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------*/ +/* Type declarations */ +/*---------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------*/ +/* Variable declarations */ +/*---------------------------------------------------------------------------*/ + +//#ifndef lint +//static char rcsid[] DD_UNUSED = "$Id: cuddSymmetry.c,v 1.28 2012/02/05 01:07:19 fabio Exp $"; +//#endif + + static int *entry; + + extern int ddTotalNumberSwapping; +#ifdef DD_STATS + extern int ddTotalNISwaps; +#endif + +/*---------------------------------------------------------------------------*/ +/* Macro declarations */ +/*---------------------------------------------------------------------------*/ + +/**AutomaticStart*************************************************************/ + +/*---------------------------------------------------------------------------*/ +/* Static function prototypes */ +/*---------------------------------------------------------------------------*/ + + static int ddSymmUniqueCompare (int *ptrX, int *ptrY); + static int ddSymmSiftingAux (DdManager *table, int x, int xLow, int xHigh); + static int ddSymmSiftingConvAux (DdManager *table, int x, int xLow, int xHigh); + static Move * ddSymmSiftingUp (DdManager *table, int y, int xLow); + static Move * ddSymmSiftingDown (DdManager *table, int x, int xHigh); + static int ddSymmGroupMove (DdManager *table, int x, int y, Move **moves); + static int ddSymmGroupMoveBackward (DdManager *table, int x, int y); + static int ddSymmSiftingBackward (DdManager *table, Move *moves, int size); + static void ddSymmSummary (DdManager *table, int lower, int upper, int *symvars, int *symgroups); + +/**AutomaticEnd***************************************************************/ + + +/*---------------------------------------------------------------------------*/ +/* Definition of exported functions */ +/*---------------------------------------------------------------------------*/ + + +/*---------------------------------------------------------------------------*/ +/* Definition of internal functions */ +/*---------------------------------------------------------------------------*/ + + +/**Function******************************************************************** + + Synopsis [Checks for symmetry of x and y.] + + Description [Checks for symmetry of x and y. Ignores projection + functions, unless they are isolated. Returns 1 in case of symmetry; 0 + otherwise.] + + SideEffects [None] + +******************************************************************************/ + int + cuddSymmCheck( + DdManager * table, + int x, + int y) + { + DdNode *f,*f0,*f1,*f01,*f00,*f11,*f10; + int comple; /* f0 is complemented */ + int xsymmy; /* x and y may be positively symmetric */ + int xsymmyp; /* x and y may be negatively symmetric */ + int arccount; /* number of arcs from layer x to layer y */ + int TotalRefCount; /* total reference count of layer y minus 1 */ + int yindex; + int i; + DdNodePtr *list; + int slots; + DdNode *sentinel = &(table->sentinel); +#ifdef DD_DEBUG + int xindex; +#endif + + /* Checks that x and y are not the projection functions. + ** For x it is sufficient to check whether there is only one + ** node; indeed, if there is one node, it is the projection function + ** and it cannot point to y. Hence, if y isn't just the projection + ** function, it has one arc coming from a layer different from x. + */ + if (table->subtables[x].keys == 1) { + return(0); + } + yindex = table->invperm[y]; + if (table->subtables[y].keys == 1) { + if (table->vars[yindex]->ref == 1) + return(0); + } + + xsymmy = xsymmyp = 1; + arccount = 0; + slots = table->subtables[x].slots; + list = table->subtables[x].nodelist; + for (i = 0; i < slots; i++) { + f = list[i]; + while (f != sentinel) { + /* Find f1, f0, f11, f10, f01, f00. */ + f1 = cuddT(f); + f0 = Cudd_Regular(cuddE(f)); + comple = Cudd_IsComplement(cuddE(f)); + if ((int) f1->index == yindex) { + arccount++; + f11 = cuddT(f1); f10 = cuddE(f1); + } else { + if ((int) f0->index != yindex) { + /* If f is an isolated projection function it is + ** allowed to bypass layer y. + */ + if (f1 != DD_ONE(table) || f0 != DD_ONE(table) || f->ref != 1) + return(0); /* f bypasses layer y */ + } + f11 = f10 = f1; + } + if ((int) f0->index == yindex) { + arccount++; + f01 = cuddT(f0); f00 = cuddE(f0); + } else { + f01 = f00 = f0; + } + if (comple) { + f01 = Cudd_Not(f01); + f00 = Cudd_Not(f00); + } + + if (f1 != DD_ONE(table) || f0 != DD_ONE(table) || f->ref != 1) { + xsymmy &= f01 == f10; + xsymmyp &= f11 == f00; + if ((xsymmy == 0) && (xsymmyp == 0)) + return(0); + } + + f = f->next; + } /* while */ + } /* for */ + + /* Calculate the total reference counts of y */ + TotalRefCount = -1; /* -1 for projection function */ + slots = table->subtables[y].slots; + list = table->subtables[y].nodelist; + for (i = 0; i < slots; i++) { + f = list[i]; + while (f != sentinel) { + TotalRefCount += f->ref; + f = f->next; + } + } + +#if defined(DD_DEBUG) && defined(DD_VERBOSE) + if (arccount == TotalRefCount) { + xindex = table->invperm[x]; + (void) fprintf(table->out, + "Found symmetry! x =%d\ty = %d\tPos(%d,%d)\n", + xindex,yindex,x,y); + } +#endif + + return(arccount == TotalRefCount); + + } /* end of cuddSymmCheck */ + + +/**Function******************************************************************** + + Synopsis [Symmetric sifting algorithm.] + + Description [Symmetric sifting algorithm. + Assumes that no dead nodes are present. +
        +
      1. Order all the variables according to the number of entries in + each unique subtable. +
      2. Sift the variable up and down, remembering each time the total + size of the DD heap and grouping variables that are symmetric. +
      3. Select the best permutation. +
      4. Repeat 3 and 4 for all variables. +
      + Returns 1 plus the number of symmetric variables if successful; 0 + otherwise.] + + SideEffects [None] + + SeeAlso [cuddSymmSiftingConv] + +******************************************************************************/ + int + cuddSymmSifting( + DdManager * table, + int lower, + int upper) + { + int i; + int *var; + int size; + int x; + int result; + int symvars; + int symgroups; +#ifdef DD_STATS + int previousSize; +#endif + + size = table->size; + + /* Find order in which to sift variables. */ + var = NULL; + entry = ALLOC(int,size); + if (entry == NULL) { + table->errorCode = CUDD_MEMORY_OUT; + goto ddSymmSiftingOutOfMem; + } + var = ALLOC(int,size); + if (var == NULL) { + table->errorCode = CUDD_MEMORY_OUT; + goto ddSymmSiftingOutOfMem; + } + + for (i = 0; i < size; i++) { + x = table->perm[i]; + entry[i] = table->subtables[x].keys; + var[i] = i; + } + + qsort((void *)var,size,sizeof(int),(DD_QSFP)ddSymmUniqueCompare); + + /* Initialize the symmetry of each subtable to itself. */ + for (i = lower; i <= upper; i++) { + table->subtables[i].next = i; + } + + for (i = 0; i < ddMin(table->siftMaxVar,size); i++) { + if (ddTotalNumberSwapping >= table->siftMaxSwap) + break; + if (util_cpu_time() - table->startTime > table->timeLimit) { + table->autoDyn = 0; /* prevent further reordering */ + break; + } + x = table->perm[var[i]]; +#ifdef DD_STATS + previousSize = table->keys - table->isolated; +#endif + if (x < lower || x > upper) continue; + if (table->subtables[x].next == (unsigned) x) { + result = ddSymmSiftingAux(table,x,lower,upper); + if (!result) goto ddSymmSiftingOutOfMem; +#ifdef DD_STATS + if (table->keys < (unsigned) previousSize + table->isolated) { + (void) fprintf(table->out,"-"); + } else if (table->keys > (unsigned) previousSize + + table->isolated) { + (void) fprintf(table->out,"+"); /* should never happen */ + } else { + (void) fprintf(table->out,"="); + } + fflush(table->out); +#endif + } + } + + FREE(var); + FREE(entry); + + ddSymmSummary(table, lower, upper, &symvars, &symgroups); + +#ifdef DD_STATS + (void) fprintf(table->out, "\n#:S_SIFTING %8d: symmetric variables\n", + symvars); + (void) fprintf(table->out, "#:G_SIFTING %8d: symmetric groups", + symgroups); +#endif + + return(1+symvars); + + ddSymmSiftingOutOfMem: + + if (entry != NULL) FREE(entry); + if (var != NULL) FREE(var); + + return(0); + + } /* end of cuddSymmSifting */ + + +/**Function******************************************************************** + + Synopsis [Symmetric sifting to convergence algorithm.] + + Description [Symmetric sifting to convergence algorithm. + Assumes that no dead nodes are present. +
        +
      1. Order all the variables according to the number of entries in + each unique subtable. +
      2. Sift the variable up and down, remembering each time the total + size of the DD heap and grouping variables that are symmetric. +
      3. Select the best permutation. +
      4. Repeat 3 and 4 for all variables. +
      5. Repeat 1-4 until no further improvement. +
      + Returns 1 plus the number of symmetric variables if successful; 0 + otherwise.] + + SideEffects [None] + + SeeAlso [cuddSymmSifting] + +******************************************************************************/ + int + cuddSymmSiftingConv( + DdManager * table, + int lower, + int upper) + { + int i; + int *var; + int size; + int x; + int result; + int symvars; + int symgroups; + int classes; + int initialSize; +#ifdef DD_STATS + int previousSize; +#endif + + initialSize = table->keys - table->isolated; + + size = table->size; + + /* Find order in which to sift variables. */ + var = NULL; + entry = ALLOC(int,size); + if (entry == NULL) { + table->errorCode = CUDD_MEMORY_OUT; + goto ddSymmSiftingConvOutOfMem; + } + var = ALLOC(int,size); + if (var == NULL) { + table->errorCode = CUDD_MEMORY_OUT; + goto ddSymmSiftingConvOutOfMem; + } + + for (i = 0; i < size; i++) { + x = table->perm[i]; + entry[i] = table->subtables[x].keys; + var[i] = i; + } + + qsort((void *)var,size,sizeof(int),(DD_QSFP)ddSymmUniqueCompare); + + /* Initialize the symmetry of each subtable to itself + ** for first pass of converging symmetric sifting. + */ + for (i = lower; i <= upper; i++) { + table->subtables[i].next = i; + } + + for (i = 0; i < ddMin(table->siftMaxVar, table->size); i++) { + if (ddTotalNumberSwapping >= table->siftMaxSwap) + break; + if (util_cpu_time() - table->startTime > table->timeLimit) { + table->autoDyn = 0; /* prevent further reordering */ + break; + } + x = table->perm[var[i]]; + if (x < lower || x > upper) continue; + /* Only sift if not in symmetry group already. */ + if (table->subtables[x].next == (unsigned) x) { +#ifdef DD_STATS + previousSize = table->keys - table->isolated; +#endif + result = ddSymmSiftingAux(table,x,lower,upper); + if (!result) goto ddSymmSiftingConvOutOfMem; +#ifdef DD_STATS +if (table->keys < (unsigned) previousSize + table->isolated) { +(void) fprintf(table->out,"-"); +} else if (table->keys > (unsigned) previousSize + +table->isolated) { +(void) fprintf(table->out,"+"); +} else { +(void) fprintf(table->out,"="); +} +fflush(table->out); +#endif +} +} + +/* Sifting now until convergence. */ +while ((unsigned) initialSize > table->keys - table->isolated) { +initialSize = table->keys - table->isolated; +#ifdef DD_STATS +(void) fprintf(table->out,"\n"); +#endif +/* Here we consider only one representative for each symmetry class. */ +for (x = lower, classes = 0; x <= upper; x++, classes++) { +while ((unsigned) x < table->subtables[x].next) { +x = table->subtables[x].next; +} +/* Here x is the largest index in a group. +** Groups consist of adjacent variables. +** Hence, the next increment of x will move it to a new group. +*/ +i = table->invperm[x]; +entry[i] = table->subtables[x].keys; +var[classes] = i; +} + +qsort((void *)var,classes,sizeof(int),(DD_QSFP)ddSymmUniqueCompare); + +/* Now sift. */ +for (i = 0; i < ddMin(table->siftMaxVar,classes); i++) { +if (ddTotalNumberSwapping >= table->siftMaxSwap) +break; +if (util_cpu_time() - table->startTime > table->timeLimit) { +table->autoDyn = 0; /* prevent further reordering */ +break; +} +x = table->perm[var[i]]; +if ((unsigned) x >= table->subtables[x].next) { +#ifdef DD_STATS +previousSize = table->keys - table->isolated; +#endif +result = ddSymmSiftingConvAux(table,x,lower,upper); +if (!result ) goto ddSymmSiftingConvOutOfMem; +#ifdef DD_STATS +if (table->keys < (unsigned) previousSize + table->isolated) { +(void) fprintf(table->out,"-"); +} else if (table->keys > (unsigned) previousSize + +table->isolated) { +(void) fprintf(table->out,"+"); +} else { +(void) fprintf(table->out,"="); +} +fflush(table->out); +#endif +} +} /* for */ +} + +ddSymmSummary(table, lower, upper, &symvars, &symgroups); + +#ifdef DD_STATS +(void) fprintf(table->out, "\n#:S_SIFTING %8d: symmetric variables\n", +symvars); +(void) fprintf(table->out, "#:G_SIFTING %8d: symmetric groups", +symgroups); +#endif + +FREE(var); +FREE(entry); + +return(1+symvars); + +ddSymmSiftingConvOutOfMem: + +if (entry != NULL) FREE(entry); +if (var != NULL) FREE(var); + +return(0); + +} /* end of cuddSymmSiftingConv */ + + +/*---------------------------------------------------------------------------*/ +/* Definition of static functions */ +/*---------------------------------------------------------------------------*/ + + +/**Function******************************************************************** + + Synopsis [Comparison function used by qsort.] + + Description [Comparison function used by qsort to order the variables + according to the number of keys in the subtables. + Returns the difference in number of keys between the two + variables being compared.] + + SideEffects [None] + +******************************************************************************/ +static int +ddSymmUniqueCompare( +int * ptrX, +int * ptrY) +{ +#if 0 +if (entry[*ptrY] == entry[*ptrX]) { +return((*ptrX) - (*ptrY)); +} +#endif +return(entry[*ptrY] - entry[*ptrX]); + +} /* end of ddSymmUniqueCompare */ + + +/**Function******************************************************************** + + Synopsis [Given xLow <= x <= xHigh moves x up and down between the + boundaries.] + + Description [Given xLow <= x <= xHigh moves x up and down between the + boundaries. Finds the best position and does the required changes. + Assumes that x is not part of a symmetry group. Returns 1 if + successful; 0 otherwise.] + + SideEffects [None] + +******************************************************************************/ +static int +ddSymmSiftingAux( +DdManager * table, +int x, +int xLow, +int xHigh) +{ +Move *move; +Move *moveUp; /* list of up moves */ +Move *moveDown; /* list of down moves */ +int initialSize; +int result; +int i; +int topbot; /* index to either top or bottom of symmetry group */ +int initGroupSize, finalGroupSize; + + +#ifdef DD_DEBUG +/* check for previously detected symmetry */ +assert(table->subtables[x].next == (unsigned) x); +#endif + +initialSize = table->keys - table->isolated; + +moveDown = NULL; +moveUp = NULL; + +if ((x - xLow) > (xHigh - x)) { +/* Will go down first, unless x == xHigh: +** Look for consecutive symmetries above x. +*/ +for (i = x; i > xLow; i--) { +if (!cuddSymmCheck(table,i-1,i)) +break; +topbot = table->subtables[i-1].next; /* find top of i-1's group */ +table->subtables[i-1].next = i; +table->subtables[x].next = topbot; /* x is bottom of group so its */ +/* next is top of i-1's group */ +i = topbot + 1; /* add 1 for i--; new i is top of symm group */ +} +} else { +/* Will go up first unless x == xlow: +** Look for consecutive symmetries below x. +*/ +for (i = x; i < xHigh; i++) { +if (!cuddSymmCheck(table,i,i+1)) +break; +/* find bottom of i+1's symm group */ +topbot = i + 1; +while ((unsigned) topbot < table->subtables[topbot].next) { +topbot = table->subtables[topbot].next; +} +table->subtables[topbot].next = table->subtables[i].next; +table->subtables[i].next = i + 1; +i = topbot - 1; /* subtract 1 for i++; new i is bottom of group */ +} +} + +/* Now x may be in the middle of a symmetry group. +** Find bottom of x's symm group. +*/ +while ((unsigned) x < table->subtables[x].next) +x = table->subtables[x].next; + +if (x == xLow) { /* Sift down */ + +#ifdef DD_DEBUG +/* x must be a singleton */ +assert((unsigned) x == table->subtables[x].next); +#endif +if (x == xHigh) return(1); /* just one variable */ + +initGroupSize = 1; + +moveDown = ddSymmSiftingDown(table,x,xHigh); +/* after this point x --> xHigh, unless early term */ +if (moveDown == MV_OOM) goto ddSymmSiftingAuxOutOfMem; +if (moveDown == NULL) return(1); + +x = moveDown->y; +/* Find bottom of x's group */ +i = x; +while ((unsigned) i < table->subtables[i].next) { +i = table->subtables[i].next; +} +#ifdef DD_DEBUG +/* x should be the top of the symmetry group and i the bottom */ +assert((unsigned) i >= table->subtables[i].next); +assert((unsigned) x == table->subtables[i].next); +#endif +finalGroupSize = i - x + 1; + +if (initGroupSize == finalGroupSize) { +/* No new symmetry groups detected, return to best position */ +result = ddSymmSiftingBackward(table,moveDown,initialSize); +} else { +initialSize = table->keys - table->isolated; +moveUp = ddSymmSiftingUp(table,x,xLow); +result = ddSymmSiftingBackward(table,moveUp,initialSize); +} +if (!result) goto ddSymmSiftingAuxOutOfMem; + +} else if (cuddNextHigh(table,x) > xHigh) { /* Sift up */ +/* Find top of x's symm group */ +i = x; /* bottom */ +x = table->subtables[x].next; /* top */ + +if (x == xLow) return(1); /* just one big group */ + +initGroupSize = i - x + 1; + +moveUp = ddSymmSiftingUp(table,x,xLow); +/* after this point x --> xLow, unless early term */ +if (moveUp == MV_OOM) goto ddSymmSiftingAuxOutOfMem; +if (moveUp == NULL) return(1); + +x = moveUp->x; +/* Find top of x's group */ +i = table->subtables[x].next; +#ifdef DD_DEBUG +/* x should be the bottom of the symmetry group and i the top */ +assert((unsigned) x >= table->subtables[x].next); +assert((unsigned) i == table->subtables[x].next); +#endif +finalGroupSize = x - i + 1; + +if (initGroupSize == finalGroupSize) { +/* No new symmetry groups detected, return to best position */ +result = ddSymmSiftingBackward(table,moveUp,initialSize); +} else { +initialSize = table->keys - table->isolated; +moveDown = ddSymmSiftingDown(table,x,xHigh); +result = ddSymmSiftingBackward(table,moveDown,initialSize); +} +if (!result) goto ddSymmSiftingAuxOutOfMem; + +} else if ((x - xLow) > (xHigh - x)) { /* must go down first: shorter */ + +moveDown = ddSymmSiftingDown(table,x,xHigh); +/* at this point x == xHigh, unless early term */ +if (moveDown == MV_OOM) goto ddSymmSiftingAuxOutOfMem; + +if (moveDown != NULL) { +x = moveDown->y; /* x is top here */ +i = x; +while ((unsigned) i < table->subtables[i].next) { +i = table->subtables[i].next; +} +} else { +i = x; +while ((unsigned) i < table->subtables[i].next) { +i = table->subtables[i].next; +} +x = table->subtables[i].next; +} +#ifdef DD_DEBUG +/* x should be the top of the symmetry group and i the bottom */ +assert((unsigned) i >= table->subtables[i].next); +assert((unsigned) x == table->subtables[i].next); +#endif +initGroupSize = i - x + 1; + +moveUp = ddSymmSiftingUp(table,x,xLow); +if (moveUp == MV_OOM) goto ddSymmSiftingAuxOutOfMem; + +if (moveUp != NULL) { +x = moveUp->x; +i = table->subtables[x].next; +} else { +i = x; +while ((unsigned) x < table->subtables[x].next) +x = table->subtables[x].next; +} +#ifdef DD_DEBUG +/* x should be the bottom of the symmetry group and i the top */ +assert((unsigned) x >= table->subtables[x].next); +assert((unsigned) i == table->subtables[x].next); +#endif +finalGroupSize = x - i + 1; + +if (initGroupSize == finalGroupSize) { +/* No new symmetry groups detected, return to best position */ +result = ddSymmSiftingBackward(table,moveUp,initialSize); +} else { +while (moveDown != NULL) { +move = moveDown->next; +cuddDeallocMove(table, moveDown); +moveDown = move; +} +initialSize = table->keys - table->isolated; +moveDown = ddSymmSiftingDown(table,x,xHigh); +result = ddSymmSiftingBackward(table,moveDown,initialSize); +} +if (!result) goto ddSymmSiftingAuxOutOfMem; + +} else { /* moving up first: shorter */ +/* Find top of x's symmetry group */ +x = table->subtables[x].next; + +moveUp = ddSymmSiftingUp(table,x,xLow); +/* at this point x == xHigh, unless early term */ +if (moveUp == MV_OOM) goto ddSymmSiftingAuxOutOfMem; + +if (moveUp != NULL) { +x = moveUp->x; +i = table->subtables[x].next; +} else { +while ((unsigned) x < table->subtables[x].next) +x = table->subtables[x].next; +i = table->subtables[x].next; +} +#ifdef DD_DEBUG +/* x is bottom of the symmetry group and i is top */ +assert((unsigned) x >= table->subtables[x].next); +assert((unsigned) i == table->subtables[x].next); +#endif +initGroupSize = x - i + 1; + +moveDown = ddSymmSiftingDown(table,x,xHigh); +if (moveDown == MV_OOM) goto ddSymmSiftingAuxOutOfMem; + +if (moveDown != NULL) { +x = moveDown->y; +i = x; +while ((unsigned) i < table->subtables[i].next) { +i = table->subtables[i].next; +} +} else { +i = x; +x = table->subtables[x].next; +} +#ifdef DD_DEBUG +/* x should be the top of the symmetry group and i the bottom */ +assert((unsigned) i >= table->subtables[i].next); +assert((unsigned) x == table->subtables[i].next); +#endif +finalGroupSize = i - x + 1; + +if (initGroupSize == finalGroupSize) { +/* No new symmetries detected, go back to best position */ +result = ddSymmSiftingBackward(table,moveDown,initialSize); +} else { +while (moveUp != NULL) { +move = moveUp->next; +cuddDeallocMove(table, moveUp); +moveUp = move; +} +initialSize = table->keys - table->isolated; +moveUp = ddSymmSiftingUp(table,x,xLow); +result = ddSymmSiftingBackward(table,moveUp,initialSize); +} +if (!result) goto ddSymmSiftingAuxOutOfMem; +} + +while (moveDown != NULL) { +move = moveDown->next; +cuddDeallocMove(table, moveDown); +moveDown = move; +} +while (moveUp != NULL) { +move = moveUp->next; +cuddDeallocMove(table, moveUp); +moveUp = move; +} + +return(1); + +ddSymmSiftingAuxOutOfMem: +if (moveDown != MV_OOM) { +while (moveDown != NULL) { +move = moveDown->next; +cuddDeallocMove(table, moveDown); +moveDown = move; +} +} +if (moveUp != MV_OOM) { +while (moveUp != NULL) { +move = moveUp->next; +cuddDeallocMove(table, moveUp); +moveUp = move; +} +} + +return(0); + +} /* end of ddSymmSiftingAux */ + + +/**Function******************************************************************** + + Synopsis [Given xLow <= x <= xHigh moves x up and down between the + boundaries.] + + Description [Given xLow <= x <= xHigh moves x up and down between the + boundaries. Finds the best position and does the required changes. + Assumes that x is either an isolated variable, or it is the bottom of + a symmetry group. All symmetries may not have been found, because of + exceeded growth limit. Returns 1 if successful; 0 otherwise.] + + SideEffects [None] + +******************************************************************************/ +static int +ddSymmSiftingConvAux( +DdManager * table, +int x, +int xLow, +int xHigh) +{ +Move *move; +Move *moveUp; /* list of up moves */ +Move *moveDown; /* list of down moves */ +int initialSize; +int result; +int i; +int initGroupSize, finalGroupSize; + + +initialSize = table->keys - table->isolated; + +moveDown = NULL; +moveUp = NULL; + +if (x == xLow) { /* Sift down */ +#ifdef DD_DEBUG +/* x is bottom of symmetry group */ +assert((unsigned) x >= table->subtables[x].next); +#endif +i = table->subtables[x].next; +initGroupSize = x - i + 1; + +moveDown = ddSymmSiftingDown(table,x,xHigh); +/* at this point x == xHigh, unless early term */ +if (moveDown == MV_OOM) goto ddSymmSiftingConvAuxOutOfMem; +if (moveDown == NULL) return(1); + +x = moveDown->y; +i = x; +while ((unsigned) i < table->subtables[i].next) { +i = table->subtables[i].next; +} +#ifdef DD_DEBUG +/* x should be the top of the symmetric group and i the bottom */ +assert((unsigned) i >= table->subtables[i].next); +assert((unsigned) x == table->subtables[i].next); +#endif +finalGroupSize = i - x + 1; + +if (initGroupSize == finalGroupSize) { +/* No new symmetries detected, go back to best position */ +result = ddSymmSiftingBackward(table,moveDown,initialSize); +} else { +initialSize = table->keys - table->isolated; +moveUp = ddSymmSiftingUp(table,x,xLow); +result = ddSymmSiftingBackward(table,moveUp,initialSize); +} +if (!result) goto ddSymmSiftingConvAuxOutOfMem; + +} else if (cuddNextHigh(table,x) > xHigh) { /* Sift up */ +/* Find top of x's symm group */ +while ((unsigned) x < table->subtables[x].next) +x = table->subtables[x].next; +i = x; /* bottom */ +x = table->subtables[x].next; /* top */ + +if (x == xLow) return(1); + +initGroupSize = i - x + 1; + +moveUp = ddSymmSiftingUp(table,x,xLow); +/* at this point x == xLow, unless early term */ +if (moveUp == MV_OOM) goto ddSymmSiftingConvAuxOutOfMem; +if (moveUp == NULL) return(1); + +x = moveUp->x; +i = table->subtables[x].next; +#ifdef DD_DEBUG +/* x should be the bottom of the symmetry group and i the top */ +assert((unsigned) x >= table->subtables[x].next); +assert((unsigned) i == table->subtables[x].next); +#endif +finalGroupSize = x - i + 1; + +if (initGroupSize == finalGroupSize) { +/* No new symmetry groups detected, return to best position */ +result = ddSymmSiftingBackward(table,moveUp,initialSize); +} else { +initialSize = table->keys - table->isolated; +moveDown = ddSymmSiftingDown(table,x,xHigh); +result = ddSymmSiftingBackward(table,moveDown,initialSize); +} +if (!result) +goto ddSymmSiftingConvAuxOutOfMem; + +} else if ((x - xLow) > (xHigh - x)) { /* must go down first: shorter */ +moveDown = ddSymmSiftingDown(table,x,xHigh); +/* at this point x == xHigh, unless early term */ +if (moveDown == MV_OOM) goto ddSymmSiftingConvAuxOutOfMem; + +if (moveDown != NULL) { +x = moveDown->y; +i = x; +while ((unsigned) i < table->subtables[i].next) { +i = table->subtables[i].next; +} +} else { +while ((unsigned) x < table->subtables[x].next) +x = table->subtables[x].next; +i = x; +x = table->subtables[x].next; +} +#ifdef DD_DEBUG +/* x should be the top of the symmetry group and i the bottom */ +assert((unsigned) i >= table->subtables[i].next); +assert((unsigned) x == table->subtables[i].next); +#endif +initGroupSize = i - x + 1; + +moveUp = ddSymmSiftingUp(table,x,xLow); +if (moveUp == MV_OOM) goto ddSymmSiftingConvAuxOutOfMem; + +if (moveUp != NULL) { +x = moveUp->x; +i = table->subtables[x].next; +} else { +i = x; +while ((unsigned) x < table->subtables[x].next) +x = table->subtables[x].next; +} +#ifdef DD_DEBUG +/* x should be the bottom of the symmetry group and i the top */ +assert((unsigned) x >= table->subtables[x].next); +assert((unsigned) i == table->subtables[x].next); +#endif +finalGroupSize = x - i + 1; + +if (initGroupSize == finalGroupSize) { +/* No new symmetry groups detected, return to best position */ +result = ddSymmSiftingBackward(table,moveUp,initialSize); +} else { +while (moveDown != NULL) { +move = moveDown->next; +cuddDeallocMove(table, moveDown); +moveDown = move; +} +initialSize = table->keys - table->isolated; +moveDown = ddSymmSiftingDown(table,x,xHigh); +result = ddSymmSiftingBackward(table,moveDown,initialSize); +} +if (!result) goto ddSymmSiftingConvAuxOutOfMem; + +} else { /* moving up first: shorter */ +/* Find top of x's symmetry group */ +x = table->subtables[x].next; + +moveUp = ddSymmSiftingUp(table,x,xLow); +/* at this point x == xHigh, unless early term */ +if (moveUp == MV_OOM) goto ddSymmSiftingConvAuxOutOfMem; + +if (moveUp != NULL) { +x = moveUp->x; +i = table->subtables[x].next; +} else { +i = x; +while ((unsigned) x < table->subtables[x].next) +x = table->subtables[x].next; +} +#ifdef DD_DEBUG +/* x is bottom of the symmetry group and i is top */ +assert((unsigned) x >= table->subtables[x].next); +assert((unsigned) i == table->subtables[x].next); +#endif +initGroupSize = x - i + 1; + +moveDown = ddSymmSiftingDown(table,x,xHigh); +if (moveDown == MV_OOM) goto ddSymmSiftingConvAuxOutOfMem; + +if (moveDown != NULL) { +x = moveDown->y; +i = x; +while ((unsigned) i < table->subtables[i].next) { +i = table->subtables[i].next; +} +} else { +i = x; +x = table->subtables[x].next; +} +#ifdef DD_DEBUG +/* x should be the top of the symmetry group and i the bottom */ +assert((unsigned) i >= table->subtables[i].next); +assert((unsigned) x == table->subtables[i].next); +#endif +finalGroupSize = i - x + 1; + +if (initGroupSize == finalGroupSize) { +/* No new symmetries detected, go back to best position */ +result = ddSymmSiftingBackward(table,moveDown,initialSize); +} else { +while (moveUp != NULL) { +move = moveUp->next; +cuddDeallocMove(table, moveUp); +moveUp = move; +} +initialSize = table->keys - table->isolated; +moveUp = ddSymmSiftingUp(table,x,xLow); +result = ddSymmSiftingBackward(table,moveUp,initialSize); +} +if (!result) goto ddSymmSiftingConvAuxOutOfMem; +} + +while (moveDown != NULL) { +move = moveDown->next; +cuddDeallocMove(table, moveDown); +moveDown = move; +} +while (moveUp != NULL) { +move = moveUp->next; +cuddDeallocMove(table, moveUp); +moveUp = move; +} + +return(1); + +ddSymmSiftingConvAuxOutOfMem: +if (moveDown != MV_OOM) { +while (moveDown != NULL) { +move = moveDown->next; +cuddDeallocMove(table, moveDown); +moveDown = move; +} +} +if (moveUp != MV_OOM) { +while (moveUp != NULL) { +move = moveUp->next; +cuddDeallocMove(table, moveUp); +moveUp = move; +} +} + +return(0); + +} /* end of ddSymmSiftingConvAux */ + + +/**Function******************************************************************** + + Synopsis [Moves x up until either it reaches the bound (xLow) or + the size of the DD heap increases too much.] + + Description [Moves x up until either it reaches the bound (xLow) or + the size of the DD heap increases too much. Assumes that x is the top + of a symmetry group. Checks x for symmetry to the adjacent + variables. If symmetry is found, the symmetry group of x is merged + with the symmetry group of the other variable. Returns the set of + moves in case of success; MV_OOM if memory is full.] + + SideEffects [None] + +******************************************************************************/ +static Move * +ddSymmSiftingUp( +DdManager * table, +int y, +int xLow) +{ +Move *moves; +Move *move; +int x; +int size; +int i; +int gxtop,gybot; +int limitSize; +int xindex, yindex; +int zindex; +int z; +int isolated; +int L; /* lower bound on DD size */ +#ifdef DD_DEBUG +int checkL; +#endif + + +moves = NULL; +yindex = table->invperm[y]; + +/* Initialize the lower bound. +** The part of the DD below the bottom of y' group will not change. +** The part of the DD above y that does not interact with y will not +** change. The rest may vanish in the best case, except for +** the nodes at level xLow, which will not vanish, regardless. +*/ +limitSize = L = table->keys - table->isolated; +gybot = y; +while ((unsigned) gybot < table->subtables[gybot].next) +gybot = table->subtables[gybot].next; +for (z = xLow + 1; z <= gybot; z++) { +zindex = table->invperm[z]; +if (zindex == yindex || cuddTestInteract(table,zindex,yindex)) { +isolated = table->vars[zindex]->ref == 1; +L -= table->subtables[z].keys - isolated; +} +} + +x = cuddNextLow(table,y); +while (x >= xLow && L <= limitSize) { +#ifdef DD_DEBUG +gybot = y; +while ((unsigned) gybot < table->subtables[gybot].next) +gybot = table->subtables[gybot].next; +checkL = table->keys - table->isolated; +for (z = xLow + 1; z <= gybot; z++) { +zindex = table->invperm[z]; +if (zindex == yindex || cuddTestInteract(table,zindex,yindex)) { +isolated = table->vars[zindex]->ref == 1; +checkL -= table->subtables[z].keys - isolated; +} +} +assert(L == checkL); +#endif +gxtop = table->subtables[x].next; +if (cuddSymmCheck(table,x,y)) { +/* Symmetry found, attach symm groups */ +table->subtables[x].next = y; +i = table->subtables[y].next; +while (table->subtables[i].next != (unsigned) y) +i = table->subtables[i].next; +table->subtables[i].next = gxtop; +} else if (table->subtables[x].next == (unsigned) x && +table->subtables[y].next == (unsigned) y) { +/* x and y have self symmetry */ +xindex = table->invperm[x]; +size = cuddSwapInPlace(table,x,y); +#ifdef DD_DEBUG +assert(table->subtables[x].next == (unsigned) x); +assert(table->subtables[y].next == (unsigned) y); +#endif +if (size == 0) goto ddSymmSiftingUpOutOfMem; +/* Update the lower bound. */ +if (cuddTestInteract(table,xindex,yindex)) { +isolated = table->vars[xindex]->ref == 1; +L += table->subtables[y].keys - isolated; +} +move = (Move *) cuddDynamicAllocNode(table); +if (move == NULL) goto ddSymmSiftingUpOutOfMem; +move->x = x; +move->y = y; +move->size = size; +move->next = moves; +moves = move; +if ((double) size > (double) limitSize * table->maxGrowth) +return(moves); +if (size < limitSize) limitSize = size; +} else { /* Group move */ +size = ddSymmGroupMove(table,x,y,&moves); +if (size == 0) goto ddSymmSiftingUpOutOfMem; +/* Update the lower bound. */ +z = moves->y; +do { +zindex = table->invperm[z]; +if (cuddTestInteract(table,zindex,yindex)) { +isolated = table->vars[zindex]->ref == 1; +L += table->subtables[z].keys - isolated; +} +z = table->subtables[z].next; +} while (z != (int) moves->y); +if ((double) size > (double) limitSize * table->maxGrowth) +return(moves); +if (size < limitSize) limitSize = size; +} +y = gxtop; +x = cuddNextLow(table,y); +} + +return(moves); + +ddSymmSiftingUpOutOfMem: +while (moves != NULL) { +move = moves->next; +cuddDeallocMove(table, moves); +moves = move; +} +return(MV_OOM); + +} /* end of ddSymmSiftingUp */ + + +/**Function******************************************************************** + + Synopsis [Moves x down until either it reaches the bound (xHigh) or + the size of the DD heap increases too much.] + + Description [Moves x down until either it reaches the bound (xHigh) + or the size of the DD heap increases too much. Assumes that x is the + bottom of a symmetry group. Checks x for symmetry to the adjacent + variables. If symmetry is found, the symmetry group of x is merged + with the symmetry group of the other variable. Returns the set of + moves in case of success; MV_OOM if memory is full.] + + SideEffects [None] + +******************************************************************************/ +static Move * +ddSymmSiftingDown( +DdManager * table, +int x, +int xHigh) +{ +Move *moves; +Move *move; +int y; +int size; +int limitSize; +int gxtop,gybot; +int R; /* upper bound on node decrease */ +int xindex, yindex; +int isolated; +int z; +int zindex; +#ifdef DD_DEBUG +int checkR; +#endif + +moves = NULL; +/* Initialize R */ +xindex = table->invperm[x]; +gxtop = table->subtables[x].next; +limitSize = size = table->keys - table->isolated; +R = 0; +for (z = xHigh; z > gxtop; z--) { +zindex = table->invperm[z]; +if (zindex == xindex || cuddTestInteract(table,xindex,zindex)) { +isolated = table->vars[zindex]->ref == 1; +R += table->subtables[z].keys - isolated; +} +} + +y = cuddNextHigh(table,x); +while (y <= xHigh && size - R < limitSize) { +#ifdef DD_DEBUG +gxtop = table->subtables[x].next; +checkR = 0; +for (z = xHigh; z > gxtop; z--) { +zindex = table->invperm[z]; +if (zindex == xindex || cuddTestInteract(table,xindex,zindex)) { +isolated = table->vars[zindex]->ref == 1; +checkR += table->subtables[z].keys - isolated; +} +} +assert(R == checkR); +#endif +gybot = table->subtables[y].next; +while (table->subtables[gybot].next != (unsigned) y) +gybot = table->subtables[gybot].next; +if (cuddSymmCheck(table,x,y)) { +/* Symmetry found, attach symm groups */ +gxtop = table->subtables[x].next; +table->subtables[x].next = y; +table->subtables[gybot].next = gxtop; +} else if (table->subtables[x].next == (unsigned) x && +table->subtables[y].next == (unsigned) y) { +/* x and y have self symmetry */ +/* Update upper bound on node decrease. */ +yindex = table->invperm[y]; +if (cuddTestInteract(table,xindex,yindex)) { +isolated = table->vars[yindex]->ref == 1; +R -= table->subtables[y].keys - isolated; +} +size = cuddSwapInPlace(table,x,y); +#ifdef DD_DEBUG +assert(table->subtables[x].next == (unsigned) x); +assert(table->subtables[y].next == (unsigned) y); +#endif +if (size == 0) goto ddSymmSiftingDownOutOfMem; +move = (Move *) cuddDynamicAllocNode(table); +if (move == NULL) goto ddSymmSiftingDownOutOfMem; +move->x = x; +move->y = y; +move->size = size; +move->next = moves; +moves = move; +if ((double) size > (double) limitSize * table->maxGrowth) +return(moves); +if (size < limitSize) limitSize = size; +} else { /* Group move */ +/* Update upper bound on node decrease: first phase. */ +gxtop = table->subtables[x].next; +z = gxtop + 1; +do { +zindex = table->invperm[z]; +if (zindex == xindex || cuddTestInteract(table,xindex,zindex)) { +isolated = table->vars[zindex]->ref == 1; +R -= table->subtables[z].keys - isolated; +} +z++; +} while (z <= gybot); +size = ddSymmGroupMove(table,x,y,&moves); +if (size == 0) goto ddSymmSiftingDownOutOfMem; +if ((double) size > (double) limitSize * table->maxGrowth) +return(moves); +if (size < limitSize) limitSize = size; +/* Update upper bound on node decrease: second phase. */ +gxtop = table->subtables[gybot].next; +for (z = gxtop + 1; z <= gybot; z++) { +zindex = table->invperm[z]; +if (zindex == xindex || cuddTestInteract(table,xindex,zindex)) { +isolated = table->vars[zindex]->ref == 1; +R += table->subtables[z].keys - isolated; +} +} +} +x = gybot; +y = cuddNextHigh(table,x); +} + +return(moves); + +ddSymmSiftingDownOutOfMem: +while (moves != NULL) { +move = moves->next; +cuddDeallocMove(table, moves); +moves = move; +} +return(MV_OOM); + +} /* end of ddSymmSiftingDown */ + + +/**Function******************************************************************** + + Synopsis [Swaps two groups.] + + Description [Swaps two groups. x is assumed to be the bottom variable + of the first group. y is assumed to be the top variable of the second + group. Updates the list of moves. Returns the number of keys in the + table if successful; 0 otherwise.] + + SideEffects [None] + +******************************************************************************/ +static int +ddSymmGroupMove( +DdManager * table, +int x, +int y, +Move ** moves) +{ +Move *move; +int size; +int i,j; +int xtop,xbot,xsize,ytop,ybot,ysize,newxtop; +int swapx,swapy; + +#ifdef DD_DEBUG +assert(x < y); /* we assume that x < y */ +#endif +/* Find top, bottom, and size for the two groups. */ +xbot = x; +xtop = table->subtables[x].next; +xsize = xbot - xtop + 1; +ybot = y; +while ((unsigned) ybot < table->subtables[ybot].next) +ybot = table->subtables[ybot].next; +ytop = y; +ysize = ybot - ytop + 1; + +/* Sift the variables of the second group up through the first group. */ +for (i = 1; i <= ysize; i++) { +for (j = 1; j <= xsize; j++) { +size = cuddSwapInPlace(table,x,y); +if (size == 0) return(0); +swapx = x; swapy = y; +y = x; +x = y - 1; +} +y = ytop + i; +x = y - 1; +} + +/* fix symmetries */ +y = xtop; /* ytop is now where xtop used to be */ +for (i = 0; i < ysize-1 ; i++) { +table->subtables[y].next = y + 1; +y = y + 1; +} +table->subtables[y].next = xtop; /* y is bottom of its group, join */ +/* its symmetry to top of its group */ +x = y + 1; +newxtop = x; +for (i = 0; i < xsize - 1 ; i++) { +table->subtables[x].next = x + 1; +x = x + 1; +} +table->subtables[x].next = newxtop; /* x is bottom of its group, join */ +/* its symmetry to top of its group */ +/* Store group move */ +move = (Move *) cuddDynamicAllocNode(table); +if (move == NULL) return(0); +move->x = swapx; +move->y = swapy; +move->size = size; +move->next = *moves; +*moves = move; + +return(size); + +} /* end of ddSymmGroupMove */ + + +/**Function******************************************************************** + + Synopsis [Undoes the swap of two groups.] + + Description [Undoes the swap of two groups. x is assumed to be the + bottom variable of the first group. y is assumed to be the top + variable of the second group. Returns the number of keys in the table + if successful; 0 otherwise.] + + SideEffects [None] + +******************************************************************************/ +static int +ddSymmGroupMoveBackward( +DdManager * table, +int x, +int y) +{ +int size; +int i,j; +int xtop,xbot,xsize,ytop,ybot,ysize,newxtop; + +#ifdef DD_DEBUG +assert(x < y); /* We assume that x < y */ +#endif + +/* Find top, bottom, and size for the two groups. */ +xbot = x; +xtop = table->subtables[x].next; +xsize = xbot - xtop + 1; +ybot = y; +while ((unsigned) ybot < table->subtables[ybot].next) +ybot = table->subtables[ybot].next; +ytop = y; +ysize = ybot - ytop + 1; + +/* Sift the variables of the second group up through the first group. */ +for (i = 1; i <= ysize; i++) { +for (j = 1; j <= xsize; j++) { +size = cuddSwapInPlace(table,x,y); +if (size == 0) return(0); +y = x; +x = cuddNextLow(table,y); +} +y = ytop + i; +x = y - 1; +} + +/* Fix symmetries. */ +y = xtop; +for (i = 0; i < ysize-1 ; i++) { +table->subtables[y].next = y + 1; +y = y + 1; +} +table->subtables[y].next = xtop; /* y is bottom of its group, join */ +/* its symmetry to top of its group */ +x = y + 1; +newxtop = x; +for (i = 0; i < xsize-1 ; i++) { +table->subtables[x].next = x + 1; +x = x + 1; +} +table->subtables[x].next = newxtop; /* x is bottom of its group, join */ +/* its symmetry to top of its group */ + +return(size); + +} /* end of ddSymmGroupMoveBackward */ + + +/**Function******************************************************************** + + Synopsis [Given a set of moves, returns the DD heap to the position + giving the minimum size.] + + Description [Given a set of moves, returns the DD heap to the + position giving the minimum size. In case of ties, returns to the + closest position giving the minimum size. Returns 1 in case of + success; 0 otherwise.] + + SideEffects [None] + +******************************************************************************/ +static int +ddSymmSiftingBackward( +DdManager * table, +Move * moves, +int size) +{ +Move *move; +int res; + +for (move = moves; move != NULL; move = move->next) { +if (move->size < size) { +size = move->size; +} +} + +for (move = moves; move != NULL; move = move->next) { +if (move->size == size) return(1); +if (table->subtables[move->x].next == move->x && table->subtables[move->y].next == move->y) { +res = cuddSwapInPlace(table,(int)move->x,(int)move->y); +#ifdef DD_DEBUG +assert(table->subtables[move->x].next == move->x); +assert(table->subtables[move->y].next == move->y); +#endif +} else { /* Group move necessary */ +res = ddSymmGroupMoveBackward(table,(int)move->x,(int)move->y); +} +if (!res) return(0); +} + +return(1); + +} /* end of ddSymmSiftingBackward */ + + +/**Function******************************************************************** + + Synopsis [Counts numbers of symmetric variables and symmetry + groups.] + + Description [] + + SideEffects [None] + +******************************************************************************/ +static void +ddSymmSummary( +DdManager * table, +int lower, +int upper, +int * symvars, +int * symgroups) +{ +int i,x,gbot; +int TotalSymm = 0; +int TotalSymmGroups = 0; + +for (i = lower; i <= upper; i++) { +if (table->subtables[i].next != (unsigned) i) { +TotalSymmGroups++; +x = i; +do { +TotalSymm++; +gbot = x; +x = table->subtables[x].next; +} while (x != i); +#ifdef DD_DEBUG +assert(table->subtables[gbot].next == (unsigned) i); +#endif +i = gbot; +} +} +*symvars = TotalSymm; +*symgroups = TotalSymmGroups; + +return; + +} /* end of ddSymmSummary */ + +/**CFile*********************************************************************** + + FileName [cuddTable.c] + + PackageName [cudd] + + Synopsis [Unique table management functions.] + + Description [External procedures included in this module: +
        +
      • Cudd_Prime() +
      • Cudd_Reserve() +
      + Internal procedures included in this module: +
        +
      • cuddAllocNode() +
      • cuddInitTable() +
      • cuddFreeTable() +
      • cuddGarbageCollect() +
      • cuddZddGetNode() +
      • cuddZddGetNodeIVO() +
      • cuddUniqueInter() +
      • cuddUniqueInterIVO() +
      • cuddUniqueInterZdd() +
      • cuddUniqueConst() +
      • cuddRehash() +
      • cuddShrinkSubtable() +
      • cuddInsertSubtables() +
      • cuddDestroySubtables() +
      • cuddResizeTableZdd() +
      • cuddSlowTableGrowth() +
      + Static procedures included in this module: +
        +
      • ddRehashZdd() +
      • ddResizeTable() +
      • cuddFindParent() +
      • cuddOrderedInsert() +
      • cuddOrderedThread() +
      • cuddRotateLeft() +
      • cuddRotateRight() +
      • cuddDoRebalance() +
      • cuddCheckCollisionOrdering() +
      ] + + SeeAlso [] + + Author [Fabio Somenzi] + + Copyright [Copyright (c) 1995-2012, Regents of the University of Colorado + + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + Neither the name of the University of Colorado nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE.] + +******************************************************************************/ + + +/*---------------------------------------------------------------------------*/ +/* Constant declarations */ +/*---------------------------------------------------------------------------*/ + +#ifndef DD_UNSORTED_FREE_LIST +#ifdef DD_RED_BLACK_FREE_LIST +/* Constants for red/black trees. */ +#define DD_STACK_SIZE 128 +#define DD_RED 0 +#define DD_BLACK 1 +#define DD_PAGE_SIZE 8192 +#define DD_PAGE_MASK ~(DD_PAGE_SIZE - 1) +#endif +#endif + +/*---------------------------------------------------------------------------*/ +/* Stucture declarations */ +/*---------------------------------------------------------------------------*/ + +/* This is a hack for when CUDD_VALUE_TYPE is double */ +typedef union hack { +CUDD_VALUE_TYPE value; +unsigned int bits[2]; +} hack; + +/*---------------------------------------------------------------------------*/ +/* Type declarations */ +/*---------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------*/ +/* Variable declarations */ +/*---------------------------------------------------------------------------*/ + +//#ifndef lint +//static char rcsid[] DD_UNUSED = "$Id: cuddTable.c,v 1.126 2012/02/05 01:07:19 fabio Exp $"; +//#endif + +/*---------------------------------------------------------------------------*/ +/* Macro declarations */ +/*---------------------------------------------------------------------------*/ + + +#ifndef DD_UNSORTED_FREE_LIST +#ifdef DD_RED_BLACK_FREE_LIST +/* Macros for red/black trees. */ +#define DD_INSERT_COMPARE(x,y) \ + (((ptruint) (x) & DD_PAGE_MASK) - ((ptruint) (y) & DD_PAGE_MASK)) +#define DD_COLOR(p) ((p)->index) +#define DD_IS_BLACK(p) ((p)->index == DD_BLACK) +#define DD_IS_RED(p) ((p)->index == DD_RED) +#define DD_LEFT(p) cuddT(p) +#define DD_RIGHT(p) cuddE(p) +#define DD_NEXT(p) ((p)->next) +#endif +#endif + + +/**AutomaticStart*************************************************************/ + +/*---------------------------------------------------------------------------*/ +/* Static function prototypes */ +/*---------------------------------------------------------------------------*/ + +static void ddRehashZdd (DdManager *unique, int i); +static int ddResizeTable (DdManager *unique, int index, int amount); + +DD_INLINE static void ddFixLimits (DdManager *unique); +#ifdef DD_RED_BLACK_FREE_LIST +static void cuddOrderedInsert (DdNodePtr *root, DdNodePtr node); +static DdNode * cuddOrderedThread (DdNode *root, DdNode *list); +static void cuddRotateLeft (DdNodePtr *nodeP); +static void cuddRotateRight (DdNodePtr *nodeP); +static void cuddDoRebalance (DdNodePtr **stack, int stackN); +#endif +static void ddPatchTree (DdManager *dd, MtrNode *treenode); +#ifdef DD_DEBUG +static int cuddCheckCollisionOrdering (DdManager *unique, int i, int j); +#endif +static void ddReportRefMess (DdManager *unique, int i, const char *caller); + +/**AutomaticEnd***************************************************************/ + + +/*---------------------------------------------------------------------------*/ +/* Definition of exported functions */ +/*---------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------*/ +/* Definition of internal functions */ +/*---------------------------------------------------------------------------*/ + + +/**Function******************************************************************** + + Synopsis [Fast storage allocation for DdNodes in the table.] + + Description [Fast storage allocation for DdNodes in the table. The + first 4 bytes of a chunk contain a pointer to the next block; the + rest contains DD_MEM_CHUNK spaces for DdNodes. Returns a pointer to + a new node if successful; NULL is memory is full.] + + SideEffects [None] + + SeeAlso [cuddDynamicAllocNode] + +******************************************************************************/ +DdNode * +cuddAllocNode( +DdManager * unique) +{ +int i; +DdNodePtr *mem; +DdNode *list, *node; +extern DD_OOMFP MMoutOfMemory; +DD_OOMFP saveHandler; + +if (unique->nextFree == NULL) { /* free list is empty */ +/* Check for exceeded limits. */ +if ((unique->keys - unique->dead) + (unique->keysZ - unique->deadZ) > +unique->maxLive) { +unique->errorCode = CUDD_TOO_MANY_NODES; +return(NULL); +} +if (util_cpu_time() - unique->startTime > unique->timeLimit) { +unique->errorCode = CUDD_TIMEOUT_EXPIRED; +return(NULL); +} +if (unique->stash == NULL || unique->memused > unique->maxmemhard) { +(void) cuddGarbageCollect(unique,1); +mem = NULL; +} +if (unique->nextFree == NULL) { +if (unique->memused > unique->maxmemhard) { +unique->errorCode = CUDD_MAX_MEM_EXCEEDED; +return(NULL); +} +/* Try to allocate a new block. */ +saveHandler = MMoutOfMemory; +MMoutOfMemory = Cudd_OutOfMem; +mem = (DdNodePtr *) ALLOC(DdNode,DD_MEM_CHUNK + 1); +MMoutOfMemory = saveHandler; +if (mem == NULL) { +/* No more memory: Try collecting garbage. If this succeeds, +** we end up with mem still NULL, but unique->nextFree != +** NULL. */ +if (cuddGarbageCollect(unique,1) == 0) { +/* Last resort: Free the memory stashed away, if there +** any. If this succeeeds, mem != NULL and +** unique->nextFree still NULL. */ +if (unique->stash != NULL) { +FREE(unique->stash); +unique->stash = NULL; +/* Inhibit resizing of tables. */ +cuddSlowTableGrowth(unique); +/* Now try again. */ +mem = (DdNodePtr *) ALLOC(DdNode,DD_MEM_CHUNK + 1); +} +if (mem == NULL) { +/* Out of luck. Call the default handler to do +** whatever it specifies for a failed malloc. +** If this handler returns, then set error code, +** print warning, and return. */ +(*MMoutOfMemory)(sizeof(DdNode)*(DD_MEM_CHUNK + 1)); +unique->errorCode = CUDD_MEMORY_OUT; +#ifdef DD_VERBOSE +(void) fprintf(unique->err, +"cuddAllocNode: out of memory"); +(void) fprintf(unique->err, "Memory in use = %lu\n", +unique->memused); +#endif +return(NULL); +} +} +} +if (mem != NULL) { /* successful allocation; slice memory */ +ptruint offset; +unique->memused += (DD_MEM_CHUNK + 1) * sizeof(DdNode); +mem[0] = (DdNodePtr) unique->memoryList; +unique->memoryList = mem; + +/* Here we rely on the fact that a DdNode is as large +** as 4 pointers. */ +offset = (ptruint) mem & (sizeof(DdNode) - 1); +mem += (sizeof(DdNode) - offset) / sizeof(DdNodePtr); +assert(((ptruint) mem & (sizeof(DdNode) - 1)) == 0); +list = (DdNode *) mem; + +i = 1; +do { +list[i - 1].ref = 0; +list[i - 1].next = &list[i]; +} while (++i < DD_MEM_CHUNK); + +list[DD_MEM_CHUNK-1].ref = 0; +list[DD_MEM_CHUNK-1].next = NULL; + +unique->nextFree = &list[0]; +} +} +} +unique->allocated++; +node = unique->nextFree; +unique->nextFree = node->next; +return(node); + +} /* end of cuddAllocNode */ + + +/**Function******************************************************************** + + Synopsis [Creates and initializes the unique table.] + + Description [Creates and initializes the unique table. Returns a pointer + to the table if successful; NULL otherwise.] + + SideEffects [None] + + SeeAlso [Cudd_Init cuddFreeTable] + +******************************************************************************/ +DdManager * +cuddInitTable( +unsigned int numVars /* Initial number of BDD variables (and subtables) */, +unsigned int numVarsZ /* Initial number of ZDD variables (and subtables) */, +unsigned int numSlots /* Initial size of the BDD subtables */, +unsigned int looseUpTo /* Limit for fast table growth */) +{ +DdManager *unique = ALLOC(DdManager,1); +int i, j; +DdNodePtr *nodelist; +DdNode *sentinel; +unsigned int slots; +int shift; + +if (unique == NULL) { +return(NULL); +} +sentinel = &(unique->sentinel); +sentinel->ref = 0; +sentinel->index = 0; +cuddT(sentinel) = NULL; +cuddE(sentinel) = NULL; +sentinel->next = NULL; +unique->epsilon = DD_EPSILON; +unique->size = numVars; +unique->sizeZ = numVarsZ; +unique->maxSize = ddMax(DD_DEFAULT_RESIZE, numVars); +unique->maxSizeZ = ddMax(DD_DEFAULT_RESIZE, numVarsZ); + +/* Adjust the requested number of slots to a power of 2. */ +slots = 8; +while (slots < numSlots) { +slots <<= 1; +} +unique->initSlots = slots; +shift = sizeof(int) * 8 - cuddComputeFloorLog2(slots); + +unique->slots = (numVars + numVarsZ + 1) * slots; +unique->keys = 0; +unique->maxLive = ~0; /* very large number */ +unique->keysZ = 0; +unique->dead = 0; +unique->deadZ = 0; +unique->gcFrac = DD_GC_FRAC_HI; +unique->minDead = (unsigned) (DD_GC_FRAC_HI * (double) unique->slots); +unique->looseUpTo = looseUpTo; +unique->gcEnabled = 1; +unique->allocated = 0; +unique->reclaimed = 0; +unique->subtables = ALLOC(DdSubtable,unique->maxSize); +if (unique->subtables == NULL) { +FREE(unique); +return(NULL); +} +unique->subtableZ = ALLOC(DdSubtable,unique->maxSizeZ); +if (unique->subtableZ == NULL) { +FREE(unique->subtables); +FREE(unique); +return(NULL); +} +unique->perm = ALLOC(int,unique->maxSize); +if (unique->perm == NULL) { +FREE(unique->subtables); +FREE(unique->subtableZ); +FREE(unique); +return(NULL); +} +unique->invperm = ALLOC(int,unique->maxSize); +if (unique->invperm == NULL) { +FREE(unique->subtables); +FREE(unique->subtableZ); +FREE(unique->perm); +FREE(unique); +return(NULL); +} +unique->permZ = ALLOC(int,unique->maxSizeZ); +if (unique->permZ == NULL) { +FREE(unique->subtables); +FREE(unique->subtableZ); +FREE(unique->perm); +FREE(unique->invperm); +FREE(unique); +return(NULL); +} +unique->invpermZ = ALLOC(int,unique->maxSizeZ); +if (unique->invpermZ == NULL) { +FREE(unique->subtables); +FREE(unique->subtableZ); +FREE(unique->perm); +FREE(unique->invperm); +FREE(unique->permZ); +FREE(unique); +return(NULL); +} +unique->map = NULL; +unique->stack = ALLOC(DdNodePtr,ddMax(unique->maxSize,unique->maxSizeZ)+1); +if (unique->stack == NULL) { +FREE(unique->subtables); +FREE(unique->subtableZ); +FREE(unique->perm); +FREE(unique->invperm); +FREE(unique->permZ); +FREE(unique->invpermZ); +FREE(unique); +return(NULL); +} +unique->stack[0] = NULL; /* to suppress harmless UMR */ + +#ifndef DD_NO_DEATH_ROW +unique->deathRowDepth = 1 << cuddComputeFloorLog2(unique->looseUpTo >> 2); +unique->deathRow = ALLOC(DdNodePtr,unique->deathRowDepth); +if (unique->deathRow == NULL) { +FREE(unique->subtables); +FREE(unique->subtableZ); +FREE(unique->perm); +FREE(unique->invperm); +FREE(unique->permZ); +FREE(unique->invpermZ); +FREE(unique->stack); +FREE(unique); +return(NULL); +} +for (i = 0; i < unique->deathRowDepth; i++) { +unique->deathRow[i] = NULL; +} +unique->nextDead = 0; +unique->deadMask = unique->deathRowDepth - 1; +#endif + +for (i = 0; (unsigned) i < numVars; i++) { +unique->subtables[i].slots = slots; +unique->subtables[i].shift = shift; +unique->subtables[i].keys = 0; +unique->subtables[i].dead = 0; +unique->subtables[i].maxKeys = slots * DD_MAX_SUBTABLE_DENSITY; +unique->subtables[i].bindVar = 0; +unique->subtables[i].varType = CUDD_VAR_PRIMARY_INPUT; +unique->subtables[i].pairIndex = 0; +unique->subtables[i].varHandled = 0; +unique->subtables[i].varToBeGrouped = CUDD_LAZY_NONE; + +nodelist = unique->subtables[i].nodelist = ALLOC(DdNodePtr,slots); +if (nodelist == NULL) { +for (j = 0; j < i; j++) { +FREE(unique->subtables[j].nodelist); +} +FREE(unique->subtables); +FREE(unique->subtableZ); +FREE(unique->perm); +FREE(unique->invperm); +FREE(unique->permZ); +FREE(unique->invpermZ); +FREE(unique->stack); +FREE(unique); +return(NULL); +} +for (j = 0; (unsigned) j < slots; j++) { +nodelist[j] = sentinel; +} +unique->perm[i] = i; +unique->invperm[i] = i; +} +for (i = 0; (unsigned) i < numVarsZ; i++) { +unique->subtableZ[i].slots = slots; +unique->subtableZ[i].shift = shift; +unique->subtableZ[i].keys = 0; +unique->subtableZ[i].dead = 0; +unique->subtableZ[i].maxKeys = slots * DD_MAX_SUBTABLE_DENSITY; +nodelist = unique->subtableZ[i].nodelist = ALLOC(DdNodePtr,slots); +if (nodelist == NULL) { +for (j = 0; (unsigned) j < numVars; j++) { +FREE(unique->subtables[j].nodelist); +} +FREE(unique->subtables); +for (j = 0; j < i; j++) { +FREE(unique->subtableZ[j].nodelist); +} +FREE(unique->subtableZ); +FREE(unique->perm); +FREE(unique->invperm); +FREE(unique->permZ); +FREE(unique->invpermZ); +FREE(unique->stack); +FREE(unique); +return(NULL); +} +for (j = 0; (unsigned) j < slots; j++) { +nodelist[j] = NULL; +} +unique->permZ[i] = i; +unique->invpermZ[i] = i; +} +unique->constants.slots = slots; +unique->constants.shift = shift; +unique->constants.keys = 0; +unique->constants.dead = 0; +unique->constants.maxKeys = slots * DD_MAX_SUBTABLE_DENSITY; +nodelist = unique->constants.nodelist = ALLOC(DdNodePtr,slots); +if (nodelist == NULL) { +for (j = 0; (unsigned) j < numVars; j++) { +FREE(unique->subtables[j].nodelist); +} +FREE(unique->subtables); +for (j = 0; (unsigned) j < numVarsZ; j++) { +FREE(unique->subtableZ[j].nodelist); +} +FREE(unique->subtableZ); +FREE(unique->perm); +FREE(unique->invperm); +FREE(unique->permZ); +FREE(unique->invpermZ); +FREE(unique->stack); +FREE(unique); +return(NULL); +} +for (j = 0; (unsigned) j < slots; j++) { +nodelist[j] = NULL; +} + +unique->memoryList = NULL; +unique->nextFree = NULL; + +unique->memused = sizeof(DdManager) + (unique->maxSize + unique->maxSizeZ) +* (sizeof(DdSubtable) + 2 * sizeof(int)) + (numVars + 1) * +slots * sizeof(DdNodePtr) + +(ddMax(unique->maxSize,unique->maxSizeZ) + 1) * sizeof(DdNodePtr); +#ifndef DD_NO_DEATH_ROW +unique->memused += unique->deathRowDepth * sizeof(DdNodePtr); +#endif + +/* Initialize fields concerned with automatic dynamic reordering. */ +unique->reordered = 0; +unique->reorderings = 0; +unique->maxReorderings = ~0; +unique->siftMaxVar = DD_SIFT_MAX_VAR; +unique->siftMaxSwap = DD_SIFT_MAX_SWAPS; +unique->maxGrowth = DD_MAX_REORDER_GROWTH; +unique->maxGrowthAlt = 2.0 * DD_MAX_REORDER_GROWTH; +unique->reordCycle = 0; /* do not use alternate threshold */ +unique->autoDyn = 0; /* initially disabled */ +unique->autoDynZ = 0; /* initially disabled */ +unique->autoMethod = CUDD_REORDER_SIFT; +unique->autoMethodZ = CUDD_REORDER_SIFT; +unique->realign = 0; /* initially disabled */ +unique->realignZ = 0; /* initially disabled */ +unique->nextDyn = DD_FIRST_REORDER; +unique->countDead = ~0; +unique->tree = NULL; +unique->treeZ = NULL; +unique->groupcheck = CUDD_GROUP_CHECK7; +unique->recomb = DD_DEFAULT_RECOMB; +unique->symmviolation = 0; +unique->arcviolation = 0; +unique->populationSize = 0; +unique->numberXovers = 0; +unique->randomizeOrder = 0; +unique->linear = NULL; +unique->linearSize = 0; + +/* Initialize ZDD universe. */ +unique->univ = (DdNodePtr *)NULL; + +/* Initialize auxiliary fields. */ +unique->localCaches = NULL; +unique->preGCHook = NULL; +unique->postGCHook = NULL; +unique->preReorderingHook = NULL; +unique->postReorderingHook = NULL; +unique->out = stdout; +unique->err = stderr; +unique->errorCode = CUDD_NO_ERROR; +unique->startTime = util_cpu_time(); +unique->timeLimit = ~0UL; + +/* Initialize statistical counters. */ +unique->maxmemhard = ~ 0UL; +unique->garbageCollections = 0; +unique->GCTime = 0; +unique->reordTime = 0; +#ifdef DD_STATS +unique->nodesDropped = 0; +unique->nodesFreed = 0; +#endif +unique->peakLiveNodes = 0; +#ifdef DD_UNIQUE_PROFILE +unique->uniqueLookUps = 0; +unique->uniqueLinks = 0; +#endif +#ifdef DD_COUNT +unique->recursiveCalls = 0; +unique->swapSteps = 0; +#ifdef DD_STATS +unique->nextSample = 250000; +#endif +#endif + +return(unique); + +} /* end of cuddInitTable */ + + +/**Function******************************************************************** + + Synopsis [Frees the resources associated to a unique table.] + + Description [] + + SideEffects [None] + + SeeAlso [cuddInitTable] + +******************************************************************************/ +void +cuddFreeTable( +DdManager * unique) +{ +DdNodePtr *next; +DdNodePtr *memlist = unique->memoryList; +int i; + +if (unique->univ != NULL) cuddZddFreeUniv(unique); +while (memlist != NULL) { +next = (DdNodePtr *) memlist[0]; /* link to next block */ +FREE(memlist); +memlist = next; +} +unique->nextFree = NULL; +unique->memoryList = NULL; + +for (i = 0; i < unique->size; i++) { +FREE(unique->subtables[i].nodelist); +} +for (i = 0; i < unique->sizeZ; i++) { +FREE(unique->subtableZ[i].nodelist); +} +FREE(unique->constants.nodelist); +FREE(unique->subtables); +FREE(unique->subtableZ); +FREE(unique->acache); +FREE(unique->perm); +FREE(unique->permZ); +FREE(unique->invperm); +FREE(unique->invpermZ); +FREE(unique->vars); +if (unique->map != NULL) FREE(unique->map); +FREE(unique->stack); +#ifndef DD_NO_DEATH_ROW +FREE(unique->deathRow); +#endif +if (unique->tree != NULL) Mtr_FreeTree(unique->tree); +if (unique->treeZ != NULL) Mtr_FreeTree(unique->treeZ); +if (unique->linear != NULL) FREE(unique->linear); +while (unique->preGCHook != NULL) +Cudd_RemoveHook(unique,unique->preGCHook->f,CUDD_PRE_GC_HOOK); +while (unique->postGCHook != NULL) +Cudd_RemoveHook(unique,unique->postGCHook->f,CUDD_POST_GC_HOOK); +while (unique->preReorderingHook != NULL) +Cudd_RemoveHook(unique,unique->preReorderingHook->f, +CUDD_PRE_REORDERING_HOOK); +while (unique->postReorderingHook != NULL) +Cudd_RemoveHook(unique,unique->postReorderingHook->f, +CUDD_POST_REORDERING_HOOK); +FREE(unique); + +} /* end of cuddFreeTable */ + + +/**Function******************************************************************** + + Synopsis [Performs garbage collection on the unique tables.] + + Description [Performs garbage collection on the BDD and ZDD unique tables. + If clearCache is 0, the cache is not cleared. This should only be + specified if the cache has been cleared right before calling + cuddGarbageCollect. (As in the case of dynamic reordering.) + Returns the total number of deleted nodes.] + + SideEffects [None] + + SeeAlso [] + +******************************************************************************/ +int +cuddGarbageCollect( +DdManager * unique, +int clearCache) +{ +DdHook *hook; +DdCache *cache = unique->cache; +DdNode *sentinel = &(unique->sentinel); +DdNodePtr *nodelist; +int i, j, deleted, totalDeleted, totalDeletedZ; +DdCache *c; +DdNode *node,*next; +DdNodePtr *lastP; +int slots; +unsigned long localTime; +#ifndef DD_UNSORTED_FREE_LIST +#ifdef DD_RED_BLACK_FREE_LIST +DdNodePtr tree; +#else +DdNodePtr *memListTrav, *nxtNode; +DdNode *downTrav, *sentry; +int k; +#endif +#endif + +#ifndef DD_NO_DEATH_ROW +cuddClearDeathRow(unique); +#endif + +hook = unique->preGCHook; +while (hook != NULL) { +int res = (hook->f)(unique,"DD",NULL); +if (res == 0) return(0); +hook = hook->next; +} + +if (unique->dead + unique->deadZ == 0) { +hook = unique->postGCHook; +while (hook != NULL) { +int res = (hook->f)(unique,"DD",NULL); +if (res == 0) return(0); +hook = hook->next; +} +return(0); +} + +/* If many nodes are being reclaimed, we want to resize the tables +** more aggressively, to reduce the frequency of garbage collection. +*/ +if (clearCache && unique->gcFrac == DD_GC_FRAC_LO && +unique->slots <= unique->looseUpTo && unique->stash != NULL) { +unique->minDead = (unsigned) (DD_GC_FRAC_HI * (double) unique->slots); +#ifdef DD_VERBOSE +(void) fprintf(unique->err,"GC fraction = %.2f\t", DD_GC_FRAC_HI); +(void) fprintf(unique->err,"minDead = %d\n", unique->minDead); +#endif +unique->gcFrac = DD_GC_FRAC_HI; +return(0); +} + +localTime = util_cpu_time(); + +unique->garbageCollections++; +#ifdef DD_VERBOSE +(void) fprintf(unique->err, +"garbage collecting (%d dead BDD nodes out of %d, min %d)...", +unique->dead, unique->keys, unique->minDead); +(void) fprintf(unique->err, +" (%d dead ZDD nodes out of %d)...", +unique->deadZ, unique->keysZ); +#endif + +/* Remove references to garbage collected nodes from the cache. */ +if (clearCache) { +slots = unique->cacheSlots; +for (i = 0; i < slots; i++) { +c = &cache[i]; +if (c->data != NULL) { +if (cuddClean(c->f)->ref == 0 || +cuddClean(c->g)->ref == 0 || +(((ptruint)c->f & 0x2) && Cudd_Regular(c->h)->ref == 0) || +(c->data != DD_NON_CONSTANT && +Cudd_Regular(c->data)->ref == 0)) { +c->data = NULL; +unique->cachedeletions++; +} +} +} +cuddLocalCacheClearDead(unique); +} + +/* Now return dead nodes to free list. Count them for sanity check. */ +totalDeleted = 0; +#ifndef DD_UNSORTED_FREE_LIST +#ifdef DD_RED_BLACK_FREE_LIST +tree = NULL; +#endif +#endif + +for (i = 0; i < unique->size; i++) { +if (unique->subtables[i].dead == 0) continue; +nodelist = unique->subtables[i].nodelist; + +deleted = 0; +slots = unique->subtables[i].slots; +for (j = 0; j < slots; j++) { +lastP = &(nodelist[j]); +node = *lastP; +while (node != sentinel) { +next = node->next; +if (node->ref == 0) { +deleted++; +#ifndef DD_UNSORTED_FREE_LIST +#ifdef DD_RED_BLACK_FREE_LIST +cuddOrderedInsert(&tree,node); +#endif +#else +cuddDeallocNode(unique,node); +#endif +} else { +*lastP = node; +lastP = &(node->next); +} +node = next; +} +*lastP = sentinel; +} +if ((unsigned) deleted != unique->subtables[i].dead) { +ddReportRefMess(unique, i, "cuddGarbageCollect"); +} +totalDeleted += deleted; +unique->subtables[i].keys -= deleted; +unique->subtables[i].dead = 0; +} +if (unique->constants.dead != 0) { +nodelist = unique->constants.nodelist; +deleted = 0; +slots = unique->constants.slots; +for (j = 0; j < slots; j++) { +lastP = &(nodelist[j]); +node = *lastP; +while (node != NULL) { +next = node->next; +if (node->ref == 0) { +deleted++; +#ifndef DD_UNSORTED_FREE_LIST +#ifdef DD_RED_BLACK_FREE_LIST +cuddOrderedInsert(&tree,node); +#endif +#else +cuddDeallocNode(unique,node); +#endif +} else { +*lastP = node; +lastP = &(node->next); +} +node = next; +} +*lastP = NULL; +} +if ((unsigned) deleted != unique->constants.dead) { +ddReportRefMess(unique, CUDD_CONST_INDEX, "cuddGarbageCollect"); +} +totalDeleted += deleted; +unique->constants.keys -= deleted; +unique->constants.dead = 0; +} +if ((unsigned) totalDeleted != unique->dead) { +ddReportRefMess(unique, -1, "cuddGarbageCollect"); +} +unique->keys -= totalDeleted; +unique->dead = 0; +#ifdef DD_STATS +unique->nodesFreed += (double) totalDeleted; +#endif + +totalDeletedZ = 0; + +for (i = 0; i < unique->sizeZ; i++) { +if (unique->subtableZ[i].dead == 0) continue; +nodelist = unique->subtableZ[i].nodelist; + +deleted = 0; +slots = unique->subtableZ[i].slots; +for (j = 0; j < slots; j++) { +lastP = &(nodelist[j]); +node = *lastP; +while (node != NULL) { +next = node->next; +if (node->ref == 0) { +deleted++; +#ifndef DD_UNSORTED_FREE_LIST +#ifdef DD_RED_BLACK_FREE_LIST +cuddOrderedInsert(&tree,node); +#endif +#else +cuddDeallocNode(unique,node); +#endif +} else { +*lastP = node; +lastP = &(node->next); +} +node = next; +} +*lastP = NULL; +} +if ((unsigned) deleted != unique->subtableZ[i].dead) { +ddReportRefMess(unique, i, "cuddGarbageCollect"); +} +totalDeletedZ += deleted; +unique->subtableZ[i].keys -= deleted; +unique->subtableZ[i].dead = 0; +} + +/* No need to examine the constant table for ZDDs. +** If we did we should be careful not to count whatever dead +** nodes we found there among the dead ZDD nodes. */ +if ((unsigned) totalDeletedZ != unique->deadZ) { +ddReportRefMess(unique, -1, "cuddGarbageCollect"); +} +unique->keysZ -= totalDeletedZ; +unique->deadZ = 0; +#ifdef DD_STATS +unique->nodesFreed += (double) totalDeletedZ; +#endif + + +#ifndef DD_UNSORTED_FREE_LIST +#ifdef DD_RED_BLACK_FREE_LIST +unique->nextFree = cuddOrderedThread(tree,unique->nextFree); +#else +memListTrav = unique->memoryList; +sentry = NULL; +while (memListTrav != NULL) { +ptruint offset; +nxtNode = (DdNodePtr *)memListTrav[0]; +offset = (ptruint) memListTrav & (sizeof(DdNode) - 1); +memListTrav += (sizeof(DdNode) - offset) / sizeof(DdNodePtr); +downTrav = (DdNode *)memListTrav; +k = 0; +do { +if (downTrav[k].ref == 0) { +if (sentry == NULL) { +unique->nextFree = sentry = &downTrav[k]; +} else { +/* First hook sentry->next to the dead node and then +** reassign sentry to the dead node. */ +sentry = (sentry->next = &downTrav[k]); +} +} +} while (++k < DD_MEM_CHUNK); +memListTrav = nxtNode; +} +sentry->next = NULL; +#endif +#endif + +unique->GCTime += util_cpu_time() - localTime; + +hook = unique->postGCHook; +while (hook != NULL) { +int res = (hook->f)(unique,"DD",NULL); +if (res == 0) return(0); +hook = hook->next; +} + +#ifdef DD_VERBOSE +(void) fprintf(unique->err," done\n"); +#endif + +return(totalDeleted+totalDeletedZ); + +} /* end of cuddGarbageCollect */ + + +/**Function******************************************************************** + + Synopsis [Wrapper for cuddUniqueInterZdd.] + + Description [Wrapper for cuddUniqueInterZdd, which applies the ZDD + reduction rule. Returns a pointer to the result node under normal + conditions; NULL if reordering occurred or memory was exhausted.] + + SideEffects [None] + + SeeAlso [cuddUniqueInterZdd] + +******************************************************************************/ +DdNode * +cuddZddGetNode( +DdManager * zdd, +int id, +DdNode * T, +DdNode * E) +{ +DdNode *node; + +if (T == DD_ZERO(zdd)) +return(E); +node = cuddUniqueInterZdd(zdd, id, T, E); +return(node); + +} /* end of cuddZddGetNode */ + + +/**Function******************************************************************** + + Synopsis [Wrapper for cuddUniqueInterZdd that is independent of variable + ordering.] + + Description [Wrapper for cuddUniqueInterZdd that is independent of + variable ordering (IVO). This function does not require parameter + index to precede the indices of the top nodes of g and h in the + variable order. Returns a pointer to the result node under normal + conditions; NULL if reordering occurred or memory was exhausted.] + + SideEffects [None] + + SeeAlso [cuddZddGetNode cuddZddIsop] + +******************************************************************************/ +DdNode * +cuddZddGetNodeIVO( +DdManager * dd, +int index, +DdNode * g, +DdNode * h) +{ +DdNode *f, *r, *t; +DdNode *zdd_one = DD_ONE(dd); +DdNode *zdd_zero = DD_ZERO(dd); + +f = cuddUniqueInterZdd(dd, index, zdd_one, zdd_zero); +if (f == NULL) { +return(NULL); +} +cuddRef(f); +t = cuddZddProduct(dd, f, g); +if (t == NULL) { +Cudd_RecursiveDerefZdd(dd, f); +return(NULL); +} +cuddRef(t); +Cudd_RecursiveDerefZdd(dd, f); +r = cuddZddUnion(dd, t, h); +if (r == NULL) { +Cudd_RecursiveDerefZdd(dd, t); +return(NULL); +} +cuddRef(r); +Cudd_RecursiveDerefZdd(dd, t); + +cuddDeref(r); +return(r); + +} /* end of cuddZddGetNodeIVO */ + + +/**Function******************************************************************** + + Synopsis [Checks the unique table for the existence of an internal node.] + + Description [Checks the unique table for the existence of an internal + node. If it does not exist, it creates a new one. Does not + modify the reference count of whatever is returned. A newly created + internal node comes back with a reference count 0. For a newly + created node, increments the reference counts of what T and E point + to. Returns a pointer to the new node if successful; NULL if memory + is exhausted or if reordering took place.] + + SideEffects [None] + + SeeAlso [cuddUniqueInterZdd] + +******************************************************************************/ +DdNode * +cuddUniqueInter( +DdManager * unique, +int index, +DdNode * T, +DdNode * E) +{ +int pos; +unsigned int level; +int retval; +DdNodePtr *nodelist; +DdNode *looking; +DdNodePtr *previousP; +DdSubtable *subtable; +int gcNumber; + +#ifdef DD_UNIQUE_PROFILE +unique->uniqueLookUps++; +#endif + +if ((0x1ffffUL & (unsigned long) unique->cacheMisses) == 0) { +if (util_cpu_time() - unique->startTime > unique->timeLimit) { +unique->errorCode = CUDD_TIMEOUT_EXPIRED; +return(NULL); +} +} +if (index >= unique->size) { +int amount = ddMax(DD_DEFAULT_RESIZE,unique->size/20); +if (!ddResizeTable(unique,index,amount)) return(NULL); +} + +level = unique->perm[index]; +subtable = &(unique->subtables[level]); + +#ifdef DD_DEBUG +assert(level < (unsigned) cuddI(unique,T->index)); +assert(level < (unsigned) cuddI(unique,Cudd_Regular(E)->index)); +#endif + +pos = ddHash(T, E, subtable->shift); +nodelist = subtable->nodelist; +previousP = &(nodelist[pos]); +looking = *previousP; + +while (T < cuddT(looking)) { +previousP = &(looking->next); +looking = *previousP; +#ifdef DD_UNIQUE_PROFILE +unique->uniqueLinks++; +#endif +} +while (T == cuddT(looking) && E < cuddE(looking)) { +previousP = &(looking->next); +looking = *previousP; +#ifdef DD_UNIQUE_PROFILE +unique->uniqueLinks++; +#endif +} +if (T == cuddT(looking) && E == cuddE(looking)) { +if (looking->ref == 0) { +cuddReclaim(unique,looking); +} +return(looking); +} + +/* countDead is 0 if deads should be counted and ~0 if they should not. */ +if (unique->autoDyn && +unique->keys - (unique->dead & unique->countDead) >= unique->nextDyn && +unique->maxReorderings > 0) { +unsigned long cpuTime; +#ifdef DD_DEBUG +retval = Cudd_DebugCheck(unique); +if (retval != 0) return(NULL); +retval = Cudd_CheckKeys(unique); +if (retval != 0) return(NULL); +#endif +retval = Cudd_ReduceHeap(unique,unique->autoMethod,10); /* 10 = whatever */ +unique->maxReorderings--; +if (retval == 0) { +unique->reordered = 2; +} else if ((cpuTime = util_cpu_time()) - unique->startTime > unique->timeLimit) { +unique->errorCode = CUDD_TIMEOUT_EXPIRED; +unique->reordered = 0; +} else if (unique->timeLimit - (cpuTime - unique->startTime) +< unique->reordTime) { +unique->autoDyn = 0; +} +#ifdef DD_DEBUG +retval = Cudd_DebugCheck(unique); +if (retval != 0) unique->reordered = 2; +retval = Cudd_CheckKeys(unique); +if (retval != 0) unique->reordered = 2; +#endif +return(NULL); +} + +if (subtable->keys > subtable->maxKeys) { +if (unique->gcEnabled && +((unique->dead > unique->minDead) || +((unique->dead > unique->minDead / 2) && +(subtable->dead > subtable->keys * 0.95)))) { /* too many dead */ +if (util_cpu_time() - unique->startTime > unique->timeLimit) { +unique->errorCode = CUDD_TIMEOUT_EXPIRED; +return(NULL); +} +(void) cuddGarbageCollect(unique,1); +} else { +cuddRehash(unique,(int)level); +} +/* Update pointer to insertion point. In the case of rehashing, +** the slot may have changed. In the case of garbage collection, +** the predecessor may have been dead. */ +pos = ddHash(T, E, subtable->shift); +nodelist = subtable->nodelist; +previousP = &(nodelist[pos]); +looking = *previousP; + +while (T < cuddT(looking)) { +previousP = &(looking->next); +looking = *previousP; +#ifdef DD_UNIQUE_PROFILE +unique->uniqueLinks++; +#endif +} +while (T == cuddT(looking) && E < cuddE(looking)) { +previousP = &(looking->next); +looking = *previousP; +#ifdef DD_UNIQUE_PROFILE +unique->uniqueLinks++; +#endif +} +} + +gcNumber = unique->garbageCollections; +looking = cuddAllocNode(unique); +if (looking == NULL) { +return(NULL); +} +unique->keys++; +subtable->keys++; + +if (gcNumber != unique->garbageCollections) { +DdNode *looking2; +pos = ddHash(T, E, subtable->shift); +nodelist = subtable->nodelist; +previousP = &(nodelist[pos]); +looking2 = *previousP; + +while (T < cuddT(looking2)) { +previousP = &(looking2->next); +looking2 = *previousP; +#ifdef DD_UNIQUE_PROFILE +unique->uniqueLinks++; +#endif +} +while (T == cuddT(looking2) && E < cuddE(looking2)) { +previousP = &(looking2->next); +looking2 = *previousP; +#ifdef DD_UNIQUE_PROFILE +unique->uniqueLinks++; +#endif +} +} +looking->index = index; +cuddT(looking) = T; +cuddE(looking) = E; +looking->next = *previousP; +*previousP = looking; +cuddSatInc(T->ref); /* we know T is a regular pointer */ +cuddRef(E); + +#ifdef DD_DEBUG +cuddCheckCollisionOrdering(unique,level,pos); +#endif + +return(looking); + +} /* end of cuddUniqueInter */ + + +/**Function******************************************************************** + + Synopsis [Wrapper for cuddUniqueInter that is independent of variable + ordering.] + + Description [Wrapper for cuddUniqueInter that is independent of + variable ordering (IVO). This function does not require parameter + index to precede the indices of the top nodes of T and E in the + variable order. Returns a pointer to the result node under normal + conditions; NULL if reordering occurred or memory was exhausted.] + + SideEffects [None] + + SeeAlso [cuddUniqueInter Cudd_MakeBddFromZddCover] + +******************************************************************************/ +DdNode * +cuddUniqueInterIVO( +DdManager * unique, +int index, +DdNode * T, +DdNode * E) +{ +DdNode *result; +DdNode *v; + +v = cuddUniqueInter(unique, index, DD_ONE(unique), +Cudd_Not(DD_ONE(unique))); +if (v == NULL) +return(NULL); +/* Since v is a projection function, we can skip the call to cuddRef. */ +result = cuddBddIteRecur(unique, v, T, E); +return(result); + +} /* end of cuddUniqueInterIVO */ + + +/**Function******************************************************************** + + Synopsis [Checks the unique table for the existence of an internal + ZDD node.] + + Description [Checks the unique table for the existence of an internal + ZDD node. If it does not exist, it creates a new one. Does not + modify the reference count of whatever is returned. A newly created + internal node comes back with a reference count 0. For a newly + created node, increments the reference counts of what T and E point + to. Returns a pointer to the new node if successful; NULL if memory + is exhausted or if reordering took place.] + + SideEffects [None] + + SeeAlso [cuddUniqueInter] + +******************************************************************************/ +DdNode * +cuddUniqueInterZdd( +DdManager * unique, +int index, +DdNode * T, +DdNode * E) +{ +int pos; +unsigned int level; +int retval; +DdNodePtr *nodelist; +DdNode *looking; +DdSubtable *subtable; + +#ifdef DD_UNIQUE_PROFILE +unique->uniqueLookUps++; +#endif + +if (index >= unique->sizeZ) { +if (!cuddResizeTableZdd(unique,index)) return(NULL); +} + +level = unique->permZ[index]; +subtable = &(unique->subtableZ[level]); + +#ifdef DD_DEBUG +assert(level < (unsigned) cuddIZ(unique,T->index)); +assert(level < (unsigned) cuddIZ(unique,Cudd_Regular(E)->index)); +#endif + +if (subtable->keys > subtable->maxKeys) { +if (unique->gcEnabled && ((unique->deadZ > unique->minDead) || +(10 * subtable->dead > 9 * subtable->keys))) { /* too many dead */ +(void) cuddGarbageCollect(unique,1); +} else { +ddRehashZdd(unique,(int)level); +} +} + +pos = ddHash(T, E, subtable->shift); +nodelist = subtable->nodelist; +looking = nodelist[pos]; + +while (looking != NULL) { +if (cuddT(looking) == T && cuddE(looking) == E) { +if (looking->ref == 0) { +cuddReclaimZdd(unique,looking); +} +return(looking); +} +looking = looking->next; +#ifdef DD_UNIQUE_PROFILE +unique->uniqueLinks++; +#endif +} + +/* countDead is 0 if deads should be counted and ~0 if they should not. */ +if (unique->autoDynZ && +unique->keysZ - (unique->deadZ & unique->countDead) >= unique->nextDyn) { +#ifdef DD_DEBUG +retval = Cudd_DebugCheck(unique); +if (retval != 0) return(NULL); +retval = Cudd_CheckKeys(unique); +if (retval != 0) return(NULL); +#endif +retval = Cudd_zddReduceHeap(unique,unique->autoMethodZ,10); /* 10 = whatever */ +if (retval == 0) unique->reordered = 2; +#ifdef DD_DEBUG +retval = Cudd_DebugCheck(unique); +if (retval != 0) unique->reordered = 2; +retval = Cudd_CheckKeys(unique); +if (retval != 0) unique->reordered = 2; +#endif +return(NULL); +} + +unique->keysZ++; +subtable->keys++; + +looking = cuddAllocNode(unique); +if (looking == NULL) return(NULL); +looking->index = index; +cuddT(looking) = T; +cuddE(looking) = E; +looking->next = nodelist[pos]; +nodelist[pos] = looking; +cuddRef(T); +cuddRef(E); + +return(looking); + +} /* end of cuddUniqueInterZdd */ + + +/**Function******************************************************************** + + Synopsis [Checks the unique table for the existence of a constant node.] + + Description [Checks the unique table for the existence of a constant node. + If it does not exist, it creates a new one. Does not + modify the reference count of whatever is returned. A newly created + internal node comes back with a reference count 0. Returns a + pointer to the new node.] + + SideEffects [None] + +******************************************************************************/ +DdNode * +cuddUniqueConst( +DdManager * unique, +CUDD_VALUE_TYPE value) +{ +int pos; +DdNodePtr *nodelist; +DdNode *looking; +hack split; + +#ifdef DD_UNIQUE_PROFILE +unique->uniqueLookUps++; +#endif + +if (unique->constants.keys > unique->constants.maxKeys) { +if (unique->gcEnabled && ((unique->dead > unique->minDead) || +(10 * unique->constants.dead > 9 * unique->constants.keys))) { /* too many dead */ +(void) cuddGarbageCollect(unique,1); +} else { +cuddRehash(unique,CUDD_CONST_INDEX); +} +} + +cuddAdjust(value); /* for the case of crippled infinities */ + +if (ddAbs(value) < unique->epsilon) { +value = 0.0; +} +split.value = value; + +pos = ddHash(split.bits[0], split.bits[1], unique->constants.shift); +nodelist = unique->constants.nodelist; +looking = nodelist[pos]; + +/* Here we compare values both for equality and for difference less + * than epsilon. The first comparison is required when values are + * infinite, since Infinity - Infinity is NaN and NaN < X is 0 for + * every X. + */ +while (looking != NULL) { +if (looking->type.value == value || +ddEqualVal(looking->type.value,value,unique->epsilon)) { +if (looking->ref == 0) { +cuddReclaim(unique,looking); +} +return(looking); +} +looking = looking->next; +#ifdef DD_UNIQUE_PROFILE +unique->uniqueLinks++; +#endif +} + +unique->keys++; +unique->constants.keys++; + +looking = cuddAllocNode(unique); +if (looking == NULL) return(NULL); +looking->index = CUDD_CONST_INDEX; +looking->type.value = value; +looking->next = nodelist[pos]; +nodelist[pos] = looking; + +return(looking); + +} /* end of cuddUniqueConst */ + + +/**Function******************************************************************** + + Synopsis [Rehashes a unique subtable.] + + Description [Doubles the size of a unique subtable and rehashes its + contents.] + + SideEffects [None] + + SeeAlso [] + +******************************************************************************/ +void +cuddRehash( +DdManager * unique, +int i) +{ +unsigned int slots, oldslots; +int shift, oldshift; +int j, pos; +DdNodePtr *nodelist, *oldnodelist; +DdNode *node, *next; +DdNode *sentinel = &(unique->sentinel); +hack split; +extern DD_OOMFP MMoutOfMemory; +DD_OOMFP saveHandler; + +if (unique->gcFrac == DD_GC_FRAC_HI && unique->slots > unique->looseUpTo) { +unique->gcFrac = DD_GC_FRAC_LO; +unique->minDead = (unsigned) (DD_GC_FRAC_LO * (double) unique->slots); +#ifdef DD_VERBOSE +(void) fprintf(unique->err,"GC fraction = %.2f\t", DD_GC_FRAC_LO); +(void) fprintf(unique->err,"minDead = %d\n", unique->minDead); +#endif +} + +if (unique->gcFrac != DD_GC_FRAC_MIN && unique->memused > unique->maxmem) { +unique->gcFrac = DD_GC_FRAC_MIN; +unique->minDead = (unsigned) (DD_GC_FRAC_MIN * (double) unique->slots); +#ifdef DD_VERBOSE +(void) fprintf(unique->err,"GC fraction = %.2f\t", DD_GC_FRAC_MIN); +(void) fprintf(unique->err,"minDead = %d\n", unique->minDead); +#endif +cuddShrinkDeathRow(unique); +if (cuddGarbageCollect(unique,1) > 0) return; +} + +if (i != CUDD_CONST_INDEX) { +oldslots = unique->subtables[i].slots; +oldshift = unique->subtables[i].shift; +oldnodelist = unique->subtables[i].nodelist; + +/* Compute the new size of the subtable. */ +slots = oldslots << 1; +shift = oldshift - 1; + +saveHandler = MMoutOfMemory; +MMoutOfMemory = Cudd_OutOfMem; +nodelist = ALLOC(DdNodePtr, slots); +MMoutOfMemory = saveHandler; +if (nodelist == NULL) { +(void) fprintf(unique->err, +"Unable to resize subtable %d for lack of memory\n", +i); +/* Prevent frequent resizing attempts. */ +(void) cuddGarbageCollect(unique,1); +if (unique->stash != NULL) { +FREE(unique->stash); +unique->stash = NULL; +/* Inhibit resizing of tables. */ +cuddSlowTableGrowth(unique); +} +return; +} +unique->subtables[i].nodelist = nodelist; +unique->subtables[i].slots = slots; +unique->subtables[i].shift = shift; +unique->subtables[i].maxKeys = slots * DD_MAX_SUBTABLE_DENSITY; + +/* Move the nodes from the old table to the new table. +** This code depends on the type of hash function. +** It assumes that the effect of doubling the size of the table +** is to retain one more bit of the 32-bit hash value. +** The additional bit is the LSB. */ +for (j = 0; (unsigned) j < oldslots; j++) { +DdNodePtr *evenP, *oddP; +node = oldnodelist[j]; +evenP = &(nodelist[j<<1]); +oddP = &(nodelist[(j<<1)+1]); +while (node != sentinel) { +next = node->next; +pos = ddHash(cuddT(node), cuddE(node), shift); +if (pos & 1) { +*oddP = node; +oddP = &(node->next); +} else { +*evenP = node; +evenP = &(node->next); +} +node = next; +} +*evenP = *oddP = sentinel; +} +FREE(oldnodelist); + +#ifdef DD_VERBOSE +(void) fprintf(unique->err, +"rehashing layer %d: keys %d dead %d new size %d\n", +i, unique->subtables[i].keys, +unique->subtables[i].dead, slots); +#endif +} else { +oldslots = unique->constants.slots; +oldshift = unique->constants.shift; +oldnodelist = unique->constants.nodelist; + +/* The constant subtable is never subjected to reordering. +** Therefore, when it is resized, it is because it has just +** reached the maximum load. We can safely just double the size, +** with no need for the loop we use for the other tables. +*/ +slots = oldslots << 1; +shift = oldshift - 1; +saveHandler = MMoutOfMemory; +MMoutOfMemory = Cudd_OutOfMem; +nodelist = ALLOC(DdNodePtr, slots); +MMoutOfMemory = saveHandler; +if (nodelist == NULL) { +(void) fprintf(unique->err, +"Unable to resize constant subtable for lack of memory\n"); +(void) cuddGarbageCollect(unique,1); +for (j = 0; j < unique->size; j++) { +unique->subtables[j].maxKeys <<= 1; +} +unique->constants.maxKeys <<= 1; +return; +} +unique->constants.slots = slots; +unique->constants.shift = shift; +unique->constants.maxKeys = slots * DD_MAX_SUBTABLE_DENSITY; +unique->constants.nodelist = nodelist; +for (j = 0; (unsigned) j < slots; j++) { +nodelist[j] = NULL; +} +for (j = 0; (unsigned) j < oldslots; j++) { +node = oldnodelist[j]; +while (node != NULL) { +next = node->next; +split.value = cuddV(node); +pos = ddHash(split.bits[0], split.bits[1], shift); +node->next = nodelist[pos]; +nodelist[pos] = node; +node = next; +} +} +FREE(oldnodelist); + +#ifdef DD_VERBOSE +(void) fprintf(unique->err, +"rehashing constants: keys %d dead %d new size %d\n", +unique->constants.keys,unique->constants.dead,slots); +#endif +} + +/* Update global data */ + +unique->memused += (slots - oldslots) * sizeof(DdNodePtr); +unique->slots += (slots - oldslots); +ddFixLimits(unique); + +} /* end of cuddRehash */ + +/**Function******************************************************************** + + Synopsis [Increases the number of ZDD subtables in a unique table so + that it meets or exceeds index.] + + Description [Increases the number of ZDD subtables in a unique table so + that it meets or exceeds index. When new ZDD variables are created, it + is possible to preserve the functions unchanged, or it is possible to + preserve the covers unchanged, but not both. cuddResizeTableZdd preserves + the covers. Returns 1 if successful; 0 otherwise.] + + SideEffects [None] + + SeeAlso [ddResizeTable] + +******************************************************************************/ +int +cuddResizeTableZdd( +DdManager * unique, +int index) +{ +DdSubtable *newsubtables; +DdNodePtr *newnodelist; +int oldsize,newsize; +int i,j,reorderSave; +unsigned int numSlots = unique->initSlots; +int *newperm, *newinvperm; + +oldsize = unique->sizeZ; +/* Easy case: there is still room in the current table. */ +if (index < unique->maxSizeZ) { +for (i = oldsize; i <= index; i++) { +unique->subtableZ[i].slots = numSlots; +unique->subtableZ[i].shift = sizeof(int) * 8 - +cuddComputeFloorLog2(numSlots); +unique->subtableZ[i].keys = 0; +unique->subtableZ[i].maxKeys = numSlots * DD_MAX_SUBTABLE_DENSITY; +unique->subtableZ[i].dead = 0; +unique->permZ[i] = i; +unique->invpermZ[i] = i; +newnodelist = unique->subtableZ[i].nodelist = +ALLOC(DdNodePtr, numSlots); +if (newnodelist == NULL) { +unique->errorCode = CUDD_MEMORY_OUT; +return(0); +} +for (j = 0; (unsigned) j < numSlots; j++) { +newnodelist[j] = NULL; +} +} +} else { +/* The current table is too small: we need to allocate a new, +** larger one; move all old subtables, and initialize the new +** subtables up to index included. +*/ +newsize = index + DD_DEFAULT_RESIZE; +#ifdef DD_VERBOSE +(void) fprintf(unique->err, +"Increasing the ZDD table size from %d to %d\n", +unique->maxSizeZ, newsize); +#endif +newsubtables = ALLOC(DdSubtable,newsize); +if (newsubtables == NULL) { +unique->errorCode = CUDD_MEMORY_OUT; +return(0); +} +newperm = ALLOC(int,newsize); +if (newperm == NULL) { +unique->errorCode = CUDD_MEMORY_OUT; +return(0); +} +newinvperm = ALLOC(int,newsize); +if (newinvperm == NULL) { +unique->errorCode = CUDD_MEMORY_OUT; +return(0); +} +unique->memused += (newsize - unique->maxSizeZ) * ((numSlots+1) * +sizeof(DdNode *) + 2 * sizeof(int) + sizeof(DdSubtable)); +if (newsize > unique->maxSize) { +FREE(unique->stack); +unique->stack = ALLOC(DdNodePtr,newsize + 1); +if (unique->stack == NULL) { +unique->errorCode = CUDD_MEMORY_OUT; +return(0); +} +unique->stack[0] = NULL; /* to suppress harmless UMR */ +unique->memused += +(newsize - ddMax(unique->maxSize,unique->maxSizeZ)) +* sizeof(DdNode *); +} +for (i = 0; i < oldsize; i++) { +newsubtables[i].slots = unique->subtableZ[i].slots; +newsubtables[i].shift = unique->subtableZ[i].shift; +newsubtables[i].keys = unique->subtableZ[i].keys; +newsubtables[i].maxKeys = unique->subtableZ[i].maxKeys; +newsubtables[i].dead = unique->subtableZ[i].dead; +newsubtables[i].nodelist = unique->subtableZ[i].nodelist; +newperm[i] = unique->permZ[i]; +newinvperm[i] = unique->invpermZ[i]; +} +for (i = oldsize; i <= index; i++) { +newsubtables[i].slots = numSlots; +newsubtables[i].shift = sizeof(int) * 8 - +cuddComputeFloorLog2(numSlots); +newsubtables[i].keys = 0; +newsubtables[i].maxKeys = numSlots * DD_MAX_SUBTABLE_DENSITY; +newsubtables[i].dead = 0; +newperm[i] = i; +newinvperm[i] = i; +newnodelist = newsubtables[i].nodelist = ALLOC(DdNodePtr, numSlots); +if (newnodelist == NULL) { +unique->errorCode = CUDD_MEMORY_OUT; +return(0); +} +for (j = 0; (unsigned) j < numSlots; j++) { +newnodelist[j] = NULL; +} +} +FREE(unique->subtableZ); +unique->subtableZ = newsubtables; +unique->maxSizeZ = newsize; +FREE(unique->permZ); +unique->permZ = newperm; +FREE(unique->invpermZ); +unique->invpermZ = newinvperm; +} +unique->slots += (index + 1 - unique->sizeZ) * numSlots; +ddFixLimits(unique); +unique->sizeZ = index + 1; + +/* Now that the table is in a coherent state, update the ZDD +** universe. We need to temporarily disable reordering, +** because we cannot reorder without universe in place. +*/ + +reorderSave = unique->autoDynZ; +unique->autoDynZ = 0; +cuddZddFreeUniv(unique); +if (!cuddZddInitUniv(unique)) { +unique->autoDynZ = reorderSave; +return(0); +} +unique->autoDynZ = reorderSave; + +return(1); + +} /* end of cuddResizeTableZdd */ + + +/**Function******************************************************************** + + Synopsis [Adjusts parameters of a table to slow down its growth.] + + Description [] + + SideEffects [None] + + SeeAlso [] + +******************************************************************************/ +void +cuddSlowTableGrowth( +DdManager *unique) +{ +int i; + +unique->maxCacheHard = unique->cacheSlots - 1; +unique->cacheSlack = - (int) (unique->cacheSlots + 1); +for (i = 0; i < unique->size; i++) { +unique->subtables[i].maxKeys <<= 2; +} +unique->gcFrac = DD_GC_FRAC_MIN; +unique->minDead = (unsigned) (DD_GC_FRAC_MIN * (double) unique->slots); +cuddShrinkDeathRow(unique); +(void) fprintf(unique->err,"Slowing down table growth: "); +(void) fprintf(unique->err,"GC fraction = %.2f\t", unique->gcFrac); +(void) fprintf(unique->err,"minDead = %u\n", unique->minDead); + +} /* end of cuddSlowTableGrowth */ + + +/*---------------------------------------------------------------------------*/ +/* Definition of static functions */ +/*---------------------------------------------------------------------------*/ + + +/**Function******************************************************************** + + Synopsis [Rehashes a ZDD unique subtable.] + + Description [] + + SideEffects [None] + + SeeAlso [cuddRehash] + +******************************************************************************/ +static void +ddRehashZdd( +DdManager * unique, +int i) +{ +unsigned int slots, oldslots; +int shift, oldshift; +int j, pos; +DdNodePtr *nodelist, *oldnodelist; +DdNode *node, *next; +extern DD_OOMFP MMoutOfMemory; +DD_OOMFP saveHandler; + +if (unique->slots > unique->looseUpTo) { +unique->minDead = (unsigned) (DD_GC_FRAC_LO * (double) unique->slots); +#ifdef DD_VERBOSE +if (unique->gcFrac == DD_GC_FRAC_HI) { +(void) fprintf(unique->err,"GC fraction = %.2f\t", +DD_GC_FRAC_LO); +(void) fprintf(unique->err,"minDead = %d\n", unique->minDead); +} +#endif +unique->gcFrac = DD_GC_FRAC_LO; +} + +assert(i != CUDD_MAXINDEX); +oldslots = unique->subtableZ[i].slots; +oldshift = unique->subtableZ[i].shift; +oldnodelist = unique->subtableZ[i].nodelist; + +/* Compute the new size of the subtable. Normally, we just +** double. However, after reordering, a table may be severely +** overloaded. Therefore, we iterate. */ +slots = oldslots; +shift = oldshift; +do { +slots <<= 1; +shift--; +} while (slots * DD_MAX_SUBTABLE_DENSITY < unique->subtableZ[i].keys); + +saveHandler = MMoutOfMemory; +MMoutOfMemory = Cudd_OutOfMem; +nodelist = ALLOC(DdNodePtr, slots); +MMoutOfMemory = saveHandler; +if (nodelist == NULL) { +(void) fprintf(unique->err, +"Unable to resize ZDD subtable %d for lack of memory.\n", +i); +(void) cuddGarbageCollect(unique,1); +for (j = 0; j < unique->sizeZ; j++) { +unique->subtableZ[j].maxKeys <<= 1; +} +return; +} +unique->subtableZ[i].nodelist = nodelist; +unique->subtableZ[i].slots = slots; +unique->subtableZ[i].shift = shift; +unique->subtableZ[i].maxKeys = slots * DD_MAX_SUBTABLE_DENSITY; +for (j = 0; (unsigned) j < slots; j++) { +nodelist[j] = NULL; +} +for (j = 0; (unsigned) j < oldslots; j++) { +node = oldnodelist[j]; +while (node != NULL) { +next = node->next; +pos = ddHash(cuddT(node), cuddE(node), shift); +node->next = nodelist[pos]; +nodelist[pos] = node; +node = next; +} +} +FREE(oldnodelist); + +#ifdef DD_VERBOSE +(void) fprintf(unique->err, +"rehashing layer %d: keys %d dead %d new size %d\n", +i, unique->subtableZ[i].keys, +unique->subtableZ[i].dead, slots); +#endif + +/* Update global data. */ +unique->memused += (slots - oldslots) * sizeof(DdNode *); +unique->slots += (slots - oldslots); +ddFixLimits(unique); + +} /* end of ddRehashZdd */ + + +/**Function******************************************************************** + + Synopsis [Increases the number of subtables in a unique table so + that it meets or exceeds index.] + + Description [Increases the number of subtables in a unique table so + that it meets or exceeds index. The parameter amount determines how + much spare space is allocated to prevent too frequent resizing. If + index is negative, the table is resized, but no new variables are + created. Returns 1 if successful; 0 otherwise.] + + SideEffects [None] + + SeeAlso [Cudd_Reserve cuddResizeTableZdd] + +******************************************************************************/ +static int +ddResizeTable( +DdManager * unique, +int index, +int amount) +{ +DdSubtable *newsubtables; +DdNodePtr *newnodelist; +DdNodePtr *newvars; +DdNode *sentinel = &(unique->sentinel); +int oldsize,newsize; +int i,j,reorderSave; +int numSlots = unique->initSlots; +int *newperm, *newinvperm, *newmap; +DdNode *one, *zero; + +oldsize = unique->size; +/* Easy case: there is still room in the current table. */ +if (index >= 0 && index < unique->maxSize) { +for (i = oldsize; i <= index; i++) { +unique->subtables[i].slots = numSlots; +unique->subtables[i].shift = sizeof(int) * 8 - +cuddComputeFloorLog2(numSlots); +unique->subtables[i].keys = 0; +unique->subtables[i].maxKeys = numSlots * DD_MAX_SUBTABLE_DENSITY; +unique->subtables[i].dead = 0; +unique->subtables[i].bindVar = 0; +unique->subtables[i].varType = CUDD_VAR_PRIMARY_INPUT; +unique->subtables[i].pairIndex = 0; +unique->subtables[i].varHandled = 0; +unique->subtables[i].varToBeGrouped = CUDD_LAZY_NONE; + +unique->perm[i] = i; +unique->invperm[i] = i; +newnodelist = unique->subtables[i].nodelist = +ALLOC(DdNodePtr, numSlots); +if (newnodelist == NULL) { +for (j = oldsize; j < i; j++) { +FREE(unique->subtables[j].nodelist); +} +unique->errorCode = CUDD_MEMORY_OUT; +return(0); +} +for (j = 0; j < numSlots; j++) { +newnodelist[j] = sentinel; +} +} +if (unique->map != NULL) { +for (i = oldsize; i <= index; i++) { +unique->map[i] = i; +} +} +} else { +/* The current table is too small: we need to allocate a new, +** larger one; move all old subtables, and initialize the new +** subtables up to index included. +*/ +newsize = (index < 0) ? amount : index + amount; +#ifdef DD_VERBOSE +(void) fprintf(unique->err, +"Increasing the table size from %d to %d\n", +unique->maxSize, newsize); +#endif +newsubtables = ALLOC(DdSubtable,newsize); +if (newsubtables == NULL) { +unique->errorCode = CUDD_MEMORY_OUT; +return(0); +} +newvars = ALLOC(DdNodePtr,newsize); +if (newvars == NULL) { +FREE(newsubtables); +unique->errorCode = CUDD_MEMORY_OUT; +return(0); +} +newperm = ALLOC(int,newsize); +if (newperm == NULL) { +FREE(newsubtables); +FREE(newvars); +unique->errorCode = CUDD_MEMORY_OUT; +return(0); +} +newinvperm = ALLOC(int,newsize); +if (newinvperm == NULL) { +FREE(newsubtables); +FREE(newvars); +FREE(newperm); +unique->errorCode = CUDD_MEMORY_OUT; +return(0); +} +if (unique->map != NULL) { +newmap = ALLOC(int,newsize); +if (newmap == NULL) { +FREE(newsubtables); +FREE(newvars); +FREE(newperm); +FREE(newinvperm); +unique->errorCode = CUDD_MEMORY_OUT; +return(0); +} +unique->memused += (newsize - unique->maxSize) * sizeof(int); +} +unique->memused += (newsize - unique->maxSize) * ((numSlots+1) * +sizeof(DdNode *) + 2 * sizeof(int) + sizeof(DdSubtable)); +if (newsize > unique->maxSizeZ) { +FREE(unique->stack); +unique->stack = ALLOC(DdNodePtr,newsize + 1); +if (unique->stack == NULL) { +FREE(newsubtables); +FREE(newvars); +FREE(newperm); +FREE(newinvperm); +if (unique->map != NULL) { +FREE(newmap); +} +unique->errorCode = CUDD_MEMORY_OUT; +return(0); +} +unique->stack[0] = NULL; /* to suppress harmless UMR */ +unique->memused += +(newsize - ddMax(unique->maxSize,unique->maxSizeZ)) +* sizeof(DdNode *); +} +for (i = 0; i < oldsize; i++) { +newsubtables[i].slots = unique->subtables[i].slots; +newsubtables[i].shift = unique->subtables[i].shift; +newsubtables[i].keys = unique->subtables[i].keys; +newsubtables[i].maxKeys = unique->subtables[i].maxKeys; +newsubtables[i].dead = unique->subtables[i].dead; +newsubtables[i].nodelist = unique->subtables[i].nodelist; +newsubtables[i].bindVar = unique->subtables[i].bindVar; +newsubtables[i].varType = unique->subtables[i].varType; +newsubtables[i].pairIndex = unique->subtables[i].pairIndex; +newsubtables[i].varHandled = unique->subtables[i].varHandled; +newsubtables[i].varToBeGrouped = unique->subtables[i].varToBeGrouped; + +newvars[i] = unique->vars[i]; +newperm[i] = unique->perm[i]; +newinvperm[i] = unique->invperm[i]; +} +for (i = oldsize; i <= index; i++) { +newsubtables[i].slots = numSlots; +newsubtables[i].shift = sizeof(int) * 8 - +cuddComputeFloorLog2(numSlots); +newsubtables[i].keys = 0; +newsubtables[i].maxKeys = numSlots * DD_MAX_SUBTABLE_DENSITY; +newsubtables[i].dead = 0; +newsubtables[i].bindVar = 0; +newsubtables[i].varType = CUDD_VAR_PRIMARY_INPUT; +newsubtables[i].pairIndex = 0; +newsubtables[i].varHandled = 0; +newsubtables[i].varToBeGrouped = CUDD_LAZY_NONE; + +newperm[i] = i; +newinvperm[i] = i; +newnodelist = newsubtables[i].nodelist = ALLOC(DdNodePtr, numSlots); +if (newnodelist == NULL) { +unique->errorCode = CUDD_MEMORY_OUT; +return(0); +} +for (j = 0; j < numSlots; j++) { +newnodelist[j] = sentinel; +} +} +if (unique->map != NULL) { +for (i = 0; i < oldsize; i++) { +newmap[i] = unique->map[i]; +} +for (i = oldsize; i <= index; i++) { +newmap[i] = i; +} +FREE(unique->map); +unique->map = newmap; +} +FREE(unique->subtables); +unique->subtables = newsubtables; +unique->maxSize = newsize; +FREE(unique->vars); +unique->vars = newvars; +FREE(unique->perm); +unique->perm = newperm; +FREE(unique->invperm); +unique->invperm = newinvperm; +} + +/* Now that the table is in a coherent state, create the new +** projection functions. We need to temporarily disable reordering, +** because we cannot reorder without projection functions in place. +**/ +if (index >= 0) { +one = unique->one; +zero = Cudd_Not(one); + +unique->size = index + 1; +if (unique->tree != NULL) { +unique->tree->size = ddMax(unique->tree->size, unique->size); +} +unique->slots += (index + 1 - oldsize) * numSlots; +ddFixLimits(unique); + +reorderSave = unique->autoDyn; +unique->autoDyn = 0; +for (i = oldsize; i <= index; i++) { +unique->vars[i] = cuddUniqueInter(unique,i,one,zero); +if (unique->vars[i] == NULL) { +unique->autoDyn = reorderSave; +for (j = oldsize; j < i; j++) { +Cudd_IterDerefBdd(unique,unique->vars[j]); +cuddDeallocNode(unique,unique->vars[j]); +unique->vars[j] = NULL; +} +for (j = oldsize; j <= index; j++) { +FREE(unique->subtables[j].nodelist); +unique->subtables[j].nodelist = NULL; +} +unique->size = oldsize; +unique->slots -= (index + 1 - oldsize) * numSlots; +ddFixLimits(unique); +return(0); +} +cuddRef(unique->vars[i]); +} +unique->autoDyn = reorderSave; +} + +return(1); + +} /* end of ddResizeTable */ + + +/* end of cuddFindParent */ + + +/**Function******************************************************************** + + Synopsis [Adjusts the values of table limits.] + + Description [Adjusts the values of table fields controlling the. + sizes of subtables and computed table. If the computed table is too small + according to the new values, it is resized.] + + SideEffects [Modifies manager fields. May resize computed table.] + + SeeAlso [] + +******************************************************************************/ +DD_INLINE +static void +ddFixLimits( +DdManager *unique) +{ +unique->minDead = (unsigned) (unique->gcFrac * (double) unique->slots); +unique->cacheSlack = (int) ddMin(unique->maxCacheHard, +DD_MAX_CACHE_TO_SLOTS_RATIO * unique->slots) - +2 * (int) unique->cacheSlots; +if (unique->cacheSlots < unique->slots/2 && unique->cacheSlack >= 0) +cuddCacheResize(unique); +return; + +} /* end of ddFixLimits */ + + +#ifndef DD_UNSORTED_FREE_LIST +#ifdef DD_RED_BLACK_FREE_LIST +/**Function******************************************************************** + + Synopsis [Inserts a DdNode in a red/black search tree.] + + Description [Inserts a DdNode in a red/black search tree. Nodes from + the same "page" (defined by DD_PAGE_MASK) are linked in a LIFO list.] + + SideEffects [None] + + SeeAlso [cuddOrderedThread] + +******************************************************************************/ +static void +cuddOrderedInsert( +DdNodePtr * root, +DdNodePtr node) +{ +DdNode *scan; +DdNodePtr *scanP; +DdNodePtr *stack[DD_STACK_SIZE]; +int stackN = 0; + +scanP = root; +while ((scan = *scanP) != NULL) { +stack[stackN++] = scanP; +if (DD_INSERT_COMPARE(node, scan) == 0) { /* add to page list */ +DD_NEXT(node) = DD_NEXT(scan); +DD_NEXT(scan) = node; +return; +} +scanP = (node < scan) ? &DD_LEFT(scan) : &DD_RIGHT(scan); +} +DD_RIGHT(node) = DD_LEFT(node) = DD_NEXT(node) = NULL; +DD_COLOR(node) = DD_RED; +*scanP = node; +stack[stackN] = &node; +cuddDoRebalance(stack,stackN); + +} /* end of cuddOrderedInsert */ + + +/**Function******************************************************************** + + Synopsis [Threads all the nodes of a search tree into a linear list.] + + Description [Threads all the nodes of a search tree into a linear + list. For each node of the search tree, the "left" child, if non-null, has + a lower address than its parent, and the "right" child, if non-null, has a + higher address than its parent. + The list is sorted in order of increasing addresses. The search + tree is destroyed as a result of this operation. The last element of + the linear list is made to point to the address passed in list. Each + node if the search tree is a linearly-linked list of nodes from the + same memory page (as defined in DD_PAGE_MASK). When a node is added to + the linear list, all the elements of the linked list are added.] + + SideEffects [The search tree is destroyed as a result of this operation.] + + SeeAlso [cuddOrderedInsert] + +******************************************************************************/ +static DdNode * +cuddOrderedThread( +DdNode * root, +DdNode * list) +{ +DdNode *current, *next, *prev, *end; + +current = root; +/* The first word in the node is used to implement a stack that holds +** the nodes from the root of the tree to the current node. Here we +** put the root of the tree at the bottom of the stack. +*/ +*((DdNodePtr *) current) = NULL; + +while (current != NULL) { +if (DD_RIGHT(current) != NULL) { +/* If possible, we follow the "right" link. Eventually we'll +** find the node with the largest address in the current tree. +** In this phase we use the first word of a node to implemen +** a stack of the nodes on the path from the root to "current". +** Also, we disconnect the "right" pointers to indicate that +** we have already followed them. +*/ +next = DD_RIGHT(current); +DD_RIGHT(current) = NULL; +*((DdNodePtr *)next) = current; +current = next; +} else { +/* We can't proceed along the "right" links any further. +** Hence "current" is the largest element in the current tree. +** We make this node the new head of "list". (Repeating this +** operation until the tree is empty yields the desired linear +** threading of all nodes.) +*/ +prev = *((DdNodePtr *) current); /* save prev node on stack in prev */ +/* Traverse the linked list of current until the end. */ +for (end = current; DD_NEXT(end) != NULL; end = DD_NEXT(end)); +DD_NEXT(end) = list; /* attach "list" at end and make */ +list = current; /* "current" the new head of "list" */ +/* Now, if current has a "left" child, we push it on the stack. +** Otherwise, we just continue with the parent of "current". +*/ +if (DD_LEFT(current) != NULL) { +next = DD_LEFT(current); +*((DdNodePtr *) next) = prev; +current = next; +} else { +current = prev; +} +} +} + +return(list); + +} /* end of cuddOrderedThread */ + + +/**Function******************************************************************** + + Synopsis [Performs the left rotation for red/black trees.] + + Description [] + + SideEffects [None] + + SeeAlso [cuddRotateRight] + +******************************************************************************/ +DD_INLINE +static void +cuddRotateLeft( +DdNodePtr * nodeP) +{ +DdNode *newRoot; +DdNode *oldRoot = *nodeP; + +*nodeP = newRoot = DD_RIGHT(oldRoot); +DD_RIGHT(oldRoot) = DD_LEFT(newRoot); +DD_LEFT(newRoot) = oldRoot; + +} /* end of cuddRotateLeft */ + + +/**Function******************************************************************** + + Synopsis [Performs the right rotation for red/black trees.] + + Description [] + + SideEffects [None] + + SeeAlso [cuddRotateLeft] + +******************************************************************************/ +DD_INLINE +static void +cuddRotateRight( +DdNodePtr * nodeP) +{ +DdNode *newRoot; +DdNode *oldRoot = *nodeP; + +*nodeP = newRoot = DD_LEFT(oldRoot); +DD_LEFT(oldRoot) = DD_RIGHT(newRoot); +DD_RIGHT(newRoot) = oldRoot; + +} /* end of cuddRotateRight */ + + +/**Function******************************************************************** + + Synopsis [Rebalances a red/black tree.] + + Description [] + + SideEffects [None] + + SeeAlso [] + +******************************************************************************/ +static void +cuddDoRebalance( +DdNodePtr ** stack, +int stackN) +{ +DdNodePtr *xP, *parentP, *grandpaP; +DdNode *x, *y, *parent, *grandpa; + +xP = stack[stackN]; +x = *xP; +/* Work our way back up, re-balancing the tree. */ +while (--stackN >= 0) { +parentP = stack[stackN]; +parent = *parentP; +if (DD_IS_BLACK(parent)) break; +/* Since the root is black, here a non-null grandparent exists. */ +grandpaP = stack[stackN-1]; +grandpa = *grandpaP; +if (parent == DD_LEFT(grandpa)) { +y = DD_RIGHT(grandpa); +if (y != NULL && DD_IS_RED(y)) { +DD_COLOR(parent) = DD_BLACK; +DD_COLOR(y) = DD_BLACK; +DD_COLOR(grandpa) = DD_RED; +x = grandpa; +stackN--; +} else { +if (x == DD_RIGHT(parent)) { +cuddRotateLeft(parentP); +DD_COLOR(x) = DD_BLACK; +} else { +DD_COLOR(parent) = DD_BLACK; +} +DD_COLOR(grandpa) = DD_RED; +cuddRotateRight(grandpaP); +break; +} +} else { +y = DD_LEFT(grandpa); +if (y != NULL && DD_IS_RED(y)) { +DD_COLOR(parent) = DD_BLACK; +DD_COLOR(y) = DD_BLACK; +DD_COLOR(grandpa) = DD_RED; +x = grandpa; +stackN--; +} else { +if (x == DD_LEFT(parent)) { +cuddRotateRight(parentP); +DD_COLOR(x) = DD_BLACK; +} else { +DD_COLOR(parent) = DD_BLACK; +} +DD_COLOR(grandpa) = DD_RED; +cuddRotateLeft(grandpaP); +} +} +} +DD_COLOR(*(stack[0])) = DD_BLACK; + +} /* end of cuddDoRebalance */ +#endif +#endif + + +/**Function******************************************************************** + + Synopsis [Fixes a variable tree after the insertion of new subtables.] + + Description [Fixes a variable tree after the insertion of new subtables. + After such an insertion, the low fields of the tree below the insertion + point are inconsistent.] + + SideEffects [None] + + SeeAlso [] + +******************************************************************************/ +static void +ddPatchTree( +DdManager *dd, +MtrNode *treenode) +{ +MtrNode *auxnode = treenode; + +while (auxnode != NULL) { +auxnode->low = dd->perm[auxnode->index]; +if (auxnode->child != NULL) { +ddPatchTree(dd, auxnode->child); +} +auxnode = auxnode->younger; +} + +return; + +} /* end of ddPatchTree */ + + +#ifdef DD_DEBUG +/**Function******************************************************************** + + Synopsis [Checks whether a collision list is ordered.] + + Description [] + + SideEffects [None] + + SeeAlso [] + +******************************************************************************/ +static int +cuddCheckCollisionOrdering( +DdManager *unique, +int i, +int j) +{ +int slots; +DdNode *node, *next; +DdNodePtr *nodelist; +DdNode *sentinel = &(unique->sentinel); + +nodelist = unique->subtables[i].nodelist; +slots = unique->subtables[i].slots; +node = nodelist[j]; +if (node == sentinel) return(1); +next = node->next; +while (next != sentinel) { +if (cuddT(node) < cuddT(next) || +(cuddT(node) == cuddT(next) && cuddE(node) < cuddE(next))) { +(void) fprintf(unique->err, +"Unordered list: index %u, position %d\n", i, j); +return(0); +} +node = next; +next = node->next; +} +return(1); + +} /* end of cuddCheckCollisionOrdering */ +#endif + + + + +/**Function******************************************************************** + + Synopsis [Reports problem in garbage collection.] + + Description [] + + SideEffects [None] + + SeeAlso [cuddGarbageCollect cuddGarbageCollectZdd] + +******************************************************************************/ +static void +ddReportRefMess( +DdManager *unique /* manager */, +int i /* table in which the problem occurred */, +const char *caller /* procedure that detected the problem */) +{ +if (i == CUDD_CONST_INDEX) { +(void) fprintf(unique->err, +"%s: problem in constants\n", caller); +} else if (i != -1) { +(void) fprintf(unique->err, +"%s: problem in table %d\n", caller, i); +} +(void) fprintf(unique->err, " dead count != deleted\n"); +(void) fprintf(unique->err, " This problem is often due to a missing \ +call to Cudd_Ref\n or to an extra call to Cudd_RecursiveDeref.\n \ +See the CUDD Programmer's Guide for additional details."); +abort(); + +} /* end of ddReportRefMess */ +/**CFile*********************************************************************** + + FileName [cuddUtil.c] + + PackageName [cudd] + + Synopsis [Utility functions.] + + Description [External procedures included in this module: +
        +
      • Cudd_PrintMinterm() +
      • Cudd_bddPrintCover() +
      • Cudd_PrintDebug() +
      • Cudd_DagSize() +
      • Cudd_EstimateCofactor() +
      • Cudd_EstimateCofactorSimple() +
      • Cudd_SharingSize() +
      • Cudd_CountMinterm() +
      • Cudd_EpdCountMinterm() +
      • Cudd_CountPath() +
      • Cudd_CountPathsToNonZero() +
      • Cudd_SupportIndices() +
      • Cudd_Support() +
      • Cudd_SupportIndex() +
      • Cudd_SupportSize() +
      • Cudd_VectorSupportIndices() +
      • Cudd_VectorSupport() +
      • Cudd_VectorSupportIndex() +
      • Cudd_VectorSupportSize() +
      • Cudd_ClassifySupport() +
      • Cudd_CountLeaves() +
      • Cudd_bddPickOneCube() +
      • Cudd_bddPickOneMinterm() +
      • Cudd_bddPickArbitraryMinterms() +
      • Cudd_SubsetWithMaskVars() +
      • Cudd_FirstCube() +
      • Cudd_NextCube() +
      • Cudd_bddComputeCube() +
      • Cudd_addComputeCube() +
      • Cudd_FirstNode() +
      • Cudd_NextNode() +
      • Cudd_GenFree() +
      • Cudd_IsGenEmpty() +
      • Cudd_IndicesToCube() +
      • Cudd_PrintVersion() +
      • Cudd_AverageDistance() +
      • Cudd_Random() +
      • Cudd_Srandom() +
      • Cudd_Density() +
      + Internal procedures included in this module: +
        +
      • cuddP() +
      • cuddStCountfree() +
      • cuddCollectNodes() +
      • cuddNodeArray() +
      + Static procedures included in this module: +
        +
      • dp2() +
      • ddPrintMintermAux() +
      • ddDagInt() +
      • ddCountMintermAux() +
      • ddEpdCountMintermAux() +
      • ddCountPathAux() +
      • ddSupportStep() +
      • ddClearFlag() +
      • ddLeavesInt() +
      • ddPickArbitraryMinterms() +
      • ddPickRepresentativeCube() +
      • ddEpdFree() +
      • ddFindSupport() +
      • ddClearVars() +
      • indexCompare() +
      ] + + Author [Fabio Somenzi] + + Copyright [Copyright (c) 1995-2012, Regents of the University of Colorado + + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + Neither the name of the University of Colorado nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE.] + +******************************************************************************/ + + +/*---------------------------------------------------------------------------*/ +/* Constant declarations */ +/*---------------------------------------------------------------------------*/ + +/* Random generator constants. */ +#define MODULUS1 2147483563 +#define LEQA1 40014 +#define LEQQ1 53668 +#define LEQR1 12211 +#define MODULUS2 2147483399 +#define LEQA2 40692 +#define LEQQ2 52774 +#define LEQR2 3791 +#define STAB_SIZE 64 +#define STAB_DIV (1 + (MODULUS1 - 1) / STAB_SIZE) + +/*---------------------------------------------------------------------------*/ +/* Stucture declarations */ +/*---------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------*/ +/* Type declarations */ +/*---------------------------------------------------------------------------*/ + + +/*---------------------------------------------------------------------------*/ +/* Variable declarations */ +/*---------------------------------------------------------------------------*/ + +//#ifndef lint +//static char rcsid[] DD_UNUSED = "$Id: cuddUtil.c,v 1.83 2012/02/05 01:07:19 fabio Exp $"; +//#endif + +static DdNode *background, *zero; + +static long cuddRand = 0; +static long cuddRand2; +static long shuffleSelect; +static long shuffleTable[STAB_SIZE]; + +/*---------------------------------------------------------------------------*/ +/* Macro declarations */ +/*---------------------------------------------------------------------------*/ + +#define bang(f) ((Cudd_IsComplement(f)) ? '!' : ' ') + +#ifdef __cplusplus +extern "C" { +#endif + +/**AutomaticStart*************************************************************/ + +/*---------------------------------------------------------------------------*/ +/* Static function prototypes */ +/*---------------------------------------------------------------------------*/ + +static int dp2 (DdManager *dd, DdNode *f, st_table *t); +static void ddPrintMintermAux (DdManager *dd, DdNode *node, int *list); +static int ddDagInt (DdNode *n); +static int cuddNodeArrayRecur (DdNode *f, DdNodePtr *table, int index); +static int cuddEstimateCofactor (DdManager *dd, st_table *table, DdNode * node, int i, int phase, DdNode ** ptr); +static DdNode * cuddUniqueLookup (DdManager * unique, int index, DdNode * T, DdNode * E); +static int cuddEstimateCofactorSimple (DdNode * node, int i); +static double ddCountMintermAux (DdNode *node, double max, DdHashTable *table); +static int ddEpdCountMintermAux (DdNode *node, EpDouble *max, EpDouble *epd, st_table *table); +static double ddCountPathAux (DdNode *node, st_table *table); +static double ddCountPathsToNonZero (DdNode * N, st_table * table); +static void ddSupportStep (DdNode *f, int *support); +static void ddClearFlag (DdNode *f); +static int ddLeavesInt (DdNode *n); +static int ddPickArbitraryMinterms (DdManager *dd, DdNode *node, int nvars, int nminterms, char **string); + +static void ddFindSupport(DdManager *dd, DdNode *f, int *SP); + +/**AutomaticEnd***************************************************************/ + +#ifdef __cplusplus +} +#endif + +/*---------------------------------------------------------------------------*/ +/* Definition of exported functions */ +/*---------------------------------------------------------------------------*/ + + +/**Function******************************************************************** + + Synopsis [Counts the number of minterms of a DD.] + + Description [Counts the number of minterms of a DD. The function is + assumed to depend on nvars variables. The minterm count is + represented as a double, to allow for a larger number of variables. + Returns the number of minterms of the function rooted at node if + successful; (double) CUDD_OUT_OF_MEM otherwise.] + + SideEffects [None] + + SeeAlso [Cudd_PrintDebug Cudd_CountPath] + +******************************************************************************/ +double +Cudd_CountMinterm( +DdManager * manager, +DdNode * node, +int nvars) +{ +double max; +DdHashTable *table; +double res; +CUDD_VALUE_TYPE epsilon; + +background = manager->background; +zero = Cudd_Not(manager->one); + +max = pow(2.0,(double)nvars); +table = cuddHashTableInit(manager,1,2); +if (table == NULL) { +return((double)CUDD_OUT_OF_MEM); +} +epsilon = Cudd_ReadEpsilon(manager); +Cudd_SetEpsilon(manager,(CUDD_VALUE_TYPE)0.0); +res = ddCountMintermAux(node,max,table); +cuddHashTableQuit(table); +Cudd_SetEpsilon(manager,epsilon); + +return(res); + +} /* end of Cudd_CountMinterm */ + + + + +/**Function******************************************************************** + + Synopsis [Finds the first cube of a decision diagram.] + + Description [Defines an iterator on the onset of a decision diagram + and finds its first cube. Returns a generator that contains the + information necessary to continue the enumeration if successful; NULL + otherwise.

      + A cube is represented as an array of literals, which are integers in + {0, 1, 2}; 0 represents a complemented literal, 1 represents an + uncomplemented literal, and 2 stands for don't care. The enumeration + produces a disjoint cover of the function associated with the diagram. + The size of the array equals the number of variables in the manager at + the time Cudd_FirstCube is called.

      + For each cube, a value is also returned. This value is always 1 for a + BDD, while it may be different from 1 for an ADD. + For BDDs, the offset is the set of cubes whose value is the logical zero. + For ADDs, the offset is the set of cubes whose value is the + background value. The cubes of the offset are not enumerated.] + + SideEffects [The first cube and its value are returned as side effects.] + + SeeAlso [Cudd_ForeachCube Cudd_NextCube Cudd_GenFree Cudd_IsGenEmpty + Cudd_FirstNode] + +******************************************************************************/ +DdGen * +Cudd_FirstCube( +DdManager * dd, +DdNode * f, +int ** cube, +CUDD_VALUE_TYPE * value) +{ +DdGen *gen; +DdNode *top, *treg, *next, *nreg, *prev, *preg; +int i; +int nvars; + +/* Sanity Check. */ +if (dd == NULL || f == NULL) return(NULL); + +/* Allocate generator an initialize it. */ +gen = ALLOC(DdGen,1); +if (gen == NULL) { +dd->errorCode = CUDD_MEMORY_OUT; +return(NULL); +} + +gen->manager = dd; +gen->type = CUDD_GEN_CUBES; +gen->status = CUDD_GEN_EMPTY; +gen->gen.cubes.cube = NULL; +gen->gen.cubes.value = DD_ZERO_VAL; +gen->stack.sp = 0; +gen->stack.stack = NULL; +gen->node = NULL; + +nvars = dd->size; +gen->gen.cubes.cube = ALLOC(int,nvars); +if (gen->gen.cubes.cube == NULL) { +dd->errorCode = CUDD_MEMORY_OUT; +FREE(gen); +return(NULL); +} +for (i = 0; i < nvars; i++) gen->gen.cubes.cube[i] = 2; + +/* The maximum stack depth is one plus the number of variables. +** because a path may have nodes at all levels, including the +** constant level. +*/ +gen->stack.stack = ALLOC(DdNodePtr, nvars+1); +if (gen->stack.stack == NULL) { +dd->errorCode = CUDD_MEMORY_OUT; +FREE(gen->gen.cubes.cube); +FREE(gen); +return(NULL); +} +for (i = 0; i <= nvars; i++) gen->stack.stack[i] = NULL; + +/* Find the first cube of the onset. */ +gen->stack.stack[gen->stack.sp] = f; gen->stack.sp++; + +while (1) { +top = gen->stack.stack[gen->stack.sp-1]; +treg = Cudd_Regular(top); +if (!cuddIsConstant(treg)) { +/* Take the else branch first. */ +gen->gen.cubes.cube[treg->index] = 0; +next = cuddE(treg); +if (top != treg) next = Cudd_Not(next); +gen->stack.stack[gen->stack.sp] = next; gen->stack.sp++; +} else if (top == Cudd_Not(DD_ONE(dd)) || top == dd->background) { +/* Backtrack */ +while (1) { +if (gen->stack.sp == 1) { +/* The current node has no predecessor. */ +gen->status = CUDD_GEN_EMPTY; +gen->stack.sp--; +goto done; +} +prev = gen->stack.stack[gen->stack.sp-2]; +preg = Cudd_Regular(prev); +nreg = cuddT(preg); +if (prev != preg) {next = Cudd_Not(nreg);} else {next = nreg;} +if (next != top) { /* follow the then branch next */ +gen->gen.cubes.cube[preg->index] = 1; +gen->stack.stack[gen->stack.sp-1] = next; +break; +} +/* Pop the stack and try again. */ +gen->gen.cubes.cube[preg->index] = 2; +gen->stack.sp--; +top = gen->stack.stack[gen->stack.sp-1]; +treg = Cudd_Regular(top); +} +} else { +gen->status = CUDD_GEN_NONEMPTY; +gen->gen.cubes.value = cuddV(top); +goto done; +} +} + +done: +*cube = gen->gen.cubes.cube; +*value = gen->gen.cubes.value; +return(gen); + +} /* end of Cudd_FirstCube */ + + +/**Function******************************************************************** + + Synopsis [Generates the next cube of a decision diagram onset.] + + Description [Generates the next cube of a decision diagram onset, + using generator gen. Returns 0 if the enumeration is completed; 1 + otherwise.] + + SideEffects [The cube and its value are returned as side effects. The + generator is modified.] + + SeeAlso [Cudd_ForeachCube Cudd_FirstCube Cudd_GenFree Cudd_IsGenEmpty + Cudd_NextNode] + +******************************************************************************/ +int +Cudd_NextCube( +DdGen * gen, +int ** cube, +CUDD_VALUE_TYPE * value) +{ +DdNode *top, *treg, *next, *nreg, *prev, *preg; +DdManager *dd = gen->manager; + +/* Backtrack from previously reached terminal node. */ +while (1) { +if (gen->stack.sp == 1) { +/* The current node has no predecessor. */ +gen->status = CUDD_GEN_EMPTY; +gen->stack.sp--; +goto done; +} +top = gen->stack.stack[gen->stack.sp-1]; +treg = Cudd_Regular(top); +prev = gen->stack.stack[gen->stack.sp-2]; +preg = Cudd_Regular(prev); +nreg = cuddT(preg); +if (prev != preg) {next = Cudd_Not(nreg);} else {next = nreg;} +if (next != top) { /* follow the then branch next */ +gen->gen.cubes.cube[preg->index] = 1; +gen->stack.stack[gen->stack.sp-1] = next; +break; +} +/* Pop the stack and try again. */ +gen->gen.cubes.cube[preg->index] = 2; +gen->stack.sp--; +} + +while (1) { +top = gen->stack.stack[gen->stack.sp-1]; +treg = Cudd_Regular(top); +if (!cuddIsConstant(treg)) { +/* Take the else branch first. */ +gen->gen.cubes.cube[treg->index] = 0; +next = cuddE(treg); +if (top != treg) next = Cudd_Not(next); +gen->stack.stack[gen->stack.sp] = next; gen->stack.sp++; +} else if (top == Cudd_Not(DD_ONE(dd)) || top == dd->background) { +/* Backtrack */ +while (1) { +if (gen->stack.sp == 1) { +/* The current node has no predecessor. */ +gen->status = CUDD_GEN_EMPTY; +gen->stack.sp--; +goto done; +} +prev = gen->stack.stack[gen->stack.sp-2]; +preg = Cudd_Regular(prev); +nreg = cuddT(preg); +if (prev != preg) {next = Cudd_Not(nreg);} else {next = nreg;} +if (next != top) { /* follow the then branch next */ +gen->gen.cubes.cube[preg->index] = 1; +gen->stack.stack[gen->stack.sp-1] = next; +break; +} +/* Pop the stack and try again. */ +gen->gen.cubes.cube[preg->index] = 2; +gen->stack.sp--; +top = gen->stack.stack[gen->stack.sp-1]; +treg = Cudd_Regular(top); +} +} else { +gen->status = CUDD_GEN_NONEMPTY; +gen->gen.cubes.value = cuddV(top); +goto done; +} +} + +done: +if (gen->status == CUDD_GEN_EMPTY) return(0); +*cube = gen->gen.cubes.cube; +*value = gen->gen.cubes.value; +return(1); + +} /* end of Cudd_NextCube */ + + +/**Function******************************************************************** + + Synopsis [Finds the first prime of a Boolean function.] + + Description [Defines an iterator on a pair of BDDs describing a + (possibly incompletely specified) Boolean functions and finds the + first cube of a cover of the function. Returns a generator + that contains the information necessary to continue the enumeration + if successful; NULL otherwise.

      + + The two argument BDDs are the lower and upper bounds of an interval. + It is a mistake to call this function with a lower bound that is not + less than or equal to the upper bound.

      + + A cube is represented as an array of literals, which are integers in + {0, 1, 2}; 0 represents a complemented literal, 1 represents an + uncomplemented literal, and 2 stands for don't care. The enumeration + produces a prime and irredundant cover of the function associated + with the two BDDs. The size of the array equals the number of + variables in the manager at the time Cudd_FirstCube is called.

      + + This iterator can only be used on BDDs.] + + SideEffects [The first cube is returned as side effect.] + + SeeAlso [Cudd_ForeachPrime Cudd_NextPrime Cudd_GenFree Cudd_IsGenEmpty + Cudd_FirstCube Cudd_FirstNode] + +******************************************************************************/ +DdGen * +Cudd_FirstPrime( +DdManager *dd, +DdNode *l, +DdNode *u, +int **cube) +{ +DdGen *gen; +DdNode *implicant, *prime, *tmp; +int length, result; + +/* Sanity Check. */ +if (dd == NULL || l == NULL || u == NULL) return(NULL); + +/* Allocate generator an initialize it. */ +gen = ALLOC(DdGen,1); +if (gen == NULL) { +dd->errorCode = CUDD_MEMORY_OUT; +return(NULL); +} + +gen->manager = dd; +gen->type = CUDD_GEN_PRIMES; +gen->status = CUDD_GEN_EMPTY; +gen->gen.primes.cube = NULL; +gen->gen.primes.ub = u; +gen->stack.sp = 0; +gen->stack.stack = NULL; +gen->node = l; +cuddRef(l); + +gen->gen.primes.cube = ALLOC(int,dd->size); +if (gen->gen.primes.cube == NULL) { +dd->errorCode = CUDD_MEMORY_OUT; +FREE(gen); +return(NULL); +} + +if (gen->node == Cudd_ReadLogicZero(dd)) { +gen->status = CUDD_GEN_EMPTY; +} else { +implicant = Cudd_LargestCube(dd,gen->node,&length); +if (implicant == NULL) { +Cudd_RecursiveDeref(dd,gen->node); +FREE(gen->gen.primes.cube); +FREE(gen); +return(NULL); +} +cuddRef(implicant); +prime = Cudd_bddMakePrime(dd,implicant,gen->gen.primes.ub); +if (prime == NULL) { +Cudd_RecursiveDeref(dd,gen->node); +Cudd_RecursiveDeref(dd,implicant); +FREE(gen->gen.primes.cube); +FREE(gen); +return(NULL); +} +cuddRef(prime); +Cudd_RecursiveDeref(dd,implicant); +tmp = Cudd_bddAnd(dd,gen->node,Cudd_Not(prime)); +if (tmp == NULL) { +Cudd_RecursiveDeref(dd,gen->node); +Cudd_RecursiveDeref(dd,prime); +FREE(gen->gen.primes.cube); +FREE(gen); +return(NULL); +} +cuddRef(tmp); +Cudd_RecursiveDeref(dd,gen->node); +gen->node = tmp; +result = Cudd_BddToCubeArray(dd,prime,gen->gen.primes.cube); +if (result == 0) { +Cudd_RecursiveDeref(dd,gen->node); +Cudd_RecursiveDeref(dd,prime); +FREE(gen->gen.primes.cube); +FREE(gen); +return(NULL); +} +Cudd_RecursiveDeref(dd,prime); +gen->status = CUDD_GEN_NONEMPTY; +} +*cube = gen->gen.primes.cube; +return(gen); + +} /* end of Cudd_FirstPrime */ + + +/**Function******************************************************************** + + Synopsis [Generates the next prime of a Boolean function.] + + Description [Generates the next cube of a Boolean function, + using generator gen. Returns 0 if the enumeration is completed; 1 + otherwise.] + + SideEffects [The cube and is returned as side effects. The + generator is modified.] + + SeeAlso [Cudd_ForeachPrime Cudd_FirstPrime Cudd_GenFree Cudd_IsGenEmpty + Cudd_NextCube Cudd_NextNode] + +******************************************************************************/ +int +Cudd_NextPrime( +DdGen *gen, +int **cube) +{ +DdNode *implicant, *prime, *tmp; +DdManager *dd = gen->manager; +int length, result; + +if (gen->node == Cudd_ReadLogicZero(dd)) { +gen->status = CUDD_GEN_EMPTY; +} else { +implicant = Cudd_LargestCube(dd,gen->node,&length); +if (implicant == NULL) { +gen->status = CUDD_GEN_EMPTY; +return(0); +} +cuddRef(implicant); +prime = Cudd_bddMakePrime(dd,implicant,gen->gen.primes.ub); +if (prime == NULL) { +Cudd_RecursiveDeref(dd,implicant); +gen->status = CUDD_GEN_EMPTY; +return(0); +} +cuddRef(prime); +Cudd_RecursiveDeref(dd,implicant); +tmp = Cudd_bddAnd(dd,gen->node,Cudd_Not(prime)); +if (tmp == NULL) { +Cudd_RecursiveDeref(dd,prime); +gen->status = CUDD_GEN_EMPTY; +return(0); +} +cuddRef(tmp); +Cudd_RecursiveDeref(dd,gen->node); +gen->node = tmp; +result = Cudd_BddToCubeArray(dd,prime,gen->gen.primes.cube); +if (result == 0) { +Cudd_RecursiveDeref(dd,prime); +gen->status = CUDD_GEN_EMPTY; +return(0); +} +Cudd_RecursiveDeref(dd,prime); +gen->status = CUDD_GEN_NONEMPTY; +} +if (gen->status == CUDD_GEN_EMPTY) return(0); +*cube = gen->gen.primes.cube; +return(1); + +} /* end of Cudd_NextPrime */ + + + +/**Function******************************************************************** + + Synopsis [Builds a positional array from the BDD of a cube.] + + Description [Builds a positional array from the BDD of a cube. + Array must have one entry for each BDD variable. The positional + array has 1 in i-th position if the variable of index i appears in + true form in the cube; it has 0 in i-th position if the variable of + index i appears in complemented form in the cube; finally, it has 2 + in i-th position if the variable of index i does not appear in the + cube. Returns 1 if successful (the BDD is indeed a cube); 0 + otherwise.] + + SideEffects [The result is in the array passed by reference.] + + SeeAlso [Cudd_CubeArrayToBdd] + +******************************************************************************/ +int +Cudd_BddToCubeArray( +DdManager *dd, +DdNode *cube, +int *array) +{ +DdNode *scan, *t, *e; +int i; +int size = Cudd_ReadSize(dd); +DdNode *zero = Cudd_Not(DD_ONE(dd)); + +for (i = size-1; i >= 0; i--) { +array[i] = 2; +} +scan = cube; +while (!Cudd_IsConstant(scan)) { +int index = Cudd_Regular(scan)->index; +cuddGetBranches(scan,&t,&e); +if (t == zero) { +array[index] = 0; +scan = e; +} else if (e == zero) { +array[index] = 1; +scan = t; +} else { +return(0); /* cube is not a cube */ +} +} +if (scan == zero) { +return(0); +} else { +return(1); +} + +} /* end of Cudd_BddToCubeArray */ + + +/**Function******************************************************************** + + Synopsis [Finds the first node of a decision diagram.] + + Description [Defines an iterator on the nodes of a decision diagram + and finds its first node. Returns a generator that contains the + information necessary to continue the enumeration if successful; + NULL otherwise. The nodes are enumerated in a reverse topological + order, so that a node is always preceded in the enumeration by its + descendants.] + + SideEffects [The first node is returned as a side effect.] + + SeeAlso [Cudd_ForeachNode Cudd_NextNode Cudd_GenFree Cudd_IsGenEmpty + Cudd_FirstCube] + +******************************************************************************/ +DdGen * +Cudd_FirstNode( +DdManager * dd, +DdNode * f, +DdNode ** node) +{ +DdGen *gen; +int size; + +/* Sanity Check. */ +if (dd == NULL || f == NULL) return(NULL); + +/* Allocate generator an initialize it. */ +gen = ALLOC(DdGen,1); +if (gen == NULL) { +dd->errorCode = CUDD_MEMORY_OUT; +return(NULL); +} + +gen->manager = dd; +gen->type = CUDD_GEN_NODES; +gen->status = CUDD_GEN_EMPTY; +gen->stack.sp = 0; +gen->node = NULL; + +/* Collect all the nodes on the generator stack for later perusal. */ +gen->stack.stack = cuddNodeArray(Cudd_Regular(f), &size); +if (gen->stack.stack == NULL) { +FREE(gen); +dd->errorCode = CUDD_MEMORY_OUT; +return(NULL); +} +gen->gen.nodes.size = size; + +/* Find the first node. */ +if (gen->stack.sp < gen->gen.nodes.size) { +gen->status = CUDD_GEN_NONEMPTY; +gen->node = gen->stack.stack[gen->stack.sp]; +*node = gen->node; +} + +return(gen); + +} /* end of Cudd_FirstNode */ + + +/**Function******************************************************************** + + Synopsis [Finds the next node of a decision diagram.] + + Description [Finds the node of a decision diagram, using generator + gen. Returns 0 if the enumeration is completed; 1 otherwise.] + + SideEffects [The next node is returned as a side effect.] + + SeeAlso [Cudd_ForeachNode Cudd_FirstNode Cudd_GenFree Cudd_IsGenEmpty + Cudd_NextCube] + +******************************************************************************/ +int +Cudd_NextNode( +DdGen * gen, +DdNode ** node) +{ +/* Find the next node. */ +gen->stack.sp++; +if (gen->stack.sp < gen->gen.nodes.size) { +gen->node = gen->stack.stack[gen->stack.sp]; +*node = gen->node; +return(1); +} else { +gen->status = CUDD_GEN_EMPTY; +return(0); +} + +} /* end of Cudd_NextNode */ + + +/**Function******************************************************************** + + Synopsis [Frees a CUDD generator.] + + Description [Frees a CUDD generator. Always returns 0, so that it can + be used in mis-like foreach constructs.] + + SideEffects [None] + + SeeAlso [Cudd_ForeachCube Cudd_ForeachNode Cudd_FirstCube Cudd_NextCube + Cudd_FirstNode Cudd_NextNode Cudd_IsGenEmpty] + +******************************************************************************/ +int +Cudd_GenFree( +DdGen * gen) +{ +if (gen == NULL) return(0); +switch (gen->type) { +case CUDD_GEN_CUBES: +case CUDD_GEN_ZDD_PATHS: +FREE(gen->gen.cubes.cube); +FREE(gen->stack.stack); +break; +case CUDD_GEN_PRIMES: +FREE(gen->gen.primes.cube); +Cudd_RecursiveDeref(gen->manager,gen->node); +break; +case CUDD_GEN_NODES: +FREE(gen->stack.stack); +break; +default: +return(0); +} +FREE(gen); +return(0); + +} /* end of Cudd_GenFree */ + + +/**Function******************************************************************** + + Synopsis [Queries the status of a generator.] + + Description [Queries the status of a generator. Returns 1 if the + generator is empty or NULL; 0 otherswise.] + + SideEffects [None] + + SeeAlso [Cudd_ForeachCube Cudd_ForeachNode Cudd_FirstCube Cudd_NextCube + Cudd_FirstNode Cudd_NextNode Cudd_GenFree] + +******************************************************************************/ +int +Cudd_IsGenEmpty( +DdGen * gen) +{ +if (gen == NULL) return(1); +return(gen->status == CUDD_GEN_EMPTY); + +} /* end of Cudd_IsGenEmpty */ + + +/**Function******************************************************************** + + Synopsis [Portable random number generator.] + + Description [Portable number generator based on ran2 from "Numerical + Recipes in C." It is a long period (> 2 * 10^18) random number generator + of L'Ecuyer with Bays-Durham shuffle. Returns a long integer uniformly + distributed between 0 and 2147483561 (inclusive of the endpoint values). + The random generator can be explicitly initialized by calling + Cudd_Srandom. If no explicit initialization is performed, then the + seed 1 is assumed.] + + SideEffects [None] + + SeeAlso [Cudd_Srandom] + +******************************************************************************/ +long +Cudd_Random(void) +{ +int i; /* index in the shuffle table */ +long int w; /* work variable */ + +/* cuddRand == 0 if the geneartor has not been initialized yet. */ +if (cuddRand == 0) Cudd_Srandom(1); + +/* Compute cuddRand = (cuddRand * LEQA1) % MODULUS1 avoiding +** overflows by Schrage's method. +*/ +w = cuddRand / LEQQ1; +cuddRand = LEQA1 * (cuddRand - w * LEQQ1) - w * LEQR1; +cuddRand += (cuddRand < 0) * MODULUS1; + +/* Compute cuddRand2 = (cuddRand2 * LEQA2) % MODULUS2 avoiding +** overflows by Schrage's method. +*/ +w = cuddRand2 / LEQQ2; +cuddRand2 = LEQA2 * (cuddRand2 - w * LEQQ2) - w * LEQR2; +cuddRand2 += (cuddRand2 < 0) * MODULUS2; + +/* cuddRand is shuffled with the Bays-Durham algorithm. +** shuffleSelect and cuddRand2 are combined to generate the output. +*/ + +/* Pick one element from the shuffle table; "i" will be in the range +** from 0 to STAB_SIZE-1. +*/ +i = (int) (shuffleSelect / STAB_DIV); +/* Mix the element of the shuffle table with the current iterate of +** the second sub-generator, and replace the chosen element of the +** shuffle table with the current iterate of the first sub-generator. +*/ +shuffleSelect = shuffleTable[i] - cuddRand2; +shuffleTable[i] = cuddRand; +shuffleSelect += (shuffleSelect < 1) * (MODULUS1 - 1); +/* Since shuffleSelect != 0, and we want to be able to return 0, +** here we subtract 1 before returning. +*/ +return(shuffleSelect - 1); + +} /* end of Cudd_Random */ + + +/**Function******************************************************************** + + Synopsis [Initializer for the portable random number generator.] + + Description [Initializer for the portable number generator based on + ran2 in "Numerical Recipes in C." The input is the seed for the + generator. If it is negative, its absolute value is taken as seed. + If it is 0, then 1 is taken as seed. The initialized sets up the two + recurrences used to generate a long-period stream, and sets up the + shuffle table.] + + SideEffects [None] + + SeeAlso [Cudd_Random] + +******************************************************************************/ +void +Cudd_Srandom( +long seed) +{ +int i; + +if (seed < 0) cuddRand = -seed; +else if (seed == 0) cuddRand = 1; +else cuddRand = seed; +cuddRand2 = cuddRand; +/* Load the shuffle table (after 11 warm-ups). */ +for (i = 0; i < STAB_SIZE + 11; i++) { +long int w; +w = cuddRand / LEQQ1; +cuddRand = LEQA1 * (cuddRand - w * LEQQ1) - w * LEQR1; +cuddRand += (cuddRand < 0) * MODULUS1; +shuffleTable[i % STAB_SIZE] = cuddRand; +} +shuffleSelect = shuffleTable[1 % STAB_SIZE]; + +} /* end of Cudd_Srandom */ + + + +/**Function******************************************************************** + + Synopsis [Warns that a memory allocation failed.] + + Description [Warns that a memory allocation failed. + This function can be used as replacement of MMout_of_memory to prevent + the safe_mem functions of the util package from exiting when malloc + returns NULL. One possible use is in case of discretionary allocations; + for instance, the allocation of memory to enlarge the computed table.] + + SideEffects [None] + + SeeAlso [] + +******************************************************************************/ +void +Cudd_OutOfMem( +long size /* size of the allocation that failed */) +{ +(void) fflush(stdout); +(void) fprintf(stderr, "\nunable to allocate %ld bytes\n", size); +return; + +} /* end of Cudd_OutOfMem */ + + +/*---------------------------------------------------------------------------*/ +/* Definition of internal functions */ +/*---------------------------------------------------------------------------*/ + + +/**Function******************************************************************** + + Synopsis [Recursively collects all the nodes of a DD in a symbol + table.] + + Description [Traverses the DD f and collects all its nodes in a + symbol table. f is assumed to be a regular pointer and + cuddCollectNodes guarantees this assumption in the recursive calls. + Returns 1 in case of success; 0 otherwise.] + + SideEffects [None] + + SeeAlso [] + +******************************************************************************/ +int +cuddCollectNodes( +DdNode * f, +st_table * visited) +{ +DdNode *T, *E; +int retval; + +#ifdef DD_DEBUG +assert(!Cudd_IsComplement(f)); +#endif + +/* If already visited, nothing to do. */ +if (st_is_member(visited, (char *) f) == 1) +return(1); + +/* Check for abnormal condition that should never happen. */ +if (f == NULL) +return(0); + +/* Mark node as visited. */ +if (st_add_direct(visited, (char *) f, NULL) == ST_OUT_OF_MEM) +return(0); + +/* Check terminal case. */ +if (cuddIsConstant(f)) +return(1); + +/* Recursive calls. */ +T = cuddT(f); +retval = cuddCollectNodes(T,visited); +if (retval != 1) return(retval); +E = Cudd_Regular(cuddE(f)); +retval = cuddCollectNodes(E,visited); +return(retval); + +} /* end of cuddCollectNodes */ + + +/**Function******************************************************************** + + Synopsis [Recursively collects all the nodes of a DD in an array.] + + Description [Traverses the DD f and collects all its nodes in an array. + The caller should free the array returned by cuddNodeArray. + Returns a pointer to the array of nodes in case of success; NULL + otherwise. The nodes are collected in reverse topological order, so + that a node is always preceded in the array by all its descendants.] + + SideEffects [The number of nodes is returned as a side effect.] + + SeeAlso [Cudd_FirstNode] + +******************************************************************************/ +DdNodePtr * +cuddNodeArray( +DdNode *f, +int *n) +{ +DdNodePtr *table; +int size, retval; + +size = ddDagInt(Cudd_Regular(f)); +table = ALLOC(DdNodePtr, size); +if (table == NULL) { +ddClearFlag(Cudd_Regular(f)); +return(NULL); +} + +retval = cuddNodeArrayRecur(f, table, 0); +assert(retval == size); + +*n = size; +return(table); + +} /* cuddNodeArray */ + + +/*---------------------------------------------------------------------------*/ +/* Definition of static functions */ +/*---------------------------------------------------------------------------*/ + + +/**Function******************************************************************** + + Synopsis [Performs the recursive step of cuddP.] + + Description [Performs the recursive step of cuddP. Returns 1 in case + of success; 0 otherwise.] + + SideEffects [None] + +******************************************************************************/ +static int +dp2( +DdManager *dd, +DdNode * f, +st_table * t) +{ +DdNode *g, *n, *N; +int T,E; + +if (f == NULL) { +return(0); +} +g = Cudd_Regular(f); +if (cuddIsConstant(g)) { +#if SIZEOF_VOID_P == 8 +(void) fprintf(dd->out,"ID = %c0x%lx\tvalue = %-9g\n", bang(f), +(ptruint) g / (ptruint) sizeof(DdNode),cuddV(g)); +#else +(void) fprintf(dd->out,"ID = %c0x%x\tvalue = %-9g\n", bang(f), +(ptruint) g / (ptruint) sizeof(DdNode),cuddV(g)); +#endif +return(1); +} +if (st_is_member(t,(char *) g) == 1) { +return(1); +} +if (st_add_direct(t,(char *) g,NULL) == ST_OUT_OF_MEM) +return(0); +#ifdef DD_STATS +#if SIZEOF_VOID_P == 8 +(void) fprintf(dd->out,"ID = %c0x%lx\tindex = %d\tr = %d\t", bang(f), +(ptruint) g / (ptruint) sizeof(DdNode), g->index, g->ref); +#else +(void) fprintf(dd->out,"ID = %c0x%x\tindex = %d\tr = %d\t", bang(f), +(ptruint) g / (ptruint) sizeof(DdNode),g->index,g->ref); +#endif +#else +#if SIZEOF_VOID_P == 8 +(void) fprintf(dd->out,"ID = %c0x%lx\tindex = %u\t", bang(f), +(ptruint) g / (ptruint) sizeof(DdNode),g->index); +#else +(void) fprintf(dd->out,"ID = %c0x%x\tindex = %hu\t", bang(f), +(ptruint) g / (ptruint) sizeof(DdNode),g->index); +#endif +#endif +n = cuddT(g); +if (cuddIsConstant(n)) { +(void) fprintf(dd->out,"T = %-9g\t",cuddV(n)); +T = 1; +} else { +#if SIZEOF_VOID_P == 8 +(void) fprintf(dd->out,"T = 0x%lx\t",(ptruint) n / (ptruint) sizeof(DdNode)); +#else +(void) fprintf(dd->out,"T = 0x%x\t",(ptruint) n / (ptruint) sizeof(DdNode)); +#endif +T = 0; +} + +n = cuddE(g); +N = Cudd_Regular(n); +if (cuddIsConstant(N)) { +(void) fprintf(dd->out,"E = %c%-9g\n",bang(n),cuddV(N)); +E = 1; +} else { +#if SIZEOF_VOID_P == 8 +(void) fprintf(dd->out,"E = %c0x%lx\n", bang(n), (ptruint) N/(ptruint) sizeof(DdNode)); +#else +(void) fprintf(dd->out,"E = %c0x%x\n", bang(n), (ptruint) N/(ptruint) sizeof(DdNode)); +#endif +E = 0; +} +if (E == 0) { +if (dp2(dd,N,t) == 0) +return(0); +} +if (T == 0) { +if (dp2(dd,cuddT(g),t) == 0) +return(0); +} +return(1); + +} /* end of dp2 */ + + +/**Function******************************************************************** + + Synopsis [Performs the recursive step of Cudd_PrintMinterm.] + + Description [] + + SideEffects [None] + +******************************************************************************/ +static void +ddPrintMintermAux( +DdManager * dd /* manager */, +DdNode * node /* current node */, +int * list /* current recursion path */) +{ +DdNode *N,*Nv,*Nnv; +int i,v,index; + +N = Cudd_Regular(node); + +if (cuddIsConstant(N)) { +/* Terminal case: Print one cube based on the current recursion +** path, unless we have reached the background value (ADDs) or +** the logical zero (BDDs). +*/ +if (node != background && node != zero) { +for (i = 0; i < dd->size; i++) { +v = list[i]; +if (v == 0) (void) fprintf(dd->out,"0"); +else if (v == 1) (void) fprintf(dd->out,"1"); +else (void) fprintf(dd->out,"-"); +} +(void) fprintf(dd->out," % g\n", cuddV(node)); +} +} else { +Nv = cuddT(N); +Nnv = cuddE(N); +if (Cudd_IsComplement(node)) { +Nv = Cudd_Not(Nv); +Nnv = Cudd_Not(Nnv); +} +index = N->index; +list[index] = 0; +ddPrintMintermAux(dd,Nnv,list); +list[index] = 1; +ddPrintMintermAux(dd,Nv,list); +list[index] = 2; +} +return; + +} /* end of ddPrintMintermAux */ + + +/**Function******************************************************************** + + Synopsis [Performs the recursive step of Cudd_DagSize.] + + Description [Performs the recursive step of Cudd_DagSize. Returns the + number of nodes in the graph rooted at n.] + + SideEffects [None] + +******************************************************************************/ +static int +ddDagInt( +DdNode * n) +{ +int tval, eval; + +if (Cudd_IsComplement(n->next)) { +return(0); +} +n->next = Cudd_Not(n->next); +if (cuddIsConstant(n)) { +return(1); +} +tval = ddDagInt(cuddT(n)); +eval = ddDagInt(Cudd_Regular(cuddE(n))); +return(1 + tval + eval); + +} /* end of ddDagInt */ + + +/**Function******************************************************************** + + Synopsis [Performs the recursive step of cuddNodeArray.] + + Description [Performs the recursive step of cuddNodeArray. Returns + an the number of nodes in the DD. Clear the least significant bit + of the next field that was used as visited flag by + cuddNodeArrayRecur when counting the nodes. node is supposed to be + regular; the invariant is maintained by this procedure.] + + SideEffects [None] + + SeeAlso [] + +******************************************************************************/ +static int +cuddNodeArrayRecur( +DdNode *f, +DdNodePtr *table, +int index) +{ +int tindex, eindex; + +if (!Cudd_IsComplement(f->next)) { +return(index); +} +/* Clear visited flag. */ +f->next = Cudd_Regular(f->next); +if (cuddIsConstant(f)) { +table[index] = f; +return(index + 1); +} +tindex = cuddNodeArrayRecur(cuddT(f), table, index); +eindex = cuddNodeArrayRecur(Cudd_Regular(cuddE(f)), table, tindex); +table[eindex] = f; +return(eindex + 1); + +} /* end of cuddNodeArrayRecur */ + + +/**Function******************************************************************** + + Synopsis [Performs the recursive step of Cudd_CofactorEstimate.] + + Description [Performs the recursive step of Cudd_CofactorEstimate. + Returns an estimate of the number of nodes in the DD of a + cofactor of node. Uses the least significant bit of the next field as + visited flag. node is supposed to be regular; the invariant is maintained + by this procedure.] + + SideEffects [None] + + SeeAlso [] + +******************************************************************************/ +static int +cuddEstimateCofactor( +DdManager *dd, +st_table *table, +DdNode * node, +int i, +int phase, +DdNode ** ptr) +{ +int tval, eval, val; +DdNode *ptrT, *ptrE; + +if (Cudd_IsComplement(node->next)) { +if (!st_lookup(table,(char *)node,(char **)ptr)) { +if (st_add_direct(table,(char *)node,(char *)node) == +ST_OUT_OF_MEM) +return(CUDD_OUT_OF_MEM); +*ptr = node; +} +return(0); +} +node->next = Cudd_Not(node->next); +if (cuddIsConstant(node)) { +*ptr = node; +if (st_add_direct(table,(char *)node,(char *)node) == ST_OUT_OF_MEM) +return(CUDD_OUT_OF_MEM); +return(1); +} +if ((int) node->index == i) { +if (phase == 1) { +*ptr = cuddT(node); +val = ddDagInt(cuddT(node)); +} else { +*ptr = cuddE(node); +val = ddDagInt(Cudd_Regular(cuddE(node))); +} +if (node->ref > 1) { +if (st_add_direct(table,(char *)node,(char *)*ptr) == +ST_OUT_OF_MEM) +return(CUDD_OUT_OF_MEM); +} +return(val); +} +if (dd->perm[node->index] > dd->perm[i]) { +*ptr = node; +tval = ddDagInt(cuddT(node)); +eval = ddDagInt(Cudd_Regular(cuddE(node))); +if (node->ref > 1) { +if (st_add_direct(table,(char *)node,(char *)node) == +ST_OUT_OF_MEM) +return(CUDD_OUT_OF_MEM); +} +val = 1 + tval + eval; +return(val); +} +tval = cuddEstimateCofactor(dd,table,cuddT(node),i,phase,&ptrT); +eval = cuddEstimateCofactor(dd,table,Cudd_Regular(cuddE(node)),i, +phase,&ptrE); +ptrE = Cudd_NotCond(ptrE,Cudd_IsComplement(cuddE(node))); +if (ptrT == ptrE) { /* recombination */ +*ptr = ptrT; +val = tval; +if (node->ref > 1) { +if (st_add_direct(table,(char *)node,(char *)*ptr) == +ST_OUT_OF_MEM) +return(CUDD_OUT_OF_MEM); +} +} else if ((ptrT != cuddT(node) || ptrE != cuddE(node)) && +(*ptr = cuddUniqueLookup(dd,node->index,ptrT,ptrE)) != NULL) { +if (Cudd_IsComplement((*ptr)->next)) { +val = 0; +} else { +val = 1 + tval + eval; +} +if (node->ref > 1) { +if (st_add_direct(table,(char *)node,(char *)*ptr) == +ST_OUT_OF_MEM) +return(CUDD_OUT_OF_MEM); +} +} else { +*ptr = node; +val = 1 + tval + eval; +} +return(val); + +} /* end of cuddEstimateCofactor */ + + +/**Function******************************************************************** + + Synopsis [Checks the unique table for the existence of an internal node.] + + Description [Checks the unique table for the existence of an internal + node. Returns a pointer to the node if it is in the table; NULL otherwise.] + + SideEffects [None] + + SeeAlso [cuddUniqueInter] + +******************************************************************************/ +static DdNode * +cuddUniqueLookup( +DdManager * unique, +int index, +DdNode * T, +DdNode * E) +{ +int posn; +unsigned int level; +DdNodePtr *nodelist; +DdNode *looking; +DdSubtable *subtable; + +if (index >= unique->size) { +return(NULL); +} + +level = unique->perm[index]; +subtable = &(unique->subtables[level]); + +#ifdef DD_DEBUG +assert(level < (unsigned) cuddI(unique,T->index)); +assert(level < (unsigned) cuddI(unique,Cudd_Regular(E)->index)); +#endif + +posn = ddHash(T, E, subtable->shift); +nodelist = subtable->nodelist; +looking = nodelist[posn]; + +while (T < cuddT(looking)) { +looking = Cudd_Regular(looking->next); +} +while (T == cuddT(looking) && E < cuddE(looking)) { +looking = Cudd_Regular(looking->next); +} +if (cuddT(looking) == T && cuddE(looking) == E) { +return(looking); +} + +return(NULL); + +} /* end of cuddUniqueLookup */ + + +/**Function******************************************************************** + + Synopsis [Performs the recursive step of Cudd_CofactorEstimateSimple.] + + Description [Performs the recursive step of Cudd_CofactorEstimateSimple. + Returns an estimate of the number of nodes in the DD of the positive + cofactor of node. Uses the least significant bit of the next field as + visited flag. node is supposed to be regular; the invariant is maintained + by this procedure.] + + SideEffects [None] + + SeeAlso [] + +******************************************************************************/ +static int +cuddEstimateCofactorSimple( +DdNode * node, +int i) +{ +int tval, eval; + +if (Cudd_IsComplement(node->next)) { +return(0); +} +node->next = Cudd_Not(node->next); +if (cuddIsConstant(node)) { +return(1); +} +tval = cuddEstimateCofactorSimple(cuddT(node),i); +if ((int) node->index == i) return(tval); +eval = cuddEstimateCofactorSimple(Cudd_Regular(cuddE(node)),i); +return(1 + tval + eval); + +} /* end of cuddEstimateCofactorSimple */ + + +/**Function******************************************************************** + + Synopsis [Performs the recursive step of Cudd_CountMinterm.] + + Description [Performs the recursive step of Cudd_CountMinterm. + It is based on the following identity. Let |f| be the + number of minterms of f. Then: +

      + |f| = (|f0|+|f1|)/2 + + where f0 and f1 are the two cofactors of f. Does not use the + identity |f'| = max - |f|, to minimize loss of accuracy due to + roundoff. Returns the number of minterms of the function rooted at + node.] + + SideEffects [None] + +******************************************************************************/ +static double +ddCountMintermAux( +DdNode * node, +double max, +DdHashTable * table) +{ +DdNode *N, *Nt, *Ne; +double min, minT, minE; +DdNode *res; + +N = Cudd_Regular(node); + +if (cuddIsConstant(N)) { +if (node == background || node == zero) { +return(0.0); +} else { +return(max); +} +} +if (N->ref != 1 && (res = cuddHashTableLookup1(table,node)) != NULL) { +min = cuddV(res); +if (res->ref == 0) { +table->manager->dead++; +table->manager->constants.dead++; +} +return(min); +} + +Nt = cuddT(N); Ne = cuddE(N); +if (Cudd_IsComplement(node)) { +Nt = Cudd_Not(Nt); Ne = Cudd_Not(Ne); +} + +minT = ddCountMintermAux(Nt,max,table); +if (minT == (double)CUDD_OUT_OF_MEM) return((double)CUDD_OUT_OF_MEM); +minT *= 0.5; +minE = ddCountMintermAux(Ne,max,table); +if (minE == (double)CUDD_OUT_OF_MEM) return((double)CUDD_OUT_OF_MEM); +minE *= 0.5; +min = minT + minE; + +if (N->ref != 1) { +ptrint fanout = (ptrint) N->ref; +cuddSatDec(fanout); +res = cuddUniqueConst(table->manager,min); +if (!cuddHashTableInsert1(table,node,res,fanout)) { +cuddRef(res); Cudd_RecursiveDeref(table->manager, res); +return((double)CUDD_OUT_OF_MEM); +} +} + +return(min); + +} /* end of ddCountMintermAux */ + + +/**Function******************************************************************** + + Synopsis [Performs the recursive step of Cudd_CountPath.] + + Description [Performs the recursive step of Cudd_CountPath. + It is based on the following identity. Let |f| be the + number of paths of f. Then: + + |f| = |f0|+|f1| + + where f0 and f1 are the two cofactors of f. Uses the + identity |f'| = |f|, to improve the utilization of the (local) cache. + Returns the number of paths of the function rooted at node.] + + SideEffects [None] + +******************************************************************************/ +static double +ddCountPathAux( +DdNode * node, +st_table * table) +{ + +DdNode *Nv, *Nnv; +double paths, *ppaths, paths1, paths2; +double *dummy; + + +if (cuddIsConstant(node)) { +return(1.0); +} +if (st_lookup(table, node, &dummy)) { +paths = *dummy; +return(paths); +} + +Nv = cuddT(node); Nnv = cuddE(node); + +paths1 = ddCountPathAux(Nv,table); +if (paths1 == (double)CUDD_OUT_OF_MEM) return((double)CUDD_OUT_OF_MEM); +paths2 = ddCountPathAux(Cudd_Regular(Nnv),table); +if (paths2 == (double)CUDD_OUT_OF_MEM) return((double)CUDD_OUT_OF_MEM); +paths = paths1 + paths2; + +ppaths = ALLOC(double,1); +if (ppaths == NULL) { +return((double)CUDD_OUT_OF_MEM); +} + +*ppaths = paths; + +if (st_add_direct(table,(char *)node, (char *)ppaths) == ST_OUT_OF_MEM) { +FREE(ppaths); +return((double)CUDD_OUT_OF_MEM); +} +return(paths); + +} /* end of ddCountPathAux */ + + +/**Function******************************************************************** + + Synopsis [Performs the recursive step of Cudd_EpdCountMinterm.] + + Description [Performs the recursive step of Cudd_EpdCountMinterm. + It is based on the following identity. Let |f| be the + number of minterms of f. Then: + + |f| = (|f0|+|f1|)/2 + + where f0 and f1 are the two cofactors of f. Does not use the + identity |f'| = max - |f|, to minimize loss of accuracy due to + roundoff. Returns the number of minterms of the function rooted at + node.] + + SideEffects [None] + +******************************************************************************/ +static int +ddEpdCountMintermAux( +DdNode * node, +EpDouble * max, +EpDouble * epd, +st_table * table) +{ +DdNode *Nt, *Ne; +EpDouble *min, minT, minE; +EpDouble *res; +int status; + +/* node is assumed to be regular */ +if (cuddIsConstant(node)) { +if (node == background || node == zero) { +EpdMakeZero(epd, 0); +} else { +EpdCopy(max, epd); +} +return(0); +} +if (node->ref != 1 && st_lookup(table, node, &res)) { +EpdCopy(res, epd); +return(0); +} + +Nt = cuddT(node); Ne = cuddE(node); + +status = ddEpdCountMintermAux(Nt,max,&minT,table); +if (status == CUDD_OUT_OF_MEM) return(CUDD_OUT_OF_MEM); +EpdMultiply(&minT, (double)0.5); +status = ddEpdCountMintermAux(Cudd_Regular(Ne),max,&minE,table); +if (status == CUDD_OUT_OF_MEM) return(CUDD_OUT_OF_MEM); +if (Cudd_IsComplement(Ne)) { +EpdSubtract3(max, &minE, epd); +EpdCopy(epd, &minE); +} +EpdMultiply(&minE, (double)0.5); +EpdAdd3(&minT, &minE, epd); + +if (node->ref > 1) { +min = EpdAlloc(); +if (!min) +return(CUDD_OUT_OF_MEM); +EpdCopy(epd, min); +if (st_insert(table, (char *)node, (char *)min) == ST_OUT_OF_MEM) { +EpdFree(min); +return(CUDD_OUT_OF_MEM); +} +} + +return(0); + +} /* end of ddEpdCountMintermAux */ + + +/**Function******************************************************************** + + Synopsis [Performs the recursive step of Cudd_CountPathsToNonZero.] + + Description [Performs the recursive step of Cudd_CountPathsToNonZero. + It is based on the following identity. Let |f| be the + number of paths of f. Then: + + |f| = |f0|+|f1| + + where f0 and f1 are the two cofactors of f. Returns the number of + paths of the function rooted at node.] + + SideEffects [None] + +******************************************************************************/ +static double +ddCountPathsToNonZero( +DdNode * N, +st_table * table) +{ + +DdNode *node, *Nt, *Ne; +double paths, *ppaths, paths1, paths2; +double *dummy; + +node = Cudd_Regular(N); +if (cuddIsConstant(node)) { +return((double) !(Cudd_IsComplement(N) || cuddV(node)==DD_ZERO_VAL)); +} +if (st_lookup(table, N, &dummy)) { +paths = *dummy; +return(paths); +} + +Nt = cuddT(node); Ne = cuddE(node); +if (node != N) { +Nt = Cudd_Not(Nt); Ne = Cudd_Not(Ne); +} + +paths1 = ddCountPathsToNonZero(Nt,table); +if (paths1 == (double)CUDD_OUT_OF_MEM) return((double)CUDD_OUT_OF_MEM); +paths2 = ddCountPathsToNonZero(Ne,table); +if (paths2 == (double)CUDD_OUT_OF_MEM) return((double)CUDD_OUT_OF_MEM); +paths = paths1 + paths2; + +ppaths = ALLOC(double,1); +if (ppaths == NULL) { +return((double)CUDD_OUT_OF_MEM); +} + +*ppaths = paths; + +if (st_add_direct(table,(char *)N, (char *)ppaths) == ST_OUT_OF_MEM) { +FREE(ppaths); +return((double)CUDD_OUT_OF_MEM); +} +return(paths); + +} /* end of ddCountPathsToNonZero */ + + +/**Function******************************************************************** + + Synopsis [Performs the recursive step of Cudd_Support.] + + Description [Performs the recursive step of Cudd_Support. Performs a + DFS from f. The support is accumulated in supp as a side effect. Uses + the LSB of the then pointer as visited flag.] + + SideEffects [None] + + SeeAlso [ddClearFlag] + +******************************************************************************/ +static void +ddSupportStep( +DdNode * f, +int * support) +{ +if (cuddIsConstant(f) || Cudd_IsComplement(f->next)) +return; + +support[f->index] = 1; +ddSupportStep(cuddT(f),support); +ddSupportStep(Cudd_Regular(cuddE(f)),support); +/* Mark as visited. */ +f->next = Cudd_Complement(f->next); + +} /* end of ddSupportStep */ + + +/**Function******************************************************************** + + Synopsis [Performs a DFS from f, clearing the LSB of the next + pointers.] + + Description [] + + SideEffects [None] + + SeeAlso [ddSupportStep ddFindSupport ddLeavesInt ddDagInt] + +******************************************************************************/ +static void +ddClearFlag( +DdNode * f) +{ +if (!Cudd_IsComplement(f->next)) { +return; +} +/* Clear visited flag. */ +f->next = Cudd_Regular(f->next); +if (cuddIsConstant(f)) { +return; +} +ddClearFlag(cuddT(f)); +ddClearFlag(Cudd_Regular(cuddE(f))); +return; + +} /* end of ddClearFlag */ + + +/**Function******************************************************************** + + Synopsis [Performs the recursive step of Cudd_CountLeaves.] + + Description [Performs the recursive step of Cudd_CountLeaves. Returns + the number of leaves in the DD rooted at n.] + + SideEffects [None] + + SeeAlso [Cudd_CountLeaves] + +******************************************************************************/ +static int +ddLeavesInt( +DdNode * n) +{ +int tval, eval; + +if (Cudd_IsComplement(n->next)) { +return(0); +} +n->next = Cudd_Not(n->next); +if (cuddIsConstant(n)) { +return(1); +} +tval = ddLeavesInt(cuddT(n)); +eval = ddLeavesInt(Cudd_Regular(cuddE(n))); +return(tval + eval); + +} /* end of ddLeavesInt */ + + +/**Function******************************************************************** + + Synopsis [Performs the recursive step of Cudd_bddPickArbitraryMinterms.] + + Description [Performs the recursive step of Cudd_bddPickArbitraryMinterms. + Returns 1 if successful; 0 otherwise.] + + SideEffects [none] + + SeeAlso [Cudd_bddPickArbitraryMinterms] + +******************************************************************************/ +static int +ddPickArbitraryMinterms( +DdManager *dd, +DdNode *node, +int nvars, +int nminterms, +char **string) +{ +DdNode *N, *T, *E; +DdNode *one, *bzero; +int i, t, result; +double min1, min2; + +if (string == NULL || node == NULL) return(0); + +/* The constant 0 function has no on-set cubes. */ +one = DD_ONE(dd); +bzero = Cudd_Not(one); +if (nminterms == 0 || node == bzero) return(1); +if (node == one) { +return(1); +} + +N = Cudd_Regular(node); +T = cuddT(N); E = cuddE(N); +if (Cudd_IsComplement(node)) { +T = Cudd_Not(T); E = Cudd_Not(E); +} + +min1 = Cudd_CountMinterm(dd, T, nvars) / 2.0; +if (min1 == (double)CUDD_OUT_OF_MEM) return(0); +min2 = Cudd_CountMinterm(dd, E, nvars) / 2.0; +if (min2 == (double)CUDD_OUT_OF_MEM) return(0); + +t = (int)((double)nminterms * min1 / (min1 + min2) + 0.5); +for (i = 0; i < t; i++) +string[i][N->index] = '1'; +for (i = t; i < nminterms; i++) +string[i][N->index] = '0'; + +result = ddPickArbitraryMinterms(dd,T,nvars,t,&string[0]); +if (result == 0) +return(0); +result = ddPickArbitraryMinterms(dd,E,nvars,nminterms-t,&string[t]); +return(result); + +} /* end of ddPickArbitraryMinterms */ + + +/* end of ddPickRepresentativeCube */ + + +/* end of ddEpdFree */ + + +/**Function******************************************************************** + + Synopsis [Recursively find the support of f.] + + Description [Recursively find the support of f. This function uses the + LSB of the next field of the nodes of f as visited flag. It also uses the + LSB of the next field of the variables as flag to remember whether a + certain index has already been seen. Finally, it uses the manager stack + to record all seen indices.] + + SideEffects [The stack pointer SP is modified by side-effect. The next + fields are changed and need to be reset.] + +******************************************************************************/ +static void +ddFindSupport( +DdManager *dd, +DdNode *f, +int *SP) +{ +int index; +DdNode *var; + +if (cuddIsConstant(f) || Cudd_IsComplement(f->next)) { +return; +} + +index = f->index; +var = dd->vars[index]; +/* It is possible that var is embedded in f. That causes no problem, +** though, because if we see it after encountering another node with +** the same index, nothing is supposed to happen. +*/ +if (!Cudd_IsComplement(var->next)) { +var->next = Cudd_Complement(var->next); +dd->stack[*SP] = (DdNode *)(ptrint) index; +(*SP)++; +} +ddFindSupport(dd, cuddT(f), SP); +ddFindSupport(dd, Cudd_Regular(cuddE(f)), SP); +/* Mark as visited. */ +f->next = Cudd_Complement(f->next); + +} /* end of ddFindSupport */ + + +/* end of ddClearVars */ + + +/* end of indexCompare */ +/**CFile*********************************************************************** + + FileName [cuddWindow.c] + + PackageName [cudd] + + Synopsis [Functions for variable reordering by window permutation.] + + Description [Internal procedures included in this module: +
        +
      • cuddWindowReorder() +
      + Static procedures included in this module: +
        +
      • ddWindow2() +
      • ddWindowConv2() +
      • ddPermuteWindow3() +
      • ddWindow3() +
      • ddWindowConv3() +
      • ddPermuteWindow4() +
      • ddWindow4() +
      • ddWindowConv4() +
      ] + + Author [Fabio Somenzi] + + Copyright [Copyright (c) 1995-2012, Regents of the University of Colorado + + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + Neither the name of the University of Colorado nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE.] + +******************************************************************************/ + + +/*---------------------------------------------------------------------------*/ +/* Constant declarations */ +/*---------------------------------------------------------------------------*/ + + +/*---------------------------------------------------------------------------*/ +/* Stucture declarations */ +/*---------------------------------------------------------------------------*/ + + +/*---------------------------------------------------------------------------*/ +/* Type declarations */ +/*---------------------------------------------------------------------------*/ + + +/*---------------------------------------------------------------------------*/ +/* Variable declarations */ +/*---------------------------------------------------------------------------*/ + +//#ifndef lint +//static char rcsid[] DD_UNUSED = "$Id: cuddWindow.c,v 1.15 2012/02/05 01:07:19 fabio Exp $"; +//#endif + +#ifdef DD_STATS +extern int ddTotalNumberSwapping; +extern int ddTotalNISwaps; +#endif + +/*---------------------------------------------------------------------------*/ +/* Macro declarations */ +/*---------------------------------------------------------------------------*/ + + +/**AutomaticStart*************************************************************/ + +/*---------------------------------------------------------------------------*/ +/* Static function prototypes */ +/*---------------------------------------------------------------------------*/ + +static int ddWindow2 (DdManager *table, int low, int high); +static int ddWindowConv2 (DdManager *table, int low, int high); +static int ddPermuteWindow3 (DdManager *table, int x); +static int ddWindow3 (DdManager *table, int low, int high); +static int ddWindowConv3 (DdManager *table, int low, int high); +static int ddPermuteWindow4 (DdManager *table, int w); +static int ddWindow4 (DdManager *table, int low, int high); +static int ddWindowConv4 (DdManager *table, int low, int high); + +/**AutomaticEnd***************************************************************/ + + +/*---------------------------------------------------------------------------*/ +/* Definition of exported functions */ +/*---------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------*/ +/* Definition of internal functions */ +/*---------------------------------------------------------------------------*/ + + +/**Function******************************************************************** + + Synopsis [Reorders by applying the method of the sliding window.] + + Description [Reorders by applying the method of the sliding window. + Tries all possible permutations to the variables in a window that + slides from low to high. The size of the window is determined by + submethod. Assumes that no dead nodes are present. Returns 1 in + case of success; 0 otherwise.] + + SideEffects [None] + +******************************************************************************/ +int +cuddWindowReorder( +DdManager * table /* DD table */, +int low /* lowest index to reorder */, +int high /* highest index to reorder */, +Cudd_ReorderingType submethod /* window reordering option */) +{ + +int res; +#ifdef DD_DEBUG +int supposedOpt; +#endif + +switch (submethod) { +case CUDD_REORDER_WINDOW2: +res = ddWindow2(table,low,high); +break; +case CUDD_REORDER_WINDOW3: +res = ddWindow3(table,low,high); +break; +case CUDD_REORDER_WINDOW4: +res = ddWindow4(table,low,high); +break; +case CUDD_REORDER_WINDOW2_CONV: +res = ddWindowConv2(table,low,high); +break; +case CUDD_REORDER_WINDOW3_CONV: +res = ddWindowConv3(table,low,high); +#ifdef DD_DEBUG +supposedOpt = table->keys - table->isolated; +res = ddWindow3(table,low,high); +if (table->keys - table->isolated != (unsigned) supposedOpt) { +(void) fprintf(table->err, "Convergence failed! (%d != %d)\n", +table->keys - table->isolated, supposedOpt); +} +#endif +break; +case CUDD_REORDER_WINDOW4_CONV: +res = ddWindowConv4(table,low,high); +#ifdef DD_DEBUG +supposedOpt = table->keys - table->isolated; +res = ddWindow4(table,low,high); +if (table->keys - table->isolated != (unsigned) supposedOpt) { +(void) fprintf(table->err,"Convergence failed! (%d != %d)\n", +table->keys - table->isolated, supposedOpt); +} +#endif +break; +default: return(0); +} + +return(res); + +} /* end of cuddWindowReorder */ + + +/*---------------------------------------------------------------------------*/ +/* Definition of static functions */ +/*---------------------------------------------------------------------------*/ + +/**Function******************************************************************** + + Synopsis [Reorders by applying a sliding window of width 2.] + + Description [Reorders by applying a sliding window of width 2. + Tries both permutations of the variables in a window + that slides from low to high. Assumes that no dead nodes are + present. Returns 1 in case of success; 0 otherwise.] + + SideEffects [None] + +******************************************************************************/ +static int +ddWindow2( +DdManager * table, +int low, +int high) +{ + +int x; +int res; +int size; + +#ifdef DD_DEBUG +assert(low >= 0 && high < table->size); +#endif + +if (high-low < 1) return(0); + +res = table->keys - table->isolated; +for (x = low; x < high; x++) { +size = res; +res = cuddSwapInPlace(table,x,x+1); +if (res == 0) return(0); +if (res >= size) { /* no improvement: undo permutation */ +res = cuddSwapInPlace(table,x,x+1); +if (res == 0) return(0); +} +#ifdef DD_STATS +if (res < size) { +(void) fprintf(table->out,"-"); +} else { +(void) fprintf(table->out,"="); +} +fflush(table->out); +#endif +} + +return(1); + +} /* end of ddWindow2 */ + + +/**Function******************************************************************** + + Synopsis [Reorders by repeatedly applying a sliding window of width 2.] + + Description [Reorders by repeatedly applying a sliding window of width + 2. Tries both permutations of the variables in a window + that slides from low to high. Assumes that no dead nodes are + present. Uses an event-driven approach to determine convergence. + Returns 1 in case of success; 0 otherwise.] + + SideEffects [None] + +******************************************************************************/ +static int +ddWindowConv2( +DdManager * table, +int low, +int high) +{ +int x; +int res; +int nwin; +int newevent; +int *events; +int size; + +#ifdef DD_DEBUG +assert(low >= 0 && high < table->size); +#endif + +if (high-low < 1) return(ddWindowConv2(table,low,high)); + +nwin = high-low; +events = ALLOC(int,nwin); +if (events == NULL) { +table->errorCode = CUDD_MEMORY_OUT; +return(0); +} +for (x=0; xkeys - table->isolated; +do { +newevent = 0; +for (x=0; x= size) { /* no improvement: undo permutation */ +res = cuddSwapInPlace(table,x+low,x+low+1); +if (res == 0) { +FREE(events); +return(0); +} +} +if (res < size) { +if (x < nwin-1) events[x+1] = 1; +if (x > 0) events[x-1] = 1; +newevent = 1; +} +events[x] = 0; +#ifdef DD_STATS +if (res < size) { +(void) fprintf(table->out,"-"); +} else { +(void) fprintf(table->out,"="); +} +fflush(table->out); +#endif +} +} +#ifdef DD_STATS +if (newevent) { +(void) fprintf(table->out,"|"); +fflush(table->out); +} +#endif +} while (newevent); + +FREE(events); + +return(1); + +} /* end of ddWindowConv3 */ + + +/**Function******************************************************************** + + Synopsis [Tries all the permutations of the three variables between + x and x+2 and retains the best.] + + Description [Tries all the permutations of the three variables between + x and x+2 and retains the best. Assumes that no dead nodes are + present. Returns the index of the best permutation (1-6) in case of + success; 0 otherwise.Assumes that no dead nodes are present. Returns + the index of the best permutation (1-6) in case of success; 0 + otherwise.] + + SideEffects [None] + +******************************************************************************/ +static int +ddPermuteWindow3( +DdManager * table, +int x) +{ +int y,z; +int size,sizeNew; +int best; + +#ifdef DD_DEBUG +assert(table->dead == 0); +assert(x+2 < table->size); +#endif + +size = table->keys - table->isolated; +y = x+1; z = y+1; + +/* The permutation pattern is: +** (x,y)(y,z) +** repeated three times to get all 3! = 6 permutations. +*/ +#define ABC 1 +best = ABC; + +#define BAC 2 +sizeNew = cuddSwapInPlace(table,x,y); +if (sizeNew < size) { +if (sizeNew == 0) return(0); +best = BAC; +size = sizeNew; +} +#define BCA 3 +sizeNew = cuddSwapInPlace(table,y,z); +if (sizeNew < size) { +if (sizeNew == 0) return(0); +best = BCA; +size = sizeNew; +} +#define CBA 4 +sizeNew = cuddSwapInPlace(table,x,y); +if (sizeNew < size) { +if (sizeNew == 0) return(0); +best = CBA; +size = sizeNew; +} +#define CAB 5 +sizeNew = cuddSwapInPlace(table,y,z); +if (sizeNew < size) { +if (sizeNew == 0) return(0); +best = CAB; +size = sizeNew; +} +#define ACB 6 +sizeNew = cuddSwapInPlace(table,x,y); +if (sizeNew < size) { +if (sizeNew == 0) return(0); +best = ACB; +size = sizeNew; +} + +/* Now take the shortest route to the best permuytation. +** The initial permutation is ACB. +*/ +switch(best) { +case BCA: if (!cuddSwapInPlace(table,y,z)) return(0); +case CBA: if (!cuddSwapInPlace(table,x,y)) return(0); +case ABC: if (!cuddSwapInPlace(table,y,z)) return(0); +case ACB: break; +case BAC: if (!cuddSwapInPlace(table,y,z)) return(0); +case CAB: if (!cuddSwapInPlace(table,x,y)) return(0); +break; +default: return(0); +} + +#ifdef DD_DEBUG +assert(table->keys - table->isolated == (unsigned) size); +#endif + +return(best); + +} /* end of ddPermuteWindow3 */ + + +/**Function******************************************************************** + + Synopsis [Reorders by applying a sliding window of width 3.] + + Description [Reorders by applying a sliding window of width 3. + Tries all possible permutations to the variables in a + window that slides from low to high. Assumes that no dead nodes are + present. Returns 1 in case of success; 0 otherwise.] + + SideEffects [None] + +******************************************************************************/ +static int +ddWindow3( +DdManager * table, +int low, +int high) +{ + +int x; +int res; + +#ifdef DD_DEBUG +assert(low >= 0 && high < table->size); +#endif + +if (high-low < 2) return(ddWindow2(table,low,high)); + +for (x = low; x+1 < high; x++) { +res = ddPermuteWindow3(table,x); +if (res == 0) return(0); +#ifdef DD_STATS +if (res == ABC) { +(void) fprintf(table->out,"="); +} else { +(void) fprintf(table->out,"-"); +} +fflush(table->out); +#endif +} + +return(1); + +} /* end of ddWindow3 */ + + +/**Function******************************************************************** + + Synopsis [Reorders by repeatedly applying a sliding window of width 3.] + + Description [Reorders by repeatedly applying a sliding window of width + 3. Tries all possible permutations to the variables in a + window that slides from low to high. Assumes that no dead nodes are + present. Uses an event-driven approach to determine convergence. + Returns 1 in case of success; 0 otherwise.] + + SideEffects [None] + +******************************************************************************/ +static int +ddWindowConv3( +DdManager * table, +int low, +int high) +{ +int x; +int res; +int nwin; +int newevent; +int *events; + +#ifdef DD_DEBUG +assert(low >= 0 && high < table->size); +#endif + +if (high-low < 2) return(ddWindowConv2(table,low,high)); + +nwin = high-low-1; +events = ALLOC(int,nwin); +if (events == NULL) { +table->errorCode = CUDD_MEMORY_OUT; +return(0); +} +for (x=0; x 1) events[x-2] = 1; +newevent = 1; +break; +case BCA: +case CBA: +case CAB: +if (x < nwin-2) events[x+2] = 1; +if (x < nwin-1) events[x+1] = 1; +if (x > 0) events[x-1] = 1; +if (x > 1) events[x-2] = 1; +newevent = 1; +break; +case ACB: +if (x < nwin-2) events[x+2] = 1; +if (x > 0) events[x-1] = 1; +newevent = 1; +break; +default: +FREE(events); +return(0); +} +events[x] = 0; +#ifdef DD_STATS +if (res == ABC) { +(void) fprintf(table->out,"="); +} else { +(void) fprintf(table->out,"-"); +} +fflush(table->out); +#endif +} +} +#ifdef DD_STATS +if (newevent) { +(void) fprintf(table->out,"|"); +fflush(table->out); +} +#endif +} while (newevent); + +FREE(events); + +return(1); + +} /* end of ddWindowConv3 */ + + +/**Function******************************************************************** + + Synopsis [Tries all the permutations of the four variables between w + and w+3 and retains the best.] + + Description [Tries all the permutations of the four variables between + w and w+3 and retains the best. Assumes that no dead nodes are + present. Returns the index of the best permutation (1-24) in case of + success; 0 otherwise.] + + SideEffects [None] + +******************************************************************************/ +static int +ddPermuteWindow4( +DdManager * table, +int w) +{ +int x,y,z; +int size,sizeNew; +int best; + +#ifdef DD_DEBUG +assert(table->dead == 0); +assert(w+3 < table->size); +#endif + +size = table->keys - table->isolated; +x = w+1; y = x+1; z = y+1; + +/* The permutation pattern is: + * (w,x)(y,z)(w,x)(x,y) + * (y,z)(w,x)(y,z)(x,y) + * repeated three times to get all 4! = 24 permutations. + * This gives a hamiltonian circuit of Cayley's graph. + * The codes to the permutation are assigned in topological order. + * The permutations at lower distance from the final permutation are + * assigned lower codes. This way we can choose, between + * permutations that give the same size, one that requires the minimum + * number of swaps from the final permutation of the hamiltonian circuit. + * There is an exception to this rule: ABCD is given Code 1, to + * avoid oscillation when convergence is sought. + */ +#define ABCD 1 +best = ABCD; + +#define BACD 7 +sizeNew = cuddSwapInPlace(table,w,x); +if (sizeNew < size) { +if (sizeNew == 0) return(0); +best = BACD; +size = sizeNew; +} +#define BADC 13 +sizeNew = cuddSwapInPlace(table,y,z); +if (sizeNew < size) { +if (sizeNew == 0) return(0); +best = BADC; +size = sizeNew; +} +#define ABDC 8 +sizeNew = cuddSwapInPlace(table,w,x); +if (sizeNew < size || (sizeNew == size && ABDC < best)) { +if (sizeNew == 0) return(0); +best = ABDC; +size = sizeNew; +} +#define ADBC 14 +sizeNew = cuddSwapInPlace(table,x,y); +if (sizeNew < size) { +if (sizeNew == 0) return(0); +best = ADBC; +size = sizeNew; +} +#define ADCB 9 +sizeNew = cuddSwapInPlace(table,y,z); +if (sizeNew < size || (sizeNew == size && ADCB < best)) { +if (sizeNew == 0) return(0); +best = ADCB; +size = sizeNew; +} +#define DACB 15 +sizeNew = cuddSwapInPlace(table,w,x); +if (sizeNew < size) { +if (sizeNew == 0) return(0); +best = DACB; +size = sizeNew; +} +#define DABC 20 +sizeNew = cuddSwapInPlace(table,y,z); +if (sizeNew < size) { +if (sizeNew == 0) return(0); +best = DABC; +size = sizeNew; +} +#define DBAC 23 +sizeNew = cuddSwapInPlace(table,x,y); +if (sizeNew < size) { +if (sizeNew == 0) return(0); +best = DBAC; +size = sizeNew; +} +#define BDAC 19 +sizeNew = cuddSwapInPlace(table,w,x); +if (sizeNew < size || (sizeNew == size && BDAC < best)) { +if (sizeNew == 0) return(0); +best = BDAC; +size = sizeNew; +} +#define BDCA 21 +sizeNew = cuddSwapInPlace(table,y,z); +if (sizeNew < size || (sizeNew == size && BDCA < best)) { +if (sizeNew == 0) return(0); +best = BDCA; +size = sizeNew; +} +#define DBCA 24 +sizeNew = cuddSwapInPlace(table,w,x); +if (sizeNew < size) { +if (sizeNew == 0) return(0); +best = DBCA; +size = sizeNew; +} +#define DCBA 22 +sizeNew = cuddSwapInPlace(table,x,y); +if (sizeNew < size || (sizeNew == size && DCBA < best)) { +if (sizeNew == 0) return(0); +best = DCBA; +size = sizeNew; +} +#define DCAB 18 +sizeNew = cuddSwapInPlace(table,y,z); +if (sizeNew < size || (sizeNew == size && DCAB < best)) { +if (sizeNew == 0) return(0); +best = DCAB; +size = sizeNew; +} +#define CDAB 12 +sizeNew = cuddSwapInPlace(table,w,x); +if (sizeNew < size || (sizeNew == size && CDAB < best)) { +if (sizeNew == 0) return(0); +best = CDAB; +size = sizeNew; +} +#define CDBA 17 +sizeNew = cuddSwapInPlace(table,y,z); +if (sizeNew < size || (sizeNew == size && CDBA < best)) { +if (sizeNew == 0) return(0); +best = CDBA; +size = sizeNew; +} +#define CBDA 11 +sizeNew = cuddSwapInPlace(table,x,y); +if (sizeNew < size || (sizeNew == size && CBDA < best)) { +if (sizeNew == 0) return(0); +best = CBDA; +size = sizeNew; +} +#define BCDA 16 +sizeNew = cuddSwapInPlace(table,w,x); +if (sizeNew < size || (sizeNew == size && BCDA < best)) { +if (sizeNew == 0) return(0); +best = BCDA; +size = sizeNew; +} +#define BCAD 10 +sizeNew = cuddSwapInPlace(table,y,z); +if (sizeNew < size || (sizeNew == size && BCAD < best)) { +if (sizeNew == 0) return(0); +best = BCAD; +size = sizeNew; +} +#define CBAD 5 +sizeNew = cuddSwapInPlace(table,w,x); +if (sizeNew < size || (sizeNew == size && CBAD < best)) { +if (sizeNew == 0) return(0); +best = CBAD; +size = sizeNew; +} +#define CABD 3 +sizeNew = cuddSwapInPlace(table,x,y); +if (sizeNew < size || (sizeNew == size && CABD < best)) { +if (sizeNew == 0) return(0); +best = CABD; +size = sizeNew; +} +#define CADB 6 +sizeNew = cuddSwapInPlace(table,y,z); +if (sizeNew < size || (sizeNew == size && CADB < best)) { +if (sizeNew == 0) return(0); +best = CADB; +size = sizeNew; +} +#define ACDB 4 +sizeNew = cuddSwapInPlace(table,w,x); +if (sizeNew < size || (sizeNew == size && ACDB < best)) { +if (sizeNew == 0) return(0); +best = ACDB; +size = sizeNew; +} +#define ACBD 2 +sizeNew = cuddSwapInPlace(table,y,z); +if (sizeNew < size || (sizeNew == size && ACBD < best)) { +if (sizeNew == 0) return(0); +best = ACBD; +size = sizeNew; +} + +/* Now take the shortest route to the best permutation. +** The initial permutation is ACBD. +*/ +switch(best) { +case DBCA: if (!cuddSwapInPlace(table,y,z)) return(0); +case BDCA: if (!cuddSwapInPlace(table,x,y)) return(0); +case CDBA: if (!cuddSwapInPlace(table,w,x)) return(0); +case ADBC: if (!cuddSwapInPlace(table,y,z)) return(0); +case ABDC: if (!cuddSwapInPlace(table,x,y)) return(0); +case ACDB: if (!cuddSwapInPlace(table,y,z)) return(0); +case ACBD: break; +case DCBA: if (!cuddSwapInPlace(table,y,z)) return(0); +case BCDA: if (!cuddSwapInPlace(table,x,y)) return(0); +case CBDA: if (!cuddSwapInPlace(table,w,x)) return(0); +if (!cuddSwapInPlace(table,x,y)) return(0); +if (!cuddSwapInPlace(table,y,z)) return(0); +break; +case DBAC: if (!cuddSwapInPlace(table,x,y)) return(0); +case DCAB: if (!cuddSwapInPlace(table,w,x)) return(0); +case DACB: if (!cuddSwapInPlace(table,y,z)) return(0); +case BACD: if (!cuddSwapInPlace(table,x,y)) return(0); +case CABD: if (!cuddSwapInPlace(table,w,x)) return(0); +break; +case DABC: if (!cuddSwapInPlace(table,y,z)) return(0); +case BADC: if (!cuddSwapInPlace(table,x,y)) return(0); +case CADB: if (!cuddSwapInPlace(table,w,x)) return(0); +if (!cuddSwapInPlace(table,y,z)) return(0); +break; +case BDAC: if (!cuddSwapInPlace(table,x,y)) return(0); +case CDAB: if (!cuddSwapInPlace(table,w,x)) return(0); +case ADCB: if (!cuddSwapInPlace(table,y,z)) return(0); +case ABCD: if (!cuddSwapInPlace(table,x,y)) return(0); +break; +case BCAD: if (!cuddSwapInPlace(table,x,y)) return(0); +case CBAD: if (!cuddSwapInPlace(table,w,x)) return(0); +if (!cuddSwapInPlace(table,x,y)) return(0); +break; +default: return(0); +} + +#ifdef DD_DEBUG +assert(table->keys - table->isolated == (unsigned) size); +#endif + +return(best); + +} /* end of ddPermuteWindow4 */ + + +/**Function******************************************************************** + + Synopsis [Reorders by applying a sliding window of width 4.] + + Description [Reorders by applying a sliding window of width 4. + Tries all possible permutations to the variables in a + window that slides from low to high. Assumes that no dead nodes are + present. Returns 1 in case of success; 0 otherwise.] + + SideEffects [None] + +******************************************************************************/ +static int +ddWindow4( +DdManager * table, +int low, +int high) +{ + +int w; +int res; + +#ifdef DD_DEBUG +assert(low >= 0 && high < table->size); +#endif + +if (high-low < 3) return(ddWindow3(table,low,high)); + +for (w = low; w+2 < high; w++) { +res = ddPermuteWindow4(table,w); +if (res == 0) return(0); +#ifdef DD_STATS +if (res == ABCD) { +(void) fprintf(table->out,"="); +} else { +(void) fprintf(table->out,"-"); +} +fflush(table->out); +#endif +} + +return(1); + +} /* end of ddWindow4 */ + + +/**Function******************************************************************** + + Synopsis [Reorders by repeatedly applying a sliding window of width 4.] + + Description [Reorders by repeatedly applying a sliding window of width + 4. Tries all possible permutations to the variables in a + window that slides from low to high. Assumes that no dead nodes are + present. Uses an event-driven approach to determine convergence. + Returns 1 in case of success; 0 otherwise.] + + SideEffects [None] + +******************************************************************************/ +static int +ddWindowConv4( +DdManager * table, +int low, +int high) +{ +int x; +int res; +int nwin; +int newevent; +int *events; + +#ifdef DD_DEBUG +assert(low >= 0 && high < table->size); +#endif + +if (high-low < 3) return(ddWindowConv3(table,low,high)); + +nwin = high-low-2; +events = ALLOC(int,nwin); +if (events == NULL) { +table->errorCode = CUDD_MEMORY_OUT; +return(0); +} +for (x=0; x 2) events[x-3] = 1; +newevent = 1; +break; +case BADC: +if (x < nwin-3) events[x+3] = 1; +if (x < nwin-1) events[x+1] = 1; +if (x > 0) events[x-1] = 1; +if (x > 2) events[x-3] = 1; +newevent = 1; +break; +case ABDC: +if (x < nwin-3) events[x+3] = 1; +if (x > 0) events[x-1] = 1; +newevent = 1; +break; +case ADBC: +case ADCB: +case ACDB: +if (x < nwin-3) events[x+3] = 1; +if (x < nwin-2) events[x+2] = 1; +if (x > 0) events[x-1] = 1; +if (x > 1) events[x-2] = 1; +newevent = 1; +break; +case DACB: +case DABC: +case DBAC: +case BDAC: +case BDCA: +case DBCA: +case DCBA: +case DCAB: +case CDAB: +case CDBA: +case CBDA: +case BCDA: +case CADB: +if (x < nwin-3) events[x+3] = 1; +if (x < nwin-2) events[x+2] = 1; +if (x < nwin-1) events[x+1] = 1; +if (x > 0) events[x-1] = 1; +if (x > 1) events[x-2] = 1; +if (x > 2) events[x-3] = 1; +newevent = 1; +break; +case BCAD: +case CBAD: +case CABD: +if (x < nwin-2) events[x+2] = 1; +if (x < nwin-1) events[x+1] = 1; +if (x > 1) events[x-2] = 1; +if (x > 2) events[x-3] = 1; +newevent = 1; +break; +case ACBD: +if (x < nwin-2) events[x+2] = 1; +if (x > 1) events[x-2] = 1; +newevent = 1; +break; +default: +FREE(events); +return(0); +} +events[x] = 0; +#ifdef DD_STATS +if (res == ABCD) { +(void) fprintf(table->out,"="); +} else { +(void) fprintf(table->out,"-"); +} +fflush(table->out); +#endif +} +} +#ifdef DD_STATS +if (newevent) { +(void) fprintf(table->out,"|"); +fflush(table->out); +} +#endif +} while (newevent); + +FREE(events); + +return(1); + +} /* end of ddWindowConv4 */ +/**CFile*********************************************************************** + + FileName [cuddZddFuncs.c] + + PackageName [cudd] + + Synopsis [Functions to manipulate covers represented as ZDDs.] + + Description [External procedures included in this module: +
        +
      • Cudd_zddProduct(); +
      • Cudd_zddUnateProduct(); +
      • Cudd_zddWeakDiv(); +
      • Cudd_zddWeakDivF(); +
      • Cudd_zddDivide(); +
      • Cudd_zddDivideF(); +
      • Cudd_zddComplement(); +
      + Internal procedures included in this module: +
        +
      • cuddZddProduct(); +
      • cuddZddUnateProduct(); +
      • cuddZddWeakDiv(); +
      • cuddZddWeakDivF(); +
      • cuddZddDivide(); +
      • cuddZddDivideF(); +
      • cuddZddGetCofactors3() +
      • cuddZddGetCofactors2() +
      • cuddZddComplement(); +
      • cuddZddGetPosVarIndex(); +
      • cuddZddGetNegVarIndex(); +
      • cuddZddGetPosVarLevel(); +
      • cuddZddGetNegVarLevel(); +
      + Static procedures included in this module: +
        +
      + ] + + SeeAlso [] + + Author [In-Ho Moon] + + Copyright [Copyright (c) 1995-2012, Regents of the University of Colorado + + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + Neither the name of the University of Colorado nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE.] + +******************************************************************************/ + + +/*---------------------------------------------------------------------------*/ +/* Constant declarations */ +/*---------------------------------------------------------------------------*/ + + +/*---------------------------------------------------------------------------*/ +/* Stucture declarations */ +/*---------------------------------------------------------------------------*/ + + +/*---------------------------------------------------------------------------*/ +/* Type declarations */ +/*---------------------------------------------------------------------------*/ + + +/*---------------------------------------------------------------------------*/ +/* Variable declarations */ +/*---------------------------------------------------------------------------*/ + +//#ifndef lint +//static char rcsid[] DD_UNUSED = "$Id: cuddZddFuncs.c,v 1.17 2012/02/05 01:07:19 fabio Exp $"; +//#endif + +/*---------------------------------------------------------------------------*/ +/* Macro declarations */ +/*---------------------------------------------------------------------------*/ + + +/**AutomaticStart*************************************************************/ + +/*---------------------------------------------------------------------------*/ +/* Static function prototypes */ +/*---------------------------------------------------------------------------*/ + + +/**AutomaticEnd***************************************************************/ + + +/*---------------------------------------------------------------------------*/ +/* Definition of exported functions */ +/*---------------------------------------------------------------------------*/ + + +/*---------------------------------------------------------------------------*/ +/* Definition of internal functions */ +/*---------------------------------------------------------------------------*/ + + +/**Function******************************************************************** + + Synopsis [Performs the recursive step of Cudd_zddProduct.] + + Description [] + + SideEffects [None] + + SeeAlso [Cudd_zddProduct] + +******************************************************************************/ +DdNode * +cuddZddProduct( + DdManager * dd, + DdNode * f, + DdNode * g) +{ + int v, top_f, top_g; + DdNode *tmp, *term1, *term2, *term3; + DdNode *f0, *f1, *fd, *g0, *g1, *gd; + DdNode *R0, *R1, *Rd, *N0, *N1; + DdNode *r; + DdNode *one = DD_ONE(dd); + DdNode *zero = DD_ZERO(dd); + int flag; + int pv, nv; + + statLine(dd); + if (f == zero || g == zero) + return(zero); + if (f == one) + return(g); + if (g == one) + return(f); + + top_f = dd->permZ[f->index]; + top_g = dd->permZ[g->index]; + + if (top_f > top_g) + return(cuddZddProduct(dd, g, f)); + + /* Check cache */ + r = cuddCacheLookup2Zdd(dd, cuddZddProduct, f, g); + if (r) + return(r); + + v = f->index; /* either yi or zi */ + flag = cuddZddGetCofactors3(dd, f, v, &f1, &f0, &fd); + if (flag == 1) + return(NULL); + Cudd_Ref(f1); + Cudd_Ref(f0); + Cudd_Ref(fd); + flag = cuddZddGetCofactors3(dd, g, v, &g1, &g0, &gd); + if (flag == 1) { + Cudd_RecursiveDerefZdd(dd, f1); + Cudd_RecursiveDerefZdd(dd, f0); + Cudd_RecursiveDerefZdd(dd, fd); + return(NULL); + } + Cudd_Ref(g1); + Cudd_Ref(g0); + Cudd_Ref(gd); + pv = cuddZddGetPosVarIndex(dd, v); + nv = cuddZddGetNegVarIndex(dd, v); + + Rd = cuddZddProduct(dd, fd, gd); + if (Rd == NULL) { + Cudd_RecursiveDerefZdd(dd, f1); + Cudd_RecursiveDerefZdd(dd, f0); + Cudd_RecursiveDerefZdd(dd, fd); + Cudd_RecursiveDerefZdd(dd, g1); + Cudd_RecursiveDerefZdd(dd, g0); + Cudd_RecursiveDerefZdd(dd, gd); + return(NULL); + } + Cudd_Ref(Rd); + + term1 = cuddZddProduct(dd, f0, g0); + if (term1 == NULL) { + Cudd_RecursiveDerefZdd(dd, f1); + Cudd_RecursiveDerefZdd(dd, f0); + Cudd_RecursiveDerefZdd(dd, fd); + Cudd_RecursiveDerefZdd(dd, g1); + Cudd_RecursiveDerefZdd(dd, g0); + Cudd_RecursiveDerefZdd(dd, gd); + Cudd_RecursiveDerefZdd(dd, Rd); + return(NULL); + } + Cudd_Ref(term1); + term2 = cuddZddProduct(dd, f0, gd); + if (term2 == NULL) { + Cudd_RecursiveDerefZdd(dd, f1); + Cudd_RecursiveDerefZdd(dd, f0); + Cudd_RecursiveDerefZdd(dd, fd); + Cudd_RecursiveDerefZdd(dd, g1); + Cudd_RecursiveDerefZdd(dd, g0); + Cudd_RecursiveDerefZdd(dd, gd); + Cudd_RecursiveDerefZdd(dd, Rd); + Cudd_RecursiveDerefZdd(dd, term1); + return(NULL); + } + Cudd_Ref(term2); + term3 = cuddZddProduct(dd, fd, g0); + if (term3 == NULL) { + Cudd_RecursiveDerefZdd(dd, f1); + Cudd_RecursiveDerefZdd(dd, f0); + Cudd_RecursiveDerefZdd(dd, fd); + Cudd_RecursiveDerefZdd(dd, g1); + Cudd_RecursiveDerefZdd(dd, g0); + Cudd_RecursiveDerefZdd(dd, gd); + Cudd_RecursiveDerefZdd(dd, Rd); + Cudd_RecursiveDerefZdd(dd, term1); + Cudd_RecursiveDerefZdd(dd, term2); + return(NULL); + } + Cudd_Ref(term3); + Cudd_RecursiveDerefZdd(dd, f0); + Cudd_RecursiveDerefZdd(dd, g0); + tmp = cuddZddUnion(dd, term1, term2); + if (tmp == NULL) { + Cudd_RecursiveDerefZdd(dd, f1); + Cudd_RecursiveDerefZdd(dd, fd); + Cudd_RecursiveDerefZdd(dd, g1); + Cudd_RecursiveDerefZdd(dd, gd); + Cudd_RecursiveDerefZdd(dd, Rd); + Cudd_RecursiveDerefZdd(dd, term1); + Cudd_RecursiveDerefZdd(dd, term2); + Cudd_RecursiveDerefZdd(dd, term3); + return(NULL); + } + Cudd_Ref(tmp); + Cudd_RecursiveDerefZdd(dd, term1); + Cudd_RecursiveDerefZdd(dd, term2); + R0 = cuddZddUnion(dd, tmp, term3); + if (R0 == NULL) { + Cudd_RecursiveDerefZdd(dd, f1); + Cudd_RecursiveDerefZdd(dd, fd); + Cudd_RecursiveDerefZdd(dd, g1); + Cudd_RecursiveDerefZdd(dd, gd); + Cudd_RecursiveDerefZdd(dd, Rd); + Cudd_RecursiveDerefZdd(dd, term3); + Cudd_RecursiveDerefZdd(dd, tmp); + return(NULL); + } + Cudd_Ref(R0); + Cudd_RecursiveDerefZdd(dd, tmp); + Cudd_RecursiveDerefZdd(dd, term3); + N0 = cuddZddGetNode(dd, nv, R0, Rd); /* nv = zi */ + if (N0 == NULL) { + Cudd_RecursiveDerefZdd(dd, f1); + Cudd_RecursiveDerefZdd(dd, fd); + Cudd_RecursiveDerefZdd(dd, g1); + Cudd_RecursiveDerefZdd(dd, gd); + Cudd_RecursiveDerefZdd(dd, Rd); + Cudd_RecursiveDerefZdd(dd, R0); + return(NULL); + } + Cudd_Ref(N0); + Cudd_RecursiveDerefZdd(dd, R0); + Cudd_RecursiveDerefZdd(dd, Rd); + + term1 = cuddZddProduct(dd, f1, g1); + if (term1 == NULL) { + Cudd_RecursiveDerefZdd(dd, f1); + Cudd_RecursiveDerefZdd(dd, fd); + Cudd_RecursiveDerefZdd(dd, g1); + Cudd_RecursiveDerefZdd(dd, gd); + Cudd_RecursiveDerefZdd(dd, N0); + return(NULL); + } + Cudd_Ref(term1); + term2 = cuddZddProduct(dd, f1, gd); + if (term2 == NULL) { + Cudd_RecursiveDerefZdd(dd, f1); + Cudd_RecursiveDerefZdd(dd, fd); + Cudd_RecursiveDerefZdd(dd, g1); + Cudd_RecursiveDerefZdd(dd, gd); + Cudd_RecursiveDerefZdd(dd, N0); + Cudd_RecursiveDerefZdd(dd, term1); + return(NULL); + } + Cudd_Ref(term2); + term3 = cuddZddProduct(dd, fd, g1); + if (term3 == NULL) { + Cudd_RecursiveDerefZdd(dd, f1); + Cudd_RecursiveDerefZdd(dd, fd); + Cudd_RecursiveDerefZdd(dd, g1); + Cudd_RecursiveDerefZdd(dd, gd); + Cudd_RecursiveDerefZdd(dd, N0); + Cudd_RecursiveDerefZdd(dd, term1); + Cudd_RecursiveDerefZdd(dd, term2); + return(NULL); + } + Cudd_Ref(term3); + Cudd_RecursiveDerefZdd(dd, f1); + Cudd_RecursiveDerefZdd(dd, g1); + Cudd_RecursiveDerefZdd(dd, fd); + Cudd_RecursiveDerefZdd(dd, gd); + tmp = cuddZddUnion(dd, term1, term2); + if (tmp == NULL) { + Cudd_RecursiveDerefZdd(dd, N0); + Cudd_RecursiveDerefZdd(dd, term1); + Cudd_RecursiveDerefZdd(dd, term2); + Cudd_RecursiveDerefZdd(dd, term3); + return(NULL); + } + Cudd_Ref(tmp); + Cudd_RecursiveDerefZdd(dd, term1); + Cudd_RecursiveDerefZdd(dd, term2); + R1 = cuddZddUnion(dd, tmp, term3); + if (R1 == NULL) { + Cudd_RecursiveDerefZdd(dd, N0); + Cudd_RecursiveDerefZdd(dd, term3); + Cudd_RecursiveDerefZdd(dd, tmp); + return(NULL); + } + Cudd_Ref(R1); + Cudd_RecursiveDerefZdd(dd, tmp); + Cudd_RecursiveDerefZdd(dd, term3); + N1 = cuddZddGetNode(dd, pv, R1, N0); /* pv = yi */ + if (N1 == NULL) { + Cudd_RecursiveDerefZdd(dd, N0); + Cudd_RecursiveDerefZdd(dd, R1); + return(NULL); + } + Cudd_Ref(N1); + Cudd_RecursiveDerefZdd(dd, R1); + Cudd_RecursiveDerefZdd(dd, N0); + + cuddCacheInsert2(dd, cuddZddProduct, f, g, N1); + Cudd_Deref(N1); + return(N1); + +} /* end of cuddZddProduct */ + + +/**Function******************************************************************** + + Synopsis [Performs the recursive step of Cudd_zddUnateProduct.] + + Description [] + + SideEffects [None] + + SeeAlso [Cudd_zddUnateProduct] + +******************************************************************************/ +DdNode * +cuddZddUnateProduct( + DdManager * dd, + DdNode * f, + DdNode * g) +{ + int v, top_f, top_g; + DdNode *term1, *term2, *term3, *term4; + DdNode *sum1, *sum2; + DdNode *f0, *f1, *g0, *g1; + DdNode *r; + DdNode *one = DD_ONE(dd); + DdNode *zero = DD_ZERO(dd); + int flag; + + statLine(dd); + if (f == zero || g == zero) + return(zero); + if (f == one) + return(g); + if (g == one) + return(f); + + top_f = dd->permZ[f->index]; + top_g = dd->permZ[g->index]; + + if (top_f > top_g) + return(cuddZddUnateProduct(dd, g, f)); + + /* Check cache */ + r = cuddCacheLookup2Zdd(dd, cuddZddUnateProduct, f, g); + if (r) + return(r); + + v = f->index; /* either yi or zi */ + flag = cuddZddGetCofactors2(dd, f, v, &f1, &f0); + if (flag == 1) + return(NULL); + Cudd_Ref(f1); + Cudd_Ref(f0); + flag = cuddZddGetCofactors2(dd, g, v, &g1, &g0); + if (flag == 1) { + Cudd_RecursiveDerefZdd(dd, f1); + Cudd_RecursiveDerefZdd(dd, f0); + return(NULL); + } + Cudd_Ref(g1); + Cudd_Ref(g0); + + term1 = cuddZddUnateProduct(dd, f1, g1); + if (term1 == NULL) { + Cudd_RecursiveDerefZdd(dd, f1); + Cudd_RecursiveDerefZdd(dd, f0); + Cudd_RecursiveDerefZdd(dd, g1); + Cudd_RecursiveDerefZdd(dd, g0); + return(NULL); + } + Cudd_Ref(term1); + term2 = cuddZddUnateProduct(dd, f1, g0); + if (term2 == NULL) { + Cudd_RecursiveDerefZdd(dd, f1); + Cudd_RecursiveDerefZdd(dd, f0); + Cudd_RecursiveDerefZdd(dd, g1); + Cudd_RecursiveDerefZdd(dd, g0); + Cudd_RecursiveDerefZdd(dd, term1); + return(NULL); + } + Cudd_Ref(term2); + term3 = cuddZddUnateProduct(dd, f0, g1); + if (term3 == NULL) { + Cudd_RecursiveDerefZdd(dd, f1); + Cudd_RecursiveDerefZdd(dd, f0); + Cudd_RecursiveDerefZdd(dd, g1); + Cudd_RecursiveDerefZdd(dd, g0); + Cudd_RecursiveDerefZdd(dd, term1); + Cudd_RecursiveDerefZdd(dd, term2); + return(NULL); + } + Cudd_Ref(term3); + term4 = cuddZddUnateProduct(dd, f0, g0); + if (term4 == NULL) { + Cudd_RecursiveDerefZdd(dd, f1); + Cudd_RecursiveDerefZdd(dd, f0); + Cudd_RecursiveDerefZdd(dd, g1); + Cudd_RecursiveDerefZdd(dd, g0); + Cudd_RecursiveDerefZdd(dd, term1); + Cudd_RecursiveDerefZdd(dd, term2); + Cudd_RecursiveDerefZdd(dd, term3); + return(NULL); + } + Cudd_Ref(term4); + Cudd_RecursiveDerefZdd(dd, f1); + Cudd_RecursiveDerefZdd(dd, f0); + Cudd_RecursiveDerefZdd(dd, g1); + Cudd_RecursiveDerefZdd(dd, g0); + sum1 = cuddZddUnion(dd, term1, term2); + if (sum1 == NULL) { + Cudd_RecursiveDerefZdd(dd, term1); + Cudd_RecursiveDerefZdd(dd, term2); + Cudd_RecursiveDerefZdd(dd, term3); + Cudd_RecursiveDerefZdd(dd, term4); + return(NULL); + } + Cudd_Ref(sum1); + Cudd_RecursiveDerefZdd(dd, term1); + Cudd_RecursiveDerefZdd(dd, term2); + sum2 = cuddZddUnion(dd, sum1, term3); + if (sum2 == NULL) { + Cudd_RecursiveDerefZdd(dd, term3); + Cudd_RecursiveDerefZdd(dd, term4); + Cudd_RecursiveDerefZdd(dd, sum1); + return(NULL); + } + Cudd_Ref(sum2); + Cudd_RecursiveDerefZdd(dd, sum1); + Cudd_RecursiveDerefZdd(dd, term3); + r = cuddZddGetNode(dd, v, sum2, term4); + if (r == NULL) { + Cudd_RecursiveDerefZdd(dd, term4); + Cudd_RecursiveDerefZdd(dd, sum2); + return(NULL); + } + Cudd_Ref(r); + Cudd_RecursiveDerefZdd(dd, sum2); + Cudd_RecursiveDerefZdd(dd, term4); + + cuddCacheInsert2(dd, cuddZddUnateProduct, f, g, r); + Cudd_Deref(r); + return(r); + +} /* end of cuddZddUnateProduct */ + + +/**Function******************************************************************** + + Synopsis [Performs the recursive step of Cudd_zddWeakDiv.] + + Description [] + + SideEffects [None] + + SeeAlso [Cudd_zddWeakDiv] + +******************************************************************************/ +DdNode * +cuddZddWeakDiv( + DdManager * dd, + DdNode * f, + DdNode * g) +{ + int v; + DdNode *one = DD_ONE(dd); + DdNode *zero = DD_ZERO(dd); + DdNode *f0, *f1, *fd, *g0, *g1, *gd; + DdNode *q, *tmp; + DdNode *r; + int flag; + + statLine(dd); + if (g == one) + return(f); + if (f == zero || f == one) + return(zero); + if (f == g) + return(one); + + /* Check cache. */ + r = cuddCacheLookup2Zdd(dd, cuddZddWeakDiv, f, g); + if (r) + return(r); + + v = g->index; + + flag = cuddZddGetCofactors3(dd, f, v, &f1, &f0, &fd); + if (flag == 1) + return(NULL); + Cudd_Ref(f1); + Cudd_Ref(f0); + Cudd_Ref(fd); + flag = cuddZddGetCofactors3(dd, g, v, &g1, &g0, &gd); + if (flag == 1) { + Cudd_RecursiveDerefZdd(dd, f1); + Cudd_RecursiveDerefZdd(dd, f0); + Cudd_RecursiveDerefZdd(dd, fd); + return(NULL); + } + Cudd_Ref(g1); + Cudd_Ref(g0); + Cudd_Ref(gd); + + q = g; + + if (g0 != zero) { + q = cuddZddWeakDiv(dd, f0, g0); + if (q == NULL) { + Cudd_RecursiveDerefZdd(dd, f1); + Cudd_RecursiveDerefZdd(dd, f0); + Cudd_RecursiveDerefZdd(dd, fd); + Cudd_RecursiveDerefZdd(dd, g1); + Cudd_RecursiveDerefZdd(dd, g0); + Cudd_RecursiveDerefZdd(dd, gd); + return(NULL); + } + Cudd_Ref(q); + } + else + Cudd_Ref(q); + Cudd_RecursiveDerefZdd(dd, f0); + Cudd_RecursiveDerefZdd(dd, g0); + + if (q == zero) { + Cudd_RecursiveDerefZdd(dd, f1); + Cudd_RecursiveDerefZdd(dd, g1); + Cudd_RecursiveDerefZdd(dd, fd); + Cudd_RecursiveDerefZdd(dd, gd); + cuddCacheInsert2(dd, cuddZddWeakDiv, f, g, zero); + Cudd_Deref(q); + return(zero); + } + + if (g1 != zero) { + Cudd_RecursiveDerefZdd(dd, q); + tmp = cuddZddWeakDiv(dd, f1, g1); + if (tmp == NULL) { + Cudd_RecursiveDerefZdd(dd, f1); + Cudd_RecursiveDerefZdd(dd, g1); + Cudd_RecursiveDerefZdd(dd, fd); + Cudd_RecursiveDerefZdd(dd, gd); + return(NULL); + } + Cudd_Ref(tmp); + Cudd_RecursiveDerefZdd(dd, f1); + Cudd_RecursiveDerefZdd(dd, g1); + if (q == g) + q = tmp; + else { + q = cuddZddIntersect(dd, q, tmp); + if (q == NULL) { + Cudd_RecursiveDerefZdd(dd, fd); + Cudd_RecursiveDerefZdd(dd, gd); + return(NULL); + } + Cudd_Ref(q); + Cudd_RecursiveDerefZdd(dd, tmp); + } + } + else { + Cudd_RecursiveDerefZdd(dd, f1); + Cudd_RecursiveDerefZdd(dd, g1); + } + + if (q == zero) { + Cudd_RecursiveDerefZdd(dd, fd); + Cudd_RecursiveDerefZdd(dd, gd); + cuddCacheInsert2(dd, cuddZddWeakDiv, f, g, zero); + Cudd_Deref(q); + return(zero); + } + + if (gd != zero) { + Cudd_RecursiveDerefZdd(dd, q); + tmp = cuddZddWeakDiv(dd, fd, gd); + if (tmp == NULL) { + Cudd_RecursiveDerefZdd(dd, fd); + Cudd_RecursiveDerefZdd(dd, gd); + return(NULL); + } + Cudd_Ref(tmp); + Cudd_RecursiveDerefZdd(dd, fd); + Cudd_RecursiveDerefZdd(dd, gd); + if (q == g) + q = tmp; + else { + q = cuddZddIntersect(dd, q, tmp); + if (q == NULL) { + Cudd_RecursiveDerefZdd(dd, tmp); + return(NULL); + } + Cudd_Ref(q); + Cudd_RecursiveDerefZdd(dd, tmp); + } + } + else { + Cudd_RecursiveDerefZdd(dd, fd); + Cudd_RecursiveDerefZdd(dd, gd); + } + + cuddCacheInsert2(dd, cuddZddWeakDiv, f, g, q); + Cudd_Deref(q); + return(q); + +} /* end of cuddZddWeakDiv */ + + +/**Function******************************************************************** + + Synopsis [Performs the recursive step of Cudd_zddWeakDivF.] + + Description [] + + SideEffects [None] + + SeeAlso [Cudd_zddWeakDivF] + +******************************************************************************/ +DdNode * +cuddZddWeakDivF( + DdManager * dd, + DdNode * f, + DdNode * g) +{ + int v, top_f, top_g, vf, vg; + DdNode *one = DD_ONE(dd); + DdNode *zero = DD_ZERO(dd); + DdNode *f0, *f1, *fd, *g0, *g1, *gd; + DdNode *q, *tmp; + DdNode *r; + DdNode *term1, *term0, *termd; + int flag; + int pv, nv; + + statLine(dd); + if (g == one) + return(f); + if (f == zero || f == one) + return(zero); + if (f == g) + return(one); + + /* Check cache. */ + r = cuddCacheLookup2Zdd(dd, cuddZddWeakDivF, f, g); + if (r) + return(r); + + top_f = dd->permZ[f->index]; + top_g = dd->permZ[g->index]; + vf = top_f >> 1; + vg = top_g >> 1; + v = ddMin(top_f, top_g); + + if (v == top_f && vf < vg) { + v = f->index; + flag = cuddZddGetCofactors3(dd, f, v, &f1, &f0, &fd); + if (flag == 1) + return(NULL); + Cudd_Ref(f1); + Cudd_Ref(f0); + Cudd_Ref(fd); + + pv = cuddZddGetPosVarIndex(dd, v); + nv = cuddZddGetNegVarIndex(dd, v); + + term1 = cuddZddWeakDivF(dd, f1, g); + if (term1 == NULL) { + Cudd_RecursiveDerefZdd(dd, f1); + Cudd_RecursiveDerefZdd(dd, f0); + Cudd_RecursiveDerefZdd(dd, fd); + return(NULL); + } + Cudd_Ref(term1); + Cudd_RecursiveDerefZdd(dd, f1); + term0 = cuddZddWeakDivF(dd, f0, g); + if (term0 == NULL) { + Cudd_RecursiveDerefZdd(dd, f0); + Cudd_RecursiveDerefZdd(dd, fd); + Cudd_RecursiveDerefZdd(dd, term1); + return(NULL); + } + Cudd_Ref(term0); + Cudd_RecursiveDerefZdd(dd, f0); + termd = cuddZddWeakDivF(dd, fd, g); + if (termd == NULL) { + Cudd_RecursiveDerefZdd(dd, fd); + Cudd_RecursiveDerefZdd(dd, term1); + Cudd_RecursiveDerefZdd(dd, term0); + return(NULL); + } + Cudd_Ref(termd); + Cudd_RecursiveDerefZdd(dd, fd); + + tmp = cuddZddGetNode(dd, nv, term0, termd); /* nv = zi */ + if (tmp == NULL) { + Cudd_RecursiveDerefZdd(dd, term1); + Cudd_RecursiveDerefZdd(dd, term0); + Cudd_RecursiveDerefZdd(dd, termd); + return(NULL); + } + Cudd_Ref(tmp); + Cudd_RecursiveDerefZdd(dd, term0); + Cudd_RecursiveDerefZdd(dd, termd); + q = cuddZddGetNode(dd, pv, term1, tmp); /* pv = yi */ + if (q == NULL) { + Cudd_RecursiveDerefZdd(dd, term1); + Cudd_RecursiveDerefZdd(dd, tmp); + return(NULL); + } + Cudd_Ref(q); + Cudd_RecursiveDerefZdd(dd, term1); + Cudd_RecursiveDerefZdd(dd, tmp); + + cuddCacheInsert2(dd, cuddZddWeakDivF, f, g, q); + Cudd_Deref(q); + return(q); + } + + if (v == top_f) + v = f->index; + else + v = g->index; + + flag = cuddZddGetCofactors3(dd, f, v, &f1, &f0, &fd); + if (flag == 1) + return(NULL); + Cudd_Ref(f1); + Cudd_Ref(f0); + Cudd_Ref(fd); + flag = cuddZddGetCofactors3(dd, g, v, &g1, &g0, &gd); + if (flag == 1) { + Cudd_RecursiveDerefZdd(dd, f1); + Cudd_RecursiveDerefZdd(dd, f0); + Cudd_RecursiveDerefZdd(dd, fd); + return(NULL); + } + Cudd_Ref(g1); + Cudd_Ref(g0); + Cudd_Ref(gd); + + q = g; + + if (g0 != zero) { + q = cuddZddWeakDivF(dd, f0, g0); + if (q == NULL) { + Cudd_RecursiveDerefZdd(dd, f1); + Cudd_RecursiveDerefZdd(dd, f0); + Cudd_RecursiveDerefZdd(dd, fd); + Cudd_RecursiveDerefZdd(dd, g1); + Cudd_RecursiveDerefZdd(dd, g0); + Cudd_RecursiveDerefZdd(dd, gd); + return(NULL); + } + Cudd_Ref(q); + } + else + Cudd_Ref(q); + Cudd_RecursiveDerefZdd(dd, f0); + Cudd_RecursiveDerefZdd(dd, g0); + + if (q == zero) { + Cudd_RecursiveDerefZdd(dd, f1); + Cudd_RecursiveDerefZdd(dd, g1); + Cudd_RecursiveDerefZdd(dd, fd); + Cudd_RecursiveDerefZdd(dd, gd); + cuddCacheInsert2(dd, cuddZddWeakDivF, f, g, zero); + Cudd_Deref(q); + return(zero); + } + + if (g1 != zero) { + Cudd_RecursiveDerefZdd(dd, q); + tmp = cuddZddWeakDivF(dd, f1, g1); + if (tmp == NULL) { + Cudd_RecursiveDerefZdd(dd, f1); + Cudd_RecursiveDerefZdd(dd, g1); + Cudd_RecursiveDerefZdd(dd, fd); + Cudd_RecursiveDerefZdd(dd, gd); + return(NULL); + } + Cudd_Ref(tmp); + Cudd_RecursiveDerefZdd(dd, f1); + Cudd_RecursiveDerefZdd(dd, g1); + if (q == g) + q = tmp; + else { + q = cuddZddIntersect(dd, q, tmp); + if (q == NULL) { + Cudd_RecursiveDerefZdd(dd, fd); + Cudd_RecursiveDerefZdd(dd, gd); + return(NULL); + } + Cudd_Ref(q); + Cudd_RecursiveDerefZdd(dd, tmp); + } + } + else { + Cudd_RecursiveDerefZdd(dd, f1); + Cudd_RecursiveDerefZdd(dd, g1); + } + + if (q == zero) { + Cudd_RecursiveDerefZdd(dd, fd); + Cudd_RecursiveDerefZdd(dd, gd); + cuddCacheInsert2(dd, cuddZddWeakDivF, f, g, zero); + Cudd_Deref(q); + return(zero); + } + + if (gd != zero) { + Cudd_RecursiveDerefZdd(dd, q); + tmp = cuddZddWeakDivF(dd, fd, gd); + if (tmp == NULL) { + Cudd_RecursiveDerefZdd(dd, fd); + Cudd_RecursiveDerefZdd(dd, gd); + return(NULL); + } + Cudd_Ref(tmp); + Cudd_RecursiveDerefZdd(dd, fd); + Cudd_RecursiveDerefZdd(dd, gd); + if (q == g) + q = tmp; + else { + q = cuddZddIntersect(dd, q, tmp); + if (q == NULL) { + Cudd_RecursiveDerefZdd(dd, tmp); + return(NULL); + } + Cudd_Ref(q); + Cudd_RecursiveDerefZdd(dd, tmp); + } + } + else { + Cudd_RecursiveDerefZdd(dd, fd); + Cudd_RecursiveDerefZdd(dd, gd); + } + + cuddCacheInsert2(dd, cuddZddWeakDivF, f, g, q); + Cudd_Deref(q); + return(q); + +} /* end of cuddZddWeakDivF */ + + +/**Function******************************************************************** + + Synopsis [Performs the recursive step of Cudd_zddDivide.] + + Description [] + + SideEffects [None] + + SeeAlso [Cudd_zddDivide] + +******************************************************************************/ +DdNode * +cuddZddDivide( + DdManager * dd, + DdNode * f, + DdNode * g) +{ + int v; + DdNode *one = DD_ONE(dd); + DdNode *zero = DD_ZERO(dd); + DdNode *f0, *f1, *g0, *g1; + DdNode *q, *r, *tmp; + int flag; + + statLine(dd); + if (g == one) + return(f); + if (f == zero || f == one) + return(zero); + if (f == g) + return(one); + + /* Check cache. */ + r = cuddCacheLookup2Zdd(dd, cuddZddDivide, f, g); + if (r) + return(r); + + v = g->index; + + flag = cuddZddGetCofactors2(dd, f, v, &f1, &f0); + if (flag == 1) + return(NULL); + Cudd_Ref(f1); + Cudd_Ref(f0); + flag = cuddZddGetCofactors2(dd, g, v, &g1, &g0); /* g1 != zero */ + if (flag == 1) { + Cudd_RecursiveDerefZdd(dd, f1); + Cudd_RecursiveDerefZdd(dd, f0); + return(NULL); + } + Cudd_Ref(g1); + Cudd_Ref(g0); + + r = cuddZddDivide(dd, f1, g1); + if (r == NULL) { + Cudd_RecursiveDerefZdd(dd, f1); + Cudd_RecursiveDerefZdd(dd, f0); + Cudd_RecursiveDerefZdd(dd, g1); + Cudd_RecursiveDerefZdd(dd, g0); + return(NULL); + } + Cudd_Ref(r); + + if (r != zero && g0 != zero) { + tmp = r; + q = cuddZddDivide(dd, f0, g0); + if (q == NULL) { + Cudd_RecursiveDerefZdd(dd, f1); + Cudd_RecursiveDerefZdd(dd, f0); + Cudd_RecursiveDerefZdd(dd, g1); + Cudd_RecursiveDerefZdd(dd, g0); + return(NULL); + } + Cudd_Ref(q); + r = cuddZddIntersect(dd, r, q); + if (r == NULL) { + Cudd_RecursiveDerefZdd(dd, f1); + Cudd_RecursiveDerefZdd(dd, f0); + Cudd_RecursiveDerefZdd(dd, g1); + Cudd_RecursiveDerefZdd(dd, g0); + Cudd_RecursiveDerefZdd(dd, q); + return(NULL); + } + Cudd_Ref(r); + Cudd_RecursiveDerefZdd(dd, q); + Cudd_RecursiveDerefZdd(dd, tmp); + } + + Cudd_RecursiveDerefZdd(dd, f1); + Cudd_RecursiveDerefZdd(dd, f0); + Cudd_RecursiveDerefZdd(dd, g1); + Cudd_RecursiveDerefZdd(dd, g0); + + cuddCacheInsert2(dd, cuddZddDivide, f, g, r); + Cudd_Deref(r); + return(r); + +} /* end of cuddZddDivide */ + + +/**Function******************************************************************** + + Synopsis [Performs the recursive step of Cudd_zddDivideF.] + + Description [] + + SideEffects [None] + + SeeAlso [Cudd_zddDivideF] + +******************************************************************************/ +DdNode * +cuddZddDivideF( + DdManager * dd, + DdNode * f, + DdNode * g) +{ + int v; + DdNode *one = DD_ONE(dd); + DdNode *zero = DD_ZERO(dd); + DdNode *f0, *f1, *g0, *g1; + DdNode *q, *r, *tmp; + int flag; + + statLine(dd); + if (g == one) + return(f); + if (f == zero || f == one) + return(zero); + if (f == g) + return(one); + + /* Check cache. */ + r = cuddCacheLookup2Zdd(dd, cuddZddDivideF, f, g); + if (r) + return(r); + + v = g->index; + + flag = cuddZddGetCofactors2(dd, f, v, &f1, &f0); + if (flag == 1) + return(NULL); + Cudd_Ref(f1); + Cudd_Ref(f0); + flag = cuddZddGetCofactors2(dd, g, v, &g1, &g0); /* g1 != zero */ + if (flag == 1) { + Cudd_RecursiveDerefZdd(dd, f1); + Cudd_RecursiveDerefZdd(dd, f0); + return(NULL); + } + Cudd_Ref(g1); + Cudd_Ref(g0); + + r = cuddZddDivideF(dd, f1, g1); + if (r == NULL) { + Cudd_RecursiveDerefZdd(dd, f1); + Cudd_RecursiveDerefZdd(dd, f0); + Cudd_RecursiveDerefZdd(dd, g1); + Cudd_RecursiveDerefZdd(dd, g0); + return(NULL); + } + Cudd_Ref(r); + + if (r != zero && g0 != zero) { + tmp = r; + q = cuddZddDivideF(dd, f0, g0); + if (q == NULL) { + Cudd_RecursiveDerefZdd(dd, f1); + Cudd_RecursiveDerefZdd(dd, f0); + Cudd_RecursiveDerefZdd(dd, g1); + Cudd_RecursiveDerefZdd(dd, g0); + return(NULL); + } + Cudd_Ref(q); + r = cuddZddIntersect(dd, r, q); + if (r == NULL) { + Cudd_RecursiveDerefZdd(dd, f1); + Cudd_RecursiveDerefZdd(dd, f0); + Cudd_RecursiveDerefZdd(dd, g1); + Cudd_RecursiveDerefZdd(dd, g0); + Cudd_RecursiveDerefZdd(dd, q); + return(NULL); + } + Cudd_Ref(r); + Cudd_RecursiveDerefZdd(dd, q); + Cudd_RecursiveDerefZdd(dd, tmp); + } + + Cudd_RecursiveDerefZdd(dd, f1); + Cudd_RecursiveDerefZdd(dd, f0); + Cudd_RecursiveDerefZdd(dd, g1); + Cudd_RecursiveDerefZdd(dd, g0); + + cuddCacheInsert2(dd, cuddZddDivideF, f, g, r); + Cudd_Deref(r); + return(r); + +} /* end of cuddZddDivideF */ + + +/**Function******************************************************************** + + Synopsis [Computes the three-way decomposition of f w.r.t. v.] + + Description [Computes the three-way decomposition of function f (represented + by a ZDD) wit respect to variable v. Returns 0 if successful; 1 otherwise.] + + SideEffects [The results are returned in f1, f0, and fd.] + + SeeAlso [cuddZddGetCofactors2] + +******************************************************************************/ +int +cuddZddGetCofactors3( + DdManager * dd, + DdNode * f, + int v, + DdNode ** f1, + DdNode ** f0, + DdNode ** fd) +{ + DdNode *pc, *nc; + DdNode *zero = DD_ZERO(dd); + int top, hv, ht, pv, nv; + int level; + + top = dd->permZ[f->index]; + level = dd->permZ[v]; + hv = level >> 1; + ht = top >> 1; + + if (hv < ht) { + *f1 = zero; + *f0 = zero; + *fd = f; + } + else { + pv = cuddZddGetPosVarIndex(dd, v); + nv = cuddZddGetNegVarIndex(dd, v); + + /* not to create intermediate ZDD node */ + if (cuddZddGetPosVarLevel(dd, v) < cuddZddGetNegVarLevel(dd, v)) { + pc = cuddZddSubset1(dd, f, pv); + if (pc == NULL) + return(1); + Cudd_Ref(pc); + nc = cuddZddSubset0(dd, f, pv); + if (nc == NULL) { + Cudd_RecursiveDerefZdd(dd, pc); + return(1); + } + Cudd_Ref(nc); + + *f1 = cuddZddSubset0(dd, pc, nv); + if (*f1 == NULL) { + Cudd_RecursiveDerefZdd(dd, pc); + Cudd_RecursiveDerefZdd(dd, nc); + return(1); + } + Cudd_Ref(*f1); + *f0 = cuddZddSubset1(dd, nc, nv); + if (*f0 == NULL) { + Cudd_RecursiveDerefZdd(dd, pc); + Cudd_RecursiveDerefZdd(dd, nc); + Cudd_RecursiveDerefZdd(dd, *f1); + return(1); + } + Cudd_Ref(*f0); + + *fd = cuddZddSubset0(dd, nc, nv); + if (*fd == NULL) { + Cudd_RecursiveDerefZdd(dd, pc); + Cudd_RecursiveDerefZdd(dd, nc); + Cudd_RecursiveDerefZdd(dd, *f1); + Cudd_RecursiveDerefZdd(dd, *f0); + return(1); + } + Cudd_Ref(*fd); + } else { + pc = cuddZddSubset1(dd, f, nv); + if (pc == NULL) + return(1); + Cudd_Ref(pc); + nc = cuddZddSubset0(dd, f, nv); + if (nc == NULL) { + Cudd_RecursiveDerefZdd(dd, pc); + return(1); + } + Cudd_Ref(nc); + + *f0 = cuddZddSubset0(dd, pc, pv); + if (*f0 == NULL) { + Cudd_RecursiveDerefZdd(dd, pc); + Cudd_RecursiveDerefZdd(dd, nc); + return(1); + } + Cudd_Ref(*f0); + *f1 = cuddZddSubset1(dd, nc, pv); + if (*f1 == NULL) { + Cudd_RecursiveDerefZdd(dd, pc); + Cudd_RecursiveDerefZdd(dd, nc); + Cudd_RecursiveDerefZdd(dd, *f0); + return(1); + } + Cudd_Ref(*f1); + + *fd = cuddZddSubset0(dd, nc, pv); + if (*fd == NULL) { + Cudd_RecursiveDerefZdd(dd, pc); + Cudd_RecursiveDerefZdd(dd, nc); + Cudd_RecursiveDerefZdd(dd, *f1); + Cudd_RecursiveDerefZdd(dd, *f0); + return(1); + } + Cudd_Ref(*fd); + } + + Cudd_RecursiveDerefZdd(dd, pc); + Cudd_RecursiveDerefZdd(dd, nc); + Cudd_Deref(*f1); + Cudd_Deref(*f0); + Cudd_Deref(*fd); + } + return(0); + +} /* end of cuddZddGetCofactors3 */ + + +/**Function******************************************************************** + + Synopsis [Computes the two-way decomposition of f w.r.t. v.] + + Description [] + + SideEffects [The results are returned in f1 and f0.] + + SeeAlso [cuddZddGetCofactors3] + +******************************************************************************/ +int +cuddZddGetCofactors2( + DdManager * dd, + DdNode * f, + int v, + DdNode ** f1, + DdNode ** f0) +{ + *f1 = cuddZddSubset1(dd, f, v); + if (*f1 == NULL) + return(1); + *f0 = cuddZddSubset0(dd, f, v); + if (*f0 == NULL) { + Cudd_RecursiveDerefZdd(dd, *f1); + return(1); + } + return(0); + +} /* end of cuddZddGetCofactors2 */ + + +/**Function******************************************************************** + + Synopsis [Computes a complement of a ZDD node.] + + Description [Computes the complement of a ZDD node. So far, since we + couldn't find a direct way to get the complement of a ZDD cover, we first + convert a ZDD cover to a BDD, then make the complement of the ZDD cover + from the complement of the BDD node by using ISOP.] + + SideEffects [The result depends on current variable order.] + + SeeAlso [] + +******************************************************************************/ +DdNode * +cuddZddComplement( + DdManager * dd, + DdNode *node) +{ + DdNode *b, *isop, *zdd_I; + + /* Check cache */ + zdd_I = cuddCacheLookup1Zdd(dd, cuddZddComplement, node); + if (zdd_I) + return(zdd_I); + + b = cuddMakeBddFromZddCover(dd, node); + if (!b) + return(NULL); + cuddRef(b); + isop = cuddZddIsop(dd, Cudd_Not(b), Cudd_Not(b), &zdd_I); + if (!isop) { + Cudd_RecursiveDeref(dd, b); + return(NULL); + } + cuddRef(isop); + cuddRef(zdd_I); + Cudd_RecursiveDeref(dd, b); + Cudd_RecursiveDeref(dd, isop); + + cuddCacheInsert1(dd, cuddZddComplement, node, zdd_I); + cuddDeref(zdd_I); + return(zdd_I); +} /* end of cuddZddComplement */ + + +/**Function******************************************************************** + + Synopsis [Returns the index of positive ZDD variable.] + + Description [Returns the index of positive ZDD variable.] + + SideEffects [] + + SeeAlso [] + +******************************************************************************/ +int +cuddZddGetPosVarIndex( + DdManager * dd, + int index) +{ + int pv = (index >> 1) << 1; + return(pv); +} /* end of cuddZddGetPosVarIndex */ + + +/**Function******************************************************************** + + Synopsis [Returns the index of negative ZDD variable.] + + Description [Returns the index of negative ZDD variable.] + + SideEffects [] + + SeeAlso [] + +******************************************************************************/ +int +cuddZddGetNegVarIndex( + DdManager * dd, + int index) +{ + int nv = index | 0x1; + return(nv); +} /* end of cuddZddGetPosVarIndex */ + + +/**Function******************************************************************** + + Synopsis [Returns the level of positive ZDD variable.] + + Description [Returns the level of positive ZDD variable.] + + SideEffects [] + + SeeAlso [] + +******************************************************************************/ +int +cuddZddGetPosVarLevel( + DdManager * dd, + int index) +{ + int pv = cuddZddGetPosVarIndex(dd, index); + return(dd->permZ[pv]); +} /* end of cuddZddGetPosVarLevel */ + + +/**Function******************************************************************** + + Synopsis [Returns the level of negative ZDD variable.] + + Description [Returns the level of negative ZDD variable.] + + SideEffects [] + + SeeAlso [] + +******************************************************************************/ +int +cuddZddGetNegVarLevel( + DdManager * dd, + int index) +{ + int nv = cuddZddGetNegVarIndex(dd, index); + return(dd->permZ[nv]); +} /* end of cuddZddGetNegVarLevel */ +/**CFile*********************************************************************** + + FileName [cuddZddGroup.c] + + PackageName [cudd] + + Synopsis [Functions for ZDD group sifting.] + + Description [External procedures included in this file: +
        +
      • Cudd_MakeZddTreeNode() +
      + Internal procedures included in this file: +
        +
      • cuddZddTreeSifting() +
      + Static procedures included in this module: +
        +
      • zddTreeSiftingAux() +
      • zddCountInternalMtrNodes() +
      • zddReorderChildren() +
      • zddFindNodeHiLo() +
      • zddUniqueCompareGroup() +
      • zddGroupSifting() +
      • zddGroupSiftingAux() +
      • zddGroupSiftingUp() +
      • zddGroupSiftingDown() +
      • zddGroupMove() +
      • zddGroupMoveBackward() +
      • zddGroupSiftingBackward() +
      • zddMergeGroups() +
      ] + + Author [Fabio Somenzi] + + Copyright [Copyright (c) 1995-2012, Regents of the University of Colorado + + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + Neither the name of the University of Colorado nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE.] + +******************************************************************************/ + + +/*---------------------------------------------------------------------------*/ +/* Constant declarations */ +/*---------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------*/ +/* Stucture declarations */ +/*---------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------*/ +/* Type declarations */ +/*---------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------*/ +/* Variable declarations */ +/*---------------------------------------------------------------------------*/ + +//#ifndef lint +//static char rcsid[] DD_UNUSED = "$Id: cuddZddGroup.c,v 1.22 2012/02/05 01:07:19 fabio Exp $"; +//#endif + +static int *entry; +extern int zddTotalNumberSwapping; +#ifdef DD_STATS +static int extsymmcalls; +static int extsymm; +static int secdiffcalls; +static int secdiff; +static int secdiffmisfire; +#endif +#ifdef DD_DEBUG +static int pr = 0; /* flag to enable printing while debugging */ + /* by depositing a 1 into it */ +#endif + +/*---------------------------------------------------------------------------*/ +/* Macro declarations */ +/*---------------------------------------------------------------------------*/ + +/**AutomaticStart*************************************************************/ + +/*---------------------------------------------------------------------------*/ +/* Static function prototypes */ +/*---------------------------------------------------------------------------*/ + +static int zddTreeSiftingAux (DdManager *table, MtrNode *treenode, Cudd_ReorderingType method); +#ifdef DD_STATS +static int zddCountInternalMtrNodes (DdManager *table, MtrNode *treenode); +#endif +static int zddReorderChildren (DdManager *table, MtrNode *treenode, Cudd_ReorderingType method); +static void zddFindNodeHiLo (DdManager *table, MtrNode *treenode, int *lower, int *upper); +static int zddUniqueCompareGroup (int *ptrX, int *ptrY); +static int zddGroupSifting (DdManager *table, int lower, int upper); +static int zddGroupSiftingAux (DdManager *table, int x, int xLow, int xHigh); +static int zddGroupSiftingUp (DdManager *table, int y, int xLow, Move **moves); +static int zddGroupSiftingDown (DdManager *table, int x, int xHigh, Move **moves); +static int zddGroupMove (DdManager *table, int x, int y, Move **moves); +static int zddGroupMoveBackward (DdManager *table, int x, int y); +static int zddGroupSiftingBackward (DdManager *table, Move *moves, int size); +static void zddMergeGroups (DdManager *table, MtrNode *treenode, int low, int high); + +/**AutomaticEnd***************************************************************/ + + +/*---------------------------------------------------------------------------*/ +/* Definition of exported functions */ +/*---------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------*/ +/* Definition of internal functions */ +/*---------------------------------------------------------------------------*/ + + +/**Function******************************************************************** + + Synopsis [Tree sifting algorithm for ZDDs.] + + Description [Tree sifting algorithm for ZDDs. Assumes that a tree + representing a group hierarchy is passed as a parameter. It then + reorders each group in postorder fashion by calling + zddTreeSiftingAux. Assumes that no dead nodes are present. Returns + 1 if successful; 0 otherwise.] + + SideEffects [None] + +******************************************************************************/ +int +cuddZddTreeSifting( + DdManager * table /* DD table */, + Cudd_ReorderingType method /* reordering method for the groups of leaves */) +{ + int i; + int nvars; + int result; + int tempTree; + + /* If no tree is provided we create a temporary one in which all + ** variables are in a single group. After reordering this tree is + ** destroyed. + */ + tempTree = table->treeZ == NULL; + if (tempTree) { + table->treeZ = Mtr_InitGroupTree(0,table->sizeZ); + table->treeZ->index = table->invpermZ[0]; + } + nvars = table->sizeZ; + +#ifdef DD_DEBUG + if (pr > 0 && !tempTree) + (void) fprintf(table->out,"cuddZddTreeSifting:"); + Mtr_PrintGroups(table->treeZ,pr <= 0); +#endif +#if 0 + /* Debugging code. */ + if (table->tree && table->treeZ) { + (void) fprintf(table->out,"\n"); + Mtr_PrintGroups(table->tree, 0); + cuddPrintVarGroups(table,table->tree,0,0); + for (i = 0; i < table->size; i++) { + (void) fprintf(table->out,"%s%d", + (i == 0) ? "" : ",", table->invperm[i]); + } + (void) fprintf(table->out,"\n"); + for (i = 0; i < table->size; i++) { + (void) fprintf(table->out,"%s%d", + (i == 0) ? "" : ",", table->perm[i]); + } + (void) fprintf(table->out,"\n\n"); + Mtr_PrintGroups(table->treeZ,0); + cuddPrintVarGroups(table,table->treeZ,1,0); + for (i = 0; i < table->sizeZ; i++) { + (void) fprintf(table->out,"%s%d", + (i == 0) ? "" : ",", table->invpermZ[i]); + } + (void) fprintf(table->out,"\n"); + for (i = 0; i < table->sizeZ; i++) { + (void) fprintf(table->out,"%s%d", + (i == 0) ? "" : ",", table->permZ[i]); + } + (void) fprintf(table->out,"\n"); + } + /* End of debugging code. */ +#endif +#ifdef DD_STATS + extsymmcalls = 0; + extsymm = 0; + secdiffcalls = 0; + secdiff = 0; + secdiffmisfire = 0; + + (void) fprintf(table->out,"\n"); + if (!tempTree) + (void) fprintf(table->out,"#:IM_NODES %8d: group tree nodes\n", + zddCountInternalMtrNodes(table,table->treeZ)); +#endif + + /* Initialize the group of each subtable to itself. Initially + ** there are no groups. Groups are created according to the tree + ** structure in postorder fashion. + */ + for (i = 0; i < nvars; i++) + table->subtableZ[i].next = i; + + /* Reorder. */ + result = zddTreeSiftingAux(table, table->treeZ, method); + +#ifdef DD_STATS /* print stats */ + if (!tempTree && method == CUDD_REORDER_GROUP_SIFT && + (table->groupcheck == CUDD_GROUP_CHECK7 || + table->groupcheck == CUDD_GROUP_CHECK5)) { + (void) fprintf(table->out,"\nextsymmcalls = %d\n",extsymmcalls); + (void) fprintf(table->out,"extsymm = %d",extsymm); + } + if (!tempTree && method == CUDD_REORDER_GROUP_SIFT && + table->groupcheck == CUDD_GROUP_CHECK7) { + (void) fprintf(table->out,"\nsecdiffcalls = %d\n",secdiffcalls); + (void) fprintf(table->out,"secdiff = %d\n",secdiff); + (void) fprintf(table->out,"secdiffmisfire = %d",secdiffmisfire); + } +#endif + + if (tempTree) + Cudd_FreeZddTree(table); + return(result); + +} /* end of cuddZddTreeSifting */ + + +/*---------------------------------------------------------------------------*/ +/* Definition of static functions */ +/*---------------------------------------------------------------------------*/ + + +/**Function******************************************************************** + + Synopsis [Visits the group tree and reorders each group.] + + Description [Recursively visits the group tree and reorders each + group in postorder fashion. Returns 1 if successful; 0 otherwise.] + + SideEffects [None] + +******************************************************************************/ +static int +zddTreeSiftingAux( + DdManager * table, + MtrNode * treenode, + Cudd_ReorderingType method) +{ + MtrNode *auxnode; + int res; + +#ifdef DD_DEBUG + Mtr_PrintGroups(treenode,1); +#endif + + auxnode = treenode; + while (auxnode != NULL) { + if (auxnode->child != NULL) { + if (!zddTreeSiftingAux(table, auxnode->child, method)) + return(0); + res = zddReorderChildren(table, auxnode, CUDD_REORDER_GROUP_SIFT); + if (res == 0) + return(0); + } else if (auxnode->size > 1) { + if (!zddReorderChildren(table, auxnode, method)) + return(0); + } + auxnode = auxnode->younger; + } + + return(1); + +} /* end of zddTreeSiftingAux */ + + +#ifdef DD_STATS +/**Function******************************************************************** + + Synopsis [Counts the number of internal nodes of the group tree.] + + Description [Counts the number of internal nodes of the group tree. + Returns the count.] + + SideEffects [None] + +******************************************************************************/ +static int +zddCountInternalMtrNodes( + DdManager * table, + MtrNode * treenode) +{ + MtrNode *auxnode; + int count,nodeCount; + + + nodeCount = 0; + auxnode = treenode; + while (auxnode != NULL) { + if (!(MTR_TEST(auxnode,MTR_TERMINAL))) { + nodeCount++; + count = zddCountInternalMtrNodes(table,auxnode->child); + nodeCount += count; + } + auxnode = auxnode->younger; + } + + return(nodeCount); + +} /* end of zddCountInternalMtrNodes */ +#endif + + +/**Function******************************************************************** + + Synopsis [Reorders the children of a group tree node according to + the options.] + + Description [Reorders the children of a group tree node according to + the options. After reordering puts all the variables in the group + and/or its descendents in a single group. This allows hierarchical + reordering. If the variables in the group do not exist yet, simply + does nothing. Returns 1 if successful; 0 otherwise.] + + SideEffects [None] + +******************************************************************************/ +static int +zddReorderChildren( + DdManager * table, + MtrNode * treenode, + Cudd_ReorderingType method) +{ + int lower; + int upper; + int result; + unsigned int initialSize; + + zddFindNodeHiLo(table,treenode,&lower,&upper); + /* If upper == -1 these variables do not exist yet. */ + if (upper == -1) + return(1); + + if (treenode->flags == MTR_FIXED) { + result = 1; + } else { +#ifdef DD_STATS + (void) fprintf(table->out," "); +#endif + switch (method) { + case CUDD_REORDER_RANDOM: + case CUDD_REORDER_RANDOM_PIVOT: + result = cuddZddSwapping(table,lower,upper,method); + break; + case CUDD_REORDER_SIFT: + result = cuddZddSifting(table,lower,upper); + break; + case CUDD_REORDER_SIFT_CONVERGE: + do { + initialSize = table->keysZ; + result = cuddZddSifting(table,lower,upper); + if (initialSize <= table->keysZ) + break; +#ifdef DD_STATS + else + (void) fprintf(table->out,"\n"); +#endif + } while (result != 0); + break; + case CUDD_REORDER_SYMM_SIFT: + result = cuddZddSymmSifting(table,lower,upper); + break; + case CUDD_REORDER_SYMM_SIFT_CONV: + result = cuddZddSymmSiftingConv(table,lower,upper); + break; + case CUDD_REORDER_GROUP_SIFT: + result = zddGroupSifting(table,lower,upper); + break; + case CUDD_REORDER_LINEAR: + result = cuddZddLinearSifting(table,lower,upper); + break; + case CUDD_REORDER_LINEAR_CONVERGE: + do { + initialSize = table->keysZ; + result = cuddZddLinearSifting(table,lower,upper); + if (initialSize <= table->keysZ) + break; +#ifdef DD_STATS + else + (void) fprintf(table->out,"\n"); +#endif + } while (result != 0); + break; + default: + return(0); + } + } + + /* Create a single group for all the variables that were sifted, + ** so that they will be treated as a single block by successive + ** invocations of zddGroupSifting. + */ + zddMergeGroups(table,treenode,lower,upper); + +#ifdef DD_DEBUG + if (pr > 0) (void) fprintf(table->out,"zddReorderChildren:"); +#endif + + return(result); + +} /* end of zddReorderChildren */ + + +/**Function******************************************************************** + + Synopsis [Finds the lower and upper bounds of the group represented + by treenode.] + + Description [Finds the lower and upper bounds of the group represented + by treenode. The high and low fields of treenode are indices. From + those we need to derive the current positions, and find maximum and + minimum.] + + SideEffects [The bounds are returned as side effects.] + + SeeAlso [] + +******************************************************************************/ +static void +zddFindNodeHiLo( + DdManager * table, + MtrNode * treenode, + int * lower, + int * upper) +{ + int low; + int high; + + /* Check whether no variables in this group already exist. + ** If so, return immediately. The calling procedure will know from + ** the values of upper that no reordering is needed. + */ + if ((int) treenode->low >= table->sizeZ) { + *lower = table->sizeZ; + *upper = -1; + return; + } + + *lower = low = (unsigned int) table->permZ[treenode->index]; + high = (int) (low + treenode->size - 1); + + if (high >= table->sizeZ) { + /* This is the case of a partially existing group. The aim is to + ** reorder as many variables as safely possible. If the tree + ** node is terminal, we just reorder the subset of the group + ** that is currently in existence. If the group has + ** subgroups, then we only reorder those subgroups that are + ** fully instantiated. This way we avoid breaking up a group. + */ + MtrNode *auxnode = treenode->child; + if (auxnode == NULL) { + *upper = (unsigned int) table->sizeZ - 1; + } else { + /* Search the subgroup that strands the table->sizeZ line. + ** If the first group starts at 0 and goes past table->sizeZ + ** upper will get -1, thus correctly signaling that no reordering + ** should take place. + */ + while (auxnode != NULL) { + int thisLower = table->permZ[auxnode->low]; + int thisUpper = thisLower + auxnode->size - 1; + if (thisUpper >= table->sizeZ && thisLower < table->sizeZ) + *upper = (unsigned int) thisLower - 1; + auxnode = auxnode->younger; + } + } + } else { + /* Normal case: All the variables of the group exist. */ + *upper = (unsigned int) high; + } + +#ifdef DD_DEBUG + /* Make sure that all variables in group are contiguous. */ + assert(treenode->size >= *upper - *lower + 1); +#endif + + return; + +} /* end of zddFindNodeHiLo */ + + +/**Function******************************************************************** + + Synopsis [Comparison function used by qsort.] + + Description [Comparison function used by qsort to order the variables + according to the number of keys in the subtables. Returns the + difference in number of keys between the two variables being + compared.] + + SideEffects [None] + +******************************************************************************/ +static int +zddUniqueCompareGroup( + int * ptrX, + int * ptrY) +{ +#if 0 + if (entry[*ptrY] == entry[*ptrX]) { + return((*ptrX) - (*ptrY)); + } +#endif + return(entry[*ptrY] - entry[*ptrX]); + +} /* end of zddUniqueCompareGroup */ + + +/**Function******************************************************************** + + Synopsis [Sifts from treenode->low to treenode->high.] + + Description [Sifts from treenode->low to treenode->high. If + croupcheck == CUDD_GROUP_CHECK7, it checks for group creation at the + end of the initial sifting. If a group is created, it is then sifted + again. After sifting one variable, the group that contains it is + dissolved. Returns 1 in case of success; 0 otherwise.] + + SideEffects [None] + +******************************************************************************/ +static int +zddGroupSifting( + DdManager * table, + int lower, + int upper) +{ + int *var; + int i,j,x,xInit; + int nvars; + int classes; + int result; + int *sifted; +#ifdef DD_STATS + unsigned previousSize; +#endif + int xindex; + + nvars = table->sizeZ; + + /* Order variables to sift. */ + entry = NULL; + sifted = NULL; + var = ALLOC(int,nvars); + if (var == NULL) { + table->errorCode = CUDD_MEMORY_OUT; + goto zddGroupSiftingOutOfMem; + } + entry = ALLOC(int,nvars); + if (entry == NULL) { + table->errorCode = CUDD_MEMORY_OUT; + goto zddGroupSiftingOutOfMem; + } + sifted = ALLOC(int,nvars); + if (sifted == NULL) { + table->errorCode = CUDD_MEMORY_OUT; + goto zddGroupSiftingOutOfMem; + } + + /* Here we consider only one representative for each group. */ + for (i = 0, classes = 0; i < nvars; i++) { + sifted[i] = 0; + x = table->permZ[i]; + if ((unsigned) x >= table->subtableZ[x].next) { + entry[i] = table->subtableZ[x].keys; + var[classes] = i; + classes++; + } + } + + qsort((void *)var,classes,sizeof(int),(DD_QSFP)zddUniqueCompareGroup); + + /* Now sift. */ + for (i = 0; i < ddMin(table->siftMaxVar,classes); i++) { + if (zddTotalNumberSwapping >= table->siftMaxSwap) + break; + if (util_cpu_time() - table->startTime > table->timeLimit) { + table->autoDynZ = 0; /* prevent further reordering */ + break; + } + xindex = var[i]; + if (sifted[xindex] == 1) /* variable already sifted as part of group */ + continue; + x = table->permZ[xindex]; /* find current level of this variable */ + if (x < lower || x > upper) + continue; +#ifdef DD_STATS + previousSize = table->keysZ; +#endif +#ifdef DD_DEBUG + /* x is bottom of group */ + assert((unsigned) x >= table->subtableZ[x].next); +#endif + result = zddGroupSiftingAux(table,x,lower,upper); + if (!result) goto zddGroupSiftingOutOfMem; + +#ifdef DD_STATS + if (table->keysZ < previousSize) { + (void) fprintf(table->out,"-"); + } else if (table->keysZ > previousSize) { + (void) fprintf(table->out,"+"); + } else { + (void) fprintf(table->out,"="); + } + fflush(table->out); +#endif + + /* Mark variables in the group just sifted. */ + x = table->permZ[xindex]; + if ((unsigned) x != table->subtableZ[x].next) { + xInit = x; + do { + j = table->invpermZ[x]; + sifted[j] = 1; + x = table->subtableZ[x].next; + } while (x != xInit); + } + +#ifdef DD_DEBUG + if (pr > 0) (void) fprintf(table->out,"zddGroupSifting:"); +#endif + } /* for */ + + FREE(sifted); + FREE(var); + FREE(entry); + + return(1); + + zddGroupSiftingOutOfMem: + if (entry != NULL) FREE(entry); + if (var != NULL) FREE(var); + if (sifted != NULL) FREE(sifted); + + return(0); + +} /* end of zddGroupSifting */ + + +/**Function******************************************************************** + + Synopsis [Sifts one variable up and down until it has taken all + positions. Checks for aggregation.] + + Description [Sifts one variable up and down until it has taken all + positions. Checks for aggregation. There may be at most two sweeps, + even if the group grows. Assumes that x is either an isolated + variable, or it is the bottom of a group. All groups may not have + been found. The variable being moved is returned to the best position + seen during sifting. Returns 1 in case of success; 0 otherwise.] + + SideEffects [None] + +******************************************************************************/ +static int +zddGroupSiftingAux( + DdManager * table, + int x, + int xLow, + int xHigh) +{ + Move *move; + Move *moves; /* list of moves */ + int initialSize; + int result; + + +#ifdef DD_DEBUG + if (pr > 0) (void) fprintf(table->out,"zddGroupSiftingAux from %d to %d\n",xLow,xHigh); + assert((unsigned) x >= table->subtableZ[x].next); /* x is bottom of group */ +#endif + + initialSize = table->keysZ; + moves = NULL; + + if (x == xLow) { /* Sift down */ +#ifdef DD_DEBUG + /* x must be a singleton */ + assert((unsigned) x == table->subtableZ[x].next); +#endif + if (x == xHigh) return(1); /* just one variable */ + + if (!zddGroupSiftingDown(table,x,xHigh,&moves)) + goto zddGroupSiftingAuxOutOfMem; + /* at this point x == xHigh, unless early term */ + + /* move backward and stop at best position */ + result = zddGroupSiftingBackward(table,moves,initialSize); +#ifdef DD_DEBUG + assert(table->keysZ <= (unsigned) initialSize); +#endif + if (!result) goto zddGroupSiftingAuxOutOfMem; + + } else if (cuddZddNextHigh(table,x) > xHigh) { /* Sift up */ +#ifdef DD_DEBUG + /* x is bottom of group */ + assert((unsigned) x >= table->subtableZ[x].next); +#endif + /* Find top of x's group */ + x = table->subtableZ[x].next; + + if (!zddGroupSiftingUp(table,x,xLow,&moves)) + goto zddGroupSiftingAuxOutOfMem; + /* at this point x == xLow, unless early term */ + + /* move backward and stop at best position */ + result = zddGroupSiftingBackward(table,moves,initialSize); +#ifdef DD_DEBUG + assert(table->keysZ <= (unsigned) initialSize); +#endif + if (!result) goto zddGroupSiftingAuxOutOfMem; + + } else if (x - xLow > xHigh - x) { /* must go down first: shorter */ + if (!zddGroupSiftingDown(table,x,xHigh,&moves)) + goto zddGroupSiftingAuxOutOfMem; + /* at this point x == xHigh, unless early term */ + + /* Find top of group */ + if (moves) { + x = moves->y; + } + while ((unsigned) x < table->subtableZ[x].next) + x = table->subtableZ[x].next; + x = table->subtableZ[x].next; +#ifdef DD_DEBUG + /* x should be the top of a group */ + assert((unsigned) x <= table->subtableZ[x].next); +#endif + + if (!zddGroupSiftingUp(table,x,xLow,&moves)) + goto zddGroupSiftingAuxOutOfMem; + + /* move backward and stop at best position */ + result = zddGroupSiftingBackward(table,moves,initialSize); +#ifdef DD_DEBUG + assert(table->keysZ <= (unsigned) initialSize); +#endif + if (!result) goto zddGroupSiftingAuxOutOfMem; + + } else { /* moving up first: shorter */ + /* Find top of x's group */ + x = table->subtableZ[x].next; + + if (!zddGroupSiftingUp(table,x,xLow,&moves)) + goto zddGroupSiftingAuxOutOfMem; + /* at this point x == xHigh, unless early term */ + + if (moves) { + x = moves->x; + } + while ((unsigned) x < table->subtableZ[x].next) + x = table->subtableZ[x].next; +#ifdef DD_DEBUG + /* x is bottom of a group */ + assert((unsigned) x >= table->subtableZ[x].next); +#endif + + if (!zddGroupSiftingDown(table,x,xHigh,&moves)) + goto zddGroupSiftingAuxOutOfMem; + + /* move backward and stop at best position */ + result = zddGroupSiftingBackward(table,moves,initialSize); +#ifdef DD_DEBUG + assert(table->keysZ <= (unsigned) initialSize); +#endif + if (!result) goto zddGroupSiftingAuxOutOfMem; + } + + while (moves != NULL) { + move = moves->next; + cuddDeallocMove(table, moves); + moves = move; + } + + return(1); + + zddGroupSiftingAuxOutOfMem: + while (moves != NULL) { + move = moves->next; + cuddDeallocMove(table, moves); + moves = move; + } + + return(0); + +} /* end of zddGroupSiftingAux */ + + +/**Function******************************************************************** + + Synopsis [Sifts up a variable until either it reaches position xLow + or the size of the DD heap increases too much.] + + Description [Sifts up a variable until either it reaches position + xLow or the size of the DD heap increases too much. Assumes that y is + the top of a group (or a singleton). Checks y for aggregation to the + adjacent variables. Records all the moves that are appended to the + list of moves received as input and returned as a side effect. + Returns 1 in case of success; 0 otherwise.] + + SideEffects [None] + +******************************************************************************/ +static int +zddGroupSiftingUp( + DdManager * table, + int y, + int xLow, + Move ** moves) +{ + Move *move; + int x; + int size; + int gxtop; + int limitSize; + + limitSize = table->keysZ; + + x = cuddZddNextLow(table,y); + while (x >= xLow) { + gxtop = table->subtableZ[x].next; + if (table->subtableZ[x].next == (unsigned) x && + table->subtableZ[y].next == (unsigned) y) { + /* x and y are self groups */ + size = cuddZddSwapInPlace(table,x,y); +#ifdef DD_DEBUG + assert(table->subtableZ[x].next == (unsigned) x); + assert(table->subtableZ[y].next == (unsigned) y); +#endif + if (size == 0) goto zddGroupSiftingUpOutOfMem; + move = (Move *)cuddDynamicAllocNode(table); + if (move == NULL) goto zddGroupSiftingUpOutOfMem; + move->x = x; + move->y = y; + move->flags = MTR_DEFAULT; + move->size = size; + move->next = *moves; + *moves = move; + +#ifdef DD_DEBUG + if (pr > 0) (void) fprintf(table->out,"zddGroupSiftingUp (2 single groups):\n"); +#endif + if ((double) size > (double) limitSize * table->maxGrowth) + return(1); + if (size < limitSize) limitSize = size; + } else { /* group move */ + size = zddGroupMove(table,x,y,moves); + if (size == 0) goto zddGroupSiftingUpOutOfMem; + if ((double) size > (double) limitSize * table->maxGrowth) + return(1); + if (size < limitSize) limitSize = size; + } + y = gxtop; + x = cuddZddNextLow(table,y); + } + + return(1); + + zddGroupSiftingUpOutOfMem: + while (*moves != NULL) { + move = (*moves)->next; + cuddDeallocMove(table, *moves); + *moves = move; + } + return(0); + +} /* end of zddGroupSiftingUp */ + + +/**Function******************************************************************** + + Synopsis [Sifts down a variable until it reaches position xHigh.] + + Description [Sifts down a variable until it reaches position xHigh. + Assumes that x is the bottom of a group (or a singleton). Records + all the moves. Returns 1 in case of success; 0 otherwise.] + + SideEffects [None] + +******************************************************************************/ +static int +zddGroupSiftingDown( + DdManager * table, + int x, + int xHigh, + Move ** moves) +{ + Move *move; + int y; + int size; + int limitSize; + int gybot; + + + /* Initialize R */ + limitSize = size = table->keysZ; + y = cuddZddNextHigh(table,x); + while (y <= xHigh) { + /* Find bottom of y group. */ + gybot = table->subtableZ[y].next; + while (table->subtableZ[gybot].next != (unsigned) y) + gybot = table->subtableZ[gybot].next; + + if (table->subtableZ[x].next == (unsigned) x && + table->subtableZ[y].next == (unsigned) y) { + /* x and y are self groups */ + size = cuddZddSwapInPlace(table,x,y); +#ifdef DD_DEBUG + assert(table->subtableZ[x].next == (unsigned) x); + assert(table->subtableZ[y].next == (unsigned) y); +#endif + if (size == 0) goto zddGroupSiftingDownOutOfMem; + + /* Record move. */ + move = (Move *) cuddDynamicAllocNode(table); + if (move == NULL) goto zddGroupSiftingDownOutOfMem; + move->x = x; + move->y = y; + move->flags = MTR_DEFAULT; + move->size = size; + move->next = *moves; + *moves = move; + +#ifdef DD_DEBUG + if (pr > 0) (void) fprintf(table->out,"zddGroupSiftingDown (2 single groups):\n"); +#endif + if ((double) size > (double) limitSize * table->maxGrowth) + return(1); + if (size < limitSize) limitSize = size; + x = y; + y = cuddZddNextHigh(table,x); + } else { /* Group move */ + size = zddGroupMove(table,x,y,moves); + if (size == 0) goto zddGroupSiftingDownOutOfMem; + if ((double) size > (double) limitSize * table->maxGrowth) + return(1); + if (size < limitSize) limitSize = size; + } + x = gybot; + y = cuddZddNextHigh(table,x); + } + + return(1); + + zddGroupSiftingDownOutOfMem: + while (*moves != NULL) { + move = (*moves)->next; + cuddDeallocMove(table, *moves); + *moves = move; + } + + return(0); + +} /* end of zddGroupSiftingDown */ + + +/**Function******************************************************************** + + Synopsis [Swaps two groups and records the move.] + + Description [Swaps two groups and records the move. Returns the + number of keys in the DD table in case of success; 0 otherwise.] + + SideEffects [None] + +******************************************************************************/ +static int +zddGroupMove( + DdManager * table, + int x, + int y, + Move ** moves) +{ + Move *move; + int size; + int i,j,xtop,xbot,xsize,ytop,ybot,ysize,newxtop; + int swapx,swapy; +#if defined(DD_DEBUG) && defined(DD_VERBOSE) + int initialSize,bestSize; +#endif + +#ifdef DD_DEBUG + /* We assume that x < y */ + assert(x < y); +#endif + /* Find top, bottom, and size for the two groups. */ + xbot = x; + xtop = table->subtableZ[x].next; + xsize = xbot - xtop + 1; + ybot = y; + while ((unsigned) ybot < table->subtableZ[ybot].next) + ybot = table->subtableZ[ybot].next; + ytop = y; + ysize = ybot - ytop + 1; + +#if defined(DD_DEBUG) && defined(DD_VERBOSE) + initialSize = bestSize = table->keysZ; +#endif + /* Sift the variables of the second group up through the first group */ + for (i = 1; i <= ysize; i++) { + for (j = 1; j <= xsize; j++) { + size = cuddZddSwapInPlace(table,x,y); + if (size == 0) goto zddGroupMoveOutOfMem; +#if defined(DD_DEBUG) && defined(DD_VERBOSE) + if (size < bestSize) + bestSize = size; +#endif + swapx = x; swapy = y; + y = x; + x = cuddZddNextLow(table,y); + } + y = ytop + i; + x = cuddZddNextLow(table,y); + } +#if defined(DD_DEBUG) && defined(DD_VERBOSE) + if ((bestSize < initialSize) && (bestSize < size)) + (void) fprintf(table->out,"Missed local minimum: initialSize:%d bestSize:%d finalSize:%d\n",initialSize,bestSize,size); +#endif + + /* fix groups */ + y = xtop; /* ytop is now where xtop used to be */ + for (i = 0; i < ysize - 1; i++) { + table->subtableZ[y].next = cuddZddNextHigh(table,y); + y = cuddZddNextHigh(table,y); + } + table->subtableZ[y].next = xtop; /* y is bottom of its group, join */ + /* it to top of its group */ + x = cuddZddNextHigh(table,y); + newxtop = x; + for (i = 0; i < xsize - 1; i++) { + table->subtableZ[x].next = cuddZddNextHigh(table,x); + x = cuddZddNextHigh(table,x); + } + table->subtableZ[x].next = newxtop; /* x is bottom of its group, join */ + /* it to top of its group */ +#ifdef DD_DEBUG + if (pr > 0) (void) fprintf(table->out,"zddGroupMove:\n"); +#endif + + /* Store group move */ + move = (Move *) cuddDynamicAllocNode(table); + if (move == NULL) goto zddGroupMoveOutOfMem; + move->x = swapx; + move->y = swapy; + move->flags = MTR_DEFAULT; + move->size = table->keysZ; + move->next = *moves; + *moves = move; + + return(table->keysZ); + + zddGroupMoveOutOfMem: + while (*moves != NULL) { + move = (*moves)->next; + cuddDeallocMove(table, *moves); + *moves = move; + } + return(0); + +} /* end of zddGroupMove */ + + +/**Function******************************************************************** + + Synopsis [Undoes the swap two groups.] + + Description [Undoes the swap two groups. Returns 1 in case of + success; 0 otherwise.] + + SideEffects [None] + +******************************************************************************/ +static int +zddGroupMoveBackward( + DdManager * table, + int x, + int y) +{ + int size; + int i,j,xtop,xbot,xsize,ytop,ybot,ysize,newxtop; + + +#ifdef DD_DEBUG + /* We assume that x < y */ + assert(x < y); +#endif + + /* Find top, bottom, and size for the two groups. */ + xbot = x; + xtop = table->subtableZ[x].next; + xsize = xbot - xtop + 1; + ybot = y; + while ((unsigned) ybot < table->subtableZ[ybot].next) + ybot = table->subtableZ[ybot].next; + ytop = y; + ysize = ybot - ytop + 1; + + /* Sift the variables of the second group up through the first group */ + for (i = 1; i <= ysize; i++) { + for (j = 1; j <= xsize; j++) { + size = cuddZddSwapInPlace(table,x,y); + if (size == 0) + return(0); + y = x; + x = cuddZddNextLow(table,y); + } + y = ytop + i; + x = cuddZddNextLow(table,y); + } + + /* fix groups */ + y = xtop; + for (i = 0; i < ysize - 1; i++) { + table->subtableZ[y].next = cuddZddNextHigh(table,y); + y = cuddZddNextHigh(table,y); + } + table->subtableZ[y].next = xtop; /* y is bottom of its group, join */ + /* to its top */ + x = cuddZddNextHigh(table,y); + newxtop = x; + for (i = 0; i < xsize - 1; i++) { + table->subtableZ[x].next = cuddZddNextHigh(table,x); + x = cuddZddNextHigh(table,x); + } + table->subtableZ[x].next = newxtop; /* x is bottom of its group, join */ + /* to its top */ +#ifdef DD_DEBUG + if (pr > 0) (void) fprintf(table->out,"zddGroupMoveBackward:\n"); +#endif + + return(1); + +} /* end of zddGroupMoveBackward */ + + +/**Function******************************************************************** + + Synopsis [Determines the best position for a variables and returns + it there.] + + Description [Determines the best position for a variables and returns + it there. Returns 1 in case of success; 0 otherwise.] + + SideEffects [None] + +******************************************************************************/ +static int +zddGroupSiftingBackward( + DdManager * table, + Move * moves, + int size) +{ + Move *move; + int res; + + + for (move = moves; move != NULL; move = move->next) { + if (move->size < size) { + size = move->size; + } + } + + for (move = moves; move != NULL; move = move->next) { + if (move->size == size) return(1); + if ((table->subtableZ[move->x].next == move->x) && + (table->subtableZ[move->y].next == move->y)) { + res = cuddZddSwapInPlace(table,(int)move->x,(int)move->y); + if (!res) return(0); +#ifdef DD_DEBUG + if (pr > 0) (void) fprintf(table->out,"zddGroupSiftingBackward:\n"); + assert(table->subtableZ[move->x].next == move->x); + assert(table->subtableZ[move->y].next == move->y); +#endif + } else { /* Group move necessary */ + res = zddGroupMoveBackward(table,(int)move->x,(int)move->y); + if (!res) return(0); + } + } + + return(1); + +} /* end of zddGroupSiftingBackward */ + + +/**Function******************************************************************** + + Synopsis [Merges groups in the DD table.] + + Description [Creates a single group from low to high and adjusts the + idex field of the tree node.] + + SideEffects [None] + +******************************************************************************/ +static void +zddMergeGroups( + DdManager * table, + MtrNode * treenode, + int low, + int high) +{ + int i; + MtrNode *auxnode; + int saveindex; + int newindex; + + /* Merge all variables from low to high in one group, unless + ** this is the topmost group. In such a case we do not merge lest + ** we lose the symmetry information. */ + if (treenode != table->treeZ) { + for (i = low; i < high; i++) + table->subtableZ[i].next = i+1; + table->subtableZ[high].next = low; + } + + /* Adjust the index fields of the tree nodes. If a node is the + ** first child of its parent, then the parent may also need adjustment. */ + saveindex = treenode->index; + newindex = table->invpermZ[low]; + auxnode = treenode; + do { + auxnode->index = newindex; + if (auxnode->parent == NULL || + (int) auxnode->parent->index != saveindex) + break; + auxnode = auxnode->parent; + } while (1); + return; + +} /* end of zddMergeGroups */ +/**CFile*********************************************************************** + + FileName [cuddZddIsop.c] + + PackageName [cudd] + + Synopsis [Functions to find irredundant SOP covers as ZDDs from BDDs.] + + Description [External procedures included in this module: +
        +
      • Cudd_bddIsop() +
      • Cudd_zddIsop() +
      • Cudd_MakeBddFromZddCover() +
      + Internal procedures included in this module: +
        +
      • cuddBddIsop() +
      • cuddZddIsop() +
      • cuddMakeBddFromZddCover() +
      + Static procedures included in this module: +
        +
      + ] + + SeeAlso [] + + Author [In-Ho Moon] + + Copyright [Copyright (c) 1995-2012, Regents of the University of Colorado + + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + Neither the name of the University of Colorado nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE.] + +******************************************************************************/ + + +/*---------------------------------------------------------------------------*/ +/* Constant declarations */ +/*---------------------------------------------------------------------------*/ + + +/*---------------------------------------------------------------------------*/ +/* Stucture declarations */ +/*---------------------------------------------------------------------------*/ + + +/*---------------------------------------------------------------------------*/ +/* Type declarations */ +/*---------------------------------------------------------------------------*/ + + +/*---------------------------------------------------------------------------*/ +/* Variable declarations */ +/*---------------------------------------------------------------------------*/ + +//#ifndef lint +//static char rcsid[] DD_UNUSED = "$Id: cuddZddIsop.c,v 1.22 2012/02/05 01:07:19 fabio Exp $"; +//#endif + +/*---------------------------------------------------------------------------*/ +/* Macro declarations */ +/*---------------------------------------------------------------------------*/ + + +/**AutomaticStart*************************************************************/ + +/*---------------------------------------------------------------------------*/ +/* Static function prototypes */ +/*---------------------------------------------------------------------------*/ + + +/**AutomaticEnd***************************************************************/ + +/*---------------------------------------------------------------------------*/ +/* Definition of exported functions */ +/*---------------------------------------------------------------------------*/ + + +/*---------------------------------------------------------------------------*/ +/* Definition of internal functions */ +/*---------------------------------------------------------------------------*/ + + +/**Function******************************************************************** + + Synopsis [Performs the recursive step of Cudd_zddIsop.] + + Description [] + + SideEffects [None] + + SeeAlso [Cudd_zddIsop] + +******************************************************************************/ +DdNode * +cuddZddIsop( + DdManager * dd, + DdNode * L, + DdNode * U, + DdNode ** zdd_I) +{ + DdNode *one = DD_ONE(dd); + DdNode *zero = Cudd_Not(one); + DdNode *zdd_one = DD_ONE(dd); + DdNode *zdd_zero = DD_ZERO(dd); + int v, top_l, top_u; + DdNode *Lsub0, *Usub0, *Lsub1, *Usub1, *Ld, *Ud; + DdNode *Lsuper0, *Usuper0, *Lsuper1, *Usuper1; + DdNode *Isub0, *Isub1, *Id; + DdNode *zdd_Isub0, *zdd_Isub1, *zdd_Id; + DdNode *x; + DdNode *term0, *term1, *sum; + DdNode *Lv, *Uv, *Lnv, *Unv; + DdNode *r, *y, *z; + int index; + DD_CTFP cacheOp; + + statLine(dd); + if (L == zero) { + *zdd_I = zdd_zero; + return(zero); + } + if (U == one) { + *zdd_I = zdd_one; + return(one); + } + + if (U == zero || L == one) { + printf("*** ERROR : illegal condition for ISOP (U < L).\n"); + exit(1); + } + + /* Check the cache. We store two results for each recursive call. + ** One is the BDD, and the other is the ZDD. Both are needed. + ** Hence we need a double hit in the cache to terminate the + ** recursion. Clearly, collisions may evict only one of the two + ** results. */ + cacheOp = (DD_CTFP) cuddZddIsop; + r = cuddCacheLookup2(dd, cuddBddIsop, L, U); + if (r) { + *zdd_I = cuddCacheLookup2Zdd(dd, cacheOp, L, U); + if (*zdd_I) + return(r); + else { + /* The BDD result may have been dead. In that case + ** cuddCacheLookup2 would have called cuddReclaim, + ** whose effects we now have to undo. */ + cuddRef(r); + Cudd_RecursiveDeref(dd, r); + } + } + + top_l = dd->perm[Cudd_Regular(L)->index]; + top_u = dd->perm[Cudd_Regular(U)->index]; + v = ddMin(top_l, top_u); + + /* Compute cofactors. */ + if (top_l == v) { + index = Cudd_Regular(L)->index; + Lv = Cudd_T(L); + Lnv = Cudd_E(L); + if (Cudd_IsComplement(L)) { + Lv = Cudd_Not(Lv); + Lnv = Cudd_Not(Lnv); + } + } + else { + index = Cudd_Regular(U)->index; + Lv = Lnv = L; + } + + if (top_u == v) { + Uv = Cudd_T(U); + Unv = Cudd_E(U); + if (Cudd_IsComplement(U)) { + Uv = Cudd_Not(Uv); + Unv = Cudd_Not(Unv); + } + } + else { + Uv = Unv = U; + } + + Lsub0 = cuddBddAndRecur(dd, Lnv, Cudd_Not(Uv)); + if (Lsub0 == NULL) + return(NULL); + Cudd_Ref(Lsub0); + Usub0 = Unv; + Lsub1 = cuddBddAndRecur(dd, Lv, Cudd_Not(Unv)); + if (Lsub1 == NULL) { + Cudd_RecursiveDeref(dd, Lsub0); + return(NULL); + } + Cudd_Ref(Lsub1); + Usub1 = Uv; + + Isub0 = cuddZddIsop(dd, Lsub0, Usub0, &zdd_Isub0); + if (Isub0 == NULL) { + Cudd_RecursiveDeref(dd, Lsub0); + Cudd_RecursiveDeref(dd, Lsub1); + return(NULL); + } + /* + if ((!cuddIsConstant(Cudd_Regular(Isub0))) && + (Cudd_Regular(Isub0)->index != zdd_Isub0->index / 2 || + dd->permZ[index * 2] > dd->permZ[zdd_Isub0->index])) { + printf("*** ERROR : illegal permutation in ZDD. ***\n"); + } + */ + Cudd_Ref(Isub0); + Cudd_Ref(zdd_Isub0); + Isub1 = cuddZddIsop(dd, Lsub1, Usub1, &zdd_Isub1); + if (Isub1 == NULL) { + Cudd_RecursiveDeref(dd, Lsub0); + Cudd_RecursiveDeref(dd, Lsub1); + Cudd_RecursiveDeref(dd, Isub0); + Cudd_RecursiveDerefZdd(dd, zdd_Isub0); + return(NULL); + } + /* + if ((!cuddIsConstant(Cudd_Regular(Isub1))) && + (Cudd_Regular(Isub1)->index != zdd_Isub1->index / 2 || + dd->permZ[index * 2] > dd->permZ[zdd_Isub1->index])) { + printf("*** ERROR : illegal permutation in ZDD. ***\n"); + } + */ + Cudd_Ref(Isub1); + Cudd_Ref(zdd_Isub1); + Cudd_RecursiveDeref(dd, Lsub0); + Cudd_RecursiveDeref(dd, Lsub1); + + Lsuper0 = cuddBddAndRecur(dd, Lnv, Cudd_Not(Isub0)); + if (Lsuper0 == NULL) { + Cudd_RecursiveDeref(dd, Isub0); + Cudd_RecursiveDerefZdd(dd, zdd_Isub0); + Cudd_RecursiveDeref(dd, Isub1); + Cudd_RecursiveDerefZdd(dd, zdd_Isub1); + return(NULL); + } + Cudd_Ref(Lsuper0); + Lsuper1 = cuddBddAndRecur(dd, Lv, Cudd_Not(Isub1)); + if (Lsuper1 == NULL) { + Cudd_RecursiveDeref(dd, Isub0); + Cudd_RecursiveDerefZdd(dd, zdd_Isub0); + Cudd_RecursiveDeref(dd, Isub1); + Cudd_RecursiveDerefZdd(dd, zdd_Isub1); + Cudd_RecursiveDeref(dd, Lsuper0); + return(NULL); + } + Cudd_Ref(Lsuper1); + Usuper0 = Unv; + Usuper1 = Uv; + + /* Ld = Lsuper0 + Lsuper1 */ + Ld = cuddBddAndRecur(dd, Cudd_Not(Lsuper0), Cudd_Not(Lsuper1)); + if (Ld == NULL) { + Cudd_RecursiveDeref(dd, Isub0); + Cudd_RecursiveDerefZdd(dd, zdd_Isub0); + Cudd_RecursiveDeref(dd, Isub1); + Cudd_RecursiveDerefZdd(dd, zdd_Isub1); + Cudd_RecursiveDeref(dd, Lsuper0); + Cudd_RecursiveDeref(dd, Lsuper1); + return(NULL); + } + Ld = Cudd_Not(Ld); + Cudd_Ref(Ld); + /* Ud = Usuper0 * Usuper1 */ + Ud = cuddBddAndRecur(dd, Usuper0, Usuper1); + if (Ud == NULL) { + Cudd_RecursiveDeref(dd, Isub0); + Cudd_RecursiveDerefZdd(dd, zdd_Isub0); + Cudd_RecursiveDeref(dd, Isub1); + Cudd_RecursiveDerefZdd(dd, zdd_Isub1); + Cudd_RecursiveDeref(dd, Lsuper0); + Cudd_RecursiveDeref(dd, Lsuper1); + Cudd_RecursiveDeref(dd, Ld); + return(NULL); + } + Cudd_Ref(Ud); + Cudd_RecursiveDeref(dd, Lsuper0); + Cudd_RecursiveDeref(dd, Lsuper1); + + Id = cuddZddIsop(dd, Ld, Ud, &zdd_Id); + if (Id == NULL) { + Cudd_RecursiveDeref(dd, Isub0); + Cudd_RecursiveDerefZdd(dd, zdd_Isub0); + Cudd_RecursiveDeref(dd, Isub1); + Cudd_RecursiveDerefZdd(dd, zdd_Isub1); + Cudd_RecursiveDeref(dd, Ld); + Cudd_RecursiveDeref(dd, Ud); + return(NULL); + } + /* + if ((!cuddIsConstant(Cudd_Regular(Id))) && + (Cudd_Regular(Id)->index != zdd_Id->index / 2 || + dd->permZ[index * 2] > dd->permZ[zdd_Id->index])) { + printf("*** ERROR : illegal permutation in ZDD. ***\n"); + } + */ + Cudd_Ref(Id); + Cudd_Ref(zdd_Id); + Cudd_RecursiveDeref(dd, Ld); + Cudd_RecursiveDeref(dd, Ud); + + x = cuddUniqueInter(dd, index, one, zero); + if (x == NULL) { + Cudd_RecursiveDeref(dd, Isub0); + Cudd_RecursiveDerefZdd(dd, zdd_Isub0); + Cudd_RecursiveDeref(dd, Isub1); + Cudd_RecursiveDerefZdd(dd, zdd_Isub1); + Cudd_RecursiveDeref(dd, Id); + Cudd_RecursiveDerefZdd(dd, zdd_Id); + return(NULL); + } + Cudd_Ref(x); + /* term0 = x * Isub0 */ + term0 = cuddBddAndRecur(dd, Cudd_Not(x), Isub0); + if (term0 == NULL) { + Cudd_RecursiveDeref(dd, Isub0); + Cudd_RecursiveDerefZdd(dd, zdd_Isub0); + Cudd_RecursiveDeref(dd, Isub1); + Cudd_RecursiveDerefZdd(dd, zdd_Isub1); + Cudd_RecursiveDeref(dd, Id); + Cudd_RecursiveDerefZdd(dd, zdd_Id); + Cudd_RecursiveDeref(dd, x); + return(NULL); + } + Cudd_Ref(term0); + Cudd_RecursiveDeref(dd, Isub0); + /* term1 = x * Isub1 */ + term1 = cuddBddAndRecur(dd, x, Isub1); + if (term1 == NULL) { + Cudd_RecursiveDerefZdd(dd, zdd_Isub0); + Cudd_RecursiveDeref(dd, Isub1); + Cudd_RecursiveDerefZdd(dd, zdd_Isub1); + Cudd_RecursiveDeref(dd, Id); + Cudd_RecursiveDerefZdd(dd, zdd_Id); + Cudd_RecursiveDeref(dd, x); + Cudd_RecursiveDeref(dd, term0); + return(NULL); + } + Cudd_Ref(term1); + Cudd_RecursiveDeref(dd, x); + Cudd_RecursiveDeref(dd, Isub1); + /* sum = term0 + term1 */ + sum = cuddBddAndRecur(dd, Cudd_Not(term0), Cudd_Not(term1)); + if (sum == NULL) { + Cudd_RecursiveDerefZdd(dd, zdd_Isub0); + Cudd_RecursiveDerefZdd(dd, zdd_Isub1); + Cudd_RecursiveDeref(dd, Id); + Cudd_RecursiveDerefZdd(dd, zdd_Id); + Cudd_RecursiveDeref(dd, term0); + Cudd_RecursiveDeref(dd, term1); + return(NULL); + } + sum = Cudd_Not(sum); + Cudd_Ref(sum); + Cudd_RecursiveDeref(dd, term0); + Cudd_RecursiveDeref(dd, term1); + /* r = sum + Id */ + r = cuddBddAndRecur(dd, Cudd_Not(sum), Cudd_Not(Id)); + r = Cudd_NotCond(r, r != NULL); + if (r == NULL) { + Cudd_RecursiveDerefZdd(dd, zdd_Isub0); + Cudd_RecursiveDerefZdd(dd, zdd_Isub1); + Cudd_RecursiveDeref(dd, Id); + Cudd_RecursiveDerefZdd(dd, zdd_Id); + Cudd_RecursiveDeref(dd, sum); + return(NULL); + } + Cudd_Ref(r); + Cudd_RecursiveDeref(dd, sum); + Cudd_RecursiveDeref(dd, Id); + + if (zdd_Isub0 != zdd_zero) { + z = cuddZddGetNodeIVO(dd, index * 2 + 1, zdd_Isub0, zdd_Id); + if (z == NULL) { + Cudd_RecursiveDerefZdd(dd, zdd_Isub0); + Cudd_RecursiveDerefZdd(dd, zdd_Isub1); + Cudd_RecursiveDerefZdd(dd, zdd_Id); + Cudd_RecursiveDeref(dd, r); + return(NULL); + } + } + else { + z = zdd_Id; + } + Cudd_Ref(z); + if (zdd_Isub1 != zdd_zero) { + y = cuddZddGetNodeIVO(dd, index * 2, zdd_Isub1, z); + if (y == NULL) { + Cudd_RecursiveDerefZdd(dd, zdd_Isub0); + Cudd_RecursiveDerefZdd(dd, zdd_Isub1); + Cudd_RecursiveDerefZdd(dd, zdd_Id); + Cudd_RecursiveDeref(dd, r); + Cudd_RecursiveDerefZdd(dd, z); + return(NULL); + } + } + else + y = z; + Cudd_Ref(y); + + Cudd_RecursiveDerefZdd(dd, zdd_Isub0); + Cudd_RecursiveDerefZdd(dd, zdd_Isub1); + Cudd_RecursiveDerefZdd(dd, zdd_Id); + Cudd_RecursiveDerefZdd(dd, z); + + cuddCacheInsert2(dd, cuddBddIsop, L, U, r); + cuddCacheInsert2(dd, cacheOp, L, U, y); + + Cudd_Deref(r); + Cudd_Deref(y); + *zdd_I = y; + /* + if (Cudd_Regular(r)->index != y->index / 2) { + printf("*** ERROR : mismatch in indices between BDD and ZDD. ***\n"); + } + */ + return(r); + +} /* end of cuddZddIsop */ + + +/**Function******************************************************************** + + Synopsis [Performs the recursive step of Cudd_bddIsop.] + + Description [] + + SideEffects [None] + + SeeAlso [Cudd_bddIsop] + +******************************************************************************/ +DdNode * +cuddBddIsop( + DdManager * dd, + DdNode * L, + DdNode * U) +{ + DdNode *one = DD_ONE(dd); + DdNode *zero = Cudd_Not(one); + int v, top_l, top_u; + DdNode *Lsub0, *Usub0, *Lsub1, *Usub1, *Ld, *Ud; + DdNode *Lsuper0, *Usuper0, *Lsuper1, *Usuper1; + DdNode *Isub0, *Isub1, *Id; + DdNode *x; + DdNode *term0, *term1, *sum; + DdNode *Lv, *Uv, *Lnv, *Unv; + DdNode *r; + int index; + + statLine(dd); + if (L == zero) + return(zero); + if (U == one) + return(one); + + /* Check cache */ + r = cuddCacheLookup2(dd, cuddBddIsop, L, U); + if (r) + return(r); + + top_l = dd->perm[Cudd_Regular(L)->index]; + top_u = dd->perm[Cudd_Regular(U)->index]; + v = ddMin(top_l, top_u); + + /* Compute cofactors */ + if (top_l == v) { + index = Cudd_Regular(L)->index; + Lv = Cudd_T(L); + Lnv = Cudd_E(L); + if (Cudd_IsComplement(L)) { + Lv = Cudd_Not(Lv); + Lnv = Cudd_Not(Lnv); + } + } + else { + index = Cudd_Regular(U)->index; + Lv = Lnv = L; + } + + if (top_u == v) { + Uv = Cudd_T(U); + Unv = Cudd_E(U); + if (Cudd_IsComplement(U)) { + Uv = Cudd_Not(Uv); + Unv = Cudd_Not(Unv); + } + } + else { + Uv = Unv = U; + } + + Lsub0 = cuddBddAndRecur(dd, Lnv, Cudd_Not(Uv)); + if (Lsub0 == NULL) + return(NULL); + Cudd_Ref(Lsub0); + Usub0 = Unv; + Lsub1 = cuddBddAndRecur(dd, Lv, Cudd_Not(Unv)); + if (Lsub1 == NULL) { + Cudd_RecursiveDeref(dd, Lsub0); + return(NULL); + } + Cudd_Ref(Lsub1); + Usub1 = Uv; + + Isub0 = cuddBddIsop(dd, Lsub0, Usub0); + if (Isub0 == NULL) { + Cudd_RecursiveDeref(dd, Lsub0); + Cudd_RecursiveDeref(dd, Lsub1); + return(NULL); + } + Cudd_Ref(Isub0); + Isub1 = cuddBddIsop(dd, Lsub1, Usub1); + if (Isub1 == NULL) { + Cudd_RecursiveDeref(dd, Lsub0); + Cudd_RecursiveDeref(dd, Lsub1); + Cudd_RecursiveDeref(dd, Isub0); + return(NULL); + } + Cudd_Ref(Isub1); + Cudd_RecursiveDeref(dd, Lsub0); + Cudd_RecursiveDeref(dd, Lsub1); + + Lsuper0 = cuddBddAndRecur(dd, Lnv, Cudd_Not(Isub0)); + if (Lsuper0 == NULL) { + Cudd_RecursiveDeref(dd, Isub0); + Cudd_RecursiveDeref(dd, Isub1); + return(NULL); + } + Cudd_Ref(Lsuper0); + Lsuper1 = cuddBddAndRecur(dd, Lv, Cudd_Not(Isub1)); + if (Lsuper1 == NULL) { + Cudd_RecursiveDeref(dd, Isub0); + Cudd_RecursiveDeref(dd, Isub1); + Cudd_RecursiveDeref(dd, Lsuper0); + return(NULL); + } + Cudd_Ref(Lsuper1); + Usuper0 = Unv; + Usuper1 = Uv; + + /* Ld = Lsuper0 + Lsuper1 */ + Ld = cuddBddAndRecur(dd, Cudd_Not(Lsuper0), Cudd_Not(Lsuper1)); + Ld = Cudd_NotCond(Ld, Ld != NULL); + if (Ld == NULL) { + Cudd_RecursiveDeref(dd, Isub0); + Cudd_RecursiveDeref(dd, Isub1); + Cudd_RecursiveDeref(dd, Lsuper0); + Cudd_RecursiveDeref(dd, Lsuper1); + return(NULL); + } + Cudd_Ref(Ld); + Ud = cuddBddAndRecur(dd, Usuper0, Usuper1); + if (Ud == NULL) { + Cudd_RecursiveDeref(dd, Isub0); + Cudd_RecursiveDeref(dd, Isub1); + Cudd_RecursiveDeref(dd, Lsuper0); + Cudd_RecursiveDeref(dd, Lsuper1); + Cudd_RecursiveDeref(dd, Ld); + return(NULL); + } + Cudd_Ref(Ud); + Cudd_RecursiveDeref(dd, Lsuper0); + Cudd_RecursiveDeref(dd, Lsuper1); + + Id = cuddBddIsop(dd, Ld, Ud); + if (Id == NULL) { + Cudd_RecursiveDeref(dd, Isub0); + Cudd_RecursiveDeref(dd, Isub1); + Cudd_RecursiveDeref(dd, Ld); + Cudd_RecursiveDeref(dd, Ud); + return(NULL); + } + Cudd_Ref(Id); + Cudd_RecursiveDeref(dd, Ld); + Cudd_RecursiveDeref(dd, Ud); + + x = cuddUniqueInter(dd, index, one, zero); + if (x == NULL) { + Cudd_RecursiveDeref(dd, Isub0); + Cudd_RecursiveDeref(dd, Isub1); + Cudd_RecursiveDeref(dd, Id); + return(NULL); + } + Cudd_Ref(x); + term0 = cuddBddAndRecur(dd, Cudd_Not(x), Isub0); + if (term0 == NULL) { + Cudd_RecursiveDeref(dd, Isub0); + Cudd_RecursiveDeref(dd, Isub1); + Cudd_RecursiveDeref(dd, Id); + Cudd_RecursiveDeref(dd, x); + return(NULL); + } + Cudd_Ref(term0); + Cudd_RecursiveDeref(dd, Isub0); + term1 = cuddBddAndRecur(dd, x, Isub1); + if (term1 == NULL) { + Cudd_RecursiveDeref(dd, Isub1); + Cudd_RecursiveDeref(dd, Id); + Cudd_RecursiveDeref(dd, x); + Cudd_RecursiveDeref(dd, term0); + return(NULL); + } + Cudd_Ref(term1); + Cudd_RecursiveDeref(dd, x); + Cudd_RecursiveDeref(dd, Isub1); + /* sum = term0 + term1 */ + sum = cuddBddAndRecur(dd, Cudd_Not(term0), Cudd_Not(term1)); + sum = Cudd_NotCond(sum, sum != NULL); + if (sum == NULL) { + Cudd_RecursiveDeref(dd, Id); + Cudd_RecursiveDeref(dd, term0); + Cudd_RecursiveDeref(dd, term1); + return(NULL); + } + Cudd_Ref(sum); + Cudd_RecursiveDeref(dd, term0); + Cudd_RecursiveDeref(dd, term1); + /* r = sum + Id */ + r = cuddBddAndRecur(dd, Cudd_Not(sum), Cudd_Not(Id)); + r = Cudd_NotCond(r, r != NULL); + if (r == NULL) { + Cudd_RecursiveDeref(dd, Id); + Cudd_RecursiveDeref(dd, sum); + return(NULL); + } + Cudd_Ref(r); + Cudd_RecursiveDeref(dd, sum); + Cudd_RecursiveDeref(dd, Id); + + cuddCacheInsert2(dd, cuddBddIsop, L, U, r); + + Cudd_Deref(r); + return(r); + +} /* end of cuddBddIsop */ + + +/**Function******************************************************************** + + Synopsis [Converts a ZDD cover to a BDD.] + + Description [Converts a ZDD cover to a BDD. If successful, it returns + a BDD node, otherwise it returns NULL. It is a recursive algorithm + that works as follows. First it computes 3 cofactors of a ZDD cover: + f1, f0 and fd. Second, it compute BDDs (b1, b0 and bd) of f1, f0 and fd. + Third, it computes T=b1+bd and E=b0+bd. Fourth, it computes ITE(v,T,E) where + v is the variable which has the index of the top node of the ZDD cover. + In this case, since the index of v can be larger than either the one of T + or the one of E, cuddUniqueInterIVO is called, where IVO stands for + independent from variable ordering.] + + SideEffects [] + + SeeAlso [Cudd_MakeBddFromZddCover] + +******************************************************************************/ +DdNode * +cuddMakeBddFromZddCover( + DdManager * dd, + DdNode * node) +{ + DdNode *neW; + int v; + DdNode *f1, *f0, *fd; + DdNode *b1, *b0, *bd; + DdNode *T, *E; + + statLine(dd); + if (node == dd->one) + return(dd->one); + if (node == dd->zero) + return(Cudd_Not(dd->one)); + + /* Check cache */ + neW = cuddCacheLookup1(dd, cuddMakeBddFromZddCover, node); + if (neW) + return(neW); + + v = Cudd_Regular(node)->index; /* either yi or zi */ + if (cuddZddGetCofactors3(dd, node, v, &f1, &f0, &fd)) return(NULL); + Cudd_Ref(f1); + Cudd_Ref(f0); + Cudd_Ref(fd); + + b1 = cuddMakeBddFromZddCover(dd, f1); + if (!b1) { + Cudd_RecursiveDerefZdd(dd, f1); + Cudd_RecursiveDerefZdd(dd, f0); + Cudd_RecursiveDerefZdd(dd, fd); + return(NULL); + } + Cudd_Ref(b1); + b0 = cuddMakeBddFromZddCover(dd, f0); + if (!b0) { + Cudd_RecursiveDerefZdd(dd, f1); + Cudd_RecursiveDerefZdd(dd, f0); + Cudd_RecursiveDerefZdd(dd, fd); + Cudd_RecursiveDeref(dd, b1); + return(NULL); + } + Cudd_Ref(b0); + Cudd_RecursiveDerefZdd(dd, f1); + Cudd_RecursiveDerefZdd(dd, f0); + if (fd != dd->zero) { + bd = cuddMakeBddFromZddCover(dd, fd); + if (!bd) { + Cudd_RecursiveDerefZdd(dd, fd); + Cudd_RecursiveDeref(dd, b1); + Cudd_RecursiveDeref(dd, b0); + return(NULL); + } + Cudd_Ref(bd); + Cudd_RecursiveDerefZdd(dd, fd); + + T = cuddBddAndRecur(dd, Cudd_Not(b1), Cudd_Not(bd)); + if (!T) { + Cudd_RecursiveDeref(dd, b1); + Cudd_RecursiveDeref(dd, b0); + Cudd_RecursiveDeref(dd, bd); + return(NULL); + } + T = Cudd_NotCond(T, T != NULL); + Cudd_Ref(T); + Cudd_RecursiveDeref(dd, b1); + E = cuddBddAndRecur(dd, Cudd_Not(b0), Cudd_Not(bd)); + if (!E) { + Cudd_RecursiveDeref(dd, b0); + Cudd_RecursiveDeref(dd, bd); + Cudd_RecursiveDeref(dd, T); + return(NULL); + } + E = Cudd_NotCond(E, E != NULL); + Cudd_Ref(E); + Cudd_RecursiveDeref(dd, b0); + Cudd_RecursiveDeref(dd, bd); + } + else { + Cudd_RecursiveDerefZdd(dd, fd); + T = b1; + E = b0; + } + + if (Cudd_IsComplement(T)) { + neW = cuddUniqueInterIVO(dd, v / 2, Cudd_Not(T), Cudd_Not(E)); + if (!neW) { + Cudd_RecursiveDeref(dd, T); + Cudd_RecursiveDeref(dd, E); + return(NULL); + } + neW = Cudd_Not(neW); + } + else { + neW = cuddUniqueInterIVO(dd, v / 2, T, E); + if (!neW) { + Cudd_RecursiveDeref(dd, T); + Cudd_RecursiveDeref(dd, E); + return(NULL); + } + } + Cudd_Ref(neW); + Cudd_RecursiveDeref(dd, T); + Cudd_RecursiveDeref(dd, E); + + cuddCacheInsert1(dd, cuddMakeBddFromZddCover, node, neW); + Cudd_Deref(neW); + return(neW); + +} /* end of cuddMakeBddFromZddCover */ + + +/*---------------------------------------------------------------------------*/ +/* Definition of static functions */ +/*---------------------------------------------------------------------------*/ +/**CFile*********************************************************************** + + FileName [cuddZddLin.c] + + PackageName [cudd] + + Synopsis [Procedures for dynamic variable ordering of ZDDs.] + + Description [Internal procedures included in this module: +
        +
      • cuddZddLinearSifting() +
      + Static procedures included in this module: +
        +
      • cuddZddLinearInPlace() +
      • cuddZddLinerAux() +
      • cuddZddLinearUp() +
      • cuddZddLinearDown() +
      • cuddZddLinearBackward() +
      • cuddZddUndoMoves() +
      + ] + + SeeAlso [cuddLinear.c cuddZddReord.c] + + Author [Fabio Somenzi] + + Copyright [Copyright (c) 1995-2012, Regents of the University of Colorado + + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + Neither the name of the University of Colorado nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE.] + +******************************************************************************/ + + +/*---------------------------------------------------------------------------*/ +/* Constant declarations */ +/*---------------------------------------------------------------------------*/ + +#define CUDD_SWAP_MOVE 0 +#define CUDD_LINEAR_TRANSFORM_MOVE 1 +#define CUDD_INVERSE_TRANSFORM_MOVE 2 + +/*---------------------------------------------------------------------------*/ +/* Stucture declarations */ +/*---------------------------------------------------------------------------*/ + + +/*---------------------------------------------------------------------------*/ +/* Type declarations */ +/*---------------------------------------------------------------------------*/ + + +/*---------------------------------------------------------------------------*/ +/* Variable declarations */ +/*---------------------------------------------------------------------------*/ + +//#ifndef lint +//static char rcsid[] DD_UNUSED = "$Id: cuddZddLin.c,v 1.16 2012/02/05 01:07:19 fabio Exp $"; +//#endif + +extern int *zdd_entry; +extern int zddTotalNumberSwapping; +static int zddTotalNumberLinearTr; +static DdNode *empty; + + +/*---------------------------------------------------------------------------*/ +/* Macro declarations */ +/*---------------------------------------------------------------------------*/ + + +/**AutomaticStart*************************************************************/ + +/*---------------------------------------------------------------------------*/ +/* Static function prototypes */ +/*---------------------------------------------------------------------------*/ + +static int cuddZddLinearInPlace (DdManager * table, int x, int y); +static int cuddZddLinearAux (DdManager *table, int x, int xLow, int xHigh); +static Move * cuddZddLinearUp (DdManager *table, int y, int xLow, Move *prevMoves); +static Move * cuddZddLinearDown (DdManager *table, int x, int xHigh, Move *prevMoves); +static int cuddZddLinearBackward (DdManager *table, int size, Move *moves); +static Move* cuddZddUndoMoves (DdManager *table, Move *moves); + +/**AutomaticEnd***************************************************************/ + + +/*---------------------------------------------------------------------------*/ +/* Definition of exported functions */ +/*---------------------------------------------------------------------------*/ + + +/*---------------------------------------------------------------------------*/ +/* Definition of internal functions */ +/*---------------------------------------------------------------------------*/ + + + + +/**Function******************************************************************** + + Synopsis [Implementation of the linear sifting algorithm for ZDDs.] + + Description [Implementation of the linear sifting algorithm for ZDDs. + Assumes that no dead nodes are present. +
        +
      1. Order all the variables according to the number of entries + in each unique table. +
      2. Sift the variable up and down and applies the XOR transformation, + remembering each time the total size of the DD heap. +
      3. Select the best permutation. +
      4. Repeat 3 and 4 for all variables. +
      + Returns 1 if successful; 0 otherwise.] + + SideEffects [None] + + SeeAlso [] + +******************************************************************************/ +int +cuddZddLinearSifting( + DdManager * table, + int lower, + int upper) +{ + int i; + int *var; + int size; + int x; + int result; +#ifdef DD_STATS + int previousSize; +#endif + + size = table->sizeZ; + empty = table->zero; + + /* Find order in which to sift variables. */ + var = NULL; + zdd_entry = ALLOC(int, size); + if (zdd_entry == NULL) { + table->errorCode = CUDD_MEMORY_OUT; + goto cuddZddSiftingOutOfMem; + } + var = ALLOC(int, size); + if (var == NULL) { + table->errorCode = CUDD_MEMORY_OUT; + goto cuddZddSiftingOutOfMem; + } + + for (i = 0; i < size; i++) { + x = table->permZ[i]; + zdd_entry[i] = table->subtableZ[x].keys; + var[i] = i; + } + + qsort((void *)var, size, sizeof(int), (DD_QSFP)cuddZddUniqueCompare); + + /* Now sift. */ + for (i = 0; i < ddMin(table->siftMaxVar, size); i++) { + if (zddTotalNumberSwapping >= table->siftMaxSwap) + break; + if (util_cpu_time() - table->startTime > table->timeLimit) { + table->autoDynZ = 0; /* prevent further reordering */ + break; + } + x = table->permZ[var[i]]; + if (x < lower || x > upper) continue; +#ifdef DD_STATS + previousSize = table->keysZ; +#endif + result = cuddZddLinearAux(table, x, lower, upper); + if (!result) + goto cuddZddSiftingOutOfMem; +#ifdef DD_STATS + if (table->keysZ < (unsigned) previousSize) { + (void) fprintf(table->out,"-"); + } else if (table->keysZ > (unsigned) previousSize) { + (void) fprintf(table->out,"+"); /* should never happen */ + (void) fprintf(table->out,"\nSize increased from %d to %d while sifting variable %d\n", previousSize, table->keysZ , var[i]); + } else { + (void) fprintf(table->out,"="); + } + fflush(table->out); +#endif + } + + FREE(var); + FREE(zdd_entry); + + return(1); + + cuddZddSiftingOutOfMem: + + if (zdd_entry != NULL) FREE(zdd_entry); + if (var != NULL) FREE(var); + + return(0); + +} /* end of cuddZddLinearSifting */ + + +/*---------------------------------------------------------------------------*/ +/* Definition of static functions */ +/*---------------------------------------------------------------------------*/ + + +/**Function******************************************************************** + + Synopsis [Linearly combines two adjacent variables.] + + Description [Linearly combines two adjacent variables. It assumes + that no dead nodes are present on entry to this procedure. The + procedure then guarantees that no dead nodes will be present when it + terminates. cuddZddLinearInPlace assumes that x < y. Returns the + number of keys in the table if successful; 0 otherwise.] + + SideEffects [None] + + SeeAlso [cuddZddSwapInPlace cuddLinearInPlace] + +******************************************************************************/ +static int +cuddZddLinearInPlace( + DdManager * table, + int x, + int y) +{ + DdNodePtr *xlist, *ylist; + int xindex, yindex; + int xslots, yslots; + int xshift, yshift; + int oldxkeys, oldykeys; + int newxkeys, newykeys; + int i; + int posn; + DdNode *f, *f1, *f0, *f11, *f10, *f01, *f00; + DdNode *newf1, *newf0, *g, *next, *previous; + DdNode *special; + +#ifdef DD_DEBUG + assert(x < y); + assert(cuddZddNextHigh(table,x) == y); + assert(table->subtableZ[x].keys != 0); + assert(table->subtableZ[y].keys != 0); + assert(table->subtableZ[x].dead == 0); + assert(table->subtableZ[y].dead == 0); +#endif + + zddTotalNumberLinearTr++; + + /* Get parameters of x subtable. */ + xindex = table->invpermZ[x]; + xlist = table->subtableZ[x].nodelist; + oldxkeys = table->subtableZ[x].keys; + xslots = table->subtableZ[x].slots; + xshift = table->subtableZ[x].shift; + newxkeys = 0; + + /* Get parameters of y subtable. */ + yindex = table->invpermZ[y]; + ylist = table->subtableZ[y].nodelist; + oldykeys = table->subtableZ[y].keys; + yslots = table->subtableZ[y].slots; + yshift = table->subtableZ[y].shift; + newykeys = oldykeys; + + /* The nodes in the x layer are put in two chains. The chain + ** pointed by g holds the normal nodes. When re-expressed they stay + ** in the x list. The chain pointed by special holds the elements + ** that will move to the y list. + */ + g = special = NULL; + for (i = 0; i < xslots; i++) { + f = xlist[i]; + if (f == NULL) continue; + xlist[i] = NULL; + while (f != NULL) { + next = f->next; + f1 = cuddT(f); + /* if (f1->index == yindex) */ cuddSatDec(f1->ref); + f0 = cuddE(f); + /* if (f0->index == yindex) */ cuddSatDec(f0->ref); + if ((int) f1->index == yindex && cuddE(f1) == empty && + (int) f0->index != yindex) { + f->next = special; + special = f; + } else { + f->next = g; + g = f; + } + f = next; + } /* while there are elements in the collision chain */ + } /* for each slot of the x subtable */ + + /* Mark y nodes with pointers from above x. We mark them by + ** changing their index to x. + */ + for (i = 0; i < yslots; i++) { + f = ylist[i]; + while (f != NULL) { + if (f->ref != 0) { + f->index = xindex; + } + f = f->next; + } /* while there are elements in the collision chain */ + } /* for each slot of the y subtable */ + + /* Move special nodes to the y list. */ + f = special; + while (f != NULL) { + next = f->next; + f1 = cuddT(f); + f11 = cuddT(f1); + cuddT(f) = f11; + cuddSatInc(f11->ref); + f0 = cuddE(f); + cuddSatInc(f0->ref); + f->index = yindex; + /* Insert at the beginning of the list so that it will be + ** found first if there is a duplicate. The duplicate will + ** eventually be moved or garbage collected. No node + ** re-expression will add a pointer to it. + */ + posn = ddHash(f11, f0, yshift); + f->next = ylist[posn]; + ylist[posn] = f; + newykeys++; + f = next; + } + + /* Take care of the remaining x nodes that must be re-expressed. + ** They form a linked list pointed by g. + */ + f = g; + while (f != NULL) { +#ifdef DD_COUNT + table->swapSteps++; +#endif + next = f->next; + /* Find f1, f0, f11, f10, f01, f00. */ + f1 = cuddT(f); + if ((int) f1->index == yindex || (int) f1->index == xindex) { + f11 = cuddT(f1); f10 = cuddE(f1); + } else { + f11 = empty; f10 = f1; + } + f0 = cuddE(f); + if ((int) f0->index == yindex || (int) f0->index == xindex) { + f01 = cuddT(f0); f00 = cuddE(f0); + } else { + f01 = empty; f00 = f0; + } + /* Create the new T child. */ + if (f01 == empty) { + newf1 = f10; + cuddSatInc(newf1->ref); + } else { + /* Check ylist for triple (yindex, f01, f10). */ + posn = ddHash(f01, f10, yshift); + /* For each element newf1 in collision list ylist[posn]. */ + newf1 = ylist[posn]; + /* Search the collision chain skipping the marked nodes. */ + while (newf1 != NULL) { + if (cuddT(newf1) == f01 && cuddE(newf1) == f10 && + (int) newf1->index == yindex) { + cuddSatInc(newf1->ref); + break; /* match */ + } + newf1 = newf1->next; + } /* while newf1 */ + if (newf1 == NULL) { /* no match */ + newf1 = cuddDynamicAllocNode(table); + if (newf1 == NULL) + goto zddSwapOutOfMem; + newf1->index = yindex; newf1->ref = 1; + cuddT(newf1) = f01; + cuddE(newf1) = f10; + /* Insert newf1 in the collision list ylist[pos]; + ** increase the ref counts of f01 and f10 + */ + newykeys++; + newf1->next = ylist[posn]; + ylist[posn] = newf1; + cuddSatInc(f01->ref); + cuddSatInc(f10->ref); + } + } + cuddT(f) = newf1; + + /* Do the same for f0. */ + /* Create the new E child. */ + if (f11 == empty) { + newf0 = f00; + cuddSatInc(newf0->ref); + } else { + /* Check ylist for triple (yindex, f11, f00). */ + posn = ddHash(f11, f00, yshift); + /* For each element newf0 in collision list ylist[posn]. */ + newf0 = ylist[posn]; + while (newf0 != NULL) { + if (cuddT(newf0) == f11 && cuddE(newf0) == f00 && + (int) newf0->index == yindex) { + cuddSatInc(newf0->ref); + break; /* match */ + } + newf0 = newf0->next; + } /* while newf0 */ + if (newf0 == NULL) { /* no match */ + newf0 = cuddDynamicAllocNode(table); + if (newf0 == NULL) + goto zddSwapOutOfMem; + newf0->index = yindex; newf0->ref = 1; + cuddT(newf0) = f11; cuddE(newf0) = f00; + /* Insert newf0 in the collision list ylist[posn]; + ** increase the ref counts of f11 and f00. + */ + newykeys++; + newf0->next = ylist[posn]; + ylist[posn] = newf0; + cuddSatInc(f11->ref); + cuddSatInc(f00->ref); + } + } + cuddE(f) = newf0; + + /* Re-insert the modified f in xlist. + ** The modified f does not already exists in xlist. + ** (Because of the uniqueness of the cofactors.) + */ + posn = ddHash(newf1, newf0, xshift); + newxkeys++; + f->next = xlist[posn]; + xlist[posn] = f; + f = next; + } /* while f != NULL */ + + /* GC the y layer and move the marked nodes to the x list. */ + + /* For each node f in ylist. */ + for (i = 0; i < yslots; i++) { + previous = NULL; + f = ylist[i]; + while (f != NULL) { + next = f->next; + if (f->ref == 0) { + cuddSatDec(cuddT(f)->ref); + cuddSatDec(cuddE(f)->ref); + cuddDeallocNode(table, f); + newykeys--; + if (previous == NULL) + ylist[i] = next; + else + previous->next = next; + } else if ((int) f->index == xindex) { /* move marked node */ + if (previous == NULL) + ylist[i] = next; + else + previous->next = next; + f1 = cuddT(f); + cuddSatDec(f1->ref); + /* Check ylist for triple (yindex, f1, empty). */ + posn = ddHash(f1, empty, yshift); + /* For each element newf1 in collision list ylist[posn]. */ + newf1 = ylist[posn]; + while (newf1 != NULL) { + if (cuddT(newf1) == f1 && cuddE(newf1) == empty && + (int) newf1->index == yindex) { + cuddSatInc(newf1->ref); + break; /* match */ + } + newf1 = newf1->next; + } /* while newf1 */ + if (newf1 == NULL) { /* no match */ + newf1 = cuddDynamicAllocNode(table); + if (newf1 == NULL) + goto zddSwapOutOfMem; + newf1->index = yindex; newf1->ref = 1; + cuddT(newf1) = f1; cuddE(newf1) = empty; + /* Insert newf1 in the collision list ylist[posn]; + ** increase the ref counts of f1 and empty. + */ + newykeys++; + newf1->next = ylist[posn]; + ylist[posn] = newf1; + if (posn == i && previous == NULL) + previous = newf1; + cuddSatInc(f1->ref); + cuddSatInc(empty->ref); + } + cuddT(f) = newf1; + f0 = cuddE(f); + /* Insert f in x list. */ + posn = ddHash(newf1, f0, xshift); + newxkeys++; + newykeys--; + f->next = xlist[posn]; + xlist[posn] = f; + } else { + previous = f; + } + f = next; + } /* while f */ + } /* for i */ + + /* Set the appropriate fields in table. */ + table->subtableZ[x].keys = newxkeys; + table->subtableZ[y].keys = newykeys; + + table->keysZ += newxkeys + newykeys - oldxkeys - oldykeys; + + /* Update univ section; univ[x] remains the same. */ + table->univ[y] = cuddT(table->univ[x]); + +#if 0 + (void) fprintf(table->out,"x = %d y = %d\n", x, y); + (void) Cudd_DebugCheck(table); + (void) Cudd_CheckKeys(table); +#endif + + return (table->keysZ); + + zddSwapOutOfMem: + (void) fprintf(table->err, "Error: cuddZddSwapInPlace out of memory\n"); + + return (0); + +} /* end of cuddZddLinearInPlace */ + + +/**Function******************************************************************** + + Synopsis [Given xLow <= x <= xHigh moves x up and down between the + boundaries.] + + Description [Given xLow <= x <= xHigh moves x up and down between the + boundaries. Finds the best position and does the required changes. + Returns 1 if successful; 0 otherwise.] + + SideEffects [None] + + SeeAlso [] + +******************************************************************************/ +static int +cuddZddLinearAux( + DdManager * table, + int x, + int xLow, + int xHigh) +{ + Move *move; + Move *moveUp; /* list of up move */ + Move *moveDown; /* list of down move */ + + int initial_size; + int result; + + initial_size = table->keysZ; + +#ifdef DD_DEBUG + assert(table->subtableZ[x].keys > 0); +#endif + + moveDown = NULL; + moveUp = NULL; + + if (x == xLow) { + moveDown = cuddZddLinearDown(table, x, xHigh, NULL); + /* At this point x --> xHigh. */ + if (moveDown == (Move *) CUDD_OUT_OF_MEM) + goto cuddZddLinearAuxOutOfMem; + /* Move backward and stop at best position. */ + result = cuddZddLinearBackward(table, initial_size, moveDown); + if (!result) + goto cuddZddLinearAuxOutOfMem; + + } else if (x == xHigh) { + moveUp = cuddZddLinearUp(table, x, xLow, NULL); + /* At this point x --> xLow. */ + if (moveUp == (Move *) CUDD_OUT_OF_MEM) + goto cuddZddLinearAuxOutOfMem; + /* Move backward and stop at best position. */ + result = cuddZddLinearBackward(table, initial_size, moveUp); + if (!result) + goto cuddZddLinearAuxOutOfMem; + + } else if ((x - xLow) > (xHigh - x)) { /* must go down first: shorter */ + moveDown = cuddZddLinearDown(table, x, xHigh, NULL); + /* At this point x --> xHigh. */ + if (moveDown == (Move *) CUDD_OUT_OF_MEM) + goto cuddZddLinearAuxOutOfMem; + moveUp = cuddZddUndoMoves(table,moveDown); +#ifdef DD_DEBUG + assert(moveUp == NULL || moveUp->x == x); +#endif + moveUp = cuddZddLinearUp(table, x, xLow, moveUp); + if (moveUp == (Move *) CUDD_OUT_OF_MEM) + goto cuddZddLinearAuxOutOfMem; + /* Move backward and stop at best position. */ + result = cuddZddLinearBackward(table, initial_size, moveUp); + if (!result) + goto cuddZddLinearAuxOutOfMem; + + } else { + moveUp = cuddZddLinearUp(table, x, xLow, NULL); + /* At this point x --> xHigh. */ + if (moveUp == (Move *) CUDD_OUT_OF_MEM) + goto cuddZddLinearAuxOutOfMem; + /* Then move up. */ + moveDown = cuddZddUndoMoves(table,moveUp); +#ifdef DD_DEBUG + assert(moveDown == NULL || moveDown->y == x); +#endif + moveDown = cuddZddLinearDown(table, x, xHigh, moveDown); + if (moveDown == (Move *) CUDD_OUT_OF_MEM) + goto cuddZddLinearAuxOutOfMem; + /* Move backward and stop at best position. */ + result = cuddZddLinearBackward(table, initial_size, moveDown); + if (!result) + goto cuddZddLinearAuxOutOfMem; + } + + while (moveDown != NULL) { + move = moveDown->next; + cuddDeallocMove(table, moveDown); + moveDown = move; + } + while (moveUp != NULL) { + move = moveUp->next; + cuddDeallocMove(table, moveUp); + moveUp = move; + } + + return(1); + + cuddZddLinearAuxOutOfMem: + if (moveDown != (Move *) CUDD_OUT_OF_MEM) { + while (moveDown != NULL) { + move = moveDown->next; + cuddDeallocMove(table, moveDown); + moveDown = move; + } + } + if (moveUp != (Move *) CUDD_OUT_OF_MEM) { + while (moveUp != NULL) { + move = moveUp->next; + cuddDeallocMove(table, moveUp); + moveUp = move; + } + } + + return(0); + +} /* end of cuddZddLinearAux */ + + +/**Function******************************************************************** + + Synopsis [Sifts a variable up applying the XOR transformation.] + + Description [Sifts a variable up applying the XOR + transformation. Moves y up until either it reaches the bound (xLow) + or the size of the ZDD heap increases too much. Returns the set of + moves in case of success; NULL if memory is full.] + + SideEffects [None] + + SeeAlso [] + +******************************************************************************/ +static Move * +cuddZddLinearUp( + DdManager * table, + int y, + int xLow, + Move * prevMoves) +{ + Move *moves; + Move *move; + int x; + int size, newsize; + int limitSize; + + moves = prevMoves; + limitSize = table->keysZ; + + x = cuddZddNextLow(table, y); + while (x >= xLow) { + size = cuddZddSwapInPlace(table, x, y); + if (size == 0) + goto cuddZddLinearUpOutOfMem; + newsize = cuddZddLinearInPlace(table, x, y); + if (newsize == 0) + goto cuddZddLinearUpOutOfMem; + move = (Move *) cuddDynamicAllocNode(table); + if (move == NULL) + goto cuddZddLinearUpOutOfMem; + move->x = x; + move->y = y; + move->next = moves; + moves = move; + move->flags = CUDD_SWAP_MOVE; + if (newsize > size) { + /* Undo transformation. The transformation we apply is + ** its own inverse. Hence, we just apply the transformation + ** again. + */ + newsize = cuddZddLinearInPlace(table,x,y); + if (newsize == 0) goto cuddZddLinearUpOutOfMem; +#ifdef DD_DEBUG + if (newsize != size) { + (void) fprintf(table->err,"Change in size after identity transformation! From %d to %d\n",size,newsize); + } +#endif + } else { + size = newsize; + move->flags = CUDD_LINEAR_TRANSFORM_MOVE; + } + move->size = size; + + if ((double)size > (double)limitSize * table->maxGrowth) + break; + if (size < limitSize) + limitSize = size; + + y = x; + x = cuddZddNextLow(table, y); + } + return(moves); + + cuddZddLinearUpOutOfMem: + while (moves != NULL) { + move = moves->next; + cuddDeallocMove(table, moves); + moves = move; + } + return((Move *) CUDD_OUT_OF_MEM); + +} /* end of cuddZddLinearUp */ + + +/**Function******************************************************************** + + Synopsis [Sifts a variable down and applies the XOR transformation.] + + Description [Sifts a variable down. Moves x down until either it + reaches the bound (xHigh) or the size of the ZDD heap increases too + much. Returns the set of moves in case of success; NULL if memory is + full.] + + SideEffects [None] + + SeeAlso [] + +******************************************************************************/ +static Move * +cuddZddLinearDown( + DdManager * table, + int x, + int xHigh, + Move * prevMoves) +{ + Move *moves; + Move *move; + int y; + int size, newsize; + int limitSize; + + moves = prevMoves; + limitSize = table->keysZ; + + y = cuddZddNextHigh(table, x); + while (y <= xHigh) { + size = cuddZddSwapInPlace(table, x, y); + if (size == 0) + goto cuddZddLinearDownOutOfMem; + newsize = cuddZddLinearInPlace(table, x, y); + if (newsize == 0) + goto cuddZddLinearDownOutOfMem; + move = (Move *) cuddDynamicAllocNode(table); + if (move == NULL) + goto cuddZddLinearDownOutOfMem; + move->x = x; + move->y = y; + move->next = moves; + moves = move; + move->flags = CUDD_SWAP_MOVE; + if (newsize > size) { + /* Undo transformation. The transformation we apply is + ** its own inverse. Hence, we just apply the transformation + ** again. + */ + newsize = cuddZddLinearInPlace(table,x,y); + if (newsize == 0) goto cuddZddLinearDownOutOfMem; + if (newsize != size) { + (void) fprintf(table->err,"Change in size after identity transformation! From %d to %d\n",size,newsize); + } + } else { + size = newsize; + move->flags = CUDD_LINEAR_TRANSFORM_MOVE; + } + move->size = size; + + if ((double)size > (double)limitSize * table->maxGrowth) + break; + if (size < limitSize) + limitSize = size; + + x = y; + y = cuddZddNextHigh(table, x); + } + return(moves); + + cuddZddLinearDownOutOfMem: + while (moves != NULL) { + move = moves->next; + cuddDeallocMove(table, moves); + moves = move; + } + return((Move *) CUDD_OUT_OF_MEM); + +} /* end of cuddZddLinearDown */ + + +/**Function******************************************************************** + + Synopsis [Given a set of moves, returns the ZDD heap to the position + giving the minimum size.] + + Description [Given a set of moves, returns the ZDD heap to the + position giving the minimum size. In case of ties, returns to the + closest position giving the minimum size. Returns 1 in case of + success; 0 otherwise.] + + SideEffects [None] + + SeeAlso [] + +******************************************************************************/ +static int +cuddZddLinearBackward( + DdManager * table, + int size, + Move * moves) +{ + Move *move; + int res; + + /* Find the minimum size among moves. */ + for (move = moves; move != NULL; move = move->next) { + if (move->size < size) { + size = move->size; + } + } + + for (move = moves; move != NULL; move = move->next) { + if (move->size == size) return(1); + if (move->flags == CUDD_LINEAR_TRANSFORM_MOVE) { + res = cuddZddLinearInPlace(table,(int)move->x,(int)move->y); + if (!res) return(0); + } + res = cuddZddSwapInPlace(table, move->x, move->y); + if (!res) + return(0); + if (move->flags == CUDD_INVERSE_TRANSFORM_MOVE) { + res = cuddZddLinearInPlace(table,(int)move->x,(int)move->y); + if (!res) return(0); + } + } + + return(1); + +} /* end of cuddZddLinearBackward */ + + +/**Function******************************************************************** + + Synopsis [Given a set of moves, returns the ZDD heap to the order + in effect before the moves.] + + Description [Given a set of moves, returns the ZDD heap to the + order in effect before the moves. Returns 1 in case of success; + 0 otherwise.] + + SideEffects [None] + +******************************************************************************/ +static Move* +cuddZddUndoMoves( + DdManager * table, + Move * moves) +{ + Move *invmoves = NULL; + Move *move; + Move *invmove; + int size; + + for (move = moves; move != NULL; move = move->next) { + invmove = (Move *) cuddDynamicAllocNode(table); + if (invmove == NULL) goto cuddZddUndoMovesOutOfMem; + invmove->x = move->x; + invmove->y = move->y; + invmove->next = invmoves; + invmoves = invmove; + if (move->flags == CUDD_SWAP_MOVE) { + invmove->flags = CUDD_SWAP_MOVE; + size = cuddZddSwapInPlace(table,(int)move->x,(int)move->y); + if (!size) goto cuddZddUndoMovesOutOfMem; + } else if (move->flags == CUDD_LINEAR_TRANSFORM_MOVE) { + invmove->flags = CUDD_INVERSE_TRANSFORM_MOVE; + size = cuddZddLinearInPlace(table,(int)move->x,(int)move->y); + if (!size) goto cuddZddUndoMovesOutOfMem; + size = cuddZddSwapInPlace(table,(int)move->x,(int)move->y); + if (!size) goto cuddZddUndoMovesOutOfMem; + } else { /* must be CUDD_INVERSE_TRANSFORM_MOVE */ +#ifdef DD_DEBUG + (void) fprintf(table->err,"Unforseen event in ddUndoMoves!\n"); +#endif + invmove->flags = CUDD_LINEAR_TRANSFORM_MOVE; + size = cuddZddSwapInPlace(table,(int)move->x,(int)move->y); + if (!size) goto cuddZddUndoMovesOutOfMem; + size = cuddZddLinearInPlace(table,(int)move->x,(int)move->y); + if (!size) goto cuddZddUndoMovesOutOfMem; + } + invmove->size = size; + } + + return(invmoves); + + cuddZddUndoMovesOutOfMem: + while (invmoves != NULL) { + move = invmoves->next; + cuddDeallocMove(table, invmoves); + invmoves = move; + } + return((Move *) CUDD_OUT_OF_MEM); + +} /* end of cuddZddUndoMoves */ + +/**CFile*********************************************************************** + + FileName [cuddZddReord.c] + + PackageName [cudd] + + Synopsis [Procedures for dynamic variable ordering of ZDDs.] + + Description [External procedures included in this module: +
        +
      • Cudd_zddReduceHeap() +
      • Cudd_zddShuffleHeap() +
      + Internal procedures included in this module: +
        +
      • cuddZddAlignToBdd() +
      • cuddZddNextHigh() +
      • cuddZddNextLow() +
      • cuddZddUniqueCompare() +
      • cuddZddSwapInPlace() +
      • cuddZddSwapping() +
      • cuddZddSifting() +
      + Static procedures included in this module: +
        +
      • zddSwapAny() +
      • cuddZddSiftingAux() +
      • cuddZddSiftingUp() +
      • cuddZddSiftingDown() +
      • cuddZddSiftingBackward() +
      • zddReorderPreprocess() +
      • zddReorderPostprocess() +
      • zddShuffle() +
      • zddSiftUp() +
      + ] + + SeeAlso [] + + Author [Hyong-Kyoon Shin, In-Ho Moon] + + Copyright [Copyright (c) 1995-2012, Regents of the University of Colorado + + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + Neither the name of the University of Colorado nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE.] + +******************************************************************************/ + + +/*---------------------------------------------------------------------------*/ +/* Constant declarations */ +/*---------------------------------------------------------------------------*/ + +#define DD_MAX_SUBTABLE_SPARSITY 8 + +/*---------------------------------------------------------------------------*/ +/* Stucture declarations */ +/*---------------------------------------------------------------------------*/ + + +/*---------------------------------------------------------------------------*/ +/* Type declarations */ +/*---------------------------------------------------------------------------*/ + + +/*---------------------------------------------------------------------------*/ +/* Variable declarations */ +/*---------------------------------------------------------------------------*/ + +//#ifndef lint +//static char rcsid[] DD_UNUSED = "$Id: cuddZddReord.c,v 1.49 2012/02/05 01:07:19 fabio Exp $"; +//#endif + +int *zdd_entry; + +int zddTotalNumberSwapping; + +static DdNode *empty; + + +/*---------------------------------------------------------------------------*/ +/* Macro declarations */ +/*---------------------------------------------------------------------------*/ + + +/**AutomaticStart*************************************************************/ + +/*---------------------------------------------------------------------------*/ +/* Static function prototypes */ +/*---------------------------------------------------------------------------*/ + +static Move * zddSwapAny (DdManager *table, int x, int y); +static int cuddZddSiftingAux (DdManager *table, int x, int x_low, int x_high); +static Move * cuddZddSiftingUp (DdManager *table, int x, int x_low, int initial_size); +static Move * cuddZddSiftingDown (DdManager *table, int x, int x_high, int initial_size); +static int cuddZddSiftingBackward (DdManager *table, Move *moves, int size); +static void zddReorderPreprocess (DdManager *table); +static int zddReorderPostprocess (DdManager *table); +static int zddShuffle (DdManager *table, int *permutation); +static int zddSiftUp (DdManager *table, int x, int xLow); +static void zddFixTree (DdManager *table, MtrNode *treenode); + +/**AutomaticEnd***************************************************************/ + + +/*---------------------------------------------------------------------------*/ +/* Definition of exported functions */ +/*---------------------------------------------------------------------------*/ + + +/**Function******************************************************************** + + Synopsis [Main dynamic reordering routine for ZDDs.] + + Description [Main dynamic reordering routine for ZDDs. + Calls one of the possible reordering procedures: +
        +
      • Swapping +
      • Sifting +
      • Symmetric Sifting +
      + + For sifting and symmetric sifting it is possible to request reordering + to convergence.

      + + The core of all methods is the reordering procedure + cuddZddSwapInPlace() which swaps two adjacent variables. + Returns 1 in case of success; 0 otherwise. In the case of symmetric + sifting (with and without convergence) returns 1 plus the number of + symmetric variables, in case of success.] + + SideEffects [Changes the variable order for all ZDDs and clears + the cache.] + +******************************************************************************/ +int +Cudd_zddReduceHeap( + DdManager * table /* DD manager */, + Cudd_ReorderingType heuristic /* method used for reordering */, + int minsize /* bound below which no reordering occurs */) +{ + DdHook *hook; + int result; + unsigned int nextDyn; +#ifdef DD_STATS + unsigned int initialSize; + unsigned int finalSize; +#endif + unsigned long localTime; + + /* Don't reorder if there are too many dead nodes. */ + if (table->keysZ - table->deadZ < (unsigned) minsize) + return(1); + + if (heuristic == CUDD_REORDER_SAME) { + heuristic = table->autoMethodZ; + } + if (heuristic == CUDD_REORDER_NONE) { + return(1); + } + + /* This call to Cudd_zddReduceHeap does initiate reordering. Therefore + ** we count it. + */ + table->reorderings++; + empty = table->zero; + + localTime = util_cpu_time(); + + /* Run the hook functions. */ + hook = table->preReorderingHook; + while (hook != NULL) { + int res = (hook->f)(table, "ZDD", (void *)heuristic); + if (res == 0) return(0); + hook = hook->next; + } + + /* Clear the cache and collect garbage. */ + zddReorderPreprocess(table); + zddTotalNumberSwapping = 0; + +#ifdef DD_STATS + initialSize = table->keysZ; + + switch(heuristic) { + case CUDD_REORDER_RANDOM: + case CUDD_REORDER_RANDOM_PIVOT: + (void) fprintf(table->out,"#:I_RANDOM "); + break; + case CUDD_REORDER_SIFT: + case CUDD_REORDER_SIFT_CONVERGE: + case CUDD_REORDER_SYMM_SIFT: + case CUDD_REORDER_SYMM_SIFT_CONV: + (void) fprintf(table->out,"#:I_SIFTING "); + break; + case CUDD_REORDER_LINEAR: + case CUDD_REORDER_LINEAR_CONVERGE: + (void) fprintf(table->out,"#:I_LINSIFT "); + break; + default: + (void) fprintf(table->err,"Unsupported ZDD reordering method\n"); + return(0); + } + (void) fprintf(table->out,"%8d: initial size",initialSize); +#endif + + result = cuddZddTreeSifting(table,heuristic); + +#ifdef DD_STATS + (void) fprintf(table->out,"\n"); + finalSize = table->keysZ; + (void) fprintf(table->out,"#:F_REORDER %8d: final size\n",finalSize); + (void) fprintf(table->out,"#:T_REORDER %8g: total time (sec)\n", + ((double)(util_cpu_time() - localTime)/1000.0)); + (void) fprintf(table->out,"#:N_REORDER %8d: total swaps\n", + zddTotalNumberSwapping); +#endif + + if (result == 0) + return(0); + + if (!zddReorderPostprocess(table)) + return(0); + + if (table->realignZ) { + if (!cuddBddAlignToZdd(table)) + return(0); + } + + nextDyn = table->keysZ * DD_DYN_RATIO; + if (table->reorderings < 20 || nextDyn > table->nextDyn) + table->nextDyn = nextDyn; + else + table->nextDyn += 20; + + table->reordered = 1; + + /* Run hook functions. */ + hook = table->postReorderingHook; + while (hook != NULL) { + int res = (hook->f)(table, "ZDD", (void *)localTime); + if (res == 0) return(0); + hook = hook->next; + } + /* Update cumulative reordering time. */ + table->reordTime += util_cpu_time() - localTime; + + return(result); + +} /* end of Cudd_zddReduceHeap */ + + +/*---------------------------------------------------------------------------*/ +/* Definition of internal functions */ +/*---------------------------------------------------------------------------*/ + + +/**Function******************************************************************** + + Synopsis [Reorders ZDD variables according to the order of the BDD + variables.] + + Description [Reorders ZDD variables according to the order of the + BDD variables. This function can be called at the end of BDD + reordering to insure that the order of the ZDD variables is + consistent with the order of the BDD variables. The number of ZDD + variables must be a multiple of the number of BDD variables. Let + M be the ratio of the two numbers. cuddZddAlignToBdd + then considers the ZDD variables from M*i to + (M+1)*i-1 as corresponding to BDD variable + i. This function should be normally called from + Cudd_ReduceHeap, which clears the cache. Returns 1 in case of + success; 0 otherwise.] + + SideEffects [Changes the ZDD variable order for all diagrams and performs + garbage collection of the ZDD unique table.] + + SeeAlso [Cudd_zddShuffleHeap Cudd_ReduceHeap] + +******************************************************************************/ +int +cuddZddAlignToBdd( + DdManager * table /* DD manager */) +{ + int *invpermZ; /* permutation array */ + int M; /* ratio of ZDD variables to BDD variables */ + int i,j; /* loop indices */ + int result; /* return value */ + + /* We assume that a ratio of 0 is OK. */ + if (table->sizeZ == 0) + return(1); + + empty = table->zero; + M = table->sizeZ / table->size; + /* Check whether the number of ZDD variables is a multiple of the + ** number of BDD variables. + */ + if (M * table->size != table->sizeZ) + return(0); + /* Create and initialize the inverse permutation array. */ + invpermZ = ALLOC(int,table->sizeZ); + if (invpermZ == NULL) { + table->errorCode = CUDD_MEMORY_OUT; + return(0); + } + for (i = 0; i < table->size; i++) { + int index = table->invperm[i]; + int indexZ = index * M; + int levelZ = table->permZ[indexZ]; + levelZ = (levelZ / M) * M; + for (j = 0; j < M; j++) { + invpermZ[M * i + j] = table->invpermZ[levelZ + j]; + } + } + /* Eliminate dead nodes. Do not scan the cache again, because we + ** assume that Cudd_ReduceHeap has already cleared it. + */ + cuddGarbageCollect(table,0); + + result = zddShuffle(table, invpermZ); + FREE(invpermZ); + /* Fix the ZDD variable group tree. */ + zddFixTree(table,table->treeZ); + return(result); + +} /* end of cuddZddAlignToBdd */ + + +/**Function******************************************************************** + + Synopsis [Finds the next subtable with a larger index.] + + Description [Finds the next subtable with a larger index. Returns the + index.] + + SideEffects [None] + + SeeAlso [] + +******************************************************************************/ +int +cuddZddNextHigh( + DdManager * table, + int x) +{ + return(x + 1); + +} /* end of cuddZddNextHigh */ + + +/**Function******************************************************************** + + Synopsis [Finds the next subtable with a smaller index.] + + Description [Finds the next subtable with a smaller index. Returns the + index.] + + SideEffects [None] + + SeeAlso [] + +******************************************************************************/ +int +cuddZddNextLow( + DdManager * table, + int x) +{ + return(x - 1); + +} /* end of cuddZddNextLow */ + + +/**Function******************************************************************** + + Synopsis [Comparison function used by qsort.] + + Description [Comparison function used by qsort to order the + variables according to the number of keys in the subtables. + Returns the difference in number of keys between the two + variables being compared.] + + SideEffects [None] + + SeeAlso [] + +******************************************************************************/ +int +cuddZddUniqueCompare( + int * ptr_x, + int * ptr_y) +{ + return(zdd_entry[*ptr_y] - zdd_entry[*ptr_x]); + +} /* end of cuddZddUniqueCompare */ + + +/**Function******************************************************************** + + Synopsis [Swaps two adjacent variables.] + + Description [Swaps two adjacent variables. It assumes that no dead + nodes are present on entry to this procedure. The procedure then + guarantees that no dead nodes will be present when it terminates. + cuddZddSwapInPlace assumes that x < y. Returns the number of keys in + the table if successful; 0 otherwise.] + + SideEffects [None] + + SeeAlso [] + +******************************************************************************/ +int +cuddZddSwapInPlace( + DdManager * table, + int x, + int y) +{ + DdNodePtr *xlist, *ylist; + int xindex, yindex; + int xslots, yslots; + int xshift, yshift; + int oldxkeys, oldykeys; + int newxkeys, newykeys; + int i; + int posn; + DdNode *f, *f1, *f0, *f11, *f10, *f01, *f00; + DdNode *newf1, *newf0, *next; + DdNodePtr g, *lastP, *previousP; + +#ifdef DD_DEBUG + assert(x < y); + assert(cuddZddNextHigh(table,x) == y); + assert(table->subtableZ[x].keys != 0); + assert(table->subtableZ[y].keys != 0); + assert(table->subtableZ[x].dead == 0); + assert(table->subtableZ[y].dead == 0); +#endif + + zddTotalNumberSwapping++; + + /* Get parameters of x subtable. */ + xindex = table->invpermZ[x]; + xlist = table->subtableZ[x].nodelist; + oldxkeys = table->subtableZ[x].keys; + xslots = table->subtableZ[x].slots; + xshift = table->subtableZ[x].shift; + newxkeys = 0; + + yindex = table->invpermZ[y]; + ylist = table->subtableZ[y].nodelist; + oldykeys = table->subtableZ[y].keys; + yslots = table->subtableZ[y].slots; + yshift = table->subtableZ[y].shift; + newykeys = oldykeys; + + /* The nodes in the x layer that don't depend on y directly + ** will stay there; the others are put in a chain. + ** The chain is handled as a FIFO; g points to the beginning and + ** last points to the end. + */ + + g = NULL; + lastP = &g; + for (i = 0; i < xslots; i++) { + previousP = &(xlist[i]); + f = *previousP; + while (f != NULL) { + next = f->next; + f1 = cuddT(f); f0 = cuddE(f); + if ((f1->index != (DdHalfWord) yindex) && + (f0->index != (DdHalfWord) yindex)) { /* stays */ + newxkeys++; + *previousP = f; + previousP = &(f->next); + } else { + f->index = yindex; + *lastP = f; + lastP = &(f->next); + } + f = next; + } /* while there are elements in the collision chain */ + *previousP = NULL; + } /* for each slot of the x subtable */ + *lastP = NULL; + + +#ifdef DD_COUNT + table->swapSteps += oldxkeys - newxkeys; +#endif + /* Take care of the x nodes that must be re-expressed. + ** They form a linked list pointed by g. Their index has been + ** changed to yindex already. + */ + f = g; + while (f != NULL) { + next = f->next; + /* Find f1, f0, f11, f10, f01, f00. */ + f1 = cuddT(f); + if ((int) f1->index == yindex) { + f11 = cuddT(f1); f10 = cuddE(f1); + } else { + f11 = empty; f10 = f1; + } + f0 = cuddE(f); + if ((int) f0->index == yindex) { + f01 = cuddT(f0); f00 = cuddE(f0); + } else { + f01 = empty; f00 = f0; + } + + /* Decrease ref count of f1. */ + cuddSatDec(f1->ref); + /* Create the new T child. */ + if (f11 == empty) { + if (f01 != empty) { + newf1 = f01; + cuddSatInc(newf1->ref); + } + /* else case was already handled when finding nodes + ** with both children below level y + */ + } else { + /* Check xlist for triple (xindex, f11, f01). */ + posn = ddHash(f11, f01, xshift); + /* For each element newf1 in collision list xlist[posn]. */ + newf1 = xlist[posn]; + while (newf1 != NULL) { + if (cuddT(newf1) == f11 && cuddE(newf1) == f01) { + cuddSatInc(newf1->ref); + break; /* match */ + } + newf1 = newf1->next; + } /* while newf1 */ + if (newf1 == NULL) { /* no match */ + newf1 = cuddDynamicAllocNode(table); + if (newf1 == NULL) + goto zddSwapOutOfMem; + newf1->index = xindex; newf1->ref = 1; + cuddT(newf1) = f11; + cuddE(newf1) = f01; + /* Insert newf1 in the collision list xlist[pos]; + ** increase the ref counts of f11 and f01 + */ + newxkeys++; + newf1->next = xlist[posn]; + xlist[posn] = newf1; + cuddSatInc(f11->ref); + cuddSatInc(f01->ref); + } + } + cuddT(f) = newf1; + + /* Do the same for f0. */ + /* Decrease ref count of f0. */ + cuddSatDec(f0->ref); + /* Create the new E child. */ + if (f10 == empty) { + newf0 = f00; + cuddSatInc(newf0->ref); + } else { + /* Check xlist for triple (xindex, f10, f00). */ + posn = ddHash(f10, f00, xshift); + /* For each element newf0 in collision list xlist[posn]. */ + newf0 = xlist[posn]; + while (newf0 != NULL) { + if (cuddT(newf0) == f10 && cuddE(newf0) == f00) { + cuddSatInc(newf0->ref); + break; /* match */ + } + newf0 = newf0->next; + } /* while newf0 */ + if (newf0 == NULL) { /* no match */ + newf0 = cuddDynamicAllocNode(table); + if (newf0 == NULL) + goto zddSwapOutOfMem; + newf0->index = xindex; newf0->ref = 1; + cuddT(newf0) = f10; cuddE(newf0) = f00; + /* Insert newf0 in the collision list xlist[posn]; + ** increase the ref counts of f10 and f00. + */ + newxkeys++; + newf0->next = xlist[posn]; + xlist[posn] = newf0; + cuddSatInc(f10->ref); + cuddSatInc(f00->ref); + } + } + cuddE(f) = newf0; + + /* Insert the modified f in ylist. + ** The modified f does not already exists in ylist. + ** (Because of the uniqueness of the cofactors.) + */ + posn = ddHash(newf1, newf0, yshift); + newykeys++; + f->next = ylist[posn]; + ylist[posn] = f; + f = next; + } /* while f != NULL */ + + /* GC the y layer. */ + + /* For each node f in ylist. */ + for (i = 0; i < yslots; i++) { + previousP = &(ylist[i]); + f = *previousP; + while (f != NULL) { + next = f->next; + if (f->ref == 0) { + cuddSatDec(cuddT(f)->ref); + cuddSatDec(cuddE(f)->ref); + cuddDeallocNode(table, f); + newykeys--; + } else { + *previousP = f; + previousP = &(f->next); + } + f = next; + } /* while f */ + *previousP = NULL; + } /* for i */ + + /* Set the appropriate fields in table. */ + table->subtableZ[x].nodelist = ylist; + table->subtableZ[x].slots = yslots; + table->subtableZ[x].shift = yshift; + table->subtableZ[x].keys = newykeys; + table->subtableZ[x].maxKeys = yslots * DD_MAX_SUBTABLE_DENSITY; + + table->subtableZ[y].nodelist = xlist; + table->subtableZ[y].slots = xslots; + table->subtableZ[y].shift = xshift; + table->subtableZ[y].keys = newxkeys; + table->subtableZ[y].maxKeys = xslots * DD_MAX_SUBTABLE_DENSITY; + + table->permZ[xindex] = y; table->permZ[yindex] = x; + table->invpermZ[x] = yindex; table->invpermZ[y] = xindex; + + table->keysZ += newxkeys + newykeys - oldxkeys - oldykeys; + + /* Update univ section; univ[x] remains the same. */ + table->univ[y] = cuddT(table->univ[x]); + + return (table->keysZ); + + zddSwapOutOfMem: + (void) fprintf(table->err, "Error: cuddZddSwapInPlace out of memory\n"); + + return (0); + +} /* end of cuddZddSwapInPlace */ + + +/**Function******************************************************************** + + Synopsis [Reorders variables by a sequence of (non-adjacent) swaps.] + + Description [Implementation of Plessier's algorithm that reorders + variables by a sequence of (non-adjacent) swaps. +

        +
      1. Select two variables (RANDOM or HEURISTIC). +
      2. Permute these variables. +
      3. If the nodes have decreased accept the permutation. +
      4. Otherwise reconstruct the original heap. +
      5. Loop. +
      + Returns 1 in case of success; 0 otherwise.] + + SideEffects [None] + + SeeAlso [] + +******************************************************************************/ +int +cuddZddSwapping( + DdManager * table, + int lower, + int upper, + Cudd_ReorderingType heuristic) +{ + int i, j; + int max, keys; + int nvars; + int x, y; + int iterate; + int previousSize; + Move *moves, *move; + int pivot; + int modulo; + int result; + +#ifdef DD_DEBUG + /* Sanity check */ + assert(lower >= 0 && upper < table->sizeZ && lower <= upper); +#endif + + nvars = upper - lower + 1; + iterate = nvars; + + for (i = 0; i < iterate; i++) { + if (heuristic == CUDD_REORDER_RANDOM_PIVOT) { + /* Find pivot <= id with maximum keys. */ + for (max = -1, j = lower; j <= upper; j++) { + if ((keys = table->subtableZ[j].keys) > max) { + max = keys; + pivot = j; + } + } + + modulo = upper - pivot; + if (modulo == 0) { + y = pivot; /* y = nvars-1 */ + } else { + /* y = random # from {pivot+1 .. nvars-1} */ + y = pivot + 1 + (int) (Cudd_Random() % modulo); + } + + modulo = pivot - lower - 1; + if (modulo < 1) { /* if pivot = 1 or 0 */ + x = lower; + } else { + do { /* x = random # from {0 .. pivot-2} */ + x = (int) Cudd_Random() % modulo; + } while (x == y); + /* Is this condition really needed, since x and y + are in regions separated by pivot? */ + } + } else { + x = (int) (Cudd_Random() % nvars) + lower; + do { + y = (int) (Cudd_Random() % nvars) + lower; + } while (x == y); + } + + previousSize = table->keysZ; + moves = zddSwapAny(table, x, y); + if (moves == NULL) + goto cuddZddSwappingOutOfMem; + + result = cuddZddSiftingBackward(table, moves, previousSize); + if (!result) + goto cuddZddSwappingOutOfMem; + + while (moves != NULL) { + move = moves->next; + cuddDeallocMove(table, moves); + moves = move; + } +#ifdef DD_STATS + if (table->keysZ < (unsigned) previousSize) { + (void) fprintf(table->out,"-"); + } else if (table->keysZ > (unsigned) previousSize) { + (void) fprintf(table->out,"+"); /* should never happen */ + } else { + (void) fprintf(table->out,"="); + } + fflush(table->out); +#endif + } + + return(1); + + cuddZddSwappingOutOfMem: + while (moves != NULL) { + move = moves->next; + cuddDeallocMove(table, moves); + moves = move; + } + return(0); + +} /* end of cuddZddSwapping */ + + +/**Function******************************************************************** + + Synopsis [Implementation of Rudell's sifting algorithm.] + + Description [Implementation of Rudell's sifting algorithm. + Assumes that no dead nodes are present. +
        +
      1. Order all the variables according to the number of entries + in each unique table. +
      2. Sift the variable up and down, remembering each time the + total size of the DD heap. +
      3. Select the best permutation. +
      4. Repeat 3 and 4 for all variables. +
      + Returns 1 if successful; 0 otherwise.] + + SideEffects [None] + + SeeAlso [] + +******************************************************************************/ +int +cuddZddSifting( + DdManager * table, + int lower, + int upper) +{ + int i; + int *var; + int size; + int x; + int result; +#ifdef DD_STATS + int previousSize; +#endif + + size = table->sizeZ; + + /* Find order in which to sift variables. */ + var = NULL; + zdd_entry = ALLOC(int, size); + if (zdd_entry == NULL) { + table->errorCode = CUDD_MEMORY_OUT; + goto cuddZddSiftingOutOfMem; + } + var = ALLOC(int, size); + if (var == NULL) { + table->errorCode = CUDD_MEMORY_OUT; + goto cuddZddSiftingOutOfMem; + } + + for (i = 0; i < size; i++) { + x = table->permZ[i]; + zdd_entry[i] = table->subtableZ[x].keys; + var[i] = i; + } + + qsort((void *)var, size, sizeof(int), (DD_QSFP)cuddZddUniqueCompare); + + /* Now sift. */ + for (i = 0; i < ddMin(table->siftMaxVar, size); i++) { + if (zddTotalNumberSwapping >= table->siftMaxSwap) + break; + if (util_cpu_time() - table->startTime > table->timeLimit) { + table->autoDynZ = 0; /* prevent further reordering */ + break; + } + x = table->permZ[var[i]]; + if (x < lower || x > upper) continue; +#ifdef DD_STATS + previousSize = table->keysZ; +#endif + result = cuddZddSiftingAux(table, x, lower, upper); + if (!result) + goto cuddZddSiftingOutOfMem; +#ifdef DD_STATS + if (table->keysZ < (unsigned) previousSize) { + (void) fprintf(table->out,"-"); + } else if (table->keysZ > (unsigned) previousSize) { + (void) fprintf(table->out,"+"); /* should never happen */ + (void) fprintf(table->out,"\nSize increased from %d to %d while sifting variable %d\n", previousSize, table->keysZ , var[i]); + } else { + (void) fprintf(table->out,"="); + } + fflush(table->out); +#endif + } + + FREE(var); + FREE(zdd_entry); + + return(1); + + cuddZddSiftingOutOfMem: + + if (zdd_entry != NULL) FREE(zdd_entry); + if (var != NULL) FREE(var); + + return(0); + +} /* end of cuddZddSifting */ + + +/*---------------------------------------------------------------------------*/ +/* Definition of static functions */ +/*---------------------------------------------------------------------------*/ + + +/**Function******************************************************************** + + Synopsis [Swaps any two variables.] + + Description [Swaps any two variables. Returns the set of moves.] + + SideEffects [None] + + SeeAlso [] + +******************************************************************************/ +static Move * +zddSwapAny( + DdManager * table, + int x, + int y) +{ + Move *move, *moves; + int tmp, size; + int x_ref, y_ref; + int x_next, y_next; + int limit_size; + + if (x > y) { /* make x precede y */ + tmp = x; x = y; y = tmp; + } + + x_ref = x; y_ref = y; + + x_next = cuddZddNextHigh(table, x); + y_next = cuddZddNextLow(table, y); + moves = NULL; + limit_size = table->keysZ; + + for (;;) { + if (x_next == y_next) { /* x < x_next = y_next < y */ + size = cuddZddSwapInPlace(table, x, x_next); + if (size == 0) + goto zddSwapAnyOutOfMem; + move = (Move *) cuddDynamicAllocNode(table); + if (move == NULL) + goto zddSwapAnyOutOfMem; + move->x = x; + move->y = x_next; + move->size = size; + move->next = moves; + moves = move; + + size = cuddZddSwapInPlace(table, y_next, y); + if (size == 0) + goto zddSwapAnyOutOfMem; + move = (Move *)cuddDynamicAllocNode(table); + if (move == NULL) + goto zddSwapAnyOutOfMem; + move->x = y_next; + move->y = y; + move->size = size; + move->next = moves; + moves = move; + + size = cuddZddSwapInPlace(table, x, x_next); + if (size == 0) + goto zddSwapAnyOutOfMem; + move = (Move *)cuddDynamicAllocNode(table); + if (move == NULL) + goto zddSwapAnyOutOfMem; + move->x = x; + move->y = x_next; + move->size = size; + move->next = moves; + moves = move; + + tmp = x; x = y; y = tmp; + + } else if (x == y_next) { /* x = y_next < y = x_next */ + size = cuddZddSwapInPlace(table, x, x_next); + if (size == 0) + goto zddSwapAnyOutOfMem; + move = (Move *)cuddDynamicAllocNode(table); + if (move == NULL) + goto zddSwapAnyOutOfMem; + move->x = x; + move->y = x_next; + move->size = size; + move->next = moves; + moves = move; + + tmp = x; x = y; y = tmp; + } else { + size = cuddZddSwapInPlace(table, x, x_next); + if (size == 0) + goto zddSwapAnyOutOfMem; + move = (Move *)cuddDynamicAllocNode(table); + if (move == NULL) + goto zddSwapAnyOutOfMem; + move->x = x; + move->y = x_next; + move->size = size; + move->next = moves; + moves = move; + + size = cuddZddSwapInPlace(table, y_next, y); + if (size == 0) + goto zddSwapAnyOutOfMem; + move = (Move *)cuddDynamicAllocNode(table); + if (move == NULL) + goto zddSwapAnyOutOfMem; + move->x = y_next; + move->y = y; + move->size = size; + move->next = moves; + moves = move; + + x = x_next; y = y_next; + } + + x_next = cuddZddNextHigh(table, x); + y_next = cuddZddNextLow(table, y); + if (x_next > y_ref) + break; /* if x == y_ref */ + + if ((double) size > table->maxGrowth * (double) limit_size) + break; + if (size < limit_size) + limit_size = size; + } + if (y_next >= x_ref) { + size = cuddZddSwapInPlace(table, y_next, y); + if (size == 0) + goto zddSwapAnyOutOfMem; + move = (Move *)cuddDynamicAllocNode(table); + if (move == NULL) + goto zddSwapAnyOutOfMem; + move->x = y_next; + move->y = y; + move->size = size; + move->next = moves; + moves = move; + } + + return(moves); + + zddSwapAnyOutOfMem: + while (moves != NULL) { + move = moves->next; + cuddDeallocMove(table, moves); + moves = move; + } + return(NULL); + +} /* end of zddSwapAny */ + + +/**Function******************************************************************** + + Synopsis [Given xLow <= x <= xHigh moves x up and down between the + boundaries.] + + Description [Given xLow <= x <= xHigh moves x up and down between the + boundaries. Finds the best position and does the required changes. + Returns 1 if successful; 0 otherwise.] + + SideEffects [None] + + SeeAlso [] + +******************************************************************************/ +static int +cuddZddSiftingAux( + DdManager * table, + int x, + int x_low, + int x_high) +{ + Move *move; + Move *moveUp; /* list of up move */ + Move *moveDown; /* list of down move */ + + int initial_size; + int result; + + initial_size = table->keysZ; + +#ifdef DD_DEBUG + assert(table->subtableZ[x].keys > 0); +#endif + + moveDown = NULL; + moveUp = NULL; + + if (x == x_low) { + moveDown = cuddZddSiftingDown(table, x, x_high, initial_size); + /* after that point x --> x_high */ + if (moveDown == NULL) + goto cuddZddSiftingAuxOutOfMem; + result = cuddZddSiftingBackward(table, moveDown, + initial_size); + /* move backward and stop at best position */ + if (!result) + goto cuddZddSiftingAuxOutOfMem; + + } + else if (x == x_high) { + moveUp = cuddZddSiftingUp(table, x, x_low, initial_size); + /* after that point x --> x_low */ + if (moveUp == NULL) + goto cuddZddSiftingAuxOutOfMem; + result = cuddZddSiftingBackward(table, moveUp, initial_size); + /* move backward and stop at best position */ + if (!result) + goto cuddZddSiftingAuxOutOfMem; + } + else if ((x - x_low) > (x_high - x)) { + /* must go down first:shorter */ + moveDown = cuddZddSiftingDown(table, x, x_high, initial_size); + /* after that point x --> x_high */ + if (moveDown == NULL) + goto cuddZddSiftingAuxOutOfMem; + moveUp = cuddZddSiftingUp(table, moveDown->y, x_low, + initial_size); + if (moveUp == NULL) + goto cuddZddSiftingAuxOutOfMem; + result = cuddZddSiftingBackward(table, moveUp, initial_size); + /* move backward and stop at best position */ + if (!result) + goto cuddZddSiftingAuxOutOfMem; + } + else { + moveUp = cuddZddSiftingUp(table, x, x_low, initial_size); + /* after that point x --> x_high */ + if (moveUp == NULL) + goto cuddZddSiftingAuxOutOfMem; + moveDown = cuddZddSiftingDown(table, moveUp->x, x_high, + initial_size); + /* then move up */ + if (moveDown == NULL) + goto cuddZddSiftingAuxOutOfMem; + result = cuddZddSiftingBackward(table, moveDown, + initial_size); + /* move backward and stop at best position */ + if (!result) + goto cuddZddSiftingAuxOutOfMem; + } + + while (moveDown != NULL) { + move = moveDown->next; + cuddDeallocMove(table, moveDown); + moveDown = move; + } + while (moveUp != NULL) { + move = moveUp->next; + cuddDeallocMove(table, moveUp); + moveUp = move; + } + + return(1); + + cuddZddSiftingAuxOutOfMem: + while (moveDown != NULL) { + move = moveDown->next; + cuddDeallocMove(table, moveDown); + moveDown = move; + } + while (moveUp != NULL) { + move = moveUp->next; + cuddDeallocMove(table, moveUp); + moveUp = move; + } + + return(0); + +} /* end of cuddZddSiftingAux */ + + +/**Function******************************************************************** + + Synopsis [Sifts a variable up.] + + Description [Sifts a variable up. Moves y up until either it reaches + the bound (x_low) or the size of the ZDD heap increases too much. + Returns the set of moves in case of success; NULL if memory is full.] + + SideEffects [None] + + SeeAlso [] + +******************************************************************************/ +static Move * +cuddZddSiftingUp( + DdManager * table, + int x, + int x_low, + int initial_size) +{ + Move *moves; + Move *move; + int y; + int size; + int limit_size = initial_size; + + moves = NULL; + y = cuddZddNextLow(table, x); + while (y >= x_low) { + size = cuddZddSwapInPlace(table, y, x); + if (size == 0) + goto cuddZddSiftingUpOutOfMem; + move = (Move *)cuddDynamicAllocNode(table); + if (move == NULL) + goto cuddZddSiftingUpOutOfMem; + move->x = y; + move->y = x; + move->size = size; + move->next = moves; + moves = move; + + if ((double)size > (double)limit_size * table->maxGrowth) + break; + if (size < limit_size) + limit_size = size; + + x = y; + y = cuddZddNextLow(table, x); + } + return(moves); + + cuddZddSiftingUpOutOfMem: + while (moves != NULL) { + move = moves->next; + cuddDeallocMove(table, moves); + moves = move; + } + return(NULL); + +} /* end of cuddZddSiftingUp */ + + +/**Function******************************************************************** + + Synopsis [Sifts a variable down.] + + Description [Sifts a variable down. Moves x down until either it + reaches the bound (x_high) or the size of the ZDD heap increases too + much. Returns the set of moves in case of success; NULL if memory is + full.] + + SideEffects [None] + + SeeAlso [] + +******************************************************************************/ +static Move * +cuddZddSiftingDown( + DdManager * table, + int x, + int x_high, + int initial_size) +{ + Move *moves; + Move *move; + int y; + int size; + int limit_size = initial_size; + + moves = NULL; + y = cuddZddNextHigh(table, x); + while (y <= x_high) { + size = cuddZddSwapInPlace(table, x, y); + if (size == 0) + goto cuddZddSiftingDownOutOfMem; + move = (Move *)cuddDynamicAllocNode(table); + if (move == NULL) + goto cuddZddSiftingDownOutOfMem; + move->x = x; + move->y = y; + move->size = size; + move->next = moves; + moves = move; + + if ((double)size > (double)limit_size * table->maxGrowth) + break; + if (size < limit_size) + limit_size = size; + + x = y; + y = cuddZddNextHigh(table, x); + } + return(moves); + + cuddZddSiftingDownOutOfMem: + while (moves != NULL) { + move = moves->next; + cuddDeallocMove(table, moves); + moves = move; + } + return(NULL); + +} /* end of cuddZddSiftingDown */ + + +/**Function******************************************************************** + + Synopsis [Given a set of moves, returns the ZDD heap to the position + giving the minimum size.] + + Description [Given a set of moves, returns the ZDD heap to the + position giving the minimum size. In case of ties, returns to the + closest position giving the minimum size. Returns 1 in case of + success; 0 otherwise.] + + SideEffects [None] + + SeeAlso [] + +******************************************************************************/ +static int +cuddZddSiftingBackward( + DdManager * table, + Move * moves, + int size) +{ + int i; + int i_best; + Move *move; + int res; + + /* Find the minimum size among moves. */ + i_best = -1; + for (move = moves, i = 0; move != NULL; move = move->next, i++) { + if (move->size < size) { + i_best = i; + size = move->size; + } + } + + for (move = moves, i = 0; move != NULL; move = move->next, i++) { + if (i == i_best) + break; + res = cuddZddSwapInPlace(table, move->x, move->y); + if (!res) + return(0); + if (i_best == -1 && res == size) + break; + } + + return(1); + +} /* end of cuddZddSiftingBackward */ + + +/**Function******************************************************************** + + Synopsis [Prepares the ZDD heap for dynamic reordering.] + + Description [Prepares the ZDD heap for dynamic reordering. Does + garbage collection, to guarantee that there are no dead nodes; + and clears the cache, which is invalidated by dynamic reordering.] + + SideEffects [None] + +******************************************************************************/ +static void +zddReorderPreprocess( + DdManager * table) +{ + + /* Clear the cache. */ + cuddCacheFlush(table); + + /* Eliminate dead nodes. Do not scan the cache again. */ + cuddGarbageCollect(table,0); + + return; + +} /* end of ddReorderPreprocess */ + + +/**Function******************************************************************** + + Synopsis [Shrinks almost empty ZDD subtables at the end of reordering + to guarantee that they have a reasonable load factor.] + + Description [Shrinks almost empty subtables at the end of reordering to + guarantee that they have a reasonable load factor. However, if there many + nodes are being reclaimed, then no resizing occurs. Returns 1 in case of + success; 0 otherwise.] + + SideEffects [None] + +******************************************************************************/ +static int +zddReorderPostprocess( + DdManager * table) +{ + int i, j, posn; + DdNodePtr *nodelist, *oldnodelist; + DdNode *node, *next; + unsigned int slots, oldslots; + extern DD_OOMFP MMoutOfMemory; + DD_OOMFP saveHandler; + +#ifdef DD_VERBOSE + (void) fflush(table->out); +#endif + + /* If we have very many reclaimed nodes, we do not want to shrink + ** the subtables, because this will lead to more garbage + ** collections. More garbage collections mean shorter mean life for + ** nodes with zero reference count; hence lower probability of finding + ** a result in the cache. + */ + if (table->reclaimed > table->allocated * 0.5) return(1); + + /* Resize subtables. */ + for (i = 0; i < table->sizeZ; i++) { + int shift; + oldslots = table->subtableZ[i].slots; + if (oldslots < table->subtableZ[i].keys * DD_MAX_SUBTABLE_SPARSITY || + oldslots <= table->initSlots) continue; + oldnodelist = table->subtableZ[i].nodelist; + slots = oldslots >> 1; + saveHandler = MMoutOfMemory; + MMoutOfMemory = Cudd_OutOfMem; + nodelist = ALLOC(DdNodePtr, slots); + MMoutOfMemory = saveHandler; + if (nodelist == NULL) { + return(1); + } + table->subtableZ[i].nodelist = nodelist; + table->subtableZ[i].slots = slots; + table->subtableZ[i].shift++; + table->subtableZ[i].maxKeys = slots * DD_MAX_SUBTABLE_DENSITY; +#ifdef DD_VERBOSE + (void) fprintf(table->err, + "shrunk layer %d (%d keys) from %d to %d slots\n", + i, table->subtableZ[i].keys, oldslots, slots); +#endif + + for (j = 0; (unsigned) j < slots; j++) { + nodelist[j] = NULL; + } + shift = table->subtableZ[i].shift; + for (j = 0; (unsigned) j < oldslots; j++) { + node = oldnodelist[j]; + while (node != NULL) { + next = node->next; + posn = ddHash(cuddT(node), cuddE(node), shift); + node->next = nodelist[posn]; + nodelist[posn] = node; + node = next; + } + } + FREE(oldnodelist); + + table->memused += (slots - oldslots) * sizeof(DdNode *); + table->slots += slots - oldslots; + table->minDead = (unsigned) (table->gcFrac * (double) table->slots); + table->cacheSlack = (int) ddMin(table->maxCacheHard, + DD_MAX_CACHE_TO_SLOTS_RATIO*table->slots) - + 2 * (int) table->cacheSlots; + } + /* We don't look at the constant subtable, because it is not + ** affected by reordering. + */ + + return(1); + +} /* end of zddReorderPostprocess */ + + +/**Function******************************************************************** + + Synopsis [Reorders ZDD variables according to a given permutation.] + + Description [Reorders ZDD variables according to a given permutation. + The i-th permutation array contains the index of the variable that + should be brought to the i-th level. zddShuffle assumes that no + dead nodes are present. The reordering is achieved by a series of + upward sifts. Returns 1 if successful; 0 otherwise.] + + SideEffects [None] + + SeeAlso [] + +******************************************************************************/ +static int +zddShuffle( + DdManager * table, + int * permutation) +{ + int index; + int level; + int position; + int numvars; + int result; +#ifdef DD_STATS + unsigned long localTime; + int initialSize; + int finalSize; + int previousSize; +#endif + + zddTotalNumberSwapping = 0; +#ifdef DD_STATS + localTime = util_cpu_time(); + initialSize = table->keysZ; + (void) fprintf(table->out,"#:I_SHUFFLE %8d: initial size\n", + initialSize); +#endif + + numvars = table->sizeZ; + + for (level = 0; level < numvars; level++) { + index = permutation[level]; + position = table->permZ[index]; +#ifdef DD_STATS + previousSize = table->keysZ; +#endif + result = zddSiftUp(table,position,level); + if (!result) return(0); +#ifdef DD_STATS + if (table->keysZ < (unsigned) previousSize) { + (void) fprintf(table->out,"-"); + } else if (table->keysZ > (unsigned) previousSize) { + (void) fprintf(table->out,"+"); /* should never happen */ + } else { + (void) fprintf(table->out,"="); + } + fflush(table->out); +#endif + } + +#ifdef DD_STATS + (void) fprintf(table->out,"\n"); + finalSize = table->keysZ; + (void) fprintf(table->out,"#:F_SHUFFLE %8d: final size\n",finalSize); + (void) fprintf(table->out,"#:T_SHUFFLE %8g: total time (sec)\n", + ((double)(util_cpu_time() - localTime)/1000.0)); + (void) fprintf(table->out,"#:N_SHUFFLE %8d: total swaps\n", + zddTotalNumberSwapping); +#endif + + return(1); + +} /* end of zddShuffle */ + + +/**Function******************************************************************** + + Synopsis [Moves one ZDD variable up.] + + Description [Takes a ZDD variable from position x and sifts it up to + position xLow; xLow should be less than or equal to x. + Returns 1 if successful; 0 otherwise] + + SideEffects [None] + + SeeAlso [] + +******************************************************************************/ +static int +zddSiftUp( + DdManager * table, + int x, + int xLow) +{ + int y; + int size; + + y = cuddZddNextLow(table,x); + while (y >= xLow) { + size = cuddZddSwapInPlace(table,y,x); + if (size == 0) { + return(0); + } + x = y; + y = cuddZddNextLow(table,x); + } + return(1); + +} /* end of zddSiftUp */ + + +/**Function******************************************************************** + + Synopsis [Fixes the ZDD variable group tree after a shuffle.] + + Description [Fixes the ZDD variable group tree after a + shuffle. Assumes that the order of the variables in a terminal node + has not been changed.] + + SideEffects [Changes the ZDD variable group tree.] + + SeeAlso [] + +******************************************************************************/ +static void +zddFixTree( + DdManager * table, + MtrNode * treenode) +{ + if (treenode == NULL) return; + treenode->low = ((int) treenode->index < table->sizeZ) ? + table->permZ[treenode->index] : treenode->index; + if (treenode->child != NULL) { + zddFixTree(table, treenode->child); + } + if (treenode->younger != NULL) + zddFixTree(table, treenode->younger); + if (treenode->parent != NULL && treenode->low < treenode->parent->low) { + treenode->parent->low = treenode->low; + treenode->parent->index = treenode->index; + } + return; + +} /* end of zddFixTree */ + +/**CFile*********************************************************************** + + FileName [cuddZddSetop.c] + + PackageName [cudd] + + Synopsis [Set operations on ZDDs.] + + Description [External procedures included in this module: +
        +
      • Cudd_zddIte() +
      • Cudd_zddUnion() +
      • Cudd_zddIntersect() +
      • Cudd_zddDiff() +
      • Cudd_zddDiffConst() +
      • Cudd_zddSubset1() +
      • Cudd_zddSubset0() +
      • Cudd_zddChange() +
      + Internal procedures included in this module: +
        +
      • cuddZddIte() +
      • cuddZddUnion() +
      • cuddZddIntersect() +
      • cuddZddDiff() +
      • cuddZddChangeAux() +
      • cuddZddSubset1() +
      • cuddZddSubset0() +
      + Static procedures included in this module: +
        +
      • zdd_subset1_aux() +
      • zdd_subset0_aux() +
      • zddVarToConst() +
      + ] + + SeeAlso [] + + Author [Hyong-Kyoon Shin, In-Ho Moon] + + Copyright [Copyright (c) 1995-2012, Regents of the University of Colorado + + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + Neither the name of the University of Colorado nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE.] + +******************************************************************************/ + + +/*---------------------------------------------------------------------------*/ +/* Constant declarations */ +/*---------------------------------------------------------------------------*/ + + +/*---------------------------------------------------------------------------*/ +/* Stucture declarations */ +/*---------------------------------------------------------------------------*/ + + +/*---------------------------------------------------------------------------*/ +/* Type declarations */ +/*---------------------------------------------------------------------------*/ + + +/*---------------------------------------------------------------------------*/ +/* Variable declarations */ +/*---------------------------------------------------------------------------*/ + +//#ifndef lint +//static char rcsid[] DD_UNUSED = "$Id: cuddZddSetop.c,v 1.26 2012/02/05 01:07:19 fabio Exp $"; +//#endif + +/*---------------------------------------------------------------------------*/ +/* Macro declarations */ +/*---------------------------------------------------------------------------*/ + +#ifdef __cplusplus +extern "C" { +#endif + +/**AutomaticStart*************************************************************/ + +/*---------------------------------------------------------------------------*/ +/* Static function prototypes */ +/*---------------------------------------------------------------------------*/ + +static DdNode * zdd_subset1_aux (DdManager *zdd, DdNode *P, DdNode *zvar); +static DdNode * zdd_subset0_aux (DdManager *zdd, DdNode *P, DdNode *zvar); +static void zddVarToConst (DdNode *f, DdNode **gp, DdNode **hp, DdNode *base, DdNode *empty); + +/**AutomaticEnd***************************************************************/ + +#ifdef __cplusplus +} +#endif + +/*---------------------------------------------------------------------------*/ +/* Definition of exported functions */ +/*---------------------------------------------------------------------------*/ + + +/**Function******************************************************************** + + Synopsis [Performs the inclusion test for ZDDs (P implies Q).] + + Description [Inclusion test for ZDDs (P implies Q). No new nodes are + generated by this procedure. Returns empty if true; + a valid pointer different from empty or DD_NON_CONSTANT otherwise.] + + SideEffects [None] + + SeeAlso [Cudd_zddDiff] + +******************************************************************************/ +DdNode * +Cudd_zddDiffConst( + DdManager * zdd, + DdNode * P, + DdNode * Q) +{ + int p_top, q_top; + DdNode *empty = DD_ZERO(zdd), *t, *res; + DdManager *table = zdd; + + statLine(zdd); + if (P == empty) + return(empty); + if (Q == empty) + return(P); + if (P == Q) + return(empty); + + /* Check cache. The cache is shared by cuddZddDiff(). */ + res = cuddCacheLookup2Zdd(table, cuddZddDiff, P, Q); + if (res != NULL) + return(res); + + if (cuddIsConstant(P)) + p_top = P->index; + else + p_top = zdd->permZ[P->index]; + if (cuddIsConstant(Q)) + q_top = Q->index; + else + q_top = zdd->permZ[Q->index]; + if (p_top < q_top) { + res = DD_NON_CONSTANT; + } else if (p_top > q_top) { + res = Cudd_zddDiffConst(zdd, P, cuddE(Q)); + } else { + t = Cudd_zddDiffConst(zdd, cuddT(P), cuddT(Q)); + if (t != empty) + res = DD_NON_CONSTANT; + else + res = Cudd_zddDiffConst(zdd, cuddE(P), cuddE(Q)); + } + + cuddCacheInsert2(table, cuddZddDiff, P, Q, res); + + return(res); + +} /* end of Cudd_zddDiffConst */ + + +/*---------------------------------------------------------------------------*/ +/* Definition of internal functions */ +/*---------------------------------------------------------------------------*/ + + +/**Function******************************************************************** + + Synopsis [Performs the recursive step of Cudd_zddIte.] + + Description [] + + SideEffects [None] + + SeeAlso [] + +******************************************************************************/ +DdNode * +cuddZddIte( + DdManager * dd, + DdNode * f, + DdNode * g, + DdNode * h) +{ + DdNode *tautology, *empty; + DdNode *r,*Gv,*Gvn,*Hv,*Hvn,*t,*e; + unsigned int topf,topg,toph,v,top; + int index; + + statLine(dd); + /* Trivial cases. */ + /* One variable cases. */ + if (f == (empty = DD_ZERO(dd))) { /* ITE(0,G,H) = H */ + return(h); + } + topf = cuddIZ(dd,f->index); + topg = cuddIZ(dd,g->index); + toph = cuddIZ(dd,h->index); + v = ddMin(topg,toph); + top = ddMin(topf,v); + + tautology = (top == CUDD_MAXINDEX) ? DD_ONE(dd) : dd->univ[top]; + if (f == tautology) { /* ITE(1,G,H) = G */ + return(g); + } + + /* From now on, f is known to not be a constant. */ + zddVarToConst(f,&g,&h,tautology,empty); + + /* Check remaining one variable cases. */ + if (g == h) { /* ITE(F,G,G) = G */ + return(g); + } + + if (g == tautology) { /* ITE(F,1,0) = F */ + if (h == empty) return(f); + } + + /* Check cache. */ + r = cuddCacheLookupZdd(dd,DD_ZDD_ITE_TAG,f,g,h); + if (r != NULL) { + return(r); + } + + /* Recompute these because they may have changed in zddVarToConst. */ + topg = cuddIZ(dd,g->index); + toph = cuddIZ(dd,h->index); + v = ddMin(topg,toph); + + if (topf < v) { + r = cuddZddIte(dd,cuddE(f),g,h); + if (r == NULL) return(NULL); + } else if (topf > v) { + if (topg > v) { + Gvn = g; + index = h->index; + } else { + Gvn = cuddE(g); + index = g->index; + } + if (toph > v) { + Hv = empty; Hvn = h; + } else { + Hv = cuddT(h); Hvn = cuddE(h); + } + e = cuddZddIte(dd,f,Gvn,Hvn); + if (e == NULL) return(NULL); + cuddRef(e); + r = cuddZddGetNode(dd,index,Hv,e); + if (r == NULL) { + Cudd_RecursiveDerefZdd(dd,e); + return(NULL); + } + cuddDeref(e); + } else { + index = f->index; + if (topg > v) { + Gv = empty; Gvn = g; + } else { + Gv = cuddT(g); Gvn = cuddE(g); + } + if (toph > v) { + Hv = empty; Hvn = h; + } else { + Hv = cuddT(h); Hvn = cuddE(h); + } + e = cuddZddIte(dd,cuddE(f),Gvn,Hvn); + if (e == NULL) return(NULL); + cuddRef(e); + t = cuddZddIte(dd,cuddT(f),Gv,Hv); + if (t == NULL) { + Cudd_RecursiveDerefZdd(dd,e); + return(NULL); + } + cuddRef(t); + r = cuddZddGetNode(dd,index,t,e); + if (r == NULL) { + Cudd_RecursiveDerefZdd(dd,e); + Cudd_RecursiveDerefZdd(dd,t); + return(NULL); + } + cuddDeref(t); + cuddDeref(e); + } + + cuddCacheInsert(dd,DD_ZDD_ITE_TAG,f,g,h,r); + + return(r); + +} /* end of cuddZddIte */ + + +/**Function******************************************************************** + + Synopsis [Performs the recursive step of Cudd_zddUnion.] + + Description [] + + SideEffects [None] + + SeeAlso [] + +******************************************************************************/ +DdNode * +cuddZddUnion( + DdManager * zdd, + DdNode * P, + DdNode * Q) +{ + int p_top, q_top; + DdNode *empty = DD_ZERO(zdd), *t, *e, *res; + DdManager *table = zdd; + + statLine(zdd); + if (P == empty) + return(Q); + if (Q == empty) + return(P); + if (P == Q) + return(P); + + /* Check cache */ + res = cuddCacheLookup2Zdd(table, cuddZddUnion, P, Q); + if (res != NULL) + return(res); + + if (cuddIsConstant(P)) + p_top = P->index; + else + p_top = zdd->permZ[P->index]; + if (cuddIsConstant(Q)) + q_top = Q->index; + else + q_top = zdd->permZ[Q->index]; + if (p_top < q_top) { + e = cuddZddUnion(zdd, cuddE(P), Q); + if (e == NULL) return (NULL); + cuddRef(e); + res = cuddZddGetNode(zdd, P->index, cuddT(P), e); + if (res == NULL) { + Cudd_RecursiveDerefZdd(table, e); + return(NULL); + } + cuddDeref(e); + } else if (p_top > q_top) { + e = cuddZddUnion(zdd, P, cuddE(Q)); + if (e == NULL) return(NULL); + cuddRef(e); + res = cuddZddGetNode(zdd, Q->index, cuddT(Q), e); + if (res == NULL) { + Cudd_RecursiveDerefZdd(table, e); + return(NULL); + } + cuddDeref(e); + } else { + t = cuddZddUnion(zdd, cuddT(P), cuddT(Q)); + if (t == NULL) return(NULL); + cuddRef(t); + e = cuddZddUnion(zdd, cuddE(P), cuddE(Q)); + if (e == NULL) { + Cudd_RecursiveDerefZdd(table, t); + return(NULL); + } + cuddRef(e); + res = cuddZddGetNode(zdd, P->index, t, e); + if (res == NULL) { + Cudd_RecursiveDerefZdd(table, t); + Cudd_RecursiveDerefZdd(table, e); + return(NULL); + } + cuddDeref(t); + cuddDeref(e); + } + + cuddCacheInsert2(table, cuddZddUnion, P, Q, res); + + return(res); + +} /* end of cuddZddUnion */ + + +/**Function******************************************************************** + + Synopsis [Performs the recursive step of Cudd_zddIntersect.] + + Description [] + + SideEffects [None] + + SeeAlso [] + +******************************************************************************/ +DdNode * +cuddZddIntersect( + DdManager * zdd, + DdNode * P, + DdNode * Q) +{ + int p_top, q_top; + DdNode *empty = DD_ZERO(zdd), *t, *e, *res; + DdManager *table = zdd; + + statLine(zdd); + if (P == empty) + return(empty); + if (Q == empty) + return(empty); + if (P == Q) + return(P); + + /* Check cache. */ + res = cuddCacheLookup2Zdd(table, cuddZddIntersect, P, Q); + if (res != NULL) + return(res); + + if (cuddIsConstant(P)) + p_top = P->index; + else + p_top = zdd->permZ[P->index]; + if (cuddIsConstant(Q)) + q_top = Q->index; + else + q_top = zdd->permZ[Q->index]; + if (p_top < q_top) { + res = cuddZddIntersect(zdd, cuddE(P), Q); + if (res == NULL) return(NULL); + } else if (p_top > q_top) { + res = cuddZddIntersect(zdd, P, cuddE(Q)); + if (res == NULL) return(NULL); + } else { + t = cuddZddIntersect(zdd, cuddT(P), cuddT(Q)); + if (t == NULL) return(NULL); + cuddRef(t); + e = cuddZddIntersect(zdd, cuddE(P), cuddE(Q)); + if (e == NULL) { + Cudd_RecursiveDerefZdd(table, t); + return(NULL); + } + cuddRef(e); + res = cuddZddGetNode(zdd, P->index, t, e); + if (res == NULL) { + Cudd_RecursiveDerefZdd(table, t); + Cudd_RecursiveDerefZdd(table, e); + return(NULL); + } + cuddDeref(t); + cuddDeref(e); + } + + cuddCacheInsert2(table, cuddZddIntersect, P, Q, res); + + return(res); + +} /* end of cuddZddIntersect */ + + +/**Function******************************************************************** + + Synopsis [Performs the recursive step of Cudd_zddDiff.] + + Description [] + + SideEffects [None] + + SeeAlso [] + +******************************************************************************/ +DdNode * +cuddZddDiff( + DdManager * zdd, + DdNode * P, + DdNode * Q) +{ + int p_top, q_top; + DdNode *empty = DD_ZERO(zdd), *t, *e, *res; + DdManager *table = zdd; + + statLine(zdd); + if (P == empty) + return(empty); + if (Q == empty) + return(P); + if (P == Q) + return(empty); + + /* Check cache. The cache is shared by Cudd_zddDiffConst(). */ + res = cuddCacheLookup2Zdd(table, cuddZddDiff, P, Q); + if (res != NULL && res != DD_NON_CONSTANT) + return(res); + + if (cuddIsConstant(P)) + p_top = P->index; + else + p_top = zdd->permZ[P->index]; + if (cuddIsConstant(Q)) + q_top = Q->index; + else + q_top = zdd->permZ[Q->index]; + if (p_top < q_top) { + e = cuddZddDiff(zdd, cuddE(P), Q); + if (e == NULL) return(NULL); + cuddRef(e); + res = cuddZddGetNode(zdd, P->index, cuddT(P), e); + if (res == NULL) { + Cudd_RecursiveDerefZdd(table, e); + return(NULL); + } + cuddDeref(e); + } else if (p_top > q_top) { + res = cuddZddDiff(zdd, P, cuddE(Q)); + if (res == NULL) return(NULL); + } else { + t = cuddZddDiff(zdd, cuddT(P), cuddT(Q)); + if (t == NULL) return(NULL); + cuddRef(t); + e = cuddZddDiff(zdd, cuddE(P), cuddE(Q)); + if (e == NULL) { + Cudd_RecursiveDerefZdd(table, t); + return(NULL); + } + cuddRef(e); + res = cuddZddGetNode(zdd, P->index, t, e); + if (res == NULL) { + Cudd_RecursiveDerefZdd(table, t); + Cudd_RecursiveDerefZdd(table, e); + return(NULL); + } + cuddDeref(t); + cuddDeref(e); + } + + cuddCacheInsert2(table, cuddZddDiff, P, Q, res); + + return(res); + +} /* end of cuddZddDiff */ + + +/**Function******************************************************************** + + Synopsis [Performs the recursive step of Cudd_zddChange.] + + Description [] + + SideEffects [None] + + SeeAlso [] + +******************************************************************************/ +DdNode * +cuddZddChangeAux( + DdManager * zdd, + DdNode * P, + DdNode * zvar) +{ + int top_var, level; + DdNode *res, *t, *e; + DdNode *base = DD_ONE(zdd); + DdNode *empty = DD_ZERO(zdd); + + statLine(zdd); + if (P == empty) + return(empty); + if (P == base) + return(zvar); + + /* Check cache. */ + res = cuddCacheLookup2Zdd(zdd, cuddZddChangeAux, P, zvar); + if (res != NULL) + return(res); + + top_var = zdd->permZ[P->index]; + level = zdd->permZ[zvar->index]; + + if (top_var > level) { + res = cuddZddGetNode(zdd, zvar->index, P, DD_ZERO(zdd)); + if (res == NULL) return(NULL); + } else if (top_var == level) { + res = cuddZddGetNode(zdd, zvar->index, cuddE(P), cuddT(P)); + if (res == NULL) return(NULL); + } else { + t = cuddZddChangeAux(zdd, cuddT(P), zvar); + if (t == NULL) return(NULL); + cuddRef(t); + e = cuddZddChangeAux(zdd, cuddE(P), zvar); + if (e == NULL) { + Cudd_RecursiveDerefZdd(zdd, t); + return(NULL); + } + cuddRef(e); + res = cuddZddGetNode(zdd, P->index, t, e); + if (res == NULL) { + Cudd_RecursiveDerefZdd(zdd, t); + Cudd_RecursiveDerefZdd(zdd, e); + return(NULL); + } + cuddDeref(t); + cuddDeref(e); + } + + cuddCacheInsert2(zdd, cuddZddChangeAux, P, zvar, res); + + return(res); + +} /* end of cuddZddChangeAux */ + + +/**Function******************************************************************** + + Synopsis [Computes the positive cofactor of a ZDD w.r.t. a variable.] + + Description [Computes the positive cofactor of a ZDD w.r.t. a + variable. In terms of combinations, the result is the set of all + combinations in which the variable is asserted. Returns a pointer to + the result if successful; NULL otherwise. cuddZddSubset1 performs + the same function as Cudd_zddSubset1, but does not restart if + reordering has taken place. Therefore it can be called from within a + recursive procedure.] + + SideEffects [None] + + SeeAlso [cuddZddSubset0 Cudd_zddSubset1] + +******************************************************************************/ +DdNode * +cuddZddSubset1( + DdManager * dd, + DdNode * P, + int var) +{ + DdNode *zvar, *r; + DdNode *base, *empty; + + base = DD_ONE(dd); + empty = DD_ZERO(dd); + + zvar = cuddUniqueInterZdd(dd, var, base, empty); + if (zvar == NULL) { + return(NULL); + } else { + cuddRef(zvar); + r = zdd_subset1_aux(dd, P, zvar); + if (r == NULL) { + Cudd_RecursiveDerefZdd(dd, zvar); + return(NULL); + } + cuddRef(r); + Cudd_RecursiveDerefZdd(dd, zvar); + } + + cuddDeref(r); + return(r); + +} /* end of cuddZddSubset1 */ + + +/**Function******************************************************************** + + Synopsis [Computes the negative cofactor of a ZDD w.r.t. a variable.] + + Description [Computes the negative cofactor of a ZDD w.r.t. a + variable. In terms of combinations, the result is the set of all + combinations in which the variable is negated. Returns a pointer to + the result if successful; NULL otherwise. cuddZddSubset0 performs + the same function as Cudd_zddSubset0, but does not restart if + reordering has taken place. Therefore it can be called from within a + recursive procedure.] + + SideEffects [None] + + SeeAlso [cuddZddSubset1 Cudd_zddSubset0] + +******************************************************************************/ +DdNode * +cuddZddSubset0( + DdManager * dd, + DdNode * P, + int var) +{ + DdNode *zvar, *r; + DdNode *base, *empty; + + base = DD_ONE(dd); + empty = DD_ZERO(dd); + + zvar = cuddUniqueInterZdd(dd, var, base, empty); + if (zvar == NULL) { + return(NULL); + } else { + cuddRef(zvar); + r = zdd_subset0_aux(dd, P, zvar); + if (r == NULL) { + Cudd_RecursiveDerefZdd(dd, zvar); + return(NULL); + } + cuddRef(r); + Cudd_RecursiveDerefZdd(dd, zvar); + } + + cuddDeref(r); + return(r); + +} /* end of cuddZddSubset0 */ + + +/*---------------------------------------------------------------------------*/ +/* Definition of static functions */ +/*---------------------------------------------------------------------------*/ + + +/**Function******************************************************************** + + Synopsis [Performs the recursive step of Cudd_zddSubset1.] + + Description [] + + SideEffects [None] + + SeeAlso [] + +******************************************************************************/ +static DdNode * +zdd_subset1_aux( + DdManager * zdd, + DdNode * P, + DdNode * zvar) +{ + int top_var, level; + DdNode *res, *t, *e; + DdNode *empty; + + statLine(zdd); + empty = DD_ZERO(zdd); + + /* Check cache. */ + res = cuddCacheLookup2Zdd(zdd, zdd_subset1_aux, P, zvar); + if (res != NULL) + return(res); + + if (cuddIsConstant(P)) { + res = empty; + cuddCacheInsert2(zdd, zdd_subset1_aux, P, zvar, res); + return(res); + } + + top_var = zdd->permZ[P->index]; + level = zdd->permZ[zvar->index]; + + if (top_var > level) { + res = empty; + } else if (top_var == level) { + res = cuddT(P); + } else { + t = zdd_subset1_aux(zdd, cuddT(P), zvar); + if (t == NULL) return(NULL); + cuddRef(t); + e = zdd_subset1_aux(zdd, cuddE(P), zvar); + if (e == NULL) { + Cudd_RecursiveDerefZdd(zdd, t); + return(NULL); + } + cuddRef(e); + res = cuddZddGetNode(zdd, P->index, t, e); + if (res == NULL) { + Cudd_RecursiveDerefZdd(zdd, t); + Cudd_RecursiveDerefZdd(zdd, e); + return(NULL); + } + cuddDeref(t); + cuddDeref(e); + } + + cuddCacheInsert2(zdd, zdd_subset1_aux, P, zvar, res); + + return(res); + +} /* end of zdd_subset1_aux */ + + +/**Function******************************************************************** + + Synopsis [Performs the recursive step of Cudd_zddSubset0.] + + Description [] + + SideEffects [None] + + SeeAlso [] + +******************************************************************************/ +static DdNode * +zdd_subset0_aux( + DdManager * zdd, + DdNode * P, + DdNode * zvar) +{ + int top_var, level; + DdNode *res, *t, *e; + + statLine(zdd); + + /* Check cache. */ + res = cuddCacheLookup2Zdd(zdd, zdd_subset0_aux, P, zvar); + if (res != NULL) + return(res); + + if (cuddIsConstant(P)) { + res = P; + cuddCacheInsert2(zdd, zdd_subset0_aux, P, zvar, res); + return(res); + } + + top_var = zdd->permZ[P->index]; + level = zdd->permZ[zvar->index]; + + if (top_var > level) { + res = P; + } + else if (top_var == level) { + res = cuddE(P); + } + else { + t = zdd_subset0_aux(zdd, cuddT(P), zvar); + if (t == NULL) return(NULL); + cuddRef(t); + e = zdd_subset0_aux(zdd, cuddE(P), zvar); + if (e == NULL) { + Cudd_RecursiveDerefZdd(zdd, t); + return(NULL); + } + cuddRef(e); + res = cuddZddGetNode(zdd, P->index, t, e); + if (res == NULL) { + Cudd_RecursiveDerefZdd(zdd, t); + Cudd_RecursiveDerefZdd(zdd, e); + return(NULL); + } + cuddDeref(t); + cuddDeref(e); + } + + cuddCacheInsert2(zdd, zdd_subset0_aux, P, zvar, res); + + return(res); + +} /* end of zdd_subset0_aux */ + + +/**Function******************************************************************** + + Synopsis [Replaces variables with constants if possible (part of + canonical form).] + + Description [] + + SideEffects [None] + + SeeAlso [] + +******************************************************************************/ +static void +zddVarToConst( + DdNode * f, + DdNode ** gp, + DdNode ** hp, + DdNode * base, + DdNode * empty) +{ + DdNode *g = *gp; + DdNode *h = *hp; + + if (f == g) { /* ITE(F,F,H) = ITE(F,1,H) = F + H */ + *gp = base; + } + + if (f == h) { /* ITE(F,G,F) = ITE(F,G,0) = F * G */ + *hp = empty; + } + +} /* end of zddVarToConst */ + +/**CFile*********************************************************************** + + FileName [cuddZddSymm.c] + + PackageName [cudd] + + Synopsis [Functions for symmetry-based ZDD variable reordering.] + + Description [External procedures included in this module: +
        +
      • Cudd_zddSymmProfile() +
      + Internal procedures included in this module: +
        +
      • cuddZddSymmCheck() +
      • cuddZddSymmSifting() +
      • cuddZddSymmSiftingConv() +
      + Static procedures included in this module: +
        +
      • cuddZddUniqueCompare() +
      • cuddZddSymmSiftingAux() +
      • cuddZddSymmSiftingConvAux() +
      • cuddZddSymmSifting_up() +
      • cuddZddSymmSifting_down() +
      • zdd_group_move() +
      • cuddZddSymmSiftingBackward() +
      • zdd_group_move_backward() +
      + ] + + SeeAlso [cuddSymmetry.c] + + Author [Hyong-Kyoon Shin, In-Ho Moon] + + Copyright [Copyright (c) 1995-2012, Regents of the University of Colorado + + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + Neither the name of the University of Colorado nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE.] + +******************************************************************************/ + + +/*---------------------------------------------------------------------------*/ +/* Constant declarations */ +/*---------------------------------------------------------------------------*/ + +#define ZDD_MV_OOM (Move *)1 + +/*---------------------------------------------------------------------------*/ +/* Stucture declarations */ +/*---------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------*/ +/* Type declarations */ +/*---------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------*/ +/* Variable declarations */ +/*---------------------------------------------------------------------------*/ + +//#ifndef lint +//static char rcsid[] DD_UNUSED = "$Id: cuddZddSymm.c,v 1.31 2012/02/05 01:07:19 fabio Exp $"; +//#endif + +extern int *zdd_entry; + +extern int zddTotalNumberSwapping; + +static DdNode *empty; + +/*---------------------------------------------------------------------------*/ +/* Macro declarations */ +/*---------------------------------------------------------------------------*/ + + +/**AutomaticStart*************************************************************/ + +/*---------------------------------------------------------------------------*/ +/* Static function prototypes */ +/*---------------------------------------------------------------------------*/ + +static int cuddZddSymmSiftingAux (DdManager *table, int x, int x_low, int x_high); +static int cuddZddSymmSiftingConvAux (DdManager *table, int x, int x_low, int x_high); +static Move * cuddZddSymmSifting_up (DdManager *table, int x, int x_low, int initial_size); +static Move * cuddZddSymmSifting_down (DdManager *table, int x, int x_high, int initial_size); +static int cuddZddSymmSiftingBackward (DdManager *table, Move *moves, int size); +static int zdd_group_move (DdManager *table, int x, int y, Move **moves); +static int zdd_group_move_backward (DdManager *table, int x, int y); +static void cuddZddSymmSummary (DdManager *table, int lower, int upper, int *symvars, int *symgroups); + +/**AutomaticEnd***************************************************************/ + + +/*---------------------------------------------------------------------------*/ +/* Definition of exported functions */ +/*---------------------------------------------------------------------------*/ + + + +/*---------------------------------------------------------------------------*/ +/* Definition of internal functions */ +/*---------------------------------------------------------------------------*/ + + +/**Function******************************************************************** + + Synopsis [Checks for symmetry of x and y.] + + Description [Checks for symmetry of x and y. Ignores projection + functions, unless they are isolated. Returns 1 in case of + symmetry; 0 otherwise.] + + SideEffects [None] + + SeeAlso [] + +******************************************************************************/ +int +cuddZddSymmCheck( + DdManager * table, + int x, + int y) +{ + int i; + DdNode *f, *f0, *f1, *f01, *f00, *f11, *f10; + int yindex; + int xsymmy = 1; + int xsymmyp = 1; + int arccount = 0; + int TotalRefCount = 0; + int symm_found; + + empty = table->zero; + + yindex = table->invpermZ[y]; + for (i = table->subtableZ[x].slots - 1; i >= 0; i--) { + f = table->subtableZ[x].nodelist[i]; + while (f != NULL) { + /* Find f1, f0, f11, f10, f01, f00 */ + f1 = cuddT(f); + f0 = cuddE(f); + if ((int) f1->index == yindex) { + f11 = cuddT(f1); + f10 = cuddE(f1); + if (f10 != empty) + arccount++; + } else { + if ((int) f0->index != yindex) { + return(0); /* f bypasses layer y */ + } + f11 = empty; + f10 = f1; + } + if ((int) f0->index == yindex) { + f01 = cuddT(f0); + f00 = cuddE(f0); + if (f00 != empty) + arccount++; + } else { + f01 = empty; + f00 = f0; + } + if (f01 != f10) + xsymmy = 0; + if (f11 != f00) + xsymmyp = 0; + if ((xsymmy == 0) && (xsymmyp == 0)) + return(0); + + f = f->next; + } /* for each element of the collision list */ + } /* for each slot of the subtable */ + + /* Calculate the total reference counts of y + ** whose else arc is not empty. + */ + for (i = table->subtableZ[y].slots - 1; i >= 0; i--) { + f = table->subtableZ[y].nodelist[i]; + while (f != NIL(DdNode)) { + if (cuddE(f) != empty) + TotalRefCount += f->ref; + f = f->next; + } + } + + symm_found = (arccount == TotalRefCount); +#if defined(DD_DEBUG) && defined(DD_VERBOSE) + if (symm_found) { + int xindex = table->invpermZ[x]; + (void) fprintf(table->out, + "Found symmetry! x =%d\ty = %d\tPos(%d,%d)\n", + xindex,yindex,x,y); + } +#endif + + return(symm_found); + +} /* end cuddZddSymmCheck */ + + +/**Function******************************************************************** + + Synopsis [Symmetric sifting algorithm for ZDDs.] + + Description [Symmetric sifting algorithm. + Assumes that no dead nodes are present. +
        +
      1. Order all the variables according to the number of entries in + each unique subtable. +
      2. Sift the variable up and down, remembering each time the total + size of the ZDD heap and grouping variables that are symmetric. +
      3. Select the best permutation. +
      4. Repeat 3 and 4 for all variables. +
      + Returns 1 plus the number of symmetric variables if successful; 0 + otherwise.] + + SideEffects [None] + + SeeAlso [cuddZddSymmSiftingConv] + +******************************************************************************/ +int +cuddZddSymmSifting( + DdManager * table, + int lower, + int upper) +{ + int i; + int *var; + int nvars; + int x; + int result; + int symvars; + int symgroups; + int iteration; +#ifdef DD_STATS + int previousSize; +#endif + + nvars = table->sizeZ; + + /* Find order in which to sift variables. */ + var = NULL; + zdd_entry = ALLOC(int, nvars); + if (zdd_entry == NULL) { + table->errorCode = CUDD_MEMORY_OUT; + goto cuddZddSymmSiftingOutOfMem; + } + var = ALLOC(int, nvars); + if (var == NULL) { + table->errorCode = CUDD_MEMORY_OUT; + goto cuddZddSymmSiftingOutOfMem; + } + + for (i = 0; i < nvars; i++) { + x = table->permZ[i]; + zdd_entry[i] = table->subtableZ[x].keys; + var[i] = i; + } + + qsort((void *)var, nvars, sizeof(int), (DD_QSFP)cuddZddUniqueCompare); + + /* Initialize the symmetry of each subtable to itself. */ + for (i = lower; i <= upper; i++) + table->subtableZ[i].next = i; + + iteration = ddMin(table->siftMaxVar, nvars); + for (i = 0; i < iteration; i++) { + if (zddTotalNumberSwapping >= table->siftMaxSwap) + break; + if (util_cpu_time() - table->startTime > table->timeLimit) { + table->autoDynZ = 0; /* prevent further reordering */ + break; + } + x = table->permZ[var[i]]; +#ifdef DD_STATS + previousSize = table->keysZ; +#endif + if (x < lower || x > upper) continue; + if (table->subtableZ[x].next == (unsigned) x) { + result = cuddZddSymmSiftingAux(table, x, lower, upper); + if (!result) + goto cuddZddSymmSiftingOutOfMem; +#ifdef DD_STATS + if (table->keysZ < (unsigned) previousSize) { + (void) fprintf(table->out,"-"); + } else if (table->keysZ > (unsigned) previousSize) { + (void) fprintf(table->out,"+"); +#ifdef DD_VERBOSE + (void) fprintf(table->out,"\nSize increased from %d to %d while sifting variable %d\n", previousSize, table->keysZ, var[i]); +#endif + } else { + (void) fprintf(table->out,"="); + } + fflush(table->out); +#endif + } + } + + FREE(var); + FREE(zdd_entry); + + cuddZddSymmSummary(table, lower, upper, &symvars, &symgroups); + +#ifdef DD_STATS + (void) fprintf(table->out,"\n#:S_SIFTING %8d: symmetric variables\n",symvars); + (void) fprintf(table->out,"#:G_SIFTING %8d: symmetric groups\n",symgroups); +#endif + + return(1+symvars); + + cuddZddSymmSiftingOutOfMem: + + if (zdd_entry != NULL) + FREE(zdd_entry); + if (var != NULL) + FREE(var); + + return(0); + +} /* end of cuddZddSymmSifting */ + + +/**Function******************************************************************** + + Synopsis [Symmetric sifting to convergence algorithm for ZDDs.] + + Description [Symmetric sifting to convergence algorithm for ZDDs. + Assumes that no dead nodes are present. +
        +
      1. Order all the variables according to the number of entries in + each unique subtable. +
      2. Sift the variable up and down, remembering each time the total + size of the ZDD heap and grouping variables that are symmetric. +
      3. Select the best permutation. +
      4. Repeat 3 and 4 for all variables. +
      5. Repeat 1-4 until no further improvement. +
      + Returns 1 plus the number of symmetric variables if successful; 0 + otherwise.] + + SideEffects [None] + + SeeAlso [cuddZddSymmSifting] + +******************************************************************************/ +int +cuddZddSymmSiftingConv( + DdManager * table, + int lower, + int upper) +{ + int i; + int *var; + int nvars; + int initialSize; + int x; + int result; + int symvars; + int symgroups; + int classes; + int iteration; +#ifdef DD_STATS + int previousSize; +#endif + + initialSize = table->keysZ; + + nvars = table->sizeZ; + + /* Find order in which to sift variables. */ + var = NULL; + zdd_entry = ALLOC(int, nvars); + if (zdd_entry == NULL) { + table->errorCode = CUDD_MEMORY_OUT; + goto cuddZddSymmSiftingConvOutOfMem; + } + var = ALLOC(int, nvars); + if (var == NULL) { + table->errorCode = CUDD_MEMORY_OUT; + goto cuddZddSymmSiftingConvOutOfMem; + } + + for (i = 0; i < nvars; i++) { + x = table->permZ[i]; + zdd_entry[i] = table->subtableZ[x].keys; + var[i] = i; + } + + qsort((void *)var, nvars, sizeof(int), (DD_QSFP)cuddZddUniqueCompare); + + /* Initialize the symmetry of each subtable to itself + ** for first pass of converging symmetric sifting. + */ + for (i = lower; i <= upper; i++) + table->subtableZ[i].next = i; + + iteration = ddMin(table->siftMaxVar, table->sizeZ); + for (i = 0; i < iteration; i++) { + if (zddTotalNumberSwapping >= table->siftMaxSwap) + break; + if (util_cpu_time() - table->startTime > table->timeLimit) { + table->autoDynZ = 0; /* prevent further reordering */ + break; + } + x = table->permZ[var[i]]; + if (x < lower || x > upper) continue; + /* Only sift if not in symmetry group already. */ + if (table->subtableZ[x].next == (unsigned) x) { +#ifdef DD_STATS + previousSize = table->keysZ; +#endif + result = cuddZddSymmSiftingAux(table, x, lower, upper); + if (!result) + goto cuddZddSymmSiftingConvOutOfMem; +#ifdef DD_STATS + if (table->keysZ < (unsigned) previousSize) { + (void) fprintf(table->out,"-"); + } else if (table->keysZ > (unsigned) previousSize) { + (void) fprintf(table->out,"+"); +#ifdef DD_VERBOSE + (void) fprintf(table->out,"\nSize increased from %d to %d while sifting variable %d\n", previousSize, table->keysZ, var[i]); +#endif + } else { + (void) fprintf(table->out,"="); + } + fflush(table->out); +#endif + } + } + + /* Sifting now until convergence. */ + while ((unsigned) initialSize > table->keysZ) { + initialSize = table->keysZ; +#ifdef DD_STATS + (void) fprintf(table->out,"\n"); +#endif + /* Here we consider only one representative for each symmetry class. */ + for (x = lower, classes = 0; x <= upper; x++, classes++) { + while ((unsigned) x < table->subtableZ[x].next) + x = table->subtableZ[x].next; + /* Here x is the largest index in a group. + ** Groups consists of adjacent variables. + ** Hence, the next increment of x will move it to a new group. + */ + i = table->invpermZ[x]; + zdd_entry[i] = table->subtableZ[x].keys; + var[classes] = i; + } + + qsort((void *)var,classes,sizeof(int),(DD_QSFP)cuddZddUniqueCompare); + + /* Now sift. */ + iteration = ddMin(table->siftMaxVar, nvars); + for (i = 0; i < iteration; i++) { + if (zddTotalNumberSwapping >= table->siftMaxSwap) + break; + if (util_cpu_time() - table->startTime > table->timeLimit) { + table->autoDynZ = 0; /* prevent further reordering */ + break; + } + x = table->permZ[var[i]]; + if ((unsigned) x >= table->subtableZ[x].next) { +#ifdef DD_STATS + previousSize = table->keysZ; +#endif + result = cuddZddSymmSiftingConvAux(table, x, lower, upper); + if (!result) + goto cuddZddSymmSiftingConvOutOfMem; +#ifdef DD_STATS + if (table->keysZ < (unsigned) previousSize) { + (void) fprintf(table->out,"-"); + } else if (table->keysZ > (unsigned) previousSize) { + (void) fprintf(table->out,"+"); +#ifdef DD_VERBOSE + (void) fprintf(table->out,"\nSize increased from %d to %d while sifting variable %d\n", previousSize, table->keysZ, var[i]); +#endif + } else { + (void) fprintf(table->out,"="); + } + fflush(table->out); +#endif + } + } /* for */ + } + + cuddZddSymmSummary(table, lower, upper, &symvars, &symgroups); + +#ifdef DD_STATS + (void) fprintf(table->out,"\n#:S_SIFTING %8d: symmetric variables\n", + symvars); + (void) fprintf(table->out,"#:G_SIFTING %8d: symmetric groups\n", + symgroups); +#endif + + FREE(var); + FREE(zdd_entry); + + return(1+symvars); + + cuddZddSymmSiftingConvOutOfMem: + + if (zdd_entry != NULL) + FREE(zdd_entry); + if (var != NULL) + FREE(var); + + return(0); + +} /* end of cuddZddSymmSiftingConv */ + + +/*---------------------------------------------------------------------------*/ +/* Definition of static functions */ +/*---------------------------------------------------------------------------*/ + + +/**Function******************************************************************** + + Synopsis [Given x_low <= x <= x_high moves x up and down between the + boundaries.] + + Description [Given x_low <= x <= x_high moves x up and down between the + boundaries. Finds the best position and does the required changes. + Assumes that x is not part of a symmetry group. Returns 1 if + successful; 0 otherwise.] + + SideEffects [None] + + SeeAlso [] + +******************************************************************************/ +static int +cuddZddSymmSiftingAux( + DdManager * table, + int x, + int x_low, + int x_high) +{ + Move *move; + Move *move_up; /* list of up move */ + Move *move_down; /* list of down move */ + int initial_size; + int result; + int i; + int topbot; /* index to either top or bottom of symmetry group */ + int init_group_size, final_group_size; + + initial_size = table->keysZ; + + move_down = NULL; + move_up = NULL; + + /* Look for consecutive symmetries above x. */ + for (i = x; i > x_low; i--) { + if (!cuddZddSymmCheck(table, i - 1, i)) + break; + /* find top of i-1's symmetry */ + topbot = table->subtableZ[i - 1].next; + table->subtableZ[i - 1].next = i; + table->subtableZ[x].next = topbot; + /* x is bottom of group so its symmetry is top of i-1's + group */ + i = topbot + 1; /* add 1 for i--, new i is top of symm group */ + } + /* Look for consecutive symmetries below x. */ + for (i = x; i < x_high; i++) { + if (!cuddZddSymmCheck(table, i, i + 1)) + break; + /* find bottom of i+1's symm group */ + topbot = i + 1; + while ((unsigned) topbot < table->subtableZ[topbot].next) + topbot = table->subtableZ[topbot].next; + + table->subtableZ[topbot].next = table->subtableZ[i].next; + table->subtableZ[i].next = i + 1; + i = topbot - 1; /* add 1 for i++, + new i is bottom of symm group */ + } + + /* Now x maybe in the middle of a symmetry group. */ + if (x == x_low) { /* Sift down */ + /* Find bottom of x's symm group */ + while ((unsigned) x < table->subtableZ[x].next) + x = table->subtableZ[x].next; + + i = table->subtableZ[x].next; + init_group_size = x - i + 1; + + move_down = cuddZddSymmSifting_down(table, x, x_high, + initial_size); + /* after that point x --> x_high, unless early term */ + if (move_down == ZDD_MV_OOM) + goto cuddZddSymmSiftingAuxOutOfMem; + + if (move_down == NULL || + table->subtableZ[move_down->y].next != move_down->y) { + /* symmetry detected may have to make another complete + pass */ + if (move_down != NULL) + x = move_down->y; + else + x = table->subtableZ[x].next; + i = x; + while ((unsigned) i < table->subtableZ[i].next) { + i = table->subtableZ[i].next; + } + final_group_size = i - x + 1; + + if (init_group_size == final_group_size) { + /* No new symmetry groups detected, + return to best position */ + result = cuddZddSymmSiftingBackward(table, + move_down, initial_size); + } + else { + initial_size = table->keysZ; + move_up = cuddZddSymmSifting_up(table, x, x_low, + initial_size); + result = cuddZddSymmSiftingBackward(table, move_up, + initial_size); + } + } + else { + result = cuddZddSymmSiftingBackward(table, move_down, + initial_size); + /* move backward and stop at best position */ + } + if (!result) + goto cuddZddSymmSiftingAuxOutOfMem; + } + else if (x == x_high) { /* Sift up */ + /* Find top of x's symm group */ + while ((unsigned) x < table->subtableZ[x].next) + x = table->subtableZ[x].next; + x = table->subtableZ[x].next; + + i = x; + while ((unsigned) i < table->subtableZ[i].next) { + i = table->subtableZ[i].next; + } + init_group_size = i - x + 1; + + move_up = cuddZddSymmSifting_up(table, x, x_low, initial_size); + /* after that point x --> x_low, unless early term */ + if (move_up == ZDD_MV_OOM) + goto cuddZddSymmSiftingAuxOutOfMem; + + if (move_up == NULL || + table->subtableZ[move_up->x].next != move_up->x) { + /* symmetry detected may have to make another complete + pass */ + if (move_up != NULL) + x = move_up->x; + else { + while ((unsigned) x < table->subtableZ[x].next) + x = table->subtableZ[x].next; + } + i = table->subtableZ[x].next; + final_group_size = x - i + 1; + + if (init_group_size == final_group_size) { + /* No new symmetry groups detected, + return to best position */ + result = cuddZddSymmSiftingBackward(table, move_up, + initial_size); + } + else { + initial_size = table->keysZ; + move_down = cuddZddSymmSifting_down(table, x, x_high, + initial_size); + result = cuddZddSymmSiftingBackward(table, move_down, + initial_size); + } + } + else { + result = cuddZddSymmSiftingBackward(table, move_up, + initial_size); + /* move backward and stop at best position */ + } + if (!result) + goto cuddZddSymmSiftingAuxOutOfMem; + } + else if ((x - x_low) > (x_high - x)) { /* must go down first: + shorter */ + /* Find bottom of x's symm group */ + while ((unsigned) x < table->subtableZ[x].next) + x = table->subtableZ[x].next; + + move_down = cuddZddSymmSifting_down(table, x, x_high, + initial_size); + /* after that point x --> x_high, unless early term */ + if (move_down == ZDD_MV_OOM) + goto cuddZddSymmSiftingAuxOutOfMem; + + if (move_down != NULL) { + x = move_down->y; + } + else { + x = table->subtableZ[x].next; + } + i = x; + while ((unsigned) i < table->subtableZ[i].next) { + i = table->subtableZ[i].next; + } + init_group_size = i - x + 1; + + move_up = cuddZddSymmSifting_up(table, x, x_low, initial_size); + if (move_up == ZDD_MV_OOM) + goto cuddZddSymmSiftingAuxOutOfMem; + + if (move_up == NULL || + table->subtableZ[move_up->x].next != move_up->x) { + /* symmetry detected may have to make another complete + pass */ + if (move_up != NULL) { + x = move_up->x; + } + else { + while ((unsigned) x < table->subtableZ[x].next) + x = table->subtableZ[x].next; + } + i = table->subtableZ[x].next; + final_group_size = x - i + 1; + + if (init_group_size == final_group_size) { + /* No new symmetry groups detected, + return to best position */ + result = cuddZddSymmSiftingBackward(table, move_up, + initial_size); + } + else { + while (move_down != NULL) { + move = move_down->next; + cuddDeallocMove(table, move_down); + move_down = move; + } + initial_size = table->keysZ; + move_down = cuddZddSymmSifting_down(table, x, x_high, + initial_size); + result = cuddZddSymmSiftingBackward(table, move_down, + initial_size); + } + } + else { + result = cuddZddSymmSiftingBackward(table, move_up, + initial_size); + /* move backward and stop at best position */ + } + if (!result) + goto cuddZddSymmSiftingAuxOutOfMem; + } + else { /* moving up first:shorter */ + /* Find top of x's symmetry group */ + while ((unsigned) x < table->subtableZ[x].next) + x = table->subtableZ[x].next; + x = table->subtableZ[x].next; + + move_up = cuddZddSymmSifting_up(table, x, x_low, initial_size); + /* after that point x --> x_high, unless early term */ + if (move_up == ZDD_MV_OOM) + goto cuddZddSymmSiftingAuxOutOfMem; + + if (move_up != NULL) { + x = move_up->x; + } + else { + while ((unsigned) x < table->subtableZ[x].next) + x = table->subtableZ[x].next; + } + i = table->subtableZ[x].next; + init_group_size = x - i + 1; + + move_down = cuddZddSymmSifting_down(table, x, x_high, + initial_size); + if (move_down == ZDD_MV_OOM) + goto cuddZddSymmSiftingAuxOutOfMem; + + if (move_down == NULL || + table->subtableZ[move_down->y].next != move_down->y) { + /* symmetry detected may have to make another complete + pass */ + if (move_down != NULL) { + x = move_down->y; + } + else { + x = table->subtableZ[x].next; + } + i = x; + while ((unsigned) i < table->subtableZ[i].next) { + i = table->subtableZ[i].next; + } + final_group_size = i - x + 1; + + if (init_group_size == final_group_size) { + /* No new symmetries detected, + go back to best position */ + result = cuddZddSymmSiftingBackward(table, move_down, + initial_size); + } + else { + while (move_up != NULL) { + move = move_up->next; + cuddDeallocMove(table, move_up); + move_up = move; + } + initial_size = table->keysZ; + move_up = cuddZddSymmSifting_up(table, x, x_low, + initial_size); + result = cuddZddSymmSiftingBackward(table, move_up, + initial_size); + } + } + else { + result = cuddZddSymmSiftingBackward(table, move_down, + initial_size); + /* move backward and stop at best position */ + } + if (!result) + goto cuddZddSymmSiftingAuxOutOfMem; + } + + while (move_down != NULL) { + move = move_down->next; + cuddDeallocMove(table, move_down); + move_down = move; + } + while (move_up != NULL) { + move = move_up->next; + cuddDeallocMove(table, move_up); + move_up = move; + } + + return(1); + + cuddZddSymmSiftingAuxOutOfMem: + if (move_down != ZDD_MV_OOM) { + while (move_down != NULL) { + move = move_down->next; + cuddDeallocMove(table, move_down); + move_down = move; + } + } + if (move_up != ZDD_MV_OOM) { + while (move_up != NULL) { + move = move_up->next; + cuddDeallocMove(table, move_up); + move_up = move; + } + } + + return(0); + +} /* end of cuddZddSymmSiftingAux */ + + +/**Function******************************************************************** + + Synopsis [Given x_low <= x <= x_high moves x up and down between the + boundaries.] + + Description [Given x_low <= x <= x_high moves x up and down between the + boundaries. Finds the best position and does the required changes. + Assumes that x is either an isolated variable, or it is the bottom of + a symmetry group. All symmetries may not have been found, because of + exceeded growth limit. Returns 1 if successful; 0 otherwise.] + + SideEffects [None] + + SeeAlso [] + +******************************************************************************/ +static int +cuddZddSymmSiftingConvAux( + DdManager * table, + int x, + int x_low, + int x_high) +{ + Move *move; + Move *move_up; /* list of up move */ + Move *move_down; /* list of down move */ + int initial_size; + int result; + int i; + int init_group_size, final_group_size; + + initial_size = table->keysZ; + + move_down = NULL; + move_up = NULL; + + if (x == x_low) { /* Sift down */ + i = table->subtableZ[x].next; + init_group_size = x - i + 1; + + move_down = cuddZddSymmSifting_down(table, x, x_high, + initial_size); + /* after that point x --> x_high, unless early term */ + if (move_down == ZDD_MV_OOM) + goto cuddZddSymmSiftingConvAuxOutOfMem; + + if (move_down == NULL || + table->subtableZ[move_down->y].next != move_down->y) { + /* symmetry detected may have to make another complete + pass */ + if (move_down != NULL) + x = move_down->y; + else { + while ((unsigned) x < table->subtableZ[x].next) + x = table->subtableZ[x].next; + x = table->subtableZ[x].next; + } + i = x; + while ((unsigned) i < table->subtableZ[i].next) { + i = table->subtableZ[i].next; + } + final_group_size = i - x + 1; + + if (init_group_size == final_group_size) { + /* No new symmetries detected, + go back to best position */ + result = cuddZddSymmSiftingBackward(table, move_down, + initial_size); + } + else { + initial_size = table->keysZ; + move_up = cuddZddSymmSifting_up(table, x, x_low, + initial_size); + result = cuddZddSymmSiftingBackward(table, move_up, + initial_size); + } + } + else { + result = cuddZddSymmSiftingBackward(table, move_down, + initial_size); + /* move backward and stop at best position */ + } + if (!result) + goto cuddZddSymmSiftingConvAuxOutOfMem; + } + else if (x == x_high) { /* Sift up */ + /* Find top of x's symm group */ + while ((unsigned) x < table->subtableZ[x].next) + x = table->subtableZ[x].next; + x = table->subtableZ[x].next; + + i = x; + while ((unsigned) i < table->subtableZ[i].next) { + i = table->subtableZ[i].next; + } + init_group_size = i - x + 1; + + move_up = cuddZddSymmSifting_up(table, x, x_low, initial_size); + /* after that point x --> x_low, unless early term */ + if (move_up == ZDD_MV_OOM) + goto cuddZddSymmSiftingConvAuxOutOfMem; + + if (move_up == NULL || + table->subtableZ[move_up->x].next != move_up->x) { + /* symmetry detected may have to make another complete + pass */ + if (move_up != NULL) + x = move_up->x; + else { + while ((unsigned) x < table->subtableZ[x].next) + x = table->subtableZ[x].next; + } + i = table->subtableZ[x].next; + final_group_size = x - i + 1; + + if (init_group_size == final_group_size) { + /* No new symmetry groups detected, + return to best position */ + result = cuddZddSymmSiftingBackward(table, move_up, + initial_size); + } + else { + initial_size = table->keysZ; + move_down = cuddZddSymmSifting_down(table, x, x_high, + initial_size); + result = cuddZddSymmSiftingBackward(table, move_down, + initial_size); + } + } + else { + result = cuddZddSymmSiftingBackward(table, move_up, + initial_size); + /* move backward and stop at best position */ + } + if (!result) + goto cuddZddSymmSiftingConvAuxOutOfMem; + } + else if ((x - x_low) > (x_high - x)) { /* must go down first: + shorter */ + move_down = cuddZddSymmSifting_down(table, x, x_high, + initial_size); + /* after that point x --> x_high */ + if (move_down == ZDD_MV_OOM) + goto cuddZddSymmSiftingConvAuxOutOfMem; + + if (move_down != NULL) { + x = move_down->y; + } + else { + while ((unsigned) x < table->subtableZ[x].next) + x = table->subtableZ[x].next; + x = table->subtableZ[x].next; + } + i = x; + while ((unsigned) i < table->subtableZ[i].next) { + i = table->subtableZ[i].next; + } + init_group_size = i - x + 1; + + move_up = cuddZddSymmSifting_up(table, x, x_low, initial_size); + if (move_up == ZDD_MV_OOM) + goto cuddZddSymmSiftingConvAuxOutOfMem; + + if (move_up == NULL || + table->subtableZ[move_up->x].next != move_up->x) { + /* symmetry detected may have to make another complete + pass */ + if (move_up != NULL) { + x = move_up->x; + } + else { + while ((unsigned) x < table->subtableZ[x].next) + x = table->subtableZ[x].next; + } + i = table->subtableZ[x].next; + final_group_size = x - i + 1; + + if (init_group_size == final_group_size) { + /* No new symmetry groups detected, + return to best position */ + result = cuddZddSymmSiftingBackward(table, move_up, + initial_size); + } + else { + while (move_down != NULL) { + move = move_down->next; + cuddDeallocMove(table, move_down); + move_down = move; + } + initial_size = table->keysZ; + move_down = cuddZddSymmSifting_down(table, x, x_high, + initial_size); + result = cuddZddSymmSiftingBackward(table, move_down, + initial_size); + } + } + else { + result = cuddZddSymmSiftingBackward(table, move_up, + initial_size); + /* move backward and stop at best position */ + } + if (!result) + goto cuddZddSymmSiftingConvAuxOutOfMem; + } + else { /* moving up first:shorter */ + /* Find top of x's symmetry group */ + x = table->subtableZ[x].next; + + move_up = cuddZddSymmSifting_up(table, x, x_low, initial_size); + /* after that point x --> x_high, unless early term */ + if (move_up == ZDD_MV_OOM) + goto cuddZddSymmSiftingConvAuxOutOfMem; + + if (move_up != NULL) { + x = move_up->x; + } + else { + while ((unsigned) x < table->subtableZ[x].next) + x = table->subtableZ[x].next; + } + i = table->subtableZ[x].next; + init_group_size = x - i + 1; + + move_down = cuddZddSymmSifting_down(table, x, x_high, + initial_size); + if (move_down == ZDD_MV_OOM) + goto cuddZddSymmSiftingConvAuxOutOfMem; + + if (move_down == NULL || + table->subtableZ[move_down->y].next != move_down->y) { + /* symmetry detected may have to make another complete + pass */ + if (move_down != NULL) { + x = move_down->y; + } + else { + while ((unsigned) x < table->subtableZ[x].next) + x = table->subtableZ[x].next; + x = table->subtableZ[x].next; + } + i = x; + while ((unsigned) i < table->subtableZ[i].next) { + i = table->subtableZ[i].next; + } + final_group_size = i - x + 1; + + if (init_group_size == final_group_size) { + /* No new symmetries detected, + go back to best position */ + result = cuddZddSymmSiftingBackward(table, move_down, + initial_size); + } + else { + while (move_up != NULL) { + move = move_up->next; + cuddDeallocMove(table, move_up); + move_up = move; + } + initial_size = table->keysZ; + move_up = cuddZddSymmSifting_up(table, x, x_low, + initial_size); + result = cuddZddSymmSiftingBackward(table, move_up, + initial_size); + } + } + else { + result = cuddZddSymmSiftingBackward(table, move_down, + initial_size); + /* move backward and stop at best position */ + } + if (!result) + goto cuddZddSymmSiftingConvAuxOutOfMem; + } + + while (move_down != NULL) { + move = move_down->next; + cuddDeallocMove(table, move_down); + move_down = move; + } + while (move_up != NULL) { + move = move_up->next; + cuddDeallocMove(table, move_up); + move_up = move; + } + + return(1); + + cuddZddSymmSiftingConvAuxOutOfMem: + if (move_down != ZDD_MV_OOM) { + while (move_down != NULL) { + move = move_down->next; + cuddDeallocMove(table, move_down); + move_down = move; + } + } + if (move_up != ZDD_MV_OOM) { + while (move_up != NULL) { + move = move_up->next; + cuddDeallocMove(table, move_up); + move_up = move; + } + } + + return(0); + +} /* end of cuddZddSymmSiftingConvAux */ + + +/**Function******************************************************************** + + Synopsis [Moves x up until either it reaches the bound (x_low) or + the size of the ZDD heap increases too much.] + + Description [Moves x up until either it reaches the bound (x_low) or + the size of the ZDD heap increases too much. Assumes that x is the top + of a symmetry group. Checks x for symmetry to the adjacent + variables. If symmetry is found, the symmetry group of x is merged + with the symmetry group of the other variable. Returns the set of + moves in case of success; ZDD_MV_OOM if memory is full.] + + SideEffects [None] + + SeeAlso [] + +******************************************************************************/ +static Move * +cuddZddSymmSifting_up( + DdManager * table, + int x, + int x_low, + int initial_size) +{ + Move *moves; + Move *move; + int y; + int size; + int limit_size = initial_size; + int i, gytop; + + moves = NULL; + y = cuddZddNextLow(table, x); + while (y >= x_low) { + gytop = table->subtableZ[y].next; + if (cuddZddSymmCheck(table, y, x)) { + /* Symmetry found, attach symm groups */ + table->subtableZ[y].next = x; + i = table->subtableZ[x].next; + while (table->subtableZ[i].next != (unsigned) x) + i = table->subtableZ[i].next; + table->subtableZ[i].next = gytop; + } + else if ((table->subtableZ[x].next == (unsigned) x) && + (table->subtableZ[y].next == (unsigned) y)) { + /* x and y have self symmetry */ + size = cuddZddSwapInPlace(table, y, x); + if (size == 0) + goto cuddZddSymmSifting_upOutOfMem; + move = (Move *)cuddDynamicAllocNode(table); + if (move == NULL) + goto cuddZddSymmSifting_upOutOfMem; + move->x = y; + move->y = x; + move->size = size; + move->next = moves; + moves = move; + if ((double)size > + (double)limit_size * table->maxGrowth) + return(moves); + if (size < limit_size) + limit_size = size; + } + else { /* Group move */ + size = zdd_group_move(table, y, x, &moves); + if ((double)size > + (double)limit_size * table->maxGrowth) + return(moves); + if (size < limit_size) + limit_size = size; + } + x = gytop; + y = cuddZddNextLow(table, x); + } + + return(moves); + + cuddZddSymmSifting_upOutOfMem: + while (moves != NULL) { + move = moves->next; + cuddDeallocMove(table, moves); + moves = move; + } + return(ZDD_MV_OOM); + +} /* end of cuddZddSymmSifting_up */ + + +/**Function******************************************************************** + + Synopsis [Moves x down until either it reaches the bound (x_high) or + the size of the ZDD heap increases too much.] + + Description [Moves x down until either it reaches the bound (x_high) + or the size of the ZDD heap increases too much. Assumes that x is the + bottom of a symmetry group. Checks x for symmetry to the adjacent + variables. If symmetry is found, the symmetry group of x is merged + with the symmetry group of the other variable. Returns the set of + moves in case of success; ZDD_MV_OOM if memory is full.] + + SideEffects [None] + + SeeAlso [] + +******************************************************************************/ +static Move * +cuddZddSymmSifting_down( + DdManager * table, + int x, + int x_high, + int initial_size) +{ + Move *moves; + Move *move; + int y; + int size; + int limit_size = initial_size; + int i, gxtop, gybot; + + moves = NULL; + y = cuddZddNextHigh(table, x); + while (y <= x_high) { + gybot = table->subtableZ[y].next; + while (table->subtableZ[gybot].next != (unsigned) y) + gybot = table->subtableZ[gybot].next; + if (cuddZddSymmCheck(table, x, y)) { + /* Symmetry found, attach symm groups */ + gxtop = table->subtableZ[x].next; + table->subtableZ[x].next = y; + i = table->subtableZ[y].next; + while (table->subtableZ[i].next != (unsigned) y) + i = table->subtableZ[i].next; + table->subtableZ[i].next = gxtop; + } + else if ((table->subtableZ[x].next == (unsigned) x) && + (table->subtableZ[y].next == (unsigned) y)) { + /* x and y have self symmetry */ + size = cuddZddSwapInPlace(table, x, y); + if (size == 0) + goto cuddZddSymmSifting_downOutOfMem; + move = (Move *)cuddDynamicAllocNode(table); + if (move == NULL) + goto cuddZddSymmSifting_downOutOfMem; + move->x = x; + move->y = y; + move->size = size; + move->next = moves; + moves = move; + if ((double)size > + (double)limit_size * table->maxGrowth) + return(moves); + if (size < limit_size) + limit_size = size; + x = y; + y = cuddZddNextHigh(table, x); + } + else { /* Group move */ + size = zdd_group_move(table, x, y, &moves); + if ((double)size > + (double)limit_size * table->maxGrowth) + return(moves); + if (size < limit_size) + limit_size = size; + } + x = gybot; + y = cuddZddNextHigh(table, x); + } + + return(moves); + + cuddZddSymmSifting_downOutOfMem: + while (moves != NULL) { + move = moves->next; + cuddDeallocMove(table, moves); + moves = move; + } + return(ZDD_MV_OOM); + +} /* end of cuddZddSymmSifting_down */ + + +/**Function******************************************************************** + + Synopsis [Given a set of moves, returns the ZDD heap to the position + giving the minimum size.] + + Description [Given a set of moves, returns the ZDD heap to the + position giving the minimum size. In case of ties, returns to the + closest position giving the minimum size. Returns 1 in case of + success; 0 otherwise.] + + SideEffects [None] + + SeeAlso [] + +******************************************************************************/ +static int +cuddZddSymmSiftingBackward( + DdManager * table, + Move * moves, + int size) +{ + int i; + int i_best; + Move *move; + int res; + + i_best = -1; + for (move = moves, i = 0; move != NULL; move = move->next, i++) { + if (move->size < size) { + i_best = i; + size = move->size; + } + } + + for (move = moves, i = 0; move != NULL; move = move->next, i++) { + if (i == i_best) break; + if ((table->subtableZ[move->x].next == move->x) && + (table->subtableZ[move->y].next == move->y)) { + res = cuddZddSwapInPlace(table, move->x, move->y); + if (!res) return(0); + } + else { /* Group move necessary */ + res = zdd_group_move_backward(table, move->x, move->y); + } + if (i_best == -1 && res == size) + break; + } + + return(1); + +} /* end of cuddZddSymmSiftingBackward */ + + +/**Function******************************************************************** + + Synopsis [Swaps two groups.] + + Description [Swaps two groups. x is assumed to be the bottom variable + of the first group. y is assumed to be the top variable of the second + group. Updates the list of moves. Returns the number of keys in the + table if successful; 0 otherwise.] + + SideEffects [None] + + SeeAlso [] + +******************************************************************************/ +static int +zdd_group_move( + DdManager * table, + int x, + int y, + Move ** moves) +{ + Move *move; + int size; + int i, temp, gxtop, gxbot, gybot, yprev; + int swapx, swapy; + +#ifdef DD_DEBUG + assert(x < y); /* we assume that x < y */ +#endif + /* Find top and bottom for the two groups. */ + gxtop = table->subtableZ[x].next; + gxbot = x; + gybot = table->subtableZ[y].next; + while (table->subtableZ[gybot].next != (unsigned) y) + gybot = table->subtableZ[gybot].next; + yprev = gybot; + + while (x <= y) { + while (y > gxtop) { + /* Set correct symmetries. */ + temp = table->subtableZ[x].next; + if (temp == x) + temp = y; + i = gxtop; + for (;;) { + if (table->subtableZ[i].next == (unsigned) x) { + table->subtableZ[i].next = y; + break; + } else { + i = table->subtableZ[i].next; + } + } + if (table->subtableZ[y].next != (unsigned) y) { + table->subtableZ[x].next = table->subtableZ[y].next; + } else { + table->subtableZ[x].next = x; + } + + if (yprev != y) { + table->subtableZ[yprev].next = x; + } else { + yprev = x; + } + table->subtableZ[y].next = temp; + + size = cuddZddSwapInPlace(table, x, y); + if (size == 0) + goto zdd_group_moveOutOfMem; + swapx = x; + swapy = y; + y = x; + x--; + } /* while y > gxtop */ + + /* Trying to find the next y. */ + if (table->subtableZ[y].next <= (unsigned) y) { + gybot = y; + } else { + y = table->subtableZ[y].next; + } + + yprev = gxtop; + gxtop++; + gxbot++; + x = gxbot; + } /* while x <= y, end of group movement */ + move = (Move *)cuddDynamicAllocNode(table); + if (move == NULL) + goto zdd_group_moveOutOfMem; + move->x = swapx; + move->y = swapy; + move->size = table->keysZ; + move->next = *moves; + *moves = move; + + return(table->keysZ); + + zdd_group_moveOutOfMem: + while (*moves != NULL) { + move = (*moves)->next; + cuddDeallocMove(table, *moves); + *moves = move; + } + return(0); + +} /* end of zdd_group_move */ + + +/**Function******************************************************************** + + Synopsis [Undoes the swap of two groups.] + + Description [Undoes the swap of two groups. x is assumed to be the + bottom variable of the first group. y is assumed to be the top + variable of the second group. Returns 1 if successful; 0 otherwise.] + + SideEffects [None] + + SeeAlso [] + +******************************************************************************/ +static int +zdd_group_move_backward( + DdManager * table, + int x, + int y) +{ + int size; + int i, temp, gxtop, gxbot, gybot, yprev; + +#ifdef DD_DEBUG + assert(x < y); /* we assume that x < y */ +#endif + /* Find top and bottom of the two groups. */ + gxtop = table->subtableZ[x].next; + gxbot = x; + gybot = table->subtableZ[y].next; + while (table->subtableZ[gybot].next != (unsigned) y) + gybot = table->subtableZ[gybot].next; + yprev = gybot; + + while (x <= y) { + while (y > gxtop) { + /* Set correct symmetries. */ + temp = table->subtableZ[x].next; + if (temp == x) + temp = y; + i = gxtop; + for (;;) { + if (table->subtableZ[i].next == (unsigned) x) { + table->subtableZ[i].next = y; + break; + } else { + i = table->subtableZ[i].next; + } + } + if (table->subtableZ[y].next != (unsigned) y) { + table->subtableZ[x].next = table->subtableZ[y].next; + } else { + table->subtableZ[x].next = x; + } + + if (yprev != y) { + table->subtableZ[yprev].next = x; + } else { + yprev = x; + } + table->subtableZ[y].next = temp; + + size = cuddZddSwapInPlace(table, x, y); + if (size == 0) + return(0); + y = x; + x--; + } /* while y > gxtop */ + + /* Trying to find the next y. */ + if (table->subtableZ[y].next <= (unsigned) y) { + gybot = y; + } else { + y = table->subtableZ[y].next; + } + + yprev = gxtop; + gxtop++; + gxbot++; + x = gxbot; + } /* while x <= y, end of group movement backward */ + + return(size); + +} /* end of zdd_group_move_backward */ + + +/**Function******************************************************************** + + Synopsis [Counts numbers of symmetric variables and symmetry + groups.] + + Description [] + + SideEffects [None] + +******************************************************************************/ +static void +cuddZddSymmSummary( + DdManager * table, + int lower, + int upper, + int * symvars, + int * symgroups) +{ + int i,x,gbot; + int TotalSymm = 0; + int TotalSymmGroups = 0; + + for (i = lower; i <= upper; i++) { + if (table->subtableZ[i].next != (unsigned) i) { + TotalSymmGroups++; + x = i; + do { + TotalSymm++; + gbot = x; + x = table->subtableZ[x].next; + } while (x != i); +#ifdef DD_DEBUG + assert(table->subtableZ[gbot].next == (unsigned) i); +#endif + i = gbot; + } + } + *symvars = TotalSymm; + *symgroups = TotalSymmGroups; + + return; + +} /* end of cuddZddSymmSummary */ + diff --git a/svf/lib/CUDD/epd.c b/svf/lib/CUDD/epd.c new file mode 100644 index 000000000..9e0e37a8b --- /dev/null +++ b/svf/lib/CUDD/epd.c @@ -0,0 +1,724 @@ +/**CFile*********************************************************************** + + FileName [epd.c] + + PackageName [epd] + + Synopsis [Arithmetic functions with extended double precision.] + + Description [] + + SeeAlso [] + + Author [In-Ho Moon] + + Copyright [Copyright (c) 1995-2004, Regents of the University of Colorado + + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + Neither the name of the University of Colorado nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE.] + + Revision [$Id: epd.c,v 1.10 2004/08/13 18:20:30 fabio Exp $] + +******************************************************************************/ + +#include +#include +#include +#include +#include "CUDD/util.h" +#include "CUDD/epd.h" + + +/**Function******************************************************************** + + Synopsis [Allocates an EpDouble struct.] + + Description [Allocates an EpDouble struct.] + + SideEffects [] + + SeeAlso [] + +******************************************************************************/ +EpDouble * +EpdAlloc(void) +{ + EpDouble *epd; + + epd = ALLOC(EpDouble, 1); + return(epd); +} + + +/**Function******************************************************************** + + Synopsis [Frees an EpDouble struct.] + + Description [Frees an EpDouble struct.] + + SideEffects [] + + SeeAlso [] + +******************************************************************************/ +void +EpdFree(EpDouble *epd) +{ + FREE(epd); +} + + +/**Function******************************************************************** + + Synopsis [Converts double to EpDouble struct.] + + Description [Converts double to EpDouble struct.] + + SideEffects [] + + SeeAlso [] + +******************************************************************************/ +void +EpdConvert(double value, EpDouble *epd) +{ + epd->type.value = value; + epd->exponent = 0; + EpdNormalize(epd); +} + + +/**Function******************************************************************** + + Synopsis [Multiplies two arbitrary precision double values.] + + Description [Multiplies two arbitrary precision double values.] + + SideEffects [] + + SeeAlso [] + +******************************************************************************/ +void +EpdMultiply(EpDouble *epd1, double value) +{ + EpDouble epd2; + double tmp; + int exponent; + + if (EpdIsNan(epd1) || IsNanDouble(value)) { + EpdMakeNan(epd1); + return; + } else if (EpdIsInf(epd1) || IsInfDouble(value)) { + int sign; + + EpdConvert(value, &epd2); + sign = epd1->type.bits.sign ^ epd2.type.bits.sign; + EpdMakeInf(epd1, sign); + return; + } + + assert(epd1->type.bits.exponent == EPD_MAX_BIN); + + EpdConvert(value, &epd2); + tmp = epd1->type.value * epd2.type.value; + exponent = epd1->exponent + epd2.exponent; + epd1->type.value = tmp; + epd1->exponent = exponent; + EpdNormalize(epd1); +} + + + +/**Function******************************************************************** + + Synopsis [Multiplies two arbitrary precision double values.] + + Description [Multiplies two arbitrary precision double values.] + + SideEffects [] + + SeeAlso [] + +******************************************************************************/ +void +EpdMultiply3(EpDouble *epd1, EpDouble *epd2, EpDouble *epd3) +{ + if (EpdIsNan(epd1) || EpdIsNan(epd2)) { + EpdMakeNan(epd1); + return; + } else if (EpdIsInf(epd1) || EpdIsInf(epd2)) { + int sign; + + sign = epd1->type.bits.sign ^ epd2->type.bits.sign; + EpdMakeInf(epd3, sign); + return; + } + + assert(epd1->type.bits.exponent == EPD_MAX_BIN); + assert(epd2->type.bits.exponent == EPD_MAX_BIN); + + epd3->type.value = epd1->type.value * epd2->type.value; + epd3->exponent = epd1->exponent + epd2->exponent; + EpdNormalize(epd3); +} + + +/**Function******************************************************************** + + Synopsis [Multiplies two arbitrary precision double values.] + + Description [Multiplies two arbitrary precision double values.] + + SideEffects [] + + SeeAlso [] + +******************************************************************************/ +void +EpdMultiply3Decimal(EpDouble *epd1, EpDouble *epd2, EpDouble *epd3) +{ + if (EpdIsNan(epd1) || EpdIsNan(epd2)) { + EpdMakeNan(epd1); + return; + } else if (EpdIsInf(epd1) || EpdIsInf(epd2)) { + int sign; + + sign = epd1->type.bits.sign ^ epd2->type.bits.sign; + EpdMakeInf(epd3, sign); + return; + } + + epd3->type.value = epd1->type.value * epd2->type.value; + epd3->exponent = epd1->exponent + epd2->exponent; + EpdNormalizeDecimal(epd3); +} + +/**Function******************************************************************** + + Synopsis [Adds two arbitrary precision double values.] + + Description [Adds two arbitrary precision double values.] + + SideEffects [] + + SeeAlso [] + +******************************************************************************/ +void +EpdAdd3(EpDouble *epd1, EpDouble *epd2, EpDouble *epd3) +{ + double value; + int exponent, diff; + + if (EpdIsNan(epd1) || EpdIsNan(epd2)) { + EpdMakeNan(epd3); + return; + } else if (EpdIsInf(epd1) || EpdIsInf(epd2)) { + int sign; + + if (EpdIsInf(epd1) && EpdIsInf(epd2)) { + sign = epd1->type.bits.sign ^ epd2->type.bits.sign; + if (sign == 1) + EpdMakeNan(epd3); + else + EpdCopy(epd1, epd3); + } else if (EpdIsInf(epd1)) { + EpdCopy(epd1, epd3); + } else { + EpdCopy(epd2, epd3); + } + return; + } + + assert(epd1->type.bits.exponent == EPD_MAX_BIN); + assert(epd2->type.bits.exponent == EPD_MAX_BIN); + + if (epd1->exponent > epd2->exponent) { + diff = epd1->exponent - epd2->exponent; + if (diff <= EPD_MAX_BIN) { + value = epd1->type.value + + epd2->type.value / pow((double)2.0, (double)diff); + } else + value = epd1->type.value; + exponent = epd1->exponent; + } else if (epd1->exponent < epd2->exponent) { + diff = epd2->exponent - epd1->exponent; + if (diff <= EPD_MAX_BIN) { + value = epd1->type.value / pow((double)2.0, (double)diff) + + epd2->type.value; + } else + value = epd2->type.value; + exponent = epd2->exponent; + } else { + value = epd1->type.value + epd2->type.value; + exponent = epd1->exponent; + } + epd3->type.value = value; + epd3->exponent = exponent; + EpdNormalize(epd3); +} + + +/**Function******************************************************************** + + Synopsis [Subtracts two arbitrary precision double values.] + + Description [Subtracts two arbitrary precision double values.] + + SideEffects [] + + SeeAlso [] + +******************************************************************************/ +void +EpdSubtract3(EpDouble *epd1, EpDouble *epd2, EpDouble *epd3) +{ + double value; + int exponent, diff; + + if (EpdIsNan(epd1) || EpdIsNan(epd2)) { + EpdMakeNan(epd3); + return; + } else if (EpdIsInf(epd1) || EpdIsInf(epd2)) { + int sign; + + if (EpdIsInf(epd1) && EpdIsInf(epd2)) { + sign = epd1->type.bits.sign ^ epd2->type.bits.sign; + if (sign == 0) + EpdCopy(epd1, epd3); + else + EpdMakeNan(epd3); + } else if (EpdIsInf(epd1)) { + EpdCopy(epd1, epd1); + } else { + sign = epd2->type.bits.sign ^ 0x1; + EpdMakeInf(epd3, sign); + } + return; + } + + assert(epd1->type.bits.exponent == EPD_MAX_BIN); + assert(epd2->type.bits.exponent == EPD_MAX_BIN); + + if (epd1->exponent > epd2->exponent) { + diff = epd1->exponent - epd2->exponent; + if (diff <= EPD_MAX_BIN) { + value = epd1->type.value - + epd2->type.value / pow((double)2.0, (double)diff); + } else + value = epd1->type.value; + exponent = epd1->exponent; + } else if (epd1->exponent < epd2->exponent) { + diff = epd2->exponent - epd1->exponent; + if (diff <= EPD_MAX_BIN) { + value = epd1->type.value / pow((double)2.0, (double)diff) - + epd2->type.value; + } else + value = epd2->type.value * (double)(-1.0); + exponent = epd2->exponent; + } else { + value = epd1->type.value - epd2->type.value; + exponent = epd1->exponent; + } + epd3->type.value = value; + epd3->exponent = exponent; + EpdNormalize(epd3); +} + + +/**Function******************************************************************** + + Synopsis [Computes arbitrary precision pow of base 2.] + + Description [Computes arbitrary precision pow of base 2.] + + SideEffects [] + + SeeAlso [] + +******************************************************************************/ +void +EpdPow2(int n, EpDouble *epd) +{ + if (n <= EPD_MAX_BIN) { + EpdConvert(pow((double)2.0, (double)n), epd); + } else { + EpDouble epd1, epd2; + int n1, n2; + + n1 = n / 2; + n2 = n - n1; + EpdPow2(n1, &epd1); + EpdPow2(n2, &epd2); + EpdMultiply3(&epd1, &epd2, epd); + } +} + + +/**Function******************************************************************** + + Synopsis [Computes arbitrary precision pow of base 2.] + + Description [Computes arbitrary precision pow of base 2.] + + SideEffects [] + + SeeAlso [] + +******************************************************************************/ +void +EpdPow2Decimal(int n, EpDouble *epd) +{ + if (n <= EPD_MAX_BIN) { + epd->type.value = pow((double)2.0, (double)n); + epd->exponent = 0; + EpdNormalizeDecimal(epd); + } else { + EpDouble epd1, epd2; + int n1, n2; + + n1 = n / 2; + n2 = n - n1; + EpdPow2Decimal(n1, &epd1); + EpdPow2Decimal(n2, &epd2); + EpdMultiply3Decimal(&epd1, &epd2, epd); + } +} + + +/**Function******************************************************************** + + Synopsis [Normalize an arbitrary precision double value.] + + Description [Normalize an arbitrary precision double value.] + + SideEffects [] + + SeeAlso [] + +******************************************************************************/ +void +EpdNormalize(EpDouble *epd) +{ + int exponent; + + if (IsNanOrInfDouble(epd->type.value)) { + epd->exponent = 0; + return; + } + + exponent = EpdGetExponent(epd->type.value); + if (exponent == EPD_MAX_BIN) + return; + exponent -= EPD_MAX_BIN; + epd->type.bits.exponent = EPD_MAX_BIN; + epd->exponent += exponent; +} + + +/**Function******************************************************************** + + Synopsis [Normalize an arbitrary precision double value.] + + Description [Normalize an arbitrary precision double value.] + + SideEffects [] + + SeeAlso [] + +******************************************************************************/ +void +EpdNormalizeDecimal(EpDouble *epd) +{ + int exponent; + + if (IsNanOrInfDouble(epd->type.value)) { + epd->exponent = 0; + return; + } + + exponent = EpdGetExponentDecimal(epd->type.value); + epd->type.value /= pow((double)10.0, (double)exponent); + epd->exponent += exponent; +} + + +/**Function******************************************************************** + + Synopsis [Returns the exponent value of a double.] + + Description [Returns the exponent value of a double.] + + SideEffects [] + + SeeAlso [] + +******************************************************************************/ +int +EpdGetExponent(double value) +{ + int exponent; + EpDouble epd; + + epd.type.value = value; + exponent = epd.type.bits.exponent; + return(exponent); +} + + +/**Function******************************************************************** + + Synopsis [Returns the decimal exponent value of a double.] + + Description [Returns the decimal exponent value of a double.] + + SideEffects [] + + SeeAlso [] + +******************************************************************************/ +int +EpdGetExponentDecimal(double value) +{ + char *pos, str[24]; + int exponent; + + sprintf(str, "%E", value); + pos = strstr(str, "E"); + sscanf(pos, "E%d", &exponent); + return(exponent); +} + + +/**Function******************************************************************** + + Synopsis [Makes EpDouble Inf.] + + Description [Makes EpDouble Inf.] + + SideEffects [] + + SeeAlso [] + +******************************************************************************/ +void +EpdMakeInf(EpDouble *epd, int sign) +{ + epd->type.bits.mantissa1 = 0; + epd->type.bits.mantissa0 = 0; + epd->type.bits.exponent = EPD_EXP_INF; + epd->type.bits.sign = sign; + epd->exponent = 0; +} + + +/**Function******************************************************************** + + Synopsis [Makes EpDouble Zero.] + + Description [Makes EpDouble Zero.] + + SideEffects [] + + SeeAlso [] + +******************************************************************************/ +void +EpdMakeZero(EpDouble *epd, int sign) +{ + epd->type.bits.mantissa1 = 0; + epd->type.bits.mantissa0 = 0; + epd->type.bits.exponent = 0; + epd->type.bits.sign = sign; + epd->exponent = 0; +} + + +/**Function******************************************************************** + + Synopsis [Makes EpDouble NaN.] + + Description [Makes EpDouble NaN.] + + SideEffects [] + + SeeAlso [] + +******************************************************************************/ +void +EpdMakeNan(EpDouble *epd) +{ + epd->type.nan.mantissa1 = 0; + epd->type.nan.mantissa0 = 0; + epd->type.nan.quiet_bit = 1; + epd->type.nan.exponent = EPD_EXP_INF; + epd->type.nan.sign = 1; + epd->exponent = 0; +} + + +/**Function******************************************************************** + + Synopsis [Copies a EpDouble struct.] + + Description [Copies a EpDouble struct.] + + SideEffects [] + + SeeAlso [] + +******************************************************************************/ +void +EpdCopy(EpDouble *from, EpDouble *to) +{ + to->type.value = from->type.value; + to->exponent = from->exponent; +} + + +/**Function******************************************************************** + + Synopsis [Checks whether the value is Inf.] + + Description [Checks whether the value is Inf.] + + SideEffects [] + + SeeAlso [] + +******************************************************************************/ +int +EpdIsInf(EpDouble *epd) +{ + return(IsInfDouble(epd->type.value)); +} + + +/**Function******************************************************************** + + Synopsis [Checks whether the value is NaN.] + + Description [Checks whether the value is NaN.] + + SideEffects [] + + SeeAlso [] + +******************************************************************************/ +int +EpdIsNan(EpDouble *epd) +{ + return(IsNanDouble(epd->type.value)); +} + + +/**Function******************************************************************** + + Synopsis [Checks whether the value is Inf.] + + Description [Checks whether the value is Inf.] + + SideEffects [] + + SeeAlso [] + +******************************************************************************/ +int +IsInfDouble(double value) +{ + EpType val; + + val.value = value; + if (val.bits.exponent == EPD_EXP_INF && + val.bits.mantissa0 == 0 && + val.bits.mantissa1 == 0) { + if (val.bits.sign == 0) + return(1); + else + return(-1); + } + return(0); +} + + +/**Function******************************************************************** + + Synopsis [Checks whether the value is NaN.] + + Description [Checks whether the value is NaN.] + + SideEffects [] + + SeeAlso [] + +******************************************************************************/ +int +IsNanDouble(double value) +{ + EpType val; + + val.value = value; + if (val.nan.exponent == EPD_EXP_INF && + val.nan.sign == 1 && + val.nan.quiet_bit == 1 && + val.nan.mantissa0 == 0 && + val.nan.mantissa1 == 0) { + return(1); + } + return(0); +} + + +/**Function******************************************************************** + + Synopsis [Checks whether the value is NaN or Inf.] + + Description [Checks whether the value is NaN or Inf.] + + SideEffects [] + + SeeAlso [] + +******************************************************************************/ +int +IsNanOrInfDouble(double value) +{ + EpType val; + + val.value = value; + if (val.nan.exponent == EPD_EXP_INF && + val.nan.mantissa0 == 0 && + val.nan.mantissa1 == 0 && + (val.nan.sign == 1 || val.nan.quiet_bit == 0)) { + return(1); + } + return(0); +} diff --git a/svf/lib/CUDD/mtr.c b/svf/lib/CUDD/mtr.c new file mode 100644 index 000000000..3e44cd4c1 --- /dev/null +++ b/svf/lib/CUDD/mtr.c @@ -0,0 +1,913 @@ +/**CFile*********************************************************************** + + FileName [mtrBasic.c] + + PackageName [mtr] + + Synopsis [Basic manipulation of multiway branching trees.] + + Description [External procedures included in this module: +
        +
      • Mtr_AllocNode() +
      • Mtr_DeallocNode() +
      • Mtr_InitTree() +
      • Mtr_FreeTree() +
      • Mtr_CopyTree() +
      • Mtr_MakeFirstChild() +
      • Mtr_MakeLastChild() +
      • Mtr_CreateFirstChild() +
      • Mtr_CreateLastChild() +
      • Mtr_MakeNextSibling() +
      • Mtr_PrintTree() +
      + ] + + SeeAlso [cudd package] + + Author [Fabio Somenzi] + + Copyright [Copyright (c) 1995-2012, Regents of the University of Colorado + + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + Neither the name of the University of Colorado nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE.] + +******************************************************************************/ + +#include "CUDD/util.h" +#include "CUDD/mtr.h" + + +/*---------------------------------------------------------------------------*/ +/* Constant declarations */ +/*---------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------*/ +/* Stucture declarations */ +/*---------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------*/ +/* Type declarations */ +/*---------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------*/ +/* Variable declarations */ +/*---------------------------------------------------------------------------*/ + +#ifndef lint +static char rcsid[] MTR_UNUSED = "$Id: mtrBasic.c,v 1.15 2012/02/05 01:06:19 fabio Exp $"; +#endif + +/*---------------------------------------------------------------------------*/ +/* Macro declarations */ +/*---------------------------------------------------------------------------*/ + +/**AutomaticStart*************************************************************/ + +/*---------------------------------------------------------------------------*/ +/* Static function prototypes */ +/*---------------------------------------------------------------------------*/ + + +/**AutomaticEnd***************************************************************/ + + +/*---------------------------------------------------------------------------*/ +/* Definition of exported functions */ +/*---------------------------------------------------------------------------*/ + +/**Function******************************************************************** + + Synopsis [Allocates new tree node.] + + Description [Allocates new tree node. Returns pointer to node.] + + SideEffects [None] + + SeeAlso [Mtr_DeallocNode] + +******************************************************************************/ +MtrNode * +Mtr_AllocNode(void) +{ + MtrNode *node; + + node = ALLOC(MtrNode,1); + return node; + +} /* Mtr_AllocNode */ + + +/**Function******************************************************************** + + Synopsis [Deallocates tree node.] + + Description [] + + SideEffects [None] + + SeeAlso [Mtr_AllocNode] + +******************************************************************************/ +void +Mtr_DeallocNode( + MtrNode * node /* node to be deallocated */) +{ + FREE(node); + return; + +} /* end of Mtr_DeallocNode */ + + +/**Function******************************************************************** + + Synopsis [Initializes tree with one node.] + + Description [Initializes tree with one node. Returns pointer to node.] + + SideEffects [None] + + SeeAlso [Mtr_FreeTree Mtr_InitGroupTree] + +******************************************************************************/ +MtrNode * +Mtr_InitTree(void) +{ + MtrNode *node; + + node = Mtr_AllocNode(); + if (node == NULL) return(NULL); + + node->parent = node->child = node->elder = node->younger = NULL; + node->flags = 0; + + return(node); + +} /* end of Mtr_InitTree */ + + +/**Function******************************************************************** + + Synopsis [Disposes of tree rooted at node.] + + Description [] + + SideEffects [None] + + SeeAlso [Mtr_InitTree] + +******************************************************************************/ +void +Mtr_FreeTree( + MtrNode * node) +{ + if (node == NULL) return; + if (! MTR_TEST(node,MTR_TERMINAL)) Mtr_FreeTree(node->child); + Mtr_FreeTree(node->younger); + Mtr_DeallocNode(node); + return; + +} /* end of Mtr_FreeTree */ + + +/**Function******************************************************************** + + Synopsis [Makes a copy of tree.] + + Description [Makes a copy of tree. If parameter expansion is greater + than 1, it will expand the tree by that factor. It is an error for + expansion to be less than 1. Returns a pointer to the copy if + successful; NULL otherwise.] + + SideEffects [None] + + SeeAlso [Mtr_InitTree] + +******************************************************************************/ +MtrNode * +Mtr_CopyTree( + MtrNode * node, + int expansion) +{ + MtrNode *copy; + + if (node == NULL) return(NULL); + if (expansion < 1) return(NULL); + copy = Mtr_AllocNode(); + if (copy == NULL) return(NULL); + copy->parent = copy->elder = copy->child = copy->younger = NULL; + if (node->child != NULL) { + copy->child = Mtr_CopyTree(node->child, expansion); + if (copy->child == NULL) { + Mtr_DeallocNode(copy); + return(NULL); + } + } + if (node->younger != NULL) { + copy->younger = Mtr_CopyTree(node->younger, expansion); + if (copy->younger == NULL) { + Mtr_FreeTree(copy); + return(NULL); + } + } + copy->flags = node->flags; + copy->low = node->low * expansion; + copy->size = node->size * expansion; + copy->index = node->index * expansion; + if (copy->younger) copy->younger->elder = copy; + if (copy->child) { + MtrNode *auxnode = copy->child; + while (auxnode != NULL) { + auxnode->parent = copy; + auxnode = auxnode->younger; + } + } + return(copy); + +} /* end of Mtr_CopyTree */ + + +/**Function******************************************************************** + + Synopsis [Prints a tree, one node per line.] + + Description [] + + SideEffects [None] + + SeeAlso [Mtr_PrintGroups] + +******************************************************************************/ +void +Mtr_PrintTree( + MtrNode * node) +{ + if (node == NULL) return; + (void) fprintf(stdout, +#if SIZEOF_VOID_P == 8 + "N=0x%-8lx C=0x%-8lx Y=0x%-8lx E=0x%-8lx P=0x%-8lx F=%x L=%u S=%u\n", + (unsigned long) node, (unsigned long) node->child, + (unsigned long) node->younger, (unsigned long) node->elder, + (unsigned long) node->parent, node->flags, node->low, node->size); +#else + "N=0x%-8x C=0x%-8x Y=0x%-8x E=0x%-8x P=0x%-8x F=%x L=%hu S=%hu\n", + (unsigned) node, (unsigned) node->child, + (unsigned) node->younger, (unsigned) node->elder, + (unsigned) node->parent, node->flags, node->low, node->size); +#endif + if (!MTR_TEST(node,MTR_TERMINAL)) Mtr_PrintTree(node->child); + Mtr_PrintTree(node->younger); + return; + +} /* end of Mtr_PrintTree */ + +/*---------------------------------------------------------------------------*/ +/* Definition of internal functions */ +/*---------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------*/ +/* Definition of static functions */ +/*---------------------------------------------------------------------------*/ +/**CFile*********************************************************************** + + FileName [mtrGroup.c] + + PackageName [mtr] + + Synopsis [Functions to support group specification for reordering.] + + Description [External procedures included in this module: +
        +
      • Mtr_InitGroupTree() +
      • Mtr_MakeGroup() +
      • Mtr_DissolveGroup() +
      • Mtr_FindGroup() +
      • Mtr_SwapGroups() +
      • Mtr_ReorderGroups() +
      • Mtr_PrintGroups() +
      • Mtr_ReadGroups() +
      + Static procedures included in this module: +
        +
      • mtrShiftHL +
      + ] + + SeeAlso [cudd package] + + Author [Fabio Somenzi] + + Copyright [Copyright (c) 1995-2012, Regents of the University of Colorado + + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + Neither the name of the University of Colorado nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE.] + +******************************************************************************/ + +/*---------------------------------------------------------------------------*/ +/* Constant declarations */ +/*---------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------*/ +/* Stucture declarations */ +/*---------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------*/ +/* Type declarations */ +/*---------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------*/ +/* Variable declarations */ +/*---------------------------------------------------------------------------*/ + +//#ifndef lint +//static char rcsid[] MTR_UNUSED = "$Id: mtrGroup.c,v 1.21 2012/02/05 01:06:19 fabio Exp $"; +//#endif + +/*---------------------------------------------------------------------------*/ +/* Macro declarations */ +/*---------------------------------------------------------------------------*/ + +/**AutomaticStart*************************************************************/ + +/*---------------------------------------------------------------------------*/ +/* Static function prototypes */ + +/**AutomaticEnd***************************************************************/ + +/*---------------------------------------------------------------------------*/ +/* Definition of exported functions */ +/*---------------------------------------------------------------------------*/ + + +/**Function******************************************************************** + + Synopsis [Allocate new tree.] + + Description [Allocate new tree with one node, whose low and size + fields are specified by the lower and size parameters. + Returns pointer to tree root.] + + SideEffects [None] + + SeeAlso [Mtr_InitTree Mtr_FreeTree] + +******************************************************************************/ +MtrNode * +Mtr_InitGroupTree( + int lower, + int size) +{ + MtrNode *root; + + root = Mtr_InitTree(); + if (root == NULL) return(NULL); + root->flags = MTR_DEFAULT; + root->low = lower; + root->size = size; + return(root); + +} /* end of Mtr_InitGroupTree */ + + +/**Function******************************************************************** + + Synopsis [Makes a new group with size leaves starting at low.] + + Description [Makes a new group with size leaves starting at low. + If the new group intersects an existing group, it must + either contain it or be contained by it. This procedure relies on + the low and size fields of each node. It also assumes that the + children of each node are sorted in order of increasing low. In + case of a valid request, the flags of the new group are set to the + value passed in `flags.' Returns the pointer to the root of the new + group upon successful termination; NULL otherwise. If the group + already exists, the pointer to its root is returned.] + + SideEffects [None] + + SeeAlso [Mtr_DissolveGroup Mtr_ReadGroups Mtr_FindGroup] + +******************************************************************************/ +MtrNode * +Mtr_MakeGroup( + MtrNode * root /* root of the group tree */, + unsigned int low /* lower bound of the group */, + unsigned int size /* size of the group */, + unsigned int flags /* flags for the new group */) +{ + MtrNode *node, + *first, + *last, + *previous, + *newn; + + /* Sanity check. */ + if (size == 0) + return(NULL); + + /* Check whether current group includes new group. This check is + ** necessary at the top-level call. In the subsequent calls it is + ** redundant. */ + if (low < (unsigned int) root->low || + low + size > (unsigned int) (root->low + root->size)) + return(NULL); + + /* At this point we know that the new group is contained + ** in the group of root. We have two possible cases here: + ** - root is a terminal node; + ** - root has children. */ + + /* Root has no children: create a new group. */ + if (root->child == NULL) { + newn = Mtr_AllocNode(); + if (newn == NULL) return(NULL); /* out of memory */ + newn->low = low; + newn->size = size; + newn->flags = flags; + newn->parent = root; + newn->elder = newn->younger = newn->child = NULL; + root->child = newn; + return(newn); + } + + /* Root has children: Find all children of root that are included + ** in the new group. If the group of any child entirely contains + ** the new group, call Mtr_MakeGroup recursively. */ + previous = NULL; + first = root->child; /* guaranteed to be non-NULL */ + while (first != NULL && low >= (unsigned int) (first->low + first->size)) { + previous = first; + first = first->younger; + } + if (first == NULL) { + /* We have scanned the entire list and we need to append a new + ** child at the end of it. Previous points to the last child + ** of root. */ + newn = Mtr_AllocNode(); + if (newn == NULL) return(NULL); /* out of memory */ + newn->low = low; + newn->size = size; + newn->flags = flags; + newn->parent = root; + newn->elder = previous; + previous->younger = newn; + newn->younger = newn->child = NULL; + return(newn); + } + /* Here first is non-NULL and low < first->low + first->size. */ + if (low >= (unsigned int) first->low && + low + size <= (unsigned int) (first->low + first->size)) { + /* The new group is contained in the group of first. */ + newn = Mtr_MakeGroup(first, low, size, flags); + return(newn); + } else if (low + size <= first->low) { + /* The new group is entirely contained in the gap between + ** previous and first. */ + newn = Mtr_AllocNode(); + if (newn == NULL) return(NULL); /* out of memory */ + newn->low = low; + newn->size = size; + newn->flags = flags; + newn->child = NULL; + newn->parent = root; + newn->elder = previous; + newn->younger = first; + first->elder = newn; + if (previous != NULL) { + previous->younger = newn; + } else { + root->child = newn; + } + return(newn); + } else if (low < (unsigned int) first->low && + low + size < (unsigned int) (first->low + first->size)) { + /* Trying to cut an existing group: not allowed. */ + return(NULL); + } else if (low > first->low) { + /* The new group neither is contained in the group of first + ** (this was tested above) nor contains it. It is therefore + ** trying to cut an existing group: not allowed. */ + return(NULL); + } + + /* First holds the pointer to the first child contained in the new + ** group. Here low <= first->low and low + size >= first->low + + ** first->size. One of the two inequalities is strict. */ + last = first->younger; + while (last != NULL && + (unsigned int) (last->low + last->size) < low + size) { + last = last->younger; + } + if (last == NULL) { + /* All the chilren of root from first onward become children + ** of the new group. */ + newn = Mtr_AllocNode(); + if (newn == NULL) return(NULL); /* out of memory */ + newn->low = low; + newn->size = size; + newn->flags = flags; + newn->child = first; + newn->parent = root; + newn->elder = previous; + newn->younger = NULL; + first->elder = NULL; + if (previous != NULL) { + previous->younger = newn; + } else { + root->child = newn; + } + last = first; + while (last != NULL) { + last->parent = newn; + last = last->younger; + } + return(newn); + } + + /* Here last != NULL and low + size <= last->low + last->size. */ + if (low + size - 1 >= (unsigned int) last->low && + low + size < (unsigned int) (last->low + last->size)) { + /* Trying to cut an existing group: not allowed. */ + return(NULL); + } + + /* First and last point to the first and last of the children of + ** root that are included in the new group. Allocate a new node + ** and make all children of root between first and last chidren of + ** the new node. Previous points to the child of root immediately + ** preceeding first. If it is NULL, then first is the first child + ** of root. */ + newn = Mtr_AllocNode(); + if (newn == NULL) return(NULL); /* out of memory */ + newn->low = low; + newn->size = size; + newn->flags = flags; + newn->child = first; + newn->parent = root; + if (previous == NULL) { + root->child = newn; + } else { + previous->younger = newn; + } + newn->elder = previous; + newn->younger = last->younger; + if (last->younger != NULL) { + last->younger->elder = newn; + } + last->younger = NULL; + first->elder = NULL; + for (node = first; node != NULL; node = node->younger) { + node->parent = newn; + } + + return(newn); + +} /* end of Mtr_MakeGroup */ + + +/**Function******************************************************************** + + Synopsis [Finds a group with size leaves starting at low, if it exists.] + + Description [Finds a group with size leaves starting at low, if it + exists. This procedure relies on the low and size fields of each + node. It also assumes that the children of each node are sorted in + order of increasing low. Returns the pointer to the root of the + group upon successful termination; NULL otherwise.] + + SideEffects [None] + + SeeAlso [] + +******************************************************************************/ +MtrNode * +Mtr_FindGroup( + MtrNode * root /* root of the group tree */, + unsigned int low /* lower bound of the group */, + unsigned int size /* upper bound of the group */) +{ + MtrNode *node; + +#ifdef MTR_DEBUG + /* We cannot have a non-empty proper subgroup of a singleton set. */ + assert(!MTR_TEST(root,MTR_TERMINAL)); +#endif + + /* Sanity check. */ + if (size < 1) return(NULL); + + /* Check whether current group includes the group sought. This + ** check is necessary at the top-level call. In the subsequent + ** calls it is redundant. */ + if (low < (unsigned int) root->low || + low + size > (unsigned int) (root->low + root->size)) + return(NULL); + + if (root->size == size && root->low == low) + return(root); + + if (root->child == NULL) + return(NULL); + + /* Find all chidren of root that are included in the new group. If + ** the group of any child entirely contains the new group, call + ** Mtr_MakeGroup recursively. */ + node = root->child; + while (low >= (unsigned int) (node->low + node->size)) { + node = node->younger; + } + if (low + size <= (unsigned int) (node->low + node->size)) { + /* The group is contained in the group of node. */ + node = Mtr_FindGroup(node, low, size); + return(node); + } else { + return(NULL); + } + +} /* end of Mtr_FindGroup */ + + + +/**Function******************************************************************** + + Synopsis [Fix variable tree at the end of tree sifting.] + + Description [Fix the levels in the variable tree sorting siblings + according to them. It should be called on a non-NULL tree. It then + maintains this invariant. It applies insertion sorting to the list of + siblings The order is determined by permutation, which is used to find + the new level of the node index. Index must refer to the first variable + in the group.] + + SideEffects [The tree is modified.] + + SeeAlso [] + +******************************************************************************/ +void +Mtr_ReorderGroups( + MtrNode *treenode, + int *permutation) +{ + MtrNode *auxnode; + /* Initialize sorted list to first element. */ + MtrNode *sorted = treenode; + sorted->low = permutation[sorted->index]; + if (sorted->child != NULL) + Mtr_ReorderGroups(sorted->child, permutation); + + auxnode = treenode->younger; + while (auxnode != NULL) { + MtrNode *rightplace; + MtrNode *moving = auxnode; + auxnode->low = permutation[auxnode->index]; + if (auxnode->child != NULL) + Mtr_ReorderGroups(auxnode->child, permutation); + rightplace = auxnode->elder; + /* Find insertion point. */ + while (rightplace != NULL && auxnode->low < rightplace->low) + rightplace = rightplace->elder; + auxnode = auxnode->younger; + if (auxnode != NULL) { + auxnode->elder = moving->elder; + auxnode->elder->younger = auxnode; + } else { + moving->elder->younger = NULL; + } + if (rightplace == NULL) { /* Move to head of sorted list. */ + sorted->elder = moving; + moving->elder = NULL; + moving->younger = sorted; + sorted = moving; + } else { /* Splice. */ + moving->elder = rightplace; + moving->younger = rightplace->younger; + if (rightplace->younger != NULL) + rightplace->younger->elder = moving; + rightplace->younger = moving; + } + } + /* Fix parent. */ + if (sorted->parent != NULL) + sorted->parent->child = sorted; + +} /* end of Mtr_ReorderGroups */ + + +/**Function******************************************************************** + + Synopsis [Prints the groups as a parenthesized list.] + + Description [Prints the groups as a parenthesized list. After each + group, the group's flag are printed, preceded by a `|'. For each + flag (except MTR_TERMINAL) a character is printed. +
        +
      • F: MTR_FIXED +
      • N: MTR_NEWNODE +
      • S: MTR_SOFT +
      + The second argument, silent, if different from 0, causes + Mtr_PrintGroups to only check the syntax of the group tree. + ] + + SideEffects [None] + + SeeAlso [Mtr_PrintTree] + +******************************************************************************/ +void +Mtr_PrintGroups( + MtrNode * root /* root of the group tree */, + int silent /* flag to check tree syntax only */) +{ + MtrNode *node; + + assert(root != NULL); + assert(root->younger == NULL || root->younger->elder == root); + assert(root->elder == NULL || root->elder->younger == root); +#if SIZEOF_VOID_P == 8 + if (!silent) (void) printf("(%u",root->low); +#else + if (!silent) (void) printf("(%hu",root->low); +#endif + if (MTR_TEST(root,MTR_TERMINAL) || root->child == NULL) { + if (!silent) (void) printf(","); + } else { + node = root->child; + while (node != NULL) { + assert(node->low >= root->low && (int) (node->low + node->size) <= (int) (root->low + root->size)); + assert(node->parent == root); + Mtr_PrintGroups(node,silent); + node = node->younger; + } + } + if (!silent) { +#if SIZEOF_VOID_P == 8 + (void) printf("%u", root->low + root->size - 1); +#else + (void) printf("%hu", root->low + root->size - 1); +#endif + if (root->flags != MTR_DEFAULT) { + (void) printf("|"); + if (MTR_TEST(root,MTR_FIXED)) (void) printf("F"); + if (MTR_TEST(root,MTR_NEWNODE)) (void) printf("N"); + if (MTR_TEST(root,MTR_SOFT)) (void) printf("S"); + } + (void) printf(")"); + if (root->parent == NULL) (void) printf("\n"); + } + assert((root->flags &~(MTR_TERMINAL | MTR_SOFT | MTR_FIXED | MTR_NEWNODE)) == 0); + return; + +} /* end of Mtr_PrintGroups */ + + +/**Function******************************************************************** + + Synopsis [Prints the variable order as a parenthesized list.] + + Description [Prints the variable order as a parenthesized list. After each + group, the group's flag are printed, preceded by a `|'. For each + flag (except MTR_TERMINAL) a character is printed. +
        +
      • F: MTR_FIXED +
      • N: MTR_NEWNODE +
      • S: MTR_SOFT +
      + The second argument, gives the map from levels to variable indices. + ] + + SideEffects [None] + + SeeAlso [Mtr_PrintGroups] + +******************************************************************************/ +int +Mtr_PrintGroupedOrder( + MtrNode * root /* root of the group tree */, + int *invperm /* map from levels to indices */, + FILE *fp /* output file */) +{ + MtrNode *child; + MtrHalfWord level; + int retval; + + assert(root != NULL); + assert(root->younger == NULL || root->younger->elder == root); + assert(root->elder == NULL || root->elder->younger == root); + retval = fprintf(fp,"("); + if (retval == EOF) return(0); + level = root->low; + child = root->child; + while (child != NULL) { + assert(child->low >= root->low && (child->low + child->size) <= (root->low + root->size)); + assert(child->parent == root); + while (level < child->low) { + retval = fprintf(fp,"%d%s", invperm[level], (level < root->low + root->size - 1) ? "," : ""); + if (retval == EOF) return(0); + level++; + } + retval = Mtr_PrintGroupedOrder(child,invperm,fp); + if (retval == 0) return(0); + level += child->size; + if (level < root->low + root->size - 1) { + retval = fprintf(fp,","); + if (retval == EOF) return(0); + } + child = child->younger; + } + while (level < root->low + root->size) { + retval = fprintf(fp,"%d%s", invperm[level], (level < root->low + root->size - 1) ? "," : ""); + if (retval == EOF) return(0); + level++; + } + if (root->flags != MTR_DEFAULT) { + retval = fprintf(fp,"|"); + if (retval == EOF) return(0); + if (MTR_TEST(root,MTR_FIXED)) { + retval = fprintf(fp,"F"); + if (retval == EOF) return(0); + } + if (MTR_TEST(root,MTR_NEWNODE)) { + retval = fprintf(fp,"N"); + if (retval == EOF) return(0); + } + if (MTR_TEST(root,MTR_SOFT)) { + retval = fprintf(fp,"S"); + if (retval == EOF) return(0); + } + } + retval = fprintf(fp,")"); + if (retval == EOF) return(0); + if (root->parent == NULL) { + retval = fprintf(fp,"\n"); + if (retval == EOF) return(0); + } + assert((root->flags &~(MTR_SOFT | MTR_FIXED | MTR_NEWNODE)) == 0); + return(1); + +} /* end of Mtr_PrintGroupedOrder */ + + +/*---------------------------------------------------------------------------*/ +/* Definition of internal functions */ +/*---------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------*/ +/* Definition of static functions */ +/*---------------------------------------------------------------------------*/ + diff --git a/svf/lib/CUDD/st.c b/svf/lib/CUDD/st.c new file mode 100644 index 000000000..94e81850e --- /dev/null +++ b/svf/lib/CUDD/st.c @@ -0,0 +1,801 @@ +/**CFile*********************************************************************** + + FileName [st.c] + + PackageName [st] + + Synopsis [Symbol table package.] + + Description [The st library provides functions to create, maintain, + and query symbol tables.] + + SeeAlso [] + + Author [] + + Copyright [] + +******************************************************************************/ + +#include "CUDD/util.h" +#include "CUDD/st.h" + +/*---------------------------------------------------------------------------*/ +/* Constant declarations */ +/*---------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------*/ +/* Stucture declarations */ +/*---------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------*/ +/* Type declarations */ +/*---------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------*/ +/* Variable declarations */ +/*---------------------------------------------------------------------------*/ + +#ifndef lint +static char rcsid[] UTIL_UNUSED = " $Id: st.c,v 1.12 2010/04/22 19:00:55 fabio Exp fabio $"; +#endif + +/*---------------------------------------------------------------------------*/ +/* Macro declarations */ +/*---------------------------------------------------------------------------*/ + +#define ST_NUMCMP(x,y) ((x) != (y)) + +#define ST_NUMHASH(x,size) ((unsigned long)(x)%(size)) + +#if SIZEOF_VOID_P == 8 +#define st_shift 3 +#else +#define st_shift 2 +#endif + +#define ST_PTRHASH(x,size) ((unsigned int)((unsigned long)(x)>>st_shift)%size) + +#define EQUAL(func, x, y) \ + ((((func) == st_numcmp) || ((func) == st_ptrcmp)) ?\ + (ST_NUMCMP((x),(y)) == 0) : ((*func)((x), (y)) == 0)) + +#define do_hash(key, table)\ + ((int)((table->hash == st_ptrhash) ? ST_PTRHASH((char *)(key),(table)->num_bins) :\ + (table->hash == st_numhash) ? ST_NUMHASH((char *)(key), (table)->num_bins) :\ + (*table->hash)((char *)(key), (table)->num_bins))) + +#define PTR_NOT_EQUAL(table, ptr, user_key)\ +(ptr != NIL(st_table_entry) && !EQUAL(table->compare, (char *)user_key, (ptr)->key)) + +#define FIND_ENTRY(table, hash_val, key, ptr, last) \ + (last) = &(table)->bins[hash_val];\ + (ptr) = *(last);\ + while (PTR_NOT_EQUAL((table), (ptr), (key))) {\ + (last) = &(ptr)->next; (ptr) = *(last);\ + }\ + if ((ptr) != NIL(st_table_entry) && (table)->reorder_flag) {\ + *(last) = (ptr)->next;\ + (ptr)->next = (table)->bins[hash_val];\ + (table)->bins[hash_val] = (ptr);\ + } + +/* This macro does not check if memory allocation fails. Use at you own risk */ + +/**AutomaticStart*************************************************************/ + +/*---------------------------------------------------------------------------*/ +/* Static function prototypes */ +/*---------------------------------------------------------------------------*/ + +static int rehash (st_table *); + +/**AutomaticEnd***************************************************************/ + + +/*---------------------------------------------------------------------------*/ +/* Definition of exported functions */ +/*---------------------------------------------------------------------------*/ + +/**Function******************************************************************** + + Synopsis [Create and initialize a table.] + + Description [Create and initialize a table with the comparison function + compare_fn and hash function hash_fn. compare_fn is +
      +	int compare_fn(const char *key1, const char *key2)
      +  
      + It returns <,=,> 0 depending on whether key1 <,=,> key2 by some measure.

      + hash_fn is +

      +	int hash_fn(char *key, int modulus)
      +  
      + It returns a integer between 0 and modulus-1 such that if + compare_fn(key1,key2) == 0 then hash_fn(key1) == hash_fn(key2).

      + There are five predefined hash and comparison functions in st. + For keys as numbers: +

      +	 st_numhash(key, modulus) { return (unsigned int) key % modulus; }
      +  
      +
      +	 st_numcmp(x,y) { return (int) x - (int) y; }
      +  
      + For keys as pointers: +
      +	 st_ptrhash(key, modulus) { return ((unsigned int) key/4) % modulus }
      +  
      +
      +	 st_ptrcmp(x,y) { return (int) x - (int) y; }
      +  
      + For keys as strings: +
      +         st_strhash(x,y) - a reasonable hashing function for strings
      +  
      +
      +	 strcmp(x,y) - the standard library function
      +  
      + It is recommended to use these particular functions if they fit your + needs, since st will recognize certain of them and run more quickly + because of it.] + + SideEffects [None] + + SeeAlso [st_init_table_with_params st_free_table] + +******************************************************************************/ +st_table * +st_init_table(ST_PFICPCP compare, ST_PFICPI hash) +{ + return st_init_table_with_params(compare, hash, ST_DEFAULT_INIT_TABLE_SIZE, + ST_DEFAULT_MAX_DENSITY, + ST_DEFAULT_GROW_FACTOR, + ST_DEFAULT_REORDER_FLAG); + +} /* st_init_table */ + + +/**Function******************************************************************** + + Synopsis [Create a table with given parameters.] + + Description [The full blown table initializer. compare and hash are + the same as in st_init_table. density is the largest the average + number of entries per hash bin there should be before the table is + grown. grow_factor is the factor the table is grown by when it + becomes too full. size is the initial number of bins to be allocated + for the hash table. If reorder_flag is non-zero, then every time an + entry is found, it is moved to the top of the chain.

      + st_init_table(compare, hash) is equivelent to +

      +  st_init_table_with_params(compare, hash, ST_DEFAULT_INIT_TABLE_SIZE,
      +			    ST_DEFAULT_MAX_DENSITY,
      +			    ST_DEFAULT_GROW_FACTOR,
      +			    ST_DEFAULT_REORDER_FLAG);
      +  
      + ] + + SideEffects [None] + + SeeAlso [st_init_table st_free_table] + +******************************************************************************/ +st_table * +st_init_table_with_params( + ST_PFICPCP compare, + ST_PFICPI hash, + int size, + int density, + double grow_factor, + int reorder_flag) +{ + int i; + st_table *newt; + + newt = ALLOC(st_table, 1); + if (newt == NIL(st_table)) { + return NIL(st_table); + } + newt->compare = compare; + newt->hash = hash; + newt->num_entries = 0; + newt->max_density = density; + newt->grow_factor = grow_factor; + newt->reorder_flag = reorder_flag; + if (size <= 0) { + size = 1; + } + newt->num_bins = size; + newt->bins = ALLOC(st_table_entry *, size); + if (newt->bins == NIL(st_table_entry *)) { + FREE(newt); + return NIL(st_table); + } + for(i = 0; i < size; i++) { + newt->bins[i] = 0; + } + return newt; + +} /* st_init_table_with_params */ + + +/**Function******************************************************************** + + Synopsis [Free a table.] + + Description [Any internal storage associated with table is freed. + It is the user's responsibility to free any storage associated + with the pointers he placed in the table (by perhaps using + st_foreach).] + + SideEffects [None] + + SeeAlso [st_init_table st_init_table_with_params] + +******************************************************************************/ +void +st_free_table(st_table *table) +{ + st_table_entry *ptr, *next; + int i; + + for(i = 0; i < table->num_bins ; i++) { + ptr = table->bins[i]; + while (ptr != NIL(st_table_entry)) { + next = ptr->next; + FREE(ptr); + ptr = next; + } + } + FREE(table->bins); + FREE(table); + +} /* st_free_table */ + + +/**Function******************************************************************** + + Synopsis [Lookup up `key' in `table'.] + + Description [Lookup up `key' in `table'. If an entry is found, 1 is + returned and if `value' is not nil, the variable it points to is set + to the associated value. If an entry is not found, 0 is returned + and the variable pointed by value is unchanged.] + + SideEffects [None] + + SeeAlso [st_lookup_int] + +******************************************************************************/ +int +st_lookup(st_table *table, void *key, void *value) +{ + int hash_val; + st_table_entry *ptr, **last; + + hash_val = do_hash(key, table); + + FIND_ENTRY(table, hash_val, key, ptr, last); + + if (ptr == NIL(st_table_entry)) { + return 0; + } else { + if (value != NIL(void)) { + *(char **)value = ptr->record; + } + return 1; + } + +} /* st_lookup */ + + +/**Function******************************************************************** + + Synopsis [Lookup up `key' in `table'.] + + Description [Lookup up `key' in `table'. If an entry is found, 1 is + returned and if `value' is not nil, the variable it points to is + set to the associated integer value. If an entry is not found, 0 is + return and the variable pointed by `value' is unchanged.] + + SideEffects [None] + + SeeAlso [st_lookup] + +******************************************************************************/ +int +st_lookup_int(st_table *table, void *key, int *value) +{ + int hash_val; + st_table_entry *ptr, **last; + + hash_val = do_hash(key, table); + + FIND_ENTRY(table, hash_val, key, ptr, last); + + if (ptr == NIL(st_table_entry)) { + return 0; + } else { + if (value != NIL(int)) { + *value = (int) (long) ptr->record; + } + return 1; + } + +} /* st_lookup_int */ + + +/**Function******************************************************************** + + Synopsis [Insert value in table under the key 'key'.] + + Description [Insert value in table under the key 'key'. Returns 1 + if there was an entry already under the key; 0 if there was no entry + under the key and insertion was successful; ST_OUT_OF_MEM otherwise. + In either of the first two cases the new value is added.] + + SideEffects [None] + + SeeAlso [] + +******************************************************************************/ +int +st_insert(st_table *table, void *key, void *value) +{ + int hash_val; + st_table_entry *newt; + st_table_entry *ptr, **last; + + hash_val = do_hash(key, table); + + FIND_ENTRY(table, hash_val, key, ptr, last); + + if (ptr == NIL(st_table_entry)) { + if (table->num_entries/table->num_bins >= table->max_density) { + if (rehash(table) == ST_OUT_OF_MEM) { + return ST_OUT_OF_MEM; + } + hash_val = do_hash(key, table); + } + newt = ALLOC(st_table_entry, 1); + if (newt == NIL(st_table_entry)) { + return ST_OUT_OF_MEM; + } + newt->key = (char *)key; + newt->record = (char *)value; + newt->next = table->bins[hash_val]; + table->bins[hash_val] = newt; + table->num_entries++; + return 0; + } else { + ptr->record = (char *)value; + return 1; + } + +} /* st_insert */ + + +/**Function******************************************************************** + + Synopsis [Place 'value' in 'table' under the key 'key'.] + + Description [Place 'value' in 'table' under the key 'key'. This is + done without checking if 'key' is in 'table' already. This should + only be used if you are sure there is not already an entry for + 'key', since it is undefined which entry you would later get from + st_lookup or st_find_or_add. Returns 1 if successful; ST_OUT_OF_MEM + otherwise.] + + SideEffects [None] + + SeeAlso [] + +******************************************************************************/ +int +st_add_direct(st_table *table, void *key, void *value) +{ + int hash_val; + st_table_entry *newt; + + hash_val = do_hash(key, table); + if (table->num_entries / table->num_bins >= table->max_density) { + if (rehash(table) == ST_OUT_OF_MEM) { + return ST_OUT_OF_MEM; + } + } + hash_val = do_hash(key, table); + newt = ALLOC(st_table_entry, 1); + if (newt == NIL(st_table_entry)) { + return ST_OUT_OF_MEM; + } + newt->key = (char *)key; + newt->record = (char *)value; + newt->next = table->bins[hash_val]; + table->bins[hash_val] = newt; + table->num_entries++; + return 1; + +} /* st_add_direct */ + +/**Function******************************************************************** + + Synopsis [Delete the entry with the key pointed to by `keyp'.] + + Description [Delete the entry with the key pointed to by `keyp'. If + the entry is found, 1 is returned, the variable pointed by `keyp' is + set to the actual key and the variable pointed by `value' is set to + the corresponding entry. (This allows the freeing of the associated + storage.) If the entry is not found, then 0 is returned and nothing + is changed.] + + SideEffects [None] + + SeeAlso [st_delete_int] + +******************************************************************************/ +int +st_delete(st_table *table, void *keyp, void *value) +{ + int hash_val; + char *key = *(char **)keyp; + st_table_entry *ptr, **last; + + hash_val = do_hash(key, table); + + FIND_ENTRY(table, hash_val, key, ptr ,last); + + if (ptr == NIL(st_table_entry)) { + return 0; + } + + *last = ptr->next; + if (value != NIL(void)) *(char **)value = ptr->record; + *(char **)keyp = ptr->key; + FREE(ptr); + table->num_entries--; + return 1; + +} /* st_delete */ + + +/**Function******************************************************************** + + Synopsis [Iterates over the elements of a table.] + + Description [For each (key, value) record in `table', st_foreach + call func with the arguments +
      +	  (*func)(key, value, arg)
      +  
      + If func returns ST_CONTINUE, st_foreach continues processing + entries. If func returns ST_STOP, st_foreach stops processing and + returns immediately. If func returns ST_DELETE, then the entry is + deleted from the symbol table and st_foreach continues. In the case + of ST_DELETE, it is func's responsibility to free the key and value, + if necessary.

      + + The routine returns 1 if all items in the table were generated and 0 + if the generation sequence was aborted using ST_STOP. The order in + which the records are visited will be seemingly random.] + + SideEffects [None] + + SeeAlso [st_foreach_item st_foreach_item_int] + +******************************************************************************/ +int +st_foreach(st_table *table, ST_PFSR func, char *arg) +{ + st_table_entry *ptr, **last; + enum st_retval retval; + int i; + + for(i = 0; i < table->num_bins; i++) { + last = &table->bins[i]; ptr = *last; + while (ptr != NIL(st_table_entry)) { + retval = (*func)(ptr->key, ptr->record, arg); + switch (retval) { + case ST_CONTINUE: + last = &ptr->next; ptr = *last; + break; + case ST_STOP: + return 0; + case ST_DELETE: + *last = ptr->next; + table->num_entries--; /* cstevens@ic */ + FREE(ptr); + ptr = *last; + } + } + } + return 1; + +} /* st_foreach */ + + +/**Function******************************************************************** + + Synopsis [Number hash function.] + + Description [Integer number hash function.] + + SideEffects [None] + + SeeAlso [st_init_table st_numcmp] + +******************************************************************************/ +int +st_numhash(char *x, int size) +{ + return ST_NUMHASH(x, size); + +} /* st_numhash */ + + +/**Function******************************************************************** + + Synopsis [Pointer hash function.] + + Description [Pointer hash function.] + + SideEffects [None] + + SeeAlso [st_init_table st_ptrcmp] + +******************************************************************************/ +int +st_ptrhash(char *x, int size) +{ + return ST_PTRHASH(x, size); + +} /* st_ptrhash */ + + +/**Function******************************************************************** + + Synopsis [Number comparison function.] + + Description [integer number comparison function.] + + SideEffects [None] + + SeeAlso [st_init_table st_numhash] + +******************************************************************************/ +int +st_numcmp(const char *x, const char *y) +{ + return ST_NUMCMP(x, y); + +} /* st_numcmp */ + + +/**Function******************************************************************** + + Synopsis [Pointer comparison function.] + + Description [Pointer comparison function.] + + SideEffects [None] + + SeeAlso [st_init_table st_ptrhash] + +******************************************************************************/ +int +st_ptrcmp(const char *x, const char *y) +{ + return ST_NUMCMP(x, y); + +} /* st_ptrcmp */ + + +/**Function******************************************************************** + + Synopsis [Initializes a generator.] + + Description [Returns a generator handle which when used with + st_gen() will progressively return each (key, value) record in + `table'.] + + SideEffects [None] + + SeeAlso [st_free_gen] + +******************************************************************************/ +st_generator * +st_init_gen(st_table *table) +{ + st_generator *gen; + + gen = ALLOC(st_generator, 1); + if (gen == NIL(st_generator)) { + return NIL(st_generator); + } + gen->table = table; + gen->entry = NIL(st_table_entry); + gen->index = 0; + return gen; + +} /* st_init_gen */ + + +/**Function******************************************************************** + + Synopsis [returns the next (key, value) pair in the generation + sequence. ] + + Description [Given a generator returned by st_init_gen(), this + routine returns the next (key, value) pair in the generation + sequence. The pointer `value_p' can be zero which means no value + will be returned. When there are no more items in the generation + sequence, the routine returns 0. + + While using a generation sequence, deleting any (key, value) pair + other than the one just generated may cause a fatal error when + st_gen() is called later in the sequence and is therefore not + recommended.] + + SideEffects [None] + + SeeAlso [st_gen_int] + +******************************************************************************/ +int +st_gen(st_generator *gen, void *key_p, void *value_p) +{ + int i; + + if (gen->entry == NIL(st_table_entry)) { + /* try to find next entry */ + for(i = gen->index; i < gen->table->num_bins; i++) { + if (gen->table->bins[i] != NIL(st_table_entry)) { + gen->index = i+1; + gen->entry = gen->table->bins[i]; + break; + } + } + if (gen->entry == NIL(st_table_entry)) { + return 0; /* that's all folks ! */ + } + } + *(char **)key_p = gen->entry->key; + if (value_p != NIL(void)) { + *(char **)value_p = gen->entry->record; + } + gen->entry = gen->entry->next; + return 1; + +} /* st_gen */ + + +/**Function******************************************************************** + + Synopsis [Returns the next (key, value) pair in the generation + sequence.] + + Description [Given a generator returned by st_init_gen(), this + routine returns the next (key, value) pair in the generation + sequence. `value_p' must be a pointer to an integer. The pointer + `value_p' can be zero which means no value will be returned. When + there are no more items in the generation sequence, the routine + returns 0.] + + SideEffects [None] + + SeeAlso [st_gen] + +******************************************************************************/ +int +st_gen_int(st_generator *gen, void *key_p, int *value_p) +{ + int i; + + if (gen->entry == NIL(st_table_entry)) { + /* try to find next entry */ + for(i = gen->index; i < gen->table->num_bins; i++) { + if (gen->table->bins[i] != NIL(st_table_entry)) { + gen->index = i+1; + gen->entry = gen->table->bins[i]; + break; + } + } + if (gen->entry == NIL(st_table_entry)) { + return 0; /* that's all folks ! */ + } + } + *(char **)key_p = gen->entry->key; + if (value_p != NIL(int)) { + *value_p = (int) (long) gen->entry->record; + } + gen->entry = gen->entry->next; + return 1; + +} /* st_gen_int */ + + +/**Function******************************************************************** + + Synopsis [Reclaims the resources associated with `gen'.] + + Description [After generating all items in a generation sequence, + this routine must be called to reclaim the resources associated with + `gen'.] + + SideEffects [None] + + SeeAlso [st_init_gen] + +******************************************************************************/ +void +st_free_gen(st_generator *gen) +{ + FREE(gen); + +} /* st_free_gen */ + + +/*---------------------------------------------------------------------------*/ +/* Definition of internal functions */ +/*---------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------*/ +/* Definition of static functions */ +/*---------------------------------------------------------------------------*/ + +/**Function******************************************************************** + + Synopsis [Rehashes a symbol table.] + + Description [Rehashes a symbol table.] + + SideEffects [None] + + SeeAlso [st_insert] + +******************************************************************************/ +static int +rehash(st_table *table) +{ + st_table_entry *ptr, *next, **old_bins; + int i, old_num_bins, hash_val, old_num_entries; + + /* save old values */ + old_bins = table->bins; + old_num_bins = table->num_bins; + old_num_entries = table->num_entries; + + /* rehash */ + table->num_bins = (int) (table->grow_factor * old_num_bins); + if (table->num_bins % 2 == 0) { + table->num_bins += 1; + } + table->num_entries = 0; + table->bins = ALLOC(st_table_entry *, table->num_bins); + if (table->bins == NIL(st_table_entry *)) { + table->bins = old_bins; + table->num_bins = old_num_bins; + table->num_entries = old_num_entries; + return ST_OUT_OF_MEM; + } + /* initialize */ + for (i = 0; i < table->num_bins; i++) { + table->bins[i] = 0; + } + + /* copy data over */ + for (i = 0; i < old_num_bins; i++) { + ptr = old_bins[i]; + while (ptr != NIL(st_table_entry)) { + next = ptr->next; + hash_val = do_hash(ptr->key, table); + ptr->next = table->bins[hash_val]; + table->bins[hash_val] = ptr; + table->num_entries++; + ptr = next; + } + } + FREE(old_bins); + + return 1; + +} /* rehash */ diff --git a/svf/lib/CUDD/util.c b/svf/lib/CUDD/util.c new file mode 100644 index 000000000..03b01de10 --- /dev/null +++ b/svf/lib/CUDD/util.c @@ -0,0 +1,213 @@ +/* LINTLIBRARY */ + +#include +#include "CUDD/util.h" + +#ifdef IBM_WATC /* IBM Waterloo-C compiler (same as bsd 4.2) */ +#define void int +#define BSD +#endif + +#ifdef BSD +#include +#include +#include +#endif + +#if defined(UNIX60) || defined(UNIX100) || defined(__CYGWIN32__) +#include +#include +#endif + +#ifdef vms /* VAX/C compiler -- times() with 100 HZ clock */ +#include +#include +#endif + + + +/* + * util_cpu_time -- return a long which represents the elapsed processor + * time in milliseconds since some constant reference + */ +long +util_cpu_time() +{ + long t = 0; + +#ifdef BSD + struct rusage rusage; + (void) getrusage(RUSAGE_SELF, &rusage); + t = (long) rusage.ru_utime.tv_sec*1000 + rusage.ru_utime.tv_usec/1000; +#endif + +#ifdef IBMPC + long ltime; + (void) time(<ime); + t = ltime * 1000; +#endif + +#ifdef UNIX60 /* times() with 60 Hz resolution */ + struct tms buffer; + times(&buffer); + t = buffer.tms_utime * 16.6667; +#endif + +#ifdef UNIX100 + struct tms buffer; /* times() with 100 Hz resolution */ + times(&buffer); + t = buffer.tms_utime * 10; +#endif + +#ifdef __CYGWIN32__ + /* Works under Windows NT but not Windows 95. */ + struct tms buffer; /* times() with 1000 Hz resolution */ + times(&buffer); + t = buffer.tms_utime; +#endif + +#ifdef vms + tbuffer_t buffer; /* times() with 100 Hz resolution */ + times(&buffer); + t = buffer.proc_user_time * 10; +#endif + + return t; +} + + +/* + * These are interface routines to be placed between a program and the + * system memory allocator. + * + * It forces well-defined semantics for several 'borderline' cases: + * + * malloc() of a 0 size object is guaranteed to return something + * which is not 0, and can safely be freed (but not dereferenced) + * free() accepts (silently) an 0 pointer + * realloc of a 0 pointer is allowed, and is equiv. to malloc() + * For the IBM/PC it forces no object > 64K; note that the size argument + * to malloc/realloc is a 'long' to catch this condition + * + * The function pointer MMoutOfMemory() contains a vector to handle a + * 'out-of-memory' error (which, by default, points at a simple wrap-up + * and exit routine). + */ + +#ifdef __cplusplus +extern "C" { +#endif + +extern char *MMalloc(long); +extern void MMout_of_memory(long); +extern char *MMrealloc(char *, long); + +void (*MMoutOfMemory)(long) = MMout_of_memory; + +#ifdef __cplusplus +} +#endif + + +/* MMout_of_memory -- out of memory for lazy people, flush and exit */ +void +MMout_of_memory(long size) +{ + (void) fflush(stdout); + (void) fprintf(stderr, "\nout of memory allocating %lu bytes\n", + (unsigned long) size); + exit(1); +} + + +char * +MMalloc(long size) +{ + char *p; + +#ifdef IBMPC + if (size > 65000L) { + if (MMoutOfMemory != (void (*)(long)) 0 ) (*MMoutOfMemory)(size); + return NIL(char); + } +#endif + if (size == 0) size = sizeof(long); + if ((p = (char *) malloc((unsigned long) size)) == NIL(char)) { + if (MMoutOfMemory != 0 ) (*MMoutOfMemory)(size); + return NIL(char); + } + return p; +} + + +char * +MMrealloc(char *obj, long size) +{ + char *p; + +#ifdef IBMPC + if (size > 65000L) { + if (MMoutOfMemory != 0 ) (*MMoutOfMemory)(size); + return NIL(char); + } +#endif + if (obj == NIL(char)) return MMalloc(size); + if (size <= 0) size = sizeof(long); + if ((p = (char *) realloc(obj, (unsigned long) size)) == NIL(char)) { + if (MMoutOfMemory != 0 ) (*MMoutOfMemory)(size); + return NIL(char); + } + return p; +} + + +/* $Id: datalimit.c,v 1.5 2007/08/24 18:17:31 fabio Exp fabio $ */ + +#ifndef HAVE_SYS_RESOURCE_H +#define HAVE_SYS_RESOURCE_H 1 +#endif +#ifndef HAVE_SYS_TIME_H +#define HAVE_SYS_TIME_H 1 +#endif +#ifndef HAVE_GETRLIMIT +#define HAVE_GETRLIMIT 1 +#endif + +#if HAVE_SYS_RESOURCE_H == 1 +#if HAVE_SYS_TIME_H == 1 +#include +#endif +#include +#endif + +#ifndef RLIMIT_DATA_DEFAULT +#define RLIMIT_DATA_DEFAULT 67108864 /* assume 64MB by default */ +#endif + +#ifndef EXTERN +# ifdef __cplusplus +# define EXTERN extern "C" +# else +# define EXTERN extern +# endif +#endif + +EXTERN unsigned long getSoftDataLimit(void); + +unsigned long +getSoftDataLimit(void) +{ +#if HAVE_SYS_RESOURCE_H == 1 && HAVE_GETRLIMIT == 1 && defined(RLIMIT_DATA) + struct rlimit rl; + int result; + + result = getrlimit(RLIMIT_DATA, &rl); + if (result != 0 || rl.rlim_cur == RLIM_INFINITY) + return((unsigned long) RLIMIT_DATA_DEFAULT); + else + return((unsigned long) rl.rlim_cur); +#else + return((unsigned long) RLIMIT_DATA_DEFAULT); +#endif + +} /* end of getSoftDataLimit */ diff --git a/svf/lib/DDA/ContextDDA.cpp b/svf/lib/DDA/ContextDDA.cpp index eb67b121e..eea0e5e4f 100644 --- a/svf/lib/DDA/ContextDDA.cpp +++ b/svf/lib/DDA/ContextDDA.cpp @@ -1,25 +1,3 @@ -//===- ContextDDA.cpp -- Context-sensitive demand-driven analysis-------------// -// -// SVF: Static Value-Flow Analysis -// -// Copyright (C) <2013-> -// - -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU Affero General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Affero General Public License for more details. - -// You should have received a copy of the GNU Affero General Public License -// along with this program. If not, see . -// -//===----------------------------------------------------------------------===// - /* * ContextDDA.cpp * @@ -31,7 +9,6 @@ #include "DDA/ContextDDA.h" #include "DDA/FlowDDA.h" #include "DDA/DDAClient.h" -#include "MemoryModel/PointsTo.h" using namespace SVF; using namespace SVFUtil; @@ -39,7 +16,7 @@ using namespace SVFUtil; /*! * Constructor */ -ContextDDA::ContextDDA(SVFIR* _pag, DDAClient* client) +ContextDDA::ContextDDA(PAG* _pag, DDAClient* client) : CondPTAImpl(_pag, PointerAnalysis::Cxt_DDA),DDAVFSolver(), _client(client) { @@ -76,10 +53,10 @@ const CxtPtSet& ContextDDA::computeDDAPts(const CxtVar& var) { resetQuery(); - LocDPItem::setMaxBudget(Options::CxtBudget()); + LocDPItem::setMaxBudget(Options::CxtBudget); NodeID id = var.get_id(); - PAGNode* node = getPAG()->getGNode(id); + PAGNode* node = getPAG()->getPAGNode(id); CxtLocDPItem dpm = getDPIm(var, getDefSVFGNode(node)); // start DDA analysis @@ -162,19 +139,19 @@ CxtPtSet ContextDDA::processGepPts(const GepSVFGNode* gep, const CxtPtSet& srcPt tmpDstPts.set(ptd); else { - const GepStmt* gepStmt = SVFUtil::cast(gep->getPAGEdge()); - if (gepStmt->isVariantFieldGep()) + if (SVFUtil::isa(gep->getPAGEdge())) { setObjFieldInsensitive(ptd.get_id()); - CxtVar var(ptd.get_cond(),getFIObjVar(ptd.get_id())); + CxtVar var(ptd.get_cond(),getFIObjNode(ptd.get_id())); tmpDstPts.set(var); } - else + else if (const NormalGepPE* normalGep = SVFUtil::dyn_cast(gep->getPAGEdge())) { - CxtVar var(ptd.get_cond(),getGepObjVar(ptd.get_id(), - gepStmt->getAccessPath().getConstantStructFldIdx())); + CxtVar var(ptd.get_cond(),getGepObjNode(ptd.get_id(),normalGep->getLocationSet())); tmpDstPts.set(var); } + else + assert(false && "new gep edge?"); } } @@ -186,16 +163,16 @@ CxtPtSet ContextDDA::processGepPts(const GepSVFGNode* gep, const CxtPtSet& srcPt return tmpDstPts; } -bool ContextDDA::testIndCallReachability(CxtLocDPItem& dpm, const SVFFunction* callee, const CallICFGNode* cs) +bool ContextDDA::testIndCallReachability(CxtLocDPItem& dpm, const SVFFunction* callee, const CallBlockNode* cs) { if(getPAG()->isIndirectCallSites(cs)) { NodeID id = getPAG()->getFunPtr(cs); - PAGNode* node = getPAG()->getGNode(id); + PAGNode* node = getPAG()->getPAGNode(id); CxtVar funptrVar(dpm.getCondVar().get_cond(), id); CxtLocDPItem funptrDpm = getDPIm(funptrVar,getDefSVFGNode(node)); PointsTo pts = getBVPointsTo(findPT(funptrDpm)); - if(pts.test(getPAG()->getObjectNode(callee))) + if(pts.test(getPAG()->getObjectNode(callee->getLLVMFun()))) return true; else return false; @@ -216,7 +193,7 @@ CallSiteID ContextDDA::getCSIDAtCall(CxtLocDPItem&, const SVFGEdge* edge) else svfg_csId = SVFUtil::cast(edge)->getCallSiteId(); - const CallICFGNode* cbn = getSVFG()->getCallSite(svfg_csId); + const CallBlockNode* cbn = getSVFG()->getCallSite(svfg_csId); const SVFFunction* callee = edge->getDstNode()->getFun(); if(getPTACallGraph()->hasCallSiteID(cbn,callee)) @@ -240,7 +217,7 @@ CallSiteID ContextDDA::getCSIDAtRet(CxtLocDPItem&, const SVFGEdge* edge) else svfg_csId = SVFUtil::cast(edge)->getCallSiteId(); - const CallICFGNode* cbn = getSVFG()->getCallSite(svfg_csId); + const CallBlockNode* cbn = getSVFG()->getCallSite(svfg_csId); const SVFFunction* callee = edge->getSrcNode()->getFun(); if(getPTACallGraph()->hasCallSiteID(cbn,callee)) @@ -336,32 +313,27 @@ bool ContextDDA::isHeapCondMemObj(const CxtVar& var, const StoreSVFGNode*) { const MemObj* mem = _pag->getObject(getPtrNodeID(var)); assert(mem && "memory object is null??"); - if (mem->isHeap()) + if(mem->isHeap()) { - if (!mem->getValue()) - { - PAGNode *pnode = _pag->getGNode(getPtrNodeID(var)); - GepObjVar* gepobj = SVFUtil::dyn_cast(pnode); - if (gepobj != nullptr) - { - assert(SVFUtil::isa(_pag->getGNode(gepobj->getBaseNode())) - && "empty refVal in a gep object whose base is a non-dummy object"); + if (!mem->getRefVal()) { + PAGNode *pnode = _pag->getPAGNode(getPtrNodeID(var)); + if(GepObjPN* gepobj = SVFUtil::dyn_cast(pnode)){ + assert(SVFUtil::isa(_pag->getPAGNode(gepobj->getBaseNode())) && "emtpy refVal in a gep object whose base is a non-dummy object"); } - else - { - assert((SVFUtil::isa(pnode)) - && "empty refVal in non-dummy object"); + else{ + assert((SVFUtil::isa(pnode) || SVFUtil::isa(pnode)) && "empty refVal in non-dummy object"); } return true; } - else if(const SVFInstruction* mallocSite = SVFUtil::dyn_cast(mem->getValue())) + else if(const Instruction* mallocSite = SVFUtil::dyn_cast(mem->getRefVal())) { - const SVFFunction* svfFun = mallocSite->getFunction(); + const Function* fun = mallocSite->getFunction(); + const SVFFunction* svfFun = LLVMModuleSet::getLLVMModuleSet()->getSVFFunction(fun); if(_ander->isInRecursion(svfFun)) return true; if(var.get_cond().isConcreteCxt() == false) return true; - if(_pag->getICFG()->isInLoop(mallocSite)) + if(loopInfoBuilder.getLoopInfo(fun)->getLoopFor(mallocSite->getParent())) return true; } } diff --git a/svf/lib/DDA/DDAClient.cpp b/svf/lib/DDA/DDAClient.cpp index ba22664a2..110d9d47c 100644 --- a/svf/lib/DDA/DDAClient.cpp +++ b/svf/lib/DDA/DDAClient.cpp @@ -1,25 +1,3 @@ -//===- DDAClient.cpp -- Clients of demand-driven analysis-------------// -// -// SVF: Static Value-Flow Analysis -// -// Copyright (C) <2013-> -// - -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU Affero General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Affero General Public License for more details. - -// You should have received a copy of the GNU Affero General Public License -// along with this program. If not, see . -// -//===----------------------------------------------------------------------===// - /* * @file: DDAClient.cpp * @author: yesen @@ -32,7 +10,7 @@ #include "Util/Options.h" #include "Util/SVFUtil.h" -#include "MemoryModel/PointsTo.h" +#include "SVF-FE/CPPUtil.h" #include "DDA/DDAClient.h" #include "DDA/FlowDDA.h" @@ -58,7 +36,7 @@ void DDAClient::answerQueries(PointerAnalysis* pta) for (OrderedNodeSet::iterator nIter = candidateQueries.begin(); nIter != candidateQueries.end(); ++nIter,++count) { - PAGNode* node = pta->getPAG()->getGNode(*nIter); + PAGNode* node = pta->getPAG()->getPAGNode(*nIter); if(pta->getPAG()->isValidTopLevelPtr(node)) { DBOUT(DGENERAL,outs() << "\n@@Computing PointsTo for :" << node->getId() << @@ -75,15 +53,15 @@ void DDAClient::answerQueries(PointerAnalysis* pta) stat->setMemUsageAfter(vmrss, vmsize); } -OrderedNodeSet& FunptrDDAClient::collectCandidateQueries(SVFIR* p) +OrderedNodeSet& FunptrDDAClient::collectCandidateQueries(PAG* p) { setPAG(p); - for(SVFIR::CallSiteToFunPtrMap::const_iterator it = pag->getIndirectCallsites().begin(), + for(PAG::CallSiteToFunPtrMap::const_iterator it = pag->getIndirectCallsites().begin(), eit = pag->getIndirectCallsites().end(); it!=eit; ++it) { - if (SVFUtil::getSVFCallSite(it->first->getCallSite()).isVirtualCall()) + if (cppUtil::isVirtualCallSite(SVFUtil::getLLVMCallSite(it->first->getCallSite()))) { - const SVFValue* vtblPtr = SVFUtil::getSVFCallSite(it->first->getCallSite()).getVtablePtr(); + const Value *vtblPtr = cppUtil::getVCallVtblPtr(SVFUtil::getLLVMCallSite(it->first->getCallSite())); assert(pag->hasValueNode(vtblPtr) && "not a vtable pointer?"); NodeID vtblId = pag->getValueNode(vtblPtr); addCandidate(vtblId); @@ -116,7 +94,7 @@ void FunptrDDAClient::performStat(PointerAnalysis* pta) const PointsTo& anderPts = ander->getPts(vtptr); PTACallGraph* callgraph = ander->getPTACallGraph(); - const CallICFGNode* cbn = nIter->second; + const CallBlockNode* cbn = nIter->second; if(!callgraph->hasIndCSCallees(cbn)) { @@ -145,8 +123,8 @@ void FunptrDDAClient::performStat(PointerAnalysis* pta) ++morePreciseCallsites; outs() << "============more precise callsite =================\n"; - outs() << (nIter->second)->getCallSite()->toString() << "\n"; - outs() << (nIter->second)->getCallSite()->getSourceLoc() << "\n"; + outs() << *(nIter->second)->getCallSite() << "\n"; + outs() << getSourceLoc((nIter->second)->getCallSite()) << "\n"; outs() << "\n"; outs() << "------ander pts or vtable num---(" << anderPts.count() << ")--\n"; outs() << "------DDA vfn num---(" << ander_vfns.size() << ")--\n"; @@ -173,11 +151,11 @@ void FunptrDDAClient::performStat(PointerAnalysis* pta) /// Only collect function pointers as query candidates. -OrderedNodeSet& AliasDDAClient::collectCandidateQueries(SVFIR* pag) +OrderedNodeSet& AliasDDAClient::collectCandidateQueries(PAG* pag) { setPAG(pag); - SVFStmt::SVFStmtSetTy& loads = pag->getSVFStmtSet(SVFStmt::Load); - for (SVFStmt::SVFStmtSetTy::iterator iter = loads.begin(), eiter = + PAGEdge::PAGEdgeSetTy& loads = pag->getEdgeSet(PAGEdge::Load); + for (PAGEdge::PAGEdgeSetTy::iterator iter = loads.begin(), eiter = loads.end(); iter != eiter; ++iter) { PAGNode* loadsrc = (*iter)->getSrcNode(); @@ -185,16 +163,16 @@ OrderedNodeSet& AliasDDAClient::collectCandidateQueries(SVFIR* pag) addCandidate(loadsrc->getId()); } - SVFStmt::SVFStmtSetTy& stores = pag->getSVFStmtSet(SVFStmt::Store); - for (SVFStmt::SVFStmtSetTy::iterator iter = stores.begin(), eiter = + PAGEdge::PAGEdgeSetTy& stores = pag->getEdgeSet(PAGEdge::Store); + for (PAGEdge::PAGEdgeSetTy::iterator iter = stores.begin(), eiter = stores.end(); iter != eiter; ++iter) { PAGNode* storedst = (*iter)->getDstNode(); storeDstNodes.insert(storedst); addCandidate(storedst->getId()); } - SVFStmt::SVFStmtSetTy& geps = pag->getSVFStmtSet(SVFStmt::Gep); - for (SVFStmt::SVFStmtSetTy::iterator iter = geps.begin(), eiter = + PAGEdge::PAGEdgeSetTy& geps = pag->getEdgeSet(PAGEdge::NormalGep); + for (PAGEdge::PAGEdgeSetTy::iterator iter = geps.begin(), eiter = geps.end(); iter != eiter; ++iter) { PAGNode* gepsrc = (*iter)->getSrcNode(); @@ -218,8 +196,8 @@ void AliasDDAClient::performStat(PointerAnalysis* pta) AliasResult result = pta->alias(node1->getId(),node2->getId()); outs() << "\n=================================================\n"; - outs() << "Alias Query for (" << node1->getValue()->toString() << ","; - outs() << node2->getValue()->toString() << ") \n"; + outs() << "Alias Query for (" << *node1->getValue() << ","; + outs() << *node2->getValue() << ") \n"; outs() << "[NodeID:" << node1->getId() << ", NodeID:" << node2->getId() << " " << result << "]\n"; outs() << "=================================================\n"; diff --git a/svf/lib/DDA/DDAPass.cpp b/svf/lib/DDA/DDAPass.cpp index 0b3a8e01a..8f16f8c16 100644 --- a/svf/lib/DDA/DDAPass.cpp +++ b/svf/lib/DDA/DDAPass.cpp @@ -1,26 +1,3 @@ -//===- DDAPass.cpp -- Demand-driven analysis driver pass-------------// -// -// SVF: Static Value-Flow Analysis -// -// Copyright (C) <2013-> -// - -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU Affero General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Affero General Public License for more details. - -// You should have received a copy of the GNU Affero General Public License -// along with this program. If not, see . -// -//===----------------------------------------------------------------------===// - - /* * @file: DDAPass.cpp * @author: Yulei Sui @@ -34,16 +11,18 @@ #include "DDA/FlowDDA.h" #include "DDA/ContextDDA.h" #include "DDA/DDAClient.h" +#include "SVF-FE/PAGBuilder.h" #include #include using namespace SVF; using namespace SVFUtil; -using namespace std; char DDAPass::ID = 0; +static llvm::RegisterPass DDAPA("dda", "Demand-driven Pointer Analysis Pass"); + DDAPass::~DDAPass() { // _pta->dumpStat(); @@ -52,34 +31,40 @@ DDAPass::~DDAPass() } -void DDAPass::runOnModule(SVFIR* pag) +void DDAPass::runOnModule(SVFModule* module) { /// initialization for llvm alias analyzer - //InitializeAliasAnalysis(this, getDataLayout(&module)); + //InitializeAliasAnalysis(this, SymbolTableInfo::getDataLayout(&module)); - selectClient(pag->getModule()); + selectClient(module); for (u32_t i = PointerAnalysis::FlowS_DDA; i < PointerAnalysis::Default_PTA; i++) { - PointerAnalysis::PTATY iPtTy = static_cast(i); - if (Options::DDASelected(iPtTy)) - runPointerAnalysis(pag, i); + if (Options::DDASelected.isSet(i)) + runPointerAnalysis(module, i); } } +bool DDAPass::runOnModule(Module& module) +{ + SVFModule* svfModule = LLVMModuleSet::getLLVMModuleSet()->buildSVFModule(module); + runOnModule(svfModule); + return false; +} + /// select a client to initialize queries void DDAPass::selectClient(SVFModule* module) { - if (!Options::UserInputQuery().empty()) + if (!Options::UserInputQuery.empty()) { /// solve function pointer - if (Options::UserInputQuery() == "funptr") + if (Options::UserInputQuery == "funptr") { _client = new FunptrDDAClient(module); } - else if (Options::UserInputQuery() == "alias") + else if (Options::UserInputQuery == "alias") { _client = new AliasDDAClient(module); } @@ -87,10 +72,10 @@ void DDAPass::selectClient(SVFModule* module) else { _client = new DDAClient(module); - if (Options::UserInputQuery() != "all") + if (Options::UserInputQuery != "all") { u32_t buf; // Have a buffer - stringstream ss(Options::UserInputQuery()); // Insert the user input string into a stream + stringstream ss(Options::UserInputQuery); // Insert the user input string into a stream while (ss >> buf) _client->setQuery(buf); } @@ -105,23 +90,26 @@ void DDAPass::selectClient(SVFModule* module) } /// Create pointer analysis according to specified kind and analyze the module. -void DDAPass::runPointerAnalysis(SVFIR* pag, u32_t kind) +void DDAPass::runPointerAnalysis(SVFModule* module, u32_t kind) { - ContextCond::setMaxPathLen(Options::MaxPathLen()); - ContextCond::setMaxCxtLen(Options::MaxContextLen()); + PAGBuilder builder; + PAG* pag = builder.build(module); + + ContextCond::setMaxPathLen(Options::MaxPathLen); + ContextCond::setMaxCxtLen(Options::MaxContextLen); /// Initialize pointer analysis. switch (kind) { case PointerAnalysis::Cxt_DDA: { - _pta = std::make_unique(pag, _client); + _pta = new ContextDDA(pag, _client); break; } case PointerAnalysis::FlowS_DDA: { - _pta = std::make_unique(pag, _client); + _pta = new FlowDDA(pag, _client); break; } default: @@ -129,25 +117,25 @@ void DDAPass::runPointerAnalysis(SVFIR* pag, u32_t kind) break; } - if(Options::WPANum()) + if(Options::WPANum) { - _client->collectWPANum(pag->getModule()); + _client->collectWPANum(module); } else { ///initialize _pta->initialize(); ///compute points-to - _client->answerQueries(_pta.get()); + _client->answerQueries(_pta); ///finalize _pta->finalize(); - if(Options::PrintCPts()) + if(Options::PrintCPts) _pta->dumpCPts(); if (_pta->printStat()) - _client->performStat(_pta.get()); + _client->performStat(_pta); - if (Options::PrintQueryPts()) + if (Options::PrintQueryPts) printQueryPTS(); } } @@ -158,9 +146,9 @@ void DDAPass::runPointerAnalysis(SVFIR* pag, u32_t kind) */ void DDAPass::initCxtInsensitiveEdges(PointerAnalysis* pta, const SVFG* svfg,const SVFGSCC* svfgSCC, SVFGEdgeSet& insensitveEdges) { - if(Options::InsenRecur()) + if(Options::InsenRecur) collectCxtInsenEdgeForRecur(pta,svfg,insensitveEdges); - else if(Options::InsenCycle()) + else if(Options::InsenCycle) collectCxtInsenEdgeForVFCycle(pta,svfg,svfgSCC,insensitveEdges); } @@ -177,8 +165,8 @@ bool DDAPass::edgeInSVFGSCC(const SVFGSCC* svfgSCC,const SVFGEdge* edge) */ bool DDAPass::edgeInCallGraphSCC(PointerAnalysis* pta,const SVFGEdge* edge) { - const SVFFunction* srcFun = edge->getSrcNode()->getICFGNode()->getFun(); - const SVFFunction* dstFun = edge->getDstNode()->getICFGNode()->getFun(); + const SVFFunction* srcFun = edge->getSrcNode()->getICFGNode()->getFun(); + const SVFFunction* dstFun = edge->getDstNode()->getICFGNode()->getFun(); if(srcFun && dstFun) { @@ -234,8 +222,8 @@ void DDAPass::collectCxtInsenEdgeForVFCycle(PointerAnalysis* pta, const SVFG* sv if(this->edgeInSVFGSCC(svfgSCC,edge)) { - const SVFFunction* srcFun = edge->getSrcNode()->getICFGNode()->getFun(); - const SVFFunction* dstFun = edge->getDstNode()->getICFGNode()->getFun(); + const SVFFunction* srcFun = edge->getSrcNode()->getICFGNode()->getFun(); + const SVFFunction* dstFun = edge->getDstNode()->getICFGNode()->getFun(); if(srcFun && dstFun) { @@ -280,12 +268,12 @@ void DDAPass::collectCxtInsenEdgeForVFCycle(PointerAnalysis* pta, const SVFG* sv AliasResult DDAPass::alias(NodeID node1, NodeID node2) { - SVFIR* pag = _pta->getPAG(); + PAG* pag = _pta->getPAG(); - if(pag->isValidTopLevelPtr(pag->getGNode(node1))) + if(pag->isValidTopLevelPtr(pag->getPAGNode(node1))) _pta->computeDDAPts(node1); - if(pag->isValidTopLevelPtr(pag->getGNode(node2))) + if(pag->isValidTopLevelPtr(pag->getPAGNode(node2))) _pta->computeDDAPts(node2); return _pta->alias(node1,node2); @@ -294,29 +282,29 @@ AliasResult DDAPass::alias(NodeID node1, NodeID node2) * Return alias results based on our points-to/alias analysis * TODO: Need to handle PartialAlias and MustAlias here. */ -AliasResult DDAPass::alias(const SVFValue* V1, const SVFValue* V2) +AliasResult DDAPass::alias(const Value* V1, const Value* V2) { - SVFIR* pag = _pta->getPAG(); + PAG* pag = _pta->getPAG(); /// TODO: When this method is invoked during compiler optimizations, the IR /// used for pointer analysis may been changed, so some Values may not - /// find corresponding SVFIR node. In this case, we only check alias - /// between two Values if they both have SVFIR nodes. Otherwise, MayAlias + /// find corresponding PAG node. In this case, we only check alias + /// between two Values if they both have PAG nodes. Otherwise, MayAlias /// will be returned. if (pag->hasValueNode(V1) && pag->hasValueNode(V2)) { - PAGNode* node1 = pag->getGNode(pag->getValueNode(V1)); + PAGNode* node1 = pag->getPAGNode(pag->getValueNode(V1)); if(pag->isValidTopLevelPtr(node1)) _pta->computeDDAPts(node1->getId()); - PAGNode* node2 = pag->getGNode(pag->getValueNode(V2)); + PAGNode* node2 = pag->getPAGNode(pag->getValueNode(V2)); if(pag->isValidTopLevelPtr(node2)) _pta->computeDDAPts(node2->getId()); return _pta->alias(V1,V2); } - return AliasResult::MayAlias; + return llvm::MayAlias; } /*! diff --git a/svf/lib/DDA/DDAStat.cpp b/svf/lib/DDA/DDAStat.cpp index e9c3a2bc3..540737e69 100644 --- a/svf/lib/DDA/DDAStat.cpp +++ b/svf/lib/DDA/DDAStat.cpp @@ -1,25 +1,3 @@ -//===- DDAStat.cpp -- Statistics for demand-driven pass-------------// -// -// SVF: Static Value-Flow Analysis -// -// Copyright (C) <2013-> -// - -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU Affero General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Affero General Public License for more details. - -// You should have received a copy of the GNU Affero General Public License -// along with this program. If not, see . -// -//===----------------------------------------------------------------------===// - /* * DDAStat.cpp * @@ -31,13 +9,11 @@ #include "DDA/FlowDDA.h" #include "DDA/ContextDDA.h" #include "Graphs/SVFGStat.h" -#include "MemoryModel/PointsTo.h" #include using namespace SVF; using namespace SVFUtil; -using namespace std; DDAStat::DDAStat(FlowDDA* pta) : PTAStat(pta), flowDDA(pta), contextDDA(nullptr) { @@ -82,6 +58,9 @@ void DDAStat::initDefault() _AnaTimePerQuery = 0; _AnaTimeCyclePerQuery = 0; _TotalTimeOfQueries = 0; + + _vmrssUsageBefore = _vmrssUsageAfter = 0; + _vmsizeUsageBefore = _vmsizeUsageAfter = 0; } SVFG* DDAStat::getSVFG() const @@ -225,10 +204,10 @@ void DDAStat::performStat() getNumOfOOBQuery(); - for (SVFIR::const_iterator nodeIt = SVFIR::getPAG()->begin(), nodeEit = SVFIR::getPAG()->end(); nodeIt != nodeEit; nodeIt++) + for (PAG::const_iterator nodeIt = PAG::getPAG()->begin(), nodeEit = PAG::getPAG()->end(); nodeIt != nodeEit; nodeIt++) { PAGNode* pagNode = nodeIt->second; - if(SVFUtil::isa(pagNode)) + if(SVFUtil::isa(pagNode)) { if(getPTA()->isLocalVarInRecursiveFun(nodeIt->first)) { @@ -262,9 +241,9 @@ void DDAStat::performStat() PTNumStatMap["PointsToBlkPtr"] = _NumOfBlackholePtr; PTNumStatMap["NumOfMustAA"] = _TotalNumOfMustAliases; PTNumStatMap["NumOfInfePath"] = _TotalNumOfInfeasiblePath; - PTNumStatMap["NumOfStore"] = SVFIR::getPAG()->getPTASVFStmtSet(SVFStmt::Store).size(); - timeStatMap["MemoryUsageVmrss"] = _vmrssUsageAfter - _vmrssUsageBefore; - timeStatMap["MemoryUsageVmsize"] = _vmsizeUsageAfter - _vmsizeUsageBefore; + PTNumStatMap["NumOfStore"] = PAG::getPAG()->getPTAEdgeSet(PAGEdge::Store).size(); + PTNumStatMap["MemoryUsageVmrss"] = _vmrssUsageAfter - _vmrssUsageBefore; + PTNumStatMap["MemoryUsageVmsize"] = _vmsizeUsageAfter - _vmsizeUsageBefore; printStat(); } @@ -274,25 +253,25 @@ void DDAStat::printStatPerQuery(NodeID ptr, const PointsTo& pts) if (timeStatMap.empty() == false && NumPerQueryStatMap.empty() == false) { - SVFUtil::outs().flags(std::ios::left); + std::cout.flags(std::ios::left); unsigned field_width = 20; - SVFUtil::outs() << "---------------------Stat Per Query--------------------------------\n"; + std::cout << "---------------------Stat Per Query--------------------------------\n"; for (TIMEStatMap::iterator it = timeStatMap.begin(), eit = timeStatMap.end(); it != eit; ++it) { // format out put with width 20 space - SVFUtil::outs() << std::setw(field_width) << it->first << it->second << "\n"; + std::cout << std::setw(field_width) << it->first << it->second << "\n"; } for (NUMStatMap::iterator it = NumPerQueryStatMap.begin(), eit = NumPerQueryStatMap.end(); it != eit; ++it) { // format out put with width 20 space - SVFUtil::outs() << std::setw(field_width) << it->first << it->second << "\n"; + std::cout << std::setw(field_width) << it->first << it->second << "\n"; } } getPTA()->dumpPts(ptr, pts); } -void DDAStat::printStat(string str) +void DDAStat::printStat() { if(flowDDA) @@ -305,6 +284,6 @@ void DDAStat::printStat(string str) contextDDA->getSVFG()->getStat()->performSCCStat(contextDDA->getInsensitiveEdgeSet()); } - SVFUtil::outs() << "\n****Demand-Driven Pointer Analysis Statistics****\n"; - PTAStat::printStat(str); + std::cout << "\n****Demand-Driven Pointer Analysis Statistics****\n"; + PTAStat::printStat(); } diff --git a/svf/lib/DDA/FlowDDA.cpp b/svf/lib/DDA/FlowDDA.cpp index bf22a1793..e81a1fafb 100644 --- a/svf/lib/DDA/FlowDDA.cpp +++ b/svf/lib/DDA/FlowDDA.cpp @@ -1,25 +1,3 @@ -//===- FlowDDA.cpp -- Flow-sensitive demand-driven analysis -------------// -// -// SVF: Static Value-Flow Analysis -// -// Copyright (C) <2013-> -// - -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU Affero General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Affero General Public License for more details. - -// You should have received a copy of the GNU Affero General Public License -// along with this program. If not, see . -// -//===----------------------------------------------------------------------===// - /* * FlowDDA.cpp * @@ -30,7 +8,6 @@ #include "Util/Options.h" #include "DDA/FlowDDA.h" #include "DDA/DDAClient.h" -#include "MemoryModel/PointsTo.h" using namespace std; using namespace SVF; @@ -43,9 +20,9 @@ using namespace SVFUtil; void FlowDDA::computeDDAPts(NodeID id) { resetQuery(); - LocDPItem::setMaxBudget(Options::FlowBudget()); + LocDPItem::setMaxBudget(Options::FlowBudget); - PAGNode* node = getPAG()->getGNode(id); + PAGNode* node = getPAG()->getPAGNode(id); LocDPItem dpm = getDPIm(node->getId(),getDefSVFGNode(node)); /// start DDA analysis @@ -81,7 +58,7 @@ void FlowDDA::handleOutOfBudgetDpm(const LocDPItem& dpm) bool FlowDDA::testIndCallReachability(LocDPItem&, const SVFFunction* callee, CallSiteID csId) { - const CallICFGNode* cbn = getSVFG()->getCallSite(csId); + const CallBlockNode* cbn = getSVFG()->getCallSite(csId); if(getPAG()->isIndirectCallSites(cbn)) { @@ -148,17 +125,18 @@ PointsTo FlowDDA::processGepPts(const GepSVFGNode* gep, const PointsTo& srcPts) tmpDstPts.set(ptd); else { - const GepStmt* gepStmt = SVFUtil::cast(gep->getPAGEdge()); - if (gepStmt->isVariantFieldGep()) + if (SVFUtil::isa(gep->getPAGEdge())) { setObjFieldInsensitive(ptd); - tmpDstPts.set(getFIObjVar(ptd)); + tmpDstPts.set(getFIObjNode(ptd)); } - else + else if (const NormalGepPE* normalGep = SVFUtil::dyn_cast(gep->getPAGEdge())) { - NodeID fieldSrcPtdNode = getGepObjVar(ptd, gepStmt->getAccessPath().getConstantStructFldIdx()); + NodeID fieldSrcPtdNode = getGepObjNode(ptd, normalGep->getLocationSet()); tmpDstPts.set(fieldSrcPtdNode); } + else + assert(false && "new gep edge?"); } } DBOUT(DDDA, outs() << "\t return created gep objs {"); @@ -180,14 +158,14 @@ bool FlowDDA::isHeapCondMemObj(const NodeID& var, const StoreSVFGNode*) assert(mem && "memory object is null??"); if(mem->isHeap()) { -// if(const Instruction* mallocSite = SVFUtil::dyn_cast(mem->getValue())) { +// if(const Instruction* mallocSite = SVFUtil::dyn_cast(mem->getRefVal())) { // const SVFFunction* fun = mallocSite->getParent()->getParent(); // const SVFFunction* curFun = store->getBB() ? store->getBB()->getParent() : nullptr; // if(fun!=curFun) // return true; // if(_callGraphSCC->isInCycle(_callGraph->getCallGraphNode(fun)->getId())) // return true; -// if(_pag->getICFG()->isInLoop(mallocSite)) +// if(loopInfoBuilder.getLoopInfo(fun)->getLoopFor(mallocSite->getParent())) // return true; // // return false; diff --git a/svf/lib/FastCluster/LICENSE.TXT b/svf/lib/FastCluster/LICENSE.TXT deleted file mode 100644 index ab8b4db7d..000000000 --- a/svf/lib/FastCluster/LICENSE.TXT +++ /dev/null @@ -1,13 +0,0 @@ -Copyright: - * fastcluster_dm.cpp & fastcluster_R_dm.cpp: - © 2011 Daniel Müllner - * fastcluster.(h|cpp) & demo.cpp & plotresult.r: - © 2018 Christoph Dalitz -All rights reserved. - -Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - - * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/svf/lib/FastCluster/fastcluster.cpp b/svf/lib/FastCluster/fastcluster.cpp deleted file mode 100644 index ad79f6d08..000000000 --- a/svf/lib/FastCluster/fastcluster.cpp +++ /dev/null @@ -1,202 +0,0 @@ -// -// C++ standalone version of fastcluster by Daniel Müllner -// -// Copyright: Christoph Dalitz, 2020 -// Daniel Müllner, 2011 -// License: BSD style license -// (see the file LICENSE for details) -// - - -#include -#include -#include - -#include "FastCluster/fastcluster.h" - -// Code by Daniel Müllner -// workaround to make it usable as a standalone version (without R) -bool fc_isnan(double x) -{ - return false; -} -#include "fastcluster_dm.cpp.inc" -#include "fastcluster_R_dm.cpp.inc" - -// -// Assigns cluster labels (0, ..., nclust-1) to the n points such -// that the cluster result is split into nclust clusters. -// -// Input arguments: -// n = number of observables -// merge = clustering result in R format -// nclust = number of clusters -// Output arguments: -// labels = allocated integer array of size n for result -// -void cutree_k(int n, const int* merge, int nclust, int* labels) -{ - - int k,m1,m2,j,l; - - if (nclust > n || nclust < 2) - { - for (j=0; j last_merge(n, 0); - for (k=1; k<=(n-nclust); k++) - { - // (m1,m2) = merge[k,] - m1 = merge[k-1]; - m2 = merge[n-1+k-1]; - if (m1 < 0 && m2 < 0) // both single observables - { - last_merge[-m1-1] = last_merge[-m2-1] = k; - } - else if (m1 < 0 || m2 < 0) // one is a cluster - { - if(m1 < 0) - { - j = -m1; - m1 = m2; - } - else j = -m2; - // merging single observable and cluster - for(l = 0; l < n; l++) - if (last_merge[l] == m1) - last_merge[l] = k; - last_merge[j-1] = k; - } - else // both cluster - { - for(l=0; l < n; l++) - { - if( last_merge[l] == m1 || last_merge[l] == m2 ) - last_merge[l] = k; - } - } - } - - // assign cluster labels - int label = 0; - std::vector z(n,-1); - for (j=0; j= cdist -// -// Input arguments: -// n = number of observables -// merge = clustering result in R format -// height = cluster distance at each merge step -// cdist = cutoff cluster distance -// Output arguments: -// labels = allocated integer array of size n for result -// -void cutree_cdist(int n, const int* merge, double* height, double cdist, int* labels) -{ - - int k; - - for (k=0; k<(n-1); k++) - { - if (height[k] >= cdist) - { - break; - } - } - cutree_k(n, merge, n-k, labels); -} - - -// -// Hierarchical clustering with one of Daniel Muellner's fast algorithms -// -// Input arguments: -// n = number of observables -// distmat = condensed distance matrix, i.e. an n*(n-1)/2 array representing -// the upper triangle (without diagonal elements) of the distance -// matrix, e.g. for n=4: -// d00 d01 d02 d03 -// d10 d11 d12 d13 -> d01 d02 d03 d12 d13 d23 -// d20 d21 d22 d23 -// d30 d31 d32 d33 -// method = cluster metric (see enum hclust_fast_methods) -// Output arguments: -// merge = allocated (n-1)x2 matrix (2*(n-1) array) for storing result. -// Result follows R hclust convention: -// - observable indices start with one -// - merge[i][] contains the merged nodes in step i -// - merge[i][j] is negative when the node is an atom -// height = allocated (n-1) array with distances at each merge step -// Return code: -// 0 = ok -// 1 = invalid method -// -int hclust_fast(int n, double* distmat, int method, int* merge, double* height) -{ - - // call appropriate clustering function - cluster_result Z2(n-1); - if (method == HCLUST_METHOD_SINGLE) - { - // single link - MST_linkage_core(n, distmat, Z2); - } - else if (method == HCLUST_METHOD_COMPLETE) - { - // complete link - NN_chain_core(n, distmat, NULL, Z2); - } - else if (method == HCLUST_METHOD_AVERAGE) - { - // best average distance - double* members = new double[n]; - for (int i=0; i(n, distmat, members, Z2); - delete[] members; - } - else if (method == HCLUST_METHOD_MEDIAN) - { - // best median distance (beware: O(n^3)) - generic_linkage(n, distmat, NULL, Z2); - } - else - { - return 1; - } - - int* order = new int[n]; - if (method == HCLUST_METHOD_MEDIAN) - { - generate_R_dendrogram(merge, height, order, Z2, n); - } - else - { - generate_R_dendrogram(merge, height, order, Z2, n); - } - - delete[] order; // only needed for visualization - - return 0; -} diff --git a/svf/lib/FastCluster/fastcluster_R_dm.cpp.inc b/svf/lib/FastCluster/fastcluster_R_dm.cpp.inc deleted file mode 100644 index cbe126c15..000000000 --- a/svf/lib/FastCluster/fastcluster_R_dm.cpp.inc +++ /dev/null @@ -1,115 +0,0 @@ -// -// Excerpt from fastcluster_R.cpp -// -// Copyright: Daniel Müllner, 2011 -// - -struct pos_node { - t_index pos; - int node; -}; - -void order_nodes(const int N, const int * const merge, const t_index * const node_size, int * const order) { - /* Parameters: - N : number of data points - merge : (N-1)×2 array which specifies the node indices which are - merged in each step of the clustering procedure. - Negative entries -1...-N point to singleton nodes, while - positive entries 1...(N-1) point to nodes which are themselves - parents of other nodes. - node_size : array of node sizes - makes it easier - order : output array of size N - - Runtime: Θ(N) - */ - auto_array_ptr queue(N/2); - - int parent; - int child; - t_index pos = 0; - - queue[0].pos = 0; - queue[0].node = N-2; - t_index idx = 1; - - do { - --idx; - pos = queue[idx].pos; - parent = queue[idx].node; - - // First child - child = merge[parent]; - if (child<0) { // singleton node, write this into the 'order' array. - order[pos] = -child; - ++pos; - } - else { /* compound node: put it on top of the queue and decompose it - in a later iteration. */ - queue[idx].pos = pos; - queue[idx].node = child-1; // convert index-1 based to index-0 based - ++idx; - pos += node_size[child-1]; - } - // Second child - child = merge[parent+N-1]; - if (child<0) { - order[pos] = -child; - } - else { - queue[idx].pos = pos; - queue[idx].node = child-1; - ++idx; - } - } while (idx>0); -} - -#define size_(r_) ( ((r_ -void generate_R_dendrogram(int * const merge, double * const height, int * const order, cluster_result & Z2, const int N) { - // The array "nodes" is a union-find data structure for the cluster - // identites (only needed for unsorted cluster_result input). - union_find nodes(sorted ? 0 : N); - if (!sorted) { - std::stable_sort(Z2[0], Z2[N-1]); - } - - t_index node1, node2; - auto_array_ptr node_size(N-1); - - for (t_index i=0; inode1; - node2 = Z2[i]->node2; - } - else { - node1 = nodes.Find(Z2[i]->node1); - node2 = nodes.Find(Z2[i]->node2); - // Merge the nodes in the union-find data structure by making them - // children of a new node. - nodes.Union(node1, node2); - } - // Sort the nodes in the output array. - if (node1>node2) { - t_index tmp = node1; - node1 = node2; - node2 = tmp; - } - /* Conversion between labeling conventions. - Input: singleton nodes 0,...,N-1 - compound nodes N,...,2N-2 - Output: singleton nodes -1,...,-N - compound nodes 1,...,N - */ - merge[i] = (node1(node1)-1 - : static_cast(node1)-N+1; - merge[i+N-1] = (node2(node2)-1 - : static_cast(node2)-N+1; - height[i] = Z2[i]->dist; - node_size[i] = size_(node1) + size_(node2); - } - - order_nodes(N, merge, node_size, order); -} diff --git a/svf/lib/FastCluster/fastcluster_dm.cpp.inc b/svf/lib/FastCluster/fastcluster_dm.cpp.inc deleted file mode 100644 index 31a142bb2..000000000 --- a/svf/lib/FastCluster/fastcluster_dm.cpp.inc +++ /dev/null @@ -1,1795 +0,0 @@ -/* - fastcluster: Fast hierarchical clustering routines for R and Python - - Copyright © 2011 Daniel Müllner - - - This library implements various fast algorithms for hierarchical, - agglomerative clustering methods: - - (1) Algorithms for the "stored matrix approach": the input is the array of - pairwise dissimilarities. - - MST_linkage_core: single linkage clustering with the "minimum spanning - tree algorithm (Rohlfs) - - NN_chain_core: nearest-neighbor-chain algorithm, suitable for single, - complete, average, weighted and Ward linkage (Murtagh) - - generic_linkage: generic algorithm, suitable for all distance update - formulas (Müllner) - - (2) Algorithms for the "stored data approach": the input are points in a - vector space. - - MST_linkage_core_vector: single linkage clustering for vector data - - generic_linkage_vector: generic algorithm for vector data, suitable for - the Ward, centroid and median methods. - - generic_linkage_vector_alternative: alternative scheme for updating the - nearest neighbors. This method seems faster than "generic_linkage_vector" - for the centroid and median methods but slower for the Ward method. - - All these implementation treat infinity values correctly. They throw an - exception if a NaN distance value occurs. -*/ - -// Older versions of Microsoft Visual Studio do not have the fenv header. -#ifdef _MSC_VER -#if (_MSC_VER == 1500 || _MSC_VER == 1600) -#define NO_INCLUDE_FENV -#endif -#endif -// NaN detection via fenv might not work on systems with software -// floating-point emulation (bug report for Debian armel). -#ifdef __SOFTFP__ -#define NO_INCLUDE_FENV -#endif -#ifdef NO_INCLUDE_FENV -// #pragma message("Do not use fenv header.") -#else -// #pragma message("Use fenv header. If there is a warning about unknown #pragma STDC FENV_ACCESS, this can be ignored.") -// TODO -//#pragma STDC FENV_ACCESS ON -#include -#endif - -#include // for std::pow, std::sqrt -#include // for std::ptrdiff_t -#include // for std::numeric_limits<...>::infinity() -#include // for std::fill_n -#include // for std::runtime_error -#include // for std::string - -#include // also for DBL_MAX, DBL_MIN -#ifndef DBL_MANT_DIG -#error The constant DBL_MANT_DIG could not be defined. -#endif -#define T_FLOAT_MANT_DIG DBL_MANT_DIG - -#ifndef LONG_MAX -#include -#endif -#ifndef LONG_MAX -#error The constant LONG_MAX could not be defined. -#endif -#ifndef INT_MAX -#error The constant INT_MAX could not be defined. -#endif - -#ifndef INT32_MAX -#ifdef _MSC_VER -#if _MSC_VER >= 1600 -#define __STDC_LIMIT_MACROS -#include -#else -typedef __int32 int_fast32_t; -typedef __int64 int64_t; -#endif -#else -#define __STDC_LIMIT_MACROS -#include -#endif -#endif - -#define FILL_N std::fill_n -#ifdef _MSC_VER -#if _MSC_VER < 1600 -#undef FILL_N -#define FILL_N stdext::unchecked_fill_n -#endif -#endif - -// Suppress warnings about (potentially) uninitialized variables. -#ifdef _MSC_VER - #pragma warning (disable:4700) -#endif - -#ifndef HAVE_DIAGNOSTIC -#if __GNUC__ > 4 || (__GNUC__ == 4 && (__GNUC_MINOR__ >= 6)) -#define HAVE_DIAGNOSTIC 1 -#endif -#endif - -#ifndef HAVE_VISIBILITY -#if __GNUC__ >= 4 -#define HAVE_VISIBILITY 1 -#endif -#endif - -/* Since the public interface is given by the Python respectively R interface, - * we do not want other symbols than the interface initalization routines to be - * visible in the shared object file. The "visibility" switch is a GCC concept. - * Hiding symbols keeps the relocation table small and decreases startup time. - * See http://gcc.gnu.org/wiki/Visibility - */ -#if HAVE_VISIBILITY -#pragma GCC visibility push(hidden) -#endif - -typedef int_fast32_t t_index; -#ifndef INT32_MAX -#define MAX_INDEX 0x7fffffffL -#else -#define MAX_INDEX INT32_MAX -#endif -#if (LONG_MAX < MAX_INDEX) -#error The integer format "t_index" must not have a greater range than "long int". -#endif -#if (INT_MAX > MAX_INDEX) -#error The integer format "int" must not have a greater range than "t_index". -#endif -typedef double t_float; - -/* Method codes. - - These codes must agree with the METHODS array in fastcluster.R and the - dictionary mthidx in fastcluster.py. -*/ -enum method_codes { - // non-Euclidean methods - METHOD_METR_SINGLE = 0, - METHOD_METR_COMPLETE = 1, - METHOD_METR_AVERAGE = 2, - METHOD_METR_WEIGHTED = 3, - METHOD_METR_WARD = 4, - METHOD_METR_WARD_D = METHOD_METR_WARD, - METHOD_METR_CENTROID = 5, - METHOD_METR_MEDIAN = 6, - METHOD_METR_WARD_D2 = 7, - - MIN_METHOD_CODE = 0, - MAX_METHOD_CODE = 7 -}; - -enum method_codes_vector { - // Euclidean methods - METHOD_VECTOR_SINGLE = 0, - METHOD_VECTOR_WARD = 1, - METHOD_VECTOR_CENTROID = 2, - METHOD_VECTOR_MEDIAN = 3, - - MIN_METHOD_VECTOR_CODE = 0, - MAX_METHOD_VECTOR_CODE = 3 -}; - -// self-destructing array pointer -template -class auto_array_ptr{ -private: - type * ptr; - auto_array_ptr(auto_array_ptr const &); // non construction-copyable - auto_array_ptr& operator=(auto_array_ptr const &); // non copyable -public: - auto_array_ptr() - : ptr(NULL) - { } - template - auto_array_ptr(index const size) - : ptr(new type[size]) - { } - template - auto_array_ptr(index const size, value const val) - : ptr(new type[size]) - { - FILL_N(ptr, size, val); - } - ~auto_array_ptr() { - delete [] ptr; } - void free() { - delete [] ptr; - ptr = NULL; - } - template - void init(index const size) { - ptr = new type [size]; - } - template - void init(index const size, value const val) { - init(size); - FILL_N(ptr, size, val); - } - inline operator type *() const { return ptr; } -}; - -struct node { - t_index node1, node2; - t_float dist; -}; - -inline bool operator< (const node a, const node b) { - return (a.dist < b.dist); -} - -class cluster_result { -private: - auto_array_ptr Z; - t_index pos; - -public: - cluster_result(const t_index size) - : Z(size) - , pos(0) - {} - - void append(const t_index node1, const t_index node2, const t_float dist) { - Z[pos].node1 = node1; - Z[pos].node2 = node2; - Z[pos].dist = dist; - ++pos; - } - - node * operator[] (const t_index idx) const { return Z + idx; } - - /* Define several methods to postprocess the distances. All these functions - are monotone, so they do not change the sorted order of distances. */ - - void sqrt() const { - for (node * ZZ=Z; ZZ!=Z+pos; ++ZZ) { - ZZ->dist = std::sqrt(ZZ->dist); - } - } - - void sqrt(const t_float) const { // ignore the argument - sqrt(); - } - - void sqrtdouble(const t_float) const { // ignore the argument - for (node * ZZ=Z; ZZ!=Z+pos; ++ZZ) { - ZZ->dist = std::sqrt(2*ZZ->dist); - } - } - - #ifdef R_pow - #define my_pow R_pow - #else - #define my_pow std::pow - #endif - - void power(const t_float p) const { - t_float const q = 1/p; - for (node * ZZ=Z; ZZ!=Z+pos; ++ZZ) { - ZZ->dist = my_pow(ZZ->dist,q); - } - } - - void plusone(const t_float) const { // ignore the argument - for (node * ZZ=Z; ZZ!=Z+pos; ++ZZ) { - ZZ->dist += 1; - } - } - - void divide(const t_float denom) const { - for (node * ZZ=Z; ZZ!=Z+pos; ++ZZ) { - ZZ->dist /= denom; - } - } -}; - -class doubly_linked_list { - /* - Class for a doubly linked list. Initially, the list is the integer range - [0, size]. We provide a forward iterator and a method to delete an index - from the list. - - Typical use: for (i=L.start; L succ; - -private: - auto_array_ptr pred; - // Not necessarily private, we just do not need it in this instance. - -public: - doubly_linked_list(const t_index size) - // Initialize to the given size. - : start(0) - , succ(size+1) - , pred(size+1) - { - for (t_index i=0; i(2*N-3-(r_))*(r_)>>1)+(c_)-1] ) -// Z is an ((N-1)x4)-array -#define Z_(_r, _c) (Z[(_r)*4 + (_c)]) - -/* - Lookup function for a union-find data structure. - - The function finds the root of idx by going iteratively through all - parent elements until a root is found. An element i is a root if - nodes[i] is zero. To make subsequent searches faster, the entry for - idx and all its parents is updated with the root element. - */ -class union_find { -private: - auto_array_ptr parent; - t_index nextparent; - -public: - union_find(const t_index size) - : parent(size>0 ? 2*size-1 : 0, 0) - , nextparent(size) - { } - - t_index Find (t_index idx) const { - if (parent[idx] != 0 ) { // a → b - t_index p = idx; - idx = parent[idx]; - if (parent[idx] != 0 ) { // a → b → c - do { - idx = parent[idx]; - } while (parent[idx] != 0); - do { - t_index tmp = parent[p]; - parent[p] = idx; - p = tmp; - } while (parent[p] != idx); - } - } - return idx; - } - - void Union (const t_index node1, const t_index node2) { - parent[node1] = parent[node2] = nextparent++; - } -}; - -class nan_error{}; -#ifdef FE_INVALID -class fenv_error{}; -#endif - -static void MST_linkage_core(const t_index N, const t_float * const D, - cluster_result & Z2) { -/* - N: integer, number of data points - D: condensed distance matrix N*(N-1)/2 - Z2: output data structure - - The basis of this algorithm is an algorithm by Rohlf: - - F. James Rohlf, Hierarchical clustering using the minimum spanning tree, - The Computer Journal, vol. 16, 1973, p. 93–95. -*/ - t_index i; - t_index idx2; - doubly_linked_list active_nodes(N); - auto_array_ptr d(N); - - t_index prev_node; - t_float min; - - // first iteration - idx2 = 1; - min = std::numeric_limits::infinity(); - for (i=1; i tmp) - d[i] = tmp; - else if (fc_isnan(tmp)) - assert(false && "fastcluster: nan error"); -#if HAVE_DIAGNOSTIC -#pragma GCC diagnostic pop -#endif - if (d[i] < min) { - min = d[i]; - idx2 = i; - } - } - Z2.append(prev_node, idx2, min); - } -} - -/* Functions for the update of the dissimilarity array */ - -inline static void f_single( t_float * const b, const t_float a ) { - if (*b > a) *b = a; -} -inline static void f_complete( t_float * const b, const t_float a ) { - if (*b < a) *b = a; -} -inline static void f_average( t_float * const b, const t_float a, const t_float s, const t_float t) { - *b = s*a + t*(*b); - #ifndef FE_INVALID -#if HAVE_DIAGNOSTIC -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wfloat-equal" -#endif - if (fc_isnan(*b)) { - assert(false && "fastcluster: nan error"); - } -#if HAVE_DIAGNOSTIC -#pragma GCC diagnostic pop -#endif - #endif -} -inline static void f_weighted( t_float * const b, const t_float a) { - *b = (a+*b)*.5; - #ifndef FE_INVALID -#if HAVE_DIAGNOSTIC -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wfloat-equal" -#endif - if (fc_isnan(*b)) { - assert(false && "fastcluster: nan error"); - } -#if HAVE_DIAGNOSTIC -#pragma GCC diagnostic pop -#endif - #endif -} -inline static void f_ward( t_float * const b, const t_float a, const t_float c, const t_float s, const t_float t, const t_float v) { - *b = ( (v+s)*a - v*c + (v+t)*(*b) ) / (s+t+v); - //*b = a+(*b)-(t*a+s*(*b)+v*c)/(s+t+v); - #ifndef FE_INVALID -#if HAVE_DIAGNOSTIC -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wfloat-equal" -#endif - if (fc_isnan(*b)) { - assert(false && "fastcluster: nan error"); - } -#if HAVE_DIAGNOSTIC -#pragma GCC diagnostic pop -#endif - #endif -} -inline static void f_centroid( t_float * const b, const t_float a, const t_float stc, const t_float s, const t_float t) { - *b = s*a - stc + t*(*b); - #ifndef FE_INVALID - if (fc_isnan(*b)) { - assert(false && "fastcluster: nan error"); - } -#if HAVE_DIAGNOSTIC -#pragma GCC diagnostic pop -#endif - #endif -} -inline static void f_median( t_float * const b, const t_float a, const t_float c_4) { - *b = (a+(*b))*.5 - c_4; - #ifndef FE_INVALID -#if HAVE_DIAGNOSTIC -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wfloat-equal" -#endif - if (fc_isnan(*b)) { - assert(false && "fastcluster: nan error"); - } -#if HAVE_DIAGNOSTIC -#pragma GCC diagnostic pop -#endif - #endif -} - -template -static void NN_chain_core(const t_index N, t_float * const D, t_members * const members, cluster_result & Z2) { -/* - N: integer - D: condensed distance matrix N*(N-1)/2 - Z2: output data structure - - This is the NN-chain algorithm, described on page 86 in the following book: - - Fionn Murtagh, Multidimensional Clustering Algorithms, - Vienna, Würzburg: Physica-Verlag, 1985. -*/ - t_index i; - - auto_array_ptr NN_chain(N); - t_index NN_chain_tip = 0; - - t_index idx1, idx2; - - t_float size1, size2; - doubly_linked_list active_nodes(N); - - t_float min; - - for (t_float const * DD=D; DD!=D+(static_cast(N)*(N-1)>>1); - ++DD) { -#if HAVE_DIAGNOSTIC -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wfloat-equal" -#endif - if (fc_isnan(*DD)) { - assert(false && "fastcluster: nan error"); - } -#if HAVE_DIAGNOSTIC -#pragma GCC diagnostic pop -#endif - } - - #ifdef FE_INVALID - if (feclearexcept(FE_INVALID)) assert(false && "fastcluster: fenv error"); - #endif - - for (t_index j=0; jidx2) { - t_index tmp = idx1; - idx1 = idx2; - idx2 = tmp; - } - - if (method==METHOD_METR_AVERAGE || - method==METHOD_METR_WARD) { - size1 = static_cast(members[idx1]); - size2 = static_cast(members[idx2]); - members[idx2] += members[idx1]; - } - - // Remove the smaller index from the valid indices (active_nodes). - active_nodes.remove(idx1); - - switch (method) { - case METHOD_METR_SINGLE: - /* - Single linkage. - - Characteristic: new distances are never longer than the old distances. - */ - // Update the distance matrix in the range [start, idx1). - for (i=active_nodes.start; i(members[i]); - for (i=active_nodes.start; i(members[i]) ); - // Update the distance matrix in the range (idx1, idx2). - for (; i(members[i]) ); - // Update the distance matrix in the range (idx2, N). - for (i=active_nodes.succ[idx2]; i(members[i]) ); - break; - - default: - assert(false && "fastcluster: invalid method"); - } - } - #ifdef FE_INVALID - if (fetestexcept(FE_INVALID)) assert(false && "fastcluster: fenv error"); - #endif -} - -class binary_min_heap { - /* - Class for a binary min-heap. The data resides in an array A. The elements of - A are not changed but two lists I and R of indices are generated which point - to elements of A and backwards. - - The heap tree structure is - - H[2*i+1] H[2*i+2] - \ / - \ / - ≤ ≤ - \ / - \ / - H[i] - - where the children must be less or equal than their parent. Thus, H[0] - contains the minimum. The lists I and R are made such that H[i] = A[I[i]] - and R[I[i]] = i. - - This implementation is not designed to handle NaN values. - */ -private: - t_float * const A; - t_index size; - auto_array_ptr I; - auto_array_ptr R; - - // no default constructor - binary_min_heap(); - // noncopyable - binary_min_heap(binary_min_heap const &); - binary_min_heap & operator=(binary_min_heap const &); - -public: - binary_min_heap(t_float * const A_, const t_index size_) - : A(A_), size(size_), I(size), R(size) - { // Allocate memory and initialize the lists I and R to the identity. This - // does not make it a heap. Call heapify afterwards! - for (t_index i=0; i>1); idx>0; ) { - --idx; - update_geq_(idx); - } - } - - inline t_index argmin() const { - // Return the minimal element. - return I[0]; - } - - void heap_pop() { - // Remove the minimal element from the heap. - --size; - I[0] = I[size]; - R[I[0]] = 0; - update_geq_(0); - } - - void remove(t_index idx) { - // Remove an element from the heap. - --size; - R[I[size]] = R[idx]; - I[R[idx]] = I[size]; - if ( H(size)<=A[idx] ) { - update_leq_(R[idx]); - } - else { - update_geq_(R[idx]); - } - } - - void replace ( const t_index idxold, const t_index idxnew, - const t_float val) { - R[idxnew] = R[idxold]; - I[R[idxnew]] = idxnew; - if (val<=A[idxold]) - update_leq(idxnew, val); - else - update_geq(idxnew, val); - } - - void update ( const t_index idx, const t_float val ) const { - // Update the element A[i] with val and re-arrange the indices to preserve - // the heap condition. - if (val<=A[idx]) - update_leq(idx, val); - else - update_geq(idx, val); - } - - void update_leq ( const t_index idx, const t_float val ) const { - // Use this when the new value is not more than the old value. - A[idx] = val; - update_leq_(R[idx]); - } - - void update_geq ( const t_index idx, const t_float val ) const { - // Use this when the new value is not less than the old value. - A[idx] = val; - update_geq_(R[idx]); - } - -private: - void update_leq_ (t_index i) const { - t_index j; - for ( ; (i>0) && ( H(i)>1) ); i=j) - heap_swap(i,j); - } - - void update_geq_ (t_index i) const { - t_index j; - for ( ; (j=2*i+1)=H(i) ) { - ++j; - if ( j>=size || H(j)>=H(i) ) break; - } - else if ( j+1 -static void generic_linkage(const t_index N, t_float * const D, t_members * const members, cluster_result & Z2) { - /* - N: integer, number of data points - D: condensed distance matrix N*(N-1)/2 - Z2: output data structure - */ - - const t_index N_1 = N-1; - t_index i, j; // loop variables - t_index idx1, idx2; // row and column indices - - auto_array_ptr n_nghbr(N_1); // array of nearest neighbors - auto_array_ptr mindist(N_1); // distances to the nearest neighbors - auto_array_ptr row_repr(N); // row_repr[i]: node number that the - // i-th row represents - doubly_linked_list active_nodes(N); - binary_min_heap nn_distances(&*mindist, N_1); // minimum heap structure for - // the distance to the nearest neighbor of each point - t_index node1, node2; // node numbers in the output - t_float size1, size2; // and their cardinalities - - t_float min; // minimum and row index for nearest-neighbor search - t_index idx; - - for (i=0; ii} D(i,j) for i in range(N-1) - t_float const * DD = D; - for (i=0; i::infinity(); - for (idx=j=i+1; ji} D(i,j) - - Normally, we have equality. However, this minimum may become invalid due - to the updates in the distance matrix. The rules are: - - 1) If mindist[i] is equal to D(i, n_nghbr[i]), this is the correct - minimum and n_nghbr[i] is a nearest neighbor. - - 2) If mindist[i] is smaller than D(i, n_nghbr[i]), this might not be the - correct minimum. The minimum needs to be recomputed. - - 3) mindist[i] is never bigger than the true minimum. Hence, we never - miss the true minimum if we take the smallest mindist entry, - re-compute the value if necessary (thus maybe increasing it) and - looking for the now smallest mindist entry until a valid minimal - entry is found. This step is done in the lines below. - - The update process for D below takes care that these rules are - fulfilled. This makes sure that the minima in the rows D(i,i+1:)of D are - re-calculated when necessary but re-calculation is avoided whenever - possible. - - The re-calculation of the minima makes the worst-case runtime of this - algorithm cubic in N. We avoid this whenever possible, and in most cases - the runtime appears to be quadratic. - */ - idx1 = nn_distances.argmin(); - if (method != METHOD_METR_SINGLE) { - while ( mindist[idx1] < D_(idx1, n_nghbr[idx1]) ) { - // Recompute the minimum mindist[idx1] and n_nghbr[idx1]. - n_nghbr[idx1] = j = active_nodes.succ[idx1]; // exists, maximally N-1 - min = D_(idx1,j); - for (j=active_nodes.succ[j]; j(members[idx1]); - size2 = static_cast(members[idx2]); - members[idx2] += members[idx1]; - } - Z2.append(node1, node2, mindist[idx1]); - - // Remove idx1 from the list of active indices (active_nodes). - active_nodes.remove(idx1); - // Index idx2 now represents the new (merged) node with label N+i. - row_repr[idx2] = N+i; - - // Update the distance matrix - switch (method) { - case METHOD_METR_SINGLE: - /* - Single linkage. - - Characteristic: new distances are never longer than the old distances. - */ - // Update the distance matrix in the range [start, idx1). - for (j=active_nodes.start; j(members[j]) ); - if (n_nghbr[j] == idx1) - n_nghbr[j] = idx2; - } - // Update the distance matrix in the range (idx1, idx2). - for (; j(members[j]) ); - if (D_(j, idx2) < mindist[j]) { - nn_distances.update_leq(j, D_(j, idx2)); - n_nghbr[j] = idx2; - } - } - // Update the distance matrix in the range (idx2, N). - if (idx2(members[j]) ); - min = D_(idx2,j); - for (j=active_nodes.succ[j]; j(members[j]) ); - if (D_(idx2,j) < min) { - min = D_(idx2,j); - n_nghbr[idx2] = j; - } - } - nn_distances.update(idx2, min); - } - break; - - case METHOD_METR_CENTROID: { - /* - Centroid linkage. - - Shorter and longer distances can occur, not bigger than max(d1,d2) - but maybe smaller than min(d1,d2). - */ - // Update the distance matrix in the range [start, idx1). - t_float s = size1/(size1+size2); - t_float t = size2/(size1+size2); - t_float stc = s*t*mindist[idx1]; - for (j=active_nodes.start; j -static void MST_linkage_core_vector(const t_index N, - t_dissimilarity & dist, - cluster_result & Z2) { -/* - N: integer, number of data points - dist: function pointer to the metric - Z2: output data structure - - The basis of this algorithm is an algorithm by Rohlf: - - F. James Rohlf, Hierarchical clustering using the minimum spanning tree, - The Computer Journal, vol. 16, 1973, p. 93–95. -*/ - t_index i; - t_index idx2; - doubly_linked_list active_nodes(N); - auto_array_ptr d(N); - - t_index prev_node; - t_float min; - - // first iteration - idx2 = 1; - min = std::numeric_limits::infinity(); - for (i=1; i tmp) - d[i] = tmp; - else if (fc_isnan(tmp)) - assert(false && "fastcluster: nan error"); -#if HAVE_DIAGNOSTIC -#pragma GCC diagnostic pop -#endif - if (d[i] < min) { - min = d[i]; - idx2 = i; - } - } - Z2.append(prev_node, idx2, min); - } -} - -template -static void generic_linkage_vector(const t_index N, - t_dissimilarity & dist, - cluster_result & Z2) { - /* - N: integer, number of data points - dist: function pointer to the metric - Z2: output data structure - - This algorithm is valid for the distance update methods - "Ward", "centroid" and "median" only! - */ - const t_index N_1 = N-1; - t_index i, j; // loop variables - t_index idx1, idx2; // row and column indices - - auto_array_ptr n_nghbr(N_1); // array of nearest neighbors - auto_array_ptr mindist(N_1); // distances to the nearest neighbors - auto_array_ptr row_repr(N); // row_repr[i]: node number that the - // i-th row represents - doubly_linked_list active_nodes(N); - binary_min_heap nn_distances(&*mindist, N_1); // minimum heap structure for - // the distance to the nearest neighbor of each point - t_index node1, node2; // node numbers in the output - t_float min; // minimum and row index for nearest-neighbor search - - for (i=0; ii} D(i,j) for i in range(N-1) - for (i=0; i::infinity(); - t_index idx; - for (idx=j=i+1; j(i,j); - } - if (tmp(idx1,j); - for (j=active_nodes.succ[j]; j(idx1,j); - if (tmp(j, idx2); - if (tmp < mindist[j]) { - nn_distances.update_leq(j, tmp); - n_nghbr[j] = idx2; - } - else if (n_nghbr[j] == idx2) - n_nghbr[j] = idx1; // invalidate - } - // Find the nearest neighbor for idx2. - if (idx2(idx2,j); - for (j=active_nodes.succ[j]; j(idx2, j); - if (tmp < min) { - min = tmp; - n_nghbr[idx2] = j; - } - } - nn_distances.update(idx2, min); - } - } - } -} - -template -static void generic_linkage_vector_alternative(const t_index N, - t_dissimilarity & dist, - cluster_result & Z2) { - /* - N: integer, number of data points - dist: function pointer to the metric - Z2: output data structure - - This algorithm is valid for the distance update methods - "Ward", "centroid" and "median" only! - */ - const t_index N_1 = N-1; - t_index i, j=0; // loop variables - t_index idx1, idx2; // row and column indices - - auto_array_ptr n_nghbr(2*N-2); // array of nearest neighbors - auto_array_ptr mindist(2*N-2); // distances to the nearest neighbors - - doubly_linked_list active_nodes(N+N_1); - binary_min_heap nn_distances(&*mindist, N_1, 2*N-2, 1); // minimum heap - // structure for the distance to the nearest neighbor of each point - - t_float min; // minimum for nearest-neighbor searches - - // Initialize the minimal distances: - // Find the nearest neighbor of each point. - // n_nghbr[i] = argmin_{j>i} D(i,j) for i in range(N-1) - for (i=1; i::infinity(); - t_index idx; - for (idx=j=0; j(i,j); - } - if (tmp -// - -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU Affero General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Affero General Public License for more details. - -// You should have received a copy of the GNU Affero General Public License -// along with this program. If not, see . -// -//===----------------------------------------------------------------------===// - -/* - * CDG.cpp - * - * Created on: Sep 27, 2023 - * Author: Xiao Cheng - */ -#include "Graphs/CDG.h" - -using namespace SVF; - -CDG *CDG::controlDg = nullptr; - -void CDG::addCDGEdgeFromSrcDst(const ICFGNode *src, const ICFGNode *dst, const SVFValue *pNode, s32_t branchID) -{ - if (!hasCDGNode(src->getId())) - { - addGNode(src->getId(), new CDGNode(src)); - } - if (!hasCDGNode(dst->getId())) - { - addGNode(dst->getId(), new CDGNode(dst)); - } - if (!hasCDGEdge(getCDGNode(src->getId()), getCDGNode(dst->getId()))) - { - CDGEdge *pEdge = new CDGEdge(getCDGNode(src->getId()), - getCDGNode(dst->getId())); - pEdge->insertBranchCondition(pNode, branchID); - addCDGEdge(pEdge); - incEdgeNum(); - } - else - { - CDGEdge *pEdge = getCDGEdge(getCDGNode(src->getId()), - getCDGNode(dst->getId())); - pEdge->insertBranchCondition(pNode, branchID); - } -} \ No newline at end of file diff --git a/svf/lib/Graphs/CFLGraph.cpp b/svf/lib/Graphs/CFLGraph.cpp deleted file mode 100644 index 9ae84f58c..000000000 --- a/svf/lib/Graphs/CFLGraph.cpp +++ /dev/null @@ -1,171 +0,0 @@ -//===----- CFLGraph.cpp -- Graph for context-free language reachability analysis --// -// -// SVF: Static Value-Flow Analysis -// -// Copyright (C) <2013-> -// - -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU Affero General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Affero General Public License for more details. - -// You should have received a copy of the GNU Affero General Public License -// along with this program. If not, see . -// -//===----------------------------------------------------------------------===// - - -/* - * CFLGraph.cpp - * - * Created on: March 5, 2022 - * Author: Yulei Sui - */ - -#include "Util/Options.h" -#include "Graphs/CFLGraph.h" -#include "Util/SVFUtil.h" - -using namespace SVF; - -CFLGraph::Kind CFLGraph::getStartKind() const -{ - return this->startKind; -} - -void CFLGraph::addCFLNode(NodeID id, CFLNode* node) -{ - addGNode(id, node); -} - -const CFLEdge* CFLGraph::addCFLEdge(CFLNode* src, CFLNode* dst, CFLEdge::GEdgeFlag label) -{ - CFLEdge* edge = new CFLEdge(src,dst,label); - if(cflEdgeSet.insert(edge).second) - { - src->addOutgoingEdge(edge); - dst->addIngoingEdge(edge); - return edge; - } - else - { - delete edge; - return nullptr; - } -} - -const CFLEdge* CFLGraph::hasEdge(CFLNode* src, CFLNode* dst, CFLEdge::GEdgeFlag label) -{ - CFLEdge edge(src,dst,label); - auto it = cflEdgeSet.find(&edge); - if(it !=cflEdgeSet.end()) - return *it; - else - return nullptr; -} - -void CFLGraph::dump(const std::string& filename) -{ - GraphPrinter::WriteGraphToFile(SVFUtil::outs(), filename, this); -} - -void CFLGraph::view() -{ - SVF::ViewGraph(this, "CFL Graph"); -} - -namespace SVF -{ -/*! - * Write CFL graph into dot file for debugging - */ -template<> -struct DOTGraphTraits : public DefaultDOTGraphTraits -{ - - typedef CFLNode NodeType; - - DOTGraphTraits(bool isSimple = false) : DefaultDOTGraphTraits(isSimple) - { - } - - /// Return name of the graph - static std::string getGraphName(CFLGraph*) - { - return "CFL Reachability Graph"; - } - /// Return function name; - static std::string getNodeLabel(CFLNode *node, CFLGraph*) - { - std::string str; - std::stringstream rawstr(str); - rawstr << "Node ID: " << node->getId() << " "; - return rawstr.str(); - } - - static std::string getNodeAttributes(CFLNode *node, CFLGraph*) - { - return "shape=box"; - } - - template - static std::string getEdgeAttributes(CFLNode*, EdgeIter EI, CFLGraph* graph) - { - CFLEdge* edge = *(EI.getCurrent()); - assert(edge && "No edge found!!"); - std::string str; - std::stringstream rawstr(str); - if (edge->getEdgeKind() == ConstraintEdge::Addr) - { - rawstr << "color=green"; - } - else if (edge->getEdgeKind() == ConstraintEdge::Copy) - { - rawstr << "color=black"; - } - else if (edge->getEdgeKindWithMask() == ConstraintEdge::NormalGep) - { - rawstr << "color=purple,label=" << '"' << "Gep_" << edge->getEdgeAttri() << '"'; - } - else if (edge->getEdgeKindWithMask() == ConstraintEdge::VariantGep) - { - rawstr << "color=purple,label=" << '"' << "VGep" << '"'; - } - else if (edge->getEdgeKind() == ConstraintEdge::Store) - { - rawstr << "color=blue"; - } - else if (edge->getEdgeKind() == ConstraintEdge::Load) - { - rawstr << "color=red"; - } - else if (edge->getEdgeKind() == graph->getStartKind()) - { - rawstr << "color=Turquoise"; - } - else - { - rawstr << "style=invis"; - } - return rawstr.str(); - } - - template - static std::string getEdgeSourceLabel(NodeType*, EdgeIter EI) - { - CFLEdge* edge = *(EI.getCurrent()); - assert(edge && "No edge found!!"); - std::string str; - std::stringstream rawstr(str); - rawstr << "Edge label: " << edge->getEdgeKind() << " "; - return rawstr.str(); - } -}; - -} \ No newline at end of file diff --git a/svf/lib/Graphs/CHG.cpp b/svf/lib/Graphs/CHG.cpp deleted file mode 100644 index 44c119478..000000000 --- a/svf/lib/Graphs/CHG.cpp +++ /dev/null @@ -1,282 +0,0 @@ -//===----- CHG.cpp Base class of pointer analyses ---------------------------// -// -// SVF: Static Value-Flow Analysis -// -// Copyright (C) <2013-2017> -// - -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU Affero General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Affero General Public License for more details. - -// You should have received a copy of the GNU Affero General Public License -// along with this program. If not, see . -// -//===----------------------------------------------------------------------===// - -/* - * CHG.cpp (previously CHA.cpp) - * - * Created on: Apr 13, 2016 - * Author: Xiaokang Fan - */ - -#include "Graphs/CHG.h" -#include "Util/SVFUtil.h" - -using namespace SVF; -using namespace SVFUtil; -using namespace std; - -static bool hasEdge(const CHNode *src, const CHNode *dst, - CHEdge::CHEDGETYPE et) -{ - for (CHEdge::CHEdgeSetTy::const_iterator it = src->getOutEdges().begin(), - eit = src->getOutEdges().end(); it != eit; ++it) - { - CHNode *node = (*it)->getDstNode(); - CHEdge::CHEDGETYPE edgeType = (*it)->getEdgeType(); - if (node == dst && edgeType == et) - return true; - } - return false; -} - -static bool checkArgTypes(CallSite cs, const SVFFunction* fn) -{ - - // here we skip the first argument (i.e., this pointer) - u32_t arg_size = (fn->arg_size() > cs.arg_size()) ? cs.arg_size(): fn->arg_size(); - if(arg_size > 1) - { - for (unsigned i = 1; i < arg_size; i++) - { - auto cs_arg = cs.getArgOperand(i); - auto fn_arg = fn->getArg(i); - if (cs_arg->getType() != fn_arg->getType()) - { - return false; - } - } - } - - return true; -} - -void CHGraph::addEdge(const string className, const string baseClassName, - CHEdge::CHEDGETYPE edgeType) -{ - CHNode *srcNode = getNode(className); - CHNode *dstNode = getNode(baseClassName); - assert(srcNode && dstNode && "node not found?"); - - if (!hasEdge(srcNode, dstNode, edgeType)) - { - CHEdge *edge = new CHEdge(srcNode, dstNode, edgeType); - srcNode->addOutgoingEdge(edge); - dstNode->addIncomingEdge(edge); - } -} - -CHNode *CHGraph::getNode(const string name) const -{ - auto chNode = classNameToNodeMap.find(name); - if (chNode != classNameToNodeMap.end()) return chNode->second; - else return nullptr; -} - - -/* - * Get virtual functions for callsite "cs" based on vtbls (calculated - * based on pointsto set) - */ -void CHGraph::getVFnsFromVtbls(CallSite cs, const VTableSet &vtbls, VFunSet &virtualFunctions) -{ - - /// get target virtual functions - size_t idx = cs.getFunIdxInVtable(); - /// get the function name of the virtual callsite - string funName = cs.getFunNameOfVirtualCall(); - for (const SVFGlobalValue *vt : vtbls) - { - const CHNode *child = getNode(vt->getName()); - if (child == nullptr) - continue; - CHNode::FuncVector vfns; - child->getVirtualFunctions(idx, vfns); - for (CHNode::FuncVector::const_iterator fit = vfns.begin(), - feit = vfns.end(); fit != feit; ++fit) - { - const SVFFunction* callee = *fit; - if (cs.arg_size() == callee->arg_size() || - (cs.isVarArg() && callee->isVarArg())) - { - - // if argument types do not match - // skip this one - if (!checkArgTypes(cs, callee)) - continue; - - string calleeName = callee->getName(); - - /* - * The compiler will add some special suffix (e.g., - * "[abi:cxx11]") to the end of some virtual function: - * In dealII - * function: FE_Q<3>::get_name - * will be mangled as: _ZNK4FE_QILi3EE8get_nameB5cxx11Ev - * after demangling: FE_Q<3>::get_name[abi:cxx11] - * The special suffix ("[abi:cxx11]") needs to be removed - */ - const std::string suffix("[abi:cxx11]"); - size_t suffix_pos = calleeName.rfind(suffix); - if (suffix_pos != string::npos) - calleeName.erase(suffix_pos, suffix.size()); - - /* - * if we can't get the function name of a virtual callsite, all virtual - * functions calculated by idx will be valid - */ - if (funName.size() == 0) - { - virtualFunctions.insert(callee); - } - else if (funName[0] == '~') - { - /* - * if the virtual callsite is calling a destructor, then all - * destructors in the ch will be valid - * class A { virtual ~A(){} }; - * class B: public A { virtual ~B(){} }; - * int main() { - * A *a = new B; - * delete a; /// the function name of this virtual callsite is ~A() - * } - */ - if (calleeName[0] == '~') - { - virtualFunctions.insert(callee); - } - } - else - { - /* - * for other virtual function calls, the function name of the callsite - * and the function name of the target callee should match exactly - */ - if (funName.compare(calleeName) == 0) - { - virtualFunctions.insert(callee); - } - } - } - } - } -} - - -void CHNode::getVirtualFunctions(u32_t idx, FuncVector &virtualFunctions) const -{ - for (vector::const_iterator it = virtualFunctionVectors.begin(), - eit = virtualFunctionVectors.end(); it != eit; ++it) - { - if ((*it).size() > idx) - virtualFunctions.push_back((*it)[idx]); - } -} - -void CHGraph::printCH() -{ - for (CHGraph::const_iterator it = this->begin(), eit = this->end(); - it != eit; ++it) - { - const CHNode *node = it->second; - outs() << "class: " << node->getName() << "\n"; - for (CHEdge::CHEdgeSetTy::const_iterator it = node->OutEdgeBegin(); - it != node->OutEdgeEnd(); ++it) - { - if ((*it)->getEdgeType() == CHEdge::INHERITANCE) - outs() << (*it)->getDstNode()->getName() << " --inheritance--> " - << (*it)->getSrcNode()->getName() << "\n"; - else - outs() << (*it)->getSrcNode()->getName() << " --instance--> " - << (*it)->getDstNode()->getName() << "\n"; - } - } - outs() << '\n'; -} - -/*! - * Dump call graph into dot file - */ -void CHGraph::dump(const std::string& filename) -{ - GraphPrinter::WriteGraphToFile(outs(), filename, this); - printCH(); -} - -void CHGraph::view() -{ - SVF::ViewGraph(this, "Class Hierarchy Graph"); -} - -namespace SVF -{ - -/*! - * Write value flow graph into dot file for debugging - */ -template<> -struct DOTGraphTraits : public DefaultDOTGraphTraits -{ - - typedef CHNode NodeType; - DOTGraphTraits(bool isSimple = false) : - DefaultDOTGraphTraits(isSimple) - { - } - - /// Return name of the graph - static std::string getGraphName(CHGraph*) - { - return "Class Hierarchy Graph"; - } - /// Return function name; - static std::string getNodeLabel(CHNode *node, CHGraph*) - { - return node->getName(); - } - - static std::string getNodeAttributes(CHNode *node, CHGraph*) - { - if (node->isPureAbstract()) - { - return "shape=tab"; - } - else - return "shape=box"; - } - - template - static std::string getEdgeAttributes(CHNode*, EdgeIter EI, CHGraph*) - { - - CHEdge* edge = *(EI.getCurrent()); - assert(edge && "No edge found!!"); - if (edge->getEdgeType() == CHEdge::INHERITANCE) - { - return "style=solid"; - } - else - { - return "style=dashed"; - } - } -}; -} // End namespace llvm diff --git a/svf/lib/Graphs/ConsG.cpp b/svf/lib/Graphs/ConsG.cpp index 27bb70ed5..152846410 100644 --- a/svf/lib/Graphs/ConsG.cpp +++ b/svf/lib/Graphs/ConsG.cpp @@ -6,16 +6,16 @@ // // This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU Affero General Public License as published by +// it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Affero General Public License for more details. +// GNU General Public License for more details. -// You should have received a copy of the GNU Affero General Public License +// You should have received a copy of the GNU General Public License // along with this program. If not, see . // //===----------------------------------------------------------------------===// @@ -34,6 +34,8 @@ using namespace SVF; using namespace SVFUtil; +ConstraintNode::SCCEdgeFlag ConstraintNode::sccEdgeFlag = ConstraintNode::Direct; + /*! * Start building constraint graph */ @@ -41,128 +43,122 @@ void ConstraintGraph::buildCG() { // initialize nodes - for(SVFIR::iterator it = pag->begin(), eit = pag->end(); it!=eit; ++it) + for(PAG::iterator it = pag->begin(), eit = pag->end(); it!=eit; ++it) { - addConstraintNode(new ConstraintNode(it->first), it->first); + addConstraintNode(new ConstraintNode(it->first), it->first); } // initialize edges - SVFStmt::SVFStmtSetTy& addrs = getPAGEdgeSet(SVFStmt::Addr); - for (SVFStmt::SVFStmtSetTy::iterator iter = addrs.begin(), eiter = + PAGEdge::PAGEdgeSetTy& addrs = getPAGEdgeSet(PAGEdge::Addr); + for (PAGEdge::PAGEdgeSetTy::iterator iter = addrs.begin(), eiter = addrs.end(); iter != eiter; ++iter) { - const AddrStmt* edge = SVFUtil::cast(*iter); - addAddrCGEdge(edge->getRHSVarID(),edge->getLHSVarID()); + PAGEdge* edge = *iter; + addAddrCGEdge(edge->getSrcID(),edge->getDstID()); } - SVFStmt::SVFStmtSetTy& copys = getPAGEdgeSet(SVFStmt::Copy); - for (SVFStmt::SVFStmtSetTy::iterator iter = copys.begin(), eiter = + PAGEdge::PAGEdgeSetTy& copys = getPAGEdgeSet(PAGEdge::Copy); + for (PAGEdge::PAGEdgeSetTy::iterator iter = copys.begin(), eiter = copys.end(); iter != eiter; ++iter) { - const CopyStmt* edge = SVFUtil::cast(*iter); - if(edge->isBitCast() || edge->isValueCopy()) - addCopyCGEdge(edge->getRHSVarID(),edge->getLHSVarID()); - } - - SVFStmt::SVFStmtSetTy& phis = getPAGEdgeSet(SVFStmt::Phi); - for (SVFStmt::SVFStmtSetTy::iterator iter = phis.begin(), eiter = - phis.end(); iter != eiter; ++iter) - { - const PhiStmt* edge = SVFUtil::cast(*iter); - for(const auto opVar : edge->getOpndVars()) - addCopyCGEdge(opVar->getId(),edge->getResID()); - } - - SVFStmt::SVFStmtSetTy& selects = getPAGEdgeSet(SVFStmt::Select); - for (SVFStmt::SVFStmtSetTy::iterator iter = selects.begin(), eiter = - selects.end(); iter != eiter; ++iter) - { - const SelectStmt* edge = SVFUtil::cast(*iter); - for(const auto opVar : edge->getOpndVars()) - addCopyCGEdge(opVar->getId(),edge->getResID()); + PAGEdge* edge = *iter; + ConstraintEdge* consEdge = addCopyCGEdge(edge->getSrcID(),edge->getDstID()); + if (consEdge) { + consEdge->setLLVMValue(edge->getValue()); + } } - SVFStmt::SVFStmtSetTy& calls = getPAGEdgeSet(SVFStmt::Call); - for (SVFStmt::SVFStmtSetTy::iterator iter = calls.begin(), eiter = + PAGEdge::PAGEdgeSetTy& calls = getPAGEdgeSet(PAGEdge::Call); + for (PAGEdge::PAGEdgeSetTy::iterator iter = calls.begin(), eiter = calls.end(); iter != eiter; ++iter) { - const CallPE* edge = SVFUtil::cast(*iter); - addCopyCGEdge(edge->getRHSVarID(),edge->getLHSVarID()); + PAGEdge* edge = *iter; + ConstraintEdge* consEdge = addCopyCGEdge(edge->getSrcID(),edge->getDstID()); + if (consEdge) { + consEdge->setLLVMValue(edge->getValue()); + } } - SVFStmt::SVFStmtSetTy& rets = getPAGEdgeSet(SVFStmt::Ret); - for (SVFStmt::SVFStmtSetTy::iterator iter = rets.begin(), eiter = + PAGEdge::PAGEdgeSetTy& rets = getPAGEdgeSet(PAGEdge::Ret); + for (PAGEdge::PAGEdgeSetTy::iterator iter = rets.begin(), eiter = rets.end(); iter != eiter; ++iter) { - const RetPE* edge = SVFUtil::cast(*iter); - addCopyCGEdge(edge->getRHSVarID(),edge->getLHSVarID()); + PAGEdge* edge = *iter; + ConstraintEdge* consEdge = addCopyCGEdge(edge->getSrcID(),edge->getDstID()); + if (consEdge) { + consEdge->setLLVMValue(edge->getValue()); + } } - SVFStmt::SVFStmtSetTy& tdfks = getPAGEdgeSet(SVFStmt::ThreadFork); - for (SVFStmt::SVFStmtSetTy::iterator iter = tdfks.begin(), eiter = + PAGEdge::PAGEdgeSetTy& tdfks = getPAGEdgeSet(PAGEdge::ThreadFork); + for (PAGEdge::PAGEdgeSetTy::iterator iter = tdfks.begin(), eiter = tdfks.end(); iter != eiter; ++iter) { - const TDForkPE* edge = SVFUtil::cast(*iter); - addCopyCGEdge(edge->getRHSVarID(),edge->getLHSVarID()); + PAGEdge* edge = *iter; + ConstraintEdge* consEdge = addCopyCGEdge(edge->getSrcID(),edge->getDstID()); + if (consEdge) { + consEdge->setLLVMValue(edge->getValue()); + } } - SVFStmt::SVFStmtSetTy& tdjns = getPAGEdgeSet(SVFStmt::ThreadJoin); - for (SVFStmt::SVFStmtSetTy::iterator iter = tdjns.begin(), eiter = + PAGEdge::PAGEdgeSetTy& tdjns = getPAGEdgeSet(PAGEdge::ThreadJoin); + for (PAGEdge::PAGEdgeSetTy::iterator iter = tdjns.begin(), eiter = tdjns.end(); iter != eiter; ++iter) { - const TDJoinPE* edge = SVFUtil::cast(*iter); - addCopyCGEdge(edge->getRHSVarID(),edge->getLHSVarID()); + PAGEdge* edge = *iter; + ConstraintEdge* consEdge = addCopyCGEdge(edge->getSrcID(),edge->getDstID()); + if (consEdge) { + consEdge->setLLVMValue(edge->getValue()); + } } - SVFStmt::SVFStmtSetTy& ngeps = getPAGEdgeSet(SVFStmt::Gep); - for (SVFStmt::SVFStmtSetTy::iterator iter = ngeps.begin(), eiter = + PAGEdge::PAGEdgeSetTy& ngeps = getPAGEdgeSet(PAGEdge::NormalGep); + for (PAGEdge::PAGEdgeSetTy::iterator iter = ngeps.begin(), eiter = ngeps.end(); iter != eiter; ++iter) { - GepStmt* edge = SVFUtil::cast(*iter); - if(edge->isVariantFieldGep()) - addVariantGepCGEdge(edge->getRHSVarID(),edge->getLHSVarID()); - else - addNormalGepCGEdge(edge->getRHSVarID(),edge->getLHSVarID(),edge->getAccessPath()); + NormalGepPE* edge = SVFUtil::cast(*iter); + ConstraintEdge* consEdge = addNormalGepCGEdge(edge->getSrcID(),edge->getDstID(),edge->getLocationSet()); + if (consEdge) { + consEdge->setLLVMValue(edge->getValue()); + + } } - SVFStmt::SVFStmtSetTy& loads = getPAGEdgeSet(SVFStmt::Load); - for (SVFStmt::SVFStmtSetTy::iterator iter = loads.begin(), eiter = - loads.end(); iter != eiter; ++iter) + PAGEdge::PAGEdgeSetTy& vgeps = getPAGEdgeSet(PAGEdge::VariantGep); + for (PAGEdge::PAGEdgeSetTy::iterator iter = vgeps.begin(), eiter = + vgeps.end(); iter != eiter; ++iter) { - LoadStmt* edge = SVFUtil::cast(*iter); - addLoadCGEdge(edge->getRHSVarID(),edge->getLHSVarID()); + VariantGepPE* edge = SVFUtil::cast(*iter); + ConstraintEdge* consEdge = addVariantGepCGEdge(edge->getSrcID(),edge->getDstID(), edge->isStructTy()); + if (consEdge) { + consEdge->setLLVMValue(edge->getValue()); + } } - SVFStmt::SVFStmtSetTy& stores = getPAGEdgeSet(SVFStmt::Store); - for (SVFStmt::SVFStmtSetTy::iterator iter = stores.begin(), eiter = + PAGEdge::PAGEdgeSetTy& stores = getPAGEdgeSet(PAGEdge::Load); + for (PAGEdge::PAGEdgeSetTy::iterator iter = stores.begin(), eiter = stores.end(); iter != eiter; ++iter) { - StoreStmt* edge = SVFUtil::cast(*iter); - addStoreCGEdge(edge->getRHSVarID(),edge->getLHSVarID()); + PAGEdge* edge = *iter; + ConstraintEdge* consEdge = addLoadCGEdge(edge->getSrcID(),edge->getDstID()); + if (consEdge) { + consEdge->setLLVMValue(edge->getValue()); + } } - clearSolitaries(); -} - -/*! - * Remove nodes that are neither pointers nor connected with any edge - */ -void ConstraintGraph::clearSolitaries() -{ - Set nodesToRemove; - for (auto it = this->begin(); it != this->end(); ++it) + PAGEdge::PAGEdgeSetTy& loads = getPAGEdgeSet(PAGEdge::Store); + for (PAGEdge::PAGEdgeSetTy::iterator iter = loads.begin(), eiter = + loads.end(); iter != eiter; ++iter) { - if (it->second->hasIncomingEdge() || it->second->hasOutgoingEdge()) - continue; - if (pag->getGNode(it->first)->isPointer()) - continue; - nodesToRemove.insert(it->second); + PAGEdge* edge = *iter; + ConstraintEdge* consEdge = addStoreCGEdge(edge->getSrcID(),edge->getDstID()); + if (consEdge) { + consEdge->setLLVMValue(edge->getValue()); + } } - - for (auto node : nodesToRemove) - removeConstraintNode(node); } + /*! * Memory has been cleaned up at GenericGraph */ @@ -177,12 +173,9 @@ AddrCGEdge::AddrCGEdge(ConstraintNode* s, ConstraintNode* d, EdgeID id) : ConstraintEdge(s,d,Addr,id) { // Retarget addr edges may lead s to be a dummy node - PAGNode* node = SVFIR::getPAG()->getGNode(s->getId()); - (void)node; // Suppress warning of unused variable under release build + PAGNode* node = PAG::getPAG()->getPAGNode(s->getId()); if (!SVFModule::pagReadFromTXT()) - { - assert(!SVFUtil::isa(node) && "a dummy node??"); - } + assert(!SVFUtil::isa(node) && "a dummy node??"); } /*! @@ -192,14 +185,11 @@ AddrCGEdge* ConstraintGraph::addAddrCGEdge(NodeID src, NodeID dst) { ConstraintNode* srcNode = getConstraintNode(src); ConstraintNode* dstNode = getConstraintNode(dst); - if (hasEdge(srcNode, dstNode, ConstraintEdge::Addr)) + if(hasEdge(srcNode,dstNode,ConstraintEdge::Addr)) return nullptr; AddrCGEdge* edge = new AddrCGEdge(srcNode, dstNode, edgeIndex++); - - bool inserted = AddrCGEdgeSet.insert(edge).second; - (void)inserted; // Suppress warning of unused variable under release build - assert(inserted && "new AddrCGEdge not added??"); - + bool added = AddrCGEdgeSet.insert(edge).second; + assert(added && "not added??"); srcNode->addOutgoingAddrEdge(edge); dstNode->addIncomingAddrEdge(edge); return edge; @@ -210,18 +200,24 @@ AddrCGEdge* ConstraintGraph::addAddrCGEdge(NodeID src, NodeID dst) */ CopyCGEdge* ConstraintGraph::addCopyCGEdge(NodeID src, NodeID dst) { +// llvm::errs() << "src = " << src << " dst = " << dst << "\n"; + // Did we blacklist this edge? + if (blackListEdges.find(std::make_tuple(src, dst)) != + blackListEdges.end()) { + return nullptr; + } + + //llvm::errs() << "Adding copy edge: " << src << " --> " << dst << "\n"; ConstraintNode* srcNode = getConstraintNode(src); ConstraintNode* dstNode = getConstraintNode(dst); - if (hasEdge(srcNode, dstNode, ConstraintEdge::Copy) || srcNode == dstNode) + if(hasEdge(srcNode,dstNode,ConstraintEdge::Copy) + || srcNode == dstNode) return nullptr; CopyCGEdge* edge = new CopyCGEdge(srcNode, dstNode, edgeIndex++); - - bool inserted = directEdgeSet.insert(edge).second; - (void)inserted; // Suppress warning of unused variable under release build - assert(inserted && "new CopyCGEdge not added??"); - + bool added = directEdgeSet.insert(edge).second; + assert(added && "not added??"); srcNode->addOutgoingCopyEdge(edge); dstNode->addIncomingCopyEdge(edge); return edge; @@ -231,20 +227,16 @@ CopyCGEdge* ConstraintGraph::addCopyCGEdge(NodeID src, NodeID dst) /*! * Add Gep edge */ -NormalGepCGEdge* ConstraintGraph::addNormalGepCGEdge(NodeID src, NodeID dst, const AccessPath& ap) +NormalGepCGEdge* ConstraintGraph::addNormalGepCGEdge(NodeID src, NodeID dst, const LocationSet& ls) { ConstraintNode* srcNode = getConstraintNode(src); ConstraintNode* dstNode = getConstraintNode(dst); - if (hasEdge(srcNode, dstNode, ConstraintEdge::NormalGep)) + if(hasEdge(srcNode,dstNode,ConstraintEdge::NormalGep)) return nullptr; - NormalGepCGEdge* edge = - new NormalGepCGEdge(srcNode, dstNode, ap, edgeIndex++); - - bool inserted = directEdgeSet.insert(edge).second; - (void)inserted; // Suppress warning of unused variable under release build - assert(inserted && "new NormalGepCGEdge not added??"); - + NormalGepCGEdge* edge = new NormalGepCGEdge(srcNode, dstNode,ls, edgeIndex++); + bool added = directEdgeSet.insert(edge).second; + assert(added && "not added??"); srcNode->addOutgoingGepEdge(edge); dstNode->addIncomingGepEdge(edge); return edge; @@ -253,19 +245,16 @@ NormalGepCGEdge* ConstraintGraph::addNormalGepCGEdge(NodeID src, NodeID dst, co /*! * Add variant gep edge */ -VariantGepCGEdge* ConstraintGraph::addVariantGepCGEdge(NodeID src, NodeID dst) +VariantGepCGEdge* ConstraintGraph::addVariantGepCGEdge(NodeID src, NodeID dst, bool flag) { ConstraintNode* srcNode = getConstraintNode(src); ConstraintNode* dstNode = getConstraintNode(dst); - if (hasEdge(srcNode, dstNode, ConstraintEdge::VariantGep)) + if(hasEdge(srcNode,dstNode,ConstraintEdge::VariantGep)) return nullptr; - VariantGepCGEdge* edge = new VariantGepCGEdge(srcNode, dstNode, edgeIndex++); - - bool inserted = directEdgeSet.insert(edge).second; - (void)inserted; // Suppress warning of unused variable under release build - assert(inserted && "new VariantGepCGEdge not added??"); - + VariantGepCGEdge* edge = new VariantGepCGEdge(srcNode, dstNode, edgeIndex++, flag); + bool added = directEdgeSet.insert(edge).second; + assert(added && "not added??"); srcNode->addOutgoingGepEdge(edge); dstNode->addIncomingGepEdge(edge); return edge; @@ -276,17 +265,17 @@ VariantGepCGEdge* ConstraintGraph::addVariantGepCGEdge(NodeID src, NodeID dst) */ LoadCGEdge* ConstraintGraph::addLoadCGEdge(NodeID src, NodeID dst) { + if (src == 224119 && dst == 74467) { + llvm::errs() << "break!\n"; + } ConstraintNode* srcNode = getConstraintNode(src); ConstraintNode* dstNode = getConstraintNode(dst); - if (hasEdge(srcNode, dstNode, ConstraintEdge::Load)) + if(hasEdge(srcNode,dstNode,ConstraintEdge::Load)) return nullptr; LoadCGEdge* edge = new LoadCGEdge(srcNode, dstNode, edgeIndex++); - - bool inserted = LoadCGEdgeSet.insert(edge).second; - (void)inserted; // Suppress warning of unused variable under release build - assert(inserted && "new LoadCGEdge not added??"); - + bool added = LoadCGEdgeSet.insert(edge).second; + assert(added && "not added??"); srcNode->addOutgoingLoadEdge(edge); dstNode->addIncomingLoadEdge(edge); return edge; @@ -299,15 +288,12 @@ StoreCGEdge* ConstraintGraph::addStoreCGEdge(NodeID src, NodeID dst) { ConstraintNode* srcNode = getConstraintNode(src); ConstraintNode* dstNode = getConstraintNode(dst); - if (hasEdge(srcNode, dstNode, ConstraintEdge::Store)) + if(hasEdge(srcNode,dstNode,ConstraintEdge::Store)) return nullptr; StoreCGEdge* edge = new StoreCGEdge(srcNode, dstNode, edgeIndex++); - - bool inserted = StoreCGEdgeSet.insert(edge).second; - (void)inserted; // Suppress warning of unused variable under release build - assert(inserted && "new StoreCGEdge not added??"); - + bool added = StoreCGEdgeSet.insert(edge).second; + assert(added && "not added??"); srcNode->addOutgoingStoreEdge(edge); dstNode->addIncomingStoreEdge(edge); return edge; @@ -319,37 +305,56 @@ StoreCGEdge* ConstraintGraph::addStoreCGEdge(NodeID src, NodeID dst) * * (1) Remove edge from old dst target, * (2) Change edge dst id and - * (3) Add modified edge into new dst + * (3) Add modifed edge into new dst */ void ConstraintGraph::reTargetDstOfEdge(ConstraintEdge* edge, ConstraintNode* newDstNode) { NodeID newDstNodeID = newDstNode->getId(); NodeID srcId = edge->getSrcID(); + + Value* oldValue = edge->getLLVMValue(); if(LoadCGEdge* load = SVFUtil::dyn_cast(edge)) { removeLoadEdge(load); - addLoadCGEdge(srcId,newDstNodeID); + ConstraintEdge* newEdge = addLoadCGEdge(srcId,newDstNodeID); + if (newEdge) { + newEdge->setLLVMValue(oldValue); + } } else if(StoreCGEdge* store = SVFUtil::dyn_cast(edge)) { removeStoreEdge(store); - addStoreCGEdge(srcId,newDstNodeID); + ConstraintEdge* newEdge = addStoreCGEdge(srcId,newDstNodeID); + if (newEdge) { + newEdge->setLLVMValue(oldValue); + } } else if(CopyCGEdge* copy = SVFUtil::dyn_cast(edge)) { removeDirectEdge(copy); - addCopyCGEdge(srcId,newDstNodeID); + ConstraintEdge* newEdge = addCopyCGEdge(srcId,newDstNodeID); + if (newEdge) { + newEdge->setLLVMValue(oldValue); + newEdge->setSourceEdge(edge->getSourceEdge()); + } } else if(NormalGepCGEdge* gep = SVFUtil::dyn_cast(edge)) { - const AccessPath ap = gep->getAccessPath(); + const LocationSet ls = gep->getLocationSet(); removeDirectEdge(gep); - addNormalGepCGEdge(srcId,newDstNodeID, ap); + ConstraintEdge* newEdge = addNormalGepCGEdge(srcId,newDstNodeID,ls); + if (newEdge) { + newEdge->setLLVMValue(oldValue); + } } else if(VariantGepCGEdge* gep = SVFUtil::dyn_cast(edge)) { + removeDirectEdge(gep); - addVariantGepCGEdge(srcId,newDstNodeID); + ConstraintEdge* newEdge = addVariantGepCGEdge(srcId,newDstNodeID, gep->isStructTy()); + if (newEdge) { + newEdge->setLLVMValue(oldValue); + } } else if(AddrCGEdge* addr = SVFUtil::dyn_cast(edge)) { @@ -369,31 +374,48 @@ void ConstraintGraph::reTargetSrcOfEdge(ConstraintEdge* edge, ConstraintNode* ne { NodeID newSrcNodeID = newSrcNode->getId(); NodeID dstId = edge->getDstID(); + Value* oldValue = edge->getLLVMValue(); + if(LoadCGEdge* load = SVFUtil::dyn_cast(edge)) { removeLoadEdge(load); - addLoadCGEdge(newSrcNodeID,dstId); + ConstraintEdge* newEdge = addLoadCGEdge(newSrcNodeID,dstId); + if (newEdge) { + newEdge->setLLVMValue(oldValue); + } } else if(StoreCGEdge* store = SVFUtil::dyn_cast(edge)) { removeStoreEdge(store); - addStoreCGEdge(newSrcNodeID,dstId); + ConstraintEdge* newEdge = addStoreCGEdge(newSrcNodeID,dstId); + if (newEdge) { + newEdge->setLLVMValue(oldValue); + } } else if(CopyCGEdge* copy = SVFUtil::dyn_cast(edge)) { removeDirectEdge(copy); - addCopyCGEdge(newSrcNodeID,dstId); + ConstraintEdge* newEdge = addCopyCGEdge(newSrcNodeID,dstId); + if (newEdge) { + newEdge->setLLVMValue(oldValue); + } } else if(NormalGepCGEdge* gep = SVFUtil::dyn_cast(edge)) { - const AccessPath ap = gep->getAccessPath(); + const LocationSet ls = gep->getLocationSet(); removeDirectEdge(gep); - addNormalGepCGEdge(newSrcNodeID, dstId, ap); + ConstraintEdge* newEdge = addNormalGepCGEdge(newSrcNodeID,dstId,ls); + if (newEdge) { + newEdge->setLLVMValue(oldValue); + } } else if(VariantGepCGEdge* gep = SVFUtil::dyn_cast(edge)) { removeDirectEdge(gep); - addVariantGepCGEdge(newSrcNodeID,dstId); + ConstraintEdge* newEdge = addVariantGepCGEdge(newSrcNodeID,dstId, gep->isStructTy()); + if (newEdge) { + newEdge->setLLVMValue(oldValue); + } } else if(AddrCGEdge* addr = SVFUtil::dyn_cast(edge)) { @@ -410,10 +432,9 @@ void ConstraintGraph::removeAddrEdge(AddrCGEdge* edge) { getConstraintNode(edge->getSrcID())->removeOutgoingAddrEdge(edge); getConstraintNode(edge->getDstID())->removeIncomingAddrEdge(edge); - u32_t num = AddrCGEdgeSet.erase(edge); - (void)num; // Suppress warning of unused variable under release build - assert(num && "edge not in the set, can not remove!!!"); + Size_t num = AddrCGEdgeSet.erase(edge); delete edge; + assert(num && "edge not in the set, can not remove!!!"); } /*! @@ -423,10 +444,9 @@ void ConstraintGraph::removeLoadEdge(LoadCGEdge* edge) { getConstraintNode(edge->getSrcID())->removeOutgoingLoadEdge(edge); getConstraintNode(edge->getDstID())->removeIncomingLoadEdge(edge); - u32_t num = LoadCGEdgeSet.erase(edge); - (void)num; // Suppress warning of unused variable under release build + Size_t num = LoadCGEdgeSet.erase(edge); + // delete edge; assert(num && "edge not in the set, can not remove!!!"); - delete edge; } /*! @@ -436,10 +456,9 @@ void ConstraintGraph::removeStoreEdge(StoreCGEdge* edge) { getConstraintNode(edge->getSrcID())->removeOutgoingStoreEdge(edge); getConstraintNode(edge->getDstID())->removeIncomingStoreEdge(edge); - u32_t num = StoreCGEdgeSet.erase(edge); - (void)num; // Suppress warning of unused variable under release build + Size_t num = StoreCGEdgeSet.erase(edge); + // delete edge; assert(num && "edge not in the set, can not remove!!!"); - delete edge; } /*! @@ -450,8 +469,8 @@ void ConstraintGraph::removeDirectEdge(ConstraintEdge* edge) getConstraintNode(edge->getSrcID())->removeOutgoingDirectEdge(edge); getConstraintNode(edge->getDstID())->removeIncomingDirectEdge(edge); - u32_t num = directEdgeSet.erase(edge); - (void)num; // Suppress warning of unused variable under release build + Size_t num = directEdgeSet.erase(edge); + assert(num && "edge not in the set, can not remove!!!"); delete edge; } @@ -460,7 +479,7 @@ void ConstraintGraph::removeDirectEdge(ConstraintEdge* edge) * Move incoming direct edges of a sub node which is outside SCC to its rep node * Remove incoming direct edges of a sub node which is inside SCC from its rep node */ -bool ConstraintGraph::moveInEdgesToRepNode(ConstraintNode* node, ConstraintNode* rep ) +bool ConstraintGraph::moveInEdgesToRepNode(ConstraintNode* node, ConstraintNode* rep, std::vector& criticalGepEdges ) { std::vector sccEdges; std::vector nonSccEdges; @@ -484,6 +503,7 @@ bool ConstraintGraph::moveInEdgesToRepNode(ConstraintNode* node, ConstraintNode* } bool criticalGepInsideSCC = false; + // if this edge is inside scc, then remove this edge and two end nodes while(!sccEdges.empty()) { @@ -499,10 +519,12 @@ bool ConstraintGraph::moveInEdgesToRepNode(ConstraintNode* node, ConstraintNode* if (!isZeroOffsettedGepCGEdge(edge)) { criticalGepInsideSCC = true; + criticalGepEdges.push_back(edge); + } removeDirectEdge(edge); } - else if(SVFUtil::isa(edge)) + else if(SVFUtil::isa(edge) || SVFUtil::isa(edge)) reTargetDstOfEdge(edge,rep); else if(AddrCGEdge* addr = SVFUtil::dyn_cast(edge)) { @@ -518,7 +540,7 @@ bool ConstraintGraph::moveInEdgesToRepNode(ConstraintNode* node, ConstraintNode* * Move outgoing direct edges of a sub node which is outside SCC to its rep node * Remove outgoing direct edges of a sub node which is inside SCC from its rep node */ -bool ConstraintGraph::moveOutEdgesToRepNode(ConstraintNode*node, ConstraintNode* rep ) +bool ConstraintGraph::moveOutEdgesToRepNode(ConstraintNode*node, ConstraintNode* rep, std::vector& criticalGepEdges) { std::vector sccEdges; @@ -558,10 +580,11 @@ bool ConstraintGraph::moveOutEdgesToRepNode(ConstraintNode*node, ConstraintNode* if (!isZeroOffsettedGepCGEdge(edge)) { criticalGepInsideSCC = true; + criticalGepEdges.push_back(edge); } removeDirectEdge(edge); } - else if(SVFUtil::isa(edge)) + else if(SVFUtil::isa(edge) || SVFUtil::isa(edge)) reTargetSrcOfEdge(edge,rep); else if(AddrCGEdge* addr = SVFUtil::dyn_cast(edge)) { @@ -579,7 +602,7 @@ bool ConstraintGraph::moveOutEdgesToRepNode(ConstraintNode*node, ConstraintNode* */ void ConstraintGraph::dump(std::string name) { - GraphPrinter::WriteGraphToFile(outs(), name, this); + GraphPrinter::WriteGraphToFile(outs(), name, this); } /*! @@ -609,7 +632,7 @@ void ConstraintGraph::print() } else if (NormalGepCGEdge* ngep = SVFUtil::dyn_cast(*iter)) { - outs() << ngep->getSrcID() << " -- NormalGep (" << ngep->getConstantFieldIdx() + outs() << ngep->getSrcID() << " -- NormalGep (" << ngep->getOffset() << ") --> " << ngep->getDstID() << "\n"; } else if (VariantGepCGEdge* vgep = SVFUtil::dyn_cast(*iter)) @@ -645,95 +668,22 @@ void ConstraintGraph::print() /*! * View dot graph of Constraint graph from debugger. */ -void ConstraintGraph::view() -{ - SVF::ViewGraph(this, "Constraint Graph"); -} - -/// Iterators of direct edges for ConsGNode -//@{ -ConstraintNode::iterator ConstraintNode::directOutEdgeBegin() -{ - if (Options::DetectPWC()) - return directOutEdges.begin(); - else - return copyOutEdges.begin(); -} - -ConstraintNode::iterator ConstraintNode::directOutEdgeEnd() -{ - if (Options::DetectPWC()) - return directOutEdges.end(); - else - return copyOutEdges.end(); -} - -ConstraintNode::iterator ConstraintNode::directInEdgeBegin() -{ - if (Options::DetectPWC()) - return directInEdges.begin(); - else - return copyInEdges.begin(); -} - -ConstraintNode::iterator ConstraintNode::directInEdgeEnd() -{ - if (Options::DetectPWC()) - return directInEdges.end(); - else - return copyInEdges.end(); -} - -ConstraintNode::const_iterator ConstraintNode::directOutEdgeBegin() const -{ - if (Options::DetectPWC()) - return directOutEdges.begin(); - else - return copyOutEdges.begin(); -} - -ConstraintNode::const_iterator ConstraintNode::directOutEdgeEnd() const -{ - if (Options::DetectPWC()) - return directOutEdges.end(); - else - return copyOutEdges.end(); -} - -ConstraintNode::const_iterator ConstraintNode::directInEdgeBegin() const -{ - if (Options::DetectPWC()) - return directInEdges.begin(); - else - return copyInEdges.begin(); -} - -ConstraintNode::const_iterator ConstraintNode::directInEdgeEnd() const -{ - if (Options::DetectPWC()) - return directInEdges.end(); - else - return copyInEdges.end(); -} -//@} - -const std::string ConstraintNode::toString() const -{ - return SVFIR::getPAG()->getGNode(getId())->toString(); +void ConstraintGraph::view() { + llvm::ViewGraph(this, "Constraint Graph"); } /*! * GraphTraits specialization for constraint graph */ -namespace SVF +namespace llvm { template<> -struct DOTGraphTraits : public DOTGraphTraits +struct DOTGraphTraits : public DOTGraphTraits { typedef ConstraintNode NodeType; DOTGraphTraits(bool isSimple = false) : - DOTGraphTraits(isSimple) + DOTGraphTraits(isSimple) { } @@ -743,25 +693,28 @@ struct DOTGraphTraits : public DOTGraphTraits return "ConstraintG"; } - static bool isNodeHidden(NodeType *n, ConstraintGraph *) - { - if (Options::ShowHiddenNode()) return false; - else return (n->getInEdges().empty() && n->getOutEdges().empty()); +#if LLVM_VERSION_MAJOR >= 12 + static bool isNodeHidden(NodeType *n, ConstraintGraph*) { +#else + static bool isNodeHidden(NodeType *n) { +#endif + PAGNode* node = PAG::getPAG()->getPAGNode(n->getId()); + return node->isIsolatedNode(); } /// Return label of a VFG node with two display mode /// Either you can choose to display the name of the value or the whole instruction static std::string getNodeLabel(NodeType *n, ConstraintGraph*) { - PAGNode* node = SVFIR::getPAG()->getGNode(n->getId()); - bool briefDisplay = Options::BriefConsCGDotGraph(); + PAGNode* node = PAG::getPAG()->getPAGNode(n->getId()); + bool briefDisplay = Options::BriefConsCGDotGraph; bool nameDisplay = true; std::string str; - std::stringstream rawstr(str); + raw_string_ostream rawstr(str); if (briefDisplay) { - if (SVFUtil::isa(node)) + if (SVFUtil::isa(node)) { if (nameDisplay) rawstr << node->getId() << ":" << node->getValueName(); @@ -774,8 +727,8 @@ struct DOTGraphTraits : public DOTGraphTraits else { // print the whole value - if (!SVFUtil::isa(node) && !SVFUtil::isa(node)) - rawstr << node->getId() << ":" << node->getValue()->toString(); + if (!SVFUtil::isa(node) && !SVFUtil::isa(node)) + rawstr << node->getId() << ":" << value2String(node->getValue()); else rawstr << node->getId() << ":"; @@ -786,40 +739,8 @@ struct DOTGraphTraits : public DOTGraphTraits static std::string getNodeAttributes(NodeType *n, ConstraintGraph*) { - PAGNode* node = SVFIR::getPAG()->getGNode(n->getId()); - if (SVFUtil::isa(node)) - { - if(SVFUtil::isa(node)) - return "shape=hexagon"; - else if (SVFUtil::isa(node)) - return "shape=diamond"; - else - return "shape=box"; - } - else if (SVFUtil::isa(node)) - { - if(SVFUtil::isa(node)) - return "shape=doubleoctagon"; - else if(SVFUtil::isa(node)) - return "shape=box3d"; - else if (SVFUtil::isa(node)) - return "shape=tab"; - else - return "shape=component"; - } - else if (SVFUtil::isa(node)) - { - return "shape=Mrecord"; - } - else if (SVFUtil::isa(node)) - { - return "shape=octagon"; - } - else - { - assert(0 && "no such kind!!"); - } - return ""; + PAGNode* node = PAG::getPAG()->getPAGNode(n->getId()); + return node->getNodeAttrForDotDisplay(); } template @@ -835,10 +756,12 @@ struct DOTGraphTraits : public DOTGraphTraits { return "color=black"; } - else if (edge->getEdgeKind() == ConstraintEdge::NormalGep - || edge->getEdgeKind() == ConstraintEdge::VariantGep) + else if (edge->getEdgeKind() == ConstraintEdge::NormalGep) + { + return "color=purple, headlabel=\"{" + std::to_string(((NormalGepCGEdge*)edge)->getOffset()) + "}\""; + } else if (edge->getEdgeKind() == ConstraintEdge::VariantGep) { - return "color=purple"; + return "color=pink"; } else if (edge->getEdgeKind() == ConstraintEdge::Store) { diff --git a/svf/lib/Graphs/ExternalPAG.cpp b/svf/lib/Graphs/ExternalPAG.cpp new file mode 100644 index 000000000..7fa67f38f --- /dev/null +++ b/svf/lib/Graphs/ExternalPAG.cpp @@ -0,0 +1,623 @@ +//===- ExternalPAG.cpp -- Program assignment graph ---------------------------// + +/* + * ExternalPAG.cpp + * + * Created on: Sep 22, 2018 + * Author: Mohamad Barbar + */ + +#include +#include +#include + +#include "Graphs/PAG.h" +#include "Graphs/ExternalPAG.h" +#include "Util/BasicTypes.h" +#include "Graphs/ICFG.h" + +using namespace SVF; +using namespace SVFUtil; + +llvm::cl::list ExternalPAGArgs("extpags", + llvm::cl::desc("ExternalPAGs to use during PAG construction (format: func1@/path/to/graph,func2@/foo,..."), + llvm::cl::CommaSeparated); + +llvm::cl::list DumpPAGFunctions("dump-function-pags", + llvm::cl::desc("Dump PAG for functions"), + llvm::cl::CommaSeparated); + + +Map> + ExternalPAG::functionToExternalPAGEntries; +Map ExternalPAG::functionToExternalPAGReturns; + +std::vector> + ExternalPAG::parseExternalPAGs(llvm::cl::list &extpagsArgs) +{ + std::vector> parsedExternalPAGs; + for (auto arg = extpagsArgs.begin(); arg != extpagsArgs.end(); + ++arg) + { + std::stringstream ss(*arg); + std::string functionName; + getline(ss, functionName, '@'); + std::string path; + getline(ss, path); + parsedExternalPAGs.push_back( + std::pair(functionName, path)); + } + + return parsedExternalPAGs; +} + +void ExternalPAG::initialise(SVFModule*) +{ + std::vector> parsedExternalPAGs + = ExternalPAG::parseExternalPAGs(ExternalPAGArgs); + + // Build ext PAGs (and add them) first to use them in PAG construction. + for (auto extpagPair= parsedExternalPAGs.begin(); + extpagPair != parsedExternalPAGs.end(); ++extpagPair) + { + std::string fname = extpagPair->first; + std::string path = extpagPair->second; + + ExternalPAG extpag = ExternalPAG(fname); + extpag.readFromFile(path); + extpag.addExternalPAG(getFunction(fname)); + } +} + +bool ExternalPAG::connectCallsiteToExternalPAG(CallSite *cs) +{ + PAG *pag = PAG::getPAG(); + + Function* function = cs->getCalledFunction(); + std::string functionName = function->getName().str(); + + const SVFFunction* svfFun = LLVMModuleSet::getLLVMModuleSet()->getSVFFunction(function); + if (!hasExternalPAG(svfFun)) return false; + + Map argNodes = + functionToExternalPAGEntries[svfFun]; + PAGNode *retNode = functionToExternalPAGReturns[svfFun]; + + // Handle the return. + if (llvm::isa(cs->getType())) + { + NodeID dstrec = pag->getValueNode(cs->getInstruction()); + // Does it actually return a pointer? + if (SVFUtil::isa(function->getReturnType())) + { + if (retNode != nullptr) + { + CallBlockNode* icfgNode = pag->getICFG()->getCallBlockNode(cs->getInstruction()); + pag->addRetPE(retNode->getId(), dstrec, icfgNode); + } + } + else + { + // This is a int2ptr cast during parameter passing + pag->addBlackHoleAddrPE(dstrec); + } + } + + // Handle the arguments; + // Actual arguments. + CallSite::arg_iterator itA = cs->arg_begin(), ieA = cs->arg_end(); + Function::const_arg_iterator itF = function->arg_begin(), + ieF = function->arg_end(); + // Formal arguments. + size_t formalNodeIndex = 0; + + for (; itF != ieF ; ++itA, ++itF, ++formalNodeIndex) + { + if (itA == ieA) + { + // When unneeded args are left empty, e.g. Linux kernel. + break; + } + + // Formal arg node is from the extpag, actual arg node would come from + // the main pag. + PAGNode *formalArgNode = argNodes[formalNodeIndex]; + NodeID actualArgNodeId = pag->getValueNode(*itA); + + const llvm::Value *formalArg = &*itF; + if (!SVFUtil::isa(formalArg->getType())) continue; + + if (SVFUtil::isa((*itA)->getType())) + { + CallBlockNode* icfgNode = pag->getICFG()->getCallBlockNode(cs->getInstruction()); + pag->addCallPE(actualArgNodeId, formalArgNode->getId(), icfgNode); + } + else + { + // This is a int2ptr cast during parameter passing + //addFormalParamBlackHoleAddrEdge(formalArgNode->getId(), &*itF); + assert(false && "you need to set the current location of this PAGEdge"); + pag->addBlackHoleAddrPE(formalArgNode->getId()); + } + // TODO proofread. + } + + return true; +} + +bool ExternalPAG::hasExternalPAG(const SVFFunction* function) +{ + bool ret = functionToExternalPAGEntries.find(function) + != functionToExternalPAGEntries.end(); + return ret; +} + +int getArgNo(const SVFFunction* function, const Value *arg) +{ + int argNo = 0; + for (auto it = function->getLLVMFun()->arg_begin(); it != function->getLLVMFun()->arg_end(); + ++it, ++argNo) + { + if (arg->getName() == it->getName()) return argNo; + } + + return -1; +} + +static void outputPAGNodeNoNewLine(raw_ostream &o, PAGNode *pagNode) +{ + o << pagNode->getId() << " "; + // TODO: is this check enough? + if (!ObjPN::classof(pagNode)) o << "v"; + else o << "o"; +} + +static void outputPAGNode(raw_ostream &o, PAGNode *pagNode) +{ + outputPAGNodeNoNewLine(o, pagNode); + o << "\n"; +} + +static void outputPAGNode(raw_ostream &o, PAGNode *pagNode, int argno) +{ + outputPAGNodeNoNewLine(o, pagNode); + o << " " << argno; + o << "\n"; +} + +static void outputPAGNode(raw_ostream &o, PAGNode *pagNode, + std::string trail) +{ + outputPAGNodeNoNewLine(o, pagNode); + o << " " << trail; + o << "\n"; +} + +static void outputPAGEdge(raw_ostream &o, PAGEdge *pagEdge) +{ + NodeID srcId = pagEdge->getSrcID(); + NodeID dstId = pagEdge->getDstID(); + u32_t offset = 0; + std::string edgeKind = "-"; + + switch (pagEdge->getEdgeKind()) + { + case PAGEdge::Addr: + edgeKind = "addr"; + break; + case PAGEdge::Copy: + edgeKind = "copy"; + break; + case PAGEdge::Store: + edgeKind = "store"; + break; + case PAGEdge::Load: + edgeKind = "load"; + break; + case PAGEdge::Call: + edgeKind = "call"; + break; + case PAGEdge::Ret: + edgeKind = "ret"; + break; + case PAGEdge::NormalGep: + edgeKind = "gep"; + break; + case PAGEdge::VariantGep: + edgeKind = "variant-gep"; + break; + case PAGEdge::Cmp: + edgeKind = "cmp"; + break; + case PAGEdge::BinaryOp: + edgeKind = "binary-op"; + break; + case PAGEdge::UnaryOp: + edgeKind = "unary-op"; + break; + case PAGEdge::ThreadFork: + outs() << "dump-function-pags: found ThreadFork edge.\n"; + break; + case PAGEdge::ThreadJoin: + outs() << "dump-function-pags: found ThreadJoin edge.\n"; + break; + } + + if (NormalGepPE::classof(pagEdge)) offset = + static_cast(pagEdge)->getOffset(); + + o << srcId << " " << edgeKind << " " << dstId << " " << offset << "\n"; +} + + +/*! + * Dump PAGs for the functions + */ +void ExternalPAG::dumpFunctions(std::vector functions) +{ + PAG *pag = PAG::getPAG(); + + // Naive: first map functions to entries in PAG, then dump them. + Map> functionToPAGNodes; + + Set callDsts; + for (PAG::iterator it = pag->begin(); it != pag->end(); ++it) + { + PAGNode *currNode = it->second; + if (!currNode->hasOutgoingEdges(PAGEdge::PEDGEK::Call)) continue; + + // Where are these calls going? + for (PAGEdge::PAGEdgeSetTy::iterator it = + currNode->getOutgoingEdgesBegin(PAGEdge::PEDGEK::Call); + it != currNode->getOutgoingEdgesEnd(PAGEdge::PEDGEK::Call); ++it) + { + CallPE *callEdge = static_cast(*it); + const Instruction *inst = callEdge->getCallInst()->getCallSite(); + :: Function* currFunction = + static_cast(inst)->getCalledFunction(); + + if (currFunction != nullptr) + { + // Otherwise, it would be an indirect call which we don't want. + std::string currFunctionName = currFunction->getName().str(); + + if (std::find(functions.begin(), functions.end(), + currFunctionName) != functions.end()) + { + // If the dst has already been added, we'd be duplicating + // due to multiple actual->arg call edges. + if (callDsts.find(callEdge->getDstNode()) == callDsts.end()) + { + callDsts.insert(callEdge->getDstNode()); + const SVFFunction* svfFun = LLVMModuleSet::getLLVMModuleSet()->getSVFFunction(currFunction); + functionToPAGNodes[svfFun].push_back(callEdge->getDstNode()); + } + } + } + } + } + + for (auto it = functionToPAGNodes.begin(); it != functionToPAGNodes.end(); + ++it) + { + const SVFFunction* function = it->first; + std::string functionName = it->first->getName().str(); + + // The final nodes and edges we will print. + Set nodes; + Set edges; + // The search stack. + std::stack todoNodes; + // The arguments to the function. + std::vector argNodes = it->second; + PAGNode *retNode = nullptr; + + + outs() << "PAG for function: " << functionName << "\n"; + for (auto node = argNodes.begin(); node != argNodes.end(); ++node) + { + todoNodes.push(*node); + } + + while (!todoNodes.empty()) + { + PAGNode *currNode = todoNodes.top(); + todoNodes.pop(); + + // If the node has been dealt with, ignore it. + if (nodes.find(currNode) != nodes.end()) continue; + nodes.insert(currNode); + + // Return signifies the end of a path. + if (RetPN::classof(currNode)) + { + retNode = currNode; + continue; + } + + auto outEdges = currNode->getOutEdges(); + for (auto outEdge = outEdges.begin(); outEdge != outEdges.end(); + ++outEdge) + { + edges.insert(*outEdge); + todoNodes.push((*outEdge)->getDstNode()); + } + } + + for (auto node = nodes.begin(); node != nodes.end(); ++node) + { + // TODO: proper file. + // Argument nodes use extra information: it's argument number. + if (std::find(argNodes.begin(), argNodes.end(), *node) + != argNodes.end()) + { + outputPAGNode(outs(), *node, + getArgNo(function, (*node)->getValue())); + } + else if (*node == retNode) + { + outputPAGNode(outs(), *node, "ret"); + } + else + { + outputPAGNode(outs(), *node); + } + } + + for (auto edge = edges.begin(); edge != edges.end(); ++edge) + { + // TODO: proper file. + outputPAGEdge(outs(), *edge); + } + + outs() << "PAG for functionName " << functionName << " done\n"; + } +} + +bool ExternalPAG::addExternalPAG(const SVFFunction* function) +{ + // The function does not exist in the module - bad arg? + // TODO: maybe some warning? + if (function == nullptr) return false; + + PAG *pag = PAG::getPAG(); + if (hasExternalPAG(function)) return false; + + outs() << "Adding extpag " << this->getFunctionName() << "\n"; + // Temporarily trick SVF Module into thinking we are reading from + // file to avoid setting BBs/Values (as these nodes don't have any). + std::string oldSVFModuleFileName = SVFModule::pagFileName(); + SVFModule::setPagFromTXT("tmp"); + + // We need: the new nodes. + // : the new edges. + // : to map function names to the entry nodes. + // : to map function names to the return node. + + // To create the new edges. + Map extToNewNodes; + + // Add the value nodes. + for (auto extNodeIt = this->getValueNodes().begin(); + extNodeIt != this->getValueNodes().end(); ++extNodeIt) + { + NodeID newNodeId = pag->addDummyValNode(); + extToNewNodes[*extNodeIt] = pag->getPAGNode(newNodeId); + } + + // Add the object nodes. + for (auto extNodeIt = this->getObjectNodes().begin(); + extNodeIt != this->getObjectNodes().end(); ++extNodeIt) + { + // TODO: fix obj node - there's more to it? + NodeID newNodeId = pag->addDummyObjNode(); + extToNewNodes[*extNodeIt] = pag->getPAGNode(newNodeId); + } + + // Add the edges. + for (auto extEdgeIt = this->getEdges().begin(); + extEdgeIt != this->getEdges().end(); ++extEdgeIt) + { + NodeID extSrcId = std::get<0>(*extEdgeIt); + NodeID extDstId = std::get<1>(*extEdgeIt); + std::string extEdgeType = std::get<2>(*extEdgeIt); + int extOffsetOrCSId = std::get<3>(*extEdgeIt); + + PAGNode *srcNode = extToNewNodes[extSrcId]; + PAGNode *dstNode = extToNewNodes[extDstId]; + NodeID srcId = srcNode->getId(); + NodeID dstId = dstNode->getId(); + + if (extEdgeType == "addr") + { + pag->addAddrPE(srcId, dstId); + } + else if (extEdgeType == "copy") + { + pag->addCopyPE(srcId, dstId); + } + else if (extEdgeType == "load") + { + pag->addLoadPE(srcId, dstId); + } + else if (extEdgeType == "store") + { + pag->addStorePE(srcId, dstId, nullptr); + } + else if (extEdgeType == "gep") + { + pag->addNormalGepPE(srcId, dstId, LocationSet(extOffsetOrCSId)); + } + else if (extEdgeType == "variant-gep") + { + pag->addVariantGepPE(srcId, dstId); + } + else if (extEdgeType == "call") + { + pag->addEdge(srcNode, dstNode, new CallPE(srcNode, dstNode, nullptr)); + } + else if (extEdgeType == "ret") + { + pag->addEdge(srcNode, dstNode, new RetPE(srcNode, dstNode, nullptr)); + } + else if (extEdgeType == "cmp") + { + pag->addCmpPE(srcId, dstId); + } + else if (extEdgeType == "binary-op") + { + pag->addBinaryOPPE(srcId, dstId); + } + else if (extEdgeType == "unary-op") + { + pag->addUnaryOPPE(srcId, dstId); + } + else + { + outs() << "Bad edge type found during extpag addition\n"; + } + } + + // Record the arg nodes. + Map argNodes; + for (auto argNodeIt = this->getArgNodes().begin(); + argNodeIt != this->getArgNodes().end(); ++argNodeIt) + { + int index = argNodeIt->first; + NodeID extNodeId = argNodeIt->second; + argNodes[index] = extToNewNodes[extNodeId]; + } + + functionToExternalPAGEntries[function] = argNodes; + + // Record the return node. + if (this->hasReturnNode()) + { + functionToExternalPAGReturns[function] = + extToNewNodes[this->getReturnNode()]; + } + + // Put it back as if nothing happened. + SVFModule::setPagFromTXT(oldSVFModuleFileName); + + return true; +} + +// Very similar implementation to the PAGBuilderFromFile. +void ExternalPAG::readFromFile(std::string filename) +{ + std::string line; + std::ifstream pagFile(filename.c_str()); + + if (!pagFile.is_open()) + { + outs() << "ExternalPAG::buildFromFile: could not open " << filename + << "\n"; + return; + } + + while (pagFile.good()) + { + std::getline(pagFile, line); + + Size_t tokenCount = 0; + std::string tmps; + std::istringstream ss(line); + while (ss.good()) + { + ss >> tmps; + tokenCount++; + } + + if (tokenCount == 0) + { + // Empty line. + continue; + } + + if (tokenCount == 2 || tokenCount == 3) + { + // It's a node. + NodeID nodeId; + std::string nodeType; + std::istringstream ss(line); + ss >> nodeId; + ss >> nodeType; + + if (nodeType == "v") + { + valueNodes.insert(nodeId); + } + else if (nodeType == "o") + { + objectNodes.insert(nodeId); + } + else + { + assert(false + && "format not supported, please specify node type"); + } + + + if (tokenCount == 3) + { + // If there are 3 tokens, it should be 0, 1, 2, ... or ret. + std::string argNoOrRet; + ss >> argNoOrRet; + + if (argNoOrRet == "ret") + { + setReturnNode(nodeId); + } + else if (std::all_of(argNoOrRet.begin(), argNoOrRet.end(), + ::isdigit)) + { + int argNo = std::stoi(argNoOrRet); + argNodes[argNo] = nodeId; + } + else + { + assert(false + && "format not supported, not arg number or ret"); + } + + } + } + else if (tokenCount == 4) + { + // It's an edge + NodeID nodeSrc; + NodeID nodeDst; + Size_t offsetOrCSId; + std::string edge; + std::istringstream ss(line); + ss >> nodeSrc; + ss >> edge; + ss >> nodeDst; + ss >> offsetOrCSId; + + edges.insert( + std::tuple(nodeSrc, nodeDst, + edge, + offsetOrCSId)); + } + else + { + if (!line.empty()) + { + outs() << "format not supported, token count = " + << tokenCount << "\n"; + assert(false && "format not supported"); + } + } + } + + /* + TODO: might need to include elsewhere. + /// new gep node's id from lower bound, nodeNum may not reflect the total nodes + u32_t lower_bound = 1000; + for(u32_t i = 0; i < lower_bound; i++) + pag->incNodeNum(); + */ +} + diff --git a/svf/lib/Graphs/GraphWriter.cpp b/svf/lib/Graphs/GraphWriter.cpp deleted file mode 100644 index 47130c94a..000000000 --- a/svf/lib/Graphs/GraphWriter.cpp +++ /dev/null @@ -1,53 +0,0 @@ -//===- GraphWriter.cpp - Implements GraphWriter support routines ----------===// -// -// From the LLVM Project with some modifications, under the Apache License v2.0 -// with LLVM Exceptions. See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// - -#include "Graphs/GraphWriter.h" - -#include - -std::string SVF::DOT::EscapeStr(const std::string &Label) -{ - std::string Str(Label); - for (unsigned i = 0; i != Str.length(); ++i) - switch (Str[i]) - { - case '\n': - Str.insert(Str.begin()+i, '\\'); // Escape character... - ++i; - Str[i] = 'n'; - break; - case '\t': - Str.insert(Str.begin()+i, ' '); // Convert to two spaces - ++i; - Str[i] = ' '; - break; - case '\\': - if (i+1 != Str.length()) - switch (Str[i+1]) - { - case 'l': - continue; // don't disturb \l - case '|': - case '{': - case '}': - Str.erase(Str.begin()+i); - continue; - default: - break; - } - case '{': - case '}': - case '<': - case '>': - case '|': - case '"': - Str.insert(Str.begin()+i, '\\'); // Escape character... - ++i; // don't infinite loop - break; - } - return Str; -} diff --git a/svf/lib/Graphs/ICFG.cpp b/svf/lib/Graphs/ICFG.cpp index 8df418bdc..4ed8eb2ff 100644 --- a/svf/lib/Graphs/ICFG.cpp +++ b/svf/lib/Graphs/ICFG.cpp @@ -6,16 +6,16 @@ // // This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU Affero General Public License as published by +// it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Affero General Public License for more details. +// GNU General Public License for more details. -// You should have received a copy of the GNU Affero General Public License +// You should have received a copy of the GNU General Public License // along with this program. If not, see . // //===----------------------------------------------------------------------===// @@ -28,166 +28,136 @@ */ #include -#include "SVFIR/SVFModule.h" +#include "SVF-FE/LLVMUtil.h" +#include "Util/SVFModule.h" #include "Graphs/ICFG.h" -#include "SVFIR/SVFIR.h" +#include "Graphs/PAG.h" #include "Graphs/PTACallGraph.h" using namespace SVF; using namespace SVFUtil; -FunEntryICFGNode::FunEntryICFGNode(NodeID id, const SVFFunction* f) : InterICFGNode(id, FunEntryBlock) +FunEntryBlockNode::FunEntryBlockNode(NodeID id, const SVFFunction* f) : InterBlockNode(id, FunEntryBlock) { fun = f; // if function is implemented - if (f->begin() != f->end()) - { - bb = f->getEntryBlock(); + if (f->getLLVMFun()->begin() != f->getLLVMFun()->end()) { + bb = &(f->getLLVMFun()->getEntryBlock()); } } -FunExitICFGNode::FunExitICFGNode(NodeID id, const SVFFunction* f) - : InterICFGNode(id, FunExitBlock), formalRet(nullptr) +FunExitBlockNode::FunExitBlockNode(NodeID id, const SVFFunction* f) : InterBlockNode(id, FunExitBlock), fun(f), formalRet(nullptr) { fun = f; // if function is implemented - if (f->begin() != f->end()) - { - bb = f->getExitBB(); + if (f->getLLVMFun()->begin() != f->getLLVMFun()->end()) { + bb = SVFUtil::getFunExitBB(f->getLLVMFun()); } + } -const std::string ICFGNode::toString() const -{ + +const std::string ICFGNode::toString() const { std::string str; - std::stringstream rawstr(str); - rawstr << "ICFGNode" << getId(); + raw_string_ostream rawstr(str); + rawstr << "ICFGNode ID: " << getId(); return rawstr.str(); } -void ICFGNode::dump() const -{ +void ICFGNode::dump() const { outs() << this->toString() << "\n"; } -const std::string GlobalICFGNode::toString() const -{ +const std::string GlobalBlockNode::toString() const { std::string str; - std::stringstream rawstr(str); - rawstr << "GlobalICFGNode" << getId(); - for (const SVFStmt *stmt : getSVFStmts()) - rawstr << "\n" << stmt->toString(); + raw_string_ostream rawstr(str); + rawstr << "GlobalBlockNode ID: " << getId(); return rawstr.str(); } -const std::string IntraICFGNode::toString() const -{ +const std::string IntraBlockNode::toString() const { std::string str; - std::stringstream rawstr(str); - rawstr << "IntraICFGNode" << getId(); - rawstr << " {fun: " << getFun()->getName() << getInst()->getSourceLoc() << "}"; - for (const SVFStmt *stmt : getSVFStmts()) - rawstr << "\n" << stmt->toString(); - if(getSVFStmts().empty()) - rawstr << "\n" << getInst()->toString(); + raw_string_ostream rawstr(str); + rawstr << "IntraBlockNode ID: " << getId(); + rawstr << value2String(getInst()) << " {fun: " << getFun()->getName() << "}"; return rawstr.str(); } -const std::string FunEntryICFGNode::toString() const -{ +const std::string FunEntryBlockNode::toString() const { std::string str; - std::stringstream rawstr(str); - rawstr << "FunEntryICFGNode" << getId(); - rawstr << " {fun: " << getFun()->getName(); - if (isExtCall(getFun())==false) - rawstr << getFun()->getSourceLoc(); - rawstr << "}"; - for (const SVFStmt *stmt : getSVFStmts()) - rawstr << "\n" << stmt->toString(); + raw_string_ostream rawstr(str); + rawstr << "FunEntryBlockNode ID: " << getId(); + if (isExtCall(getFun())) + rawstr << " Entry(" << ")\n"; + else + rawstr << " Entry(" << getSourceLoc(getFun()->getLLVMFun()) << ")\n"; + rawstr << " {fun: " << getFun()->getName() << "}"; return rawstr.str(); } -const std::string FunExitICFGNode::toString() const -{ - const SVFFunction *fun = getFun(); +const std::string FunExitBlockNode::toString() const { std::string str; - std::stringstream rawstr(str); - rawstr << "FunExitICFGNode" << getId(); - rawstr << " {fun: " << fun->getName(); - // ensure the enclosing function has exit basic block - if (!isExtCall(fun) && fun->hasReturn()) - rawstr << fun->getExitBB()->front()->getSourceLoc(); - rawstr << "}"; - for (const SVFStmt *stmt : getSVFStmts()) - rawstr << "\n" << stmt->toString(); + raw_string_ostream rawstr(str); + rawstr << "FunExitBlockNode ID: " << getId(); + if (isExtCall(getFun())) + rawstr << " Exit(" << ")\n"; + else + rawstr << " Exit(" << getSourceLoc(getFunExitBB(getFun()->getLLVMFun())->getFirstNonPHI()) << ")\n"; + rawstr << " {fun: " << getFun()->getName() << "}"; return rawstr.str(); } -const std::string CallICFGNode::toString() const -{ +const std::string CallBlockNode::toString() const { std::string str; - std::stringstream rawstr(str); - rawstr << "CallICFGNode" << getId(); - rawstr << " {fun: " << getFun()->getName() << getCallSite()->getSourceLoc() << "}"; - for (const SVFStmt *stmt : getSVFStmts()) - rawstr << "\n" << stmt->toString(); - if(getSVFStmts().empty() && cs) - rawstr << "\n" << cs->toString(); + raw_string_ostream rawstr(str); + rawstr << "CallBlockNode ID: " << getId(); + rawstr << value2String(getCallSite()) << " {fun: " << getFun()->getName() << "}"; return rawstr.str(); } -const std::string RetICFGNode::toString() const -{ +const std::string RetBlockNode::toString() const { std::string str; - std::stringstream rawstr(str); - rawstr << "RetICFGNode" << getId(); - rawstr << " {fun: " << getFun()->getName() << getCallSite()->getSourceLoc() << "}"; - for (const SVFStmt *stmt : getSVFStmts()) - rawstr << "\n" << stmt->toString(); - if(getSVFStmts().empty() && cs) - rawstr << "\n" << cs->toString(); + raw_string_ostream rawstr(str); + rawstr << "RetBlockNode ID: " << getId(); + rawstr << value2String(getCallSite()) << " {fun: " << getFun()->getName() << "}"; return rawstr.str(); } -const std::string ICFGEdge::toString() const -{ +const std::string ICFGEdge::toString() const { std::string str; - std::stringstream rawstr(str); - rawstr << "ICFGEdge: [ICFGNode" << getDstID() << " <-- ICFGNode" << getSrcID() << "]\t"; + raw_string_ostream rawstr(str); + rawstr << "ICFGEdge: [" << getDstID() << "<--" << getSrcID() << "]\t"; return rawstr.str(); } -const std::string IntraCFGEdge::toString() const -{ +const std::string IntraCFGEdge::toString() const { std::string str; - std::stringstream rawstr(str); - if(getCondition() == nullptr) - rawstr << "IntraCFGEdge: [ICFGNode" << getDstID() << " <-- ICFGNode" << getSrcID() << "]\t"; + raw_string_ostream rawstr(str); + if(brCondition.first == nullptr) + rawstr << "IntraCFGEdge: [" << getDstID() << "<--" << getSrcID() << "]\t"; else - rawstr << "IntraCFGEdge: [ICFGNode" << getDstID() << " <-- ICFGNode" << getSrcID() << "] (branchCondition:" << getCondition()->toString() << ") (succCondValue: " << getSuccessorCondValue() << ") \t"; + rawstr << "IntraCFGEdge: [" << getDstID() << "<--" << getSrcID() << "] with condition (" << *brCondition.first << "==" << brCondition.second << ") \t"; return rawstr.str(); } -const std::string CallCFGEdge::toString() const -{ +const std::string CallCFGEdge::toString() const { std::string str; - std::stringstream rawstr(str); - rawstr << "CallCFGEdge " << " [ICFGNode"; - rawstr << getDstID() << " <-- ICFGNode" << getSrcID() << "]\t CallSite: " << cs->toString() << "\t"; + raw_string_ostream rawstr(str); + rawstr << "CallCFGEdge " << " ["; + rawstr << getDstID() << "<--" << getSrcID() << "]\t CallSite: " << *cs << "\t"; return rawstr.str(); } -const std::string RetCFGEdge::toString() const -{ +const std::string RetCFGEdge::toString() const { std::string str; - std::stringstream rawstr(str); - rawstr << "RetCFGEdge " << " [ICFGNode"; - rawstr << getDstID() << " <-- ICFGNode" << getSrcID() << "]\t CallSite: " << cs->toString() << "\t"; + raw_string_ostream rawstr(str); + rawstr << "RetCFGEdge " << " ["; + rawstr << getDstID() << "<--" << getSrcID() << "]\t CallSite: " << *cs << "\t"; return rawstr.str(); } @@ -199,92 +169,78 @@ const std::string RetCFGEdge::toString() const * 2) connect ICFG edges * between two statements (PAGEdges) */ -ICFG::ICFG(): totalICFGNode(0), globalBlockNode(nullptr) +ICFG::ICFG(): totalICFGNode(0) { + DBOUT(DGENERAL, outs() << pasMsg("\tCreate ICFG ...\n")); + globalBlockNode = new GlobalBlockNode(totalICFGNode++); + addICFGNode(globalBlockNode); } -ICFG::~ICFG() -{ - Set loops; - for (const auto &it: icfgNodeToSVFLoopVec) - { - for (const auto &loop: it.second) - { - loops.insert(loop); - } - } - - for (const auto &it: loops) - { - delete it; - } - icfgNodeToSVFLoopVec.clear(); -} /// Get a basic block ICFGNode -ICFGNode* ICFG::getICFGNode(const SVFInstruction* inst) +ICFGNode* ICFG::getBlockICFGNode(const Instruction* inst) { ICFGNode* node; if(SVFUtil::isNonInstricCallSite(inst)) - node = getCallICFGNode(inst); + node = getCallBlockNode(inst); else if(SVFUtil::isIntrinsicInst(inst)) - node = getIntraICFGNode(inst); + node = getIntraBlockNode(inst); // assert (false && "associating an intrinsic instruction with an ICFGNode!"); else - node = getIntraICFGNode(inst); + node = getIntraBlockNode(inst); assert (node!=nullptr && "no ICFGNode for this instruction?"); return node; } -CallICFGNode* ICFG::getCallICFGNode(const SVFInstruction* inst) +CallBlockNode* ICFG::getCallBlockNode(const Instruction* inst) { - if(SVFUtil::isCallSite(inst) ==false) - outs() << inst->toString() << "\n"; + if(SVFUtil::isCallSite(inst) ==false) + outs() << *inst << "\n"; assert(SVFUtil::isCallSite(inst) && "not a call instruction?"); assert(SVFUtil::isNonInstricCallSite(inst) && "associating an intrinsic debug instruction with an ICFGNode!"); - CallICFGNode* node = getCallBlock(inst); + CallBlockNode* node = getCallICFGNode(inst); if(node==nullptr) - node = addCallBlock(inst); - assert (node!=nullptr && "no CallICFGNode for this instruction?"); + node = addCallICFGNode(inst); + assert (node!=nullptr && "no CallBlockNode for this instruction?"); return node; } -RetICFGNode* ICFG::getRetICFGNode(const SVFInstruction* inst) +RetBlockNode* ICFG::getRetBlockNode(const Instruction* inst) { assert(SVFUtil::isCallSite(inst) && "not a call instruction?"); assert(SVFUtil::isNonInstricCallSite(inst) && "associating an intrinsic debug instruction with an ICFGNode!"); - RetICFGNode* node = getRetBlock(inst); + RetBlockNode* node = getRetICFGNode(inst); if(node==nullptr) - node = addRetBlock(inst); - assert (node!=nullptr && "no RetICFGNode for this instruction?"); + node = addRetICFGNode(inst); + assert (node!=nullptr && "no RetBlockNode for this instruction?"); return node; } -IntraICFGNode* ICFG::getIntraICFGNode(const SVFInstruction* inst) +IntraBlockNode* ICFG::getIntraBlockNode(const Instruction* inst) { - IntraICFGNode* node = getIntraBlock(inst); + IntraBlockNode* node = getIntraBlockICFGNode(inst); if(node==nullptr) - node = addIntraBlock(inst); + node = addIntraBlockICFGNode(inst); return node; } /// Add a function entry node -FunEntryICFGNode* ICFG::getFunEntryICFGNode(const SVFFunction* fun) +FunEntryBlockNode* ICFG::getFunEntryBlockNode(const SVFFunction* fun) { - FunEntryICFGNode* b = getFunEntryBlock(fun); + FunEntryBlockNode* b = getFunEntryICFGNode(fun); if (b == nullptr) - return addFunEntryBlock(fun); + return addFunEntryICFGNode(fun); else return b; } /// Add a function exit node -FunExitICFGNode* ICFG::getFunExitICFGNode(const SVFFunction* fun) +FunExitBlockNode* ICFG::getFunExitBlockNode(const SVFFunction* fun) { - FunExitICFGNode* b = getFunExitBlock(fun); + FunExitBlockNode* b = getFunExitICFGNode(fun); if (b == nullptr) - return addFunExitBlock(fun); + return addFunExitICFGNode(fun); else return b; } @@ -348,7 +304,7 @@ ICFGEdge* ICFG::getICFGEdge(const ICFGNode* src, const ICFGNode* dst, ICFGEdge:: { ICFGEdge * edge = nullptr; - u32_t counter = 0; + Size_t counter = 0; for (ICFGEdge::ICFGEdgeSetTy::iterator iter = src->OutEdgeBegin(); iter != src->OutEdgeEnd(); ++iter) { @@ -369,8 +325,7 @@ ICFGEdge* ICFG::getICFGEdge(const ICFGNode* src, const ICFGNode* dst, ICFGEdge:: ICFGEdge* ICFG::addIntraEdge(ICFGNode* srcNode, ICFGNode* dstNode) { checkIntraEdgeParents(srcNode, dstNode); - ICFGEdge* edge = hasIntraICFGEdge(srcNode, dstNode, ICFGEdge::IntraCF); - if (edge != nullptr) + if(ICFGEdge* edge = hasIntraICFGEdge(srcNode,dstNode, ICFGEdge::IntraCF)) { assert(edge->isIntraCFGEdge() && "this should be an intra CFG edge!"); return nullptr; @@ -385,12 +340,10 @@ ICFGEdge* ICFG::addIntraEdge(ICFGNode* srcNode, ICFGNode* dstNode) /*! * Add conditional intraprocedural edges between two nodes */ -ICFGEdge* ICFG::addConditionalIntraEdge(ICFGNode* srcNode, ICFGNode* dstNode, const SVFValue* condition, s32_t branchCondVal) -{ +ICFGEdge* ICFG::addConditionalIntraEdge(ICFGNode* srcNode, ICFGNode* dstNode, const Value* condition, NodeID branchID){ checkIntraEdgeParents(srcNode, dstNode); - ICFGEdge* edge = hasIntraICFGEdge(srcNode, dstNode, ICFGEdge::IntraCF); - if (edge != nullptr) + if(ICFGEdge* edge = hasIntraICFGEdge(srcNode,dstNode, ICFGEdge::IntraCF)) { assert(edge->isIntraCFGEdge() && "this should be an intra CFG edge!"); return nullptr; @@ -398,7 +351,7 @@ ICFGEdge* ICFG::addConditionalIntraEdge(ICFGNode* srcNode, ICFGNode* dstNode, co else { IntraCFGEdge* intraEdge = new IntraCFGEdge(srcNode,dstNode); - intraEdge->setBranchCondition(condition,branchCondVal); + intraEdge->setBranchCondtion(condition,branchID); return (addICFGEdge(intraEdge) ? intraEdge : nullptr); } } @@ -407,10 +360,9 @@ ICFGEdge* ICFG::addConditionalIntraEdge(ICFGNode* srcNode, ICFGNode* dstNode, co /*! * Add interprocedural call edges between two nodes */ -ICFGEdge* ICFG::addCallEdge(ICFGNode* srcNode, ICFGNode* dstNode, const SVFInstruction* cs) +ICFGEdge* ICFG::addCallEdge(ICFGNode* srcNode, ICFGNode* dstNode, const Instruction* cs) { - ICFGEdge* edge = hasInterICFGEdge(srcNode,dstNode, ICFGEdge::CallCF); - if (edge != nullptr) + if(ICFGEdge* edge = hasInterICFGEdge(srcNode,dstNode, ICFGEdge::CallCF)) { assert(edge->isCallCFGEdge() && "this should be a call CFG edge!"); return nullptr; @@ -425,10 +377,9 @@ ICFGEdge* ICFG::addCallEdge(ICFGNode* srcNode, ICFGNode* dstNode, const SVFInstr /*! * Add interprocedural return edges between two nodes */ -ICFGEdge* ICFG::addRetEdge(ICFGNode* srcNode, ICFGNode* dstNode, const SVFInstruction* cs) +ICFGEdge* ICFG::addRetEdge(ICFGNode* srcNode, ICFGNode* dstNode, const Instruction* cs) { - ICFGEdge* edge = hasInterICFGEdge(srcNode, dstNode, ICFGEdge::RetCF); - if (edge != nullptr) + if(ICFGEdge* edge = hasInterICFGEdge(srcNode,dstNode, ICFGEdge::RetCF)) { assert(edge->isRetCFGEdge() && "this should be a return CFG edge!"); return nullptr; @@ -454,7 +405,7 @@ void ICFG::dump(const std::string& file, bool simple) */ void ICFG::view() { - SVF::ViewGraph(this, "Interprocedural Control-Flow Graph"); + llvm::ViewGraph(this, "Interprocedural Control-Flow Graph"); } /*! @@ -466,71 +417,38 @@ void ICFG::updateCallGraph(PTACallGraph* callgraph) PTACallGraph::CallEdgeMap::const_iterator eiter = callgraph->getIndCallMap().end(); for (; iter != eiter; iter++) { - const CallICFGNode* callBlock = iter->first; - const SVFInstruction* cs = callBlock->getCallSite(); + const CallBlockNode* callBlock = iter->first; + const Instruction* cs = callBlock->getCallSite(); assert(callBlock->isIndirectCall() && "this is not an indirect call?"); const PTACallGraph::FunctionSet & functions = iter->second; for (PTACallGraph::FunctionSet::const_iterator func_iter = functions.begin(); func_iter != functions.end(); func_iter++) { const SVFFunction* callee = *func_iter; - CallICFGNode* callBlockNode = getCallICFGNode(cs); - RetICFGNode* retBlockNode = getRetICFGNode(cs); - /// if this is an external function (no function body), connect calleeEntryNode to calleeExitNode - if (isExtCall(callee)) - addIntraEdge(callBlockNode, retBlockNode); - else + CallBlockNode* CallBlockNode = getCallBlockNode(cs); + if (!isExtCall(callee)) { - FunEntryICFGNode* calleeEntryNode = getFunEntryBlock(callee); - FunExitICFGNode* calleeExitNode = getFunExitBlock(callee); - if(ICFGEdge* callEdge = addCallEdge(callBlockNode, calleeEntryNode, cs)) - { - for (const SVFStmt *stmt : callBlockNode->getSVFStmts()) - { - if(const CallPE *callPE = SVFUtil::dyn_cast(stmt)) - { - if(callPE->getFunEntryICFGNode() == calleeEntryNode) - SVFUtil::cast(callEdge)->addCallPE(callPE); - } - } - } - if(ICFGEdge* retEdge = addRetEdge(calleeExitNode, retBlockNode, cs)) - { - for (const SVFStmt *stmt : retBlockNode->getSVFStmts()) - { - if(const RetPE *retPE = SVFUtil::dyn_cast(stmt)) - { - if(retPE->getFunExitICFGNode() == calleeExitNode) - SVFUtil::cast(retEdge)->addRetPE(retPE); - } - } - } - - /// Remove callBlockNode to retBlockNode intraICFGEdge since we found at least one inter procedural edge - if(ICFGEdge* edge = hasIntraICFGEdge(callBlockNode,retBlockNode, ICFGEdge::IntraCF)) - removeICFGEdge(edge); + FunEntryBlockNode* calleeEntryNode = getFunEntryICFGNode(callee); + addCallEdge(CallBlockNode, calleeEntryNode, cs); + RetBlockNode* retBlockNode = getRetBlockNode(cs); + FunExitBlockNode* calleeExitNode = getFunExitICFGNode(callee); + addRetEdge(calleeExitNode, retBlockNode, cs); } - } } - // dump ICFG - if (Options::DumpICFG()) - { - dump("icfg_final"); - } } /*! * GraphTraits specialization */ -namespace SVF +namespace llvm { template<> -struct DOTGraphTraits : public DOTGraphTraits +struct DOTGraphTraits : public DOTGraphTraits { typedef ICFGNode NodeType; DOTGraphTraits(bool isSimple = false) : - DOTGraphTraits(isSimple) + DOTGraphTraits(isSimple) { } @@ -548,43 +466,81 @@ struct DOTGraphTraits : public DOTGraphTraits /// Return the label of an ICFG node static std::string getSimpleNodeLabel(NodeType *node, ICFG*) { - return node->toString(); - } - - static bool isNodeHidden(ICFGNode *node, ICFG *) - { - if (Options::ShowHiddenNode()) - return false; + std::string str; + raw_string_ostream rawstr(str); + rawstr << "NodeID: " << node->getId() << "\n"; + if (IntraBlockNode* bNode = SVFUtil::dyn_cast(node)) + { + rawstr << "IntraBlockNode ID: " << bNode->getId() << " \t"; + PAG::PAGEdgeList& edges = PAG::getPAG()->getInstPTAPAGEdgeList(bNode); + if (edges.empty()) { + rawstr << value2String(bNode->getInst()) << " \t"; + } else { + for (PAG::PAGEdgeList::iterator it = edges.begin(), eit = edges.end(); it != eit; ++it) + { + const PAGEdge* edge = *it; + rawstr << edge->toString(); + } + } + rawstr << " {fun: " << bNode->getFun()->getName() << "}"; + } + else if (FunEntryBlockNode* entry = SVFUtil::dyn_cast(node)) + { + rawstr << entry->toString(); + } + else if (FunExitBlockNode* exit = SVFUtil::dyn_cast(node)) + { + rawstr << exit->toString(); + } + else if (CallBlockNode* call = SVFUtil::dyn_cast(node)) + { + rawstr << call->toString(); + } + else if (RetBlockNode* ret = SVFUtil::dyn_cast(node)) + { + rawstr << ret->toString(); + } + else if (GlobalBlockNode* glob = SVFUtil::dyn_cast(node) ) + { + PAG::PAGEdgeList& edges = PAG::getPAG()->getInstPTAPAGEdgeList(glob); + for (PAG::PAGEdgeList::iterator it = edges.begin(), eit = edges.end(); it != eit; ++it) + { + const PAGEdge* edge = *it; + rawstr << edge->toString(); + } + } else - return node->getInEdges().empty() && node->getOutEdges().empty(); + assert(false && "what else kinds of nodes do we have??"); + + return rawstr.str(); } static std::string getNodeAttributes(NodeType *node, ICFG*) { std::string str; - std::stringstream rawstr(str); + raw_string_ostream rawstr(str); - if(SVFUtil::isa(node)) + if(SVFUtil::isa(node)) { rawstr << "color=black"; } - else if(SVFUtil::isa(node)) + else if(SVFUtil::isa(node)) { rawstr << "color=yellow"; } - else if(SVFUtil::isa(node)) + else if(SVFUtil::isa(node)) { rawstr << "color=green"; } - else if(SVFUtil::isa(node)) + else if(SVFUtil::isa(node)) { rawstr << "color=red"; } - else if(SVFUtil::isa(node)) + else if(SVFUtil::isa(node)) { rawstr << "color=blue"; } - else if(SVFUtil::isa(node)) + else if(SVFUtil::isa(node)) { rawstr << "color=purple"; } @@ -617,7 +573,7 @@ struct DOTGraphTraits : public DOTGraphTraits assert(edge && "No edge found!!"); std::string str; - std::stringstream rawstr(str); + raw_string_ostream rawstr(str); if (CallCFGEdge* dirCall = SVFUtil::dyn_cast(edge)) rawstr << dirCall->getCallSite(); else if (RetCFGEdge* dirRet = SVFUtil::dyn_cast(edge)) diff --git a/svf/lib/Graphs/IRGraph.cpp b/svf/lib/Graphs/IRGraph.cpp deleted file mode 100644 index 824eba71e..000000000 --- a/svf/lib/Graphs/IRGraph.cpp +++ /dev/null @@ -1,287 +0,0 @@ -//===- SVFIR.cpp -- Program assignment graph------------------------------------// -// -// SVF: Static Value-Flow Analysis -// -// Copyright (C) <2013-2017> -// - -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU Affero General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Affero General Public License for more details. - -// You should have received a copy of the GNU Affero General Public License -// along with this program. If not, see . -// -//===----------------------------------------------------------------------===// - -/* - * SVFIR.cpp - * - * Created on: Nov 1, 2013 - * Author: Yulei Sui - */ - -#include "Graphs/IRGraph.h" -#include "Util/Options.h" - -using namespace SVF; -using namespace SVFUtil; - -IRGraph::~IRGraph() -{ - SymbolTableInfo::releaseSymbolInfo(); - symInfo = nullptr; -} - -/*! - * Add a SVFIR edge into edge map - */ -bool IRGraph::addEdge(SVFVar* src, SVFVar* dst, SVFStmt* edge) -{ - - DBOUT(DPAGBuild, - outs() << "add edge from " << src->getId() << " kind :" - << src->getNodeKind() << " to " << dst->getId() - << " kind :" << dst->getNodeKind() << "\n"); - src->addOutEdge(edge); - dst->addInEdge(edge); - return true; -} - -/*! - * Return true if it is an intra-procedural edge - */ -SVFStmt* IRGraph::hasNonlabeledEdge(SVFVar* src, SVFVar* dst, SVFStmt::PEDGEK kind) -{ - SVFStmt edge(src,dst,kind, false); - SVFStmt::SVFStmtSetTy::iterator it = KindToSVFStmtSetMap[kind].find(&edge); - if (it != KindToSVFStmtSetMap[kind].end()) - { - return *it; - } - return nullptr; -} - -/*! - * Return an MultiOpndStmt if found - */ -SVFStmt* IRGraph::hasLabeledEdge(SVFVar* src, SVFVar* op1, SVFStmt::PEDGEK kind, const SVFVar* op2) -{ - SVFStmt edge(src,op1,SVFStmt::makeEdgeFlagWithAddionalOpnd(kind,op2), false); - SVFStmt::SVFStmtSetTy::iterator it = KindToSVFStmtSetMap[kind].find(&edge); - if (it != KindToSVFStmtSetMap[kind].end()) - { - return *it; - } - return nullptr; -} - -/*! - * Return an inter-procedural edge if found - */ -SVFStmt* IRGraph::hasLabeledEdge(SVFVar* src, SVFVar* dst, SVFStmt::PEDGEK kind, const ICFGNode* callInst) -{ - SVFStmt edge(src,dst,SVFStmt::makeEdgeFlagWithCallInst(kind,callInst), false); - SVFStmt::SVFStmtSetTy::iterator it = KindToSVFStmtSetMap[kind].find(&edge); - if (it != KindToSVFStmtSetMap[kind].end()) - { - return *it; - } - return nullptr; -} - -/*! - * Dump this IRGraph - */ -void IRGraph::dump(std::string name) -{ - GraphPrinter::WriteGraphToFile(outs(), name, this); -} - -/*! - * View IRGraph - */ -void IRGraph::view() -{ - SVF::ViewGraph(this, "ProgramAssignmentGraph"); -} - - -namespace SVF -{ -/*! - * Write value flow graph into dot file for debugging - */ -template<> -struct DOTGraphTraits : public DefaultDOTGraphTraits -{ - - typedef SVFVar NodeType; - typedef NodeType::iterator ChildIteratorType; - DOTGraphTraits(bool isSimple = false) : - DefaultDOTGraphTraits(isSimple) - { - } - - /// Return name of the graph - static std::string getGraphName(IRGraph *graph) - { - return graph->getGraphName(); - } - - /// isNodeHidden - If the function returns true, the given node is not - /// displayed in the graph - static bool isNodeHidden(SVFVar *node, IRGraph *) - { - if (Options::ShowHiddenNode()) return false; - else return node->isIsolatedNode(); - } - - /// Return label of a VFG node with two display mode - /// Either you can choose to display the name of the value or the whole instruction - static std::string getNodeLabel(SVFVar *node, IRGraph*) - { - std::string str; - std::stringstream rawstr(str); - // print function info - if (node->getFunction()) - rawstr << "[" << node->getFunction()->getName() << "] "; - - rawstr << node->toString(); - - return rawstr.str(); - - } - - static std::string getNodeAttributes(SVFVar *node, IRGraph*) - { - if (SVFUtil::isa(node)) - { - if(SVFUtil::isa(node)) - return "shape=hexagon"; - else if (SVFUtil::isa(node)) - return "shape=diamond"; - else - return "shape=box"; - } - else if (SVFUtil::isa(node)) - { - if(SVFUtil::isa(node)) - return "shape=doubleoctagon"; - else if(SVFUtil::isa(node)) - return "shape=box3d"; - else if (SVFUtil::isa(node)) - return "shape=tab"; - else - return "shape=component"; - } - else if (SVFUtil::isa(node)) - { - return "shape=Mrecord"; - } - else if (SVFUtil::isa(node)) - { - return "shape=octagon"; - } - else - { - assert(0 && "no such kind!!"); - } - return ""; - } - - template - static std::string getEdgeAttributes(SVFVar*, EdgeIter EI, IRGraph*) - { - const SVFStmt* edge = *(EI.getCurrent()); - assert(edge && "No edge found!!"); - if (SVFUtil::isa(edge)) - { - return "color=green"; - } - else if (SVFUtil::isa(edge)) - { - return "color=black"; - } - else if (SVFUtil::isa(edge)) - { - return "color=purple"; - } - else if (SVFUtil::isa(edge)) - { - return "color=blue"; - } - else if (SVFUtil::isa(edge)) - { - return "color=red"; - } - else if (SVFUtil::isa(edge)) - { - return "color=grey"; - } - else if (SVFUtil::isa(edge)) - { - return "color=grey"; - } - else if (SVFUtil::isa(edge)) - { - return "color=grey"; - } - else if (SVFUtil::isa(edge)) - { - return "color=grey"; - } - else if (SVFUtil::isa(edge)) - { - return "color=grey"; - } - else if (SVFUtil::isa(edge)) - { - return "color=grey"; - } - else if (SVFUtil::isa(edge)) - { - return "color=Turquoise"; - } - else if (SVFUtil::isa(edge)) - { - return "color=Turquoise"; - } - else if (SVFUtil::isa(edge)) - { - return "color=black,style=dashed"; - } - else if (SVFUtil::isa(edge)) - { - return "color=black,style=dotted"; - } - - assert(false && "No such kind edge!!"); - exit(1); - } - - template - static std::string getEdgeSourceLabel(SVFVar*, EdgeIter EI) - { - const SVFStmt* edge = *(EI.getCurrent()); - assert(edge && "No edge found!!"); - if(const CallPE* calledge = SVFUtil::dyn_cast(edge)) - { - const SVFInstruction* callInst= calledge->getCallSite()->getCallSite(); - return callInst->getSourceLoc(); - } - else if(const RetPE* retedge = SVFUtil::dyn_cast(edge)) - { - const SVFInstruction* callInst= retedge->getCallSite()->getCallSite(); - return callInst->getSourceLoc(); - } - return ""; - } -}; -} // End namespace llvm diff --git a/svf/lib/Graphs/OfflineConsG.cpp b/svf/lib/Graphs/OfflineConsG.cpp new file mode 100644 index 000000000..04609fa2f --- /dev/null +++ b/svf/lib/Graphs/OfflineConsG.cpp @@ -0,0 +1,293 @@ +//===- OfflineConsG.cpp -- Offline constraint graph -----------------------------// +// +// SVF: Static Value-Flow Analysis +// +// Copyright (C) <2013-2017> +// + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// +//===----------------------------------------------------------------------===// + +/* + * OfflineConsG.cpp + * + * Created on: Oct 28, 2018 + * Author: Yuxiang Lei + */ + +#include "Util/Options.h" +#include "Graphs/OfflineConsG.h" + +using namespace SVF; +using namespace SVFUtil; + +/*! + * Builder of offline constraint graph + */ +void OfflineConsG::buildOfflineCG() +{ + LoadEdges loads; + StoreEdges stores; + + // Add a copy edge between the ref node of src node and dst node + for (ConstraintEdge::ConstraintEdgeSetTy::iterator it = LoadCGEdgeSet.begin(), eit = + LoadCGEdgeSet.end(); it != eit; ++it) + { + LoadCGEdge *load = SVFUtil::dyn_cast(*it); + loads.insert(load); + NodeID src = load->getSrcID(); + NodeID dst = load->getDstID(); + addRefLoadEdge(src, dst); + } + // Add a copy edge between src node and the ref node of dst node + for (ConstraintEdge::ConstraintEdgeSetTy::iterator it = StoreCGEdgeSet.begin(), eit = + StoreCGEdgeSet.end(); it != eit; ++it) + { + StoreCGEdge *store = SVFUtil::dyn_cast(*it); + stores.insert(store); + NodeID src = store->getSrcID(); + NodeID dst = store->getDstID(); + addRefStoreEdge(src, dst); + } + + // Dump offline graph with all edges + dump("oCG_initial"); + + // Remove load and store edges in offline constraint graph + for (LoadEdges::iterator it = loads.begin(), eit = loads.end(); it != eit; ++it) + { + removeLoadEdge(*it); + } + for (StoreEdges::iterator it = stores.begin(), eit = stores.end(); it != eit; ++it) + { + removeStoreEdge(*it); + } + + // Dump offline graph with removed load and store edges + dump("oCG_final"); +} + +/*! + * Add a copy edge between the ref node of src node and dst node, + * while meeting a LOAD constraint. + */ +bool OfflineConsG::addRefLoadEdge(NodeID src, NodeID dst) +{ + createRefNode(src); + NodeID ref = nodeToRefMap[src]; + return addCopyCGEdge(ref, dst); +} + +/*! + * Add a copy edge between src node and the ref node of dst node, + * while meeting a STORE constraint. + */ +bool OfflineConsG::addRefStoreEdge(NodeID src, NodeID dst) +{ + createRefNode(dst); + NodeID ref = nodeToRefMap[dst]; + return addCopyCGEdge(src, ref); +} + +/*! + * Create a ref node for a constraint node if it does not have one + */ +bool OfflineConsG::createRefNode(NodeID nodeId) +{ + if (hasRef(nodeId)) + return false; + + NodeID refId = pag->addDummyValNode(); + ConstraintNode* node = new ConstraintNode(refId); + addConstraintNode(node, refId); + refNodes.insert(refId); + nodeToRefMap[nodeId] = refId; + return true; +} + +/*! + * Use a offline SCC detector to solve node relations in OCG. + * Generally, the 'oscc' should be solved first. + */ +void OfflineConsG::solveOfflineSCC(OSCC* oscc) +{ + // Build offline nodeToRepMap + buildOfflineMap(oscc); +} + +/*! + * Build offline node to rep map, which only collect nodes having a ref node + */ +void OfflineConsG::buildOfflineMap(OSCC* oscc) +{ + for (NodeToRepMap::const_iterator it = nodeToRefMap.begin(); it != nodeToRefMap.end(); ++it) + { + NodeID node = it->first; + NodeID ref = getRef(node); + NodeID rep = solveRep(oscc,oscc->repNode(ref)); + if (!isaRef(rep) && !isaRef(node)) + setNorRep(node, rep); + } +} + +/*! + * The rep nodes of offline constraint graph are possible to be 'ref' nodes. + * These nodes should be replaced by one of its sub nodes which is not a ref node. + */ +NodeID OfflineConsG::solveRep(OSCC* oscc, NodeID rep) +{ + if (isaRef(rep)) + { + NodeBS subNodes = oscc->subNodes(rep); + for (NodeBS::iterator subIt = subNodes.begin(), subEit = subNodes.end(); subIt != subEit; ++subIt) + { + if (isaRef(*subIt)) + { + rep = *subIt; + break; + } + } + } + return rep; +} + +/*! + * Dump offline constraint graph + */ +void OfflineConsG::dump(std::string name) +{ + if (Options::OCGDotGraph) + GraphPrinter::WriteGraphToFile(outs(), name, this); +} + + + +// --- GraphTraits specialization for offline constraint graph --- + +namespace llvm +{ +template<> +struct DOTGraphTraits : public DOTGraphTraits +{ + + typedef ConstraintNode NodeType; + DOTGraphTraits(bool isSimple = false) : + DOTGraphTraits(isSimple) + { + } + + /// Return name of the graph + static std::string getGraphName(OfflineConsG*) + { + return "Offline Constraint Graph"; + } + + /// Return label of a VFG node with two display mode + /// Either you can choose to display the name of the value or the whole instruction + static std::string getNodeLabel(NodeType *n, OfflineConsG*) + { + std::string str; + raw_string_ostream rawstr(str); + if (PAG::getPAG()->findPAGNode(n->getId())) + { + PAGNode *node = PAG::getPAG()->getPAGNode(n->getId()); + bool briefDisplay = true; + bool nameDisplay = true; + + + if (briefDisplay) + { + if (SVFUtil::isa(node)) + { + if (nameDisplay) + rawstr << node->getId() << ":" << node->getValueName(); + else + rawstr << node->getId(); + } + else + rawstr << node->getId(); + } + else + { + // print the whole value + if (!SVFUtil::isa(node) && !SVFUtil::isa(node)) + rawstr << *node->getValue(); + else + rawstr << ""; + + } + + return rawstr.str(); + } + else + { + rawstr<< n->getId(); + return rawstr.str(); + } + } + + static std::string getNodeAttributes(NodeType *n, OfflineConsG*) + { + if (PAG::getPAG()->findPAGNode(n->getId())) + { + PAGNode *node = PAG::getPAG()->getPAGNode(n->getId()); + return node->getNodeAttrForDotDisplay(); + } + else + { + return "shape=folder"; + } + } + + template + static std::string getEdgeAttributes(NodeType*, EdgeIter EI, OfflineConsG*) + { + ConstraintEdge* edge = *(EI.getCurrent()); + assert(edge && "No edge found!!"); + if (edge->getEdgeKind() == ConstraintEdge::Addr) + { + return "color=green"; + } + else if (edge->getEdgeKind() == ConstraintEdge::Copy) + { + return "color=black"; + } + else if (edge->getEdgeKind() == ConstraintEdge::NormalGep + || edge->getEdgeKind() == ConstraintEdge::VariantGep) + { + return "color=purple"; + } + else if (edge->getEdgeKind() == ConstraintEdge::Store) + { + return "color=blue"; + } + else if (edge->getEdgeKind() == ConstraintEdge::Load) + { + return "color=red"; + } + else + { + assert(0 && "No such kind edge!!"); + } + return ""; + } + + template + static std::string getEdgeSourceLabel(NodeType*, EdgeIter) + { + return ""; + } +}; +} // End namespace llvm diff --git a/svf/lib/Graphs/PAG.cpp b/svf/lib/Graphs/PAG.cpp new file mode 100644 index 000000000..510cfb813 --- /dev/null +++ b/svf/lib/Graphs/PAG.cpp @@ -0,0 +1,1238 @@ +//===- PAG.cpp -- Program assignment graph------------------------------------// +// +// SVF: Static Value-Flow Analysis +// +// Copyright (C) <2013-2017> +// + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// +//===----------------------------------------------------------------------===// + +/* + * PAG.cpp + * + * Created on: Nov 1, 2013 + * Author: Yulei Sui + */ + +#include "Util/Options.h" +#include "Graphs/PAG.h" +#include "SVF-FE/LLVMUtil.h" +#include "SVF-FE/ICFGBuilder.h" + +using namespace SVF; +using namespace SVFUtil; + + +u64_t PAGEdge::callEdgeLabelCounter = 0; +u64_t PAGEdge::storeEdgeLabelCounter = 0; +PAGEdge::Inst2LabelMap PAGEdge::inst2LabelMap; + +PAG* PAG::pag = nullptr; + + +const std::string PAGNode::toString() const { + std::string str; + raw_string_ostream rawstr(str); + rawstr << "PAGNode ID: " << getId(); + return rawstr.str(); +} + +/// Get shape and/or color of node for .dot display. +const std::string PAGNode::getNodeAttrForDotDisplay() const { + // TODO: Maybe use over-rides instead of these ifs, + // But this puts them conveniently together. + if (SVFUtil::isa(this)) + { + if(SVFUtil::isa(this)) + return "shape=hexagon"; + else if (SVFUtil::isa(this)) + return "shape=diamond"; + else + return "shape=box"; + } + else if (SVFUtil::isa(this)) + { + if(SVFUtil::isa(this)) + return "shape=doubleoctagon"; + else if(SVFUtil::isa(this)) + return "shape=box3d"; + else if (SVFUtil::isa(this)) + return "shape=tab"; + else + return "shape=component"; + } + else if (SVFUtil::isa(this)) + { + return "shape=Mrecord"; + } + else if (SVFUtil::isa(this)) + { + return "shape=octagon"; + } + else + { + assert(0 && "no such kind!!"); + } + return ""; +} + +void PAGNode::dump() const { + outs() << this->toString() << "\n"; +} + +const std::string ValPN::toString() const { + std::string str; + raw_string_ostream rawstr(str); + rawstr << "ValPN ID: " << getId(); + if (Options::PAGDotGraphShorter) { + rawstr << "\n"; + } + rawstr << value2String(value); + return rawstr.str(); +} + +const std::string ObjPN::toString() const { + std::string str; + raw_string_ostream rawstr(str); + rawstr << "ObjPN ID: " << getId(); + if (Options::PAGDotGraphShorter) { + rawstr << "\n"; + } + rawstr << value2String(value); + return rawstr.str(); +} + +const std::string GepValPN::toString() const { + std::string str; + raw_string_ostream rawstr(str); + rawstr << "GepValPN ID: " << getId() << " with offset_" + llvm::utostr(getOffset()); + if (Options::PAGDotGraphShorter) { + rawstr << "\n"; + } + rawstr << value2String(value); + return rawstr.str(); +} + +const std::string GepObjPN::toString() const { + std::string str; + raw_string_ostream rawstr(str); + rawstr << "GepObjPN ID: " << getId() << " with offset_" + llvm::itostr(ls.getOffset()); + if (Options::PAGDotGraphShorter) { + rawstr << "\n"; + } + rawstr << value2String(value); + return rawstr.str(); +} + +const std::string FIObjPN::toString() const { + std::string str; + raw_string_ostream rawstr(str); + rawstr << "FIObjPN ID: " << getId() << " (base object)"; + if (Options::PAGDotGraphShorter) { + rawstr << "\n"; + } + rawstr << value2String(value); + return rawstr.str(); +} + +const std::string RetPN::toString() const { + std::string str; + raw_string_ostream rawstr(str); + rawstr << "RetPN ID: " << getId() << " unique return node for function " << SVFUtil::cast(value)->getName(); + return rawstr.str(); +} + +const std::string VarArgPN::toString() const { + std::string str; + raw_string_ostream rawstr(str); + rawstr << "VarArgPN ID: " << getId() << " Var arg node for function " << SVFUtil::cast(value)->getName(); + return rawstr.str(); +} + +const std::string DummyValPN::toString() const { + std::string str; + raw_string_ostream rawstr(str); + rawstr << "DummyValPN ID: " << getId(); + return rawstr.str(); +} + +const std::string DummyObjPN::toString() const { + std::string str; + raw_string_ostream rawstr(str); + rawstr << "DummyObjPN ID: " << getId(); + return rawstr.str(); +} + +const std::string CloneDummyObjPN::toString() const { + std::string str; + raw_string_ostream rawstr(str); + rawstr << "CloneDummyObjPN ID: " << getId(); + return rawstr.str(); +} + +const std::string CloneGepObjPN::toString() const { + std::string str; + raw_string_ostream rawstr(str); + rawstr << "CloneGepObjPN ID: " << getId(); + return rawstr.str(); +} + +const std::string CloneFIObjPN::toString() const { + std::string str; + raw_string_ostream rawstr(str); + rawstr << "CloneFIObjPN ID: " << getId(); + return rawstr.str(); +} + +const std::string PAGEdge::toString() const { + std::string str; + raw_string_ostream rawstr(str); + rawstr << "PAGEdge: [" << getDstID() << "<--" << getSrcID() << "]\t"; + return rawstr.str(); +} + +const std::string AddrPE::toString() const{ + std::string str; + raw_string_ostream rawstr(str); + rawstr << "AddrPE: [" << getDstID() << "<--" << getSrcID() << "]\t"; + if (Options::PAGDotGraphShorter) { + rawstr << "\n"; + } + rawstr << value2String(getValue()); + return rawstr.str(); +} + +const std::string CopyPE::toString() const{ + std::string str; + raw_string_ostream rawstr(str); + rawstr << "CopyPE: [" << getDstID() << "<--" << getSrcID() << "]\t"; + if (Options::PAGDotGraphShorter) { + rawstr << "\n"; + } + rawstr << value2String(getValue()); + return rawstr.str(); +} + +const std::string CmpPE::toString() const{ + std::string str; + raw_string_ostream rawstr(str); + rawstr << "CmpPE: [" << getDstID() << "<--" << getSrcID() << "]\t"; + if (Options::PAGDotGraphShorter) { + rawstr << "\n"; + } + rawstr << value2String(getValue()); + return rawstr.str(); +} + +const std::string BinaryOPPE::toString() const{ + std::string str; + raw_string_ostream rawstr(str); + rawstr << "BinaryOPPE: [" << getDstID() << "<--" << getSrcID() << "]\t"; + if (Options::PAGDotGraphShorter) { + rawstr << "\n"; + } + rawstr << value2String(getValue()); + return rawstr.str(); +} + +const std::string UnaryOPPE::toString() const{ + std::string str; + raw_string_ostream rawstr(str); + rawstr << "UnaryOPPE: [" << getDstID() << "<--" << getSrcID() << "]\t"; + if (Options::PAGDotGraphShorter) { + rawstr << "\n"; + } + rawstr << value2String(getValue()); + return rawstr.str(); +} + +const std::string LoadPE::toString() const{ + std::string str; + raw_string_ostream rawstr(str); + rawstr << "LoadPE: [" << getDstID() << "<--" << getSrcID() << "]\t"; + if (Options::PAGDotGraphShorter) { + rawstr << "\n"; + } + rawstr << value2String(getValue()); + return rawstr.str(); +} + +const std::string StorePE::toString() const{ + std::string str; + raw_string_ostream rawstr(str); + rawstr << "StorePE: [" << getDstID() << "<--" << getSrcID() << "]\t"; + if (Options::PAGDotGraphShorter) { + rawstr << "\n"; + } + rawstr << value2String(getValue()); + return rawstr.str(); +} + +const std::string GepPE::toString() const{ + std::string str; + raw_string_ostream rawstr(str); + rawstr << "GepPE: [" << getDstID() << "<--" << getSrcID() << "]\t"; + if (Options::PAGDotGraphShorter) { + rawstr << "\n"; + } + rawstr << value2String(getValue()); + return rawstr.str(); +} + +const std::string NormalGepPE::toString() const{ + std::string str; + raw_string_ostream rawstr(str); + rawstr << "NormalGepPE: [" << getDstID() << "<--" << getSrcID() << "]\t"; + if (Options::PAGDotGraphShorter) { + rawstr << "\n"; + } + rawstr << value2String(getValue()); + return rawstr.str(); +} + +const std::string VariantGepPE::toString() const{ + std::string str; + raw_string_ostream rawstr(str); + rawstr << "VariantGepPE: [" << getDstID() << "<--" << getSrcID() << "]\t"; + if (Options::PAGDotGraphShorter) { + rawstr << "\n"; + } + rawstr << value2String(getValue()); + return rawstr.str(); +} + +const std::string CallPE::toString() const{ + std::string str; + raw_string_ostream rawstr(str); + rawstr << "CallPE: [" << getDstID() << "<--" << getSrcID() << "]\t"; + if (Options::PAGDotGraphShorter) { + rawstr << "\n"; + } + rawstr << value2String(getValue()); + return rawstr.str(); +} + +const std::string RetPE::toString() const{ + std::string str; + raw_string_ostream rawstr(str); + rawstr << "RetPE: [" << getDstID() << "<--" << getSrcID() << "]\t"; + if (Options::PAGDotGraphShorter) { + rawstr << "\n"; + } + rawstr << value2String(getValue()); + return rawstr.str(); +} + +const std::string TDForkPE::toString() const{ + std::string str; + raw_string_ostream rawstr(str); + rawstr << "TDForkPE: [" << getDstID() << "<--" << getSrcID() << "]\t"; + if (Options::PAGDotGraphShorter) { + rawstr << "\n"; + } + rawstr << value2String(getValue()); + return rawstr.str(); +} + +const std::string TDJoinPE::toString() const{ + std::string str; + raw_string_ostream rawstr(str); + rawstr << "TDJoinPE: [" << getDstID() << "<--" << getSrcID() << "]\t"; + if (Options::PAGDotGraphShorter) { + rawstr << "\n"; + } + rawstr << value2String(getValue()); + return rawstr.str(); +} + + +PAG::PAG(bool buildFromFile) : fromFile(buildFromFile), nodeNumAfterPAGBuild(0), totalPTAPAGEdge(0) +{ + symInfo = SymbolTableInfo::SymbolInfo(); + icfg = new ICFG(); + ICFGBuilder builder(icfg); + builder.build(getModule()); + // insert dummy value if a correct value cannot be found + valueToEdgeMap[nullptr] = PAGEdgeSet(); +} + +/*! + * Add Address edge + */ +AddrPE* PAG::addAddrPE(NodeID src, NodeID dst) +{ + PAGNode* srcNode = getPAGNode(src); + PAGNode* dstNode = getPAGNode(dst); + if(PAGEdge* edge = hasNonlabeledEdge(srcNode,dstNode, PAGEdge::Addr)) + return SVFUtil::cast(edge); + else + { + AddrPE* addrPE = new AddrPE(srcNode, dstNode); + addEdge(srcNode,dstNode, addrPE); + return addrPE; + } +} + +/*! + * Add Copy edge + */ +CopyPE* PAG::addCopyPE(NodeID src, NodeID dst) +{ + PAGNode* srcNode = getPAGNode(src); + PAGNode* dstNode = getPAGNode(dst); + if(PAGEdge* edge = hasNonlabeledEdge(srcNode,dstNode, PAGEdge::Copy)) + return SVFUtil::cast(edge); + else + { + CopyPE* copyPE = new CopyPE(srcNode, dstNode); + addEdge(srcNode,dstNode, copyPE); + return copyPE; + } +} + +/*! + * Add Compare edge + */ +CmpPE* PAG::addCmpPE(NodeID src, NodeID dst) +{ + PAGNode* srcNode = getPAGNode(src); + PAGNode* dstNode = getPAGNode(dst); + if(PAGEdge* edge = hasNonlabeledEdge(srcNode,dstNode, PAGEdge::Cmp)) + return SVFUtil::cast(edge); + else + { + CmpPE* cmp = new CmpPE(srcNode, dstNode); + addEdge(srcNode,dstNode, cmp); + return cmp; + } +} + + +/*! + * Add Compare edge + */ +BinaryOPPE* PAG::addBinaryOPPE(NodeID src, NodeID dst) +{ + PAGNode* srcNode = getPAGNode(src); + PAGNode* dstNode = getPAGNode(dst); + if(PAGEdge* edge = hasNonlabeledEdge(srcNode,dstNode, PAGEdge::BinaryOp)) + return SVFUtil::cast(edge); + else + { + BinaryOPPE* binaryOP = new BinaryOPPE(srcNode, dstNode); + addEdge(srcNode,dstNode, binaryOP); + return binaryOP; + } +} + +/*! + * Add Unary edge + */ +UnaryOPPE* PAG::addUnaryOPPE(NodeID src, NodeID dst) +{ + PAGNode* srcNode = getPAGNode(src); + PAGNode* dstNode = getPAGNode(dst); + if(PAGEdge* edge = hasNonlabeledEdge(srcNode,dstNode, PAGEdge::UnaryOp)) + return SVFUtil::cast(edge); + else + { + UnaryOPPE* unaryOP = new UnaryOPPE(srcNode, dstNode); + addEdge(srcNode,dstNode, unaryOP); + return unaryOP; + } +} + +/*! + * Add Load edge + */ +LoadPE* PAG::addLoadPE(NodeID src, NodeID dst) +{ + PAGNode* srcNode = getPAGNode(src); + PAGNode* dstNode = getPAGNode(dst); + if(PAGEdge* edge = hasNonlabeledEdge(srcNode,dstNode, PAGEdge::Load)) + return SVFUtil::cast(edge); + else + { + LoadPE* loadPE = new LoadPE(srcNode, dstNode); + addEdge(srcNode,dstNode, loadPE); + return loadPE; + } +} + +/*! + * Add Store edge + * Note that two store instructions may share the same Store PAGEdge + */ +StorePE* PAG::addStorePE(NodeID src, NodeID dst, const IntraBlockNode* curVal) +{ + PAGNode* srcNode = getPAGNode(src); + PAGNode* dstNode = getPAGNode(dst); + if(PAGEdge* edge = hasLabeledEdge(srcNode,dstNode, PAGEdge::Store, curVal)) + return SVFUtil::cast(edge); + else + { + StorePE* storePE = new StorePE(srcNode, dstNode, curVal); + addEdge(srcNode,dstNode, storePE); + return storePE; + } +} + +/*! + * Add Call edge + */ +CallPE* PAG::addCallPE(NodeID src, NodeID dst, const CallBlockNode* cs) +{ + PAGNode* srcNode = getPAGNode(src); + PAGNode* dstNode = getPAGNode(dst); + if(PAGEdge* edge = hasLabeledEdge(srcNode,dstNode, PAGEdge::Call, cs)) + return SVFUtil::cast(edge); + else + { + CallPE* callPE = new CallPE(srcNode, dstNode, cs); + addEdge(srcNode,dstNode, callPE); + return callPE; + } +} + +/*! + * Add Return edge + */ +RetPE* PAG::addRetPE(NodeID src, NodeID dst, const CallBlockNode* cs) +{ + PAGNode* srcNode = getPAGNode(src); + PAGNode* dstNode = getPAGNode(dst); + if(PAGEdge* edge = hasLabeledEdge(srcNode,dstNode, PAGEdge::Ret, cs)) + return SVFUtil::cast(edge); + else + { + RetPE* retPE = new RetPE(srcNode, dstNode, cs); + addEdge(srcNode,dstNode, retPE); + return retPE; + } +} + +/*! + * Add blackhole/constant edge + */ +PAGEdge* PAG::addBlackHoleAddrPE(NodeID node) +{ + if(Options::HandBlackHole) + return pag->addAddrPE(pag->getBlackHoleNode(), node); + else + return pag->addCopyPE(pag->getNullPtr(), node); +} + +/*! + * Add Thread fork edge for parameter passing from a spawner to its spawnees + */ +TDForkPE* PAG::addThreadForkPE(NodeID src, NodeID dst, const CallBlockNode* cs) +{ + PAGNode* srcNode = getPAGNode(src); + PAGNode* dstNode = getPAGNode(dst); + if(PAGEdge* edge = hasLabeledEdge(srcNode,dstNode, PAGEdge::ThreadFork, cs)) + return SVFUtil::cast(edge); + else + { + TDForkPE* forkPE = new TDForkPE(srcNode, dstNode, cs); + addEdge(srcNode,dstNode, forkPE); + return forkPE; + } +} + +/*! + * Add Thread fork edge for parameter passing from a spawnee back to its spawners + */ +TDJoinPE* PAG::addThreadJoinPE(NodeID src, NodeID dst, const CallBlockNode* cs) +{ + PAGNode* srcNode = getPAGNode(src); + PAGNode* dstNode = getPAGNode(dst); + if(PAGEdge* edge = hasLabeledEdge(srcNode,dstNode, PAGEdge::ThreadJoin, cs)) + return SVFUtil::cast(edge); + else + { + TDJoinPE* joinPE = new TDJoinPE(srcNode, dstNode, cs); + addEdge(srcNode,dstNode, joinPE); + return joinPE; + } +} + + +/*! + * Add Offset(Gep) edge + * Find the base node id of src and connect base node to dst node + * Create gep offset: (offset + baseOff ) + */ +GepPE* PAG::addGepPE(NodeID src, NodeID dst, const LocationSet& ls, bool constGep) +{ + +// llvm::errs() << "adding gep edge with consGep = " << constGep << "\n"; + PAGNode* node = getPAGNode(src); + if (!constGep || node->hasIncomingVariantGepEdge()) + { + /// Since the offset from base to src is variant, + /// the new gep edge being created is also a VariantGepPE edge. + return addVariantGepPE(src, dst); + } + else + { + return addNormalGepPE(src, dst, ls); + } +} + +/*! + * Add normal (Gep) edge + */ +NormalGepPE* PAG::addNormalGepPE(NodeID src, NodeID dst, const LocationSet& ls) +{ + const LocationSet& baseLS = getLocationSetFromBaseNode(src); + PAGNode* baseNode = getPAGNode(getBaseValNode(src)); + PAGNode* dstNode = getPAGNode(dst); + if(PAGEdge* edge = hasNonlabeledEdge(baseNode, dstNode, PAGEdge::NormalGep)) + return SVFUtil::cast(edge); + else + { + NormalGepPE* gepPE = new NormalGepPE(baseNode, dstNode, ls+baseLS); + addEdge(baseNode, dstNode, gepPE); + return gepPE; + } +} + +/*! + * Add variant(Gep) edge + * Find the base node id of src and connect base node to dst node + */ +VariantGepPE* PAG::addVariantGepPE(NodeID src, NodeID dst) +{ + + PAGNode* baseNode = getPAGNode(getBaseValNode(src)); + PAGNode* dstNode = getPAGNode(dst); + if(PAGEdge* edge = hasNonlabeledEdge(baseNode, dstNode, PAGEdge::VariantGep)) + return SVFUtil::cast(edge); + else + { + VariantGepPE* gepPE = new VariantGepPE(baseNode, dstNode); + addEdge(baseNode, dstNode, gepPE); + return gepPE; + } +} + + + +/*! + * Add a temp field value node, this method can only invoked by getGepValNode + * due to constaint expression, curInst is used to distinguish different instructions (e.g., memorycpy) when creating GepValPN. + */ +NodeID PAG::addGepValNode(const Value* curInst,const Value* gepVal, const LocationSet& ls, NodeID i, const Type *type, u32_t fieldidx) +{ + NodeID base = getBaseValNode(getValueNode(gepVal)); + //assert(findPAGNode(i) == false && "this node should not be created before"); + assert(0==GepValNodeMap[curInst].count(std::make_pair(base, ls)) + && "this node should not be created before"); + GepValNodeMap[curInst][std::make_pair(base, ls)] = i; + GepValPN *node = new GepValPN(gepVal, i, ls, type, fieldidx); + return addValNode(gepVal, node, i); +} + +/*! + * Given an object node, find its field object node + */ +NodeID PAG::getGepObjNode(NodeID id, const LocationSet& ls) +{ + PAGNode* node = pag->getPAGNode(id); + if (GepObjPN* gepNode = SVFUtil::dyn_cast(node)) + return getGepObjNode(gepNode->getMemObj(), gepNode->getLocationSet() + ls); + else if (FIObjPN* baseNode = SVFUtil::dyn_cast(node)) + return getGepObjNode(baseNode->getMemObj(), ls); + else if (DummyObjPN* baseNode = SVFUtil::dyn_cast(node)) + return getGepObjNode(baseNode->getMemObj(), ls); + else + { + assert(false && "new gep obj node kind?"); + return id; + } +} + +/*! + * Get a field obj PAG node according to base mem obj and offset + * To support flexible field sensitive analysis with regard to MaxFieldOffset + * offset = offset % obj->getMaxFieldOffsetLimit() to create limited number of mem objects + * maximum number of field object creation is obj->getMaxFieldOffsetLimit() + */ +NodeID PAG::getGepObjNode(const MemObj* obj, const LocationSet& ls) +{ + NodeID base = getObjectNode(obj); + + /// if this obj is field-insensitive, just return the field-insensitive node. + if (obj->isFieldInsensitive()) + return getFIObjNode(obj); + + LocationSet newLS = SymbolTableInfo::SymbolInfo()->getModulusOffset(obj,ls); + + // Base and first field are the same memory location. + if (Options::FirstFieldEqBase && newLS.getOffset() == 0) return base; + + NodeLocationSetMap::iterator iter = GepObjNodeMap.find(std::make_pair(base, newLS)); + if (iter == GepObjNodeMap.end()) + return addGepObjNode(obj, newLS); + else + return iter->second; + +} + +/*! + * Add a field obj node, this method can only invoked by getGepObjNode + */ +NodeID PAG::addGepObjNode(const MemObj* obj, const LocationSet& ls) +{ + //assert(findPAGNode(i) == false && "this node should not be created before"); + NodeID base = getObjectNode(obj); + assert(0==GepObjNodeMap.count(std::make_pair(base, ls)) + && "this node should not be created before"); + + NodeID gepId = NodeIDAllocator::get()->allocateGepObjectId(base, ls.getOffset(), StInfo::getMaxFieldLimit()); + GepObjNodeMap[std::make_pair(base, ls)] = gepId; + GepObjPN *node = new GepObjPN(obj, gepId, ls); + memToFieldsMap[base].set(gepId); + return addObjNode(obj->getRefVal(), node, gepId); +} + +/*! + * Add a field-insensitive node, this method can only invoked by getFIGepObjNode + */ +NodeID PAG::addFIObjNode(const MemObj* obj) +{ + //assert(findPAGNode(i) == false && "this node should not be created before"); + NodeID base = getObjectNode(obj); + memToFieldsMap[base].set(obj->getSymId()); + FIObjPN *node = new FIObjPN(obj->getRefVal(), obj->getSymId(), obj); + return addObjNode(obj->getRefVal(), node, obj->getSymId()); +} + + +/*! + * Return true if it is an intra-procedural edge + */ +PAGEdge* PAG::hasNonlabeledEdge(PAGNode* src, PAGNode* dst, PAGEdge::PEDGEK kind) +{ + PAGEdge edge(src,dst,kind); + PAGEdge::PAGEdgeSetTy::iterator it = PAGEdgeKindToSetMap[kind].find(&edge); + if (it != PAGEdgeKindToSetMap[kind].end()) + { + return *it; + } + return nullptr; +} + +/*! + * Return true if it is an inter-procedural edge + */ +PAGEdge* PAG::hasLabeledEdge(PAGNode* src, PAGNode* dst, PAGEdge::PEDGEK kind, const ICFGNode* callInst) +{ + PAGEdge edge(src,dst,PAGEdge::makeEdgeFlagWithCallInst(kind,callInst)); + PAGEdge::PAGEdgeSetTy::iterator it = PAGEdgeKindToSetMap[kind].find(&edge); + if (it != PAGEdgeKindToSetMap[kind].end()) + { + return *it; + } + return nullptr; +} + + +/*! + * Add a PAG edge into edge map + */ +bool PAG::addEdge(PAGNode* src, PAGNode* dst, PAGEdge* edge) +{ + + DBOUT(DPAGBuild, + outs() << "add edge from " << src->getId() << " kind :" + << src->getNodeKind() << " to " << dst->getId() + << " kind :" << dst->getNodeKind() << "\n"); + src->addOutEdge(edge); + dst->addInEdge(edge); + bool added = PAGEdgeKindToSetMap[edge->getEdgeKind()].insert(edge).second; + assert(added && "duplicated edge, not added!!!"); + if (edge->isPTAEdge()) + { + totalPTAPAGEdge++; + PTAPAGEdgeKindToSetMap[edge->getEdgeKind()].insert(edge); + } + return true; +} + +/*! + * Get all fields object nodes of an object + */ +NodeBS& PAG::getAllFieldsObjNode(const MemObj* obj) +{ + NodeID base = getObjectNode(obj); + return memToFieldsMap[base]; +} + +/*! + * Get all fields object nodes of an object + */ +NodeBS& PAG::getAllFieldsObjNode(NodeID id) +{ + const PAGNode* node = pag->getPAGNode(id); + assert(SVFUtil::isa(node) && "need an object node"); + const ObjPN* obj = SVFUtil::cast(node); + return getAllFieldsObjNode(obj->getMemObj()); +} + +/*! + * Get all fields object nodes of an object + * If this object is collapsed into one field insensitive object + * Then only return this field insensitive object + */ +NodeBS PAG::getFieldsAfterCollapse(NodeID id) +{ + const PAGNode* node = pag->getPAGNode(id); + assert(SVFUtil::isa(node) && "need an object node"); + const MemObj* mem = SVFUtil::cast(node)->getMemObj(); + if(mem->isFieldInsensitive()) + { + NodeBS bs; + bs.set(getFIObjNode(mem)); + return bs; + } + else + return getAllFieldsObjNode(mem); +} + +/*! + * Get a base pointer given a pointer + * Return the source node of its connected gep edge if this pointer has + * Otherwise return the node id itself + */ +NodeID PAG::getBaseValNode(NodeID nodeId) +{ + PAGNode* node = getPAGNode(nodeId); + if (node->hasIncomingEdges(PAGEdge::NormalGep) || node->hasIncomingEdges(PAGEdge::VariantGep)) + { + PAGEdge::PAGEdgeSetTy& ngeps = node->getIncomingEdges(PAGEdge::NormalGep); + PAGEdge::PAGEdgeSetTy& vgeps = node->getIncomingEdges(PAGEdge::VariantGep); + + assert(((ngeps.size()+vgeps.size())==1) && "one node can only be connected by at most one gep edge!"); + + PAGNode::iterator it; + if(!ngeps.empty()) + it = ngeps.begin(); + else + it = vgeps.begin(); + + assert(SVFUtil::isa(*it) && "not a gep edge??"); + return (*it)->getSrcID(); + } + else + return nodeId; +} + +/*! + * Get a base PAGNode given a pointer + * Return the source node of its connected normal gep edge + * Otherwise return the node id itself + * Size_t offset : gep offset + */ +LocationSet PAG::getLocationSetFromBaseNode(NodeID nodeId) +{ + PAGNode* node = getPAGNode(nodeId); + PAGEdge::PAGEdgeSetTy& geps = node->getIncomingEdges(PAGEdge::NormalGep); + /// if this node is already a base node + if(geps.empty()) + return LocationSet(0); + + assert(geps.size()==1 && "one node can only be connected by at most one gep edge!"); + PAGNode::iterator it = geps.begin(); + const PAGEdge* edge = *it; + assert(SVFUtil::isa(edge) && "not a get edge??"); + const NormalGepPE* gepEdge = SVFUtil::cast(edge); + return gepEdge->getLocationSet(); +} + +/*! + * Clean up memory + */ +void PAG::destroy() +{ + for (PAGEdge::PAGKindToEdgeSetMapTy::iterator I = + PAGEdgeKindToSetMap.begin(), E = PAGEdgeKindToSetMap.end(); I != E; + ++I) + { + for (PAGEdge::PAGEdgeSetTy::iterator edgeIt = I->second.begin(), + endEdgeIt = I->second.end(); edgeIt != endEdgeIt; ++edgeIt) + { + delete *edgeIt; + } + } + SymbolTableInfo::releaseSymbolInfo(); + symInfo = nullptr; +} + +/*! + * Print this PAG graph including its nodes and edges + */ +void PAG::print() +{ + + outs() << "-------------------PAG------------------------------------\n"; + PAGEdge::PAGEdgeSetTy& addrs = pag->getEdgeSet(PAGEdge::Addr); + for (PAGEdge::PAGEdgeSetTy::iterator iter = addrs.begin(), eiter = + addrs.end(); iter != eiter; ++iter) + { + outs() << (*iter)->getSrcID() << " -- Addr --> " << (*iter)->getDstID() + << "\n"; + } + + PAGEdge::PAGEdgeSetTy& copys = pag->getEdgeSet(PAGEdge::Copy); + for (PAGEdge::PAGEdgeSetTy::iterator iter = copys.begin(), eiter = + copys.end(); iter != eiter; ++iter) + { + outs() << (*iter)->getSrcID() << " -- Copy --> " << (*iter)->getDstID() + << "\n"; + } + + PAGEdge::PAGEdgeSetTy& calls = pag->getEdgeSet(PAGEdge::Call); + for (PAGEdge::PAGEdgeSetTy::iterator iter = calls.begin(), eiter = + calls.end(); iter != eiter; ++iter) + { + outs() << (*iter)->getSrcID() << " -- Call --> " << (*iter)->getDstID() + << "\n"; + } + + PAGEdge::PAGEdgeSetTy& rets = pag->getEdgeSet(PAGEdge::Ret); + for (PAGEdge::PAGEdgeSetTy::iterator iter = rets.begin(), eiter = + rets.end(); iter != eiter; ++iter) + { + outs() << (*iter)->getSrcID() << " -- Ret --> " << (*iter)->getDstID() + << "\n"; + } + + PAGEdge::PAGEdgeSetTy& tdfks = pag->getEdgeSet(PAGEdge::ThreadFork); + for (PAGEdge::PAGEdgeSetTy::iterator iter = tdfks.begin(), eiter = + tdfks.end(); iter != eiter; ++iter) + { + outs() << (*iter)->getSrcID() << " -- ThreadFork --> " + << (*iter)->getDstID() << "\n"; + } + + PAGEdge::PAGEdgeSetTy& tdjns = pag->getEdgeSet(PAGEdge::ThreadJoin); + for (PAGEdge::PAGEdgeSetTy::iterator iter = tdjns.begin(), eiter = + tdjns.end(); iter != eiter; ++iter) + { + outs() << (*iter)->getSrcID() << " -- ThreadJoin --> " + << (*iter)->getDstID() << "\n"; + } + + PAGEdge::PAGEdgeSetTy& ngeps = pag->getEdgeSet(PAGEdge::NormalGep); + for (PAGEdge::PAGEdgeSetTy::iterator iter = ngeps.begin(), eiter = + ngeps.end(); iter != eiter; ++iter) + { + NormalGepPE* gep = SVFUtil::cast(*iter); + outs() << gep->getSrcID() << " -- NormalGep (" << gep->getOffset() + << ") --> " << gep->getDstID() << "\n"; + } + + PAGEdge::PAGEdgeSetTy& vgeps = pag->getEdgeSet(PAGEdge::VariantGep); + for (PAGEdge::PAGEdgeSetTy::iterator iter = vgeps.begin(), eiter = + vgeps.end(); iter != eiter; ++iter) + { + outs() << (*iter)->getSrcID() << " -- VariantGep --> " + << (*iter)->getDstID() << "\n"; + } + + PAGEdge::PAGEdgeSetTy& loads = pag->getEdgeSet(PAGEdge::Load); + for (PAGEdge::PAGEdgeSetTy::iterator iter = loads.begin(), eiter = + loads.end(); iter != eiter; ++iter) + { + outs() << (*iter)->getSrcID() << " -- Load --> " << (*iter)->getDstID() + << "\n"; + } + + PAGEdge::PAGEdgeSetTy& stores = pag->getEdgeSet(PAGEdge::Store); + for (PAGEdge::PAGEdgeSetTy::iterator iter = stores.begin(), eiter = + stores.end(); iter != eiter; ++iter) + { + outs() << (*iter)->getSrcID() << " -- Store --> " << (*iter)->getDstID() + << "\n"; + } + outs() << "----------------------------------------------------------\n"; + +} + +/* + * If this is a dummy node or node does not have incoming edges we assume it is not a pointer here + */ +bool PAG::isValidPointer(NodeID nodeId) const +{ + PAGNode* node = pag->getPAGNode(nodeId); + if ((node->getInEdges().empty() && node->getOutEdges().empty())) + return false; + return node->isPointer(); +} + +bool PAG::isValidTopLevelPtr(const PAGNode* node) +{ + if (node->isTopLevelPtr()) + { + if (isValidPointer(node->getId()) && node->hasValue()) + { + if (SVFUtil::ArgInNoCallerFunction(node->getValue())) + return false; + return true; + } + } + return false; +} +/*! + * PAGEdge constructor + */ +PAGEdge::PAGEdge(PAGNode* s, PAGNode* d, GEdgeFlag k) : + GenericPAGEdgeTy(s,d,k),value(nullptr),basicBlock(nullptr),icfgNode(nullptr) +{ + edgeId = PAG::getPAG()->getTotalEdgeNum(); + PAG::getPAG()->incEdgeNum(); +} + +/*! + * Whether src and dst nodes are both pointer type + */ +bool PAGEdge::isPTAEdge() const +{ + return getSrcNode()->isPointer() && getDstNode()->isPointer(); +} + +/*! + * PAGNode constructor + */ +PAGNode::PAGNode(const Value* val, NodeID i, PNODEK k) : + GenericPAGNodeTy(i,k), value(val) +{ + + assert( ValNode <= k && k <= CloneDummyObjNode && "new PAG node kind?"); + + switch (k) + { + case ValNode: + case GepValNode: + { + assert(val != nullptr && "value is nullptr for ValPN or GepValNode"); + isTLPointer = val->getType()->isPointerTy(); + isATPointer = false; + break; + } + + case RetNode: + { + assert(val != nullptr && "value is nullptr for RetNode"); + isTLPointer = SVFUtil::cast(val)->getReturnType()->isPointerTy(); + isATPointer = false; + break; + } + + case VarargNode: + case DummyValNode: + { + isTLPointer = true; + isATPointer = false; + break; + } + + case ObjNode: + case GepObjNode: + case FIObjNode: + case DummyObjNode: + case CloneGepObjNode: + case CloneFIObjNode: + case CloneDummyObjNode: + { + isTLPointer = false; + isATPointer = true; + break; + } + } +} + +bool PAGNode::isIsolatedNode() const{ + if (getInEdges().empty() && getOutEdges().empty()) + return true; + else if (isConstantData()) + return true; + else if (value && SVFUtil::isa(value)) + return SVFUtil::isIntrinsicFun(SVFUtil::cast(value)); + else + return false; +} + + +/*! + * Dump this PAG + */ +void PAG::dump(std::string name) +{ + GraphPrinter::WriteGraphToFile(outs(), name, this); +} + +/*! + * View PAG + */ +void PAG::view() +{ + llvm::ViewGraph(this, "ProgramAssignmentGraph"); +} + +/*! + * Whether to handle blackhole edge + */ +void PAG::handleBlackHole(bool b) +{ + Options::HandBlackHole = b; +} + +namespace llvm +{ +/*! + * Write value flow graph into dot file for debugging + */ +template<> +struct DOTGraphTraits : public DefaultDOTGraphTraits +{ + + typedef PAGNode NodeType; + typedef NodeType::iterator ChildIteratorType; + DOTGraphTraits(bool isSimple = false) : + DefaultDOTGraphTraits(isSimple) + { + } + + /// Return name of the graph + static std::string getGraphName(PAG *graph) + { + return graph->getGraphName(); + } + + /// isNodeHidden - If the function returns true, the given node is not + /// displayed in the graph +#if LLVM_VERSION_MAJOR >= 12 + static bool isNodeHidden(PAGNode *node, PAG*) { +#else + static bool isNodeHidden(PAGNode *node) { +#endif + return node->isIsolatedNode(); + } + + /// Return label of a VFG node with two display mode + /// Either you can choose to display the name of the value or the whole instruction + static std::string getNodeLabel(PAGNode *node, PAG*) + { + std::string str; + raw_string_ostream rawstr(str); + // print function info + if (node->getFunction()) + rawstr << "[" << node->getFunction()->getName() << "] "; + + rawstr << node->toString(); + + return rawstr.str(); + + } + + static std::string getNodeAttributes(PAGNode *node, PAG*) + { + return node->getNodeAttrForDotDisplay(); + } + + template + static std::string getEdgeAttributes(PAGNode*, EdgeIter EI, PAG*) + { + const PAGEdge* edge = *(EI.getCurrent()); + assert(edge && "No edge found!!"); + if (SVFUtil::isa(edge)) + { + return "color=green"; + } + else if (SVFUtil::isa(edge)) + { + return "color=black"; + } + else if (SVFUtil::isa(edge)) + { + return "color=purple"; + } + else if (SVFUtil::isa(edge)) + { + return "color=pink"; + } + else if (SVFUtil::isa(edge)) + { + return "color=blue"; + } + else if (SVFUtil::isa(edge)) + { + return "color=red"; + } + else if (SVFUtil::isa(edge)) + { + return "color=grey"; + } + else if (SVFUtil::isa(edge)) + { + return "color=grey"; + } + else if (SVFUtil::isa(edge)) + { + return "color=grey"; + } + else if (SVFUtil::isa(edge)) + { + return "color=Turquoise"; + } + else if (SVFUtil::isa(edge)) + { + return "color=Turquoise"; + } + else if (SVFUtil::isa(edge)) + { + return "color=black,style=dashed"; + } + else if (SVFUtil::isa(edge)) + { + return "color=black,style=dotted"; + } + + assert(false && "No such kind edge!!"); + exit(1); + } + + template + static std::string getEdgeSourceLabel(PAGNode*, EdgeIter EI) + { + const PAGEdge* edge = *(EI.getCurrent()); + assert(edge && "No edge found!!"); + if(const CallPE* calledge = SVFUtil::dyn_cast(edge)) + { + const Instruction* callInst= calledge->getCallSite()->getCallSite(); + return SVFUtil::getSourceLoc(callInst); + } + else if(const RetPE* retedge = SVFUtil::dyn_cast(edge)) + { + const Instruction* callInst= retedge->getCallSite()->getCallSite(); + return SVFUtil::getSourceLoc(callInst); + } + return ""; + } +}; +} // End namespace llvm diff --git a/svf/lib/Graphs/PTACallGraph.cpp b/svf/lib/Graphs/PTACallGraph.cpp index 29fbd4fcf..bf405addd 100644 --- a/svf/lib/Graphs/PTACallGraph.cpp +++ b/svf/lib/Graphs/PTACallGraph.cpp @@ -6,16 +6,16 @@ // // This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU Affero General Public License as published by +// it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Affero General Public License for more details. +// GNU General Public License for more details. -// You should have received a copy of the GNU Affero General Public License +// You should have received a copy of the GNU General Public License // along with this program. If not, see . // //===----------------------------------------------------------------------===// @@ -28,9 +28,8 @@ * Author: Yulei Sui */ -#include -#include "SVFIR/SVFModule.h" -#include "Util/SVFUtil.h" +#include "Util/SVFModule.h" +#include "SVF-FE/LLVMUtil.h" #include "Graphs/PTACallGraph.h" using namespace SVF; @@ -43,23 +42,22 @@ CallSiteID PTACallGraph::totalCallSiteNum = 1; /// Add direct and indirect callsite //@{ -void PTACallGraphEdge::addDirectCallSite(const CallICFGNode* call) +void PTACallGraphEdge::addDirectCallSite(const CallBlockNode* call) { assert(SVFUtil::getCallee(call->getCallSite()) && "not a direct callsite??"); directCalls.insert(call); } -void PTACallGraphEdge::addInDirectCallSite(const CallICFGNode* call) +void PTACallGraphEdge::addInDirectCallSite(const CallBlockNode* call) { - assert((nullptr == SVFUtil::getCallee(call->getCallSite()) || nullptr == SVFUtil::dyn_cast (SVFUtil::getForkedFun(call->getCallSite()))) && "not an indirect callsite??"); + assert((nullptr == SVFUtil::getCallee(call->getCallSite()) || nullptr == SVFUtil::dyn_cast (SVFUtil::getForkedFun(call->getCallSite()))) && "not an indirect callsite??"); indirectCalls.insert(call); } //@} -const std::string PTACallGraphEdge::toString() const -{ +const std::string PTACallGraphEdge::toString() const { std::string str; - std::stringstream rawstr(str); + raw_string_ostream rawstr(str); rawstr << "CallSite ID: " << getCallSiteID(); if(isDirectCallEdge()) rawstr << "direct call"; @@ -69,10 +67,9 @@ const std::string PTACallGraphEdge::toString() const return rawstr.str(); } -const std::string PTACallGraphNode::toString() const -{ +const std::string PTACallGraphNode::toString() const { std::string str; - std::stringstream rawstr(str); + raw_string_ostream rawstr(str); rawstr << "CallGraphNode ID: " << getId() << " {fun: " << fun->getName() << "}"; return rawstr.str(); } @@ -165,7 +162,7 @@ PTACallGraphEdge* PTACallGraph::getGraphEdge(PTACallGraphNode* src, PTACallGraph /*! * Add direct call edges */ -void PTACallGraph::addDirectCallGraphEdge(const CallICFGNode* cs,const SVFFunction* callerFun, const SVFFunction* calleeFun) +void PTACallGraph::addDirectCallGraphEdge(const CallBlockNode* cs,const SVFFunction* callerFun, const SVFFunction* calleeFun) { PTACallGraphNode* caller = getCallGraphNode(callerFun); @@ -185,7 +182,7 @@ void PTACallGraph::addDirectCallGraphEdge(const CallICFGNode* cs,const SVFFuncti /*! * Add indirect call edge to update call graph */ -void PTACallGraph::addIndirectCallGraphEdge(const CallICFGNode* cs,const SVFFunction* callerFun, const SVFFunction* calleeFun) +void PTACallGraph::addIndirectCallGraphEdge(const CallBlockNode* cs,const SVFFunction* callerFun, const SVFFunction* calleeFun) { PTACallGraphNode* caller = getCallGraphNode(callerFun); @@ -272,10 +269,10 @@ void PTACallGraph::verifyCallGraph() const FunctionSet& targets = it->second; if (targets.empty() == false) { - const CallICFGNode* cs = it->first; + const CallBlockNode* cs = it->first; const SVFFunction* func = cs->getCaller(); if (getCallGraphNode(func)->isReachableFromProgEntry() == false) - writeWrnMsg(func->getName() + " has indirect call site but not reachable from main"); + writeWrnMsg(func->getName().str() + " has indirect call site but not reachable from main"); } } } @@ -316,15 +313,15 @@ bool PTACallGraph::isReachableBetweenFunctions(const SVFFunction* srcFn, const S */ void PTACallGraph::dump(const std::string& filename) { - GraphPrinter::WriteGraphToFile(outs(), filename, this); + GraphPrinter::WriteGraphToFile(outs(), filename, this); } void PTACallGraph::view() { - SVF::ViewGraph(this, "Call Graph"); + llvm::ViewGraph(this, "Call Graph"); } -namespace SVF +namespace llvm { /*! @@ -399,7 +396,7 @@ struct DOTGraphTraits : public DefaultDOTGraphTraits assert(edge && "No edge found!!"); std::string str; - std::stringstream rawstr(str); + raw_string_ostream rawstr(str); rawstr << edge->getCallSiteID(); return rawstr.str(); diff --git a/svf/lib/Graphs/SVFG.cpp b/svf/lib/Graphs/SVFG.cpp index b417fba47..65bbbe5a4 100644 --- a/svf/lib/Graphs/SVFG.cpp +++ b/svf/lib/Graphs/SVFG.cpp @@ -6,16 +6,16 @@ // // This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU Affero General Public License as published by +// it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Affero General Public License for more details. +// GNU General Public License for more details. -// You should have received a copy of the GNU Affero General Public License +// You should have received a copy of the GNU General Public License // along with this program. If not, see . // //===----------------------------------------------------------------------===// @@ -27,81 +27,69 @@ * Author: Yulei Sui */ -#include "SVFIR/SVFModule.h" -#include "Util/SVFUtil.h" +#include "Util/SVFModule.h" +#include "SVF-FE/LLVMUtil.h" #include "Graphs/SVFG.h" #include "Graphs/SVFGOPT.h" #include "Graphs/SVFGStat.h" #include "Graphs/ICFG.h" -#include "Util/Options.h" -#include "MemoryModel/PointerAnalysisImpl.h" #include -#include "Util/Options.h" + using namespace SVF; using namespace SVFUtil; -const NodeBS MRSVFGNode::getDefSVFVars() const -{ - return getPointsTo(); -} -const std::string MRSVFGNode::toString() const -{ +const std::string MRSVFGNode::toString() const { std::string str; - std::stringstream rawstr(str); + raw_string_ostream rawstr(str); rawstr << "MRSVFGNode ID: " << getId(); return rawstr.str(); } -const std::string FormalINSVFGNode::toString() const -{ +const std::string FormalINSVFGNode::toString() const { std::string str; - std::stringstream rawstr(str); + raw_string_ostream rawstr(str); rawstr << "FormalINSVFGNode ID: " << getId() << " {fun: " << getFun()->getName() << "}"; rawstr << getMRVer()->getMR()->getMRID() << "V_" << getMRVer()->getSSAVersion() << - " = ENCHI(MR_" << getMRVer()->getMR()->getMRID() << "V_" << getMRVer()->getSSAVersion() << ")\n"; + " = ENCHI(MR_" << getMRVer()->getMR()->getMRID() << "V_" << getMRVer()->getSSAVersion() << ")\n"; rawstr << getMRVer()->getMR()->dumpStr() << "\n"; return rawstr.str(); } -const std::string FormalOUTSVFGNode::toString() const -{ +const std::string FormalOUTSVFGNode::toString() const { std::string str; - std::stringstream rawstr(str); + raw_string_ostream rawstr(str); rawstr << "FormalOUTSVFGNode ID: " << getId() << " {fun: " << getFun()->getName() << "}"; rawstr << "RETMU(" << getMRVer()->getMR()->getMRID() << "V_" << getMRVer()->getSSAVersion() << ")\n"; - rawstr << getMRVer()->getMR()->dumpStr() << "\n"; + rawstr << getMRVer()->getMR()->dumpStr() << "\n"; return rawstr.str(); } -const std::string ActualINSVFGNode::toString() const -{ +const std::string ActualINSVFGNode::toString() const { std::string str; - std::stringstream rawstr(str); - rawstr << "ActualINSVFGNode ID: " << getId() << " at callsite: " << getCallSite()->getCallSite()->toString() << " {fun: " << getFun()->getName() << "}"; + raw_string_ostream rawstr(str); + rawstr << "ActualINSVFGNode ID: " << getId() << " at callsite: " << *getCallSite()->getCallSite() << " {fun: " << getFun()->getName() << "}"; rawstr << "CSMU(" << getMRVer()->getMR()->getMRID() << "V_" << getMRVer()->getSSAVersion() << ")\n"; - rawstr << getMRVer()->getMR()->dumpStr() << "\n"; - rawstr << "CS[" << getCallSite()->getCallSite()->getSourceLoc() << "]"; + rawstr << getMRVer()->getMR()->dumpStr() << "\n"; + rawstr << "CS[" << getSourceLoc(getCallSite()->getCallSite()) << "]"; return rawstr.str(); } -const std::string ActualOUTSVFGNode::toString() const -{ +const std::string ActualOUTSVFGNode::toString() const { std::string str; - std::stringstream rawstr(str); - rawstr << "ActualOUTSVFGNode ID: " << getId() << " at callsite: " << getCallSite()->getCallSite()->toString() << " {fun: " << getFun()->getName() << "}"; + raw_string_ostream rawstr(str); + rawstr << "ActualOUTSVFGNode ID: " << getId() << " at callsite: " << *getCallSite()->getCallSite() << " {fun: " << getFun()->getName() << "}"; rawstr << getMRVer()->getMR()->getMRID() << "V_" << getMRVer()->getSSAVersion() << " = CSCHI(MR_" << getMRVer()->getMR()->getMRID() << "V_" << getMRVer()->getSSAVersion() << ")\n"; rawstr << getMRVer()->getMR()->dumpStr() << "\n"; - rawstr << "CS[" << getCallSite()->getCallSite()->getSourceLoc() << "]" ; + rawstr << "CS[" << getSourceLoc(getCallSite()->getCallSite()) << "]" ; return rawstr.str(); } -const std::string MSSAPHISVFGNode::toString() const -{ +const std::string MSSAPHISVFGNode::toString() const { std::string str; - std::stringstream rawstr(str); + raw_string_ostream rawstr(str); rawstr << "MSSAPHISVFGNode ID: " << getId() << " {fun: " << getFun()->getName() << "}"; rawstr << "MR_" << getResVer()->getMR()->getMRID() << "V_" << getResVer()->getSSAVersion() << " = PHI("; @@ -111,93 +99,99 @@ const std::string MSSAPHISVFGNode::toString() const rawstr << ")\n"; rawstr << getResVer()->getMR()->dumpStr(); - rawstr << getICFGNode()->getBB()->back()->getSourceLoc(); + rawstr << getSourceLoc(&getICFGNode()->getBB()->back()); return rawstr.str(); } -const std::string IntraMSSAPHISVFGNode::toString() const -{ +const std::string IntraMSSAPHISVFGNode::toString() const { std::string str; - std::stringstream rawstr(str); + raw_string_ostream rawstr(str); rawstr << "IntraMSSAPHISVFGNode ID: " << getId() << " {fun: " << getFun()->getName() << "}"; rawstr << MSSAPHISVFGNode::toString(); return rawstr.str(); } -const NodeBS DummyVersionPropSVFGNode::getDefSVFVars() const -{ - NodeBS nb; - nb.set(object); - return nb; -} - -const std::string InterMSSAPHISVFGNode::toString() const -{ +const std::string InterMSSAPHISVFGNode::toString() const { std::string str; - std::stringstream rawstr(str); + raw_string_ostream rawstr(str); if(isFormalINPHI()) rawstr << "FormalINPHISVFGNode ID: " << getId() << " {fun: " << getFun()->getName() << "}"; else - rawstr << "ActualOUTPHISVFGNode ID: " << getId() << " at callsite: " << getCallSite()->getCallSite()->toString() << " {fun: " << getFun()->getName() << "}"; + rawstr << "ActualOUTPHISVFGNode ID: " << getId() << " at callsite: " << *getCallSite()->getCallSite() << " {fun: " << getFun()->getName() << "}"; rawstr << MSSAPHISVFGNode::toString(); return rawstr.str(); } -const std::string IndirectSVFGEdge::toString() const -{ +const std::string IndirectSVFGEdge::toString() const { std::string str; - std::stringstream rawstr(str); + raw_string_ostream rawstr(str); rawstr << "IndirectSVFGEdge: " << getDstID() << "<--" << getSrcID() << "\n"; return rawstr.str(); } -const std::string IntraIndSVFGEdge::toString() const -{ +const std::string IntraIndSVFGEdge::toString() const { std::string str; - std::stringstream rawstr(str); + raw_string_ostream rawstr(str); rawstr << "IntraIndSVFGEdge: " << getDstID() << "<--" << getSrcID() << "\n"; return rawstr.str(); } -const std::string CallIndSVFGEdge::toString() const -{ +const std::string CallIndSVFGEdge::toString() const { std::string str; - std::stringstream rawstr(str); + raw_string_ostream rawstr(str); rawstr << "CallIndSVFGEdge CallSite ID: " << getCallSiteId() << " "; rawstr << getDstID() << "<--" << getSrcID() << "\n"; return rawstr.str(); } -const std::string RetIndSVFGEdge::toString() const -{ +const std::string RetIndSVFGEdge::toString() const { std::string str; - std::stringstream rawstr(str); + raw_string_ostream rawstr(str); rawstr << "RetIndSVFGEdge CallSite ID: " << getCallSiteId() << " "; rawstr << getDstID() << "<--" << getSrcID() << "\n"; return rawstr.str(); } -const std::string ThreadMHPIndSVFGEdge::toString() const -{ +const std::string ThreadMHPIndSVFGEdge::toString() const { std::string str; - std::stringstream rawstr(str); + raw_string_ostream rawstr(str); rawstr << "ThreadMHPIndSVFGEdge: " << getDstID() << "<--" << getSrcID() << "\n"; return rawstr.str(); } +const Value* StmtSVFGNode::getValue() const { + return getPAGEdge()->getValue(); +} + +const Value* CmpVFGNode::getValue() const { + return getRes()->getValue(); +} + +const Value* BinaryOPVFGNode::getValue() const { + return getRes()->getValue(); +} + +const Value* PHIVFGNode::getValue() const { + return getRes()->getValue(); +} + +const Value* ArgumentVFGNode::getValue() const { + return param->getValue(); +} + -FormalOUTSVFGNode::FormalOUTSVFGNode(NodeID id, const MRVer* mrVer, const FunExitICFGNode* funExit): MRSVFGNode(id, FPOUT) +FormalOUTSVFGNode::FormalOUTSVFGNode(NodeID id, const MRVer* mrVer, const FunExitBlockNode* funExit): MRSVFGNode(id, FPOUT) { cpts = mrVer->getMR()->getPointsTo(); ver = mrVer; - funExitNode = funExit; + funExitNode = funExit; } /*! * Constructor */ -SVFG::SVFG(std::unique_ptr mssa, VFGK k): VFG(mssa->getPTA()->getPTACallGraph(),k),mssa(std::move(mssa)), pta(this->mssa->getPTA()) +SVFG::SVFG(MemSSA* _mssa, VFGK k): VFG(_mssa->getPTA()->getPTACallGraph(),k),mssa(_mssa), pta(mssa->getPTA()) { stat = new SVFGStat(this); } @@ -226,23 +220,19 @@ void SVFG::buildSVFG() DBOUT(DGENERAL, outs() << pasMsg("Build Sparse Value-Flow Graph \n")); stat->startClk(); - if (!Options::ReadSVFG().empty()) - { - readFile(Options::ReadSVFG()); - } - else - { - DBOUT(DGENERAL, outs() << pasMsg("\tCreate SVFG Addr-taken Node\n")); - stat->ATVFNodeStart(); - addSVFGNodesForAddrTakenVars(); - stat->ATVFNodeEnd(); - DBOUT(DGENERAL, outs() << pasMsg("\tCreate SVFG Indirect Edge\n")); - stat->indVFEdgeStart(); - connectIndirectSVFGEdges(); - stat->indVFEdgeEnd(); - if (!Options::WriteSVFG().empty()) - writeToFile(Options::WriteSVFG()); - } + + DBOUT(DGENERAL, outs() << pasMsg("\tCreate SVFG Addr-taken Node\n")); + + stat->ATVFNodeStart(); + addSVFGNodesForAddrTakenVars(); + stat->ATVFNodeEnd(); + + DBOUT(DGENERAL, outs() << pasMsg("\tCreate SVFG Indirect Edge\n")); + + stat->indVFEdgeStart(); + connectIndirectSVFGEdges(); + stat->indVFEdgeEnd(); + } /* @@ -252,11 +242,11 @@ void SVFG::addSVFGNodesForAddrTakenVars() { // set defs for address-taken vars defined at store statements - SVFStmt::SVFStmtSetTy& stores = getPAGEdgeSet(SVFStmt::Store); - for (SVFStmt::SVFStmtSetTy::iterator iter = stores.begin(), eiter = + PAGEdge::PAGEdgeSetTy& stores = getPAGEdgeSet(PAGEdge::Store); + for (PAGEdge::PAGEdgeSetTy::iterator iter = stores.begin(), eiter = stores.end(); iter != eiter; ++iter) { - StoreStmt* store = SVFUtil::cast(*iter); + StorePE* store = SVFUtil::cast(*iter); const StmtSVFGNode* sNode = getStmtVFGNode(store); for(CHISet::iterator pi = mssa->getCHISet(store).begin(), epi = mssa->getCHISet(store).end(); pi!=epi; ++pi) setDef((*pi)->getResVer(),sNode); @@ -268,31 +258,27 @@ void SVFG::addSVFGNodesForAddrTakenVars() for(MemSSA::BBToPhiSetMap::iterator it = mssa->getBBToPhiSetMap().begin(), eit = mssa->getBBToPhiSetMap().end(); it!=eit; ++it) { - for(PHISet::iterator pi = it->second.begin(), epi = it->second.end(); pi!=epi; ++pi) - { + for(PHISet::iterator pi = it->second.begin(), epi = it->second.end(); pi!=epi; ++pi){ MemSSA::PHI* phi = *pi; - const SVFInstruction* inst = phi->getBasicBlock()->front(); - addIntraMSSAPHISVFGNode(pag->getICFG()->getICFGNode(inst), phi->opVerBegin(), phi->opVerEnd(),phi->getResVer(), totalVFGNode++); + addIntraMSSAPHISVFGNode(pag->getICFG()->getBlockICFGNode(&(phi->getBasicBlock()->front())), phi->opVerBegin(), phi->opVerEnd(),phi->getResVer(), totalVFGNode++); } } /// initialize memory SSA entry chi nodes for(MemSSA::FunToEntryChiSetMap::iterator it = mssa->getFunToEntryChiSetMap().begin(), eit = mssa->getFunToEntryChiSetMap().end(); it!=eit; ++it) { - for(CHISet::iterator pi = it->second.begin(), epi = it->second.end(); pi!=epi; ++pi) - { + for(CHISet::iterator pi = it->second.begin(), epi = it->second.end(); pi!=epi; ++pi){ const MemSSA::ENTRYCHI* chi = SVFUtil::cast(*pi); - addFormalINSVFGNode(pag->getICFG()->getFunEntryICFGNode(chi->getFunction()), chi->getResVer(), totalVFGNode++); + addFormalINSVFGNode(pag->getICFG()->getFunEntryBlockNode(chi->getFunction()), chi->getResVer(), totalVFGNode++); } } /// initialize memory SSA return mu nodes for(MemSSA::FunToReturnMuSetMap::iterator it = mssa->getFunToRetMuSetMap().begin(), eit = mssa->getFunToRetMuSetMap().end(); it!=eit; ++it) { - for(MUSet::iterator pi = it->second.begin(), epi = it->second.end(); pi!=epi; ++pi) - { - const MemSSA::RETMU* mu = SVFUtil::cast(*pi); - addFormalOUTSVFGNode(pag->getICFG()->getFunExitICFGNode(mu->getFunction()), mu->getMRVer(), totalVFGNode++); + for(MUSet::iterator pi = it->second.begin(), epi = it->second.end(); pi!=epi; ++pi){ + const MemSSA::RETMU* mu = SVFUtil::cast(*pi); + addFormalOUTSVFGNode(pag->getICFG()->getFunExitBlockNode(mu->getFunction()), mu->getMRVer(), totalVFGNode++); } } /// initialize memory SSA callsite mu nodes @@ -300,8 +286,7 @@ void SVFG::addSVFGNodesForAddrTakenVars() eit = mssa->getCallSiteToMuSetMap().end(); it!=eit; ++it) { - for(MUSet::iterator pi = it->second.begin(), epi = it->second.end(); pi!=epi; ++pi) - { + for(MUSet::iterator pi = it->second.begin(), epi = it->second.end(); pi!=epi; ++pi){ const MemSSA::CALLMU* mu = SVFUtil::cast(*pi); addActualINSVFGNode(mu->getCallSite(), mu->getMRVer(), totalVFGNode++); } @@ -311,8 +296,7 @@ void SVFG::addSVFGNodesForAddrTakenVars() eit = mssa->getCallSiteToChiSetMap().end(); it!=eit; ++it) { - for(CHISet::iterator pi = it->second.begin(), epi = it->second.end(); pi!=epi; ++pi) - { + for(CHISet::iterator pi = it->second.begin(), epi = it->second.end(); pi!=epi; ++pi){ const MemSSA::CALLCHI* chi = SVFUtil::cast(*pi); addActualOUTSVFGNode(chi->getCallSite(), chi->getResVer(), totalVFGNode++); } @@ -332,7 +316,7 @@ void SVFG::connectIndirectSVFGEdges() const SVFGNode* node = it->second; if(const LoadSVFGNode* loadNode = SVFUtil::dyn_cast(node)) { - MUSet& muSet = mssa->getMUSet(SVFUtil::cast(loadNode->getPAGEdge())); + MUSet& muSet = mssa->getMUSet(SVFUtil::cast(loadNode->getPAGEdge())); for(MUSet::iterator it = muSet.begin(), eit = muSet.end(); it!=eit; ++it) { if(LOADMU* mu = SVFUtil::dyn_cast(*it)) @@ -344,7 +328,7 @@ void SVFG::connectIndirectSVFGEdges() } else if(const StoreSVFGNode* storeNode = SVFUtil::dyn_cast(node)) { - CHISet& chiSet = mssa->getCHISet(SVFUtil::cast(storeNode->getPAGEdge())); + CHISet& chiSet = mssa->getCHISet(SVFUtil::cast(storeNode->getPAGEdge())); for(CHISet::iterator it = chiSet.begin(), eit = chiSet.end(); it!=eit; ++it) { if(STORECHI* chi = SVFUtil::dyn_cast(*it)) @@ -360,7 +344,7 @@ void SVFG::connectIndirectSVFGEdges() mssa->getPTA()->getPTACallGraph()->getDirCallSitesInvokingCallee(formalIn->getFun(),callInstSet); for(PTACallGraphEdge::CallInstSet::iterator it = callInstSet.begin(), eit = callInstSet.end(); it!=eit; ++it) { - const CallICFGNode* cs = *it; + const CallBlockNode* cs = *it; if(!mssa->hasMU(cs)) continue; ActualINSVFGNodeSet& actualIns = getActualINSVFGNodes(cs); @@ -378,7 +362,7 @@ void SVFG::connectIndirectSVFGEdges() mssa->getPTA()->getPTACallGraph()->getDirCallSitesInvokingCallee(formalOut->getFun(),callInstSet); for(PTACallGraphEdge::CallInstSet::iterator it = callInstSet.begin(), eit = callInstSet.end(); it!=eit; ++it) { - const CallICFGNode* cs = *it; + const CallBlockNode* cs = *it; if(!mssa->hasCHI(cs)) continue; ActualOUTSVFGNodeSet& actualOuts = getActualOUTSVFGNodes(cs); @@ -436,13 +420,14 @@ void SVFG::connectFromGlobalToProgEntry() if (const StoreSVFGNode* store = SVFUtil::dyn_cast(*storeIt)) { /// connect this store to main function entry - const NodeBS& storePts = mssa->getPTA()->getPts(store->getPAGDstNodeID()).toNodeBS(); + const PointsTo& storePts = mssa->getPTA()->getPts( + store->getPAGDstNodeID()); - for (FormalINSVFGNodeSet::iterator fiIt = formalIns.begin(), fiEit = + for (NodeBS::iterator fiIt = formalIns.begin(), fiEit = formalIns.end(); fiIt != fiEit; ++fiIt) { NodeID formalInID = *fiIt; - NodeBS formalInPts = ((FormalINSVFGNode*) getSVFGNode(formalInID))->getPointsTo(); + PointsTo formalInPts = ((FormalINSVFGNode*) getSVFGNode(formalInID))->getPointsTo(); formalInPts &= storePts; if (formalInPts.empty()) @@ -458,7 +443,7 @@ void SVFG::connectFromGlobalToProgEntry() /* * Add def-use edges of a memory region between two statements */ -SVFGEdge* SVFG::addIntraIndirectVFEdge(NodeID srcId, NodeID dstId, const NodeBS& cpts) +SVFGEdge* SVFG::addIntraIndirectVFEdge(NodeID srcId, NodeID dstId, const PointsTo& cpts) { SVFGNode* srcNode = getSVFGNode(srcId); SVFGNode* dstNode = getSVFGNode(dstId); @@ -480,7 +465,7 @@ SVFGEdge* SVFG::addIntraIndirectVFEdge(NodeID srcId, NodeID dstId, const NodeBS& /*! * Add def-use edges of a memory region between two may-happen-in-parallel statements for multithreaded program */ -SVFGEdge* SVFG::addThreadMHPIndirectVFEdge(NodeID srcId, NodeID dstId, const NodeBS& cpts) +SVFGEdge* SVFG::addThreadMHPIndirectVFEdge(NodeID srcId, NodeID dstId, const PointsTo& cpts) { SVFGNode* srcNode = getSVFGNode(srcId); SVFGNode* dstNode = getSVFGNode(dstId); @@ -500,7 +485,7 @@ SVFGEdge* SVFG::addThreadMHPIndirectVFEdge(NodeID srcId, NodeID dstId, const Nod /* * Add def-use call edges of a memory region between two statements */ -SVFGEdge* SVFG::addCallIndirectVFEdge(NodeID srcId, NodeID dstId, const NodeBS& cpts,CallSiteID csId) +SVFGEdge* SVFG::addCallIndirectVFEdge(NodeID srcId, NodeID dstId, const PointsTo& cpts,CallSiteID csId) { SVFGNode* srcNode = getSVFGNode(srcId); SVFGNode* dstNode = getSVFGNode(dstId); @@ -520,7 +505,7 @@ SVFGEdge* SVFG::addCallIndirectVFEdge(NodeID srcId, NodeID dstId, const NodeBS& /* * Add def-use return edges of a memory region between two statements */ -SVFGEdge* SVFG::addRetIndirectVFEdge(NodeID srcId, NodeID dstId, const NodeBS& cpts,CallSiteID csId) +SVFGEdge* SVFG::addRetIndirectVFEdge(NodeID srcId, NodeID dstId, const PointsTo& cpts,CallSiteID csId) { SVFGNode* srcNode = getSVFGNode(srcId); SVFGNode* dstNode = getSVFGNode(dstId); @@ -542,8 +527,8 @@ SVFGEdge* SVFG::addRetIndirectVFEdge(NodeID srcId, NodeID dstId, const NodeBS& c */ SVFGEdge* SVFG::addInterIndirectVFCallEdge(const ActualINSVFGNode* src, const FormalINSVFGNode* dst,CallSiteID csId) { - NodeBS cpts1 = src->getPointsTo(); - NodeBS cpts2 = dst->getPointsTo(); + PointsTo cpts1 = src->getPointsTo(); + PointsTo cpts2 = dst->getPointsTo(); if(cpts1.intersects(cpts2)) { cpts1 &= cpts2; @@ -558,8 +543,8 @@ SVFGEdge* SVFG::addInterIndirectVFCallEdge(const ActualINSVFGNode* src, const Fo SVFGEdge* SVFG::addInterIndirectVFRetEdge(const FormalOUTSVFGNode* src, const ActualOUTSVFGNode* dst,CallSiteID csId) { - NodeBS cpts1 = src->getPointsTo(); - NodeBS cpts2 = dst->getPointsTo(); + PointsTo cpts1 = src->getPointsTo(); + PointsTo cpts2 = dst->getPointsTo(); if(cpts1.intersects(cpts2)) { cpts1 &= cpts2; @@ -576,58 +561,77 @@ void SVFG::dump(const std::string& file, bool simple) GraphPrinter::WriteGraphToFile(outs(), file, this, simple); } +std::set SVFG::fromValue(const llvm::Value* value) const +{ + PAG* pag = PAG::getPAG(); + std::set ret; + // search for all PAGEdges first + for (const PAGEdge* pagEdge : pag->getValueEdges(value)) { + PAGEdgeToStmtVFGNodeMapTy::const_iterator it = PAGEdgeToStmtVFGNodeMap.find(pagEdge); + if (it != PAGEdgeToStmtVFGNodeMap.end()) { + ret.emplace(it->second); + } + } + // add all PAGNodes + PAGNode* pagNode = pag->getPAGNode(pag->getValueNode(value)); + if(hasDef(pagNode)) { + ret.emplace(getDefSVFGNode(pagNode)); + } + return ret; +} + /** * Get all inter value flow edges at this indirect call site, including call and return edges. */ -void SVFG::getInterVFEdgesForIndirectCallSite(const CallICFGNode* callICFGNode, const SVFFunction* callee, SVFGEdgeSetTy& edges) +void SVFG::getInterVFEdgesForIndirectCallSite(const CallBlockNode* callBlockNode, const SVFFunction* callee, SVFGEdgeSetTy& edges) { - CallSiteID csId = getCallSiteID(callICFGNode, callee); - RetICFGNode* retICFGNode = pag->getICFG()->getRetICFGNode(callICFGNode->getCallSite()); + CallSiteID csId = getCallSiteID(callBlockNode, callee); + RetBlockNode* retBlockNode = pag->getICFG()->getRetBlockNode(callBlockNode->getCallSite()); // Find inter direct call edges between actual param and formal param. - if (pag->hasCallSiteArgsMap(callICFGNode) && pag->hasFunArgsList(callee)) + if (pag->hasCallSiteArgsMap(callBlockNode) && pag->hasFunArgsList(callee)) { - const SVFIR::SVFVarList& csArgList = pag->getCallSiteArgsList(callICFGNode); - const SVFIR::SVFVarList& funArgList = pag->getFunArgsList(callee); - SVFIR::SVFVarList::const_iterator csArgIt = csArgList.begin(), csArgEit = csArgList.end(); - SVFIR::SVFVarList::const_iterator funArgIt = funArgList.begin(), funArgEit = funArgList.end(); + const PAG::PAGNodeList& csArgList = pag->getCallSiteArgsList(callBlockNode); + const PAG::PAGNodeList& funArgList = pag->getFunArgsList(callee); + PAG::PAGNodeList::const_iterator csArgIt = csArgList.begin(), csArgEit = csArgList.end(); + PAG::PAGNodeList::const_iterator funArgIt = funArgList.begin(), funArgEit = funArgList.end(); for (; funArgIt != funArgEit && csArgIt != csArgEit; funArgIt++, csArgIt++) { const PAGNode *cs_arg = *csArgIt; const PAGNode *fun_arg = *funArgIt; - if (isInterestedPAGNode(fun_arg) && isInterestedPAGNode(cs_arg)) - getInterVFEdgeAtIndCSFromAPToFP(cs_arg, fun_arg, callICFGNode, csId, edges); + if (fun_arg->isPointer() && cs_arg->isPointer()) + getInterVFEdgeAtIndCSFromAPToFP(cs_arg, fun_arg, callBlockNode, csId, edges); } assert(funArgIt == funArgEit && "function has more arguments than call site"); - if (callee->isVarArg()) + if (callee->getLLVMFun()->isVarArg()) { NodeID varFunArg = pag->getVarargNode(callee); - const PAGNode* varFunArgNode = pag->getGNode(varFunArg); - if (isInterestedPAGNode(varFunArgNode)) + const PAGNode* varFunArgNode = pag->getPAGNode(varFunArg); + if (varFunArgNode->isPointer()) { for (; csArgIt != csArgEit; csArgIt++) { const PAGNode *cs_arg = *csArgIt; - if (isInterestedPAGNode(cs_arg)) - getInterVFEdgeAtIndCSFromAPToFP(cs_arg, varFunArgNode, callICFGNode, csId, edges); + if (cs_arg->isPointer()) + getInterVFEdgeAtIndCSFromAPToFP(cs_arg, varFunArgNode, callBlockNode, csId, edges); } } } } // Find inter direct return edges between actual return and formal return. - if (pag->funHasRet(callee) && pag->callsiteHasRet(retICFGNode)) + if (pag->funHasRet(callee) && pag->callsiteHasRet(retBlockNode)) { - const PAGNode* cs_return = pag->getCallSiteRet(retICFGNode); + const PAGNode* cs_return = pag->getCallSiteRet(retBlockNode); const PAGNode* fun_return = pag->getFunRet(callee); - if (isInterestedPAGNode(cs_return) && isInterestedPAGNode(fun_return)) + if (cs_return->isPointer() && fun_return->isPointer()) getInterVFEdgeAtIndCSFromFRToAR(fun_return, cs_return, csId, edges); } // Find inter indirect call edges between actual-in and formal-in svfg nodes. - if (hasFuncEntryChi(callee) && hasCallSiteMu(callICFGNode)) + if (hasFuncEntryChi(callee) && hasCallSiteMu(callBlockNode)) { - SVFG::ActualINSVFGNodeSet& actualInNodes = getActualINSVFGNodes(callICFGNode); + SVFG::ActualINSVFGNodeSet& actualInNodes = getActualINSVFGNodes(callBlockNode); for(SVFG::ActualINSVFGNodeSet::iterator ai_it = actualInNodes.begin(), ai_eit = actualInNodes.end(); ai_it!=ai_eit; ++ai_it) { @@ -637,9 +641,9 @@ void SVFG::getInterVFEdgesForIndirectCallSite(const CallICFGNode* callICFGNode, } // Find inter indirect return edges between actual-out and formal-out svfg nodes. - if (hasFuncRetMu(callee) && hasCallSiteChi(callICFGNode)) + if (hasFuncRetMu(callee) && hasCallSiteChi(callBlockNode)) { - SVFG::ActualOUTSVFGNodeSet& actualOutNodes = getActualOUTSVFGNodes(callICFGNode); + SVFG::ActualOUTSVFGNodeSet& actualOutNodes = getActualOUTSVFGNodes(callBlockNode); for(SVFG::ActualOUTSVFGNodeSet::iterator ao_it = actualOutNodes.begin(), ao_eit = actualOutNodes.end(); ao_it!=ao_eit; ++ao_it) { @@ -653,7 +657,7 @@ void SVFG::getInterVFEdgesForIndirectCallSite(const CallICFGNode* callICFGNode, * Connect actual params/return to formal params/return for top-level variables. * Also connect indirect actual in/out and formal in/out. */ -void SVFG::connectCallerAndCallee(const CallICFGNode* cs, const SVFFunction* callee, SVFGEdgeSetTy& edges) +void SVFG::connectCallerAndCallee(const CallBlockNode* cs, const SVFFunction* callee, SVFGEdgeSetTy& edges) { VFG::connectCallerAndCallee(cs,callee,edges); @@ -719,7 +723,7 @@ const SVFFunction* SVFG::isFunEntrySVFGNode(const SVFGNode* node) const else if(const InterMSSAPHISVFGNode* mphi = SVFUtil::dyn_cast(node)) { if(mphi->isFormalINPHI()) - return mphi->getFun(); + return phi->getFun(); } return nullptr; } @@ -727,7 +731,7 @@ const SVFFunction* SVFG::isFunEntrySVFGNode(const SVFGNode* node) const /*! * Whether this is an callsite return SVFGNode (actual return, actual out) */ -const CallICFGNode* SVFG::isCallSiteRetSVFGNode(const SVFGNode* node) const +const CallBlockNode* SVFG::isCallSiteRetSVFGNode(const SVFGNode* node) const { if(const ActualRetSVFGNode* ar = SVFUtil::dyn_cast(node)) { @@ -761,15 +765,15 @@ void SVFG::performStat() /*! * GraphTraits specialization */ -namespace SVF +namespace llvm { template<> -struct DOTGraphTraits : public DOTGraphTraits +struct DOTGraphTraits : public DOTGraphTraits { typedef SVFGNode NodeType; DOTGraphTraits(bool isSimple = false) : - DOTGraphTraits(isSimple) + DOTGraphTraits(isSimple) { } @@ -781,10 +785,12 @@ struct DOTGraphTraits : public DOTGraphTraits /// isNodeHidden - If the function returns true, the given node is not /// displayed in the graph - static bool isNodeHidden(SVFGNode *node, SVFG *) - { - if (Options::ShowHiddenNode()) return false; - else return node->getInEdges().empty() && node->getOutEdges().empty(); +#if LLVM_VERSION_MAJOR >= 12 + static bool isNodeHidden(SVFGNode *node, SVFG*) { +#else + static bool isNodeHidden(SVFGNode *node) { +#endif + return node->getInEdges().empty() && node->getOutEdges().empty(); } std::string getNodeLabel(NodeType *node, SVFG *graph) @@ -799,7 +805,7 @@ struct DOTGraphTraits : public DOTGraphTraits static std::string getSimpleNodeLabel(NodeType *node, SVFG*) { std::string str; - std::stringstream rawstr(str); + raw_string_ostream rawstr(str); if(StmtSVFGNode* stmtNode = SVFUtil::dyn_cast(node)) { rawstr << stmtNode->toString(); @@ -871,7 +877,7 @@ struct DOTGraphTraits : public DOTGraphTraits { std::string str; - std::stringstream rawstr(str); + raw_string_ostream rawstr(str); if(StmtSVFGNode* stmtNode = SVFUtil::dyn_cast(node)) { rawstr << stmtNode->toString(); @@ -932,10 +938,6 @@ struct DOTGraphTraits : public DOTGraphTraits { rawstr << fr->toString(); } - else if (BranchVFGNode* br = SVFUtil::dyn_cast(node)) - { - rawstr << br->toString(); - } else assert(false && "what else kinds of nodes do we have??"); @@ -945,16 +947,16 @@ struct DOTGraphTraits : public DOTGraphTraits static std::string getNodeAttributes(NodeType *node, SVFG *graph) { std::string str; - std::stringstream rawstr(str); + raw_string_ostream rawstr(str); if(StmtSVFGNode* stmtNode = SVFUtil::dyn_cast(node)) { const PAGEdge* edge = stmtNode->getPAGEdge(); - if (SVFUtil::isa(edge)) + if (SVFUtil::isa(edge)) { rawstr << "color=green"; } - else if (SVFUtil::isa(edge)) + else if (SVFUtil::isa(edge)) { rawstr << "color=black"; } @@ -962,15 +964,15 @@ struct DOTGraphTraits : public DOTGraphTraits { rawstr << "color=black,style=dotted"; } - else if (SVFUtil::isa(edge)) + else if (SVFUtil::isa(edge)) { rawstr << "color=purple"; } - else if (SVFUtil::isa(edge)) + else if (SVFUtil::isa(edge)) { rawstr << "color=blue"; } - else if (SVFUtil::isa(edge)) + else if (SVFUtil::isa(edge)) { rawstr << "color=red"; } @@ -1036,10 +1038,6 @@ struct DOTGraphTraits : public DOTGraphTraits { rawstr << "color=black,penwidth=2"; } - else if (SVFUtil::isa(node)) - { - rawstr << "color=gold,penwidth=2"; - } else assert(false && "no such kind of node!!"); @@ -1097,7 +1095,7 @@ struct DOTGraphTraits : public DOTGraphTraits assert(edge && "No edge found!!"); std::string str; - std::stringstream rawstr(str); + raw_string_ostream rawstr(str); if (CallDirSVFGEdge* dirCall = SVFUtil::dyn_cast(edge)) rawstr << dirCall->getCallSiteId(); else if (RetDirSVFGEdge* dirRet = SVFUtil::dyn_cast(edge)) diff --git a/svf/lib/Graphs/SVFGOPT.cpp b/svf/lib/Graphs/SVFGOPT.cpp index 0fb9e1df7..88b6d6633 100644 --- a/svf/lib/Graphs/SVFGOPT.cpp +++ b/svf/lib/Graphs/SVFGOPT.cpp @@ -6,16 +6,16 @@ // // This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU Affero General Public License as published by +// it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Affero General Public License for more details. +// GNU General Public License for more details. -// You should have received a copy of the GNU Affero General Public License +// You should have received a copy of the GNU General Public License // along with this program. If not, see . // //===----------------------------------------------------------------------===// @@ -48,12 +48,12 @@ void SVFGOPT::buildSVFG() { SVFG::buildSVFG(); - if(Options::DumpVFG()) + if(Options::DumpVFG) dump("SVFG_before_opt"); DBOUT(DGENERAL, outs() << SVFUtil::pasMsg("\tSVFG Optimisation\n")); - keepActualOutFormalIn = Options::KeepAOFI(); + keepActualOutFormalIn = Options::KeepAOFI; stat->sfvgOptStart(); handleInterValueFlow(); @@ -65,9 +65,9 @@ void SVFGOPT::buildSVFG() /*! * */ -SVFGEdge* SVFGOPT::addCallIndirectSVFGEdge(NodeID srcId, NodeID dstId, CallSiteID csid, const NodeBS& cpts) +SVFGEdge* SVFGOPT::addCallIndirectSVFGEdge(NodeID srcId, NodeID dstId, CallSiteID csid, const PointsTo& cpts) { - if (Options::ContextInsensitive()) + if (Options::ContextInsensitive) return addIntraIndirectVFEdge(srcId, dstId, cpts); else return addCallIndirectVFEdge(srcId, dstId, cpts, csid); @@ -76,9 +76,9 @@ SVFGEdge* SVFGOPT::addCallIndirectSVFGEdge(NodeID srcId, NodeID dstId, CallSiteI /*! * */ -SVFGEdge* SVFGOPT::addRetIndirectSVFGEdge(NodeID srcId, NodeID dstId, CallSiteID csid, const NodeBS& cpts) +SVFGEdge* SVFGOPT::addRetIndirectSVFGEdge(NodeID srcId, NodeID dstId, CallSiteID csid, const PointsTo& cpts) { - if (Options::ContextInsensitive()) + if (Options::ContextInsensitive) return addIntraIndirectVFEdge(srcId, dstId, cpts); else return addRetIndirectVFEdge(srcId, dstId, cpts, csid); @@ -94,9 +94,10 @@ void SVFGOPT::handleInterValueFlow() it!=eit; ++it) { SVFGNode* node = it->second; - if (SVFUtil::isa(node)) + if (SVFUtil::isa(node) || SVFUtil::isa(node) + || SVFUtil::isa(node) || SVFUtil::isa(node) + || SVFUtil::isa(node) || SVFUtil::isa(node) + || SVFUtil::isa(node) || SVFUtil::isa(node)) candidates.insert(node); } @@ -115,16 +116,16 @@ void SVFGOPT::handleInterValueFlow() replaceFParamARetWithPHI(addInterPHIForAR(ar), ar); nodesToBeDeleted.insert(ar); } - else if (SVFUtil::isa(node)) + else if (SVFUtil::isa(node) || SVFUtil::isa(node)) { nodesToBeDeleted.insert(node); } - else if (SVFUtil::isa(node)) + else if (SVFUtil::isa(node) || SVFUtil::isa(node)) { retargetEdgesOfAInFOut(node); nodesToBeDeleted.insert(node); } - else if (SVFUtil::isa(node)) + else if (SVFUtil::isa(node) || SVFUtil::isa(node)) { if(keepActualOutFormalIn == false) nodesToBeDeleted.insert(node); @@ -136,7 +137,7 @@ void SVFGOPT::handleInterValueFlow() SVFGNode* node = *it; if (canBeRemoved(node)) { - if (SVFUtil::isa(node)) + if (SVFUtil::isa(node) || SVFUtil::isa(node)) retargetEdgesOfAOutFIn(node); /// reset def of address-taken variable removeAllEdges(node); @@ -150,7 +151,7 @@ void SVFGOPT::handleInterValueFlow() */ void SVFGOPT::replaceFParamARetWithPHI(PHISVFGNode* phi, SVFGNode* svfgNode) { - assert((SVFUtil::isa(svfgNode)) + assert((SVFUtil::isa(svfgNode) || SVFUtil::isa(svfgNode)) && "expecting a formal param or actual ret svfg node"); /// create a new PHISVFGNode. @@ -206,7 +207,7 @@ void SVFGOPT::retargetEdgesOfAInFOut(SVFGNode* node) assert(node->getInEdges().size() == 1 && "actual-in/formal-out can only have one incoming edge as its def size"); SVFGNode* def = nullptr; - NodeBS inPointsTo; + PointsTo inPointsTo; SVFGNode::const_iterator it = node->InEdgeBegin(); SVFGNode::const_iterator eit = node->InEdgeEnd(); @@ -226,7 +227,7 @@ void SVFGOPT::retargetEdgesOfAInFOut(SVFGNode* node) for (; it != eit; ++it) { const IndirectSVFGEdge* outEdge = SVFUtil::cast(*it); - NodeBS intersection = inPointsTo; + PointsTo intersection = inPointsTo; intersection &= outEdge->getPointsTo(); if (intersection.empty()) @@ -262,7 +263,7 @@ void SVFGOPT::retargetEdgesOfAOutFIn(SVFGNode* node) { const IndirectSVFGEdge* outEdge = SVFUtil::cast(*outIt); - NodeBS intersection = inEdge->getPointsTo(); + PointsTo intersection = inEdge->getPointsTo(); intersection &= outEdge->getPointsTo(); if (intersection.empty()) continue; @@ -298,7 +299,7 @@ bool SVFGOPT::isConnectingTwoCallSites(const SVFGNode* node) const SVFGNode::const_iterator edgeEit = node->InEdgeEnd(); for (; edgeIt != edgeEit; ++edgeIt) { - if (SVFUtil::isa(*edgeIt)) + if (SVFUtil::isa(*edgeIt) || SVFUtil::isa(*edgeIt)) { hasInCallRet = true; break; @@ -309,7 +310,7 @@ bool SVFGOPT::isConnectingTwoCallSites(const SVFGNode* node) const edgeEit = node->OutEdgeEnd(); for (; edgeIt != edgeEit; ++edgeIt) { - if (SVFUtil::isa(*edgeIt)) + if (SVFUtil::isa(*edgeIt) || SVFUtil::isa(*edgeIt)) { hasOutCallRet = true; break; @@ -333,14 +334,15 @@ bool SVFGOPT::isConnectingTwoCallSites(const SVFGNode* node) const /// 5. FormalOUT if it doesn't reside at the exit of address-taken function bool SVFGOPT::canBeRemoved(const SVFGNode * node) { - if (SVFUtil::isa(node)) + if (SVFUtil::isa(node) || SVFUtil::isa(node) + || SVFUtil::isa(node) || SVFUtil::isa(node)) return true; - else if (SVFUtil::isa(node)) + else if (SVFUtil::isa(node) || SVFUtil::isa(node) + || SVFUtil::isa(node) || SVFUtil::isa(node) + || SVFUtil::isa(node)) { /// Now each SVFG edge can only be associated with one call site id, - /// so if this node has both incoming call/ret and outgoing call/ret + /// so if this node has both incoming call/ret and outgoting call/ret /// edges, we don't remove this node. if (isConnectingTwoCallSites(node)) return false; @@ -371,7 +373,7 @@ bool SVFGOPT::canBeRemoved(const SVFGNode * node) */ void SVFGOPT::parseSelfCycleHandleOption() { - std::string choice = (Options::SelfCycle().empty()) ? "" : Options::SelfCycle(); + std::string choice = (Options::SelfCycle.getValue().empty()) ? "" : Options::SelfCycle.getValue(); if (choice.empty() || choice == KeepAllSelfCycle) keepAllSelfCycle = true; else if (choice == KeepContextSelfCycle) @@ -438,7 +440,7 @@ void SVFGOPT::handleIntraValueFlow() /// 1. keepAllSelfCycle = TRUE: all self cycle edges are kept; /// 2. keepContextSelfCycle = TRUE: all self cycle edges related-to context are kept; /// 3. Otherwise, all self cycle edges are NOT kept. -/// Return TRUE if some self cycle edges remain in this node. +/// Return TRUE if some self cycle edges remaine in this node. bool SVFGOPT::checkSelfCycleEdges(const MSSAPHISVFGNode* node) { bool hasSelfCycle = false; @@ -458,7 +460,7 @@ bool SVFGOPT::checkSelfCycleEdges(const MSSAPHISVFGNode* node) break; /// There's no need to check other edge if we do not remove self cycle } else if (keepContextSelfCycle && - SVFUtil::isa(preEdge)) + (SVFUtil::isa(preEdge) || SVFUtil::isa(preEdge))) { hasSelfCycle = true; continue; /// Continue checking and remove other self cycle which are NOT context-related @@ -495,7 +497,7 @@ void SVFGOPT::bypassMSSAPHINode(const MSSAPHISVFGNode* node) const SVFGEdge* succEdge = *outEdgeIt; const SVFGNode* dstNode = (*outEdgeIt)->getDstNode(); if (srcNode->getId() != dstNode->getId() - && addNewSVFGEdge(srcNode->getId(), dstNode->getId(), preEdge, succEdge)) + && addNewSVFGEdge(srcNode->getId(), dstNode->getId(), preEdge, succEdge)) added = true; else { @@ -528,7 +530,7 @@ bool SVFGOPT::addNewSVFGEdge(NodeID srcId, NodeID dstId, const SVFGEdge* preEdge const IndirectSVFGEdge* preIndEdge = SVFUtil::cast(preEdge); const IndirectSVFGEdge* succIndEdge = SVFUtil::cast(succEdge); - NodeBS intersection = preIndEdge->getPointsTo(); + PointsTo intersection = preIndEdge->getPointsTo(); intersection &= succIndEdge->getPointsTo(); if (intersection.empty()) diff --git a/svf/lib/Graphs/SVFGReadWrite.cpp b/svf/lib/Graphs/SVFGReadWrite.cpp deleted file mode 100644 index fc15a8dd8..000000000 --- a/svf/lib/Graphs/SVFGReadWrite.cpp +++ /dev/null @@ -1,480 +0,0 @@ -//===- SVFGReadWrite.cpp -- SVFG read & write-----------------------------------// -// -// SVF: Static Value-Flow Analysis -// -// Copyright (C) <2013-2017> -// - -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU Affero General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Affero General Public License for more details. - -// You should have received a copy of the GNU Affero General Public License -// along with this program. If not, see . -// -//===----------------------------------------------------------------------===// - -/* - * SVFGReadWrite.cpp - * - * Created on: May 21, 2022 - * Author: Jeffrey Ma - */ - -#include "SVFIR/SVFModule.h" -#include "Util/SVFUtil.h" -#include "Graphs/SVFG.h" -#include "Graphs/SVFGStat.h" -#include "MemoryModel/PointerAnalysisImpl.h" -#include -#include "Util/Options.h" - -using namespace SVF; -using namespace SVFUtil; -using namespace std; - -// Format of file -// __Nodes__ -// SVFGNodeID: >= >= MVER: {MRVERID: MemRegion: pts{ } MRVERSION: MSSADef: , pts{ }} >= ICFGNodeID: -// __Edges__ -// srcSVFGNodeID: => dstSVFGNodeID: >= | MVER: {MRVERID: MemRegion: pts{ } MRVERSION: MSSADef: , pts{ }} -void SVFG::writeToFile(const string& filename) -{ - outs() << "Writing SVFG analysis to '" << filename << "'..."; - error_code err; - std::fstream f(filename.c_str(), std::ios_base::out); - if (!f.good()) - { - outs() << " error opening file for writing!\n"; - return; - } - f << "__Nodes__\n"; - // Iterate over nodes and write to file - for(iterator it = begin(), eit = end(); it!=eit; ++it) - { - NodeID nodeId = it->first; - const SVFGNode* node = it->second; - if(const FormalINSVFGNode* formalIn = SVFUtil::dyn_cast(node)) - { - //node - f << "SVFGNodeID: " << nodeId << " >= " << "FormalINSVFGNode"; - f << " >= MVER: {"; - f << *formalIn->getMRVer() << "} >= ICFGNodeID: " << formalIn->getFunEntryNode()->getId() << "\n"; - } - else if(const FormalOUTSVFGNode* formalOut = SVFUtil::dyn_cast(node)) - { - //node - f << "SVFGNodeID: " << nodeId << " >= " << "FormalOUTSVFGNode"; - f << " >= MVER: {"; - f << *formalOut->getMRVer() << "} >= ICFGNodeID: " << formalOut->getFunExitNode()->getId() << "\n"; - } - else if(const ActualINSVFGNode* actualIn = SVFUtil::dyn_cast(node)) - { - //node - f << "SVFGNodeID: " << nodeId << " >= " << "ActualINSVFGNode"; - f << " >= MVER: {"; - f << *actualIn->getMRVer() << "} >= ICFGNodeID: " << actualIn->getCallSite()->getId() << "\n"; - } - else if(const ActualOUTSVFGNode* actualOut = SVFUtil::dyn_cast(node)) - { - //node - f << "SVFGNodeID: " << nodeId << " >= " << "ActualOUTSVFGNode" << " >= MVER: {"; - f << *actualOut->getMRVer() << "} >= ICFGNodeID: " << actualOut->getCallSite()->getId() << "\n"; - } - else if(const MSSAPHISVFGNode* phiNode = SVFUtil::dyn_cast(node)) - { - //node - f << "SVFGNodeID: " << nodeId << " >= " << "PHISVFGNode"; - unordered_map opvers; - for (MemSSA::PHI::OPVers::const_iterator it = phiNode->opVerBegin(), eit = phiNode->opVerEnd(); - it != eit; it++) - { - opvers.insert(make_pair(it->first, it->second)); - } - // opvers - f << " >= MVER: {"; - f << *phiNode->getResVer(); - const SVFInstruction* inst = phiNode->getICFGNode()->getBB()->front(); - f << "} >= ICFGNodeID: " << pag->getICFG()->getICFGNode(inst)->getId(); - f << " >= OPVers: {"; - for (auto x: opvers) - { - const MRVer* op = x.second; - f << "{" << *op << "}" << ","; - } - f << "}\n"; - } - } - - f << "\n\n__Edges__\n"; - // Iterate over edges and write to file - for(iterator it = begin(), eit = end(); it!=eit; ++it) - { - NodeID nodeId = it->first; - const SVFGNode* node = it->second; - if(const LoadSVFGNode* loadNode = SVFUtil::dyn_cast(node)) - { - MUSet& muSet = mssa->getMUSet(SVFUtil::cast(loadNode->getPAGEdge())); - for(MUSet::iterator it = muSet.begin(), eit = muSet.end(); it!=eit; ++it) - { - if(LOADMU* mu = SVFUtil::dyn_cast(*it)) - { - NodeID def = getDef(mu->getMRVer()); - f << "srcSVFGNodeID: " << nodeId << " => " << "dstSVFGNodeID: " << def << " >= LoadNode | MVER: {" << *mu->getMRVer() << "}" << "\n"; - } - } - } - else if(const StoreSVFGNode* storeNode = SVFUtil::dyn_cast(node)) - { - CHISet& chiSet = mssa->getCHISet(SVFUtil::cast(storeNode->getPAGEdge())); - for(CHISet::iterator it = chiSet.begin(), eit = chiSet.end(); it!=eit; ++it) - { - if(STORECHI* chi = SVFUtil::dyn_cast(*it)) - { - NodeID def = getDef(chi->getOpVer()); - f << "srcSVFGNodeID: " << nodeId << " => " << "dstSVFGNodeID: " << def << " >= StoreNode | MVER: {" << *chi->getOpVer() << "}" << "\n"; - } - } - } - else if(const FormalINSVFGNode* formalIn = SVFUtil::dyn_cast(node)) - { - PTACallGraphEdge::CallInstSet callInstSet; - mssa->getPTA()->getPTACallGraph()->getDirCallSitesInvokingCallee(formalIn->getFun(),callInstSet); - for(PTACallGraphEdge::CallInstSet::iterator it = callInstSet.begin(), eit = callInstSet.end(); it!=eit; ++it) - { - const CallICFGNode* cs = *it; - if(!mssa->hasMU(cs)) - continue; - ActualINSVFGNodeSet& actualIns = getActualINSVFGNodes(cs); - for(ActualINSVFGNodeSet::iterator ait = actualIns.begin(), aeit = actualIns.end(); ait!=aeit; ++ait) - { - const ActualINSVFGNode* actualIn = SVFUtil::cast(getSVFGNode(*ait)); - f << "srcSVFGNodeID: " << nodeId << " => " << "dstSVFGNodeID: " << actualIn->getId() << " >= FormalINSVFGNode" << "\n"; - } - } - } - else if(const FormalOUTSVFGNode* formalOut = SVFUtil::dyn_cast(node)) - { - PTACallGraphEdge::CallInstSet callInstSet; - mssa->getPTA()->getPTACallGraph()->getDirCallSitesInvokingCallee(formalOut->getFun(),callInstSet); - for(PTACallGraphEdge::CallInstSet::iterator it = callInstSet.begin(), eit = callInstSet.end(); it!=eit; ++it) - { - const CallICFGNode* cs = *it; - if(!mssa->hasCHI(cs)) - continue; - ActualOUTSVFGNodeSet& actualOuts = getActualOUTSVFGNodes(cs); - for(ActualOUTSVFGNodeSet::iterator ait = actualOuts.begin(), aeit = actualOuts.end(); ait!=aeit; ++ait) - { - const ActualOUTSVFGNode* actualOut = SVFUtil::cast(getSVFGNode(*ait)); - f << "srcSVFGNodeID: " << nodeId << " => " << "dstSVFGNodeID: " << actualOut->getId() << " >= FormalOUTSVFGNode" << "\n"; - } - } - NodeID def = getDef(formalOut->getMRVer()); - f << "srcSVFGNodeID: " << nodeId << " => " << "dstSVFGNodeID: " << def << " >= FormalOUTSVFGNode | intra" << "\n"; - } - else if(const ActualINSVFGNode* actualIn = SVFUtil::dyn_cast(node)) - { - NodeID def = getDef(actualIn->getMRVer()); - f << "srcSVFGNodeID: " << nodeId << " => " << "dstSVFGNodeID: " << def << " >= ActualINSVFGNode" << "\n"; - - } - else if(const MSSAPHISVFGNode* phiNode = SVFUtil::dyn_cast(node)) - { - for (MemSSA::PHI::OPVers::const_iterator it = phiNode->opVerBegin(), eit = phiNode->opVerEnd(); - it != eit; it++) - { - const MRVer* op = it->second; - NodeID def = getDef(op); - f << "srcSVFGNodeID: " << nodeId << " => " << "dstSVFGNodeID: " << def << " >= PHISVFGNode | MVER: {" << *op << "}" << "\n"; - } - } - } - // Job finish and close file - f.close(); - if (f.good()) - { - outs() << "\n"; - return; - } -} - -void SVFG::readFile(const string& filename) -{ - outs() << "Loading SVFG analysis results from '" << filename << "'..."; - ifstream F(filename.c_str()); - if (!F.is_open()) - { - outs() << " error opening file for reading!\n"; - return; - } - - PAGEdge::PAGEdgeSetTy& stores = getPAGEdgeSet(PAGEdge::Store); - for (PAGEdge::PAGEdgeSetTy::iterator iter = stores.begin(), eiter = - stores.end(); iter != eiter; ++iter) - { - StoreStmt* store = SVFUtil::cast(*iter); - const StmtSVFGNode* sNode = getStmtVFGNode(store); - for(CHISet::iterator pi = mssa->getCHISet(store).begin(), epi = mssa->getCHISet(store).end(); pi!=epi; ++pi) - setDef((*pi)->getResVer(),sNode); - } - //outer loop through each line in the file - string line; - // add nodes - stat->ATVFNodeStart(); - while (F.good()) - { - getline(F, line); - if (line.empty()) - continue; - if (line.find("__Edges__") != std::string::npos) - break; - - std::string s = line; - std::string delimiter = " >= "; - string temp; - int index = 0; - //implement delimiter to split string using ">=" - size_t next = 0; - size_t last = 0; - size_t outer_last = 0; - size_t nextTemp; //size_t lastTemp; - NodeID id = 0; - string type; - string MR; - string basicBlock; - string opVer; - //inner loop through to get each element in the line - while ((next = s.find(delimiter, last)) != string::npos) - { - temp = s.substr(last, next-last); - last = next + 4; - outer_last = next + 4; - if(index == 0) - { - nextTemp = temp.find("SVFGNodeID: ") + 12; - id = atoi(temp.substr(nextTemp).c_str()); - } - if(index == 1) - { - type = temp; - } - if(index > 1) - { - if(index == 2) - { - MR = temp; - } - if(index == 3) - { - basicBlock = temp; - } - } - index++; - } - MRVer* tempMRVer; - if(!MR.empty()) - { - tempMRVer = getMRVERFromString(MR); - } - else - { - tempMRVer = getMRVERFromString(""); - } - //add nodes using the variables we extracted - if(type == "FormalINSVFGNode") - { - outer_last = s.find("ICFGNodeID: ") + 12; - NodeID FunID = atoi(s.substr(outer_last).c_str()); - addFormalINSVFGNode(SVFUtil::dyn_cast(pag->getICFG()->getICFGNode(FunID)), tempMRVer, id); - } - else if(type == "FormalOUTSVFGNode") - { - outer_last = s.find("ICFGNodeID: ") + 12; - NodeID FunID = atoi(s.substr(outer_last).c_str()); - addFormalOUTSVFGNode(SVFUtil::dyn_cast(pag->getICFG()->getICFGNode(FunID)), tempMRVer, id); - } - else if(type == "ActualINSVFGNode") - { - outer_last = s.find("ICFGNodeID: ") + 12; - NodeID CallSiteID = atoi(s.substr(outer_last).c_str()); - addActualINSVFGNode(SVFUtil::dyn_cast(pag->getICFG()->getICFGNode(CallSiteID)), tempMRVer, id); - } - else if(type == "ActualOUTSVFGNode") - { - outer_last = s.find("ICFGNodeID: ") + 12; - NodeID CallSiteID = atoi(s.substr(outer_last).c_str()); - addActualOUTSVFGNode(SVFUtil::dyn_cast(pag->getICFG()->getICFGNode(CallSiteID)), tempMRVer, id); - } - else if (type == "PHISVFGNode") - { - opVer = s.substr(outer_last); - next = opVer.find("{") + 1; - last = opVer.find(",}"); - temp = opVer.substr(next, last); - Map OPVers; - int index = 0; - while ((next = temp.find("{") + 1) != string::npos) - { - if (temp == ",}") - break; - last = temp.find("},"); - string temp1; - temp1 = temp.substr(next, last-next); - MRVer* tempOPVer = getMRVERFromString(temp1); - OPVers.insert(make_pair(index, tempOPVer)); - temp = temp.substr(last + 1); - index++; - } - next = basicBlock.find("ICFGNodeID: ") + 12; - temp = basicBlock.substr(next); - addIntraMSSAPHISVFGNode(pag->getICFG()->getICFGNode(atoi(temp.c_str())), OPVers.begin(), OPVers.end(), tempMRVer, id); - } - else - { - } - - if (totalVFGNode < id) - totalVFGNode = id + 1; - } - stat->ATVFNodeEnd(); - - stat->indVFEdgeStart(); - // Edges - while (F.good()) - { - getline(F, line); - if (line.empty()) - continue; - - std::string s = line; - std::string delimiter = " >= "; - string temp; - // int index = 0; - size_t last = 0; - size_t next = 0; // size_t outer_last = 0; - string edge; - string attributes; - - next = s.find(delimiter); - - edge = s.substr(0, next); - attributes = s.substr(next + 4); - - // extract nodeIDs for src and dst nodes - NodeID src; - NodeID dst; - next = edge.find("srcSVFGNodeID: ") + 15; - last = edge.find(" => "); - src = atoi(edge.substr(next, last-next).c_str()); - next = edge.find("dstSVFGNodeID: ") + 15; - dst = atoi(edge.substr(next).c_str()); - - string type; - string attribute; - if (attributes.find(" | ") == string::npos) - type = attributes; - else - { - next = attributes.find(" | "); - type = attributes.substr(0, next); - attribute = attributes.substr(next + 3); - } - - if(type == "FormalINSVFGNode") - { - const FormalINSVFGNode* formalIn = SVFUtil::cast(getSVFGNode(src)); - const ActualINSVFGNode* actualIn = SVFUtil::cast(getSVFGNode(dst)); - addInterIndirectVFCallEdge(actualIn,formalIn, getCallSiteID(actualIn->getCallSite(), formalIn->getFun())); - } - else if(type == "FormalOUTSVFGNode") - { - const FormalOUTSVFGNode* formalOut = SVFUtil::cast(getSVFGNode(src)); - if (attribute.find("intra") != string::npos) - { - addIntraIndirectVFEdge(dst, src, formalOut->getMRVer()->getMR()->getPointsTo()); - } - else - { - const ActualOUTSVFGNode* actualOut = SVFUtil::cast(getSVFGNode(dst)); - addInterIndirectVFRetEdge(formalOut,actualOut,getCallSiteID(actualOut->getCallSite(), formalOut->getFun())); - } - } - else if(type == "ActualINSVFGNode") - { - const ActualINSVFGNode* actualIn = SVFUtil::cast(getSVFGNode(src)); - addIntraIndirectVFEdge(dst,src, actualIn->getMRVer()->getMR()->getPointsTo()); - } - else if(type == "ActualOUTSVFGNode") - { - // There's no need to connect actual out node to its definition site in the same function. - } - else if (type == "StoreNode" || type == "LoadNode" || type == "PHISVFGNode") - { - MRVer* tempMRVer; - tempMRVer = getMRVERFromString(attribute); - addIntraIndirectVFEdge(dst,src, tempMRVer->getMR()->getPointsTo()); - } - else - { - } - } - stat->indVFEdgeEnd(); - connectFromGlobalToProgEntry(); -} - -MRVer* SVFG::getMRVERFromString(const string& s) -{ - if(s == "") - { - return NULL; - } - string temp; - size_t last = 0; - size_t next = 0; - MRVer* tempMRVer; - MemRegion* tempMemRegion; - MSSADEF* tempDef; - //{create Memory Region object - next = s.find("MemRegion: pts{") + 15; - last = s.find("} MRVERSION: "); - temp = s.substr(next, last-next); - // convert string to PointsTo - NodeBS dstPts; - string point; - stringstream ss(temp); - while (getline(ss, point, ' ')) - { - istringstream sss(point); - NodeID obj; - sss >> obj; - dstPts.set(obj); - } - tempMemRegion = new MemRegion(dstPts); - // create mssdef - next = s.find("MSSADef: ") + 9; - last = s.find("} >="); - temp = s.substr(next, last-next); - // convert string to deftype - istringstream ss1(temp.substr(0, temp.find(", "))); - int obj1; - ss1 >> obj1; - MSSADEF::DEFTYPE defType = static_cast(obj1); - tempDef = new MSSADEF(defType, tempMemRegion); - // mrversion - next = s.find("MRVERSION: ") + 11; - last = s.find(" MSSADef:"); - temp = s.substr(next, last-next); - // convert mrversion to nodeid - istringstream ss2(temp); - NodeID obj2; - ss2 >> obj2; - // create mrver - tempMRVer = new MRVer(tempMemRegion, obj2, tempDef); - return tempMRVer; -} \ No newline at end of file diff --git a/svf/lib/Graphs/SVFGStat.cpp b/svf/lib/Graphs/SVFGStat.cpp index 79bc1b074..3d593dae9 100644 --- a/svf/lib/Graphs/SVFGStat.cpp +++ b/svf/lib/Graphs/SVFGStat.cpp @@ -6,16 +6,16 @@ // // This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU Affero General Public License as published by +// it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Affero General Public License for more details. +// GNU General Public License for more details. -// You should have received a copy of the GNU Affero General Public License +// You should have received a copy of the GNU General Public License // along with this program. If not, see . // //===----------------------------------------------------------------------===// @@ -32,7 +32,6 @@ #include "Graphs/PTACallGraph.h" using namespace SVF; -using namespace std; const char* MemSSAStat::TotalTimeOfConstructMemSSA = "TotalMSSATime"; ///< Total time for constructing memory SSA const char* MemSSAStat::TimeOfGeneratingMemRegions = "GenRegionTime"; ///< Time for allocating regions @@ -125,9 +124,11 @@ void MemSSAStat::performStat() /*! * Print statistics */ -void MemSSAStat::printStat(string str) +void MemSSAStat::printStat() { - PTAStat::printStat("Memory SSA Statistics"); + + std::cout << "\n****Memory SSA Statistics****\n"; + PTAStat::printStat(); } /*! @@ -309,7 +310,7 @@ void SVFGStat::calculateNodeDegrees(SVFGNode* node, NodeSet& nodeHasIndInEdge, N totalInEdge += inEdges.size(); // indirect in edge - u32_t indInEdges = 0; + Size_t indInEdges = 0; SVFGEdge::SVFGEdgeSetTy::const_iterator edgeIt = inEdges.begin(); SVFGEdge::SVFGEdgeSetTy::const_iterator edgeEit = inEdges.end(); for (; edgeIt != edgeEit; ++edgeIt) @@ -320,7 +321,7 @@ void SVFGStat::calculateNodeDegrees(SVFGNode* node, NodeSet& nodeHasIndInEdge, N nodeHasIndInEdge.insert(node->getId()); // get edge's weight // TODO: try a new method to calculate weight. - const NodeBS& cpts = edge->getPointsTo(); + const PointsTo& cpts = edge->getPointsTo(); avgWeight += cpts.count(); totalIndEdgeLabels += cpts.count(); } @@ -349,7 +350,7 @@ void SVFGStat::calculateNodeDegrees(SVFGNode* node, NodeSet& nodeHasIndInEdge, N totalOutEdge += outEdges.size(); // indirect out edge - u32_t indOutEdges = 0; + Size_t indOutEdges = 0; edgeIt = outEdges.begin(); edgeEit = outEdges.end(); for (; edgeIt != edgeEit; ++edgeIt) @@ -490,13 +491,15 @@ void SVFGStat::performSCCStat(SVFGEdgeSet insensitiveCalRetEdges) PTNumStatMap["InsenRetEdge"] = insensitiveRetEdge; - PTAStat::printStat("SVFG SCC Stat"); + std::cout << "\n****SVFG SCC Stat****\n"; + PTAStat::printStat(); delete svfgSCC; } -void SVFGStat::printStat(string str) +void SVFGStat::printStat() { - PTAStat::printStat("SVFG Statistics"); + std::cout << "\n****SVFG Statistics****\n"; + PTAStat::printStat(); } diff --git a/svf/lib/Graphs/ThreadCallGraph.cpp b/svf/lib/Graphs/ThreadCallGraph.cpp index efab703ba..ab69c2f96 100644 --- a/svf/lib/Graphs/ThreadCallGraph.cpp +++ b/svf/lib/Graphs/ThreadCallGraph.cpp @@ -6,16 +6,16 @@ // // This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU Affero General Public License as published by +// it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Affero General Public License for more details. +// GNU General Public License for more details. -// You should have received a copy of the GNU Affero General Public License +// You should have received a copy of the GNU General Public License // along with this program. If not, see . // //===----------------------------------------------------------------------===// @@ -27,7 +27,7 @@ * Author: Yulei Sui, Peng Di, Ding Ye */ -#include "SVFIR/SVFModule.h" +#include "Util/SVFModule.h" #include "Graphs/ThreadCallGraph.h" #include "Util/ThreadAPI.h" @@ -57,7 +57,7 @@ void ThreadCallGraph::updateCallGraph(PointerAnalysis* pta) PointerAnalysis::CallEdgeMap::const_iterator eiter = pta->getIndCallMap().end(); for (; iter != eiter; iter++) { - const CallICFGNode* cs = iter->first; + const CallBlockNode* cs = iter->first; const PTACallGraph::FunctionSet &functions = iter->second; for (PTACallGraph::FunctionSet::const_iterator func_iter = functions.begin(); func_iter != functions.end(); func_iter++) @@ -70,19 +70,20 @@ void ThreadCallGraph::updateCallGraph(PointerAnalysis* pta) // Fork sites for (CallSiteSet::const_iterator it = forksitesBegin(), eit = forksitesEnd(); it != eit; ++it) { - const SVFValue* forkedval = tdAPI->getForkedFun((*it)->getCallSite()); - if(SVFUtil::dyn_cast(forkedval)==nullptr) + const Value* forkedval = tdAPI->getForkedFun((*it)->getCallSite()); + if(SVFUtil::dyn_cast(forkedval)==nullptr) { - SVFIR* pag = pta->getPAG(); - const NodeBS targets = pta->getPts(pag->getValueNode(forkedval)).toNodeBS(); - for (NodeBS::iterator ii = targets.begin(), ie = targets.end(); ii != ie; ii++) + PAG* pag = pta->getPAG(); + const PointsTo& targets = pta->getPts(pag->getValueNode(forkedval)); + for (PointsTo::iterator ii = targets.begin(), ie = targets.end(); ii != ie; ii++) { - if(ObjVar* objPN = SVFUtil::dyn_cast(pag->getGNode(*ii))) + if(ObjPN* objPN = SVFUtil::dyn_cast(pag->getPAGNode(*ii))) { const MemObj* obj = pag->getObject(objPN); if(obj->isFunction()) { - const SVFFunction* svfCallee = SVFUtil::cast(obj->getValue()); + const Function* callee = SVFUtil::cast(obj->getRefVal()); + const SVFFunction* svfCallee = LLVMModuleSet::getLLVMModuleSet()->getSVFFunction(callee); this->addIndirectForkEdge(*it, svfCallee); } } @@ -93,19 +94,20 @@ void ThreadCallGraph::updateCallGraph(PointerAnalysis* pta) // parallel_for sites for (CallSiteSet::const_iterator it = parForSitesBegin(), eit = parForSitesEnd(); it != eit; ++it) { - const SVFValue* forkedval = tdAPI->getTaskFuncAtHareParForSite((*it)->getCallSite()); - if(SVFUtil::dyn_cast(forkedval)==nullptr) + const Value* forkedval = tdAPI->getTaskFuncAtHareParForSite((*it)->getCallSite()); + if(SVFUtil::dyn_cast(forkedval)==nullptr) { - SVFIR* pag = pta->getPAG(); - const NodeBS targets = pta->getPts(pag->getValueNode(forkedval)).toNodeBS(); - for (NodeBS::iterator ii = targets.begin(), ie = targets.end(); ii != ie; ii++) + PAG* pag = pta->getPAG(); + const PointsTo& targets = pta->getPts(pag->getValueNode(forkedval)); + for (PointsTo::iterator ii = targets.begin(), ie = targets.end(); ii != ie; ii++) { - if(ObjVar* objPN = SVFUtil::dyn_cast(pag->getGNode(*ii))) + if(ObjPN* objPN = SVFUtil::dyn_cast(pag->getPAGNode(*ii))) { const MemObj* obj = pag->getObject(objPN); if(obj->isFunction()) { - const SVFFunction* svfCallee = SVFUtil::cast(obj->getValue()); + const Function* callee = SVFUtil::cast(obj->getRefVal()); + const SVFFunction* svfCallee = LLVMModuleSet::getLLVMModuleSet()->getSVFFunction(callee); this->addIndirectForkEdge(*it, svfCallee); } } @@ -123,12 +125,12 @@ void ThreadCallGraph::updateJoinEdge(PointerAnalysis* pta) for (CallSiteSet::const_iterator it = joinsitesBegin(), eit = joinsitesEnd(); it != eit; ++it) { - const SVFValue* jointhread = tdAPI->getJoinedThread((*it)->getCallSite()); + const Value* jointhread = tdAPI->getJoinedThread((*it)->getCallSite()); // find its corresponding fork sites first CallSiteSet forkset; for (CallSiteSet::const_iterator it = forksitesBegin(), eit = forksitesEnd(); it != eit; ++it) { - const SVFValue* forkthread = tdAPI->getForkedThread((*it)->getCallSite()); + const Value* forkthread = tdAPI->getForkedThread((*it)->getCallSite()); if (pta->alias(jointhread, forkthread)) { forkset.insert(*it); @@ -142,13 +144,13 @@ void ThreadCallGraph::updateJoinEdge(PointerAnalysis* pta) /*! * Add direct fork edges */ -void ThreadCallGraph::addDirectForkEdge(const CallICFGNode* cs) +void ThreadCallGraph::addDirectForkEdge(const CallBlockNode* cs) { PTACallGraphNode* caller = getCallGraphNode(cs->getCaller()); - const SVFFunction* forkee = SVFUtil::dyn_cast(tdAPI->getForkedFun(cs->getCallSite())); + const Function* forkee = SVFUtil::dyn_cast(tdAPI->getForkedFun(cs->getCallSite())); assert(forkee && "callee does not exist"); - PTACallGraphNode* callee = getCallGraphNode(forkee->getDefFunForMultipleModule()); + PTACallGraphNode* callee = getCallGraphNode(getDefFunForMultipleModule(forkee)); CallSiteID csId = addCallSite(cs, callee->getFunction()); if (!hasGraphEdge(caller, callee, PTACallGraphEdge::TDForkEdge, csId)) @@ -166,7 +168,7 @@ void ThreadCallGraph::addDirectForkEdge(const CallICFGNode* cs) /*! * Add indirect fork edge to update call graph */ -void ThreadCallGraph::addIndirectForkEdge(const CallICFGNode* cs, const SVFFunction* calleefun) +void ThreadCallGraph::addIndirectForkEdge(const CallBlockNode* cs, const SVFFunction* calleefun) { PTACallGraphNode* caller = getCallGraphNode(cs->getCaller()); PTACallGraphNode* callee = getCallGraphNode(calleefun); @@ -191,7 +193,7 @@ void ThreadCallGraph::addIndirectForkEdge(const CallICFGNode* cs, const SVFFunct * A ThreadJoinEdge is created from the functions where join sites reside in to the start routine function * But we don't invoke addEdge() method to add the edge to src and dst, otherwise it makes a scc cycle */ -void ThreadCallGraph::addDirectJoinEdge(const CallICFGNode* cs,const CallSiteSet& forkset) +void ThreadCallGraph::addDirectJoinEdge(const CallBlockNode* cs,const CallSiteSet& forkset) { PTACallGraphNode* joinFunNode = getCallGraphNode(cs->getCaller()); @@ -199,10 +201,11 @@ void ThreadCallGraph::addDirectJoinEdge(const CallICFGNode* cs,const CallSiteSet for (CallSiteSet::const_iterator it = forkset.begin(), eit = forkset.end(); it != eit; ++it) { - const SVFFunction* threadRoutineFun = SVFUtil::dyn_cast(tdAPI->getForkedFun((*it)->getCallSite())); + const Function* threadRoutineFun = SVFUtil::dyn_cast(tdAPI->getForkedFun((*it)->getCallSite())); assert(threadRoutineFun && "thread routine function does not exist"); - PTACallGraphNode* threadRoutineFunNode = getCallGraphNode(threadRoutineFun); - CallSiteID csId = addCallSite(cs, threadRoutineFun); + const SVFFunction* svfRoutineFun = LLVMModuleSet::getLLVMModuleSet()->getSVFFunction(threadRoutineFun); + PTACallGraphNode* threadRoutineFunNode = getCallGraphNode(svfRoutineFun); + CallSiteID csId = addCallSite(cs, svfRoutineFun); if (!hasThreadJoinEdge(cs,joinFunNode,threadRoutineFunNode, csId)) { @@ -218,14 +221,15 @@ void ThreadCallGraph::addDirectJoinEdge(const CallICFGNode* cs,const CallSiteSet /*! * Add a direct ParFor edges */ -void ThreadCallGraph::addDirectParForEdge(const CallICFGNode* cs) +void ThreadCallGraph::addDirectParForEdge(const CallBlockNode* cs) { PTACallGraphNode* caller = getCallGraphNode(cs->getCaller()); - const SVFFunction* taskFunc = SVFUtil::dyn_cast(tdAPI->getTaskFuncAtHareParForSite(cs->getCallSite())); + const Function* taskFunc = SVFUtil::dyn_cast(tdAPI->getTaskFuncAtHareParForSite(cs->getCallSite())); assert(taskFunc && "callee does not exist"); + const SVFFunction* svfTaskFun = LLVMModuleSet::getLLVMModuleSet()->getSVFFunction(taskFunc); - PTACallGraphNode* callee = getCallGraphNode(taskFunc); + PTACallGraphNode* callee = getCallGraphNode(svfTaskFun); CallSiteID csId = addCallSite(cs, callee->getFunction()); @@ -244,7 +248,7 @@ void ThreadCallGraph::addDirectParForEdge(const CallICFGNode* cs) /*! * Add an indirect ParFor edge to update call graph */ -void ThreadCallGraph::addIndirectParForEdge(const CallICFGNode* cs, const SVFFunction* calleefun) +void ThreadCallGraph::addIndirectParForEdge(const CallBlockNode* cs, const SVFFunction* calleefun) { PTACallGraphNode* caller = getCallGraphNode(cs->getCaller()); diff --git a/svf/lib/Graphs/VFG.cpp b/svf/lib/Graphs/VFG.cpp index 0e3e01432..7a2ba720b 100644 --- a/svf/lib/Graphs/VFG.cpp +++ b/svf/lib/Graphs/VFG.cpp @@ -6,16 +6,16 @@ // // This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU Affero General Public License as published by +// it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Affero General Public License for more details. +// GNU General Public License for more details. -// You should have received a copy of the GNU Affero General Public License +// You should have received a copy of the GNU General Public License // along with this program. If not, see . // //===----------------------------------------------------------------------===// @@ -31,316 +31,188 @@ #include #include "Util/Options.h" #include "Graphs/VFG.h" -#include "SVFIR/SVFModule.h" -#include "Util/SVFUtil.h" +#include "Util/SVFModule.h" +#include "SVF-FE/LLVMUtil.h" using namespace SVF; using namespace SVFUtil; -const std::string VFGNode::toString() const -{ +const std::string VFGNode::toString() const { std::string str; - std::stringstream rawstr(str); + raw_string_ostream rawstr(str); rawstr << "VFGNode ID: " << getId() << " "; return rawstr.str(); } -const std::string StmtVFGNode::toString() const -{ +const std::string StmtVFGNode::toString() const { std::string str; - std::stringstream rawstr(str); + raw_string_ostream rawstr(str); rawstr << "StmtVFGNode ID: " << getId() << " "; rawstr << getPAGEdge()->toString(); return rawstr.str(); } -const NodeBS LoadVFGNode::getDefSVFVars() const -{ - NodeBS nb; - nb.set(getPAGDstNodeID()); - return nb; -} - -const std::string LoadVFGNode::toString() const -{ +const std::string LoadVFGNode::toString() const { std::string str; - std::stringstream rawstr(str); + raw_string_ostream rawstr(str); rawstr << "LoadVFGNode ID: " << getId() << " "; rawstr << getPAGEdge()->toString(); return rawstr.str(); } -const NodeBS StoreVFGNode::getDefSVFVars() const -{ - NodeBS nb; - for (auto edge: getOutEdges()) - { - if (IndirectSVFGEdge *iedge = SVFUtil::dyn_cast(edge)) - { - nb |= iedge->getPointsTo(); - } - } - return nb; -} -const std::string StoreVFGNode::toString() const -{ +const std::string StoreVFGNode::toString() const { std::string str; - std::stringstream rawstr(str); + raw_string_ostream rawstr(str); rawstr << "StoreVFGNode ID: " << getId() << " "; rawstr << getPAGEdge()->toString(); return rawstr.str(); } -const NodeBS CopyVFGNode::getDefSVFVars() const -{ - NodeBS nb; - nb.set(getPAGDstNodeID()); - return nb; -} - -const std::string CopyVFGNode::toString() const -{ +const std::string CopyVFGNode::toString() const { std::string str; - std::stringstream rawstr(str); + raw_string_ostream rawstr(str); rawstr << "CopyVFGNode ID: " << getId() << " "; rawstr << getPAGEdge()->toString(); return rawstr.str(); } -const NodeBS CmpVFGNode::getDefSVFVars() const -{ - NodeBS nb; - nb.set(getRes()->getId()); - return nb; -} - -const std::string CmpVFGNode::toString() const -{ +const std::string CmpVFGNode::toString() const { std::string str; - std::stringstream rawstr(str); + raw_string_ostream rawstr(str); rawstr << "CmpVFGNode ID: " << getId() << " "; rawstr << "PAGEdge: [" << res->getId() << " = cmp("; for(CmpVFGNode::OPVers::const_iterator it = opVerBegin(), eit = opVerEnd(); it != eit; it++) rawstr << it->second->getId() << ", "; rawstr << ")]\n"; - if(res->hasValue()) - { - rawstr << " " << res->getValue()->toString(); + if(res->hasValue()){ + rawstr << " " << value2String(res->getValue()); } return rawstr.str(); } -const NodeBS BinaryOPVFGNode::getDefSVFVars() const -{ - NodeBS nb; - nb.set(getRes()->getId()); - return nb; -} - -const std::string BinaryOPVFGNode::toString() const -{ +const std::string BinaryOPVFGNode::toString() const { std::string str; - std::stringstream rawstr(str); + raw_string_ostream rawstr(str); rawstr << "BinaryOPVFGNode ID: " << getId() << " "; rawstr << "PAGEdge: [" << res->getId() << " = Binary("; for(BinaryOPVFGNode::OPVers::const_iterator it = opVerBegin(), eit = opVerEnd(); it != eit; it++) rawstr << it->second->getId() << ", "; rawstr << ")]\t"; - if(res->hasValue()) - { - rawstr << " " << res->getValue()->toString(); + if(res->hasValue()){ + rawstr << " " << value2String(res->getValue()); } return rawstr.str(); } -const NodeBS UnaryOPVFGNode::getDefSVFVars() const -{ - NodeBS nb; - nb.set(getRes()->getId()); - return nb; -} - -const std::string UnaryOPVFGNode::toString() const -{ +const std::string UnaryOPVFGNode::toString() const { std::string str; - std::stringstream rawstr(str); + raw_string_ostream rawstr(str); rawstr << "UnaryOPVFGNode ID: " << getId() << " "; rawstr << "PAGEdge: [" << res->getId() << " = Unary("; for(UnaryOPVFGNode::OPVers::const_iterator it = opVerBegin(), eit = opVerEnd(); it != eit; it++) rawstr << it->second->getId() << ", "; rawstr << ")]\t"; - if(res->hasValue()) - { - rawstr << " " << res->getValue()->toString(); + if(res->hasValue()){ + rawstr << " " << value2String(res->getValue()); } return rawstr.str(); } -const NodeBS BranchVFGNode::getDefSVFVars() const -{ - return NodeBS(); -} - -const std::string BranchVFGNode::toString() const -{ +const std::string GepVFGNode::toString() const { std::string str; - std::stringstream rawstr(str); - rawstr << "BranchVFGNode ID: " << getId() << " "; - rawstr << "PAGEdge: [" << brstmt->toString() << "\t"; - return rawstr.str(); -} - -const NodeBS GepVFGNode::getDefSVFVars() const -{ - NodeBS nb; - nb.set(getPAGDstNodeID()); - return nb; -} - -const std::string GepVFGNode::toString() const -{ - std::string str; - std::stringstream rawstr(str); + raw_string_ostream rawstr(str); rawstr << "GepVFGNode ID: " << getId() << " "; rawstr << getPAGEdge()->toString(); return rawstr.str(); } -const NodeBS PHIVFGNode::getDefSVFVars() const -{ - NodeBS nb; - nb.set(getRes()->getId()); - return nb; -} -const std::string PHIVFGNode::toString() const -{ +const std::string PHIVFGNode::toString() const { std::string str; - std::stringstream rawstr(str); + raw_string_ostream rawstr(str); rawstr << "PHIVFGNode ID: " << getId() << " "; rawstr << "PAGNode: [" << res->getId() << " = PHI("; for(PHIVFGNode::OPVers::const_iterator it = opVerBegin(), eit = opVerEnd(); it != eit; it++) rawstr << it->second->getId() << ", "; rawstr << ")]\t"; - if(res->hasValue()) - { - rawstr << " " << res->getValue()->toString(); + if(res->hasValue()){ + rawstr << " " << value2String(res->getValue()); } return rawstr.str(); } -const std::string IntraPHIVFGNode::toString() const -{ +const std::string IntraPHIVFGNode::toString() const { std::string str; - std::stringstream rawstr(str); + raw_string_ostream rawstr(str); rawstr << "IntraPHIVFGNode ID: " << getId() << " "; rawstr << "PAGNode: [" << res->getId() << " = PHI("; for(PHIVFGNode::OPVers::const_iterator it = opVerBegin(), eit = opVerEnd(); it != eit; it++) rawstr << it->second->getId() << ", "; rawstr << ")]\t"; - if(res->hasValue()) - { - rawstr << " " << res->getValue()->toString(); + if(res->hasValue()){ + rawstr << " " << value2String(res->getValue()); } return rawstr.str(); } -const NodeBS AddrVFGNode::getDefSVFVars() const -{ - NodeBS nb; - nb.set(getPAGDstNodeID()); - return nb; -} -const std::string AddrVFGNode::toString() const -{ +const std::string AddrVFGNode::toString() const { std::string str; - std::stringstream rawstr(str); + raw_string_ostream rawstr(str); rawstr << "AddrVFGNode ID: " << getId() << " "; rawstr << getPAGEdge()->toString(); return rawstr.str(); } -const std::string ArgumentVFGNode::toString() const -{ +const std::string ArgumentVFGNode::toString() const { std::string str; - std::stringstream rawstr(str); + raw_string_ostream rawstr(str); rawstr << "ArgumentVFGNode ID: " << getId() << " "; rawstr << param->toString(); return rawstr.str(); } -const NodeBS ActualParmVFGNode::getDefSVFVars() const -{ - NodeBS nb; - nb.set(getParam()->getId()); - return nb; -} -const std::string ActualParmVFGNode::toString() const -{ +const std::string ActualParmVFGNode::toString() const { std::string str; - std::stringstream rawstr(str); + raw_string_ostream rawstr(str); rawstr << "ActualParmVFGNode ID: " << getId() << " "; - rawstr << "CS[" << getCallSite()->getCallSite()->getSourceLoc() << "]"; + rawstr << "CS[" << getSourceLoc(getCallSite()->getCallSite()) << "]"; rawstr << param->toString(); return rawstr.str(); } -const NodeBS FormalParmVFGNode::getDefSVFVars() const -{ - NodeBS nb; - nb.set(getParam()->getId()); - return nb; -} -const std::string FormalParmVFGNode::toString() const -{ +const std::string FormalParmVFGNode::toString() const { std::string str; - std::stringstream rawstr(str); + raw_string_ostream rawstr(str); rawstr << "FormalParmVFGNode ID: " << getId() << " "; rawstr << "Fun[" << getFun()->getName() << "]"; rawstr << param->toString(); return rawstr.str(); } -const NodeBS ActualRetVFGNode::getDefSVFVars() const -{ - NodeBS nb; - nb.set(getRev()->getId()); - return nb; -} - -const std::string ActualRetVFGNode::toString() const -{ +const std::string ActualRetVFGNode::toString() const { std::string str; - std::stringstream rawstr(str); + raw_string_ostream rawstr(str); rawstr << "ActualRetVFGNode ID: " << getId() << " "; - rawstr << "CS[" << getCallSite()->getCallSite()->getSourceLoc() << "]"; + rawstr << "CS[" << getSourceLoc(getCallSite()->getCallSite()) << "]"; rawstr << param->toString(); return rawstr.str(); } -const NodeBS FormalRetVFGNode::getDefSVFVars() const -{ - NodeBS nb; - nb.set(getRet()->getId()); - return nb; -} - -const std::string FormalRetVFGNode::toString() const -{ +const std::string FormalRetVFGNode::toString() const { std::string str; - std::stringstream rawstr(str); + raw_string_ostream rawstr(str); rawstr << "FormalRetVFGNode ID: " << getId() << " "; rawstr << "Fun[" << getFun()->getName() << "]"; rawstr << param->toString(); @@ -348,71 +220,57 @@ const std::string FormalRetVFGNode::toString() const } -const std::string InterPHIVFGNode::toString() const -{ +const std::string InterPHIVFGNode::toString() const { std::string str; - std::stringstream rawstr(str); + raw_string_ostream rawstr(str); if(isFormalParmPHI()) - rawstr << "FormalParmPHI ID: " << getId() << " PAGNode ID: " << res->getId() << "\n" << res->getValue()->toString(); + rawstr << "FormalParmPHI ID: " << getId() << " PAGNode ID: " << res->getId() << "\n" << value2String(res->getValue()); else - rawstr << "ActualRetPHI ID: " << getId() << " PAGNode ID: " << res->getId() << "\n" << res->getValue()->toString(); + rawstr << "ActualRetPHI ID: " << getId() << " PAGNode ID: " << res->getId() << "\n" << value2String(res->getValue()); return rawstr.str(); } -const NodeBS NullPtrVFGNode::getDefSVFVars() const -{ - NodeBS nb; - nb.set(getPAGNode()->getId()); - return nb; -} - -const std::string NullPtrVFGNode::toString() const -{ +const std::string NullPtrVFGNode::toString() const { std::string str; - std::stringstream rawstr(str); + raw_string_ostream rawstr(str); rawstr << "NullPtrVFGNode ID: " << getId(); rawstr << " PAGNode ID: " << node->getId() << "\n"; return rawstr.str(); } -const std::string VFGEdge::toString() const -{ +const std::string VFGEdge::toString() const { std::string str; - std::stringstream rawstr(str); + raw_string_ostream rawstr(str); rawstr << "VFGEdge: [" << getDstID() << "<--" << getSrcID() << "]\t"; return rawstr.str(); } -const std::string DirectSVFGEdge::toString() const -{ +const std::string DirectSVFGEdge::toString() const { std::string str; - std::stringstream rawstr(str); + raw_string_ostream rawstr(str); rawstr << "DirectVFGEdge: [" << getDstID() << "<--" << getSrcID() << "]\t"; return rawstr.str(); } -const std::string IntraDirSVFGEdge::toString() const -{ +const std::string IntraDirSVFGEdge::toString() const { std::string str; - std::stringstream rawstr(str); + raw_string_ostream rawstr(str); rawstr << "IntraDirSVFGEdge: [" << getDstID() << "<--" << getSrcID() << "]\t"; return rawstr.str(); } -const std::string CallDirSVFGEdge::toString() const -{ +const std::string CallDirSVFGEdge::toString() const { std::string str; - std::stringstream rawstr(str); + raw_string_ostream rawstr(str); rawstr << "CallDirSVFGEdge CallSite ID: " << getCallSiteId() << " ["; rawstr << getDstID() << "<--" << getSrcID() << "]\t"; return rawstr.str(); } -const std::string RetDirSVFGEdge::toString() const -{ +const std::string RetDirSVFGEdge::toString() const { std::string str; - std::stringstream rawstr(str); + raw_string_ostream rawstr(str); rawstr << "RetDirSVFGEdge CallSite ID: " << getCallSiteId() << " ["; rawstr << getDstID() << "<--" << getSrcID() << "]\t"; return rawstr.str(); @@ -438,7 +296,7 @@ PHIVFGNode::PHIVFGNode(NodeID id, const PAGNode* r,VFGNodeK k): VFGNode(id, k), * 2) connect VFG edges * between two statements (PAGEdges) */ -VFG::VFG(PTACallGraph* cg, VFGK k): totalVFGNode(0), callgraph(cg), pag(SVFIR::getPAG()), kind(k) +VFG::VFG(PTACallGraph* cg, VFGK k): totalVFGNode(0), callgraph(cg), pag(PAG::getPAG()), kind(k) { DBOUT(DGENERAL, outs() << pasMsg("\tCreate VFG Top Level Node\n")); @@ -463,66 +321,73 @@ void VFG::destroy() void VFG::addVFGNodes() { - // initialize dummy definition null pointers in order to uniform the construction + // initialize dummy definition null pointers in order to uniform the construction // to be noted for black hole pointer it has already has address edge connected, - // and its definition will be set when processing addr SVFIR edge. - addNullPtrVFGNode(pag->getGNode(pag->getNullPtr())); + // and its definition will be set when processing addr PAG edge. + addNullPtrVFGNode(pag->getPAGNode(pag->getNullPtr())); // initialize address nodes - SVFStmt::SVFStmtSetTy& addrs = getPAGEdgeSet(SVFStmt::Addr); - for (SVFStmt::SVFStmtSetTy::iterator iter = addrs.begin(), eiter = + PAGEdge::PAGEdgeSetTy& addrs = getPAGEdgeSet(PAGEdge::Addr); + for (PAGEdge::PAGEdgeSetTy::iterator iter = addrs.begin(), eiter = addrs.end(); iter != eiter; ++iter) { - addAddrVFGNode(SVFUtil::cast(*iter)); + addAddrVFGNode(SVFUtil::cast(*iter)); } // initialize copy nodes - SVFStmt::SVFStmtSetTy& copys = getPAGEdgeSet(SVFStmt::Copy); - for (SVFStmt::SVFStmtSetTy::iterator iter = copys.begin(), eiter = + PAGEdge::PAGEdgeSetTy& copys = getPAGEdgeSet(PAGEdge::Copy); + for (PAGEdge::PAGEdgeSetTy::iterator iter = copys.begin(), eiter = copys.end(); iter != eiter; ++iter) { - const CopyStmt* edge = SVFUtil::cast(*iter); - assert(!isPhiCopyEdge(edge) && "Copy edges can not be a PhiNode (or from PhiNode)"); - addCopyVFGNode(edge); + const CopyPE* edge = SVFUtil::cast(*iter); + if(!isPhiCopyEdge(edge)) + addCopyVFGNode(edge); } // initialize gep nodes - SVFStmt::SVFStmtSetTy& ngeps = getPAGEdgeSet(SVFStmt::Gep); - for (SVFStmt::SVFStmtSetTy::iterator iter = ngeps.begin(), eiter = + PAGEdge::PAGEdgeSetTy& ngeps = getPAGEdgeSet(PAGEdge::NormalGep); + for (PAGEdge::PAGEdgeSetTy::iterator iter = ngeps.begin(), eiter = ngeps.end(); iter != eiter; ++iter) { - addGepVFGNode(SVFUtil::cast(*iter)); + addGepVFGNode(SVFUtil::cast(*iter)); + } + + PAGEdge::PAGEdgeSetTy& vgeps = getPAGEdgeSet(PAGEdge::VariantGep); + for (PAGEdge::PAGEdgeSetTy::iterator iter = vgeps.begin(), eiter = + vgeps.end(); iter != eiter; ++iter) + { + addGepVFGNode(SVFUtil::cast(*iter)); } // initialize load nodes - SVFStmt::SVFStmtSetTy& loads = getPAGEdgeSet(SVFStmt::Load); - for (SVFStmt::SVFStmtSetTy::iterator iter = loads.begin(), eiter = + PAGEdge::PAGEdgeSetTy& loads = getPAGEdgeSet(PAGEdge::Load); + for (PAGEdge::PAGEdgeSetTy::iterator iter = loads.begin(), eiter = loads.end(); iter != eiter; ++iter) { - addLoadVFGNode(SVFUtil::cast(*iter)); + addLoadVFGNode(SVFUtil::cast(*iter)); } // initialize store nodes - SVFStmt::SVFStmtSetTy& stores = getPAGEdgeSet(SVFStmt::Store); - for (SVFStmt::SVFStmtSetTy::iterator iter = stores.begin(), eiter = + PAGEdge::PAGEdgeSetTy& stores = getPAGEdgeSet(PAGEdge::Store); + for (PAGEdge::PAGEdgeSetTy::iterator iter = stores.begin(), eiter = stores.end(); iter != eiter; ++iter) { - addStoreVFGNode(SVFUtil::cast(*iter)); + addStoreVFGNode(SVFUtil::cast(*iter)); } - SVFStmt::SVFStmtSetTy& forks = getPAGEdgeSet(SVFStmt::ThreadFork); - for (SVFStmt::SVFStmtSetTy::iterator iter = forks.begin(), eiter = + PAGEdge::PAGEdgeSetTy& forks = getPAGEdgeSet(PAGEdge::ThreadFork); + for (PAGEdge::PAGEdgeSetTy::iterator iter = forks.begin(), eiter = forks.end(); iter != eiter; ++iter) { TDForkPE* forkedge = SVFUtil::cast(*iter); - addActualParmVFGNode(forkedge->getRHSVar(),forkedge->getCallSite()); + addActualParmVFGNode(forkedge->getSrcNode(),forkedge->getCallSite()); } // initialize actual parameter nodes - for(SVFIR::CSToArgsListMap::iterator it = pag->getCallSiteArgsMap().begin(), eit = pag->getCallSiteArgsMap().end(); it !=eit; ++it) + for(PAG::CSToArgsListMap::iterator it = pag->getCallSiteArgsMap().begin(), eit = pag->getCallSiteArgsMap().end(); it !=eit; ++it) { - for(SVFIR::SVFVarList::iterator pit = it->second.begin(), epit = it->second.end(); pit!=epit; ++pit) + for(PAG::PAGNodeList::iterator pit = it->second.begin(), epit = it->second.end(); pit!=epit; ++pit) { const PAGNode* pagNode = *pit; if (isInterestedPAGNode(pagNode)) @@ -531,7 +396,7 @@ void VFG::addVFGNodes() } // initialize actual return nodes (callsite return) - for(SVFIR::CSToRetMap::iterator it = pag->getCallSiteRets().begin(), eit = pag->getCallSiteRets().end(); it !=eit; ++it) + for(PAG::CSToRetMap::iterator it = pag->getCallSiteRets().begin(), eit = pag->getCallSiteRets().end(); it !=eit; ++it) { /// for external function we do not create acutalRet VFGNode @@ -540,48 +405,48 @@ void VFG::addVFGNodes() if(isInterestedPAGNode(it->second) == false || hasDef(it->second)) continue; - addActualRetVFGNode(it->second,it->first->getCallICFGNode()); + addActualRetVFGNode(it->second,it->first->getCallBlockNode()); } // initialize formal parameter nodes - for(SVFIR::FunToArgsListMap::iterator it = pag->getFunArgsMap().begin(), eit = pag->getFunArgsMap().end(); it !=eit; ++it) + for(PAG::FunToArgsListMap::iterator it = pag->getFunArgsMap().begin(), eit = pag->getFunArgsMap().end(); it !=eit; ++it) { const SVFFunction* func = it->first; - for(SVFIR::SVFVarList::iterator pit = it->second.begin(), epit = it->second.end(); pit!=epit; ++pit) + for(PAG::PAGNodeList::iterator pit = it->second.begin(), epit = it->second.end(); pit!=epit; ++pit) { const PAGNode* param = *pit; if (isInterestedPAGNode(param) == false || hasBlackHoleConstObjAddrAsDef(param)) continue; CallPESet callPEs; - if (param->hasIncomingEdges(SVFStmt::Call)) + if (param->hasIncomingEdges(PAGEdge::Call)) { - for (SVFStmt::SVFStmtSetTy::const_iterator cit = param->getIncomingEdgesBegin(SVFStmt::Call), ecit = - param->getIncomingEdgesEnd(SVFStmt::Call); cit != ecit; ++cit) + for (PAGEdge::PAGEdgeSetTy::const_iterator cit = param->getIncomingEdgesBegin(PAGEdge::Call), ecit = + param->getIncomingEdgesEnd(PAGEdge::Call); cit != ecit; ++cit) { CallPE* callPE = SVFUtil::cast(*cit); - if (isInterestedPAGNode(callPE->getRHSVar())) + if (isInterestedPAGNode(callPE->getSrcNode())) callPEs.insert(callPE); } } addFormalParmVFGNode(param,func,callPEs); } - if (func->isVarArg()) + if (func->getLLVMFun()->getFunctionType()->isVarArg()) { - const PAGNode* varParam = pag->getGNode(pag->getVarargNode(func)); + const PAGNode* varParam = pag->getPAGNode(pag->getVarargNode(func)); if (isInterestedPAGNode(varParam) == false || hasBlackHoleConstObjAddrAsDef(varParam)) continue; CallPESet callPEs; - if (varParam->hasIncomingEdges(SVFStmt::Call)) + if (varParam->hasIncomingEdges(PAGEdge::Call)) { - for(SVFStmt::SVFStmtSetTy::const_iterator cit = varParam->getIncomingEdgesBegin(SVFStmt::Call), - ecit = varParam->getIncomingEdgesEnd(SVFStmt::Call); cit!=ecit; ++cit) + for(PAGEdge::PAGEdgeSetTy::const_iterator cit = varParam->getIncomingEdgesBegin(PAGEdge::Call), + ecit = varParam->getIncomingEdgesEnd(PAGEdge::Call); cit!=ecit; ++cit) { CallPE* callPE = SVFUtil::cast(*cit); - if(isInterestedPAGNode(callPE->getRHSVar())) + if(isInterestedPAGNode(callPE->getSrcNode())) callPEs.insert(callPE); } } @@ -590,82 +455,56 @@ void VFG::addVFGNodes() } // initialize formal return nodes (callee return) - for (SVFIR::FunToRetMap::iterator it = pag->getFunRets().begin(), eit = pag->getFunRets().end(); it != eit; ++it) + for (PAG::FunToRetMap::iterator it = pag->getFunRets().begin(), eit = pag->getFunRets().end(); it != eit; ++it) { const SVFFunction* func = it->first; const PAGNode* uniqueFunRetNode = it->second; RetPESet retPEs; - if (uniqueFunRetNode->hasOutgoingEdges(SVFStmt::Ret)) + if (uniqueFunRetNode->hasOutgoingEdges(PAGEdge::Ret)) { - for (SVFStmt::SVFStmtSetTy::const_iterator cit = uniqueFunRetNode->getOutgoingEdgesBegin(SVFStmt::Ret), - ecit = uniqueFunRetNode->getOutgoingEdgesEnd(SVFStmt::Ret); + for (PAGEdge::PAGEdgeSetTy::const_iterator cit = uniqueFunRetNode->getOutgoingEdgesBegin(PAGEdge::Ret), + ecit = uniqueFunRetNode->getOutgoingEdgesEnd(PAGEdge::Ret); cit != ecit; ++cit) { const RetPE* retPE = SVFUtil::cast(*cit); - if (isInterestedPAGNode(retPE->getLHSVar())) + if (isInterestedPAGNode(retPE->getDstNode())) retPEs.insert(retPE); } } - if(isInterestedPAGNode(uniqueFunRetNode)) - addFormalRetVFGNode(uniqueFunRetNode, func, retPEs); + if(isInterestedPAGNode(uniqueFunRetNode)) + addFormalRetVFGNode(uniqueFunRetNode, func, retPEs); } // initialize llvm phi nodes (phi of top level pointers) - SVFStmt::SVFStmtSetTy& phis = getPAGEdgeSet(SVFStmt::Phi); - for (SVFStmt::SVFStmtSetTy::iterator iter = phis.begin(), eiter = - phis.end(); iter != eiter; ++iter) - { - const PhiStmt* edge = SVFUtil::cast(*iter); - if(isInterestedPAGNode(edge->getRes())) - addIntraPHIVFGNode(edge); - } - // initialize select statement - SVFStmt::SVFStmtSetTy& selects = getPAGEdgeSet(SVFStmt::Select); - for (SVFStmt::SVFStmtSetTy::iterator iter = selects.begin(), eiter = - selects.end(); iter != eiter; ++iter) + PAG::PHINodeMap& phiNodeMap = pag->getPhiNodeMap(); + for (PAG::PHINodeMap::iterator pit = phiNodeMap.begin(), epit = phiNodeMap.end(); pit != epit; ++pit) { - const MultiOpndStmt* edge = SVFUtil::cast(*iter); - if(isInterestedPAGNode(edge->getRes())) - addIntraPHIVFGNode(edge); + if (isInterestedPAGNode(pit->first)) + addIntraPHIVFGNode(pit->first, pit->second); } // initialize llvm binary nodes (binary operators) - SVFStmt::SVFStmtSetTy& binaryops = getPAGEdgeSet(SVFStmt::BinaryOp); - for (SVFStmt::SVFStmtSetTy::iterator iter = binaryops.begin(), eiter = - binaryops.end(); iter != eiter; ++iter) + PAG::BinaryNodeMap& binaryNodeMap = pag->getBinaryNodeMap(); + for (PAG::BinaryNodeMap::iterator pit = binaryNodeMap.begin(), epit = binaryNodeMap.end(); pit != epit; ++pit) { - const BinaryOPStmt* edge = SVFUtil::cast(*iter); - if(isInterestedPAGNode(edge->getRes())) - addBinaryOPVFGNode(edge); + if (isInterestedPAGNode(pit->first)) + addBinaryOPVFGNode(pit->first, pit->second); } // initialize llvm unary nodes (unary operators) - SVFStmt::SVFStmtSetTy& unaryops = getPAGEdgeSet(SVFStmt::UnaryOp); - for (SVFStmt::SVFStmtSetTy::iterator iter = unaryops.begin(), eiter = - unaryops.end(); iter != eiter; ++iter) + PAG::UnaryNodeMap& unaryNodeMap = pag->getUnaryNodeMap(); + for (PAG::UnaryNodeMap::iterator pit = unaryNodeMap.begin(), epit = unaryNodeMap.end(); pit != epit; ++pit) { - const UnaryOPStmt* edge = SVFUtil::cast(*iter); - if(isInterestedPAGNode(edge->getRes())) - addUnaryOPVFGNode(edge); + if (isInterestedPAGNode(pit->first)) + addUnaryOPVFGNode(pit->first, pit->second); } - // initialize llvm unary nodes (unary operators) - SVFStmt::SVFStmtSetTy& brs = getPAGEdgeSet(SVFStmt::Branch); - for (SVFStmt::SVFStmtSetTy::iterator iter = brs.begin(), eiter = - brs.end(); iter != eiter; ++iter) - { - const BranchStmt* edge = SVFUtil::cast(*iter); - if(isInterestedPAGNode(edge->getBranchInst())) - addBranchVFGNode(edge); - } - // initialize llvm cmp nodes (comparison) - SVFStmt::SVFStmtSetTy& cmps = getPAGEdgeSet(SVFStmt::Cmp); - for (SVFStmt::SVFStmtSetTy::iterator iter = cmps.begin(), eiter = - cmps.end(); iter != eiter; ++iter) + // initialize llvm cmp nodes (comparision) + PAG::CmpNodeMap& cmpNodeMap = pag->getCmpNodeMap(); + for (PAG::CmpNodeMap::iterator pit = cmpNodeMap.begin(), epit =cmpNodeMap.end(); pit != epit; ++pit) { - const CmpStmt* edge = SVFUtil::cast(*iter); - if(isInterestedPAGNode(edge->getRes())) - addCmpVFGNode(edge); + if (isInterestedPAGNode(pit->first)) + addCmpVFGNode(pit->first, pit->second); } } @@ -677,21 +516,19 @@ VFGEdge* VFG::addIntraDirectVFEdge(NodeID srcId, NodeID dstId) VFGNode* srcNode = getVFGNode(srcId); VFGNode* dstNode = getVFGNode(dstId); checkIntraEdgeParents(srcNode, dstNode); - VFGEdge* edge = hasIntraVFGEdge(srcNode, dstNode, VFGEdge::IntraDirectVF); - if (edge != nullptr) + if(VFGEdge* edge = hasIntraVFGEdge(srcNode,dstNode, VFGEdge::IntraDirectVF)) { assert(edge->isDirectVFGEdge() && "this should be a direct value flow edge!"); return nullptr; } else { - if(srcNode!=dstNode) - { - IntraDirSVFGEdge* directEdge = new IntraDirSVFGEdge(srcNode,dstNode); - return (addVFGEdge(directEdge) ? directEdge : nullptr); - } - else - return nullptr; + if(srcNode!=dstNode){ + IntraDirSVFGEdge* directEdge = new IntraDirSVFGEdge(srcNode,dstNode); + return (addVFGEdge(directEdge) ? directEdge : nullptr); + } + else + return nullptr; } } @@ -702,8 +539,7 @@ VFGEdge* VFG::addCallEdge(NodeID srcId, NodeID dstId, CallSiteID csId) { VFGNode* srcNode = getVFGNode(srcId); VFGNode* dstNode = getVFGNode(dstId); - VFGEdge* edge = hasInterVFGEdge(srcNode, dstNode, VFGEdge::CallDirVF, csId); - if (edge != nullptr) + if(VFGEdge* edge = hasInterVFGEdge(srcNode,dstNode, VFGEdge::CallDirVF,csId)) { assert(edge->isCallDirectVFGEdge() && "this should be a direct value flow edge!"); return nullptr; @@ -722,8 +558,7 @@ VFGEdge* VFG::addRetEdge(NodeID srcId, NodeID dstId, CallSiteID csId) { VFGNode* srcNode = getVFGNode(srcId); VFGNode* dstNode = getVFGNode(dstId); - VFGEdge* edge = hasInterVFGEdge(srcNode, dstNode, VFGEdge::RetDirVF, csId); - if (edge != nullptr) + if(VFGEdge* edge = hasInterVFGEdge(srcNode,dstNode, VFGEdge::RetDirVF,csId)) { assert(edge->isRetDirectVFGEdge() && "this should be a direct value flow edge!"); return nullptr; @@ -753,21 +588,11 @@ void VFG::connectDirectVFGEdges() if(SVFUtil::isa(stmtNode)) continue; /// for all other cases, like copy/gep/load/ret, connect the RHS pointer to its def - if (stmtNode->getPAGSrcNode()->isConstDataOrAggDataButNotNullPtr() == false) - // for ptr vfg, we skip src node of integer type if it is at a int2ptr copystmt - if(isInterestedPAGNode(stmtNode->getPAGSrcNode())) - addIntraDirectVFEdge(getDef(stmtNode->getPAGSrcNode()), nodeId); - if (const GepStmt* gepStmt = SVFUtil::dyn_cast(stmtNode->getPAGEdge())) - { - for (const auto &varType: gepStmt->getOffsetVarAndGepTypePairVec()) - { - if(varType.first->isConstDataOrAggDataButNotNullPtr() || isInterestedPAGNode(varType.first) == false) - continue; - addIntraDirectVFEdge(getDef(varType.first), nodeId); - } - } + if (stmtNode->getPAGSrcNode()->isConstantData() == false) + addIntraDirectVFEdge(getDef(stmtNode->getPAGSrcNode()), nodeId); + /// for store, connect the RHS/LHS pointer to its def - if(SVFUtil::isa(stmtNode) && (stmtNode->getPAGDstNode()->isConstDataOrAggDataButNotNullPtr() == false)) + if(SVFUtil::isa(stmtNode) && (stmtNode->getPAGDstNode()->isConstantData() == false)) { addIntraDirectVFEdge(getDef(stmtNode->getPAGDstNode()), nodeId); } @@ -777,7 +602,7 @@ void VFG::connectDirectVFGEdges() { for (PHIVFGNode::OPVers::const_iterator it = phiNode->opVerBegin(), eit = phiNode->opVerEnd(); it != eit; it++) { - if (it->second->isConstDataOrAggDataButNotNullPtr() == false) + if (it->second->isConstantData() == false) addIntraDirectVFEdge(getDef(it->second), nodeId); } } @@ -785,7 +610,7 @@ void VFG::connectDirectVFGEdges() { for (BinaryOPVFGNode::OPVers::const_iterator it = binaryNode->opVerBegin(), eit = binaryNode->opVerEnd(); it != eit; it++) { - if (it->second->isConstDataOrAggDataButNotNullPtr() == false) + if (it->second->isConstantData() == false) addIntraDirectVFEdge(getDef(it->second), nodeId); } } @@ -793,7 +618,7 @@ void VFG::connectDirectVFGEdges() { for (UnaryOPVFGNode::OPVers::const_iterator it = unaryNode->opVerBegin(), eit = unaryNode->opVerEnd(); it != eit; it++) { - if (it->second->isConstDataOrAggDataButNotNullPtr() == false) + if (it->second->isConstantData() == false) addIntraDirectVFEdge(getDef(it->second), nodeId); } } @@ -801,19 +626,13 @@ void VFG::connectDirectVFGEdges() { for (CmpVFGNode::OPVers::const_iterator it = cmpNode->opVerBegin(), eit = cmpNode->opVerEnd(); it != eit; it++) { - if (it->second->isConstDataOrAggDataButNotNullPtr() == false) + if (it->second->isConstantData() == false) addIntraDirectVFEdge(getDef(it->second), nodeId); } } - else if(BranchVFGNode* branchNode = SVFUtil::dyn_cast(node)) - { - const SVFVar* cond = branchNode->getBranchStmt()->getCondition(); - if (cond->isConstDataOrAggDataButNotNullPtr() == false) - addIntraDirectVFEdge(getDef(cond), nodeId); - } else if(ActualParmVFGNode* actualParm = SVFUtil::dyn_cast(node)) { - if (actualParm->getParam()->isConstDataOrAggDataButNotNullPtr() == false) + if (actualParm->getParam()->isConstantData() == false) addIntraDirectVFEdge(getDef(actualParm->getParam()), nodeId); } else if(FormalParmVFGNode* formalParm = SVFUtil::dyn_cast(node)) @@ -821,8 +640,8 @@ void VFG::connectDirectVFGEdges() for(CallPESet::const_iterator it = formalParm->callPEBegin(), eit = formalParm->callPEEnd(); it!=eit; ++it) { - const CallICFGNode* cs = (*it)->getCallSite(); - ActualParmVFGNode* acutalParm = getActualParmVFGNode((*it)->getRHSVar(),cs); + const CallBlockNode* cs = (*it)->getCallSite(); + ActualParmVFGNode* acutalParm = getActualParmVFGNode((*it)->getSrcNode(),cs); addInterEdgeFromAPToFP(acutalParm,formalParm,getCallSiteID(cs, formalParm->getFun())); } } @@ -834,9 +653,9 @@ void VFG::connectDirectVFGEdges() /// connect formal ret to actual ret for(RetPESet::const_iterator it = calleeRet->retPEBegin(), eit = calleeRet->retPEEnd(); it!=eit; ++it) { - ActualRetVFGNode* callsiteRev = getActualRetVFGNode((*it)->getLHSVar()); - const CallICFGNode* retBlockNode = (*it)->getCallSite(); - CallICFGNode* callBlockNode = pag->getICFG()->getCallICFGNode(retBlockNode->getCallSite()); + ActualRetVFGNode* callsiteRev = getActualRetVFGNode((*it)->getDstNode()); + const CallBlockNode* retBlockNode = (*it)->getCallSite(); + CallBlockNode* callBlockNode = pag->getICFG()->getCallBlockNode(retBlockNode->getCallSite()); addInterEdgeFromFRToAR(calleeRet,callsiteRev, getCallSiteID(callBlockNode, calleeRet->getFun())); } } @@ -845,26 +664,25 @@ void VFG::connectDirectVFGEdges() } /// connect direct value-flow edges (parameter passing) for thread fork/join - if(Options::EnableThreadCallGraph()) - { + if(Options::EnableThreadCallGraph){ /// add fork edge - SVFStmt::SVFStmtSetTy& forks = getPAGEdgeSet(SVFStmt::ThreadFork); - for (SVFStmt::SVFStmtSetTy::iterator iter = forks.begin(), eiter = - forks.end(); iter != eiter; ++iter) + PAGEdge::PAGEdgeSetTy& forks = getPAGEdgeSet(PAGEdge::ThreadFork); + for (PAGEdge::PAGEdgeSetTy::iterator iter = forks.begin(), eiter = + forks.end(); iter != eiter; ++iter) { TDForkPE* forkedge = SVFUtil::cast(*iter); - ActualParmVFGNode* acutalParm = getActualParmVFGNode(forkedge->getRHSVar(),forkedge->getCallSite()); - FormalParmVFGNode* formalParm = getFormalParmVFGNode(forkedge->getLHSVar()); + ActualParmVFGNode* acutalParm = getActualParmVFGNode(forkedge->getSrcNode(),forkedge->getCallSite()); + FormalParmVFGNode* formalParm = getFormalParmVFGNode(forkedge->getDstNode()); addInterEdgeFromAPToFP(acutalParm,formalParm,getCallSiteID(forkedge->getCallSite(), formalParm->getFun())); } /// add join edge - SVFStmt::SVFStmtSetTy& joins = getPAGEdgeSet(SVFStmt::ThreadJoin); - for (SVFStmt::SVFStmtSetTy::iterator iter = joins.begin(), eiter = - joins.end(); iter != eiter; ++iter) + PAGEdge::PAGEdgeSetTy& joins = getPAGEdgeSet(PAGEdge::ThreadJoin); + for (PAGEdge::PAGEdgeSetTy::iterator iter = joins.begin(), eiter = + joins.end(); iter != eiter; ++iter) { TDJoinPE* joinedge = SVFUtil::cast(*iter); - NodeID callsiteRev = getDef(joinedge->getLHSVar()); - FormalRetVFGNode* calleeRet = getFormalRetVFGNode(joinedge->getRHSVar()); + NodeID callsiteRev = getDef(joinedge->getDstNode()); + FormalRetVFGNode* calleeRet = getFormalRetVFGNode(joinedge->getSrcNode()); addRetEdge(calleeRet->getId(),callsiteRev, getCallSiteID(joinedge->getCallSite(), calleeRet->getFun())); } } @@ -945,7 +763,7 @@ void VFG::dump(const std::string& file, bool simple) */ void VFG::view() { - SVF::ViewGraph(this, "Value Flow Graph"); + llvm::ViewGraph(this, "Value Flow Graph"); } @@ -956,7 +774,7 @@ void VFG::updateCallGraph(PointerAnalysis* pta) PointerAnalysis::CallEdgeMap::const_iterator eiter = pta->getIndCallMap().end(); for (; iter != eiter; iter++) { - const CallICFGNode* newcs = iter->first; + const CallBlockNode* newcs = iter->first; assert(newcs->isIndirectCall() && "this is not an indirect call?"); const PointerAnalysis::FunctionSet & functions = iter->second; for (PointerAnalysis::FunctionSet::const_iterator func_iter = functions.begin(); func_iter != functions.end(); func_iter++) @@ -971,39 +789,37 @@ void VFG::updateCallGraph(PointerAnalysis* pta) * Connect actual params/return to formal params/return for top-level variables. * Also connect indirect actual in/out and formal in/out. */ -void VFG::connectCallerAndCallee(const CallICFGNode* callBlockNode, const SVFFunction* callee, VFGEdgeSetTy& edges) +void VFG::connectCallerAndCallee(const CallBlockNode* callBlockNode, const SVFFunction* callee, VFGEdgeSetTy& edges) { - SVFIR * pag = SVFIR::getPAG(); + PAG * pag = PAG::getPAG(); ICFG * icfg = pag->getICFG(); CallSiteID csId = getCallSiteID(callBlockNode, callee); - RetICFGNode* retBlockNode = icfg->getRetICFGNode(callBlockNode->getCallSite()); + RetBlockNode* retBlockNode = icfg->getRetBlockNode(callBlockNode->getCallSite()); // connect actual and formal param - if (pag->hasCallSiteArgsMap(callBlockNode) && pag->hasFunArgsList(callee) && - matchArgs(callBlockNode->getCallSite(), callee)) + if (pag->hasCallSiteArgsMap(callBlockNode) && pag->hasFunArgsList(callee)) { - const SVFIR::SVFVarList& csArgList = pag->getCallSiteArgsList(callBlockNode); - const SVFIR::SVFVarList& funArgList = pag->getFunArgsList(callee); - SVFIR::SVFVarList::const_iterator csArgIt = csArgList.begin(), csArgEit = csArgList.end(); - SVFIR::SVFVarList::const_iterator funArgIt = funArgList.begin(), funArgEit = funArgList.end(); + const PAG::PAGNodeList& csArgList = pag->getCallSiteArgsList(callBlockNode); + const PAG::PAGNodeList& funArgList = pag->getFunArgsList(callee); + PAG::PAGNodeList::const_iterator csArgIt = csArgList.begin(), csArgEit = csArgList.end(); + PAG::PAGNodeList::const_iterator funArgIt = funArgList.begin(), funArgEit = funArgList.end(); for (; funArgIt != funArgEit && csArgIt != csArgEit; funArgIt++, csArgIt++) { const PAGNode *cs_arg = *csArgIt; const PAGNode *fun_arg = *funArgIt; - if (isInterestedPAGNode(cs_arg) && isInterestedPAGNode(fun_arg)) + if (fun_arg->isPointer() && cs_arg->isPointer()) connectAParamAndFParam(cs_arg, fun_arg, callBlockNode, csId, edges); } assert(funArgIt == funArgEit && "function has more arguments than call site"); - - if (callee->isVarArg()) + if (callee->getLLVMFun()->isVarArg()) { NodeID varFunArg = pag->getVarargNode(callee); - const PAGNode* varFunArgNode = pag->getGNode(varFunArg); - if (isInterestedPAGNode(varFunArgNode)) + const PAGNode* varFunArgNode = pag->getPAGNode(varFunArg); + if (varFunArgNode->isPointer()) { for (; csArgIt != csArgEit; csArgIt++) { const PAGNode *cs_arg = *csArgIt; - if (isInterestedPAGNode(cs_arg)) + if (cs_arg->isPointer()) connectAParamAndFParam(cs_arg, varFunArgNode, callBlockNode, csId, edges); } } @@ -1015,7 +831,7 @@ void VFG::connectCallerAndCallee(const CallICFGNode* callBlockNode, const SVFFun { const PAGNode* cs_return = pag->getCallSiteRet(retBlockNode); const PAGNode* fun_return = pag->getFunRet(callee); - if (isInterestedPAGNode(cs_return) && isInterestedPAGNode(fun_return)) + if (cs_return->isPointer() && fun_return->isPointer()) connectFRetAndARet(fun_return, cs_return, csId, edges); } } @@ -1075,43 +891,20 @@ const SVFFunction* VFG::isFunEntryVFGNode(const VFGNode* node) const } -const SVFValue* StmtVFGNode::getValue() const -{ - return getPAGEdge()->getValue(); -} - -const SVFValue* CmpVFGNode::getValue() const -{ - return getRes()->getValue(); -} - -const SVFValue* BinaryOPVFGNode::getValue() const -{ - return getRes()->getValue(); -} - -const SVFValue* PHIVFGNode::getValue() const -{ - return getRes()->getValue(); -} -const SVFValue* ArgumentVFGNode::getValue() const -{ - return param->hasValue() ? param->getValue() : nullptr; -} /*! * GraphTraits specialization */ -namespace SVF +namespace llvm { template<> -struct DOTGraphTraits : public DOTGraphTraits +struct DOTGraphTraits : public DOTGraphTraits { typedef VFGNode NodeType; DOTGraphTraits(bool isSimple = false) : - DOTGraphTraits(isSimple) + DOTGraphTraits(isSimple) { } @@ -1133,7 +926,7 @@ struct DOTGraphTraits : public DOTGraphTraits static std::string getSimpleNodeLabel(NodeType *node, VFG*) { std::string str; - std::stringstream rawstr(str); + raw_string_ostream rawstr(str); if(StmtVFGNode* stmtNode = SVFUtil::dyn_cast(node)) { rawstr << stmtNode->toString(); @@ -1152,11 +945,11 @@ struct DOTGraphTraits : public DOTGraphTraits } else if (ActualRetVFGNode* ar = SVFUtil::dyn_cast(node)) { - rawstr << ar->toString(); + rawstr << ar->toString(); } else if (FormalRetVFGNode* fr = SVFUtil::dyn_cast(node)) { - rawstr << fr->toString(); + rawstr << fr->toString(); } else if(SVFUtil::isa(node)) { @@ -1174,10 +967,6 @@ struct DOTGraphTraits : public DOTGraphTraits { rawstr << cmp->toString();; } - else if (BranchVFGNode* branchNode = SVFUtil::dyn_cast(node)) - { - rawstr << branchNode->toString(); - } else assert(false && "what else kinds of nodes do we have??"); @@ -1189,7 +978,7 @@ struct DOTGraphTraits : public DOTGraphTraits { std::string str; - std::stringstream rawstr(str); + raw_string_ostream rawstr(str); if(StmtVFGNode* stmtNode = SVFUtil::dyn_cast(node)) { rawstr << stmtNode->toString(); @@ -1234,10 +1023,6 @@ struct DOTGraphTraits : public DOTGraphTraits { rawstr << mr->toString(); } - else if (BranchVFGNode* branchNode = SVFUtil::dyn_cast(node)) - { - rawstr << branchNode->toString(); - } else assert(false && "what else kinds of nodes do we have??"); @@ -1247,16 +1032,16 @@ struct DOTGraphTraits : public DOTGraphTraits static std::string getNodeAttributes(NodeType *node, VFG*) { std::string str; - std::stringstream rawstr(str); + raw_string_ostream rawstr(str); if(StmtVFGNode* stmtNode = SVFUtil::dyn_cast(node)) { const PAGEdge* edge = stmtNode->getPAGEdge(); - if (SVFUtil::isa(edge)) + if (SVFUtil::isa(edge)) { rawstr << "color=green"; } - else if (SVFUtil::isa(edge)) + else if (SVFUtil::isa(edge)) { rawstr << "color=black"; } @@ -1264,15 +1049,15 @@ struct DOTGraphTraits : public DOTGraphTraits { rawstr << "color=black,style=dotted"; } - else if (SVFUtil::isa(edge)) + else if (SVFUtil::isa(edge)) { rawstr << "color=purple"; } - else if (SVFUtil::isa(edge)) + else if (SVFUtil::isa(edge)) { rawstr << "color=blue"; } - else if (SVFUtil::isa(edge)) + else if (SVFUtil::isa(edge)) { rawstr << "color=red"; } @@ -1322,10 +1107,6 @@ struct DOTGraphTraits : public DOTGraphTraits { rawstr << "color=orange,penwidth=2"; } - else if (SVFUtil::isa(node)) - { - rawstr << "color=gold,penwidth=2"; - } else assert(false && "no such kind of node!!"); @@ -1371,7 +1152,7 @@ struct DOTGraphTraits : public DOTGraphTraits assert(edge && "No edge found!!"); std::string str; - std::stringstream rawstr(str); + raw_string_ostream rawstr(str); if (CallDirSVFGEdge* dirCall = SVFUtil::dyn_cast(edge)) rawstr << dirCall->getCallSiteId(); else if (RetDirSVFGEdge* dirRet = SVFUtil::dyn_cast(edge)) diff --git a/svf/lib/MSSA/MemPartition.cpp b/svf/lib/MSSA/MemPartition.cpp index 48a481048..2393eb0ae 100644 --- a/svf/lib/MSSA/MemPartition.cpp +++ b/svf/lib/MSSA/MemPartition.cpp @@ -6,16 +6,16 @@ // // This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU Affero General Public License as published by +// it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Affero General Public License for more details. +// GNU General Public License for more details. -// You should have received a copy of the GNU Affero General Public License +// You should have received a copy of the GNU General Public License // along with this program. If not, see . // //===----------------------------------------------------------------------===// @@ -41,15 +41,15 @@ using namespace SVF; */ void DistinctMRG::partitionMRs() { - for(FunToPointsTosMap::iterator it = getFunToPointsToList().begin(), eit = getFunToPointsToList().end(); + for(FunToPointsToMap::iterator it = getFunToPointsToList().begin(), eit = getFunToPointsToList().end(); it!=eit; ++it) { const SVFFunction* fun = it->first; /// Collect all points-to target in a function scope. - NodeBS mergePts; + PointsTo mergePts; for(PointsToList::iterator cit = it->second.begin(), ecit = it->second.end(); cit!=ecit; ++cit) { - const NodeBS& pts = *cit; + const PointsTo& pts = *cit; mergePts |= pts; } createDistinctMR(fun, mergePts); @@ -61,17 +61,17 @@ void DistinctMRG::partitionMRs() * 1. collect all points-to targets in a function scope. * 2. create memory region for each point-to target. */ -void DistinctMRG::createDistinctMR(const SVFFunction* func, const NodeBS& pts) +void DistinctMRG::createDistinctMR(const SVFFunction* func, const PointsTo& pts) { /// Create memory regions for each points-to target. - NodeBS::iterator ptsIt = pts.begin(); - NodeBS::iterator ptsEit = pts.end(); + PointsTo::iterator ptsIt = pts.begin(); + PointsTo::iterator ptsEit = pts.end(); for (; ptsIt != ptsEit; ++ptsIt) { NodeID id = *ptsIt; // create new conditional points-to set with this single element. - NodeBS newPts; + PointsTo newPts; newPts.set(id); // set the rep cpts as itself. @@ -88,17 +88,17 @@ void DistinctMRG::createDistinctMR(const SVFFunction* func, const NodeBS& pts) * @param fun The function being analyzed. * @param mrs Memory region set contains all possible target memory regions. */ -void DistinctMRG::getMRsForLoad(MRSet& mrs, const NodeBS& pts, const SVFFunction*) +void DistinctMRG::getMRsForLoad(MRSet& mrs, const PointsTo& pts, const SVFFunction*) { /// Get memory regions for each points-to element in cpts. - NodeBS::iterator ptsIt = pts.begin(); - NodeBS::iterator ptsEit = pts.end(); + PointsTo::iterator ptsIt = pts.begin(); + PointsTo::iterator ptsEit = pts.end(); for (; ptsIt != ptsEit; ++ptsIt) { NodeID id = *ptsIt; // create new conditional points-to set with this single element. - NodeBS newPts; + PointsTo newPts; newPts.set(id); MemRegion mr(newPts); @@ -112,7 +112,7 @@ void DistinctMRG::getMRsForLoad(MRSet& mrs, const NodeBS& pts, const SVFFunction * Get memory regions to be inserted at a load statement. * Just process as getMRsForLoad(). */ -void DistinctMRG::getMRsForCallSiteRef(MRSet& aliasMRs, const NodeBS& cpts, const SVFFunction* fun) +void DistinctMRG::getMRsForCallSiteRef(MRSet& aliasMRs, const PointsTo& cpts, const SVFFunction* fun) { getMRsForLoad(aliasMRs, cpts, fun); } @@ -121,7 +121,7 @@ void DistinctMRG::getMRsForCallSiteRef(MRSet& aliasMRs, const NodeBS& cpts, cons void IntraDisjointMRG::partitionMRs() { - for(FunToPointsTosMap::iterator it = getFunToPointsToList().begin(), + for(FunToPointsToMap::iterator it = getFunToPointsToList().begin(), eit = getFunToPointsToList().end(); it!=eit; ++it) { const SVFFunction* fun = it->first; @@ -129,7 +129,7 @@ void IntraDisjointMRG::partitionMRs() for(PointsToList::iterator cit = it->second.begin(), ecit = it->second.end(); cit!=ecit; ++cit) { - const NodeBS& cpts = *cit; + const PointsTo& cpts = *cit; PointsToList& inters = getIntersList(fun); computeIntersections(cpts, inters); @@ -140,7 +140,7 @@ void IntraDisjointMRG::partitionMRs() for (PointsToList::const_iterator interIt = inters.begin(), interEit = inters.end(); interIt != interEit; ++interIt) { - const NodeBS& inter = *interIt; + const PointsTo& inter = *interIt; createDisjointMR(fun, inter); } } @@ -149,7 +149,7 @@ void IntraDisjointMRG::partitionMRs() /** * Compute intersections between cpts and computed cpts intersections before. */ -void IntraDisjointMRG::computeIntersections(const NodeBS& cpts, PointsToList& inters) +void IntraDisjointMRG::computeIntersections(const PointsTo& cpts, PointsToList& inters) { if (inters.find(cpts) != inters.end()) { @@ -168,18 +168,18 @@ void IntraDisjointMRG::computeIntersections(const NodeBS& cpts, PointsToList& in PointsToList toBeDeleted; PointsToList newInters; - NodeBS cpts_copy = cpts; // make a copy since cpts may be changed. + PointsTo cpts_copy = cpts; // make a copy since cpts may be changed. // check intersections with existing cpts in subSetMap for (PointsToList::const_iterator interIt = inters.begin(), interEit = inters.end(); interIt != interEit; ++interIt) { - const NodeBS& inter = *interIt; + const PointsTo& inter = *interIt; if (cpts_copy.intersects(inter)) { // compute intersection between cpts and inter - NodeBS new_inter = inter; + PointsTo new_inter = inter; new_inter &= cpts_copy; // remove old intersection and add new one if possible @@ -189,7 +189,7 @@ void IntraDisjointMRG::computeIntersections(const NodeBS& cpts, PointsToList& in newInters.insert(new_inter); // compute complement after intersection - NodeBS complement = inter; + PointsTo complement = inter; complement.intersectWithComplement(new_inter); if (complement.empty() == false) { @@ -208,7 +208,7 @@ void IntraDisjointMRG::computeIntersections(const NodeBS& cpts, PointsToList& in for (PointsToList::const_iterator it = toBeDeleted.begin(), eit = toBeDeleted.end(); it != eit; ++it) { - const NodeBS& temp_cpts = *it; + const PointsTo& temp_cpts = *it; inters.erase(temp_cpts); } @@ -216,7 +216,7 @@ void IntraDisjointMRG::computeIntersections(const NodeBS& cpts, PointsToList& in for (PointsToList::const_iterator it = newInters.begin(), eit = newInters.end(); it != eit; ++it) { - const NodeBS& temp_cpts = *it; + const PointsTo& temp_cpts = *it; inters.insert(temp_cpts); } @@ -229,7 +229,7 @@ void IntraDisjointMRG::computeIntersections(const NodeBS& cpts, PointsToList& in /** * Create memory regions for each points-to target. */ -void IntraDisjointMRG::createDisjointMR(const SVFFunction* func, const NodeBS& cpts) +void IntraDisjointMRG::createDisjointMR(const SVFFunction* func, const PointsTo& cpts) { // set the rep cpts as itself. cptsToRepCPtsMap[cpts] = cpts; @@ -238,13 +238,13 @@ void IntraDisjointMRG::createDisjointMR(const SVFFunction* func, const NodeBS& c createMR(func, cpts); } -void IntraDisjointMRG::getMRsForLoadFromInterList(MRSet& mrs, const NodeBS& cpts, const PointsToList& inters) +void IntraDisjointMRG::getMRsForLoadFromInterList(MRSet& mrs, const PointsTo& cpts, const PointsToList& inters) { PointsToList::const_iterator it = inters.begin(); PointsToList::const_iterator eit = inters.end(); for (; it != eit; ++it) { - const NodeBS& inter = *it; + const PointsTo& inter = *it; if (cpts.contains(inter)) { MemRegion mr(inter); @@ -259,7 +259,7 @@ void IntraDisjointMRG::getMRsForLoadFromInterList(MRSet& mrs, const NodeBS& cpts * Get memory regions to be inserted at a load statement. * Just process as getMRsForLoad(). */ -void IntraDisjointMRG::getMRsForCallSiteRef(MRSet& aliasMRs, const NodeBS& cpts, const SVFFunction* fun) +void IntraDisjointMRG::getMRsForCallSiteRef(MRSet& aliasMRs, const PointsTo& cpts, const SVFFunction* fun) { getMRsForLoad(aliasMRs, cpts, fun); } @@ -269,20 +269,20 @@ void IntraDisjointMRG::getMRsForCallSiteRef(MRSet& aliasMRs, const NodeBS& cpts, void InterDisjointMRG::partitionMRs() { /// Generate disjoint cpts. - for(FunToPointsTosMap::iterator it = getFunToPointsToList().begin(), + for(FunToPointsToMap::iterator it = getFunToPointsToList().begin(), eit = getFunToPointsToList().end(); it!=eit; ++it) { for(PointsToList::iterator cit = it->second.begin(), ecit = it->second.end(); cit!=ecit; ++cit) { - const NodeBS& cpts = *cit; + const PointsTo& cpts = *cit; computeIntersections(cpts, inters); } } /// Create memory regions. - for(FunToPointsTosMap::iterator it = getFunToPointsToList().begin(), + for(FunToPointsToMap::iterator it = getFunToPointsToList().begin(), eit = getFunToPointsToList().end(); it!=eit; ++it) { const SVFFunction* fun = it->first; @@ -290,12 +290,12 @@ void InterDisjointMRG::partitionMRs() for(PointsToList::iterator cit = it->second.begin(), ecit = it->second.end(); cit!=ecit; ++cit) { - const NodeBS& cpts = *cit; + const PointsTo& cpts = *cit; for (PointsToList::const_iterator interIt = inters.begin(), interEit = inters.end(); interIt != interEit; ++interIt) { - const NodeBS& inter = *interIt; + const PointsTo& inter = *interIt; if (cpts.contains(inter)) createDisjointMR(fun, inter); } diff --git a/svf/lib/MSSA/MemRegion.cpp b/svf/lib/MSSA/MemRegion.cpp index 967c99cb8..fb55fc980 100644 --- a/svf/lib/MSSA/MemRegion.cpp +++ b/svf/lib/MSSA/MemRegion.cpp @@ -6,16 +6,16 @@ // // This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU Affero General Public License as published by +// it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Affero General Public License for more details. +// GNU General Public License for more details. -// You should have received a copy of the GNU Affero General Public License +// You should have received a copy of the GNU General Public License // along with this program. If not, see . // //===----------------------------------------------------------------------===// @@ -28,22 +28,17 @@ */ #include "Util/Options.h" -#include "SVFIR/SVFModule.h" +#include "Util/SVFModule.h" #include "MSSA/MemRegion.h" #include "MSSA/MSSAMuChi.h" +#include "SVF-FE/LLVMUtil.h" using namespace SVF; using namespace SVFUtil; -u32_t MemRegion::totalMRNum = 0; -u32_t MRVer::totalVERNum = 0; +Size_t MemRegion::totalMRNum = 0; +Size_t MRVer::totalVERNum = 0; -MRGenerator::MRGenerator(BVDataPTAImpl* p, bool ptrOnly) : - pta(p), ptrOnlyMSSA(ptrOnly) -{ - callGraph = pta->getPTACallGraph(); - callGraphSCC = new SCC(callGraph); -} /*! * Clean up memory @@ -66,9 +61,9 @@ void MRGenerator::destroy() /*! * Generate a memory region and put in into functions which use it */ -void MRGenerator::createMR(const SVFFunction* fun, const NodeBS& cpts) +void MRGenerator::createMR(const SVFFunction* fun, const PointsTo& cpts) { - const NodeBS& repCPts = getRepPointsTo(cpts); + const PointsTo& repCPts = getRepPointsTo(cpts); MemRegion mr(repCPts); MRSet::const_iterator mit = memRegSet.find(&mr); if(mit!=memRegSet.end()) @@ -89,7 +84,7 @@ void MRGenerator::createMR(const SVFFunction* fun, const NodeBS& cpts) /*! * Generate a memory region and put in into functions which use it */ -const MemRegion* MRGenerator::getMR(const NodeBS& cpts) const +const MemRegion* MRGenerator::getMR(const PointsTo& cpts) const { MemRegion mr(getRepPointsTo(cpts)); MRSet::iterator mit = memRegSet.find(&mr); @@ -103,10 +98,10 @@ const MemRegion* MRGenerator::getMR(const NodeBS& cpts) const */ void MRGenerator::collectGlobals() { - SVFIR* pag = pta->getPAG(); - for (SVFIR::iterator nIter = pag->begin(); nIter != pag->end(); ++nIter) + PAG* pag = pta->getPAG(); + for (PAG::iterator nIter = pag->begin(); nIter != pag->end(); ++nIter) { - if(ObjVar* obj = SVFUtil::dyn_cast(nIter->second)) + if(ObjPN* obj = SVFUtil::dyn_cast(nIter->second)) { if (obj->getMemObj()->isGlobalObj()) { @@ -135,7 +130,7 @@ void MRGenerator::generateMRs() /// collect mod-ref for loads/stores collectModRefForLoadStore(); - DBOUT(DGENERAL, outs() << pasMsg("\tCollect ModRef For const CallICFGNode*\n")); + DBOUT(DGENERAL, outs() << pasMsg("\tCollect ModRef For const CallBlockNode*\n")); /// collect mod-ref for calls collectModRefForCall(); @@ -147,24 +142,6 @@ void MRGenerator::generateMRs() updateAliasMRs(); } -bool MRGenerator::hasSVFStmtList(const SVFInstruction* inst) -{ - SVFIR* pag = pta->getPAG(); - if (ptrOnlyMSSA) - return pag->hasPTASVFStmtList(pag->getICFG()->getICFGNode(inst)); - else - return pag->hasSVFStmtList(pag->getICFG()->getICFGNode(inst)); -} - -SVFIR::SVFStmtList& MRGenerator::getPAGEdgesFromInst(const SVFInstruction* inst) -{ - SVFIR* pag = pta->getPAG(); - if (ptrOnlyMSSA) - return pag->getPTASVFStmtList(pag->getICFG()->getICFGNode(inst)); - else - return pag->getSVFStmtList(pag->getICFG()->getICFGNode(inst)); -} - /*! * Generate memory regions for loads/stores */ @@ -178,26 +155,26 @@ void MRGenerator::collectModRefForLoadStore() const SVFFunction& fun = **fi; /// if this function does not have any caller, then we do not care its MSSA - if (Options::IgnoreDeadFun() && fun.isUncalledFunction()) + if (Options::IgnoreDeadFun && isDeadFunction(fun.getLLVMFun())) continue; - for (SVFFunction::const_iterator iter = fun.begin(), eiter = fun.end(); + for (Function::const_iterator iter = fun.getLLVMFun()->begin(), eiter = fun.getLLVMFun()->end(); iter != eiter; ++iter) { - const SVFBasicBlock* bb = *iter; - for (SVFBasicBlock::const_iterator bit = bb->begin(), ebit = bb->end(); + const BasicBlock& bb = *iter; + for (BasicBlock::const_iterator bit = bb.begin(), ebit = bb.end(); bit != ebit; ++bit) { - const SVFInstruction* svfInst = *bit; - SVFStmtList& pagEdgeList = getPAGEdgesFromInst(svfInst); - for (SVFStmtList::iterator bit = pagEdgeList.begin(), ebit = + const Instruction& inst = *bit; + PAGEdgeList& pagEdgeList = getPAGEdgesFromInst(&inst); + for (PAGEdgeList::iterator bit = pagEdgeList.begin(), ebit = pagEdgeList.end(); bit != ebit; ++bit) { const PAGEdge* inst = *bit; pagEdgeToFunMap[inst] = &fun; - if (const StoreStmt *st = SVFUtil::dyn_cast(inst)) + if (const StorePE *st = SVFUtil::dyn_cast(inst)) { - NodeBS cpts(pta->getPts(st->getLHSVarID()).toNodeBS()); + PointsTo cpts(pta->getPts(st->getDstID())); // TODO: change this assertion check later when we have conditional points-to set if (cpts.empty()) continue; @@ -205,9 +182,9 @@ void MRGenerator::collectModRefForLoadStore() addCPtsToStore(cpts, st, &fun); } - else if (const LoadStmt *ld = SVFUtil::dyn_cast(inst)) + else if (const LoadPE *ld = SVFUtil::dyn_cast(inst)) { - NodeBS cpts(pta->getPts(ld->getRHSVarID()).toNodeBS()); + PointsTo cpts(pta->getPts(ld->getSrcID())); // TODO: change this assertion check later when we have conditional points-to set if (cpts.empty()) continue; @@ -230,7 +207,7 @@ void MRGenerator::collectModRefForCall() DBOUT(DGENERAL, outs() << pasMsg("\t\tCollect Callsite PointsTo \n")); /// collect points-to information for callsites - for(SVFIR::CallSiteSet::const_iterator it = pta->getPAG()->getCallSiteSet().begin(), + for(PAG::CallSiteSet::const_iterator it = pta->getPAG()->getCallSiteSet().begin(), eit = pta->getPAG()->getCallSiteSet().end(); it!=eit; ++it) { collectCallSitePts((*it)); @@ -256,8 +233,9 @@ void MRGenerator::collectModRefForCall() DBOUT(DGENERAL, outs() << pasMsg("\t\tAdd PointsTo to Callsites \n")); - for (const CallICFGNode* callBlockNode : pta->getPAG()->getCallSiteSet()) + for (CallSite cs : SymbolTableInfo::SymbolInfo()->getCallSiteSet()) { + const CallBlockNode* callBlockNode = pta->getPAG()->getICFG()->getCallBlockNode(cs.getInstruction()); if(hasRefSideEffectOfCallSite(callBlockNode)) { NodeBS refs = getRefSideEffectOfCallSite(callBlockNode); @@ -279,18 +257,18 @@ void MRGenerator::collectModRefForCall() * 1) map cpts to its superset(rep) which exists in the map, otherwise its superset is itself * 2) adjust existing items in the map if their supersets are cpts */ -void MRGenerator::sortPointsTo(const NodeBS& cpts) +void MRGenerator::sortPointsTo(const PointsTo& cpts) { if(cptsToRepCPtsMap.find(cpts)!=cptsToRepCPtsMap.end()) return; PointsToList subSetList; - NodeBS repCPts = cpts; + PointsTo repCPts = cpts; for(PtsToRepPtsSetMap::iterator it = cptsToRepCPtsMap.begin(), eit = cptsToRepCPtsMap.end(); it!=eit; ++it) { - NodeBS& existCPts = it->second; + PointsTo& existCPts = it->second; if(cpts.contains(existCPts)) { subSetList.insert(it->first); @@ -319,7 +297,7 @@ void MRGenerator::partitionMRs() /// TODO: we may need some refined region partitioning algorithm here /// For now, we just collapse all refs/mods objects at callsites into one region /// Consider modularly partition memory regions to speed up analysis (only partition regions within function scope) - for(FunToPointsTosMap::iterator it = getFunToPointsToList().begin(), eit = getFunToPointsToList().end(); + for(FunToPointsToMap::iterator it = getFunToPointsToList().begin(), eit = getFunToPointsToList().end(); it!=eit; ++it) { for(PointsToList::iterator cit = it->second.begin(), ecit = it->second.end(); cit!=ecit; ++cit) @@ -328,7 +306,7 @@ void MRGenerator::partitionMRs() } } /// Generate memory regions according to condition pts after computing superset - for(FunToPointsTosMap::iterator it = getFunToPointsToList().begin(), eit = getFunToPointsToList().end(); + for(FunToPointsToMap::iterator it = getFunToPointsToList().begin(), eit = getFunToPointsToList().end(); it!=eit; ++it) { const SVFFunction* fun = it->first; @@ -351,7 +329,7 @@ void MRGenerator::updateAliasMRs() { MRSet aliasMRs; const SVFFunction* fun = getFunction(it->first); - const NodeBS& storeCPts = it->second; + const PointsTo& storeCPts = it->second; getAliasMemRegions(aliasMRs,storeCPts,fun); for(MRSet::iterator ait = aliasMRs.begin(), eait = aliasMRs.end(); ait!=eait; ++ait) { @@ -363,7 +341,7 @@ void MRGenerator::updateAliasMRs() { MRSet aliasMRs; const SVFFunction* fun = getFunction(it->first); - const NodeBS& loadCPts = it->second; + const PointsTo& loadCPts = it->second; getMRsForLoad(aliasMRs, loadCPts, fun); for(MRSet::iterator ait = aliasMRs.begin(), eait = aliasMRs.end(); ait!=eait; ++ait) { @@ -377,7 +355,7 @@ void MRGenerator::updateAliasMRs() { const SVFFunction* fun = it->first->getCaller(); MRSet aliasMRs; - const NodeBS& callsiteModCPts = it->second; + const PointsTo& callsiteModCPts = it->second; getAliasMemRegions(aliasMRs,callsiteModCPts,fun); for(MRSet::iterator ait = aliasMRs.begin(), eait = aliasMRs.end(); ait!=eait; ++ait) { @@ -389,7 +367,7 @@ void MRGenerator::updateAliasMRs() { const SVFFunction* fun = it->first->getCaller(); MRSet aliasMRs; - const NodeBS& callsiteRefCPts = it->second; + const PointsTo& callsiteRefCPts = it->second; getMRsForCallSiteRef(aliasMRs, callsiteRefCPts, fun); for(MRSet::iterator ait = aliasMRs.begin(), eait = aliasMRs.end(); ait!=eait; ++ait) { @@ -426,7 +404,7 @@ void MRGenerator::addModSideEffectOfFunction(const SVFFunction* fun, const NodeB /*! * Add indirect uses an memory object in the function */ -bool MRGenerator::addRefSideEffectOfCallSite(const CallICFGNode* cs, const NodeBS& refs) +bool MRGenerator::addRefSideEffectOfCallSite(const CallBlockNode* cs, const NodeBS& refs) { if(!refs.empty()) { @@ -442,7 +420,7 @@ bool MRGenerator::addRefSideEffectOfCallSite(const CallICFGNode* cs, const NodeB /*! * Add indirect def an memory object in the function */ -bool MRGenerator::addModSideEffectOfCallSite(const CallICFGNode* cs, const NodeBS& mods) +bool MRGenerator::addModSideEffectOfCallSite(const CallBlockNode* cs, const NodeBS& mods) { if(!mods.empty()) { @@ -474,19 +452,19 @@ void MRGenerator::getCallGraphSCCRevTopoOrder(WorkList& worklist) /*! * Get all objects might pass into and pass out of callee(s) from a callsite */ -void MRGenerator::collectCallSitePts(const CallICFGNode* cs) +void MRGenerator::collectCallSitePts(const CallBlockNode* cs) { /// collect the pts chain of the callsite arguments NodeBS& argsPts = csToCallSiteArgsPtsMap[cs]; - SVFIR* pag = pta->getPAG(); - CallICFGNode* callBlockNode = pag->getICFG()->getCallICFGNode(cs->getCallSite()); - RetICFGNode* retBlockNode = pag->getICFG()->getRetICFGNode(cs->getCallSite()); + PAG* pag = pta->getPAG(); + CallBlockNode* callBlockNode = pag->getICFG()->getCallBlockNode(cs->getCallSite()); + RetBlockNode* retBlockNode = pag->getICFG()->getRetBlockNode(cs->getCallSite()); WorkList worklist; if (pag->hasCallSiteArgsMap(callBlockNode)) { - const SVFIR::SVFVarList& args = pta->getPAG()->getCallSiteArgsList(callBlockNode); - for(SVFIR::SVFVarList::const_iterator itA = args.begin(), ieA = args.end(); itA!=ieA; ++itA) + const PAG::PAGNodeList& args = pta->getPAG()->getCallSiteArgsList(callBlockNode); + for(PAG::PAGNodeList::const_iterator itA = args.begin(), ieA = args.end(); itA!=ieA; ++itA) { const PAGNode* node = *itA; if(node->isPointer()) @@ -497,8 +475,8 @@ void MRGenerator::collectCallSitePts(const CallICFGNode* cs) while(!worklist.empty()) { NodeID nodeId = worklist.pop(); - const NodeBS& tmp = pta->getPts(nodeId).toNodeBS(); - for(NodeBS::iterator it = tmp.begin(), eit = tmp.end(); it!=eit; ++it) + const PointsTo& tmp = pta->getPts(nodeId); + for(PointsTo::iterator it = tmp.begin(), eit = tmp.end(); it!=eit; ++it) argsPts |= CollectPtsChain(*it); } @@ -510,8 +488,8 @@ void MRGenerator::collectCallSitePts(const CallICFGNode* cs) const PAGNode* node = pta->getPAG()->getCallSiteRet(retBlockNode); if(node->isPointer()) { - const NodeBS& tmp = pta->getPts(node->getId()).toNodeBS(); - for(NodeBS::iterator it = tmp.begin(), eit = tmp.end(); it!=eit; ++it) + const PointsTo& tmp = pta->getPts(node->getId()); + for(PointsTo::iterator it = tmp.begin(), eit = tmp.end(); it!=eit; ++it) retPts |= CollectPtsChain(*it); } } @@ -520,28 +498,28 @@ void MRGenerator::collectCallSitePts(const CallICFGNode* cs) /*! - * Recursively collect all points-to of the whole struct fields + * Recurisively collect all points-to of the whole struct fields */ NodeBS& MRGenerator::CollectPtsChain(NodeID id) { - NodeID baseId = pta->getPAG()->getBaseObjVar(id); + NodeID baseId = pta->getPAG()->getBaseObjNode(id); NodeToPTSSMap::iterator it = cachedPtsChainMap.find(baseId); if(it!=cachedPtsChainMap.end()) return it->second; else { - NodeBS& pts = cachedPtsChainMap[baseId]; + PointsTo& pts = cachedPtsChainMap[baseId]; pts |= pta->getPAG()->getFieldsAfterCollapse(baseId); WorkList worklist; - for(NodeBS::iterator it = pts.begin(), eit = pts.end(); it!=eit; ++it) + for(PointsTo::iterator it = pts.begin(), eit = pts.end(); it!=eit; ++it) worklist.push(*it); while(!worklist.empty()) { NodeID nodeId = worklist.pop(); - const NodeBS& tmp = pta->getPts(nodeId).toNodeBS(); - for(NodeBS::iterator it = tmp.begin(), eit = tmp.end(); it!=eit; ++it) + const PointsTo& tmp = pta->getPts(nodeId); + for(PointsTo::iterator it = tmp.begin(), eit = tmp.end(); it!=eit; ++it) { pts |= CollectPtsChain(*it); } @@ -561,7 +539,6 @@ void MRGenerator::getEscapObjviaGlobals(NodeBS& globs, const NodeBS& calleeModRe for(NodeBS::iterator it = calleeModRef.begin(), eit = calleeModRef.end(); it!=eit; ++it) { const MemObj* obj = pta->getPAG()->getObject(*it); - (void)obj; // Suppress warning of unused variable under release build assert(obj && "object not found!!"); if(allGlobals.test(*it)) globs.set(*it); @@ -583,12 +560,13 @@ bool MRGenerator::isNonLocalObject(NodeID id, const SVFFunction* curFun) const /// or a local variable is in function recursion cycles else if(obj->isStack()) { - if(const SVFFunction* svffun = pta->getPAG()->getGNode(id)->getFunction()) + if(const AllocaInst* local = SVFUtil::dyn_cast(obj->getRefVal())) { - if(svffun!=curFun) + const SVFFunction* fun = LLVMModuleSet::getLLVMModuleSet()->getSVFFunction(local->getFunction()); + if(fun!=curFun) return true; else - return callGraphSCC->isInCycle(callGraph->getCallGraphNode(svffun)->getId()); + return callGraphSCC->isInCycle(callGraph->getCallGraphNode(fun)->getId()); } } @@ -598,18 +576,18 @@ bool MRGenerator::isNonLocalObject(NodeID id, const SVFFunction* curFun) const /*! * Get Mod-Ref of a callee function */ -bool MRGenerator::handleCallsiteModRef(NodeBS& mod, NodeBS& ref, const CallICFGNode* cs, const SVFFunction* callee) +bool MRGenerator::handleCallsiteModRef(NodeBS& mod, NodeBS& ref, const CallBlockNode* cs, const SVFFunction* callee) { /// if a callee is a heap allocator function, then its mod set of this callsite is the heap object. if(isHeapAllocExtCall(cs->getCallSite())) { - SVFStmtList& pagEdgeList = getPAGEdgesFromInst(cs->getCallSite()); - for (SVFStmtList::const_iterator bit = pagEdgeList.begin(), + PAGEdgeList& pagEdgeList = getPAGEdgesFromInst(cs->getCallSite()); + for (PAGEdgeList::const_iterator bit = pagEdgeList.begin(), ebit = pagEdgeList.end(); bit != ebit; ++bit) { const PAGEdge* edge = *bit; - if (const AddrStmt* addr = SVFUtil::dyn_cast(edge)) - mod.set(addr->getRHSVarID()); + if (const AddrPE* addr = SVFUtil::dyn_cast(edge)) + mod.set(addr->getSrcID()); } } /// otherwise, we find the mod/ref sets from the callee function, who has definition and been processed @@ -644,7 +622,7 @@ void MRGenerator::modRefAnalysis(PTACallGraphNode* callGraphNode, WorkList& work ecit = edge->getDirectCalls().end(); cit!=ecit; ++cit) { NodeBS mod, ref; - const CallICFGNode* cs = (*cit); + const CallBlockNode* cs = (*cit); bool modrefchanged = handleCallsiteModRef(mod, ref, cs, callGraphNode->getFunction()); if(modrefchanged) worklist.push(edge->getSrcID()); @@ -654,7 +632,7 @@ void MRGenerator::modRefAnalysis(PTACallGraphNode* callGraphNode, WorkList& work ecit = edge->getIndirectCalls().end(); cit!=ecit; ++cit) { NodeBS mod, ref; - const CallICFGNode* cs = (*cit); + const CallBlockNode* cs = (*cit); bool modrefchanged = handleCallsiteModRef(mod, ref, cs, callGraphNode->getFunction()); if(modrefchanged) worklist.push(edge->getSrcID()); @@ -665,18 +643,18 @@ void MRGenerator::modRefAnalysis(PTACallGraphNode* callGraphNode, WorkList& work /*! * Obtain the mod sets for a call, used for external ModRefInfo queries */ -NodeBS MRGenerator::getModInfoForCall(const CallICFGNode* cs) +PointsTo MRGenerator::getModInfoForCall(const CallBlockNode* cs) { if (isExtCall(cs->getCallSite()) && !isHeapAllocExtCall(cs->getCallSite())) { - SVFStmtList& pagEdgeList = getPAGEdgesFromInst(cs->getCallSite()); - NodeBS mods; - for (SVFStmtList::const_iterator bit = pagEdgeList.begin(), ebit = + PAGEdgeList& pagEdgeList = getPAGEdgesFromInst(cs->getCallSite()); + PointsTo mods; + for (PAGEdgeList::const_iterator bit = pagEdgeList.begin(), ebit = pagEdgeList.end(); bit != ebit; ++bit) { const PAGEdge* edge = *bit; - if (const StoreStmt* st = SVFUtil::dyn_cast(edge)) - mods |= pta->getPts(st->getLHSVarID()).toNodeBS(); + if (const StorePE* st = SVFUtil::dyn_cast(edge)) + mods |= pta->getPts(st->getDstID()); } return mods; } @@ -689,18 +667,18 @@ NodeBS MRGenerator::getModInfoForCall(const CallICFGNode* cs) /*! * Obtain the ref sets for a call, used for external ModRefInfo queries */ -NodeBS MRGenerator::getRefInfoForCall(const CallICFGNode* cs) +PointsTo MRGenerator::getRefInfoForCall(const CallBlockNode* cs) { if (isExtCall(cs->getCallSite()) && !isHeapAllocExtCall(cs->getCallSite())) { - SVFStmtList& pagEdgeList = getPAGEdgesFromInst(cs->getCallSite()); - NodeBS refs; - for (SVFStmtList::const_iterator bit = pagEdgeList.begin(), ebit = + PAGEdgeList& pagEdgeList = getPAGEdgesFromInst(cs->getCallSite()); + PointsTo refs; + for (PAGEdgeList::const_iterator bit = pagEdgeList.begin(), ebit = pagEdgeList.end(); bit != ebit; ++bit) { const PAGEdge* edge = *bit; - if (const LoadStmt* ld = SVFUtil::dyn_cast(edge)) - refs |= pta->getPts(ld->getRHSVarID()).toNodeBS(); + if (const LoadPE* ld = SVFUtil::dyn_cast(edge)) + refs |= pta->getPts(ld->getSrcID()); } return refs; } @@ -714,7 +692,7 @@ NodeBS MRGenerator::getRefInfoForCall(const CallICFGNode* cs) * Determine whether a CallSite instruction can mod or ref * any memory location */ -ModRefInfo MRGenerator::getModRefInfo(const CallICFGNode* cs) +ModRefInfo MRGenerator::getModRefInfo(const CallBlockNode* cs) { bool ref = !getRefInfoForCall(cs).empty(); bool mod = !getModInfoForCall(cs).empty(); @@ -730,20 +708,20 @@ ModRefInfo MRGenerator::getModRefInfo(const CallICFGNode* cs) } /*! - * Determine whether a const CallICFGNode* instruction can mod or ref + * Determine whether a const CallBlockNode* instruction can mod or ref * a specific memory location pointed by V */ -ModRefInfo MRGenerator::getModRefInfo(const CallICFGNode* cs, const SVFValue* V) +ModRefInfo MRGenerator::getModRefInfo(const CallBlockNode* cs, const Value* V) { bool ref = false; bool mod = false; if (pta->getPAG()->hasValueNode(V)) { - const NodeBS pts(pta->getPts(pta->getPAG()->getValueNode(V)).toNodeBS()); - const NodeBS csRef = getRefInfoForCall(cs); - const NodeBS csMod = getModInfoForCall(cs); - NodeBS ptsExpanded, csRefExpanded, csModExpanded; + const PointsTo pts(pta->getPts(pta->getPAG()->getValueNode(V))); + const PointsTo csRef = getRefInfoForCall(cs); + const PointsTo csMod = getModInfoForCall(cs); + PointsTo ptsExpanded, csRefExpanded, csModExpanded; pta->expandFIObjs(pts, ptsExpanded); pta->expandFIObjs(csRef, csRefExpanded); pta->expandFIObjs(csMod, csModExpanded); @@ -765,9 +743,9 @@ ModRefInfo MRGenerator::getModRefInfo(const CallICFGNode* cs, const SVFValue* V) } /*! - * Determine mod-ref relations between two const CallICFGNode* instructions + * Determine mod-ref relations between two const CallBlockNode* instructions */ -ModRefInfo MRGenerator::getModRefInfo(const CallICFGNode* cs1, const CallICFGNode* cs2) +ModRefInfo MRGenerator::getModRefInfo(const CallBlockNode* cs1, const CallBlockNode* cs2) { bool ref = false; bool mod = false; @@ -776,11 +754,11 @@ ModRefInfo MRGenerator::getModRefInfo(const CallICFGNode* cs1, const CallICFGNod if (getModRefInfo(cs1) == ModRefInfo::NoModRef || getModRefInfo(cs2) == ModRefInfo::NoModRef) return ModRefInfo::NoModRef; - const NodeBS cs1Ref = getRefInfoForCall(cs1); - const NodeBS cs1Mod = getModInfoForCall(cs1); - const NodeBS cs2Ref = getRefInfoForCall(cs2); - const NodeBS cs2Mod = getModInfoForCall(cs2); - NodeBS cs1RefExpanded, cs1ModExpanded, cs2RefExpanded, cs2ModExpanded; + const PointsTo cs1Ref = getRefInfoForCall(cs1); + const PointsTo cs1Mod = getModInfoForCall(cs1); + const PointsTo cs2Ref = getRefInfoForCall(cs2); + const PointsTo cs2Mod = getModInfoForCall(cs2); + PointsTo cs1RefExpanded, cs1ModExpanded, cs2RefExpanded, cs2ModExpanded; pta->expandFIObjs(cs1Ref, cs1RefExpanded); pta->expandFIObjs(cs1Mod, cs1ModExpanded); pta->expandFIObjs(cs2Ref, cs2RefExpanded); @@ -805,10 +783,3 @@ ModRefInfo MRGenerator::getModRefInfo(const CallICFGNode* cs1, const CallICFGNod else return ModRefInfo::NoModRef; } - -std::ostream& SVF::operator<<(std::ostream &o, const MRVer& mrver) -{ - o << "MRVERID: " << mrver.getID() <<" MemRegion: " << mrver.getMR()->dumpStr() << " MRVERSION: " << mrver.getSSAVersion() << " MSSADef: " << mrver.getDef()->getType() << ", " - << mrver.getDef()->getMR()->dumpStr() ; - return o; -} diff --git a/svf/lib/MSSA/MemSSA.cpp b/svf/lib/MSSA/MemSSA.cpp index 5f5b5622e..437f8adca 100644 --- a/svf/lib/MSSA/MemSSA.cpp +++ b/svf/lib/MSSA/MemSSA.cpp @@ -6,16 +6,16 @@ // // This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU Affero General Public License as published by +// it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Affero General Public License for more details. +// GNU General Public License for more details. -// You should have received a copy of the GNU Affero General Public License +// You should have received a copy of the GNU General Public License // along with this program. If not, see . // //===----------------------------------------------------------------------===// @@ -28,6 +28,7 @@ */ #include "Util/Options.h" +#include "SVF-FE/LLVMUtil.h" #include "MSSA/MemPartition.h" #include "MSSA/MemSSA.h" #include "Graphs/SVFGStat.h" @@ -35,6 +36,7 @@ using namespace SVF; using namespace SVFUtil; + double MemSSA::timeOfGeneratingMemRegions = 0; ///< Time for allocating regions double MemSSA::timeOfCreateMUCHI = 0; ///< Time for generating mu/chi for load/store/calls double MemSSA::timeOfInsertingPHI = 0; ///< Time for inserting phis @@ -43,17 +45,17 @@ double MemSSA::timeOfSSARenaming = 0; ///< Time for SSA rename /*! * Constructor */ -MemSSA::MemSSA(BVDataPTAImpl* p, bool ptrOnlyMSSA) +MemSSA::MemSSA(BVDataPTAImpl* p, bool ptrOnlyMSSA) : df(nullptr),dt(nullptr) { pta = p; assert((pta->getAnalysisTy()!=PointerAnalysis::Default_PTA) && "please specify a pointer analysis"); - if (Options::MemPar() == MemPartition::Distinct) + if (Options::MemPar == MemPartition::Distinct) mrGen = new DistinctMRG(pta, ptrOnlyMSSA); - else if (Options::MemPar() == MemPartition::IntraDisjoint) + else if (Options::MemPar == MemPartition::IntraDisjoint) mrGen = new IntraDisjointMRG(pta, ptrOnlyMSSA); - else if (Options::MemPar() == MemPartition::InterDisjoint) + else if (Options::MemPar == MemPartition::InterDisjoint) mrGen = new InterDisjointMRG(pta, ptrOnlyMSSA); else assert(false && "unrecognised memory partition strategy"); @@ -65,18 +67,24 @@ MemSSA::MemSSA(BVDataPTAImpl* p, bool ptrOnlyMSSA) double mrStart = stat->getClk(true); mrGen->generateMRs(); double mrEnd = stat->getClk(true); - timeOfGeneratingMemRegions = (mrEnd - mrStart)/TIMEINTERVAL; + timeOfGeneratingMemRegions += (mrEnd - mrStart)/TIMEINTERVAL; } -SVFIR* MemSSA::getPAG() +/*! + * Set DF/DT + */ +void MemSSA::setCurrentDFDT(DominanceFrontier* f, DominatorTree* t) { - return pta->getPAG(); + df = f; + dt = t; + usedRegs.clear(); + reg2BBMap.clear(); } /*! * Start building memory SSA */ -void MemSSA::buildMemSSA(const SVFFunction& fun) +void MemSSA::buildMemSSA(const SVFFunction& fun, DominanceFrontier* f, DominatorTree* t) { assert(!isExtCall(&fun) && "we do not build memory ssa for external functions"); @@ -84,8 +92,7 @@ void MemSSA::buildMemSSA(const SVFFunction& fun) DBOUT(DMSSA, outs() << "Building Memory SSA for function " << fun.getName() << " \n"); - usedRegs.clear(); - reg2BBMap.clear(); + setCurrentDFDT(f,t); /// Create mus/chis for loads/stores/calls for memory regions double muchiStart = stat->getClk(true); @@ -114,7 +121,7 @@ void MemSSA::buildMemSSA(const SVFFunction& fun) void MemSSA::createMUCHI(const SVFFunction& fun) { - SVFIR* pag = pta->getPAG(); + PAG* pag = pta->getPAG(); DBOUT(DMSSA, outs() << "\t creating mu chi for function " << fun.getName() @@ -135,32 +142,34 @@ void MemSSA::createMUCHI(const SVFFunction& fun) /// get all reachable basic blocks from function entry /// ignore dead basic blocks - BBList reachableBBs = fun.getReachableBBs(); + BBList reachableBBs; + getFunReachableBBs(fun.getLLVMFun(),getDT(fun),reachableBBs); for (BBList::const_iterator iter = reachableBBs.begin(), eiter = reachableBBs.end(); iter != eiter; ++iter) { - const SVFBasicBlock* bb = *iter; + const BasicBlock* bb = *iter; varKills.clear(); - for (SVFBasicBlock::const_iterator it = bb->begin(), eit = bb->end(); it != eit; ++it) + for (BasicBlock::const_iterator it = bb->begin(), eit = bb->end(); + it != eit; ++it) { - const SVFInstruction* inst = *it; - if(mrGen->hasSVFStmtList(inst)) + const Instruction* inst = &*it; + if(mrGen->hasPAGEdgeList(inst)) { - SVFStmtList& pagEdgeList = mrGen->getPAGEdgesFromInst(inst); - for (SVFStmtList::const_iterator bit = pagEdgeList.begin(), + PAGEdgeList& pagEdgeList = mrGen->getPAGEdgesFromInst(inst); + for (PAGEdgeList::const_iterator bit = pagEdgeList.begin(), ebit = pagEdgeList.end(); bit != ebit; ++bit) { const PAGEdge* inst = *bit; - if (const LoadStmt* load = SVFUtil::dyn_cast(inst)) + if (const LoadPE* load = SVFUtil::dyn_cast(inst)) AddLoadMU(bb, load, mrGen->getLoadMRSet(load)); - else if (const StoreStmt* store = SVFUtil::dyn_cast(inst)) + else if (const StorePE* store = SVFUtil::dyn_cast(inst)) AddStoreCHI(bb, store, mrGen->getStoreMRSet(store)); } } if (isNonInstricCallSite(inst)) { - const CallICFGNode* cs = pag->getICFG()->getCallICFGNode(inst); + const CallBlockNode* cs = pag->getICFG()->getCallBlockNode(inst); if(mrGen->hasRefMRSet(cs)) AddCallSiteMU(cs,mrGen->getCallSiteRefMRSet(cs)); @@ -186,7 +195,7 @@ void MemSSA::createMUCHI(const SVFFunction& fun) /// if the function does not have a reachable return instruction from function entry /// then we won't create return mu for it - if(fun.hasReturn()) + if(functionDoesNotRet(fun.getLLVMFun()) == false) { RETMU* mu = new RETMU(&fun, mr); funToReturnMuSetMap[&fun].insert(mu); @@ -205,7 +214,7 @@ void MemSSA::insertPHI(const SVFFunction& fun) DBOUT(DMSSA, outs() << "\t insert phi for function " << fun.getName() << "\n"); - const Map>& df = fun.getDomFrontierMap(); + const DominanceFrontier* df = getDF(fun); // record whether a phi of mr has already been inserted into the bb. BBToMRSetMap bb2MRSetMap; @@ -218,17 +227,19 @@ void MemSSA::insertPHI(const SVFFunction& fun) BBList bbs = reg2BBMap[mr]; while (!bbs.empty()) { - const SVFBasicBlock* bb = bbs.back(); + const BasicBlock* bb = bbs.back(); bbs.pop_back(); - Map>::const_iterator it = df.find(bb); - if(it == df.end()) + DominanceFrontierBase::const_iterator it = df->find(const_cast(bb)); + if(it == df->end()) { writeWrnMsg("bb not in the dominance frontier map??"); continue; } - const Set& domSet = it->second; - for (const SVFBasicBlock* pbb : domSet) + const DominanceFrontierBase::DomSetType& domSet = it->second; + for (DominanceFrontierBase::DomSetType::const_iterator bit = + domSet.begin(); bit != domSet.end(); ++bit) { + const BasicBlock* pbb = *bit; // if we never insert this phi node before if (0 == bb2MRSetMap[pbb].count(mr)) { @@ -253,17 +264,17 @@ void MemSSA::SSARename(const SVFFunction& fun) DBOUT(DMSSA, outs() << "\t ssa rename for function " << fun.getName() << "\n"); - SSARenameBB(*fun.getEntryBlock()); + SSARenameBB(fun.getLLVMFun()->getEntryBlock()); } /*! * Renaming for each memory regions * See the renaming algorithm in book Engineering A Compiler (Figure 9.12) */ -void MemSSA::SSARenameBB(const SVFBasicBlock& bb) +void MemSSA::SSARenameBB(const BasicBlock& bb) { - SVFIR* pag = pta->getPAG(); + PAG* pag = pta->getPAG(); // record which mem region needs to pop stack MRVector memRegs; @@ -281,60 +292,61 @@ void MemSSA::SSARenameBB(const SVFBasicBlock& bb) // rewrite r' with top mrver of stack(r) // rewrite r with new name - for (SVFBasicBlock::const_iterator it = bb.begin(), eit = bb.end(); + for (BasicBlock::const_iterator it = bb.begin(), eit = bb.end(); it != eit; ++it) { - const SVFInstruction* inst = *it; - if(mrGen->hasSVFStmtList(inst)) + const Instruction* inst = &*it; + if(mrGen->hasPAGEdgeList(inst)) { - SVFStmtList& pagEdgeList = mrGen->getPAGEdgesFromInst(inst); - for(SVFStmtList::const_iterator bit = pagEdgeList.begin(), ebit= pagEdgeList.end(); + PAGEdgeList& pagEdgeList = mrGen->getPAGEdgesFromInst(inst); + for(PAGEdgeList::const_iterator bit = pagEdgeList.begin(), ebit= pagEdgeList.end(); bit!=ebit; ++bit) { const PAGEdge* inst = *bit; - if (const LoadStmt* load = SVFUtil::dyn_cast(inst)) + if (const LoadPE* load = SVFUtil::dyn_cast(inst)) RenameMuSet(getMUSet(load)); - else if (const StoreStmt* store = SVFUtil::dyn_cast(inst)) + else if (const StorePE* store = SVFUtil::dyn_cast(inst)) RenameChiSet(getCHISet(store),memRegs); } } if (isNonInstricCallSite(inst)) { - const CallICFGNode* cs = pag->getICFG()->getCallICFGNode(inst); + const CallBlockNode* cs = pag->getICFG()->getCallBlockNode(inst); if(mrGen->hasRefMRSet(cs)) RenameMuSet(getMUSet(cs)); if(mrGen->hasModMRSet(cs)) RenameChiSet(getCHISet(cs),memRegs); } - else if(inst->isRetInst()) + else if(isReturn(inst)) { - const SVFFunction* fun = bb.getParent(); + const SVFFunction* fun = LLVMModuleSet::getLLVMModuleSet()->getSVFFunction(bb.getParent()); RenameMuSet(getReturnMuSet(fun)); } } // fill phi operands of succ basic blocks - for (const SVFBasicBlock* succ : bb.getSuccessors()) + for (succ_const_iterator sit = succ_begin(&bb), esit = succ_end(&bb); + sit != esit; ++sit) { - u32_t pos = bb.getBBPredecessorPos(succ); + const BasicBlock* succ = *sit; + u32_t pos = getBBPredecessorPos(&bb, succ); if (hasPHISet(succ)) RenamePhiOps(getPHISet(succ),pos,memRegs); } // for succ basic block in dominator tree - const SVFFunction* fun = bb.getParent(); - const Map>& dtBBsMap = fun->getDomTreeMap(); - Map>::const_iterator mapIter = dtBBsMap.find(&bb); - if (mapIter != dtBBsMap.end()) + const SVFFunction* fun = LLVMModuleSet::getLLVMModuleSet()->getSVFFunction(bb.getParent()); + DominatorTree* dt = getDT(*fun); + if(DomTreeNode *dtNode = dt->getNode(const_cast(&bb))) { - const Set& dtBBs = mapIter->second; - for (const SVFBasicBlock* dtbb : dtBBs) + for (DomTreeNode::iterator DI = dtNode->begin(), DE = dtNode->end(); + DI != DE; ++DI) { - SSARenameBB(*dtbb); + SSARenameBB(*((*DI)->getBlock())); } } // for each r = chi(..), and r = phi(..) @@ -357,11 +369,9 @@ MRVer* MemSSA::newSSAName(const MemRegion* mr, MSSADEF* def) MRVERSION version = mr2CounterMap[mr]; mr2CounterMap[mr] = version + 1; - auto mrVer = std::make_unique(mr, version, def); - auto mrVerPtr = mrVer.get(); - mr2VerStackMap[mr].push_back(mrVerPtr); - usedMRVers.push_back(std::move(mrVer)); - return mrVerPtr; + MRVer* mrVer = new MRVer(mr, version, def); + mr2VerStackMap[mr].push_back(mrVer); + return mrVer; } /*! @@ -577,15 +587,15 @@ u32_t MemSSA::getBBPhiNum() const /*! * Print SSA */ -void MemSSA::dumpMSSA(OutStream& Out) +void MemSSA::dumpMSSA(raw_ostream& Out) { - SVFIR* pag = pta->getPAG(); + PAG* pag = pta->getPAG(); for (SVFModule::iterator fit = pta->getModule()->begin(), efit = pta->getModule()->end(); fit != efit; ++fit) { const SVFFunction* fun = *fit; - if(Options::MSSAFun()!="" && Options::MSSAFun()!=fun->getName()) + if(Options::MSSAFun!="" && Options::MSSAFun!=fun->getName()) continue; Out << "==========FUNCTION: " << fun->getName() << "==========\n"; @@ -599,26 +609,27 @@ void MemSSA::dumpMSSA(OutStream& Out) } } - for (SVFFunction::const_iterator bit = fun->begin(), ebit = fun->end(); + for (Function::iterator bit = fun->getLLVMFun()->begin(), ebit = fun->getLLVMFun()->end(); bit != ebit; ++bit) { - const SVFBasicBlock* bb = *bit; - Out << bb->getName() << "\n"; - PHISet& phiSet = getPHISet(bb); + BasicBlock& bb = *bit; + if (bb.hasName()) + Out << bb.getName() << "\n"; + PHISet& phiSet = getPHISet(&bb); for(PHISet::iterator pi = phiSet.begin(), epi = phiSet.end(); pi !=epi; ++pi) { (*pi)->dump(); } bool last_is_chi = false; - for (SVFBasicBlock::const_iterator it = bb->begin(), eit = bb->end(); + for (BasicBlock::iterator it = bb.begin(), eit = bb.end(); it != eit; ++it) { - const SVFInstruction* inst = *it; - bool isAppCall = isNonInstricCallSite(inst) && !isExtCall(inst); - if (isAppCall || isHeapAllocExtCall(inst)) + Instruction& inst = *it; + bool isAppCall = isNonInstricCallSite(&inst) && !isExtCall(&inst); + if (isAppCall || isHeapAllocExtCall(&inst)) { - const CallICFGNode* cs = pag->getICFG()->getCallICFGNode(inst); + const CallBlockNode* cs = pag->getICFG()->getCallBlockNode(&inst); if(hasMU(cs)) { if (!last_is_chi) @@ -632,7 +643,7 @@ void MemSSA::dumpMSSA(OutStream& Out) } } - Out << inst->toString() << "\n"; + Out << inst << "\n"; if(hasCHI(cs)) { @@ -650,12 +661,12 @@ void MemSSA::dumpMSSA(OutStream& Out) else { bool dump_preamble = false; - SVFStmtList& pagEdgeList = mrGen->getPAGEdgesFromInst(inst); - for(SVFStmtList::const_iterator bit = pagEdgeList.begin(), ebit= pagEdgeList.end(); + PAGEdgeList& pagEdgeList = mrGen->getPAGEdgesFromInst(&inst); + for(PAGEdgeList::const_iterator bit = pagEdgeList.begin(), ebit= pagEdgeList.end(); bit!=ebit; ++bit) { const PAGEdge* edge = *bit; - if (const LoadStmt* load = SVFUtil::dyn_cast(edge)) + if (const LoadPE* load = SVFUtil::dyn_cast(edge)) { MUSet& muSet = getMUSet(load); for(MUSet::iterator it = muSet.begin(), eit = muSet.end(); it!=eit; ++it) @@ -670,14 +681,14 @@ void MemSSA::dumpMSSA(OutStream& Out) } } - Out << inst->toString() << "\n"; + Out << inst << "\n"; bool has_chi = false; - for(SVFStmtList::const_iterator bit = pagEdgeList.begin(), ebit= pagEdgeList.end(); + for(PAGEdgeList::const_iterator bit = pagEdgeList.begin(), ebit= pagEdgeList.end(); bit!=ebit; ++bit) { const PAGEdge* edge = *bit; - if (const StoreStmt* store = SVFUtil::dyn_cast(edge)) + if (const StorePE* store = SVFUtil::dyn_cast(edge)) { CHISet& chiSet = getCHISet(store); for(CHISet::iterator it = chiSet.begin(), eit = chiSet.end(); it!=eit; ++it) diff --git a/svf/lib/MSSA/SVFGBuilder.cpp b/svf/lib/MSSA/SVFGBuilder.cpp index 33974d24a..26e221cb3 100644 --- a/svf/lib/MSSA/SVFGBuilder.cpp +++ b/svf/lib/MSSA/SVFGBuilder.cpp @@ -6,16 +6,16 @@ // // This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU Affero General Public License as published by +// it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Affero General Public License for more details. +// GNU General Public License for more details. -// You should have received a copy of the GNU Affero General Public License +// You should have received a copy of the GNU General Public License // along with this program. If not, see . // //===----------------------------------------------------------------------===// @@ -27,8 +27,8 @@ * Author: Yulei Sui */ #include "Util/Options.h" -#include "SVFIR/SVFModule.h" -#include "Util/SVFUtil.h" +#include "Util/SVFModule.h" +#include "SVF-FE/LLVMUtil.h" #include "MSSA/MemSSA.h" #include "Graphs/SVFG.h" #include "MSSA/SVFGBuilder.h" @@ -38,15 +38,25 @@ using namespace SVF; using namespace SVFUtil; +SVFG* SVFGBuilder::globalSvfg = nullptr; + + SVFG* SVFGBuilder::buildPTROnlySVFG(BVDataPTAImpl* pta) { - if(Options::OPTSVFG()) - return build(pta, VFG::PTRONLYSVFG_OPT); - else - return build(pta, VFG::PTRONLYSVFG); + return build(pta, VFG::PTRONLYSVFG_OPT); +} + +SVFG* SVFGBuilder::buildPTROnlySVFGWithoutOPT(BVDataPTAImpl* pta) +{ + return build(pta, VFG::PTRONLYSVFG); } SVFG* SVFGBuilder::buildFullSVFG(BVDataPTAImpl* pta) +{ + return build(pta, VFG::FULLSVFG_OPT); +} + +SVFG* SVFGBuilder::buildFullSVFGWithoutOPT(BVDataPTAImpl* pta) { return build(pta, VFG::FULLSVFG); } @@ -57,33 +67,48 @@ SVFG* SVFGBuilder::buildFullSVFG(BVDataPTAImpl* pta) */ void SVFGBuilder::buildSVFG() { + MemSSA* mssa = svfg->getMSSA(); svfg->buildSVFG(); + if(mssa->getPTA()->printStat()) + svfg->performStat(); } /// Create DDA SVFG SVFG* SVFGBuilder::build(BVDataPTAImpl* pta, VFG::VFGK kind) { - auto mssa = buildMSSA(pta, (VFG::PTRONLYSVFG==kind || VFG::PTRONLYSVFG_OPT==kind)); + MemSSA* mssa = buildMSSA(pta, (VFG::PTRONLYSVFG==kind || VFG::PTRONLYSVFG_OPT==kind)); DBOUT(DGENERAL, outs() << pasMsg("Build Sparse Value-Flow Graph \n")); - if(kind == VFG::FULLSVFG_OPT || kind == VFG::PTRONLYSVFG_OPT) - svfg = std::make_unique(std::move(mssa), kind); + if(Options::SingleVFG) + { + if(globalSvfg==nullptr) + { + /// Note that we use callgraph from andersen analysis here + if(kind == VFG::FULLSVFG_OPT || kind == VFG::PTRONLYSVFG_OPT) + svfg = globalSvfg = new SVFGOPT(mssa, kind); + else + svfg = globalSvfg = new SVFG(mssa, kind); + buildSVFG(); + } + } else - svfg = std::unique_ptr(new SVFG(std::move(mssa),kind)); - buildSVFG(); + { + if(kind == VFG::FULLSVFG_OPT || kind == VFG::PTRONLYSVFG_OPT) + svfg = new SVFGOPT(mssa, kind); + else + svfg = new SVFG(mssa,kind); + buildSVFG(); + } /// Update call graph using pre-analysis results - if(Options::SVFGWithIndirectCall() || SVFGWithIndCall) + if(Options::SVFGWithIndirectCall || SVFGWithIndCall) svfg->updateCallGraph(pta); - if(svfg->getMSSA()->getPTA()->printStat()) - svfg->performStat(); - - if(Options::DumpVFG()) + if(Options::DumpVFG) svfg->dump("svfg_final"); - return svfg.get(); + return svfg; } /*! @@ -94,12 +119,15 @@ void SVFGBuilder::releaseMemory() svfg->clearMSSA(); } -std::unique_ptr SVFGBuilder::buildMSSA(BVDataPTAImpl* pta, bool ptrOnlyMSSA) +MemSSA* SVFGBuilder::buildMSSA(BVDataPTAImpl* pta, bool ptrOnlyMSSA) { DBOUT(DGENERAL, outs() << pasMsg("Build Memory SSA \n")); - auto mssa = std::make_unique(pta, ptrOnlyMSSA); + MemSSA* mssa = new MemSSA(pta, ptrOnlyMSSA); + + DominatorTree dt; + MemSSADF df; SVFModule* svfModule = mssa->getPTA()->getModule(); for (SVFModule::const_iterator iter = svfModule->begin(), eiter = svfModule->end(); @@ -110,11 +138,14 @@ std::unique_ptr SVFGBuilder::buildMSSA(BVDataPTAImpl* pta, bool ptrOnlyM if (isExtCall(fun)) continue; - mssa->buildMemSSA(*fun); + dt.recalculate(*fun->getLLVMFun()); + df.runOnDT(dt); + + mssa->buildMemSSA(*fun, &df, &dt); } mssa->performStat(); - if (Options::DumpMSSA()) + if (Options::DumpMSSA) { mssa->dumpMSSA(); } diff --git a/svf/lib/MTA/FSMPTA.cpp b/svf/lib/MTA/FSMPTA.cpp index a6d12875b..2e260fbee 100644 --- a/svf/lib/MTA/FSMPTA.cpp +++ b/svf/lib/MTA/FSMPTA.cpp @@ -1,25 +1,3 @@ -//===- FSMPTA.cpp -- Flow-sensitive analysis of multithreaded programs-------------// -// -// SVF: Static Value-Flow Analysis -// -// Copyright (C) <2013-> -// - -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU Affero General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Affero General Public License for more details. - -// You should have received a copy of the GNU Affero General Public License -// along with this program. If not, see . -// -//===----------------------------------------------------------------------===// - /* * FSMPTA.cpp * @@ -31,7 +9,6 @@ #include "MTA/FSMPTA.h" #include "MTA/MHP.h" #include "MTA/PCG.h" -#include "MemoryModel/PointsTo.h" using namespace SVF; using namespace SVFUtil; @@ -48,7 +25,7 @@ void MTASVFGBuilder::buildSVFG() { MemSSA* mssa = svfg->getMSSA(); svfg->buildSVFG(); - if (ADDEDGE_NOEDGE != Options::AddModelFlag()) + if (ADDEDGE_NOEDGE != Options::AddModelFlag) { DBOUT(DGENERAL, outs() << SVFUtil::pasMsg("FSMPTA adding edge\n")); DBOUT(DMTA, outs() << SVFUtil::pasMsg("FSMPTA adding edge\n")); @@ -131,13 +108,13 @@ SVFGEdge* MTASVFGBuilder::addTDEdges(NodeID srcId, NodeID dstId, PointsTo& pts) if(SVFGEdge* edge = svfg->hasThreadVFGEdge(srcNode,dstNode,SVFGEdge::TheadMHPIndirectVF)) { assert(SVFUtil::isa(edge) && "this should be a indirect value flow edge!"); - return (SVFUtil::cast(edge)->addPointsTo(pts.toNodeBS()) ? edge : nullptr); + return (SVFUtil::cast(edge)->addPointsTo(pts) ? edge : nullptr); } else { MTASVFGBuilder::numOfNewSVFGEdges++; ThreadMHPIndSVFGEdge* indirectEdge = new ThreadMHPIndSVFGEdge(srcNode,dstNode); - indirectEdge->addPointsTo(pts.toNodeBS()); + indirectEdge->addPointsTo(pts); return (svfg->addSVFGEdge(indirectEdge) ? indirectEdge : nullptr); } } @@ -164,12 +141,12 @@ void MTASVFGBuilder::performRemovingMHPEdges() if (edge->isIndirectVFGEdge() && (edge->getDstNode()==n2)) { IndirectSVFGEdge* e = SVFUtil::cast(edge); - const NodeBS& pts = e->getPointsTo(); - for (PointsTo::iterator o = remove_pts.begin(), eo = remove_pts.end(); o != eo; ++o) + const PointsTo& pts = e->getPointsTo(); + for (NodeBS::iterator o = remove_pts.begin(), eo = remove_pts.end(); o != eo; ++o) { - if (const_cast(pts).test(*o)) + if (const_cast(pts).test(*o)) { - const_cast(pts).reset(*o); + const_cast(pts).reset(*o); MTASVFGBuilder::numOfRemovedPTS ++; } } @@ -210,7 +187,7 @@ bool MTASVFGBuilder::isHeadofSpan(const StmtSVFGNode* n, LockAnalysis::LockSpan { assert (SVFUtil::isa(svfg->getSVFGNode(*it)) && "prev is not a store node"); const StmtSVFGNode* prevNode = SVFUtil::dyn_cast(svfg->getSVFGNode(*it)); - const SVFInstruction* prevIns = prevNode->getInst(); + const Instruction* prevIns = prevNode->getInst(); if (lockana->hasOneCxtInLockSpan(prevIns, lspan)) { @@ -231,7 +208,7 @@ bool MTASVFGBuilder::isHeadofSpan(const StmtSVFGNode* n, InstSet mergespan) { assert (SVFUtil::isa(svfg->getSVFGNode(*it)) && "prev is not a store node"); const StmtSVFGNode* prevNode = SVFUtil::dyn_cast(svfg->getSVFGNode(*it)); - const SVFInstruction* prevIns = prevNode->getInst(); + const Instruction* prevIns = prevNode->getInst(); if (mergespan.find(prevIns)!=mergespan.end()) return false; } @@ -251,7 +228,7 @@ bool MTASVFGBuilder::isHeadofSpan(const StmtSVFGNode* n) { assert(SVFUtil::isa(svfg->getSVFGNode(*it)) && "prev is not a store node"); const StmtSVFGNode* prevNode = SVFUtil::dyn_cast(svfg->getSVFGNode(*it)); - const SVFInstruction* prevIns = prevNode->getInst(); + const Instruction* prevIns = prevNode->getInst(); if (lockana->isInSameSpan(prevIns, n->getInst())) { @@ -270,10 +247,9 @@ bool MTASVFGBuilder::isTailofSpan(const StmtSVFGNode* n, InstSet mergespan) for (SVFGNodeIDSet::iterator it = succ.begin(), eit = succ.end(); it != eit; ++it) { - assert((SVFUtil::isa(svfg->getSVFGNode(*it))) && - "succ is not a store/load node"); + assert ((SVFUtil::isa(svfg->getSVFGNode(*it)) || SVFUtil::isa(svfg->getSVFGNode(*it))) && "succ is not a store/load node"); const StmtSVFGNode* succNode = SVFUtil::dyn_cast(svfg->getSVFGNode(*it)); - const SVFInstruction* succIns = succNode->getInst(); + const Instruction* succIns = succNode->getInst(); if (mergespan.find(succIns)!=mergespan.end()) return false; @@ -293,12 +269,11 @@ bool MTASVFGBuilder::isTailofSpan(const StmtSVFGNode* n, LockAnalysis::LockSpan SVFGNodeIDSet succ = getSuccNodes(n); for (SVFGNodeIDSet::iterator it = succ.begin(), eit = succ.end(); it != eit; ++it) { - assert((SVFUtil::isa(svfg->getSVFGNode(*it))) && - "succ is not a store/load node"); + assert ((SVFUtil::isa(svfg->getSVFGNode(*it)) || SVFUtil::isa(svfg->getSVFGNode(*it))) && "succ is not a store/load node"); if (SVFUtil::isa(svfg->getSVFGNode(*it))) continue; const StmtSVFGNode* succNode = SVFUtil::dyn_cast(svfg->getSVFGNode(*it)); - const SVFInstruction* succIns = succNode->getInst(); + const Instruction* succIns = succNode->getInst(); if (lockana->hasOneCxtInLockSpan(succIns, lspan)) { @@ -324,12 +299,13 @@ bool MTASVFGBuilder::isTailofSpan(const StmtSVFGNode* n) for (SVFGNodeIDSet::iterator it = succ.begin(), eit = succ.end(); it != eit; ++it) { - assert((SVFUtil::isa(svfg->getSVFGNode(*it))) && "succ is not a store/load node"); + assert((SVFUtil::isa(svfg->getSVFGNode(*it)) || SVFUtil::isa(svfg->getSVFGNode(*it))) + && "succ is not a store/load node"); if (SVFUtil::isa(svfg->getSVFGNode(*it))) continue; const StmtSVFGNode* succNode = SVFUtil::dyn_cast(svfg->getSVFGNode(*it)); - const SVFInstruction* succIns = succNode->getInst(); + const Instruction* succIns = succNode->getInst(); if (lockana->isInSameSpan(succIns, n->getInst())) { @@ -402,7 +378,7 @@ MTASVFGBuilder::SVFGNodeIDSet MTASVFGBuilder::getSuccNodes(const StmtSVFGNode* n const SVFGNode* node = *worklist.begin(); worklist.erase(worklist.begin()); visited.insert(node); - if (SVFUtil::isa(node)) + if (SVFUtil::isa(node) || SVFUtil::isa(node)) succ.set(node->getId()); else { @@ -430,7 +406,7 @@ MTASVFGBuilder::SVFGNodeIDSet MTASVFGBuilder::getSuccNodes(const StmtSVFGNode* n if (edge->isIndirectVFGEdge()) { IndirectSVFGEdge* e = SVFUtil::cast(edge); - NodeBS pts = e->getPointsTo(); + PointsTo pts = e->getPointsTo(); if(pts.test(o)) worklist.insert(edge->getDstNode()); } @@ -441,7 +417,7 @@ MTASVFGBuilder::SVFGNodeIDSet MTASVFGBuilder::getSuccNodes(const StmtSVFGNode* n const SVFGNode* node = *worklist.begin(); worklist.erase(worklist.begin()); visited.insert(node); - if (SVFUtil::isa(node)) + if (SVFUtil::isa(node) || SVFUtil::isa(node)) succ.set(node->getId()); else { @@ -451,7 +427,7 @@ MTASVFGBuilder::SVFGNodeIDSet MTASVFGBuilder::getSuccNodes(const StmtSVFGNode* n if (edge->isIndirectVFGEdge() && visited.find(edge->getDstNode()) == visited.end()) { IndirectSVFGEdge* e = SVFUtil::cast(edge); - NodeBS pts = e->getPointsTo(); + PointsTo pts = e->getPointsTo(); if(pts.test(o)) worklist.insert(edge->getDstNode()); } @@ -483,13 +459,13 @@ void MTASVFGBuilder::handleStoreStoreNonSparse(const StmtSVFGNode* n1,const Stmt void MTASVFGBuilder::handleStoreLoad(const StmtSVFGNode* n1,const StmtSVFGNode* n2, PointerAnalysis* pta) { - const SVFInstruction* i1 = n1->getInst(); - const SVFInstruction* i2 = n2->getInst(); + const Instruction* i1 = n1->getInst(); + const Instruction* i2 = n2->getInst(); /// MHP - if (ADDEDGE_NOMHP!=Options::AddModelFlag() && !mhp->mayHappenInParallel(i1, i2)) + if (ADDEDGE_NOMHP!=Options::AddModelFlag && !mhp->mayHappenInParallel(i1, i2)) return; /// Alias - if (ADDEDGE_NOALIAS!=Options::AddModelFlag() && !pta->alias(n1->getPAGDstNodeID(), n2->getPAGSrcNodeID())) + if (ADDEDGE_NOALIAS!=Options::AddModelFlag && !pta->alias(n1->getPAGDstNodeID(), n2->getPAGSrcNodeID())) return; @@ -499,10 +475,10 @@ void MTASVFGBuilder::handleStoreLoad(const StmtSVFGNode* n1,const StmtSVFGNode* /// Lock /// todo: we only consider all cxtstmt of one instruction in one lock span, /// otherwise we think this instruction is not locked - /// This constraint is too strong. All cxt lock under different cxt cannot be identified. + /// This constrait is too strong. All cxt lock under different cxt cannot be identified. - if (ADDEDGE_NOLOCK!=Options::AddModelFlag() && lockana->isProtectedByCommonLock(i1, i2)) + if (ADDEDGE_NOLOCK!=Options::AddModelFlag && lockana->isProtectedByCommonLock(i1, i2)) { if (isTailofSpan(n1) && isHeadofSpan(n2)) addTDEdges(n1->getId(), n2->getId(), pts); @@ -517,20 +493,20 @@ void MTASVFGBuilder::handleStoreLoad(const StmtSVFGNode* n1,const StmtSVFGNode* void MTASVFGBuilder::handleStoreStore(const StmtSVFGNode* n1,const StmtSVFGNode* n2, PointerAnalysis* pta) { - const SVFInstruction* i1 = n1->getInst(); - const SVFInstruction* i2 = n2->getInst(); + const Instruction* i1 = n1->getInst(); + const Instruction* i2 = n2->getInst(); /// MHP - if (ADDEDGE_NOMHP!=Options::AddModelFlag() && !mhp->mayHappenInParallel(i1, i2)) + if (ADDEDGE_NOMHP!=Options::AddModelFlag && !mhp->mayHappenInParallel(i1, i2)) return; /// Alias - if (ADDEDGE_NOALIAS!=Options::AddModelFlag() && !pta->alias(n1->getPAGDstNodeID(), n2->getPAGDstNodeID())) + if (ADDEDGE_NOALIAS!=Options::AddModelFlag && !pta->alias(n1->getPAGDstNodeID(), n2->getPAGDstNodeID())) return; PointsTo pts = pta->getPts(n1->getPAGDstNodeID()); pts &= pta->getPts(n2->getPAGDstNodeID()); /// Lock - if (ADDEDGE_NOLOCK!=Options::AddModelFlag() && lockana->isProtectedByCommonLock(i1, i2)) + if (ADDEDGE_NOLOCK!=Options::AddModelFlag && lockana->isProtectedByCommonLock(i1, i2)) { if (isTailofSpan(n1) && isHeadofSpan(n2)) addTDEdges(n1->getId(), n2->getId(), pts); @@ -552,8 +528,8 @@ void MTASVFGBuilder::handleStoreLoadWithLockPrecisely(const StmtSVFGNode* n1,con // PointsTo pts = pta->getPts(n1->getPAGDstNodeID()); // pts &= pta->getPts(n2->getPAGSrcNodeID()); // -// const SVFInstruction* i1 = n1->getInst(); -// const SVFInstruction* i2 = n2->getInst(); +// const Instruction* i1 = n1->getInst(); +// const Instruction* i2 = n2->getInst(); // // NodeBS comlocks1; // NodeBS comlocks2; @@ -593,8 +569,8 @@ void MTASVFGBuilder::handleStoreStoreWithLockPrecisely(const StmtSVFGNode* n1,co // PointsTo pts = pta->getPts(n1->getPAGDstNodeID()); // pts &= pta->getPts(n2->getPAGDstNodeID()); // -// const SVFInstruction* i1 = n1->getInst(); -// const SVFInstruction* i2 = n2->getInst(); +// const Instruction* i1 = n1->getInst(); +// const Instruction* i2 = n2->getInst(); // NodeBS comlocks1; @@ -673,7 +649,7 @@ void MTASVFGBuilder::readPrecision() const StmtSVFGNode* n2 = SVFUtil::cast(edge->getSrcNode()); IndirectSVFGEdge* e = SVFUtil::cast(edge); - NodeBS pts = e->getPointsTo(); + PointsTo pts = e->getPointsTo(); PointsTo remove_pts; for (NodeBS::iterator o = pts.begin(), eo = pts.end(); o != eo; ++o) @@ -705,9 +681,10 @@ void MTASVFGBuilder::readPrecision() void MTASVFGBuilder::connectMHPEdges(PointerAnalysis* pta) { - PCG* pcg = new PCG(pta); - if ((ADDEDGE_NONSPARSE==Options::AddModelFlag()) && Options::UsePCG()) + PCG* pcg; + if (ADDEDGE_NONSPARSE==Options::AddModelFlag) { + pcg= new PCG(pta); pcg->analyze(); } collectLoadStoreSVFGNodes(); @@ -719,15 +696,15 @@ void MTASVFGBuilder::connectMHPEdges(PointerAnalysis* pta) for (SVFGNodeSet::const_iterator it1 = stnodeSet.begin(), eit1 = stnodeSet.end(); it1!=eit1; ++it1) { const StmtSVFGNode* n1 = SVFUtil::cast(*it1); - const SVFInstruction* i1 = n1->getInst(); + const Instruction* i1 = n1->getInst(); for (SVFGNodeSet::const_iterator it2 = ldnodeSet.begin(), eit2 = ldnodeSet.end(); it2 != eit2; ++it2) { const StmtSVFGNode* n2 = SVFUtil::cast(*it2); - const SVFInstruction* i2 = n2->getInst(); - if (ADDEDGE_NONSPARSE==Options::AddModelFlag()) + const Instruction* i2 = n2->getInst(); + if (ADDEDGE_NONSPARSE==Options::AddModelFlag) { - if (Options::UsePCG()) + if (Options::UsePCG) { if (pcg->mayHappenInParallel(i1, i2) || mhp->mayHappenInParallel(i1, i2)) handleStoreLoadNonSparse(n1, n2, pta); @@ -746,10 +723,10 @@ void MTASVFGBuilder::connectMHPEdges(PointerAnalysis* pta) for (SVFGNodeSet::const_iterator it2 = std::next(it1), eit2 = stnodeSet.end(); it2!=eit2; ++it2) { const StmtSVFGNode* n2 = SVFUtil::cast(*it2); - const SVFInstruction* i2 = n2->getInst(); - if (ADDEDGE_NONSPARSE == Options::AddModelFlag()) + const Instruction* i2 = n2->getInst(); + if (ADDEDGE_NONSPARSE == Options::AddModelFlag) { - if (Options::UsePCG()) + if (Options::UsePCG) { if(pcg->mayHappenInParallel(i1, i2) || mhp->mayHappenInParallel(i1, i2)) handleStoreStoreNonSparse(n1, n2, pta); @@ -766,7 +743,7 @@ void MTASVFGBuilder::connectMHPEdges(PointerAnalysis* pta) } } - if(Options::ReadPrecisionTDEdge() && ADDEDGE_NORP!=Options::AddModelFlag()) + if(Options::ReadPrecisionTDEdge && ADDEDGE_NORP!=Options::AddModelFlag) { DBOUT(DGENERAL,outs()<<"Read precision edge removing \n"); DBOUT(DMTA,outs()<<"Read precision edge removing \n"); diff --git a/svf/lib/MTA/LockAnalysis.cpp b/svf/lib/MTA/LockAnalysis.cpp index 9e6fe99a1..6b7fb5b70 100644 --- a/svf/lib/MTA/LockAnalysis.cpp +++ b/svf/lib/MTA/LockAnalysis.cpp @@ -1,25 +1,3 @@ -//===- LockAnalysis.cpp -- Analysis of locksets-------------// -// -// SVF: Static Value-Flow Analysis -// -// Copyright (C) <2013-> -// - -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU Affero General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Affero General Public License for more details. - -// You should have received a copy of the GNU Affero General Public License -// along with this program. If not, see . -// -//===----------------------------------------------------------------------===// - /* * LocksetAnalysis.cpp * @@ -30,14 +8,36 @@ #include "Util/Options.h" #include "MTA/LockAnalysis.h" #include "MTA/MTA.h" +#include "MTA/MTAResultValidator.h" #include "Util/SVFUtil.h" -#include "Util/PTAStat.h" +#include "MemoryModel/PTAStat.h" +#include "MTA/LockResultValidator.h" using namespace SVF; using namespace SVFUtil; +namespace SVF +{ + +// Subclassing RCResultValidator to define the abstract methods. +class RaceValidator : public RaceResultValidator +{ +public: + RaceValidator(LockAnalysis* ls) :lsa(ls) + { + } + bool protectedByCommonLocks(const Instruction *I1, const Instruction *I2) + { + return lsa->isProtectedByCommonLock(I1, I2); + } +private: + LockAnalysis *lsa; +}; + +} // End namespace SVF + void LockAnalysis::analyze() { @@ -60,6 +60,8 @@ void LockAnalysis::analyze() DOTIMESTAT(double lockEnd = PTAStat::getClk(true)); DOTIMESTAT(lockTime += (lockEnd - lockStart) / TIMEINTERVAL); + if(Options::LockValid) + validateResults(); } @@ -69,20 +71,18 @@ void LockAnalysis::analyze() void LockAnalysis::collectLockUnlocksites() { ThreadCallGraph* tcg=tct->getThreadCallGraph(); - for (const SVFFunction* F : tct->getSVFModule()->getFunctionSet()) + for (SVFModule::iterator F = tct->getSVFModule()->begin(), E = tct->getSVFModule()->end(); F != E; ++F) { - for (const SVFBasicBlock* bb : F->getBasicBlockList()) + for (inst_iterator II = inst_begin((*F)->getLLVMFun()), EE = inst_end((*F)->getLLVMFun()); II != EE; ++II) { - for (const SVFInstruction* inst : bb->getInstructionList()) + const Instruction *inst = &*II; + if (tcg->getThreadAPI()->isTDRelease(inst)) { - if (tcg->getThreadAPI()->isTDRelease(inst)) - { - unlocksites.insert(inst); - } - if (tcg->getThreadAPI()->isTDAcquire(inst)) - { - locksites.insert(inst); - } + unlocksites.insert(inst); + } + if (tcg->getThreadAPI()->isTDAcquire(inst)) + { + locksites.insert(inst); } } } @@ -101,8 +101,8 @@ void LockAnalysis::buildCandidateFuncSetforLock() for (InstSet::iterator it = locksites.begin(), eit = locksites.end(); it != eit; ++it) { - const SVFFunction* fun=(*it)->getParent()->getParent(); - PTACallGraphNode* cgnode = tcg->getCallGraphNode(fun); + const Function* fun=(*it)->getParent()->getParent(); + PTACallGraphNode* cgnode = tcg->getCallGraphNode(tct->getSVFFun(fun)); if (visited.find(cgnode) == visited.end()) { worklist.push(cgnode); @@ -111,8 +111,8 @@ void LockAnalysis::buildCandidateFuncSetforLock() } for (InstSet::iterator it = unlocksites.begin(), eit = unlocksites.end(); it != eit; ++it) { - const SVFFunction* fun = (*it)->getParent()->getParent(); - PTACallGraphNode* cgnode = tcg->getCallGraphNode(fun); + const Function* fun = (*it)->getParent()->getParent(); + PTACallGraphNode* cgnode = tcg->getCallGraphNode(tct->getSVFFun(fun)); if (visited.find(cgnode) == visited.end()) { worklist.push(cgnode); @@ -122,7 +122,7 @@ void LockAnalysis::buildCandidateFuncSetforLock() while (!worklist.empty()) { const PTACallGraphNode* node = worklist.pop(); - lockcandidateFuncSet.insert(node->getFunction()); + lockcandidateFuncSet.insert(node->getFunction()->getLLVMFun()); for (PTACallGraphNode::const_iterator nit = node->InEdgeBegin(), neit = node->InEdgeEnd(); nit != neit; nit++) { const PTACallGraphNode* srcNode = (*nit)->getSrcNode(); @@ -145,8 +145,8 @@ void LockAnalysis::analyzeIntraProcedualLock() // Identify the protected Instructions. for (InstSet::const_iterator it = locksites.begin(), ie = locksites.end(); it != ie; ++it) { - const SVFInstruction* lockSite = *it; - assert(SVFUtil::isCallSite(lockSite) && "Lock acquire instruction must be a CallSite"); + const CallInst *lockSite = SVFUtil::dyn_cast(*it); + assert(lockSite && "Lock acquire instruction must be CallInst"); // Perform forward traversal InstSet forwardInsts; @@ -167,19 +167,19 @@ void LockAnalysis::analyzeIntraProcedualLock() /*! * Intra-procedural forward traversal */ -bool LockAnalysis::intraForwardTraverse(const SVFInstruction* lockSite, InstSet& unlockSet, InstSet& forwardInsts) +bool LockAnalysis::intraForwardTraverse(const Instruction* lockSite, InstSet& unlockSet, InstSet& forwardInsts) { - const SVFFunction* svfFun = lockSite->getFunction(); + const Function* fun = lockSite->getParent()->getParent(); InstVec worklist; worklist.push_back(lockSite); while (!worklist.empty()) { - const SVFInstruction *I = worklist.back(); + const Instruction *I = worklist.back(); worklist.pop_back(); - const SVFInstruction* exitInst = svfFun->getExitBB()->back(); - if(exitInst == I) + + if(&(getFunExitBB(fun)->back()) == I) return false; // Skip the visited Instructions. @@ -190,12 +190,13 @@ bool LockAnalysis::intraForwardTraverse(const SVFInstruction* lockSite, InstSet& if (isTDRelease(I) && isAliasedLocks(lockSite, I)) { unlockSet.insert(I); - DBOUT(DMTA, outs() << "LockAnalysis ci lock -- " << lockSite->getSourceLoc()<<"\n"); - DBOUT(DMTA, outs() << "LockAnalysis ci unlock -- " << I->getSourceLoc()<<"\n"); + DBOUT(DMTA, outs() << "LockAnalysis ci lock -- " << SVFUtil::getSourceLoc(lockSite)<<"\n"); + DBOUT(DMTA, outs() << "LockAnalysis ci unlock -- " << SVFUtil::getSourceLoc(I)<<"\n"); continue; } - const InstVec& nextInsts = I->getSuccInstructions(); + InstVec nextInsts; + getNextInsts(I, nextInsts); for (InstVec::const_iterator nit = nextInsts.begin(), enit = nextInsts.end(); nit != enit; ++nit) { worklist.push_back(*nit); @@ -215,13 +216,14 @@ bool LockAnalysis::intraBackwardTraverse(const InstSet& unlockSet, InstSet& back InstVec worklist; for(InstSet::const_iterator it = unlockSet.begin(), eit = unlockSet.end(); it!=eit; ++it) { - const SVFInstruction* unlockSite = *it; - const SVFInstruction* entryInst = unlockSite->getFunction()->getEntryBlock()->back(); + const Instruction* unlockSite = *it; + const Function* fun = unlockSite->getParent()->getParent(); + const Instruction* entryInst = &(fun->getEntryBlock().back()); worklist.push_back(*it); while (!worklist.empty()) { - const SVFInstruction *I = worklist.back(); + const Instruction *I = worklist.back(); worklist.pop_back(); if(entryInst == I) @@ -234,12 +236,13 @@ bool LockAnalysis::intraBackwardTraverse(const InstSet& unlockSet, InstSet& back if (isTDAcquire(I) && isAliasedLocks(unlockSite, I)) { - DBOUT(DMTA, outs() << "LockAnalysis ci lock -- " << I->getSourceLoc()<<"\n"); - DBOUT(DMTA, outs() << "LockAnalysis ci unlock -- " << unlockSite->getSourceLoc()<<"\n"); + DBOUT(DMTA, outs() << "LockAnalysis ci lock -- " << SVFUtil::getSourceLoc(I)<<"\n"); + DBOUT(DMTA, outs() << "LockAnalysis ci unlock -- " << SVFUtil::getSourceLoc(unlockSite)<<"\n"); continue; } - const InstVec& nextInsts = I->getPredInstructions(); + InstVec nextInsts; + getPrevInsts(I, nextInsts); for (InstVec::const_iterator nit = nextInsts.begin(), enit = nextInsts.end(); nit != enit; ++nit) { worklist.push_back(*nit); @@ -259,7 +262,7 @@ void LockAnalysis::collectCxtLock() if (!isLockCandidateFun(*it)) continue; CallStrCxt cxt; - CxtLockProc t(cxt, *it); + CxtLockProc t(cxt, tct->getSVFFun(*it)); pushToCTPWorkList(t); } @@ -268,7 +271,7 @@ void LockAnalysis::collectCxtLock() CxtLockProc clp = popFromCTPWorkList(); PTACallGraphNode* cgNode = getTCG()->getCallGraphNode(clp.getProc()); // lzh TODO. - if (!isLockCandidateFun(cgNode->getFunction())) + if (!isLockCandidateFun(cgNode->getFunction()->getLLVMFun())) continue; for (PTACallGraphNode::const_iterator nit = cgNode->OutEdgeBegin(), neit = cgNode->OutEdgeEnd(); nit != neit; nit++) @@ -281,7 +284,7 @@ void LockAnalysis::collectCxtLock() DBOUT(DMTA, outs() << "\nCollecting CxtLocks: handling direct call:" << **cit << "\t" << cgEdge->getSrcNode()->getFunction()->getName() << "-->" << cgEdge->getDstNode()->getFunction()->getName() << "\n"); - handleCallRelation(clp, cgEdge, getSVFCallSite((*cit)->getCallSite())); + handleCallRelation(clp, cgEdge, getLLVMCallSite((*cit)->getCallSite())); } for (PTACallGraphEdge::CallInstSet::const_iterator ind = cgEdge->indirectCallsBegin(), eind = cgEdge->indirectCallsEnd(); ind != eind; ++ind) @@ -290,7 +293,7 @@ void LockAnalysis::collectCxtLock() outs() << "\nCollecting CxtLocks: handling indirect call:" << **ind << "\t" << cgEdge->getSrcNode()->getFunction()->getName() << "-->" << cgEdge->getDstNode()->getFunction()->getName() << "\n"); - handleCallRelation(clp, cgEdge, getSVFCallSite((*ind)->getCallSite())); + handleCallRelation(clp, cgEdge, getLLVMCallSite((*ind)->getCallSite())); } } } @@ -310,8 +313,9 @@ void LockAnalysis::handleCallRelation(CxtLockProc& clp, const PTACallGraphEdge* addCxtLock(cxt,cs.getInstruction()); return; } - const SVFFunction* svfcallee = cgEdge->getDstNode()->getFunction(); - pushCxt(cxt, cs.getInstruction(), svfcallee); + const SVFFunction* svfcallee = cgEdge->getDstNode()->getFunction(); + const Function* callee = svfcallee->getLLVMFun(); + pushCxt(cxt, cs.getInstruction(), callee); CxtLockProc newclp(cxt, svfcallee); if (pushToCTPWorkList(newclp)) @@ -331,8 +335,7 @@ void LockAnalysis::analyzeLockSpanCxtStmt() if (!isLockCandidateFun(*it)) continue; CallStrCxt cxt; - const SVFInstruction* frontInst = (*it)->getEntryBlock()->front(); - CxtStmt cxtstmt(cxt, frontInst); + CxtStmt cxtstmt(cxt, &((*it)->front().front())); pushToCTSWorkList(cxtstmt); } @@ -341,7 +344,7 @@ void LockAnalysis::analyzeLockSpanCxtStmt() CxtStmt cts = popFromCTSWorkList(); touchCxtStmt(cts); - const SVFInstruction* curInst = cts.getStmt(); + const Instruction* curInst = cts.getStmt(); instToCxtStmtSet[curInst].insert(cts); DBOUT(DMTA, outs() << "\nVisit cxtStmt: "); @@ -365,11 +368,11 @@ void LockAnalysis::analyzeLockSpanCxtStmt() if(removeCxtStmtToSpan(cts,cts)) handleIntra(cts); } - else if (SVFUtil::isCallSite(curInst) && !isExtCall(curInst)) + else if (SVFUtil::isa(curInst) && !isExtCall(curInst)) { handleCall(cts); } - else if (curInst->isRetInst()) + else if (SVFUtil::isa(curInst)) { handleRet(cts); } @@ -401,19 +404,19 @@ void LockAnalysis::printLocks(const CxtStmt& cts) /// Handle fork void LockAnalysis::handleFork(const CxtStmt& cts) { - const SVFInstruction* call = cts.getStmt(); + const CallInst* call = SVFUtil::cast(cts.getStmt()); const CallStrCxt& curCxt = cts.getContext(); - CallICFGNode* cbn = tct->getCallICFGNode(call); + CallBlockNode* cbn = tct->getCallBlockNode(call); if(getTCG()->hasThreadForkEdge(cbn)) { for (ThreadCallGraph::ForkEdgeSet::const_iterator cgIt = getTCG()->getForkEdgeBegin(cbn), ecgIt = getTCG()->getForkEdgeEnd(cbn); cgIt != ecgIt; ++cgIt) { const SVFFunction* svfcallee = (*cgIt)->getDstNode()->getFunction(); + const Function* callee = svfcallee->getLLVMFun(); CallStrCxt newCxt = curCxt; - pushCxt(newCxt,call,svfcallee); - const SVFInstruction* svfInst = svfcallee->getEntryBlock()->front(); - CxtStmt newCts(newCxt, svfInst); + pushCxt(newCxt,call,callee); + CxtStmt newCts(newCxt, &(callee->getEntryBlock().front())); markCxtStmtFlag(newCts, cts); } } @@ -424,21 +427,21 @@ void LockAnalysis::handleFork(const CxtStmt& cts) void LockAnalysis::handleCall(const CxtStmt& cts) { - const SVFInstruction* call = cts.getStmt(); + const CallInst* call = SVFUtil::cast(cts.getStmt()); const CallStrCxt& curCxt = cts.getContext(); - CallICFGNode* cbn = tct->getCallICFGNode(call); + CallBlockNode* cbn = tct->getCallBlockNode(call); if (getTCG()->hasCallGraphEdge(cbn)) { for (PTACallGraph::CallGraphEdgeSet::const_iterator cgIt = getTCG()->getCallEdgeBegin(cbn), ecgIt = getTCG()->getCallEdgeEnd(cbn); cgIt != ecgIt; ++cgIt) { const SVFFunction* svfcallee = (*cgIt)->getDstNode()->getFunction(); + const Function* callee = svfcallee->getLLVMFun(); if (isExtCall(svfcallee)) continue; CallStrCxt newCxt = curCxt; - pushCxt(newCxt, call, svfcallee); - const SVFInstruction* svfInst = svfcallee->getEntryBlock()->front(); - CxtStmt newCts(newCxt, svfInst); + pushCxt(newCxt, call, callee); + CxtStmt newCts(newCxt, &(callee->getEntryBlock().front())); markCxtStmtFlag(newCts, cts); } } @@ -448,24 +451,25 @@ void LockAnalysis::handleCall(const CxtStmt& cts) void LockAnalysis::handleRet(const CxtStmt& cts) { - const SVFInstruction* curInst = cts.getStmt(); + const Instruction* curInst = cts.getStmt(); const CallStrCxt& curCxt = cts.getContext(); - const SVFFunction* svffun = curInst->getFunction(); + const SVFFunction* svffun = tct->getSVFFun(curInst->getParent()->getParent()); PTACallGraphNode* curFunNode = getTCG()->getCallGraphNode(svffun); - + for (PTACallGraphNode::const_iterator it = curFunNode->getInEdges().begin(), eit = curFunNode->getInEdges().end(); it != eit; ++it) { PTACallGraphEdge* edge = *it; - if (SVFUtil::isa(edge)) + if (SVFUtil::isa(edge) || SVFUtil::isa(edge)) continue; for (PTACallGraphEdge::CallInstSet::const_iterator cit = (edge)->directCallsBegin(), ecit = (edge)->directCallsEnd(); cit != ecit; ++cit) { CallStrCxt newCxt = curCxt; - const SVFInstruction* inst = (*cit)->getCallSite(); - if (matchCxt(newCxt, inst, curFunNode->getFunction())) + const Instruction* inst = (*cit)->getCallSite(); + if (matchCxt(newCxt, inst, curFunNode->getFunction()->getLLVMFun())) { - const InstVec& nextInsts = inst->getSuccInstructions(); + InstVec nextInsts; + getNextInsts(inst, nextInsts); for (InstVec::const_iterator nit = nextInsts.begin(), enit = nextInsts.end(); nit != enit; ++nit) { CxtStmt newCts(newCxt, *nit); @@ -477,10 +481,11 @@ void LockAnalysis::handleRet(const CxtStmt& cts) cit != ecit; ++cit) { CallStrCxt newCxt = curCxt; - const SVFInstruction* inst = (*cit)->getCallSite(); - if (matchCxt(newCxt, inst, curFunNode->getFunction())) + const Instruction* inst = (*cit)->getCallSite(); + if (matchCxt(newCxt, inst, curFunNode->getFunction()->getLLVMFun())) { - const InstVec& nextInsts = inst->getSuccInstructions(); + InstVec nextInsts; + getNextInsts(inst, nextInsts); for (InstVec::const_iterator nit = nextInsts.begin(), enit = nextInsts.end(); nit != enit; ++nit) { CxtStmt newCts(newCxt, *nit); @@ -495,10 +500,11 @@ void LockAnalysis::handleRet(const CxtStmt& cts) void LockAnalysis::handleIntra(const CxtStmt& cts) { - const SVFInstruction* curInst = cts.getStmt(); + const Instruction* curInst = cts.getStmt(); const CallStrCxt& curCxt = cts.getContext(); - const InstVec& nextInsts = curInst->getSuccInstructions(); + InstVec nextInsts; + getNextInsts(curInst, nextInsts); for (InstVec::const_iterator nit = nextInsts.begin(), enit = nextInsts.end(); nit != enit; ++nit) { CxtStmt newCts(curCxt, *nit); @@ -507,28 +513,32 @@ void LockAnalysis::handleIntra(const CxtStmt& cts) } -void LockAnalysis::pushCxt(CallStrCxt& cxt, const SVFInstruction* call, const SVFFunction* callee) +void LockAnalysis::pushCxt(CallStrCxt& cxt, const Instruction* call, const Function* callee) { - const SVFFunction* svfcaller = call->getParent()->getParent(); - CallICFGNode* cbn = tct->getCallICFGNode(call); - CallSiteID csId = getTCG()->getCallSiteID(cbn, callee); - + const Function* caller = call->getParent()->getParent(); + const SVFFunction* svfcallee = tct->getSVFFun(callee); + const SVFFunction* svfcaller = tct->getSVFFun(caller); + CallBlockNode* cbn = tct->getCallBlockNode(call); + CallSiteID csId = getTCG()->getCallSiteID(cbn, svfcallee); + // /// handle calling context for candidate functions only // if (isLockCandidateFun(caller) == false) // return; - if (tct->inSameCallGraphSCC(getTCG()->getCallGraphNode(svfcaller), getTCG()->getCallGraphNode(callee)) == false) + if (tct->inSameCallGraphSCC(getTCG()->getCallGraphNode(svfcaller), getTCG()->getCallGraphNode(svfcallee)) == false) { tct->pushCxt(cxt,csId); DBOUT(DMTA, tct->dumpCxt(cxt)); } } -bool LockAnalysis::matchCxt(CallStrCxt& cxt, const SVFInstruction* call, const SVFFunction* callee) +bool LockAnalysis::matchCxt(CallStrCxt& cxt, const Instruction* call, const Function* callee) { - const SVFFunction* svfcaller = call->getParent()->getParent(); - CallICFGNode* cbn = tct->getCallICFGNode(call); - CallSiteID csId = getTCG()->getCallSiteID(cbn, callee); + const Function* caller = call->getParent()->getParent(); + const SVFFunction* svfcallee = tct->getSVFFun(callee); + const SVFFunction* svfcaller = tct->getSVFFun(caller); + CallBlockNode* cbn = tct->getCallBlockNode(call); + CallSiteID csId = getTCG()->getCallSiteID(cbn, svfcallee); // /// handle calling context for candidate functions only // if (isLockCandidateFun(caller) == false) @@ -538,7 +548,7 @@ bool LockAnalysis::matchCxt(CallStrCxt& cxt, const SVFInstruction* call, const S if (cxt.empty()) return true; - if (tct->inSameCallGraphSCC(getTCG()->getCallGraphNode(svfcaller), getTCG()->getCallGraphNode(callee)) == false) + if (tct->inSameCallGraphSCC(getTCG()->getCallGraphNode(svfcaller), getTCG()->getCallGraphNode(svfcallee)) == false) { if (cxt.back() == csId) cxt.pop_back(); @@ -553,16 +563,16 @@ bool LockAnalysis::matchCxt(CallStrCxt& cxt, const SVFInstruction* call, const S /*! * Protected by at least one common lock under every context */ -bool LockAnalysis::isProtectedByCommonLock(const SVFInstruction *i1, const SVFInstruction *i2) +bool LockAnalysis::isProtectedByCommonLock(const Instruction *i1, const Instruction *i2) { numOfTotalQueries++; bool commonlock = false; - DOTIMESTAT(double queryStart = PTAStat::getClk(true)); + DOTIMESTAT(double queryStart = PTAStat::getClk()); if (isInsideIntraLock(i1) && isInsideIntraLock(i2)) commonlock = isProtectedByCommonCILock(i1,i2) ; else commonlock = isProtectedByCommonCxtLock(i1,i2); - DOTIMESTAT(double queryEnd = PTAStat::getClk(true)); + DOTIMESTAT(double queryEnd = PTAStat::getClk()); DOTIMESTAT(lockQueriesTime += (queryEnd - queryStart) / TIMEINTERVAL); return commonlock; } @@ -570,7 +580,7 @@ bool LockAnalysis::isProtectedByCommonLock(const SVFInstruction *i1, const SVFIn /*! * Protected by at least one common context-insensitive lock */ -bool LockAnalysis::isProtectedByCommonCILock(const SVFInstruction *i1, const SVFInstruction *i2) +bool LockAnalysis::isProtectedByCommonCILock(const Instruction *i1, const Instruction *i2) { if(!isInsideCondIntraLock(i1) && !isInsideCondIntraLock(i2)) @@ -598,13 +608,16 @@ bool LockAnalysis::isProtectedByCommonCxtLock(const CxtStmt& cxtStmt1, const Cxt return true; const CxtLockSet& lockset1 = getCxtLockfromCxtStmt(cxtStmt1); const CxtLockSet& lockset2 = getCxtLockfromCxtStmt(cxtStmt2); - return alias(lockset1,lockset2); + if (alias(lockset1,lockset2)) + return true; + + return false; } /*! * Protected by at least one common context-sensitive lock under each context */ -bool LockAnalysis::isProtectedByCommonCxtLock(const SVFInstruction *i1, const SVFInstruction *i2) +bool LockAnalysis::isProtectedByCommonCxtLock(const Instruction *i1, const Instruction *i2) { if(!hasCxtStmtfromInst(i1) || !hasCxtStmtfromInst(i2)) return false; @@ -628,9 +641,9 @@ bool LockAnalysis::isProtectedByCommonCxtLock(const SVFInstruction *i1, const SV /*! * Return true if two instructions are inside at least one common lock span */ -bool LockAnalysis::isInSameSpan(const SVFInstruction *i1, const SVFInstruction *i2) +bool LockAnalysis::isInSameSpan(const Instruction *i1, const Instruction *i2) { - DOTIMESTAT(double queryStart = PTAStat::getClk(true)); + DOTIMESTAT(double queryStart = PTAStat::getClk()); bool sameSpan = false; if (isInsideIntraLock(i1) && isInsideIntraLock(i2)) @@ -638,7 +651,7 @@ bool LockAnalysis::isInSameSpan(const SVFInstruction *i1, const SVFInstruction * else sameSpan = isInSameCSSpan(i1, i2); - DOTIMESTAT(double queryEnd = PTAStat::getClk(true)); + DOTIMESTAT(double queryEnd = PTAStat::getClk()); DOTIMESTAT(lockQueriesTime += (queryEnd - queryStart) / TIMEINTERVAL); return sameSpan; } @@ -646,7 +659,7 @@ bool LockAnalysis::isInSameSpan(const SVFInstruction *i1, const SVFInstruction * /*! * Return true if two instructions are inside same context-insensitive lock span */ -bool LockAnalysis::isInSameCISpan(const SVFInstruction *i1, const SVFInstruction *i2) const +bool LockAnalysis::isInSameCISpan(const Instruction *i1, const Instruction *i2) const { if(!isInsideCondIntraLock(i1) && !isInsideCondIntraLock(i2)) { @@ -673,12 +686,15 @@ bool LockAnalysis::isInSameCSSpan(const CxtStmt& cxtStmt1, const CxtStmt& cxtStm return true; const CxtLockSet& lockset1 = getCxtLockfromCxtStmt(cxtStmt1); const CxtLockSet& lockset2 = getCxtLockfromCxtStmt(cxtStmt2); - return intersects(lockset1,lockset2); + if (intersects(lockset1,lockset2)) + return true; + + return false; } /*! - * Return true if two instructions are inside at least one common context-sensitive lock span + * Return true if two instructions are inside at least one common contex-sensitive lock span */ -bool LockAnalysis::isInSameCSSpan(const SVFInstruction *I1, const SVFInstruction *I2) const +bool LockAnalysis::isInSameCSSpan(const Instruction *I1, const Instruction *I2) const { if(!hasCxtStmtfromInst(I1) || !hasCxtStmtfromInst(I2)) return false; @@ -698,3 +714,15 @@ bool LockAnalysis::isInSameCSSpan(const SVFInstruction *I1, const SVFInstruction } return true; } + +void LockAnalysis::validateResults() +{ + + // Initialize the validator and perform validation. + LockResultValidator lockvalidator(this); + lockvalidator.analyze(); + + RaceValidator validator(this); + validator.init(tct->getSVFModule()); + validator.analyze(); +} diff --git a/svf/lib/MTA/LockResultValidator.cpp b/svf/lib/MTA/LockResultValidator.cpp new file mode 100644 index 000000000..04eee702d --- /dev/null +++ b/svf/lib/MTA/LockResultValidator.cpp @@ -0,0 +1,158 @@ +/* + * LOCKResultValidator.cpp + * + * Created on: 24/07/2021 + */ + +#include "Util/Options.h" +#include +#include +#include "MTA/LockResultValidator.h" + +using namespace SVF; +using namespace SVFUtil; + +Set LockResultValidator::getStringArg(const Instruction* inst, unsigned int arg_num) +{ + assert(SVFUtil::isa(inst) && "getFirstIntArg: inst is not a callinst"); + CallSite cs = SVFUtil::getLLVMCallSite(inst); + assert((arg_num < cs.arg_size()) && "Does not has this argument"); + const GetElementPtrInst* gepinst = SVFUtil::dyn_cast(cs.getArgument(arg_num)); + const Constant* arrayinst = SVFUtil::dyn_cast(gepinst->getOperand(0)); + const ConstantDataArray* cxtarray = SVFUtil::dyn_cast(arrayinst->getOperand(0)); + if (!cxtarray) + { + Set strvec; + return strvec; + } + const StringRef vthdcxtstring = cxtarray->getAsCString(); + return split(vthdcxtstring.str(), ','); +} + +Set &LockResultValidator::split(const std::string &s, char delim, Set &elems) +{ + std::stringstream ss(s); + std::string item; + while (std::getline(ss, item, delim)) + { + elems.insert(item); + } + return elems; +} + +Set LockResultValidator::split(const std::string &s, char delim) +{ + Set elems; + split(s, delim, elems); + return elems; +} + +inline std::string LockResultValidator::getOutput(const char *scenario, LOCK_FLAG analysisRes) +{ + std::string ret(scenario); + ret += "\t"; + switch (analysisRes) + { + case LOCK_TRUE: + ret += SVFUtil::sucMsg("SUCCESS"); + break; + case LOCK_UNSOUND: + ret += SVFUtil::bugMsg2("UNSOUND"); + break; + case LOCK_IMPRECISE: + ret += SVFUtil::bugMsg1("IMPRECISE"); + break; + default: + ret += SVFUtil::errMsg("FAILURE"); + } + return ret; +} + +bool LockResultValidator::collectLockTargets() { + const Function* F; + for(auto it = getModule()->llvmFunBegin(); it != getModule()->llvmFunEnd(); it++) { + const std::string fName = (*it)->getName().str(); + if(fName.find(LOCK) != std::string::npos) { + F = (*it); + break; + } + } + if (!F) + return false; + for(Value::const_use_iterator it = F->use_begin(), ie = F->use_end(); it!=ie; it++) { + const Use *u = &*it; + const Value *user = u->getUser(); + const Instruction *inst = SVFUtil::dyn_cast(user); + + CxtLockSetStr y = getStringArg(inst, 0); + const Instruction* memInst = getPreviousMemoryAccessInst(inst); + instToCxtLockSet[memInst] = y; + if(const StoreInst* store = SVFUtil::dyn_cast (memInst)) { + if(const BinaryOperator* bop = SVFUtil::dyn_cast (store->getValueOperand())) { + const Value* v = bop->getOperand(0); + const Instruction* prevInst = SVFUtil::dyn_cast (v); + instToCxtLockSet[prevInst] = y; + } + } + } + return true; +} + +LockResultValidator::LOCK_FLAG LockResultValidator::validateStmtInLock() { + LockResultValidator::LOCK_FLAG res = LockResultValidator::LOCK_TRUE; + LockAnalysis::CxtStmtToCxtLockSet analyedLS = _la->getCSTCLS(); + for(LockAnalysis::CxtStmtToCxtLockSet::iterator it = analyedLS.begin(), + eit = analyedLS.end(); it!=eit; it++) { + const Instruction* inst = ((*it).first).getStmt(); + if(!SVFUtil::isa (inst) && !SVFUtil::isa (inst)) + continue; + const Function* F = inst->getParent()->getParent(); + if(inFilter(F->getName().str())) + continue; + CxtLockSetStr LS = instToCxtLockSet[inst]; + if(LS.size() != (*it).second.size()) { + if (Options::PrintValidRes) { + outs() << errMsg("\nValidate Stmt's Lock : Wrong at: ") << SVFUtil::value2String(inst) << "\n"; + outs() << "Reason: The number of lock on current stmt is wrong\n"; + outs() << "\n----Given locks:\n"; + for (CxtLockSetStr::iterator it1 = LS.begin(),eit1 = LS.end(); it1 != eit1; it++) { + outs() << "Lock " << *it1 << " "; + } + outs() << "\n----Analysis locks:\n"; + for (LockAnalysis::CxtLockSet::iterator it2 = (*it).second.begin(), + eit2 = (*it).second.end(); it2 != eit2; ++it) { + const Instruction* call = (*it2).getStmt(); + std::string lockName = call->getOperand(0)->getName().str(); + outs()<<"Lock " << lockName << " "; + } + outs() << "\n"; + } + res = LockResultValidator::LOCK_UNSOUND; + } + LockAnalysis::CxtLockSet LSA = (*it).second; + + for(LockAnalysis::CxtLockSet::iterator it3 = LSA.begin(), eit3=LSA.end(); it3!=eit3; it3++) { + const Instruction* call = (*it3).getStmt(); + std::string lockName = call->getOperand(0)->getName().str(); + if(!match(lockName, LS)) { + if(Options::PrintValidRes) { + outs() << "\nValidate Stmt's Lock : Wrong at (" << SVFUtil::value2String(inst) << ")\n"; + outs() << "Reason: The number of lock on current stmt is wrong\n"; + outs() << "\n Lock " << lockName << " should not protect current instruction\n"; + res = LockResultValidator::LOCK_IMPRECISE; + } + } + } + } + return res; +} + +void LockResultValidator::analyze() { + outs() << SVFUtil::pasMsg(" --- Lock Analysis Result Validation ---\n"); + if(!collectLockTargets()) + return; + std::string errstring; + errstring = getOutput("Validate Lock Analysis :", validateStmtInLock()); + outs() << "======" << errstring << "======\n"; +} + diff --git a/svf/lib/MTA/MHP.cpp b/svf/lib/MTA/MHP.cpp index 1a94a921d..0515df0e5 100644 --- a/svf/lib/MTA/MHP.cpp +++ b/svf/lib/MTA/MHP.cpp @@ -1,27 +1,5 @@ -//===- MHP.cpp -- May-happen-in-parallel analysis-------------// -// -// SVF: Static Value-Flow Analysis -// -// Copyright (C) <2013-> -// - -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU Affero General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Affero General Public License for more details. - -// You should have received a copy of the GNU Affero General Public License -// along with this program. If not, see . -// -//===----------------------------------------------------------------------===// - /* - * MHP.cpp + * MTA.cpp * * Created on: Jan 21, 2014 * Author: Yulei Sui, Peng Di @@ -31,18 +9,75 @@ #include "MTA/MHP.h" #include "MTA/MTA.h" #include "MTA/LockAnalysis.h" +#include "MTA/MTAResultValidator.h" #include "Util/SVFUtil.h" -#include "Util/PTAStat.h" +#include "MemoryModel/PTAStat.h" using namespace SVF; using namespace SVFUtil; +/*! + * Get the base pointer from any GEP. + */ +static const Value *getBasePtr(const Value *v) +{ + const GetElementPtrInst *GEP = SVFUtil::dyn_cast(v); + while (GEP) + { + v = GEP->getOperand(0); + GEP = SVFUtil::dyn_cast(v); + } + return v; +} + + +/*! + * Compute a SCEV that represents the subtraction of two given SCEVs. + */ +static const SCEV *getSCEVMinusExpr(const SCEV *s1,const SCEV *s2, ScalarEvolution *SE) +{ + if (SE->getCouldNotCompute() == s1 || SE->getCouldNotCompute() == s2) + return SE->getCouldNotCompute(); + + Type *t1 = SE->getEffectiveSCEVType(s1->getType()); + Type *t2 = SE->getEffectiveSCEVType(s2->getType()); + if (t1 != t2) + { + if (SE->getTypeSizeInBits(t1) < SE->getTypeSizeInBits(t2)) + s1 = SE->getSignExtendExpr(s1, t2); + else + s2 = SE->getSignExtendExpr(s2, t1); + } + + return SE->getMinusSCEV(s1, s2); +} + +namespace SVF +{ + +// Subclassing RCResultValidator to define the abstract methods. +class MHPValidator : public RaceResultValidator +{ +public: + MHPValidator(MHP *mhp) :mhp(mhp) + { + } + bool mayHappenInParallel(const Instruction *I1, const Instruction *I2) + { + return mhp->mayHappenInParallel(I1, I2); + } +private: + MHP *mhp; +}; + +} // End namespace SVF + /*! * Constructor */ -MHP::MHP(TCT* t) : tcg(t->getThreadCallGraph()), tct(t), numOfTotalQueries(0), numOfMHPQueries(0), - interleavingTime(0), interleavingQueriesTime(0) +MHP::MHP(TCT* t) :tcg(t->getThreadCallGraph()),tct(t),numOfTotalQueries(0),numOfMHPQueries(0), + interleavingTime(0),interleavingQueriesTime(0) { fja = new ForkJoinAnalysis(tct); fja->analyzeForkJoinPair(); @@ -61,12 +96,14 @@ MHP::~MHP() */ void MHP::analyze() { + DBOUT(DGENERAL, outs() << pasMsg("MHP interleaving analysis\n")); DBOUT(DMTA, outs() << pasMsg("MHP interleaving analysis\n")); DOTIMESTAT(double interleavingStart = PTAStat::getClk(true)); analyzeInterleaving(); DOTIMESTAT(double interleavingEnd = PTAStat::getClk(true)); DOTIMESTAT(interleavingTime += (interleavingEnd - interleavingStart) / TIMEINTERVAL); + } /*! @@ -74,27 +111,26 @@ void MHP::analyze() */ void MHP::analyzeInterleaving() { - for (const std::pair& tpair : *tct) + for(TCT::const_iterator it = tct->begin(), eit = tct->end(); it!=eit; ++it) { - const CxtThread& ct = tpair.second->getCxtThread(); - NodeID rootTid = tpair.first; - const SVFFunction* routine = tct->getStartRoutineOfCxtThread(ct); - const SVFInstruction* svfInst = routine->getEntryBlock()->front(); - CxtThreadStmt rootcts(rootTid, ct.getContext(), svfInst); + const CxtThread& ct = it->second->getCxtThread(); + NodeID rootTid = it->first; + const Function* routine = tct->getStartRoutineOfCxtThread(ct); + CxtThreadStmt rootcts(rootTid,ct.getContext(),&(routine->getEntryBlock().front())); - addInterleavingThread(rootcts, rootTid); + addInterleavingThread(rootcts,rootTid); updateAncestorThreads(rootTid); updateSiblingThreads(rootTid); - while (!cxtStmtList.empty()) + while(!cxtStmtList.empty()) { CxtThreadStmt cts = popFromCTSWorkList(); - const SVFInstruction* curInst = cts.getStmt(); - DBOUT(DMTA, outs() << "-----\nMHP analysis root thread: " << rootTid << " "); - DBOUT(DMTA, cts.dump()); - DBOUT(DMTA, outs() << "current thread interleaving: < "); - DBOUT(DMTA, dumpSet(getInterleavingThreads(cts))); - DBOUT(DMTA, outs() << " >\n-----\n"); + const Instruction* curInst = cts.getStmt(); + DBOUT(DMTA,outs() << "-----\nMHP analysis root thread: " << rootTid << " "); + DBOUT(DMTA,cts.dump()); + DBOUT(DMTA,outs() << "current thread interleaving: < "); + DBOUT(DMTA,dumpSet(getInterleavingThreads(cts))); + DBOUT(DMTA,outs() << " >\n-----\n"); /// handle non-candidate function if (!tct->isCandidateFun(curInst->getParent()->getParent())) @@ -104,22 +140,22 @@ void MHP::analyzeInterleaving() /// handle candidate function else { - if (isTDFork(curInst)) + if(isTDFork(curInst)) { - handleFork(cts, rootTid); + handleFork(cts,rootTid); } - else if (isTDJoin(curInst)) + else if(isTDJoin(curInst)) { - handleJoin(cts, rootTid); + handleJoin(cts,rootTid); } - else if (SVFUtil::isCallSite(curInst) && !isExtCall(curInst)) + else if(SVFUtil::isa(curInst) && !isExtCall(curInst)) { - handleCall(cts, rootTid); + handleCall(cts,rootTid); PTACallGraph::FunctionSet callees; - if (!tct->isCandidateFun(getCallee(curInst, callees))) - handleIntra(cts); + if(!tct->isCandidateFun(getCallee(curInst, callees))) + handleIntra(cts); } - else if (curInst->isRetInst()) + else if(SVFUtil::isa(curInst)) { handleRet(cts); } @@ -134,8 +170,11 @@ void MHP::analyzeInterleaving() /// update non-candidate functions' interleaving updateNonCandidateFunInterleaving(); - if (Options::PrintInterLev()) + + if(Options::PrintInterLev) printInterleaving(); + + validateResults(); } /*! @@ -144,30 +183,30 @@ void MHP::analyzeInterleaving() void MHP::updateNonCandidateFunInterleaving() { SVFModule* module = tct->getSVFModule(); - for (const SVFFunction* fun : module->getFunctionSet()) + for (SVFModule::iterator F = module->begin(), E = module->end(); F != E; ++F) { - if (!tct->isCandidateFun(fun) && !isExtCall(fun)) + const SVFFunction* fun = *F; + if (!tct->isCandidateFun(fun->getLLVMFun()) && !isExtCall(fun)) { - const SVFInstruction* entryinst = fun->getEntryBlock()->front(); + const Instruction *entryinst = &(fun->getLLVMFun()->getEntryBlock().front()); if (!hasThreadStmtSet(entryinst)) continue; const CxtThreadStmtSet& tsSet = getThreadStmtSet(entryinst); - for (const CxtThreadStmt& cts : tsSet) + for (CxtThreadStmtSet::const_iterator it1 = tsSet.begin(), eit1 = tsSet.end(); it1 != eit1; ++it1) { + const CxtThreadStmt& cts = *it1; const CallStrCxt& curCxt = cts.getContext(); - for (const SVFBasicBlock* svfbb : fun->getBasicBlockList()) + for (inst_iterator II = inst_begin(fun->getLLVMFun()), EE = inst_end(fun->getLLVMFun()); II != EE; ++II) { - for (const SVFInstruction* svfInst : svfbb->getInstructionList()) - { - if (svfInst == entryinst) - continue; - CxtThreadStmt newCts(cts.getTid(), curCxt, svfInst); - threadStmtToTheadInterLeav[newCts] |= threadStmtToTheadInterLeav[cts]; - instToTSMap[svfInst].insert(newCts); - } + const Instruction *inst = &*II; + if (inst == entryinst) + continue; + CxtThreadStmt newCts(cts.getTid(), curCxt, inst); + threadStmtToTheadInterLeav[newCts] |= threadStmtToTheadInterLeav[cts]; + instToTSMap[inst].insert(newCts); } } } @@ -179,18 +218,17 @@ void MHP::updateNonCandidateFunInterleaving() */ void MHP::handleNonCandidateFun(const CxtThreadStmt& cts) { - const SVFInstruction* curInst = cts.getStmt(); - const SVFFunction* curfun = curInst->getParent()->getParent(); - assert((curInst == curfun->getEntryBlock()->front()) && "curInst is not the entry of non candidate function."); + const Instruction* curInst = cts.getStmt(); + const Function* curfun = curInst->getParent()->getParent(); + assert(curInst == &(curfun->getEntryBlock().front()) && "curInst is not the entry of non candidate function."); const CallStrCxt& curCxt = cts.getContext(); - PTACallGraphNode* node = tcg->getCallGraphNode(curfun); + PTACallGraphNode* node = tcg->getCallGraphNode(tct->getSVFFun(curfun)); for (PTACallGraphNode::const_iterator nit = node->OutEdgeBegin(), neit = node->OutEdgeEnd(); nit != neit; nit++) { const SVFFunction* callee = (*nit)->getDstNode()->getFunction(); if (!isExtCall(callee)) { - const SVFInstruction* calleeInst = callee->getEntryBlock()->front(); - CxtThreadStmt newCts(cts.getTid(), curCxt, calleeInst); + CxtThreadStmt newCts(cts.getTid(), curCxt, &(callee->getLLVMFun()->getEntryBlock().front())); addInterleavingThread(newCts, cts); } } @@ -202,25 +240,25 @@ void MHP::handleNonCandidateFun(const CxtThreadStmt& cts) void MHP::handleFork(const CxtThreadStmt& cts, NodeID rootTid) { - const SVFInstruction* call = cts.getStmt(); + const CallInst* call = SVFUtil::cast(cts.getStmt()); const CallStrCxt& curCxt = cts.getContext(); assert(isTDFork(call)); - CallICFGNode* cbn = getCBN(call); - if (tct->getThreadCallGraph()->hasCallGraphEdge(cbn)) + CallBlockNode* cbn = getCBN(call); + if(tct->getThreadCallGraph()->hasCallGraphEdge(cbn)) { - + for (ThreadCallGraph::ForkEdgeSet::const_iterator cgIt = tcg->getForkEdgeBegin(cbn), - ecgIt = tcg->getForkEdgeEnd(cbn); - cgIt != ecgIt; ++cgIt) + ecgIt = tcg->getForkEdgeEnd(cbn); cgIt != ecgIt; ++cgIt) { const SVFFunction* svfroutine = (*cgIt)->getDstNode()->getFunction(); + const Function* routine = svfroutine->getLLVMFun(); CallStrCxt newCxt = curCxt; - pushCxt(newCxt, call, svfroutine); - const SVFInstruction* stmt = svfroutine->getEntryBlock()->front(); - CxtThread ct(newCxt, call); - CxtThreadStmt newcts(tct->getTCTNode(ct)->getId(), ct.getContext(), stmt); - addInterleavingThread(newcts, cts); + pushCxt(newCxt,call,routine); + const Instruction* stmt = &(routine->getEntryBlock().front()); + CxtThread ct(newCxt,call); + CxtThreadStmt newcts(tct->getTCTNode(ct)->getId(),ct.getContext(),stmt); + addInterleavingThread(newcts,cts); } } handleIntra(cts); @@ -232,50 +270,46 @@ void MHP::handleFork(const CxtThreadStmt& cts, NodeID rootTid) void MHP::handleJoin(const CxtThreadStmt& cts, NodeID rootTid) { - const SVFInstruction* call = cts.getStmt(); + const CallInst* call = SVFUtil::cast(cts.getStmt()); const CallStrCxt& curCxt = cts.getContext(); assert(isTDJoin(call)); - NodeBS joinedTids = getDirAndIndJoinedTid(curCxt, call); - if (!joinedTids.empty()) + NodeBS joinedTids = getDirAndIndJoinedTid(curCxt,call); + if(!joinedTids.empty()) { - if (fja->hasJoinLoop(call)) + if(const Loop* joinLoop = fja->getJoinLoop(call)) { - std::vector exitbbs; - call->getFunction()->getExitBlocksOfLoop(call->getParent(), exitbbs); - while (!exitbbs.empty()) + SmallBBVector exitbbs; + joinLoop->getExitBlocks(exitbbs); + while(!exitbbs.empty()) { - const SVFBasicBlock* eb = exitbbs.back(); - exitbbs.pop_back(); - const SVFInstruction* svfEntryInst = eb->front(); - CxtThreadStmt newCts(cts.getTid(), curCxt, svfEntryInst); - addInterleavingThread(newCts, cts); - if (hasJoinInSymmetricLoop(curCxt, call)) - rmInterleavingThread(newCts, joinedTids, call); + BasicBlock* eb = exitbbs.pop_back_val(); + CxtThreadStmt newCts(cts.getTid(),curCxt,&(eb->front())); + addInterleavingThread(newCts,cts); + if(isJoinInSymmetricLoop(curCxt,call)) + rmInterleavingThread(newCts,joinedTids,call); } } else { - rmInterleavingThread(cts, joinedTids, call); - DBOUT(DMTA, outs() << "\n\t match join site " << call->toString() << " for thread " << rootTid << "\n"); + rmInterleavingThread(cts,joinedTids,call); + DBOUT(DMTA,outs() << "\n\t match join site " << *call << " for thread " << rootTid << "\n"); } } /// for the join site in a loop loop which does not join the current thread /// we process the loop exit else { - if (fja->hasJoinLoop(call)) + if(const Loop* joinLoop = fja->getJoinLoop(call)) { - std::vector exitbbs; - call->getFunction()->getExitBlocksOfLoop(call->getParent(), exitbbs); - while (!exitbbs.empty()) + SmallBBVector exitbbs; + joinLoop->getExitBlocks(exitbbs); + while(!exitbbs.empty()) { - const SVFBasicBlock* eb = exitbbs.back(); - exitbbs.pop_back(); - const SVFInstruction* svfEntryInst = eb->front(); - CxtThreadStmt newCts(cts.getTid(), cts.getContext(), svfEntryInst); - addInterleavingThread(newCts, cts); + BasicBlock* eb = exitbbs.pop_back_val(); + CxtThreadStmt newCts(cts.getTid(),cts.getContext(),&(eb->front())); + addInterleavingThread(newCts,cts); } } } @@ -288,24 +322,23 @@ void MHP::handleJoin(const CxtThreadStmt& cts, NodeID rootTid) void MHP::handleCall(const CxtThreadStmt& cts, NodeID rootTid) { - const SVFInstruction* call = cts.getStmt(); + const CallInst* call = SVFUtil::cast(cts.getStmt()); const CallStrCxt& curCxt = cts.getContext(); - CallICFGNode* cbn = getCBN(call); - if (tct->getThreadCallGraph()->hasCallGraphEdge(cbn)) + CallBlockNode* cbn = getCBN(call); + if(tct->getThreadCallGraph()->hasCallGraphEdge(cbn)) { for (PTACallGraph::CallGraphEdgeSet::const_iterator cgIt = tcg->getCallEdgeBegin(cbn), - ecgIt = tcg->getCallEdgeEnd(cbn); - cgIt != ecgIt; ++cgIt) + ecgIt = tcg->getCallEdgeEnd(cbn); cgIt != ecgIt; ++cgIt) { - + const SVFFunction* svfcallee = (*cgIt)->getDstNode()->getFunction(); + const Function* callee = svfcallee->getLLVMFun(); if (isExtCall(svfcallee)) continue; CallStrCxt newCxt = curCxt; - pushCxt(newCxt, call, svfcallee); - const SVFInstruction* svfEntryInst = svfcallee->getEntryBlock()->front(); - CxtThreadStmt newCts(cts.getTid(), newCxt, svfEntryInst); - addInterleavingThread(newCts, cts); + pushCxt(newCxt,call,callee); + CxtThreadStmt newCts(cts.getTid(),newCxt,&(callee->getEntryBlock().front())); + addInterleavingThread(newCts,cts); } } } @@ -315,38 +348,40 @@ void MHP::handleCall(const CxtThreadStmt& cts, NodeID rootTid) */ void MHP::handleRet(const CxtThreadStmt& cts) { - PTACallGraphNode* curFunNode = tcg->getCallGraphNode(cts.getStmt()->getParent()->getParent()); - for (PTACallGraphEdge* edge : curFunNode->getInEdges()) + + PTACallGraphNode* curFunNode = tcg->getCallGraphNode(tct->getSVFFun(cts.getStmt()->getParent()->getParent())); + for(PTACallGraphNode::const_iterator it = curFunNode->getInEdges().begin(), eit = curFunNode->getInEdges().end(); it!=eit; ++it) { - if (SVFUtil::isa(edge)) + PTACallGraphEdge* edge = *it; + if(SVFUtil::isa(edge) || SVFUtil::isa(edge)) continue; - for (PTACallGraphEdge::CallInstSet::const_iterator cit = (edge)->directCallsBegin(), - ecit = (edge)->directCallsEnd(); - cit != ecit; ++cit) + for(PTACallGraphEdge::CallInstSet::const_iterator cit = (edge)->directCallsBegin(), + ecit = (edge)->directCallsEnd(); cit!=ecit; ++cit) { CallStrCxt newCxt = cts.getContext(); - if (matchCxt(newCxt, (*cit)->getCallSite(), curFunNode->getFunction())) + if(matchCxt(newCxt,(*cit)->getCallSite(),curFunNode->getFunction()->getLLVMFun())) { - const InstVec& nextInsts = (*cit)->getCallSite()->getSuccInstructions(); - for (const auto& ni : nextInsts) + InstVec nextInsts; + getNextInsts((*cit)->getCallSite(),nextInsts); + for(InstVec::const_iterator nit = nextInsts.begin(), enit = nextInsts.end(); nit!=enit; ++nit) { - CxtThreadStmt newCts(cts.getTid(), newCxt, ni); - addInterleavingThread(newCts, cts); + CxtThreadStmt newCts(cts.getTid(),newCxt,*nit); + addInterleavingThread(newCts,cts); } } } - for (PTACallGraphEdge::CallInstSet::const_iterator cit = (edge)->indirectCallsBegin(), - ecit = (edge)->indirectCallsEnd(); - cit != ecit; ++cit) + for(PTACallGraphEdge::CallInstSet::const_iterator cit = (edge)->indirectCallsBegin(), + ecit = (edge)->indirectCallsEnd(); cit!=ecit; ++cit) { CallStrCxt newCxt = cts.getContext(); - if (matchCxt(newCxt, (*cit)->getCallSite(), curFunNode->getFunction())) + if(matchCxt(newCxt,(*cit)->getCallSite(),curFunNode->getFunction()->getLLVMFun())) { - const InstVec& nextInsts = (*cit)->getCallSite()->getSuccInstructions(); - for (const auto& ni: nextInsts) + InstVec nextInsts; + getNextInsts((*cit)->getCallSite(),nextInsts); + for(InstVec::const_iterator nit = nextInsts.begin(), enit = nextInsts.end(); nit!=enit; ++nit) { - CxtThreadStmt newCts(cts.getTid(), newCxt, ni); - addInterleavingThread(newCts, cts); + CxtThreadStmt newCts(cts.getTid(),newCxt,*nit); + addInterleavingThread(newCts,cts); } } } @@ -359,36 +394,39 @@ void MHP::handleRet(const CxtThreadStmt& cts) void MHP::handleIntra(const CxtThreadStmt& cts) { - const InstVec& nextInsts = cts.getStmt()->getSuccInstructions(); - for (const auto& ni: nextInsts) + InstVec nextInsts; + getNextInsts(cts.getStmt(),nextInsts); + for(InstVec::const_iterator nit = nextInsts.begin(), enit = nextInsts.end(); nit!=enit; ++nit) { - CxtThreadStmt newCts(cts.getTid(), cts.getContext(), ni); - addInterleavingThread(newCts, cts); + CxtThreadStmt newCts(cts.getTid(),cts.getContext(),*nit); + addInterleavingThread(newCts,cts); } } + /*! * Update interleavings of ancestor threads according to TCT */ void MHP::updateAncestorThreads(NodeID curTid) { NodeBS tds = tct->getAncestorThread(curTid); - DBOUT(DMTA, outs() << "##Ancestor thread of " << curTid << " is : "); - DBOUT(DMTA, dumpSet(tds)); - DBOUT(DMTA, outs() << "\n"); + DBOUT(DMTA,outs() << "##Ancestor thread of " << curTid << " is : "); + DBOUT(DMTA,dumpSet(tds)); + DBOUT(DMTA,outs() << "\n"); tds.set(curTid); - for (const unsigned i : tds) + for(NodeBS::iterator it = tds.begin(), eit = tds.end(); it!=eit; ++it) { - const CxtThread& ct = tct->getTCTNode(i)->getCxtThread(); - if (const SVFInstruction* forkInst = ct.getThread()) + const CxtThread& ct = tct->getTCTNode(*it)->getCxtThread(); + if(const CallInst* forkInst = ct.getThread()) { CallStrCxt forkSiteCxt = tct->getCxtOfCxtThread(ct); - const InstVec& nextInsts = forkInst->getSuccInstructions(); - for (const auto& ni: nextInsts) + InstVec nextInsts; + getNextInsts(forkInst,nextInsts); + for(InstVec::const_iterator nit = nextInsts.begin(), enit = nextInsts.end(); nit!=enit; ++nit) { - CxtThreadStmt cts(tct->getParentThread(i), forkSiteCxt, ni); - addInterleavingThread(cts, curTid); + CxtThreadStmt cts(tct->getParentThread(*it),forkSiteCxt,*nit); + addInterleavingThread(cts,curTid); } } } @@ -400,7 +438,7 @@ void MHP::updateAncestorThreads(NodeID curTid) * Exclude sibling thread that never happen in parallel based on ForkJoinAnalysis * * The interleaving of a thread t is not unnecessary to be updated if - * (1) t HB Sibling and t fully joins curTid recursively + * (1) t HB Sibling and t fully joins curTid recusively * or * (2) Sibling HB t */ @@ -408,24 +446,25 @@ void MHP::updateSiblingThreads(NodeID curTid) { NodeBS tds = tct->getAncestorThread(curTid); tds.set(curTid); - for (const unsigned tid : tds) + for(NodeBS::iterator cit = tds.begin(), ecit = tds.end(); cit!=ecit; ++cit) { - NodeBS siblingTds = tct->getSiblingThread(tid); - for (const unsigned stid : siblingTds) + NodeBS siblingTds = tct->getSiblingThread(*cit); + for(NodeBS::iterator it = siblingTds.begin(), eit = siblingTds.end(); it!=eit; ++it) { - if ((isHBPair(tid, stid) && isRecurFullJoin(tid, curTid)) || isHBPair(stid, tid)) + + if((isHBPair(*cit,*it) && isRecurFullJoin(*cit,curTid)) || isHBPair(*it,*cit) ) continue; - const CxtThread& ct = tct->getTCTNode(stid)->getCxtThread(); - const SVFFunction* routine = tct->getStartRoutineOfCxtThread(ct); - const SVFInstruction* stmt = routine->getEntryBlock()->front(); - CxtThreadStmt cts(stid, ct.getContext(), stmt); - addInterleavingThread(cts, curTid); + const CxtThread& ct = tct->getTCTNode(*it)->getCxtThread(); + const Function* routine = tct->getStartRoutineOfCxtThread(ct); + const Instruction* stmt = &(routine->getEntryBlock().front()); + CxtThreadStmt cts(*it,ct.getContext(),stmt); + addInterleavingThread(cts,curTid); } - DBOUT(DMTA, outs() << "##Sibling thread of " << curTid << " is : "); - DBOUT(DMTA, dumpSet(siblingTds)); - DBOUT(DMTA, outs() << "\n"); + DBOUT(DMTA,outs() << "##Sibling thread of " << curTid << " is : "); + DBOUT(DMTA,dumpSet(siblingTds)); + DBOUT(DMTA,outs() << "\n"); } } @@ -434,24 +473,24 @@ void MHP::updateSiblingThreads(NodeID curTid) */ bool MHP::isRecurFullJoin(NodeID parentTid, NodeID curTid) { - if (parentTid == curTid) + if(parentTid==curTid) return true; const TCTNode* curNode = tct->getTCTNode(curTid); FIFOWorkList worklist; worklist.push(curNode); - while (!worklist.empty()) + while(!worklist.empty()) { const TCTNode* node = worklist.pop(); - for (TCTEdge* edge : node->getInEdges()) + for(TCT::ThreadCreateEdgeSet::const_iterator it = node->getInEdges().begin(), eit = node->getInEdges().end(); it!=eit; ++it) { - NodeID srcID = edge->getSrcID(); - if (fja->isFullJoin(srcID, node->getId())) + NodeID srcID = (*it)->getSrcID(); + if(fja->isFullJoin(srcID,node->getId())) { - if (srcID == parentTid) + if(srcID == parentTid) return true; else - worklist.push(edge->getSrcNode()); + worklist.push((*it)->getSrcNode()); } else { @@ -462,12 +501,13 @@ bool MHP::isRecurFullJoin(NodeID parentTid, NodeID curTid) return false; } + /*! * A join site must join t if * (1) t is not a multiforked thread * (2) the join site of t is not in recursion */ -bool MHP::isMustJoin(NodeID curTid, const SVFInstruction* joinsite) +bool MHP::isMustJoin(NodeID curTid, const Instruction* joinsite) { assert(isTDJoin(joinsite) && "not a join site!"); return !isMultiForkedThread(curTid) && !tct->isJoinSiteInRecursion(joinsite); @@ -476,26 +516,19 @@ bool MHP::isMustJoin(NodeID curTid, const SVFInstruction* joinsite) /*! * Return thread id(s) which are directly or indirectly joined at this join site */ -NodeBS MHP::getDirAndIndJoinedTid(const CallStrCxt& cxt, const SVFInstruction* call) +NodeBS MHP::getDirAndIndJoinedTid(const CallStrCxt& cxt, const Instruction* call) { - CxtStmt cs(cxt, call); + CxtStmt cs(cxt,call); return fja->getDirAndIndJoinedTid(cs); } /*! * Whether a context-sensitive join satisfies symmetric loop pattern */ -bool MHP::hasJoinInSymmetricLoop(const CallStrCxt& cxt, const SVFInstruction* call) const +const Loop* MHP::isJoinInSymmetricLoop(const CallStrCxt& cxt, const Instruction* call) const { - CxtStmt cs(cxt, call); - return fja->hasJoinInSymmetricLoop(cs); -} - -/// Whether a context-sensitive join satisfies symmetric loop pattern -const MHP::LoopBBs& MHP::getJoinInSymmetricLoop(const CallStrCxt& cxt, const SVFInstruction* call) const -{ - CxtStmt cs(cxt, call); - return fja->getJoinInSymmetricLoop(cs); + CxtStmt cs(cxt,call); + return fja->isJoinInSymmetricLoop(cs); } /*! @@ -503,12 +536,14 @@ const MHP::LoopBBs& MHP::getJoinInSymmetricLoop(const CallStrCxt& cxt, const SVF */ bool MHP::isHBPair(NodeID tid1, NodeID tid2) { - return fja->isHBPair(tid1, tid2); + return fja->isHBPair(tid1,tid2); } -bool MHP::isConnectedfromMain(const SVFFunction* fun) + + +bool MHP::isConnectedfromMain(const Function* fun) { - PTACallGraphNode* cgnode = tcg->getCallGraphNode(fun); + PTACallGraphNode* cgnode = tcg->getCallGraphNode(tct->getSVFFun(fun)); FIFOWorkList worklist; TCT::PTACGNodeSet visited; worklist.push(cgnode); @@ -516,7 +551,7 @@ bool MHP::isConnectedfromMain(const SVFFunction* fun) while (!worklist.empty()) { const PTACallGraphNode* node = worklist.pop(); - if ("main" == node->getFunction()->getName()) + if("main" == node->getFunction()->getName()) return true; for (PTACallGraphNode::const_iterator nit = node->InEdgeBegin(), neit = node->InEdgeEnd(); nit != neit; nit++) { @@ -541,24 +576,26 @@ bool MHP::isConnectedfromMain(const SVFFunction* fun) * (2) t1!=t2 and t1 \in l2 and t2 \in l1 */ -bool MHP::mayHappenInParallelInst(const SVFInstruction* i1, const SVFInstruction* i2) +bool MHP::mayHappenInParallelInst(const Instruction* i1, const Instruction* i2) { /// TODO: Any instruction in dead function is assumed no MHP with others - if (!hasThreadStmtSet(i1) || !hasThreadStmtSet(i2)) + if(!hasThreadStmtSet(i1) || !hasThreadStmtSet(i2)) return false; const CxtThreadStmtSet& tsSet1 = getThreadStmtSet(i1); const CxtThreadStmtSet& tsSet2 = getThreadStmtSet(i2); - for (const CxtThreadStmt& ts1 : tsSet1) + for(CxtThreadStmtSet::const_iterator it1 = tsSet1.begin(), eit1 = tsSet1.end(); it1!=eit1; ++it1) { + const CxtThreadStmt& ts1 = *it1; NodeBS l1 = getInterleavingThreads(ts1); - for (const CxtThreadStmt& ts2 : tsSet2) + for(CxtThreadStmtSet::const_iterator it2 = tsSet2.begin(), eit2 = tsSet2.end(); it2!=eit2; ++it2) { + const CxtThreadStmt& ts2 = *it2; NodeBS l2 = getInterleavingThreads(ts2); - if (ts1.getTid() != ts2.getTid()) + if(ts1.getTid()!=ts2.getTid()) { - if (l1.test(ts2.getTid()) && l2.test(ts1.getTid())) + if(l1.test(ts2.getTid()) && l2.test(ts1.getTid())) { numOfMHPQueries++; return true; @@ -577,13 +614,13 @@ bool MHP::mayHappenInParallelInst(const SVFInstruction* i1, const SVFInstruction return false; } -bool MHP::mayHappenInParallelCache(const SVFInstruction* i1, const SVFInstruction* i2) +bool MHP::mayHappenInParallelCache(const Instruction* i1, const Instruction* i2) { - if (!tct->isCandidateFun(i1->getParent()->getParent()) && !tct->isCandidateFun(i2->getParent()->getParent())) + if(!tct->isCandidateFun(i1->getParent()->getParent()) &&!tct->isCandidateFun(i2->getParent()->getParent())) { - FuncPair funpair = std::make_pair(i1->getFunction(), i2->getFunction()); + FuncPair funpair = std::make_pair(i1->getParent()->getParent(), i2->getParent()->getParent()); FuncPairToBool::const_iterator it = nonCandidateFuncMHPRelMap.find(funpair); - if (it == nonCandidateFuncMHPRelMap.end()) + if (it==nonCandidateFuncMHPRelMap.end()) { bool mhp = mayHappenInParallelInst(i1, i2); nonCandidateFuncMHPRelMap[funpair] = mhp; @@ -591,62 +628,78 @@ bool MHP::mayHappenInParallelCache(const SVFInstruction* i1, const SVFInstructio } else { - if (it->second) + if(it->second) numOfMHPQueries++; return it->second; } } - return mayHappenInParallelInst(i1, i2); + return mayHappenInParallelInst(i1,i2); } -bool MHP::mayHappenInParallel(const SVFInstruction* i1, const SVFInstruction* i2) +bool MHP::mayHappenInParallel(const Instruction* i1, const Instruction* i2) { numOfTotalQueries++; - DOTIMESTAT(double queryStart = PTAStat::getClk(true)); - bool mhp = mayHappenInParallelCache(i1, i2); - DOTIMESTAT(double queryEnd = PTAStat::getClk(true)); + DOTIMESTAT(double queryStart = PTAStat::getClk()); + bool mhp=mayHappenInParallelCache(i1,i2); + DOTIMESTAT(double queryEnd = PTAStat::getClk()); DOTIMESTAT(interleavingQueriesTime += (queryEnd - queryStart) / TIMEINTERVAL); return mhp; } -bool MHP::executedByTheSameThread(const SVFInstruction* i1, const SVFInstruction* i2) +bool MHP::executedByTheSameThread(const Instruction* i1, const Instruction* i2) { - if (!hasThreadStmtSet(i1) || !hasThreadStmtSet(i2)) + if(!hasThreadStmtSet(i1) || !hasThreadStmtSet(i2)) return true; const CxtThreadStmtSet& tsSet1 = getThreadStmtSet(i1); const CxtThreadStmtSet& tsSet2 = getThreadStmtSet(i2); - for (const CxtThreadStmt&ts1 : tsSet1) + for(CxtThreadStmtSet::const_iterator it1 = tsSet1.begin(), eit1 = tsSet1.end(); it1!=eit1; ++it1) { - for (const CxtThreadStmt& ts2 : tsSet2) + const CxtThreadStmt& ts1 = *it1; + for(CxtThreadStmtSet::const_iterator it2 = tsSet2.begin(), eit2 = tsSet2.end(); it2!=eit2; ++it2) { - if (ts1.getTid() != ts2.getTid() || isMultiForkedThread(ts1.getTid())) + const CxtThreadStmt& ts2 = *it2; + if(ts1.getTid()!=ts2.getTid()) + return false; + else if (isMultiForkedThread(ts1.getTid())) return false; } } return true; } +void MHP::validateResults() +{ + + // Initialize the validator and perform validation. + MHPValidator validator(this); + validator.init(getTCT()->getSVFModule()); + validator.analyze(); + + MTAResultValidator MTAValidator(this); + MTAValidator.analyze(); +} + /*! * Print interleaving results */ void MHP::printInterleaving() { - for (const auto& pair : threadStmtToTheadInterLeav) + for(ThreadStmtToThreadInterleav::const_iterator it = threadStmtToTheadInterLeav.begin(), eit = threadStmtToTheadInterLeav.end(); it!=eit; ++it) { - outs() << "( t" << pair.first.getTid() - << " , $" << pair.first.getStmt()->getSourceLoc() - << "$" << pair.first.getStmt()->toString() << " ) ==> ["; - for (unsigned i : pair.second) + outs() << "( t" << it->first.getTid() << " , $" << SVFUtil::getSourceLoc(it->first.getStmt()) << "$" << *(it->first.getStmt()) << " ) ==> ["; + for (NodeBS::iterator ii = it->second.begin(), ie = it->second.end(); + ii != ie; ii++) { - outs() << " " << i << " "; + outs() << " " << *ii << " "; } outs() << "]\n"; } } + /*! * Collect SCEV pass information for pointers at fork/join sites * Because ScalarEvolution is a function pass, previous knowledge of a function @@ -655,53 +708,53 @@ void MHP::printInterleaving() */ void ForkJoinAnalysis::collectSCEVInfo() { - typedef Set CallInstSet; - typedef Map FunToFJSites; + typedef Set CallInstSet; + typedef Map FunToFJSites; FunToFJSites funToFJSites; - for (ThreadCallGraph::CallSiteSet::const_iterator it = tct->getThreadCallGraph()->forksitesBegin(), - eit = tct->getThreadCallGraph()->forksitesEnd(); - it != eit; ++it) + for(ThreadCallGraph::CallSiteSet::const_iterator it = tct->getThreadCallGraph()->forksitesBegin(), + eit = tct->getThreadCallGraph()->forksitesEnd(); it!=eit; ++it) { - const SVFInstruction* fork = (*it)->getCallSite(); - funToFJSites[fork->getFunction()].insert(fork); + const Instruction* fork = (*it)->getCallSite(); + const Function* fun = fork->getParent()->getParent(); + funToFJSites[fun].insert(fork); } - for (ThreadCallGraph::CallSiteSet::const_iterator it = tct->getThreadCallGraph()->joinsitesBegin(), - eit = tct->getThreadCallGraph()->joinsitesEnd(); - it != eit; ++it) + for(ThreadCallGraph::CallSiteSet::const_iterator it = tct->getThreadCallGraph()->joinsitesBegin(), + eit = tct->getThreadCallGraph()->joinsitesEnd(); it!=eit; ++it) { - const SVFInstruction* join = (*it)->getCallSite(); - funToFJSites[join->getFunction()].insert(join); + const Instruction* join = (*it)->getCallSite(); + funToFJSites[join->getParent()->getParent()].insert(join); } - // for(FunToFJSites::const_iterator it = funToFJSites.begin(), eit = funToFJSites.end(); it!=eit; ++it) - // { - // // ScalarEvolution* SE = MTA::getSE(it->first); - // for(CallInstSet::const_iterator sit = it->second.begin(), esit = it->second.end(); sit!=esit; ++sit) - // { - // const SVFInstruction* callInst = *sit; - // if(tct->getThreadCallGraph()->isForksite(getCBN(callInst))) - // { - // // const SVFValue* forkSiteTidPtr = getForkedThread(callInst); - // // const SCEV *forkSiteTidPtrSCEV = SE->getSCEV(const_cast(forkSiteTidPtr)); - // // const SCEV *baseForkTidPtrSCEV = SE->getSCEV(const_cast(getBasePtr(forkSiteTidPtr))); - // // forkSiteTidPtrSCEV = getSCEVMinusExpr(forkSiteTidPtrSCEV, baseForkTidPtrSCEV, SE); - // // PTASCEV scev(forkSiteTidPtr,nullptr,nullptr); - // // fkjnToPTASCEVMap.insert(std::make_pair(callInst,scev)); - // } - // else - // { - // // const SVFValue* joinSiteTidPtr = getJoinedThread(callInst); - // //const SCEV *joinSiteTidPtrSCEV = SE->getSCEV(const_cast(joinSiteTidPtr)); - // //const SCEV *baseJoinTidPtrSCEV = SE->getSCEV(const_cast(getBasePtr(joinSiteTidPtr))); - // //joinSiteTidPtrSCEV = getSCEVMinusExpr(joinSiteTidPtrSCEV, baseJoinTidPtrSCEV, SE); - - // // PTASCEV scev(joinSiteTidPtr,nullptr,nullptr); - // // fkjnToPTASCEVMap.insert(std::make_pair(callInst,scev)); - // } - // } - // } + for(FunToFJSites::const_iterator it = funToFJSites.begin(), eit = funToFJSites.end(); it!=eit; ++it) + { + // ScalarEvolution* SE = MTA::getSE(it->first); + for(CallInstSet::const_iterator sit = it->second.begin(), esit = it->second.end(); sit!=esit; ++sit) + { + const Instruction* callInst = *sit; + if(tct->getThreadCallGraph()->isForksite(getCBN(callInst))) + { + const Value *forkSiteTidPtr = getForkedThread(callInst); + // const SCEV *forkSiteTidPtrSCEV = SE->getSCEV(const_cast(forkSiteTidPtr)); + // const SCEV *baseForkTidPtrSCEV = SE->getSCEV(const_cast(getBasePtr(forkSiteTidPtr))); + // forkSiteTidPtrSCEV = getSCEVMinusExpr(forkSiteTidPtrSCEV, baseForkTidPtrSCEV, SE); + PTASCEV scev(forkSiteTidPtr,nullptr,nullptr); + fkjnToPTASCEVMap.insert(std::make_pair(callInst,scev)); + } + else + { + const Value *joinSiteTidPtr = getJoinedThread(callInst); + //const SCEV *joinSiteTidPtrSCEV = SE->getSCEV(const_cast(joinSiteTidPtr)); + //const SCEV *baseJoinTidPtrSCEV = SE->getSCEV(const_cast(getBasePtr(joinSiteTidPtr))); + //joinSiteTidPtrSCEV = getSCEVMinusExpr(joinSiteTidPtrSCEV, baseJoinTidPtrSCEV, SE); + + PTASCEV scev(joinSiteTidPtr,nullptr,nullptr); + fkjnToPTASCEVMap.insert(std::make_pair(callInst,scev)); + } + } + + } } /*! @@ -709,45 +762,46 @@ void ForkJoinAnalysis::collectSCEVInfo() */ void ForkJoinAnalysis::analyzeForkJoinPair() { - for (const std::pair& tpair : *tct) + for(TCT::const_iterator it = tct->begin(), eit = tct->end(); it!=eit; ++it) { - const CxtThread& ct = tpair.second->getCxtThread(); - const NodeID rootTid = tpair.first; + const CxtThread& ct = it->second->getCxtThread(); + const NodeID rootTid = it->first; clearFlagMap(); - if (const SVFInstruction* forkInst = ct.getThread()) + if(const CallInst* forkInst = ct.getThread()) { CallStrCxt forkSiteCxt = tct->getCxtOfCxtThread(ct); - const SVFInstruction* exitInst = getExitInstOfParentRoutineFun(rootTid); + const Instruction* exitInst = getExitInstOfParentRoutineFun(rootTid); - const InstVec& nextInsts = forkInst->getSuccInstructions(); - for (const SVFInstruction* ni : nextInsts) + InstVec nextInsts; + getNextInsts(forkInst,nextInsts); + for(InstVec::const_iterator nit = nextInsts.begin(), enit = nextInsts.end(); nit!=enit; ++nit) { - CxtStmt cs(forkSiteCxt, ni); - markCxtStmtFlag(cs, TDAlive); + CxtStmt cs(forkSiteCxt,*nit); + markCxtStmtFlag(cs,TDAlive); } - while (!cxtStmtList.empty()) + while(!cxtStmtList.empty()) { CxtStmt cts = popFromCTSWorkList(); - const SVFInstruction* curInst = cts.getStmt(); - DBOUT(DMTA, outs() << "-----\nForkJoinAnalysis root thread: " << tpair.first << " "); - DBOUT(DMTA, cts.dump()); - DBOUT(DMTA, outs() << "-----\n"); - PTACallGraph::FunctionSet callees; - if (isTDFork(curInst)) + const Instruction* curInst = cts.getStmt(); + DBOUT(DMTA,outs() << "-----\nForkJoinAnalysis root thread: " << it->first << " "); + DBOUT(DMTA,cts.dump()); + DBOUT(DMTA,outs() << "-----\n"); + PTACallGraph::FunctionSet callees; + if(isTDFork(curInst)) { - handleFork(cts, rootTid); + handleFork(cts,rootTid); } - else if (isTDJoin(curInst)) + else if(isTDJoin(curInst)) { - handleJoin(cts, rootTid); + handleJoin(cts,rootTid); } - else if (SVFUtil::isCallSite(curInst) && tct->isCandidateFun(getCallee(curInst, callees))) + else if(SVFUtil::isa(curInst) && tct->isCandidateFun(getCallee(curInst, callees))) { - - handleCall(cts, rootTid); + + handleCall(cts,rootTid); } - else if (curInst->isRetInst()) + else if(SVFUtil::isa(curInst)) { handleRet(cts); } @@ -756,14 +810,15 @@ void ForkJoinAnalysis::analyzeForkJoinPair() handleIntra(cts); } - if (curInst == exitInst) + if(curInst==exitInst) { - if (getMarkedFlag(cts) != TDAlive) - addToFullJoin(tct->getParentThread(rootTid), rootTid); + if(getMarkedFlag(cts)!=TDAlive) + addToFullJoin(tct->getParentThread(rootTid),rootTid); else - addToPartial(tct->getParentThread(rootTid), rootTid); + addToPartial(tct->getParentThread(rootTid),rootTid); } } + } } } @@ -771,25 +826,24 @@ void ForkJoinAnalysis::analyzeForkJoinPair() /// Handle fork void ForkJoinAnalysis::handleFork(const CxtStmt& cts, NodeID rootTid) { - const SVFInstruction* call = cts.getStmt(); + const CallInst* call = SVFUtil::cast(cts.getStmt()); const CallStrCxt& curCxt = cts.getContext(); assert(isTDFork(call)); - CallICFGNode* cbn = getCBN(call); - if (getTCG()->hasThreadForkEdge(cbn)) + CallBlockNode* cbn = getCBN(call); + if(getTCG()->hasThreadForkEdge(cbn)) { for (ThreadCallGraph::ForkEdgeSet::const_iterator cgIt = getTCG()->getForkEdgeBegin(cbn), - ecgIt = getTCG()->getForkEdgeEnd(cbn); - cgIt != ecgIt; ++cgIt) + ecgIt = getTCG()->getForkEdgeEnd(cbn); cgIt != ecgIt; ++cgIt) { - const SVFFunction* callee = (*cgIt)->getDstNode()->getFunction(); + const Function* callee = (*cgIt)->getDstNode()->getFunction()->getLLVMFun(); CallStrCxt newCxt = curCxt; - pushCxt(newCxt, call, callee); - CxtThread ct(newCxt, call); - if (getMarkedFlag(cts) != TDAlive) - addToHBPair(rootTid, tct->getTCTNode(ct)->getId()); + pushCxt(newCxt,call,callee); + CxtThread ct(newCxt,call); + if(getMarkedFlag(cts)!=TDAlive) + addToHBPair(rootTid,tct->getTCTNode(ct)->getId()); else - addToHPPair(rootTid, tct->getTCTNode(ct)->getId()); + addToHPPair(rootTid,tct->getTCTNode(ct)->getId()); } } handleIntra(cts); @@ -798,61 +852,56 @@ void ForkJoinAnalysis::handleFork(const CxtStmt& cts, NodeID rootTid) /// Handle join void ForkJoinAnalysis::handleJoin(const CxtStmt& cts, NodeID rootTid) { - const SVFInstruction* call = cts.getStmt(); + const CallInst* call = SVFUtil::cast(cts.getStmt()); const CallStrCxt& curCxt = cts.getContext(); assert(isTDJoin(call)); - CallICFGNode* cbn = getCBN(call); - if (getTCG()->hasCallGraphEdge(cbn)) + CallBlockNode* cbn = getCBN(call); + if(getTCG()->hasCallGraphEdge(cbn)) { - const SVFInstruction* forkSite = tct->getTCTNode(rootTid)->getCxtThread().getThread(); - const SVFInstruction* joinSite = cts.getStmt(); + const Instruction* forkSite = tct->getTCTNode(rootTid)->getCxtThread().getThread(); + const Instruction* joinSite = cts.getStmt(); - if (isAliasedForkJoin(forkSite, joinSite)) + if(isAliasedForkJoin(forkSite, joinSite)) { - if (hasJoinLoop(joinSite)) + if(const Loop* joinLoop = getJoinLoop(joinSite)) { - LoopBBs& joinLoop = getJoinLoop(joinSite); - std::vector exitbbs; - joinSite->getFunction()->getExitBlocksOfLoop(joinSite->getParent(), exitbbs); - while (!exitbbs.empty()) + SmallBBVector exitbbs; + joinLoop->getExitBlocks(exitbbs); + while(!exitbbs.empty()) { - const SVFBasicBlock* eb = exitbbs.back(); - exitbbs.pop_back(); - const SVFInstruction* svfEntryInst = eb->front(); - CxtStmt newCts(curCxt, svfEntryInst); - addDirectlyJoinTID(cts, rootTid); - if (isSameSCEV(forkSite, joinSite)) + BasicBlock* eb = exitbbs.pop_back_val(); + CxtStmt newCts(curCxt,&(eb->front())); + addDirectlyJoinTID(cts,rootTid); + if(isSameSCEV(forkSite,joinSite)) { - markCxtStmtFlag(newCts, TDDead); - addSymmetricLoopJoin(cts, joinLoop); + markCxtStmtFlag(newCts,TDDead); + addSymmetricLoopJoin(cts,joinLoop); } else - markCxtStmtFlag(cts, TDAlive); + markCxtStmtFlag(cts,TDAlive); } } else { - markCxtStmtFlag(cts, TDDead); - addDirectlyJoinTID(cts, rootTid); - DBOUT(DMTA, outs() << "\n\t match join site " << call->toString() << "for thread " << rootTid << "\n"); + markCxtStmtFlag(cts,TDDead); + addDirectlyJoinTID(cts,rootTid); + DBOUT(DMTA,outs() << "\n\t match join site " << *call << "for thread " << rootTid << "\n"); } } /// for the join site in a loop loop which does not join the current thread /// we process the loop exit else { - if (hasJoinLoop(joinSite)) + if(const Loop* joinLoop = getJoinLoop(joinSite)) { - std::vector exitbbs; - joinSite->getFunction()->getExitBlocksOfLoop(joinSite->getParent(), exitbbs); - while (!exitbbs.empty()) + SmallBBVector exitbbs; + joinLoop->getExitBlocks(exitbbs); + while(!exitbbs.empty()) { - const SVFBasicBlock* eb = exitbbs.back(); - exitbbs.pop_back(); - const SVFInstruction* svfEntryInst = eb->front(); - CxtStmt newCts(curCxt, svfEntryInst); - markCxtStmtFlag(newCts, cts); + BasicBlock* eb = exitbbs.pop_back_val(); + CxtStmt newCts(curCxt,&(eb->front())); + markCxtStmtFlag(newCts,cts); } } } @@ -864,23 +913,22 @@ void ForkJoinAnalysis::handleJoin(const CxtStmt& cts, NodeID rootTid) void ForkJoinAnalysis::handleCall(const CxtStmt& cts, NodeID rootTid) { - const SVFInstruction* call = cts.getStmt(); + const CallInst* call = SVFUtil::cast(cts.getStmt()); const CallStrCxt& curCxt = cts.getContext(); - CallICFGNode* cbn = getCBN(call); - if (getTCG()->hasCallGraphEdge(cbn)) + CallBlockNode* cbn = getCBN(call); + if(getTCG()->hasCallGraphEdge(cbn)) { for (PTACallGraph::CallGraphEdgeSet::const_iterator cgIt = getTCG()->getCallEdgeBegin(cbn), - ecgIt = getTCG()->getCallEdgeEnd(cbn); - cgIt != ecgIt; ++cgIt) + ecgIt = getTCG()->getCallEdgeEnd(cbn); cgIt != ecgIt; ++cgIt) { const SVFFunction* svfcallee = (*cgIt)->getDstNode()->getFunction(); + const Function* callee = svfcallee->getLLVMFun(); if (isExtCall(svfcallee)) continue; CallStrCxt newCxt = curCxt; - pushCxt(newCxt, call, svfcallee); - const SVFInstruction* svfEntryInst = svfcallee->getEntryBlock()->front(); - CxtStmt newCts(newCxt, svfEntryInst); - markCxtStmtFlag(newCts, cts); + pushCxt(newCxt,call,callee); + CxtStmt newCts(newCxt,&(callee->getEntryBlock().front())); + markCxtStmtFlag(newCts,cts); } } } @@ -889,41 +937,42 @@ void ForkJoinAnalysis::handleCall(const CxtStmt& cts, NodeID rootTid) void ForkJoinAnalysis::handleRet(const CxtStmt& cts) { - const SVFInstruction* curInst = cts.getStmt(); + const Instruction* curInst = cts.getStmt(); const CallStrCxt& curCxt = cts.getContext(); - PTACallGraphNode* curFunNode = getTCG()->getCallGraphNode(curInst->getFunction()); - for (PTACallGraphEdge* edge : curFunNode->getInEdges()) + PTACallGraphNode* curFunNode = getTCG()->getCallGraphNode(tct->getSVFFun(curInst->getParent()->getParent())); + for(PTACallGraphNode::const_iterator it = curFunNode->getInEdges().begin(), eit = curFunNode->getInEdges().end(); it!=eit; ++it) { - if (SVFUtil::isa(edge)) + PTACallGraphEdge* edge = *it; + if(SVFUtil::isa(edge) || SVFUtil::isa(edge)) continue; - for (PTACallGraphEdge::CallInstSet::const_iterator cit = edge->directCallsBegin(), - ecit = edge->directCallsEnd(); - cit != ecit; ++cit) + for(PTACallGraphEdge::CallInstSet::const_iterator cit = (edge)->directCallsBegin(), + ecit = (edge)->directCallsEnd(); cit!=ecit; ++cit) { CallStrCxt newCxt = curCxt; - if (matchCxt(newCxt, (*cit)->getCallSite(), curFunNode->getFunction())) + if(matchCxt(newCxt,(*cit)->getCallSite(),curFunNode->getFunction()->getLLVMFun())) { - const InstVec& nextInsts = (*cit)->getCallSite()->getSuccInstructions(); - for (const auto& ni : nextInsts) + InstVec nextInsts; + getNextInsts((*cit)->getCallSite(),nextInsts); + for(InstVec::const_iterator nit = nextInsts.begin(), enit = nextInsts.end(); nit!=enit; ++nit) { - CxtStmt newCts(newCxt, ni); - markCxtStmtFlag(newCts, cts); + CxtStmt newCts(newCxt,*nit); + markCxtStmtFlag(newCts,cts); } } } - for (PTACallGraphEdge::CallInstSet::const_iterator cit = edge->indirectCallsBegin(), - ecit = edge->indirectCallsEnd(); - cit != ecit; ++cit) + for(PTACallGraphEdge::CallInstSet::const_iterator cit = (edge)->indirectCallsBegin(), + ecit = (edge)->indirectCallsEnd(); cit!=ecit; ++cit) { CallStrCxt newCxt = curCxt; - if (matchCxt(newCxt, (*cit)->getCallSite(), curFunNode->getFunction())) + if(matchCxt(newCxt,(*cit)->getCallSite(),curFunNode->getFunction()->getLLVMFun())) { - const InstVec& nextInsts = (*cit)->getCallSite()->getSuccInstructions(); - for (const auto& ni : nextInsts) + InstVec nextInsts; + getNextInsts((*cit)->getCallSite(),nextInsts); + for(InstVec::const_iterator nit = nextInsts.begin(), enit = nextInsts.end(); nit!=enit; ++nit) { - CxtStmt newCts(newCxt, ni); - markCxtStmtFlag(newCts, cts); + CxtStmt newCts(newCxt,*nit); + markCxtStmtFlag(newCts,cts); } } } @@ -934,14 +983,15 @@ void ForkJoinAnalysis::handleRet(const CxtStmt& cts) void ForkJoinAnalysis::handleIntra(const CxtStmt& cts) { - const SVFInstruction* curInst = cts.getStmt(); + const Instruction* curInst = cts.getStmt(); const CallStrCxt& curCxt = cts.getContext(); - const InstVec& nextInsts = curInst->getSuccInstructions(); - for (const auto& ni: nextInsts) + InstVec nextInsts; + getNextInsts(curInst,nextInsts); + for(InstVec::const_iterator nit = nextInsts.begin(), enit = nextInsts.end(); nit!=enit; ++nit) { - CxtStmt newCts(curCxt, ni); - markCxtStmtFlag(newCts, cts); + CxtStmt newCts(curCxt,*nit); + markCxtStmtFlag(newCts,cts); } } @@ -954,26 +1004,26 @@ NodeBS ForkJoinAnalysis::getDirAndIndJoinedTid(const CxtStmt& cs) { CxtStmtToTIDMap::const_iterator it = dirAndIndJoinMap.find(cs); - if (it != dirAndIndJoinMap.end()) + if(it!=dirAndIndJoinMap.end()) return it->second; - const NodeBS& directJoinTids = getDirectlyJoinedTid(cs); + const NodeBS& directJoinTids = getDirectlyJoinedTid(cs); NodeBS allJoinTids = directJoinTids; FIFOWorkList worklist; - for (unsigned id : directJoinTids) + for(NodeBS::iterator it = directJoinTids.begin(), eit = directJoinTids.end(); it!=eit; ++it) { - worklist.push(id); + worklist.push(*it); } - while (!worklist.empty()) + while(!worklist.empty()) { NodeID tid = worklist.pop(); TCTNode* node = tct->getTCTNode(tid); - for (TCT::ThreadCreateEdgeSet::const_iterator it = tct->getChildrenBegin(node), eit = tct->getChildrenEnd(node); it != eit; ++it) + for(TCT::ThreadCreateEdgeSet::const_iterator it = tct->getChildrenBegin(node), eit = tct->getChildrenEnd(node); it!=eit; ++it) { NodeID childTid = (*it)->getDstID(); - if (isFullJoin(tid, childTid)) + if(isFullJoin(tid,childTid)) { allJoinTids.set(childTid); worklist.push(childTid); @@ -986,37 +1036,37 @@ NodeBS ForkJoinAnalysis::getDirAndIndJoinedTid(const CxtStmt& cs) return allJoinTids; } -// static bool accessSameArrayIndex(const GetElementPtrInst* ptr1, const GetElementPtrInst* ptr2) -// { - -// std::vector ptr1vec; -// for (gep_type_iterator gi = gep_type_begin(*ptr1), ge = gep_type_end(*ptr1); -// gi != ge; ++gi) -// { -// if(SVFConstantInt* ci = SVFUtil::dyn_cast(LLVMModuleSet::getLLVMModuleSet()->getSVFValue(gi.getOperand()))) -// { -// s32_t idx = ci->getSExtValue(); -// ptr1vec.push_back(idx); -// } -// else -// return false; -// } - -// std::vector ptr2vec; -// for (gep_type_iterator gi = gep_type_begin(*ptr2), ge = gep_type_end(*ptr2); -// gi != ge; ++gi) -// { -// if(SVFConstantInt* ci = SVFUtil::dyn_cast(LLVMModuleSet::getLLVMModuleSet()->getSVFValue(gi.getOperand()))) -// { -// s32_t idx = ci->getSExtValue(); -// ptr2vec.push_back(idx); -// } -// else -// return false; -// } - -// return ptr1vec==ptr2vec; -// } +static bool accessSameArrayIndex(const GetElementPtrInst* ptr1, const GetElementPtrInst* ptr2) +{ + + std::vector ptr1vec; + for (gep_type_iterator gi = gep_type_begin(*ptr1), ge = gep_type_end(*ptr1); + gi != ge; ++gi) + { + if(ConstantInt* ci = SVFUtil::dyn_cast(gi.getOperand())) + { + Size_t idx = ci->getSExtValue(); + ptr1vec.push_back(idx); + } + else + return false; + } + + std::vector ptr2vec; + for (gep_type_iterator gi = gep_type_begin(*ptr2), ge = gep_type_end(*ptr2); + gi != ge; ++gi) + { + if(ConstantInt* ci = SVFUtil::dyn_cast(gi.getOperand())) + { + Size_t idx = ci->getSExtValue(); + ptr2vec.push_back(idx); + } + else + return false; + } + + return ptr1vec==ptr2vec; +} /*! * We assume a pair of fork and join sites are must-alias if they have same PTASCEV @@ -1025,52 +1075,50 @@ NodeBS ForkJoinAnalysis::getDirAndIndJoinedTid(const CxtStmt& cs) * pointers of fork thread and join thread should have same scev start and step. * and should have same loop trip count */ -bool ForkJoinAnalysis::isSameSCEV(const SVFInstruction* forkSite, const SVFInstruction* joinSite) +bool ForkJoinAnalysis::isSameSCEV(const Instruction* forkSite, const Instruction* joinSite) { - // const PTASCEV& forkse = fkjnToPTASCEVMap[forkSite]; - // const PTASCEV& joinse = fkjnToPTASCEVMap[joinSite]; + const PTASCEV& forkse = fkjnToPTASCEVMap[forkSite]; + const PTASCEV& joinse = fkjnToPTASCEVMap[joinSite]; - // //if(sameLoopTripCount(forkSite,joinSite) == false) - // // return false; + //if(sameLoopTripCount(forkSite,joinSite) == false) + // return false; - // if(forkse.inloop && joinse.inloop) - // return forkse.start==joinse.start && forkse.step == joinse.step && forkse.tripcount <= joinse.tripcount; - // else if(SVFUtil::isa(forkse.ptr) && SVFUtil::isa(joinse.ptr)) - // return accessSameArrayIndex(SVFUtil::cast(forkse.ptr),SVFUtil::cast(joinse.ptr)); - // else if(SVFUtil::isa(joinse.ptr)) - // return false; - // else - // return true; - - return false; + if(forkse.inloop && joinse.inloop) + return forkse.start==joinse.start && forkse.step == joinse.step && forkse.tripcount <= joinse.tripcount; + else if(SVFUtil::isa(forkse.ptr) && SVFUtil::isa(joinse.ptr)) + return accessSameArrayIndex(SVFUtil::cast(forkse.ptr),SVFUtil::cast(joinse.ptr)); + else if(SVFUtil::isa(forkse.ptr) || SVFUtil::isa(joinse.ptr)) + return false; + else + return true; } /*! * The fork and join have same loop trip count */ -bool ForkJoinAnalysis::sameLoopTripCount(const SVFInstruction* forkSite, const SVFInstruction* joinSite) +bool ForkJoinAnalysis::sameLoopTripCount(const Instruction* forkSite, const Instruction* joinSite) { - // ScalarEvolution* forkSE = getSE(forkSite); - // ScalarEvolution* joinSE = getSE(joinSite); + ScalarEvolution* forkSE = getSE(forkSite); + ScalarEvolution* joinSE = getSE(joinSite); - // if(tct->hasLoop(forkSite) == false || tct->hasLoop(joinSite) == false) - // return false; + // Get loops + const Loop *forkSiteLoop = tct->getLoop(forkSite); + const Loop *joinSiteLoop = tct->getLoop(joinSite); - // // Get loops - // const LoopBBs& forkSiteLoop = tct->getLoop(forkSite); - // const LoopBBs& joinSiteLoop = tct->getLoop(joinSite); + if(forkSiteLoop == nullptr || joinSiteLoop == nullptr) + return false; - // const SCEV* forkLoopCountScev = forkSE->getBackedgeTakenCount(forkSiteLoop); - // const SCEV* joinLoopCountScev = joinSE->getBackedgeTakenCount(joinSiteLoop); + const SCEV* forkLoopCountScev = forkSE->getBackedgeTakenCount(forkSiteLoop); + const SCEV* joinLoopCountScev = joinSE->getBackedgeTakenCount(joinSiteLoop); - // if(forkLoopCountScev!=forkSE->getCouldNotCompute()) - // { - // if(forkLoopCountScev==joinLoopCountScev) - // { - // return true; - // } - // } + if(forkLoopCountScev!=forkSE->getCouldNotCompute()) + { + if(forkLoopCountScev==joinLoopCountScev) + { + return true; + } + } return false; } diff --git a/svf/lib/MTA/MTA.cpp b/svf/lib/MTA/MTA.cpp index f49bd22f4..bf3e6bf46 100644 --- a/svf/lib/MTA/MTA.cpp +++ b/svf/lib/MTA/MTA.cpp @@ -1,26 +1,3 @@ -//===- MTA.h -- Analysis of multithreaded programs-------------// -// -// SVF: Static Value-Flow Analysis -// -// Copyright (C) <2013-> -// - -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU Affero General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Affero General Public License for more details. - -// You should have received a copy of the GNU Affero General Public License -// along with this program. If not, see . -// -//===----------------------------------------------------------------------===// - - /* * MTA.cpp * @@ -37,13 +14,23 @@ #include "WPA/Andersen.h" #include "MTA/FSMPTA.h" #include "Util/SVFUtil.h" +#include "SVF-FE/PAGBuilder.h" using namespace SVF; using namespace SVFUtil; -MTA::MTA() : tcg(nullptr), tct(nullptr), mhp(nullptr), lsa(nullptr) +static llvm::RegisterPass RACEDETECOR("mta", "May-Happen-in-Parallel Analysis"); + + +char MTA::ID = 0; +ModulePass* MTA::modulePass = nullptr; +MTA::FunToSEMap MTA::func2ScevMap; +MTA::FunToLoopInfoMap MTA::func2LoopInfoMap; + +MTA::MTA() : + ModulePass(ID), tcg(nullptr), tct(nullptr) { - stat = std::make_unique(); + stat = new MTAStat(); } MTA::~MTA() @@ -52,27 +39,35 @@ MTA::~MTA() delete tcg; //if (tct) // delete tct; - delete mhp; - delete lsa; +} + +bool MTA::runOnModule(Module& module) +{ + SVFModule mm(module.getName().str()); + return runOnModule(&mm); } /*! * Perform data race detection */ -bool MTA::runOnModule(SVFIR* pag) +bool MTA::runOnModule(SVFModule* module) { - mhp = computeMHP(pag->getModule()); - lsa = computeLocksets(mhp->getTCT()); - if(Options::RaceCheck()) - detect(pag->getModule()); + + modulePass = this; + + MHP* mhp = computeMHP(module); + LockAnalysis* lsa = computeLocksets(mhp->getTCT()); + + + /* - if (Options::AndersenAnno()) { + if (Options::AndersenAnno) { pta = mhp->getTCT()->getPTA(); if (pta->printStat()) stat->performMHPPairStat(mhp,lsa); AndersenWaveDiff::releaseAndersenWaveDiff(); - } else if (Options::FSAnno()) { + } else if (Options::FSAnno) { reportMemoryUsageKB("Mem before analysis"); DBOUT(DGENERAL, outs() << pasMsg("FSMPTA analysis\n")); @@ -104,6 +99,9 @@ bool MTA::runOnModule(SVFIR* pag) } */ + delete mhp; + delete lsa; + return false; } @@ -122,14 +120,15 @@ MHP* MTA::computeMHP(SVFModule* module) DBOUT(DGENERAL, outs() << pasMsg("MTA analysis\n")); DBOUT(DMTA, outs() << pasMsg("MTA analysis\n")); - SVFIR* pag = PAG::getPAG(); + PAGBuilder builder; + PAG* pag = builder.build(module); PointerAnalysis* pta = AndersenWaveDiff::createAndersenWaveDiff(pag); pta->getPTACallGraph()->dump("ptacg"); DBOUT(DGENERAL, outs() << pasMsg("Build TCT\n")); DBOUT(DMTA, outs() << pasMsg("Build TCT\n")); DOTIMESTAT(double tctStart = stat->getClk()); - tct = std::make_unique(pta); + tct = new TCT(pta); tcg = tct->getThreadCallGraph(); DOTIMESTAT(double tctEnd = stat->getClk()); DOTIMESTAT(stat->TCTTime += (tctEnd - tctStart) / TIMEINTERVAL); @@ -137,7 +136,7 @@ MHP* MTA::computeMHP(SVFModule* module) if (pta->printStat()) { stat->performThreadCallGraphStat(tcg); - stat->performTCTStat(tct.get()); + stat->performTCTStat(tct); } tcg->dump("tcg"); @@ -146,7 +145,7 @@ MHP* MTA::computeMHP(SVFModule* module) DBOUT(DMTA, outs() << pasMsg("MHP analysis\n")); DOTIMESTAT(double mhpStart = stat->getClk()); - MHP* mhp = new MHP(tct.get()); + MHP* mhp = new MHP(tct); mhp->analyze(); DOTIMESTAT(double mhpEnd = stat->getClk()); DOTIMESTAT(stat->MHPTime += (mhpEnd - mhpStart) / TIMEINTERVAL); @@ -157,9 +156,9 @@ MHP* MTA::computeMHP(SVFModule* module) } ///*! -// * Check (1) write-read race -// * (2) write-write race (optional) -// * (3) read-read race (optional) +// * Check (1) write-write race +// * (2) write-read race +// * (3) read-read race // * when two memory access may-happen in parallel and are not protected by the same lock // * (excluding global constraints because they are initialized before running the main function) // */ @@ -168,47 +167,43 @@ void MTA::detect(SVFModule* module) DBOUT(DGENERAL, outs() << pasMsg("Starting Race Detection\n")); - Set loads; - Set stores; - SVFIR* pag = SVFIR::getPAG(); - PointerAnalysis* pta = AndersenWaveDiff::createAndersenWaveDiff(pag); + LoadSet loads; + StoreSet stores; - Set needcheckinst; + Set needcheckinst; // Add symbols for all of the functions and the instructions in them. - for (const SVFFunction* F : module->getFunctionSet()) + for (SVFModule::iterator F = module->begin(), E = module->end(); F != E; ++F) { // collect and create symbols inside the function body - for (const SVFBasicBlock* svfbb : F->getBasicBlockList()) + for (inst_iterator II = inst_begin((*F)->getLLVMFun()), E = inst_end((*F)->getLLVMFun()); II != E; ++II) { - for (const SVFInstruction* svfInst : svfbb->getInstructionList()) + const Instruction *inst = &*II; + if (const LoadInst* load = SVFUtil::dyn_cast(inst)) + { + loads.insert(load); + } + else if (const StoreInst* store = SVFUtil::dyn_cast(inst)) { - for(const SVFStmt* stmt : pag->getSVFStmtList(pag->getICFG()->getICFGNode(svfInst))) - { - if (const LoadStmt* l = SVFUtil::dyn_cast(stmt)) - { - loads.insert(l); - } - else if (const StoreStmt* s = SVFUtil::dyn_cast(stmt)) - { - stores.insert(s); - } - } + stores.insert(store); } } } - for (Set::const_iterator lit = loads.begin(), elit = loads.end(); lit != elit; ++lit) + for (LoadSet::const_iterator lit = loads.begin(), elit = loads.end(); lit != elit; ++lit) { - const LoadStmt* load = *lit; - for (Set::const_iterator sit = stores.begin(), esit = stores.end(); sit != esit; ++sit) + const LoadInst* load = *lit; + bool loadneedcheck = false; + for (StoreSet::const_iterator sit = stores.begin(), esit = stores.end(); sit != esit; ++sit) { - const StoreStmt* store = *sit; - if(load->getInst()==nullptr || store->getInst()==nullptr) - continue; - if(mhp->mayHappenInParallelInst(load->getInst(),store->getInst()) && pta->alias(load->getRHSVarID(),store->getLHSVarID())) - if(lsa->isProtectedByCommonLock(load->getInst(),store->getInst()) == false) - outs() << SVFUtil::bugMsg1("race pair(") << " store: " << store->toString() << ", load: " << load->toString() << SVFUtil::bugMsg1(")") << "\n"; + const StoreInst* store = *sit; + + loadneedcheck = true; + needcheckinst.insert(store); } + if (loadneedcheck) + needcheckinst.insert(load); } + + outs() << "HP needcheck: " << needcheckinst.size() << "\n"; } diff --git a/svf/lib/MTA/MTAAnnotator.cpp b/svf/lib/MTA/MTAAnnotator.cpp new file mode 100644 index 000000000..0428219ee --- /dev/null +++ b/svf/lib/MTA/MTAAnnotator.cpp @@ -0,0 +1,280 @@ +/* + * MTAAnnotator.cpp + * + * Created on: May 4, 2014 + * Author: Yulei Sui, Peng Di + */ + +#include "Util/Options.h" +#include "MTA/MTAAnnotator.h" +#include "MTA/LockAnalysis.h" +#include + +using namespace SVF; +using namespace SVFUtil; + + +void MTAAnnotator::annotateDRCheck(Instruction* inst) +{ + std::string str; + raw_string_ostream rawstr(str); + rawstr << DR_CHECK; + + /// memcpy and memset is not annotated + if (StoreInst* st = SVFUtil::dyn_cast(inst)) + { + numOfAnnotatedSt++; + addMDTag(inst, st->getPointerOperand(), rawstr.str()); + } + else if (LoadInst* ld = SVFUtil::dyn_cast(inst)) + { + numOfAnnotatedLd++; + addMDTag(inst, ld->getPointerOperand(), rawstr.str()); + } +} + +void MTAAnnotator::collectLoadStoreInst(SVFModule* mod) +{ + + for (SVFModule::iterator F = mod->begin(), E = mod->end(); F != E; ++F) + { + if (SVFUtil::isExtCall(*F)) + continue; + for (inst_iterator II = inst_begin((*F)->getLLVMFun()), E = inst_end((*F)->getLLVMFun()); II != E; ++II) + { + const Instruction *inst = &*II; + if (SVFUtil::isa(inst)) + { + loadset.insert(inst); + } + else if (SVFUtil::isa(inst)) + { + storeset.insert(inst); + } + else if (isMemset(inst)) + { + storeset.insert(inst); + } + else if (isMemcpy(inst)) + { + storeset.insert(inst); + loadset.insert(inst); + } + } + } + + numOfAllSt = storeset.size(); + numOfAllLd = loadset.size(); +} + +const Value* MTAAnnotator::getStoreOperand(const Instruction* inst) +{ + if (const StoreInst* st = SVFUtil::dyn_cast(inst)) + { + return st->getPointerOperand(); + } + else if (isMemset(inst)) + { + return inst->getOperand(0); + } + else if (isMemcpy(inst)) + { + return inst->getOperand(0); + } + + assert(false); + return nullptr; +} +const Value* MTAAnnotator::getLoadOperand(const Instruction* inst) +{ + if (const LoadInst* ld = SVFUtil::dyn_cast(inst)) + { + return ld->getPointerOperand(); + } + else if (isMemcpy(inst)) + { + return inst->getOperand(1); + } + + assert(false); + return nullptr; +} + +void MTAAnnotator::initialize(MHP* m, LockAnalysis* la) +{ + mhp = m; + lsa = la; + if (!Options::AnnoFlag) + return; + collectLoadStoreInst(mhp->getTCT()->getPTA()->getModule()); +} + +void MTAAnnotator::pruneThreadLocal(PointerAnalysis* pta) +{ + bool AnnoLocal = Options::AnnoFlag & ANNO_LOCAL; + if (!AnnoLocal) + return; + + DBOUT(DGENERAL, outs() << pasMsg("Run annotator prune thread local pairs\n")); + PAG* pag = pta->getPAG(); + PointsTo nonlocalobjs; + PointsTo worklist; + + /// find fork arguments' objects + const PAGEdge::PAGEdgeSetTy& forkedges = pag->getPTAEdgeSet(PAGEdge::ThreadFork); + for (PAGEdge::PAGEdgeSetTy::const_iterator it = forkedges.begin(), eit = forkedges.end(); it != eit; ++it) + { + PAGEdge* edge = *it; + worklist |= pta->getPts(edge->getDstID()); + worklist |= pta->getPts(edge->getSrcID()); + } + + /// find global pointer-to objects + const PAG::PAGEdgeSet& globaledges = pag->getGlobalPAGEdgeSet(); + for (PAG::PAGEdgeSet::const_iterator it = globaledges.begin(), eit = globaledges.end(); it != eit; ++it) + { + const PAGEdge* edge = *it; + if (edge->getEdgeKind() == PAGEdge::Addr) + { + worklist.set(edge->getSrcID()); + } + } + + /// find all non-local objects that are transitively pointed by global and fork arguments. + while (!worklist.empty()) + { + NodeID obj = worklist.find_first(); + nonlocalobjs.set(obj); + worklist.reset(obj); + PointsTo pts = pta->getPts(obj); + for (PointsTo::iterator pit = pts.begin(), epit = pts.end(); pit != epit; ++pit) + { + if (!nonlocalobjs.test(*pit)) + worklist.set(*pit); + } + NodeBS fields = pag->getAllFieldsObjNode(obj); + for (NodeBS::iterator pit = fields.begin(), epit = fields.end(); pit != epit; ++pit) + { + if (!nonlocalobjs.test(*pit)) + worklist.set(*pit); + } + } + + /// compute all store and load instructions that may operate a non-local object. + InstSet needannost; + InstSet needannold; + for (InstSet::iterator it = storeset.begin(), eit = storeset.end(); it != eit; ++it) + { + PointsTo pts = pta->getPts(pag->getValueNode(getStoreOperand(*it))); + for (PointsTo::iterator pit = pts.begin(), epit = pts.end(); pit != epit; ++pit) + { + if (nonlocalobjs.test(*pit)) + { + needannost.insert(*it); + break; + } + } + } + + for (InstSet::iterator it = loadset.begin(), eit = loadset.end(); it != eit; ++it) + { + PointsTo pts = pta->getPts(pag->getValueNode(getLoadOperand(*it))); + for (PointsTo::iterator pit = pts.begin(), epit = pts.end(); pit != epit; ++pit) + { + if (nonlocalobjs.test(*pit)) + { + needannold.insert(*it); + break; + } + } + } + + storeset = needannost; + loadset = needannold; + + numOfNonLocalSt = storeset.size(); + numOfNonLocalLd = loadset.size(); +} +void MTAAnnotator::pruneAliasMHP(PointerAnalysis* pta) +{ + + bool AnnoMHP = Options::AnnoFlag & ANNO_MHP; + bool AnnoAlias = Options::AnnoFlag & ANNO_ALIAS; + + if (!AnnoMHP && !AnnoAlias) + return; + + DBOUT(DGENERAL, outs() << pasMsg("Run annotator prune Alias or MHP pairs\n")); + InstSet needannost; + InstSet needannold; + for (InstSet::iterator it1 = storeset.begin(), eit1 = storeset.end(); it1 != eit1; ++it1) + { + for (InstSet::iterator it2 = it1, eit2 = storeset.end(); it2 != eit2; ++it2) + { + if(!pta->alias(getStoreOperand(*it1), getStoreOperand(*it2))) + continue; + + if (AnnoMHP) + { + if (mhp->mayHappenInParallel(*it1, *it2) && !lsa->isProtectedByCommonLock(*it1, *it2)) + { + needannost.insert(*it1); + needannost.insert(*it2); + } + } + else + { + /// if it1 == it2, mhp analysis will annotate it1 that locates in loop or recursion. + /// but alias analysis fails to determine whether it1 is in loop or recursion, that means + /// all store instructions will be annotated by alias analysis to guarantee sound. + needannost.insert(*it1); + needannost.insert(*it2); + } + } + for (InstSet::iterator it2 = loadset.begin(), eit2 = loadset.end(); it2 != eit2; ++it2) + { + if(!pta->alias(getStoreOperand(*it1), getLoadOperand(*it2))) + continue; + + if (AnnoMHP) + { + if (mhp->mayHappenInParallel(*it1, *it2) && !lsa->isProtectedByCommonLock(*it1, *it2)) + { + needannost.insert(*it1); + needannold.insert(*it2); + } + } + else + { + needannost.insert(*it1); + needannold.insert(*it2); + } + } + } + storeset = needannost; + loadset = needannold; + + if (AnnoMHP) + { + numOfMHPSt = storeset.size(); + numOfMHPLd = loadset.size(); + } + else if (AnnoAlias) + { + numOfAliasSt = storeset.size(); + numOfAliasLd = loadset.size(); + } +} +void MTAAnnotator::performAnnotate() +{ + if (!Options::AnnoFlag) + return; + for (InstSet::iterator it = storeset.begin(), eit = storeset.end(); it != eit; ++it) + { + annotateDRCheck(const_cast(*it)); + } + for (InstSet::iterator it = loadset.begin(), eit = loadset.end(); it != eit; ++it) + { + annotateDRCheck(const_cast(*it)); + } +} diff --git a/svf/lib/MTA/MTAResultValidator.cpp b/svf/lib/MTA/MTAResultValidator.cpp new file mode 100644 index 000000000..c015ed089 --- /dev/null +++ b/svf/lib/MTA/MTAResultValidator.cpp @@ -0,0 +1,550 @@ +/* + * MTAResultValidator.cpp + * + * Created on: 29/06/2015 + * Author: Peng Di + */ + +#include "Util/Options.h" +#include +#include + +#include "MTA/MTAResultValidator.h" + +using namespace SVF; +using namespace SVFUtil; + + +void MTAResultValidator::analyze() +{ + + std::string errstring; + if (!collectCallsiteTargets()) + return; + if (!collectCxtThreadTargets()) + return; + + errstring = getOutput("Validate CxtThread:", validateCxtThread()); + outs() << "======" << errstring << "======\n"; + + if (!collectTCTTargets()) + return; + errstring = getOutput("Validate TCT: ", validateTCT()); + outs() << "======" << errstring << "======\n"; + + if (!collectInterleavingTargets()) + return; + errstring = getOutputforInterlevAnalysis("Validate Interleaving:", validateInterleaving()); + outs() << "======" << errstring << "======\n"; +} + +std::vector &MTAResultValidator::split(const std::string &s, char delim, std::vector &elems) +{ + std::stringstream ss(s); + std::string item; + while (std::getline(ss, item, delim)) + { + elems.push_back(item); + } + return elems; +} + +std::vector MTAResultValidator::split(const std::string &s, char delim) +{ + std::vector elems; + split(s, delim, elems); + return elems; +} +NodeID MTAResultValidator::getIntArg(const Instruction* inst, unsigned int arg_num) +{ + assert(SVFUtil::isa(inst) && "getFirstIntArg: inst is not a callinst"); + CallSite cs = SVFUtil::getLLVMCallSite(inst); + ConstantInt* x = SVFUtil::dyn_cast(cs.getArgument(arg_num)); + assert((arg_num < cs.arg_size()) && "Does not has this argument"); + return (NodeID) x->getSExtValue(); +} + +std::vector MTAResultValidator::getStringArg(const Instruction* inst, unsigned int arg_num) +{ + assert(SVFUtil::isa(inst) && "getFirstIntArg: inst is not a callinst"); + CallSite cs = SVFUtil::getLLVMCallSite(inst); + assert((arg_num < cs.arg_size()) && "Does not has this argument"); + const GetElementPtrInst* gepinst = SVFUtil::dyn_cast(cs.getArgument(arg_num)); + const Constant* arrayinst = SVFUtil::dyn_cast(gepinst->getOperand(0)); + const ConstantDataArray* cxtarray = SVFUtil::dyn_cast(arrayinst->getOperand(0)); + if (!cxtarray) + { + std::vector strvec; + return strvec; + } + const StringRef vthdcxtstring = cxtarray->getAsCString(); + return split(vthdcxtstring.str(), ','); +} + +CallStrCxt MTAResultValidator::getCxtArg(const Instruction* inst, unsigned int arg_num) +{ + std::vector x = getStringArg(inst, arg_num); + CallStrCxt cxt; + if (0 == x.size()) + return cxt; + // Deal with the the second argument that records all callsites + for (std::vector::iterator i = x.begin(); i != x.end(); i++) + { + std::vector y = split((*i), '.'); + y[0].erase(y[0].find("cs"), 2); + + const SVFFunction* callee = SVFUtil::getFunction(y[1]); + CallSite cs = SVFUtil::getLLVMCallSite(csnumToInstMap[atoi(y[0].c_str())]); + assert(callee && "callee error"); + CallBlockNode* cbn = mhp->getTCT()->getCallBlockNode(cs.getInstruction()); + CallSiteID csId = tcg->getCallSiteID(cbn, callee); + cxt.push_back(csId); + } + return cxt; +} + +const Instruction* MTAResultValidator::getPreviousMemoryAccessInst(const Instruction *I) +{ + I = I->getPrevNode(); + while (I) + { + if (SVFUtil::isa(I) || SVFUtil::isa(I)) + return I; + I = I->getPrevNode(); + } + return nullptr; +} + +inline std::string MTAResultValidator::getOutput(const char *scenario, bool analysisRes) +{ + std::string ret(scenario); + ret += "\t"; + + if (analysisRes) + ret += SVFUtil::sucMsg("SUCCESS"); + else + ret += SVFUtil::errMsg("FAILURE"); + return ret; +} + +inline std::string MTAResultValidator::getOutputforInterlevAnalysis(const char *scenario, INTERLEV_FLAG analysisRes) +{ + std::string ret(scenario); + ret += "\t"; + switch (analysisRes) + { + case INTERLEV_TRUE: + ret += SVFUtil::sucMsg("SUCCESS"); + break; + case INTERLEV_UNSOUND: + ret += SVFUtil::bugMsg2("UNSOUND"); + break; + case INTERLEV_IMPRECISE: + ret += SVFUtil::bugMsg1("IMPRECISE"); + break; + default: + ret += SVFUtil::errMsg("FAILURE"); + } + return ret; +} + +bool MTAResultValidator::matchCxt(const CallStrCxt cxt1, const CallStrCxt cxt2) const +{ + if (cxt1.size() != cxt2.size()) + return false; + return std::equal(cxt1.begin(), cxt1.end(), cxt2.begin()); +} + +void MTAResultValidator::dumpCxt(const CallStrCxt& cxt) const +{ + std::string str; + raw_string_ostream rawstr(str); + rawstr << "[:"; + for (CallStrCxt::const_iterator it = cxt.begin(), eit = cxt.end(); it != eit; ++it) + { + rawstr << " ' " << *it << " ' "; + rawstr << *(tcg->getCallSite(*it)->getCallSite()); + rawstr << " call " << tcg->getCallSite(*it)->getCaller()->getName() << "-->" << tcg->getCalleeOfCallSite(*it)->getName() << ", \n"; + } + rawstr << " ]"; + outs() << "max cxt = " << cxt.size() << rawstr.str() << "\n"; +} + +void MTAResultValidator::dumpInterlev(NodeBS& lev) +{ + outs() << " [ "; + for (NodeBS::iterator it = lev.begin(), eit = lev.end(); it != eit; it++) + { + NodeID id = *it; + outs() << rthdTovthd[id] << ", "; + } + outs() << "]\n"; +} + +bool MTAResultValidator::collectCallsiteTargets() +{ + for (SVFModule::const_iterator fi = getModule()->begin(), efi = getModule()->end(); fi != efi; ++fi) + { + for (Function::const_iterator bi = ((*fi)->getLLVMFun())->begin(), ebi = ((*fi)->getLLVMFun())->end(); bi != ebi; ++bi) + { + const BasicBlock* bb = &*bi; + if (!bb->getName().str().compare(0, 2, "cs")) + { + NodeID csnum = atoi(bb->getName().str().substr(2).c_str()); + const Instruction* inst = &bb->front(); + while (1) + { + if (SVFUtil::isa(inst)) + { + break; + } + inst = inst->getNextNode(); + assert(inst && "Wrong cs label, cannot find callsite"); + } + const CallInst *csInst = SVFUtil::dyn_cast(inst); + csnumToInstMap[csnum] = csInst; + } + } + } + if (csnumToInstMap.empty()) + return false; + return true; +} + +bool MTAResultValidator::collectCxtThreadTargets() +{ + const Function *F; + for(auto it = getModule()->llvmFunBegin(); it != getModule()->llvmFunEnd(); it++) { + const std::string fName = (*it)->getName().str(); + if(fName.find(CXT_THREAD) != std::string::npos) { + F = (*it); + break; + } + } + if (!F) + return false; + + // Push main thread into vthdToCxt; + CallStrCxt main_cxt; + vthdToCxt[0] = main_cxt; + + // Collect call sites of all CXT_THREAD function calls. + + for (Value::const_use_iterator it = F->use_begin(), ie = F->use_end(); it != ie; ++it) + { + const Use *u = &*it; + const Value *user = u->getUser(); + const Instruction *inst = SVFUtil::dyn_cast(user); + + NodeID vthdnum = getIntArg(inst, 0); + CallStrCxt cxt = getCxtArg(inst, 1); + + vthdToCxt[vthdnum] = cxt; + } + return true; +} + +bool MTAResultValidator::collectTCTTargets() +{ + + // Collect call sites of all TCT_ACCESS function calls. + const Function *F; + for(auto it = getModule()->llvmFunBegin(); it != getModule()->llvmFunEnd(); it++) { + const std::string fName = (*it)->getName().str(); + if(fName.find(TCT_ACCESS) != std::string::npos) { + F = (*it); + break; + } + } + if (!F) + return false; + + for (Value::const_use_iterator it = F->use_begin(), ie = F->use_end(); it != ie; ++it) + { + const Use *u = &*it; + const Value *user = u->getUser(); + const Instruction *inst = SVFUtil::dyn_cast(user); + + NodeID vthdnum = getIntArg(inst, 0); + NodeID rthdnum = vthdTorthd[vthdnum]; + std::vector x = getStringArg(inst, 1); + + for (std::vector::iterator i = x.begin(); i != x.end(); i++) + { + rthdToChildren[rthdnum].insert(vthdTorthd[atoi((*i).c_str())]); + } + } + return true; +} + +bool MTAResultValidator::collectInterleavingTargets() +{ + + // Collect call sites of all INTERLEV_ACCESS function calls. + const Function *F; + for(auto it = getModule()->llvmFunBegin(); it != getModule()->llvmFunEnd(); it++) { + const std::string fName = (*it)->getName().str(); + if(fName.find(INTERLEV_ACCESS) != std::string::npos) { + F = (*it); + break; + } + } + if (!F) + return false; + + for (Value::const_use_iterator it = F->use_begin(), ie = F->use_end(); it != ie; ++it) + { + const Use *u = &*it; + const Value *user = u->getUser(); + const Instruction *inst = SVFUtil::dyn_cast(user); + + NodeID vthdnum = getIntArg(inst, 0); + NodeID rthdnum = vthdTorthd[vthdnum]; + CallStrCxt x = getCxtArg(inst, 1); + std::vector y = getStringArg(inst, 2); + + // Record given interleaving + NodeBS lev; + // Push thread itself into interleaving set + lev.set(rthdnum); + // Find rthd of given vthd and push it into interleaving set + for (std::vector::iterator i = y.begin(); i != y.end(); i++) + { + lev.set(vthdTorthd[atoi((*i).c_str())]); + } + + const Instruction* memInst = getPreviousMemoryAccessInst(inst); + CxtThreadStmt cts(rthdnum, x, memInst); + instToTSMap[memInst].insert(cts); + threadStmtToInterLeaving[cts] = lev; + } + return true; +} + +bool MTAResultValidator::validateCxtThread() +{ + + bool res = true; + TCT* tct = mhp->getTCT(); + if (tct->getTCTNodeNum() != vthdToCxt.size()) + { + res = false; + if (Options::PrintValidRes) + { + outs() << errMsg("\nValidate CxtThread: The number of CxtThread is different from given result!!!\n"); + outs() << "Given threads:\t" << vthdToCxt.size() << "\nAnalysis result:\t" << tct->getTCTNodeNum() << "\n"; + assert(false && "test case failed!"); + } + } + + Set visitedvthd; + + for (NodeID i = 0; i < tct->getTCTNodeNum(); i++) + { + const CxtThread rthd = tct->getTCTNode(i)->getCxtThread(); + bool matched = false; + for (Map::iterator j = vthdToCxt.begin(), ej = vthdToCxt.end(); j != ej; j++) + { + NodeID vthdid = (*j).first; + if (matchCxt(rthd.getContext(), vthdToCxt[vthdid])) + { + if (visitedvthd.find(vthdid) != visitedvthd.end()) + { + res = false; + if (Options::PrintValidRes) + { + outs() << "\nValidate CxtThread: Repeat real CxtThread !!!\n"; + rthd.dump(); + tct->getTCTNode(vthdTorthd[vthdid])->getCxtThread().dump(); + } + } + vthdTorthd[vthdid] = i; + rthdTovthd[i] = vthdid; + visitedvthd.insert(vthdid); + matched = true; + break; + } + } + if (!matched) + { + res = false; + if (Options::PrintValidRes) + { + SVFUtil::errs() << errMsg("\nValidate CxtThread: Cannot match real CxtThread !!!\n"); + rthd.dump(); + assert(false && "test case failed!"); + } + } + } + if (visitedvthd.size() != vthdToCxt.size()) + { + res = false; + if (Options::PrintValidRes) + { + SVFUtil::errs() << errMsg("\nValidate CxtThread: Some given CxtThreads cannot be found !!!\n"); + assert(false && "test case failed!"); + for (Map::iterator j = vthdToCxt.begin(), ej = vthdToCxt.end(); j != ej; j++) + { + NodeID vthdid = (*j).first; + if (visitedvthd.find(vthdid) == visitedvthd.end()) + { + dumpCxt(vthdToCxt[vthdid]); + } + } + } + } + return res; +} + +bool MTAResultValidator::validateTCT() +{ + bool res = true; + + TCT* tct = mhp->getTCT(); + for (NodeID i = 0; i < tct->getTCTNodeNum(); i++) + { + bool res_node = true; + TCTNode* pnode = tct->getTCTNode(i); + for (TCT::ThreadCreateEdgeSet::const_iterator ci = tct->getChildrenBegin(pnode), cei = tct->getChildrenEnd(pnode); ci != cei; + ci++) + { + NodeID tid = (*ci)->getDstID(); + if (rthdToChildren[i].find(tid) == rthdToChildren[i].end()) + { + res = false; + res_node = false; + } + } + + for (Set::iterator j = rthdToChildren[i].begin(), ej = rthdToChildren[i].end(); j != ej; j++) + { + NodeID gid = *j; + if (!tct->hasGraphEdge(pnode, tct->getTCTNode(gid), TCTEdge::ThreadCreateEdge)) + { + res = false; + res_node = false; + } + } + if ((!res_node) && Options::PrintValidRes) + { + outs() << errMsg("\nValidate TCT: Wrong at TID ") << rthdTovthd[i] << "\n"; + outs() << "Given children: \t"; + for (Set::iterator j = rthdToChildren[i].begin(), ej = rthdToChildren[i].end(); j != ej; j++) + { + NodeID gid = *j; + outs() << rthdTovthd[gid] << ", "; + } + outs() << "\nAnalysis children:\t"; + for (TCT::ThreadCreateEdgeSet::const_iterator ci = tct->getChildrenBegin(pnode), cei = tct->getChildrenEnd(pnode); ci != cei; + ci++) + { + NodeID tid = (*ci)->getDstID(); + outs() << rthdTovthd[tid] << ", "; + } + outs() << "\n"; + } + } + return res; +} + +MTAResultValidator::INTERLEV_FLAG MTAResultValidator::validateInterleaving() +{ + MTAResultValidator::INTERLEV_FLAG res = MTAResultValidator::INTERLEV_TRUE; + + for (MHP::InstToThreadStmtSetMap::iterator seti = instToTSMap.begin(), eseti = instToTSMap.end(); seti != eseti; ++seti) + { + const Instruction* inst = (*seti).first; + + const MHP::CxtThreadStmtSet& tsSet = mhp->getThreadStmtSet(inst); + + if ((*seti).second.size() != tsSet.size()) + { + if (Options::PrintValidRes) + { + outs() << errMsg("\n Validate Interleaving: Wrong at : ") << SVFUtil::getSourceLoc(inst) << "\n"; + outs() << "Reason: The number of thread running on stmt is wrong\n"; + outs() << "\n----Given threads:\n"; + for (MHP::CxtThreadStmtSet::iterator thdlevi = (*seti).second.begin(), ethdlevi = (*seti).second.end(); thdlevi != ethdlevi; + ++thdlevi) + { + outs() << "TID " << rthdTovthd[(*thdlevi).getTid()] << ": "; + dumpCxt((*thdlevi).getContext()); + } + outs() << "\n----Analysis threads:\n"; + for (MHP::CxtThreadStmtSet::const_iterator it = tsSet.begin(), eit = tsSet.end(); it != eit; ++it) + { + outs() << "TID " << rthdTovthd[(*it).getTid()] << ": "; + dumpCxt((*it).getContext()); + } + outs() << "\n"; + } + res = MTAResultValidator::INTERLEV_UNSOUND; + } + + for (MHP::CxtThreadStmtSet::const_iterator it = tsSet.begin(), eit = tsSet.end(); it != eit; ++it) + { + const CxtThreadStmt& ts = *it; + bool matched = false; + for (MHP::CxtThreadStmtSet::iterator it2 = (*seti).second.begin(), eit2 = (*seti).second.end(); it2 != eit2; ++it2) + { + const CxtThreadStmt& ts2 = *it2; + + if (ts2.getTid() == ts.getTid() && matchCxt(ts2.getContext(), ts.getContext())) + { + matched = true; + NodeBS lev = mhp->getInterleavingThreads(ts); + NodeBS lev2 = threadStmtToInterLeaving[ts2]; + if (lev != lev2) + { + if (Options::PrintValidRes) + { + outs() << errMsg("\nValidate Interleaving: Wrong at: ") << SVFUtil::getSourceLoc(inst) << "\n"; + outs() << "Reason: thread interleaving on stmt is wrong\n"; + dumpCxt(ts.getContext()); + outs() << "Given result: \tTID " << rthdTovthd[ts.getTid()]; + dumpInterlev(lev2); + outs() << "Analysis result: \tTID " << rthdTovthd[ts.getTid()]; + dumpInterlev(lev); + } + + if (MTAResultValidator::INTERLEV_IMPRECISE > res) + res = MTAResultValidator::INTERLEV_IMPRECISE; + + if (lev.count() >= lev2.count()) + { + bool findeveryelement = true; + for (NodeBS::iterator it = lev2.begin(), eit = lev2.end(); it != eit; it++) + { + if (!lev.test(*it)) + { + findeveryelement = false; + break; + } + } + if (!findeveryelement) + res = MTAResultValidator::INTERLEV_UNSOUND; + } + else + res = MTAResultValidator::INTERLEV_UNSOUND; + } + } + } + + if (!matched) + { + if (Options::PrintValidRes) + { + outs() << errMsg("\nValidate Interleaving: Wrong at:") << SVFUtil::getSourceLoc(inst) << "\n"; + outs() << "Reason: analysis thread cxt is not matched by given thread cxt\n"; + dumpCxt(ts.getContext()); + NodeBS lev = mhp->getInterleavingThreads(ts); + + outs() << "Analysis result: \tTID " << rthdTovthd[ts.getTid()]; + dumpInterlev(lev); + } + res = MTAResultValidator::INTERLEV_UNSOUND; + } + } + } + return res; +} diff --git a/svf/lib/MTA/MTAStat.cpp b/svf/lib/MTA/MTAStat.cpp index 62ccb4713..3167c75a6 100644 --- a/svf/lib/MTA/MTAStat.cpp +++ b/svf/lib/MTA/MTAStat.cpp @@ -1,25 +1,3 @@ -//===- MTAStat.cpp -- Statistics for MTA-------------// -// -// SVF: Static Value-Flow Analysis -// -// Copyright (C) <2013-> -// - -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU Affero General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Affero General Public License for more details. - -// You should have received a copy of the GNU Affero General Public License -// along with this program. If not, see . -// -//===----------------------------------------------------------------------===// - /* * MTAStat.cpp * @@ -33,6 +11,7 @@ #include "MTA/MHP.h" #include "MTA/LockAnalysis.h" #include "MTA/FSMPTA.h" +#include "MTA/MTAAnnotator.h" #include "Graphs/ThreadCallGraph.h" using namespace SVF; @@ -50,7 +29,7 @@ void MTAStat::performThreadCallGraphStat(ThreadCallGraph* tcg) for (ThreadCallGraph::CallSiteSet::const_iterator it = tcg->forksitesBegin(), eit = tcg->forksitesEnd(); it != eit; ++it) { bool indirectfork = false; - const SVFFunction* spawnee = SVFUtil::dyn_cast(tcg->getThreadAPI()->getForkedFun((*it)->getCallSite())); + const Function* spawnee = SVFUtil::dyn_cast(tcg->getThreadAPI()->getForkedFun((*it)->getCallSite())); if(spawnee==nullptr) { numOfIndForksite++; @@ -84,7 +63,7 @@ void MTAStat::performThreadCallGraphStat(ThreadCallGraph* tcg) PTNumStatMap["NumOfIndForkEdge"] = numOfIndForkEdge; PTNumStatMap["NumOfIndCallEdge"] = tcg->getNumOfResolvedIndCallEdge(); - SVFUtil::outs() << "\n****Thread Call Graph Statistics****\n"; + std::cout << "\n****Thread Call Graph Statistics****\n"; PTAStat::printStat(); } @@ -100,7 +79,7 @@ void MTAStat::performTCTStat(TCT* tct) PTNumStatMap["NumOfTCTEdge"] = tct->getTCTEdgeNum(); PTNumStatMap["MaxCxtSize"] = tct->getMaxCxtSize(); timeStatMap["BuildingTCTTime"] = TCTTime; - SVFUtil::outs() << "\n****Thread Creation Tree Statistics****\n"; + std::cout << "\n****Thread Creation Tree Statistics****\n"; PTAStat::printStat(); } @@ -112,45 +91,38 @@ void MTAStat::performTCTStat(TCT* tct) void MTAStat::performMHPPairStat(MHP* mhp, LockAnalysis* lsa) { - SVFIR* pag = SVFIR::getPAG(); - if(Options::AllPairMHP()) + if(Options::AllPairMHP) { - Set instSet1; - Set instSet2; + InstSet instSet1; + InstSet instSet2; SVFModule* mod = mhp->getTCT()->getSVFModule(); - for (const SVFFunction* fun : mod->getFunctionSet()) + for (SVFModule::iterator F = mod->begin(), E = mod->end(); F != E; ++F) { + const SVFFunction* fun = (*F); + const Function* llvmfun = fun->getLLVMFun(); if(SVFUtil::isExtCall(fun)) continue; - if(!mhp->isConnectedfromMain(fun)) + if(!mhp->isConnectedfromMain(llvmfun)) continue; - for (SVFFunction::const_iterator bit = fun->begin(), ebit = fun->end(); bit != ebit; ++bit) + for (const_inst_iterator II = inst_begin(llvmfun), E = inst_end(llvmfun); II != E; ++II) { - const SVFBasicBlock* bb = *bit; - for (SVFBasicBlock::const_iterator ii = bb->begin(), eii = bb->end(); ii != eii; ++ii) + const Instruction *inst = &*II; + if(SVFUtil::isa(inst)) { - const SVFInstruction* inst = *ii; - for(const SVFStmt* stmt : pag->getSVFStmtList(pag->getICFG()->getICFGNode(inst))) - { - if(SVFUtil::isa(stmt)) - { - instSet1.insert(inst); - } - else if(SVFUtil::isa(stmt)) - { - instSet1.insert(inst); - instSet2.insert(inst); - } - } - + instSet1.insert(inst); + } + else if(SVFUtil::isa(inst)) + { + instSet1.insert(inst); + instSet2.insert(inst); } } } - for(Set::const_iterator it1 = instSet1.begin(), eit1 = instSet1.end(); it1!=eit1; ++it1) + for(InstSet::const_iterator it1 = instSet1.begin(), eit1 = instSet1.end(); it1!=eit1; ++it1) { - for(Set::const_iterator it2 = instSet2.begin(), eit2 = instSet2.end(); it2!=eit2; ++it2) + for(InstSet::const_iterator it2 = instSet2.begin(), eit2 = instSet2.end(); it2!=eit2; ++it2) { mhp->mayHappenInParallel(*it1,*it2); } @@ -176,28 +148,28 @@ void MTAStat::performMHPPairStat(MHP* mhp, LockAnalysis* lsa) timeStatMap["MHPAnalysisTime"] = MHPTime; timeStatMap["MFSPTATime"] = FSMPTATime; - SVFUtil::outs() << "\n****MHP Stmt Pairs Statistics****\n"; + std::cout << "\n****MHP Stmt Pairs Statistics****\n"; PTAStat::printStat(); } -// void MTAStat::performAnnotationStat(MTAAnnotator* anno) -// { - -// PTNumStatMap.clear(); -// timeStatMap.clear(); -// PTNumStatMap["TotalNumOfStore"] = anno->numOfAllSt; -// PTNumStatMap["TotalNumOfLoad"] = anno->numOfAllLd; -// PTNumStatMap["NumOfNonLocalStore"] = anno->numOfNonLocalSt; -// PTNumStatMap["NumOfNonLocalLoad"] = anno->numOfNonLocalLd; -// PTNumStatMap["NumOfAliasStore"] = anno->numOfAliasSt; -// PTNumStatMap["NumOfAliasLoad"] = anno->numOfAliasLd; -// PTNumStatMap["NumOfMHPStore"] = anno->numOfMHPSt; -// PTNumStatMap["NumOfMHPLoad"] = anno->numOfMHPLd; -// PTNumStatMap["NumOfAnnotatedStore"] = anno->numOfAnnotatedSt; -// PTNumStatMap["NumOfAnnotatedLoad"] = anno->numOfAnnotatedLd; -// timeStatMap["AnnotationTime"] = AnnotationTime; - -// SVFUtil::outs() << "\n****Annotation Statistics****\n"; -// PTAStat::printStat(); -// } +void MTAStat::performAnnotationStat(MTAAnnotator* anno) +{ + + PTNumStatMap.clear(); + timeStatMap.clear(); + PTNumStatMap["TotalNumOfStore"] = anno->numOfAllSt; + PTNumStatMap["TotalNumOfLoad"] = anno->numOfAllLd; + PTNumStatMap["NumOfNonLocalStore"] = anno->numOfNonLocalSt; + PTNumStatMap["NumOfNonLocalLoad"] = anno->numOfNonLocalLd; + PTNumStatMap["NumOfAliasStore"] = anno->numOfAliasSt; + PTNumStatMap["NumOfAliasLoad"] = anno->numOfAliasLd; + PTNumStatMap["NumOfMHPStore"] = anno->numOfMHPSt; + PTNumStatMap["NumOfMHPLoad"] = anno->numOfMHPLd; + PTNumStatMap["NumOfAnnotatedStore"] = anno->numOfAnnotatedSt; + PTNumStatMap["NumOfAnnotatedLoad"] = anno->numOfAnnotatedLd; + timeStatMap["AnnotationTime"] = AnnotationTime; + + std::cout << "\n****Annotation Statistics****\n"; + PTAStat::printStat(); +} diff --git a/svf/lib/MTA/PCG.cpp b/svf/lib/MTA/PCG.cpp index eed87d0ef..140bfd31d 100644 --- a/svf/lib/MTA/PCG.cpp +++ b/svf/lib/MTA/PCG.cpp @@ -1,25 +1,3 @@ -//===- PCG.cpp -- Procedure creation graph-------------// -// -// SVF: Static Value-Flow Analysis -// -// Copyright (C) <2013-> -// - -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU Affero General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Affero General Public License for more details. - -// You should have received a copy of the GNU Affero General Public License -// along with this program. If not, see . -// -//===----------------------------------------------------------------------===// - /* * PCG.cpp * @@ -27,7 +5,6 @@ * Author: Yulei Sui, Peng Di */ -#include "Util/CommandLine.h" #include "Util/Options.h" #include "MTA/PCG.h" #include "Util/SVFUtil.h" @@ -40,12 +17,7 @@ using namespace SVFUtil; * Whether two functions may happen in parallel */ -//static Option TDPrint( -// "print-td", -// true, -// "Print Thread Analysis Results" -//); - +//static llvm::cl::opt TDPrint("print-td", llvm::cl::init(true), llvm::cl::desc("Print Thread Analysis Results")); bool PCG::analyze() { @@ -59,14 +31,14 @@ bool PCG::analyze() //interferenceAnalysis(); - //if (Options::TDPrint()) { + //if (Options::TDPrint) { //printResults(); //tdAPI->performAPIStat(mod); //} return false; } -bool PCG::mayHappenInParallelBetweenFunctions(const SVFFunction* fun1, const SVFFunction* fun2) const +bool PCG::mayHappenInParallelBetweenFunctions(const Function* fun1, const Function* fun2) const { // if neither of functions are spawnees, then they won't happen in parallel if (isSpawneeFun(fun1) == false && isSpawneeFun(fun2) == false) @@ -80,10 +52,10 @@ bool PCG::mayHappenInParallelBetweenFunctions(const SVFFunction* fun1, const SVF return true; } -bool PCG::mayHappenInParallel(const SVFInstruction* i1, const SVFInstruction* i2) const +bool PCG::mayHappenInParallel(const Instruction* i1, const Instruction* i2) const { - const SVFFunction* fun1 = i1->getFunction(); - const SVFFunction* fun2 = i2->getFunction(); + const Function* fun1 = i1->getParent()->getParent(); + const Function* fun2 = i2->getParent()->getParent(); return mayHappenInParallelBetweenFunctions(fun1, fun2); } @@ -95,28 +67,27 @@ bool PCG::mayHappenInParallel(const SVFInstruction* i1, const SVFInstruction* i2 */ void PCG::initFromThreadAPI(SVFModule* module) { - for (const SVFFunction* fun : module->getFunctionSet()) + for (SVFModule::const_iterator fi = module->begin(), efi = module->end(); fi != efi; ++fi) { - for (const SVFBasicBlock* svfbb : fun->getBasicBlockList()) + const Function* fun = (*fi)->getLLVMFun(); + for (inst_iterator II = inst_begin((*fi)->getLLVMFun()), E = inst_end((*fi)->getLLVMFun()); II != E; ++II) { - for (const SVFInstruction* inst : svfbb->getInstructionList()) + const Instruction *inst = &*II; + if (tdAPI->isTDFork(inst)) { - if (tdAPI->isTDFork(inst)) + const Value* forkVal = tdAPI->getForkedFun(inst); + if (const Function* forkFun = getLLVMFunction(forkVal)) { - const SVFValue* forkVal = tdAPI->getForkedFun(inst); - if (const SVFFunction* svForkfun = SVFUtil::dyn_cast(forkVal)) - { - addSpawnsite(inst); - spawners.insert(fun); - spawnees.insert(svForkfun); - } - /// TODO: handle indirect call here for the fork Fun - else - { - writeWrnMsg("pthread create"); - outs() << inst->toString() << "\n"; - writeWrnMsg("invoke spawnee indirectly"); - } + addSpawnsite(inst); + spawners.insert(fun); + spawnees.insert(forkFun); + } + /// TODO: handle indirect call here for the fork Fun + else + { + writeWrnMsg("pthread create"); + outs() << *inst << "\n"; + writeWrnMsg("invoke spawnee indirectly"); } } } @@ -154,13 +125,14 @@ void PCG::collectSpawners() } while (!worklist.empty()) { - const SVFFunction* svffun = worklist.pop(); + const Function* fun = worklist.pop(); + const SVFFunction* svffun = getSVFFun(fun); PTACallGraphNode* funNode = callgraph->getCallGraphNode(svffun); for (PTACallGraphNode::const_iterator it = funNode->InEdgeBegin(), eit = funNode->InEdgeEnd(); it != eit; ++it) { PTACallGraphEdge* callEdge = (*it); - const SVFFunction* caller = callEdge->getSrcNode()->getFunction(); + const Function* caller = callEdge->getSrcNode()->getFunction()->getLLVMFun(); if (isSpawnerFun(caller) == false) { worklist.push(caller); @@ -195,12 +167,13 @@ void PCG::collectSpawnees() } while (!worklist.empty()) { - const SVFFunction* svffun = worklist.pop(); + const Function* fun = worklist.pop(); + const SVFFunction* svffun = getSVFFun(fun); PTACallGraphNode* funNode = callgraph->getCallGraphNode(svffun); for (PTACallGraphNode::const_iterator it = funNode->OutEdgeBegin(), eit = funNode->OutEdgeEnd(); it != eit; ++it) { - const SVFFunction* caller = (*it)->getDstNode()->getFunction(); + const Function* caller = (*it)->getDstNode()->getFunction()->getLLVMFun(); if (isSpawneeFun(caller) == false) { worklist.push(caller); @@ -219,38 +192,39 @@ void PCG::identifyFollowers() for (CallInstSet::const_iterator sit = spawnSitesBegin(), esit = spawnSitesEnd(); sit != esit; ++sit) { - const SVFInstruction* inst = *sit; + const Instruction* inst = *sit; BBWorkList bb_worklist; - Set visitedBBs; + Set visitedBBs; bb_worklist.push(inst->getParent()); while (!bb_worklist.empty()) { - const SVFBasicBlock* bb = bb_worklist.pop(); - for (SVFBasicBlock::const_iterator it = bb->begin(), eit = bb->end(); it != eit; ++it) + const BasicBlock* bb = bb_worklist.pop(); + for (BasicBlock::const_iterator it = bb->begin(), eit = bb->end(); it != eit; ++it) { - const SVFInstruction* inst = *it; + const Instruction* inst = &*it; // mark the callee of this callsite as follower // if this is an call/invoke instruction but not a spawn site - if ((SVFUtil::isCallSite(inst)) && !isSpawnsite(inst)) + if ((SVFUtil::isa(inst) || SVFUtil::isa(inst)) && !isSpawnsite(inst)) { - CallICFGNode* cbn = getCallICFGNode(inst); + CallBlockNode* cbn = getCallBlockNode(inst); if (callgraph->hasCallGraphEdge(cbn)) { for (PTACallGraph::CallGraphEdgeSet::const_iterator cgIt = callgraph->getCallEdgeBegin(cbn), ecgIt = callgraph->getCallEdgeEnd(cbn); cgIt != ecgIt; ++cgIt) { const PTACallGraphEdge* edge = *cgIt; - addFollowerFun(edge->getDstNode()->getFunction()); + addFollowerFun(edge->getDstNode()->getFunction()->getLLVMFun()); } } } } - for (const SVFBasicBlock* svf_scc_bb : bb->getSuccessors()) + for (succ_const_iterator succ_it = succ_begin(bb); succ_it != succ_end(bb); succ_it++) { - if (visitedBBs.count(svf_scc_bb) == 0) + const BasicBlock* succ_bb = *succ_it; + if (visitedBBs.count(succ_bb) == 0) { - visitedBBs.insert(svf_scc_bb); - bb_worklist.push(svf_scc_bb); + visitedBBs.insert(succ_bb); + bb_worklist.push(succ_bb); } } } @@ -276,12 +250,13 @@ void PCG::collectFollowers() } while (!worklist.empty()) { - const SVFFunction* svffun = worklist.pop(); + const Function* fun = worklist.pop(); + const SVFFunction* svffun = getSVFFun(fun); PTACallGraphNode* funNode = callgraph->getCallGraphNode(svffun); for (PTACallGraphNode::const_iterator it = funNode->OutEdgeBegin(), eit = funNode->OutEdgeEnd(); it != eit; ++it) { - const SVFFunction* caller = (*it)->getDstNode()->getFunction(); + const Function* caller = (*it)->getDstNode()->getFunction()->getLLVMFun(); if (isFollowerFun(caller) == false) { worklist.push(caller); @@ -310,18 +285,18 @@ void PCG::interferenceAnalysis() const SVFFunction* fun = *F; if (isExtCall(fun)) continue; - worklist.push_back(fun); + worklist.push_back(fun->getLLVMFun()); } while (!worklist.empty()) { - const SVFFunction* fun1 = worklist.back(); + const Function* fun1 = worklist.back(); worklist.pop_back(); bool ismhpfun = false; for (PCG::FunVec::iterator it = worklist.begin(), eit = worklist.end(); it != eit; ++it) { - const SVFFunction* fun2 = *it; + const Function* fun2 = *it; if (mayHappenInParallelBetweenFunctions(fun1, fun2)) { ismhpfun = true; @@ -352,7 +327,7 @@ void PCG::printTDFuns() for (SVFModule::const_iterator fi = mod->begin(), efi = mod->end(); fi != efi; ++fi) { - const SVFFunction* fun = (*fi); + const Function* fun = (*fi)->getLLVMFun(); if (fun->isDeclaration()) continue; diff --git a/svf/lib/MTA/TCT.cpp b/svf/lib/MTA/TCT.cpp index 8dcb21969..1b6fe0528 100644 --- a/svf/lib/MTA/TCT.cpp +++ b/svf/lib/MTA/TCT.cpp @@ -1,26 +1,3 @@ -//===- TCT.cpp -- Thread creation tree-------------// -// -// SVF: Static Value-Flow Analysis -// -// Copyright (C) <2013-> -// - -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU Affero General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Affero General Public License for more details. - -// You should have received a copy of the GNU Affero General Public License -// along with this program. If not, see . -// -//===----------------------------------------------------------------------===// - - /* * TCT.cpp * @@ -31,6 +8,7 @@ #include "Util/Options.h" #include "MTA/TCT.h" #include "MTA/MTA.h" +#include "SVF-FE/DataFlowUtil.h" #include @@ -42,19 +20,19 @@ using namespace SVFUtil; * (1) the instruction i itself * (2) all the callsites invoke the function where i resides in */ -bool TCT::isInLoopInstruction(const SVFInstruction* inst) +bool TCT::isInLoopInstruction(const Instruction* inst) { assert(inst && "null value instruction!!"); InstSet insts; - FIFOWorkList worklist; + FIFOWorkList worklist; worklist.push(inst); while(!worklist.empty()) { - const SVFInstruction* inst = worklist.pop(); + const Instruction* inst = worklist.pop(); insts.insert(inst); - PTACallGraphNode* cgnode = tcg->getCallGraphNode(inst->getFunction()); + PTACallGraphNode* cgnode = tcg->getCallGraphNode(getSVFFun(inst->getParent()->getParent())); for(PTACallGraphNode::const_iterator nit = cgnode->InEdgeBegin(), neit = cgnode->InEdgeEnd(); nit!=neit; nit++) { for(PTACallGraphEdge::CallInstSet::const_iterator cit = (*nit)->directCallsBegin(), @@ -74,8 +52,8 @@ bool TCT::isInLoopInstruction(const SVFInstruction* inst) for(InstSet::const_iterator it = insts.begin(), eit = insts.end(); it!=eit; ++it) { - const SVFInstruction* i = *it; - if(i->getFunction()->hasLoopInfo(i->getParent())) + const Instruction* i = *it; + if(getLoop(i)) return true; } @@ -88,17 +66,18 @@ bool TCT::isInLoopInstruction(const SVFInstruction* inst) * (1) the function f where i resides in is in a recursion * (2) any caller function starting from the function f in is in a recursion */ -bool TCT::isInRecursion(const SVFInstruction* inst) const +bool TCT::isInRecursion(const Instruction* inst) const { - const SVFFunction* f = inst->getFunction(); - FIFOWorkList worklist; - Set visits; + const Function* f = inst->getParent()->getParent(); + FIFOWorkList worklist; + Set visits; worklist.push(f); while(!worklist.empty()) { - const SVFFunction* svffun = worklist.pop(); - visits.insert(svffun); + const Function* fun = worklist.pop(); + visits.insert(fun); + const SVFFunction* svffun = getSVFFun(fun); if(tcgSCC->isInCycle(tcg->getCallGraphNode(svffun)->getId())) return true; @@ -109,14 +88,14 @@ bool TCT::isInRecursion(const SVFInstruction* inst) const for(PTACallGraphEdge::CallInstSet::const_iterator cit = (*nit)->directCallsBegin(), ecit = (*nit)->directCallsEnd(); cit!=ecit; ++cit) { - const SVFFunction* caller = (*cit)->getCallSite()->getFunction(); + const Function* caller = (*cit)->getCallSite()->getParent()->getParent(); if(visits.find(caller)==visits.end()) worklist.push(caller); } for(PTACallGraphEdge::CallInstSet::const_iterator cit = (*nit)->indirectCallsBegin(), ecit = (*nit)->indirectCallsEnd(); cit!=ecit; ++cit) { - const SVFFunction* caller = (*cit)->getCallSite()->getFunction(); + const Function* caller = (*cit)->getCallSite()->getParent()->getParent(); if(visits.find(caller)==visits.end()) worklist.push(caller); } @@ -134,21 +113,19 @@ void TCT::markRelProcs() { for (ThreadCallGraph::CallSiteSet::const_iterator it = tcg->forksitesBegin(), eit = tcg->forksitesEnd(); it != eit; ++it) { - const SVFFunction* svfun = (*it)->getParent()->getParent(); - markRelProcs(svfun); + markRelProcs((*it)->getParent()->getParent()); for(ThreadCallGraph::ForkEdgeSet::const_iterator nit = tcg->getForkEdgeBegin(*it), neit = tcg->getForkEdgeEnd(*it); nit!=neit; nit++) { const PTACallGraphNode* forkeeNode = (*nit)->getDstNode(); - candidateFuncSet.insert(forkeeNode->getFunction()); + candidateFuncSet.insert(forkeeNode->getFunction()->getLLVMFun()); } } for (ThreadCallGraph::CallSiteSet::const_iterator it = tcg->joinsitesBegin(), eit = tcg->joinsitesEnd(); it != eit; ++it) { - const SVFFunction* svfun = (*it)->getParent()->getParent(); - markRelProcs(svfun); + markRelProcs((*it)->getParent()->getParent()); } if(candidateFuncSet.empty()) @@ -158,8 +135,9 @@ void TCT::markRelProcs() /*! * */ -void TCT::markRelProcs(const SVFFunction* svffun) +void TCT::markRelProcs(const Function* fun) { + const SVFFunction* svffun = getSVFFun(fun); PTACallGraphNode* cgnode = tcg->getCallGraphNode(svffun); FIFOWorkList worklist; PTACGNodeSet visited; @@ -168,7 +146,7 @@ void TCT::markRelProcs(const SVFFunction* svffun) while(!worklist.empty()) { const PTACallGraphNode* node = worklist.pop(); - candidateFuncSet.insert(node->getFunction()); + candidateFuncSet.insert(node->getFunction()->getLLVMFun()); for(PTACallGraphNode::const_iterator nit = node->InEdgeBegin(), neit = node->InEdgeEnd(); nit!=neit; nit++) { const PTACallGraphNode* srcNode = (*nit)->getSrcNode(); @@ -194,7 +172,7 @@ void TCT::collectEntryFunInCallGraph() PTACallGraphNode* node = tcg->getCallGraphNode(fun); if (!node->hasIncomingEdge()) { - entryFuncSet.insert(fun); + entryFuncSet.insert(fun->getLLVMFun()); } } assert(!entryFuncSet.empty() && "Can't find any function in module!"); @@ -243,11 +221,12 @@ void TCT::collectMultiForkedThreads() */ void TCT::handleCallRelation(CxtThreadProc& ctp, const PTACallGraphEdge* cgEdge, CallSite cs) { - const SVFFunction* callee = cgEdge->getDstNode()->getFunction(); + const SVFFunction* callee = cgEdge->getDstNode()->getFunction(); + const Function* llvmcallee = callee->getLLVMFun(); CallStrCxt cxt(ctp.getContext()); CallStrCxt oldCxt = cxt; - pushCxt(cxt,cs.getInstruction(),callee); + pushCxt(cxt,cs.getInstruction(),llvmcallee); if(cgEdge->getEdgeKind() == PTACallGraphEdge::CallRetEdge) { @@ -261,8 +240,10 @@ void TCT::handleCallRelation(CxtThreadProc& ctp, const PTACallGraphEdge* cgEdge, else if(cgEdge->getEdgeKind() == PTACallGraphEdge::TDForkEdge) { + const CallInst* fork = SVFUtil::cast(cs.getInstruction()); + /// Create spawnee TCT node - TCTNode* spawneeNode = getOrCreateTCTNode(cxt,cs.getInstruction(), oldCxt, callee); + TCTNode* spawneeNode = getOrCreateTCTNode(cxt,fork, oldCxt, llvmcallee); CxtThreadProc newctp(spawneeNode->getId(),cxt,callee); if(pushToCTPWorkList(newctp)) @@ -286,19 +267,19 @@ void TCT::handleCallRelation(CxtThreadProc& ctp, const PTACallGraphEdge* cgEdge, * Return true if a join instruction must be executed inside a loop * joinbb should post dominate the successive basic block of a loop header */ -bool TCT::isJoinMustExecutedInLoop(const LoopBBs& lp,const SVFInstruction* join) +bool TCT::isJoinMustExecutedInLoop(const Loop* lp,const Instruction* join) { - assert(!lp.empty() && "this is not a loop, empty basic block"); - const SVFFunction* svffun = join->getFunction(); - const SVFBasicBlock* loopheadbb = svffun->getLoopHeader(lp); - const SVFBasicBlock* joinbb = join->getParent(); + const BasicBlock* loopheadbb = lp->getHeader(); + const BasicBlock* joinbb = join->getParent(); assert(loopheadbb->getParent()==joinbb->getParent() && "should inside same function"); - for (const SVFBasicBlock* svf_scc_bb : loopheadbb->getSuccessors()) + const PostDominatorTree* pdt = getPostDT(loopheadbb->getParent()); + for (succ_const_iterator it = succ_begin(loopheadbb), ie = succ_end(loopheadbb); + it != ie; ++it) { - if(svffun->loopContainsBB(lp,svf_scc_bb)) + if(lp->contains(*it)) { - if(svffun->dominate(joinbb,svf_scc_bb)==false) + if(pdt->dominates(joinbb,*it)==false) return false; } } @@ -314,17 +295,11 @@ void TCT::collectLoopInfoForJoin() { for(ThreadCallGraph::CallSiteSet::const_iterator it = tcg->joinsitesBegin(), eit = tcg->joinsitesEnd(); it!=eit; ++it) { - const SVFInstruction* join = (*it)->getCallSite(); - const SVFFunction* svffun = join->getFunction(); - const SVFBasicBlock* svfbb = join->getParent(); - - if(svffun->hasLoopInfo(svfbb)) + const Instruction* join = (*it)->getCallSite(); + const Loop* lp = getLoop(join); + if(lp && isJoinMustExecutedInLoop(lp,join)) { - const LoopBBs& lp = svffun->getLoopInfo(svfbb); - if(!lp.empty() && isJoinMustExecutedInLoop(lp,join)) - { - joinSiteToLoopMap[join] = lp; - } + joinSiteToLoopMap[join] = lp; } if(isInRecursion(join)) @@ -335,11 +310,11 @@ void TCT::collectLoopInfoForJoin() /*! * Return true if a given bb is a loop head of a inloop join site */ -bool TCT::isLoopHeaderOfJoinLoop(const SVFBasicBlock* bb) +bool TCT::isLoopHeaderOfJoinLoop(const BasicBlock* bb) { for(InstToLoopMap::const_iterator it = joinSiteToLoopMap.begin(), eit = joinSiteToLoopMap.end(); it!=eit; ++it) { - if(bb->getParent()->getLoopHeader(it->second) == bb) + if(it->second->getHeader() == bb) return true; } @@ -349,16 +324,15 @@ bool TCT::isLoopHeaderOfJoinLoop(const SVFBasicBlock* bb) /*! * Whether a given bb is an exit of a inloop join site */ -bool TCT::isLoopExitOfJoinLoop(const SVFBasicBlock* bb) +bool TCT::isLoopExitOfJoinLoop(const BasicBlock* bb) { for(InstToLoopMap::const_iterator it = joinSiteToLoopMap.begin(), eit = joinSiteToLoopMap.end(); it!=eit; ++it) { - std::vector exitbbs; - it->first->getFunction()->getExitBlocksOfLoop(it->first->getParent(),exitbbs); + SmallBBVector exitbbs; + it->second->getExitBlocks(exitbbs); while(!exitbbs.empty()) { - const SVFBasicBlock* eb = exitbbs.back(); - exitbbs.pop_back(); + BasicBlock* eb = exitbbs.pop_back_val(); if(eb == bb) return true; } @@ -370,10 +344,39 @@ bool TCT::isLoopExitOfJoinLoop(const SVFBasicBlock* bb) /*! * Get loop for fork/join site */ -const TCT::LoopBBs& TCT::getLoop(const SVFBasicBlock* bb) +const Loop* TCT::getLoop(const Instruction* inst) +{ + const Function* fun = inst->getParent()->getParent(); + return loopInfoBuilder.getLoopInfo(fun)->getLoopFor(inst->getParent()); +} + +/// Get dominator for a function +const DominatorTree* TCT::getDT(const Function* fun) +{ + return loopInfoBuilder.getDT(fun); +} + +/// Get dominator for a function +const PostDominatorTree* TCT::getPostDT(const Function* fun) +{ + return loopInfoBuilder.getPostDT(fun); +} +/*! + * Get loop for fork/join site + */ +const Loop* TCT::getLoop(const BasicBlock* bb) { - const SVFFunction* fun = bb->getParent(); - return fun->getLoopInfo(bb); + const Function* fun = bb->getParent(); + return loopInfoBuilder.getLoopInfo(fun)->getLoopFor(bb); +} + +/*! + * Get SE for function + */ +ScalarEvolution* TCT::getSE(const Instruction* inst) +{ + const Function* fun = inst->getParent()->getParent(); + return MTA::getSE(fun); } /*! @@ -397,7 +400,7 @@ void TCT::build() continue; CallStrCxt cxt; TCTNode* mainTCTNode = getOrCreateTCTNode(cxt, nullptr, cxt, *it); - CxtThreadProc t(mainTCTNode->getId(), cxt, *it); + CxtThreadProc t(mainTCTNode->getId(), cxt, getSVFFun(*it)); pushToCTPWorkList(t); } @@ -405,7 +408,7 @@ void TCT::build() { CxtThreadProc ctp = popFromCTPWorkList(); PTACallGraphNode* cgNode = tcg->getCallGraphNode(ctp.getProc()); - if(isCandidateFun(cgNode->getFunction()) == false) + if(isCandidateFun(cgNode->getFunction()->getLLVMFun()) == false) continue; for(PTACallGraphNode::const_iterator nit = cgNode->OutEdgeBegin(), neit = cgNode->OutEdgeEnd(); nit!=neit; nit++) @@ -416,20 +419,20 @@ void TCT::build() ecit = cgEdge->directCallsEnd(); cit!=ecit; ++cit) { DBOUT(DMTA,outs() << "\nTCT handling direct call:" << **cit << "\t" << cgEdge->getSrcNode()->getFunction()->getName() << "-->" << cgEdge->getDstNode()->getFunction()->getName() << "\n"); - handleCallRelation(ctp,cgEdge,getSVFCallSite((*cit)->getCallSite())); + handleCallRelation(ctp,cgEdge,getLLVMCallSite((*cit)->getCallSite())); } for(PTACallGraphEdge::CallInstSet::const_iterator ind = cgEdge->indirectCallsBegin(), eind = cgEdge->indirectCallsEnd(); ind!=eind; ++ind) { DBOUT(DMTA,outs() << "\nTCT handling indirect call:" << **ind << "\t" << cgEdge->getSrcNode()->getFunction()->getName() << "-->" << cgEdge->getDstNode()->getFunction()->getName() << "\n"); - handleCallRelation(ctp,cgEdge,getSVFCallSite((*ind)->getCallSite())); + handleCallRelation(ctp,cgEdge,getLLVMCallSite((*ind)->getCallSite())); } } } collectMultiForkedThreads(); - if (Options::TCTDotGraph()) + if (Options::TCTDotGraph) { print(); dump("tct"); @@ -437,20 +440,49 @@ void TCT::build() } +/*! + * Get the next instructions following control flow + */ +void TCT::getNextInsts(const Instruction* curInst, InstVec& instList) +{ + /// traverse to successive statements + if (!curInst->isTerminator()) + { + instList.push_back(curInst->getNextNode()); + } + else + { + const BasicBlock *BB = curInst->getParent(); + // Visit all successors of BB in the CFG + for (succ_const_iterator it = succ_begin(BB), ie = succ_end(BB); + it != ie; ++it) + { + /// if we are sitting at the loop header, then go inside the loop but ignore loop exit + if(isLoopHeaderOfJoinLoop(BB) && !getLoop(BB)->contains(*it)) + { + continue; + } + instList.push_back(&((*it)->front())); + } + } +} + /*! * Push calling context */ -void TCT::pushCxt(CallStrCxt& cxt, const SVFInstruction* call, const SVFFunction* callee) +void TCT::pushCxt(CallStrCxt& cxt, const Instruction* call, const Function* callee) { - const SVFFunction* caller = call->getFunction(); - CallSiteID csId = tcg->getCallSiteID(getCallICFGNode(call), callee); + const Function* caller = call->getParent()->getParent(); + const SVFFunction* svfcaller = getSVFFun(caller); + const SVFFunction* svfcallee = getSVFFun(callee); + CallSiteID csId = tcg->getCallSiteID(getCallBlockNode(call), svfcallee); /// handle calling context for candidate functions only if(isCandidateFun(caller) == false) return; - if(inSameCallGraphSCC(tcg->getCallGraphNode(caller),tcg->getCallGraphNode(callee))==false) + if(inSameCallGraphSCC(tcg->getCallGraphNode(getSVFFun(caller)),tcg->getCallGraphNode(getSVFFun(callee)))==false) { pushCxt(cxt,csId); DBOUT(DMTA,dumpCxt(cxt)); @@ -461,11 +493,12 @@ void TCT::pushCxt(CallStrCxt& cxt, const SVFInstruction* call, const SVFFunction /*! * Match calling context */ -bool TCT::matchCxt(CallStrCxt& cxt, const SVFInstruction* call, const SVFFunction* callee) +bool TCT::matchCxt(CallStrCxt& cxt, const Instruction* call, const Function* callee) { - const SVFFunction* caller = call->getFunction(); - CallSiteID csId = tcg->getCallSiteID(getCallICFGNode(call), callee); + const Function* caller = call->getParent()->getParent(); + const SVFFunction* svfcallee = getSVFFun(callee); + CallSiteID csId = tcg->getCallSiteID(getCallBlockNode(call), svfcallee); /// handle calling context for candidate functions only if(isCandidateFun(caller) == false) @@ -475,7 +508,7 @@ bool TCT::matchCxt(CallStrCxt& cxt, const SVFInstruction* call, const SVFFunctio if(cxt.empty()) return true; - if(inSameCallGraphSCC(tcg->getCallGraphNode(caller),tcg->getCallGraphNode(callee))==false) + if(inSameCallGraphSCC(tcg->getCallGraphNode(getSVFFun(caller)),tcg->getCallGraphNode(svfcallee))==false) { if(cxt.back() == csId) cxt.pop_back(); @@ -494,12 +527,12 @@ bool TCT::matchCxt(CallStrCxt& cxt, const SVFInstruction* call, const SVFFunctio void TCT::dumpCxt(CallStrCxt& cxt) { std::string str; - std::stringstream rawstr(str); + raw_string_ostream rawstr(str); rawstr << "[:"; for(CallStrCxt::const_iterator it = cxt.begin(), eit = cxt.end(); it!=eit; ++it) { rawstr << " ' "<< *it << " ' "; - rawstr << tcg->getCallSite(*it)->getCallSite()->toString(); + rawstr << *(tcg->getCallSite(*it)->getCallSite()); rawstr << " call " << tcg->getCallSite(*it)->getCaller()->getName() << "-->" << tcg->getCalleeOfCallSite(*it)->getName() << ", \n"; } rawstr << " ]"; @@ -511,7 +544,7 @@ void TCT::dumpCxt(CallStrCxt& cxt) */ void TCT::dump(const std::string& filename) { - if (Options::TCTDotGraph()) + if (Options::TCTDotGraph) GraphPrinter::WriteGraphToFile(outs(), filename, this); } @@ -557,7 +590,7 @@ TCTEdge* TCT::getGraphEdge(TCTNode* src, TCTNode* dst, TCTEdge::CEDGEK kind) } return nullptr; } -namespace SVF +namespace llvm { /*! @@ -604,7 +637,6 @@ struct DOTGraphTraits : public DefaultDOTGraphTraits { TCTEdge* edge = csThreadTree->getGraphEdge(node, *EI, TCTEdge::ThreadCreateEdge); - (void)edge; // Suppress warning of unused variable under release build assert(edge && "No edge found!!"); /// black edge for direct call while two functions contain indirect calls will be label with red color return "color=black"; diff --git a/svf/lib/MemoryModel/AccessPath.cpp b/svf/lib/MemoryModel/AccessPath.cpp deleted file mode 100644 index be763bbfd..000000000 --- a/svf/lib/MemoryModel/AccessPath.cpp +++ /dev/null @@ -1,349 +0,0 @@ -//===- AccessPath.cpp -- Location set for modeling abstract memory object----// -// -// SVF: Static Value-Flow Analysis -// -// Copyright (C) <2013-> -// - -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU Affero General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Affero General Public License for more details. - -// You should have received a copy of the GNU Affero General Public License -// along with this program. If not, see . -// -//===----------------------------------------------------------------------===// - -/* - * @file: AccessPath.cpp - * @author: yesen - * @date: 26 Sep 2014 - * - * LICENSE - * - */ - -#include "Util/Options.h" -#include "MemoryModel/AccessPath.h" -#include "Util/SVFUtil.h" - -using namespace SVF; -using namespace SVFUtil; - -/*! - * Add offset value to vector offsetVarAndGepTypePairs - */ -bool AccessPath::addOffsetVarAndGepTypePair(const SVFVar* var, const SVFType* gepIterType) -{ - idxOperandPairs.emplace_back(var, gepIterType); - return true; -} - -/// Return true if all offset values are constants -bool AccessPath::isConstantOffset() const -{ - for(auto it : idxOperandPairs) - { - if(SVFUtil::isa(it.first->getValue()) == false) - return false; - } - return true; -} - -/// Return element number of a type -/// (1) StructType or Array, return flattened number elements. -/// (2) Pointer type, return max field limit -/// (3) Non-pointer SingleValueType or Function Type, return 1 -u32_t AccessPath::getElementNum(const SVFType* type) const -{ - if (SVFUtil::isa(type)) - { - return SymbolTableInfo::SymbolInfo()->getNumOfFlattenElements(type); - } - else if (type->isPointerTy()) - { - // if type is a pointer, should be like: - // %2 = getelementptr inbounds i32*, i32** %1, ... - // where gepSrcPointee is of pointer type (i32*). - // this can be transformed to: - // %2 = getelementptr inbounds [N x i32], [N x i32]* %1, ... - // However, we do not know N without context information. int** implies non-contiguous blocks of memory - // In this case, we conservatively return max field limit - return Options::MaxFieldLimit(); - } - else if (type->isSingleValueType() || SVFUtil::isa(type)) - { - return 1; - } - else - { - SVFUtil::outs() << "GepIter Type" << *type << "\n"; - assert(false && "What other types for this gep?"); - abort(); - } -} - - -/// Return byte offset from the beginning of the structure to the field where it is located for struct type -/// -// e.g. idxOperandVar: i32 2 idxOperandType: %struct.Student = type { i32, [i8 x 12], i32 } -// we accumulate field 0 (i32) byte size (4 Bytes), and field 1 ([i8x12]) byte size (12 Bytes) -// then the return byte offset is 16 Bytes. -u32_t AccessPath::getStructFieldOffset(const SVFVar* idxOperandVar, const SVFStructType* idxOperandType) const -{ - const SVFValue* idxValue = idxOperandVar->getValue(); - u32_t structByteOffset = 0; - if (const SVFConstantInt *op = SVFUtil::dyn_cast(idxValue)) - { - for (u32_t structField = 0; structField < (u32_t) op->getSExtValue(); ++structField) - { - u32_t flattenIdx = idxOperandType->getTypeInfo()->getFlattenedFieldIdxVec()[structField]; - structByteOffset += idxOperandType->getTypeInfo()->getOriginalElemType(flattenIdx)->getByteSize(); - } - return structByteOffset; - } - else - { - assert(false && "struct type can only pair with constant idx"); - abort(); - } -} - -/// Return accumulated constant offset -/// -/// "value" is the offset variable (must be a constant) -/// "type" is the location where we want to compute offset -/// Given a vector and elem byte size: [(value1,type1), (value2,type2), (value3,type3)], bytesize -/// totalConstByteOffset = ByteOffset(value1,type1) * ByteOffset(value2,type2) + ByteOffset(value3,type3) -/// For a pointer type (e.g., t1 is PointerType), we will retrieve the pointee type and times the offset, i.e., getElementNum(t1) X off1 -APOffset AccessPath::computeConstantByteOffset() const -{ - assert(isConstantOffset() && "not a constant offset"); - - APOffset totalConstOffset = 0; - for(int i = idxOperandPairs.size() - 1; i >= 0; i--) - { - /// For example, there is struct DEST{int a, char b[10], int c[5]} - /// (1) %c = getelementptr inbounds %struct.DEST, %struct.DEST* %arr, i32 0, i32 2 - // (2) %arrayidx = getelementptr inbounds [10 x i8], [10 x i8]* %b, i64 0, i64 8 - const SVFValue* value = idxOperandPairs[i].first->getValue(); - /// for (1) offsetVarAndGepTypePairs.size() = 2 - /// i = 0, type: %struct.DEST*, PtrType, op = 0 - /// i = 1, type: %struct.DEST, StructType, op = 2 - /// for (2) offsetVarAndGepTypePairs.size() = 2 - /// i = 0, type: [10 x i8]*, PtrType, op = 0 - /// i = 1, type: [10 x i8], ArrType, op = 8 - const SVFType* type = idxOperandPairs[i].second; - /// if offsetVarAndGepTypePairs[i].second is nullptr, it means - /// GepStmt comes from external API, this GepStmt is assigned in SVFIRExtAPI.cpp - /// at SVFIRBuilder::getBaseTypeAndFlattenedFields ls.addOffsetVarAndGepTypePair() - assert(type && "this GepStmt comes from ExternalAPI cannot call this api"); - const SVFType* type2 = type; - if (const SVFArrayType* arrType = SVFUtil::dyn_cast(type)) - { - /// for (2) i = 1, arrType: [10 x i8], type2 = i8 - type2 = arrType->getTypeOfElement(); - } - else if (SVFUtil::isa(type)) - { - /// for (1) i = 0, ptrType: %struct.DEST*, type2: %struct.DEST - /// for (2) i = 0, ptrType: [10 x i8]*, type2 = [10 x i8] - type2 = gepSrcPointeeType(); - } - - const SVFConstantInt* op = SVFUtil::dyn_cast(value); - if (const SVFStructType* structType = SVFUtil::dyn_cast(type)) - { - /// for (1) structType: %struct.DEST - /// structField = 0, flattenIdx = 0, type2: int - /// structField = 1, flattenIdx = 1, type2: char[10] - /// structField = 2, flattenIdx = 11, type2: int[5] - for (u32_t structField = 0; structField < (u32_t)op->getSExtValue(); ++structField) - { - u32_t flattenIdx = structType->getTypeInfo()->getFlattenedFieldIdxVec()[structField]; - type2 = structType->getTypeInfo()->getOriginalElemType(flattenIdx); - totalConstOffset += type2->getByteSize(); - } - } - else - { - /// for (2) i = 0, op: 0, type: [10 x i8]*(Ptr), type2: [10 x i8](Arr) - /// i = 1, op: 8, type: [10 x i8](Arr), type2: i8 - totalConstOffset += op->getSExtValue() * type2->getByteSize(); - } - } - totalConstOffset = Options::MaxFieldLimit() > totalConstOffset? totalConstOffset: Options::MaxFieldLimit(); - return totalConstOffset; -} - -/// Return accumulated constant offset -/// -/// "value" is the offset variable (must be a constant) -/// "type" is the location where we want to compute offset -/// Given a vector: [(value1,type1), (value2,type2), (value3,type3)] -/// totalConstOffset = flattenOffset(value1,type1) * flattenOffset(value2,type2) + flattenOffset(value3,type3) -/// For a pointer type (e.g., t1 is PointerType), we will retrieve the pointee type and times the offset, i.e., getElementNum(t1) X off1 - -/// For example, -// struct inner{ int rollNumber; float percentage;}; -// struct Student { struct inner rollNumber; char studentName[10][3];} -// char x = studentRecord[1].studentName[3][2]; - -/// %5 = getelementptr inbounds %struct.Student, %struct.Student* %4, i64 1 -/// value1: i64 1 type1: %struct.Student* -/// computeConstantOffset = 32 -/// %6 = getelementptr inbounds %struct.Student, %struct.Student* %5, i32 0, i32 1 -/// value1: i32 0 type1: %struct.Student* -/// value2: i32 1 type2: %struct.Student = type { %struct.inner, [10 x [3 x i8]] } -/// computeConstantOffset = 2 -/// %7 = getelementptr inbounds [10 x [3 x i8]], [10 x [3 x i8]]* %6, i64 0, i64 3 -/// value1: i64 0 type1: [10 x [3 x i8]]* -/// value2: i64 3 type2: [10 x [3 x i8]] -/// computeConstantOffset = 9 -/// %8 = getelementptr inbounds [3 x i8], [3 x i8]* %7, i64 0, i64 2 -/// value1: i64 0 type1: [3 x i8]* -/// value2: i64 2 type2: [3 x i8] -/// computeConstantOffset = 2 -APOffset AccessPath::computeConstantOffset() const -{ - - assert(isConstantOffset() && "not a constant offset"); - - APOffset totalConstOffset = 0; - //After the model-const and model-array options are turned on, - // the gepstmt offset generated by the array on the global - // node will be saved in getConstantStructFldIdx - if (idxOperandPairs.size() == 0) - return getConstantStructFldIdx(); - for(int i = idxOperandPairs.size() - 1; i >= 0; i--) - { - const SVFValue* value = idxOperandPairs[i].first->getValue(); - const SVFType* type = idxOperandPairs[i].second; - const SVFConstantInt* op = SVFUtil::dyn_cast(value); - assert(op && "not a constant offset?"); - if(type==nullptr) - { - totalConstOffset += op->getSExtValue(); - continue; - } - - if(SVFUtil::isa(type)) - totalConstOffset += op->getSExtValue() * getElementNum(gepPointeeType); - else - { - APOffset offset = op->getSExtValue(); - if (offset >= 0) - { - const std::vector& so = SymbolTableInfo::SymbolInfo()->getTypeInfo(type)->getFlattenedElemIdxVec(); - // if offset is larger than the size of getFlattenedElemIdxVec (overflow) - // set offset the last index of getFlattenedElemIdxVec to avoid assertion - if (offset >= (APOffset)so.size()) - { - SVFUtil::errs() << "It is an overflow access, hence it is the last idx\n"; - offset = so.size() - 1; - } - else - { - - } - - u32_t flattenOffset = - SymbolTableInfo::SymbolInfo()->getFlattenedElemIdx(type, - offset); - totalConstOffset += flattenOffset; - } - } - } - return totalConstOffset; -} -/*! - * Compute all possible locations according to offset and number-stride pairs. - */ -NodeBS AccessPath::computeAllLocations() const -{ - NodeBS result; - result.set(getConstantStructFldIdx()); - return result; -} - -AccessPath AccessPath::operator+(const AccessPath& rhs) const -{ - assert(gepPointeeType == rhs.gepSrcPointeeType() && "source element type not match"); - AccessPath ap(rhs); - ap.fldIdx += getConstantStructFldIdx(); - for (auto &p : ap.getIdxOperandPairVec()) - ap.addOffsetVarAndGepTypePair(p.first, p.second); - - return ap; -} - -bool AccessPath::operator< (const AccessPath& rhs) const -{ - if (fldIdx != rhs.fldIdx) - return (fldIdx < rhs.fldIdx); - else - { - const IdxOperandPairs& pairVec = getIdxOperandPairVec(); - const IdxOperandPairs& rhsPairVec = rhs.getIdxOperandPairVec(); - if (pairVec.size() != rhsPairVec.size()) - return (pairVec.size() < rhsPairVec.size()); - else - { - IdxOperandPairs::const_iterator it = pairVec.begin(); - IdxOperandPairs::const_iterator rhsIt = rhsPairVec.begin(); - for (; it != pairVec.end() && rhsIt != rhsPairVec.end(); ++it, ++rhsIt) - { - return (*it) < (*rhsIt); - } - - return false; - } - } -} - -SVF::AccessPath::LSRelation AccessPath::checkRelation(const AccessPath& LHS, const AccessPath& RHS) -{ - NodeBS lhsLocations = LHS.computeAllLocations(); - NodeBS rhsLocations = RHS.computeAllLocations(); - if (lhsLocations.intersects(rhsLocations)) - { - if (lhsLocations == rhsLocations) - return Same; - else if (lhsLocations.contains(rhsLocations)) - return Superset; - else if (rhsLocations.contains(lhsLocations)) - return Subset; - else - return Overlap; - } - else - { - return NonOverlap; - } -} - -/// Dump location set -std::string AccessPath::dump() const -{ - std::string str; - std::stringstream rawstr(str); - - rawstr << "AccessPath\tField_Index: " << getConstantStructFldIdx(); - rawstr << ",\tNum-Stride: {"; - const IdxOperandPairs& vec = getIdxOperandPairVec(); - IdxOperandPairs::const_iterator it = vec.begin(); - IdxOperandPairs::const_iterator eit = vec.end(); - for (; it != eit; ++it) - { - const SVFType* ty = it->second; - rawstr << " (Svf var: " << it->first->toString() << ", Iter type: " << *ty << ")"; - } - rawstr << " }\n"; - return rawstr.str(); -} diff --git a/svf/lib/MemoryModel/LocationSet.cpp b/svf/lib/MemoryModel/LocationSet.cpp new file mode 100644 index 000000000..8ac5dfa82 --- /dev/null +++ b/svf/lib/MemoryModel/LocationSet.cpp @@ -0,0 +1,153 @@ +//===- LocationSet.cpp -- Location set for modeling abstract memory object----// +// +// SVF: Static Value-Flow Analysis +// +// Copyright (C) <2013-2017> +// + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// +//===----------------------------------------------------------------------===// + +/* + * @file: LocationSet.cpp + * @author: yesen + * @date: 26 Sep 2014 + * + * LICENSE + * + */ + +#include "Util/Options.h" +#include "MemoryModel/LocationSet.h" +#include "MemoryModel/MemModel.h" + +using namespace SVF; + + +/*! + * Add element num and stride pair + */ +void LocationSet::addElemNumStridePair(const NodePair& pair) +{ + /// The pair will not be added if any number of a stride is zero, + /// because they will not have effect on the locations represented by this LocationSet. + if (pair.first == 0 || pair.second == 0) + return; + + if (Options::SingleStride) + { + if (numStridePair.empty()) + numStridePair.push_back(std::make_pair(StInfo::getMaxFieldLimit(),pair.second)); + else + { + /// Find the GCD stride + NodeID existStride = (*numStridePair.begin()).second; + NodeID newStride = gcd(pair.second, existStride); + if (newStride != existStride) + { + numStridePair.pop_back(); + numStridePair.push_back(std::make_pair(StInfo::getMaxFieldLimit(),newStride)); + } + } + } + else + { + numStridePair.push_back(pair); + } +} + + +/*! + * Return TRUE if it successfully increases any index by 1 + */ +bool LocationSet::increaseIfNotReachUpperBound(std::vector& indices, + const ElemNumStridePairVec& pairVec) const +{ + assert(indices.size() == pairVec.size() && "vector size not match"); + + /// Check if all indices reach upper bound + bool reachUpperBound = true; + for (u32_t i = 0; i < indices.size(); i++) + { + assert(pairVec[i].first > 0 && "number must be greater than 0"); + if (indices[i] < (pairVec[i].first - 1)) + reachUpperBound = false; + } + + /// Increase index if not reach upper bound + bool increased = false; + if (reachUpperBound == false) + { + u32_t i = 0; + while (increased == false) + { + if (indices[i] < (pairVec[i].first - 1)) + { + indices[i] += 1; + increased = true; + } + else + { + indices[i] = 0; + i++; + } + } + } + + return increased; +} + + +/*! + * Compute all possible locations according to offset and number-stride pairs. + */ +PointsTo LocationSet::computeAllLocations() const +{ + + PointsTo result; + result.set(getOffset()); + + if (isConstantOffset() == false) + { + const ElemNumStridePairVec& lhsVec = getNumStridePair(); + std::vector indices; + u32_t size = lhsVec.size(); + while (size) + { + indices.push_back(0); + size--; + } + + do + { + u32_t i = 0; + NodeID ofst = getOffset(); + while (i < lhsVec.size()) + { + ofst += (lhsVec[i].second * indices[i]); + i++; + } + + result.set(ofst); + + } + while (increaseIfNotReachUpperBound(indices, lhsVec)); + } + + return result; +} + + + diff --git a/svf/lib/MemoryModel/MemModel.cpp b/svf/lib/MemoryModel/MemModel.cpp new file mode 100644 index 000000000..93aa1272e --- /dev/null +++ b/svf/lib/MemoryModel/MemModel.cpp @@ -0,0 +1,622 @@ +//===- MemModel.cpp -- Memory model for pointer analysis----------------------// +// +// SVF: Static Value-Flow Analysis +// +// Copyright (C) <2013-2017> +// + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// +//===----------------------------------------------------------------------===// + +/* + * MemModel.cpp + * + * Created on: Oct 11, 2013 + * Author: Yulei Sui + */ + +#include "SVF-FE/SymbolTableInfo.h" +#include "MemoryModel/MemModel.h" +#include "Util/SVFModule.h" +#include "Util/Options.h" +#include "Util/SVFUtil.h" +#include "SVF-FE/LLVMUtil.h" +#include "SVF-FE/CPPUtil.h" +#include "SVF-FE/BreakConstantExpr.h" +#include "SVF-FE/GEPTypeBridgeIterator.h" // include bridge_gep_iterator + +using namespace std; +using namespace SVF; +using namespace SVFUtil; + +u32_t StInfo::maxFieldLimit = 0; + +/*! + * Analyse types of all flattened fields of this object + */ +void ObjTypeInfo::analyzeGlobalStackObjType(const Value* val) +{ + + const PointerType * refty = SVFUtil::dyn_cast(val->getType()); + assert(SVFUtil::isa(refty) && "this value should be a pointer type!"); + Type* elemTy = refty->getElementType(); + bool isPtrObj = false; + // Find the inter nested array element + while (const ArrayType *AT= SVFUtil::dyn_cast(elemTy)) + { + elemTy = AT->getElementType(); + if(elemTy->isPointerTy()) + isPtrObj = true; + if(SVFUtil::isa(val) && SVFUtil::cast(val)->hasInitializer() + && SVFUtil::isa(SVFUtil::cast(val)->getInitializer())) + { + setFlag(CONST_ARRAY_OBJ); + } + else + setFlag(VAR_ARRAY_OBJ); + } + if (const StructType *ST= SVFUtil::dyn_cast(elemTy)) + { + const std::vector& flattenFields = SymbolTableInfo::SymbolInfo()->getFlattenFieldInfoVec(ST); + for(std::vector::const_iterator it = flattenFields.begin(), eit = flattenFields.end(); + it!=eit; ++it) + { + if((*it).getFlattenElemTy()->isPointerTy()) + isPtrObj = true; + } + if(SVFUtil::isa(val) && SVFUtil::cast(val)->hasInitializer() + && SVFUtil::isa(SVFUtil::cast(val)->getInitializer())) + setFlag(CONST_STRUCT_OBJ); + else + setFlag(VAR_STRUCT_OBJ); + } + else if (elemTy->isPointerTy()) + { + isPtrObj = true; + } + + if(isPtrObj) + setFlag(HASPTR_OBJ); +} + +/*! + * Analyse types of heap and static objects + */ +void ObjTypeInfo::analyzeHeapStaticObjType(const Value* heapValue) +{ + if (Options::InvariantVGEP) { + // TODO: Heap and static objects are considered as pointers right now. + // Refine this function to get more details about heap and static objects. + if (const CallInst* heapCall = SVFUtil::dyn_cast(heapValue)) { + if (heapCall->hasMetadata("annotation")) { + MDNode* mdNode = heapCall->getMetadata("annotation"); + MDString* tyAnnotStr = (MDString*)mdNode->getOperand(0).get(); + if (tyAnnotStr->getString() == "ArrayType") { + setFlag(VAR_ARRAY_OBJ); + } else if (tyAnnotStr->getString() == "StructType") { + setFlag(VAR_STRUCT_OBJ); + } + } else { + // If it doesn't have an annotation, we treat them as scalar + // setFlag(HASPTR_OBJ); + } + } else { + setFlag(HASPTR_OBJ); + } + } else { + setFlag(HASPTR_OBJ); + } + setFlag(HASPTR_OBJ); +} + +/*! + * Return size of this Object + */ +u32_t ObjTypeInfo::getObjSize(const Value* val) +{ + + Type* ety = SVFUtil::cast(val->getType())->getElementType(); + u32_t numOfFields = 1; + if (SVFUtil::isa(ety) || SVFUtil::isa(ety)) + { + numOfFields = SymbolTableInfo::SymbolInfo()->getFlattenFieldInfoVec(ety).size(); + } + return numOfFields; +} + +/*! + * Initialize the type info of an object + */ +void ObjTypeInfo::init(const Value* val) +{ + + Size_t objSize = 1; + // Global variable + if (SVFUtil::isa(val)) + { + setFlag(FUNCTION_OBJ); + analyzeGlobalStackObjType(val); + objSize = getObjSize(val); + } + else if(SVFUtil::isa(val)) + { + setFlag(STACK_OBJ); + analyzeGlobalStackObjType(val); + objSize = getObjSize(val); + } + else if(SVFUtil::isa(val)) + { + setFlag(GLOBVAR_OBJ); + if(SymbolTableInfo::SymbolInfo()->isConstantObjSym(val)) + setFlag(CONST_OBJ); + analyzeGlobalStackObjType(val); + objSize = getObjSize(val); + } + else if (SVFUtil::isa(val) && isHeapAllocExtCall(SVFUtil::cast(val))) + { + setFlag(HEAP_OBJ); + analyzeHeapStaticObjType(val); + // Heap object, label its field as infinite here + objSize = -1; + } + else if (SVFUtil::isa(val) && isStaticExtCall(SVFUtil::cast(val))) + { + setFlag(STATIC_OBJ); + analyzeHeapStaticObjType(val); + // static object allocated before main, label its field as infinite here + objSize = -1; + } + else if(ArgInProgEntryFunction(val)) + { + setFlag(STATIC_OBJ); + analyzeHeapStaticObjType(val); + // user input data, label its field as infinite here + objSize = -1; + } + else + assert("what other object do we have??"); + + // Reset maxOffsetLimit if it is over the total fieldNum of this object + if(objSize > 0 && maxOffsetLimit > objSize) + maxOffsetLimit = objSize; + +} + +/*! + * Whether a location set is a pointer type or not + */ +bool ObjTypeInfo::isNonPtrFieldObj(const LocationSet& ls) +{ + if (isHeap() || isStaticObj()) + return false; + + const Type* ety = getType(); + while (const ArrayType *AT= SVFUtil::dyn_cast(ety)) + { + ety = AT->getElementType(); + } + + if (SVFUtil::isa(ety) || SVFUtil::isa(ety)) + { + bool hasIntersection = false; + const vector &infovec = SymbolTableInfo::SymbolInfo()->getFlattenFieldInfoVec(ety); + vector::const_iterator it = infovec.begin(); + vector::const_iterator eit = infovec.end(); + for (; it != eit; ++it) + { + const FieldInfo& fieldLS = *it; + if (ls.intersects(LocationSet(fieldLS))) + { + hasIntersection = true; + if (fieldLS.getFlattenElemTy()->isPointerTy()) + return false; + } + } + assert(hasIntersection && "cannot find field of specified offset"); + return true; + } + else + { + if (isStaticObj() || isHeap()) + { + // TODO: Objects which cannot find proper field for a certain offset including + // arguments in main(), static objects allocated before main and heap + // objects. Right now they're considered to have infinite fields and we + // treat each field as pointers conservatively. + // Try to model static and heap objects more accurately in the future. + return false; + } + else + { + // TODO: Using new memory model (locMM) may create objects with spurious offset + // as we simply return new offset by mod operation without checking its + // correctness in LocSymTableInfo::getModulusOffset(). So the following + // assertion may fail. Try to refine the new memory model. + //assert(ls.getOffset() == 0 && "cannot get a field from a non-struct type"); + return (hasPtrObj() == false); + } + } +} + + +/*! + * Set mem object to be field sensitive (up to maximum field limit) + */ +void MemObj::setFieldSensitive() +{ + typeInfo->setMaxFieldOffsetLimit(StInfo::getMaxFieldLimit()); +} +/* + * Initial the memory object here + */ +void MemObj::init(const Type* type) +{ + typeInfo = new ObjTypeInfo(StInfo::getMaxFieldLimit(),type); + typeInfo->setFlag(ObjTypeInfo::HEAP_OBJ); + typeInfo->setFlag(ObjTypeInfo::HASPTR_OBJ); +} + +/*! + * Constructor of a memory object + */ +MemObj::MemObj(const Value *val, SymID id) : + refVal(val), GSymID(id), typeInfo(nullptr) +{ + init(val); +} + +/*! + * Constructor of a memory object + */ +MemObj::MemObj(SymID id, const Type* type) : + refVal(nullptr), GSymID(id), typeInfo(nullptr) +{ + init(type); +} + +/*! + * Whether it is a black hole object + */ +bool MemObj::isBlackHoleObj() const +{ + return SymbolTableInfo::isBlkObj(GSymID); +} + + +/// Get obj type info +const Type* MemObj::getType() const +{ + if (isHeap() == false) + { + if(const PointerType* type = SVFUtil::dyn_cast(typeInfo->getType())) + return type->getElementType(); + else + return typeInfo->getType(); + } + else if (refVal && SVFUtil::isa(refVal)) + return SVFUtil::getTypeOfHeapAlloc(SVFUtil::cast(refVal)); + else + return typeInfo->getType(); +} +/* + * Destroy the fields of the memory object + */ +void MemObj::destroy() +{ + delete typeInfo; + typeInfo = nullptr; +} + + +/*! + * Compute gep offset + */ +bool LocSymTableInfo::computeGepOffset(const User *V, LocationSet& ls) +{ + + assert(V); + int baseIndex = -1; + int index = 0; + for (bridge_gep_iterator gi = bridge_gep_begin(*V), ge = bridge_gep_end(*V); + gi != ge; ++gi, ++index) + { + if(SVFUtil::isa(gi.getOperand()) == false) + baseIndex = index; + } + + index = 0; + for (bridge_gep_iterator gi = bridge_gep_begin(*V), ge = bridge_gep_end(*V); + gi != ge; ++gi, ++index) + { + + if (index <= baseIndex) + { + /// variant offset + // Handling pointer types + if (const PointerType* pty = SVFUtil::dyn_cast(*gi)) + { + const Type* et = pty->getElementType(); + Size_t sz = getTypeSizeInBytes(et); + + Size_t num = 1; + if (const ArrayType* aty = SVFUtil::dyn_cast(et)) + num = aty->getNumElements(); + else + num = StInfo::getMaxFieldLimit(); + + ls.addElemNumStridePair(std::make_pair(num, sz)); + } + // Calculate the size of the array element + else if(const ArrayType* at = SVFUtil::dyn_cast(*gi)) + { + const Type* et = at->getElementType(); + Size_t sz = getTypeSizeInBytes(et); + Size_t num = at->getNumElements(); + ls.addElemNumStridePair(std::make_pair(num, sz)); + } + else + assert(false && "what other types?"); + } + // constant offset + else + { + assert(SVFUtil::isa(gi.getOperand()) && "expecting a constant"); + + ConstantInt *op = SVFUtil::cast(gi.getOperand()); + + //The actual index + Size_t idx = op->getSExtValue(); + + // Handling pointer types + // These GEP instructions are simply making address computations from the base pointer address + // e.g. idx1 = (char*) &MyVar + 4, at this case gep only one offset index (idx) + if (const PointerType* pty = SVFUtil::dyn_cast(*gi)) + { + const Type* et = pty->getElementType(); + Size_t sz = getTypeSizeInBytes(et); + ls.setByteOffset(ls.getByteOffset() + idx * sz); + } + // Calculate the size of the array element + else if(const ArrayType* at = SVFUtil::dyn_cast(*gi)) + { + const Type* et = at->getElementType(); + Size_t sz = getTypeSizeInBytes(et); + ls.setByteOffset(ls.getByteOffset() + idx * sz); + } + // Handling struct here + else if (const StructType *ST = SVFUtil::dyn_cast(*gi)) + { + assert(op && "non-const struct index in GEP"); + const vector &so = SymbolTableInfo::SymbolInfo()->getFattenFieldOffsetVec(ST); + if ((unsigned)idx >= so.size()) + { + outs() << "!! Struct index out of bounds" << idx << "\n"; + assert(0); + } + //add the translated offset + ls.setByteOffset(ls.getByteOffset() + so[idx]); + } + else + assert(false && "what other types?"); + } + } + return true; +} + +/*! + * Collect array information + */ +void LocSymTableInfo::collectArrayInfo(const llvm::ArrayType*) +{ + /* + StInfo *stinfo = new StInfo(); + typeToFieldInfo[ty] = stinfo; + + /// If this is an array type, calculate the outmost array + /// information and append them to the inner elements' type + /// information later. + u64_t out_num = ty->getNumElements(); + const Type* elemTy = ty->getElementType(); + u32_t out_stride = getTypeSizeInBytes(elemTy); + + /// Array itself only has one field which is the inner most element + stinfo->addOffsetWithType(0, elemTy); + + while (const ArrayType* aty = dyn_cast(elemTy)) { + out_num *= aty->getNumElements(); + elemTy = aty->getElementType(); + out_stride = getTypeSizeInBytes(elemTy); + } + + /// Array's flatten field infor is the same as its element's + /// flatten infor with an additional slot for array's element + /// number and stride pair. + StInfo* elemStInfo = getStructInfo(elemTy); + u32_t nfE = elemStInfo->getFlattenFieldInfoVec().size(); + for (u32_t j = 0; j < nfE; j++) { + u32_t off = elemStInfo->getFlattenFieldInfoVec()[j].getFlattenOffset(); + const Type* fieldTy = elemStInfo->getFlattenFieldInfoVec()[j].getFlattenElemTy(); + FieldInfo::ElemNumStridePairVec pair = elemStInfo->getFlattenFieldInfoVec()[j].getElemNumStridePairVect(); + /// append the additional number + pair.push_back(std::make_pair(out_num, out_stride)); + FieldInfo field(off, fieldTy, pair); + stinfo->getFlattenFieldInfoVec().push_back(field); + } + */ +} + + +/* + * Recursively collect the memory layout information for a struct type + */ +void LocSymTableInfo::collectStructInfo(const StructType*) +{ + /* + StInfo *stinfo = new StInfo(); + typeToFieldInfo[ty] = stinfo; + + const StructLayout *stTySL = getDataLayout(getModule().getMainLLVMModule())->getStructLayout( const_cast(ty) ); + + u32_t field_idx = 0; + for (StructType::element_iterator it = ty->element_begin(), ie = + ty->element_end(); it != ie; ++it, ++field_idx) { + const Type *et = *it; + + // The offset is where this element will be placed in the struct. + // This offset is computed after alignment with the current struct + u64_t eOffsetInBytes = stTySL->getElementOffset(field_idx); + + //The offset is where this element will be placed in the exp. struct. + /// FIXME: As the layout size is uint_64, here we assume + /// offset with uint_32 (Size_t) is large enough and will not cause overflow + stinfo->addOffsetWithType(static_cast(eOffsetInBytes), et); + + StInfo* fieldStinfo = getStructInfo(et); + u32_t nfE = fieldStinfo->getFlattenFieldInfoVec().size(); + //Copy ST's info, whose element 0 is the size of ST itself. + for (u32_t j = 0; j < nfE; j++) { + u32_t oft = eOffsetInBytes + fieldStinfo->getFlattenFieldInfoVec()[j].getFlattenOffset(); + const Type* elemTy = fieldStinfo->getFlattenFieldInfoVec()[j].getFlattenElemTy(); + FieldInfo::ElemNumStridePairVec pair = fieldStinfo->getFlattenFieldInfoVec()[j].getElemNumStridePairVect(); + pair.push_back(std::make_pair(1, 0)); + FieldInfo newField(oft, elemTy, pair); + stinfo->getFlattenFieldInfoVec().push_back(newField); + } + } + + // verifyStructSize(stinfo,stTySL->getSizeInBytes()); + + //Record the size of the complete struct and update max_struct. + if (stTySL->getSizeInBytes() > maxStSize) { + maxStruct = ty; + maxStSize = stTySL->getSizeInBytes(); + } + */ +} + + +/*! + * Given LocationSet from a Gep Instruction, return a new LocationSet which matches + * the field information of this ObjTypeInfo by considering memory layout + */ +LocationSet LocSymTableInfo::getModulusOffset(const MemObj* obj, const LocationSet& ls) +{ + const Type* ety = obj->getType(); + + if (SVFUtil::isa(ety) || SVFUtil::isa(ety)) + { + /// Find an appropriate field for this LocationSet + const std::vector& infovec = SymbolTableInfo::SymbolInfo()->getFlattenFieldInfoVec(ety); + std::vector::const_iterator it = infovec.begin(); + std::vector::const_iterator eit = infovec.end(); + for (; it != eit; ++it) + { + const FieldInfo& fieldLS = *it; + LocationSet rhsLS(fieldLS); + LocationSet::LSRelation result = LocationSet::checkRelation(ls, rhsLS); + if (result == LocationSet::Same || + result == LocationSet::Superset || + result == LocationSet::Subset) + return ls; + else if (result == LocationSet::Overlap) + { + // TODO: + return ls; + } + else if (result == LocationSet::NonOverlap) + { + continue; + } + + assert(false && "cannot find an appropriate field for specified LocationSet"); + return ls; + } + } + else + { + if (obj->isStaticObj() || obj->isHeap()) + { + // TODO: Objects which cannot find proper field for a certain offset including + // arguments in main(), static objects allocated before main and heap + // objects. Right now they're considered to have infinite fields. So we + // just return the location set without modifying it. + return ls; + } + else + { + // TODO: Using new memory model (locMM) may create objects with spurious offset + // as we simply return new offset by mod operation without checking its + // correctness in LocSymTableInfo::getModulusOffset(). So the following + // assertion may fail. Try to refine the new memory model. + assert(ls.isConstantOffset() && "expecting a constant location set"); + return ls; + } + } + + /// This location set represent one object + if (ls.isConstantOffset()) + { + /// if the offset is negative, it's possible that we're looking for an obj node out of range + /// of current struct. Make the offset positive so we can still get a node within current + /// struct to represent this obj. + Size_t offset = ls.getOffset(); + if(offset < 0) + { + writeWrnMsg("try to create a gep node with negative offset."); + offset = abs(offset); + } + u32_t maxOffset = obj->getMaxFieldOffsetLimit(); + if (maxOffset != 0) + offset = offset % maxOffset; + else + offset = 0; + } + /// This location set represents multiple objects + else + { + + } + + return ls; +} + +/*! + * Verify struct size + */ +void LocSymTableInfo::verifyStructSize(StInfo *stinfo, u32_t structSize) +{ + + u32_t lastOff = stinfo->getFlattenFieldInfoVec().back().getFlattenByteOffset(); + u32_t strideSize = 0; + FieldInfo::ElemNumStridePairVec::const_iterator pit = stinfo->getFlattenFieldInfoVec().back().elemStridePairBegin(); + FieldInfo::ElemNumStridePairVec::const_iterator epit = stinfo->getFlattenFieldInfoVec().back().elemStridePairEnd(); + for(; pit!=epit; ++pit) + strideSize += pit->first * pit->second; + + u32_t lastSize = getTypeSizeInBytes(stinfo->getFlattenFieldInfoVec().back().getFlattenElemTy()); + /// Please note this verify may not be complete as different machine has different alignment mechanism + assert((structSize == lastOff + strideSize + lastSize) && "struct size not consistent"); + +} + +/*! + * Get the size of this object + */ +u32_t LocObjTypeInfo::getObjSize(const Value* val) +{ + + Type* ety = SVFUtil::cast(val->getType())->getElementType(); + return LocSymTableInfo::SymbolInfo()->getTypeSizeInBytes(ety); +} diff --git a/svf/lib/SVFIR/PAGBuilderFromFile.cpp b/svf/lib/MemoryModel/PAGBuilderFromFile.cpp similarity index 52% rename from svf/lib/SVFIR/PAGBuilderFromFile.cpp rename to svf/lib/MemoryModel/PAGBuilderFromFile.cpp index 6cfda819c..4937ecf4d 100644 --- a/svf/lib/SVFIR/PAGBuilderFromFile.cpp +++ b/svf/lib/MemoryModel/PAGBuilderFromFile.cpp @@ -1,21 +1,21 @@ -//===- PAGBuilderFromFile.cpp -- SVFIR builder-------------------------------// +//===- PAGBuilderFromFile.cpp -- PAG builder-------------------------------// // // SVF: Static Value-Flow Analysis // -// Copyright (C) <2013-> +// Copyright (C) <2013-2018> // // This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU Affero General Public License as published by +// it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Affero General Public License for more details. +// GNU General Public License for more details. -// You should have received a copy of the GNU Affero General Public License +// You should have received a copy of the GNU General Public License // along with this program. If not, see . // //===----------------------------------------------------------------------===// @@ -27,7 +27,7 @@ * Author: 136884 */ -#include "SVFIR/PAGBuilderFromFile.h" +#include "MemoryModel/PAGBuilderFromFile.h" #include // for PAGBuilderFromFile #include // for PAGBuilderFromFile #include // for PAGBuilderFromFile @@ -38,7 +38,7 @@ using namespace SVFUtil; static u32_t gepNodeNumIndex = 100000; /* - * You can build a SVFIR from a file written by yourself + * You can build a PAG from a file written by yourself * * The file should follow the format: * Node: nodeID Nodetype @@ -52,13 +52,11 @@ static u32_t gepNodeNumIndex = 100000; 9 v 5 addr 6 0 6 gep 7 4 -7 copy-ZEXT 8 0 +7 copy 8 0 6 store 8 0 8 load 9 0 */ -// for copy stmt, the enum is COPYVAL, ZEXT, SEXT, BITCAST, TRUNC, FPTRUNC, -// FPTOUI, FPTOSI, UITOFP, SITOFP, INTTOPTR, PTRTOINT, UNKNOWN -SVFIR* PAGBuilderFromFile::build() +PAG* PAGBuilderFromFile::build() { string line; @@ -69,7 +67,7 @@ SVFIR* PAGBuilderFromFile::build() { getline(myfile, line); - u32_t token_count = 0; + Size_t token_count = 0; string tmps; istringstream ss(line); while (ss.good()) @@ -105,7 +103,7 @@ SVFIR* PAGBuilderFromFile::build() { NodeID nodeSrc; NodeID nodeDst; - APOffset offsetOrCSId; + Size_t offsetOrCSId; string edge; istringstream ss(line); ss >> nodeSrc; @@ -143,113 +141,47 @@ SVFIR* PAGBuilderFromFile::build() } /*! - * Add SVFIR edge according to a file format + * Add PAG edge according to a file format */ void PAGBuilderFromFile::addEdge(NodeID srcID, NodeID dstID, - APOffset offsetOrCSId, std::string edge) + Size_t offsetOrCSId, std::string edge) { //check whether these two nodes available - PAGNode* srcNode = pag->getGNode(srcID); - PAGNode* dstNode = pag->getGNode(dstID); + PAGNode* srcNode = pag->getPAGNode(srcID); + PAGNode* dstNode = pag->getPAGNode(dstID); - /// sanity check for SVFIR from txt - assert(SVFUtil::isa(dstNode) && "dst not an value node?"); + /// sanity check for PAG from txt + assert(SVFUtil::isa(dstNode) && "dst not an value node?"); if(edge=="addr") - assert(SVFUtil::isa(srcNode) && "src not an value node?"); + assert(SVFUtil::isa(srcNode) && "src not an value node?"); else - assert(!SVFUtil::isa(srcNode) && "src not an object node?"); + assert(!SVFUtil::isa(srcNode) && "src not an object node?"); if (edge == "addr") { - pag->addAddrStmt(srcID, dstID); - } - if (edge.rfind("copy-", 0) == 0) - { - // the enum is COPYVAL, ZEXT, SEXT, BITCAST, TRUNC, FPTRUNC, - //// FPTOUI, FPTOSI, UITOFP, SITOFP, INTTOPTR, PTRTOINT, UNKNOWN - std::string opType = edge.substr(5); // start substring from 5th char - - if (opType == "COPYVAL") - { - pag->addCopyStmt(srcID, dstID, CopyStmt::COPYVAL); - } - else if (opType == "ZEXT") - { - pag->addCopyStmt(srcID, dstID, CopyStmt::ZEXT); - } - else if (opType == "SEXT") - { - pag->addCopyStmt(srcID, dstID, CopyStmt::SEXT); - } - else if (opType == "BITCAST") - { - pag->addCopyStmt(srcID, dstID, CopyStmt::BITCAST); - } - else if (opType == "TRUNC") - { - pag->addCopyStmt(srcID, dstID, CopyStmt::TRUNC); - } - else if (opType == "FPTRUNC") - { - pag->addCopyStmt(srcID, dstID, CopyStmt::FPTRUNC); - } - else if (opType == "FPTOUI") - { - pag->addCopyStmt(srcID, dstID, CopyStmt::FPTOUI); - } - else if (opType == "FPTOSI") - { - pag->addCopyStmt(srcID, dstID, CopyStmt::FPTOSI); - } - else if (opType == "UITOFP") - { - pag->addCopyStmt(srcID, dstID, CopyStmt::UITOFP); - } - else if (opType == "SITOFP") - { - pag->addCopyStmt(srcID, dstID, CopyStmt::SITOFP); - } - else if (opType == "INTTOPTR") - { - pag->addCopyStmt(srcID, dstID, CopyStmt::INTTOPTR); - } - else if (opType == "PTRTOINT") - { - pag->addCopyStmt(srcID, dstID, CopyStmt::PTRTOINT); - } - else - { - assert(false && "format not support, can not create such edge"); - } + pag->addAddrPE(srcID, dstID); } + else if (edge == "copy") + pag->addCopyPE(srcID, dstID); else if (edge == "load") - pag->addLoadStmt(srcID, dstID); + pag->addLoadPE(srcID, dstID); else if (edge == "store") - pag->addStoreStmt(srcID, dstID, nullptr); + pag->addStorePE(srcID, dstID, nullptr); else if (edge == "gep") - pag->addNormalGepStmt(srcID, dstID, AccessPath(offsetOrCSId)); + pag->addNormalGepPE(srcID, dstID, LocationSet(offsetOrCSId)); else if (edge == "variant-gep") - pag->addVariantGepStmt(srcID, dstID, AccessPath(offsetOrCSId)); + pag->addVariantGepPE(srcID, dstID); else if (edge == "call") - pag->addEdge(srcNode, dstNode, new CallPE(srcNode, dstNode, nullptr, nullptr)); + pag->addEdge(srcNode, dstNode, new CallPE(srcNode, dstNode, nullptr)); else if (edge == "ret") - pag->addEdge(srcNode, dstNode, new RetPE(srcNode, dstNode, nullptr,nullptr)); + pag->addEdge(srcNode, dstNode, new RetPE(srcNode, dstNode, nullptr)); else if (edge == "cmp") - pag->addCmpStmt(srcID, dstID, dstID, dstID); + pag->addCmpPE(srcID, dstID); else if (edge == "binary-op") - pag->addBinaryOPStmt(srcID, dstID, dstID, dstID); + pag->addBinaryOPPE(srcID, dstID); else if (edge == "unary-op") - pag->addUnaryOPStmt(srcID, dstID, dstID); - else if (edge == "phi") - assert(false && "fix phi here!"); - else if (edge == "select") - assert(false && "fix select here!"); - else if (edge == "branch") - { - assert(false && "fix successors here!"); - //pag->addBranchStmt(srcID, dstID, nullptr); - } + pag->addUnaryOPPE(srcID, dstID); else assert(false && "format not support, can not create such edge"); } diff --git a/svf/lib/MemoryModel/PointerAnalysis.cpp b/svf/lib/MemoryModel/PointerAnalysis.cpp index ba6a2bb14..34ab4fb9c 100644 --- a/svf/lib/MemoryModel/PointerAnalysis.cpp +++ b/svf/lib/MemoryModel/PointerAnalysis.cpp @@ -2,20 +2,20 @@ // // SVF: Static Value-Flow Analysis // -// Copyright (C) <2013-> +// Copyright (C) <2013-2017> // // This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU Affero General Public License as published by +// it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Affero General Public License for more details. +// GNU General Public License for more details. -// You should have received a copy of the GNU Affero General Public License +// You should have received a copy of the GNU General Public License // along with this program. If not, see . // //===----------------------------------------------------------------------===// @@ -28,15 +28,22 @@ */ #include "Util/Options.h" -#include "SVFIR/SVFModule.h" +#include "SVF-FE/CallGraphBuilder.h" +#include "SVF-FE/CHG.h" +#include "SVF-FE/DCHG.h" +#include "SVF-FE/CPPUtil.h" +#include "Util/SVFModule.h" #include "Util/SVFUtil.h" +#include "SVF-FE/LLVMUtil.h" #include "MemoryModel/PointerAnalysisImpl.h" -#include "SVFIR/PAGBuilderFromFile.h" -#include "Util/PTAStat.h" +#include "MemoryModel/PAGBuilderFromFile.h" +#include "MemoryModel/PTAStat.h" #include "Graphs/ThreadCallGraph.h" #include "Graphs/ICFG.h" -#include "Util/CallGraphBuilder.h" +#include "MemoryModel/PTAType.h" +#include "Graphs/ExternalPAG.h" +#include "WPA/FlowSensitiveTBHC.h" #include #include @@ -45,9 +52,11 @@ using namespace SVF; using namespace SVFUtil; +using namespace cppUtil; -SVFIR* PointerAnalysis::pag = nullptr; +CommonCHGraph* PointerAnalysis::chgraph = nullptr; +PAG* PointerAnalysis::pag = nullptr; const std::string PointerAnalysis::aliasTestMayAlias = "MAYALIAS"; const std::string PointerAnalysis::aliasTestMayAliasMangled = "_Z8MAYALIASPvS_"; @@ -65,14 +74,14 @@ const std::string PointerAnalysis::aliasTestFailNoAliasMangled = "_Z20EXPECTEDF /*! * Constructor */ -PointerAnalysis::PointerAnalysis(SVFIR* p, PTATY ty, bool alias_check) : - svfMod(nullptr),ptaTy(ty),stat(nullptr),ptaCallGraph(nullptr),callGraphSCC(nullptr),icfg(nullptr),chgraph(nullptr) +PointerAnalysis::PointerAnalysis(PAG* p, PTATY ty, bool alias_check) : + svfMod(nullptr),ptaTy(ty),stat(nullptr),ptaCallGraph(nullptr),callGraphSCC(nullptr),icfg(nullptr),typeSystem(nullptr) { pag = p; - OnTheFlyIterBudgetForStat = Options::StatBudget(); - print_stat = Options::PStat(); + OnTheFlyIterBudgetForStat = Options::StatBudget; + print_stat = Options::PStat; ptaImplTy = BaseImpl; - alias_validation = (alias_check && Options::EnableAliasCheck()); + alias_validation = (alias_check && Options::EnableAliasCheck); } /*! @@ -81,7 +90,7 @@ PointerAnalysis::PointerAnalysis(SVFIR* p, PTATY ty, bool alias_check) : PointerAnalysis::~PointerAnalysis() { destroy(); - // do not delete the SVFIR for now + // do not delete the PAG for now //delete pag; } @@ -96,6 +105,9 @@ void PointerAnalysis::destroy() delete stat; stat = nullptr; + + delete typeSystem; + typeSystem = nullptr; } /*! @@ -103,13 +115,36 @@ void PointerAnalysis::destroy() */ void PointerAnalysis::initialize() { - assert(pag && "SVFIR has not been built!"); + assert(pag && "PAG has not been built!"); + if (chgraph == nullptr) { + if (LLVMModuleSet::getLLVMModuleSet()->allCTir()) { + DCHGraph *dchg = new DCHGraph(pag->getModule()); + // TODO: we might want to have an option for extending. + dchg->buildCHG(true); + chgraph = dchg; + } else { + CHGraph *chg = new CHGraph(pag->getModule()); + chg->buildCHG(); + chgraph = chg; + } + } svfMod = pag->getModule(); - chgraph = pag->getCHG(); + + // dump PAG + if (dumpGraph()) + pag->dump("pag_initial"); + + // dump ICFG + if (Options::DumpICFG) + pag->getICFG()->dump("icfg_initial"); + + // print to command line of the PAG graph + if (Options::PAGPrint) + pag->print(); /// initialise pta call graph for every pointer analysis instance - if(Options::EnableThreadCallGraph()) + if(Options::EnableThreadCallGraph) { ThreadCallGraph* cg = new ThreadCallGraph(); ThreadCallGraphBuilder bd(cg, pag->getICFG()); @@ -124,8 +159,8 @@ void PointerAnalysis::initialize() callGraphSCCDetection(); // dump callgraph - if (Options::CallGraphDotGraph()) - getPTACallGraph()->dump("callgraph_initial"); + if (Options::CallGraphDotGraph) + getPTACallGraph()->dump("callgraph_initial"); } @@ -134,13 +169,14 @@ void PointerAnalysis::initialize() */ bool PointerAnalysis::isLocalVarInRecursiveFun(NodeID id) const { - const MemObj* obj = pag->getObject(id); + const MemObj* obj = this->pag->getObject(id); assert(obj && "object not found!!"); if(obj->isStack()) { - if(const SVFFunction* svffun = pag->getGNode(id)->getFunction()) + if(const AllocaInst* local = SVFUtil::dyn_cast(obj->getRefVal())) { - return callGraphSCC->isInCycle(getPTACallGraph()->getCallGraphNode(svffun)->getId()); + const SVFFunction* fun = LLVMModuleSet::getLLVMModuleSet()->getSVFFunction(local->getFunction()); + return callGraphSCC->isInCycle(getPTACallGraph()->getCallGraphNode(fun)->getId()); } } return false; @@ -151,13 +187,21 @@ bool PointerAnalysis::isLocalVarInRecursiveFun(NodeID id) const */ void PointerAnalysis::resetObjFieldSensitive() { - for (SVFIR::iterator nIter = pag->begin(); nIter != pag->end(); ++nIter) + for (PAG::iterator nIter = pag->begin(); nIter != pag->end(); ++nIter) { - if(ObjVar* node = SVFUtil::dyn_cast(nIter->second)) + if(ObjPN* node = SVFUtil::dyn_cast(nIter->second)) const_cast(node->getMemObj())->setFieldSensitive(); } } +/*! + * Flag in order to dump graph + */ +bool PointerAnalysis::dumpGraph() +{ + return Options::PAGDotGraph; +} + /* * Dump statistics */ @@ -181,32 +225,47 @@ void PointerAnalysis::finalize() /// Print statistics dumpStat(); + PAG* pag = PAG::getPAG(); + // dump PAG + if (dumpGraph()) + pag->dump("pag_final"); + + // dump ICFG + if (Options::DumpICFG){ + pag->getICFG()->updateCallGraph(ptaCallGraph); + pag->getICFG()->dump("icfg_final"); + } + + if (!DumpPAGFunctions.empty()) ExternalPAG::dumpFunctions(DumpPAGFunctions); + /// Dump results - if (Options::PTSPrint()) + if (Options::PTSPrint) { dumpTopLevelPtsTo(); //dumpAllPts(); //dumpCPts(); } - if (Options::TypePrint()) + if (Options::TypePrint) dumpAllTypes(); - if(Options::PTSAllPrint()) + if(Options::PTSAllPrint) dumpAllPts(); - if (Options::FuncPointerPrint()) + if (Options::FuncPointerPrint) printIndCSTargets(); getPTACallGraph()->verifyCallGraph(); - if (Options::CallGraphDotGraph()) - getPTACallGraph()->dump("callgraph_final"); + if (Options::CallGraphDotGraph) + getPTACallGraph()->dump("callgraph_final"); - if(!pag->isBuiltFromFile() && alias_validation) + // FSTBHC has its own TBHC-specific test validation. + if(!pag->isBuiltFromFile() && alias_validation + && !SVFUtil::isa(this)) validateTests(); - if (!Options::UsePreCompFieldSensitive()) + if (!Options::UsePreCompFieldSensitive) resetObjFieldSensitive(); } @@ -236,39 +295,52 @@ void PointerAnalysis::dumpAllTypes() for (OrderedNodeSet::iterator nIter = this->getAllValidPtrs().begin(); nIter != this->getAllValidPtrs().end(); ++nIter) { - const PAGNode* node = getPAG()->getGNode(*nIter); - if (SVFUtil::isa(node)) + const PAGNode* node = getPAG()->getPAGNode(*nIter); + if (SVFUtil::isa(node) || SVFUtil::isa(node)) continue; outs() << "##<" << node->getValue()->getName() << "> "; - outs() << "Source Loc: " << node->getValue()->getSourceLoc(); + outs() << "Source Loc: " << getSourceLoc(node->getValue()); outs() << "\nNodeID " << node->getId() << "\n"; - const SVFType* type = node->getValue()->getType(); - pag->getSymbolInfo()->printFlattenFields(type); + Type* type = node->getValue()->getType(); + SymbolTableInfo::SymbolInfo()->printFlattenFields(type); + if (PointerType* ptType = SVFUtil::dyn_cast(type)) + SymbolTableInfo::SymbolInfo()->printFlattenFields(ptType->getElementType()); } } /*! - * Dump points-to of top-level pointers (ValVar) + * Dump points-to of top-level pointers (ValPN) */ void PointerAnalysis::dumpPts(NodeID ptr, const PointsTo& pts) { - - const PAGNode* node = pag->getGNode(ptr); + std::unordered_set DebugFuncNames(Options::DebugFuncsList.begin(), + Options::DebugFuncsList.end()); + + const PAGNode* node = pag->getPAGNode(ptr); + if (!DebugFuncNames.empty()) { + if (node->hasValue()) { + const Value* pagValue = node->getValue(); + if (const Instruction* inst = SVFUtil::dyn_cast(pagValue)) { + if (DebugFuncNames.find(inst->getFunction()->getName().str()) + == DebugFuncNames.end()) { + return; + } + } + } + } /// print the points-to set of node which has the maximum pts size. - if (SVFUtil::isa (node)) + if (SVFUtil::isa (node)) { outs() << "## id:" << node->getId(); } - else if (!SVFUtil::isa(node) && !SVFModule::pagReadFromTXT()) - { - if (node->hasValue()) - { - outs() << "##<" << node->getValue()->getName() << "> "; - outs() << "Source Loc: " << node->getValue()->getSourceLoc(); - } - } + else if (!SVFUtil::isa(node) && !SVFModule::pagReadFromTXT()) { + if (node->hasValue()) { + outs() << "##<" << node->getValue()->getName() << "> "; + outs() << "Source Loc: " << getSourceLoc(node->getValue()); + } + } outs() << "\nPtr " << node->getId() << " "; if (pts.empty()) @@ -286,44 +358,47 @@ void PointerAnalysis::dumpPts(NodeID ptr, const PointsTo& pts) outs() << ""; - for (PointsTo::iterator it = pts.begin(), eit = pts.end(); it != eit; ++it) + for (NodeBS::iterator it = pts.begin(), eit = pts.end(); it != eit; ++it) { - const PAGNode* node = pag->getGNode(*it); - if(SVFUtil::isa(node) == false) + const PAGNode* node = pag->getPAGNode(*it); + if(SVFUtil::isa(node) == false) continue; NodeID ptd = node->getId(); outs() << "!!Target NodeID " << ptd << "\t ["; - const PAGNode* pagNode = pag->getGNode(ptd); - if (SVFUtil::isa(node)) + const PAGNode* pagNode = pag->getPAGNode(ptd); + if (SVFUtil::isa(node)) outs() << "DummyVal\n"; - else if (SVFUtil::isa(node)) + else if (SVFUtil::isa(node)) outs() << "Dummy Obj id: " << node->getId() << "]\n"; - else - { - if (!SVFModule::pagReadFromTXT()) - { - if (node->hasValue()) - { - outs() << "<" << pagNode->getValue()->getName() << "> "; + else { + if (!SVFModule::pagReadFromTXT()) { + if (node->hasValue()) { + + outs() << "<" << pagNode->getValue()->getName() << "> "; + if (const GepObjPN* gepObj = SVFUtil::dyn_cast(node)) { + outs() << "Field: " << gepObj->getLocationSet().getOffset(); + } + outs() << ">"; outs() << "Source Loc: " - << pagNode->getValue()->getSourceLoc() << "] \n"; - } - } - } + << getSourceLoc(pagNode->getValue()) << "] \n"; + } + } + } } } /*! * Print indirect call targets at an indirect callsite */ -void PointerAnalysis::printIndCSTargets(const CallICFGNode* cs, const FunctionSet& targets) +void PointerAnalysis::printIndCSTargets(const CallBlockNode* cs, const FunctionSet& targets) { outs() << "\nNodeID: " << getFunPtr(cs); outs() << "\nCallSite: "; - outs() << cs->getCallSite()->toString(); - outs() << "\tLocation: " << cs->getCallSite()->getSourceLoc(); + cs->getCallSite()->print(outs()); + outs() << "\tLocation: " << SVFUtil::getSourceLoc(cs->getCallSite()); outs() << "\t with Targets: "; + if (!targets.empty()) { FunctionSet::const_iterator fit = targets.begin(); @@ -340,6 +415,7 @@ void PointerAnalysis::printIndCSTargets(const CallICFGNode* cs, const FunctionSe } outs() << "\n"; + } /*! @@ -353,7 +429,7 @@ void PointerAnalysis::printIndCSTargets() CallEdgeMap::const_iterator eit = callEdges.end(); for (; it != eit; ++it) { - const CallICFGNode* cs = it->first; + const CallBlockNode* cs = it->first; const FunctionSet& targets = it->second; printIndCSTargets(cs, targets); } @@ -363,13 +439,13 @@ void PointerAnalysis::printIndCSTargets() CallSiteToFunPtrMap::const_iterator csEit = indCS.end(); for (; csIt != csEit; ++csIt) { - const CallICFGNode* cs = csIt->first; + const CallBlockNode* cs = csIt->first; if (hasIndCSCallees(cs) == false) { outs() << "\nNodeID: " << csIt->second; outs() << "\nCallSite: "; - outs() << cs->getCallSite()->toString(); - outs() << "\tLocation: " << cs->getCallSite()->getSourceLoc(); + cs->getCallSite()->print(outs()); + outs() << "\tLocation: " << SVFUtil::getSourceLoc(cs->getCallSite()); outs() << "\n\t!!!has no targets!!!\n"; } } @@ -380,7 +456,7 @@ void PointerAnalysis::printIndCSTargets() /*! * Resolve indirect calls */ -void PointerAnalysis::resolveIndCalls(const CallICFGNode* cs, const PointsTo& target, CallEdgeMap& newEdges) +void PointerAnalysis::resolveIndCalls(const CallBlockNode* cs, const PointsTo& target, CallEdgeMap& newEdges, LLVMCallGraph*) { assert(pag->isIndirectCallSites(cs) && "not an indirect callsite?"); @@ -389,24 +465,32 @@ void PointerAnalysis::resolveIndCalls(const CallICFGNode* cs, const PointsTo& ta ii != ie; ii++) { - if(getNumOfResolvedIndCallEdge() >= Options::IndirectCallLimit()) + if(getNumOfResolvedIndCallEdge() >= Options::IndirectCallLimit) { wrnMsg("Resolved Indirect Call Edges are Out-Of-Budget, please increase the limit"); return; } - if(ObjVar* objPN = SVFUtil::dyn_cast(pag->getGNode(*ii))) + if(ObjPN* objPN = SVFUtil::dyn_cast(pag->getPAGNode(*ii))) { const MemObj* obj = pag->getObject(objPN); if(obj->isFunction()) { - const SVFFunction* calleefun = SVFUtil::cast(obj->getValue()); - const SVFFunction* callee = calleefun->getDefFunForMultipleModule(); - - if(SVFUtil::matchArgs(cs->getCallSite(), callee) == false) - continue; - + const Function* calleefun = SVFUtil::cast(obj->getRefVal()); + const SVFFunction* callee = getDefFunForMultipleModule(calleefun); + + if (Options::InvariantPWC) { + /// if the arg size does not match then we do not need to connect this parameter + /// even if the callee is a variadic function (the first parameter of variadic function is its paramter number) + if(matchArgs(cs, callee) == false) + continue; + + if (pag->getImpactedByCollapseSet().test(*ii)) { + if (matchArgTypes(cs, callee) == false) + continue; + } + } if(0 == getIndCallMap()[cs].count(callee)) { newEdges[cs].insert(callee); @@ -423,72 +507,110 @@ void PointerAnalysis::resolveIndCalls(const CallICFGNode* cs, const PointsTo& ta } } +/*! + * Match arguments for callsite at caller and callee + */ +bool PointerAnalysis::matchArgTypes(const CallBlockNode* cs, const SVFFunction* callee) +{ + const Instruction* inst = cs->getCallSite(); + const CallInst* cInst = SVFUtil::dyn_cast(inst); + if (!cInst) return true; // whatever this is + + const Function* fun = callee->getLLVMFun(); + + PointerType* voidPtrTy = PointerType::get(Type::getInt8Ty(cInst->getContext()), 0); + const FunctionType* fType = fun->getFunctionType(); + for (int i = 0; i < cInst->arg_size(); i++) { + Value* arg = cInst->getArgOperand(i); + Type* argTy = arg->getType(); + Type* paramTy = fType->getParamType(i); + + if (argTy != voidPtrTy && paramTy != voidPtrTy && argTy != paramTy) { + //llvm::errs() << "Filtered " << fun->getName() << " from " << *cInst << " in function " << cInst->getParent()->getParent()->getName() << "\n"; + return false; + } + } + return true; +} + + +/*! + * Match arguments for callsite at caller and callee + */ +bool PointerAnalysis::matchArgs(const CallBlockNode* cs, const SVFFunction* callee) +{ + if(ThreadAPI::getThreadAPI()->isTDFork(cs->getCallSite())) + return true; + else + return SVFUtil::getLLVMCallSite(cs->getCallSite()).arg_size() == callee->arg_size(); +} + /* * Get virtual functions "vfns" based on CHA */ -void PointerAnalysis::getVFnsFromCHA(const CallICFGNode* cs, VFunSet &vfns) +void PointerAnalysis::getVFnsFromCHA(const CallBlockNode* cs, VFunSet &vfns) { - if (chgraph->csHasVFnsBasedonCHA(SVFUtil::getSVFCallSite(cs->getCallSite()))) - vfns = chgraph->getCSVFsBasedonCHA(SVFUtil::getSVFCallSite(cs->getCallSite())); + if (chgraph->csHasVFnsBasedonCHA(SVFUtil::getLLVMCallSite(cs->getCallSite()))) + vfns = chgraph->getCSVFsBasedonCHA(SVFUtil::getLLVMCallSite(cs->getCallSite())); } /* * Get virtual functions "vfns" from PoninsTo set "target" for callsite "cs" */ -void PointerAnalysis::getVFnsFromPts(const CallICFGNode* cs, const PointsTo &target, VFunSet &vfns) +void PointerAnalysis::getVFnsFromPts(const CallBlockNode* cs, const PointsTo &target, VFunSet &vfns) { - if (chgraph->csHasVtblsBasedonCHA(SVFUtil::getSVFCallSite(cs->getCallSite()))) + if (chgraph->csHasVtblsBasedonCHA(SVFUtil::getLLVMCallSite(cs->getCallSite()))) { - Set vtbls; - const VTableSet &chaVtbls = chgraph->getCSVtblsBasedonCHA(SVFUtil::getSVFCallSite(cs->getCallSite())); + Set vtbls; + const VTableSet &chaVtbls = chgraph->getCSVtblsBasedonCHA(SVFUtil::getLLVMCallSite(cs->getCallSite())); for (PointsTo::iterator it = target.begin(), eit = target.end(); it != eit; ++it) { - const PAGNode *ptdnode = pag->getGNode(*it); + const PAGNode *ptdnode = pag->getPAGNode(*it); if (ptdnode->hasValue()) { - if (const SVFGlobalValue *vtbl = SVFUtil::dyn_cast(ptdnode->getValue())) + if (const GlobalValue *vtbl = SVFUtil::dyn_cast(ptdnode->getValue())) { if (chaVtbls.find(vtbl) != chaVtbls.end()) vtbls.insert(vtbl); } } } - chgraph->getVFnsFromVtbls(SVFUtil::getSVFCallSite(cs->getCallSite()), vtbls, vfns); + chgraph->getVFnsFromVtbls(SVFUtil::getLLVMCallSite(cs->getCallSite()), vtbls, vfns); } } /* * Connect callsite "cs" to virtual functions in "vfns" */ -void PointerAnalysis::connectVCallToVFns(const CallICFGNode* cs, const VFunSet &vfns, CallEdgeMap& newEdges) +void PointerAnalysis::connectVCallToVFns(const CallBlockNode* cs, const VFunSet &vfns, CallEdgeMap& newEdges) { //// connect all valid functions for (VFunSet::const_iterator fit = vfns.begin(), feit = vfns.end(); fit != feit; ++fit) { const SVFFunction* callee = *fit; - callee = callee->getDefFunForMultipleModule(); + callee = getDefFunForMultipleModule(callee->getLLVMFun()); if (getIndCallMap()[cs].count(callee) > 0) continue; - if(SVFUtil::getSVFCallSite(cs->getCallSite()).arg_size() == callee->arg_size() || - (SVFUtil::getSVFCallSite(cs->getCallSite()).isVarArg() && callee->isVarArg())) + if(SVFUtil::getLLVMCallSite(cs->getCallSite()).arg_size() == callee->arg_size() || + (SVFUtil::getLLVMCallSite(cs->getCallSite()).getFunctionType()->isVarArg() && callee->isVarArg())) { newEdges[cs].insert(callee); getIndCallMap()[cs].insert(callee); - const CallICFGNode* callBlockNode = pag->getICFG()->getCallICFGNode(cs->getCallSite()); + const CallBlockNode* callBlockNode = pag->getICFG()->getCallBlockNode(cs->getCallSite()); ptaCallGraph->addIndirectCallGraphEdge(callBlockNode, cs->getCaller(),callee); } } } /// Resolve cpp indirect call edges -void PointerAnalysis::resolveCPPIndCalls(const CallICFGNode* cs, const PointsTo& target, CallEdgeMap& newEdges) +void PointerAnalysis::resolveCPPIndCalls(const CallBlockNode* cs, const PointsTo& target, CallEdgeMap& newEdges) { - assert(SVFUtil::getSVFCallSite(cs->getCallSite()).isVirtualCall() && "not cpp virtual call"); + assert(isVirtualCallSite(SVFUtil::getLLVMCallSite(cs->getCallSite())) && "not cpp virtual call"); VFunSet vfns; - if (Options::ConnectVCallOnCHA()) + if (Options::ConnectVCallOnCHA) getVFnsFromCHA(cs, vfns); else getVFnsFromPts(cs, target, vfns); @@ -501,46 +623,46 @@ void PointerAnalysis::resolveCPPIndCalls(const CallICFGNode* cs, const PointsTo& */ void PointerAnalysis::validateSuccessTests(std::string fun) { + // check for must alias cases, whether our alias analysis produce the correct results - if (const SVFFunction* checkFun = svfMod->getSVFFunction(fun)) + if (const SVFFunction* checkFun = getFunction(fun)) { - if(!checkFun->isUncalledFunction()) + if(!checkFun->getLLVMFun()->use_empty()) outs() << "[" << this->PTAName() << "] Checking " << fun << "\n"; - for(const CallICFGNode* callNode : pag->getCallSiteSet()) - { - const SVFInstruction* svfInst = callNode->getCallSite(); - if (SVFUtil::getCallee(svfInst) == checkFun) + for (Value::user_iterator i = checkFun->getLLVMFun()->user_begin(), e = + checkFun->getLLVMFun()->user_end(); i != e; ++i) + if (SVFUtil::isCallSite(*i)) { - CallSite cs(svfInst); + CallSite cs(*i); assert(cs.getNumArgOperands() == 2 && "arguments should be two pointers!!"); - const SVFValue* V1 = cs.getArgOperand(0); - const SVFValue* V2 = cs.getArgOperand(1); + Value* V1 = cs.getArgOperand(0); + Value* V2 = cs.getArgOperand(1); AliasResult aliasRes = alias(V1, V2); bool checkSuccessful = false; if (fun == aliasTestMayAlias || fun == aliasTestMayAliasMangled) { - if (aliasRes == AliasResult::MayAlias || aliasRes == AliasResult::MustAlias) + if (aliasRes == llvm::MayAlias || aliasRes == llvm::MustAlias) checkSuccessful = true; } else if (fun == aliasTestNoAlias || fun == aliasTestNoAliasMangled) { - if (aliasRes == AliasResult::NoAlias) + if (aliasRes == llvm::NoAlias) checkSuccessful = true; } else if (fun == aliasTestMustAlias || fun == aliasTestMustAliasMangled) { // change to must alias when our analysis support it - if (aliasRes == AliasResult::MayAlias || aliasRes == AliasResult::MustAlias) + if (aliasRes == llvm::MayAlias || aliasRes == llvm::MustAlias) checkSuccessful = true; } else if (fun == aliasTestPartialAlias || fun == aliasTestPartialAliasMangled) { // change to partial alias when our analysis support it - if (aliasRes == AliasResult::MayAlias) + if (aliasRes == llvm::MayAlias) checkSuccessful = true; } else @@ -551,16 +673,18 @@ void PointerAnalysis::validateSuccessTests(std::string fun) if (checkSuccessful) outs() << sucMsg("\t SUCCESS :") << fun << " check at (" - << svfInst->getSourceLoc() << ")\n"; + << getSourceLoc(*i) << ")\n"; else { SVFUtil::errs() << errMsg("\t FAILURE :") << fun << " check at (" << svfInst->getSourceLoc() << ")\n"; + << "> at (" << getSourceLoc(*i) << ")\n"; assert(false && "test case failed!"); } } - } + else + assert(false && "alias check functions not only used at callsite??"); + } } @@ -570,34 +694,32 @@ void PointerAnalysis::validateSuccessTests(std::string fun) void PointerAnalysis::validateExpectedFailureTests(std::string fun) { - if (const SVFFunction* checkFun = svfMod->getSVFFunction(fun)) + if (const SVFFunction* checkFun = getFunction(fun)) { - if(!checkFun->isUncalledFunction()) + if(!checkFun->getLLVMFun()->use_empty()) outs() << "[" << this->PTAName() << "] Checking " << fun << "\n"; - for(const CallICFGNode* callNode : pag->getCallSiteSet()) - { - const SVFInstruction* svfInst = callNode->getCallSite(); - if (SVFUtil::getCallee(svfInst) == checkFun) + for (Value::user_iterator i = checkFun->getLLVMFun()->user_begin(), e = + checkFun->getLLVMFun()->user_end(); i != e; ++i) + if (CallInst *call = SVFUtil::dyn_cast(*i)) { - CallSite call = getSVFCallSite(svfInst); - assert(call.arg_size() == 2 + assert(call->getNumArgOperands() == 2 && "arguments should be two pointers!!"); - const SVFValue* V1 = call.getArgOperand(0); - const SVFValue* V2 = call.getArgOperand(1); + Value* V1 = call->getArgOperand(0); + Value* V2 = call->getArgOperand(1); AliasResult aliasRes = alias(V1, V2); bool expectedFailure = false; if (fun == aliasTestFailMayAlias || fun == aliasTestFailMayAliasMangled) { // change to must alias when our analysis support it - if (aliasRes == AliasResult::NoAlias) + if (aliasRes == llvm::NoAlias) expectedFailure = true; } else if (fun == aliasTestFailNoAlias || fun == aliasTestFailNoAliasMangled) { // change to partial alias when our analysis support it - if (aliasRes == AliasResult::MayAlias || aliasRes == AliasResult::PartialAlias || aliasRes == AliasResult::MustAlias) + if (aliasRes == llvm::MayAlias || aliasRes == llvm::PartialAlias || aliasRes == llvm::MustAlias) expectedFailure = true; } else @@ -608,14 +730,15 @@ void PointerAnalysis::validateExpectedFailureTests(std::string fun) if (expectedFailure) outs() << sucMsg("\t EXPECTED-FAILURE :") << fun << " check at (" - << call.getInstruction()->getSourceLoc() << ")\n"; + << getSourceLoc(call) << ")\n"; else { SVFUtil::errs() << errMsg("\t UNEXPECTED FAILURE :") << fun << " check at (" - << call.getInstruction()->getSourceLoc() << ")\n"; + << getSourceLoc(call) << ")\n"; assert(false && "test case failed!"); } } - } + else + assert(false && "alias check functions not only used at callsite??"); } } diff --git a/svf/lib/MemoryModel/PointerAnalysisImpl.cpp b/svf/lib/MemoryModel/PointerAnalysisImpl.cpp index de0ccd365..fe30ab4c7 100644 --- a/svf/lib/MemoryModel/PointerAnalysisImpl.cpp +++ b/svf/lib/MemoryModel/PointerAnalysisImpl.cpp @@ -1,26 +1,3 @@ -//===- PointerAnalysisImpl.cpp -- Pointer analysis implementation--------------------// -// -// SVF: Static Value-Flow Analysis -// -// Copyright (C) <2013-> -// - -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU Affero General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Affero General Public License for more details. - -// You should have received a copy of the GNU Affero General Public License -// along with this program. If not, see . -// -//===----------------------------------------------------------------------===// - - /* * PointerAnalysisImpl.cpp * @@ -29,58 +6,65 @@ */ +#include "Util/Options.h" #include "MemoryModel/PointerAnalysisImpl.h" +#include "SVF-FE/CPPUtil.h" +#include "SVF-FE/DCHG.h" #include "Util/Options.h" +#include "Util/IRAnnotator.h" #include #include using namespace SVF; using namespace SVFUtil; +using namespace cppUtil; using namespace std; +PersistentPointsToCache BVDataPTAImpl::ptCache = PersistentPointsToCache(PointsTo()); + /*! * Constructor */ -BVDataPTAImpl::BVDataPTAImpl(SVFIR* p, PointerAnalysis::PTATY type, bool alias_check) : - PointerAnalysis(p, type, alias_check), ptCache() +BVDataPTAImpl::BVDataPTAImpl(PAG* p, PointerAnalysis::PTATY type, bool alias_check) : + PointerAnalysis(p, type, alias_check) { - if (type == Andersen_BASE || type == Andersen_WPA || type == AndersenWaveDiff_WPA - || type == TypeCPP_WPA || type == FlowS_DDA - || type == AndersenSCD_WPA || type == AndersenSFR_WPA || type == CFLFICI_WPA || type == CFLFSCS_WPA) + if (type == Andersen_BASE || type == Andersen_WPA || type == AndersenWaveDiff_WPA || type == AndersenHCD_WPA || type == AndersenHLCD_WPA + || type == AndersenLCD_WPA || type == TypeCPP_WPA || type == FlowS_DDA || type == AndersenWaveDiffWithType_WPA + || type == AndersenSCD_WPA || type == AndersenSFR_WPA) { // Only maintain reverse points-to when the analysis is field-sensitive, as objects turning // field-insensitive is all it is used for. - bool maintainRevPts = Options::MaxFieldLimit() != 0; - if (Options::ptDataBacking() == PTBackingType::Mutable) ptD = std::make_unique(maintainRevPts); - else if (Options::ptDataBacking() == PTBackingType::Persistent) ptD = std::make_unique(getPtCache(), maintainRevPts); + bool maintainRevPts = Options::MaxFieldLimit != 0; + if (Options::ptDataBacking == PTBackingType::Mutable) ptD = new MutDiffPTDataTy(maintainRevPts); + else if (Options::ptDataBacking == PTBackingType::Persistent) ptD = new PersDiffPTDataTy(getPtCache(), maintainRevPts); else assert(false && "BVDataPTAImpl::BVDataPTAImpl: unexpected points-to backing type!"); } else if (type == Steensgaard_WPA) { // Steensgaard is only field-insensitive (for now?), so no reverse points-to. - if (Options::ptDataBacking() == PTBackingType::Mutable) ptD = std::make_unique(false); - else if (Options::ptDataBacking() == PTBackingType::Persistent) ptD = std::make_unique(getPtCache(), false); + if (Options::ptDataBacking == PTBackingType::Mutable) ptD = new MutDiffPTDataTy(false); + else if (Options::ptDataBacking == PTBackingType::Persistent) ptD = new PersDiffPTDataTy(getPtCache(), false); else assert(false && "BVDataPTAImpl::BVDataPTAImpl: unexpected points-to backing type!"); } - else if (type == FSSPARSE_WPA) + else if (type == FSSPARSE_WPA || type == FSTBHC_WPA) { - if (Options::INCDFPTData()) + if (Options::INCDFPTData) { - if (Options::ptDataBacking() == PTBackingType::Mutable) ptD = std::make_unique(false); - else if (Options::ptDataBacking() == PTBackingType::Persistent) ptD = std::make_unique(getPtCache(), false); + if (Options::ptDataBacking == PTBackingType::Mutable) ptD = new MutIncDFPTDataTy(false); + else if (Options::ptDataBacking == PTBackingType::Persistent) ptD = new PersIncDFPTDataTy(getPtCache(), false); else assert(false && "BVDataPTAImpl::BVDataPTAImpl: unexpected points-to backing type!"); } else { - if (Options::ptDataBacking() == PTBackingType::Mutable) ptD = std::make_unique(false); - else if (Options::ptDataBacking() == PTBackingType::Persistent) ptD = std::make_unique(getPtCache(), false); + if (Options::ptDataBacking == PTBackingType::Mutable) ptD = new MutDFPTDataTy(false); + else if (Options::ptDataBacking == PTBackingType::Persistent) ptD = new PersDFPTDataTy(getPtCache(), false); else assert(false && "BVDataPTAImpl::BVDataPTAImpl: unexpected points-to backing type!"); } } else if (type == VFS_WPA) { - if (Options::ptDataBacking() == PTBackingType::Mutable) ptD = std::make_unique(false); - else if (Options::ptDataBacking() == PTBackingType::Persistent) ptD = std::make_unique(getPtCache(), false); + if (Options::ptDataBacking == PTBackingType::Mutable) ptD = new MutVersionedPTDataTy(false); + else if (Options::ptDataBacking == PTBackingType::Persistent) ptD = new PersVersionedPTDataTy(getPtCache(), false); else assert(false && "BVDataPTAImpl::BVDataPTAImpl: unexpected points-to backing type!"); } else assert(false && "no points-to data available"); @@ -88,43 +72,6 @@ BVDataPTAImpl::BVDataPTAImpl(SVFIR* p, PointerAnalysis::PTATY type, bool alias_c ptaImplTy = BVDataImpl; } -void BVDataPTAImpl::finalize() -{ - normalizePointsTo(); - PointerAnalysis::finalize(); - - if (Options::ptDataBacking() == PTBackingType::Persistent && print_stat) - { - std::string moduleName(pag->getModule()->getModuleIdentifier()); - std::vector names = SVFUtil::split(moduleName,'/'); - if (names.size() > 1) - moduleName = names[names.size() - 1]; - - std::string subtitle; - - if(ptaTy >= Andersen_BASE && ptaTy <= Steensgaard_WPA) - subtitle = "Andersen's analysis bitvector"; - else if(ptaTy >=FSDATAFLOW_WPA && ptaTy <=FSCS_WPA) - subtitle = "flow-sensitive analysis bitvector"; - else if(ptaTy >=CFLFICI_WPA && ptaTy <=CFLFSCS_WPA) - subtitle = "CFL analysis bitvector"; - else if(ptaTy == TypeCPP_WPA) - subtitle = "Type analysis bitvector"; - else if(ptaTy >=FieldS_DDA && ptaTy <=Cxt_DDA) - subtitle = "DDA bitvector"; - else - subtitle = "bitvector"; - - SVFUtil::outs() << "\n****Persistent Points-To Cache Statistics: " << subtitle << "****\n"; - SVFUtil::outs() << "################ (program : " << moduleName << ")###############\n"; - SVFUtil::outs().flags(std::ios::left); - ptCache.printStats("bitvector"); - SVFUtil::outs() << "#######################################################" << std::endl; - SVFUtil::outs().flush(); - } - -} - /*! * Expand all fields of an aggregate in all points-to sets */ @@ -133,162 +80,113 @@ void BVDataPTAImpl::expandFIObjs(const PointsTo& pts, PointsTo& expandedPts) expandedPts = pts;; for(PointsTo::iterator pit = pts.begin(), epit = pts.end(); pit!=epit; ++pit) { - if (pag->getBaseObjVar(*pit) == *pit || isFieldInsensitive(*pit)) + if (pag->getBaseObjNode(*pit) == *pit || isFieldInsensitive(*pit)) { - expandedPts |= pag->getAllFieldsObjVars(*pit); + expandedPts |= pag->getAllFieldsObjNode(*pit); } } } -void BVDataPTAImpl::expandFIObjs(const NodeBS& pts, NodeBS& expandedPts) +/*! + * Store pointer analysis result into a file. + * It includes the points-to relations, and all PAG nodes including those + * created when solving Andersen's constraints. + */ +void BVDataPTAImpl::writeToFile(const string& filename) { - expandedPts = pts; - for (const NodeID o : pts) - { - if (pag->getBaseObjVar(o) == o || isFieldInsensitive(o)) - { - expandedPts |= pag->getAllFieldsObjVars(o); - } - } -} + writeToModule(); -void BVDataPTAImpl::remapPointsToSets(void) -{ - getPTDataTy()->remapAllPts(); -} + outs() << "Storing pointer analysis results to '" << filename << "'..."; -void BVDataPTAImpl::writeObjVarToFile(const string& filename) -{ - outs() << "Storing ObjVar to '" << filename << "'..."; error_code err; - std::fstream f(filename.c_str(), std::ios_base::out); - if (!f.good()) + ToolOutputFile F(filename.c_str(), err, llvm::sys::fs::F_None); + if (err) { outs() << " error opening file for writing!\n"; + F.os().clear_error(); return; } - // Write BaseNodes insensitivity to file - NodeBS NodeIDs; - for (auto it = pag->begin(), ie = pag->end(); it != ie; ++it) - { - PAGNode* pagNode = it->second; - if (!isa(pagNode)) continue; - NodeID n = pag->getBaseObjVar(it->first); - if (NodeIDs.test(n)) continue; - f << n << " "; - f << isFieldInsensitive(n) << "\n"; - NodeIDs.set(n); - } - - f << "------\n"; - - f.close(); - if (f.good()) - { - outs() << "\n"; - return; - } - - -} - -void BVDataPTAImpl::writePtsResultToFile(std::fstream& f) -{ // Write analysis results to file - for (auto it = pag->begin(), ie = pag->end(); it != ie; ++it) - { + for (auto it = pag->begin(), ie = pag->end(); it != ie; ++it) { NodeID var = it->first; const PointsTo &pts = getPts(var); stringstream ss; - f << var << " -> { "; - if (pts.empty()) - { - f << " "; - } - else - { - for (NodeID n: pts) - { - f << n << " "; + F.os() << var << " -> { "; + if (pts.empty()) { + F.os() << " "; + } else { + for (NodeID n: pts) { + F.os() << n << " "; } } - f << "}\n"; - } - -} - -void BVDataPTAImpl::writeGepObjVarMapToFile(std::fstream& f) -{ - //write gepObjVarMap to file(in form of: baseID offset gepObjNodeId) - SVFIR::NodeOffsetMap &gepObjVarMap = pag->getGepObjNodeMap(); - for(SVFIR::NodeOffsetMap::const_iterator it = gepObjVarMap.begin(), eit = gepObjVarMap.end(); it != eit; it++) - { - const SVFIR::NodeOffset offsetPair = it -> first; - //write the base id to file - f << offsetPair.first << " "; - //write the offset to file - f << offsetPair.second << " "; - //write the gepObjNodeId to file - f << it->second << "\n"; + F.os() << "}\n"; } -} - -/*! - * Store pointer analysis result into a file. - * It includes the points-to relations, and all SVFIR nodes including those - * created when solving Andersen's constraints. - */ -void BVDataPTAImpl::writeToFile(const string& filename) -{ - - outs() << "Storing pointer analysis results to '" << filename << "'..."; - error_code err; - std::fstream f(filename.c_str(), std::ios_base::app); - if (!f.good()) + // Write GepPAGNodes to file + for (auto it = pag->begin(), ie = pag->end(); it != ie; ++it) { - outs() << " error opening file for writing!\n"; - return; + PAGNode* pagNode = it->second; + if (GepObjPN *gepObjPN = SVFUtil::dyn_cast(pagNode)) + { + F.os() << it->first << " "; + F.os() << pag->getBaseObjNode(it->first) << " "; + F.os() << gepObjPN->getLocationSet().getOffset() << "\n"; + } } - writePtsResultToFile(f); - - f << "------\n"; - - writeGepObjVarMapToFile(f); - - f << "------\n"; - + F.os() << "------\n"; // Write BaseNodes insensitivity to file NodeBS NodeIDs; for (auto it = pag->begin(), ie = pag->end(); it != ie; ++it) { PAGNode* pagNode = it->second; - if (!isa(pagNode)) continue; - NodeID n = pag->getBaseObjVar(it->first); + if (!isa(pagNode)) continue; + NodeID n = pag->getBaseObjNode(it->first); if (NodeIDs.test(n)) continue; - f << n << " "; - f << isFieldInsensitive(n) << "\n"; + F.os() << n << " "; + F.os() << isFieldInsensitive(n) << "\n"; NodeIDs.set(n); } // Job finish and close file - f.close(); - if (f.good()) + F.os().close(); + if (!F.os().has_error()) { outs() << "\n"; + F.keep(); return; } } -void BVDataPTAImpl::readPtsResultFromFile(std::ifstream& F) +/*! + * Load pointer analysis result form a file. + * It populates BVDataPTAImpl with the points-to data, and updates PAG with + * the PAG offset nodes created during Andersen's solving stage. + */ +bool BVDataPTAImpl::readFromFile(const string& filename) { - string line; + // If the module annotations are available, read from there instead + auto mainModule = SVF::LLVMModuleSet::getLLVMModuleSet()->getMainLLVMModule(); + if (mainModule->getNamedMetadata("PAG-Annotated") != nullptr) + { + return readFromModule(); + } + + outs() << "Loading pointer analysis results from '" << filename << "'..."; + + ifstream F(filename.c_str()); + if (!F.is_open()) + { + outs() << " error opening file for reading!\n"; + return false; + } + // Read analysis results from file PTDataTy *ptD = getPTDataTy(); + string line; // Read points-to sets string delimiter1 = " -> { "; @@ -300,8 +198,6 @@ void BVDataPTAImpl::readPtsResultFromFile(std::ifstream& F) { // Parse a single line in the form of "var -> { obj1 obj2 obj3 }" getline(F, line); - if (line.at(0) == '[' || line == "---VERSIONED---") continue; - if (line == "------") break; size_t pos = line.find(delimiter1); if (pos == string::npos) break; if (line.back() != '}') break; @@ -336,53 +232,28 @@ void BVDataPTAImpl::readPtsResultFromFile(std::ifstream& F) // map the variable ID to its pointer set for (auto t: nodePtsMap) ptD->unionPts(t.first, strPtsMap[t.second]); -} -void BVDataPTAImpl::readGepObjVarMapFromFile(std::ifstream& F) -{ - string line; - //read GepObjVarMap from file - SVFIR::NodeOffsetMap gepObjVarMap = pag->getGepObjNodeMap(); + // Read PAG offset nodes while (F.good()) { - getline(F, line); if (line == "------") break; // Parse a single line in the form of "ID baseNodeID offset" istringstream ss(line); + NodeID id; NodeID base; size_t offset; - NodeID id; - ss >> base >> offset >>id; - SVFIR::NodeOffsetMap::const_iterator iter = gepObjVarMap.find(std::make_pair(base, offset)); - if (iter == gepObjVarMap.end()) - { - SVFVar* node = pag->getGNode(base); - const MemObj* obj = nullptr; - if (GepObjVar* gepObjVar = SVFUtil::dyn_cast(node)) - obj = gepObjVar->getMemObj(); - else if (FIObjVar* baseNode = SVFUtil::dyn_cast(node)) - obj = baseNode->getMemObj(); - else if (DummyObjVar* baseNode = SVFUtil::dyn_cast(node)) - obj = baseNode->getMemObj(); - else - assert(false && "new gep obj node kind?"); - pag->addGepObjNode(obj, offset, id); - NodeIDAllocator::get()->increaseNumOfObjAndNodes(); - } + ss >> id >> base >> offset; + NodeID n = pag->getGepObjNode(pag->getObject(base), LocationSet(offset)); + assert(id == n && "Error adding GepObjNode into PAG!"); + getline(F, line); } -} -void BVDataPTAImpl::readAndSetObjFieldSensitivity(std::ifstream& F, const std::string& delimiterStr) -{ - string line; - // //update ObjVar status + // Read BaseNode insensitivity while (F.good()) { getline(F, line); - if (line.empty() || line == delimiterStr) - break; // Parse a single line in the form of "baseNodeID insensitive" istringstream ss(line); NodeID base; @@ -393,33 +264,6 @@ void BVDataPTAImpl::readAndSetObjFieldSensitivity(std::ifstream& F, const std::s setObjFieldInsensitive(base); } -} - -/*! - * Load pointer analysis result form a file. - * It populates BVDataPTAImpl with the points-to data, and updates SVFIR with - * the SVFIR offset nodes created during Andersen's solving stage. - */ -bool BVDataPTAImpl::readFromFile(const string& filename) -{ - - outs() << "Loading pointer analysis results from '" << filename << "'..."; - - ifstream F(filename.c_str()); - if (!F.is_open()) - { - outs() << " error opening file for reading!\n"; - return false; - } - - readAndSetObjFieldSensitivity(F,"------"); - - readPtsResultFromFile(F); - - readGepObjVarMapFromFile(F); - - readAndSetObjFieldSensitivity(F,""); - // Update callgraph updateCallGraph(pag->getIndirectCallsites()); @@ -429,6 +273,30 @@ bool BVDataPTAImpl::readFromFile(const string& filename) return true; } +/*! + * Store pointer analysis result into the current LLVM module as metadata. + * It includes the points-to relations, and all PAG nodes including those + * created when solving Andersen's constraints. + */ +void BVDataPTAImpl::writeToModule() +{ + auto irAnnotator = std::make_unique(); + auto mainModule = SVF::LLVMModuleSet::getLLVMModuleSet()->getMainLLVMModule(); + + irAnnotator->processAndersenResults(pag, this, true); +} + +/*! + * Load pointer analysis result from the metadata in the module. + * It populates BVDataPTAImpl with the points-to data, and updates PAG with + * the PAG offset nodes created during Andersen's solving stage. + */ +bool BVDataPTAImpl::readFromModule() +{ + auto irAnnotator = std::make_unique(); + irAnnotator->processAndersenResults(pag, this, false); + return true; +} /*! * Dump points-to of each pag node @@ -438,7 +306,7 @@ void BVDataPTAImpl::dumpTopLevelPtsTo() for (OrderedNodeSet::iterator nIter = this->getAllValidPtrs().begin(); nIter != this->getAllValidPtrs().end(); ++nIter) { - const PAGNode* node = getPAG()->getGNode(*nIter); + const PAGNode* node = getPAG()->getPAGNode(*nIter); if (getPAG()->isValidTopLevelPtr(node)) { const PointsTo& pts = this->getPts(node->getId()); @@ -464,12 +332,12 @@ void BVDataPTAImpl::dumpTopLevelPtsTo() /*! - * Dump all points-to including top-level (ValVar) and address-taken (ObjVar) variables + * Dump all points-to including top-level (ValPN) and address-taken (ObjPN) variables */ void BVDataPTAImpl::dumpAllPts() { OrderedNodeSet pagNodes; - for(SVFIR::iterator it = pag->begin(), eit = pag->end(); it!=eit; it++) + for(PAG::iterator it = pag->begin(), eit = pag->end(); it!=eit; it++) { pagNodes.insert(it->first); } @@ -493,73 +361,65 @@ void BVDataPTAImpl::onTheFlyCallGraphSolve(const CallSiteToFunPtrMap& callsites, { for(CallSiteToFunPtrMap::const_iterator iter = callsites.begin(), eiter = callsites.end(); iter!=eiter; ++iter) { - const CallICFGNode* cs = iter->first; + const CallBlockNode* cs = iter->first; - if (SVFUtil::getSVFCallSite(cs->getCallSite()).isVirtualCall()) + if (isVirtualCallSite(SVFUtil::getLLVMCallSite(cs->getCallSite()))) { - const SVFValue* vtbl = SVFUtil::getSVFCallSite(cs->getCallSite()).getVtablePtr(); + const Value *vtbl = getVCallVtblPtr(SVFUtil::getLLVMCallSite(cs->getCallSite())); assert(pag->hasValueNode(vtbl)); NodeID vtblId = pag->getValueNode(vtbl); resolveCPPIndCalls(cs, getPts(vtblId), newEdges); } - else - resolveIndCalls(iter->first,getPts(iter->second),newEdges); + else { + resolveIndCalls(iter->first,getPts(iter->second),newEdges); + } } } /*! * Normalize points-to information for field-sensitive analysis */ -void BVDataPTAImpl::normalizePointsTo() -{ - SVFIR::MemObjToFieldsMap &memToFieldsMap = pag->getMemToFieldsMap(); - SVFIR::NodeOffsetMap &GepObjVarMap = pag->getGepObjNodeMap(); +void BVDataPTAImpl::normalizePointsTo() { + PAG::MemObjToFieldsMap &memToFieldsMap = pag->getMemToFieldsMap(); + PAG::NodeLocationSetMap &GepObjNodeMap = pag->getGepObjNodeMap(); // collect each gep node whose base node has been set as field-insensitive NodeBS dropNodes; - for (auto t: memToFieldsMap) - { + for (auto t: memToFieldsMap){ NodeID base = t.first; const MemObj* memObj = pag->getObject(base); assert(memObj && "Invalid memobj in memToFieldsMap"); - if (memObj->isFieldInsensitive()) - { - for (NodeID id : t.second) - { - if (SVFUtil::isa(pag->getGNode(id))) - { + if (memObj->isFieldInsensitive()) { + for (NodeID id : t.second) { + if (SVFUtil::isa(pag->getPAGNode(id))) { dropNodes.set(id); - } - else + } else assert(id == base && "Not a GepObj Node or a baseObj Node?"); } } } // remove the collected redundant gep nodes in each pointers's pts - for (SVFIR::iterator nIter = pag->begin(); nIter != pag->end(); ++nIter) - { + for (PAG::iterator nIter = pag->begin(); nIter != pag->end(); ++nIter) { NodeID n = nIter->first; const PointsTo &tmpPts = getPts(n); - for (NodeID obj : tmpPts) - { + for (NodeID obj : tmpPts) { if (!dropNodes.test(obj)) continue; - NodeID baseObj = pag->getBaseObjVar(obj); + NodeID baseObj = pag->getBaseObjNode(obj); clearPts(n, obj); addPts(n, baseObj); } } - // clear GepObjVarMap and memToFieldsMap for redundant gepnodes + // clear GepObjNodeMap and memToFieldsMap for redundant gepnodes // and remove those nodes from pag - for (NodeID n: dropNodes) - { - NodeID base = pag->getBaseObjVar(n); - GepObjVar *gepNode = SVFUtil::dyn_cast(pag->getGNode(n)); - const APOffset apOffset = gepNode->getConstantFieldIdx(); - GepObjVarMap.erase(std::make_pair(base, apOffset)); + for (NodeID n: dropNodes) { + NodeID base = pag->getBaseObjNode(n); + GepObjPN *gepNode = SVFUtil::dyn_cast(pag->getPAGNode(n)); + const LocationSet ls = gepNode->getLocationSet(); + GepObjNodeMap.erase(std::make_pair(base, ls)); memToFieldsMap[base].reset(n); pag->removeGNode(gepNode); @@ -569,8 +429,17 @@ void BVDataPTAImpl::normalizePointsTo() /*! * Return alias results based on our points-to/alias analysis */ -AliasResult BVDataPTAImpl::alias(const SVFValue* V1, - const SVFValue* V2) +AliasResult BVDataPTAImpl::alias(const MemoryLocation &LocA, + const MemoryLocation &LocB) +{ + return alias(LocA.Ptr, LocB.Ptr); +} + +/*! + * Return alias results based on our points-to/alias analysis + */ +AliasResult BVDataPTAImpl::alias(const Value* V1, + const Value* V2) { return alias(pag->getValueNode(V1),pag->getValueNode(V2)); } @@ -595,7 +464,7 @@ AliasResult BVDataPTAImpl::alias(const PointsTo& p1, const PointsTo& p2) expandFIObjs(p2,pts2); if (containBlackHoleNode(pts1) || containBlackHoleNode(pts2) || pts1.intersects(pts2)) - return AliasResult::MayAlias; + return llvm::MayAlias; else - return AliasResult::NoAlias; + return llvm::NoAlias; } diff --git a/svf/lib/MemoryModel/PointsTo.cpp b/svf/lib/MemoryModel/PointsTo.cpp deleted file mode 100644 index 6170c5d3d..000000000 --- a/svf/lib/MemoryModel/PointsTo.cpp +++ /dev/null @@ -1,589 +0,0 @@ -//===- PointsTo.cpp -- Wrapper of set-like data structures ------------// - -/* - * PointsTo.cpp - * - * Abstracts away data structures to be used as points-to sets (implementation). - * - * Created on: Feb 01, 2021 - * Author: Mohamad Barbar - */ - -#include -#include - -#include "Util/Options.h" -#include "MemoryModel/PointsTo.h" -#include "SVFIR/SVFValue.h" - -namespace SVF -{ - -PointsTo::MappingPtr PointsTo::currentBestNodeMapping = nullptr; -PointsTo::MappingPtr PointsTo::currentBestReverseNodeMapping = nullptr; - -PointsTo::PointsTo() - : type(Options::PtType()), nodeMapping(currentBestNodeMapping), - reverseNodeMapping(currentBestReverseNodeMapping) -{ - if (type == SBV) new (&sbv) SparseBitVector<>(); - else if (type == CBV) new (&cbv) CoreBitVector(); - else if (type == BV) new (&bv) BitVector(); - else assert(false && "PointsTo::PointsTo: unknown type"); -} - -PointsTo::PointsTo(const PointsTo &pt) - : type(pt.type), nodeMapping(pt.nodeMapping), - reverseNodeMapping(pt.reverseNodeMapping) -{ - if (type == SBV) new (&sbv) SparseBitVector<>(pt.sbv); - else if (type == CBV) new (&cbv) CoreBitVector(pt.cbv); - else if (type == BV) new (&bv) BitVector(pt.bv); - else assert(false && "PointsTo::PointsTo&: unknown type"); -} - -PointsTo::PointsTo(PointsTo &&pt) -noexcept : type(pt.type), nodeMapping(std::move(pt.nodeMapping)), - reverseNodeMapping(std::move(pt.reverseNodeMapping)) -{ - if (type == SBV) new (&sbv) SparseBitVector<>(std::move(pt.sbv)); - else if (type == CBV) new (&cbv) CoreBitVector(std::move(pt.cbv)); - else if (type == BV) new (&bv) BitVector(std::move(pt.bv)); - else assert(false && "PointsTo::PointsTo&&: unknown type"); -} - -PointsTo::~PointsTo() -{ - if (type == SBV) sbv.~SparseBitVector<>(); - else if (type == CBV) cbv.~CoreBitVector(); - else if (type == BV) bv.~BitVector(); - else assert(false && "PointsTo::~PointsTo: unknown type"); - - nodeMapping = nullptr; - reverseNodeMapping = nullptr; -} - -PointsTo &PointsTo::operator=(const PointsTo &rhs) -{ - if (this == &rhs) - return *this; - this->type = rhs.type; - this->nodeMapping = rhs.nodeMapping; - this->reverseNodeMapping = rhs.reverseNodeMapping; - // Placement new because if type has changed, we have - // not constructed the new type yet. - if (type == SBV) new (&sbv) SparseBitVector<>(rhs.sbv); - else if (type == CBV) new (&cbv) CoreBitVector(rhs.cbv); - else if (type == BV) new (&bv) BitVector(rhs.bv); - else assert(false && "PointsTo::PointsTo=&: unknown type"); - - return *this; -} - -PointsTo &PointsTo::operator=(PointsTo &&rhs) -noexcept -{ - this->type = rhs.type; - this->nodeMapping = rhs.nodeMapping; - this->reverseNodeMapping = rhs.reverseNodeMapping; - // See comment in copy assignment. - if (type == SBV) new (&sbv) SparseBitVector<>(std::move(rhs.sbv)); - else if (type == CBV) new (&cbv) CoreBitVector(std::move(rhs.cbv)); - else if (type == BV) new (&bv) BitVector(std::move(rhs.bv)); - else assert(false && "PointsTo::PointsTo=&&: unknown type"); - - return *this; -} - -bool PointsTo::empty() const -{ - if (type == CBV) return cbv.empty(); - else if (type == SBV) return sbv.empty(); - else if (type == BV) return bv.empty(); - else - { - assert(false && "PointsTo::empty: unknown type"); - abort(); - } -} - -/// Returns number of elements. -u32_t PointsTo::count(void) const -{ - if (type == CBV) return cbv.count(); - else if (type == SBV) return sbv.count(); - else if (type == BV) return bv.count(); - else - { - assert(false && "PointsTo::count: unknown type"); - abort(); - } -} - -void PointsTo::clear() -{ - if (type == CBV) cbv.clear(); - else if (type == SBV) sbv.clear(); - else if (type == BV) bv.clear(); - else assert(false && "PointsTo::clear: unknown type"); -} - -bool PointsTo::test(u32_t n) const -{ - n = getInternalNode(n); - if (type == CBV) return cbv.test(n); - else if (type == SBV) return sbv.test(n); - else if (type == BV) return bv.test(n); - else - { - assert(false && "PointsTo::test: unknown type"); - abort(); - } -} - -bool PointsTo::test_and_set(u32_t n) -{ - n = getInternalNode(n); - if (type == CBV) return cbv.test_and_set(n); - else if (type == SBV) return sbv.test_and_set(n); - else if (type == BV) return bv.test_and_set(n); - else - { - assert(false && "PointsTo::test_and_set: unknown type"); - abort(); - } -} - -void PointsTo::set(u32_t n) -{ - n = getInternalNode(n); - if (type == CBV) cbv.set(n); - else if (type == SBV) sbv.set(n); - else if (type == BV) bv.set(n); - else assert(false && "PointsTo::set: unknown type"); -} - -void PointsTo::reset(u32_t n) -{ - n = getInternalNode(n); - if (type == CBV) cbv.reset(n); - else if (type == SBV) sbv.reset(n); - else if (type == BV) bv.reset(n); - else assert(false && "PointsTo::reset: unknown type"); -} - -bool PointsTo::contains(const PointsTo &rhs) const -{ - assert(metaSame(rhs) && "PointsTo::contains: mappings of operands do not match!"); - - if (type == CBV) return cbv.contains(rhs.cbv); - else if (type == SBV) return sbv.contains(rhs.sbv); - else if (type == BV) return bv.contains(rhs.bv); - else - { - assert(false && "PointsTo::contains: unknown type"); - abort(); - } -} - -bool PointsTo::intersects(const PointsTo &rhs) const -{ - assert(metaSame(rhs) && "PointsTo::intersects: mappings of operands do not match!"); - - if (type == CBV) return cbv.intersects(rhs.cbv); - else if (type == SBV) return sbv.intersects(rhs.sbv); - else if (type == BV) return bv.intersects(rhs.bv); - else - { - assert(false && "PointsTo::intersects: unknown type"); - abort(); - } -} - -int PointsTo::find_first() -{ - if (count() == 0) return -1; - return *begin(); -} - -bool PointsTo::operator==(const PointsTo &rhs) const -{ - assert(metaSame(rhs) && "PointsTo::==: mappings of operands do not match!"); - - if (type == CBV) return cbv == rhs.cbv; - else if (type == SBV) return sbv == rhs.sbv; - else if (type == BV) return bv == rhs.bv; - else - { - assert(false && "PointsTo::==: unknown type"); - abort(); - } -} - -bool PointsTo::operator!=(const PointsTo &rhs) const -{ - // TODO: we're asserting and checking twice... should be okay... - assert(metaSame(rhs) && "PointsTo::!=: mappings of operands do not match!"); - - return !(*this == rhs); -} - -bool PointsTo::operator|=(const PointsTo &rhs) -{ - assert(metaSame(rhs) && "PointsTo::|=: mappings of operands do not match!"); - - if (type == CBV) return cbv |= rhs.cbv; - else if (type == SBV) return sbv |= rhs.sbv; - else if (type == BV) return bv |= rhs.bv; - else - { - assert(false && "PointsTo::|=: unknown type"); - abort(); - } -} - -bool PointsTo::operator|=(const NodeBS &rhs) -{ - // TODO: - bool changed = false; - for (NodeID n : rhs) - { - if (changed) set(n); - else changed = test_and_set(n); - } - - return changed; -} - -bool PointsTo::operator&=(const PointsTo &rhs) -{ - assert(metaSame(rhs) && "PointsTo::&=: mappings of operands do not match!"); - - if (type == CBV) return cbv &= rhs.cbv; - else if (type == SBV) return sbv &= rhs.sbv; - else if (type == BV) return bv &= rhs.bv; - else - { - assert(false && "PointsTo::&=: unknown type"); - abort(); - } -} - -bool PointsTo::operator-=(const PointsTo &rhs) -{ - assert(metaSame(rhs) && "PointsTo::-=: mappings of operands do not match!"); - - if (type == CBV) return cbv.intersectWithComplement(rhs.cbv); - else if (type == SBV) return sbv.intersectWithComplement(rhs.sbv); - else if (type == BV) return bv.intersectWithComplement(rhs.bv); - else - { - assert(false && "PointsTo::-=: unknown type"); - abort(); - } -} - -bool PointsTo::intersectWithComplement(const PointsTo &rhs) -{ - assert(metaSame(rhs) && "PointsTo::intersectWithComplement: mappings of operands do not match!"); - - if (type == CBV) return cbv.intersectWithComplement(rhs.cbv); - else if (type == SBV) return sbv.intersectWithComplement(rhs.sbv); - else if (type == BV) return bv.intersectWithComplement(rhs.bv); - - assert(false && "PointsTo::intersectWithComplement(PT): unknown type"); - abort(); -} - -void PointsTo::intersectWithComplement(const PointsTo &lhs, const PointsTo &rhs) -{ - assert(metaSame(rhs) && "PointsTo::intersectWithComplement: mappings of operands do not match!"); - assert(metaSame(lhs) && "PointsTo::intersectWithComplement: mappings of operands do not match!"); - - if (type == CBV) cbv.intersectWithComplement(lhs.cbv, rhs.cbv); - else if (type == SBV) sbv.intersectWithComplement(lhs.sbv, rhs.sbv); - else if (type == BV) bv.intersectWithComplement(lhs.bv, rhs.bv); - else - { - assert(false && "PointsTo::intersectWithComplement(PT, PT): unknown type"); - abort(); - } -} - -NodeBS PointsTo::toNodeBS() const -{ - NodeBS nbs; - for (const NodeID o : *this) nbs.set(o); - return nbs; -} - -size_t PointsTo::hash() const -{ - if (type == CBV) return cbv.hash(); - else if (type == SBV) - { - std::hash> h; - return h(sbv); - } - else if (type == BV) return bv.hash(); - - else - { - assert(false && "PointsTo::hash: unknown type"); - abort(); - } -} - -PointsTo::MappingPtr PointsTo::getNodeMapping() const -{ - return nodeMapping; -} - -NodeID PointsTo::getInternalNode(NodeID n) const -{ - if (nodeMapping == nullptr) return n; - assert(n < nodeMapping->size()); - return nodeMapping->at(n); -} - -NodeID PointsTo::getExternalNode(NodeID n) const -{ - if (reverseNodeMapping == nullptr) return n; - assert(n < reverseNodeMapping->size()); - return reverseNodeMapping->at(n); -} - -bool PointsTo::metaSame(const PointsTo &pt) const -{ - return nodeMapping == pt.nodeMapping && reverseNodeMapping == pt.reverseNodeMapping; -} - -PointsTo::MappingPtr PointsTo::getCurrentBestNodeMapping() -{ - return currentBestNodeMapping; -} - -PointsTo::MappingPtr PointsTo::getCurrentBestReverseNodeMapping() -{ - return currentBestReverseNodeMapping; -} - -void PointsTo::setCurrentBestNodeMapping(MappingPtr newCurrentBestNodeMapping, - MappingPtr newCurrentBestReverseNodeMapping) -{ - currentBestNodeMapping = std::move(newCurrentBestNodeMapping); - currentBestReverseNodeMapping = std::move(newCurrentBestReverseNodeMapping); -} - -void PointsTo::checkAndRemap() -{ - if (nodeMapping != currentBestNodeMapping) - { - // newPt constructed with correct node mapping. - PointsTo newPt; - for (const NodeID o : *this) newPt.set(o); - *this = std::move(newPt); - } -} - -PointsTo::PointsToIterator::PointsToIterator(const PointsTo *pt, bool end) - : pt(pt) -{ - if (pt->type == Type::CBV) - { - new (&cbvIt) CoreBitVector::iterator(end ? pt->cbv.end() : pt->cbv.begin()); - } - else if (pt->type == Type::SBV) - { - new (&sbvIt) SparseBitVector<>::iterator(end ? pt->sbv.end() : pt->sbv.begin()); - } - else if (pt->type == Type::BV) - { - new (&bvIt) BitVector::iterator(end ? pt->bv.end() : pt->bv.begin()); - } - else - { - assert(false && "PointsToIterator::PointsToIterator: unknown type"); - abort(); - } -} - -PointsTo::PointsToIterator::PointsToIterator(const PointsToIterator &pt) - : pt(pt.pt) -{ - if (this->pt->type == PointsTo::Type::SBV) - { - new (&sbvIt) SparseBitVector<>::iterator(pt.sbvIt); - } - else if (this->pt->type == PointsTo::Type::CBV) - { - new (&cbvIt) CoreBitVector::iterator(pt.cbvIt); - } - else if (this->pt->type == PointsTo::Type::BV) - { - new (&bvIt) BitVector::iterator(pt.bvIt); - } - else - { - assert(false && "PointsToIterator::PointsToIterator&: unknown type"); - abort(); - } -} - -PointsTo::PointsToIterator::PointsToIterator(PointsToIterator &&pt) -noexcept : pt(pt.pt) -{ - if (this->pt->type == PointsTo::Type::SBV) - { - new (&sbvIt) SparseBitVector<>::iterator(std::move(pt.sbvIt)); - } - else if (this->pt->type == PointsTo::Type::CBV) - { - new (&cbvIt) CoreBitVector::iterator(std::move(pt.cbvIt)); - } - else if (this->pt->type == PointsTo::Type::BV) - { - new (&bvIt) BitVector::iterator(std::move(pt.bvIt)); - } - else - { - assert(false && "PointsToIterator::PointsToIterator&&: unknown type"); - abort(); - } -} - -PointsTo::PointsToIterator &PointsTo::PointsToIterator::operator=(const PointsToIterator &rhs) -{ - this->pt = rhs.pt; - - if (this->pt->type == PointsTo::Type::SBV) - { - new (&sbvIt) SparseBitVector<>::iterator(rhs.sbvIt); - } - else if (this->pt->type == PointsTo::Type::CBV) - { - new (&cbvIt) CoreBitVector::iterator(rhs.cbvIt); - } - else if (this->pt->type == PointsTo::Type::BV) - { - new (&bvIt) BitVector::iterator(rhs.bvIt); - } - else assert(false && "PointsToIterator::PointsToIterator&: unknown type"); - - return *this; -} - -PointsTo::PointsToIterator &PointsTo::PointsToIterator::operator=(PointsToIterator &&rhs) noexcept -{ - this->pt = rhs.pt; - - if (this->pt->type == PointsTo::Type::SBV) - { - new (&sbvIt) SparseBitVector<>::iterator(std::move(rhs.sbvIt)); - } - else if (this->pt->type == PointsTo::Type::CBV) - { - new (&cbvIt) CoreBitVector::iterator(std::move(rhs.cbvIt)); - } - else if (this->pt->type == PointsTo::Type::BV) - { - new (&bvIt) BitVector::iterator(std::move(rhs.bvIt)); - } - else assert(false && "PointsToIterator::PointsToIterator&&: unknown type"); - - return *this; -} - -const PointsTo::PointsToIterator &PointsTo::PointsToIterator::operator++() -{ - assert(!atEnd() && "PointsToIterator::++(pre): incrementing past end!"); - if (pt->type == Type::CBV) ++cbvIt; - else if (pt->type == Type::SBV) ++sbvIt; - else if (pt->type == Type::BV) ++bvIt; - else assert(false && "PointsToIterator::++(void): unknown type"); - - return *this; -} - -const PointsTo::PointsToIterator PointsTo::PointsToIterator::operator++(int) -{ - assert(!atEnd() && "PointsToIterator::++(pre): incrementing past end!"); - PointsToIterator old = *this; - ++*this; - return old; -} - -NodeID PointsTo::PointsToIterator::operator*() const -{ - assert(!atEnd() && "PointsToIterator: dereferencing end!"); - if (pt->type == Type::CBV) return pt->getExternalNode(*cbvIt); - else if (pt->type == Type::SBV) return pt->getExternalNode(*sbvIt); - else if (pt->type == Type::BV) return pt->getExternalNode(*bvIt); - else - { - assert(false && "PointsToIterator::*: unknown type"); - abort(); - } -} - -bool PointsTo::PointsToIterator::operator==(const PointsToIterator &rhs) const -{ - assert(pt == rhs.pt - && "PointsToIterator::==: comparing iterators from different PointsTos!"); - - // Handles end implicitly. - if (pt->type == Type::CBV) return cbvIt == rhs.cbvIt; - else if (pt->type == Type::SBV) return sbvIt == rhs.sbvIt; - else if (pt->type == Type::BV) return bvIt == rhs.bvIt; - else - { - assert(false && "PointsToIterator::==: unknown type"); - abort(); - } -} - -bool PointsTo::PointsToIterator::operator!=(const PointsToIterator &rhs) const -{ - assert(pt == rhs.pt - && "PointsToIterator::!=: comparing iterators from different PointsTos!"); - return !(*this == rhs); -} - -bool PointsTo::PointsToIterator::atEnd() const -{ - assert(pt != nullptr && "PointsToIterator::atEnd: iterator iterating over nothing!"); - if (pt->type == Type::CBV) return cbvIt == pt->cbv.end(); - else if (pt->type == Type::SBV) return sbvIt == pt->sbv.end(); - else if (pt->type == Type::BV) return bvIt == pt->bv.end(); - else - { - assert(false && "PointsToIterator::atEnd: unknown type"); - abort(); - } -} - -PointsTo operator|(const PointsTo &lhs, const PointsTo &rhs) -{ - // TODO: optimise. - PointsTo result = lhs; - result |= rhs; - return result; -} - -PointsTo operator&(const PointsTo &lhs, const PointsTo &rhs) -{ - // TODO: optimise. - PointsTo result = lhs; - result &= rhs; - return result; -} - -PointsTo operator-(const PointsTo &lhs, const PointsTo &rhs) -{ - // TODO: optimise. - PointsTo result = lhs; - result -= rhs; - return result; -} - -}; // namespace SVF diff --git a/svf/lib/SABER/DoubleFreeChecker.cpp b/svf/lib/SABER/DoubleFreeChecker.cpp index e16dddf18..8165db255 100644 --- a/svf/lib/SABER/DoubleFreeChecker.cpp +++ b/svf/lib/SABER/DoubleFreeChecker.cpp @@ -2,20 +2,20 @@ // // SVF: Static Value-Flow Analysis // -// Copyright (C) <2013-> +// Copyright (C) <2013-2017> // // This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU Affero General Public License as published by +// it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Affero General Public License for more details. +// GNU General Public License for more details. -// You should have received a copy of the GNU Affero General Public License +// You should have received a copy of the GNU General Public License // along with this program. If not, see . // //===----------------------------------------------------------------------===// @@ -28,8 +28,6 @@ */ #include "SABER/DoubleFreeChecker.h" -#include "Util/SVFUtil.h" -#include "Util/Options.h" using namespace SVF; using namespace SVFUtil; @@ -39,118 +37,12 @@ void DoubleFreeChecker::reportBug(ProgSlice* slice) if(slice->isSatisfiableForPairs() == false) { - GenericBug::EventStack eventStack; - slice->evalFinalCond2Event(eventStack); - eventStack.push_back( - SVFBugEvent(SVFBugEvent::SourceInst, getSrcCSID(slice->getSource())->getCallSite())); - report.addSaberBug(GenericBug::DOUBLEFREE, eventStack); - } - if(Options::ValidateTests()) - testsValidation(slice); -} - - - -void DoubleFreeChecker::testsValidation(ProgSlice *slice) -{ - const SVFGNode* source = slice->getSource(); - const CallICFGNode* cs = getSrcCSID(source); - const SVFFunction* fun = getCallee(cs->getCallSite()); - if(fun==nullptr) - return; - validateSuccessTests(slice,fun); - validateExpectedFailureTests(slice,fun); -} - -void DoubleFreeChecker::validateSuccessTests(ProgSlice *slice, const SVFFunction *fun) -{ - const SVFGNode* source = slice->getSource(); - const CallICFGNode* cs = getSrcCSID(source); - - bool success = false; - - if(fun->getName() == "SAFEMALLOC") - { - if(slice->isSatisfiableForPairs() == true) - success = true; - } - else if(fun->getName() == "DOUBLEFREEMALLOC") - { - if(slice->isSatisfiableForPairs() == false) - success = true; - } - else if(fun->getName() == "DOUBLEFREEMALLOCFN" || fun->getName() == "SAFEMALLOCFP") - { - return; - } - else - { - writeWrnMsg("\t can not validate, check function not found, please put it at the right place!!"); - return; - } - - std::string funName = source->getFun()->getName(); - - if (success) - { - outs() << sucMsg("\t SUCCESS :") << funName << " check getId() - << ", cs id:" << getSrcCSID(source)->getCallSite()->toString() << "> at (" - << cs->getCallSite()->getSourceLoc() << ")\n"; - outs() << "\t\t double free path: \n" << slice->evalFinalCond() << "\n"; - } - else - { - SVFUtil::errs() << errMsg("\t FAILURE :") << funName << " check getId() - << ", cs id:" <getCallSite()->toString() << "> at (" - << cs->getCallSite()->getSourceLoc() << ")\n"; + const SVFGNode* src = slice->getSource(); + const CallBlockNode* cs = getSrcCSID(src); + SVFUtil::errs() << bugMsg2("\t Double Free :") << " memory allocation at : (" + << getSourceLoc(cs->getCallSite()) << ")\n"; SVFUtil::errs() << "\t\t double free path: \n" << slice->evalFinalCond() << "\n"; - assert(false && "test case failed!"); + slice->annotatePaths(); } } -void DoubleFreeChecker::validateExpectedFailureTests(ProgSlice *slice, const SVFFunction *fun) -{ - const SVFGNode* source = slice->getSource(); - const CallICFGNode* cs = getSrcCSID(source); - - bool expectedFailure = false; - /// output safe but should be double free - if(fun->getName() == "DOUBLEFREEMALLOCFN") - { - if(slice->isSatisfiableForPairs() == true) - expectedFailure = true; - } /// output double free but should be safe - else if(fun->getName() == "SAFEMALLOCFP") - { - if(slice->isSatisfiableForPairs() == false) - expectedFailure = true; - } - else if(fun->getName() == "SAFEMALLOC" || fun->getName() == "DOUBLEFREEMALLOC") - { - return; - } - else - { - writeWrnMsg("\t can not validate, check function not found, please put it at the right place!!"); - return; - } - - std::string funName = source->getFun()->getName(); - - if (expectedFailure) - { - outs() << sucMsg("\t EXPECTED-FAILURE :") << funName << " check getId() - << ", cs id:" << getSrcCSID(source)->getCallSite()->toString() << "> at (" - << cs->getCallSite()->getSourceLoc() << ")\n"; - outs() << "\t\t double free path: \n" << slice->evalFinalCond() << "\n"; - } - else - { - SVFUtil::errs() << errMsg("\t UNEXPECTED FAILURE :") << funName - << " check getId() - << ", cs id:" << getSrcCSID(source)->getCallSite()->toString() << "> at (" - << cs->getCallSite()->getSourceLoc() << ")\n"; - SVFUtil::errs() << "\t\t double free path: \n" << slice->evalFinalCond() << "\n"; - assert(false && "test case failed!"); - } -} diff --git a/svf/lib/SABER/FileChecker.cpp b/svf/lib/SABER/FileChecker.cpp index e5d03d48a..c53c72984 100644 --- a/svf/lib/SABER/FileChecker.cpp +++ b/svf/lib/SABER/FileChecker.cpp @@ -2,20 +2,20 @@ // // SVF: Static Value-Flow Analysis // -// Copyright (C) <2013-> +// Copyright (C) <2013-2017> // // This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU Affero General Public License as published by +// it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Affero General Public License for more details. +// GNU General Public License for more details. -// You should have received a copy of the GNU Affero General Public License +// You should have received a copy of the GNU General Public License // along with this program. If not, see . // //===----------------------------------------------------------------------===// @@ -33,24 +33,32 @@ using namespace SVF; using namespace SVFUtil; +void FileChecker::reportNeverClose(const SVFGNode* src) +{ + const CallBlockNode* cs = getSrcCSID(src); + SVFUtil::errs() << bugMsg1("\t FileNeverClose :") << " file open location at : (" + << getSourceLoc(cs->getCallSite()) << ")\n"; +} + +void FileChecker::reportPartialClose(const SVFGNode* src) +{ + const CallBlockNode* cs = getSrcCSID(src); + SVFUtil::errs() << bugMsg2("\t PartialFileClose :") << " file open location at : (" + << getSourceLoc(cs->getCallSite()) << ")\n"; +} + void FileChecker::reportBug(ProgSlice* slice) { if(isAllPathReachable() == false && isSomePathReachable() == false) { - // full leakage - GenericBug::EventStack eventStack = - { - SVFBugEvent(SVFBugEvent::SourceInst, getSrcCSID(slice->getSource())->getCallSite()) - }; - report.addSaberBug(GenericBug::FILENEVERCLOSE, eventStack); + reportNeverClose(slice->getSource()); } else if (isAllPathReachable() == false && isSomePathReachable() == true) { - GenericBug::EventStack eventStack; - slice->evalFinalCond2Event(eventStack); - eventStack.push_back( - SVFBugEvent(SVFBugEvent::SourceInst, getSrcCSID(slice->getSource())->getCallSite())); - report.addSaberBug(GenericBug::FILEPARTIALCLOSE, eventStack); + reportPartialClose(slice->getSource()); + SVFUtil::errs() << "\t\t conditional file close path: \n" << slice->evalFinalCond() << "\n"; + slice->annotatePaths(); } + } diff --git a/svf/lib/SABER/LeakChecker.cpp b/svf/lib/SABER/LeakChecker.cpp index cc4fcc1ab..700bd758a 100644 --- a/svf/lib/SABER/LeakChecker.cpp +++ b/svf/lib/SABER/LeakChecker.cpp @@ -2,20 +2,20 @@ // // SVF: Static Value-Flow Analysis // -// Copyright (C) <2013-> +// Copyright (C) <2013-2017> // // This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU Affero General Public License as published by +// it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Affero General Public License for more details. +// GNU General Public License for more details. -// You should have received a copy of the GNU Affero General Public License +// You should have received a copy of the GNU General Public License // along with this program. If not, see . // //===----------------------------------------------------------------------===// @@ -28,6 +28,7 @@ */ #include "Util/Options.h" +#include "SVF-FE/LLVMUtil.h" #include "SABER/LeakChecker.h" using namespace SVF; @@ -40,20 +41,19 @@ using namespace SVFUtil; void LeakChecker::initSrcs() { - SVFIR* pag = getPAG(); + PAG* pag = getPAG(); ICFG* icfg = pag->getICFG(); - for(SVFIR::CSToRetMap::iterator it = pag->getCallSiteRets().begin(), + for(PAG::CSToRetMap::iterator it = pag->getCallSiteRets().begin(), eit = pag->getCallSiteRets().end(); it!=eit; ++it) { - const RetICFGNode* cs = it->first; + const RetBlockNode* cs = it->first; /// if this callsite return reside in a dead function then we do not care about its leaks - /// for example instruction `int* p = malloc(size)` is in a dead function, then program won't allocate this memory - /// for example a customized malloc `int p = malloc()` returns an integer value, then program treat it as a system malloc - if(cs->getCallSite()->ptrInUncalledFunction() || !cs->getCallSite()->getType()->isPointerTy()) + /// for example instruction p = malloc is in a dead function, then program won't allocate this memory + if(isPtrInDeadFunction(cs->getCallSite())) continue; PTACallGraph::FunctionSet callees; - getCallgraph()->getCallees(cs->getCallICFGNode(),callees); + getCallgraph()->getCallees(cs->getCallBlockNode(),callees); for(PTACallGraph::FunctionSet::const_iterator cit = callees.begin(), ecit = callees.end(); cit!=ecit; cit++) { const SVFFunction* fun = *cit; @@ -61,11 +61,11 @@ void LeakChecker::initSrcs() { CSWorkList worklist; SVFGNodeBS visited; - worklist.push(it->first->getCallICFGNode()); + worklist.push(it->first->getCallBlockNode()); while (!worklist.empty()) { - const CallICFGNode* cs = worklist.pop(); - const RetICFGNode* retBlockNode = icfg->getRetICFGNode(cs->getCallSite()); + const CallBlockNode* cs = worklist.pop(); + const RetBlockNode* retBlockNode = icfg->getRetBlockNode(cs->getCallSite()); const PAGNode* pagNode = pag->getCallSiteRet(retBlockNode); const SVFGNode* node = getSVFG()->getDefSVFGNode(pagNode); if (visited.test(node->getId()) == 0) @@ -86,8 +86,8 @@ void LeakChecker::initSrcs() // otherwise, this is the source we are interested else { - // exclude sources in dead functions or sources in functions that have summary - if (!cs->getCallSite()->ptrInUncalledFunction() && !isExtCall(cs->getCallSite()->getParent()->getParent())) + // exclude sources in dead functions + if (isPtrInDeadFunction(cs->getCallSite()) == false) { addToSources(node); addSrcToCSID(node, cs); @@ -106,9 +106,9 @@ void LeakChecker::initSrcs() void LeakChecker::initSnks() { - SVFIR* pag = getPAG(); + PAG* pag = getPAG(); - for(SVFIR::CSToArgsListMap::iterator it = pag->getCallSiteArgsMap().begin(), + for(PAG::CSToArgsListMap::iterator it = pag->getCallSiteArgsMap().begin(), eit = pag->getCallSiteArgsMap().end(); it!=eit; ++it) { @@ -117,58 +117,53 @@ void LeakChecker::initSnks() for(PTACallGraph::FunctionSet::const_iterator cit = callees.begin(), ecit = callees.end(); cit!=ecit; cit++) { const SVFFunction* fun = *cit; - if (isSinkLikeFun(fun)) - { - SVFIR::SVFVarList &arglist = it->second; - assert(!arglist.empty() && "no actual parameter at deallocation site?"); - /// we only choose pointer parameters among all the actual parameters - for (SVFIR::SVFVarList::const_iterator ait = arglist.begin(), - aeit = arglist.end(); ait != aeit; ++ait) - { - const PAGNode *pagNode = *ait; - if (pagNode->isPointer()) - { - const SVFGNode *snk = getSVFG()->getActualParmVFGNode(pagNode, it->first); - addToSinks(snk); - - // For any multi-level pointer e.g., XFree(void** pagNode) that passed into a ExtAPI::EFT_FREE_MULTILEVEL function (e.g., XFree), - // we will add the DstNode of a load edge, i.e., dummy = *pagNode - SVFStmt::SVFStmtSetTy& loads = const_cast(pagNode)->getOutgoingEdges(SVFStmt::Load); - for(const SVFStmt* ld : loads) - { - if(SVFUtil::isa(ld->getDstNode())) - addToSinks(getSVFG()->getStmtVFGNode(ld)); - } - } - } - } + if (isSinkLikeFun(fun)) { + PAG::PAGNodeList &arglist = it->second; + assert(!arglist.empty() && "no actual parameter at deallocation site?"); + /// we only choose pointer parameters among all the actual parameters + for (PAG::PAGNodeList::const_iterator ait = arglist.begin(), + aeit = arglist.end(); ait != aeit; ++ait) { + const PAGNode *pagNode = *ait; + if (pagNode->isPointer()) { + const SVFGNode *snk = getSVFG()->getActualParmVFGNode(pagNode, it->first); + addToSinks(snk); + } + } + } } } } + +void LeakChecker::reportNeverFree(const SVFGNode* src) +{ + const CallBlockNode* cs = getSrcCSID(src); + SVFUtil::errs() << bugMsg1("\t NeverFree :") << " memory allocation at : (" + << getSourceLoc(cs->getCallSite()) << ")\n"; +} + +void LeakChecker::reportPartialLeak(const SVFGNode* src) +{ + const CallBlockNode* cs = getSrcCSID(src); + SVFUtil::errs() << bugMsg2("\t PartialLeak :") << " memory allocation at : (" + << getSourceLoc(cs->getCallSite()) << ")\n"; +} + void LeakChecker::reportBug(ProgSlice* slice) { if(isAllPathReachable() == false && isSomePathReachable() == false) { - // full leakage - GenericBug::EventStack eventStack = - { - SVFBugEvent(SVFBugEvent::SourceInst, getSrcCSID(slice->getSource())->getCallSite()) - }; - report.addSaberBug(GenericBug::NEVERFREE, eventStack); + reportNeverFree(slice->getSource()); } else if (isAllPathReachable() == false && isSomePathReachable() == true) { - // partial leakage - GenericBug::EventStack eventStack; - slice->evalFinalCond2Event(eventStack); - eventStack.push_back( - SVFBugEvent(SVFBugEvent::SourceInst, getSrcCSID(slice->getSource())->getCallSite())); - report.addSaberBug(GenericBug::PARTIALLEAK, eventStack); + reportPartialLeak(slice->getSource()); + SVFUtil::errs() << "\t\t conditional free path: \n" << slice->evalFinalCond() << "\n"; + slice->annotatePaths(); } - if(Options::ValidateTests()) + if(Options::ValidateTests) testsValidation(slice); } @@ -179,7 +174,7 @@ void LeakChecker::reportBug(ProgSlice* slice) void LeakChecker::testsValidation(const ProgSlice* slice) { const SVFGNode* source = slice->getSource(); - const CallICFGNode* cs = getSrcCSID(source); + const CallBlockNode* cs = getSrcCSID(source); const SVFFunction* fun = getCallee(cs->getCallSite()); if(fun==nullptr) return; @@ -192,7 +187,7 @@ void LeakChecker::testsValidation(const ProgSlice* slice) void LeakChecker::validateSuccessTests(const SVFGNode* source, const SVFFunction* fun) { - const CallICFGNode* cs = getSrcCSID(source); + const CallBlockNode* cs = getSrcCSID(source); bool success = false; @@ -227,19 +222,17 @@ void LeakChecker::validateSuccessTests(const SVFGNode* source, const SVFFunction return; } - std::string funName = source->getFun()->getName(); + std::string funName = source->getFun()->getName().str(); if (success) - { outs() << sucMsg("\t SUCCESS :") << funName << " check getId() - << ", cs id:" << getSrcCSID(source)->getCallSite()->toString() << "> at (" - << cs->getCallSite()->getSourceLoc() << ")\n"; - } + << ", cs id:" << *getSrcCSID(source)->getCallSite() << "> at (" + << getSourceLoc(cs->getCallSite()) << ")\n"; else { SVFUtil::errs() << errMsg("\t FAILURE :") << funName << " check getId() - << ", cs id:" << getSrcCSID(source)->getCallSite()->toString() << "> at (" - << cs->getCallSite()->getSourceLoc() << ")\n"; + << ", cs id:" << *getSrcCSID(source)->getCallSite() << "> at (" + << getSourceLoc(cs->getCallSite()) << ")\n"; assert(false && "test case failed!"); } } @@ -247,7 +240,7 @@ void LeakChecker::validateSuccessTests(const SVFGNode* source, const SVFFunction void LeakChecker::validateExpectedFailureTests(const SVFGNode* source, const SVFFunction* fun) { - const CallICFGNode* cs = getSrcCSID(source); + const CallBlockNode* cs = getSrcCSID(source); bool expectedFailure = false; @@ -277,20 +270,18 @@ void LeakChecker::validateExpectedFailureTests(const SVFGNode* source, const SVF return; } - std::string funName = source->getFun()->getName(); + std::string funName = source->getFun()->getName().str(); if (expectedFailure) - { outs() << sucMsg("\t EXPECTED-FAILURE :") << funName << " check getId() - << ", cs id:" << getSrcCSID(source)->getCallSite()->toString() << "> at (" - << cs->getCallSite()->getSourceLoc() << ")\n"; - } + << ", cs id:" << *getSrcCSID(source)->getCallSite() << "> at (" + << getSourceLoc(cs->getCallSite()) << ")\n"; else { SVFUtil::errs() << errMsg("\t UNEXPECTED FAILURE :") << funName << " check getId() - << ", cs id:" << getSrcCSID(source)->getCallSite()->toString() << "> at (" - << cs->getCallSite()->getSourceLoc() << ")\n"; + << ", cs id:" << *getSrcCSID(source)->getCallSite() << "> at (" + << getSourceLoc(cs->getCallSite()) << ")\n"; assert(false && "test case failed!"); } } diff --git a/svf/lib/SABER/ProgSlice.cpp b/svf/lib/SABER/ProgSlice.cpp index 7ecf3a159..8210336bc 100644 --- a/svf/lib/SABER/ProgSlice.cpp +++ b/svf/lib/SABER/ProgSlice.cpp @@ -2,20 +2,20 @@ // // SVF: Static Value-Flow Analysis // -// Copyright (C) <2013-> +// Copyright (C) <2013-2017> // // This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU Affero General Public License as published by +// it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Affero General Public License for more details. +// GNU General Public License for more details. -// You should have received a copy of the GNU Affero General Public License +// You should have received a copy of the GNU General Public License // along with this program. If not, see . // //===----------------------------------------------------------------------===// @@ -28,6 +28,7 @@ */ #include "SABER/ProgSlice.h" +#include "SABER/SaberAnnotator.h" using namespace SVF; using namespace SVFUtil; @@ -52,18 +53,16 @@ bool ProgSlice::AllPathReachableSolve() { const SVFGNode* node = worklist.pop(); setCurSVFGNode(node); - - Condition invalidCond = computeInvalidCondFromRemovedSUVFEdge(node); - Condition cond = getVFCond(node); + Condition* cond = getVFCond(node); for(SVFGNode::const_iterator it = node->OutEdgeBegin(), eit = node->OutEdgeEnd(); it!=eit; ++it) { const SVFGEdge* edge = (*it); const SVFGNode* succ = edge->getDstNode(); if(inBackwardSlice(succ)) { - Condition vfCond; - const SVFBasicBlock* nodeBB = getSVFGNodeBB(node); - const SVFBasicBlock* succBB = getSVFGNodeBB(succ); + Condition* vfCond = nullptr; + const BasicBlock* nodeBB = getSVFGNodeBB(node); + const BasicBlock* succBB = getSVFGNodeBB(succ); /// clean up the control flow conditions for next round guard computation clearCFCond(); @@ -77,8 +76,8 @@ bool ProgSlice::AllPathReachableSolve() } else vfCond = ComputeIntraVFGGuard(nodeBB,succBB); - vfCond = condAnd(vfCond, condNeg(invalidCond)); - Condition succPathCond = condAnd(cond, vfCond); + + Condition* succPathCond = condAnd(cond, vfCond); if(setVFCond(succ, condOr(getVFCond(succ), succPathCond) )) worklist.push(succ); } @@ -91,58 +90,13 @@ bool ProgSlice::AllPathReachableSolve() return isSatisfiableForAll(); } -/*! - * Compute invalid branch condition stemming from removed strong update value-flow edges - * - * Fix issue: https://github.com/SVF-tools/SVF/issues/1306 - * Line 11->13 is removed due to a strong update at Line 13, which means Line 11 is unreachable to Line 13 on the value flow graph. - * However on the control flow graph they are still considered as reachable, - * making the vf guard on Line 11 -> Line 15 a true condition (should consider the infeasible branch Line 11 -> Line 13) - * Therefore, we collect this infeasible branch condition (condition on Line 11 -> Line 13, `a == b`) as an invalid condition (invalidCond), - * and add the negation of invalidCond when computing value flow guard starting from the source of the SU. - * In this example, we add `a != b` on Line 11 -> Line 15. - * - * @param cur current SVFG node - * @return invalid branch condition - */ -ProgSlice::Condition ProgSlice::computeInvalidCondFromRemovedSUVFEdge(const SVFGNode * cur) -{ - Set validOutBBs; // the BBs of valid successors - for(SVFGNode::const_iterator it = cur->OutEdgeBegin(), eit = cur->OutEdgeEnd(); it!=eit; ++it) - { - const SVFGEdge* edge = (*it); - const SVFGNode* succ = edge->getDstNode(); - if(inBackwardSlice(succ)) - { - validOutBBs.insert(getSVFGNodeBB(succ)); - } - } - Condition invalidCond = getFalseCond(); - auto suVFEdgesIt = getRemovedSUVFEdges().find(cur); - if (suVFEdgesIt != getRemovedSUVFEdges().end()) - { - for (const auto &succ: suVFEdgesIt->second) - { - if (!validOutBBs.count(getSVFGNodeBB(succ))) - { - // removed vfg node does not reside in the BBs of valid successors - const SVFBasicBlock *nodeBB = getSVFGNodeBB(cur); - const SVFBasicBlock *succBB = getSVFGNodeBB(succ); - clearCFCond(); - invalidCond = condOr(invalidCond, ComputeIntraVFGGuard(nodeBB, succBB)); - } - } - } - return invalidCond; -} - /*! * Solve by computing disjunction of conditions from all sinks (e.g., memory leak) */ bool ProgSlice::isSatisfiableForAll() { - Condition guard = getFalseCond(); + Condition* guard = getFalseCond(); for(SVFGNodeSetIter it = sinksBegin(), eit = sinksEnd(); it!=eit; ++it) { guard = condOr(guard,getVFCond(*it)); @@ -164,8 +118,8 @@ bool ProgSlice::isSatisfiableForPairs() { if(*it == *sit) continue; - Condition guard = condAnd(getVFCond(*sit),getVFCond(*it)); - if(!isEquivalentBranchCond(guard, getFalseCond())) + Condition* guard = condAnd(getVFCond(*sit),getVFCond(*it)); + if(guard != getFalseCond()) { setFinalCond(guard); return false; @@ -176,7 +130,7 @@ bool ProgSlice::isSatisfiableForPairs() return true; } -const CallICFGNode* ProgSlice::getCallSite(const SVFGEdge* edge) const +const CallBlockNode* ProgSlice::getCallSite(const SVFGEdge* edge) const { assert(edge->isCallVFGEdge() && "not a call svfg edge?"); if(const CallDirSVFGEdge* callEdge = SVFUtil::dyn_cast(edge)) @@ -184,7 +138,7 @@ const CallICFGNode* ProgSlice::getCallSite(const SVFGEdge* edge) const else return getSVFG()->getCallSite(SVFUtil::cast(edge)->getCallSiteId()); } -const CallICFGNode* ProgSlice::getRetSite(const SVFGEdge* edge) const +const CallBlockNode* ProgSlice::getRetSite(const SVFGEdge* edge) const { assert(edge->isRetVFGEdge() && "not a return svfg edge?"); if(const RetDirSVFGEdge* callEdge = SVFUtil::dyn_cast(edge)) @@ -193,26 +147,49 @@ const CallICFGNode* ProgSlice::getRetSite(const SVFGEdge* edge) const return getSVFG()->getCallSite(SVFUtil::cast(edge)->getCallSiteId()); } -void ProgSlice::evalFinalCond2Event(GenericBug::EventStack &eventStack) const +/*! + * Return llvm value for addr/copy/gep/load/phi/actualParam/formalParam/actualRet/formalRet + * but not for store/mssaphi/actualIn/acutalOut/formalIn/formalOut + */ +const Value* ProgSlice::getLLVMValue(const SVFGNode* node) const { - NodeBS elems = pathAllocator->exactCondElem(finalCond); - for(NodeBS::iterator it = elems.begin(), eit = elems.end(); it!=eit; ++it) + if(const StmtSVFGNode* stmt = SVFUtil::dyn_cast(node)) + { + if(SVFUtil::isa(stmt) == false) + { + if(stmt->getPAGDstNode()->hasValue()) + return stmt->getPAGDstNode()->getValue(); + } + } + else if(const PHISVFGNode* phi = SVFUtil::dyn_cast(node)) { - const SVFInstruction* tinst = pathAllocator->getCondInst(*it); - if(pathAllocator->isNegCond(*it)) - eventStack.push_back(SVFBugEvent( - SVFBugEvent::Branch|((((u32_t)false) << 4) & BRANCHFLAGMASK), tinst)); - else - eventStack.push_back(SVFBugEvent( - SVFBugEvent::Branch|((((u32_t)true) << 4) & BRANCHFLAGMASK), tinst)); + return phi->getRes()->getValue(); } + else if(const ActualParmSVFGNode* ap = SVFUtil::dyn_cast(node)) + { + return ap->getParam()->getValue(); + } + else if(const FormalParmSVFGNode* fp = SVFUtil::dyn_cast(node)) + { + return fp->getParam()->getValue(); + } + else if(const ActualRetSVFGNode* ar = SVFUtil::dyn_cast(node)) + { + return ar->getRev()->getValue(); + } + else if(const FormalRetSVFGNode* fr = SVFUtil::dyn_cast(node)) + { + return fr->getRet()->getValue(); + } + + return nullptr; } /*! * Evaluate Atoms of a condition * TODO: for now we only evaluate one path, evaluate every single path * - * Atom -- a propositional variable: a, b, c + * Atom -- a propositional valirable: a, b, c * Literal -- an atom or its negation: a, ~a * Clause -- A disjunction of some literals: a \vee b * CNF formula -- a conjunction of some clauses: (a \vee b ) \wedge (c \vee d) @@ -220,19 +197,15 @@ void ProgSlice::evalFinalCond2Event(GenericBug::EventStack &eventStack) const std::string ProgSlice::evalFinalCond() const { std::string str; - std::stringstream rawstr(str); - Set locations; + raw_string_ostream rawstr(str); NodeBS elems = pathAllocator->exactCondElem(finalCond); - + Set locations; for(NodeBS::iterator it = elems.begin(), eit = elems.end(); it!=eit; ++it) { - const SVFInstruction* tinst = pathAllocator->getCondInst(*it); - if(pathAllocator->isNegCond(*it)) - locations.insert(tinst->getSourceLoc()+"|False"); - else - locations.insert(tinst->getSourceLoc()+"|True"); + Condition* atom = pathAllocator->getCond(*it); + const Instruction* tinst = pathAllocator->getCondInst(atom); + locations.insert(getSourceLoc(tinst)); } - /// print leak path after eliminating duplicated element for(Set::iterator iter = locations.begin(), eiter = locations.end(); iter!=eiter; ++iter) @@ -243,6 +216,37 @@ std::string ProgSlice::evalFinalCond() const return rawstr.str(); } +/*! + * Annotate program paths according to the final path condition computed + */ +void ProgSlice::annotatePaths() +{ + + SaberAnnotator annotator(this); + annotator.annotateSource(); + annotator.annotateSinks(); + + NodeBS elems = pathAllocator->exactCondElem(finalCond); + for(NodeBS::iterator it = elems.begin(), eit = elems.end(); it!=eit; ++it) + { + Condition* atom = pathAllocator->getCond(*it); + const Instruction* tinst = pathAllocator->getCondInst(atom); + if(const BranchInst* br = SVFUtil::dyn_cast(tinst)) + { + annotator.annotateFeasibleBranch(br,0); + annotator.annotateFeasibleBranch(br,1); + } + } +} + + void ProgSlice::destroy() { + /// TODO: how to clean bdd memory +// for(SVFGNodeToCondMap::const_iterator it = svfgNodeToCondMap.begin(), eit = svfgNodeToCondMap.end(); it!=eit; ++it){ +// pathAllocator->markForRelease(it->second); +// } +// for(BBToCondMap::const_iterator it = bbToCondMap.begin(), eit = bbToCondMap.end(); it!=eit; ++it){ +// pathAllocator->markForRelease(it->second); +// } } diff --git a/svf/lib/SABER/SaberAnnotator.cpp b/svf/lib/SABER/SaberAnnotator.cpp new file mode 100644 index 000000000..c2dc50055 --- /dev/null +++ b/svf/lib/SABER/SaberAnnotator.cpp @@ -0,0 +1,116 @@ +//===- SaberAnnotator.cpp -- Annotation---------------------------------------// +// +// SVF: Static Value-Flow Analysis +// +// Copyright (C) <2013-2017> +// + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// +//===----------------------------------------------------------------------===// + +/* + * SaberAnnotator.cpp + * + * Created on: May 4, 2014 + * Author: Yulei Sui + */ + +#include "SABER/SaberAnnotator.h" +#include "SABER/ProgSlice.h" + +using namespace SVF; + +/*! + * + */ +void SaberAnnotator::annotateSource() +{ + std::string str; + raw_string_ostream rawstr(str); + rawstr << SB_SLICESOURCE ; //<< _curSlice->getSource()->getId(); + if(const Instruction* sourceinst = SVFUtil::dyn_cast(_curSlice->getLLVMValue(_curSlice->getSource()))) + { + addMDTag(const_cast(sourceinst),rawstr.str()); + } + else + assert(false && "instruction of source node not found"); + +} + +/*! + * + */ +void SaberAnnotator::annotateSinks() +{ + for(ProgSlice::SVFGNodeSet::const_iterator it = _curSlice->getSinks().begin(), + eit = _curSlice->getSinks().end(); it!=eit; ++it) + { + if(const ActualParmSVFGNode* ap = SVFUtil::dyn_cast(*it)) + { + const Instruction* sinkinst = ap->getCallSite()->getCallSite(); + assert(SVFUtil::isa(sinkinst) && "not a call instruction?"); + const CallInst* sink = SVFUtil::cast(sinkinst); + std::string str; + raw_string_ostream rawstr(str); + rawstr << SB_SLICESINK << _curSlice->getSource()->getId(); + addMDTag(const_cast(sink),sink->getArgOperand(0),rawstr.str()); + } + else + assert(false && "sink node is not a actual parameter?"); + } +} + +/*! + * Annotate branch instruction and its corresponding feasible path + */ +void SaberAnnotator::annotateFeasibleBranch(const BranchInst *brInst, u32_t succPos) +{ + + assert((succPos == 0 || succPos == 1) && "branch instruction should only have two successors"); + + std::string str; + raw_string_ostream rawstr(str); + rawstr << SB_FESIBLE << _curSlice->getSource()->getId(); + BranchInst* br = const_cast(brInst); + addMDTag(br,br->getCondition(),rawstr.str()); +} + +/*! + * Annotate branch instruction and its corresponding infeasible path + */ +void SaberAnnotator::annotateInfeasibleBranch(const BranchInst *brInst, u32_t succPos) +{ + + assert((succPos == 0 || succPos == 1) && "branch instruction should only have two successors"); + + std::string str; + raw_string_ostream rawstr(str); + rawstr << SB_INFESIBLE << _curSlice->getSource()->getId(); + BranchInst* br = const_cast(brInst); + addMDTag(br,br->getCondition(),rawstr.str()); +} + + +/*! + * Annotate switch instruction and its corresponding feasible path + */ +void SaberAnnotator::annotateSwitch(SwitchInst *switchInst, u32_t succPos) +{ + assert(succPos < switchInst->getNumSuccessors() && "successor position not correct!"); +} + + + + diff --git a/svf/lib/SABER/SaberCheckerAPI.cpp b/svf/lib/SABER/SaberCheckerAPI.cpp index 9baab29dc..c8f3546b7 100644 --- a/svf/lib/SABER/SaberCheckerAPI.cpp +++ b/svf/lib/SABER/SaberCheckerAPI.cpp @@ -2,20 +2,20 @@ // // SVF: Static Value-Flow Analysis // -// Copyright (C) <2013-> +// Copyright (C) <2013-2017> // // This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU Affero General Public License as published by +// it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Affero General Public License for more details. +// GNU General Public License for more details. -// You should have received a copy of the GNU Affero General Public License +// You should have received a copy of the GNU General Public License // along with this program. If not, see . // //===----------------------------------------------------------------------===// @@ -34,8 +34,7 @@ using namespace SVF; SaberCheckerAPI* SaberCheckerAPI::ckAPI = nullptr; -namespace -{ +namespace { /// string and type pair struct ei_pair @@ -76,9 +75,9 @@ static const ei_pair ei_pairs[]= {"xmalloc", SaberCheckerAPI::CK_ALLOC}, {"SSL_CTX_new", SaberCheckerAPI::CK_ALLOC}, {"SSL_new", SaberCheckerAPI::CK_ALLOC}, - {"VOS_MemAlloc", SaberCheckerAPI::CK_ALLOC}, + {"VOS_MemAlloc", SaberCheckerAPI::CK_ALLOC}, - {"VOS_MemFree", SaberCheckerAPI::CK_FREE}, + {"VOS_MemFree", SaberCheckerAPI::CK_FREE}, {"cfree", SaberCheckerAPI::CK_FREE}, {"free", SaberCheckerAPI::CK_FREE}, {"free_all_mem", SaberCheckerAPI::CK_FREE}, @@ -97,7 +96,6 @@ static const ei_pair ei_pairs[]= {"xfree", SaberCheckerAPI::CK_FREE}, {"SSL_CTX_free", SaberCheckerAPI::CK_FREE}, {"SSL_free", SaberCheckerAPI::CK_FREE}, - {"XFree", SaberCheckerAPI::CK_FREE}, {"fopen", SaberCheckerAPI::CK_FOPEN}, {"\01_fopen", SaberCheckerAPI::CK_FOPEN}, diff --git a/svf/lib/SABER/SaberCondAllocator.cpp b/svf/lib/SABER/SaberCondAllocator.cpp deleted file mode 100644 index b44fea9d7..000000000 --- a/svf/lib/SABER/SaberCondAllocator.cpp +++ /dev/null @@ -1,649 +0,0 @@ -//===- PathAllocator.cpp -- Path condition analysis---------------------------// -// -// SVF: Static Value-Flow Analysis -// -// Copyright (C) <2013-> -// - -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU Affero General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Affero General Public License for more details. - -// You should have received a copy of the GNU Affero General Public License -// along with this program. If not, see . -// -//===----------------------------------------------------------------------===// - - -/* - * PathAllocator.cpp - * - * Created on: Apr 3, 2014 - * Author: Yulei Sui - */ - -#include "Util/Options.h" -#include "SABER/SaberCondAllocator.h" -#include "Util/DPItem.h" -#include "Graphs/SVFG.h" -#include -#include -#include "SVFIR/SVFStatements.h" - -using namespace SVF; -using namespace SVFUtil; - -u64_t DPItem::maximumBudget = ULONG_MAX - 1; -u32_t ContextCond::maximumCxtLen = 0; -u32_t ContextCond::maximumCxt = 0; -u32_t ContextCond::maximumPathLen = 0; -u32_t ContextCond::maximumPath = 0; -u32_t SaberCondAllocator::totalCondNum = 0; - - -SaberCondAllocator::SaberCondAllocator() -{ - -} - -/*! - * Allocate path condition for each branch - */ -void SaberCondAllocator::allocate(const SVFModule *M) -{ - DBOUT(DGENERAL, outs() << pasMsg("path condition allocation starts\n")); - - for (const auto &func: *M) - { - if (!SVFUtil::isExtCall(func)) - { - // Allocate conditions for a program. - for (SVFFunction::const_iterator bit = func->begin(), ebit = func->end(); - bit != ebit; ++bit) - { - const SVFBasicBlock* bb = *bit; - collectBBCallingProgExit(*bb); - allocateForBB(*bb); - } - } - } - - if (Options::PrintPathCond()) - printPathCond(); - - DBOUT(DGENERAL, outs() << pasMsg("path condition allocation ends\n")); -} - -/*! - * Allocate conditions for a basic block and propagate its condition to its successors. - */ -void SaberCondAllocator::allocateForBB(const SVFBasicBlock &bb) -{ - - u32_t succ_number = bb.getNumSuccessors(); - - // if successor number greater than 1, allocate new decision variable for successors - if (succ_number > 1) - { - - //allocate log2(num_succ) decision variables - double num = log(succ_number) / log(2); - u32_t bit_num = (u32_t) ceil(num); - u32_t succ_index = 0; - std::vector condVec; - for (u32_t i = 0; i < bit_num; i++) - { - const SVFInstruction* svfInst = bb.getTerminator(); - condVec.push_back(newCond(svfInst)); - } - - // iterate each successor - for (const SVFBasicBlock* svf_succ_bb : bb.getSuccessors()) - { - Condition path_cond = getTrueCond(); - - ///TODO: handle BranchInst and SwitchInst individually here!! - - // for each successor decide its bit representation - // decide whether each bit of succ_index is 1 or 0, if (three successor) succ_index is 000 then use C1^C2^C3 - // if 001 use C1^C2^negC3 - for (u32_t j = 0; j < bit_num; j++) - { - //test each bit of this successor's index (binary representation) - u32_t tool = 0x01 << j; - if (tool & succ_index) - { - path_cond = condAnd(path_cond, (condNeg(condVec.at(j)))); - } - else - { - path_cond = condAnd(path_cond, condVec.at(j)); - } - } - setBranchCond(&bb, svf_succ_bb, path_cond); - - succ_index++; - } - - } -} - -/*! - * Get a branch condition - */ -SaberCondAllocator::Condition SaberCondAllocator::getBranchCond(const SVFBasicBlock* bb, const SVFBasicBlock* succ) const -{ - u32_t pos = bb->getBBSuccessorPos(succ); - if(bb->getNumSuccessors() == 1) - return getTrueCond(); - else - { - BBCondMap::const_iterator it = bbConds.find(bb); - assert(it != bbConds.end() && "basic block does not have branch and conditions??"); - CondPosMap::const_iterator cit = it->second.find(pos); - assert(cit != it->second.end() && "no condition on the branch??"); - return cit->second; - } -} - -SaberCondAllocator::Condition SaberCondAllocator::getEvalBrCond(const SVFBasicBlock* bb, const SVFBasicBlock* succ) -{ - if (getCurEvalSVFGNode() && getCurEvalSVFGNode()->getValue()) - return evaluateBranchCond(bb, succ); - else - return getBranchCond(bb, succ); -} - -/*! - * Set a branch condition - */ -void SaberCondAllocator::setBranchCond(const SVFBasicBlock* bb, const SVFBasicBlock* succ, const Condition &cond) -{ - /// we only care about basic blocks have more than one successor - assert(bb->getNumSuccessors() > 1 && "not more than one successor??"); - u32_t pos = bb->getBBSuccessorPos(succ); - CondPosMap& condPosMap = bbConds[bb]; - - /// FIXME: llvm getNumSuccessors allows duplicated block in the successors, it makes this assertion fail - /// In this case we may waste a condition allocation, because the overwrite of the previous cond - //assert(condPosMap.find(pos) == condPosMap.end() && "this branch has already been set "); - - condPosMap[pos] = cond; -} - -/*! - * Evaluate null like expression for source-sink related bug detection in SABER - */ -SaberCondAllocator::Condition -SaberCondAllocator::evaluateTestNullLikeExpr(const BranchStmt *branchStmt, const SVFBasicBlock* succ) -{ - - const SVFBasicBlock* succ1 = branchStmt->getSuccessor(0)->getBB(); - - if (isTestNullExpr(branchStmt->getCondition()->getValue())) - { - // succ is then branch - if (succ1 == succ) - return getFalseCond(); - // succ is else branch - else - return getTrueCond(); - } - if (isTestNotNullExpr(branchStmt->getCondition()->getValue())) - { - // succ is then branch - if (succ1 == succ) - return getTrueCond(); - // succ is else branch - else - return getFalseCond(); - } - return Condition::nullExpr(); -} - -/*! - * Evaluate condition for program exit (e.g., exit(0)) - */ -SaberCondAllocator::Condition SaberCondAllocator::evaluateProgExit(const BranchStmt *branchStmt, const SVFBasicBlock* succ) -{ - const SVFBasicBlock* succ1 = branchStmt->getSuccessor(0)->getBB(); - const SVFBasicBlock* succ2 = branchStmt->getSuccessor(1)->getBB(); - - bool branch1 = isBBCallsProgExit(succ1); - bool branch2 = isBBCallsProgExit(succ2); - - /// then branch calls program exit - if (branch1 && !branch2) - { - // succ is then branch - if (succ1 == succ) - return getFalseCond(); - // succ is else branch - else - return getTrueCond(); - } - /// else branch calls program exit - else if (!branch1 && branch2) - { - // succ is else branch - if (succ2 == succ) - return getFalseCond(); - // succ is then branch - else - return getTrueCond(); - } - // two branches both call program exit - else if (branch1 && branch2) - { - return getFalseCond(); - } - /// no branch call program exit - else - return Condition::nullExpr(); - -} - -/*! - * Evaluate loop exit branch to be true if - * bb is loop header and succ is the only exit basic block outside the loop (excluding exit bbs which call program exit) - * for all other case, we conservatively evaluate false for now - */ -SaberCondAllocator::Condition SaberCondAllocator::evaluateLoopExitBranch(const SVFBasicBlock* bb, const SVFBasicBlock* dst) -{ - const SVFFunction* svffun = bb->getParent(); - assert(svffun == dst->getParent() && "two basic blocks should be in the same function"); - - if (svffun->isLoopHeader(bb)) - { - Set filteredbbs; - std::vector exitbbs; - svffun->getExitBlocksOfLoop(bb,exitbbs); - /// exclude exit bb which calls program exit - for(const SVFBasicBlock* eb : exitbbs) - { - if(!isBBCallsProgExit(eb)) - filteredbbs.insert(eb); - } - - /// if the dst dominate all other loop exit bbs, then dst can certainly be reached - bool allPDT = true; - for (const auto &filteredbb: filteredbbs) - { - if (!postDominate(dst, filteredbb)) - allPDT = false; - } - - if (allPDT) - return getTrueCond(); - } - return Condition::nullExpr(); -} - -/*! - * (1) Evaluate a branch when it reaches a program exit - * (2) Evaluate a branch when it is loop exit branch - * (3) Evaluate a branch when it is a test null like condition - */ -SaberCondAllocator::Condition SaberCondAllocator::evaluateBranchCond(const SVFBasicBlock* bb, const SVFBasicBlock* succ) -{ - if(bb->getNumSuccessors() == 1) - { - return getTrueCond(); - } - - const SVFInstruction* svfInst = bb->getTerminator(); - if (ICFGNode *icfgNode = getICFG()->getICFGNode(svfInst)) - { - for (const auto &svfStmt: icfgNode->getSVFStmts()) - { - if (const BranchStmt *branchStmt = SVFUtil::dyn_cast(svfStmt)) - { - if (branchStmt->getNumSuccessors() == 2) - { - const SVFBasicBlock* succ1 = branchStmt->getSuccessor(0)->getBB(); - const SVFBasicBlock* succ2 = branchStmt->getSuccessor(1)->getBB(); - bool is_succ = (succ1 == succ || succ2 == succ); - (void)is_succ; // Suppress warning of unused variable under release build - assert(is_succ && "not a successor??"); - Condition evalLoopExit = evaluateLoopExitBranch(bb, succ); - if (!eq(evalLoopExit, Condition::nullExpr())) - return evalLoopExit; - - Condition evalProgExit = evaluateProgExit(branchStmt, succ); - if (!eq(evalProgExit, Condition::nullExpr())) - return evalProgExit; - - Condition evalTestNullLike = evaluateTestNullLikeExpr(branchStmt, succ); - if (!eq(evalTestNullLike, Condition::nullExpr())) - return evalTestNullLike; - break; - } - } - } - } - - return getBranchCond(bb, succ); -} - -bool SaberCondAllocator::isEQCmp(const CmpStmt *cmp) const -{ - return (cmp->getPredicate() == CmpStmt::ICMP_EQ); -} - -bool SaberCondAllocator::isNECmp(const CmpStmt *cmp) const -{ - return (cmp->getPredicate() == CmpStmt::ICMP_NE); -} - -bool SaberCondAllocator::isTestNullExpr(const SVFValue* test) const -{ - if(const SVFInstruction* svfInst = SVFUtil::dyn_cast(test)) - { - for(const SVFStmt* stmt : PAG::getPAG()->getSVFStmtList(getICFG()->getICFGNode(svfInst))) - { - if(const CmpStmt* cmp = SVFUtil::dyn_cast(stmt)) - { - return isTestContainsNullAndTheValue(cmp) && isEQCmp(cmp); - } - } - } - return false; -} - -bool SaberCondAllocator::isTestNotNullExpr(const SVFValue* test) const -{ - if(const SVFInstruction* svfInst = SVFUtil::dyn_cast(test)) - { - for(const SVFStmt* stmt : PAG::getPAG()->getSVFStmtList(getICFG()->getICFGNode(svfInst))) - { - if(const CmpStmt* cmp = SVFUtil::dyn_cast(stmt)) - { - return isTestContainsNullAndTheValue(cmp) && isNECmp(cmp); - } - } - } - return false; -} - -/*! - * Return true if: - * (1) cmp contains a null value - * (2) there is an indirect/direct edge from cur evaluated SVFG node to cmp operand - * - * e.g., - * indirect edge: - * cur svfg node -> 1. store i32* %0, i32** %p, align 8, !dbg !157 - * cmp operand -> 2. %1 = load i32*, i32** %p, align 8, !dbg !159 - * 3. %tobool = icmp ne i32* %1, null, !dbg !159 - * 4. br i1 %tobool, label %if.end, label %if.then, !dbg !161 - * There is an indirect edge 1->2 with value %0 - * - * direct edge: - * cur svfg node -> 1. %3 = tail call i8* @malloc(i64 16), !dbg !22 - * (cmp operand) 2. %4 = icmp eq i8* %3, null, !dbg !28 - * 3. br i1 %4, label %7, label %5, !dbg !30 - * There is an direct edge 1->2 with value %3 - * - */ -bool SaberCondAllocator::isTestContainsNullAndTheValue(const CmpStmt *cmp) const -{ - - const SVFValue* op0 = cmp->getOpVar(0)->getValue(); - const SVFValue* op1 = cmp->getOpVar(1)->getValue(); - if (SVFUtil::isa(op1)) - { - Set inDirVal; - inDirVal.insert(getCurEvalSVFGNode()->getValue()); - for (const auto &it: getCurEvalSVFGNode()->getOutEdges()) - { - inDirVal.insert(it->getDstNode()->getValue()); - } - return inDirVal.find(op0) != inDirVal.end(); - } - else if (SVFUtil::isa(op0)) - { - Set inDirVal; - inDirVal.insert(getCurEvalSVFGNode()->getValue()); - for (const auto &it: getCurEvalSVFGNode()->getOutEdges()) - { - inDirVal.insert(it->getDstNode()->getValue()); - } - return inDirVal.find(op1) != inDirVal.end(); - } - return false; -} - -/*! - * Whether this basic block contains program exit function call - */ -void SaberCondAllocator::collectBBCallingProgExit(const SVFBasicBlock &bb) -{ - - for (SVFBasicBlock::const_iterator it = bb.begin(), eit = bb.end(); it != eit; it++) - { - const SVFInstruction* svfInst = *it; - if (SVFUtil::isCallSite(svfInst)) - if (SVFUtil::isProgExitCall(svfInst)) - { - const SVFFunction* svfun = bb.getParent(); - funToExitBBsMap[svfun].insert(&bb); - } - } -} - -/*! - * Whether this basic block contains program exit function call - */ -bool SaberCondAllocator::isBBCallsProgExit(const SVFBasicBlock* bb) -{ - const SVFFunction* svfun = bb->getParent(); - FunToExitBBsMap::const_iterator it = funToExitBBsMap.find(svfun); - if (it != funToExitBBsMap.end()) - { - for (const auto &bit: it->second) - { - if (postDominate(bit, bb)) - return true; - } - } - return false; -} - -/*! - * Get complement phi condition - * e.g., B0: dstBB; B1:incomingBB; B2:complementBB - * Assume B0 (phi node) is the successor of both B1 and B2. - * If B1 dominates B2, and B0 not dominate B2 then condition from B1-->B0 = neg(B1-->B2)^(B1-->B0) - */ -SaberCondAllocator::Condition -SaberCondAllocator::getPHIComplementCond(const SVFBasicBlock* BB1, const SVFBasicBlock* BB2, const SVFBasicBlock* BB0) -{ - assert(BB1 && BB2 && "expect nullptr BB here!"); - - /// avoid both BB0 and BB1 dominate BB2 (e.g., while loop), then BB2 is not necessarily a complement BB - if (dominate(BB1, BB2) && ! dominate(BB0, BB2)) - { - Condition cond = ComputeIntraVFGGuard(BB1, BB2); - return condNeg(cond); - } - - return getTrueCond(); -} - -/*! - * Compute calling inter-procedural guards between two SVFGNodes (from caller to callee) - * src --c1--> callBB --true--> funEntryBB --c2--> dst - * the InterCallVFGGuard is c1 ^ c2 - */ -SaberCondAllocator::Condition -SaberCondAllocator::ComputeInterCallVFGGuard(const SVFBasicBlock* srcBB, const SVFBasicBlock* dstBB, - const SVFBasicBlock* callBB) -{ - const SVFBasicBlock* funEntryBB = dstBB->getParent()->getEntryBlock(); - - Condition c1 = ComputeIntraVFGGuard(srcBB, callBB); - setCFCond(funEntryBB, condOr(getCFCond(funEntryBB), getCFCond(callBB))); - Condition c2 = ComputeIntraVFGGuard(funEntryBB, dstBB); - return condAnd(c1, c2); -} - -/*! - * Compute return inter-procedural guards between two SVFGNodes (from callee to caller) - * src --c1--> funExitBB --true--> retBB --c2--> dst - * the InterRetVFGGuard is c1 ^ c2 - */ -SaberCondAllocator::Condition -SaberCondAllocator::ComputeInterRetVFGGuard(const SVFBasicBlock* srcBB, const SVFBasicBlock* dstBB, const SVFBasicBlock* retBB) -{ - const SVFFunction* parent = srcBB->getParent(); - const SVFBasicBlock* funExitBB = parent->getExitBB(); - - Condition c1 = ComputeIntraVFGGuard(srcBB, funExitBB); - setCFCond(retBB, condOr(getCFCond(retBB), getCFCond(funExitBB))); - Condition c2 = ComputeIntraVFGGuard(retBB, dstBB); - return condAnd(c1, c2); -} - -/*! - * Compute intra-procedural guards between two SVFGNodes (inside same function) - */ -SaberCondAllocator::Condition SaberCondAllocator::ComputeIntraVFGGuard(const SVFBasicBlock* srcBB, const SVFBasicBlock* dstBB) -{ - - assert(srcBB->getParent() == dstBB->getParent() && "two basic blocks are not in the same function??"); - - if (postDominate(dstBB, srcBB)) - return getTrueCond(); - - CFWorkList worklist; - worklist.push(srcBB); - setCFCond(srcBB, getTrueCond()); - - while (!worklist.empty()) - { - const SVFBasicBlock* bb = worklist.pop(); - Condition cond = getCFCond(bb); - - /// if the dstBB is the eligible loop exit of the current basic block - /// we can early terminate the computation - Condition loopExitCond = evaluateLoopExitBranch(bb, dstBB); - if (!eq(loopExitCond, Condition::nullExpr())) - return condAnd(cond, loopExitCond); - - for (const SVFBasicBlock* succ : bb->getSuccessors()) - { - /// calculate the branch condition - /// if succ post dominate bb, then we get brCond quicker by using postDT - /// note that we assume loop exit always post dominate loop bodys - /// which means loops are approximated only once. - Condition brCond; - if (postDominate(succ, bb)) - brCond = getTrueCond(); - else - brCond = getEvalBrCond(bb, succ); - - DBOUT(DSaber, outs() << " bb (" << bb->getName() << - ") --> " << "succ_bb (" << succ->getName() << ") condition: " << brCond << "\n"); - Condition succPathCond = condAnd(cond, brCond); - if (setCFCond(succ, condOr(getCFCond(succ), succPathCond))) - worklist.push(succ); - } - } - - DBOUT(DSaber, outs() << " src_bb (" << srcBB->getName() << - ") --> " << "dst_bb (" << dstBB->getName() << ") condition: " << getCFCond(dstBB) - << "\n"); - - return getCFCond(dstBB); -} - - -/*! - * Print path conditions - */ -void SaberCondAllocator::printPathCond() -{ - - outs() << "print path condition\n"; - - for (const auto &bbCond: bbConds) - { - const SVFBasicBlock* bb = bbCond.first; - for (const auto &cit: bbCond.second) - { - u32_t i = 0; - for (const SVFBasicBlock* succ: bb->getSuccessors()) - { - if (i == cit.first) - { - Condition cond = cit.second; - outs() << bb->getName() << "-->" << succ->getName() << ":"; - outs() << dumpCond(cond) << "\n"; - break; - } - i++; - } - } - } -} - -/// Allocate a new condition -SaberCondAllocator::Condition SaberCondAllocator::newCond(const SVFInstruction* inst) -{ - u32_t condCountIdx = totalCondNum++; - Condition expr = Condition::getContext().bool_const(("c" + std::to_string(condCountIdx)).c_str()); - Condition negCond = Condition::NEG(expr); - setCondInst(expr, inst); - setNegCondInst(negCond, inst); - conditionVec.push_back(expr); - conditionVec.push_back(negCond); - return expr; -} - -/// Whether lhs and rhs are equivalent branch conditions -bool SaberCondAllocator::isEquivalentBranchCond(const Condition &lhs, - const Condition &rhs) const -{ - Condition::getSolver().push(); - Condition::getSolver().add(lhs.getExpr() != rhs.getExpr()); /// check equal using z3 solver - z3::check_result res = Condition::getSolver().check(); - Condition::getSolver().pop(); - return res == z3::unsat; -} - -/// whether condition is satisfiable -bool SaberCondAllocator::isSatisfiable(const Condition &condition) -{ - Condition::getSolver().add(condition.getExpr()); - z3::check_result result = Condition::getSolver().check(); - Condition::getSolver().pop(); - if (result == z3::sat || result == z3::unknown) - return true; - else - return false; -} - -/// extract subexpression from a Z3 expression -void SaberCondAllocator::extractSubConds(const Condition &condition, NodeBS &support) const -{ - if (condition.getExpr().num_args() == 1 && isNegCond(condition.id())) - { - support.set(condition.getExpr().id()); - return; - } - if (condition.getExpr().num_args() == 0) - if (!condition.getExpr().is_true() && !condition.getExpr().is_false()) - support.set(condition.getExpr().id()); - for (u32_t i = 0; i < condition.getExpr().num_args(); ++i) - { - Condition expr = condition.getExpr().arg(i); - extractSubConds(expr, support); - } - -} diff --git a/svf/lib/SABER/SaberSVFGBuilder.cpp b/svf/lib/SABER/SaberSVFGBuilder.cpp index 479c4424a..882be446c 100644 --- a/svf/lib/SABER/SaberSVFGBuilder.cpp +++ b/svf/lib/SABER/SaberSVFGBuilder.cpp @@ -2,20 +2,20 @@ // // SVF: Static Value-Flow Analysis // -// Copyright (C) <2013-> +// Copyright (C) <2013-2017> // // This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU Affero General Public License as published by +// it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Affero General Public License for more details. +// GNU General Public License for more details. -// You should have received a copy of the GNU Affero General Public License +// You should have received a copy of the GNU General Public License // along with this program. If not, see . // //===----------------------------------------------------------------------===// @@ -29,11 +29,7 @@ #include "SABER/SaberSVFGBuilder.h" #include "SABER/SaberCheckerAPI.h" -#include "MemoryModel/PointerAnalysisImpl.h" #include "Graphs/SVFG.h" -#include "Util/Options.h" -#include "SABER/SaberCondAllocator.h" - using namespace SVF; using namespace SVFUtil; @@ -52,7 +48,6 @@ void SaberSVFGBuilder::buildSVFG() rmDerefDirSVFGEdges(pta); - assert(saberCondAllocator && "saber condition allocator not set yet!"); rmIncomingEdgeForSUStore(pta); DBOUT(DGENERAL, outs() << pasMsg("\tAdd Sink SVFG Nodes\n")); @@ -69,22 +64,21 @@ void SaberSVFGBuilder::buildSVFG() */ void SaberSVFGBuilder::collectGlobals(BVDataPTAImpl* pta) { - SVFIR* pag = svfg->getPAG(); + PAG* pag = svfg->getPAG(); NodeVector worklist; - for(SVFIR::iterator it = pag->begin(), eit = pag->end(); it!=eit; it++) + for(PAG::iterator it = pag->begin(), eit = pag->end(); it!=eit; it++) { PAGNode* pagNode = it->second; - if (SVFUtil::isa(pagNode)) + if(SVFUtil::isa(pagNode) || SVFUtil::isa(pagNode)) continue; - if(GepObjVar* gepobj = SVFUtil::dyn_cast(pagNode)) - { - if(SVFUtil::isa(pag->getGNode(gepobj->getBaseNode()))) + if(GepObjPN* gepobj = SVFUtil::dyn_cast(pagNode)) { + if(SVFUtil::isa(pag->getPAGNode(gepobj->getBaseNode()))) continue; } - if(const SVFValue* val = pagNode->getValue()) + if(const Value* val = pagNode->getValue()) { - if(SVFUtil::isa(val)) + if(SVFUtil::isa(val)) worklist.push_back(it->first); } } @@ -103,48 +97,17 @@ void SaberSVFGBuilder::collectGlobals(BVDataPTAImpl* pta) } } - -/* - * https://github.com/SVF-tools/SVF/issues/991 - * - * Originally, this function will collect all base pointers with all their fields - * inside the points-to set of global variables. But if a global variable points - * to the pointer returned by malloc() at some program points, then all pointers - * returned by malloc() will be included in the global set because of the - * context-insensitive pointer analysis results. This will make saber abandon - * too many slicing thus miss potential bugs. - * - * We add an option "saber-collect-extret-globals" to control whether this function - * will collect external functions' returned pointers. This option is true by default, - * making it to be false will let saber analyze more slicing but cause performance downgrade. - * - */ -PointsTo& SaberSVFGBuilder::CollectPtsChain(BVDataPTAImpl* pta, NodeID id, NodeToPTSSMap& cachedPtsMap) +NodeBS& SaberSVFGBuilder::CollectPtsChain(BVDataPTAImpl* pta,NodeID id, NodeToPTSSMap& cachedPtsMap) { - SVFIR* pag = svfg->getPAG(); + PAG* pag = svfg->getPAG(); - NodeID baseId = pag->getBaseObjVar(id); + NodeID baseId = pag->getBaseObjNode(id); NodeToPTSSMap::iterator it = cachedPtsMap.find(baseId); if(it!=cachedPtsMap.end()) - { return it->second; - } else { PointsTo& pts = cachedPtsMap[baseId]; - // base object - if (!Options::CollectExtRetGlobals()) - { - if(pta->isFIObjNode(baseId) && pag->getGNode(baseId)->hasValue()) - { - const SVFCallInst* inst = SVFUtil::dyn_cast(pag->getGNode(baseId)->getValue()); - if(inst && SVFUtil::isExtCall(inst)) - { - return pts; - } - } - } - pts |= pag->getFieldsAfterCollapse(baseId); WorkList worklist; @@ -162,6 +125,7 @@ PointsTo& SaberSVFGBuilder::CollectPtsChain(BVDataPTAImpl* pta, NodeID id, NodeT } return pts; } + } /*! @@ -190,10 +154,9 @@ void SaberSVFGBuilder::rmDerefDirSVFGEdges(BVDataPTAImpl* pta) if(SVFUtil::isa(stmtNode)) { const SVFGNode* def = svfg->getDefSVFGNode(stmtNode->getPAGDstNode()); - if(SVFGEdge* edge = svfg->getIntraVFGEdge(def,stmtNode,SVFGEdge::IntraDirectVF)) - svfg->removeSVFGEdge(edge); - else - assert((svfg->getKind()==VFG::FULLSVFG_OPT || svfg->getKind()==VFG::PTRONLYSVFG_OPT) && "Edge not found!"); + SVFGEdge* edge = svfg->getIntraVFGEdge(def,stmtNode,SVFGEdge::IntraDirectVF); + assert(edge && "Edge not found!"); + svfg->removeSVFGEdge(edge); if(accessGlobal(pta,stmtNode->getPAGDstNode())) { @@ -203,10 +166,9 @@ void SaberSVFGBuilder::rmDerefDirSVFGEdges(BVDataPTAImpl* pta) else if(SVFUtil::isa(stmtNode)) { const SVFGNode* def = svfg->getDefSVFGNode(stmtNode->getPAGSrcNode()); - if(SVFGEdge* edge = svfg->getIntraVFGEdge(def,stmtNode,SVFGEdge::IntraDirectVF)) - svfg->removeSVFGEdge(edge); - else - assert((svfg->getKind()==VFG::FULLSVFG_OPT || svfg->getKind()==VFG::PTRONLYSVFG_OPT) && "Edge not found!"); + SVFGEdge* edge = svfg->getIntraVFGEdge(def,stmtNode,SVFGEdge::IntraDirectVF); + assert(edge && "Edge not found!"); + svfg->removeSVFGEdge(edge); if(accessGlobal(pta,stmtNode->getPAGSrcNode())) { @@ -235,8 +197,8 @@ bool SaberSVFGBuilder::isStrongUpdate(const SVFGNode* node, NodeID& singleton, B // Strong update can be made if this points-to target is not heap, array or field-insensitive. if (!pta->isHeapMemObj(singleton) && !pta->isArrayMemObj(singleton) - && SVFIR::getPAG()->getBaseObj(singleton)->isFieldInsensitive() == false - && !pta->isLocalVarInRecursiveFun(singleton)) + && PAG::getPAG()->getBaseObj(singleton)->isFieldInsensitive() == false + && !pta->isLocalVarInRecursiveFun(singleton)) { isSU = true; } @@ -262,28 +224,23 @@ void SaberSVFGBuilder::rmIncomingEdgeForSUStore(BVDataPTAImpl* pta) { const SVFGNode* node = it->second; - if(const StoreSVFGNode* stmtNode = SVFUtil::dyn_cast(node)) + if(const StmtSVFGNode* stmtNode = SVFUtil::dyn_cast(node)) { - if(SVFUtil::isa(stmtNode->getPAGEdge())) + if(SVFUtil::isa(stmtNode) && SVFUtil::isa(stmtNode->getValue())) { NodeID singleton; - if(isStrongUpdate(node, singleton, pta)) - { + if(isStrongUpdate(node, singleton, pta)) { Set toRemove; - for (SVFGNode::const_iterator it2 = node->InEdgeBegin(), eit2 = node->InEdgeEnd(); it2 != eit2; ++it2) - { - if ((*it2)->isIndirectVFGEdge()) - { + for (SVFGNode::const_iterator it2 = node->InEdgeBegin(), eit2 = node->InEdgeEnd(); it2 != eit2; ++it2) { + if ((*it2)->isIndirectVFGEdge()) { toRemove.insert(*it2); } } - for (SVFGEdge* edge: toRemove) - { - if (isa(edge->getSrcNode())) - saberCondAllocator->getRemovedSUVFEdges()[edge->getSrcNode()].insert(edge->getDstNode()); + for (SVFGEdge* edge: toRemove) { svfg->removeSVFGEdge(edge); } } + } } } @@ -293,8 +250,8 @@ void SaberSVFGBuilder::rmIncomingEdgeForSUStore(BVDataPTAImpl* pta) /// Add actual parameter SVFGNode for 1st argument of a deallocation like external function void SaberSVFGBuilder::AddExtActualParmSVFGNodes(PTACallGraph* callgraph) { - SVFIR* pag = SVFIR::getPAG(); - for(SVFIR::CSToArgsListMap::iterator it = pag->getCallSiteArgsMap().begin(), + PAG* pag = PAG::getPAG(); + for(PAG::CSToArgsListMap::iterator it = pag->getCallSiteArgsMap().begin(), eit = pag->getCallSiteArgsMap().end(); it!=eit; ++it) { PTACallGraph::FunctionSet callees; @@ -307,15 +264,13 @@ void SaberSVFGBuilder::AddExtActualParmSVFGNodes(PTACallGraph* callgraph) if (SaberCheckerAPI::getCheckerAPI()->isMemDealloc(fun) || SaberCheckerAPI::getCheckerAPI()->isFClose(fun)) { - SVFIR::SVFVarList& arglist = it->second; - for(SVFIR::SVFVarList::const_iterator ait = arglist.begin(), aeit = arglist.end(); ait!=aeit; ++ait) - { - const PAGNode *pagNode = *ait; - if (pagNode->isPointer()) - { - addActualParmVFGNode(pagNode, it->first); - svfg->addIntraDirectVFEdge(svfg->getDefSVFGNode(pagNode)->getId(), svfg->getActualParmVFGNode(pagNode, it->first)->getId()); - } + PAG::PAGNodeList& arglist = it->second; + for(PAG::PAGNodeList::const_iterator ait = arglist.begin(), aeit = arglist.end(); ait!=aeit; ++ait){ + const PAGNode *pagNode = *ait; + if (pagNode->isPointer()) { + addActualParmVFGNode(pagNode, it->first); + svfg->addIntraDirectVFEdge(svfg->getDefSVFGNode(pagNode)->getId(), svfg->getActualParmVFGNode(pagNode, it->first)->getId()); + } } } } diff --git a/svf/lib/SABER/SrcSnkDDA.cpp b/svf/lib/SABER/SrcSnkDDA.cpp index 99f0a89f6..2bb0e6e5a 100644 --- a/svf/lib/SABER/SrcSnkDDA.cpp +++ b/svf/lib/SABER/SrcSnkDDA.cpp @@ -2,20 +2,20 @@ // // SVF: Static Value-Flow Analysis // -// Copyright (C) <2013-> +// Copyright (C) <2013-2017> // // This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU Affero General Public License as published by +// it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Affero General Public License for more details. +// GNU General Public License for more details. -// You should have received a copy of the GNU Affero General Public License +// You should have received a copy of the GNU General Public License // along with this program. If not, see . // //===----------------------------------------------------------------------===// @@ -31,8 +31,7 @@ #include "Util/Options.h" #include "SABER/SrcSnkDDA.h" #include "Graphs/SVFGStat.h" -#include "Util/Options.h" -#include "WPA/Andersen.h" +#include "SVF-FE/PAGBuilder.h" using namespace SVF; using namespace SVFUtil; @@ -40,19 +39,16 @@ using namespace SVFUtil; /// Initialize analysis void SrcSnkDDA::initialize(SVFModule* module) { - SVFIR* pag = PAG::getPAG(); + PAGBuilder builder; + PAG* pag = builder.build(module); AndersenWaveDiff* ander = AndersenWaveDiff::createAndersenWaveDiff(pag); - memSSA.setSaberCondAllocator(getSaberCondAllocator()); - if(Options::SABERFULLSVFG()) - svfg = memSSA.buildFullSVFG(ander); - else - svfg = memSSA.buildPTROnlySVFG(ander); + svfg = memSSA.buildPTROnlySVFG(ander); setGraph(memSSA.getSVFG()); ptaCallGraph = ander->getPTACallGraph(); //AndersenWaveDiff::releaseAndersenWaveDiff(); /// allocate control-flow graph branch conditions - getSaberCondAllocator()->allocate(getPAG()->getModule()); + getPathAllocator()->allocate(getPAG()->getModule()); initSrcs(); initSnks(); @@ -63,7 +59,7 @@ void SrcSnkDDA::analyze(SVFModule* module) initialize(module); - ContextCond::setMaxCxtLen(Options::CxtLimit()); + ContextCond::setMaxCxtLen(Options::CxtLimit); for (SVFGNodeSetIter iter = sourcesBegin(), eiter = sourcesEnd(); iter != eiter; ++iter) @@ -95,7 +91,7 @@ void SrcSnkDDA::analyze(SVFModule* module) DBOUT(DSaber, outs() << "Backward process for slice:" << (*iter)->getId() << " (size = " << getCurSlice()->getBackwardSliceSize() << ")\n"); - if(Options::DumpSlice()) + if(Options::DumpSlice) annotateSlice(_curSlice); if(_curSlice->AllPathReachableSolve()) @@ -106,8 +102,8 @@ void SrcSnkDDA::analyze(SVFModule* module) reportBug(getCurSlice()); } - finalize(); + finalize(); } @@ -133,7 +129,7 @@ bool SrcSnkDDA::isInAWrapper(const SVFGNode* src, CallSiteSet& csIdSet) else continue; // reaching maximum steps when traversing on SVFG to identify a memory allocation wrapper - if (step++ > Options::MaxStepInWrapper()) + if (step++ > Options::MaxStepInWrapper) return false; for (SVFGNode::const_iterator it = node->OutEdgeBegin(), eit = @@ -158,18 +154,17 @@ bool SrcSnkDDA::isInAWrapper(const SVFGNode* src, CallSiteSet& csIdSet) else { const SVFGNode* succ = edge->getDstNode(); - if(SVFUtil::isa(edge)) - { - if (SVFUtil::isa(succ)) + if(SVFUtil::isa(edge)){ + if (SVFUtil::isa(succ) || SVFUtil::isa(succ) + || SVFUtil::isa(succ) || SVFUtil::isa(succ) + || SVFUtil::isa(succ) || SVFUtil::isa(succ)) { worklist.push(succ); } } else if(SVFUtil::isa(edge)) { - if(SVFUtil::isa(succ)) + if(SVFUtil::isa(succ)) { worklist.push(succ); } @@ -278,7 +273,7 @@ void SrcSnkDDA::setCurSlice(const SVFGNode* src) clearVisitedMap(); } - _curSlice = new ProgSlice(src,getSaberCondAllocator(), getSVFG()); + _curSlice = new ProgSlice(src,getPathAllocator(), getSVFG()); } void SrcSnkDDA::annotateSlice(ProgSlice* slice) @@ -295,13 +290,13 @@ void SrcSnkDDA::annotateSlice(ProgSlice* slice) void SrcSnkDDA::dumpSlices() { - if(Options::DumpSlice()) + if(Options::DumpSlice) const_cast(getSVFG())->dump("Slice",true); } -void SrcSnkDDA::printZ3Stat() +void SrcSnkDDA::printBDDStat() { - outs() << "Z3 Mem usage: " << getSaberCondAllocator()->getMemUsage() << "\n"; - outs() << "Z3 Number: " << getSaberCondAllocator()->getCondNum() << "\n"; + outs() << "BDD Mem usage: " << getPathAllocator()->getMemUsage() << "\n"; + outs() << "BDD Number: " << getPathAllocator()->getCondNum() << "\n"; } diff --git a/svf/lib/SVF-FE/ArgFlowAnalysis.cpp b/svf/lib/SVF-FE/ArgFlowAnalysis.cpp new file mode 100644 index 000000000..73df11005 --- /dev/null +++ b/svf/lib/SVF-FE/ArgFlowAnalysis.cpp @@ -0,0 +1,235 @@ +/* SVF - Static Value-Flow Analysis Framework +Copyright (C) 2015 Yulei Sui +Copyright (C) 2015 Jingling Xue + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + + +#define DEBUG_TYPE "arg-flow-analysis" + +#include "llvm/ADT/Statistic.h" +#include "llvm/IR/Constants.h" +#include "llvm/IR/InstrTypes.h" +#include "llvm/IR/Instruction.h" +#include "llvm/IR/Instructions.h" +#include "llvm/IR/LLVMContext.h" +#include "llvm/IR/InstIterator.h" + + +#include "SVF-FE/ArgFlowAnalysis.h" + +using namespace SVF; + +// Identifier variable for the pass +char ArgFlowAnalysis::ID = 0; + + +// Register the pass +static llvm::RegisterPass ARGFLOW ("argflow", + "ArgFlowAnalysis"); + + +void ArgFlowSummary::findSinkSites(Argument* arg) { + std::vector workList; + std::vector processedList; + workList.push_back(arg); + + while (!workList.empty()) { + Value* val = workList.back(); + workList.pop_back(); + + processedList.push_back(val); + + for (User* u: val->users()) { + if (Value* uVal = SVFUtil::dyn_cast(u)) { + if (StoreInst* store = SVFUtil::dyn_cast(uVal)) { + // Is val being sunk? + if (store->getValueOperand() == val) { + Value* ptr = store->getPointerOperand(); + + getArgSinkMap()[arg].push_back(ptr); + getArgToSinkStoreMap()[arg].push_back(ptr); + + // We track multiple levels of sinks + if (std::find(processedList.begin(), processedList.end(), ptr) + == processedList.end()) { + workList.push_back(ptr); + } + } + // If it's not being sunk, then something else is being sunk into it + // We don't care about this situation because we always check the + // following condition: + // Is the sink-site of any arg in the _forward slice_ of another arg + // The forward slice doesn't have to capture the store instruction + // itself, only its ptr component. + + } else { + // If it is a call instruction ignore + if (SVFUtil::isa(uVal)) continue; + // Just push it back + if (std::find(processedList.begin(), processedList.end(), uVal) == processedList.end() + && SVFUtil::isa(uVal->getType())) { + workList.push_back(uVal); + } + getArgForwardSliceMap()[arg].push_back(uVal); + } + + // Just copy the backward slice vectors + getBackwardSliceMap()[uVal] = getBackwardSliceMap()[val]; + + // Append val to the end of all of the backward slice vectors + for (auto& vec: getBackwardSliceMap()[uVal]) { + vec.push_back(val); + } + } + } + } +} + +ArgFlowSummary* ArgFlowSummary::argFlowSummary; + + +void ArgFlowSummary::dumpBackwardSlice (Value* val) { + +} + +void ArgFlowAnalysis::buildCallGraph(Module& module, ArgFlowSummary* summary) { + for (Function &F : module) { + for (BasicBlock &BB : F) { + for (Instruction &I : BB) { + if (CallInst *callInst = SVFUtil::dyn_cast(&I)) { + if (callInst->getCalledFunction()) { + if (callInst->getCalledFunction() != &F) { // Don't handle recursive functions + calleeMap[&F].push_back(callInst->getCalledFunction()); + callerMap[callInst->getCalledFunction()].push_back(&F); + } + } + } + } + } + } + + computeIsSummarizable(); + +} + +void ArgFlowAnalysis::computeIsSummarizable() { + // Criteria: + // We want to only summarize at most one level + // That is funcA -> funcB + // If there are further callers of funcA then summarizing + // the last two levels won't help anyway because funcA will + // in all likelihood + // become the bottleneck + for (auto it: callerMap) { + Function* calledFunc = it.first; + int firstLevelCount = it.second.size(); + int totalCalleeCount = 0; + for (Function* caller: it.second) { + int secondLevelCount = callerMap[caller].size(); + totalCalleeCount += secondLevelCount*firstLevelCount; + } + + if (totalCalleeCount <= 5) { + continue; + } + + /* + if (calledFunc->getName() == "event_assign") { + llvm::errs() << "# of callers: " << it.second.size() << "\n"; + } + */ + + // Additional criteria: Only capture those cases + // where the number of callers is 1. This isn't + // _strictly_ necessary, but will ensure that we only + // capture the "wrappers" + if (it.second.size() == 1) { + continue; + } + + // Final criteria: The single caller (funcA) should + // have only once calling context with more than 2 callers + // Very arbitrary I know, but I need a prototype + Function* caller = it.second[0]; + int numMultiCallers = 0; + for (Function* callerCaller: callerMap[caller]) { + if (callerMap[callerCaller].size() > 2) { + numMultiCallers++; + } + } + + /* + if (calledFunc->getName() == "event_assign") { + Function* caller = it.second[0]; + for (Function* callerCaller: callerMap[caller]) { + llvm::errs() << "event_assign caller " << callerCaller->getName() << "\n"; + } + } + */ + if (numMultiCallers > 1) { + continue; + } + + isSummarizableMap[calledFunc] = true; + + } +} + +bool +ArgFlowAnalysis::runOnModule (Module & module) { + ArgFlowSummary* summary = ArgFlowSummary::getArgFlowSummary(); + buildCallGraph(module, summary); + for (Function& func: module.getFunctionList()) { + if (func.arg_size() < 2 || !isSummarizableMap[&func]) { + continue; + } + + for (Argument& arg: func.args()) { + // Check if this is a pointer type (Note that this isn't going to be a + // pointer to a pointer because Args aren't assumed to be stack + // locations in LLVM IR + Type* argTy = arg.getType(); + if (SVFUtil::isa(argTy)) { + summary->findSinkSites(&arg); + } + } + for (Argument& arg1Ref: func.args()) { + Argument* arg1 = &arg1Ref; + std::map>::iterator sinkIt; + for (sinkIt = summary->getArgSinkMap().begin(); sinkIt != summary->getArgSinkMap().end(); sinkIt++) { + for (Value* sinkSite: sinkIt->second) { + // Check if this sink-site is in the forward slice of any of the + // other arguments + for (Argument& arg2Ref: func.args()) { + Argument* arg2 = &arg2Ref; + if (arg1 == arg2 || !SVFUtil::isa(arg1->getType()) || !SVFUtil::isa(arg1->getType())) { + continue; + } + std::vector::iterator forwardSliceIt; + if (std::find(summary->getArgForwardSliceMap()[arg2].begin(), summary->getArgForwardSliceMap()[arg2].end(), sinkSite) + != summary->getArgForwardSliceMap()[arg2].end()) { + // Match found + //llvm::errs() << "Function: " << func.getName() << ", Argument: " << *arg1 << " stored to " << *arg2 << "\n"; + summary->dumpBackwardSlice(sinkSite); + } + } + } + } + } + } + return false; +} diff --git a/svf/lib/SVF-FE/BreakConstantExpr.cpp b/svf/lib/SVF-FE/BreakConstantExpr.cpp new file mode 100644 index 000000000..5ef130e45 --- /dev/null +++ b/svf/lib/SVF-FE/BreakConstantExpr.cpp @@ -0,0 +1,384 @@ +/* SVF - Static Value-Flow Analysis Framework +Copyright (C) 2015 Yulei Sui +Copyright (C) 2015 Jingling Xue + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +/* + * BreakConstantExpr.cpp + * + * Created on: Oct 8, 2013 + * Author: Yulei Sui + */ + + + +//===- BreakConstantGEPs.cpp - Change constant GEPs into GEP instructions - --// +// +// The SAFECode Compiler +// +// This file was developed by the LLVM research group and is distributed under +// the University of Illinois Open Source License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This pass changes all GEP constant expressions into GEP instructions. This +// permits the rest of SAFECode to put run-time checks on them if necessary. +// +//===----------------------------------------------------------------------===// + +#define DEBUG_TYPE "break-constgeps" + +#include "llvm/ADT/Statistic.h" +#include "llvm/IR/Constants.h" +#include "llvm/IR/InstrTypes.h" +#include "llvm/IR/Instruction.h" +#include "llvm/IR/Instructions.h" +#include "llvm/IR/LLVMContext.h" +#include "llvm/IR/InstIterator.h" + +#include "SVF-FE/BreakConstantExpr.h" + +#include +#include +#include + +using namespace SVF; + +// Identifier variable for the pass +char BreakConstantGEPs::ID = 0; +char MergeFunctionRets::ID = 0; +// Statistics +STATISTIC (GEPChanges, "Number of Converted GEP Constant Expressions"); +STATISTIC (TotalChanges, "Number of Converted Constant Expressions"); + +// Register the pass +static llvm::RegisterPass BP ("break-constgeps", + "Remove GEP Constant Expressions"); +static llvm::RegisterPass MP ("merge-rets", + "Merge function rets into one"); +// +// Function: hasConstantGEP() +// +// Description: +// This function determines whether the given value is a constant expression +// that has a constant GEP expression embedded within it. +// +// Inputs: +// V - The value to check. +// +// Return value: +// nullptr - This value is not a constant expression with a constant expression +// GEP within it. +// ~nullptr - A pointer to the value casted into a ConstantExpr is returned. +// +static ConstantExpr * +hasConstantGEP (Value * V) +{ + if (ConstantExpr * CE = SVFUtil::dyn_cast(V)) + { + if (CE->getOpcode() == Instruction::GetElementPtr) + { + return CE; + } + else + { + for (unsigned index = 0; index < CE->getNumOperands(); ++index) + { + if (hasConstantGEP (CE->getOperand(index))) + return CE; + } + } + } + + return 0; +} + +// +// Function: convertGEP() +// +// Description: +// Convert a GEP constant expression into a GEP instruction. +// +// Inputs: +// CE - The GEP constant expression. +// InsertPt - The instruction before which to insert the new GEP instruction. +// +// Return value: +// A pointer to the new GEP instruction is returned. +// +static Instruction * +convertGEP (ConstantExpr * CE, Instruction * InsertPt) +{ + // + // Create iterators to the indices of the constant expression. + // + std::vector Indices; + for (unsigned index = 1; index < CE->getNumOperands(); ++index) + { + Indices.push_back (CE->getOperand (index)); + } + llvm::ArrayRef arrayIdices(Indices); + // + // Update the statistics. + // + ++GEPChanges; + + + // + // Make the new GEP instruction. + // + return (GetElementPtrInst::Create (nullptr,CE->getOperand(0), + arrayIdices, + CE->getName(), + InsertPt)); +} + +// +// Function: convertExpression() +// +// Description: +// Convert a constant expression into an instruction. This routine does *not* +// perform any recursion, so the resulting instruction may have constant +// expression operands. +// +static Instruction * +convertExpression (ConstantExpr * CE, Instruction * InsertPt) +{ + // + // Convert this constant expression into a regular instruction. + // + Instruction * NewInst = 0; + switch (CE->getOpcode()) + { + case Instruction::GetElementPtr: + { + NewInst = convertGEP (CE, InsertPt); + break; + } + + case Instruction::Add: + case Instruction::Sub: + case Instruction::Mul: + case Instruction::FAdd: + case Instruction::FSub: + case Instruction::FMul: + case Instruction::UDiv: + case Instruction::SDiv: + case Instruction::FDiv: + case Instruction::URem: + case Instruction::SRem: + case Instruction::FRem: + case Instruction::Shl: + case Instruction::LShr: + case Instruction::AShr: + case Instruction::And: + case Instruction::Or: + case Instruction::Xor: + { + Instruction::BinaryOps Op = (Instruction::BinaryOps)(CE->getOpcode()); + NewInst = llvm::BinaryOperator::Create (Op, + CE->getOperand(0), + CE->getOperand(1), + CE->getName(), + InsertPt); + break; + } + + case Instruction::FNeg: + { + Instruction::UnaryOps Op = (Instruction::UnaryOps)(CE->getOpcode()); + NewInst = llvm::UnaryOperator::Create (Op, + CE->getOperand(0), + CE->getName(), + InsertPt); + break; + } + + case Instruction::Trunc: + case Instruction::ZExt: + case Instruction::SExt: + case Instruction::FPToUI: + case Instruction::FPToSI: + case Instruction::UIToFP: + case Instruction::SIToFP: + case Instruction::FPTrunc: + case Instruction::FPExt: + case Instruction::PtrToInt: + case Instruction::IntToPtr: + case Instruction::BitCast: + { + Instruction::CastOps Op = (Instruction::CastOps)(CE->getOpcode()); + NewInst = CastInst::Create (Op, + CE->getOperand(0), + CE->getType(), + CE->getName(), + InsertPt); + break; + } + + case Instruction:: FCmp: + case Instruction:: ICmp: + { + Instruction::OtherOps Op = (Instruction::OtherOps)(CE->getOpcode()); + NewInst = CmpInst::Create (Op, + static_cast(CE->getPredicate()), + CE->getOperand(0), + CE->getOperand(1), + CE->getName(), + InsertPt); + break; + } + + case Instruction:: Select: + NewInst = SelectInst::Create (CE->getOperand(0), + CE->getOperand(1), + CE->getOperand(2), + CE->getName(), + InsertPt); + break; + + case Instruction:: ExtractElement: + case Instruction:: InsertElement: + case Instruction:: ShuffleVector: + case Instruction:: InsertValue: + default: + assert (0 && "Unhandled constant expression!\n"); + break; + } + + // + // Update the statistics. + // + ++TotalChanges; + + return NewInst; +} + +// +// Method: runOnFunction() +// +// Description: +// Entry point for this LLVM pass. +// +// Return value: +// true - The function was modified. +// false - The function was not modified. +// +bool +BreakConstantGEPs::runOnModule (Module & module) +{ + bool modified = false; + for (Module::iterator F = module.begin(), E = module.end(); F != E; ++F) + { + // Worklist of values to check for constant GEP expressions + std::vector Worklist; + + // + // Initialize the worklist by finding all instructions that have one or more + // operands containing a constant GEP expression. + // + for (Function::iterator BB = (*F).begin(); BB != (*F).end(); ++BB) + { + for (BasicBlock::iterator i = BB->begin(); i != BB->end(); ++i) + { + // + // Scan through the operands of this instruction. If it is a constant + // expression GEP, insert an instruction GEP before the instruction. + // + Instruction * I = &(*i); + for (unsigned index = 0; index < I->getNumOperands(); ++index) + { + if (hasConstantGEP (I->getOperand(index))) + { + Worklist.push_back (I); + } + } + } + } + + // + // Determine whether we will modify anything. + // + if (Worklist.size()) modified = true; + + // + // While the worklist is not empty, take an item from it, convert the + // operands into instructions if necessary, and determine if the newly + // added instructions need to be processed as well. + // + while (Worklist.size()) + { + Instruction * I = Worklist.back(); + Worklist.pop_back(); + + // + // Scan through the operands of this instruction and convert each into an + // instruction. Note that this works a little differently for phi + // instructions because the new instruction must be added to the + // appropriate predecessor block. + // + if (PHINode * PHI = SVFUtil::dyn_cast(I)) + { + for (unsigned index = 0; index < PHI->getNumIncomingValues(); ++index) + { + // + // For PHI Nodes, if an operand is a constant expression with a GEP, we + // want to insert the new instructions in the predecessor basic block. + // + // Note: It seems that it's possible for a phi to have the same + // incoming basic block listed multiple times; this seems okay as long + // the same value is listed for the incoming block. + // + Instruction * InsertPt = PHI->getIncomingBlock(index)->getTerminator(); + if (ConstantExpr * CE = hasConstantGEP (PHI->getIncomingValue(index))) + { + Instruction * NewInst = convertExpression (CE, InsertPt); + for (unsigned i2 = index; i2 < PHI->getNumIncomingValues(); ++i2) + { + if ((PHI->getIncomingBlock (i2)) == PHI->getIncomingBlock (index)) + PHI->setIncomingValue (i2, NewInst); + } + Worklist.push_back (NewInst); + } + } + } + else + { + for (unsigned index = 0; index < I->getNumOperands(); ++index) + { + // + // For other instructions, we want to insert instructions replacing + // constant expressions immediently before the instruction using the + // constant expression. + // + if (ConstantExpr * CE = hasConstantGEP (I->getOperand(index))) + { + Instruction * NewInst = convertExpression (CE, I); + I->replaceUsesOfWith (CE, NewInst); + Worklist.push_back (NewInst); + } + } + } + } + + } + return modified; +} + + + + diff --git a/svf/lib/SVF-FE/CHG.cpp b/svf/lib/SVF-FE/CHG.cpp new file mode 100644 index 000000000..2b6031092 --- /dev/null +++ b/svf/lib/SVF-FE/CHG.cpp @@ -0,0 +1,958 @@ +//===----- CHG.cpp Base class of pointer analyses ---------------------------// +// +// SVF: Static Value-Flow Analysis +// +// Copyright (C) <2013-2017> +// + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// +//===----------------------------------------------------------------------===// + +/* + * CHG.cpp (previously CHA.cpp) + * + * Created on: Apr 13, 2016 + * Author: Xiaokang Fan + */ + +#include +#include +#include +#include +#include +#include // setw() for formatting cout +#include +#include + +#include "Util/Options.h" +#include "SVF-FE/CPPUtil.h" +#include "SVF-FE/CHG.h" +#include "SVF-FE/SymbolTableInfo.h" +#include "Util/SVFUtil.h" +#include "SVF-FE/LLVMUtil.h" +#include "Util/SVFModule.h" + +using namespace SVF; +using namespace SVFUtil; +using namespace cppUtil; +using namespace std; + + +const string pureVirtualFunName = "__cxa_pure_virtual"; + +const string ztiLabel = "_ZTI"; + +static bool hasEdge(const CHNode *src, const CHNode *dst, + CHEdge::CHEDGETYPE et) +{ + for (CHEdge::CHEdgeSetTy::const_iterator it = src->getOutEdges().begin(), + eit = src->getOutEdges().end(); it != eit; ++it) + { + CHNode *node = (*it)->getDstNode(); + CHEdge::CHEDGETYPE edgeType = (*it)->getEdgeType(); + if (node == dst && edgeType == et) + return true; + } + return false; +} + +void CHNode::getVirtualFunctions(u32_t idx, FuncVector &virtualFunctions) const +{ + for (vector::const_iterator it = virtualFunctionVectors.begin(), + eit = virtualFunctionVectors.end(); it != eit; ++it) + { + if ((*it).size() > idx) + virtualFunctions.push_back((*it)[idx]); + } +} + +CHGraph::~CHGraph() +{ + for (CHGraph::iterator it = this->begin(), eit = this->end(); it != eit; ++it) + delete it->second; +} + +void CHGraph::buildCHG() +{ + + double timeStart, timeEnd; + timeStart = CLOCK_IN_MS(); + for (u32_t i = 0; i < LLVMModuleSet::getLLVMModuleSet()->getModuleNum(); ++i) + { + Module *M = LLVMModuleSet::getLLVMModuleSet()->getModule(i); + assert(M && "module not found?"); + DBOUT(DGENERAL, outs() << SVFUtil::pasMsg("construct CHGraph From module " + + M->getName().str() + "...\n")); + readInheritanceMetadataFromModule(*M); + for (Module::const_global_iterator I = M->global_begin(), E = M->global_end(); I != E; ++I) + buildCHGNodes(&(*I)); + for (Module::const_iterator F = M->begin(), E = M->end(); F != E; ++F) { + if ((*F).getName() == "switch_view") { + continue; + } + buildCHGNodes(getDefFunForMultipleModule(&(*F))); + } + for (Module::const_iterator F = M->begin(), E = M->end(); F != E; ++F) { + if ((*F).getName() == "switch_view") { + continue; + } + buildCHGEdges(getDefFunForMultipleModule(&(*F))); + } + + analyzeVTables(*M); + } + + DBOUT(DGENERAL, outs() << SVFUtil::pasMsg("build Internal Maps ...\n")); + buildInternalMaps(); + + timeEnd = CLOCK_IN_MS(); + buildingCHGTime = (timeEnd - timeStart) / TIMEINTERVAL; + + if (Options::DumpCHA) + dump("cha"); +} + +void CHGraph::buildCHGNodes(const GlobalValue *globalvalue) +{ + if (isValVtbl(globalvalue) && globalvalue->getNumOperands() > 0) + { + const ConstantStruct *vtblStruct = SVFUtil::dyn_cast(globalvalue->getOperand(0)); + assert(vtblStruct && "Initializer of a vtable not a struct?"); + string className = getClassNameFromVtblObj(globalvalue); + if (!getNode(className)) + createNode(className); + + for (unsigned int ei = 0; ei < vtblStruct->getNumOperands(); ++ei) + { + const ConstantArray *vtbl = SVFUtil::dyn_cast(vtblStruct->getOperand(ei)); + assert(vtbl && "Element of initializer not an array?"); + for (u32_t i = 0; i < vtbl->getNumOperands(); ++i) + { + if(const ConstantExpr *ce = isCastConstantExpr(vtbl->getOperand(i))) + { + const Value *bitcastValue = ce->getOperand(0); + if (const Function* func = SVFUtil::dyn_cast(bitcastValue)) + { + struct DemangledName dname = demangle(func->getName().str()); + if (!getNode(dname.className)) + createNode(dname.className); + } + } + } + } + } +} + +void CHGraph::buildCHGNodes(const SVFFunction* fun) +{ + const Function* F = fun->getLLVMFun(); + if (isConstructor(F) || isDestructor(F)) + { + struct DemangledName dname = demangle(F->getName().str()); + DBOUT(DCHA, outs() << "\t build CHANode for class " + dname.className + "...\n"); + if (!getNode(dname.className)) + createNode(dname.className); + } +} + +void CHGraph::buildCHGEdges(const SVFFunction* fun) +{ + const Function* F = fun->getLLVMFun(); + + if (isConstructor(F) || isDestructor(F)) + { + for (Function::const_iterator B = F->begin(), E = F->end(); B != E; ++B) + { + for (BasicBlock::const_iterator I = B->begin(), E = B->end(); I != E; ++I) + { + if (SVFUtil::isCallSite(&(*I))) + { + CallSite cs = SVFUtil::getLLVMCallSite(&(*I)); + connectInheritEdgeViaCall(fun, cs); + } + else if (const StoreInst *store = SVFUtil::dyn_cast(&(*I))) + { + connectInheritEdgeViaStore(fun, store); + } + } + } + } +} + + +void CHGraph::buildInternalMaps() +{ + buildClassNameToAncestorsDescendantsMap(); + buildVirtualFunctionToIDMap(); + buildCSToCHAVtblsAndVfnsMap(); +} + +void CHGraph::connectInheritEdgeViaCall(const SVFFunction* callerfun, CallSite cs) +{ + if (getCallee(cs) == nullptr) + return; + + const Function* callee = getCallee(cs)->getLLVMFun(); + const Function* caller = callerfun->getLLVMFun(); + + struct DemangledName dname = demangle(caller->getName().str()); + if ((isConstructor(caller) && isConstructor(callee)) || (isDestructor(caller) && isDestructor(callee))) + { + if (cs.arg_size() < 1 || (cs.arg_size() < 2 && cs.paramHasAttr(0, llvm::Attribute::StructRet))) + return; + const Value *csThisPtr = getVCallThisPtr(cs); + //const Argument *consThisPtr = getConstructorThisPtr(caller); + //bool samePtr = isSameThisPtrInConstructor(consThisPtr, csThisPtr); + bool samePtrTrue = true; + if (csThisPtr != nullptr && samePtrTrue) + { + struct DemangledName basename = demangle(callee->getName().str()); + if (!SVFUtil::isCallSite(csThisPtr) && + basename.className.size() > 0) + { + addEdge(dname.className, basename.className, CHEdge::INHERITANCE); + } + } + } +} + +void CHGraph::connectInheritEdgeViaStore(const SVFFunction* caller, const StoreInst* storeInst) +{ + struct DemangledName dname = demangle(caller->getName().str()); + if (const ConstantExpr *ce = SVFUtil::dyn_cast(storeInst->getValueOperand())) + { + if (ce->getOpcode() == Instruction::BitCast) + { + const Value *bitcastval = ce->getOperand(0); + if (const ConstantExpr *bcce = SVFUtil::dyn_cast(bitcastval)) + { + if (bcce->getOpcode() == Instruction::GetElementPtr) + { + const Value *gepval = bcce->getOperand(0); + if (isValVtbl(gepval)) + { + string vtblClassName = getClassNameFromVtblObj(gepval); + if (vtblClassName.size() > 0 && dname.className.compare(vtblClassName) != 0) + { + addEdge(dname.className, vtblClassName, CHEdge::INHERITANCE); + } + } + } + } + } + } +} + +void CHGraph::readInheritanceMetadataFromModule(const Module &M) +{ + for (Module::const_named_metadata_iterator mdit = M.named_metadata_begin(), + mdeit = M.named_metadata_end(); mdit != mdeit; ++mdit) + { + const NamedMDNode *md = &*mdit; + string mdname = md->getName().str(); + if (mdname.compare(0, 15, "__cxx_bases_of_") != 0) + continue; + string className = mdname.substr(15); + for (NamedMDNode::const_op_iterator opit = md->op_begin(), + opeit = md->op_end(); opit != opeit; ++opit) + { + const MDNode *N = *opit; + const MDString &mdstr = SVFUtil::cast(N->getOperand(0)); + string baseName = mdstr.getString().str(); + addEdge(className, baseName, CHEdge::INHERITANCE); + } + } +} + +void CHGraph::addEdge(const string className, const string baseClassName, + CHEdge::CHEDGETYPE edgeType) +{ + CHNode *srcNode = getNode(className); + CHNode *dstNode = getNode(baseClassName); + assert(srcNode && dstNode && "node not found?"); + + if (!hasEdge(srcNode, dstNode, edgeType)) + { + CHEdge *edge = new CHEdge(srcNode, dstNode, edgeType); + srcNode->addOutgoingEdge(edge); + dstNode->addIncomingEdge(edge); + } +} + +CHNode *CHGraph::getNode(const string name) const +{ + auto chNode = classNameToNodeMap.find(name); + if (chNode != classNameToNodeMap.end()) return chNode->second; + else return nullptr; +} + + +CHNode *CHGraph::createNode(const std::string className) +{ + assert(!getNode(className) && "this node should never be created before!"); + CHNode * node = new CHNode(className, classNum++); + classNameToNodeMap[className] = node; + addGNode(node->getId(), node); + if (className.size() > 0 && className[className.size() - 1] == '>') + { + string templateName = getBeforeBrackets(className); + CHNode* templateNode = getNode(templateName); + if (!templateNode) + { + DBOUT(DCHA, outs() << "\t Create Template CHANode " + templateName + " for class " + className + "...\n"); + templateNode = createNode(templateName); + templateNode->setTemplate(); + } + addEdge(className, templateName, CHEdge::INSTANTCE); + addInstances(templateName,node); + } + return node; +} + +/* + * build the following two maps: + * classNameToDescendantsMap + * classNameToAncestorsMap + */ +void CHGraph::buildClassNameToAncestorsDescendantsMap() +{ + + for (CHGraph::const_iterator it = this->begin(), eit = this->end(); + it != eit; ++it) + { + const CHNode *node = it->second; + WorkList worklist; + CHNodeSetTy visitedNodes; + worklist.push(node); + while (!worklist.empty()) + { + const CHNode *curnode = worklist.pop(); + if (visitedNodes.find(curnode) == visitedNodes.end()) + { + for (CHEdge::CHEdgeSetTy::const_iterator it = + curnode->getOutEdges().begin(), eit = + curnode->getOutEdges().end(); it != eit; ++it) + { + if ((*it)->getEdgeType() == CHEdge::INHERITANCE) + { + CHNode *succnode = (*it)->getDstNode(); + classNameToAncestorsMap[node->getName()].insert(succnode); + classNameToDescendantsMap[succnode->getName()].insert(node); + worklist.push(succnode); + } + } + visitedNodes.insert(curnode); + } + } + } +} + + +const CHGraph::CHNodeSetTy& CHGraph::getInstancesAndDescendants(const string className) +{ + + NameToCHNodesMap::const_iterator it = classNameToInstAndDescsMap.find(className); + if (it != classNameToInstAndDescsMap.end()) + { + return it->second; + } + else + { + classNameToInstAndDescsMap[className] = getDescendants(className); + if (getNode(className)->isTemplate()) + { + const CHNodeSetTy& instances = getInstances(className); + for (CHNodeSetTy::const_iterator it = instances.begin(), eit = instances.end(); it != eit; ++it) + { + const CHNode *node = *it; + classNameToInstAndDescsMap[className].insert(node); + const CHNodeSetTy& instance_descendants = getDescendants(node->getName()); + for (CHNodeSetTy::const_iterator dit = + instance_descendants.begin(), deit = + instance_descendants.end(); dit != deit; ++dit) + { + classNameToInstAndDescsMap[className].insert(*dit); + } + } + } + return classNameToInstAndDescsMap[className]; + } +} + + +void CHGraph::addFuncToFuncVector(CHNode::FuncVector &v, const SVFFunction *f) { + const auto *lf = f->getLLVMFun(); + if (isCPPThunkFunction(lf)) { + if(const auto *tf = getThunkTarget(lf)) + v.push_back(svfMod->getSVFFunction(tf)); + } else { + v.push_back(f); + } +} + + +/* + * do the following things: + * 1. initialize virtualFunctions for each class + * 2. mark multi-inheritance classes + * 3. mark pure abstract classes + * + * Layout of VTables: + * + * 1. single inheritance + * class A {...}; + * class B: public A {...}; + * B's vtable: {i8 *null, _ZTI1B, ...} + * + * 2. normal multiple inheritance + * class A {...}; + * class B {...}; + * class C: public A, public B {...}; + * C's vtable: {i8 *null, _ZTI1C, ..., inttoptr xxx, _ZTI1C, ...} + * "inttoptr xxx" servers as a delimiter for dividing virtual methods inherited + * from "class A" and "class B" + * + * 3. virtual diamond inheritance + * class A {...}; + * class B: public virtual A {...}; + * class C: public virtual A {...}; + * class D: public B, public C {...}; + * D's vtable: {i8 *null, _ZTI1C, ..., inttoptr xxx, _ZTI1C, i8 *null, ...} + * there will several "i8 *null" following "inttoptr xxx, _ZTI1C,", and the + * number of "i8 *null" is the same as the number of virtual methods in + * "class A" + */ +void CHGraph::analyzeVTables(const Module &M) +{ + for (Module::const_global_iterator I = M.global_begin(), + E = M.global_end(); I != E; ++I) + { + const GlobalValue *globalvalue = SVFUtil::dyn_cast(&(*I)); + if (isValVtbl(globalvalue) && globalvalue->getNumOperands() > 0) + { + const ConstantStruct *vtblStruct = + SVFUtil::dyn_cast(globalvalue->getOperand(0)); + assert(vtblStruct && "Initializer of a vtable not a struct?"); + + string vtblClassName = getClassNameFromVtblObj(globalvalue); + CHNode *node = getNode(vtblClassName); + assert(node && "node not found?"); + + node->setVTable(globalvalue); + + for (unsigned int ei = 0; ei < vtblStruct->getNumOperands(); ++ei) + { + const ConstantArray *vtbl = + SVFUtil::dyn_cast(vtblStruct->getOperand(ei)); + assert(vtbl && "Element of initializer not an array?"); + + /* + * items in vtables can be classified into 3 categories: + * 1. i8* null + * 2. i8* inttoptr xxx + * 3. i8* bitcast xxx + */ + bool pure_abstract = true; + u32_t i = 0; + while (i < vtbl->getNumOperands()) + { + CHNode::FuncVector virtualFunctions; + bool is_virtual = false; // virtual inheritance + int null_ptr_num = 0; + for (; i < vtbl->getNumOperands(); ++i) + { + if (SVFUtil::isa(vtbl->getOperand(i))) + { + if (i > 0 && !SVFUtil::isa(vtbl->getOperand(i-1))) + { + const ConstantExpr *ce = + SVFUtil::dyn_cast(vtbl->getOperand(i-1)); + if (ce->getOpcode() == Instruction::BitCast) + { + const Value *bitcastValue = ce->getOperand(0); + string bitcastValueName = bitcastValue->getName().str(); + if (bitcastValueName.compare(0, ztiLabel.size(), ztiLabel) == 0) + { + is_virtual = true; + null_ptr_num = 1; + while (i+null_ptr_num < vtbl->getNumOperands()) + { + if (SVFUtil::isa(vtbl->getOperand(i+null_ptr_num))) + null_ptr_num++; + else + break; + } + } + } + } + continue; + } + const ConstantExpr *ce = + SVFUtil::dyn_cast(vtbl->getOperand(i)); + assert(ce != nullptr && "item in vtable not constantexp or null"); + u32_t opcode = ce->getOpcode(); + assert(opcode == Instruction::IntToPtr || + opcode == Instruction::BitCast); + assert(ce->getNumOperands() == 1 && + "inttptr or bitcast operand num not 1"); + if (opcode == Instruction::IntToPtr) + { + node->setMultiInheritance(); + ++i; + break; + } + if (opcode == Instruction::BitCast) + { + const Value *bitcastValue = ce->getOperand(0); + string bitcastValueName = bitcastValue->getName().str(); + /* + * value in bitcast: + * _ZTIXXX + * Function + * GlobalAlias (alias to other function) + */ + assert(SVFUtil::isa(bitcastValue) || + SVFUtil::isa(bitcastValue)); + if (const Function* f = SVFUtil::dyn_cast(bitcastValue)) + { + const SVFFunction* func = LLVMModuleSet::getLLVMModuleSet()->getSVFFunction(f); + addFuncToFuncVector(virtualFunctions, func); + if (func->getName().str().compare(pureVirtualFunName) == 0) + { + pure_abstract &= true; + } + else + { + pure_abstract &= false; + } + struct DemangledName dname = demangle(func->getName().str()); + if (dname.className.size() > 0 && + vtblClassName.compare(dname.className) != 0) + { + addEdge(vtblClassName, dname.className, CHEdge::INHERITANCE); + } + } + else + { + if (const GlobalAlias *alias = + SVFUtil::dyn_cast(bitcastValue)) + { + const Constant *aliasValue = alias->getAliasee(); + if (const Function* aliasFunc = + SVFUtil::dyn_cast(aliasValue)) + { + const SVFFunction* func = LLVMModuleSet::getLLVMModuleSet()->getSVFFunction(aliasFunc); + addFuncToFuncVector(virtualFunctions, func); + } + else if (const ConstantExpr *aliasconst = + SVFUtil::dyn_cast(aliasValue)) + { + u32_t aliasopcode = aliasconst->getOpcode(); + assert(aliasopcode == Instruction::BitCast && + "aliased constantexpr in vtable not a bitcast"); + const Function* aliasbitcastfunc = + SVFUtil::dyn_cast(aliasconst->getOperand(0)); + assert(aliasbitcastfunc && + "aliased bitcast in vtable not a function"); + const SVFFunction* func = LLVMModuleSet::getLLVMModuleSet()->getSVFFunction(aliasbitcastfunc); + addFuncToFuncVector(virtualFunctions, func); + } + else + { + assert(false && "alias not function or bitcast"); + } + + pure_abstract &= false; + } + else if (bitcastValueName.compare(0, ztiLabel.size(), + ztiLabel) == 0) + { + } + else + { + assert("what else can be in bitcast of a vtable?"); + } + } + } + } + if (is_virtual && virtualFunctions.size() > 0) + { + for (int i = 0; i < null_ptr_num; ++i) + { + const SVFFunction* fun = virtualFunctions[i]; + virtualFunctions.insert(virtualFunctions.begin(), fun); + } + } + if (virtualFunctions.size() > 0) + node->addVirtualFunctionVector(virtualFunctions); + } + if (pure_abstract == true) + { + node->setPureAbstract(); + } + } + } + } +} + + +void CHGraph::buildVirtualFunctionToIDMap() +{ + /* + * 1. Divide classes into groups + * 2. Get all virtual functions in a group + * 3. Assign consecutive IDs to virtual functions that have + * the same name (after demangling) in a group + */ + CHNodeSetTy visitedNodes; + for (CHGraph::const_iterator nit = this->begin(), + neit = this->end(); nit != neit; ++nit) + { + CHNode *node = nit->second; + if (visitedNodes.find(node) != visitedNodes.end()) + continue; + + string className = node->getName(); + + /* + * get all the classes in a specific group + */ + CHNodeSetTy group; + stack nodeStack; + nodeStack.push(node); + while (!nodeStack.empty()) + { + const CHNode *curnode = nodeStack.top(); + nodeStack.pop(); + group.insert(curnode); + if (visitedNodes.find(curnode) != visitedNodes.end()) + continue; + for (CHEdge::CHEdgeSetTy::const_iterator it = curnode->getOutEdges().begin(), + eit = curnode->getOutEdges().end(); it != eit; ++it) + { + CHNode *tmpnode = (*it)->getDstNode(); + nodeStack.push(tmpnode); + group.insert(tmpnode); + } + for (CHEdge::CHEdgeSetTy::const_iterator it = curnode->getInEdges().begin(), + eit = curnode->getInEdges().end(); it != eit; ++it) + { + CHNode *tmpnode = (*it)->getSrcNode(); + nodeStack.push(tmpnode); + group.insert(tmpnode); + } + visitedNodes.insert(curnode); + } + + /* + * get all virtual functions in a specific group + */ + set virtualFunctions; + for (CHNodeSetTy::iterator it = group.begin(), + eit = group.end(); it != eit; ++it) + { + const vector &vecs = (*it)->getVirtualFunctionVectors(); + for (vector::const_iterator vit = vecs.begin(), + veit = vecs.end(); vit != veit; ++vit) + { + for (vector::const_iterator fit = (*vit).begin(), + feit = (*vit).end(); fit != feit; ++fit) + { + virtualFunctions.insert(*fit); + } + } + } + + /* + * build a set of pairs of demangled function name and function in a + * specific group, items in the set will be sort by the first item of the + * pair, so all the virtual functions in a group will be sorted by the + * demangled function name + * + * + * + * + * + * <~A, A::~A> + * <~B, B::~B> + * <~C, C::~C> + * ... + */ + set> fNameSet; + for (set::iterator fit = virtualFunctions.begin(), + feit = virtualFunctions.end(); fit != feit; ++fit) + { + const SVFFunction* f = *fit; + struct DemangledName dname = demangle(f->getName().str()); + fNameSet.insert(pair(dname.funcName, f)); + } + for (set>::iterator it = fNameSet.begin(), + eit = fNameSet.end(); it != eit; ++it) + { + virtualFunctionToIDMap[it->second] = vfID++; + } + } +} + +const CHGraph::CHNodeSetTy& CHGraph::getCSClasses(CallSite cs) +{ + assert(isVirtualCallSite(cs) && "not virtual callsite!"); + + CallSiteToCHNodesMap::const_iterator it = csToClassesMap.find(cs); + if (it != csToClassesMap.end()) + { + return it->second; + } + else + { + string thisPtrClassName = getClassNameOfThisPtr(cs); + if (const CHNode* thisNode = getNode(thisPtrClassName)) + { + const CHNodeSetTy& instAndDesces = getInstancesAndDescendants(thisPtrClassName); + csToClassesMap[cs].insert(thisNode); + for (CHNodeSetTy::const_iterator it = instAndDesces.begin(), eit = instAndDesces.end(); it != eit; ++it) + csToClassesMap[cs].insert(*it); + } + return csToClassesMap[cs]; + } +} + +static bool checkArgTypes(CallSite cs, const Function *fn) { + + // here we skip the first argument (i.e., this pointer) + u32_t arg_size = (fn->arg_size() > cs.arg_size()) ? cs.arg_size(): fn->arg_size(); + if(arg_size > 1){ + for (unsigned i = 1; i < arg_size; i++) { + auto cs_arg = cs.getArgOperand(i); + auto fn_arg = fn->getArg(i); + if (cs_arg->getType() != fn_arg->getType()) { + return false; + } + } + } + + return true; +} + +/* + * Get virtual functions for callsite "cs" based on vtbls (calculated + * based on pointsto set) + */ +void CHGraph::getVFnsFromVtbls(CallSite cs, const VTableSet &vtbls, VFunSet &virtualFunctions) +{ + + /// get target virtual functions + size_t idx = getVCallIdx(cs); + /// get the function name of the virtual callsite + string funName = getFunNameOfVCallSite(cs); + for (const GlobalValue *vt : vtbls) + { + const CHNode *child = getNode(getClassNameFromVtblObj(vt)); + if (child == nullptr) + continue; + CHNode::FuncVector vfns; + child->getVirtualFunctions(idx, vfns); + for (CHNode::FuncVector::const_iterator fit = vfns.begin(), + feit = vfns.end(); fit != feit; ++fit) + { + const SVFFunction* callee = *fit; + if (cs.arg_size() == callee->arg_size() || + (cs.getFunctionType()->isVarArg() && callee->isVarArg())) + { + + // if argument types do not match + // skip this one + if (!checkArgTypes(cs, callee->getLLVMFun())) + continue; + + DemangledName dname = demangle(callee->getName().str()); + string calleeName = dname.funcName; + + /* + * The compiler will add some special suffix (e.g., + * "[abi:cxx11]") to the end of some virtual function: + * In dealII + * function: FE_Q<3>::get_name + * will be mangled as: _ZNK4FE_QILi3EE8get_nameB5cxx11Ev + * after demangling: FE_Q<3>::get_name[abi:cxx11] + * The special suffix ("[abi:cxx11]") needs to be removed + */ + const std::string suffix("[abi:cxx11]"); + size_t suffix_pos = calleeName.rfind(suffix); + if (suffix_pos != string::npos) + calleeName.erase(suffix_pos, suffix.size()); + + /* + * if we can't get the function name of a virtual callsite, all virtual + * functions calculated by idx will be valid + */ + if (funName.size() == 0) + { + virtualFunctions.insert(callee); + } + else if (funName[0] == '~') + { + /* + * if the virtual callsite is calling a destructor, then all + * destructors in the ch will be valid + * class A { virtual ~A(){} }; + * class B: public A { virtual ~B(){} }; + * int main() { + * A *a = new B; + * delete a; /// the function name of this virtual callsite is ~A() + * } + */ + if (calleeName[0] == '~') + { + virtualFunctions.insert(callee); + } + } + else + { + /* + * for other virtual function calls, the function name of the callsite + * and the function name of the target callee should match exactly + */ + if (funName.compare(calleeName) == 0) + { + virtualFunctions.insert(callee); + } + } + } + } + } +} + +void CHGraph::buildCSToCHAVtblsAndVfnsMap() +{ + + for (SymbolTableInfo::CallSiteSet::const_iterator it = + SymbolTableInfo::SymbolInfo()->getCallSiteSet().begin(), eit = + SymbolTableInfo::SymbolInfo()->getCallSiteSet().end(); it != eit; ++it) + { + CallSite cs = *it; + if (!cppUtil::isVirtualCallSite(cs)) + continue; + VTableSet vtbls; + const CHNodeSetTy& chClasses = getCSClasses(cs); + for (CHNodeSetTy::const_iterator it = chClasses.begin(), eit = chClasses.end(); it != eit; ++it) + { + const CHNode *child = *it; + const GlobalValue *vtbl = child->getVTable(); + if (vtbl != nullptr) + { + vtbls.insert(vtbl); + } + } + if (vtbls.size() > 0) + { + csToCHAVtblsMap[cs] = vtbls; + VFunSet virtualFunctions; + getVFnsFromVtbls(cs, vtbls, virtualFunctions); + if (virtualFunctions.size() > 0) + csToCHAVFnsMap[cs] = virtualFunctions; + } + } +} + +void CHGraph::printCH() +{ + for (CHGraph::const_iterator it = this->begin(), eit = this->end(); + it != eit; ++it) + { + const CHNode *node = it->second; + outs() << "class: " << node->getName() << "\n"; + for (CHEdge::CHEdgeSetTy::const_iterator it = node->OutEdgeBegin(); + it != node->OutEdgeEnd(); ++it) + { + if ((*it)->getEdgeType() == CHEdge::INHERITANCE) + outs() << (*it)->getDstNode()->getName() << " --inheritance--> " + << (*it)->getSrcNode()->getName() << "\n"; + else + outs() << (*it)->getSrcNode()->getName() << " --instance--> " + << (*it)->getDstNode()->getName() << "\n"; + } + } + outs() << '\n'; +} + +/*! + * Dump call graph into dot file + */ +void CHGraph::dump(const std::string& filename) +{ + GraphPrinter::WriteGraphToFile(outs(), filename, this); + printCH(); +} + +void CHGraph::view() +{ + llvm::ViewGraph(this, "Class Hierarchy Graph"); +} + +namespace llvm +{ + +/*! + * Write value flow graph into dot file for debugging + */ +template<> +struct DOTGraphTraits : public DefaultDOTGraphTraits +{ + + typedef CHNode NodeType; + DOTGraphTraits(bool isSimple = false) : + DefaultDOTGraphTraits(isSimple) + { + } + + /// Return name of the graph + static std::string getGraphName(CHGraph*) + { + return "Class Hierarchy Graph"; + } + /// Return function name; + static std::string getNodeLabel(CHNode *node, CHGraph*) + { + return node->getName(); + } + + static std::string getNodeAttributes(CHNode *node, CHGraph*) + { + if (node->isPureAbstract()) + { + return "shape=tab"; + } + else + return "shape=box"; + } + + template + static std::string getEdgeAttributes(CHNode*, EdgeIter EI, CHGraph*) + { + + CHEdge* edge = *(EI.getCurrent()); + assert(edge && "No edge found!!"); + if (edge->getEdgeType() == CHEdge::INHERITANCE) + { + return "style=solid"; + } + else + { + return "style=dashed"; + } + } +}; +} // End namespace llvm diff --git a/svf/lib/SVF-FE/CPPUtil.cpp b/svf/lib/SVF-FE/CPPUtil.cpp new file mode 100644 index 000000000..f957538c1 --- /dev/null +++ b/svf/lib/SVF-FE/CPPUtil.cpp @@ -0,0 +1,569 @@ +//===- CPPUtil.cpp -- Base class of pointer analyses -------------------------// +// +// SVF: Static Value-Flow Analysis +// +// Copyright (C) <2013-2017> +// + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// +//===----------------------------------------------------------------------===// + +/* + * CPPUtil.cpp + * + * Created on: Apr 13, 2016 + * Author: Xiaokang Fan + */ + +#include "SVF-FE/CPPUtil.h" +#include "Util/SVFUtil.h" +#include "SVF-FE/LLVMUtil.h" + + +#include // for demangling + +using namespace std; +using namespace SVF; + +// label for global vtbl value before demangle +const string vtblLabelBeforeDemangle = "_ZTV"; + +// label for global vtbl value before demangle +const string vtblLabelAfterDemangle = "vtable for "; + +// label for virtual functions +const string vfunPreLabel = "_Z"; + +// label for multi inheritance virtual function +const string NVThunkFunLabel = "non-virtual thunk to "; +const string VThunkFuncLabel = "virtual thunk to "; + +const string clsName = "class."; +const string structName = "struct."; + +static bool isOperOverload(const string name) +{ + s32_t leftnum = 0, rightnum = 0; + string subname = name; + size_t leftpos, rightpos; + leftpos = subname.find("<"); + while (leftpos != string::npos) + { + subname = subname.substr(leftpos+1); + leftpos = subname.find("<"); + leftnum++; + } + subname = name; + rightpos = subname.find(">"); + while (rightpos != string::npos) + { + subname = subname.substr(rightpos+1); + rightpos = subname.find(">"); + rightnum++; + } + if (leftnum != rightnum) + { + return true; + } + else + { + return false; + } +} + +static string getBeforeParenthesis(const string &name) +{ + size_t lastRightParen = name.rfind(")"); + assert(lastRightParen > 0); + + s32_t paren_num = 1, pos; + for (pos = lastRightParen - 1; pos >= 0; pos--) + { + if (name[pos] == ')') + paren_num++; + if (name[pos] == '(') + paren_num--; + if (paren_num == 0) + break; + } + return name.substr(0, pos); +} + +string cppUtil::getBeforeBrackets(const string &name) +{ + if (name.size() == 0 || name[name.size() - 1] != '>') + { + return name; + } + s32_t bracket_num = 1, pos; + for (pos = name.size() - 2; pos >= 0; pos--) + { + if (name[pos] == '>') + bracket_num++; + if (name[pos] == '<') + bracket_num--; + if (bracket_num == 0) + break; + } + return name.substr(0, pos); +} + +bool cppUtil::isValVtbl(const Value *val) +{ + if (!SVFUtil::isa(val)) + return false; + string valName = val->getName().str(); + if (valName.compare(0, vtblLabelBeforeDemangle.size(), + vtblLabelBeforeDemangle) == 0) + return true; + else + return false; +} + +static void handleThunkFunction(cppUtil::DemangledName &dname) { + // when handling multi-inheritance, + // the compiler may generate thunk functions + // to perform `this` pointer adjustment + // they are indicated with `virtual thunk to ` + // and `nun-virtual thunk to`. + // if the classname starts with part of a + // demangled name starts with + // these prefixes, we need to remove the prefix + // to get the real class name + + static vector thunkPrefixes = {VThunkFuncLabel, NVThunkFunLabel}; + for (unsigned i = 0; i < thunkPrefixes.size(); i++) { + auto prefix = thunkPrefixes[i]; + if (dname.className.size() > prefix.size() && + dname.className.compare(0, prefix.size(), prefix) == 0) + { + dname.className = dname.className.substr(prefix.size()); + dname.isThunkFunc = true; + return; + + } + } +} + +/* + * input: _ZN**** + * after abi::__cxa_demangle: + * namespace::A<...::...>::f<...::...>(...) + * ^ + * delimiter + * + * step1: getBeforeParenthesis + * namespace::A<...::...>::f<...::...> + * + * step2: getBeforeBrackets + * namespace::A<...::...>::f + * + * step3: find delimiter + * namespace::A<...::...>:: + * ^ + * + * className: namespace::A<...::...> + * functionName: f<...::...> + */ + +struct cppUtil::DemangledName cppUtil::demangle(const string &name) +{ + struct cppUtil::DemangledName dname; + dname.isThunkFunc = false; + + s32_t status; + char *realname = abi::__cxa_demangle(name.c_str(), 0, 0, &status); + if (realname == nullptr) + { + dname.className = ""; + dname.funcName = ""; + } + else + { + string realnameStr = string(realname); + string beforeParenthesis = getBeforeParenthesis(realnameStr); + if (beforeParenthesis.find("::") == string::npos || + isOperOverload(beforeParenthesis)) + { + dname.className = ""; + dname.funcName = ""; + } + else + { + string beforeBracket = getBeforeBrackets(beforeParenthesis); + size_t colon = beforeBracket.rfind("::"); + if (colon == string::npos) + { + dname.className = ""; + dname.funcName = ""; + } + else + { + dname.className = beforeParenthesis.substr(0, colon); + dname.funcName = beforeParenthesis.substr(colon + 2); + } + } + } + + handleThunkFunction(dname); + + return dname; +} + +bool cppUtil::isLoadVtblInst(const LoadInst *loadInst) +{ + const Value *loadSrc = loadInst->getPointerOperand(); + const Type *valTy = loadSrc->getType(); + const Type *elemTy = valTy; + for (s32_t i = 0; i < 3; ++i) + { + if (const PointerType *ptrTy = SVFUtil::dyn_cast(elemTy)) + elemTy = ptrTy->getElementType(); + else + return false; + } + if (const FunctionType *functy = SVFUtil::dyn_cast(elemTy)) + { + const Type *paramty = functy->getParamType(0); + string className = cppUtil::getClassNameFromType(paramty); + if (className.size() > 0) + { + return true; + } + } + return false; +} + +/* + * a virtual callsite follows the following instruction sequence pattern: + * %vtable = load this + * %vfn = getelementptr %vtable, idx + * %x = load %vfn + * call %x (this) + */ +bool cppUtil::isVirtualCallSite(CallSite cs) +{ + // the callsite must be an indirect one with at least one argument (this ptr) + if (cs.getCalledFunction() != nullptr || cs.arg_empty()) + return false; + + // When compiled with ctir, we'd be using the DCHG which has its own + // virtual annotations. + if (LLVMModuleSet::getLLVMModuleSet()->allCTir()) + { + return cs.getInstruction()->getMetadata(cppUtil::ctir::derefMDName) != nullptr; + } + + const Value *vfunc = cs.getCalledValue(); + if (const LoadInst *vfuncloadinst = SVFUtil::dyn_cast(vfunc)) + { + const Value *vfuncptr = vfuncloadinst->getPointerOperand(); + if (const GetElementPtrInst *vfuncptrgepinst = + SVFUtil::dyn_cast(vfuncptr)) + { + if (vfuncptrgepinst->getNumIndices() != 1) + return false; + const Value *vtbl = vfuncptrgepinst->getPointerOperand(); + if (SVFUtil::isa(vtbl)) + { + return true; + } + } + } + return false; +} + +bool cppUtil::isCPPThunkFunction(const Function *F) { + cppUtil::DemangledName dname = cppUtil::demangle(F->getName().str()); + return dname.isThunkFunc; +} + +const Function *cppUtil::getThunkTarget(const Function *F) { + const Function *ret = nullptr; + + for (auto &bb:*F) { + for (auto &inst: bb) { + if (llvm::isa(inst) || llvm::isa(inst) + || llvm::isa(inst)) { + CallSite cs(const_cast(&inst)); + // assert(cs.getCalledFunction() && + // "Indirect call detected in thunk func"); + // assert(ret == nullptr && "multiple callsites in thunk func"); + + ret = cs.getCalledFunction(); + } + } + } + + return ret; +} + +const Value *cppUtil::getVCallThisPtr(CallSite cs) +{ + if (cs.paramHasAttr(0, llvm::Attribute::StructRet)) + { + return cs.getArgument(1); + } + else + { + return cs.getArgument(0); + } +} + +/*! + * Given a inheritance relation B is a child of A + * We assume B::B(thisPtr1){ A::A(thisPtr2) } such that thisPtr1 == thisPtr2 + * In the following code thisPtr1 is "%class.B1* %this" and thisPtr2 is "%class.A* %0". + * +define linkonce_odr dso_local void @B1::B1()(%class.B1* %this) unnamed_addr #6 comdat + %this.addr = alloca %class.B1*, align 8 + store %class.B1* %this, %class.B1** %this.addr, align 8 + %this1 = load %class.B1*, %class.B1** %this.addr, align 8 + %0 = bitcast %class.B1* %this1 to %class.A* + call void @A::A()(%class.A* %0) + */ +bool cppUtil::isSameThisPtrInConstructor(const Argument* thisPtr1, const Value* thisPtr2) +{ + if (thisPtr1 == thisPtr2) + { + return true; + } + else + { + for (const User *thisU : thisPtr1->users()) + { + if (const StoreInst *store = SVFUtil::dyn_cast(thisU)) + { + for (const User *storeU : store->getPointerOperand()->users()) + { + if (const LoadInst *load = SVFUtil::dyn_cast(storeU)) + { + if(load->getNextNode() && SVFUtil::isa(load->getNextNode())) + return SVFUtil::cast(load->getNextNode()) == (thisPtr2->stripPointerCasts()); + } + } + } + } + return false; + } +} + +const Argument *cppUtil::getConstructorThisPtr(const Function* fun) +{ + assert((isConstructor(fun) || isDestructor(fun)) && "not a constructor?"); + assert(fun->arg_size() >=1 && "argument size >= 1?"); + const Argument* thisPtr = &*(fun->arg_begin()); + return thisPtr; +} + +/* + * get the ptr "vtable" for a given virtual callsite: + * %vtable = load ... + * %vfn = getelementptr %vtable, idx + * %x = load %vfn + * call %x (...) + */ +const Value *cppUtil::getVCallVtblPtr(CallSite cs) +{ + const LoadInst *loadInst = SVFUtil::dyn_cast(cs.getCalledValue()); + assert(loadInst != nullptr); + const Value *vfuncptr = loadInst->getPointerOperand(); + const GetElementPtrInst *gepInst = SVFUtil::dyn_cast(vfuncptr); + assert(gepInst != nullptr); + const Value *vtbl = gepInst->getPointerOperand(); + return vtbl; +} + +u64_t cppUtil::getVCallIdx(CallSite cs) +{ + const LoadInst *vfuncloadinst = SVFUtil::dyn_cast(cs.getCalledValue()); + assert(vfuncloadinst != nullptr); + const Value *vfuncptr = vfuncloadinst->getPointerOperand(); + const GetElementPtrInst *vfuncptrgepinst = + SVFUtil::dyn_cast(vfuncptr); + User::const_op_iterator oi = vfuncptrgepinst->idx_begin(); + const ConstantInt *idx = SVFUtil::dyn_cast(&(*oi)); + u64_t idx_value; + if (idx == nullptr) + { + SVFUtil::errs() << "vcall gep idx not constantint\n"; + idx_value = 0; + } + else + idx_value = idx->getSExtValue(); + return idx_value; +} + +string cppUtil::getClassNameFromType(const Type *ty) +{ + string className = ""; + if (const PointerType *ptrType = SVFUtil::dyn_cast(ty)) + { + const Type *elemType = ptrType->getElementType(); + if (SVFUtil::isa(elemType) && + !((SVFUtil::cast(elemType))->isLiteral())) + { + string elemTypeName = elemType->getStructName().str(); + if (elemTypeName.compare(0, clsName.size(), clsName) == 0) + { + className = elemTypeName.substr(clsName.size()); + } + else if (elemTypeName.compare(0, structName.size(), structName) == 0) + { + className = elemTypeName.substr(structName.size()); + } + } + } + return className; +} + +string cppUtil::getClassNameFromVtblObj(const Value *value) +{ + string className = ""; + + string vtblName = value->getName().str(); + s32_t status; + char *realname = abi::__cxa_demangle(vtblName.c_str(), 0, 0, &status); + if (realname != nullptr) + { + string realnameStr = string(realname); + if (realnameStr.compare(0, vtblLabelAfterDemangle.size(), + vtblLabelAfterDemangle) == 0) + { + className = realnameStr.substr(vtblLabelAfterDemangle.size()); + } + } + return className; +} + +bool cppUtil::isConstructor(const Function *F) +{ + if (F->isDeclaration()) + return false; + string funcName = F->getName().str(); + if (funcName.compare(0, vfunPreLabel.size(), vfunPreLabel) != 0) + { + return false; + } + struct cppUtil::DemangledName dname = demangle(funcName.c_str()); + if (dname.className.size() == 0) { + return false; + } + dname.funcName = getBeforeBrackets(dname.funcName); + dname.className = getBeforeBrackets(dname.className); + size_t colon = dname.className.rfind("::"); + if (colon == string::npos) + { + dname.className = getBeforeBrackets(dname.className); + } + else + { + dname.className = getBeforeBrackets(dname.className.substr(colon+2)); + } + if (dname.className.size() > 0 && (dname.className.compare(dname.funcName) == 0)) + /// TODO: on mac os function name is an empty string after demangling + return true; + else + return false; +} + +bool cppUtil::isDestructor(const Function *F) +{ + if (F->isDeclaration()) + return false; + string funcName = F->getName().str(); + if (funcName.compare(0, vfunPreLabel.size(), vfunPreLabel) != 0) + { + return false; + } + struct cppUtil::DemangledName dname = demangle(funcName.c_str()); + if (dname.className.size() == 0) { + return false; + } + dname.funcName = getBeforeBrackets(dname.funcName); + dname.className = getBeforeBrackets(dname.className); + size_t colon = dname.className.rfind("::"); + if (colon == string::npos) + { + dname.className = getBeforeBrackets(dname.className); + } + else + { + dname.className = getBeforeBrackets(dname.className.substr(colon+2)); + } + if (dname.className.size() > 0 && dname.funcName.size() > 0 && + dname.className.size() + 1 == dname.funcName.size() && + dname.funcName.compare(0, 1, "~") == 0 && + dname.className.compare(dname.funcName.substr(1)) == 0) + return true; + else + return false; +} + +string cppUtil::getClassNameOfThisPtr(CallSite cs) +{ + string thisPtrClassName; + Instruction *inst = cs.getInstruction(); + if (const MDNode *N = inst->getMetadata("VCallPtrType")) + { + const MDString &mdstr = SVFUtil::cast((N->getOperand(0))); + thisPtrClassName = mdstr.getString().str(); + } + if (thisPtrClassName.size() == 0) + { + const Value *thisPtr = getVCallThisPtr(cs); + thisPtrClassName = getClassNameFromType(thisPtr->getType()); + } + + size_t found = thisPtrClassName.find_last_not_of("0123456789"); + if (found != string::npos) + { + if (found != thisPtrClassName.size() - 1 && thisPtrClassName[found] == '.') + { + return thisPtrClassName.substr(0, found); + } + } + + return thisPtrClassName; +} + +string cppUtil::getFunNameOfVCallSite(CallSite cs) +{ + string funName; + Instruction *inst = cs.getInstruction(); + if (const MDNode *N = inst->getMetadata("VCallFunName")) + { + const MDString &mdstr = SVFUtil::cast((N->getOperand(0))); + funName = mdstr.getString().str(); + } + return funName; +} + + +/* + * Is this virtual call inside its own constructor or destructor? + */ +bool cppUtil::VCallInCtorOrDtor(CallSite cs) +{ + std::string classNameOfThisPtr = getClassNameOfThisPtr(cs); + const Function *func = cs.getCaller(); + if (isConstructor(func) || isDestructor(func)) + { + struct DemangledName dname = demangle(func->getName().str()); + if (classNameOfThisPtr.compare(dname.className) == 0) + return true; + } + return false; +} diff --git a/svf/lib/SVF-FE/CallGraphBuilder.cpp b/svf/lib/SVF-FE/CallGraphBuilder.cpp new file mode 100644 index 000000000..ad9c8bbf7 --- /dev/null +++ b/svf/lib/SVF-FE/CallGraphBuilder.cpp @@ -0,0 +1,136 @@ +//===- CallGraphBuilder.cpp ----------------------------------------------------------------// +// +// SVF: Static Value-Flow Analysis +// +// Copyright (C) <2013-> +// + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// +//===----------------------------------------------------------------------===// + + +/* + * CallGraphBuilder.cpp + * + * Created on: + * Author: Yulei + */ + +#include "SVF-FE/LLVMUtil.h" +#include "SVF-FE/CallGraphBuilder.h" +#include "Graphs/ICFG.h" + +using namespace SVF; +using namespace SVFUtil; + +PTACallGraph* CallGraphBuilder::buildCallGraph(SVFModule* svfModule) +{ + /// create nodes + for (SVFModule::llvm_iterator F = svfModule->llvmFunBegin(), E = svfModule->llvmFunEnd(); F != E; ++F) + { + const SVFFunction* fun = LLVMModuleSet::getLLVMModuleSet()->getSVFFunction(*F); + callgraph->addCallGraphNode(fun); + } + + /// create edges + for (SVFModule::llvm_iterator F = svfModule->llvmFunBegin(), E = svfModule->llvmFunEnd(); F != E; ++F) + { + Function *fun = *F; + for (inst_iterator I = inst_begin(*fun), J = inst_end(*fun); I != J; ++I) + { + const Instruction *inst = &*I; + if (SVFUtil::isNonInstricCallSite(inst)) + { + if(const SVFFunction* callee = getCallee(inst)) + { + const CallBlockNode* callBlockNode = icfg->getCallBlockNode(inst); + const SVFFunction* caller = LLVMModuleSet::getLLVMModuleSet()->getSVFFunction(fun); + callgraph->addDirectCallGraphEdge(callBlockNode,caller,callee); + } + } + } + } + + return callgraph; +} + +PTACallGraph* ThreadCallGraphBuilder::buildThreadCallGraph(SVFModule* svfModule) +{ + + buildCallGraph(svfModule); + + ThreadCallGraph* cg = dyn_cast(callgraph); + assert(cg && "not a thread callgraph?"); + + ThreadAPI* tdAPI = ThreadAPI::getThreadAPI(); + for (SVFModule::llvm_const_iterator fi = svfModule->llvmFunBegin(), efi = svfModule->llvmFunEnd(); fi != efi; ++fi) + { + const Function *fun = *fi; + for (const_inst_iterator II = inst_begin(*fun), E = inst_end(*fun); II != E; ++II) + { + const Instruction *inst = &*II; + if (tdAPI->isTDFork(inst)) + { + const CallBlockNode* cs = icfg->getCallBlockNode(inst); + cg->addForksite(cs); + const Function* forkee = SVFUtil::dyn_cast(tdAPI->getForkedFun(inst)); + if (forkee) + { + cg->addDirectForkEdge(cs); + } + // indirect call to the start routine function + else + { + cg->addThreadForkEdgeSetMap(cs,nullptr); + } + } + else if (tdAPI->isHareParFor(inst)) + { + const CallBlockNode* cs = icfg->getCallBlockNode(inst); + cg->addParForSite(cs); + const Function* taskFunc = SVFUtil::dyn_cast(tdAPI->getTaskFuncAtHareParForSite(inst)); + if (taskFunc) + { + cg->addDirectParForEdge(cs); + } + // indirect call to the start routine function + else + { + cg->addHareParForEdgeSetMap(cs,nullptr); + } + } + } + } + // record join sites + for (SVFModule::llvm_const_iterator fi = svfModule->llvmFunBegin(), efi = svfModule->llvmFunEnd(); fi != efi; ++fi) + { + const Function *fun = *fi; + for (const_inst_iterator II = inst_begin(*fun), E = inst_end(*fun); II != E; ++II) + { + const Instruction *inst = &*II; + if (tdAPI->isTDJoin(inst)) + { + const CallBlockNode* cs = icfg->getCallBlockNode(inst); + cg->addJoinsite(cs); + } + } + } + + return cg; +} + + + + diff --git a/svf/lib/SVF-FE/DCHG.cpp b/svf/lib/SVF-FE/DCHG.cpp new file mode 100644 index 000000000..c74ddc5be --- /dev/null +++ b/svf/lib/SVF-FE/DCHG.cpp @@ -0,0 +1,1265 @@ +//===----- DCHG.cpp CHG using DWARF debug info ------------------------// +// +/* + * DCHG.cpp + * + * Created on: Aug 24, 2019 + * Author: Mohamad Barbar + */ + +#include + +#include "Util/Options.h" +#include "SVF-FE/DCHG.h" +#include "SVF-FE/CPPUtil.h" +#include "Util/SVFUtil.h" + +#include "llvm/IR/DebugInfo.h" + +using namespace SVF; + + +void DCHGraph::handleDIBasicType(const DIBasicType *basicType) +{ + getOrCreateNode(basicType); +} + +void DCHGraph::handleDICompositeType(const DICompositeType *compositeType) +{ + switch (compositeType->getTag()) + { + case dwarf::DW_TAG_array_type: + if (extended) getOrCreateNode(compositeType); + gatherAggs(compositeType); + break; + case dwarf::DW_TAG_class_type: + case dwarf::DW_TAG_structure_type: + getOrCreateNode(compositeType); + // If we're extending, we need to add the first-field relation. + if (extended) + { + DINodeArray fields = compositeType->getElements(); + if (!fields.empty()) + { + // We want the first non-static, non-function member; it may not exist. + DIDerivedType *firstMember = nullptr; + for (DINode *n : fields) + { + if (DIDerivedType *fm = SVFUtil::dyn_cast(n)) + { + if (fm->getTag() == dwarf::DW_TAG_member && !fm->isStaticMember()) + { + firstMember = fm; + break; + } + } + } + + if (firstMember != nullptr) + { + // firstMember is a DW_TAG_member, we want the base type beneath it. + addEdge(compositeType, firstMember->getBaseType(), DCHEdge::FIRST_FIELD); + } + } + } + + flatten(compositeType); + gatherAggs(compositeType); + + break; + case dwarf::DW_TAG_union_type: + getOrCreateNode(compositeType); + // All fields are first fields. + if (extended) + { + DINodeArray fields = compositeType->getElements(); + for (DINode *field : fields) + { + // fields[0] gives a type which is DW_TAG_member, we want the member's type (getBaseType). + DIDerivedType *firstMember = SVFUtil::dyn_cast(field); + assert(firstMember != nullptr && "DCHG: expected member type"); + addEdge(compositeType, firstMember->getBaseType(), DCHEdge::FIRST_FIELD); + } + } + + // flatten(compositeType); + gatherAggs(compositeType); + + break; + case dwarf::DW_TAG_enumeration_type: + getOrCreateNode(compositeType); + break; + default: + assert(false && "DCHGraph::buildCHG: unexpected CompositeType tag."); + } +} + +void DCHGraph::handleDIDerivedType(const DIDerivedType *derivedType) +{ + switch (derivedType->getTag()) + { + case dwarf::DW_TAG_inheritance: + { + assert(SVFUtil::isa(derivedType->getScope()) && "inheriting from non-type?"); + DCHEdge *edge = addEdge(SVFUtil::dyn_cast(derivedType->getScope()), + derivedType->getBaseType(), DCHEdge::INHERITANCE); + // If the offset does not exist (for primary base), getOffset should return 0. + edge->setOffset(derivedType->getOffsetInBits()); + break; + } + case dwarf::DW_TAG_member: + case dwarf::DW_TAG_friend: + // Don't care. + break; + case dwarf::DW_TAG_typedef: + handleTypedef(derivedType); + break; + case dwarf::DW_TAG_pointer_type: + case dwarf::DW_TAG_ptr_to_member_type: + case dwarf::DW_TAG_reference_type: + case dwarf::DW_TAG_rvalue_reference_type: + if (extended) getOrCreateNode(derivedType); + break; + case dwarf::DW_TAG_const_type: + case dwarf::DW_TAG_atomic_type: + case dwarf::DW_TAG_volatile_type: + case dwarf::DW_TAG_restrict_type: + break; + default: + assert(false && "DCHGraph::buildCHG: unexpected DerivedType tag."); + } +} + +void DCHGraph::handleDISubroutineType(const DISubroutineType *subroutineType) +{ + getOrCreateNode(subroutineType); +} + +void DCHGraph::handleTypedef(const DIType *typedefType) +{ + assert(typedefType && typedefType->getTag() == dwarf::DW_TAG_typedef); + + // Need to gather them in a set first because we don't know the base type till + // we get to the bottom of the (potentially many) typedefs. + std::vector typedefs; + // Check for nullptr because you can typedef void. + while (typedefType != nullptr && typedefType->getTag() == dwarf::DW_TAG_typedef) + { + const DIDerivedType *typedefDerivedType = SVFUtil::dyn_cast(typedefType); + // The typedef itself. + typedefs.push_back(typedefDerivedType); + + // Next in the typedef line. + typedefType = typedefDerivedType->getBaseType(); + } + + const DIType *baseType = typedefType; + DCHNode *baseTypeNode = getOrCreateNode(baseType); + + for (const DIDerivedType *tdef : typedefs) + { + // Base type needs to hold its typedefs. + baseTypeNode->addTypedef(tdef); + } +} + +void DCHGraph::buildVTables(const Module &module) +{ + for (Module::const_global_iterator gvI = module.global_begin(); gvI != module.global_end(); ++gvI) + { + // Though this will return more than GlobalVariables, we only care about GlobalVariables (for the vtbls). + const GlobalVariable *gv = SVFUtil::dyn_cast(&(*gvI)); + if (gv == nullptr) continue; + if (gv->hasMetadata(cppUtil::ctir::vtMDName) && gv->getNumOperands() > 0) + { + DIType *type = SVFUtil::dyn_cast(gv->getMetadata(cppUtil::ctir::vtMDName)); + assert(type && "DCHG::buildVTables: bad metadata for ctir.vt"); + DCHNode *node = getOrCreateNode(type); + node->setVTable(gv); + vtblToTypeMap[gv] = getCanonicalType(type); + + const ConstantStruct *vtbls = SVFUtil::dyn_cast(gv->getOperand(0)); + assert(vtbls && "unexpected vtable type"); + for (unsigned nthVtbl = 0; nthVtbl < vtbls->getNumOperands(); ++nthVtbl) + { + const ConstantArray *vtbl = SVFUtil::dyn_cast(vtbls->getOperand(nthVtbl)); + assert(vtbl && "Element of vtbl struct not an array"); + + std::vector &vfns = node->getVfnVector(nthVtbl); + + // Iterating over the vtbl, we will run into: + // 1. i8* null (don't care for now). + // 2. i8* inttoptr ... (don't care for now). + // 3. i8* bitcast ... (we only care when a function pointer is being bitcasted). + for (unsigned cN = 0; cN < vtbl->getNumOperands(); ++cN) + { + Constant *c = vtbl->getOperand(cN); + if (SVFUtil::isa(c)) + { + // Don't care for now. + continue; + } + + ConstantExpr *ce = SVFUtil::dyn_cast(c); + assert(ce && "non-ConstantExpr, non-ConstantPointerNull in vtable?"); + if (ce->getOpcode() == Instruction::BitCast) + { + // Could be a GlobalAlias which we don't care about, or a virtual/thunk function. + const Function *vfn = SVFUtil::dyn_cast(ce->getOperand(0)); + if (vfn != nullptr) + { + vfns.push_back(vfn); + } + } + } + } + } + } +} + +const NodeBS &DCHGraph::cha(const DIType *type, bool firstField) +{ + type = getCanonicalType(type); + Map &cacheMap = firstField ? chaFFMap : chaMap; + + // Check if we've already computed. + if (cacheMap.find(type) != cacheMap.end()) + { + return cacheMap[type]; + } + + NodeBS children; + const DCHNode *node = getOrCreateNode(type); + // Consider oneself a child, otherwise the recursion will just come up with nothing. + children.set(node->getId()); + for (const DCHEdge *edge : node->getInEdges()) + { + // Don't care about anything but inheritance, first-field, and standard def. edges. + if ( edge->getEdgeKind() != DCHEdge::INHERITANCE + && edge->getEdgeKind() != DCHEdge::FIRST_FIELD + && edge->getEdgeKind() != DCHEdge::STD_DEF) + { + continue; + } + + // We only care about first-field edges if the flag is on. + if (!firstField && edge->getEdgeKind() == DCHEdge::FIRST_FIELD) + { + continue; + } + + const NodeBS &cchildren = cha(edge->getSrcNode()->getType(), firstField); + // Children's children are my children. + for (NodeID cchild : cchildren) + { + children.set(cchild); + } + } + + // Cache results. + cacheMap.insert({type, children}); + // Return the permanent object; we're returning a reference. + return cacheMap[type]; +} + +void DCHGraph::flatten(const DICompositeType *type) +{ + type = SVFUtil::dyn_cast(getCanonicalType(type)); + assert(type && "DCHG::flatten: canon type of struct/class is not struct/class"); + if (fieldTypes.find(type) != fieldTypes.end()) + { + // Already done (necessary because of the recursion). + return; + } + + // Create empty vector. + fieldTypes[type]; + + assert(type != nullptr + && (type->getTag() == dwarf::DW_TAG_class_type + || type->getTag() == dwarf::DW_TAG_structure_type) + && "DCHG::flatten: expected a class/struct"); + + // Sort the fields from getElements. Especially a problem for classes; it's all jumbled up. + std::vector fields; + DINodeArray fieldsDINA = type->getElements(); + for (unsigned i = 0; i < fieldsDINA.size(); ++i) + { + if (const DIDerivedType *dt = SVFUtil::dyn_cast(fieldsDINA[i])) + { + // Don't care about subprograms, only member/inheritance. + fields.push_back(dt); + } + } + + // TODO: virtual inheritance is not handled at all! + std::sort(fields.begin(), fields.end(), + [](const DIDerivedType *&a, const DIDerivedType *&b) -> bool + { return a->getOffsetInBits() < b->getOffsetInBits(); }); + + for (const DIDerivedType *mt : fields) + { + assert((mt->getTag() == dwarf::DW_TAG_member || mt->getTag() == dwarf::DW_TAG_inheritance) + && "DCHG: expected member/inheritance"); + // Either we have a class, struct, array, or something not in need of flattening. + const DIType *fieldType = mt->getBaseType(); + if (fieldType->getTag() == dwarf::DW_TAG_structure_type + || fieldType->getTag() == dwarf::DW_TAG_class_type) + { + flatten(SVFUtil::dyn_cast(fieldType)); + for (const DIType *ft : fieldTypes[fieldType]) + { + // ft is already a canonical type because the "root" additions insert + // canonical types. + fieldTypes[type].push_back(ft); + } + } + else if (fieldType->getTag() == dwarf::DW_TAG_array_type) + { + const DICompositeType *arrayType = SVFUtil::dyn_cast(fieldType); + const DIType *baseType = arrayType->getBaseType(); + if (const DICompositeType *cbt = SVFUtil::dyn_cast(baseType)) + { + flatten(cbt); + for (const DIType *ft : fieldTypes[cbt]) + { + // ft is already a canonical type like above. + fieldTypes[type].push_back(ft); + } + } + else + { + fieldTypes[type].push_back(getCanonicalType(baseType)); + } + } + else + { + fieldTypes[type].push_back(getCanonicalType(fieldType)); + } + } +} + +bool DCHGraph::isAgg(const DIType *t) +{ + if (t == nullptr) return false; + return t->getTag() == dwarf::DW_TAG_array_type + || t->getTag() == dwarf::DW_TAG_structure_type + || t->getTag() == dwarf::DW_TAG_class_type; +} + +void DCHGraph::gatherAggs(const DICompositeType *type) +{ + if (containingAggs.find(getCanonicalType(type)) != containingAggs.end()) + { + return; + } + + // Initialise an empty set. We want all aggregates to have a value in + // this map, even if empty (e.g. struct has no aggs, only scalars). + containingAggs[getCanonicalType(type)]; + + if (type->getTag() == dwarf::DW_TAG_array_type) + { + const DIType *bt = type->getBaseType(); + bt = stripQualifiers(bt); + + if (isAgg(bt)) + { + const DICompositeType *cbt = SVFUtil::dyn_cast(bt); + containingAggs[getCanonicalType(type)].insert(getCanonicalType(cbt)); + gatherAggs(cbt); + // These must be canonical already because of aggs.insert above/below. + containingAggs[getCanonicalType(type)].insert( + containingAggs[getCanonicalType(cbt)].begin(), + containingAggs[getCanonicalType(cbt)].end()); + } + } + else + { + DINodeArray fields = type->getElements(); + for (unsigned i = 0; i < fields.size(); ++i) + { + // Unwrap the member (could be a subprogram, not type, so guard needed). + if (const DIDerivedType *mt = SVFUtil::dyn_cast(fields[i])) + { + const DIType *ft = mt->getBaseType(); + ft = stripQualifiers(ft); + + if (isAgg(ft)) + { + const DICompositeType *cft = SVFUtil::dyn_cast(ft); + containingAggs[getCanonicalType(type)].insert(getCanonicalType(cft)); + gatherAggs(cft); + // These must be canonical already because of aggs.insert above. + containingAggs[getCanonicalType(type)].insert( + containingAggs[getCanonicalType(cft)].begin(), + containingAggs[getCanonicalType(cft)].end()); + } + } + } + } +} + +DCHNode *DCHGraph::getOrCreateNode(const DIType *type) +{ + type = getCanonicalType(type); + + // Check, does the node for type exist? + if (diTypeToNodeMap[type] != nullptr) + { + return diTypeToNodeMap[type]; + } + + DCHNode *node = new DCHNode(type, numTypes++); + addGNode(node->getId(), node); + diTypeToNodeMap[type] = node; + // TODO: handle templates. + + return node; +} + +DCHEdge *DCHGraph::addEdge(const DIType *t1, const DIType *t2, DCHEdge::GEdgeKind et) +{ + DCHNode *src = getOrCreateNode(t1); + DCHNode *dst = getOrCreateNode(t2); + + DCHEdge *edge = hasEdge(t1, t2, et); + if (edge == nullptr) + { + // Create a new edge. + edge = new DCHEdge(src, dst, et); + src->addOutgoingEdge(edge); + dst->addIncomingEdge(edge); + } + + return edge; +} + +DCHEdge *DCHGraph::hasEdge(const DIType *t1, const DIType *t2, DCHEdge::GEdgeKind et) +{ + DCHNode *src = getOrCreateNode(t1); + DCHNode *dst = getOrCreateNode(t2); + + for (DCHEdge *edge : src->getOutEdges()) + { + DCHNode *node = edge->getDstNode(); + DCHEdge::GEdgeKind edgeType = edge->getEdgeKind(); + if (node == dst && edgeType == et) + { + assert(SVFUtil::isa(edge) && "Non-DCHEdge in DCHNode edge set."); + return edge; + } + } + + return nullptr; +} + +void DCHGraph::buildCHG(bool extend) +{ + extended = extend; + DebugInfoFinder finder; + for (u32_t i = 0; i < LLVMModuleSet::getLLVMModuleSet()->getModuleNum(); ++i) + { + finder.processModule(*(LLVMModuleSet::getLLVMModuleSet()->getModule(i))); + } + + // Create the void node regardless of whether it appears. + getOrCreateNode(nullptr); + // Find any char type. + const DIType *charType = nullptr; + /* + * We want void at the top, char as a child, and everything is a child of char: + * void + * | + * char + * / | \ + * x y z + */ + + + for (const DIType *type : finder.types()) + { + if (const DIBasicType *basicType = SVFUtil::dyn_cast(type)) + { + if (basicType->getEncoding() == dwarf::DW_ATE_unsigned_char + || basicType->getEncoding() == dwarf::DW_ATE_signed_char) + { + charType = type; + } + + handleDIBasicType(basicType); + } + else if (const DICompositeType *compositeType = SVFUtil::dyn_cast(type)) + { + handleDICompositeType(compositeType); + } + else if (const DIDerivedType *derivedType = SVFUtil::dyn_cast(type)) + { + handleDIDerivedType(derivedType); + } + else if (const DISubroutineType *subroutineType = SVFUtil::dyn_cast(type)) + { + handleDISubroutineType(subroutineType); + } + else + { + assert(false && "DCHGraph::buildCHG: unexpected DIType."); + } + } + + for (u32_t i = 0; i < LLVMModuleSet::getLLVMModuleSet()->getModuleNum(); ++i) + { + buildVTables(*(LLVMModuleSet::getLLVMModuleSet()->getModule(i))); + } + + // Build the void/char/everything else relation. + if (extended && charType != nullptr) + { + // void <-- char + addEdge(charType, nullptr, DCHEdge::STD_DEF); + // char <-- x, char <-- y, ... + for (iterator nodeI = begin(); nodeI != end(); ++nodeI) + { + // Everything without a parent gets char as a parent. + if (nodeI->second->getType() != nullptr + && nodeI->second->getOutEdges().size() == 0) + { + addEdge(nodeI->second->getType(), charType, DCHEdge::STD_DEF); + } + } + } + + if (Options::PrintDCHG) + { + print(); + } +} + +const VFunSet &DCHGraph::getCSVFsBasedonCHA(CallSite cs) +{ + if (csCHAMap.find(cs) != csCHAMap.end()) + { + return csCHAMap[cs]; + } + + VFunSet vfns; + const VTableSet &vtbls = getCSVtblsBasedonCHA(cs); + getVFnsFromVtbls(cs, vtbls, vfns); + + // Cache. + csCHAMap.insert({cs, vfns}); + // Return cached object, not the stack object. + return csCHAMap[cs]; +} + +const VTableSet &DCHGraph::getCSVtblsBasedonCHA(CallSite cs) +{ + const DIType *type = getCanonicalType(getCSStaticType(cs)); + // Check if we've already computed. + if (vtblCHAMap.find(type) != vtblCHAMap.end()) + { + return vtblCHAMap[type]; + } + + VTableSet vtblSet; + const NodeBS &children = cha(type, false); + for (NodeID childId : children) + { + DCHNode *child = getGNode(childId); + const GlobalValue *vtbl = child->getVTable(); + // TODO: what if it is null? + if (vtbl != nullptr) + { + vtblSet.insert(vtbl); + } + } + + // Cache. + vtblCHAMap.insert({type, vtblSet}); + // Return cached version - not the stack object. + return vtblCHAMap[type]; +} + +void DCHGraph::getVFnsFromVtbls(CallSite cs, const VTableSet &vtbls, VFunSet &virtualFunctions) +{ + size_t idx = cppUtil::getVCallIdx(cs); + std::string funName = cppUtil::getFunNameOfVCallSite(cs); + for (const GlobalValue *vtbl : vtbls) + { + assert(vtblToTypeMap.find(vtbl) != vtblToTypeMap.end() && "floating vtbl"); + const DIType *type = vtblToTypeMap[vtbl]; + assert(hasNode(type) && "trying to get vtbl for type not in graph"); + const DCHNode *node = getNode(type); + std::vector> allVfns = node->getVfnVectors(); + for (std::vector vfnV : allVfns) + { + // We only care about any virtual function corresponding to idx. + if (idx >= vfnV.size()) + { + continue; + } + + const Function *callee = vfnV[idx]; + // Practically a copy of that in lib/MemoryModel/CHA.cpp + if (cs.arg_size() == callee->arg_size() || (cs.getFunctionType()->isVarArg() && callee->isVarArg())) + { + cppUtil::DemangledName dname = cppUtil::demangle(callee->getName().str()); + std::string calleeName = dname.funcName; + + /* + * The compiler will add some special suffix (e.g., + * "[abi:cxx11]") to the end of some virtual function: + * In dealII + * function: FE_Q<3>::get_name + * will be mangled as: _ZNK4FE_QILi3EE8get_nameB5cxx11Ev + * after demangling: FE_Q<3>::get_name[abi:cxx11] + * The special suffix ("[abi:cxx11]") needs to be removed + */ + const std::string suffix("[abi:cxx11]"); + size_t suffixPos = calleeName.rfind(suffix); + if (suffixPos != std::string::npos) + { + calleeName.erase(suffixPos, suffix.size()); + } + + /* + * if we can't get the function name of a virtual callsite, all virtual + * functions corresponding to idx will be valid + */ + if (funName.size() == 0) + { + virtualFunctions.insert(SVFUtil::getFunction(callee->getName())); + } + else if (funName[0] == '~') + { + /* + * if the virtual callsite is calling a destructor, then all + * destructors in the ch will be valid + * class A { virtual ~A(){} }; + * class B: public A { virtual ~B(){} }; + * int main() { + * A *a = new B; + * delete a; /// the function name of this virtual callsite is ~A() + * } + */ + if (calleeName[0] == '~') + { + virtualFunctions.insert(SVFUtil::getFunction(callee->getName())); + } + } + else + { + /* + * For other virtual function calls, the function name of the callsite + * and the function name of the target callee should match exactly + */ + if (funName.compare(calleeName) == 0) + { + virtualFunctions.insert(SVFUtil::getFunction(callee->getName())); + } + } + } + } + } +} + +bool DCHGraph::isBase(const DIType *a, const DIType *b, bool firstField) +{ + a = getCanonicalType(a); + b = getCanonicalType(b); + assert(hasNode(a) && hasNode(b) && "DCHG: isBase query for non-existent node!"); + const DCHNode *bNode = getNode(b); + + const NodeBS &aChildren = cha(a, firstField); + return aChildren.test(bNode->getId()); +} + +bool DCHGraph::isFieldOf(const DIType *f, const DIType *b) +{ + assert(f && b && "DCHG::isFieldOf: given nullptr!"); + + f = getCanonicalType(f); + b = getCanonicalType(b); + if (f == b) return true; + + if (b->getTag() == dwarf::DW_TAG_array_type || b->getTag() == dwarf::DW_TAG_pointer_type) + { + const DIType *baseType = nullptr; + if (const DICompositeType *arrayType = SVFUtil::dyn_cast(b)) + { + baseType = arrayType->getBaseType(); + } + else if (const DIDerivedType *ptrType = SVFUtil::dyn_cast(b)) + { + baseType = ptrType->getBaseType(); + } + assert(baseType && "DCHG::isFieldOf: baseType is neither DIComposite nor DIDerived!"); + + baseType = getCanonicalType(baseType); + return f == baseType || (baseType != nullptr && isFieldOf(f, baseType)); + } + else if (b->getTag() == dwarf::DW_TAG_class_type + || b->getTag() == dwarf::DW_TAG_structure_type) + { + const std::vector &fields = getFieldTypes(b); + return std::find(fields.begin(), fields.end(), f) != fields.end(); + } + else + { + return false; + } +} + +const DIType *DCHGraph::getCanonicalType(const DIType *t) +{ + // We want stripped types to be canonical. + const DIType *unstrippedT = t; + t = stripQualifiers(t); + + // Is there a mapping for the unstripped type? Yes - return it. + if (canonicalTypeMap.find(unstrippedT) != canonicalTypeMap.end()) + { + return canonicalTypeMap[unstrippedT]; + } + + // There is no mapping for unstripped type (^), is there one for the stripped + // type? Yes - map the unstripped type to the same thing. + if (unstrippedT != t) + { + if (canonicalTypeMap.find(t) != canonicalTypeMap.end()) + { + canonicalTypeMap[unstrippedT] = canonicalTypeMap[t]; + return canonicalTypeMap[unstrippedT]; + } + } + + // Canonical type for t is not cached, find one for it. + for (const DIType *canonType : canonicalTypes) + { + if (teq(t, canonType)) + { + // Found a canonical type. + canonicalTypeMap[t] = canonType; + return canonicalTypeMap[t]; + } + } + + // No canonical type found, so t will be a canonical type. + canonicalTypes.insert(t); + canonicalTypeMap.insert({t, t}); + + return canonicalTypeMap[t]; +} + +const DIType *DCHGraph::stripQualifiers(const DIType *t) +{ + while (true) + { + // nullptr means void. + if (t == nullptr + || SVFUtil::isa(t) + || SVFUtil::isa(t)) + { + break; + } + + unsigned tag = t->getTag(); + // Verbose for clarity. + if ( tag == dwarf::DW_TAG_const_type + || tag == dwarf::DW_TAG_atomic_type + || tag == dwarf::DW_TAG_volatile_type + || tag == dwarf::DW_TAG_restrict_type + || tag == dwarf::DW_TAG_typedef) + { + // Qualifier - get underlying type. + const DIDerivedType *dt = SVFUtil::dyn_cast(t); + assert(t && "DCHG: expected DerivedType"); + t = dt->getBaseType(); + } + else if ( tag == dwarf::DW_TAG_array_type + || tag == dwarf::DW_TAG_class_type + || tag == dwarf::DW_TAG_structure_type + || tag == dwarf::DW_TAG_union_type + || tag == dwarf::DW_TAG_enumeration_type + || tag == dwarf::DW_TAG_member + || tag == dwarf::DW_TAG_pointer_type + || tag == dwarf::DW_TAG_ptr_to_member_type + || tag == dwarf::DW_TAG_reference_type + || tag == dwarf::DW_TAG_rvalue_reference_type) + { + // Hit a non-qualifier. + break; + } + else if ( tag == dwarf::DW_TAG_inheritance + || tag == dwarf::DW_TAG_friend) + { + assert(false && "DCHG: unexpected tag when stripping qualifiers"); + } + else + { + assert(false && "DCHG: unhandled tag when stripping qualifiers"); + } + } + + return t; +} + +const DIType *DCHGraph::stripArray(const DIType *t) +{ + t = stripQualifiers(t); + if (t->getTag() == dwarf::DW_TAG_array_type) + { + const DICompositeType *at = SVFUtil::dyn_cast(t); + return stripArray(at->getBaseType()); + } + + return t; +} + +bool DCHGraph::teq(const DIType *t1, const DIType *t2) +{ + t1 = stripQualifiers(t1); + t2 = stripQualifiers(t2); + + if (t1 == t2) + { + // Trivial case. Handles SubRoutineTypes too. + return true; + } + + if (t1 == nullptr || t2 == nullptr) + { + // Since t1 != t2 and one of them is null, it is + // impossible for them to be equal. + return false; + } + + // Check if we need base type comparisons. + if (SVFUtil::isa(t1) && SVFUtil::isa(t2)) + { + const DIBasicType *b1 = SVFUtil::dyn_cast(t1); + const DIBasicType *b2 = SVFUtil::dyn_cast(t2); + + unsigned enc1 = b1->getEncoding(); + unsigned enc2 = b2->getEncoding(); + bool okayEnc = ((enc1 == dwarf::DW_ATE_signed || enc1 == dwarf::DW_ATE_unsigned || enc1 == dwarf::DW_ATE_boolean) + && (enc2 == dwarf::DW_ATE_signed || enc2 == dwarf::DW_ATE_unsigned || enc2 == dwarf::DW_ATE_boolean)) + || + (enc1 == dwarf::DW_ATE_float && enc2 == dwarf::DW_ATE_float) + || + ((enc1 == dwarf::DW_ATE_signed_char || enc1 == dwarf::DW_ATE_unsigned_char) + && + (enc2 == dwarf::DW_ATE_signed_char || enc2 == dwarf::DW_ATE_unsigned_char)); + + if (!okayEnc) return false; + // Now we have split integers, floats, and chars, ignoring signedness. + + return t1->getSizeInBits() == t2->getSizeInBits() + && t1->getAlignInBits() == t2->getAlignInBits(); + } + + // Check, do we need to compare base types? + // This makes pointers, references, and arrays equivalent. + // Will handle member types. + if ((SVFUtil::isa(t1) || t1->getTag() == dwarf::DW_TAG_array_type) + && (SVFUtil::isa(t2) || t2->getTag() == dwarf::DW_TAG_array_type)) + { + const DIType *base1, *base2; + + // Set base1. + if (const DIDerivedType *d1 = SVFUtil::dyn_cast(t1)) + { + base1 = d1->getBaseType(); + } + else + { + const DICompositeType *c1 = SVFUtil::dyn_cast(t1); + assert(c1 && "teq: bad cast for array type"); + base1 = c1->getBaseType(); + } + + // Set base2. + if (const DIDerivedType *d2 = SVFUtil::dyn_cast(t2)) + { + base2 = d2->getBaseType(); + } + else + { + const DICompositeType *c2 = SVFUtil::dyn_cast(t2); + assert(c2 && "teq: bad cast for array type"); + base2 = c2->getBaseType(); + } + + // For ptr-to-member, there is some imprecision (but soundness) in + // that we don't check the class type. + return teq(base1, base2); + } + + if (SVFUtil::isa(t1) && SVFUtil::isa(t2)) + { + const DICompositeType *ct1 = SVFUtil::dyn_cast(t1); + const DICompositeType *ct2 = SVFUtil::dyn_cast(t2); + + if (ct1->getTag() != ct2->getTag()) return false; + + // Treat all enums the same. + if (ct1->getTag() == dwarf::DW_TAG_enumeration_type) + { + return true; + } + + // C++ classes? Check mangled name. + if (ct1->getTag() == dwarf::DW_TAG_class_type) + { + return ct1->getIdentifier() == ct2->getIdentifier(); + } + + // Either union or struct, simply test all fields are equal. + // Seems like it is enough to check it was defined in the same place. + // The elements sometimes differ but are referring to the same fields. + return ct1->getName() == ct2->getName() + && ct1->getFile() == ct2->getFile() + && ct1->getLine() == ct2->getLine(); + } + + // They were not equal base types (discounting signedness), nor were they + // "equal" pointers/references/arrays, nor were they the structurally equivalent, + // nor were they completely equal. + return false; +} + +bool DCHGraph::isFirstField(const DIType *f, const DIType *b) +{ + // TODO: some improvements. + // - cha should be changed to accept which edge types to use, + // then we can call cha(..., DCHEdge::FIRST_FIELD). + // - If not ^, this could benefit from caching. + f = getCanonicalType(f); + b = getCanonicalType(b); + + if (f == b) return true; + + const DCHNode *node = getNode(f); + assert(node && "DCHG::isFirstField: node not found"); + // Consider oneself a child, otherwise the recursion will just come up with nothing. + for (const DCHEdge *edge : node->getInEdges()) + { + // Only care about first-field edges. + if (edge->getEdgeKind() == DCHEdge::FIRST_FIELD) + { + if (edge->getSrcNode()->getType() == b) return true; + if (isFirstField(edge->getSrcNode()->getType(), b)) return true; + } + } + + return false; +} + +std::string DCHGraph::diTypeToStr(const DIType *t) +{ + std::stringstream ss; + + if (t == nullptr) + { + return "void"; + } + + if (const DIBasicType *bt = SVFUtil::dyn_cast(t)) + { + ss << std::string(bt->getName()); + } + else if (const DIDerivedType *dt = SVFUtil::dyn_cast(t)) + { + if (dt->getName() == "__vtbl_ptr_type") + { + ss << "(vtbl * =) __vtbl_ptr_type"; + } + else if (dt->getTag() == dwarf::DW_TAG_const_type) + { + ss << "const " << diTypeToStr(dt->getBaseType()); + } + else if (dt->getTag() == dwarf::DW_TAG_volatile_type) + { + ss << "volatile " << diTypeToStr(dt->getBaseType()); + } + else if (dt->getTag() == dwarf::DW_TAG_restrict_type) + { + ss << "restrict " << diTypeToStr(dt->getBaseType()); + } + else if (dt->getTag() == dwarf::DW_TAG_atomic_type) + { + ss << "atomic " << diTypeToStr(dt->getBaseType()); + } + else if (dt->getTag() == dwarf::DW_TAG_pointer_type) + { + ss << diTypeToStr(dt->getBaseType()) << " *"; + } + else if (dt->getTag() == dwarf::DW_TAG_ptr_to_member_type) + { + ss << diTypeToStr(dt->getBaseType()) + << " " << diTypeToStr(SVFUtil::dyn_cast(dt->getExtraData())) << "::*"; + } + else if (dt->getTag() == dwarf::DW_TAG_reference_type) + { + ss << diTypeToStr(dt->getBaseType()) << " &"; + } + else if (dt->getTag() == dwarf::DW_TAG_rvalue_reference_type) + { + ss << diTypeToStr(dt->getBaseType()) << " &&"; + } + else if (dt->getTag() == dwarf::DW_TAG_typedef) + { + ss << std::string(dt->getName()) << "->" << diTypeToStr(dt->getBaseType()); + } + } + else if (const DICompositeType *ct = SVFUtil::dyn_cast(t)) + { + if (ct->getTag() == dwarf::DW_TAG_class_type + || ct->getTag() == dwarf::DW_TAG_structure_type + || ct->getTag() == dwarf::DW_TAG_union_type) + { + + if (ct->getTag() == dwarf::DW_TAG_class_type) + { + ss << "class"; + } + else if (ct->getTag() == dwarf::DW_TAG_structure_type) + { + ss << "struct"; + } + else if (ct->getTag() == dwarf::DW_TAG_union_type) + { + ss << "union"; + } + + ss << "."; + + if (ct->getName() != "") + { + ss << std::string(ct->getName()); + } + else + { + // Iterate over the element types. + ss << "{ "; + + DINodeArray fields = ct->getElements(); + for (unsigned i = 0; i < fields.size(); ++i) + { + // fields[i] gives a type which is DW_TAG_member, we want the member's type (getBaseType). + // It can also give a Subprogram type if the class just had non-virtual functions. + if (const DISubprogram *sp = SVFUtil::dyn_cast(fields[i])) + { + ss << std::string(sp->getName()); + } + else if (const DIDerivedType *mt = SVFUtil::dyn_cast(fields[i])) + { + assert(mt->getTag() == dwarf::DW_TAG_member && "DCHG: expected member"); + ss << diTypeToStr(mt->getBaseType()); + } + + if (i != fields.size() - 1) + { + ss << ", "; + } + } + + ss << " }"; + } + } + else if (ct->getTag() == dwarf::DW_TAG_array_type) + { + ss << diTypeToStr(ct->getBaseType()); + DINodeArray sizes = ct->getElements(); + for (unsigned i = 0; i < sizes.size(); ++i) + { + DISubrange *sr = SVFUtil::dyn_cast(sizes[0]); + assert(sr != nullptr && "DCHG: non-subrange as array element?"); + int64_t count = -1; + if (const ConstantInt *ci = sr->getCount().dyn_cast()) + { + count = ci->getSExtValue(); + } + + ss << "[" << count << "]"; + } + } + else if (ct->getTag() == dwarf::DW_TAG_enumeration_type) + { + ss << "enum " << diTypeToStr(ct->getBaseType()); + } + else if (ct->getTag() == dwarf::DW_TAG_union_type) + { + + } + } + else if (const DISubroutineType *st = SVFUtil::dyn_cast(t)) + { + DITypeRefArray types = st->getTypeArray(); + // Must have one element at least (the first type). + ss << diTypeToStr(types[0]) << " fn("; + if (types.size() == 1) + { + ss << "void)"; + } + else + { + for (unsigned i = 1; i < types.size(); ++i) + { + ss << diTypeToStr(types[i]); + if (i + 1 != types.size()) + { + // There's another type. + ss << ", "; + } + } + + ss << ")"; + } + + ss << std::string(st->getName()); + } + + return ss.str(); +} + +static std::string indent(size_t n) +{ + return std::string(n, ' '); +} + +void DCHGraph::print(void) +{ + static const std::string line = "-------------------------------------\n"; + static const std::string thickLine = "=====================================\n"; + static const size_t singleIndent = 2; + + size_t currIndent = 0; + SVFUtil::outs() << thickLine; + unsigned numStructs = 0; + unsigned largestStruct = 0; + NodeSet nodes; + for (DCHGraph::const_iterator it = begin(); it != end(); ++it) + { + nodes.insert(it->first); + } + + for (NodeID id : nodes) + { + if (*nodes.begin() != id) + { + SVFUtil::outs() << line; + } + + const DCHNode *node = getGNode(id); + + SVFUtil::outs() << indent(currIndent) << id << ": " << diTypeToStr(node->getType()) << " [" << node->getType() << "]" << "\n"; + if (node->getType() != nullptr + && (node->getType()->getTag() == dwarf::DW_TAG_class_type + || node->getType()->getTag() == dwarf::DW_TAG_structure_type)) + { + ++numStructs; + unsigned numFields = getFieldTypes(node->getType()).size(); + largestStruct = numFields > largestStruct ? numFields : largestStruct; + } + + currIndent += singleIndent; + SVFUtil::outs() << indent(currIndent) << "Virtual functions\n"; + currIndent += singleIndent; + const std::vector> &vfnVectors = node->getVfnVectors(); + for (unsigned i = 0; i < vfnVectors.size(); ++i) + { + SVFUtil::outs() << indent(currIndent) << "[ vtable #" << i << " ]\n"; + currIndent += singleIndent; + for (unsigned j = 0; j < vfnVectors[i].size(); ++j) + { + struct cppUtil::DemangledName dname = cppUtil::demangle(vfnVectors[i][j]->getName().str()); + SVFUtil::outs() << indent(currIndent) << "[" << j << "] " + << dname.className << "::" << dname.funcName << "\n"; + } + + currIndent -= singleIndent; + } + + // Nothing was printed. + if (vfnVectors.size() == 0) + { + SVFUtil::outs() << indent(currIndent) << "(none)\n"; + } + + currIndent -= singleIndent; + + SVFUtil::outs() << indent(currIndent) << "Bases\n"; + currIndent += singleIndent; + for (const DCHEdge *edge : node->getOutEdges()) + { + std::string arrow; + if (edge->getEdgeKind() == DCHEdge::INHERITANCE) + { + arrow = "--inheritance-->"; + } + else if (edge->getEdgeKind() == DCHEdge::FIRST_FIELD) + { + arrow = "--first-field-->"; + } + else if (edge->getEdgeKind() == DCHEdge::INSTANCE) + { + arrow = "---instance---->"; + } + else if (edge->getEdgeKind() == DCHEdge::STD_DEF) + { + arrow = "---standard---->"; + } + else + { + arrow = "----unknown---->"; + } + + SVFUtil::outs() << indent(currIndent) << "[ " << diTypeToStr(node->getType()) << " ] " + << arrow << " [ " << diTypeToStr(edge->getDstNode()->getType()) << " ]\n"; + } + + if (node->getOutEdges().size() == 0) + { + SVFUtil::outs() << indent(currIndent) << "(none)\n"; + } + + currIndent -= singleIndent; + + SVFUtil::outs() << indent(currIndent) << "Typedefs\n"; + + currIndent += singleIndent; + + const Set &typedefs = node->getTypedefs(); + for (const DIDerivedType *tdef : typedefs) + { + std::string typedefName = "void"; + if (tdef != nullptr) + { + typedefName = tdef->getName().str(); + } + + SVFUtil::outs() << indent(currIndent) << typedefName << "\n"; + } + + if (typedefs.size() == 0) + { + SVFUtil::outs() << indent(currIndent) << "(none)\n"; + } + + currIndent -= singleIndent; + + currIndent -= singleIndent; + } + + SVFUtil::outs() << thickLine; + + SVFUtil::outs() << "Other stats\n"; + SVFUtil::outs() << line; + SVFUtil::outs() << "# Canonical types : " << canonicalTypes.size() << "\n"; + SVFUtil::outs() << "# structs : " << numStructs << "\n"; + SVFUtil::outs() << "Largest struct : " << largestStruct << " fields\n"; + SVFUtil::outs() << thickLine; + + SVFUtil::outs().flush(); +} diff --git a/svf/lib/SVF-FE/DataFlowUtil.cpp b/svf/lib/SVF-FE/DataFlowUtil.cpp new file mode 100644 index 000000000..6fbcee563 --- /dev/null +++ b/svf/lib/SVF-FE/DataFlowUtil.cpp @@ -0,0 +1,147 @@ +//===- DataFlowUtil.cpp -- Helper class for data-flow analysis----------------// +// +// SVF: Static Value-Flow Analysis +// +// Copyright (C) <2013-2017> +// + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// +//===----------------------------------------------------------------------===// + + +/* + * DataFlowUtil.cpp + * + * Created on: Jun 4, 2013 + * Author: Yulei Sui + */ + +#include "SVF-FE/DataFlowUtil.h" +#include "SVF-FE/LLVMModule.h" + +using namespace SVF; +using namespace llvm; + +char IteratedDominanceFrontier::ID = 0; +//static RegisterPass IDF("IDF", +// "IteratedDominanceFrontier Pass"); + +PTACFInfoBuilder::PTACFInfoBuilder() +{ +// Module* mod = LLVMModuleSet::getLLVMModuleSet()->getMainLLVMModule(); +// llvm::legacy::PassManager PM; +// PM.add(new llvm::PTACFInfoBuilderPass()); +// PM.run(*mod); +} + +PTACFInfoBuilder::~PTACFInfoBuilder(){ + for(FunToLoopInfoMap::iterator it = funToLoopInfoMap.begin(), eit = funToLoopInfoMap.end(); it!=eit; ++it) + { + if(it->second != nullptr) + { + delete it->second; + } + } + for(FunToDTMap::iterator it = funToDTMap.begin(), eit = funToDTMap.end(); it!=eit; ++it) + { + if(it->second != nullptr) + { + delete it->second; + } + } + for(FunToPostDTMap::iterator it = funToPDTMap.begin(), eit = funToPDTMap.end(); it!=eit; ++it) + { + if(it->second != nullptr) + { + delete it->second; + } + } +} + +/// Get loop info of a function +LoopInfo* PTACFInfoBuilder::getLoopInfo(const Function* f) +{ + assert(f->isDeclaration()==false && "external function (without body) does not have a loopInfo"); + Function* fun = const_cast(f); + FunToLoopInfoMap::iterator it = funToLoopInfoMap.find(fun); + if(it==funToLoopInfoMap.end()) + { + DominatorTree* dt = new DominatorTree(*fun); + LoopInfo* loopInfo = new LoopInfo(*dt); + funToLoopInfoMap[fun] = loopInfo; + return loopInfo; + } + else + return it->second; +} + +/// Get post dominator tree of a function +PostDominatorTree* PTACFInfoBuilder::getPostDT(const Function* f) +{ + assert(f->isDeclaration()==false && "external function (without body) does not have a PostDominatorTree"); + + Function* fun = const_cast(f); + if(f->isDeclaration()) + return nullptr; + FunToPostDTMap::iterator it = funToPDTMap.find(fun); + if(it==funToPDTMap.end()) + { + PostDominatorTree * PDT = new PostDominatorTree(*fun); + funToPDTMap[fun] = PDT; + return PDT; + } + else + return it->second; +} + +/// Get dominator tree of a function +DominatorTree* PTACFInfoBuilder::getDT(const Function* f) +{ + Function* fun = const_cast(f); + FunToDTMap::iterator it = funToDTMap.find(fun); + if(it==funToDTMap.end()) + { + DominatorTree* dt = new DominatorTree(*fun); + funToDTMap[fun] = dt; + return dt; + } + else + return it->second; +} + + +void IteratedDominanceFrontier::calculate(BasicBlock * bb, + const DominanceFrontier &DF) +{ + + DomSetType worklist; + + DominanceFrontierBase::const_iterator it = DF.find(bb); + assert(it != DF.end()); + + worklist.insert(it->second.begin(), it->second.end()); + while (!worklist.empty()) + { + BasicBlock *item = *worklist.begin(); + worklist.erase(worklist.begin()); + if (Frontiers[bb].find(item) == Frontiers[bb].end()) + { + Frontiers[bb].insert(item); + const_iterator parent = DF.find(item); + assert(parent != DF.end()); + worklist.insert(parent->second.begin(), parent->second.end()); + } + } +} diff --git a/svf/lib/SVF-FE/Graph2Json.cpp b/svf/lib/SVF-FE/Graph2Json.cpp new file mode 100644 index 000000000..54599f077 --- /dev/null +++ b/svf/lib/SVF-FE/Graph2Json.cpp @@ -0,0 +1,248 @@ +#include "llvm/Support/JSON.h" + +#include "SVF-FE/Graph2Json.h" + +#include // for ICFGBuilderFromFile +#include // for ICFGBuilderFromFile +#include // for ICFGBuilderFromFile + +using namespace std; +using namespace SVF; +using namespace SVFUtil; + +ICFGPrinter::ICFGPrinter() {} +void ICFGPrinter::printICFGToJson(const std::string& filename) +{ + outs() << "write symbols to '" << filename << "'..."; + std::error_code err; + ToolOutputFile F(filename.c_str(), err, llvm::sys::fs::F_None); + if (err) + { + outs() << " error opening file for writing!\n"; + F.os().clear_error(); + return; + } + + llvm::json::Array root_array; + for(ICFG::iterator it = begin(), eit = end(); it!=eit; ++it) + { + ICFGNode* node = it->second; + NodeID id = node->getId(); + llvm::json::Object ICFGNode_Obj; + ICFGNode_Obj["ICFG_ID"] = id; + ICFGNode_Obj["Node Type"] = getICFGKind(node->getNodeKind()); + if(IntraBlockNode* bNode = SVFUtil::dyn_cast(node)) + { + ICFGNode_Obj["Source Location"] = getSourceLoc(bNode->getInst()); + PAG::PAGEdgeList& edges = PAG::getPAG()->getInstPTAPAGEdgeList(bNode); + llvm::json::Array PAGEdge_array; + + //dump pag edges + for (PAG::PAGEdgeList::iterator it = edges.begin(), + eit = edges.end(); it != eit; ++it) + { + const PAGEdge* edge = *it; + llvm::json::Object edge_obj; + edge_obj["Source Node"] = edge->getSrcID(); + edge_obj["Destination Node"] = edge->getDstID(); + edge_obj["Source Type"] = getPAGNodeKindValue(edge->getSrcNode()->getNodeKind()); + edge_obj["Destination Type"] = getPAGNodeKindValue(edge->getDstNode()->getNodeKind()); + edge_obj["Edge Type"] = getPAGEdgeKindValue(edge->getEdgeKind()); + edge_obj["srcValueName"] = edge->getSrcNode()->getValueName(); + edge_obj["dstValueName"] = edge->getDstNode()->getValueName(); + if(edge->getEdgeKind()==PAGEdge::NormalGep) + { + const NormalGepPE* gepEdge = SVFUtil::cast(edge); + edge_obj["offset"] = gepEdge->getOffset(); + } + llvm::json::Value edge_value = llvm::json::Object{edge_obj}; + PAGEdge_array.push_back(edge_value); + } + llvm::json::Value PagEdge_value = llvm::json::Array{PAGEdge_array}; + ICFGNode_Obj["PAG Edges"] = PagEdge_value; + } + else if(FunEntryBlockNode* entry = SVFUtil::dyn_cast(node)) + { + if (isExtCall(entry->getFun())) + ICFGNode_Obj["isExtCall"] = true; + else + { + ICFGNode_Obj["isExtCall"] = false; + ICFGNode_Obj["Source Location"] = getSourceLoc(entry->getFun()->getLLVMFun()); + } + ICFGNode_Obj["Function Name"] = entry->getFun()->getName(); + } + else if (FunExitBlockNode* exit = SVFUtil::dyn_cast(node)) + { + if (isExtCall(exit->getFun())) + ICFGNode_Obj["isExtCall"] = true; + else + { + ICFGNode_Obj["isExtCall"] = false; + ICFGNode_Obj["Source Location"] = getSourceLoc(&(exit->getFun()->getLLVMFun()->back())); + } + ICFGNode_Obj["Function Name"] = exit->getFun()->getName(); + } + else if (CallBlockNode* call = SVFUtil::dyn_cast(node)) + { + ICFGNode_Obj["Source Location"] = getSourceLoc(call->getCallSite()); + } + else if (RetBlockNode* ret = SVFUtil::dyn_cast(node)) + { + ICFGNode_Obj["Source Location"] = getSourceLoc(ret->getCallSite()); + } + else + assert(false && "what else kinds of nodes do we have??"); + + llvm::json::Array ICFGEdges_array; + for(ICFGNode::iterator sit = node->OutEdgeBegin(), esit = node->OutEdgeEnd(); sit!=esit; ++sit) + { + ICFGEdge* edge = *sit; + llvm::json::Object ICFGEdge_obj; + if (SVFUtil::isa(edge)) + { + CallCFGEdge* call = SVFUtil::dyn_cast(edge); + ICFGEdge_obj["ICFG Edge Type"] = "CallCFGEdge"; + ICFGEdge_obj["ICFGEdgeSrcID"] = call->getSrcID(); + ICFGEdge_obj["ICFGEdgeDstID"] = call->getDstID(); + } + else if (SVFUtil::isa(edge)) + { + RetCFGEdge* call = SVFUtil::dyn_cast(edge); + ICFGEdge_obj["ICFG Edge Type"] = "RetCFGEdge"; + ICFGEdge_obj["ICFGEdgeSrcID"] = call->getSrcID(); + ICFGEdge_obj["ICFGEdgeDstID"] = call->getDstID(); + } + else if(SVFUtil::isa(edge)) + { + IntraCFGEdge* intraCFGEdge = SVFUtil::dyn_cast(edge); + ICFGEdge_obj["ICFG Edge Type"] = "IntraCFGEdge"; + ICFGEdge_obj["ICFGEdgeSrcID"] = intraCFGEdge->getSrcID(); + ICFGEdge_obj["ICFGEdgeDstID"] = intraCFGEdge->getDstID(); + } + llvm::json::Value ICFGEdge_Val = llvm::json::Object{ICFGEdge_obj}; + ICFGEdges_array.push_back(ICFGEdge_Val); + } + llvm::json::Value ICFGEdges_val = llvm::json::Array{ICFGEdges_array}; + ICFGNode_Obj["ICFGEdges"] = ICFGEdges_val; + llvm::json::Value ICFGNode_val = llvm::json::Object{ICFGNode_Obj}; + root_array.push_back(ICFGNode_val); + } + llvm::json::Value root_value = llvm::json::Array{root_array}; + llvm::json::operator<<(F.os(),root_value); + + F.os().close(); + if (!F.os().has_error()) + { + outs() << "\n"; + F.keep(); + return; + } +} + +std::string ICFGPrinter::getICFGKind(const int kind) +{ + switch(kind) + { + case ICFGNode::IntraBlock: + return "IntraBlock"; + break; + case ICFGNode::FunEntryBlock: + return "FunEntryBlock"; + break; + case ICFGNode::FunExitBlock: + return "FunExitBlock"; + break; + case ICFGNode::FunCallBlock: + return "FunCallBlock"; + break; + case ICFGNode::FunRetBlock: + return "FunRetBlock"; + break; + default: + return ""; + } + return ""; +} + +std::string ICFGPrinter::getPAGNodeKindValue(int kind) +{ + switch (kind) + { + case (PAGNode::ValNode): + return "ValNode"; + break; + case PAGNode::ObjNode: + return "ObjNode"; + break; + case PAGNode::RetNode: + return "RetNode"; + break; + case PAGNode::VarargNode: + return "VarargNode"; + break; + case PAGNode::GepValNode: + return "GepValNode"; + break; + case PAGNode::GepObjNode: + return "GepObjNode"; + break; + case PAGNode::FIObjNode: + return "FIObjNode"; + break; + case PAGNode::DummyValNode: + return "DummyValNode"; + break; + case PAGNode::DummyObjNode: + return "DummyObjNode"; + break; + } + return ""; +} + +std::string ICFGPrinter::getPAGEdgeKindValue(int kind) +{ + switch(kind) + { + case (PAGEdge::Addr): + return "Addr"; + break; + case (PAGEdge::Copy): + return "Copy"; + break; + case (PAGEdge::Store): + return "Store"; + break; + case (PAGEdge::Load): + return "Load"; + break; + case (PAGEdge::Call): + return "Call"; + break; + case (PAGEdge::Ret): + return "Ret"; + break; + case (PAGEdge::NormalGep): + return "NormalGep"; + break; + case (PAGEdge::VariantGep): + return "VariantGep"; + break; + case (PAGEdge::ThreadFork): + return "ThreadFork"; + break; + case (PAGEdge::ThreadJoin): + return "ThreadJoin"; + break; + case (PAGEdge::Cmp): + return "Cmp"; + break; + case (PAGEdge::BinaryOp): + return "BinaryOp"; + break; + case (PAGEdge::UnaryOp): + return "UnaryOp"; + break; + } + return ""; +} diff --git a/svf/lib/SVF-FE/HeapSimplifier.cpp b/svf/lib/SVF-FE/HeapSimplifier.cpp new file mode 100644 index 000000000..f5bd7e567 --- /dev/null +++ b/svf/lib/SVF-FE/HeapSimplifier.cpp @@ -0,0 +1,743 @@ +/* SVF - Static Value-Flow Analysis Framework +Copyright (C) 2015 Yulei Sui +Copyright (C) 2015 Jingling Xue + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +/* + * HeapSimplifier.cpp + * + * Created on: Oct 8, 2013 + */ + + + +#define DEBUG_TYPE "heap-type" + +#include "llvm/ADT/Statistic.h" +#include "llvm/IR/Constants.h" +#include "llvm/IR/InstrTypes.h" +#include "llvm/IR/Instruction.h" +#include "llvm/IR/Instructions.h" +#include "llvm/IR/LLVMContext.h" +#include "llvm/IR/InstIterator.h" +#include "llvm/Transforms/Utils/Cloning.h" +#include "Util/Options.h" +#include "llvm/IR/InstIterator.h" + +#include "SVF-FE/HeapSimplifier.h" + +using namespace SVF; + +// Identifier variable for the pass +char HeapSimplifier::ID = 0; + +// Register the pass +static llvm::RegisterPass HT ("heap-type-analyzer", + "Heap Type Analyzer"); + +void HeapSimplifier::handleVersions(Module& module) { + for (Module::iterator MIterator = module.begin(); MIterator != module.end(); MIterator++) { + if (Function *F = SVFUtil::dyn_cast(&*MIterator)) { + for (llvm::inst_iterator I = llvm::inst_begin(F), E = llvm::inst_end(F); I != E; ++I) { + Instruction* inst = &*I; + CallInst* cInst = SVFUtil::dyn_cast(inst); + if (cInst) { + Function* calledFunc = cInst->getCalledFunction(); + if (calledFunc) { + for (std::string memAllocFnName: memAllocFns) { + if (calledFunc->getName().startswith(memAllocFnName+".")) { + Function* memAllocFun = module.getFunction(memAllocFnName); + cInst->setCalledFunction(memAllocFun); // remove versioning + } + } + for (std::string la0FnName: L_A0_Fns) { + if (calledFunc->getName().startswith(la0FnName+".")) { + Function* la0Fun = module.getFunction(la0FnName); + cInst->setCalledFunction(la0Fun); // remove versioning + } + } + } + } + } + } + } + +} + +void HeapSimplifier::removePoolAllocatorBody(Module& module) { + for (Module::iterator MIterator = module.begin(); MIterator != module.end(); MIterator++) { + if (Function *F = SVFUtil::dyn_cast(&*MIterator)) { + if (std::find(memAllocFns.begin(), memAllocFns.end(), F->getName()) != memAllocFns.end()) { + F->deleteBody(); + } + if (std::find(L_A0_Fns.begin(), L_A0_Fns.end(), F->getName()) != L_A0_Fns.end()) { + F->deleteBody(); + } + /* + if (F->getName().startswith("Curl_dyn") + || F->getName().startswith("Curl_str") + || F->getName().startswith("curl_str") + || F->getName().startswith("Curl_llist") + || F->getName().startswith("Curl_infof") + || F->getName().startswith("Curl_failf") + || F->getName().startswith("curl_easy_getinfo") + || F->getName().startswith("curl_slist") + || F->getName().startswith("tool_setopt") + || F->getName().startswith("curl_msnprintf")) { + F->deleteBody(); + } + */ + /* + if (F->getName().startswith("ngx_log_error_core") + || F->getName().startswith("config") + || F->getName().startswith("array_insert_unique")) { + F->deleteBody(); + } + */ + } + } + +} + +CallInst* HeapSimplifier::findCInstFA(Value* val) { + std::vector workList; + workList.push_back(val); + + while (!workList.empty()) { + Value* val = workList.back(); + workList.pop_back(); + + for (User* user: val->users()) { + if (user == val) continue; + Instruction* userInst = SVFUtil::dyn_cast(user); + assert(userInst && "Must be instruction"); + if (CallInst* cInst = SVFUtil::dyn_cast(userInst)) { + return cInst; + } else { + workList.push_back(userInst); + } + } + } + return nullptr; +} + +/** + * func: The function that must be deep cloned + * mallocFunctions: The list of malloc wrappers that our system is aware of + * (malloc, calloc) + * cloneHeapTy: What type the deepest malloc clone should be set to + * origHeapTy: What type the deepest malloc of the original should be set to + */ +/* +bool HeapSimplifier::deepClone(llvm::Function* func, llvm::Function*& topClonedFunc, std::vector& mallocFunctions, + Type* cloneHeapTy, Type* origHeapTy) { + std::vector workList; + std::vector visited; + + std::vector cloneFuncs; // The list of functions to clone + cloneFuncs.push_back(func); + std::vector mallocCalls; // the list of calls to malloc. Should be 1 + + workList.push_back(func); + visited.push_back(func); + while (!workList.empty()) { + Function* F = workList.back(); + workList.pop_back(); + for (llvm::inst_iterator I = llvm::inst_begin(F), E = llvm::inst_end(F); I != E; ++I) { + if (CallInst* CI = SVFUtil::dyn_cast(&*I)) { + Function* calledFunc = CI->getCalledFunction(); + if (!calledFunc) { + // We can't handle indirect calls + return false; + } + if (std::find(mallocFunctions.begin(), mallocFunctions.end(), calledFunc->getName()) + != mallocFunctions.end()) { + mallocCalls.push_back(CI); + } else { + // If it returns a pointer + PointerType* retPtrTy = SVFUtil::dyn_cast(calledFunc->getReturnType()); + if (!retPtrTy) { + // We don't need to clone this function as it doesn't + // return a pointer. + // + // We only need to clone the path that returns the + // heap object created on the heap + continue; + } + if (std::find(visited.begin(), visited.end(), calledFunc) == visited.end()) { + workList.push_back(calledFunc); + visited.push_back(calledFunc); + cloneFuncs.push_back(calledFunc); + } + } + } + } + } + + if (mallocCalls.size() != 1) { + return false; + } + + std::map cloneMap; + + // Fix the cloned Functions + // We will handle the callers of the clone later + for (Function* toCloneFunc: cloneFuncs) { + llvm::ValueToValueMapTy VMap; + Function* clonedFunc = llvm::CloneFunction(toCloneFunc, VMap); + // If the VMap contains the call to malloc, then we can set it right + // away + llvm::ValueToValueMapTy::iterator it = VMap.find(mallocCalls[0]); + if (it != VMap.end()) { + Value* clonedMallocValue = VMap[mallocCalls[0]]; + //llvm::errs() << *clonedMallocValue << "\n"; + Instruction* clonedMallocInst = SVFUtil::dyn_cast(clonedMallocValue); + clonedMallocInst->addAnnotationMetadata("ArrayType"); + // CallInst* clonedMalloc = SVFUtil::dyn_cast(&(VMap[mallocCalls[0]])); + // SVFUtil::dyn_cast(it->second()); + } + cloneMap[toCloneFunc] = clonedFunc; + } + + mallocCalls[0]->addAnnotationMetadata("StructType"); + // Okay, now go over all the original copies of these functions + // If they had a call to _another_ function that was also cloned, replace + // them with the code. + // + + Function* topLevelClone = cloneMap[func]; + + workList.clear(); + workList.push_back(topLevelClone); + + for (auto it: cloneMap) { + llvm::errs() << "Function " << it.first->getName() << " cloned to " << it.second->getName() << "\n"; + } + + while (!workList.empty()) { + Function* F = workList.back(); + workList.pop_back(); + for (llvm::inst_iterator I = llvm::inst_begin(F), E = llvm::inst_end(F); I != E; ++I) { + if (CallInst* CI = SVFUtil::dyn_cast(&*I)) { + Function* calledFunc = CI->getCalledFunction(); + assert(calledFunc && "We shouldn't have to reach here"); + // If this was cloned + if (std::find(cloneFuncs.begin(), cloneFuncs.end(), calledFunc) != cloneFuncs.end()) { + Function* clonedFunction = cloneMap[calledFunc]; + CI->setCalledFunction(clonedFunction); + llvm::errs() << "Updated CI " << *CI << "\n"; + } + } + } + } + topClonedFunc = cloneMap[func]; + return true; +} +*/ + +HeapSimplifier::HeapTy HeapSimplifier::getSizeOfTy(Module& module, LLVMContext& Ctx, MDNode* sizeOfTyName, MDNode* sizeOfTyArgNum, MDNode* mulFactor) { + // Get the type + MDString* typeNameStr = (MDString*)sizeOfTyName->getOperand(0).get(); + Type* sizeOfTy = nullptr; + if (typeNameStr->getString() == "scalar_type") { + return HeapTy::ScalarTy; + } else { + MDString* mulFactorStr = (MDString*)mulFactor->getOperand(0).get(); + int mulFactorInt = std::stoi(mulFactorStr->getString().str()); + assert(mulFactorInt > 0 && "The multiplicator must be greater than 1"); + + if (mulFactorInt == 1) { + return HeapTy::StructTy; + } else { + return HeapTy::ArrayTy; + } + } + assert(false && "Shouldn't reach here"); + +} + +void HeapSimplifier::deriveHeapAllocationTypes(llvm::Module& module) { + // Just check all the locations that call the malloc functions + // or the pool allocators + for (Module::iterator MIterator = module.begin(); MIterator != module.end(); MIterator++) { + if (Function *F = SVFUtil::dyn_cast(&*MIterator)) { + for (llvm::inst_iterator I = llvm::inst_begin(F), E = llvm::inst_end(F); I != E; ++I) { + Instruction* inst = &*I; + LLVMContext& Ctx = inst->getContext(); + + MDNode* sizeOfTyName = inst->getMetadata("sizeOfTypeName"); + MDNode* sizeOfTyArgNum = inst->getMetadata("sizeOfTypeArgNum"); + MDNode* mulFactor = inst->getMetadata("sizeOfMulFactor"); + + bool handled = false; + // Check if this is a call to malloc or pool allocator + CallInst* callInst = SVFUtil::dyn_cast(inst); + if (callInst) { + Function* calledFunc = callInst->getCalledFunction(); + if (!sizeOfTyName) { + Function* calledFunc = callInst->getCalledFunction(); + if (calledFunc) { + if (std::find(memAllocFns.begin(), memAllocFns.end(), calledFunc->getName()) != memAllocFns.end() + && std::find(heapCalls.begin(), heapCalls.end(), F) == heapCalls.end() + /* if the caller is a heap allocator we don't care*/) { + //llvm::errs() << "No type annotation for heap call: " << *callInst << " in function : " << callInst->getFunction()->getName() << " treating as scalar\n"; + callInst->addAnnotationMetadata("IntegerType"); + handled = true; + } + } + + continue; + } + + HeapTy ty = getSizeOfTy(module, Ctx, sizeOfTyName, sizeOfTyArgNum, mulFactor); + switch(ty) { + case ScalarTy: + callInst->addAnnotationMetadata("IntegerType"); + break; + case StructTy: + callInst->addAnnotationMetadata("StructType"); + break; + case ArrayTy: + callInst->addAnnotationMetadata("ArrayType"); + break; + default: + assert(false && "Shouldn't reach here"); + } + handled = true; + } + /* + + // We just leave the metadata in place for future resolution of indirect heap calls + if (sizeOfTyName && !handled) { + if (callInst && callInst->getCalledFunction()) { + if (callInst->getCalledFunction()->isIntrinsic()) { + continue; + } else { + llvm::errs() << "Call to function: " << callInst->getCalledFunction()->getName() << " has type info but not a heap allocation\n"; + } + } else { + llvm::errs() << "Instruction: " << " in function: " << inst->getFunction()->getName() << " : " << *inst << " has type info but not a heap allocation\n"; + } + } + */ + } + } + } +} + +/* +void HeapSimplifier::deriveHeapAllocationTypesWithCloning(llvm::Module& module) { + + //StructType* stTy = StructType::getTypeByName(module.getContext(), tyName); + std::vector mallocFunctions; + + mallocFunctions.push_back("malloc"); + mallocFunctions.push_back("calloc"); + + typedef std::map> FunctionSizeOfTypeMapTy; + FunctionSizeOfTypeMapTy functionSizeOfTypeMap; + typedef std::map MallocSizeOfTypeMapTy; + MallocSizeOfTypeMapTy mallocSizeOfMap; + + typedef std::map> WrapperTypeCallerTy; + WrapperTypeCallerTy arrayTyCallers; + WrapperTypeCallerTy structTyCallers; + + + for (Module::iterator MIterator = module.begin(); MIterator != module.end(); MIterator++) { + if (Function *F = SVFUtil::dyn_cast(&*MIterator)) { + if (!F->isDeclaration()) { + for (llvm::inst_iterator I = llvm::inst_begin(F), E = llvm::inst_end(F); I != E; ++I) { + Instruction* inst = &*I; + LLVMContext& Ctx = inst->getContext(); + + MDNode* sizeOfTyName = inst->getMetadata("sizeOfTypeName"); + MDNode* sizeOfTyArgNum = inst->getMetadata("sizeOfTypeArgNum"); + MDNode* mulFactor = inst->getMetadata("sizeOfMulFactor"); + if (sizeOfTyName) { + CallInst* cInst = SVFUtil::dyn_cast(&*I); + if (!cInst) { + StoreInst* SI = SVFUtil::dyn_cast(&*I); + assert(SI && "Can only handle Store Insts here"); + + cInst = findCInstFA(SI->getPointerOperand()); + } + if (!cInst) { + continue; + } + Function* calledFunction = cInst->getCalledFunction(); + if (!calledFunction) { + continue; + } + // Get the type + MDString* typeNameStr = (MDString*)sizeOfTyName->getOperand(0).get(); + Type* sizeOfTy = nullptr; + if (typeNameStr->getString() == "scalar_type") { + sizeOfTy = IntegerType::get(Ctx, 8); + } else { + std::string prefix = "struct."; + std::string structNameStr = typeNameStr->getString().str(); + StringRef llvmTypeName(prefix+structNameStr); + sizeOfTy = StructType::getTypeByName(module.getContext(), llvmTypeName); + } + if (!sizeOfTy) { + continue; + } + + assert(mulFactor && "Sizeof operator must have a multiplicative factor present"); + + MDString* mulFactorStr = (MDString*)mulFactor->getOperand(0).get(); + int mulFactorInt = std::stoi(mulFactorStr->getString().str()); + assert(mulFactorInt > 0 && "The multiplicator must be greater than 1"); + + // Create the type + if (mulFactorInt > 1) { + sizeOfTy = ArrayType::get(sizeOfTy, mulFactorInt); + } + + if (std::find(mallocFunctions.begin(), mallocFunctions.end(), calledFunction->getName()) == mallocFunctions.end()) { + functionSizeOfTypeMap[calledFunction].insert(sizeOfTy); + if (SVFUtil::isa(sizeOfTy)) { + arrayTyCallers[calledFunction].insert(cInst); + } else if (SVFUtil::isa(sizeOfTy)) { + structTyCallers[calledFunction].insert(cInst); + } + } else { + mallocSizeOfMap[cInst] = sizeOfTy; + } + } + } + } + } + } + + for (auto it: functionSizeOfTypeMap) { + Function* potentialMallocWrapper = it.first; + std::set& types = it.second; + Type* arrTy = nullptr; + Type* structTy = nullptr; + + llvm::errs() << "Potential malloc wrapper: " << potentialMallocWrapper->getName() << "\n"; + for (Type* type: types) { + llvm::errs() << "\t" << *type << "\n"; + if (SVFUtil::isa(type)) { + arrTy = SVFUtil::dyn_cast(type); + } else if (SVFUtil::isa(type)) { + structTy = SVFUtil::dyn_cast(type); + } + } + + if (arrTy && structTy) { + // This function is invoked with both structtype and arraytype + // Try to deep clone it, and specialize it for the + Function* clonedFunc = nullptr; + bool couldSpecialize = deepClone(potentialMallocWrapper, clonedFunc, mallocFunctions, arrTy, structTy); + // Update the callsites + // It _might_ be possible we're re-updating something that was + // already updated inside deepClone, but it _should_ be okay + // The cloned function is for the array, and the original is for + // the struct type. + + if (couldSpecialize) { + for (CallInst* caller: arrayTyCallers[potentialMallocWrapper]) { + caller->setCalledFunction(clonedFunc); + } + } + } + } + + for (auto it: mallocSizeOfMap) { + CallInst* cInst = it.first; + Type* type = it.second; + if (SVFUtil::isa(type)) { + cInst->addAnnotationMetadata("ArrayType"); + } else if (SVFUtil::isa(type)) { + cInst->addAnnotationMetadata("StructType"); + } else if (SVFUtil::isa(type)) { + cInst->addAnnotationMetadata("IntegerType"); + } + llvm::errs() << "Call inst in function: " << cInst->getFunction()->getName() << " takes type " << *type << "\n"; + } + +} +*/ + + +void HeapSimplifier::buildCallGraphs (Module & module) { + for (llvm::Function& F: module.getFunctionList()) { + for (inst_iterator I = inst_begin(F), E = inst_end(F); I != E; ++I) { + if (CallInst* cInst = SVFUtil::dyn_cast(&*I)) { + if (Function* calledFunc = cInst->getCalledFunction()) { + callers[calledFunc].push_back(&F); + callees[&F].push_back(calledFunc); + } + } + } + } + + std::map funcToCallerSizeMap; + + for (auto it: callers) { + Function* calledFunc = it.first; + bool hasStructTyArg = false; + for (int i = 0; i< calledFunc->arg_size(); i++) { + Argument* arg = calledFunc->getArg(i); + Type* argTy = arg->getType(); + while (PointerType* argPtrTy = SVFUtil::dyn_cast(argTy)) { + argTy = argPtrTy->getPointerElementType(); + } + if (SVFUtil::isa(argTy)) { + hasStructTyArg = true; + } + } + if (hasStructTyArg) + funcToCallerSizeMap[calledFunc] = it.second.size(); + } + + for (auto it: funcToCallerSizeMap) { + Function* calledFunc = it.first; + if (calledFunc->isDeclaration()) continue; + callerDistMap[it.second].push_back(calledFunc); + } + + int i = 0; + for (auto it: callerDistMap) { + int callerCount = it.first; + llvm::errs() << callerCount << " callers: "; + for (Function* func: it.second) { + llvm::errs() << func->getName() << " ["<< func->isDeclaration() << "], "; + if ( i < Options::RemoveThres + && std::find(heapCalls.begin(), heapCalls.end(), func) + == heapCalls.end()) { + func->deleteBody(); + llvm::errs() << "Removing body\n"; + i++; + } + } + llvm::errs() << "\n"; + + } + +} + +bool HeapSimplifier::returnsUntypedMalloc(Function* potentialMallocWrapper) { + llvm::CFLAndersAAWrapperPass& aaPass = getAnalysis(); + llvm::CFLAndersAAResult& aaResult = aaPass.getResult(); + + Instruction* mallockedPtr = nullptr; + + + for (inst_iterator I = inst_begin(potentialMallocWrapper), E = inst_end(potentialMallocWrapper); I != E; ++I) { + if (CallInst* cInst = SVFUtil::dyn_cast(&*I)) { + if (cInst->getCalledFunction() && + std::find(memAllocFns.begin(), memAllocFns.end(), cInst->getCalledFunction()->getName().str()) != memAllocFns.end()) { + mallockedPtr = cInst; + break; + } + } + } + // There are situations that call malloc with a known type inside a small function. + // We don't care about these if the type is known. + if (mallockedPtr->getMetadata("sizeOfTypeName")) { + return false; + } + + if (!mallockedPtr) { + return false; + } + + for (inst_iterator I = inst_begin(potentialMallocWrapper), E = inst_end(potentialMallocWrapper); I != E; ++I) { + if (ReturnInst* retInst = SVFUtil::dyn_cast(&*I)) { + if (Instruction* retValue = SVFUtil::dyn_cast(retInst->getReturnValue())) { + if (retValue == mallockedPtr) return true; + llvm::AliasResult isAlias = aaResult.query( + llvm::MemoryLocation(mallockedPtr, llvm::LocationSize(64)), + llvm::MemoryLocation(retValue, llvm::LocationSize(64))); + if (isAlias == llvm::AliasResult::MayAlias) { + return true; + } + } else { + return false; + } + } + } + return false; +} + +void HeapSimplifier::findHeapContexts (Module& M) { + std::vector oneLevelFuncs; + + std::vector twoLevelFuncs; + + for (std::string& memFnStr: memAllocFns) { + Function* memAllocFn = M.getFunction(memFnStr); + if (memAllocFn) { + for (Function* caller: callers[memAllocFn]) { + if (caller->getReturnType()->isVoidTy()) { + continue; + } + if (returnsUntypedMalloc(caller) && callees[caller].size() < 7) { + oneLevelFuncs.push_back(caller); + heapCalls.push_back(caller); + } + } + } + } + + for (Function* f: oneLevelFuncs) { + llvm::errs() << "One Level Function name: " << f->getName() << "\n"; + memAllocFns.push_back(f->getName().str()); + } + + for (Function* memAllocFn: oneLevelFuncs) { + for (Function* caller: callers[memAllocFn]) { + if (caller->getInstructionCount() < 10) { + heapCalls.push_back(caller); + twoLevelFuncs.push_back(caller); + } + } + } + + for (Function* f: twoLevelFuncs) { + llvm::errs() << "Two Level Function name: " << f->getName() << "\n"; + memAllocFns.push_back(f->getName().str()); + } + + // IMPORTANT: Make sure this is consistent with the list in ExtAPI.cpp + std::vector allocFns { + "ngx_alloc", + "ngx_array_create", + "ngx_calloc", + "ngx_palloc", + "ngx_palloc_small", + "ngx_pcalloc", + "ngx_pnalloc", + "ngx_resolver_alloc", + "ngx_resolver_calloc", + "ngx_slab_alloc", + "ngx_slab_calloc_locked", + "ngx_palloc_large", + "ngx_calloc", + "ngx_create_pool", + "ngx_array_push", + "ngx_array_push_n", + + "luaM_reallocv", + "luaM_malloc", + "luaM_new", + "luaM_newvector", + "luaM_growvector", + "luaM_reallocvector", + "mytest_malloc" + }; + + for (Function& f: M.getFunctionList()) { + if (std::find(allocFns.begin(), allocFns.end(), f.getName().str()) != allocFns.end()) { + heapCalls.push_back(&f); + + llvm::ValueToValueMapTy vmap; + Function* cloned = llvm::CloneFunction(&f, vmap, NULL); + clonedFunctionMap[f.getName()] = cloned; + } + } + + + + + for (Function* f: heapCalls) { + llvm::errs() << " Heap call function: " << f->getName() << "\n"; + memAllocFns.push_back(f->getName().str()); + } + + buildCallGraphs(M); +} + +bool +HeapSimplifier::runOnModule (Module & module) { + /* + // Replace all invariant geps with 1-field index + + IntegerType* i64Ty = IntegerType::get(module.getContext(), 64); + Constant* oneCons = ConstantInt::get(i64Ty, 1); + + for (Function& F: module.getFunctionList()) { + for (inst_iterator I = inst_begin(F), E = inst_end(F); I != E; ++I) { + Instruction* inst = &*I; + if (GetElementPtrInst* gep = SVFUtil::dyn_cast(inst)) { + int i = 0; + for (auto& it: gep->indices()) { + Value& op = *it; + if (!SVFUtil::isa(&op)) { + gep->setOperand(i+1, oneCons); + } + i++; + } + } + } + } + */ + /* + llvm::CFLAndersAAWrapperPass& aaPass = getAnalysis(); + llvm::CFLAndersAAResult& aaResult = aaPass.getResult(); + + for (Function& F: module.getFunctionList()) { + for (inst_iterator I = inst_begin(F), E = inst_end(F); I != E; ++I) { + if (ReturnInst* retInst = SVFUtil::dyn_cast(&*I)) { + if (!retInst->getReturnValue()) continue; + if (Instruction* retValue = SVFUtil::dyn_cast(retInst->getReturnValue())) { + // Check if it aliases with an argument + for (int i = 0; i < F.arg_size(); i++) { + Argument* arg = F.getArg(i); + // Find the stack location + AllocaInst* stackArg = nullptr; + for (User* u: arg->users()) { + if (StoreInst* store = SVFUtil::dyn_cast(u)) { + if (AllocaInst* stack = SVFUtil::dyn_cast(store->getPointerOperand())) { + stackArg = stack; + break; + } + } + } + if (!stackArg || !stackArg->getType()->isPointerTy() || !retValue->getType()->isPointerTy()) { + continue; + } + llvm::AliasResult isAlias = aaResult.query( + llvm::MemoryLocation(stackArg, llvm::LocationSize(8)), + llvm::MemoryLocation(retValue, llvm::LocationSize(8))); + if (isAlias == llvm::AliasResult::MayAlias) { + llvm::errs() << "Function " << F.getName() << " returns an argument\n"; + } + } + } + } + } + } + */ + + findHeapContexts(module); +// handleVersions(module); + removePoolAllocatorBody(module); + deriveHeapAllocationTypes(module); + + std::error_code EC; + llvm::raw_fd_ostream OS("heap-cloned-module.bc", EC, + llvm::sys::fs::F_None); + WriteBitcodeToFile(module, OS); + OS.flush(); + + return false; +} diff --git a/svf/lib/SVF-FE/ICFGBuilder.cpp b/svf/lib/SVF-FE/ICFGBuilder.cpp new file mode 100644 index 000000000..aeb908a18 --- /dev/null +++ b/svf/lib/SVF-FE/ICFGBuilder.cpp @@ -0,0 +1,205 @@ +//===- ICFGBuilder.cpp ----------------------------------------------------------------// +// +// SVF: Static Value-Flow Analysis +// +// Copyright (C) <2013-> +// + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// +//===----------------------------------------------------------------------===// + + +/* + * ICFGBuilder.cpp + * + * Created on: + * Author: yulei + */ + +#include "SVF-FE/LLVMUtil.h" +#include "SVF-FE/ICFGBuilder.h" +#include "Graphs/PAG.h" + +using namespace SVF; +using namespace SVFUtil; + +/*! + * Create ICFG nodes and edges + */ +void ICFGBuilder::build(SVFModule* svfModule) +{ + for (SVFModule::const_iterator iter = svfModule->begin(), eiter = svfModule->end(); iter != eiter; ++iter) + { + const SVFFunction *fun = *iter; + if (SVFUtil::isExtCall(fun)) + continue; + WorkList worklist; + processFunEntry(fun,worklist); + processFunBody(worklist); + processFunExit(fun); + } + connectGlobalToProgEntry(svfModule); +} + +/*! + * function entry + */ +void ICFGBuilder::processFunEntry(const SVFFunction* fun, WorkList& worklist) +{ + FunEntryBlockNode* FunEntryBlockNode = icfg->getFunEntryBlockNode(fun); + const Instruction* entryInst = &((fun->getLLVMFun()->getEntryBlock()).front()); + InstVec insts; + if (isIntrinsicInst(entryInst)) + getNextInsts(entryInst, insts); + else + insts.push_back(entryInst); + for (InstVec::const_iterator nit = insts.begin(), enit = insts.end(); + nit != enit; ++nit) + { + ICFGNode* instNode = getOrAddBlockICFGNode(*nit); //add interprocedure edge + icfg->addIntraEdge(FunEntryBlockNode, instNode); + worklist.push(*nit); + } +} + +/*! + * function body + */ +void ICFGBuilder::processFunBody(WorkList& worklist) +{ + BBSet visited; + /// function body + while (!worklist.empty()) + { + const Instruction* inst = worklist.pop(); + if (visited.find(inst) == visited.end()) + { + visited.insert(inst); + ICFGNode* srcNode = getOrAddBlockICFGNode(inst); + if (isReturn(inst)) + { + const Function* fun = inst->getFunction(); + const SVFFunction* svfFun = LLVMModuleSet::getLLVMModuleSet()->getSVFFunction(fun); + FunExitBlockNode* FunExitBlockNode = icfg->getFunExitBlockNode(svfFun); + icfg->addIntraEdge(srcNode, FunExitBlockNode); + } + InstVec nextInsts; + getNextInsts(inst, nextInsts); + NodeID branchID = 0; + for (InstVec::const_iterator nit = nextInsts.begin(), enit = + nextInsts.end(); nit != enit; ++nit) + { + const Instruction* succ = *nit; + ICFGNode* dstNode = getOrAddBlockICFGNode(succ); + if (isNonInstricCallSite(inst)) + { + RetBlockNode* retICFGNode = getOrAddRetICFGNode(inst); + icfg->addIntraEdge(srcNode, retICFGNode); + srcNode = retICFGNode; + } + + + if (const BranchInst* br = SVFUtil::dyn_cast(inst)) + { + if(br->isConditional()) + icfg->addConditionalIntraEdge(srcNode, dstNode, br->getCondition(), branchID); + else + icfg->addIntraEdge(srcNode, dstNode); + } + + if (const SwitchInst* si = SVFUtil::dyn_cast(inst)) + { + icfg->addConditionalIntraEdge(srcNode, dstNode, si->getCondition(),branchID); + } + else + icfg->addIntraEdge(srcNode, dstNode); + + worklist.push(succ); + branchID++; + } + } + } +} + +/*! + * function exit e.g., exit(0). In LLVM, it usually manifests as "unreachable" instruction + * If a function has multiple exit(0), we will only have one "unreachle" instruction + * after the UnifyFunctionExitNodes pass. + */ +void ICFGBuilder::processFunExit(const SVFFunction* fun) +{ + FunExitBlockNode* FunExitBlockNode = icfg->getFunExitBlockNode(fun); + const Instruction* exitInst = &(getFunExitBB(fun->getLLVMFun())->back()); + InstVec insts; + if (isIntrinsicInst(exitInst)) + getPrevInsts(exitInst, insts); + else + insts.push_back(exitInst); + for (InstVec::const_iterator nit = insts.begin(), enit = insts.end(); + nit != enit; ++nit) + { + ICFGNode* instNode = getOrAddBlockICFGNode(*nit); + icfg->addIntraEdge(instNode, FunExitBlockNode); + } +} + + + + +/*! + * (1) Add and get CallBlockICFGNode + * (2) Handle call instruction by creating interprocedural edges + */ +InterBlockNode* ICFGBuilder::getOrAddInterBlockICFGNode(const Instruction* inst) +{ + assert(SVFUtil::isCallSite(inst) && "not a call instruction?"); + assert(SVFUtil::isNonInstricCallSite(inst) && "associating an intrinsic debug instruction with an ICFGNode!"); + CallBlockNode* callICFGNode = getOrAddCallICFGNode(inst); + if (const SVFFunction* callee = getCallee(inst)) + addICFGInterEdges(inst, callee); //creating interprocedural edges + return callICFGNode; +} + +/*! + * Create edges between ICFG nodes across functions + */ +void ICFGBuilder::addICFGInterEdges(const Instruction* cs, const SVFFunction* callee) +{ + CallBlockNode* CallBlockNode = getOrAddCallICFGNode(cs); + FunEntryBlockNode* calleeEntryNode = icfg->getFunEntryBlockNode(callee); + icfg->addCallEdge(CallBlockNode, calleeEntryNode, cs); + + if (!isExtCall(callee)) + { + RetBlockNode* retBlockNode = getOrAddRetICFGNode(cs); + FunExitBlockNode* calleeExitNode = icfg->getFunExitBlockNode(callee); + icfg->addRetEdge(calleeExitNode, retBlockNode, cs); + } +} + +void ICFGBuilder::connectGlobalToProgEntry(SVFModule* svfModule) +{ + const SVFFunction* mainFunc = SVFUtil::getProgEntryFunction(svfModule); + + /// Return back if the main function is not found, the bc file might be a library only + if(mainFunc == nullptr) + return; + + FunEntryBlockNode* entryNode = icfg->getFunEntryBlockNode(mainFunc); + GlobalBlockNode* globalNode = icfg->getGlobalBlockNode(); + IntraCFGEdge* intraEdge = new IntraCFGEdge(entryNode,globalNode); + icfg->addICFGEdge(intraEdge); +} + diff --git a/svf/lib/SVF-FE/LLVMModule.cpp b/svf/lib/SVF-FE/LLVMModule.cpp new file mode 100644 index 000000000..b347c8fd8 --- /dev/null +++ b/svf/lib/SVF-FE/LLVMModule.cpp @@ -0,0 +1,570 @@ +//===----- SVFModule.cpp Base class of pointer analyses ---------------------// +// +// SVF: Static Value-Flow Analysis +// +// Copyright (C) <2013-2017> +// + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// +//===----------------------------------------------------------------------===// + +/* + * SVFModule.cpp + * + * Created on: Aug 4, 2017 + * Author: Xiaokang Fan + */ + +#include "Util/Options.h" +#include +#include "Util/SVFModule.h" +#include "Util/SVFUtil.h" +#include "SVF-FE/LLVMUtil.h" +#include "SVF-FE/BreakConstantExpr.h" +#include "SVF-FE/HeapSimplifier.h" +#include "SVF-FE/ArgFlowAnalysis.h" +#include "llvm/IR/LegacyPassManager.h" +#include "llvm/Analysis/TargetLibraryInfo.h" +#include "llvm/Analysis/CFLAndersAliasAnalysis.h" + +using namespace std; +using namespace SVF; + +/* + svf.main() is used to model the real entry point of a C++ program, + which initializes all global C++ objects and then call main(). + For example, given a "int main(int argc, char * argv[])", the corresponding + svf.main will be generated as follows: + define void @svf.main(i32, i8**, i8**) { + entry: + call void @_GLOBAL__sub_I_cast.cpp() + call void @_GLOBAL__sub_I_1.cpp() + call void @_GLOBAL__sub_I_2.cpp() + %3 = call i32 @main(i32 %0, i8** %1) + ret void + } + */ +#define SVF_MAIN_FUNC_NAME "svf.main" +#define SVF_GLOBAL_SUB_I_XXX "_GLOBAL__sub_I_" + +LLVMModuleSet *LLVMModuleSet::llvmModuleSet = nullptr; +std::string SVFModule::pagReadFromTxt = ""; + +SVFModule* LLVMModuleSet::buildSVFModule(Module &mod) +{ + svfModule = new SVFModule(mod.getModuleIdentifier()); + modules.emplace_back(mod); + + + build(); + + for (Function* heapCall: heapCalls) { + svfModule->addHeapCall(heapCall); + } + + return svfModule; +} + +SVFModule* LLVMModuleSet::buildSVFModule(const std::vector &moduleNameVec) +{ + assert(llvmModuleSet && "LLVM Module set needs to be created!"); + + loadModules(moduleNameVec); + + if(!moduleNameVec.empty()) + svfModule = new SVFModule(*moduleNameVec.begin()); + else + svfModule = new SVFModule(); + + + llvm::Module *module = SVF::LLVMModuleSet::getLLVMModuleSet()->getMainLLVMModule(); + // Add the global map + // A map id: last-seen-value + LLVMContext& ctx = module->getContext(); + Type* int8Ty = Type::getInt8Ty(ctx); + Type* int64Ty = Type::getInt64Ty(ctx); + Type* voidPtrTy = PointerType::get(int8Ty, 0); + + // Add the view-switching function + + // The switch-view function + llvm::ArrayRef switchViewFnTypeArr = {}; + + FunctionType* switchViewFnTy = FunctionType::get(Type::getVoidTy(module->getContext()), switchViewFnTypeArr, false); + Function::Create(switchViewFnTy, Function::ExternalLinkage, "switch_view", module); + + /* + std::vector types; + types.push_back(voidPtrTy); + types.push_back(int64Ty); + llvm::ArrayRef typesArrayRef(types); + StructType* mapElemTy = StructType::get(ctx, typesArrayRef); + */ + ArrayType* mapTy = ArrayType::get(voidPtrTy, 100); + ConstantAggregateZero* zero = ConstantAggregateZero::get(mapTy); + GlobalVariable* kaliMap = new GlobalVariable(*module, + /*Type=*/mapTy, + /*isConstant=*/false, + /*Linkage=*/GlobalValue::CommonLinkage, + /*Initializer=*/zero, + /*Name=*/"kaliMap"); + + + build(); + + for (Function* heapCall: heapCalls) { + svfModule->addHeapCall(heapCall); + } + + + return svfModule; +} + +void LLVMModuleSet::preProcessBCs(std::vector &moduleNameVec) +{ + loadModules(moduleNameVec); + prePassSchedule(); + + std::string preProcessSuffix = ".pre.bc"; + // Get the existing module names, remove old extention, add preProcessSuffix + for (u32_t i = 0; i < moduleNameVec.size(); i++) + { + u32_t lastIndex = moduleNameVec[i].find_last_of("."); + std::string rawName = moduleNameVec[i].substr(0, lastIndex); + moduleNameVec[i] = (rawName + preProcessSuffix); + } + + dumpModulesToFile(preProcessSuffix); + preProcessed = true; + + releaseLLVMModuleSet(); +} + +void LLVMModuleSet::reattachHeapContexts() { + for (u32_t i = 0; i < LLVMModuleSet::getLLVMModuleSet()->getModuleNum(); ++i) { + Module *module = LLVMModuleSet::getLLVMModuleSet()->getModule(i); + for (auto it: clonedFunctionMap) { + Function* oldFunc = module->getFunction(it.first); + if (oldFunc) { + oldFunc->replaceAllUsesWith(it.second); + //module->getFunctionList().push_back(it.second); + } + } + } + +} + +void LLVMModuleSet::build() +{ + preInitialize(); + initialize(); + buildFunToFunMap(); + buildGlobalDefToRepMap(); + if(preProcessed==false) + prePassSchedule(); +} + +void LLVMModuleSet::preInitialize() { + for (u32_t i = 0; i < LLVMModuleSet::getLLVMModuleSet()->getModuleNum(); ++i) { + Module *module = LLVMModuleSet::getLLVMModuleSet()->getModule(i); + llvm::legacy::PassManager PM; + llvm::TargetLibraryInfoWrapperPass* infoPass = new llvm::TargetLibraryInfoWrapperPass(); + llvm::CFLAndersAAWrapperPass* aaPass = new llvm::CFLAndersAAWrapperPass(); + HeapSimplifier* heapTyPass = new HeapSimplifier(); + ArgFlowAnalysis* argFlowAnalysisPass = new ArgFlowAnalysis(); + PM.add(argFlowAnalysisPass); + PM.add(infoPass); + PM.add(aaPass); + PM.add(heapTyPass); + PM.run(*module); + + for (Function* heapCall: heapTyPass->getHeapCalls()) { + heapCalls.push_back(heapCall); + } + + for (auto it: heapTyPass->getClonedFunctionMap()) { + StringRef fName = it.first; + Function* func = it.second; + clonedFunctionMap[fName] = func; + } + } +} + +/*! + * Invoke llvm passes to modify module + */ +void LLVMModuleSet::prePassSchedule() +{ + /// BreakConstantGEPs Pass + std::unique_ptr p1 = std::make_unique(); + for (u32_t i = 0; i < LLVMModuleSet::getLLVMModuleSet()->getModuleNum(); ++i) + { + Module *module = LLVMModuleSet::getLLVMModuleSet()->getModule(i); + p1->runOnModule(*module); + } + + /// MergeFunctionRets Pass + std::unique_ptr p2 = + std::make_unique(); + for (u32_t i = 0; i < LLVMModuleSet::getLLVMModuleSet()->getModuleNum(); ++i) + { + Module *module = LLVMModuleSet::getLLVMModuleSet()->getModule(i); + for (auto F = module->begin(), E = module->end(); F != E; ++F) + { + Function &fun = *F; + if (fun.isDeclaration()) + continue; + p2->runOnFunction(fun); + } + } + + +} + + +void LLVMModuleSet::loadModules(const std::vector &moduleNameVec) +{ + + // We read PAG from LLVM IR + if(Options::Graphtxt.getValue().empty()) + { + if(moduleNameVec.empty()) + { + SVFUtil::outs() << "no LLVM bc file is found!\n"; + exit(0); + } + //assert(!moduleNameVec.empty() && "no LLVM bc file is found!"); + } + // We read PAG from a user-defined txt instead of parsing PAG from LLVM IR + else + SVFModule::setPagFromTXT(Options::Graphtxt.getValue()); + + // + // To avoid the following type bugs (t1 != t3) when parsing multiple modules, + // We should use only one LLVMContext object for multiple modules in the same thread. + // No such problem if only one module is processed by SVF. + // ------------------------------------------------------------------ + // LLVMContext ctxa,ctxb; + // IntegerType * t1 = IntegerType::get(ctxa,32); + // IntegerType * t2 = IntegerType::get(ctxa,32); + // assert(t1 == t2); + // IntegerType * t3 = IntegerType::get(ctxb,32); + // IntegerType * t4 = IntegerType::get(ctxb,32); + // assert(t3 == t4); + // assert(t1 != t3); + // ------------------------------------------------------------------ + // + cxts = std::make_unique(); + + for (const std::string& moduleName : moduleNameVec) { + SMDiagnostic Err; + std::unique_ptr mod = parseIRFile(moduleName, Err, *cxts); + if (mod == nullptr) + { + SVFUtil::errs() << "load module: " << moduleName << "failed!!\n\n"; + Err.print("SVFModuleLoader", SVFUtil::errs()); + continue; + } + modules.emplace_back(*mod); + owned_modules.emplace_back(std::move(mod)); + } +} + +void LLVMModuleSet::initialize() +{ + if (Options::SVFMain) + addSVFMain(); + + for (Module& mod : modules) + { + /// Function + for (Module::iterator it = mod.begin(), eit = mod.end(); + it != eit; ++it) + { + Function *func = &*it; + svfModule->addFunctionSet(func); + } + + /// GlobalVariable + for (Module::global_iterator it = mod.global_begin(), + eit = mod.global_end(); it != eit; ++it) + { + GlobalVariable *global = &*it; + svfModule->addGlobalSet(global); + } + + /// GlobalAlias + for (Module::alias_iterator it = mod.alias_begin(), + eit = mod.alias_end(); it != eit; ++it) + { + GlobalAlias *alias = &*it; + svfModule->addAliasSet(alias); + } + } +} + +void LLVMModuleSet::addSVFMain() +{ + std::vector init_funcs; + Function * orgMain = 0; + Module* mainMod = nullptr; + for (Module& mod : modules) + { + for (auto &func: mod) + { + if(func.getName().startswith(SVF_GLOBAL_SUB_I_XXX)) + init_funcs.push_back(&func); + if(func.getName().equals(SVF_MAIN_FUNC_NAME)) + assert(false && SVF_MAIN_FUNC_NAME " already defined"); + if(func.getName().equals("main")) + { + orgMain = &func; + mainMod = &mod; + } + } + } + if(orgMain && getModuleNum() > 0 && init_funcs.size() > 0) + { + assert(mainMod && "Module with main function not found."); + Module & M = *mainMod; + // char ** + Type * i8ptr2 = PointerType::getInt8PtrTy(M.getContext())->getPointerTo(); + Type * i32 = IntegerType::getInt32Ty(M.getContext()); + // define void @svf.main(i32, i8**, i8**) +#if (LLVM_VERSION_MAJOR >= 9) + FunctionCallee svfmainFn = M.getOrInsertFunction( + SVF_MAIN_FUNC_NAME, + Type::getVoidTy(M.getContext()), + i32,i8ptr2,i8ptr2 + ); + Function *svfmain = SVFUtil::dyn_cast(svfmainFn.getCallee()); +#else + Function *svfmain = SVFUtil::dyn_cast(M.getOrInsertFunction( + SVF_MAIN_FUNC_NAME, + Type::getVoidTy(M.getContext()), + i32,i8ptr2,i8ptr2 + )); +#endif + svfmain->setCallingConv(llvm::CallingConv::C); + BasicBlock* block = BasicBlock::Create(M.getContext(), "entry", svfmain); + IRBuilder Builder(block); + // emit "call void @_GLOBAL__sub_I_XXX()" + for(auto & init: init_funcs) + { + auto target = M.getOrInsertFunction( + init->getName(), + Type::getVoidTy(M.getContext()) + ); + Builder.CreateCall(target); + } + // main() should be called after all _GLOBAL__sub_I_XXX functions. + Function::arg_iterator arg_it = svfmain->arg_begin(); + Value * args[] = {arg_it, arg_it + 1, arg_it + 2 }; + size_t cnt = orgMain->arg_size(); + assert(cnt <= 3 && "Too many arguments for main()"); + Builder.CreateCall(orgMain, llvm::ArrayRef(args,args + cnt)); + // return; + Builder.CreateRetVoid(); + } +} + + +void LLVMModuleSet::buildFunToFunMap() +{ + Set funDecls, funDefs; + OrderedSet declNames, defNames, intersectNames; + typedef Map NameToFunDefMapTy; + typedef Map> NameToFunDeclsMapTy; + + for (SVFModule::LLVMFunctionSetType::iterator it = svfModule->llvmFunBegin(), + eit = svfModule->llvmFunEnd(); it != eit; ++it) + { + Function *fun = *it; + if (fun->isDeclaration()) + { + funDecls.insert(fun); + declNames.insert(fun->getName().str()); + } + else + { + funDefs.insert(fun); + defNames.insert(fun->getName().str()); + } + } + // Find the intersectNames + OrderedSet::iterator declIter, defIter; + declIter = declNames.begin(); + defIter = defNames.begin(); + while (declIter != declNames.end() && defIter != defNames.end()) + { + if (*declIter < *defIter) + { + declIter++; + } + else + { + if (!(*defIter < *declIter)) + { + intersectNames.insert(*declIter); + declIter++; + } + defIter++; + } + } + + ///// name to def map + NameToFunDefMapTy nameToFunDefMap; + for (Set::iterator it = funDefs.begin(), + eit = funDefs.end(); it != eit; ++it) + { + Function *fdef = *it; + string funName = fdef->getName().str(); + if (intersectNames.find(funName) == intersectNames.end()) + continue; + nameToFunDefMap[funName] = fdef; + } + + ///// name to decls map + NameToFunDeclsMapTy nameToFunDeclsMap; + for (Set::iterator it = funDecls.begin(), + eit = funDecls.end(); it != eit; ++it) + { + Function *fdecl = *it; + string funName = fdecl->getName().str(); + if (intersectNames.find(funName) == intersectNames.end()) + continue; + NameToFunDeclsMapTy::iterator mit = nameToFunDeclsMap.find(funName); + if (mit == nameToFunDeclsMap.end()) + { + Set decls; + decls.insert(fdecl); + nameToFunDeclsMap[funName] = decls; + } + else + { + Set &decls = mit->second; + decls.insert(fdecl); + } + } + + /// Fun decl --> def + for (Set::iterator it = funDecls.begin(), + eit = funDecls.end(); it != eit; ++it) + { + const Function *fdecl = *it; + string funName = fdecl->getName().str(); + if (intersectNames.find(funName) == intersectNames.end()) + continue; + NameToFunDefMapTy::iterator mit = nameToFunDefMap.find(funName); + if (mit == nameToFunDefMap.end()) + continue; + FunDeclToDefMap[svfModule->getSVFFunction(fdecl)] = svfModule->getSVFFunction(mit->second); + } + + /// Fun def --> decls + for (Set::iterator it = funDefs.begin(), + eit = funDefs.end(); it != eit; ++it) + { + const Function *fdef = *it; + string funName = fdef->getName().str(); + if (intersectNames.find(funName) == intersectNames.end()) + continue; + NameToFunDeclsMapTy::iterator mit = nameToFunDeclsMap.find(funName); + if (mit == nameToFunDeclsMap.end()) + continue; + std::vector& decls = FunDefToDeclsMap[svfModule->getSVFFunction(fdef)]; + for (Set::iterator sit = mit->second.begin(), + seit = mit->second.end(); sit != seit; ++sit) + { + decls.push_back(svfModule->getSVFFunction(*sit)); + } + } +} + +void LLVMModuleSet::buildGlobalDefToRepMap() +{ + typedef Map> NameToGlobalsMapTy; + NameToGlobalsMapTy nameToGlobalsMap; + for (SVFModule::global_iterator it = svfModule->global_begin(), + eit = svfModule->global_end(); it != eit; ++it) + { + GlobalVariable *global = *it; + if (global->hasPrivateLinkage()) + continue; + string name = global->getName().str(); + NameToGlobalsMapTy::iterator mit = nameToGlobalsMap.find(name); + if (mit == nameToGlobalsMap.end()) + { + Set globals; + globals.insert(global); + nameToGlobalsMap[name] = globals; + } + else + { + Set &globals = mit->second; + globals.insert(global); + } + } + + for (NameToGlobalsMapTy::iterator it = nameToGlobalsMap.begin(), + eit = nameToGlobalsMap.end(); it != eit; ++it) + { + Set &globals = it->second; + GlobalVariable *rep = *(globals.begin()); + Set::iterator repit = globals.begin(); + while (repit != globals.end()) + { + GlobalVariable *cur = *repit; + if (cur->hasInitializer()) + { + rep = cur; + break; + } + repit++; + } + for (Set::iterator sit = globals.begin(), + seit = globals.end(); sit != seit; ++sit) + { + GlobalVariable *cur = *sit; + GlobalDefToRepMap[cur] = rep; + } + } +} + +// Dump modules to files +void LLVMModuleSet::dumpModulesToFile(const std::string suffix) +{ + for (Module& mod : modules) + { + std::string moduleName = mod.getName().str(); + std::string OutputFilename; + std::size_t pos = moduleName.rfind('.'); + if (pos != std::string::npos) + OutputFilename = moduleName.substr(0, pos) + suffix; + else + OutputFilename = moduleName + suffix; + + std::error_code EC; + raw_fd_ostream OS(OutputFilename.c_str(), EC, llvm::sys::fs::F_None); + +#if (LLVM_VERSION_MAJOR >= 7) + WriteBitcodeToFile(mod, OS); +#else + WriteBitcodeToFile(&mod, OS); +#endif + + OS.flush(); + } +} diff --git a/svf/lib/SVF-FE/LLVMUtil.cpp b/svf/lib/SVF-FE/LLVMUtil.cpp new file mode 100644 index 000000000..207b7ff72 --- /dev/null +++ b/svf/lib/SVF-FE/LLVMUtil.cpp @@ -0,0 +1,396 @@ +//===- SVFUtil.cpp -- Analysis helper functions----------------------------// +// +// SVF: Static Value-Flow Analysis +// +// Copyright (C) <2013-> +// + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// +//===----------------------------------------------------------------------===// + +/* + * SVFUtil.cpp + * + * Created on: Apr 11, 2013 + * Author: Yulei Sui + */ + +#include "SVF-FE/LLVMUtil.h" + +using namespace SVF; + +/*! + * A value represents an object if it is + * 1) function, + * 2) global + * 3) stack + * 4) heap + */ +bool SVFUtil::isObject(const Value * ref) +{ + bool createobj = false; + if (SVFUtil::isa(ref) && SVFUtil::isStaticExtCall(SVFUtil::cast(ref)) ) + createobj = true; + if (SVFUtil::isa(ref) && SVFUtil::isHeapAllocExtCallViaRet(SVFUtil::cast(ref))) + createobj = true; + if (SVFUtil::isa(ref)) + createobj = true; + if (SVFUtil::isa(ref) || SVFUtil::isa(ref) ) + createobj = true; + + return createobj; +} + + +/*! + * Return reachable bbs from function entry + */ +void SVFUtil::getFunReachableBBs (const Function * fun, DominatorTree* dt, std::vector &reachableBBs) +{ + Set visited; + std::vector bbVec; + bbVec.push_back(&fun->getEntryBlock()); + while(!bbVec.empty()) + { + const BasicBlock* bb = bbVec.back(); + bbVec.pop_back(); + reachableBBs.push_back(bb); + if(DomTreeNode *dtNode = dt->getNode(const_cast(bb))) + { + for (DomTreeNode::iterator DI = dtNode->begin(), DE = dtNode->end(); + DI != DE; ++DI) + { + const BasicBlock* succbb = (*DI)->getBlock(); + if(visited.find(succbb)==visited.end()) + visited.insert(succbb); + else + continue; + bbVec.push_back(succbb); + } + } + } +} + +/*! + * Return true if the function has a return instruction reachable from function entry + */ +bool SVFUtil::functionDoesNotRet (const Function * fun) +{ + + std::vector bbVec; + Set visited; + bbVec.push_back(&fun->getEntryBlock()); + while(!bbVec.empty()) + { + const BasicBlock* bb = bbVec.back(); + bbVec.pop_back(); + for (BasicBlock::const_iterator it = bb->begin(), eit = bb->end(); + it != eit; ++it) + { + if(SVFUtil::isa(*it)) + return false; + } + + for (succ_const_iterator sit = succ_begin(bb), esit = succ_end(bb); + sit != esit; ++sit) + { + const BasicBlock* succbb = (*sit); + if(visited.find(succbb)==visited.end()) + visited.insert(succbb); + else + continue; + bbVec.push_back(succbb); + } + } +// if(isProgEntryFunction(fun)==false) { +// writeWrnMsg(fun->getName().str() + " does not have return"); +// } + return true; +} + +/*! + * Return true if this is a function without any possible caller + */ +bool SVFUtil::isDeadFunction (const Function * fun) +{ + if(fun->hasAddressTaken()) + return false; + if(isProgEntryFunction(fun)) + return false; + for (Value::const_user_iterator i = fun->user_begin(), e = fun->user_end(); i != e; ++i) + { + if (SVFUtil::isCallSite(*i)) + return false; + } + if (LLVMModuleSet::getLLVMModuleSet()->hasDeclaration(fun)) + { + const SVFModule::FunctionSetType &decls = LLVMModuleSet::getLLVMModuleSet()->getDeclaration(fun); + for (SVFModule::FunctionSetType::const_iterator it = decls.begin(), + eit = decls.end(); it != eit; ++it) + { + const Function *decl = (*it)->getLLVMFun(); + if(decl->hasAddressTaken()) + return false; + for (Value::const_user_iterator i = decl->user_begin(), e = decl->user_end(); i != e; ++i) + { + if (SVFUtil::isCallSite(*i)) + return false; + } + } + } + return true; +} + +/*! + * Return true if this is a value in a dead function (function without any caller) + */ +bool SVFUtil::isPtrInDeadFunction (const Value * value) +{ + if(const Instruction* inst = SVFUtil::dyn_cast(value)) + { + if(isDeadFunction(inst->getParent()->getParent())) + return true; + } + else if(const Argument* arg = SVFUtil::dyn_cast(value)) + { + if(isDeadFunction(arg->getParent())) + return true; + } + return false; +} + +/*! + * Strip constant casts + */ +const Value * SVFUtil::stripConstantCasts(const Value *val) +{ + if (SVFUtil::isa(val) || isInt2PtrConstantExpr(val)) + return val; + else if (const ConstantExpr *CE = SVFUtil::dyn_cast(val)) + { + if (Instruction::isCast(CE->getOpcode())) + return stripConstantCasts(CE->getOperand(0)); + } + return val; +} + +/*! + * Strip all casts + */ +Value * SVFUtil::stripAllCasts(Value *val) +{ + while (true) + { + if (CastInst *ci = SVFUtil::dyn_cast(val)) + { + val = ci->getOperand(0); + } + else if (ConstantExpr *ce = SVFUtil::dyn_cast(val)) + { + if(ce->isCast()) + val = ce->getOperand(0); + } + else + { + return val; + } + } + return nullptr; +} + +/// Get the next instructions following control flow +void SVFUtil::getNextInsts(const Instruction* curInst, std::vector& instList) +{ + if (!curInst->isTerminator()) + { + const Instruction* nextInst = curInst->getNextNode(); + if (isIntrinsicInst(nextInst)) + getNextInsts(nextInst, instList); + else + instList.push_back(nextInst); + } + else + { + const BasicBlock *BB = curInst->getParent(); + // Visit all successors of BB in the CFG + for (succ_const_iterator it = succ_begin(BB), ie = succ_end(BB); it != ie; ++it) + { + const Instruction* nextInst = &((*it)->front()); + if (isIntrinsicInst(nextInst)) + getNextInsts(nextInst, instList); + else + instList.push_back(nextInst); + } + } +} + + +/// Get the previous instructions following control flow +void SVFUtil::getPrevInsts(const Instruction* curInst, std::vector& instList) +{ + if (curInst != &(curInst->getParent()->front())) + { + const Instruction* prevInst = curInst->getPrevNode(); + if (isIntrinsicInst(prevInst)) + getPrevInsts(prevInst, instList); + else + instList.push_back(prevInst); + } + else + { + const BasicBlock *BB = curInst->getParent(); + // Visit all successors of BB in the CFG + for (const_pred_iterator it = pred_begin(BB), ie = pred_end(BB); it != ie; ++it) + { + const Instruction* prevInst = &((*it)->back()); + if (isIntrinsicInst(prevInst)) + getPrevInsts(prevInst, instList); + else + instList.push_back(prevInst); + } + } +} + + +/*! + * Return the type of the object from a heap allocation + */ +const Type* SVFUtil::getTypeOfHeapAlloc(const Instruction *inst) +{ + const PointerType* type = SVFUtil::dyn_cast(inst->getType()); + + if(isHeapAllocExtCallViaRet(inst)) + { + const Instruction* nextInst = inst->getNextNode(); + if(nextInst && nextInst->getOpcode() == Instruction::BitCast) + // we only consider bitcast instructions and ignore others (e.g., IntToPtr and ZExt) + type = SVFUtil::dyn_cast(inst->getNextNode()->getType()); + } + else if(isHeapAllocExtCallViaArg(inst)) + { + CallSite cs = getLLVMCallSite(inst); + int arg_pos = getHeapAllocHoldingArgPosition(getCallee(cs)); + const Value *arg = cs.getArgument(arg_pos); + type = SVFUtil::dyn_cast(arg->getType()); + } + else + { + assert( false && "not a heap allocation instruction?"); + } + + assert(type && "not a pointer type?"); + return type->getElementType(); +} + +/*! + * Get position of a successor basic block + */ +u32_t SVFUtil::getBBSuccessorPos(const BasicBlock *BB, const BasicBlock *Succ) +{ + u32_t i = 0; + for (const BasicBlock *SuccBB: successors(BB)) + { + if (SuccBB == Succ) + return i; + i++; + } + assert(false && "Didn't find succesor edge?"); + return 0; +} + + +/*! + * Return a position index from current bb to it successor bb + */ +u32_t SVFUtil::getBBPredecessorPos(const BasicBlock *bb, const BasicBlock *succbb) +{ + u32_t pos = 0; + for (const_pred_iterator it = pred_begin(succbb), et = pred_end(succbb); it != et; ++it, ++pos) + { + if(*it==bb) + return pos; + } + assert(false && "Didn't find predecessor edge?"); + return pos; +} + +/*! + * Get the num of BB's successors + */ +u32_t SVFUtil::getBBSuccessorNum(const BasicBlock *BB) +{ + return BB->getTerminator()->getNumSuccessors(); +} + +/*! + * Get the num of BB's predecessors + */ +u32_t SVFUtil::getBBPredecessorNum(const BasicBlock *BB) +{ + u32_t num = 0; + for (const_pred_iterator it = pred_begin(BB), et = pred_end(BB); it != et; ++it) + num++; + return num; +} + +/* + * Reference functions: + * llvm::parseIRFile (lib/IRReader/IRReader.cpp) + * llvm::parseIR (lib/IRReader/IRReader.cpp) + */ +bool SVFUtil::isIRFile(const std::string &filename) +{ + llvm::ErrorOr> FileOrErr = llvm::MemoryBuffer::getFileOrSTDIN(filename); + if (FileOrErr.getError()) + return false; + llvm::MemoryBufferRef Buffer = FileOrErr.get()->getMemBufferRef(); + const unsigned char *bufferStart = + (const unsigned char *)Buffer.getBufferStart(); + const unsigned char *bufferEnd = + (const unsigned char *)Buffer.getBufferEnd(); + return llvm::isBitcode(bufferStart, bufferEnd) ? true : + Buffer.getBuffer().startswith("; ModuleID ="); +} + + +/// Get the names of all modules into a vector +/// And process arguments +void SVFUtil::processArguments(int argc, char **argv, int &arg_num, char **arg_value, + std::vector &moduleNameVec) +{ + bool first_ir_file = true; + for (s32_t i = 0; i < argc; ++i) + { + std::string argument(argv[i]); + if (SVFUtil::isIRFile(argument)) + { + if (find(moduleNameVec.begin(), moduleNameVec.end(), argument) + == moduleNameVec.end()) + moduleNameVec.push_back(argument); + if (first_ir_file) + { + arg_value[arg_num] = argv[i]; + arg_num++; + first_ir_file = false; + } + } + else + { + arg_value[arg_num] = argv[i]; + arg_num++; + } + } +} + diff --git a/svf/lib/SVF-FE/LocMemModel.cpp b/svf/lib/SVF-FE/LocMemModel.cpp new file mode 100644 index 000000000..ef48c56eb --- /dev/null +++ b/svf/lib/SVF-FE/LocMemModel.cpp @@ -0,0 +1,31 @@ +//===- LocMemModel.cpp -- Location set based memory model---------------------// +// +// SVF: Static Value-Flow Analysis +// +// Copyright (C) <2013-2017> +// + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// +//===----------------------------------------------------------------------===// + +/* + * LocMemModel.cpp + * + * Created on: Apr 28, 2014 + * Author: Yulei Sui + */ + + + diff --git a/svf/lib/SVF-FE/PAGBuilder.cpp b/svf/lib/SVF-FE/PAGBuilder.cpp new file mode 100644 index 000000000..4bd308f03 --- /dev/null +++ b/svf/lib/SVF-FE/PAGBuilder.cpp @@ -0,0 +1,1721 @@ +//===- PAGBuilder.cpp -- PAG builder-----------------------------------------// +// +// SVF: Static Value-Flow Analysis +// +// Copyright (C) <2013-2017> +// + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// +//===----------------------------------------------------------------------===// + +/* + * PAGBuilder.cpp + * + * Created on: Nov 1, 2013 + * Author: Yulei Sui + */ +#include "Util/Options.h" +#include "SVF-FE/PAGBuilder.h" +#include "Util/SVFModule.h" +#include "Util/SVFUtil.h" +#include "SVF-FE/LLVMUtil.h" +#include "SVF-FE/CPPUtil.h" +#include "Graphs/ExternalPAG.h" +#include "Util/BasicTypes.h" +#include "MemoryModel/PAGBuilderFromFile.h" +#include "llvm/Transforms/Utils/BasicBlockUtils.h" +#include "llvm/Analysis/LoopInfo.h" + +using namespace std; +using namespace SVF; +using namespace SVFUtil; + + +/*! + * Start building PAG here + */ +PAG* PAGBuilder::build(SVFModule* svfModule) +{ + + // We read PAG from a user-defined txt instead of parsing PAG from LLVM IR + if (SVFModule::pagReadFromTXT()) + { + PAGBuilderFromFile fileBuilder(SVFModule::pagFileName()); + return fileBuilder.build(); + } + + // If the PAG has been built before, then we return the unique PAG of the program + if(pag->getNodeNumAfterPAGBuild() > 1) + return pag; + + svfMod = svfModule; + + /// initial external library information + /// initial PAG nodes + initialiseNodes(); + /// initial PAG edges: + ///// handle globals + visitGlobal(svfModule); + ///// collect exception vals in the program + + ExternalPAG::initialise(svfModule); + + /// handle functions + for (SVFModule::iterator fit = svfModule->begin(), efit = svfModule->end(); + fit != efit; ++fit) + { + const SVFFunction& fun = **fit; + const Function* llvmFun = fun.getLLVMFun(); + if (isPseudoExtHeapCall(fun.getLLVMFun())) { + continue; + } + + /// collect return node of function fun + if(!SVFUtil::isExtCall(&fun)) + { + /// Return PAG node will not be created for function which can not + /// reach the return instruction due to call to abort(), exit(), + /// etc. In 176.gcc of SPEC 2000, function build_objc_string() from + /// c-lang.c shows an example when fun.doesNotReturn() evaluates + /// to TRUE because of abort(). + if(fun.getLLVMFun()->doesNotReturn() == false && fun.getLLVMFun()->getReturnType()->isVoidTy() == false) + pag->addFunRet(&fun,pag->getPAGNode(pag->getReturnNode(&fun))); + + /// To be noted, we do not record arguments which are in declared function without body + /// TODO: what about external functions with PAG imported by commandline? + for (Function::arg_iterator I = fun.getLLVMFun()->arg_begin(), E = fun.getLLVMFun()->arg_end(); + I != E; ++I) { + setCurrentLocation(&*I,&fun.getLLVMFun()->getEntryBlock()); + NodeID argValNodeId = pag->getValueNode(&*I); + // if this is the function does not have caller (e.g. main) + // or a dead function, shall we create a black hole address edge for it? + // it is (1) too conservative, and (2) make FormalParmVFGNode defined at blackhole address PAGEdge. + // if(SVFUtil::ArgInNoCallerFunction(&*I)) { + // if(I->getType()->isPointerTy()) + // addBlackHoleAddrEdge(argValNodeId); + //} + pag->addFunArgs(&fun,pag->getPAGNode(argValNodeId)); + } + } + for (Function::iterator bit = fun.getLLVMFun()->begin(), ebit = fun.getLLVMFun()->end(); + bit != ebit; ++bit) + { + BasicBlock& bb = *bit; + for (BasicBlock::iterator it = bb.begin(), eit = bb.end(); + it != eit; ++it) + { + Instruction& inst = *it; + setCurrentLocation(&inst,&bb); + visit(inst); + } + } + } + + for (Value* eventCTLObj: pag->getEpollCTLEventObjs()) { + for (Value* eventWaitObj: pag->getEpollWAITEventObjs()) { + addEpollComplexCons(eventWaitObj, eventCTLObj); + } + } + + sanityCheck(); + + pag->initialiseCandidatePointers(); + + pag->setNodeNumAfterPAGBuild(pag->getTotalNodeNum()); + + /* + int totalAddressTaken = 0; + int storedToArray = 0; + + // Validate + for (PAG::iterator it = pag->begin(), eit = pag->end(); it != eit; it++) { + NodeID node = it->first; + PAGNode* pagNode = pag->getPAGNode(node); + if (ValPN* valPN = SVFUtil::dyn_cast(pagNode)) { + if (valPN->hasValue()) { + const Value* v = valPN->getValue(); + int iterations = 10; + if (const Function* f = SVFUtil::dyn_cast(v)) { + if (f->hasAddressTaken()) { + totalAddressTaken++; + } + // Track if this is + llvm::errs() << "PAG Function: " << f->getName() << "\n"; + std::vector workList; + for(PAGEdge* edge: valPN->getOutgoingEdges(PAGEdge::Store)) { + workList.push_back(edge); + } + + for(PAGEdge* edge: valPN->getOutgoingEdges(PAGEdge::Copy)) { + workList.push_back(edge); + } + while(!workList.empty()) { + PAGEdge* e = workList.back(); + workList.pop_back(); + if (SVFUtil::isa(e)) { + NodeID dst = e->getDstID(); + PAGNode* dstNode = pag->getPAGNode(dst); + for(PAGEdge* edge: dstNode->getOutgoingEdges(PAGEdge::Store)) { + workList.push_back(edge); + } + for(PAGEdge* edge: dstNode->getOutgoingEdges(PAGEdge::Copy)) { + workList.push_back(edge); + } + } else if (StorePE* storePE = SVFUtil::dyn_cast(e)) { + NodeID dest = storePE->getDstID(); + PAGNode* destNode = pag->getPAGNode(dest); + const Value* storeValue = destNode->getValue(); + Type* storeTargetType = storeValue->getType(); + llvm::errs() << "Store type: " << *storeTargetType << "\n"; + if (storeTargetType->isPointerTy()) { + storeTargetType = storeTargetType->getPointerElementType(); + } + if (SVFUtil::isa(storeTargetType)) { + llvm::errs() << "PAG Function " << f->getName() << " eventually stored to array: " << *storeValue << "\n"; + storedToArray++; + } + + if (iterations-- < 0) { + continue; + } + if (GepValPN* gepValPN = SVFUtil::dyn_cast(destNode)) { + // Find the base pointer + for (PAGEdge* edge: gepValPN->getIncomingEdges(PAGEdge::NormalGep)) { + if (NormalGepPE* ngep = SVFUtil::dyn_cast(edge)) { + NodeID src = ngep->getSrcID(); + PAGNode* srcBasePtr = pag->getPAGNode(src); + // Where is this stored? + for (PAGEdge* edge: srcBasePtr->getOutgoingEdges(PAGEdge::Store)) { + workList.push_back(edge); + } + for (PAGEdge* edge: srcBasePtr->getOutgoingEdges(PAGEdge::Copy)) { + workList.push_back(edge); + } + } + } + } + } + } + + } + } + } + } + + llvm::errs() << "Validation done\n"; + llvm::errs() << "Total address taken: " << totalAddressTaken << "\n"; + llvm::errs() << "Stored to array: " << storedToArray << "\n"; + */ + + return pag; +} + +/* + * Initial all the nodes from symbol table + */ +void PAGBuilder::initialiseNodes() +{ + DBOUT(DPAGBuild, outs() << "Initialise PAG Nodes ...\n"); + + SymbolTableInfo* symTable = SymbolTableInfo::SymbolInfo(); + + pag->addBlackholeObjNode(); + pag->addConstantObjNode(); + pag->addBlackholePtrNode(); + addNullPtrNode(); + + for (SymbolTableInfo::ValueToIDMapTy::iterator iter = + symTable->valSyms().begin(); iter != symTable->valSyms().end(); + ++iter) + { + DBOUT(DPAGBuild, outs() << "add val node " << iter->second << "\n"); + if(iter->second == symTable->blkPtrSymID() || iter->second == symTable->nullPtrSymID()) + continue; + pag->addValNode(iter->first, iter->second); + } + + for (SymbolTableInfo::ValueToIDMapTy::iterator iter = + symTable->objSyms().begin(); iter != symTable->objSyms().end(); + ++iter) + { + DBOUT(DPAGBuild, outs() << "add obj node " << iter->second << "\n"); + if(iter->second == symTable->blackholeSymID() || iter->second == symTable->constantSymID()) + continue; + pag->addObjNode(iter->first, iter->second); + } + + for (SymbolTableInfo::FunToIDMapTy::iterator iter = + symTable->retSyms().begin(); iter != symTable->retSyms().end(); + ++iter) + { + DBOUT(DPAGBuild, outs() << "add ret node " << iter->second << "\n"); + const SVFFunction* fun = LLVMModuleSet::getLLVMModuleSet()->getSVFFunction(iter->first); + pag->addRetNode(fun, iter->second); + } + + for (SymbolTableInfo::FunToIDMapTy::iterator iter = + symTable->varargSyms().begin(); + iter != symTable->varargSyms().end(); ++iter) + { + DBOUT(DPAGBuild, outs() << "add vararg node " << iter->second << "\n"); + const SVFFunction* fun = LLVMModuleSet::getLLVMModuleSet()->getSVFFunction(iter->first); + pag->addVarargNode(fun, iter->second); + } + + /// add address edges for constant nodes. + for (SymbolTableInfo::ValueToIDMapTy::iterator iter = + symTable->objSyms().begin(); iter != symTable->objSyms().end(); ++iter) + { + DBOUT(DPAGBuild, outs() << "add address edges for constant node " << iter->second << "\n"); + const Value* val = iter->first; + if (symTable->isConstantObjSym(val)) + { + NodeID ptr = pag->getValueNode(val); + if(ptr!= pag->getBlkPtr() && ptr!= pag->getNullPtr()) + { + setCurrentLocation(val, nullptr); + addAddrEdge(iter->second, ptr); + } + } + } + + assert(pag->getTotalNodeNum() >= symTable->getTotalSymNum() + && "not all node been inititalize!!!"); + +} + +/*! + * Return the object node offset according to GEP insn (V). + * Given a gep edge p = q + i, if "i" is a constant then we return its offset size + * otherwise if "i" is a variable determined by runtime, then it is a variant offset + * Return TRUE if the offset of this GEP insn is a constant. + */ +bool PAGBuilder::computeGepOffset(const User *V, LocationSet& ls) +{ + return SymbolTableInfo::SymbolInfo()->computeGepOffset(V,ls); +} + +/*! + * Handle constant expression, and connect the gep edge + */ +void PAGBuilder::processCE(const Value *val) +{ + if (const Constant* ref = SVFUtil::dyn_cast(val)) + { + if (const ConstantExpr* gepce = isGepConstantExpr(ref)) + { + DBOUT(DPAGBuild, + outs() << "handle gep constant expression " << *ref << "\n"); + const Constant* opnd = gepce->getOperand(0); + // handle recursive constant express case (gep (bitcast (gep X 1)) 1) + processCE(opnd); + LocationSet ls; + bool constGep = computeGepOffset(gepce, ls); + // must invoke pag methods here, otherwise it will be a dead recursion cycle + const Value* cval = getCurrentValue(); + const BasicBlock* cbb = getCurrentBB(); + setCurrentLocation(gepce, nullptr); + /* + * The gep edge created are like constexpr (same edge may appear at multiple callsites) + * so bb/inst of this edge may be rewritten several times, we treat it as global here. + */ + addGepEdge(pag->getValueNode(opnd), pag->getValueNode(gepce), ls, constGep); + setCurrentLocation(cval, cbb); + } + else if (const ConstantExpr* castce = isCastConstantExpr(ref)) + { + DBOUT(DPAGBuild, + outs() << "handle cast constant expression " << *ref << "\n"); + const Constant* opnd = castce->getOperand(0); + processCE(opnd); + const Value* cval = getCurrentValue(); + const BasicBlock* cbb = getCurrentBB(); + setCurrentLocation(castce, nullptr); + addCopyEdge(pag->getValueNode(opnd), pag->getValueNode(castce)); + setCurrentLocation(cval, cbb); + } + else if (const ConstantExpr* selectce = isSelectConstantExpr(ref)) + { + DBOUT(DPAGBuild, + outs() << "handle select constant expression " << *ref << "\n"); + const Constant* src1 = selectce->getOperand(1); + const Constant* src2 = selectce->getOperand(2); + processCE(src1); + processCE(src2); + const Value* cval = getCurrentValue(); + const BasicBlock* cbb = getCurrentBB(); + setCurrentLocation(selectce, nullptr); + NodeID nsrc1 = pag->getValueNode(src1); + NodeID nsrc2 = pag->getValueNode(src2); + NodeID nres = pag->getValueNode(selectce); + const CopyPE* cpy1 = addCopyEdge(nsrc1, nres); + const CopyPE* cpy2 = addCopyEdge(nsrc2, nres); + pag->addPhiNode(pag->getPAGNode(nres),cpy1); + pag->addPhiNode(pag->getPAGNode(nres),cpy2); + setCurrentLocation(cval, cbb); + } + // if we meet a int2ptr, then it points-to black hole + else if (const ConstantExpr* int2Ptrce = isInt2PtrConstantExpr(ref)) + { + addGlobalBlackHoleAddrEdge(pag->getValueNode(int2Ptrce), int2Ptrce); + } + else if (const ConstantExpr* ptr2Intce = isPtr2IntConstantExpr(ref)) + { + const Constant* opnd = ptr2Intce->getOperand(0); + processCE(opnd); + const BasicBlock* cbb = getCurrentBB(); + const Value* cval = getCurrentValue(); + setCurrentLocation(ptr2Intce, nullptr); + addCopyEdge(pag->getValueNode(opnd), pag->getValueNode(ptr2Intce)); + setCurrentLocation(cval, cbb); + } + else if(isTruncConstantExpr(ref) || isCmpConstantExpr(ref)) + { + // we don't handle trunc and cmp instruction for now + const Value* cval = getCurrentValue(); + const BasicBlock* cbb = getCurrentBB(); + setCurrentLocation(ref, nullptr); + NodeID dst = pag->getValueNode(ref); + addBlackHoleAddrEdge(dst); + setCurrentLocation(cval, cbb); + } + else if (isBinaryConstantExpr(ref)) + { + // we don't handle binary constant expression like add(x,y) now + const Value* cval = getCurrentValue(); + const BasicBlock* cbb = getCurrentBB(); + setCurrentLocation(ref, nullptr); + NodeID dst = pag->getValueNode(ref); + addBlackHoleAddrEdge(dst); + setCurrentLocation(cval, cbb); + } + else if (isUnaryConstantExpr(ref)) + { + // we don't handle unary constant expression like fneg(x) now + const Value* cval = getCurrentValue(); + const BasicBlock* cbb = getCurrentBB(); + setCurrentLocation(ref, nullptr); + NodeID dst = pag->getValueNode(ref); + addBlackHoleAddrEdge(dst); + setCurrentLocation(cval, cbb); + } + else if (SVFUtil::isa(ref)) + { + // we don't handle constant agrgregate like constant vectors + } + else if (SVFUtil::isa(ref)) + { + // blockaddress instruction (e.g. i8* blockaddress(@run_vm, %182)) + // is treated as constant data object for now, see LLVMUtil.h:397, SymbolTableInfo.cpp:674 and PAGBuilder.cpp:183-194 + const Value *cval = getCurrentValue(); + const BasicBlock *cbb = getCurrentBB(); + setCurrentLocation(ref, nullptr); + NodeID dst = pag->getValueNode(ref); + addAddrEdge(pag->getConstantNode(), dst); + setCurrentLocation(cval, cbb); + } + else + { + if(SVFUtil::isa(val)) + assert(false && "we don't handle all other constant expression for now!"); + } + } +} +/*! + * Get the field of the global variable node + * FIXME:Here we only get the field that actually used in the program + * We ignore the initialization of global variable field that not used in the program + */ +NodeID PAGBuilder::getGlobalVarField(const GlobalVariable *gvar, u32_t offset) +{ + + // if the global variable do not have any field needs to be initialized + if (offset == 0 && gvar->getInitializer()->getType()->isSingleValueType()) + { + return getValueNode(gvar); + } + /// if we did not find the constant expression in the program, + /// then we need to create a gep node for this field + else + { + const Type *gvartype = gvar->getType(); + while (const PointerType *ptype = SVFUtil::dyn_cast(gvartype)) + gvartype = ptype->getElementType(); + return getGepValNode(gvar, LocationSet(offset), gvartype, offset); + } +} + +/*For global variable initialization + * Give a simple global variable + * int x = 10; // store 10 x (constant, non pointer) | + * int *y = &x; // store x y (pointer type) + * Given a struct + * struct Z { int s; int *t;}; + * Global initialization: + * struct Z z = {10,&x}; // store x z.t (struct type) + * struct Z *m = &z; // store z m (pointer type) + * struct Z n = {10,&z.s}; // store z.s n , &z.s constant expression (constant expression) + */ +void PAGBuilder::InitialGlobal(const GlobalVariable *gvar, Constant *C, + u32_t offset) +{ + DBOUT(DPAGBuild, + outs() << "global " << *gvar << " constant initializer: " << *C + << "\n"); + + if (C->getType()->isSingleValueType()) + { + NodeID src = getValueNode(C); + // get the field value if it is avaiable, otherwise we create a dummy field node. + setCurrentLocation(gvar, nullptr); + NodeID field = getGlobalVarField(gvar, offset); + + if (SVFUtil::isa(C) || SVFUtil::isa(C)) + { + setCurrentLocation(C, nullptr); + addStoreEdge(src, field); + } + else if (SVFUtil::isa(C)) + { + // add gep edge of C1 itself is a constant expression + processCE(C); + setCurrentLocation(C, nullptr); + addStoreEdge(src, field); + } + else if (SVFUtil::isa(C)) + { + // blockaddress instruction (e.g. i8* blockaddress(@run_vm, %182)) + // is treated as constant data object for now, see LLVMUtil.h:397, SymbolTableInfo.cpp:674 and PAGBuilder.cpp:183-194 + processCE(C); + setCurrentLocation(C, nullptr); + addAddrEdge(pag->getConstantNode(), src); + } + else + { + setCurrentLocation(C, nullptr); + addStoreEdge(src, field); + /// src should not point to anything yet + if (C->getType()->isPtrOrPtrVectorTy() && src != pag->getNullPtr()) + addCopyEdge(pag->getNullPtr(), src); + } + } + else if (SVFUtil::isa(C)) + { + if (cppUtil::isValVtbl(gvar) == false) + for (u32_t i = 0, e = C->getNumOperands(); i != e; i++) + InitialGlobal(gvar, SVFUtil::cast(C->getOperand(i)), offset); + + } + else if (SVFUtil::isa(C)) + { + const StructType *sty = SVFUtil::cast(C->getType()); + const std::vector& offsetvect = + SymbolTableInfo::SymbolInfo()->getFattenFieldIdxVec(sty); + for (u32_t i = 0, e = C->getNumOperands(); i != e; i++) + { + u32_t off = offsetvect[i]; + InitialGlobal(gvar, SVFUtil::cast(C->getOperand(i)), offset + off); + } + + } + else + { + //TODO:assert(false,"what else do we have"); + } +} + +/*! + * Visit global variables for building PAG + */ +void PAGBuilder::visitGlobal(SVFModule* svfModule) +{ + + /// initialize global variable + for (SVFModule::global_iterator I = svfModule->global_begin(), E = + svfModule->global_end(); I != E; ++I) + { + GlobalVariable *gvar = *I; + NodeID idx = getValueNode(gvar); + NodeID obj = getObjectNode(gvar); + + setCurrentLocation(gvar, nullptr); + addAddrEdge(obj, idx); + + if (gvar->hasInitializer()) + { + Constant *C = gvar->getInitializer(); + DBOUT(DPAGBuild, outs() << "add global var node " << *gvar << "\n"); + InitialGlobal(gvar, C, 0); + } + } + + /// initialize global functions + for (SVFModule::llvm_const_iterator I = svfModule->llvmFunBegin(), E = + svfModule->llvmFunEnd(); I != E; ++I) + { + const Function *fun = *I; + NodeID idx = getValueNode(fun); + NodeID obj = getObjectNode(fun); + + DBOUT(DPAGBuild, outs() << "add global function node " << fun->getName() << "\n"); + setCurrentLocation(fun, nullptr); + addAddrEdge(obj, idx); + } + + // Handle global aliases (due to linkage of multiple bc files), e.g., @x = internal alias @y. We need to add a copy from y to x. + for (SVFModule::alias_iterator I = svfModule->alias_begin(), E = svfModule->alias_end(); I != E; I++) + { + NodeID dst = pag->getValueNode(*I); + NodeID src = pag->getValueNode((*I)->getAliasee()); + processCE((*I)->getAliasee()); + setCurrentLocation(*I, nullptr); + addCopyEdge(src,dst); + } +} + +/*! + * Visit alloca instructions + * Add edge V (dst) <-- O (src), V here is a value node on PAG, O is object node on PAG + */ +void PAGBuilder::visitAllocaInst(AllocaInst &inst) +{ + + // AllocaInst should always be a pointer type + assert(SVFUtil::isa(inst.getType())); + + DBOUT(DPAGBuild, outs() << "process alloca " << inst << " \n"); + NodeID dst = getValueNode(&inst); + + NodeID src = getObjectNode(&inst); + + addAddrEdge(src, dst); + +} + +/*! + * Visit phi instructions + */ +void PAGBuilder::visitPHINode(PHINode &inst) +{ + + DBOUT(DPAGBuild, outs() << "process phi " << inst << " \n"); + + NodeID dst = getValueNode(&inst); + + for (Size_t i = 0; i < inst.getNumIncomingValues(); ++i) + { + const Value* val = inst.getIncomingValue(i); + const Instruction* incomingInst = SVFUtil::dyn_cast(val); + assert((incomingInst==nullptr) || (incomingInst->getFunction() == inst.getFunction())); + + NodeID src = getValueNode(val); + const CopyPE* copy = addCopyEdge(src, dst); + pag->addPhiNode(pag->getPAGNode(dst), copy); + } +} + +/* + * Visit load instructions + */ +void PAGBuilder::visitLoadInst(LoadInst &inst) +{ + DBOUT(DPAGBuild, outs() << "process load " << inst << " \n"); + + NodeID dst = getValueNode(&inst); + + NodeID src = getValueNode(inst.getPointerOperand()); + + addLoadEdge(src, dst); +} + +/*! + * Visit store instructions + */ +void PAGBuilder::visitStoreInst(StoreInst &inst) +{ + // StoreInst itself should always not be a pointer type + assert(!SVFUtil::isa(inst.getType())); + + DBOUT(DPAGBuild, outs() << "process store " << inst << " \n"); + + NodeID dst = getValueNode(inst.getPointerOperand()); + + NodeID src = getValueNode(inst.getValueOperand()); + + addStoreEdge(src, dst); + +} + +/* +bool PAGBuilder::instrumentInvariant(GetElementPtrInst* gepInst) { + // assert that the pointer isn't pointing in the middle of an object + Type* gepSrcTy = gepInst->getResultElementType(); + Value* index = gepInst->getOperand(gepInst->getNumOperands() - 1); + + llvm::Module *mod = SVF::LLVMModuleSet::getLLVMModuleSet()->getMainLLVMModule(); + if (StructType* stTy = SVFUtil::dyn_cast(gepSrcTy)) { + vgeps.push_back(gepInst); + return true; + } else if (ArrayType* arrTy = SVFUtil::dyn_cast(gepSrcTy)) { + // No need to add any invaraints + return true; + } else { + return false; + } +} +*/ + +/*! + * Visit getelementptr instructions + */ +void PAGBuilder::visitGetElementPtrInst(GetElementPtrInst &inst) +{ + NodeID dst = getValueNode(&inst); + // GetElementPtrInst should always be a pointer or a vector contains pointers + // for now we don't handle vector type here + if(SVFUtil::isa(inst.getType())) + { + addBlackHoleAddrEdge(dst); + return; + } + + assert(SVFUtil::isa(inst.getType())); + + DBOUT(DPAGBuild, outs() << "process gep " << inst << " \n"); + + NodeID src = getValueNode(inst.getPointerOperand()); + + LocationSet ls; + bool constGep = computeGepOffset(&inst, ls); + GepPE* gepEdge = addGepEdge(src, dst, ls, constGep); + if (VariantGepPE* vgepEdge = SVFUtil::dyn_cast(gepEdge)) { + const Value* edgeValue = vgepEdge->getValue(); + const GetElementPtrInst* gepInst = SVFUtil::dyn_cast(edgeValue); + assert(gepInst && "Not gep inst and is variantGep edge?"); + pag->getVarGeps().push_back(gepInst); + // Check the type of the pointer. If it is a pointer to a struct type + // then it's probably iterating over an array of that type. We need to + // record this here, so that we can skip VGEP invariants later on + Type* gepTy = gepInst->getPointerOperand()->getType(); + + // See if it's a pointer to array-of-struct, or + // arrays-of-pointers-of-structs + while (PointerType* ptrTy = SVFUtil::dyn_cast(gepTy)) { + gepTy = gepTy->getPointerElementType(); + } + + if (SVFUtil::isa(gepTy)) { + vgepEdge->setStructType(true); + } else { + vgepEdge->setStructType(false); + } + + }/* else { + NormalGepPE* normalGep = SVFUtil::dyn_cast(gepEdge); + llvm::errs() << "Normal Gep: " << inst << " ls offset: " << normalGep->getOffset() << "\n"; + + }*/ +} + +/* + * Visit cast instructions + */ +void PAGBuilder::visitCastInst(CastInst &inst) +{ + + DBOUT(DPAGBuild, outs() << "process cast " << inst << " \n"); + NodeID dst = getValueNode(&inst); + + if (SVFUtil::isa(&inst)) + { + addBlackHoleAddrEdge(dst); + } + else + { + Value * opnd = inst.getOperand(0); + if (!SVFUtil::isa(opnd->getType())) + opnd = stripAllCasts(opnd); + + NodeID src = getValueNode(opnd); + addCopyEdge(src, dst); + } +} + +/*! + * Visit Binary Operator + */ +void PAGBuilder::visitBinaryOperator(BinaryOperator &inst) +{ + NodeID dst = getValueNode(&inst); + for (u32_t i = 0; i < inst.getNumOperands(); i++) + { + Value* opnd = inst.getOperand(i); + NodeID src = getValueNode(opnd); + const BinaryOPPE* binayPE = addBinaryOPEdge(src, dst); + pag->addBinaryNode(pag->getPAGNode(dst),binayPE); + } +} + +/*! + * Visit Unary Operator + */ +void PAGBuilder::visitUnaryOperator(UnaryOperator &inst) +{ + NodeID dst = getValueNode(&inst); + for (u32_t i = 0; i < inst.getNumOperands(); i++) + { + Value* opnd = inst.getOperand(i); + NodeID src = getValueNode(opnd); + const UnaryOPPE* unaryPE = addUnaryOPEdge(src, dst); + pag->addUnaryNode(pag->getPAGNode(dst),unaryPE); + } +} + +/*! + * Visit compare instruction + */ +void PAGBuilder::visitCmpInst(CmpInst &inst) +{ + NodeID dst = getValueNode(&inst); + for (u32_t i = 0; i < inst.getNumOperands(); i++) + { + Value* opnd = inst.getOperand(i); + NodeID src = getValueNode(opnd); + const CmpPE* cmpPE = addCmpEdge(src, dst); + pag->addCmpNode(pag->getPAGNode(dst),cmpPE); + } +} + + +/*! + * Visit select instructions + */ +void PAGBuilder::visitSelectInst(SelectInst &inst) +{ + + DBOUT(DPAGBuild, outs() << "process select " << inst << " \n"); + + NodeID dst = getValueNode(&inst); + NodeID src1 = getValueNode(inst.getTrueValue()); + NodeID src2 = getValueNode(inst.getFalseValue()); + const CopyPE* cpy1 = addCopyEdge(src1, dst); + const CopyPE* cpy2 = addCopyEdge(src2, dst); + + /// Two operands have same incoming basic block, both are the current BB + pag->addPhiNode(pag->getPAGNode(dst), cpy1); + pag->addPhiNode(pag->getPAGNode(dst), cpy2); +} + +/* + * Visit callsites + */ +void PAGBuilder::visitCallSite(CallSite cs) +{ + + // skip llvm intrinsics + if(isIntrinsicInst(cs.getInstruction())) + return; + + DBOUT(DPAGBuild, + outs() << "process callsite " << *cs.getInstruction() << "\n"); + + CallBlockNode* callBlockNode = pag->getICFG()->getCallBlockNode(cs.getInstruction()); + RetBlockNode* retBlockNode = pag->getICFG()->getRetBlockNode(cs.getInstruction()); + + pag->addCallSite(callBlockNode); + + /// Collect callsite arguments and returns + for(CallSite::arg_iterator itA = cs.arg_begin(), ieA = cs.arg_end(); itA!=ieA; ++itA) + pag->addCallSiteArgs(callBlockNode,pag->getPAGNode(getValueNode(*itA))); + + if(!cs.getType()->isVoidTy()) + pag->addCallSiteRets(retBlockNode,pag->getPAGNode(getValueNode(cs.getInstruction()))); + + const SVFFunction *callee = getCallee(cs); + + if (callee) + { + if (isExtCall(callee) || isPseudoExtHeapCall(cs)) + { + if (ExternalPAG::hasExternalPAG(callee)) + { + ExternalPAG::connectCallsiteToExternalPAG(&cs); + } + else + { + // There is no extpag for the function, use the old method. + handleExtCall(cs, callee); + } + } + else + { + handleDirectCall(cs, callee); + } + } + else + { + //If the callee was not identified as a function (null F), this is indirect. + handleIndCall(cs); + } +} + +/*! + * Visit return instructions of a function + */ +void PAGBuilder::visitReturnInst(ReturnInst &inst) +{ + + // ReturnInst itself should always not be a pointer type + assert(!SVFUtil::isa(inst.getType())); + + DBOUT(DPAGBuild, outs() << "process return " << inst << " \n"); + + if(Value *src = inst.getReturnValue()) + { + const SVFFunction *F = LLVMModuleSet::getLLVMModuleSet()->getSVFFunction(inst.getParent()->getParent()); + + NodeID rnF = getReturnNode(F); + NodeID vnS = getValueNode(src); + //vnS may be null if src is a null ptr + const CopyPE* copy = addCopyEdge(vnS, rnF); + pag->addPhiNode(pag->getPAGNode(rnF), copy); + } +} + + +/*! + * visit extract value instructions for structures in registers + * TODO: for now we just assume the pointer after extraction points to blackhole + * for example %24 = extractvalue { i32, %struct.s_hash* } %call34, 0 + * %24 is a pointer points to first field of a register value %call34 + * however we can not create %call34 as an memory object, as it is register value. + * Is that necessary treat extract value as getelementptr instruction later to get more precise results? + */ +void PAGBuilder::visitExtractValueInst(ExtractValueInst &inst) +{ + NodeID dst = getValueNode(&inst); + addBlackHoleAddrEdge(dst); +} + +/*! + * The �extractelement� instruction extracts a single scalar element from a vector at a specified index. + * TODO: for now we just assume the pointer after extraction points to blackhole + * The first operand of an �extractelement� instruction is a value of vector type. + * The second operand is an index indicating the position from which to extract the element. + * + * = extractelement <4 x i32> %vec, i32 0 ; yields i32 + */ +void PAGBuilder::visitExtractElementInst(ExtractElementInst &inst) +{ + NodeID dst = getValueNode(&inst); + addBlackHoleAddrEdge(dst); +} + +/*! + * Branch and switch instructions are treated as UnaryOP + * br %cmp label %if.then, label %if.else + */ +void PAGBuilder::visitBranchInst(BranchInst &inst){ + NodeID dst = getValueNode(&inst); + NodeID src; + if (inst.isConditional()) + src = getValueNode(inst.getCondition()); + else + src = pag->getNullPtr(); + const UnaryOPPE *unaryPE = addUnaryOPEdge(src, dst); + pag->addUnaryNode(pag->getPAGNode(dst),unaryPE); +} + +void PAGBuilder::visitSwitchInst(SwitchInst &inst){ + NodeID dst = getValueNode(&inst); + Value* opnd = inst.getCondition(); + NodeID src = getValueNode(opnd); + const UnaryOPPE* unaryPE = addUnaryOPEdge(src, dst); + pag->addUnaryNode(pag->getPAGNode(dst),unaryPE); +} + +/// %ap = alloca %struct.va_list +/// %ap2 = bitcast %struct.va_list* %ap to i8* +/// ; Read a single integer argument from %ap2 +/// %tmp = va_arg i8* %ap2, i32 (VAArgInst) +/// TODO: for now, create a copy edge from %ap2 to %tmp, we assume here %tmp should point to the n-th argument of the var_args +void PAGBuilder::visitVAArgInst(VAArgInst &inst){ + NodeID dst = getValueNode(&inst); + Value* opnd = inst.getPointerOperand(); + NodeID src = getValueNode(opnd); + addCopyEdge(src,dst); +} + +/// = freeze ty +/// If is undef or poison, ‘freeze’ returns an arbitrary, but fixed value of type `ty` +/// Otherwise, this instruction is a no-op and returns the input +/// For now, we assume is never a posion or undef. +void PAGBuilder::visitFreezeInst(FreezeInst &inst){ + NodeID dst = getValueNode(&inst); + for (u32_t i = 0; i < inst.getNumOperands(); i++) + { + Value* opnd = inst.getOperand(i); + NodeID src = getValueNode(opnd); + addCopyEdge(src,dst); + } +} + + +/*! + * Add the constraints for a direct, non-external call. + */ +void PAGBuilder::handleDirectCall(CallSite cs, const SVFFunction *F) +{ + + assert(F); + + DBOUT(DPAGBuild, + outs() << "handle direct call " << *cs.getInstruction() << " callee " << *F << "\n"); + + //Only handle the ret.val. if it's used as a ptr. + NodeID dstrec = getValueNode(cs.getInstruction()); + //Does it actually return a ptr? + if (!cs.getType()->isVoidTy()) + { + NodeID srcret = getReturnNode(F); + CallBlockNode* icfgNode = pag->getICFG()->getCallBlockNode(cs.getInstruction()); + addRetEdge(srcret, dstrec,icfgNode); + } + //Iterators for the actual and formal parameters + CallSite::arg_iterator itA = cs.arg_begin(), ieA = cs.arg_end(); + Function::const_arg_iterator itF = F->getLLVMFun()->arg_begin(), ieF = F->getLLVMFun()->arg_end(); + //Go through the fixed parameters. + DBOUT(DPAGBuild, outs() << " args:"); + for (; itF != ieF; ++itA, ++itF) + { + //Some programs (e.g. Linux kernel) leave unneeded parameters empty. + if (itA == ieA) + { + DBOUT(DPAGBuild, outs() << " !! not enough args\n"); + break; + } + const Value *AA = *itA, *FA = &*itF; //current actual/formal arg + + DBOUT(DPAGBuild, outs() << "process actual parm " << *AA << " \n"); + + NodeID dstFA = getValueNode(FA); + NodeID srcAA = getValueNode(AA); + CallBlockNode* icfgNode = pag->getICFG()->getCallBlockNode(cs.getInstruction()); + addCallEdge(srcAA, dstFA, icfgNode); + } + //Any remaining actual args must be varargs. + if (F->getLLVMFun()->isVarArg()) + { + NodeID vaF = getVarargNode(F); + DBOUT(DPAGBuild, outs() << "\n varargs:"); + for (; itA != ieA; ++itA) + { + Value *AA = *itA; + NodeID vnAA = getValueNode(AA); + CallBlockNode* icfgNode = pag->getICFG()->getCallBlockNode(cs.getInstruction()); + addCallEdge(vnAA,vaF, icfgNode); + } + } + if(itA != ieA) + { + /// FIXME: this assertion should be placed for correct checking except + /// bug program like 188.ammp, 300.twolf + writeWrnMsg("too many args to non-vararg func."); + writeWrnMsg("(" + getSourceLoc(cs.getInstruction()) + ")"); + + } +} + + +/*! + * Find the base type and the max possible offset of an object pointed to by (V). + */ +const Type *PAGBuilder::getBaseTypeAndFlattenedFields(Value *V, std::vector &fields) +{ + return SymbolTableInfo::SymbolInfo()->getBaseTypeAndFlattenedFields(V, fields); +} + +void PAGBuilder::addEpollComplexCons(Value* D, Value* S) { + assert(D && S); + llvm::Module *module = SVF::LLVMModuleSet::getLLVMModuleSet()->getMainLLVMModule(); + + NodeID vnD= getValueNode(D), vnS= getValueNode(S); + if(!vnD || !vnS) + return; + + int sz = 2; // 2 fields + + // Get the type + StructType* epollEventTy = nullptr; + + for (auto* S: module->getIdentifiedStructTypes()) { + if (S->getName() == "struct.epoll_event") { + epollEventTy = S; + break; + } + } + + Type* dtype, *stype; + dtype = stype = epollEventTy; + + assert(epollEventTy && "epoll event type should be found"); + + if (SVFUtil::isa(S)) { + LocationSet ls(1); + NodeID dField = getGepValNode(D,ls,dtype,1); + NodeID funcValId = pag->getValueNode(S); + addStoreEdge(funcValId, dField); + + } else { + //For each field (i), add (Ti = *S + i) and (*D + i = Ti). + for (u32_t index = 0; index < sz; index++) { + LocationSet ls1(index); + LocationSet ls2(index); + + NodeID dField = getGepValNode(D,ls1,dtype,index); + NodeID sField = getGepValNode(S,ls2,stype,index); + NodeID dummy = pag->addDummyValNode(); + addLoadEdge(sField,dummy); + addStoreEdge(dummy,dField); + } + } +} + +/*! + * Add the load/store constraints and temp. nodes for the complex constraint + * *D = *S (where D/S may point to structs). + */ +void PAGBuilder::addComplexConsForExt(Value *D, Value *S, u32_t sz) +{ + assert(D && S); + NodeID vnD= getValueNode(D), vnS= getValueNode(S); + if(!vnD || !vnS) + return; + + std::vector fields; + + //Get the max possible size of the copy, unless it was provided. + std::vector srcFields; + std::vector dstFields; + const Type *stype = getBaseTypeAndFlattenedFields(S, srcFields); + const Type *dtype = getBaseTypeAndFlattenedFields(D, dstFields); + if(srcFields.size() > dstFields.size()) + fields = dstFields; + else + fields = srcFields; + + /// If sz is 0, we will add edges for all fields. + if (sz == 0) + sz = fields.size(); + + assert(fields.size() >= sz && "the number of flattened fields is smaller than size"); + if (fields.size() == 1 && (isConstantData(D) || isConstantData(S))) { + NodeID dummy = pag->addDummyValNode(); + addLoadEdge(vnD,dummy); + addStoreEdge(dummy,vnS); + return; + } + + //For each field (i), add (Ti = *S + i) and (*D + i = Ti). + for (u32_t index = 0; index < sz; index++) + { + NodeID dField = getGepValNode(D,fields[index],dtype,index); + NodeID sField = getGepValNode(S,fields[index],stype,index); + NodeID dummy = pag->addDummyValNode(); + addLoadEdge(sField,dummy); + addStoreEdge(dummy,dField); + } +} + + +/*! + * Handle external calls + */ +void PAGBuilder::handleExtCall(CallSite cs, const SVFFunction *callee) +{ + const Instruction* inst = cs.getInstruction(); + if (isHeapAllocOrStaticExtCall(cs) || isPseudoExtHeapCall(cs) ) + { + // case 1: ret = new obj + if (isHeapAllocExtCallViaRet(cs) || isStaticExtCall(cs) || isPseudoExtHeapCall(cs)) + { + NodeID val = getValueNode(inst); + NodeID obj = getObjectNode(inst); + addAddrEdge(obj, val); + } + // case 2: *arg = new obj + else + { + assert(isHeapAllocExtCallViaArg(cs) && "Must be heap alloc call via arg."); + int arg_pos = getHeapAllocHoldingArgPosition(callee); + const Value *arg = cs.getArgument(arg_pos); + if (arg->getType()->isPointerTy()) + { + NodeID vnArg = getValueNode(arg); + NodeID dummy = pag->addDummyValNode(); + NodeID obj = pag->addDummyObjNode(); + if (vnArg && dummy && obj) + { + addAddrEdge(obj, dummy); + addStoreEdge(dummy, vnArg); + } + } + else + { + writeWrnMsg("Arg receiving new object must be pointer type"); + } + } + } + else + { + if(isExtCall(callee)) + { + ExtAPI::extf_t tF= extCallTy(callee); + switch(tF) + { + case ExtAPI::EFT_REALLOC: + { + if(!SVFUtil::isa(inst->getType())) + break; + // e.g. void *realloc(void *ptr, size_t size) + // if ptr is null then we will treat it as a malloc + // if ptr is not null, then we assume a new data memory will be attached to + // the tail of old allocated memory block. + if(SVFUtil::isa(cs.getArgument(0))) + { + NodeID val = getValueNode(inst); + NodeID obj = getObjectNode(inst); + addAddrEdge(obj, val); + } + break; + } + case ExtAPI::EFT_L_A0: + case ExtAPI::EFT_L_A1: + case ExtAPI::EFT_L_A2: + case ExtAPI::EFT_L_A8: + { + if(!SVFUtil::isa(inst->getType())) + break; + NodeID dstNode = getValueNode(inst); + Size_t arg_pos; + switch(tF) + { + case ExtAPI::EFT_L_A1: + arg_pos= 1; + break; + case ExtAPI::EFT_L_A2: + arg_pos= 2; + break; + case ExtAPI::EFT_L_A8: + arg_pos= 8; + break; + default: + arg_pos= 0; + } + Value *src= cs.getArgument(arg_pos); + if(SVFUtil::isa(src->getType())) + { + NodeID srcNode = getValueNode(src); + addCopyEdge(srcNode, dstNode); + } + else + addBlackHoleAddrEdge(dstNode); + break; + break; + } + case ExtAPI::EFT_L_A0__A0R_A1: + { + // this is only for memset(void *str, int c, size_t n) + // which copies the character c (an unsigned char) to the first n characters of the string pointed to, by the argument str + // However, the second argument is non-pointer, thus we can not use addComplexConsForExt + // addComplexConsForExt(cs.getArgument(0), cs.getArgument(1)); + if(SVFUtil::isa(inst->getType())) + addCopyEdge(getValueNode(cs.getArgument(0)), getValueNode(inst)); + break; + } + case ExtAPI::EFT_L_A0__A0R_A1R: + { + addComplexConsForExt(cs.getArgument(0), cs.getArgument(1)); + //memcpy returns the dest. + if(SVFUtil::isa(inst->getType())) + { + addCopyEdge(getValueNode(cs.getArgument(0)), getValueNode(inst)); + } + break; + } + case ExtAPI::EFT_A1R_A0R: + addComplexConsForExt(cs.getArgument(1), cs.getArgument(0)); + break; + case ExtAPI::EFT_L_A1__FunPtr: + { + /// handling external function e.g., void *dlsym(void *handle, const char *funname); + const Value *src = cs.getArgument(1); + if(const GetElementPtrInst* gep = SVFUtil::dyn_cast(src)) + src = stripConstantCasts(gep->getPointerOperand()); + if(const GlobalVariable* glob = SVFUtil::dyn_cast(src)){ + if(const ConstantDataArray* constarray = SVFUtil::dyn_cast(glob->getInitializer())){ + if(const SVFFunction* fun = getProgFunction(svfMod,constarray->getAsCString().str())){ + NodeID srcNode = getValueNode(fun->getLLVMFun()); + addCopyEdge(srcNode, getValueNode(inst)); + } + } + } + break; + } + case ExtAPI::EFT_A3R_A1R_NS: + //These func. are never used to copy structs, so the size is 1. + addComplexConsForExt(cs.getArgument(3), cs.getArgument(1), 1); + break; + case ExtAPI::EFT_A1R_A0: + { + NodeID vnD= getValueNode(cs.getArgument(1)); + NodeID vnS= getValueNode(cs.getArgument(0)); + if(vnD && vnS) + addStoreEdge(vnS,vnD); + break; + } + case ExtAPI::EFT_A2R_A1: + { + NodeID vnD= getValueNode(cs.getArgument(2)); + NodeID vnS= getValueNode(cs.getArgument(1)); + if(vnD && vnS) + addStoreEdge(vnS,vnD); + break; + } + case ExtAPI::EFT_A4R_A1: + { + NodeID vnD= getValueNode(cs.getArgument(4)); + NodeID vnS= getValueNode(cs.getArgument(1)); + if(vnD && vnS) + addStoreEdge(vnS,vnD); + break; + } + case ExtAPI::EFT_L_A0__A1_A0: + { + /// this case is for strcpy(dst,src); to maintain its semantics + /// we will store src to the base of dst instead of dst. + /// dst = load base + /// store src base + if (const LoadInst *load = SVFUtil::dyn_cast(cs.getArgument(0))) { + addStoreEdge(getValueNode(cs.getArgument(1)), getValueNode(load->getPointerOperand())); + if (SVFUtil::isa(inst->getType())) + addLoadEdge(getValueNode(load->getPointerOperand()), getValueNode(inst)); + } +// else if (const GetElementPtrInst *gep = SVFUtil::dyn_cast(cs.getArgument(0))) { +// addStoreEdge(getValueNode(cs.getArgument(1)), getValueNode(cs.getArgument(0))); +// if (SVFUtil::isa(inst->getType())) +// addCopyEdge(getValueNode(cs.getArgument(0)), getValueNode(inst)); +// } + break; + } + case ExtAPI::EFT_L_A0__A2R_A0: + { + if(SVFUtil::isa(inst->getType())) + { + //Do the L_A0 part if the retval is used. + NodeID vnD= getValueNode(inst); + Value *src= cs.getArgument(0); + if(SVFUtil::isa(src->getType())) + { + NodeID vnS= getValueNode(src); + if(vnS) + addCopyEdge(vnS,vnD); + } + else + addBlackHoleAddrEdge(vnD); + } + //Do the A2R_A0 part. + NodeID vnD= getValueNode(cs.getArgument(2)); + NodeID vnS= getValueNode(cs.getArgument(0)); + if(vnD && vnS) + addStoreEdge(vnS,vnD); + break; + } + case ExtAPI::EFT_A0R_NEW: + case ExtAPI::EFT_A1R_NEW: + case ExtAPI::EFT_A2R_NEW: + case ExtAPI::EFT_A4R_NEW: + case ExtAPI::EFT_A11R_NEW: + { + assert(!"Alloc via arg cases are not handled here."); + break; + } + case ExtAPI::EFT_ALLOC: + case ExtAPI::EFT_NOSTRUCT_ALLOC: + case ExtAPI::EFT_STAT: + case ExtAPI::EFT_STAT2: + if(SVFUtil::isa(inst->getType())) + assert(!"alloc type func. are not handled here"); + else + { + // fdopen will return an integer in LLVM IR. + writeWrnMsg("alloc type func do not return pointer type"); + } + break; + case ExtAPI::EFT_NOOP: + case ExtAPI::EFT_FREE: + break; + case ExtAPI::EFT_STD_RB_TREE_INSERT_AND_REBALANCE: + { + Value *vArg1 = cs.getArgument(1); + Value *vArg3 = cs.getArgument(3); + + // We have vArg3 points to the entry of _Rb_tree_node_base { color; parent; left; right; }. + // Now we calculate the offset from base to vArg3 + NodeID vnArg3 = pag->getValueNode(vArg3); + Size_t offset = pag->getLocationSetFromBaseNode(vnArg3).getOffset(); + + // We get all flattened fields of base + vector fields; + const Type *type = getBaseTypeAndFlattenedFields(vArg3, fields); + assert(fields.size() >= 4 && "_Rb_tree_node_base should have at least 4 fields.\n"); + + // We summarize the side effects: arg3->parent = arg1, arg3->left = arg1, arg3->right = arg1 + // Note that arg0 is aligned with "offset". + for (int i = offset + 1; i <= offset + 3; ++i) + { + NodeID vnD = getGepValNode(vArg3, fields[i], type, i); + NodeID vnS = getValueNode(vArg1); + if(vnD && vnS) + addStoreEdge(vnS,vnD); + } + break; + } + case ExtAPI::EFT_STD_RB_TREE_INCREMENT: + { + NodeID vnD = pag->getValueNode(inst); + + Value *vArg = cs.getArgument(0); + NodeID vnArg = pag->getValueNode(vArg); + Size_t offset = pag->getLocationSetFromBaseNode(vnArg).getOffset(); + + // We get all fields + vector fields; + const Type *type = getBaseTypeAndFlattenedFields(vArg,fields); + assert(fields.size() >= 4 && "_Rb_tree_node_base should have at least 4 fields.\n"); + + // We summarize the side effects: ret = arg->parent, ret = arg->left, ret = arg->right + // Note that arg0 is aligned with "offset". + for (int i = offset + 1; i <= offset + 3; ++i) + { + NodeID vnS = getGepValNode(vArg, fields[i], type, i); + if(vnD && vnS) + addStoreEdge(vnS,vnD); + } + break; + } + case ExtAPI::EFT_STD_LIST_HOOK: + { + Value *vSrc = cs.getArgument(0); + Value *vDst = cs.getArgument(1); + NodeID src = pag->getValueNode(vSrc); + NodeID dst = pag->getValueNode(vDst); + addStoreEdge(src, dst); + break; + } + case ExtAPI::CPP_EFT_A0R_A1: + { + SymbolTableInfo* symTable = SymbolTableInfo::SymbolInfo(); + if (symTable->getModelConstants()) + { + NodeID vnD = pag->getValueNode(cs.getArgument(0)); + NodeID vnS = pag->getValueNode(cs.getArgument(1)); + addStoreEdge(vnS, vnD); + } + break; + } + case ExtAPI::CPP_EFT_A0R_A1R: + { + SymbolTableInfo* symTable = SymbolTableInfo::SymbolInfo(); + if (symTable->getModelConstants()) + { + NodeID vnD = getValueNode(cs.getArgument(0)); + NodeID vnS = getValueNode(cs.getArgument(1)); + assert(vnD && vnS && "dst or src not exist?"); + NodeID dummy = pag->addDummyValNode(); + addLoadEdge(vnS,dummy); + addStoreEdge(dummy,vnD); + } + break; + } + case ExtAPI::CPP_EFT_A1R: + { + SymbolTableInfo* symTable = SymbolTableInfo::SymbolInfo(); + if (symTable->getModelConstants()) + { + NodeID vnS = getValueNode(cs.getArgument(1)); + assert(vnS && "src not exist?"); + NodeID dummy = pag->addDummyValNode(); + addLoadEdge(vnS,dummy); + } + break; + } + case ExtAPI::CPP_EFT_DYNAMIC_CAST: + { + Value *vArg0 = cs.getArgument(0); + Value *retVal = cs.getInstruction(); + NodeID src = getValueNode(vArg0); + assert(src && "src not exist?"); + NodeID dst = getValueNode(retVal); + assert(dst && "dst not exist?"); + addCopyEdge(src, dst); + break; + } + case ExtAPI::EFT_CXA_BEGIN_CATCH: + { + break; + } + //default: + case ExtAPI::EFT_EPOLL_CTL_3: { + // Store the arg + Value* eventArg = cs.getArgument(3); + pag->getEpollCTLEventObjs().push_back(eventArg); + // Get the first argument of struct epoll_event { uint32_t events; epoll_data_t data}; + // Check if we're dealing with a ptr in epoll_data_t + StructType* epollEventTy = SVFUtil::dyn_cast(eventArg->getType()->getPointerElementType()); + assert(epollEventTy && "Epoll event must be a struct"); + StructType* epollDataTy = SVFUtil::dyn_cast(epollEventTy->getElementType(1)); + break; + } + case ExtAPI::EFT_EPOLL_WAIT_2: { + Value* eventArg = cs.getArgument(1); + pag->getEpollWAITEventObjs().push_back(eventArg); + + StructType* epollEventTy = SVFUtil::dyn_cast(eventArg->getType()->getPointerElementType()); + assert(epollEventTy && "Epoll event must be a struct"); + StructType* epollDataTy = SVFUtil::dyn_cast(epollEventTy->getElementType(1)); + break; + } + case ExtAPI::EFT_OTHER: + { + if(SVFUtil::isa(inst->getType())) + { + std::string str; + raw_string_ostream rawstr(str); + rawstr << "function " << callee->getName() << " not in the external function summary list"; + writeWrnMsg(rawstr.str()); + //assert("unknown ext.func type"); + } + } + } + } + + /// create inter-procedural PAG edges for thread forks + if(isThreadForkCall(inst)) + { + if(const Function* forkedFun = getLLVMFunction(getForkedFun(inst)) ) + { + forkedFun = getDefFunForMultipleModule(forkedFun)->getLLVMFun(); + const Value* actualParm = getActualParmAtForkSite(inst); + /// pthread_create has 1 arg. + /// apr_thread_create has 2 arg. + assert((forkedFun->arg_size() <= 2) && "Size of formal parameter of start routine should be one"); + if(forkedFun->arg_size() <= 2 && forkedFun->arg_size() >= 1) + { + const Argument* formalParm = &(*forkedFun->arg_begin()); + /// Connect actual parameter to formal parameter of the start routine + if(SVFUtil::isa(actualParm->getType()) && SVFUtil::isa(formalParm->getType()) ) + { + CallBlockNode* icfgNode = pag->getICFG()->getCallBlockNode(inst); + addThreadForkEdge(pag->getValueNode(actualParm), pag->getValueNode(formalParm),icfgNode); + } + } + } + else + { + /// handle indirect calls at pthread create APIs e.g., pthread_create(&t1, nullptr, fp, ...); + ///const Value* fun = ThreadAPI::getThreadAPI()->getForkedFun(inst); + ///if(!SVFUtil::isa(fun)) + /// pag->addIndirectCallsites(cs,pag->getValueNode(fun)); + } + /// If forkedFun does not pass to spawnee as function type but as void pointer + /// remember to update inter-procedural callgraph/PAG/SVFG etc. when indirect call targets are resolved + /// We don't connect the callgraph here, further investigation is need to hanle mod-ref during SVFG construction. + } + + /// create inter-procedural PAG edges for hare_parallel_for calls + else if(isHareParForCall(inst)) + { + if(const Function* taskFunc = getLLVMFunction(getTaskFuncAtHareParForSite(inst)) ) + { + /// The task function of hare_parallel_for has 3 args. + assert((taskFunc->arg_size() == 3) && "Size of formal parameter of hare_parallel_for's task routine should be 3"); + const Value* actualParm = getTaskDataAtHareParForSite(inst); + const Argument* formalParm = &(*taskFunc->arg_begin()); + /// Connect actual parameter to formal parameter of the start routine + if(SVFUtil::isa(actualParm->getType()) && SVFUtil::isa(formalParm->getType()) ) + { + CallBlockNode* icfgNode = pag->getICFG()->getCallBlockNode(inst); + addThreadForkEdge(pag->getValueNode(actualParm), pag->getValueNode(formalParm),icfgNode); + } + } + else + { + /// handle indirect calls at hare_parallel_for (e.g., hare_parallel_for(..., fp, ...); + ///const Value* fun = ThreadAPI::getThreadAPI()->getForkedFun(inst); + ///if(!SVFUtil::isa(fun)) + /// pag->addIndirectCallsites(cs,pag->getValueNode(fun)); + } + } + + /// TODO: inter-procedural PAG edges for thread joins + } +} + +/*! + * Indirect call is resolved on-the-fly during pointer analysis + */ +void PAGBuilder::handleIndCall(CallSite cs) +{ + const CallBlockNode* cbn = pag->getICFG()->getCallBlockNode(cs.getInstruction()); + pag->addIndirectCallsites(cbn,pag->getValueNode(cs.getCalledValue())); +} + +/* + * TODO: more sanity checks might be needed here + */ +void PAGBuilder::sanityCheck() +{ + for (PAG::iterator nIter = pag->begin(); nIter != pag->end(); ++nIter) + { + (void) pag->getPAGNode(nIter->first); + //TODO:: + // (1) every source(root) node of a pag tree should be object node + // if a node has no incoming edge, but has outgoing edges + // then it has to be an object node. + // (2) make sure every variable should be initialized + // otherwise it causes the a null pointer, the aliasing relation may not be captured + // when loading a pointer value should make sure + // some value has been store into this pointer before + // q = load p, some value should stored into p first like store w p; + // (3) make sure PAGNode should not have a const expr value (pointer should have unique def) + // (4) look closely into addComplexConsForExt, make sure program locations(e.g.,inst bb) + // are set correctly for dummy gepval node + // (5) reduce unnecessary copy edge (const casts) and ensure correctness. + } +} + + +/*! + * Add a temp field value node according to base value and offset + * this node is after the initial node method, it is out of scope of symInfo table + */ +NodeID PAGBuilder::getGepValNode(const Value* val, const LocationSet& ls, const Type *baseType, u32_t fieldidx) +{ + NodeID base = pag->getBaseValNode(getValueNode(val)); + NodeID gepval = pag->getGepValNode(curVal, base, ls); + if (gepval==UINT_MAX) + { + assert(((int) UINT_MAX)==-1 && "maximum limit of unsigned int is not -1?"); + /* + * getGepValNode can only be called from two places: + * 1. PAGBuilder::addComplexConsForExt to handle external calls + * 2. PAGBuilder::getGlobalVarField to initialize global variable + * so curVal can only be + * 1. Instruction + * 2. GlobalVariable + */ + assert((SVFUtil::isa(curVal) || SVFUtil::isa(curVal)) && "curVal not an instruction or a globalvariable?"); + const std::vector &fieldinfo = SymbolTableInfo::SymbolInfo()->getFlattenFieldInfoVec(baseType); + const Type *type = fieldinfo[fieldidx].getFlattenElemTy(); + + // We assume every GepValNode and its GepEdge to the baseNode are unique across the whole program + // We preserve the current BB information to restore it after creating the gepNode + const Value* cval = getCurrentValue(); + const BasicBlock* cbb = getCurrentBB(); + setCurrentLocation(curVal, nullptr); + NodeID gepNode= pag->addGepValNode(curVal, val,ls, NodeIDAllocator::get()->allocateValueId(),type,fieldidx); + addGepEdge(base, gepNode, ls, true); + setCurrentLocation(cval, cbb); + return gepNode; + } + else + return gepval; +} + + +/* + * curVal <--------> PAGEdge + * Instruction Any Edge + * Argument CopyEdge (PAG::addFormalParamBlackHoleAddrEdge) + * ConstantExpr CopyEdge (Int2PtrConstantExpr CastConstantExpr PAGBuilder::processCE) + * GepEdge (GepConstantExpr PAGBuilder::processCE) + * ConstantPointerNull CopyEdge (3-->2 NullPtr-->BlkPtr PAG::addNullPtrNode) + * AddrEdge (0-->2 BlkObj-->BlkPtr PAG::addNullPtrNode) + * GlobalVariable AddrEdge (PAGBuilder::visitGlobal) + * GepEdge (PAGBuilder::getGlobalVarField) + * Function AddrEdge (PAGBuilder::visitGlobal) + * Constant StoreEdge (PAGBuilder::InitialGlobal) + */ +void PAGBuilder::setCurrentBBAndValueForPAGEdge(PAGEdge* edge) +{ + if (SVFModule::pagReadFromTXT()) + return; + + assert(curVal && "current Val is nullptr?"); + edge->setBB(curBB); + edge->setValue(curVal); + // backmap in valuToEdgeMap + pag->mapValueToEdge(curVal, edge); + ICFGNode* icfgNode = pag->getICFG()->getGlobalBlockNode(); + if (const Instruction *curInst = SVFUtil::dyn_cast(curVal)) + { + const Function* srcFun = edge->getSrcNode()->getFunction(); + const Function* dstFun = edge->getDstNode()->getFunction(); +/* + if(srcFun!=nullptr && !SVFUtil::isa(edge) && !SVFUtil::isa(edge->getSrcNode()->getValue())) { + assert(srcFun==curInst->getFunction() && "SrcNode of the PAGEdge not in the same function?"); + } + if(dstFun!=nullptr && !SVFUtil::isa(edge) && !SVFUtil::isa(edge->getDstNode()->getValue())) { + assert(dstFun==curInst->getFunction() && "DstNode of the PAGEdge not in the same function?"); + } + + /// We assume every GepValPN and its GepPE are unique across whole program + if (!(SVFUtil::isa(edge) && SVFUtil::isa(edge->getDstNode()))) + assert(curBB && "instruction does not have a basic block??"); +*/ + + icfgNode = pag->getICFG()->getBlockICFGNode(curInst); + } + else if (const Argument* arg = SVFUtil::dyn_cast(curVal)) + { + assert(curBB && (&curBB->getParent()->getEntryBlock() == curBB)); + const SVFFunction* fun = LLVMModuleSet::getLLVMModuleSet()->getSVFFunction(arg->getParent()); + icfgNode = pag->getICFG()->getFunEntryBlockNode(fun); + } + else if (SVFUtil::isa(curVal)) + { + if (!curBB) + pag->addGlobalPAGEdge(edge); + else + icfgNode = pag->getICFG()->getBlockICFGNode(&curBB->front()); + } + else if (SVFUtil::isa(curVal) || + SVFUtil::isa(curVal) || + SVFUtil::isa(curVal) || + SVFUtil::isa(curVal)) + { + pag->addGlobalPAGEdge(edge); + } + else + { + assert(false && "what else value can we have?"); + } + + pag->addToInstPAGEdgeList(icfgNode,edge); + icfgNode->addPAGEdge(edge); +} + + diff --git a/svf/lib/SVF-FE/SymbolTableInfo.cpp b/svf/lib/SVF-FE/SymbolTableInfo.cpp new file mode 100644 index 000000000..3b1ec7da0 --- /dev/null +++ b/svf/lib/SVF-FE/SymbolTableInfo.cpp @@ -0,0 +1,1140 @@ +//===- SymbolTableInfo.cpp -- Symbol information from IR------------------------// +// +// SVF: Static Value-Flow Analysis +// +// Copyright (C) <2013-> +// + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// +//===----------------------------------------------------------------------===// + + +/* + * SymbolTableInfo.cpp + * + * Created on: Nov 11, 2013 + * Author: Yulei Sui + */ + +#include + +#include "SVF-FE/SymbolTableInfo.h" +#include "MemoryModel/MemModel.h" +#include "Util/NodeIDAllocator.h" +#include "Util/Options.h" +#include "Util/SVFModule.h" +#include "Util/SVFUtil.h" +#include "SVF-FE/LLVMUtil.h" +#include "SVF-FE/CPPUtil.h" +#include "SVF-FE/GEPTypeBridgeIterator.h" // include bridge_gep_iterator + +using namespace std; +using namespace SVF; +using namespace SVFUtil; + +DataLayout* SymbolTableInfo::dl = nullptr; +SymbolTableInfo* SymbolTableInfo::symInfo = nullptr; + +/* + * Initial the memory object here + */ +void MemObj::init(const Value *val) +{ + const PointerType *refTy = nullptr; + + const Instruction *I = SVFUtil::dyn_cast(val); + + // We consider two types of objects: + // (1) A heap/static object from a callsite + if (I && isNonInstricCallSite(I)) + refTy = getRefTypeOfHeapAllocOrStatic(I); + // (2) Other objects (e.g., alloca, global, etc.) + else + refTy = SVFUtil::dyn_cast(val->getType()); + + if (refTy) + { + Type *objTy = refTy->getElementType(); + if(Options::LocMemModel) + typeInfo = new LocObjTypeInfo(val, objTy, Options::MaxFieldLimit); + else + typeInfo = new ObjTypeInfo(val, objTy, Options::MaxFieldLimit); + typeInfo->init(val); + } + else + { + writeWrnMsg("try to create an object with a non-pointer type."); + writeWrnMsg(val->getName().str()); + writeWrnMsg("(" + getSourceLoc(val) + ")"); + assert(false && "Memory object must be held by a pointer-typed ref value."); + } +} + +/*! + * Get the symbol table instance + */ +SymbolTableInfo* SymbolTableInfo::SymbolInfo() +{ + if (symInfo == nullptr) + { + if(Options::LocMemModel) + symInfo = new LocSymTableInfo(); + else + symInfo = new SymbolTableInfo(); + symInfo->setModelConstants(Options::ModelConsts); + } + return symInfo; +} + +/*! + * Collect a LLVM type info + */ +void SymbolTableInfo::collectTypeInfo(const Type* ty) +{ + assert(typeToFieldInfo.find(ty) == typeToFieldInfo.end() && "this type has been collected before"); + + if (const ArrayType* aty = SVFUtil::dyn_cast(ty)) + collectArrayInfo(aty); + else if (const StructType* sty = SVFUtil::dyn_cast(ty)) + collectStructInfo(sty); + else + collectSimpleTypeInfo(ty); +} + + +/*! + * Fill in StInfo for an array type. + */ +void SymbolTableInfo::collectArrayInfo(const ArrayType* ty) +{ + StInfo* stinfo = new StInfo(); + typeToFieldInfo[ty] = stinfo; + + u64_t out_num = ty->getNumElements(); + const llvm::Type* elemTy = ty->getElementType(); + u32_t out_stride = getTypeSizeInBytes(elemTy); + while (const ArrayType* aty = SVFUtil::dyn_cast(elemTy)) + { + out_num *= aty->getNumElements(); + elemTy = aty->getElementType(); + out_stride = getTypeSizeInBytes(elemTy); + } + + /// Array itself only has one field which is the inner most element + stinfo->addFldWithType(0, 0, elemTy); + + /// Array's flatten field infor is the same as its element's + /// flatten infor. + StInfo* elemStInfo = getStructInfo(elemTy); + u32_t nfE = elemStInfo->getFlattenFieldInfoVec().size(); + for (u32_t j = 0; j < nfE; j++) + { + u32_t idx = elemStInfo->getFlattenFieldInfoVec()[j].getFlattenFldIdx(); + u32_t off = elemStInfo->getFlattenFieldInfoVec()[j].getFlattenByteOffset(); + const Type* fieldTy = elemStInfo->getFlattenFieldInfoVec()[j].getFlattenElemTy(); + FieldInfo::ElemNumStridePairVec pair = elemStInfo->getFlattenFieldInfoVec()[j].getElemNumStridePairVect(); + /// append the additional number + pair.push_back(std::make_pair(out_num, out_stride)); + FieldInfo field(idx, off, fieldTy, pair); + stinfo->getFlattenFieldInfoVec().push_back(field); + } +} + + +/*! + * Fill in struct_info for T. + * Given a Struct type, we recursively extend and record its fields and types. + */ +void SymbolTableInfo::collectStructInfo(const StructType *sty) +{ + /// The struct info should not be processed before + StInfo* stinfo = new StInfo(); + typeToFieldInfo[sty] = stinfo; + + // Number of fields after flattening the struct + u32_t nf = 0; + // field of the current struct + u32_t field_idx = 0; + for (StructType::element_iterator it = sty->element_begin(), ie = + sty->element_end(); it != ie; ++it, ++field_idx) + { + const Type *et = *it; + // This offset is computed after alignment with the current struct + u64_t eOffsetInBytes = getTypeSizeInBytes(sty, field_idx); + //The offset is where this element will be placed in the exp. struct. + /// FIXME: As the layout size is uint_64, here we assume + /// offset with uint_32 (Size_t) is large enough and will not cause overflow + stinfo->addFldWithType(nf, static_cast(eOffsetInBytes), et); + + if (SVFUtil::isa(et) || SVFUtil::isa(et)) + { + StInfo * subStinfo = getStructInfo(et); + u32_t nfE = subStinfo->getFlattenFieldInfoVec().size(); + //Copy ST's info, whose element 0 is the size of ST itself. + for (u32_t j = 0; j < nfE; j++) + { + u32_t fldIdx = nf + subStinfo->getFlattenFieldInfoVec()[j].getFlattenFldIdx(); + u32_t off = eOffsetInBytes + subStinfo->getFlattenFieldInfoVec()[j].getFlattenByteOffset(); + const Type* elemTy = subStinfo->getFlattenFieldInfoVec()[j].getFlattenElemTy(); + FieldInfo::ElemNumStridePairVec pair = subStinfo->getFlattenFieldInfoVec()[j].getElemNumStridePairVect(); + pair.push_back(std::make_pair(1, 0)); + FieldInfo field(fldIdx, off,elemTy,pair); + stinfo->getFlattenFieldInfoVec().push_back(field); + } + nf += nfE; + } + else //simple type + { + FieldInfo::ElemNumStridePairVec pair; + pair.push_back(std::make_pair(1,0)); + FieldInfo field(nf, eOffsetInBytes, et,pair); + stinfo->getFlattenFieldInfoVec().push_back(field); + ++nf; + } + } + + //Record the size of the complete struct and update max_struct. + if (nf > maxStSize) + { + maxStruct = sty; + maxStSize = nf; + } +} + + +/*! + * Collect simple type (non-aggregate) info + */ +void SymbolTableInfo::collectSimpleTypeInfo(const Type* ty) +{ + StInfo* stinfo = new StInfo(); + typeToFieldInfo[ty] = stinfo; + + /// Only one field + stinfo->addFldWithType(0,0, ty); + + FieldInfo::ElemNumStridePairVec pair; + pair.push_back(std::make_pair(1,0)); + FieldInfo field(0, 0, ty, pair); + stinfo->getFlattenFieldInfoVec().push_back(field); +} + +/*! + * Compute gep offset + */ +bool SymbolTableInfo::computeGepOffset(const User *V, LocationSet& ls) +{ + assert(V); + + const llvm::GEPOperator *gepOp = SVFUtil::dyn_cast(V); + DataLayout * dataLayout = getDataLayout(LLVMModuleSet::getLLVMModuleSet()->getMainLLVMModule()); + llvm::APInt byteOffset(dataLayout->getIndexSizeInBits(gepOp->getPointerAddressSpace()),0,true); + if(gepOp && dataLayout && gepOp->accumulateConstantOffset(*dataLayout,byteOffset)) // Accumulates the constant offsets if possible. + { + Size_t bo = byteOffset.getSExtValue(); + ls.setByteOffset(bo + ls.getByteOffset()); // ls.getByteOffset() would likely be zero + } + + /** + * bridge_gep_iterator, or generic_bridge_gep_type_iterator + * is actually a fully specialized templated class, that extends the + * std::iterator template. + * + * It iterates over the operands of the GEPOperator. + * The bridge_gep_begin, bridge_gep_end interfaces just invoke the begin() + * and end() interfaces of this templated class. + * + * The ++ operator simply increments the operand iterator and sets the + * CurTy pointer to the current type. + * + * Then when you do a * it returns CurTy. + */ + for (bridge_gep_iterator gi = bridge_gep_begin(*V), ge = bridge_gep_end(*V); + gi != ge; ++gi) + { + + Type* giType = *gi; + // Handling array types, skipe array handling here + // We treat whole array as one, then we can distinguish different field of an array of struct + // e.g. s[1].f1 is differet from s[0].f2 + if(SVFUtil::isa(giType)) + continue; + + // <---If the pointer type is known and it's a pointer to a struct then it's like an array + // Because if you're doing a gep on a struct-to-pointer type, then + // you can't access internal fields of the struct---> + // Even if it is a pointer to an array of struct, you can still access + // the different elements of that array using this pointer. + // + // If you treat this as a constant gep, then you'll only point to the + // first element and not be able to capture any of the other pointer + // relationships. + /* + if (Options::InvariantVGEP) { + while (giType->isPointerTy()) { + giType = giType->getPointerElementType(); + } + if (SVFUtil::isa(giType)) { + continue; + } + } + */ + + //The int-value object of the current index operand + // (may not be constant for arrays). + ConstantInt *op = SVFUtil::dyn_cast(gi.getOperand()); + + /// given a gep edge p = q + i, + if(!op) + { + return false; + } + //The actual index + Size_t idx = op->getSExtValue(); + + + // Handling pointer types + // These GEP instructions are simply making address computations from the base pointer address + // e.g. idx = (char*) &p + 4, at this case gep only one offset index (idx) + // Case 1: This operation is likely accessing an array through pointer p. + // Case 2: It may also be used to access a field of a struct (which is not ANSI-compliant) + // Since this is a field-index based memory model, + // for case 1: we consider the whole array as one element, This can be improved by LocMemModel as it can distinguish different + // elements of the same array. + // for case 2: we treat idx the same as &p by ignoring const 4 (This handling is unsound since the program itself is not ANSI-compliant). + if (SVFUtil::isa(*gi)) + { + //off += idx; + } + + + // Handling struct here + if (const StructType *ST = SVFUtil::dyn_cast(*gi) ) + { + assert(op && "non-const struct index in GEP"); + const vector &so = SymbolTableInfo::SymbolInfo()->getFattenFieldIdxVec(ST); + if ((unsigned)idx >= so.size()) + { + outs() << "!! Struct index out of bounds" << idx << "\n"; + assert(0); + } + //add the translated offset + ls.setFldIdx(ls.getOffset() + so[idx]); + } + } + return true; +} + + +/*! + * Replace fields with flatten fields of T if the number of its fields is larger than msz. + */ +u32_t SymbolTableInfo::getFields(std::vector& fields, const Type* T, u32_t msz) +{ + if (!SVFUtil::isa(T)) + return 0; + + T = T->getContainedType(0); + const std::vector& stVec = SymbolTableInfo::SymbolInfo()->getFlattenFieldInfoVec(T); + u32_t sz = stVec.size(); + if (msz < sz) + { + /// Replace fields with T's flatten fields. + fields.clear(); + for(std::vector::const_iterator it = stVec.begin(), eit = stVec.end(); it!=eit; ++it) + fields.push_back(LocationSet(*it)); + } + + return sz; +} + + +/*! + * Find the base type and the max possible offset for an object pointed to by (V). + */ +const Type *SymbolTableInfo::getBaseTypeAndFlattenedFields(const Value *V, std::vector &fields) +{ + assert(V); + fields.push_back(LocationSet(0)); + + const Type *T = V->getType(); + // Use the biggest struct type out of all operands. + if (const User *U = SVFUtil::dyn_cast(V)) + { + u32_t msz = 1; //the max size seen so far + // In case of BitCast, try the target type itself + if (SVFUtil::isa(V)) + { + u32_t sz = getFields(fields, T, msz); + if (msz < sz) + { + msz = sz; + } + } + // Try the types of all operands + for (User::const_op_iterator it = U->op_begin(), ie = U->op_end(); + it != ie; ++it) + { + const Type *operandtype = it->get()->getType(); + + u32_t sz = getFields(fields, operandtype, msz); + if (msz < sz) + { + msz = sz; + T = operandtype; + } + } + } + // If V is a CE, the actual pointer type is its operand. + else if (const ConstantExpr *E = SVFUtil::dyn_cast(V)) + { + T = E->getOperand(0)->getType(); + getFields(fields, T, 0); + } + // Handle Argument case + else if (SVFUtil::isa(V)) + { + getFields(fields, T, 0); + } + + while (const PointerType *ptype = SVFUtil::dyn_cast(T)) + T = ptype->getElementType(); + return T; +} + +/*! + * Get modulus offset given the type information + */ +LocationSet SymbolTableInfo::getModulusOffset(const MemObj* obj, const LocationSet& ls) +{ + + /// if the offset is negative, it's possible that we're looking for an obj node out of range + /// of current struct. Make the offset positive so we can still get a node within current + /// struct to represent this obj. + + Size_t offset = ls.getOffset(); + if(offset < 0) + { + writeWrnMsg("try to create a gep node with negative offset."); + llvm::errs() << "Trying to create a gep node with negative offset."; + offset = abs(offset); + } + u32_t maxOffset = obj->getMaxFieldOffsetLimit(); + if (maxOffset != 0) + offset = offset % maxOffset; + else + offset = 0; + + return LocationSet(offset); +} + + +/*! + * This method identify which is value sym and which is object sym + */ +void SymbolTableInfo::buildMemModel(SVFModule* svfModule) +{ + SVFUtil::increaseStackSize(); + + mod = svfModule; + + StInfo::setMaxFieldLimit(Options::MaxFieldLimit); + + // Object #0 is black hole the object that may point to any object + assert(totalSymNum == BlackHole && "Something changed!"); + symTyMap.insert(std::make_pair(totalSymNum++, BlackHole)); + createBlkOrConstantObj(BlackHole); + + // Object #1 always represents the constant + assert(totalSymNum == ConstantObj && "Something changed!"); + symTyMap.insert(std::make_pair(totalSymNum++, ConstantObj)); + createBlkOrConstantObj(ConstantObj); + + // Pointer #2 always represents the pointer points-to black hole. + assert(totalSymNum == BlkPtr && "Something changed!"); + symTyMap.insert(std::make_pair(totalSymNum++, BlkPtr)); + + // Pointer #3 always represents the null pointer. + assert(totalSymNum == NullPtr && "Something changed!"); + symTyMap.insert(std::make_pair(totalSymNum, NullPtr)); + + // Add symbols for all the globals . + for (SVFModule::global_iterator I = svfModule->global_begin(), E = + svfModule->global_end(); I != E; ++I) + { + collectSym(*I); + } + + // Add symbols for all the global aliases + for (SVFModule::alias_iterator I = svfModule->alias_begin(), E = + svfModule->alias_end(); I != E; I++) + { + collectSym(*I); + collectSym((*I)->getAliasee()); + } + + // Add symbols for all of the functions and the instructions in them. + for (SVFModule::llvm_iterator F = svfModule->llvmFunBegin(), E = svfModule->llvmFunEnd(); F != E; ++F) + { + Function *fun = *F; + collectSym(fun); + collectRet(fun); + if (fun->getFunctionType()->isVarArg()) + collectVararg(fun); + + // Add symbols for all formal parameters. + for (Function::arg_iterator I = fun->arg_begin(), E = fun->arg_end(); + I != E; ++I) + { + collectSym(&*I); + } + + // collect and create symbols inside the function body + for (inst_iterator II = inst_begin(*fun), E = inst_end(*fun); II != E; ++II) + { + const Instruction *inst = &*II; + collectSym(inst); + + // Special handling for CallInsts to psuedo heap calls + // We need this callsite to be treated as an object + // Update: We change the ExtAPI cache + /* + if (const CallInst* cInst = SVFUtil::dyn_cast(inst)) { + if (Function* calledFunc = cInst->getCalledFunction()) { + if (svfModule->isHeapCall(calledFunc)) { + collectObj(inst); + } + } + } + */ + + // initialization for some special instructions + //{@ + if (const StoreInst *st = SVFUtil::dyn_cast(inst)) + { + collectSym(st->getPointerOperand()); + collectSym(st->getValueOperand()); + } + else if (const LoadInst *ld = SVFUtil::dyn_cast(inst)) + { + collectSym(ld->getPointerOperand()); + } + else if (const PHINode *phi = SVFUtil::dyn_cast(inst)) + { + for (u32_t i = 0; i < phi->getNumIncomingValues(); ++i) + { + collectSym(phi->getIncomingValue(i)); + } + } + else if (const GetElementPtrInst *gep = SVFUtil::dyn_cast( + inst)) + { + collectSym(gep->getPointerOperand()); + } + else if (const SelectInst *sel = SVFUtil::dyn_cast(inst)) + { + collectSym(sel->getTrueValue()); + collectSym(sel->getFalseValue()); + } + else if (const BinaryOperator *binary = SVFUtil::dyn_cast(inst)) + { + for (u32_t i = 0; i < binary->getNumOperands(); i++) + collectSym(binary->getOperand(i)); + } + else if (const UnaryOperator *unary = SVFUtil::dyn_cast(inst)) + { + for (u32_t i = 0; i < unary->getNumOperands(); i++) + collectSym(unary->getOperand(i)); + } + else if (const CmpInst *cmp = SVFUtil::dyn_cast(inst)) + { + for (u32_t i = 0; i < cmp->getNumOperands(); i++) + collectSym(cmp->getOperand(i)); + } + else if (const CastInst *cast = SVFUtil::dyn_cast(inst)) + { + collectSym(cast->getOperand(0)); + } + else if (const ReturnInst *ret = SVFUtil::dyn_cast(inst)) + { + if(ret->getReturnValue()) + collectSym(ret->getReturnValue()); + } + else if (const BranchInst *br = SVFUtil::dyn_cast(inst)) + { + Value* opnd = br->isConditional() ? br->getCondition() : br->getOperand(0); + collectSym(opnd); + } + else if (const SwitchInst *sw = SVFUtil::dyn_cast(inst)) + { + collectSym(sw->getCondition()); + } + else if (isNonInstricCallSite(inst)) + { + + CallSite cs = SVFUtil::getLLVMCallSite(inst); + callSiteSet.insert(cs); + for (CallSite::arg_iterator it = cs.arg_begin(); + it != cs.arg_end(); ++it) + { + collectSym(*it); + } + // Calls to inline asm need to be added as well because the callee isn't + // referenced anywhere else. + const Value *Callee = cs.getCalledValue(); + collectSym(Callee); + + //TODO handle inlineAsm + ///if (SVFUtil::isa(Callee)) + + } + //@} + } + } + + NodeIDAllocator::get()->endSymbolAllocation(); + if (Options::SymTabPrint) { + SymbolTableInfo::SymbolInfo()->dump(); + } +} + +/*! + * Destroy the memory for this symbol table after use + */ +void SymbolTableInfo::destroy() +{ + + for (IDToMemMapTy::iterator iter = objMap.begin(); iter != objMap.end(); + ++iter) + { + if (iter->second) + delete iter->second; + } + for (TypeToFieldInfoMap::iterator iter = typeToFieldInfo.begin(); + iter != typeToFieldInfo.end(); ++iter) + { + if (iter->second) + delete iter->second; + } +} + +/*! + * Collect symbols, including value and object syms + */ +void SymbolTableInfo::collectSym(const Value *val) +{ + + //TODO: filter the non-pointer type // if (!SVFUtil::isa(val->getType())) return; + + DBOUT(DMemModel, outs() << "collect sym from ##" << *val << " \n"); + + // special sym here + if (isNullPtrSym(val) || isBlackholeSym(val)) + return; + + //TODO handle constant expression value here?? + handleCE(val); + + // create a value sym + collectVal(val); + + // create an object If it is a heap, stack, global, function. + if (isObject(val)) + { + collectObj(val); + } +} + +/*! + * Get value sym, if not available create a new one + */ +void SymbolTableInfo::collectVal(const Value *val) +{ + ValueToIDMapTy::iterator iter = valSymMap.find(val); + if (iter == valSymMap.end()) + { + // create val sym and sym type + SymID id = NodeIDAllocator::get()->allocateValueId(); + valSymMap.insert(std::make_pair(val, id)); + symTyMap.insert(std::make_pair(id, ValSym)); + DBOUT(DMemModel, + outs() << "create a new value sym " << id << "\n"); + /// handle global constant expression here + if (const GlobalVariable* globalVar = SVFUtil::dyn_cast(val)) + handleGlobalCE(globalVar); + } + + if (isConstantObjSym(val)) + collectObj(val); +} + +/*! + * Get memory object sym, if not available create a new one + */ +void SymbolTableInfo::collectObj(const Value *val) +{ + val = getGlobalRep(val); + ValueToIDMapTy::iterator iter = objSymMap.find(val); + if (iter == objSymMap.end()) + { + // if the object pointed by the pointer is a constant data (e.g., i32 0) or a global constant object (e.g. string) + // then we treat them as one ConstantObj + if(isConstantData(val) || (isConstantObjSym(val) && !getModelConstants())) + { + objSymMap.insert(std::make_pair(val, constantSymID())); + } + // otherwise, we will create an object for each abstract memory location + else + { + // create obj sym and sym type + SymID id = NodeIDAllocator::get()->allocateObjectId(); + objSymMap.insert(std::make_pair(val, id)); + symTyMap.insert(std::make_pair(id, ObjSym)); + DBOUT(DMemModel, + outs() << "create a new obj sym " << id << "\n"); + + // create a memory object + MemObj* mem = new MemObj(val, id); + assert(objMap.find(id) == objMap.end()); + objMap[id] = mem; + } + } +} + +/*! + * Create unique return sym, if not available create a new one + */ +void SymbolTableInfo::collectRet(const Function *val) +{ + FunToIDMapTy::iterator iter = returnSymMap.find(val); + if (iter == returnSymMap.end()) + { + SymID id = NodeIDAllocator::get()->allocateValueId(); + returnSymMap.insert(std::make_pair(val, id)); + symTyMap.insert(std::make_pair(id, RetSym)); + DBOUT(DMemModel, + outs() << "create a return sym " << id << "\n"); + } +} + +/*! + * Create vararg sym, if not available create a new one + */ +void SymbolTableInfo::collectVararg(const Function *val) +{ + FunToIDMapTy::iterator iter = varargSymMap.find(val); + if (iter == varargSymMap.end()) + { + SymID id = NodeIDAllocator::get()->allocateValueId(); + varargSymMap.insert(std::make_pair(val, id)); + symTyMap.insert(std::make_pair(id, VarargSym)); + DBOUT(DMemModel, + outs() << "create a vararg sym " << id << "\n"); + } +} + +/*! + * Check whether this value is null pointer + */ +bool SymbolTableInfo::isNullPtrSym(const Value *val) +{ + if (const Constant* v = SVFUtil::dyn_cast(val)) + { + return v->isNullValue() && v->getType()->isPointerTy(); + } + return false; +} + +/*! + * Check whether this value is a black hole + */ +bool SymbolTableInfo::isBlackholeSym(const Value *val) +{ + return (SVFUtil::isa(val)); +} + +/*! + * Check whether this value points-to a constant object + */ +bool SymbolTableInfo::isConstantObjSym(const Value *val) +{ + if (const GlobalVariable* v = SVFUtil::dyn_cast(val)) + { + if (cppUtil::isValVtbl(const_cast(v))) + return false; + else if (!v->hasInitializer()){ + if(v->isExternalLinkage(v->getLinkage())) + return false; + else + return true; + } + else + { + StInfo *stInfo = getStructInfo(v->getInitializer()->getType()); + const std::vector &fields = stInfo->getFlattenFieldInfoVec(); + for (std::vector::const_iterator it = fields.begin(), eit = fields.end(); it != eit; ++it) + { + const FieldInfo &field = *it; + const Type *elemTy = field.getFlattenElemTy(); + assert(!SVFUtil::isa(elemTy) && "Initializer of a global is a function?"); + if (SVFUtil::isa(elemTy)) + return false; + } + + return v->isConstant(); + } + } + return SVFUtil::isConstantData(val); +} + + +/*! + * Handle constant expression + */ +void SymbolTableInfo::handleCE(const Value *val) +{ + if (const Constant* ref = SVFUtil::dyn_cast(val)) + { + if (const ConstantExpr* ce = isGepConstantExpr(ref)) + { + DBOUT(DMemModelCE, + outs() << "handle constant expression " << *ref << "\n"); + collectVal(ce); + collectVal(ce->getOperand(0)); + // handle the recursive constant express case + // like (gep (bitcast (gep X 1)) 1); the inner gep is ce->getOperand(0) + handleCE(ce->getOperand(0)); + } + else if (const ConstantExpr* ce = isCastConstantExpr(ref)) + { + DBOUT(DMemModelCE, + outs() << "handle constant expression " << *ref << "\n"); + collectVal(ce); + collectVal(ce->getOperand(0)); + // handle the recursive constant express case + // like (gep (bitcast (gep X 1)) 1); the inner gep is ce->getOperand(0) + handleCE(ce->getOperand(0)); + } + else if (const ConstantExpr* ce = isSelectConstantExpr(ref)) + { + DBOUT(DMemModelCE, + outs() << "handle constant expression " << *ref << "\n"); + collectVal(ce); + collectVal(ce->getOperand(0)); + collectVal(ce->getOperand(1)); + collectVal(ce->getOperand(2)); + // handle the recursive constant express case + // like (gep (bitcast (gep X 1)) 1); the inner gep is ce->getOperand(0) + handleCE(ce->getOperand(0)); + handleCE(ce->getOperand(1)); + handleCE(ce->getOperand(2)); + } + // if we meet a int2ptr, then it points-to black hole + else if (const ConstantExpr *int2Ptrce = isInt2PtrConstantExpr(ref)) { + collectVal(int2Ptrce); + } else if (const ConstantExpr *ptr2Intce = isPtr2IntConstantExpr(ref)) { + collectVal(ptr2Intce); + const Constant *opnd = ptr2Intce->getOperand(0); + handleCE(opnd); + } else if (isTruncConstantExpr(ref) || isCmpConstantExpr(ref)) { + collectVal(ref); + } else if (isBinaryConstantExpr(ref)) { + collectVal(ref); + } else if (isUnaryConstantExpr(ref)) { + // we don't handle unary constant expression like fneg(x) now + collectVal(ref); + } else if (SVFUtil::isa(ref)) { + // we don't handle constant agrgregate like constant vectors + collectVal(ref); + } else { + if (SVFUtil::isa(val)) + assert(false && "we don't handle all other constant expression for now!"); + collectVal(ref); + } + } +} + +/*! + * Handle global constant expression + */ +void SymbolTableInfo::handleGlobalCE(const GlobalVariable *G) +{ + assert(G); + + //The type this global points to + const Type *T = G->getType()->getContainedType(0); + bool is_array = 0; + //An array is considered a single variable of its type. + while (const ArrayType *AT = SVFUtil::dyn_cast(T)) + { + T = AT->getElementType(); + is_array = 1; + } + + if (SVFUtil::isa(T)) + { + //A struct may be used in constant GEP expr. + for (Value::const_user_iterator it = G->user_begin(), ie = G->user_end(); + it != ie; ++it) + { + handleCE(*it); + } + } + else + { + if (is_array) + { + for (Value::const_user_iterator it = G->user_begin(), ie = + G->user_end(); it != ie; ++it) + { + handleCE(*it); + } + } + } + + if (G->hasInitializer()) + { + handleGlobalInitializerCE(G->getInitializer(), 0); + } +} + +/*! + * Handle global variable initialization + */ +void SymbolTableInfo::handleGlobalInitializerCE(const Constant *C, + u32_t offset) +{ + + if (C->getType()->isSingleValueType()) + { + if (const ConstantExpr *E = SVFUtil::dyn_cast(C)) + { + handleCE(E); + } + else + { + collectVal(C); + } + } + else if (SVFUtil::isa(C)) + { + for (u32_t i = 0, e = C->getNumOperands(); i != e; i++) + { + handleGlobalInitializerCE(SVFUtil::cast(C->getOperand(i)), offset); + } + } + else if (SVFUtil::isa(C)) + { + const StructType *sty = SVFUtil::cast(C->getType()); + const std::vector& offsetvect = + SymbolTableInfo::SymbolInfo()->getFattenFieldIdxVec(sty); + for (u32_t i = 0, e = C->getNumOperands(); i != e; i++) + { + u32_t off = offsetvect[i]; + handleGlobalInitializerCE(SVFUtil::cast(C->getOperand(i)), + offset + off); + } + } +} + + +/* + * Print out the composite type information + */ +void SymbolTableInfo::printFlattenFields(const Type* type) +{ + + if(const ArrayType *at = SVFUtil::dyn_cast (type)) + { + outs() <<" {Type: "; + at->print(outs()); + outs() << "}\n"; + outs() << "\tarray type "; + outs() << "\t [element size = " << getTypeSizeInBytes(at->getElementType()) << "]\n"; + outs() << "\n"; + } + + else if(const StructType *st = SVFUtil::dyn_cast (type)) + { + outs() <<" {Type: "; + st->print(outs()); + outs() << "}\n"; + std::vector& finfo = getStructInfo(st)->getFlattenFieldInfoVec(); + int field_idx = 0; + for(std::vector::iterator it = finfo.begin(), eit = finfo.end(); + it!=eit; ++it, field_idx++) + { + outs() << " \tField_idx = " << (*it).getFlattenFldIdx() << " [offset: " << (*it).getFlattenByteOffset(); + outs() << ", field type: "; + (*it).getFlattenElemTy()->print(outs()); + outs() << ", field size: " << getTypeSizeInBytes((*it).getFlattenElemTy()); + outs() << ", field stride pair: "; + for(FieldInfo::ElemNumStridePairVec::const_iterator pit = (*it).elemStridePairBegin(), + peit = (*it).elemStridePairEnd(); pit!=peit; ++pit) + { + outs() << "[ " << pit->first << ", " << pit->second << " ] "; + } + outs() << "\n"; + } + outs() << "\n"; + } + + else if (const PointerType* pt= SVFUtil::dyn_cast (type)) + { + u32_t sizeInBits = getTypeSizeInBytes(type); + u32_t eSize = getTypeSizeInBytes(pt->getElementType()); + outs() << " {Type: "; + pt->print(outs()); + outs() << "}\n"; + outs() <<"\t [pointer size = " << sizeInBits << "]"; + outs() <<"\t [target size = " << eSize << "]\n"; + outs() << "\n"; + } + + else if ( const FunctionType* fu= SVFUtil::dyn_cast (type)) + { + outs() << " {Type: "; + fu->getReturnType()->print(outs()); + outs() << "(Function)}\n\n"; + } + + else + { + assert(type->isSingleValueType() && "not a single value type, then what else!!"); + /// All rest types are scalar type? + u32_t eSize = getTypeSizeInBytes(type); + outs() <<" {Type: "; + type->print(outs()); + outs() << "}\n"; + outs() <<"\t [object size = " << eSize << "]\n"; + outs() << "\n"; + } +} + +std::string SymbolTableInfo::toString(SYMTYPE symtype) +{ + switch (symtype) { + case SYMTYPE::BlackHole: { + return "BlackHole"; + } + case SYMTYPE::ConstantObj: { + return "ConstantObj"; + } + case SYMTYPE::BlkPtr: { + return "BlkPtr"; + } + case SYMTYPE::NullPtr: { + return "NullPtr"; + } + case SYMTYPE::ValSym: { + return "ValSym"; + } + case SYMTYPE::ObjSym: { + return "ObjSym"; + } + case SYMTYPE::RetSym: { + return "RetSym"; + } + case SYMTYPE::VarargSym: { + return "VarargSym"; + } + default: { + return "Invalid SYMTYPE"; + } + } +} + +void SymbolTableInfo::dump() +{ + OrderedMap idmap; + SymID maxid = 0; + for (ValueToIDMapTy::iterator iter = valSymMap.begin(); iter != valSymMap.end(); + ++iter) + { + const SymID i = iter->second; + maxid = max(i, maxid); + Value* val = (Value*) iter->first; + idmap[i] = val; + } + for (ValueToIDMapTy::iterator iter = objSymMap.begin(); iter != objSymMap.end(); + ++iter) + { + const SymID i = iter->second; + maxid = max(i, maxid); + Value* val = (Value*) iter->first; + idmap[i] = val; + } + for (FunToIDMapTy::iterator iter = returnSymMap.begin(); iter != returnSymMap.end(); + ++iter) + { + const SymID i = iter->second; + maxid = max(i, maxid); + Value* val = (Value*) iter->first; + idmap[i] = val; + } + for (FunToIDMapTy::iterator iter = varargSymMap.begin(); iter != varargSymMap.end(); + ++iter) + { + const SymID i = iter->second; + maxid = max(i, maxid); + Value* val = (Value*) iter->first; + idmap[i] = val; + } + outs() << "{SymbolTableInfo \n"; + for (SymID symid = 0; symid <= maxid; ++symid) { + SYMTYPE symtype = this->symTyMap.at(symid); + string typestring = toString(symtype); + outs() << " " << typestring << symid; + if (symtype < SYMTYPE::ValSym) { + outs() << "\n"; + } else { + auto I = idmap.find(symid); + if (I == idmap.end()) { + outs() << "No value\n"; + break; + } + const Value* val = I->second; + outs() << " -> " << value2String(val) << "\n"; + } + } + outs() << "}\n"; +} + +/* + * Get the type size given a target data layout + */ +u32_t SymbolTableInfo::getTypeSizeInBytes(const Type* type) +{ + + // if the type has size then simply return it, otherwise just return 0 + if(type->isSized()) + return getDataLayout(LLVMModuleSet::getLLVMModuleSet()->getMainLLVMModule())->getTypeStoreSize(const_cast(type)); + else + return 0; +} + +u32_t SymbolTableInfo::getTypeSizeInBytes(const StructType *sty, u32_t field_idx) +{ + + const StructLayout *stTySL = getDataLayout(LLVMModuleSet::getLLVMModuleSet()->getMainLLVMModule())->getStructLayout( const_cast(sty) ); + /// if this struct type does not have any element, i.e., opaque + if(sty->isOpaque()) + return 0; + else + return stTySL->getElementOffset(field_idx); +} + + + diff --git a/svf/lib/SVFIR/SVFFileSystem.cpp b/svf/lib/SVFIR/SVFFileSystem.cpp deleted file mode 100644 index 4e8675be5..000000000 --- a/svf/lib/SVFIR/SVFFileSystem.cpp +++ /dev/null @@ -1,2579 +0,0 @@ -#include "SVFIR/SVFFileSystem.h" -#include "Graphs/CHG.h" -#include "SVFIR/SVFIR.h" -#include "Util/CommandLine.h" -#include -#include -#include -#include -#include - -static const Option humanReadableOption( - "human-readable", "Whether to output human-readable JSON", true); - -namespace SVF -{ - -SVFType* createSVFType(SVFType::GNodeK kind, bool isSingleValTy) -{ - switch (kind) - { - default: - ABORT_MSG(kind << " is an impossible SVFTyKind in create()"); - case SVFType::SVFTy: - ABORT_MSG("Creation of RAW SVFType isn't allowed"); - case SVFType::SVFPointerTy: - ABORT_IFNOT(isSingleValTy, "Pointer type must be single-valued"); - return new SVFPointerType(); - case SVFType::SVFIntegerTy: - ABORT_IFNOT(isSingleValTy, "Integer type must be single-valued"); - return new SVFIntegerType(); - case SVFType::SVFFunctionTy: - ABORT_IFNOT(!isSingleValTy, "Function type must be multi-valued"); - return new SVFFunctionType(nullptr); - case SVFType::SVFStructTy: - ABORT_IFNOT(!isSingleValTy, "Struct type must be multi-valued"); - return new SVFStructType(); - case SVFType::SVFArrayTy: - ABORT_IFNOT(!isSingleValTy, "Array type must be multi-valued"); - return new SVFArrayType(); - case SVFType::SVFOtherTy: - return new SVFOtherType(isSingleValTy); - } -} - -static SVFValue* createSVFValue(SVFValue::GNodeK kind, const SVFType* type, - std::string&& name) -{ - auto creator = [=]() -> SVFValue* - { - switch (kind) - { - default: - ABORT_MSG(kind << " is an impossible SVFValueKind in create()"); - case SVFValue::SVFVal: - ABORT_MSG("Creation of RAW SVFValue isn't allowed"); - case SVFValue::SVFFunc: - return new SVFFunction(type, {}, {}, {}, {}, {}, {}); - case SVFValue::SVFBB: - return new SVFBasicBlock(type, {}); - case SVFValue::SVFInst: - return new SVFInstruction(type, {}, {}, {}); - case SVFValue::SVFCall: - return new SVFCallInst(type, {}, {}, {}); - case SVFValue::SVFVCall: - return new SVFVirtualCallInst(type, {}, {}, {}); - case SVFValue::SVFGlob: - return new SVFGlobalValue(type); - case SVFValue::SVFArg: - return new SVFArgument(type, {}, {}, {}); - case SVFValue::SVFConst: - return new SVFConstant(type); - case SVFValue::SVFConstData: - return new SVFConstantData(type); - case SVFValue::SVFConstInt: - return new SVFConstantInt(type, {}, {}); - case SVFValue::SVFConstFP: - return new SVFConstantFP(type, {}); - case SVFValue::SVFNullPtr: - return new SVFConstantNullPtr(type); - case SVFValue::SVFBlackHole: - return new SVFBlackHoleValue(type); - case SVFValue::SVFMetaAsValue: - return new SVFMetadataAsValue(type); - case SVFValue::SVFOther: - return new SVFOtherValue(type); - } - }; - auto val = creator(); - val->setName(std::move(name)); - return val; -} - -template -static inline void readSmallNumber(const cJSON* obj, SmallNumberType& val) -{ - val = static_cast(jsonGetNumber(obj)); -} - -template -static inline void readBigNumber(const cJSON* obj, BigNumberType& val, CStrToVal conv) -{ - ABORT_IFNOT(jsonIsString(obj), - "Expect (number) string JSON for " << JSON_KEY(obj)); - val = conv(obj->valuestring); -} - -cJSON* SVFIRWriter::toJson(bool flag) -{ - return jsonCreateBool(flag); -} - -cJSON* SVFIRWriter::toJson(unsigned number) -{ - // OK, double precision enough - return jsonCreateNumber(number); -} - -cJSON* SVFIRWriter::toJson(int number) -{ - // OK, double precision enough - return jsonCreateNumber(number); -} - -cJSON* SVFIRWriter::toJson(const std::string& str) -{ - return jsonCreateString(str.c_str()); -} - -cJSON* SVFIRWriter::toJson(float number) -{ - return jsonCreateNumber(number); -} - -cJSON* SVFIRWriter::toJson(unsigned long number) -{ - // unsigned long is subset of unsigned long long - return toJson(static_cast(number)); -} - -cJSON* SVFIRWriter::toJson(long long number) -{ - return toJson(static_cast(number)); -} - -cJSON* SVFIRWriter::toJson(unsigned long long number) -{ - return jsonCreateString(numToStr(number)); -} - -cJSON* SVFIRWriter::virtToJson(const SVFType* type) -{ - auto kind = type->getKind(); - - switch (kind) - { - default: - assert(false && "Impossible SVFType kind"); - -#define CASE(Kind) \ - case SVFType::Kind: \ - return contentToJson(static_cast(type)) - - CASE(SVFTy); - CASE(SVFPointerTy); - CASE(SVFIntegerTy); - CASE(SVFFunctionTy); - CASE(SVFStructTy); - CASE(SVFArrayTy); - CASE(SVFOtherTy); -#undef CASE - } -} - -cJSON* SVFIRWriter::virtToJson(const SVFValue* value) -{ - auto kind = value->getKind(); - - switch (kind) - { - default: - assert(false && "Impossible SVFValue kind"); - -#define CASE(ValueKind, type) \ - case SVFValue::ValueKind: \ - return contentToJson(static_cast(value)) - - CASE(SVFVal, SVFValue); - CASE(SVFFunc, SVFFunction); - CASE(SVFBB, SVFBasicBlock); - CASE(SVFInst, SVFInstruction); - CASE(SVFCall, SVFCallInst); - CASE(SVFVCall, SVFVirtualCallInst); - CASE(SVFGlob, SVFGlobalValue); - CASE(SVFArg, SVFArgument); - CASE(SVFConst, SVFConstant); - CASE(SVFConstData, SVFConstantData); - CASE(SVFConstInt, SVFConstantInt); - CASE(SVFConstFP, SVFConstantFP); - CASE(SVFNullPtr, SVFConstantNullPtr); - CASE(SVFBlackHole, SVFBlackHoleValue); - CASE(SVFMetaAsValue, SVFMetadataAsValue); - CASE(SVFOther, SVFOtherValue); -#undef CASE - } -} - -cJSON* SVFIRWriter::virtToJson(const SVFVar* var) -{ - switch (var->getNodeKind()) - { - default: - assert(false && "Unknown SVFVar kind"); - -#define CASE(VarKind, VarType) \ - case SVFVar::VarKind: \ - return contentToJson(static_cast(var)) - - CASE(ValNode, ValVar); - CASE(ObjNode, ObjVar); - CASE(RetNode, RetPN); - CASE(VarargNode, VarArgPN); - CASE(GepValNode, GepValVar); - CASE(GepObjNode, GepObjVar); - CASE(FIObjNode, FIObjVar); - CASE(DummyValNode, DummyValVar); - CASE(DummyObjNode, DummyObjVar); -#undef CASE - } -} - -cJSON* SVFIRWriter::virtToJson(const SVFStmt* stmt) -{ - switch (stmt->getEdgeKind()) - { - default: - assert(false && "Unknown SVFStmt kind"); - -#define CASE(EdgeKind, EdgeType) \ - case SVFStmt::EdgeKind: \ - return contentToJson(static_cast(stmt)) - - CASE(Addr, AddrStmt); - CASE(Copy, CopyStmt); - CASE(Store, StoreStmt); - CASE(Load, LoadStmt); - CASE(Call, CallPE); - CASE(Ret, RetPE); - CASE(Gep, GepStmt); - CASE(Phi, PhiStmt); - CASE(Select, SelectStmt); - CASE(Cmp, CmpStmt); - CASE(BinaryOp, BinaryOPStmt); - CASE(UnaryOp, UnaryOPStmt); - CASE(Branch, BranchStmt); - CASE(ThreadFork, TDForkPE); - CASE(ThreadJoin, TDJoinPE); -#undef CASE - } -} - -cJSON* SVFIRWriter::virtToJson(const ICFGNode* node) -{ - switch (node->getNodeKind()) - { - default: - assert(false && "Unknown ICFGNode kind"); - -#define CASE(NodeKind, NodeType) \ - case ICFGNode::NodeKind: \ - return contentToJson(static_cast(node)) - - CASE(IntraBlock, IntraICFGNode); - CASE(FunEntryBlock, FunEntryICFGNode); - CASE(FunExitBlock, FunExitICFGNode); - CASE(FunCallBlock, CallICFGNode); - CASE(FunRetBlock, RetICFGNode); - CASE(GlobalBlock, GlobalICFGNode); -#undef CASE - } -} - -cJSON* SVFIRWriter::virtToJson(const ICFGEdge* edge) -{ - switch (edge->getEdgeKind()) - { - default: - assert(false && "Unknown ICFGEdge kind"); - case ICFGEdge::IntraCF: - return contentToJson(static_cast(edge)); - case ICFGEdge::CallCF: - return contentToJson(static_cast(edge)); - case ICFGEdge::RetCF: - return contentToJson(static_cast(edge)); - } -} - -cJSON* SVFIRWriter::virtToJson(const CHNode* node) -{ - return contentToJson(node); -} - -cJSON* SVFIRWriter::virtToJson(const CHEdge* edge) -{ - return contentToJson(edge); -} - -cJSON* SVFIRWriter::contentToJson(const SVFVar* var) -{ - cJSON* root = genericNodeToJson(var); - JSON_WRITE_FIELD(root, var, value); - JSON_WRITE_FIELD(root, var, InEdgeKindToSetMap); - JSON_WRITE_FIELD(root, var, OutEdgeKindToSetMap); - JSON_WRITE_FIELD(root, var, isPtr); - return root; -} - -cJSON* SVFIRWriter::contentToJson(const ValVar* var) -{ - return contentToJson(static_cast(var)); -} - -cJSON* SVFIRWriter::contentToJson(const ObjVar* var) -{ - cJSON* root = contentToJson(static_cast(var)); - JSON_WRITE_FIELD(root, var, mem); - return root; -} - -cJSON* SVFIRWriter::contentToJson(const GepValVar* var) -{ - cJSON* root = contentToJson(static_cast(var)); - JSON_WRITE_FIELD(root, var, ap); - JSON_WRITE_FIELD(root, var, gepValType); - return root; -} - -cJSON* SVFIRWriter::contentToJson(const GepObjVar* var) -{ - cJSON* root = contentToJson(static_cast(var)); - JSON_WRITE_FIELD(root, var, apOffset); - JSON_WRITE_FIELD(root, var, base); - return root; -} - -cJSON* SVFIRWriter::contentToJson(const FIObjVar* var) -{ - return contentToJson(static_cast(var)); -} - -cJSON* SVFIRWriter::contentToJson(const RetPN* var) -{ - return contentToJson(static_cast(var)); -} - -cJSON* SVFIRWriter::contentToJson(const VarArgPN* var) -{ - return contentToJson(static_cast(var)); -} - -cJSON* SVFIRWriter::contentToJson(const DummyValVar* var) -{ - return contentToJson(static_cast(var)); -} - -cJSON* SVFIRWriter::contentToJson(const DummyObjVar* var) -{ - return contentToJson(static_cast(var)); -} - -cJSON* SVFIRWriter::contentToJson(const ICFGNode* node) -{ - cJSON* root = genericNodeToJson(node); - JSON_WRITE_FIELD(root, node, fun); - JSON_WRITE_FIELD(root, node, bb); - // TODO: Ensure this? - assert(node->VFGNodes.empty() && "VFGNodes list not empty?"); - JSON_WRITE_FIELD(root, node, pagEdges); - return root; -} - -cJSON* SVFIRWriter::contentToJson(const GlobalICFGNode* node) -{ - return contentToJson(static_cast(node)); -} - -cJSON* SVFIRWriter::contentToJson(const IntraICFGNode* node) -{ - cJSON* root = contentToJson(static_cast(node)); - JSON_WRITE_FIELD(root, node, inst); - return root; -} - -cJSON* SVFIRWriter::contentToJson(const InterICFGNode* node) -{ - return contentToJson(static_cast(node)); -} - -cJSON* SVFIRWriter::contentToJson(const FunEntryICFGNode* node) -{ - cJSON* root = contentToJson(static_cast(node)); - JSON_WRITE_FIELD(root, node, FPNodes); - return root; -} - -cJSON* SVFIRWriter::contentToJson(const FunExitICFGNode* node) -{ - cJSON* root = contentToJson(static_cast(node)); - JSON_WRITE_FIELD(root, node, formalRet); - return root; -} - -cJSON* SVFIRWriter::contentToJson(const CallICFGNode* node) -{ - cJSON* root = contentToJson(static_cast(node)); - JSON_WRITE_FIELD(root, node, cs); - JSON_WRITE_FIELD(root, node, ret); - JSON_WRITE_FIELD(root, node, APNodes); - return root; -} - -cJSON* SVFIRWriter::contentToJson(const RetICFGNode* node) -{ - cJSON* root = contentToJson(static_cast(node)); - JSON_WRITE_FIELD(root, node, cs); - JSON_WRITE_FIELD(root, node, actualRet); - JSON_WRITE_FIELD(root, node, callBlockNode); - return root; -} - -cJSON* SVFIRWriter::contentToJson(const ICFGEdge* edge) -{ - return genericEdgeToJson(edge); -} - -cJSON* SVFIRWriter::contentToJson(const IntraCFGEdge* edge) -{ - cJSON* root = contentToJson(static_cast(edge)); - JSON_WRITE_FIELD(root, edge, conditionVar); - JSON_WRITE_FIELD(root, edge, branchCondVal); - return root; -} - -cJSON* SVFIRWriter::contentToJson(const CallCFGEdge* edge) -{ - cJSON* root = contentToJson(static_cast(edge)); - JSON_WRITE_FIELD(root, edge, cs); - JSON_WRITE_FIELD(root, edge, callPEs); - return root; -} - -cJSON* SVFIRWriter::contentToJson(const RetCFGEdge* edge) -{ - cJSON* root = contentToJson(static_cast(edge)); - JSON_WRITE_FIELD(root, edge, cs); - JSON_WRITE_FIELD(root, edge, retPE); - return root; -} - -cJSON* SVFIRWriter::contentToJson(const CHNode* node) -{ - cJSON* root = genericNodeToJson(node); - JSON_WRITE_FIELD(root, node, vtable); - JSON_WRITE_FIELD(root, node, className); - JSON_WRITE_FIELD(root, node, flags); - JSON_WRITE_FIELD(root, node, virtualFunctionVectors); - return root; -} - -cJSON* SVFIRWriter::contentToJson(const CHEdge* edge) -{ - cJSON* root = genericEdgeToJson(edge); - JSON_WRITE_FIELD(root, edge, edgeType); - return root; -} - -cJSON* SVFIRWriter::contentToJson(const SVFType* type) -{ - cJSON* root = jsonCreateObject(); - JSON_WRITE_FIELD(root, type, kind); - JSON_WRITE_FIELD(root, type, isSingleValTy); - JSON_WRITE_FIELD(root, type, typeinfo); - return root; -} - -cJSON* SVFIRWriter::contentToJson(const SVFPointerType* type) -{ - cJSON* root = contentToJson(static_cast(type)); - return root; -} - -cJSON* SVFIRWriter::contentToJson(const SVFIntegerType* type) -{ - cJSON* root = contentToJson(static_cast(type)); - JSON_WRITE_FIELD(root, type, signAndWidth); - return root; -} - -cJSON* SVFIRWriter::contentToJson(const SVFFunctionType* type) -{ - cJSON* root = contentToJson(static_cast(type)); - JSON_WRITE_FIELD(root, type, retTy); - return root; -} - -cJSON* SVFIRWriter::contentToJson(const SVFStructType* type) -{ - cJSON* root = contentToJson(static_cast(type)); - JSON_WRITE_FIELD(root, type, name); - return root; -} - -cJSON* SVFIRWriter::contentToJson(const SVFArrayType* type) -{ - cJSON* root = contentToJson(static_cast(type)); - JSON_WRITE_FIELD(root, type, numOfElement); - JSON_WRITE_FIELD(root, type, typeOfElement); - return root; -} - -cJSON* SVFIRWriter::contentToJson(const SVFOtherType* type) -{ - cJSON* root = contentToJson(static_cast(type)); - JSON_WRITE_FIELD(root, type, repr); - return root; -} - -cJSON* SVFIRWriter::contentToJson(const SVFValue* value) -{ - cJSON* root = jsonCreateObject(); - JSON_WRITE_FIELD(root, value, kind); - JSON_WRITE_FIELD(root, value, type); - JSON_WRITE_FIELD(root, value, name); - JSON_WRITE_FIELD(root, value, ptrInUncalledFun); - JSON_WRITE_FIELD(root, value, constDataOrAggData); - JSON_WRITE_FIELD(root, value, sourceLoc); - return root; -} - -cJSON* SVFIRWriter::contentToJson(const SVFFunction* value) -{ - cJSON* root = contentToJson(static_cast(value)); -#define F(f) JSON_WRITE_FIELD(root, value, f); - F(isDecl); - F(intrinsic); - F(addrTaken); - F(isUncalled); - F(isNotRet); - F(varArg); - F(funcType); - F(loopAndDom); - F(realDefFun); - F(allBBs); - F(allArgs); - F(annotations); -#undef F - return root; -} - -cJSON* SVFIRWriter::contentToJson(const SVFBasicBlock* value) -{ - cJSON* root = contentToJson(static_cast(value)); - JSON_WRITE_FIELD(root, value, allInsts); - JSON_WRITE_FIELD(root, value, succBBs); - JSON_WRITE_FIELD(root, value, predBBs); - JSON_WRITE_FIELD(root, value, fun); - return root; -} - -cJSON* SVFIRWriter::contentToJson(const SVFInstruction* value) -{ - cJSON* root = contentToJson(static_cast(value)); - JSON_WRITE_FIELD(root, value, bb); - JSON_WRITE_FIELD(root, value, terminator); - JSON_WRITE_FIELD(root, value, ret); - JSON_WRITE_FIELD(root, value, succInsts); - JSON_WRITE_FIELD(root, value, predInsts); - return root; -} - -cJSON* SVFIRWriter::contentToJson(const SVFCallInst* value) -{ - cJSON* root = contentToJson(static_cast(value)); - JSON_WRITE_FIELD(root, value, args); - JSON_WRITE_FIELD(root, value, varArg); - JSON_WRITE_FIELD(root, value, calledVal); - return root; -} - -cJSON* SVFIRWriter::contentToJson(const SVFVirtualCallInst* value) -{ - cJSON* root = contentToJson(static_cast(value)); - JSON_WRITE_FIELD(root, value, vCallVtblPtr); - JSON_WRITE_FIELD(root, value, virtualFunIdx); - JSON_WRITE_FIELD(root, value, funNameOfVcall); - return root; -} - -cJSON* SVFIRWriter::contentToJson(const SVFConstant* value) -{ - return contentToJson(static_cast(value)); -} - -cJSON* SVFIRWriter::contentToJson(const SVFGlobalValue* value) -{ - cJSON* root = contentToJson(static_cast(value)); - JSON_WRITE_FIELD(root, value, realDefGlobal); - return root; -} - -cJSON* SVFIRWriter::contentToJson(const SVFArgument* value) -{ - cJSON* root = contentToJson(static_cast(value)); - JSON_WRITE_FIELD(root, value, fun); - JSON_WRITE_FIELD(root, value, argNo); - JSON_WRITE_FIELD(root, value, uncalled); - return root; -} - -cJSON* SVFIRWriter::contentToJson(const SVFConstantData* value) -{ - return contentToJson(static_cast(value)); -} - -cJSON* SVFIRWriter::contentToJson(const SVFConstantInt* value) -{ - cJSON* root = contentToJson(static_cast(value)); - JSON_WRITE_FIELD(root, value, zval); - JSON_WRITE_FIELD(root, value, sval); - return root; -} - -cJSON* SVFIRWriter::contentToJson(const SVFConstantFP* value) -{ - cJSON* root = contentToJson(static_cast(value)); - JSON_WRITE_FIELD(root, value, dval); - return root; -} - -cJSON* SVFIRWriter::contentToJson(const SVFConstantNullPtr* value) -{ - return contentToJson(static_cast(value)); -} - -cJSON* SVFIRWriter::contentToJson(const SVFBlackHoleValue* value) -{ - return contentToJson(static_cast(value)); -} - -cJSON* SVFIRWriter::contentToJson(const SVFOtherValue* value) -{ - return contentToJson(static_cast(value)); -} - -cJSON* SVFIRWriter::contentToJson(const SVFMetadataAsValue* value) -{ - return contentToJson(static_cast(value)); -} - -cJSON* SVFIRWriter::contentToJson(const SVFStmt* edge) -{ - cJSON* root = genericEdgeToJson(edge); - JSON_WRITE_FIELD(root, edge, value); - JSON_WRITE_FIELD(root, edge, basicBlock); - JSON_WRITE_FIELD(root, edge, icfgNode); - JSON_WRITE_FIELD(root, edge, edgeId); - return root; -} - -cJSON* SVFIRWriter::contentToJson(const SVFLoop* loop) -{ - cJSON* root = jsonCreateObject(); -#define F(field) JSON_WRITE_FIELD(root, loop, field) - F(entryICFGEdges); - F(backICFGEdges); - F(inICFGEdges); - F(outICFGEdges); - F(icfgNodes); - F(loopBound); -#undef F - return root; -} - -cJSON* SVFIRWriter::contentToJson(const AssignStmt* edge) -{ - return contentToJson(static_cast(edge)); -} - -cJSON* SVFIRWriter::contentToJson(const AddrStmt* edge) -{ - return contentToJson(static_cast(edge)); -} - -cJSON* SVFIRWriter::contentToJson(const CopyStmt* edge) -{ - cJSON* root = contentToJson(static_cast(edge)); - JSON_WRITE_FIELD(root, edge, copyKind); - return root; -} - -cJSON* SVFIRWriter::contentToJson(const StoreStmt* edge) -{ - return contentToJson(static_cast(edge)); -} - -cJSON* SVFIRWriter::contentToJson(const LoadStmt* edge) -{ - return contentToJson(static_cast(edge)); -} - -cJSON* SVFIRWriter::contentToJson(const GepStmt* edge) -{ - cJSON* root = contentToJson(static_cast(edge)); - JSON_WRITE_FIELD(root, edge, ap); - JSON_WRITE_FIELD(root, edge, variantField); - return root; -} - -cJSON* SVFIRWriter::contentToJson(const CallPE* edge) -{ - cJSON* root = contentToJson(static_cast(edge)); - JSON_WRITE_FIELD(root, edge, call); - JSON_WRITE_FIELD(root, edge, entry); - return root; -} - -cJSON* SVFIRWriter::contentToJson(const RetPE* edge) -{ - cJSON* root = contentToJson(static_cast(edge)); - JSON_WRITE_FIELD(root, edge, call); - JSON_WRITE_FIELD(root, edge, exit); - return root; -} - -cJSON* SVFIRWriter::contentToJson(const MultiOpndStmt* edge) -{ - cJSON* root = contentToJson(static_cast(edge)); - JSON_WRITE_FIELD(root, edge, opVars); - return root; -} - -cJSON* SVFIRWriter::contentToJson(const PhiStmt* edge) -{ - cJSON* root = contentToJson(static_cast(edge)); - JSON_WRITE_FIELD(root, edge, opICFGNodes); - return root; -} - -cJSON* SVFIRWriter::contentToJson(const SelectStmt* edge) -{ - cJSON* root = contentToJson(static_cast(edge)); - JSON_WRITE_FIELD(root, edge, condition); - return root; -} - -cJSON* SVFIRWriter::contentToJson(const CmpStmt* edge) -{ - cJSON* root = contentToJson(static_cast(edge)); - JSON_WRITE_FIELD(root, edge, predicate); - return root; -} - -cJSON* SVFIRWriter::contentToJson(const BinaryOPStmt* edge) -{ - cJSON* root = contentToJson(static_cast(edge)); - JSON_WRITE_FIELD(root, edge, opcode); - return root; -} - -cJSON* SVFIRWriter::contentToJson(const UnaryOPStmt* edge) -{ - cJSON* root = contentToJson(static_cast(edge)); - JSON_WRITE_FIELD(root, edge, opcode); - return root; -} - -cJSON* SVFIRWriter::contentToJson(const BranchStmt* edge) -{ - cJSON* root = contentToJson(static_cast(edge)); - JSON_WRITE_FIELD(root, edge, successors); - JSON_WRITE_FIELD(root, edge, cond); - JSON_WRITE_FIELD(root, edge, brInst); - return root; -} - -cJSON* SVFIRWriter::contentToJson(const TDForkPE* edge) -{ - return contentToJson(static_cast(edge)); -} - -cJSON* SVFIRWriter::contentToJson(const TDJoinPE* edge) -{ - return contentToJson(static_cast(edge)); -} - -bool jsonAddNumberToObject(cJSON* obj, const char* name, double number) -{ - cJSON* node = cJSON_CreateNumber(number); - return jsonAddItemToObject(obj, name, node); -} - -bool jsonAddStringToObject(cJSON* obj, const char* name, const char* str) -{ - cJSON* node = cJSON_CreateStringReference(str); - return jsonAddItemToObject(obj, name, node); -} - -bool jsonAddStringToObject(cJSON* obj, const char* name, const std::string& s) -{ - return jsonAddStringToObject(obj, name, s.c_str()); -} - -bool jsonIsBool(const cJSON* item) -{ - return humanReadableOption() - ? cJSON_IsBool(item) - : cJSON_IsNumber(item) && - (item->valuedouble == 0 || item->valuedouble == 1); -} - -bool jsonIsBool(const cJSON* item, bool& flag) -{ - if (humanReadableOption()) - { - if (!cJSON_IsBool(item)) - return false; - flag = cJSON_IsTrue(item); - return true; - } - else - { - if (!cJSON_IsNumber(item)) - return false; - flag = item->valuedouble == 1; - return true; - } -} - -bool jsonIsNumber(const cJSON* item) -{ - return cJSON_IsNumber(item); -} - -bool jsonIsString(const cJSON* item) -{ - return cJSON_IsString(item); -} - -bool jsonIsNullId(const cJSON* item) -{ - // TODO: optimize - return cJSON_IsNull(item); -} - -bool jsonIsArray(const cJSON* item) -{ - return cJSON_IsArray(item); -} - -bool jsonIsMap(const cJSON* item) -{ - return cJSON_IsArray(item); -} - -bool jsonIsObject(const cJSON* item) -{ - return humanReadableOption() ? cJSON_IsObject(item) : cJSON_IsArray(item); -} - -bool jsonKeyEquals(const cJSON* item, const char* key) -{ - return item && !(humanReadableOption() && std::strcmp(item->string, key)); -} - -std::pair jsonUnpackPair(const cJSON* item) -{ - ABORT_IFNOT(jsonIsArray(item), "Expected array as pair"); - cJSON* child1 = item->child; - ABORT_IFNOT(child1, "Missing first child of pair"); - cJSON* child2 = child1->next; - ABORT_IFNOT(child2, "Missing first child of pair"); - ABORT_IFNOT(!child2->next, "Pair has more than two children"); - return {child1, child2}; -} - -double jsonGetNumber(const cJSON* item) -{ - ABORT_IFNOT(jsonIsNumber(item), "Expected number for " << JSON_KEY(item)); - return item->valuedouble; -} - -cJSON* jsonCreateNullId() -{ - // TODO: optimize - return cJSON_CreateNull(); -} - -cJSON* jsonCreateObject() -{ - return humanReadableOption() ? cJSON_CreateObject() : cJSON_CreateArray(); -} - -cJSON* jsonCreateArray() -{ - return cJSON_CreateArray(); -} - -cJSON* jsonCreateMap() -{ - // Don't use cJSON_CreateObject() here, because it only allows string keys. - return cJSON_CreateArray(); -} - -cJSON* jsonCreateString(const char* str) -{ - return cJSON_CreateStringReference(str); -} - -cJSON* jsonCreateIndex(size_t index) -{ - constexpr size_t maxPreciseIntInDouble = (1ull << 53); - (void)maxPreciseIntInDouble; // silence unused warning - assert(index <= maxPreciseIntInDouble); - return cJSON_CreateNumber(index); -} - -cJSON* jsonCreateBool(bool flag) -{ - bool hr = humanReadableOption(); - return hr ? cJSON_CreateBool(flag) : cJSON_CreateNumber(flag); -} - -cJSON* jsonCreateNumber(double num) -{ - return cJSON_CreateNumber(num); -} - -bool jsonAddPairToMap(cJSON* mapObj, cJSON* key, cJSON* value) -{ - cJSON* pair = cJSON_CreateArray(); - jsonAddItemToArray(pair, key); - jsonAddItemToArray(pair, value); - jsonAddItemToArray(mapObj, pair); - return pair; -} - -bool jsonAddItemToObject(cJSON* obj, const char* name, cJSON* item) -{ - return humanReadableOption() ? cJSON_AddItemToObject(obj, name, item) - : cJSON_AddItemToArray(obj, item); -} - -bool jsonAddItemToArray(cJSON* array, cJSON* item) -{ - return cJSON_AddItemToArray(array, item); -} - -ICFGWriter::ICFGWriter(const ICFG* icfg) : GenericICFGWriter(icfg) -{ - for (const auto& pair : icfg->getIcfgNodeToSVFLoopVec()) - { - for (const SVFLoop* loop : pair.second) - { - svfLoopPool.saveID(loop); - } - } -} - -SVFModuleWriter::SVFModuleWriter(const SVFModule* svfModule) -{ - // TODO: SVFType & StInfo are managed by SymbolTableInfo. Refactor it? - auto symTab = SymbolTableInfo::SymbolInfo(); - - const auto& svfTypes = symTab->getSVFTypes(); - svfTypePool.reserve(svfTypes.size()); - for (const SVFType* type : svfTypes) - { - svfTypePool.saveID(type); - } - - const auto& stInfos = symTab->getStInfos(); - stInfoPool.reserve(stInfos.size()); - for (const StInfo* stInfo : stInfos) - { - stInfoPool.saveID(stInfo); - } - - svfValuePool.reserve(svfModule->getFunctionSet().size() + - svfModule->getConstantSet().size() + - svfModule->getOtherValueSet().size()); -} - -SVFIRWriter::SVFIRWriter(const SVFIR* svfir) - : svfIR(svfir), svfModuleWriter(svfir->svfModule), icfgWriter(svfir->icfg), - chgWriter(SVFUtil::dyn_cast(svfir->chgraph)), - irGraphWriter(svfir) -{ -} - -void SVFIRWriter::writeJsonToOstream(const SVFIR* svfir, std::ostream& os) -{ - SVFIRWriter writer(svfir); - os << writer.generateJsonString().get() << '\n'; -} - -void SVFIRWriter::writeJsonToPath(const SVFIR* svfir, const std::string& path) -{ - std::ofstream jsonFile(path); - if (jsonFile.is_open()) - { - writeJsonToOstream(svfir, jsonFile); - jsonFile.close(); - } - else - { - SVFUtil::errs() << "Failed to open file '" << path - << "' to write SVFIR's JSON\n"; - } -} - -const char* SVFIRWriter::numToStr(size_t n) -{ - auto it = numToStrMap.find(n); - if (it != numToStrMap.end() && !(numToStrMap.key_comp()(n, it->first))) - { - return it->second.c_str(); - } - return numToStrMap.emplace_hint(it, n, std::to_string(n))->second.c_str(); -} - -SVFIRWriter::autoCStr SVFIRWriter::generateJsonString() -{ - autoJSON object = generateJson(); - char* str = humanReadableOption() ? cJSON_Print(object.get()) - : cJSON_PrintUnformatted(object.get()); - return {str, cJSON_free}; -} - -SVFIRWriter::autoJSON SVFIRWriter::generateJson() -{ - const IRGraph* const irGraph = svfIR; - NodeIDAllocator* nodeIDAllocator = NodeIDAllocator::allocator; - assert(nodeIDAllocator && "NodeIDAllocator is not initialized?"); - - cJSON* root = jsonCreateObject(); -#define F(field) JSON_WRITE_FIELD(root, svfIR, field) - F(svfModule); - F(symInfo); - F(icfg); - F(chgraph); - jsonAddJsonableToObject(root, FIELD_NAME_ITEM(irGraph)); - F(icfgNode2SVFStmtsMap); - F(icfgNode2PTASVFStmtsMap); - F(GepValObjMap); - F(typeLocSetsMap); - F(GepObjVarMap); - F(memToFieldsMap); - F(globSVFStmtSet); - F(phiNodeMap); - F(funArgsListMap); - F(callSiteArgsListMap); - F(callSiteRetMap); - F(funRetMap); - F(indCallSiteToFunPtrMap); - F(funPtrToCallSitesMap); - F(candidatePointers); - F(callSiteSet); - jsonAddJsonableToObject(root, FIELD_NAME_ITEM(nodeIDAllocator)); -#undef F - - return {root, cJSON_Delete}; -} - -cJSON* SVFIRWriter::toJson(const SVFType* type) -{ - return jsonCreateIndex(svfModuleWriter.getSVFTypeID(type)); -} - -cJSON* SVFIRWriter::toJson(const SVFValue* value) -{ - return jsonCreateIndex(svfModuleWriter.getSVFValueID(value)); -} - -cJSON* SVFIRWriter::toJson(const IRGraph* graph) -{ - ENSURE_NOT_VISITED(graph); - - cJSON* root = genericGraphToJson(graph, irGraphWriter.edgePool.getPool()); -#define F(field) JSON_WRITE_FIELD(root, graph, field) - F(KindToSVFStmtSetMap); - F(KindToPTASVFStmtSetMap); - F(fromFile); - F(nodeNumAfterPAGBuild); - F(totalPTAPAGEdge); - F(valueToEdgeMap); -#undef F - return root; -} - -cJSON* SVFIRWriter::toJson(const SVFVar* var) -{ - return var ? jsonCreateIndex(var->getId()) : jsonCreateNullId(); -} - -cJSON* SVFIRWriter::toJson(const SVFStmt* stmt) -{ - return jsonCreateIndex(irGraphWriter.getEdgeID(stmt)); -} - -cJSON* SVFIRWriter::toJson(const ICFG* icfg) -{ - cJSON* allSvfLoop = jsonCreateArray(); // all indices seen in constructor - for (const SVFLoop* svfLoop : icfgWriter.svfLoopPool) - { - cJSON* svfLoopObj = contentToJson(svfLoop); - jsonAddItemToArray(allSvfLoop, svfLoopObj); - } - -#define F(field) JSON_WRITE_FIELD(root, icfg, field) - cJSON* root = genericGraphToJson(icfg, icfgWriter.edgePool.getPool()); - jsonAddItemToObject(root, FIELD_NAME_ITEM(allSvfLoop)); // Meta field - F(totalICFGNode); - F(FunToFunEntryNodeMap); - F(FunToFunExitNodeMap); - F(CSToCallNodeMap); - F(CSToRetNodeMap); - F(InstToBlockNodeMap); - F(globalBlockNode); - F(icfgNodeToSVFLoopVec); -#undef F - return root; -} - -cJSON* SVFIRWriter::toJson(const ICFGNode* node) -{ - assert(node && "ICFGNode is null!"); - return jsonCreateIndex(node->getId()); -} - -cJSON* SVFIRWriter::toJson(const ICFGEdge* edge) -{ - assert(edge && "ICFGNode is null!"); - return jsonCreateIndex(icfgWriter.getEdgeID(edge)); -} - -cJSON* SVFIRWriter::toJson(const CommonCHGraph* graph) -{ - auto chg = SVFUtil::dyn_cast(graph); - assert(chg && "Unsupported CHGraph type!"); - return toJson(chg); -} - -cJSON* SVFIRWriter::toJson(const CHGraph* graph) -{ - cJSON* root = genericGraphToJson(graph, chgWriter.edgePool.getPool()); -#define F(field) JSON_WRITE_FIELD(root, graph, field) - // TODO: Ensure svfMod is the same as the SVFIR's? - F(classNum); - F(vfID); - // F(buildingCHGTime); No need - F(classNameToNodeMap); - F(classNameToDescendantsMap); - F(classNameToAncestorsMap); - F(classNameToInstAndDescsMap); - F(templateNameToInstancesMap); - F(csToClassesMap); - F(virtualFunctionToIDMap); - F(csToCHAVtblsMap); - F(csToCHAVFnsMap); -#undef F - return root; -} - -cJSON* SVFIRWriter::toJson(const CHNode* node) -{ - return jsonCreateIndex(node->getId()); -} - -cJSON* SVFIRWriter::toJson(const CHEdge* edge) -{ - return jsonCreateIndex(chgWriter.getEdgeID(edge)); -} - -cJSON* SVFIRWriter::toJson(const CallSite& cs) -{ - return toJson(cs.getInstruction()); -} - -cJSON* SVFIRWriter::toJson(const SVFLoop* loop) -{ - return jsonCreateIndex(icfgWriter.getSvfLoopID(loop)); -} - -cJSON* SVFIRWriter::contentToJson(const MemObj* memObj) -{ - cJSON* root = jsonCreateObject(); - JSON_WRITE_FIELD(root, memObj, symId); - JSON_WRITE_FIELD(root, memObj, typeInfo); - JSON_WRITE_FIELD(root, memObj, refVal); - return root; -} - -cJSON* SVFIRWriter::contentToJson(const StInfo* stInfo) -{ - cJSON* root = jsonCreateObject(); -#define F(field) JSON_WRITE_FIELD(root, stInfo, field) - F(stride); - F(numOfFlattenElements); - F(numOfFlattenFields); - F(fldIdxVec); - F(elemIdxVec); - F(fldIdx2TypeMap); - F(finfo); - F(flattenElementTypes); -#undef F - return root; -} - -cJSON* SVFIRWriter::toJson(const ObjTypeInfo* objTypeInfo) -{ - ENSURE_NOT_VISITED(objTypeInfo); - - cJSON* root = jsonCreateObject(); - JSON_WRITE_FIELD(root, objTypeInfo, type); - JSON_WRITE_FIELD(root, objTypeInfo, flags); - JSON_WRITE_FIELD(root, objTypeInfo, maxOffsetLimit); - JSON_WRITE_FIELD(root, objTypeInfo, elemNum); - return root; -} - -cJSON* SVFIRWriter::toJson(const MemObj* memObj) -{ - return jsonCreateIndex(memObj->getId()); -} - -cJSON* SVFIRWriter::toJson(const SVFLoopAndDomInfo* ldInfo) -{ - ENSURE_NOT_VISITED(ldInfo); - - cJSON* root = jsonCreateObject(); - JSON_WRITE_FIELD(root, ldInfo, reachableBBs); - JSON_WRITE_FIELD(root, ldInfo, dtBBsMap); - JSON_WRITE_FIELD(root, ldInfo, pdtBBsMap); - JSON_WRITE_FIELD(root, ldInfo, dfBBsMap); - JSON_WRITE_FIELD(root, ldInfo, bb2LoopMap); - JSON_WRITE_FIELD(root, ldInfo, bb2PdomLevel); - JSON_WRITE_FIELD(root, ldInfo, bb2PIdom); - return root; -} - -cJSON* SVFIRWriter::toJson(const StInfo* stInfo) -{ - return jsonCreateIndex(svfModuleWriter.getStInfoID(stInfo)); -} - -cJSON* SVFIRWriter::toJson(const AccessPath& ap) -{ - cJSON* root = jsonCreateObject(); - JSON_WRITE_FIELD(root, &ap, fldIdx); - JSON_WRITE_FIELD(root, &ap, idxOperandPairs); - return root; -} - -cJSON* SVFIRWriter::toJson(const NodeIDAllocator* nodeIDAllocator) -{ - ENSURE_NOT_VISITED(nodeIDAllocator); - - cJSON* root = jsonCreateObject(); - JSON_WRITE_FIELD(root, nodeIDAllocator, strategy); - JSON_WRITE_FIELD(root, nodeIDAllocator, numObjects); - JSON_WRITE_FIELD(root, nodeIDAllocator, numValues); - JSON_WRITE_FIELD(root, nodeIDAllocator, numSymbols); - JSON_WRITE_FIELD(root, nodeIDAllocator, numNodes); - return root; -} - -cJSON* SVFIRWriter::toJson(const SymbolTableInfo* symTable) -{ - ENSURE_NOT_VISITED(symTable); - - cJSON* root = jsonCreateObject(); - cJSON* allMemObj = jsonCreateArray(); - for (const auto& pair : symTable->objMap) - { - const MemObj* memObj = pair.second; - cJSON* memObjJson = contentToJson(memObj); - jsonAddItemToArray(allMemObj, memObjJson); - } - -#define F(field) JSON_WRITE_FIELD(root, symTable, field) - jsonAddItemToObject(root, FIELD_NAME_ITEM(allMemObj)); // Actual field - - F(valSymMap); - F(objSymMap); - F(returnSymMap); - F(varargSymMap); - // Field objMap can be represented by allMemObj - // Field mod can be represented by svfIR->svfModule. Delete it? - assert(symTable->mod == svfIR->svfModule && "SVFModule mismatch!"); - F(modelConstants); - F(totalSymNum); - F(maxStruct); - F(maxStSize); - // Field svfTypes can be represented by svfModuleWriter.svfTypePool - // Field stInfos can be represented by svfModuleWriter.stInfoPool -#undef F - - return root; -} - -cJSON* SVFIRWriter::toJson(const SVFModule* module) -{ - cJSON* root = jsonCreateObject(); - cJSON* allSVFType = jsonCreateArray(); - cJSON* allStInfo = jsonCreateArray(); - cJSON* allSVFValue = jsonCreateArray(); - - for (const SVFType* svfType : svfModuleWriter.svfTypePool) - { - cJSON* svfTypeObj = virtToJson(svfType); - jsonAddItemToArray(allSVFType, svfTypeObj); - } - - for (const StInfo* stInfo : svfModuleWriter.stInfoPool) - { - cJSON* stInfoObj = contentToJson(stInfo); - jsonAddItemToArray(allStInfo, stInfoObj); - } - -#define F(field) JSON_WRITE_FIELD(root, module, field) - jsonAddItemToObject(root, FIELD_NAME_ITEM(allSVFType)); // Meta field - jsonAddItemToObject(root, FIELD_NAME_ITEM(allStInfo)); // Meta field - jsonAddItemToObject(root, FIELD_NAME_ITEM(allSVFValue)); // Meta field - F(pagReadFromTxt); - F(moduleIdentifier); - - F(FunctionSet); - F(GlobalSet); - F(AliasSet); - F(ConstantSet); - F(OtherValueSet); -#undef F - - for (size_t i = 1; i <= svfModuleWriter.sizeSVFValuePool(); ++i) - { - cJSON* value = virtToJson(svfModuleWriter.getSVFValuePtr(i)); - jsonAddItemToArray(allSVFValue, value); - } - - return root; -} - -SVFIR* SVFIRReader::read(const cJSON* root) -{ - const cJSON* svfirField = createObjs(root); - - SVFIR* svfIR = SVFIR::getPAG(); // SVFIR constructor sets symInfo - IRGraph* irGraph = svfIR; - - auto svfModule = SVFModule::getSVFModule(); - auto icfg = new ICFG(); - auto chgraph = new CHGraph(svfModule); - auto symInfo = SymbolTableInfo::SymbolInfo(); - symInfo->mod = svfModule; - - svfIR->svfModule = svfModule; - svfIR->icfg = icfg; - svfIR->chgraph = chgraph; - -#define F(field) JSON_READ_FIELD_FWD(svfirField, svfIR, field) - readJson(symInfo); - readJson(irGraph); - readJson(icfg); - readJson(chgraph); - readJson(svfModule); - - F(icfgNode2SVFStmtsMap); - F(icfgNode2PTASVFStmtsMap); - F(GepValObjMap); - F(typeLocSetsMap); - F(GepObjVarMap); - F(memToFieldsMap); - F(globSVFStmtSet); - F(phiNodeMap); - F(funArgsListMap); - F(callSiteArgsListMap); - F(callSiteRetMap); - F(funRetMap); - F(indCallSiteToFunPtrMap); - F(funPtrToCallSitesMap); - F(candidatePointers); - F(callSiteSet); -#undef F - assert(!NodeIDAllocator::allocator && "NodeIDAllocator should be NULL"); - auto nodeIDAllocator = NodeIDAllocator::get(); - JSON_READ_OBJ_FWD(svfirField, nodeIDAllocator); - - return svfIR; -} - -const cJSON* SVFIRReader::createObjs(const cJSON* root) -{ -#define READ_CREATE_NODE_FWD(GType) \ - [](const cJSON*& nodeJson) { \ - JSON_DEF_READ_FWD(nodeJson, NodeID, id); \ - JSON_DEF_READ_FWD(nodeJson, GNodeK, nodeKind); \ - return std::make_pair(id, create##GType##Node(id, nodeKind)); \ - } -#define READ_CREATE_EDGE_FWD(GType) \ - [](const cJSON*& edgeJson) { \ - JSON_DEF_READ_FWD(edgeJson, GEdgeFlag, edgeFlag); \ - auto kind = applyEdgeMask(edgeFlag); \ - auto edge = create##GType##Edge(kind); \ - setEdgeFlag(edge, edgeFlag); \ - return edge; \ - } - - ABORT_IFNOT(jsonIsObject(root), "Root should be an object"); - - const cJSON* const svfModule = root->child; - CHECK_JSON_KEY(svfModule); - svfModuleReader.createObjs( - svfModule, - // SVFType Creator - [](const cJSON*& svfTypeFldJson) - { - JSON_DEF_READ_FWD(svfTypeFldJson, SVFType::GNodeK, kind); - JSON_DEF_READ_FWD(svfTypeFldJson, bool, isSingleValTy); - return createSVFType(kind, isSingleValTy); - }, - // SVFType Filler - [this](const cJSON*& svfVarFldJson, SVFType* type) - { - virtFill(svfVarFldJson, type); - }, - // SVFValue Creator - [this](const cJSON*& svfValueFldJson) - { - JSON_DEF_READ_FWD(svfValueFldJson, SVFValue::GNodeK, kind); - JSON_DEF_READ_FWD(svfValueFldJson, const SVFType*, type, {}); - JSON_DEF_READ_FWD(svfValueFldJson, std::string, name); - return createSVFValue(kind, type, std::move(name)); - }, - // SVFValue Filler - [this](const cJSON*& svfVarFldJson, SVFValue* value) - { - virtFill(svfVarFldJson, value); - }, - // StInfo Creator (no filler needed) - [this](const cJSON*& stInfoFldJson) - { - JSON_DEF_READ_FWD(stInfoFldJson, u32_t, stride); - auto si = new StInfo(stride); - fill(stInfoFldJson, si); - ABORT_IFNOT(!stInfoFldJson, "StInfo has extra field"); - return si; - }); - - const cJSON* const symInfo = svfModule->next; - CHECK_JSON_KEY(symInfo); - symTableReader.createObjs( - symInfo, - // MemObj Creator (no filler needed) - [this](const cJSON*& memObjFldJson) - { - JSON_DEF_READ_FWD(memObjFldJson, SymID, symId); - JSON_DEF_READ_FWD(memObjFldJson, ObjTypeInfo*, typeInfo, {}); - JSON_DEF_READ_FWD(memObjFldJson, const SVFValue*, refVal, {}); - return std::make_pair(symId, new MemObj(symId, typeInfo, refVal)); - }); - - const cJSON* const icfg = symInfo->next; - CHECK_JSON_KEY(icfg); - icfgReader.createObjs(icfg, READ_CREATE_NODE_FWD(ICFG), - READ_CREATE_EDGE_FWD(ICFG), - [](auto) - { - return new SVFLoop({}, 0); - }); - - const cJSON* const chgraph = icfg->next; - CHECK_JSON_KEY(chgraph); - chGraphReader.createObjs(chgraph, READ_CREATE_NODE_FWD(CH), - READ_CREATE_EDGE_FWD(CH)); - - const cJSON* const irGraph = chgraph->next; - CHECK_JSON_KEY(irGraph); - irGraphReader.createObjs(irGraph, READ_CREATE_NODE_FWD(PAG), - READ_CREATE_EDGE_FWD(PAG)); - - icfgReader.fillObjs( - [this](const cJSON*& j, ICFGNode* node) - { - virtFill(j, node); - }, - [this](const cJSON*& j, ICFGEdge* edge) - { - virtFill(j, edge); - }, - [this](const cJSON*& j, SVFLoop* loop) - { - fill(j, loop); - }); - chGraphReader.fillObjs( - [this](const cJSON*& j, CHNode* node) - { - virtFill(j, node); - }, - [this](const cJSON*& j, CHEdge* edge) - { - virtFill(j, edge); - }); - irGraphReader.fillObjs( - [this](const cJSON*& j, SVFVar* var) - { - virtFill(j, var); - }, - [this](const cJSON*& j, SVFStmt* stmt) - { - virtFill(j, stmt); - }); - - return irGraph->next; - -#undef READ_CREATE_EDGE_FWD -#undef READ_CREATE_NODE_FWD -} - -void SVFIRReader::readJson(const cJSON* obj, bool& flag) -{ - ABORT_IFNOT(jsonIsBool(obj, flag), "Expect bool for " << obj->string); -} - -void SVFIRReader::readJson(const cJSON* obj, unsigned& val) -{ - readSmallNumber(obj, val); -} - -void SVFIRReader::readJson(const cJSON* obj, int& val) -{ - readSmallNumber(obj, val); -} - -void SVFIRReader::readJson(const cJSON* obj, short& val) -{ - readSmallNumber(obj, val); -} - -void SVFIRReader::readJson(const cJSON* obj, float& val) -{ - readSmallNumber(obj, val); -} - -void SVFIRReader::readJson(const cJSON* obj, unsigned long& val) -{ - readBigNumber(obj, val, - [](const char* s) - { - return std::strtoul(s, nullptr, 10); - }); -} - -void SVFIRReader::readJson(const cJSON* obj, long long& val) -{ - readBigNumber(obj, val, - [](const char* s) - { - return std::strtoll(s, nullptr, 10); - }); -} - -void SVFIRReader::readJson(const cJSON* obj, unsigned long long& val) -{ - readBigNumber(obj, val, - [](const char* s) - { - return std::strtoull(s, nullptr, 10); - }); -} - -void SVFIRReader::readJson(const cJSON* obj, std::string& str) -{ - ABORT_IFNOT(jsonIsString(obj), "Expect string for " << obj->string); - str = obj->valuestring; -} - -ICFGNode* SVFIRReader::createICFGNode(NodeID id, GNodeK kind) -{ - switch (kind) - { - default: - ABORT_MSG(kind << " is an impossible ICFGNodeKind in create()"); -#define CASE(kind, constructor) \ - case ICFGNode::kind: \ - return new constructor(id); - CASE(IntraBlock, IntraICFGNode); - CASE(FunEntryBlock, FunEntryICFGNode); - CASE(FunExitBlock, FunExitICFGNode); - CASE(FunCallBlock, CallICFGNode); - CASE(FunRetBlock, RetICFGNode); - CASE(GlobalBlock, GlobalICFGNode); -#undef CASE - } -} - -ICFGEdge* SVFIRReader::createICFGEdge(GEdgeKind kind) -{ - constexpr ICFGNode* src = nullptr; - constexpr ICFGNode* dst = nullptr; - - switch (kind) - { - default: - ABORT_MSG(kind << " is an impossible ICFGEdgeKind in create()"); - case ICFGEdge::IntraCF: - return new IntraCFGEdge(src, dst); - case ICFGEdge::CallCF: - return new CallCFGEdge(src, dst, nullptr); - case ICFGEdge::RetCF: - return new RetCFGEdge(src, dst, nullptr); - } -} - -CHNode* SVFIRReader::createCHNode(NodeID id, GNodeK kind) -{ - ABORT_IFNOT(kind == 0, "Impossible CHNode kind " << kind); - return new CHNode("", id); -} - -CHEdge* SVFIRReader::createCHEdge(GEdgeKind kind) -{ - ABORT_IFNOT(kind == 0, "Unsupported CHEdge kind " << kind); - return new CHEdge(nullptr, nullptr, {}); -} - -SVFVar* SVFIRReader::createPAGNode(NodeID id, GNodeK kind) -{ - switch (kind) - { - default: - ABORT_MSG(kind << " is an impossible SVFVarKind in create()"); -#define CASE(kind, constructor) \ - case SVFVar::kind: \ - return new constructor(id); - CASE(ValNode, ValVar); - CASE(RetNode, RetPN); - CASE(ObjNode, ObjVar); - CASE(VarargNode, VarArgPN); - CASE(GepValNode, GepValVar); - CASE(GepObjNode, GepObjVar); - CASE(FIObjNode, FIObjVar); - CASE(DummyValNode, DummyValVar); - CASE(DummyObjNode, DummyObjVar); -#undef CASE - } -} - -SVFStmt* SVFIRReader::createPAGEdge(GEdgeKind kind) -{ - switch (kind) - { - default: - ABORT_MSG(kind << " is an impossible SVFStmtKind in create()"); -#define CASE(kind, constructor) \ - case SVFStmt::kind: \ - return new constructor; - CASE(Addr, AddrStmt); - CASE(Copy, CopyStmt); - CASE(Store, StoreStmt); - CASE(Load, LoadStmt); - CASE(Call, CallPE); - CASE(Ret, RetPE); - CASE(Gep, GepStmt); - CASE(Phi, PhiStmt); - CASE(Select, SelectStmt); - CASE(Cmp, CmpStmt); - CASE(BinaryOp, BinaryOPStmt); - CASE(UnaryOp, UnaryOPStmt); - CASE(Branch, BranchStmt); - CASE(ThreadFork, TDForkPE); - CASE(ThreadJoin, TDJoinPE); -#undef CASE - } -} - -void SVFIRReader::readJson(const cJSON* obj, NodeIDAllocator* idAllocator) -{ - assert(idAllocator && "idAllocator should be nonempty"); - - ABORT_IFNOT(jsonIsObject(obj), "Expect object for " << JSON_KEY(obj)); - obj = obj->child; - - JSON_DEF_READ_FWD(obj, int, strategy); - static_assert(sizeof(idAllocator->strategy) == sizeof(strategy), - "idAllocator->strategy should be represented by int"); - idAllocator->strategy = static_cast(strategy); - JSON_READ_FIELD_FWD(obj, idAllocator, numObjects); - JSON_READ_FIELD_FWD(obj, idAllocator, numValues); - JSON_READ_FIELD_FWD(obj, idAllocator, numSymbols); - JSON_READ_FIELD_FWD(obj, idAllocator, numNodes); - - ABORT_IFNOT(!obj, "Extra field " << JSON_KEY(obj) << " in NodeIDAllocator"); -} - -void SVFIRReader::readJson(SymbolTableInfo* symTabInfo) -{ - const cJSON* obj = symTableReader.getFieldJson(); -#define F(field) JSON_READ_FIELD_FWD(obj, symTabInfo, field) - // `allMemObj` was consumed during create & fill phase. - F(valSymMap); - F(objSymMap); - F(returnSymMap); - F(varargSymMap); - symTableReader.memObjMap.saveToIDToObjMap(symTabInfo->objMap); // objMap - F(modelConstants); - F(totalSymNum); - F(maxStruct); - F(maxStSize); -#undef F - ABORT_IFNOT(!obj, "Extra field " << JSON_KEY(obj) << " in SymbolTableInfo"); -} - -void SVFIRReader::readJson(IRGraph* graph) -{ - assert(SymbolTableInfo::symInfo && "SymbolTableInfo should be nonempty"); - assert(graph->symInfo == SymbolTableInfo::SymbolInfo() && "symInfo differ"); - - auto& valToEdgeMap = graph->valueToEdgeMap; - valToEdgeMap.clear(); - - irGraphReader.saveToGenericGraph(graph); - const cJSON* obj = irGraphReader.getFieldJson(); -#define F(field) JSON_READ_FIELD_FWD(obj, graph, field) - // base and symInfo have already been read - F(KindToSVFStmtSetMap); - F(KindToPTASVFStmtSetMap); - F(fromFile); - F(nodeNumAfterPAGBuild); - F(totalPTAPAGEdge); - F(valueToEdgeMap); -#undef F - - auto nullit = valToEdgeMap.find(nullptr); - ABORT_IFNOT(nullit != valToEdgeMap.end(), "valueToEdgeMap should has key NULL"); - ABORT_IFNOT(nullit->second.empty(), "valueToEdgeMap[NULL] should be empty"); -} - -void SVFIRReader::readJson(ICFG* icfg) -{ - icfgReader.saveToGenericGraph(icfg); - const cJSON* obj = icfgReader.getFieldJson(); -#define F(field) JSON_READ_FIELD_FWD(obj, icfg, field) - F(totalICFGNode); - F(FunToFunEntryNodeMap); - F(FunToFunExitNodeMap); - F(CSToCallNodeMap); - F(CSToRetNodeMap); - F(InstToBlockNodeMap); - F(globalBlockNode); - F(icfgNodeToSVFLoopVec); -#undef F -} - -void SVFIRReader::readJson(CHGraph* graph) -{ - chGraphReader.saveToGenericGraph(graph); - const cJSON* obj = chGraphReader.getFieldJson(); -#define F(field) JSON_READ_FIELD_FWD(obj, graph, field) - F(classNum); - F(vfID); - F(classNameToNodeMap); - F(classNameToDescendantsMap); - F(classNameToAncestorsMap); - F(classNameToInstAndDescsMap); - F(templateNameToInstancesMap); - F(csToClassesMap); - F(virtualFunctionToIDMap); - F(csToCHAVtblsMap); - F(csToCHAVFnsMap); -#undef F -} - -void SVFIRReader::readJson(SVFModule* module) -{ - const cJSON* obj = svfModuleReader.getFieldJson(); - auto symInfo = SymbolTableInfo::symInfo; - assert(symInfo && "SymbolTableInfo should be non-NULL"); - svfModuleReader.svfTypePool.saveToSet(symInfo->svfTypes); - svfModuleReader.stInfoPool.saveToSet(symInfo->stInfos); - -#define F(field) JSON_READ_FIELD_FWD(obj, module, field) - F(pagReadFromTxt); - F(moduleIdentifier); - F(FunctionSet); - F(GlobalSet); - F(AliasSet); - F(ConstantSet); - F(OtherValueSet); -#undef F -} - -void SVFIRReader::readJson(const cJSON* obj, SVFType*& type) -{ - assert(!type && "SVFType already read?"); - type = svfModuleReader.getSVFTypePtr(jsonGetNumber(obj)); -} - -void SVFIRReader::readJson(const cJSON* obj, StInfo*& stInfo) -{ - assert(!stInfo && "StInfo already read?"); - stInfo = svfModuleReader.getStInfoPtr(jsonGetNumber(obj)); -} - -void SVFIRReader::readJson(const cJSON* obj, SVFValue*& value) -{ - assert(!value && "SVFValue already read?"); - value = svfModuleReader.getSVFValuePtr(jsonGetNumber(obj)); -} - -void SVFIRReader::readJson(const cJSON* obj, SVFVar*& var) -{ - assert(!var && "SVFVar already read?"); - if (jsonIsNullId(obj)) - var = nullptr; - else - var = irGraphReader.getNodePtr(jsonGetNumber(obj)); -} - -void SVFIRReader::readJson(const cJSON* obj, SVFStmt*& stmt) -{ - assert(!stmt && "SVFStmt already read?"); - stmt = irGraphReader.getEdgePtr(jsonGetNumber(obj)); -} - -void SVFIRReader::readJson(const cJSON* obj, ICFGNode*& node) -{ - assert(!node && "ICFGNode already read?"); - NodeID id = jsonGetNumber(obj); - node = icfgReader.getNodePtr(id); -} - -void SVFIRReader::readJson(const cJSON* obj, ICFGEdge*& edge) -{ - assert(!edge && "ICFGEdge already read?"); - edge = icfgReader.getEdgePtr(jsonGetNumber(obj)); -} - -void SVFIRReader::readJson(const cJSON* obj, CHNode*& node) -{ - assert(!node && "CHNode already read?"); - node = chGraphReader.getNodePtr(jsonGetNumber(obj)); -} - -void SVFIRReader::readJson(const cJSON* obj, CHEdge*& edge) -{ - assert(!edge && "CHEdge already read?"); - edge = chGraphReader.getEdgePtr(jsonGetNumber(obj)); -} - -void SVFIRReader::readJson(const cJSON* obj, CallSite& cs) -{ - readJson(obj, cs.CB); -} - -void SVFIRReader::readJson(const cJSON* obj, AccessPath& ap) -{ - ABORT_IFNOT(jsonIsObject(obj), "Expected obj for AccessPath"); - obj = obj->child; - JSON_READ_FIELD_FWD(obj, &ap, fldIdx); - JSON_READ_FIELD_FWD(obj, &ap, idxOperandPairs); - ABORT_IFNOT(!obj, "Extra field " << JSON_KEY(obj) << " in AccessPath"); -} - -void SVFIRReader::readJson(const cJSON* obj, SVFLoop*& loop) -{ - assert(!loop && "SVFLoop already read?"); - unsigned id = jsonGetNumber(obj); - loop = icfgReader.getSVFLoopPtr(id); -} - -void SVFIRReader::readJson(const cJSON* obj, MemObj*& memObj) -{ - assert(!memObj && "MemObj already read?"); - memObj = symTableReader.getMemObjPtr(jsonGetNumber(obj)); -} - -void SVFIRReader::readJson(const cJSON* obj, ObjTypeInfo*& objTypeInfo) -{ - assert(!objTypeInfo && "ObjTypeInfo already read?"); - ABORT_IFNOT(jsonIsObject(obj), "Expected object for objTypeInfo"); - cJSON* field = obj->child; - - JSON_DEF_READ_FWD(field, SVFType*, type, {}); - JSON_DEF_READ_FWD(field, u32_t, flags); - JSON_DEF_READ_FWD(field, u32_t, maxOffsetLimit); - JSON_DEF_READ_FWD(field, u32_t, elemNum); - - ABORT_IFNOT(!field, "Extra field in objTypeInfo: " << JSON_KEY(field)); - objTypeInfo = new ObjTypeInfo(type, maxOffsetLimit); - objTypeInfo->flags = flags; - objTypeInfo->elemNum = elemNum; -} - -void SVFIRReader::readJson(const cJSON* obj, SVFLoopAndDomInfo*& ldInfo) -{ - assert(!ldInfo && "SVFLoopAndDomInfo already read?"); - ABORT_IFNOT(jsonIsObject(obj), "Expected object for SVFLoopAndDomInfo"); - cJSON* field = obj->child; - - ldInfo = new SVFLoopAndDomInfo(); - - JSON_READ_FIELD_FWD(field, ldInfo, reachableBBs); - JSON_READ_FIELD_FWD(field, ldInfo, dtBBsMap); - JSON_READ_FIELD_FWD(field, ldInfo, pdtBBsMap); - JSON_READ_FIELD_FWD(field, ldInfo, dfBBsMap); - JSON_READ_FIELD_FWD(field, ldInfo, bb2LoopMap); - JSON_READ_FIELD_FWD(field, ldInfo, bb2PdomLevel); - JSON_READ_FIELD_FWD(field, ldInfo, bb2PIdom); - - ABORT_IFNOT(!field, - "Extra field in SVFLoopAndDomInfo: " << JSON_KEY(field)); -} - -void SVFIRReader::virtFill(const cJSON*& fieldJson, SVFVar* var) -{ - switch (var->getNodeKind()) - { - default: - assert(false && "Unknown SVFVar kind"); - -#define CASE(VarKind, VarType) \ - case SVFVar::VarKind: \ - return fill(fieldJson, static_cast(var)) - - CASE(ValNode, ValVar); - CASE(ObjNode, ObjVar); - CASE(RetNode, RetPN); - CASE(VarargNode, VarArgPN); - CASE(GepValNode, GepValVar); - CASE(GepObjNode, GepObjVar); - CASE(FIObjNode, FIObjVar); - CASE(DummyValNode, DummyValVar); - CASE(DummyObjNode, DummyObjVar); -#undef CASE - } -} - -void SVFIRReader::fill(const cJSON*& fieldJson, SVFVar* var) -{ - fill(fieldJson, static_cast(var)); - JSON_READ_FIELD_FWD(fieldJson, var, value); - JSON_READ_FIELD_FWD(fieldJson, var, InEdgeKindToSetMap); - JSON_READ_FIELD_FWD(fieldJson, var, OutEdgeKindToSetMap); - JSON_READ_FIELD_FWD(fieldJson, var, isPtr); -} - -void SVFIRReader::fill(const cJSON*& fieldJson, ValVar* var) -{ - fill(fieldJson, static_cast(var)); -} - -void SVFIRReader::fill(const cJSON*& fieldJson, ObjVar* var) -{ - fill(fieldJson, static_cast(var)); - JSON_READ_FIELD_FWD(fieldJson, var, mem); -} - -void SVFIRReader::fill(const cJSON*& fieldJson, GepValVar* var) -{ - fill(fieldJson, static_cast(var)); - JSON_READ_FIELD_FWD(fieldJson, var, ap); - JSON_READ_FIELD_FWD(fieldJson, var, gepValType); -} - -void SVFIRReader::fill(const cJSON*& fieldJson, GepObjVar* var) -{ - fill(fieldJson, static_cast(var)); - JSON_READ_FIELD_FWD(fieldJson, var, apOffset); - JSON_READ_FIELD_FWD(fieldJson, var, base); -} - -void SVFIRReader::fill(const cJSON*& fieldJson, FIObjVar* var) -{ - fill(fieldJson, static_cast(var)); -} - -void SVFIRReader::fill(const cJSON*& fieldJson, RetPN* var) -{ - fill(fieldJson, static_cast(var)); -} - -void SVFIRReader::fill(const cJSON*& fieldJson, VarArgPN* var) -{ - fill(fieldJson, static_cast(var)); -} - -void SVFIRReader::fill(const cJSON*& fieldJson, DummyValVar* var) -{ - fill(fieldJson, static_cast(var)); -} - -void SVFIRReader::fill(const cJSON*& fieldJson, DummyObjVar* var) -{ - fill(fieldJson, static_cast(var)); -} - -void SVFIRReader::virtFill(const cJSON*& fieldJson, SVFStmt* stmt) -{ - auto kind = stmt->getEdgeKind(); - - switch (kind) - { - default: - ABORT_MSG("Unknown SVFStmt kind " << kind); - -#define CASE(EdgeKind, EdgeType) \ - case SVFStmt::EdgeKind: \ - return fill(fieldJson, static_cast(stmt)) - - CASE(Addr, AddrStmt); - CASE(Copy, CopyStmt); - CASE(Store, StoreStmt); - CASE(Load, LoadStmt); - CASE(Call, CallPE); - CASE(Ret, RetPE); - CASE(Gep, GepStmt); - CASE(Phi, PhiStmt); - CASE(Select, SelectStmt); - CASE(Cmp, CmpStmt); - CASE(BinaryOp, BinaryOPStmt); - CASE(UnaryOp, UnaryOPStmt); - CASE(Branch, BranchStmt); - CASE(ThreadFork, TDForkPE); - CASE(ThreadJoin, TDJoinPE); -#undef CASE - } -} - -void SVFIRReader::fill(const cJSON*& fieldJson, SVFStmt* stmt) -{ - fill(fieldJson, static_cast(stmt)); - JSON_READ_FIELD_FWD(fieldJson, stmt, value); - JSON_READ_FIELD_FWD(fieldJson, stmt, basicBlock); - JSON_READ_FIELD_FWD(fieldJson, stmt, icfgNode); - JSON_READ_FIELD_FWD(fieldJson, stmt, edgeId); -} - -void SVFIRReader::fill(const cJSON*& fieldJson, AssignStmt* stmt) -{ - fill(fieldJson, static_cast(stmt)); -} - -void SVFIRReader::fill(const cJSON*& fieldJson, AddrStmt* stmt) -{ - fill(fieldJson, static_cast(stmt)); -} - -void SVFIRReader::fill(const cJSON*& fieldJson, CopyStmt* stmt) -{ - fill(fieldJson, static_cast(stmt)); - JSON_READ_FIELD_FWD(fieldJson, stmt, copyKind); -} - -void SVFIRReader::fill(const cJSON*& fieldJson, StoreStmt* stmt) -{ - fill(fieldJson, static_cast(stmt)); -} - -void SVFIRReader::fill(const cJSON*& fieldJson, LoadStmt* stmt) -{ - fill(fieldJson, static_cast(stmt)); -} - -void SVFIRReader::fill(const cJSON*& fieldJson, GepStmt* stmt) -{ - fill(fieldJson, static_cast(stmt)); - JSON_READ_FIELD_FWD(fieldJson, stmt, ap); - JSON_READ_FIELD_FWD(fieldJson, stmt, variantField); -} - -void SVFIRReader::fill(const cJSON*& fieldJson, CallPE* stmt) -{ - fill(fieldJson, static_cast(stmt)); - JSON_READ_FIELD_FWD(fieldJson, stmt, call); - JSON_READ_FIELD_FWD(fieldJson, stmt, entry); -} - -void SVFIRReader::fill(const cJSON*& fieldJson, RetPE* stmt) -{ - fill(fieldJson, static_cast(stmt)); - JSON_READ_FIELD_FWD(fieldJson, stmt, call); - JSON_READ_FIELD_FWD(fieldJson, stmt, exit); -} - -void SVFIRReader::fill(const cJSON*& fieldJson, MultiOpndStmt* stmt) -{ - fill(fieldJson, static_cast(stmt)); - JSON_READ_FIELD_FWD(fieldJson, stmt, opVars); -} - -void SVFIRReader::fill(const cJSON*& fieldJson, PhiStmt* stmt) -{ - fill(fieldJson, static_cast(stmt)); - JSON_READ_FIELD_FWD(fieldJson, stmt, opICFGNodes); -} - -void SVFIRReader::fill(const cJSON*& fieldJson, SelectStmt* stmt) -{ - fill(fieldJson, static_cast(stmt)); - JSON_READ_FIELD_FWD(fieldJson, stmt, condition); -} - -void SVFIRReader::fill(const cJSON*& fieldJson, CmpStmt* stmt) -{ - fill(fieldJson, static_cast(stmt)); - JSON_READ_FIELD_FWD(fieldJson, stmt, predicate); -} - -void SVFIRReader::fill(const cJSON*& fieldJson, BinaryOPStmt* stmt) -{ - fill(fieldJson, static_cast(stmt)); - JSON_READ_FIELD_FWD(fieldJson, stmt, opcode); -} - -void SVFIRReader::fill(const cJSON*& fieldJson, UnaryOPStmt* stmt) -{ - fill(fieldJson, static_cast(stmt)); - JSON_READ_FIELD_FWD(fieldJson, stmt, opcode); -} - -void SVFIRReader::fill(const cJSON*& fieldJson, BranchStmt* stmt) -{ - fill(fieldJson, static_cast(stmt)); - JSON_READ_FIELD_FWD(fieldJson, stmt, successors); - JSON_READ_FIELD_FWD(fieldJson, stmt, cond); - JSON_READ_FIELD_FWD(fieldJson, stmt, brInst); -} - -void SVFIRReader::fill(const cJSON*& fieldJson, TDForkPE* stmt) -{ - fill(fieldJson, static_cast(stmt)); -} - -void SVFIRReader::fill(const cJSON*& fieldJson, TDJoinPE* stmt) -{ - fill(fieldJson, static_cast(stmt)); -} - -void SVFIRReader::fill(const cJSON*& fieldJson, MemObj* memObj) -{ - // symId has already been read - JSON_READ_FIELD_FWD(fieldJson, memObj, typeInfo); - JSON_READ_FIELD_FWD(fieldJson, memObj, refVal); -} - -void SVFIRReader::fill(const cJSON*& fieldJson, StInfo* stInfo) -{ -#define F(field) JSON_READ_FIELD_FWD(fieldJson, stInfo, field) - // stride has already been read upon construction - F(numOfFlattenElements); - F(numOfFlattenFields); - F(fldIdxVec); - F(elemIdxVec); - F(fldIdx2TypeMap); - F(finfo); - F(flattenElementTypes); -#undef F -} - -void SVFIRReader::virtFill(const cJSON*& fieldJson, ICFGNode* node) -{ - switch (node->getNodeKind()) - { - default: - ABORT_MSG("Unknown ICFGNode kind " << node->getNodeKind()); - -#define CASE(NodeKind, NodeType) \ - case ICFGNode::NodeKind: \ - return fill(fieldJson, static_cast(node)) - - CASE(IntraBlock, IntraICFGNode); - CASE(FunEntryBlock, FunEntryICFGNode); - CASE(FunExitBlock, FunExitICFGNode); - CASE(FunCallBlock, CallICFGNode); - CASE(FunRetBlock, RetICFGNode); - CASE(GlobalBlock, GlobalICFGNode); -#undef CASE - } -} - -void SVFIRReader::fill(const cJSON*& fieldJson, ICFGNode* node) -{ - fill(fieldJson, static_cast(node)); - JSON_READ_FIELD_FWD(fieldJson, node, fun); - JSON_READ_FIELD_FWD(fieldJson, node, bb); - // Skip VFGNodes as it is empty - JSON_READ_FIELD_FWD(fieldJson, node, pagEdges); -} - -void SVFIRReader::fill(const cJSON*& fieldJson, GlobalICFGNode* node) -{ - fill(fieldJson, static_cast(node)); -} - -void SVFIRReader::fill(const cJSON*& fieldJson, IntraICFGNode* node) -{ - fill(fieldJson, static_cast(node)); - JSON_READ_FIELD_FWD(fieldJson, node, inst); -} - -void SVFIRReader::fill(const cJSON*& fieldJson, InterICFGNode* node) -{ - fill(fieldJson, static_cast(node)); -} - -void SVFIRReader::fill(const cJSON*& fieldJson, FunEntryICFGNode* node) -{ - fill(fieldJson, static_cast(node)); - JSON_READ_FIELD_FWD(fieldJson, node, FPNodes); -} - -void SVFIRReader::fill(const cJSON*& fieldJson, FunExitICFGNode* node) -{ - fill(fieldJson, static_cast(node)); - JSON_READ_FIELD_FWD(fieldJson, node, formalRet); -} - -void SVFIRReader::fill(const cJSON*& fieldJson, CallICFGNode* node) -{ - fill(fieldJson, static_cast(node)); - JSON_READ_FIELD_FWD(fieldJson, node, cs); - JSON_READ_FIELD_FWD(fieldJson, node, ret); - JSON_READ_FIELD_FWD(fieldJson, node, APNodes); -} - -void SVFIRReader::fill(const cJSON*& fieldJson, RetICFGNode* node) -{ - fill(fieldJson, static_cast(node)); - JSON_READ_FIELD_FWD(fieldJson, node, cs); - JSON_READ_FIELD_FWD(fieldJson, node, actualRet); - JSON_READ_FIELD_FWD(fieldJson, node, callBlockNode); -} - -void SVFIRReader::virtFill(const cJSON*& fieldJson, ICFGEdge* edge) -{ - auto kind = edge->getEdgeKind(); - switch (kind) - { - default: - ABORT_MSG("Unknown ICFGEdge kind " << kind); - case ICFGEdge::IntraCF: - return fill(fieldJson, static_cast(edge)); - case ICFGEdge::CallCF: - return fill(fieldJson, static_cast(edge)); - case ICFGEdge::RetCF: - return fill(fieldJson, static_cast(edge)); - } -} - -void SVFIRReader::fill(const cJSON*& fieldJson, ICFGEdge* edge) -{ - fill(fieldJson, static_cast(edge)); -} - -void SVFIRReader::fill(const cJSON*& fieldJson, IntraCFGEdge* edge) -{ - fill(fieldJson, static_cast(edge)); - JSON_READ_FIELD_FWD(fieldJson, edge, conditionVar); - JSON_READ_FIELD_FWD(fieldJson, edge, branchCondVal); -} - -void SVFIRReader::fill(const cJSON*& fieldJson, CallCFGEdge* edge) -{ - fill(fieldJson, static_cast(edge)); - JSON_READ_FIELD_FWD(fieldJson, edge, cs); - JSON_READ_FIELD_FWD(fieldJson, edge, callPEs); -} - -void SVFIRReader::fill(const cJSON*& fieldJson, RetCFGEdge* edge) -{ - fill(fieldJson, static_cast(edge)); - JSON_READ_FIELD_FWD(fieldJson, edge, cs); - JSON_READ_FIELD_FWD(fieldJson, edge, retPE); -} - -void SVFIRReader::fill(const cJSON*& fieldJson, SVFLoop* loop) -{ -#define F(field) JSON_READ_FIELD_FWD(fieldJson, loop, field) - F(entryICFGEdges); - F(backICFGEdges); - F(inICFGEdges); - F(outICFGEdges); - F(icfgNodes); - F(loopBound); -#undef F -} - -void SVFIRReader::virtFill(const cJSON*& fieldJson, CHNode* node) -{ - assert(node->getNodeKind() == 0 && "Unknown CHNode kind"); - fill(fieldJson, static_cast(node)); - JSON_READ_FIELD_FWD(fieldJson, node, vtable); - JSON_READ_FIELD_FWD(fieldJson, node, className); - JSON_READ_FIELD_FWD(fieldJson, node, flags); - JSON_READ_FIELD_FWD(fieldJson, node, virtualFunctionVectors); -} - -void SVFIRReader::virtFill(const cJSON*& fieldJson, CHEdge* edge) -{ - assert(edge->getEdgeKind() == 0 && "Unknown CHEdge kind"); - fill(fieldJson, static_cast(edge)); - // edgeType is a enum - JSON_DEF_READ_FWD(fieldJson, unsigned, edgeType); - if (edgeType == CHEdge::INHERITANCE) - edge->edgeType = CHEdge::INHERITANCE; - else if (edgeType == CHEdge::INSTANTCE) - edge->edgeType = CHEdge::INSTANTCE; - else - ABORT_MSG("Unknown CHEdge type " << edgeType); -} - -void SVFIRReader::virtFill(const cJSON*& fieldJson, SVFValue* value) -{ - auto kind = value->getKind(); - - switch (kind) - { - default: - ABORT_MSG("Impossible SVFValue kind " << kind); - -#define CASE(ValueKind, Type) \ - case SVFValue::ValueKind: \ - return fill(fieldJson, static_cast(value)) - - CASE(SVFVal, SVFValue); - CASE(SVFFunc, SVFFunction); - CASE(SVFBB, SVFBasicBlock); - CASE(SVFInst, SVFInstruction); - CASE(SVFCall, SVFCallInst); - CASE(SVFVCall, SVFVirtualCallInst); - CASE(SVFGlob, SVFGlobalValue); - CASE(SVFArg, SVFArgument); - CASE(SVFConst, SVFConstant); - CASE(SVFConstData, SVFConstantData); - CASE(SVFConstInt, SVFConstantInt); - CASE(SVFConstFP, SVFConstantFP); - CASE(SVFNullPtr, SVFConstantNullPtr); - CASE(SVFBlackHole, SVFBlackHoleValue); - CASE(SVFMetaAsValue, SVFMetadataAsValue); - CASE(SVFOther, SVFOtherValue); -#undef CASE - } -} - -void SVFIRReader::fill(const cJSON*& fieldJson, SVFValue* value) -{ - // kind, type, name have already been read. - JSON_READ_FIELD_FWD(fieldJson, value, ptrInUncalledFun); - JSON_READ_FIELD_FWD(fieldJson, value, constDataOrAggData); - JSON_READ_FIELD_FWD(fieldJson, value, sourceLoc); -} - -void SVFIRReader::fill(const cJSON*& fieldJson, SVFFunction* value) -{ - fill(fieldJson, static_cast(value)); -#define F(f) JSON_READ_FIELD_FWD(fieldJson, value, f) - F(isDecl); - F(intrinsic); - F(addrTaken); - F(isUncalled); - F(isNotRet); - F(varArg); - F(funcType); - F(loopAndDom); - F(realDefFun); - F(allBBs); - F(allArgs); - F(annotations); -#undef F -} - -void SVFIRReader::fill(const cJSON*& fieldJson, SVFBasicBlock* value) -{ - fill(fieldJson, static_cast(value)); - JSON_READ_FIELD_FWD(fieldJson, value, allInsts); - JSON_READ_FIELD_FWD(fieldJson, value, succBBs); - JSON_READ_FIELD_FWD(fieldJson, value, predBBs); - JSON_READ_FIELD_FWD(fieldJson, value, fun); -} - -void SVFIRReader::fill(const cJSON*& fieldJson, SVFInstruction* value) -{ - fill(fieldJson, static_cast(value)); - JSON_READ_FIELD_FWD(fieldJson, value, bb); - JSON_READ_FIELD_FWD(fieldJson, value, terminator); - JSON_READ_FIELD_FWD(fieldJson, value, ret); - JSON_READ_FIELD_FWD(fieldJson, value, succInsts); - JSON_READ_FIELD_FWD(fieldJson, value, predInsts); -} - -void SVFIRReader::fill(const cJSON*& fieldJson, SVFCallInst* value) -{ - fill(fieldJson, static_cast(value)); - JSON_READ_FIELD_FWD(fieldJson, value, args); - JSON_READ_FIELD_FWD(fieldJson, value, varArg); - JSON_READ_FIELD_FWD(fieldJson, value, calledVal); -} - -void SVFIRReader::fill(const cJSON*& fieldJson, SVFVirtualCallInst* value) -{ - fill(fieldJson, static_cast(value)); - JSON_READ_FIELD_FWD(fieldJson, value, vCallVtblPtr); - JSON_READ_FIELD_FWD(fieldJson, value, virtualFunIdx); - JSON_READ_FIELD_FWD(fieldJson, value, funNameOfVcall); -} - -void SVFIRReader::fill(const cJSON*& fieldJson, SVFConstant* value) -{ - fill(fieldJson, static_cast(value)); -} - -void SVFIRReader::fill(const cJSON*& fieldJson, SVFGlobalValue* value) -{ - fill(fieldJson, static_cast(value)); - JSON_READ_FIELD_FWD(fieldJson, value, realDefGlobal); -} - -void SVFIRReader::fill(const cJSON*& fieldJson, SVFArgument* value) -{ - fill(fieldJson, static_cast(value)); - JSON_READ_FIELD_FWD(fieldJson, value, fun); - JSON_READ_FIELD_FWD(fieldJson, value, argNo); - JSON_READ_FIELD_FWD(fieldJson, value, uncalled); -} - -void SVFIRReader::fill(const cJSON*& fieldJson, SVFConstantData* value) -{ - fill(fieldJson, static_cast(value)); -} - -void SVFIRReader::fill(const cJSON*& fieldJson, SVFConstantInt* value) -{ - fill(fieldJson, static_cast(value)); - JSON_READ_FIELD_FWD(fieldJson, value, zval); - JSON_READ_FIELD_FWD(fieldJson, value, sval); -} - -void SVFIRReader::fill(const cJSON*& fieldJson, SVFConstantFP* value) -{ - fill(fieldJson, static_cast(value)); - JSON_READ_FIELD_FWD(fieldJson, value, dval); -} - -void SVFIRReader::fill(const cJSON*& fieldJson, SVFConstantNullPtr* value) -{ - fill(fieldJson, static_cast(value)); -} - -void SVFIRReader::fill(const cJSON*& fieldJson, SVFBlackHoleValue* value) -{ - fill(fieldJson, static_cast(value)); -} - -void SVFIRReader::fill(const cJSON*& fieldJson, SVFOtherValue* value) -{ - fill(fieldJson, static_cast(value)); -} - -void SVFIRReader::fill(const cJSON*& fieldJson, SVFMetadataAsValue* value) -{ - fill(fieldJson, static_cast(value)); -} - -void SVFIRReader::virtFill(const cJSON*& fieldJson, SVFType* type) -{ - auto kind = type->getKind(); - - switch (kind) - { - default: - assert(false && "Impossible SVFType kind"); - -#define CASE(Kind) \ - case SVFType::Kind: \ - return fill(fieldJson, SVFUtil::dyn_cast(type)) - - CASE(SVFTy); - CASE(SVFPointerTy); - CASE(SVFIntegerTy); - CASE(SVFFunctionTy); - CASE(SVFStructTy); - CASE(SVFArrayTy); - CASE(SVFOtherTy); -#undef CASE - } -} - -void SVFIRReader::fill(const cJSON*& fieldJson, SVFType* type) -{ - // kind has already been read - JSON_READ_FIELD_FWD(fieldJson, type, typeinfo); -} - -void SVFIRReader::fill(const cJSON*& fieldJson, SVFPointerType* type) -{ - fill(fieldJson, static_cast(type)); -} - -void SVFIRReader::fill(const cJSON*& fieldJson, SVFIntegerType* type) -{ - fill(fieldJson, static_cast(type)); - JSON_READ_FIELD_FWD(fieldJson, type, signAndWidth); -} - -void SVFIRReader::fill(const cJSON*& fieldJson, SVFFunctionType* type) -{ - fill(fieldJson, static_cast(type)); - JSON_READ_FIELD_FWD(fieldJson, type, retTy); -} - -void SVFIRReader::fill(const cJSON*& fieldJson, SVFStructType* type) -{ - fill(fieldJson, static_cast(type)); - JSON_READ_FIELD_FWD(fieldJson, type, name); -} - -void SVFIRReader::fill(const cJSON*& fieldJson, SVFArrayType* type) -{ - fill(fieldJson, static_cast(type)); - JSON_READ_FIELD_FWD(fieldJson, type, numOfElement); - JSON_READ_FIELD_FWD(fieldJson, type, typeOfElement); -} - -void SVFIRReader::fill(const cJSON*& fieldJson, SVFOtherType* type) -{ - fill(fieldJson, static_cast(type)); - JSON_READ_FIELD_FWD(fieldJson, type, repr); -} - -SVFIR* SVFIRReader::read(const std::string& path) -{ - struct stat buf; - int fd = open(path.c_str(), O_RDONLY); - if (fd == -1) - { - std::string info = "open(\"" + path + "\")"; - perror(info.c_str()); - abort(); - } - if (fstat(fd, &buf) == -1) - { - std::string info = "fstate(\"" + path + "\")"; - perror(info.c_str()); - abort(); - } - auto addr = - (char*)mmap(nullptr, buf.st_size, PROT_READ, MAP_PRIVATE, fd, 0); - if (addr == MAP_FAILED) - { - std::string info = "mmap(content of \"" + path + "\")"; - perror(info.c_str()); - abort(); - } - - auto root = cJSON_ParseWithLength(addr, buf.st_size); - - if (munmap(addr, buf.st_size) == -1) - perror("munmap()"); - - if (close(fd) < 0) - perror("close()"); - - SVFIRReader reader; - SVFIR* ir = reader.read(root); - - cJSON_Delete(root); - return ir; -} - -} // namespace SVF diff --git a/svf/lib/SVFIR/SVFIR.cpp b/svf/lib/SVFIR/SVFIR.cpp deleted file mode 100644 index b9e289620..000000000 --- a/svf/lib/SVFIR/SVFIR.cpp +++ /dev/null @@ -1,692 +0,0 @@ -//===- SVFIR.cpp -- IR of SVF ---------------------------------------------// -// -// SVF: Static Value-Flow Analysis -// -// Copyright (C) <2013-> -// - -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU Affero General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Affero General Public License for more details. - -// You should have received a copy of the GNU Affero General Public License -// along with this program. If not, see . -// -//===----------------------------------------------------------------------===// - -/* - * SVFIR.cpp - * - * Created on: 31, 12, 2021 - * Author: Yulei Sui - */ - -#include "Util/Options.h" -#include "SVFIR/SVFIR.h" - -using namespace SVF; -using namespace SVFUtil; - - -std::unique_ptr SVFIR::pag; - -SVFIR::SVFIR(bool buildFromFile) : IRGraph(buildFromFile), svfModule(nullptr), icfg(nullptr), chgraph(nullptr) -{ -} - -/*! - * Add Address edge - */ -AddrStmt* SVFIR::addAddrStmt(NodeID src, NodeID dst) -{ - SVFVar* srcNode = getGNode(src); - SVFVar* dstNode = getGNode(dst); - if(hasNonlabeledEdge(srcNode,dstNode, SVFStmt::Addr)) - return nullptr; - else - { - AddrStmt* addrPE = new AddrStmt(srcNode, dstNode); - addToStmt2TypeMap(addrPE); - addEdge(srcNode,dstNode, addrPE); - return addrPE; - } -} - -/*! - * Add Copy edge - */ -CopyStmt* SVFIR::addCopyStmt(NodeID src, NodeID dst, CopyStmt::CopyKind type) -{ - SVFVar* srcNode = getGNode(src); - SVFVar* dstNode = getGNode(dst); - if(hasNonlabeledEdge(srcNode,dstNode, SVFStmt::Copy)) - return nullptr; - else - { - CopyStmt* copyPE = new CopyStmt(srcNode, dstNode, type); - addToStmt2TypeMap(copyPE); - addEdge(srcNode,dstNode, copyPE); - return copyPE; - } -} - -/*! - * Add Phi statement - */ -PhiStmt* SVFIR::addPhiStmt(NodeID res, NodeID opnd, const ICFGNode* pred) -{ - SVFVar* opNode = getGNode(opnd); - SVFVar* resNode = getGNode(res); - PHINodeMap::iterator it = phiNodeMap.find(resNode); - if(it == phiNodeMap.end()) - { - PhiStmt* phi = new PhiStmt(resNode, {opNode}, {pred}); - addToStmt2TypeMap(phi); - addEdge(opNode, resNode, phi); - phiNodeMap[resNode] = phi; - return phi; - } - else - { - it->second->addOpVar(opNode,pred); - /// return null if we already added this PhiStmt - return nullptr; - } -} - -/*! - * Add Phi statement - */ -SelectStmt* SVFIR::addSelectStmt(NodeID res, NodeID op1, NodeID op2, NodeID cond) -{ - SVFVar* op1Node = getGNode(op1); - SVFVar* op2Node = getGNode(op2); - SVFVar* dstNode = getGNode(res); - SVFVar* condNode = getGNode(cond); - if(hasLabeledEdge(op1Node, dstNode, SVFStmt::Select, op2Node)) - return nullptr; - else - { - std::vector opnds = {op1Node, op2Node}; - SelectStmt* select = new SelectStmt(dstNode, opnds, condNode); - addToStmt2TypeMap(select); - addEdge(op1Node, dstNode, select); - return select; - } -} - -/*! - * Add Compare edge - */ -CmpStmt* SVFIR::addCmpStmt(NodeID op1, NodeID op2, NodeID dst, u32_t predicate) -{ - SVFVar* op1Node = getGNode(op1); - SVFVar* op2Node = getGNode(op2); - SVFVar* dstNode = getGNode(dst); - if(hasLabeledEdge(op1Node, dstNode, SVFStmt::Cmp, op2Node)) - return nullptr; - else - { - std::vector opnds = {op1Node, op2Node}; - CmpStmt* cmp = new CmpStmt(dstNode, opnds, predicate); - addToStmt2TypeMap(cmp); - addEdge(op1Node, dstNode, cmp); - return cmp; - } -} - - -/*! - * Add Compare edge - */ -BinaryOPStmt* SVFIR::addBinaryOPStmt(NodeID op1, NodeID op2, NodeID dst, u32_t opcode) -{ - SVFVar* op1Node = getGNode(op1); - SVFVar* op2Node = getGNode(op2); - SVFVar* dstNode = getGNode(dst); - if(hasLabeledEdge(op1Node, dstNode, SVFStmt::BinaryOp, op2Node)) - return nullptr; - else - { - std::vector opnds = {op1Node, op2Node}; - BinaryOPStmt* binaryOP = new BinaryOPStmt(dstNode, opnds, opcode); - addToStmt2TypeMap(binaryOP); - addEdge(op1Node,dstNode, binaryOP); - return binaryOP; - } -} - -/*! - * Add Unary edge - */ -UnaryOPStmt* SVFIR::addUnaryOPStmt(NodeID src, NodeID dst, u32_t opcode) -{ - SVFVar* srcNode = getGNode(src); - SVFVar* dstNode = getGNode(dst); - if(hasNonlabeledEdge(srcNode,dstNode, SVFStmt::UnaryOp)) - return nullptr; - else - { - UnaryOPStmt* unaryOP = new UnaryOPStmt(srcNode, dstNode, opcode); - addToStmt2TypeMap(unaryOP); - addEdge(srcNode,dstNode, unaryOP); - return unaryOP; - } -} - -/* -* Add BranchStmt -*/ -BranchStmt* SVFIR::addBranchStmt(NodeID br, NodeID cond, const BranchStmt::SuccAndCondPairVec& succs) -{ - SVFVar* brNode = getGNode(br); - SVFVar* condNode = getGNode(cond); - if(hasNonlabeledEdge(condNode,brNode, SVFStmt::Branch)) - return nullptr; - else - { - BranchStmt* branch = new BranchStmt(brNode, condNode, succs); - addToStmt2TypeMap(branch); - addEdge(condNode,brNode, branch); - return branch; - } -} - -/*! - * Add Load edge - */ -LoadStmt* SVFIR::addLoadStmt(NodeID src, NodeID dst) -{ - SVFVar* srcNode = getGNode(src); - SVFVar* dstNode = getGNode(dst); - if(hasNonlabeledEdge(srcNode,dstNode, SVFStmt::Load)) - return nullptr; - else - { - LoadStmt* loadPE = new LoadStmt(srcNode, dstNode); - addToStmt2TypeMap(loadPE); - addEdge(srcNode,dstNode, loadPE); - return loadPE; - } -} - -/*! - * Add Store edge - * Note that two store instructions may share the same Store SVFStmt - */ -StoreStmt* SVFIR::addStoreStmt(NodeID src, NodeID dst, const IntraICFGNode* curVal) -{ - SVFVar* srcNode = getGNode(src); - SVFVar* dstNode = getGNode(dst); - if(hasLabeledEdge(srcNode,dstNode, SVFStmt::Store, curVal)) - return nullptr; - else - { - StoreStmt* storePE = new StoreStmt(srcNode, dstNode, curVal); - addToStmt2TypeMap(storePE); - addEdge(srcNode,dstNode, storePE); - return storePE; - } -} - -/*! - * Add Call edge - */ -CallPE* SVFIR::addCallPE(NodeID src, NodeID dst, const CallICFGNode* cs, const FunEntryICFGNode* entry) -{ - SVFVar* srcNode = getGNode(src); - SVFVar* dstNode = getGNode(dst); - if(hasLabeledEdge(srcNode,dstNode, SVFStmt::Call, cs)) - return nullptr; - else - { - CallPE* callPE = new CallPE(srcNode, dstNode, cs,entry); - addToStmt2TypeMap(callPE); - addEdge(srcNode,dstNode, callPE); - return callPE; - } -} - -/*! - * Add Return edge - */ -RetPE* SVFIR::addRetPE(NodeID src, NodeID dst, const CallICFGNode* cs, const FunExitICFGNode* exit) -{ - SVFVar* srcNode = getGNode(src); - SVFVar* dstNode = getGNode(dst); - if(hasLabeledEdge(srcNode,dstNode, SVFStmt::Ret, cs)) - return nullptr; - else - { - RetPE* retPE = new RetPE(srcNode, dstNode, cs, exit); - addToStmt2TypeMap(retPE); - addEdge(srcNode,dstNode, retPE); - return retPE; - } -} - -/*! - * Add blackhole/constant edge - */ -SVFStmt* SVFIR::addBlackHoleAddrStmt(NodeID node) -{ - if(Options::HandBlackHole()) - return pag->addAddrStmt(pag->getBlackHoleNode(), node); - else - return pag->addCopyStmt(pag->getNullPtr(), node, CopyStmt::COPYVAL); -} - -/*! - * Add Thread fork edge for parameter passing from a spawner to its spawnees - */ -TDForkPE* SVFIR::addThreadForkPE(NodeID src, NodeID dst, const CallICFGNode* cs, const FunEntryICFGNode* entry) -{ - SVFVar* srcNode = getGNode(src); - SVFVar* dstNode = getGNode(dst); - if(hasLabeledEdge(srcNode,dstNode, SVFStmt::ThreadFork, cs)) - return nullptr; - else - { - TDForkPE* forkPE = new TDForkPE(srcNode, dstNode, cs, entry); - addToStmt2TypeMap(forkPE); - addEdge(srcNode,dstNode, forkPE); - return forkPE; - } -} - -/*! - * Add Thread fork edge for parameter passing from a spawnee back to its spawners - */ -TDJoinPE* SVFIR::addThreadJoinPE(NodeID src, NodeID dst, const CallICFGNode* cs, const FunExitICFGNode* exit) -{ - SVFVar* srcNode = getGNode(src); - SVFVar* dstNode = getGNode(dst); - if(hasLabeledEdge(srcNode,dstNode, SVFStmt::ThreadJoin, cs)) - return nullptr; - else - { - TDJoinPE* joinPE = new TDJoinPE(srcNode, dstNode, cs, exit); - addToStmt2TypeMap(joinPE); - addEdge(srcNode,dstNode, joinPE); - return joinPE; - } -} - - -/*! - * Add Offset(Gep) edge - * Find the base node id of src and connect base node to dst node - * Create gep offset: (offset + baseOff ) - */ -GepStmt* SVFIR::addGepStmt(NodeID src, NodeID dst, const AccessPath& ap, bool constGep) -{ - - SVFVar* node = getGNode(src); - if (!constGep || node->hasIncomingVariantGepEdge()) - { - /// Since the offset from base to src is variant, - /// the new gep edge being created is also a Variant GepStmt edge. - return addVariantGepStmt(src, dst, ap); - } - else - { - return addNormalGepStmt(src, dst, ap); - } -} - -/*! - * Add normal (Gep) edge - */ -GepStmt* SVFIR::addNormalGepStmt(NodeID src, NodeID dst, const AccessPath& ap) -{ - SVFVar* baseNode = getGNode(src); - SVFVar* dstNode = getGNode(dst); - if(hasNonlabeledEdge(baseNode, dstNode, SVFStmt::Gep)) - return nullptr; - else - { - GepStmt* gepPE = new GepStmt(baseNode, dstNode, ap); - addToStmt2TypeMap(gepPE); - addEdge(baseNode, dstNode, gepPE); - return gepPE; - } -} - -/*! - * Add variant(Gep) edge - * Find the base node id of src and connect base node to dst node - */ -GepStmt* SVFIR::addVariantGepStmt(NodeID src, NodeID dst, const AccessPath& ap) -{ - SVFVar* baseNode = getGNode(src); - SVFVar* dstNode = getGNode(dst); - if(hasNonlabeledEdge(baseNode, dstNode, SVFStmt::Gep)) - return nullptr; - else - { - GepStmt* gepPE = new GepStmt(baseNode, dstNode, ap, true); - addToStmt2TypeMap(gepPE); - addEdge(baseNode, dstNode, gepPE); - return gepPE; - } -} - - - -/*! - * Add a temp field value node, this method can only invoked by getGepValVar - * due to constraint expression, curInst is used to distinguish different instructions (e.g., memorycpy) when creating GepValVar. - */ -NodeID SVFIR::addGepValNode(const SVFValue* curInst,const SVFValue* gepVal, const AccessPath& ap, NodeID i, const SVFType* type) -{ - NodeID base = getValueNode(gepVal); - //assert(findPAGNode(i) == false && "this node should not be created before"); - assert(0==GepValObjMap[curInst].count(std::make_pair(base, ap)) - && "this node should not be created before"); - GepValObjMap[curInst][std::make_pair(base, ap)] = i; - GepValVar *node = new GepValVar(gepVal, i, ap, type); - return addValNode(gepVal, node, i); -} - -/*! - * Given an object node, find its field object node - */ -NodeID SVFIR::getGepObjVar(NodeID id, const APOffset& apOffset) -{ - SVFVar* node = pag->getGNode(id); - if (GepObjVar* gepNode = SVFUtil::dyn_cast(node)) - return getGepObjVar(gepNode->getMemObj(), gepNode->getConstantFieldIdx() + apOffset); - else if (FIObjVar* baseNode = SVFUtil::dyn_cast(node)) - return getGepObjVar(baseNode->getMemObj(), apOffset); - else if (DummyObjVar* baseNode = SVFUtil::dyn_cast(node)) - return getGepObjVar(baseNode->getMemObj(), apOffset); - else - { - assert(false && "new gep obj node kind?"); - return id; - } -} - -/*! - * Get a field obj SVFIR node according to base mem obj and offset - * To support flexible field sensitive analysis with regard to MaxFieldOffset - * offset = offset % obj->getMaxFieldOffsetLimit() to create limited number of mem objects - * maximum number of field object creation is obj->getMaxFieldOffsetLimit() - */ -NodeID SVFIR::getGepObjVar(const MemObj* obj, const APOffset& apOffset) -{ - NodeID base = obj->getId(); - - /// if this obj is field-insensitive, just return the field-insensitive node. - if (obj->isFieldInsensitive()) - return getFIObjVar(obj); - - APOffset newLS = pag->getSymbolInfo()->getModulusOffset(obj, apOffset); - - // Base and first field are the same memory location. - if (Options::FirstFieldEqBase() && newLS == 0) return base; - - NodeOffsetMap::iterator iter = GepObjVarMap.find(std::make_pair(base, newLS)); - if (iter == GepObjVarMap.end()) - { - NodeID gepId = NodeIDAllocator::get()->allocateGepObjectId(base, apOffset, Options::MaxFieldLimit()); - return addGepObjNode(obj, newLS,gepId); - } - else - return iter->second; - -} - -/*! - * Add a field obj node, this method can only invoked by getGepObjVar - */ -NodeID SVFIR::addGepObjNode(const MemObj* obj, const APOffset& apOffset, const NodeID gepId) -{ - //assert(findPAGNode(i) == false && "this node should not be created before"); - NodeID base = obj->getId(); - assert(0==GepObjVarMap.count(std::make_pair(base, apOffset)) - && "this node should not be created before"); - - GepObjVarMap[std::make_pair(base, apOffset)] = gepId; - GepObjVar *node = new GepObjVar(obj, gepId, apOffset); - memToFieldsMap[base].set(gepId); - return addObjNode(obj->getValue(), node, gepId); -} - -/*! - * Add a field-insensitive node, this method can only invoked by getFIGepObjNode - */ -NodeID SVFIR::addFIObjNode(const MemObj* obj) -{ - //assert(findPAGNode(i) == false && "this node should not be created before"); - NodeID base = obj->getId(); - memToFieldsMap[base].set(obj->getId()); - FIObjVar *node = new FIObjVar(obj->getValue(), obj->getId(), obj); - return addObjNode(obj->getValue(), node, obj->getId()); -} - -/*! - * Get all fields object nodes of an object - */ -NodeBS& SVFIR::getAllFieldsObjVars(const MemObj* obj) -{ - NodeID base = obj->getId(); - return memToFieldsMap[base]; -} - -/*! - * Get all fields object nodes of an object - */ -NodeBS& SVFIR::getAllFieldsObjVars(NodeID id) -{ - const SVFVar* node = pag->getGNode(id); - assert(SVFUtil::isa(node) && "need an object node"); - const ObjVar* obj = SVFUtil::cast(node); - return getAllFieldsObjVars(obj->getMemObj()); -} - -/*! - * Get all fields object nodes of an object - * If this object is collapsed into one field insensitive object - * Then only return this field insensitive object - */ -NodeBS SVFIR::getFieldsAfterCollapse(NodeID id) -{ - const SVFVar* node = pag->getGNode(id); - assert(SVFUtil::isa(node) && "need an object node"); - const MemObj* mem = SVFUtil::cast(node)->getMemObj(); - if(mem->isFieldInsensitive()) - { - NodeBS bs; - bs.set(getFIObjVar(mem)); - return bs; - } - else - return getAllFieldsObjVars(mem); -} - -/*! - * It is used to create a dummy GepValVar during global initialization. - */ -NodeID SVFIR::getGepValVar(const SVFValue* curInst, NodeID base, const AccessPath& ap) const -{ - GepValueVarMap::const_iterator iter = GepValObjMap.find(curInst); - if(iter==GepValObjMap.end()) - { - return UINT_MAX; - } - else - { - NodeAccessPathMap::const_iterator lit = - iter->second.find(std::make_pair(base, ap)); - if (lit == iter->second.end()) - return UINT_MAX; - else - return lit->second; - } -} - - -/*! - * Clean up memory - */ -void SVFIR::destroy() -{ - delete icfg; - icfg = nullptr; - delete chgraph; - chgraph = nullptr; - SVFModule::releaseSVFModule(); - svfModule = nullptr; -} - -/*! - * Print this SVFIR graph including its nodes and edges - */ -void SVFIR::print() -{ - - outs() << "-------------------SVFIR------------------------------------\n"; - SVFStmt::SVFStmtSetTy& addrs = pag->getSVFStmtSet(SVFStmt::Addr); - for (SVFStmt::SVFStmtSetTy::iterator iter = addrs.begin(), eiter = - addrs.end(); iter != eiter; ++iter) - { - outs() << (*iter)->getSrcID() << " -- Addr --> " << (*iter)->getDstID() - << "\n"; - } - - SVFStmt::SVFStmtSetTy& copys = pag->getSVFStmtSet(SVFStmt::Copy); - for (SVFStmt::SVFStmtSetTy::iterator iter = copys.begin(), eiter = - copys.end(); iter != eiter; ++iter) - { - outs() << (*iter)->getSrcID() << " -- Copy --> " << (*iter)->getDstID() - << "\n"; - } - - SVFStmt::SVFStmtSetTy& calls = pag->getSVFStmtSet(SVFStmt::Call); - for (SVFStmt::SVFStmtSetTy::iterator iter = calls.begin(), eiter = - calls.end(); iter != eiter; ++iter) - { - outs() << (*iter)->getSrcID() << " -- Call --> " << (*iter)->getDstID() - << "\n"; - } - - SVFStmt::SVFStmtSetTy& rets = pag->getSVFStmtSet(SVFStmt::Ret); - for (SVFStmt::SVFStmtSetTy::iterator iter = rets.begin(), eiter = - rets.end(); iter != eiter; ++iter) - { - outs() << (*iter)->getSrcID() << " -- Ret --> " << (*iter)->getDstID() - << "\n"; - } - - SVFStmt::SVFStmtSetTy& tdfks = pag->getSVFStmtSet(SVFStmt::ThreadFork); - for (SVFStmt::SVFStmtSetTy::iterator iter = tdfks.begin(), eiter = - tdfks.end(); iter != eiter; ++iter) - { - outs() << (*iter)->getSrcID() << " -- ThreadFork --> " - << (*iter)->getDstID() << "\n"; - } - - SVFStmt::SVFStmtSetTy& tdjns = pag->getSVFStmtSet(SVFStmt::ThreadJoin); - for (SVFStmt::SVFStmtSetTy::iterator iter = tdjns.begin(), eiter = - tdjns.end(); iter != eiter; ++iter) - { - outs() << (*iter)->getSrcID() << " -- ThreadJoin --> " - << (*iter)->getDstID() << "\n"; - } - - SVFStmt::SVFStmtSetTy& ngeps = pag->getSVFStmtSet(SVFStmt::Gep); - for (SVFStmt::SVFStmtSetTy::iterator iter = ngeps.begin(), eiter = - ngeps.end(); iter != eiter; ++iter) - { - GepStmt* gep = SVFUtil::cast(*iter); - if(gep->isVariantFieldGep()) - outs() << (*iter)->getSrcID() << " -- VariantGep --> " - << (*iter)->getDstID() << "\n"; - else - outs() << gep->getRHSVarID() << " -- Gep (" << gep->getConstantStructFldIdx() - << ") --> " << gep->getLHSVarID() << "\n"; - } - - SVFStmt::SVFStmtSetTy& loads = pag->getSVFStmtSet(SVFStmt::Load); - for (SVFStmt::SVFStmtSetTy::iterator iter = loads.begin(), eiter = - loads.end(); iter != eiter; ++iter) - { - outs() << (*iter)->getSrcID() << " -- Load --> " << (*iter)->getDstID() - << "\n"; - } - - SVFStmt::SVFStmtSetTy& stores = pag->getSVFStmtSet(SVFStmt::Store); - for (SVFStmt::SVFStmtSetTy::iterator iter = stores.begin(), eiter = - stores.end(); iter != eiter; ++iter) - { - outs() << (*iter)->getSrcID() << " -- Store --> " << (*iter)->getDstID() - << "\n"; - } - outs() << "----------------------------------------------------------\n"; - -} - -/// Initialize candidate pointers -void SVFIR::initialiseCandidatePointers() -{ - // collect candidate pointers for demand-driven analysis - for (iterator nIter = begin(); nIter != end(); ++nIter) - { - NodeID nodeId = nIter->first; - // do not compute points-to for isolated node - if (isValidPointer(nodeId) == false) - continue; - candidatePointers.insert(nodeId); - } -} -/* - * If this is a dummy node or node does not have incoming edges and outgoing edges we assume it is not a pointer here. - * However, if it is a pointer and it is an argument of a function definition, we assume it is a pointer here. - */ -bool SVFIR::isValidPointer(NodeID nodeId) const -{ - SVFVar* node = pag->getGNode(nodeId); - - if (node->hasValue() && node->isPointer()) - { - if(const SVFArgument* arg = SVFUtil::dyn_cast(node->getValue())) - { - if (!(arg->getParent()->isDeclaration())) - return true; - } - } - - if ((node->getInEdges().empty() && node->getOutEdges().empty())) - return false; - return node->isPointer(); -} - -bool SVFIR::isValidTopLevelPtr(const SVFVar* node) -{ - if (SVFUtil::isa(node)) - { - if (isValidPointer(node->getId()) && node->hasValue()) - { - return !SVFUtil::isArgOfUncalledFunction(node->getValue()); - } - } - return false; -} - -/*! - * Whether to handle blackhole edge - */ -void SVFIR::handleBlackHole(bool b) -{ - Options::HandBlackHole.setValue(b); -} - - - diff --git a/svf/lib/SVFIR/SVFModule.cpp b/svf/lib/SVFIR/SVFModule.cpp deleted file mode 100644 index 9c4da6128..000000000 --- a/svf/lib/SVFIR/SVFModule.cpp +++ /dev/null @@ -1,74 +0,0 @@ -//===- SVFModule.cpp -- Helper functions for pointer analysis--------------// -// -// SVF: Static Value-Flow Analysis -// -// Copyright (C) <2013-2017> -// - -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU Affero General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Affero General Public License for more details. - -// You should have received a copy of the GNU Affero General Public License -// along with this program. If not, see . -// -//===----------------------------------------------------------------------===// - - -#include "SVFIR/SVFModule.h" -#include "SVFIR/SymbolTableInfo.h" -#include "Util/SVFUtil.h" -#include "Util/SVFStat.h" -#include "Util/Options.h" - -using namespace SVF; - -std::string SVFModule::pagReadFromTxt = ""; -SVFModule* SVFModule::svfModule = nullptr; - -SVFModule::~SVFModule() -{ - for (const SVFFunction* f : FunctionSet) - delete f; - for (const SVFConstant* c : ConstantSet) - delete c; - for (const SVFValue* o : OtherValueSet) - delete o; - NodeIDAllocator::unset(); - ThreadAPI::destroy(); - ExtAPI::destory(); -} - -const SVFFunction* SVFModule::getSVFFunction(const std::string& name) -{ - for (const SVFFunction* fun : getFunctionSet()) - { - if (fun->getName() == name) - { - return fun; - } - } - return nullptr; -} - -SVFModule* SVFModule::getSVFModule() -{ - if (svfModule == nullptr) - { - svfModule = new SVFModule; - } - return svfModule; -} - -void SVFModule::releaseSVFModule() -{ - assert(svfModule != nullptr && "SVFModule is not initialized?"); - delete svfModule; - svfModule = nullptr; -} diff --git a/svf/lib/SVFIR/SVFStatements.cpp b/svf/lib/SVFIR/SVFStatements.cpp deleted file mode 100644 index 5335bc27a..000000000 --- a/svf/lib/SVFIR/SVFStatements.cpp +++ /dev/null @@ -1,373 +0,0 @@ -//===- SVFStatements.cpp -- SVF program statements----------------------// -// -// SVF: Static Value-Flow Analysis -// -// Copyright (C) <2013-> -// - -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU Affero General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Affero General Public License for more details. - -// You should have received a copy of the GNU Affero General Public License -// along with this program. If not, see . -// -//===----------------------------------------------------------------------===// - -/* - * SVFStatements.cpp - * - * Created on: Oct 11, 2013 - * Author: Yulei Sui - */ - -#include "SVFIR/SVFStatements.h" -#include "SVFIR/SVFIR.h" -#include "Util/Options.h" - -using namespace SVF; -using namespace SVFUtil; - - -u64_t SVFStmt::callEdgeLabelCounter = 0; -u64_t SVFStmt::storeEdgeLabelCounter = 0; -u64_t SVFStmt::multiOpndLabelCounter = 0; -SVFStmt::Inst2LabelMap SVFStmt::inst2LabelMap; -SVFStmt::Var2LabelMap SVFStmt::var2LabelMap; - - -/*! - * SVFStmt constructor - */ -SVFStmt::SVFStmt(SVFVar* s, SVFVar* d, GEdgeFlag k, bool real) : - GenericPAGEdgeTy(s,d,k),value(nullptr),basicBlock(nullptr),icfgNode(nullptr),edgeId(UINT_MAX) -{ - if(real) - { - edgeId = SVFIR::getPAG()->getTotalEdgeNum(); - SVFIR::getPAG()->incEdgeNum(); - } -} - -/*! - * Whether src and dst nodes are both pointer type - */ -bool SVFStmt::isPTAEdge() const -{ - return getSrcNode()->isPointer() && getDstNode()->isPointer(); -} - -const std::string SVFStmt::toString() const -{ - std::string str; - std::stringstream rawstr(str); - rawstr << "SVFStmt: [Var" << getDstID() << " <-- Var" << getSrcID() << "]\t"; - return rawstr.str(); -} - -const std::string AddrStmt::toString() const -{ - std::string str; - std::stringstream rawstr(str); - rawstr << "AddrStmt: [Var" << getLHSVarID() << " <-- Var" << getRHSVarID() << "]\t"; - if (Options::ShowSVFIRValue()) - { - rawstr << "\n"; - rawstr << getValue()->toString(); - } - return rawstr.str(); -} - -const std::string CopyStmt::toString() const -{ - std::string str; - std::stringstream rawstr(str); - rawstr << "CopyStmt: [Var" << getLHSVarID() << " <-- Var" << getRHSVarID() << "]\t"; - if (Options::ShowSVFIRValue()) - { - rawstr << "\n"; - rawstr << getValue()->toString(); - } - return rawstr.str(); -} - -const std::string PhiStmt::toString() const -{ - std::string str; - std::stringstream rawstr(str); - rawstr << "PhiStmt: [Var" << getResID() << " <-- ("; - for(u32_t i = 0; i < getOpVarNum(); i++) - rawstr << "[Var" << getOpVar(i)->getId() << ", ICFGNode" << getOpICFGNode(i)->getId() << "],"; - rawstr << ")]\t"; - if (Options::ShowSVFIRValue()) - { - rawstr << "\n"; - rawstr << getValue()->toString(); - } - return rawstr.str(); -} - -const std::string SelectStmt::toString() const -{ - std::string str; - std::stringstream rawstr(str); - rawstr << "SelectStmt: (Condition Var" << getCondition()->getId() << ") [Var" << getResID() << " <-- (Var"; - for(const SVFVar* op : getOpndVars()) - rawstr << op->getId() << ","; - rawstr << ")]\t"; - if (Options::ShowSVFIRValue()) - { - rawstr << "\n"; - rawstr << getValue()->toString(); - } - return rawstr.str(); -} - -const std::string CmpStmt::toString() const -{ - std::string str; - std::stringstream rawstr(str); - rawstr << "CmpStmt: [Var" << getResID() << " <-- (Var" << getOpVarID(0) << " predicate" << getPredicate() << " Var" << getOpVarID(1) << ")]\t"; - if (Options::ShowSVFIRValue()) - { - rawstr << "\n"; - rawstr << getValue()->toString(); - } - return rawstr.str(); -} - -const std::string BinaryOPStmt::toString() const -{ - std::string str; - std::stringstream rawstr(str); - rawstr << "BinaryOPStmt: [Var" << getResID() << " <-- (Var" << getOpVarID(0) << " opcode" << getOpcode() << " Var" << getOpVarID(1) << ")]\t"; - if (Options::ShowSVFIRValue()) - { - rawstr << "\n"; - rawstr << getValue()->toString(); - } - return rawstr.str(); -} - -const std::string UnaryOPStmt::toString() const -{ - std::string str; - std::stringstream rawstr(str); - rawstr << "UnaryOPStmt: [Var" << getResID() << " <-- " << " opcode" << getOpcode() << " Var" << getOpVarID() << "]\t"; - if (Options::ShowSVFIRValue()) - { - rawstr << "\n"; - rawstr << getValue()->toString(); - } - return rawstr.str(); -} - -const std::string BranchStmt::toString() const -{ - std::string str; - std::stringstream rawstr(str); - if(isConditional()) - rawstr << "BranchStmt: [Condition Var" << getCondition()->getId() << "]\n"; - else - rawstr << "BranchStmt: [" << " Unconditional branch" << "]\n"; - - for(u32_t i = 0; i < getNumSuccessors(); i++) - rawstr << "Successor " << i << " ICFGNode" << getSuccessor(i)->getId() << " "; - - if (Options::ShowSVFIRValue()) - { - rawstr << "\n"; - rawstr << getValue()->toString(); - } - return rawstr.str(); -} - - -const std::string LoadStmt::toString() const -{ - std::string str; - std::stringstream rawstr(str); - rawstr << "LoadStmt: [Var" << getLHSVarID() << " <-- Var" << getRHSVarID() << "]\t"; - if (Options::ShowSVFIRValue()) - { - rawstr << "\n"; - rawstr << getValue()->toString(); - } - return rawstr.str(); -} - -const std::string StoreStmt::toString() const -{ - std::string str; - std::stringstream rawstr(str); - rawstr << "StoreStmt: [Var" << getLHSVarID() << " <-- Var" << getRHSVarID() << "]\t"; - if (Options::ShowSVFIRValue()) - { - rawstr << "\n"; - rawstr << getValue()->toString(); - } - return rawstr.str(); -} - -const std::string GepStmt::toString() const -{ - std::string str; - std::stringstream rawstr(str); - rawstr << "GepStmt: [Var" << getLHSVarID() << " <-- Var" << getRHSVarID() << "]\t"; - if (Options::ShowSVFIRValue()) - { - rawstr << "\n"; - rawstr << getValue()->toString(); - } - return rawstr.str(); -} - -const std::string CallPE::toString() const -{ - std::string str; - std::stringstream rawstr(str); - rawstr << "CallPE: [Var" << getLHSVarID() << " <-- Var" << getRHSVarID() << "]\t"; - if (Options::ShowSVFIRValue()) - { - rawstr << "\n"; - rawstr << getValue()->toString(); - } - return rawstr.str(); -} - -const std::string RetPE::toString() const -{ - std::string str; - std::stringstream rawstr(str); - rawstr << "RetPE: [Var" << getLHSVarID() << " <-- Var" << getRHSVarID() << "]\t"; - if (Options::ShowSVFIRValue()) - { - rawstr << "\n"; - rawstr << getValue()->toString(); - } - return rawstr.str(); -} - -const std::string TDForkPE::toString() const -{ - std::string str; - std::stringstream rawstr(str); - rawstr << "TDForkPE: [Var" << getLHSVarID() << " <-- Var" << getRHSVarID() << "]\t"; - if (Options::ShowSVFIRValue()) - { - rawstr << "\n"; - rawstr << getValue()->toString(); - } - return rawstr.str(); -} - -const std::string TDJoinPE::toString() const -{ - std::string str; - std::stringstream rawstr(str); - rawstr << "TDJoinPE: [Var" << getLHSVarID() << " <-- Var" << getRHSVarID() << "]\t"; - if (Options::ShowSVFIRValue()) - { - rawstr << "\n"; - rawstr << getValue()->toString(); - } - return rawstr.str(); -} - - -NodeID MultiOpndStmt::getOpVarID(u32_t pos) const -{ - return getOpVar(pos)->getId(); -} - -NodeID MultiOpndStmt::getResID() const -{ - return getRes()->getId(); -} - -NodeID UnaryOPStmt::getOpVarID() const -{ - return getOpVar()->getId(); -} -NodeID UnaryOPStmt::getResID() const -{ - return getRes()->getId(); -} - -/// Return true if this is a phi at the function exit -/// to receive one or multiple return values of this function -bool PhiStmt::isFunctionRetPhi() const -{ - return SVFUtil::isa(getRes()); -} - - -/// The branch is unconditional if cond is a null value -bool BranchStmt::isUnconditional() const -{ - return cond->getId() == SymbolTableInfo::SymbolInfo()->nullPtrSymID(); -} -/// The branch is conditional if cond is not a null value -bool BranchStmt::isConditional() const -{ - return cond->getId() != SymbolTableInfo::SymbolInfo()->nullPtrSymID();; -} -/// Return the condition -const SVFVar* BranchStmt::getCondition() const -{ - //assert(isConditional() && "this is a unconditional branch"); - return cond; -} - -StoreStmt::StoreStmt(SVFVar* s, SVFVar* d, const IntraICFGNode* st) - : AssignStmt(s, d, makeEdgeFlagWithStoreInst(SVFStmt::Store, st)) -{ -} - -CallPE::CallPE(SVFVar* s, SVFVar* d, const CallICFGNode* i, - const FunEntryICFGNode* e, GEdgeKind k) - : AssignStmt(s, d, makeEdgeFlagWithCallInst(k, i)), call(i), entry(e) -{ -} - -RetPE::RetPE(SVFVar* s, SVFVar* d, const CallICFGNode* i, - const FunExitICFGNode* e, GEdgeKind k) - : AssignStmt(s, d, makeEdgeFlagWithCallInst(k, i)), call(i), exit(e) -{ -} - -MultiOpndStmt::MultiOpndStmt(SVFVar* r, const OPVars& opnds, GEdgeFlag k) - : SVFStmt(opnds.at(0), r, k), opVars(opnds) -{ -} - -CmpStmt::CmpStmt(SVFVar* s, const OPVars& opnds, u32_t pre) - : MultiOpndStmt(s, opnds, - makeEdgeFlagWithAddionalOpnd(SVFStmt::Cmp, opnds.at(1))), - predicate(pre) -{ - assert(opnds.size() == 2 && "CmpStmt can only have two operands!"); -} - -SelectStmt::SelectStmt(SVFVar* s, const OPVars& opnds, const SVFVar* cond) - : MultiOpndStmt(s, opnds, - makeEdgeFlagWithAddionalOpnd(SVFStmt::Select, opnds.at(1))), - condition(cond) -{ - assert(opnds.size() == 2 && "SelectStmt can only have two operands!"); -} - -BinaryOPStmt::BinaryOPStmt(SVFVar* s, const OPVars& opnds, u32_t oc) - : MultiOpndStmt( - s, opnds, - makeEdgeFlagWithAddionalOpnd(SVFStmt::BinaryOp, opnds.at(1))), - opcode(oc) -{ - assert(opnds.size() == 2 && "BinaryOPStmt can only have two operands!"); -} diff --git a/svf/lib/SVFIR/SVFType.cpp b/svf/lib/SVFIR/SVFType.cpp deleted file mode 100644 index f262b4764..000000000 --- a/svf/lib/SVFIR/SVFType.cpp +++ /dev/null @@ -1,57 +0,0 @@ -#include "SVFIR/SVFType.h" -#include - -namespace SVF -{ - -SVFType* SVFType::svfI8Ty = nullptr; -SVFType* SVFType::svfPtrTy = nullptr; - -__attribute__((weak)) -std::string SVFType::toString() const -{ - std::ostringstream os; - print(os); - return os.str(); -} - -std::ostream& operator<<(std::ostream& os, const SVFType& type) -{ - type.print(os); - return os; -} - -void SVFPointerType::print(std::ostream& os) const -{ - os << "ptr"; -} - -void SVFIntegerType::print(std::ostream& os) const -{ - if (signAndWidth < 0) - os << 'i' << -signAndWidth; - else - os << 'u' << signAndWidth; -} - -void SVFFunctionType::print(std::ostream& os) const -{ - os << *getReturnType() << "()"; -} - -void SVFStructType::print(std::ostream& os) const -{ - os << "S." << name; -} - -void SVFArrayType::print(std::ostream& os) const -{ - os << '[' << numOfElement << 'x' << *typeOfElement << ']'; -} - -void SVFOtherType::print(std::ostream& os) const -{ - os << repr; -} - -} // namespace SVF diff --git a/svf/lib/SVFIR/SVFValue.cpp b/svf/lib/SVFIR/SVFValue.cpp deleted file mode 100644 index a0a9082b0..000000000 --- a/svf/lib/SVFIR/SVFValue.cpp +++ /dev/null @@ -1,285 +0,0 @@ -#include "SVFIR/SVFValue.h" -#include "Util/SVFUtil.h" - -using namespace SVF; -using namespace SVFUtil; - -__attribute__((weak)) -std::string SVFValue::toString() const -{ - assert("SVFValue::toString should be implemented or supported by fronted" && false); - abort(); -} - -/// Add field (index and offset) with its corresponding type -void StInfo::addFldWithType(u32_t fldIdx, const SVFType* type, u32_t elemIdx) -{ - fldIdxVec.push_back(fldIdx); - elemIdxVec.push_back(elemIdx); - fldIdx2TypeMap[fldIdx] = type; -} - -/// struct A { int id; int salary; }; struct B { char name[20]; struct A a;} B b; -/// OriginalFieldType of b with field_idx 1 : Struct A -/// FlatternedFieldType of b with field_idx 1 : int -//{@ -const SVFType* StInfo::getOriginalElemType(u32_t fldIdx) const -{ - Map::const_iterator it = fldIdx2TypeMap.find(fldIdx); - if(it!=fldIdx2TypeMap.end()) - return it->second; - return nullptr; -} - -const SVFLoopAndDomInfo::LoopBBs& SVFLoopAndDomInfo::getLoopInfo(const SVFBasicBlock* bb) const -{ - assert(hasLoopInfo(bb) && "loopinfo does not exist (bb not in a loop)"); - Map::const_iterator mapIter = bb2LoopMap.find(bb); - return mapIter->second; -} - -void SVFLoopAndDomInfo::getExitBlocksOfLoop(const SVFBasicBlock* bb, BBList& exitbbs) const -{ - if (hasLoopInfo(bb)) - { - const LoopBBs blocks = getLoopInfo(bb); - if (!blocks.empty()) - { - for (const SVFBasicBlock* block : blocks) - { - for (const SVFBasicBlock* succ : block->getSuccessors()) - { - if ((std::find(blocks.begin(), blocks.end(), succ)==blocks.end())) - exitbbs.push_back(succ); - } - } - } - } -} - -bool SVFLoopAndDomInfo::dominate(const SVFBasicBlock* bbKey, const SVFBasicBlock* bbValue) const -{ - if (bbKey == bbValue) - return true; - - // An unreachable node is dominated by anything. - if (isUnreachable(bbValue)) - { - return true; - } - - // And dominates nothing. - if (isUnreachable(bbKey)) - { - return false; - } - - const Map& dtBBsMap = getDomTreeMap(); - Map::const_iterator mapIter = dtBBsMap.find(bbKey); - if (mapIter != dtBBsMap.end()) - { - const BBSet & dtBBs = mapIter->second; - if (dtBBs.find(bbValue) != dtBBs.end()) - { - return true; - } - } - - return false; -} - -bool SVFLoopAndDomInfo::postDominate(const SVFBasicBlock* bbKey, const SVFBasicBlock* bbValue) const -{ - if (bbKey == bbValue) - return true; - - // An unreachable node is dominated by anything. - if (isUnreachable(bbValue)) - { - return true; - } - - // And dominates nothing. - if (isUnreachable(bbKey)) - { - return false; - } - - const Map& dtBBsMap = getPostDomTreeMap(); - Map::const_iterator mapIter = dtBBsMap.find(bbKey); - if (mapIter != dtBBsMap.end()) - { - const BBSet & dtBBs = mapIter->second; - if (dtBBs.find(bbValue) != dtBBs.end()) - { - return true; - } - } - return false; -} - -const SVFBasicBlock* SVFLoopAndDomInfo::findNearestCommonPDominator(const SVFBasicBlock* A, const SVFBasicBlock* B) const -{ - assert(A && B && "Pointers are not valid"); - assert(A->getParent() == B->getParent() && - "Two blocks are not in same function"); - - // Use level information to go up the tree until the levels match. Then - // continue going up til we arrive at the same node. - while (A != B) - { - // no common PDominator - if(A == NULL) return NULL; - const auto lvA = getBBPDomLevel().find(A); - const auto lvB = getBBPDomLevel().find(B); - assert(lvA != getBBPDomLevel().end() && lvB != getBBPDomLevel().end()); - - if (lvA->second < lvB->second) std::swap(A, B); - - const auto lvAIdom = getBB2PIdom().find(A); - assert(lvAIdom != getBB2PIdom().end()); - A = lvAIdom->second; - } - - return A; -} - -bool SVFLoopAndDomInfo::isLoopHeader(const SVFBasicBlock* bb) const -{ - if (hasLoopInfo(bb)) - { - const LoopBBs& blocks = getLoopInfo(bb); - assert(!blocks.empty() && "no available loop info?"); - return blocks.front() == bb; - } - return false; -} - -SVFFunction::SVFFunction(const SVFType* ty, const SVFFunctionType* ft, - bool declare, bool intrinsic, bool adt, bool varg, - SVFLoopAndDomInfo* ld) - : SVFValue(ty, SVFValue::SVFFunc), isDecl(declare), intrinsic(intrinsic), - addrTaken(adt), isUncalled(false), isNotRet(false), varArg(varg), - funcType(ft), loopAndDom(ld), realDefFun(nullptr), exitBlock(nullptr) -{ -} - -SVFFunction::~SVFFunction() -{ - for(const SVFBasicBlock* bb : allBBs) - delete bb; - for(const SVFArgument* arg : allArgs) - delete arg; - delete loopAndDom; -} - -u32_t SVFFunction::arg_size() const -{ - return allArgs.size(); -} - -const SVFArgument* SVFFunction::getArg(u32_t idx) const -{ - assert (idx < allArgs.size() && "getArg() out of range!"); - return allArgs[idx]; -} - -bool SVFFunction::isVarArg() const -{ - return varArg; -} - -const SVFBasicBlock *SVFFunction::getExitBB() const -{ - assert(hasBasicBlock() && "function does not have any Basicblock, external function?"); - assert((!hasReturn() || exitBlock->back()->isRetInst()) && "last inst must be return inst"); - assert(exitBlock && "must have an exitBlock"); - return exitBlock; -} - -void SVFFunction::setExitBlock(SVFBasicBlock *bb) -{ - assert(!exitBlock && "have already set exit Basicblock!"); - exitBlock = bb; -} - -SVFBasicBlock::SVFBasicBlock(const SVFType* ty, const SVFFunction* f) - : SVFValue(ty, SVFValue::SVFBB), fun(f) -{ -} - -SVFBasicBlock::~SVFBasicBlock() -{ - for(const SVFInstruction* inst : allInsts) - delete inst; -} - -/*! - * Get position of a successor basic block - */ -u32_t SVFBasicBlock::getBBSuccessorPos(const SVFBasicBlock* Succ) -{ - u32_t i = 0; - for (const SVFBasicBlock* SuccBB: succBBs) - { - if (SuccBB == Succ) - return i; - i++; - } - assert(false && "Didn't find successor edge?"); - return 0; -} - -u32_t SVFBasicBlock::getBBSuccessorPos(const SVFBasicBlock* Succ) const -{ - u32_t i = 0; - for (const SVFBasicBlock* SuccBB: succBBs) - { - if (SuccBB == Succ) - return i; - i++; - } - assert(false && "Didn't find successor edge?"); - return 0; -} - -const SVFInstruction* SVFBasicBlock::getTerminator() const -{ - if (allInsts.empty() || !allInsts.back()->isTerminator()) - return nullptr; - return allInsts.back(); -} - -/*! - * Return a position index from current bb to it successor bb - */ -u32_t SVFBasicBlock::getBBPredecessorPos(const SVFBasicBlock* succbb) -{ - u32_t pos = 0; - for (const SVFBasicBlock* PredBB : succbb->getPredecessors()) - { - if(PredBB == this) - return pos; - ++pos; - } - assert(false && "Didn't find predecessor edge?"); - return pos; -} -u32_t SVFBasicBlock::getBBPredecessorPos(const SVFBasicBlock* succbb) const -{ - u32_t pos = 0; - for (const SVFBasicBlock* PredBB : succbb->getPredecessors()) - { - if(PredBB == this) - return pos; - ++pos; - } - assert(false && "Didn't find predecessor edge?"); - return pos; -} - -SVFInstruction::SVFInstruction(const SVFType* ty, const SVFBasicBlock* b, - bool tm, bool isRet, SVFValKind k) - : SVFValue(ty, k), bb(b), terminator(tm), ret(isRet) -{ -} diff --git a/svf/lib/SVFIR/SVFVariables.cpp b/svf/lib/SVFIR/SVFVariables.cpp deleted file mode 100644 index e936e1dea..000000000 --- a/svf/lib/SVFIR/SVFVariables.cpp +++ /dev/null @@ -1,211 +0,0 @@ -//===- SVFVariables.cpp -- SVF symbols and variables----------------------// -// -// SVF: Static Value-Flow Analysis -// -// Copyright (C) <2013-> -// - -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU Affero General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Affero General Public License for more details. - -// You should have received a copy of the GNU Affero General Public License -// along with this program. If not, see . -// -//===----------------------------------------------------------------------===// - -/* - * SVFVariables.cpp - * - * Created on: Oct 11, 2013 - * Author: Yulei Sui - */ - -#include "SVFIR/SVFVariables.h" -#include "Util/Options.h" -#include "Util/SVFUtil.h" - -using namespace SVF; -using namespace SVFUtil; - - -/*! - * SVFVar constructor - */ -SVFVar::SVFVar(const SVFValue* val, NodeID i, PNODEK k) : - GenericPAGNodeTy(i,k), value(val) -{ - assert( ValNode <= k && k <= DummyObjNode && "new SVFIR node kind?"); - switch (k) - { - case ValNode: - case GepValNode: - { - assert(val != nullptr && "value is nullptr for ValVar or GepValNode"); - isPtr = val->getType()->isPointerTy(); - break; - } - case RetNode: - { - assert(val != nullptr && "value is nullptr for RetNode"); - isPtr = SVFUtil::cast(val)->getReturnType()->isPointerTy(); - break; - } - case VarargNode: - case DummyValNode: - { - isPtr = true; - break; - } - case ObjNode: - case GepObjNode: - case FIObjNode: - case DummyObjNode: - { - isPtr = true; - if(val) - isPtr = val->getType()->isPointerTy(); - break; - } - } -} - -bool SVFVar::isIsolatedNode() const -{ - if (getInEdges().empty() && getOutEdges().empty()) - return true; - else if (isConstDataOrAggDataButNotNullPtr()) - return true; - else if (value && SVFUtil::isa(value)) - return SVFUtil::cast(value)->isIntrinsic(); - else - return false; -} - - -const std::string SVFVar::toString() const -{ - std::string str; - std::stringstream rawstr(str); - rawstr << "SVFVar ID: " << getId(); - return rawstr.str(); -} - -void SVFVar::dump() const -{ - outs() << this->toString() << "\n"; -} - -const std::string ValVar::toString() const -{ - std::string str; - std::stringstream rawstr(str); - rawstr << "ValVar ID: " << getId(); - if (Options::ShowSVFIRValue()) - { - rawstr << "\n"; - rawstr << value->toString(); - } - return rawstr.str(); -} - -const std::string ObjVar::toString() const -{ - std::string str; - std::stringstream rawstr(str); - rawstr << "ObjVar ID: " << getId(); - if (Options::ShowSVFIRValue()) - { - rawstr << "\n"; - rawstr << value->toString(); - } - return rawstr.str(); -} - -const std::string GepValVar::toString() const -{ - std::string str; - std::stringstream rawstr(str); - rawstr << "GepValVar ID: " << getId() << " with offset_" + std::to_string(getConstantFieldIdx()); - if (Options::ShowSVFIRValue()) - { - rawstr << "\n"; - rawstr << value->toString(); - } - return rawstr.str(); -} - -const std::string GepObjVar::toString() const -{ - std::string str; - std::stringstream rawstr(str); - rawstr << "GepObjVar ID: " << getId() << " with offset_" + std::to_string(apOffset); - if (Options::ShowSVFIRValue()) - { - rawstr << "\n"; - rawstr << value->toString(); - } - return rawstr.str(); -} - -const std::string FIObjVar::toString() const -{ - std::string str; - std::stringstream rawstr(str); - rawstr << "FIObjVar ID: " << getId() << " (base object)"; - if (Options::ShowSVFIRValue()) - { - rawstr << "\n"; - rawstr << value->toString(); - } - return rawstr.str(); -} - -const std::string RetPN::toString() const -{ - std::string str; - std::stringstream rawstr(str); - rawstr << "RetPN ID: " << getId() << " unique return node for function " << SVFUtil::cast(value)->getName(); - return rawstr.str(); -} - -const std::string VarArgPN::toString() const -{ - std::string str; - std::stringstream rawstr(str); - rawstr << "VarArgPN ID: " << getId() << " Var arg node for function " << SVFUtil::cast(value)->getName(); - return rawstr.str(); -} - -const std::string DummyValVar::toString() const -{ - std::string str; - std::stringstream rawstr(str); - rawstr << "DummyValVar ID: " << getId(); - return rawstr.str(); -} - -const std::string DummyObjVar::toString() const -{ - std::string str; - std::stringstream rawstr(str); - rawstr << "DummyObjVar ID: " << getId(); - return rawstr.str(); -} - -/// Whether it is constant data, i.e., "0", "1.001", "str" -/// or llvm's metadata, i.e., metadata !4087 -bool SVFVar::isConstDataOrAggDataButNotNullPtr() const -{ - if (hasValue()) - return value->isConstDataOrAggData() && (!SVFUtil::isa(value)) && (!SVFUtil::isa(value)); - else - return false; -} - diff --git a/svf/lib/SVFIR/SymbolTableInfo.cpp b/svf/lib/SVFIR/SymbolTableInfo.cpp deleted file mode 100644 index e0d201653..000000000 --- a/svf/lib/SVFIR/SymbolTableInfo.cpp +++ /dev/null @@ -1,543 +0,0 @@ -//===- SymbolTableInfo.cpp -- Symbol information from IR------------------------// -// -// SVF: Static Value-Flow Analysis -// -// Copyright (C) <2013-> -// - -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU Affero General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Affero General Public License for more details. - -// You should have received a copy of the GNU Affero General Public License -// along with this program. If not, see . -// -//===----------------------------------------------------------------------===// - - -/* - * SymbolTableInfo.cpp - * - * Created on: Nov 11, 2013 - * Author: Yulei Sui - */ - -#include - -#include "SVFIR/SymbolTableInfo.h" -#include "Util/Options.h" -#include "SVFIR/SVFModule.h" - -using namespace std; -using namespace SVF; -using namespace SVFUtil; - -SymbolTableInfo* SymbolTableInfo::symInfo = nullptr; - - -ObjTypeInfo::ObjTypeInfo(const SVFType* t, u32_t max) : type(t), flags(0), maxOffsetLimit(max), elemNum(max) -{ - assert(t && "no type information for this object?"); -} - - -void ObjTypeInfo::resetTypeForHeapStaticObj(const SVFType* t) -{ - assert((isStaticObj() || isHeap()) && "can only reset the inferred type for heap and static objects!"); - type = t; -} - -const StInfo* SymbolTableInfo::getTypeInfo(const SVFType* T) const -{ - assert(T); - SVFTypeSet::const_iterator it = svfTypes.find(T); - assert(it != svfTypes.end() && "type info not found? collect them first during SVFIR Building"); - return (*it)->getTypeInfo(); -} - -/* - * Initial the memory object here (for a dummy object) - */ -ObjTypeInfo* SymbolTableInfo::createObjTypeInfo(const SVFType* type) -{ - ObjTypeInfo* typeInfo = new ObjTypeInfo(type, Options::MaxFieldLimit()); - if(type && type->isPointerTy()) - { - typeInfo->setFlag(ObjTypeInfo::HEAP_OBJ); - } - return typeInfo; -} - -/*! - * Get the symbol table instance - */ -SymbolTableInfo* SymbolTableInfo::SymbolInfo() -{ - if (symInfo == nullptr) - { - symInfo = new SymbolTableInfo(); - symInfo->setModelConstants(Options::ModelConsts()); - } - return symInfo; -} - -/*! - * Get modulus offset given the type information - */ -APOffset SymbolTableInfo::getModulusOffset(const MemObj* obj, const APOffset& apOffset) -{ - - /// if the offset is negative, it's possible that we're looking for an obj node out of range - /// of current struct. Make the offset positive so we can still get a node within current - /// struct to represent this obj. - - APOffset offset = apOffset; - if(offset < 0) - { - writeWrnMsg("try to create a gep node with negative offset."); - offset = abs(offset); - } - u32_t maxOffset = obj->getMaxFieldOffsetLimit(); - - /*! - * @offset: the index allocated to the newly generated field node; - * @Options::MaxFieldLimit(): preset upper bound of field number; - * @maxOffset: the max field number of the base object; - */ - if (maxOffset == 0) - offset = 0; - else if (Options::MaxFieldLimit() < maxOffset) - /*! - * E.g., offset == 260, maxOffset == 270, Options::MaxFieldLimit() == 256 ==> offset = 4 - */ - offset = offset % maxOffset; - else if ((u32_t)offset > maxOffset - 1) - { - if (Options::CyclicFldIdx()) - /*! - * E.g., offset == 100, maxOffset == 98, Options::MaxFieldLimit() == 256 ==> offset = 2 - */ - offset = offset % maxOffset; - else - /*! - * E.g., offset == 100, maxOffset == 98, Options::MaxFieldLimit() == 256 ==> offset = 97 - */ - offset = maxOffset - 1; - } - - return offset; -} - - - -/*! - * Destroy the memory for this symbol table after use - */ -void SymbolTableInfo::destroy() -{ - - for (auto &pair: objMap) - { - if (MemObj* memObj = pair.second) - delete memObj; - } - - for (const SVFType* type : svfTypes) - delete type; - svfTypes.clear(); - - for (const StInfo* st : stInfos) - delete st; - stInfos.clear(); - - mod = nullptr; -} - -const MemObj* SymbolTableInfo::createDummyObj(SymID symId, const SVFType* type) -{ - assert(objMap.find(symId)==objMap.end() && "this dummy obj has been created before"); - MemObj* memObj = new MemObj(symId, createObjTypeInfo(type)); - objMap[symId] = memObj; - return memObj; -} - -/// Number of flattened elements of an array or struct -u32_t SymbolTableInfo::getNumOfFlattenElements(const SVFType* T) -{ - if(Options::ModelArrays()) - return getTypeInfo(T)->getNumOfFlattenElements(); - else - return getTypeInfo(T)->getNumOfFlattenFields(); -} - -/// Flattened offset information of a struct or an array including its array fields -u32_t SymbolTableInfo::getFlattenedElemIdx(const SVFType* T, u32_t origId) -{ - if(Options::ModelArrays()) - { - const std::vector& so = getTypeInfo(T)->getFlattenedElemIdxVec(); - assert ((unsigned)origId < so.size() && !so.empty() && "element index out of bounds, can't get flattened index!"); - return so[origId]; - } - else - { - if(SVFUtil::isa(T)) - { - const std::vector& so = getTypeInfo(T)->getFlattenedFieldIdxVec(); - assert ((unsigned)origId < so.size() && !so.empty() && "Struct index out of bounds, can't get flattened index!"); - return so[origId]; - } - else - { - /// When Options::ModelArrays is disabled, any element index Array is modeled as the base - assert(SVFUtil::isa(T) && "Only accept struct or array type if Options::ModelArrays is disabled!"); - return 0; - } - } -} - -const SVFType* SymbolTableInfo::getOriginalElemType(const SVFType* baseType, u32_t origId) const -{ - return getTypeInfo(baseType)->getOriginalElemType(origId); -} - -/// Return the type of a flattened element given a flattened index -const SVFType* SymbolTableInfo::getFlatternedElemType(const SVFType* baseType, u32_t flatten_idx) -{ - if(Options::ModelArrays()) - { - const std::vector& so = getTypeInfo(baseType)->getFlattenElementTypes(); - assert (flatten_idx < so.size() && !so.empty() && "element index out of bounds or struct opaque type, can't get element type!"); - return so[flatten_idx]; - } - else - { - const std::vector& so = getTypeInfo(baseType)->getFlattenFieldTypes(); - assert (flatten_idx < so.size() && !so.empty() && "element index out of bounds or struct opaque type, can't get element type!"); - return so[flatten_idx]; - } -} - - -const std::vector& SymbolTableInfo::getFlattenFieldTypes(const SVFStructType *T) -{ - return getTypeInfo(T)->getFlattenFieldTypes(); -} - -/* - * Print out the composite type information - */ -void SymbolTableInfo::printFlattenFields(const SVFType* type) -{ - - if (const SVFArrayType* at = SVFUtil::dyn_cast(type)) - { - outs() << " {Type: " << *at << "}\n" - << "\tarray type " - << "\t [element size = " << getNumOfFlattenElements(at) << "]\n" - << "\n"; - } - else if (const SVFStructType *st = SVFUtil::dyn_cast(type)) - { - outs() <<" {Type: " << *st << "}\n"; - const std::vector& finfo = getTypeInfo(st)->getFlattenFieldTypes(); - int field_idx = 0; - for(const SVFType* type : finfo) - { - outs() << " \tField_idx = " << ++field_idx - << ", field type: " << *type << "\n"; - } - outs() << "\n"; - } - else if (const SVFPointerType* pt= SVFUtil::dyn_cast(type)) - { - outs() << *pt << "\n"; - } - else if (const SVFFunctionType* fu = - SVFUtil::dyn_cast(type)) - { - outs() << " {Type: " << *fu << "}\n\n"; - } - else if (const SVFOtherType* ot = SVFUtil::dyn_cast(type)) - { - outs() << " {Type: "<< *ot << "(SVFOtherType)}\n\n"; - } - else - { - assert(type->isSingleValueType() && "not a single value type, then what else!!"); - /// All rest types are scalar type? - u32_t eSize = getNumOfFlattenElements(type); - outs() << " {Type: " << *type << "}\n" - << "\t [object size = " << eSize << "]\n" - << "\n"; - } -} - -std::string SymbolTableInfo::toString(SYMTYPE symtype) -{ - switch (symtype) - { - case SYMTYPE::BlackHole: - { - return "BlackHole"; - } - case SYMTYPE::ConstantObj: - { - return "ConstantObj"; - } - case SYMTYPE::BlkPtr: - { - return "BlkPtr"; - } - case SYMTYPE::NullPtr: - { - return "NullPtr"; - } - case SYMTYPE::ValSymbol: - { - return "ValSym"; - } - case SYMTYPE::ObjSymbol: - { - return "ObjSym"; - } - case SYMTYPE::RetSymbol: - { - return "RetSym"; - } - case SYMTYPE::VarargSymbol: - { - return "VarargSym"; - } - default: - { - return "Invalid SYMTYPE"; - } - } -} - -void SymbolTableInfo::dump() -{ - OrderedMap idmap; - for (ValueToIDMapTy::iterator iter = valSymMap.begin(); iter != valSymMap.end(); - ++iter) - { - const SymID i = iter->second; - SVFValue* val = (SVFValue*) iter->first; - idmap[i] = val; - } - for (ValueToIDMapTy::iterator iter = objSymMap.begin(); iter != objSymMap.end(); - ++iter) - { - const SymID i = iter->second; - SVFValue* val = (SVFValue*) iter->first; - idmap[i] = val; - } - for (FunToIDMapTy::iterator iter = returnSymMap.begin(); iter != returnSymMap.end(); - ++iter) - { - const SymID i = iter->second; - SVFValue* val = (SVFValue*) iter->first; - idmap[i] = val; - } - for (FunToIDMapTy::iterator iter = varargSymMap.begin(); iter != varargSymMap.end(); - ++iter) - { - const SymID i = iter->second; - SVFValue* val = (SVFValue*) iter->first; - idmap[i] = val; - } - outs() << "{SymbolTableInfo \n"; - for (auto iter : idmap) - { - outs() << iter.first << " " << iter.second->toString() << "\n"; - } - outs() << "}\n"; -} - -/*! - * Set mem object to be field sensitive (up to maximum field limit) - */ -void MemObj::setFieldSensitive() -{ - typeInfo->setMaxFieldOffsetLimit(typeInfo->getNumOfElements()); -} - - -/*! - * Constructor of a memory object - */ -MemObj::MemObj(SymID id, ObjTypeInfo* ti, const SVFValue* val) : - typeInfo(ti), refVal(val), symId(id) -{ -} - -/*! - * Whether it is a black hole object - */ -bool MemObj::isBlackHoleObj() const -{ - return SymbolTableInfo::isBlkObj(getId()); -} - -/// Get the number of elements of this object -u32_t MemObj::getNumOfElements() const -{ - return typeInfo->getNumOfElements(); -} - -/// Get the byte size of this object -u32_t MemObj::getByteSizeOfObj() const -{ - return typeInfo->getByteSizeOfObj(); -} - -/// Check if byte size is static determined -bool MemObj::isConstantByteSize() const -{ - return typeInfo->isConstantByteSize(); -} - - -/// Set the number of elements of this object -void MemObj::setNumOfElements(u32_t num) -{ - return typeInfo->setNumOfElements(num); -} - -/// Get obj type info -const SVFType* MemObj::getType() const -{ - return typeInfo->getType(); -} -/* - * Destroy the fields of the memory object - */ -void MemObj::destroy() -{ - delete typeInfo; - typeInfo = nullptr; -} - -/// Get max field offset limit -u32_t MemObj::getMaxFieldOffsetLimit() const -{ - return typeInfo->getMaxFieldOffsetLimit(); -} - -/// Return true if its field limit is 0 -bool MemObj::isFieldInsensitive() const -{ - return getMaxFieldOffsetLimit() == 0; -} - -/// Set the memory object to be field insensitive -void MemObj::setFieldInsensitive() -{ - typeInfo->setMaxFieldOffsetLimit(0); -} - -bool MemObj::isFunction() const -{ - return typeInfo->isFunction(); -} - -bool MemObj::isGlobalObj() const -{ - return typeInfo->isGlobalObj(); -} - -bool MemObj::isStaticObj() const -{ - return typeInfo->isStaticObj(); -} - -bool MemObj::isStack() const -{ - return typeInfo->isStack(); -} - -bool MemObj::isHeap() const -{ - return typeInfo->isHeap(); -} - -bool MemObj::isStruct() const -{ - return typeInfo->isStruct(); -} - -bool MemObj::isArray() const -{ - return typeInfo->isArray(); -} - -bool MemObj::isVarStruct() const -{ - return typeInfo->isVarStruct(); -} - -bool MemObj::isVarArray() const -{ - return typeInfo->isVarArray(); -} - -bool MemObj::isConstantStruct() const -{ - return typeInfo->isConstantStruct(); -} - -bool MemObj::isConstantArray() const -{ - return typeInfo->isConstantArray(); -} - -bool MemObj::isConstDataOrConstGlobal() const -{ - return typeInfo->isConstDataOrConstGlobal(); -} - -bool MemObj::isConstDataOrAggData() const -{ - return typeInfo->isConstDataOrAggData(); -} - - -const std::string MemObj::toString() const -{ - std::string str; - std::stringstream rawstr(str); - rawstr << "MemObj : " << getId() << getValue()->toString() << "\n"; - return rawstr.str(); -} - -/// Get different kinds of syms -//@{ -SymID SymbolTableInfo::getValSym(const SVFValue* val) -{ - - if(val->isNullPtr()) - return nullPtrSymID(); - else if (val->isblackHole()) - return blkPtrSymID(); - else - { - ValueToIDMapTy::const_iterator iter = valSymMap.find(val); - assert(iter!=valSymMap.end() &&"value sym not found"); - return iter->second; - } -} - -bool SymbolTableInfo::hasValSym(const SVFValue* val) -{ - if (val->isNullPtr() || val->isblackHole()) - return true; - else - return (valSymMap.find(val) != valSymMap.end()); -} diff --git a/svf/lib/Util/BitVector.cpp b/svf/lib/Util/BitVector.cpp deleted file mode 100644 index c71698aac..000000000 --- a/svf/lib/Util/BitVector.cpp +++ /dev/null @@ -1,46 +0,0 @@ -//===- BitVector.cpp -- bit vector data structure implementation ------------// - -/* - * BitVector.cpp - * - * Contiguous bit vector with trailing 0s stripped (implementation). - * - * Created on: Jul 4, 2021 - * Author: Mohamad Barbar - */ - -#include - -namespace SVF -{ - -BitVector::BitVector(void) - : BitVector(0) { } - -BitVector::BitVector(size_t n) - : CoreBitVector(n) -{ - // This ensures that leading zeroes are never stripped. - set(0); - reset(0); -} - -BitVector::BitVector(const BitVector &bv) - : CoreBitVector(bv) { } - -BitVector::BitVector(BitVector &&bv) - : CoreBitVector(bv) { } - -BitVector &BitVector::operator=(const BitVector &rhs) -{ - CoreBitVector::operator=(rhs); - return *this; -} - -BitVector &BitVector::operator=(BitVector &&rhs) -{ - CoreBitVector::operator=(rhs); - return *this; -} - -} // namespace SVF diff --git a/svf/lib/Util/CDGBuilder.cpp b/svf/lib/Util/CDGBuilder.cpp deleted file mode 100644 index df0b13d90..000000000 --- a/svf/lib/Util/CDGBuilder.cpp +++ /dev/null @@ -1,214 +0,0 @@ -//===----- CDGBuilder.cpp -- Control Dependence Graph Builder -------------// -// -// SVF: Static Value-Flow Analysis -// -// Copyright (C) <2013-> -// - -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU Affero General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Affero General Public License for more details. - -// You should have received a copy of the GNU Affero General Public License -// along with this program. If not, see . -// -//===----------------------------------------------------------------------===// - -/* - * CDGBuilder.cpp - * - * Created on: Sep 27, 2023 - * Author: Xiao Cheng - */ -#include "Util/CDGBuilder.h" - -using namespace SVF; -using namespace SVFUtil; - -void CDGBuilder::dfsNodesBetweenPdomNodes(const SVFBasicBlock *cur, - const SVFBasicBlock *tgt, - std::vector &path, - std::vector &tgtNodes, - SVFLoopAndDomInfo *ld) -{ - path.push_back(cur); - if (cur == tgt) - { - tgtNodes.insert(tgtNodes.end(), path.begin() + 1, path.end()); - } - else - { - auto it = ld->getPostDomTreeMap().find(cur); - for (const auto &nxt: it->second) - { - dfsNodesBetweenPdomNodes(nxt, tgt, path, tgtNodes, ld); - } - } - path.pop_back(); -} - -/*! - * (3) extract nodes from succ to the least common ancestor LCA of pred and succ - * including LCA if LCA is pred, excluding LCA if LCA is not pred - * @param succ - * @param LCA - * @param tgtNodes - */ -void -CDGBuilder::extractNodesBetweenPdomNodes(const SVFBasicBlock *succ, const SVFBasicBlock *LCA, - std::vector &tgtNodes) -{ - if (succ == LCA) return; - std::vector path; - SVFLoopAndDomInfo *ld = const_cast(LCA->getFunction())->getLoopAndDomInfo(); - dfsNodesBetweenPdomNodes(LCA, succ, path, tgtNodes, ld); -} - -/*! - * Start here - */ -void CDGBuilder::build() -{ - if (_controlDG->getTotalNodeNum() > 0) - return; - PAG *pag = PAG::getPAG(); - buildControlDependence(pag->getModule()); - buildICFGNodeControlMap(); -} - - -s64_t CDGBuilder::getBBSuccessorBranchID(const SVFBasicBlock *BB, const SVFBasicBlock *Succ) -{ - ICFG *icfg = PAG::getPAG()->getICFG(); - const ICFGNode *pred = icfg->getICFGNode(BB->getTerminator()); - const ICFGEdge *edge = nullptr; - for (const auto &inst: Succ->getInstructionList()) - { - if (const ICFGEdge *e = icfg->getICFGEdge(pred, icfg->getICFGNode(inst), ICFGEdge::ICFGEdgeK::IntraCF)) - { - edge = e; - break; - } - } - if (const IntraCFGEdge *intraEdge = SVFUtil::dyn_cast(edge)) - { - if(intraEdge->getCondition()) - return intraEdge->getSuccessorCondValue(); - else - return 0; - } - else - { - assert(false && "not intra edge?"); - abort(); - } -} - -/*! - * Build control dependence for each function - * - * (1) construct CFG for each function - * (2) extract basic block edges (pred->succ) on the CFG to be processed - * succ does not post-dominates pred (!postDT->dominates(succ, pred)) - * (3) extract nodes from succ to the least common ancestor LCA of pred and succ - * including LCA if LCA is pred, excluding LCA if LCA is not pred - * @param svfgModule - */ -void CDGBuilder::buildControlDependence(const SVFModule *svfgModule) -{ - for (const auto &svfFun: *svfgModule) - { - if (SVFUtil::isExtCall(svfFun)) continue; - // extract basic block edges to be processed - Map> BBS; - extractBBS(svfFun, BBS); - - for (const auto &item: BBS) - { - const SVFBasicBlock *pred = item.first; - // for each bb pair - for (const SVFBasicBlock *succ: item.second) - { - const SVFBasicBlock *SVFLCA = const_cast(svfFun)-> - getLoopAndDomInfo()->findNearestCommonPDominator(pred, succ); - std::vector tgtNodes; - // no common ancestor, may be exit() - if (SVFLCA == NULL) - tgtNodes.push_back(succ); - else - { - if (SVFLCA == pred) tgtNodes.push_back(SVFLCA); - // from succ to LCA - extractNodesBetweenPdomNodes(succ, SVFLCA, tgtNodes); - } - - s64_t pos = getBBSuccessorBranchID(pred, succ); - for (const SVFBasicBlock *bb: tgtNodes) - { - updateMap(pred, bb, pos); - } - } - } - } -} - - -/*! - * (2) extract basic block edges on the CFG (pred->succ) to be processed - * succ does not post-dominates pred (!postDT->dominates(succ, pred)) - * @param func - * @param res - */ -void CDGBuilder::extractBBS(const SVF::SVFFunction *func, - Map> &res) -{ - for (const auto &bb: *func) - { - for (const auto &succ: bb->getSuccessors()) - { - if (func->postDominate(succ, bb)) - continue; - res[bb].push_back(succ); - } - } -} - -/*! - * Build map at ICFG node level - */ -void CDGBuilder::buildICFGNodeControlMap() -{ - ICFG *icfg = PAG::getPAG()->getICFG(); - for (const auto &it: _svfcontrolMap) - { - for (const auto &it2: it.second) - { - const SVFBasicBlock *controllingBB = it2.first; - // const ICFGNode *controlNode = _bbToNode[it.first].first; - // if(!controlNode) continue; - const SVFInstruction *terminator = it.first->getInstructionList().back(); - if (!terminator) continue; - const ICFGNode *controlNode = icfg->getICFGNode(terminator); - if (!controlNode) continue; - // controlNode control at pos - for (const auto &inst: *controllingBB) - { - const ICFGNode *controllee = icfg->getICFGNode(inst); - _nodeControlMap[controlNode][controllee].insert(it2.second.begin(), it2.second.end()); - _nodeDependentOnMap[controllee][controlNode].insert(it2.second.begin(), it2.second.end()); - for (s32_t pos: it2.second) - { - _controlDG->addCDGEdgeFromSrcDst(controlNode, controllee, - SVFUtil::dyn_cast(controlNode)->getInst(), - pos); - } - } - } - } -} \ No newline at end of file diff --git a/svf/lib/Util/CallGraphBuilder.cpp b/svf/lib/Util/CallGraphBuilder.cpp deleted file mode 100644 index 1d9d309fd..000000000 --- a/svf/lib/Util/CallGraphBuilder.cpp +++ /dev/null @@ -1,137 +0,0 @@ -//===- CallGraphBuilder.cpp ----------------------------------------------------------------// -// -// SVF: Static Value-Flow Analysis -// -// Copyright (C) <2013-> -// - -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU Affero General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Affero General Public License for more details. - -// You should have received a copy of the GNU Affero General Public License -// along with this program. If not, see . -// -//===----------------------------------------------------------------------===// - - -/* - * CallGraphBuilder.cpp - * - * Created on: - * Author: Yulei - */ - -#include "Util/SVFUtil.h" -#include "Util/CallGraphBuilder.h" -#include "Graphs/ICFG.h" - -using namespace SVF; -using namespace SVFUtil; - -PTACallGraph* CallGraphBuilder::buildCallGraph(SVFModule* svfModule) -{ - /// create nodes - for (SVFModule::const_iterator F = svfModule->begin(), E = svfModule->end(); F != E; ++F) - { - callgraph->addCallGraphNode(*F); - } - - /// create edges - for (SVFModule::const_iterator F = svfModule->begin(), E = svfModule->end(); F != E; ++F) - { - for (const SVFBasicBlock* svfbb : (*F)->getBasicBlockList()) - { - for (const SVFInstruction* inst : svfbb->getInstructionList()) - { - if (SVFUtil::isNonInstricCallSite(inst)) - { - if(const SVFFunction* callee = getCallee(inst)) - { - const CallICFGNode* callBlockNode = icfg->getCallICFGNode(inst); - callgraph->addDirectCallGraphEdge(callBlockNode,*F,callee); - } - } - } - } - } - - return callgraph; -} - -PTACallGraph* ThreadCallGraphBuilder::buildThreadCallGraph(SVFModule* svfModule) -{ - - buildCallGraph(svfModule); - - ThreadCallGraph* cg = dyn_cast(callgraph); - assert(cg && "not a thread callgraph?"); - - ThreadAPI* tdAPI = ThreadAPI::getThreadAPI(); - for (SVFModule::const_iterator F = svfModule->begin(), E = svfModule->end(); F != E; ++F) - { - for (const SVFBasicBlock* svfbb : (*F)->getBasicBlockList()) - { - for (const SVFInstruction* inst : svfbb->getInstructionList()) - { - if (tdAPI->isTDFork(inst)) - { - const CallICFGNode* cs = icfg->getCallICFGNode(inst); - cg->addForksite(cs); - const SVFFunction* forkee = SVFUtil::dyn_cast(tdAPI->getForkedFun(inst)); - if (forkee) - { - cg->addDirectForkEdge(cs); - } - // indirect call to the start routine function - else - { - cg->addThreadForkEdgeSetMap(cs,nullptr); - } - } - else if (tdAPI->isHareParFor(inst)) - { - const CallICFGNode* cs = icfg->getCallICFGNode(inst); - cg->addParForSite(cs); - const SVFFunction* taskFunc = SVFUtil::dyn_cast(tdAPI->getTaskFuncAtHareParForSite(inst)); - if (taskFunc) - { - cg->addDirectParForEdge(cs); - } - // indirect call to the start routine function - else - { - cg->addHareParForEdgeSetMap(cs,nullptr); - } - } - } - } - } - // record join sites - for (SVFModule::const_iterator F = svfModule->begin(), E = svfModule->end(); F != E; ++F) - { - for (const SVFBasicBlock* svfbb : (*F)->getBasicBlockList()) - { - for (const SVFInstruction* inst : svfbb->getInstructionList()) - { - if (tdAPI->isTDJoin(inst)) - { - const CallICFGNode* cs = icfg->getCallICFGNode(inst); - cg->addJoinsite(cs); - } - } - } - } - - return cg; -} - - - - diff --git a/svf/lib/Util/Conditions.cpp b/svf/lib/Util/Conditions.cpp new file mode 100644 index 000000000..cd35188f8 --- /dev/null +++ b/svf/lib/Util/Conditions.cpp @@ -0,0 +1,400 @@ +//===- Conditions.cpp -- Context/path conditions in the form of BDDs----------// +// +// SVF: Static Value-Flow Analysis +// +// Copyright (C) <2013-2017> +// + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// +//===----------------------------------------------------------------------===// + + +/* + * Conditions.cpp + * + * Created on: Oct 19, 2021 + * Author: Yulei and Xiao + */ + +#include "Util/Options.h" +#include "Util/Conditions.h" +#include "Util/SVFUtil.h" + +using namespace SVF; + + +CondExpr* CondManager::trueCond = nullptr; +CondExpr* CondManager::falseCond = nullptr; +CondManager* CondManager::condMgr = nullptr; +u32_t CondManager::totalCondNum = 0; + +/*! + * Constructor + */ +CondManager::CondManager() : sol(cxt) +{ + const z3::expr &trueExpr = cxt.bool_val(true); + trueCond = getOrAddBranchCond(trueExpr, branchCondManager.getTrueCond()); + const z3::expr &falseExpr = cxt.bool_val(false); + falseCond = getOrAddBranchCond(falseExpr, branchCondManager.getFalseCond()); +} + +/*! + * Destructor + */ +CondManager::~CondManager() +{ + for (const auto& it : allocatedConds) + { + delete it.second; + } +} + +/*! + * Preprocess the condition, + * e.g., Compressing using And-Inverter-Graph, Gaussian Elimination + */ +z3::expr CondManager::simplify(const z3::expr& expr) const{ + z3::goal g(expr.ctx()); + z3::tactic qe = + z3::tactic(expr.ctx(), "aig"); + g.add(expr); + z3::apply_result r = qe(g); + z3::expr res(expr.ctx().bool_val(false)); + for (u32_t i = 0; i < r.size(); ++i) { + if (res.is_false()) { + res = r[i].as_expr(); + } else { + res = res || r[i].as_expr(); + } + } + return res; +} + + + +/*! + * Create a fresh condition to encode each program branch + */ +CondExpr* CondManager::createFreshBranchCond(const Instruction* inst) +{ + u32_t condCountIdx = totalCondNum++; + const z3::expr &expr = cxt.bool_const(("c" + std::to_string(condCountIdx)).c_str()); + IDToCondExprMap::const_iterator it = allocatedConds.find(expr.id()); + if (it != allocatedConds.end()) + return it->second; + else{ + BranchCond *branchCond = branchCondManager.createCond(condCountIdx); + auto *cond = new BranchCondExpr(expr, branchCond); + auto *negCond = NEG(cond); + setCondInst(cond, inst); + setCondInst(negCond, inst); + branchCondToCondExpr.emplace(branchCond, cond); + return allocatedConds.emplace(expr.id(), cond).first->second; + } +} + +/*! + * Get or add a single branch condition, + * e.g., when doing condition conjunction + */ +CondExpr* CondManager::getOrAddBranchCond(const z3::expr& e, BranchCond* branchCond) +{ + auto it = branchCondToCondExpr.find(branchCond); + if(it != branchCondToCondExpr.end()) + return it->second; + else{ + auto *cond = new BranchCondExpr(e, branchCond); + branchCondToCondExpr.emplace(branchCond, cond); + return allocatedConds.emplace(e.id(), cond).first->second; + } +} + +/*! + * Return the number of condition expressions + */ +u32_t CondManager::getCondNumber() +{ + return sol.get_model().size(); +} +/// Operations on conditions. +//@{ +CondExpr* CondManager::AND(CondExpr* lhs, CondExpr* rhs){ + if (lhs == getFalseCond() || rhs == getFalseCond()) + return getFalseCond(); + else if (lhs == getTrueCond()) + return rhs; + else if (rhs == getTrueCond()) + return lhs; + else { + BranchCond *branchCond = branchCondManager.AND(SVFUtil::dyn_cast(lhs)->getBranchCond(), + SVFUtil::dyn_cast(rhs)->getBranchCond()); + const z3::expr &expr = lhs->getExpr() && rhs->getExpr(); + return getOrAddBranchCond(expr, branchCond); + } +} + +CondExpr* CondManager::OR(CondExpr* lhs, CondExpr* rhs){ + if (lhs == getTrueCond() || rhs == getTrueCond()) + return getTrueCond(); + else if (lhs == getFalseCond()) + return rhs; + else if (rhs == getFalseCond()) + return lhs; + else{ + BranchCond *branchCond = branchCondManager.OR(SVFUtil::dyn_cast(lhs)->getBranchCond(), + SVFUtil::dyn_cast(rhs)->getBranchCond()); + const z3::expr &expr = lhs->getExpr() || rhs->getExpr(); + return getOrAddBranchCond(expr, branchCond); + } +} +CondExpr* CondManager::NEG(CondExpr* lhs){ + if (lhs == getTrueCond()) + return getFalseCond(); + else if (lhs == getFalseCond()) + return getTrueCond(); + else{ + BranchCond *branchCond = branchCondManager.NEG(SVFUtil::dyn_cast(lhs)->getBranchCond()); + const z3::expr &expr = !lhs->getExpr(); + return getOrAddBranchCond(expr, branchCond); + } +} +//@} + +/*! + * Print the expressions in this model + */ +void CondManager::printModel() +{ + std::cout << sol.check() << "\n"; + z3::model m = sol.get_model(); + for (u32_t i = 0; i < m.size(); i++) + { + z3::func_decl v = m[static_cast(i)]; + std::cout << v.name() << " = " << m.get_const_interp(v) << "\n"; + } +} + +/*! + * Return memory usage for this condition manager + */ +std::string CondManager::getMemUsage() +{ + //std::ostringstream os; + //memory::display_max_usage(os); + //return os.str(); + return ""; +} + +/*! + * Extract sub conditions of this expression + */ +void CondManager::extractSubConds(const CondExpr* cond, NodeBS &support) const +{ + if (cond->getExpr().num_args() == 0) + if (!cond->getExpr().is_true() && !cond->getExpr().is_false()) + support.set(cond->getExpr().id()); + for (u32_t i = 0; i < cond->getExpr().num_args(); ++i) { + const z3::expr &expr = cond->getExpr().arg(i); + extractSubConds(getCond(expr.id()), support); + } +} + +/*! + * Whether the condition is satisfiable + */ +bool CondManager::isSatisfiable(const CondExpr* cond){ + sol.reset(); + sol.add(cond->getExpr()); + z3::check_result result = sol.check(); + if (result == z3::sat || result == z3::unknown) + return true; + else + return false; +} + +/*! + * Whether **All Paths** are reachable + */ +bool CondManager::isAllPathReachable(const CondExpr* e){ + return isEquivalentBranchCond(e, getTrueCond()); +} + +/*! + * Print out one particular expression + */ +inline void CondManager::printDbg(const CondExpr *e) +{ + std::cout << e->getExpr() << "\n"; +} + +/*! + * Return string format of this expression + */ +std::string CondManager::dumpStr(const CondExpr *e) const +{ + std::ostringstream out; + out << e->getExpr(); + return out.str(); +} + +/// Operations on conditions. +//@{ +/// use Cudd_bddAndLimit interface to avoid bdds blow up +BranchCondManager::BranchCond* BranchCondManager::AND(BranchCond* lhs, BranchCond* rhs) +{ + if (lhs == getFalseCond() || rhs == getFalseCond()) + return getFalseCond(); + else if (lhs == getTrueCond()) + return rhs; + else if (rhs == getTrueCond()) + return lhs; + else + { + BranchCond* tmp = Cudd_bddAndLimit(m_bdd_mgr, lhs, rhs, Options::MaxBddSize); + if(tmp==nullptr) + { + SVFUtil::writeWrnMsg("exceeds max bdd size \n"); + ///drop the rhs condition + return lhs; + } + else + { + Cudd_Ref(tmp); + return tmp; + } + } +} + +/*! + * Use Cudd_bddOrLimit interface to avoid bdds blow up + */ +BranchCondManager::BranchCond* BranchCondManager::OR(BranchCond* lhs, BranchCond* rhs) +{ + if (lhs == getTrueCond() || rhs == getTrueCond()) + return getTrueCond(); + else if (lhs == getFalseCond()) + return rhs; + else if (rhs == getFalseCond()) + return lhs; + else + { + BranchCond* tmp = Cudd_bddOrLimit(m_bdd_mgr, lhs, rhs, Options::MaxBddSize); + if(tmp==nullptr) + { + SVFUtil::writeWrnMsg("exceeds max bdd size \n"); + /// drop the two conditions here + return getTrueCond(); + } + else + { + Cudd_Ref(tmp); + return tmp; + } + } +} + +BranchCondManager::BranchCond* BranchCondManager::NEG(BranchCond* lhs) +{ + if (lhs == getTrueCond()) + return getFalseCond(); + else if (lhs == getFalseCond()) + return getTrueCond(); + else + return Cudd_Not(lhs); +} +//@} + +/*! + * Utilities for dumping conditions. These methods use global functions from CUDD + * package and they can be removed outside this class scope to be used by others. + */ +void BranchCondManager::ddClearFlag(BranchCond * f) const +{ + if (!Cudd_IsComplement(f->next)) + return; + /* Clear visited flag. */ + f->next = Cudd_Regular(f->next); + if (cuddIsConstant(f)) + return; + ddClearFlag(cuddT(f)); + ddClearFlag(Cudd_Regular(cuddE(f))); + return; +} + +void BranchCondManager::BddSupportStep(BranchCond * f, NodeBS &support) const +{ + if (cuddIsConstant(f) || Cudd_IsComplement(f->next)) + return; + + support.set(f->index); + + BddSupportStep(cuddT(f), support); + BddSupportStep(Cudd_Regular(cuddE(f)), support); + /* Mark as visited. */ + f->next = Cudd_Complement(f->next); +} + +void BranchCondManager::extractSubConds(BranchCond * f, NodeBS &support) const +{ + BddSupportStep( Cudd_Regular(f), support); + ddClearFlag(Cudd_Regular(f)); +} + +/*! + * Dump BDD + */ +void BranchCondManager::dump(BranchCond* lhs, raw_ostream & O) +{ + if (lhs == getTrueCond()) + O << "T"; + else + { + NodeBS support; + extractSubConds(lhs, support); + for (NodeBS::iterator iter = support.begin(); iter != support.end(); + ++iter) + { + unsigned rid = *iter; + O << rid << " "; + } + } +} + +/*! + * Dump BDD + */ +std::string BranchCondManager::dumpStr(BranchCond* lhs) const +{ + std::string str; + if (lhs == getTrueCond()) + str += "T"; + else + { + NodeBS support; + extractSubConds(lhs, support); + for (NodeBS::iterator iter = support.begin(); iter != support.end(); + ++iter) + { + unsigned rid = *iter; + char int2str[16]; + sprintf(int2str, "%d", rid); + str += int2str; + str += " "; + } + } + return str; +} + diff --git a/svf/lib/Util/CoreBitVector.cpp b/svf/lib/Util/CoreBitVector.cpp deleted file mode 100644 index d32fe2ba5..000000000 --- a/svf/lib/Util/CoreBitVector.cpp +++ /dev/null @@ -1,457 +0,0 @@ -//===- CoreBitVector.cpp -- Dynamically sized bit vector data structure ------------// - -/* - * CoreBitVector.h - * - * Contiguous bit vector which resizes as required by common operations (implementation). - * - * Created on: Jan 31, 2021 - * Author: Mohamad Barbar - */ - -#include - -#include "Util/SparseBitVector.h" // For LLVM's countPopulation. -#include "Util/CoreBitVector.h" -#include "SVFIR/SVFType.h" -#include "Util/SVFUtil.h" - -namespace SVF -{ - -const size_t CoreBitVector::WordSize = sizeof(Word) * CHAR_BIT; - -CoreBitVector::CoreBitVector(void) - : CoreBitVector(0) { } - -CoreBitVector::CoreBitVector(size_t n) - : offset(0), words(n, 0) { } - -CoreBitVector::CoreBitVector(const CoreBitVector &cbv) - : offset(cbv.offset), words(cbv.words) { } - -CoreBitVector::CoreBitVector(CoreBitVector &&cbv) - : offset(cbv.offset), words(std::move(cbv.words)) { } - -CoreBitVector &CoreBitVector::operator=(const CoreBitVector &rhs) -{ - this->offset = rhs.offset; - this->words = rhs.words; - return *this; -} - -CoreBitVector &CoreBitVector::operator=(CoreBitVector &&rhs) -{ - this->offset = rhs.offset; - this->words = std::move(rhs.words); - return *this; -} - -bool CoreBitVector::empty(void) const -{ - for (const Word& w : words) - if (w) - return false; - return true; -} - -u32_t CoreBitVector::count(void) const -{ - u32_t n = 0; - for (const Word &w : words) n += countPopulation(w); - return n; -} - -void CoreBitVector::clear(void) -{ - offset = 0; - words.clear(); - words.shrink_to_fit(); -} - -bool CoreBitVector::test(u32_t bit) const -{ - if (bit < offset || bit >= offset + words.size() * WordSize) return false; - const Word &containingWord = words[(bit - offset) / WordSize]; - const Word mask = (Word)0b1 << (bit % WordSize); - return mask & containingWord; -} - -bool CoreBitVector::test_and_set(u32_t bit) -{ - // TODO: can be faster. - if (test(bit)) return false; - set(bit); - return true; -} - -void CoreBitVector::set(u32_t bit) -{ - extendTo(bit); - - Word &containingWord = words[(bit - offset) / WordSize]; - Word mask = (Word)0b1 << (bit % WordSize); - containingWord |= mask; -} - -void CoreBitVector::reset(u32_t bit) -{ - if (bit < offset || bit >= offset + words.size() * WordSize) return; - Word &containingWord = words[(bit - offset) / WordSize]; - Word mask = ~((Word)0b1 << (bit % WordSize)); - containingWord &= mask; -} - -bool CoreBitVector::contains(const CoreBitVector &rhs) const -{ - CoreBitVector tmp(*this); - tmp &= rhs; - return tmp == rhs; -} - -bool CoreBitVector::intersects(const CoreBitVector &rhs) const -{ - // TODO: want some common iteration method. - if (empty() && rhs.empty()) return false; - - const CoreBitVector &earlierOffsetCBV = offset <= rhs.offset ? *this : rhs; - const CoreBitVector &laterOffsetCBV = offset <= rhs.offset ? rhs : *this; - - size_t earlierOffset = (offset < rhs.offset ? offset : rhs.offset) / WordSize; - size_t laterOffset = (offset > rhs.offset ? offset : rhs.offset) / WordSize; - laterOffset -= earlierOffset; - - const Word *eWords = &earlierOffsetCBV.words[0]; - const size_t eSize = earlierOffsetCBV.words.size(); - const Word *lWords = &laterOffsetCBV.words[0]; - const size_t lSize = laterOffsetCBV.words.size(); - - size_t e = 0; - for ( ; e != laterOffset && e != eSize; ++e) { } - - size_t l = 0; - for ( ; e != eSize && l != lSize; ++e, ++l) - { - if (eWords[e] & lWords[l]) return true; - } - - return false; -} - -bool CoreBitVector::operator==(const CoreBitVector &rhs) const -{ - if (this == &rhs) return true; - - // TODO: maybe a simple equal offset, equal size path? - - size_t lhsSetIndex = nextSetIndex(0); - size_t rhsSetIndex = rhs.nextSetIndex(0); - // Iterate comparing only words with set bits, if there is ever a mismatch, - // then the bit-vectors aren't equal. - while (lhsSetIndex < words.size() && rhsSetIndex < rhs.words.size()) - { - // If the first bit is not the same in the word or words are different, - // then we have a mismatch. - if (lhsSetIndex * WordSize + offset != rhsSetIndex * WordSize + rhs.offset - || words[lhsSetIndex] != rhs.words[rhsSetIndex]) - { - return false; - } - - lhsSetIndex = nextSetIndex(lhsSetIndex + 1); - rhsSetIndex = rhs.nextSetIndex(rhsSetIndex + 1); - } - - // Make sure both got to the end at the same time. - return lhsSetIndex >= words.size() && rhsSetIndex >= rhs.words.size(); -} - -bool CoreBitVector::operator!=(const CoreBitVector &rhs) const -{ - return !(*this == rhs); -} - -bool CoreBitVector::operator|=(const CoreBitVector &rhs) -{ - if (words.size() == 0) - { - *this = rhs; - return words.size() != 0; - } - - if (rhs.words.size() == 0) return false; - - if (this == &rhs) return false; - - // TODO: some redundancy in extendTo calls. - if (finalBit() < rhs.finalBit()) extendForward(rhs.finalBit()); - if (offset > rhs.offset) extendBackward(rhs.offset); - - // Start counting this where rhs starts. - const size_t thisIndex = indexForBit(rhs.offset); - size_t rhsIndex = 0; - - // Only need to test against rhs's size since we extended this to hold rhs. - Word *thisWords = &words[thisIndex]; - const Word *rhsWords = &rhs.words[rhsIndex]; - const size_t length = rhs.words.size(); - Word changed = 0; - - // Can start counting from 0 because we took the addresses of both - // word vectors at the correct index. - // #pragma omp simd - for (size_t i = 0 ; i < length; ++i) - { - const Word oldWord = thisWords[i]; - // Is there anything in rhs not in *this? - thisWords[i] = thisWords[i] | rhsWords[i]; - changed |= oldWord ^ thisWords[i]; - } - - return changed; -} - -bool CoreBitVector::operator&=(const CoreBitVector &rhs) -{ - // The first bit this and rhs have in common is the greater of - // their offsets if the CBV with the smaller offset can hold - // the greater offset. - u32_t greaterOffset = std::max(offset, rhs.offset); - - // If there is no overlap, then clear this CBV. - if (!canHold(greaterOffset) || !rhs.canHold(greaterOffset)) - { - bool changed = false; - for (size_t i = 0; i < words.size(); ++i) - { - if (!changed) changed = words[i] != 0; - words[i] = 0; - } - - return changed; - } - - bool changed = false; - size_t thisIndex = indexForBit(greaterOffset); - size_t rhsIndex = rhs.indexForBit(greaterOffset); - - // Clear everything before the overlapping part. - for (size_t i = 0; i < thisIndex; ++i) - { - if (!changed) changed = words[i] != 0; - words[i] = 0; - } - - Word oldWord; - for ( ; thisIndex < words.size() && rhsIndex < rhs.words.size(); ++thisIndex, ++rhsIndex) - { - if (!changed) oldWord = words[thisIndex]; - words[thisIndex] &= rhs.words[rhsIndex]; - if (!changed) changed = oldWord != words[thisIndex]; - } - - // Clear the remaining bits with no rhs analogue. - for ( ; thisIndex < words.size(); ++thisIndex) - { - if (!changed && words[thisIndex] != 0) changed = true; - words[thisIndex] = 0; - } - - return changed; -} - -bool CoreBitVector::operator-=(const CoreBitVector &rhs) -{ - // Similar to |= in that we only iterate over rhs within this, but we - // don't need to extend anything since nothing from rhs is being added. - u32_t greaterOffset = std::max(offset, rhs.offset); - // TODO: calling twice. - // No overlap if either cannot hold the greater offset. - if (!canHold(greaterOffset) || !rhs.canHold(greaterOffset)) return false; - - bool changed = false; - size_t thisIndex = indexForBit(greaterOffset); - size_t rhsIndex = rhs.indexForBit(greaterOffset); - Word oldWord; - for ( ; thisIndex < words.size() && rhsIndex < rhs.words.size(); ++thisIndex, ++rhsIndex) - { - if (!changed) oldWord = words[thisIndex]; - words[thisIndex] &= ~rhs.words[rhsIndex]; - if (!changed) changed = oldWord != words[thisIndex]; - } - - return changed; -} - -bool CoreBitVector::intersectWithComplement(const CoreBitVector &rhs) -{ - return *this -= rhs; -} - -void CoreBitVector::intersectWithComplement(const CoreBitVector &lhs, const CoreBitVector &rhs) -{ - // TODO: inefficient! - *this = lhs; - intersectWithComplement(rhs); -} - -size_t CoreBitVector::hash(void) const -{ - // From https://stackoverflow.com/a/27216842 - size_t h = words.size(); - for (const Word &w : words) - { - h ^= w + 0x9e3779b9 + (h << 6) + (h >> 2); - } - - return h + offset; -} - -CoreBitVector::const_iterator CoreBitVector::end(void) const -{ - return CoreBitVectorIterator(this, true); -} - -CoreBitVector::const_iterator CoreBitVector::begin(void) const -{ - return CoreBitVectorIterator(this); -} - -void CoreBitVector::extendBackward(u32_t bit) -{ - // New offset is the starting bit of the word which contains bit. - u32_t newOffset = (bit / WordSize) * WordSize; - - // TODO: maybe assertions? - // Check if bit can already be included in this BV or if it's extendForward's problem. - if (newOffset >= offset) return; - - words.insert(words.begin(), (offset - newOffset) / WordSize, 0); - offset = newOffset; -} - -void CoreBitVector::extendForward(u32_t bit) -{ - // TODO: maybe assertions? - // Not our problem; extendBackward's problem, or there is nothing to do. - if (bit < offset || canHold(bit)) return; - - // Starting bit of word which would contain bit. - u32_t bitsWord = (bit / WordSize) * WordSize; - - // Add 1 to represent the final word starting at bitsWord. - u32_t wordsToAdd = 1 + (bitsWord - words.size() * WordSize - offset) / WordSize; - words.insert(words.end(), wordsToAdd, 0); -} - -void CoreBitVector::extendTo(u32_t bit) -{ - if (offset == 0 && words.size() == 0) - { - offset = (bit / WordSize) * WordSize; - words.push_back(0); - } - else if (bit < offset) extendBackward(bit); - else if (bit >= offset + words.size() * WordSize) extendForward(bit); -} - -size_t CoreBitVector::indexForBit(u32_t bit) const -{ - assert(canHold(bit)); - // Recall, offset (and the bits in that word) are represented by words[0], - // so solve (offset + x) / WordSize == 0... x == -offset. - return (bit - offset) / WordSize; -} - -bool CoreBitVector::canHold(u32_t bit) const -{ - return bit >= offset && bit < offset + words.size() * WordSize; -} - -u32_t CoreBitVector::finalBit(void) const -{ - return offset + words.size() * WordSize - 1; -} - -size_t CoreBitVector::nextSetIndex(const size_t start) const -{ - size_t index = start; - for ( ; index < words.size(); ++index) - { - if (words[index]) break; - } - - return index; -} - -CoreBitVector::CoreBitVectorIterator::CoreBitVectorIterator(const CoreBitVector *cbv, bool end) - : cbv(cbv), bit(0) -{ - wordIt = end ? cbv->words.end() : cbv->words.begin(); - // If user didn't request an end iterator, or words is non-empty, - // from 0, go to the next bit. But if the 0 bit is set, we don't - // need to because that is the first element. - if (wordIt != cbv->words.end() && !(cbv->words[0] & (Word)0b1)) ++(*this); -} - -const CoreBitVector::CoreBitVectorIterator &CoreBitVector::CoreBitVectorIterator::operator++(void) -{ - assert(!atEnd() && "CoreBitVectorIterator::++(pre): incrementing past end!"); - - ++bit; - // Check if no more bits in wordIt. Find word with a bit set. - if (bit == WordSize || (*wordIt >> bit) == 0) - { - bit = 0; - ++wordIt; - while (wordIt != cbv->words.end() && *wordIt == 0) ++wordIt; - } - - // Find set bit if we're not at the end. - if (wordIt != cbv->words.end()) - { - while (bit != WordSize && (*wordIt & ((Word)0b1 << bit)) == 0) ++bit; - } - - return *this; -} - -const CoreBitVector::CoreBitVectorIterator CoreBitVector::CoreBitVectorIterator::operator++(int) -{ - assert(!atEnd() && "CoreBitVectorIterator::++(pre): incrementing past end!"); - CoreBitVectorIterator old = *this; - ++*this; - return old; -} - -u32_t CoreBitVector::CoreBitVectorIterator::operator*(void) const -{ - assert(!atEnd() && "CoreBitVectorIterator::*: dereferencing end!"); - size_t wordsIndex = wordIt - cbv->words.begin(); - // Add where the bit vector starts (offset), with the number of bits - // in the passed words (the index encodes how many we have completely - // passed since it is position - 1) and the bit we are up to for the - // current word (i.e., in the n+1th) - return cbv->offset + wordsIndex * WordSize + bit; -} - -bool CoreBitVector::CoreBitVectorIterator::operator==(const CoreBitVectorIterator &rhs) const -{ - assert(cbv == rhs.cbv && "CoreBitVectorIterator::==: comparing iterators from different CBVs"); - // When we're at the end we don't care about bit. - if (wordIt == cbv->words.end()) return rhs.wordIt == cbv->words.end(); - return wordIt == rhs.wordIt && bit == rhs.bit; -} - -bool CoreBitVector::CoreBitVectorIterator::operator!=(const CoreBitVectorIterator &rhs) const -{ - assert(cbv == rhs.cbv && "CoreBitVectorIterator::!=: comparing iterators from different CBVs"); - return !(*this == rhs); -} - -bool CoreBitVector::CoreBitVectorIterator::atEnd(void) const -{ - return wordIt == cbv->words.end(); -} - -}; // namespace SVF diff --git a/svf/lib/Util/ExtAPI.cpp b/svf/lib/Util/ExtAPI.cpp index 57f4470b4..7dab65bb9 100644 --- a/svf/lib/Util/ExtAPI.cpp +++ b/svf/lib/Util/ExtAPI.cpp @@ -1,239 +1,934 @@ -//===- ExtAPI.cpp -- External functions -----------------------------------------// -// -// SVF: Static Value-Flow Analysis -// -// Copyright (C) <2013-2017> -// - -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU Affero General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Affero General Public License for more details. - -// You should have received a copy of the GNU Affero General Public License -// along with this program. If not, see . -// -//===----------------------------------------------------------------------===// +/* [ExtAPI.cpp] The actual database of external functions + * v. 005, 2008-08-08 + *------------------------------------------------------------------------------ + */ /* - * ExtAPI.cpp - * - * Created on: July 1, 2022 - * Author: Shuangxiang Kan - */ + * Modified by Yulei Sui 2013 +*/ #include "Util/ExtAPI.h" -#include "Util/SVFUtil.h" -#include "Util/Options.h" -#include "Util/config.h" -#include -#include +#include +using namespace std; using namespace SVF; -ExtAPI* ExtAPI::extOp = nullptr; -std::string ExtAPI::extBcPath = ""; - -ExtAPI* ExtAPI::getExtAPI() -{ - if (extOp == nullptr) - { - extOp = new ExtAPI; - } - return extOp; -} - -void ExtAPI::destory() -{ - if (extOp != nullptr) - { - delete extOp; - extOp = nullptr; - } -} - -// Set extapi.bc file path -bool ExtAPI::setExtBcPath(const std::string& path) -{ - struct stat statbuf; - if (!path.empty() && !stat(path.c_str(), &statbuf)) - { - extBcPath = path; - return true; - } - return false; -} - -// Get environment variables $SVF_DIR and "npm root" through popen() method -static std::string GetStdoutFromCommand(const std::string& command) -{ - char buffer[128]; - std::string result = ""; - // Open pipe to file - FILE* pipe = popen(command.c_str(), "r"); - if (!pipe) - { - return "popen failed!"; - } - // read till end of process: - while (!feof(pipe)) - { - // use buffer to read and add to result - if (fgets(buffer, 128, pipe) != NULL) - result += buffer; - } - pclose(pipe); - // remove "\n" - result.erase(remove(result.begin(), result.end(), '\n'), result.end()); - return result; -} - -// Get extapi.bc file path in npm -static std::string getFilePath(const std::string& path) -{ - std::string bcFilePath = ""; - if (path.compare("SVF_DIR") == 0) - { - char const* svfdir = getenv("SVF_DIR"); - if (svfdir) - bcFilePath = svfdir; - } - else if (path.compare("npm root") == 0) - { - bcFilePath = GetStdoutFromCommand(path); - bcFilePath.append("/SVF"); - } - - if (!bcFilePath.empty() && bcFilePath.back() != '/') - bcFilePath.push_back('/'); - bcFilePath.append(SVF_BUILD_TYPE "-build").append("/lib/extapi.bc"); - return bcFilePath; -} - -// Get extapi.bc path -std::string ExtAPI::getExtBcPath() -{ - // Default ways of retrieving extapi.bc (in order of precedence): - // 1. Set `path/to/extapi.bc` through `setExtBcPath()` - // 2. Set `path/to/extapi.bc` through the command line argument `-extapi=path/to/extapi.bc` - // 3. Get location generated by CMakeLists.txt from `config.h` header file (if SVF was installed) - // 4. Get location in build tree based from `config.h` header file (if SVF was only built) - // 5. Get location based on environment variable $ENV{SVF_DIR} - // 6. Search for `extapi.bc` from root directory for npm installation (iff SVF installed through npm) - - // 1. Set `path/to/extapi.bc` through `setExtBcPath()` - if (!extBcPath.empty()) - return extBcPath; - - // 2. Set `path/to/extapi.bc` through the command line argument `-extapi=path/to/extapi.bc` - if (setExtBcPath(Options::ExtAPIPath())) - return extBcPath; - - // 3. Get location generated by CMakeLists.txt from `config.h` header file (if SVF was installed) - if (setExtBcPath(SVF_EXTAPI_BC)) // Full path is available (for custom file names) - return extBcPath; - if (setExtBcPath(SVF_EXTAPI_DIR "/extapi.bc")) // Based on directory & default filename - return extBcPath; - - // 4. Get location in build tree based from `config.h` header file (if SVF was only built) - if (setExtBcPath(SVF_BUILD_DIR "/lib/extapi.bc")) - return extBcPath; - - // 5. Get location based on environment variable $ENV{SVF_DIR} - if (setExtBcPath(getFilePath("SVF_DIR"))) - return extBcPath; - - // 6. Search for `extapi.bc` from root directory for npm installation (iff SVF installed through npm) - if (setExtBcPath(getFilePath("npm root"))) - return extBcPath; - - SVFUtil::errs() << "ERROR: Failed to find \"extapi.bc\" LLVM bitcode file in " << extBcPath << std::endl - << "To override the default locations for \"extapi.bc\", you can:" << std::endl - << "\t1. Use the command line argument \"-extapi=path/to/extapi.bc\"" << std::endl - << "\t2. Use the \"setExtBcPath()\" function *BEFORE* calling \"buildSVFModule()\"" << std::endl - << "\t3. Override the paths in \"svf/Util/config.h\" (WARNING: will be overwritten when " - << "rebuilding SVF (generated by CMakeLists.txt))" << std::endl; - abort(); -} +ExtAPI* ExtAPI::extAPI = nullptr; -std::string ExtAPI::getExtFuncAnnotation(const SVFFunction* fun, const std::string& funcAnnotation) -{ - assert(fun && "Null SVFFunction* pointer"); - for (const std::string& annotation : fun->getAnnotations()) - if (annotation.find(funcAnnotation) != std::string::npos) - return annotation; - return ""; -} +namespace { -bool ExtAPI::hasExtFuncAnnotation(const SVFFunction* fun, const std::string& funcAnnotation) +struct ei_pair { - assert(fun && "Null SVFFunction* pointer"); - for (const std::string& annotation : fun->getAnnotations()) - if (annotation.find(funcAnnotation) != std::string::npos) - return true; - return false; -} + const char *n; + ExtAPI::extf_t t; +}; -bool ExtAPI::is_memcpy(const SVFFunction *F) -{ - return F && - (hasExtFuncAnnotation(F, "MEMCPY") || hasExtFuncAnnotation(F, "STRCPY") - || hasExtFuncAnnotation(F, "STRCAT")); -} +} // End anonymous namespace -bool ExtAPI::is_memset(const SVFFunction *F) +//Each (name, type) pair will be inserted into the map. +//All entries of the same type must occur together (for error detection). +static const ei_pair ei_pairs[]= { - return F && hasExtFuncAnnotation(F, "MEMSET"); -} + //The current llvm-gcc puts in the \01. + {"\01creat64", ExtAPI::EFT_NOOP}, + {"\01fseeko64", ExtAPI::EFT_NOOP}, + {"\01fstat64", ExtAPI::EFT_NOOP}, + {"\01fstatvfs64", ExtAPI::EFT_NOOP}, + {"\01ftello64", ExtAPI::EFT_NOOP}, + {"\01getrlimit64", ExtAPI::EFT_NOOP}, + {"\01lstat64", ExtAPI::EFT_NOOP}, + {"\01open64", ExtAPI::EFT_NOOP}, + {"\01stat64", ExtAPI::EFT_NOOP}, + {"\01statvfs64", ExtAPI::EFT_NOOP}, + {"Gpm_GetEvent", ExtAPI::EFT_NOOP}, + {"Gpm_Open", ExtAPI::EFT_NOOP}, + {"RAND_seed", ExtAPI::EFT_NOOP}, + {"SSL_CTX_set_default_verify_paths", ExtAPI::EFT_NOOP}, + {"SSL_get_error", ExtAPI::EFT_NOOP}, + {"SSL_get_fd", ExtAPI::EFT_NOOP}, + {"SSL_pending", ExtAPI::EFT_NOOP}, + {"SSL_read", ExtAPI::EFT_NOOP}, + {"SSL_set_bio", ExtAPI::EFT_NOOP}, + {"SSL_set_connect_state", ExtAPI::EFT_NOOP}, + {"SSL_shutdown", ExtAPI::EFT_NOOP}, + {"SSL_state", ExtAPI::EFT_NOOP}, + {"SSL_write", ExtAPI::EFT_NOOP}, + {"Void_FreeCore", ExtAPI::EFT_NOOP}, + {"X509_STORE_CTX_get_error", ExtAPI::EFT_NOOP}, + {"XAllocColor", ExtAPI::EFT_NOOP}, + {"XCloseDisplay", ExtAPI::EFT_NOOP}, + {"XCopyArea", ExtAPI::EFT_NOOP}, + {"XCreateColormap", ExtAPI::EFT_NOOP}, + {"XCreatePixmap", ExtAPI::EFT_NOOP}, + {"XCreateWindow", ExtAPI::EFT_NOOP}, + {"XDrawPoint", ExtAPI::EFT_NOOP}, + {"XDrawString", ExtAPI::EFT_NOOP}, + {"XDrawText", ExtAPI::EFT_NOOP}, + {"XFillRectangle", ExtAPI::EFT_NOOP}, + {"XFillRectangles", ExtAPI::EFT_NOOP}, + {"XFree", ExtAPI::EFT_NOOP}, + {"XFreeColormap", ExtAPI::EFT_NOOP}, + {"XFreeColors", ExtAPI::EFT_NOOP}, + {"XFreeFont", ExtAPI::EFT_NOOP}, + {"XFreeFontNames", ExtAPI::EFT_NOOP}, + {"XFreeGC", ExtAPI::EFT_NOOP}, + {"XFreePixmap", ExtAPI::EFT_NOOP}, + {"XGetGCValues", ExtAPI::EFT_NOOP}, + {"XGetGeometry", ExtAPI::EFT_NOOP}, + {"XInternAtom", ExtAPI::EFT_NOOP}, + {"XMapWindow", ExtAPI::EFT_NOOP}, + {"XNextEvent", ExtAPI::EFT_NOOP}, + {"XPutImage", ExtAPI::EFT_NOOP}, + {"XQueryColor", ExtAPI::EFT_NOOP}, + {"XResizeWindow", ExtAPI::EFT_NOOP}, + {"XSelectInput", ExtAPI::EFT_NOOP}, + {"XSendEvent", ExtAPI::EFT_NOOP}, + {"XSetBackground", ExtAPI::EFT_NOOP}, + {"XSetClipMask", ExtAPI::EFT_NOOP}, + {"XSetClipOrigin", ExtAPI::EFT_NOOP}, + {"XSetFillStyle", ExtAPI::EFT_NOOP}, + {"XSetFont", ExtAPI::EFT_NOOP}, + {"XSetForeground", ExtAPI::EFT_NOOP}, + {"XSetFunction", ExtAPI::EFT_NOOP}, + {"XSetGraphicsExposures", ExtAPI::EFT_NOOP}, + {"XSetLineAttributes", ExtAPI::EFT_NOOP}, + {"XSetTile", ExtAPI::EFT_NOOP}, + {"XSetWMHints", ExtAPI::EFT_NOOP}, + {"XSetWMNormalHints", ExtAPI::EFT_NOOP}, + {"XSetWindowBackgroundPixmap", ExtAPI::EFT_NOOP}, + {"XStoreName", ExtAPI::EFT_NOOP}, + {"XSync", ExtAPI::EFT_NOOP}, + {"XVisualIDFromVisual", ExtAPI::EFT_NOOP}, + {"XWMGeometry", ExtAPI::EFT_NOOP}, + {"XtAppSetFallbackResources", ExtAPI::EFT_NOOP}, + {"XtCloseDisplay", ExtAPI::EFT_NOOP}, + {"XtDestroyApplicationContext", ExtAPI::EFT_NOOP}, + {"XtDestroyWidget", ExtAPI::EFT_NOOP}, + {"_IO_getc", ExtAPI::EFT_NOOP}, + {"_IO_putc", ExtAPI::EFT_NOOP}, + {"__assert_fail", ExtAPI::EFT_NOOP}, + {"__dn_expand", ExtAPI::EFT_NOOP}, + {"__dn_skipname", ExtAPI::EFT_NOOP}, + {"__res_nclose", ExtAPI::EFT_NOOP}, + {"__res_ninit", ExtAPI::EFT_NOOP}, + {"__res_nmkquery", ExtAPI::EFT_NOOP}, + {"__res_nsend", ExtAPI::EFT_NOOP}, + {"__res_query", ExtAPI::EFT_NOOP}, + {"__res_querydomain", ExtAPI::EFT_NOOP}, + {"__res_search", ExtAPI::EFT_NOOP}, + {"__sigsetjmp", ExtAPI::EFT_NOOP}, + {"_obstack_begin", ExtAPI::EFT_NOOP}, + {"_obstack_memory_used", ExtAPI::EFT_NOOP}, + {"_obstack_newchunk", ExtAPI::EFT_NOOP}, + {"_setjmp", ExtAPI::EFT_NOOP}, + {"accept", ExtAPI::EFT_NOOP}, + {"access", ExtAPI::EFT_NOOP}, + {"asprintf", ExtAPI::EFT_NOOP}, + {"atexit", ExtAPI::EFT_NOOP}, + {"atof", ExtAPI::EFT_NOOP}, + {"atoi", ExtAPI::EFT_NOOP}, + {"atol", ExtAPI::EFT_NOOP}, + {"bind", ExtAPI::EFT_NOOP}, + {"cfgetospeed", ExtAPI::EFT_NOOP}, + {"cfsetispeed", ExtAPI::EFT_NOOP}, + {"cfsetospeed", ExtAPI::EFT_NOOP}, + {"chdir", ExtAPI::EFT_NOOP}, + {"chmod", ExtAPI::EFT_NOOP}, + {"chown", ExtAPI::EFT_NOOP}, + {"chroot", ExtAPI::EFT_NOOP}, + {"clearerr", ExtAPI::EFT_NOOP}, + {"clearok", ExtAPI::EFT_NOOP}, + {"closedir", ExtAPI::EFT_NOOP}, + {"compress2", ExtAPI::EFT_NOOP}, + {"confstr", ExtAPI::EFT_NOOP}, + {"connect", ExtAPI::EFT_NOOP}, + {"crc32", ExtAPI::EFT_NOOP}, + {"creat", ExtAPI::EFT_NOOP}, + {"creat64", ExtAPI::EFT_NOOP}, + {"deflate", ExtAPI::EFT_NOOP}, + {"deflateEnd", ExtAPI::EFT_NOOP}, + {"deflateInit2_", ExtAPI::EFT_NOOP}, + {"deflateReset", ExtAPI::EFT_NOOP}, + {"delwin", ExtAPI::EFT_NOOP}, + {"dladdr", ExtAPI::EFT_NOOP}, + {"dlclose", ExtAPI::EFT_NOOP}, + {"execl", ExtAPI::EFT_NOOP}, + {"execle", ExtAPI::EFT_NOOP}, + {"execlp", ExtAPI::EFT_NOOP}, + {"execv", ExtAPI::EFT_NOOP}, + {"execve", ExtAPI::EFT_NOOP}, + {"execvp", ExtAPI::EFT_NOOP}, + {"feof", ExtAPI::EFT_NOOP}, + {"ferror", ExtAPI::EFT_NOOP}, + {"fflush", ExtAPI::EFT_NOOP}, + {"fgetc", ExtAPI::EFT_NOOP}, + {"fgetpos", ExtAPI::EFT_NOOP}, + {"fileno", ExtAPI::EFT_NOOP}, + {"flockfile", ExtAPI::EFT_NOOP}, + {"fnmatch", ExtAPI::EFT_NOOP}, + {"forkpty", ExtAPI::EFT_NOOP}, + {"fprintf", ExtAPI::EFT_NOOP}, + {"fputc", ExtAPI::EFT_NOOP}, + {"fputs", ExtAPI::EFT_NOOP}, + {"fread", ExtAPI::EFT_NOOP}, + {"frexp", ExtAPI::EFT_NOOP}, + {"fscanf", ExtAPI::EFT_NOOP}, + {"fseek", ExtAPI::EFT_NOOP}, + {"fseeko", ExtAPI::EFT_NOOP}, + {"fseeko64", ExtAPI::EFT_NOOP}, + {"fsetpos", ExtAPI::EFT_NOOP}, + {"fstat", ExtAPI::EFT_NOOP}, + {"fstat64", ExtAPI::EFT_NOOP}, + {"fstatfs", ExtAPI::EFT_NOOP}, + {"fstatvfs64", ExtAPI::EFT_NOOP}, + {"ftell", ExtAPI::EFT_NOOP}, + {"ftello", ExtAPI::EFT_NOOP}, + {"ftello64", ExtAPI::EFT_NOOP}, + {"ftok", ExtAPI::EFT_NOOP}, + {"funlockfile", ExtAPI::EFT_NOOP}, + {"fwrite", ExtAPI::EFT_NOOP}, + {"g_scanner_destroy", ExtAPI::EFT_NOOP}, + {"g_scanner_eof", ExtAPI::EFT_NOOP}, + {"g_scanner_get_next_token", ExtAPI::EFT_NOOP}, + {"g_scanner_input_file", ExtAPI::EFT_NOOP}, + {"g_scanner_scope_add_symbol", ExtAPI::EFT_NOOP}, + {"gcry_cipher_close", ExtAPI::EFT_NOOP}, + {"gcry_cipher_ctl", ExtAPI::EFT_NOOP}, + {"gcry_cipher_decrypt", ExtAPI::EFT_NOOP}, + {"gcry_cipher_map_name", ExtAPI::EFT_NOOP}, + {"gcry_cipher_open", ExtAPI::EFT_NOOP}, + {"gcry_md_close", ExtAPI::EFT_NOOP}, + {"gcry_md_ctl", ExtAPI::EFT_NOOP}, + {"gcry_md_get_algo", ExtAPI::EFT_NOOP}, + {"gcry_md_hash_buffer", ExtAPI::EFT_NOOP}, + {"gcry_md_map_name", ExtAPI::EFT_NOOP}, + {"gcry_md_open", ExtAPI::EFT_NOOP}, + {"gcry_md_setkey", ExtAPI::EFT_NOOP}, + {"gcry_md_write", ExtAPI::EFT_NOOP}, + {"gcry_mpi_add", ExtAPI::EFT_NOOP}, + {"gcry_mpi_add_ui", ExtAPI::EFT_NOOP}, + {"gcry_mpi_clear_highbit", ExtAPI::EFT_NOOP}, + {"gcry_mpi_print", ExtAPI::EFT_NOOP}, + {"getaddrinfo", ExtAPI::EFT_NOOP}, + {"getc_unlocked", ExtAPI::EFT_NOOP}, + {"getgroups", ExtAPI::EFT_NOOP}, + {"gethostname", ExtAPI::EFT_NOOP}, + {"getloadavg", ExtAPI::EFT_NOOP}, + {"getopt", ExtAPI::EFT_NOOP}, + {"getopt_long", ExtAPI::EFT_NOOP}, + {"getopt_long_only", ExtAPI::EFT_NOOP}, + {"getpeername", ExtAPI::EFT_NOOP}, + {"getresgid", ExtAPI::EFT_NOOP}, + {"getresuid", ExtAPI::EFT_NOOP}, + {"getrlimit", ExtAPI::EFT_NOOP}, + {"getrlimit64", ExtAPI::EFT_NOOP}, + {"getrusage", ExtAPI::EFT_NOOP}, + {"getsockname", ExtAPI::EFT_NOOP}, + {"getsockopt", ExtAPI::EFT_NOOP}, + {"gettimeofday", ExtAPI::EFT_NOOP}, + {"gnutls_pkcs12_bag_decrypt", ExtAPI::EFT_NOOP}, + {"gnutls_pkcs12_bag_deinit", ExtAPI::EFT_NOOP}, + {"gnutls_pkcs12_bag_get_count", ExtAPI::EFT_NOOP}, + {"gnutls_pkcs12_bag_get_type", ExtAPI::EFT_NOOP}, + {"gnutls_x509_crt_deinit", ExtAPI::EFT_NOOP}, + {"gnutls_x509_crt_get_dn_by_oid", ExtAPI::EFT_NOOP}, + {"gnutls_x509_crt_get_key_id", ExtAPI::EFT_NOOP}, + {"gnutls_x509_privkey_deinit", ExtAPI::EFT_NOOP}, + {"gnutls_x509_privkey_get_key_id", ExtAPI::EFT_NOOP}, + {"gzclose", ExtAPI::EFT_NOOP}, + {"gzeof", ExtAPI::EFT_NOOP}, + {"gzgetc", ExtAPI::EFT_NOOP}, + {"gzread", ExtAPI::EFT_NOOP}, + {"gzseek", ExtAPI::EFT_NOOP}, + {"gztell", ExtAPI::EFT_NOOP}, + {"gzwrite", ExtAPI::EFT_NOOP}, + {"hstrerror", ExtAPI::EFT_NOOP}, + {"iconv_close", ExtAPI::EFT_NOOP}, + {"inet_addr", ExtAPI::EFT_NOOP}, + {"inet_aton", ExtAPI::EFT_NOOP}, + {"inet_pton", ExtAPI::EFT_NOOP}, + {"inflate", ExtAPI::EFT_NOOP}, + {"inflateEnd", ExtAPI::EFT_NOOP}, + {"inflateInit2_", ExtAPI::EFT_NOOP}, + {"inflateInit_", ExtAPI::EFT_NOOP}, + {"inflateReset", ExtAPI::EFT_NOOP}, + {"initgroups", ExtAPI::EFT_NOOP}, + {"jpeg_CreateCompress", ExtAPI::EFT_NOOP}, + {"jpeg_CreateDecompress", ExtAPI::EFT_NOOP}, + {"jpeg_destroy", ExtAPI::EFT_NOOP}, + {"jpeg_finish_compress", ExtAPI::EFT_NOOP}, + {"jpeg_finish_decompress", ExtAPI::EFT_NOOP}, + {"jpeg_read_header", ExtAPI::EFT_NOOP}, + {"jpeg_read_scanlines", ExtAPI::EFT_NOOP}, + {"jpeg_resync_to_restart", ExtAPI::EFT_NOOP}, + {"jpeg_set_colorspace", ExtAPI::EFT_NOOP}, + {"jpeg_set_defaults", ExtAPI::EFT_NOOP}, + {"jpeg_set_linear_quality", ExtAPI::EFT_NOOP}, + {"jpeg_set_quality", ExtAPI::EFT_NOOP}, + {"jpeg_start_compress", ExtAPI::EFT_NOOP}, + {"jpeg_start_decompress", ExtAPI::EFT_NOOP}, + {"jpeg_write_scanlines", ExtAPI::EFT_NOOP}, + {"keypad", ExtAPI::EFT_NOOP}, + {"lchown", ExtAPI::EFT_NOOP}, + {"link", ExtAPI::EFT_NOOP}, + {"llvm.dbg", ExtAPI::EFT_NOOP}, + {"llvm.stackrestore", ExtAPI::EFT_NOOP}, + {"llvm.va_copy", ExtAPI::EFT_NOOP}, + {"llvm.va_end", ExtAPI::EFT_NOOP}, + {"llvm.va_start", ExtAPI::EFT_NOOP}, + {"longjmp", ExtAPI::EFT_NOOP}, + {"lstat", ExtAPI::EFT_NOOP}, + {"lstat64", ExtAPI::EFT_NOOP}, + {"mblen", ExtAPI::EFT_NOOP}, + {"mbrlen", ExtAPI::EFT_NOOP}, + {"mbrtowc", ExtAPI::EFT_NOOP}, + {"mbtowc", ExtAPI::EFT_NOOP}, + {"memcmp", ExtAPI::EFT_NOOP}, + {"mkdir", ExtAPI::EFT_NOOP}, + {"mknod", ExtAPI::EFT_NOOP}, + {"mkfifo", ExtAPI::EFT_NOOP}, + {"mkstemp", ExtAPI::EFT_NOOP}, + {"mkstemp64", ExtAPI::EFT_NOOP}, + {"mktime", ExtAPI::EFT_NOOP}, + {"modf", ExtAPI::EFT_NOOP}, + {"mprotect", ExtAPI::EFT_NOOP}, + {"munmap", ExtAPI::EFT_NOOP}, + {"nanosleep", ExtAPI::EFT_NOOP}, + {"nodelay", ExtAPI::EFT_NOOP}, + {"open", ExtAPI::EFT_NOOP}, + {"open64", ExtAPI::EFT_NOOP}, + {"openlog", ExtAPI::EFT_NOOP}, + {"openpty", ExtAPI::EFT_NOOP}, + {"pathconf", ExtAPI::EFT_NOOP}, + {"pclose", ExtAPI::EFT_NOOP}, + {"perror", ExtAPI::EFT_NOOP}, + {"pipe", ExtAPI::EFT_NOOP}, + {"png_destroy_write_struct", ExtAPI::EFT_NOOP}, + {"png_init_io", ExtAPI::EFT_NOOP}, + {"png_set_bKGD", ExtAPI::EFT_NOOP}, + {"png_set_invert_alpha", ExtAPI::EFT_NOOP}, + {"png_set_invert_mono", ExtAPI::EFT_NOOP}, + {"png_write_end", ExtAPI::EFT_NOOP}, + {"png_write_info", ExtAPI::EFT_NOOP}, + {"png_write_rows", ExtAPI::EFT_NOOP}, + {"poll", ExtAPI::EFT_NOOP}, + {"pread64", ExtAPI::EFT_NOOP}, + {"printf", ExtAPI::EFT_NOOP}, + {"pthread_attr_destroy", ExtAPI::EFT_NOOP}, + {"pthread_attr_init", ExtAPI::EFT_NOOP}, + {"pthread_attr_setscope", ExtAPI::EFT_NOOP}, + {"pthread_attr_setstacksize", ExtAPI::EFT_NOOP}, + {"pthread_create", ExtAPI::EFT_NOOP}, + {"pthread_mutex_destroy", ExtAPI::EFT_NOOP}, + {"pthread_mutex_init", ExtAPI::EFT_NOOP}, + {"pthread_mutex_lock", ExtAPI::EFT_NOOP}, + {"pthread_mutex_unlock", ExtAPI::EFT_NOOP}, + {"pthread_mutexattr_destroy", ExtAPI::EFT_NOOP}, + {"pthread_mutexattr_init", ExtAPI::EFT_NOOP}, + {"pthread_mutexattr_settype", ExtAPI::EFT_NOOP}, + {"ptsname", ExtAPI::EFT_NOOP}, + {"putenv", ExtAPI::EFT_NOOP}, + {"puts", ExtAPI::EFT_NOOP}, + {"qsort", ExtAPI::EFT_NOOP}, + {"re_compile_fastmap", ExtAPI::EFT_NOOP}, + {"re_exec", ExtAPI::EFT_NOOP}, + {"re_search", ExtAPI::EFT_NOOP}, + {"read", ExtAPI::EFT_NOOP}, + {"readlink", ExtAPI::EFT_NOOP}, + {"recv", ExtAPI::EFT_NOOP}, + {"recvfrom", ExtAPI::EFT_NOOP}, + {"regcomp", ExtAPI::EFT_NOOP}, + {"regerror", ExtAPI::EFT_NOOP}, + {"remove", ExtAPI::EFT_NOOP}, + {"rename", ExtAPI::EFT_NOOP}, + {"rewind", ExtAPI::EFT_NOOP}, + {"rewinddir", ExtAPI::EFT_NOOP}, + {"rmdir", ExtAPI::EFT_NOOP}, + {"rresvport", ExtAPI::EFT_NOOP}, + {"scrollok", ExtAPI::EFT_NOOP}, + {"select", ExtAPI::EFT_NOOP}, + {"sem_destroy", ExtAPI::EFT_NOOP}, + {"sem_init", ExtAPI::EFT_NOOP}, + {"sem_post", ExtAPI::EFT_NOOP}, + {"sem_trywait", ExtAPI::EFT_NOOP}, + {"sem_wait", ExtAPI::EFT_NOOP}, + {"send", ExtAPI::EFT_NOOP}, + {"sendto", ExtAPI::EFT_NOOP}, + {"setbuf", ExtAPI::EFT_NOOP}, + {"setenv", ExtAPI::EFT_NOOP}, + {"setgroups", ExtAPI::EFT_NOOP}, + {"setitimer", ExtAPI::EFT_NOOP}, + {"setrlimit", ExtAPI::EFT_NOOP}, + {"setsockopt", ExtAPI::EFT_NOOP}, + {"setvbuf", ExtAPI::EFT_NOOP}, + {"sigaction", ExtAPI::EFT_NOOP}, + {"sigaddset", ExtAPI::EFT_NOOP}, + {"sigaltstack", ExtAPI::EFT_NOOP}, + {"sigdelset", ExtAPI::EFT_NOOP}, + {"sigemptyset", ExtAPI::EFT_NOOP}, + {"sigfillset", ExtAPI::EFT_NOOP}, + {"sigisemptyset", ExtAPI::EFT_NOOP}, + {"sigismember", ExtAPI::EFT_NOOP}, + {"siglongjmp", ExtAPI::EFT_NOOP}, + {"sigprocmask", ExtAPI::EFT_NOOP}, + {"sigsuspend", ExtAPI::EFT_NOOP}, + {"snprintf", ExtAPI::EFT_NOOP}, + {"socketpair", ExtAPI::EFT_NOOP}, + {"sprintf", ExtAPI::EFT_NOOP}, + {"sscanf", ExtAPI::EFT_NOOP}, + {"stat", ExtAPI::EFT_NOOP}, + {"stat64", ExtAPI::EFT_NOOP}, + {"statfs", ExtAPI::EFT_NOOP}, + {"statvfs", ExtAPI::EFT_NOOP}, + {"statvfs64", ExtAPI::EFT_NOOP}, + {"strcasecmp", ExtAPI::EFT_NOOP}, + {"strcmp", ExtAPI::EFT_NOOP}, + {"strcoll", ExtAPI::EFT_NOOP}, + {"strcspn", ExtAPI::EFT_NOOP}, + {"strfmon", ExtAPI::EFT_NOOP}, + {"strftime", ExtAPI::EFT_NOOP}, + {"strlen", ExtAPI::EFT_NOOP}, + {"strncasecmp", ExtAPI::EFT_NOOP}, + {"strncmp", ExtAPI::EFT_NOOP}, + {"strspn", ExtAPI::EFT_NOOP}, + {"symlink", ExtAPI::EFT_NOOP}, + {"sysinfo", ExtAPI::EFT_NOOP}, + {"syslog", ExtAPI::EFT_NOOP}, + {"system", ExtAPI::EFT_NOOP}, + {"tcgetattr", ExtAPI::EFT_NOOP}, + {"tcsetattr", ExtAPI::EFT_NOOP}, + {"tgetent", ExtAPI::EFT_NOOP}, + {"tgetflag", ExtAPI::EFT_NOOP}, + {"tgetnum", ExtAPI::EFT_NOOP}, + {"time", ExtAPI::EFT_NOOP}, + {"timegm", ExtAPI::EFT_NOOP}, + {"times", ExtAPI::EFT_NOOP}, + {"tputs", ExtAPI::EFT_NOOP}, + {"truncate", ExtAPI::EFT_NOOP}, + {"uname", ExtAPI::EFT_NOOP}, + {"uncompress", ExtAPI::EFT_NOOP}, + {"ungetc", ExtAPI::EFT_NOOP}, + {"unlink", ExtAPI::EFT_NOOP}, + {"unsetenv", ExtAPI::EFT_NOOP}, + {"utime", ExtAPI::EFT_NOOP}, + {"utimes", ExtAPI::EFT_NOOP}, + {"vasprintf", ExtAPI::EFT_NOOP}, + {"vfprintf", ExtAPI::EFT_NOOP}, + {"vprintf", ExtAPI::EFT_NOOP}, + {"vsnprintf", ExtAPI::EFT_NOOP}, + {"vsprintf", ExtAPI::EFT_NOOP}, + {"waddch", ExtAPI::EFT_NOOP}, + {"waddnstr", ExtAPI::EFT_NOOP}, + {"wait", ExtAPI::EFT_NOOP}, + {"wait3", ExtAPI::EFT_NOOP}, + {"wait4", ExtAPI::EFT_NOOP}, + {"waitpid", ExtAPI::EFT_NOOP}, + {"wattr_off", ExtAPI::EFT_NOOP}, + {"wattr_on", ExtAPI::EFT_NOOP}, + {"wborder", ExtAPI::EFT_NOOP}, + {"wclrtobot", ExtAPI::EFT_NOOP}, + {"wclrtoeol", ExtAPI::EFT_NOOP}, + {"wcrtomb", ExtAPI::EFT_NOOP}, + {"wctomb", ExtAPI::EFT_NOOP}, + {"wctype", ExtAPI::EFT_NOOP}, + {"werase", ExtAPI::EFT_NOOP}, + {"wgetch", ExtAPI::EFT_NOOP}, + {"wmove", ExtAPI::EFT_NOOP}, + {"wrefresh", ExtAPI::EFT_NOOP}, + {"write", ExtAPI::EFT_NOOP}, + {"wtouchln", ExtAPI::EFT_NOOP}, + {"ptdTargetCheck", ExtAPI::EFT_NOOP}, + + {"\01_fopen", ExtAPI::EFT_ALLOC}, + {"\01fopen64", ExtAPI::EFT_ALLOC}, + {"\01readdir64", ExtAPI::EFT_ALLOC}, + {"\01tmpfile64", ExtAPI::EFT_ALLOC}, + {"BIO_new_socket", ExtAPI::EFT_ALLOC}, + {"FT_Get_Sfnt_Table", ExtAPI::EFT_ALLOC}, + {"FcFontList", ExtAPI::EFT_ALLOC}, + {"FcFontMatch", ExtAPI::EFT_ALLOC}, + {"FcFontRenderPrepare", ExtAPI::EFT_ALLOC}, + {"FcFontSetCreate", ExtAPI::EFT_ALLOC}, + {"FcFontSort", ExtAPI::EFT_ALLOC}, + {"FcInitLoadConfig", ExtAPI::EFT_ALLOC}, + {"FcObjectSetBuild", ExtAPI::EFT_ALLOC}, + {"FcObjectSetCreate", ExtAPI::EFT_ALLOC}, + {"FcPatternBuild", ExtAPI::EFT_ALLOC}, + {"FcPatternCreate", ExtAPI::EFT_ALLOC}, + {"FcPatternDuplicate", ExtAPI::EFT_ALLOC}, + {"SSL_CTX_new", ExtAPI::EFT_ALLOC}, + {"SSL_get_peer_certificate", ExtAPI::EFT_ALLOC}, + {"SSL_new", ExtAPI::EFT_ALLOC}, + {"SSLv23_client_method", ExtAPI::EFT_ALLOC}, + {"SyGetmem", ExtAPI::EFT_ALLOC}, + {"TLSv1_client_method", ExtAPI::EFT_ALLOC}, + {"Void_ExtendCore", ExtAPI::EFT_ALLOC}, + {"XAddExtension", ExtAPI::EFT_ALLOC}, + {"XAllocClassHint", ExtAPI::EFT_ALLOC}, + {"XAllocSizeHints", ExtAPI::EFT_ALLOC}, + {"XAllocStandardColormap", ExtAPI::EFT_ALLOC}, + {"XCreateFontSet", ExtAPI::EFT_ALLOC}, + {"XCreateImage", ExtAPI::EFT_ALLOC}, + {"XCreateGC", ExtAPI::EFT_ALLOC}, + //Returns the prev. defined handler. + {"XESetCloseDisplay", ExtAPI::EFT_ALLOC}, + {"XGetImage", ExtAPI::EFT_ALLOC}, + {"XGetModifierMapping", ExtAPI::EFT_ALLOC}, + {"XGetMotionEvents", ExtAPI::EFT_ALLOC}, + {"XGetVisualInfo", ExtAPI::EFT_ALLOC}, + {"XLoadQueryFont", ExtAPI::EFT_ALLOC}, + {"XListPixmapFormats", ExtAPI::EFT_ALLOC}, + {"XRenderFindFormat", ExtAPI::EFT_ALLOC}, + {"XRenderFindStandardFormat", ExtAPI::EFT_ALLOC}, + {"XRenderFindVisualFormat", ExtAPI::EFT_ALLOC}, + {"XOpenDisplay", ExtAPI::EFT_ALLOC}, + //These 2 return the previous handler. + {"XSetErrorHandler", ExtAPI::EFT_ALLOC}, + {"XSetIOErrorHandler", ExtAPI::EFT_ALLOC}, + {"XShapeGetRectangles", ExtAPI::EFT_ALLOC}, + {"XShmCreateImage", ExtAPI::EFT_ALLOC}, + //This returns the handler last passed to XSetAfterFunction(). + {"XSynchronize", ExtAPI::EFT_ALLOC}, + {"XcursorImageCreate", ExtAPI::EFT_ALLOC}, + {"XcursorLibraryLoadImages", ExtAPI::EFT_ALLOC}, + {"XcursorShapeLoadImages", ExtAPI::EFT_ALLOC}, + {"XineramaQueryScreens", ExtAPI::EFT_ALLOC}, + {"XkbGetMap", ExtAPI::EFT_ALLOC}, + {"XtAppCreateShell", ExtAPI::EFT_ALLOC}, + {"XtCreateApplicationContext", ExtAPI::EFT_ALLOC}, + {"XtOpenDisplay", ExtAPI::EFT_ALLOC}, + {"alloc", ExtAPI::EFT_ALLOC}, + {"alloc_check", ExtAPI::EFT_ALLOC}, + {"alloc_clear", ExtAPI::EFT_ALLOC}, + {"art_svp_from_vpath", ExtAPI::EFT_ALLOC}, + {"art_svp_vpath_stroke", ExtAPI::EFT_ALLOC}, + {"art_svp_writer_rewind_new", ExtAPI::EFT_ALLOC}, + //FIXME: returns arg0->svp + {"art_svp_writer_rewind_reap", ExtAPI::EFT_ALLOC}, + {"art_vpath_dash", ExtAPI::EFT_ALLOC}, + {"cairo_create", ExtAPI::EFT_ALLOC}, + {"cairo_image_surface_create_for_data", ExtAPI::EFT_ALLOC}, + {"cairo_pattern_create_for_surface", ExtAPI::EFT_ALLOC}, + {"cairo_surface_create_similar", ExtAPI::EFT_ALLOC}, + {"calloc", ExtAPI::EFT_ALLOC}, + {"fopen", ExtAPI::EFT_ALLOC}, + {"fopen64", ExtAPI::EFT_ALLOC}, + {"fopencookie", ExtAPI::EFT_ALLOC}, + {"g_scanner_new", ExtAPI::EFT_ALLOC}, + {"gcry_sexp_nth_mpi", ExtAPI::EFT_ALLOC}, + {"gzdopen", ExtAPI::EFT_ALLOC}, + {"iconv_open", ExtAPI::EFT_ALLOC}, + {"jpeg_alloc_huff_table", ExtAPI::EFT_ALLOC}, + {"jpeg_alloc_quant_table", ExtAPI::EFT_ALLOC}, + {"lalloc", ExtAPI::EFT_ALLOC}, + {"lalloc_clear", ExtAPI::EFT_ALLOC}, + {"malloc", ExtAPI::EFT_ALLOC}, + + {"nhalloc", ExtAPI::EFT_ALLOC}, + {"oballoc", ExtAPI::EFT_ALLOC}, + + {"ngx_alloc", ExtAPI::EFT_ALLOC}, + {"ngx_array_create", ExtAPI::EFT_ALLOC}, + {"ngx_calloc", ExtAPI::EFT_ALLOC}, + {"ngx_palloc", ExtAPI::EFT_ALLOC}, + {"ngx_palloc_small", ExtAPI::EFT_ALLOC}, + {"ngx_pcalloc", ExtAPI::EFT_ALLOC}, + {"ngx_pnalloc", ExtAPI::EFT_ALLOC}, + {"ngx_resolver_alloc", ExtAPI::EFT_ALLOC}, + {"ngx_resolver_calloc", ExtAPI::EFT_ALLOC}, + {"ngx_slab_alloc", ExtAPI::EFT_ALLOC}, + {"ngx_slab_calloc_locked", ExtAPI::EFT_ALLOC}, + {"ngx_palloc_large", ExtAPI::EFT_ALLOC}, + {"ngx_create_pool", ExtAPI::EFT_ALLOC}, + {"ngx_array_push", ExtAPI::EFT_ALLOC}, + {"ngx_array_push_n", ExtAPI::EFT_ALLOC}, + + {"luaM_reallocv", ExtAPI::EFT_ALLOC}, + {"luaM_malloc", ExtAPI::EFT_ALLOC}, + {"luaM_new", ExtAPI::EFT_ALLOC}, + {"luaM_newvector", ExtAPI::EFT_ALLOC}, + {"luaM_growvector", ExtAPI::EFT_ALLOC}, + {"luaM_reallocvector", ExtAPI::EFT_ALLOC}, + + {"pango_cairo_font_map_create_context", ExtAPI::EFT_ALLOC}, + //This may also point *arg2 to a new string. + {"pcre_compile", ExtAPI::EFT_ALLOC}, + {"pcre_study", ExtAPI::EFT_ALLOC}, + {"permalloc", ExtAPI::EFT_ALLOC}, + {"png_create_info_struct", ExtAPI::EFT_ALLOC}, + {"png_create_write_struct", ExtAPI::EFT_ALLOC}, + {"popen", ExtAPI::EFT_ALLOC}, + {"pthread_getspecific", ExtAPI::EFT_ALLOC}, + {"readdir", ExtAPI::EFT_ALLOC}, + {"readdir64", ExtAPI::EFT_ALLOC}, + {"safe_calloc", ExtAPI::EFT_ALLOC}, + {"safe_malloc", ExtAPI::EFT_ALLOC}, + {"safecalloc", ExtAPI::EFT_ALLOC}, + {"safemalloc", ExtAPI::EFT_ALLOC}, + {"safexcalloc", ExtAPI::EFT_ALLOC}, + {"safexmalloc", ExtAPI::EFT_ALLOC}, + {"savealloc", ExtAPI::EFT_ALLOC}, + {"setmntent", ExtAPI::EFT_ALLOC}, + {"shmat", ExtAPI::EFT_ALLOC}, + //These 2 return the previous handler. + {"__sysv_signal", ExtAPI::EFT_ALLOC}, + {"signal", ExtAPI::EFT_ALLOC}, + {"sigset", ExtAPI::EFT_ALLOC}, + {"tempnam", ExtAPI::EFT_ALLOC}, + {"tmpfile", ExtAPI::EFT_ALLOC}, + {"tmpfile64", ExtAPI::EFT_ALLOC}, + {"xalloc", ExtAPI::EFT_ALLOC}, + {"xcalloc", ExtAPI::EFT_ALLOC}, + {"xmalloc", ExtAPI::EFT_ALLOC}, + //C++ functions + {"_Znwm", ExtAPI::EFT_ALLOC}, // new + {"_Znam", ExtAPI::EFT_ALLOC}, // new [] + {"_Znaj", ExtAPI::EFT_ALLOC}, // new + {"_Znwj", ExtAPI::EFT_ALLOC}, // new [] + {"__cxa_allocate_exception", ExtAPI::EFT_ALLOC}, // allocate an exception + {"aligned_alloc", ExtAPI::EFT_ALLOC}, + {"memalign", ExtAPI::EFT_ALLOC}, + {"valloc", ExtAPI::EFT_ALLOC}, + {"SRE_LockCreate", ExtAPI::EFT_ALLOC}, + {"VOS_MemAlloc", ExtAPI::EFT_ALLOC}, + {"kmalloc", ExtAPI::EFT_ALLOC}, + {"kmalloc_array", ExtAPI::EFT_ALLOC}, + {"krealloc_array", ExtAPI::EFT_ALLOC}, + {"kcalloc", ExtAPI::EFT_ALLOC}, + {"kzalloc", ExtAPI::EFT_ALLOC}, + {"kzalloc_node", ExtAPI::EFT_ALLOC}, + {"kmem_cache_alloc", ExtAPI::EFT_ALLOC}, + {"kmem_cache_alloc_node", ExtAPI::EFT_ALLOC}, + {"krealloc", ExtAPI::EFT_ALLOC}, + {"kvmalloc_node", ExtAPI::EFT_ALLOC}, + {"vmap", ExtAPI::EFT_ALLOC}, + {"vmap_pfn", ExtAPI::EFT_ALLOC}, + {"vmalloc", ExtAPI::EFT_ALLOC}, + {"vmalloc_no_huge", ExtAPI::EFT_ALLOC}, + {"vzalloc", ExtAPI::EFT_ALLOC}, + {"vmalloc_user", ExtAPI::EFT_ALLOC}, + {"vmalloc_node", ExtAPI::EFT_ALLOC}, + {"vzalloc_node", ExtAPI::EFT_ALLOC}, + {"vmalloc_32", ExtAPI::EFT_ALLOC}, + {"vmalloc_32_user", ExtAPI::EFT_ALLOC}, + {"__kmalloc", ExtAPI::EFT_ALLOC}, + {"__kcalloc", ExtAPI::EFT_ALLOC}, + {"__kzalloc", ExtAPI::EFT_ALLOC}, + {"\01mmap64", ExtAPI::EFT_NOSTRUCT_ALLOC}, + //FIXME: this is like realloc but with arg1. + {"X509_NAME_oneline", ExtAPI::EFT_NOSTRUCT_ALLOC}, + {"X509_verify_cert_error_string", ExtAPI::EFT_NOSTRUCT_ALLOC}, + {"XBaseFontNameListOfFontSet", ExtAPI::EFT_NOSTRUCT_ALLOC}, + {"XGetAtomName", ExtAPI::EFT_NOSTRUCT_ALLOC}, + {"XGetDefault", ExtAPI::EFT_NOSTRUCT_ALLOC}, + {"XGetKeyboardMapping", ExtAPI::EFT_NOSTRUCT_ALLOC}, + {"XListDepths", ExtAPI::EFT_NOSTRUCT_ALLOC}, + {"XListFonts", ExtAPI::EFT_NOSTRUCT_ALLOC}, + {"XSetLocaleModifiers", ExtAPI::EFT_NOSTRUCT_ALLOC}, + {"XcursorGetTheme", ExtAPI::EFT_NOSTRUCT_ALLOC}, + {"__strdup", ExtAPI::EFT_NOSTRUCT_ALLOC}, + {"crypt", ExtAPI::EFT_NOSTRUCT_ALLOC}, + {"ctime", ExtAPI::EFT_NOSTRUCT_ALLOC}, + {"dlerror", ExtAPI::EFT_NOSTRUCT_ALLOC}, + {"dlopen", ExtAPI::EFT_NOSTRUCT_ALLOC}, + {"gai_strerror", ExtAPI::EFT_NOSTRUCT_ALLOC}, + {"gcry_cipher_algo_name", ExtAPI::EFT_NOSTRUCT_ALLOC}, + {"gcry_md_algo_name", ExtAPI::EFT_NOSTRUCT_ALLOC}, + {"gcry_md_read", ExtAPI::EFT_NOSTRUCT_ALLOC}, + {"getenv", ExtAPI::EFT_NOSTRUCT_ALLOC}, + {"getlogin", ExtAPI::EFT_NOSTRUCT_ALLOC}, + {"getpass", ExtAPI::EFT_NOSTRUCT_ALLOC}, + {"gnutls_strerror", ExtAPI::EFT_NOSTRUCT_ALLOC}, + {"gpg_strerror", ExtAPI::EFT_NOSTRUCT_ALLOC}, + {"gzerror", ExtAPI::EFT_NOSTRUCT_ALLOC}, + {"inet_ntoa", ExtAPI::EFT_NOSTRUCT_ALLOC}, + {"initscr", ExtAPI::EFT_NOSTRUCT_ALLOC}, + {"llvm.stacksave", ExtAPI::EFT_NOSTRUCT_ALLOC}, + {"mmap", ExtAPI::EFT_NOSTRUCT_ALLOC}, + {"mmap64", ExtAPI::EFT_NOSTRUCT_ALLOC}, + {"newwin", ExtAPI::EFT_NOSTRUCT_ALLOC}, + {"nl_langinfo", ExtAPI::EFT_NOSTRUCT_ALLOC}, + {"opendir", ExtAPI::EFT_NOSTRUCT_ALLOC}, + {"sbrk", ExtAPI::EFT_NOSTRUCT_ALLOC}, + {"strdup", ExtAPI::EFT_NOSTRUCT_ALLOC}, + {"strerror", ExtAPI::EFT_NOSTRUCT_ALLOC}, + {"strsignal", ExtAPI::EFT_NOSTRUCT_ALLOC}, + {"textdomain", ExtAPI::EFT_NOSTRUCT_ALLOC}, + {"tgetstr", ExtAPI::EFT_NOSTRUCT_ALLOC}, + {"tigetstr", ExtAPI::EFT_NOSTRUCT_ALLOC}, + {"tmpnam", ExtAPI::EFT_NOSTRUCT_ALLOC}, + {"ttyname", ExtAPI::EFT_NOSTRUCT_ALLOC}, + + {"__ctype_b_loc", ExtAPI::EFT_STAT2}, + {"__ctype_tolower_loc", ExtAPI::EFT_STAT2}, + {"__ctype_toupper_loc", ExtAPI::EFT_STAT2}, + + {"XKeysymToString", ExtAPI::EFT_STAT}, + {"__errno_location", ExtAPI::EFT_STAT}, + {"__h_errno_location", ExtAPI::EFT_STAT}, + {"__res_state", ExtAPI::EFT_STAT}, + {"asctime", ExtAPI::EFT_STAT}, + {"bindtextdomain", ExtAPI::EFT_STAT}, + {"bind_textdomain_codeset", ExtAPI::EFT_STAT}, + //This is L_A0 when arg0 is not null. + {"ctermid", ExtAPI::EFT_STAT}, + {"dcgettext", ExtAPI::EFT_STAT}, + {"dgettext", ExtAPI::EFT_STAT}, + {"dngettext", ExtAPI::EFT_STAT}, + {"fdopen", ExtAPI::EFT_STAT}, + {"gcry_strerror", ExtAPI::EFT_STAT}, + {"gcry_strsource", ExtAPI::EFT_STAT}, + {"getgrgid", ExtAPI::EFT_STAT}, + {"getgrnam", ExtAPI::EFT_STAT}, + {"gethostbyaddr", ExtAPI::EFT_STAT}, + {"gethostbyname", ExtAPI::EFT_STAT}, + {"gethostbyname2", ExtAPI::EFT_STAT}, + {"getmntent", ExtAPI::EFT_STAT}, + {"getprotobyname", ExtAPI::EFT_STAT}, + {"getprotobynumber", ExtAPI::EFT_STAT}, + {"getpwent", ExtAPI::EFT_STAT}, + {"getpwnam", ExtAPI::EFT_STAT}, + {"getpwuid", ExtAPI::EFT_STAT}, + {"getservbyname", ExtAPI::EFT_STAT}, + {"getservbyport", ExtAPI::EFT_STAT}, + {"getspnam", ExtAPI::EFT_STAT}, + {"gettext", ExtAPI::EFT_STAT}, + {"gmtime", ExtAPI::EFT_STAT}, + {"gnu_get_libc_version", ExtAPI::EFT_STAT}, + {"gnutls_check_version", ExtAPI::EFT_STAT}, + {"localeconv", ExtAPI::EFT_STAT}, + {"localtime", ExtAPI::EFT_STAT}, + {"ngettext", ExtAPI::EFT_STAT}, + {"pango_cairo_font_map_get_default", ExtAPI::EFT_STAT}, + {"re_comp", ExtAPI::EFT_STAT}, + {"setlocale", ExtAPI::EFT_STAT}, + {"tgoto", ExtAPI::EFT_STAT}, + {"tparm", ExtAPI::EFT_STAT}, + {"zError", ExtAPI::EFT_STAT}, + + {"getcwd", ExtAPI::EFT_REALLOC}, + {"mem_realloc", ExtAPI::EFT_REALLOC}, + {"realloc", ExtAPI::EFT_REALLOC}, + {"realloc_obj", ExtAPI::EFT_REALLOC}, + {"safe_realloc", ExtAPI::EFT_REALLOC}, + {"saferealloc", ExtAPI::EFT_REALLOC}, + {"safexrealloc", ExtAPI::EFT_REALLOC}, + //FIXME: when arg0 is null, the return points into the string that was + // last passed in arg0 (rather than a new string, as for realloc). + {"strtok", ExtAPI::EFT_REALLOC}, + //As above, but also stores the last string into *arg2. + {"strtok_r", ExtAPI::EFT_REALLOC}, + {"xrealloc", ExtAPI::EFT_REALLOC}, + + {"SSL_CTX_free", ExtAPI::EFT_FREE}, + {"SSL_free", ExtAPI::EFT_FREE}, + {"cfree", ExtAPI::EFT_FREE}, + {"free", ExtAPI::EFT_FREE}, + {"free_all_mem", ExtAPI::EFT_FREE}, + {"freeaddrinfo", ExtAPI::EFT_FREE}, + {"gcry_mpi_release", ExtAPI::EFT_FREE}, + {"gcry_sexp_release", ExtAPI::EFT_FREE}, + {"globfree", ExtAPI::EFT_FREE}, + {"nhfree", ExtAPI::EFT_FREE}, + {"obstack_free", ExtAPI::EFT_FREE}, + {"safe_cfree", ExtAPI::EFT_FREE}, + {"safe_free", ExtAPI::EFT_FREE}, + {"safefree", ExtAPI::EFT_FREE}, + {"safexfree", ExtAPI::EFT_FREE}, + {"sm_free", ExtAPI::EFT_FREE}, + {"vim_free", ExtAPI::EFT_FREE}, + {"xfree", ExtAPI::EFT_FREE}, + {"fclose", ExtAPI::EFT_FREE}, + //C++ functions + {"_ZdaPv", ExtAPI::EFT_FREE}, // delete + {"_ZdlPv", ExtAPI::EFT_FREE}, // delete [] + + {"__rawmemchr", ExtAPI::EFT_L_A0}, + {"cairo_surface_reference", ExtAPI::EFT_L_A0}, + {"fgets", ExtAPI::EFT_L_A0}, + {"jpeg_std_error", ExtAPI::EFT_L_A0}, + {"memchr", ExtAPI::EFT_L_A0}, + //This may return a new ptr if the region was moved. + {"mremap", ExtAPI::EFT_L_A0}, + {"strchr", ExtAPI::EFT_L_A0}, + {"strerror_r", ExtAPI::EFT_L_A0}, + {"strpbrk", ExtAPI::EFT_L_A0}, + {"strptime", ExtAPI::EFT_L_A0}, + {"strrchr", ExtAPI::EFT_L_A0}, + {"strstr", ExtAPI::EFT_L_A0}, + /* + {"ngx_array_push", ExtAPI::EFT_L_A0}, + {"ngx_array_push_n", ExtAPI::EFT_L_A0}, + {"ngx_list_push", ExtAPI::EFT_L_A0}, + */ + {"tmpnam_r", ExtAPI::EFT_L_A0}, + {"asctime_r", ExtAPI::EFT_L_A1}, + {"bsearch", ExtAPI::EFT_L_A1}, + {"getmntent_r", ExtAPI::EFT_L_A1}, + {"gmtime_r", ExtAPI::EFT_L_A1}, + {"gzgets", ExtAPI::EFT_L_A1}, + {"localtime_r", ExtAPI::EFT_L_A1}, + {"realpath", ExtAPI::EFT_L_A1}, + {"\01freopen64", ExtAPI::EFT_L_A2}, + //FIXME: may do L_A3 if arg5 > 0. + {"_XGetAsyncReply", ExtAPI::EFT_L_A2}, + {"freopen", ExtAPI::EFT_L_A2}, + {"freopen64", ExtAPI::EFT_L_A2}, + {"inet_ntop", ExtAPI::EFT_L_A2}, + {"XGetSubImage", ExtAPI::EFT_L_A8}, + + {"memset", ExtAPI::EFT_L_A0__A0R_A1}, + {"llvm.memset", ExtAPI::EFT_L_A0__A0R_A1}, + {"llvm.memset.p0i8.i32", ExtAPI::EFT_L_A0__A0R_A1}, + {"llvm.memset.p0i8.i64", ExtAPI::EFT_L_A0__A0R_A1}, + {"llvm.memcpy", ExtAPI::EFT_L_A0__A0R_A1R}, + {"llvm.memcpy.p0i8.p0i8.i32", ExtAPI::EFT_L_A0__A0R_A1R}, + {"llvm.memcpy.p0i8.p0i8.i64", ExtAPI::EFT_L_A0__A0R_A1R}, + {"llvm.memmove", ExtAPI::EFT_L_A0__A0R_A1R}, + {"llvm.memmove.p0i8.p0i8.i32", ExtAPI::EFT_L_A0__A0R_A1R}, + {"llvm.memmove.p0i8.p0i8.i64", ExtAPI::EFT_L_A0__A0R_A1R}, + {"memccpy", ExtAPI::EFT_L_A0__A0R_A1R}, + {"memcpy", ExtAPI::EFT_L_A0__A0R_A1R}, + {"memmove", ExtAPI::EFT_L_A0__A0R_A1R}, + {"dlsym", ExtAPI::EFT_L_A1__FunPtr}, + {"bcopy", ExtAPI::EFT_A1R_A0R}, + {"iconv", ExtAPI::EFT_A3R_A1R_NS}, + {"strtod", ExtAPI::EFT_A1R_A0}, + {"strtof", ExtAPI::EFT_A1R_A0}, + {"strtol", ExtAPI::EFT_A1R_A0}, + {"strtold", ExtAPI::EFT_A1R_A0}, + {"strtoll", ExtAPI::EFT_A1R_A0}, + {"strtoul", ExtAPI::EFT_A1R_A0}, + {"readdir_r", ExtAPI::EFT_A2R_A1}, + + {"__strcpy_chk", ExtAPI::EFT_L_A0__A1_A0}, + {"__strcat_chk", ExtAPI::EFT_L_A0__A1_A0}, + {"stpcpy", ExtAPI::EFT_L_A0__A1_A0}, + {"strcat", ExtAPI::EFT_L_A0__A1_A0}, + {"strcpy", ExtAPI::EFT_L_A0__A1_A0}, + {"strncat", ExtAPI::EFT_L_A0__A1_A0}, + {"strncpy", ExtAPI::EFT_L_A0__A1_A0}, + + //These also set arg1->pw_name etc. to new strings. + {"getpwnam_r", ExtAPI::EFT_A4R_A1}, + {"getpwuid_r", ExtAPI::EFT_A4R_A1}, + + {"db_create", ExtAPI::EFT_A0R_NEW}, + {"gcry_mpi_scan", ExtAPI::EFT_A0R_NEW}, + {"gcry_pk_decrypt", ExtAPI::EFT_A0R_NEW}, + {"gcry_sexp_build", ExtAPI::EFT_A0R_NEW}, + {"gnutls_pkcs12_bag_init", ExtAPI::EFT_A0R_NEW}, + {"gnutls_pkcs12_init", ExtAPI::EFT_A0R_NEW}, + {"gnutls_x509_crt_init", ExtAPI::EFT_A0R_NEW}, + {"gnutls_x509_privkey_init", ExtAPI::EFT_A0R_NEW}, + {"posix_memalign", ExtAPI::EFT_A0R_NEW}, + {"scandir", ExtAPI::EFT_A1R_NEW}, + {"XGetRGBColormaps", ExtAPI::EFT_A2R_NEW}, + {"XmbTextPropertyToTextList", ExtAPI::EFT_A2R_NEW}, + {"SRE_SplSpecCreate", ExtAPI::EFT_A2R_NEW}, + {"XQueryTree", ExtAPI::EFT_A4R_NEW}, + {"XGetWindowProperty", ExtAPI::EFT_A11R_NEW}, + + // C++ STL functions + // std::_Rb_tree_insert_and_rebalance(bool, std::_Rb_tree_node_base*, std::_Rb_tree_node_base*, std::_Rb_tree_node_base&) + {"_ZSt29_Rb_tree_insert_and_rebalancebPSt18_Rb_tree_node_baseS0_RS_", ExtAPI::EFT_STD_RB_TREE_INSERT_AND_REBALANCE}, + + // std::_Rb_tree_increment and std::_Rb_tree_decrement + // TODO: the following side effects seem not to be necessary +// {"_ZSt18_Rb_tree_incrementPKSt18_Rb_tree_node_base", ExtAPI::EFT_STD_RB_TREE_INCREMENT}, +// {"_ZSt18_Rb_tree_decrementPSt18_Rb_tree_node_base", ExtAPI::EFT_STD_RB_TREE_INCREMENT}, + + {"_ZNSt8__detail15_List_node_base7_M_hookEPS0_", ExtAPI::EFT_STD_LIST_HOOK}, + + + /// string constructor: string (const char *s) + {"_ZNSsC1EPKcRKSaIcE", ExtAPI::CPP_EFT_A0R_A1}, // c++98 + {"_ZNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEC1EPKcRKS3_", ExtAPI::CPP_EFT_A0R_A1}, // c++11 + + /// string constructor: string (const char *s, size_t n) + {"_ZNSsC1EPKcmRKSaIcE", ExtAPI::CPP_EFT_A0R_A1}, // c++98 + {"_ZNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEC1EPKcmRKS3_", ExtAPI::CPP_EFT_A0R_A1}, // c++11 + + /// string operator=: operator= (const char *s) + {"_ZNSsaSEPKc", ExtAPI::CPP_EFT_A0R_A1}, // c++98 + {"_ZNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEaSEPKc", ExtAPI::CPP_EFT_A0R_A1}, // c++11 + + /// string constructor: string (const string &str) + {"_ZNSsC1ERKSs", ExtAPI::CPP_EFT_A0R_A1R}, // c++98 + {"_ZNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEC1ERKS4_", ExtAPI::CPP_EFT_A0R_A1R}, // c++11 + + /// string constructor: string (const string &str, size_t pos, size_t len) + {"_ZNSsC1ERKSsmm", ExtAPI::CPP_EFT_A0R_A1R}, // c++98 + {"_ZNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEC1ERKS4_mm", ExtAPI::CPP_EFT_A0R_A1R}, // c++11 + + /// string operator=: operator= (const string &str) + {"_ZNSsaSERKSs", ExtAPI::CPP_EFT_A0R_A1R}, // c++98 + {"_ZNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEaSERKS4_", ExtAPI::CPP_EFT_A0R_A1R}, // c++11 + + /// std::operator<<: operator<< (const string &str) + {"_ZStlsIcSt11char_traitsIcESaIcEERSt13basic_ostreamIT_T0_ES7_RKSbIS4_S5_T1_E", ExtAPI::CPP_EFT_A1R}, // c++98 + {"_ZStlsIcSt11char_traitsIcESaIcEERSt13basic_ostreamIT_T0_ES7_RKNSt7__cxx1112basic_stringIS4_S5_T1_EE", ExtAPI::CPP_EFT_A1R}, // c++11 + + {"__dynamic_cast", ExtAPI::CPP_EFT_DYNAMIC_CAST}, + {"epoll_ctl", ExtAPI::EFT_EPOLL_CTL_3}, + {"epoll_wait", ExtAPI::EFT_EPOLL_WAIT_2}, + + //This must be the last entry. + {0, ExtAPI::EFT_NOOP} +}; + +/* FIXME: + * SSL_CTX_ctrl, SSL_ctrl - may set the ptr field arg0->x + * SSL_CTX_set_verify - sets the function ptr field arg0->x + * X509_STORE_CTX_get_current_cert - returns arg0->x + * X509_get_subject_name - returns arg0->x->y + * XStringListToTextProperty, XGetWindowAttributes - sets arg2->x + * XInitImage - sets function ptrs arg0->x->y + * XMatchVisualInfo - sets arg4->x + * XtGetApplicationResources - ??? + * glob - sets arg3->gl_pathv + * gnutls_pkcs12_bag_get_data - copies arg0->element[arg1].data to *arg2 + * gnutls_pkcs12_get_bag - finds the arg1'th bag in the ASN1 tree structure + * rooted at arg0->pkcs12 and copies it to *arg2 + * gnutls_pkcs12_import - builds an ASN1 tree rooted at arg0->pkcs12, + * based on decrypted data + * gnutls_x509_crt_import - builds an ASN1 tree rooted at arg0->cert + * gnutls_x509_privkey_export_rsa_raw - points arg1->data thru arg6->data + * to new strings + * gnutls_x509_privkey_import, gnutls_x509_privkey_import_pkcs8 - + * builds an ASN1 tree rooted at arg0->key from decrypted data + * cairo_get_target - returns arg0->gstate->original_target + * hasmntopt - returns arg0->mnt_opts + */ -bool ExtAPI::is_alloc(const SVFFunction* F) -{ - return F && hasExtFuncAnnotation(F, "ALLOC_RET"); -} -// Does (F) allocate a new object and assign it to one of its arguments? -bool ExtAPI::is_arg_alloc(const SVFFunction* F) +void ExtAPI::init() { - return F && hasExtFuncAnnotation(F, "ALLOC_ARG"); -} - -// Get the position of argument which holds the new object -s32_t ExtAPI::get_alloc_arg_pos(const SVFFunction* F) -{ - std::string allocArg = getExtFuncAnnotation(F, "ALLOC_ARG"); - assert(!allocArg.empty() && "Not an alloc call via argument or incorrect extern function annotation!"); - - std::string number; - for (char c : allocArg) + set t_seen; + extf_t prev_t= EFT_NOOP; + t_seen.insert(EFT_NOOP); + for(const ei_pair *p= ei_pairs; p->n; ++p) { - if (isdigit(c)) - number.push_back(c); + if(p->t != prev_t) + { + //This will detect if you move an entry to another block + // but forget to change the type. + if(t_seen.count(p->t)) + { + fputs(p->n, stderr); + putc('\n', stderr); + assert(!"ei_pairs not grouped by type"); + } + t_seen.insert(p->t); + prev_t= p->t; + } + if(info.count(p->n)) + { + fputs(p->n, stderr); + putc('\n', stderr); + assert(!"duplicate name in ei_pairs"); + } + info[p->n]= p->t; } - assert(!number.empty() && "Incorrect naming convention for svf external functions(ALLOC_ARG + number)?"); - return std::stoi(number); } -// Does (F) reallocate a new object? -bool ExtAPI::is_realloc(const SVFFunction* F) -{ - return F && hasExtFuncAnnotation(F, "REALLOC_RET"); -} - - -// Should (F) be considered "external" (either not defined in the program -// or a user-defined version of a known alloc or no-op)? -bool ExtAPI::is_ext(const SVFFunction* F) -{ - assert(F && "Null SVFFunction* pointer"); - if (F->isDeclaration() || F->isIntrinsic()) - return true; - else if (hasExtFuncAnnotation(F, "OVERWRITE") && F->getAnnotations().size() == 1) - return false; - else - return !F->getAnnotations().empty(); -} diff --git a/svf/lib/Util/NodeIDAllocator.cpp b/svf/lib/Util/NodeIDAllocator.cpp index 32fc94959..18f80fa9e 100644 --- a/svf/lib/Util/NodeIDAllocator.cpp +++ b/svf/lib/Util/NodeIDAllocator.cpp @@ -1,717 +1,146 @@ //===- NodeIDAllocator.cpp -- Allocates node IDs on request ------------------------// -#include -#include -#include -#include - -#include "FastCluster/fastcluster.h" -#include "MemoryModel/PointerAnalysisImpl.h" -#include "Util/PTAStat.h" #include "Util/NodeIDAllocator.h" -#include "SVFIR/SVFValue.h" -#include "SVFIR/SVFType.h" -#include "Util/SVFUtil.h" #include "Util/Options.h" namespace SVF { -const NodeID NodeIDAllocator::blackHoleObjectId = 0; -const NodeID NodeIDAllocator::constantObjectId = 1; -const NodeID NodeIDAllocator::blackHolePointerId = 2; -const NodeID NodeIDAllocator::nullPointerId = 3; - -NodeIDAllocator *NodeIDAllocator::allocator = nullptr; - -NodeIDAllocator *NodeIDAllocator::get(void) -{ - if (allocator == nullptr) - { - allocator = new NodeIDAllocator(); - } - - return allocator; -} - -void NodeIDAllocator::unset(void) -{ - if (allocator != nullptr) - { - delete allocator; - allocator = nullptr; - } -} - -// Initialise counts to 4 because that's how many special nodes we have. -NodeIDAllocator::NodeIDAllocator(void) - : numObjects(4), numValues(4), numSymbols(4), numNodes(4), strategy(Options::NodeAllocStrat()) -{ } - -NodeID NodeIDAllocator::allocateObjectId(void) -{ - NodeID id = 0; - if (strategy == Strategy::DENSE) - { - // We allocate objects from 0(-ish, considering the special nodes) to # of objects. - id = numObjects; - } - else if (strategy == Strategy::REVERSE_DENSE) - { - id = UINT_MAX - numObjects; - } - else if (strategy == Strategy::SEQ) - { - // Everything is sequential and intermixed. - id = numNodes; - } - else if (strategy == Strategy::DBUG) - { - // Non-GEPs just grab the next available ID. - // We may have "holes" because GEPs increment the total - // but allocate far away. This is not a problem because - // we don't care about the relative distances between nodes. - id = numNodes; - } - else - { - assert(false && "NodeIDAllocator::allocateObjectId: unimplemented node allocation strategy."); - } - - increaseNumOfObjAndNodes(); - - assert(id != 0 && "NodeIDAllocator::allocateObjectId: ID not allocated"); - return id; -} - -NodeID NodeIDAllocator::allocateGepObjectId(NodeID base, u32_t offset, u32_t maxFieldLimit) -{ - NodeID id = 0; - if (strategy == Strategy::DENSE) - { - // Nothing different to the other case. - id = numObjects; - } - else if (strategy == Strategy::REVERSE_DENSE) - { - id = UINT_MAX - numObjects; - } - else if (strategy == Strategy::SEQ) - { - // Everything is sequential and intermixed. - id = numNodes; - } - else if (strategy == Strategy::DBUG) - { - // For a gep id, base id is set at lower bits, and offset is set at higher bits - // e.g., 1100050 denotes base=50 and offset=10 - // The offset is 10, not 11, because we add 1 to the offset to ensure that the - // high bits are never 0. For example, we do not want the gep id to be 50 when - // the base is 50 and the offset is 0. - NodeID gepMultiplier = pow(10, ceil(log10( - numSymbols > maxFieldLimit ? - numSymbols : maxFieldLimit - ))); - id = (offset + 1) * gepMultiplier + base; - assert(id > numSymbols && "NodeIDAllocator::allocateGepObjectId: GEP allocation clashing with other nodes"); - } - else - { - assert(false && "NodeIDAllocator::allocateGepObjectId: unimplemented node allocation strategy"); - } - - increaseNumOfObjAndNodes(); + const NodeID NodeIDAllocator::blackHoleObjectId = 0; + const NodeID NodeIDAllocator::constantObjectId = 1; + const NodeID NodeIDAllocator::blackHolePointerId = 2; + const NodeID NodeIDAllocator::nullPointerId = 3; - assert(id != 0 && "NodeIDAllocator::allocateGepObjectId: ID not allocated"); - return id; -} + NodeIDAllocator *NodeIDAllocator::allocator = nullptr; -NodeID NodeIDAllocator::allocateValueId(void) -{ - NodeID id = 0; - if (strategy == Strategy::DENSE) - { - // We allocate values from UINT_MAX to UINT_MAX - # of values. - // TODO: UINT_MAX does not allow for an easily changeable type - // of NodeID (though it is already in use elsewhere). - id = UINT_MAX - numValues; - } - else if (strategy == Strategy::REVERSE_DENSE) + NodeIDAllocator *NodeIDAllocator::get(void) { - id = numValues; - } - else if (strategy == Strategy::SEQ) - { - // Everything is sequential and intermixed. - id = numNodes; - } - else if (strategy == Strategy::DBUG) - { - id = numNodes; - } - else - { - assert(false && "NodeIDAllocator::allocateValueId: unimplemented node allocation strategy"); - } - - ++numValues; - ++numNodes; - - assert(id != 0 && "NodeIDAllocator::allocateValueId: ID not allocated"); - return id; -} - -NodeID NodeIDAllocator::endSymbolAllocation(void) -{ - numSymbols = numNodes; - return numSymbols; -} - -const std::string NodeIDAllocator::Clusterer::NumObjects = "NumObjects"; -const std::string NodeIDAllocator::Clusterer::RegioningTime = "RegioningTime"; -const std::string NodeIDAllocator::Clusterer::DistanceMatrixTime = "DistanceMatrixTime"; -const std::string NodeIDAllocator::Clusterer::FastClusterTime = "FastClusterTime"; -const std::string NodeIDAllocator::Clusterer::DendrogramTraversalTime = "DendrogramTravTime"; -const std::string NodeIDAllocator::Clusterer::EvalTime = "EvalTime"; -const std::string NodeIDAllocator::Clusterer::TotalTime = "TotalTime"; -const std::string NodeIDAllocator::Clusterer::TheoreticalNumWords = "TheoreticalWords"; -const std::string NodeIDAllocator::Clusterer::OriginalBvNumWords = "OriginalBvWords"; -const std::string NodeIDAllocator::Clusterer::OriginalSbvNumWords = "OriginalSbvWords"; -const std::string NodeIDAllocator::Clusterer::NewBvNumWords = "NewBvWords"; -const std::string NodeIDAllocator::Clusterer::NewSbvNumWords = "NewSbvWords"; -const std::string NodeIDAllocator::Clusterer::NumRegions = "NumRegions"; -const std::string NodeIDAllocator::Clusterer::NumGtIntRegions = "NumGtIntRegions"; -const std::string NodeIDAllocator::Clusterer::LargestRegion = "LargestRegion"; -const std::string NodeIDAllocator::Clusterer::BestCandidate = "BestCandidate"; -const std::string NodeIDAllocator::Clusterer::NumNonTrivialRegionObjects = "NumNonTrivObj"; - -std::vector NodeIDAllocator::Clusterer::cluster(BVDataPTAImpl *pta, const std::vector> keys, std::vector>> &candidates, std::string evalSubtitle) -{ - assert(pta != nullptr && "Clusterer::cluster: given null BVDataPTAImpl"); - assert(Options::NodeAllocStrat() == Strategy::DENSE && "Clusterer::cluster: only dense allocation clustering currently supported"); - - Map overallStats; - double fastClusterTime = 0.0; - double distanceMatrixTime = 0.0; - double dendrogramTraversalTime = 0.0; - double regioningTime = 0.0; - double evalTime = 0.0; - - // Pair of nodes to their (minimum) distance and the number of occurrences of that distance. - Map, std::pair> distances; - - double clkStart = PTAStat::getClk(true); - - // Map points-to sets to occurrences. - Map pointsToSets; - - // Objects each object shares at least a points-to set with. - Map> coPointeeGraph; - for (const std::pair &keyOcc : keys) - { - const PointsTo &pts = pta->getPts(keyOcc.first); - const size_t oldSize = pointsToSets.size(); - pointsToSets[pts] += keyOcc.second;; - - // Edges in this graph have no weight or uniqueness, so we only need to - // do this for each points-to set once. - if (oldSize != pointsToSets.size()) + if (allocator == nullptr) { - NodeID firstO = !pts.empty() ? *(pts.begin()) : 0; - Set &firstOsNeighbours = coPointeeGraph[firstO]; - for (const NodeID o : pts) - { - if (o != firstO) - { - firstOsNeighbours.insert(o); - coPointeeGraph[o].insert(firstO); - } - } + allocator = new NodeIDAllocator(); } - } - - size_t numObjects = NodeIDAllocator::get()->numObjects; - overallStats[NumObjects] = std::to_string(numObjects); - size_t numRegions = 0; - std::vector objectsRegion; - if (Options::RegionedClustering()) - { - objectsRegion = regionObjects(coPointeeGraph, numObjects, numRegions); - } - else - { - // Just a single big region (0). - objectsRegion.insert(objectsRegion.end(), numObjects, 0); - numRegions = 1; + return allocator; } - // Set needs to be ordered because getDistanceMatrix, in its n^2 iteration, expects - // sets to be ordered (we are building a condensed matrix, not a full matrix, so it - // matters). In getDistanceMatrix, doing regionReverseMapping for oi and oj, where - // oi < oj, and getting a result moi > moj gives incorrect results. - // In the condensed matrix, [b][a] where b >= a, is incorrect. - std::vector> regionsObjects(numRegions); - for (NodeID o = 0; o < numObjects; ++o) regionsObjects[objectsRegion[o]].insert(o); - - // Size of the return node mapping. It is potentially larger than the number of - // objects because we align each region to NATIVE_INT_SIZE. - // size_t numMappings = 0; - - // Maps a region to a mapping which maps 0 to n to all objects - // in that region. - std::vector> regionMappings(numRegions); - // The reverse: region to mapping of objects to a 0 to n from above. - std::vector> regionReverseMappings(numRegions); - // We can thus use 0 to n for each region to create smaller distance matrices. - for (unsigned region = 0; region < numRegions; ++region) + void NodeIDAllocator::unset(void) { - size_t curr = 0; - // With the OrderedSet above, o1 < o2 => map[o1] < map[o2]. - for (NodeID o : regionsObjects[region]) + if (allocator != nullptr) { - // push_back here is just like p...[region][curr] = o. - regionMappings[region].push_back(o); - regionReverseMappings[region][o] = curr++; + delete allocator; } - - // curr is the number of objects. A region with no objects makes no sense. - assert(curr != 0); - - // Number of bits needed for this region if we were - // to start assigning from 0 rounded up to the fewest needed - // native ints. This is added to the number of mappings since - // we align each region to a native int. - // numMappings += requiredBits(regionsObjects[region].size()); - } - - // Points-to sets which are relevant to a region, i.e., those whose elements - // belong to that region. Pair is for occurrences. - std::vector>> regionsPointsTos(numRegions); - for (const Map::value_type &ptocc : pointsToSets) - { - const PointsTo &pt = ptocc.first; - const unsigned occ = ptocc.second; - if (pt.empty()) continue; - // Guaranteed that begin() != end() because of the continue above. All objects in pt - // will be relevant to the same region. - unsigned region = objectsRegion[*(pt.begin())]; - // In our "graph", objects in the same points-to set have an edge between them, - // so they are all in the same connected component/region. - regionsPointsTos[region].push_back(std::make_pair(&pt, occ)); } - double clkEnd = PTAStat::getClk(true); - regioningTime = (clkEnd - clkStart) / TIMEINTERVAL; - overallStats[RegioningTime] = std::to_string(regioningTime); - overallStats[NumRegions] = std::to_string(numRegions); - - std::vector methods; - if (Options::ClusterMethod() == HCLUST_METHOD_SVF_BEST) - { - methods.push_back(HCLUST_METHOD_SINGLE); - methods.push_back(HCLUST_METHOD_COMPLETE); - methods.push_back(HCLUST_METHOD_AVERAGE); - } - else - { - methods.push_back(Options::ClusterMethod()); - } + // Initialise counts to 4 because that's how many special nodes we have. + NodeIDAllocator::NodeIDAllocator(void) + : numObjects(4), numValues(4), numSymbols(4), numNodes(4), strategy(Options::NodeAllocStrat) + { } - for (const hclust_fast_methods method : methods) + NodeID NodeIDAllocator::allocateObjectId(void) { - std::vector nodeMap(numObjects, UINT_MAX); - - unsigned numGtIntRegions = 0; - unsigned largestRegion = 0; - unsigned nonTrivialRegionObjects = 0; - unsigned allocCounter = 0; - for (unsigned region = 0; region < numRegions; ++region) + NodeID id = 0; + if (strategy == Strategy::DENSE) { - const size_t regionNumObjects = regionsObjects[region].size(); - // Round up to next Word: ceiling of current allocation to get how - // many words and multiply to get the number of bits; if we're aligning. - if (Options::RegionAlign()) - { - allocCounter = - ((allocCounter + NATIVE_INT_SIZE - 1) / NATIVE_INT_SIZE) * NATIVE_INT_SIZE; - } - - if (regionNumObjects > largestRegion) largestRegion = regionNumObjects; - - // For regions with fewer than 64 objects, we can just allocate them - // however as they will be in the one int regardless.. - if (regionNumObjects < NATIVE_INT_SIZE) - { - for (NodeID o : regionsObjects[region]) nodeMap[o] = allocCounter++; - continue; - } - - ++numGtIntRegions; - nonTrivialRegionObjects += regionNumObjects; - - double *distMatrix = getDistanceMatrix(regionsPointsTos[region], regionNumObjects, - regionReverseMappings[region], distanceMatrixTime); - - clkStart = PTAStat::getClk(true); - int *dendrogram = new int[2 * (regionNumObjects - 1)]; - double *height = new double[regionNumObjects - 1]; - hclust_fast(regionNumObjects, distMatrix, method, dendrogram, height); - delete[] distMatrix; - delete[] height; - clkEnd = PTAStat::getClk(true); - fastClusterTime += (clkEnd - clkStart) / TIMEINTERVAL; - - clkStart = PTAStat::getClk(true); - Set visited; - traverseDendrogram(nodeMap, dendrogram, regionNumObjects, allocCounter, - visited, regionNumObjects - 1, regionMappings[region]); - delete[] dendrogram; - clkEnd = PTAStat::getClk(true); - dendrogramTraversalTime += (clkEnd - clkStart) / TIMEINTERVAL; + // We allocate objects from 0(-ish, considering the special nodes) to # of objects. + id = numObjects; } - - candidates.push_back(std::make_pair(method, nodeMap)); - - // Though we "update" these in the loop, they will be the same every iteration. - overallStats[NumGtIntRegions] = std::to_string(numGtIntRegions); - overallStats[LargestRegion] = std::to_string(largestRegion); - overallStats[NumNonTrivialRegionObjects] = std::to_string(nonTrivialRegionObjects); - } - - // Work out which of the mappings we generated looks best. - std::pair> bestMapping = determineBestMapping(candidates, pointsToSets, - evalSubtitle, evalTime); - - overallStats[DistanceMatrixTime] = std::to_string(distanceMatrixTime); - overallStats[DendrogramTraversalTime] = std::to_string(dendrogramTraversalTime); - overallStats[FastClusterTime] = std::to_string(fastClusterTime); - overallStats[EvalTime] = std::to_string(evalTime); - overallStats[TotalTime] = std::to_string(distanceMatrixTime + dendrogramTraversalTime + fastClusterTime + regioningTime + evalTime); - - overallStats[BestCandidate] = SVFUtil::hclustMethodToString(bestMapping.first); - printStats(evalSubtitle + ": overall", overallStats); - - return bestMapping.second; -} - -std::vector NodeIDAllocator::Clusterer::getReverseNodeMapping(const std::vector &nodeMapping) -{ - // nodeMapping.size() may not be big enough because we leave some gaps, but it's a start. - std::vector reverseNodeMapping(nodeMapping.size(), UINT_MAX); - for (size_t i = 0; i < nodeMapping.size(); ++i) - { - const NodeID mapsTo = nodeMapping.at(i); - if (mapsTo >= reverseNodeMapping.size()) reverseNodeMapping.resize(mapsTo + 1, UINT_MAX); - reverseNodeMapping.at(mapsTo) = i; - } - - return reverseNodeMapping; -} - -size_t NodeIDAllocator::Clusterer::condensedIndex(size_t n, size_t i, size_t j) -{ - // From https://stackoverflow.com/a/14839010 - return n*(n-1)/2 - (n-i)*(n-i-1)/2 + j - i - 1; -} - -unsigned NodeIDAllocator::Clusterer::requiredBits(const PointsTo &pts) -{ - return requiredBits(pts.count()); -} - -unsigned NodeIDAllocator::Clusterer::requiredBits(const size_t n) -{ - if (n == 0) return 0; - // Ceiling of number of bits amongst each native integer gives needed native ints, - // so we then multiply again by the number of bits in each native int. - return ((n - 1) / NATIVE_INT_SIZE + 1) * NATIVE_INT_SIZE; -} - -double *NodeIDAllocator::Clusterer::getDistanceMatrix(const std::vector> pointsToSets, - const size_t numObjects, const Map &nodeMap, - double &distanceMatrixTime) -{ - const double clkStart = PTAStat::getClk(true); - size_t condensedSize = (numObjects * (numObjects - 1)) / 2; - double *distMatrix = new double[condensedSize]; - for (size_t i = 0; i < condensedSize; ++i) distMatrix[i] = numObjects * numObjects; - - // TODO: maybe use machine epsilon? - // For reducing distance due to extra occurrences. - // Can differentiate ~9999 occurrences. - double occurrenceEpsilon = 0.0001; - - for (const std::pair &ptsOcc : pointsToSets) - { - const PointsTo *pts = ptsOcc.first; - assert(pts != nullptr); - const unsigned occ = ptsOcc.second; - - // Distance between each element of pts. - unsigned distance = requiredBits(*pts) / NATIVE_INT_SIZE; - - // Use a vector so we can index into pts. - std::vector ptsVec; - for (const NodeID o : *pts) ptsVec.push_back(o); - for (size_t i = 0; i < ptsVec.size(); ++i) + else if (strategy == Strategy::SEQ) { - const NodeID oi = ptsVec[i]; - const Map::const_iterator moi = nodeMap.find(oi); - assert(moi != nodeMap.end()); - for (size_t j = i + 1; j < ptsVec.size(); ++j) - { - const NodeID oj = ptsVec[j]; - const Map::const_iterator moj = nodeMap.find(oj); - assert(moj != nodeMap.end()); - double &existingDistance = distMatrix[condensedIndex(numObjects, moi->second, moj->second)]; - - // Subtract extra occurrenceEpsilon to make upcoming logic simpler. - // When existingDistance is never whole, it is always between two distances. - if (distance < existingDistance) existingDistance = distance - occurrenceEpsilon; - - if (distance == std::ceil(existingDistance)) - { - // We have something like distance == x, existingDistance == x - e, for some e < 1 - // (potentially even set during this iteration). - // So, the new distance is an occurrence the existingDistance being tracked, it just - // had some reductions because of multiple occurrences. - // If there is not room within this distance to reduce more (increase priority), - // just ignore it. TODO: maybe warn? - if (existingDistance - occ * occurrenceEpsilon > std::floor(existingDistance)) - { - existingDistance -= occ * occurrenceEpsilon; - } - else - { - // Reached minimum. - existingDistance = std::floor(existingDistance) + occurrenceEpsilon; - } - } - } + // Everything is sequential and intermixed. + id = numNodes; + } + else if (strategy == Strategy::DEBUG) + { + // Non-GEPs just grab the next available ID. + // We may have "holes" because GEPs increment the total + // but allocate far away. This is not a problem because + // we don't care about the relative distances between nodes. + id = numNodes; + } + else + { + assert(false && "NodeIDAllocator::allocateObjectId: unimplemented node allocation strategy."); } - } - - const double clkEnd = PTAStat::getClk(true); - distanceMatrixTime += (clkEnd - clkStart) / TIMEINTERVAL; - - return distMatrix; -} - -void NodeIDAllocator::Clusterer::traverseDendrogram(std::vector &nodeMap, const int *dendrogram, const size_t numObjects, unsigned &allocCounter, Set &visited, const int index, const std::vector ®ionNodeMap) -{ - if (visited.find(index) != visited.end()) return; - visited.insert(index); + ++numObjects; + ++numNodes; - int left = dendrogram[index - 1]; - if (left < 0) - { - // Reached a leaf. - // -1 because the items start from 1 per fastcluster (TODO). - nodeMap[regionNodeMap[std::abs(left) - 1]] = allocCounter; - ++allocCounter; - } - else - { - traverseDendrogram(nodeMap, dendrogram, numObjects, allocCounter, visited, left, regionNodeMap); + assert(id != 0 && "NodeIDAllocator::allocateObjectId: ID not allocated"); + return id; } - // Repeat for the right child. - int right = dendrogram[(numObjects - 1) + index - 1]; - if (right < 0) - { - nodeMap[regionNodeMap[std::abs(right) - 1]] = allocCounter; - ++allocCounter; - } - else + NodeID NodeIDAllocator::allocateGepObjectId(NodeID base, u32_t offset, u32_t maxFieldLimit) { - traverseDendrogram(nodeMap, dendrogram, numObjects, allocCounter, visited, right, regionNodeMap); - } -} - -std::vector NodeIDAllocator::Clusterer::regionObjects(const Map> &graph, size_t numObjects, size_t &numLabels) -{ - unsigned label = UINT_MAX; - std::vector labels(numObjects, UINT_MAX); - Set labelled; - for (const Map>::value_type &oos : graph) - { - const NodeID o = oos.first; - if (labels[o] != UINT_MAX) continue; - std::queue bfsQueue; - bfsQueue.push(o); - ++label; - while (!bfsQueue.empty()) + NodeID id = 0; + if (strategy == Strategy::DENSE) { - const NodeID o = bfsQueue.front(); - bfsQueue.pop(); - if (labels[o] != UINT_MAX) - { - assert(labels[o] == label); - continue; - } - - labels[o] = label; - Map>::const_iterator neighboursIt = graph.find(o); - assert(neighboursIt != graph.end()); - for (const NodeID neighbour : neighboursIt->second) bfsQueue.push(neighbour); + // Nothing different to the other case. + id = numObjects; } - } - - // The remaining objects have no relation with others: they get their own label. - for (size_t o = 0; o < numObjects; ++o) - { - if (labels[o] == UINT_MAX) labels[o] = ++label; - } - - numLabels = label + 1; - - return labels; -} - -void NodeIDAllocator::Clusterer::evaluate(const std::vector &nodeMap, const Map pointsToSets, Map &stats, bool accountForOcc) -{ - u64_t totalTheoretical = 0; - u64_t totalOriginalSbv = 0; - u64_t totalOriginalBv = 0; - u64_t totalNewSbv = 0; - u64_t totalNewBv = 0; - - for (const Map::value_type &ptsOcc : pointsToSets) - { - const PointsTo &pts = ptsOcc.first; - const unsigned occ = ptsOcc.second; - if (pts.count() == 0) continue; - - u64_t theoretical = requiredBits(pts) / NATIVE_INT_SIZE; - if (accountForOcc) theoretical *= occ; - - // Check number of words for original SBV. - Set words; - // TODO: nasty hardcoding. - for (const NodeID o : pts) words.insert(o / 128); - u64_t originalSbv = words.size() * 2; - if (accountForOcc) originalSbv *= occ; - - // Check number of words for original BV. - NodeID min = UINT_MAX; - NodeID max = 0; - for (NodeID o : pts) + else if (strategy == Strategy::SEQ) { - if (o < min) min = o; - if (o > max) max = o; + // Everything is sequential and intermixed. + id = numNodes; } - words.clear(); - for (NodeID b = min; b <= max; ++b) + else if (strategy == Strategy::DEBUG) { - words.insert(b / NATIVE_INT_SIZE); + // For a gep id, base id is set at lower bits, and offset is set at higher bits + // e.g., 1100050 denotes base=50 and offset=10 + // The offset is 10, not 11, because we add 1 to the offset to ensure that the + // high bits are never 0. For example, we do not want the gep id to be 50 when + // the base is 50 and the offset is 0. + NodeID gepMultiplier = pow(10, ceil(log10( + numSymbols > maxFieldLimit ? + numSymbols : maxFieldLimit + ))); + id = (offset + 1) * gepMultiplier + base; + assert(id > numSymbols && "NodeIDAllocator::allocateGepObjectId: GEP allocation clashing with other nodes"); } - u64_t originalBv = words.size(); - if (accountForOcc) originalBv *= occ; - - // Check number of words for new SBV. - words.clear(); - // TODO: nasty hardcoding. - for (const NodeID o : pts) words.insert(nodeMap[o] / 128); - u64_t newSbv = words.size() * 2; - if (accountForOcc) newSbv *= occ; - - // Check number of words for new BV. - min = UINT_MAX; - max = 0; - for (const NodeID o : pts) + else { - const NodeID mappedO = nodeMap[o]; - if (mappedO < min) min = mappedO; - if (mappedO > max) max = mappedO; + assert(false && "NodeIDAllocator::allocateGepObjectId: unimplemented node allocation strategy"); } - words.clear(); - // No nodeMap[b] because min and max and from nodeMap. - for (NodeID b = min; b <= max; ++b) words.insert(b / NATIVE_INT_SIZE); - u64_t newBv = words.size(); - if (accountForOcc) newBv *= occ; + ++numObjects; + ++numNodes; - totalTheoretical += theoretical; - totalOriginalSbv += originalSbv; - totalOriginalBv += originalBv; - totalNewSbv += newSbv; - totalNewBv += newBv; + assert(id != 0 && "NodeIDAllocator::allocateGepObjectId: ID not allocated"); + return id; } - stats[TheoreticalNumWords] = std::to_string(totalTheoretical); - stats[OriginalSbvNumWords] = std::to_string(totalOriginalSbv); - stats[OriginalBvNumWords] = std::to_string(totalOriginalBv); - stats[NewSbvNumWords] = std::to_string(totalNewSbv); - stats[NewBvNumWords] = std::to_string(totalNewBv); -} - -// Work out which of the mappings we generated looks best. -std::pair> NodeIDAllocator::Clusterer::determineBestMapping( - const std::vector>> &candidates, - Map pointsToSets, const std::string &evalSubtitle, double &evalTime) -{ - // In case we're not comparing anything, set to first "candidate". - std::pair> bestMapping = candidates[0]; - // Number of bits required for the best candidate. - size_t bestWords = std::numeric_limits::max(); - if (evalSubtitle != "" || Options::ClusterMethod() == HCLUST_METHOD_SVF_BEST) + NodeID NodeIDAllocator::allocateValueId(void) { - for (const std::pair> &candidate : candidates) + NodeID id = 0; + if (strategy == Strategy::DENSE) { - Map candidateStats; - hclust_fast_methods candidateMethod = candidate.first; - std::string candidateMethodName = SVFUtil::hclustMethodToString(candidateMethod); - std::vector candidateMapping = candidate.second; - - // TODO: parameterise final arg. - const double clkStart = PTAStat::getClk(true); - evaluate(candidateMapping, pointsToSets, candidateStats, true); - const double clkEnd = PTAStat::getClk(true); - evalTime += (clkEnd - clkStart) / TIMEINTERVAL; - printStats(evalSubtitle + ": candidate " + candidateMethodName, candidateStats); - - size_t candidateWords = 0; - if (Options::PtType() == PointsTo::SBV) candidateWords = std::stoull(candidateStats[NewSbvNumWords]); - else if (Options::PtType() == PointsTo::CBV) candidateWords = std::stoull(candidateStats[NewBvNumWords]); - else assert(false && "Clusterer::cluster: unsupported BV type for clustering."); - - if (candidateWords < bestWords) - { - bestWords = candidateWords; - bestMapping = candidate; - } + // We allocate values from UINT_MAX to UINT_MAX - # of values. + // TODO: UINT_MAX does not allow for an easily changeable type + // of NodeID (though it is already in use elsewhere). + id = UINT_MAX - numValues; + } + else if (strategy == Strategy::SEQ) + { + // Everything is sequential and intermixed. + id = numNodes; + } + else if (strategy == Strategy::DEBUG) + { + id = numNodes; + } + else + { + assert(false && "NodeIDAllocator::allocateValueId: unimplemented node allocation strategy"); } - } - return bestMapping; -} + ++numValues; + ++numNodes; -void NodeIDAllocator::Clusterer::printStats(std::string subtitle, Map &stats) -{ - // When not in order, it is too hard to compare original/new SBV/BV words, so this array forces an order. - static const std::string statKeys[] = - { - NumObjects, TheoreticalNumWords, OriginalSbvNumWords, OriginalBvNumWords, - NewSbvNumWords, NewBvNumWords, NumRegions, NumGtIntRegions, - NumNonTrivialRegionObjects, LargestRegion, RegioningTime, - DistanceMatrixTime, FastClusterTime, DendrogramTraversalTime, - EvalTime, TotalTime, BestCandidate - }; + assert(id != 0 && "NodeIDAllocator::allocateValueId: ID not allocated"); + return id; + } - const unsigned fieldWidth = 20; - SVFUtil::outs().flags(std::ios::left); - SVFUtil::outs() << "****Clusterer Statistics: " << subtitle << "****\n"; - for (const std::string& statKey : statKeys) + void NodeIDAllocator::endSymbolAllocation(void) { - Map::const_iterator stat = stats.find(statKey); - if (stat != stats.end()) - { - SVFUtil::outs() << std::setw(fieldWidth) << statKey << " " << stat->second << "\n"; - } + numSymbols = numNodes; } - SVFUtil::outs().flush(); -} - -}; // namespace SVF. +} // namespace SVF. diff --git a/svf/lib/Util/Options.cpp b/svf/lib/Util/Options.cpp index 2ca5a5977..45da74b47 100644 --- a/svf/lib/Util/Options.cpp +++ b/svf/lib/Util/Options.cpp @@ -1,872 +1,804 @@ //===- Options.cpp -- Command line options ------------------------// +#include #include "Util/Options.h" -#include "Util/CommandLine.h" -#include "Util/ExtAPI.h" namespace SVF { -const OptionMap Options::ClockType( - "clock-type", - "how time should be measured", - PTAStat::ClockType::CPU, -{ - {PTAStat::ClockType::Wall, "wall", "use wall time"}, - {PTAStat::ClockType::CPU, "cpu", "use CPU time"}, -} -); - -const Option Options::MarkedClocksOnly( - "marked-clocks-only", - "Only measure times where explicitly marked", - true -); - -const OptionMap Options::NodeAllocStrat( - "node-alloc-strat", - "Method of allocating (LLVM) values and memory objects as node IDs", - NodeIDAllocator::Strategy::SEQ, -{ - {NodeIDAllocator::Strategy::DENSE, "dense", "allocate objects together [0-n] and values together [m-MAX], separately"}, - {NodeIDAllocator::Strategy::REVERSE_DENSE, "reverse-dense", "like dense but flipped, objects are [m-MAX], values are [0-n]"}, - {NodeIDAllocator::Strategy::SEQ, "seq", "allocate values and objects sequentially, intermixed (default)"}, - {NodeIDAllocator::Strategy::DBUG, "debug", "allocate value and objects sequentially, intermixed, except GEP objects as offsets"}, -} -); - -const Option Options::MaxFieldLimit( - "field-limit", - "Maximum number of fields for field sensitive analysis", - 512 -); - -const OptionMap Options::ptDataBacking( - "ptd", - "Overarching points-to data structure", - BVDataPTAImpl::PTBackingType::Persistent, -{ - {BVDataPTAImpl::PTBackingType::Mutable, "mutable", "points-to set per pointer"}, - {BVDataPTAImpl::PTBackingType::Persistent, "persistent", "points-to set ID per pointer, operations hash-consed"}, -} -); - -const Option Options::FsTimeLimit( - "fs-time-limit", - "time limit for main phase of flow-sensitive analyses", - 0 -); - -const Option Options::VersioningThreads( - "versioning-threads", - "number of threads to use in the versioning phase of versioned flow-sensitive analysis", - 1 -); - -const Option Options::AnderTimeLimit( - "ander-time-limit", - "time limit for Andersen's analyses (ignored when -fs-time-limit set)", - 0 -); - -// ContextDDA.cpp -const Option Options::CxtBudget( - "cxt-bg", - "Maximum step budget of context-sensitive traversing", - 10000 -); - -// DDAPass.cpp -const Option Options::MaxPathLen( - "max-path", - "Maximum path limit for DDA", - 100000 -); - -const Option Options::MaxContextLen( - "max-cxt", - "Maximum context limit for DDA", - 3 -); - -const Option Options::MaxStepInWrapper( - "max-step", - "Maximum steps when traversing on SVFG to identify a memory allocation wrapper", - 10 -); - -const Option Options::UserInputQuery( - "query", - "Please specify queries by inputing their pointer ids", - "all" -); - -const Option Options::InsenRecur( - "in-recur", - "Mark context insensitive SVFG edges due to function recursions", - false -); - -const Option Options::InsenCycle( - "in-cycle", - "Mark context insensitive SVFG edges due to value-flow cycles", - false -); - -const Option Options::PrintCPts( - "cpts", - "Dump conditional points-to set ", - false -); - -const Option Options::PrintQueryPts( - "print-query-pts", - "Dump queries' conditional points-to set ", - false -); - -const Option Options::WPANum( - "wpa-num", - "collect WPA FS number only ", - false -); - -/// register this into alias analysis group -//static RegisterAnalysisGroup AA_GROUP(DDAPA); -OptionMultiple Options::DDASelected( - "Select pointer analysis", -{ - {PointerAnalysis::FlowS_DDA, "dfs", "Demand-driven flow sensitive analysis"}, - {PointerAnalysis::Cxt_DDA, "cxt", "Demand-driven context- flow- sensitive analysis"}, -} -); - -// FlowDDA.cpp -const Option Options::FlowBudget( - "flow-bg", - "Maximum step budget of flow-sensitive traversing", - 10000 -); - - -// Offline constraint graph (OfflineConsG.cpp) -const Option Options::OCGDotGraph( - "dump-ocg", - "Dump dot graph of Offline Constraint Graph", - false -); - - -// Program Assignment Graph for pointer analysis (SVFIR.cpp) -Option Options::HandBlackHole( - "blk", - "Handle blackhole edge", - false -); - -const Option Options::FirstFieldEqBase( - "ff-eq-base", - "Treat base objects as their first fields", - false -); - - -// SVFG optimizer (SVFGOPT.cpp) -const Option Options::ContextInsensitive( - "ci-svfg", - "Reduce SVFG into a context-insensitive one", - false -); - -const Option Options::KeepAOFI( - "keep-aofi", - "Keep formal-in and actual-out parameters", - false -); - -const Option Options::SelfCycle( - "keep-self-cycle", - "How to handle self cycle edges: all, context, none", - "" -); - - -// Sparse value-flow graph (VFG.cpp) -const Option Options::DumpVFG( - "dump-vfg", - "Dump dot graph of VFG", - false -); - - -// Base class of pointer analyses (PointerAnalysis.cpp) -const Option Options::TypePrint( - "print-type", - "Print type", - false -); - -const Option Options::FuncPointerPrint( - "print-fp", - "Print targets of indirect call site", - false -); - -const Option Options::PTSPrint( - "print-pts", - "Print points-to set of top-level pointers", - false -); - -const Option Options::PrintFieldWithBasePrefix( - "print-field", - "Print field object with base object id as the prefix", - false -); - -const Option Options::PTSAllPrint( - "print-all-pts", - "Print all points-to set of both top-level and address-taken variables", - false -); - -const Option Options::PStat( - "stat", - "Statistic for Pointer analysis", - true -); - -const Option Options::StatBudget( - "stat-limit", - "Iteration budget for On-the-fly statistics", - 20 -); - -const Option Options::PAGDotGraph( - "dump-pag", - "Dump dot graph of SVFIR", - false -); - -const Option Options::ShowSVFIRValue( - "show-ir-value", - "Show values of SVFIR (e.g., when generating dot graph)", - true -); - -const Option Options::DumpICFG( - "dump-icfg", - "Dump dot graph of ICFG", - false -); - -const Option Options::DumpJson( - "dump-json", - "Dump the SVFIR in JSON format", - "" -); - -const Option Options::ReadJson( - "read-json", - "Read the SVFIR in JSON format", - false -); - -const Option Options::CallGraphDotGraph( - "dump-callgraph", - "Dump dot graph of Call Graph", - false -); - -const Option Options::PAGPrint( - "print-pag", - "Print SVFIR to command line", - false -); - -const Option Options::IndirectCallLimit( - "ind-call-limit", - "Indirect solved call edge limit", - 50000 -); - -const Option Options::UsePreCompFieldSensitive( - "pre-field-sensitive", - "Use pre-computed field-sensitivity for later analysis", - true -); - -const Option Options::EnableAliasCheck( - "alias-check", - "Enable alias check functions", - true -); - -const Option Options::EnableTypeCheck( - "type-check", - "Enable type check functions", - true -); - -const Option Options::EnableThreadCallGraph( - "enable-tcg", - "Enable pointer analysis to use thread call graph", - true -); - -const Option Options::ConnectVCallOnCHA( - "v-call-cha", - "connect virtual calls using cha", - false -); - - -// PointerAnalysisImpl.cpp -const Option Options::INCDFPTData( - "inc-data", - "Enable incremental DFPTData for flow-sensitive analysis", - true -); - -const Option Options::ClusterAnder( - "cluster-ander", - "Stage Andersen's with Steensgard's and cluster based on that", - false -); - -const Option Options::ClusterFs( - "cluster-fs", - "Cluster for FS/VFS with auxiliary Andersen's", - false -); - -const Option Options::PlainMappingFs( - "plain-mapping-fs", - "Use an explicitly (not null) plain mapping for FS", - false -); - -const OptionMap Options::PtType( - "pt-type", - "points-to set data structure to use in all analyses", - PointsTo::Type::SBV, -{ - {PointsTo::Type::SBV, "sbv", "sparse bit-vector"}, - {PointsTo::Type::CBV, "cbv", "core bit-vector (dynamic bit-vector without leading and trailing 0s)"}, - {PointsTo::Type::BV, "bv", "bit-vector (dynamic bit-vector without trailing 0s)"}, -} -); - -const OptionMap Options::ClusterMethod( - "cluster-method", - "hierarchical clustering method for objects", - HCLUST_METHOD_SVF_BEST, -{ - {HCLUST_METHOD_SINGLE, "single", "single linkage; minimum spanning tree algorithm"}, - {HCLUST_METHOD_COMPLETE, "complete", "complete linkage; nearest-neighbour-chain algorithm"}, - {HCLUST_METHOD_AVERAGE, "average", "unweighted average linkage; nearest-neighbour-chain algorithm"}, - {HCLUST_METHOD_SVF_BEST, "best", "try all linkage criteria; choose best"}, -} -); - -const Option Options::RegionedClustering( - // Use cluster to "gather" the options closer together, even if it sounds a little worse. - "cluster-regioned", - "cluster regions separately", - true -); - -const Option Options::RegionAlign( - "cluster-region-aligned", - "align each region's identifiers to the native word size", - true -); - -const Option Options::PredictPtOcc( - "cluster-predict-occ", - "try to predict which points-to sets are more important in staged analysis", - false -); - -// Memory region (MemRegion.cpp) -const Option Options::IgnoreDeadFun( - "mssa-ignore-dead-fun", - "Don't construct memory SSA for deadfunction", - false -); - - -// Base class of pointer analyses (MemSSA.cpp) -const Option Options::DumpMSSA( - "dump-mssa", - "Dump memory SSA", - false -); - -const Option Options::MSSAFun( - "mssa-fun", - "Please specify which function needs to be dumped", - "" -); - -const OptionMap Options::MemPar( - "mem-par", - "Memory region partition strategies (e.g., for SVFG construction)", - MemSSA::MemPartition::IntraDisjoint, -{ - {MemSSA::MemPartition::Distinct, "distinct", "memory region per each object"}, - {MemSSA::MemPartition::IntraDisjoint, "intra-disjoint", "memory regions partitioned based on each function"}, - {MemSSA::MemPartition::InterDisjoint, "inter-disjoint", "memory regions partitioned across functions"}, -} -); - - -// SVFG builder (SVFGBuilder.cpp) -const Option Options::SVFGWithIndirectCall( - "svfg-with-ind-call", - "Update Indirect Calls for SVFG using pre-analysis", - false -); - -Option Options::OPTSVFG( - "opt-svfg", - "Optimize SVFG to eliminate formal-in and actual-out", - false -); - -const Option Options::WriteSVFG( - "write-svfg", - "Write SVFG's analysis results to a file", - "" -); - -const Option Options::ReadSVFG( - "read-svfg", - "Read SVFG's analysis results from a file", - "" -); - -// FSMPTA.cpp -const Option Options::UsePCG( - "pcg-td-edge", - "Use PCG lock for non-sparsely adding SVFG edges", - false -); - -const Option Options::IntraLock( - "intra-lock-td-edge", - "Use simple intra-procedural lock for adding SVFG edges", - true -); - -const Option Options::ReadPrecisionTDEdge( - "rp-td-edge", - "perform read precision to refine SVFG edges", - false -); - -const Option Options::AddModelFlag( - "add-td-edge", - "Add thread SVFG edges with models: 0 Non Add Edge; 1 NonSparse; 2 All Optimisation; 3 No MHP; 4 No Alias; 5 No Lock; 6 No Read Precision.", - 0 -); - - -// LockAnalysis.cpp -const Option Options::PrintLockSpan( - "print-lock", - "Print Thread Interleaving Results", - false -); - - -// MHP.cpp -const Option Options::PrintInterLev( - "print-interlev", - "Print Thread Interleaving Results", - false -); - -const Option Options::DoLockAnalysis( - "lock-analysis", - "Run Lock Analysis", - true -); - - -// MTA.cpp -const Option Options::AndersenAnno( - "tsan-ander", - "Add TSan annotation according to Andersen", - false -); - -const Option Options::FSAnno( - "tsan-fs", - "Add TSan annotation according to flow-sensitive analysis", - false -); - - -// MTAAnnotator.cpp -const Option Options::AnnoFlag( - "anno", - "prune annotated instructions: 0001 Thread Local; 0002 Alias; 0004 MHP.", - 0 -); - - -// MTAResultValidator.cpp -const Option Options::PrintValidRes( - "mhp-validation", - "Print MHP Validation Results", - false -); -// LockResultValidator.cpp -const Option Options::LockValid( - "lock-validation", - "Print Lock Validation Results", - false -); - - -// MTAStat.cpp -const Option Options::AllPairMHP( - "all-pair-mhp", - "All pair MHP computation", - false -); - - -// PCG.cpp -//const Option TDPrint( -// "print-td", -// "Print Thread Analysis Results", -// true -//); - - -// TCT.cpp -const Option Options::TCTDotGraph( - "dump-tct", - "Dump dot graph of Call Graph", - false -); - - -// LeakChecker.cpp -const Option Options::ValidateTests( - "valid-tests", - "Validate memory leak tests", - false -); - - -// Source-sink analyzer (SrcSnkDDA.cpp) -const Option Options::DumpSlice( - "dump-slice", - "Dump dot graph of Saber Slices", - false -); - -const Option Options::CxtLimit( - "cxt-limit", - "Source-Sink Analysis Contexts Limit", - 3 -); - - -// CHG.cpp -const Option Options::DumpCHA( - "dump-cha", - "dump the class hierarchy graph", - false -); - - -// DCHG.cpp -const Option Options::PrintDCHG( - "print-dchg", - "print the DCHG if debug information is available", - false -); - - -// LLVMModule.cpp -const Option Options::Graphtxt( - "graph-txt", - "graph txt file to build SVFIR", - "" -); - -const Option Options::SVFMain( - "svf-main", - "add svf.main()", - false -); - -const Option Options::ModelConsts( - "model-consts", - "Modeling individual constant objects", - false -); - -const Option Options::ModelArrays( - "model-arrays", - "Modeling Gep offsets for array accesses", - false -); - -const Option Options::CyclicFldIdx( - "cyclic-field-index", - "Enable cyclic field index when generating field objects using modulus offset", - false -); - -const Option Options::SymTabPrint( - "print-symbol-table", - "Print Symbol Table to command line", - false -); - -// Conditions.cpp -const Option Options::MaxZ3Size( - "max-z3-size", - "Maximum size limit for Z3 expression", - 30 -); - -// BoundedZ3Expr.cpp -const Option Options::MaxBVLen( - "max-bv-len", - "Maximum length limit for Z3 bitvector", - 64 -); - - - -// SaberCondAllocator.cpp -const Option Options::PrintPathCond( - "print-pc", - "Print out path condition", - false -); - - -// SaberSVFGBuilder.cpp -const Option Options::CollectExtRetGlobals( - "saber-collect-extret-globals", - "Don't include pointers returned by external function during collecting globals", - true -); - - -// SVFUtil.cpp -const Option Options::DisableWarn( - "dwarn", - "Disable warning", - true -); - - -// Andersen.cpp -const Option Options::ConsCGDotGraph( - "dump-constraint-graph", - "Dump dot graph of Constraint Graph", - false -); - -const Option Options::BriefConsCGDotGraph( - "brief-constraint-graph", - "Dump dot graph of Constraint Graph", - true -); - -const Option Options::PrintCGGraph( - "print-constraint-graph", - "Print Constraint Graph to Terminal", - false -); - -const Option Options::WriteAnder( - "write-ander", - "-write-ander=ir_annotator (Annotated IR with Andersen's results) or write Andersen's analysis results to a user-specified text file", - "" -); - -const Option Options::ReadAnder( - "read-ander", - "-read-ander=ir_annotator (Read Andersen's analysis results from the annotated IR, e.g., *.pre.bc) or from a text file", - "" -); - -const Option Options::DiffPts( - "diff", - "Enable differential point-to set", - true -); - -Option Options::DetectPWC( - "merge-pwc", - "Enable PWC detection", - true -); - -//SVFIRBuilder.cpp -const Option Options::VtableInSVFIR( - "vt-in-ir", - "Handle vtable in ConstantArray/ConstantStruct in SVFIRBuilder (already handled in CHA?)", - false -); - -//WPAPass.cpp -const Option Options::ExtAPIPath( - "extapi", - "External API extapi.bc", - "" -); - -const Option Options::AnderSVFG( - "svfg", - "Generate SVFG after Andersen's Analysis", - false -); - -const Option Options::SABERFULLSVFG( - "saber-full-svfg", - "When using SABER for bug detection pass, enable full svfg on top of the pointer-only one", - false -); - -const Option Options::PrintAliases( - "print-aliases", - "Print results for all pair aliases", - false -); - -OptionMultiple Options::PASelected( - "Select pointer analysis", -{ - {PointerAnalysis::Andersen_WPA, "nander", "Standard inclusion-based analysis"}, - {PointerAnalysis::AndersenSCD_WPA, "sander", "Selective cycle detection inclusion-based analysis"}, - {PointerAnalysis::AndersenSFR_WPA, "sfrander", "Stride-based field representation inclusion-based analysis"}, - {PointerAnalysis::AndersenWaveDiff_WPA, "ander", "Diff wave propagation inclusion-based analysis"}, - {PointerAnalysis::Steensgaard_WPA, "steens", "Steensgaard's pointer analysis"}, - // Disabled till further work is done. - {PointerAnalysis::FSSPARSE_WPA, "fspta", "Sparse flow sensitive pointer analysis"}, - {PointerAnalysis::VFS_WPA, "vfspta", "Versioned sparse flow-sensitive points-to analysis"}, - {PointerAnalysis::TypeCPP_WPA, "type", "Type-based fast analysis for Callgraph, SVFIR and CHA"}, -} -); - - -OptionMultiple Options::AliasRule( - "Select alias check rule", -{ - {WPAPass::Conservative, "conservative", "return MayAlias if any pta says alias"}, - {WPAPass::Veto, "veto", "return NoAlias if any pta says no alias"}, -} -); - -const Option Options::ShowHiddenNode( - "show-hidden-nodes", - "Show hidden nodes on DOT Graphs (e.g., isolated node on a graph)", - false -); - -const Option Options::GrammarFilename( - "grammar", - "", - "" -); - -const Option Options::CFLGraph( - "cflgraph", - "", - "" -); - -const Option Options::PrintCFL( - "print-cfl", - "Print ir, grammar and cflgraph for debug.", - false -); - -const Option Options::FlexSymMap( - "flex-symmap", - "Extend exist sym map while read graph from dot if sym not in map.", - false -); - -const Option Options::PEGTransfer( - "peg-transfer", - "When explicit to true, cfl graph builder will transfer PAG load and store edges to copy and addr.", - false -); - -const Option Options::CFLSVFG( - "cflsvfg", - "When explicit to true, cfl graph builder will transfer SVFG to CFL Reachability.", - false -); - -const Option Options::POCRAlias( - "pocr-alias", - "When explicit to true, cfl data builder will transfer CFL graph to CFLData.", - false -); - -const Option Options::POCRHybrid( - "pocr-hybrid", - "When explicit to true, POCRHybridSolver transfer CFL graph to internal hybrid graph representation.", - false -); - -const Option Options::Customized( - "customized", - "When explicit to true, user can use any grammar file.", - false -); - -const Option Options::LoopAnalysis( - "loop-analysis", - "Analyze every func and get loop info and loop bounds.", - true -); - -const Option Options::LoopBound( - "loop-bound", - "Maximum number of loop", - 1 -); - -const Option Options::WidenDelay( - "widen-delay", "Loop Widen Delay", 3); -const Option Options::Timeout( - "timeout", "time out (seconds), set -1 (no timeout), default 14400s",14400); -const Option Options::OutputName( - "output","output db file","output.db"); -const Option Options::BufferOverflowCheck( - "overflow","Buffer Overflow Detection",false); -const Option Options::MemoryLeakCheck( - "leak", "Memory Leak Detection",false); -const Option Options::FileCheck( - "fileck", "File Open/Close Detection",false); -const Option Options::DFreeCheck( - "dfree", "Double Free Detection",false); -const Option Options::RaceCheck( - "race", "Data race Detection",false); -const Option Options::GepUnknownIdx( - "gep-unknown-idx","Skip Gep Unknown Index",false); -const Option Options::RunUncallFuncs( - "run-uncall-fun","Skip Gep Unknown Index",false); -const Option Options::ICFGMergeAdjacentNodes( - "icfg-merge-adjnodes","ICFG Simplification - Merge Adjacent Nodes in the Same Basic Block.",false); - - -const Option Options::AEPrecision( - "precision", - "symbolic abstraction precision for float", - 0 -); + llvm::cl::opt Options::RemoveThres( + "remove-thres", + llvm::cl::init(-1), + llvm::cl::desc("Remove top N popular functions")); + + + llvm::cl::list + Options::DebugFuncsList("debug-funcs", llvm::cl::value_desc("function names to debug"), + llvm::cl::desc("Only debug the functions listed here"), + llvm::cl::CommaSeparated, llvm::cl::Hidden); + + llvm::cl::opt Options::DumpCycle( + "dump-cycle", + llvm::cl::init(false), + llvm::cl::desc("Dump Cycle")); + + llvm::cl::opt Options::ShortCircuit( + "short-circuit", + llvm::cl::init(true), + llvm::cl::desc("Short circuit")); + + /* + llvm::cl::opt Options::PreventCollapseExplosion( + "prevent-collapse-explosion", + llvm::cl::init(true), + llvm::cl::desc("Prevent the explosion at the collapse")); + */ + + llvm::cl::opt Options::LogAll( + "log-all", + llvm::cl::init(false), + llvm::cl::desc("Log everything")); + + llvm::cl::opt Options::DumpCFIStat( + "dump-cfi-stat", + llvm::cl::init(true), + llvm::cl::desc("Dump CFI Stat")); + + llvm::cl::opt Options::InvariantVGEP( + "invariant-vgep", + llvm::cl::init(true), + llvm::cl::desc("Handle invariants for the vgep")); + + /* + const llvm::cl::opt Options::Kaleidoscope( + "kaleidoscope", + llvm::cl::init(false), + llvm::cl::desc("Analyze with the kaleidoscope system")); + + + const llvm::cl::opt Options::KaliBreakNullTypeEdges( + "kali-break-null-type-edges", + llvm::cl::init(true), + llvm::cl::desc("Kaleidoscope: compare null types")); + + + const llvm::cl::opt Options::HandleVGEP( + "handle-vgep", + llvm::cl::init(true), + llvm::cl::desc("Handle VGEP")); + */ + + + llvm::cl::opt Options::InvariantPWC( + "invariant-pwc", + llvm::cl::init(true), + llvm::cl::desc("Handle invariants for PWC")); + + llvm::cl::opt Options::ApplyCFI( + "apply-cfi", + llvm::cl::init(false), + llvm::cl::desc("Apply CFI")); + + llvm::cl::opt Options::NoInvariants( + "no-invariants", + llvm::cl::init(false), + llvm::cl::desc("Don't handle invariants")); + + + const llvm::cl::opt Options::KaliRunTestDriver( + "kali-run-test-driver", + llvm::cl::init(false), + llvm::cl::desc("Kaleidoscope: Kali run test driver")); + + const llvm::cl::opt Options::MarkedClocksOnly( + "marked-clocks-only", + llvm::cl::init(false), + llvm::cl::desc("Only measure times where explicitly marked")); + + const llvm::cl::opt Options::NodeAllocStrat( + "node-alloc-strat", + llvm::cl::init(NodeIDAllocator::Strategy::DEBUG), + llvm::cl::desc("Method of allocating (LLVM) values and memory objects as node IDs"), + llvm::cl::values( + clEnumValN(NodeIDAllocator::Strategy::DENSE, "dense", "allocate objects together and values together, separately"), + clEnumValN(NodeIDAllocator::Strategy::SEQ, "seq", "allocate values and objects sequentially, intermixed"), + clEnumValN(NodeIDAllocator::Strategy::DEBUG, "debug", "allocate value and objects sequentially, intermixed, except GEP objects as offsets (default)"))); + + const llvm::cl::opt Options::MaxFieldLimit( + "field-limit", + llvm::cl::init(512), + llvm::cl::desc("Maximum number of fields for field sensitive analysis")); + + const llvm::cl::opt Options::ptDataBacking( + "ptd", + llvm::cl::init(BVDataPTAImpl::PTBackingType::Mutable), + llvm::cl::desc("Overarching points-to data structure"), + llvm::cl::values( + clEnumValN(BVDataPTAImpl::PTBackingType::Mutable, "mutable", "points-to set per pointer"), + clEnumValN(BVDataPTAImpl::PTBackingType::Persistent, "persistent", "points-to set ID per pointer, operations hash-consed"))); + + const llvm::cl::opt Options::FsTimeLimit( + "fs-time-limit", + llvm::cl::init(0), + llvm::cl::desc("time limit for main phase of flow-sensitive analyses") + ); + + const llvm::cl::opt Options::AnderTimeLimit( + "ander-time-limit", + llvm::cl::init(0), + llvm::cl::desc("time limit for Andersen's analyses (ignored when -fs-time-limit set)") + ); + + // ContextDDA.cpp + const llvm::cl::opt Options::CxtBudget( + "cxt-bg", + llvm::cl::init(10000), + llvm::cl::desc("Maximum step budget of context-sensitive traversing") + ); + + + // DDAClient.cpp + const llvm::cl::opt Options::SingleLoad( + "single-load", + llvm::cl::init(true), + llvm::cl::desc("Count load pointer with same source operand as one query") + ); + + const llvm::cl::opt Options::DumpFree( + "dump-free", + llvm::cl::init(false), + llvm::cl::desc("Dump use after free locations") + ); + + const llvm::cl::opt Options::DumpUninitVar( + "dump-uninit-var", + llvm::cl::init(false), + llvm::cl::desc("Dump uninitialised variables") + ); + + const llvm::cl::opt Options::DumpUninitPtr( + "dump-uninit-ptr", + llvm::cl::init(false), + llvm::cl::desc("Dump uninitialised pointers") + ); + + const llvm::cl::opt Options::DumpSUPts( + "dump-su-pts", + llvm::cl::init(false), + llvm::cl::desc("Dump strong updates store") + ); + + const llvm::cl::opt Options::DumpSUStore( + "dump-su-store", + llvm::cl::init(false), + llvm::cl::desc("Dump strong updates store") + ); + + const llvm::cl::opt Options::MallocOnly( + "malloc-only", + llvm::cl::init(true), + llvm::cl::desc("Only add tainted objects for malloc") + ); + + const llvm::cl::opt Options::TaintUninitHeap( + "uninit-heap", + llvm::cl::init(true), + llvm::cl::desc("detect uninitialized heap variables") + ); + + const llvm::cl::opt Options::TaintUninitStack( + "uninit-stack", + llvm::cl::init(true), + llvm::cl::desc("detect uninitialized stack variables") + ); + + // DDAPass.cpp + const llvm::cl::opt Options::MaxPathLen( + "max-path", + llvm::cl::init(100000), + llvm::cl::desc("Maximum path limit for DDA") + ); + + const llvm::cl::opt Options::MaxContextLen( + "max-cxt", + llvm::cl::init(3), + llvm::cl::desc("Maximum context limit for DDA") + ); + + const llvm::cl::opt Options::MaxStepInWrapper( + "max-step", + llvm::cl::init(10), + llvm::cl::desc("Maximum steps when traversing on SVFG to identify a memory allocation wrapper") + ); + + const llvm::cl::opt Options::UserInputQuery( + "query", + llvm::cl::init("all"), + llvm::cl::desc("Please specify queries by inputing their pointer ids") + ); + + const llvm::cl::opt Options::InsenRecur( + "in-recur", + llvm::cl::init(false), + llvm::cl::desc("Mark context insensitive SVFG edges due to function recursions") + ); + + const llvm::cl::opt Options::InsenCycle( + "in-cycle", + llvm::cl::init(false), + llvm::cl::desc("Mark context insensitive SVFG edges due to value-flow cycles") + ); + + const llvm::cl::opt Options::PrintCPts( + "cpts", + llvm::cl::init(false), + llvm::cl::desc("Dump conditional points-to set ") + ); + + const llvm::cl::opt Options::PrintQueryPts( + "print-query-pts", + llvm::cl::init(false), + llvm::cl::desc("Dump queries' conditional points-to set ") + ); + + const llvm::cl::opt Options::WPANum( + "wpa-num", + llvm::cl::init(false), + llvm::cl::desc("collect WPA FS number only ") + ); + + /// register this into alias analysis group + //static RegisterAnalysisGroup AA_GROUP(DDAPA); + llvm::cl::bits Options::DDASelected( + llvm::cl::desc("Select pointer analysis"), + llvm::cl::values( + clEnumValN(PointerAnalysis::FlowS_DDA, "dfs", "Demand-driven flow sensitive analysis"), + clEnumValN(PointerAnalysis::Cxt_DDA, "cxt", "Demand-driven context- flow- sensitive analysis") + )); + + // FlowDDA.cpp + const llvm::cl::opt Options::FlowBudget( + "flow-bg", + llvm::cl::init(10000), + llvm::cl::desc("Maximum step budget of flow-sensitive traversing") + ); + + + // Offline constraint graph (OfflineConsG.cpp) + const llvm::cl::opt Options::OCGDotGraph( + "dump-ocg", + llvm::cl::init(false), + llvm::cl::desc("Dump dot graph of Offline Constraint Graph") + ); + + + // Program Assignment Graph for pointer analysis (PAG.cpp) + llvm::cl::opt Options::HandBlackHole( + "blk", + llvm::cl::init(false), + llvm::cl::desc("Hanle blackhole edge") + ); + + const llvm::cl::opt Options::FirstFieldEqBase( + "ff-eq-base", + llvm::cl::init(true), + llvm::cl::desc("Treat base objects as their first fields") + ); + + + // SVFG optimizer (SVFGOPT.cpp) + const llvm::cl::opt Options::ContextInsensitive( + "ci-svfg", + llvm::cl::init(false), + llvm::cl::desc("Reduce SVFG into a context-insensitive one") + ); + + const llvm::cl::opt Options::KeepAOFI( + "keep-aofi", + llvm::cl::init(false), + llvm::cl::desc("Keep formal-in and actual-out parameters") + ); + + const llvm::cl::opt Options::SelfCycle( + "keep-self-cycle", + llvm::cl::value_desc("keep self cycle"), + llvm::cl::desc("How to handle self cycle edges: all, context, none") + ); + + + // Sparse value-flow graph (VFG.cpp) + const llvm::cl::opt Options::DumpVFG( + "dump-vfg", + llvm::cl::init(false), + llvm::cl::desc("Dump dot graph of VFG") + ); + + + // Location set for modeling abstract memory object (LocationSet.cpp) + const llvm::cl::opt Options::SingleStride( + "stride-only", + llvm::cl::init(false), + llvm::cl::desc("Only use single stride in LocMemoryModel") + ); + + + // Base class of pointer analyses (PointerAnalysis.cpp) + const llvm::cl::opt Options::TypePrint( + "print-type", + llvm::cl::init(false), + llvm::cl::desc("Print type") + ); + + const llvm::cl::opt Options::FuncPointerPrint( + "print-fp", + llvm::cl::init(false), + llvm::cl::desc("Print targets of indirect call site") + ); + + const llvm::cl::opt Options::PTSPrint( + "print-pts", + llvm::cl::init(false), + llvm::cl::desc("Print points-to set of top-level pointers") + ); + + const llvm::cl::opt Options::PTSAllPrint( + "print-all-pts", + llvm::cl::init(false), + llvm::cl::desc("Print all points-to set of both top-level and address-taken variables") + ); + + const llvm::cl::opt Options::PStat( + "stat", + llvm::cl::init(true), + llvm::cl::desc("Statistic for Pointer analysis") + ); + + const llvm::cl::opt Options::StatBudget( + "stat-limit", + llvm::cl::init(200), + llvm::cl::desc("Iteration budget for On-the-fly statistics") + ); + + const llvm::cl::opt Options::PAGDotGraph( + "dump-pag", + llvm::cl::init(false), + llvm::cl::desc("Dump dot graph of PAG") + ); + + const llvm::cl::opt Options::PAGDotGraphShorter( + "dump-pag-shorter", + llvm::cl::init(true), + llvm::cl::desc("If dumping dot graph of PAG, use shorter lines") + ); + + const llvm::cl::opt Options::DumpICFG( + "dump-icfg", + llvm::cl::init(false), + llvm::cl::desc("Dump dot graph of ICFG") + ); + + const llvm::cl::opt Options::CallGraphDotGraph( + "dump-callgraph", + llvm::cl::init(false), + llvm::cl::desc("Dump dot graph of Call Graph") + ); + + const llvm::cl::opt Options::PAGPrint( + "print-pag", + llvm::cl::init(false), + llvm::cl::desc("Print PAG to command line") + ); + + const llvm::cl::opt Options::IndirectCallLimit( + "ind-call-limit", + llvm::cl::init(500000), + llvm::cl::desc("Indirect solved call edge limit") + ); + + const llvm::cl::opt Options::UsePreCompFieldSensitive( + "pre-field-sensitive", + llvm::cl::init(true), + llvm::cl::desc("Use pre-computed field-sensitivity for later analysis") + ); + + const llvm::cl::opt Options::EnableAliasCheck( + "alias-check", + llvm::cl::init(true), + llvm::cl::desc("Enable alias check functions") + ); + + const llvm::cl::opt Options::EnableThreadCallGraph( + "enable-tcg", + llvm::cl::init(true), + llvm::cl::desc("Enable pointer analysis to use thread call graph") + ); + + const llvm::cl::opt Options::ConnectVCallOnCHA( + "v-call-cha", + llvm::cl::init(false), + llvm::cl::desc("connect virtual calls using cha") + ); + + + // PointerAnalysisImpl.cpp + const llvm::cl::opt Options::INCDFPTData( + "inc-data", + llvm::cl::init(true), + llvm::cl::desc("Enable incremental DFPTData for flow-sensitive analysis") + ); + + + // Memory region (MemRegion.cpp) + const llvm::cl::opt Options::IgnoreDeadFun( + "mssa-ignore-dead-fun", + llvm::cl::init(false), + llvm::cl::desc("Don't construct memory SSA for deadfunction") + ); + + + // Base class of pointer analyses (MemSSA.cpp) + const llvm::cl::opt Options::DumpMSSA( + "dump-mssa", + llvm::cl::init(false), + llvm::cl::desc("Dump memory SSA") + ); + + const llvm::cl::opt Options::MSSAFun( + "mssa-fun", + llvm::cl::init(""), + llvm::cl::desc("Please specify which function needs to be dumped") + ); + + const llvm::cl::opt Options::MemPar( + "mem-par", + llvm::cl::init(MemSSA::MemPartition::IntraDisjoint), + llvm::cl::desc("Memory region partiion strategies (e.g., for SVFG construction)"), + llvm::cl::values( + clEnumValN(MemSSA::MemPartition::Distinct, "distinct", "memory region per each object"), + clEnumValN(MemSSA::MemPartition::IntraDisjoint, "intra-disjoint", "memory regions partioned based on each function"), + clEnumValN(MemSSA::MemPartition::InterDisjoint, "inter-disjoint", "memory regions partioned across functions")) + ); + + + // SVFG builder (SVFGBuilder.cpp) + const llvm::cl::opt Options::SVFGWithIndirectCall( + "svfg-with-ind-call", + llvm::cl::init(false), + llvm::cl::desc("Update Indirect Calls for SVFG using pre-analysis") + ); + + const llvm::cl::opt Options::SingleVFG( + "single-vfg", + llvm::cl::init(false), + llvm::cl::desc("Create a single VFG shared by multiple analysis") + ); + + llvm::cl::opt Options::OPTSVFG( + "opt-svfg", + llvm::cl::init(true), + llvm::cl::desc("Optimize SVFG to eliminate formal-in and actual-out") + ); + + + // FSMPTA.cpp + const llvm::cl::opt Options::UsePCG( + "pcg-td-edge", + llvm::cl::init(false), + llvm::cl::desc("Use PCG lock for non-sparsely adding SVFG edges") + ); + + const llvm::cl::opt Options::IntraLock( + "intra-lock-td-edge", + llvm::cl::init(true), + llvm::cl::desc("Use simple intra-procedual lock for adding SVFG edges") + ); + + const llvm::cl::opt Options::ReadPrecisionTDEdge( + "rp-td-edge", + llvm::cl::init(false), + llvm::cl::desc("perform read precision to refine SVFG edges") + ); + + const llvm::cl::opt Options::AddModelFlag( + "add-td-edge", + llvm::cl::init(0), + llvm::cl::desc("Add thread SVFG edges with models: 0 Non Add Edge; 1 NonSparse; 2 All Optimisation; 3 No MHP; 4 No Alias; 5 No Lock; 6 No Read Precision.") + ); + + + // LockAnalysis.cpp + const llvm::cl::opt Options::PrintLockSpan( + "print-lock", + llvm::cl::init(false), + llvm::cl::desc("Print Thread Interleaving Results") + ); + + + // MHP.cpp + const llvm::cl::opt Options::PrintInterLev( + "print-interlev", + llvm::cl::init(false), + llvm::cl::desc("Print Thread Interleaving Results") + ); + + const llvm::cl::opt Options::DoLockAnalysis( + "lock-analysis", + llvm::cl::init(true), + llvm::cl::desc("Run Lock Analysis") + ); + + + // MTA.cpp + const llvm::cl::opt Options::AndersenAnno( + "tsan-ander", + llvm::cl::init(false), + llvm::cl::desc("Add TSan annotation according to Andersen") + ); + + const llvm::cl::opt Options::FSAnno( + "tsan-fs", + llvm::cl::init(false), + llvm::cl::desc("Add TSan annotation according to flow-sensitive analysis") + ); + + + // MTAAnnotator.cpp + const llvm::cl::opt Options::AnnoFlag( + "anno", + llvm::cl::init(0), + llvm::cl::desc("prune annotated instructions: 0001 Thread Local; 0002 Alias; 0004 MHP.") + ); + + + // MTAResultValidator.cpp + const llvm::cl::opt Options::PrintValidRes( + "mhp-validation", + llvm::cl::init(false), + llvm::cl::desc("Print MHP Validation Results") + ); + // LockResultValidator.cpp + const llvm::cl::opt Options::LockValid( + "lock-validation", + llvm::cl::init(false), + llvm::cl::desc("Print Lock Validation Results") + ); + + + // MTAStat.cpp + const llvm::cl::opt Options::AllPairMHP( + "all-pair-mhp", + llvm::cl::init(false), + llvm::cl::desc("All pair MHP computation") + ); + + + // PCG.cpp + //const llvm::cl::opt TDPrint( + // "print-td", + // llvm::cl::init(true), + // llvm::cl::desc("Print Thread Analysis Results")); + + + // TCT.cpp + const llvm::cl::opt Options::TCTDotGraph( + "dump-tct", + llvm::cl::init(false), + llvm::cl::desc("Dump dot graph of Call Graph") + ); + + + // LeakChecker.cpp + const llvm::cl::opt Options::ValidateTests( + "valid-tests", + llvm::cl::init(false), + llvm::cl::desc("Validate memory leak tests") + ); + + + // Source-sink analyzer (SrcSnkDDA.cpp) + const llvm::cl::opt Options::DumpSlice( + "dump-slice", + llvm::cl::init(false), + llvm::cl::desc("Dump dot graph of Saber Slices") + ); + + const llvm::cl::opt Options::CxtLimit( + "cxt-limit", + llvm::cl::init(3), + llvm::cl::desc("Source-Sink Analysis Contexts Limit") + ); + + + // CHG.cpp + const llvm::cl::opt Options::DumpCHA( + "dump-cha", + llvm::cl::init(false), + llvm::cl::desc("dump the class hierarchy graph") + ); + + + // DCHG.cpp + const llvm::cl::opt Options::PrintDCHG( + "print-dchg", + llvm::cl::init(false), + llvm::cl::desc("print the DCHG if debug information is available") + ); + + + // LLVMModule.cpp + const llvm::cl::opt Options::Graphtxt( + "graph-txt", + llvm::cl::value_desc("filename"), + llvm::cl::desc("graph txt file to build PAG") + ); + + const llvm::cl::opt Options::SVFMain( + "svf-main", + llvm::cl::init(false), + llvm::cl::desc("add svf.main()") + ); + + + // SymbolTableInfo.cpp + const llvm::cl::opt Options::LocMemModel( + "loc-mm", + llvm::cl::init(false), + llvm::cl::desc("Bytes/bits modeling of memory locations") + ); + + const llvm::cl::opt Options::ModelConsts( + "model-consts", + llvm::cl::init(true), + llvm::cl::desc("Modeling individual constant objects") + ); + + const llvm::cl::opt Options::SymTabPrint( + "print-symbol-table", llvm::cl::init(false), + llvm::cl::desc("Print Symbol Table to command line") + ); + + + // Conditions.cpp + const llvm::cl::opt Options::MaxBddSize( + "max-bdd-size", + llvm::cl::init(100000), + llvm::cl::desc("Maximum context limit for DDA") + ); + + + // PathCondAllocator.cpp + const llvm::cl::opt Options::PrintPathCond( + "print-pc", + llvm::cl::init(false), + llvm::cl::desc("Print out path condition") + ); + + + // SVFUtil.cpp + const llvm::cl::opt Options::DisableWarn( + "dwarn", + llvm::cl::init(true), + llvm::cl::desc("Disable warning") + ); + + + // Andersen.cpp + const llvm::cl::opt Options::ConsCGDotGraph( + "dump-constraint-graph", + llvm::cl::init(false), + llvm::cl::desc("Dump dot graph of Constraint Graph") + ); + + const llvm::cl::opt Options::BriefConsCGDotGraph( + "brief-constraint-graph", + llvm::cl::init(true), + llvm::cl::desc("Dump dot graph of Constraint Graph") + ); + + const llvm::cl::opt Options::PrintCGGraph( + "print-constraint-graph", + llvm::cl::init(false), + llvm::cl::desc("Print Constraint Graph to Terminal") + ); + + const llvm::cl::opt Options::WriteAnder( + "write-ander", + llvm::cl::init(""), + llvm::cl::desc("-write-ander=ir_annotator (Annotated IR with Andersen's results) or write Andersen's analysis results to a user-specified text file") + ); + + const llvm::cl::opt Options::ReadAnder( + "read-ander", + llvm::cl::init(""), + llvm::cl::desc("-read-ander=ir_annotator (Read Andersen's analysis results from the annotated IR, e.g., *.pre.bc) or from a text file") + ); + + const llvm::cl::opt Options::PtsDiff( + "diff", + llvm::cl::init(true), + llvm::cl::desc("Disable diff pts propagation") + ); + + const llvm::cl::opt Options::MergePWC( + "merge-pwc", + llvm::cl::init(true), + llvm::cl::desc("Enable PWC in graph solving") + ); + + + // FlowSensitive.cpp + const llvm::cl::opt Options::CTirAliasEval( + "ctir-alias-eval", + llvm::cl::init(false), + llvm::cl::desc("Prints alias evaluation of ctir instructions in FS analyses") + ); + + + // FlowSensitiveTBHC.cpp + /// Whether we allow reuse for TBHC. + const llvm::cl::opt Options::TBHCStoreReuse( + "tbhc-store-reuse", + llvm::cl::init(false), + llvm::cl::desc("Allow for object reuse in at stores in FSTBHC") + ); + + const llvm::cl::opt Options::TBHCAllReuse( + "tbhc-all-reuse", + llvm::cl::init(false), + llvm::cl::desc("Allow for object reuse everywhere in FSTBHC") + ); + + + // TypeAnalysis.cpp + const llvm::cl::opt Options::GenICFG( + "gen-icfg", + llvm::cl::init(true), + llvm::cl::desc("Generate ICFG graph") + ); + + + //WPAPass.cpp + const llvm::cl::opt Options::AnderSVFG( + "svfg", + llvm::cl::init(false), + llvm::cl::desc("Generate SVFG after Andersen's Analysis") + ); + + const llvm::cl::opt Options::WPAOPTSVFG( + "wpa-opt-svfg", + llvm::cl::init(false), + llvm::cl::desc("When using WPA pass, optimize SVFG to eliminate formal-in and actual-out (default false)") + ); + + const llvm::cl::opt Options::PrintAliases( + "print-aliases", + llvm::cl::init(false), + llvm::cl::desc("Print results for all pair aliases") + ); + + llvm::cl::bits Options::PASelected( + llvm::cl::desc("Select pointer analysis"), + llvm::cl::values( + clEnumValN(PointerAnalysis::Andersen_WPA, "nander", "Standard inclusion-based analysis"), + clEnumValN(PointerAnalysis::AndersenLCD_WPA, "lander", "Lazy cycle detection inclusion-based analysis"), + clEnumValN(PointerAnalysis::AndersenHCD_WPA, "hander", "Hybrid cycle detection inclusion-based analysis"), + clEnumValN(PointerAnalysis::AndersenHLCD_WPA, "hlander", "Hybrid lazy cycle detection inclusion-based analysis"), + clEnumValN(PointerAnalysis::AndersenSCD_WPA, "sander", "Selective cycle detection inclusion-based analysis"), + clEnumValN(PointerAnalysis::AndersenSFR_WPA, "sfrander", "Stride-based field representation includion-based analysis"), + clEnumValN(PointerAnalysis::AndersenWaveDiff_WPA, "ander", "Diff wave propagation inclusion-based analysis"), + clEnumValN(PointerAnalysis::Steensgaard_WPA, "steens", "Steensgaard's pointer analysis"), + // Disabled till further work is done. + // clEnumValN(PointerAnalysis::AndersenWaveDiffWithType_WPA, "andertype", "Diff wave propagation with type inclusion-based analysis"), + clEnumValN(PointerAnalysis::FSSPARSE_WPA, "fspta", "Sparse flow sensitive pointer analysis"), + clEnumValN(PointerAnalysis::FSTBHC_WPA, "fstbhc", "Sparse flow-sensitive type-based heap cloning pointer analysis"), + clEnumValN(PointerAnalysis::VFS_WPA, "vfspta", "Versioned sparse flow-sensitive points-to analysis"), + clEnumValN(PointerAnalysis::TypeCPP_WPA, "type", "Type-based fast analysis for Callgraph, PAG and CHA") + )); + + + llvm::cl::bits Options::AliasRule(llvm::cl::desc("Select alias check rule"), + llvm::cl::values( + clEnumValN(WPAPass::Conservative, "conservative", "return MayAlias if any pta says alias"), + clEnumValN(WPAPass::Veto, "veto", "return NoAlias if any pta says no alias") + )); } // namespace SVF. diff --git a/svf/lib/Util/PTAStat.cpp b/svf/lib/Util/PTAStat.cpp index 1c567127b..178d5e1f0 100644 --- a/svf/lib/Util/PTAStat.cpp +++ b/svf/lib/Util/PTAStat.cpp @@ -2,20 +2,20 @@ // // SVF: Static Value-Flow Analysis // -// Copyright (C) <2013-> +// Copyright (C) <2013-2017> // // This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU Affero General Public License as published by +// it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Affero General Public License for more details. +// GNU General Public License for more details. -// You should have received a copy of the GNU Affero General Public License +// You should have received a copy of the GNU General Public License // along with this program. If not, see . // //===----------------------------------------------------------------------===// @@ -29,53 +29,189 @@ #include #include "Graphs/PTACallGraph.h" -#include "Util/PTAStat.h" +#include "MemoryModel/PTAStat.h" #include "MemoryModel/PointerAnalysisImpl.h" -#include "SVFIR/SVFIR.h" +#include "Graphs/PAG.h" using namespace SVF; -using namespace std; - -PTAStat::PTAStat(PointerAnalysis* p) : SVFStat(), - pta(p), - _vmrssUsageBefore(0), - _vmrssUsageAfter(0), - _vmsizeUsageBefore(0), - _vmsizeUsageAfter(0) + +const char* PTAStat:: TotalAnalysisTime = "TotalTime"; ///< PAG value nodes +const char* PTAStat:: SCCDetectionTime = "SCCDetectTime"; ///< Total SCC detection time +const char* PTAStat:: SCCMergeTime = "SCCMergeTime"; ///< Total SCC merge time + +const char* PTAStat:: ProcessLoadStoreTime = "LoadStoreTime"; ///< process load and store time +const char* PTAStat:: ProcessCopyGepTime = "CopyGepTime"; ///< process copy and gep time +const char* PTAStat:: UpdateCallGraphTime = "UpdateCGTime"; ///< process copy and gep time + +const char* PTAStat:: TotalNumOfPointers = "TotalPointers"; ///< PAG value nodes +const char* PTAStat:: TotalNumOfObjects = "TotalObjects"; ///< Total PAG object node +const char* PTAStat:: TotalNumOfFieldObjects = "TotalFieldObjects"; ///< Total PAG field object node +const char* PTAStat:: MaxStructSize = "MaxStructSize"; ///< Max struct size (bytes) +const char* PTAStat:: TotalNumOfEdges = "TotalPAGEdges"; ///< Total PAG edge number + +const char* PTAStat:: NumOfFunctionObjs = "FunctionObjs"; ///< function numbers +const char* PTAStat:: NumOfGlobalObjs = "GlobalObjs"; ///< PAG global object node +const char* PTAStat:: NumOfHeapObjs = "HeapObjs"; ///< PAG heap object node +const char* PTAStat:: NumOfStackObjs = "StackObjs"; ///< PAG stack object node + +const char* PTAStat::NumberOfFieldInsensitiveObj = "FIObjNum"; +const char* PTAStat::NumberOfFieldSensitiveObj = "FSObjNum"; + +const char* PTAStat:: NumOfObjsHasVarStruct = "VarStructObj"; ///< PAG object node has struct (maybe nested with array) +const char* PTAStat:: NumOfObjsHasVarArray = "VarArrayObj"; ///< PAG object node has array (maybe nested with struct) +const char* PTAStat:: NumOfObjsHasConstStruct = "ConstStructObj"; ///< PAG object node has const struct (maybe nested with array) +const char* PTAStat:: NumOfObjsHasConstArray = "ConstArrayObj"; ///< PAG object node has const array (maybe nested with struct) +const char* PTAStat:: NumOfNonPtrObjs = "NonPtrObj"; ///< PAG object node which is non pointer type object (do not have pts) +const char* PTAStat:: NumOfConstantObjs = "ConstantObj"; ///< PAG object node which is purely scalar + +const char* PTAStat:: NumOfAddrs = "AddrsNum"; ///< PAG addr edge +const char* PTAStat:: NumOfLoads = "LoadsNum"; ///< PAG load edge +const char* PTAStat:: NumOfStores = "StoresNum"; ///< PAG store edge +const char* PTAStat:: NumOfCopys = "CopysNum"; ///< PAG copy edge +const char* PTAStat:: NumOfGeps = "GepsNum"; ///< PAG gep edge +const char* PTAStat:: NumOfCalls = "CallsNum"; ///< PAG call edge +const char* PTAStat:: NumOfReturns = "ReturnsNum"; ///< PAG return edge + +const char* PTAStat:: NumOfProcessedAddrs = "AddrProcessed"; ///< PAG addr processed edge +const char* PTAStat:: NumOfProcessedLoads = "LoadProcessed"; ///< PAG load processed edge +const char* PTAStat:: NumOfProcessedStores = "StoreProcessed"; ///< PAG store processed edge +const char* PTAStat:: NumOfProcessedCopys = "CopyProcessed"; ///< PAG copy processed edge +const char* PTAStat:: NumOfProcessedGeps = "GepProcessed"; ///< PAG gep processed edge + +const char* PTAStat::NumOfSfr = "NumOfSFRs"; ///< number of field representatives +const char* PTAStat::NumOfFieldExpand = "NumOfFieldExpand"; + +const char* PTAStat:: NumOfPointers = "Pointers"; ///< PAG value node, each of them maps to a llvm value +const char* PTAStat:: NumOfGepFieldPointers = "DYFieldPtrs"; ///< PAG gep value node (field value, dynamically created dummy node) + +const char* PTAStat:: NumOfMemObjects = "MemObjects"; ///< PAG object node, each of them maps to a llvm value +const char* PTAStat:: NumOfGepFieldObjects = "DYFieldObjs"; ///< PAG gep object node (field obj, dynamically created dummy node) + +const char* PTAStat:: AveragePointsToSetSize = "AvgPtsSetSize"; ///< Average Points-to set size +const char* PTAStat:: AverageTopLevPointsToSetSize = "AvgTopLvlPtsSize"; ///< Average Points-to set size of top level pointers +const char* PTAStat:: MaxPointsToSetSize = "MaxPtsSetSize"; ///< Max Points-to set size + +const char* PTAStat:: AveragePointsToSetSizeFields = "AvgPtsSizeFields"; +const char* PTAStat:: MaxPointsToSetSizeFields = "MaxPtsSizeFields"; + +const char* PTAStat:: NumOfIterations = "Iterations"; ///< Number of iterations during resolution + +const char* PTAStat:: NumOfIndirectCallSites = "IndCallSites"; ///< Number of indirect callsites +const char* PTAStat:: NumOfIndirectEdgeSolved = "IndEdgeSolved"; ///< Number of indirect calledge resolved + +const char* PTAStat:: NumOfSCCDetection = "NumOfSCCDetect"; ///< Number of scc detection performed +const char* PTAStat:: NumOfCycles = "TotalCycleNum"; ///< Number of scc cycles detected +const char* PTAStat:: NumOfPWCCycles = "TotalPWCCycleNum"; ///< Number of pwc scc cycles detected +const char* PTAStat:: NumOfNodesInCycles = "NodesInCycles"; ///< Number of nodes in cycles detected +const char* PTAStat:: MaxNumOfNodesInSCC = "MaxNodesInSCC"; ///< max Number of nodes in one scc cycle + +const char* PTAStat:: NumOfNullPointer = "NullPointer"; ///< Number of pointers points-to null + +PTAStat::PTAStat(PointerAnalysis* p) : startTime(0), endTime(0), pta(p) { - u32_t vmrss = 0; - u32_t vmsize = 0; - SVFUtil::getMemoryUsageKB(&vmrss, &vmsize); - setMemUsageBefore(vmrss, vmsize); + } void PTAStat::performStat() { - SVFStat::performStat(); - callgraphStat(); - SVFIR* pag = SVFIR::getPAG(); - for(SVFIR::iterator it = pag->begin(), eit = pag->end(); it!=eit; ++it) + PAG* pag = PAG::getPAG(); + u32_t numOfFunction = 0; + u32_t numOfGlobal = 0; + u32_t numOfStack = 0; + u32_t numOfHeap = 0; + u32_t numOfHasVarArray = 0; + u32_t numOfHasVarStruct = 0; + u32_t numOfHasConstArray = 0; + u32_t numOfHasConstStruct = 0; + u32_t numOfScalar = 0; + u32_t numOfConstant = 0; + u32_t fiObjNumber = 0; + u32_t fsObjNumber = 0; + Set memObjSet; + for(PAG::iterator it = pag->begin(), eit = pag->end(); it!=eit; ++it) { PAGNode* node = it->second; - if(SVFUtil::isa(node)) + if(ObjPN* obj = SVFUtil::dyn_cast(node)) { + const MemObj* mem = obj->getMemObj(); + if (memObjSet.insert(mem->getSymId()).second == false) + continue; + if(mem->isBlackHoleObj()) + continue; + if(mem->isFunction()) + numOfFunction++; + if(mem->isGlobalObj()) + numOfGlobal++; + if(mem->isStack()) + numOfStack++; + if(mem->isHeap()) + numOfHeap++; + if(mem->isVarArray()) + numOfHasVarArray++; + if(mem->isVarStruct()) + numOfHasVarStruct++; + if(mem->isConstArray()) + numOfHasConstArray++; + if(mem->isConstStruct()) + numOfHasConstStruct++; + if(mem->hasPtrObj() == false) + numOfScalar++; + if(mem->isConstant()) + numOfConstant++; + + if (mem->isFieldInsensitive()) + fiObjNumber++; + else + fsObjNumber++; + if(pta->isLocalVarInRecursiveFun(node->getId())) { localVarInRecursion.set(node->getId()); } } } - PTNumStatMap["LocalVarInRecur"] = localVarInRecursion.count(); - - u32_t vmrss = 0; - u32_t vmsize = 0; - SVFUtil::getMemoryUsageKB(&vmrss, &vmsize); - setMemUsageAfter(vmrss, vmsize); - timeStatMap["MemoryUsageVmrss"] = _vmrssUsageAfter - _vmrssUsageBefore; - timeStatMap["MemoryUsageVmsize"] = _vmsizeUsageAfter - _vmsizeUsageBefore; + + + + generalNumMap[TotalNumOfPointers] = pag->getValueNodeNum() + pag->getFieldValNodeNum(); + generalNumMap[TotalNumOfObjects] = pag->getObjectNodeNum(); + generalNumMap[TotalNumOfFieldObjects] = pag->getFieldObjNodeNum(); + generalNumMap[MaxStructSize] = SymbolTableInfo::SymbolInfo()->getMaxStructSize(); + generalNumMap[TotalNumOfEdges] = pag->getPAGEdgeNum(); + generalNumMap["TotalPTAPAGEdges"] = pag->totalPTAPAGEdge; + generalNumMap[NumberOfFieldInsensitiveObj] = fiObjNumber; + generalNumMap[NumberOfFieldSensitiveObj] = fsObjNumber; + + generalNumMap[NumOfAddrs] = pag->getEdgeSet(PAGEdge::Addr).size(); + generalNumMap[NumOfLoads] = pag->getEdgeSet(PAGEdge::Load).size(); + generalNumMap[NumOfStores] = pag->getEdgeSet(PAGEdge::Store).size(); + generalNumMap[NumOfCopys] = pag->getEdgeSet(PAGEdge::Copy).size(); + generalNumMap[NumOfGeps] = pag->getEdgeSet(PAGEdge::NormalGep).size() + pag->getEdgeSet(PAGEdge::VariantGep).size(); + generalNumMap[NumOfCalls] = pag->getEdgeSet(PAGEdge::Call).size(); + generalNumMap[NumOfReturns] = pag->getEdgeSet(PAGEdge::Ret).size(); + + generalNumMap[NumOfFunctionObjs] = numOfFunction; + generalNumMap[NumOfGlobalObjs] = numOfGlobal; + generalNumMap[NumOfHeapObjs] = numOfHeap; + generalNumMap[NumOfStackObjs] = numOfStack; + + generalNumMap[NumOfObjsHasVarStruct] = numOfHasVarStruct; + generalNumMap[NumOfObjsHasVarArray] = numOfHasVarArray; + generalNumMap[NumOfObjsHasConstStruct] = numOfHasConstStruct; + generalNumMap[NumOfObjsHasConstArray] = numOfHasConstArray; + generalNumMap[NumOfNonPtrObjs] = numOfScalar; + + generalNumMap[NumOfIndirectCallSites] = pag->getIndirectCallsites().size(); + generalNumMap["TotalCallSite"] = pag->getCallSiteSet().size(); + generalNumMap["LocalVarInRecur"] = localVarInRecursion.count(); + bitcastInstStat(); + branchStat(); + + printStat("General Stats"); + } void PTAStat::callgraphStat() @@ -129,16 +265,96 @@ void PTAStat::callgraphStat() PTNumStatMap["TotalEdge"] = totalEdge; PTNumStatMap["CalRetPairInCycle"] = edgeInCycle; - if(pta->getAnalysisTy() >= PointerAnalysis::PTATY::Andersen_BASE && pta->getAnalysisTy() <= PointerAnalysis::PTATY::Steensgaard_WPA) - SVFStat::printStat("CallGraph Stats (Andersen analysis)"); - else if(pta->getAnalysisTy() >= PointerAnalysis::PTATY::FSDATAFLOW_WPA && pta->getAnalysisTy() <= PointerAnalysis::PTATY::FSCS_WPA) - SVFStat::printStat("CallGraph Stats (Flow-sensitive analysis)"); - else if(pta->getAnalysisTy() >= PointerAnalysis::PTATY::CFLFICI_WPA && pta->getAnalysisTy() <= PointerAnalysis::PTATY::CFLFSCS_WPA) - SVFStat::printStat("CallGraph Stats (CFL-R analysis)"); - else if(pta->getAnalysisTy() >= PointerAnalysis::PTATY::FieldS_DDA && pta->getAnalysisTy() <= PointerAnalysis::PTATY::Cxt_DDA) - SVFStat::printStat("CallGraph Stats (DDA analysis)"); - else - SVFStat::printStat("CallGraph Stats"); + PTAStat::printStat("CallGraph Stats"); delete callgraphSCC; } + +void PTAStat::printStat(string statname) +{ + + StringRef fullName(SymbolTableInfo::SymbolInfo()->getModule()->getModuleIdentifier()); + StringRef name = fullName.split('/').second; + moduleName = name.split('.').first.str(); + + std::cout << "\n*********" << statname << "***************\n"; + std::cout << "################ (program : " << moduleName << ")###############\n"; + std::cout.flags(std::ios::left); + unsigned field_width = 20; + for(NUMStatMap::iterator it = generalNumMap.begin(), eit = generalNumMap.end(); it!=eit; ++it) + { + // format out put with width 20 space + std::cout << std::setw(field_width) << it->first << it->second << "\n"; + } + std::cout << "-------------------------------------------------------\n"; + for(TIMEStatMap::iterator it = timeStatMap.begin(), eit = timeStatMap.end(); it!=eit; ++it) + { + // format out put with width 20 space + std::cout << std::setw(field_width) << it->first << it->second << "\n"; + } + for(NUMStatMap::iterator it = PTNumStatMap.begin(), eit = PTNumStatMap.end(); it!=eit; ++it) + { + // format out put with width 20 space + std::cout << std::setw(field_width) << it->first << it->second << "\n"; + } + + std::cout << "#######################################################" << std::endl; + std::cout.flush(); + generalNumMap.clear(); + PTNumStatMap.clear(); + timeStatMap.clear(); +} + + +void PTAStat::bitcastInstStat() +{ + SVFModule* module = pta->getModule(); + u32_t numberOfBitCast = 0; + for (SVFModule::llvm_const_iterator funIter = module->llvmFunBegin(), funEiter = module->llvmFunEnd(); + funIter != funEiter; ++funIter) + { + const Function* func = *funIter; + for (Function::const_iterator bbIt = func->begin(), bbEit = func->end(); + bbIt != bbEit; ++bbIt) + { + const BasicBlock& bb = *bbIt; + for (BasicBlock::const_iterator instIt = bb.begin(), instEit = bb.end(); + instIt != instEit; ++instIt) + { + const Instruction& inst = *instIt; + if (const BitCastInst* bitcast = SVFUtil::dyn_cast(&inst)) + { + if (SVFUtil::isa(bitcast->getSrcTy())) + numberOfBitCast++; + } + } + } + } + + generalNumMap["BitCastNumber"] = numberOfBitCast; +} + +void PTAStat::branchStat() +{ + SVFModule* module = pta->getModule(); + u32_t numOfBB_2Succ = 0; + u32_t numOfBB_3Succ = 0; + for (SVFModule::llvm_const_iterator funIter = module->llvmFunBegin(), funEiter = module->llvmFunEnd(); + funIter != funEiter; ++funIter) + { + const Function* func = *funIter; + for (Function::const_iterator bbIt = func->begin(), bbEit = func->end(); + bbIt != bbEit; ++bbIt) + { + const BasicBlock& bb = *bbIt; + u32_t numOfSucc = bb.getTerminator()->getNumSuccessors(); + if (numOfSucc == 2) + numOfBB_2Succ++; + else if (numOfSucc > 2) + numOfBB_3Succ++; + } + } + + generalNumMap["BBWith2Succ"] = numOfBB_2Succ; + generalNumMap["BBWith3Succ"] = numOfBB_3Succ; +} diff --git a/svf/lib/Util/PathCondAllocator.cpp b/svf/lib/Util/PathCondAllocator.cpp new file mode 100644 index 000000000..640a70ebb --- /dev/null +++ b/svf/lib/Util/PathCondAllocator.cpp @@ -0,0 +1,550 @@ +//===- PathAllocator.cpp -- Path condition analysis---------------------------// +// +// SVF: Static Value-Flow Analysis +// +// Copyright (C) <2013-2017> +// + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// +//===----------------------------------------------------------------------===// + + +/* + * PathAllocator.cpp + * + * Created on: Apr 3, 2014 + * Author: Yulei Sui + */ + +#include "Util/Options.h" +#include "SVF-FE/LLVMUtil.h" +#include "Util/PathCondAllocator.h" +#include "Util/DPItem.h" +#include + +using namespace SVF; +using namespace SVFUtil; + +u64_t DPItem::maximumBudget = ULONG_MAX - 1; +u32_t ContextCond::maximumCxtLen = 0; +u32_t ContextCond::maximumCxt = 0; +u32_t ContextCond::maximumPathLen = 0; +u32_t ContextCond::maximumPath = 0; + + +/*! + * Allocate path condition for each branch + */ +void PathCondAllocator::allocate(const SVFModule* M) +{ + DBOUT(DGENERAL,outs() << pasMsg("path condition allocation starts\n")); + + for (const auto& func : *M) + { + if (!SVFUtil::isExtCall(func)) + { + // Allocate conditions for a program. + for (Function::const_iterator bit = func->getLLVMFun()->begin(), ebit = func->getLLVMFun()->end(); bit != ebit; ++bit) + { + const BasicBlock & bb = *bit; + collectBBCallingProgExit(bb); + allocateForBB(bb); + } + } + } + + if(Options::PrintPathCond) + printPathCond(); + + DBOUT(DGENERAL,outs() << pasMsg("path condition allocation ends\n")); +} + +/*! + * Allocate conditions for a basic block and propagate its condition to its successors. + */ +void PathCondAllocator::allocateForBB(const BasicBlock & bb) +{ + + u32_t succ_number = getBBSuccessorNum(&bb); + + // if successor number greater than 1, allocate new decision variable for successors + if(succ_number > 1) + { + + //allocate log2(num_succ) decision variables + double num = log(succ_number)/log(2); + u32_t bit_num = (u32_t)ceil(num); + u32_t succ_index = 0; + std::vector condVec; + for(u32_t i = 0 ; i < bit_num; i++) + { + condVec.push_back(newCond(bb.getTerminator())); + } + + // iterate each successor + for (succ_const_iterator succ_it = succ_begin(&bb); + succ_it != succ_end(&bb); + succ_it++, succ_index++) + { + + const BasicBlock* succ = *succ_it; + + Condition* path_cond = getTrueCond(); + + ///TODO: handle BranchInst and SwitchInst individually here!! + + // for each successor decide its bit representation + // decide whether each bit of succ_index is 1 or 0, if (three successor) succ_index is 000 then use C1^C2^C3 + // if 001 use C1^C2^negC3 + for(u32_t j = 0 ; j < bit_num; j++) + { + //test each bit of this successor's index (binary representation) + u32_t tool = 0x01 << j; + if(tool & succ_index) + { + path_cond = condAnd(path_cond, (condNeg(condVec.at(j)))); + } + else + { + path_cond = condAnd(path_cond, condVec.at(j)); + } + } + setBranchCond(&bb,succ,path_cond); + } + + } +} + +/*! + * Get a branch condition + */ +PathCondAllocator::Condition* PathCondAllocator::getBranchCond(const BasicBlock * bb, const BasicBlock *succ) const +{ + u32_t pos = getBBSuccessorPos(bb,succ); + if(getBBSuccessorNum(bb) == 1) + return getTrueCond(); + else + { + BBCondMap::const_iterator it = bbConds.find(bb); + assert(it!=bbConds.end() && "basic block does not have branch and conditions??"); + CondPosMap::const_iterator cit = it->second.find(pos); + assert(cit!=it->second.end() && "no condition on the branch??"); + return cit->second; + } +} + +/*! + * Set a branch condition + */ +void PathCondAllocator::setBranchCond(const BasicBlock *bb, const BasicBlock *succ, Condition* cond) +{ + /// we only care about basic blocks have more than one successor + assert(getBBSuccessorNum(bb) > 1 && "not more than one successor??"); + u32_t pos = getBBSuccessorPos(bb,succ); + CondPosMap& condPosMap = bbConds[bb]; + + /// FIXME: llvm getNumSuccessors allows duplicated block in the successors, it makes this assertion fail + /// In this case we may waste a condition allocation, because the overwrite of the previous cond + //assert(condPosMap.find(pos) == condPosMap.end() && "this branch has already been set "); + + condPosMap[pos] = cond; +} + +/*! + * Evaluate null like expression for source-sink related bug detection in SABER + */ +PathCondAllocator::Condition* PathCondAllocator::evaluateTestNullLikeExpr(const BranchInst* brInst, const BasicBlock *succ) +{ + + const BasicBlock* succ1 = brInst->getSuccessor(0); + + if(isTestNullExpr(brInst->getCondition())) + { + // succ is then branch + if(succ1 == succ) + return getFalseCond(); + // succ is else branch + else + return getTrueCond(); + } + if(isTestNotNullExpr(brInst->getCondition())) + { + // succ is then branch + if(succ1 == succ) + return getTrueCond(); + // succ is else branch + else + return getFalseCond(); + } + + return nullptr; +} + +/*! + * Evaluate condition for program exit (e.g., exit(0)) + */ +PathCondAllocator::Condition* PathCondAllocator::evaluateProgExit(const BranchInst* brInst, const BasicBlock *succ) +{ + const BasicBlock* succ1 = brInst->getSuccessor(0); + const BasicBlock* succ2 = brInst->getSuccessor(1); + + bool branch1 = isBBCallsProgExit(succ1); + bool branch2 = isBBCallsProgExit(succ2); + + /// then branch calls program exit + if(branch1 && !branch2) + { + // succ is then branch + if(succ1 == succ) + return getFalseCond(); + // succ is else branch + else + return getTrueCond(); + } + /// else branch calls program exit + else if(!branch1 && branch2) + { + // succ is else branch + if(succ2 == succ) + return getFalseCond(); + // succ is then branch + else + return getTrueCond(); + } + // two branches both call program exit + else if(branch1 && branch2) + { + return getFalseCond(); + } + /// no branch call program exit + else + return nullptr; + +} + +/*! + * Evaluate loop exit branch to be true if + * bb is loop header and succ is the only exit basic block outside the loop (excluding exit bbs which call program exit) + * for all other case, we conservatively evaluate false for now + */ +PathCondAllocator::Condition* PathCondAllocator::evaluateLoopExitBranch(const BasicBlock * bb, const BasicBlock *dst) +{ + const Function* fun = bb->getParent(); + assert(fun==dst->getParent() && "two basic blocks should be in the same function"); + + const LoopInfo* loopInfo = getLoopInfo(fun); + if(loopInfo->isLoopHeader(const_cast(bb))) + { + const Loop *loop = loopInfo->getLoopFor(bb); + SmallBBVector exitbbs; + Set filteredbbs; + loop->getExitBlocks(exitbbs); + /// exclude exit bb which calls program exit + while(!exitbbs.empty()) + { + BasicBlock* eb = exitbbs.pop_back_val(); + if(!isBBCallsProgExit(eb)) + filteredbbs.insert(eb); + } + + /// if the dst dominate all other loop exit bbs, then dst can certainly be reached + bool allPDT = true; + PostDominatorTree* pdt = getPostDT(fun); + for(const auto& filteredbb : filteredbbs) + { + if(!pdt->dominates(dst,filteredbb)) + allPDT =false; + } + + if(allPDT) + return getTrueCond(); + } + return nullptr; +} + +/*! + * (1) Evaluate a branch when it reaches a program exit + * (2) Evaluate a branch when it is loop exit branch + * (3) Evaluate a branch when it is a test null like condition + */ +PathCondAllocator::Condition* PathCondAllocator::evaluateBranchCond(const BasicBlock * bb, const BasicBlock *succ) +{ + if(getBBSuccessorNum(bb) == 1) + { + assert(bb->getTerminator()->getSuccessor(0) == succ && "not the unique successor?"); + return getTrueCond(); + } + + if(const BranchInst* brInst = SVFUtil::dyn_cast(bb->getTerminator())) + { + assert(brInst->getNumSuccessors() == 2 && "not a two successors branch??"); + const BasicBlock* succ1 = brInst->getSuccessor(0); + const BasicBlock* succ2 = brInst->getSuccessor(1); + assert((succ1 == succ || succ2 == succ) && "not a successor??"); + + Condition* evalLoopExit = evaluateLoopExitBranch(bb,succ); + if(evalLoopExit) + return evalLoopExit; + + Condition* evalProgExit = evaluateProgExit(brInst,succ); + if(evalProgExit) + return evalProgExit; + + Condition* evalTestNullLike = evaluateTestNullLikeExpr(brInst,succ); + if(evalTestNullLike) + return evalTestNullLike; + + } + return getBranchCond(bb, succ); +} + +bool PathCondAllocator::isEQCmp(const CmpInst* cmp) const +{ + return (cmp->getPredicate() == CmpInst::ICMP_EQ); +} + +bool PathCondAllocator::isNECmp(const CmpInst* cmp) const +{ + return (cmp->getPredicate() == CmpInst::ICMP_NE); +} + +bool PathCondAllocator::isTestNullExpr(const Value* test) const +{ + if(const CmpInst* cmp = SVFUtil::dyn_cast(test)) + { + return isTestContainsNullAndTheValue(cmp) && isEQCmp(cmp); + } + return false; +} + +bool PathCondAllocator::isTestNotNullExpr(const Value* test) const +{ + if(const CmpInst* cmp = SVFUtil::dyn_cast(test)) + { + return isTestContainsNullAndTheValue(cmp) && isNECmp(cmp); + } + return false; +} + +bool PathCondAllocator::isTestContainsNullAndTheValue(const CmpInst* cmp) const +{ + + const Value *op0 = cmp->getOperand(0); + const Value *op1 = cmp->getOperand(1); + if (SVFUtil::isa(getCurEvalSVFGNode()->getValue())) { + if (SVFUtil::isa(op1)) { + if (SVFUtil::isa(op0)) { + Set inDirVal; + for (const auto& it: getCurEvalSVFGNode()->getOutEdges()) { + if (it->isIndirectVFGEdge()) { + inDirVal.insert(it->getDstNode()->getValue()); + } + } + // There is an indirect edge from cur svfg node (store) to cmp operand (load from top-level pointer) + // e.g., + // cur svfg node -> 1. store i32* %0, i32** %p, align 8, !dbg !157 + // cmp operand -> 2. %1 = load i32*, i32** %p, align 8, !dbg !159 + // 3. %tobool = icmp ne i32* %1, null, !dbg !159 + // 4. br i1 %tobool, label %if.end, label %if.then, !dbg !161 + // There is an indirect edge 1->2 with value %0 + return inDirVal.find(op0) != inDirVal.end(); + } + } else if (SVFUtil::isa(op0)) { + if (SVFUtil::isa(op1)) { + Set inDirVal; + for (const auto& it: getCurEvalSVFGNode()->getOutEdges()) { + if (it->isIndirectVFGEdge()) { + inDirVal.insert(it->getDstNode()->getValue()); + } + } + return inDirVal.find(op1) != inDirVal.end(); + } + } + } + return false; + +} + +/*! + * Whether this basic block contains program exit function call + */ +void PathCondAllocator::collectBBCallingProgExit(const BasicBlock & bb) +{ + + for(BasicBlock::const_iterator it = bb.begin(), eit = bb.end(); it!=eit; it++) + { + const Instruction* inst = &*it; + if(SVFUtil::isa(inst) || SVFUtil::isa(inst)) + if(SVFUtil::isProgExitCall(inst)) + { + funToExitBBsMap[bb.getParent()].insert(&bb); + } + } +} + +/*! + * Whether this basic block contains program exit function call + */ +bool PathCondAllocator::isBBCallsProgExit(const BasicBlock* bb) +{ + const Function* fun = bb->getParent(); + FunToExitBBsMap::const_iterator it = funToExitBBsMap.find(fun); + if(it!=funToExitBBsMap.end()) + { + PostDominatorTree* pdt = getPostDT(fun); + for(const auto& bit : it->second) + { + if(pdt->dominates(bit,bb)) + return true; + } + } + return false; +} + +/*! + * Get complement phi condition + * e.g., B0: dstBB; B1:incomingBB; B2:complementBB + * Assume B0 (phi node) is the successor of both B1 and B2. + * If B1 dominates B2, and B0 not dominate B2 then condition from B1-->B0 = neg(B1-->B2)^(B1-->B0) + */ +PathCondAllocator::Condition* PathCondAllocator::getPHIComplementCond(const BasicBlock* BB1, const BasicBlock* BB2, const BasicBlock* BB0) +{ + assert(BB1 && BB2 && "expect nullptr BB here!"); + + DominatorTree* dt = getDT(BB1->getParent()); + /// avoid both BB0 and BB1 dominate BB2 (e.g., while loop), then BB2 is not necessaryly a complement BB + if(dt->dominates(BB1,BB2) && !dt->dominates(BB0,BB2)) + { + Condition* cond = ComputeIntraVFGGuard(BB1,BB2); + return condNeg(cond); + } + + return getTrueCond(); +} + +/*! + * Compute calling inter-procedural guards between two SVFGNodes (from caller to callee) + * src --c1--> callBB --true--> funEntryBB --c2--> dst + * the InterCallVFGGuard is c1 ^ c2 + */ +PathCondAllocator::Condition* PathCondAllocator::ComputeInterCallVFGGuard(const BasicBlock* srcBB, const BasicBlock* dstBB, const BasicBlock* callBB) +{ + const BasicBlock* funEntryBB = &dstBB->getParent()->getEntryBlock(); + + Condition* c1 = ComputeIntraVFGGuard(srcBB,callBB); + setCFCond(funEntryBB,condOr(getCFCond(funEntryBB),getCFCond(callBB))); + Condition* c2 = ComputeIntraVFGGuard(funEntryBB,dstBB); + return condAnd(c1,c2); +} + +/*! + * Compute return inter-procedural guards between two SVFGNodes (from callee to caller) + * src --c1--> funExitBB --true--> retBB --c2--> dst + * the InterRetVFGGuard is c1 ^ c2 + */ +PathCondAllocator::Condition* PathCondAllocator::ComputeInterRetVFGGuard(const BasicBlock* srcBB, const BasicBlock* dstBB, const BasicBlock* retBB) +{ + const BasicBlock* funExitBB = getFunExitBB(srcBB->getParent()); + + Condition* c1 = ComputeIntraVFGGuard(srcBB,funExitBB); + setCFCond(retBB,condOr(getCFCond(retBB),getCFCond(funExitBB))); + Condition* c2 = ComputeIntraVFGGuard(retBB,dstBB); + return condAnd(c1,c2); +} + +/*! + * Compute intra-procedural guards between two SVFGNodes (inside same function) + */ +PathCondAllocator::Condition* PathCondAllocator::ComputeIntraVFGGuard(const BasicBlock* srcBB, const BasicBlock* dstBB) +{ + + assert(srcBB->getParent() == dstBB->getParent() && "two basic blocks are not in the same function??"); + + PostDominatorTree* postDT = getPostDT(srcBB->getParent()); + if(postDT->dominates(dstBB,srcBB)) + return getTrueCond(); + + CFWorkList worklist; + worklist.push(srcBB); + setCFCond(srcBB,getTrueCond()); + + while(!worklist.empty()) + { + const BasicBlock* bb = worklist.pop(); + Condition* cond = getCFCond(bb); + + /// if the dstBB is the eligible loop exit of the current basic block + /// we can early terminate the computation + if(Condition* loopExitCond = evaluateLoopExitBranch(bb,dstBB)) + return condAnd(cond, loopExitCond); + + + for (succ_const_iterator succ_it = succ_begin(bb); + succ_it != succ_end(bb); succ_it++) + { + const BasicBlock* succ = *succ_it; + /// calculate the branch condition + /// if succ post dominate bb, then we get brCond quicker by using postDT + /// note that we assume loop exit always post dominate loop bodys + /// which means loops are approximated only once. + Condition* brCond; + if(postDT->dominates(succ,bb)) + brCond = getTrueCond(); + else + brCond = getEvalBrCond(bb, succ); + + DBOUT(DSaber, outs() << " bb (" << bb->getName() << + ") --> " << "succ_bb (" << succ->getName() << ") condition: " << brCond << "\n"); + Condition* succPathCond = condAnd(cond, brCond); + if(setCFCond(succ, condOr(getCFCond(succ), succPathCond))) + worklist.push(succ); + } + } + + DBOUT(DSaber, outs() << " src_bb (" << srcBB->getName() << + ") --> " << "dst_bb (" << dstBB->getName() << ") condition: " << getCFCond(dstBB) << "\n"); + + return getCFCond(dstBB); +} + + +/*! + * Print path conditions + */ +void PathCondAllocator::printPathCond() +{ + + outs() << "print path condition\n"; + + for(const auto & bbCond : bbConds) + { + const BasicBlock* bb = bbCond.first; + for(const auto& cit : bbCond.second) + { + u32_t i=0; + for (const BasicBlock *succ: successors(bb)) + { + if (i == cit.first) + { + Condition* cond = cit.second; + outs() << bb->getName() << "-->" << succ->getName() << ":"; + outs() << dumpCond(cond) << "\n"; + break; + } + i++; + } + } + } +} diff --git a/svf/lib/Util/SVFBugReport.cpp b/svf/lib/Util/SVFBugReport.cpp deleted file mode 100644 index fea0c3029..000000000 --- a/svf/lib/Util/SVFBugReport.cpp +++ /dev/null @@ -1,438 +0,0 @@ -//===- SVFBugReport.cpp -- Base class for statistics---------------------------------// -// -// SVF: Static Value-Flow Analysis -// -// Copyright (C) <2013-> -// - -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU Affero General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Affero General Public License for more details. - -// You should have received a copy of the GNU Affero General Public License -// along with this program. If not, see . -// -//===----------------------------------------------------------------------===// - - -// -// Created by JoelYang on 2023/4/19. -// - -#include "Util/SVFBugReport.h" -#include -#include "Util/cJSON.h" -#include "Util/SVFUtil.h" -#include -#include - -using namespace std; -using namespace SVF; - -const std::map GenericBug::BugType2Str = -{ - {GenericBug::FULLBUFOVERFLOW, "Full Buffer Overflow"}, - {GenericBug::PARTIALBUFOVERFLOW, "Partial Buffer Overflow"}, - {GenericBug::NEVERFREE, "Never Free"}, - {GenericBug::PARTIALLEAK, "Partial Leak"}, - {GenericBug::FILENEVERCLOSE, "File Never Close"}, - {GenericBug::FILEPARTIALCLOSE, "File Partial Close"}, - {GenericBug::DOUBLEFREE, "Double Free"}, - {GenericBug::FULLNULLPTRDEREFERENCE, "Full Null Ptr Dereference"}, - {GenericBug::PARTIALNULLPTRDEREFERENCE, "Partial Null Ptr Dereference"} -}; - -const std::string GenericBug::getLoc() const -{ - const SVFBugEvent&sourceInstEvent = bugEventStack.at(bugEventStack.size() -1); - return sourceInstEvent.getEventLoc(); -} - -const std::string GenericBug::getFuncName() const -{ - const SVFBugEvent&sourceInstEvent = bugEventStack.at(bugEventStack.size() -1); - return sourceInstEvent.getFuncName(); -} - -cJSON *BufferOverflowBug::getBugDescription() const -{ - cJSON *bugDescription = cJSON_CreateObject(); - cJSON *allocLB = cJSON_CreateNumber(allocLowerBound); - cJSON *allocUB = cJSON_CreateNumber(allocUpperBound); - cJSON *accessLB = cJSON_CreateNumber(accessLowerBound); - cJSON *accessUB = cJSON_CreateNumber(accessUpperBound); - - cJSON_AddItemToObject(bugDescription, "AllocLowerBound", allocLB); - cJSON_AddItemToObject(bugDescription, "AllocUpperBound", allocUB); - cJSON_AddItemToObject(bugDescription, "AccessLowerBound", accessLB); - cJSON_AddItemToObject(bugDescription, "AccessUpperBound", accessUB); - - return bugDescription; -} - -void BufferOverflowBug::printBugToTerminal() const -{ - stringstream bugInfo; - if(FullBufferOverflowBug::classof(this)) - { - SVFUtil::errs() << SVFUtil::bugMsg1("\t Full Overflow :") << " accessing at : (" - << GenericBug::getLoc() << ")\n"; - - } - else - { - SVFUtil::errs() << SVFUtil::bugMsg1("\t Partial Overflow :") << " accessing at : (" - << GenericBug::getLoc() << ")\n"; - } - bugInfo << "\t\t allocate size : [" << allocLowerBound << ", " << allocUpperBound << "], "; - bugInfo << "access size : [" << accessLowerBound << ", " << accessUpperBound << "]\n"; - SVFUtil::errs() << "\t\t Info : \n" << bugInfo.str(); - SVFUtil::errs() << "\t\t Events : \n"; - - for(auto event : bugEventStack) - { - switch(event.getEventType()) - { - case SVFBugEvent::CallSite: - { - SVFUtil::errs() << "\t\t callsite at : ( " << event.getEventLoc() << " )\n"; - break; - } - default: - { - // TODO: implement more events when needed - break; - } - } - } -} - -cJSON * NeverFreeBug::getBugDescription() const -{ - cJSON *bugDescription = cJSON_CreateObject(); - return bugDescription; -} - -void NeverFreeBug::printBugToTerminal() const -{ - SVFUtil::errs() << SVFUtil::bugMsg1("\t NeverFree :") << " memory allocation at : (" - << GenericBug::getLoc() << ")\n"; -} - -cJSON * PartialLeakBug::getBugDescription() const -{ - cJSON *bugDescription = cJSON_CreateObject(); - cJSON *pathInfo = cJSON_CreateArray(); - auto lastBranchEventIt = bugEventStack.end() - 1; - for(auto eventIt = bugEventStack.begin(); eventIt != lastBranchEventIt; eventIt++) - { - cJSON *newBranch = cJSON_CreateObject(); - cJSON *branchLoc = cJSON_Parse((*eventIt).getEventLoc().c_str()); - if(branchLoc == nullptr) branchLoc = cJSON_CreateObject(); - - cJSON *branchCondition = cJSON_CreateString((*eventIt).getEventDescription().c_str()); - - cJSON_AddItemToObject(newBranch, "BranchLoc", branchLoc); - cJSON_AddItemToObject(newBranch, "BranchCond", branchCondition); - - cJSON_AddItemToArray(pathInfo, newBranch); - } - - cJSON_AddItemToObject(bugDescription, "ConditionalFreePath", pathInfo); - - return bugDescription; -} - -void PartialLeakBug::printBugToTerminal() const -{ - SVFUtil::errs() << SVFUtil::bugMsg2("\t PartialLeak :") << " memory allocation at : (" - << GenericBug::getLoc() << ")\n"; - - SVFUtil::errs() << "\t\t conditional free path: \n"; - auto lastBranchEventIt = bugEventStack.end() - 1; - for(auto eventIt = bugEventStack.begin(); eventIt != lastBranchEventIt; eventIt++) - { - SVFUtil::errs() << "\t\t --> (" << (*eventIt).getEventLoc() << "|" << (*eventIt).getEventDescription() << ") \n"; - } - SVFUtil::errs() << "\n"; -} - -cJSON * DoubleFreeBug::getBugDescription() const -{ - cJSON *bugDescription = cJSON_CreateObject(); - - cJSON *pathInfo = cJSON_CreateArray(); - auto lastBranchEventIt = bugEventStack.end() - 1; - for(auto eventIt = bugEventStack.begin(); eventIt != lastBranchEventIt; eventIt++) - { - cJSON *newBranch = cJSON_CreateObject(); - cJSON *branchLoc = cJSON_Parse((*eventIt).getEventLoc().c_str()); - if(branchLoc == nullptr) branchLoc = cJSON_CreateObject(); - cJSON *branchCondition = cJSON_CreateString((*eventIt).getEventDescription().c_str()); - - cJSON_AddItemToObject(newBranch, "BranchLoc", branchLoc); - cJSON_AddItemToObject(newBranch, "BranchCond", branchCondition); - - cJSON_AddItemToArray(pathInfo, newBranch); - } - cJSON_AddItemToObject(bugDescription, "DoubleFreePath", pathInfo); - - return bugDescription; -} - -void DoubleFreeBug::printBugToTerminal() const -{ - SVFUtil::errs() << SVFUtil::bugMsg2("\t Double Free :") << " memory allocation at : (" - << GenericBug::getLoc() << ")\n"; - - SVFUtil::errs() << "\t\t double free path: \n"; - auto lastBranchEventIt = bugEventStack.end() - 1; - for(auto eventIt = bugEventStack.begin(); eventIt != lastBranchEventIt; eventIt++) - { - SVFUtil::errs() << "\t\t --> (" << (*eventIt).getEventLoc() << "|" << (*eventIt).getEventDescription() << ") \n"; - } - SVFUtil::errs() << "\n"; -} - -cJSON * FileNeverCloseBug::getBugDescription() const -{ - cJSON *bugDescription = cJSON_CreateObject(); - return bugDescription; -} - -void FileNeverCloseBug::printBugToTerminal() const -{ - SVFUtil::errs() << SVFUtil::bugMsg1("\t FileNeverClose :") << " file open location at : (" - << GenericBug::getLoc() << ")\n"; -} - -cJSON * FilePartialCloseBug::getBugDescription() const -{ - cJSON *bugDescription = cJSON_CreateObject(); - - cJSON *pathInfo = cJSON_CreateArray(); - - auto lastBranchEventIt = bugEventStack.end() - 1; - for(auto eventIt = bugEventStack.begin(); eventIt != lastBranchEventIt; eventIt++) - { - cJSON *newBranch = cJSON_CreateObject(); - cJSON *branchLoc = cJSON_Parse((*eventIt).getEventLoc().c_str()); - if(branchLoc == nullptr) branchLoc = cJSON_CreateObject(); - cJSON *branchCondition = cJSON_CreateString((*eventIt).getEventDescription().c_str()); - - cJSON_AddItemToObject(newBranch, "BranchLoc", branchLoc); - cJSON_AddItemToObject(newBranch, "BranchCond", branchCondition); - - cJSON_AddItemToArray(pathInfo, newBranch); - } - cJSON_AddItemToObject(bugDescription, "ConditionalFileClosePath", pathInfo); - - return bugDescription; -} - -void FilePartialCloseBug::printBugToTerminal() const -{ - SVFUtil::errs() << SVFUtil::bugMsg2("\t PartialFileClose :") << " file open location at : (" - << GenericBug::getLoc() << ")\n"; - - SVFUtil::errs() << "\t\t conditional file close path: \n"; - auto lastBranchEventIt = bugEventStack.end() - 1; - for(auto eventIt = bugEventStack.begin(); eventIt != lastBranchEventIt; eventIt++) - { - SVFUtil::errs() << "\t\t --> (" << (*eventIt).getEventLoc() << "|" << (*eventIt).getEventDescription() << ") \n"; - } - SVFUtil::errs() << "\n"; -} - -cJSON *FullNullPtrDereferenceBug::getBugDescription() const -{ - cJSON *bugDescription = cJSON_CreateObject(); - return bugDescription; -} - -void FullNullPtrDereferenceBug::printBugToTerminal() const -{ - SVFUtil::errs() << SVFUtil::bugMsg2("\t FullNullPtrDereference :") << " dereference at : (" - << GenericBug::getLoc() << ")\n"; -} - -cJSON *PartialNullPtrDereferenceBug::getBugDescription() const -{ - cJSON *bugDescription = cJSON_CreateObject(); - return bugDescription; -} - -void PartialNullPtrDereferenceBug::printBugToTerminal() const -{ - SVFUtil::errs() << SVFUtil::bugMsg2("\t PartialNullPtrDereference :") << " dereference at : (" - << GenericBug::getLoc() << ")\n"; -} - -const std::string SVFBugEvent::getFuncName() const -{ - return eventInst->getFunction()->getName(); -} - -const std::string SVFBugEvent::getEventLoc() const -{ - return eventInst->getSourceLoc(); -} - -const std::string SVFBugEvent::getEventDescription() const -{ - switch(getEventType()) - { - case SVFBugEvent::Branch: - { - if (typeAndInfoFlag & BRANCHFLAGMASK) - { - return "True"; - } - else - { - return "False"; - } - break; - } - case SVFBugEvent::CallSite: - { - std::string description("calls "); - const SVFFunction *callee = SVFUtil::getCallee(eventInst); - if(callee == nullptr) - { - description += ""; - } - else - { - description += callee->getName(); - } - return description; - break; - } - case SVFBugEvent::SourceInst: - { - return "None"; - } - default: - { - assert(false && "No such type of event!"); - abort(); - } - } -} - -SVFBugReport::~SVFBugReport() -{ - for(auto bugIt: bugSet) - { - delete bugIt; - } -} - -void SVFBugReport::dumpToJsonFile(const std::string& filePath) const -{ - std::map eventType2Str = - { - {SVFBugEvent::CallSite, "call site"}, - {SVFBugEvent::Caller, "caller"}, - {SVFBugEvent::Loop, "loop"}, - {SVFBugEvent::Branch, "branch"} - }; - - ofstream jsonFile(filePath, ios::out); - - jsonFile << "{\n"; - - /// Add defects - jsonFile << "\"Defects\": [\n"; - size_t commaCounter = bugSet.size() - 1; - for (auto bugPtr : bugSet) - { - cJSON *singleDefect = cJSON_CreateObject(); - - /// Add bug information to JSON - cJSON *bugType = cJSON_CreateString( - GenericBug::BugType2Str.at(bugPtr->getBugType()).c_str()); - cJSON_AddItemToObject(singleDefect, "DefectType", bugType); - - cJSON *bugLoc = cJSON_Parse(bugPtr->getLoc().c_str()); - if (bugLoc == nullptr) - { - bugLoc = cJSON_CreateObject(); - } - cJSON_AddItemToObject(singleDefect, "Location", bugLoc); - - cJSON *bugFunction = cJSON_CreateString( - bugPtr->getFuncName().c_str()); - cJSON_AddItemToObject(singleDefect, "Function", bugFunction); - - cJSON_AddItemToObject(singleDefect, "Description", - bugPtr->getBugDescription()); - - /// Add event information to JSON - cJSON *eventList = cJSON_CreateArray(); - const GenericBug::EventStack &bugEventStack = bugPtr->getEventStack(); - if (BufferOverflowBug::classof(bugPtr)) - { - // Add only when bug is context sensitive - for (const SVFBugEvent &event : bugEventStack) - { - if (event.getEventType() == SVFBugEvent::SourceInst) - { - continue; - } - - cJSON *singleEvent = cJSON_CreateObject(); - // Event type - cJSON *eventType = cJSON_CreateString( - eventType2Str[event.getEventType()].c_str()); - cJSON_AddItemToObject(singleEvent, "EventType", eventType); - // Function name - cJSON *eventFunc = cJSON_CreateString( - event.getFuncName().c_str()); - cJSON_AddItemToObject(singleEvent, "Function", eventFunc); - // Event loc - cJSON *eventLoc = cJSON_Parse(event.getEventLoc().c_str()); - if (eventLoc == nullptr) - { - eventLoc = cJSON_CreateObject(); - } - cJSON_AddItemToObject(singleEvent, "Location", eventLoc); - // Event description - cJSON *eventDescription = cJSON_CreateString( - event.getEventDescription().c_str()); - cJSON_AddItemToObject(singleEvent, "Description", eventDescription); - - cJSON_AddItemToArray(eventList, singleEvent); - } - } - cJSON_AddItemToObject(singleDefect, "Events", eventList); - - /// Dump single bug to JSON string and write to file - char *singleDefectStr = cJSON_Print(singleDefect); - jsonFile << singleDefectStr; - if (commaCounter != 0) - { - jsonFile << ",\n"; - } - commaCounter--; - - /// Destroy the cJSON object - cJSON_Delete(singleDefect); - } - jsonFile << "\n],\n"; - - /// Add program information - jsonFile << "\"Time\": " << time << ",\n"; - jsonFile << "\"Memory\": \"" << mem << "\",\n"; - jsonFile << "\"Coverage\": " << coverage << "\n"; - - jsonFile << "}"; - jsonFile.close(); -} diff --git a/svf/lib/Util/SVFModule.cpp b/svf/lib/Util/SVFModule.cpp new file mode 100644 index 000000000..86525b517 --- /dev/null +++ b/svf/lib/Util/SVFModule.cpp @@ -0,0 +1,39 @@ +//===- SVFModule.cpp -- Helper functions for pointer analysis--------------// +// +// SVF: Static Value-Flow Analysis +// +// Copyright (C) <2013-2017> +// + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// +//===----------------------------------------------------------------------===// + + +#include "Util/SVFModule.h" +#include "SVF-FE/SymbolTableInfo.h" +#include "Util/SVFUtil.h" + +using namespace SVF; + +void SVFModule::buildSymbolTableInfo() +{ + if (!pagReadFromTXT()) + { + /// building symbol table + DBOUT(DGENERAL, SVFUtil::outs() << SVFUtil::pasMsg("Building Symbol table ...\n")); + SymbolTableInfo *symInfo = SymbolTableInfo::SymbolInfo(); + symInfo->buildMemModel(this); + } +} \ No newline at end of file diff --git a/svf/lib/Util/SVFStat.cpp b/svf/lib/Util/SVFStat.cpp deleted file mode 100644 index 7cab07720..000000000 --- a/svf/lib/Util/SVFStat.cpp +++ /dev/null @@ -1,268 +0,0 @@ -//===- SVFStat.cpp -- Base class for statistics---------------------------------// -// -// SVF: Static Value-Flow Analysis -// -// Copyright (C) <2013-> -// - -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU Affero General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Affero General Public License for more details. - -// You should have received a copy of the GNU Affero General Public License -// along with this program. If not, see . -// -//===----------------------------------------------------------------------===// - -/* - * SVFStat.cpp - * - * Created on: Sep 1, 2022 - * Author: Xiao Cheng - */ - -#include "Util/Options.h" -#include "Util/SVFStat.h" - -using namespace SVF; -using namespace std; - -double SVFStat::timeOfBuildingLLVMModule = 0; -double SVFStat::timeOfBuildingSVFIR = 0; -double SVFStat::timeOfBuildingSymbolTable = 0; -bool SVFStat::printGeneralStats = true; - -SVFStat::SVFStat() : startTime(0), endTime(0) -{ - assert((Options::ClockType() == ClockType::Wall || Options::ClockType() == ClockType::CPU) - && "PTAStat: unknown clock type!"); -} - -double SVFStat::getClk(bool mark) -{ - if (Options::MarkedClocksOnly() && !mark) return 0.0; - - if (Options::ClockType() == ClockType::Wall) - { - struct timespec time; - clock_gettime(CLOCK_MONOTONIC, &time); - return (double)(time.tv_nsec + time.tv_sec * 1000000000) / 1000000.0; - } - else if (Options::ClockType() == ClockType::CPU) - { - return CLOCK_IN_MS(); - } - - assert(false && "PTAStat::getClk: unknown clock type"); - abort(); -} - -void SVFStat::printStat(string statname) -{ - - std::string moduleName(SVFIR::getPAG()->getModule()->getModuleIdentifier()); - std::vector names = SVFUtil::split(moduleName,'/'); - if (names.size() > 1) - { - moduleName = names[names.size() - 1]; - } - - SVFUtil::outs() << "\n*********" << statname << "***************\n"; - SVFUtil::outs() << "################ (program : " << moduleName << ")###############\n"; - SVFUtil::outs().flags(std::ios::left); - unsigned field_width = 20; - - for(NUMStatMap::iterator it = generalNumMap.begin(), eit = generalNumMap.end(); it!=eit; ++it) - { - // format out put with width 20 space - std::cout << std::setw(field_width) << it->first << it->second << "\n"; - } - - if(!timeStatMap.empty()) - SVFUtil::outs() << "----------------Time and memory stats--------------------\n"; - for(TIMEStatMap::iterator it = timeStatMap.begin(), eit = timeStatMap.end(); it!=eit; ++it) - { - // format out put with width 20 space - SVFUtil::outs() << std::setw(field_width) << it->first << it->second << "\n"; - } - - if(!PTNumStatMap.empty()) - SVFUtil::outs() << "----------------Numbers stats----------------------------\n"; - for(NUMStatMap::iterator it = PTNumStatMap.begin(), eit = PTNumStatMap.end(); it!=eit; ++it) - { - // format out put with width 20 space - SVFUtil::outs() << std::setw(field_width) << it->first << it->second << "\n"; - } - SVFUtil::outs() << "#######################################################" << std::endl; - SVFUtil::outs().flush(); - generalNumMap.clear(); - PTNumStatMap.clear(); - timeStatMap.clear(); -} - -void SVFStat::performStat() -{ - - /// SVF's general statistics are only printed once even if you run multiple anayses - if(printGeneralStats == false) - return; - - SVFIR* pag = SVFIR::getPAG(); - u32_t numOfFunction = 0; - u32_t numOfGlobal = 0; - u32_t numOfStack = 0; - u32_t numOfHeap = 0; - u32_t numOfHasVarArray = 0; - u32_t numOfHasVarStruct = 0; - u32_t numOfHasConstArray = 0; - u32_t numOfHasConstStruct = 0; - u32_t numOfScalar = 0; - u32_t numOfConstant = 0; - u32_t fiObjNumber = 0; - u32_t fsObjNumber = 0; - Set memObjSet; - for(SVFIR::iterator it = pag->begin(), eit = pag->end(); it!=eit; ++it) - { - PAGNode* node = it->second; - if(ObjVar* obj = SVFUtil::dyn_cast(node)) - { - const MemObj* mem = obj->getMemObj(); - if (memObjSet.insert(mem->getId()).second == false) - continue; - if(mem->isBlackHoleObj()) - continue; - if(mem->isFunction()) - numOfFunction++; - if(mem->isGlobalObj()) - numOfGlobal++; - if(mem->isStack()) - numOfStack++; - if(mem->isHeap()) - numOfHeap++; - if(mem->isVarArray()) - numOfHasVarArray++; - if(mem->isVarStruct()) - numOfHasVarStruct++; - if(mem->isConstantArray()) - numOfHasConstArray++; - if(mem->isConstantStruct()) - numOfHasConstStruct++; - if(mem->getType()->isPointerTy() == false) - numOfScalar++; - if(mem->isConstDataOrConstGlobal()) - numOfConstant++; - - if (mem->isFieldInsensitive()) - fiObjNumber++; - else - fsObjNumber++; - } - } - - - - generalNumMap["TotalPointers"] = pag->getValueNodeNum() + pag->getFieldValNodeNum(); - generalNumMap["TotalObjects"] = pag->getObjectNodeNum(); - generalNumMap["TotalFieldObjects"] = pag->getFieldObjNodeNum(); - generalNumMap["MaxStructSize"] = SymbolTableInfo::SymbolInfo()->getMaxStructSize(); - generalNumMap["TotalSVFStmts"] = pag->getPAGEdgeNum(); - generalNumMap["TotalPTASVFStmts"] = pag->getPTAPAGEdgeNum(); - generalNumMap["FIObjNum"] = fiObjNumber; - generalNumMap["FSObjNum"] = fsObjNumber; - - generalNumMap["AddrsNum"] = pag->getSVFStmtSet(SVFStmt::Addr).size(); - generalNumMap["LoadsNum"] = pag->getSVFStmtSet(SVFStmt::Load).size(); - generalNumMap["StoresNum"] = pag->getSVFStmtSet(SVFStmt::Store).size(); - generalNumMap["CopysNum"] = pag->getSVFStmtSet(SVFStmt::Copy).size(); - generalNumMap["GepsNum"] = pag->getSVFStmtSet(SVFStmt::Gep).size(); - generalNumMap["CallsNum"] = pag->getSVFStmtSet(SVFStmt::Call).size(); - generalNumMap["ReturnsNum"] = pag->getSVFStmtSet(SVFStmt::Ret).size(); - - generalNumMap["FunctionObjs"] = numOfFunction; - generalNumMap["GlobalObjs"] = numOfGlobal; - generalNumMap["HeapObjs"] = numOfHeap; - generalNumMap["StackObjs"] = numOfStack; - - generalNumMap["VarStructObj"] = numOfHasVarStruct; - generalNumMap["VarArrayObj"] = numOfHasVarArray; - generalNumMap["ConstStructObj"] = numOfHasConstStruct; - generalNumMap["ConstArrayObj"] = numOfHasConstArray; - generalNumMap["NonPtrObj"] = numOfScalar; - generalNumMap["ConstantObj"] = numOfConstant; - - generalNumMap["IndCallSites"] = pag->getIndirectCallsites().size(); - generalNumMap["TotalCallSite"] = pag->getCallSiteSet().size(); - - timeStatMap["LLVMIRTime"] = SVFStat::timeOfBuildingLLVMModule; - timeStatMap["SymbolTableTime"] = SVFStat::timeOfBuildingSymbolTable; - timeStatMap["SVFIRTime"] = SVFStat::timeOfBuildingSVFIR; - - // REFACTOR-TODO bitcastInstStat(); - branchStat(); - - printStat("General Stats"); - - printGeneralStats = false; -} - - -void SVFStat::branchStat() -{ - SVFModule* module = SVFIR::getPAG()->getModule(); - u32_t numOfBB_2Succ = 0; - u32_t numOfBB_3Succ = 0; - for (SVFModule::const_iterator funIter = module->begin(), funEiter = module->end(); - funIter != funEiter; ++funIter) - { - const SVFFunction* func = *funIter; - for (SVFFunction::const_iterator bbIt = func->begin(), bbEit = func->end(); - bbIt != bbEit; ++bbIt) - { - const SVFBasicBlock* bb = *bbIt; - u32_t numOfSucc = bb->getNumSuccessors(); - if (numOfSucc == 2) - numOfBB_2Succ++; - else if (numOfSucc > 2) - numOfBB_3Succ++; - } - } - - generalNumMap["BBWith2Succ"] = numOfBB_2Succ; - generalNumMap["BBWith3Succ"] = numOfBB_3Succ; -} - -/* REFACTOR-TODO -void PTAStat::bitcastInstStat() -{ - SVFModule* module = pta->getModule(); - u32_t numberOfBitCast = 0; - for (SVFModule::llvm_const_iterator funIter = module->llvmFunBegin(), funEiter = module->llvmFunEnd(); - funIter != funEiter; ++funIter) - { - const Function* func = *funIter; - for (Function::const_iterator bbIt = func->begin(), bbEit = func->end(); - bbIt != bbEit; ++bbIt) - { - const BasicBlock& bb = *bbIt; - for (BasicBlock::const_iterator instIt = bb.begin(), instEit = bb.end(); - instIt != instEit; ++instIt) - { - const Instruction& inst = *instIt; - if (const BitCastInst* bitcast = SVFUtil::dyn_cast(&inst)) - { - if (SVFUtil::isa(bitcast->getSrcTy())) - numberOfBitCast++; - } - } - } - } - - generalNumMap["BitCastNumber"] = numberOfBitCast; -} -*/ diff --git a/svf/lib/Util/SVFUtil.cpp b/svf/lib/Util/SVFUtil.cpp index 9579c04b0..ca4fbc35b 100644 --- a/svf/lib/Util/SVFUtil.cpp +++ b/svf/lib/Util/SVFUtil.cpp @@ -6,16 +6,16 @@ // // This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU Affero General Public License as published by +// it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Affero General Public License for more details. +// GNU General Public License for more details. -// You should have received a copy of the GNU Affero General Public License +// You should have received a copy of the GNU General Public License // along with this program. If not, see . // //===----------------------------------------------------------------------===// @@ -29,7 +29,7 @@ #include "Util/Options.h" #include "Util/SVFUtil.h" -#include "MemoryModel/PointsTo.h" +#include "SVF-FE/LLVMUtil.h" #include /// increase stack size @@ -50,7 +50,7 @@ using namespace SVF; /*! * print successful message by converting a string into green string output */ -std::string SVFUtil::sucMsg(const std::string& msg) +std::string SVFUtil::sucMsg(std::string msg) { return KGRN + msg + KNRM; } @@ -58,37 +58,36 @@ std::string SVFUtil::sucMsg(const std::string& msg) /*! * print warning message by converting a string into yellow string output */ -std::string SVFUtil::wrnMsg(const std::string& msg) +std::string SVFUtil::wrnMsg(std::string msg) { return KYEL + msg + KNRM; } -void SVFUtil::writeWrnMsg(const std::string& msg) +void SVFUtil::writeWrnMsg(std::string msg) { - if (Options::DisableWarn()) - return; + if(Options::DisableWarn) return; outs() << wrnMsg(msg) << "\n"; } /*! * print error message by converting a string into red string output */ -std::string SVFUtil::errMsg(const std::string& msg) +std::string SVFUtil::errMsg(std::string msg) { return KRED + msg + KNRM; } -std::string SVFUtil::bugMsg1(const std::string& msg) +std::string SVFUtil::bugMsg1(std::string msg) { return KYEL + msg + KNRM; } -std::string SVFUtil::bugMsg2(const std::string& msg) +std::string SVFUtil::bugMsg2(std::string msg) { return KPUR + msg + KNRM; } -std::string SVFUtil::bugMsg3(const std::string& msg) +std::string SVFUtil::bugMsg3(std::string msg) { return KCYA + msg + KNRM; } @@ -96,7 +95,7 @@ std::string SVFUtil::bugMsg3(const std::string& msg) /*! * print each pass/phase message by converting a string into blue string output */ -std::string SVFUtil::pasMsg(const std::string& msg) +std::string SVFUtil::pasMsg(std::string msg) { return KBLU + msg + KNRM; } @@ -123,10 +122,10 @@ void SVFUtil::dumpPointsToList(const PointsToList& ptl) { outs() << "{"; for (PointsToList::const_iterator ii = ptl.begin(), ie = ptl.end(); - ii != ie; ii++) + ii != ie; ii++) { auto bs = *ii; - dumpSet(bs); + dumpSparseSet(bs); } outs() << "}\n"; } @@ -144,7 +143,7 @@ void SVFUtil::dumpAliasSet(unsigned node, NodeBS bs) /*! * Dump bit vector set */ -void SVFUtil::dumpSet(NodeBS bs, OutStream & O) +void SVFUtil::dumpSet(NodeBS bs, raw_ostream & O) { for (NodeBS::iterator ii = bs.begin(), ie = bs.end(); ii != ie; ii++) @@ -153,18 +152,10 @@ void SVFUtil::dumpSet(NodeBS bs, OutStream & O) } } -void SVFUtil::dumpSet(PointsTo pt, OutStream &o) -{ - for (NodeID n : pt) - { - o << " " << n << " "; - } -} - /*! * Print memory usage */ -void SVFUtil::reportMemoryUsageKB(const std::string& infor, OutStream & O) +void SVFUtil::reportMemoryUsageKB(const std::string& infor, raw_ostream & O) { u32_t vmrss, vmsize; if (getMemoryUsageKB(&vmrss, &vmsize)) @@ -176,7 +167,7 @@ void SVFUtil::reportMemoryUsageKB(const std::string& infor, OutStream & O) */ bool SVFUtil::getMemoryUsageKB(u32_t* vmrss_kb, u32_t* vmsize_kb) { - /* Get the current process' status file from the proc filesystem */ + /* Get the the current process' status file from the proc filesystem */ char buffer[8192]; FILE* procfile = fopen("/proc/self/status", "r"); if(procfile) @@ -189,7 +180,7 @@ bool SVFUtil::getMemoryUsageKB(u32_t* vmrss_kb, u32_t* vmsize_kb) } else { - SVFUtil::writeWrnMsg(" /proc/self/status file not exit!"); + fputs ("/proc/self/status file not exit\n",stderr); return false; } fclose(procfile); @@ -236,49 +227,162 @@ void SVFUtil::increaseStackSize() rl.rlim_cur = kStackSize; result = setrlimit(RLIMIT_STACK, &rl); if (result != 0) - writeWrnMsg("setrlimit returned result !=0 \n"); + writeWrnMsg("setrlimit returned result !=0 \n"); } } } + /*! - * Return true if it is an llvm intrinsic instruction -*/ -bool SVFUtil::isIntrinsicInst(const SVFInstruction* inst) + * Get source code line number of a function according to debug info + */ +std::string SVFUtil::getSourceLocOfFunction(const Function *F) { - if (const SVFCallInst* call = SVFUtil::dyn_cast(inst)) + std::string str; + raw_string_ostream rawstr(str); + /* + * https://reviews.llvm.org/D18074?id=50385 + * looks like the relevant + */ + if (llvm::DISubprogram *SP = F->getSubprogram()) { - const SVFFunction* func = call->getCalledFunction(); - if (func && func->isIntrinsic()) - { - return true; - } + if (SP->describes(F)) + rawstr << "in line: " << SP->getLine() << " file: " << SP->getFilename(); } - return false; + return rawstr.str(); } -std::string SVFUtil::hclustMethodToString(hclust_fast_methods method) +/*! + * Get the meta data (line number and file name) info of a LLVM value + */ +std::string SVFUtil::getSourceLoc(const Value* val) { - switch (method) + if(val==nullptr) return "{ empty val }"; + + std::string str; + raw_string_ostream rawstr(str); + rawstr << "{ "; + + if (const Instruction *inst = SVFUtil::dyn_cast(val)) + { + if (SVFUtil::isa(inst)) + { + for (llvm::DbgInfoIntrinsic *DII : FindDbgAddrUses(const_cast(inst))) + { + if (llvm::DbgDeclareInst *DDI = SVFUtil::dyn_cast(DII)) + { + llvm::DIVariable *DIVar = SVFUtil::cast(DDI->getVariable()); + rawstr << "ln: " << DIVar->getLine() << " fl: " << DIVar->getFilename(); + break; + } + } + } + else if (MDNode *N = inst->getMetadata("dbg")) // Here I is an LLVM instruction + { + llvm::DILocation* Loc = SVFUtil::cast(N); // DILocation is in DebugInfo.h + unsigned Line = Loc->getLine(); + unsigned Column = Loc->getColumn(); + StringRef File = Loc->getFilename(); + //StringRef Dir = Loc.getDirectory(); + if(File.str().empty() || Line == 0) { + auto inlineLoc = Loc->getInlinedAt(); + if(inlineLoc) { + Line = inlineLoc->getLine(); + Column = inlineLoc->getColumn(); + File = inlineLoc->getFilename(); + } + } + rawstr << "ln: " << Line << " cl: " << Column << " fl: " << File; + } + } + else if (const Argument* argument = SVFUtil::dyn_cast(val)) + { + if (argument->getArgNo()%10 == 1) + rawstr << argument->getArgNo() << "st"; + else if (argument->getArgNo()%10 == 2) + rawstr << argument->getArgNo() << "nd"; + else if (argument->getArgNo()%10 == 3) + rawstr << argument->getArgNo() << "rd"; + else + rawstr << argument->getArgNo() << "th"; + rawstr << " arg " << argument->getParent()->getName() << " " + << getSourceLocOfFunction(argument->getParent()); + } + else if (const GlobalVariable* gvar = SVFUtil::dyn_cast(val)) + { + rawstr << "Glob "; + NamedMDNode* CU_Nodes = gvar->getParent()->getNamedMetadata("llvm.dbg.cu"); + if(CU_Nodes) + { + for (unsigned i = 0, e = CU_Nodes->getNumOperands(); i != e; ++i) + { + llvm::DICompileUnit *CUNode = SVFUtil::cast(CU_Nodes->getOperand(i)); + for (llvm::DIGlobalVariableExpression *GV : CUNode->getGlobalVariables()) + { + llvm::DIGlobalVariable * DGV = GV->getVariable(); + + if(DGV->getName() == gvar->getName()) + { + rawstr << "ln: " << DGV->getLine() << " fl: " << DGV->getFilename(); + } + + } + } + } + } + else if (const Function* func = SVFUtil::dyn_cast(val)) + { + rawstr << getSourceLocOfFunction(func); + } + else if (const BasicBlock* bb = SVFUtil::dyn_cast(val)) { - case HCLUST_METHOD_SINGLE: - return "single"; - case HCLUST_METHOD_COMPLETE: - return "complete"; - case HCLUST_METHOD_AVERAGE: - return "average"; - case HCLUST_METHOD_MEDIAN: - return "median"; - case HCLUST_METHOD_SVF_BEST: - return "svf-best"; - default: - assert(false && "SVFUtil::hclustMethodToString: unknown method"); - abort(); + rawstr << "basic block: " << bb->getName() << " " << getSourceLoc(bb->getFirstNonPHI()); + } + else if(SVFUtil::isConstantData(val)) + { + rawstr << "constant data"; + } + else + { + rawstr << "Can only get source location for instruction, argument, global var, function or constant data."; + } + rawstr << " }"; + + return rawstr.str(); +} + + +/*! + * return string of an LLVM Value + */ +const std::string SVFUtil::value2String(const Value* value) { + std::string str; + raw_string_ostream rawstr(str); + if(value){ + if(const SVF::Function* fun = SVFUtil::dyn_cast(value)) + rawstr << " " << fun->getName() << " "; + else + rawstr << " " << *value << " "; + rawstr << getSourceLoc(value); + } + return rawstr.str(); +} + +void SVFFunction::viewCFG() { + if (fun != nullptr) { + fun->viewCFG(); + } +} + +void SVFFunction::viewCFGOnly() { + if (fun != nullptr) { + fun->viewCFGOnly(); } } void SVFUtil::timeLimitReached(int) { + std::cout.flush(); SVFUtil::outs().flush(); // TODO: output does not indicate which time limit is reached. // This can be better in the future. @@ -311,16 +415,3 @@ void SVFUtil::stopAnalysisLimitTimer(bool limitTimerSet) { if (limitTimerSet) alarm(0); } - -/// Match arguments for callsite at caller and callee -/// if the arg size does not match then we do not need to connect this parameter -/// unless the callee is a variadic function (the first parameter of variadic function is its parameter number) -/// e.g., void variadicFoo(int num, ...); variadicFoo(5, 1,2,3,4,5) -/// for variadic function, callsite arg size must be greater than or equal to callee arg size -bool SVFUtil::matchArgs(const SVFInstruction* cs, const SVFFunction* callee) -{ - if (callee->isVarArg() || ThreadAPI::getThreadAPI()->isTDFork(cs)) - return getSVFCallSite(cs).arg_size() >= callee->arg_size(); - else - return getSVFCallSite(cs).arg_size() == callee->arg_size(); -} diff --git a/svf/lib/Util/ThreadAPI.cpp b/svf/lib/Util/ThreadAPI.cpp index 5933c6486..d87e9a5ec 100644 --- a/svf/lib/Util/ThreadAPI.cpp +++ b/svf/lib/Util/ThreadAPI.cpp @@ -6,16 +6,16 @@ // // This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU Affero General Public License as published by +// it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Affero General Public License for more details. +// GNU General Public License for more details. -// You should have received a copy of the GNU Affero General Public License +// You should have received a copy of the GNU General Public License // along with this program. If not, see . // //===----------------------------------------------------------------------===// @@ -32,7 +32,6 @@ #include "Util/ThreadAPI.h" #include "Util/SVFUtil.h" -#include "SVFIR/SVFIR.h" #include /// std output #include @@ -132,7 +131,7 @@ void ThreadAPI::init() /*! * */ -const SVFFunction* ThreadAPI::getCallee(const SVFInstruction *inst) const +const SVFFunction* ThreadAPI::getCallee(const Instruction *inst) const { return SVFUtil::getCallee(inst); } @@ -148,33 +147,15 @@ const SVFFunction* ThreadAPI::getCallee(const CallSite cs) const /*! * */ -const CallSite ThreadAPI::getSVFCallSite(const SVFInstruction *inst) const +const CallSite ThreadAPI::getLLVMCallSite(const Instruction *inst) const { - return SVFUtil::getSVFCallSite(inst); -} - -const SVFValue* ThreadAPI::getJoinedThread(const SVFInstruction *inst) const -{ - assert(isTDJoin(inst) && "not a thread join function!"); - CallSite cs = getSVFCallSite(inst); - const SVFValue* join = cs.getArgument(0); - const SVFVar* var = PAG::getPAG()->getGNode(PAG::getPAG()->getValueNode(join)); - for(const SVFStmt* stmt : var->getInEdges()) - { - if(SVFUtil::isa(stmt)) - return stmt->getSrcNode()->getValue(); - } - if(SVFUtil::isa(join)) - return join; - - assert(false && "the value of the first argument at join is not a load instruction?"); - return nullptr; + return SVFUtil::getLLVMCallSite(inst); } /*! * */ -void ThreadAPI::statInit(Map& tdAPIStatMap) +void ThreadAPI::statInit(StringMap& tdAPIStatMap) { tdAPIStatMap["pthread_create"] = 0; @@ -217,144 +198,140 @@ void ThreadAPI::statInit(Map& tdAPIStatMap) void ThreadAPI::performAPIStat(SVFModule* module) { - Map tdAPIStatMap; + StringMap tdAPIStatMap; statInit(tdAPIStatMap); - for (SVFModule::const_iterator it = module->begin(), eit = module->end(); it != eit; ++it) + for (SVFModule::llvm_iterator it = module->llvmFunBegin(), eit = module->llvmFunEnd(); it != eit; + ++it) { - for (SVFFunction::const_iterator bit = (*it)->begin(), ebit = (*it)->end(); bit != ebit; ++bit) + + for (inst_iterator II = inst_begin(*it), E = inst_end(*it); II != E; + ++II) { - const SVFBasicBlock* bb = *bit; - for (SVFBasicBlock::const_iterator ii = bb->begin(), eii = bb->end(); ii != eii; ++ii) + const Instruction *inst = &*II; + if (!SVFUtil::isa(inst)) + continue; + const SVFFunction* fun = getCallee(inst); + TD_TYPE type = getType(fun); + switch (type) + { + case TD_FORK: + { + tdAPIStatMap["pthread_create"]++; + break; + } + case TD_JOIN: + { + tdAPIStatMap["pthread_join"]++; + break; + } + case TD_ACQUIRE: + { + tdAPIStatMap["pthread_mutex_lock"]++; + break; + } + case TD_TRY_ACQUIRE: + { + tdAPIStatMap["pthread_mutex_trylock"]++; + break; + } + case TD_RELEASE: + { + tdAPIStatMap["pthread_mutex_unlock"]++; + break; + } + case TD_CANCEL: + { + tdAPIStatMap["pthread_cancel"]++; + break; + } + case TD_EXIT: + { + tdAPIStatMap["pthread_exit"]++; + break; + } + case TD_DETACH: + { + tdAPIStatMap["pthread_detach"]++; + break; + } + case TD_COND_WAIT: { - const SVFInstruction* svfInst = *ii; - if (!SVFUtil::isCallSite(svfInst)) - continue; - const SVFFunction* fun = getCallee(svfInst); - TD_TYPE type = getType(fun); - switch (type) - { - case TD_FORK: - { - tdAPIStatMap["pthread_create"]++; - break; - } - case TD_JOIN: - { - tdAPIStatMap["pthread_join"]++; - break; - } - case TD_ACQUIRE: - { - tdAPIStatMap["pthread_mutex_lock"]++; - break; - } - case TD_TRY_ACQUIRE: - { - tdAPIStatMap["pthread_mutex_trylock"]++; - break; - } - case TD_RELEASE: - { - tdAPIStatMap["pthread_mutex_unlock"]++; - break; - } - case TD_CANCEL: - { - tdAPIStatMap["pthread_cancel"]++; - break; - } - case TD_EXIT: - { - tdAPIStatMap["pthread_exit"]++; - break; - } - case TD_DETACH: - { - tdAPIStatMap["pthread_detach"]++; - break; - } - case TD_COND_WAIT: - { - tdAPIStatMap["pthread_cond_wait"]++; - break; - } - case TD_COND_SIGNAL: - { - tdAPIStatMap["pthread_cond_signal"]++; - break; - } - case TD_COND_BROADCAST: - { - tdAPIStatMap["pthread_cond_broadcast"]++; - break; - } - case TD_CONDVAR_INI: - { - tdAPIStatMap["pthread_cond_init"]++; - break; - } - case TD_CONDVAR_DESTROY: - { - tdAPIStatMap["pthread_cond_destroy"]++; - break; - } - case TD_MUTEX_INI: - { - tdAPIStatMap["pthread_mutex_init"]++; - break; - } - case TD_MUTEX_DESTROY: - { - tdAPIStatMap["pthread_mutex_destroy"]++; - break; - } - case TD_BAR_INIT: - { - tdAPIStatMap["pthread_barrier_init"]++; - break; - } - case TD_BAR_WAIT: - { - tdAPIStatMap["pthread_barrier_wait"]++; - break; - } - case HARE_PAR_FOR: - { - tdAPIStatMap["hare_parallel_for"]++; - break; - } - case TD_DUMMY: - { - break; - } - } + tdAPIStatMap["pthread_cond_wait"]++; + break; + } + case TD_COND_SIGNAL: + { + tdAPIStatMap["pthread_cond_signal"]++; + break; + } + case TD_COND_BROADCAST: + { + tdAPIStatMap["pthread_cond_broadcast"]++; + break; + } + case TD_CONDVAR_INI: + { + tdAPIStatMap["pthread_cond_init"]++; + break; + } + case TD_CONDVAR_DESTROY: + { + tdAPIStatMap["pthread_cond_destroy"]++; + break; + } + case TD_MUTEX_INI: + { + tdAPIStatMap["pthread_mutex_init"]++; + break; + } + case TD_MUTEX_DESTROY: + { + tdAPIStatMap["pthread_mutex_destroy"]++; + break; + } + case TD_BAR_INIT: + { + tdAPIStatMap["pthread_barrier_init"]++; + break; + } + case TD_BAR_WAIT: + { + tdAPIStatMap["pthread_barrier_wait"]++; + break; + } + case HARE_PAR_FOR: + { + tdAPIStatMap["hare_parallel_for"]++; + break; + } + case TD_DUMMY: + { + break; + } } } } - std::string name(module->getModuleIdentifier()); - std::vector fullNames = SVFUtil::split(name,'/'); - if (fullNames.size() > 1) - { - name = fullNames[fullNames.size() - 1]; - } - SVFUtil::outs() << "################ (program : " << name - << ")###############\n"; - SVFUtil::outs().flags(std::ios::left); + StringRef n(module->getModuleIdentifier()); + StringRef name = n.split('/').second; + name = name.split('.').first; + std::cout << "################ (program : " << name.str() + << ")###############\n"; + std::cout.flags(std::ios::left); unsigned field_width = 20; - for (Map::iterator it = tdAPIStatMap.begin(), eit = + for (llvm::StringMap::iterator it = tdAPIStatMap.begin(), eit = tdAPIStatMap.end(); it != eit; ++it) { - std::string apiName = it->first; + std::string apiName = it->first().str(); // format out put with width 20 space - SVFUtil::outs() << std::setw(field_width) << apiName << " : " << it->second - << "\n"; + std::cout << std::setw(field_width) << apiName << " : " << it->second + << "\n"; } - SVFUtil::outs() << "#######################################################" - << std::endl; + std::cout << "#######################################################" + << std::endl; } diff --git a/svf/lib/Util/TypeBasedHeapCloning.cpp b/svf/lib/Util/TypeBasedHeapCloning.cpp new file mode 100644 index 000000000..abb179804 --- /dev/null +++ b/svf/lib/Util/TypeBasedHeapCloning.cpp @@ -0,0 +1,794 @@ +//===- TypeBasedHeapCloning.cpp -- Type filter/type-based heap cloning base ------------// + +/* + * TypeBasedHeapCloning.cpp + * + * Created on: Feb 08, 2020 + * Author: Mohamad Barbar + */ + +#include "SVF-FE/CPPUtil.h" +#include "Util/TypeBasedHeapCloning.h" +#include "MemoryModel/PointerAnalysisImpl.h" + +using namespace SVF; + +const DIType *TypeBasedHeapCloning::undefType = nullptr; + +const std::string TypeBasedHeapCloning::derefFnName = "deref"; +const std::string TypeBasedHeapCloning::mangledDerefFnName = "_Z5derefv"; + +TypeBasedHeapCloning::TypeBasedHeapCloning(BVDataPTAImpl *pta) +{ + this->pta = pta; +} + +void TypeBasedHeapCloning::setDCHG(DCHGraph *dchg) +{ + this->dchg = dchg; +} + +void TypeBasedHeapCloning::setPAG(PAG *pag) +{ + ppag = pag; +} + +bool TypeBasedHeapCloning::isBlkObjOrConstantObj(NodeID o) const +{ + if (isClone(o)) o = cloneToOriginalObj.at(o); + return SVFUtil::isa(ppag->getPAGNode(o)) && ppag->isBlkObjOrConstantObj(o); +} + +bool TypeBasedHeapCloning::isBase(const DIType *a, const DIType *b) const +{ + assert(dchg && "TBHC: DCHG not set!"); + return dchg->isBase(a, b, true); +} + +bool TypeBasedHeapCloning::isClone(NodeID o) const +{ + return cloneToOriginalObj.find(o) != cloneToOriginalObj.end(); +} + +void TypeBasedHeapCloning::setType(NodeID o, const DIType *t) +{ + objToType.insert({o, t}); +} + +const DIType *TypeBasedHeapCloning::getType(NodeID o) const +{ + assert(objToType.find(o) != objToType.end() && "TBHC: object has no type?"); + return objToType.at(o); +} + +void TypeBasedHeapCloning::setAllocationSite(NodeID o, NodeID site) +{ + objToAllocation.insert({o, site}); +} + +NodeID TypeBasedHeapCloning::getAllocationSite(NodeID o) const +{ + assert(objToAllocation.find(o) != objToAllocation.end() && "TBHC: object has no allocation site?"); + return objToAllocation.at(o); +} + +const NodeBS TypeBasedHeapCloning::getObjsWithClones(void) +{ + NodeBS objs; + for (std::pair oc : objToClones) + { + objs.set(oc.first); + } + + return objs; +} + +void TypeBasedHeapCloning::addClone(NodeID o, NodeID c) +{ + objToClones[o].set(c); +} + +const NodeBS &TypeBasedHeapCloning::getClones(NodeID o) +{ + return objToClones[o]; +} + +void TypeBasedHeapCloning::setOriginalObj(NodeID c, NodeID o) +{ + cloneToOriginalObj.insert({c, o}); +} + +NodeID TypeBasedHeapCloning::getOriginalObj(NodeID c) const +{ + if (isClone(c)) + { + assert(cloneToOriginalObj.find(c) != cloneToOriginalObj.end() + && "TBHC: original object not set for clone?"); + return cloneToOriginalObj.at(c); + } + + return c; +} + +PointsTo &TypeBasedHeapCloning::getFilterSet(NodeID loc) +{ + return locToFilterSet[loc]; +} + +void TypeBasedHeapCloning::addGepToObj(NodeID gep, NodeID base, unsigned offset) +{ + objToGeps[base].set(gep); + const PAGNode *baseNode = ppag->getPAGNode(base); + assert(baseNode && "TBHC: given bad base node?"); + const ObjPN *baseObj = SVFUtil::dyn_cast(baseNode); + assert(baseObj && "TBHC: non-object given for base?"); + // We can use the base or the gep mem. obj.; should be identical. + const MemObj *baseMemObj = baseObj->getMemObj(); + + objToGeps[base].set(gep); + memObjToGeps[baseMemObj][offset].set(gep); +} + +const NodeBS &TypeBasedHeapCloning::getGepObjsFromMemObj(const MemObj *memObj, unsigned offset) +{ + return memObjToGeps[memObj][offset]; +} + +const NodeBS &TypeBasedHeapCloning::getGepObjs(NodeID base) +{ + return objToGeps[base]; +} + +const NodeBS TypeBasedHeapCloning::getGepObjClones(NodeID base, unsigned offset) +{ + assert(dchg && "TBHC: DCHG not set!"); + // Set of GEP objects we will return. + NodeBS geps; + + PAGNode *node = ppag->getPAGNode(base); + assert(node && "TBHC: base object node does not exist."); + ObjPN *baseNode = SVFUtil::dyn_cast(node); + assert(baseNode && "TBHC: base \"object\" node is not an object."); + + // totalOffset is the offset from the real base (i.e. base of base), + // offset is the offset into base, whether it is a field itself or not. + unsigned totalOffset = offset; + if (const GepObjPN *baseGep = SVFUtil::dyn_cast(baseNode)) + { + totalOffset += baseGep->getLocationSet().getOffset(); + } + + const DIType *baseType = getType(base); + + // First field? Just return the whole object; same thing. + // For arrays, we want things to work as normal because an array *object* is more + // like a pointer than a struct object. + if (offset == 0 && baseType->getTag() != dwarf::DW_TAG_array_type) + { + // The base object is the 0 gep object. + addGepToObj(base, base, 0); + geps.set(base); + return geps; + } + + if (baseNode->getMemObj()->isFieldInsensitive()) + { + // If it's field-insensitive, the base represents everything. + geps.set(base); + return geps; + } + + // Caching on offset would improve performance but it seems minimal. + const NodeBS &gepObjs = getGepObjs(base); + for (NodeID gep : gepObjs) + { + PAGNode *node = ppag->getPAGNode(gep); + assert(node && "TBHC: expected gep node doesn't exist."); + assert((SVFUtil::isa(node) || SVFUtil::isa(node)) + && "TBHC: expected a GEP or FI object."); + + if (GepObjPN *gepNode = SVFUtil::dyn_cast(node)) + { + if (gepNode->getLocationSet().getOffset() == totalOffset) + { + geps.set(gep); + } + } + else + { + // Definitely a FIObj (asserted), but we don't want to add it if + // the object is field-sensitive because in that case it actually + // represents the 0th field, not the whole object. + if (baseNode->getMemObj()->isFieldInsensitive()) + { + geps.set(gep); + } + } + } + + if (geps.empty()) + { + // No gep node has even be created, so create one. + NodeID newGep; + LocationSet newLS; + // fldIdx is what is returned by getOffset. + newLS.setFldIdx(totalOffset); + + if (isClone(base)) + { + // Don't use ppag->getGepObjNode because base and it's original object + // have the same memory object which is the key PAG uses. + newGep = addCloneGepObjNode(baseNode->getMemObj(), newLS); + } + else + { + newGep = ppag->getGepObjNode(base, newLS); + } + + if (GepObjPN *gep = SVFUtil::dyn_cast(ppag->getPAGNode(newGep))) + { + gep->setBaseNode(base); + } + + addGepToObj(newGep, base, totalOffset); + const DIType *newGepType = nullptr; + if (baseType->getTag() == dwarf::DW_TAG_array_type || baseType->getTag() == dwarf::DW_TAG_pointer_type) + { + if (const DICompositeType *arrayType = SVFUtil::dyn_cast(baseType)) + { + // Array access. + newGepType = arrayType->getBaseType(); + } + else if (const DIDerivedType *ptrType = SVFUtil::dyn_cast(baseType)) + { + // Pointer access. + newGepType = ptrType->getBaseType(); + } + assert(newGepType && "TBHC: newGepType is neither DIComposite nor DIDerived"); + + // Get the canonical type because we got the type from the DIType infrastructure directly. + newGepType = dchg->getCanonicalType(newGepType); + } + else + { + // Must be a struct/class. + // Don't use totalOffset because we're operating on the Gep object which is our parent + // (i.e. field of some base, not the base itself). + newGepType = dchg->getFieldType(getType(base), offset); + } + + setType(newGep, newGepType); + // We call the object created in the non-TBHC analysis the original object. + setOriginalObj(newGep, ppag->getGepObjNode(baseNode->getMemObj(), offset)); + setAllocationSite(newGep, 0); + + geps.set(newGep); + } + + return geps; +} + +bool TypeBasedHeapCloning::init(NodeID loc, NodeID p, const DIType *tildet, bool reuse, bool gep) +{ + assert(dchg && "TBHC: DCHG not set!"); + bool changed = false; + + const PointsTo &pPt = pta->getPts(p); + // The points-to set we will populate in the loop to fill pPt. + PointsTo pNewPt; + + PointsTo &filterSet = getFilterSet(loc); + for (NodeID o : pPt) + { + // If it's been filtered before, it'll be filtered again. + if (filterSet.test(o)) continue; + + PAGNode *obj = ppag->getPAGNode(o); + assert(obj && "TBHC: pointee object does not exist in PAG?"); + const DIType *tp = getType(o); // tp is t' + + // When an object is field-insensitive, we can't filter on any of the fields' types. + // i.e. a pointer of the field type can safely access an object of the base/struct + // type if that object is field-insensitive. + bool fieldInsensitive = false; + std::vector fieldTypes; + if (ObjPN *obj = SVFUtil::dyn_cast(ppag->getPAGNode(o))) + { + fieldInsensitive = obj->getMemObj()->isFieldInsensitive(); + if (tp != nullptr && (tp->getTag() == dwarf::DW_TAG_structure_type + || tp->getTag() == dwarf::DW_TAG_class_type)) + { + fieldTypes = dchg->getFieldTypes(tp); + } + } + + const Set &aggs = dchg->isAgg(tp) + ? dchg->getAggs(tp) : Set(); + + NodeID prop; + bool filter = false; + if (tp == undefType) + { + // o is uninitialised. + // GEP objects should never be uninitialised; type assigned at creation. + assert(!isGep(obj) && "TBHC: GEP object is untyped!"); + prop = cloneObject(o, tildet, false); + ++numInit; + if (!pta->isHeapMemObj(o) && !SVFUtil::isa(obj)) ++numSGInit; + } + else if (fieldInsensitive && tp && dchg->isFieldOf(tildet, tp)) + { + // Field-insensitive object but the instruction is operating on a field. + prop = o; + ++numTBWU; + if (!pta->isHeapMemObj(o) && !SVFUtil::isa(obj)) ++numSGTBWU; + } + else if (gep && aggs.find(tildet) != aggs.end()) + { + // SVF treats two consecutive GEPs as children to the same load/store. + // Special case for aggregates. + // SVF will transform (for example) + // `1: s = get struct element X from array a; 2: f = get field of struct Y from s;` + // to `1: s = get struct element X from array a; 2: f = get field of struct Y from a;` + // so we want the second instruction to be operating on an object of type + // 'Struct S', not 'Array of S'. + prop = cloneObject(o, tildet, false); + ++numAgg; + if (!pta->isHeapMemObj(o) && !SVFUtil::isa(obj)) ++numSGAgg; + } + else if (isBase(tp, tildet) && tp != tildet + && (reuse || dchg->isFirstField(tp, tildet) || (!reuse && pta->isHeapMemObj(o)))) + { + // Downcast. + // One of three conditions: + // - !reuse && heap: because downcasts should not happen to stack/globals. + // - isFirstField because ^ can happen because when we take the field of a + // struct that is a struct, we get its first field, then it may downcast + // back to the struct at another GEP. + // TODO: this can probably be solved more cleanly. + // - reuse: because it can happen to stack/heap objects. + prop = cloneObject(o, tildet, reuse); + ++numTBSSU; + if (!pta->isHeapMemObj(o) && !SVFUtil::isa(obj)) ++numSGTBSSU; + } + else if (isBase(tildet, tp)) + { + // Upcast. + prop = o; + ++numTBWU; + if (!pta->isHeapMemObj(o) && !SVFUtil::isa(obj)) ++numSGTBWU; + } + else if (tildet != tp && reuse) + { + // Reuse. + prop = cloneObject(o, tildet, true); + ++numReuse; + if (!pta->isHeapMemObj(o) && !SVFUtil::isa(obj)) ++numSGReuse; + } + else + { + // Some spurious objects will be filtered. + filter = true; + prop = o; + ++numTBSU; + if (!pta->isHeapMemObj(o) && !SVFUtil::isa(obj)) ++numSGTBSU; + } + + if (prop != o) + { + // If we cloned, we want to keep o in p's PTS but filter it (ignore it later). + pNewPt.set(o); + filterSet.set(o); + // TODO: hack, sound but imprecise and unclean. + // In the aggs case there is a difference between it being good for + // arrays and structs. For now, just propagate both the clone and the original + // object till a cleaner solution is found. + if (gep && aggs.find(tildet) != aggs.end()) + { + filterSet.reset(o); + } + } + + pNewPt.set(prop); + + if (filter) + { + filterSet.set(o); + } + } + + if (pPt != pNewPt) + { + // Seems fast enough to perform in the naive way. + pta->clearFullPts(p); + pta->unionPts(p, pNewPt); + changed = true; + } + + return changed; +} + +NodeID TypeBasedHeapCloning::cloneObject(NodeID o, const DIType *type, bool) +{ + NodeID clone; + const PAGNode *obj = ppag->getPAGNode(o); + if (const GepObjPN *gepObj = SVFUtil::dyn_cast(obj)) + { + const NodeBS &clones = getGepObjClones(gepObj->getBaseNode(), gepObj->getLocationSet().getOffset()); + // TODO: a bit of repetition. + for (NodeID clone : clones) + { + if (getType(clone) == type) + { + return clone; + } + } + + clone = addCloneGepObjNode(gepObj->getMemObj(), gepObj->getLocationSet()); + + // The base needs to know about the new clone. + addGepToObj(clone, gepObj->getBaseNode(), gepObj->getLocationSet().getOffset()); + + addClone(o, clone); + addClone(getOriginalObj(o), clone); + // The only instance of original object of a Gep object being retrieved is for + // IN sets and gepToSVFGRetriever in FSTBHC, so we don't care that clone comes + // from o (we can get that by checking the base and offset). + setOriginalObj(clone, getOriginalObj(o)); + CloneGepObjPN *cloneGepObj = SVFUtil::dyn_cast(ppag->getPAGNode(clone)); + cloneGepObj->setBaseNode(gepObj->getBaseNode()); + } + else if (SVFUtil::isa(obj) || SVFUtil::isa(obj)) + { + o = getOriginalObj(o); + // Check there isn't an appropriate clone already. + const NodeBS &clones = getClones(o); + for (NodeID clone : clones) + { + if (getType(clone) == type) + { + return clone; + } + } + + if (const FIObjPN *fiObj = SVFUtil::dyn_cast(obj)) + { + clone = addCloneFIObjNode(fiObj->getMemObj()); + } + else + { + const DummyObjPN *dummyObj = SVFUtil::dyn_cast(obj); + clone = addCloneDummyObjNode(dummyObj->getMemObj()); + } + // We checked above that it's an FIObj or a DummyObj. + + // Tracking object<->clone mappings. + addClone(o, clone); + setOriginalObj(clone, o); + } + else + { + assert(false && "FSTBHC: trying to clone unhandled object"); + } + + // Clone's metadata. This can be shared between Geps/otherwise. + setType(clone, type); + setAllocationSite(clone, getAllocationSite(o)); + + backPropagate(clone); + + return clone; +} + +const MDNode *TypeBasedHeapCloning::getRawCTirMetadata(const Value *v) +{ + assert(v != nullptr && "TBHC: trying to get metadata from nullptr!"); + + const MDNode *mdNode = nullptr; + if (const Instruction *inst = SVFUtil::dyn_cast(v)) + { + mdNode = inst->getMetadata(cppUtil::ctir::derefMDName); + } + else if (const GlobalObject *go = SVFUtil::dyn_cast(v)) + { + mdNode = go->getMetadata(cppUtil::ctir::derefMDName); + } + + // Will be nullptr if metadata isn't there. + return mdNode; +} + +NodeID TypeBasedHeapCloning::addCloneDummyObjNode(const MemObj *mem) +{ + NodeID id = NodeIDAllocator::get()->allocateObjectId(); + return ppag->addObjNode(nullptr, new CloneDummyObjPN(id, mem), id); +} + +NodeID TypeBasedHeapCloning::addCloneGepObjNode(const MemObj *mem, const LocationSet &l) +{ + NodeID id = NodeIDAllocator::get()->allocateObjectId(); + return ppag->addObjNode(mem->getRefVal(), new CloneGepObjPN(mem, id, l), id); +} + +NodeID TypeBasedHeapCloning::addCloneFIObjNode(const MemObj *mem) +{ + NodeID id = NodeIDAllocator::get()->allocateObjectId(); + return ppag->addObjNode(mem->getRefVal(), new CloneFIObjPN(mem->getRefVal(), id, mem), id); +} + +const DIType *TypeBasedHeapCloning::getTypeFromCTirMetadata(const Value *v) +{ + assert(v != nullptr && "TBHC: trying to get type from nullptr!"); + + const MDNode *mdNode = getRawCTirMetadata(v); + if (mdNode == nullptr) + { + return nullptr; + } + + const DIType *type = SVFUtil::dyn_cast(mdNode); + if (type == nullptr) + { + SVFUtil::errs() << "TBHC: bad ctir metadata type\n"; + return nullptr; + } + + return dchg->getCanonicalType(type); +} + +bool TypeBasedHeapCloning::isGep(const PAGNode *n) const +{ + assert(n != nullptr && "TBHC: testing if null is a GEP object!"); + return SVFUtil::isa(n); +} + +/// Returns true if the function name matches MAYALIAS, NOALIAS, etc. +static bool isAliasTestFunction(std::string name) +{ + return name == PointerAnalysis::aliasTestMayAlias + || name == PointerAnalysis::aliasTestMayAliasMangled + || name == PointerAnalysis::aliasTestNoAlias + || name == PointerAnalysis::aliasTestNoAliasMangled + || name == PointerAnalysis::aliasTestPartialAlias + || name == PointerAnalysis::aliasTestPartialAliasMangled + || name == PointerAnalysis::aliasTestMustAlias + || name == PointerAnalysis::aliasTestMustAliasMangled + || name == PointerAnalysis::aliasTestFailMayAlias + || name == PointerAnalysis::aliasTestFailMayAliasMangled + || name == PointerAnalysis::aliasTestFailNoAlias + || name == PointerAnalysis::aliasTestFailNoAliasMangled; +} + +void TypeBasedHeapCloning::validateTBHCTests(SVFModule*) +{ + const LLVMModuleSet *llvmModuleSet = LLVMModuleSet::getLLVMModuleSet(); + for (u32_t i = 0; i < llvmModuleSet->getModuleNum(); ++i) + { + const PAG::CallSiteSet &callSites = ppag->getCallSiteSet(); + for (const CallBlockNode *cbn : callSites) + { + const CallSite &cs = SVFUtil::getLLVMCallSite(cbn->getCallSite()); + const Function *fn = cs.getCalledFunction(); + if (fn == nullptr || !isAliasTestFunction(fn->getName().str())) + { + continue; + } + + // We have a test call, + // We want the store which stores to the pointer in question (i.e. operand of the + // store is the pointer, and the store itself is the dereference). + const StoreInst *ps = nullptr, *qs = nullptr; + // Check: currInst is a deref call, so p/q is prevInst. + + // Find p. + const Instruction *prevInst = nullptr; + const Instruction *currInst = cs.getInstruction(); + do + { + if (const CallInst *ci = SVFUtil::dyn_cast(currInst)) + { + std::string calledFnName = ci->getCalledFunction()->getName().str(); + if (calledFnName == derefFnName || calledFnName == mangledDerefFnName) + { + const StoreInst *si = SVFUtil::dyn_cast(prevInst); + assert(si && "TBHC: validation macro not producing stores?"); + ps = si; + break; + } + } + + prevInst = currInst; + currInst = currInst->getNextNonDebugInstruction(); + } + while (currInst != nullptr); + + // Repeat for q. + while (currInst != nullptr) + { + // while loop, not do-while, because we need to the next instruction (current + // instruction is the first deref()). + prevInst = currInst; + currInst = currInst->getNextNonDebugInstruction(); + + if (const CallInst *ci = SVFUtil::dyn_cast(currInst)) + { + std::string calledFnName = ci->getCalledFunction()->getName().str(); + if (calledFnName == derefFnName || calledFnName == mangledDerefFnName) + { + const StoreInst *si = SVFUtil::dyn_cast(prevInst); + assert(si && "TBHC: validation macro not producing stores?"); + qs = si; + break; + } + } + } + + assert(ps != nullptr && qs != nullptr && "TBHC: malformed alias test?"); + NodeID p = ppag->getValueNode(ps->getPointerOperand()), + q = ppag->getValueNode(qs->getPointerOperand()); + const DIType *pt = getTypeFromCTirMetadata(ps), *qt = getTypeFromCTirMetadata(qs); + + // Now filter both points-to sets according to the type of the value. + const PointsTo &pPts = pta->getPts(p), &qPts = pta->getPts(q); + PointsTo pPtsFiltered, qPtsFiltered; + for (NodeID o : pPts) + { + if (getType(o) != undefType && isBase(pt, getType(o))) + { + pPtsFiltered.set(o); + } + } + + for (NodeID o : qPts) + { + if (getType(o) != undefType && isBase(qt, getType(o))) + { + qPtsFiltered.set(o); + } + } + + BVDataPTAImpl *bvpta = SVFUtil::dyn_cast(pta); + assert(bvpta && "TBHC: need a BVDataPTAImpl for TBHC alias testing."); + AliasResult res = bvpta->alias(pPtsFiltered, qPtsFiltered); + + bool passed = false; + std::string testName = ""; + if (fn->getName() == PointerAnalysis::aliasTestMayAlias + || fn->getName() == PointerAnalysis::aliasTestMayAliasMangled) + { + passed = res == llvm::MayAlias || res == llvm::MustAlias; + testName = PointerAnalysis::aliasTestMayAlias; + } + else if (fn->getName() == PointerAnalysis::aliasTestNoAlias + || fn->getName() == PointerAnalysis::aliasTestNoAliasMangled) + { + passed = res == llvm::NoAlias; + testName = PointerAnalysis::aliasTestNoAlias; + } + else if (fn->getName() == PointerAnalysis::aliasTestMustAlias + || fn->getName() == PointerAnalysis::aliasTestMustAliasMangled) + { + passed = res == llvm::MustAlias || res == llvm::MayAlias; + testName = PointerAnalysis::aliasTestMustAlias; + } + else if (fn->getName() == PointerAnalysis::aliasTestPartialAlias + || fn->getName() == PointerAnalysis::aliasTestPartialAliasMangled) + { + passed = res == llvm::MayAlias || res == llvm::PartialAlias; + testName = PointerAnalysis::aliasTestPartialAlias; + } + else if (fn->getName() == PointerAnalysis::aliasTestFailMayAlias + || fn->getName() == PointerAnalysis::aliasTestFailMayAliasMangled) + { + passed = res != llvm::MayAlias && res != llvm::MustAlias && res != llvm::PartialAlias; + testName = PointerAnalysis::aliasTestFailMayAlias; + } + else if (fn->getName() == PointerAnalysis::aliasTestFailNoAlias + || fn->getName() == PointerAnalysis::aliasTestFailNoAliasMangled) + { + passed = res != llvm::NoAlias; + testName = PointerAnalysis::aliasTestFailNoAlias; + } + + SVFUtil::outs() << "[" << pta->PTAName() << "] Checking " << testName << "\n"; + raw_ostream &msgStream = passed ? SVFUtil::outs() : SVFUtil::errs(); + msgStream << (passed ? SVFUtil::sucMsg("\t SUCCESS") : SVFUtil::errMsg("\t FAILURE")) + << " : " << testName + << " check " + << "at (" << SVFUtil::getSourceLoc(cs.getInstruction()) << ")\n"; + if (!passed) + assert(false && "test case failed!"); + + if (pPtsFiltered.empty()) + { + msgStream << SVFUtil::wrnMsg("\t WARNING") + << " : pts(" << p << ") is empty\n"; + } + + if (qPtsFiltered.empty()) + { + msgStream << SVFUtil::wrnMsg("\t WARNING") + << " : pts(" << q << ") is empty\n"; + } + + if (testName == PointerAnalysis::aliasTestMustAlias) + { + msgStream << SVFUtil::wrnMsg("\t WARNING") + << " : MUSTALIAS tests are actually MAYALIAS tests\n"; + } + + if (testName == PointerAnalysis::aliasTestPartialAlias) + { + msgStream << SVFUtil::wrnMsg("\t WARNING") + << " : PARTIALALIAS tests are actually MAYALIAS tests\n"; + } + + msgStream.flush(); + } + } +} + +void TypeBasedHeapCloning::dumpStats(void) +{ + std::string indent = ""; + SVFUtil::outs() << "@@@@@@@@@ TBHC STATISTICS @@@@@@@@@\n"; + indent = " "; + + // Print clones with their types. + SVFUtil::outs() << indent << "=== Original objects to clones ===\n"; + indent = " "; + unsigned totalClones = 0; + const NodeBS objs = getObjsWithClones(); + for (NodeID o : objs) + { + const NodeBS &clones = getClones(o); + if (clones.count() == 0) continue; + + totalClones += clones.count(); + SVFUtil::outs() << indent + << " " << o << " : " + << "(" << clones.count() << ")" + << "[ "; + bool first = true; + for (NodeID c : clones) + { + if (!first) + { + SVFUtil::outs() << ", "; + } + + SVFUtil::outs() << c + << "{" + << dchg->diTypeToStr(getType(c)) + << "}"; + first = false; + } + + SVFUtil::outs() << " ]\n"; + } + + indent = " "; + SVFUtil::outs() << indent + << "Total: " << ppag->getObjectNodeNum() + ppag->getFieldObjNodeNum() + totalClones + << " (" << totalClones << " clones)\n"; + SVFUtil::outs() << indent << "==================================\n"; + + SVFUtil::outs() << indent << "INITIALISE : " << numInit << "\n"; + SVFUtil::outs() << indent << "TBWU : " << numTBWU << "\n"; + SVFUtil::outs() << indent << "TBSSU : " << numTBSSU << "\n"; + SVFUtil::outs() << indent << "TBSU : " << numTBSU << "\n"; + SVFUtil::outs() << indent << "REUSE : " << numReuse << "\n"; + SVFUtil::outs() << indent << "AGG CASE : " << numAgg << "\n"; + + SVFUtil::outs() << "\n"; + SVFUtil::outs() << indent << "STACK/GLOBAL OBJECTS\n"; + indent = " "; + SVFUtil::outs() << indent << "INITIALISE : " << numSGInit << "\n"; + SVFUtil::outs() << indent << "TBWU : " << numSGTBWU << "\n"; + SVFUtil::outs() << indent << "TBSSU : " << numSGTBSSU << "\n"; + SVFUtil::outs() << indent << "TBSU : " << numSGTBSU << "\n"; + SVFUtil::outs() << indent << "REUSE : " << numSGReuse << "\n"; + SVFUtil::outs() << indent << "AGG CASE : " << numSGAgg << "\n"; + + SVFUtil::outs() << "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@\n"; +} diff --git a/svf/lib/Util/Z3Expr.cpp b/svf/lib/Util/Z3Expr.cpp deleted file mode 100644 index b27242f3c..000000000 --- a/svf/lib/Util/Z3Expr.cpp +++ /dev/null @@ -1,175 +0,0 @@ -//===- Z3Expr.h -- Z3 conditions----------------------------// -// -// SVF: Static Value-Flow Analysis -// -// Copyright (C) <2013-2017> -// - -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU Affero General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Affero General Public License for more details. - -// You should have received a copy of the GNU Affero General Public License -// along with this program. If not, see . -// -//===----------------------------------------------------------------------===// -/* - * Z3Expr.h - * - * Created on: April 29, 2022 - * Author: Xiao - */ - -#include "Util/Z3Expr.h" -#include "Util/Options.h" - -namespace SVF -{ - -z3::context *Z3Expr::ctx = nullptr; -z3::solver* Z3Expr::solver = nullptr; - - -/// release z3 context -void Z3Expr::releaseContext() -{ - if(solver) - releaseSolver(); - delete ctx; - ctx = nullptr; -} - -/// release z3 solver -void Z3Expr::releaseSolver() -{ - delete solver; - solver = nullptr; -} - -/// Get z3 solver, singleton design here to make sure we only have one context -z3::solver &Z3Expr::getSolver() -{ - if (solver == nullptr) - { - solver = new z3::solver(getContext()); - } - return *solver; -} - -/// Get z3 context, singleton design here to make sure we only have one context -z3::context &Z3Expr::getContext() -{ - if (ctx == nullptr) - { - ctx = new z3::context(); - } - return *ctx; -} - -/// get the number of subexpression of a Z3 expression -u32_t Z3Expr::getExprSize(const Z3Expr &z3Expr) -{ - u32_t res = 1; - if (z3Expr.getExpr().num_args() == 0) - { - return 1; - } - for (u32_t i = 0; i < z3Expr.getExpr().num_args(); ++i) - { - Z3Expr expr = z3Expr.getExpr().arg(i); - res += getExprSize(expr); - } - return res; -} - -/// compute AND, used for branch condition -Z3Expr Z3Expr::AND(const Z3Expr &lhs, const Z3Expr &rhs) -{ - if (eq(lhs, Z3Expr::getFalseCond()) || eq(rhs, Z3Expr::getFalseCond())) - { - return Z3Expr::getFalseCond(); - } - else if (eq(lhs, Z3Expr::getTrueCond())) - { - return rhs; - } - else if (eq(rhs, Z3Expr::getTrueCond())) - { - return lhs; - } - else - { - Z3Expr expr = (lhs && rhs).simplify(); - // check subexpression size and option limit - if (Z3Expr::getExprSize(expr) > Options::MaxZ3Size()) - { - getSolver().push(); - getSolver().add(expr.getExpr()); - z3::check_result res = getSolver().check(); - getSolver().pop(); - if (res != z3::unsat) - { - return lhs; - } - else - { - return Z3Expr::getFalseCond(); - } - } - return expr; - } -} - -/// compute OR, used for branch condition -Z3Expr Z3Expr::OR(const Z3Expr &lhs, const Z3Expr &rhs) -{ - if (eq(lhs, Z3Expr::getTrueCond()) || eq(rhs, Z3Expr::getTrueCond())) - { - return Z3Expr::getTrueCond(); - } - else if (eq(lhs, Z3Expr::getFalseCond())) - { - return rhs; - } - else if (eq(rhs, Z3Expr::getFalseCond())) - { - return lhs; - } - else - { - Z3Expr expr = (lhs || rhs).simplify(); - // check subexpression size and option limit - if (Z3Expr::getExprSize(expr) > Options::MaxZ3Size()) - { - getSolver().push(); - getSolver().add(expr.getExpr()); - z3::check_result res = getSolver().check(); - getSolver().pop(); - if (res != z3::unsat) - { - return Z3Expr::getTrueCond(); - } - else - { - return Z3Expr::getFalseCond(); - } - } - return expr; - } -} - -/// output Z3 expression as a string -std::string Z3Expr::dumpStr(const Z3Expr &z3Expr) -{ - std::ostringstream out; - out << z3Expr.getExpr(); - return out.str(); -} -} - diff --git a/svf/lib/Util/cJSON.cpp b/svf/lib/Util/cJSON.cpp deleted file mode 100644 index 0cdf8c5d4..000000000 --- a/svf/lib/Util/cJSON.cpp +++ /dev/null @@ -1,3142 +0,0 @@ -/* - Copyright (c) 2009-2017 Dave Gamble and cJSON contributors - - Permission is hereby granted, free of charge, to any person obtaining a copy - of this software and associated documentation files (the "Software"), to deal - in the Software without restriction, including without limitation the rights - to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - copies of the Software, and to permit persons to whom the Software is - furnished to do so, subject to the following conditions: - - The above copyright notice and this permission notice shall be included in - all copies or substantial portions of the Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - THE SOFTWARE. -*/ - -/* cJSON */ -/* JSON parser in C. */ - -/* disable warnings about old C89 functions in MSVC */ -#if !defined(_CRT_SECURE_NO_DEPRECATE) && defined(_MSC_VER) -#define _CRT_SECURE_NO_DEPRECATE -#endif - -#ifdef __GNUC__ -#pragma GCC visibility push(default) -#endif -#if defined(_MSC_VER) -#pragma warning (push) -/* disable warning about single line comments in system headers */ -#pragma warning (disable : 4001) -#endif - -#include -#include -#include -#include -#include -#include -#include - -#ifdef ENABLE_LOCALES -#include -#endif - -#if defined(_MSC_VER) -#pragma warning (pop) -#endif -#ifdef __GNUC__ -#pragma GCC visibility pop -#endif - -#include "Util/cJSON.h" - -/* define our own boolean type */ -#ifdef true -#undef true -#endif -#define true ((cJSON_bool)1) - -#ifdef false -#undef false -#endif -#define false ((cJSON_bool)0) - -/* define isnan and isinf for ANSI C, if in C99 or above, isnan and isinf has been defined in math.h */ -#ifndef isinf -#define isinf(d) (isnan((d - d)) && !isnan(d)) -#endif -#ifndef isnan -#define isnan(d) (d != d) -#endif - -#ifndef NAN -#ifdef _WIN32 -#define NAN sqrt(-1.0) -#else -#define NAN 0.0/0.0 -#endif -#endif - -typedef struct -{ - const unsigned char *json; - size_t position; -} error; -static error global_error = { NULL, 0 }; - -CJSON_PUBLIC(const char *) cJSON_GetErrorPtr(void) -{ - return (const char*) (global_error.json + global_error.position); -} - -CJSON_PUBLIC(char *) cJSON_GetStringValue(const cJSON * const item) -{ - if (!cJSON_IsString(item)) - { - return NULL; - } - - return item->valuestring; -} - -CJSON_PUBLIC(double) cJSON_GetNumberValue(const cJSON * const item) -{ - if (!cJSON_IsNumber(item)) - { - return (double) NAN; - } - - return item->valuedouble; -} - -/* This is a safeguard to prevent copy-pasters from using incompatible C and header files */ -#if (CJSON_VERSION_MAJOR != 1) || (CJSON_VERSION_MINOR != 7) || (CJSON_VERSION_PATCH != 15) -#error cJSON.h and cJSON.c have different versions. Make sure that both have the same. -#endif - -CJSON_PUBLIC(const char*) cJSON_Version(void) -{ - static char version[15]; - snprintf(version, sizeof(version), "%i.%i.%i", CJSON_VERSION_MAJOR, CJSON_VERSION_MINOR, CJSON_VERSION_PATCH); - - return version; -} - -/* Case insensitive string comparison, doesn't consider two NULL pointers equal though */ -static int case_insensitive_strcmp(const unsigned char *string1, const unsigned char *string2) -{ - if ((string1 == NULL) || (string2 == NULL)) - { - return 1; - } - - if (string1 == string2) - { - return 0; - } - - for(; tolower(*string1) == tolower(*string2); (void)string1++, string2++) - { - if (*string1 == '\0') - { - return 0; - } - } - - return tolower(*string1) - tolower(*string2); -} - -typedef struct internal_hooks -{ - void *(CJSON_CDECL *allocate)(size_t size); - void (CJSON_CDECL *deallocate)(void *pointer); - void *(CJSON_CDECL *reallocate)(void *pointer, size_t size); -} internal_hooks; - -#if defined(_MSC_VER) -/* work around MSVC error C2322: '...' address of dllimport '...' is not static */ -static void * CJSON_CDECL internal_malloc(size_t size) -{ - return malloc(size); -} -static void CJSON_CDECL internal_free(void *pointer) -{ - free(pointer); -} -static void * CJSON_CDECL internal_realloc(void *pointer, size_t size) -{ - return realloc(pointer, size); -} -#else -#define internal_malloc malloc -#define internal_free free -#define internal_realloc realloc -#endif - -/* strlen of character literals resolved at compile time */ -#define static_strlen(string_literal) (sizeof(string_literal) - sizeof("")) - -static internal_hooks global_hooks = { internal_malloc, internal_free, internal_realloc }; - -static unsigned char* cJSON_strdup(const unsigned char* string, const internal_hooks * const hooks) -{ - size_t length = 0; - unsigned char *copy = NULL; - - if (string == NULL) - { - return NULL; - } - - length = strlen((const char*)string) + sizeof(""); - copy = (unsigned char*)hooks->allocate(length); - if (copy == NULL) - { - return NULL; - } - memcpy(copy, string, length); - - return copy; -} - -CJSON_PUBLIC(void) cJSON_InitHooks(cJSON_Hooks* hooks) -{ - if (hooks == NULL) - { - /* Reset hooks */ - global_hooks.allocate = malloc; - global_hooks.deallocate = free; - global_hooks.reallocate = realloc; - return; - } - - global_hooks.allocate = malloc; - if (hooks->malloc_fn != NULL) - { - global_hooks.allocate = hooks->malloc_fn; - } - - global_hooks.deallocate = free; - if (hooks->free_fn != NULL) - { - global_hooks.deallocate = hooks->free_fn; - } - - /* use realloc only if both free and malloc are used */ - global_hooks.reallocate = NULL; - if ((global_hooks.allocate == malloc) && (global_hooks.deallocate == free)) - { - global_hooks.reallocate = realloc; - } -} - -/* Internal constructor. */ -static cJSON *cJSON_New_Item(const internal_hooks * const hooks) -{ - cJSON* node = (cJSON*)hooks->allocate(sizeof(cJSON)); - if (node) - { - memset(node, '\0', sizeof(cJSON)); - } - - return node; -} - -/* Delete a cJSON structure. */ -CJSON_PUBLIC(void) cJSON_Delete(cJSON *item) -{ - cJSON *next = NULL; - while (item != NULL) - { - next = item->next; - if (!(item->type & cJSON_IsReference) && (item->child != NULL)) - { - cJSON_Delete(item->child); - } - if (!(item->type & cJSON_IsReference) && (item->valuestring != NULL)) - { - global_hooks.deallocate(item->valuestring); - } - if (!(item->type & cJSON_StringIsConst) && (item->string != NULL)) - { - global_hooks.deallocate(item->string); - } - global_hooks.deallocate(item); - item = next; - } -} - -/* get the decimal point character of the current locale */ -static unsigned char get_decimal_point(void) -{ -#ifdef ENABLE_LOCALES - struct lconv *lconv = localeconv(); - return (unsigned char) lconv->decimal_point[0]; -#else - return '.'; -#endif -} - -typedef struct -{ - const unsigned char *content; - size_t length; - size_t offset; - size_t depth; /* How deeply nested (in arrays/objects) is the input at the current offset. */ - internal_hooks hooks; -} parse_buffer; - -/* check if the given size is left to read in a given parse buffer (starting with 1) */ -#define can_read(buffer, size) ((buffer != NULL) && (((buffer)->offset + size) <= (buffer)->length)) -/* check if the buffer can be accessed at the given index (starting with 0) */ -#define can_access_at_index(buffer, index) ((buffer != NULL) && (((buffer)->offset + index) < (buffer)->length)) -#define cannot_access_at_index(buffer, index) (!can_access_at_index(buffer, index)) -/* get a pointer to the buffer at the position */ -#define buffer_at_offset(buffer) ((buffer)->content + (buffer)->offset) - -/* Parse the input text to generate a number, and populate the result into item. */ -static cJSON_bool parse_number(cJSON * const item, parse_buffer * const input_buffer) -{ - double number = 0; - unsigned char *after_end = NULL; - unsigned char number_c_string[64]; - unsigned char decimal_point = get_decimal_point(); - size_t i = 0; - - if ((input_buffer == NULL) || (input_buffer->content == NULL)) - { - return false; - } - - /* copy the number into a temporary buffer and replace '.' with the decimal point - * of the current locale (for strtod) - * This also takes care of '\0' not necessarily being available for marking the end of the input */ - for (i = 0; (i < (sizeof(number_c_string) - 1)) && can_access_at_index(input_buffer, i); i++) - { - switch (buffer_at_offset(input_buffer)[i]) - { - case '0': - case '1': - case '2': - case '3': - case '4': - case '5': - case '6': - case '7': - case '8': - case '9': - case '+': - case '-': - case 'e': - case 'E': - number_c_string[i] = buffer_at_offset(input_buffer)[i]; - break; - - case '.': - number_c_string[i] = decimal_point; - break; - - default: - goto loop_end; - } - } -loop_end: - number_c_string[i] = '\0'; - - number = strtod((const char*)number_c_string, (char**)&after_end); - if (number_c_string == after_end) - { - return false; /* parse_error */ - } - - item->valuedouble = number; - - /* use saturation in case of overflow */ - if (number >= INT_MAX) - { - item->valueint = INT_MAX; - } - else if (number <= (double)INT_MIN) - { - item->valueint = INT_MIN; - } - else - { - item->valueint = (int)number; - } - - item->type = cJSON_Number; - - input_buffer->offset += (size_t)(after_end - number_c_string); - return true; -} - -/* don't ask me, but the original cJSON_SetNumberValue returns an integer or double */ -CJSON_PUBLIC(double) cJSON_SetNumberHelper(cJSON *object, double number) -{ - if (number >= INT_MAX) - { - object->valueint = INT_MAX; - } - else if (number <= (double)INT_MIN) - { - object->valueint = INT_MIN; - } - else - { - object->valueint = (int)number; - } - - return object->valuedouble = number; -} - -CJSON_PUBLIC(char*) cJSON_SetValuestring(cJSON *object, const char *valuestring) -{ - char *copy = NULL; - /* if object's type is not cJSON_String or is cJSON_IsReference, it should not set valuestring */ - if (!(object->type & cJSON_String) || (object->type & cJSON_IsReference)) - { - return NULL; - } - if (strlen(valuestring) <= strlen(object->valuestring)) - { - strcpy(object->valuestring, valuestring); - return object->valuestring; - } - copy = (char*) cJSON_strdup((const unsigned char*)valuestring, &global_hooks); - if (copy == NULL) - { - return NULL; - } - if (object->valuestring != NULL) - { - cJSON_free(object->valuestring); - } - object->valuestring = copy; - - return copy; -} - -typedef struct -{ - unsigned char *buffer; - size_t length; - size_t offset; - size_t depth; /* current nesting depth (for formatted printing) */ - cJSON_bool noalloc; - cJSON_bool format; /* is this print a formatted print */ - internal_hooks hooks; -} printbuffer; - -/* realloc printbuffer if necessary to have at least "needed" bytes more */ -static unsigned char* ensure(printbuffer * const p, size_t needed) -{ - unsigned char *newbuffer = NULL; - size_t newsize = 0; - - if ((p == NULL) || (p->buffer == NULL)) - { - return NULL; - } - - if ((p->length > 0) && (p->offset >= p->length)) - { - /* make sure that offset is valid */ - return NULL; - } - - if (needed > INT_MAX) - { - /* sizes bigger than INT_MAX are currently not supported */ - return NULL; - } - - needed += p->offset + 1; - if (needed <= p->length) - { - return p->buffer + p->offset; - } - - if (p->noalloc) - { - return NULL; - } - - /* calculate new buffer size */ - if (needed > (INT_MAX / 2)) - { - /* overflow of int, use INT_MAX if possible */ - if (needed <= INT_MAX) - { - newsize = INT_MAX; - } - else - { - return NULL; - } - } - else - { - newsize = needed * 2; - } - - if (p->hooks.reallocate != NULL) - { - /* reallocate with realloc if available */ - newbuffer = (unsigned char*)p->hooks.reallocate(p->buffer, newsize); - if (newbuffer == NULL) - { - p->hooks.deallocate(p->buffer); - p->length = 0; - p->buffer = NULL; - - return NULL; - } - } - else - { - /* otherwise reallocate manually */ - newbuffer = (unsigned char*)p->hooks.allocate(newsize); - if (!newbuffer) - { - p->hooks.deallocate(p->buffer); - p->length = 0; - p->buffer = NULL; - - return NULL; - } - - memcpy(newbuffer, p->buffer, p->offset + 1); - p->hooks.deallocate(p->buffer); - } - p->length = newsize; - p->buffer = newbuffer; - - return newbuffer + p->offset; -} - -/* calculate the new length of the string in a printbuffer and update the offset */ -static void update_offset(printbuffer * const buffer) -{ - const unsigned char *buffer_pointer = NULL; - if ((buffer == NULL) || (buffer->buffer == NULL)) - { - return; - } - buffer_pointer = buffer->buffer + buffer->offset; - - buffer->offset += strlen((const char*)buffer_pointer); -} - -/* securely comparison of floating-point variables */ -static cJSON_bool compare_double(double a, double b) -{ - double maxVal = fabs(a) > fabs(b) ? fabs(a) : fabs(b); - return (fabs(a - b) <= maxVal * DBL_EPSILON); -} - -/* Render the number nicely from the given item into a string. */ -static cJSON_bool print_number(const cJSON * const item, printbuffer * const output_buffer) -{ - unsigned char *output_pointer = NULL; - double d = item->valuedouble; - int length = 0; - size_t i = 0; - unsigned char number_buffer[26] = {0}; /* temporary buffer to print the number into */ - unsigned char decimal_point = get_decimal_point(); - double test = 0.0; - - if (output_buffer == NULL) - { - return false; - } - - /* This checks for NaN and Infinity */ - if (isnan(d) || isinf(d)) - { - length = snprintf((char*)number_buffer, sizeof(number_buffer), "null"); - } - else if(d == (double)item->valueint) - { - length = snprintf((char*)number_buffer, sizeof(number_buffer), "%d", item->valueint); - } - else - { - /* Try 15 decimal places of precision to avoid nonsignificant nonzero digits */ - length = snprintf((char*)number_buffer, sizeof(number_buffer), "%1.15g", d); - - /* Check whether the original double can be recovered */ - if ((sscanf((char*)number_buffer, "%lg", &test) != 1) || !compare_double((double)test, d)) - { - /* If not, print with 17 decimal places of precision */ - length = snprintf((char*)number_buffer, sizeof(number_buffer), "%1.17g", d); - } - } - - /* sprintf failed or buffer overrun occurred */ - if ((length < 0) || (length > (int)(sizeof(number_buffer) - 1))) - { - return false; - } - - /* reserve appropriate space in the output */ - output_pointer = ensure(output_buffer, (size_t)length + sizeof("")); - if (output_pointer == NULL) - { - return false; - } - - /* copy the printed number to the output and replace locale - * dependent decimal point with '.' */ - for (i = 0; i < ((size_t)length); i++) - { - if (number_buffer[i] == decimal_point) - { - output_pointer[i] = '.'; - continue; - } - - output_pointer[i] = number_buffer[i]; - } - output_pointer[i] = '\0'; - - output_buffer->offset += (size_t)length; - - return true; -} - -/* parse 4 digit hexadecimal number */ -static unsigned parse_hex4(const unsigned char * const input) -{ - unsigned int h = 0; - size_t i = 0; - - for (i = 0; i < 4; i++) - { - /* parse digit */ - if ((input[i] >= '0') && (input[i] <= '9')) - { - h += (unsigned int) input[i] - '0'; - } - else if ((input[i] >= 'A') && (input[i] <= 'F')) - { - h += (unsigned int) 10 + input[i] - 'A'; - } - else if ((input[i] >= 'a') && (input[i] <= 'f')) - { - h += (unsigned int) 10 + input[i] - 'a'; - } - else /* invalid */ - { - return 0; - } - - if (i < 3) - { - /* shift left to make place for the next nibble */ - h = h << 4; - } - } - - return h; -} - -/* converts a UTF-16 literal to UTF-8 - * A literal can be one or two sequences of the form \uXXXX */ -static unsigned char utf16_literal_to_utf8(const unsigned char * const input_pointer, const unsigned char * const input_end, unsigned char **output_pointer) -{ - long unsigned int codepoint = 0; - unsigned int first_code = 0; - const unsigned char *first_sequence = input_pointer; - unsigned char utf8_length = 0; - unsigned char utf8_position = 0; - unsigned char sequence_length = 0; - unsigned char first_byte_mark = 0; - - if ((input_end - first_sequence) < 6) - { - /* input ends unexpectedly */ - goto fail; - } - - /* get the first utf16 sequence */ - first_code = parse_hex4(first_sequence + 2); - - /* check that the code is valid */ - if (((first_code >= 0xDC00) && (first_code <= 0xDFFF))) - { - goto fail; - } - - /* UTF16 surrogate pair */ - if ((first_code >= 0xD800) && (first_code <= 0xDBFF)) - { - const unsigned char *second_sequence = first_sequence + 6; - unsigned int second_code = 0; - sequence_length = 12; /* \uXXXX\uXXXX */ - - if ((input_end - second_sequence) < 6) - { - /* input ends unexpectedly */ - goto fail; - } - - if ((second_sequence[0] != '\\') || (second_sequence[1] != 'u')) - { - /* missing second half of the surrogate pair */ - goto fail; - } - - /* get the second utf16 sequence */ - second_code = parse_hex4(second_sequence + 2); - /* check that the code is valid */ - if ((second_code < 0xDC00) || (second_code > 0xDFFF)) - { - /* invalid second half of the surrogate pair */ - goto fail; - } - - - /* calculate the unicode codepoint from the surrogate pair */ - codepoint = 0x10000 + (((first_code & 0x3FF) << 10) | (second_code & 0x3FF)); - } - else - { - sequence_length = 6; /* \uXXXX */ - codepoint = first_code; - } - - /* encode as UTF-8 - * takes at maximum 4 bytes to encode: - * 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx */ - if (codepoint < 0x80) - { - /* normal ascii, encoding 0xxxxxxx */ - utf8_length = 1; - } - else if (codepoint < 0x800) - { - /* two bytes, encoding 110xxxxx 10xxxxxx */ - utf8_length = 2; - first_byte_mark = 0xC0; /* 11000000 */ - } - else if (codepoint < 0x10000) - { - /* three bytes, encoding 1110xxxx 10xxxxxx 10xxxxxx */ - utf8_length = 3; - first_byte_mark = 0xE0; /* 11100000 */ - } - else if (codepoint <= 0x10FFFF) - { - /* four bytes, encoding 1110xxxx 10xxxxxx 10xxxxxx 10xxxxxx */ - utf8_length = 4; - first_byte_mark = 0xF0; /* 11110000 */ - } - else - { - /* invalid unicode codepoint */ - goto fail; - } - - /* encode as utf8 */ - for (utf8_position = (unsigned char)(utf8_length - 1); utf8_position > 0; utf8_position--) - { - /* 10xxxxxx */ - (*output_pointer)[utf8_position] = (unsigned char)((codepoint | 0x80) & 0xBF); - codepoint >>= 6; - } - /* encode first byte */ - if (utf8_length > 1) - { - (*output_pointer)[0] = (unsigned char)((codepoint | first_byte_mark) & 0xFF); - } - else - { - (*output_pointer)[0] = (unsigned char)(codepoint & 0x7F); - } - - *output_pointer += utf8_length; - - return sequence_length; - -fail: - return 0; -} - -/* Parse the input text into an unescaped cinput, and populate item. */ -static cJSON_bool parse_string(cJSON * const item, parse_buffer * const input_buffer) -{ - const unsigned char *input_pointer = buffer_at_offset(input_buffer) + 1; - const unsigned char *input_end = buffer_at_offset(input_buffer) + 1; - unsigned char *output_pointer = NULL; - unsigned char *output = NULL; - - /* not a string */ - if (buffer_at_offset(input_buffer)[0] != '\"') - { - goto fail; - } - - { - /* calculate approximate size of the output (overestimate) */ - size_t allocation_length = 0; - size_t skipped_bytes = 0; - while (((size_t)(input_end - input_buffer->content) < input_buffer->length) && (*input_end != '\"')) - { - /* is escape sequence */ - if (input_end[0] == '\\') - { - if ((size_t)(input_end + 1 - input_buffer->content) >= input_buffer->length) - { - /* prevent buffer overflow when last input character is a backslash */ - goto fail; - } - skipped_bytes++; - input_end++; - } - input_end++; - } - if (((size_t)(input_end - input_buffer->content) >= input_buffer->length) || (*input_end != '\"')) - { - goto fail; /* string ended unexpectedly */ - } - - /* This is at most how much we need for the output */ - allocation_length = (size_t) (input_end - buffer_at_offset(input_buffer)) - skipped_bytes; - output = (unsigned char*)input_buffer->hooks.allocate(allocation_length + sizeof("")); - if (output == NULL) - { - goto fail; /* allocation failure */ - } - } - - output_pointer = output; - /* loop through the string literal */ - while (input_pointer < input_end) - { - if (*input_pointer != '\\') - { - *output_pointer++ = *input_pointer++; - } - /* escape sequence */ - else - { - unsigned char sequence_length = 2; - if ((input_end - input_pointer) < 1) - { - goto fail; - } - - switch (input_pointer[1]) - { - case 'b': - *output_pointer++ = '\b'; - break; - case 'f': - *output_pointer++ = '\f'; - break; - case 'n': - *output_pointer++ = '\n'; - break; - case 'r': - *output_pointer++ = '\r'; - break; - case 't': - *output_pointer++ = '\t'; - break; - case '\"': - case '\\': - case '/': - *output_pointer++ = input_pointer[1]; - break; - - /* UTF-16 literal */ - case 'u': - sequence_length = utf16_literal_to_utf8(input_pointer, input_end, &output_pointer); - if (sequence_length == 0) - { - /* failed to convert UTF16-literal to UTF-8 */ - goto fail; - } - break; - - default: - goto fail; - } - input_pointer += sequence_length; - } - } - - /* zero terminate the output */ - *output_pointer = '\0'; - - item->type = cJSON_String; - item->valuestring = (char*)output; - - input_buffer->offset = (size_t) (input_end - input_buffer->content); - input_buffer->offset++; - - return true; - -fail: - if (output != NULL) - { - input_buffer->hooks.deallocate(output); - } - - if (input_pointer != NULL) - { - input_buffer->offset = (size_t)(input_pointer - input_buffer->content); - } - - return false; -} - -/* Render the cstring provided to an escaped version that can be printed. */ -static cJSON_bool print_string_ptr(const unsigned char * const input, printbuffer * const output_buffer) -{ - const unsigned char *input_pointer = NULL; - unsigned char *output = NULL; - unsigned char *output_pointer = NULL; - size_t output_length = 0; - /* numbers of additional characters needed for escaping */ - size_t escape_characters = 0; - - if (output_buffer == NULL) - { - return false; - } - - /* empty string */ - if (input == NULL) - { - output = ensure(output_buffer, sizeof("\"\"")); - if (output == NULL) - { - return false; - } - strcpy((char*)output, "\"\""); - - return true; - } - - /* set "flag" to 1 if something needs to be escaped */ - for (input_pointer = input; *input_pointer; input_pointer++) - { - switch (*input_pointer) - { - case '\"': - case '\\': - case '\b': - case '\f': - case '\n': - case '\r': - case '\t': - /* one character escape sequence */ - escape_characters++; - break; - default: - if (*input_pointer < 32) - { - /* UTF-16 escape sequence uXXXX */ - escape_characters += 5; - } - break; - } - } - output_length = (size_t)(input_pointer - input) + escape_characters; - - output = ensure(output_buffer, output_length + sizeof("\"\"")); - if (output == NULL) - { - return false; - } - - /* no characters have to be escaped */ - if (escape_characters == 0) - { - output[0] = '\"'; - memcpy(output + 1, input, output_length); - output[output_length + 1] = '\"'; - output[output_length + 2] = '\0'; - - return true; - } - - output[0] = '\"'; - output_pointer = output + 1; - /* copy the string */ - for (input_pointer = input; *input_pointer != '\0'; (void)input_pointer++, output_pointer++) - { - if ((*input_pointer > 31) && (*input_pointer != '\"') && (*input_pointer != '\\')) - { - /* normal character, copy */ - *output_pointer = *input_pointer; - } - else - { - /* character needs to be escaped */ - *output_pointer++ = '\\'; - switch (*input_pointer) - { - case '\\': - *output_pointer = '\\'; - break; - case '\"': - *output_pointer = '\"'; - break; - case '\b': - *output_pointer = 'b'; - break; - case '\f': - *output_pointer = 'f'; - break; - case '\n': - *output_pointer = 'n'; - break; - case '\r': - *output_pointer = 'r'; - break; - case '\t': - *output_pointer = 't'; - break; - default: - /* escape and print as unicode codepoint */ - snprintf((char*)output_pointer, sizeof(output_pointer), "u%04x", *input_pointer); - output_pointer += 4; - break; - } - } - } - output[output_length + 1] = '\"'; - output[output_length + 2] = '\0'; - - return true; -} - -/* Invoke print_string_ptr (which is useful) on an item. */ -static cJSON_bool print_string(const cJSON * const item, printbuffer * const p) -{ - return print_string_ptr((unsigned char*)item->valuestring, p); -} - -/* Predeclare these prototypes. */ -static cJSON_bool parse_value(cJSON * const item, parse_buffer * const input_buffer); -static cJSON_bool print_value(const cJSON * const item, printbuffer * const output_buffer); -static cJSON_bool parse_array(cJSON * const item, parse_buffer * const input_buffer); -static cJSON_bool print_array(const cJSON * const item, printbuffer * const output_buffer); -static cJSON_bool parse_object(cJSON * const item, parse_buffer * const input_buffer); -static cJSON_bool print_object(const cJSON * const item, printbuffer * const output_buffer); - -/* Utility to jump whitespace and cr/lf */ -static parse_buffer *buffer_skip_whitespace(parse_buffer * const buffer) -{ - if ((buffer == NULL) || (buffer->content == NULL)) - { - return NULL; - } - - if (cannot_access_at_index(buffer, 0)) - { - return buffer; - } - - while (can_access_at_index(buffer, 0) && (buffer_at_offset(buffer)[0] <= 32)) - { - buffer->offset++; - } - - if (buffer->offset == buffer->length) - { - buffer->offset--; - } - - return buffer; -} - -/* skip the UTF-8 BOM (byte order mark) if it is at the beginning of a buffer */ -static parse_buffer *skip_utf8_bom(parse_buffer * const buffer) -{ - if ((buffer == NULL) || (buffer->content == NULL) || (buffer->offset != 0)) - { - return NULL; - } - - if (can_access_at_index(buffer, 4) && (strncmp((const char*)buffer_at_offset(buffer), "\xEF\xBB\xBF", 3) == 0)) - { - buffer->offset += 3; - } - - return buffer; -} - -CJSON_PUBLIC(cJSON *) cJSON_ParseWithOpts(const char *value, const char **return_parse_end, cJSON_bool require_null_terminated) -{ - size_t buffer_length; - - if (NULL == value) - { - return NULL; - } - - /* Adding null character size due to require_null_terminated. */ - buffer_length = strlen(value) + sizeof(""); - - return cJSON_ParseWithLengthOpts(value, buffer_length, return_parse_end, require_null_terminated); -} - -/* Parse an object - create a new root, and populate. */ -CJSON_PUBLIC(cJSON *) cJSON_ParseWithLengthOpts(const char *value, size_t buffer_length, const char **return_parse_end, cJSON_bool require_null_terminated) -{ - parse_buffer buffer = { 0, 0, 0, 0, { 0, 0, 0 } }; - cJSON *item = NULL; - - /* reset error position */ - global_error.json = NULL; - global_error.position = 0; - - if (value == NULL || 0 == buffer_length) - { - goto fail; - } - - buffer.content = (const unsigned char*)value; - buffer.length = buffer_length; - buffer.offset = 0; - buffer.hooks = global_hooks; - - item = cJSON_New_Item(&global_hooks); - if (item == NULL) /* memory fail */ - { - goto fail; - } - - if (!parse_value(item, buffer_skip_whitespace(skip_utf8_bom(&buffer)))) - { - /* parse failure. ep is set. */ - goto fail; - } - - /* if we require null-terminated JSON without appended garbage, skip and then check for a null terminator */ - if (require_null_terminated) - { - buffer_skip_whitespace(&buffer); - if ((buffer.offset >= buffer.length) || buffer_at_offset(&buffer)[0] != '\0') - { - goto fail; - } - } - if (return_parse_end) - { - *return_parse_end = (const char*)buffer_at_offset(&buffer); - } - - return item; - -fail: - if (item != NULL) - { - cJSON_Delete(item); - } - - if (value != NULL) - { - error local_error; - local_error.json = (const unsigned char*)value; - local_error.position = 0; - - if (buffer.offset < buffer.length) - { - local_error.position = buffer.offset; - } - else if (buffer.length > 0) - { - local_error.position = buffer.length - 1; - } - - if (return_parse_end != NULL) - { - *return_parse_end = (const char*)local_error.json + local_error.position; - } - - global_error = local_error; - } - - return NULL; -} - -/* Default options for cJSON_Parse */ -CJSON_PUBLIC(cJSON *) cJSON_Parse(const char *value) -{ - return cJSON_ParseWithOpts(value, 0, 0); -} - -CJSON_PUBLIC(cJSON *) cJSON_ParseWithLength(const char *value, size_t buffer_length) -{ - return cJSON_ParseWithLengthOpts(value, buffer_length, 0, 0); -} - -#define cjson_min(a, b) (((a) < (b)) ? (a) : (b)) - -static unsigned char *print(const cJSON * const item, cJSON_bool format, const internal_hooks * const hooks) -{ - static const size_t default_buffer_size = 256; - printbuffer buffer[1]; - unsigned char *printed = NULL; - - memset(buffer, 0, sizeof(buffer)); - - /* create buffer */ - buffer->buffer = (unsigned char*) hooks->allocate(default_buffer_size); - buffer->length = default_buffer_size; - buffer->format = format; - buffer->hooks = *hooks; - if (buffer->buffer == NULL) - { - goto fail; - } - - /* print the value */ - if (!print_value(item, buffer)) - { - goto fail; - } - update_offset(buffer); - - /* check if reallocate is available */ - if (hooks->reallocate != NULL) - { - printed = (unsigned char*) hooks->reallocate(buffer->buffer, buffer->offset + 1); - if (printed == NULL) - { - goto fail; - } - buffer->buffer = NULL; - } - else /* otherwise copy the JSON over to a new buffer */ - { - printed = (unsigned char*) hooks->allocate(buffer->offset + 1); - if (printed == NULL) - { - goto fail; - } - memcpy(printed, buffer->buffer, cjson_min(buffer->length, buffer->offset + 1)); - printed[buffer->offset] = '\0'; /* just to be sure */ - - /* free the buffer */ - hooks->deallocate(buffer->buffer); - } - - return printed; - -fail: - if (buffer->buffer != NULL) - { - hooks->deallocate(buffer->buffer); - } - - if (printed != NULL) - { - hooks->deallocate(printed); - } - - return NULL; -} - -/* Render a cJSON item/entity/structure to text. */ -CJSON_PUBLIC(char *) cJSON_Print(const cJSON *item) -{ - return (char*)print(item, true, &global_hooks); -} - -CJSON_PUBLIC(char *) cJSON_PrintUnformatted(const cJSON *item) -{ - return (char*)print(item, false, &global_hooks); -} - -CJSON_PUBLIC(char *) cJSON_PrintBuffered(const cJSON *item, int prebuffer, cJSON_bool fmt) -{ - printbuffer p = { 0, 0, 0, 0, 0, 0, { 0, 0, 0 } }; - - if (prebuffer < 0) - { - return NULL; - } - - p.buffer = (unsigned char*)global_hooks.allocate((size_t)prebuffer); - if (!p.buffer) - { - return NULL; - } - - p.length = (size_t)prebuffer; - p.offset = 0; - p.noalloc = false; - p.format = fmt; - p.hooks = global_hooks; - - if (!print_value(item, &p)) - { - global_hooks.deallocate(p.buffer); - return NULL; - } - - return (char*)p.buffer; -} - -CJSON_PUBLIC(cJSON_bool) cJSON_PrintPreallocated(cJSON *item, char *buffer, const int length, const cJSON_bool format) -{ - printbuffer p = { 0, 0, 0, 0, 0, 0, { 0, 0, 0 } }; - - if ((length < 0) || (buffer == NULL)) - { - return false; - } - - p.buffer = (unsigned char*)buffer; - p.length = (size_t)length; - p.offset = 0; - p.noalloc = true; - p.format = format; - p.hooks = global_hooks; - - return print_value(item, &p); -} - -/* Parser core - when encountering text, process appropriately. */ -static cJSON_bool parse_value(cJSON * const item, parse_buffer * const input_buffer) -{ - if ((input_buffer == NULL) || (input_buffer->content == NULL)) - { - return false; /* no input */ - } - - /* parse the different types of values */ - /* null */ - if (can_read(input_buffer, 4) && (strncmp((const char*)buffer_at_offset(input_buffer), "null", 4) == 0)) - { - item->type = cJSON_NULL; - input_buffer->offset += 4; - return true; - } - /* false */ - if (can_read(input_buffer, 5) && (strncmp((const char*)buffer_at_offset(input_buffer), "false", 5) == 0)) - { - item->type = cJSON_False; - input_buffer->offset += 5; - return true; - } - /* true */ - if (can_read(input_buffer, 4) && (strncmp((const char*)buffer_at_offset(input_buffer), "true", 4) == 0)) - { - item->type = cJSON_True; - item->valueint = 1; - input_buffer->offset += 4; - return true; - } - /* string */ - if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == '\"')) - { - return parse_string(item, input_buffer); - } - /* number */ - if (can_access_at_index(input_buffer, 0) && ((buffer_at_offset(input_buffer)[0] == '-') || ((buffer_at_offset(input_buffer)[0] >= '0') && (buffer_at_offset(input_buffer)[0] <= '9')))) - { - return parse_number(item, input_buffer); - } - /* array */ - if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == '[')) - { - return parse_array(item, input_buffer); - } - /* object */ - if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == '{')) - { - return parse_object(item, input_buffer); - } - - return false; -} - -/* Render a value to text. */ -static cJSON_bool print_value(const cJSON * const item, printbuffer * const output_buffer) -{ - unsigned char *output = NULL; - - if ((item == NULL) || (output_buffer == NULL)) - { - return false; - } - - switch ((item->type) & 0xFF) - { - case cJSON_NULL: - output = ensure(output_buffer, 5); - if (output == NULL) - { - return false; - } - strcpy((char*)output, "null"); - return true; - - case cJSON_False: - output = ensure(output_buffer, 6); - if (output == NULL) - { - return false; - } - strcpy((char*)output, "false"); - return true; - - case cJSON_True: - output = ensure(output_buffer, 5); - if (output == NULL) - { - return false; - } - strcpy((char*)output, "true"); - return true; - - case cJSON_Number: - return print_number(item, output_buffer); - - case cJSON_Raw: - { - size_t raw_length = 0; - if (item->valuestring == NULL) - { - return false; - } - - raw_length = strlen(item->valuestring) + sizeof(""); - output = ensure(output_buffer, raw_length); - if (output == NULL) - { - return false; - } - memcpy(output, item->valuestring, raw_length); - return true; - } - - case cJSON_String: - return print_string(item, output_buffer); - - case cJSON_Array: - return print_array(item, output_buffer); - - case cJSON_Object: - return print_object(item, output_buffer); - - default: - return false; - } -} - -/* Build an array from input text. */ -static cJSON_bool parse_array(cJSON * const item, parse_buffer * const input_buffer) -{ - cJSON *head = NULL; /* head of the linked list */ - cJSON *current_item = NULL; - - if (input_buffer->depth >= CJSON_NESTING_LIMIT) - { - return false; /* to deeply nested */ - } - input_buffer->depth++; - - if (buffer_at_offset(input_buffer)[0] != '[') - { - /* not an array */ - goto fail; - } - - input_buffer->offset++; - buffer_skip_whitespace(input_buffer); - if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == ']')) - { - /* empty array */ - goto success; - } - - /* check if we skipped to the end of the buffer */ - if (cannot_access_at_index(input_buffer, 0)) - { - input_buffer->offset--; - goto fail; - } - - /* step back to character in front of the first element */ - input_buffer->offset--; - /* loop through the comma separated array elements */ - do - { - /* allocate next item */ - cJSON *new_item = cJSON_New_Item(&(input_buffer->hooks)); - if (new_item == NULL) - { - goto fail; /* allocation failure */ - } - - /* attach next item to list */ - if (head == NULL) - { - /* start the linked list */ - current_item = head = new_item; - } - else - { - /* add to the end and advance */ - current_item->next = new_item; - new_item->prev = current_item; - current_item = new_item; - } - - /* parse next value */ - input_buffer->offset++; - buffer_skip_whitespace(input_buffer); - if (!parse_value(current_item, input_buffer)) - { - goto fail; /* failed to parse value */ - } - buffer_skip_whitespace(input_buffer); - } - while (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == ',')); - - if (cannot_access_at_index(input_buffer, 0) || buffer_at_offset(input_buffer)[0] != ']') - { - goto fail; /* expected end of array */ - } - -success: - input_buffer->depth--; - - if (head != NULL) - { - head->prev = current_item; - } - - item->type = cJSON_Array; - item->child = head; - - input_buffer->offset++; - - return true; - -fail: - if (head != NULL) - { - cJSON_Delete(head); - } - - return false; -} - -/* Render an array to text */ -static cJSON_bool print_array(const cJSON * const item, printbuffer * const output_buffer) -{ - unsigned char *output_pointer = NULL; - size_t length = 0; - cJSON *current_element = item->child; - - if (output_buffer == NULL) - { - return false; - } - - /* Compose the output array. */ - /* opening square bracket */ - output_pointer = ensure(output_buffer, 1); - if (output_pointer == NULL) - { - return false; - } - - *output_pointer = '['; - output_buffer->offset++; - output_buffer->depth++; - - while (current_element != NULL) - { - if (!print_value(current_element, output_buffer)) - { - return false; - } - update_offset(output_buffer); - if (current_element->next) - { - length = (size_t) (output_buffer->format ? 2 : 1); - output_pointer = ensure(output_buffer, length + 1); - if (output_pointer == NULL) - { - return false; - } - *output_pointer++ = ','; - if(output_buffer->format) - { - *output_pointer++ = ' '; - } - *output_pointer = '\0'; - output_buffer->offset += length; - } - current_element = current_element->next; - } - - output_pointer = ensure(output_buffer, 2); - if (output_pointer == NULL) - { - return false; - } - *output_pointer++ = ']'; - *output_pointer = '\0'; - output_buffer->depth--; - - return true; -} - -/* Build an object from the text. */ -static cJSON_bool parse_object(cJSON * const item, parse_buffer * const input_buffer) -{ - cJSON *head = NULL; /* linked list head */ - cJSON *current_item = NULL; - - if (input_buffer->depth >= CJSON_NESTING_LIMIT) - { - return false; /* to deeply nested */ - } - input_buffer->depth++; - - if (cannot_access_at_index(input_buffer, 0) || (buffer_at_offset(input_buffer)[0] != '{')) - { - goto fail; /* not an object */ - } - - input_buffer->offset++; - buffer_skip_whitespace(input_buffer); - if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == '}')) - { - goto success; /* empty object */ - } - - /* check if we skipped to the end of the buffer */ - if (cannot_access_at_index(input_buffer, 0)) - { - input_buffer->offset--; - goto fail; - } - - /* step back to character in front of the first element */ - input_buffer->offset--; - /* loop through the comma separated array elements */ - do - { - /* allocate next item */ - cJSON *new_item = cJSON_New_Item(&(input_buffer->hooks)); - if (new_item == NULL) - { - goto fail; /* allocation failure */ - } - - /* attach next item to list */ - if (head == NULL) - { - /* start the linked list */ - current_item = head = new_item; - } - else - { - /* add to the end and advance */ - current_item->next = new_item; - new_item->prev = current_item; - current_item = new_item; - } - - /* parse the name of the child */ - input_buffer->offset++; - buffer_skip_whitespace(input_buffer); - if (!parse_string(current_item, input_buffer)) - { - goto fail; /* failed to parse name */ - } - buffer_skip_whitespace(input_buffer); - - /* swap valuestring and string, because we parsed the name */ - current_item->string = current_item->valuestring; - current_item->valuestring = NULL; - - if (cannot_access_at_index(input_buffer, 0) || (buffer_at_offset(input_buffer)[0] != ':')) - { - goto fail; /* invalid object */ - } - - /* parse the value */ - input_buffer->offset++; - buffer_skip_whitespace(input_buffer); - if (!parse_value(current_item, input_buffer)) - { - goto fail; /* failed to parse value */ - } - buffer_skip_whitespace(input_buffer); - } - while (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == ',')); - - if (cannot_access_at_index(input_buffer, 0) || (buffer_at_offset(input_buffer)[0] != '}')) - { - goto fail; /* expected end of object */ - } - -success: - input_buffer->depth--; - - if (head != NULL) - { - head->prev = current_item; - } - - item->type = cJSON_Object; - item->child = head; - - input_buffer->offset++; - return true; - -fail: - if (head != NULL) - { - cJSON_Delete(head); - } - - return false; -} - -/* Render an object to text. */ -static cJSON_bool print_object(const cJSON * const item, printbuffer * const output_buffer) -{ - unsigned char *output_pointer = NULL; - size_t length = 0; - cJSON *current_item = item->child; - - if (output_buffer == NULL) - { - return false; - } - - /* Compose the output: */ - length = (size_t) (output_buffer->format ? 2 : 1); /* fmt: {\n */ - output_pointer = ensure(output_buffer, length + 1); - if (output_pointer == NULL) - { - return false; - } - - *output_pointer++ = '{'; - output_buffer->depth++; - if (output_buffer->format) - { - *output_pointer++ = '\n'; - } - output_buffer->offset += length; - - while (current_item) - { - if (output_buffer->format) - { - size_t i; - output_pointer = ensure(output_buffer, output_buffer->depth); - if (output_pointer == NULL) - { - return false; - } - for (i = 0; i < output_buffer->depth; i++) - { - *output_pointer++ = '\t'; - } - output_buffer->offset += output_buffer->depth; - } - - /* print key */ - if (!print_string_ptr((unsigned char*)current_item->string, output_buffer)) - { - return false; - } - update_offset(output_buffer); - - length = (size_t) (output_buffer->format ? 2 : 1); - output_pointer = ensure(output_buffer, length); - if (output_pointer == NULL) - { - return false; - } - *output_pointer++ = ':'; - if (output_buffer->format) - { - *output_pointer++ = '\t'; - } - output_buffer->offset += length; - - /* print value */ - if (!print_value(current_item, output_buffer)) - { - return false; - } - update_offset(output_buffer); - - /* print comma if not last */ - length = ((size_t)(output_buffer->format ? 1 : 0) + (size_t)(current_item->next ? 1 : 0)); - output_pointer = ensure(output_buffer, length + 1); - if (output_pointer == NULL) - { - return false; - } - if (current_item->next) - { - *output_pointer++ = ','; - } - - if (output_buffer->format) - { - *output_pointer++ = '\n'; - } - *output_pointer = '\0'; - output_buffer->offset += length; - - current_item = current_item->next; - } - - output_pointer = ensure(output_buffer, output_buffer->format ? (output_buffer->depth + 1) : 2); - if (output_pointer == NULL) - { - return false; - } - if (output_buffer->format) - { - size_t i; - for (i = 0; i < (output_buffer->depth - 1); i++) - { - *output_pointer++ = '\t'; - } - } - *output_pointer++ = '}'; - *output_pointer = '\0'; - output_buffer->depth--; - - return true; -} - -/* Get Array size/item / object item. */ -CJSON_PUBLIC(int) cJSON_GetArraySize(const cJSON *array) -{ - cJSON *child = NULL; - size_t size = 0; - - if (array == NULL) - { - return 0; - } - - child = array->child; - - while(child != NULL) - { - size++; - child = child->next; - } - - /* FIXME: Can overflow here. Cannot be fixed without breaking the API */ - - return (int)size; -} - -static cJSON* get_array_item(const cJSON *array, size_t index) -{ - cJSON *current_child = NULL; - - if (array == NULL) - { - return NULL; - } - - current_child = array->child; - while ((current_child != NULL) && (index > 0)) - { - index--; - current_child = current_child->next; - } - - return current_child; -} - -CJSON_PUBLIC(cJSON *) cJSON_GetArrayItem(const cJSON *array, int index) -{ - if (index < 0) - { - return NULL; - } - - return get_array_item(array, (size_t)index); -} - -static cJSON *get_object_item(const cJSON * const object, const char * const name, const cJSON_bool case_sensitive) -{ - cJSON *current_element = NULL; - - if ((object == NULL) || (name == NULL)) - { - return NULL; - } - - current_element = object->child; - if (case_sensitive) - { - while ((current_element != NULL) && (current_element->string != NULL) && (strcmp(name, current_element->string) != 0)) - { - current_element = current_element->next; - } - } - else - { - while ((current_element != NULL) && (case_insensitive_strcmp((const unsigned char*)name, (const unsigned char*)(current_element->string)) != 0)) - { - current_element = current_element->next; - } - } - - if ((current_element == NULL) || (current_element->string == NULL)) - { - return NULL; - } - - return current_element; -} - -CJSON_PUBLIC(cJSON *) cJSON_GetObjectItem(const cJSON * const object, const char * const string) -{ - return get_object_item(object, string, false); -} - -CJSON_PUBLIC(cJSON *) cJSON_GetObjectItemCaseSensitive(const cJSON * const object, const char * const string) -{ - return get_object_item(object, string, true); -} - -CJSON_PUBLIC(cJSON_bool) cJSON_HasObjectItem(const cJSON *object, const char *string) -{ - return cJSON_GetObjectItem(object, string) ? 1 : 0; -} - -/* Utility for array list handling. */ -static void suffix_object(cJSON *prev, cJSON *item) -{ - prev->next = item; - item->prev = prev; -} - -/* Utility for handling references. */ -static cJSON *create_reference(const cJSON *item, const internal_hooks * const hooks) -{ - cJSON *reference = NULL; - if (item == NULL) - { - return NULL; - } - - reference = cJSON_New_Item(hooks); - if (reference == NULL) - { - return NULL; - } - - memcpy(reference, item, sizeof(cJSON)); - reference->string = NULL; - reference->type |= cJSON_IsReference; - reference->next = reference->prev = NULL; - return reference; -} - -static cJSON_bool add_item_to_array(cJSON *array, cJSON *item) -{ - cJSON *child = NULL; - - if ((item == NULL) || (array == NULL) || (array == item)) - { - return false; - } - - child = array->child; - /* - * To find the last item in array quickly, we use prev in array - */ - if (child == NULL) - { - /* list is empty, start new one */ - array->child = item; - item->prev = item; - item->next = NULL; - } - else - { - /* append to the end */ - if (child->prev) - { - suffix_object(child->prev, item); - array->child->prev = item; - } - } - - return true; -} - -/* Add item to array/object. */ -CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToArray(cJSON *array, cJSON *item) -{ - return add_item_to_array(array, item); -} - -#if defined(__clang__) || (defined(__GNUC__) && ((__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ > 5)))) -#pragma GCC diagnostic push -#endif -#ifdef __GNUC__ -#pragma GCC diagnostic ignored "-Wcast-qual" -#endif -/* helper function to cast away const */ -static void* cast_away_const(const void* string) -{ - return (void*)string; -} -#if defined(__clang__) || (defined(__GNUC__) && ((__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ > 5)))) -#pragma GCC diagnostic pop -#endif - - -static cJSON_bool add_item_to_object(cJSON * const object, const char * const string, cJSON * const item, const internal_hooks * const hooks, const cJSON_bool constant_key) -{ - char *new_key = NULL; - int new_type = cJSON_Invalid; - - if ((object == NULL) || (string == NULL) || (item == NULL) || (object == item)) - { - return false; - } - - if (constant_key) - { - new_key = (char*)cast_away_const(string); - new_type = item->type | cJSON_StringIsConst; - } - else - { - new_key = (char*)cJSON_strdup((const unsigned char*)string, hooks); - if (new_key == NULL) - { - return false; - } - - new_type = item->type & ~cJSON_StringIsConst; - } - - if (!(item->type & cJSON_StringIsConst) && (item->string != NULL)) - { - hooks->deallocate(item->string); - } - - item->string = new_key; - item->type = new_type; - - return add_item_to_array(object, item); -} - -CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToObject(cJSON *object, const char *string, cJSON *item) -{ - return add_item_to_object(object, string, item, &global_hooks, false); -} - -/* Add an item to an object with constant string as key */ -CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToObjectCS(cJSON *object, const char *string, cJSON *item) -{ - return add_item_to_object(object, string, item, &global_hooks, true); -} - -CJSON_PUBLIC(cJSON_bool) cJSON_AddItemReferenceToArray(cJSON *array, cJSON *item) -{ - if (array == NULL) - { - return false; - } - - return add_item_to_array(array, create_reference(item, &global_hooks)); -} - -CJSON_PUBLIC(cJSON_bool) cJSON_AddItemReferenceToObject(cJSON *object, const char *string, cJSON *item) -{ - if ((object == NULL) || (string == NULL)) - { - return false; - } - - return add_item_to_object(object, string, create_reference(item, &global_hooks), &global_hooks, false); -} - -CJSON_PUBLIC(cJSON*) cJSON_AddNullToObject(cJSON * const object, const char * const name) -{ - cJSON *null = cJSON_CreateNull(); - if (add_item_to_object(object, name, null, &global_hooks, false)) - { - return null; - } - - cJSON_Delete(null); - return NULL; -} - -CJSON_PUBLIC(cJSON*) cJSON_AddTrueToObject(cJSON * const object, const char * const name) -{ - cJSON *true_item = cJSON_CreateTrue(); - if (add_item_to_object(object, name, true_item, &global_hooks, false)) - { - return true_item; - } - - cJSON_Delete(true_item); - return NULL; -} - -CJSON_PUBLIC(cJSON*) cJSON_AddFalseToObject(cJSON * const object, const char * const name) -{ - cJSON *false_item = cJSON_CreateFalse(); - if (add_item_to_object(object, name, false_item, &global_hooks, false)) - { - return false_item; - } - - cJSON_Delete(false_item); - return NULL; -} - -CJSON_PUBLIC(cJSON*) cJSON_AddBoolToObject(cJSON * const object, const char * const name, const cJSON_bool boolean) -{ - cJSON *bool_item = cJSON_CreateBool(boolean); - if (add_item_to_object(object, name, bool_item, &global_hooks, false)) - { - return bool_item; - } - - cJSON_Delete(bool_item); - return NULL; -} - -CJSON_PUBLIC(cJSON*) cJSON_AddNumberToObject(cJSON * const object, const char * const name, const double number) -{ - cJSON *number_item = cJSON_CreateNumber(number); - if (add_item_to_object(object, name, number_item, &global_hooks, false)) - { - return number_item; - } - - cJSON_Delete(number_item); - return NULL; -} - -CJSON_PUBLIC(cJSON*) cJSON_AddStringToObject(cJSON * const object, const char * const name, const char * const string) -{ - cJSON *string_item = cJSON_CreateString(string); - if (add_item_to_object(object, name, string_item, &global_hooks, false)) - { - return string_item; - } - - cJSON_Delete(string_item); - return NULL; -} - -CJSON_PUBLIC(cJSON*) cJSON_AddRawToObject(cJSON * const object, const char * const name, const char * const raw) -{ - cJSON *raw_item = cJSON_CreateRaw(raw); - if (add_item_to_object(object, name, raw_item, &global_hooks, false)) - { - return raw_item; - } - - cJSON_Delete(raw_item); - return NULL; -} - -CJSON_PUBLIC(cJSON*) cJSON_AddObjectToObject(cJSON * const object, const char * const name) -{ - cJSON *object_item = cJSON_CreateObject(); - if (add_item_to_object(object, name, object_item, &global_hooks, false)) - { - return object_item; - } - - cJSON_Delete(object_item); - return NULL; -} - -CJSON_PUBLIC(cJSON*) cJSON_AddArrayToObject(cJSON * const object, const char * const name) -{ - cJSON *array = cJSON_CreateArray(); - if (add_item_to_object(object, name, array, &global_hooks, false)) - { - return array; - } - - cJSON_Delete(array); - return NULL; -} - -CJSON_PUBLIC(cJSON *) cJSON_DetachItemViaPointer(cJSON *parent, cJSON * const item) -{ - if ((parent == NULL) || (item == NULL)) - { - return NULL; - } - - if (item != parent->child) - { - /* not the first element */ - item->prev->next = item->next; - } - if (item->next != NULL) - { - /* not the last element */ - item->next->prev = item->prev; - } - - if (item == parent->child) - { - /* first element */ - parent->child = item->next; - } - else if (item->next == NULL) - { - /* last element */ - parent->child->prev = item->prev; - } - - /* make sure the detached item doesn't point anywhere anymore */ - item->prev = NULL; - item->next = NULL; - - return item; -} - -CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromArray(cJSON *array, int which) -{ - if (which < 0) - { - return NULL; - } - - return cJSON_DetachItemViaPointer(array, get_array_item(array, (size_t)which)); -} - -CJSON_PUBLIC(void) cJSON_DeleteItemFromArray(cJSON *array, int which) -{ - cJSON_Delete(cJSON_DetachItemFromArray(array, which)); -} - -CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromObject(cJSON *object, const char *string) -{ - cJSON *to_detach = cJSON_GetObjectItem(object, string); - - return cJSON_DetachItemViaPointer(object, to_detach); -} - -CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromObjectCaseSensitive(cJSON *object, const char *string) -{ - cJSON *to_detach = cJSON_GetObjectItemCaseSensitive(object, string); - - return cJSON_DetachItemViaPointer(object, to_detach); -} - -CJSON_PUBLIC(void) cJSON_DeleteItemFromObject(cJSON *object, const char *string) -{ - cJSON_Delete(cJSON_DetachItemFromObject(object, string)); -} - -CJSON_PUBLIC(void) cJSON_DeleteItemFromObjectCaseSensitive(cJSON *object, const char *string) -{ - cJSON_Delete(cJSON_DetachItemFromObjectCaseSensitive(object, string)); -} - -/* Replace array/object items with new ones. */ -CJSON_PUBLIC(cJSON_bool) cJSON_InsertItemInArray(cJSON *array, int which, cJSON *newitem) -{ - cJSON *after_inserted = NULL; - - if (which < 0) - { - return false; - } - - after_inserted = get_array_item(array, (size_t)which); - if (after_inserted == NULL) - { - return add_item_to_array(array, newitem); - } - - newitem->next = after_inserted; - newitem->prev = after_inserted->prev; - after_inserted->prev = newitem; - if (after_inserted == array->child) - { - array->child = newitem; - } - else - { - newitem->prev->next = newitem; - } - return true; -} - -CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemViaPointer(cJSON * const parent, cJSON * const item, cJSON * replacement) -{ - if ((parent == NULL) || (replacement == NULL) || (item == NULL)) - { - return false; - } - - if (replacement == item) - { - return true; - } - - replacement->next = item->next; - replacement->prev = item->prev; - - if (replacement->next != NULL) - { - replacement->next->prev = replacement; - } - if (parent->child == item) - { - if (parent->child->prev == parent->child) - { - replacement->prev = replacement; - } - parent->child = replacement; - } - else - { - /* - * To find the last item in array quickly, we use prev in array. - * We can't modify the last item's next pointer where this item was the parent's child - */ - if (replacement->prev != NULL) - { - replacement->prev->next = replacement; - } - if (replacement->next == NULL) - { - parent->child->prev = replacement; - } - } - - item->next = NULL; - item->prev = NULL; - cJSON_Delete(item); - - return true; -} - -CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemInArray(cJSON *array, int which, cJSON *newitem) -{ - if (which < 0) - { - return false; - } - - return cJSON_ReplaceItemViaPointer(array, get_array_item(array, (size_t)which), newitem); -} - -static cJSON_bool replace_item_in_object(cJSON *object, const char *string, cJSON *replacement, cJSON_bool case_sensitive) -{ - if ((replacement == NULL) || (string == NULL)) - { - return false; - } - - /* replace the name in the replacement */ - if (!(replacement->type & cJSON_StringIsConst) && (replacement->string != NULL)) - { - cJSON_free(replacement->string); - } - replacement->string = (char*)cJSON_strdup((const unsigned char*)string, &global_hooks); - if (replacement->string == NULL) - { - return false; - } - - replacement->type &= ~cJSON_StringIsConst; - - return cJSON_ReplaceItemViaPointer(object, get_object_item(object, string, case_sensitive), replacement); -} - -CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemInObject(cJSON *object, const char *string, cJSON *newitem) -{ - return replace_item_in_object(object, string, newitem, false); -} - -CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemInObjectCaseSensitive(cJSON *object, const char *string, cJSON *newitem) -{ - return replace_item_in_object(object, string, newitem, true); -} - -/* Create basic types: */ -CJSON_PUBLIC(cJSON *) cJSON_CreateNull(void) -{ - cJSON *item = cJSON_New_Item(&global_hooks); - if(item) - { - item->type = cJSON_NULL; - } - - return item; -} - -CJSON_PUBLIC(cJSON *) cJSON_CreateTrue(void) -{ - cJSON *item = cJSON_New_Item(&global_hooks); - if(item) - { - item->type = cJSON_True; - } - - return item; -} - -CJSON_PUBLIC(cJSON *) cJSON_CreateFalse(void) -{ - cJSON *item = cJSON_New_Item(&global_hooks); - if(item) - { - item->type = cJSON_False; - } - - return item; -} - -CJSON_PUBLIC(cJSON *) cJSON_CreateBool(cJSON_bool boolean) -{ - cJSON *item = cJSON_New_Item(&global_hooks); - if(item) - { - item->type = boolean ? cJSON_True : cJSON_False; - } - - return item; -} - -CJSON_PUBLIC(cJSON *) cJSON_CreateNumber(double num) -{ - cJSON *item = cJSON_New_Item(&global_hooks); - if(item) - { - item->type = cJSON_Number; - item->valuedouble = num; - - /* use saturation in case of overflow */ - if (num >= INT_MAX) - { - item->valueint = INT_MAX; - } - else if (num <= (double)INT_MIN) - { - item->valueint = INT_MIN; - } - else - { - item->valueint = (int)num; - } - } - - return item; -} - -CJSON_PUBLIC(cJSON *) cJSON_CreateString(const char *string) -{ - cJSON *item = cJSON_New_Item(&global_hooks); - if(item) - { - item->type = cJSON_String; - item->valuestring = (char*)cJSON_strdup((const unsigned char*)string, &global_hooks); - if(!item->valuestring) - { - cJSON_Delete(item); - return NULL; - } - } - - return item; -} - -CJSON_PUBLIC(cJSON *) cJSON_CreateStringReference(const char *string) -{ - cJSON *item = cJSON_New_Item(&global_hooks); - if (item != NULL) - { - item->type = cJSON_String | cJSON_IsReference; - item->valuestring = (char*)cast_away_const(string); - } - - return item; -} - -CJSON_PUBLIC(cJSON *) cJSON_CreateObjectReference(const cJSON *child) -{ - cJSON *item = cJSON_New_Item(&global_hooks); - if (item != NULL) - { - item->type = cJSON_Object | cJSON_IsReference; - item->child = (cJSON*)cast_away_const(child); - } - - return item; -} - -CJSON_PUBLIC(cJSON *) cJSON_CreateArrayReference(const cJSON *child) -{ - cJSON *item = cJSON_New_Item(&global_hooks); - if (item != NULL) - { - item->type = cJSON_Array | cJSON_IsReference; - item->child = (cJSON*)cast_away_const(child); - } - - return item; -} - -CJSON_PUBLIC(cJSON *) cJSON_CreateRaw(const char *raw) -{ - cJSON *item = cJSON_New_Item(&global_hooks); - if(item) - { - item->type = cJSON_Raw; - item->valuestring = (char*)cJSON_strdup((const unsigned char*)raw, &global_hooks); - if(!item->valuestring) - { - cJSON_Delete(item); - return NULL; - } - } - - return item; -} - -CJSON_PUBLIC(cJSON *) cJSON_CreateArray(void) -{ - cJSON *item = cJSON_New_Item(&global_hooks); - if(item) - { - item->type=cJSON_Array; - } - - return item; -} - -CJSON_PUBLIC(cJSON *) cJSON_CreateObject(void) -{ - cJSON *item = cJSON_New_Item(&global_hooks); - if (item) - { - item->type = cJSON_Object; - } - - return item; -} - -/* Create Arrays: */ -CJSON_PUBLIC(cJSON *) cJSON_CreateIntArray(const int *numbers, int count) -{ - size_t i = 0; - cJSON *n = NULL; - cJSON *p = NULL; - cJSON *a = NULL; - - if ((count < 0) || (numbers == NULL)) - { - return NULL; - } - - a = cJSON_CreateArray(); - - for(i = 0; a && (i < (size_t)count); i++) - { - n = cJSON_CreateNumber(numbers[i]); - if (!n) - { - cJSON_Delete(a); - return NULL; - } - if(!i) - { - a->child = n; - } - else - { - suffix_object(p, n); - } - p = n; - } - - if (a && a->child) - { - a->child->prev = n; - } - - return a; -} - -CJSON_PUBLIC(cJSON *) cJSON_CreateFloatArray(const float *numbers, int count) -{ - size_t i = 0; - cJSON *n = NULL; - cJSON *p = NULL; - cJSON *a = NULL; - - if ((count < 0) || (numbers == NULL)) - { - return NULL; - } - - a = cJSON_CreateArray(); - - for(i = 0; a && (i < (size_t)count); i++) - { - n = cJSON_CreateNumber((double)numbers[i]); - if(!n) - { - cJSON_Delete(a); - return NULL; - } - if(!i) - { - a->child = n; - } - else - { - suffix_object(p, n); - } - p = n; - } - - if (a && a->child) - { - a->child->prev = n; - } - - return a; -} - -CJSON_PUBLIC(cJSON *) cJSON_CreateDoubleArray(const double *numbers, int count) -{ - size_t i = 0; - cJSON *n = NULL; - cJSON *p = NULL; - cJSON *a = NULL; - - if ((count < 0) || (numbers == NULL)) - { - return NULL; - } - - a = cJSON_CreateArray(); - - for(i = 0; a && (i < (size_t)count); i++) - { - n = cJSON_CreateNumber(numbers[i]); - if(!n) - { - cJSON_Delete(a); - return NULL; - } - if(!i) - { - a->child = n; - } - else - { - suffix_object(p, n); - } - p = n; - } - - if (a && a->child) - { - a->child->prev = n; - } - - return a; -} - -CJSON_PUBLIC(cJSON *) cJSON_CreateStringArray(const char *const *strings, int count) -{ - size_t i = 0; - cJSON *n = NULL; - cJSON *p = NULL; - cJSON *a = NULL; - - if ((count < 0) || (strings == NULL)) - { - return NULL; - } - - a = cJSON_CreateArray(); - - for (i = 0; a && (i < (size_t)count); i++) - { - n = cJSON_CreateString(strings[i]); - if(!n) - { - cJSON_Delete(a); - return NULL; - } - if(!i) - { - a->child = n; - } - else - { - suffix_object(p,n); - } - p = n; - } - - if (a && a->child) - { - a->child->prev = n; - } - - return a; -} - -/* Duplication */ -CJSON_PUBLIC(cJSON *) cJSON_Duplicate(const cJSON *item, cJSON_bool recurse) -{ - cJSON *newitem = NULL; - cJSON *child = NULL; - cJSON *next = NULL; - cJSON *newchild = NULL; - - /* Bail on bad ptr */ - if (!item) - { - goto fail; - } - /* Create new item */ - newitem = cJSON_New_Item(&global_hooks); - if (!newitem) - { - goto fail; - } - /* Copy over all vars */ - newitem->type = item->type & (~cJSON_IsReference); - newitem->valueint = item->valueint; - newitem->valuedouble = item->valuedouble; - if (item->valuestring) - { - newitem->valuestring = (char*)cJSON_strdup((unsigned char*)item->valuestring, &global_hooks); - if (!newitem->valuestring) - { - goto fail; - } - } - if (item->string) - { - newitem->string = (item->type&cJSON_StringIsConst) ? item->string : (char*)cJSON_strdup((unsigned char*)item->string, &global_hooks); - if (!newitem->string) - { - goto fail; - } - } - /* If non-recursive, then we're done! */ - if (!recurse) - { - return newitem; - } - /* Walk the ->next chain for the child. */ - child = item->child; - while (child != NULL) - { - newchild = cJSON_Duplicate(child, true); /* Duplicate (with recurse) each item in the ->next chain */ - if (!newchild) - { - goto fail; - } - if (next != NULL) - { - /* If newitem->child already set, then crosswire ->prev and ->next and move on */ - next->next = newchild; - newchild->prev = next; - next = newchild; - } - else - { - /* Set newitem->child and move to it */ - newitem->child = newchild; - next = newchild; - } - child = child->next; - } - if (newitem && newitem->child) - { - newitem->child->prev = newchild; - } - - return newitem; - -fail: - if (newitem != NULL) - { - cJSON_Delete(newitem); - } - - return NULL; -} - -static void skip_oneline_comment(char **input) -{ - *input += static_strlen("//"); - - for (; (*input)[0] != '\0'; ++(*input)) - { - if ((*input)[0] == '\n') - { - *input += static_strlen("\n"); - return; - } - } -} - -static void skip_multiline_comment(char **input) -{ - *input += static_strlen("/*"); - - for (; (*input)[0] != '\0'; ++(*input)) - { - if (((*input)[0] == '*') && ((*input)[1] == '/')) - { - *input += static_strlen("*/"); - return; - } - } -} - -static void minify_string(char **input, char **output) -{ - (*output)[0] = (*input)[0]; - *input += static_strlen("\""); - *output += static_strlen("\""); - - - for (; (*input)[0] != '\0'; (void)++(*input), ++(*output)) - { - (*output)[0] = (*input)[0]; - - if ((*input)[0] == '\"') - { - (*output)[0] = '\"'; - *input += static_strlen("\""); - *output += static_strlen("\""); - return; - } - else if (((*input)[0] == '\\') && ((*input)[1] == '\"')) - { - (*output)[1] = (*input)[1]; - *input += static_strlen("\""); - *output += static_strlen("\""); - } - } -} - -CJSON_PUBLIC(void) cJSON_Minify(char *json) -{ - char *into = json; - - if (json == NULL) - { - return; - } - - while (json[0] != '\0') - { - switch (json[0]) - { - case ' ': - case '\t': - case '\r': - case '\n': - json++; - break; - - case '/': - if (json[1] == '/') - { - skip_oneline_comment(&json); - } - else if (json[1] == '*') - { - skip_multiline_comment(&json); - } - else - { - json++; - } - break; - - case '\"': - minify_string(&json, (char**)&into); - break; - - default: - into[0] = json[0]; - json++; - into++; - } - } - - /* and null-terminate. */ - *into = '\0'; -} - -CJSON_PUBLIC(cJSON_bool) cJSON_IsInvalid(const cJSON * const item) -{ - if (item == NULL) - { - return false; - } - - return (item->type & 0xFF) == cJSON_Invalid; -} - -CJSON_PUBLIC(cJSON_bool) cJSON_IsFalse(const cJSON * const item) -{ - if (item == NULL) - { - return false; - } - - return (item->type & 0xFF) == cJSON_False; -} - -CJSON_PUBLIC(cJSON_bool) cJSON_IsTrue(const cJSON * const item) -{ - if (item == NULL) - { - return false; - } - - return (item->type & 0xff) == cJSON_True; -} - - -CJSON_PUBLIC(cJSON_bool) cJSON_IsBool(const cJSON * const item) -{ - if (item == NULL) - { - return false; - } - - return (item->type & (cJSON_True | cJSON_False)) != 0; -} -CJSON_PUBLIC(cJSON_bool) cJSON_IsNull(const cJSON * const item) -{ - if (item == NULL) - { - return false; - } - - return (item->type & 0xFF) == cJSON_NULL; -} - -CJSON_PUBLIC(cJSON_bool) cJSON_IsNumber(const cJSON * const item) -{ - if (item == NULL) - { - return false; - } - - return (item->type & 0xFF) == cJSON_Number; -} - -CJSON_PUBLIC(cJSON_bool) cJSON_IsString(const cJSON * const item) -{ - if (item == NULL) - { - return false; - } - - return (item->type & 0xFF) == cJSON_String; -} - -CJSON_PUBLIC(cJSON_bool) cJSON_IsArray(const cJSON * const item) -{ - if (item == NULL) - { - return false; - } - - return (item->type & 0xFF) == cJSON_Array; -} - -CJSON_PUBLIC(cJSON_bool) cJSON_IsObject(const cJSON * const item) -{ - if (item == NULL) - { - return false; - } - - return (item->type & 0xFF) == cJSON_Object; -} - -CJSON_PUBLIC(cJSON_bool) cJSON_IsRaw(const cJSON * const item) -{ - if (item == NULL) - { - return false; - } - - return (item->type & 0xFF) == cJSON_Raw; -} - -CJSON_PUBLIC(cJSON_bool) cJSON_Compare(const cJSON * const a, const cJSON * const b, const cJSON_bool case_sensitive) -{ - if ((a == NULL) || (b == NULL) || ((a->type & 0xFF) != (b->type & 0xFF))) - { - return false; - } - - /* check if type is valid */ - switch (a->type & 0xFF) - { - case cJSON_False: - case cJSON_True: - case cJSON_NULL: - case cJSON_Number: - case cJSON_String: - case cJSON_Raw: - case cJSON_Array: - case cJSON_Object: - break; - - default: - return false; - } - - /* identical objects are equal */ - if (a == b) - { - return true; - } - - switch (a->type & 0xFF) - { - /* in these cases and equal type is enough */ - case cJSON_False: - case cJSON_True: - case cJSON_NULL: - return true; - - case cJSON_Number: - if (compare_double(a->valuedouble, b->valuedouble)) - { - return true; - } - return false; - - case cJSON_String: - case cJSON_Raw: - if ((a->valuestring == NULL) || (b->valuestring == NULL)) - { - return false; - } - if (strcmp(a->valuestring, b->valuestring) == 0) - { - return true; - } - - return false; - - case cJSON_Array: - { - cJSON *a_element = a->child; - cJSON *b_element = b->child; - - for (; (a_element != NULL) && (b_element != NULL);) - { - if (!cJSON_Compare(a_element, b_element, case_sensitive)) - { - return false; - } - - a_element = a_element->next; - b_element = b_element->next; - } - - /* one of the arrays is longer than the other */ - if (a_element != b_element) - { - return false; - } - - return true; - } - - case cJSON_Object: - { - cJSON *a_element = NULL; - cJSON *b_element = NULL; - cJSON_ArrayForEach(a_element, a) - { - /* TODO This has O(n^2) runtime, which is horrible! */ - b_element = get_object_item(b, a_element->string, case_sensitive); - if (b_element == NULL) - { - return false; - } - - if (!cJSON_Compare(a_element, b_element, case_sensitive)) - { - return false; - } - } - - /* doing this twice, once on a and b to prevent true comparison if a subset of b - * TODO: Do this the proper way, this is just a fix for now */ - cJSON_ArrayForEach(b_element, b) - { - a_element = get_object_item(a, b_element->string, case_sensitive); - if (a_element == NULL) - { - return false; - } - - if (!cJSON_Compare(b_element, a_element, case_sensitive)) - { - return false; - } - } - - return true; - } - - default: - return false; - } -} - -CJSON_PUBLIC(void *) cJSON_malloc(size_t size) -{ - return global_hooks.allocate(size); -} - -CJSON_PUBLIC(void) cJSON_free(void *object) -{ - global_hooks.deallocate(object); -} \ No newline at end of file diff --git a/svf/lib/WPA/Andersen.cpp b/svf/lib/WPA/Andersen.cpp index e054f5d99..b8b436a06 100644 --- a/svf/lib/WPA/Andersen.cpp +++ b/svf/lib/WPA/Andersen.cpp @@ -6,16 +6,16 @@ // // This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU Affero General Public License as published by +// it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Affero General Public License for more details. +// GNU General Public License for more details. -// You should have received a copy of the GNU Affero General Public License +// You should have received a copy of the GNU General Public License // along with this program. If not, see . // //===----------------------------------------------------------------------===// @@ -28,59 +28,46 @@ */ #include "Util/Options.h" -#include "Graphs/CHG.h" -#include "Util/SVFUtil.h" -#include "MemoryModel/PointsTo.h" +#include "SVF-FE/LLVMUtil.h" #include "WPA/Andersen.h" -#include "WPA/Steensgaard.h" +#include "llvm/Transforms/Utils/BasicBlockUtils.h" + using namespace SVF; using namespace SVFUtil; -using namespace std; -u32_t AndersenBase::numOfProcessedAddr = 0; -u32_t AndersenBase::numOfProcessedCopy = 0; -u32_t AndersenBase::numOfProcessedGep = 0; -u32_t AndersenBase::numOfProcessedLoad = 0; -u32_t AndersenBase::numOfProcessedStore = 0; -u32_t AndersenBase::numOfSfrs = 0; -u32_t AndersenBase::numOfFieldExpand = 0; +Size_t AndersenBase::numOfProcessedAddr = 0; +Size_t AndersenBase::numOfProcessedCopy = 0; +Size_t AndersenBase::numOfProcessedGep = 0; +Size_t AndersenBase::numOfProcessedLoad = 0; +Size_t AndersenBase::numOfProcessedStore = 0; +Size_t AndersenBase::numOfSfrs = 0; +Size_t AndersenBase::numOfFieldExpand = 0; -u32_t AndersenBase::numOfSCCDetection = 0; +Size_t AndersenBase::numOfSCCDetection = 0; double AndersenBase::timeOfSCCDetection = 0; double AndersenBase::timeOfSCCMerges = 0; double AndersenBase::timeOfCollapse = 0; -u32_t AndersenBase::AveragePointsToSetSize = 0; -u32_t AndersenBase::MaxPointsToSetSize = 0; +Size_t AndersenBase::AveragePointsToSetSize = 0; +Size_t AndersenBase::MaxPointsToSetSize = 0; double AndersenBase::timeOfProcessCopyGep = 0; double AndersenBase::timeOfProcessLoadStore = 0; double AndersenBase::timeOfUpdateCallGraph = 0; /*! - * Destructor - */ -AndersenBase::~AndersenBase() -{ - delete consCG; - consCG = nullptr; -} - -/*! - * Initialize analysis + * Initilize analysis */ void AndersenBase::initialize() { - /// Build SVFIR + /// Build PAG PointerAnalysis::initialize(); - /// Create statistic class - stat = new AndersenStat(this); /// Build Constraint Graph consCG = new ConstraintGraph(pag); setGraph(consCG); - if (Options::ConsCGDotGraph()) - consCG->dump("consCG_initial"); + if (Options::ConsCGDotGraph) + consCG->dump("consCG_initial"); } /*! @@ -89,118 +76,105 @@ void AndersenBase::initialize() void AndersenBase::finalize() { /// dump constraint graph if PAGDotGraph flag is enabled - if (Options::ConsCGDotGraph()) - consCG->dump("consCG_final"); + if (Options::ConsCGDotGraph) + consCG->dump("consCG_final"); - if (Options::PrintCGGraph()) - consCG->print(); + if (Options::PrintCGGraph) + consCG->print(); BVDataPTAImpl::finalize(); } -void AndersenBase::solveConstraints() -{ - // Start solving constraints - DBOUT(DGENERAL, outs() << SVFUtil::pasMsg("Start Solving Constraints\n")); - - bool limitTimerSet = SVFUtil::startAnalysisLimitTimer(Options::AnderTimeLimit()); - - initWorklist(); - do - { - numOfIteration++; - if (0 == numOfIteration % iterationForPrintStat) - printStat(); - - reanalyze = false; - - solveWorklist(); - - if (updateCallGraph(getIndirectCallsites())) - reanalyze = true; - - } - while (reanalyze); - - // Analysis is finished, reset the alarm if we set it. - SVFUtil::stopAnalysisLimitTimer(limitTimerSet); - - DBOUT(DGENERAL, outs() << SVFUtil::pasMsg("Finish Solving Constraints\n")); -} - /*! * Andersen analysis */ void AndersenBase::analyze() { - if(!Options::ReadAnder().empty()) - { - readPtsFromFile(Options::ReadAnder()); + /// Initialization for the Solver + initialize(); + + bool readResultsFromFile = false; + if(!Options::ReadAnder.empty()) { + readResultsFromFile = this->readFromFile(Options::ReadAnder); + // Finalize the analysis + PointerAnalysis::finalize(); } - else + + if(!readResultsFromFile) { - if(Options::WriteAnder().empty()) - { - initialize(); - solveConstraints(); - finalize(); - } - else + // Start solving constraints + DBOUT(DGENERAL, outs() << SVFUtil::pasMsg("Start Solving Constraints\n")); + + bool limitTimerSet = SVFUtil::startAnalysisLimitTimer(Options::AnderTimeLimit); + + initWorklist(); + do { - solveAndwritePtsToFile(Options::WriteAnder()); + numOfIteration++; +// if (numOfIteration > 4) break; + if (0 == numOfIteration % iterationForPrintStat) + printStat(); + + reanalyze = false; + + solveWorklist(); + + if (updateCallGraph(getIndirectCallsites())) + reanalyze = true; + + /* + llvm::errs() << "Did one iteration\n"; + llvm::errs() << "Blacklisted edges and edges in constraint graph:\n"; + for (auto& tup: consCG->getBlackListEdges()) { + NodeID src = std::get<0>(tup); + NodeID dst = std::get<1>(tup); + llvm::errs() << "blacklist edge details: " << src << " : " << dst << "\n"; + if (consCG->hasConstraintNode(src) && consCG->hasConstraintNode(dst)) { + ConstraintNode* srcNode = consCG->getConstraintNode(src); + ConstraintNode* dstNode = consCG->getConstraintNode(dst); + llvm::errs() << "constraint node found\n"; + if (consCG->hasEdge(srcNode, dstNode, ConstraintEdge::Copy)) { + llvm::errs() << "----> blacklisted edge " << src << " " << dst << " reappeared!!!\n"; + } + } + } + */ } - } -} + while (reanalyze); -/*! - * Andersen analysis: read pointer analysis result from file - */ -void AndersenBase::readPtsFromFile(const std::string& filename) -{ - initialize(); - if (!filename.empty()) - this->readFromFile(filename); - finalize(); -} + // Analysis is finished, reset the alarm if we set it. + SVFUtil::stopAnalysisLimitTimer(limitTimerSet); -/*! - * Andersen analysis: solve constraints and write pointer analysis result to file - */ -void AndersenBase:: solveAndwritePtsToFile(const std::string& filename) -{ - /// Initialization for the Solver - initialize(); - if (!filename.empty()) - this->writeObjVarToFile(filename); - solveConstraints(); - if (!filename.empty()) - this->writeToFile(filename); - finalize(); + DBOUT(DGENERAL, outs() << SVFUtil::pasMsg("Finish Solving Constraints\n")); + + // Finalize the analysis + finalize(); + } + + if (!Options::WriteAnder.empty()) + this->writeToFile(Options::WriteAnder); } -void AndersenBase::cleanConsCG(NodeID id) -{ +void AndersenBase::cleanConsCG(NodeID id) { consCG->resetSubs(consCG->getRep(id)); for (NodeID sub: consCG->getSubs(id)) consCG->resetRep(sub); consCG->resetSubs(id); consCG->resetRep(id); - assert(!consCG->hasGNode(id) && "this is either a rep nodeid or a sub nodeid should have already been merged to its field-insensitive base! "); } void AndersenBase::normalizePointsTo() { - SVFIR::MemObjToFieldsMap &memToFieldsMap = pag->getMemToFieldsMap(); - SVFIR::NodeOffsetMap &GepObjVarMap = pag->getGepObjNodeMap(); + PAG::MemObjToFieldsMap &memToFieldsMap = pag->getMemToFieldsMap(); + PAG::NodeLocationSetMap &GepObjNodeMap = pag->getGepObjNodeMap(); - // clear GepObjVarMap/memToFieldsMap/nodeToSubsMap/nodeToRepMap + // clear GepObjNodeMap/memToFieldsMap/nodeToSubsMap/nodeToRepMap // for redundant gepnodes and remove those nodes from pag - for (NodeID n: redundantGepNodes) - { - NodeID base = pag->getBaseObjVar(n); - GepObjVar *gepNode = SVFUtil::dyn_cast(pag->getGNode(n)); + for (NodeID n: redundantGepNodes) { + NodeID base = pag->getBaseObjNode(n); + GepObjPN *gepNode = SVFUtil::dyn_cast(pag->getPAGNode(n)); assert(gepNode && "Not a gep node in redundantGepNodes set"); - const APOffset apOffset = gepNode->getConstantFieldIdx(); - GepObjVarMap.erase(std::make_pair(base, apOffset)); + const LocationSet ls = gepNode->getLocationSet(); + GepObjNodeMap.erase(std::make_pair(base, ls)); memToFieldsMap[base].reset(n); cleanConsCG(n); @@ -209,15 +183,14 @@ void AndersenBase::normalizePointsTo() } /*! - * Initialize analysis + * Initilize analysis */ void Andersen::initialize() { resetData(); + setDiffOpt(Options::PtsDiff); + setPWCOpt(Options::MergePWC); AndersenBase::initialize(); - - if (Options::ClusterAnder()) cluster(); - /// Initialize worklist processAllAddr(); } @@ -227,22 +200,10 @@ void Andersen::initialize() */ void Andersen::finalize() { - // TODO: check -stat too. - // TODO: broken - if (Options::ClusterAnder()) - { - Map stats; - const PTDataTy *ptd = getPTDataTy(); - // TODO: should we use liveOnly? - // TODO: parameterise final arg. - NodeIDAllocator::Clusterer::evaluate(*PointsTo::getCurrentBestNodeMapping(), ptd->getAllPts(true), stats, true); - NodeIDAllocator::Clusterer::printStats("post-main", stats); - } - /// sanitize field insensitive obj /// TODO: Fields has been collapsed during Andersen::collapseField(). // sanitizePts(); - AndersenBase::finalize(); + AndersenBase::finalize(); } /*! @@ -350,15 +311,15 @@ bool Andersen::processLoad(NodeID node, const ConstraintEdge* load) { /// TODO: New copy edges are also added for black hole obj node to /// make gcc in spec 2000 pass the flow-sensitive analysis. - /// Try to handle black hole obj in an appropriate way. -// if (pag->isBlkObjOrConstantObj(node)) - if (pag->isConstantObj(node) || pag->getGNode(load->getDstID())->isPointer() == false) + /// Try to handle black hole obj in an appropiate way. +// if (pag->isBlkObjOrConstantObj(node) || isNonPointerObj(node)) + if (pag->isConstantObj(node) || isNonPointerObj(node)) return false; numOfProcessedLoad++; NodeID dst = load->getDstID(); - return addCopyEdge(node, dst); + return addCopyEdge(node, dst, 1, const_cast(load)); } /*! @@ -370,15 +331,15 @@ bool Andersen::processStore(NodeID node, const ConstraintEdge* store) { /// TODO: New copy edges are also added for black hole obj node to /// make gcc in spec 2000 pass the flow-sensitive analysis. - /// Try to handle black hole obj in an appropriate way -// if (pag->isBlkObjOrConstantObj(node)) - if (pag->isConstantObj(node) || pag->getGNode(store->getSrcID())->isPointer() == false) + /// Try to handle black hole obj in an appropiate way +// if (pag->isBlkObjOrConstantObj(node) || isNonPointerObj(node)) + if (pag->isConstantObj(node) || isNonPointerObj(node)) return false; numOfProcessedStore++; NodeID src = store->getSrcID(); - return addCopyEdge(src, node); + return addCopyEdge(src, node, 1, const_cast(store)); } /*! @@ -394,6 +355,8 @@ bool Andersen::processCopy(NodeID node, const ConstraintEdge* edge) NodeID dst = edge->getDstID(); const PointsTo& srcPts = getDiffPts(node); + + bool changed = unionPts(dst, srcPts); if (changed) pushIntoWorklist(dst); @@ -412,6 +375,14 @@ bool Andersen::processGep(NodeID, const GepCGEdge* edge) return processGepPts(srcPts, edge); } +bool Andersen::canApplyPAInvariant(VariantGepCGEdge* vgepCGEdge, NodeID obj) { + if (vgepCGEdge->isStructTy() && consCG->isArrayTy(obj)) { + llvm::errs() << "Can't apply invariant: " << *(vgepCGEdge->getLLVMValue()) << "\n"; + return false; + } else { + return true; + } +} /*! * Compute points-to for gep edges */ @@ -420,28 +391,64 @@ bool Andersen::processGepPts(const PointsTo& pts, const GepCGEdge* edge) numOfProcessedGep++; PointsTo tmpDstPts; - if (SVFUtil::isa(edge)) + if (const VariantGepCGEdge* vgepCGEdge = SVFUtil::dyn_cast(edge)) { // If a pointer is connected by a variant gep edge, // then set this memory object to be field insensitive, // unless the object is a black hole/constant. + llvm::Value* llvmValue = vgepCGEdge->getLLVMValue(); for (NodeID o : pts) { if (consCG->isBlkObjOrConstantObj(o)) { tmpDstPts.set(o); continue; - } + } + + PAGNode* objNode = pag->getPAGNode(o); + + if (Options::InvariantVGEP /*&& !mustCollapse*/) { + // First of all, we believe that variable indices + // when the type is a complex type, are most definitely accessing + // an element in the array. + // GetElementPtrInst* vgep = vgepCGEdge->getLLVMValue(); + + // For the rest, we add the invariant + + if (consCG->isStructTy(o)) { + // We assume these don't happen + // We will add the invariant later + pag->addPtdForVarGep(vgepCGEdge->getLLVMValue(), o); + PAGNode* node = pag->getPAGNode(o); + continue; + } else if (consCG->isArrayTy(o)) { + LocationSet ls(0); + NodeID fieldSrcPtdNode = consCG->getGepObjNode(o, ls); + tmpDstPts.set(fieldSrcPtdNode); + } else { + /* + LocationSet ls(0); + NodeID fieldSrcPtdNode = consCG->getGepObjNode(o, ls); + tmpDstPts.set(fieldSrcPtdNode); + */ + // Add the field-insensitive node into pts. + NodeID baseId = consCG->getFIObjNode(o); + tmpDstPts.set(baseId); - if (!isFieldInsensitive(o)) - { - setObjFieldInsensitive(o); - consCG->addNodeToBeCollapsed(consCG->getBaseObjVar(o)); - } + } + } else { - // Add the field-insensitive node into pts. - NodeID baseId = consCG->getFIObjVar(o); - tmpDstPts.set(baseId); + if (!isFieldInsensitive(o)) + { + PAGNode* ptdNode = pag->getPAGNode(o); + setObjFieldInsensitive(o); + consCG->addNodeToBeCollapsed(consCG->getBaseObjNode(o)); + } + + // Add the field-insensitive node into pts. + NodeID baseId = consCG->getFIObjNode(o); + tmpDstPts.set(baseId); + } } } else if (const NormalGepCGEdge* normalGepEdge = SVFUtil::dyn_cast(edge)) @@ -449,16 +456,34 @@ bool Andersen::processGepPts(const PointsTo& pts, const GepCGEdge* edge) // TODO: after the node is set to field insensitive, handling invariant // gep edge may lose precision because offsets here are ignored, and the // base object is always returned. + + PAGNode* srcNode = pag->getPAGNode(normalGepEdge->getSrcID()); + + for (NodeID o : pts) { - if (consCG->isBlkObjOrConstantObj(o) || isFieldInsensitive(o)) + /* + if (Options::PreventCollapseExplosion) { + //llvm::errs() << "Doing GEP for nodeid: " << srcNode->getId() << "\n"; + if (srcNode->getDiffPtd().test(o)) { + PAGNode* obj = pag->getPAGNode(o); + llvm::errs() << "Must prevent explosion for :" << *obj << "\n"; + } + } + */ + if (consCG->isBlkObjOrConstantObj(o)) { tmpDstPts.set(o); continue; } - NodeID fieldSrcPtdNode = consCG->getGepObjVar(o, normalGepEdge->getAccessPath().getConstantStructFldIdx()); + if (!matchType(edge->getSrcID(), o, normalGepEdge)) continue; + + + NodeID fieldSrcPtdNode = consCG->getGepObjNode(o, normalGepEdge->getLocationSet()); + tmpDstPts.set(fieldSrcPtdNode); + addTypeForGepObjNode(fieldSrcPtdNode, normalGepEdge); } } else @@ -467,6 +492,62 @@ bool Andersen::processGepPts(const PointsTo& pts, const GepCGEdge* edge) } NodeID dstId = edge->getDstID(); + + if (Options::LogAll) { + llvm::errs() << "$$ ------------\n"; + NodeID src = edge->getSrcID(); + NodeID dst = edge->getDstID(); + + llvm::errs() << "$$ Solving gep edge between: " << src << " and " << dst << "\n"; + + PAGNode* srcNode = pag->getPAGNode(src); + PAGNode* dstNode = pag->getPAGNode(dst); + + if (srcNode->hasValue()) { + const Value* srcVal = srcNode->getValue(); + llvm::errs() << "$$ Src value: " << *srcVal << " : " << SVFUtil::getSourceLoc(srcVal) << "\n"; + } + + if (dstNode->hasValue()) { + const Value* dstVal = dstNode->getValue(); + llvm::errs() << "$$ Dst value: " << *dstVal << " : " << SVFUtil::getSourceLoc(dstVal) << "\n"; + } + + if (edge->getLLVMValue()) { + Value* gepVal = edge->getLLVMValue(); + if (Instruction* inst = SVFUtil::dyn_cast(gepVal)) { + llvm::errs() << "$$ Processing gep edge: [PRIMARY] : " << edge->getEdgeID() << " : " << *inst << " : " << inst->getFunction()->getName() << "\n"; + } else { + llvm::errs() << "$$ Processing gep edge: [PRIMARY] : " << edge->getEdgeID() << " : " << *gepVal << "\n"; + } + } else { + llvm::errs() << "$$ Processing gep edge: [PRIMARY] : " << edge->getEdgeID() << " : NO VALUE!\n"; + } + for (NodeBS::iterator nodeIt = tmpDstPts.begin(); nodeIt != tmpDstPts.end(); nodeIt++) { + NodeID ptd = *nodeIt; + PAGNode* pagNode = pag->getPAGNode(ptd); + int idx = -1; + if (GepObjPN* gepNode = SVFUtil::dyn_cast(pagNode)) { + idx = gepNode->getLocationSet().getOffset(); + } + if (pagNode->hasValue()) { + Value* ptdValue = const_cast(pagNode->getValue()); + if (Function* f = SVFUtil::dyn_cast(ptdValue)) { + llvm::errs() << "$$ PTD : " << ptd << " Function : " << f->getName() << " : " << SVFUtil::getSourceLoc(f) << "\n"; + } else if (Instruction* I = SVFUtil::dyn_cast(ptdValue)) { + + llvm::errs() << "$$ PTD : " << ptd << " Stack object: " << *I << " : " << idx << " : " << I->getFunction()->getName() << " : " << SVFUtil::getSourceLoc(I) << "\n"; + } else if (GlobalVariable* v = SVFUtil::dyn_cast(ptdValue)) { + llvm::errs() << "$$ PTD : " << ptd << " Global variable: " << *v << " : " << idx << " : " << *v << " : " << SVFUtil::getSourceLoc(v) << "\n"; + } + } else { + llvm::errs() << "$$ PTD : " << ptd << "PTD Dummy node: " << ptd << " : " << idx << "\n"; + } + + } + llvm::errs() << "$$ ------------\n"; + } + if (unionPts(dstId, tmpDstPts)) { pushIntoWorklist(dstId); @@ -481,10 +562,10 @@ bool Andersen::processGepPts(const PointsTo& pts, const GepCGEdge* edge) */ inline void Andersen::collapsePWCNode(NodeID nodeId) { - // If a node is a PWC node, collapse all its points-to target. + // If a node is a PWC node, collapse all its points-to tarsget. // collapseNodePts() may change the points-to set of the nodes which have been processed // before, in this case, we may need to re-do the analysis. - if (consCG->isPWCNode(nodeId) && collapseNodePts(nodeId)) + if (mergePWC() && consCG->isPWCNode(nodeId) && collapseNodePts(nodeId)) reanalyze = true; } @@ -492,7 +573,15 @@ inline void Andersen::collapseFields() { while (consCG->hasNodesToBeCollapsed()) { + //llvm::errs() << "Collapsing fields\n"; NodeID node = consCG->getNextCollapseNode(); + /* + if (consCG->isStructTy(node)) { + llvm::errs() << "Collapsing struct type object\n"; + } else if (consCG->isArrayTy(node)) { + llvm::errs() << "Collapsing array type object\n"; + } + */ // collapseField() may change the points-to set of the nodes which have been processed // before, in this case, we may need to re-do the analysis. if (collapseField(node)) @@ -526,6 +615,66 @@ void Andersen::mergeSccCycle() } } +/* +bool Andersen::addInvariant(ConstraintEdge* edge) { + ConstraintEdge* srcEdge = edge->getSourceEdge(); + if (LoadCGEdge* loadEdge = dyn_cast(srcEdge)) { + NodeID ptdID = edge->getSrcID(); + PAGNode* ptdNode = pag->getPAGNode(ptdID); + if (SVFUtil::isa(ptdNode)) { + return false; + } else if(isFieldInsensitive(ptdID)) { + return false; + } + + if (!ptdNode->hasValue()) { + llvm::errs() << "ptd doesn't have value\n"; + return false; + } + Value* ptdValue = const_cast(ptdNode->getValue()); + if (SVFUtil::isa(ptdValue)) { + return true; + } + if (instrumentInvariant(loadEdge->getLLVMValue(), ptdValue)) { + return true; + } + + } else if (StoreCGEdge* storeEdge = dyn_cast(srcEdge)) { + NodeID ptdID = edge->getDstID(); + PAGNode* ptdNode = pag->getPAGNode(ptdID); + if (SVFUtil::isa(ptdNode)) { + return false; + } else if(isFieldInsensitive(ptdID)) { + return false; + } + if (!ptdNode->hasValue()) { + llvm::errs() << "ptd doesn't have value\n"; + return false; + } + Value* ptdValue = const_cast(ptdNode->getValue()); + if (SVFUtil::isa(ptdValue)) { + return true; + } + if (instrumentInvariant(storeEdge->getLLVMValue(), ptdValue)) { + return true; + } + + } + return false; +} +*/ + +void Andersen::addCycleInvariants(CycleID pwcID, PAG::PWCList* gepNodesInSCC) { + pag->addPWCInvariants(pwcID, gepNodesInSCC); +} + +void Andersen::handlePointersAsPA(std::set* gepsInPWC) { + // TODO pick a vgep outside of a loop if possible + for (const llvm::Value* gep: *gepsInPWC) { + pag->addPtdForVarGep(gep, -1); + break; + } +} /** * Union points-to of subscc nodes into its rep nodes @@ -533,14 +682,90 @@ void Andersen::mergeSccCycle() */ void Andersen::mergeSccNodes(NodeID repNodeId, const NodeBS& subNodes) { + std::vector criticalGepEdges; + std::vector subPAGNodes; + bool treatAsPAInv = false; + bool inLoop = false; + bool hasStructType = false; + + + // Check if there's going to be a PWC + // If yes, then don't collapse the fields + // We still merge the node to the rep because we can't have cycles + // in AndersenWaveDiff algorithm. + + std::set cycleFuncSet; for (NodeBS::iterator nodeIt = subNodes.begin(); nodeIt != subNodes.end(); nodeIt++) { NodeID subNodeId = *nodeIt; - if (subNodeId != repNodeId) - { - mergeNodeToRep(subNodeId, repNodeId); + PAGNode* pagNode = pag->getPAGNode(subNodeId); + if (Options::DumpCycle) { + if (subNodes.count() > 1) { + if (pagNode->hasValue()) { + const Value* v = pagNode->getValue(); + if (const Instruction* inst = SVFUtil::dyn_cast(v)) { + llvm::errs() << "Node: " << *inst << " in function: " << inst->getParent()->getParent()->getName() << "\n"; + cycleFuncSet.insert(inst->getParent()->getParent()); + } else { + llvm::errs() << "Node: " << *(pagNode->getValue()) << "\n"; + } + } + } + } + + subPAGNodes.push_back(pagNode); + + if (subNodeId != repNodeId) { + mergeNodeToRep(subNodeId, repNodeId, criticalGepEdges); + } + } + + std::set* gepNodesInSCC = new std::set(); + if (criticalGepEdges.size() > 0) { + if (Options::InvariantPWC) { + // is a PWC + for (PAGNode* pagNode: subPAGNodes) { + if (pagNode->hasValue()) { + const Value* pagVal = pagNode->getValue(); + if (const Instruction* inst = SVFUtil::dyn_cast(pagVal)) { + BasicBlock* bb = const_cast(inst->getParent()); + Function* func = bb->getParent(); + // TODO handle the loop correctly + // I think this is the case where the PWC manifests as + // p = p + k + /* + if (svfLoopInfo->bbIsLoop(bb)) { + inLoop = true; + } + */ + if (const GetElementPtrInst* gepVal = SVFUtil::dyn_cast(inst)) { + Type* gepPtrTy = gepVal->getPointerOperand()->getType(); + while (SVFUtil::isa(gepPtrTy)) { + PointerType* pty = SVFUtil::dyn_cast(gepPtrTy); + gepPtrTy = pty->getPointerElementType(); + } + hasStructType = SVFUtil::isa(gepPtrTy); + + gepNodesInSCC->insert(gepVal); + } + } + + } + } + treatAsPAInv = inLoop && !hasStructType; + if (!treatAsPAInv) { + pwcCycleId++; + addCycleInvariants(pwcCycleId, gepNodesInSCC); + } else { + // We assume these can never point to structs, so no need to do a field sensitive analysis + handlePointersAsPA(gepNodesInSCC); + } + } else { + consCG->setPWCNode(repNodeId); } } + + delete(gepNodesInSCC); } /** @@ -548,6 +773,7 @@ void Andersen::mergeSccNodes(NodeID repNodeId, const NodeBS& subNodes) */ bool Andersen::collapseNodePts(NodeID nodeId) { + //llvm::errs() << "Collapsing node pts\n"; bool changed = false; const PointsTo& nodePts = getPts(nodeId); /// Points to set may be changed during collapse, so use a clone instead. @@ -583,9 +809,10 @@ bool Andersen::collapseField(NodeID nodeId) setObjFieldInsensitive(nodeId); // replace all occurrences of each field with the field-insensitive node - NodeID baseId = consCG->getFIObjVar(nodeId); + NodeID baseId = consCG->getFIObjNode(nodeId); NodeID baseRepNodeId = consCG->sccRepNode(baseId); - NodeBS & allFields = consCG->getAllFieldsObjVars(baseId); + NodeBS & allFields = consCG->getAllFieldsObjNode(baseId); + std::vector criticalGepEdges; for (NodeBS::iterator fieldIt = allFields.begin(), fieldEit = allFields.end(); fieldIt != fieldEit; fieldIt++) { NodeID fieldId = *fieldIt; @@ -604,13 +831,14 @@ bool Andersen::collapseField(NodeID nodeId) } // merge field node into base node, including edges and pts. NodeID fieldRepNodeId = consCG->sccRepNode(fieldId); - mergeNodeToRep(fieldRepNodeId, baseRepNodeId); - if (fieldId != baseRepNodeId) - { - // gep node fieldId becomes redundant if it is merged to its base node who is set as field-insensitive - // two node IDs should be different otherwise this field is actually the base and should not be removed. - redundantGepNodes.set(fieldId); - } + if (fieldRepNodeId != baseRepNodeId) + mergeNodeToRep(fieldRepNodeId, baseRepNodeId, criticalGepEdges); + /// TODO: I don't think we care about criticalGepEdges here. + /// We're any way turning the object into field sensitive + /// This isn't a PWC situation + + // collect each gep node whose base node has been set as field-insensitive + redundantGepNodes.set(fieldId); } } @@ -661,7 +889,7 @@ bool Andersen::updateCallGraph(const CallSiteToFunPtrMap& callsites) NodePairSet cpySrcNodes; /// nodes as a src of a generated new copy edge for(CallEdgeMap::iterator it = newEdges.begin(), eit = newEdges.end(); it!=eit; ++it ) { - CallSite cs = SVFUtil::getSVFCallSite(it->first->getCallSite()); + CallSite cs = SVFUtil::getLLVMCallSite(it->first->getCallSite()); for(FunctionSet::iterator cit = it->second.begin(), ecit = it->second.end(); cit!=ecit; ++cit) { connectCaller2CalleeParams(cs,*cit,cpySrcNodes); @@ -678,13 +906,241 @@ bool Andersen::updateCallGraph(const CallSiteToFunPtrMap& callsites) return (!newEdges.empty()); } +std::tuple Andersen::pickCycleEdgeToBreak(std::set& cycleEdges) { + ConstraintEdge* candidateEdge = nullptr; + for (ConstraintEdge* candidateEdge: cycleEdges) { + PAGNode* srcNode = pag->getPAGNode(candidateEdge->getSrcID()); + PAGNode* dstNode = pag->getPAGNode(candidateEdge->getDstID()); + Type* srcTy = nullptr; + Type* dstTy = nullptr; + + if (srcNode) { + srcTy = const_cast(srcNode->getType()); + } + if (dstNode) { + dstTy = const_cast(dstNode->getType()); + } + + if (srcTy == dstTy) + continue; + + /* + if (!Options::KaliBreakNullTypeEdges) { + if (!srcTy || !dstTy) { + //llvm::errs() << "srcTy = null? " << srcTy << " dstTy = null? " << dstTy << "\n"; + continue; + } + } + */ + + if (!candidateEdge->getSourceEdge()) { + continue; + } + + if (candidateEdge->getDerivedWeight() > 0) { + ConstraintEdge* srcEdge = candidateEdge->getSourceEdge(); + + LoadInst* loadMemInst = SVFUtil::dyn_cast(srcEdge->getLLVMValue()); + StoreInst* storeMemInst = SVFUtil::dyn_cast(srcEdge->getLLVMValue()); + + // We don't handle any edge other than load and store + if (!loadMemInst && !storeMemInst) { + continue; + } + + PAGNode* tgtPtdNode = nullptr; + if (LoadCGEdge* loadEdge = SVFUtil::dyn_cast(srcEdge)) { + NodeID ptdID = candidateEdge->getSrcID(); + tgtPtdNode = pag->getPAGNode(ptdID); + + } else if (StoreCGEdge* storeEdge = SVFUtil::dyn_cast(srcEdge)) { + NodeID ptdID = candidateEdge->getDstID(); + tgtPtdNode = pag->getPAGNode(ptdID); + } + + // TODO: Sometimes getting a ValPN here... why? + if (!SVFUtil::isa(tgtPtdNode)) { + continue; + } + + if (SVFUtil::isa(tgtPtdNode)) { + continue; + } else if(isFieldInsensitive(tgtPtdNode->getId())) { + continue; + } + + if (!tgtPtdNode->hasValue()) { + continue; + } + Value* tgtValue = const_cast(tgtPtdNode->getValue()); + + NodeID valID = (pag->hasValueNode(tgtValue)? pag->getValueNode(tgtValue): -1); + + if (SVFUtil::isa(tgtValue)) { + continue; + } + + Instruction* memInst = nullptr; + // Don't pick obvious edges. These are the ones that were created + // by the C -> IR transformations + if (loadMemInst) { + if (loadMemInst->getPointerOperand() == tgtValue) { + continue; + } + memInst = loadMemInst; + } else if (storeMemInst) { + if (storeMemInst->getPointerOperand() == tgtValue) { + continue; + } + memInst = storeMemInst; + } else { + assert(false && "What else?"); + } + + //llvm::errs() << "Returning edge " << candidateEdge->getSrcID() << " : " << candidateEdge->getDstID() << "\n"; + return std::make_tuple(candidateEdge, memInst, tgtValue); + + } + } + return std::make_tuple(nullptr, nullptr, nullptr); +} + +void Andersen::instrumentInvariant(Instruction* memoryInst, Value* target) { + // Some types + /* + LLVMContext& C = target->getContext(); + Type* voidPtrTy = PointerType::get(Type::getInt8Ty(C), 0); + IntegerType* i64Ty = IntegerType::get(C, 64); + + // We need to check the pointer operand of the memory instruction + // does not point to target + + // So first we record the last value of the address of the target (for + // stack targets), or the returned address for mallocs etc + + + Module* mod = memoryInst->getParent()->getParent()->getParent(); + int id = -1; + bool targetRecorded = false; + + if (valueToKaliIdMap.find(target) == valueToKaliIdMap.end()) { + // Give this guy a static id + id = kaliInvariantId++; + valueToKaliIdMap[target] = id; + kaliIdToValueMap[id] = target; + } else { + id = valueToKaliIdMap[target]; + targetRecorded = true; + } + + // Get the global map + GlobalVariable* kaliMapGVar = mod->getGlobalVariable("kaliMap"); + assert(kaliMapGVar && "can't find KaliMap"); + + // Get the index into the kaliMap + Constant* zero = ConstantInt::get(i64Ty, 0); + Constant* idConstant = ConstantInt::get(i64Ty, id); + std::vector idxVec; + idxVec.push_back(zero); + idxVec.push_back(idConstant); + llvm::ArrayRef idxs(idxVec); + + if (!targetRecorded) { + if (!SVFUtil::isa(target)) { + // TODO: Can also be a GEP + // Need to assess this carefully for both the source and target + assert(SVFUtil::isa(target) || SVFUtil::isa(target) || SVFUtil::isa(target) || SVFUtil::isa(target) && "what else can it be?"); + Instruction* targetInst = SVFUtil::dyn_cast(target); + + // Now instrument the stack allocation to store the address into the + // the global kaliMap + IRBuilder builder(targetInst->getNextNode()); + + // Get the index into the kaliMap + Value* gepIndexMapEntry = builder.CreateGEP(kaliMapGVar, idxs); + //Value* gepIndexAddress = builder.CreateGEP(gepIndexMapEntry, idConstant); + + // Cast the address to void* + Value* voidPtrAddress = builder.CreateBitCast(targetInst, voidPtrTy); + + // Do the store now! + StoreInst* storeInst = builder.CreateStore(voidPtrAddress, gepIndexMapEntry); + } else { + GlobalVariable* gvar = SVFUtil::dyn_cast(target); + + Function* mainFunction = mod->getFunction("main"); + Instruction* inst = mainFunction->getEntryBlock().getFirstNonPHIOrDbg(); + IRBuilder builder(inst); + + Value* gepIndexMapEntry = builder.CreateGEP(kaliMapGVar, idxs); + //Value* gepIndexAddress = builder.CreateGEP(gepIndexMapEntry, idConstant); + + // Cast the address to void* + Value* voidPtrAddress = builder.CreateBitCast(target, voidPtrTy); + + // Do the store now! + StoreInst* storeInst = builder.CreateStore(voidPtrAddress, gepIndexMapEntry); + inst->getParent()->dump(); + } + } + + + //llvm::errs() << "Target = " << *target << "\n"; + // IN case of a load, the memory instruction does not need to be + // instrumented any further + LoadInst* ldPtrInst = nullptr; + if (LoadInst* ldInst = SVFUtil::dyn_cast(memoryInst)) { + ldPtrInst = ldInst; + } else if (StoreInst* storeInst = SVFUtil::dyn_cast(memoryInst)) { + Value* ptr = storeInst->getPointerOperand(); + Instruction* storePtrInst = SVFUtil::dyn_cast(ptr); + assert(storePtrInst && "store pointer not inst?"); + IRBuilder builder1(storePtrInst->getNextNode()); + // llvm::errs() << "Adding instrumentation after store inst: " << *storePtrInst << " in function: " << storePtrInst->getParent()->getParent()->getName() << "\n"; + Type* loadTy = ptr->getType()->getPointerElementType(); + assert(loadTy && "Should be of pointer type"); + ldPtrInst = builder1.CreateLoad(loadTy, storePtrInst); + } else { + assert(false && "what is this instruction?"); + } + + IRBuilder builder(ldPtrInst->getNextNode()); + // Cast it to void* + Value* bitCastInst = builder.CreateBitCast(ldPtrInst, voidPtrTy); // <== 1 + + // Load the target value from the kaliMap + + Value* gepIndexMapEntry = builder.CreateGEP(kaliMapGVar, idxs); + Value* gepIndexAddress = builder.CreateGEP(gepIndexMapEntry, idConstant); + + LoadInst* targetLoadVal = builder.CreateLoad(voidPtrTy, gepIndexAddress); // <=== 2 + + // Compare 1 and 2 + Value* cmp = builder.CreateICmpEQ(bitCastInst, targetLoadVal); + Instruction* cmpInst = SVFUtil::dyn_cast(cmp); + assert(cmpInst && "Cmp must be instruction"); + + // Ah, now split the current basic block + BasicBlock* headBB = ldPtrInst->getParent(); + Instruction* termInst = llvm::SplitBlockAndInsertIfThen(cmpInst, cmpInst->getNextNode(), false); + + // Insert call to the switch view function + Function* switchViewFn = mod->getFunction("switch_view"); + IRBuilder switcherBuilder(termInst); + switcherBuilder.CreateCall(switchViewFn->getFunctionType(), switchViewFn); + */ + +} + void Andersen::heapAllocatorViaIndCall(CallSite cs, NodePairSet &cpySrcNodes) { assert(SVFUtil::getCallee(cs) == nullptr && "not an indirect callsite?"); - RetICFGNode* retBlockNode = pag->getICFG()->getRetICFGNode(cs.getInstruction()); + RetBlockNode* retBlockNode = pag->getICFG()->getRetBlockNode(cs.getInstruction()); const PAGNode* cs_return = pag->getCallSiteRet(retBlockNode); NodeID srcret; + // It matches it with the call-site CallSite2DummyValPN::const_iterator it = callsite2DummyValPN.find(cs); + if(it != callsite2DummyValPN.end()) { srcret = sccRepNode(it->second); @@ -698,6 +1154,20 @@ void Andersen::heapAllocatorViaIndCall(CallSite cs, NodePairSet &cpySrcNodes) consCG->addConstraintNode(new ConstraintNode(valNode),valNode); consCG->addConstraintNode(new ConstraintNode(objNode),objNode); srcret = valNode; + // Get the Call type + CallBase* callBase = cs.getInstruction(); + CallInst* heapCall = SVFUtil::dyn_cast(callBase); + + assert(heapCall && "Must be an instruction"); + if (heapCall->hasMetadata("annotation")) { + MDNode* mdNode = heapCall->getMetadata("annotation"); + MDString* tyAnnotStr = (MDString*)mdNode->getOperand(0).get(); + if (tyAnnotStr->getString() == "ArrayType") { + consCG->addArrayIndHeapCall(objNode); + } else if (tyAnnotStr->getString() == "StructType") { + consCG->addStructIndHeapCall(objNode); + } + } } NodeID dstrec = sccRepNode(cs_return->getId()); @@ -712,10 +1182,15 @@ void Andersen::connectCaller2CalleeParams(CallSite cs, const SVFFunction* F, Nod { assert(F); - DBOUT(DAndersen, outs() << "connect parameters from indirect callsite " << cs.getInstruction()->toString() << " to callee " << *F << "\n"); + DBOUT(DAndersen, outs() << "connect parameters from indirect callsite " << *cs.getInstruction() << " to callee " << *F << "\n"); + + CallBlockNode* callBlockNode = pag->getICFG()->getCallBlockNode(cs.getInstruction()); + RetBlockNode* retBlockNode = pag->getICFG()->getRetBlockNode(cs.getInstruction()); + + if (matchArgs(callBlockNode, F) == false) { + return; + } - CallICFGNode* callBlockNode = pag->getICFG()->getCallICFGNode(cs.getInstruction()); - RetICFGNode* retBlockNode = pag->getICFG()->getRetICFGNode(cs.getInstruction()); if(SVFUtil::isHeapAllocExtFunViaRet(F) && pag->callsiteHasRet(retBlockNode)) { @@ -745,13 +1220,13 @@ void Andersen::connectCaller2CalleeParams(CallSite cs, const SVFFunction* F, Nod { // connect actual and formal param - const SVFIR::SVFVarList& csArgList = pag->getCallSiteArgsList(callBlockNode); - const SVFIR::SVFVarList& funArgList = pag->getFunArgsList(F); + const PAG::PAGNodeList& csArgList = pag->getCallSiteArgsList(callBlockNode); + const PAG::PAGNodeList& funArgList = pag->getFunArgsList(F); //Go through the fixed parameters. DBOUT(DPAGBuild, outs() << " args:"); - SVFIR::SVFVarList::const_iterator funArgIt = funArgList.begin(), funArgEit = funArgList.end(); - SVFIR::SVFVarList::const_iterator csArgIt = csArgList.begin(), csArgEit = csArgList.end(); - for (; funArgIt != funArgEit; ++csArgIt, ++funArgIt) + PAG::PAGNodeList::const_iterator funArgIt = funArgList.begin(), funArgEit = funArgList.end(); + PAG::PAGNodeList::const_iterator csArgIt = csArgList.begin(), csArgEit = csArgList.end(); + for (; funArgIt != funArgEit && csArgIt != csArgEit; ++csArgIt, ++funArgIt) { //Some programs (e.g. Linux kernel) leave unneeded parameters empty. if (csArgIt == csArgEit) @@ -795,7 +1270,7 @@ void Andersen::connectCaller2CalleeParams(CallSite cs, const SVFFunction* F, Nod if(csArgIt != csArgEit) { writeWrnMsg("too many args to non-vararg func."); - writeWrnMsg("(" + cs.getInstruction()->getSourceLoc() + ")"); + writeWrnMsg("(" + getSourceLoc(cs.getInstruction()) + ")"); } } } @@ -803,42 +1278,114 @@ void Andersen::connectCaller2CalleeParams(CallSite cs, const SVFFunction* F, Nod /*! * merge nodeId to newRepId. Return true if the newRepId is a PWC node */ -bool Andersen::mergeSrcToTgt(NodeID nodeId, NodeID newRepId) +bool Andersen::mergeSrcToTgt(NodeID nodeId, NodeID newRepId, std::vector& criticalGepEdges) { if(nodeId==newRepId) return false; + /* + if (Options::PreventCollapseExplosion) { + const PointsTo& repPtd = getPts(newRepId); // rep node's points-to set + const PointsTo& nodePtd = getPts(nodeId); // the mergee node's points to set + PointsTo diffPtd = repPtd; + diffPtd.intersectWithComplement(nodePtd); // subtract to get the new points-to nodes + llvm::errs() << "Merging " << nodeId << " to rep " << newRepId << " with diffptd " << diffPtd.count () << "\n"; + PAGNode* repNode = pag->getPAGNode(newRepId); + PointsTo gepDiffPtd; + for (NodeID o: diffPtd) { + PAGNode* obj = pag->getPAGNode(o); + if (GepObjPN* gep = SVFUtil::dyn_cast(obj)) { + gepDiffPtd.set(o); + } + } + + repNode->updateDiffPtd(gepDiffPtd); + } + */ + /// union pts of node to rep updatePropaPts(newRepId, nodeId); unionPts(newRepId,nodeId); + if (Options::LogAll) { + llvm::errs() << "$$ --------\n"; + llvm::errs() << "$$ Merging " << nodeId << " to rep " << newRepId << "\n"; + const PointsTo& ptd = getPts(newRepId); + PAGNode* mergeeNode = pag->getPAGNode(nodeId); + PAGNode* repNode = pag->getPAGNode(newRepId); + + int idx = -1; + if (GepObjPN* gepNode = SVFUtil::dyn_cast(mergeeNode)) { + idx = gepNode->getLocationSet().getOffset(); + } + + if (mergeeNode->hasValue()) { + Value* ptrValue = const_cast(mergeeNode->getValue()); + if (Function* f = SVFUtil::dyn_cast(ptrValue)) { + llvm::errs() << "$$ MERGEE : " << nodeId << " Function : " << f->getName() << " : " << SVFUtil::getSourceLoc(f) << "\n"; + } else if (Instruction* I = SVFUtil::dyn_cast(ptrValue)) { + llvm::errs() << "$$ MERGEE : " << nodeId << " Stack object: " << *I << " : " << idx << " : " << I->getFunction()->getName() << SVFUtil::getSourceLoc(I) << "\n"; + + } else if (GlobalVariable* v = SVFUtil::dyn_cast(ptrValue)) { + llvm::errs() << "$$ MERGEE : " << nodeId << " Global variable: " << *v << " : " << idx << " : " << *v << " : " << SVFUtil::getSourceLoc(v) << "\n"; + } + } else { + llvm::errs() << "$$ MERGEE : " << nodeId << "MERGEE Dummy node: " << idx << "\n"; + } + + if (GepObjPN* gepNode = SVFUtil::dyn_cast(repNode)) { + idx = gepNode->getLocationSet().getOffset(); + } + if (repNode->hasValue()) { + Value* ptrValue = const_cast(repNode->getValue()); + if (Function* f = SVFUtil::dyn_cast(ptrValue)) { + llvm::errs() << "$$ REP: " << newRepId << " Function : " << f->getName() << "\n"; + } else if (Instruction* I = SVFUtil::dyn_cast(ptrValue)) { + llvm::errs() << "$$ REP: " << newRepId << " Stack object: " << *I << " : " << idx << " : " << I->getFunction()->getName() << " : " << SVFUtil::getSourceLoc(I) << "\n"; + } else if (GlobalVariable* v = SVFUtil::dyn_cast(ptrValue)) { + llvm::errs() << "$$ REP: " << newRepId << " Global variable: " << *v << " : " << idx << " : " << *v << " : " << SVFUtil::getSourceLoc(v) << "\n"; + } + } else { + llvm::errs() << "$$ REP: " << newRepId << "REP Dummy node: " << idx << "\n"; + } + llvm::errs() << "$$ --------\n"; + } + /// move the edges from node to rep, and remove the node ConstraintNode* node = consCG->getConstraintNode(nodeId); - bool pwc = consCG->moveEdgesToRepNode(node, consCG->getConstraintNode(newRepId)); - - /// 1. if find gep edges inside SCC cycle, the rep node will become a PWC node and - /// its pts should be collapsed later. - /// 2. if the node to be merged is already a PWC node, the rep node will also become - /// a PWC node as it will have a self-cycle gep edge. - if(node->isPWCNode()) - pwc = true; + bool gepInsideScc = consCG->moveEdgesToRepNode(node, consCG->getConstraintNode(newRepId), criticalGepEdges); /// set rep and sub relations updateNodeRepAndSubs(node->getId(),newRepId); consCG->removeConstraintNode(node); - return pwc; + return gepInsideScc; } + + /* * Merge a node to its rep node based on SCC detection */ -void Andersen::mergeNodeToRep(NodeID nodeId,NodeID newRepId) +void Andersen::mergeNodeToRep(NodeID nodeId,NodeID newRepId, std::vector& criticalGepEdges) { - - if (mergeSrcToTgt(nodeId,newRepId)) - consCG->setPWCNode(newRepId); + //llvm::errs() << "Merging node: " << nodeId << " to newRepId " << newRepId << "\n"; + ConstraintNode* node = consCG->getConstraintNode(nodeId); + bool gepInsideScc = mergeSrcToTgt(nodeId,newRepId, criticalGepEdges); + //llvm::errs() << "Found critical Gep inside SCC\n"; + /// We do this in mergeSccNodes + /// 1. if find gep edges inside SCC cycle, the rep node will become a PWC node and + /// its pts should be collapsed later. + /// 2. if the node to be merged is already a PWC node, the rep node will also become + /// a PWC node as it will have a self-cycle gep edge. + if (!Options::InvariantPWC) { + if (gepInsideScc || node->isPWCNode()) + consCG->setPWCNode(newRepId); + } + + // Record that these points-to objects are somewhat sketchy + pag->getImpactedByCollapseSet() |= getPts(nodeId); } /* @@ -862,25 +1409,6 @@ void Andersen::updateNodeRepAndSubs(NodeID nodeId, NodeID newRepId) consCG->resetSubs(nodeId); } -void Andersen::cluster(void) const -{ - assert(Options::MaxFieldLimit() == 0 && "Andersen::cluster: clustering for Andersen's is currently only supported in field-insensitive analysis"); - Steensgaard *steens = Steensgaard::createSteensgaard(pag); - std::vector> keys; - for (SVFIR::iterator pit = pag->begin(); pit != pag->end(); ++pit) - { - keys.push_back(std::make_pair(pit->first, 1)); - } - - std::vector>> candidates; - PointsTo::MappingPtr nodeMapping = - std::make_shared>(NodeIDAllocator::Clusterer::cluster(steens, keys, candidates, "aux-steens")); - PointsTo::MappingPtr reverseNodeMapping = - std::make_shared>(NodeIDAllocator::Clusterer::getReverseNodeMapping(*nodeMapping)); - - PointsTo::setCurrentBestNodeMapping(nodeMapping, reverseNodeMapping); -} - /*! * Print pag nodes' pts by an ascending order */ @@ -889,7 +1417,7 @@ void Andersen::dumpTopLevelPtsTo() for (OrderedNodeSet::iterator nIter = this->getAllValidPtrs().begin(); nIter != this->getAllValidPtrs().end(); ++nIter) { - const PAGNode* node = getPAG()->getGNode(*nIter); + const PAGNode* node = getPAG()->getPAGNode(*nIter); if (getPAG()->isValidTopLevelPtr(node)) { const PointsTo& pts = this->getPts(node->getId()); @@ -897,33 +1425,24 @@ void Andersen::dumpTopLevelPtsTo() if (pts.empty()) { - outs() << "\t\tPointsTo: {empty}\n"; + outs() << "\t\tPointsTo: {empty}\n\n"; } else { outs() << "\t\tPointsTo: { "; - multiset line; + multiset line; for (PointsTo::iterator it = pts.begin(), eit = pts.end(); it != eit; ++it) { line.insert(*it); } - for (multiset::const_iterator it = line.begin(); it != line.end(); ++it) - { - if(Options::PrintFieldWithBasePrefix()) - if (auto gepNode = SVFUtil::dyn_cast(pag->getGNode(*it))) - outs() << gepNode->getBaseNode() << "_" << gepNode->getConstantFieldIdx() << " "; - else - outs() << *it << " "; - else - outs() << *it << " "; - } - outs() << "}\n"; + for (multiset::const_iterator it = line.begin(); it != line.end(); ++it) + outs() << *it << " "; + outs() << "}\n\n"; } } } outs().flush(); } - diff --git a/svf/lib/WPA/AndersenHCD.cpp b/svf/lib/WPA/AndersenHCD.cpp new file mode 100644 index 000000000..32ef66a4f --- /dev/null +++ b/svf/lib/WPA/AndersenHCD.cpp @@ -0,0 +1,118 @@ +//===- AndersenHCD.cpp -- HCD based Field-sensitive Andersen's analysis-------------------// +// +// SVF: Static Value-Flow Analysis +// +// Copyright (C) <2013-2017> +// + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// +//===----------------------------------------------------------------------===// + +/* + * AndersenHCD.cpp + * + * Created on: 09 Oct. 2018 + * Author: Yuxiang Lei + */ + +#include "WPA/Andersen.h" + +using namespace SVF; +using namespace SVFUtil; + +AndersenHCD *AndersenHCD::hcdAndersen = nullptr; + + +// --- Methods of AndersenHCD --- + +/*! + * AndersenHCD initilizer, + * including initilization of PAG, constraint graph and offline constraint graph + */ +void AndersenHCD::initialize() +{ + Andersen::initialize(); + // Build offline constraint graph and solve its constraints + oCG = new OfflineConsG(pag); + OSCC* oscc = new OSCC(oCG); + oscc->find(); + oCG->solveOfflineSCC(oscc); + delete oscc; +} + +/*! + * AndersenHCD worklist solver + */ +void AndersenHCD::solveWorklist() +{ + while (!isWorklistEmpty()) + { + NodeID nodeId = popFromWorklist(); + collapsePWCNode(nodeId); + + //Merge detected offline SCC cycles + mergeSCC(nodeId); + + // Keep solving until workList is empty. + processNode(nodeId); + collapseFields(); + } +} + +/*! + * Collapse a node to its ref, if the ref exists + */ +void AndersenHCD::mergeSCC(NodeID nodeId) +{ + if (hasOfflineRep(nodeId)) + { + // get offline rep node + NodeID oRep = getOfflineRep(nodeId); + // get online rep node + NodeID rep = consCG->sccRepNode(oRep); + const PointsTo &pts = getPts(nodeId); + for (PointsTo::iterator ptIt = pts.begin(), ptEit = pts.end(); ptIt != ptEit; ++ptIt) + { + NodeID tgt = *ptIt; + ConstraintNode* tgtNode = consCG->getConstraintNode(tgt); + if (!tgtNode->getDirectInEdges().empty()) + continue; + if (tgtNode->getAddrOutEdges().size() > 1) + continue; + assert(!oCG->isaRef(tgt) && "Point-to target should not be a ref node!"); + mergeNodeAndPts(tgt, rep); + } + } +} + +/*! + * Merge node and its pts to the rep node + */ +void AndersenHCD::mergeNodeAndPts(NodeID node, NodeID rep) +{ + node = sccRepNode(node); + rep = sccRepNode(rep); + std::vector criticalGepEdges; + + if (!isaMergedNode(node)) + { + if (unionPts(rep, node)) + pushIntoWorklist(rep); + // Once a 'Node' is merged to its rep, it is collapsed, + // only its 'NodeID' remaining in the set 'subNodes' of its rep node. + mergeNodeToRep(node, rep, criticalGepEdges); + setMergedNode(node); + } +} diff --git a/svf/lib/WPA/AndersenHLCD.cpp b/svf/lib/WPA/AndersenHLCD.cpp new file mode 100644 index 000000000..d23da6159 --- /dev/null +++ b/svf/lib/WPA/AndersenHLCD.cpp @@ -0,0 +1,45 @@ +//===- AndersenHLCD.cpp -- HLCD based Field-sensitive Andersen's analysis-------------------// +// +// SVF: Static Value-Flow Analysis +// +// Copyright (C) <2013-2017> +// + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// +//===----------------------------------------------------------------------===// + +/* + * AndersenHLCD.cpp + * + * Created on: 02 Jun. 2019 + * Author: Yuxiang Lei + */ + +#include "WPA/Andersen.h" + +using namespace SVF; +using namespace SVFUtil; + +AndersenHLCD *AndersenHLCD::hlcdAndersen = nullptr; + + +/*! + * Collapse nodes and fields based on the result of both offline and online SCC detection + */ +void AndersenHLCD::mergeSCC(NodeID nodeId) +{ + AndersenHCD::mergeSCC(nodeId); + AndersenLCD::mergeSCC(); +} diff --git a/svf/lib/WPA/AndersenLCD.cpp b/svf/lib/WPA/AndersenLCD.cpp new file mode 100644 index 000000000..4b7d94d1b --- /dev/null +++ b/svf/lib/WPA/AndersenLCD.cpp @@ -0,0 +1,150 @@ +//===- AndersenLCD.cpp -- LCD based field-sensitive Andersen's analysis-------// +// +// SVF: Static Value-Flow Analysis +// +// Copyright (C) <2013-2017> +// + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// +//===----------------------------------------------------------------------===// + +/* + * AndersenLCD.cpp + * + * Created on: 13 Sep. 2018 + * Author: Yuxiang Lei + */ + +#include "WPA/Andersen.h" + +using namespace SVF; +using namespace SVFUtil; + +AndersenLCD* AndersenLCD::lcdAndersen = nullptr; + + +void AndersenLCD::solveWorklist() +{ + while (!isWorklistEmpty()) + { + // Merge detected SCC cycles + mergeSCC(); + + NodeID nodeId = popFromWorklist(); + collapsePWCNode(nodeId); + // Keep solving until workList is empty. + processNode(nodeId); + collapseFields(); + } +} + +/*! + * Process copy and gep edges + */ +void AndersenLCD::handleCopyGep(ConstraintNode* node) +{ + double propStart = stat->getClk(); + + NodeID nodeId = node->getId(); + computeDiffPts(nodeId); + + for (ConstraintEdge* edge : node->getCopyOutEdges()) + { + NodeID dstNodeId = edge->getDstID(); + const PointsTo& srcPts = getPts(nodeId); + const PointsTo& dstPts = getPts(dstNodeId); + // In one edge, if the pts of src node equals to that of dst node, and the edge + // is never met, push it into 'metEdges' and push the dst node into 'lcdCandidates' + if (!srcPts.empty() && srcPts == dstPts && !isMetEdge(edge)) + { + addMetEdge(edge); + addLCDCandidate((edge)->getDstID()); + } + processCopy(nodeId, edge); + } + for (ConstraintEdge* edge : node->getGepOutEdges()) + { + if (GepCGEdge* gepEdge = SVFUtil::dyn_cast(edge)) + processGep(nodeId, gepEdge); + } + + double propEnd = stat->getClk(); + timeOfProcessCopyGep += (propEnd - propStart) / TIMEINTERVAL; +} + +/*! + * Collapse nodes and fields based on 'lcdCandidates' + */ +void AndersenLCD::mergeSCC() +{ + if (hasLCDCandidate()) + { + SCCDetect(); + cleanLCDCandidate(); + } +} + +/*! + * AndersenLCD specified SCC detector, need to input a nodeStack 'lcdCandidate' + */ +NodeStack& AndersenLCD::SCCDetect() +{ + numOfSCCDetection++; + + NodeSet sccCandidates; + sccCandidates.clear(); + for (NodeSet::iterator it = lcdCandidates.begin(); it != lcdCandidates.end(); ++it) + if (sccRepNode(*it) == *it) + sccCandidates.insert(*it); + + double sccStart = stat->getClk(); + /// Detect SCC cycles + getSCCDetector()->find(sccCandidates); + double sccEnd = stat->getClk(); + timeOfSCCDetection += (sccEnd - sccStart) / TIMEINTERVAL; + + double mergeStart = stat->getClk(); + /// Merge SCC cycles + mergeSccCycle(); + double mergeEnd = stat->getClk(); + timeOfSCCMerges += (mergeEnd - mergeStart) / TIMEINTERVAL; + + return getSCCDetector()->topoNodeStack(); +} + +/*! + * merge nodeId to newRepId. Return true if the newRepId is a PWC node + */ +bool AndersenLCD::mergeSrcToTgt(NodeID nodeId, NodeID newRepId, std::vector& criticalGepEdges) +{ + if(nodeId==newRepId) + return false; + + /// union pts of node to rep + updatePropaPts(newRepId, nodeId); + unionPts(newRepId,nodeId); + pushIntoWorklist(newRepId); + + /// move the edges from node to rep, and remove the node + ConstraintNode* node = consCG->getConstraintNode(nodeId); + bool gepInsideScc = consCG->moveEdgesToRepNode(node, consCG->getConstraintNode(newRepId), criticalGepEdges); + + /// set rep and sub relations + updateNodeRepAndSubs(node->getId(),newRepId); + + consCG->removeConstraintNode(node); + + return gepInsideScc; +} diff --git a/svf/lib/WPA/AndersenSCD.cpp b/svf/lib/WPA/AndersenSCD.cpp index e3a14e9c8..ead8a9d59 100644 --- a/svf/lib/WPA/AndersenSCD.cpp +++ b/svf/lib/WPA/AndersenSCD.cpp @@ -6,16 +6,16 @@ // // This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU Affero General Public License as published by +// it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Affero General Public License for more details. +// GNU General Public License for more details. -// You should have received a copy of the GNU Affero General Public License +// You should have received a copy of the GNU General Public License // along with this program. If not, see . // //===----------------------------------------------------------------------===// @@ -27,13 +27,10 @@ * Author: Yuxiang Lei */ -#include "WPA/AndersenPWC.h" -#include "MemoryModel/PointsTo.h" -#include "Util/Options.h" +#include "WPA/AndersenSFR.h" using namespace SVF; using namespace SVFUtil; -using namespace std; AndersenSCD* AndersenSCD::scdAndersen = nullptr; @@ -108,11 +105,11 @@ NodeStack& AndersenSCD::SCCDetect() double mergeEnd = stat->getClk(); timeOfSCCMerges += (mergeEnd - mergeStart)/TIMEINTERVAL; - if (!Options::DetectPWC()) + if (!mergePWC()) { - sccStart = stat->getClk(); + double sccStart = stat->getClk(); PWCDetect(); - sccEnd = stat->getClk(); + double sccEnd = stat->getClk(); timeOfSCCDetection += (sccEnd - sccStart) / TIMEINTERVAL; } @@ -133,13 +130,13 @@ void AndersenSCD::PWCDetect() tmpSccCandidates.clear(); // set scc edge type as direct edge - bool pwcFlag = Options::DetectPWC(); - setDetectPWC(true); + ConstraintNode::SCCEdgeFlag f = ConstraintNode::sccEdgeFlag; + setSCCEdgeFlag(ConstraintNode::Direct); getSCCDetector()->find(sccCandidates); // reset scc edge type - setDetectPWC(pwcFlag); + setSCCEdgeFlag(f); } @@ -150,7 +147,7 @@ void AndersenSCD::handleCopyGep(ConstraintNode* node) { NodeID nodeId = node->getId(); - if (!Options::DetectPWC() && getSCCDetector()->subNodes(nodeId).count() > 1) + if (!mergePWC() && getSCCDetector()->subNodes(nodeId).count() > 1) processPWC(node); else if(isInWorklist(nodeId)) Andersen::handleCopyGep(node); @@ -281,7 +278,7 @@ bool AndersenSCD::updateCallGraph(const PointerAnalysis::CallSiteToFunPtrMap& ca NodePairSet cpySrcNodes; /// nodes as a src of a generated new copy edge for(CallEdgeMap::iterator it = newEdges.begin(), eit = newEdges.end(); it!=eit; ++it ) { - CallSite cs = SVFUtil::getSVFCallSite(it->first->getCallSite()); + CallSite cs = SVFUtil::getLLVMCallSite(it->first->getCallSite()); for(FunctionSet::iterator cit = it->second.begin(), ecit = it->second.end(); cit!=ecit; ++cit) { connectCaller2CalleeParams(cs,*cit,cpySrcNodes); diff --git a/svf/lib/WPA/AndersenSFR.cpp b/svf/lib/WPA/AndersenSFR.cpp index 3b9a31629..4a8ccc971 100644 --- a/svf/lib/WPA/AndersenSFR.cpp +++ b/svf/lib/WPA/AndersenSFR.cpp @@ -6,16 +6,16 @@ // // This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU Affero General Public License as published by +// it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Affero General Public License for more details. +// GNU General Public License for more details. -// You should have received a copy of the GNU Affero General Public License +// You should have received a copy of the GNU General Public License // along with this program. If not, see . // //===----------------------------------------------------------------------===// @@ -27,12 +27,10 @@ * Author: Yuxiang Lei */ -#include "WPA/AndersenPWC.h" -#include "MemoryModel/PointsTo.h" +#include "WPA/AndersenSFR.h" using namespace SVF; using namespace SVFUtil; -using namespace std; AndersenSFR *AndersenSFR::sfrAndersen = nullptr; @@ -42,12 +40,12 @@ AndersenSFR *AndersenSFR::sfrAndersen = nullptr; void AndersenSFR::initialize() { AndersenSCD::initialize(); - setDetectPWC(false); // SCC will detect only copy edges + setPWCOpt(false); if (!csc) - csc = new CSC(_graph, scc.get()); + csc = new CSC(_graph, scc); - /// Detect and collapse cycles consisting of only copy edges + // detect and collapse cycles that only comprise copy edges getSCCDetector()->find(); mergeSccCycle(); } @@ -66,7 +64,9 @@ void AndersenSFR::PWCDetect() /*! * */ -bool AndersenSFR::mergeSrcToTgt(NodeID nodeId, NodeID newRepId) +bool AndersenSFR::mergeSrcToTgt(NodeID nodeId, NodeID newRepId, + std::vector& criticalGepEdges + ) { ConstraintNode* node = consCG->getConstraintNode(nodeId); if (!node->strides.empty()) @@ -74,14 +74,14 @@ bool AndersenSFR::mergeSrcToTgt(NodeID nodeId, NodeID newRepId) ConstraintNode* newRepNode = consCG->getConstraintNode(newRepId); newRepNode->strides |= node->strides; } - return Andersen::mergeSrcToTgt(nodeId, newRepId); + return AndersenSCD::mergeSrcToTgt(nodeId, newRepId, criticalGepEdges); } /*! * Propagate point-to set via a gep edge, using SFR */ -bool AndersenSFR::processGepPts(const PointsTo& pts, const GepCGEdge* edge) +bool AndersenSFR::processGepPts(PointsTo& pts, const GepCGEdge* edge) { ConstraintNode* dst = edge->getDstNode(); NodeID dstId = dst->getId(); @@ -97,7 +97,7 @@ bool AndersenSFR::processGepPts(const PointsTo& pts, const GepCGEdge* edge) for (NodeID ptd : srcInits) sortSrcInits.insert(ptd); - APOffset offset = SVFUtil::dyn_cast(edge)->getConstantFieldIdx(); + Size_t offset = SVFUtil::dyn_cast(edge)->getOffset(); fieldExpand(sortSrcInits, offset, dst->strides, tmpDstPts); } @@ -115,9 +115,9 @@ bool AndersenSFR::processGepPts(const PointsTo& pts, const GepCGEdge* edge) /*! - * Expand field IDs in target pts based on the initials and offsets + * */ -void AndersenSFR::fieldExpand(NodeSet& initials, APOffset offset, NodeBS& strides, PointsTo& expandPts) +void AndersenSFR::fieldExpand(NodeSet& initials, Size_t offset, NodeBS& strides, PointsTo& expandPts) { numOfFieldExpand++; @@ -130,21 +130,18 @@ void AndersenSFR::fieldExpand(NodeSet& initials, APOffset offset, NodeBS& stride expandPts.set(init); else { - PAGNode* initPN = pag->getGNode(init); + PAGNode* initPN = pag->getPAGNode(init); const MemObj* obj = pag->getBaseObj(init); - const u32_t maxLimit = obj->getMaxFieldOffsetLimit(); - APOffset initOffset; - if (GepObjVar *gepNode = SVFUtil::dyn_cast(initPN)) - initOffset = gepNode->getConstantFieldIdx(); - else if (SVFUtil::isa(initPN)) + const Size_t maxLimit = obj->getMaxFieldOffsetLimit(); + Size_t initOffset; + if (GepObjPN *gepNode = SVFUtil::dyn_cast(initPN)) + initOffset = gepNode->getLocationSet().getOffset(); + else if (SVFUtil::isa(initPN) || SVFUtil::isa(initPN)) initOffset = 0; else - { assert(false && "Not an object node!!"); - abort(); - } - Set offsets; + Set offsets; offsets.insert(offset); // calculate offsets @@ -155,17 +152,17 @@ void AndersenSFR::fieldExpand(NodeSet& initials, APOffset offset, NodeBS& stride for (auto _f : offsets) for (auto _s : strides) { - APOffset _f1 = _f + _s; - loopFlag = (offsets.find(_f1) == offsets.end()) && ( (u32_t)(initOffset + _f1) < maxLimit); + Size_t _f1 = _f + _s; + loopFlag = (offsets.find(_f1) == offsets.end()) && (initOffset + _f1 < maxLimit); if (loopFlag) offsets.insert(_f1); } } // get gep objs - for (APOffset _f : offsets) + for (Size_t _f : offsets) { - NodeID gepId = consCG->getGepObjVar(init, _f); + NodeID gepId = consCG->getGepObjNode(init, LocationSet(_f)); initials.erase(gepId); // gep id in initials should be removed to avoid redundant derivation expandPts.set(gepId); } diff --git a/svf/lib/WPA/AndersenStat.cpp b/svf/lib/WPA/AndersenStat.cpp index 0b1c787a9..6c66ae5d6 100644 --- a/svf/lib/WPA/AndersenStat.cpp +++ b/svf/lib/WPA/AndersenStat.cpp @@ -6,16 +6,16 @@ // // This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU Affero General Public License as published by +// it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Affero General Public License for more details. +// GNU General Public License for more details. -// You should have received a copy of the GNU Affero General Public License +// You should have received a copy of the GNU General Public License // along with this program. If not, see . // //===----------------------------------------------------------------------===// @@ -27,13 +27,22 @@ * Author: Yulei Sui */ -#include "MemoryModel/PointerAnalysis.h" +#include "SVF-FE/LLVMUtil.h" #include "WPA/WPAStat.h" #include "WPA/Andersen.h" +// Headers for writing the output +#include +#include +#include +#include +#include +#include +#include +#include + using namespace SVF; using namespace SVFUtil; -using namespace std; u32_t AndersenStat::_MaxPtsSize = 0; u32_t AndersenStat::_NumOfCycles = 0; @@ -73,10 +82,13 @@ void AndersenStat::collectCycleInfo(ConstraintGraph* consCG) for (NodeBS::iterator it = subNodes.begin(), eit = subNodes.end(); it != eit; ++it) { NodeID nodeId = *it; - PAGNode* pagNode = pta->getPAG()->getGNode(nodeId); - if (SVFUtil::isa(pagNode) && pta->isFieldInsensitive(nodeId)) + if (!pta->getPAG()->hasPAGNode(nodeId)) { + continue; + } + PAGNode* pagNode = pta->getPAG()->getPAGNode(nodeId); + if (SVFUtil::isa(pagNode) && pta->isFieldInsensitive(nodeId)) { - NodeID baseId = consCG->getBaseObjVar(nodeId); + NodeID baseId = consCG->getBaseObjNode(nodeId); clone.reset(nodeId); clone.set(baseId); } @@ -121,15 +133,19 @@ void AndersenStat::constraintGraphStat() u32_t cgNodeNumber = 0; u32_t objNodeNumber = 0; u32_t addrtotalIn = 0; + u32_t addrtotalOut = 0; u32_t addrmaxIn = 0; u32_t addrmaxOut = 0; u32_t copytotalIn = 0; + u32_t copytotalOut = 0; u32_t copymaxIn = 0; u32_t copymaxOut = 0; u32_t loadtotalIn = 0; + u32_t loadtotalOut = 0; u32_t loadmaxIn = 0; u32_t loadmaxOut = 0; u32_t storetotalIn = 0; + u32_t storetotalOut = 0; u32_t storemaxIn = 0; u32_t storemaxOut = 0; @@ -141,7 +157,8 @@ void AndersenStat::constraintGraphStat() if(nodeIt->second->getInEdges().empty() && nodeIt->second->getOutEdges().empty()) continue; cgNodeNumber++; - if(SVFUtil::isa(pta->getPAG()->getGNode(nodeIt->first))) + if (!pta->getPAG()->hasPAGNode(nodeIt->first)) continue; + if(SVFUtil::isa(pta->getPAG()->getPAGNode(nodeIt->first))) objNodeNumber++; u32_t nCopyIn = nodeIt->second->getDirectInEdges().size(); @@ -151,6 +168,7 @@ void AndersenStat::constraintGraphStat() u32_t nCopyOut = nodeIt->second->getDirectOutEdges().size(); if(nCopyOut > copymaxOut) copymaxOut = nCopyOut; + copytotalOut +=nCopyOut; u32_t nLoadIn = nodeIt->second->getLoadInEdges().size(); if(nLoadIn > loadmaxIn) loadmaxIn = nLoadIn; @@ -158,6 +176,7 @@ void AndersenStat::constraintGraphStat() u32_t nLoadOut = nodeIt->second->getLoadOutEdges().size(); if(nLoadOut > loadmaxOut) loadmaxOut = nLoadOut; + loadtotalOut +=nLoadOut; u32_t nStoreIn = nodeIt->second->getStoreInEdges().size(); if(nStoreIn > storemaxIn) storemaxIn = nStoreIn; @@ -165,6 +184,7 @@ void AndersenStat::constraintGraphStat() u32_t nStoreOut = nodeIt->second->getStoreOutEdges().size(); if(nStoreOut > storemaxOut) storemaxOut = nStoreOut; + storetotalOut +=nStoreOut; u32_t nAddrIn = nodeIt->second->getAddrInEdges().size(); if(nAddrIn > addrmaxIn) addrmaxIn = nAddrIn; @@ -172,6 +192,7 @@ void AndersenStat::constraintGraphStat() u32_t nAddrOut = nodeIt->second->getAddrOutEdges().size(); if(nAddrOut > addrmaxOut) addrmaxOut = nAddrOut; + addrtotalOut +=nAddrOut; } double storeavgIn = (double)storetotalIn/cgNodeNumber; double loadavgIn = (double)loadtotalIn/cgNodeNumber; @@ -181,8 +202,8 @@ void AndersenStat::constraintGraphStat() PTNumStatMap["NumOfCGNode"] = totalNodeNumber; - PTNumStatMap["NumOfValidNode"] = cgNodeNumber; - PTNumStatMap["NumOfValidObjNode"] = objNodeNumber; + PTNumStatMap["TotalValidNode"] = cgNodeNumber; + PTNumStatMap["TotalValidObjNode"] = objNodeNumber; PTNumStatMap["NumOfCGEdge"] = consCG->getLoadCGEdges().size() + consCG->getStoreCGEdges().size() + numOfCopys + numOfGeps; PTNumStatMap["NumOfAddrs"] = consCG->getAddrCGEdges().size(); @@ -196,13 +217,15 @@ void AndersenStat::constraintGraphStat() PTNumStatMap["MaxOutLoadEdge"] = loadmaxOut; PTNumStatMap["MaxInStoreEdge"] = storemaxIn; PTNumStatMap["MaxOutStoreEdge"] = storemaxOut; + PTNumStatMap["AvgIn/OutStoreEdge"] = storeavgIn; PTNumStatMap["MaxInAddrEdge"] = addrmaxIn; PTNumStatMap["MaxOutAddrEdge"] = addrmaxOut; - timeStatMap["AvgIn/OutStoreEdge"] = storeavgIn; timeStatMap["AvgIn/OutCopyEdge"] = copyavgIn; timeStatMap["AvgIn/OutLoadEdge"] = loadavgIn; timeStatMap["AvgIn/OutAddrEdge"] = addravgIn; timeStatMap["AvgIn/OutEdge"] = avgIn; + + PTAStat::printStat("Constraint Graph Stats"); } /*! * Stat null pointers @@ -211,15 +234,15 @@ void AndersenStat::statNullPtr() { _NumOfNullPtr = 0; - for (SVFIR::iterator iter = pta->getPAG()->begin(), eiter = pta->getPAG()->end(); + for (PAG::iterator iter = pta->getPAG()->begin(), eiter = pta->getPAG()->end(); iter != eiter; ++iter) { NodeID pagNodeId = iter->first; PAGNode* pagNode = iter->second; - if (SVFUtil::isa(pagNode) == false) + if (pagNode->isTopLevelPtr() == false) continue; - SVFStmt::SVFStmtSetTy& inComingStore = pagNode->getIncomingEdges(SVFStmt::Store); - SVFStmt::SVFStmtSetTy& outGoingLoad = pagNode->getOutgoingEdges(SVFStmt::Load); + PAGEdge::PAGEdgeSetTy& inComingStore = pagNode->getIncomingEdges(PAGEdge::Store); + PAGEdge::PAGEdgeSetTy& outGoingLoad = pagNode->getOutgoingEdges(PAGEdge::Load); if (inComingStore.empty()==false || outGoingLoad.empty()==false) { ///TODO: change the condition here to fetch the points-to set @@ -233,11 +256,11 @@ void AndersenStat::statNullPtr() if(pts.empty()) { std::string str; - std::stringstream rawstr(str); - if (!SVFUtil::isa(pagNode) && !SVFUtil::isa(pagNode) ) + raw_string_ostream rawstr(str); + if (!SVFUtil::isa(pagNode) && !SVFUtil::isa(pagNode) ) { // if a pointer is in dead function, we do not care - if(pagNode->getValue()->ptrInUncalledFunction() == false) + if(isPtrInDeadFunction(pagNode->getValue()) == false) { _NumOfNullPtr++; rawstr << "##Null Pointer : (NodeID " << pagNode->getId() @@ -258,6 +281,23 @@ void AndersenStat::statNullPtr() } +void AndersenStat::performFieldSensitivityAwareExpansion(NodeBS& fiPts, const PointsTo& pts) { + ConstraintGraph* consCG = pta->getConstraintGraph(); + PAG* pag = pta->getPAG(); + + for (PointsTo::iterator piter = pts.begin(), epiter = pts.end(); + piter != epiter; ++piter) { + NodeID ptd = *piter; + if (pag->hasPAGNode(ptd)) { + // fiPts |= consCG->getAllFieldsObjNode(ptd); + fiPts.set(consCG->getBaseObjNode(ptd)); + // fiPts.insert(consCG->getBaseObjNode(ptd)); + } else { + fiPts.set(ptd); + } + } +} + /*! * Start here */ @@ -267,7 +307,7 @@ void AndersenStat::performStat() assert(SVFUtil::isa(pta) && "not an andersen pta pass!! what else??"); endClk(); - SVFIR* pag = pta->getPAG(); + PAG* pag = pta->getPAG(); ConstraintGraph* consCG = pta->getConstraintGraph(); // collect constraint graph cycles @@ -280,23 +320,172 @@ void AndersenStat::performStat() u32_t totalTopLevPointers = 0; u32_t totalPtsSize = 0; u32_t totalTopLevPtsSize = 0; - for (SVFIR::iterator iter = pta->getPAG()->begin(), eiter = pta->getPAG()->end(); + + + u32_t totalFieldPtsSize = 0; // treat each field as distinct + u32_t maxFieldPtsSize = 0; + + SVFModule* svfModule = pag->getModule(); + if (Options::DumpCFIStat) { + std::map> indCallMap; + std::map histogram; + + if (statDir.size() == 0) { + createStatDirectory(svfModule); + } + + const PTACallGraph* ptaCallGraph = pta->getPTACallGraph(); + const PTACallGraph::CallInstToCallGraphEdgesMap& cInstMap = ptaCallGraph->getCallInstToCallGraphEdgesMap(); + + for (auto it: cInstMap) { + const CallBlockNode* callBlockNode = it.first; + const Instruction* inst = callBlockNode->getCallSite(); + const CallInst* cInst = SVFUtil::dyn_cast(inst); + + assert(cInst && "If not callinst then what?"); + const Function* callerFun = cInst->getParent()->getParent(); + + const Function* defaultFn = callerFun; // TODO: Some callsites are having no targets + // This is a fix to prevent + // skewing because of that. + if (cInst->isIndirectCall()) { + llvm::errs() << "For a callsite in " << callerFun->getName() << " : \n"; + for (auto callGraphEdge: it.second) { + const SVFFunction* svfFun = callGraphEdge->getDstNode()->getFunction(); + Function* calledFunction = svfFun->getLLVMFun(); + llvm::errs() << " calls " << calledFunction->getName() << "\n"; + indCallMap[cInst].insert(calledFunction); + } + if (it.second.size() == 0) { + indCallMap[cInst].insert(callerFun); + } + } + } + + // Create the calltarget.csv file in the directory + std::string filename; + if (Options::InvariantPWC && Options::InvariantVGEP) { + filename = statDir + "/" + svfModule->getModuleIdentifier() + "-call-targets-full.csv"; + } else if (Options::InvariantPWC) { + filename = statDir + "/" + svfModule->getModuleIdentifier() + "-call-targets-pwc.csv"; + } else if (Options::InvariantVGEP) { + filename = statDir + "/" + svfModule->getModuleIdentifier() + "-call-targets-vgep.csv"; + } else { + filename = statDir + "/" + svfModule->getModuleIdentifier() + "-call-targets-default.csv"; + } + + std::ofstream call_targets_file(filename); + + for (auto it: indCallMap) { + const CallInst* cInst = it.first; + int sz = it.second.size(); + call_targets_file << sz << std::endl; + histogram[sz]++; + } + call_targets_file.close(); + + llvm::errs() << "EC Size:\t Ind. Call-sites\n"; + int totalTgts = 0; + int totalIndCallSites = 0; + for (auto it: histogram) { + llvm::errs() << it.first << " : " << it.second << "\n"; + totalIndCallSites += it.second; + totalTgts += it.first*it.second; + } + llvm::errs() << "Total Ind. Call-sites: " << totalIndCallSites << "\n"; + llvm::errs() << "Total Tgts: " << totalTgts << "\n"; + std::cerr << "Average CFI: " << std::fixed << (float)totalTgts / (float)totalIndCallSites << "\n"; + } + + NodeID maxPtsNodeID = -1; + int maxFields = 0; + NodeID maxFieldsNodeID = 0; + + // Create the calltarget.csv file in the directory + std::string filename; + if (Options::InvariantPWC && Options::InvariantVGEP) { + filename = statDir + "/" + svfModule->getModuleIdentifier() + "-ptd-targets-full.csv"; + } else if (Options::InvariantPWC) { + filename = statDir + "/" + svfModule->getModuleIdentifier() + "-ptd-targets-pwc.csv"; + } else if (Options::InvariantVGEP) { + filename = statDir + "/" + svfModule->getModuleIdentifier() + "-ptd-targets-vgep.csv"; + } else { + filename = statDir + "/" + svfModule->getModuleIdentifier() + "-ptd-targets-default.csv"; + } + std::ofstream ptd_file(filename); + + for (PAG::iterator iter = pta->getPAG()->begin(), eiter = pta->getPAG()->end(); iter != eiter; ++iter) { NodeID node = iter->first; const PointsTo& pts = pta->getPts(node); - u32_t size = pts.count(); + // Changing how we collect stats + NodeBS fiPts; + performFieldSensitivityAwareExpansion(fiPts, pts); + u32_t size = fiPts.count(); + + //u32_t size = pts.count(); totalPointers++; totalPtsSize+=size; + totalFieldPtsSize+= pts.count(); - if(pta->getPAG()->isValidTopLevelPtr(pta->getPAG()->getGNode(node))) + if(pta->getPAG()->isValidTopLevelPtr(pta->getPAG()->getPAGNode(node))) { totalTopLevPointers++; totalTopLevPtsSize+=size; + ptd_file << size << std::endl; } if(size > _MaxPtsSize ) _MaxPtsSize = size; + + if (pts.count() > maxFieldPtsSize) { + maxFieldPtsSize = pts.count(); + maxPtsNodeID = node; + } + if (pag->hasPAGNode(node)) { + PAGNode* pagNode = pag->getPAGNode(node); + if (SVFUtil::isa(pagNode)) { + NodeBS& fieldSize = consCG->getAllFieldsObjNode(node); + if (fieldSize.count() > maxFields) { + maxFields = fieldSize.count(); + maxFieldsNodeID = node; + } + } + } + } + ptd_file.close(); + + PAGNode* maxPtsNode = pag->getPAGNode(maxPtsNodeID); + if (maxPtsNode->hasValue()) { + llvm::errs() << "Max pts node: " << *(maxPtsNode->getValue()) << " " << (maxPtsNode->getFunction()? maxPtsNode->getFunction()->getName() : "") << "\n"; + } else { + llvm::errs() << "Max pts node: " << *maxPtsNode << "\n"; + } + + if (maxFieldsNodeID > 0 && pag->hasPAGNode(maxFieldsNodeID)) { + llvm::errs() << "Max fields info ---------------------- \n"; + PAGNode* maxFieldsNode = pag->getPAGNode(maxFieldsNodeID); + if (maxFieldsNode->hasValue()) { + llvm::errs() << "Max fields node: " << *(maxFieldsNode->getValue()) << " ... max fields: " << maxFields << "\n"; + if (const Instruction* heap = SVFUtil::dyn_cast(maxFieldsNode->getValue())) { + llvm::errs() << "Max fields node: heap object in function: " << heap->getParent()->getParent()->getName() << "\n"; + } + } else { + llvm::errs() << "Max fields node: " << *maxFieldsNode << " ... max fields: " << maxFields << "\n"; + } + if (GepObjPN* gepObj = SVFUtil::dyn_cast(maxFieldsNode)) { + NodeID parent = gepObj->getBaseNode(); + PAGNode* parentNode = pag->getPAGNode(parent); + llvm::errs() << "Max fields node: for gep node, parent id: " << parent << "\n"; + llvm::errs() << "Max fields node: for gep node, parend has type: " << parentNode->getNodeKind() << "\n"; + if (parentNode->hasValue()) { + llvm::errs() << "Max fields node: parent node has value: " << *(parentNode->getValue()) << "\n"; + } else { + llvm::errs() << "Max fields node: parent node has no value: \n"; + } + } + llvm::errs() << "----------------------------\n"; } @@ -304,52 +493,81 @@ void AndersenStat::performStat() constraintGraphStat(); - timeStatMap["TotalTime"] = (endTime - startTime)/TIMEINTERVAL; - timeStatMap["SCCDetectTime"] = Andersen::timeOfSCCDetection; - timeStatMap["SCCMergeTime"] = Andersen::timeOfSCCMerges; + timeStatMap[TotalAnalysisTime] = (endTime - startTime)/TIMEINTERVAL; + timeStatMap[SCCDetectionTime] = Andersen::timeOfSCCDetection; + timeStatMap[SCCMergeTime] = Andersen::timeOfSCCMerges; timeStatMap[CollapseTime] = Andersen::timeOfCollapse; - timeStatMap["LoadStoreTime"] = Andersen::timeOfProcessLoadStore; - timeStatMap["CopyGepTime"] = Andersen::timeOfProcessCopyGep; - timeStatMap["UpdateCGTime"] = Andersen::timeOfUpdateCallGraph; + timeStatMap[ProcessLoadStoreTime] = Andersen::timeOfProcessLoadStore; + timeStatMap[ProcessCopyGepTime] = Andersen::timeOfProcessCopyGep; + timeStatMap[UpdateCallGraphTime] = Andersen::timeOfUpdateCallGraph; + + PTNumStatMap[TotalNumOfPointers] = pag->getValueNodeNum() + pag->getFieldValNodeNum(); + PTNumStatMap[TotalNumOfObjects] = pag->getObjectNodeNum() + pag->getFieldObjNodeNum(); - PTNumStatMap["TotalPointers"] = pag->getValueNodeNum() + pag->getFieldValNodeNum(); - PTNumStatMap["TotalObjects"] = pag->getObjectNodeNum() + pag->getFieldObjNodeNum(); + PTNumStatMap[NumOfProcessedAddrs] = Andersen::numOfProcessedAddr; + PTNumStatMap[NumOfProcessedCopys] = Andersen::numOfProcessedCopy; + PTNumStatMap[NumOfProcessedGeps] = Andersen::numOfProcessedGep; + PTNumStatMap[NumOfProcessedLoads] = Andersen::numOfProcessedLoad; + PTNumStatMap[NumOfProcessedStores] = Andersen::numOfProcessedStore; - PTNumStatMap["AddrProcessed"] = Andersen::numOfProcessedAddr; - PTNumStatMap["CopyProcessed"] = Andersen::numOfProcessedCopy; - PTNumStatMap["GepProcessed"] = Andersen::numOfProcessedGep; - PTNumStatMap["LoadProcessed"] = Andersen::numOfProcessedLoad; - PTNumStatMap["StoreProcessed"] = Andersen::numOfProcessedStore; + PTNumStatMap[NumOfSfr] = Andersen::numOfSfrs; + PTNumStatMap[NumOfFieldExpand] = Andersen::numOfFieldExpand; - PTNumStatMap["NumOfSFRs"] = Andersen::numOfSfrs; - PTNumStatMap["NumOfFieldExpand"] = Andersen::numOfFieldExpand; + PTNumStatMap[NumOfPointers] = pag->getValueNodeNum(); + PTNumStatMap[NumOfMemObjects] = pag->getObjectNodeNum(); + PTNumStatMap[NumOfGepFieldPointers] = pag->getFieldValNodeNum(); + PTNumStatMap[NumOfGepFieldObjects] = pag->getFieldObjNodeNum(); - PTNumStatMap["Pointers"] = pag->getValueNodeNum(); - PTNumStatMap["MemObjects"] = pag->getObjectNodeNum(); - PTNumStatMap["DummyFieldPtrs"] = pag->getFieldValNodeNum(); - PTNumStatMap["FieldObjs"] = pag->getFieldObjNodeNum(); + timeStatMap[AveragePointsToSetSize] = (double)totalPtsSize/totalPointers;; + timeStatMap[AveragePointsToSetSizeFields] = (double)totalFieldPtsSize/totalPointers;; - timeStatMap["AvgPtsSetSize"] = (double)totalPtsSize/totalPointers;; - timeStatMap["AvgTopLvlPtsSize"] = (double)totalTopLevPtsSize/totalTopLevPointers;; + timeStatMap[AverageTopLevPointsToSetSize] = (double)totalTopLevPtsSize/totalTopLevPointers;; - PTNumStatMap["MaxPtsSetSize"] = _MaxPtsSize; + timeStatMap[MaxPointsToSetSizeFields] = (double)maxFieldPtsSize; - PTNumStatMap["SolveIterations"] = pta->numOfIteration; - PTNumStatMap["IndCallSites"] = consCG->getIndirectCallsites().size(); - PTNumStatMap["IndEdgeSolved"] = pta->getNumOfResolvedIndCallEdge(); + PTNumStatMap[MaxPointsToSetSize] = _MaxPtsSize; - PTNumStatMap["NumOfSCCDetect"] = Andersen::numOfSCCDetection; - PTNumStatMap["TotalCycleNum"] = _NumOfCycles; - PTNumStatMap["TotalPWCCycleNum"] = _NumOfPWCCycles; - PTNumStatMap["NodesInCycles"] = _NumOfNodesInCycles; - PTNumStatMap["MaxNodesInSCC"] = _MaxNumOfNodesInSCC; - PTNumStatMap["NullPointer"] = _NumOfNullPtr; + PTNumStatMap[NumOfIterations] = pta->numOfIteration; + + PTNumStatMap[NumOfIndirectCallSites] = consCG->getIndirectCallsites().size(); + PTNumStatMap[NumOfIndirectEdgeSolved] = pta->getNumOfResolvedIndCallEdge(); + + PTNumStatMap[NumOfSCCDetection] = Andersen::numOfSCCDetection; + PTNumStatMap[NumOfCycles] = _NumOfCycles; + PTNumStatMap[NumOfPWCCycles] = _NumOfPWCCycles; + PTNumStatMap[NumOfNodesInCycles] = _NumOfNodesInCycles; + PTNumStatMap[MaxNumOfNodesInSCC] = _MaxNumOfNodesInSCC; + PTNumStatMap[NumOfNullPointer] = _NumOfNullPtr; PTNumStatMap["PointsToConstPtr"] = _NumOfConstantPtr; PTNumStatMap["PointsToBlkPtr"] = _NumOfBlackholePtr; PTAStat::printStat("Andersen Pointer Analysis Stats"); } +// Function to create a directory with the given name +bool AndersenStat::createStatDirectory(SVFModule* svfMod) { + // Get current date and time + std::time_t t = std::time(nullptr); + std::tm tm = *std::localtime(&t); + + // Format the date and time + std::stringstream date_time_ss; + date_time_ss << "../full-results-dir_" + << std::setfill('0') << std::setw(2) << tm.tm_mon + 1 << "_" + << std::setfill('0') << std::setw(2) << tm.tm_mday << "_" + << std::setfill('0') << std::setw(2) << (tm.tm_year + 1900) % 100; + /* + << std::setfill('0') << std::setw(2) << tm.tm_hour << "_" + << std::setfill('0') << std::setw(2) << tm.tm_min << "_" + << std::setfill('0') << std::setw(2) << tm.tm_sec; + */ + + std::string directoryName = date_time_ss.str(); + + statDir = directoryName; + return mkdir(directoryName.c_str(), 0777) == 0; +} + diff --git a/svf/lib/WPA/AndersenWaveDiff.cpp b/svf/lib/WPA/AndersenWaveDiff.cpp index 539c0b689..bf6e11a7e 100644 --- a/svf/lib/WPA/AndersenWaveDiff.cpp +++ b/svf/lib/WPA/AndersenWaveDiff.cpp @@ -6,16 +6,16 @@ // // This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU Affero General Public License as published by +// it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Affero General Public License for more details. +// GNU General Public License for more details. -// You should have received a copy of the GNU Affero General Public License +// You should have received a copy of the GNU General Public License // along with this program. If not, see . // //===--------------------------------------------------------------------------------===// @@ -28,21 +28,28 @@ */ #include "WPA/Andersen.h" -#include "MemoryModel/PointsTo.h" using namespace SVF; using namespace SVFUtil; -using namespace std; AndersenWaveDiff* AndersenWaveDiff::diffWave = nullptr; -/*! - * Initialize - */ -void AndersenWaveDiff::initialize() -{ - Andersen::initialize(); - setDetectPWC(true); // Standard wave propagation always collapses PWCs +bool AndersenWaveDiff::matchType(NodeID ptrId, NodeID objId, const NormalGepCGEdge* gep) { + /* + if (Options::InvariantPWC) { + PAGNode* ptrNode = pag->getPAGNode(ptrId); + PAGNode* ptdNode = pag->getPAGNode(objId); + if (ptrNode->isPWCRepNode() || ptdNode->isPWCRepNode()) { + // This is a PWC rep node + // Then we compare types + if (ptrNode->hasValue() && ptdNode->hasValue() && ptrNode->getType() != ptdNode->getType()) { +// llvm::errs() << "Filtering " << *(ptrNode->getValue()) << " points to " << *(ptdNode->getValue()) << "\n"; + return false; + } + } + } + */ + return true; } /*! @@ -52,6 +59,7 @@ void AndersenWaveDiff::solveWorklist() { // Initialize the nodeStack via a whole SCC detection // Nodes in nodeStack are in topological order by default. + NodeStack& nodeStack = SCCDetect(); // Process nodeStack and put the changed nodes into workList. @@ -65,6 +73,27 @@ void AndersenWaveDiff::solveWorklist() collapseFields(); } + // This modification is to make WAVE feasible to handle PWC analysis + if (!mergePWC()) + { + NodeStack tmpWorklist; + while (!isWorklistEmpty()) + { + NodeID nodeId = popFromWorklist(); + collapsePWCNode(nodeId); + // process nodes in nodeStack + processNode(nodeId); + collapseFields(); + tmpWorklist.push(nodeId); + } + while (!tmpWorklist.empty()) + { + NodeID nodeId = tmpWorklist.top(); + tmpWorklist.pop(); + pushIntoWorklist(nodeId); + } + } + // New nodes will be inserted into workList during processing. while (!isWorklistEmpty()) { @@ -104,7 +133,10 @@ void AndersenWaveDiff::postProcessNode(NodeID nodeId) for (ConstraintNode::const_iterator it = node->outgoingLoadsBegin(), eit = node->outgoingLoadsEnd(); it != eit; ++it) { - if (handleLoad(nodeId, *it)) + // Why doesn't it do a pushIntoworklist? Probably because it's a wave diff and + // only solves the tree once? And has a separate node addition and solving phase + // But for copy edges it does the push-into worklist thing.. + if (handleLoad(nodeId, *it)) reanalyze = true; } // handle store @@ -119,6 +151,25 @@ void AndersenWaveDiff::postProcessNode(NodeID nodeId) timeOfProcessLoadStore += (insertEnd - insertStart) / TIMEINTERVAL; } +/*! + * Handle copy gep + */ +void AndersenWaveDiff::handleCopyGep(ConstraintNode* node) +{ + NodeID nodeId = node->getId(); + computeDiffPts(nodeId); + + if (!getDiffPts(nodeId).empty()) + { + for (ConstraintEdge* edge : node->getCopyOutEdges()) + if (CopyCGEdge* copyEdge = SVFUtil::dyn_cast(edge)) + processCopy(nodeId, copyEdge); + for (ConstraintEdge* edge : node->getGepOutEdges()) + if (GepCGEdge* gepEdge = SVFUtil::dyn_cast(edge)) + processGep(nodeId, gepEdge); + } +} + /*! * Handle load */ @@ -152,3 +203,117 @@ bool AndersenWaveDiff::handleStore(NodeID nodeId, const ConstraintEdge* edge) } return changed; } + +/*! + * Propagate diff points-to set from src to dst + */ +bool AndersenWaveDiff::processCopy(NodeID node, const ConstraintEdge* edge) +{ + numOfProcessedCopy++; + + bool changed = false; + assert((SVFUtil::isa(edge)) && "not copy/call/ret ??"); + NodeID dst = edge->getDstID(); + PointsTo& srcDiffPts = const_cast(getDiffPts(node)); + + + /* + if (Options::PreventCollapseExplosion) { + PAGNode* srcNode = pag->getPAGNode(node); + srcDiffPts.intersectWithComplement(srcNode->getDiffPtd()); + } + */ + + + if (Options::LogAll) { + llvm::errs() << "$$ ------------\n"; + NodeID src = edge->getSrcID(); + NodeID dst = edge->getDstID(); + + llvm::errs() << "$$ Solving copy edge between: " << src << " and " << dst << "\n"; + + PAGNode* srcNode = pag->getPAGNode(src); + PAGNode* dstNode = pag->getPAGNode(dst); + + if (srcNode->hasValue()) { + const Value* srcVal = srcNode->getValue(); + llvm::errs() << "$$ Src value: " << *srcVal << " : " << SVFUtil::getSourceLoc(srcVal) << "\n"; + } + + if (dstNode->hasValue()) { + const Value* dstVal = dstNode->getValue(); + llvm::errs() << "$$ Dst value: " << *dstVal << " : " << SVFUtil::getSourceLoc(dstVal) << "\n"; + } + + if (edge->getLLVMValue()) { + Value* copyVal = edge->getLLVMValue(); + if (Instruction* inst = SVFUtil::dyn_cast(copyVal)) { + llvm::errs() << "$$ Processing copy edge: [PRIMARY] : " << edge->getEdgeID() << " : " << *inst << " : " << inst->getFunction()->getName() << " : " << SVFUtil::getSourceLoc(inst) << "\n"; + } else { + llvm::errs() << "$$ Processing copy edge: [PRIMARY] : " << edge->getEdgeID() << " : " << *copyVal << "\n"; + } + } else { + llvm::errs() << "$$ Processing copy edge: [DERIVED/GLOBAL] : " << edge->getEdgeID() << " : " ; + if (edge->getSourceEdge()) { + llvm::errs() << edge->getSourceEdge()->getEdgeID() << " : "; + Value* sourceVal = edge->getSourceEdge()->getLLVMValue(); + if (sourceVal) { + if (Instruction* sourceInst = SVFUtil::dyn_cast(sourceVal)) { + llvm::errs() << *sourceInst << " : " << sourceInst->getFunction()->getName() << " : " << SVFUtil::getSourceLoc(sourceInst) << "\n"; + } else { + llvm::errs() << *sourceVal << " : " << SVFUtil::getSourceLoc(sourceVal) << "\n"; + } + } else { + llvm::errs() << "$$ NO SOURCE VAL\n"; + } + } else { + llvm::errs() << "$$ NO SOURCE EDGE\n"; + } + } + for (NodeBS::iterator nodeIt = srcDiffPts.begin(); nodeIt != srcDiffPts.end(); nodeIt++) { + NodeID ptd = *nodeIt; + PAGNode* pagNode = pag->getPAGNode(ptd); + int idx = -1; + if (GepObjPN* gepNode = SVFUtil::dyn_cast(pagNode)) { + idx = gepNode->getLocationSet().getOffset(); + } + if (pagNode->hasValue()) { + Value* ptdValue = const_cast(pagNode->getValue()); + if (Function* f = SVFUtil::dyn_cast(ptdValue)) { + llvm::errs() << "$$ PTD : " << ptd << " Function : " << f->getName() << "\n"; + } else if (Instruction* I = SVFUtil::dyn_cast(ptdValue)) { + llvm::errs() << "$$ PTD : " << ptd << " Stack object: " << *I << " : " << idx << " : " << I->getFunction()->getName() << "\n"; + } else if (GlobalVariable* v = SVFUtil::dyn_cast(ptdValue)) { + llvm::errs() << "$$ PTD : " << ptd << " Global variable: " << *v << " : " << idx << " : " << *v << "\n"; + } + } else { + llvm::errs() << "$$ PTD : " << ptd << "PTD Dummy node: " << ptd << " : " << idx << "\n"; + } + + } + } + processCast(edge); + if(unionPts(dst,srcDiffPts)) + { + changed = true; + pushIntoWorklist(dst); + } + + (const_cast(edge))->incrementSolvedCount(); + + return changed; +} + +/* + * Merge a node to its rep node + */ +void AndersenWaveDiff::mergeNodeToRep(NodeID nodeId,NodeID newRepId, std::vector& criticalGepEdges) +{ + if(nodeId==newRepId) + return; + + /// update rep's propagated points-to set + updatePropaPts(newRepId, nodeId); + + Andersen::mergeNodeToRep(nodeId, newRepId, criticalGepEdges); +} diff --git a/svf/lib/WPA/AndersenWaveDiffWithType.cpp b/svf/lib/WPA/AndersenWaveDiffWithType.cpp new file mode 100644 index 000000000..4c264cc22 --- /dev/null +++ b/svf/lib/WPA/AndersenWaveDiffWithType.cpp @@ -0,0 +1,157 @@ +#include "WPA/Andersen.h" +#include "MemoryModel/PTAType.h" + +using namespace SVF; +using namespace SVFUtil; + +AndersenWaveDiffWithType* AndersenWaveDiffWithType::diffWaveWithType = nullptr; + + +/// process "bitcast" CopyCGEdge +void AndersenWaveDiffWithType::processCast(const ConstraintEdge *edge) +{ + NodeID srcId = edge->getSrcID(); + NodeID dstId = edge->getDstID(); + if (pag->hasNonlabeledEdge(pag->getPAGNode(srcId), pag->getPAGNode(dstId), PAGEdge::Copy)) + { + const Value *val = pag->getIntraPAGEdge(srcId, dstId, PAGEdge::Copy)->getValue(); + if (val) + { + if (const CastInst *castInst = SVFUtil::dyn_cast(val)) + { + updateObjType(castInst->getType(), getPts(edge->getSrcID())); + } + else if (const ConstantExpr *ce = SVFUtil::dyn_cast(val)) + { + if (ce->getOpcode() == Instruction::BitCast) + { + updateObjType(ce->getType(), getPts(edge->getSrcID())); + } + } + } + } +} + +/// update type of objects when process "bitcast" CopyCGEdge +void AndersenWaveDiffWithType::updateObjType(const Type *type, const PointsTo &objs) +{ + for (PointsTo::iterator it = objs.begin(), eit = objs.end(); it != eit; ++it) + { + if (typeSystem->addTypeForVar(*it, type)) + { + typeSystem->addVarForType(*it, type); + processTypeMismatchedGep(*it, type); + } + } +} + +/// process mismatched gep edges +void AndersenWaveDiffWithType::processTypeMismatchedGep(NodeID obj, const Type *type) +{ + TypeMismatchedObjToEdgeTy::iterator it = typeMismatchedObjToEdges.find(obj); + if (it == typeMismatchedObjToEdges.end()) + return; + Set &edges = it->second; + Set processed; + + PTAType ptaTy(type); + NodeBS &nodesOfType = typeSystem->getVarsForType(ptaTy); + + for (Set::iterator nit = edges.begin(), neit = edges.end(); nit != neit; ++nit) + { + if (const NormalGepCGEdge *normalGepEdge = SVFUtil::dyn_cast(*nit)) + { + if (!nodesOfType.test(normalGepEdge->getSrcID())) + continue; + PointsTo tmpPts; + tmpPts.set(obj); + Andersen::processGepPts(tmpPts, normalGepEdge); + processed.insert(normalGepEdge); + } + } + + for (Set::iterator nit = processed.begin(), neit = processed.end(); nit != neit; ++nit) + edges.erase(*nit); +} + +/// match types for Gep Edges +bool AndersenWaveDiffWithType::matchType(NodeID ptrid, NodeID objid, const NormalGepCGEdge *normalGepEdge) +{ + if (!typeSystem->hasTypeSet(ptrid) || !typeSystem->hasTypeSet(objid)) + return true; + const TypeSet *ptrTypeSet = typeSystem->getTypeSet(ptrid); + const TypeSet *objTypeSet = typeSystem->getTypeSet(objid); + if (ptrTypeSet->intersect(objTypeSet)) + { + return true; + } + else + { + recordTypeMismatchedGep(objid, normalGepEdge); + return false; + } +} + +/// add type for newly created GepObjNode +void AndersenWaveDiffWithType::addTypeForGepObjNode(NodeID id, const NormalGepCGEdge* normalGepEdge) +{ + NodeID srcId = normalGepEdge->getSrcID(); + NodeID dstId = normalGepEdge->getDstID(); + if (pag->hasNonlabeledEdge(pag->getPAGNode(srcId), pag->getPAGNode(dstId), PAGEdge::NormalGep)) + { + const Value *val = pag->getIntraPAGEdge(srcId, dstId, PAGEdge::NormalGep)->getValue(); + if (val) + { + PTAType ptaTy(val->getType()); + if(typeSystem->addTypeForVar(id, ptaTy)) + typeSystem->addVarForType(id, ptaTy); + } + } +} + +NodeStack& AndersenWaveDiffWithType::SCCDetect() +{ + Andersen::SCCDetect(); + + /// merge types of nodes in SCC + const NodeBS &repNodes = getSCCDetector()->getRepNodes(); + for (NodeBS::iterator it = repNodes.begin(), eit = repNodes.end(); it != eit; ++it) + { + NodeBS subNodes = getSCCDetector()->subNodes(*it); + mergeTypeOfNodes(subNodes); + } + + return getSCCDetector()->topoNodeStack(); +} + +/// merge types of nodes in a cycle +void AndersenWaveDiffWithType::mergeTypeOfNodes(const NodeBS &nodes) +{ + + /// collect types in a cycle + OrderedSet typesInSCC; + for (NodeBS::iterator it = nodes.begin(), eit = nodes.end(); it != eit; ++it) + { + if (typeSystem->hasTypeSet(*it)) + { + const TypeSet *typeSet = typeSystem->getTypeSet(*it); + for (TypeSet::const_iterator tyit = typeSet->begin(), tyeit = typeSet->end(); tyit != tyeit; ++tyit) + { + const PTAType &ptaTy = *tyit; + typesInSCC.insert(ptaTy); + } + } + } + + /// merge types of nodes in a cycle + for (NodeBS::iterator it = nodes.begin(), eit = nodes.end(); it != eit; ++it) + { + for (OrderedSet::iterator tyit = typesInSCC.begin(), tyeit = typesInSCC.end(); tyit != tyeit; ++tyit) + { + const PTAType &ptaTy = *tyit; + if (typeSystem->addTypeForVar(*it, ptaTy)) + typeSystem->addVarForType(*it, ptaTy); + } + } + +} diff --git a/svf/lib/WPA/CSC.cpp b/svf/lib/WPA/CSC.cpp index 513f33776..1059a7947 100644 --- a/svf/lib/WPA/CSC.cpp +++ b/svf/lib/WPA/CSC.cpp @@ -6,16 +6,16 @@ // // This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU Affero General Public License as published by +// it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Affero General Public License for more details. +// GNU General Public License for more details. -// You should have received a copy of the GNU Affero General Public License +// You should have received a copy of the GNU General Public License // along with this program. If not, see . // //===----------------------------------------------------------------------===// @@ -73,7 +73,7 @@ void CSC::find(NodeStack& candidates) /*! * */ -void CSC::visit(NodeID nodeId, s32_t _w) +void CSC::visit(NodeID nodeId, Size_t _w) { // pwcReps[nodeId] = _scc->repNode(nodeId); setVisited(nodeId); @@ -85,9 +85,9 @@ void CSC::visit(NodeID nodeId, s32_t _w) ConstraintNode* node = _consG->getConstraintNode(nodeId); for (ConstraintNode::const_iterator eit = node->directOutEdgeBegin(); eit != node->directOutEdgeEnd(); ++eit) { - s32_t offset; + Size_t offset; if (NormalGepCGEdge* gepCGEdge = SVFUtil::dyn_cast(*eit)) - offset = gepCGEdge->getConstantFieldIdx(); + offset = gepCGEdge->getOffset(); else offset = 0; NodeID dstId = (*eit)->getDstID(); @@ -107,8 +107,8 @@ void CSC::visit(NodeID nodeId, s32_t _w) if (_consG->hasEdge(node, backNode, ConstraintEdge::NormalGep)) { NormalGepCGEdge* normalGep = SVFUtil::dyn_cast(_consG->getEdge(node, backNode, ConstraintEdge::NormalGep)); - s32_t _w = normalGep->getConstantFieldIdx(); - s32_t _l = _D[nodeId] +_w - _D[backNodeId]; + Size_t _w = normalGep->getLocationSet().getOffset(); + Size_t _l = _D[nodeId] +_w - _D[backNodeId]; backNode->strides.set(_l); for (auto cNodeId : _C) _consG->getConstraintNode(cNodeId)->strides.set(_l); @@ -116,7 +116,7 @@ void CSC::visit(NodeID nodeId, s32_t _w) else if (_consG->hasEdge(node, backNode, ConstraintEdge::VariantGep) || _consG->hasEdge(node, backNode, ConstraintEdge::Copy)) { - s32_t _l = _D[nodeId] - _D[backNodeId]; + Size_t _l = _D[nodeId] - _D[backNodeId]; backNode->strides.set(_l); for (auto cNodeId : _C) _consG->getConstraintNode(cNodeId)->strides.set(_l); diff --git a/svf/lib/WPA/Debugger.cpp b/svf/lib/WPA/Debugger.cpp new file mode 100644 index 000000000..2f9d539d5 --- /dev/null +++ b/svf/lib/WPA/Debugger.cpp @@ -0,0 +1,157 @@ +#include +#include "llvm/IR/InstIterator.h" +#include +#include "Util/Options.h" + +void Debugger::instrumentPointer(Instruction* inst) { + LLVMContext& C = inst->getContext(); + Type* longType = IntegerType::get(mod->getContext(), 64); + Type* intType = IntegerType::get(mod->getContext(), 32); + Type* ptrToLongType = PointerType::get(longType, 0); + + Value* pointer = nullptr; + + if (LoadInst* loadInst = SVFUtil::dyn_cast(inst)) { + pointer = loadInst; + } else if (StoreInst* storeInst = SVFUtil::dyn_cast(inst)) { + pointer = storeInst->getPointerOperand(); + } /*else if (AllocaInst* allocaInst = SVFUtil::dyn_cast(inst)) { + pointer = allocaInst; + } + */ + + NodeID nodeId = pag->getValueNode(inst); + bool isRelax = false; + + std::vector dbgTgtIDs; + for (PointsTo::iterator piter = _pta->getPts(nodeId).begin(), epiter = + _pta->getPts(nodeId).end(); piter != epiter; ++piter) { + NodeID ptd = *piter; + PAGNode* ptdNode = pag->getPAGNode(ptd); + + + if (ptdNode->hasValue()) { + Value* ptdVal = const_cast(ptdNode->getValue()); + if (ptdVal == pointer) continue; + + auto it = recorded.find(ptdVal); + if (it == recorded.end()) { + dbgTgtID++; + recorded[ptdVal] = dbgTgtID; + recordTarget(dbgTgtID, ptdVal, recordTargetFn); + } else { + dbgTgtID = it->second; + } + dbgTgtIDs.push_back(dbgTgtID); + } + if (SVFUtil::isa(ptdNode)) { + isRelax = true; + } + } + + IRBuilder Builder(inst->getNextNode()); + + // Call the ptdTargetCheckFn + + int len = dbgTgtIDs.size(); + Constant* clen = ConstantInt::get(longType, len); + + ArrayType* arrTy = ArrayType::get(longType, len); + + std::vector consIdVec; + + for (int i: dbgTgtIDs) { + consIdVec.push_back(ConstantInt::get(longType, i)); + } + + llvm::ArrayRef consIdArr(consIdVec); + + Constant* dbgIdArr = ConstantArray::get(arrTy, consIdArr); + + GlobalVariable* dbgArrGvar = new GlobalVariable(*mod, + /*Type=*/arrTy, + /*isConstant=*/true, + /*Linkage=*/GlobalValue::ExternalLinkage, + /*Initializer=*/0, // has initializer, specified below + /*Name=*/"cons id"); + dbgArrGvar->setInitializer(dbgIdArr); + + + Constant* Zero = ConstantInt::get(C, llvm::APInt(64, 0)); + llvm::ArrayRef zeroIdxs {Zero, Zero}; + Value* firstCons = ConstantExpr::getGetElementPtr(arrTy, dbgArrGvar, zeroIdxs); + + Value* arg1 = Builder.CreateBitOrPointerCast(pointer, ptrToLongType); + + Value* arg2 = clen; + Value* arg3 = Builder.CreateBitOrPointerCast(firstCons, ptrToLongType); + + Constant* arg4 = ConstantInt::get(longType, isRelax); + Builder.CreateCall(ptdTargetCheckFn, {arg1, arg2, arg3, arg4}); +} + +void Debugger::addFunctionDefs() { + Type* voidType = Type::getVoidTy(mod->getContext()); + Type* longType = IntegerType::get(mod->getContext(), 64); + Type* intType = IntegerType::get(mod->getContext(), 32); + + // Install the Record Routine + std::vector recordTypes; + recordTypes.push_back(intType); + recordTypes.push_back(longType); + + llvm::ArrayRef recordTypeArr(recordTypes); + + FunctionType* recordFnTy = FunctionType::get(voidType, recordTypeArr, false); + recordTargetFn = Function::Create(recordFnTy, Function::ExternalLinkage, "recordTarget", mod); + + svfMod->addFunctionSet(recordTargetFn); + // Install the ptdTargetCheck Routine + // ptdTargetCheck(unsigned long* valid_tgts, long len, unsigned long* tgt); + std::vector ptdTargetCheckType; + + Type* ptrToLongType = PointerType::get(longType, 0); + ptdTargetCheckType.push_back(ptrToLongType); + ptdTargetCheckType.push_back(longType); + ptdTargetCheckType.push_back(ptrToLongType); + ptdTargetCheckType.push_back(longType); + + llvm::ArrayRef ptdTargetCheckTypeArr(ptdTargetCheckType); + + FunctionType* ptdTargetCheckFnTy = FunctionType::get(intType, ptdTargetCheckTypeArr, false); + ptdTargetCheckFn = Function::Create(ptdTargetCheckFnTy, Function::ExternalLinkage, "checkPtr", mod); + + svfMod->addFunctionSet(ptdTargetCheckFn); +} + +void Debugger::init() { + dbgTgtID = 0; + addFunctionDefs(); + std::unordered_set DebugFuncNames(Options::DebugFuncsList.begin(), + Options::DebugFuncsList.end()); + + std::vector instList; + + for (Function& F: mod->getFunctionList()) { + if (std::find(DebugFuncNames.begin(), DebugFuncNames.end(), F.getName().str()) + != DebugFuncNames.end()) { + for (inst_iterator I = inst_begin(F), E = inst_end(F); I != E; ++I) { + Instruction* inst = &*I; + if (LoadInst* ldInst = SVFUtil::dyn_cast(inst)) { + if (inst->getType()->isPointerTy()) { + Value* ptr = ldInst->getPointerOperand(); + if (ptr->hasName() && ptr->getName().contains("argv")) { + continue; + } + instList.push_back(inst); + } + } + } + } + } + + for (Instruction* inst: instList) { + instrumentPointer(inst); + } +// mod->dump(); +} diff --git a/svf/lib/WPA/FlowSensitive.cpp b/svf/lib/WPA/FlowSensitive.cpp index 9d523c35d..f952f4543 100644 --- a/svf/lib/WPA/FlowSensitive.cpp +++ b/svf/lib/WPA/FlowSensitive.cpp @@ -6,16 +6,16 @@ // // This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU Affero General Public License as published by +// it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Affero General Public License for more details. +// GNU General Public License for more details. -// You should have received a copy of the GNU Affero General Public License +// You should have received a copy of the GNU General Public License // along with this program. If not, see . // //===----------------------------------------------------------------------===// @@ -28,16 +28,18 @@ */ #include "Util/Options.h" -#include "SVFIR/SVFModule.h" +#include "SVF-FE/DCHG.h" +#include "Util/SVFModule.h" +#include "Util/TypeBasedHeapCloning.h" #include "WPA/WPAStat.h" #include "WPA/FlowSensitive.h" #include "WPA/Andersen.h" -#include "MemoryModel/PointsTo.h" + using namespace SVF; using namespace SVFUtil; -std::unique_ptr FlowSensitive::fspta; +FlowSensitive* FlowSensitive::fspta = nullptr; /*! * Initialize analysis @@ -48,35 +50,26 @@ void FlowSensitive::initialize() stat = new FlowSensitiveStat(this); - // TODO: support clustered aux. Andersen's. - assert(!Options::ClusterAnder() && "FlowSensitive::initialize: clustering auxiliary Andersen's unsupported."); ander = AndersenWaveDiff::createAndersenWaveDiff(getPAG()); - - // If cluster option is not set, it will give us a no-mapping points-to set. - assert(!(Options::ClusterFs() && Options::PlainMappingFs()) - && "FS::init: plain-mapping and cluster-fs are mutually exclusive."); - if (Options::ClusterFs()) - { - cluster(); - // Reset the points-to cache although empty so the new mapping could - // be applied to the inserted empty set. - getPtCache().reset(); - } - else if (Options::PlainMappingFs()) - { - plainMap(); - // As above. - getPtCache().reset(); - } - - svfg = memSSA.buildPTROnlySVFG(ander); + // When evaluating ctir aliases, we want the whole SVFG. + if(Options::OPTSVFG) + svfg = Options::CTirAliasEval ? memSSA.buildFullSVFG(ander) : memSSA.buildPTROnlySVFG(ander); + else + svfg = memSSA.buildPTROnlySVFGWithoutOPT(ander); setGraph(svfg); //AndersenWaveDiff::releaseAndersenWaveDiff(); } -void FlowSensitive::solveConstraints() + +/*! + * Start analysis + */ +void FlowSensitive::analyze() { - bool limitTimerSet = SVFUtil::startAnalysisLimitTimer(Options::FsTimeLimit()); + bool limitTimerSet = SVFUtil::startAnalysisLimitTimer(Options::FsTimeLimit); + + /// Initialization for the Solver + initialize(); double start = stat->getClk(true); /// Start solving constraints @@ -104,55 +97,11 @@ void FlowSensitive::solveConstraints() double end = stat->getClk(true); solveTime += (end - start) / TIMEINTERVAL; -} - -/*! - * Start analysis - */ -void FlowSensitive::solveAndwritePtsToFile(const std::string& filename) -{ - /// Initialization for the Solver - initialize(); - if(!filename.empty()) - writeObjVarToFile(filename); - solveConstraints(); - if(!filename.empty()) - writeToFile(filename); - /// finalize the analysis - finalize(); -} - -/*! - * Start analysis - */ -void FlowSensitive::analyze() -{ - if(!Options::ReadAnder().empty()) - { - readPtsFromFile(Options::ReadAnder()); - } - else + if (Options::CTirAliasEval) { - if(Options::WriteAnder().empty()) - { - initialize(); - solveConstraints(); - finalize(); - } - else - { - solveAndwritePtsToFile(Options::WriteAnder()); - } + printCTirAliasStats(); } -} -void FlowSensitive::readPtsFromFile(const std::string& filename) -{ - /// Initialization for the Solver - initialize(); - /// Load the pts from file - if(!filename.empty()) - this->readFromFile(filename); /// finalize the analysis finalize(); } @@ -162,8 +111,8 @@ void FlowSensitive::readPtsFromFile(const std::string& filename) */ void FlowSensitive::finalize() { - if(Options::DumpVFG()) - svfg->dump("fs_solved", true); + if(Options::DumpVFG) + svfg->dump("fs_solved", true); NodeStack& nodeStack = WPASolver::SCCDetect(); while (nodeStack.empty() == false) @@ -180,27 +129,7 @@ void FlowSensitive::finalize() } } - // TODO: check -stat too. - if (Options::ClusterFs()) - { - Map stats; - const PTDataTy *ptd = getPTDataTy(); - // TODO: should we use liveOnly? - Map allPts = ptd->getAllPts(true); - // TODO: parameterise final arg. - NodeIDAllocator::Clusterer::evaluate(*PointsTo::getCurrentBestNodeMapping(), allPts, stats, true); - NodeIDAllocator::Clusterer::printStats("post-main: best", stats); - - // Do the same for the candidates. TODO: probably temporary for eval. purposes. - for (std::pair> &candidate : candidateMappings) - { - // Can reuse stats, since we're always filling it with `evaluate`, it will always be overwritten. - NodeIDAllocator::Clusterer::evaluate(candidate.second, allPts, stats, true); - NodeIDAllocator::Clusterer::printStats("post-main: candidate " + SVFUtil::hclustMethodToString(candidate.first), stats); - } - } - - BVDataPTAImpl::finalize(); + PointerAnalysis::finalize(); } /*! @@ -234,63 +163,61 @@ bool FlowSensitive::processSVFGNode(SVFGNode* node) { double start = stat->getClk(); bool changed = false; - if (AddrSVFGNode* addr = SVFUtil::dyn_cast(node)) + if(AddrSVFGNode* addr = SVFUtil::dyn_cast(node)) { numOfProcessedAddr++; - if (processAddr(addr)) + if(processAddr(addr)) changed = true; } - else if (CopySVFGNode* copy = SVFUtil::dyn_cast(node)) + else if(CopySVFGNode* copy = SVFUtil::dyn_cast(node)) { numOfProcessedCopy++; - if (processCopy(copy)) + if(processCopy(copy)) changed = true; } - else if (GepSVFGNode* gep = SVFUtil::dyn_cast(node)) + else if(GepSVFGNode* gep = SVFUtil::dyn_cast(node)) { numOfProcessedGep++; if(processGep(gep)) changed = true; } - else if (LoadSVFGNode* load = SVFUtil::dyn_cast(node)) + else if(LoadSVFGNode* load = SVFUtil::dyn_cast(node)) { numOfProcessedLoad++; if(processLoad(load)) changed = true; } - else if (StoreSVFGNode* store = SVFUtil::dyn_cast(node)) + else if(StoreSVFGNode* store = SVFUtil::dyn_cast(node)) { numOfProcessedStore++; - if (processStore(store)) + if(processStore(store)) changed = true; } - else if (PHISVFGNode* phi = SVFUtil::dyn_cast(node)) + else if(PHISVFGNode* phi = SVFUtil::dyn_cast(node)) { numOfProcessedPhi++; if (processPhi(phi)) changed = true; } - else if (SVFUtil::isa(node)) + else if(SVFUtil::isa(node) || SVFUtil::isa(node) + || SVFUtil::isa(node) || SVFUtil::isa(node) + || SVFUtil::isa(node)) { numOfProcessedMSSANode++; changed = true; } - else if (SVFUtil::isa(node)) + else if(SVFUtil::isa(node) || SVFUtil::isa(node) + || SVFUtil::isa(node) || SVFUtil::isa(node) + || SVFUtil::isa(node)) { changed = true; } - else if (SVFUtil::isa(node) || - SVFUtil::dyn_cast(node)) + else if(SVFUtil::isa(node) || SVFUtil::isa(node) || SVFUtil::dyn_cast(node)) { + } else - { assert(false && "unexpected kind of SVFG nodes"); - } double end = stat->getClk(); processTime += (end - start) / TIMEINTERVAL; @@ -399,8 +326,8 @@ bool FlowSensitive::propAlongIndirectEdge(const IndirectSVFGEdge* edge) // Get points-to targets may be used by next SVFG node. // Propagate points-to set for node used in dst. - const NodeBS& pts = edge->getPointsTo(); - for (NodeBS::iterator ptdIt = pts.begin(), ptdEit = pts.end(); ptdIt != ptdEit; ++ptdIt) + const PointsTo& pts = edge->getPointsTo(); + for (PointsTo::iterator ptdIt = pts.begin(), ptdEit = pts.end(); ptdIt != ptdEit; ++ptdIt) { NodeID ptd = *ptdIt; @@ -410,7 +337,7 @@ bool FlowSensitive::propAlongIndirectEdge(const IndirectSVFGEdge* edge) if (isFieldInsensitive(ptd)) { /// If this is a field-insensitive obj, propagate all field node's pts - const NodeBS& allFields = getAllFieldsObjVars(ptd); + const NodeBS& allFields = getAllFieldsObjNode(ptd); for (NodeBS::iterator fieldIt = allFields.begin(), fieldEit = allFields.end(); fieldIt != fieldEit; ++fieldIt) { @@ -454,7 +381,7 @@ bool FlowSensitive::processAddr(const AddrSVFGNode* addr) /// TODO: If this object has been set as field-insensitive, just /// add the insensitive object node into dst pointer's pts. if (isFieldInsensitive(srcID)) - srcID = getFIObjVar(srcID); + srcID = getFIObjNode(srcID); bool changed = addPts(addr->getPAGDstNodeID(), srcID); double end = stat->getClk(); addrTime += (end - start) / TIMEINTERVAL; @@ -504,8 +431,7 @@ bool FlowSensitive::processGep(const GepSVFGNode* edge) const PointsTo& srcPts = getPts(edge->getPAGSrcNodeID()); PointsTo tmpDstPts; - const GepStmt* gepStmt = SVFUtil::cast(edge->getPAGEdge()); - if (gepStmt->isVariantFieldGep()) + if (SVFUtil::isa(edge->getPAGEdge())) { for (NodeID o : srcPts) { @@ -516,23 +442,27 @@ bool FlowSensitive::processGep(const GepSVFGNode* edge) } setObjFieldInsensitive(o); - tmpDstPts.set(getFIObjVar(o)); + tmpDstPts.set(getFIObjNode(o)); } } - else + else if (const NormalGepPE* normalGep = SVFUtil::dyn_cast(edge->getPAGEdge())) { for (NodeID o : srcPts) { - if (isBlkObjOrConstantObj(o) || isFieldInsensitive(o)) + if (isBlkObjOrConstantObj(o)) { tmpDstPts.set(o); continue; } - NodeID fieldSrcPtdNode = getGepObjVar(o, gepStmt->getAccessPath().getConstantStructFldIdx()); + NodeID fieldSrcPtdNode = getGepObjNode(o, normalGep->getLocationSet()); tmpDstPts.set(fieldSrcPtdNode); } } + else + { + assert(false && "FlowSensitive::processGep: New type GEP edge type?"); + } if (unionPts(edge->getPAGDstNodeID(), tmpDstPts)) changed = true; @@ -557,34 +487,30 @@ bool FlowSensitive::processLoad(const LoadSVFGNode* load) NodeID dstVar = load->getPAGDstNodeID(); const PointsTo& srcPts = getPts(load->getPAGSrcNodeID()); - - // p = *q, the type of p must be a pointer - if(load->getPAGDstNode()->isPointer()) + for (PointsTo::iterator ptdIt = srcPts.begin(); ptdIt != srcPts.end(); ++ptdIt) { - for (PointsTo::iterator ptdIt = srcPts.begin(); ptdIt != srcPts.end(); ++ptdIt) - { - NodeID ptd = *ptdIt; + NodeID ptd = *ptdIt; - if (pag->isConstantObj(ptd)) - continue; + if (pag->isConstantObj(ptd) || pag->isNonPointerObj(ptd)) + continue; - if (unionPtsFromIn(load, ptd, dstVar)) - changed = true; + if (unionPtsFromIn(load, ptd, dstVar)) + changed = true; - if (isFieldInsensitive(ptd)) + if (isFieldInsensitive(ptd)) + { + /// If the ptd is a field-insensitive node, we should also get all field nodes' + /// points-to sets and pass them to pagDst. + const NodeBS& allFields = getAllFieldsObjNode(ptd); + for (NodeBS::iterator fieldIt = allFields.begin(), fieldEit = allFields.end(); + fieldIt != fieldEit; ++fieldIt) { - /// If the ptd is a field-insensitive node, we should also get all field nodes' - /// points-to sets and pass them to pagDst. - const NodeBS& allFields = getAllFieldsObjVars(ptd); - for (NodeBS::iterator fieldIt = allFields.begin(), fieldEit = allFields.end(); - fieldIt != fieldEit; ++fieldIt) - { - if (unionPtsFromIn(load, *fieldIt, dstVar)) - changed = true; - } + if (unionPtsFromIn(load, *fieldIt, dstVar)) + changed = true; } } } + double end = stat->getClk(); loadTime += (end - start) / TIMEINTERVAL; return changed; @@ -613,14 +539,13 @@ bool FlowSensitive::processStore(const StoreSVFGNode* store) double start = stat->getClk(); bool changed = false; - // *p = q, the type of q must be a pointer - if(getPts(store->getPAGSrcNodeID()).empty() == false && store->getPAGSrcNode()->isPointer()) + if(getPts(store->getPAGSrcNodeID()).empty() == false) { for (PointsTo::iterator it = dstPts.begin(), eit = dstPts.end(); it != eit; ++it) { NodeID ptd = *it; - if (pag->isConstantObj(ptd)) + if (pag->isConstantObj(ptd) || pag->isNonPointerObj(ptd)) continue; if (unionPtsFromTop(store, store->getPAGSrcNodeID(), ptd)) @@ -690,40 +615,6 @@ bool FlowSensitive::updateCallGraph(const CallSiteToFunPtrMap& callsites) CallEdgeMap newEdges; onTheFlyCallGraphSolve(callsites, newEdges); - // Bound the new edges by the Andersen's call graph. - // TODO: we want this to be an assertion eventually. - const CallEdgeMap &andersCallEdgeMap = ander->getIndCallMap(); - for (typename CallEdgeMap::value_type &csfs : newEdges) - { - const CallICFGNode *potentialCallSite = csfs.first; - FunctionSet &potentialFunctionSet = csfs.second; - - // Check this callsite even calls anything per Andersen's. - typename CallEdgeMap::const_iterator andersFunctionSetIt - = andersCallEdgeMap.find(potentialCallSite); - if (andersFunctionSetIt == andersCallEdgeMap.end()) - { - potentialFunctionSet.clear(); - } - - const FunctionSet &andersFunctionSet = andersFunctionSetIt->second; - for (FunctionSet::iterator potentialFunctionIt = potentialFunctionSet.begin(); - potentialFunctionIt != potentialFunctionSet.end(); ) - { - const SVFFunction *potentialFunction = *potentialFunctionIt; - if (andersFunctionSet.find(potentialFunction) == andersFunctionSet.end()) - { - // potentialFunction is not in the Andersen's call graph -- remove it. - potentialFunctionIt = potentialFunctionSet.erase(potentialFunctionIt); - } - else - { - // potentialFunction is in the Andersen's call graph -- keep it.. - ++potentialFunctionIt; - } - } - } - SVFGEdgeSetTy svfgEdges; connectCallerAndCallee(newEdges, svfgEdges); @@ -743,7 +634,7 @@ void FlowSensitive::connectCallerAndCallee(const CallEdgeMap& newEdges, SVFGEdge CallEdgeMap::const_iterator eiter = newEdges.end(); for (; iter != eiter; iter++) { - const CallICFGNode* cs = iter->first; + const CallBlockNode* cs = iter->first; const FunctionSet & functions = iter->second; for (FunctionSet::const_iterator func_iter = functions.begin(); func_iter != functions.end(); func_iter++) { @@ -759,8 +650,10 @@ void FlowSensitive::connectCallerAndCallee(const CallEdgeMap& newEdges, SVFGEdge */ void FlowSensitive::updateConnectedNodes(const SVFGEdgeSetTy& edges) { - for (const SVFGEdge* edge : edges) + for (SVFGEdgeSetTy::const_iterator it = edges.begin(), eit = edges.end(); + it != eit; ++it) { + const SVFGEdge* edge = *it; SVFGNode* dstNode = edge->getDstNode(); if (SVFUtil::isa(dstNode)) { @@ -768,7 +661,7 @@ void FlowSensitive::updateConnectedNodes(const SVFGEdgeSetTy& edges) /// node in next iteration pushIntoWorklist(dstNode->getId()); } - else if (SVFUtil::isa(dstNode)) + else if (SVFUtil::isa(dstNode) || SVFUtil::isa(dstNode)) { /// If this is a formal-in or actual-out node, we need to propagate points-to /// information from its predecessor node. @@ -776,8 +669,8 @@ void FlowSensitive::updateConnectedNodes(const SVFGEdgeSetTy& edges) SVFGNode* srcNode = edge->getSrcNode(); - const NodeBS& pts = SVFUtil::cast(edge)->getPointsTo(); - for (NodeBS::iterator ptdIt = pts.begin(), ptdEit = pts.end(); ptdIt != ptdEit; ++ptdIt) + const PointsTo& pts = SVFUtil::cast(edge)->getPointsTo(); + for (PointsTo::iterator ptdIt = pts.begin(), ptdEit = pts.end(); ptdIt != ptdEit; ++ptdIt) { NodeID ptd = *ptdIt; @@ -787,7 +680,7 @@ void FlowSensitive::updateConnectedNodes(const SVFGEdgeSetTy& edges) if (isFieldInsensitive(ptd)) { /// If this is a field-insensitive obj, propagate all field node's pts - const NodeBS& allFields = getAllFieldsObjVars(ptd); + const NodeBS& allFields = getAllFieldsObjNode(ptd); for (NodeBS::iterator fieldIt = allFields.begin(), fieldEit = allFields.end(); fieldIt != fieldEit; ++fieldIt) { @@ -822,35 +715,69 @@ bool FlowSensitive::propVarPtsAfterCGUpdated(NodeID var, const SVFGNode* src, co return false; } -void FlowSensitive::cluster(void) +void FlowSensitive::printCTirAliasStats(void) { - std::vector> keys; - for (const auto& pair : *pag) - keys.emplace_back(pair.first, 1); + DCHGraph *dchg = SVFUtil::dyn_cast(chgraph); + assert(dchg && "eval-ctir-aliases needs DCHG."); - PointsTo::MappingPtr nodeMapping = - std::make_shared>(NodeIDAllocator::Clusterer::cluster(ander, keys, candidateMappings, "aux-ander")); - PointsTo::MappingPtr reverseNodeMapping = - std::make_shared>(NodeIDAllocator::Clusterer::getReverseNodeMapping(*nodeMapping)); + // < SVFG node ID (loc), PAG node of interest (top-level pointer) >. + Set> cmpLocs; + for (SVFG::iterator npair = svfg->begin(); npair != svfg->end(); ++npair) + { + NodeID loc = npair->first; + SVFGNode *node = npair->second; - PointsTo::setCurrentBestNodeMapping(nodeMapping, reverseNodeMapping); -} + // Only care about loads, stores, and GEPs. + if (StmtSVFGNode *stmt = SVFUtil::dyn_cast(node)) + { + if (!SVFUtil::isa(stmt) && !SVFUtil::isa(stmt) + && !SVFUtil::isa(stmt)) + { + continue; + } -void FlowSensitive::plainMap(void) const -{ - assert(Options::NodeAllocStrat() == NodeIDAllocator::Strategy::DENSE - && "FS::cluster: plain mapping requires dense allocation strategy."); + if (!TypeBasedHeapCloning::getRawCTirMetadata(stmt->getInst() ? stmt->getInst() : stmt->getPAGEdge()->getValue())) + { + continue; + } - const size_t numObjects = NodeIDAllocator::get()->getNumObjects(); - PointsTo::MappingPtr plainMapping = std::make_shared>(numObjects); - PointsTo::MappingPtr reversePlainMapping = std::make_shared>(numObjects); - for (NodeID i = 0; i < plainMapping->size(); ++i) - { - plainMapping->at(i) = i; - reversePlainMapping->at(i) = i; + NodeID p = 0; + if (SVFUtil::isa(stmt)) + { + p = stmt->getPAGSrcNodeID(); + } + else if (SVFUtil::isa(stmt)) + { + p = stmt->getPAGDstNodeID(); + } + else if (SVFUtil::isa(stmt)) + { + p = stmt->getPAGSrcNodeID(); + } + else + { + // Not interested. + continue; + } + + cmpLocs.insert(std::make_pair(loc, p)); + } } - PointsTo::setCurrentBestNodeMapping(plainMapping, reversePlainMapping); + unsigned mayAliases = 0, noAliases = 0; + countAliases(cmpLocs, &mayAliases, &noAliases); + + unsigned total = mayAliases + noAliases; + llvm::outs() << "eval-ctir-aliases " + << total << " " + << mayAliases << " " + << noAliases << " " + << "\n"; + llvm::outs() << " " << "TOTAL : " << total << "\n" + << " " << "MAY : " << mayAliases << "\n" + << " " << "MAY % : " << 100 * ((double)mayAliases/(double)(total)) << "\n" + << " " << "NO : " << noAliases << "\n" + << " " << "NO % : " << 100 * ((double)noAliases/(double)(total)) << "\n"; } void FlowSensitive::countAliases(Set> cmp, unsigned *mayAliases, unsigned *noAliases) @@ -867,10 +794,10 @@ void FlowSensitive::countAliases(Set> cmp, unsigned *m switch (alias(p, q)) { - case AliasResult::NoAlias: + case llvm::NoAlias: ++(*noAliases); break; - case AliasResult::MayAlias: + case llvm::MayAlias: ++(*mayAliases); break; default: diff --git a/svf/lib/WPA/FlowSensitiveStat.cpp b/svf/lib/WPA/FlowSensitiveStat.cpp index ef98c4190..60937c2ba 100644 --- a/svf/lib/WPA/FlowSensitiveStat.cpp +++ b/svf/lib/WPA/FlowSensitiveStat.cpp @@ -6,16 +6,16 @@ // // This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU Affero General Public License as published by +// it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Affero General Public License for more details. +// GNU General Public License for more details. -// You should have received a copy of the GNU Affero General Public License +// You should have received a copy of the GNU General Public License // along with this program. If not, see . // //===-----------------------------------------------------------------------===// @@ -27,10 +27,10 @@ * Author: yesen */ +#include "SVF-FE/LLVMUtil.h" #include "WPA/Andersen.h" #include "WPA/WPAStat.h" #include "WPA/FlowSensitive.h" -#include "MemoryModel/PointsTo.h" using namespace SVF; using namespace SVFUtil; @@ -61,7 +61,7 @@ void FlowSensitiveStat::clearStat() _NumOfStoreSVFGNodesHaveInOut[i] = 0; _NumOfMSSAPhiSVFGNodesHaveInOut[i] = 0; - /// SVFIR nodes. + /// PAG nodes. _NumOfVarHaveINOUTPts[i] = 0; _NumOfVarHaveEmptyINOUTPts[i] = 0; _NumOfVarHaveINOUTPtsInFormalIn[i] = 0; @@ -88,7 +88,7 @@ void FlowSensitiveStat::performStat() clearStat(); - SVFIR* pag = fspta->getPAG(); + PAG* pag = fspta->getPAG(); // stat null ptr number statNullPtr(); @@ -102,14 +102,14 @@ void FlowSensitiveStat::performStat() u32_t fiObjNumber = 0; u32_t fsObjNumber = 0; Set nodeSet; - for (SVFIR::const_iterator nodeIt = pag->begin(), nodeEit = pag->end(); nodeIt != nodeEit; nodeIt++) + for (PAG::const_iterator nodeIt = pag->begin(), nodeEit = pag->end(); nodeIt != nodeEit; nodeIt++) { NodeID nodeId = nodeIt->first; PAGNode* pagNode = nodeIt->second; - if(SVFUtil::isa(pagNode)) + if(SVFUtil::isa(pagNode)) { const MemObj * memObj = pag->getBaseObj(nodeId); - SymID baseId = memObj->getId(); + SymID baseId = memObj->getSymId(); if (nodeSet.insert(baseId).second) { if (memObj->isFieldInsensitive()) @@ -120,15 +120,14 @@ void FlowSensitiveStat::performStat() } } - PTNumStatMap["FIObjNum"] = fiObjNumber; - PTNumStatMap["FSObjNum"] = fsObjNumber; - unsigned numOfCopy = 0; unsigned numOfStore = 0; + unsigned numOfNode = 0; SVFG::iterator svfgNodeIt = fspta->svfg->begin(); SVFG::iterator svfgNodeEit = fspta->svfg->end(); for (; svfgNodeIt != svfgNodeEit; ++svfgNodeIt) { + numOfNode++; SVFGNode* svfgNode = svfgNodeIt->second; if (SVFUtil::isa(svfgNode)) numOfCopy++; @@ -138,7 +137,7 @@ void FlowSensitiveStat::performStat() PTAStat::performStat(); - timeStatMap["TotalTime"] = (endTime - startTime)/TIMEINTERVAL; + timeStatMap[TotalAnalysisTime] = (endTime - startTime)/TIMEINTERVAL; timeStatMap["SolveTime"] = fspta->solveTime; timeStatMap["SCCTime"] = fspta->sccTime; timeStatMap["ProcessTime"] = fspta->processTime; @@ -154,22 +153,22 @@ void FlowSensitiveStat::performStat() timeStatMap["UpdateCGTime"] = fspta->updateCallGraphTime; timeStatMap["PhiTime"] = fspta->phiTime; - PTNumStatMap["TotalPointers"] = pag->getValueNodeNum() + pag->getFieldValNodeNum(); - PTNumStatMap["TotalObjects"] = pag->getObjectNodeNum() + pag->getFieldObjNodeNum(); + PTNumStatMap[TotalNumOfPointers] = pag->getValueNodeNum() + pag->getFieldValNodeNum(); + PTNumStatMap[TotalNumOfObjects] = pag->getObjectNodeNum() + pag->getFieldObjNodeNum(); - PTNumStatMap["Pointers"] = pag->getValueNodeNum(); - PTNumStatMap["MemObjects"] = pag->getObjectNodeNum(); - PTNumStatMap["DummyFieldPtrs"] = pag->getFieldValNodeNum(); - PTNumStatMap["FieldObjs"] = pag->getFieldObjNodeNum(); + PTNumStatMap[NumOfPointers] = pag->getValueNodeNum(); + PTNumStatMap[NumOfMemObjects] = pag->getObjectNodeNum(); + PTNumStatMap[NumOfGepFieldPointers] = pag->getFieldValNodeNum(); + PTNumStatMap[NumOfGepFieldObjects] = pag->getFieldObjNodeNum(); - PTNumStatMap["CopysNum"] = numOfCopy; - PTNumStatMap["StoresNum"] = numOfStore; + PTNumStatMap[NumOfCopys] = numOfCopy; + PTNumStatMap[NumOfStores] = numOfStore; - PTNumStatMap["SolveIterations"] = fspta->numOfIteration; + PTNumStatMap[NumOfIterations] = fspta->numOfIteration; - PTNumStatMap["IndEdgeSolved"] = fspta->getNumOfResolvedIndCallEdge(); + PTNumStatMap[NumOfIndirectEdgeSolved] = fspta->getNumOfResolvedIndCallEdge(); - PTNumStatMap["NullPointer"] = _NumOfNullPtr; + PTNumStatMap[NumOfNullPointer] = _NumOfNullPtr; PTNumStatMap["PointsToConstPtr"] = _NumOfConstantPtr; PTNumStatMap["PointsToBlkPtr"] = _NumOfBlackholePtr; @@ -201,7 +200,7 @@ void FlowSensitiveStat::performStat() PTNumStatMap["PHI_SNodesHaveOUT"] = _NumOfMSSAPhiSVFGNodesHaveInOut[OUT]; /*-----------------------------------------------------*/ - // SVFIR nodes. + // PAG nodes. PTNumStatMap["VarHaveIN"] = _NumOfVarHaveINOUTPts[IN]; PTNumStatMap["VarHaveOUT"] = _NumOfVarHaveINOUTPts[OUT]; @@ -264,19 +263,20 @@ void FlowSensitiveStat::performStat() timeStatMap["AverageSCCSize"] = (fspta->numOfSCC == 0) ? 0 : ((double)fspta->numOfNodesInSCC / fspta->numOfSCC); - PTAStat::printStat("Flow-Sensitive Pointer Analysis Statistics"); + std::cout << "\n****Flow-Sensitive Pointer Analysis Statistics****\n"; + PTAStat::printStat(); } void FlowSensitiveStat::statNullPtr() { _NumOfNullPtr = 0; - for (SVFIR::iterator iter = fspta->getPAG()->begin(), eiter = fspta->getPAG()->end(); + for (PAG::iterator iter = fspta->getPAG()->begin(), eiter = fspta->getPAG()->end(); iter != eiter; ++iter) { NodeID pagNodeId = iter->first; PAGNode* pagNode = iter->second; - SVFStmt::SVFStmtSetTy& inComingStore = pagNode->getIncomingEdges(SVFStmt::Store); - SVFStmt::SVFStmtSetTy& outGoingLoad = pagNode->getOutgoingEdges(SVFStmt::Load); + PAGEdge::PAGEdgeSetTy& inComingStore = pagNode->getIncomingEdges(PAGEdge::Store); + PAGEdge::PAGEdgeSetTy& outGoingLoad = pagNode->getOutgoingEdges(PAGEdge::Load); if (inComingStore.empty()==false || outGoingLoad.empty()==false) { ///TODO: change the condition here to fetch the points-to set @@ -292,11 +292,11 @@ void FlowSensitiveStat::statNullPtr() if(pts.empty()) { std::string str; - std::stringstream rawstr(str); - if (!SVFUtil::isa(pagNode) && !SVFUtil::isa(pagNode)) + raw_string_ostream rawstr(str); + if (!SVFUtil::isa(pagNode) && !SVFUtil::isa(pagNode)) { // if a pointer is in dead function, we do not care - if(pagNode->getValue()->ptrInUncalledFunction() == false) + if(isPtrInDeadFunction(pagNode->getValue()) == false) { _NumOfNullPtr++; rawstr << "##Null Pointer : (NodeID " << pagNode->getId() @@ -332,7 +332,7 @@ void FlowSensitiveStat::statPtsSize() /// get points-to set size information for top-level pointers. u32_t totalValidTopLvlPointers = 0; u32_t topTopLvlPtsSize = 0; - for (SVFIR::iterator iter = fspta->getPAG()->begin(), eiter = fspta->getPAG()->end(); + for (PAG::iterator iter = fspta->getPAG()->begin(), eiter = fspta->getPAG()->end(); iter != eiter; ++iter) { NodeID node = iter->first; @@ -389,13 +389,13 @@ void FlowSensitiveStat::statInOutPtsSize(const DFInOutMap& data, ENUM_INOUT inOr /*-----------------------------------------------------*/ - // Count SVFIR nodes and their points-to set size. + // Count PAG nodes and their points-to set size. const PtsMap& cptsMap = it->second; PtsMap::const_iterator ptsIt = cptsMap.begin(); PtsMap::const_iterator ptsEit = cptsMap.end(); for (; ptsIt != ptsEit; ++ptsIt) { - if (ptsIt->second.empty()) + if (ptsIt->second.empty()) { _NumOfVarHaveEmptyINOUTPts[inOrOut]++; continue; diff --git a/svf/lib/WPA/FlowSensitiveTBHC.cpp b/svf/lib/WPA/FlowSensitiveTBHC.cpp new file mode 100644 index 000000000..e7262163f --- /dev/null +++ b/svf/lib/WPA/FlowSensitiveTBHC.cpp @@ -0,0 +1,696 @@ +//===- FlowSensitiveTBHC.cpp -- flow-sensitive type filter ------------// + +/* + * FlowSensitiveTBHC.cpp + * + * Created on: Oct 08, 2019 + * Author: Mohamad Barbar + */ + +#include "Util/Options.h" +#include "SVF-FE/DCHG.h" +#include "SVF-FE/CPPUtil.h" +#include "WPA/FlowSensitiveTBHC.h" +#include "WPA/WPAStat.h" +#include "WPA/Andersen.h" + +using namespace SVF; + +FlowSensitiveTBHC::FlowSensitiveTBHC(PAG* _pag, PTATY type) : FlowSensitive(_pag, type), TypeBasedHeapCloning(this) +{ + // Using `this` as the argument for TypeBasedHeapCloning is okay. As PointerAnalysis, it's + // already constructed. TypeBasedHeapCloning also doesn't use pta in the constructor so it + // just needs to be allocated, which it is. + allReuse = Options::TBHCAllReuse; + storeReuse = allReuse || Options::TBHCStoreReuse; +} + +void FlowSensitiveTBHC::analyze() +{ + FlowSensitive::analyze(); +} + +void FlowSensitiveTBHC::initialize() +{ + PointerAnalysis::initialize(); + AndersenWaveDiff* ander = AndersenWaveDiff::createAndersenWaveDiff(getPAG()); + svfg = memSSA.buildFullSVFG(ander); + setGraph(svfg); + stat = new FlowSensitiveStat(this); + + DCHGraph *dchg = SVFUtil::dyn_cast(getCHGraph()); + assert(dchg != nullptr && "FSTBHC: DCHGraph required!"); + + TypeBasedHeapCloning::setDCHG(dchg); + TypeBasedHeapCloning::setPAG(pag); + + // Populates loadGeps. + determineWhichGepsAreLoads(); +} + +void FlowSensitiveTBHC::finalize(void) +{ + FlowSensitive::finalize(); + // ^ Will print call graph and alias stats. + + if(print_stat) + dumpStats(); + // getDFPTDataTy()->dumpPTData(); + + validateTBHCTests(svfMod); +} + +void FlowSensitiveTBHC::backPropagate(NodeID clone) +{ + PAGNode *cloneObj = pag->getPAGNode(clone); + assert(cloneObj && "FSTBHC: clone does not exist in PAG?"); + PAGNode *originalObj = pag->getPAGNode(getOriginalObj(clone)); + assert(cloneObj && "FSTBHC: original object does not exist in PAG?"); + // Check the original object too because when reuse of a gep occurs, the new object + // is an FI object. + if (SVFUtil::isa(cloneObj) || SVFUtil::isa(originalObj)) + { + // Since getGepObjClones is updated, some GEP nodes need to be redone. + const NodeBS &retrievers = gepToSVFGRetrievers[getOriginalObj(clone)]; + for (NodeID r : retrievers) + { + pushIntoWorklist(r); + } + } + else if (SVFUtil::isa(cloneObj) || SVFUtil::isa(cloneObj)) + { + pushIntoWorklist(getAllocationSite(getOriginalObj(clone))); + } + else + { + assert(false && "FSTBHC: unexpected object type?"); + } +} + +bool FlowSensitiveTBHC::propAlongIndirectEdge(const IndirectSVFGEdge* edge) +{ + SVFGNode* src = edge->getSrcNode(); + SVFGNode* dst = edge->getDstNode(); + + bool changed = false; + + // Get points-to targets may be used by next SVFG node. + // Propagate points-to set for node used in dst. + const PointsTo& pts = edge->getPointsTo(); + + // Since the base Andersen's analysis does NOT perform type-based heap cloning, + // it uses only the base objects; we want to account for clones too. + PointsTo edgePtsAndClones; + + // TODO: the conditional bool may be unnecessary. + // Adding all clones is redundant, and introduces too many calls to propVarPts... + // This introduces performance and precision penalties. + // We should filter out according to src. + bool isStore = false; + const DIType *tildet = nullptr; + PointsTo storePts; + if (const StoreSVFGNode *store = SVFUtil::dyn_cast(src)) + { + tildet = getTypeFromCTirMetadata(store); + isStore = true; + storePts = getPts(store->getPAGDstNodeID()); + } + + const PointsTo &filterSet = getFilterSet(src->getId()); + for (NodeID o : pts) + { + if (!filterSet.test(o)) + { + edgePtsAndClones.set(o); + } + + for (NodeID c : getClones(o)) + { + if (!isStore) + { + if (!filterSet.test(c)) + { + edgePtsAndClones.set(c); + } + } + else + { + if (storePts.test(c) && !filterSet.test(c)) + { + edgePtsAndClones.set(c); + } + } + } + + if (GepObjPN *gep = SVFUtil::dyn_cast(pag->getPAGNode(o))) + { + // Want the geps which are at the same "level" as this one (same mem obj, same offset). + const NodeBS &geps = getGepObjsFromMemObj(gep->getMemObj(), gep->getLocationSet().getOffset()); + for (NodeID g : geps) + { + const DIType *gepType = getType(g); + if (!isStore || gepType || isBase(tildet, gepType)) + { + if (!filterSet.test(g)) + { + edgePtsAndClones.set(g); + } + } + } + } + } + + for (NodeID o : edgePtsAndClones) + { + if (propVarPtsFromSrcToDst(o, src, dst)) + changed = true; + + if (isFIObjNode(o)) + { + /// If this is a field-insensitive obj, propagate all field node's pts + const NodeBS &allFields = getAllFieldsObjNode(o); + for (NodeID f : allFields) + { + if (propVarPtsFromSrcToDst(f, src, dst)) + changed = true; + } + } + } + + return changed; +} + +bool FlowSensitiveTBHC::propAlongDirectEdge(const DirectSVFGEdge* edge) +{ + double start = stat->getClk(); + bool changed = false; + + SVFGNode* src = edge->getSrcNode(); + SVFGNode* dst = edge->getDstNode(); + // If this is an actual-param or formal-ret, top-level pointer's pts must be + // propagated from src to dst. + if (ActualParmSVFGNode* ap = SVFUtil::dyn_cast(src)) + { + if (!ap->getParam()->isPointer()) return false; + changed = propagateFromAPToFP(ap, dst); + } + else if (FormalRetSVFGNode* fp = SVFUtil::dyn_cast(src)) + { + if (!fp->getRet()->isPointer()) return false; + changed = propagateFromFRToAR(fp, dst); + } + else + { + // Direct SVFG edge links between def and use of a top-level pointer. + // There's no points-to information propagated along direct edge. + // Since the top-level pointer's value has been changed at src node, + // return TRUE to put dst node into the work list. + changed = true; + } + + double end = stat->getClk(); + directPropaTime += (end - start) / TIMEINTERVAL; + return changed; +} + +bool FlowSensitiveTBHC::processAddr(const AddrSVFGNode* addr) +{ + double start = stat->getClk(); + + NodeID srcID = addr->getPAGSrcNodeID(); + NodeID dstID = addr->getPAGDstNodeID(); + + double end = stat->getClk(); + addrTime += (end - start) / TIMEINTERVAL; + + if (!addr->getPAGEdge()->isPTAEdge()) return false; + + bool changed = FlowSensitive::processAddr(addr); + + start = stat->getClk(); + + const DIType *objType; + if (isHeapMemObj(srcID)) + { + objType = undefType; + } + else if (pag->isConstantObj(srcID)) + { + // Probably constants that have been merged into one. + // We make it undefined even though it's technically a global + // to keep in line with SVF's design. + // This will end up splitting into one for each type of constant. + objType = undefType; + } + else + { + // Stack/global. + objType = getTypeFromCTirMetadata(addr); + } + + setType(srcID, objType); + setAllocationSite(srcID, addr->getId()); + + // All the typed versions of srcID. This handles back-propagation. + const NodeBS &clones = getClones(srcID); + for (NodeID c : clones) + { + changed = addPts(dstID, c) || changed; + // No need for typing these are all clones; they are all typed. + } + + end = stat->getClk(); + addrTime += (end - start) / TIMEINTERVAL; + + return changed; +} + +bool FlowSensitiveTBHC::processGep(const GepSVFGNode* gep) +{ + // Copy of that in FlowSensitive.cpp + some changes. + double start = stat->getClk(); + bool changed = false; + + NodeID q = gep->getPAGSrcNodeID(); + + const DIType *tildet = getTypeFromCTirMetadata(gep); + if (tildet != undefType) + { + bool reuse = Options::TBHCAllReuse || (Options::TBHCStoreReuse && !gepIsLoad(gep->getId())); + changed = init(gep->getId(), q, tildet, reuse, true); + } + + if (!gep->getPAGEdge()->isPTAEdge()) + { + return changed; + } + + const PointsTo& qPts = getPts(q); + PointsTo &filterSet = getFilterSet(gep->getId()); + PointsTo tmpDstPts; + for (NodeID oq : qPts) + { + if (filterSet.test(oq)) continue; + + if (TypeBasedHeapCloning::isBlkObjOrConstantObj(oq)) + { + tmpDstPts.set(oq); + } + else + { + if (SVFUtil::isa(gep->getPAGEdge())) + { + setObjFieldInsensitive(oq); + tmpDstPts.set(oq); + const DIType *t = getType(oq); + if (t && (t->getTag() == dwarf::DW_TAG_array_type || t->getTag() == dwarf::DW_TAG_pointer_type)) + { + const NodeBS fieldClones = getGepObjClones(oq, 1); + for (NodeID fc : fieldClones) + { + gepToSVFGRetrievers[getOriginalObj(fc)].set(gep->getId()); + tmpDstPts.set(fc); + } + } + } + else if (const NormalGepPE* normalGep = SVFUtil::dyn_cast(gep->getPAGEdge())) + { + const DIType *baseType = getType(oq); + + // Drop down to field insensitive. + if (baseType == nullptr) + { + setObjFieldInsensitive(oq); + NodeID fiObj = oq; + tmpDstPts.set(fiObj); + continue; + } + + if (DCHGraph::isAgg(baseType) && baseType->getTag() != dwarf::DW_TAG_array_type + && normalGep->getLocationSet().getOffset() >= dchg->getNumFields(baseType)) + { + // If the field offset is too high for this object, it is killed. It seems that a + // clone was made on this GEP but this is not the base (e.g. base->f1->f2), and SVF + // expects to operate on the base (hence the large offset). The base will have been + // cloned at another GEP and back-propagated, thus it'll reach here safe and sound. + // We ignore arrays/pointers because those are array accesses/pointer arithmetic we + // assume are correct. + // Obviously, non-aggregates cannot have their fields taken so they are spurious. + filterSet.set(oq); + } + else + { + // Operate on the field and all its clones. + const NodeBS fieldClones = getGepObjClones(oq, normalGep->getLocationSet().getOffset()); + for (NodeID fc : fieldClones) + { + gepToSVFGRetrievers[getOriginalObj(fc)].set(gep->getId()); + tmpDstPts.set(fc); + } + } + } + else + { + assert(false && "FSTBHC: new gep edge?"); + } + } + } + + double end = stat->getClk(); + gepTime += (end - start) / TIMEINTERVAL; + + changed = unionPts(gep->getPAGDstNodeID(), tmpDstPts) || changed; + return changed; +} + +bool FlowSensitiveTBHC::processLoad(const LoadSVFGNode* load) +{ + double start = stat->getClk(); + + bool changed = false; + const DIType *tildet = getTypeFromCTirMetadata(load); + if (tildet != undefType) + { + changed = init(load->getId(), load->getPAGSrcNodeID(), tildet, Options::TBHCAllReuse); + } + + // We want to perform the initialisation for non-pointer nodes but not process the load. + if (!load->getPAGEdge()->isPTAEdge()) + { + return changed; + } + + NodeID dstVar = load->getPAGDstNodeID(); + + const PointsTo& srcPts = getPts(load->getPAGSrcNodeID()); + const PointsTo &filterSet = getFilterSet(load->getId()); + // unionPtsFromIn is going to call getOriginalObj on ptd anyway. + // This results in fewer loop iterations. o_t, o_s --> o. + PointsTo srcOriginalObjs; + for (NodeID s : srcPts) + { + if (filterSet.test(s)) continue; + if (pag->isConstantObj(s) || pag->isNonPointerObj(s)) continue; + srcOriginalObjs.set(getOriginalObj(s)); + } + + for (NodeID ptd : srcOriginalObjs) + { + // filterSet tests happened while building srcOriginalObjs. + if (unionPtsFromIn(load, ptd, dstVar)) + changed = true; + + if (isFIObjNode(ptd)) + { + /// If the ptd is a field-insensitive node, we should also get all field nodes' + /// points-to sets and pass them to pagDst. + const NodeBS &allFields = getAllFieldsObjNode(ptd); + for (NodeID f : allFields) + { + if (unionPtsFromIn(load, f, dstVar)) + changed = true; + } + } + } + + double end = stat->getClk(); + loadTime += (end - start) / TIMEINTERVAL; + return changed; +} + +bool FlowSensitiveTBHC::processStore(const StoreSVFGNode* store) +{ + double start = stat->getClk(); + + bool changed = false; + const DIType *tildet = getTypeFromCTirMetadata(store); + if (tildet != undefType) + { + changed = init(store->getId(), store->getPAGDstNodeID(), tildet, Options::TBHCAllReuse || Options::TBHCStoreReuse); + } + + // Like processLoad: we want to perform initialisation for non-pointers but not the store. + if (!store->getPAGEdge()->isPTAEdge()) + { + // Pass through and return because there may be some pointer nodes + // relying on this node's parents. + changed = getDFPTDataTy()->updateAllDFOutFromIn(store->getId(), 0, false); + return changed; + } + + const PointsTo & dstPts = getPts(store->getPAGDstNodeID()); + + /// STORE statement can only be processed if the pointer on the LHS + /// points to something. If we handle STORE with an empty points-to + /// set, the OUT set will be updated from IN set. Then if LHS pointer + /// points-to one target and it has been identified as a strong + /// update, we can't remove those points-to information computed + /// before this strong update from the OUT set. + if (dstPts.empty()) + { + return changed; + } + + changed = false; + const PointsTo &filterSet = getFilterSet(store->getId()); + if(getPts(store->getPAGSrcNodeID()).empty() == false) + { + for (NodeID ptd : dstPts) + { + if (filterSet.test(ptd)) continue; + + if (pag->isConstantObj(ptd) || pag->isNonPointerObj(ptd)) + continue; + + if (unionPtsFromTop(store, store->getPAGSrcNodeID(), ptd)) + changed = true; + } + } + + double end = stat->getClk(); + storeTime += (end - start) / TIMEINTERVAL; + + double updateStart = stat->getClk(); + // also merge the DFInSet to DFOutSet. + /// check if this is a strong updates store + NodeID singleton; + bool isSU = isStrongUpdate(store, singleton); + if (isSU) + { + svfgHasSU.set(store->getId()); + if (strongUpdateOutFromIn(store, singleton)) + changed = true; + } + else + { + svfgHasSU.reset(store->getId()); + if (weakUpdateOutFromIn(store)) + changed = true; + } + double updateEnd = stat->getClk(); + updateTime += (updateEnd - updateStart) / TIMEINTERVAL; + + return changed; +} + +bool FlowSensitiveTBHC::processPhi(const PHISVFGNode* phi) +{ + if (!phi->isPTANode()) return false; + return FlowSensitive::processPhi(phi); +} + +/// Returns whether this instruction initialisates an object's +/// vtable (metadata: ctir.vt.init). Returns the object's type, +/// otherwise, nullptr. +static const DIType *getVTInitType(const CopySVFGNode *copy, DCHGraph *dchg) +{ + if (copy->getInst() == nullptr) return nullptr; + const Instruction *inst = copy->getInst(); + + const MDNode *mdNode = inst->getMetadata(cppUtil::ctir::vtInitMDName); + if (mdNode == nullptr) return nullptr; + + const DIType *type = SVFUtil::dyn_cast(mdNode); + assert(type != nullptr && "TBHC: bad ctir.vt.init metadata"); + return dchg->getCanonicalType(type); +} + +bool FlowSensitiveTBHC::processCopy(const CopySVFGNode* copy) +{ + const DIType *vtInitType = getVTInitType(copy, dchg); + bool changed = false; + if (vtInitType != nullptr) + { + // Setting the virtual table pointer. + changed = init(copy->getId(), copy->getPAGSrcNodeID(), vtInitType, true); + } + + return FlowSensitive::processCopy(copy) || changed; +} + +const NodeBS& FlowSensitiveTBHC::getAllFieldsObjNode(NodeID id) +{ + return getGepObjs(id); +} + +bool FlowSensitiveTBHC::updateInFromIn(const SVFGNode* srcStmt, NodeID srcVar, const SVFGNode* dstStmt, NodeID dstVar) +{ + // IN sets are only based on the original object. + return getDFPTDataTy()->updateDFInFromIn(srcStmt->getId(), getOriginalObj(srcVar), + dstStmt->getId(), getOriginalObj(dstVar)); +} + +bool FlowSensitiveTBHC::updateInFromOut(const SVFGNode* srcStmt, NodeID srcVar, const SVFGNode* dstStmt, NodeID dstVar) +{ + // OUT/IN sets only have original objects. + return getDFPTDataTy()->updateDFInFromOut(srcStmt->getId(), getOriginalObj(srcVar), + dstStmt->getId(), getOriginalObj(dstVar)); +} + +bool FlowSensitiveTBHC::unionPtsFromIn(const SVFGNode* stmt, NodeID srcVar, NodeID dstVar) +{ + // IN sets only have original objects. + return getDFPTDataTy()->updateTLVPts(stmt->getId(), getOriginalObj(srcVar), dstVar); +} + +bool FlowSensitiveTBHC::unionPtsFromTop(const SVFGNode* stmt, NodeID srcVar, NodeID dstVar) +{ + // OUT sets only have original objects. + return getDFPTDataTy()->updateATVPts(srcVar, stmt->getId(), getOriginalObj(dstVar)); +} + +bool FlowSensitiveTBHC::propDFInToIn(const SVFGNode* srcStmt, NodeID srcVar, const SVFGNode* dstStmt, NodeID dstVar) +{ + // IN sets are only based on the original object. + return getDFPTDataTy()->updateAllDFInFromIn(srcStmt->getId(), getOriginalObj(srcVar), + dstStmt->getId(), getOriginalObj(dstVar)); +} + +bool FlowSensitiveTBHC::propDFOutToIn(const SVFGNode* srcStmt, NodeID srcVar, const SVFGNode* dstStmt, NodeID dstVar) +{ + // OUT/IN sets only have original objects. + return getDFPTDataTy()->updateAllDFInFromOut(srcStmt->getId(), getOriginalObj(srcVar), + dstStmt->getId(), getOriginalObj(dstVar)); +} + +void FlowSensitiveTBHC::determineWhichGepsAreLoads(void) +{ + for (SVFG::iterator nI = svfg->begin(); nI != svfg->end(); ++nI) + { + SVFGNode *svfgNode = nI->second; + if (const StmtSVFGNode *gep = SVFUtil::dyn_cast(svfgNode)) + { + // Only care about ctir nodes - they have the reuse problem. + if (getTypeFromCTirMetadata(gep)) + { + bool isLoad = true; + for (const SVFGEdge *e : gep->getOutEdges()) + { + SVFGNode *dst = e->getDstNode(); + + // Loop on itself - don't care. + if (gep == dst) continue; + + if (!SVFUtil::isa(dst)) + { + isLoad = false; + break; + } + } + + if (isLoad) + { + loadGeps.set(gep->getId()); + } + } + } + } +} + +bool FlowSensitiveTBHC::gepIsLoad(NodeID gep) +{ + // Handles when gep is not even a GEP; loadGeps only contains GEPs. + return loadGeps.test(gep); +} + +const MDNode *FlowSensitiveTBHC::getRawCTirMetadata(const SVFGNode *s) +{ + if (const StmtSVFGNode *stmt = SVFUtil::dyn_cast(s)) + { + const Value *v = stmt->getInst() ? stmt->getInst() : stmt->getPAGEdge()->getValue(); + if (v != nullptr) + { + return TypeBasedHeapCloning::getRawCTirMetadata(v); + } + } + + return nullptr; +} + +const DIType *FlowSensitiveTBHC::getTypeFromCTirMetadata(const SVFGNode *s) +{ + if (const StmtSVFGNode *stmt = SVFUtil::dyn_cast(s)) + { + const Value *v = stmt->getInst(); + if (v != nullptr) + { + return TypeBasedHeapCloning::getTypeFromCTirMetadata(v); + } + } + + return nullptr; +} + +void FlowSensitiveTBHC::expandFIObjs(const PointsTo& pts, PointsTo& expandedPts) +{ + expandedPts = pts; + for (NodeID o : pts) + { + expandedPts |= getAllFieldsObjNode(o); + while (const GepObjPN *gepObj = SVFUtil::dyn_cast(pag->getPAGNode(o))) + { + expandedPts |= getAllFieldsObjNode(o); + o = gepObj->getBaseNode(); + } + } +} + +void FlowSensitiveTBHC::countAliases(Set> cmp, unsigned *mayAliases, unsigned *noAliases) +{ + Map, PointsTo> filteredPts; + for (std::pair locP : cmp) + { + const PointsTo &filterSet = getFilterSet(locP.first); + const PointsTo &pts = getPts(locP.second); + PointsTo &ptsFiltered = filteredPts[locP]; + + for (NodeID o : pts) + { + if (filterSet.test(o)) continue; + ptsFiltered.set(o); + } + } + + for (std::pair locPA : cmp) + { + const PointsTo &aPts = filteredPts[locPA]; + for (std::pair locPB : cmp) + { + if (locPB == locPA) continue; + const PointsTo &bPts = filteredPts[locPB]; + + switch (alias(aPts, bPts)) + { + case llvm::NoAlias: + ++(*noAliases); + break; + case llvm::MayAlias: + ++(*mayAliases); + break; + default: + assert("Not May/NoAlias?"); + } + } + } + +} diff --git a/svf/lib/WPA/InvariantHandler.cpp b/svf/lib/WPA/InvariantHandler.cpp new file mode 100644 index 000000000..701496a6e --- /dev/null +++ b/svf/lib/WPA/InvariantHandler.cpp @@ -0,0 +1,355 @@ +#include +#include "llvm/IR/InstIterator.h" + + +void InvariantHandler::recordTarget(int id, Value* target, Function* instFun) { + IRBuilder* builder; + LLVMContext& C = mod->getContext(); + + IntegerType* i32Ty = IntegerType::get(C, 32); + IntegerType* i64Ty = IntegerType::get(C, 64); + PointerType* i64PtrTy = PointerType::get(i64Ty, 0); + bool shouldReset = false; + AllocaInst* stackVar = nullptr; + + if (AllocaInst* allocaInst = SVFUtil::dyn_cast(target)) { + builder = new IRBuilder(allocaInst->getNextNode()); + stackVar = allocaInst; + shouldReset = true; + } else if (GlobalValue* gvar = SVFUtil::dyn_cast(target)) { + Function* mainFunction = mod->getFunction("main"); + Instruction* inst = mainFunction->getEntryBlock().getFirstNonPHIOrDbg(); + builder = new IRBuilder(inst); + } else if (CallInst* heapCall = SVFUtil::dyn_cast(target)) { + builder = new IRBuilder(heapCall->getNextNode()); + } else { + assert(false && "Not handling stuff here"); + } + + Constant* idConstant = ConstantInt::get(i32Ty, id); + Value* ptrVal = builder->CreateBitOrPointerCast(target, i64Ty); + builder->CreateCall(instFun, {idConstant, ptrVal}); + + if (shouldReset) { + Function* func = stackVar->getParent()->getParent(); + //std::vector innerCalls; + std::vector returns; + for (inst_iterator I = inst_begin(func), E = inst_end(func); I != E; ++I) { + if (ReturnInst* ret = SVFUtil::dyn_cast(&*I)) { + returns.push_back(ret); + } + /* + if (CallInst* call = SVFUtil::dyn_cast(&*I)) { + if (call->getCalledFunction() != instFun) { + innerCalls.push_back(call); + } + } + */ + } + for (ReturnInst* ret: returns) { + builder->SetInsertPoint(ret); + // Reset the Invariant + builder->CreateCall(instFun, {idConstant, Constant::getNullValue(i64Ty)}); + } + /* + for (CallInst* call: innerCalls) { + if (call->getCalledFunction() && + call->getCalledFunction()->getName().contains(".dbg")) { + continue; + } + builder->SetInsertPoint(call); + // Reset the Invariant before the call + builder->CreateCall(instFun, {idConstant, Constant::getNullValue(i64Ty)}); + // Restore it after the call + builder->SetInsertPoint(call->getNextNode()); + Value* ptrVal = builder->CreateBitOrPointerCast(target, i64Ty); + builder->CreateCall(instFun, {idConstant, ptrVal}); + } + */ + } +} + +/** + * Check that the gep can never point to the objects in target + */ +void InvariantHandler::instrumentVGEPInvariant(GetElementPtrInst* gep, std::vector& targets) { + LLVMContext& C = gep->getContext(); + Type* longType = IntegerType::get(mod->getContext(), 64); + Type* intType = IntegerType::get(mod->getContext(), 32); + Type* ptrToLongType = PointerType::get(longType, 0); + + std::vector tgtKaliIDs; + for (Value* target: targets) { + int id = -1; + if (valueToKaliIdMap.find(target) == valueToKaliIdMap.end()) { + id = kaliInvariantId++; + valueToKaliIdMap[target] = id; + kaliIdToValueMap[id] = target; + recordTarget(id, target, vgepPtdRecordFn); + } else { + id = valueToKaliIdMap[target]; + } + tgtKaliIDs.push_back(id); + } + + Value* pointer = gep->getPointerOperand(); + + // Call the ptdTargetCheckFn + + int len = tgtKaliIDs.size(); + Constant* clen = ConstantInt::get(longType, len); + + ArrayType* arrTy = ArrayType::get(longType, len); + + std::vector consIdVec; + + for (int i: tgtKaliIDs) { + consIdVec.push_back(ConstantInt::get(longType, i)); + } + + llvm::ArrayRef consIdArr(consIdVec); + + Constant* kaliIdArr = ConstantArray::get(arrTy, consIdArr); + + GlobalVariable* kaliArrGvar = new GlobalVariable(*mod, + /*Type=*/arrTy, + /*isConstant=*/true, + /*Linkage=*/GlobalValue::ExternalLinkage, + /*Initializer=*/0, // has initializer, specified below + /*Name=*/"cons id"); + kaliArrGvar->setInitializer(kaliIdArr); + + IRBuilder Builder(gep); + + Constant* Zero = ConstantInt::get(C, llvm::APInt(64, 0)); + //Value* firstCons = Builder.CreateGEP(kaliIdArr->getType(), kaliIdArr, Zero); + llvm::ArrayRef zeroIdxs {Zero, Zero}; + Value* firstCons = ConstantExpr::getGetElementPtr(arrTy, kaliArrGvar, zeroIdxs); + + Value* arg1 = Builder.CreateBitOrPointerCast(pointer, ptrToLongType); + + Value* arg2 = clen; + Value* arg3 = Builder.CreateBitOrPointerCast(firstCons, ptrToLongType); + + Builder.CreateCall(ptdTargetCheckFn, {arg1, arg2, arg3}); +} + +void InvariantHandler::handleVGEPInvariants() { +#define SANITIZE_VGEP // TODO: The checks contained inside this macro should likely go + for (const GetElementPtrInst* gep: pag->getVarGeps()) { +#ifdef SANITIZE_VGEP + // We only care if the geps are of base pointer type, not array of + // structs + // TODO: In fact there should be nothing else here at all + // if there is then we've messed something up earlier on + Type* resultType = gep->getResultElementType(); + if (PointerType* ptrType = SVFUtil::dyn_cast(resultType)) { + Type* elemTy = nullptr; + while (ptrType) { + elemTy = ptrType->getPointerElementType(); + // We can discard ptr-to-ptr-to... because these are automatically + // field insensitive + ptrType = SVFUtil::dyn_cast(elemTy); + } + if (SVFUtil::isa(elemTy) || SVFUtil::isa(elemTy)) { + continue; + } + } else { + // If it's not a pointer then we don't care either + continue; + } + // Now here we are left with the gep results of type (i8/iN)* + // But there's another caveat, if the variable gep index was an inner + // index, then we can ignore it too + // v[i]->k for example. Here v[i] is clearly pointing to a struct and + // this isn't a case of using a[i] to access each struct _element_ + // byte-by-byte + if (gep->getNumIndices() > 1) { + continue; + } + llvm::errs() << "Gep return type: " << *(resultType) << " for gep: " << *gep << "\n"; +#endif + std::vector targets; + for (NodeID ptdId: pag->getVarGepPtdMap()[gep]) { + if (pag->hasPAGNode(ptdId)) { + PAGNode* pagNode = pag->getPAGNode(ptdId); + if (pagNode->hasValue()) { + Value* ptdValue = const_cast(pagNode->getValue()); + // we will be modifying the value by inserting instrumentation + targets.push_back(ptdValue); + } + } + } + instrumentVGEPInvariant(const_cast(gep), targets); + } +} + +void InvariantHandler::handlePWCInvariants() { + PAG::PWCInvariantIterator it, beg = pag->getPWCInvariants().begin(); + PAG::PWCInvariantIterator end = pag->getPWCInvariants().end(); + Type* longType = IntegerType::get(mod->getContext(), 64); + Type* intType = IntegerType::get(mod->getContext(), 32); + + int ptrID = 0; + std::set instrumented; // TODO: make this handle different PWCs + for (it = beg; it != end; it++) { + CycleID pwcID = it->first; + // Not all pointer nodes have backing values + // Let's get the count of the ones that do + int ptrValCount = it->second.size(); + GetElementPtrInst* nonLoopGep = nullptr; + std::vector geps; + + for (const Value* vPtr: it->second) { + if (const GetElementPtrInst* gep = SVFUtil::dyn_cast(vPtr)) { + geps.push_back(const_cast(gep)); + /* + const BasicBlock* bb = gep->getParent(); + if (!loopInfo->getLoopFor(bb)) { + llvm::errs() << "found gep outside loop\n"; + nonLoopGep = const_cast(gep); + break; + } + */ + } + } + nonLoopGep = geps[0]; + /* + if (!nonLoopGep) { + llvm::errs() << "Did not find gep outside loop\n"; + nonLoopGep = geps[0]; + } + */ + + if (std::find(instrumented.begin(), instrumented.end(), nonLoopGep) != instrumented.end()) { + continue; + } + + instrumented.insert(nonLoopGep); + // p = r + 4 + // If the cycle is complete, then the first time r will have the value + // r' + // When the cycle is complete it'll have r' + 4 + // This indicates the cycle is complete + + // Invoke updatePWC (pwcId, gepVal) after this gep + IRBuilder builder(nonLoopGep->getNextNode()); + std::vector updatePWCArgsList; + updatePWCArgsList.push_back(ConstantInt::get(intType, pwcID)); + updatePWCArgsList.push_back(builder.CreateBitOrPointerCast(nonLoopGep, longType)); + + llvm::ArrayRef updateArgs(updatePWCArgsList); + FunctionType* fTy = updatePWCFn->getFunctionType(); + builder.CreateCall(fTy, updatePWCFn, updateArgs); + + // Invoke checkPWC (pwcId, gepPtrVal, offset) before this gep + + builder.SetInsertPoint(nonLoopGep); + Value* gepPtr = nonLoopGep->getPointerOperand(); + std::vector checkPWCArgsList; + checkPWCArgsList.push_back(ConstantInt::get(intType, pwcID)); + checkPWCArgsList.push_back(builder.CreateBitOrPointerCast(gepPtr, longType)); + int totalOffset = computeOffsetInPWC(geps, nonLoopGep); + checkPWCArgsList.push_back(ConstantInt::get(longType, totalOffset)); + llvm::ArrayRef checkPWCArgs(checkPWCArgsList); + + fTy = checkPWCFn->getFunctionType(); + builder.CreateCall(fTy, checkPWCFn, checkPWCArgs); + } +} + +int InvariantHandler::computeOffsetInPWC(std::vector& geps, GetElementPtrInst* nonLoopGep) { + llvm::Module* mod = nonLoopGep->getModule(); + + int totalOffset = 0; + for (GetElementPtrInst* gep: geps) { + llvm::APInt offset(64, 0); + if (gep->accumulateConstantOffset(mod->getDataLayout(), offset)) { + totalOffset += offset.getZExtValue(); + } + } + return totalOffset; +} + +/** + * Initialization routine for the VGEP invariant + * Install the ptdTargetCheck function + * ptdTargetCheck(unsigned long* valid_tgts, long len, unsigned long* tgt); + */ +void InvariantHandler::initVGEPInvariants() { + Type* voidType = Type::getVoidTy(mod->getContext()); + Type* longType = IntegerType::get(mod->getContext(), 64); + Type* intType = IntegerType::get(mod->getContext(), 32); + + // Install the vgepRecord Routine + std::vector vgepRecordTypes; + vgepRecordTypes.push_back(intType); + vgepRecordTypes.push_back(longType); + + llvm::ArrayRef vgepRecordTypeArr(vgepRecordTypes); + + FunctionType* vgepRecordFnTy = FunctionType::get(voidType, vgepRecordTypeArr, false); + vgepPtdRecordFn = Function::Create(vgepRecordFnTy, Function::ExternalLinkage, "vgepRecordTarget", mod); + + svfMod->addFunctionSet(vgepPtdRecordFn); + // Install the ptdTargetCheck Routine + // ptdTargetCheck(unsigned long* valid_tgts, long len, unsigned long* tgt); + std::vector ptdTargetCheckType; + + Type* ptrToLongType = PointerType::get(longType, 0); + ptdTargetCheckType.push_back(ptrToLongType); + ptdTargetCheckType.push_back(longType); + ptdTargetCheckType.push_back(ptrToLongType); + + llvm::ArrayRef ptdTargetCheckTypeArr(ptdTargetCheckType); + + FunctionType* ptdTargetCheckFnTy = FunctionType::get(intType, ptdTargetCheckTypeArr, false); + ptdTargetCheckFn = Function::Create(ptdTargetCheckFnTy, Function::ExternalLinkage, "ptdTargetCheck", mod); + + svfMod->addFunctionSet(ptdTargetCheckFn); +} + +/** + * Initialization routine for the PWC invariant + * Install the updateAndCheckPWC function + * Each cycle consists of a list of pointers that need to be equal to each + * other for the cycle to hold. + * int updateAndCheckPWC(int cycleID, int totalInvariants, int invariantId, unsigned long val, int isGep); + * where, cycleID is the PWC CycleID + * invariantID is the NodeID of the pointer involved in the PWC + * val is the value of the pointer updated + * It returns true if the invariant no longer is held + */ +void InvariantHandler::initPWCInvariants() { + Type* voidType = Type::getVoidTy(mod->getContext()); + Type* intType = IntegerType::get(mod->getContext(), 32); + Type* longType = IntegerType::get(mod->getContext(), 64); + + std::vector updatePWCType; + + updatePWCType.push_back(intType); + updatePWCType.push_back(longType); + + FunctionType* updatePWCFnTy = FunctionType::get(voidType, {intType, longType}, false); + updatePWCFn = Function::Create(updatePWCFnTy, Function::ExternalLinkage, "updatePWC", mod); + + svfMod->addFunctionSet(updatePWCFn); + + std::vector checkPWCType; + + checkPWCType.push_back(intType); + checkPWCType.push_back(longType); + checkPWCType.push_back(longType); + + llvm::ArrayRef checkPWCTypeArr(checkPWCType); + + FunctionType* checkFnTy = FunctionType::get(intType, checkPWCTypeArr, false); + checkPWCFn = Function::Create(checkFnTy, Function::ExternalLinkage, "checkPWC", mod); + + svfMod->addFunctionSet(checkPWCFn); +} + +void InvariantHandler::init() { + initVGEPInvariants(); + initPWCInvariants(); +} diff --git a/svf/lib/WPA/LoopInfoConsolidatorPass.cpp b/svf/lib/WPA/LoopInfoConsolidatorPass.cpp new file mode 100644 index 000000000..e94af61fb --- /dev/null +++ b/svf/lib/WPA/LoopInfoConsolidatorPass.cpp @@ -0,0 +1,31 @@ +#include "WPA/LoopInfoConsolidatorPass.h" + +using namespace llvm; + +bool LoopInfoConsolidatorPass::runOnModule(Module& M) { + errs() << "I saw a module called " << M.getName() << "!\n"; + + for (Function& func: M.getFunctionList()) { + if (func.isDeclaration()) { + continue; + } + LoopInfoWrapperPass& lInfoPass = getAnalysis(func); + LoopInfo& lInfo = lInfoPass.getLoopInfo(); + for (Loop* loop: lInfo) { + for (BasicBlock* bb: loop->getBlocksVector()) { + bbInLoops.insert(bb); + } + } + } + + /* + errs() << "loop bbs:\n"; + for (BasicBlock* bb: bbInLoops) { + bb->dump(); + } + */ + return false; +} + + +char LoopInfoConsolidatorPass::ID = 0; diff --git a/svf/lib/WPA/Steensgaard.cpp b/svf/lib/WPA/Steensgaard.cpp index fd4d4336f..8a1a08581 100644 --- a/svf/lib/WPA/Steensgaard.cpp +++ b/svf/lib/WPA/Steensgaard.cpp @@ -1,26 +1,3 @@ -//===- Steensgaard.cpp -- Steensgaard's field-insensitive -// analysis--------------// -// -// SVF: Static Value-Flow Analysis -// -// Copyright (C) <2013-2017> -// - -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU Affero General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Affero General Public License for more details. - -// You should have received a copy of the GNU Affero General Public License -// along with this program. If not, see . -// -//===----------------------------------------------------------------------===// - /* * Steensgaard.cpp * @@ -33,14 +10,13 @@ using namespace SVF; using namespace SVFUtil; -Steensgaard* Steensgaard::steens = nullptr; +Steensgaard *Steensgaard::steens = nullptr; /*! * Steensgaard analysis */ -void Steensgaard::solveWorklist() -{ +void Steensgaard::solveWorklist(){ processAllAddr(); @@ -51,52 +27,47 @@ void Steensgaard::solveWorklist() ConstraintNode* node = consCG->getConstraintNode(nodeId); /// foreach o \in pts(p) - for (NodeID o : getPts(nodeId)) - { + for(NodeID o : getPts(nodeId)){ /// *p = q : EC(o) == EC(q) - for (ConstraintEdge* edge : node->getStoreInEdges()) - { + for (ConstraintEdge* edge : node->getStoreInEdges()){ ecUnion(edge->getSrcID(), o); } // r = *p : EC(r) == EC(o) - for (ConstraintEdge* edge : node->getLoadOutEdges()) - { + for (ConstraintEdge* edge : node->getLoadOutEdges()){ ecUnion(o, edge->getDstID()); } } /// q = p : EC(q) == EC(p) - for (ConstraintEdge* edge : node->getCopyOutEdges()) - { - ecUnion(edge->getSrcID(), edge->getDstID()); + for (ConstraintEdge* edge : node->getCopyOutEdges()){ + ecUnion(edge->getSrcID(),edge->getDstID()); } /// q = &p->f : EC(q) == EC(p) - for (ConstraintEdge* edge : node->getGepOutEdges()) - { - ecUnion(edge->getSrcID(), edge->getDstID()); + for (ConstraintEdge* edge : node->getGepOutEdges()){ + ecUnion(edge->getSrcID(),edge->getDstID()); } } } + void Steensgaard::setEC(NodeID node, NodeID rep) { rep = getEC(rep); Set& subNodes = getSubNodes(node); - for (NodeID sub : subNodes) - { + for(NodeID sub : subNodes){ nodeToECMap[sub] = rep; - addSubNode(rep, sub); + addSubNode(rep,sub); } subNodes.clear(); } + /// merge node into equiv class and merge node's pts into ec's pts -void Steensgaard::ecUnion(NodeID node, NodeID ec) -{ - if (unionPts(ec, node)) +void Steensgaard::ecUnion(NodeID node, NodeID ec){ + if(unionPts(ec, node)) pushIntoWorklist(ec); - setEC(node, ec); + setEC(node,ec); } /*! @@ -104,188 +75,19 @@ void Steensgaard::ecUnion(NodeID node, NodeID ec) */ void Steensgaard::processAllAddr() { - for (ConstraintGraph::const_iterator nodeIt = consCG->begin(), - nodeEit = consCG->end(); - nodeIt != nodeEit; nodeIt++) + for (ConstraintGraph::const_iterator nodeIt = consCG->begin(), nodeEit = consCG->end(); nodeIt != nodeEit; nodeIt++) { - ConstraintNode* cgNode = nodeIt->second; - for (ConstraintNode::const_iterator it = cgNode->incomingAddrsBegin(), - eit = cgNode->incomingAddrsEnd(); - it != eit; ++it) - { + ConstraintNode * cgNode = nodeIt->second; + for (ConstraintNode::const_iterator it = cgNode->incomingAddrsBegin(), eit = cgNode->incomingAddrsEnd(); + it != eit; ++it){ numOfProcessedAddr++; const AddrCGEdge* addr = cast(*it); NodeID dst = addr->getDstID(); NodeID src = addr->getSrcID(); - if (addPts(dst, src)) + if(addPts(dst,src)) pushIntoWorklist(dst); } } } -bool Steensgaard::updateCallGraph(const CallSiteToFunPtrMap& callsites) -{ - - double cgUpdateStart = stat->getClk(); - - CallEdgeMap newEdges; - onTheFlyCallGraphSolve(callsites, newEdges); - NodePairSet cpySrcNodes; /// nodes as a src of a generated new copy edge - for (CallEdgeMap::iterator it = newEdges.begin(), eit = newEdges.end(); - it != eit; ++it) - { - CallSite cs = SVFUtil::getSVFCallSite(it->first->getCallSite()); - for (FunctionSet::iterator cit = it->second.begin(), - ecit = it->second.end(); - cit != ecit; ++cit) - { - connectCaller2CalleeParams(cs, *cit, cpySrcNodes); - } - } - for (NodePairSet::iterator it = cpySrcNodes.begin(), - eit = cpySrcNodes.end(); - it != eit; ++it) - { - pushIntoWorklist(it->first); - } - - double cgUpdateEnd = stat->getClk(); - timeOfUpdateCallGraph += (cgUpdateEnd - cgUpdateStart) / TIMEINTERVAL; - - return (!newEdges.empty()); -} - -void Steensgaard::heapAllocatorViaIndCall(CallSite cs, NodePairSet& cpySrcNodes) -{ - assert(SVFUtil::getCallee(cs) == nullptr && "not an indirect callsite?"); - RetICFGNode* retBlockNode = - pag->getICFG()->getRetICFGNode(cs.getInstruction()); - const PAGNode* cs_return = pag->getCallSiteRet(retBlockNode); - NodeID srcret; - CallSite2DummyValPN::const_iterator it = callsite2DummyValPN.find(cs); - if (it != callsite2DummyValPN.end()) - { - srcret = getEC(it->second); - } - else - { - NodeID valNode = pag->addDummyValNode(); - NodeID objNode = pag->addDummyObjNode(cs.getType()); - addPts(valNode, objNode); - callsite2DummyValPN.insert(std::make_pair(cs, valNode)); - consCG->addConstraintNode(new ConstraintNode(valNode), valNode); - consCG->addConstraintNode(new ConstraintNode(objNode), objNode); - srcret = valNode; - } - - NodeID dstrec = getEC(cs_return->getId()); - if (addCopyEdge(srcret, dstrec)) - cpySrcNodes.insert(std::make_pair(srcret, dstrec)); -} - -/*! - * Connect formal and actual parameters for indirect callsites - */ -void Steensgaard::connectCaller2CalleeParams(CallSite cs, const SVFFunction* F, - NodePairSet& cpySrcNodes) -{ - assert(F); - - DBOUT(DAndersen, outs() << "connect parameters from indirect callsite " - << cs.getInstruction()->toString() << " to callee " - << *F << "\n"); - - CallICFGNode* callBlockNode = - pag->getICFG()->getCallICFGNode(cs.getInstruction()); - RetICFGNode* retBlockNode = - pag->getICFG()->getRetICFGNode(cs.getInstruction()); - - if (SVFUtil::isHeapAllocExtFunViaRet(F) && - pag->callsiteHasRet(retBlockNode)) - { - heapAllocatorViaIndCall(cs, cpySrcNodes); - } - - if (pag->funHasRet(F) && pag->callsiteHasRet(retBlockNode)) - { - const PAGNode* cs_return = pag->getCallSiteRet(retBlockNode); - const PAGNode* fun_return = pag->getFunRet(F); - if (cs_return->isPointer() && fun_return->isPointer()) - { - NodeID dstrec = getEC(cs_return->getId()); - NodeID srcret = getEC(fun_return->getId()); - if (addCopyEdge(srcret, dstrec)) - { - cpySrcNodes.insert(std::make_pair(srcret, dstrec)); - } - } - else - { - DBOUT(DAndersen, outs() << "not a pointer ignored\n"); - } - } - - if (pag->hasCallSiteArgsMap(callBlockNode) && pag->hasFunArgsList(F)) - { - - // connect actual and formal param - const SVFIR::SVFVarList& csArgList = - pag->getCallSiteArgsList(callBlockNode); - const SVFIR::SVFVarList& funArgList = pag->getFunArgsList(F); - // Go through the fixed parameters. - DBOUT(DPAGBuild, outs() << " args:"); - SVFIR::SVFVarList::const_iterator funArgIt = funArgList.begin(), - funArgEit = funArgList.end(); - SVFIR::SVFVarList::const_iterator csArgIt = csArgList.begin(), - csArgEit = csArgList.end(); - for (; funArgIt != funArgEit; ++csArgIt, ++funArgIt) - { - // Some programs (e.g. Linux kernel) leave unneeded parameters - // empty. - if (csArgIt == csArgEit) - { - DBOUT(DAndersen, outs() << " !! not enough args\n"); - break; - } - const PAGNode* cs_arg = *csArgIt; - const PAGNode* fun_arg = *funArgIt; - - if (cs_arg->isPointer() && fun_arg->isPointer()) - { - DBOUT(DAndersen, outs() << "process actual parm " - << cs_arg->toString() << " \n"); - NodeID srcAA = getEC(cs_arg->getId()); - NodeID dstFA = getEC(fun_arg->getId()); - if (addCopyEdge(srcAA, dstFA)) - { - cpySrcNodes.insert(std::make_pair(srcAA, dstFA)); - } - } - } - - // Any remaining actual args must be varargs. - if (F->isVarArg()) - { - NodeID vaF = getEC(pag->getVarargNode(F)); - DBOUT(DPAGBuild, outs() << "\n varargs:"); - for (; csArgIt != csArgEit; ++csArgIt) - { - const PAGNode* cs_arg = *csArgIt; - if (cs_arg->isPointer()) - { - NodeID vnAA = getEC(cs_arg->getId()); - if (addCopyEdge(vnAA, vaF)) - { - cpySrcNodes.insert(std::make_pair(vnAA, vaF)); - } - } - } - } - if (csArgIt != csArgEit) - { - writeWrnMsg("too many args to non-vararg func."); - writeWrnMsg("(" + cs.getInstruction()->getSourceLoc() + ")"); - } - } -} diff --git a/svf/lib/WPA/TypeAnalysis.cpp b/svf/lib/WPA/TypeAnalysis.cpp index e19235164..180ee19ed 100644 --- a/svf/lib/WPA/TypeAnalysis.cpp +++ b/svf/lib/WPA/TypeAnalysis.cpp @@ -1,4 +1,4 @@ -//===- TypeAnalysis.cpp -- Fast type-based analysis without pointer analysis------// +//===- Andersen.h -- Field-sensitive Andersen's pointer analysis-------------// // // SVF: Static Value-Flow Analysis // @@ -6,16 +6,16 @@ // // This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU Affero General Public License as published by +// it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Affero General Public License for more details. +// GNU General Public License for more details. -// You should have received a copy of the GNU Affero General Public License +// You should have received a copy of the GNU General Public License // along with this program. If not, see . // //===----------------------------------------------------------------------===// @@ -27,25 +27,28 @@ * Author: Yulei Sui */ +#include "Util/Options.h" +#include "SVF-FE/CPPUtil.h" +#include "SVF-FE/ICFGBuilder.h" +#include "SVF-FE/CHG.h" #include "WPA/TypeAnalysis.h" -#include "Graphs/CHG.h" +#include "MemoryModel/PTAStat.h" #include "Graphs/ICFGStat.h" #include "Graphs/VFG.h" -#include "Util/Options.h" -#include "Util/PTAStat.h" using namespace SVF; using namespace SVFUtil; +using namespace cppUtil; using namespace std; /// Initialize analysis void TypeAnalysis::initialize() { - AndersenBase::initialize(); - if (Options::DumpICFG()) + AndersenBase::initialize(); + if (Options::GenICFG) { - icfg = SVFIR::getPAG()->getICFG(); + icfg = PAG::getPAG()->getICFG(); icfg->dump("icfg_initial"); icfg->dump("vfg_initial"); if (print_stat) @@ -76,12 +79,12 @@ void TypeAnalysis::callGraphSolveBasedOnCHA(const CallSiteToFunPtrMap& callsites { for(CallSiteToFunPtrMap::const_iterator iter = callsites.begin(), eiter = callsites.end(); iter!=eiter; ++iter) { - const CallICFGNode* cbn = iter->first; - CallSite cs = SVFUtil::getSVFCallSite(cbn->getCallSite()); - if (cs.isVirtualCall()) + const CallBlockNode* cbn = iter->first; + CallSite cs = SVFUtil::getLLVMCallSite(cbn->getCallSite()); + if (isVirtualCallSite(cs)) { - const SVFValue* vtbl = cs.getVtablePtr(); - (void)vtbl; // Suppress warning of unused variable under release build + virtualCallSites.insert(cs); + const Value *vtbl = getVCallVtblPtr(cs); assert(pag->hasValueNode(vtbl)); VFunSet vfns; getVFnsFromCHA(cbn, vfns); @@ -101,7 +104,7 @@ void TypeAnalysis::dumpCHAStats() return; } - u32_t pure_abstract_class_num = 0, + s32_t pure_abstract_class_num = 0, multi_inheritance_class_num = 0; for (CHGraph::const_iterator it = chgraph->begin(), eit = chgraph->end(); it != eit; ++it) @@ -124,7 +127,7 @@ void TypeAnalysis::dumpCHAStats() * vtbl max vfunction * pure abstract class */ - u32_t vtblnum = 0, + s32_t vtblnum = 0, vfunc_total = 0, vtbl_max = 0, pure_abstract = 0; @@ -136,7 +139,7 @@ void TypeAnalysis::dumpCHAStats() if (node->isPureAbstract()) pure_abstract++; - u32_t vfuncs_size = 0; + s32_t vfuncs_size = 0; const vector& vecs = node->getVirtualFunctionVectors(); for (vector::const_iterator vit = vecs.begin(), veit = vecs.end(); vit != veit; ++vit) @@ -163,7 +166,6 @@ void TypeAnalysis::dumpCHAStats() outs() << "vtblnum:\t" << vtblnum << '\n'; outs() << "vtbl_average:\t" << (double)(vfunc_total)/vtblnum << '\n'; outs() << "vtbl_max:\t" << vtbl_max << '\n'; - outs() << "pure_abstract:\t" << pure_abstract << '\n'; } diff --git a/svf/lib/WPA/VersionedFlowSensitive.cpp b/svf/lib/WPA/VersionedFlowSensitive.cpp index f62f7969d..64cf8ed6c 100644 --- a/svf/lib/WPA/VersionedFlowSensitive.cpp +++ b/svf/lib/WPA/VersionedFlowSensitive.cpp @@ -9,12 +9,7 @@ #include "WPA/Andersen.h" #include "WPA/VersionedFlowSensitive.h" -#include "Util/Options.h" -#include "MemoryModel/PointsTo.h" #include -#include -#include -#include using namespace SVF; @@ -27,19 +22,15 @@ VersionedVar VersionedFlowSensitive::atKey(NodeID var, Version version) return std::make_pair(var, version); } -VersionedFlowSensitive::VersionedFlowSensitive(SVFIR *_pag, PTATY type) +VersionedFlowSensitive::VersionedFlowSensitive(PAG *_pag, PTATY type) : FlowSensitive(_pag, type) { numPrelabeledNodes = numPrelabelVersions = 0; - prelabelingTime = meldLabelingTime = versionPropTime = 0.0; + relianceTime = prelabelingTime = meldLabelingTime = meldMappingTime = versionPropTime = 0.0; // We'll grab vPtD in initialize. - for (SVFIR::const_iterator it = pag->begin(); it != pag->end(); ++it) - { - if (SVFUtil::isa(it->second)) equivalentObject[it->first] = it->first; - } - - assert(!Options::OPTSVFG() && "VFS: -opt-svfg not currently supported with VFS."); + consumeCache = { 0, nullptr, false }; + yieldCache = { 0, nullptr, false }; } void VersionedFlowSensitive::initialize() @@ -51,15 +42,11 @@ void VersionedFlowSensitive::initialize() vPtD = getVersionedPTDataTy(); - buildIsStoreLoadMaps(); - buildDeltaMaps(); - consume.resize(svfg->getTotalNodeNum()); - yield.resize(svfg->getTotalNodeNum()); - prelabel(); meldLabel(); + mapMeldVersions(); - removeAllIndirectSVFGEdges(); + determineReliance(); } void VersionedFlowSensitive::finalize() @@ -83,9 +70,10 @@ void VersionedFlowSensitive::prelabel(void) // l: *p = q. // If p points to o (Andersen's), l yields a new version for o. NodeID p = stn->getPAGDstNodeID(); + ObjToMeldVersionMap &myl = meldYield[l]; for (NodeID o : ander->getPts(p)) { - prelabeledObjects.insert(o); + myl[o] = newMeldVersion(o); } vWorklist.push(l); @@ -101,9 +89,10 @@ void VersionedFlowSensitive::prelabel(void) const MRSVFGNode *mr = SVFUtil::dyn_cast(ln); if (mr != nullptr) { + ObjToMeldVersionMap &mcl = meldConsume[l]; for (const NodeID o : mr->getPointsTo()) { - prelabeledObjects.insert(o); + mcl[o] = newMeldVersion(o); } // Push into worklist because its consume == its yield. @@ -117,440 +106,220 @@ void VersionedFlowSensitive::prelabel(void) prelabelingTime = (end - start) / TIMEINTERVAL; } -void VersionedFlowSensitive::meldLabel(void) -{ +void VersionedFlowSensitive::meldLabel(void) { double start = stat->getClk(true); - assert(Options::VersioningThreads() > 0 && "VFS::meldLabel: number of versioning threads must be > 0!"); - - // Nodes which have at least one object on them given a prelabel + the Andersen's points-to - // set of interest so we don't keep calling getPts. For Store nodes, we'll fill that in, for - // MR nodes, we won't as its getPointsTo is cheap. - // TODO: preferably we cache both for ease and to avoid the dyn_cast/isa, but Andersen's points-to - // sets are PointsTo and MR's sets are NodeBS, which are incompatible types. Maybe when we can - // use std::option. - std::vector> prelabeledNodes; - // Fast query for the above. - std::vector isPrelabeled(svfg->getTotalNodeNum(), false); - while (!vWorklist.empty()) - { - const NodeID n = vWorklist.pop(); - isPrelabeled[n] = true; + while (!vWorklist.empty()) { + NodeID l = vWorklist.pop(); + const SVFGNode *ln = svfg->getSVFGNode(l); - const SVFGNode *sn = svfg->getSVFGNode(n); - const PointsTo *nPts = nullptr; - if (const StoreSVFGNode *store = SVFUtil::dyn_cast(sn)) - { - const NodeID p = store->getPAGDstNodeID(); - nPts = &(this->ander->getPts(p)); - } + // Propagate l's y to lp's c for all l --o--> lp. + for (const SVFGEdge *e : ln->getOutEdges()) { + const IndirectSVFGEdge *ie = SVFUtil::dyn_cast(e); + if (!ie) continue; + + NodeID lp = ie->getDstNode()->getId(); + // Delta nodes had c set already and they are permanent. + if (delta(lp)) continue; + + bool lpIsStore = SVFUtil::isa(svfg->getSVFGNode(lp)); + // Consume and yield are the same at non-stores, so ignore any self-loop + // at a non-store. + if (l == lp && !lpIsStore) continue; + + // At stores yield != consume, otherwise they are the same (so just use meldConsume). + const ObjToMeldVersionMap &myl = SVFUtil::isa(ln) ? meldYield[l] : meldConsume[l]; + ObjToMeldVersionMap &mclp = meldConsume[lp]; + bool yieldChanged = false; + for (NodeID o : ie->getPointsTo()) { + ObjToMeldVersionMap::const_iterator myloIt = myl.find(o); + if (myloIt == myl.end()) continue; + + // Yield == consume for non-stores, so when consume is updated, so is yield. + // For stores, yield was already set, and it's static. + yieldChanged = (meld(mclp[o], myloIt->second) && !lpIsStore) || yieldChanged; + } - prelabeledNodes.push_back(std::make_pair(sn, nPts)); + if (yieldChanged) vWorklist.push(lp); + } } - // Delta, delta source, store, and load nodes, which require versions during - // solving, unlike other nodes with which we can make do with the reliance map. - std::vector nodesWhichNeedVersions; - for (SVFG::const_iterator it = svfg->begin(); it != svfg->end(); ++it) - { - const NodeID n = it->first; - if (delta(n) || deltaSource(n) || isStore(n) || isLoad(n)) nodesWhichNeedVersions.push_back(n); - } + double end = stat->getClk(true); + meldLabelingTime = (end - start) / TIMEINTERVAL; +} + +bool VersionedFlowSensitive::meld(MeldVersion &mv1, const MeldVersion &mv2) +{ + // Meld operator is union of bit vectors. + return mv1 |= mv2; +} - std::mutex *versionMutexes = new std::mutex[nodesWhichNeedVersions.size()]; +void VersionedFlowSensitive::mapMeldVersions(void) +{ + double start = stat->getClk(true); - // Map of footprints to the canonical object "owning" the footprint. - Map, NodeID> footprintOwner; + // We want to uniquely map MeldVersions (SparseBitVectors) to a Version (unsigned integer). + // mvv keeps track, and curVersion is used to generate new Versions. + Map mvv; + Version curVersion = 1; - std::queue objectQueue; - for (const NodeID o : prelabeledObjects) + // meldConsume -> consume. + for (LocMeldVersionMap::value_type &lomv : meldConsume) { - // "Touch" maps with o so we don't need to lock on them. - versionReliance[o]; - stmtReliance[o]; - objectQueue.push(o); + NodeID l = lomv.first; + bool lIsStore = SVFUtil::isa(svfg->getSVFGNode(l)); + for (ObjToMeldVersionMap::value_type &omv : lomv.second) + { + NodeID o = omv.first; + MeldVersion &mv = omv.second; + + Map::const_iterator foundVersion = mvv.find(mv); + // If a mapping for foudnVersion exists, use it, otherwise create a new Version (++), + // keep track of it ([mv]), and use that. + Version v = foundVersion == mvv.end() ? mvv[mv] = ++curVersion : foundVersion->second; + setConsume(l, o, v); + // At non-stores, consume == yield. + // Unlike meldYield, we use yield for all yields, not just where consume != yield. + // This affords simplicity later. meldYield is expensive to explicitly represent + // always, unlike yield. + if (!lIsStore) setYield(l, o, v); + } } - std::mutex objectQueueMutex; - std::mutex footprintOwnerMutex; - - auto meldVersionWorker = [this, &footprintOwner, &objectQueue, - &objectQueueMutex, &footprintOwnerMutex, &versionMutexes, - &prelabeledNodes, &isPrelabeled, &nodesWhichNeedVersions] - (const unsigned thread) + // meldYield -> yield. + for (LocMeldVersionMap::value_type &lomv : meldYield) { - while (true) + NodeID l = lomv.first; + for (ObjToMeldVersionMap::value_type &omv : lomv.second) { - NodeID o; - { - std::lock_guard guard(objectQueueMutex); - // No more objects? Done. - if (objectQueue.empty()) return; - o = objectQueue.front(); - objectQueue.pop(); - } - - // 1. Compute the SCCs for the nodes on the graph overlay of o. - // For starting nodes, we only need those which did prelabeling for o specifically. - // TODO: maybe we should move this to prelabel with a map (o -> starting nodes). - std::vector osStartingNodes; - for (std::pair snPts : prelabeledNodes) - { - const SVFGNode *sn = snPts.first; - const PointsTo *pts = snPts.second; - if (pts != nullptr) - { - if (pts->test(o)) osStartingNodes.push_back(sn); - } - else if (const MRSVFGNode *mr = SVFUtil::dyn_cast(sn)) - { - if (mr->getPointsTo().test(o)) osStartingNodes.push_back(sn); - } - else - { - assert(false && "VFS::meldLabel: unexpected prelabeled node!"); - } - } - - std::vector partOf; - std::vector footprint; - unsigned numSCCs = SCC::detectSCCs(this, this->svfg, o, osStartingNodes, partOf, footprint); - - // 2. Skip any further processing of a footprint we have seen before. - { - std::lock_guard guard(footprintOwnerMutex); - const Map, NodeID>::const_iterator canonOwner - = footprintOwner.find(footprint); - if (canonOwner == footprintOwner.end()) - { - this->equivalentObject[o] = o; - footprintOwner[footprint] = o; - } - else - { - this->equivalentObject[o] = canonOwner->second; - // Same version and stmt reliance as the canonical. During solving we cannot just reuse - // the canonical object's reliance because it may change due to on-the-fly call graph - // construction. Something like copy-on-write could be good... probably negligible. - this->versionReliance.at(o) = this->versionReliance.at(canonOwner->second); - this->stmtReliance.at(o) = this->stmtReliance.at(canonOwner->second); - continue; - } - } - - // 3. a. Initialise the MeldVersion of prelabeled nodes (SCCs). - // b. Initialise a todo list of all the nodes we need to version, - // sorted according to topological order. - // We will use a map of sccs to meld versions for what is consumed. - std::vector sccToMeldVersion(numSCCs); - // At stores, what is consumed is different to what is yielded, so we - // maintain that separately. - Map storesYieldedMeldVersion; - // SVFG nodes of interest -- those part of an SCC from the starting nodes. - std::vector todoList; - unsigned bit = 0; - // To calculate reachable nodes, we can see what nodes n exist where - // partOf[n] != -1. Since the SVFG can be large this can be expensive. - // Instead, we can gather this from the edges in the footprint and - // the starting nodes (incase such nodes have no edges). - // TODO: should be able to do this better: too many redundant inserts. - Set reachableNodes; - for (const SVFGNode *sn : osStartingNodes) reachableNodes.insert(sn->getId()); - for (const SVFGEdge *se : footprint) - { - reachableNodes.insert(se->getSrcNode()->getId()); - reachableNodes.insert(se->getDstNode()->getId()); - } - - for (const NodeID n : reachableNodes) - { - if (isPrelabeled[n]) - { - if (this->isStore(n)) storesYieldedMeldVersion[n].set(bit); - else sccToMeldVersion[partOf[n]].set(bit); - ++bit; - } - - todoList.push_back(n); - } - - // Sort topologically so each nodes is only visited once. - auto cmp = [&partOf](const NodeID a, const NodeID b) - { - return partOf[a] > partOf[b]; - }; - std::sort(todoList.begin(), todoList.end(), cmp); - - // 4. a. Do meld versioning. - // b. Determine SCC reliances. - // c. Build a footprint for o (all edges which it is found on). - // d. Determine which SCCs belong to stores. - - // sccReliance[x] = { y_1, y_2, ... } if there exists an edge from a node - // in SCC x to SCC y_i. - std::vector> sccReliance(numSCCs); - // Maps SCC to the store it corresponds to or -1 if it doesn't. TODO: unsigned vs signed -- nasty. - std::vector storeSCC(numSCCs, -1); - for (size_t i = 0; i < todoList.size(); ++i) - { - const NodeID n = todoList[i]; - const SVFGNode *sn = this->svfg->getSVFGNode(n); - const bool nIsStore = this->isStore(n); - - int nSCC = partOf[n]; - if (nIsStore) storeSCC[nSCC] = n; - - // Given n -> m, the yielded version of n will be melded into m. - // For stores, that is in storesYieldedMeldVersion, otherwise, consume == yield and - // we can just use sccToMeldVersion. - const MeldVersion &nMV = nIsStore ? storesYieldedMeldVersion[n] : sccToMeldVersion[nSCC]; - for (const SVFGEdge *e : sn->getOutEdges()) - { - const IndirectSVFGEdge *ie = SVFUtil::dyn_cast(e); - if (!ie) continue; - - const NodeID m = ie->getDstNode()->getId(); - // Ignoreedges which don't involve o. - if (!ie->getPointsTo().test(o)) continue; - - int mSCC = partOf[m]; - - // There is an edge from the SCC n belongs to that m belongs to. - sccReliance[nSCC].insert(mSCC); - - // Ignore edges to delta nodes (prelabeled consume). - // No point propagating when n's SCC == m's SCC (same meld version there) - // except when it is a store, because we are actually propagating n's yielded - // into m's consumed. Store nodes are in their own SCCs, so it is a self - // loop on a store node. - if (!this->delta(m) && (nSCC != mSCC || nIsStore)) - { - sccToMeldVersion[mSCC] |= nMV; - } - } - } - - // 5. Transform meld versions belonging to SCCs into versions. - Map mvv; - std::vector sccToVersion(numSCCs, invalidVersion); - Version curVersion = 0; - for (u32_t scc = 0; scc < sccToMeldVersion.size(); ++scc) - { - const MeldVersion &mv = sccToMeldVersion[scc]; - Map::const_iterator foundVersion = mvv.find(mv); - Version v = foundVersion == mvv.end() ? mvv[mv] = ++curVersion : foundVersion->second; - sccToVersion[scc] = v; - } - - sccToMeldVersion.clear(); - - // Same for storesYieldedMeldVersion. - Map storesYieldedVersion; - for (auto const& nmv : storesYieldedMeldVersion) - { - const NodeID n = nmv.first; - const MeldVersion &mv = nmv.second; + NodeID o = omv.first; + MeldVersion &mv = omv.second; - Map::const_iterator foundVersion = mvv.find(mv); - Version v = foundVersion == mvv.end() ? mvv[mv] = ++curVersion : foundVersion->second; - storesYieldedVersion[n] = v; - } - - storesYieldedMeldVersion.clear(); - - mvv.clear(); - - // 6. From SCC reliance, determine version reliances. - Map> &osVersionReliance = this->versionReliance.at(o); - for (u32_t scc = 0; scc < numSCCs; ++scc) - { - if (sccReliance[scc].empty()) continue; - - // Some consume relies on a yield. When it's a store, we need to pick whether to - // use the consume or yield unlike when it is not because they are the same. - const Version version - = storeSCC[scc] != -1 ? storesYieldedVersion[storeSCC[scc]] : sccToVersion[scc]; - - std::vector &reliantVersions = osVersionReliance[version]; - for (const int reliantSCC : sccReliance[scc]) - { - const Version reliantVersion = sccToVersion[reliantSCC]; - if (version != reliantVersion) - { - // sccReliance is a set, no need to worry about duplicates. - reliantVersions.push_back(reliantVersion); - } - } - } - - // 7. a. Save versions for nodes which need them. - // b. Fill in stmtReliance. - // TODO: maybe randomize iteration order for less contention? Needs profiling. - Map &osStmtReliance = this->stmtReliance.at(o); - for (size_t i = 0; i < nodesWhichNeedVersions.size(); ++i) - { - const NodeID n = nodesWhichNeedVersions[i]; - std::mutex &mutex = versionMutexes[i]; - - const int scc = partOf[n]; - if (scc == -1) continue; - - std::lock_guard guard(mutex); - - const Version c = sccToVersion[scc]; - if (c != invalidVersion) - { - this->setConsume(n, o, c); - if (this->isStore(n) || this->isLoad(n)) osStmtReliance[c].set(n); - } - - if (this->isStore(n)) - { - const Map::const_iterator yIt = storesYieldedVersion.find(n); - if (yIt != storesYieldedVersion.end()) this->setYield(n, o, yIt->second); - } - } + Map::const_iterator foundVersion = mvv.find(mv); + Version v = foundVersion == mvv.end() ? mvv[mv] = ++curVersion : foundVersion->second; + setYield(l, o, v); } - }; - - std::vector workers; - for (unsigned i = 0; i < Options::VersioningThreads(); ++i) workers.push_back(std::thread(meldVersionWorker, i)); - for (std::thread &worker : workers) worker.join(); + } - delete[] versionMutexes; + // No longer necessary. + meldConsume.clear(); + meldYield.clear(); double end = stat->getClk(true); - meldLabelingTime = (end - start) / TIMEINTERVAL; -} - -bool VersionedFlowSensitive::meld(MeldVersion &mv1, const MeldVersion &mv2) -{ - // Meld operator is union of bit vectors. - return mv1 |= mv2; + meldMappingTime += (end - start) / TIMEINTERVAL; } -bool VersionedFlowSensitive::delta(const NodeID l) const +bool VersionedFlowSensitive::delta(NodeID l) const { - assert(l < deltaMap.size() && "VFS::delta: deltaMap is missing SVFG nodes!"); - return deltaMap[l]; -} + // Whether a node is a delta node or not. Decent boon to performance. + static Map deltaCache; -bool VersionedFlowSensitive::deltaSource(const NodeID l) const -{ - assert(l < deltaSourceMap.size() && "VFS::delta: deltaSourceMap is missing SVFG nodes!"); - return deltaSourceMap[l]; -} + Map::const_iterator isDeltaIt = deltaCache.find(l); + if (isDeltaIt != deltaCache.end()) return isDeltaIt->second; -void VersionedFlowSensitive::buildIsStoreLoadMaps(void) -{ - isStoreMap.resize(svfg->getTotalNodeNum(), false); - isLoadMap.resize(svfg->getTotalNodeNum(), false); - for (SVFG::const_iterator it = svfg->begin(); it != svfg->end(); ++it) + const SVFGNode *s = svfg->getSVFGNode(l); + // Cases: + // * Function entry: can get new incoming indirect edges through ind. callsites. + // * Callsite returns: can get new incoming indirect edges if the callsite is indirect. + // * Otherwise: static. + bool isDelta = false; + if (const SVFFunction *fn = svfg->isFunEntrySVFGNode(s)) { - if (SVFUtil::isa(it->second)) isStoreMap[it->first] = true; - else if (SVFUtil::isa(it->second)) isLoadMap[it->first] = true; + PTACallGraphEdge::CallInstSet callsites; + /// use pre-analysis call graph to approximate all potential callsites + ander->getPTACallGraph()->getIndCallSitesInvokingCallee(fn, callsites); + isDelta = !callsites.empty(); + } + else if (const CallBlockNode *cbn = svfg->isCallSiteRetSVFGNode(s)) + { + isDelta = cbn->isIndirectCall(); } -} -bool VersionedFlowSensitive::isStore(const NodeID l) const -{ - assert(l < isStoreMap.size() && "VFS::isStore: isStoreMap is missing SVFG nodes!"); - return isStoreMap[l]; + deltaCache[l] = isDelta; + return isDelta; } -bool VersionedFlowSensitive::isLoad(const NodeID l) const + +VersionedFlowSensitive::MeldVersion VersionedFlowSensitive::newMeldVersion(NodeID o) { - assert(l < isLoadMap.size() && "VFS::isLoad: isLoadMap is missing SVFG nodes!"); - return isLoadMap[l]; + ++numPrelabelVersions; + MeldVersion nv; + nv.set(++meldVersions[o]); + return nv; } -void VersionedFlowSensitive::buildDeltaMaps(void) +void VersionedFlowSensitive::determineReliance(void) { - deltaMap.resize(svfg->getTotalNodeNum(), false); - - // Call block nodes corresponding to all delta nodes. - Set deltaCBNs; + // Use a set-based version to build, then we'll move things to vectors. + Map>> setVersionReliance; - for (SVFG::const_iterator it = svfg->begin(); it != svfg->end(); ++it) + double start = stat->getClk(true); + for (SVFG::iterator it = svfg->begin(); it != svfg->end(); ++it) { - const NodeID l = it->first; - const SVFGNode *s = it->second; - - // Cases: - // * Function entry: can get new incoming indirect edges through ind. callsites. - // * Callsite returns: can get new incoming indirect edges if the callsite is indirect. - // * Otherwise: static. - bool isDelta = false; - if (const SVFFunction *fn = svfg->isFunEntrySVFGNode(s)) + NodeID l = it->first; + const SVFGNode *ln = it->second; + for (const SVFGEdge *e : ln->getOutEdges()) { - PTACallGraphEdge::CallInstSet callsites; - /// use pre-analysis call graph to approximate all potential callsites - ander->getPTACallGraph()->getIndCallSitesInvokingCallee(fn, callsites); - isDelta = !callsites.empty(); + const IndirectSVFGEdge *ie = SVFUtil::dyn_cast(e); + if (!ie) continue; - if (isDelta) + for (const NodeID o : ie->getPointsTo()) { - // TODO: could we use deltaCBNs in the call above, avoiding this loop? - for (const CallICFGNode *cbn : callsites) deltaCBNs.insert(cbn); - } - } - else if (const CallICFGNode *cbn = svfg->isCallSiteRetSVFGNode(s)) - { - isDelta = cbn->isIndirectCall(); - if (isDelta) deltaCBNs.insert(cbn); - } + // Given l --o--> lp, c(o) at lp relies on y(o) at l. + const NodeID lp = ie->getDstNode()->getId(); - deltaMap[l] = isDelta; - } - - deltaSourceMap.resize(svfg->getTotalNodeNum(), false); + const Version y = getYield(l, o); + if (y == invalidVersion) continue; + const Version cp = getConsume(lp, o); + if (cp == invalidVersion) continue; - for (SVFG::const_iterator it = svfg->begin(); it != svfg->end(); ++it) - { - const NodeID l = it->first; - const SVFGNode *s = it->second; + if (cp != y) setVersionReliance[o][y].insert(cp); + } + } - if (const CallICFGNode *cbn = SVFUtil::dyn_cast(s->getICFGNode())) + // When an object/version points-to set changes, these nodes need to know. + if (SVFUtil::isa(ln) || SVFUtil::isa(ln)) { - if (deltaCBNs.find(cbn) != deltaCBNs.end()) deltaSourceMap[l] = true; + const LocVersionMap::const_iterator lovmIt = consume.find(l); + if (lovmIt != consume.end()) + { + for (const ObjToVersionMap::value_type &ov : lovmIt->second) + { + const NodeID o = ov.first; + const Version v = ov.second; + stmtReliance[o][v].set(l); + } + } } - - // TODO: this is an over-approximation but it sound, marking every formal out as - // a delta-source. - if (SVFUtil::isa(s)) deltaSourceMap[l] = true; } -} -void VersionedFlowSensitive::removeAllIndirectSVFGEdges(void) -{ - for (SVFG::iterator nodeIt = svfg->begin(); nodeIt != svfg->end(); ++nodeIt) + for (const std::pair>> &ovvs : setVersionReliance) { - SVFGNode *sn = nodeIt->second; - - const SVFGEdgeSetTy &inEdges = sn->getInEdges(); - std::vector toDeleteFromIn; - for (SVFGEdge *e : inEdges) + const NodeID o = ovvs.first; + Map> &osRelying = versionReliance[o]; + for (const std::pair> &vvs : ovvs.second) { - if (SVFUtil::isa(e)) toDeleteFromIn.push_back(e); + const Version v = vvs.first; + const Set &vs = vvs.second; + osRelying[v] = std::vector(vs.begin(), vs.end()); } - - for (SVFGEdge *e : toDeleteFromIn) svfg->removeSVFGEdge(e); - - // Only need to iterate over incoming edges for each node because edges - // will be deleted from in/out through removeSVFGEdge. } - setGraph(svfg); + double end = stat->getClk(true); + relianceTime = (end - start) / TIMEINTERVAL; } void VersionedFlowSensitive::propagateVersion(NodeID o, Version v) { double start = stat->getClk(); - const std::vector &reliantVersions = getReliantVersions(o, v); - for (Version r : reliantVersions) + Map>::iterator relyingVersions = versionReliance[o].find(v); + if (relyingVersions != versionReliance[o].end()) { - propagateVersion(o, v, r, false); + for (Version r : relyingVersions->second) + { + propagateVersion(o, v, r, false); + } } double end = stat->getClk(); @@ -573,14 +342,13 @@ void VersionedFlowSensitive::propagateVersion(const NodeID o, const Version v, c { dvp = svfg->addDummyVersionPropSVFGNode(o, vp); versionedVarToPropNode[dstVar] = dvp; - } - else dvp = dvpIt->second; + } else dvp = dvpIt->second; assert(dvp != nullptr && "VFS::propagateVersion: propagation dummy node not found?"); pushIntoWorklist(dvp->getId()); // Notify nodes which rely on o:vp that it changed. - for (NodeID s : getStmtReliance(o, vp)) pushIntoWorklist(s); + for (NodeID s : stmtReliance[o][vp]) pushIntoWorklist(s); } double end = time ? stat->getClk() : 0.0; @@ -610,9 +378,11 @@ void VersionedFlowSensitive::updateConnectedNodes(const SVFGEdgeSetTy& newEdges) NodeID src = e->getSrcNode()->getId(); NodeID dst = dstNode->getId(); + assert(delta(dst) && "VFS::updateConnectedNodes: new edges should be to delta nodes!"); + if (SVFUtil::isa(dstNode) - || SVFUtil::isa(dstNode) - || SVFUtil::isa(dstNode)) + || SVFUtil::isa(dstNode) + || SVFUtil::isa(dstNode)) { pushIntoWorklist(dst); } @@ -621,10 +391,7 @@ void VersionedFlowSensitive::updateConnectedNodes(const SVFGEdgeSetTy& newEdges) const IndirectSVFGEdge *ie = SVFUtil::dyn_cast(e); assert(ie != nullptr && "VFS::updateConnectedNodes: given direct edge?"); - assert(delta(dst) && "VFS::updateConnectedNodes: new edges should be to delta nodes!"); - assert(deltaSource(src) && "VFS::updateConnectedNodes: new indirect edges should be from delta source nodes!"); - - const NodeBS &ept = ie->getPointsTo(); + const PointsTo &ept = ie->getPointsTo(); // For every o, such that src --o--> dst, we need to set up reliance (and propagate). for (const NodeID o : ept) { @@ -633,7 +400,7 @@ void VersionedFlowSensitive::updateConnectedNodes(const SVFGEdgeSetTy& newEdges) Version dstC = getConsume(dst, o); if (dstC == invalidVersion) continue; - std::vector &versionsRelyingOnSrcY = getReliantVersions(o, srcY); + std::vector &versionsRelyingOnSrcY = versionReliance[o][srcY]; if (std::find(versionsRelyingOnSrcY.begin(), versionsRelyingOnSrcY.end(), dstC) == versionsRelyingOnSrcY.end()) { versionsRelyingOnSrcY.push_back(dstC); @@ -656,35 +423,32 @@ bool VersionedFlowSensitive::processLoad(const LoadSVFGNode* load) NodeID q = load->getPAGSrcNodeID(); const PointsTo& qpt = getPts(q); - // p = *q, the type of p must be a pointer - if (load->getPAGDstNode()->isPointer()) + for (NodeID o : qpt) { - for (NodeID o : qpt) - { - if (pag->isConstantObj(o)) continue; + if (pag->isConstantObj(o) || pag->isNonPointerObj(o)) continue; - const Version c = getConsume(l, o); - if (c != invalidVersion && vPtD->unionPts(p, atKey(o, c))) - { - changed = true; - } + const Version c = getConsume(l, o); + if (c != invalidVersion && vPtD->unionPts(p, atKey(o, c))) + { + changed = true; + } - if (isFieldInsensitive(o)) + if (isFieldInsensitive(o)) + { + /// If o is a field-insensitive object, we should also get all field nodes' + /// points-to sets and pass them to p. + const NodeBS& fields = getAllFieldsObjNode(o); + for (NodeID of : fields) { - /// If o is a field-insensitive object, we should also get all field nodes' - /// points-to sets and pass them to p. - const NodeBS& fields = getAllFieldsObjVars(o); - for (NodeID of : fields) + const Version c = getConsume(l, of); + if (c != invalidVersion && vPtD->unionPts(p, atKey(of, c))) { - const Version c = getConsume(l, of); - if (c != invalidVersion && vPtD->unionPts(p, atKey(of, c))) - { - changed = true; - } + changed = true; } } } } + double end = stat->getClk(); loadTime += (end - start) / TIMEINTERVAL; return changed; @@ -710,19 +474,15 @@ bool VersionedFlowSensitive::processStore(const StoreSVFGNode* store) if (!qpt.empty()) { - // *p = q, the type of q must be a pointer - if (store->getPAGSrcNode()->isPointer()) + for (NodeID o : ppt) { - for (NodeID o : ppt) - { - if (pag->isConstantObj(o)) continue; + if (pag->isConstantObj(o) || pag->isNonPointerObj(o)) continue; - const Version y = getYield(l, o); - if (y != invalidVersion && vPtD->unionPts(atKey(o, y), q)) - { - changed = true; - changedObjects.set(o); - } + const Version y = getYield(l, o); + if (y != invalidVersion && vPtD->unionPts(atKey(o, y), q)) + { + changed = true; + changedObjects.set(o); } } } @@ -739,19 +499,23 @@ bool VersionedFlowSensitive::processStore(const StoreSVFGNode* store) // For all objects, perform pts(o:y) = pts(o:y) U pts(o:c) at loc, // except when a strong update is taking place. - for (const ObjToVersionMap::value_type &oc : consume[l]) + const LocVersionMap::const_iterator lovmIt = consume.find(l); + if (lovmIt != consume.end()) { - const NodeID o = oc.first; - const Version c = oc.second; + for (const ObjToVersionMap::value_type &oc : lovmIt->second) + { + const NodeID o = oc.first; + const Version c = oc.second; - // Strong-updated; don't propagate. - if (isSU && o == singleton) continue; + // Strong-updated; don't propagate. + if (isSU && o == singleton) continue; - const Version y = getYield(l, o); - if (y != invalidVersion && vPtD->unionPts(atKey(o, y), atKey(o, c))) - { - changed = true; - changedObjects.set(o); + const Version y = getYield(l, o); + if (y != invalidVersion && vPtD->unionPts(atKey(o, y), atKey(o, c))) + { + changed = true; + changedObjects.set(o); + } } } @@ -770,81 +534,82 @@ bool VersionedFlowSensitive::processStore(const StoreSVFGNode* store) propagateVersion(o, y); // Some o/v pairs changed: statements need to know. - for (NodeID s : getStmtReliance(o, y)) pushIntoWorklist(s); + for (NodeID s : stmtReliance[o][y]) pushIntoWorklist(s); } } return changed; } -void VersionedFlowSensitive::cluster(void) +Version VersionedFlowSensitive::getVersion(const NodeID l, const NodeID o, VersionCache &cache, LocVersionMap &lvm) { - std::vector> keys; - for (SVFIR::iterator pit = pag->begin(); pit != pag->end(); ++pit) + if (cache.valid && l == cache.l) { - unsigned occ = 1; - unsigned v = pit->first; - if (Options::PredictPtOcc() && pag->getObject(v) != nullptr) occ = stmtReliance[v].size() + 1; - assert(occ != 0); - keys.push_back(std::make_pair(v, occ)); + ObjToVersionMap::const_iterator foundVersion = cache.ovm->find(o); + return foundVersion == cache.ovm->end() ? invalidVersion : foundVersion->second; } - PointsTo::MappingPtr nodeMapping = - std::make_shared>(NodeIDAllocator::Clusterer::cluster(ander, keys, candidateMappings, "aux-ander")); - PointsTo::MappingPtr reverseNodeMapping = - std::make_shared>(NodeIDAllocator::Clusterer::getReverseNodeMapping(*nodeMapping)); - - PointsTo::setCurrentBestNodeMapping(nodeMapping, reverseNodeMapping); -} + // Not const because the cache isn't as it is shared with setVersion. + LocVersionMap::iterator foundObjToVersionMap = lvm.find(l); + // If there are no obj -> version maps at l, then obviously there is no version for o. + if (foundObjToVersionMap == lvm.end()) return invalidVersion; -Version VersionedFlowSensitive::getVersion(const NodeID l, const NodeID o, const LocVersionMap &lvm) const -{ - const Map::const_iterator canonObjectIt = equivalentObject.find(o); - const NodeID op = canonObjectIt == equivalentObject.end() ? o : canonObjectIt->second; + // We can cache lvm[l]. + cache.l = l; + cache.ovm = &foundObjToVersionMap->second; + cache.valid = true; - const ObjToVersionMap &ovm = lvm[l]; - const ObjToVersionMap::const_iterator foundVersion = ovm.find(op); - return foundVersion == ovm.end() ? invalidVersion : foundVersion->second; + ObjToVersionMap::const_iterator foundVersion = cache.ovm->find(o); + return foundVersion == cache.ovm->end() ? invalidVersion : foundVersion->second; } -Version VersionedFlowSensitive::getConsume(const NodeID l, const NodeID o) const +Version VersionedFlowSensitive::getConsume(const NodeID l, const NodeID o) { - return getVersion(l, o, consume); + return getVersion(l, o, consumeCache, consume); } -Version VersionedFlowSensitive::getYield(const NodeID l, const NodeID o) const +Version VersionedFlowSensitive::getYield(const NodeID l, const NodeID o) { - // Non-store: consume == yield. - if (isStore(l)) return getVersion(l, o, yield); - else return getVersion(l, o, consume); + return getVersion(l, o, yieldCache, yield); } -void VersionedFlowSensitive::setVersion(const NodeID l, const NodeID o, const Version v, LocVersionMap &lvm) +void VersionedFlowSensitive::setVersion(const NodeID l, const NodeID o, const Version v, VersionCache &cache, LocVersionMap &lvm) { + if (cache.valid && l == cache.l) + { + (*cache.ovm)[o] = v; + return; + } + + // This can invalidate the cache but we're updating it in the next statement anyway. ObjToVersionMap &ovm = lvm[l]; + + // We can cache lvm[l]. + cache.l = l; + cache.ovm = &ovm; + cache.valid = true; + ovm[o] = v; } void VersionedFlowSensitive::setConsume(const NodeID l, const NodeID o, const Version v) { - setVersion(l, o, v, consume); + setVersion(l, o, v, consumeCache, consume); } void VersionedFlowSensitive::setYield(const NodeID l, const NodeID o, const Version v) { - // Non-store: consume == yield. - if (isStore(l)) setVersion(l, o, v, yield); - else setVersion(l, o, v, consume); + setVersion(l, o, v, yieldCache, yield); } -std::vector &VersionedFlowSensitive::getReliantVersions(const NodeID o, const Version v) +void VersionedFlowSensitive::invalidateYieldCache(void) { - return versionReliance[o][v]; + yieldCache.valid = false; } -NodeBS &VersionedFlowSensitive::getStmtReliance(const NodeID o, const Version v) +void VersionedFlowSensitive::invalidateConsumeCache(void) { - return stmtReliance[o][v]; + consumeCache.valid = false; } void VersionedFlowSensitive::dumpReliances(void) const @@ -854,7 +619,7 @@ void VersionedFlowSensitive::dumpReliances(void) const { NodeID o = ovrv.first; SVFUtil::outs() << " Object " << o << "\n"; - for (const Map>::value_type& vrv : ovrv.second) + for (const Map>::value_type vrv : ovrv.second) { Version v = vrv.first; SVFUtil::outs() << " Version " << v << " is a reliance for: "; @@ -911,12 +676,9 @@ void VersionedFlowSensitive::dumpLocVersionMaps(void) const { const NodeID loc = it->first; bool locPrinted = false; - for (const LocVersionMap *lvm : - { - &consume, &yield - }) + for (const LocVersionMap *lvm : { &consume, &yield }) { - if (lvm->at(loc).empty()) continue; + if (lvm->find(loc) == lvm->end() || lvm->at(loc).empty()) continue; if (!locPrinted) { SVFUtil::outs() << " " << "SVFG node " << loc << "\n"; @@ -957,249 +719,3 @@ void VersionedFlowSensitive::dumpMeldVersion(MeldVersion &v) SVFUtil::outs() << " ]"; } - -void VersionedFlowSensitive::readPtsFromFile(const std::string& filename) -{ - /// Initialization for the Solver - initialize(); - /// Load the pts from file - if(!filename.empty()) - { - SVFUtil::outs() << "Loading versioned pointer analysis results from '" << filename << "'..."; - - std::ifstream F(filename.c_str()); - if (!F.is_open()) - { - SVFUtil::outs() << " error opening file for reading!\n"; - return ; - } - readAndSetObjFieldSensitivity(F,"------"); - - readVersionedAnalysisResultFromFile(F); - - readPtsResultFromFile(F); - - readGepObjVarMapFromFile(F); - - readAndSetObjFieldSensitivity(F,""); - - // Update callgraph - updateCallGraph(pag->getIndirectCallsites()); - - F.close(); - SVFUtil::outs() << "\n"; - } - - /// finalize the analysis - finalize(); -} - -void VersionedFlowSensitive::solveAndwritePtsToFile(const std::string& filename) -{ - /// Initialization for the Solver - initialize(); - if(!filename.empty()) - writeObjVarToFile(filename); - solveConstraints(); - if(!filename.empty()) - { - writeVersionedAnalysisResultToFile(filename); - writeToFile(filename); - } - /// finalize the analysis - finalize(); -} - -void VersionedFlowSensitive::writeVersionedAnalysisResultToFile(const std::string& filename) -{ - SVFUtil::outs() << "Storing Versioned Analysis Result to '" << filename << "'..."; - std::error_code err; - std::fstream f(filename.c_str(), std::ios_base::app); - if (!f.good()) - { - SVFUtil::outs() << " error opening file for writing!\n"; - return; - } - - for (const VersionedFlowSensitive::LocVersionMap *lvm : - { - &this->consume, &this->yield - }) - { - for (const VersionedFlowSensitive::ObjToVersionMap &lov : *lvm) - { - for (const VersionedFlowSensitive::ObjToVersionMap::value_type &ov : lov) - { - const NodeID o = ov.first; - const Version v = ov.second; - if (vPtD->getPts(atKey(o, v)).empty()) continue; - - f <<"[ " < { "; - const PointsTo &ovPts = vPtD->getPts(atKey(o, v)); - if (!ovPts.empty()) - { - for (NodeID n: ovPts) - { - f << n << " "; - } - } - else - { - f << " "; - } - f << "}\n"; - } - } - } - - f << "---VERSIONED---\n"; - - f.close(); - if (f.good()) - { - SVFUtil::outs() << "\n"; - return; - } -} - -void VersionedFlowSensitive::readVersionedAnalysisResultFromFile(std::ifstream& F) -{ - std::string line; - std::string delimiter1 = " -> { "; - std::string delimiter2 = " }"; - while (F.good()) - { - // Parse a single line in the form of "[ var version ] -> { obj1 obj2 obj3 }" - getline(F, line); - if (line == "---VERSIONED---") break; - std::string pair = line.substr(line.find("[ ")+1, line.find(" ]")); - - // Parse VersionKey - std::istringstream ss(pair); - NodeID nodeID; - Version nodeVersion; - ss>> nodeID >> nodeVersion; - VersionedVar keyPair = atKey(nodeID,nodeVersion); - - // Parse Point-to set - size_t pos = line.find(delimiter1); - if (pos == std::string::npos) break; - if (line.back() != '}') break; - pos = pos + delimiter1.length(); - size_t len = line.length() - pos - delimiter2.length(); - std::string objs = line.substr(pos, len); - PointsTo dstPts; - if (!objs.empty()) - { - std::istringstream pt(objs); - NodeID obj; - while (pt.good()) - { - pt >> obj; - dstPts.set(obj); - } - } - - // union point-to reuslt - vPtD->unionPts(keyPair, dstPts); - } - -} - -unsigned VersionedFlowSensitive::SCC::detectSCCs(VersionedFlowSensitive *vfs, - const SVFG *svfg, const NodeID object, - const std::vector &startingNodes, - std::vector &partOf, - std::vector &footprint) -{ - partOf.resize(svfg->getTotalNodeNum()); - std::fill(partOf.begin(), partOf.end(), -1); - footprint.clear(); - - std::vector nodeData(svfg->getTotalNodeNum(), { -1, -1, false}); - std::stack stack; - - int index = 0; - int currentSCC = 0; - - for (const SVFGNode *v : startingNodes) - { - if (nodeData[v->getId()].index == -1) - { - visit(vfs, object, partOf, footprint, nodeData, stack, index, currentSCC, v); - } - } - - // Make sure footprints with the same edges pass ==/hash the same. - std::sort(footprint.begin(), footprint.end()); - - return currentSCC; -} - -void VersionedFlowSensitive::SCC::visit(VersionedFlowSensitive *vfs, - const NodeID object, - std::vector &partOf, - std::vector &footprint, - std::vector &nodeData, - std::stack &stack, - int &index, - int ¤tSCC, - const SVFGNode *v) -{ - const NodeID vId = v->getId(); - - nodeData[vId].index = index; - nodeData[vId].lowlink = index; - ++index; - - stack.push(v); - nodeData[vId].onStack = true; - - for (const SVFGEdge *e : v->getOutEdges()) - { - const IndirectSVFGEdge *ie = SVFUtil::dyn_cast(e); - if (!ie) continue; - - const SVFGNode *w = ie->getDstNode(); - const NodeID wId = w->getId(); - - // If object is not part of the edge, there is no edge from v to w. - if (!ie->getPointsTo().test(object)) continue; - - // Even if we don't count edges to stores and deltas for SCCs' sake, they - // are relevant to the footprint as a propagation still occurs over such edges. - footprint.push_back(ie); - - // Ignore edges to delta nodes because they are prelabeled so cannot - // be part of the SCC v is in (already in nodesTodo from the prelabeled set). - // Similarly, store nodes. - if (vfs->delta(wId) || vfs->isStore(wId)) continue; - - if (nodeData[wId].index == -1) - { - visit(vfs, object, partOf, footprint, nodeData, stack, index, currentSCC, w); - nodeData[vId].lowlink = std::min(nodeData[vId].lowlink, nodeData[wId].lowlink); - } - else if (nodeData[wId].onStack) - { - nodeData[vId].lowlink = std::min(nodeData[vId].lowlink, nodeData[wId].index); - } - } - - if (nodeData[vId].lowlink == nodeData[vId].index) - { - const SVFGNode *w = nullptr; - do - { - w = stack.top(); - stack.pop(); - const NodeID wId = w->getId(); - nodeData[wId].onStack = false; - partOf[wId] = currentSCC; - } - while (w != v); - - // For the next SCC. - ++currentSCC; - } -} diff --git a/svf/lib/WPA/VersionedFlowSensitiveStat.cpp b/svf/lib/WPA/VersionedFlowSensitiveStat.cpp index 042652cd7..9dd8eb593 100644 --- a/svf/lib/WPA/VersionedFlowSensitiveStat.cpp +++ b/svf/lib/WPA/VersionedFlowSensitiveStat.cpp @@ -7,29 +7,28 @@ * Author: mbarbar */ -#include "Util/SVFUtil.h" +#include "SVF-FE/LLVMUtil.h" #include "WPA/WPAStat.h" #include "WPA/VersionedFlowSensitive.h" -#include "MemoryModel/PointsTo.h" using namespace SVF; using namespace SVFUtil; void VersionedFlowSensitiveStat::clearStat() { - _NumVersions = 0; - _MaxVersions = 0; - _NumNonEmptyVersions = 0; - _NumSingleVersion = 0; - _NumUsedVersions = 0; - _NumEmptyVersions = 0; - _MaxPtsSize = 0; - _MaxTopLvlPtsSize = 0; - _MaxVersionPtsSize = 0; - _TotalPtsSize = 0; - _AvgPtsSize = 0.0; - _AvgTopLvlPtsSize = 0.0; - _AvgVersionPtsSize = 0.0; + _NumVersions = 0; + _MaxVersions = 0; + _NumNonEmptyVersions = 0; + _NumSingleVersion = 0; + _NumUsedVersions = 0; + _NumEmptyVersions = 0; + _MaxPtsSize = 0; + _MaxTopLvlPtsSize = 0; + _MaxVersionPtsSize = 0; + _TotalPtsSize = 0; + _AvgPtsSize = 0.0; + _AvgTopLvlPtsSize = 0.0; + _AvgVersionPtsSize = 0.0; } void VersionedFlowSensitiveStat::performStat() @@ -41,7 +40,7 @@ void VersionedFlowSensitiveStat::performStat() clearStat(); - SVFIR *pag = vfspta->getPAG(); + PAG *pag = vfspta->getPAG(); versionStat(); ptsSizeStat(); @@ -49,14 +48,14 @@ void VersionedFlowSensitiveStat::performStat() u32_t fiObjNumber = 0; u32_t fsObjNumber = 0; Set nodeSet; - for (SVFIR::const_iterator it = pag->begin(); it != pag->end(); ++it) + for (PAG::const_iterator it = pag->begin(); it != pag->end(); ++it) { NodeID nodeId = it->first; PAGNode* pagNode = it->second; - if (SVFUtil::isa(pagNode)) + if (SVFUtil::isa(pagNode)) { const MemObj *memObj = pag->getBaseObj(nodeId); - SymID baseId = memObj->getId(); + SymID baseId = memObj->getSymId(); if (nodeSet.insert(baseId).second) { if (memObj->isFieldInsensitive()) fiObjNumber++; @@ -65,13 +64,12 @@ void VersionedFlowSensitiveStat::performStat() } } - PTNumStatMap["FIObjNum"] = fiObjNumber; - PTNumStatMap["FSObjNum"] = fsObjNumber; - unsigned numOfCopy = 0; unsigned numOfStore = 0; + unsigned numOfNode = 0; for (SVFG::iterator it = vfspta->svfg->begin(); it != vfspta->svfg->end(); ++it) { + numOfNode++; SVFGNode* svfgNode = it->second; if (SVFUtil::isa(svfgNode)) numOfCopy++; else if (SVFUtil::isa(svfgNode)) numOfStore++; @@ -79,7 +77,7 @@ void VersionedFlowSensitiveStat::performStat() PTAStat::performStat(); - timeStatMap["TotalTime"] = (endTime - startTime)/TIMEINTERVAL; + timeStatMap[TotalAnalysisTime] = (endTime - startTime)/TIMEINTERVAL; timeStatMap["SolveTime"] = vfspta->solveTime; timeStatMap["SCCTime"] = vfspta->sccTime; timeStatMap["ProcessTime"] = vfspta->processTime; @@ -96,15 +94,17 @@ void VersionedFlowSensitiveStat::performStat() timeStatMap["PhiTime"] = vfspta->phiTime; timeStatMap["meldLabelingTime"] = vfspta->meldLabelingTime; timeStatMap["PrelabelingTime"] = vfspta->prelabelingTime; + timeStatMap["RelianceTime"] = vfspta->relianceTime; timeStatMap["VersionPropTime"] = vfspta->versionPropTime; + timeStatMap["MeldMappingTime"] = vfspta->meldMappingTime; - PTNumStatMap["TotalPointers"] = pag->getValueNodeNum() + pag->getFieldValNodeNum(); - PTNumStatMap["TotalObjects"] = pag->getObjectNodeNum() + pag->getFieldObjNodeNum(); + PTNumStatMap[TotalNumOfPointers] = pag->getValueNodeNum() + pag->getFieldValNodeNum(); + PTNumStatMap[TotalNumOfObjects] = pag->getObjectNodeNum() + pag->getFieldObjNodeNum(); - PTNumStatMap["Pointers"] = pag->getValueNodeNum(); - PTNumStatMap["MemObjects"] = pag->getObjectNodeNum(); - PTNumStatMap["DummyFieldPtrs"] = pag->getFieldValNodeNum(); - PTNumStatMap["FieldObjs"] = pag->getFieldObjNodeNum(); + PTNumStatMap[NumOfPointers] = pag->getValueNodeNum(); + PTNumStatMap[NumOfMemObjects] = pag->getObjectNodeNum(); + PTNumStatMap[NumOfGepFieldPointers] = pag->getFieldValNodeNum(); + PTNumStatMap[NumOfGepFieldObjects] = pag->getFieldObjNodeNum(); PTNumStatMap["TotalVersions"] = _NumVersions; PTNumStatMap["MaxVersionsForObj"] = _MaxVersions; @@ -113,12 +113,12 @@ void VersionedFlowSensitiveStat::performStat() PTNumStatMap["TotalExistingVPts"] = _NumUsedVersions; PTNumStatMap["TotalSingleVObjs"] = _NumSingleVersion; - PTNumStatMap["CopysNum"] = numOfCopy; - PTNumStatMap["StoresNum"] = numOfStore; + PTNumStatMap[NumOfCopys] = numOfCopy; + PTNumStatMap[NumOfStores] = numOfStore; - PTNumStatMap["SolveIterations"] = vfspta->numOfIteration; + PTNumStatMap[NumOfIterations] = vfspta->numOfIteration; - PTNumStatMap["IndEdgeSolved"] = vfspta->getNumOfResolvedIndCallEdge(); + PTNumStatMap[NumOfIndirectEdgeSolved] = vfspta->getNumOfResolvedIndCallEdge(); PTNumStatMap["StrongUpdates"] = vfspta->svfgHasSU.count(); @@ -146,43 +146,54 @@ void VersionedFlowSensitiveStat::performStat() timeStatMap["AverageSCCSize"] = (vfspta->numOfSCC == 0) ? 0 : ((double)vfspta->numOfNodesInSCC / vfspta->numOfSCC); - PTAStat::printStat("Versioned Flow-Sensitive Pointer Analysis Statistics"); + std::cout << "\n****Versioned Flow-Sensitive Pointer Analysis Statistics****\n"; + PTAStat::printStat(); } void VersionedFlowSensitiveStat::versionStat(void) { - // TODO! Need to merge yield/consume. - _NumSingleVersion = 0; - _MaxVersions = 0; - - u32_t totalVersionPtsSize = 0; - for (const VersionedFlowSensitive::LocVersionMap *lvm : - { - &vfspta->consume, &vfspta->yield - }) + Map> versions; + for (VersionedFlowSensitive::LocVersionMap::value_type &lov : vfspta->consume) { - for (const VersionedFlowSensitive::ObjToVersionMap &lov : *lvm) + for (VersionedFlowSensitive::ObjToVersionMap::value_type &ov : lov.second) { - for (const VersionedFlowSensitive::ObjToVersionMap::value_type &ov : lov) - { - const NodeID o = ov.first; - const Version v = ov.second; + versions[ov.first].insert(ov.second); + } + } - ++_NumVersions; + for (VersionedFlowSensitive::LocVersionMap::value_type &lov : vfspta->yield) + { + for (VersionedFlowSensitive::ObjToVersionMap::value_type &ov : lov.second) + { + versions[ov.first].insert(ov.second); + } + } - // If the version was just over-approximate and never accessed, ignore. - // TODO: with vPtD changed there is no interface to check if the PTS - // exists; an emptiness check is *not* an existence check. - if (vfspta->vPtD->getPts(vfspta->atKey(o, v)).empty()) continue; + u32_t totalVersionPtsSize = 0; + for (Map>::value_type &ovs : versions) + { + NodeID o = ovs.first; + Set vs = ovs.second; - const PointsTo &ovPts = vfspta->vPtD->getPts(vfspta->atKey(o, v)); - if (!ovPts.empty()) ++_NumNonEmptyVersions; - else ++_NumEmptyVersions; + u32_t numOVersions = vs.size(); + _NumVersions += numOVersions; + if (numOVersions > _MaxVersions) _MaxVersions = numOVersions; + if (numOVersions == 1) ++_NumSingleVersion; - _TotalPtsSize += ovPts.count(); - totalVersionPtsSize += ovPts.count(); - if (ovPts.count() > _MaxVersionPtsSize) _MaxVersionPtsSize = ovPts.count(); - } + for (const Set::value_type &v : vs) + { + // If the version was just over-approximate and never accessed, ignore. + // TODO: with vPtD changed there is no interface to check if the PTS + // exists; an emptiness check is *not* an existence check. + if (vfspta->vPtD->getPts(vfspta->atKey(o, v)).empty()) continue; + + const PointsTo &ovPts = vfspta->vPtD->getPts(vfspta->atKey(o, v)); + if (!ovPts.empty()) ++_NumNonEmptyVersions; + else ++_NumEmptyVersions; + + _TotalPtsSize += ovPts.count(); + totalVersionPtsSize += ovPts.count(); + if (ovPts.count() > _MaxVersionPtsSize) _MaxVersionPtsSize = ovPts.count(); } } @@ -197,7 +208,7 @@ void VersionedFlowSensitiveStat::ptsSizeStat() { u32_t totalValidTopLvlPointers = 0; u32_t totalTopLvlPtsSize = 0; - for (SVFIR::iterator it = vfspta->getPAG()->begin(); it != vfspta->getPAG()->end(); ++it) + for (PAG::iterator it = vfspta->getPAG()->begin(); it != vfspta->getPAG()->end(); ++it) { if (!vfspta->getPAG()->isValidTopLevelPtr(it->second)) continue; diff --git a/svf/lib/WPA/WPAPass.cpp b/svf/lib/WPA/WPAPass.cpp index 7b3601e09..76e657c94 100644 --- a/svf/lib/WPA/WPAPass.cpp +++ b/svf/lib/WPA/WPAPass.cpp @@ -2,20 +2,20 @@ // // SVF: Static Value-Flow Analysis // -// Copyright (C) <2013-> +// Copyright (C) <2013-2017> // // This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU Affero General Public License as published by +// it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Affero General Public License for more details. +// GNU General Public License for more details. -// You should have received a copy of the GNU Affero General Public License +// You should have received a copy of the GNU General Public License // along with this program. If not, see . // //===-----------------------------------------------------------------------===// @@ -34,20 +34,43 @@ #include "Util/Options.h" -#include "SVFIR/SVFModule.h" +#include "Util/SVFModule.h" #include "MemoryModel/PointerAnalysisImpl.h" #include "WPA/WPAPass.h" #include "WPA/Andersen.h" -#include "WPA/AndersenPWC.h" +#include "WPA/AndersenSFR.h" #include "WPA/FlowSensitive.h" +#include "WPA/FlowSensitiveTBHC.h" #include "WPA/VersionedFlowSensitive.h" #include "WPA/TypeAnalysis.h" #include "WPA/Steensgaard.h" +#include "SVF-FE/PAGBuilder.h" +#include "WPA/InvariantHandler.h" +#include "WPA/Debugger.h" +//#include "InsertFunctionSwitch.h" +//#include "InsertExecutionSwitch.h" + +#include "llvm/IR/InstIterator.h" +#include "llvm/Transforms/Utils/Cloning.h" + +#include "llvm/Transforms/Utils/FunctionComparator.h" +#include "llvm/Analysis/LoopInfo.h" +#include "llvm/IR/LegacyPassManager.h" +#include "WPA/LoopInfoConsolidatorPass.h" + +#include "llvm/Analysis/LoopInfo.h" + + +#include using namespace SVF; char WPAPass::ID = 0; +static llvm::RegisterPass WHOLEPROGRAMPA("wpa", + "Whole Program Pointer AnalysWPAis Pass"); + + /*! * Destructor */ @@ -63,87 +86,679 @@ WPAPass::~WPAPass() ptaVector.clear(); } +/* +CallInst* WPAPass::findCorrespondingCallInClone(CallInst* indCall, llvm::Module* clonedModule) { + int indCallIndex = -1; + Function* origFunc = indCall->getFunction(); + llvm::StringRef indCallFuncName = origFunc->getName(); + for (inst_iterator I = llvm::inst_begin(origFunc), E = llvm::inst_end(origFunc); I != E; ++I) { + if (CallInst* callInst = SVFUtil::dyn_cast(&*I)) { + if (callInst->isIndirectCall()) { + indCallIndex++; + if (indCall == callInst) { + break; + } + } + } + } + + int indexIt = -1; + // Now find that same function and call-index in the cloned + Function* clonedFunc = clonedModule->getFunction(indCallFuncName); + for (inst_iterator I = llvm::inst_begin(clonedFunc), E = llvm::inst_end(clonedFunc); I != E; ++I) { + if (CallInst* callInst = SVFUtil::dyn_cast(&*I)) { + if (callInst->isIndirectCall()) { + indexIt++; + if (indexIt == indCallIndex) { + return callInst; + } + } + } + } + return nullptr; +} +*/ + +void WPAPass::collectCFI(SVFModule* svfModule, Module& M, bool woInv) { + + std::map>* indCallMap; + std::set* indCallProhibited; + + std::map* histogram; + + if (!woInv) { + indCallMap = &wInvIndCallMap; + indCallProhibited = &wInvIndCallProhibited; + histogram = &wInvHistogram; + } else { + indCallMap = &woInvIndCallMap; + indCallProhibited = &woInvIndCallProhibited; + histogram = &woInvHistogram; + } + + + PAG* pag = _pta->getPAG(); + const PTACallGraph* ptaCallGraph = _pta->getPTACallGraph(); + const PTACallGraph::CallInstToCallGraphEdgesMap& cInstMap = ptaCallGraph->getCallInstToCallGraphEdgesMap(); + + for (auto it: cInstMap) { + const CallBlockNode* callBlockNode = it.first; + const Instruction* inst = callBlockNode->getCallSite(); + const CallInst* cInst = SVFUtil::dyn_cast(inst); + + assert(cInst && "If not callinst then what?"); + const Function* callerFun = cInst->getParent()->getParent(); + + if (cInst->isIndirectCall()) { + //llvm::errs() << "For a callsite in " << callerFun->getName() << " : \n"; + for (auto callGraphEdge: it.second) { + const SVFFunction* svfFun = callGraphEdge->getDstNode()->getFunction(); + const Function* calledFunction = svfFun->getLLVMFun(); + //llvm::errs() << " calls " << calledFunction->getName() << "\n"; + (*indCallMap)[cInst].insert(calledFunction); + + } + } + } + + (*histogram)[0] = indCallProhibited->size(); + + for (auto it: *indCallMap) { + const CallInst* cInst = it.first; + int sz = it.second.size(); + /* + llvm::errs() << "[DUMPING IND CALL] Function: " << cInst->getFunction()->getName() << " : "; + for (const Function* tgt: it.second) { + llvm::errs() << tgt->getName() << ", "; + } + */ + (*histogram)[sz]++; + //llvm::errs() << "\n"; + } + + /* + llvm::errs() << "EC Size:\t Ind. Call-sites\n"; + int totalTgts = 0; + int totalIndCallSites = 0; + for (auto it: (*histogram)) { + llvm::errs() << it.first << " : " << it.second << "\n"; + totalIndCallSites += it.second; + totalTgts += it.first*it.second; + } + llvm::errs() << "Total Ind. Call-sites: " << totalIndCallSites << "\n"; + llvm::errs() << "Total Tgts: " << totalTgts << "\n"; + std::cerr << "Average CFI: " << std::fixed << (float)totalTgts / (float)totalIndCallSites << "\n"; + */ +} + +void WPAPass::instrumentCFICheck(llvm::CallInst* indCall) { + llvm::Module* M = indCall->getParent()->getParent()->getParent(); + Type* longType = IntegerType::get(M->getContext(), 64); + Type* intType = IntegerType::get(M->getContext(), 32); + Type* ptrToLongType = PointerType::get(longType, 0); + + // Create an AllocaInst + IRBuilder Builder(indCall); + + Constant* csIdCons = ConstantInt::get(intType, indCSToIDMap[indCall]); + + Value* tgt = Builder.CreateBitOrPointerCast(indCall->getCalledOperand(), longType); + + Builder.CreateCall(checkCFI, {csIdCons, tgt}); + + +} + +/** + * We can go forward, from a sizeof operator + * Or backward from a heap allocation call (malloc, etc) to a cast + * Going backward might have unintended consequences. + * How do you know when you stop? void* p = malloc(..); return p; + * + * You can track all type-cast operations and track if they type-cast + * any of the return values + * + */ +/* +void WPAPass::deriveHeapAllocationTypes(llvm::Module& module) { + std::vector callInsts; // functions that return pointers + std::vector typeCasts; + std::map callInstCastToStructs; + std::map callInstCastToArrays; + + std::vector mallocCallers; + + mallocCallers.push_back("malloc"); + mallocCallers.push_back("calloc"); + + for (Module::iterator MIterator = module.begin(); MIterator != module.end(); MIterator++) { + if (Function *F = SVFUtil::dyn_cast(&*MIterator)) { + std::vector stackVars; + std::vector loadInsts; + std::vector storeInsts; + if (!F->isDeclaration()) { + for (llvm::inst_iterator I = llvm::inst_begin(F), E = llvm::inst_end(F); I != E; ++I) { + if (CallInst* cInst = SVFUtil::dyn_cast(&*I)) { + // Does it return a pointer + Function* calledFunc = cInst->getCalledFunction(); + Type* retTy = cInst->getType(); + if (retTy->isPointerTy()) { + callInsts.push_back(cInst); + } + } + } + } + } + } + + // Type-casts + std::vector workList; + for (CallInst* cInst: callInsts) { + workList.push_back(cInst); + while (!workList.empty()) { + Instruction* inst = workList.back(); + workList.pop_back(); + bool term = false; + for (User* u: inst->users()) { + Instruction* uInst = SVFUtil::dyn_cast(u); + if (BitCastInst* bcInst = SVFUtil::dyn_cast(u)) { + Type* destTy = bcInst->getDestTy(); + if (destTy->isPointerTy()) { + Type* ptrTy = destTy->getPointerElementType(); + if (StructType* stTy = SVFUtil::dyn_cast(ptrTy)) { + term = true; + callInstCastToStructs[cInst] = stTy; + } else if (ArrayType* arrTy = SVFUtil::dyn_cast(ptrTy)) { + term = true; + callInstCastToArrays[cInst] = arrTy; + } + } + } + if (!term) { + workList.push_back(uInst); + } + } + } + } + + for (auto pair: callInstCastToStructs) { + CallInst* cInst = pair.first; + StructType* stTy = pair.second; + llvm::errs() << "CallInst in function: " << cInst->getFunction()->getName() << " is cast to struct: " << *stTy << "\n"; + + } + + for (auto pair: callInstCastToArrays) { + CallInst* cInst = pair.first; + ArrayType* arrTy = pair.second; + llvm::errs() << "CallInst in function: " << cInst->getFunction()->getName() << " is cast to array: " << *arrTy << "\n"; + + } + +} +*/ + /*! * We start from here */ -void WPAPass::runOnModule(SVFIR* pag) +void WPAPass::runOnModule(SVFModule* svfModule) { - for (u32_t i = 0; i<= PointerAnalysis::Default_PTA; i++) - { - PointerAnalysis::PTATY iPtaTy = static_cast(i); - if (Options::PASelected(iPtaTy)) - runPointerAnalysis(pag, i); + llvm::Module *module = SVF::LLVMModuleSet::getLLVMModuleSet()->getMainLLVMModule(); + //deriveHeapAllocationTypesWithCloning(*module); + + /* + if (Options::KaliRunTestDriver) { + invariantInstrumentationDriver(*module); + } else { + for (u32_t i = 0; i<= PointerAnalysis::Default_PTA; i++) + { + if (Options::PASelected.isSet(i)) + runPointerAnalysis(svfModule, i); + } + assert(!ptaVector.empty() && "No pointer analysis is specified.\n"); + + } + */ + runPointerAnalysis(svfModule, 0); + + + //module->dump(); + std::error_code EC; + llvm::raw_fd_ostream OS("instrumented-module.bc", EC, + llvm::sys::fs::F_None); + WriteBitcodeToFile(*module, OS); + OS.flush(); +} + +/*! + * We start from here + */ +bool WPAPass::runOnModule(Module& module) +{ + SVFModule* svfModule = LLVMModuleSet::getLLVMModuleSet()->buildSVFModule(module); + runOnModule(svfModule); + return false; +} + +void WPAPass::invariantInstrumentationDriver(Module& module) { + PAG* pag = nullptr; + Andersen* andersen = new Andersen(pag); + Module::GlobalListType &Globals = module.getGlobalList(); + for (Module::iterator MIterator = module.begin(); MIterator != module.end(); MIterator++) { + if (Function *F = SVFUtil::dyn_cast(&*MIterator)) { + std::vector stackVars; + std::vector loadInsts; + std::vector storeInsts; + if (!F->isDeclaration()) { + // Find the AllocaInsts + for (llvm::inst_iterator I = llvm::inst_begin(F), E = llvm::inst_end(F); I != E; ++I) { + if (AllocaInst* stackVar = SVFUtil::dyn_cast(&*I)) { + if (stackVar->hasName() && stackVar->getName() == "retval") { + continue; + } + stackVars.push_back(stackVar); + } else if (LoadInst* loadInst = SVFUtil::dyn_cast(&*I)) { + // Is it a pointer + if (SVFUtil::isa(loadInst->getType())) { + loadInsts.push_back(loadInst); + } + } else if (StoreInst* storeInst = SVFUtil::dyn_cast(&*I)) { + if (SVFUtil::isa(storeInst->getType())) { + storeInsts.push_back(storeInst); + } + } + } + for (LoadInst* loadInst: loadInsts) { + /* + for (AllocaInst* stackVar: stackVars) { + andersen->instrumentInvariant(loadInst, stackVar); + break; + } + break; + */ + for (GlobalVariable& globalVar: Globals) { + if (globalVar.getName() == "myGlobal") { + andersen->instrumentInvariant(loadInst, &globalVar); + } + } + } + } + } + } +} + +void WPAPass::initKaleidoscope(Module* module) { + LLVMContext& C = module->getContext(); + Type* longType = IntegerType::get(module->getContext(), 64); + Type* intType = IntegerType::get(module->getContext(), 32); + + Function* mainFunction = module->getFunction("main"); + Instruction* inst = mainFunction->getEntryBlock().getFirstNonPHIOrDbg(); + IRBuilder builder(inst); + + FunctionType* kaleidoscopeInitFnTy = FunctionType::get(Type::getVoidTy(module->getContext()), {}, false); + Function* kaleidoscopeInitFn = Function::Create(kaleidoscopeInitFnTy, Function::ExternalLinkage, "kaleidoscopeInit", module); + builder.CreateCall(kaleidoscopeInitFn, {}); +} + +void WPAPass::initializeCFITargets(llvm::Module* module) { + LLVMContext& C = module->getContext(); + Type* longType = IntegerType::get(module->getContext(), 64); + Type* intType = IntegerType::get(module->getContext(), 32); + + Function* mainFunction = module->getFunction("main"); + Instruction* inst = mainFunction->getEntryBlock().getFirstNonPHIOrDbg(); + IRBuilder builder(inst); + + for (auto pair: wInvIndCallMap) { + const CallInst* callInst = pair.first; + std::set& wInvTgts = pair.second; + std::set& woInvTgts = woInvIndCallMap[callInst]; // TODO: does it need to have it? + + indIDToCSMap[indCSId] = callInst; + indCSToIDMap[callInst] = indCSId; + + Constant* csIdCons = ConstantInt::get(intType, indCSId); + for (const Function* tgt: wInvTgts) { + Value* funcAddr = builder.CreateBitOrPointerCast(const_cast(tgt), longType); + builder.CreateCall(updateTgtWInvFn->getFunctionType(), updateTgtWInvFn, {csIdCons, funcAddr}); + } + + for (const Function* tgt: woInvTgts) { + Value* funcAddr = builder.CreateBitOrPointerCast(const_cast(tgt), longType); + builder.CreateCall(updateTgtWOInvFn->getFunctionType(), updateTgtWOInvFn, {csIdCons, funcAddr}); + } + indCSId++; + } + + if (Options::NoInvariants) { + // Flip the inv Flag + FunctionType* initWithNoInvType = FunctionType::get(Type::getVoidTy(module->getContext()), {}, false); + Function* initWithNoInvFn = Function::Create(initWithNoInvType, Function::ExternalLinkage, "initWithNoInvariant", module); + builder.CreateCall(initWithNoInvFn, {}); + + } +} + +void WPAPass::addCFIFunctions(llvm::Module* module) { + Type* longType = IntegerType::get(module->getContext(), 64); + Type* intType = IntegerType::get(module->getContext(), 32); + Type* ptrToLongType = PointerType::get(longType, 0); + + // The updateTgtXX function + FunctionType* updateTgtFnTy = FunctionType::get(Type::getVoidTy(module->getContext()), {intType, longType}, false); + updateTgtWInvFn = Function::Create(updateTgtFnTy, Function::ExternalLinkage, "updateTgtWInv", module); + updateTgtWOInvFn = Function::Create(updateTgtFnTy, Function::ExternalLinkage, "updateTgtWoInv", module); + + // The checkCFI function + std::vector typeCheckCFI; + typeCheckCFI.push_back(intType); + typeCheckCFI.push_back(longType); // checkCFI(unsigned long* valid_tgts, int len, unsigned long* tgt); + + llvm::ArrayRef typeCheckCFIArr(typeCheckCFI); + + FunctionType* checkCFIFnTy = FunctionType::get(Type::getVoidTy(module->getContext()), typeCheckCFIArr, false); + checkCFI = Function::Create(checkCFIFnTy, Function::ExternalLinkage, "checkCFI", module); +} + +void WPAPass::linkVariadics(SVFModule* svfModule, PAG* pag) { + SVFModule::iterator it = svfModule->begin(); + for (; it != svfModule->end(); it++) { + const SVFFunction* fun = *it; + if (fun->isVarArg()) { + NodeID varargNode = pag->getVarargNode(fun); + const PointsTo& pts = _pta->getPts(varargNode); + for (PointsTo::iterator piter = pts.begin(), epiter = pts.end(); piter != epiter; ++piter) { + NodeID ptd = *piter; + if (pag->hasPAGNode(ptd)) { + PAGNode* tgtNode = pag->getPAGNode(ptd); + if (tgtNode->hasValue()) { + if (const Function* func = SVFUtil::dyn_cast(tgtNode->getValue())) { + fixupSet.insert(const_cast(func)); + } + } + } + } + } + } + + for (Function* fixup: fixupSet) { + llvm::errs() << "Fix up with function: " << fixup->getName() << "\n"; + } + /* + for (auto it: wInvIndCallMap) { + for (Function* fixUp: fixupVec) { + wInvIndCallMap[it.first].insert(fixUp); + } + } + + for (auto it: woInvIndCallMap) { + for (Function* fixUp: fixupVec) { + woInvIndCallMap[it.first].insert(fixUp); + } } - assert(!ptaVector.empty() && "No pointer analysis is specified.\n"); + */ +} + +bool WPAPass::matchFunctionType(Function* func, CallInst* call) { + FunctionType* ft1 = func->getFunctionType(); + FunctionType* ft2 = call->getFunctionType(); + + if (ft1->getNumParams() != ft2->getNumParams()) return false; + //if (ft1->getReturnType() != ft1->getReturnType()) return false; + return true; } /*! * Create pointer analysis according to a specified kind and then analyze the module. */ -void WPAPass::runPointerAnalysis(SVFIR* pag, u32_t kind) +void WPAPass::runPointerAnalysis(SVFModule* svfModule, u32_t kind) { - /// Initialize pointer analysis. - switch (kind) - { - case PointerAnalysis::Andersen_WPA: - _pta = new Andersen(pag); - break; - case PointerAnalysis::AndersenSCD_WPA: - _pta = new AndersenSCD(pag); - break; - case PointerAnalysis::AndersenSFR_WPA: - _pta = new AndersenSFR(pag); - break; - case PointerAnalysis::AndersenWaveDiff_WPA: - _pta = new AndersenWaveDiff(pag); - break; - case PointerAnalysis::Steensgaard_WPA: - _pta = new Steensgaard(pag); - break; - case PointerAnalysis::FSSPARSE_WPA: - _pta = new FlowSensitive(pag); - break; - case PointerAnalysis::VFS_WPA: - _pta = new VersionedFlowSensitive(pag); - break; - case PointerAnalysis::TypeCPP_WPA: - _pta = new TypeAnalysis(pag); - break; - default: - assert(false && "This pointer analysis has not been implemented yet.\n"); + llvm::Module *module = SVF::LLVMModuleSet::getLLVMModuleSet()->getMainLLVMModule(); + + llvm::legacy::PassManager PM; + llvm::DominatorTreeWrapperPass* domPass = new llvm::DominatorTreeWrapperPass(); + LoopInfoWrapperPass* loopInfoPass = new llvm::LoopInfoWrapperPass(); + svfLoopInfo = new LoopInfoConsolidatorPass(); + PM.add(domPass); + PM.add(loopInfoPass); + PM.add(svfLoopInfo); + PM.run(*module); + + if ((Options::InvariantVGEP || Options::InvariantPWC) && Options::NoInvariants) { + llvm::errs() << "Invalid configuration ...\n"; return; } + /// Build PAG + PAGBuilder builder; + Options::InvariantVGEP = true; + Options::InvariantPWC = true; + + PAG* pag = builder.build(svfModule); + Andersen* anderAnalysis = new AndersenWaveDiff(pag); + _pta = anderAnalysis; + anderAnalysis->setSVFLoopInfo(svfLoopInfo); ptaVector.push_back(_pta); _pta->analyze(); - if (Options::AnderSVFG()) - { - SVFGBuilder memSSA(true); - assert(SVFUtil::isa(_pta) && "supports only andersen/steensgaard for pre-computed SVFG"); - SVFG *svfg = memSSA.buildFullSVFG((BVDataPTAImpl*)_pta); - /// support mod-ref queries only for -ander - if (Options::PASelected(PointerAnalysis::AndersenWaveDiff_WPA)) - _svfg = svfg; + + collectCFI(svfModule, *module, false); + + std::string statDir = _pta->getStat()->getStatDir(); + if (Options::ShortCircuit) { + collectCFI(svfModule, *module, true); + } else { + + /* + // Round 2: VGEP + Options::InvariantVGEP = true; + Options::InvariantPWC = false; + + llvm::errs() << "Running with InvariantVGEP = " << Options::InvariantVGEP << " InvariantPWC = " << Options::InvariantPWC << "\n"; + + builder.getPAG()->resetPAG(); + SymbolTableInfo::releaseSymbolInfo(); + + svfModule->buildSymbolTableInfo(); + + PAGBuilder builder2; + + PAG* pag2 = builder2.build(svfModule); + _pta = new AndersenWaveDiff(pag2); + _pta->getStat()->setStatDir(statDir); // copy the stat dir + ptaVector.clear(); + ptaVector.push_back(_pta); + _pta->analyze(); + + // Round 2: VGEP + Options::InvariantVGEP = false; + Options::InvariantPWC = true; + + llvm::errs() << "Running with InvariantVGEP = " << Options::InvariantVGEP << " InvariantPWC = " << Options::InvariantPWC << "\n"; + + builder.getPAG()->resetPAG(); + builder2.getPAG()->resetPAG(); + SymbolTableInfo::releaseSymbolInfo(); + + svfModule->buildSymbolTableInfo(); + + PAGBuilder builder3; + + PAG* pag3 = builder3.build(svfModule); + _pta = new AndersenWaveDiff(pag3); + _pta->getStat()->setStatDir(statDir); // copy the stat dir + ptaVector.clear(); + ptaVector.push_back(_pta); + _pta->analyze(); + */ + + // Round 4: The default + Options::InvariantVGEP = false; + Options::InvariantPWC = false; + + + builder.getPAG()->resetPAG(); + /* + builder2.getPAG()->resetPAG(); + builder3.getPAG()->resetPAG(); + */ + SymbolTableInfo::releaseSymbolInfo(); + + svfModule->buildSymbolTableInfo(); + + PAGBuilder builder4; + + PAG* pag4 = builder4.build(svfModule); + _pta = new AndersenWaveDiff(pag4); + _pta->getStat()->setStatDir(statDir); // copy the stat dir + ptaVector.clear(); + ptaVector.push_back(_pta); + _pta->analyze(); + + // Collect CFI at the end + collectCFI(svfModule, *module, true); } - if (Options::PrintAliases()) - PrintAliasPairs(_pta); + + // At the end of collectCFI + // we have two maps inside WPAPass populated + // the wInvIndCallMap and the woInvIndCallMap + // + // Using these, we must build the CFI policies for both the with invariant + // and without invariant versions. + // And then have the memory view switcher switch between these + // + // We do this as follows: + // We use the wInvMaps to instrument the module + // In case of indirect calls where the profile produced by wInvMap and + // woInvMap differs, we create a clone of the Function and add the pair + // (the original Function and the cloned Function) to + // a vector of memory-view-pairs. + // + // We instrument the original function according to the wInvMap's profile + // and the cloned function according to the woInvMap's profile + // + // We pass the memory-view-pairs vector the the Memory View Switcher pass + // + + + if (Options::ApplyCFI) { + addCFIFunctions(module); + initializeCFITargets(module); + + for (auto pair: wInvIndCallMap) { + llvm::CallInst* callInst = const_cast(pair.first); + + instrumentCFICheck(callInst); + } + } + + // Now go over all functions + // and see if the versions are different + + // Handle the invariants + if (!Options::NoInvariants) { + initKaleidoscope(module); + InvariantHandler IHandler(svfModule, module, pag, loopInfoPass); // this one should be the original PAG + IHandler.init(); + IHandler.handleVGEPInvariants(); + IHandler.handlePWCInvariants(); + + Debugger debugger(svfModule, module, pag, loopInfoPass, _pta); + debugger.init(); + + } + +#if 0 + // Print CDF / PDF + std::map pdfWInv; + std::map cdfWInv; + std::map pdfWoInv; + std::map cdfWoInv; + + // Find the largest EC Size (this is going to be without Invariant + // Find the total number of callsites. This won't change depending on + // whether or not we're running w/ or wo/ invariants + + int largestEC = 0; + int totalCS = 0; + for (auto it: woInvHistogram) { + if (it.first > largestEC) { + largestEC = it.first; + } + totalCS += it.second; + } + + // Compute the PDF and CDF for wo/ invariant + for (auto it: woInvHistogram) { + int ecSize = it.first; + int csCount = it.second; + pdfWoInv[ecSize] = ((float) csCount) / ((float) totalCS) * 100; + } + + for (int i = 0; i <= largestEC; i++) { + if (pdfWoInv.find(i) == pdfWoInv.end()) { + pdfWoInv[i] = 0.0; + } + } + + cdfWoInv[0] = pdfWoInv[0]; + for (int i = 1; i <= largestEC; i++) { + cdfWoInv[i] = cdfWoInv[i-1] + pdfWoInv[i]; + } + cdfWoInv[0] = 0; // testing + + // Compute the PDF and CDF for w/ invariant + for (auto it: wInvHistogram) { + int ecSize = it.first; + int csCount = it.second; + pdfWInv[ecSize] = ((float) csCount) / ((float) totalCS) * 100; + } + + for (int i = 0; i <= largestEC; i++) { + if (pdfWInv.find(i) == pdfWInv.end()) { + pdfWInv[i] = 0.0; + } + } + + cdfWInv[0] = pdfWInv[0]; + for (int i = 1; i <= largestEC; i++) { + cdfWInv[i] = cdfWInv[i-1] + pdfWInv[i]; + } + cdfWInv[0] = 0; // testing + + + std::cerr << std::fixed; + std::cerr << "CDF:\n"; + std::string appName = std::get<0>(module->getName().split(".")).str(); + appName[0] = std::toupper(appName[0]); + for (auto it: cdfWoInv) { + std::cerr << appName << "," << "0, " << it.first << "," << it.second << "\n"; + } + + for (auto it: cdfWInv) { + std::cerr << appName << "," << "1, " << it.first << "," << it.second << "\n"; + if (it.second >= 100.0) { + break; + } + } +#endif } void WPAPass::PrintAliasPairs(PointerAnalysis* pta) { - SVFIR* pag = pta->getPAG(); - for (SVFIR::iterator lit = pag->begin(), elit = pag->end(); lit != elit; ++lit) + PAG* pag = pta->getPAG(); + for (PAG::iterator lit = pag->begin(), elit = pag->end(); lit != elit; ++lit) { PAGNode* node1 = lit->second; PAGNode* node2 = node1; - for (SVFIR::iterator rit = lit, erit = pag->end(); rit != erit; ++rit) + for (PAG::iterator rit = lit, erit = pag->end(); rit != erit; ++rit) { node2 = rit->second; if(node1==node2) continue; - const SVFFunction* fun1 = node1->getFunction(); - const SVFFunction* fun2 = node2->getFunction(); + const Function* fun1 = node1->getFunction(); + const Function* fun2 = node2->getFunction(); AliasResult result = pta->alias(node1->getId(), node2->getId()); SVFUtil::outs() << (result == AliasResult::NoAlias ? "NoAlias" : "MayAlias") << " var" << node1->getId() << "[" << node1->getValueName() @@ -154,60 +769,47 @@ void WPAPass::PrintAliasPairs(PointerAnalysis* pta) } } -const PointsTo& WPAPass::getPts(const SVFValue* value) -{ - assert(_pta && "initialize a pointer analysis first"); - SVFIR* pag = _pta->getPAG(); - return getPts(pag->getValueNode(value)); -} - -const PointsTo& WPAPass::getPts(NodeID var) -{ - assert(_pta && "initialize a pointer analysis first"); - return _pta->getPts(var); -} - /*! * Return alias results based on our points-to/alias analysis * TODO: Need to handle PartialAlias and MustAlias here. */ -AliasResult WPAPass::alias(const SVFValue* V1, const SVFValue* V2) +AliasResult WPAPass::alias(const Value* V1, const Value* V2) { - AliasResult result = AliasResult::MayAlias; + AliasResult result = llvm::MayAlias; - SVFIR* pag = _pta->getPAG(); + PAG* pag = _pta->getPAG(); /// TODO: When this method is invoked during compiler optimizations, the IR /// used for pointer analysis may been changed, so some Values may not - /// find corresponding SVFIR node. In this case, we only check alias - /// between two Values if they both have SVFIR nodes. Otherwise, MayAlias + /// find corresponding PAG node. In this case, we only check alias + /// between two Values if they both have PAG nodes. Otherwise, MayAlias /// will be returned. if (pag->hasValueNode(V1) && pag->hasValueNode(V2)) { /// Veto is used by default - if (Options::AliasRule.nothingSet() || Options::AliasRule(Veto)) + if (Options::AliasRule.getBits() == 0 || Options::AliasRule.isSet(Veto)) { /// Return NoAlias if any PTA gives NoAlias result - result = AliasResult::MayAlias; + result = llvm::MayAlias; for (PTAVector::const_iterator it = ptaVector.begin(), eit = ptaVector.end(); it != eit; ++it) { - if ((*it)->alias(V1, V2) == AliasResult::NoAlias) - result = AliasResult::NoAlias; + if ((*it)->alias(V1, V2) == llvm::NoAlias) + result = llvm::NoAlias; } } - else if (Options::AliasRule(Conservative)) + else if (Options::AliasRule.isSet(Conservative)) { /// Return MayAlias if any PTA gives MayAlias result - result = AliasResult::NoAlias; + result = llvm::NoAlias; for (PTAVector::const_iterator it = ptaVector.begin(), eit = ptaVector.end(); it != eit; ++it) { - if ((*it)->alias(V1, V2) == AliasResult::MayAlias) - result = AliasResult::MayAlias; + if ((*it)->alias(V1, V2) == llvm::MayAlias) + result = llvm::MayAlias; } } } @@ -216,35 +818,35 @@ AliasResult WPAPass::alias(const SVFValue* V1, const SVFValue* V2) } /*! - * Return mod-ref result of a Callsite + * Return mod-ref result of a CallInst */ -ModRefInfo WPAPass::getModRefInfo(const CallSite callInst) +ModRefInfo WPAPass::getModRefInfo(const CallInst* callInst) { - assert(Options::PASelected(PointerAnalysis::AndersenWaveDiff_WPA) && Options::AnderSVFG() && "mod-ref query is only support with -ander and -svfg turned on"); + assert(Options::PASelected.isSet(PointerAnalysis::AndersenWaveDiff_WPA) && Options::AnderSVFG && "mod-ref query is only support with -ander and -svfg turned on"); ICFG* icfg = _svfg->getPAG()->getICFG(); - const CallICFGNode* cbn = icfg->getCallICFGNode(callInst.getInstruction()); + const CallBlockNode* cbn = icfg->getCallBlockNode(callInst); return _svfg->getMSSA()->getMRGenerator()->getModRefInfo(cbn); } /*! - * Return mod-ref results of a Callsite to a specific memory location + * Return mod-ref results of a CallInst to a specific memory location */ -ModRefInfo WPAPass::getModRefInfo(const CallSite callInst, const SVFValue* V) +ModRefInfo WPAPass::getModRefInfo(const CallInst* callInst, const Value* V) { - assert(Options::PASelected(PointerAnalysis::AndersenWaveDiff_WPA) && Options::AnderSVFG() && "mod-ref query is only support with -ander and -svfg turned on"); + assert(Options::PASelected.isSet(PointerAnalysis::AndersenWaveDiff_WPA) && Options::AnderSVFG && "mod-ref query is only support with -ander and -svfg turned on"); ICFG* icfg = _svfg->getPAG()->getICFG(); - const CallICFGNode* cbn = icfg->getCallICFGNode(callInst.getInstruction()); + const CallBlockNode* cbn = icfg->getCallBlockNode(callInst); return _svfg->getMSSA()->getMRGenerator()->getModRefInfo(cbn, V); } /*! * Return mod-ref result between two CallInsts */ -ModRefInfo WPAPass::getModRefInfo(const CallSite callInst1, const CallSite callInst2) +ModRefInfo WPAPass::getModRefInfo(const CallInst* callInst1, const CallInst* callInst2) { - assert(Options::PASelected(PointerAnalysis::AndersenWaveDiff_WPA) && Options::AnderSVFG() && "mod-ref query is only support with -ander and -svfg turned on"); + assert(Options::PASelected.isSet(PointerAnalysis::AndersenWaveDiff_WPA) && Options::AnderSVFG && "mod-ref query is only support with -ander and -svfg turned on"); ICFG* icfg = _svfg->getPAG()->getICFG(); - const CallICFGNode* cbn1 = icfg->getCallICFGNode(callInst1.getInstruction()); - const CallICFGNode* cbn2 = icfg->getCallICFGNode(callInst2.getInstruction()); + const CallBlockNode* cbn1 = icfg->getCallBlockNode(callInst1); + const CallBlockNode* cbn2 = icfg->getCallBlockNode(callInst2); return _svfg->getMSSA()->getMRGenerator()->getModRefInfo(cbn1, cbn2); }