From 61610386ef5399b915748f9fde57e905c848964a Mon Sep 17 00:00:00 2001 From: Sandro Grebant <sandro.grebant@univ-lille.fr> Date: Fri, 24 Mar 2023 10:18:04 +0100 Subject: [PATCH] input conditionals calculation --- AUTHORS | 3 +- Makefile | 29 ++- include/BranchConditionner.h | 173 +++++++++++++++ include/LoopAnalyzer.h | 82 +++++++ include/PPLDomain.h | 21 +- include/PPLManager.h | 5 - include/PolyAnalysis.h | 1 + include/PolyWrap.h | 1 + poly_BranchConditionner.cpp | 127 +++++++++++ poly_LoopAnalyzer.cpp | 329 ++++++++++++++++++++++++++++ poly_PPLDomain.cpp | 411 ++++++++++++++++++++++++++++------- poly_PolyAnalysis.cpp | 143 +++++++++++- poly_PolyWrap.cpp | 5 + 13 files changed, 1212 insertions(+), 118 deletions(-) create mode 100644 include/BranchConditionner.h create mode 100644 include/LoopAnalyzer.h create mode 100644 poly_BranchConditionner.cpp create mode 100644 poly_LoopAnalyzer.cpp diff --git a/AUTHORS b/AUTHORS index 4fea413..b5fb89e 100644 --- a/AUTHORS +++ b/AUTHORS @@ -1,3 +1,2 @@ Clement Ballabriga <Clement.Ballabriga@univ-lille.fr> -Julien Forget <Julien.Forget@univ-lille.fr> -Jordy Ruiz <jordyruiz@gmail.com> +Sandro Grebant <sandro.grebant@univ-lille.fr> diff --git a/Makefile b/Makefile index 32c86a9..64ba3d5 100644 --- a/Makefile +++ b/Makefile @@ -1,15 +1,20 @@ -CXXFLAGS=`otawa/bin/otawa-config otawa/oslice --cflags` -LIBS=`otawa/bin/otawa-config otawa/oslice --libs` +CXXFLAGS=`otawa-config otawa/oslice --cflags` +LIBS=`otawa-config otawa/oslice --libs` LIBS+=-lppl CC=gcc CXX=g++ -CXXFLAGS+=-fPIC -Wall -DUSE_CLANG_COMPLETER -std=c++14 -O3 -march=native +CXXFLAGS+= -g -fPIC -Wall -DUSE_CLANG_COMPLETER -std=c++14 -O3 -march=native #CXXFLAGS+=-fPIC -Wall -DUSE_CLANG_COMPLETER -std=c++14 -O0 -g -all: poly.so +default: poly.so install test_poly + +all: clean poly.so install test_poly + +test_poly: + cd tests;$(MAKE) dbg: CXXFLAGS += -DELM_LOG dbg: poly.so @@ -17,17 +22,23 @@ dbg: poly.so debug: CXXFLAGS += -DPOLY_DEBUG debug: poly.so -poly.so: poly_PolyAnalysis.o poly_PlugHook.o poly_PPLDomain.o poly_PPLManager.o poly_PolyWrap.o - $(CC) -shared -o poly.so poly_PolyAnalysis.o poly_PlugHook.o poly_PPLDomain.o poly_PPLManager.o poly_PolyWrap.o $(LIBS) - +poly.so: poly_PolyAnalysis.o poly_PlugHook.o poly_PPLDomain.o poly_PPLManager.o poly_PolyWrap.o poly_BranchConditionner.o poly_LoopAnalyzer.o + $(CC) -shared -o poly.so poly_PolyAnalysis.o poly_PlugHook.o poly_PPLDomain.o poly_PPLManager.o poly_PolyWrap.o poly_BranchConditionner.o poly_LoopAnalyzer.o $(LIBS) + +poly_BranchConditionner.o: poly_BranchConditionner.cpp include/BranchConditionner.h include/PPLDomain.h include/PolyWrap.h include/PolyAnalysis.h + $(CXX) $(CXXFLAGS) -c poly_BranchConditionner.cpp -o poly_BranchConditionner.o + +poly_LoopAnalyzer.o: poly_BranchConditionner.cpp include/BranchConditionner.h include/PPLDomain.h include/PolyWrap.h include/PolyAnalysis.h + $(CXX) $(CXXFLAGS) -c poly_LoopAnalyzer.cpp -o poly_LoopAnalyzer.o + -poly_PolyAnalysis.o: poly_PolyAnalysis.cpp include/PolyAnalysis.h include/PPLDomain.h include/PolyWrap.h include/PPLManager.h include/PolyCommon.h include/MyHTable.h include/OrderedDriver.h +poly_PolyAnalysis.o: poly_PolyAnalysis.cpp include/PolyAnalysis.h include/PPLDomain.h include/PolyWrap.h include/PPLManager.h include/PolyCommon.h include/MyHTable.h include/OrderedDriver.h include/BranchConditionner.h $(CXX) $(CXXFLAGS) -c poly_PolyAnalysis.cpp -o poly_PolyAnalysis.o poly_PPLManager.o: poly_PPLManager.cpp include/PolyAnalysis.h include/PPLDomain.h include/PolyWrap.h include/PPLManager.h include/PolyCommon.h include/MyHTable.h include/OrderedDriver.h $(CXX) $(CXXFLAGS) -c poly_PPLManager.cpp -o poly_PPLManager.o -poly_PPLDomain.o: poly_PPLDomain.cpp include/PolyAnalysis.h include/PPLDomain.h include/PolyWrap.h include/PPLManager.h include/PolyCommon.h include/MyHTable.h include/OrderedDriver.h +poly_PPLDomain.o: poly_PPLDomain.cpp include/PolyAnalysis.h include/PPLDomain.h include/PolyWrap.h include/PPLManager.h include/PolyCommon.h include/MyHTable.h include/OrderedDriver.h include/BranchConditionner.h $(CXX) $(CXXFLAGS) -c poly_PPLDomain.cpp -o poly_PPLDomain.o poly_PlugHook.o: poly_PlugHook.cpp diff --git a/include/BranchConditionner.h b/include/BranchConditionner.h new file mode 100644 index 0000000..63d1a79 --- /dev/null +++ b/include/BranchConditionner.h @@ -0,0 +1,173 @@ +#ifndef BC +#define BC 1 + +#include <otawa/otawa.h> +#include "PolyWrap.h" +#include "PPLDomain.h" + +#define CONSTRAINTS_FILE "constraints.csv" // the file containing parametric conditionals +#define MAX_LOOP 1000000 // macro used to have a better loop management for parametric conditionals + +namespace otawa { +namespace poly { +using namespace otawa; + /** + * Map the function arguments using register number (0 <= r <= 3) + * > 3 means that the value is in the stack (i.g. a function with more than 4 arguments) + * This identifier should be used on the function's CFG entry node + */ + extern Identifier<std::map<int,WVar>> FUNCTION_ARGS; + + /** + * Remember which variables are used in functions (useful for polyhedron projections) + * It maps a variable to the id of its special identifier + * This identifier marks a function argument variable as used + * It should be used on the function's CFG entry node + */ + extern Identifier<std::map<int,WVar>> USED_ARGS; + + /** + * Stores the root CFG on any CFG + */ + extern Identifier<CFG*> ROOT_CFG; + + /** + * Identifies single function calls in loops + * std::map<LOOP_HEADER_ID,std::map<FUNCTION_NAME,INSTANCE>> + */ + extern Identifier<std::map<int,std::map<string,int>>> CURRENT_INSTANCES; + + /** + * Tells if a loop has exited + */ + extern Identifier<bool> LOOP_RESET; + + /** + * Each loop header knows his parent loop + */ + extern Identifier<int> PREVIOUS_LOOP; + + /** + * Arm function arguments detector + * Enables to manage functino arguments and to track function arguments usage + */ + class ArmArgumentsDetector{ + public: + /** + * mark function parameters at the begining of the analysis + * @param s the initial state of the analysis + */ + void initFunctionParameters(PPLDomain* s, Block* cfgEntry); + /** + * Check if a variable is a marked as used program argument + * It should be used in order to choose the variable needed to make a polyhedron projection + * @param cfgEntry the entry point of the CFG + * @param v the variable to check + * @return the id of the parameter if exists, -1 otherwise + */ + int getParamId(Block* cfgEntry, WVar v); + }; + + /** + * Simple object class to represent the exported conditional statement + */ + class Conditional{ + private: + Block* b; + bool taken; + std::string conditions; + public: + /** + * Constructor + * @param b the basic block corresponding to the condition + * @param taken tell if the condition is taken or not + * @param conditions the conditions as a string + */ + Conditional(Block* b, bool taken, std::string conditions); + /** + * block accessor + * @return the basic block of the condition + */ + Block* getBlock(); + /** + * taken accessor + * @return if the conditional is taken or not + */ + bool isTaken(); + /** + * conditions accessor + * @return the conditions as a string + */ + std::string getConditions(); + }; + + /** + * Abstract representation of an ordered element to export + */ + class OrderedConditional{ + private: + int loopId; //< identifies the order of the custom loopIds + Conditional conditional; //< if loopId == -1 then conditional != null + public: + OrderedConditional(int loopId, Conditional conditional); //< constructor for outer loop elements + OrderedConditional(int loopId); //< constructor for inner loops element + int getLoopId(); //< accessor + Conditional getConditional(); //< accessor + }; + + /** + * Keep an order for conditional inside/outside loops + */ + class OrderedElements{ + private: + std::map<int,OrderedConditional> order; + public: + OrderedElements(); + void addElement(OrderedConditional conditional); + std::map<int,OrderedConditional> getOrder(); + }; + + /** + * Remember global order form constraints + * it should be attached on root cfg + */ + extern Identifier<OrderedElements> EXPORT_ORDER; + + /** + * Store only the last condition when in a loop context + */ + extern Identifier<std::map<int,std::map<bool,Conditional>>> LOOP_CONDITIONALS; + /** + * Store the current loop context + */ + extern Identifier<int> CURRENT_LOOP; + /** + * A simple class to export constraints + */ + class ConstraintExporter{ + public: + /** + * Export constraints into a file + * @param branchingBlock the block inside which the branching instruction is + * @param taken if the condition is in the taken branch or the other + * @param conditions the conditionnal boolean expression + */ + void exportToFile(Conditional c); + /** + * Allows to remove the file containing data, this is needed at the begining of an analysis + * @return true if the file was deleted, false otherwise + */ + void resetFile(); + /** + * Manage particular case of loops + * @param order non loop conditionals + order + * @param loopConstraints loop conditionals + */ + //void exportLoopConstraints(std::map<int, std::map<bool,Conditional>> toExport); + void exportOrderedConstraints(OrderedElements order, std::map<int, std::map<bool,Conditional>> loopConstraints); + }; + +} +} + +#endif // !BC diff --git a/include/LoopAnalyzer.h b/include/LoopAnalyzer.h new file mode 100644 index 0000000..a538b7f --- /dev/null +++ b/include/LoopAnalyzer.h @@ -0,0 +1,82 @@ +#ifndef LA +#define LA 1 + +#include <fstream> +#include <otawa/otawa.h> +#include "PolyWrap.h" +#include "PPLDomain.h" + +#define LOOP_BOUNDS_FILE "loop_bounds.csv" // the file containing loop bounds +//#define LINBOUND // exports linear bounds when needed +//#define INTER_PROCEDURAL // required by LINBOUND + +namespace otawa{ +namespace poly{ + + using namespace otawa; + + /** + * A state analysis to detect induction variables + */ + class LoopAnalyzer{ + public: + /** + * Try to compute a linear loop bound from a projected polyhedron + * @param b the header of the loop + * @param proj the result of the projection + */ + void computeLinearBound(Block* b, std::vector<WCons> cs, MyHTable<int, int> mapping); + }; + + /** + * Represent a loop bound, which can be either static or linear + */ + class LoopBound{ + private: + bool linear; //< gives the kind of bound + int staticBound; //< static bound if exists + std::string linearBound; //< linear bound f exists + public: + /** + * Constructor + * @param linear true = linear, false = static + * @param staticBound the static bound if exists + * @param linearBouns the linear bound if exists + */ + LoopBound(bool linear, int staticBound, std::string linearBound); + /** + * Exports the constraint into the giver file + * @param o the output file stream + * @param lid the loop id corresponding to that bound + */ + void exportToFile(std::ofstream *o, int lid); + }; + + /** + * A simple class to export all the loop bounds at once + */ + class LoopBoundsExporter{ + public: + /** + * Exports all, the loop bounds into a file + * @param bounds the loop bounds to export + */ + void exportToFile(std::map<int,LoopBound> bounds); + }; + + /** + * Identifier to store loop bounds + * Attached to root cfg entry + */ + extern Identifier<std::map<int,LoopBound>> LOOP_BOUNDS; + + /** + * Identifier to store the constraints detected at each loop iteration + * Attached to the loop header block + */ + extern Identifier<std::vector<WCons>> CONSTRAINTS; + +} +} + +#endif diff --git a/include/PPLDomain.h b/include/PPLDomain.h index 5bb3b90..39ccfdc 100644 --- a/include/PPLDomain.h +++ b/include/PPLDomain.h @@ -15,7 +15,8 @@ // #include "Clp.h" #include "PolyCommon.h" #include "PolyWrap.h" - +#include "BranchConditionner.h" +#include "LoopAnalyzer.h" namespace otawa { namespace poly { @@ -96,7 +97,6 @@ class Ident { void print(io::Output &out) const; - inline bool operator==(const Ident &i) const { return (_id == i._id) && (_type == i._type); } inline bool operator!=(const Ident &i) const { return (_id != i._id) || (_type != i._type); } @@ -416,10 +416,6 @@ private: // }; public: - - - void showTab(const Ident&); - /* Basic operations (constructor, destructor, copy, comparison) */ /** @@ -668,6 +664,12 @@ private: * bounded. */ bound_t getLoopBound(int loopId) const; + + /** + * Tries to find a parametric loop bound + * @param b the loop header + */ + void computeParamBound(Block* b) const; inline bound_t getBound(int loopId) const { if (bounds.length() <= loopId) @@ -737,7 +739,7 @@ private: * @return The updated state. */ - PPLDomain onBranch(bool taken) const; + PPLDomain onBranch(bool taken, Block* b) const; /** * Process join and widening @@ -770,8 +772,8 @@ private: * unreachable/unbounded) * @return The updated state. */ - PPLDomain onLoopExit(int loop, int bound) const; - PPLDomain onLoopExitLinear(int loop, const PPLDomain &bound) const; + PPLDomain onLoopExit(int loop, int bound, Block* b) const; + PPLDomain onLoopExitLinear(int loop, const PPLDomain &bound, Block* b) const; /* Operations that modify the state in-place */ @@ -1153,7 +1155,6 @@ private: bool mayIntersect(const WVar &b1, const int s1, const WVar &n1, const WVar &b2, const int s2, const WVar &n2) const; bool mustSupseteq(const WVar &b1, const int s1, const WVar &n1, const WVar &b2, const int s2, const WLinExpr &n2) const; - private: /* Private helper functions. Subject to changes, and should not be used directly. */ diff --git a/include/PPLManager.h b/include/PPLManager.h index d182222..759137e 100644 --- a/include/PPLManager.h +++ b/include/PPLManager.h @@ -43,15 +43,10 @@ class PPLManager { inline bool equals(const t &v1, const t &v2) { return v1.equals(v2); } inline void enableSummary() { _init.enableSummary(); } - - void setName(elm::String str) { - cfg_name = str; - } private: t _init; t _bot; t _top; - elm::String cfg_name; }; } // namespace poly diff --git a/include/PolyAnalysis.h b/include/PolyAnalysis.h index 4f2bb3b..b35f4b3 100644 --- a/include/PolyAnalysis.h +++ b/include/PolyAnalysis.h @@ -14,6 +14,7 @@ #include <ppl.hh> #include "PPLManager.h" +#include "BranchConditionner.h" namespace otawa { namespace poly { diff --git a/include/PolyWrap.h b/include/PolyWrap.h index d2eaf6d..9c1304f 100644 --- a/include/PolyWrap.h +++ b/include/PolyWrap.h @@ -20,6 +20,7 @@ class WCons; class WLinExpr; class WPoly; +void parse(const coef_t& coef, char* buf); output_t& operator<< (output_t& stream, const coef_t&); output_t& operator<< (output_t& stream, const WLinExpr&); output_t& operator<< (output_t& stream, const WCons&); diff --git a/poly_BranchConditionner.cpp b/poly_BranchConditionner.cpp new file mode 100644 index 0000000..a234cf5 --- /dev/null +++ b/poly_BranchConditionner.cpp @@ -0,0 +1,127 @@ +#include <fstream> +#include<cstdio> + +#include "include/BranchConditionner.h" + +namespace otawa{ + namespace poly{ + + void ArmArgumentsDetector::initFunctionParameters(PPLDomain* s, Block* cfgEntry){ + if(cfgEntry->cfg() != ROOT_CFG(cfgEntry->cfg())) + return; + std::map<int,WVar> used = USED_ARGS(cfgEntry); + // track function arguments stored in registers + for(int reg=0; reg<=3; reg++){ + WVar r = s->varNew(Ident(reg, Ident::ID_REG), NO_STEP, false, false); //< create register variable + WVar sr = s->varNew(Ident(-reg-1, Ident::ID_SPECIAL), NO_STEP, false, false); //< create argument variable + s->doNewConstraint(sr == r); //< mapping initial register value with argument variable + used.emplace(reg,sr); //< add the function arguments to the set of needed variables for projection + } + USED_ARGS(cfgEntry) = used; + } + + int ArmArgumentsDetector::getParamId(Block* cfgEntry, WVar v){ + std::map<int,WVar> map = USED_ARGS(cfgEntry); + for(auto i = map.begin(); i != map.end(); i++){ + WVar cmp = (*i).second; + if(v.guid() == cmp.guid() && v.id() == cmp.id()) + return (*i).first; + } + return -1; + } + + Conditional::Conditional(Block* b, bool taken, std::string conditions){ + this->b = b; + this->taken = taken; + this->conditions = conditions; + } + + Block* Conditional::getBlock(){ + return b; + } + + bool Conditional::isTaken(){ + return taken; + } + + std::string Conditional::getConditions(){ + return conditions; + } + + OrderedConditional::OrderedConditional(int loopId, Conditional conditional) : conditional(conditional) { + this->loopId = loopId; + this->conditional = conditional; + } + + OrderedConditional::OrderedConditional(int loopId) : conditional(nullptr,false,"") { + this->loopId = loopId; + } + + int OrderedConditional::getLoopId(){ + return loopId; + } + + Conditional OrderedConditional::getConditional(){ + return conditional; + } + + OrderedElements::OrderedElements(){ + order = std::map<int,OrderedConditional>(); + } + + void OrderedElements::addElement(OrderedConditional conditional){ + order.emplace(order.size(), conditional); + } + + std::map<int,OrderedConditional> OrderedElements::getOrder(){ + return order; + } + + void ConstraintExporter::exportToFile(Conditional c){ + std::ofstream o; + o.open(CONSTRAINTS_FILE, std::ofstream::app); + o << c.getBlock()->toBasic()->control()->address() << ";"; + if(c.isTaken()) + o << "true;"; + else + o << "false;"; + o << c.getConditions() << endl; + o.close(); + } + + void ConstraintExporter::resetFile(){ + std::ofstream o; + o.open(CONSTRAINTS_FILE, std::ofstream::trunc); + o << std::string("Branching Instruction Address;taken;conditions") <<endl; + o.close(); + } + + void ConstraintExporter::exportOrderedConstraints(OrderedElements order, std::map<int, std::map<bool,Conditional>> loopConstraints){ + std::map<int,OrderedConditional> oc = order.getOrder(); + for(int i = 0; i < oc.size(); i++){ + OrderedConditional wrapper = oc.at(i); + // manage non loop elements + if(wrapper.getLoopId() == -1){ + exportToFile(wrapper.getConditional()); + } + // manage loop elements + else{ + std::map<bool,Conditional> branching = loopConstraints.at(wrapper.getLoopId()); + for(auto i = branching.begin(); i != branching.end(); i++){ + exportToFile((*i).second); + } + } + } + } + + Identifier<std::map<int,WVar>> FUNCTION_ARGS("otawa::poly::FUNCTION_ARGS", std::map<int,WVar>()); + Identifier<std::map<int,WVar>> USED_ARGS("otawa::poly::USED_ARGS", std::map<int,WVar>()); + Identifier<CFG*> ROOT_CFG("otawa::poly::ROOT_CFG", nullptr); + Identifier<std::map<int,std::map<string,int>>> CURRENT_INSTANCES("otawa::poly::CURRENT_INSTANCES",std::map<int,std::map<string,int>>()); + Identifier<bool> LOOP_RESET("otawa::poly::LOOP_RESET", false); + Identifier<std::map<int,std::map<bool,Conditional>>> LOOP_CONDITIONALS("otawa::poly::LOOP_CONDITIONALS", std::map<int,std::map<bool,Conditional>>()); + Identifier<int> CURRENT_LOOP("otawa::poly::CURRENT_LOOP", -1); + Identifier<int> PREVIOUS_LOOP("otawa::poly::PREVIOUS_LOOP", -1); + Identifier<OrderedElements> EXPORT_ORDER("otawa::poly::EXPORT_ORDER", OrderedElements()); +} // namespace poly +} // namespace otawa diff --git a/poly_LoopAnalyzer.cpp b/poly_LoopAnalyzer.cpp new file mode 100644 index 0000000..98755f4 --- /dev/null +++ b/poly_LoopAnalyzer.cpp @@ -0,0 +1,329 @@ +#include "include/LoopAnalyzer.h" + +namespace otawa{ +namespace poly{ + + LoopBound::LoopBound(bool linear, int staticBound, std::string linearBound){ + this->linear = linear; + this->staticBound = staticBound; + this->linearBound = linearBound; + } + + void LoopBound::exportToFile(std::ofstream *o, int lid){ + *o << lid; + if(linear) + *o << ";true;" << linearBound; + else + *o << ";false;" << staticBound; + *o << endl; + } + + void LoopBoundsExporter::exportToFile(std::map<int,LoopBound> bounds){ + // prepare the file + std::ofstream o; + o.open(LOOP_BOUNDS_FILE, std::ofstream::trunc); + o << std::string("Loop id;isLinear;bound") << endl; + // exports all the loop bunds + for(auto i = bounds.begin(); i != bounds.end(); i++){ + int lid = (*i).first; + LoopBound lb = (*i).second; + lb.exportToFile(&o, lid); + } + // closing stream + o.close(); + } + + void LoopAnalyzer::computeLinearBound(Block* b, std::vector<WCons> cs, MyHTable<int, int> mapping){ + + // drop constraints that do not contain any parameter (e.g. lower bound) + std::vector<WCons> kept; + for(auto c = cs.begin(); c != cs.end(); c++){ + WCons constraint = *c; + // cout << "CONSTRAINT: " << constraint << endl; + bool hasBound = false; + bool hasPA = false; + for(WLinExpr::TermIterator i(constraint.getLE()); i; i++){ + WVar v = *i; + // v0 = loop bound variable + if(v.guid() == 0) + hasBound = true; + // vx = entry parameters + if(v.guid() != 0) + hasPA = true; + // push only if the two are present + if(hasBound && hasPA){ + kept.push_back(constraint); + break; + } + } + + } + + // cout << "Number of constraints: " << kept.size() << endl; + + // extract remaining constraints as a loop bound + if(kept.size() == 1){ + + // single loop bound + auto c = kept.begin(); + WCons cons = *c; + + // keep coefcicient of the loop bound (division when induction > 1) + char bufSign[64]; + + // coef1 * v1 + ... + coefn * vn + constant + int first = 1; + std::string loopBound; + + // pretty printing in a string + for(WLinExpr::TermIterator i(cons.getLE()); i; i++){ + WVar v = *i; + coef_t coef = cons.coefficient(v); + + // manage induction and remove loop bound variable from constraint + if(v.guid() == 0){ + gmp_snprintf(bufSign, sizeof(bufSign), "%Zd", &PPL::raw_value(-coef)); // the bound is negative in the right side of the constraint + continue; + } + + // export the loop as an expression + // sign + std::string signStr = ""; + if(!first && coef > 0) + signStr = " + "; + else if(coef == -1) + signStr = " - "; + + // coef + std::string coefStr = ""; + if(coef != 1 && coef != -1){ + char buf[64]; + gmp_snprintf(buf, sizeof(buf), "%Zd", &PPL::raw_value(coef)); + coefStr = std::string(buf); + } + + // variable mapping + int paramId = -(mapping[v.guid()] + 1); // get the parameter id from the mapping + std::string variableStr = "b:" + std::to_string(paramId); + + if(first) + first = 0; + + // building constraint term + loopBound = coef != 1 && coef != -1 ? loopBound + signStr + coefStr + " * " + variableStr : loopBound + signStr + variableStr; + } + // final bound + coef_t constant = cons.inhomogeneous_term(); + if(constant != 0){ + char cstBuf[64]; + gmp_snprintf(cstBuf, sizeof(cstBuf), "%Zd", &PPL::raw_value(constant)); + std::string constantStr(cstBuf); + loopBound = constant > 0 ? loopBound + " + " + constantStr : loopBound + constantStr; // - is in the constant + } + + int induction = atoi(bufSign); + + // checks + // check 1 : induction == 0 means that the loop is infinite (or that the coefficient is not an integer value) + if(induction == 0) + return; + // check 2 : induction != 1 means that we must do a division to find the real loop bound + if(induction != 1) + loopBound = "(" + loopBound + ")/" + std::string(bufSign); + + // save the bound + LoopBound lb(true, 0, loopBound); + std::map<int,LoopBound> lbs = LOOP_BOUNDS(ROOT_CFG(b->cfg())->entry()); + if(lbs.find(b->id()) != lbs.end()) + lbs.erase(b->id()); + lbs.emplace(b->id(), lb); + LOOP_BOUNDS(ROOT_CFG(b->cfg())->entry()) = lbs; + + } + else{ + if(kept.size() != 0) + cout << "[WARN] Loop have more than a single bound, case not supported yet !" << endl; + } + + // OLD CODE + // std::vector<WCons> cs_bef = CONSTRAINTS(b); + + // std::map<int,LoopBound> lbs = LOOP_BOUNDS(ROOT_CFG(b->cfg())->entry()); + // // clear old data if exist + // if(lbs.find(b->id()) != lbs.end()) + // lbs.erase(b->id()); + + // // filtering needed to remove weird constraints which are not the maximum loop bound + // std::vector<WCons> cs; + // int addedIterations = 0; + // std::map<int,coef_t> maximums; + // // find the maximum value for each constraint + // for(auto i = cs_bef.begin(); i != cs_bef.end(); i++){ + // WCons c = *i; + // for(WLinExpr::TermIterator i(c.getLE()); i; i++){ + // WVar v = *i; + // coef_t coef = c.coefficient(v); + // if(maximums.find(v.guid()) == maximums.end()){ + // maximums.emplace(v.guid(),coef); + // } + // else{ + // if(maximums.at(v.guid()) > coef){ + // maximums.erase(v.guid()); + // maximums.emplace(v.guid(), coef); + // } + // } + // } + // } + // // remove constraints not representing the maximum value + // for(auto i = cs_bef.begin(); i != cs_bef.end(); i++){ + // bool add = true; + // WCons c = *i; + // for(WLinExpr::TermIterator i(c.getLE()); i; i++){ + // WVar v = *i; + // coef_t coef = c.coefficient(v); + // //cout << "var :" << v.guid() << ", coef: " << coef << ", should be = to " << maximums.at(v.guid()) << endl; + // if(coef > maximums.at(v.guid())){ + // add = false; + // break; + // } + // } + // if(add){ + // cs.push_back(c); + // //cout << "added " << c << endl; + // } else { + // if(i == cs_bef.begin() || cs.size() == 0){ + // // sometimes the first loop bound is not correctly extracted since polyhedra will simplify the constraint... + // // thus we need to correct the iteration number + // addedIterations++; + // } + // } + // } + + // // use remaining constraints to compute loop induction + // WCons previous; + // WCons current; + // coef_t current_diff = 0; + // bool error = cs.size() > 0 ? false : true; + // for(auto i = cs.begin(); i != cs.end(); i++){ + // current = *i; + + + // //cout << "ANALYZING " << current << endl; + + // // detect variace between two constraints + // if(!(i == cs.begin())){ + // coef_t const_current = current.inhomogeneous_term(); + // coef_t const_previous = previous.inhomogeneous_term(); + // coef_t diff = const_current - const_previous; + // //cout << "DIFF = " << diff << ", OLD DIFF = " << current_diff << endl; + // if(current_diff != 0 && current_diff != diff){ + // error = true; //< This means that we do not have a constant induction + // cout << "WARN: There is a difference between loop iterations ? diff = " << diff << ", current diff = " << current_diff << endl; + // } + // else{ + // cout << "INFO: Found a regular induction:" << diff << ", " << current_diff << endl; + // error = false; + // } + // current_diff = diff; + // } + // previous = current; + // } + // if(error || current_diff == 0 || cs.size() < 2){ + // // Unable to compute loop induction + // if(error && cs.size() >= 2) + // cout << "WARN: Could not compute a constant loop induction" << endl; + // else if(cs.size() < 2) + // cout << "INFO: Could not compute a loop induction (too few iterations)" << endl; + // else if(current_diff == 0) + // cout << "WARN: Loop induction 0 detected, possibly an infinite loop" << endl; + + // } + // else{ + // // Found a loop induction and thus exports the parametric loop bound + // + // //cout << "COMPUTED DIFFERENCE = " << current_diff << endl; + // WCons c = *(cs.begin()); // use the first loop exit constraint to compute the bound + + // // coef_1 * v_1 + ... + coef_n * v_n + constant + // // retrieve constant + // coef_t constant = c.inhomogeneous_term(); + // //cout << "constant: " << constant << endl; + + // // format constraint as a string, which is easier to export in a file + // // we need to reverse the constraint since we are in the loop exit case + // std::string cString = /*"0 " + op*/""; + // std::string opmult = " * "; + // std::string opadd = " + "; + // std::string opminus = " - "; + // char buf[128]; + // int index=0; + // for(WLinExpr::TermIterator i(c.getLE()); i; i++){ + // WVar v = *i; + // coef_t coef = c.coefficient(v); + // + // // manage operator + // std::string opv = index == 0 ? "" : " + "; + // opv = -coef > 0 ? opv : " - "; + + // // manage coef != 1 && != 1 + // if(coef > 1 || coef < -1){ + // coef > 0 ? parse(coef, buf) : parse(-coef, buf); + // std::string cs(buf); + // opv = opv + cs + opmult; + // } + + // // manage variable name + // std::string var = std::to_string(v.guid()); + // opv = opv + "b:" + var; + // cString = cString + opv; + + // ++index; + // } + + // // manage constant value + // // correction of part of bug 1 : - when += >2, even if bugued for now + // /*cout << "CURRENT_DIFF = " << current_diff << endl; + + // constant = current_diff > 0 ? constant - current_diff : constant; + // constant = current_diff < 0 ? constant + current_diff : constant;*/ + // + // // manage constant sign + // std::string opconst = -constant > 0 ? opadd : opminus; + + // // correction of the loop bound + // if(addedIterations != 0){ + // constant = current_diff > 0 ? constant - (current_diff*addedIterations) : constant; + // constant = current_diff < 0 ? constant + (current_diff*addedIterations) : constant; + // } + + // constant = -constant > 0 ? -constant : constant; + + // parse(constant, buf); + // std::string cst(buf); + // if(cst != "0") + // cString = cString + opconst + cst; + + // // support for more than i+=1 (i+=x), involves a division operator in the resulting expression + // if(current_diff > 1 || current_diff < -1){ + // cout << "WARN: Unsupported loop bound (induction > 1 || induction < -1)" << endl; + // return; // FIXME it does not work in all cases and produces bugs + // std::cout << "cString before: " << cString << endl; + // ASSERT(current_diff > 0); // cannot be < 0 since it is a loop bound ? + // parse(current_diff, buf); + // cString = "(" + cString + ")/" + buf; + // } + + // std::cout << "COMPUTED LOOP BOUND(" << b->id() << ") = " << cString << endl; + // // save the loop bound + // LoopBound lb(true, 0, cString); + // lbs.emplace(b->id(), lb); + // } + // LOOP_BOUNDS(ROOT_CFG(b->cfg())->entry()) = lbs; + } + + Identifier<std::map<int,LoopBound>> LOOP_BOUNDS("otawa::poly::LOOP_BOUNDS", std::map<int,LoopBound>()); + Identifier<std::vector<PPLDomain>> LOOP_STATES("otawa::poly::LOOP_STATES", std::vector<PPLDomain>()); + Identifier<std::vector<WCons>> CONSTRAINTS("ortawa::poly::CONSTRAINTS", std::vector<WCons>()); + +} +} diff --git a/poly_PPLDomain.cpp b/poly_PPLDomain.cpp index 9f00f3e..abf98a4 100644 --- a/poly_PPLDomain.cpp +++ b/poly_PPLDomain.cpp @@ -215,8 +215,8 @@ void PPLDomain::print(io::Output &out) const { #ifdef INTER_PROCEDURAL cout << "Summary info: " << endl; cout << "- Inputs: "; - for (MyHTable<Ident, guid_t, HashIdent>::PairIterator it = idmap.getPairIter(); it; it++) { - const Pair<Ident, guid_t> &p = *it; + for (MyHTable<Ident, idval_t, HashIdent>::PairIterator it = idmap.getPairIter(); it; it++) { + const Pair<Ident, idval_t> &p = *it; if ((p.fst.getType() == Ident::ID_MEM_VAL_INPUT) || (p.fst.getType() == Ident::ID_REG_INPUT)) cout << p.fst << ", "; } @@ -410,7 +410,7 @@ void PPLDomain::displayLocVars(io::Output &out) const { //out << i << endl; WPoly poly_copy = poly; WVar v(num_axis); - poly_copy.add_constraint(v == ssp - i); + poly_copy.add_constraint(v == ssp - i - LOC_VAR_SIZE(_props)); out << " [SSP - " << hex(i) << "] ∈ "; bool found = false; for (MyHTable<Ident, idval_t, HashIdent>::PairIterator it = idmap.getPairIter(); it; it++) { @@ -732,11 +732,11 @@ PPLDomain PPLDomain::getLinearExpr(const Ident &id) { MyHTable<int, int> inputs; int axis = 1; PPLDomain dom(*this); /* make a working copy to do the projections */ - inputs[getVar(id).id()] = 0; - for (MyHTable<Ident, guid_t, HashIdent>::PairIterator it = idmap.getPairIter(); it; it++) { + inputs[getVar(id, nullptr).id()] = 0; + for (MyHTable<Ident, idval_t, HashIdent>::PairIterator it = idmap.getPairIter(); it; it++) { if (((*it).fst.getType() == Ident::ID_REG_INPUT) || ((*it).fst.getType() == Ident::ID_MEM_VAL_INPUT)) { - inputs[(*it).snd] = axis; + inputs[(*it).snd.g] = axis; axis++; } } @@ -785,6 +785,43 @@ void PPLDomain::doAvExpand(WVar &splus, WVar &sminus, WVar &vplus, WVar &vminus) } +void PPLDomain::computeParamBound(Block* header) const { + int loopId = header->id(); + int axis = 1; + if (isBottom()) { + // cout << "L'etat de la loop " << loopId << " est bottom\n\n"; + return; + } + MyHTable<int, int> inputs; + MyHTable<int, int> paramMapping; + Ident id(loopId, Ident::ID_LOOP); + + inputs[getVar(id, nullptr).id()] = 0; + + for (MyHTable<Ident, idval_t, HashIdent>::PairIterator it = idmap.getPairIter(); it; it++) { + if (((*it).fst.getType() == Ident::ID_SPECIAL) && (*it).fst.getId() < 0) { + inputs[(*it).snd.g] = axis; + paramMapping[axis] = (*it).fst.getId(); + axis++; + } + } + PPLDomain dom(*this); + // cout << "Etat avant projection: \n\n" << dom << "\n\n"; + dom.doMap(MapWithHash(inputs)); + // cout << "Etat projeté: \n\n" << dom << "\n\n"; + + // copy poly constraints + std::vector<WCons> csts; + for (WPoly::ConsIterator it(dom.poly); it; it++) { + const WCons &c = *it; + csts.push_back(c); + } + // extract the loop bound + LoopAnalyzer la; + la.computeLinearBound(header, csts, paramMapping); + + return; +} // getLoopBound: a appeler a l'INTERIEUR de la boucle pour avoir une maximisation de la variable d'induction bound_t PPLDomain::getLoopBound(int loopId) const { @@ -800,21 +837,21 @@ bound_t PPLDomain::getLoopBound(int loopId) const { return bound_t::UNBOUNDED; } -PPLDomain PPLDomain::onLoopExitLinear(int loop, const PPLDomain &bound) const { +PPLDomain PPLDomain::onLoopExitLinear(int loop, const PPLDomain &bound, Block* b) const { PPLManager::t s_out = *this; #ifdef LINBOUND - cout << "before onLoopExitLinear: " << s_out << endl; + //cout << "before onLoopExitLinear: " << s_out << endl; cout << "bound: " << bound << endl; Ident id(loop, Ident::ID_LOOP); ASSERT(s_out.hasIdent(id)); /* You are supposed to be already inside the loop when you call onLoopExit() */ - WVar v = s_out.getVar(id); + WVar v = s_out.getVar(id, nullptr); MyHTable<int,int> map; Vector<int> mapped; - for (MyHTable<Ident, guid_t, HashIdent>::PairIterator it = idmap.getPairIter(); it; it++) { + for (MyHTable<Ident, idval_t, HashIdent>::PairIterator it = idmap.getPairIter(); it; it++) { if (((*it).fst.getType() == Ident::ID_REG_INPUT) || ((*it).fst.getType() == Ident::ID_MEM_VAL_INPUT) || ((*it).fst == id)) { if (hasIdent((*it).fst)) { - const WVar &v2 = getVar((*it).fst); - map[(*it).snd] = v2.id(); + const WVar &v2 = getVar((*it).fst, nullptr); + map[(*it).snd.g] = v2.id(); mapped.add(v2.id()); } } @@ -830,14 +867,57 @@ PPLDomain PPLDomain::onLoopExitLinear(int loop, const PPLDomain &bound) const { s_out.poly.intersection_assign(copy.poly); - s_out.varKill(v); - cout << "after onLoopExitLinear: " << s_out << endl; + s_out.varKill(v, NO_STEP); + //cout << "after onLoopExitLinear: " << s_out << endl; + + // addon to support linear loop bounds related to entry parameters + /*if(!(*this).isBottom()){ + MyHTable<int, int> inputs; // prepare projection map + PPLDomain dom(*this); // copying the domain + + //cout << "BEFORE PROJECTION:" << endl << dom << endl; + + // retrieve function parameters + Block* entry = ROOT_CFG(b->cfg())->entry(); + std::map<int,WVar> used = USED_ARGS(entry); + + int usedSize = 0; + if(used.size() > 0){ + for (MyHTable<Ident, idval_t, HashIdent>::PairIterator it = idmap.getPairIter(); it; it++) { + int tmp = 0; + WVar v = dom.getVar((*it).fst, &tmp); + ArmArgumentsDetector detector; + int reg_id = detector.getParamId(entry, v); + if(dom.hasIdent((*it).fst) && reg_id != -1){ + inputs[(*it).snd.g] = reg_id; + //cout << "v" << (*it).snd.g << " = v" << reg_id << endl; + usedSize++; + } + } + //cout << "INPUT SIZE: " << inputs.count() << endl; + + dom.doMap(MapWithHash(inputs)); + //cout << "AFTER PROJECTION:" << endl << dom << endl; + + // handle constraintq + std::vector<WCons> csts = CONSTRAINTS(b); + for (WPoly::ConsIterator it(dom.poly); it; it++) { + const WCons &c = *it; + csts.push_back(c); + } + CONSTRAINTS(b) = csts; + LoopAnalyzer la; + la.computeLinearBound(b); + } + }*/ + // end of addon + #endif return s_out; } -PPLDomain PPLDomain::onLoopExit(int loop, int bound) const { +PPLDomain PPLDomain::onLoopExit(int loop, int bound, Block* b) const { PPLManager::t s_out = *this; Ident id(loop, Ident::ID_LOOP); ASSERT(s_out.hasIdent(id)); /* You are supposed to be already inside the loop when you call onLoopExit() */ @@ -847,6 +927,49 @@ PPLDomain PPLDomain::onLoopExit(int loop, int bound) const { s_out.poly.add_constraint(v <= bound); } s_out.varKill(v, NO_STEP); + + // addon to support linear loop bounds related to entry parameters + /*if(!(*this).isBottom()){ + MyHTable<int, int> inputs; // prepare projection map + PPLDomain dom(*this); // copying the domain + + //cout << "BEFORE PROJECTION:" << endl << dom << endl; + + // retrieve function parameters + Block* entry = ROOT_CFG(b->cfg())->entry(); + std::map<int,WVar> used = USED_ARGS(entry); + + int usedSize = 0; + if(used.size() > 0){ + for (MyHTable<Ident, idval_t, HashIdent>::PairIterator it = idmap.getPairIter(); it; it++) { + int tmp = 0; + WVar v = dom.getVar((*it).fst, &tmp); + ArmArgumentsDetector detector; + int reg_id = detector.getParamId(entry, v); + if(dom.hasIdent((*it).fst) && reg_id != -1){ + inputs[(*it).snd.g] = reg_id; + //cout << "v" << (*it).snd.g << " = v" << reg_id << endl; + usedSize++; + } + } + //cout << "INPUT SIZE: " << inputs.count() << endl; + + dom.doMap(MapWithHash(inputs)); + //cout << "AFTER PROJECTION:" << endl << dom << endl; + + // handle constraintq + std::vector<WCons> csts = CONSTRAINTS(b); + for (WPoly::ConsIterator it(dom.poly); it; it++) { + const WCons &c = *it; + csts.push_back(c); + } + CONSTRAINTS(b) = csts; + LoopAnalyzer la; + la.computeLinearBound(b); + } + }*/ + // end of addon + if (s_out.poly.is_empty()) { #ifdef POLY_DEBUG cout << "State is Bottom after onLoopExit (will not propagate states to exit-edges)" << endl; @@ -875,7 +998,7 @@ PPLDomain PPLDomain::onLoopEntry(int loop) const { return s_out; } -PPLDomain PPLDomain::onBranch(bool taken) const { +PPLDomain PPLDomain::onBranch(bool taken, Block* b) const { if (isBottom()) { return PPLDomain(); } @@ -887,19 +1010,22 @@ PPLDomain PPLDomain::onBranch(bool taken) const { cout << "Filtering, compare_reg is: " << compare_reg << ", compare_op is: " << compare_op << ", taken=" << taken << endl; #endif - + // cout << "======================================================= " << b->id() << " " << taken << endl; + // cout << "state before OP:" << endl << res << endl; switch (this_op) { case sem::NE: { WPoly poly2 = res.poly; res.poly.add_constraint(res.getVar(compare_reg, nullptr) <= -1); poly2.add_constraint(res.getVar(compare_reg, nullptr) >= 1); res.poly.poly_hull_assign(poly2); + // cout << "NE" << endl; break; PDBG("NE!" << endl) } case sem::EQ: PDBG("EQ!" << endl) res.poly.add_constraint(res.getVar(compare_reg, nullptr) == 0); + // cout << "EQ" << endl; break; case sem::GE: case sem::UGE: @@ -924,6 +1050,175 @@ PPLDomain PPLDomain::onBranch(bool taken) const { default: break; }; + + //projection of res to get conditions + if (!res.isBottom()) { + MyHTable<int, int> inputs; + PPLDomain dom(res); /* make a working copy to do the projections */ + // cout << "State after OP:" << endl << dom << endl; + //std::map<int,WVar> used = USED_ARGS(b->cfg()->entry()); + Block* entry = ROOT_CFG(b->cfg())->entry(); + std::map<int,WVar> used = USED_ARGS(entry); + //cout << "SIZE OF USED: " << used.size() << endl; + if(used.size() != 0){ + //inputs[res.getVar(compare_reg).id()] = 0; + for (MyHTable<Ident, idval_t, HashIdent>::PairIterator it = idmap.getPairIter(); it; it++) { + int tmp = 0; + WVar v = dom.getVar((*it).fst, &tmp); + ArmArgumentsDetector detector; + int reg_id = detector.getParamId(entry, v); + if(dom.hasIdent((*it).fst) && reg_id != -1){ + inputs[(*it).snd.g] = reg_id; + //cout << "v" << (*it).snd.g << " = v" << reg_id << endl; + } + } + //cout << "INPUT SIZE: " << inputs.count() << endl; + dom.doMap(MapWithHash(inputs)); + + // handle constraints + std::string constraints = ""; + for (WPoly::ConsIterator it(dom.poly); it; it++) { + const WCons &c = *it; + + //cout << "ref: " << c << endl; + + // c = 0 <OP> coef_1 * v_1 + ... + coef_n * v_n + constant + // retrieve constant + coef_t constant = c.inhomogeneous_term(); + //cout << "constant: " << constant << endl; + + // retrieve function parameters + std::map<guid_t, coef_t> vs; ///< first = parameter register, second = coef + int i = 0; + for(WPoly::VarIterator vi(dom.poly); vi; vi++){ + WVar v = *vi; + if(c.has_var(v)){ + coef_t coef = c.coefficient(v); + //cout << b->cfg()->name() << "[" << v.guid() << "] * " << coef << endl; + vs.emplace(v.guid(), coef); + } + } + + // retrieve OP + std::string op; + switch (c.getType()) + { + case CONS_EQ: + op = "="; + break; + case CONS_GE: + op = "≤"; + break; + default: + cout << "[ERROR] Unrecognized constraint type while searching for branch conditions" << endl; + abort(); + break; + } + + // format constraint as a string, which is easier to export in a file + std::string cString = "0 " + op; + std::string opmult = " * "; + std::string opadd = " + "; + std::string opminus = " - "; + char buf[128]; + for(auto i = vs.begin(); i != vs.end(); i++){ + if((*i).second > 0){ + std::string opv = " "; + if(i != vs.begin()){ + opv = opadd; + } + if((*i).second != 1){ + parse((*i).second, buf); + std::string coef(buf); + opv = opv + coef; + opv = opv + opmult; + } + cString = cString + opv; + } + else{ + std::string opv = opminus; + if((*i).second != -1){ + parse(-((*i).second), buf); + std::string coef(buf); + opv = opv + coef; + opv = opv + opmult; + } + cString = cString + opv; + } + std::string var2 = std::to_string((*i).first); + std::string var = "b:" + var2; + cString = cString + var; + } + if(constant != 0){ + std::string opconst = ""; + if(constant > 0){ + opconst = opadd; + parse(constant, buf); + } + else{ + opconst = opminus; + parse(-constant, buf); + } + std::string cst(buf); + cString = cString + opconst; + cString = cString + cst; + } + std::string concat = ""; + if(constraints.length() != 0) + concat = " & "; + cString = concat + cString; + constraints = constraints + cString; + } + + if(constraints != ""){ + //std::cout << "constraints found for " << b->toBasic()->control()->address() << ": " << constraints << endl; + // export constraints + // only if we are not in a loop + Conditional c(b, taken, constraints); + int current_loop = CURRENT_LOOP(ROOT_CFG(b->cfg())); + if(current_loop == -1){ + //ConstraintExporter exp; + //exp.exportToFile(c); + // we now export constraints later in order + OrderedElements orderingManager = EXPORT_ORDER(ROOT_CFG(b->cfg())); + OrderedConditional oc(current_loop, c); + orderingManager.addElement(oc); + EXPORT_ORDER(ROOT_CFG(b->cfg())) = orderingManager; + } + // otherwise store it to keep only the last conditional and export it later on + else{ + std::map<int, std::map<bool,Conditional>> loopConditionalMap = LOOP_CONDITIONALS(ROOT_CFG(b->cfg())); + // try to get current map if exists + std::map<bool,Conditional> conditional; + bool exists = true; + if(loopConditionalMap.find(current_loop) != loopConditionalMap.end()){ + conditional = loopConditionalMap.at(current_loop); + } + else{ + conditional = std::map<bool,Conditional>(); + exists = false; + // it does not exist yet so we can push it in ordered export + OrderedElements orderingManager = EXPORT_ORDER(ROOT_CFG(b->cfg())); + OrderedConditional oc(current_loop); + orderingManager.addElement(oc); + EXPORT_ORDER(ROOT_CFG(b->cfg())) = orderingManager; + } + // replace the conditional with current conditional + conditional.erase(taken); + conditional.emplace(taken, c); + loopConditionalMap.erase(current_loop); + loopConditionalMap.emplace(current_loop, conditional); + LOOP_CONDITIONALS(ROOT_CFG(b->cfg())) = loopConditionalMap; + } + } + else{ + //cout << "no constraints found for " << b->toBasic()->control()->address() << endl; + //cout << "STATE WITH NO CONSTRAINT RELATED TO MARKED PROGRAM PARAMETERS" << endl << "DOM (projected poly):" << endl << dom << endl << "RES (original poly):" << endl << res << endl; + } + } + } + + //res.victims.add(res.getVar(res.compare_reg).guid()); res.varKill(res.compare_reg); res.compare_reg = Ident(); @@ -1581,6 +1876,7 @@ PPLDomain PPLDomain::onSemInst(const BasicBlock *bb, const sem::inst &si, int in bool isArray = _isArrayStore(bb, storeAddr); + // cout << "arraystore: " << isArray << endl; sem::reg_t src = si.d(); Ident idStoreValue(src, Ident::ID_REG); @@ -1721,12 +2017,12 @@ PPLDomain PPLDomain::onSemInst(const BasicBlock *bb, const sem::inst &si, int in Ident idAddrNew(s_out.mem_ref - 1, Ident::ID_MEM_ADDR); int step; WVar newAddr = s_out.getVar(idAddrNew, &step); - - cout << "Create var with arraystore, attempt to find existing array" << endl; + // cout << "Create var with arraystore, attempt to find existing array" << endl; + cout << "\n"; for (MyHTable<Ident, idval_t, HashIdent>::PairIterator it = s_out.idmap.getPairIter(); it; it++) { if ((*it).fst.getType() == Ident::ID_MEM_ADDR) { if ((*it).fst == idAddrNew) break; - cout << "Checking if tab " << (*it).fst << " is the start of " << idAddrNew << " \n"; + // cout << "Checking if tab " << (*it).fst << " is the start of " << idAddrNew << " \n"; WVar tabAddr = s_out.getVar((*it).fst, &step); Ident idCountAddr((*it).fst.getId(), Ident::ID_MEM_COUNT); WVar count = s_out.getVar(idCountAddr, nullptr); @@ -1744,27 +2040,25 @@ PPLDomain PPLDomain::onSemInst(const BasicBlock *bb, const sem::inst &si, int in int tmp = num.get_si() / den.get_si(); if (tmp > 0) { step = tmp; - cout << "Guessed step: " << step << endl; + // cout << "Guessed step: " << step << endl; } } } if (step != STEP_BOT && s_out.shouldFold(tabAddr, step, count, newAddr)) { fold = true; - cout << "yes (keep existing step: " << step << ") " << endl; + // cout << "yes (keep existing step: " << step << ") " << endl; } else { - cout << "no." << endl; + // cout << "no." << endl; } Ident idValPlus((*it).fst.getId(), Ident::ID_MEM_VAL_PLUS); // cout << step << " " << fold << " " << s_out.hasIdent(idValPlus) << "\n"; if (fold && s_out.hasIdent(idValPlus)) { + cout << "actually folding with: " << step << endl; - cout << "Folding store " << idAddrNew << " into existing array " << (*it).fst << endl; - - s_out.showTab((*it).fst); - + // cout << "Folding store " << idAddrNew << " into existing array " << (*it).fst << endl; Ident idValMinus((*it).fst.getId(), Ident::ID_MEM_VAL_MINUS); Ident idValNew(idAddrNew.getId(), Ident::ID_MEM_VAL); Ident idCountNew(idAddrNew.getId(), Ident::ID_MEM_COUNT); @@ -1925,11 +2219,11 @@ PPLDomain PPLDomain::onSemInst(const BasicBlock *bb, const sem::inst &si, int in const WVar &v = s_out.memSingleCreate(loadAddr, loadReg, false); if (s_out._summary != nullptr) { // Unknown LOAD value. If we are summarizing, create an input. - Ident idInputAddr = s_out.getIdent(v); + Ident idInputAddr = s_out.getIdent(v, NULL); Ident idInputVal = Ident(idInputAddr.getId(), Ident::ID_MEM_VAL_INPUT); Ident idCurrentVal = Ident(idInputAddr.getId(), Ident::ID_MEM_VAL); - WVar currentVal = s_out.getVar(idCurrentVal); - WVar inputVal = s_out.varNew(idInputVal); // c'est une variable qu'on lit donc pas de damaged + WVar currentVal = s_out.getVar(idCurrentVal, NULL); + WVar inputVal = s_out.varNew(idInputVal, NULL, false, false); // c'est une variable qu'on lit donc pas de damaged PDBG("Summarizing: creating new input memory: " << " what= " << idInputVal << " where=" << idInputAddr << endl) // s_out._summary->_inputs.add(idInputVal); s_out.doNewConstraint(currentVal == inputVal); @@ -2275,7 +2569,8 @@ void PPLDomain::listMemoryVariables() { num_loop++; } } - cout << (num_av + num_sing + num_tab) << " var mem, dont " << num_av << " avatars (x4), " << num_tab << " tab (x3), et " << num_sing << " acces singuliers (x3), " << num_reg << " regs, " << num_loop << " boucles, et " << num_spe << "spe" << "(tot: " << total << ")\n"; + // DEBUG + //cout << (num_av + num_sing + num_tab) << " var mem, dont " << num_av << " avatars (x4), " << num_tab << " tab (x3), et " << num_sing << " acces singuliers (x3), " << num_reg << " regs, " << num_loop << " boucles, et " << num_spe << "spe" << "(tot: " << total << ")\n"; if ((num_av*4 + (num_sing + num_tab)*3 + num_reg + num_loop + num_spe) != total) { print(cout); @@ -2407,7 +2702,7 @@ WVar PPLDomain::getVarOrNew(const Ident &ident, bool create_input) { ASSERT(ident.getType() == Ident::ID_REG); Ident idInput(ident.getId(), Ident::ID_REG_INPUT); // _summary->_inputs.add(idInput); - WVar input = varNew(idInput); + WVar input = varNew(idInput, NULL, false, false); doNewConstraint(input == v); PDBG("Summarizing: creating new input register " << input << endl) } @@ -2977,7 +3272,7 @@ void PPLDomain::_doMatchGlobals(PPLDomain &l1, PPLDomain &r1, uint32_t staticVal; dfa::INITIAL_STATE(_ws)->get(staticAddr, staticVal); // get static constant value from OTAWA PDBG("Static adress: " << hex(staticAddr) << " value: " << hex(staticVal) << endl) - cout << "Static adress: " << hex(staticAddr) << " value: " << hex(staticVal) << endl; + DBG("Static adress: " << hex(staticAddr) << " value: " << hex(staticVal) << endl) Ident leftIdAddr = l1.getIdent(v, STEP_BOT); /* TODO get initial values for arrays */ Ident leftIdVal = Ident(leftIdAddr.getId(), Ident::ID_MEM_VAL); @@ -3122,10 +3417,8 @@ void PPLDomain::_doUnify(PPLDomain &l1 /* back */, PPLDomain &r1 /* header */, P invLeftCSMap.put((*it).snd, (*it).fst); } - /* _doMatchGlobals(l1, r1, leftCSMap, invRightCSMap); _doMatchGlobals(r1, l1, rightCSMap, invLeftCSMap); - */ /* Detect variable in left (i.e. backstate) subjected to avatar creation */ if (avcreate) { for (MyHTable<guid_t, LEVect >::PairIterator it(leftCSMap); it; it++) { @@ -3142,7 +3435,7 @@ void PPLDomain::_doUnify(PPLDomain &l1 /* back */, PPLDomain &r1 /* header */, P guid_t newBase = 42, newCount = 42; guid_t newValPlus = 42, newValMinus = 42; - /* cout << "Creating avatar for backstate base variable: " << (*it).fst << endl; */ + // cout << "Creating avatar for backstate base variable: " << (*it).fst << endl; bool created = PPLDomain::_avcreate_helper(r1, l1, rightVars, idBase, WVar((*it).fst), step, newBase, newCount, newValPlus, newValMinus); if (!created) { @@ -3754,10 +4047,8 @@ bool PPLDomain::_isArrayStore(const BasicBlock *bb, const WVar &storeAddr) const } else if (v.guid() > 0) { has_bound = true; } - if (has_store && has_bound) { - cout << "found cons: " << c << endl; + if (has_store && has_bound) return true; - } } } return false; @@ -3911,48 +4202,6 @@ void PPLDomain::enableSummary() { doNewConstraint(spInput == getVar(Ident(13, Ident::ID_REG))); */ } - -extern elm::String cfgname; - -void PPLDomain::showTab(const Ident &ident) { - Ident id_ssp(Ident::ID_START_SP, Ident::ID_SPECIAL); - int step; - WVar ssp = getVar(id_ssp, nullptr); - WVar store = getVar(ident, &step); - - WPoly temp = poly; - temp.filter_lambda([store, ssp](guid_t guid) { - return guid == store.guid() || guid == ssp.guid(); - }); - for (WPoly::ConsIterator it(temp); it; it++) { - const WCons &c = (*it); - bool has_store = false; - bool has_ssp = false; - bool has_other = false; - if (!c.is_equality()) continue; - for (WCons::TermIterator it2(c); it2; it2++) { - const WVar &v = (*it2); - if (v.guid() == ssp.guid()) { - has_ssp = true; - } else if (v.guid() == store.guid()) { - has_store = true; - } else { - has_other = true; - } - if (has_store && has_ssp && !has_other && c.coefficient(ssp) == -c.coefficient(store)) { - int cst = -c.inhomogeneous_term().get_si(); - int coef = -c.coefficient(store).get_si(); - cout << "ARRAY: type=local func=" << cfgname << " addr=START_SP-" << hex(cst/coef) << endl; - } - if (has_store && !has_ssp && !has_other) { - int cst = -c.inhomogeneous_term().get_si(); - int coef = c.coefficient(store).get_si(); - - cout << "ARRAY: type=global addr=" << hex(cst/coef) << endl; - } - } - } -} p::feature POLY_ANALYSIS_FEATURE("otawa::poly::POLY_ANALYSIS_FEATURE", new Maker<PolyAnalysis>()); Identifier<int> LOC_VAR_SIZE("otawa::poly::LOC_VAR_SIZE", 4); diff --git a/poly_PolyAnalysis.cpp b/poly_PolyAnalysis.cpp index c6d5a34..b9d84b1 100644 --- a/poly_PolyAnalysis.cpp +++ b/poly_PolyAnalysis.cpp @@ -18,11 +18,13 @@ #include "include/PPLDomain.h" #include "include/PPLManager.h" #include "include/PolyAnalysis.h" + +#include "include/LoopAnalyzer.h" + namespace otawa { namespace poly { using namespace otawa; using namespace util; -elm::String cfgname; p::declare PolyAnalysis::reg = p::init("otawa::poly::PolyAnalysis", Version(1, 0, 0)) .require(COLLECTED_CFG_FEATURE) @@ -44,6 +46,17 @@ void PolyAnalysis::configure(const PropList &props) { PolyAnalysis::state_t PolyAnalysis::processHeader(ai::CFGGraph &graph, MyHTable<int,PPLDomain> &lb, BasicBlock *header, PPLManager& man, ai::EdgeStore<PPLManager, ai::CFGGraph>& store, MyHTable<int, HeaderState> &headerState) { + + // manage loop context + if(CURRENT_LOOP(ROOT_CFG(header->cfg())) != header->id()){ + PREVIOUS_LOOP(header) = CURRENT_LOOP(ROOT_CFG(header->cfg())); + CURRENT_LOOP(ROOT_CFG(header->cfg())) = header->id(); + } + // reset function instances for each iteration to insert in the same place the conditionals + std::map<int,std::map<string,int>> resetMap = CURRENT_INSTANCES(ROOT_CFG(header->cfg())); + resetMap.erase(header->id()); + CURRENT_INSTANCES(ROOT_CFG(header->cfg())) = resetMap; + state_t entryState = man.bot(); state_t backState = man.bot(); @@ -92,14 +105,17 @@ DBG("Widening, entryState = " << entryState) bound_t bound = backState.getLoopBound(header->id()); backState.setBound(header->id(), bound); #endif +/* cout << "\n\n\nBackState pour calcul de bornes de boucles parametrique: \n" << backState << "\n\n\n"; */ - PDBG("ITERATION: bound=" << int(bound) << endl) + backState.computeParamBound(header); + + /*PDBG("ITERATION: bound=" << int(bound) << endl) if(bound == -1) DBG("ITERATION: bound=" << color::IRed << "UNREACHABLE") else if(bound == -2) DBG("ITERATION: bound=" << color::IRed << "UNBOUNDED") else - DBG("ITERATION: bound=" << color::IRed << int(bound)) + DBG("ITERATION: bound=" << color::IRed << int(bound))*/ #ifndef LINBOUND @@ -112,7 +128,7 @@ DBG("Widening, entryState = " << entryState) lb[header->id()] = PPLDomain(); #ifdef LINBOUND - const PPLDomain &oldBound = lb[header->id()]; + PPLDomain &oldBound = lb[header->id()]; lb[header->id()] = oldBound.onMerge(linearBound, false); #endif @@ -173,6 +189,25 @@ void PolyAnalysis::processBB(PPLManager *man, ai::CFGGraph &graph, MyHTable<int, */ state_t s = man->bot(); + if(LOOP_RESET(*ana)){ + Block* h = nullptr; + auto iter = (*ana)->ins(); + for(int i=0;i<(*ana)->countIns();i++){ + if(ROOT_CFG((*ana)->cfg()) != nullptr && (*iter)->source()->id() == CURRENT_LOOP(ROOT_CFG((*ana)->cfg()))){ + h = (*iter)->source(); + break; + } + iter++; + } + if(h != nullptr){ + CURRENT_LOOP(ROOT_CFG((*ana)->cfg())) = PREVIOUS_LOOP(h); + } + else{ + cout << "ERROR : h is null" << endl; + //exit(1); + } + } + DBG("Processing " << color::On_Blu << "BB " << ((BasicBlock*)*ana)->index() << (LOOP_HEADER(*ana) ? " (loop header)" : "") << color::RCol) /* Special processing for loop headers, to handle loop bounds and widening */ if (LOOP_HEADER(*ana)) { @@ -184,6 +219,12 @@ void PolyAnalysis::processBB(PPLManager *man, ai::CFGGraph &graph, MyHTable<int, s = ana.input(); } + /* doing this avoid bottom state */ + if(((BasicBlock*)*ana)->index() == 1){ + ArmArgumentsDetector detector; + detector.initFunctionParameters(&s, ((BasicBlock*)*ana)->cfg()->entry()); + } + if (s.isBottom()) { #ifdef POLY_DEBUG cout << "! Skip block because input state is Bottom" << endl; @@ -199,6 +240,39 @@ void PolyAnalysis::processBB(PPLManager *man, ai::CFGGraph &graph, MyHTable<int, if ((*ana)->isSynth()) { /* Handle call to another function */ CFG *subCFG = (*ana)->toSynth()->callee(); + + /* handle crash on null CFG */ + if (subCFG == nullptr) { + cout << "Error: calling a null CFG" << endl; + exit(1); + } + + ROOT_CFG(subCFG) = ROOT_CFG((*ana)->cfg()); + int oldLoop = CURRENT_LOOP(ROOT_CFG(subCFG)); //< store old loop id to restore it after return + //CURRENT_LOOP(ROOT_CFG(subCFG)) = -1; + string name = subCFG->name(); + + // create artificiel loop context for function calls + std::map<int,std::map<string,int>> currentInstancesAll = CURRENT_INSTANCES(ROOT_CFG(subCFG)); + std::map<string,int> currentInstances; + if(currentInstancesAll.find(oldLoop) == currentInstancesAll.end()){ + currentInstances = std::map<string,int>(); + } + else{ + currentInstances = currentInstancesAll.at(oldLoop); + currentInstancesAll.erase(oldLoop); + } + int instance = 1; // default value + if(currentInstances.find(name) != currentInstances.end()){ + instance = currentInstances.at(name) + 1; //< new value + currentInstances.erase(name); //< to replace the value + } + currentInstances.emplace(name,instance); + CURRENT_LOOP(ROOT_CFG(subCFG)) = - MAX_LOOP * oldLoop + instance; // unique negative value for each loop + currentInstancesAll.emplace(oldLoop,currentInstances); + CURRENT_INSTANCES(ROOT_CFG(subCFG)) = currentInstancesAll; + // end of artificial loop context + cout << "Call from " << (*ana)->toSynth()->caller()->name() << " to " << subCFG->name() << endl; bool compose_ok = false; @@ -273,6 +347,7 @@ void PolyAnalysis::processBB(PPLManager *man, ai::CFGGraph &graph, MyHTable<int, } else { processCFG(*subCFG, s, lb, false, false); cout << "Return from " << subCFG->name() << " to " << (*ana)->toSynth()->caller()->name() << endl; + CURRENT_LOOP(ROOT_CFG(subCFG)) = oldLoop; } } @@ -345,7 +420,7 @@ void PolyAnalysis::processBB(PPLManager *man, ai::CFGGraph &graph, MyHTable<int, /* Filtering: apply branch condition on edge state */ PDBG("BEFORE FILTERING: " << endl) PDBG(edgeState) - edgeState = edgeState.onBranch(e->isTaken()); + edgeState = edgeState.onBranch(e->isTaken(), *ana); if (edgeState.isBottom()) { continue; } @@ -369,6 +444,11 @@ void PolyAnalysis::processBB(PPLManager *man, ai::CFGGraph &graph, MyHTable<int, if (LOOP_EXIT_EDGE(e) != nullptr) { /* Exit edge: remove virtual loop counter, and apply loop bound constraint on state */ Block *bb = LOOP_EXIT_EDGE(e); + + // tells that we get out of a loop + if(LOOP_HEADER(e->source())){ + LOOP_RESET(e->sink()) = true; + } /* * FIXME: should be bound = s.getLoopBound(bb->id()) but we need to fix the widening to make it work @@ -380,7 +460,7 @@ void PolyAnalysis::processBB(PPLManager *man, ai::CFGGraph &graph, MyHTable<int, PDBG("Bound on loop exit: " << bound << endl) if (!linBound.isBottom()) { - edgeState = edgeState.onLoopExitLinear(bb->id(), linBound); + edgeState = edgeState.onLoopExitLinear(bb->id(), linBound, bb); if (edgeState.isBottom()) continue; } @@ -388,12 +468,14 @@ void PolyAnalysis::processBB(PPLManager *man, ai::CFGGraph &graph, MyHTable<int, #endif edgeState.doRemoveUselessAvatars(); edgeState.doRemoveAllAvatars(); +#ifndef LINBOUND if (bound >= 0) { - edgeState = edgeState.onLoopExit(bb->id(), bound); + edgeState = edgeState.onLoopExit(bb->id(), bound, bb); if (edgeState.isBottom()) { continue; } } +#endif } edgeState.doFinalizeUpdate(); ana.check(*e, edgeState); @@ -494,9 +576,6 @@ void PolyAnalysis::processCFG(CFG &cfg, state_t &s, MyHTable<int, PPLDomain> &lb PDBG("Init state: " << s << endl) cout << "Entering CFG: " << cfg.name() << endl; - auto curname = cfgname; - cfgname = cfg.name(); - WorkListDriver<PPLManager, ai::CFGGraph, ai::EdgeStore<PPLManager, ai::CFGGraph>, PseudoTopoOrder> ana(*man, graph, store, _orders[cfg.index()]); while (ana) { @@ -505,7 +584,6 @@ void PolyAnalysis::processCFG(CFG &cfg, state_t &s, MyHTable<int, PPLDomain> &lb } cout << "Exiting CFG: " << cfg.name() << endl; - cfgname = curname; Block *bb = graph.exit(); Block::EdgeIter edge(bb->ins()); @@ -546,6 +624,9 @@ void PolyAnalysis::processWorkSpace(WorkSpace *ws) { elm::log::Debug::setColorFlag(false); #endif + ConstraintExporter exporter; + exporter.resetFile(); + struct timeval now; gettimeofday(&now, nullptr); @@ -562,12 +643,17 @@ void PolyAnalysis::processWorkSpace(WorkSpace *ws) { CFG *entry = coll->get(0); + ROOT_CFG(entry) = entry; state_t dummy; ASSERT(dummy.getSummary() == nullptr); SUMMARIZE(ws) = false; MyHTable<int, PPLDomain> bounds; MyHTable<int, int> static_bounds; processCFG(*entry, dummy, bounds, true /* is entry */, false /* summarize */); + + // add non parametric loop bounds to the loop bounds + std::map<int,LoopBound> loop_bounds; + #ifdef LINBOUND cout << "PARAMETRIC LOOP BOUNDS: " << endl; for (MyHTable<int, PPLDomain>::PairIterator it(bounds); it; it++) { @@ -590,6 +676,10 @@ void PolyAnalysis::processWorkSpace(WorkSpace *ws) { for (CFGCollection::Iter iter2(coll); iter2; iter2++) { for (CFG::BlockIter iter((*iter2)->blocks()); iter; iter++) { Block *bb = (*iter); + + if(loop_bounds.size() == 0 && ROOT_CFG(bb->cfg()) != nullptr) + loop_bounds = LOOP_BOUNDS(ROOT_CFG(bb->cfg())->entry()); + if (LOOP_HEADER(bb)) { if (static_bounds.hasKey(bb->id())) { MAX_ITERATION(bb) = static_bounds[bb->id()]; @@ -600,6 +690,10 @@ void PolyAnalysis::processWorkSpace(WorkSpace *ws) { cout << "[" << (*iter2)->name() << "]" << "TOTAL_ITERATION(" << bb->id() << ") = " << TOTAL_ITERATION(bb) << endl; */ + if(MAX_ITERATION(bb) >= 0){ // if found without error, add non parametric loop bounds to the file + LoopBound lb(false, MAX_ITERATION(bb), ""); + loop_bounds.emplace(bb->id(), lb); + } } } delete _orders[(*iter2)->index()]; @@ -609,6 +703,10 @@ void PolyAnalysis::processWorkSpace(WorkSpace *ws) { for (CFGCollection::Iter iter2(coll); iter2; iter2++) { for (CFG::BlockIter iter((*iter2)->blocks()); iter; iter++) { Block *bb = (*iter); + + if(loop_bounds.size() == 0 && ROOT_CFG(bb->cfg()) != nullptr) + loop_bounds = LOOP_BOUNDS(ROOT_CFG(bb->cfg())->entry()); + if (LOOP_HEADER(bb)) { cout << "[" << (*iter2)->name() << "]" << "MAX_ITERATION(" << bb->id() << ") = " << MAX_ITERATION(bb) << endl; @@ -616,11 +714,34 @@ void PolyAnalysis::processWorkSpace(WorkSpace *ws) { cout << "[" << (*iter2)->name() << "]" << "TOTAL_ITERATION(" << bb->id() << ") = " << TOTAL_ITERATION(bb) << endl; */ + if(MAX_ITERATION(bb) >= 0){ // if found without error, add non parametric loop bounds to the file + LoopBound lb(false, MAX_ITERATION(bb), ""); + loop_bounds.emplace(bb->id(), lb); + } } } delete _orders[(*iter2)->index()]; } #endif + // export constraints + bool exported = false; + for (CFGCollection::Iter iter2(coll); iter2; iter2++) { + for (CFG::BlockIter iter((*iter2)->blocks()); iter; iter++) { + Block *bb = (*iter); + ConstraintExporter exp; + if(ROOT_CFG(bb->cfg()) == nullptr) + continue; + OrderedElements order = EXPORT_ORDER(ROOT_CFG(bb->cfg())); //< order also contains non loop conditionals + std::map<int,std::map<bool,Conditional>> loopConditions = LOOP_CONDITIONALS(ROOT_CFG(bb->cfg())); + exp.exportOrderedConstraints(order, loopConditions); + LoopBoundsExporter lbe; + lbe.exportToFile(loop_bounds); + exported = true; + break; + } + if(exported) + break; + } cout << "Max variable count: " << max_vars << endl; cout << "Max constraint count: " << max_cons << endl; struct timeval after; diff --git a/poly_PolyWrap.cpp b/poly_PolyWrap.cpp index e5242a1..d79709e 100644 --- a/poly_PolyWrap.cpp +++ b/poly_PolyWrap.cpp @@ -7,6 +7,11 @@ namespace poly { using namespace elm::io; using namespace PPL; +void parse(const coef_t& coef, char* buf){ + gmp_snprintf(buf, sizeof(buf), "%Zd", &raw_value(coef)); + buf[sizeof(buf) - 1] = 0; +} + output_t& operator<< (output_t& stream, const coef_t& coef) { char buf[128]; gmp_snprintf(buf, sizeof(buf), "%Zd", &raw_value(coef)); -- GitLab