Something went wrong. Try again.
This repository has no description
Something went wrong. Try again.
13 kB · 445 lines
C++
at develop
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446/* -*- mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*- */
/* * Main authors: * Graeme Gange <graeme.gange@monash.edu> */
/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef __MINIZINC_CODEGEN_INTERNAL_HPP__#define __MINIZINC_CODEGEN_INTERNAL_HPP__// Helper definitions for assorted bytecode generation stuff.#include <minizinc/codegen.hh>
namespace MiniZinc {
template <class V>void PUSH_INSTR_OPERAND(CG_Instr& i, std::vector<V>& vec) { for (auto v : vec) PUSH_INSTR_OPERAND(i, v);}void PUSH_INSTR_OPERAND(CG_Instr& i, CG_Value x) { i.params.push_back(x); }void PUSH_INSTR_OPERAND(CG_Instr& i, CG_ProcID p) { i.params.push_back(CG_Value::proc(p.p)); }void PUSH_INSTR_OPERAND(CG_Instr& i, BytecodeProc::Mode m) { i.params.push_back(CG::i((int)m)); }void PUSH_INSTR_OPERAND(CG_Instr& i, AggregationCtx::Symbol s) { i.params.push_back(CG::i((int)s));}void PUSH_INSTR_OPERANDS(CG_Instr& i) {}
template <class T, typename... Args>void PUSH_INSTR_OPERANDS(CG_Instr& i, T x, Args... args) { PUSH_INSTR_OPERAND(i, x); PUSH_INSTR_OPERANDS(i, args...);}
template <typename... Args>void PUSH_INSTR(CG_Builder& cg, BytecodeStream::Instr i, Args... args) { cg.instrs.push_back(CG_Instr::instr(i)); PUSH_INSTR_OPERANDS(cg.instrs.back(), args...);}
void PUSH_LABEL(CG_Builder& frag, unsigned int label) { frag.instrs.push_back(CG_Instr::label(label));}
// Basic generator manipulation.inline int GET_LABEL(CodeGen& cg) { return cg.current_label_count++; }inline int GET_REG(CodeGen& cg) { return cg.current_reg_count++; }
struct REG { REG(int _r) : r(_r) {} void operator()(CodeGen& cg, CG_Builder& frag) { PUSH_INSTR(frag, BytecodeStream::PUSH, CG::r(r)); } int r;};
// Combinators for slightly safer code generation.struct PUSH_REG { PUSH_REG(int _r) : r(_r) {} void operator()(CodeGen& cg, CG_Builder& frag) { PUSH_INSTR(frag, BytecodeStream::PUSH, CG::r(r)); } int r;};struct PUSH { PUSH(void) {} PUSH_REG operator()(int r) { return PUSH_REG(r); }};
template <class T>struct Retn { Retn(T _x) : x(_x) {}
T operator()(CodeGen& cg, CG_Builder& frag) const { return x; } T x;};struct RETN { RETN() {}
template <class T> Retn<T> operator()(T x) { return Retn<T>(x); }};
/*template<class V, class E>struct _LET { V v; E e; auto operator()(CodeGen& cg, CG_Builder& frag) -> decltype(e(0)(cg)) { // int reg(GET_REG(cg)); PUSH_INSTR(frag, BytecodeStream::OPEN_AGGREGATION, AggregationCtx::VCTX_OTHER); v(cg, frag); PUSH_INSTR(frag, BytecodeStream::POP, CG::r(reg)); PUSH_INSTR(frag, BytecodeStream::CLOSE_AGGREGATION); return e(reg)(cg, frag); }};template<class V, class E>_LET<V, E> LET(V&& v, E&& e) { return _LET<V, E> { std::move(v), std::move(e) }; }*/
// Iterating over various things -- vectors, interleaved vectors, and sets.template <class V, class E>struct _FOREACH { V v; E e; void operator()(CodeGen& cg, CG_Builder& frag) { int r(v(cg, frag)); // Get the register for v. int rB(GET_REG(cg)); int rE(GET_REG(cg)); int rV(GET_REG(cg)); int lblH(GET_LABEL(cg)); int lblE(GET_LABEL(cg)); // Set up the iterators PUSH_INSTR(frag, BytecodeStream::IMMI, CG::i(1), CG::r(rB)); PUSH_INSTR(frag, BytecodeStream::LENGTH, CG::r(r), CG::r(rE)); // Check if the vec is non-empty PUSH_INSTR(frag, BytecodeStream::LEI, CG::r(rB), CG::r(rE), CG::r(rV)); PUSH_INSTR(frag, BytecodeStream::JMPIFNOT, CG::r(rV), CG::l(lblE)); PUSH_LABEL(frag, lblH); // Dereference the iterator PUSH_INSTR(frag, BytecodeStream::GET_VEC, CG::r(r), CG::r(rB), CG::r(rV)); // Emit code for the body e(rV)(cg, frag); // Now increment and loop back. PUSH_INSTR(frag, BytecodeStream::INCI, CG::r(rB)); PUSH_INSTR(frag, BytecodeStream::LEI, CG::r(rB), CG::r(rE), CG::r(rV)); PUSH_INSTR(frag, BytecodeStream::JMPIF, CG::r(rV), CG::l(lblH)); PUSH_LABEL(frag, lblE); }};template <class V, class E>_FOREACH<V, E> FOREACH(V&& v, E&& e) { return _FOREACH<V, E>{std::move(v), std::move(e)};}
// Slightly nicer version of FOREACH.template <class E>void ITER_ARRAY(CodeGen& cg, CG_Builder& frag, int r, E e) { int rV(GET_REG(cg)); int lblH(GET_LABEL(cg)); int lblE(GET_LABEL(cg)); PUSH_INSTR(frag, BytecodeStream::ITER_ARRAY, CG::r(r), CG::l(lblE)); PUSH_LABEL(frag, lblH); PUSH_INSTR(frag, BytecodeStream::ITER_NEXT, CG::r(rV)); e(cg, frag, rV); PUSH_INSTR(frag, BytecodeStream::JMP, CG::l(lblH)); PUSH_LABEL(frag, lblE);}template <class E>void ITER_SET(CodeGen& cg, CG_Builder& frag, int r, E e) { int r_elt(GET_REG(cg)); int r_range_min(GET_REG(cg)); int r_range_max(GET_REG(cg)); int lblH1(GET_LABEL(cg)); int lblH2(GET_LABEL(cg)); int lblE(GET_LABEL(cg)); PUSH_INSTR(frag, BytecodeStream::ITER_VEC, CG::r(r), CG::l(lblE)); PUSH_LABEL(frag, lblH1); PUSH_INSTR(frag, BytecodeStream::ITER_NEXT, CG::r(r_range_min)); PUSH_INSTR(frag, BytecodeStream::ITER_NEXT, CG::r(r_range_max)); PUSH_INSTR(frag, BytecodeStream::ITER_RANGE, CG::r(r_range_min), CG::r(r_range_max), CG::l(lblH1)); PUSH_LABEL(frag, lblH2); PUSH_INSTR(frag, BytecodeStream::ITER_NEXT, CG::r(r_elt)); e(cg, frag, r_elt); PUSH_INSTR(frag, BytecodeStream::JMP, CG::l(lblH2)); PUSH_LABEL(frag, lblE);}
// Same as FOREACH, but when working with a vector of pairs (i.e. sets)/*template<class V, class E>struct _FOREACH2 { V v; E e; auto operator()(CodeGen& cg, CG_Builder& frag) -> decltype(e(0,0)(cg, frag)) { int r(v(cg, frag)); // Get the register for v. int rB(GET_REG(cg)); int rE(GET_REG(cg)); int rV1(GET_REG(cg)); int rV2(GET_REG(cg)); int lblH(GET_LABEL(cg)); int lblE(GET_LABEL(cg)); // Set up the iterators PUSH_INSTR(frag, BytecodeStream::IMMI, CG::i(0), CG::r(rB)); PUSH_INSTR(frag, BytecodeStream::LENGTH, CG::r(r), CG::r(rE)); // Check if the vec is non-empty PUSH_INSTR(frag, BytecodeStream::LTI, CG::r(rB), CG::r(rE), CG::r(rV1)); PUSH_INSTR(frag, BytecodeStream::JMPIFNOT, CG::r(rV1), CG::l(lblE)); PUSH_LABEL(frag, lblH); // Dereference the iterator PUSH_INSTR(frag, BytecodeStream::GET_VEC, CG::r(r), CG::r(rB), CG::r(rV1)); PUSH_INSTR(frag, BytecodeStream::INCI, CG::r(rB)); PUSH_INSTR(frag, BytecodeStream::GET_VEC, CG::r(r), CG::r(rB), CG::r(rV2)); PUSH_INSTR(frag, BytecodeStream::INCI, CG::r(rB)); // Emit code for the body e(rV1, rV2)(cg, frag); // Now increment and loop back. PUSH_INSTR(frag, BytecodeStream::LTI, CG::r(rB), CG::r(rE), CG::r(rV1)); PUSH_INSTR(frag, BytecodeStream::JMPIF, CG::r(rV1), CG::l(lblH)); PUSH_LABEL(frag, lblE); }};template<class V, class E>_FOREACH2<V, E> FOREACH2(V v, E e) { return _FOREACH2<V, E> { v, e }; }
template<class E>struct _FORRANGE { int rL; int rU; E e; _FORRANGE(int _rL, int _rU, E _e) : rL(_rL), rU(_rU), e(_e) { }
auto operator()(CodeGen& cg, CG_Builder& frag) -> decltype(e(0)(cg, frag)) { int lblH(GET_LABEL(cg)); int lblE(GET_LABEL(cg)); int rV(GET_REG(cg)); int rC(GET_REG(cg)); // Check if the range is non-empty. PUSH_INSTR(frag, BytecodeStream::LTI, CG::r(rL), CG::r(rU), CG::r(rC)); PUSH_INSTR(frag, BytecodeStream::JMPIFNOT, CG::r(rC), CG::l(lblE)); PUSH_INSTR(frag, BytecodeStream::MOV, CG::r(rL), CG::r(rV)); PUSH_LABEL(frag, lblH); // Emit code for the body e(rV)(cg, frag); // Now increment and loop back. PUSH_INSTR(frag, BytecodeStream::INCI, CG::r(rV)); PUSH_INSTR(frag, BytecodeStream::LTI, CG::r(rV), CG::r(rU), CG::r(rC)); PUSH_INSTR(frag, BytecodeStream::JMPIF, CG::r(rC), CG::l(lblH)); PUSH_LABEL(frag, lblE); }};template<class E>_FORRANGE<E> FORRANGE(int rL, int rU, E e) { return _FORRANGE<E>(rL, rU, e); }
// We can use FOREACH2 and FORRANGE to iterate over sets.template<class V, class E>struct _FORSET { V v; E e; _FORSET(V&& _v, E&& _e) : v(_v), e(_e) { }
void operator()(CodeGen& cg, CG_Builder& frag) { FOREACH2(v, [this](int rL, int rU) { return FORRANGE(rL, rU, e); })(cg, frag); }};template<class V, class E>_FORSET<V, E> FORSET(V&& v, E&& e) { return _FORSET<V, E>(std::move(v), std::move(e)); }*/
// Non-combinator versions of the iteration generators.// Less safe, because they don't automatically resolve containment, but more convenient// if, say, we need unbounded
struct EmitPost { virtual ~EmitPost(void){}; virtual void emit_post(CG_Builder& frag) = 0; virtual int cont(void) = 0;};
struct Foreach : public EmitPost {#if 0 Foreach(CodeGen& _cg, int _r, int k = 1) : cg(_cg), lblCont(-1), lblH(GET_LABEL(cg)), lblE(GET_LABEL(cg)) , r(_r), rB(GET_REG(cg)), rE(GET_REG(cg)) { assert(k > 0); for(int ii = 0; ii < k; ++ii) rVS.push_back(GET_REG(cg)); } void emit_pre(CG_Builder& frag) { // Set up the iterators PUSH_INSTR(frag, BytecodeStream::IMMI, CG::i(1), CG::r(rB)); PUSH_INSTR(frag, BytecodeStream::LENGTH, CG::r(r), CG::r(rE)); // Check if the vec is non-empty PUSH_INSTR(frag, BytecodeStream::LEI, CG::r(rB), CG::r(rE), CG::r(rVS[0])); PUSH_INSTR(frag, BytecodeStream::JMPIFNOT, CG::r(rVS[0]), CG::l(lblE)); PUSH_LABEL(frag, lblH); // Dereference the iterator for(int rv : rVS) { PUSH_INSTR(frag, BytecodeStream::GET_VEC, CG::r(r), CG::r(rB), CG::r(rv)); PUSH_INSTR(frag, BytecodeStream::INCI, CG::r(rB)); } }
void emit_post(CG_Builder& frag) { // Now increment and loop back. if(lblCont != -1) PUSH_LABEL(frag, lblCont); PUSH_INSTR(frag, BytecodeStream::LEI, CG::r(rB), CG::r(rE), CG::r(rVS[0])); PUSH_INSTR(frag, BytecodeStream::JMPIF, CG::r(rVS[0]), CG::l(lblH)); PUSH_LABEL(frag, lblE); }
int val(void) const { return rVS[0]; } int val(int i) const { return rVS[i]; } int cont(void) { if(lblCont == -1) lblCont = GET_LABEL(cg); return lblCont; }
CodeGen& cg; int lblCont; int lblH; int lblE; int r; int rB; int rE; std::vector<int> rVS;#else Foreach(CodeGen& _cg, int _r, int k = 1) : cg(_cg), lblH(GET_LABEL(cg)), lblE(GET_LABEL(cg)), r(_r) { assert(k > 0); for (int ii = 0; ii < k; ++ii) rVS.push_back(GET_REG(cg)); }
void emit_pre(CG_Builder& frag) { // Set up the iterators PUSH_INSTR(frag, BytecodeStream::ITER_ARRAY, CG::r(r), CG::l(lblE)); PUSH_LABEL(frag, lblH); // Dereference the iterator for (int rv : rVS) { PUSH_INSTR(frag, BytecodeStream::ITER_NEXT, CG::r(rv)); } }
void emit_post(CG_Builder& frag) { // Now increment and loop back. PUSH_INSTR(frag, BytecodeStream::JMP, CG::l(lblH)); PUSH_LABEL(frag, lblE); }
int val(void) const { return rVS[0]; } int val(int i) const { return rVS[i]; } int cont(void) { return lblH; }
CodeGen& cg; int lblH; int lblE; int r; std::vector<int> rVS;#endif};
struct Forrange : public EmitPost {#if 0 Forrange(CodeGen& _cg, int _rL, int _rU) : cg(_cg) , lblH(GET_LABEL(cg)), lblE(GET_LABEL(cg)), lblCont(-1) , rL(_rL), rU(_rU) , rV(GET_REG(cg)), rC(GET_REG(cg)) {
} void emit_pre(CG_Builder& frag) { // Check if the range is non-empty. PUSH_INSTR(frag, BytecodeStream::LEI, CG::r(rL), CG::r(rU), CG::r(rC)); PUSH_INSTR(frag, BytecodeStream::JMPIFNOT, CG::r(rC), CG::l(lblE)); PUSH_INSTR(frag, BytecodeStream::MOV, CG::r(rL), CG::r(rV)); PUSH_LABEL(frag, lblH); }
void emit_post(CG_Builder& frag) { // Now increment and loop back. if(lblCont != -1) PUSH_LABEL(frag, lblCont); PUSH_INSTR(frag, BytecodeStream::INCI, CG::r(rV)); PUSH_INSTR(frag, BytecodeStream::LEI, CG::r(rV), CG::r(rU), CG::r(rC)); PUSH_INSTR(frag, BytecodeStream::JMPIF, CG::r(rC), CG::l(lblH)); PUSH_LABEL(frag, lblE); } int val(void) const { return rV; } int cont(void) { if(lblCont == -1) lblCont = GET_LABEL(cg); return lblCont; }
CodeGen& cg; int lblH; int lblE; int lblCont; int rL; int rU; int rV; int rC;#else Forrange(CodeGen& _cg, int _rL, int _rU) : cg(_cg), lblH(GET_LABEL(cg)), lblE(GET_LABEL(cg)), rL(_rL), rU(_rU), rV(GET_REG(cg)) {}
void emit_pre(CG_Builder& frag) { // Set up the loop, and get the first element. PUSH_INSTR(frag, BytecodeStream::ITER_RANGE, CG::r(rL), CG::r(rU), CG::l(lblE)); PUSH_LABEL(frag, lblH); PUSH_INSTR(frag, BytecodeStream::ITER_NEXT, CG::r(rV)); }
void emit_post(CG_Builder& frag) { // Jump back if we made it. PUSH_INSTR(frag, BytecodeStream::JMP, CG::l(lblH)); PUSH_LABEL(frag, lblE); }
int val(void) const { return rV; } int cont(void) { return lblH; }
CodeGen& cg; int lblH; int lblE; int rL; int rU; int rV;#endif};
struct Forset : public EmitPost { Forset(CodeGen& cg, int r) : ranges(cg, r, 2), values(cg, ranges.val(0), ranges.val(1)) {}
void emit_pre(CG_Builder& frag) { ranges.emit_pre(frag); values.emit_pre(frag); } void emit_post(CG_Builder& frag) { values.emit_post(frag); ranges.emit_post(frag); }
int val(void) const { return values.val(); } int cont(void) { return values.cont(); }
Foreach ranges; Forrange values;};
}; // namespace MiniZinc
#endif