git-subtree-dir: software/mza git-subtree-split: f970a59b177c13ca3dd8aaef8cc6681d83b7e813
4767 lines
168 KiB
C++
4767 lines
168 KiB
C++
#include <minizinc/astexception.hh>
|
|
#include <minizinc/astiterator.hh>
|
|
#include <minizinc/codegen.hh>
|
|
#include <minizinc/copy.hh>
|
|
#include <minizinc/eval_par.hh>
|
|
#include <minizinc/flatten.hh>
|
|
#include <minizinc/hash.hh>
|
|
#include <minizinc/interpreter.hh>
|
|
#include <minizinc/optimize.hh>
|
|
#include <minizinc/output.hh>
|
|
#include <minizinc/prettyprinter.hh>
|
|
|
|
#include <../lib/codegen/analysis.hpp>
|
|
#include <../lib/codegen/codegen_internal.hpp>
|
|
#include <functional>
|
|
#include <set>
|
|
|
|
namespace MiniZinc {
|
|
|
|
// Known limitations:
|
|
// - Comprehensions and generator expressions are assumed to be total, so are evaluated in root
|
|
// context. Expression evaluation currently runs in two modes:
|
|
// - eval, which places the result onto the value stack on the enclosing context, or
|
|
// - locate, which places the result into a register, and returns the register.
|
|
// this is done to avoid a unnecessary push/pop sequences, when a result is already bound to
|
|
// a register, and is needed for e.g. a call.
|
|
|
|
#define ENABLE_PLUS 1
|
|
// MZNC_COLLECT_LEAVES should generate fewer aggregation contexts for models with par-resolved
|
|
// partiality (i.e. lots of array accesses)
|
|
#define MZNC_COLLECT_LEAVES
|
|
|
|
using Mode = CG::Mode;
|
|
|
|
struct builtin_t {
|
|
std::function<CG_Cond::T(Call*, Mode, CodeGen&, CG_Builder&)> boolean;
|
|
std::function<CG::Binding(Call*, Mode, CodeGen&, CG_Builder&)> general;
|
|
};
|
|
|
|
typedef std::unordered_map<std::string, builtin_t> builtin_table;
|
|
|
|
const char* instr_names[] = {
|
|
"ADDI",
|
|
"SUBI",
|
|
"MULI",
|
|
"DIVI",
|
|
"MODI",
|
|
"INCI",
|
|
"DECI",
|
|
|
|
"IMMI",
|
|
"CLEAR",
|
|
"LOAD_GLOBAL",
|
|
"STORE_GLOBAL",
|
|
"MOV",
|
|
|
|
"JMP",
|
|
"JMPIF",
|
|
"JMPIFNOT",
|
|
|
|
"EQI",
|
|
"LTI",
|
|
"LEI",
|
|
|
|
"AND",
|
|
"OR",
|
|
"NOT",
|
|
"XOR",
|
|
|
|
"ISPAR",
|
|
"ISEMPTY",
|
|
"LENGTH",
|
|
"GET_VEC",
|
|
"GET_ARRAY",
|
|
|
|
"LB",
|
|
"UB",
|
|
"DOM",
|
|
|
|
"MAKE_SET",
|
|
"INTERSECTION",
|
|
"UNION",
|
|
"DIFF",
|
|
|
|
"INTERSECT_DOMAIN",
|
|
|
|
"OPEN_AGGREGATION",
|
|
"CLOSE_AGGREGATION",
|
|
"SIMPLIFY_LIN",
|
|
|
|
"PUSH",
|
|
"POP",
|
|
"POST",
|
|
|
|
"RET",
|
|
"CALL",
|
|
"BUILTIN",
|
|
"TCALL",
|
|
|
|
"ITER_ARRAY",
|
|
"ITER_VEC",
|
|
"ITER_RANGE",
|
|
"ITER_NEXT",
|
|
"ITER_BREAK",
|
|
|
|
"TRACE",
|
|
"ABORT",
|
|
};
|
|
|
|
const char* mode_names[] = {
|
|
"ROOT", "ROOT_NEG", "FUN", "FUN_NEG", "IMP", "IMP_NEG", "MAX_MODE",
|
|
};
|
|
|
|
const char* agg_names[] = {"AND", "OR", "VEC", "OTHER"};
|
|
const char* instr_name(BytecodeStream::Instr i) { return instr_names[i]; }
|
|
|
|
const char* agg_name(AggregationCtx::Symbol s) { return agg_names[s]; }
|
|
const char* mode_name(BytecodeProc::Mode m) { return mode_names[m]; }
|
|
|
|
inline void TODO(void) { throw InternalError("Not yet implemented!"); }
|
|
|
|
void CodeGen::register_builtins(void) {
|
|
// Solver Built-ins
|
|
register_builtin("mk_intvar", 1);
|
|
|
|
// Constants
|
|
register_builtin("absent", 1);
|
|
register_builtin("infinity", 1);
|
|
register_builtin("boolean_domain", 0);
|
|
register_builtin("infinite_domain", 0);
|
|
|
|
// Interpreter Built-ins
|
|
register_builtin("uniform", 2);
|
|
register_builtin("sol", 1);
|
|
register_builtin("sort_by", 2);
|
|
register_builtin("floor", 1);
|
|
register_builtin("ceil", 1);
|
|
register_builtin("slice_Xd", 3);
|
|
register_builtin("array_Xd", 2);
|
|
register_builtin("index_set", 2);
|
|
register_builtin("internal_sort", 1);
|
|
}
|
|
|
|
void OPEN_AGG(CodeGen& cg, CG_Builder& frag, AggregationCtx::Symbol ctx) {
|
|
cg.env_push();
|
|
cg.reg_trail.push_back(cg.current_reg_count);
|
|
|
|
PUSH_INSTR(frag, BytecodeStream::OPEN_AGGREGATION, ctx);
|
|
}
|
|
void CLOSE_AGG(CodeGen& cg, CG_Builder& frag) {
|
|
assert(cg.reg_trail.size() > 0);
|
|
int old_reg_count = cg.current_reg_count;
|
|
cg.current_reg_count = cg.reg_trail.back();
|
|
cg.reg_trail.pop_back();
|
|
if (cg.current_reg_count < old_reg_count) {
|
|
PUSH_INSTR(frag, BytecodeStream::CLEAR, CG::r(cg.current_reg_count), CG::r(old_reg_count - 1));
|
|
}
|
|
PUSH_INSTR(frag, BytecodeStream::CLOSE_AGGREGATION);
|
|
cg.env_pop();
|
|
}
|
|
|
|
void OPEN_AND(CodeGen& cg, CG_Builder& frag) { OPEN_AGG(cg, frag, AggregationCtx::VCTX_AND); }
|
|
void OPEN_OR(CodeGen& cg, CG_Builder& frag) { OPEN_AGG(cg, frag, AggregationCtx::VCTX_OR); }
|
|
void OPEN_OTHER(CodeGen& cg, CG_Builder& frag) { OPEN_AGG(cg, frag, AggregationCtx::VCTX_OTHER); }
|
|
void OPEN_VEC(CodeGen& cg, CG_Builder& frag) { OPEN_AGG(cg, frag, AggregationCtx::VCTX_VEC); }
|
|
|
|
CG_ProcID CodeGen::register_builtin(std::string s, unsigned int arity) {
|
|
auto it(_proc_map.find(s));
|
|
if (it != _proc_map.end()) {
|
|
// throw InternalError(std::string("Builtin already registered: ") + s);
|
|
std::cerr << "WARNING: Builtin " << s << " already registered." << std::endl;
|
|
return it->second;
|
|
}
|
|
|
|
CG_ProcID id(CG_ProcID::builtin(_builtins.size()));
|
|
_builtins.push_back(std::make_pair(s, arity));
|
|
_proc_map.insert(std::make_pair(s, id));
|
|
return id;
|
|
}
|
|
|
|
CG_ProcID CodeGen::find_builtin(std::string s) {
|
|
auto it(_proc_map.find(s));
|
|
assert(it != _proc_map.end());
|
|
return (*it).second;
|
|
}
|
|
|
|
int bind_cst(int x, CodeGen& cg, CG_Builder& frag);
|
|
|
|
void bind_binop_var(CodeGen& cg, CG_Builder& frag, Mode ctx, BinOpType op, int r_lhs, int r_rhs) {
|
|
GCLock lock;
|
|
switch (op) {
|
|
// Actual builtins
|
|
case BOT_PLUS: {
|
|
auto fun =
|
|
find_call_fun(cg, {"op_plus"}, Type::varint(), {Type::varint(), Type::varint()}, ctx);
|
|
assert(BytecodeProc::is_neg(ctx) == BytecodeProc::is_neg(std::get<1>(fun)));
|
|
PUSH_INSTR(frag, BytecodeStream::CALL, std::get<1>(fun), std::get<0>(fun),
|
|
CG::i(!std::get<2>(fun)), CG::r(r_lhs), CG::r(r_rhs));
|
|
return;
|
|
}
|
|
case BOT_MINUS: {
|
|
auto fun =
|
|
find_call_fun(cg, {"op_minus"}, Type::varint(), {Type::varint(), Type::varint()}, ctx);
|
|
assert(BytecodeProc::is_neg(ctx) == BytecodeProc::is_neg(std::get<1>(fun)));
|
|
PUSH_INSTR(frag, BytecodeStream::CALL, std::get<1>(fun), std::get<0>(fun),
|
|
CG::i(!std::get<2>(fun)), CG::r(r_lhs), CG::r(r_rhs));
|
|
return;
|
|
}
|
|
case BOT_MULT: {
|
|
auto fun =
|
|
find_call_fun(cg, {"op_times"}, Type::varint(), {Type::varint(), Type::varint()}, ctx);
|
|
assert(BytecodeProc::is_neg(ctx) == BytecodeProc::is_neg(std::get<1>(fun)));
|
|
PUSH_INSTR(frag, BytecodeStream::CALL, std::get<1>(fun), std::get<0>(fun),
|
|
CG::i(!std::get<2>(fun)), CG::r(r_lhs), CG::r(r_rhs));
|
|
return;
|
|
}
|
|
case BOT_IDIV: {
|
|
auto fun = find_call_fun(cg, {"op_int_division"}, Type::varint(),
|
|
{Type::varint(), Type::varint()}, ctx);
|
|
assert(BytecodeProc::is_neg(ctx) == BytecodeProc::is_neg(std::get<1>(fun)));
|
|
PUSH_INSTR(frag, BytecodeStream::CALL, std::get<1>(fun), std::get<0>(fun),
|
|
CG::i(!std::get<2>(fun)), CG::r(r_lhs), CG::r(r_rhs));
|
|
return;
|
|
}
|
|
case BOT_DIV: {
|
|
auto fun = find_call_fun(cg, {"op_float_division"}, Type::varint(),
|
|
{Type::varint(), Type::varint()}, ctx);
|
|
assert(BytecodeProc::is_neg(ctx) == BytecodeProc::is_neg(std::get<1>(fun)));
|
|
PUSH_INSTR(frag, BytecodeStream::CALL, std::get<1>(fun), std::get<0>(fun),
|
|
CG::i(!std::get<2>(fun)), CG::r(r_lhs), CG::r(r_rhs));
|
|
return;
|
|
}
|
|
case BOT_MOD: {
|
|
auto fun =
|
|
find_call_fun(cg, {"op_modulus"}, Type::varint(), {Type::varint(), Type::varint()}, ctx);
|
|
assert(BytecodeProc::is_neg(ctx) == BytecodeProc::is_neg(std::get<1>(fun)));
|
|
PUSH_INSTR(frag, BytecodeStream::CALL, std::get<1>(fun), std::get<0>(fun),
|
|
CG::i(!std::get<2>(fun)), CG::r(r_lhs), CG::r(r_rhs));
|
|
return;
|
|
}
|
|
case BOT_DOTDOT: {
|
|
// The values in r_lhs and r_rhs had better be IMMIs.
|
|
OPEN_VEC(cg, frag);
|
|
PUSH_INSTR(frag, BytecodeStream::PUSH, CG::r(r_lhs));
|
|
PUSH_INSTR(frag, BytecodeStream::PUSH, CG::r(r_rhs));
|
|
CLOSE_AGG(cg, frag);
|
|
return;
|
|
}
|
|
case BOT_PLUSPLUS: {
|
|
OPEN_VEC(cg, frag);
|
|
Foreach iter_lhs(cg, r_lhs);
|
|
iter_lhs.emit_pre(frag);
|
|
PUSH_INSTR(frag, BytecodeStream::PUSH, CG::r(iter_lhs.val()));
|
|
iter_lhs.emit_post(frag);
|
|
|
|
Foreach iter_rhs(cg, r_rhs);
|
|
iter_rhs.emit_pre(frag);
|
|
PUSH_INSTR(frag, BytecodeStream::PUSH, CG::r(iter_rhs.val()));
|
|
iter_rhs.emit_post(frag);
|
|
CLOSE_AGG(cg, frag);
|
|
return;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
throw InternalError("Unexpected fall-through in bind_binop_var.");
|
|
}
|
|
|
|
int bind_binop_par_int(CodeGen& cg, CG_Builder& frag, BinOpType op, int r_lhs, int r_rhs) {
|
|
int r;
|
|
switch (op) {
|
|
// Actual builtins
|
|
case BOT_EQ:
|
|
case BOT_EQUIV:
|
|
r = GET_REG(cg);
|
|
PUSH_INSTR(frag, BytecodeStream::EQI, CG::r(r_lhs), CG::r(r_rhs), CG::r(r));
|
|
return r;
|
|
case BOT_NQ:
|
|
r = GET_REG(cg);
|
|
PUSH_INSTR(frag, BytecodeStream::EQI, CG::r(r_lhs), CG::r(r_rhs), CG::r(r));
|
|
PUSH_INSTR(frag, BytecodeStream::NOT, CG::r(r), CG::r(r));
|
|
return r;
|
|
case BOT_LE:
|
|
r = GET_REG(cg);
|
|
PUSH_INSTR(frag, BytecodeStream::LTI, CG::r(r_lhs), CG::r(r_rhs), CG::r(r));
|
|
return r;
|
|
case BOT_LQ:
|
|
r = GET_REG(cg);
|
|
PUSH_INSTR(frag, BytecodeStream::LEI, CG::r(r_lhs), CG::r(r_rhs), CG::r(r));
|
|
return r;
|
|
case BOT_GR:
|
|
r = GET_REG(cg);
|
|
PUSH_INSTR(frag, BytecodeStream::LTI, CG::r(r_rhs), CG::r(r_lhs), CG::r(r));
|
|
return r;
|
|
case BOT_GQ:
|
|
r = GET_REG(cg);
|
|
PUSH_INSTR(frag, BytecodeStream::LEI, CG::r(r_rhs), CG::r(r_lhs), CG::r(r));
|
|
return r;
|
|
case BOT_XOR:
|
|
r = GET_REG(cg);
|
|
PUSH_INSTR(frag, BytecodeStream::XOR, CG::r(r_rhs), CG::r(r_lhs), CG::r(r));
|
|
return r;
|
|
case BOT_AND:
|
|
r = GET_REG(cg);
|
|
PUSH_INSTR(frag, BytecodeStream::AND, CG::r(r_lhs), CG::r(r_rhs), CG::r(r));
|
|
return r;
|
|
case BOT_OR:
|
|
r = GET_REG(cg);
|
|
PUSH_INSTR(frag, BytecodeStream::OR, CG::r(r_lhs), CG::r(r_rhs), CG::r(r));
|
|
return r;
|
|
case BOT_IMPL:
|
|
r = GET_REG(cg);
|
|
PUSH_INSTR(frag, BytecodeStream::NOT, CG::r(r_lhs), CG::r(r));
|
|
PUSH_INSTR(frag, BytecodeStream::OR, CG::r(r), CG::r(r_rhs), CG::r(r));
|
|
return r;
|
|
case BOT_RIMPL:
|
|
r = GET_REG(cg);
|
|
PUSH_INSTR(frag, BytecodeStream::NOT, CG::r(r_rhs), CG::r(r));
|
|
PUSH_INSTR(frag, BytecodeStream::XOR, CG::r(r_lhs), CG::r(r), CG::r(r));
|
|
return r;
|
|
case BOT_PLUS:
|
|
r = GET_REG(cg);
|
|
PUSH_INSTR(frag, BytecodeStream::ADDI, CG::r(r_lhs), CG::r(r_rhs), CG::r(r));
|
|
return r;
|
|
case BOT_MINUS:
|
|
r = GET_REG(cg);
|
|
PUSH_INSTR(frag, BytecodeStream::SUBI, CG::r(r_lhs), CG::r(r_rhs), CG::r(r));
|
|
return r;
|
|
case BOT_MULT:
|
|
r = GET_REG(cg);
|
|
PUSH_INSTR(frag, BytecodeStream::MULI, CG::r(r_lhs), CG::r(r_rhs), CG::r(r));
|
|
return r;
|
|
case BOT_IDIV:
|
|
r = GET_REG(cg);
|
|
PUSH_INSTR(frag, BytecodeStream::DIVI, CG::r(r_lhs), CG::r(r_rhs), CG::r(r));
|
|
return r;
|
|
case BOT_MOD:
|
|
r = GET_REG(cg);
|
|
PUSH_INSTR(frag, BytecodeStream::MODI, CG::r(r_lhs), CG::r(r_rhs), CG::r(r));
|
|
return r;
|
|
case BOT_DOTDOT:
|
|
// The values in r_lhs and r_rhs had better be IMMIs.
|
|
OPEN_VEC(cg, frag);
|
|
PUSH_INSTR(frag, BytecodeStream::PUSH, CG::r(r_lhs));
|
|
PUSH_INSTR(frag, BytecodeStream::PUSH, CG::r(r_rhs));
|
|
CLOSE_AGG(cg, frag);
|
|
r = GET_REG(cg);
|
|
PUSH_INSTR(frag, BytecodeStream::POP, CG::r(r));
|
|
return r;
|
|
case BOT_IN: {
|
|
int l_head(GET_LABEL(cg));
|
|
int l_stop(GET_LABEL(cg));
|
|
int l_exit(GET_LABEL(cg));
|
|
r = GET_REG(cg);
|
|
PUSH_INSTR(frag, BytecodeStream::ITER_VEC, CG::r(r_rhs), CG::l(l_exit));
|
|
PUSH_LABEL(frag, l_head);
|
|
PUSH_INSTR(frag, BytecodeStream::ITER_NEXT, CG::r(r));
|
|
// Start of interval.
|
|
PUSH_INSTR(frag, BytecodeStream::LEI, CG::r(r), CG::r(r_lhs), CG::r(r));
|
|
PUSH_INSTR(frag, BytecodeStream::JMPIFNOT, CG::r(r), CG::l(l_stop));
|
|
// End of interval
|
|
PUSH_INSTR(frag, BytecodeStream::ITER_NEXT, CG::r(r));
|
|
PUSH_INSTR(frag, BytecodeStream::LEI, CG::r(r_lhs), CG::r(r), CG::r(r));
|
|
PUSH_INSTR(frag, BytecodeStream::JMPIF, CG::r(r), CG::l(l_stop));
|
|
PUSH_INSTR(frag, BytecodeStream::JMP, CG::l(l_head));
|
|
PUSH_LABEL(frag, l_stop);
|
|
PUSH_INSTR(frag, BytecodeStream::ITER_BREAK, CG::i(1));
|
|
PUSH_LABEL(frag, l_exit);
|
|
return r;
|
|
}
|
|
case BOT_PLUSPLUS: {
|
|
r = GET_REG(cg);
|
|
OPEN_VEC(cg, frag);
|
|
Foreach iter_lhs(cg, r_lhs);
|
|
iter_lhs.emit_pre(frag);
|
|
PUSH_INSTR(frag, BytecodeStream::PUSH, CG::r(iter_lhs.val()));
|
|
iter_lhs.emit_post(frag);
|
|
|
|
Foreach iter_rhs(cg, r_rhs);
|
|
iter_rhs.emit_pre(frag);
|
|
PUSH_INSTR(frag, BytecodeStream::PUSH, CG::r(iter_rhs.val()));
|
|
iter_rhs.emit_post(frag);
|
|
CLOSE_AGG(cg, frag);
|
|
PUSH_INSTR(frag, BytecodeStream::POP, CG::r(r));
|
|
return r;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
throw InternalError("Unexpected fall-through in bind_binop_par.");
|
|
}
|
|
|
|
int bind_binop_par_set(CodeGen& cg, CG_Builder& frag, BinOpType op, int r_lhs, int r_rhs) {
|
|
int r;
|
|
switch (op) {
|
|
// Actual builtins
|
|
case BOT_EQ:
|
|
case BOT_EQUIV: {
|
|
r = GET_REG(cg);
|
|
int r_tmp = GET_REG(cg);
|
|
PUSH_INSTR(frag, BytecodeStream::DIFF, CG::r(r_lhs), CG::r(r_rhs), CG::r(r));
|
|
PUSH_INSTR(frag, BytecodeStream::ISEMPTY, CG::r(r), CG::r(r));
|
|
PUSH_INSTR(frag, BytecodeStream::DIFF, CG::r(r_rhs), CG::r(r_lhs), CG::r(r_tmp));
|
|
PUSH_INSTR(frag, BytecodeStream::ISEMPTY, CG::r(r_tmp), CG::r(r_tmp));
|
|
PUSH_INSTR(frag, BytecodeStream::AND, CG::r(r), CG::r(r_tmp), CG::r(r));
|
|
return r;
|
|
}
|
|
case BOT_NQ: {
|
|
r = bind_binop_par_set(cg, frag, BOT_EQ, r_lhs, r_rhs);
|
|
PUSH_INSTR(frag, BytecodeStream::NOT, CG::r(r), CG::r(r));
|
|
return r;
|
|
}
|
|
case BOT_LE:
|
|
r = GET_REG(cg);
|
|
TODO();
|
|
return r;
|
|
case BOT_LQ:
|
|
r = GET_REG(cg);
|
|
TODO();
|
|
return r;
|
|
case BOT_GR:
|
|
r = GET_REG(cg);
|
|
TODO();
|
|
return r;
|
|
case BOT_GQ:
|
|
r = GET_REG(cg);
|
|
TODO();
|
|
return r;
|
|
case BOT_UNION:
|
|
r = GET_REG(cg);
|
|
PUSH_INSTR(frag, BytecodeStream::UNION, CG::r(r_lhs), CG::r(r_rhs), CG::r(r));
|
|
return r;
|
|
case BOT_INTERSECT:
|
|
r = GET_REG(cg);
|
|
PUSH_INSTR(frag, BytecodeStream::INTERSECTION, CG::r(r_lhs), CG::r(r_rhs), CG::r(r));
|
|
return r;
|
|
case BOT_SUBSET: {
|
|
// (L subset R) <-> ((L intersect R) == L)
|
|
r = GET_REG(cg);
|
|
int l_fin(GET_LABEL(cg));
|
|
int r_inter(GET_REG(cg));
|
|
PUSH_INSTR(frag, BytecodeStream::INTERSECTION, CG::r(r_lhs), CG::r(r_rhs), CG::r(r_inter));
|
|
|
|
int r_sz(GET_REG(cg));
|
|
int r_tmp(GET_REG(cg));
|
|
PUSH_INSTR(frag, BytecodeStream::LENGTH, CG::r(r_lhs), CG::r(r_tmp));
|
|
PUSH_INSTR(frag, BytecodeStream::LENGTH, CG::r(r_inter), CG::r(r_sz));
|
|
PUSH_INSTR(frag, BytecodeStream::EQI, CG::r(r_tmp), CG::r(r_sz), CG::r(r));
|
|
PUSH_INSTR(frag, BytecodeStream::JMPIFNOT, CG::r(r), CG::l(l_fin));
|
|
|
|
int r_idx(GET_REG(cg));
|
|
int l_loop(GET_LABEL(cg));
|
|
PUSH_INSTR(frag, BytecodeStream::IMMI, CG::i(1), CG::r(r_idx));
|
|
PUSH_LABEL(frag, l_loop);
|
|
PUSH_INSTR(frag, BytecodeStream::LEI, CG::r(r_idx), CG::r(r_sz), CG::r(r_tmp));
|
|
PUSH_INSTR(frag, BytecodeStream::JMPIFNOT, CG::r(r_tmp), CG::l(l_fin));
|
|
|
|
int r_tmp2(GET_REG(cg));
|
|
PUSH_INSTR(frag, BytecodeStream::GET_VEC, CG::r(r_lhs), CG::r(r_idx), CG::r(r_tmp));
|
|
PUSH_INSTR(frag, BytecodeStream::GET_VEC, CG::r(r_inter), CG::r(r_idx), CG::r(r_tmp2));
|
|
PUSH_INSTR(frag, BytecodeStream::EQI, CG::r(r_tmp), CG::r(r_tmp2), CG::r(r));
|
|
PUSH_INSTR(frag, BytecodeStream::JMPIFNOT, CG::r(r), CG::l(l_fin));
|
|
|
|
PUSH_INSTR(frag, BytecodeStream::INCI, CG::r(r_idx));
|
|
PUSH_INSTR(frag, BytecodeStream::JMP, CG::l(l_loop));
|
|
|
|
PUSH_LABEL(frag, l_fin);
|
|
return r;
|
|
}
|
|
case BOT_SUPERSET: {
|
|
return bind_binop_par_set(cg, frag, BOT_SUBSET, r_rhs, r_lhs);
|
|
}
|
|
case BOT_DIFF: {
|
|
int r(GET_REG(cg));
|
|
PUSH_INSTR(frag, BytecodeStream::DIFF, CG::r(r_lhs), CG::r(r_rhs), CG::r(r));
|
|
return r;
|
|
}
|
|
case BOT_SYMDIFF: {
|
|
int r(GET_REG(cg));
|
|
int r_tmp(GET_REG(cg));
|
|
PUSH_INSTR(frag, BytecodeStream::DIFF, CG::r(r_lhs), CG::r(r_rhs), CG::r(r));
|
|
PUSH_INSTR(frag, BytecodeStream::DIFF, CG::r(r_rhs), CG::r(r_lhs), CG::r(r_tmp));
|
|
PUSH_INSTR(frag, BytecodeStream::UNION, CG::r(r), CG::r(r_tmp), CG::r(r));
|
|
return r;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
throw InternalError("Unexpected fall-through in bind_binop_par_set.");
|
|
}
|
|
|
|
CG_Cond::T linear_cond(CodeGen& cg, CG_Builder& frag, BinOpType op, Mode ctx, int r_lhs,
|
|
int r_rhs) {
|
|
GCLock lock;
|
|
switch (op) {
|
|
// Actual builtins
|
|
case BOT_LQ:
|
|
case BOT_LE: {
|
|
// Check bounds
|
|
int lb_lhs = GET_REG(cg);
|
|
int ub_rhs = GET_REG(cg);
|
|
PUSH_INSTR(frag, BytecodeStream::LB, CG::r(r_lhs), CG::r(lb_lhs));
|
|
PUSH_INSTR(frag, BytecodeStream::UB, CG::r(r_rhs), CG::r(ub_rhs));
|
|
PUSH_INSTR(frag, (op == BOT_LE ? BytecodeStream::LTI : BytecodeStream::LEI), CG::r(lb_lhs),
|
|
CG::r(ub_rhs), CG::r(lb_lhs));
|
|
int c = GET_REG(cg);
|
|
int x = GET_REG(cg);
|
|
int k = GET_REG(cg);
|
|
int z = (op == BOT_LE ? +1 : 0);
|
|
PUSH_INSTR(frag, BytecodeStream::SIMPLIFY_LIN, CG::r(r_lhs), CG::r(r_rhs), CG::i(z), CG::r(c),
|
|
CG::r(x), CG::r(k));
|
|
auto linear =
|
|
CG_Cond::call({"pre_int_lin_le"}, ctx, true,
|
|
{Type::varbool(), Type::parint(1), Type::varint(1), Type::parint()},
|
|
{CG::r(c), CG::r(x), CG::r(k)});
|
|
return CG_Cond::forall(ctx, CG_Cond::reg(lb_lhs, true), linear);
|
|
}
|
|
case BOT_EQUIV:
|
|
case BOT_EQ: {
|
|
// Check domain
|
|
int dom_lhs = GET_REG(cg);
|
|
int dom_rhs = GET_REG(cg);
|
|
PUSH_INSTR(frag, BytecodeStream::DOM, CG::r(r_lhs), CG::r(dom_lhs));
|
|
PUSH_INSTR(frag, BytecodeStream::DOM, CG::r(r_rhs), CG::r(dom_rhs));
|
|
PUSH_INSTR(frag, BytecodeStream::INTERSECTION, CG::r(dom_lhs), CG::r(dom_rhs),
|
|
CG::r(dom_lhs));
|
|
PUSH_INSTR(frag, BytecodeStream::ISEMPTY, CG::r(dom_lhs), CG::r(dom_rhs));
|
|
PUSH_INSTR(frag, BytecodeStream::NOT, CG::r(dom_rhs), CG::r(dom_rhs));
|
|
// Create linear equation
|
|
int k = GET_REG(cg);
|
|
int c = GET_REG(cg);
|
|
int x = GET_REG(cg);
|
|
PUSH_INSTR(frag, BytecodeStream::SIMPLIFY_LIN, CG::r(r_lhs), CG::r(r_rhs), CG::i(0), CG::r(c),
|
|
CG::r(x), CG::r(k));
|
|
auto linear =
|
|
CG_Cond::call({"pre_int_lin_eq"}, ctx, true,
|
|
{Type::varbool(), Type::parint(1), Type::varint(1), Type::parint()},
|
|
{CG::r(c), CG::r(x), CG::r(k)});
|
|
return CG_Cond::forall(ctx, CG_Cond::reg(dom_rhs, true), linear);
|
|
}
|
|
case BOT_NQ:
|
|
return ~linear_cond(cg, frag, BOT_EQ, -ctx, r_lhs, r_rhs);
|
|
case BOT_GR:
|
|
return linear_cond(cg, frag, BOT_LE, ctx, r_rhs, r_lhs);
|
|
case BOT_GQ:
|
|
return linear_cond(cg, frag, BOT_LQ, ctx, r_rhs, r_lhs);
|
|
default:
|
|
break;
|
|
}
|
|
throw InternalError("Unexpected fall-through in linear_cond.");
|
|
}
|
|
|
|
CG_ProcID CodeGen::resolve_fun(FunctionI* fun, bool reserved_name) {
|
|
auto it(fun_bodies.find(fun));
|
|
if (it != fun_bodies.end()) return it->second;
|
|
|
|
GCLock lock;
|
|
|
|
int p_idx = bytecode.size();
|
|
CG_ProcID p_id(CG_ProcID::proc(p_idx));
|
|
ASTExprVec<VarDecl> params(fun->params());
|
|
|
|
std::stringstream ss;
|
|
if (!reserved_name && fun->e()) {
|
|
ss << "f_" << fun->id().str();
|
|
for (auto& param : params) {
|
|
ss << "_";
|
|
if (param->type().dim() > 0) {
|
|
ss << "d" << param->type().dim();
|
|
} else if (param->type().dim() < 0) {
|
|
ss << "dT";
|
|
}
|
|
if (param->type().isvar()) {
|
|
ss << "v";
|
|
}
|
|
if (param->type().is_set()) {
|
|
ss << "s";
|
|
}
|
|
switch (param->type().bt()) {
|
|
case Type::BT_BOOL: {
|
|
ss << "b";
|
|
break;
|
|
}
|
|
case Type::BT_INT: {
|
|
ss << "i";
|
|
break;
|
|
}
|
|
case Type::BT_FLOAT: {
|
|
ss << "f";
|
|
break;
|
|
}
|
|
case Type::BT_STRING: {
|
|
ss << "s";
|
|
break;
|
|
}
|
|
case Type::BT_ANN: {
|
|
ss << "a";
|
|
break;
|
|
}
|
|
case Type::BT_TOP: {
|
|
ss << "t";
|
|
break;
|
|
}
|
|
default: {
|
|
assert(false);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
ss << fun->id().str();
|
|
}
|
|
|
|
bytecode.emplace_back(ss.str(), params.size());
|
|
fun_bodies.insert(std::make_pair(fun, p_id));
|
|
return p_id;
|
|
}
|
|
|
|
struct dispatch_node {
|
|
int label;
|
|
int level;
|
|
uint64_t sig;
|
|
};
|
|
|
|
std::tuple<CG_ProcID, BytecodeProc::Mode, bool> find_call_fun(CodeGen& cg, const ASTString& ident,
|
|
const Type& ret_type,
|
|
std::vector<Type> arg_types,
|
|
BytecodeProc::Mode m,
|
|
bool reserved_name) {
|
|
for (auto& arg_type : arg_types) {
|
|
arg_type.ti(Type::TI_PAR);
|
|
}
|
|
int sz = arg_types.size();
|
|
CallSig sig(ident, arg_types);
|
|
auto it(cg.dispatch.find(sig));
|
|
|
|
BytecodeProc::Mode call_mode(ret_type.isbool() ? m : BytecodeProc::FUN);
|
|
BytecodeProc::Mode def_mode(ret_type.isbool() ? m : BytecodeProc::ROOT);
|
|
|
|
if (it != cg.dispatch.end()) {
|
|
auto d_proc(it->second);
|
|
if (d_proc.first.is_builtin() || cg.bytecode[d_proc.first.id()].is_available(call_mode)) {
|
|
return {d_proc.first, call_mode, d_proc.second};
|
|
}
|
|
}
|
|
|
|
GCLock lock;
|
|
auto bodies = std::move(cg.fun_map.get_bodies(ident, arg_types));
|
|
assert(!bodies.empty());
|
|
auto neg_bodies = std::move(cg.fun_map.get_bodies(ASTString(ident.str() + "_neg"), arg_types));
|
|
|
|
if (ret_type.isbool() && call_mode != BytecodeProc::ROOT) {
|
|
bool valid = false;
|
|
if (cg.fun_map.defines_mode(ident, arg_types, call_mode).first) {
|
|
valid = true;
|
|
} else if (BytecodeProc::is_neg(m) && std::any_of(neg_bodies.begin(), neg_bodies.end(),
|
|
[](FunctionI* fi) { return fi->e(); })) {
|
|
valid = true;
|
|
} else {
|
|
valid = cg.fun_map.defines_mode(ident, arg_types, BytecodeProc::FUN).first;
|
|
if (valid) {
|
|
call_mode = BytecodeProc::FUN;
|
|
def_mode = BytecodeProc::FUN;
|
|
}
|
|
}
|
|
if (!valid &&
|
|
std::none_of(bodies.begin(), bodies.end(), [](FunctionI* fi) { return fi->e(); })) {
|
|
throw InternalError(ident.str() +
|
|
" is used in a reified context, but no reification is available.");
|
|
}
|
|
}
|
|
|
|
std::vector<CG_ProcID> procs;
|
|
for (FunctionI* b : bodies) {
|
|
CG_ProcID body(cg.resolve_fun(b, reserved_name && bodies.size() == 1));
|
|
// Force the body to be created
|
|
procs.push_back(body);
|
|
if (!cg.bytecode[body.id()].is_available(call_mode)) {
|
|
cg.bytecode[body.id()].body(call_mode);
|
|
cg.pending_bodies.emplace_back(b, std::make_pair(call_mode, def_mode));
|
|
}
|
|
}
|
|
|
|
// If there's a unique candidate, go for it.
|
|
if (procs.size() == 1) {
|
|
if (it == cg.dispatch.end()) {
|
|
cg.dispatch.insert(std::make_pair(sig, std::make_pair(procs[0], false)));
|
|
}
|
|
return {procs[0], call_mode, false};
|
|
}
|
|
|
|
CG_ProcID d_proc(CG_ProcID::builtin(0));
|
|
if (it != cg.dispatch.end()) {
|
|
d_proc = it->second.first;
|
|
} else {
|
|
// Otherwise, generate the dispatch function.
|
|
int p_idx = cg.bytecode.size();
|
|
|
|
std::stringstream ss;
|
|
if (reserved_name) {
|
|
ss << ident.str();
|
|
} else {
|
|
ss << "d_" << ident.str();
|
|
for (auto& type : arg_types) {
|
|
ss << "_";
|
|
if (type.dim() > 0) {
|
|
ss << "d" << type.dim();
|
|
} else if (type.dim() < 0) {
|
|
ss << "dT";
|
|
}
|
|
if (type.is_set()) {
|
|
ss << "s";
|
|
}
|
|
switch (type.bt()) {
|
|
case Type::BT_BOOL: {
|
|
ss << "b";
|
|
break;
|
|
}
|
|
case Type::BT_INT: {
|
|
ss << "i";
|
|
break;
|
|
}
|
|
case Type::BT_FLOAT: {
|
|
ss << "f";
|
|
break;
|
|
}
|
|
case Type::BT_STRING: {
|
|
ss << "s";
|
|
break;
|
|
}
|
|
case Type::BT_ANN: {
|
|
ss << "a";
|
|
break;
|
|
}
|
|
case Type::BT_TOP: {
|
|
ss << "t";
|
|
break;
|
|
}
|
|
default: {
|
|
assert(false);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
d_proc = CG_ProcID::proc(p_idx);
|
|
cg.bytecode.emplace_back(ss.str(), arg_types.size());
|
|
cg.dispatch.insert(std::make_pair(sig, std::make_pair(d_proc, true)));
|
|
}
|
|
|
|
// Now generate the dispatch body.
|
|
CG_Builder frag;
|
|
std::vector<uint64_t> var_sig(sz);
|
|
std::vector<uint64_t> def_sig(bodies.size());
|
|
for (int bi = 0; bi < bodies.size(); ++bi) {
|
|
ASTExprVec<VarDecl> params(bodies[bi]->params());
|
|
for (int ii = 0; ii < params.size(); ++ii) {
|
|
if (params[ii]->type().isvar()) {
|
|
var_sig[ii] |= 1ull << bi;
|
|
def_sig[bi] |= 1ull << ii;
|
|
}
|
|
}
|
|
}
|
|
|
|
std::vector<std::unordered_map<uint64_t, int>> sig_table(sz + 1);
|
|
std::vector<dispatch_node> nodes;
|
|
|
|
nodes.push_back(dispatch_node{-1, 0, (1ull << bodies.size()) - 1});
|
|
|
|
for (int ii = 0; ii < nodes.size(); ii++) {
|
|
dispatch_node d(nodes[ii]);
|
|
if (d.label != -1) PUSH_LABEL(frag, d.label);
|
|
if (!d.sig) {
|
|
PUSH_INSTR(frag, BytecodeStream::ABORT);
|
|
} else if (d.level == sz) {
|
|
// Find the best candidate, and emit a call.
|
|
uint64_t candidates(d.sig);
|
|
unsigned int best(find_lsb(candidates));
|
|
uint64_t best_sig(def_sig[best]);
|
|
candidates ^= 1ull << best;
|
|
while (candidates) {
|
|
unsigned int curr(find_lsb(candidates));
|
|
candidates ^= 1ull << curr;
|
|
uint64_t sig(def_sig[curr]);
|
|
if (!(sig & ~best_sig)) {
|
|
// At least as good as the incumbent
|
|
best = curr;
|
|
best_sig = sig;
|
|
}
|
|
}
|
|
CG_ProcID p_id(procs[best]);
|
|
PUSH_INSTR(frag, BytecodeStream::TCALL, call_mode, p_id,
|
|
CG::i(bodies[best]->ti()->type().isvar()));
|
|
} else {
|
|
int par_label;
|
|
auto p_it(sig_table[d.level + 1].find(d.sig));
|
|
if (p_it != sig_table[d.level + 1].end()) {
|
|
par_label = p_it->second;
|
|
} else {
|
|
par_label = GET_LABEL(cg);
|
|
// int idx = nodes.size();
|
|
sig_table[d.level + 1].insert(std::make_pair(d.sig, par_label));
|
|
nodes.push_back(dispatch_node{par_label, d.level + 1, d.sig});
|
|
}
|
|
uint64_t v_sig(d.sig & var_sig[d.level]);
|
|
if (v_sig == d.sig) {
|
|
PUSH_INSTR(frag, BytecodeStream::JMP, CG::l(par_label));
|
|
} else {
|
|
int var_label;
|
|
auto v_it(sig_table[d.level + 1].find(v_sig));
|
|
if (v_it != sig_table[d.level + 1].end()) {
|
|
var_label = v_it->second;
|
|
} else {
|
|
var_label = GET_LABEL(cg);
|
|
// int idx = nodes.size();
|
|
sig_table[d.level + 1].insert(std::make_pair(v_sig, var_label));
|
|
nodes.push_back(dispatch_node{var_label, d.level + 1, v_sig});
|
|
}
|
|
|
|
PUSH_INSTR(frag, BytecodeStream::ISPAR, CG::r(d.level), CG::r(sz));
|
|
PUSH_INSTR(frag, BytecodeStream::JMPIF, CG::r(sz), CG::l(par_label));
|
|
PUSH_INSTR(frag, BytecodeStream::JMP, CG::l(var_label));
|
|
}
|
|
}
|
|
}
|
|
|
|
cg.append(d_proc.id(), call_mode, frag);
|
|
|
|
return {d_proc, call_mode, false};
|
|
}
|
|
|
|
std::tuple<CG_ProcID, BytecodeProc::Mode, bool> find_call_fun(CodeGen& cg, Call* call,
|
|
BytecodeProc::Mode m,
|
|
bool reserved_name) {
|
|
std::vector<Type> arg_types;
|
|
int sz = call->n_args();
|
|
for (int ii = 0; ii < sz; ++ii) {
|
|
Type t(call->arg(ii)->type());
|
|
arg_types.push_back(t);
|
|
}
|
|
return find_call_fun(cg, call->id(), call->type(), arg_types, m, reserved_name);
|
|
}
|
|
/*
|
|
CG_ProcID find_call_pred(CodeGen& cg, Call* c) {
|
|
return CG_ProcID::proc(0xbead);
|
|
|
|
}
|
|
*/
|
|
|
|
// Analyse an expression (and sub-expressions) for partiality
|
|
#if 0
|
|
struct ClearFlags : public EVisitor {
|
|
bool enter(Expression* e) {
|
|
if(!e->isUnboxedVal() && e->user_flag0()) {
|
|
e->user_flag0(0);
|
|
e->user_flag1(0);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
// FIXME: We need to identify call bodies.
|
|
void vCall(const Call&) {}
|
|
|
|
static void clear(Expression* e) {
|
|
ClearFlags cf;
|
|
TopDownIterator<ClearFlags> td(cf);
|
|
td.run(e);
|
|
}
|
|
};
|
|
|
|
struct Partiality {
|
|
Partiality(CodeGen& _cg) : cg(_cg) { }
|
|
|
|
// We use _flag_3 to track whether something is already on the
|
|
// stack, and _flag_4 to track whether it is eliminated as true.
|
|
bool is_partial(Expression* e) {
|
|
if(e->isUnboxedVal())
|
|
return false;
|
|
// Either on the call stack and still open, or completed.
|
|
// In either case, check whether the partiality-flag is set.
|
|
if(e->user_flag0())
|
|
return e->user_flag1();
|
|
// Otherwise, mark it as pending, and enter it.
|
|
e->user_flag0(1);
|
|
bool p = _is_partial(e);
|
|
// Record the result, and return.
|
|
e->user_flag1(p);
|
|
return p;
|
|
}
|
|
|
|
bool _is_partial(Expression* e) {
|
|
// First, Boolean expressions are always total.
|
|
if(e->type().isbool())
|
|
return false;
|
|
// Otherwise, look at the
|
|
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_ID:
|
|
return false;
|
|
case Expression::E_ARRAYLIT: {
|
|
ArrayLit* a(e->template cast<ArrayLit>());
|
|
int sz(a->size());
|
|
for(int ii = 0; ii < sz; ++ii) {
|
|
if(is_partial((*a)[ii]))
|
|
return true;
|
|
}
|
|
return true;
|
|
}
|
|
case Expression::E_ARRAYACCESS: {
|
|
/*
|
|
ArrayAccess* a(e->template cast<ArrayAccess>());
|
|
if(is_partial(a->v()))
|
|
return true;
|
|
ASTExprVec<Expression> idx(a->idx());
|
|
int sz(idx.size());
|
|
for(int ii = 0; ii < sz; ++ii) {
|
|
if(is_partial(idx[ii]))
|
|
return true;
|
|
}
|
|
break;
|
|
*/
|
|
// FIXME: Needs an analysis to determine whether
|
|
// dom(a->v) subseteq index_set(A).
|
|
return true;
|
|
}
|
|
case Expression::E_COMP: {
|
|
// A comprehension is total if all its generators
|
|
// and its body are total.
|
|
// Don't need to look in the where clauses, because
|
|
// they're Boolean, and therefore total.
|
|
Comprehension* c(e->template cast<Comprehension>());
|
|
int sz = c->n_generators();
|
|
for(int g = 0; g < c->n_generators(); ++g) {
|
|
if(is_partial(c->in(g)))
|
|
return true;
|
|
}
|
|
return is_partial(c->e());
|
|
}
|
|
case Expression::E_ITE: {
|
|
// The conditions are Boolean, so must be total.
|
|
// Look at the values.
|
|
ITE* ite(e->template cast<ITE>());
|
|
int sz(ite->size());
|
|
if(is_partial(ite->e_else()))
|
|
return false;
|
|
for(int ii = 0; ii < sz; ++ii) {
|
|
if(is_partial(ite->e_then(ii)))
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
case Expression::E_BINOP: {
|
|
BinOp* b(e->template cast<BinOp>());
|
|
// First, check if the op is itself partial.
|
|
// TODO: (Eventually) add a pass to determine whether we can exclude
|
|
// 0 from the domain of b->rhs().
|
|
if(b->op() == BOT_DIV || b->op() == BOT_IDIV || b->op() == BOT_MOD)
|
|
return true;
|
|
return is_partial(b->lhs()) || is_partial(b->rhs());
|
|
}
|
|
case Expression::E_UNOP:
|
|
return is_partial(e->template cast<UnOp>()->e());
|
|
|
|
case Expression::E_CALL: {
|
|
Call* call(e->template cast<Call>());
|
|
int sz = call->n_args();
|
|
// Check if any of its arguments are partial.
|
|
for(int ii = 0; ii < sz; ++ii) {
|
|
if(is_partial(call->arg(ii)))
|
|
return true;
|
|
}
|
|
// FIXME: Identify the relevant call body, recursively
|
|
// check for partiality.
|
|
// return false;
|
|
for(FunctionI* b : cg.fun_map.get_bodies(call)) {
|
|
// Boolean-typed values are always total
|
|
if(b->ti()->type().isbool())
|
|
continue;
|
|
if(!b->e())
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
case Expression::E_LET: {
|
|
Let* let(e->template cast<Let>());
|
|
|
|
// Check if any of the expressions are partial.
|
|
ASTExprVec<Expression> bindings(let->let());
|
|
for(Expression* item : bindings) {
|
|
if (VarDecl* vd = e->dyn_cast<VarDecl>()) {
|
|
if(vd->e()) {
|
|
// If both a domain and a definition are given,
|
|
// the domain might be constraining.
|
|
if (vd->ti()->domain())
|
|
return true;
|
|
if(is_partial(vd->e()))
|
|
return true;
|
|
}
|
|
} else {
|
|
// If there's some item that isn't a binding, it must be a constriant
|
|
return true;
|
|
}
|
|
}
|
|
return is_partial(let->in());
|
|
}
|
|
case Expression::E_ANON:
|
|
case Expression::E_VARDECL:
|
|
case Expression::E_TI:
|
|
case Expression::E_TIID:
|
|
throw InternalError("Bytecode generator encountered unexpected expression type.");
|
|
}
|
|
}
|
|
|
|
void reset_flags(Expression* e) {
|
|
/*
|
|
if(!e->isUnboxedVal()) {
|
|
if(e->_flag_3) {
|
|
e->_flag_3 = e->_flag_4 = 0;
|
|
}
|
|
}
|
|
*/
|
|
}
|
|
|
|
CodeGen& cg;
|
|
|
|
};
|
|
#endif
|
|
|
|
ASTStSet CodeGen::scope(Expression* e) {
|
|
// Is it already cached?
|
|
auto it(_exp_scope.find(e));
|
|
if (it != _exp_scope.end()) return (*it).second;
|
|
|
|
// Otherwise, dispatch on the type.
|
|
ASTStSet r;
|
|
switch (e->eid()) {
|
|
case Expression::E_INTLIT:
|
|
case Expression::E_FLOATLIT:
|
|
case Expression::E_SETLIT:
|
|
case Expression::E_BOOLLIT:
|
|
case Expression::E_STRINGLIT:
|
|
break;
|
|
case Expression::E_ID: {
|
|
Id* id(e->template cast<Id>());
|
|
r.insert(id->v());
|
|
break;
|
|
}
|
|
case Expression::E_ARRAYLIT: {
|
|
ArrayLit* a(e->template cast<ArrayLit>());
|
|
int sz(a->size());
|
|
for (int ii = 0; ii < sz; ++ii) {
|
|
ASTStSet rr(scope((*a)[ii]));
|
|
r.insert(rr.begin(), rr.end());
|
|
}
|
|
break;
|
|
}
|
|
case Expression::E_ARRAYACCESS: {
|
|
ArrayAccess* a(e->template cast<ArrayAccess>());
|
|
r = scope(a->v());
|
|
|
|
ASTExprVec<Expression> idx(a->idx());
|
|
int sz(idx.size());
|
|
for (int ii = 0; ii < sz; ++ii) {
|
|
ASTStSet rr(scope(idx[ii]));
|
|
r.insert(rr.begin(), rr.end());
|
|
}
|
|
break;
|
|
}
|
|
case Expression::E_COMP: {
|
|
// Work from the inner expression out.
|
|
Comprehension* c(e->template cast<Comprehension>());
|
|
r = scope(c->e());
|
|
int sz = c->n_generators();
|
|
for (int g = sz - 1; g >= 0; --g) {
|
|
ASTStSet r_in(scope(c->in(g)));
|
|
|
|
if (c->where(g)) {
|
|
ASTStSet r_where(scope(c->where(g)));
|
|
r.insert(r_where.begin(), r_where.end());
|
|
r.insert(r_in.begin(), r_in.end());
|
|
}
|
|
|
|
// Now remove the variables that were bound.
|
|
for (int d = 0; d < c->n_decls(g); ++d) {
|
|
VarDecl* vd(c->decl(g, d));
|
|
r.erase(vd->id()->str());
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
case Expression::E_ITE: {
|
|
ITE* ite(e->template cast<ITE>());
|
|
int sz(ite->size());
|
|
r = scope(ite->e_else());
|
|
for (int ii = 0; ii < sz; ++ii) {
|
|
ASTStSet rr(scope(ite->e_if(ii)));
|
|
r.insert(rr.begin(), rr.end());
|
|
rr = scope(ite->e_then(ii));
|
|
r.insert(rr.begin(), rr.end());
|
|
}
|
|
break;
|
|
}
|
|
case Expression::E_BINOP: {
|
|
BinOp* b(e->template cast<BinOp>());
|
|
r = scope(b->lhs());
|
|
ASTStSet rr(scope(b->rhs()));
|
|
r.insert(rr.begin(), rr.end());
|
|
break;
|
|
}
|
|
case Expression::E_UNOP:
|
|
r = scope(e->template cast<UnOp>()->e());
|
|
break;
|
|
case Expression::E_CALL: {
|
|
Call* call(e->template cast<Call>());
|
|
int sz = call->n_args();
|
|
for (int ii = 0; ii < sz; ++ii) {
|
|
ASTStSet rr(scope(call->arg(ii)));
|
|
r.insert(rr.begin(), rr.end());
|
|
}
|
|
break;
|
|
}
|
|
case Expression::E_LET: {
|
|
Let* let(e->template cast<Let>());
|
|
ASTExprVec<Expression> bindings(let->let());
|
|
int sz(bindings.size());
|
|
r = scope(let->in());
|
|
for (int ii = sz - 1; ii >= 0; --ii) {
|
|
if (VarDecl* vd = bindings[ii]->dyn_cast<VarDecl>()) {
|
|
r.erase(vd->id()->str());
|
|
if (vd->e()) {
|
|
ASTStSet r_d(scope(vd->e()));
|
|
r.insert(r_d.begin(), r_d.end());
|
|
}
|
|
} else {
|
|
ASTStSet r_e(scope(bindings[ii]));
|
|
r.insert(r_e.begin(), r_e.end());
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
case Expression::E_ANON:
|
|
case Expression::E_VARDECL:
|
|
case Expression::E_TI:
|
|
case Expression::E_TIID:
|
|
throw InternalError("Bytecode generator encountered unexpected expression type.");
|
|
}
|
|
|
|
_exp_scope.insert(std::make_pair(e, r));
|
|
return r;
|
|
}
|
|
|
|
CodeGen::Binding CodeGen::cache_lookup(Expression* e) { return env().cache_lookup(e, scope(e)); }
|
|
void CodeGen::cache_store(Expression* e, CodeGen::Binding l) { env().cache_store(e, scope(e), l); }
|
|
|
|
void _debugcond(CG_Cond::T c) {
|
|
CG_Cond::_T* p(c.get());
|
|
if (c.sign()) std::cerr << "~";
|
|
if (!p) {
|
|
std::cerr << "T";
|
|
} else {
|
|
switch (p->kind()) {
|
|
case CG_Cond::CC_Reg:
|
|
std::cerr << "R" << p->reg[0].reg;
|
|
break;
|
|
case CG_Cond::CC_Call:
|
|
std::cerr << "<Call>";
|
|
break;
|
|
case CG_Cond::CC_And:
|
|
std::cerr << "(and";
|
|
for (CG_Cond::T child : static_cast<CG_Cond::C_And*>(p)->children) {
|
|
std::cerr << " ";
|
|
_debugcond(child);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
void debugcond(CG_Cond::T c) {
|
|
_debugcond(c);
|
|
std::cerr << std::endl;
|
|
}
|
|
|
|
int bind_cst(int x, CodeGen& cg, CG_Builder& frag) {
|
|
CG::Binding b;
|
|
if (cg.env().cache_lookup_cst(x, b)) return b.first;
|
|
int r = GET_REG(cg);
|
|
PUSH_INSTR(frag, BytecodeStream::IMMI, CG::i(x), CG::r(r));
|
|
cg.env().cache_store_cst(x, std::make_pair(r, CG_Cond::ttt()));
|
|
return r;
|
|
}
|
|
|
|
// WARNING ON THE USE OF CG_Conds: register caching assumes that CG_Conds dont escape an
|
|
// aggregation context, and will be available on all paths. If the cond is forced conditionally
|
|
// (e.g. shortcutting), cg.push_env() should be called before the force() call, and cg.pop_env()
|
|
// called where control flow rejoins.
|
|
|
|
// Given CG_Cond cond, collect the disjuncts having positive or negative values.
|
|
// Returns false if the conjunction is a contradiction.
|
|
bool collect_prod(std::vector<int>& pos, std::vector<int>& neg,
|
|
std::vector<CG_Cond::C_And*>& delayed, CG_Cond::T cond, CodeGen& cg,
|
|
CG_Builder& frag) {
|
|
assert(cond.get());
|
|
CG_Cond::_T* p(cond.get());
|
|
bool sign(cond.sign());
|
|
|
|
if (p->reg[1 - sign].is_seen || p->reg[1 - sign].is_root) return false;
|
|
if (p->reg[sign].is_seen || p->reg[sign].is_root) return true;
|
|
p->reg[sign].is_seen = true;
|
|
|
|
if (p->reg[sign].has_reg()) {
|
|
pos.push_back(p->reg[sign].reg);
|
|
} else if (p->reg[1 - sign].has_reg()) {
|
|
neg.push_back(p->reg[1 - sign].reg);
|
|
} else if (p->kind() == CG_Cond::CC_And && !sign) {
|
|
// Recursively collect the children.
|
|
std::vector<CG_Cond::T>& children(static_cast<CG_Cond::C_And*>(p)->children);
|
|
for (CG_Cond::T c : children) {
|
|
if (!collect_prod(pos, neg, delayed, c, cg, frag)) return false;
|
|
}
|
|
} else if (p->kind() == CG_Cond::CC_And) {
|
|
// How do we decide which way to compile the remaining conditions?
|
|
delayed.push_back(static_cast<CG_Cond::C_And*>(p));
|
|
} else {
|
|
assert(p->kind() == CG_Cond::CC_Call);
|
|
CG_Cond::C_Call* call(static_cast<CG_Cond::C_Call*>(p));
|
|
std::vector<Type> ty(call->ty.begin() + 1, call->ty.end());
|
|
auto fun = find_call_fun(cg, call->ident, call->ty[0], ty, call->m);
|
|
PUSH_INSTR(frag, BytecodeStream::CALL, std::get<1>(fun), std::get<0>(fun),
|
|
CG::i((!std::get<2>(fun)) && call->cse), call->params);
|
|
int r = GET_REG(cg);
|
|
PUSH_INSTR(frag, BytecodeStream::POP, CG::r(r));
|
|
if (BytecodeProc::is_neg(call->m) != BytecodeProc::is_neg(std::get<1>(fun))) {
|
|
if (call->ty[0].ispar()) {
|
|
PUSH_INSTR(frag, BytecodeStream::NOT, CG::r(r), CG::r(r));
|
|
} else {
|
|
auto fun =
|
|
find_call_fun(cg, {"op_not"}, Type::varbool(), {Type::varbool()}, BytecodeProc::FUN);
|
|
assert(std::get<1>(fun) == BytecodeProc::FUN);
|
|
PUSH_INSTR(frag, BytecodeStream::CALL, BytecodeProc::FUN, std::get<0>(fun),
|
|
CG::i(!std::get<2>(fun)), CG::r(r));
|
|
PUSH_INSTR(frag, BytecodeStream::POP, CG::r(r));
|
|
}
|
|
}
|
|
if (!sign)
|
|
pos.push_back(r);
|
|
else
|
|
neg.push_back(r);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
// Given CG_Cond cond, collect the disjuncts having positive or negative values.
|
|
// Returns true if the disjunction is a tautology.
|
|
bool collect_disj(std::vector<int>& pos, std::vector<int>& neg,
|
|
std::vector<CG_Cond::C_And*>& delayed, CG_Cond::T cond, CodeGen& cg,
|
|
CG_Builder& frag) {
|
|
assert(cond.get());
|
|
CG_Cond::_T* p(cond.get());
|
|
bool sign(cond.sign());
|
|
|
|
if (p->reg[1 - sign].is_seen || p->reg[sign].is_root) return true;
|
|
if (p->reg[sign].is_seen || p->reg[1 - sign].is_root) return false;
|
|
p->reg[sign].is_seen = true;
|
|
|
|
if (p->reg[sign].has_reg()) {
|
|
pos.push_back(p->reg[sign].reg);
|
|
} else if (p->reg[1 - sign].has_reg()) {
|
|
neg.push_back(p->reg[1 - sign].reg);
|
|
} else if (p->kind() == CG_Cond::CC_And && sign) {
|
|
// Recursively collect the children.
|
|
std::vector<CG_Cond::T>& children(static_cast<CG_Cond::C_And*>(p)->children);
|
|
for (CG_Cond::T c : children) {
|
|
if (collect_disj(pos, neg, delayed, ~c, cg, frag)) return true;
|
|
}
|
|
} else if (p->kind() == CG_Cond::CC_And) {
|
|
delayed.push_back(static_cast<CG_Cond::C_And*>(p));
|
|
} else {
|
|
CG_Cond::C_Call* call(static_cast<CG_Cond::C_Call*>(p));
|
|
std::vector<Type> ty(call->ty.begin() + 1, call->ty.end());
|
|
auto fun = find_call_fun(cg, call->ident, call->ty[0], ty, call->m);
|
|
PUSH_INSTR(frag, BytecodeStream::CALL, std::get<1>(fun), std::get<0>(fun),
|
|
CG::i((!std::get<2>(fun)) && call->cse), call->params);
|
|
int r = GET_REG(cg);
|
|
PUSH_INSTR(frag, BytecodeStream::POP, CG::r(r));
|
|
if (BytecodeProc::is_neg(call->m) != BytecodeProc::is_neg(std::get<1>(fun))) {
|
|
if (call->ty[0].ispar()) {
|
|
PUSH_INSTR(frag, BytecodeStream::NOT, CG::r(r), CG::r(r));
|
|
} else {
|
|
auto fun =
|
|
find_call_fun(cg, {"op_not"}, Type::varbool(), {Type::varbool()}, BytecodeProc::FUN);
|
|
assert(std::get<1>(fun) == BytecodeProc::FUN);
|
|
PUSH_INSTR(frag, BytecodeStream::CALL, BytecodeProc::FUN, std::get<0>(fun),
|
|
CG::i(!std::get<2>(fun)), CG::r(r));
|
|
PUSH_INSTR(frag, BytecodeStream::POP, CG::r(r));
|
|
}
|
|
}
|
|
if (!sign)
|
|
pos.push_back(r);
|
|
else
|
|
neg.push_back(r);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
std::pair<int, bool> _force_cond(CG_Cond::T cond, CodeGen& cg, CG_Builder& frag);
|
|
|
|
void collect_and_leaves(std::vector<CG_Cond::T>& par_leaves, std::vector<CG_Cond::T>& var_leaves,
|
|
CG_Cond::T child, CodeGen& cg, CG_Builder& frag) {
|
|
assert(child.get());
|
|
CG_Cond::_T* p(child.get());
|
|
bool sign(child.sign());
|
|
auto push = [&](CG_Cond::T r, bool is_par) {
|
|
if (is_par) {
|
|
par_leaves.push_back(r);
|
|
} else {
|
|
var_leaves.push_back(r);
|
|
}
|
|
};
|
|
|
|
if (p->reg[sign].has_reg()) {
|
|
push(child, p->reg[sign].is_par);
|
|
return;
|
|
}
|
|
if (p->reg[1 - sign].has_reg()) {
|
|
push(child, p->reg[1 - sign].is_par);
|
|
} else if (p->kind() == CG_Cond::CC_And && !sign) {
|
|
std::vector<CG_Cond::T>& children(static_cast<CG_Cond::C_And*>(p)->children);
|
|
for (CG_Cond::T c : children) {
|
|
collect_and_leaves(par_leaves, var_leaves, c, cg, frag);
|
|
}
|
|
} else {
|
|
push(child, /* Check if this is par. */ false);
|
|
}
|
|
}
|
|
void collect_or_leaves(std::vector<CG_Cond::T>& par_leaves, std::vector<CG_Cond::T>& var_leaves,
|
|
CG_Cond::T child, CodeGen& cg, CG_Builder& frag) {
|
|
assert(child.get());
|
|
CG_Cond::_T* p(child.get());
|
|
bool sign(child.sign());
|
|
auto push = [&](CG_Cond::T r, bool is_par) {
|
|
if (is_par) {
|
|
par_leaves.push_back(r);
|
|
} else {
|
|
var_leaves.push_back(r);
|
|
}
|
|
};
|
|
|
|
if (p->reg[1 - sign].has_reg()) {
|
|
push(~child, p->reg[1 - sign].is_par);
|
|
return;
|
|
}
|
|
if (p->reg[sign].has_reg()) {
|
|
push(~child, p->reg[sign].is_par);
|
|
} else if (p->kind() == CG_Cond::CC_And && !sign) {
|
|
std::vector<CG_Cond::T>& children(static_cast<CG_Cond::C_And*>(p)->children);
|
|
for (CG_Cond::T c : children) {
|
|
collect_or_leaves(par_leaves, var_leaves, c, cg, frag);
|
|
}
|
|
} else {
|
|
push(~child, /* Check if this is par. */ false);
|
|
}
|
|
}
|
|
|
|
void force_and_leaves(std::vector<int>& var_leaves, std::vector<int>& par_leaves, CG_Cond::T child,
|
|
CodeGen& cg, CG_Builder& frag) {
|
|
assert(child.get());
|
|
CG_Cond::_T* p(child.get());
|
|
bool sign(child.sign());
|
|
auto push = [&](int r, bool is_par) {
|
|
if (is_par) {
|
|
par_leaves.push_back(r);
|
|
} else {
|
|
var_leaves.push_back(r);
|
|
}
|
|
};
|
|
if (p->reg[sign].has_reg()) {
|
|
push(p->reg[sign].reg, p->reg[sign].is_par);
|
|
return;
|
|
}
|
|
cg.env().record_cached_cond(child);
|
|
if (p->reg[1 - sign].has_reg()) {
|
|
// Create the negation
|
|
int r(GET_REG(cg));
|
|
if (p->reg[1 - sign].is_par) {
|
|
PUSH_INSTR(frag, BytecodeStream::NOT, CG::r(p->reg[1 - sign].reg), CG::r(r));
|
|
} else {
|
|
GCLock lock;
|
|
auto fun =
|
|
find_call_fun(cg, {"op_not"}, Type::varbool(), {Type::varbool()}, BytecodeProc::FUN);
|
|
assert(std::get<1>(fun) == BytecodeProc::FUN);
|
|
PUSH_INSTR(frag, BytecodeStream::CALL, BytecodeProc::FUN, std::get<0>(fun),
|
|
CG::i(!std::get<2>(fun)), CG::r(p->reg[1 - sign].reg));
|
|
PUSH_INSTR(frag, BytecodeStream::POP, CG::r(r));
|
|
}
|
|
p->reg[sign] = CG_Cond::cond_reg(r, p->reg[1 - sign].is_par);
|
|
push(p->reg[sign].reg, p->reg[sign].is_par);
|
|
} else if (p->kind() == CG_Cond::CC_And && !sign) {
|
|
std::vector<CG_Cond::T>& children(static_cast<CG_Cond::C_And*>(p)->children);
|
|
for (CG_Cond::T c : children) {
|
|
force_and_leaves(var_leaves, par_leaves, c, cg, frag);
|
|
}
|
|
} else {
|
|
auto forced = _force_cond(child, cg, frag);
|
|
push(forced.first, forced.second);
|
|
}
|
|
}
|
|
|
|
// Pushing the _negation_ of child.
|
|
void force_or_leaves(std::vector<int>& var_leaves, std::vector<int>& par_leaves, CG_Cond::T child,
|
|
CodeGen& cg, CG_Builder& frag) {
|
|
assert(child.get());
|
|
CG_Cond::_T* p(child.get());
|
|
bool sign(child.sign());
|
|
auto push = [&](int r, bool is_par) {
|
|
if (is_par) {
|
|
par_leaves.push_back(r);
|
|
} else {
|
|
var_leaves.push_back(r);
|
|
}
|
|
};
|
|
if (p->reg[1 - sign].has_reg()) {
|
|
push(p->reg[1 - sign].reg, p->reg[1 - sign].is_par);
|
|
return;
|
|
}
|
|
cg.env().record_cached_cond(~child);
|
|
if (p->reg[sign].has_reg()) {
|
|
// Create the negation
|
|
int r(GET_REG(cg));
|
|
if (p->reg[sign].is_par) {
|
|
PUSH_INSTR(frag, BytecodeStream::NOT, CG::r(p->reg[sign].reg), CG::r(r));
|
|
} else {
|
|
GCLock lock;
|
|
auto fun =
|
|
find_call_fun(cg, {"op_not"}, Type::varbool(), {Type::varbool()}, BytecodeProc::FUN);
|
|
assert(std::get<1>(fun) == BytecodeProc::FUN);
|
|
PUSH_INSTR(frag, BytecodeStream::CALL, BytecodeProc::FUN, std::get<0>(fun),
|
|
CG::i(!std::get<2>(fun)), CG::r(p->reg[sign].reg));
|
|
PUSH_INSTR(frag, BytecodeStream::POP, CG::r(r));
|
|
}
|
|
p->reg[1 - sign] = CG_Cond::cond_reg(r, p->reg[sign].is_par);
|
|
push(p->reg[1 - sign].reg, p->reg[1 - sign].is_par);
|
|
} else if (p->kind() == CG_Cond::CC_And && !sign) {
|
|
std::vector<CG_Cond::T>& children(static_cast<CG_Cond::C_And*>(p)->children);
|
|
for (CG_Cond::T c : children) {
|
|
force_or_leaves(var_leaves, par_leaves, c, cg, frag);
|
|
}
|
|
} else {
|
|
auto forced = _force_cond(~child, cg, frag);
|
|
push(forced.first, forced.second);
|
|
}
|
|
}
|
|
|
|
std::pair<int, bool> _force_cond(CG_Cond::T cond, CodeGen& cg, CG_Builder& frag) {
|
|
CG_Cond::_T* p(cond.get());
|
|
bool negated(cond.sign());
|
|
if (!p) {
|
|
// Either true or false.
|
|
// return CG::locate_immi(1 - cond.sign(), cg, frag);
|
|
return {bind_cst(1 - cond.sign(), cg, frag), true};
|
|
}
|
|
cg.env().record_cached_cond(cond);
|
|
if (p->kind() == CG_Cond::CC_Reg) {
|
|
throw InternalError("_force_cond called on value in register.");
|
|
} else if (p->kind() == CG_Cond::CC_Call) {
|
|
CG_Cond::C_Call* call(static_cast<CG_Cond::C_Call*>(p));
|
|
std::vector<Type> ty(call->ty.begin() + 1, call->ty.end());
|
|
int r;
|
|
Mode m(call->m);
|
|
if (m.strength() != CG::Mode::Root) {
|
|
Mode call_m(m.strength(), negated);
|
|
auto fun = find_call_fun(cg, call->ident, call->ty[0], ty, call->m);
|
|
PUSH_INSTR(frag, BytecodeStream::CALL, std::get<1>(fun), std::get<0>(fun),
|
|
CG::i((!std::get<2>(fun)) && call->cse), call->params);
|
|
r = GET_REG(cg);
|
|
PUSH_INSTR(frag, BytecodeStream::POP, CG::r(r));
|
|
if (BytecodeProc::is_neg(call->m) != BytecodeProc::is_neg(std::get<1>(fun))) {
|
|
if (call->ty[0].ispar()) {
|
|
PUSH_INSTR(frag, BytecodeStream::NOT, CG::r(r), CG::r(r));
|
|
} else {
|
|
auto fun =
|
|
find_call_fun(cg, {"op_not"}, Type::varbool(), {Type::varbool()}, BytecodeProc::FUN);
|
|
assert(std::get<1>(fun) == BytecodeProc::FUN);
|
|
PUSH_INSTR(frag, BytecodeStream::CALL, BytecodeProc::FUN, std::get<0>(fun),
|
|
CG::i(!std::get<2>(fun)), CG::r(r));
|
|
PUSH_INSTR(frag, BytecodeStream::POP, CG::r(r));
|
|
}
|
|
}
|
|
return {r, call->ty[0].ispar()};
|
|
}
|
|
Mode call_m(m.strength(), negated);
|
|
assert(m == call_m);
|
|
auto fun = find_call_fun(cg, call->ident, call->ty[0], ty, call->m);
|
|
if (call_m == std::get<1>(fun)) {
|
|
PUSH_INSTR(frag, BytecodeStream::CALL, call_m, std::get<0>(fun),
|
|
CG::i((!std::get<2>(fun)) && call->cse), call->params);
|
|
} else {
|
|
assert(std::get<1>(fun) == BytecodeProc::FUN);
|
|
std::cerr << "Warning: emitting reification for " << call->ident
|
|
<< " because no ROOT_NEG version is available\n";
|
|
PUSH_INSTR(frag, BytecodeStream::CALL, std::get<1>(fun), std::get<0>(fun),
|
|
CG::i(!std::get<2>(fun)), call->params);
|
|
r = GET_REG(cg);
|
|
PUSH_INSTR(frag, BytecodeStream::POP, CG::r(r));
|
|
if (BytecodeProc::is_neg(call->m) != BytecodeProc::is_neg(std::get<1>(fun))) {
|
|
if (call->ty[0].ispar()) {
|
|
PUSH_INSTR(frag, BytecodeStream::NOT, CG::r(r), CG::r(r));
|
|
} else {
|
|
auto fun =
|
|
find_call_fun(cg, {"op_not"}, Type::varbool(), {Type::varbool()}, BytecodeProc::FUN);
|
|
assert(std::get<1>(fun) == BytecodeProc::FUN);
|
|
PUSH_INSTR(frag, BytecodeStream::CALL, BytecodeProc::FUN, std::get<0>(fun),
|
|
CG::i(!std::get<2>(fun)), CG::r(r));
|
|
PUSH_INSTR(frag, BytecodeStream::POP, CG::r(r));
|
|
}
|
|
}
|
|
PUSH_INSTR(frag, BytecodeStream::POST, CG::r(r));
|
|
}
|
|
return {bind_cst(1, cg, frag), true};
|
|
} else if (p->kind() == CG_Cond::CC_And && !cond.sign()) {
|
|
#ifndef MZNC_COLLECT_LEAVES
|
|
std::vector<int> var_leaves;
|
|
std::vector<int> par_leaves;
|
|
int r = GET_REG(cg);
|
|
// I don't think these are necessary
|
|
force_and_leaves(var_leaves, par_leaves, cond, cg, frag);
|
|
if (var_leaves.empty()) {
|
|
PUSH_INSTR(frag, BytecodeStream::MOV, CG::r(par_leaves[0]), CG::r(r));
|
|
for (int i = 1; i < par_leaves.size(); ++i) {
|
|
PUSH_INSTR(frag, BytecodeStream::AND, CG::r(r), CG::r(par_leaves[i]), CG::r(r));
|
|
}
|
|
} else {
|
|
OPEN_AND(cg, frag);
|
|
for (int r_c : par_leaves) {
|
|
PUSH_INSTR(frag, BytecodeStream::PUSH, CG::r(r_c));
|
|
}
|
|
for (int r_c : var_leaves) {
|
|
PUSH_INSTR(frag, BytecodeStream::PUSH, CG::r(r_c));
|
|
}
|
|
CLOSE_AGG(cg, frag);
|
|
PUSH_INSTR(frag, BytecodeStream::POP, CG::r(r));
|
|
}
|
|
return {r, var_leaves.empty()};
|
|
#else
|
|
std::vector<CG_Cond::T> par_leaves;
|
|
std::vector<CG_Cond::T> var_leaves;
|
|
collect_and_leaves(par_leaves, var_leaves, cond, cg, frag);
|
|
|
|
// If we had a conjunction, there should be at least two leaves.
|
|
assert(var_leaves.size() + par_leaves.size() > 1);
|
|
|
|
// If there is at least one par child, we're going to do shortcutting
|
|
// -- so we need a jump, and to push a fresh env.
|
|
int r = GET_REG(cg);
|
|
int lblE = 0xdeadbeef;
|
|
if (par_leaves.size() > 0) {
|
|
lblE = GET_LABEL(cg);
|
|
cg.env_push();
|
|
|
|
PUSH_INSTR(frag, BytecodeStream::IMMI, CG::i(0), CG::r(r));
|
|
for (int ii = 0; ii < par_leaves.size() - 1; ii++) {
|
|
CG_Cond::T c(par_leaves[ii]);
|
|
int r_c = CG::force(c, BytecodeProc::FUN, cg, frag);
|
|
PUSH_INSTR(frag, BytecodeStream::JMPIFNOT, CG::r(r_c), CG::l(lblE));
|
|
}
|
|
CG_Cond::T c(par_leaves.back());
|
|
int r_c = CG::force(c, BytecodeProc::FUN, cg, frag);
|
|
if (var_leaves.size() > 0) {
|
|
PUSH_INSTR(frag, BytecodeStream::JMPIFNOT, CG::r(r_c), CG::l(lblE));
|
|
} else {
|
|
PUSH_INSTR(frag, BytecodeStream::MOV, CG::r(r_c), CG::r(r));
|
|
}
|
|
}
|
|
// Excludes the 0 case, because it's just been handled.
|
|
if (var_leaves.size() == 1) {
|
|
// Exactly one var. Don't open a context;
|
|
// we return either the result register
|
|
// or the forced cond.
|
|
int r_c = CG::force(var_leaves[0], BytecodeProc::FUN, cg, frag);
|
|
PUSH_INSTR(frag, BytecodeStream::MOV, CG::r(r_c), CG::r(r));
|
|
} else if (var_leaves.size() > 1) {
|
|
// In this case, we need to open a context.
|
|
OPEN_AND(cg, frag);
|
|
for (CG_Cond::T c : var_leaves) {
|
|
int r_c = CG::force(c, BytecodeProc::FUN, cg, frag);
|
|
PUSH_INSTR(frag, BytecodeStream::PUSH, CG::r(r_c));
|
|
}
|
|
CLOSE_AGG(cg, frag);
|
|
PUSH_INSTR(frag, BytecodeStream::POP, CG::r(r));
|
|
}
|
|
if (par_leaves.size() > 0) { // We did some shortcutting.
|
|
PUSH_LABEL(frag, lblE);
|
|
cg.env_pop();
|
|
}
|
|
return {r, var_leaves.empty()};
|
|
#endif
|
|
} else {
|
|
assert(p->kind() == CG_Cond::CC_And && cond.sign());
|
|
#ifndef MZNC_COLLECT_LEAVES
|
|
std::vector<int> var_leaves;
|
|
std::vector<int> par_leaves;
|
|
int r = GET_REG(cg);
|
|
// I don't think these are necessary
|
|
force_or_leaves(var_leaves, par_leaves, ~cond, cg, frag);
|
|
if (var_leaves.empty()) {
|
|
PUSH_INSTR(frag, BytecodeStream::MOV, CG::r(par_leaves[0]), CG::r(r));
|
|
for (int i = 1; i < par_leaves.size(); ++i) {
|
|
PUSH_INSTR(frag, BytecodeStream::OR, CG::r(r), CG::r(par_leaves[i]), CG::r(r));
|
|
}
|
|
} else {
|
|
OPEN_OR(cg, frag);
|
|
for (int r_c : par_leaves) {
|
|
PUSH_INSTR(frag, BytecodeStream::PUSH, CG::r(r_c));
|
|
}
|
|
for (int r_c : var_leaves) {
|
|
PUSH_INSTR(frag, BytecodeStream::PUSH, CG::r(r_c));
|
|
}
|
|
CLOSE_AGG(cg, frag);
|
|
PUSH_INSTR(frag, BytecodeStream::POP, CG::r(r));
|
|
}
|
|
return {r, var_leaves.empty()};
|
|
#else
|
|
std::vector<CG_Cond::T> par_leaves;
|
|
std::vector<CG_Cond::T> var_leaves;
|
|
collect_or_leaves(par_leaves, var_leaves, ~cond, cg, frag);
|
|
|
|
// If we had a conjunction, there should be at least two leaves.
|
|
assert(var_leaves.size() + par_leaves.size() > 1);
|
|
|
|
// If there is at least one par child, we're going to do shortcutting
|
|
// -- so we need a jump, and to push a fresh env.
|
|
int r = GET_REG(cg);
|
|
int lblE = 0xdeadbeef;
|
|
if (par_leaves.size() > 0) {
|
|
lblE = GET_LABEL(cg);
|
|
cg.env_push();
|
|
PUSH_INSTR(frag, BytecodeStream::IMMI, CG::i(1), CG::r(r));
|
|
|
|
for (int ii = 0; ii < par_leaves.size() - 1; ii++) {
|
|
CG_Cond::T c(par_leaves[ii]);
|
|
int r_c = CG::force(c, BytecodeProc::FUN, cg, frag);
|
|
PUSH_INSTR(frag, BytecodeStream::JMPIF, CG::r(r_c), CG::l(lblE));
|
|
}
|
|
CG_Cond::T c(par_leaves.back());
|
|
int r_c = CG::force(c, BytecodeProc::FUN, cg, frag);
|
|
if (var_leaves.size() > 0) {
|
|
PUSH_INSTR(frag, BytecodeStream::JMPIF, CG::r(r_c), CG::l(lblE));
|
|
} else {
|
|
PUSH_INSTR(frag, BytecodeStream::MOV, CG::r(r_c), CG::r(r));
|
|
}
|
|
}
|
|
// Excludes the 0 case, because it's just been handled.
|
|
if (var_leaves.size() == 1) {
|
|
// Exactly one var. Don't open a context;
|
|
// we return either the result register
|
|
// or the forced cond.
|
|
int r_c = CG::force(var_leaves[0], BytecodeProc::FUN, cg, frag);
|
|
PUSH_INSTR(frag, BytecodeStream::MOV, CG::r(r_c), CG::r(r));
|
|
} else if (var_leaves.size() > 1) {
|
|
// In this case, we need to open a context.
|
|
OPEN_OR(cg, frag);
|
|
for (CG_Cond::T c : var_leaves) {
|
|
int r_c = CG::force(c, BytecodeProc::FUN, cg, frag);
|
|
PUSH_INSTR(frag, BytecodeStream::PUSH, CG::r(r_c));
|
|
}
|
|
CLOSE_AGG(cg, frag);
|
|
PUSH_INSTR(frag, BytecodeStream::POP, CG::r(r));
|
|
}
|
|
if (par_leaves.size() > 0) { // We did some shortcutting.
|
|
PUSH_LABEL(frag, lblE);
|
|
cg.env_pop();
|
|
}
|
|
return {r, var_leaves.empty()};
|
|
#endif
|
|
}
|
|
}
|
|
int CG::force(CG_Cond::T _cond, Mode ctx, CodeGen& cg, CG_Builder& frag) {
|
|
GCLock lock;
|
|
// Negate condition if forced in negated context
|
|
CG_Cond::T cond = ctx.is_neg() ? ~_cond : _cond;
|
|
// Check if the condition is already forced.
|
|
CG_Cond::_T* p(cond.get());
|
|
bool sign(cond.sign());
|
|
if (!p) {
|
|
return bind_cst(!sign, cg, frag);
|
|
}
|
|
if (p->reg[sign].has_reg()) {
|
|
return p->reg[sign].reg;
|
|
}
|
|
if (p->reg[1 - sign].has_reg()) {
|
|
// Emit the negation
|
|
int r(GET_REG(cg));
|
|
if (p->reg[1 - sign].is_par) {
|
|
PUSH_INSTR(frag, BytecodeStream::NOT, CG::r(p->reg[1 - sign].reg), CG::r(r));
|
|
} else {
|
|
auto fun =
|
|
find_call_fun(cg, {"op_not"}, Type::varbool(), {Type::varbool()}, BytecodeProc::FUN);
|
|
assert(std::get<1>(fun) == BytecodeProc::FUN);
|
|
PUSH_INSTR(frag, BytecodeStream::CALL, BytecodeProc::FUN, std::get<0>(fun),
|
|
CG::i(!std::get<2>(fun)), CG::r(p->reg[1 - sign].reg));
|
|
PUSH_INSTR(frag, BytecodeStream::POP, CG::r(r));
|
|
}
|
|
p->reg[sign] = CG_Cond::cond_reg(r, p->reg[1 - sign].is_par);
|
|
return r;
|
|
}
|
|
auto forced = _force_cond(cond, cg, frag);
|
|
p->reg[sign] = CG_Cond::cond_reg(forced.first, forced.second);
|
|
return forced.first;
|
|
}
|
|
|
|
CG::Binding CG::force_or_bind(Expression* e, Mode ctx, CodeGen& cg, CG_Builder& frag) {
|
|
if (e->type().isbool()) {
|
|
return {CG::force(CG::compile(e, cg, frag), ctx, cg, frag), CG_Cond::ttt()};
|
|
}
|
|
return CG::bind(e, cg, frag);
|
|
}
|
|
|
|
int CG::force_or_bind(Expression* e, Mode ctx, std::vector<CG_Cond::T>& cond, CodeGen& cg,
|
|
CG_Builder& frag) {
|
|
if (e->type().isbool()) {
|
|
return CG::force(CG::compile(e, cg, frag), ctx, cg, frag);
|
|
} else {
|
|
CG::Binding b(CG::bind(e, cg, frag));
|
|
if (b.second.get()) {
|
|
cond.push_back(b.second);
|
|
}
|
|
return b.first;
|
|
}
|
|
}
|
|
|
|
class EnvInit : public ItemVisitor {
|
|
private:
|
|
friend class ItemIter<EnvInit>;
|
|
|
|
EnvInit(CodeGen& _cg, Model& _m) : cg(_cg), m(_m) {}
|
|
|
|
/// Enter model
|
|
bool enterModel(Model* m) { return true; }
|
|
/// Enter item
|
|
bool enter(Item* m) { return true; }
|
|
/// Visit variable declaration
|
|
void vVarDeclI(VarDeclI* vdi) {
|
|
VarDecl* vd(vdi->e());
|
|
if (!vd->type().isann()) {
|
|
if (vd->type().ispar()) {
|
|
if (!vd->e()) {
|
|
// cg.env().bind(vd->id()->v(), Loc::global(cg.num_globals));
|
|
int g = cg.add_global(vd, true);
|
|
}
|
|
} else {
|
|
// If it's a var with a body, feed it into the mode analyser.
|
|
// TODO: Because mode analysis is not interprocedural, we have to
|
|
// assume global params may be used in any context.
|
|
modes.def(vd, BytecodeProc::ROOT);
|
|
modes.use(vd, BytecodeProc::FUN);
|
|
}
|
|
}
|
|
}
|
|
void vConstraintI(ConstraintI* c) { modes.use(c->e(), BytecodeProc::ROOT); }
|
|
/// Visit assign item
|
|
void vAssignI(AssignI* ass) {}
|
|
|
|
void vFunctionI(FunctionI* f) { cg.register_function(f); }
|
|
|
|
void vSolveI(SolveI* si) {
|
|
GCLock lock;
|
|
if (si->st() == SolveI::ST_SAT) {
|
|
return;
|
|
}
|
|
ASTString ident("solve_this");
|
|
int mode;
|
|
Expression* objective;
|
|
switch (si->st()) {
|
|
case SolveI::ST_SAT:
|
|
mode = 0;
|
|
objective = IntLit::a(0);
|
|
break;
|
|
case SolveI::ST_MIN:
|
|
mode = 1;
|
|
objective = si->e();
|
|
break;
|
|
case SolveI::ST_MAX:
|
|
mode = 2;
|
|
objective = si->e();
|
|
break;
|
|
}
|
|
Expression* search_a;
|
|
int search_var = 0;
|
|
int search_val = 0;
|
|
Call* c;
|
|
if (Call* ann = si->ann().getCall(ASTString("int_search"))) {
|
|
search_a = ann->arg(0);
|
|
Id* varsel = ann->arg(1)->cast<Id>();
|
|
if (varsel->idn() == -1 && varsel->v() == ASTString("input_order")) {
|
|
search_var = 1;
|
|
} else if (varsel->idn() == -1 && varsel->v() == ASTString("first_fail")) {
|
|
search_var = 2;
|
|
}
|
|
Id* valsel = ann->arg(2)->cast<Id>();
|
|
if (valsel->idn() == -1 && valsel->v() == ASTString("indomain_min")) {
|
|
search_val = 1;
|
|
} else if (valsel->idn() == -1 && valsel->v() == ASTString("indomain_max")) {
|
|
search_val = 2;
|
|
}
|
|
c = new Call(
|
|
si->loc(), ident,
|
|
{IntLit::a(mode), objective, search_a, IntLit::a(search_var), IntLit::a(search_val)});
|
|
} else {
|
|
c = new Call(si->loc(), ident, {IntLit::a(mode), objective});
|
|
}
|
|
c->type(Type::varbool());
|
|
m.addItem(new ConstraintI(Location().introduce(), c));
|
|
}
|
|
|
|
CodeGen& cg;
|
|
Model& m;
|
|
ModeAnalysis modes;
|
|
|
|
public:
|
|
static void run(CodeGen& cg, Model* m) {
|
|
EnvInit eb(cg, *m);
|
|
iterItems(eb, m);
|
|
cg.mode_map = std::move(eb.modes.extract());
|
|
}
|
|
};
|
|
|
|
struct ShowVal {
|
|
ShowVal(CodeGen& _cg, CG_Value _v) : cg(_cg), v(_v) {}
|
|
|
|
CodeGen& cg;
|
|
CG_Value v;
|
|
};
|
|
std::ostream& operator<<(std::ostream& o, ShowVal s) {
|
|
switch (s.v.kind) {
|
|
case CG_Value::V_Immi:
|
|
o << s.v.value;
|
|
break;
|
|
case CG_Value::V_Global:
|
|
o << s.v.value;
|
|
break;
|
|
case CG_Value::V_Reg:
|
|
o << "R" << s.v.value;
|
|
break;
|
|
case CG_Value::V_Proc:
|
|
o << "p" << s.v.value;
|
|
break;
|
|
case CG_Value::V_Label:
|
|
o << "l" << s.v.value;
|
|
}
|
|
return o;
|
|
}
|
|
|
|
template <class O>
|
|
void show_frag(O& out, CodeGen& cg, std::vector<CG_Instr>& frag) {
|
|
auto show = [&cg](CG_Value v) { return ShowVal(cg, v); };
|
|
|
|
int num_agg = 0;
|
|
for (CG_Instr& i : frag) {
|
|
auto op(static_cast<BytecodeStream::Instr>(i.tag >> 1));
|
|
if (op == BytecodeStream::CLOSE_AGGREGATION) {
|
|
num_agg--;
|
|
}
|
|
for (int j = 0; j < num_agg; ++j) {
|
|
out << " ";
|
|
}
|
|
if (i.tag & 1) {
|
|
out << "l" << (i.tag >> 1) << ": ";
|
|
continue;
|
|
}
|
|
|
|
out << instr_name(op);
|
|
switch (op) {
|
|
case BytecodeStream::OPEN_AGGREGATION:
|
|
out << " " << agg_name((AggregationCtx::Symbol)i.params[0].value);
|
|
num_agg++;
|
|
break;
|
|
case BytecodeStream::BUILTIN: {
|
|
CG_ProcID p(CG_ProcID::of_val(i.params[0]));
|
|
assert(p.is_builtin());
|
|
out << " " << cg._builtins[p.id()].first;
|
|
for (int ii = 1; ii < i.params.size(); ++ii) {
|
|
out << " " << show(i.params[ii]);
|
|
}
|
|
break;
|
|
}
|
|
case BytecodeStream::CALL: {
|
|
// Mode
|
|
out << " " << mode_name((BytecodeProc::Mode)i.params[0].value);
|
|
// Procedure
|
|
CG_ProcID p(CG_ProcID::of_val(i.params[1]));
|
|
if (p.is_builtin()) {
|
|
out << " " << cg._builtins[p.id()].first;
|
|
} else {
|
|
out << " " << cg.bytecode[p.id()].ident;
|
|
}
|
|
// CSE flag
|
|
out << " " << show(i.params[2]);
|
|
// Arguments
|
|
for (int ii = 3; ii < i.params.size(); ++ii) {
|
|
out << " " << show(i.params[ii]);
|
|
}
|
|
break;
|
|
}
|
|
case BytecodeStream::TCALL: {
|
|
// Mode
|
|
out << " " << mode_name((BytecodeProc::Mode)i.params[0].value);
|
|
// Procedure
|
|
CG_ProcID p(CG_ProcID::of_val(i.params[1]));
|
|
if (p.is_builtin()) {
|
|
out << " " << cg._builtins[p.id()].first;
|
|
} else {
|
|
out << " " << cg.bytecode[p.id()].ident;
|
|
}
|
|
// CSE flag
|
|
out << " " << show(i.params[2]);
|
|
break;
|
|
}
|
|
default:
|
|
for (CG_Value p : i.params) out << " " << show(p);
|
|
}
|
|
out << std::endl;
|
|
}
|
|
}
|
|
|
|
template <class O>
|
|
void show(O& out, CodeGen& cg) {
|
|
{
|
|
GCLock lock;
|
|
Model m;
|
|
for (auto g : cg.globals_env) {
|
|
if (g.second.second) {
|
|
TypeInst* ti = g.first->ti();
|
|
// TODO: This removes information from the origin model. Could be reverted after printing
|
|
if (ti->isarray()) {
|
|
std::vector<TypeInst*> ranges(ti->ranges().size());
|
|
for (int i = 0; i < ranges.size(); ++i) {
|
|
ranges[i] = new TypeInst(Location().introduce(), Type::parint());
|
|
}
|
|
auto nti = new TypeInst(Location().introduce(), ti->type(), ranges);
|
|
g.first->ti(nti);
|
|
} else if (ti->domain()) {
|
|
auto nti = new TypeInst(Location().introduce(), ti->type());
|
|
g.first->ti(nti);
|
|
}
|
|
g.first->ann().add(new Call(Location().introduce(), constants().ann.global_register,
|
|
{IntLit::a(g.second.first)}));
|
|
m.addItem(new VarDeclI(Location().introduce(), g.first));
|
|
}
|
|
}
|
|
for (auto fun : cg.req_solver_predicates) {
|
|
m.addItem(fun);
|
|
}
|
|
MiniZinc::Printer p(out, 0);
|
|
p.print(&m);
|
|
}
|
|
out << "@@@@@@@@@@" << std::endl;
|
|
for (auto b : cg._builtins) {
|
|
out << ":" << b.first << ": " << b.second << std::endl;
|
|
}
|
|
for (auto& p : cg.bytecode) {
|
|
for (BytecodeProc::Mode m : p) {
|
|
out << ":" << p.ident << ":" << mode_name(m) << " " << p.arity << std::endl;
|
|
show_frag(out, cg, p.body(m));
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
void post_cond(CodeGen& cg, CG_Builder& frag, CG_Cond::T cond) {
|
|
if(!cond.get()) {
|
|
assert(!cond.sign());
|
|
return;
|
|
}
|
|
|
|
std::vector<int> leaves;
|
|
force_and_leaves(leaves, cond, cg, frag);
|
|
for(int r_c : leaves)
|
|
PUSH_INSTR(frag, BytecodeStream::POST, CG::r(r_c));
|
|
}
|
|
*/
|
|
void post_cond(CodeGen& cg, CG_Builder& frag, CG_Cond::T cond) {
|
|
GCLock lock;
|
|
if (!cond.get()) {
|
|
if (cond.sign()) PUSH_INSTR(frag, BytecodeStream::POST, CG::r(bind_cst(0, cg, frag)));
|
|
return;
|
|
}
|
|
CG_Cond::_T* p(cond.get());
|
|
bool sign(cond.sign());
|
|
if (p->reg[sign].is_root) return;
|
|
if (p->reg[1 - sign].is_root) {
|
|
PUSH_INSTR(frag, BytecodeStream::POST, CG::r(bind_cst(0, cg, frag)));
|
|
return;
|
|
}
|
|
|
|
if (p->reg[sign].has_reg()) {
|
|
PUSH_INSTR(frag, BytecodeStream::POST, CG::r(p->reg[sign].reg));
|
|
} else if (p->reg[1 - sign].has_reg()) {
|
|
int r(GET_REG(cg));
|
|
if (p->reg[1 - sign].is_par) {
|
|
PUSH_INSTR(frag, BytecodeStream::NOT, CG::r(p->reg[1 - sign].reg), CG::r(r));
|
|
} else {
|
|
auto fun =
|
|
find_call_fun(cg, {"op_not"}, Type::varbool(), {Type::varbool()}, BytecodeProc::FUN);
|
|
assert(std::get<1>(fun) == BytecodeProc::FUN);
|
|
PUSH_INSTR(frag, BytecodeStream::CALL, BytecodeProc::FUN, std::get<0>(fun),
|
|
CG::i(!std::get<2>(fun)), CG::r(p->reg[1 - sign].reg));
|
|
PUSH_INSTR(frag, BytecodeStream::POP, CG::r(r));
|
|
}
|
|
PUSH_INSTR(frag, BytecodeStream::POST, CG::r(r));
|
|
p->reg[sign] = CG_Cond::cond_reg(r, p->reg[1 - sign].is_par);
|
|
} else if (p->kind() == CG_Cond::CC_Call) {
|
|
CG_Cond::C_Call* call(static_cast<CG_Cond::C_Call*>(p));
|
|
CG::Mode call_m(CG::Mode::Root, sign);
|
|
std::vector<Type> ty(call->ty.begin() + 1, call->ty.end());
|
|
auto fun = find_call_fun(cg, call->ident, call->ty[0], ty, call_m);
|
|
if (call_m == std::get<1>(fun)) {
|
|
PUSH_INSTR(frag, BytecodeStream::CALL, call_m, std::get<0>(fun),
|
|
CG::i((!std::get<2>(fun)) && call->cse), call->params);
|
|
} else {
|
|
assert(std::get<1>(fun) == BytecodeProc::FUN);
|
|
std::cerr << "Warning: emitting reification for " << call->ident
|
|
<< " because no ROOT_NEG version is available\n";
|
|
PUSH_INSTR(frag, BytecodeStream::CALL, std::get<1>(fun), std::get<0>(fun),
|
|
CG::i((!std::get<2>(fun)) && call->cse), call->params);
|
|
int r = GET_REG(cg);
|
|
PUSH_INSTR(frag, BytecodeStream::POP, CG::r(r));
|
|
if (BytecodeProc::is_neg(call->m) != BytecodeProc::is_neg(std::get<1>(fun))) {
|
|
if (call->ty[0].ispar()) {
|
|
PUSH_INSTR(frag, BytecodeStream::NOT, CG::r(r), CG::r(r));
|
|
} else {
|
|
auto fun =
|
|
find_call_fun(cg, {"op_not"}, Type::varbool(), {Type::varbool()}, BytecodeProc::FUN);
|
|
assert(std::get<1>(fun) == BytecodeProc::FUN);
|
|
PUSH_INSTR(frag, BytecodeStream::CALL, BytecodeProc::FUN, std::get<0>(fun),
|
|
CG::i(!std::get<2>(fun)), CG::r(r));
|
|
PUSH_INSTR(frag, BytecodeStream::POP, CG::r(r));
|
|
}
|
|
}
|
|
PUSH_INSTR(frag, BytecodeStream::POST, CG::r(r));
|
|
}
|
|
} else {
|
|
assert(p->kind() == CG_Cond::CC_And);
|
|
CG_Cond::C_And* conj(reinterpret_cast<CG_Cond::C_And*>(p));
|
|
if (!sign) {
|
|
// Recurse.
|
|
for (CG_Cond::T child : conj->children) post_cond(cg, frag, child);
|
|
} else {
|
|
// FIXME: Check force context.
|
|
PUSH_INSTR(frag, BytecodeStream::POST, CG::r(CG::force(cond, BytecodeProc::FUN, cg, frag)));
|
|
}
|
|
}
|
|
p->reg[sign].is_root = true;
|
|
}
|
|
|
|
// FIXME: This always forces calls outside the aggregation.
|
|
void aggregate_cond(CodeGen& cg, CG_Builder& frag, CG_Cond::T cond) {
|
|
CG_Cond::_T* p(cond.get());
|
|
bool sign(cond.sign());
|
|
if (!p) {
|
|
// int r_val(CG::locate_immi(1 - sign, cg, frag));
|
|
int r_val(bind_cst(1 - sign, cg, frag));
|
|
PUSH_INSTR(frag, BytecodeStream::PUSH, CG::r(r_val));
|
|
return;
|
|
}
|
|
if (p->reg[sign].has_reg()) {
|
|
PUSH_INSTR(frag, BytecodeStream::PUSH, CG::r(p->reg[sign].reg));
|
|
return;
|
|
} else if (p->reg[1 - sign].has_reg()) {
|
|
int r_neg(p->reg[1 - sign].reg);
|
|
if (p->reg[1 - sign].is_par) {
|
|
int r(GET_REG(cg));
|
|
PUSH_INSTR(frag, BytecodeStream::NOT, CG::r(r_neg), CG::r(r));
|
|
PUSH_INSTR(frag, BytecodeStream::PUSH, CG::r(r));
|
|
} else {
|
|
auto fun =
|
|
find_call_fun(cg, {"op_not"}, Type::varbool(), {Type::varbool()}, BytecodeProc::FUN);
|
|
assert(std::get<1>(fun) == BytecodeProc::FUN);
|
|
PUSH_INSTR(frag, BytecodeStream::CALL, BytecodeProc::FUN, std::get<0>(fun),
|
|
CG::i(!std::get<2>(fun)), CG::r(r_neg));
|
|
}
|
|
return;
|
|
}
|
|
std::vector<int> var_leaves;
|
|
std::vector<int> par_leaves;
|
|
if (p->kind() == CG_Cond::CC_And) {
|
|
if (!sign) {
|
|
force_and_leaves(var_leaves, par_leaves, cond, cg, frag);
|
|
int r;
|
|
if (var_leaves.empty()) {
|
|
r = GET_REG(cg);
|
|
PUSH_INSTR(frag, BytecodeStream::MOV, CG::r(par_leaves[0]), CG::r(r));
|
|
for (int i = 1; i < par_leaves.size(); ++i) {
|
|
PUSH_INSTR(frag, BytecodeStream::AND, CG::r(r), CG::r(par_leaves[i]), CG::r(r));
|
|
}
|
|
PUSH_INSTR(frag, BytecodeStream::PUSH, CG::r(r));
|
|
} else {
|
|
OPEN_AND(cg, frag);
|
|
for (int r_c : par_leaves) {
|
|
PUSH_INSTR(frag, BytecodeStream::PUSH, CG::r(r_c));
|
|
}
|
|
for (int r_c : var_leaves) {
|
|
PUSH_INSTR(frag, BytecodeStream::PUSH, CG::r(r_c));
|
|
}
|
|
CLOSE_AGG(cg, frag);
|
|
}
|
|
} else {
|
|
force_or_leaves(var_leaves, par_leaves, ~cond, cg, frag);
|
|
int r;
|
|
if (var_leaves.empty()) {
|
|
r = GET_REG(cg);
|
|
PUSH_INSTR(frag, BytecodeStream::MOV, CG::r(par_leaves[0]), CG::r(r));
|
|
for (int i = 1; i < par_leaves.size(); ++i) {
|
|
PUSH_INSTR(frag, BytecodeStream::OR, CG::r(r), CG::r(par_leaves[i]), CG::r(r));
|
|
}
|
|
PUSH_INSTR(frag, BytecodeStream::PUSH, CG::r(r));
|
|
} else {
|
|
OPEN_OR(cg, frag);
|
|
for (int r_c : par_leaves) {
|
|
PUSH_INSTR(frag, BytecodeStream::PUSH, CG::r(r_c));
|
|
}
|
|
for (int r_c : var_leaves) {
|
|
PUSH_INSTR(frag, BytecodeStream::PUSH, CG::r(r_c));
|
|
}
|
|
CLOSE_AGG(cg, frag);
|
|
}
|
|
}
|
|
} else {
|
|
// FIXME: Check force context
|
|
PUSH_INSTR(frag, BytecodeStream::PUSH, CG::r(CG::force(cond, BytecodeProc::FUN, cg, frag)));
|
|
}
|
|
}
|
|
|
|
int locate_range(int l, int u, CodeGen& cg, CG_Builder& frag) {
|
|
CG::Binding b;
|
|
if (cg.env().cache_lookup_range(l, u, b)) {
|
|
return b.first;
|
|
}
|
|
if (l == 0 && u == 1) {
|
|
PUSH_INSTR(frag, BytecodeStream::BUILTIN, cg.find_builtin("boolean_domain"));
|
|
int r = GET_REG(cg);
|
|
PUSH_INSTR(frag, BytecodeStream::POP, CG::r(r));
|
|
cg.env().cache_store_range(l, u, std::make_pair(r, CG_Cond::ttt()));
|
|
return r;
|
|
}
|
|
|
|
OPEN_VEC(cg, frag);
|
|
int r = GET_REG(cg);
|
|
PUSH_INSTR(frag, BytecodeStream::IMMI, CG::i(l), CG::r(r));
|
|
PUSH_INSTR(frag, BytecodeStream::PUSH, CG::r(r));
|
|
PUSH_INSTR(frag, BytecodeStream::IMMI, CG::i(u), CG::r(r));
|
|
PUSH_INSTR(frag, BytecodeStream::PUSH, CG::r(r));
|
|
CLOSE_AGG(cg, frag);
|
|
r = GET_REG(cg);
|
|
PUSH_INSTR(frag, BytecodeStream::POP, CG::r(r));
|
|
cg.env().cache_store_range(l, u, std::make_pair(r, CG_Cond::ttt()));
|
|
return r;
|
|
}
|
|
|
|
CG::Binding bind_domain(VarDecl* vd, CodeGen& cg, CG_Builder& frag) {
|
|
if (vd->type().bt() == Type::BT_BOOL) {
|
|
int r(GET_REG(cg));
|
|
PUSH_INSTR(frag, BytecodeStream::BUILTIN, cg.find_builtin("boolean_domain"));
|
|
PUSH_INSTR(frag, BytecodeStream::POP, CG::r(r));
|
|
return {r, CG_Cond::ttt()};
|
|
}
|
|
if (Expression* d = vd->ti()->domain()) {
|
|
// Ignoring partiality here.
|
|
return CG::bind(d, cg, frag);
|
|
}
|
|
int r(GET_REG(cg));
|
|
PUSH_INSTR(frag, BytecodeStream::BUILTIN, cg.find_builtin("infinite_domain"));
|
|
PUSH_INSTR(frag, BytecodeStream::POP, CG::r(r));
|
|
return {r, CG_Cond::ttt()};
|
|
}
|
|
|
|
class Compile : public ItemVisitor {
|
|
private:
|
|
friend class ItemIter<Compile>;
|
|
|
|
Compile(CodeGen& _cg) : cg(_cg) /*, bool_dom(-1) */ {
|
|
// Force the compilation of predicate definitions that can be generated by the compiler
|
|
find_call_fun(cg, {"op_not"}, Type::varbool(), {Type::varbool()}, BytecodeProc::FUN);
|
|
find_call_fun(cg, {"bool_clause"}, Type::varbool(), {Type::varbool(1), Type::varbool(1)},
|
|
BytecodeProc::ROOT, true);
|
|
find_call_fun(cg, {"bool_clause_reif"}, Type::varbool(),
|
|
{Type::varbool(1), Type::varbool(1), Type::varbool()}, BytecodeProc::ROOT, true);
|
|
find_call_fun(cg, {"int_lin_eq"}, Type::varbool(),
|
|
{Type::parint(1), Type::varint(1), Type::parint()}, BytecodeProc::ROOT, true);
|
|
}
|
|
|
|
/// Enter model
|
|
bool enterModel(Model* m) { return true; }
|
|
/// Enter item
|
|
bool enter(Item* m) { return true; }
|
|
/// Visit variable declaration
|
|
void vVarDeclI(VarDeclI* vdi) {
|
|
VarDecl* vd(vdi->e());
|
|
if (vd->type().isann()) return;
|
|
if (vd->type().isopt()) return;
|
|
if (vd->type().isvar()) {
|
|
// In whatever case, we're going to create something,
|
|
// and dump it in a register.
|
|
int r_var;
|
|
if (vd->e()) {
|
|
// Defined
|
|
// Evaluate the definition in root context,
|
|
// add it to a register
|
|
// r_var = CG::locate(vd->e(), BytecodeProc::ROOT, cg, root_frag);
|
|
CG::Binding b_var(CG::bind(vd->e(), cg, root_frag));
|
|
r_var = b_var.first;
|
|
// TODO: Special case for root stuff.
|
|
post_cond(cg, root_frag, b_var.second);
|
|
|
|
if (Expression* d = vd->ti()->domain()) {
|
|
if (!vd->ti()->isarray()) {
|
|
CG::Binding b_d = CG::bind(d, cg, root_frag);
|
|
int r_dp = GET_REG(cg);
|
|
PUSH_INSTR(root_frag, BytecodeStream::INTERSECT_DOMAIN, CG::r(r_var), CG::r(b_d.first),
|
|
CG::r(r_dp));
|
|
post_cond(cg, root_frag, b_d.second);
|
|
} else {
|
|
// Iterate over the elements of the variable we just created, and
|
|
// bind the corresponding domain.
|
|
CG::Binding b_d = CG::bind(d, cg, root_frag);
|
|
int r_dp = GET_REG(cg);
|
|
ITER_ARRAY(cg, root_frag, r_var, [b_d, r_dp](CodeGen& cg, CG_Builder& frag, int r_elt) {
|
|
PUSH_INSTR(frag, BytecodeStream::INTERSECT_DOMAIN, CG::r(r_elt), CG::r(b_d.first),
|
|
CG::r(r_dp));
|
|
});
|
|
post_cond(cg, root_frag, b_d.second);
|
|
}
|
|
}
|
|
} else {
|
|
CG::Binding b_d(bind_domain(vd, cg, root_frag));
|
|
post_cond(cg, root_frag, b_d.second);
|
|
int r_d = b_d.first;
|
|
|
|
r_var = GET_REG(cg);
|
|
if (vd->ti()->isarray()) {
|
|
// Open nested iterators
|
|
int rTMP(GET_REG(cg));
|
|
std::vector<int> r_regs;
|
|
for (Expression* r : vd->ti()->ranges()) {
|
|
Expression* dim(r->template cast<TypeInst>()->domain());
|
|
CG::Binding b_reg(CG::bind(dim, cg, root_frag));
|
|
post_cond(cg, root_frag, b_reg.second);
|
|
r_regs.push_back(b_reg.first);
|
|
}
|
|
std::vector<Forset> nesting;
|
|
OPEN_VEC(cg, root_frag);
|
|
for (int r_r : r_regs) {
|
|
Forset iter(cg, r_r);
|
|
nesting.push_back(iter);
|
|
iter.emit_pre(root_frag);
|
|
}
|
|
PUSH_INSTR(root_frag, BytecodeStream::CALL, BytecodeProc::ROOT,
|
|
cg.find_builtin("mk_intvar"), CG::i(0), CG::r(r_d));
|
|
for (int r_i = r_regs.size() - 1; r_i >= 0; --r_i) {
|
|
nesting[r_i].emit_post(root_frag);
|
|
}
|
|
CLOSE_AGG(cg, root_frag);
|
|
|
|
PUSH_INSTR(root_frag, BytecodeStream::POP, CG::r(r_var));
|
|
OPEN_VEC(cg, root_frag);
|
|
for (int r_r : r_regs) {
|
|
PUSH_INSTR(root_frag, BytecodeStream::GET_VEC, CG::r(r_r),
|
|
CG::r(bind_cst(1, cg, root_frag)), CG::r(rTMP));
|
|
PUSH_INSTR(root_frag, BytecodeStream::PUSH, CG::r(rTMP));
|
|
PUSH_INSTR(root_frag, BytecodeStream::GET_VEC, CG::r(r_r),
|
|
CG::r(bind_cst(2, cg, root_frag)), CG::r(rTMP));
|
|
PUSH_INSTR(root_frag, BytecodeStream::PUSH, CG::r(rTMP));
|
|
}
|
|
CLOSE_AGG(cg, root_frag);
|
|
PUSH_INSTR(root_frag, BytecodeStream::POP, CG::r(rTMP));
|
|
|
|
PUSH_INSTR(root_frag, BytecodeStream::BUILTIN, cg.find_builtin("array_Xd"), CG::r(r_var),
|
|
CG::r(rTMP));
|
|
} else {
|
|
PUSH_INSTR(root_frag, BytecodeStream::CALL, BytecodeProc::ROOT,
|
|
cg.find_builtin("mk_intvar"), CG::i(0), CG::r(r_d));
|
|
}
|
|
PUSH_INSTR(root_frag, BytecodeStream::POP, CG::r(r_var));
|
|
}
|
|
// Now copy it into a global, and add it to the env.
|
|
int g = cg.add_global(vd, false);
|
|
PUSH_INSTR(root_frag, BytecodeStream::STORE_GLOBAL, CG::r(r_var), CG::g(g));
|
|
|
|
// Since it's still in a register, add it to the current env as well.
|
|
cg.env().bind(vd->id()->str(), CG::Binding(r_var, CG_Cond::ttt()));
|
|
} else {
|
|
// For par identifiers with definitions, we evaluate them.
|
|
if (vd->e() && !vd->type().isann()) {
|
|
// Evaluate the definition.
|
|
// int r = vd->type().ispar() ? CG::locate_par(vd->e(), cg, root_frag) : CG::locate(vd->e(),
|
|
// BytecodeProc::ROOT, cg, root_frag);
|
|
int r;
|
|
if (vd->type().isbool()) {
|
|
r = CG::force(CG::compile(vd->e(), cg, root_frag), BytecodeProc::ROOT, cg, root_frag);
|
|
} else {
|
|
// Par expressions may still introduce constraints.
|
|
CG::Binding b_d = CG::bind(vd->e(), cg, root_frag);
|
|
post_cond(cg, root_frag, b_d.second);
|
|
r = b_d.first;
|
|
}
|
|
int g = cg.add_global(vd, false);
|
|
PUSH_INSTR(root_frag, BytecodeStream::STORE_GLOBAL, CG::r(r), CG::g(g));
|
|
|
|
cg.env().bind(vd->id()->str(), CG::Binding(r, CG_Cond::ttt()));
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Visit assign item
|
|
void vAssignI(AssignI* ass) {}
|
|
|
|
void vConstraintI(ConstraintI* c) {
|
|
// CG::eval(c->e(), BytecodeProc::ROOT, cg, root_frag);
|
|
post_cond(cg, root_frag, CG::compile(c->e(), cg, root_frag));
|
|
}
|
|
|
|
void vFunctionI(FunctionI* f) {
|
|
GCLock l;
|
|
if (f->ann().contains(constants().ann._export)) {
|
|
CG_ProcID body(cg.resolve_fun(f));
|
|
// Force the body to be created
|
|
if (!body.is_builtin()) {
|
|
assert(body.id() < cg.bytecode.size());
|
|
if (!cg.bytecode[body.id()].is_available(BytecodeProc::ROOT)) {
|
|
cg.bytecode[body.id()].body(BytecodeProc::ROOT);
|
|
cg.pending_bodies.push_back(
|
|
std::make_pair(f, std::make_pair(BytecodeProc::ROOT, BytecodeProc::ROOT)));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// FIXME: This method of saving the CodeGen state is pretty icky.
|
|
// CodeGen should probably be split into two objects.
|
|
void compile_fun(CG_Builder& frag, const ASTExprVec<VarDecl>& params, Expression* e) {
|
|
// Save the codegen state.
|
|
auto saved_env = cg.current_env;
|
|
cg.current_env = CG_Env<CG::Binding>::spawn(nullptr);
|
|
int saved_regs = cg.current_reg_count;
|
|
|
|
cg.current_reg_count = params.size();
|
|
|
|
cg._exp_scope.clear();
|
|
cg.mode_map.clear();
|
|
|
|
// Rerun mode analysis
|
|
ModeAnalysis modes;
|
|
// FIXME: Currently assuming all functions are total.
|
|
modes.def(e, BytecodeProc::ROOT);
|
|
modes.use(e, BytecodeProc::ROOT);
|
|
cg.mode_map = std::move(modes.extract());
|
|
|
|
// Set up the new env.
|
|
{
|
|
GCLock l;
|
|
for (int ii = 0; ii < params.size(); ++ii) {
|
|
cg.env().bind(params[ii]->id()->str(), CG::Binding(ii, CG_Cond::ttt()));
|
|
}
|
|
}
|
|
|
|
// Now compile the result.
|
|
CG::Binding b_res = CG::bind(e, cg, frag);
|
|
if (b_res.second.p) {
|
|
// FIXME: What actually happens with this forced result?
|
|
// FIXME: Check force context
|
|
CG::force(b_res.second, BytecodeProc::FUN, cg, frag);
|
|
}
|
|
PUSH_INSTR(frag, BytecodeStream::PUSH, CG::r(b_res.first));
|
|
PUSH_INSTR(frag, BytecodeStream::RET);
|
|
|
|
// now restore everything
|
|
cg.current_reg_count = saved_regs;
|
|
|
|
delete cg.current_env;
|
|
cg.current_env = saved_env;
|
|
}
|
|
|
|
void compile_pred(CG_Builder& frag, const ASTExprVec<VarDecl>& params, Mode m, Expression* e) {
|
|
// Save the codegen state.
|
|
auto saved_env = cg.current_env;
|
|
cg.current_env = CG_Env<CG::Binding>::spawn(nullptr);
|
|
int saved_regs = cg.current_reg_count;
|
|
|
|
cg.current_reg_count = params.size();
|
|
|
|
cg._exp_scope.clear();
|
|
cg.mode_map.clear();
|
|
|
|
// Rerun mode analysis
|
|
ModeAnalysis modes;
|
|
modes.use(e, m);
|
|
cg.mode_map = std::move(modes.extract());
|
|
|
|
// Set up the new env.
|
|
{
|
|
GCLock l;
|
|
for (int ii = 0; ii < params.size(); ++ii) {
|
|
cg.env().bind(params[ii]->id()->str(), CG::Binding(ii, CG_Cond::ttt()));
|
|
}
|
|
}
|
|
|
|
// Now compile the result.
|
|
CG_Cond::T cond(CG::compile(e, cg, frag));
|
|
if (m.strength() == Mode::Root) {
|
|
post_cond(cg, frag, m.is_neg() ? ~cond : cond);
|
|
} else {
|
|
aggregate_cond(cg, frag, m.is_neg() ? ~cond : cond);
|
|
}
|
|
PUSH_INSTR(frag, BytecodeStream::RET);
|
|
|
|
cg.current_reg_count = saved_regs;
|
|
// then restore everything.
|
|
delete cg.current_env;
|
|
cg.current_env = saved_env;
|
|
}
|
|
CodeGen& cg;
|
|
CG_Builder root_frag;
|
|
|
|
// int bool_dom;
|
|
public:
|
|
static void run(CodeGen& cg, Model* m) {
|
|
Compile c(cg);
|
|
// Compile the main model
|
|
OPEN_OTHER(cg, c.root_frag);
|
|
iterItems(c, m);
|
|
{
|
|
int old_reg_count = cg.current_reg_count;
|
|
cg.current_reg_count = cg.reg_trail.back();
|
|
cg.reg_trail.pop_back();
|
|
PUSH_INSTR(c.root_frag, BytecodeStream::CLEAR, CG::r(cg.current_reg_count),
|
|
CG::r(old_reg_count - 1));
|
|
cg.env_pop();
|
|
}
|
|
PUSH_INSTR(c.root_frag, BytecodeStream::RET);
|
|
|
|
// Now generate procedures for any necessary function/predicate bodies.
|
|
while (!cg.pending_bodies.empty()) {
|
|
GCLock lock;
|
|
auto p(cg.pending_bodies.back());
|
|
cg.pending_bodies.pop_back();
|
|
|
|
FunctionI* fun(p.first);
|
|
Mode call_mode(p.second.first);
|
|
Mode def_mode(p.second.second);
|
|
annotate_total(fun);
|
|
// Building structures
|
|
CG_ProcID proc(cg.resolve_fun(fun));
|
|
CG_Builder frag;
|
|
// Find the body.
|
|
std::vector<Type> arg_types(fun->params().size());
|
|
for (int j = 0; j < arg_types.size(); ++j) {
|
|
arg_types[j] = fun->params()[j]->type();
|
|
}
|
|
|
|
// Introduce reification if necessary
|
|
if (call_mode == BytecodeProc::IMP || call_mode == BytecodeProc::FUN ||
|
|
call_mode == BytecodeProc::IMP_NEG || call_mode == BytecodeProc::FUN_NEG) {
|
|
auto redef = cg.fun_map.defines_mode(fun->id(), arg_types, call_mode);
|
|
if (redef.first) {
|
|
std::vector<Expression*> args;
|
|
args.reserve(fun->params().size() + 1);
|
|
for (int i = 0; i < fun->params().size(); ++i) {
|
|
VarDecl* vd = fun->params()[i];
|
|
args.emplace_back(vd->id());
|
|
}
|
|
TypeInst var_bool(Location().introduce(), Type::varbool());
|
|
VarDecl new_var(Location().introduce(), &var_bool, "b");
|
|
args.emplace_back(new_var.id());
|
|
Call call(Location().introduce(), redef.second, args);
|
|
call.type(Type::varbool());
|
|
Let let(Location().introduce(), {&new_var, &call}, new_var.id());
|
|
let.type(Type::varbool());
|
|
let.addAnnotation(constants().ann.promise_total);
|
|
c.compile_pred(frag, fun->params(), call_mode, &let);
|
|
cg.append(proc.id(), call_mode, frag);
|
|
continue;
|
|
}
|
|
}
|
|
if (BytecodeProc::is_neg(call_mode)) {
|
|
GCLock lock;
|
|
ASTString ident(fun->id().str() + "_neg");
|
|
auto neg_bodies = std::move(cg.fun_map.get_bodies(ident, arg_types));
|
|
if (std::any_of(neg_bodies.begin(), neg_bodies.end(),
|
|
[](FunctionI* fi) { return fi->e(); })) {
|
|
std::vector<Expression*> args;
|
|
args.reserve(fun->params().size());
|
|
for (int i = 0; i < fun->params().size(); ++i) {
|
|
VarDecl* vd = fun->params()[i];
|
|
args.emplace_back(vd->id());
|
|
}
|
|
Call call(Location().introduce(), ident, args);
|
|
call.type(Type::varbool());
|
|
// Call negated constraint in positive context (implementation should deal with the
|
|
// negation).
|
|
c.compile_pred(frag, fun->params(), BytecodeProc::negate(call_mode), &call);
|
|
cg.append(proc.id(), call_mode, frag);
|
|
continue;
|
|
}
|
|
}
|
|
|
|
if (fun->e()) {
|
|
if (fun->e()->type().isbool()) {
|
|
c.compile_pred(frag, fun->params(), def_mode, fun->e());
|
|
} else {
|
|
assert(def_mode == BytecodeProc::ROOT);
|
|
c.compile_fun(frag, fun->params(), fun->e());
|
|
}
|
|
} else {
|
|
assert(call_mode == BytecodeProc::ROOT);
|
|
cg.req_solver_predicates.push_back(fun);
|
|
}
|
|
cg.append(proc.id(), call_mode, frag);
|
|
}
|
|
|
|
// And finally, add the entry function.
|
|
int main_proc = cg.bytecode.size();
|
|
cg.bytecode.emplace_back("main", 0);
|
|
cg.append(main_proc, BytecodeProc::ROOT, c.root_frag);
|
|
|
|
show(std::cout, cg);
|
|
}
|
|
};
|
|
|
|
Mode open_conj(Mode ctx, CG_Builder& frag) {
|
|
if (ctx == BytecodeProc::ROOT) return ctx;
|
|
|
|
PUSH_INSTR(frag, BytecodeStream::OPEN_AGGREGATION,
|
|
ctx.is_neg() ? AggregationCtx::VCTX_OR : AggregationCtx::VCTX_AND);
|
|
return +ctx;
|
|
}
|
|
void close_conj(Mode ctx, CG_Builder& frag) {
|
|
if (ctx != BytecodeProc::ROOT) PUSH_INSTR(frag, BytecodeStream::CLOSE_AGGREGATION);
|
|
}
|
|
Mode open_disj(Mode ctx, CG_Builder& frag) {
|
|
if (ctx == BytecodeProc::ROOT_NEG) return ctx;
|
|
|
|
PUSH_INSTR(frag, BytecodeStream::OPEN_AGGREGATION,
|
|
ctx.is_neg() ? AggregationCtx::VCTX_AND : AggregationCtx::VCTX_OR);
|
|
return +ctx;
|
|
}
|
|
void close_disj(Mode ctx, CG_Builder& frag) {
|
|
if (ctx != BytecodeProc::ROOT_NEG) PUSH_INSTR(frag, BytecodeStream::CLOSE_AGGREGATION);
|
|
}
|
|
|
|
Mode left_child_ctx(Mode ctx, BinOpType b) {
|
|
switch (b) {
|
|
// Only Boolean operators change the mode.
|
|
case BOT_AND:
|
|
return ctx.is_neg() ? +ctx : ctx;
|
|
case BOT_OR:
|
|
case BOT_RIMPL:
|
|
return ctx.is_neg() ? ctx : +ctx;
|
|
case BOT_IMPL:
|
|
return ctx.is_neg() ? -ctx : -(+ctx);
|
|
case BOT_EQUIV:
|
|
case BOT_XOR:
|
|
return *ctx;
|
|
default:
|
|
return ctx;
|
|
}
|
|
}
|
|
Mode right_child_ctx(Mode ctx, BinOpType b) {
|
|
switch (b) {
|
|
// Only Boolean operators change the mode.
|
|
case BOT_AND:
|
|
return ctx.is_neg() ? +ctx : ctx;
|
|
case BOT_OR:
|
|
case BOT_IMPL:
|
|
return ctx.is_neg() ? ctx : +ctx;
|
|
case BOT_RIMPL:
|
|
return ctx.is_neg() ? -ctx : -(+ctx);
|
|
case BOT_EQUIV:
|
|
case BOT_XOR:
|
|
return *ctx;
|
|
default:
|
|
return ctx;
|
|
}
|
|
}
|
|
Mode child_ctx(Mode ctx, UnOpType u) {
|
|
switch (u) {
|
|
case UOT_NOT:
|
|
return -ctx;
|
|
default:
|
|
return ctx;
|
|
}
|
|
}
|
|
|
|
void CG::run(CodeGen& cg, Model* m) {
|
|
// First, collect the model parameters, and assign them
|
|
// global slots.
|
|
EnvInit::run(cg, m);
|
|
Compile::run(cg, m);
|
|
}
|
|
|
|
// For a non-Boolean value, place it in a register and collect its partiality.
|
|
std::pair<int, CG_Cond::T> _bind(Expression* e, CodeGen& cg, CG_Builder& frag) {
|
|
// Look up the mode we need to compile e in.
|
|
// FIXME: Figure out why some expressions don't have a mode attached.
|
|
CG::Mode ctx(BytecodeProc::FUN);
|
|
try {
|
|
ctx = cg.mode_map.at(e);
|
|
} catch (const std::out_of_range& exn) {
|
|
}
|
|
|
|
switch (e->eid()) {
|
|
case Expression::E_INTLIT: {
|
|
// return std::make_pair(CG::locate_immi(e->template cast<IntLit>()->v().toInt(), cg, frag),
|
|
// CG_Cond::ttt());
|
|
IntVal i = e->cast<IntLit>()->v();
|
|
if (!i.isFinite()) {
|
|
int r(GET_REG(cg));
|
|
PUSH_INSTR(frag, BytecodeStream::BUILTIN, cg.find_builtin("infinity"),
|
|
CG::r(bind_cst(i.isPlusInfinity(), cg, frag)));
|
|
PUSH_INSTR(frag, BytecodeStream::POP, CG::r(r));
|
|
return {r, CG_Cond::ttt()};
|
|
}
|
|
return {bind_cst(i.toInt(), cg, frag), CG_Cond::ttt()};
|
|
}
|
|
case Expression::E_FLOATLIT:
|
|
// return std::make_pair(CG::locate_par(e->template cast<FloatLit>(), cg, frag), nullptr);
|
|
// NOT YET IMPLEMENTED
|
|
return CG::Binding(bind_cst(911911, cg, frag), CG_Cond::ttt());
|
|
case Expression::E_SETLIT:
|
|
return CG::bind(e->template cast<SetLit>(), ctx, cg, frag);
|
|
case Expression::E_BOOLLIT:
|
|
throw InternalError("bind called on Boolean expression.");
|
|
case Expression::E_STRINGLIT:
|
|
// NOT YET IMPLEMENTED
|
|
return CG::Binding(bind_cst(911911, cg, frag), CG_Cond::ttt());
|
|
case Expression::E_ID:
|
|
return CG::bind(e->template cast<Id>(), ctx, cg, frag);
|
|
case Expression::E_ANON:
|
|
throw InternalError("bind reached unexpected expression type: E_ANON.");
|
|
case Expression::E_ARRAYLIT:
|
|
return CG::bind(e->template cast<ArrayLit>(), ctx, cg, frag);
|
|
case Expression::E_ARRAYACCESS:
|
|
return CG::bind(e->template cast<ArrayAccess>(), ctx, cg, frag);
|
|
case Expression::E_COMP:
|
|
return CG::bind(e->template cast<Comprehension>(), ctx, cg, frag);
|
|
case Expression::E_ITE:
|
|
return CG::bind(e->template cast<ITE>(), ctx, cg, frag);
|
|
case Expression::E_BINOP:
|
|
return CG::bind(e->template cast<BinOp>(), ctx, cg, frag);
|
|
case Expression::E_UNOP:
|
|
return CG::bind(e->template cast<UnOp>(), ctx, cg, frag);
|
|
case Expression::E_CALL:
|
|
return CG::bind(e->template cast<Call>(), ctx, cg, frag);
|
|
case Expression::E_LET:
|
|
return CG::bind(e->template cast<Let>(), ctx, cg, frag);
|
|
case Expression::E_VARDECL:
|
|
case Expression::E_TI:
|
|
case Expression::E_TIID:
|
|
throw InternalError("Bytecode generator encountered unexpected expression type in bind.");
|
|
}
|
|
}
|
|
|
|
CG::Binding CG::bind(Expression* e, CodeGen& cg, CG_Builder& frag) {
|
|
try {
|
|
return cg.cache_lookup(e);
|
|
} catch (const CG_Env<CG::Binding>::NotFound& exn) {
|
|
CG::Binding b(_bind(e, cg, frag));
|
|
cg.cache_store(e, b);
|
|
return b;
|
|
}
|
|
// return _bind(e, cg, frag); // FIXME
|
|
}
|
|
|
|
//
|
|
int CG::locate_immi(int x, CodeGen& cg, CG_Builder& frag) {
|
|
int r(GET_REG(cg));
|
|
PUSH_INSTR(frag, BytecodeStream::IMMI, CG::i(x), CG::r(r));
|
|
return r;
|
|
}
|
|
|
|
// Modified version of execute-comprehension, executing an arbitrary callable on the innter
|
|
// expression.
|
|
template <class F>
|
|
void execute_comprehension_generic(Comprehension* c, Mode ctx, CodeGen& cg, CG_Builder& frag, F f) {
|
|
// Build up the object to build the generator.
|
|
std::vector<EmitPost*> nesting;
|
|
|
|
int depth = 0;
|
|
|
|
int g = c->n_generators();
|
|
int n_gen = c->n_generators();
|
|
for (int g = 0; g < n_gen; ++g) {
|
|
// Bind the in-expression to a register.
|
|
// assert(c->in(g)->type().ispar());
|
|
Expression* in(c->in(g));
|
|
// std::cout << "Binding R" << r << " to: "; debugprint(in);
|
|
// Open the bindings.
|
|
cg.env_push();
|
|
if (in->type().is_set()) {
|
|
int r(CG::bind(in, cg, frag).first);
|
|
assert(in->type().ispar());
|
|
for (int d = 0; d < c->n_decls(g); ++d) {
|
|
Forset* iter(new Forset(cg, r));
|
|
depth += 2; // Forset first iterates over the range, then values in the range.
|
|
nesting.push_back(iter);
|
|
iter->emit_pre(frag);
|
|
// Bind vd->id() to iter.val()
|
|
VarDecl* vd(c->decl(g, d));
|
|
ASTString id(vd->id()->str());
|
|
// cg.env().bind(id, Loc::reg(iter.val()));
|
|
cg.env().bind(id, CodeGen::Binding(iter->val(), CG_Cond::ttt()));
|
|
}
|
|
} else {
|
|
assert(in->type().isboolarray() || in->type().isintarray() || in->type().isintsetarray());
|
|
int r_arr(CG::bind(in, cg, frag).first);
|
|
for (int d = 0; d < c->n_decls(g); ++d) {
|
|
Foreach* iter(new Foreach(cg, r_arr));
|
|
nesting.push_back(iter);
|
|
iter->emit_pre(frag);
|
|
depth += 1;
|
|
|
|
VarDecl* vd(c->decl(g, d));
|
|
ASTString id(vd->id()->str());
|
|
cg.env().bind(id, CodeGen::Binding(iter->val(), CG_Cond::ttt()));
|
|
}
|
|
}
|
|
// Now emit the where-clause
|
|
Expression* where(c->where(g));
|
|
if (where) {
|
|
int lblCont(nesting.back()->cont());
|
|
assert(where->type().ispar());
|
|
int rC = CG::force(CG::compile(where, cg, frag), BytecodeProc::FUN, cg, frag);
|
|
PUSH_INSTR(frag, BytecodeStream::JMPIFNOT, CG::r(rC), CG::l(lblCont));
|
|
}
|
|
}
|
|
// We're now in the deepest scope. Generate code for the body.
|
|
f(c->e(), ctx, cg, frag, nesting.back()->cont(), depth);
|
|
|
|
// Now close the iterators _in reverse order_, and restore the environment.
|
|
for (int ii = nesting.size() - 1; ii >= 0; --ii) {
|
|
nesting[ii]->emit_post(frag);
|
|
delete nesting[ii];
|
|
}
|
|
for (int ii = 0; ii < n_gen; ++ii) cg.env_pop();
|
|
}
|
|
|
|
// Specialized version when we're binding and pushing the body.
|
|
void execute_comprehension_bind(Comprehension* c, Mode ctx, CodeGen& cg, CG_Builder& frag) {
|
|
execute_comprehension_generic(
|
|
c, ctx, cg, frag,
|
|
[](Expression* e, Mode ctx, CodeGen& cg, CG_Builder& frag, int lbl_cont, int depth) {
|
|
// Bind and push everything in the comprehension.
|
|
CG::Binding b_e = CG::force_or_bind(e, ctx, cg, frag);
|
|
PUSH_INSTR(frag, BytecodeStream::PUSH, CG::r(b_e.first)); // FIXME: Discarding partiality
|
|
});
|
|
}
|
|
|
|
// Special case implementation of folds where body is a generator.
|
|
CG_Cond::T eval_forall(Call* call, Mode ctx, CodeGen& cg, CG_Builder& frag) {
|
|
assert(call->n_args() == 1);
|
|
Expression* param = call->arg(0);
|
|
// Now check the expression's type. If it's an array literal or
|
|
// comprehension, we generate code directly, rather than generating
|
|
// a concrete vector.
|
|
switch (param->eid()) {
|
|
case Expression::E_ARRAYLIT: {
|
|
std::vector<CG_Cond::T> conj;
|
|
ArrayLit* a(param->cast<ArrayLit>());
|
|
int sz(a->size());
|
|
for (int ii = 0; ii < sz; ++ii) conj.push_back(CG::compile((*a)[ii], cg, frag));
|
|
return CG_Cond::forall(ctx, conj);
|
|
} break;
|
|
case Expression::E_COMP: {
|
|
Comprehension* c(param->cast<Comprehension>());
|
|
// Special cases: if we're in root, just post everything.
|
|
if (ctx == BytecodeProc::ROOT) {
|
|
execute_comprehension_generic(
|
|
param->cast<Comprehension>(), ctx, cg, frag,
|
|
[](Expression* elt, Mode ctx, CodeGen& cg, CG_Builder& frag, int lbl_cont, int depth) {
|
|
CG_Cond::T c = CG::compile(elt, cg, frag);
|
|
post_cond(cg, frag, c);
|
|
});
|
|
;
|
|
return CG_Cond::T::ttt();
|
|
}
|
|
// If it's par, do shortcutting.
|
|
if (c->e()->type().ispar()) {
|
|
int r(GET_REG(cg));
|
|
PUSH_INSTR(frag, BytecodeStream::IMMI, CG::i(1), CG::r(r));
|
|
execute_comprehension_generic(
|
|
c, ctx, cg, frag,
|
|
[r](Expression* elt, Mode ctx, CodeGen& cg, CG_Builder& frag, int lbl_cont, int depth) {
|
|
int r_cond = CG::force(CG::compile(elt, cg, frag), BytecodeProc::FUN, cg, frag);
|
|
PUSH_INSTR(frag, BytecodeStream::JMPIF, CG::r(r_cond), CG::l(lbl_cont));
|
|
PUSH_INSTR(frag, BytecodeStream::IMMI, CG::i(0), CG::r(r));
|
|
PUSH_INSTR(frag, BytecodeStream::ITER_BREAK, CG::i(depth));
|
|
});
|
|
;
|
|
return CG_Cond::reg(r, true);
|
|
} else {
|
|
// General case, just aggregate the values.
|
|
OPEN_AND(cg, frag);
|
|
execute_comprehension_generic(
|
|
c, ctx, cg, frag,
|
|
[](Expression* elt, Mode ctx, CodeGen& cg, CG_Builder& frag, int lbl_cont, int depth) {
|
|
int r_cond =
|
|
CG::force(CG::compile(elt, cg, frag), CG::Mode(ctx.strength(), false), cg, frag);
|
|
PUSH_INSTR(frag, BytecodeStream::PUSH, CG::r(r_cond));
|
|
});
|
|
CLOSE_AGG(cg, frag);
|
|
int r(GET_REG(cg));
|
|
PUSH_INSTR(frag, BytecodeStream::POP, CG::r(r));
|
|
return CG_Cond::reg(r, false);
|
|
}
|
|
}
|
|
default: {
|
|
int r(GET_REG(cg));
|
|
CG::Binding b_param(CG::bind(param, cg, frag));
|
|
int r_A(b_param.first);
|
|
std::vector<int> p_A;
|
|
if (!b_param.second.get()) {
|
|
if (b_param.second.sign()) return CG_Cond::T::fff();
|
|
} else {
|
|
// TODO: It's probably not quicker to split par/var since we need the AND Aggregation anyway
|
|
force_and_leaves(p_A, p_A, b_param.second, cg, frag);
|
|
}
|
|
OPEN_AND(cg, frag);
|
|
// First, push the constraints attached to A.
|
|
for (int r_c : p_A) {
|
|
PUSH_INSTR(frag, BytecodeStream::PUSH, CG::r(r_c));
|
|
}
|
|
ITER_ARRAY(cg, frag, r_A, [](CodeGen& cg, CG_Builder& frag, int r_elt) {
|
|
PUSH_INSTR(frag, BytecodeStream::PUSH, CG::r(r_elt));
|
|
});
|
|
CLOSE_AGG(cg, frag);
|
|
PUSH_INSTR(frag, BytecodeStream::POP, CG::r(r));
|
|
return CG_Cond::reg(r, call->type().ispar());
|
|
}
|
|
}
|
|
}
|
|
// Same as forall, but producing an or-context.
|
|
CG_Cond::T eval_exists(Call* call, Mode ctx, CodeGen& cg, CG_Builder& frag) {
|
|
assert(call->n_args() == 1);
|
|
Expression* param = call->arg(0);
|
|
|
|
// Now check the expression's type. If it's an array literal or
|
|
// comprehension, we generate code directly, rather than generating
|
|
// a concrete vector.
|
|
switch (param->eid()) {
|
|
case Expression::E_ARRAYLIT: {
|
|
std::vector<CG_Cond::T> disj;
|
|
ArrayLit* a(param->cast<ArrayLit>());
|
|
int sz(a->size());
|
|
for (int ii = 0; ii < sz; ++ii) {
|
|
CG_Cond::T elt(CG::compile((*a)[ii], cg, frag));
|
|
disj.push_back(elt);
|
|
}
|
|
return CG_Cond::exists(ctx, disj);
|
|
} break;
|
|
/*
|
|
case Expression::E_COMP: {
|
|
Comprehension* c(param->cast<Comprehension>());
|
|
execute_comprehension(c, c_ctx, cg, frag);
|
|
}
|
|
break;
|
|
*/
|
|
#if 1
|
|
case Expression::E_COMP: {
|
|
Comprehension* c(param->cast<Comprehension>());
|
|
if (c->e()->type().ispar()) {
|
|
int r(GET_REG(cg));
|
|
PUSH_INSTR(frag, BytecodeStream::IMMI, CG::i(0), CG::r(r));
|
|
execute_comprehension_generic(
|
|
c, ctx, cg, frag,
|
|
[r](Expression* elt, Mode ctx, CodeGen& cg, CG_Builder& frag, int lbl_cont, int depth) {
|
|
int r_cond = CG::force(CG::compile(elt, cg, frag), BytecodeProc::FUN, cg, frag);
|
|
PUSH_INSTR(frag, BytecodeStream::JMPIFNOT, CG::r(r_cond), CG::l(lbl_cont));
|
|
PUSH_INSTR(frag, BytecodeStream::IMMI, CG::i(1), CG::r(r));
|
|
PUSH_INSTR(frag, BytecodeStream::ITER_BREAK, CG::i(depth));
|
|
});
|
|
return CG_Cond::reg(r, true);
|
|
} else {
|
|
OPEN_OR(cg, frag);
|
|
execute_comprehension_generic(
|
|
c, ctx, cg, frag,
|
|
[](Expression* elt, Mode ctx, CodeGen& cg, CG_Builder& frag, int lbl_cont, int depth) {
|
|
int r_cond =
|
|
CG::force(CG::compile(elt, cg, frag), CG::Mode(ctx.strength(), false), cg, frag);
|
|
PUSH_INSTR(frag, BytecodeStream::PUSH, CG::r(r_cond));
|
|
});
|
|
CLOSE_AGG(cg, frag);
|
|
int r(GET_REG(cg));
|
|
PUSH_INSTR(frag, BytecodeStream::POP, CG::r(r));
|
|
return CG_Cond::reg(r, false);
|
|
}
|
|
} // Fallthrough
|
|
#endif
|
|
default: {
|
|
int r(GET_REG(cg));
|
|
// Otherwise, get the result into a register...
|
|
CG::Binding b_A(CG::bind(param, cg, frag));
|
|
int r_A(b_A.first);
|
|
// and push every element.
|
|
OPEN_OR(cg, frag);
|
|
ITER_ARRAY(cg, frag, r_A, [](CodeGen& cg, CG_Builder& frag, int r_elt) {
|
|
PUSH_INSTR(frag, BytecodeStream::PUSH, CG::r(r_elt));
|
|
});
|
|
CLOSE_AGG(cg, frag);
|
|
PUSH_INSTR(frag, BytecodeStream::POP, CG::r(r));
|
|
return CG_Cond::forall(ctx, b_A.second, CG_Cond::reg(r, call->type().ispar()));
|
|
}
|
|
}
|
|
}
|
|
|
|
CG_Cond::T eval_isfixed_b(Call* call, Mode ctx, CodeGen& cg, CG_Builder& frag) {
|
|
assert(call->n_args() == 1);
|
|
Expression* e(call->arg(0));
|
|
|
|
if (e->type().ispar()) return CG_Cond::T::ttt();
|
|
|
|
int r_e;
|
|
if (e->type().isbool()) {
|
|
CG_Cond::T b_e(CG::compile(e, cg, frag));
|
|
if (!b_e.get()) {
|
|
return CG_Cond::T::ttt();
|
|
}
|
|
r_e = CG::force(b_e, ctx, cg, frag);
|
|
} else {
|
|
CG::Binding b(CG::bind(e, cg, frag));
|
|
r_e = b.first;
|
|
}
|
|
int r(GET_REG(cg));
|
|
PUSH_INSTR(frag, BytecodeStream::ISPAR, CG::r(r_e), CG::r(r));
|
|
return CG_Cond::reg(r, true);
|
|
}
|
|
|
|
CG::Binding bind_fix(Call* call, Mode ctx, CodeGen& cg, CG_Builder& frag) {
|
|
assert(call->n_args() == 1);
|
|
|
|
Expression* arg(call->arg(0));
|
|
CG::Binding b_arg(CG::bind(arg, cg, frag));
|
|
if (arg->type().ispar()) {
|
|
return b_arg;
|
|
} else if (arg->type().isintarray() || arg->type().isboolarray()) {
|
|
int r_cond(GET_REG(cg));
|
|
// Init the condition
|
|
PUSH_INSTR(frag, BytecodeStream::ISPAR, CG::r(b_arg.first), CG::r(r_cond));
|
|
|
|
// Gather elements
|
|
OPEN_VEC(cg, frag);
|
|
ITER_ARRAY(cg, frag, b_arg.first, [](CodeGen& cg, CG_Builder& frag, int r_elt) {
|
|
PUSH_INSTR(frag, BytecodeStream::LB, CG::r(r_elt), CG::r(r_elt));
|
|
PUSH_INSTR(frag, BytecodeStream::PUSH, CG::r(r_elt));
|
|
});
|
|
CLOSE_AGG(cg, frag);
|
|
int r_elts(GET_REG(cg));
|
|
PUSH_INSTR(frag, BytecodeStream::POP, CG::r(r_elts));
|
|
|
|
// Ensure resulting array has the same index set
|
|
PUSH_INSTR(frag, BytecodeStream::BUILTIN, cg.find_builtin("index_set"), CG::r(b_arg.first),
|
|
CG::r(bind_cst(0, cg, frag)));
|
|
int r_indxs(GET_REG(cg));
|
|
PUSH_INSTR(frag, BytecodeStream::POP, CG::r(r_indxs));
|
|
|
|
PUSH_INSTR(frag, BytecodeStream::BUILTIN, cg.find_builtin("array_Xd"), CG::r(r_elts),
|
|
CG::r(r_indxs));
|
|
PUSH_INSTR(frag, BytecodeStream::POP, CG::r(r_elts));
|
|
|
|
return {r_elts, CG_Cond::forall(ctx, b_arg.second, CG_Cond::reg(r_cond, true))};
|
|
} else {
|
|
int r(GET_REG(cg));
|
|
int r_cond(GET_REG(cg));
|
|
|
|
PUSH_INSTR(frag, BytecodeStream::ISPAR, CG::r(b_arg.first), CG::r(r_cond));
|
|
PUSH_INSTR(frag, BytecodeStream::LB, CG::r(b_arg.first), CG::r(r));
|
|
|
|
return {r, CG_Cond::forall(ctx, b_arg.second, CG_Cond::reg(r_cond, true))};
|
|
}
|
|
}
|
|
|
|
CG_Cond::T eval_fix(Call* call, Mode ctx, CodeGen& cg, CG_Builder& frag) {
|
|
assert(call->n_args() == 1);
|
|
|
|
// Should we check that the argument is actually fixed??
|
|
int r = CG::force(CG::compile(call->arg(0), cg, frag), ctx, cg, frag);
|
|
return CG_Cond::reg(r, true);
|
|
}
|
|
|
|
CG_Cond::T eval_error_b(Call* call, Mode ctx, CodeGen& cg, CG_Builder& frag) {
|
|
throw InternalError("Call should only appear in general context.");
|
|
return CG_Cond::ttt();
|
|
}
|
|
CG::Binding bind_error_g(Call* call, Mode ctx, CodeGen& cg, CG_Builder& frag) {
|
|
throw InternalError("Call should only appear in Boolean context.");
|
|
}
|
|
CG::Binding bind_sum(Call* call, Mode ctx, CodeGen& cg, CG_Builder& frag) {
|
|
GCLock lock;
|
|
assert(call->n_args() == 1);
|
|
Expression* e = call->arg(0);
|
|
// Components of the sum may be partial.
|
|
// TODO: Specialise for literals and comprehensions.
|
|
CG::Binding b_elts(CG::bind(e, cg, frag));
|
|
int r_one(bind_cst(1, cg, frag));
|
|
|
|
if (e->type().ispar()) {
|
|
int r_sum(GET_REG(cg));
|
|
PUSH_INSTR(frag, BytecodeStream::IMMI, CG::i(0), CG::r(r_sum));
|
|
ITER_ARRAY(cg, frag, b_elts.first, [&](CodeGen& cg, CG_Builder& frag, int r_elt) {
|
|
PUSH_INSTR(frag, BytecodeStream::ADDI, CG::r(r_sum), CG::r(r_elt), CG::r(r_sum));
|
|
});
|
|
return CG::Binding(r_sum, b_elts.second);
|
|
}
|
|
|
|
int r_sz(GET_REG(cg));
|
|
int r_ret(GET_REG(cg));
|
|
|
|
int l_eq0(GET_LABEL(cg));
|
|
int l_eq1(GET_LABEL(cg));
|
|
int l_end(GET_LABEL(cg));
|
|
|
|
PUSH_INSTR(frag, BytecodeStream::LENGTH, CG::r(b_elts.first), CG::r(r_sz));
|
|
PUSH_INSTR(frag, BytecodeStream::JMPIFNOT, CG::r(r_sz), CG::l(l_eq0));
|
|
PUSH_INSTR(frag, BytecodeStream::EQI, CG::r(r_one), CG::r(r_sz), CG::r(r_ret));
|
|
PUSH_INSTR(frag, BytecodeStream::JMPIF, CG::r(r_ret), CG::l(l_eq1));
|
|
|
|
// Sum n arguments
|
|
auto fun = find_call_fun(cg, {"sum_cc"}, Type::varint(), {Type::varint(1)}, BytecodeProc::FUN);
|
|
assert(std::get<1>(fun) == BytecodeProc::FUN);
|
|
PUSH_INSTR(frag, BytecodeStream::CALL, BytecodeProc::FUN, std::get<0>(fun),
|
|
CG::i(!std::get<2>(fun)), CG::r(b_elts.first));
|
|
PUSH_INSTR(frag, BytecodeStream::POP, CG::r(r_ret));
|
|
PUSH_INSTR(frag, BytecodeStream::JMP, CG::l(l_end));
|
|
|
|
// Sum zero arguments (result must be 0)
|
|
PUSH_LABEL(frag, l_eq0);
|
|
PUSH_INSTR(frag, BytecodeStream::IMMI, CG::i(0), CG::r(r_ret));
|
|
PUSH_INSTR(frag, BytecodeStream::JMP, CG::l(l_end));
|
|
|
|
// Sum 1 arguments (result == a[1])
|
|
PUSH_LABEL(frag, l_eq1);
|
|
PUSH_INSTR(frag, BytecodeStream::GET_VEC, CG::r(b_elts.first), CG::r(r_one), CG::r(r_ret));
|
|
|
|
// End of sum
|
|
PUSH_LABEL(frag, l_end);
|
|
return CG::Binding(r_ret, b_elts.second);
|
|
}
|
|
|
|
CG::Binding bind_set2array(Call* call, Mode ctx, CodeGen& cg, CG_Builder& frag) {
|
|
assert(call->n_args() == 1);
|
|
// Bind set
|
|
Expression* e = call->arg(0);
|
|
CG::Binding b_elts(CG::bind(e, cg, frag));
|
|
|
|
// Create array
|
|
|
|
OPEN_VEC(cg, frag);
|
|
ITER_SET(cg, frag, b_elts.first, [](CodeGen& cg, CG_Builder& frag, int r_elt) {
|
|
PUSH_INSTR(frag, BytecodeStream::PUSH, CG::r(r_elt));
|
|
});
|
|
CLOSE_AGG(cg, frag);
|
|
int r_ret(GET_REG(cg));
|
|
PUSH_INSTR(frag, BytecodeStream::POP, CG::r(r_ret));
|
|
|
|
return {r_ret, b_elts.second};
|
|
}
|
|
|
|
CG::Binding bind_dom_bounds_array(Call* call, Mode ctx, CodeGen& cg, CG_Builder& frag) {
|
|
assert(call->n_args() == 1);
|
|
CG::Binding b_A(CG::bind(call->arg(0), cg, frag));
|
|
|
|
// Open the context here, so we can recover all the registers after.
|
|
OPEN_VEC(cg, frag);
|
|
|
|
int r_lb(GET_REG(cg));
|
|
PUSH_INSTR(frag, BytecodeStream::BUILTIN, cg.find_builtin("infinity"),
|
|
CG::r(bind_cst(1, cg, frag)));
|
|
PUSH_INSTR(frag, BytecodeStream::POP, CG::r(r_lb));
|
|
|
|
int r_ub(GET_REG(cg));
|
|
PUSH_INSTR(frag, BytecodeStream::BUILTIN, cg.find_builtin("infinity"),
|
|
CG::r(bind_cst(0, cg, frag)));
|
|
PUSH_INSTR(frag, BytecodeStream::POP, CG::r(r_ub));
|
|
|
|
// Do the iteration
|
|
int r_test(GET_REG(cg));
|
|
int r_bound(GET_REG(cg));
|
|
|
|
int l_lb(GET_LABEL(cg));
|
|
int l_ub(GET_LABEL(cg));
|
|
// Body
|
|
ITER_ARRAY(cg, frag, b_A.first, [&](CodeGen& cg, CG_Builder& frag, int r_elt) {
|
|
PUSH_INSTR(frag, BytecodeStream::LB, CG::r(r_elt), CG::r(r_bound));
|
|
PUSH_INSTR(frag, BytecodeStream::LEI, CG::r(r_lb), CG::r(r_bound), CG::r(r_test));
|
|
PUSH_INSTR(frag, BytecodeStream::JMPIF, CG::r(r_test), CG::l(l_lb));
|
|
PUSH_INSTR(frag, BytecodeStream::MOV, CG::r(r_bound), CG::r(r_lb));
|
|
PUSH_LABEL(frag, l_lb);
|
|
PUSH_INSTR(frag, BytecodeStream::UB, CG::r(r_elt), CG::r(r_bound));
|
|
PUSH_INSTR(frag, BytecodeStream::LEI, CG::r(r_bound), CG::r(r_ub), CG::r(r_test));
|
|
PUSH_INSTR(frag, BytecodeStream::JMPIF, CG::r(r_test), CG::l(l_ub));
|
|
PUSH_INSTR(frag, BytecodeStream::MOV, CG::r(r_bound), CG::r(r_ub));
|
|
PUSH_LABEL(frag, l_ub);
|
|
});
|
|
|
|
// Now collect the elements
|
|
PUSH_INSTR(frag, BytecodeStream::PUSH, CG::r(r_lb));
|
|
PUSH_INSTR(frag, BytecodeStream::PUSH, CG::r(r_ub));
|
|
CLOSE_AGG(cg, frag);
|
|
int r(GET_REG(cg));
|
|
PUSH_INSTR(frag, BytecodeStream::POP, CG::r(r));
|
|
return CG::Binding(r, b_A.second);
|
|
}
|
|
|
|
CG_Cond::T eval_assert_b(Call* call, Mode ctx, CodeGen& cg, CG_Builder& frag) {
|
|
assert(call->arg(0)->type().ispar());
|
|
// Evaluate the assertion, abort if it fails.
|
|
/*
|
|
int r_cond = CG::force(CG::compile(call->arg(0), cg, frag), cg, frag);
|
|
int l_okay(GET_LABEL(cg));
|
|
PUSH_INSTR(frag, BytecodeStream::JMPIF, CG::r(r_cond), CG::l(l_okay));
|
|
PUSH_INSTR(frag, BytecodeStream::ABORT);
|
|
PUSH_LABEL(frag, l_okay);
|
|
*/
|
|
// Otherwise, evaluate as usual.
|
|
if (call->n_args() == 2) {
|
|
return CG_Cond::ttt();
|
|
} else {
|
|
assert(call->n_args() == 3);
|
|
return CG::compile(call->arg(2), cg, frag);
|
|
}
|
|
}
|
|
|
|
CG::Binding bind_assert_g(Call* call, Mode ctx, CodeGen& cg, CG_Builder& frag) {
|
|
assert(call->n_args() == 3);
|
|
assert(call->arg(0)->type().ispar());
|
|
/*
|
|
int r_cond = CG::force(CG::compile(call->arg(0), cg, frag), cg, frag);
|
|
int l_okay(GET_LABEL(cg));
|
|
PUSH_INSTR(frag, BytecodeStream::JMPIF, CG::r(r_cond), CG::l(l_okay));
|
|
PUSH_INSTR(frag, BytecodeStream::ABORT);
|
|
PUSH_LABEL(frag, l_okay);
|
|
*/
|
|
return CG::bind(call->arg(2), cg, frag);
|
|
}
|
|
|
|
CG::Binding bind_arrayXd(Call* call, Mode ctx, CodeGen& cg, CG_Builder& frag) {
|
|
assert(call->n_args() == 2);
|
|
CG::Binding orig(CG::bind(call->arg(0), cg, frag));
|
|
CG::Binding target(CG::bind(call->arg(1), cg, frag));
|
|
|
|
// Get index sets from orig
|
|
int r(GET_REG(cg));
|
|
PUSH_INSTR(frag, BytecodeStream::BUILTIN, cg.find_builtin("index_set"), CG::r(orig.first),
|
|
CG::r(bind_cst(0, cg, frag)));
|
|
PUSH_INSTR(frag, BytecodeStream::POP, CG::r(r));
|
|
|
|
// Create new array
|
|
PUSH_INSTR(frag, BytecodeStream::BUILTIN, cg.find_builtin("array_Xd"), CG::r(target.first),
|
|
CG::r(r));
|
|
PUSH_INSTR(frag, BytecodeStream::POP, CG::r(r));
|
|
|
|
return {r, CG_Cond::forall(ctx, orig.second, target.second)};
|
|
}
|
|
|
|
template <int N>
|
|
CG::Binding bind_arrayNd(Call* call, Mode ctx, CodeGen& cg, CG_Builder& frag) {
|
|
assert(call->n_args() == N + 1);
|
|
std::vector<CG_Cond::T> cond;
|
|
|
|
// Bind all index sets
|
|
std::vector<int> r_index(N);
|
|
for (int ii = 0; ii < N; ++ii) {
|
|
CG::Binding b(CG::bind(call->arg(ii), cg, frag));
|
|
r_index[ii] = b.first;
|
|
cond.push_back(b.second);
|
|
}
|
|
|
|
// Bind array expression
|
|
CG::Binding arr = CG::bind(call->arg(N), cg, frag);
|
|
|
|
int rI(GET_REG(cg));
|
|
OPEN_VEC(cg, frag);
|
|
for (int ii = 0; ii < N; ++ii) {
|
|
// TODO: Ensure the index set is a range (2-elements)
|
|
PUSH_INSTR(frag, BytecodeStream::GET_VEC, CG::r(r_index[ii]), CG::r(bind_cst(1, cg, frag)),
|
|
CG::r(rI));
|
|
PUSH_INSTR(frag, BytecodeStream::PUSH, CG::r(rI));
|
|
PUSH_INSTR(frag, BytecodeStream::GET_VEC, CG::r(r_index[ii]), CG::r(bind_cst(2, cg, frag)),
|
|
CG::r(rI));
|
|
PUSH_INSTR(frag, BytecodeStream::PUSH, CG::r(rI));
|
|
}
|
|
CLOSE_AGG(cg, frag);
|
|
PUSH_INSTR(frag, BytecodeStream::POP, CG::r(rI));
|
|
|
|
PUSH_INSTR(frag, BytecodeStream::BUILTIN, cg.find_builtin("array_Xd"), CG::r(arr.first),
|
|
CG::r(rI));
|
|
PUSH_INSTR(frag, BytecodeStream::POP, CG::r(rI));
|
|
|
|
return {rI, CG_Cond::forall(ctx, cond)};
|
|
}
|
|
|
|
CG::Binding bind_array1d(Call* call, Mode ctx, CodeGen& cg, CG_Builder& frag) {
|
|
if (call->n_args() == 1) {
|
|
int rA(GET_REG(cg));
|
|
CG::Binding arr = CG::bind(call->arg(0), cg, frag);
|
|
|
|
PUSH_INSTR(frag, BytecodeStream::BUILTIN, cg.find_builtin("array_Xd"), CG::r(arr.first),
|
|
CG::r(bind_cst(0, cg, frag)));
|
|
PUSH_INSTR(frag, BytecodeStream::POP, CG::r(rA));
|
|
|
|
return {rA, arr.second};
|
|
} else {
|
|
// Index set given by the user
|
|
assert(call->n_args() == 2);
|
|
return bind_arrayNd<1>(call, ctx, cg, frag);
|
|
}
|
|
}
|
|
|
|
CG::Binding bind_array_union(Call* call, Mode ctx, CodeGen& cg, CG_Builder& frag) {
|
|
assert(call->n_args() == 1);
|
|
Expression* e = call->arg(0);
|
|
// Components of the sum may be partial.
|
|
// TODO: Specialise for literals and comprehensions.
|
|
CG::Binding b_elts(CG::bind(e, cg, frag));
|
|
|
|
assert(e->type().ispar()); // TODO: var case
|
|
int r_union(GET_REG(cg));
|
|
OPEN_VEC(cg, frag);
|
|
CLOSE_AGG(cg, frag);
|
|
PUSH_INSTR(frag, BytecodeStream::POP, CG::r(r_union));
|
|
|
|
ITER_ARRAY(cg, frag, b_elts.first, [&](CodeGen& cg, CG_Builder& frag, int r_elt) {
|
|
PUSH_INSTR(frag, BytecodeStream::UNION, CG::r(r_union), CG::r(r_elt), CG::r(r_union));
|
|
});
|
|
|
|
return {r_union, b_elts.second};
|
|
}
|
|
|
|
CG::Binding bind_length(Call* call, Mode ctx, CodeGen& cg, CG_Builder& frag) {
|
|
assert(call->n_args() == 1);
|
|
CG::Binding b(CG::bind(call->arg(0), cg, frag));
|
|
int r(GET_REG(cg));
|
|
PUSH_INSTR(frag, BytecodeStream::LENGTH, CG::r(b.first), CG::r(r));
|
|
return {r, b.second};
|
|
}
|
|
|
|
CG::Binding bind_lb(Call* call, Mode ctx, CodeGen& cg, CG_Builder& frag) {
|
|
assert(call->n_args() == 1);
|
|
CG::Binding b(CG::bind(call->arg(0), cg, frag));
|
|
int r(GET_REG(cg));
|
|
PUSH_INSTR(frag, BytecodeStream::LB, CG::r(b.first), CG::r(r));
|
|
return CG::Binding(r, b.second);
|
|
}
|
|
CG::Binding bind_ub(Call* call, Mode ctx, CodeGen& cg, CG_Builder& frag) {
|
|
assert(call->n_args() == 1);
|
|
CG::Binding b(CG::bind(call->arg(0), cg, frag));
|
|
int r(GET_REG(cg));
|
|
PUSH_INSTR(frag, BytecodeStream::UB, CG::r(b.first), CG::r(r));
|
|
return CG::Binding(r, b.second);
|
|
}
|
|
CG::Binding bind_dom(Call* call, Mode ctx, CodeGen& cg, CG_Builder& frag) {
|
|
assert(call->n_args() == 1);
|
|
CG::Binding b(CG::bind(call->arg(0), cg, frag));
|
|
int r(GET_REG(cg));
|
|
PUSH_INSTR(frag, BytecodeStream::DOM, CG::r(b.first), CG::r(r));
|
|
return CG::Binding(r, b.second);
|
|
}
|
|
CG::Binding bind_lb_array(Call* call, Mode ctx, CodeGen& cg, CG_Builder& frag) {
|
|
assert(call->n_args() == 1);
|
|
CG::Binding b_A(CG::bind(call->arg(0), cg, frag));
|
|
|
|
int r_agg(GET_REG(cg));
|
|
PUSH_INSTR(frag, BytecodeStream::BUILTIN, cg.find_builtin("infinity"),
|
|
CG::r(bind_cst(1, cg, frag)));
|
|
PUSH_INSTR(frag, BytecodeStream::POP, CG::r(r_agg));
|
|
|
|
int r_tmp(GET_REG(cg));
|
|
int r_test(GET_REG(cg));
|
|
int l_end(GET_LABEL(cg));
|
|
ITER_ARRAY(cg, frag, b_A.first, [&](CodeGen& cg, CG_Builder& frag, int r_elt) {
|
|
PUSH_INSTR(frag, BytecodeStream::LB, CG::r(r_elt), CG::r(r_tmp));
|
|
PUSH_INSTR(frag, BytecodeStream::LTI, CG::r(r_tmp), CG::r(r_agg), CG::r(r_test));
|
|
PUSH_INSTR(frag, BytecodeStream::JMPIFNOT, CG::r(r_test), CG::l(l_end));
|
|
PUSH_INSTR(frag, BytecodeStream::MOV, CG::r(r_tmp), CG::r(r_agg));
|
|
PUSH_LABEL(frag, l_end);
|
|
});
|
|
|
|
return CG::Binding(r_agg, b_A.second);
|
|
}
|
|
|
|
CG::Binding bind_ub_array(Call* call, Mode ctx, CodeGen& cg, CG_Builder& frag) {
|
|
assert(call->n_args() == 1);
|
|
CG::Binding b_A(CG::bind(call->arg(0), cg, frag));
|
|
|
|
int r_agg(GET_REG(cg));
|
|
PUSH_INSTR(frag, BytecodeStream::BUILTIN, cg.find_builtin("infinity"),
|
|
CG::r(bind_cst(0, cg, frag)));
|
|
PUSH_INSTR(frag, BytecodeStream::POP, CG::r(r_agg));
|
|
|
|
int r_tmp(GET_REG(cg));
|
|
int r_test(GET_REG(cg));
|
|
int l_end(GET_LABEL(cg));
|
|
ITER_ARRAY(cg, frag, b_A.first, [&](CodeGen& cg, CG_Builder& frag, int r_elt) {
|
|
PUSH_INSTR(frag, BytecodeStream::UB, CG::r(r_elt), CG::r(r_tmp));
|
|
PUSH_INSTR(frag, BytecodeStream::LTI, CG::r(r_agg), CG::r(r_tmp), CG::r(r_test));
|
|
PUSH_INSTR(frag, BytecodeStream::JMPIFNOT, CG::r(r_test), CG::l(l_end));
|
|
PUSH_INSTR(frag, BytecodeStream::MOV, CG::r(r_tmp), CG::r(r_agg));
|
|
PUSH_LABEL(frag, l_end);
|
|
});
|
|
|
|
return CG::Binding(r_agg, b_A.second);
|
|
}
|
|
|
|
CG::Binding bind_dom_array(Call* call, Mode ctx, CodeGen& cg, CG_Builder& frag) {
|
|
assert(call->n_args() == 1);
|
|
CG::Binding b_A(CG::bind(call->arg(0), cg, frag));
|
|
|
|
int r_agg(GET_REG(cg));
|
|
OPEN_VEC(cg, frag);
|
|
CLOSE_AGG(cg, frag);
|
|
PUSH_INSTR(frag, BytecodeStream::POP, CG::r(r_agg));
|
|
|
|
int r_tmp(GET_REG(cg));
|
|
ITER_ARRAY(cg, frag, b_A.first, [&](CodeGen& cg, CG_Builder& frag, int r_elt) {
|
|
PUSH_INSTR(frag, BytecodeStream::DOM, CG::r(r_elt), CG::r(r_tmp));
|
|
PUSH_INSTR(frag, BytecodeStream::UNION, CG::r(r_agg), CG::r(r_tmp), CG::r(r_agg));
|
|
});
|
|
|
|
return {r_agg, b_A.second};
|
|
}
|
|
|
|
CG::Binding bind_index_set(Call* call, Mode ctx, CodeGen& cg, CG_Builder& frag) {
|
|
assert(call->n_args() == 1);
|
|
assert(call->arg(0)->type().dim() == 1);
|
|
CG::Binding b_arg(CG::bind(call->arg(0), cg, frag));
|
|
|
|
PUSH_INSTR(frag, BytecodeStream::BUILTIN, cg.find_builtin("index_set"), CG::r(b_arg.first),
|
|
CG::r(bind_cst(1, cg, frag)));
|
|
int r(GET_REG(cg));
|
|
PUSH_INSTR(frag, BytecodeStream::POP, CG::r(r));
|
|
|
|
return {r, b_arg.second};
|
|
}
|
|
|
|
template <int X, int Y>
|
|
CG::Binding bind_index_set_XofY(Call* call, Mode ctx, CodeGen& cg, CG_Builder& frag) {
|
|
assert(call->n_args() == 1);
|
|
CG::Binding b_arg(CG::bind(call->arg(0), cg, frag));
|
|
|
|
PUSH_INSTR(frag, BytecodeStream::BUILTIN, cg.find_builtin("index_set"), CG::r(b_arg.first),
|
|
CG::r(bind_cst(X, cg, frag)));
|
|
int r(GET_REG(cg));
|
|
PUSH_INSTR(frag, BytecodeStream::POP, CG::r(r));
|
|
|
|
return {r, b_arg.second};
|
|
}
|
|
|
|
CG::Binding bind_bool2int(Call* call, Mode ctx, CodeGen& cg, CG_Builder& frag) {
|
|
assert(call->n_args() == 1);
|
|
return CG::force_or_bind(call->arg(0), BytecodeProc::FUN, cg, frag);
|
|
}
|
|
CG::Binding bind_int2float(Call* call, Mode ctx, CodeGen& cg, CG_Builder& frag) {
|
|
assert(call->n_args() == 1);
|
|
return CG::bind(call->arg(0), cg, frag);
|
|
}
|
|
|
|
CG::Binding bind_call(Call* call, Mode ctx, CodeGen& cg, CG_Builder& frag);
|
|
CG_Cond::T compile_call(Call* call, Mode ctx, CodeGen& cg, CG_Builder& frag);
|
|
|
|
CG::Binding bind_set_max(Call* call, Mode ctx, CodeGen& cg, CG_Builder& frag) {
|
|
assert(call->type().ispar());
|
|
assert(call->n_args() == 1);
|
|
CG::Binding b_A(CG::bind(call->arg(0), cg, frag));
|
|
int r(GET_REG(cg));
|
|
int r_sz(GET_REG(cg));
|
|
PUSH_INSTR(frag, BytecodeStream::LENGTH, CG::r(b_A.first), CG::r(r_sz));
|
|
int l_fst(GET_LABEL(cg));
|
|
PUSH_INSTR(frag, BytecodeStream::LEI, CG::r(bind_cst(0, cg, frag)), CG::r(r_sz), CG::r(r));
|
|
PUSH_INSTR(frag, BytecodeStream::JMPIF, CG::r(r), CG::l(l_fst));
|
|
PUSH_INSTR(frag, BytecodeStream::ABORT);
|
|
PUSH_LABEL(frag, l_fst);
|
|
|
|
PUSH_INSTR(frag, BytecodeStream::GET_VEC, CG::r(b_A.first), CG::r(r_sz), CG::r(r));
|
|
|
|
// TODO: Also should not be allowed on empty sets
|
|
return CG::Binding(r, b_A.second);
|
|
};
|
|
|
|
CG::Binding bind_max(Call* call, Mode ctx, CodeGen& cg, CG_Builder& frag) {
|
|
assert(call->type().ispar());
|
|
assert(call->n_args() == 1);
|
|
CG::Binding b_A(CG::bind(call->arg(0), cg, frag));
|
|
|
|
int r(GET_REG(cg));
|
|
PUSH_INSTR(frag, BytecodeStream::BUILTIN, cg.find_builtin("infinity"),
|
|
CG::r(bind_cst(0, cg, frag)));
|
|
PUSH_INSTR(frag, BytecodeStream::POP, CG::r(r));
|
|
|
|
int r_test(GET_REG(cg));
|
|
int l_end(GET_LABEL(cg));
|
|
ITER_ARRAY(cg, frag, b_A.first, [&](CodeGen& cg, CG_Builder& frag, int r_elt) {
|
|
PUSH_INSTR(frag, BytecodeStream::LTI, CG::r(r), CG::r(r_elt), CG::r(r_test));
|
|
PUSH_INSTR(frag, BytecodeStream::JMPIFNOT, CG::r(r_test), CG::l(l_end));
|
|
PUSH_INSTR(frag, BytecodeStream::MOV, CG::r(r_elt), CG::r(r));
|
|
PUSH_LABEL(frag, l_end);
|
|
});
|
|
|
|
// TODO: Fail if array size < 1
|
|
return CG::Binding(r, CG_Cond::ttt());
|
|
}
|
|
|
|
CG::Binding bind_arg_max(Call* call, Mode ctx, CodeGen& cg, CG_Builder& frag) {
|
|
assert(call->n_args() == 1);
|
|
CG::Binding b_A(CG::bind(call->arg(0), cg, frag));
|
|
int r(GET_REG(cg));
|
|
int r_elt(GET_REG(cg));
|
|
int r_v(GET_REG(cg));
|
|
int r_test(GET_REG(cg));
|
|
int r_index(GET_REG(cg));
|
|
|
|
PUSH_INSTR(frag, BytecodeStream::BUILTIN, cg.find_builtin("index_set"), CG::r(b_A.first),
|
|
CG::r(bind_cst(1, cg, frag)));
|
|
PUSH_INSTR(frag, BytecodeStream::POP, CG::r(r_index));
|
|
PUSH_INSTR(frag, BytecodeStream::BUILTIN, cg.find_builtin("infinity"),
|
|
CG::r(bind_cst(0, cg, frag)));
|
|
PUSH_INSTR(frag, BytecodeStream::POP, CG::r(r_v));
|
|
|
|
int l_end(GET_LABEL(cg));
|
|
ITER_SET(cg, frag, r_index, [&](CodeGen& cg, CG_Builder& frag, int r_i) {
|
|
// Array access should be valid since we are using the actual index set.
|
|
PUSH_INSTR(frag, BytecodeStream::GET_ARRAY, CG::i(1), CG::r(b_A.first), CG::r(r_i),
|
|
CG::r(r_elt), CG::r(r_test));
|
|
PUSH_INSTR(frag, BytecodeStream::LTI, CG::r(r_v), CG::r(r_elt), CG::r(r_test));
|
|
PUSH_INSTR(frag, BytecodeStream::JMPIFNOT, CG::r(r_test), CG::l(l_end));
|
|
PUSH_INSTR(frag, BytecodeStream::MOV, CG::r(r_elt), CG::r(r_v));
|
|
PUSH_INSTR(frag, BytecodeStream::MOV, CG::r(r_i), CG::r(r));
|
|
PUSH_LABEL(frag, l_end);
|
|
});
|
|
|
|
// TODO: Fail if array size < 1
|
|
return CG::Binding(r, CG_Cond::ttt());
|
|
}
|
|
|
|
CG::Binding bind_arg_min(Call* call, Mode ctx, CodeGen& cg, CG_Builder& frag) {
|
|
assert(call->n_args() == 1);
|
|
CG::Binding b_A(CG::bind(call->arg(0), cg, frag));
|
|
int r(GET_REG(cg));
|
|
int r_elt(GET_REG(cg));
|
|
int r_v(GET_REG(cg));
|
|
int r_test(GET_REG(cg));
|
|
int r_index(GET_REG(cg));
|
|
|
|
PUSH_INSTR(frag, BytecodeStream::BUILTIN, cg.find_builtin("index_set"), CG::r(b_A.first),
|
|
CG::r(bind_cst(1, cg, frag)));
|
|
PUSH_INSTR(frag, BytecodeStream::POP, CG::r(r_index));
|
|
PUSH_INSTR(frag, BytecodeStream::BUILTIN, cg.find_builtin("infinity"),
|
|
CG::r(bind_cst(1, cg, frag)));
|
|
PUSH_INSTR(frag, BytecodeStream::POP, CG::r(r_v));
|
|
|
|
int l_end(GET_LABEL(cg));
|
|
ITER_SET(cg, frag, r_index, [&](CodeGen& cg, CG_Builder& frag, int r_i) {
|
|
// Array access should be valid since we are using the actual index set.
|
|
PUSH_INSTR(frag, BytecodeStream::GET_ARRAY, CG::i(1), CG::r(b_A.first), CG::r(r_i),
|
|
CG::r(r_elt), CG::r(r_test));
|
|
PUSH_INSTR(frag, BytecodeStream::LTI, CG::r(r_elt), CG::r(r_v), CG::r(r_test));
|
|
PUSH_INSTR(frag, BytecodeStream::JMPIFNOT, CG::r(r_test), CG::l(l_end));
|
|
PUSH_INSTR(frag, BytecodeStream::MOV, CG::r(r_elt), CG::r(r_v));
|
|
PUSH_INSTR(frag, BytecodeStream::MOV, CG::r(r_i), CG::r(r));
|
|
PUSH_LABEL(frag, l_end);
|
|
});
|
|
|
|
// TODO: Fail if array size < 1
|
|
return CG::Binding(r, CG_Cond::ttt());
|
|
}
|
|
|
|
CG::Binding bind_set_min(Call* call, Mode ctx, CodeGen& cg, CG_Builder& frag) {
|
|
assert(call->type().ispar());
|
|
assert(call->n_args() == 1);
|
|
CG::Binding b_A(CG::bind(call->arg(0), cg, frag));
|
|
int r(GET_REG(cg));
|
|
int r_sz(GET_REG(cg));
|
|
PUSH_INSTR(frag, BytecodeStream::LENGTH, CG::r(b_A.first), CG::r(r_sz));
|
|
int l_fst(GET_LABEL(cg));
|
|
PUSH_INSTR(frag, BytecodeStream::LEI, CG::r(bind_cst(1, cg, frag)), CG::r(r_sz), CG::r(r));
|
|
PUSH_INSTR(frag, BytecodeStream::JMPIF, CG::r(r), CG::l(l_fst));
|
|
PUSH_INSTR(frag, BytecodeStream::ABORT);
|
|
|
|
PUSH_LABEL(frag, l_fst);
|
|
PUSH_INSTR(frag, BytecodeStream::GET_VEC, CG::r(b_A.first), CG::r(bind_cst(1, cg, frag)),
|
|
CG::r(r));
|
|
|
|
return CG::Binding(r, CG_Cond::ttt());
|
|
}
|
|
|
|
CG::Binding bind_min(Call* call, Mode ctx, CodeGen& cg, CG_Builder& frag) {
|
|
assert(call->type().ispar());
|
|
assert(call->n_args() == 1);
|
|
CG::Binding b_A(CG::bind(call->arg(0), cg, frag));
|
|
|
|
int r(GET_REG(cg));
|
|
PUSH_INSTR(frag, BytecodeStream::BUILTIN, cg.find_builtin("infinity"),
|
|
CG::r(bind_cst(1, cg, frag)));
|
|
PUSH_INSTR(frag, BytecodeStream::POP, CG::r(r));
|
|
|
|
int r_test(GET_REG(cg));
|
|
int l_end(GET_LABEL(cg));
|
|
ITER_ARRAY(cg, frag, b_A.first, [&](CodeGen& cg, CG_Builder& frag, int r_elt) {
|
|
PUSH_INSTR(frag, BytecodeStream::LTI, CG::r(r_elt), CG::r(r), CG::r(r_test));
|
|
PUSH_INSTR(frag, BytecodeStream::JMPIFNOT, CG::r(r_test), CG::l(l_end));
|
|
PUSH_INSTR(frag, BytecodeStream::MOV, CG::r(r_elt), CG::r(r));
|
|
PUSH_LABEL(frag, l_end);
|
|
});
|
|
|
|
return CG::Binding(r, CG_Cond::ttt());
|
|
}
|
|
|
|
CG::Binding bind_card(Call* call, Mode ctx, CodeGen& cg, CG_Builder& frag) {
|
|
assert(call->type().ispar());
|
|
assert(call->n_args() == 1);
|
|
|
|
CG::Binding b_A(CG::bind(call->arg(0), cg, frag));
|
|
int r(GET_REG(cg));
|
|
int r_i(GET_REG(cg));
|
|
int r_sz(GET_REG(cg));
|
|
int r_e(GET_REG(cg));
|
|
PUSH_INSTR(frag, BytecodeStream::IMMI, CG::i(0), CG::r(r));
|
|
|
|
PUSH_INSTR(frag, BytecodeStream::IMMI, CG::i(1), CG::r(r_i));
|
|
PUSH_INSTR(frag, BytecodeStream::LENGTH, CG::r(b_A.first), CG::r(r_sz));
|
|
int l_hd(GET_LABEL(cg));
|
|
int l_tl(GET_LABEL(cg));
|
|
PUSH_INSTR(frag, BytecodeStream::LTI, CG::r(r_i), CG::r(r_sz), CG::r(r_e));
|
|
PUSH_INSTR(frag, BytecodeStream::JMPIFNOT, CG::r(r_e), CG::l(l_tl));
|
|
|
|
PUSH_LABEL(frag, l_hd);
|
|
PUSH_INSTR(frag, BytecodeStream::INCI, CG::r(r));
|
|
PUSH_INSTR(frag, BytecodeStream::GET_VEC, CG::r(b_A.first), CG::r(r_i), CG::r(r_e));
|
|
PUSH_INSTR(frag, BytecodeStream::SUBI, CG::r(r), CG::r(r_e), CG::r(r));
|
|
PUSH_INSTR(frag, BytecodeStream::INCI, CG::r(r_i));
|
|
PUSH_INSTR(frag, BytecodeStream::GET_VEC, CG::r(b_A.first), CG::r(r_i), CG::r(r_e));
|
|
PUSH_INSTR(frag, BytecodeStream::INCI, CG::r(r_i));
|
|
PUSH_INSTR(frag, BytecodeStream::ADDI, CG::r(r), CG::r(r_e), CG::r(r));
|
|
|
|
PUSH_INSTR(frag, BytecodeStream::LTI, CG::r(r_i), CG::r(r_sz), CG::r(r_e));
|
|
PUSH_INSTR(frag, BytecodeStream::JMPIF, CG::r(r_e), CG::l(l_hd));
|
|
PUSH_LABEL(frag, l_tl);
|
|
return CG::Binding(r, b_A.second);
|
|
}
|
|
|
|
CG::Binding bind_internal(Call* call, Mode ctx, CodeGen& cg, CG_Builder& frag) {
|
|
std::string name = call->decl()->id().str();
|
|
CG_ProcID proc = cg.find_builtin(name);
|
|
int r_res(GET_REG(cg));
|
|
|
|
std::vector<CG_Value> r_args(call->n_args());
|
|
std::vector<CG_Cond::T> p_args(call->n_args());
|
|
for (int i = 0; i < call->n_args(); ++i) {
|
|
CG::Binding b_arg(CG::bind(call->arg(i), cg, frag));
|
|
r_args[i] = CG::r(b_arg.first);
|
|
p_args[i] = b_arg.second;
|
|
}
|
|
|
|
// Push BUILTIN instruction with the correct id
|
|
PUSH_INSTR(frag, BytecodeStream::BUILTIN, proc);
|
|
// Append instruction with register arguments
|
|
CG_Instr& i = frag.instrs.back();
|
|
PUSH_INSTR_OPERAND(i, r_args);
|
|
|
|
PUSH_INSTR(frag, BytecodeStream::POP, CG::r(r_res));
|
|
|
|
return {r_res, CG_Cond::forall(ctx, p_args)};
|
|
}
|
|
|
|
CG_Cond::T eval_context_is_root(Call* call, Mode ctx, CodeGen& cg, CG_Builder& frag) {
|
|
// TODO: I'm not sure this is actually correct....
|
|
assert(call->n_args() == 1);
|
|
CG::Mode arg_ctx(BytecodeProc::FUN);
|
|
try {
|
|
arg_ctx = cg.mode_map.at(call->arg(0));
|
|
} catch (const std::out_of_range& exn) {
|
|
}
|
|
|
|
if (arg_ctx == BytecodeProc::ROOT) {
|
|
return CG_Cond::ttt();
|
|
} else {
|
|
return CG_Cond::fff();
|
|
}
|
|
}
|
|
|
|
CG_Cond::T eval_has_bounds(Call* call, Mode ctx, CodeGen& cg, CG_Builder& frag) {
|
|
// TODO: Actual implementation!
|
|
|
|
return CG_Cond::ttt();
|
|
}
|
|
|
|
CG::Binding bind_TODO(Call* call, Mode ctx, CodeGen& cg, CG_Builder& frag) {
|
|
PUSH_INSTR(frag, BytecodeStream::ABORT);
|
|
return {0, CG_Cond::fff()};
|
|
}
|
|
CG_Cond::T eval_TODO(Call* call, Mode ctx, CodeGen& cg, CG_Builder& frag) {
|
|
PUSH_INSTR(frag, BytecodeStream::ABORT);
|
|
return CG_Cond::fff();
|
|
}
|
|
|
|
template <int X>
|
|
CG_Cond::T eval_argX_only(Call* call, Mode ctx, CodeGen& cg, CG_Builder& frag) {
|
|
return CG::compile(call->arg(X - 1), cg, frag);
|
|
}
|
|
|
|
builtin_table init_builtins(void) {
|
|
builtin_table tbl;
|
|
Constants& c(constants());
|
|
tbl.insert(std::make_pair(c.ids.sum.str(), builtin_t{eval_error_b, bind_sum}));
|
|
tbl.insert(std::make_pair(c.ids.exists.str(), builtin_t{eval_exists, bind_error_g}));
|
|
tbl.insert(std::make_pair(c.ids.forall.str(), builtin_t{eval_forall, bind_error_g}));
|
|
tbl.insert(std::make_pair(c.ids.assert.str(), builtin_t{eval_assert_b, bind_assert_g}));
|
|
tbl.insert(std::make_pair("arrayXd", builtin_t{eval_error_b, bind_arrayXd}));
|
|
tbl.insert(std::make_pair("array1d", builtin_t{eval_error_b, bind_array1d}));
|
|
tbl.insert(std::make_pair("array2d", builtin_t{eval_error_b, bind_arrayNd<2>}));
|
|
tbl.insert(std::make_pair("array3d", builtin_t{eval_error_b, bind_arrayNd<3>}));
|
|
tbl.insert(std::make_pair("array4d", builtin_t{eval_error_b, bind_arrayNd<4>}));
|
|
tbl.insert(std::make_pair("array5d", builtin_t{eval_error_b, bind_arrayNd<5>}));
|
|
tbl.insert(std::make_pair("array6d", builtin_t{eval_error_b, bind_arrayNd<6>}));
|
|
tbl.insert(std::make_pair("array7d", builtin_t{eval_error_b, bind_arrayNd<7>}));
|
|
tbl.insert(std::make_pair("array8d", builtin_t{eval_error_b, bind_arrayNd<8>}));
|
|
tbl.insert(std::make_pair("array9d", builtin_t{eval_error_b, bind_arrayNd<9>}));
|
|
tbl.insert(std::make_pair("array_union", builtin_t{eval_error_b, bind_array_union}));
|
|
tbl.insert(std::make_pair("index_set", builtin_t{eval_error_b, bind_index_set}));
|
|
tbl.insert(std::make_pair("index_set_1of2", builtin_t{eval_error_b, bind_index_set_XofY<1, 2>}));
|
|
tbl.insert(std::make_pair("index_set_2of2", builtin_t{eval_error_b, bind_index_set_XofY<2, 2>}));
|
|
tbl.insert(std::make_pair("index_set_1of3", builtin_t{eval_error_b, bind_index_set_XofY<1, 3>}));
|
|
tbl.insert(std::make_pair("index_set_2of3", builtin_t{eval_error_b, bind_index_set_XofY<2, 3>}));
|
|
tbl.insert(std::make_pair("index_set_3of3", builtin_t{eval_error_b, bind_index_set_XofY<3, 3>}));
|
|
tbl.insert(std::make_pair("index_set_1of4", builtin_t{eval_error_b, bind_index_set_XofY<1, 4>}));
|
|
tbl.insert(std::make_pair("index_set_2of4", builtin_t{eval_error_b, bind_index_set_XofY<2, 4>}));
|
|
tbl.insert(std::make_pair("index_set_3of4", builtin_t{eval_error_b, bind_index_set_XofY<3, 4>}));
|
|
tbl.insert(std::make_pair("index_set_4of4", builtin_t{eval_error_b, bind_index_set_XofY<4, 4>}));
|
|
tbl.insert(std::make_pair("length", builtin_t{eval_error_b, bind_length}));
|
|
tbl.insert(std::make_pair("lb", builtin_t{eval_error_b, bind_lb}));
|
|
tbl.insert(std::make_pair("ub", builtin_t{eval_error_b, bind_ub}));
|
|
tbl.insert(std::make_pair("dom", builtin_t{eval_error_b, bind_dom}));
|
|
tbl.insert(std::make_pair("lb_array", builtin_t{eval_error_b, bind_lb_array}));
|
|
tbl.insert(std::make_pair("ub_array", builtin_t{eval_error_b, bind_ub_array}));
|
|
tbl.insert(std::make_pair("dom_array", builtin_t{eval_error_b, bind_dom_array}));
|
|
tbl.insert(std::make_pair("set2array", builtin_t{eval_error_b, bind_set2array}));
|
|
tbl.insert(std::make_pair("dom_bounds_array", builtin_t{eval_error_b, bind_dom_bounds_array}));
|
|
tbl.insert(std::make_pair("arg_max", builtin_t{eval_error_b, bind_arg_max}));
|
|
tbl.insert(std::make_pair("arg_min", builtin_t{eval_error_b, bind_arg_min}));
|
|
tbl.insert(std::make_pair("card", builtin_t{eval_error_b, bind_card}));
|
|
tbl.insert(std::make_pair(c.ids.bool2int.str(), builtin_t{eval_error_b, bind_bool2int}));
|
|
tbl.insert(std::make_pair(c.ids.int2float.str(), builtin_t{eval_error_b, bind_bool2int}));
|
|
tbl.insert(std::make_pair("uniform", builtin_t{eval_error_b, bind_internal}));
|
|
tbl.insert(std::make_pair("sol", builtin_t{eval_error_b, bind_internal}));
|
|
tbl.insert(std::make_pair("sort_by", builtin_t{eval_error_b, bind_internal}));
|
|
tbl.insert(std::make_pair("floor", builtin_t{eval_error_b, bind_internal}));
|
|
tbl.insert(std::make_pair("ceil", builtin_t{eval_error_b, bind_internal}));
|
|
tbl.insert(std::make_pair("mzn_in_root_context", builtin_t{eval_context_is_root, bind_error_g}));
|
|
tbl.insert(std::make_pair("has_bounds", builtin_t{eval_has_bounds, bind_error_g}));
|
|
tbl.insert(std::make_pair("is_fixed", builtin_t{eval_isfixed_b, bind_error_g}));
|
|
tbl.insert(std::make_pair("fix", builtin_t{eval_fix, bind_fix}));
|
|
tbl.insert(std::make_pair("array_Xd", builtin_t{eval_error_b, bind_internal}));
|
|
tbl.insert(std::make_pair("slice_Xd", builtin_t{eval_error_b, bind_internal}));
|
|
tbl.insert(std::make_pair("internal_sort", builtin_t{eval_error_b, bind_internal}));
|
|
tbl.insert(std::make_pair("internal_max", builtin_t{eval_error_b, bind_max}));
|
|
tbl.insert(std::make_pair("internal_set_max", builtin_t{eval_error_b, bind_set_max}));
|
|
tbl.insert(std::make_pair("internal_min", builtin_t{eval_error_b, bind_min}));
|
|
tbl.insert(std::make_pair("internal_set_min", builtin_t{eval_error_b, bind_set_min}));
|
|
tbl.insert(
|
|
std::make_pair("symmetry_breaking_constraint", builtin_t{eval_argX_only<1>, bind_error_g}));
|
|
tbl.insert(std::make_pair("redundant_constraint", builtin_t{eval_argX_only<1>, bind_error_g}));
|
|
// OPTIONAL TYPE INTERNALS
|
|
tbl.insert(std::make_pair("reverse_map", builtin_t{eval_error_b, bind_TODO}));
|
|
tbl.insert(std::make_pair("occurs", builtin_t{eval_TODO, bind_error_g}));
|
|
tbl.insert(std::make_pair("absent", builtin_t{eval_TODO, bind_error_g}));
|
|
tbl.insert(std::make_pair("deopt", builtin_t{eval_error_b, bind_TODO}));
|
|
return tbl;
|
|
}
|
|
builtin_table& builtins(void) {
|
|
static builtin_table tbl(init_builtins());
|
|
return tbl;
|
|
}
|
|
|
|
CG::Binding CG::bind(Id* x, Mode ctx, CodeGen& cg, CG_Builder& frag) {
|
|
try {
|
|
return CG::Binding(cg.env().lookup(x->v()).first, CG_Cond::ttt());
|
|
} catch (const CG_Env<CodeGen::Binding>::NotFound& exn) {
|
|
VarDecl* vd = follow_id_to_decl(x)->cast<VarDecl>();
|
|
int g = cg.find_global(vd);
|
|
int r(GET_REG(cg));
|
|
PUSH_INSTR(frag, BytecodeStream::LOAD_GLOBAL, CG::g(g), CG::r(r));
|
|
return CG::Binding(r, CG_Cond::ttt());
|
|
}
|
|
}
|
|
|
|
CG::Binding CG::bind(SetLit* sl, Mode ctx, CodeGen& cg, CG_Builder& frag) {
|
|
bool is_vec(false);
|
|
OPEN_VEC(cg, frag);
|
|
if (IntSetVal* s = sl->isv()) {
|
|
int r_t(GET_REG(cg));
|
|
for (int ii = 0; ii < s->size(); ++ii) {
|
|
IntVal l(s->min(ii));
|
|
IntVal u(s->max(ii));
|
|
if (l.isFinite()) {
|
|
PUSH_INSTR(frag, BytecodeStream::IMMI, CG::i(l.toInt()), CG::r(r_t));
|
|
} else {
|
|
assert(l.isMinusInfinity());
|
|
PUSH_INSTR(frag, BytecodeStream::BUILTIN, cg.find_builtin("infinity"),
|
|
CG::r(bind_cst(0, cg, frag)));
|
|
PUSH_INSTR(frag, BytecodeStream::POP, CG::r(r_t));
|
|
}
|
|
PUSH_INSTR(frag, BytecodeStream::PUSH, CG::r(r_t));
|
|
if (u.isFinite()) {
|
|
PUSH_INSTR(frag, BytecodeStream::IMMI, CG::i(u.toInt()), CG::r(r_t));
|
|
} else {
|
|
assert(u.isPlusInfinity());
|
|
PUSH_INSTR(frag, BytecodeStream::BUILTIN, cg.find_builtin("infinity"),
|
|
CG::r(bind_cst(1, cg, frag)));
|
|
PUSH_INSTR(frag, BytecodeStream::POP, CG::r(r_t));
|
|
}
|
|
PUSH_INSTR(frag, BytecodeStream::PUSH, CG::r(r_t));
|
|
}
|
|
} else {
|
|
is_vec = true;
|
|
int sz = sl->v().size();
|
|
int r_t(GET_REG(cg));
|
|
for (int ii = 0; ii < sz; ii++) {
|
|
// Assumes the set is sorted.
|
|
/*
|
|
IntLit* k(l->v()[ii]->template cast<IntLit>());
|
|
assert(k);
|
|
PUSH_INSTR(frag, BytecodeStream::IMMI, CG::i(k->v().toInt()), CG::r(r_t));
|
|
PUSH_INSTR(frag, BytecodeStream::PUSH, CG::r(r_t));
|
|
PUSH_INSTR(frag, BytecodeStream::PUSH, CG::r(r_t));
|
|
*/
|
|
CG::Binding b(CG::bind(sl->v()[ii], cg, frag)); // Ignores any partiality.
|
|
PUSH_INSTR(frag, BytecodeStream::PUSH, CG::r(b.first));
|
|
}
|
|
}
|
|
CLOSE_AGG(cg, frag);
|
|
int r(GET_REG(cg));
|
|
PUSH_INSTR(frag, BytecodeStream::POP, CG::r(r));
|
|
if (is_vec) PUSH_INSTR(frag, BytecodeStream::MAKE_SET, CG::r(r), CG::r(r));
|
|
return CG::Binding(r, CG_Cond::ttt());
|
|
}
|
|
|
|
CG::Binding CG::bind(ArrayLit* a, Mode ctx, CodeGen& cg, CG_Builder& frag) {
|
|
// Build up the array.
|
|
std::vector<std::pair<IntLit*, int>> r_vec;
|
|
std::vector<CG_Cond::T> p_vec;
|
|
|
|
int sz(a->size());
|
|
for (int ii = 0; ii < sz; ++ii) {
|
|
if ((*a)[ii]->type().isbool()) {
|
|
CG_Cond::T c(CG::compile((*a)[ii], cg, frag));
|
|
if (!c.get()) {
|
|
if (!c.sign()) {
|
|
r_vec.emplace_back(nullptr, bind_cst(1, cg, frag));
|
|
} else {
|
|
r_vec.emplace_back(nullptr, bind_cst(0, cg, frag));
|
|
}
|
|
} else {
|
|
r_vec.emplace_back(nullptr, CG::force(c, ctx, cg, frag));
|
|
}
|
|
} else if (auto il = (*a)[ii]->dyn_cast<IntLit>()) {
|
|
// Avoid lookup cost when constructing large array literals
|
|
r_vec.emplace_back(il, 0xdeadbeef);
|
|
} else {
|
|
Binding b_ii(CG::bind((*a)[ii], cg, frag));
|
|
r_vec.emplace_back(nullptr, b_ii.first);
|
|
p_vec.push_back(b_ii.second);
|
|
}
|
|
}
|
|
|
|
// Build Array
|
|
int r(GET_REG(cg));
|
|
OPEN_VEC(cg, frag);
|
|
for (auto r_c : r_vec) {
|
|
if (r_c.first) {
|
|
assert(r_c.first->v().isFinite());
|
|
PUSH_INSTR(frag, BytecodeStream::IMMI, CG::i(r_c.first->v().toInt()), CG::r(r));
|
|
PUSH_INSTR(frag, BytecodeStream::PUSH, CG::r(r));
|
|
} else {
|
|
PUSH_INSTR(frag, BytecodeStream::PUSH, CG::r(r_c.second));
|
|
}
|
|
}
|
|
CLOSE_AGG(cg, frag);
|
|
PUSH_INSTR(frag, BytecodeStream::POP, CG::r(r));
|
|
|
|
// Add index set if required
|
|
if (!(a->dims() == 1 && a->min(0))) {
|
|
int rI;
|
|
if (a->dims() == 1) {
|
|
rI = (locate_range(a->min(0), a->max(0), cg, frag));
|
|
} else {
|
|
rI = GET_REG(cg);
|
|
OPEN_VEC(cg, frag);
|
|
for (int ii = 0; ii < a->dims(); ++ii) {
|
|
PUSH_INSTR(frag, BytecodeStream::PUSH, CG::r(bind_cst(a->min(ii), cg, frag)));
|
|
PUSH_INSTR(frag, BytecodeStream::PUSH, CG::r(bind_cst(a->max(ii), cg, frag)));
|
|
}
|
|
CLOSE_AGG(cg, frag);
|
|
PUSH_INSTR(frag, BytecodeStream::POP, CG::r(rI));
|
|
}
|
|
|
|
// Combine array and index sets
|
|
PUSH_INSTR(frag, BytecodeStream::BUILTIN, cg.find_builtin("array_Xd"), CG::r(r), CG::r(rI));
|
|
PUSH_INSTR(frag, BytecodeStream::POP, CG::r(r));
|
|
}
|
|
|
|
return {r, CG_Cond::forall(ctx, p_vec)};
|
|
}
|
|
|
|
std::pair<int, CG_Cond::T> execute_array_access(int r_A, std::vector<int> r_idxs, CodeGen& cg,
|
|
CG_Builder& frag) {
|
|
int sz = r_idxs.size();
|
|
int r(GET_REG(cg));
|
|
int r_cond(GET_REG(cg));
|
|
PUSH_INSTR(frag, BytecodeStream::GET_ARRAY, CG::i(sz), CG::r(r_A));
|
|
std::vector<CG_Value> r_args(sz + 2);
|
|
for (int ii = 0; ii < sz; ++ii) {
|
|
r_args[ii] = CG::r(r_idxs[ii]);
|
|
}
|
|
r_args[sz] = CG::r(r);
|
|
r_args[sz + 1] = CG::r(r_cond);
|
|
CG_Instr& i = frag.instrs.back();
|
|
PUSH_INSTR_OPERAND(i, r_args);
|
|
return {r, CG_Cond::reg(r_cond, true)};
|
|
}
|
|
|
|
CG::Binding CG::bind(ArrayAccess* a, Mode ctx, CodeGen& cg, CG_Builder& frag) {
|
|
GCLock lock;
|
|
// If the array elements are Boolean, we need to check for partiality
|
|
// in the indices, and that the accesses are within-range.
|
|
ASTExprVec<Expression> idx(a->idx());
|
|
Expression* A(a->v());
|
|
int sz(idx.size());
|
|
|
|
bool is_var = false;
|
|
for (int ii = 0; ii < sz; ++ii) {
|
|
is_var = is_var || idx[ii]->type().isvar();
|
|
}
|
|
|
|
// Evaluate the indices, put them in registers.
|
|
// Collect partiality of the expression.
|
|
std::vector<CG_Cond::T> cond;
|
|
|
|
// Now evaluate the array body, and emit the indices.
|
|
std::vector<int> r_idxs(sz);
|
|
for (int ii = 0; ii < sz; ++ii) {
|
|
CG::Binding b(CG::bind(idx[ii], cg, frag));
|
|
r_idxs[ii] = b.first;
|
|
cond.push_back(b.second);
|
|
}
|
|
|
|
Binding b_A(CG::bind(A, cg, frag));
|
|
int r_A(b_A.first);
|
|
cond.push_back(b_A.second);
|
|
|
|
if (is_var) {
|
|
int r = GET_REG(cg);
|
|
|
|
std::vector<Type> types(sz + 1, Type::varint());
|
|
types[sz] = Type::varint(sz);
|
|
auto fun = find_call_fun(cg, {"element"}, Type::varint(), types, BytecodeProc::FUN);
|
|
assert(std::get<1>(fun) == BytecodeProc::FUN);
|
|
|
|
std::vector<CG_Value> r_args(sz + 1);
|
|
for (int ii = 0; ii < sz; ++ii) {
|
|
r_args[ii] = CG::r(r_idxs[ii]);
|
|
}
|
|
r_args[sz] = CG::r(r_A);
|
|
|
|
// Push CALL instruction with the correct id
|
|
PUSH_INSTR(frag, BytecodeStream::CALL, BytecodeProc::FUN, std::get<0>(fun),
|
|
CG::i(!std::get<2>(fun)));
|
|
// Append instruction with register arguments
|
|
CG_Instr& i = frag.instrs.back();
|
|
PUSH_INSTR_OPERAND(i, r_args);
|
|
|
|
PUSH_INSTR(frag, BytecodeStream::POP, CG::r(r));
|
|
return {r, CG_Cond::forall(ctx, cond)};
|
|
} else {
|
|
// Just read the vector, and get the appropriate element.
|
|
int r;
|
|
CG_Cond::T ncond;
|
|
std::tie(r, ncond) = execute_array_access(r_A, r_idxs, cg, frag);
|
|
cond.push_back(ncond);
|
|
return {r, CG_Cond::forall(ctx, cond)};
|
|
}
|
|
}
|
|
|
|
int make_vec(CodeGen& cg, CG_Builder& frag, const std::vector<int>& regs) {
|
|
OPEN_VEC(cg, frag);
|
|
for (int r : regs) PUSH_INSTR(frag, BytecodeStream::PUSH, CG::r(r));
|
|
CLOSE_AGG(cg, frag);
|
|
int r_vec(GET_REG(cg));
|
|
PUSH_INSTR(frag, BytecodeStream::POP, CG::r(r_vec));
|
|
return r_vec;
|
|
}
|
|
|
|
void call_clause(CodeGen& cg, CG_Builder& frag, Mode ctx, const std::vector<int>& pos,
|
|
const std::vector<int>& neg) {
|
|
GCLock lock;
|
|
// Build the arguments vectors.
|
|
int r_pos(make_vec(cg, frag, pos));
|
|
int r_neg(make_vec(cg, frag, neg));
|
|
auto fun =
|
|
find_call_fun(cg, {"clause"}, Type::varbool(), {Type::varbool(1), Type::varbool(1)}, ctx);
|
|
assert(BytecodeProc::is_neg(ctx) == BytecodeProc::is_neg(std::get<1>(fun)));
|
|
PUSH_INSTR(frag, BytecodeStream::CALL, std::get<1>(fun), std::get<0>(fun),
|
|
CG::i(!std::get<2>(fun)), CG::r(r_pos), CG::r(r_neg));
|
|
}
|
|
|
|
int deinterlace(CodeGen& cg, CG_Builder& frag, int vec, int width, int offset) {
|
|
int r_sz(GET_REG(cg));
|
|
int r_slice(GET_REG(cg));
|
|
|
|
OPEN_VEC(cg, frag);
|
|
// int r_i(CG::locate_immi(1 + offset, cg, frag));
|
|
// int r_step(CG::locate_immi(width, cg, frag));
|
|
int r_i(bind_cst(1 + offset, cg, frag));
|
|
int r_step(bind_cst(width, cg, frag));
|
|
int r_elt(GET_REG(cg));
|
|
|
|
PUSH_INSTR(frag, BytecodeStream::LENGTH, CG::r(vec), CG::r(r_sz));
|
|
int l_hd(GET_LABEL(cg));
|
|
int l_tl(GET_LABEL(cg));
|
|
PUSH_INSTR(frag, BytecodeStream::LEI, CG::r(r_i), CG::r(r_sz), CG::r(r_elt));
|
|
PUSH_INSTR(frag, BytecodeStream::JMPIFNOT, CG::r(r_elt), CG::l(l_tl));
|
|
PUSH_LABEL(frag, l_hd);
|
|
PUSH_INSTR(frag, BytecodeStream::GET_VEC, CG::r(vec), CG::r(r_i), CG::r(r_elt));
|
|
PUSH_INSTR(frag, BytecodeStream::PUSH, CG::r(r_elt));
|
|
PUSH_INSTR(frag, BytecodeStream::ADDI, CG::r(r_i), CG::r(r_step), CG::r(r_i));
|
|
PUSH_INSTR(frag, BytecodeStream::LEI, CG::r(r_i), CG::r(r_sz), CG::r(r_elt));
|
|
PUSH_INSTR(frag, BytecodeStream::JMPIF, CG::r(r_elt), CG::l(l_hd));
|
|
PUSH_LABEL(frag, l_tl);
|
|
CLOSE_AGG(cg, frag);
|
|
PUSH_INSTR(frag, BytecodeStream::POP, CG::r(r_slice));
|
|
|
|
return r_slice;
|
|
}
|
|
|
|
/*
|
|
inline int reg_k(int k, CodeGen& cg, CG_Builder& frag, int& r) {
|
|
if(r == -1)
|
|
r = CG::locate_immi(1, cg, frag);
|
|
return r;
|
|
}
|
|
*/
|
|
|
|
CG::Binding CG::bind(ITE* ite, Mode ctx, CodeGen& cg, CG_Builder& frag) {
|
|
int sz(ite->size());
|
|
int r_one(bind_cst(1, cg, frag));
|
|
|
|
bool all_par = true;
|
|
for (int ii = 0; ii < sz; ++ii) {
|
|
if (!ite->e_if(ii)->type().ispar()) {
|
|
all_par = false;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (all_par) {
|
|
int l_end(GET_LABEL(cg));
|
|
int r_ret(GET_REG(cg));
|
|
int r_cond(GET_REG(cg));
|
|
PUSH_INSTR(frag, BytecodeStream::IMMI, CG::i(1), CG::r(r_cond));
|
|
|
|
bool is_total = true;
|
|
// We need to interfere with the env, here, because stuff may not be available.
|
|
// Except, ite->e_if(0) will always be available.
|
|
for (int ii = 0; ii < sz; ++ii) {
|
|
int r_sel = CG::force(CG::compile(ite->e_if(ii), cg, frag), BytecodeProc::FUN, cg, frag);
|
|
int l_cont(GET_LABEL(cg));
|
|
PUSH_INSTR(frag, BytecodeStream::JMPIFNOT, CG::r(r_sel), CG::l(l_cont));
|
|
cg.env_push();
|
|
if (!ite->e_then(ii)->type().ispar()) all_par = false;
|
|
CG::Binding b_res(CG::bind(ite->e_then(ii), cg, frag));
|
|
if (b_res.second.get()) {
|
|
is_total = false;
|
|
int r_condii(CG::force(b_res.second, ctx, cg, frag));
|
|
PUSH_INSTR(frag, BytecodeStream::MOV, CG::r(r_condii), CG::r(r_cond));
|
|
} else if (!b_res.second.sign()) {
|
|
PUSH_INSTR(frag, BytecodeStream::IMMI, CG::i(0), CG::r(r_cond));
|
|
}
|
|
PUSH_INSTR(frag, BytecodeStream::MOV, CG::r(b_res.first), CG::r(r_ret));
|
|
PUSH_INSTR(frag, BytecodeStream::JMP, CG::l(l_end));
|
|
cg.env_pop();
|
|
cg.env_push();
|
|
PUSH_LABEL(frag, l_cont);
|
|
}
|
|
// Else case.
|
|
if (!ite->e_else()->type().ispar()) all_par = false;
|
|
|
|
CG::Binding b_res(CG::bind(ite->e_else(), cg, frag));
|
|
if (b_res.second.get()) {
|
|
is_total = false;
|
|
int r_condii(CG::force(b_res.second, ctx, cg, frag));
|
|
PUSH_INSTR(frag, BytecodeStream::MOV, CG::r(r_condii), CG::r(r_cond));
|
|
} else if (!b_res.second.sign()) {
|
|
PUSH_INSTR(frag, BytecodeStream::IMMI, CG::i(0), CG::r(r_cond));
|
|
}
|
|
PUSH_INSTR(frag, BytecodeStream::MOV, CG::r(b_res.first), CG::r(r_ret));
|
|
PUSH_LABEL(frag, l_end);
|
|
// Now kill the availability of all the expressions.
|
|
for (int ii = 0; ii < sz; ++ii) {
|
|
cg.env_pop();
|
|
}
|
|
return {r_ret, is_total ? CG_Cond::ttt() : CG_Cond::reg(r_cond, all_par)};
|
|
} else {
|
|
GCLock lock;
|
|
std::vector<int> r_cond;
|
|
std::vector<int> p_res;
|
|
std::vector<int> r_res;
|
|
|
|
bool is_total = true;
|
|
for (int ii = 0; ii < sz; ++ii) {
|
|
r_cond.push_back(
|
|
CG::force(CG::compile(ite->e_if(ii), cg, frag), BytecodeProc::FUN, cg, frag));
|
|
CG::Binding b_res(CG::bind(ite->e_then(ii), cg, frag));
|
|
r_res.push_back(b_res.first);
|
|
// Make sure b_res.second is evaluated _outside_ the aggregation.
|
|
if (b_res.second.get()) {
|
|
is_total = false;
|
|
p_res.push_back(CG::force(b_res.second, ctx, cg, frag));
|
|
} else {
|
|
if (b_res.second.sign()) {
|
|
p_res.push_back(bind_cst(0, cg, frag));
|
|
} else {
|
|
p_res.push_back(r_one);
|
|
}
|
|
}
|
|
}
|
|
Binding b_final = CG::bind(ite->e_else(), cg, frag);
|
|
r_cond.push_back(r_one);
|
|
r_res.push_back(b_final.first);
|
|
if (b_final.second.get()) {
|
|
is_total = false;
|
|
p_res.push_back(CG::force(b_final.second, ctx, cg, frag));
|
|
} else {
|
|
if (b_final.second.sign()) {
|
|
p_res.push_back(bind_cst(0, cg, frag));
|
|
} else {
|
|
p_res.push_back(r_one);
|
|
}
|
|
}
|
|
|
|
int r_vec_cond(vec2array(r_cond, cg, frag));
|
|
|
|
int r_part;
|
|
if (!is_total) {
|
|
int r_vec_part(vec2array(p_res, cg, frag));
|
|
|
|
auto fun = find_call_fun(cg, {"if_then_else_partiality"}, Type::varbool(),
|
|
{Type::varbool(1), Type::varbool(1)}, BytecodeProc::FUN);
|
|
assert(std::get<1>(fun) == BytecodeProc::FUN);
|
|
|
|
PUSH_INSTR(frag, BytecodeStream::CALL, std::get<1>(fun), std::get<0>(fun),
|
|
CG::i(!std::get<2>(fun)), CG::r(r_vec_cond), CG::r(r_vec_part));
|
|
r_part = GET_REG(cg);
|
|
PUSH_INSTR(frag, BytecodeStream::POP, CG::r(r_part));
|
|
}
|
|
|
|
int r_vec_res(vec2array(r_res, cg, frag));
|
|
|
|
auto fun = find_call_fun(cg, {"if_then_else"}, Type::varint(),
|
|
{Type::varbool(1), Type::varint(1)}, BytecodeProc::FUN);
|
|
assert(std::get<1>(fun) == BytecodeProc::FUN);
|
|
|
|
int r_ret(GET_REG(cg));
|
|
PUSH_INSTR(frag, BytecodeStream::CALL, std::get<1>(fun), std::get<0>(fun),
|
|
CG::i(!std::get<2>(fun)), CG::r(r_vec_cond), CG::r(r_vec_res));
|
|
PUSH_INSTR(frag, BytecodeStream::POP, CG::r(r_ret));
|
|
|
|
return {r_ret, is_total ? CG_Cond::ttt() : CG_Cond::reg(r_part, false)};
|
|
}
|
|
}
|
|
|
|
CG::Binding CG::bind(BinOp* b, Mode ctx, CodeGen& cg, CG_Builder& frag) {
|
|
if (b->type().ispar()) {
|
|
CG::Binding b_lhs(CG::force_or_bind(b->lhs(), ctx, cg, frag));
|
|
CG::Binding b_rhs(CG::force_or_bind(b->rhs(), ctx, cg, frag));
|
|
if (b->lhs()->type().isintset()) {
|
|
int r = bind_binop_par_set(cg, frag, b->op(), b_lhs.first, b_rhs.first);
|
|
return {r, CG_Cond::ttt()};
|
|
}
|
|
std::vector<CG_Cond::T> cond = {b_lhs.second, b_rhs.second};
|
|
|
|
int l_skip;
|
|
int r_ret;
|
|
if (b->op() == BOT_DIV || b->op() == BOT_IDIV || b->op() == BOT_MOD) {
|
|
l_skip = GET_LABEL(cg);
|
|
int r_cond = GET_REG(cg);
|
|
r_ret = GET_REG(cg);
|
|
PUSH_INSTR(frag, BytecodeStream::IMMI, CG::i(0), CG::r(r_ret));
|
|
cond.push_back(CG_Cond::reg(r_cond, true));
|
|
PUSH_INSTR(frag, BytecodeStream::EQI, CG::r(b_rhs.first), CG::r(r_ret), CG::r(r_cond));
|
|
PUSH_INSTR(frag, BytecodeStream::NOT, CG::r(r_cond), CG::r(r_cond));
|
|
PUSH_INSTR(frag, BytecodeStream::JMPIFNOT, CG::r(r_cond), CG::l(l_skip));
|
|
}
|
|
int r = bind_binop_par_int(cg, frag, b->op(), b_lhs.first, b_rhs.first);
|
|
if (b->op() == BOT_DIV || b->op() == BOT_IDIV || b->op() == BOT_MOD) {
|
|
PUSH_INSTR(frag, BytecodeStream::MOV, CG::r(r), CG::r(r_ret));
|
|
r = r_ret;
|
|
PUSH_LABEL(frag, l_skip);
|
|
}
|
|
|
|
return {r, CG_Cond::forall(ctx, cond)};
|
|
} else {
|
|
std::vector<CG_Cond::T> partial;
|
|
int r_lhs = CG::force_or_bind(b->lhs(), ctx, partial, cg, frag);
|
|
int r_rhs = CG::force_or_bind(b->rhs(), ctx, partial, cg, frag);
|
|
|
|
int r = GET_REG(cg);
|
|
bind_binop_var(cg, frag, BytecodeProc::FUN, b->op(), r_lhs, r_rhs);
|
|
PUSH_INSTR(frag, BytecodeStream::POP, CG::r(r));
|
|
|
|
if (b->op() == BOT_DIV || b->op() == BOT_IDIV || b->op() == BOT_MOD) {
|
|
// These all require rhs() != 0. Which means we need to evaluate the rhs,
|
|
// and put it in a register.
|
|
int r_zero(bind_cst(0, cg, frag));
|
|
partial.push_back(~linear_cond(cg, frag, BOT_EQ, -ctx, r_rhs, r_zero));
|
|
}
|
|
return {r, CG_Cond::forall(ctx, partial)};
|
|
}
|
|
}
|
|
|
|
CG::Binding CG::bind(UnOp* u, Mode ctx, CodeGen& cg, CG_Builder& frag) {
|
|
// Unop is easy.
|
|
switch (u->op()) {
|
|
case UOT_NOT:
|
|
throw InternalError("Non-Boolean bind called on boolean operator.");
|
|
case UOT_PLUS:
|
|
return CG::bind(u->e(), cg, frag);
|
|
case UOT_MINUS: {
|
|
Binding b_e(CG::bind(u->e(), cg, frag));
|
|
int r;
|
|
if (u->type().isvar()) {
|
|
GCLock lock;
|
|
auto fun =
|
|
find_call_fun(cg, {"op_minus"}, Type::varint(), {Type::varint()}, BytecodeProc::FUN);
|
|
assert(std::get<1>(fun) == BytecodeProc::FUN);
|
|
PUSH_INSTR(frag, BytecodeStream::CALL, BytecodeProc::FUN, std::get<0>(fun),
|
|
CG::i(!std::get<2>(fun)), CG::r(b_e.first));
|
|
r = GET_REG(cg);
|
|
PUSH_INSTR(frag, BytecodeStream::POP, CG::r(r));
|
|
} else {
|
|
r = GET_REG(cg);
|
|
PUSH_INSTR(frag, BytecodeStream::IMMI, CG::i(0), CG::r(r));
|
|
PUSH_INSTR(frag, BytecodeStream::SUBI, CG::r(r), CG::r(b_e.first), CG::r(r));
|
|
}
|
|
return {r, b_e.second};
|
|
}
|
|
}
|
|
}
|
|
|
|
CG::Binding bind_call(Call* call, Mode ctx, CodeGen& cg, CG_Builder& frag) {
|
|
// Collects the partiality of the arguments
|
|
int sz = call->n_args();
|
|
std::vector<CG_Value> r_arg(sz);
|
|
std::vector<CG_Cond::T> p_arg(sz);
|
|
for (int ii = 0; ii < sz; ++ii) {
|
|
CG::Binding r_bind(CG::force_or_bind(call->arg(ii), BytecodeProc::FUN, cg, frag));
|
|
r_arg[ii] = CG::r(r_bind.first);
|
|
p_arg[ii] = r_bind.second;
|
|
}
|
|
|
|
// Bind the value part.
|
|
auto fun = find_call_fun(cg, call, BytecodeProc::ROOT);
|
|
assert(std::get<1>(fun) == BytecodeProc::FUN);
|
|
PUSH_INSTR(frag, BytecodeStream::CALL, BytecodeProc::FUN, std::get<0>(fun),
|
|
CG::i((!std::get<2>(fun)) && call->type().isvar()), r_arg);
|
|
|
|
int r_ret(GET_REG(cg));
|
|
PUSH_INSTR(frag, BytecodeStream::POP, CG::r(r_ret));
|
|
|
|
return std::make_pair(r_ret, CG_Cond::forall(ctx, p_arg));
|
|
}
|
|
|
|
CG::Binding CG::bind(Call* call, Mode ctx, CodeGen& cg, CG_Builder& frag) {
|
|
{
|
|
GCLock gc;
|
|
auto it(builtins().find(call->id().str()));
|
|
if (it != builtins().end()) {
|
|
return (*it).second.general(call, ctx, cg, frag);
|
|
}
|
|
}
|
|
|
|
return bind_call(call, ctx, cg, frag);
|
|
}
|
|
|
|
CG::Binding CG::bind(Let* let, Mode ctx, CodeGen& cg, CG_Builder& frag) {
|
|
std::vector<CG_Cond::T> partial;
|
|
|
|
cg.env_push();
|
|
|
|
if (let->ann().contains(constants().ann.promise_total)) {
|
|
OPEN_OTHER(cg, frag);
|
|
eval_let_body(let, BytecodeProc::ROOT, cg, frag, partial);
|
|
|
|
CG::Binding b_in(CG::bind(let->in(), cg, frag));
|
|
partial.push_back(b_in.second);
|
|
post_cond(cg, frag, CG_Cond::forall(BytecodeProc::ROOT, partial));
|
|
PUSH_INSTR(frag, BytecodeStream::PUSH, CG::r(b_in.first));
|
|
CLOSE_AGG(cg, frag);
|
|
cg.env_pop();
|
|
|
|
int ret = GET_REG(cg);
|
|
PUSH_INSTR(frag, BytecodeStream::POP, CG::r(ret));
|
|
|
|
return {ret, CG_Cond::forall(ctx, partial)};
|
|
}
|
|
|
|
eval_let_body(let, BytecodeProc::ROOT, cg, frag, partial);
|
|
CG::Binding b_in(CG::bind(let->in(), cg, frag));
|
|
partial.push_back(b_in.second);
|
|
|
|
cg.env_pop();
|
|
return {b_in.first, CG_Cond::forall(ctx, partial)};
|
|
}
|
|
|
|
CG::Binding CG::bind(Comprehension* comp, Mode ctx, CodeGen& cg, CG_Builder& frag) {
|
|
// Lift out the outer-most comprehension, since it will always
|
|
// be executed.
|
|
CG::bind(comp->in(0), cg, frag);
|
|
|
|
cg.env_push();
|
|
OPEN_VEC(cg, frag);
|
|
execute_comprehension_bind(comp, ctx, cg, frag);
|
|
CLOSE_AGG(cg, frag);
|
|
cg.env_pop();
|
|
int r(GET_REG(cg));
|
|
PUSH_INSTR(frag, BytecodeStream::POP, CG::r(r));
|
|
if (comp->type().is_set()) {
|
|
PUSH_INSTR(frag, BytecodeStream::MAKE_SET, CG::r(r), CG::r(r));
|
|
}
|
|
return CG::Binding(r, CG_Cond::ttt());
|
|
}
|
|
|
|
CG_Cond::T _compile(Expression* e, CodeGen& cg, CG_Builder& frag) {
|
|
// Look up the mode we need to compile e in.
|
|
CG::Mode ctx(BytecodeProc::FUN);
|
|
try {
|
|
ctx = cg.mode_map.at(e);
|
|
} catch (const std::out_of_range& exn) {
|
|
std::cerr << "%% Missing mode for: ";
|
|
debugprint(e);
|
|
}
|
|
|
|
switch (e->eid()) {
|
|
case Expression::E_INTLIT:
|
|
case Expression::E_FLOATLIT:
|
|
case Expression::E_SETLIT:
|
|
case Expression::E_STRINGLIT:
|
|
case Expression::E_ARRAYLIT:
|
|
case Expression::E_COMP:
|
|
throw InternalError("compile called on non-Boolean expression.");
|
|
case Expression::E_BOOLLIT:
|
|
return (e->template cast<BoolLit>()->v()) ? CG_Cond::ttt() : CG_Cond::fff();
|
|
case Expression::E_ID:
|
|
return CG::compile(e->template cast<Id>(), ctx, cg, frag);
|
|
case Expression::E_ANON:
|
|
throw InternalError("compile reached unexpected expression type: E_ANON.");
|
|
case Expression::E_ARRAYACCESS:
|
|
return CG::compile(e->template cast<ArrayAccess>(), ctx, cg, frag);
|
|
case Expression::E_ITE:
|
|
return CG::compile(e->template cast<ITE>(), ctx, cg, frag);
|
|
case Expression::E_BINOP:
|
|
return CG::compile(e->template cast<BinOp>(), ctx, cg, frag);
|
|
case Expression::E_UNOP:
|
|
return CG::compile(e->template cast<UnOp>(), ctx, cg, frag);
|
|
case Expression::E_CALL:
|
|
return CG::compile(e->template cast<Call>(), ctx, cg, frag);
|
|
case Expression::E_LET:
|
|
return CG::compile(e->template cast<Let>(), ctx, cg, frag);
|
|
case Expression::E_VARDECL:
|
|
case Expression::E_TI:
|
|
case Expression::E_TIID:
|
|
throw InternalError("Bytecode generator encountered unexpected expression type in compile.");
|
|
}
|
|
}
|
|
|
|
CG_Cond::T CG::compile(Expression* e, CodeGen& cg, CG_Builder& frag) {
|
|
assert(e->type().isbool());
|
|
try {
|
|
CG_Cond::T r(cg.cache_lookup(e).second);
|
|
return r;
|
|
} catch (const CG_Env<CG::Binding>::NotFound& exn) {
|
|
CG_Cond::T cond(_compile(e, cg, frag));
|
|
CG::Binding b(0xdeadbeef, cond);
|
|
cg.cache_store(e, b);
|
|
return cond;
|
|
}
|
|
// return _compile(e, cg, frag); // FIXME: Update env representation.
|
|
}
|
|
|
|
CG_Cond::T CG::compile(Id* x, Mode ctx, CodeGen& cg, CG_Builder& frag) {
|
|
try {
|
|
CG::Binding b(cg.env().lookup(x->v()));
|
|
// Check if there is a complex condition
|
|
if (b.second.get()) {
|
|
// If so then the register is a placeholder and the condition holds the value
|
|
assert(b.first == 0xdeadbeef);
|
|
return b.second;
|
|
} else {
|
|
// Otherwise the value should be in the register
|
|
return CG_Cond::reg(b.first, x->type().ispar());
|
|
}
|
|
} catch (const CG_Env<Binding>::NotFound& exn) {
|
|
VarDecl* vd = follow_id_to_decl(x)->cast<VarDecl>();
|
|
int g = cg.find_global(vd);
|
|
int r(GET_REG(cg));
|
|
PUSH_INSTR(frag, BytecodeStream::LOAD_GLOBAL, CG::g(g), CG::r(r));
|
|
return CG_Cond::reg(r, x->type().ispar());
|
|
}
|
|
}
|
|
|
|
CG_Cond::T CG::compile(ArrayAccess* a, Mode ctx, CodeGen& cg, CG_Builder& frag) {
|
|
// If the array elements are Boolean, we need to check for partiality
|
|
// in the indices, and that the accesses are within-range.
|
|
ASTExprVec<Expression> idx(a->idx());
|
|
Expression* A(a->v());
|
|
int sz(idx.size());
|
|
|
|
bool is_var = false;
|
|
for (int ii = 0; ii < sz; ++ii) {
|
|
is_var = is_var || idx[ii]->type().isvar();
|
|
}
|
|
|
|
// Evaluate the indices, put them in registers.
|
|
// Collect partiality of the expression.
|
|
std::vector<CG_Cond::T> cond;
|
|
|
|
// Now evaluate the array body, and emit the indices.
|
|
std::vector<int> r_idxs(sz);
|
|
for (int ii = 0; ii < sz; ++ii) {
|
|
CG::Binding b(CG::bind(idx[ii], cg, frag));
|
|
r_idxs[ii] = b.first;
|
|
cond.push_back(b.second);
|
|
}
|
|
|
|
CG::Binding b_A(CG::bind(A, cg, frag));
|
|
int r_A(b_A.first);
|
|
cond.push_back(b_A.second);
|
|
|
|
if (is_var) {
|
|
GCLock lock;
|
|
std::vector<Type> types(sz + 2, Type::varint());
|
|
types[0] = Type::varbool();
|
|
types[sz + 1] = Type::varbool(sz);
|
|
|
|
std::vector<CG_Value> r_args(sz + 1);
|
|
for (int ii = 0; ii < sz; ++ii) {
|
|
r_args[ii] = CG::r(r_idxs[ii]);
|
|
}
|
|
r_args[sz] = CG::r(r_A);
|
|
|
|
cond.push_back(CG_Cond::call({"element"}, -ctx, true, types, r_args));
|
|
return CG_Cond::forall(ctx, cond);
|
|
} else {
|
|
// Just read the vector, and get the appropriate element.
|
|
int r;
|
|
CG_Cond::T ncond;
|
|
std::tie(r, ncond) = execute_array_access(r_A, r_idxs, cg, frag);
|
|
cond.push_back(ncond);
|
|
cond.push_back(CG_Cond::reg(r, a->type().ispar()));
|
|
return CG_Cond::forall(ctx, cond);
|
|
}
|
|
}
|
|
|
|
CG_Cond::T CG::compile(ITE* ite, Mode ctx, CodeGen& cg, CG_Builder& frag) {
|
|
int sz(ite->size());
|
|
|
|
// Check whether all the conditions are par.
|
|
bool all_par = true;
|
|
for (int ii = 0; ii < sz; ++ii) {
|
|
if (!ite->e_if(ii)->type().ispar()) {
|
|
all_par = false;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (all_par) {
|
|
// We need to interfere with the env, here, because stuff may not be available.
|
|
// Except, ite->e_if(0) will always be available.
|
|
int l_end(GET_LABEL(cg));
|
|
int r_ret(GET_REG(cg));
|
|
for (int ii = 0; ii < sz; ++ii) {
|
|
int r_sel = CG::force(CG::compile(ite->e_if(ii), cg, frag), BytecodeProc::FUN, cg, frag);
|
|
int l_cont(GET_LABEL(cg));
|
|
PUSH_INSTR(frag, BytecodeStream::JMPIFNOT, CG::r(r_sel), CG::l(l_cont));
|
|
cg.env_push();
|
|
if (!ite->e_then(ii)->type().ispar()) all_par = false;
|
|
int r_val = CG::force(CG::compile(ite->e_then(ii), cg, frag), ctx, cg, frag);
|
|
PUSH_INSTR(frag, BytecodeStream::MOV, CG::r(r_val), CG::r(r_ret));
|
|
PUSH_INSTR(frag, BytecodeStream::JMP, CG::l(l_end));
|
|
cg.env_pop();
|
|
cg.env_push();
|
|
PUSH_LABEL(frag, l_cont);
|
|
}
|
|
// Else case.
|
|
if (!ite->e_else()->type().ispar()) all_par = false;
|
|
int r_val = CG::force(CG::compile(ite->e_else(), cg, frag), ctx, cg, frag);
|
|
PUSH_INSTR(frag, BytecodeStream::MOV, CG::r(r_val), CG::r(r_ret));
|
|
PUSH_LABEL(frag, l_end);
|
|
// Now kill the availability of all the expressions.
|
|
for (int ii = 0; ii < sz; ++ii) cg.env_pop();
|
|
return CG_Cond::reg(r_ret, all_par);
|
|
} else {
|
|
GCLock lock;
|
|
|
|
// Collect conditions
|
|
OPEN_VEC(cg, frag);
|
|
for (int ii = 0; ii < sz; ++ii) {
|
|
int r_if(CG::force(CG::compile(ite->e_if(ii), cg, frag), BytecodeProc::FUN, cg, frag));
|
|
PUSH_INSTR(frag, BytecodeStream::PUSH, CG::r(r_if));
|
|
}
|
|
PUSH_INSTR(frag, BytecodeStream::PUSH, CG::r(bind_cst(1, cg, frag)));
|
|
CLOSE_AGG(cg, frag);
|
|
int r_if(GET_REG(cg));
|
|
PUSH_INSTR(frag, BytecodeStream::POP, CG::r(r_if));
|
|
|
|
// Collect results
|
|
OPEN_VEC(cg, frag);
|
|
for (int ii = 0; ii < sz; ++ii) {
|
|
int r_then(CG::force(CG::compile(ite->e_then(ii), cg, frag), ctx, cg, frag));
|
|
PUSH_INSTR(frag, BytecodeStream::PUSH, CG::r(r_then));
|
|
}
|
|
{
|
|
int r_else(CG::force(CG::compile(ite->e_else(), cg, frag), ctx, cg, frag));
|
|
PUSH_INSTR(frag, BytecodeStream::PUSH, CG::r(r_else));
|
|
}
|
|
PUSH_INSTR(frag, BytecodeStream::PUSH, CG::r(bind_cst(1, cg, frag)));
|
|
CLOSE_AGG(cg, frag);
|
|
int r_then(GET_REG(cg));
|
|
PUSH_INSTR(frag, BytecodeStream::POP, CG::r(r_then));
|
|
|
|
return CG_Cond::call({"if_then_else"}, BytecodeProc::FUN, true,
|
|
{Type::varbool(), Type::varbool(1), Type::varbool(1)},
|
|
{CG::r(r_if), CG::r(r_then)});
|
|
}
|
|
}
|
|
|
|
CG_Cond::T shortcut_par_or(Mode ctx, CodeGen& cg, CG_Builder& frag, Expression* lhs,
|
|
Expression* rhs) {
|
|
Mode f_mode(ctx.strength(), false); // Check
|
|
assert(lhs->type().ispar());
|
|
int r(GET_REG(cg));
|
|
int l_exit(GET_LABEL(cg));
|
|
int r_lhs(CG::force(CG::compile(lhs, cg, frag), f_mode, cg, frag));
|
|
PUSH_INSTR(frag, BytecodeStream::MOV, CG::r(r_lhs), CG::r(r));
|
|
PUSH_INSTR(frag, BytecodeStream::JMPIF, CG::r(r), CG::l(l_exit));
|
|
cg.env_push();
|
|
int r_rhs(CG::force(CG::compile(rhs, cg, frag), f_mode, cg, frag));
|
|
PUSH_INSTR(frag, BytecodeStream::MOV, CG::r(r_rhs), CG::r(r));
|
|
cg.env_pop();
|
|
PUSH_LABEL(frag, l_exit);
|
|
return CG_Cond::reg(r, rhs->type().ispar());
|
|
}
|
|
|
|
CG_Cond::T shortcut_par_and(Mode ctx, CodeGen& cg, CG_Builder& frag, Expression* lhs,
|
|
Expression* rhs) {
|
|
Mode f_mode(ctx.strength(), false); // Check
|
|
assert(lhs->type().ispar());
|
|
int r(GET_REG(cg));
|
|
int l_exit(GET_LABEL(cg));
|
|
int r_lhs(CG::force(CG::compile(lhs, cg, frag), f_mode, cg, frag));
|
|
PUSH_INSTR(frag, BytecodeStream::MOV, CG::r(r_lhs), CG::r(r));
|
|
PUSH_INSTR(frag, BytecodeStream::JMPIFNOT, CG::r(r), CG::l(l_exit));
|
|
cg.env_push();
|
|
int r_rhs(CG::force(CG::compile(rhs, cg, frag), f_mode, cg, frag));
|
|
PUSH_INSTR(frag, BytecodeStream::MOV, CG::r(r_rhs), CG::r(r));
|
|
cg.env_pop();
|
|
PUSH_LABEL(frag, l_exit);
|
|
return CG_Cond::reg(r, rhs->type().ispar());
|
|
}
|
|
|
|
CG_Cond::T CG::compile(BinOp* b, Mode ctx, CodeGen& cg, CG_Builder& frag) {
|
|
std::vector<CG_Cond::T> cond;
|
|
// For anything we force here, its calling mode is considered positive (because
|
|
// it needs to mean what we said.)
|
|
CG::Mode f_mode(ctx.strength(), false);
|
|
if (b->lhs()->type().dim() > 0) {
|
|
GCLock lock;
|
|
assert(b->rhs()->type().dim() > 0);
|
|
assert(b->op() == BOT_EQ || b->op() == BOT_EQUIV);
|
|
int r_lhs = CG::force_or_bind(b->lhs(), f_mode, cond, cg, frag);
|
|
int r_rhs = CG::force_or_bind(b->rhs(), f_mode, cond, cg, frag);
|
|
cond.push_back(CG_Cond::call(
|
|
{"op_equals"}, BytecodeProc::FUN, true,
|
|
{b->type().isvar() ? Type::varbool() : Type::parbool(), b->lhs()->type(), b->rhs()->type()},
|
|
{CG::r(r_lhs), CG::r(r_rhs)}));
|
|
return CG_Cond::forall(ctx, cond);
|
|
}
|
|
if (b->op() == BOT_AND) {
|
|
if (b->lhs()->type().ispar()) return shortcut_par_and(ctx, cg, frag, b->lhs(), b->rhs());
|
|
if (b->rhs()->type().ispar()) return shortcut_par_and(ctx, cg, frag, b->rhs(), b->lhs());
|
|
} else if (b->op() == BOT_OR) {
|
|
if (b->lhs()->type().ispar()) return shortcut_par_or(ctx, cg, frag, b->lhs(), b->rhs());
|
|
if (b->rhs()->type().ispar()) return shortcut_par_or(ctx, cg, frag, b->rhs(), b->lhs());
|
|
}
|
|
if (b->type().ispar()) {
|
|
int r_lhs = CG::force_or_bind(b->lhs(), f_mode, cond, cg, frag);
|
|
int r_rhs = CG::force_or_bind(b->rhs(), f_mode, cond, cg, frag);
|
|
std::vector<int> r_cond;
|
|
for (CG_Cond::T c : cond) {
|
|
// r_cond.push_back(CG::force(c, ctx, cg, frag));
|
|
// FIXME: This is probably safe, assuming all the conditions
|
|
// are also par. We need these to be in their literal,
|
|
// non-context-adjusted modes for the disentailment check to work.
|
|
// If we want to handle non-par conditions, we probably need to
|
|
// force in ctx, then flip back.
|
|
r_cond.push_back(CG::force(c, f_mode, cg, frag));
|
|
}
|
|
int r_ret;
|
|
if (b->lhs()->type().isintset()) {
|
|
r_ret = bind_binop_par_set(cg, frag, b->op(), r_lhs, r_rhs);
|
|
} else {
|
|
r_ret = bind_binop_par_int(cg, frag, b->op(), r_lhs, r_rhs);
|
|
}
|
|
if (r_cond.size() > 0) {
|
|
// If any conditions don't hold, evaluate to false.
|
|
int r(GET_REG(cg));
|
|
int l_tl(GET_LABEL(cg));
|
|
PUSH_INSTR(frag, BytecodeStream::IMMI, CG::i(0), CG::r(r));
|
|
for (int r_c : r_cond) {
|
|
PUSH_INSTR(frag, BytecodeStream::JMPIFNOT, CG::r(r_c), CG::l(l_tl));
|
|
}
|
|
PUSH_INSTR(frag, BytecodeStream::MOV, CG::r(r_ret), CG::r(r));
|
|
PUSH_LABEL(frag, l_tl);
|
|
return CG_Cond::reg(r, true);
|
|
} else {
|
|
return CG_Cond::reg(r_ret, true);
|
|
}
|
|
}
|
|
switch (b->op()) {
|
|
case BOT_LE:
|
|
case BOT_LQ:
|
|
case BOT_GR:
|
|
case BOT_GQ:
|
|
case BOT_EQ:
|
|
case BOT_EQUIV:
|
|
case BOT_NQ: {
|
|
// Potentially partial.
|
|
int r_lhs = CG::force_or_bind(b->lhs(), f_mode, cond, cg, frag);
|
|
int r_rhs = CG::force_or_bind(b->rhs(), f_mode, cond, cg, frag);
|
|
cond.push_back(linear_cond(cg, frag, b->op(), ctx, r_lhs, r_rhs));
|
|
return CG_Cond::forall(ctx, cond);
|
|
} break;
|
|
case BOT_XOR: {
|
|
CG_Cond::T c_lhs = CG::compile(b->lhs(), cg, frag);
|
|
CG_Cond::T c_rhs = CG::compile(b->rhs(), cg, frag);
|
|
return CG_Cond::forall(ctx, CG_Cond::exists(ctx, c_lhs, c_rhs),
|
|
CG_Cond::exists(ctx, ~c_lhs, ~c_rhs));
|
|
} break;
|
|
case BOT_AND: {
|
|
cond.push_back(CG::compile(b->lhs(), cg, frag));
|
|
cond.push_back(CG::compile(b->rhs(), cg, frag));
|
|
return CG_Cond::forall(ctx, cond);
|
|
} break;
|
|
case BOT_OR: {
|
|
cond.push_back(CG::compile(b->lhs(), cg, frag));
|
|
cond.push_back(CG::compile(b->rhs(), cg, frag));
|
|
return CG_Cond::exists(ctx, cond);
|
|
} break;
|
|
case BOT_IMPL: {
|
|
cond.push_back(~CG::compile(b->lhs(), cg, frag));
|
|
cond.push_back(CG::compile(b->rhs(), cg, frag));
|
|
return CG_Cond::exists(ctx, cond);
|
|
} break;
|
|
case BOT_RIMPL: {
|
|
cond.push_back(CG::compile(b->lhs(), cg, frag));
|
|
cond.push_back(~CG::compile(b->rhs(), cg, frag));
|
|
return CG_Cond::exists(ctx, cond);
|
|
}
|
|
case BOT_IN: {
|
|
CG::Binding b_lhs(CG::bind(b->lhs(), cg, frag));
|
|
CG::Binding b_rhs(CG::bind(b->rhs(), cg, frag));
|
|
assert(b->rhs()->type().ispar());
|
|
cond.push_back(b_lhs.second);
|
|
cond.push_back(b_rhs.second);
|
|
if (ctx == BytecodeProc::ROOT) {
|
|
int r(GET_REG(cg));
|
|
PUSH_INSTR(frag, BytecodeStream::INTERSECT_DOMAIN, CG::r(b_lhs.first), CG::r(b_rhs.first),
|
|
CG::r(r));
|
|
// If INTERSECT_DOMAIN fails, then the interpreter will mark inconsistent and ABORT
|
|
cond.push_back(CG_Cond::ttt());
|
|
} else if (ctx == BytecodeProc::ROOT_NEG) {
|
|
int r(GET_REG(cg));
|
|
PUSH_INSTR(frag, BytecodeStream::DOM, CG::r(b_lhs.first), CG::r(r));
|
|
PUSH_INSTR(frag, BytecodeStream::DIFF, CG::r(r), CG::r(b_rhs.first), CG::r(r));
|
|
PUSH_INSTR(frag, BytecodeStream::INTERSECT_DOMAIN, CG::r(b_lhs.first), CG::r(r), CG::r(r));
|
|
// If INTERSECT_DOMAIN fails, then the interpreter will mark inconsistent and ABORT
|
|
cond.push_back(CG_Cond::ttt());
|
|
} else {
|
|
GCLock lock;
|
|
cond.push_back(CG_Cond::call({"set_in"}, ctx, true,
|
|
{Type::varbool(), Type::varint(), Type::varsetint()},
|
|
{CG::r(b_lhs.first), CG::r(b_rhs.first)}));
|
|
}
|
|
return CG_Cond::forall(ctx, cond);
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
throw InternalError("Unexpected fall-through in compilation of BinOp.");
|
|
}
|
|
|
|
CG_Cond::T CG::compile(UnOp* u, Mode ctx, CodeGen& cg, CG_Builder& frag) {
|
|
assert(u->op() == UOT_NOT);
|
|
return ~CG::compile(u->e(), cg, frag);
|
|
}
|
|
|
|
CG_Cond::T compile_call(Call* call, Mode ctx, CodeGen& cg, CG_Builder& frag) {
|
|
int sz = call->n_args();
|
|
std::vector<CG_Value> r_arg(sz);
|
|
std::vector<CG_Cond::T> p_arg;
|
|
|
|
// Evaluate the args, collecting the conditionality.
|
|
std::vector<Type> arg_types(sz + 1);
|
|
arg_types[0] = call->type();
|
|
for (int ii = 0; ii < sz; ++ii) {
|
|
arg_types[ii + 1] = call->arg(ii)->type();
|
|
r_arg[ii] = CG::r(CG::force_or_bind(call->arg(ii), BytecodeProc::FUN, p_arg, cg, frag));
|
|
}
|
|
|
|
// And finally, add the call itself
|
|
p_arg.push_back(CG_Cond::call(call->id(), ctx, call->type().isvar(), arg_types, r_arg));
|
|
return CG_Cond::forall(ctx, p_arg);
|
|
}
|
|
|
|
CG_Cond::T CG::compile(Call* call, Mode ctx, CodeGen& cg, CG_Builder& frag) {
|
|
// If we have a builtin for this, dispatch to that instead.
|
|
{
|
|
GCLock gc;
|
|
auto it(builtins().find(call->id().str()));
|
|
if (it != builtins().end()) {
|
|
return it->second.boolean(call, ctx, cg, frag);
|
|
}
|
|
}
|
|
|
|
return compile_call(call, ctx, cg, frag);
|
|
}
|
|
|
|
void eval_let_body(Let* let, BytecodeProc::Mode ctx, CodeGen& cg, CG_Builder& frag,
|
|
std::vector<CG_Cond::T>& conj) {
|
|
ASTExprVec<Expression> bindings(let->let());
|
|
for (Expression* e : bindings) {
|
|
if (auto vd = e->dyn_cast<VarDecl>()) {
|
|
// Bind the new definitions in context
|
|
CodeGen::Binding to_bind(0xdeadbeef, CG_Cond::ttt());
|
|
if (vd->e() && vd->type().isbool()) {
|
|
CG_Cond::T cond = CG::compile(vd->e(), cg, frag);
|
|
if (!cond.get()) {
|
|
to_bind.first = bind_cst(cond.sign(), cg, frag);
|
|
} else {
|
|
to_bind.second = cond;
|
|
}
|
|
} else if (vd->e()) {
|
|
// FIXME: Assuming domain isn't constraining.
|
|
CG::Binding b_v(CG::bind(vd->e(), cg, frag));
|
|
to_bind.first = b_v.first;
|
|
conj.push_back(b_v.second);
|
|
if (Expression* d = vd->ti()->domain()) {
|
|
if (!vd->ti()->isarray()) {
|
|
CG::Binding b_d = CG::bind(d, cg, frag); // Discarding any constraints on the set.
|
|
conj.push_back(b_d.second);
|
|
int r_dp = GET_REG(cg);
|
|
PUSH_INSTR(frag, BytecodeStream::INTERSECT_DOMAIN, CG::r(to_bind.first),
|
|
CG::r(b_d.first), CG::r(r_dp));
|
|
}
|
|
}
|
|
} else {
|
|
// Variable declaration. Assumes is total and nonempty.
|
|
CG::Binding b_d(bind_domain(vd, cg, frag));
|
|
to_bind.first = GET_REG(cg);
|
|
if (!vd->ti()->isarray()) {
|
|
conj.push_back(b_d.second);
|
|
PUSH_INSTR(frag, BytecodeStream::CALL, BytecodeProc::ROOT, cg.find_builtin("mk_intvar"),
|
|
CG::i(0), CG::r(b_d.first));
|
|
} else {
|
|
int rTMP(GET_REG(cg));
|
|
std::vector<int> r_regs;
|
|
for (Expression* r : vd->ti()->ranges()) {
|
|
Expression* dim(r->template cast<TypeInst>()->domain());
|
|
CG::Binding b_reg(CG::bind(dim, cg, frag));
|
|
// post_cond(cg, frag, b_reg.second);
|
|
r_regs.push_back(b_reg.first);
|
|
}
|
|
std::vector<Forset> nesting;
|
|
OPEN_VEC(cg, frag);
|
|
for (int r_r : r_regs) {
|
|
Forset iter(cg, r_r);
|
|
nesting.push_back(iter);
|
|
iter.emit_pre(frag);
|
|
}
|
|
PUSH_INSTR(frag, BytecodeStream::CALL, BytecodeProc::ROOT, cg.find_builtin("mk_intvar"),
|
|
CG::i(0), CG::r(b_d.first));
|
|
for (int r_i = r_regs.size() - 1; r_i >= 0; --r_i) {
|
|
nesting[r_i].emit_post(frag);
|
|
}
|
|
CLOSE_AGG(cg, frag);
|
|
PUSH_INSTR(frag, BytecodeStream::POP, CG::r(to_bind.first));
|
|
OPEN_VEC(cg, frag);
|
|
for (int r_r : r_regs) {
|
|
PUSH_INSTR(frag, BytecodeStream::GET_VEC, CG::r(r_r), CG::r(bind_cst(1, cg, frag)),
|
|
CG::r(rTMP));
|
|
PUSH_INSTR(frag, BytecodeStream::PUSH, CG::r(rTMP));
|
|
PUSH_INSTR(frag, BytecodeStream::GET_VEC, CG::r(r_r), CG::r(bind_cst(2, cg, frag)),
|
|
CG::r(rTMP));
|
|
PUSH_INSTR(frag, BytecodeStream::PUSH, CG::r(rTMP));
|
|
}
|
|
CLOSE_AGG(cg, frag);
|
|
PUSH_INSTR(frag, BytecodeStream::POP, CG::r(rTMP));
|
|
|
|
PUSH_INSTR(frag, BytecodeStream::BUILTIN, cg.find_builtin("array_Xd"),
|
|
CG::r(to_bind.first), CG::r(rTMP));
|
|
}
|
|
PUSH_INSTR(frag, BytecodeStream::POP, CG::r(to_bind.first));
|
|
}
|
|
cg.env().bind(vd->id()->v(), to_bind);
|
|
} else {
|
|
conj.push_back(CG::compile(e, cg, frag));
|
|
}
|
|
}
|
|
}
|
|
|
|
CG_Cond::T CG::compile(Let* let, Mode ctx, CodeGen& cg, CG_Builder& frag) {
|
|
std::vector<CG_Cond::T> conj;
|
|
|
|
cg.env_push();
|
|
|
|
if (let->ann().contains(constants().ann.promise_total)) {
|
|
OPEN_OTHER(cg, frag);
|
|
eval_let_body(let, BytecodeProc::ROOT, cg, frag, conj);
|
|
// int r = CG::force(CG_Cond::forall(ctx, conj), BytecodeProc::ROOT, cg, frag);
|
|
post_cond(cg, frag, CG_Cond::forall(BytecodeProc::ROOT, conj));
|
|
int r = CG::force(CG::compile(let->in(), cg, frag), ctx, cg, frag);
|
|
PUSH_INSTR(frag, BytecodeStream::PUSH, CG::r(r));
|
|
CLOSE_AGG(cg, frag);
|
|
r = GET_REG(cg);
|
|
PUSH_INSTR(frag, BytecodeStream::POP, CG::r(r));
|
|
conj.clear();
|
|
conj.push_back(CG_Cond::reg(r, let->in()->type().ispar()));
|
|
} else {
|
|
eval_let_body(let, BytecodeProc::ROOT, cg, frag, conj);
|
|
conj.push_back(CG::compile(let->in(), cg, frag));
|
|
}
|
|
cg.env_pop();
|
|
return CG_Cond::forall(ctx, conj);
|
|
}
|
|
|
|
int CG::vec2array(std::vector<int> vec, CodeGen& cg, CG_Builder& frag) {
|
|
int sz(vec.size());
|
|
OPEN_VEC(cg, frag);
|
|
for (int ii = 0; ii < sz; ++ii) {
|
|
PUSH_INSTR(frag, BytecodeStream::PUSH, CG::r(vec[ii]));
|
|
}
|
|
CLOSE_AGG(cg, frag);
|
|
int r_vec(GET_REG(cg));
|
|
PUSH_INSTR(frag, BytecodeStream::POP, CG::r(r_vec));
|
|
|
|
return r_vec;
|
|
}
|
|
}; // namespace MiniZinc
|