A set of benchmarks to compare a new prototype MiniZinc implementation
1/* -*- mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*- */
2
3/*
4 * Main authors:
5 * Graeme Gange <graeme.gange@monash.edu>
6 */
7
8/* This Source Code Form is subject to the terms of the Mozilla Public
9 * License, v. 2.0. If a copy of the MPL was not distributed with this
10 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
11
12#ifndef __MINIZINC_CODEGEN_HH__
13#define __MINIZINC_CODEGEN_HH__
14
15// Simple single-pass bytecode compiler for MiniZinc.
16
17#include <minizinc/codegen_support.hh>
18#include <minizinc/flatten_internal.hh>
19#include <minizinc/interpreter.hh>
20
21#include <iostream>
22#include <set>
23#include <vector>
24
25namespace MiniZinc {
26
27struct CodeGen;
28
29// Location is either a register or global.
30class Loc {
31private:
32 Loc(int _x) : x(_x) {}
33
34public:
35 static Loc reg(int r) { return r << 1; }
36 static Loc global(int g) { return (g << 1) | 1; }
37
38 inline bool is_reg(void) const { return !(x & 1); }
39 inline bool is_global(void) const { return x & 1; }
40 inline int index(void) const { return x >> 1; }
41
42 int x;
43};
44
45// Code generators for structures.
46// Expressions can be executed in three modes:
47// - If Boolean, it is always just executed.
48// - If non-Boolean but total, it is similarly just executed.
49// - If non-Boolean and partial, we first evaluate its partiality
50// constraints in the enclosing Boolean expression, _then_
51// evaluate its numeric component.
52// For any function that is not total, we generate two procedures:
53// one which is
54// A slightly more structured representation for code generation.
55class CG_Value {
56public:
57 enum CG_ValueKind { V_Immi, V_Global, V_Reg, V_Proc, V_Label };
58
59protected:
60 CG_Value(CG_ValueKind _kind, int _value) : kind(_kind), value(_value) {}
61
62public:
63 CG_Value(void) : kind(V_Immi), value(0) {}
64 static CG_Value reg(int r) { return CG_Value(V_Reg, r); }
65 static CG_Value global(int r) { return CG_Value(V_Global, r); }
66 static CG_Value immi(long long int r) { return CG_Value(V_Immi, r); }
67 static CG_Value proc(int p) { return CG_Value(V_Proc, p); }
68 static CG_Value label(int l) { return CG_Value(V_Label, l); }
69
70 CG_ValueKind kind;
71 long long int value;
72};
73
74struct CG_Instr {
75protected:
76 CG_Instr(unsigned int _tag) : tag(_tag) {}
77
78public:
79 static CG_Instr instr(BytecodeStream::Instr i) { return static_cast<unsigned int>(i) << 1; }
80 static CG_Instr label(unsigned int l) { return (l << 1) + 1; }
81
82 unsigned int tag;
83 std::vector<CG_Value> params;
84};
85
86struct CG_ProcID {
87protected:
88 CG_ProcID(int _p) : p(_p) {}
89
90public:
91 static CG_ProcID builtin(int b) { return CG_ProcID((b << 1) | 1); }
92 static CG_ProcID proc(int p) { return CG_ProcID(p << 1); }
93 bool is_builtin(void) const { return p & 1; }
94 unsigned int id(void) const { return p >> 1; }
95
96 static CG_ProcID of_val(CG_Value v) {
97 assert(v.kind == CG_Value::V_Proc);
98 return CG_ProcID(v.value);
99 }
100
101 unsigned int p;
102};
103
104struct CG_Builder {
105 std::vector<CG_Instr> instrs;
106
107 void append(CG_Builder& o) {
108 instrs.insert(instrs.end(), o.instrs.begin(), o.instrs.end());
109 o.instrs.clear();
110 }
111 void clear(void) { instrs.clear(); }
112};
113
114// When we bind a non-Boolean expression, we also construct
115// a set of conditions we need to insert into any use-contexts.
116
117// We use CG_Cond to track the conditionality of values.
118struct CG_Cond {
119 // FIXME: Currently not GC'd, so this will leak a bunch of memory.
120 enum Kind { CC_Reg, CC_Call, CC_And };
121 class C_Reg;
122 class C_Call;
123 class C_And;
124
125 struct cond_reg {
126 cond_reg(void) : is_root(0), is_seen(0), is_par(0), reg(-1) {}
127 cond_reg(int _reg, bool _par) : is_root(0), is_seen(0), is_par(_par), reg(_reg) {}
128
129 int operator*(void) const {
130 assert(reg >= 0);
131 return reg;
132 }
133 bool has_reg(void) const { return reg >= 0; }
134
135 int is_root : 1;
136 int is_seen : 1;
137 int is_par : 1;
138 int reg : 29;
139 };
140
141 class _T {
142 public:
143 cond_reg reg[2];
144
145 _T(void) {}
146 _T(int r, bool is_par) {
147 reg[0].reg = r;
148 reg[0].is_par = is_par;
149 }
150
151 virtual Kind kind(void) const = 0;
152 };
153
154 class T {
155 T(uintptr_t _p) : p(_p) {}
156
157 public:
158 T(void) : p(0) {}
159
160 static T ttt(void) { return T(0); }
161 static T fff(void) { return T(1); }
162 static T of_ptr(_T* p) { return T(reinterpret_cast<uintptr_t>(p)); }
163
164 T operator~(void) const { return T(p ^ 1); }
165 T operator^(bool b) const { return T(p ^ b); }
166
167 bool sign(void) const { return p & 1; }
168 _T* get(void) const { return reinterpret_cast<_T*>(p & ~((uintptr_t)1)); }
169
170 uintptr_t p;
171 };
172
173 static T ttt(void) { return T::ttt(); }
174 static T fff(void) { return T::fff(); }
175
176 class C_Reg : public _T {
177 public:
178 static const Kind _kind = CC_Reg;
179 Kind kind(void) const { return _kind; }
180 C_Reg(int reg, bool is_par) : _T(reg, is_par) {}
181 };
182 class C_Call : public _T {
183 public:
184 static const Kind _kind = CC_Call;
185 Kind kind(void) const { return _kind; }
186
187 C_Call(ASTString _ident, BytecodeProc::Mode _m, bool _cse, const std::vector<Type>& _ty,
188 const std::vector<CG_Value>& _params)
189 : ident(_ident), m(std::move(_m)), ty(_ty), cse(_cse), params(_params) {}
190
191 ASTString ident;
192 // Return Types + argument types
193 BytecodeProc::Mode m;
194 bool cse;
195 std::vector<Type> ty;
196 std::vector<CG_Value> params;
197 };
198 class C_And : public _T {
199 public:
200 static const Kind _kind = CC_And;
201 Kind kind(void) const { return _kind; }
202
203 C_And(BytecodeProc::Mode _m, std::vector<T>& _children) : m(_m), children(_children) {
204 assert(children.size() > 1);
205 }
206
207 BytecodeProc::Mode m;
208 std::vector<T> children;
209 };
210
211 static T reg(int r, bool is_par) { return T::of_ptr(new C_Reg(r, is_par)); }
212
213 static T call(ASTString ident, BytecodeProc::Mode m, bool cse, const std::vector<Type>& ty,
214 const std::vector<CG_Value>& params) {
215 return _call(ident, m, cse, ty, params);
216 }
217
218 static T _call(ASTString ident, BytecodeProc::Mode m, bool cse, const std::vector<Type>& ty,
219 const std::vector<CG_Value>& params) {
220 return T::of_ptr(new C_Call(ident, m, cse, ty, params));
221 }
222
223 template <typename... Args>
224 static T _forall(BytecodeProc::Mode m, std::vector<T>& args, T next, Args... rest) {
225 args.push_back(next);
226 return _forall(m, args, rest...);
227 }
228 template <class It>
229 static void clear_seen(It b, It e) {
230 for (; b != e; ++b) {
231 if (!b->get()) continue;
232 b->get()->reg[b->sign()].is_seen = false;
233 }
234 }
235 static T _forall(BytecodeProc::Mode m, std::vector<T>& args) {
236 size_t count = 0;
237 for (T x : args) {
238 _T* p(x.get());
239 if (!p) {
240 // Either true or false.
241 if (x.sign()) {
242 clear_seen(args.begin(), args.end());
243 return T::fff();
244 }
245 continue;
246 }
247 // Otherwise, check if we've already seen this or its negation.
248 if (p->reg[1 - x.sign()].is_seen || p->reg[1 - x.sign()].is_root) {
249 clear_seen(args.begin(), args.end());
250 return T::fff();
251 }
252 if (p->reg[x.sign()].is_seen || p->reg[x.sign()].is_root) continue;
253 // Haven't seen this yet, so save and mark it.
254 p->reg[x.sign()].is_seen = true;
255 args[count] = x;
256 count++;
257 }
258 clear_seen(args.begin(), args.end());
259 args.resize(count);
260 if (count == 0) {
261 return T::ttt();
262 }
263 if (count == 1) {
264 return args[0];
265 }
266 return T::of_ptr(new C_And(m, args));
267 }
268
269 template <typename... Args>
270 static T forall(BytecodeProc::Mode m, Args... args) {
271 std::vector<CG_Cond::T> vec;
272 return _forall(m, vec, args...);
273 }
274 static T forall(BytecodeProc::Mode m, std::vector<T>& args) { return _forall(m, args); }
275
276 template <typename... Args>
277 static T _exists(BytecodeProc::Mode m, std::vector<T>& args, T next, Args... rest) {
278 args.push_back(next);
279 return _exists(m, args, rest...);
280 }
281 static T _exists(BytecodeProc::Mode m, std::vector<T>& args);
282
283 template <typename... Args>
284 static T exists(BytecodeProc::Mode m, Args... args) {
285 std::vector<T> vec;
286 return _exists(m, vec, args...);
287 }
288 static T exists(BytecodeProc::Mode m, std::vector<T>& args) { return _exists(m, args); }
289};
290
291// An environment should never outlive its parent.
292template <class T>
293class CG_Env {
294private:
295 CG_Env(CG_Env<T>* _p) : p(_p), sz(p ? p->sz : 0) {}
296
297 // Forbid copy and assignment operators.
298 CG_Env(const CG_Env& _o) = delete;
299 CG_Env& operator=(const CG_Env& o) = delete;
300
301public:
302 CG_Env(void) : p(nullptr), sz(0) {}
303 CG_Env(CG_Env&& o)
304 : bindings(std::move(o.bindings)),
305 available(std::move(o.available)),
306 available_csts(std::move(o.available_csts)),
307 available_ranges(std::move(o.available_ranges)),
308 cached_conds(std::move(o.cached_conds)),
309 occurs(std::move(o.occurs)),
310 p(o.p),
311 sz(o.sz) {}
312
313 void clear_cached_conds() {
314 for (CG_Cond::T c : cached_conds) {
315 CG_Cond::_T* p(c.get());
316 bool sign(c.sign());
317 p->reg[sign].reg = -1;
318 }
319 cached_conds.clear();
320 }
321 void record_cached_cond(CG_Cond::T c) { cached_conds.push_back(c); }
322
323 class NotFound : public std::exception {
324 public:
325 NotFound(void) {}
326 };
327
328 T lookup(const ASTString& s) const {
329 auto it(bindings.find(s));
330 if (it != bindings.end()) return (*it).second;
331 if (!p) throw NotFound();
332
333 return p->lookup(s);
334 }
335
336 void bind(const ASTString& s, T val) {
337 auto it(bindings.find(s));
338 if (it != bindings.end())
339 bindings.erase(it);
340 else
341 sz++;
342 bindings.insert(std::make_pair(s, val));
343
344 // Invalidate any cached values mentioning s.
345 auto o_it(occurs.find(s));
346 if (o_it != occurs.end()) {
347 // We're lazy here, in that we don't remove e from
348 // other occurs lists.
349 for (Expression* e : (*o_it).second) available.erase(e);
350 occurs.erase(o_it);
351 }
352 }
353
354 // Check whether e is already available in an enclosing environment.
355 T cache_lookup(Expression* e, ASTStSet e_scope) {
356 auto it(available.find(e));
357 // Anything in the current table is hasn't been invalidated.
358 if (it != available.end()) {
359 return (*it).second;
360 }
361 if (!p) throw NotFound();
362
363 // If there's a parent table, check whether we've re-bound
364 // something in its scope.
365 for (auto p : bindings) {
366 if (e_scope.find(p.first) != e_scope.end()) throw NotFound();
367 }
368 // If the scope hasn't been invalidated, check the parent.
369 return p->cache_lookup(e, e_scope);
370 }
371
372 void cache_store(Expression* e, ASTStSet e_scope, T val) {
373 available.insert(std::make_pair(e, val));
374 // Add e to the occurs lists for variables in its scope.
375 for (ASTString s : e_scope) occurs[s].push_back(e);
376 }
377
378 bool cache_lookup_cst(int x, T& ret) {
379 auto it(available_csts.find(x));
380 // Anything in the current table is hasn't been invalidated.
381 if (it != available_csts.end()) {
382 ret = (*it).second;
383 return true;
384 }
385 if (!p) return false;
386 return p->cache_lookup_cst(x, ret);
387 }
388 void cache_store_cst(int x, T val) { available_csts.insert(std::make_pair(x, val)); }
389
390 static uint64_t range_key(int l, int u) { return (((uint64_t)u) << 32ull | (uint64_t)l); }
391 bool cache_lookup_range(int l, int u, T& ret) {
392 auto it(available_ranges.find(range_key(l, u)));
393 // Anything in the current table is hasn't been invalidated.
394 if (it != available_ranges.end()) {
395 ret = (*it).second;
396 return true;
397 }
398 if (!p) return false;
399 return p->cache_lookup_range(l, u, ret);
400 }
401 void cache_store_range(int l, int u, T val) {
402 available_ranges.insert(std::make_pair(range_key(l, u), val));
403 }
404
405 static CG_Env* spawn(CG_Env* p) { return new CG_Env<T>(p); }
406
407 unsigned int size(void) const { return sz; }
408
409 typename ASTStringMap<T>::t bindings;
410
411 typename ExprMap<T>::t available;
412 std::unordered_map<int, T> available_csts;
413 std::unordered_map<uint64_t, T> available_ranges;
414 // std::unordered_map<std::pair<int, int>, T> available_ranges;
415 typename ASTStringMap<std::vector<Expression*>>::t occurs;
416
417 std::vector<CG_Cond::T> cached_conds;
418
419 // Parent environment.
420 CG_Env<T>* p;
421 unsigned int sz;
422};
423
424struct CG {
425 typedef std::pair<int, CG_Cond::T> Binding;
426
427 // This is currently (probably) sound, but unnecessarily weak. If an expression ever appears in
428 // root context, the root appearance should dominate.
429 struct Mode {
430 enum Strength { Root = 0, Imp = 1, Fun = 2 };
431 Mode(BytecodeProc::Mode _m) : m(_m) {}
432 Mode(Strength s, bool is_neg) {
433 switch (s) {
434 case Root:
435 m = is_neg ? BytecodeProc::ROOT_NEG : BytecodeProc::ROOT;
436 break;
437 case Imp:
438 m = is_neg ? BytecodeProc::IMP_NEG : BytecodeProc::IMP;
439 break;
440 case Fun:
441 m = is_neg ? BytecodeProc::FUN_NEG : BytecodeProc::FUN;
442 break;
443 }
444 }
445
446 bool is_neg(void) const {
447 switch (m) {
448 case BytecodeProc::ROOT_NEG:
449 case BytecodeProc::IMP_NEG:
450 case BytecodeProc::FUN_NEG:
451 return true;
452 default:
453 return false;
454 }
455 }
456
457 Strength strength(void) const {
458 switch (m) {
459 case BytecodeProc::ROOT:
460 case BytecodeProc::ROOT_NEG:
461 return Root;
462 case BytecodeProc::IMP:
463 case BytecodeProc::IMP_NEG:
464 return Imp;
465 case BytecodeProc::FUN:
466 case BytecodeProc::FUN_NEG:
467 return Fun;
468 default:
469 throw InternalError("Unexpected mode.");
470 }
471 }
472 bool is_root(void) const { return strength() == Root; }
473
474 Mode join(Mode o) {
475 if (is_neg() != o.is_neg()) return BytecodeProc::FUN;
476 return Mode(std::max(strength(), o.strength()), is_neg());
477 }
478 bool is_submode(Mode o) { return is_neg() == o.is_neg() && strength() <= o.strength(); }
479
480 // Half
481 Mode operator+(void) const { return Mode(strength() == Root ? Imp : strength(), is_neg()); }
482 Mode operator-(void) const { return Mode(strength(), !is_neg()); }
483
484 // Switch the current mode to functional.
485 Mode operator*(void) const { return Mode(Fun, is_neg()); }
486
487 operator BytecodeProc::Mode() const { return m; }
488
489 BytecodeProc::Mode m;
490 };
491
492 inline static CG_Value g(int g) { return CG_Value::global(g); }
493 inline static CG_Value r(int r) { return CG_Value::reg(r); }
494 inline static CG_Value i(int i) { return CG_Value::immi(i); }
495 inline static CG_Value l(int l) { return CG_Value::label(l); }
496
497 // Place a non-Boolean value in a register, and collect its partiality.
498 static Binding bind(Expression* e, CodeGen& cg, CG_Builder& frag);
499 // Compile a Boolean expression into a condition.
500 static CG_Cond::T compile(Expression* e, CodeGen& cg, CG_Builder& frag);
501 // Reify a condion, putting it in a register.
502 static int force(CG_Cond::T cond, Mode ctx, CodeGen& cg, CG_Builder& frag);
503 // Force compiled expression or Bind value depending on type
504 static Binding force_or_bind(Expression* e, Mode ctx, CodeGen& cg, CG_Builder& frag);
505 static int force_or_bind(Expression* e, Mode ctx, std::vector<CG_Cond::T>& cond, CodeGen& cg,
506 CG_Builder& frag);
507
508 static void run(CodeGen& cg, Model* m);
509
510 static int locate_immi(int x, CodeGen& cg, CG_Builder& frag);
511
512 static int vec2array(std::vector<int> vec, CodeGen& cg, CG_Builder& frag);
513
514 static Binding bind(Id* x, Mode ctx, CodeGen& cg, CG_Builder& frag);
515 static Binding bind(SetLit* l, Mode ctx, CodeGen& cg, CG_Builder& frag);
516 static Binding bind(ArrayLit* a, Mode ctx, CodeGen& cg, CG_Builder& frag);
517 static Binding bind(ArrayAccess* a, Mode ctx, CodeGen& cg, CG_Builder& frag);
518 static Binding bind(ITE* ite, Mode ctx, CodeGen& cg, CG_Builder& frag);
519 static Binding bind(BinOp* op, Mode ctx, CodeGen& cg, CG_Builder& frag);
520 static Binding bind(UnOp* op, Mode ctx, CodeGen& cg, CG_Builder& frag);
521 static Binding bind(Call* call, Mode ctx, CodeGen& cg, CG_Builder& frag);
522 static Binding bind(Let* let, Mode ctx, CodeGen& cg, CG_Builder& frag);
523 static Binding bind(Comprehension* let, Mode ctx, CodeGen& cg, CG_Builder& frag);
524
525 static CG_Cond::T compile(Id* x, Mode ctx, CodeGen& cg, CG_Builder& frag);
526 static CG_Cond::T compile(ArrayAccess* a, Mode ctx, CodeGen& cg, CG_Builder& frag);
527 static CG_Cond::T compile(ITE* ite, Mode ctx, CodeGen& cg, CG_Builder& frag);
528 static CG_Cond::T compile(BinOp* op, Mode ctx, CodeGen& cg, CG_Builder& frag);
529 static CG_Cond::T compile(UnOp* op, Mode ctx, CodeGen& cg, CG_Builder& frag);
530 static CG_Cond::T compile(Call* call, Mode ctx, CodeGen& cg, CG_Builder& frag);
531 static CG_Cond::T compile(Let* let, Mode ctx, CodeGen& cg, CG_Builder& frag);
532 static CG_Cond::T compile(Comprehension* let, Mode ctx, CodeGen& cg, CG_Builder& frag);
533};
534
535inline CG_Cond::T CG_Cond::_exists(BytecodeProc::Mode m, std::vector<T>& args) {
536 size_t count = 0;
537 for (T x : args) {
538 _T* p(x.get());
539 if (!p) {
540 // Either true or false.
541 if (!x.sign()) {
542 clear_seen(args.begin(), args.end());
543 return T::ttt();
544 }
545 continue;
546 }
547 // Otherwise, check if we've already seen this or its negation.
548 if (p->reg[1 - x.sign()].is_seen || p->reg[x.sign()].is_root) {
549 clear_seen(args.begin(), args.end());
550 return T::ttt();
551 }
552 if (p->reg[x.sign()].is_seen || p->reg[1 - x.sign()].is_root) {
553 continue;
554 }
555 // Haven't seen this yet, so save and mark it.
556 p->reg[x.sign()].is_seen = true;
557 args[count] = x;
558 count++;
559 }
560 clear_seen(args.begin(), args.end());
561 args.resize(count);
562 if (count == 0) {
563 return T::fff();
564 }
565 if (count == 1) {
566 return args[0];
567 }
568 return ~T::of_ptr(new C_And(-CG::Mode(m), args));
569}
570
571// Partially compiled bytecode.
572struct CG_Proc {
573 typedef std::vector<CG_Instr> body_t;
574
575 static unsigned char mode_mask(BytecodeProc::Mode m) {
576 return 1 << (static_cast<unsigned char>(m));
577 }
578 struct mode_iterator {
579 mode_iterator(unsigned int _x) : x(_x) {}
580 bool operator!=(const mode_iterator& o) const { return x != o.x; }
581 BytecodeProc::Mode operator*(void) const {
582 assert(x);
583 return static_cast<BytecodeProc::Mode>(find_lsb(x));
584 }
585 mode_iterator& operator++(void) {
586 x &= (x - 1);
587 return *this;
588 }
589
590 unsigned int x;
591 };
592 mode_iterator begin(void) { return mode_iterator(available_modes); }
593 mode_iterator end(void) { return mode_iterator(0); }
594
595 CG_Proc(std::string _ident, int _arity) : ident(_ident), arity(_arity), available_modes(0) {}
596
597 CG_Proc(CG_Proc&& o) : ident(o.ident), arity(o.arity), available_modes(o.available_modes) {
598 unsigned char rm(available_modes);
599 while (rm) {
600 unsigned char m(find_lsb(rm));
601 rm &= (rm - 1);
602 new (_body + m) body_t(std::move(o._body[m]));
603 o._body[m].~body_t();
604 }
605 o.available_modes = 0;
606 }
607
608 std::string ident;
609 unsigned int arity;
610
611 bool is_available(BytecodeProc::Mode m) const { return available_modes & mode_mask(m); }
612
613 std::vector<CG_Instr>& body(BytecodeProc::Mode m) {
614 static_assert(BytecodeProc::MAX_MODE < 8 * sizeof(unsigned char),
615 "Too many modes to to represent as unsigned char.");
616
617 if (!(available_modes & mode_mask(m))) {
618 available_modes |= mode_mask(m);
619 new (_body + m) body_t();
620 }
621 return _body[m];
622 }
623
624 unsigned char available_modes;
625 std::vector<CG_Instr> _body[BytecodeProc::MAX_MODE + 1];
626};
627
628// For identifying a call...
629struct CallSig {
630 ASTString id;
631 std::vector<Type> params;
632
633 CallSig(ASTString _id, std::vector<Type> _params) : id(_id) {
634 // Normalize the call types to par.
635 for (Type p : _params) {
636 p.ti(Type::TI_PAR);
637 params.push_back(p);
638 }
639 }
640
641 struct HashSig {
642 size_t operator()(const CallSig& c) const { return c.hash(); }
643 };
644 struct EqSig {
645 bool operator()(const CallSig& x, const CallSig& y) const { return x == y; }
646 };
647
648 bool operator==(const CallSig& o) const {
649 if (id != o.id || params.size() != o.params.size()) return false;
650 for (int ii = 0; ii < params.size(); ++ii) {
651 if (params[ii] != o.params[ii]) return false;
652 }
653 return true;
654 }
655
656 size_t hash(void) const {
657 size_t h(id.hash());
658 for (int ii = 0; ii < params.size(); ++ii)
659 h ^= params[ii].toInt() + 0x9e3779b9 + (h << 6) + (h >> 2);
660 return h;
661 }
662};
663
664template <class T>
665struct SigMap {
666 typedef std::unordered_map<CallSig, T, CallSig::HashSig, CallSig::EqSig> t;
667};
668
669struct CG_FunMap {
670 struct CG_FunDefn {
671 CG_FunDefn(ASTString _id) : id(_id) {}
672
673 ASTString id;
674 std::vector<FunctionI*> bodies;
675 };
676
677 ASTStringMap<unsigned int>::t id_map;
678 std::vector<CG_FunDefn> functions;
679
680 void add_body(FunctionI* f) {
681 // FIXME: Currently discarding anything with var-set.
682 ASTExprVec<VarDecl> params(f->params());
683 for (int ii = 0; ii < params.size(); ++ii) {
684 if (params[ii]->type().is_set() && !params[ii]->type().ispar()) return;
685 }
686
687 unsigned int fun_id;
688 ASTString id(f->id());
689 auto it(id_map.find(id));
690 if (it != id_map.end()) {
691 fun_id = (*it).second;
692 } else {
693 fun_id = functions.size();
694 id_map.insert(std::make_pair(id, fun_id));
695 functions.push_back(CG_FunDefn(id));
696 }
697 functions[fun_id].bodies.push_back(f);
698 }
699
700 bool dominates(FunctionI* f, FunctionI* g) {
701 auto f_params(f->params());
702 auto g_params(g->params());
703 assert(f_params.size() == g_params.size());
704 int sz(f_params.size());
705 for (int ii = 0; ii < sz; ++ii) {
706 if (!Type::bt_subtype(f_params[ii]->type(), g_params[ii]->type(), false)) return false;
707 }
708 return true;
709 }
710 void filter_bodies(std::vector<FunctionI*>::iterator& dest, std::vector<FunctionI*>::iterator b,
711 std::vector<FunctionI*>::iterator e, int arg, int sz) {
712 if (!(b != e)) // Empty partition
713 return;
714 if (arg == sz) {
715 // Find the best candidate between b and e, add it to the output.
716 // FIXME
717 auto best(b);
718 for (++b; b != e; ++b) {
719 if (dominates(*b, *best)) best = b;
720 }
721 (*dest) = (*best);
722 ++dest;
723 return;
724 }
725 // Otherwise, partition the arguments and recurse.
726 std::vector<FunctionI*>::iterator mid =
727 std::partition(b, e, [arg](FunctionI* b) { return b->params()[arg]->type().ispar(); });
728 filter_bodies(dest, b, mid, arg + 1, sz);
729 filter_bodies(dest, mid, e, arg + 1, sz);
730 }
731
732 std::vector<FunctionI*> get_bodies(unsigned int fun_id, const std::vector<Type>& args) {
733 CG_FunDefn& defn(functions[fun_id]);
734
735 // First, restrict consideration to feasible specialisations.
736 std::vector<FunctionI*> candidates;
737 int sz = args.size();
738 for (FunctionI* b : defn.bodies) {
739 ASTExprVec<VarDecl> b_params(b->params());
740 if (b_params.size() == sz) {
741 for (int pi = 0; pi < sz; ++pi) {
742 if (!args[pi].isSubtypeOf(b_params[pi]->type(), false)) goto get_bodies_continue;
743 }
744 // Can coerce args to b_params.
745 candidates.push_back(b);
746 }
747 get_bodies_continue:
748 continue;
749 }
750 // Now collect the relevant par-based refinements.
751 std::vector<FunctionI*>::iterator dest(candidates.begin());
752 filter_bodies(dest, candidates.begin(), candidates.end(), 0, sz);
753 candidates.erase(dest, candidates.end());
754 return candidates;
755 }
756
757 std::vector<FunctionI*> get_bodies(const ASTString& ident, const std::vector<Type>& args) {
758 auto it(id_map.find(ident));
759 if (it == id_map.end()) return {};
760 unsigned int fun_id((*it).second);
761 return get_bodies(fun_id, args);
762 }
763
764 std::vector<FunctionI*> get_bodies(Call* call) {
765 // Normalize all types to par, so isSubtype does what we want.
766 std::vector<Type> args;
767 int sz(call->n_args());
768 for (int ii = 0; ii < sz; ++ii) {
769 Type arg(call->arg(ii)->type());
770 arg.ti(Type::TI_PAR);
771 args.push_back(arg);
772 }
773 return get_bodies(call->id(), args);
774 }
775
776 std::pair<bool, ASTString> defines_mode(const ASTString& ident, const std::vector<Type>& args,
777 BytecodeProc::Mode mode) {
778 GCLock lock;
779 ASTString nident;
780 switch (mode) {
781 case BytecodeProc::ROOT_NEG:
782 nident = ident.str() + "_neg";
783 break;
784 case BytecodeProc::FUN:
785 nident = ident.str() + "_reif";
786 break;
787 case BytecodeProc::FUN_NEG:
788 nident = ident.str() + "_neg_reif";
789 break;
790 case BytecodeProc::IMP:
791 nident = ident.str() + "_imp";
792 break;
793 case BytecodeProc::IMP_NEG:
794 nident = ident.str() + "_neg_imp";
795 break;
796 default:
797 return {false, ASTString("")};
798 }
799 auto it(id_map.find(nident));
800 if (it == id_map.end()) {
801 return {false, nident};
802 }
803 unsigned int fun_id((*it).second);
804 std::vector<Type> reif_args;
805 reif_args.reserve(args.size() + 1);
806 for (int ii = 0; ii < args.size(); ++ii) {
807 Type arg(args[ii]);
808 arg.ti(Type::TI_PAR);
809 reif_args.push_back(arg);
810 }
811 if (mode != BytecodeProc::ROOT && mode != BytecodeProc::ROOT_NEG) {
812 reif_args.push_back(Type::parbool());
813 }
814 return {!get_bodies(fun_id, reif_args).empty(), nident};
815 }
816};
817
818struct CodeGen {
819 typedef unsigned int proc_id;
820 typedef unsigned int reg_id;
821 typedef std::pair<int, CG_Cond::T> Binding;
822 CodeGen(void)
823 : /*entry_proc(0)
824 ,*/
825 current_env(new CG_Env<Binding>()),
826 num_globals(0),
827 current_reg_count(0),
828 current_label_count(0) {
829 register_builtins();
830 }
831
832 void append(int proc, BytecodeProc::Mode m, CG_Builder& b) {
833 std::vector<CG_Instr>& body(bytecode[proc].body(m));
834
835 body.insert(body.end(), b.instrs.begin(), b.instrs.end());
836 b.clear();
837 }
838
839 void env_push(void) { current_env = CG_Env<Binding>::spawn(current_env); }
840 void env_pop(void) {
841 assert(current_env);
842 CG_Env<Binding>* c(current_env);
843 c->clear_cached_conds();
844 current_env = current_env->p;
845 delete c;
846 }
847
848 // Consult/update the available expressions in the current environment.
849 Binding cache_lookup(Expression* e);
850 void cache_store(Expression* e, Binding l);
851
852 // Function resolution
853 void register_function(FunctionI* f) { fun_map.add_body(f); }
854
855 CG_ProcID resolve_fun(FunctionI* f, bool reserved_name = false);
856 // CG_ProcID resolve_fun_pred(FunctionI* f);
857 // CG_ProcID resolve_pred_def(FunctionI* f, BytecodeProc::Mode m);
858
859 std::vector<CG_Proc> bytecode; // Bytecode we've built
860
861 inline CG_Env<Binding>& env(void) { return *current_env; }
862
863 CG_Env<Binding>* current_env; // Where are things in scope?
864 // Id -> <Register, input?>
865 std::unordered_map<VarDecl*, std::pair<int, bool>> globals_env;
866 int num_globals;
867 std::vector<FunctionI*> req_solver_predicates;
868
869 int add_global(VarDecl* vd, bool input = false) {
870 globals_env.insert(std::make_pair(vd, std::make_pair(num_globals, input)));
871 return num_globals++;
872 }
873
874 int find_global(VarDecl* str) {
875 auto pair = globals_env.at(str);
876 return pair.first;
877 }
878
879 std::vector<unsigned int> reg_trail;
880 unsigned int current_reg_count; // How many registers have been used?
881 unsigned int current_label_count;
882
883 // Helper information. For an expression, which variables does it refer to?
884 ASTStSet scope(Expression* e);
885 ExprMap<ASTStSet>::t _exp_scope;
886
887 ExprMap<CG::Mode>::t mode_map;
888
889 // Procedure information
890 void register_builtins(void);
891 CG_ProcID register_builtin(std::string s, unsigned int p);
892 CG_ProcID find_builtin(std::string s);
893 std::vector<std::pair<std::string, unsigned int>> _builtins;
894 std::unordered_map<std::string, CG_ProcID> _proc_map;
895
896 // Procedures yet to be emitted.
897 CG_FunMap fun_map;
898
899 SigMap<std::pair<CG_ProcID, bool>>::t dispatch;
900
901 std::unordered_map<FunctionI*, CG_ProcID> fun_bodies;
902 std::vector<std::pair<FunctionI*, std::pair<BytecodeProc::Mode, BytecodeProc::Mode>>>
903 pending_bodies;
904};
905
906const char* instr_name(BytecodeStream::Instr i);
907const char* agg_name(AggregationCtx::Symbol s);
908const char* mode_name(BytecodeProc::Mode m);
909
910std::tuple<CG_ProcID, BytecodeProc::Mode, bool> find_call_fun(CodeGen& cg, const ASTString& ident,
911 const Type& ret_type,
912 std::vector<Type> arg_types,
913 BytecodeProc::Mode m,
914 bool reserved_name = false);
915std::tuple<CG_ProcID, BytecodeProc::Mode, bool> find_call_fun(CodeGen& cg, Call* call,
916 BytecodeProc::Mode m,
917 bool reserved_name = false);
918
919void eval_let_body(Let* let, BytecodeProc::Mode ctx, CodeGen& cg, CG_Builder& frag,
920 std::vector<CG_Cond::T>& conj);
921
922}; // namespace MiniZinc
923
924#endif