1
0
This repository has been archived on 2025-03-06. You can view files and clone it, but cannot push or open issues or pull requests.
Jip J. Dekker f2a1c4e389 Squashed 'software/mza/' content from commit f970a59b17
git-subtree-dir: software/mza
git-subtree-split: f970a59b177c13ca3dd8aaef8cc6681d83b7e813
2021-07-11 16:34:30 +10:00

227 lines
6.9 KiB
C++

/* -*- mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*- */
/*
* Main authors:
* Jip J. Dekker <jip.dekker@monash.edu>
* Guido Tack <guido.tack@monash.edu>
*/
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#pragma once
#include <minizinc/interpreter/values.hh>
namespace MiniZinc {
class BytecodeProc;
class BytecodeStream {
protected:
/// The bytecode stream
std::vector<char> _bs;
/// The largest register used by this code
int _max_reg;
public:
/// Constructor
BytecodeStream(void) : _max_reg(0) {}
enum Instr {
ADDI, // R1, R2 -> R3
SUBI, // R1, R2 -> R3
MULI, // R1, R2 -> R3
DIVI, // R1, R2 -> R3
MODI, // R1, R2 -> R3
INCI, // R1
DECI, // R1
IMMI, // I, R : Load immediate integer into register
CLEAR, // R1, Rn : Clear all registers R1, R2, ..., Rn
LOAD_GLOBAL, // i -> R : Load global i into register R (globals are registers of the bottom
// stack frame)
STORE_GLOBAL, // R -> i : Store register R into global i
MOV, // R1 -> R2
JMP, // i : pc=i
JMPIF, // R, i: if R then pc=i
JMPIFNOT, // R, i: if not R then pc=i
EQI,
LTI,
LEI,
AND,
OR,
NOT,
XOR,
ISPAR, // R1 -> R2: put whether value in R1 is not a variable into R2
ISEMPTY, // R1 -> R2: put whether vector in R1 is empty into R2
LENGTH, // R1 -> R2: put length of array in R1 into R2
GET_VEC, // R1, R2 -> R3: put element R2 of vector in R1 into R3
GET_ARRAY, // n, R1, R2, ... Rn -> Rn+1 Rn+2: put element [R2,...,Rn] of n-dimensional vector
// in R1 into Rn+1 with success signal Rn+2
LB, // R1 -> R2: put lower bound of value in R1 into R2
UB, // R1 -> R2: put upper bound of value in R1 into R2
DOM, // R1 -> R2: put domain of value in R1 into R2
MAKE_SET, // R1 -> R2: turn a vector (of values) into a set
INTERSECTION, // R1, R2 -> R3: put intersection of sets in R1 and R2 into R3
UNION, // R1, R2 -> R3: put union of sets in R1 and R2 into R3
DIFF, // R1, R2 -> R3: put difference of sets in R1 and R2 into R3
INTERSECT_DOMAIN, // R1, R2 -> R3: Update domain of R1 with set R2, place result in R3
OPEN_AGGREGATION, // i: Create a new aggregation context with symbol i
CLOSE_AGGREGATION, // Close current aggregation context, put result onto context above
SIMPLIFY_LIN, // R1, R2, i -> R3, R4, R5: simplify linear expression (R1-R2-i), return
// coefficients (R3), variables (R4), constant (R5)
PUSH, // R: push R onto value stack
POP, // R: pop from value stack into R
POST, // R: post constraint in R
RET, // return from call
CALL, // m, i, cse, R1, ..., Rn: call code i in mode m with n arguments. The 0/1 flag 'cse' is
// used to signal if a CSE lookup should be performed
BUILTIN, // i, n, R1, ..., Rn : call builtin function i
TCALL, // m, i, cse : call code i in mode m (arguments are assumed to be in correct registers
// already). The 0/1 flag 'cse' is used to signal if a CSE lookup should be performed
ITER_ARRAY, // R, l: Iterate over array in R, jump to l when finished.
ITER_VEC, // R, l: Iterate over vector in R, jump to l when finished.
ITER_RANGE, // R1, R2, l: Iterate over values in [R1, R2]
ITER_NEXT, // R: increment the topmost loop, binding the result to R. Pop and jump to loop exit
// if finished.
ITER_BREAK, // i : drop i iterators, jump to the exit of the last.
TRACE, // R: output string representation of R
ABORT, // abort execution
};
/// Get instruction at \a pc and increment \a pc
Instr instr(int& pc) const {
assert(pc < _bs.size());
return static_cast<Instr>(_bs[pc++]);
}
Val intval(int& pc) const {
assert(pc < _bs.size());
const Val* iv = reinterpret_cast<const Val*>(&_bs[pc]);
pc += sizeof(Val);
return *iv;
}
Expression* expr(int& pc) {
assert(pc < _bs.size());
Expression** e = reinterpret_cast<Expression**>(&_bs[pc]);
pc += sizeof(Expression*);
return *e;
}
int reg(int& pc) const {
assert(pc < _bs.size());
const int* iv = reinterpret_cast<const int*>(&_bs[pc]);
pc += sizeof(int);
return *iv;
}
char chr(int& pc) const {
assert(pc < _bs.size());
return _bs[pc++];
}
const char* str(int& pc) const {
assert(pc < _bs.size());
int n = reg(pc);
const char* ret = &_bs[pc];
pc += n + 1;
return ret;
}
int size(void) const { return _bs.size(); }
bool eos(int pc) const { return pc >= _bs.size(); }
void patchAddress(int pc, int addr) {
const char* cp = reinterpret_cast<const char*>(&addr);
for (int i = 0; i < sizeof(int); i++) {
_bs[pc + i] = cp[i];
}
}
void addInstr(const Instr& i) { _bs.push_back(i); }
void addReg(int iv, bool global = false) {
if (!global) {
_max_reg = std::max(_max_reg, iv);
}
const char* cp = reinterpret_cast<const char*>(&iv);
for (int i = 0; i < sizeof(int); i++) {
_bs.push_back(cp[i]);
}
}
void addSmallInt(int iv) {
const char* cp = reinterpret_cast<const char*>(&iv);
for (int i = 0; i < sizeof(int); i++) {
_bs.push_back(cp[i]);
}
}
void addIntVal(const IntVal& iv) {
Val v = Val::fromIntVal(iv);
const char* cp = reinterpret_cast<const char*>(&v);
for (int i = 0; i < sizeof(Val); i++) {
_bs.push_back(cp[i]);
}
}
void addCharVal(const char c) { _bs.push_back(c); }
void addStr(const std::string& s) {
addReg(s.size());
for (char c : s) _bs.push_back(c);
_bs.push_back(0);
}
void setNArgs(int n) { _max_reg = std::max(_max_reg, n); }
std::string toString(const std::vector<BytecodeProc>& procs = std::vector<BytecodeProc>()) const;
int maxRegister(void) const { return _max_reg; }
};
class BytecodeProc {
public:
/// The name of this procedure
std::string name;
/// Number of arguments
int nargs;
/// Delayed execution
bool delay;
/// Modes
enum Mode { ROOT, ROOT_NEG, FUN, FUN_NEG, IMP, IMP_NEG, MAX_MODE = IMP_NEG };
static const std::string mode_to_string[MAX_MODE + 1];
static const bool is_neg(const Mode& mode) {
return mode == ROOT_NEG || mode == FUN_NEG || mode == IMP_NEG;
}
static const Mode negate(const Mode& mode) {
switch (mode) {
case ROOT:
return ROOT_NEG;
case IMP:
return IMP_NEG;
case FUN:
return FUN_NEG;
case ROOT_NEG:
return ROOT;
case IMP_NEG:
return IMP;
case FUN_NEG:
return FUN;
default:
assert(false);
}
}
/// The code for different modes
BytecodeStream mode[MAX_MODE + 1];
};
std::vector<BytecodeProc> parse(const std::string& s);
} // namespace MiniZinc