/* -*- mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*- */ /* * Main authors: * Graeme Gange */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #ifndef __MINIZINC_CODEGEN_ANALYSIS_HPP__ #define __MINIZINC_CODEGEN_ANALYSIS_HPP__ #include #include #include #include // From a given top-level expression and // mode, what is the weakest mode covering // all occurrences of each sub-expression? namespace MiniZinc { static void annotate_total(FunctionI* func) { class AnnotateTotal : public EVisitor { public: void vLet(Let& let) { let.addAnnotation(constants().ann.promise_total); ASTExprVec bindings(let.let()); for (auto expr : bindings) { if (expr->eid() != Expression::E_VARDECL) { // Must be a constraint, so it's a use. assert(expr->type().isbool()); expr->addAnnotation(constants().ann.promise_total); } } } } _at; if (func->ann().contains(constants().ann.promise_total)) { topDown(_at, func->e()); } } class ModeAnalysis { enum Occurrence { Def = 0, Use = 1 }; public: // There are two relevant modes for an expression: the mode // where the value is introduced, and the mode where the value // is used. // This distinction mostly matters for let-bindings or definitions // which construct results containing Booleans (i.e. array [int] of var bool). // At the end, Boolean values are built according to their use-context, and // non-Boolean values by their def-context. // We use user_flag0 and user_flag1 to track whether // the definition and use-modes are queued. inline void enqueue(Expression* e, Occurrence o) { if (e->isUnboxedVal()) return; if (o == Def) { if (!e->user_flag0()) { e->user_flag0(true); worklist.push(reinterpret_cast(e)); } } else { if (!e->user_flag1()) { e->user_flag1(true); worklist.push(reinterpret_cast(e) | 1); } } } void update(Expression* e, Occurrence o, CG::Mode m) { if (e->type().isbool()) { if (o == Def) { // We only keep Use-modes for Boolean things. return; } if (e->ann().contains(constants().ann.promise_total)) { m = BytecodeProc::ROOT; } } ExprMap::t& t(o == Def ? def_map : use_map); auto it(t.find(e)); if (it == t.end()) { // First time we've seen e. t.insert(std::make_pair(e, m)); } else { if (it->second.is_submode(m)) return; it->second = it->second.join(m); } enqueue(e, o); } void update(FunctionI* fun, Occurrence o, CG::Mode m) { // We only keep Use-modes for Boolean things. /* if(o == Def) { if(e->type().isbool()) return; if(e->ann().contains(constants().ann.promise_total)) m = BytecodeProc::ROOT; } ExprMap::t& t(o == Def ? def_map : use_map); auto it(t.find(e)); if(it == t.end()) { // First time we've seen e. t.insert(std::make_pair(e, m)); } else { if(it->second.is_submode(m)) return; it->second = it->second.join(m); } enqueue(e, o); */ } // Better to make this fifo, but... whatever. void process(void) { while (!worklist.empty()) { uintptr_t tag(worklist.front()); worklist.pop(); Expression* e(reinterpret_cast(tag & ~((uintptr_t)1))); Occurrence o(static_cast(tag & 1)); // Clear the queued-ness flags if (o == Def) e->user_flag0(false); else e->user_flag1(false); // Get the updated mode for e. ExprMap::t& t(o == Def ? def_map : use_map); CG::Mode m(t.at(e)); switch (e->eid()) { case Expression::E_INTLIT: case Expression::E_FLOATLIT: case Expression::E_SETLIT: case Expression::E_BOOLLIT: case Expression::E_STRINGLIT: case Expression::E_ANON: break; case Expression::E_ID: vId(*e->template cast(), o, m); break; case Expression::E_ARRAYLIT: vArrayLit(*e->template cast(), o, m); break; case Expression::E_ARRAYACCESS: vArrayAccess(*e->template cast(), o, m); break; case Expression::E_COMP: vComprehension(*e->template cast(), o, m); break; case Expression::E_ITE: vITE(*e->template cast(), o, m); break; case Expression::E_BINOP: vBinOp(*e->template cast(), o, m); break; case Expression::E_UNOP: vUnOp(*e->template cast(), o, m); break; case Expression::E_CALL: vCall(*e->template cast(), o, m); break; case Expression::E_VARDECL: vVarDecl(*e->template cast(), o, m); break; case Expression::E_LET: vLet(*e->template cast(), o, m); break; /* case Expression::E_TI: _t.vTypeInst(*c._e->template cast()); break; case Expression::E_TIID: _t.vTIId(*c._e->template cast()); break; */ default: throw InternalError("Mode analyser encounted unexpected expression type."); } } } ModeAnalysis(void) {} void def(Expression* e, CG::Mode m) { update(e, Def, m); } void use(Expression* e, CG::Mode m) { if (e->type().isbool()) update(e, Use, m); else update(e, Def, m); } ExprMap::t extract(void) { // Compute the fixpoint. process(); // Now fuse the two maps. // Start from the def_map, and add any Boolean uses. ExprMap::t fused(def_map); for (auto p : use_map) { /* if(p.first->type().isbool()) fused.insert(p); */ // If we haven't stored a def-mode, we should // apply the use-mode. fused.insert(p); } return fused; } /* static ExprMap run(Expression* e, CG::Mode m) { ModeAnalysis analyzer; analyzer.update(e, Use, m); analyzer.process(); // Now we fuse the two maps. } */ std::queue worklist; ExprMap::t def_map; ExprMap::t use_map; /// Visit identifier void vId(Id&, Occurrence, CG::Mode); /// Visit array literal void vArrayLit(ArrayLit&, Occurrence, CG::Mode); /// Visit array access void vArrayAccess(ArrayAccess&, Occurrence, CG::Mode); /// Visit array comprehension void vComprehension(Comprehension&, Occurrence, CG::Mode); /// Visit if-then-else void vITE(ITE&, Occurrence, CG::Mode); /// Visit binary operator void vBinOp(BinOp&, Occurrence, CG::Mode); /// Visit unary operator void vUnOp(UnOp&, Occurrence, CG::Mode); /// Visit call void vCall(Call&, Occurrence, CG::Mode); /// Visit let void vLet(Let&, Occurrence, CG::Mode); /// Visit variable declaration void vVarDecl(VarDecl&, Occurrence, CG::Mode); // Process a function body. void vFunctionI(FunctionI&, Occurrence, CG::Mode); }; }; // namespace MiniZinc #endif