1
0
This repository has been archived on 2025-03-06. You can view files and clone it, but cannot push or open issues or pull requests.

216 lines
6.5 KiB
C++

#include <minizinc/codegen.hh>
#include <../lib/codegen/analysis.hpp>
namespace MiniZinc {
/// Visit identifier
void ModeAnalysis::vId(Id& x, Occurrence o, CG::Mode m) {
// Use sites follow
VarDecl* decl(x.decl());
if (o == Use && decl) update(decl, o, m);
}
/// Visit array literal
void ModeAnalysis::vArrayLit(ArrayLit& a, Occurrence o, CG::Mode m) {
// If the array contents are Boolean, this doesn't count as an occurrence;
// we care about the context of the access.
// But for anything else, the partiality emerges at the occurrence site.
int sz(a.size());
for (int ii = 0; ii < sz; ++ii) {
Expression* elt(a[ii]);
if (o == Use || elt->type().bt() != Type::BT_BOOL) {
update(elt, o, m);
}
}
}
/// Visit array access
void ModeAnalysis::vArrayAccess(ArrayAccess& a, Occurrence o, CG::Mode m) {
ASTExprVec<Expression> idx(a.idx());
for (int ii = 0; ii < idx.size(); ++ii) update(idx[ii], o, m);
if (a.type().bt() == Type::BT_BOOL)
update(a.v(), o, +m);
else
update(a.v(), o, m);
}
/// Visit array comprehension
void ModeAnalysis::vComprehension(Comprehension& comp, Occurrence o, CG::Mode m) {
// Comprehension behaves like an array access:
// The current mode propagates to the generators, but only to non-Boolean bodies.
if (o == Def) {
int g_n(comp.n_generators());
for (int gg = 0; gg < g_n; ++gg) {
int d_n(comp.n_decls(gg));
for (int d = 0; d < d_n; ++d) update(comp.decl(gg, d), o, m);
update(comp.in(gg), o, m);
if (comp.where(gg)) {
update(comp.where(gg), Use, BytecodeProc::FUN);
}
}
}
if (o == Use || comp.e()->type().bt() != Type::BT_BOOL) {
int g_n(comp.n_generators());
for (int gg = 0; gg < comp.n_generators(); ++gg) {
if (comp.where(gg)) {
update(comp.where(gg), Use, BytecodeProc::FUN);
}
}
update(comp.e(), o, m);
}
}
/// Visit if-then-else
void ModeAnalysis::vITE(ITE& ite, Occurrence o, CG::Mode m) {
int sz(ite.size());
for (int ii = 0; ii < sz; ++ii) {
// We need the condition to compute the value.
update(ite.e_if(ii), Use, BytecodeProc::FUN);
update(ite.e_then(ii), o, m);
}
update(ite.e_else(), o, m);
}
/// Visit binary operator
void ModeAnalysis::vBinOp(BinOp& b, Occurrence o, CG::Mode m) {
switch (b.op()) {
case BOT_IMPL: {
CG::Mode m_c = m.is_neg() ? m : +m;
update(b.lhs(), o, -m_c);
update(b.rhs(), o, m_c);
break;
}
case BOT_RIMPL: {
CG::Mode m_c = m.is_neg() ? m : +m;
update(b.lhs(), o, m_c);
update(b.rhs(), o, -m_c);
break;
}
case BOT_OR: {
CG::Mode m_c = m.is_neg() ? m : +m;
update(b.lhs(), o, m_c);
update(b.rhs(), o, m_c);
break;
}
case BOT_AND: {
CG::Mode m_c = m.is_neg() ? +m : m;
update(b.lhs(), o, m_c);
update(b.rhs(), o, m_c);
break;
}
case BOT_EQ:
case BOT_NQ: {
CG::Mode m_l = b.lhs()->type().bt() == Type::BT_BOOL ? CG::Mode(BytecodeProc::FUN) : +m;
CG::Mode m_r = b.rhs()->type().bt() == Type::BT_BOOL ? CG::Mode(BytecodeProc::FUN) : +m;
update(b.lhs(), o, m_l);
update(b.rhs(), o, m_r);
}
case BOT_EQUIV:
case BOT_XOR:
update(b.lhs(), o, BytecodeProc::FUN);
update(b.rhs(), o, BytecodeProc::FUN);
break;
default:
update(b.lhs(), o, m);
update(b.rhs(), o, m);
}
}
/// Visit unary operator
void ModeAnalysis::vUnOp(UnOp& u, Occurrence o, CG::Mode m) {
switch (u.op()) {
case UOT_NOT:
update(u.e(), o, -m);
break;
default:
update(u.e(), o, m);
}
}
/// Visit call
// TODO: Add special cases for builtin calls, and interprocedural analysis
// so we can get more precise mode analysis.
void ModeAnalysis::vCall(Call& call, Occurrence o, CG::Mode m) {
int sz(call.n_args());
if (call.id() == constants().ids.forall) {
assert(sz == 1);
CG::Mode m_r(m.is_neg() ? +m : m);
update(call.arg(0), o, m_r);
} else if (call.id() == constants().ids.exists) {
assert(sz == 1);
CG::Mode m_r(m.is_neg() ? m : +m);
update(call.arg(0), o, m_r);
} else if (call.id() == constants().ids.clause) {
assert(sz == 2);
CG::Mode m_r(m.is_neg() ? m : +m);
update(call.arg(0), o, m_r);
update(call.arg(1), o, -m_r);
} else if (call.id() == constants().ids.assert) {
update(call.arg(0), Use, BytecodeProc::ROOT);
if (sz == 3) update(call.arg(2), o, m);
} else if (call.id() == "array1d") {
// No-op
update(call.arg(sz - 1), o, m);
} else if (call.id() == "index_set" || call.id() == "length") {
if (o == Def) update(call.arg(0), Def, m);
} else if (call.id() == "symmetry_breaking_constraint" || call.id() == "redundant_constraint") {
// No-op
update(call.arg(0), o, m);
} else {
// Propagate to the other functions.
for (int ii = 0; ii < sz; ++ii) {
Expression* arg(call.arg(ii));
// If arg is non-Boolean, this the partiality gets embedded here.
// But if arg is Boolean, we have to assume the callee can do
// anything with it, so the occurrence is considered FUN.
if (arg->type().bt() == Type::BT_BOOL) {
update(arg, Use, BytecodeProc::FUN);
} else {
update(arg, o, m);
}
}
}
}
/// Visit let
void ModeAnalysis::vLet(Let& let, Occurrence o, CG::Mode m) {
ASTExprVec<Expression> bindings(let.let());
if (o == Def || let.type().bt() == Type::BT_BOOL) {
// If the let has Boolean type, the _use_ of the let defines
// the def-mode of the bound variables.
for (Expression* e : bindings) {
// Check whether this is a decl with a def.
if (auto vd = e->dyn_cast<VarDecl>()) {
if (vd->type().bt() != Type::BT_BOOL) update(vd, Def, m);
/*
if(Expression* v_e = vd->e()) {
if(!v_e->type().isbool()) {
update(v_e, Def, m);
}
}
*/
} else {
// Must be a constraint, so it's a use.
assert(e->type().isbool());
update(e, Use, m);
}
}
update(let.in(), o, m);
} else {
// Non-Boolean use; the partiality is in an enclosing
// context.
update(let.in(), o, m);
}
}
/// Visit variable declaration
void ModeAnalysis::vVarDecl(VarDecl& decl, Occurrence o, CG::Mode m) {
if (Id* x = decl.id()) update(x, o, m);
if (Expression* d = decl.ti()->domain()) {
if (o == Def) {
update(d, Def, m);
update(d, Use, m);
}
}
if (Expression* e = decl.e()) update(e, o, m);
}
void ModeAnalysis::vFunctionI(FunctionI& fun, Occurrence o, CG::Mode m) {
// DO STUFF
}
}; // namespace MiniZinc