this repo has no description
at develop 172 kB view raw
1#include <minizinc/astexception.hh> 2#include <minizinc/astiterator.hh> 3#include <minizinc/codegen.hh> 4#include <minizinc/copy.hh> 5#include <minizinc/eval_par.hh> 6#include <minizinc/flatten.hh> 7#include <minizinc/hash.hh> 8#include <minizinc/interpreter.hh> 9#include <minizinc/optimize.hh> 10#include <minizinc/output.hh> 11#include <minizinc/prettyprinter.hh> 12 13#include <../lib/codegen/analysis.hpp> 14#include <../lib/codegen/codegen_internal.hpp> 15#include <functional> 16#include <set> 17 18namespace MiniZinc { 19 20// Known limitations: 21// - Comprehensions and generator expressions are assumed to be total, so are evaluated in root 22// context. Expression evaluation currently runs in two modes: 23// - eval, which places the result onto the value stack on the enclosing context, or 24// - locate, which places the result into a register, and returns the register. 25// this is done to avoid a unnecessary push/pop sequences, when a result is already bound to 26// a register, and is needed for e.g. a call. 27 28#define ENABLE_PLUS 1 29// MZNC_COLLECT_LEAVES should generate fewer aggregation contexts for models with par-resolved 30// partiality (i.e. lots of array accesses) 31#define MZNC_COLLECT_LEAVES 32 33using Mode = CG::Mode; 34 35struct builtin_t { 36 std::function<CG_Cond::T(Call*, Mode, CodeGen&, CG_Builder&)> boolean; 37 std::function<CG::Binding(Call*, Mode, CodeGen&, CG_Builder&)> general; 38}; 39 40typedef std::unordered_map<std::string, builtin_t> builtin_table; 41 42const char* instr_names[] = { 43 "ADDI", 44 "SUBI", 45 "MULI", 46 "DIVI", 47 "MODI", 48 "INCI", 49 "DECI", 50 51 "IMMI", 52 "CLEAR", 53 "LOAD_GLOBAL", 54 "STORE_GLOBAL", 55 "MOV", 56 57 "JMP", 58 "JMPIF", 59 "JMPIFNOT", 60 61 "EQI", 62 "LTI", 63 "LEI", 64 65 "AND", 66 "OR", 67 "NOT", 68 "XOR", 69 70 "ISPAR", 71 "ISEMPTY", 72 "LENGTH", 73 "GET_VEC", 74 "GET_ARRAY", 75 76 "LB", 77 "UB", 78 "DOM", 79 80 "MAKE_SET", 81 "INTERSECTION", 82 "UNION", 83 "DIFF", 84 85 "INTERSECT_DOMAIN", 86 87 "OPEN_AGGREGATION", 88 "CLOSE_AGGREGATION", 89 "SIMPLIFY_LIN", 90 91 "PUSH", 92 "POP", 93 "POST", 94 95 "RET", 96 "CALL", 97 "BUILTIN", 98 "TCALL", 99 100 "ITER_ARRAY", 101 "ITER_VEC", 102 "ITER_RANGE", 103 "ITER_NEXT", 104 "ITER_BREAK", 105 106 "TRACE", 107 "ABORT", 108}; 109 110const char* mode_names[] = { 111 "ROOT", "ROOT_NEG", "FUN", "FUN_NEG", "IMP", "IMP_NEG", "MAX_MODE", 112}; 113 114const char* agg_names[] = {"AND", "OR", "VEC", "OTHER"}; 115const char* instr_name(BytecodeStream::Instr i) { return instr_names[i]; } 116 117const char* agg_name(AggregationCtx::Symbol s) { return agg_names[s]; } 118const char* mode_name(BytecodeProc::Mode m) { return mode_names[m]; } 119 120inline void TODO(void) { throw InternalError("Not yet implemented!"); } 121 122void CodeGen::register_builtins(void) { 123 // Solver Built-ins 124 register_builtin("mk_intvar", 1); 125 126 // Constants 127 register_builtin("absent", 1); 128 register_builtin("infinity", 1); 129 register_builtin("boolean_domain", 0); 130 register_builtin("infinite_domain", 0); 131 132 // Interpreter Built-ins 133 register_builtin("uniform", 2); 134 register_builtin("sol", 1); 135 register_builtin("sort_by", 2); 136 register_builtin("floor", 1); 137 register_builtin("ceil", 1); 138 register_builtin("slice_Xd", 3); 139 register_builtin("array_Xd", 2); 140 register_builtin("index_set", 2); 141 register_builtin("internal_sort", 1); 142} 143 144void OPEN_AGG(CodeGen& cg, CG_Builder& frag, AggregationCtx::Symbol ctx) { 145 cg.env_push(); 146 cg.reg_trail.push_back(cg.current_reg_count); 147 148 PUSH_INSTR(frag, BytecodeStream::OPEN_AGGREGATION, ctx); 149} 150void CLOSE_AGG(CodeGen& cg, CG_Builder& frag) { 151 assert(cg.reg_trail.size() > 0); 152 int old_reg_count = cg.current_reg_count; 153 cg.current_reg_count = cg.reg_trail.back(); 154 cg.reg_trail.pop_back(); 155 if (cg.current_reg_count < old_reg_count) { 156 PUSH_INSTR(frag, BytecodeStream::CLEAR, CG::r(cg.current_reg_count), CG::r(old_reg_count - 1)); 157 } 158 PUSH_INSTR(frag, BytecodeStream::CLOSE_AGGREGATION); 159 cg.env_pop(); 160} 161 162void OPEN_AND(CodeGen& cg, CG_Builder& frag) { OPEN_AGG(cg, frag, AggregationCtx::VCTX_AND); } 163void OPEN_OR(CodeGen& cg, CG_Builder& frag) { OPEN_AGG(cg, frag, AggregationCtx::VCTX_OR); } 164void OPEN_OTHER(CodeGen& cg, CG_Builder& frag) { OPEN_AGG(cg, frag, AggregationCtx::VCTX_OTHER); } 165void OPEN_VEC(CodeGen& cg, CG_Builder& frag) { OPEN_AGG(cg, frag, AggregationCtx::VCTX_VEC); } 166 167CG_ProcID CodeGen::register_builtin(std::string s, unsigned int arity) { 168 auto it(_proc_map.find(s)); 169 if (it != _proc_map.end()) { 170 // throw InternalError(std::string("Builtin already registered: ") + s); 171 std::cerr << "WARNING: Builtin " << s << " already registered." << std::endl; 172 return it->second; 173 } 174 175 CG_ProcID id(CG_ProcID::builtin(_builtins.size())); 176 _builtins.push_back(std::make_pair(s, arity)); 177 _proc_map.insert(std::make_pair(s, id)); 178 return id; 179} 180 181CG_ProcID CodeGen::find_builtin(std::string s) { 182 auto it(_proc_map.find(s)); 183 assert(it != _proc_map.end()); 184 return (*it).second; 185} 186 187int bind_cst(int x, CodeGen& cg, CG_Builder& frag); 188 189void bind_binop_var(CodeGen& cg, CG_Builder& frag, Mode ctx, BinOpType op, int r_lhs, int r_rhs) { 190 GCLock lock; 191 switch (op) { 192 // Actual builtins 193 case BOT_PLUS: { 194 auto fun = 195 find_call_fun(cg, {"op_plus"}, Type::varint(), {Type::varint(), Type::varint()}, ctx); 196 assert(BytecodeProc::is_neg(ctx) == BytecodeProc::is_neg(std::get<1>(fun))); 197 PUSH_INSTR(frag, BytecodeStream::CALL, std::get<1>(fun), std::get<0>(fun), 198 CG::i(!std::get<2>(fun)), CG::r(r_lhs), CG::r(r_rhs)); 199 return; 200 } 201 case BOT_MINUS: { 202 auto fun = 203 find_call_fun(cg, {"op_minus"}, Type::varint(), {Type::varint(), Type::varint()}, ctx); 204 assert(BytecodeProc::is_neg(ctx) == BytecodeProc::is_neg(std::get<1>(fun))); 205 PUSH_INSTR(frag, BytecodeStream::CALL, std::get<1>(fun), std::get<0>(fun), 206 CG::i(!std::get<2>(fun)), CG::r(r_lhs), CG::r(r_rhs)); 207 return; 208 } 209 case BOT_MULT: { 210 auto fun = 211 find_call_fun(cg, {"op_times"}, Type::varint(), {Type::varint(), Type::varint()}, ctx); 212 assert(BytecodeProc::is_neg(ctx) == BytecodeProc::is_neg(std::get<1>(fun))); 213 PUSH_INSTR(frag, BytecodeStream::CALL, std::get<1>(fun), std::get<0>(fun), 214 CG::i(!std::get<2>(fun)), CG::r(r_lhs), CG::r(r_rhs)); 215 return; 216 } 217 case BOT_IDIV: { 218 auto fun = find_call_fun(cg, {"op_int_division"}, Type::varint(), 219 {Type::varint(), Type::varint()}, ctx); 220 assert(BytecodeProc::is_neg(ctx) == BytecodeProc::is_neg(std::get<1>(fun))); 221 PUSH_INSTR(frag, BytecodeStream::CALL, std::get<1>(fun), std::get<0>(fun), 222 CG::i(!std::get<2>(fun)), CG::r(r_lhs), CG::r(r_rhs)); 223 return; 224 } 225 case BOT_DIV: { 226 auto fun = find_call_fun(cg, {"op_float_division"}, Type::varint(), 227 {Type::varint(), Type::varint()}, ctx); 228 assert(BytecodeProc::is_neg(ctx) == BytecodeProc::is_neg(std::get<1>(fun))); 229 PUSH_INSTR(frag, BytecodeStream::CALL, std::get<1>(fun), std::get<0>(fun), 230 CG::i(!std::get<2>(fun)), CG::r(r_lhs), CG::r(r_rhs)); 231 return; 232 } 233 case BOT_MOD: { 234 auto fun = 235 find_call_fun(cg, {"op_modulus"}, Type::varint(), {Type::varint(), Type::varint()}, ctx); 236 assert(BytecodeProc::is_neg(ctx) == BytecodeProc::is_neg(std::get<1>(fun))); 237 PUSH_INSTR(frag, BytecodeStream::CALL, std::get<1>(fun), std::get<0>(fun), 238 CG::i(!std::get<2>(fun)), CG::r(r_lhs), CG::r(r_rhs)); 239 return; 240 } 241 case BOT_DOTDOT: { 242 // The values in r_lhs and r_rhs had better be IMMIs. 243 OPEN_VEC(cg, frag); 244 PUSH_INSTR(frag, BytecodeStream::PUSH, CG::r(r_lhs)); 245 PUSH_INSTR(frag, BytecodeStream::PUSH, CG::r(r_rhs)); 246 CLOSE_AGG(cg, frag); 247 return; 248 } 249 case BOT_PLUSPLUS: { 250 OPEN_VEC(cg, frag); 251 Foreach iter_lhs(cg, r_lhs); 252 iter_lhs.emit_pre(frag); 253 PUSH_INSTR(frag, BytecodeStream::PUSH, CG::r(iter_lhs.val())); 254 iter_lhs.emit_post(frag); 255 256 Foreach iter_rhs(cg, r_rhs); 257 iter_rhs.emit_pre(frag); 258 PUSH_INSTR(frag, BytecodeStream::PUSH, CG::r(iter_rhs.val())); 259 iter_rhs.emit_post(frag); 260 CLOSE_AGG(cg, frag); 261 return; 262 } 263 default: 264 break; 265 } 266 throw InternalError("Unexpected fall-through in bind_binop_var."); 267} 268 269int bind_binop_par_int(CodeGen& cg, CG_Builder& frag, BinOpType op, int r_lhs, int r_rhs) { 270 int r; 271 switch (op) { 272 // Actual builtins 273 case BOT_EQ: 274 case BOT_EQUIV: 275 r = GET_REG(cg); 276 PUSH_INSTR(frag, BytecodeStream::EQI, CG::r(r_lhs), CG::r(r_rhs), CG::r(r)); 277 return r; 278 case BOT_NQ: 279 r = GET_REG(cg); 280 PUSH_INSTR(frag, BytecodeStream::EQI, CG::r(r_lhs), CG::r(r_rhs), CG::r(r)); 281 PUSH_INSTR(frag, BytecodeStream::NOT, CG::r(r), CG::r(r)); 282 return r; 283 case BOT_LE: 284 r = GET_REG(cg); 285 PUSH_INSTR(frag, BytecodeStream::LTI, CG::r(r_lhs), CG::r(r_rhs), CG::r(r)); 286 return r; 287 case BOT_LQ: 288 r = GET_REG(cg); 289 PUSH_INSTR(frag, BytecodeStream::LEI, CG::r(r_lhs), CG::r(r_rhs), CG::r(r)); 290 return r; 291 case BOT_GR: 292 r = GET_REG(cg); 293 PUSH_INSTR(frag, BytecodeStream::LTI, CG::r(r_rhs), CG::r(r_lhs), CG::r(r)); 294 return r; 295 case BOT_GQ: 296 r = GET_REG(cg); 297 PUSH_INSTR(frag, BytecodeStream::LEI, CG::r(r_rhs), CG::r(r_lhs), CG::r(r)); 298 return r; 299 case BOT_XOR: 300 r = GET_REG(cg); 301 PUSH_INSTR(frag, BytecodeStream::XOR, CG::r(r_rhs), CG::r(r_lhs), CG::r(r)); 302 return r; 303 case BOT_AND: 304 r = GET_REG(cg); 305 PUSH_INSTR(frag, BytecodeStream::AND, CG::r(r_lhs), CG::r(r_rhs), CG::r(r)); 306 return r; 307 case BOT_OR: 308 r = GET_REG(cg); 309 PUSH_INSTR(frag, BytecodeStream::OR, CG::r(r_lhs), CG::r(r_rhs), CG::r(r)); 310 return r; 311 case BOT_IMPL: 312 r = GET_REG(cg); 313 PUSH_INSTR(frag, BytecodeStream::NOT, CG::r(r_lhs), CG::r(r)); 314 PUSH_INSTR(frag, BytecodeStream::OR, CG::r(r), CG::r(r_rhs), CG::r(r)); 315 return r; 316 case BOT_RIMPL: 317 r = GET_REG(cg); 318 PUSH_INSTR(frag, BytecodeStream::NOT, CG::r(r_rhs), CG::r(r)); 319 PUSH_INSTR(frag, BytecodeStream::XOR, CG::r(r_lhs), CG::r(r), CG::r(r)); 320 return r; 321 case BOT_PLUS: 322 r = GET_REG(cg); 323 PUSH_INSTR(frag, BytecodeStream::ADDI, CG::r(r_lhs), CG::r(r_rhs), CG::r(r)); 324 return r; 325 case BOT_MINUS: 326 r = GET_REG(cg); 327 PUSH_INSTR(frag, BytecodeStream::SUBI, CG::r(r_lhs), CG::r(r_rhs), CG::r(r)); 328 return r; 329 case BOT_MULT: 330 r = GET_REG(cg); 331 PUSH_INSTR(frag, BytecodeStream::MULI, CG::r(r_lhs), CG::r(r_rhs), CG::r(r)); 332 return r; 333 case BOT_IDIV: 334 r = GET_REG(cg); 335 PUSH_INSTR(frag, BytecodeStream::DIVI, CG::r(r_lhs), CG::r(r_rhs), CG::r(r)); 336 return r; 337 case BOT_MOD: 338 r = GET_REG(cg); 339 PUSH_INSTR(frag, BytecodeStream::MODI, CG::r(r_lhs), CG::r(r_rhs), CG::r(r)); 340 return r; 341 case BOT_DOTDOT: 342 // The values in r_lhs and r_rhs had better be IMMIs. 343 OPEN_VEC(cg, frag); 344 PUSH_INSTR(frag, BytecodeStream::PUSH, CG::r(r_lhs)); 345 PUSH_INSTR(frag, BytecodeStream::PUSH, CG::r(r_rhs)); 346 CLOSE_AGG(cg, frag); 347 r = GET_REG(cg); 348 PUSH_INSTR(frag, BytecodeStream::POP, CG::r(r)); 349 return r; 350 case BOT_IN: { 351 int l_head(GET_LABEL(cg)); 352 int l_stop(GET_LABEL(cg)); 353 int l_exit(GET_LABEL(cg)); 354 r = GET_REG(cg); 355 PUSH_INSTR(frag, BytecodeStream::ITER_VEC, CG::r(r_rhs), CG::l(l_exit)); 356 PUSH_LABEL(frag, l_head); 357 PUSH_INSTR(frag, BytecodeStream::ITER_NEXT, CG::r(r)); 358 // Start of interval. 359 PUSH_INSTR(frag, BytecodeStream::LEI, CG::r(r), CG::r(r_lhs), CG::r(r)); 360 PUSH_INSTR(frag, BytecodeStream::JMPIFNOT, CG::r(r), CG::l(l_stop)); 361 // End of interval 362 PUSH_INSTR(frag, BytecodeStream::ITER_NEXT, CG::r(r)); 363 PUSH_INSTR(frag, BytecodeStream::LEI, CG::r(r_lhs), CG::r(r), CG::r(r)); 364 PUSH_INSTR(frag, BytecodeStream::JMPIF, CG::r(r), CG::l(l_stop)); 365 PUSH_INSTR(frag, BytecodeStream::JMP, CG::l(l_head)); 366 PUSH_LABEL(frag, l_stop); 367 PUSH_INSTR(frag, BytecodeStream::ITER_BREAK, CG::i(1)); 368 PUSH_LABEL(frag, l_exit); 369 return r; 370 } 371 case BOT_PLUSPLUS: { 372 r = GET_REG(cg); 373 OPEN_VEC(cg, frag); 374 Foreach iter_lhs(cg, r_lhs); 375 iter_lhs.emit_pre(frag); 376 PUSH_INSTR(frag, BytecodeStream::PUSH, CG::r(iter_lhs.val())); 377 iter_lhs.emit_post(frag); 378 379 Foreach iter_rhs(cg, r_rhs); 380 iter_rhs.emit_pre(frag); 381 PUSH_INSTR(frag, BytecodeStream::PUSH, CG::r(iter_rhs.val())); 382 iter_rhs.emit_post(frag); 383 CLOSE_AGG(cg, frag); 384 PUSH_INSTR(frag, BytecodeStream::POP, CG::r(r)); 385 return r; 386 } 387 default: 388 break; 389 } 390 throw InternalError("Unexpected fall-through in bind_binop_par."); 391} 392 393int bind_binop_par_set(CodeGen& cg, CG_Builder& frag, BinOpType op, int r_lhs, int r_rhs) { 394 int r; 395 switch (op) { 396 // Actual builtins 397 case BOT_EQ: 398 case BOT_EQUIV: { 399 r = GET_REG(cg); 400 int r_tmp = GET_REG(cg); 401 PUSH_INSTR(frag, BytecodeStream::DIFF, CG::r(r_lhs), CG::r(r_rhs), CG::r(r)); 402 PUSH_INSTR(frag, BytecodeStream::ISEMPTY, CG::r(r), CG::r(r)); 403 PUSH_INSTR(frag, BytecodeStream::DIFF, CG::r(r_rhs), CG::r(r_lhs), CG::r(r_tmp)); 404 PUSH_INSTR(frag, BytecodeStream::ISEMPTY, CG::r(r_tmp), CG::r(r_tmp)); 405 PUSH_INSTR(frag, BytecodeStream::AND, CG::r(r), CG::r(r_tmp), CG::r(r)); 406 return r; 407 } 408 case BOT_NQ: { 409 r = bind_binop_par_set(cg, frag, BOT_EQ, r_lhs, r_rhs); 410 PUSH_INSTR(frag, BytecodeStream::NOT, CG::r(r), CG::r(r)); 411 return r; 412 } 413 case BOT_LE: 414 r = GET_REG(cg); 415 TODO(); 416 return r; 417 case BOT_LQ: 418 r = GET_REG(cg); 419 TODO(); 420 return r; 421 case BOT_GR: 422 r = GET_REG(cg); 423 TODO(); 424 return r; 425 case BOT_GQ: 426 r = GET_REG(cg); 427 TODO(); 428 return r; 429 case BOT_UNION: 430 r = GET_REG(cg); 431 PUSH_INSTR(frag, BytecodeStream::UNION, CG::r(r_lhs), CG::r(r_rhs), CG::r(r)); 432 return r; 433 case BOT_INTERSECT: 434 r = GET_REG(cg); 435 PUSH_INSTR(frag, BytecodeStream::INTERSECTION, CG::r(r_lhs), CG::r(r_rhs), CG::r(r)); 436 return r; 437 case BOT_SUBSET: { 438 // (L subset R) <-> ((L intersect R) == L) 439 r = GET_REG(cg); 440 int l_fin(GET_LABEL(cg)); 441 int r_inter(GET_REG(cg)); 442 PUSH_INSTR(frag, BytecodeStream::INTERSECTION, CG::r(r_lhs), CG::r(r_rhs), CG::r(r_inter)); 443 444 int r_sz(GET_REG(cg)); 445 int r_tmp(GET_REG(cg)); 446 PUSH_INSTR(frag, BytecodeStream::LENGTH, CG::r(r_lhs), CG::r(r_tmp)); 447 PUSH_INSTR(frag, BytecodeStream::LENGTH, CG::r(r_inter), CG::r(r_sz)); 448 PUSH_INSTR(frag, BytecodeStream::EQI, CG::r(r_tmp), CG::r(r_sz), CG::r(r)); 449 PUSH_INSTR(frag, BytecodeStream::JMPIFNOT, CG::r(r), CG::l(l_fin)); 450 451 int r_idx(GET_REG(cg)); 452 int l_loop(GET_LABEL(cg)); 453 PUSH_INSTR(frag, BytecodeStream::IMMI, CG::i(1), CG::r(r_idx)); 454 PUSH_LABEL(frag, l_loop); 455 PUSH_INSTR(frag, BytecodeStream::LEI, CG::r(r_idx), CG::r(r_sz), CG::r(r_tmp)); 456 PUSH_INSTR(frag, BytecodeStream::JMPIFNOT, CG::r(r_tmp), CG::l(l_fin)); 457 458 int r_tmp2(GET_REG(cg)); 459 PUSH_INSTR(frag, BytecodeStream::GET_VEC, CG::r(r_lhs), CG::r(r_idx), CG::r(r_tmp)); 460 PUSH_INSTR(frag, BytecodeStream::GET_VEC, CG::r(r_inter), CG::r(r_idx), CG::r(r_tmp2)); 461 PUSH_INSTR(frag, BytecodeStream::EQI, CG::r(r_tmp), CG::r(r_tmp2), CG::r(r)); 462 PUSH_INSTR(frag, BytecodeStream::JMPIFNOT, CG::r(r), CG::l(l_fin)); 463 464 PUSH_INSTR(frag, BytecodeStream::INCI, CG::r(r_idx)); 465 PUSH_INSTR(frag, BytecodeStream::JMP, CG::l(l_loop)); 466 467 PUSH_LABEL(frag, l_fin); 468 return r; 469 } 470 case BOT_SUPERSET: { 471 return bind_binop_par_set(cg, frag, BOT_SUBSET, r_rhs, r_lhs); 472 } 473 case BOT_DIFF: { 474 int r(GET_REG(cg)); 475 PUSH_INSTR(frag, BytecodeStream::DIFF, CG::r(r_lhs), CG::r(r_rhs), CG::r(r)); 476 return r; 477 } 478 case BOT_SYMDIFF: { 479 int r(GET_REG(cg)); 480 int r_tmp(GET_REG(cg)); 481 PUSH_INSTR(frag, BytecodeStream::DIFF, CG::r(r_lhs), CG::r(r_rhs), CG::r(r)); 482 PUSH_INSTR(frag, BytecodeStream::DIFF, CG::r(r_rhs), CG::r(r_lhs), CG::r(r_tmp)); 483 PUSH_INSTR(frag, BytecodeStream::UNION, CG::r(r), CG::r(r_tmp), CG::r(r)); 484 return r; 485 } 486 default: 487 break; 488 } 489 throw InternalError("Unexpected fall-through in bind_binop_par_set."); 490} 491 492CG_Cond::T linear_cond(CodeGen& cg, CG_Builder& frag, BinOpType op, Mode ctx, int r_lhs, 493 int r_rhs) { 494 GCLock lock; 495 switch (op) { 496 // Actual builtins 497 case BOT_LQ: 498 case BOT_LE: { 499 // Check bounds 500 int lb_lhs = GET_REG(cg); 501 int ub_rhs = GET_REG(cg); 502 PUSH_INSTR(frag, BytecodeStream::LB, CG::r(r_lhs), CG::r(lb_lhs)); 503 PUSH_INSTR(frag, BytecodeStream::UB, CG::r(r_rhs), CG::r(ub_rhs)); 504 PUSH_INSTR(frag, (op == BOT_LE ? BytecodeStream::LTI : BytecodeStream::LEI), CG::r(lb_lhs), 505 CG::r(ub_rhs), CG::r(lb_lhs)); 506 int c = GET_REG(cg); 507 int x = GET_REG(cg); 508 int k = GET_REG(cg); 509 int z = (op == BOT_LE ? +1 : 0); 510 PUSH_INSTR(frag, BytecodeStream::SIMPLIFY_LIN, CG::r(r_lhs), CG::r(r_rhs), CG::i(z), CG::r(c), 511 CG::r(x), CG::r(k)); 512 auto linear = 513 CG_Cond::call({"pre_int_lin_le"}, ctx, true, 514 {Type::varbool(), Type::parint(1), Type::varint(1), Type::parint()}, 515 {CG::r(c), CG::r(x), CG::r(k)}); 516 return CG_Cond::forall(ctx, CG_Cond::reg(lb_lhs, true), linear); 517 } 518 case BOT_EQUIV: 519 case BOT_EQ: { 520 // Check domain 521 int dom_lhs = GET_REG(cg); 522 int dom_rhs = GET_REG(cg); 523 PUSH_INSTR(frag, BytecodeStream::DOM, CG::r(r_lhs), CG::r(dom_lhs)); 524 PUSH_INSTR(frag, BytecodeStream::DOM, CG::r(r_rhs), CG::r(dom_rhs)); 525 PUSH_INSTR(frag, BytecodeStream::INTERSECTION, CG::r(dom_lhs), CG::r(dom_rhs), 526 CG::r(dom_lhs)); 527 PUSH_INSTR(frag, BytecodeStream::ISEMPTY, CG::r(dom_lhs), CG::r(dom_rhs)); 528 PUSH_INSTR(frag, BytecodeStream::NOT, CG::r(dom_rhs), CG::r(dom_rhs)); 529 // Create linear equation 530 int k = GET_REG(cg); 531 int c = GET_REG(cg); 532 int x = GET_REG(cg); 533 PUSH_INSTR(frag, BytecodeStream::SIMPLIFY_LIN, CG::r(r_lhs), CG::r(r_rhs), CG::i(0), CG::r(c), 534 CG::r(x), CG::r(k)); 535 auto linear = 536 CG_Cond::call({"pre_int_lin_eq"}, ctx, true, 537 {Type::varbool(), Type::parint(1), Type::varint(1), Type::parint()}, 538 {CG::r(c), CG::r(x), CG::r(k)}); 539 return CG_Cond::forall(ctx, CG_Cond::reg(dom_rhs, true), linear); 540 } 541 case BOT_NQ: 542 return ~linear_cond(cg, frag, BOT_EQ, -ctx, r_lhs, r_rhs); 543 case BOT_GR: 544 return linear_cond(cg, frag, BOT_LE, ctx, r_rhs, r_lhs); 545 case BOT_GQ: 546 return linear_cond(cg, frag, BOT_LQ, ctx, r_rhs, r_lhs); 547 default: 548 break; 549 } 550 throw InternalError("Unexpected fall-through in linear_cond."); 551} 552 553CG_ProcID CodeGen::resolve_fun(FunctionI* fun, bool reserved_name) { 554 auto it(fun_bodies.find(fun)); 555 if (it != fun_bodies.end()) return it->second; 556 557 GCLock lock; 558 559 int p_idx = bytecode.size(); 560 CG_ProcID p_id(CG_ProcID::proc(p_idx)); 561 ASTExprVec<VarDecl> params(fun->params()); 562 563 std::stringstream ss; 564 if (!reserved_name && fun->e()) { 565 ss << "f_" << fun->id().str(); 566 for (auto& param : params) { 567 ss << "_"; 568 if (param->type().dim() > 0) { 569 ss << "d" << param->type().dim(); 570 } else if (param->type().dim() < 0) { 571 ss << "dT"; 572 } 573 if (param->type().isvar()) { 574 ss << "v"; 575 } 576 if (param->type().is_set()) { 577 ss << "s"; 578 } 579 switch (param->type().bt()) { 580 case Type::BT_BOOL: { 581 ss << "b"; 582 break; 583 } 584 case Type::BT_INT: { 585 ss << "i"; 586 break; 587 } 588 case Type::BT_FLOAT: { 589 ss << "f"; 590 break; 591 } 592 case Type::BT_STRING: { 593 ss << "s"; 594 break; 595 } 596 case Type::BT_ANN: { 597 ss << "a"; 598 break; 599 } 600 case Type::BT_TOP: { 601 ss << "t"; 602 break; 603 } 604 default: { 605 assert(false); 606 break; 607 } 608 } 609 } 610 } else { 611 ss << fun->id().str(); 612 } 613 614 bytecode.emplace_back(ss.str(), params.size()); 615 fun_bodies.insert(std::make_pair(fun, p_id)); 616 return p_id; 617} 618 619struct dispatch_node { 620 int label; 621 int level; 622 uint64_t sig; 623}; 624 625std::tuple<CG_ProcID, BytecodeProc::Mode, bool> find_call_fun(CodeGen& cg, const ASTString& ident, 626 const Type& ret_type, 627 std::vector<Type> arg_types, 628 BytecodeProc::Mode m, 629 bool reserved_name) { 630 for (auto& arg_type : arg_types) { 631 arg_type.ti(Type::TI_PAR); 632 } 633 int sz = arg_types.size(); 634 CallSig sig(ident, arg_types); 635 auto it(cg.dispatch.find(sig)); 636 637 BytecodeProc::Mode call_mode(ret_type.isbool() ? m : BytecodeProc::FUN); 638 BytecodeProc::Mode def_mode(ret_type.isbool() ? m : BytecodeProc::ROOT); 639 640 if (it != cg.dispatch.end()) { 641 auto d_proc(it->second); 642 if (d_proc.first.is_builtin() || cg.bytecode[d_proc.first.id()].is_available(call_mode)) { 643 return {d_proc.first, call_mode, d_proc.second}; 644 } 645 } 646 647 GCLock lock; 648 auto bodies = std::move(cg.fun_map.get_bodies(ident, arg_types)); 649 assert(!bodies.empty()); 650 auto neg_bodies = std::move(cg.fun_map.get_bodies(ASTString(ident.str() + "_neg"), arg_types)); 651 652 if (ret_type.isbool() && call_mode != BytecodeProc::ROOT) { 653 bool valid = false; 654 if (cg.fun_map.defines_mode(ident, arg_types, call_mode).first) { 655 valid = true; 656 } else if (BytecodeProc::is_neg(m) && std::any_of(neg_bodies.begin(), neg_bodies.end(), 657 [](FunctionI* fi) { return fi->e(); })) { 658 valid = true; 659 } else { 660 valid = cg.fun_map.defines_mode(ident, arg_types, BytecodeProc::FUN).first; 661 if (valid) { 662 call_mode = BytecodeProc::FUN; 663 def_mode = BytecodeProc::FUN; 664 } 665 } 666 if (!valid && 667 std::none_of(bodies.begin(), bodies.end(), [](FunctionI* fi) { return fi->e(); })) { 668 throw InternalError(ident.str() + 669 " is used in a reified context, but no reification is available."); 670 } 671 } 672 673 std::vector<CG_ProcID> procs; 674 for (FunctionI* b : bodies) { 675 CG_ProcID body(cg.resolve_fun(b, reserved_name && bodies.size() == 1)); 676 // Force the body to be created 677 procs.push_back(body); 678 if (!cg.bytecode[body.id()].is_available(call_mode)) { 679 cg.bytecode[body.id()].body(call_mode); 680 cg.pending_bodies.emplace_back(b, std::make_pair(call_mode, def_mode)); 681 } 682 } 683 684 // If there's a unique candidate, go for it. 685 if (procs.size() == 1) { 686 if (it == cg.dispatch.end()) { 687 cg.dispatch.insert(std::make_pair(sig, std::make_pair(procs[0], false))); 688 } 689 return {procs[0], call_mode, false}; 690 } 691 692 CG_ProcID d_proc(CG_ProcID::builtin(0)); 693 if (it != cg.dispatch.end()) { 694 d_proc = it->second.first; 695 } else { 696 // Otherwise, generate the dispatch function. 697 int p_idx = cg.bytecode.size(); 698 699 std::stringstream ss; 700 if (reserved_name) { 701 ss << ident.str(); 702 } else { 703 ss << "d_" << ident.str(); 704 for (auto& type : arg_types) { 705 ss << "_"; 706 if (type.dim() > 0) { 707 ss << "d" << type.dim(); 708 } else if (type.dim() < 0) { 709 ss << "dT"; 710 } 711 if (type.is_set()) { 712 ss << "s"; 713 } 714 switch (type.bt()) { 715 case Type::BT_BOOL: { 716 ss << "b"; 717 break; 718 } 719 case Type::BT_INT: { 720 ss << "i"; 721 break; 722 } 723 case Type::BT_FLOAT: { 724 ss << "f"; 725 break; 726 } 727 case Type::BT_STRING: { 728 ss << "s"; 729 break; 730 } 731 case Type::BT_ANN: { 732 ss << "a"; 733 break; 734 } 735 case Type::BT_TOP: { 736 ss << "t"; 737 break; 738 } 739 default: { 740 assert(false); 741 break; 742 } 743 } 744 } 745 } 746 747 d_proc = CG_ProcID::proc(p_idx); 748 cg.bytecode.emplace_back(ss.str(), arg_types.size()); 749 cg.dispatch.insert(std::make_pair(sig, std::make_pair(d_proc, true))); 750 } 751 752 // Now generate the dispatch body. 753 CG_Builder frag; 754 std::vector<uint64_t> var_sig(sz); 755 std::vector<uint64_t> def_sig(bodies.size()); 756 for (int bi = 0; bi < bodies.size(); ++bi) { 757 ASTExprVec<VarDecl> params(bodies[bi]->params()); 758 for (int ii = 0; ii < params.size(); ++ii) { 759 if (params[ii]->type().isvar()) { 760 var_sig[ii] |= 1ull << bi; 761 def_sig[bi] |= 1ull << ii; 762 } 763 } 764 } 765 766 std::vector<std::unordered_map<uint64_t, int>> sig_table(sz + 1); 767 std::vector<dispatch_node> nodes; 768 769 nodes.push_back(dispatch_node{-1, 0, (1ull << bodies.size()) - 1}); 770 771 for (int ii = 0; ii < nodes.size(); ii++) { 772 dispatch_node d(nodes[ii]); 773 if (d.label != -1) PUSH_LABEL(frag, d.label); 774 if (!d.sig) { 775 PUSH_INSTR(frag, BytecodeStream::ABORT); 776 } else if (d.level == sz) { 777 // Find the best candidate, and emit a call. 778 uint64_t candidates(d.sig); 779 unsigned int best(find_lsb(candidates)); 780 uint64_t best_sig(def_sig[best]); 781 candidates ^= 1ull << best; 782 while (candidates) { 783 unsigned int curr(find_lsb(candidates)); 784 candidates ^= 1ull << curr; 785 uint64_t sig(def_sig[curr]); 786 if (!(sig & ~best_sig)) { 787 // At least as good as the incumbent 788 best = curr; 789 best_sig = sig; 790 } 791 } 792 CG_ProcID p_id(procs[best]); 793 PUSH_INSTR(frag, BytecodeStream::TCALL, call_mode, p_id, 794 CG::i(bodies[best]->ti()->type().isvar())); 795 } else { 796 int par_label; 797 auto p_it(sig_table[d.level + 1].find(d.sig)); 798 if (p_it != sig_table[d.level + 1].end()) { 799 par_label = p_it->second; 800 } else { 801 par_label = GET_LABEL(cg); 802 // int idx = nodes.size(); 803 sig_table[d.level + 1].insert(std::make_pair(d.sig, par_label)); 804 nodes.push_back(dispatch_node{par_label, d.level + 1, d.sig}); 805 } 806 uint64_t v_sig(d.sig & var_sig[d.level]); 807 if (v_sig == d.sig) { 808 PUSH_INSTR(frag, BytecodeStream::JMP, CG::l(par_label)); 809 } else { 810 int var_label; 811 auto v_it(sig_table[d.level + 1].find(v_sig)); 812 if (v_it != sig_table[d.level + 1].end()) { 813 var_label = v_it->second; 814 } else { 815 var_label = GET_LABEL(cg); 816 // int idx = nodes.size(); 817 sig_table[d.level + 1].insert(std::make_pair(v_sig, var_label)); 818 nodes.push_back(dispatch_node{var_label, d.level + 1, v_sig}); 819 } 820 821 PUSH_INSTR(frag, BytecodeStream::ISPAR, CG::r(d.level), CG::r(sz)); 822 PUSH_INSTR(frag, BytecodeStream::JMPIF, CG::r(sz), CG::l(par_label)); 823 PUSH_INSTR(frag, BytecodeStream::JMP, CG::l(var_label)); 824 } 825 } 826 } 827 828 cg.append(d_proc.id(), call_mode, frag); 829 830 return {d_proc, call_mode, false}; 831} 832 833std::tuple<CG_ProcID, BytecodeProc::Mode, bool> find_call_fun(CodeGen& cg, Call* call, 834 BytecodeProc::Mode m, 835 bool reserved_name) { 836 std::vector<Type> arg_types; 837 int sz = call->n_args(); 838 for (int ii = 0; ii < sz; ++ii) { 839 Type t(call->arg(ii)->type()); 840 arg_types.push_back(t); 841 } 842 return find_call_fun(cg, call->id(), call->type(), arg_types, m, reserved_name); 843} 844/* 845CG_ProcID find_call_pred(CodeGen& cg, Call* c) { 846 return CG_ProcID::proc(0xbead); 847 848} 849*/ 850 851// Analyse an expression (and sub-expressions) for partiality 852#if 0 853struct ClearFlags : public EVisitor { 854 bool enter(Expression* e) { 855 if(!e->isUnboxedVal() && e->user_flag0()) { 856 e->user_flag0(0); 857 e->user_flag1(0); 858 return true; 859 } 860 return false; 861 } 862 // FIXME: We need to identify call bodies. 863 void vCall(const Call&) {} 864 865 static void clear(Expression* e) { 866 ClearFlags cf; 867 TopDownIterator<ClearFlags> td(cf); 868 td.run(e); 869 } 870}; 871 872struct Partiality { 873 Partiality(CodeGen& _cg) : cg(_cg) { } 874 875 // We use _flag_3 to track whether something is already on the 876 // stack, and _flag_4 to track whether it is eliminated as true. 877 bool is_partial(Expression* e) { 878 if(e->isUnboxedVal()) 879 return false; 880 // Either on the call stack and still open, or completed. 881 // In either case, check whether the partiality-flag is set. 882 if(e->user_flag0()) 883 return e->user_flag1(); 884 // Otherwise, mark it as pending, and enter it. 885 e->user_flag0(1); 886 bool p = _is_partial(e); 887 // Record the result, and return. 888 e->user_flag1(p); 889 return p; 890 } 891 892 bool _is_partial(Expression* e) { 893 // First, Boolean expressions are always total. 894 if(e->type().isbool()) 895 return false; 896 // Otherwise, look at the 897 switch(e->eid()) { 898 case Expression::E_INTLIT: 899 case Expression::E_FLOATLIT: 900 case Expression::E_SETLIT: 901 case Expression::E_BOOLLIT: 902 case Expression::E_STRINGLIT: 903 case Expression::E_ID: 904 return false; 905 case Expression::E_ARRAYLIT: { 906 ArrayLit* a(e->template cast<ArrayLit>()); 907 int sz(a->size()); 908 for(int ii = 0; ii < sz; ++ii) { 909 if(is_partial((*a)[ii])) 910 return true; 911 } 912 return true; 913 } 914 case Expression::E_ARRAYACCESS: { 915 /* 916 ArrayAccess* a(e->template cast<ArrayAccess>()); 917 if(is_partial(a->v())) 918 return true; 919 ASTExprVec<Expression> idx(a->idx()); 920 int sz(idx.size()); 921 for(int ii = 0; ii < sz; ++ii) { 922 if(is_partial(idx[ii])) 923 return true; 924 } 925 break; 926 */ 927 // FIXME: Needs an analysis to determine whether 928 // dom(a->v) subseteq index_set(A). 929 return true; 930 } 931 case Expression::E_COMP: { 932 // A comprehension is total if all its generators 933 // and its body are total. 934 // Don't need to look in the where clauses, because 935 // they're Boolean, and therefore total. 936 Comprehension* c(e->template cast<Comprehension>()); 937 int sz = c->n_generators(); 938 for(int g = 0; g < c->n_generators(); ++g) { 939 if(is_partial(c->in(g))) 940 return true; 941 } 942 return is_partial(c->e()); 943 } 944 case Expression::E_ITE: { 945 // The conditions are Boolean, so must be total. 946 // Look at the values. 947 ITE* ite(e->template cast<ITE>()); 948 int sz(ite->size()); 949 if(is_partial(ite->e_else())) 950 return false; 951 for(int ii = 0; ii < sz; ++ii) { 952 if(is_partial(ite->e_then(ii))) 953 return false; 954 } 955 return true; 956 } 957 case Expression::E_BINOP: { 958 BinOp* b(e->template cast<BinOp>()); 959 // First, check if the op is itself partial. 960 // TODO: (Eventually) add a pass to determine whether we can exclude 961 // 0 from the domain of b->rhs(). 962 if(b->op() == BOT_DIV || b->op() == BOT_IDIV || b->op() == BOT_MOD) 963 return true; 964 return is_partial(b->lhs()) || is_partial(b->rhs()); 965 } 966 case Expression::E_UNOP: 967 return is_partial(e->template cast<UnOp>()->e()); 968 969 case Expression::E_CALL: { 970 Call* call(e->template cast<Call>()); 971 int sz = call->n_args(); 972 // Check if any of its arguments are partial. 973 for(int ii = 0; ii < sz; ++ii) { 974 if(is_partial(call->arg(ii))) 975 return true; 976 } 977 // FIXME: Identify the relevant call body, recursively 978 // check for partiality. 979 // return false; 980 for(FunctionI* b : cg.fun_map.get_bodies(call)) { 981 // Boolean-typed values are always total 982 if(b->ti()->type().isbool()) 983 continue; 984 if(!b->e()) 985 return true; 986 } 987 return false; 988 } 989 case Expression::E_LET: { 990 Let* let(e->template cast<Let>()); 991 992 // Check if any of the expressions are partial. 993 ASTExprVec<Expression> bindings(let->let()); 994 for(Expression* item : bindings) { 995 if (VarDecl* vd = e->dyn_cast<VarDecl>()) { 996 if(vd->e()) { 997 // If both a domain and a definition are given, 998 // the domain might be constraining. 999 if (vd->ti()->domain()) 1000 return true; 1001 if(is_partial(vd->e())) 1002 return true; 1003 } 1004 } else { 1005 // If there's some item that isn't a binding, it must be a constriant 1006 return true; 1007 } 1008 } 1009 return is_partial(let->in()); 1010 } 1011 case Expression::E_ANON: 1012 case Expression::E_VARDECL: 1013 case Expression::E_TI: 1014 case Expression::E_TIID: 1015 throw InternalError("Bytecode generator encountered unexpected expression type."); 1016 } 1017 } 1018 1019 void reset_flags(Expression* e) { 1020 /* 1021 if(!e->isUnboxedVal()) { 1022 if(e->_flag_3) { 1023 e->_flag_3 = e->_flag_4 = 0; 1024 } 1025 } 1026 */ 1027 } 1028 1029 CodeGen& cg; 1030 1031}; 1032#endif 1033 1034ASTStSet CodeGen::scope(Expression* e) { 1035 // Is it already cached? 1036 auto it(_exp_scope.find(e)); 1037 if (it != _exp_scope.end()) return (*it).second; 1038 1039 // Otherwise, dispatch on the type. 1040 ASTStSet r; 1041 switch (e->eid()) { 1042 case Expression::E_INTLIT: 1043 case Expression::E_FLOATLIT: 1044 case Expression::E_SETLIT: 1045 case Expression::E_BOOLLIT: 1046 case Expression::E_STRINGLIT: 1047 break; 1048 case Expression::E_ID: { 1049 Id* id(e->template cast<Id>()); 1050 r.insert(id->v()); 1051 break; 1052 } 1053 case Expression::E_ARRAYLIT: { 1054 ArrayLit* a(e->template cast<ArrayLit>()); 1055 int sz(a->size()); 1056 for (int ii = 0; ii < sz; ++ii) { 1057 ASTStSet rr(scope((*a)[ii])); 1058 r.insert(rr.begin(), rr.end()); 1059 } 1060 break; 1061 } 1062 case Expression::E_ARRAYACCESS: { 1063 ArrayAccess* a(e->template cast<ArrayAccess>()); 1064 r = scope(a->v()); 1065 1066 ASTExprVec<Expression> idx(a->idx()); 1067 int sz(idx.size()); 1068 for (int ii = 0; ii < sz; ++ii) { 1069 ASTStSet rr(scope(idx[ii])); 1070 r.insert(rr.begin(), rr.end()); 1071 } 1072 break; 1073 } 1074 case Expression::E_COMP: { 1075 // Work from the inner expression out. 1076 Comprehension* c(e->template cast<Comprehension>()); 1077 r = scope(c->e()); 1078 int sz = c->n_generators(); 1079 for (int g = sz - 1; g >= 0; --g) { 1080 ASTStSet r_in(scope(c->in(g))); 1081 1082 if (c->where(g)) { 1083 ASTStSet r_where(scope(c->where(g))); 1084 r.insert(r_where.begin(), r_where.end()); 1085 r.insert(r_in.begin(), r_in.end()); 1086 } 1087 1088 // Now remove the variables that were bound. 1089 for (int d = 0; d < c->n_decls(g); ++d) { 1090 VarDecl* vd(c->decl(g, d)); 1091 r.erase(vd->id()->str()); 1092 } 1093 } 1094 break; 1095 } 1096 case Expression::E_ITE: { 1097 ITE* ite(e->template cast<ITE>()); 1098 int sz(ite->size()); 1099 r = scope(ite->e_else()); 1100 for (int ii = 0; ii < sz; ++ii) { 1101 ASTStSet rr(scope(ite->e_if(ii))); 1102 r.insert(rr.begin(), rr.end()); 1103 rr = scope(ite->e_then(ii)); 1104 r.insert(rr.begin(), rr.end()); 1105 } 1106 break; 1107 } 1108 case Expression::E_BINOP: { 1109 BinOp* b(e->template cast<BinOp>()); 1110 r = scope(b->lhs()); 1111 ASTStSet rr(scope(b->rhs())); 1112 r.insert(rr.begin(), rr.end()); 1113 break; 1114 } 1115 case Expression::E_UNOP: 1116 r = scope(e->template cast<UnOp>()->e()); 1117 break; 1118 case Expression::E_CALL: { 1119 Call* call(e->template cast<Call>()); 1120 int sz = call->n_args(); 1121 for (int ii = 0; ii < sz; ++ii) { 1122 ASTStSet rr(scope(call->arg(ii))); 1123 r.insert(rr.begin(), rr.end()); 1124 } 1125 break; 1126 } 1127 case Expression::E_LET: { 1128 Let* let(e->template cast<Let>()); 1129 ASTExprVec<Expression> bindings(let->let()); 1130 int sz(bindings.size()); 1131 r = scope(let->in()); 1132 for (int ii = sz - 1; ii >= 0; --ii) { 1133 if (VarDecl* vd = bindings[ii]->dyn_cast<VarDecl>()) { 1134 r.erase(vd->id()->str()); 1135 if (vd->e()) { 1136 ASTStSet r_d(scope(vd->e())); 1137 r.insert(r_d.begin(), r_d.end()); 1138 } 1139 } else { 1140 ASTStSet r_e(scope(bindings[ii])); 1141 r.insert(r_e.begin(), r_e.end()); 1142 } 1143 } 1144 break; 1145 } 1146 case Expression::E_ANON: 1147 case Expression::E_VARDECL: 1148 case Expression::E_TI: 1149 case Expression::E_TIID: 1150 throw InternalError("Bytecode generator encountered unexpected expression type."); 1151 } 1152 1153 _exp_scope.insert(std::make_pair(e, r)); 1154 return r; 1155} 1156 1157CodeGen::Binding CodeGen::cache_lookup(Expression* e) { return env().cache_lookup(e, scope(e)); } 1158void CodeGen::cache_store(Expression* e, CodeGen::Binding l) { env().cache_store(e, scope(e), l); } 1159 1160void _debugcond(CG_Cond::T c) { 1161 CG_Cond::_T* p(c.get()); 1162 if (c.sign()) std::cerr << "~"; 1163 if (!p) { 1164 std::cerr << "T"; 1165 } else { 1166 switch (p->kind()) { 1167 case CG_Cond::CC_Reg: 1168 std::cerr << "R" << p->reg[0].reg; 1169 break; 1170 case CG_Cond::CC_Call: 1171 std::cerr << "<Call>"; 1172 break; 1173 case CG_Cond::CC_And: 1174 std::cerr << "(and"; 1175 for (CG_Cond::T child : static_cast<CG_Cond::C_And*>(p)->children) { 1176 std::cerr << " "; 1177 _debugcond(child); 1178 } 1179 break; 1180 } 1181 } 1182} 1183void debugcond(CG_Cond::T c) { 1184 _debugcond(c); 1185 std::cerr << std::endl; 1186} 1187 1188int bind_cst(int x, CodeGen& cg, CG_Builder& frag) { 1189 CG::Binding b; 1190 if (cg.env().cache_lookup_cst(x, b)) return b.first; 1191 int r = GET_REG(cg); 1192 PUSH_INSTR(frag, BytecodeStream::IMMI, CG::i(x), CG::r(r)); 1193 cg.env().cache_store_cst(x, std::make_pair(r, CG_Cond::ttt())); 1194 return r; 1195} 1196 1197// WARNING ON THE USE OF CG_Conds: register caching assumes that CG_Conds dont escape an 1198// aggregation context, and will be available on all paths. If the cond is forced conditionally 1199// (e.g. shortcutting), cg.push_env() should be called before the force() call, and cg.pop_env() 1200// called where control flow rejoins. 1201 1202// Given CG_Cond cond, collect the disjuncts having positive or negative values. 1203// Returns false if the conjunction is a contradiction. 1204bool collect_prod(std::vector<int>& pos, std::vector<int>& neg, 1205 std::vector<CG_Cond::C_And*>& delayed, CG_Cond::T cond, CodeGen& cg, 1206 CG_Builder& frag) { 1207 assert(cond.get()); 1208 CG_Cond::_T* p(cond.get()); 1209 bool sign(cond.sign()); 1210 1211 if (p->reg[1 - sign].is_seen || p->reg[1 - sign].is_root) return false; 1212 if (p->reg[sign].is_seen || p->reg[sign].is_root) return true; 1213 p->reg[sign].is_seen = true; 1214 1215 if (p->reg[sign].has_reg()) { 1216 pos.push_back(p->reg[sign].reg); 1217 } else if (p->reg[1 - sign].has_reg()) { 1218 neg.push_back(p->reg[1 - sign].reg); 1219 } else if (p->kind() == CG_Cond::CC_And && !sign) { 1220 // Recursively collect the children. 1221 std::vector<CG_Cond::T>& children(static_cast<CG_Cond::C_And*>(p)->children); 1222 for (CG_Cond::T c : children) { 1223 if (!collect_prod(pos, neg, delayed, c, cg, frag)) return false; 1224 } 1225 } else if (p->kind() == CG_Cond::CC_And) { 1226 // How do we decide which way to compile the remaining conditions? 1227 delayed.push_back(static_cast<CG_Cond::C_And*>(p)); 1228 } else { 1229 assert(p->kind() == CG_Cond::CC_Call); 1230 CG_Cond::C_Call* call(static_cast<CG_Cond::C_Call*>(p)); 1231 std::vector<Type> ty(call->ty.begin() + 1, call->ty.end()); 1232 auto fun = find_call_fun(cg, call->ident, call->ty[0], ty, call->m); 1233 PUSH_INSTR(frag, BytecodeStream::CALL, std::get<1>(fun), std::get<0>(fun), 1234 CG::i((!std::get<2>(fun)) && call->cse), call->params); 1235 int r = GET_REG(cg); 1236 PUSH_INSTR(frag, BytecodeStream::POP, CG::r(r)); 1237 if (BytecodeProc::is_neg(call->m) != BytecodeProc::is_neg(std::get<1>(fun))) { 1238 if (call->ty[0].ispar()) { 1239 PUSH_INSTR(frag, BytecodeStream::NOT, CG::r(r), CG::r(r)); 1240 } else { 1241 auto fun = 1242 find_call_fun(cg, {"op_not"}, Type::varbool(), {Type::varbool()}, BytecodeProc::FUN); 1243 assert(std::get<1>(fun) == BytecodeProc::FUN); 1244 PUSH_INSTR(frag, BytecodeStream::CALL, BytecodeProc::FUN, std::get<0>(fun), 1245 CG::i(!std::get<2>(fun)), CG::r(r)); 1246 PUSH_INSTR(frag, BytecodeStream::POP, CG::r(r)); 1247 } 1248 } 1249 if (!sign) 1250 pos.push_back(r); 1251 else 1252 neg.push_back(r); 1253 } 1254 return true; 1255} 1256 1257// Given CG_Cond cond, collect the disjuncts having positive or negative values. 1258// Returns true if the disjunction is a tautology. 1259bool collect_disj(std::vector<int>& pos, std::vector<int>& neg, 1260 std::vector<CG_Cond::C_And*>& delayed, CG_Cond::T cond, CodeGen& cg, 1261 CG_Builder& frag) { 1262 assert(cond.get()); 1263 CG_Cond::_T* p(cond.get()); 1264 bool sign(cond.sign()); 1265 1266 if (p->reg[1 - sign].is_seen || p->reg[sign].is_root) return true; 1267 if (p->reg[sign].is_seen || p->reg[1 - sign].is_root) return false; 1268 p->reg[sign].is_seen = true; 1269 1270 if (p->reg[sign].has_reg()) { 1271 pos.push_back(p->reg[sign].reg); 1272 } else if (p->reg[1 - sign].has_reg()) { 1273 neg.push_back(p->reg[1 - sign].reg); 1274 } else if (p->kind() == CG_Cond::CC_And && sign) { 1275 // Recursively collect the children. 1276 std::vector<CG_Cond::T>& children(static_cast<CG_Cond::C_And*>(p)->children); 1277 for (CG_Cond::T c : children) { 1278 if (collect_disj(pos, neg, delayed, ~c, cg, frag)) return true; 1279 } 1280 } else if (p->kind() == CG_Cond::CC_And) { 1281 delayed.push_back(static_cast<CG_Cond::C_And*>(p)); 1282 } else { 1283 CG_Cond::C_Call* call(static_cast<CG_Cond::C_Call*>(p)); 1284 std::vector<Type> ty(call->ty.begin() + 1, call->ty.end()); 1285 auto fun = find_call_fun(cg, call->ident, call->ty[0], ty, call->m); 1286 PUSH_INSTR(frag, BytecodeStream::CALL, std::get<1>(fun), std::get<0>(fun), 1287 CG::i((!std::get<2>(fun)) && call->cse), call->params); 1288 int r = GET_REG(cg); 1289 PUSH_INSTR(frag, BytecodeStream::POP, CG::r(r)); 1290 if (BytecodeProc::is_neg(call->m) != BytecodeProc::is_neg(std::get<1>(fun))) { 1291 if (call->ty[0].ispar()) { 1292 PUSH_INSTR(frag, BytecodeStream::NOT, CG::r(r), CG::r(r)); 1293 } else { 1294 auto fun = 1295 find_call_fun(cg, {"op_not"}, Type::varbool(), {Type::varbool()}, BytecodeProc::FUN); 1296 assert(std::get<1>(fun) == BytecodeProc::FUN); 1297 PUSH_INSTR(frag, BytecodeStream::CALL, BytecodeProc::FUN, std::get<0>(fun), 1298 CG::i(!std::get<2>(fun)), CG::r(r)); 1299 PUSH_INSTR(frag, BytecodeStream::POP, CG::r(r)); 1300 } 1301 } 1302 if (!sign) 1303 pos.push_back(r); 1304 else 1305 neg.push_back(r); 1306 } 1307 return false; 1308} 1309 1310std::pair<int, bool> _force_cond(CG_Cond::T cond, CodeGen& cg, CG_Builder& frag); 1311 1312void collect_and_leaves(std::vector<CG_Cond::T>& par_leaves, std::vector<CG_Cond::T>& var_leaves, 1313 CG_Cond::T child, CodeGen& cg, CG_Builder& frag) { 1314 assert(child.get()); 1315 CG_Cond::_T* p(child.get()); 1316 bool sign(child.sign()); 1317 auto push = [&](CG_Cond::T r, bool is_par) { 1318 if (is_par) { 1319 par_leaves.push_back(r); 1320 } else { 1321 var_leaves.push_back(r); 1322 } 1323 }; 1324 1325 if (p->reg[sign].has_reg()) { 1326 push(child, p->reg[sign].is_par); 1327 return; 1328 } 1329 if (p->reg[1 - sign].has_reg()) { 1330 push(child, p->reg[1 - sign].is_par); 1331 } else if (p->kind() == CG_Cond::CC_And && !sign) { 1332 std::vector<CG_Cond::T>& children(static_cast<CG_Cond::C_And*>(p)->children); 1333 for (CG_Cond::T c : children) { 1334 collect_and_leaves(par_leaves, var_leaves, c, cg, frag); 1335 } 1336 } else { 1337 push(child, /* Check if this is par. */ false); 1338 } 1339} 1340void collect_or_leaves(std::vector<CG_Cond::T>& par_leaves, std::vector<CG_Cond::T>& var_leaves, 1341 CG_Cond::T child, CodeGen& cg, CG_Builder& frag) { 1342 assert(child.get()); 1343 CG_Cond::_T* p(child.get()); 1344 bool sign(child.sign()); 1345 auto push = [&](CG_Cond::T r, bool is_par) { 1346 if (is_par) { 1347 par_leaves.push_back(r); 1348 } else { 1349 var_leaves.push_back(r); 1350 } 1351 }; 1352 1353 if (p->reg[1 - sign].has_reg()) { 1354 push(~child, p->reg[1 - sign].is_par); 1355 return; 1356 } 1357 if (p->reg[sign].has_reg()) { 1358 push(~child, p->reg[sign].is_par); 1359 } else if (p->kind() == CG_Cond::CC_And && !sign) { 1360 std::vector<CG_Cond::T>& children(static_cast<CG_Cond::C_And*>(p)->children); 1361 for (CG_Cond::T c : children) { 1362 collect_or_leaves(par_leaves, var_leaves, c, cg, frag); 1363 } 1364 } else { 1365 push(~child, /* Check if this is par. */ false); 1366 } 1367} 1368 1369void force_and_leaves(std::vector<int>& var_leaves, std::vector<int>& par_leaves, CG_Cond::T child, 1370 CodeGen& cg, CG_Builder& frag) { 1371 assert(child.get()); 1372 CG_Cond::_T* p(child.get()); 1373 bool sign(child.sign()); 1374 auto push = [&](int r, bool is_par) { 1375 if (is_par) { 1376 par_leaves.push_back(r); 1377 } else { 1378 var_leaves.push_back(r); 1379 } 1380 }; 1381 if (p->reg[sign].has_reg()) { 1382 push(p->reg[sign].reg, p->reg[sign].is_par); 1383 return; 1384 } 1385 cg.env().record_cached_cond(child); 1386 if (p->reg[1 - sign].has_reg()) { 1387 // Create the negation 1388 int r(GET_REG(cg)); 1389 if (p->reg[1 - sign].is_par) { 1390 PUSH_INSTR(frag, BytecodeStream::NOT, CG::r(p->reg[1 - sign].reg), CG::r(r)); 1391 } else { 1392 GCLock lock; 1393 auto fun = 1394 find_call_fun(cg, {"op_not"}, Type::varbool(), {Type::varbool()}, BytecodeProc::FUN); 1395 assert(std::get<1>(fun) == BytecodeProc::FUN); 1396 PUSH_INSTR(frag, BytecodeStream::CALL, BytecodeProc::FUN, std::get<0>(fun), 1397 CG::i(!std::get<2>(fun)), CG::r(p->reg[1 - sign].reg)); 1398 PUSH_INSTR(frag, BytecodeStream::POP, CG::r(r)); 1399 } 1400 p->reg[sign] = CG_Cond::cond_reg(r, p->reg[1 - sign].is_par); 1401 push(p->reg[sign].reg, p->reg[sign].is_par); 1402 } else if (p->kind() == CG_Cond::CC_And && !sign) { 1403 std::vector<CG_Cond::T>& children(static_cast<CG_Cond::C_And*>(p)->children); 1404 for (CG_Cond::T c : children) { 1405 force_and_leaves(var_leaves, par_leaves, c, cg, frag); 1406 } 1407 } else { 1408 auto forced = _force_cond(child, cg, frag); 1409 push(forced.first, forced.second); 1410 } 1411} 1412 1413// Pushing the _negation_ of child. 1414void force_or_leaves(std::vector<int>& var_leaves, std::vector<int>& par_leaves, CG_Cond::T child, 1415 CodeGen& cg, CG_Builder& frag) { 1416 assert(child.get()); 1417 CG_Cond::_T* p(child.get()); 1418 bool sign(child.sign()); 1419 auto push = [&](int r, bool is_par) { 1420 if (is_par) { 1421 par_leaves.push_back(r); 1422 } else { 1423 var_leaves.push_back(r); 1424 } 1425 }; 1426 if (p->reg[1 - sign].has_reg()) { 1427 push(p->reg[1 - sign].reg, p->reg[1 - sign].is_par); 1428 return; 1429 } 1430 cg.env().record_cached_cond(~child); 1431 if (p->reg[sign].has_reg()) { 1432 // Create the negation 1433 int r(GET_REG(cg)); 1434 if (p->reg[sign].is_par) { 1435 PUSH_INSTR(frag, BytecodeStream::NOT, CG::r(p->reg[sign].reg), CG::r(r)); 1436 } else { 1437 GCLock lock; 1438 auto fun = 1439 find_call_fun(cg, {"op_not"}, Type::varbool(), {Type::varbool()}, BytecodeProc::FUN); 1440 assert(std::get<1>(fun) == BytecodeProc::FUN); 1441 PUSH_INSTR(frag, BytecodeStream::CALL, BytecodeProc::FUN, std::get<0>(fun), 1442 CG::i(!std::get<2>(fun)), CG::r(p->reg[sign].reg)); 1443 PUSH_INSTR(frag, BytecodeStream::POP, CG::r(r)); 1444 } 1445 p->reg[1 - sign] = CG_Cond::cond_reg(r, p->reg[sign].is_par); 1446 push(p->reg[1 - sign].reg, p->reg[1 - sign].is_par); 1447 } else if (p->kind() == CG_Cond::CC_And && !sign) { 1448 std::vector<CG_Cond::T>& children(static_cast<CG_Cond::C_And*>(p)->children); 1449 for (CG_Cond::T c : children) { 1450 force_or_leaves(var_leaves, par_leaves, c, cg, frag); 1451 } 1452 } else { 1453 auto forced = _force_cond(~child, cg, frag); 1454 push(forced.first, forced.second); 1455 } 1456} 1457 1458std::pair<int, bool> _force_cond(CG_Cond::T cond, CodeGen& cg, CG_Builder& frag) { 1459 CG_Cond::_T* p(cond.get()); 1460 bool negated(cond.sign()); 1461 if (!p) { 1462 // Either true or false. 1463 // return CG::locate_immi(1 - cond.sign(), cg, frag); 1464 return {bind_cst(1 - cond.sign(), cg, frag), true}; 1465 } 1466 cg.env().record_cached_cond(cond); 1467 if (p->kind() == CG_Cond::CC_Reg) { 1468 throw InternalError("_force_cond called on value in register."); 1469 } else if (p->kind() == CG_Cond::CC_Call) { 1470 CG_Cond::C_Call* call(static_cast<CG_Cond::C_Call*>(p)); 1471 std::vector<Type> ty(call->ty.begin() + 1, call->ty.end()); 1472 int r; 1473 Mode m(call->m); 1474 if (m.strength() != CG::Mode::Root) { 1475 Mode call_m(m.strength(), negated); 1476 auto fun = find_call_fun(cg, call->ident, call->ty[0], ty, call->m); 1477 PUSH_INSTR(frag, BytecodeStream::CALL, std::get<1>(fun), std::get<0>(fun), 1478 CG::i((!std::get<2>(fun)) && call->cse), call->params); 1479 r = GET_REG(cg); 1480 PUSH_INSTR(frag, BytecodeStream::POP, CG::r(r)); 1481 if (BytecodeProc::is_neg(call->m) != BytecodeProc::is_neg(std::get<1>(fun))) { 1482 if (call->ty[0].ispar()) { 1483 PUSH_INSTR(frag, BytecodeStream::NOT, CG::r(r), CG::r(r)); 1484 } else { 1485 auto fun = 1486 find_call_fun(cg, {"op_not"}, Type::varbool(), {Type::varbool()}, BytecodeProc::FUN); 1487 assert(std::get<1>(fun) == BytecodeProc::FUN); 1488 PUSH_INSTR(frag, BytecodeStream::CALL, BytecodeProc::FUN, std::get<0>(fun), 1489 CG::i(!std::get<2>(fun)), CG::r(r)); 1490 PUSH_INSTR(frag, BytecodeStream::POP, CG::r(r)); 1491 } 1492 } 1493 return {r, call->ty[0].ispar()}; 1494 } 1495 Mode call_m(m.strength(), negated); 1496 assert(m == call_m); 1497 auto fun = find_call_fun(cg, call->ident, call->ty[0], ty, call->m); 1498 if (call_m == std::get<1>(fun)) { 1499 PUSH_INSTR(frag, BytecodeStream::CALL, call_m, std::get<0>(fun), 1500 CG::i((!std::get<2>(fun)) && call->cse), call->params); 1501 } else { 1502 assert(std::get<1>(fun) == BytecodeProc::FUN); 1503 std::cerr << "Warning: emitting reification for " << call->ident 1504 << " because no ROOT_NEG version is available\n"; 1505 PUSH_INSTR(frag, BytecodeStream::CALL, std::get<1>(fun), std::get<0>(fun), 1506 CG::i(!std::get<2>(fun)), call->params); 1507 r = GET_REG(cg); 1508 PUSH_INSTR(frag, BytecodeStream::POP, CG::r(r)); 1509 if (BytecodeProc::is_neg(call->m) != BytecodeProc::is_neg(std::get<1>(fun))) { 1510 if (call->ty[0].ispar()) { 1511 PUSH_INSTR(frag, BytecodeStream::NOT, CG::r(r), CG::r(r)); 1512 } else { 1513 auto fun = 1514 find_call_fun(cg, {"op_not"}, Type::varbool(), {Type::varbool()}, BytecodeProc::FUN); 1515 assert(std::get<1>(fun) == BytecodeProc::FUN); 1516 PUSH_INSTR(frag, BytecodeStream::CALL, BytecodeProc::FUN, std::get<0>(fun), 1517 CG::i(!std::get<2>(fun)), CG::r(r)); 1518 PUSH_INSTR(frag, BytecodeStream::POP, CG::r(r)); 1519 } 1520 } 1521 PUSH_INSTR(frag, BytecodeStream::POST, CG::r(r)); 1522 } 1523 return {bind_cst(1, cg, frag), true}; 1524 } else if (p->kind() == CG_Cond::CC_And && !cond.sign()) { 1525#ifndef MZNC_COLLECT_LEAVES 1526 std::vector<int> var_leaves; 1527 std::vector<int> par_leaves; 1528 int r = GET_REG(cg); 1529 // I don't think these are necessary 1530 force_and_leaves(var_leaves, par_leaves, cond, cg, frag); 1531 if (var_leaves.empty()) { 1532 PUSH_INSTR(frag, BytecodeStream::MOV, CG::r(par_leaves[0]), CG::r(r)); 1533 for (int i = 1; i < par_leaves.size(); ++i) { 1534 PUSH_INSTR(frag, BytecodeStream::AND, CG::r(r), CG::r(par_leaves[i]), CG::r(r)); 1535 } 1536 } else { 1537 OPEN_AND(cg, frag); 1538 for (int r_c : par_leaves) { 1539 PUSH_INSTR(frag, BytecodeStream::PUSH, CG::r(r_c)); 1540 } 1541 for (int r_c : var_leaves) { 1542 PUSH_INSTR(frag, BytecodeStream::PUSH, CG::r(r_c)); 1543 } 1544 CLOSE_AGG(cg, frag); 1545 PUSH_INSTR(frag, BytecodeStream::POP, CG::r(r)); 1546 } 1547 return {r, var_leaves.empty()}; 1548#else 1549 std::vector<CG_Cond::T> par_leaves; 1550 std::vector<CG_Cond::T> var_leaves; 1551 collect_and_leaves(par_leaves, var_leaves, cond, cg, frag); 1552 1553 // If we had a conjunction, there should be at least two leaves. 1554 assert(var_leaves.size() + par_leaves.size() > 1); 1555 1556 // If there is at least one par child, we're going to do shortcutting 1557 // -- so we need a jump, and to push a fresh env. 1558 int r = GET_REG(cg); 1559 int lblE = 0xdeadbeef; 1560 if (par_leaves.size() > 0) { 1561 lblE = GET_LABEL(cg); 1562 cg.env_push(); 1563 1564 PUSH_INSTR(frag, BytecodeStream::IMMI, CG::i(0), CG::r(r)); 1565 for (int ii = 0; ii < par_leaves.size() - 1; ii++) { 1566 CG_Cond::T c(par_leaves[ii]); 1567 int r_c = CG::force(c, BytecodeProc::FUN, cg, frag); 1568 PUSH_INSTR(frag, BytecodeStream::JMPIFNOT, CG::r(r_c), CG::l(lblE)); 1569 } 1570 CG_Cond::T c(par_leaves.back()); 1571 int r_c = CG::force(c, BytecodeProc::FUN, cg, frag); 1572 if (var_leaves.size() > 0) { 1573 PUSH_INSTR(frag, BytecodeStream::JMPIFNOT, CG::r(r_c), CG::l(lblE)); 1574 } else { 1575 PUSH_INSTR(frag, BytecodeStream::MOV, CG::r(r_c), CG::r(r)); 1576 } 1577 } 1578 // Excludes the 0 case, because it's just been handled. 1579 if (var_leaves.size() == 1) { 1580 // Exactly one var. Don't open a context; 1581 // we return either the result register 1582 // or the forced cond. 1583 int r_c = CG::force(var_leaves[0], BytecodeProc::FUN, cg, frag); 1584 PUSH_INSTR(frag, BytecodeStream::MOV, CG::r(r_c), CG::r(r)); 1585 } else if (var_leaves.size() > 1) { 1586 // In this case, we need to open a context. 1587 OPEN_AND(cg, frag); 1588 for (CG_Cond::T c : var_leaves) { 1589 int r_c = CG::force(c, BytecodeProc::FUN, cg, frag); 1590 PUSH_INSTR(frag, BytecodeStream::PUSH, CG::r(r_c)); 1591 } 1592 CLOSE_AGG(cg, frag); 1593 PUSH_INSTR(frag, BytecodeStream::POP, CG::r(r)); 1594 } 1595 if (par_leaves.size() > 0) { // We did some shortcutting. 1596 PUSH_LABEL(frag, lblE); 1597 cg.env_pop(); 1598 } 1599 return {r, var_leaves.empty()}; 1600#endif 1601 } else { 1602 assert(p->kind() == CG_Cond::CC_And && cond.sign()); 1603#ifndef MZNC_COLLECT_LEAVES 1604 std::vector<int> var_leaves; 1605 std::vector<int> par_leaves; 1606 int r = GET_REG(cg); 1607 // I don't think these are necessary 1608 force_or_leaves(var_leaves, par_leaves, ~cond, cg, frag); 1609 if (var_leaves.empty()) { 1610 PUSH_INSTR(frag, BytecodeStream::MOV, CG::r(par_leaves[0]), CG::r(r)); 1611 for (int i = 1; i < par_leaves.size(); ++i) { 1612 PUSH_INSTR(frag, BytecodeStream::OR, CG::r(r), CG::r(par_leaves[i]), CG::r(r)); 1613 } 1614 } else { 1615 OPEN_OR(cg, frag); 1616 for (int r_c : par_leaves) { 1617 PUSH_INSTR(frag, BytecodeStream::PUSH, CG::r(r_c)); 1618 } 1619 for (int r_c : var_leaves) { 1620 PUSH_INSTR(frag, BytecodeStream::PUSH, CG::r(r_c)); 1621 } 1622 CLOSE_AGG(cg, frag); 1623 PUSH_INSTR(frag, BytecodeStream::POP, CG::r(r)); 1624 } 1625 return {r, var_leaves.empty()}; 1626#else 1627 std::vector<CG_Cond::T> par_leaves; 1628 std::vector<CG_Cond::T> var_leaves; 1629 collect_or_leaves(par_leaves, var_leaves, ~cond, cg, frag); 1630 1631 // If we had a conjunction, there should be at least two leaves. 1632 assert(var_leaves.size() + par_leaves.size() > 1); 1633 1634 // If there is at least one par child, we're going to do shortcutting 1635 // -- so we need a jump, and to push a fresh env. 1636 int r = GET_REG(cg); 1637 int lblE = 0xdeadbeef; 1638 if (par_leaves.size() > 0) { 1639 lblE = GET_LABEL(cg); 1640 cg.env_push(); 1641 PUSH_INSTR(frag, BytecodeStream::IMMI, CG::i(1), CG::r(r)); 1642 1643 for (int ii = 0; ii < par_leaves.size() - 1; ii++) { 1644 CG_Cond::T c(par_leaves[ii]); 1645 int r_c = CG::force(c, BytecodeProc::FUN, cg, frag); 1646 PUSH_INSTR(frag, BytecodeStream::JMPIF, CG::r(r_c), CG::l(lblE)); 1647 } 1648 CG_Cond::T c(par_leaves.back()); 1649 int r_c = CG::force(c, BytecodeProc::FUN, cg, frag); 1650 if (var_leaves.size() > 0) { 1651 PUSH_INSTR(frag, BytecodeStream::JMPIF, CG::r(r_c), CG::l(lblE)); 1652 } else { 1653 PUSH_INSTR(frag, BytecodeStream::MOV, CG::r(r_c), CG::r(r)); 1654 } 1655 } 1656 // Excludes the 0 case, because it's just been handled. 1657 if (var_leaves.size() == 1) { 1658 // Exactly one var. Don't open a context; 1659 // we return either the result register 1660 // or the forced cond. 1661 int r_c = CG::force(var_leaves[0], BytecodeProc::FUN, cg, frag); 1662 PUSH_INSTR(frag, BytecodeStream::MOV, CG::r(r_c), CG::r(r)); 1663 } else if (var_leaves.size() > 1) { 1664 // In this case, we need to open a context. 1665 OPEN_OR(cg, frag); 1666 for (CG_Cond::T c : var_leaves) { 1667 int r_c = CG::force(c, BytecodeProc::FUN, cg, frag); 1668 PUSH_INSTR(frag, BytecodeStream::PUSH, CG::r(r_c)); 1669 } 1670 CLOSE_AGG(cg, frag); 1671 PUSH_INSTR(frag, BytecodeStream::POP, CG::r(r)); 1672 } 1673 if (par_leaves.size() > 0) { // We did some shortcutting. 1674 PUSH_LABEL(frag, lblE); 1675 cg.env_pop(); 1676 } 1677 return {r, var_leaves.empty()}; 1678#endif 1679 } 1680} 1681int CG::force(CG_Cond::T _cond, Mode ctx, CodeGen& cg, CG_Builder& frag) { 1682 GCLock lock; 1683 // Negate condition if forced in negated context 1684 CG_Cond::T cond = ctx.is_neg() ? ~_cond : _cond; 1685 // Check if the condition is already forced. 1686 CG_Cond::_T* p(cond.get()); 1687 bool sign(cond.sign()); 1688 if (!p) { 1689 return bind_cst(!sign, cg, frag); 1690 } 1691 if (p->reg[sign].has_reg()) { 1692 return p->reg[sign].reg; 1693 } 1694 if (p->reg[1 - sign].has_reg()) { 1695 // Emit the negation 1696 int r(GET_REG(cg)); 1697 if (p->reg[1 - sign].is_par) { 1698 PUSH_INSTR(frag, BytecodeStream::NOT, CG::r(p->reg[1 - sign].reg), CG::r(r)); 1699 } else { 1700 auto fun = 1701 find_call_fun(cg, {"op_not"}, Type::varbool(), {Type::varbool()}, BytecodeProc::FUN); 1702 assert(std::get<1>(fun) == BytecodeProc::FUN); 1703 PUSH_INSTR(frag, BytecodeStream::CALL, BytecodeProc::FUN, std::get<0>(fun), 1704 CG::i(!std::get<2>(fun)), CG::r(p->reg[1 - sign].reg)); 1705 PUSH_INSTR(frag, BytecodeStream::POP, CG::r(r)); 1706 } 1707 p->reg[sign] = CG_Cond::cond_reg(r, p->reg[1 - sign].is_par); 1708 return r; 1709 } 1710 auto forced = _force_cond(cond, cg, frag); 1711 p->reg[sign] = CG_Cond::cond_reg(forced.first, forced.second); 1712 return forced.first; 1713} 1714 1715CG::Binding CG::force_or_bind(Expression* e, Mode ctx, CodeGen& cg, CG_Builder& frag) { 1716 if (e->type().isbool()) { 1717 return {CG::force(CG::compile(e, cg, frag), ctx, cg, frag), CG_Cond::ttt()}; 1718 } 1719 return CG::bind(e, cg, frag); 1720} 1721 1722int CG::force_or_bind(Expression* e, Mode ctx, std::vector<CG_Cond::T>& cond, CodeGen& cg, 1723 CG_Builder& frag) { 1724 if (e->type().isbool()) { 1725 return CG::force(CG::compile(e, cg, frag), ctx, cg, frag); 1726 } else { 1727 CG::Binding b(CG::bind(e, cg, frag)); 1728 if (b.second.get()) { 1729 cond.push_back(b.second); 1730 } 1731 return b.first; 1732 } 1733} 1734 1735class EnvInit : public ItemVisitor { 1736private: 1737 friend class ItemIter<EnvInit>; 1738 1739 EnvInit(CodeGen& _cg, Model& _m) : cg(_cg), m(_m) {} 1740 1741 /// Enter model 1742 bool enterModel(Model* m) { return true; } 1743 /// Enter item 1744 bool enter(Item* m) { return true; } 1745 /// Visit variable declaration 1746 void vVarDeclI(VarDeclI* vdi) { 1747 VarDecl* vd(vdi->e()); 1748 if (!vd->type().isann()) { 1749 if (vd->type().ispar()) { 1750 if (!vd->e()) { 1751 // cg.env().bind(vd->id()->v(), Loc::global(cg.num_globals)); 1752 int g = cg.add_global(vd, true); 1753 } 1754 } else { 1755 // If it's a var with a body, feed it into the mode analyser. 1756 // TODO: Because mode analysis is not interprocedural, we have to 1757 // assume global params may be used in any context. 1758 modes.def(vd, BytecodeProc::ROOT); 1759 modes.use(vd, BytecodeProc::FUN); 1760 } 1761 } 1762 } 1763 void vConstraintI(ConstraintI* c) { modes.use(c->e(), BytecodeProc::ROOT); } 1764 /// Visit assign item 1765 void vAssignI(AssignI* ass) {} 1766 1767 void vFunctionI(FunctionI* f) { cg.register_function(f); } 1768 1769 void vSolveI(SolveI* si) { 1770 GCLock lock; 1771 if (si->st() == SolveI::ST_SAT) { 1772 return; 1773 } 1774 ASTString ident("solve_this"); 1775 int mode; 1776 Expression* objective; 1777 switch (si->st()) { 1778 case SolveI::ST_SAT: 1779 mode = 0; 1780 objective = IntLit::a(0); 1781 break; 1782 case SolveI::ST_MIN: 1783 mode = 1; 1784 objective = si->e(); 1785 break; 1786 case SolveI::ST_MAX: 1787 mode = 2; 1788 objective = si->e(); 1789 break; 1790 } 1791 Expression* search_a; 1792 int search_var = 0; 1793 int search_val = 0; 1794 Call* c; 1795 if (Call* ann = si->ann().getCall(ASTString("int_search"))) { 1796 search_a = ann->arg(0); 1797 Id* varsel = ann->arg(1)->cast<Id>(); 1798 if (varsel->idn() == -1 && varsel->v() == ASTString("input_order")) { 1799 search_var = 1; 1800 } else if (varsel->idn() == -1 && varsel->v() == ASTString("first_fail")) { 1801 search_var = 2; 1802 } 1803 Id* valsel = ann->arg(2)->cast<Id>(); 1804 if (valsel->idn() == -1 && valsel->v() == ASTString("indomain_min")) { 1805 search_val = 1; 1806 } else if (valsel->idn() == -1 && valsel->v() == ASTString("indomain_max")) { 1807 search_val = 2; 1808 } 1809 c = new Call( 1810 si->loc(), ident, 1811 {IntLit::a(mode), objective, search_a, IntLit::a(search_var), IntLit::a(search_val)}); 1812 } else { 1813 c = new Call(si->loc(), ident, {IntLit::a(mode), objective}); 1814 } 1815 c->type(Type::varbool()); 1816 m.addItem(new ConstraintI(Location().introduce(), c)); 1817 } 1818 1819 CodeGen& cg; 1820 Model& m; 1821 ModeAnalysis modes; 1822 1823public: 1824 static void run(CodeGen& cg, Model* m) { 1825 EnvInit eb(cg, *m); 1826 iterItems(eb, m); 1827 cg.mode_map = std::move(eb.modes.extract()); 1828 } 1829}; 1830 1831struct ShowVal { 1832 ShowVal(CodeGen& _cg, CG_Value _v) : cg(_cg), v(_v) {} 1833 1834 CodeGen& cg; 1835 CG_Value v; 1836}; 1837std::ostream& operator<<(std::ostream& o, ShowVal s) { 1838 switch (s.v.kind) { 1839 case CG_Value::V_Immi: 1840 o << s.v.value; 1841 break; 1842 case CG_Value::V_Global: 1843 o << s.v.value; 1844 break; 1845 case CG_Value::V_Reg: 1846 o << "R" << s.v.value; 1847 break; 1848 case CG_Value::V_Proc: 1849 o << "p" << s.v.value; 1850 break; 1851 case CG_Value::V_Label: 1852 o << "l" << s.v.value; 1853 } 1854 return o; 1855} 1856 1857template <class O> 1858void show_frag(O& out, CodeGen& cg, std::vector<CG_Instr>& frag) { 1859 auto show = [&cg](CG_Value v) { return ShowVal(cg, v); }; 1860 1861 int num_agg = 0; 1862 for (CG_Instr& i : frag) { 1863 auto op(static_cast<BytecodeStream::Instr>(i.tag >> 1)); 1864 if (op == BytecodeStream::CLOSE_AGGREGATION) { 1865 num_agg--; 1866 } 1867 for (int j = 0; j < num_agg; ++j) { 1868 out << " "; 1869 } 1870 if (i.tag & 1) { 1871 out << "l" << (i.tag >> 1) << ": "; 1872 continue; 1873 } 1874 1875 out << instr_name(op); 1876 switch (op) { 1877 case BytecodeStream::OPEN_AGGREGATION: 1878 out << " " << agg_name((AggregationCtx::Symbol)i.params[0].value); 1879 num_agg++; 1880 break; 1881 case BytecodeStream::BUILTIN: { 1882 CG_ProcID p(CG_ProcID::of_val(i.params[0])); 1883 assert(p.is_builtin()); 1884 out << " " << cg._builtins[p.id()].first; 1885 for (int ii = 1; ii < i.params.size(); ++ii) { 1886 out << " " << show(i.params[ii]); 1887 } 1888 break; 1889 } 1890 case BytecodeStream::CALL: { 1891 // Mode 1892 out << " " << mode_name((BytecodeProc::Mode)i.params[0].value); 1893 // Procedure 1894 CG_ProcID p(CG_ProcID::of_val(i.params[1])); 1895 if (p.is_builtin()) { 1896 out << " " << cg._builtins[p.id()].first; 1897 } else { 1898 out << " " << cg.bytecode[p.id()].ident; 1899 } 1900 // CSE flag 1901 out << " " << show(i.params[2]); 1902 // Arguments 1903 for (int ii = 3; ii < i.params.size(); ++ii) { 1904 out << " " << show(i.params[ii]); 1905 } 1906 break; 1907 } 1908 case BytecodeStream::TCALL: { 1909 // Mode 1910 out << " " << mode_name((BytecodeProc::Mode)i.params[0].value); 1911 // Procedure 1912 CG_ProcID p(CG_ProcID::of_val(i.params[1])); 1913 if (p.is_builtin()) { 1914 out << " " << cg._builtins[p.id()].first; 1915 } else { 1916 out << " " << cg.bytecode[p.id()].ident; 1917 } 1918 // CSE flag 1919 out << " " << show(i.params[2]); 1920 break; 1921 } 1922 default: 1923 for (CG_Value p : i.params) out << " " << show(p); 1924 } 1925 out << std::endl; 1926 } 1927} 1928 1929template <class O> 1930void show(O& out, CodeGen& cg) { 1931 { 1932 GCLock lock; 1933 Model m; 1934 for (auto g : cg.globals_env) { 1935 if (g.second.second) { 1936 TypeInst* ti = g.first->ti(); 1937 // TODO: This removes information from the origin model. Could be reverted after printing 1938 if (ti->isarray()) { 1939 std::vector<TypeInst*> ranges(ti->ranges().size()); 1940 for (int i = 0; i < ranges.size(); ++i) { 1941 ranges[i] = new TypeInst(Location().introduce(), Type::parint()); 1942 } 1943 auto nti = new TypeInst(Location().introduce(), ti->type(), ranges); 1944 g.first->ti(nti); 1945 } else if (ti->domain()) { 1946 auto nti = new TypeInst(Location().introduce(), ti->type()); 1947 g.first->ti(nti); 1948 } 1949 g.first->ann().add(new Call(Location().introduce(), constants().ann.global_register, 1950 {IntLit::a(g.second.first)})); 1951 m.addItem(new VarDeclI(Location().introduce(), g.first)); 1952 } 1953 } 1954 for (auto fun : cg.req_solver_predicates) { 1955 m.addItem(fun); 1956 } 1957 MiniZinc::Printer p(out, 0); 1958 p.print(&m); 1959 } 1960 out << "@@@@@@@@@@" << std::endl; 1961 for (auto b : cg._builtins) { 1962 out << ":" << b.first << ": " << b.second << std::endl; 1963 } 1964 for (auto& p : cg.bytecode) { 1965 for (BytecodeProc::Mode m : p) { 1966 out << ":" << p.ident << ":" << mode_name(m) << " " << p.arity << std::endl; 1967 show_frag(out, cg, p.body(m)); 1968 } 1969 } 1970} 1971 1972/* 1973void post_cond(CodeGen& cg, CG_Builder& frag, CG_Cond::T cond) { 1974 if(!cond.get()) { 1975 assert(!cond.sign()); 1976 return; 1977 } 1978 1979 std::vector<int> leaves; 1980 force_and_leaves(leaves, cond, cg, frag); 1981 for(int r_c : leaves) 1982 PUSH_INSTR(frag, BytecodeStream::POST, CG::r(r_c)); 1983} 1984*/ 1985void post_cond(CodeGen& cg, CG_Builder& frag, CG_Cond::T cond) { 1986 GCLock lock; 1987 if (!cond.get()) { 1988 if (cond.sign()) PUSH_INSTR(frag, BytecodeStream::POST, CG::r(bind_cst(0, cg, frag))); 1989 return; 1990 } 1991 CG_Cond::_T* p(cond.get()); 1992 bool sign(cond.sign()); 1993 if (p->reg[sign].is_root) return; 1994 if (p->reg[1 - sign].is_root) { 1995 PUSH_INSTR(frag, BytecodeStream::POST, CG::r(bind_cst(0, cg, frag))); 1996 return; 1997 } 1998 1999 if (p->reg[sign].has_reg()) { 2000 PUSH_INSTR(frag, BytecodeStream::POST, CG::r(p->reg[sign].reg)); 2001 } else if (p->reg[1 - sign].has_reg()) { 2002 int r(GET_REG(cg)); 2003 if (p->reg[1 - sign].is_par) { 2004 PUSH_INSTR(frag, BytecodeStream::NOT, CG::r(p->reg[1 - sign].reg), CG::r(r)); 2005 } else { 2006 auto fun = 2007 find_call_fun(cg, {"op_not"}, Type::varbool(), {Type::varbool()}, BytecodeProc::FUN); 2008 assert(std::get<1>(fun) == BytecodeProc::FUN); 2009 PUSH_INSTR(frag, BytecodeStream::CALL, BytecodeProc::FUN, std::get<0>(fun), 2010 CG::i(!std::get<2>(fun)), CG::r(p->reg[1 - sign].reg)); 2011 PUSH_INSTR(frag, BytecodeStream::POP, CG::r(r)); 2012 } 2013 PUSH_INSTR(frag, BytecodeStream::POST, CG::r(r)); 2014 p->reg[sign] = CG_Cond::cond_reg(r, p->reg[1 - sign].is_par); 2015 } else if (p->kind() == CG_Cond::CC_Call) { 2016 CG_Cond::C_Call* call(static_cast<CG_Cond::C_Call*>(p)); 2017 CG::Mode call_m(CG::Mode::Root, sign); 2018 std::vector<Type> ty(call->ty.begin() + 1, call->ty.end()); 2019 auto fun = find_call_fun(cg, call->ident, call->ty[0], ty, call_m); 2020 if (call_m == std::get<1>(fun)) { 2021 PUSH_INSTR(frag, BytecodeStream::CALL, call_m, std::get<0>(fun), 2022 CG::i((!std::get<2>(fun)) && call->cse), call->params); 2023 } else { 2024 assert(std::get<1>(fun) == BytecodeProc::FUN); 2025 std::cerr << "Warning: emitting reification for " << call->ident 2026 << " because no ROOT_NEG version is available\n"; 2027 PUSH_INSTR(frag, BytecodeStream::CALL, std::get<1>(fun), std::get<0>(fun), 2028 CG::i((!std::get<2>(fun)) && call->cse), call->params); 2029 int r = GET_REG(cg); 2030 PUSH_INSTR(frag, BytecodeStream::POP, CG::r(r)); 2031 if (BytecodeProc::is_neg(call->m) != BytecodeProc::is_neg(std::get<1>(fun))) { 2032 if (call->ty[0].ispar()) { 2033 PUSH_INSTR(frag, BytecodeStream::NOT, CG::r(r), CG::r(r)); 2034 } else { 2035 auto fun = 2036 find_call_fun(cg, {"op_not"}, Type::varbool(), {Type::varbool()}, BytecodeProc::FUN); 2037 assert(std::get<1>(fun) == BytecodeProc::FUN); 2038 PUSH_INSTR(frag, BytecodeStream::CALL, BytecodeProc::FUN, std::get<0>(fun), 2039 CG::i(!std::get<2>(fun)), CG::r(r)); 2040 PUSH_INSTR(frag, BytecodeStream::POP, CG::r(r)); 2041 } 2042 } 2043 PUSH_INSTR(frag, BytecodeStream::POST, CG::r(r)); 2044 } 2045 } else { 2046 assert(p->kind() == CG_Cond::CC_And); 2047 CG_Cond::C_And* conj(reinterpret_cast<CG_Cond::C_And*>(p)); 2048 if (!sign) { 2049 // Recurse. 2050 for (CG_Cond::T child : conj->children) post_cond(cg, frag, child); 2051 } else { 2052 // FIXME: Check force context. 2053 PUSH_INSTR(frag, BytecodeStream::POST, CG::r(CG::force(cond, BytecodeProc::FUN, cg, frag))); 2054 } 2055 } 2056 p->reg[sign].is_root = true; 2057} 2058 2059// FIXME: This always forces calls outside the aggregation. 2060void aggregate_cond(CodeGen& cg, CG_Builder& frag, CG_Cond::T cond) { 2061 CG_Cond::_T* p(cond.get()); 2062 bool sign(cond.sign()); 2063 if (!p) { 2064 // int r_val(CG::locate_immi(1 - sign, cg, frag)); 2065 int r_val(bind_cst(1 - sign, cg, frag)); 2066 PUSH_INSTR(frag, BytecodeStream::PUSH, CG::r(r_val)); 2067 return; 2068 } 2069 if (p->reg[sign].has_reg()) { 2070 PUSH_INSTR(frag, BytecodeStream::PUSH, CG::r(p->reg[sign].reg)); 2071 return; 2072 } else if (p->reg[1 - sign].has_reg()) { 2073 int r_neg(p->reg[1 - sign].reg); 2074 if (p->reg[1 - sign].is_par) { 2075 int r(GET_REG(cg)); 2076 PUSH_INSTR(frag, BytecodeStream::NOT, CG::r(r_neg), CG::r(r)); 2077 PUSH_INSTR(frag, BytecodeStream::PUSH, CG::r(r)); 2078 } else { 2079 auto fun = 2080 find_call_fun(cg, {"op_not"}, Type::varbool(), {Type::varbool()}, BytecodeProc::FUN); 2081 assert(std::get<1>(fun) == BytecodeProc::FUN); 2082 PUSH_INSTR(frag, BytecodeStream::CALL, BytecodeProc::FUN, std::get<0>(fun), 2083 CG::i(!std::get<2>(fun)), CG::r(r_neg)); 2084 } 2085 return; 2086 } 2087 std::vector<int> var_leaves; 2088 std::vector<int> par_leaves; 2089 if (p->kind() == CG_Cond::CC_And) { 2090 if (!sign) { 2091 force_and_leaves(var_leaves, par_leaves, cond, cg, frag); 2092 int r; 2093 if (var_leaves.empty()) { 2094 r = GET_REG(cg); 2095 PUSH_INSTR(frag, BytecodeStream::MOV, CG::r(par_leaves[0]), CG::r(r)); 2096 for (int i = 1; i < par_leaves.size(); ++i) { 2097 PUSH_INSTR(frag, BytecodeStream::AND, CG::r(r), CG::r(par_leaves[i]), CG::r(r)); 2098 } 2099 PUSH_INSTR(frag, BytecodeStream::PUSH, CG::r(r)); 2100 } else { 2101 OPEN_AND(cg, frag); 2102 for (int r_c : par_leaves) { 2103 PUSH_INSTR(frag, BytecodeStream::PUSH, CG::r(r_c)); 2104 } 2105 for (int r_c : var_leaves) { 2106 PUSH_INSTR(frag, BytecodeStream::PUSH, CG::r(r_c)); 2107 } 2108 CLOSE_AGG(cg, frag); 2109 } 2110 } else { 2111 force_or_leaves(var_leaves, par_leaves, ~cond, cg, frag); 2112 int r; 2113 if (var_leaves.empty()) { 2114 r = GET_REG(cg); 2115 PUSH_INSTR(frag, BytecodeStream::MOV, CG::r(par_leaves[0]), CG::r(r)); 2116 for (int i = 1; i < par_leaves.size(); ++i) { 2117 PUSH_INSTR(frag, BytecodeStream::OR, CG::r(r), CG::r(par_leaves[i]), CG::r(r)); 2118 } 2119 PUSH_INSTR(frag, BytecodeStream::PUSH, CG::r(r)); 2120 } else { 2121 OPEN_OR(cg, frag); 2122 for (int r_c : par_leaves) { 2123 PUSH_INSTR(frag, BytecodeStream::PUSH, CG::r(r_c)); 2124 } 2125 for (int r_c : var_leaves) { 2126 PUSH_INSTR(frag, BytecodeStream::PUSH, CG::r(r_c)); 2127 } 2128 CLOSE_AGG(cg, frag); 2129 } 2130 } 2131 } else { 2132 // FIXME: Check force context 2133 PUSH_INSTR(frag, BytecodeStream::PUSH, CG::r(CG::force(cond, BytecodeProc::FUN, cg, frag))); 2134 } 2135} 2136 2137int locate_range(int l, int u, CodeGen& cg, CG_Builder& frag) { 2138 CG::Binding b; 2139 if (cg.env().cache_lookup_range(l, u, b)) { 2140 return b.first; 2141 } 2142 if (l == 0 && u == 1) { 2143 PUSH_INSTR(frag, BytecodeStream::BUILTIN, cg.find_builtin("boolean_domain")); 2144 int r = GET_REG(cg); 2145 PUSH_INSTR(frag, BytecodeStream::POP, CG::r(r)); 2146 cg.env().cache_store_range(l, u, std::make_pair(r, CG_Cond::ttt())); 2147 return r; 2148 } 2149 2150 OPEN_VEC(cg, frag); 2151 int r = GET_REG(cg); 2152 PUSH_INSTR(frag, BytecodeStream::IMMI, CG::i(l), CG::r(r)); 2153 PUSH_INSTR(frag, BytecodeStream::PUSH, CG::r(r)); 2154 PUSH_INSTR(frag, BytecodeStream::IMMI, CG::i(u), CG::r(r)); 2155 PUSH_INSTR(frag, BytecodeStream::PUSH, CG::r(r)); 2156 CLOSE_AGG(cg, frag); 2157 r = GET_REG(cg); 2158 PUSH_INSTR(frag, BytecodeStream::POP, CG::r(r)); 2159 cg.env().cache_store_range(l, u, std::make_pair(r, CG_Cond::ttt())); 2160 return r; 2161} 2162 2163CG::Binding bind_domain(VarDecl* vd, CodeGen& cg, CG_Builder& frag) { 2164 if (vd->type().bt() == Type::BT_BOOL) { 2165 int r(GET_REG(cg)); 2166 PUSH_INSTR(frag, BytecodeStream::BUILTIN, cg.find_builtin("boolean_domain")); 2167 PUSH_INSTR(frag, BytecodeStream::POP, CG::r(r)); 2168 return {r, CG_Cond::ttt()}; 2169 } 2170 if (Expression* d = vd->ti()->domain()) { 2171 // Ignoring partiality here. 2172 return CG::bind(d, cg, frag); 2173 } 2174 int r(GET_REG(cg)); 2175 PUSH_INSTR(frag, BytecodeStream::BUILTIN, cg.find_builtin("infinite_domain")); 2176 PUSH_INSTR(frag, BytecodeStream::POP, CG::r(r)); 2177 return {r, CG_Cond::ttt()}; 2178} 2179 2180class Compile : public ItemVisitor { 2181private: 2182 friend class ItemIter<Compile>; 2183 2184 Compile(CodeGen& _cg) : cg(_cg) /*, bool_dom(-1) */ { 2185 // Force the compilation of predicate definitions that can be generated by the compiler 2186 find_call_fun(cg, {"op_not"}, Type::varbool(), {Type::varbool()}, BytecodeProc::FUN); 2187 find_call_fun(cg, {"bool_clause"}, Type::varbool(), {Type::varbool(1), Type::varbool(1)}, 2188 BytecodeProc::ROOT, true); 2189 find_call_fun(cg, {"bool_clause_reif"}, Type::varbool(), 2190 {Type::varbool(1), Type::varbool(1), Type::varbool()}, BytecodeProc::ROOT, true); 2191 find_call_fun(cg, {"int_lin_eq"}, Type::varbool(), 2192 {Type::parint(1), Type::varint(1), Type::parint()}, BytecodeProc::ROOT, true); 2193 } 2194 2195 /// Enter model 2196 bool enterModel(Model* m) { return true; } 2197 /// Enter item 2198 bool enter(Item* m) { return true; } 2199 /// Visit variable declaration 2200 void vVarDeclI(VarDeclI* vdi) { 2201 VarDecl* vd(vdi->e()); 2202 if (vd->type().isann()) return; 2203 if (vd->type().isopt()) return; 2204 if (vd->type().isvar()) { 2205 // In whatever case, we're going to create something, 2206 // and dump it in a register. 2207 int r_var; 2208 if (vd->e()) { 2209 // Defined 2210 // Evaluate the definition in root context, 2211 // add it to a register 2212 // r_var = CG::locate(vd->e(), BytecodeProc::ROOT, cg, root_frag); 2213 CG::Binding b_var(CG::bind(vd->e(), cg, root_frag)); 2214 r_var = b_var.first; 2215 // TODO: Special case for root stuff. 2216 post_cond(cg, root_frag, b_var.second); 2217 2218 if (Expression* d = vd->ti()->domain()) { 2219 if (!vd->ti()->isarray()) { 2220 CG::Binding b_d = CG::bind(d, cg, root_frag); 2221 int r_dp = GET_REG(cg); 2222 PUSH_INSTR(root_frag, BytecodeStream::INTERSECT_DOMAIN, CG::r(r_var), CG::r(b_d.first), 2223 CG::r(r_dp)); 2224 post_cond(cg, root_frag, b_d.second); 2225 } else { 2226 // Iterate over the elements of the variable we just created, and 2227 // bind the corresponding domain. 2228 CG::Binding b_d = CG::bind(d, cg, root_frag); 2229 int r_dp = GET_REG(cg); 2230 ITER_ARRAY(cg, root_frag, r_var, [b_d, r_dp](CodeGen& cg, CG_Builder& frag, int r_elt) { 2231 PUSH_INSTR(frag, BytecodeStream::INTERSECT_DOMAIN, CG::r(r_elt), CG::r(b_d.first), 2232 CG::r(r_dp)); 2233 }); 2234 post_cond(cg, root_frag, b_d.second); 2235 } 2236 } 2237 } else { 2238 CG::Binding b_d(bind_domain(vd, cg, root_frag)); 2239 post_cond(cg, root_frag, b_d.second); 2240 int r_d = b_d.first; 2241 2242 r_var = GET_REG(cg); 2243 if (vd->ti()->isarray()) { 2244 // Open nested iterators 2245 int rTMP(GET_REG(cg)); 2246 std::vector<int> r_regs; 2247 for (Expression* r : vd->ti()->ranges()) { 2248 Expression* dim(r->template cast<TypeInst>()->domain()); 2249 CG::Binding b_reg(CG::bind(dim, cg, root_frag)); 2250 post_cond(cg, root_frag, b_reg.second); 2251 r_regs.push_back(b_reg.first); 2252 } 2253 std::vector<Forset> nesting; 2254 OPEN_VEC(cg, root_frag); 2255 for (int r_r : r_regs) { 2256 Forset iter(cg, r_r); 2257 nesting.push_back(iter); 2258 iter.emit_pre(root_frag); 2259 } 2260 PUSH_INSTR(root_frag, BytecodeStream::CALL, BytecodeProc::ROOT, 2261 cg.find_builtin("mk_intvar"), CG::i(0), CG::r(r_d)); 2262 for (int r_i = r_regs.size() - 1; r_i >= 0; --r_i) { 2263 nesting[r_i].emit_post(root_frag); 2264 } 2265 CLOSE_AGG(cg, root_frag); 2266 2267 PUSH_INSTR(root_frag, BytecodeStream::POP, CG::r(r_var)); 2268 OPEN_VEC(cg, root_frag); 2269 for (int r_r : r_regs) { 2270 PUSH_INSTR(root_frag, BytecodeStream::GET_VEC, CG::r(r_r), 2271 CG::r(bind_cst(1, cg, root_frag)), CG::r(rTMP)); 2272 PUSH_INSTR(root_frag, BytecodeStream::PUSH, CG::r(rTMP)); 2273 PUSH_INSTR(root_frag, BytecodeStream::GET_VEC, CG::r(r_r), 2274 CG::r(bind_cst(2, cg, root_frag)), CG::r(rTMP)); 2275 PUSH_INSTR(root_frag, BytecodeStream::PUSH, CG::r(rTMP)); 2276 } 2277 CLOSE_AGG(cg, root_frag); 2278 PUSH_INSTR(root_frag, BytecodeStream::POP, CG::r(rTMP)); 2279 2280 PUSH_INSTR(root_frag, BytecodeStream::BUILTIN, cg.find_builtin("array_Xd"), CG::r(r_var), 2281 CG::r(rTMP)); 2282 } else { 2283 PUSH_INSTR(root_frag, BytecodeStream::CALL, BytecodeProc::ROOT, 2284 cg.find_builtin("mk_intvar"), CG::i(0), CG::r(r_d)); 2285 } 2286 PUSH_INSTR(root_frag, BytecodeStream::POP, CG::r(r_var)); 2287 } 2288 // Now copy it into a global, and add it to the env. 2289 int g = cg.add_global(vd, false); 2290 PUSH_INSTR(root_frag, BytecodeStream::STORE_GLOBAL, CG::r(r_var), CG::g(g)); 2291 2292 // Since it's still in a register, add it to the current env as well. 2293 cg.env().bind(vd->id()->str(), CG::Binding(r_var, CG_Cond::ttt())); 2294 } else { 2295 // For par identifiers with definitions, we evaluate them. 2296 if (vd->e() && !vd->type().isann()) { 2297 // Evaluate the definition. 2298 // int r = vd->type().ispar() ? CG::locate_par(vd->e(), cg, root_frag) : CG::locate(vd->e(), 2299 // BytecodeProc::ROOT, cg, root_frag); 2300 int r; 2301 if (vd->type().isbool()) { 2302 r = CG::force(CG::compile(vd->e(), cg, root_frag), BytecodeProc::ROOT, cg, root_frag); 2303 } else { 2304 // Par expressions may still introduce constraints. 2305 CG::Binding b_d = CG::bind(vd->e(), cg, root_frag); 2306 post_cond(cg, root_frag, b_d.second); 2307 r = b_d.first; 2308 } 2309 int g = cg.add_global(vd, false); 2310 PUSH_INSTR(root_frag, BytecodeStream::STORE_GLOBAL, CG::r(r), CG::g(g)); 2311 2312 cg.env().bind(vd->id()->str(), CG::Binding(r, CG_Cond::ttt())); 2313 } 2314 } 2315 } 2316 2317 /// Visit assign item 2318 void vAssignI(AssignI* ass) {} 2319 2320 void vConstraintI(ConstraintI* c) { 2321 // CG::eval(c->e(), BytecodeProc::ROOT, cg, root_frag); 2322 post_cond(cg, root_frag, CG::compile(c->e(), cg, root_frag)); 2323 } 2324 2325 void vFunctionI(FunctionI* f) { 2326 GCLock l; 2327 if (f->ann().contains(constants().ann._export)) { 2328 CG_ProcID body(cg.resolve_fun(f)); 2329 // Force the body to be created 2330 if (!body.is_builtin()) { 2331 assert(body.id() < cg.bytecode.size()); 2332 if (!cg.bytecode[body.id()].is_available(BytecodeProc::ROOT)) { 2333 cg.bytecode[body.id()].body(BytecodeProc::ROOT); 2334 cg.pending_bodies.push_back( 2335 std::make_pair(f, std::make_pair(BytecodeProc::ROOT, BytecodeProc::ROOT))); 2336 } 2337 } 2338 } 2339 } 2340 2341 // FIXME: This method of saving the CodeGen state is pretty icky. 2342 // CodeGen should probably be split into two objects. 2343 void compile_fun(CG_Builder& frag, const ASTExprVec<VarDecl>& params, Expression* e) { 2344 // Save the codegen state. 2345 auto saved_env = cg.current_env; 2346 cg.current_env = CG_Env<CG::Binding>::spawn(nullptr); 2347 int saved_regs = cg.current_reg_count; 2348 2349 cg.current_reg_count = params.size(); 2350 2351 cg._exp_scope.clear(); 2352 cg.mode_map.clear(); 2353 2354 // Rerun mode analysis 2355 ModeAnalysis modes; 2356 // FIXME: Currently assuming all functions are total. 2357 modes.def(e, BytecodeProc::ROOT); 2358 modes.use(e, BytecodeProc::ROOT); 2359 cg.mode_map = std::move(modes.extract()); 2360 2361 // Set up the new env. 2362 { 2363 GCLock l; 2364 for (int ii = 0; ii < params.size(); ++ii) { 2365 cg.env().bind(params[ii]->id()->str(), CG::Binding(ii, CG_Cond::ttt())); 2366 } 2367 } 2368 2369 // Now compile the result. 2370 CG::Binding b_res = CG::bind(e, cg, frag); 2371 if (b_res.second.p) { 2372 // FIXME: What actually happens with this forced result? 2373 // FIXME: Check force context 2374 CG::force(b_res.second, BytecodeProc::FUN, cg, frag); 2375 } 2376 PUSH_INSTR(frag, BytecodeStream::PUSH, CG::r(b_res.first)); 2377 PUSH_INSTR(frag, BytecodeStream::RET); 2378 2379 // now restore everything 2380 cg.current_reg_count = saved_regs; 2381 2382 delete cg.current_env; 2383 cg.current_env = saved_env; 2384 } 2385 2386 void compile_pred(CG_Builder& frag, const ASTExprVec<VarDecl>& params, Mode m, Expression* e) { 2387 // Save the codegen state. 2388 auto saved_env = cg.current_env; 2389 cg.current_env = CG_Env<CG::Binding>::spawn(nullptr); 2390 int saved_regs = cg.current_reg_count; 2391 2392 cg.current_reg_count = params.size(); 2393 2394 cg._exp_scope.clear(); 2395 cg.mode_map.clear(); 2396 2397 // Rerun mode analysis 2398 ModeAnalysis modes; 2399 modes.use(e, m); 2400 cg.mode_map = std::move(modes.extract()); 2401 2402 // Set up the new env. 2403 { 2404 GCLock l; 2405 for (int ii = 0; ii < params.size(); ++ii) { 2406 cg.env().bind(params[ii]->id()->str(), CG::Binding(ii, CG_Cond::ttt())); 2407 } 2408 } 2409 2410 // Now compile the result. 2411 CG_Cond::T cond(CG::compile(e, cg, frag)); 2412 if (m.strength() == Mode::Root) { 2413 post_cond(cg, frag, m.is_neg() ? ~cond : cond); 2414 } else { 2415 aggregate_cond(cg, frag, m.is_neg() ? ~cond : cond); 2416 } 2417 PUSH_INSTR(frag, BytecodeStream::RET); 2418 2419 cg.current_reg_count = saved_regs; 2420 // then restore everything. 2421 delete cg.current_env; 2422 cg.current_env = saved_env; 2423 } 2424 CodeGen& cg; 2425 CG_Builder root_frag; 2426 2427 // int bool_dom; 2428public: 2429 static void run(CodeGen& cg, Model* m) { 2430 Compile c(cg); 2431 // Compile the main model 2432 OPEN_OTHER(cg, c.root_frag); 2433 iterItems(c, m); 2434 { 2435 int old_reg_count = cg.current_reg_count; 2436 cg.current_reg_count = cg.reg_trail.back(); 2437 cg.reg_trail.pop_back(); 2438 PUSH_INSTR(c.root_frag, BytecodeStream::CLEAR, CG::r(cg.current_reg_count), 2439 CG::r(old_reg_count - 1)); 2440 cg.env_pop(); 2441 } 2442 PUSH_INSTR(c.root_frag, BytecodeStream::RET); 2443 2444 // Now generate procedures for any necessary function/predicate bodies. 2445 while (!cg.pending_bodies.empty()) { 2446 GCLock lock; 2447 auto p(cg.pending_bodies.back()); 2448 cg.pending_bodies.pop_back(); 2449 2450 FunctionI* fun(p.first); 2451 Mode call_mode(p.second.first); 2452 Mode def_mode(p.second.second); 2453 annotate_total(fun); 2454 // Building structures 2455 CG_ProcID proc(cg.resolve_fun(fun)); 2456 CG_Builder frag; 2457 // Find the body. 2458 std::vector<Type> arg_types(fun->params().size()); 2459 for (int j = 0; j < arg_types.size(); ++j) { 2460 arg_types[j] = fun->params()[j]->type(); 2461 } 2462 2463 // Introduce reification if necessary 2464 if (call_mode == BytecodeProc::IMP || call_mode == BytecodeProc::FUN || 2465 call_mode == BytecodeProc::IMP_NEG || call_mode == BytecodeProc::FUN_NEG) { 2466 auto redef = cg.fun_map.defines_mode(fun->id(), arg_types, call_mode); 2467 if (redef.first) { 2468 std::vector<Expression*> args; 2469 args.reserve(fun->params().size() + 1); 2470 for (int i = 0; i < fun->params().size(); ++i) { 2471 VarDecl* vd = fun->params()[i]; 2472 args.emplace_back(vd->id()); 2473 } 2474 TypeInst var_bool(Location().introduce(), Type::varbool()); 2475 VarDecl new_var(Location().introduce(), &var_bool, "b"); 2476 args.emplace_back(new_var.id()); 2477 Call call(Location().introduce(), redef.second, args); 2478 call.type(Type::varbool()); 2479 Let let(Location().introduce(), {&new_var, &call}, new_var.id()); 2480 let.type(Type::varbool()); 2481 let.addAnnotation(constants().ann.promise_total); 2482 c.compile_pred(frag, fun->params(), call_mode, &let); 2483 cg.append(proc.id(), call_mode, frag); 2484 continue; 2485 } 2486 } 2487 if (BytecodeProc::is_neg(call_mode)) { 2488 GCLock lock; 2489 ASTString ident(fun->id().str() + "_neg"); 2490 auto neg_bodies = std::move(cg.fun_map.get_bodies(ident, arg_types)); 2491 if (std::any_of(neg_bodies.begin(), neg_bodies.end(), 2492 [](FunctionI* fi) { return fi->e(); })) { 2493 std::vector<Expression*> args; 2494 args.reserve(fun->params().size()); 2495 for (int i = 0; i < fun->params().size(); ++i) { 2496 VarDecl* vd = fun->params()[i]; 2497 args.emplace_back(vd->id()); 2498 } 2499 Call call(Location().introduce(), ident, args); 2500 call.type(Type::varbool()); 2501 // Call negated constraint in positive context (implementation should deal with the 2502 // negation). 2503 c.compile_pred(frag, fun->params(), BytecodeProc::negate(call_mode), &call); 2504 cg.append(proc.id(), call_mode, frag); 2505 continue; 2506 } 2507 } 2508 2509 if (fun->e()) { 2510 if (fun->e()->type().isbool()) { 2511 c.compile_pred(frag, fun->params(), def_mode, fun->e()); 2512 } else { 2513 assert(def_mode == BytecodeProc::ROOT); 2514 c.compile_fun(frag, fun->params(), fun->e()); 2515 } 2516 } else { 2517 assert(call_mode == BytecodeProc::ROOT); 2518 cg.req_solver_predicates.push_back(fun); 2519 } 2520 cg.append(proc.id(), call_mode, frag); 2521 } 2522 2523 // And finally, add the entry function. 2524 int main_proc = cg.bytecode.size(); 2525 cg.bytecode.emplace_back("main", 0); 2526 cg.append(main_proc, BytecodeProc::ROOT, c.root_frag); 2527 2528 show(std::cout, cg); 2529 } 2530}; 2531 2532Mode open_conj(Mode ctx, CG_Builder& frag) { 2533 if (ctx == BytecodeProc::ROOT) return ctx; 2534 2535 PUSH_INSTR(frag, BytecodeStream::OPEN_AGGREGATION, 2536 ctx.is_neg() ? AggregationCtx::VCTX_OR : AggregationCtx::VCTX_AND); 2537 return +ctx; 2538} 2539void close_conj(Mode ctx, CG_Builder& frag) { 2540 if (ctx != BytecodeProc::ROOT) PUSH_INSTR(frag, BytecodeStream::CLOSE_AGGREGATION); 2541} 2542Mode open_disj(Mode ctx, CG_Builder& frag) { 2543 if (ctx == BytecodeProc::ROOT_NEG) return ctx; 2544 2545 PUSH_INSTR(frag, BytecodeStream::OPEN_AGGREGATION, 2546 ctx.is_neg() ? AggregationCtx::VCTX_AND : AggregationCtx::VCTX_OR); 2547 return +ctx; 2548} 2549void close_disj(Mode ctx, CG_Builder& frag) { 2550 if (ctx != BytecodeProc::ROOT_NEG) PUSH_INSTR(frag, BytecodeStream::CLOSE_AGGREGATION); 2551} 2552 2553Mode left_child_ctx(Mode ctx, BinOpType b) { 2554 switch (b) { 2555 // Only Boolean operators change the mode. 2556 case BOT_AND: 2557 return ctx.is_neg() ? +ctx : ctx; 2558 case BOT_OR: 2559 case BOT_RIMPL: 2560 return ctx.is_neg() ? ctx : +ctx; 2561 case BOT_IMPL: 2562 return ctx.is_neg() ? -ctx : -(+ctx); 2563 case BOT_EQUIV: 2564 case BOT_XOR: 2565 return *ctx; 2566 default: 2567 return ctx; 2568 } 2569} 2570Mode right_child_ctx(Mode ctx, BinOpType b) { 2571 switch (b) { 2572 // Only Boolean operators change the mode. 2573 case BOT_AND: 2574 return ctx.is_neg() ? +ctx : ctx; 2575 case BOT_OR: 2576 case BOT_IMPL: 2577 return ctx.is_neg() ? ctx : +ctx; 2578 case BOT_RIMPL: 2579 return ctx.is_neg() ? -ctx : -(+ctx); 2580 case BOT_EQUIV: 2581 case BOT_XOR: 2582 return *ctx; 2583 default: 2584 return ctx; 2585 } 2586} 2587Mode child_ctx(Mode ctx, UnOpType u) { 2588 switch (u) { 2589 case UOT_NOT: 2590 return -ctx; 2591 default: 2592 return ctx; 2593 } 2594} 2595 2596void CG::run(CodeGen& cg, Model* m) { 2597 // First, collect the model parameters, and assign them 2598 // global slots. 2599 EnvInit::run(cg, m); 2600 Compile::run(cg, m); 2601} 2602 2603// For a non-Boolean value, place it in a register and collect its partiality. 2604std::pair<int, CG_Cond::T> _bind(Expression* e, CodeGen& cg, CG_Builder& frag) { 2605 // Look up the mode we need to compile e in. 2606 // FIXME: Figure out why some expressions don't have a mode attached. 2607 CG::Mode ctx(BytecodeProc::FUN); 2608 try { 2609 ctx = cg.mode_map.at(e); 2610 } catch (const std::out_of_range& exn) { 2611 } 2612 2613 switch (e->eid()) { 2614 case Expression::E_INTLIT: { 2615 // return std::make_pair(CG::locate_immi(e->template cast<IntLit>()->v().toInt(), cg, frag), 2616 // CG_Cond::ttt()); 2617 IntVal i = e->cast<IntLit>()->v(); 2618 if (!i.isFinite()) { 2619 int r(GET_REG(cg)); 2620 PUSH_INSTR(frag, BytecodeStream::BUILTIN, cg.find_builtin("infinity"), 2621 CG::r(bind_cst(i.isPlusInfinity(), cg, frag))); 2622 PUSH_INSTR(frag, BytecodeStream::POP, CG::r(r)); 2623 return {r, CG_Cond::ttt()}; 2624 } 2625 return {bind_cst(i.toInt(), cg, frag), CG_Cond::ttt()}; 2626 } 2627 case Expression::E_FLOATLIT: 2628 // return std::make_pair(CG::locate_par(e->template cast<FloatLit>(), cg, frag), nullptr); 2629 // NOT YET IMPLEMENTED 2630 return CG::Binding(bind_cst(911911, cg, frag), CG_Cond::ttt()); 2631 case Expression::E_SETLIT: 2632 return CG::bind(e->template cast<SetLit>(), ctx, cg, frag); 2633 case Expression::E_BOOLLIT: 2634 throw InternalError("bind called on Boolean expression."); 2635 case Expression::E_STRINGLIT: 2636 // NOT YET IMPLEMENTED 2637 return CG::Binding(bind_cst(911911, cg, frag), CG_Cond::ttt()); 2638 case Expression::E_ID: 2639 return CG::bind(e->template cast<Id>(), ctx, cg, frag); 2640 case Expression::E_ANON: 2641 throw InternalError("bind reached unexpected expression type: E_ANON."); 2642 case Expression::E_ARRAYLIT: 2643 return CG::bind(e->template cast<ArrayLit>(), ctx, cg, frag); 2644 case Expression::E_ARRAYACCESS: 2645 return CG::bind(e->template cast<ArrayAccess>(), ctx, cg, frag); 2646 case Expression::E_COMP: 2647 return CG::bind(e->template cast<Comprehension>(), ctx, cg, frag); 2648 case Expression::E_ITE: 2649 return CG::bind(e->template cast<ITE>(), ctx, cg, frag); 2650 case Expression::E_BINOP: 2651 return CG::bind(e->template cast<BinOp>(), ctx, cg, frag); 2652 case Expression::E_UNOP: 2653 return CG::bind(e->template cast<UnOp>(), ctx, cg, frag); 2654 case Expression::E_CALL: 2655 return CG::bind(e->template cast<Call>(), ctx, cg, frag); 2656 case Expression::E_LET: 2657 return CG::bind(e->template cast<Let>(), ctx, cg, frag); 2658 case Expression::E_VARDECL: 2659 case Expression::E_TI: 2660 case Expression::E_TIID: 2661 throw InternalError("Bytecode generator encountered unexpected expression type in bind."); 2662 } 2663} 2664 2665CG::Binding CG::bind(Expression* e, CodeGen& cg, CG_Builder& frag) { 2666 try { 2667 return cg.cache_lookup(e); 2668 } catch (const CG_Env<CG::Binding>::NotFound& exn) { 2669 CG::Binding b(_bind(e, cg, frag)); 2670 cg.cache_store(e, b); 2671 return b; 2672 } 2673 // return _bind(e, cg, frag); // FIXME 2674} 2675 2676// 2677int CG::locate_immi(int x, CodeGen& cg, CG_Builder& frag) { 2678 int r(GET_REG(cg)); 2679 PUSH_INSTR(frag, BytecodeStream::IMMI, CG::i(x), CG::r(r)); 2680 return r; 2681} 2682 2683// Modified version of execute-comprehension, executing an arbitrary callable on the innter 2684// expression. 2685template <class F> 2686void execute_comprehension_generic(Comprehension* c, Mode ctx, CodeGen& cg, CG_Builder& frag, F f) { 2687 // Build up the object to build the generator. 2688 std::vector<EmitPost*> nesting; 2689 2690 int depth = 0; 2691 2692 int g = c->n_generators(); 2693 int n_gen = c->n_generators(); 2694 for (int g = 0; g < n_gen; ++g) { 2695 // Bind the in-expression to a register. 2696 // assert(c->in(g)->type().ispar()); 2697 Expression* in(c->in(g)); 2698 // std::cout << "Binding R" << r << " to: "; debugprint(in); 2699 // Open the bindings. 2700 cg.env_push(); 2701 if (in->type().is_set()) { 2702 int r(CG::bind(in, cg, frag).first); 2703 assert(in->type().ispar()); 2704 for (int d = 0; d < c->n_decls(g); ++d) { 2705 Forset* iter(new Forset(cg, r)); 2706 depth += 2; // Forset first iterates over the range, then values in the range. 2707 nesting.push_back(iter); 2708 iter->emit_pre(frag); 2709 // Bind vd->id() to iter.val() 2710 VarDecl* vd(c->decl(g, d)); 2711 ASTString id(vd->id()->str()); 2712 // cg.env().bind(id, Loc::reg(iter.val())); 2713 cg.env().bind(id, CodeGen::Binding(iter->val(), CG_Cond::ttt())); 2714 } 2715 } else { 2716 assert(in->type().isboolarray() || in->type().isintarray() || in->type().isintsetarray()); 2717 int r_arr(CG::bind(in, cg, frag).first); 2718 for (int d = 0; d < c->n_decls(g); ++d) { 2719 Foreach* iter(new Foreach(cg, r_arr)); 2720 nesting.push_back(iter); 2721 iter->emit_pre(frag); 2722 depth += 1; 2723 2724 VarDecl* vd(c->decl(g, d)); 2725 ASTString id(vd->id()->str()); 2726 cg.env().bind(id, CodeGen::Binding(iter->val(), CG_Cond::ttt())); 2727 } 2728 } 2729 // Now emit the where-clause 2730 Expression* where(c->where(g)); 2731 if (where) { 2732 int lblCont(nesting.back()->cont()); 2733 assert(where->type().ispar()); 2734 int rC = CG::force(CG::compile(where, cg, frag), BytecodeProc::FUN, cg, frag); 2735 PUSH_INSTR(frag, BytecodeStream::JMPIFNOT, CG::r(rC), CG::l(lblCont)); 2736 } 2737 } 2738 // We're now in the deepest scope. Generate code for the body. 2739 f(c->e(), ctx, cg, frag, nesting.back()->cont(), depth); 2740 2741 // Now close the iterators _in reverse order_, and restore the environment. 2742 for (int ii = nesting.size() - 1; ii >= 0; --ii) { 2743 nesting[ii]->emit_post(frag); 2744 delete nesting[ii]; 2745 } 2746 for (int ii = 0; ii < n_gen; ++ii) cg.env_pop(); 2747} 2748 2749// Specialized version when we're binding and pushing the body. 2750void execute_comprehension_bind(Comprehension* c, Mode ctx, CodeGen& cg, CG_Builder& frag) { 2751 execute_comprehension_generic( 2752 c, ctx, cg, frag, 2753 [](Expression* e, Mode ctx, CodeGen& cg, CG_Builder& frag, int lbl_cont, int depth) { 2754 // Bind and push everything in the comprehension. 2755 CG::Binding b_e = CG::force_or_bind(e, ctx, cg, frag); 2756 PUSH_INSTR(frag, BytecodeStream::PUSH, CG::r(b_e.first)); // FIXME: Discarding partiality 2757 }); 2758} 2759 2760// Special case implementation of folds where body is a generator. 2761CG_Cond::T eval_forall(Call* call, Mode ctx, CodeGen& cg, CG_Builder& frag) { 2762 assert(call->n_args() == 1); 2763 Expression* param = call->arg(0); 2764 // Now check the expression's type. If it's an array literal or 2765 // comprehension, we generate code directly, rather than generating 2766 // a concrete vector. 2767 switch (param->eid()) { 2768 case Expression::E_ARRAYLIT: { 2769 std::vector<CG_Cond::T> conj; 2770 ArrayLit* a(param->cast<ArrayLit>()); 2771 int sz(a->size()); 2772 for (int ii = 0; ii < sz; ++ii) conj.push_back(CG::compile((*a)[ii], cg, frag)); 2773 return CG_Cond::forall(ctx, conj); 2774 } break; 2775 case Expression::E_COMP: { 2776 Comprehension* c(param->cast<Comprehension>()); 2777 // Special cases: if we're in root, just post everything. 2778 if (ctx == BytecodeProc::ROOT) { 2779 execute_comprehension_generic( 2780 param->cast<Comprehension>(), ctx, cg, frag, 2781 [](Expression* elt, Mode ctx, CodeGen& cg, CG_Builder& frag, int lbl_cont, int depth) { 2782 CG_Cond::T c = CG::compile(elt, cg, frag); 2783 post_cond(cg, frag, c); 2784 }); 2785 ; 2786 return CG_Cond::T::ttt(); 2787 } 2788 // If it's par, do shortcutting. 2789 if (c->e()->type().ispar()) { 2790 int r(GET_REG(cg)); 2791 PUSH_INSTR(frag, BytecodeStream::IMMI, CG::i(1), CG::r(r)); 2792 execute_comprehension_generic( 2793 c, ctx, cg, frag, 2794 [r](Expression* elt, Mode ctx, CodeGen& cg, CG_Builder& frag, int lbl_cont, int depth) { 2795 int r_cond = CG::force(CG::compile(elt, cg, frag), BytecodeProc::FUN, cg, frag); 2796 PUSH_INSTR(frag, BytecodeStream::JMPIF, CG::r(r_cond), CG::l(lbl_cont)); 2797 PUSH_INSTR(frag, BytecodeStream::IMMI, CG::i(0), CG::r(r)); 2798 PUSH_INSTR(frag, BytecodeStream::ITER_BREAK, CG::i(depth)); 2799 }); 2800 ; 2801 return CG_Cond::reg(r, true); 2802 } else { 2803 // General case, just aggregate the values. 2804 OPEN_AND(cg, frag); 2805 execute_comprehension_generic( 2806 c, ctx, cg, frag, 2807 [](Expression* elt, Mode ctx, CodeGen& cg, CG_Builder& frag, int lbl_cont, int depth) { 2808 int r_cond = 2809 CG::force(CG::compile(elt, cg, frag), CG::Mode(ctx.strength(), false), cg, frag); 2810 PUSH_INSTR(frag, BytecodeStream::PUSH, CG::r(r_cond)); 2811 }); 2812 CLOSE_AGG(cg, frag); 2813 int r(GET_REG(cg)); 2814 PUSH_INSTR(frag, BytecodeStream::POP, CG::r(r)); 2815 return CG_Cond::reg(r, false); 2816 } 2817 } 2818 default: { 2819 int r(GET_REG(cg)); 2820 CG::Binding b_param(CG::bind(param, cg, frag)); 2821 int r_A(b_param.first); 2822 std::vector<int> p_A; 2823 if (!b_param.second.get()) { 2824 if (b_param.second.sign()) return CG_Cond::T::fff(); 2825 } else { 2826 // TODO: It's probably not quicker to split par/var since we need the AND Aggregation anyway 2827 force_and_leaves(p_A, p_A, b_param.second, cg, frag); 2828 } 2829 OPEN_AND(cg, frag); 2830 // First, push the constraints attached to A. 2831 for (int r_c : p_A) { 2832 PUSH_INSTR(frag, BytecodeStream::PUSH, CG::r(r_c)); 2833 } 2834 ITER_ARRAY(cg, frag, r_A, [](CodeGen& cg, CG_Builder& frag, int r_elt) { 2835 PUSH_INSTR(frag, BytecodeStream::PUSH, CG::r(r_elt)); 2836 }); 2837 CLOSE_AGG(cg, frag); 2838 PUSH_INSTR(frag, BytecodeStream::POP, CG::r(r)); 2839 return CG_Cond::reg(r, call->type().ispar()); 2840 } 2841 } 2842} 2843// Same as forall, but producing an or-context. 2844CG_Cond::T eval_exists(Call* call, Mode ctx, CodeGen& cg, CG_Builder& frag) { 2845 assert(call->n_args() == 1); 2846 Expression* param = call->arg(0); 2847 2848 // Now check the expression's type. If it's an array literal or 2849 // comprehension, we generate code directly, rather than generating 2850 // a concrete vector. 2851 switch (param->eid()) { 2852 case Expression::E_ARRAYLIT: { 2853 std::vector<CG_Cond::T> disj; 2854 ArrayLit* a(param->cast<ArrayLit>()); 2855 int sz(a->size()); 2856 for (int ii = 0; ii < sz; ++ii) { 2857 CG_Cond::T elt(CG::compile((*a)[ii], cg, frag)); 2858 disj.push_back(elt); 2859 } 2860 return CG_Cond::exists(ctx, disj); 2861 } break; 2862 /* 2863 case Expression::E_COMP: { 2864 Comprehension* c(param->cast<Comprehension>()); 2865 execute_comprehension(c, c_ctx, cg, frag); 2866 } 2867 break; 2868 */ 2869#if 1 2870 case Expression::E_COMP: { 2871 Comprehension* c(param->cast<Comprehension>()); 2872 if (c->e()->type().ispar()) { 2873 int r(GET_REG(cg)); 2874 PUSH_INSTR(frag, BytecodeStream::IMMI, CG::i(0), CG::r(r)); 2875 execute_comprehension_generic( 2876 c, ctx, cg, frag, 2877 [r](Expression* elt, Mode ctx, CodeGen& cg, CG_Builder& frag, int lbl_cont, int depth) { 2878 int r_cond = CG::force(CG::compile(elt, cg, frag), BytecodeProc::FUN, cg, frag); 2879 PUSH_INSTR(frag, BytecodeStream::JMPIFNOT, CG::r(r_cond), CG::l(lbl_cont)); 2880 PUSH_INSTR(frag, BytecodeStream::IMMI, CG::i(1), CG::r(r)); 2881 PUSH_INSTR(frag, BytecodeStream::ITER_BREAK, CG::i(depth)); 2882 }); 2883 return CG_Cond::reg(r, true); 2884 } else { 2885 OPEN_OR(cg, frag); 2886 execute_comprehension_generic( 2887 c, ctx, cg, frag, 2888 [](Expression* elt, Mode ctx, CodeGen& cg, CG_Builder& frag, int lbl_cont, int depth) { 2889 int r_cond = 2890 CG::force(CG::compile(elt, cg, frag), CG::Mode(ctx.strength(), false), cg, frag); 2891 PUSH_INSTR(frag, BytecodeStream::PUSH, CG::r(r_cond)); 2892 }); 2893 CLOSE_AGG(cg, frag); 2894 int r(GET_REG(cg)); 2895 PUSH_INSTR(frag, BytecodeStream::POP, CG::r(r)); 2896 return CG_Cond::reg(r, false); 2897 } 2898 } // Fallthrough 2899#endif 2900 default: { 2901 int r(GET_REG(cg)); 2902 // Otherwise, get the result into a register... 2903 CG::Binding b_A(CG::bind(param, cg, frag)); 2904 int r_A(b_A.first); 2905 // and push every element. 2906 OPEN_OR(cg, frag); 2907 ITER_ARRAY(cg, frag, r_A, [](CodeGen& cg, CG_Builder& frag, int r_elt) { 2908 PUSH_INSTR(frag, BytecodeStream::PUSH, CG::r(r_elt)); 2909 }); 2910 CLOSE_AGG(cg, frag); 2911 PUSH_INSTR(frag, BytecodeStream::POP, CG::r(r)); 2912 return CG_Cond::forall(ctx, b_A.second, CG_Cond::reg(r, call->type().ispar())); 2913 } 2914 } 2915} 2916 2917CG_Cond::T eval_isfixed_b(Call* call, Mode ctx, CodeGen& cg, CG_Builder& frag) { 2918 assert(call->n_args() == 1); 2919 Expression* e(call->arg(0)); 2920 2921 if (e->type().ispar()) return CG_Cond::T::ttt(); 2922 2923 int r_e; 2924 if (e->type().isbool()) { 2925 CG_Cond::T b_e(CG::compile(e, cg, frag)); 2926 if (!b_e.get()) { 2927 return CG_Cond::T::ttt(); 2928 } 2929 r_e = CG::force(b_e, ctx, cg, frag); 2930 } else { 2931 CG::Binding b(CG::bind(e, cg, frag)); 2932 r_e = b.first; 2933 } 2934 int r(GET_REG(cg)); 2935 PUSH_INSTR(frag, BytecodeStream::ISPAR, CG::r(r_e), CG::r(r)); 2936 return CG_Cond::reg(r, true); 2937} 2938 2939CG::Binding bind_fix(Call* call, Mode ctx, CodeGen& cg, CG_Builder& frag) { 2940 assert(call->n_args() == 1); 2941 2942 Expression* arg(call->arg(0)); 2943 CG::Binding b_arg(CG::bind(arg, cg, frag)); 2944 if (arg->type().ispar()) { 2945 return b_arg; 2946 } else if (arg->type().isintarray() || arg->type().isboolarray()) { 2947 int r_cond(GET_REG(cg)); 2948 // Init the condition 2949 PUSH_INSTR(frag, BytecodeStream::ISPAR, CG::r(b_arg.first), CG::r(r_cond)); 2950 2951 // Gather elements 2952 OPEN_VEC(cg, frag); 2953 ITER_ARRAY(cg, frag, b_arg.first, [](CodeGen& cg, CG_Builder& frag, int r_elt) { 2954 PUSH_INSTR(frag, BytecodeStream::LB, CG::r(r_elt), CG::r(r_elt)); 2955 PUSH_INSTR(frag, BytecodeStream::PUSH, CG::r(r_elt)); 2956 }); 2957 CLOSE_AGG(cg, frag); 2958 int r_elts(GET_REG(cg)); 2959 PUSH_INSTR(frag, BytecodeStream::POP, CG::r(r_elts)); 2960 2961 // Ensure resulting array has the same index set 2962 PUSH_INSTR(frag, BytecodeStream::BUILTIN, cg.find_builtin("index_set"), CG::r(b_arg.first), 2963 CG::r(bind_cst(0, cg, frag))); 2964 int r_indxs(GET_REG(cg)); 2965 PUSH_INSTR(frag, BytecodeStream::POP, CG::r(r_indxs)); 2966 2967 PUSH_INSTR(frag, BytecodeStream::BUILTIN, cg.find_builtin("array_Xd"), CG::r(r_elts), 2968 CG::r(r_indxs)); 2969 PUSH_INSTR(frag, BytecodeStream::POP, CG::r(r_elts)); 2970 2971 return {r_elts, CG_Cond::forall(ctx, b_arg.second, CG_Cond::reg(r_cond, true))}; 2972 } else { 2973 int r(GET_REG(cg)); 2974 int r_cond(GET_REG(cg)); 2975 2976 PUSH_INSTR(frag, BytecodeStream::ISPAR, CG::r(b_arg.first), CG::r(r_cond)); 2977 PUSH_INSTR(frag, BytecodeStream::LB, CG::r(b_arg.first), CG::r(r)); 2978 2979 return {r, CG_Cond::forall(ctx, b_arg.second, CG_Cond::reg(r_cond, true))}; 2980 } 2981} 2982 2983CG_Cond::T eval_fix(Call* call, Mode ctx, CodeGen& cg, CG_Builder& frag) { 2984 assert(call->n_args() == 1); 2985 2986 // Should we check that the argument is actually fixed?? 2987 int r = CG::force(CG::compile(call->arg(0), cg, frag), ctx, cg, frag); 2988 return CG_Cond::reg(r, true); 2989} 2990 2991CG_Cond::T eval_error_b(Call* call, Mode ctx, CodeGen& cg, CG_Builder& frag) { 2992 throw InternalError("Call should only appear in general context."); 2993 return CG_Cond::ttt(); 2994} 2995CG::Binding bind_error_g(Call* call, Mode ctx, CodeGen& cg, CG_Builder& frag) { 2996 throw InternalError("Call should only appear in Boolean context."); 2997} 2998CG::Binding bind_sum(Call* call, Mode ctx, CodeGen& cg, CG_Builder& frag) { 2999 GCLock lock; 3000 assert(call->n_args() == 1); 3001 Expression* e = call->arg(0); 3002 // Components of the sum may be partial. 3003 // TODO: Specialise for literals and comprehensions. 3004 CG::Binding b_elts(CG::bind(e, cg, frag)); 3005 int r_one(bind_cst(1, cg, frag)); 3006 3007 if (e->type().ispar()) { 3008 int r_sum(GET_REG(cg)); 3009 PUSH_INSTR(frag, BytecodeStream::IMMI, CG::i(0), CG::r(r_sum)); 3010 ITER_ARRAY(cg, frag, b_elts.first, [&](CodeGen& cg, CG_Builder& frag, int r_elt) { 3011 PUSH_INSTR(frag, BytecodeStream::ADDI, CG::r(r_sum), CG::r(r_elt), CG::r(r_sum)); 3012 }); 3013 return CG::Binding(r_sum, b_elts.second); 3014 } 3015 3016 int r_sz(GET_REG(cg)); 3017 int r_ret(GET_REG(cg)); 3018 3019 int l_eq0(GET_LABEL(cg)); 3020 int l_eq1(GET_LABEL(cg)); 3021 int l_end(GET_LABEL(cg)); 3022 3023 PUSH_INSTR(frag, BytecodeStream::LENGTH, CG::r(b_elts.first), CG::r(r_sz)); 3024 PUSH_INSTR(frag, BytecodeStream::JMPIFNOT, CG::r(r_sz), CG::l(l_eq0)); 3025 PUSH_INSTR(frag, BytecodeStream::EQI, CG::r(r_one), CG::r(r_sz), CG::r(r_ret)); 3026 PUSH_INSTR(frag, BytecodeStream::JMPIF, CG::r(r_ret), CG::l(l_eq1)); 3027 3028 // Sum n arguments 3029 auto fun = find_call_fun(cg, {"sum_cc"}, Type::varint(), {Type::varint(1)}, BytecodeProc::FUN); 3030 assert(std::get<1>(fun) == BytecodeProc::FUN); 3031 PUSH_INSTR(frag, BytecodeStream::CALL, BytecodeProc::FUN, std::get<0>(fun), 3032 CG::i(!std::get<2>(fun)), CG::r(b_elts.first)); 3033 PUSH_INSTR(frag, BytecodeStream::POP, CG::r(r_ret)); 3034 PUSH_INSTR(frag, BytecodeStream::JMP, CG::l(l_end)); 3035 3036 // Sum zero arguments (result must be 0) 3037 PUSH_LABEL(frag, l_eq0); 3038 PUSH_INSTR(frag, BytecodeStream::IMMI, CG::i(0), CG::r(r_ret)); 3039 PUSH_INSTR(frag, BytecodeStream::JMP, CG::l(l_end)); 3040 3041 // Sum 1 arguments (result == a[1]) 3042 PUSH_LABEL(frag, l_eq1); 3043 PUSH_INSTR(frag, BytecodeStream::GET_VEC, CG::r(b_elts.first), CG::r(r_one), CG::r(r_ret)); 3044 3045 // End of sum 3046 PUSH_LABEL(frag, l_end); 3047 return CG::Binding(r_ret, b_elts.second); 3048} 3049 3050CG::Binding bind_set2array(Call* call, Mode ctx, CodeGen& cg, CG_Builder& frag) { 3051 assert(call->n_args() == 1); 3052 // Bind set 3053 Expression* e = call->arg(0); 3054 CG::Binding b_elts(CG::bind(e, cg, frag)); 3055 3056 // Create array 3057 3058 OPEN_VEC(cg, frag); 3059 ITER_SET(cg, frag, b_elts.first, [](CodeGen& cg, CG_Builder& frag, int r_elt) { 3060 PUSH_INSTR(frag, BytecodeStream::PUSH, CG::r(r_elt)); 3061 }); 3062 CLOSE_AGG(cg, frag); 3063 int r_ret(GET_REG(cg)); 3064 PUSH_INSTR(frag, BytecodeStream::POP, CG::r(r_ret)); 3065 3066 return {r_ret, b_elts.second}; 3067} 3068 3069CG::Binding bind_dom_bounds_array(Call* call, Mode ctx, CodeGen& cg, CG_Builder& frag) { 3070 assert(call->n_args() == 1); 3071 CG::Binding b_A(CG::bind(call->arg(0), cg, frag)); 3072 3073 // Open the context here, so we can recover all the registers after. 3074 OPEN_VEC(cg, frag); 3075 3076 int r_lb(GET_REG(cg)); 3077 PUSH_INSTR(frag, BytecodeStream::BUILTIN, cg.find_builtin("infinity"), 3078 CG::r(bind_cst(1, cg, frag))); 3079 PUSH_INSTR(frag, BytecodeStream::POP, CG::r(r_lb)); 3080 3081 int r_ub(GET_REG(cg)); 3082 PUSH_INSTR(frag, BytecodeStream::BUILTIN, cg.find_builtin("infinity"), 3083 CG::r(bind_cst(0, cg, frag))); 3084 PUSH_INSTR(frag, BytecodeStream::POP, CG::r(r_ub)); 3085 3086 // Do the iteration 3087 int r_test(GET_REG(cg)); 3088 int r_bound(GET_REG(cg)); 3089 3090 int l_lb(GET_LABEL(cg)); 3091 int l_ub(GET_LABEL(cg)); 3092 // Body 3093 ITER_ARRAY(cg, frag, b_A.first, [&](CodeGen& cg, CG_Builder& frag, int r_elt) { 3094 PUSH_INSTR(frag, BytecodeStream::LB, CG::r(r_elt), CG::r(r_bound)); 3095 PUSH_INSTR(frag, BytecodeStream::LEI, CG::r(r_lb), CG::r(r_bound), CG::r(r_test)); 3096 PUSH_INSTR(frag, BytecodeStream::JMPIF, CG::r(r_test), CG::l(l_lb)); 3097 PUSH_INSTR(frag, BytecodeStream::MOV, CG::r(r_bound), CG::r(r_lb)); 3098 PUSH_LABEL(frag, l_lb); 3099 PUSH_INSTR(frag, BytecodeStream::UB, CG::r(r_elt), CG::r(r_bound)); 3100 PUSH_INSTR(frag, BytecodeStream::LEI, CG::r(r_bound), CG::r(r_ub), CG::r(r_test)); 3101 PUSH_INSTR(frag, BytecodeStream::JMPIF, CG::r(r_test), CG::l(l_ub)); 3102 PUSH_INSTR(frag, BytecodeStream::MOV, CG::r(r_bound), CG::r(r_ub)); 3103 PUSH_LABEL(frag, l_ub); 3104 }); 3105 3106 // Now collect the elements 3107 PUSH_INSTR(frag, BytecodeStream::PUSH, CG::r(r_lb)); 3108 PUSH_INSTR(frag, BytecodeStream::PUSH, CG::r(r_ub)); 3109 CLOSE_AGG(cg, frag); 3110 int r(GET_REG(cg)); 3111 PUSH_INSTR(frag, BytecodeStream::POP, CG::r(r)); 3112 return CG::Binding(r, b_A.second); 3113} 3114 3115CG_Cond::T eval_assert_b(Call* call, Mode ctx, CodeGen& cg, CG_Builder& frag) { 3116 assert(call->arg(0)->type().ispar()); 3117 // Evaluate the assertion, abort if it fails. 3118 /* 3119 int r_cond = CG::force(CG::compile(call->arg(0), cg, frag), cg, frag); 3120 int l_okay(GET_LABEL(cg)); 3121 PUSH_INSTR(frag, BytecodeStream::JMPIF, CG::r(r_cond), CG::l(l_okay)); 3122 PUSH_INSTR(frag, BytecodeStream::ABORT); 3123 PUSH_LABEL(frag, l_okay); 3124 */ 3125 // Otherwise, evaluate as usual. 3126 if (call->n_args() == 2) { 3127 return CG_Cond::ttt(); 3128 } else { 3129 assert(call->n_args() == 3); 3130 return CG::compile(call->arg(2), cg, frag); 3131 } 3132} 3133 3134CG::Binding bind_assert_g(Call* call, Mode ctx, CodeGen& cg, CG_Builder& frag) { 3135 assert(call->n_args() == 3); 3136 assert(call->arg(0)->type().ispar()); 3137 /* 3138 int r_cond = CG::force(CG::compile(call->arg(0), cg, frag), cg, frag); 3139 int l_okay(GET_LABEL(cg)); 3140 PUSH_INSTR(frag, BytecodeStream::JMPIF, CG::r(r_cond), CG::l(l_okay)); 3141 PUSH_INSTR(frag, BytecodeStream::ABORT); 3142 PUSH_LABEL(frag, l_okay); 3143 */ 3144 return CG::bind(call->arg(2), cg, frag); 3145} 3146 3147CG::Binding bind_arrayXd(Call* call, Mode ctx, CodeGen& cg, CG_Builder& frag) { 3148 assert(call->n_args() == 2); 3149 CG::Binding orig(CG::bind(call->arg(0), cg, frag)); 3150 CG::Binding target(CG::bind(call->arg(1), cg, frag)); 3151 3152 // Get index sets from orig 3153 int r(GET_REG(cg)); 3154 PUSH_INSTR(frag, BytecodeStream::BUILTIN, cg.find_builtin("index_set"), CG::r(orig.first), 3155 CG::r(bind_cst(0, cg, frag))); 3156 PUSH_INSTR(frag, BytecodeStream::POP, CG::r(r)); 3157 3158 // Create new array 3159 PUSH_INSTR(frag, BytecodeStream::BUILTIN, cg.find_builtin("array_Xd"), CG::r(target.first), 3160 CG::r(r)); 3161 PUSH_INSTR(frag, BytecodeStream::POP, CG::r(r)); 3162 3163 return {r, CG_Cond::forall(ctx, orig.second, target.second)}; 3164} 3165 3166template <int N> 3167CG::Binding bind_arrayNd(Call* call, Mode ctx, CodeGen& cg, CG_Builder& frag) { 3168 assert(call->n_args() == N + 1); 3169 std::vector<CG_Cond::T> cond; 3170 3171 // Bind all index sets 3172 std::vector<int> r_index(N); 3173 for (int ii = 0; ii < N; ++ii) { 3174 CG::Binding b(CG::bind(call->arg(ii), cg, frag)); 3175 r_index[ii] = b.first; 3176 cond.push_back(b.second); 3177 } 3178 3179 // Bind array expression 3180 CG::Binding arr = CG::bind(call->arg(N), cg, frag); 3181 3182 int rI(GET_REG(cg)); 3183 OPEN_VEC(cg, frag); 3184 for (int ii = 0; ii < N; ++ii) { 3185 // TODO: Ensure the index set is a range (2-elements) 3186 PUSH_INSTR(frag, BytecodeStream::GET_VEC, CG::r(r_index[ii]), CG::r(bind_cst(1, cg, frag)), 3187 CG::r(rI)); 3188 PUSH_INSTR(frag, BytecodeStream::PUSH, CG::r(rI)); 3189 PUSH_INSTR(frag, BytecodeStream::GET_VEC, CG::r(r_index[ii]), CG::r(bind_cst(2, cg, frag)), 3190 CG::r(rI)); 3191 PUSH_INSTR(frag, BytecodeStream::PUSH, CG::r(rI)); 3192 } 3193 CLOSE_AGG(cg, frag); 3194 PUSH_INSTR(frag, BytecodeStream::POP, CG::r(rI)); 3195 3196 PUSH_INSTR(frag, BytecodeStream::BUILTIN, cg.find_builtin("array_Xd"), CG::r(arr.first), 3197 CG::r(rI)); 3198 PUSH_INSTR(frag, BytecodeStream::POP, CG::r(rI)); 3199 3200 return {rI, CG_Cond::forall(ctx, cond)}; 3201} 3202 3203CG::Binding bind_array1d(Call* call, Mode ctx, CodeGen& cg, CG_Builder& frag) { 3204 if (call->n_args() == 1) { 3205 int rA(GET_REG(cg)); 3206 CG::Binding arr = CG::bind(call->arg(0), cg, frag); 3207 3208 PUSH_INSTR(frag, BytecodeStream::BUILTIN, cg.find_builtin("array_Xd"), CG::r(arr.first), 3209 CG::r(bind_cst(0, cg, frag))); 3210 PUSH_INSTR(frag, BytecodeStream::POP, CG::r(rA)); 3211 3212 return {rA, arr.second}; 3213 } else { 3214 // Index set given by the user 3215 assert(call->n_args() == 2); 3216 return bind_arrayNd<1>(call, ctx, cg, frag); 3217 } 3218} 3219 3220CG::Binding bind_array_union(Call* call, Mode ctx, CodeGen& cg, CG_Builder& frag) { 3221 assert(call->n_args() == 1); 3222 Expression* e = call->arg(0); 3223 // Components of the sum may be partial. 3224 // TODO: Specialise for literals and comprehensions. 3225 CG::Binding b_elts(CG::bind(e, cg, frag)); 3226 3227 assert(e->type().ispar()); // TODO: var case 3228 int r_union(GET_REG(cg)); 3229 OPEN_VEC(cg, frag); 3230 CLOSE_AGG(cg, frag); 3231 PUSH_INSTR(frag, BytecodeStream::POP, CG::r(r_union)); 3232 3233 ITER_ARRAY(cg, frag, b_elts.first, [&](CodeGen& cg, CG_Builder& frag, int r_elt) { 3234 PUSH_INSTR(frag, BytecodeStream::UNION, CG::r(r_union), CG::r(r_elt), CG::r(r_union)); 3235 }); 3236 3237 return {r_union, b_elts.second}; 3238} 3239 3240CG::Binding bind_length(Call* call, Mode ctx, CodeGen& cg, CG_Builder& frag) { 3241 assert(call->n_args() == 1); 3242 CG::Binding b(CG::bind(call->arg(0), cg, frag)); 3243 int r(GET_REG(cg)); 3244 PUSH_INSTR(frag, BytecodeStream::LENGTH, CG::r(b.first), CG::r(r)); 3245 return {r, b.second}; 3246} 3247 3248CG::Binding bind_lb(Call* call, Mode ctx, CodeGen& cg, CG_Builder& frag) { 3249 assert(call->n_args() == 1); 3250 CG::Binding b(CG::bind(call->arg(0), cg, frag)); 3251 int r(GET_REG(cg)); 3252 PUSH_INSTR(frag, BytecodeStream::LB, CG::r(b.first), CG::r(r)); 3253 return CG::Binding(r, b.second); 3254} 3255CG::Binding bind_ub(Call* call, Mode ctx, CodeGen& cg, CG_Builder& frag) { 3256 assert(call->n_args() == 1); 3257 CG::Binding b(CG::bind(call->arg(0), cg, frag)); 3258 int r(GET_REG(cg)); 3259 PUSH_INSTR(frag, BytecodeStream::UB, CG::r(b.first), CG::r(r)); 3260 return CG::Binding(r, b.second); 3261} 3262CG::Binding bind_dom(Call* call, Mode ctx, CodeGen& cg, CG_Builder& frag) { 3263 assert(call->n_args() == 1); 3264 CG::Binding b(CG::bind(call->arg(0), cg, frag)); 3265 int r(GET_REG(cg)); 3266 PUSH_INSTR(frag, BytecodeStream::DOM, CG::r(b.first), CG::r(r)); 3267 return CG::Binding(r, b.second); 3268} 3269CG::Binding bind_lb_array(Call* call, Mode ctx, CodeGen& cg, CG_Builder& frag) { 3270 assert(call->n_args() == 1); 3271 CG::Binding b_A(CG::bind(call->arg(0), cg, frag)); 3272 3273 int r_agg(GET_REG(cg)); 3274 PUSH_INSTR(frag, BytecodeStream::BUILTIN, cg.find_builtin("infinity"), 3275 CG::r(bind_cst(1, cg, frag))); 3276 PUSH_INSTR(frag, BytecodeStream::POP, CG::r(r_agg)); 3277 3278 int r_tmp(GET_REG(cg)); 3279 int r_test(GET_REG(cg)); 3280 int l_end(GET_LABEL(cg)); 3281 ITER_ARRAY(cg, frag, b_A.first, [&](CodeGen& cg, CG_Builder& frag, int r_elt) { 3282 PUSH_INSTR(frag, BytecodeStream::LB, CG::r(r_elt), CG::r(r_tmp)); 3283 PUSH_INSTR(frag, BytecodeStream::LTI, CG::r(r_tmp), CG::r(r_agg), CG::r(r_test)); 3284 PUSH_INSTR(frag, BytecodeStream::JMPIFNOT, CG::r(r_test), CG::l(l_end)); 3285 PUSH_INSTR(frag, BytecodeStream::MOV, CG::r(r_tmp), CG::r(r_agg)); 3286 PUSH_LABEL(frag, l_end); 3287 }); 3288 3289 return CG::Binding(r_agg, b_A.second); 3290} 3291 3292CG::Binding bind_ub_array(Call* call, Mode ctx, CodeGen& cg, CG_Builder& frag) { 3293 assert(call->n_args() == 1); 3294 CG::Binding b_A(CG::bind(call->arg(0), cg, frag)); 3295 3296 int r_agg(GET_REG(cg)); 3297 PUSH_INSTR(frag, BytecodeStream::BUILTIN, cg.find_builtin("infinity"), 3298 CG::r(bind_cst(0, cg, frag))); 3299 PUSH_INSTR(frag, BytecodeStream::POP, CG::r(r_agg)); 3300 3301 int r_tmp(GET_REG(cg)); 3302 int r_test(GET_REG(cg)); 3303 int l_end(GET_LABEL(cg)); 3304 ITER_ARRAY(cg, frag, b_A.first, [&](CodeGen& cg, CG_Builder& frag, int r_elt) { 3305 PUSH_INSTR(frag, BytecodeStream::UB, CG::r(r_elt), CG::r(r_tmp)); 3306 PUSH_INSTR(frag, BytecodeStream::LTI, CG::r(r_agg), CG::r(r_tmp), CG::r(r_test)); 3307 PUSH_INSTR(frag, BytecodeStream::JMPIFNOT, CG::r(r_test), CG::l(l_end)); 3308 PUSH_INSTR(frag, BytecodeStream::MOV, CG::r(r_tmp), CG::r(r_agg)); 3309 PUSH_LABEL(frag, l_end); 3310 }); 3311 3312 return CG::Binding(r_agg, b_A.second); 3313} 3314 3315CG::Binding bind_dom_array(Call* call, Mode ctx, CodeGen& cg, CG_Builder& frag) { 3316 assert(call->n_args() == 1); 3317 CG::Binding b_A(CG::bind(call->arg(0), cg, frag)); 3318 3319 int r_agg(GET_REG(cg)); 3320 OPEN_VEC(cg, frag); 3321 CLOSE_AGG(cg, frag); 3322 PUSH_INSTR(frag, BytecodeStream::POP, CG::r(r_agg)); 3323 3324 int r_tmp(GET_REG(cg)); 3325 ITER_ARRAY(cg, frag, b_A.first, [&](CodeGen& cg, CG_Builder& frag, int r_elt) { 3326 PUSH_INSTR(frag, BytecodeStream::DOM, CG::r(r_elt), CG::r(r_tmp)); 3327 PUSH_INSTR(frag, BytecodeStream::UNION, CG::r(r_agg), CG::r(r_tmp), CG::r(r_agg)); 3328 }); 3329 3330 return {r_agg, b_A.second}; 3331} 3332 3333CG::Binding bind_index_set(Call* call, Mode ctx, CodeGen& cg, CG_Builder& frag) { 3334 assert(call->n_args() == 1); 3335 assert(call->arg(0)->type().dim() == 1); 3336 CG::Binding b_arg(CG::bind(call->arg(0), cg, frag)); 3337 3338 PUSH_INSTR(frag, BytecodeStream::BUILTIN, cg.find_builtin("index_set"), CG::r(b_arg.first), 3339 CG::r(bind_cst(1, cg, frag))); 3340 int r(GET_REG(cg)); 3341 PUSH_INSTR(frag, BytecodeStream::POP, CG::r(r)); 3342 3343 return {r, b_arg.second}; 3344} 3345 3346template <int X, int Y> 3347CG::Binding bind_index_set_XofY(Call* call, Mode ctx, CodeGen& cg, CG_Builder& frag) { 3348 assert(call->n_args() == 1); 3349 CG::Binding b_arg(CG::bind(call->arg(0), cg, frag)); 3350 3351 PUSH_INSTR(frag, BytecodeStream::BUILTIN, cg.find_builtin("index_set"), CG::r(b_arg.first), 3352 CG::r(bind_cst(X, cg, frag))); 3353 int r(GET_REG(cg)); 3354 PUSH_INSTR(frag, BytecodeStream::POP, CG::r(r)); 3355 3356 return {r, b_arg.second}; 3357} 3358 3359CG::Binding bind_bool2int(Call* call, Mode ctx, CodeGen& cg, CG_Builder& frag) { 3360 assert(call->n_args() == 1); 3361 return CG::force_or_bind(call->arg(0), BytecodeProc::FUN, cg, frag); 3362} 3363CG::Binding bind_int2float(Call* call, Mode ctx, CodeGen& cg, CG_Builder& frag) { 3364 assert(call->n_args() == 1); 3365 return CG::bind(call->arg(0), cg, frag); 3366} 3367 3368CG::Binding bind_call(Call* call, Mode ctx, CodeGen& cg, CG_Builder& frag); 3369CG_Cond::T compile_call(Call* call, Mode ctx, CodeGen& cg, CG_Builder& frag); 3370 3371CG::Binding bind_set_max(Call* call, Mode ctx, CodeGen& cg, CG_Builder& frag) { 3372 assert(call->type().ispar()); 3373 assert(call->n_args() == 1); 3374 CG::Binding b_A(CG::bind(call->arg(0), cg, frag)); 3375 int r(GET_REG(cg)); 3376 int r_sz(GET_REG(cg)); 3377 PUSH_INSTR(frag, BytecodeStream::LENGTH, CG::r(b_A.first), CG::r(r_sz)); 3378 int l_fst(GET_LABEL(cg)); 3379 PUSH_INSTR(frag, BytecodeStream::LEI, CG::r(bind_cst(0, cg, frag)), CG::r(r_sz), CG::r(r)); 3380 PUSH_INSTR(frag, BytecodeStream::JMPIF, CG::r(r), CG::l(l_fst)); 3381 PUSH_INSTR(frag, BytecodeStream::ABORT); 3382 PUSH_LABEL(frag, l_fst); 3383 3384 PUSH_INSTR(frag, BytecodeStream::GET_VEC, CG::r(b_A.first), CG::r(r_sz), CG::r(r)); 3385 3386 // TODO: Also should not be allowed on empty sets 3387 return CG::Binding(r, b_A.second); 3388}; 3389 3390CG::Binding bind_max(Call* call, Mode ctx, CodeGen& cg, CG_Builder& frag) { 3391 assert(call->type().ispar()); 3392 assert(call->n_args() == 1); 3393 CG::Binding b_A(CG::bind(call->arg(0), cg, frag)); 3394 3395 int r(GET_REG(cg)); 3396 PUSH_INSTR(frag, BytecodeStream::BUILTIN, cg.find_builtin("infinity"), 3397 CG::r(bind_cst(0, cg, frag))); 3398 PUSH_INSTR(frag, BytecodeStream::POP, CG::r(r)); 3399 3400 int r_test(GET_REG(cg)); 3401 int l_end(GET_LABEL(cg)); 3402 ITER_ARRAY(cg, frag, b_A.first, [&](CodeGen& cg, CG_Builder& frag, int r_elt) { 3403 PUSH_INSTR(frag, BytecodeStream::LTI, CG::r(r), CG::r(r_elt), CG::r(r_test)); 3404 PUSH_INSTR(frag, BytecodeStream::JMPIFNOT, CG::r(r_test), CG::l(l_end)); 3405 PUSH_INSTR(frag, BytecodeStream::MOV, CG::r(r_elt), CG::r(r)); 3406 PUSH_LABEL(frag, l_end); 3407 }); 3408 3409 // TODO: Fail if array size < 1 3410 return CG::Binding(r, CG_Cond::ttt()); 3411} 3412 3413CG::Binding bind_arg_max(Call* call, Mode ctx, CodeGen& cg, CG_Builder& frag) { 3414 assert(call->n_args() == 1); 3415 CG::Binding b_A(CG::bind(call->arg(0), cg, frag)); 3416 int r(GET_REG(cg)); 3417 int r_elt(GET_REG(cg)); 3418 int r_v(GET_REG(cg)); 3419 int r_test(GET_REG(cg)); 3420 int r_index(GET_REG(cg)); 3421 3422 PUSH_INSTR(frag, BytecodeStream::BUILTIN, cg.find_builtin("index_set"), CG::r(b_A.first), 3423 CG::r(bind_cst(1, cg, frag))); 3424 PUSH_INSTR(frag, BytecodeStream::POP, CG::r(r_index)); 3425 PUSH_INSTR(frag, BytecodeStream::BUILTIN, cg.find_builtin("infinity"), 3426 CG::r(bind_cst(0, cg, frag))); 3427 PUSH_INSTR(frag, BytecodeStream::POP, CG::r(r_v)); 3428 3429 int l_end(GET_LABEL(cg)); 3430 ITER_SET(cg, frag, r_index, [&](CodeGen& cg, CG_Builder& frag, int r_i) { 3431 // Array access should be valid since we are using the actual index set. 3432 PUSH_INSTR(frag, BytecodeStream::GET_ARRAY, CG::i(1), CG::r(b_A.first), CG::r(r_i), 3433 CG::r(r_elt), CG::r(r_test)); 3434 PUSH_INSTR(frag, BytecodeStream::LTI, CG::r(r_v), CG::r(r_elt), CG::r(r_test)); 3435 PUSH_INSTR(frag, BytecodeStream::JMPIFNOT, CG::r(r_test), CG::l(l_end)); 3436 PUSH_INSTR(frag, BytecodeStream::MOV, CG::r(r_elt), CG::r(r_v)); 3437 PUSH_INSTR(frag, BytecodeStream::MOV, CG::r(r_i), CG::r(r)); 3438 PUSH_LABEL(frag, l_end); 3439 }); 3440 3441 // TODO: Fail if array size < 1 3442 return CG::Binding(r, CG_Cond::ttt()); 3443} 3444 3445CG::Binding bind_arg_min(Call* call, Mode ctx, CodeGen& cg, CG_Builder& frag) { 3446 assert(call->n_args() == 1); 3447 CG::Binding b_A(CG::bind(call->arg(0), cg, frag)); 3448 int r(GET_REG(cg)); 3449 int r_elt(GET_REG(cg)); 3450 int r_v(GET_REG(cg)); 3451 int r_test(GET_REG(cg)); 3452 int r_index(GET_REG(cg)); 3453 3454 PUSH_INSTR(frag, BytecodeStream::BUILTIN, cg.find_builtin("index_set"), CG::r(b_A.first), 3455 CG::r(bind_cst(1, cg, frag))); 3456 PUSH_INSTR(frag, BytecodeStream::POP, CG::r(r_index)); 3457 PUSH_INSTR(frag, BytecodeStream::BUILTIN, cg.find_builtin("infinity"), 3458 CG::r(bind_cst(1, cg, frag))); 3459 PUSH_INSTR(frag, BytecodeStream::POP, CG::r(r_v)); 3460 3461 int l_end(GET_LABEL(cg)); 3462 ITER_SET(cg, frag, r_index, [&](CodeGen& cg, CG_Builder& frag, int r_i) { 3463 // Array access should be valid since we are using the actual index set. 3464 PUSH_INSTR(frag, BytecodeStream::GET_ARRAY, CG::i(1), CG::r(b_A.first), CG::r(r_i), 3465 CG::r(r_elt), CG::r(r_test)); 3466 PUSH_INSTR(frag, BytecodeStream::LTI, CG::r(r_elt), CG::r(r_v), CG::r(r_test)); 3467 PUSH_INSTR(frag, BytecodeStream::JMPIFNOT, CG::r(r_test), CG::l(l_end)); 3468 PUSH_INSTR(frag, BytecodeStream::MOV, CG::r(r_elt), CG::r(r_v)); 3469 PUSH_INSTR(frag, BytecodeStream::MOV, CG::r(r_i), CG::r(r)); 3470 PUSH_LABEL(frag, l_end); 3471 }); 3472 3473 // TODO: Fail if array size < 1 3474 return CG::Binding(r, CG_Cond::ttt()); 3475} 3476 3477CG::Binding bind_set_min(Call* call, Mode ctx, CodeGen& cg, CG_Builder& frag) { 3478 assert(call->type().ispar()); 3479 assert(call->n_args() == 1); 3480 CG::Binding b_A(CG::bind(call->arg(0), cg, frag)); 3481 int r(GET_REG(cg)); 3482 int r_sz(GET_REG(cg)); 3483 PUSH_INSTR(frag, BytecodeStream::LENGTH, CG::r(b_A.first), CG::r(r_sz)); 3484 int l_fst(GET_LABEL(cg)); 3485 PUSH_INSTR(frag, BytecodeStream::LEI, CG::r(bind_cst(1, cg, frag)), CG::r(r_sz), CG::r(r)); 3486 PUSH_INSTR(frag, BytecodeStream::JMPIF, CG::r(r), CG::l(l_fst)); 3487 PUSH_INSTR(frag, BytecodeStream::ABORT); 3488 3489 PUSH_LABEL(frag, l_fst); 3490 PUSH_INSTR(frag, BytecodeStream::GET_VEC, CG::r(b_A.first), CG::r(bind_cst(1, cg, frag)), 3491 CG::r(r)); 3492 3493 return CG::Binding(r, CG_Cond::ttt()); 3494} 3495 3496CG::Binding bind_min(Call* call, Mode ctx, CodeGen& cg, CG_Builder& frag) { 3497 assert(call->type().ispar()); 3498 assert(call->n_args() == 1); 3499 CG::Binding b_A(CG::bind(call->arg(0), cg, frag)); 3500 3501 int r(GET_REG(cg)); 3502 PUSH_INSTR(frag, BytecodeStream::BUILTIN, cg.find_builtin("infinity"), 3503 CG::r(bind_cst(1, cg, frag))); 3504 PUSH_INSTR(frag, BytecodeStream::POP, CG::r(r)); 3505 3506 int r_test(GET_REG(cg)); 3507 int l_end(GET_LABEL(cg)); 3508 ITER_ARRAY(cg, frag, b_A.first, [&](CodeGen& cg, CG_Builder& frag, int r_elt) { 3509 PUSH_INSTR(frag, BytecodeStream::LTI, CG::r(r_elt), CG::r(r), CG::r(r_test)); 3510 PUSH_INSTR(frag, BytecodeStream::JMPIFNOT, CG::r(r_test), CG::l(l_end)); 3511 PUSH_INSTR(frag, BytecodeStream::MOV, CG::r(r_elt), CG::r(r)); 3512 PUSH_LABEL(frag, l_end); 3513 }); 3514 3515 return CG::Binding(r, CG_Cond::ttt()); 3516} 3517 3518CG::Binding bind_card(Call* call, Mode ctx, CodeGen& cg, CG_Builder& frag) { 3519 assert(call->type().ispar()); 3520 assert(call->n_args() == 1); 3521 3522 CG::Binding b_A(CG::bind(call->arg(0), cg, frag)); 3523 int r(GET_REG(cg)); 3524 int r_i(GET_REG(cg)); 3525 int r_sz(GET_REG(cg)); 3526 int r_e(GET_REG(cg)); 3527 PUSH_INSTR(frag, BytecodeStream::IMMI, CG::i(0), CG::r(r)); 3528 3529 PUSH_INSTR(frag, BytecodeStream::IMMI, CG::i(1), CG::r(r_i)); 3530 PUSH_INSTR(frag, BytecodeStream::LENGTH, CG::r(b_A.first), CG::r(r_sz)); 3531 int l_hd(GET_LABEL(cg)); 3532 int l_tl(GET_LABEL(cg)); 3533 PUSH_INSTR(frag, BytecodeStream::LTI, CG::r(r_i), CG::r(r_sz), CG::r(r_e)); 3534 PUSH_INSTR(frag, BytecodeStream::JMPIFNOT, CG::r(r_e), CG::l(l_tl)); 3535 3536 PUSH_LABEL(frag, l_hd); 3537 PUSH_INSTR(frag, BytecodeStream::INCI, CG::r(r)); 3538 PUSH_INSTR(frag, BytecodeStream::GET_VEC, CG::r(b_A.first), CG::r(r_i), CG::r(r_e)); 3539 PUSH_INSTR(frag, BytecodeStream::SUBI, CG::r(r), CG::r(r_e), CG::r(r)); 3540 PUSH_INSTR(frag, BytecodeStream::INCI, CG::r(r_i)); 3541 PUSH_INSTR(frag, BytecodeStream::GET_VEC, CG::r(b_A.first), CG::r(r_i), CG::r(r_e)); 3542 PUSH_INSTR(frag, BytecodeStream::INCI, CG::r(r_i)); 3543 PUSH_INSTR(frag, BytecodeStream::ADDI, CG::r(r), CG::r(r_e), CG::r(r)); 3544 3545 PUSH_INSTR(frag, BytecodeStream::LTI, CG::r(r_i), CG::r(r_sz), CG::r(r_e)); 3546 PUSH_INSTR(frag, BytecodeStream::JMPIF, CG::r(r_e), CG::l(l_hd)); 3547 PUSH_LABEL(frag, l_tl); 3548 return CG::Binding(r, b_A.second); 3549} 3550 3551CG::Binding bind_internal(Call* call, Mode ctx, CodeGen& cg, CG_Builder& frag) { 3552 std::string name = call->decl()->id().str(); 3553 CG_ProcID proc = cg.find_builtin(name); 3554 int r_res(GET_REG(cg)); 3555 3556 std::vector<CG_Value> r_args(call->n_args()); 3557 std::vector<CG_Cond::T> p_args(call->n_args()); 3558 for (int i = 0; i < call->n_args(); ++i) { 3559 CG::Binding b_arg(CG::bind(call->arg(i), cg, frag)); 3560 r_args[i] = CG::r(b_arg.first); 3561 p_args[i] = b_arg.second; 3562 } 3563 3564 // Push BUILTIN instruction with the correct id 3565 PUSH_INSTR(frag, BytecodeStream::BUILTIN, proc); 3566 // Append instruction with register arguments 3567 CG_Instr& i = frag.instrs.back(); 3568 PUSH_INSTR_OPERAND(i, r_args); 3569 3570 PUSH_INSTR(frag, BytecodeStream::POP, CG::r(r_res)); 3571 3572 return {r_res, CG_Cond::forall(ctx, p_args)}; 3573} 3574 3575CG_Cond::T eval_context_is_root(Call* call, Mode ctx, CodeGen& cg, CG_Builder& frag) { 3576 // TODO: I'm not sure this is actually correct.... 3577 assert(call->n_args() == 1); 3578 CG::Mode arg_ctx(BytecodeProc::FUN); 3579 try { 3580 arg_ctx = cg.mode_map.at(call->arg(0)); 3581 } catch (const std::out_of_range& exn) { 3582 } 3583 3584 if (arg_ctx == BytecodeProc::ROOT) { 3585 return CG_Cond::ttt(); 3586 } else { 3587 return CG_Cond::fff(); 3588 } 3589} 3590 3591CG_Cond::T eval_has_bounds(Call* call, Mode ctx, CodeGen& cg, CG_Builder& frag) { 3592 // TODO: Actual implementation! 3593 3594 return CG_Cond::ttt(); 3595} 3596 3597CG::Binding bind_TODO(Call* call, Mode ctx, CodeGen& cg, CG_Builder& frag) { 3598 PUSH_INSTR(frag, BytecodeStream::ABORT); 3599 return {0, CG_Cond::fff()}; 3600} 3601CG_Cond::T eval_TODO(Call* call, Mode ctx, CodeGen& cg, CG_Builder& frag) { 3602 PUSH_INSTR(frag, BytecodeStream::ABORT); 3603 return CG_Cond::fff(); 3604} 3605 3606template <int X> 3607CG_Cond::T eval_argX_only(Call* call, Mode ctx, CodeGen& cg, CG_Builder& frag) { 3608 return CG::compile(call->arg(X - 1), cg, frag); 3609} 3610 3611builtin_table init_builtins(void) { 3612 builtin_table tbl; 3613 Constants& c(constants()); 3614 tbl.insert(std::make_pair(c.ids.sum.str(), builtin_t{eval_error_b, bind_sum})); 3615 tbl.insert(std::make_pair(c.ids.exists.str(), builtin_t{eval_exists, bind_error_g})); 3616 tbl.insert(std::make_pair(c.ids.forall.str(), builtin_t{eval_forall, bind_error_g})); 3617 tbl.insert(std::make_pair(c.ids.assert.str(), builtin_t{eval_assert_b, bind_assert_g})); 3618 tbl.insert(std::make_pair("arrayXd", builtin_t{eval_error_b, bind_arrayXd})); 3619 tbl.insert(std::make_pair("array1d", builtin_t{eval_error_b, bind_array1d})); 3620 tbl.insert(std::make_pair("array2d", builtin_t{eval_error_b, bind_arrayNd<2>})); 3621 tbl.insert(std::make_pair("array3d", builtin_t{eval_error_b, bind_arrayNd<3>})); 3622 tbl.insert(std::make_pair("array4d", builtin_t{eval_error_b, bind_arrayNd<4>})); 3623 tbl.insert(std::make_pair("array5d", builtin_t{eval_error_b, bind_arrayNd<5>})); 3624 tbl.insert(std::make_pair("array6d", builtin_t{eval_error_b, bind_arrayNd<6>})); 3625 tbl.insert(std::make_pair("array7d", builtin_t{eval_error_b, bind_arrayNd<7>})); 3626 tbl.insert(std::make_pair("array8d", builtin_t{eval_error_b, bind_arrayNd<8>})); 3627 tbl.insert(std::make_pair("array9d", builtin_t{eval_error_b, bind_arrayNd<9>})); 3628 tbl.insert(std::make_pair("array_union", builtin_t{eval_error_b, bind_array_union})); 3629 tbl.insert(std::make_pair("index_set", builtin_t{eval_error_b, bind_index_set})); 3630 tbl.insert(std::make_pair("index_set_1of2", builtin_t{eval_error_b, bind_index_set_XofY<1, 2>})); 3631 tbl.insert(std::make_pair("index_set_2of2", builtin_t{eval_error_b, bind_index_set_XofY<2, 2>})); 3632 tbl.insert(std::make_pair("index_set_1of3", builtin_t{eval_error_b, bind_index_set_XofY<1, 3>})); 3633 tbl.insert(std::make_pair("index_set_2of3", builtin_t{eval_error_b, bind_index_set_XofY<2, 3>})); 3634 tbl.insert(std::make_pair("index_set_3of3", builtin_t{eval_error_b, bind_index_set_XofY<3, 3>})); 3635 tbl.insert(std::make_pair("index_set_1of4", builtin_t{eval_error_b, bind_index_set_XofY<1, 4>})); 3636 tbl.insert(std::make_pair("index_set_2of4", builtin_t{eval_error_b, bind_index_set_XofY<2, 4>})); 3637 tbl.insert(std::make_pair("index_set_3of4", builtin_t{eval_error_b, bind_index_set_XofY<3, 4>})); 3638 tbl.insert(std::make_pair("index_set_4of4", builtin_t{eval_error_b, bind_index_set_XofY<4, 4>})); 3639 tbl.insert(std::make_pair("length", builtin_t{eval_error_b, bind_length})); 3640 tbl.insert(std::make_pair("lb", builtin_t{eval_error_b, bind_lb})); 3641 tbl.insert(std::make_pair("ub", builtin_t{eval_error_b, bind_ub})); 3642 tbl.insert(std::make_pair("dom", builtin_t{eval_error_b, bind_dom})); 3643 tbl.insert(std::make_pair("lb_array", builtin_t{eval_error_b, bind_lb_array})); 3644 tbl.insert(std::make_pair("ub_array", builtin_t{eval_error_b, bind_ub_array})); 3645 tbl.insert(std::make_pair("dom_array", builtin_t{eval_error_b, bind_dom_array})); 3646 tbl.insert(std::make_pair("set2array", builtin_t{eval_error_b, bind_set2array})); 3647 tbl.insert(std::make_pair("dom_bounds_array", builtin_t{eval_error_b, bind_dom_bounds_array})); 3648 tbl.insert(std::make_pair("arg_max", builtin_t{eval_error_b, bind_arg_max})); 3649 tbl.insert(std::make_pair("arg_min", builtin_t{eval_error_b, bind_arg_min})); 3650 tbl.insert(std::make_pair("card", builtin_t{eval_error_b, bind_card})); 3651 tbl.insert(std::make_pair(c.ids.bool2int.str(), builtin_t{eval_error_b, bind_bool2int})); 3652 tbl.insert(std::make_pair(c.ids.int2float.str(), builtin_t{eval_error_b, bind_bool2int})); 3653 tbl.insert(std::make_pair("uniform", builtin_t{eval_error_b, bind_internal})); 3654 tbl.insert(std::make_pair("sol", builtin_t{eval_error_b, bind_internal})); 3655 tbl.insert(std::make_pair("sort_by", builtin_t{eval_error_b, bind_internal})); 3656 tbl.insert(std::make_pair("floor", builtin_t{eval_error_b, bind_internal})); 3657 tbl.insert(std::make_pair("ceil", builtin_t{eval_error_b, bind_internal})); 3658 tbl.insert(std::make_pair("mzn_in_root_context", builtin_t{eval_context_is_root, bind_error_g})); 3659 tbl.insert(std::make_pair("has_bounds", builtin_t{eval_has_bounds, bind_error_g})); 3660 tbl.insert(std::make_pair("is_fixed", builtin_t{eval_isfixed_b, bind_error_g})); 3661 tbl.insert(std::make_pair("fix", builtin_t{eval_fix, bind_fix})); 3662 tbl.insert(std::make_pair("array_Xd", builtin_t{eval_error_b, bind_internal})); 3663 tbl.insert(std::make_pair("slice_Xd", builtin_t{eval_error_b, bind_internal})); 3664 tbl.insert(std::make_pair("internal_sort", builtin_t{eval_error_b, bind_internal})); 3665 tbl.insert(std::make_pair("internal_max", builtin_t{eval_error_b, bind_max})); 3666 tbl.insert(std::make_pair("internal_set_max", builtin_t{eval_error_b, bind_set_max})); 3667 tbl.insert(std::make_pair("internal_min", builtin_t{eval_error_b, bind_min})); 3668 tbl.insert(std::make_pair("internal_set_min", builtin_t{eval_error_b, bind_set_min})); 3669 tbl.insert( 3670 std::make_pair("symmetry_breaking_constraint", builtin_t{eval_argX_only<1>, bind_error_g})); 3671 tbl.insert(std::make_pair("redundant_constraint", builtin_t{eval_argX_only<1>, bind_error_g})); 3672 // OPTIONAL TYPE INTERNALS 3673 tbl.insert(std::make_pair("reverse_map", builtin_t{eval_error_b, bind_TODO})); 3674 tbl.insert(std::make_pair("occurs", builtin_t{eval_TODO, bind_error_g})); 3675 tbl.insert(std::make_pair("absent", builtin_t{eval_TODO, bind_error_g})); 3676 tbl.insert(std::make_pair("deopt", builtin_t{eval_error_b, bind_TODO})); 3677 return tbl; 3678} 3679builtin_table& builtins(void) { 3680 static builtin_table tbl(init_builtins()); 3681 return tbl; 3682} 3683 3684CG::Binding CG::bind(Id* x, Mode ctx, CodeGen& cg, CG_Builder& frag) { 3685 try { 3686 return CG::Binding(cg.env().lookup(x->v()).first, CG_Cond::ttt()); 3687 } catch (const CG_Env<CodeGen::Binding>::NotFound& exn) { 3688 VarDecl* vd = follow_id_to_decl(x)->cast<VarDecl>(); 3689 int g = cg.find_global(vd); 3690 int r(GET_REG(cg)); 3691 PUSH_INSTR(frag, BytecodeStream::LOAD_GLOBAL, CG::g(g), CG::r(r)); 3692 return CG::Binding(r, CG_Cond::ttt()); 3693 } 3694} 3695 3696CG::Binding CG::bind(SetLit* sl, Mode ctx, CodeGen& cg, CG_Builder& frag) { 3697 bool is_vec(false); 3698 OPEN_VEC(cg, frag); 3699 if (IntSetVal* s = sl->isv()) { 3700 int r_t(GET_REG(cg)); 3701 for (int ii = 0; ii < s->size(); ++ii) { 3702 IntVal l(s->min(ii)); 3703 IntVal u(s->max(ii)); 3704 if (l.isFinite()) { 3705 PUSH_INSTR(frag, BytecodeStream::IMMI, CG::i(l.toInt()), CG::r(r_t)); 3706 } else { 3707 assert(l.isMinusInfinity()); 3708 PUSH_INSTR(frag, BytecodeStream::BUILTIN, cg.find_builtin("infinity"), 3709 CG::r(bind_cst(0, cg, frag))); 3710 PUSH_INSTR(frag, BytecodeStream::POP, CG::r(r_t)); 3711 } 3712 PUSH_INSTR(frag, BytecodeStream::PUSH, CG::r(r_t)); 3713 if (u.isFinite()) { 3714 PUSH_INSTR(frag, BytecodeStream::IMMI, CG::i(u.toInt()), CG::r(r_t)); 3715 } else { 3716 assert(u.isPlusInfinity()); 3717 PUSH_INSTR(frag, BytecodeStream::BUILTIN, cg.find_builtin("infinity"), 3718 CG::r(bind_cst(1, cg, frag))); 3719 PUSH_INSTR(frag, BytecodeStream::POP, CG::r(r_t)); 3720 } 3721 PUSH_INSTR(frag, BytecodeStream::PUSH, CG::r(r_t)); 3722 } 3723 } else { 3724 is_vec = true; 3725 int sz = sl->v().size(); 3726 int r_t(GET_REG(cg)); 3727 for (int ii = 0; ii < sz; ii++) { 3728 // Assumes the set is sorted. 3729 /* 3730 IntLit* k(l->v()[ii]->template cast<IntLit>()); 3731 assert(k); 3732 PUSH_INSTR(frag, BytecodeStream::IMMI, CG::i(k->v().toInt()), CG::r(r_t)); 3733 PUSH_INSTR(frag, BytecodeStream::PUSH, CG::r(r_t)); 3734 PUSH_INSTR(frag, BytecodeStream::PUSH, CG::r(r_t)); 3735 */ 3736 CG::Binding b(CG::bind(sl->v()[ii], cg, frag)); // Ignores any partiality. 3737 PUSH_INSTR(frag, BytecodeStream::PUSH, CG::r(b.first)); 3738 } 3739 } 3740 CLOSE_AGG(cg, frag); 3741 int r(GET_REG(cg)); 3742 PUSH_INSTR(frag, BytecodeStream::POP, CG::r(r)); 3743 if (is_vec) PUSH_INSTR(frag, BytecodeStream::MAKE_SET, CG::r(r), CG::r(r)); 3744 return CG::Binding(r, CG_Cond::ttt()); 3745} 3746 3747CG::Binding CG::bind(ArrayLit* a, Mode ctx, CodeGen& cg, CG_Builder& frag) { 3748 // Build up the array. 3749 std::vector<std::pair<IntLit*, int>> r_vec; 3750 std::vector<CG_Cond::T> p_vec; 3751 3752 int sz(a->size()); 3753 for (int ii = 0; ii < sz; ++ii) { 3754 if ((*a)[ii]->type().isbool()) { 3755 CG_Cond::T c(CG::compile((*a)[ii], cg, frag)); 3756 if (!c.get()) { 3757 if (!c.sign()) { 3758 r_vec.emplace_back(nullptr, bind_cst(1, cg, frag)); 3759 } else { 3760 r_vec.emplace_back(nullptr, bind_cst(0, cg, frag)); 3761 } 3762 } else { 3763 r_vec.emplace_back(nullptr, CG::force(c, ctx, cg, frag)); 3764 } 3765 } else if (auto il = (*a)[ii]->dyn_cast<IntLit>()) { 3766 // Avoid lookup cost when constructing large array literals 3767 r_vec.emplace_back(il, 0xdeadbeef); 3768 } else { 3769 Binding b_ii(CG::bind((*a)[ii], cg, frag)); 3770 r_vec.emplace_back(nullptr, b_ii.first); 3771 p_vec.push_back(b_ii.second); 3772 } 3773 } 3774 3775 // Build Array 3776 int r(GET_REG(cg)); 3777 OPEN_VEC(cg, frag); 3778 for (auto r_c : r_vec) { 3779 if (r_c.first) { 3780 assert(r_c.first->v().isFinite()); 3781 PUSH_INSTR(frag, BytecodeStream::IMMI, CG::i(r_c.first->v().toInt()), CG::r(r)); 3782 PUSH_INSTR(frag, BytecodeStream::PUSH, CG::r(r)); 3783 } else { 3784 PUSH_INSTR(frag, BytecodeStream::PUSH, CG::r(r_c.second)); 3785 } 3786 } 3787 CLOSE_AGG(cg, frag); 3788 PUSH_INSTR(frag, BytecodeStream::POP, CG::r(r)); 3789 3790 // Add index set if required 3791 if (!(a->dims() == 1 && a->min(0))) { 3792 int rI; 3793 if (a->dims() == 1) { 3794 rI = (locate_range(a->min(0), a->max(0), cg, frag)); 3795 } else { 3796 rI = GET_REG(cg); 3797 OPEN_VEC(cg, frag); 3798 for (int ii = 0; ii < a->dims(); ++ii) { 3799 PUSH_INSTR(frag, BytecodeStream::PUSH, CG::r(bind_cst(a->min(ii), cg, frag))); 3800 PUSH_INSTR(frag, BytecodeStream::PUSH, CG::r(bind_cst(a->max(ii), cg, frag))); 3801 } 3802 CLOSE_AGG(cg, frag); 3803 PUSH_INSTR(frag, BytecodeStream::POP, CG::r(rI)); 3804 } 3805 3806 // Combine array and index sets 3807 PUSH_INSTR(frag, BytecodeStream::BUILTIN, cg.find_builtin("array_Xd"), CG::r(r), CG::r(rI)); 3808 PUSH_INSTR(frag, BytecodeStream::POP, CG::r(r)); 3809 } 3810 3811 return {r, CG_Cond::forall(ctx, p_vec)}; 3812} 3813 3814std::pair<int, CG_Cond::T> execute_array_access(int r_A, std::vector<int> r_idxs, CodeGen& cg, 3815 CG_Builder& frag) { 3816 int sz = r_idxs.size(); 3817 int r(GET_REG(cg)); 3818 int r_cond(GET_REG(cg)); 3819 PUSH_INSTR(frag, BytecodeStream::GET_ARRAY, CG::i(sz), CG::r(r_A)); 3820 std::vector<CG_Value> r_args(sz + 2); 3821 for (int ii = 0; ii < sz; ++ii) { 3822 r_args[ii] = CG::r(r_idxs[ii]); 3823 } 3824 r_args[sz] = CG::r(r); 3825 r_args[sz + 1] = CG::r(r_cond); 3826 CG_Instr& i = frag.instrs.back(); 3827 PUSH_INSTR_OPERAND(i, r_args); 3828 return {r, CG_Cond::reg(r_cond, true)}; 3829} 3830 3831CG::Binding CG::bind(ArrayAccess* a, Mode ctx, CodeGen& cg, CG_Builder& frag) { 3832 GCLock lock; 3833 // If the array elements are Boolean, we need to check for partiality 3834 // in the indices, and that the accesses are within-range. 3835 ASTExprVec<Expression> idx(a->idx()); 3836 Expression* A(a->v()); 3837 int sz(idx.size()); 3838 3839 bool is_var = false; 3840 for (int ii = 0; ii < sz; ++ii) { 3841 is_var = is_var || idx[ii]->type().isvar(); 3842 } 3843 3844 // Evaluate the indices, put them in registers. 3845 // Collect partiality of the expression. 3846 std::vector<CG_Cond::T> cond; 3847 3848 // Now evaluate the array body, and emit the indices. 3849 std::vector<int> r_idxs(sz); 3850 for (int ii = 0; ii < sz; ++ii) { 3851 CG::Binding b(CG::bind(idx[ii], cg, frag)); 3852 r_idxs[ii] = b.first; 3853 cond.push_back(b.second); 3854 } 3855 3856 Binding b_A(CG::bind(A, cg, frag)); 3857 int r_A(b_A.first); 3858 cond.push_back(b_A.second); 3859 3860 if (is_var) { 3861 int r = GET_REG(cg); 3862 3863 std::vector<Type> types(sz + 1, Type::varint()); 3864 types[sz] = Type::varint(sz); 3865 auto fun = find_call_fun(cg, {"element"}, Type::varint(), types, BytecodeProc::FUN); 3866 assert(std::get<1>(fun) == BytecodeProc::FUN); 3867 3868 std::vector<CG_Value> r_args(sz + 1); 3869 for (int ii = 0; ii < sz; ++ii) { 3870 r_args[ii] = CG::r(r_idxs[ii]); 3871 } 3872 r_args[sz] = CG::r(r_A); 3873 3874 // Push CALL instruction with the correct id 3875 PUSH_INSTR(frag, BytecodeStream::CALL, BytecodeProc::FUN, std::get<0>(fun), 3876 CG::i(!std::get<2>(fun))); 3877 // Append instruction with register arguments 3878 CG_Instr& i = frag.instrs.back(); 3879 PUSH_INSTR_OPERAND(i, r_args); 3880 3881 PUSH_INSTR(frag, BytecodeStream::POP, CG::r(r)); 3882 return {r, CG_Cond::forall(ctx, cond)}; 3883 } else { 3884 // Just read the vector, and get the appropriate element. 3885 int r; 3886 CG_Cond::T ncond; 3887 std::tie(r, ncond) = execute_array_access(r_A, r_idxs, cg, frag); 3888 cond.push_back(ncond); 3889 return {r, CG_Cond::forall(ctx, cond)}; 3890 } 3891} 3892 3893int make_vec(CodeGen& cg, CG_Builder& frag, const std::vector<int>& regs) { 3894 OPEN_VEC(cg, frag); 3895 for (int r : regs) PUSH_INSTR(frag, BytecodeStream::PUSH, CG::r(r)); 3896 CLOSE_AGG(cg, frag); 3897 int r_vec(GET_REG(cg)); 3898 PUSH_INSTR(frag, BytecodeStream::POP, CG::r(r_vec)); 3899 return r_vec; 3900} 3901 3902void call_clause(CodeGen& cg, CG_Builder& frag, Mode ctx, const std::vector<int>& pos, 3903 const std::vector<int>& neg) { 3904 GCLock lock; 3905 // Build the arguments vectors. 3906 int r_pos(make_vec(cg, frag, pos)); 3907 int r_neg(make_vec(cg, frag, neg)); 3908 auto fun = 3909 find_call_fun(cg, {"clause"}, Type::varbool(), {Type::varbool(1), Type::varbool(1)}, ctx); 3910 assert(BytecodeProc::is_neg(ctx) == BytecodeProc::is_neg(std::get<1>(fun))); 3911 PUSH_INSTR(frag, BytecodeStream::CALL, std::get<1>(fun), std::get<0>(fun), 3912 CG::i(!std::get<2>(fun)), CG::r(r_pos), CG::r(r_neg)); 3913} 3914 3915int deinterlace(CodeGen& cg, CG_Builder& frag, int vec, int width, int offset) { 3916 int r_sz(GET_REG(cg)); 3917 int r_slice(GET_REG(cg)); 3918 3919 OPEN_VEC(cg, frag); 3920 // int r_i(CG::locate_immi(1 + offset, cg, frag)); 3921 // int r_step(CG::locate_immi(width, cg, frag)); 3922 int r_i(bind_cst(1 + offset, cg, frag)); 3923 int r_step(bind_cst(width, cg, frag)); 3924 int r_elt(GET_REG(cg)); 3925 3926 PUSH_INSTR(frag, BytecodeStream::LENGTH, CG::r(vec), CG::r(r_sz)); 3927 int l_hd(GET_LABEL(cg)); 3928 int l_tl(GET_LABEL(cg)); 3929 PUSH_INSTR(frag, BytecodeStream::LEI, CG::r(r_i), CG::r(r_sz), CG::r(r_elt)); 3930 PUSH_INSTR(frag, BytecodeStream::JMPIFNOT, CG::r(r_elt), CG::l(l_tl)); 3931 PUSH_LABEL(frag, l_hd); 3932 PUSH_INSTR(frag, BytecodeStream::GET_VEC, CG::r(vec), CG::r(r_i), CG::r(r_elt)); 3933 PUSH_INSTR(frag, BytecodeStream::PUSH, CG::r(r_elt)); 3934 PUSH_INSTR(frag, BytecodeStream::ADDI, CG::r(r_i), CG::r(r_step), CG::r(r_i)); 3935 PUSH_INSTR(frag, BytecodeStream::LEI, CG::r(r_i), CG::r(r_sz), CG::r(r_elt)); 3936 PUSH_INSTR(frag, BytecodeStream::JMPIF, CG::r(r_elt), CG::l(l_hd)); 3937 PUSH_LABEL(frag, l_tl); 3938 CLOSE_AGG(cg, frag); 3939 PUSH_INSTR(frag, BytecodeStream::POP, CG::r(r_slice)); 3940 3941 return r_slice; 3942} 3943 3944/* 3945inline int reg_k(int k, CodeGen& cg, CG_Builder& frag, int& r) { 3946 if(r == -1) 3947 r = CG::locate_immi(1, cg, frag); 3948 return r; 3949} 3950*/ 3951 3952CG::Binding CG::bind(ITE* ite, Mode ctx, CodeGen& cg, CG_Builder& frag) { 3953 int sz(ite->size()); 3954 int r_one(bind_cst(1, cg, frag)); 3955 3956 bool all_par = true; 3957 for (int ii = 0; ii < sz; ++ii) { 3958 if (!ite->e_if(ii)->type().ispar()) { 3959 all_par = false; 3960 break; 3961 } 3962 } 3963 3964 if (all_par) { 3965 int l_end(GET_LABEL(cg)); 3966 int r_ret(GET_REG(cg)); 3967 int r_cond(GET_REG(cg)); 3968 PUSH_INSTR(frag, BytecodeStream::IMMI, CG::i(1), CG::r(r_cond)); 3969 3970 bool is_total = true; 3971 // We need to interfere with the env, here, because stuff may not be available. 3972 // Except, ite->e_if(0) will always be available. 3973 for (int ii = 0; ii < sz; ++ii) { 3974 int r_sel = CG::force(CG::compile(ite->e_if(ii), cg, frag), BytecodeProc::FUN, cg, frag); 3975 int l_cont(GET_LABEL(cg)); 3976 PUSH_INSTR(frag, BytecodeStream::JMPIFNOT, CG::r(r_sel), CG::l(l_cont)); 3977 cg.env_push(); 3978 if (!ite->e_then(ii)->type().ispar()) all_par = false; 3979 CG::Binding b_res(CG::bind(ite->e_then(ii), cg, frag)); 3980 if (b_res.second.get()) { 3981 is_total = false; 3982 int r_condii(CG::force(b_res.second, ctx, cg, frag)); 3983 PUSH_INSTR(frag, BytecodeStream::MOV, CG::r(r_condii), CG::r(r_cond)); 3984 } else if (!b_res.second.sign()) { 3985 PUSH_INSTR(frag, BytecodeStream::IMMI, CG::i(0), CG::r(r_cond)); 3986 } 3987 PUSH_INSTR(frag, BytecodeStream::MOV, CG::r(b_res.first), CG::r(r_ret)); 3988 PUSH_INSTR(frag, BytecodeStream::JMP, CG::l(l_end)); 3989 cg.env_pop(); 3990 cg.env_push(); 3991 PUSH_LABEL(frag, l_cont); 3992 } 3993 // Else case. 3994 if (!ite->e_else()->type().ispar()) all_par = false; 3995 3996 CG::Binding b_res(CG::bind(ite->e_else(), cg, frag)); 3997 if (b_res.second.get()) { 3998 is_total = false; 3999 int r_condii(CG::force(b_res.second, ctx, cg, frag)); 4000 PUSH_INSTR(frag, BytecodeStream::MOV, CG::r(r_condii), CG::r(r_cond)); 4001 } else if (!b_res.second.sign()) { 4002 PUSH_INSTR(frag, BytecodeStream::IMMI, CG::i(0), CG::r(r_cond)); 4003 } 4004 PUSH_INSTR(frag, BytecodeStream::MOV, CG::r(b_res.first), CG::r(r_ret)); 4005 PUSH_LABEL(frag, l_end); 4006 // Now kill the availability of all the expressions. 4007 for (int ii = 0; ii < sz; ++ii) { 4008 cg.env_pop(); 4009 } 4010 return {r_ret, is_total ? CG_Cond::ttt() : CG_Cond::reg(r_cond, all_par)}; 4011 } else { 4012 GCLock lock; 4013 std::vector<int> r_cond; 4014 std::vector<int> p_res; 4015 std::vector<int> r_res; 4016 4017 bool is_total = true; 4018 for (int ii = 0; ii < sz; ++ii) { 4019 r_cond.push_back( 4020 CG::force(CG::compile(ite->e_if(ii), cg, frag), BytecodeProc::FUN, cg, frag)); 4021 CG::Binding b_res(CG::bind(ite->e_then(ii), cg, frag)); 4022 r_res.push_back(b_res.first); 4023 // Make sure b_res.second is evaluated _outside_ the aggregation. 4024 if (b_res.second.get()) { 4025 is_total = false; 4026 p_res.push_back(CG::force(b_res.second, ctx, cg, frag)); 4027 } else { 4028 if (b_res.second.sign()) { 4029 p_res.push_back(bind_cst(0, cg, frag)); 4030 } else { 4031 p_res.push_back(r_one); 4032 } 4033 } 4034 } 4035 Binding b_final = CG::bind(ite->e_else(), cg, frag); 4036 r_cond.push_back(r_one); 4037 r_res.push_back(b_final.first); 4038 if (b_final.second.get()) { 4039 is_total = false; 4040 p_res.push_back(CG::force(b_final.second, ctx, cg, frag)); 4041 } else { 4042 if (b_final.second.sign()) { 4043 p_res.push_back(bind_cst(0, cg, frag)); 4044 } else { 4045 p_res.push_back(r_one); 4046 } 4047 } 4048 4049 int r_vec_cond(vec2array(r_cond, cg, frag)); 4050 4051 int r_part; 4052 if (!is_total) { 4053 int r_vec_part(vec2array(p_res, cg, frag)); 4054 4055 auto fun = find_call_fun(cg, {"if_then_else_partiality"}, Type::varbool(), 4056 {Type::varbool(1), Type::varbool(1)}, BytecodeProc::FUN); 4057 assert(std::get<1>(fun) == BytecodeProc::FUN); 4058 4059 PUSH_INSTR(frag, BytecodeStream::CALL, std::get<1>(fun), std::get<0>(fun), 4060 CG::i(!std::get<2>(fun)), CG::r(r_vec_cond), CG::r(r_vec_part)); 4061 r_part = GET_REG(cg); 4062 PUSH_INSTR(frag, BytecodeStream::POP, CG::r(r_part)); 4063 } 4064 4065 int r_vec_res(vec2array(r_res, cg, frag)); 4066 4067 auto fun = find_call_fun(cg, {"if_then_else"}, Type::varint(), 4068 {Type::varbool(1), Type::varint(1)}, BytecodeProc::FUN); 4069 assert(std::get<1>(fun) == BytecodeProc::FUN); 4070 4071 int r_ret(GET_REG(cg)); 4072 PUSH_INSTR(frag, BytecodeStream::CALL, std::get<1>(fun), std::get<0>(fun), 4073 CG::i(!std::get<2>(fun)), CG::r(r_vec_cond), CG::r(r_vec_res)); 4074 PUSH_INSTR(frag, BytecodeStream::POP, CG::r(r_ret)); 4075 4076 return {r_ret, is_total ? CG_Cond::ttt() : CG_Cond::reg(r_part, false)}; 4077 } 4078} 4079 4080CG::Binding CG::bind(BinOp* b, Mode ctx, CodeGen& cg, CG_Builder& frag) { 4081 if (b->type().ispar()) { 4082 CG::Binding b_lhs(CG::force_or_bind(b->lhs(), ctx, cg, frag)); 4083 CG::Binding b_rhs(CG::force_or_bind(b->rhs(), ctx, cg, frag)); 4084 if (b->lhs()->type().isintset()) { 4085 int r = bind_binop_par_set(cg, frag, b->op(), b_lhs.first, b_rhs.first); 4086 return {r, CG_Cond::ttt()}; 4087 } 4088 std::vector<CG_Cond::T> cond = {b_lhs.second, b_rhs.second}; 4089 4090 int l_skip; 4091 int r_ret; 4092 if (b->op() == BOT_DIV || b->op() == BOT_IDIV || b->op() == BOT_MOD) { 4093 l_skip = GET_LABEL(cg); 4094 int r_cond = GET_REG(cg); 4095 r_ret = GET_REG(cg); 4096 PUSH_INSTR(frag, BytecodeStream::IMMI, CG::i(0), CG::r(r_ret)); 4097 cond.push_back(CG_Cond::reg(r_cond, true)); 4098 PUSH_INSTR(frag, BytecodeStream::EQI, CG::r(b_rhs.first), CG::r(r_ret), CG::r(r_cond)); 4099 PUSH_INSTR(frag, BytecodeStream::NOT, CG::r(r_cond), CG::r(r_cond)); 4100 PUSH_INSTR(frag, BytecodeStream::JMPIFNOT, CG::r(r_cond), CG::l(l_skip)); 4101 } 4102 int r = bind_binop_par_int(cg, frag, b->op(), b_lhs.first, b_rhs.first); 4103 if (b->op() == BOT_DIV || b->op() == BOT_IDIV || b->op() == BOT_MOD) { 4104 PUSH_INSTR(frag, BytecodeStream::MOV, CG::r(r), CG::r(r_ret)); 4105 r = r_ret; 4106 PUSH_LABEL(frag, l_skip); 4107 } 4108 4109 return {r, CG_Cond::forall(ctx, cond)}; 4110 } else { 4111 std::vector<CG_Cond::T> partial; 4112 int r_lhs = CG::force_or_bind(b->lhs(), ctx, partial, cg, frag); 4113 int r_rhs = CG::force_or_bind(b->rhs(), ctx, partial, cg, frag); 4114 4115 int r = GET_REG(cg); 4116 bind_binop_var(cg, frag, BytecodeProc::FUN, b->op(), r_lhs, r_rhs); 4117 PUSH_INSTR(frag, BytecodeStream::POP, CG::r(r)); 4118 4119 if (b->op() == BOT_DIV || b->op() == BOT_IDIV || b->op() == BOT_MOD) { 4120 // These all require rhs() != 0. Which means we need to evaluate the rhs, 4121 // and put it in a register. 4122 int r_zero(bind_cst(0, cg, frag)); 4123 partial.push_back(~linear_cond(cg, frag, BOT_EQ, -ctx, r_rhs, r_zero)); 4124 } 4125 return {r, CG_Cond::forall(ctx, partial)}; 4126 } 4127} 4128 4129CG::Binding CG::bind(UnOp* u, Mode ctx, CodeGen& cg, CG_Builder& frag) { 4130 // Unop is easy. 4131 switch (u->op()) { 4132 case UOT_NOT: 4133 throw InternalError("Non-Boolean bind called on boolean operator."); 4134 case UOT_PLUS: 4135 return CG::bind(u->e(), cg, frag); 4136 case UOT_MINUS: { 4137 Binding b_e(CG::bind(u->e(), cg, frag)); 4138 int r; 4139 if (u->type().isvar()) { 4140 GCLock lock; 4141 auto fun = 4142 find_call_fun(cg, {"op_minus"}, Type::varint(), {Type::varint()}, BytecodeProc::FUN); 4143 assert(std::get<1>(fun) == BytecodeProc::FUN); 4144 PUSH_INSTR(frag, BytecodeStream::CALL, BytecodeProc::FUN, std::get<0>(fun), 4145 CG::i(!std::get<2>(fun)), CG::r(b_e.first)); 4146 r = GET_REG(cg); 4147 PUSH_INSTR(frag, BytecodeStream::POP, CG::r(r)); 4148 } else { 4149 r = GET_REG(cg); 4150 PUSH_INSTR(frag, BytecodeStream::IMMI, CG::i(0), CG::r(r)); 4151 PUSH_INSTR(frag, BytecodeStream::SUBI, CG::r(r), CG::r(b_e.first), CG::r(r)); 4152 } 4153 return {r, b_e.second}; 4154 } 4155 } 4156} 4157 4158CG::Binding bind_call(Call* call, Mode ctx, CodeGen& cg, CG_Builder& frag) { 4159 // Collects the partiality of the arguments 4160 int sz = call->n_args(); 4161 std::vector<CG_Value> r_arg(sz); 4162 std::vector<CG_Cond::T> p_arg(sz); 4163 for (int ii = 0; ii < sz; ++ii) { 4164 CG::Binding r_bind(CG::force_or_bind(call->arg(ii), BytecodeProc::FUN, cg, frag)); 4165 r_arg[ii] = CG::r(r_bind.first); 4166 p_arg[ii] = r_bind.second; 4167 } 4168 4169 // Bind the value part. 4170 auto fun = find_call_fun(cg, call, BytecodeProc::ROOT); 4171 assert(std::get<1>(fun) == BytecodeProc::FUN); 4172 PUSH_INSTR(frag, BytecodeStream::CALL, BytecodeProc::FUN, std::get<0>(fun), 4173 CG::i((!std::get<2>(fun)) && call->type().isvar()), r_arg); 4174 4175 int r_ret(GET_REG(cg)); 4176 PUSH_INSTR(frag, BytecodeStream::POP, CG::r(r_ret)); 4177 4178 return std::make_pair(r_ret, CG_Cond::forall(ctx, p_arg)); 4179} 4180 4181CG::Binding CG::bind(Call* call, Mode ctx, CodeGen& cg, CG_Builder& frag) { 4182 { 4183 GCLock gc; 4184 auto it(builtins().find(call->id().str())); 4185 if (it != builtins().end()) { 4186 return (*it).second.general(call, ctx, cg, frag); 4187 } 4188 } 4189 4190 return bind_call(call, ctx, cg, frag); 4191} 4192 4193CG::Binding CG::bind(Let* let, Mode ctx, CodeGen& cg, CG_Builder& frag) { 4194 std::vector<CG_Cond::T> partial; 4195 4196 cg.env_push(); 4197 4198 if (let->ann().contains(constants().ann.promise_total)) { 4199 OPEN_OTHER(cg, frag); 4200 eval_let_body(let, BytecodeProc::ROOT, cg, frag, partial); 4201 4202 CG::Binding b_in(CG::bind(let->in(), cg, frag)); 4203 partial.push_back(b_in.second); 4204 post_cond(cg, frag, CG_Cond::forall(BytecodeProc::ROOT, partial)); 4205 PUSH_INSTR(frag, BytecodeStream::PUSH, CG::r(b_in.first)); 4206 CLOSE_AGG(cg, frag); 4207 cg.env_pop(); 4208 4209 int ret = GET_REG(cg); 4210 PUSH_INSTR(frag, BytecodeStream::POP, CG::r(ret)); 4211 4212 return {ret, CG_Cond::forall(ctx, partial)}; 4213 } 4214 4215 eval_let_body(let, BytecodeProc::ROOT, cg, frag, partial); 4216 CG::Binding b_in(CG::bind(let->in(), cg, frag)); 4217 partial.push_back(b_in.second); 4218 4219 cg.env_pop(); 4220 return {b_in.first, CG_Cond::forall(ctx, partial)}; 4221} 4222 4223CG::Binding CG::bind(Comprehension* comp, Mode ctx, CodeGen& cg, CG_Builder& frag) { 4224 // Lift out the outer-most comprehension, since it will always 4225 // be executed. 4226 CG::bind(comp->in(0), cg, frag); 4227 4228 cg.env_push(); 4229 OPEN_VEC(cg, frag); 4230 execute_comprehension_bind(comp, ctx, cg, frag); 4231 CLOSE_AGG(cg, frag); 4232 cg.env_pop(); 4233 int r(GET_REG(cg)); 4234 PUSH_INSTR(frag, BytecodeStream::POP, CG::r(r)); 4235 if (comp->type().is_set()) { 4236 PUSH_INSTR(frag, BytecodeStream::MAKE_SET, CG::r(r), CG::r(r)); 4237 } 4238 return CG::Binding(r, CG_Cond::ttt()); 4239} 4240 4241CG_Cond::T _compile(Expression* e, CodeGen& cg, CG_Builder& frag) { 4242 // Look up the mode we need to compile e in. 4243 CG::Mode ctx(BytecodeProc::FUN); 4244 try { 4245 ctx = cg.mode_map.at(e); 4246 } catch (const std::out_of_range& exn) { 4247 std::cerr << "%% Missing mode for: "; 4248 debugprint(e); 4249 } 4250 4251 switch (e->eid()) { 4252 case Expression::E_INTLIT: 4253 case Expression::E_FLOATLIT: 4254 case Expression::E_SETLIT: 4255 case Expression::E_STRINGLIT: 4256 case Expression::E_ARRAYLIT: 4257 case Expression::E_COMP: 4258 throw InternalError("compile called on non-Boolean expression."); 4259 case Expression::E_BOOLLIT: 4260 return (e->template cast<BoolLit>()->v()) ? CG_Cond::ttt() : CG_Cond::fff(); 4261 case Expression::E_ID: 4262 return CG::compile(e->template cast<Id>(), ctx, cg, frag); 4263 case Expression::E_ANON: 4264 throw InternalError("compile reached unexpected expression type: E_ANON."); 4265 case Expression::E_ARRAYACCESS: 4266 return CG::compile(e->template cast<ArrayAccess>(), ctx, cg, frag); 4267 case Expression::E_ITE: 4268 return CG::compile(e->template cast<ITE>(), ctx, cg, frag); 4269 case Expression::E_BINOP: 4270 return CG::compile(e->template cast<BinOp>(), ctx, cg, frag); 4271 case Expression::E_UNOP: 4272 return CG::compile(e->template cast<UnOp>(), ctx, cg, frag); 4273 case Expression::E_CALL: 4274 return CG::compile(e->template cast<Call>(), ctx, cg, frag); 4275 case Expression::E_LET: 4276 return CG::compile(e->template cast<Let>(), ctx, cg, frag); 4277 case Expression::E_VARDECL: 4278 case Expression::E_TI: 4279 case Expression::E_TIID: 4280 throw InternalError("Bytecode generator encountered unexpected expression type in compile."); 4281 } 4282} 4283 4284CG_Cond::T CG::compile(Expression* e, CodeGen& cg, CG_Builder& frag) { 4285 assert(e->type().isbool()); 4286 try { 4287 CG_Cond::T r(cg.cache_lookup(e).second); 4288 return r; 4289 } catch (const CG_Env<CG::Binding>::NotFound& exn) { 4290 CG_Cond::T cond(_compile(e, cg, frag)); 4291 CG::Binding b(0xdeadbeef, cond); 4292 cg.cache_store(e, b); 4293 return cond; 4294 } 4295 // return _compile(e, cg, frag); // FIXME: Update env representation. 4296} 4297 4298CG_Cond::T CG::compile(Id* x, Mode ctx, CodeGen& cg, CG_Builder& frag) { 4299 try { 4300 CG::Binding b(cg.env().lookup(x->v())); 4301 // Check if there is a complex condition 4302 if (b.second.get()) { 4303 // If so then the register is a placeholder and the condition holds the value 4304 assert(b.first == 0xdeadbeef); 4305 return b.second; 4306 } else { 4307 // Otherwise the value should be in the register 4308 return CG_Cond::reg(b.first, x->type().ispar()); 4309 } 4310 } catch (const CG_Env<Binding>::NotFound& exn) { 4311 VarDecl* vd = follow_id_to_decl(x)->cast<VarDecl>(); 4312 int g = cg.find_global(vd); 4313 int r(GET_REG(cg)); 4314 PUSH_INSTR(frag, BytecodeStream::LOAD_GLOBAL, CG::g(g), CG::r(r)); 4315 return CG_Cond::reg(r, x->type().ispar()); 4316 } 4317} 4318 4319CG_Cond::T CG::compile(ArrayAccess* a, Mode ctx, CodeGen& cg, CG_Builder& frag) { 4320 // If the array elements are Boolean, we need to check for partiality 4321 // in the indices, and that the accesses are within-range. 4322 ASTExprVec<Expression> idx(a->idx()); 4323 Expression* A(a->v()); 4324 int sz(idx.size()); 4325 4326 bool is_var = false; 4327 for (int ii = 0; ii < sz; ++ii) { 4328 is_var = is_var || idx[ii]->type().isvar(); 4329 } 4330 4331 // Evaluate the indices, put them in registers. 4332 // Collect partiality of the expression. 4333 std::vector<CG_Cond::T> cond; 4334 4335 // Now evaluate the array body, and emit the indices. 4336 std::vector<int> r_idxs(sz); 4337 for (int ii = 0; ii < sz; ++ii) { 4338 CG::Binding b(CG::bind(idx[ii], cg, frag)); 4339 r_idxs[ii] = b.first; 4340 cond.push_back(b.second); 4341 } 4342 4343 CG::Binding b_A(CG::bind(A, cg, frag)); 4344 int r_A(b_A.first); 4345 cond.push_back(b_A.second); 4346 4347 if (is_var) { 4348 GCLock lock; 4349 std::vector<Type> types(sz + 2, Type::varint()); 4350 types[0] = Type::varbool(); 4351 types[sz + 1] = Type::varbool(sz); 4352 4353 std::vector<CG_Value> r_args(sz + 1); 4354 for (int ii = 0; ii < sz; ++ii) { 4355 r_args[ii] = CG::r(r_idxs[ii]); 4356 } 4357 r_args[sz] = CG::r(r_A); 4358 4359 cond.push_back(CG_Cond::call({"element"}, -ctx, true, types, r_args)); 4360 return CG_Cond::forall(ctx, cond); 4361 } else { 4362 // Just read the vector, and get the appropriate element. 4363 int r; 4364 CG_Cond::T ncond; 4365 std::tie(r, ncond) = execute_array_access(r_A, r_idxs, cg, frag); 4366 cond.push_back(ncond); 4367 cond.push_back(CG_Cond::reg(r, a->type().ispar())); 4368 return CG_Cond::forall(ctx, cond); 4369 } 4370} 4371 4372CG_Cond::T CG::compile(ITE* ite, Mode ctx, CodeGen& cg, CG_Builder& frag) { 4373 int sz(ite->size()); 4374 4375 // Check whether all the conditions are par. 4376 bool all_par = true; 4377 for (int ii = 0; ii < sz; ++ii) { 4378 if (!ite->e_if(ii)->type().ispar()) { 4379 all_par = false; 4380 break; 4381 } 4382 } 4383 4384 if (all_par) { 4385 // We need to interfere with the env, here, because stuff may not be available. 4386 // Except, ite->e_if(0) will always be available. 4387 int l_end(GET_LABEL(cg)); 4388 int r_ret(GET_REG(cg)); 4389 for (int ii = 0; ii < sz; ++ii) { 4390 int r_sel = CG::force(CG::compile(ite->e_if(ii), cg, frag), BytecodeProc::FUN, cg, frag); 4391 int l_cont(GET_LABEL(cg)); 4392 PUSH_INSTR(frag, BytecodeStream::JMPIFNOT, CG::r(r_sel), CG::l(l_cont)); 4393 cg.env_push(); 4394 if (!ite->e_then(ii)->type().ispar()) all_par = false; 4395 int r_val = CG::force(CG::compile(ite->e_then(ii), cg, frag), ctx, cg, frag); 4396 PUSH_INSTR(frag, BytecodeStream::MOV, CG::r(r_val), CG::r(r_ret)); 4397 PUSH_INSTR(frag, BytecodeStream::JMP, CG::l(l_end)); 4398 cg.env_pop(); 4399 cg.env_push(); 4400 PUSH_LABEL(frag, l_cont); 4401 } 4402 // Else case. 4403 if (!ite->e_else()->type().ispar()) all_par = false; 4404 int r_val = CG::force(CG::compile(ite->e_else(), cg, frag), ctx, cg, frag); 4405 PUSH_INSTR(frag, BytecodeStream::MOV, CG::r(r_val), CG::r(r_ret)); 4406 PUSH_LABEL(frag, l_end); 4407 // Now kill the availability of all the expressions. 4408 for (int ii = 0; ii < sz; ++ii) cg.env_pop(); 4409 return CG_Cond::reg(r_ret, all_par); 4410 } else { 4411 GCLock lock; 4412 4413 // Collect conditions 4414 OPEN_VEC(cg, frag); 4415 for (int ii = 0; ii < sz; ++ii) { 4416 int r_if(CG::force(CG::compile(ite->e_if(ii), cg, frag), BytecodeProc::FUN, cg, frag)); 4417 PUSH_INSTR(frag, BytecodeStream::PUSH, CG::r(r_if)); 4418 } 4419 PUSH_INSTR(frag, BytecodeStream::PUSH, CG::r(bind_cst(1, cg, frag))); 4420 CLOSE_AGG(cg, frag); 4421 int r_if(GET_REG(cg)); 4422 PUSH_INSTR(frag, BytecodeStream::POP, CG::r(r_if)); 4423 4424 // Collect results 4425 OPEN_VEC(cg, frag); 4426 for (int ii = 0; ii < sz; ++ii) { 4427 int r_then(CG::force(CG::compile(ite->e_then(ii), cg, frag), ctx, cg, frag)); 4428 PUSH_INSTR(frag, BytecodeStream::PUSH, CG::r(r_then)); 4429 } 4430 { 4431 int r_else(CG::force(CG::compile(ite->e_else(), cg, frag), ctx, cg, frag)); 4432 PUSH_INSTR(frag, BytecodeStream::PUSH, CG::r(r_else)); 4433 } 4434 PUSH_INSTR(frag, BytecodeStream::PUSH, CG::r(bind_cst(1, cg, frag))); 4435 CLOSE_AGG(cg, frag); 4436 int r_then(GET_REG(cg)); 4437 PUSH_INSTR(frag, BytecodeStream::POP, CG::r(r_then)); 4438 4439 return CG_Cond::call({"if_then_else"}, BytecodeProc::FUN, true, 4440 {Type::varbool(), Type::varbool(1), Type::varbool(1)}, 4441 {CG::r(r_if), CG::r(r_then)}); 4442 } 4443} 4444 4445CG_Cond::T shortcut_par_or(Mode ctx, CodeGen& cg, CG_Builder& frag, Expression* lhs, 4446 Expression* rhs) { 4447 Mode f_mode(ctx.strength(), false); // Check 4448 assert(lhs->type().ispar()); 4449 int r(GET_REG(cg)); 4450 int l_exit(GET_LABEL(cg)); 4451 int r_lhs(CG::force(CG::compile(lhs, cg, frag), f_mode, cg, frag)); 4452 PUSH_INSTR(frag, BytecodeStream::MOV, CG::r(r_lhs), CG::r(r)); 4453 PUSH_INSTR(frag, BytecodeStream::JMPIF, CG::r(r), CG::l(l_exit)); 4454 cg.env_push(); 4455 int r_rhs(CG::force(CG::compile(rhs, cg, frag), f_mode, cg, frag)); 4456 PUSH_INSTR(frag, BytecodeStream::MOV, CG::r(r_rhs), CG::r(r)); 4457 cg.env_pop(); 4458 PUSH_LABEL(frag, l_exit); 4459 return CG_Cond::reg(r, rhs->type().ispar()); 4460} 4461 4462CG_Cond::T shortcut_par_and(Mode ctx, CodeGen& cg, CG_Builder& frag, Expression* lhs, 4463 Expression* rhs) { 4464 Mode f_mode(ctx.strength(), false); // Check 4465 assert(lhs->type().ispar()); 4466 int r(GET_REG(cg)); 4467 int l_exit(GET_LABEL(cg)); 4468 int r_lhs(CG::force(CG::compile(lhs, cg, frag), f_mode, cg, frag)); 4469 PUSH_INSTR(frag, BytecodeStream::MOV, CG::r(r_lhs), CG::r(r)); 4470 PUSH_INSTR(frag, BytecodeStream::JMPIFNOT, CG::r(r), CG::l(l_exit)); 4471 cg.env_push(); 4472 int r_rhs(CG::force(CG::compile(rhs, cg, frag), f_mode, cg, frag)); 4473 PUSH_INSTR(frag, BytecodeStream::MOV, CG::r(r_rhs), CG::r(r)); 4474 cg.env_pop(); 4475 PUSH_LABEL(frag, l_exit); 4476 return CG_Cond::reg(r, rhs->type().ispar()); 4477} 4478 4479CG_Cond::T CG::compile(BinOp* b, Mode ctx, CodeGen& cg, CG_Builder& frag) { 4480 std::vector<CG_Cond::T> cond; 4481 // For anything we force here, its calling mode is considered positive (because 4482 // it needs to mean what we said.) 4483 CG::Mode f_mode(ctx.strength(), false); 4484 if (b->lhs()->type().dim() > 0) { 4485 GCLock lock; 4486 assert(b->rhs()->type().dim() > 0); 4487 assert(b->op() == BOT_EQ || b->op() == BOT_EQUIV); 4488 int r_lhs = CG::force_or_bind(b->lhs(), f_mode, cond, cg, frag); 4489 int r_rhs = CG::force_or_bind(b->rhs(), f_mode, cond, cg, frag); 4490 cond.push_back(CG_Cond::call( 4491 {"op_equals"}, BytecodeProc::FUN, true, 4492 {b->type().isvar() ? Type::varbool() : Type::parbool(), b->lhs()->type(), b->rhs()->type()}, 4493 {CG::r(r_lhs), CG::r(r_rhs)})); 4494 return CG_Cond::forall(ctx, cond); 4495 } 4496 if (b->op() == BOT_AND) { 4497 if (b->lhs()->type().ispar()) return shortcut_par_and(ctx, cg, frag, b->lhs(), b->rhs()); 4498 if (b->rhs()->type().ispar()) return shortcut_par_and(ctx, cg, frag, b->rhs(), b->lhs()); 4499 } else if (b->op() == BOT_OR) { 4500 if (b->lhs()->type().ispar()) return shortcut_par_or(ctx, cg, frag, b->lhs(), b->rhs()); 4501 if (b->rhs()->type().ispar()) return shortcut_par_or(ctx, cg, frag, b->rhs(), b->lhs()); 4502 } 4503 if (b->type().ispar()) { 4504 int r_lhs = CG::force_or_bind(b->lhs(), f_mode, cond, cg, frag); 4505 int r_rhs = CG::force_or_bind(b->rhs(), f_mode, cond, cg, frag); 4506 std::vector<int> r_cond; 4507 for (CG_Cond::T c : cond) { 4508 // r_cond.push_back(CG::force(c, ctx, cg, frag)); 4509 // FIXME: This is probably safe, assuming all the conditions 4510 // are also par. We need these to be in their literal, 4511 // non-context-adjusted modes for the disentailment check to work. 4512 // If we want to handle non-par conditions, we probably need to 4513 // force in ctx, then flip back. 4514 r_cond.push_back(CG::force(c, f_mode, cg, frag)); 4515 } 4516 int r_ret; 4517 if (b->lhs()->type().isintset()) { 4518 r_ret = bind_binop_par_set(cg, frag, b->op(), r_lhs, r_rhs); 4519 } else { 4520 r_ret = bind_binop_par_int(cg, frag, b->op(), r_lhs, r_rhs); 4521 } 4522 if (r_cond.size() > 0) { 4523 // If any conditions don't hold, evaluate to false. 4524 int r(GET_REG(cg)); 4525 int l_tl(GET_LABEL(cg)); 4526 PUSH_INSTR(frag, BytecodeStream::IMMI, CG::i(0), CG::r(r)); 4527 for (int r_c : r_cond) { 4528 PUSH_INSTR(frag, BytecodeStream::JMPIFNOT, CG::r(r_c), CG::l(l_tl)); 4529 } 4530 PUSH_INSTR(frag, BytecodeStream::MOV, CG::r(r_ret), CG::r(r)); 4531 PUSH_LABEL(frag, l_tl); 4532 return CG_Cond::reg(r, true); 4533 } else { 4534 return CG_Cond::reg(r_ret, true); 4535 } 4536 } 4537 switch (b->op()) { 4538 case BOT_LE: 4539 case BOT_LQ: 4540 case BOT_GR: 4541 case BOT_GQ: 4542 case BOT_EQ: 4543 case BOT_EQUIV: 4544 case BOT_NQ: { 4545 // Potentially partial. 4546 int r_lhs = CG::force_or_bind(b->lhs(), f_mode, cond, cg, frag); 4547 int r_rhs = CG::force_or_bind(b->rhs(), f_mode, cond, cg, frag); 4548 cond.push_back(linear_cond(cg, frag, b->op(), ctx, r_lhs, r_rhs)); 4549 return CG_Cond::forall(ctx, cond); 4550 } break; 4551 case BOT_XOR: { 4552 CG_Cond::T c_lhs = CG::compile(b->lhs(), cg, frag); 4553 CG_Cond::T c_rhs = CG::compile(b->rhs(), cg, frag); 4554 return CG_Cond::forall(ctx, CG_Cond::exists(ctx, c_lhs, c_rhs), 4555 CG_Cond::exists(ctx, ~c_lhs, ~c_rhs)); 4556 } break; 4557 case BOT_AND: { 4558 cond.push_back(CG::compile(b->lhs(), cg, frag)); 4559 cond.push_back(CG::compile(b->rhs(), cg, frag)); 4560 return CG_Cond::forall(ctx, cond); 4561 } break; 4562 case BOT_OR: { 4563 cond.push_back(CG::compile(b->lhs(), cg, frag)); 4564 cond.push_back(CG::compile(b->rhs(), cg, frag)); 4565 return CG_Cond::exists(ctx, cond); 4566 } break; 4567 case BOT_IMPL: { 4568 cond.push_back(~CG::compile(b->lhs(), cg, frag)); 4569 cond.push_back(CG::compile(b->rhs(), cg, frag)); 4570 return CG_Cond::exists(ctx, cond); 4571 } break; 4572 case BOT_RIMPL: { 4573 cond.push_back(CG::compile(b->lhs(), cg, frag)); 4574 cond.push_back(~CG::compile(b->rhs(), cg, frag)); 4575 return CG_Cond::exists(ctx, cond); 4576 } 4577 case BOT_IN: { 4578 CG::Binding b_lhs(CG::bind(b->lhs(), cg, frag)); 4579 CG::Binding b_rhs(CG::bind(b->rhs(), cg, frag)); 4580 assert(b->rhs()->type().ispar()); 4581 cond.push_back(b_lhs.second); 4582 cond.push_back(b_rhs.second); 4583 if (ctx == BytecodeProc::ROOT) { 4584 int r(GET_REG(cg)); 4585 PUSH_INSTR(frag, BytecodeStream::INTERSECT_DOMAIN, CG::r(b_lhs.first), CG::r(b_rhs.first), 4586 CG::r(r)); 4587 // If INTERSECT_DOMAIN fails, then the interpreter will mark inconsistent and ABORT 4588 cond.push_back(CG_Cond::ttt()); 4589 } else if (ctx == BytecodeProc::ROOT_NEG) { 4590 int r(GET_REG(cg)); 4591 PUSH_INSTR(frag, BytecodeStream::DOM, CG::r(b_lhs.first), CG::r(r)); 4592 PUSH_INSTR(frag, BytecodeStream::DIFF, CG::r(r), CG::r(b_rhs.first), CG::r(r)); 4593 PUSH_INSTR(frag, BytecodeStream::INTERSECT_DOMAIN, CG::r(b_lhs.first), CG::r(r), CG::r(r)); 4594 // If INTERSECT_DOMAIN fails, then the interpreter will mark inconsistent and ABORT 4595 cond.push_back(CG_Cond::ttt()); 4596 } else { 4597 GCLock lock; 4598 cond.push_back(CG_Cond::call({"set_in"}, ctx, true, 4599 {Type::varbool(), Type::varint(), Type::varsetint()}, 4600 {CG::r(b_lhs.first), CG::r(b_rhs.first)})); 4601 } 4602 return CG_Cond::forall(ctx, cond); 4603 } 4604 default: 4605 break; 4606 } 4607 throw InternalError("Unexpected fall-through in compilation of BinOp."); 4608} 4609 4610CG_Cond::T CG::compile(UnOp* u, Mode ctx, CodeGen& cg, CG_Builder& frag) { 4611 assert(u->op() == UOT_NOT); 4612 return ~CG::compile(u->e(), cg, frag); 4613} 4614 4615CG_Cond::T compile_call(Call* call, Mode ctx, CodeGen& cg, CG_Builder& frag) { 4616 int sz = call->n_args(); 4617 std::vector<CG_Value> r_arg(sz); 4618 std::vector<CG_Cond::T> p_arg; 4619 4620 // Evaluate the args, collecting the conditionality. 4621 std::vector<Type> arg_types(sz + 1); 4622 arg_types[0] = call->type(); 4623 for (int ii = 0; ii < sz; ++ii) { 4624 arg_types[ii + 1] = call->arg(ii)->type(); 4625 r_arg[ii] = CG::r(CG::force_or_bind(call->arg(ii), BytecodeProc::FUN, p_arg, cg, frag)); 4626 } 4627 4628 // And finally, add the call itself 4629 p_arg.push_back(CG_Cond::call(call->id(), ctx, call->type().isvar(), arg_types, r_arg)); 4630 return CG_Cond::forall(ctx, p_arg); 4631} 4632 4633CG_Cond::T CG::compile(Call* call, Mode ctx, CodeGen& cg, CG_Builder& frag) { 4634 // If we have a builtin for this, dispatch to that instead. 4635 { 4636 GCLock gc; 4637 auto it(builtins().find(call->id().str())); 4638 if (it != builtins().end()) { 4639 return it->second.boolean(call, ctx, cg, frag); 4640 } 4641 } 4642 4643 return compile_call(call, ctx, cg, frag); 4644} 4645 4646void eval_let_body(Let* let, BytecodeProc::Mode ctx, CodeGen& cg, CG_Builder& frag, 4647 std::vector<CG_Cond::T>& conj) { 4648 ASTExprVec<Expression> bindings(let->let()); 4649 for (Expression* e : bindings) { 4650 if (auto vd = e->dyn_cast<VarDecl>()) { 4651 // Bind the new definitions in context 4652 CodeGen::Binding to_bind(0xdeadbeef, CG_Cond::ttt()); 4653 if (vd->e() && vd->type().isbool()) { 4654 CG_Cond::T cond = CG::compile(vd->e(), cg, frag); 4655 if (!cond.get()) { 4656 to_bind.first = bind_cst(cond.sign(), cg, frag); 4657 } else { 4658 to_bind.second = cond; 4659 } 4660 } else if (vd->e()) { 4661 // FIXME: Assuming domain isn't constraining. 4662 CG::Binding b_v(CG::bind(vd->e(), cg, frag)); 4663 to_bind.first = b_v.first; 4664 conj.push_back(b_v.second); 4665 if (Expression* d = vd->ti()->domain()) { 4666 if (!vd->ti()->isarray()) { 4667 CG::Binding b_d = CG::bind(d, cg, frag); // Discarding any constraints on the set. 4668 conj.push_back(b_d.second); 4669 int r_dp = GET_REG(cg); 4670 PUSH_INSTR(frag, BytecodeStream::INTERSECT_DOMAIN, CG::r(to_bind.first), 4671 CG::r(b_d.first), CG::r(r_dp)); 4672 } 4673 } 4674 } else { 4675 // Variable declaration. Assumes is total and nonempty. 4676 CG::Binding b_d(bind_domain(vd, cg, frag)); 4677 to_bind.first = GET_REG(cg); 4678 if (!vd->ti()->isarray()) { 4679 conj.push_back(b_d.second); 4680 PUSH_INSTR(frag, BytecodeStream::CALL, BytecodeProc::ROOT, cg.find_builtin("mk_intvar"), 4681 CG::i(0), CG::r(b_d.first)); 4682 } else { 4683 int rTMP(GET_REG(cg)); 4684 std::vector<int> r_regs; 4685 for (Expression* r : vd->ti()->ranges()) { 4686 Expression* dim(r->template cast<TypeInst>()->domain()); 4687 CG::Binding b_reg(CG::bind(dim, cg, frag)); 4688 // post_cond(cg, frag, b_reg.second); 4689 r_regs.push_back(b_reg.first); 4690 } 4691 std::vector<Forset> nesting; 4692 OPEN_VEC(cg, frag); 4693 for (int r_r : r_regs) { 4694 Forset iter(cg, r_r); 4695 nesting.push_back(iter); 4696 iter.emit_pre(frag); 4697 } 4698 PUSH_INSTR(frag, BytecodeStream::CALL, BytecodeProc::ROOT, cg.find_builtin("mk_intvar"), 4699 CG::i(0), CG::r(b_d.first)); 4700 for (int r_i = r_regs.size() - 1; r_i >= 0; --r_i) { 4701 nesting[r_i].emit_post(frag); 4702 } 4703 CLOSE_AGG(cg, frag); 4704 PUSH_INSTR(frag, BytecodeStream::POP, CG::r(to_bind.first)); 4705 OPEN_VEC(cg, frag); 4706 for (int r_r : r_regs) { 4707 PUSH_INSTR(frag, BytecodeStream::GET_VEC, CG::r(r_r), CG::r(bind_cst(1, cg, frag)), 4708 CG::r(rTMP)); 4709 PUSH_INSTR(frag, BytecodeStream::PUSH, CG::r(rTMP)); 4710 PUSH_INSTR(frag, BytecodeStream::GET_VEC, CG::r(r_r), CG::r(bind_cst(2, cg, frag)), 4711 CG::r(rTMP)); 4712 PUSH_INSTR(frag, BytecodeStream::PUSH, CG::r(rTMP)); 4713 } 4714 CLOSE_AGG(cg, frag); 4715 PUSH_INSTR(frag, BytecodeStream::POP, CG::r(rTMP)); 4716 4717 PUSH_INSTR(frag, BytecodeStream::BUILTIN, cg.find_builtin("array_Xd"), 4718 CG::r(to_bind.first), CG::r(rTMP)); 4719 } 4720 PUSH_INSTR(frag, BytecodeStream::POP, CG::r(to_bind.first)); 4721 } 4722 cg.env().bind(vd->id()->v(), to_bind); 4723 } else { 4724 conj.push_back(CG::compile(e, cg, frag)); 4725 } 4726 } 4727} 4728 4729CG_Cond::T CG::compile(Let* let, Mode ctx, CodeGen& cg, CG_Builder& frag) { 4730 std::vector<CG_Cond::T> conj; 4731 4732 cg.env_push(); 4733 4734 if (let->ann().contains(constants().ann.promise_total)) { 4735 OPEN_OTHER(cg, frag); 4736 eval_let_body(let, BytecodeProc::ROOT, cg, frag, conj); 4737 // int r = CG::force(CG_Cond::forall(ctx, conj), BytecodeProc::ROOT, cg, frag); 4738 post_cond(cg, frag, CG_Cond::forall(BytecodeProc::ROOT, conj)); 4739 int r = CG::force(CG::compile(let->in(), cg, frag), ctx, cg, frag); 4740 PUSH_INSTR(frag, BytecodeStream::PUSH, CG::r(r)); 4741 CLOSE_AGG(cg, frag); 4742 r = GET_REG(cg); 4743 PUSH_INSTR(frag, BytecodeStream::POP, CG::r(r)); 4744 conj.clear(); 4745 conj.push_back(CG_Cond::reg(r, let->in()->type().ispar())); 4746 } else { 4747 eval_let_body(let, BytecodeProc::ROOT, cg, frag, conj); 4748 conj.push_back(CG::compile(let->in(), cg, frag)); 4749 } 4750 cg.env_pop(); 4751 return CG_Cond::forall(ctx, conj); 4752} 4753 4754int CG::vec2array(std::vector<int> vec, CodeGen& cg, CG_Builder& frag) { 4755 int sz(vec.size()); 4756 OPEN_VEC(cg, frag); 4757 for (int ii = 0; ii < sz; ++ii) { 4758 PUSH_INSTR(frag, BytecodeStream::PUSH, CG::r(vec[ii])); 4759 } 4760 CLOSE_AGG(cg, frag); 4761 int r_vec(GET_REG(cg)); 4762 PUSH_INSTR(frag, BytecodeStream::POP, CG::r(r_vec)); 4763 4764 return r_vec; 4765} 4766}; // namespace MiniZinc