mirror of
https://github.com/danog/ton.git
synced 2024-12-11 16:49:37 +01:00
947 lines
33 KiB
C++
947 lines
33 KiB
C++
|
/*
|
||
|
This file is part of TON Blockchain Library.
|
||
|
|
||
|
TON Blockchain Library is free software: you can redistribute it and/or modify
|
||
|
it under the terms of the GNU Lesser General Public License as published by
|
||
|
the Free Software Foundation, either version 2 of the License, or
|
||
|
(at your option) any later version.
|
||
|
|
||
|
TON Blockchain Library is distributed in the hope that it will be useful,
|
||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||
|
GNU Lesser General Public License for more details.
|
||
|
|
||
|
You should have received a copy of the GNU Lesser General Public License
|
||
|
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
|
||
|
|
||
|
Copyright 2017-2019 Telegram Systems LLP
|
||
|
*/
|
||
|
#include <functional>
|
||
|
#include "vm/arithops.h"
|
||
|
#include "vm/log.h"
|
||
|
#include "vm/opctable.h"
|
||
|
#include "vm/stack.hpp"
|
||
|
#include "vm/continuation.h"
|
||
|
#include "vm/excno.hpp"
|
||
|
#include "common/bigint.hpp"
|
||
|
#include "common/refint.h"
|
||
|
|
||
|
namespace vm {
|
||
|
|
||
|
int exec_push_tinyint4(VmState* st, unsigned args) {
|
||
|
int x = (int)((args + 5) & 15) - 5;
|
||
|
Stack& stack = st->get_stack();
|
||
|
VM_LOG(st) << "execute PUSHINT " << x;
|
||
|
stack.push_smallint(x);
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
std::string dump_push_tinyint4(CellSlice&, unsigned args) {
|
||
|
int x = (int)((args + 5) & 15) - 5;
|
||
|
std::ostringstream os{"PUSHINT "};
|
||
|
os << x;
|
||
|
return os.str();
|
||
|
}
|
||
|
|
||
|
int exec_push_tinyint8(VmState* st, unsigned args) {
|
||
|
int x = (signed char)args;
|
||
|
Stack& stack = st->get_stack();
|
||
|
VM_LOG(st) << "execute PUSHINT " << x;
|
||
|
stack.push_smallint(x);
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
std::string dump_op_tinyint8(const char* op_prefix, CellSlice&, unsigned args) {
|
||
|
int x = (signed char)args;
|
||
|
std::ostringstream os{op_prefix};
|
||
|
os << x;
|
||
|
return os.str();
|
||
|
}
|
||
|
|
||
|
int exec_push_smallint(VmState* st, unsigned args) {
|
||
|
int x = (short)args;
|
||
|
Stack& stack = st->get_stack();
|
||
|
VM_LOG(st) << "execute PUSHINT " << x;
|
||
|
stack.push_smallint(x);
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
std::string dump_push_smallint(CellSlice&, unsigned args) {
|
||
|
int x = (short)args;
|
||
|
std::ostringstream os{"PUSHINT "};
|
||
|
os << x;
|
||
|
return os.str();
|
||
|
}
|
||
|
|
||
|
int exec_push_int(VmState* st, CellSlice& cs, unsigned args, int pfx_bits) {
|
||
|
int l = (int)(args & 31) + 2;
|
||
|
if (!cs.have(pfx_bits + 3 + l * 8)) {
|
||
|
throw VmError{Excno::inv_opcode, "not enough bits for integer constant in PUSHINT"};
|
||
|
}
|
||
|
cs.advance(pfx_bits);
|
||
|
td::RefInt256 x = cs.fetch_int256(3 + l * 8);
|
||
|
Stack& stack = st->get_stack();
|
||
|
VM_LOG(st) << "execute PUSHINT " << x;
|
||
|
stack.push_int(std::move(x));
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
std::string dump_push_int(CellSlice& cs, unsigned args, int pfx_bits) {
|
||
|
int l = (int)(args & 31) + 2;
|
||
|
if (!cs.have(pfx_bits + 3 + l * 8)) {
|
||
|
return "";
|
||
|
}
|
||
|
cs.advance(pfx_bits);
|
||
|
td::RefInt256 x = cs.fetch_int256(3 + l * 8);
|
||
|
std::ostringstream os{"PUSHINT "};
|
||
|
os << x;
|
||
|
return os.str();
|
||
|
}
|
||
|
|
||
|
int compute_len_push_int(const CellSlice& cs, unsigned args, int pfx_bits) {
|
||
|
int l = (int)(args & 31) + 2;
|
||
|
if (!cs.have(pfx_bits + 3 + l * 8)) {
|
||
|
return 0;
|
||
|
} else {
|
||
|
return pfx_bits + 3 + l * 8;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
int exec_push_pow2(VmState* st, unsigned args) {
|
||
|
int x = (args & 255) + 1;
|
||
|
Stack& stack = st->get_stack();
|
||
|
VM_LOG(st) << "execute PUSHPOW2 " << x;
|
||
|
td::RefInt256 r{true};
|
||
|
r.unique_write().set_pow2(x);
|
||
|
stack.push(std::move(r));
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
int exec_push_nan(VmState* st) {
|
||
|
Stack& stack = st->get_stack();
|
||
|
VM_LOG(st) << "execute PUSHNAN";
|
||
|
td::RefInt256 r{true};
|
||
|
r.unique_write().invalidate();
|
||
|
stack.push(std::move(r));
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
int exec_push_pow2dec(VmState* st, unsigned args) {
|
||
|
int x = (args & 255) + 1;
|
||
|
Stack& stack = st->get_stack();
|
||
|
VM_LOG(st) << "execute PUSHPOW2DEC " << x;
|
||
|
td::RefInt256 r{true};
|
||
|
r.unique_write().set_pow2(x).add_tiny(-1).normalize();
|
||
|
stack.push(std::move(r));
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
int exec_push_negpow2(VmState* st, unsigned args) {
|
||
|
int x = (args & 255) + 1;
|
||
|
Stack& stack = st->get_stack();
|
||
|
VM_LOG(st) << "execute PUSHNEGPOW2 " << x;
|
||
|
td::RefInt256 r{true};
|
||
|
r.unique_write().set_pow2(x).negate().normalize();
|
||
|
stack.push(std::move(r));
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
void register_int_const_ops(OpcodeTable& cp0) {
|
||
|
using namespace std::placeholders;
|
||
|
cp0.insert(OpcodeInstr::mkfixed(0x7, 4, 4, dump_push_tinyint4, exec_push_tinyint4))
|
||
|
.insert(OpcodeInstr::mkfixed(0x80, 8, 8, std::bind(dump_op_tinyint8, "PUSHINT ", _1, _2), exec_push_tinyint8))
|
||
|
.insert(OpcodeInstr::mkfixed(0x81, 8, 16, dump_push_smallint, exec_push_smallint))
|
||
|
.insert(OpcodeInstr::mkextrange(0x82 << 5, (0x82 << 5) + 31, 13, 5, dump_push_int, exec_push_int,
|
||
|
compute_len_push_int))
|
||
|
.insert(OpcodeInstr::mkfixedrange(0x8300, 0x83ff, 16, 8, instr::dump_1c_l_add(1, "PUSHPOW2 "), exec_push_pow2))
|
||
|
.insert(OpcodeInstr::mksimple(0x83ff, 16, "PUSHNAN", exec_push_nan))
|
||
|
.insert(OpcodeInstr::mkfixed(0x84, 8, 8, instr::dump_1c_l_add(1, "PUSHPOW2DEC "), exec_push_pow2dec))
|
||
|
.insert(OpcodeInstr::mkfixed(0x85, 8, 8, instr::dump_1c_l_add(1, "PUSHNEGPOW2 "), exec_push_negpow2));
|
||
|
}
|
||
|
|
||
|
int exec_add(VmState* st, bool quiet) {
|
||
|
Stack& stack = st->get_stack();
|
||
|
VM_LOG(st) << "execute ADD";
|
||
|
stack.check_underflow(2);
|
||
|
auto y = stack.pop_int();
|
||
|
stack.push_int_quiet(stack.pop_int() + std::move(y), quiet);
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
int exec_sub(VmState* st, bool quiet) {
|
||
|
Stack& stack = st->get_stack();
|
||
|
VM_LOG(st) << "execute SUB";
|
||
|
stack.check_underflow(2);
|
||
|
auto y = stack.pop_int();
|
||
|
stack.push_int_quiet(stack.pop_int() - std::move(y), quiet);
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
int exec_subr(VmState* st, bool quiet) {
|
||
|
Stack& stack = st->get_stack();
|
||
|
VM_LOG(st) << "execute SUBR";
|
||
|
stack.check_underflow(2);
|
||
|
auto y = stack.pop_int();
|
||
|
stack.push_int_quiet(std::move(y) - stack.pop_int(), quiet);
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
int exec_negate(VmState* st, bool quiet) {
|
||
|
Stack& stack = st->get_stack();
|
||
|
VM_LOG(st) << "execute NEGATE";
|
||
|
stack.check_underflow(1);
|
||
|
stack.push_int_quiet(-stack.pop_int(), quiet);
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
int exec_inc(VmState* st, bool quiet) {
|
||
|
Stack& stack = st->get_stack();
|
||
|
VM_LOG(st) << "execute INC";
|
||
|
stack.check_underflow(1);
|
||
|
stack.push_int_quiet(stack.pop_int() + 1, quiet);
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
int exec_dec(VmState* st, bool quiet) {
|
||
|
Stack& stack = st->get_stack();
|
||
|
VM_LOG(st) << "execute DEC";
|
||
|
stack.check_underflow(1);
|
||
|
stack.push_int_quiet(stack.pop_int() - 1, quiet);
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
int exec_add_tinyint8(VmState* st, unsigned args, bool quiet) {
|
||
|
int x = (signed char)args;
|
||
|
Stack& stack = st->get_stack();
|
||
|
VM_LOG(st) << "execute ADDINT " << x;
|
||
|
stack.check_underflow(1);
|
||
|
stack.push_int_quiet(stack.pop_int() + x, quiet);
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
int exec_mul_tinyint8(VmState* st, unsigned args, bool quiet) {
|
||
|
int x = (signed char)args;
|
||
|
Stack& stack = st->get_stack();
|
||
|
VM_LOG(st) << "execute MULINT " << x;
|
||
|
stack.check_underflow(1);
|
||
|
stack.push_int_quiet(stack.pop_int() * x, quiet);
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
int exec_mul(VmState* st, bool quiet) {
|
||
|
Stack& stack = st->get_stack();
|
||
|
VM_LOG(st) << "execute MUL";
|
||
|
stack.check_underflow(2);
|
||
|
auto y = stack.pop_int();
|
||
|
stack.push_int_quiet(stack.pop_int() * std::move(y), quiet);
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
void register_add_mul_ops(OpcodeTable& cp0) {
|
||
|
using namespace std::placeholders;
|
||
|
cp0.insert(OpcodeInstr::mksimple(0xa0, 8, "ADD", std::bind(exec_add, _1, false)))
|
||
|
.insert(OpcodeInstr::mksimple(0xa1, 8, "SUB", std::bind(exec_sub, _1, false)))
|
||
|
.insert(OpcodeInstr::mksimple(0xa2, 8, "SUBR", std::bind(exec_subr, _1, false)))
|
||
|
.insert(OpcodeInstr::mksimple(0xa3, 8, "NEGATE", std::bind(exec_negate, _1, false)))
|
||
|
.insert(OpcodeInstr::mksimple(0xa4, 8, "INC", std::bind(exec_inc, _1, false)))
|
||
|
.insert(OpcodeInstr::mksimple(0xa5, 8, "DEC", std::bind(exec_dec, _1, false)))
|
||
|
.insert(OpcodeInstr::mkfixed(0xa6, 8, 8, std::bind(dump_op_tinyint8, "ADDINT ", _1, _2),
|
||
|
std::bind(exec_add_tinyint8, _1, _2, false)))
|
||
|
.insert(OpcodeInstr::mkfixed(0xa7, 8, 8, std::bind(dump_op_tinyint8, "MULINT ", _1, _2),
|
||
|
std::bind(exec_mul_tinyint8, _1, _2, false)))
|
||
|
.insert(OpcodeInstr::mksimple(0xa8, 8, "MUL", std::bind(exec_mul, _1, false)));
|
||
|
cp0.insert(OpcodeInstr::mksimple(0xb7a0, 16, "QADD", std::bind(exec_add, _1, true)))
|
||
|
.insert(OpcodeInstr::mksimple(0xb7a1, 16, "QSUB", std::bind(exec_sub, _1, true)))
|
||
|
.insert(OpcodeInstr::mksimple(0xb7a2, 16, "QSUBR", std::bind(exec_subr, _1, true)))
|
||
|
.insert(OpcodeInstr::mksimple(0xb7a3, 16, "QNEGATE", std::bind(exec_negate, _1, true)))
|
||
|
.insert(OpcodeInstr::mksimple(0xb7a4, 16, "QINC", std::bind(exec_inc, _1, true)))
|
||
|
.insert(OpcodeInstr::mksimple(0xb7a5, 16, "QDEC", std::bind(exec_dec, _1, true)))
|
||
|
.insert(OpcodeInstr::mkfixed(0xb7a6, 16, 8, std::bind(dump_op_tinyint8, "QADDINT ", _1, _2),
|
||
|
std::bind(exec_add_tinyint8, _1, _2, true)))
|
||
|
.insert(OpcodeInstr::mkfixed(0xb7a7, 16, 8, std::bind(dump_op_tinyint8, "QMULINT ", _1, _2),
|
||
|
std::bind(exec_mul_tinyint8, _1, _2, true)))
|
||
|
.insert(OpcodeInstr::mksimple(0xb7a8, 16, "QMUL", std::bind(exec_mul, _1, true)));
|
||
|
}
|
||
|
|
||
|
int exec_divmod(VmState* st, unsigned args, int quiet) {
|
||
|
int round_mode = (int)(args & 3) - 1;
|
||
|
if (!(args & 12) || round_mode == 2) {
|
||
|
throw VmError{Excno::inv_opcode};
|
||
|
}
|
||
|
Stack& stack = st->get_stack();
|
||
|
VM_LOG(st) << "execute DIV/MOD " << (args & 15);
|
||
|
stack.check_underflow(2);
|
||
|
auto y = stack.pop_int();
|
||
|
auto x = stack.pop_int();
|
||
|
switch ((args >> 2) & 3) {
|
||
|
case 1:
|
||
|
stack.push_int_quiet(td::div(std::move(x), std::move(y), round_mode), quiet);
|
||
|
break;
|
||
|
case 2:
|
||
|
stack.push_int_quiet(td::mod(std::move(x), std::move(y), round_mode), quiet);
|
||
|
break;
|
||
|
case 3: {
|
||
|
auto dm = td::divmod(std::move(x), std::move(y), round_mode);
|
||
|
stack.push_int_quiet(std::move(dm.first), quiet);
|
||
|
stack.push_int_quiet(std::move(dm.second), quiet);
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
std::string dump_divmod(CellSlice&, unsigned args, bool quiet) {
|
||
|
int round_mode = (int)(args & 3);
|
||
|
if (!(args & 12) || round_mode == 3) {
|
||
|
return "";
|
||
|
}
|
||
|
std::string s = (args & 4) ? "DIV" : "";
|
||
|
if (args & 8) {
|
||
|
s += "MOD";
|
||
|
}
|
||
|
if (quiet) {
|
||
|
s = "Q" + s;
|
||
|
}
|
||
|
return s + "FRC"[round_mode];
|
||
|
}
|
||
|
|
||
|
int exec_shrmod(VmState* st, unsigned args, int mode) {
|
||
|
int y = -1;
|
||
|
if (mode & 2) {
|
||
|
y = (args & 0xff) + 1;
|
||
|
args >>= 8;
|
||
|
}
|
||
|
int round_mode = (int)(args & 3) - 1;
|
||
|
if (!(args & 12) || round_mode == 2) {
|
||
|
throw VmError{Excno::inv_opcode};
|
||
|
}
|
||
|
Stack& stack = st->get_stack();
|
||
|
VM_LOG(st) << "execute SHR/MOD " << (args & 15) << ',' << y;
|
||
|
if (!(mode & 2)) {
|
||
|
stack.check_underflow(2);
|
||
|
y = stack.pop_smallint_range(256);
|
||
|
} else {
|
||
|
stack.check_underflow(1);
|
||
|
}
|
||
|
if (!y) {
|
||
|
round_mode = -1;
|
||
|
}
|
||
|
auto x = stack.pop_int();
|
||
|
switch ((args >> 2) & 3) {
|
||
|
case 1:
|
||
|
stack.push_int_quiet(td::rshift(std::move(x), y, round_mode), mode & 1);
|
||
|
break;
|
||
|
case 3:
|
||
|
stack.push_int_quiet(td::rshift(x, y, round_mode), mode & 1);
|
||
|
// fallthrough
|
||
|
case 2:
|
||
|
x.write().mod_pow2(y, round_mode).normalize();
|
||
|
stack.push_int_quiet(std::move(x), mode & 1);
|
||
|
break;
|
||
|
}
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
std::string dump_shrmod(CellSlice&, unsigned args, int mode) {
|
||
|
int y = -1;
|
||
|
if (mode & 2) {
|
||
|
y = (args & 0xff) + 1;
|
||
|
args >>= 8;
|
||
|
}
|
||
|
int round_mode = (int)(args & 3);
|
||
|
if (!(args & 12) || round_mode == 3) {
|
||
|
return "";
|
||
|
}
|
||
|
std::string s;
|
||
|
switch (args & 12) {
|
||
|
case 4:
|
||
|
s = "RSHIFT";
|
||
|
break;
|
||
|
case 8:
|
||
|
s = "MODPOW2";
|
||
|
break;
|
||
|
case 12:
|
||
|
s = "RSHIFTMOD";
|
||
|
break;
|
||
|
}
|
||
|
if (mode & 1) {
|
||
|
s = "Q" + s;
|
||
|
}
|
||
|
s += "FRC"[round_mode];
|
||
|
if (mode & 2) {
|
||
|
char buff[8];
|
||
|
sprintf(buff, " %d", y);
|
||
|
s += buff;
|
||
|
}
|
||
|
return s;
|
||
|
}
|
||
|
|
||
|
int exec_muldivmod(VmState* st, unsigned args, int quiet) {
|
||
|
int round_mode = (int)(args & 3) - 1;
|
||
|
if (!(args & 12) || round_mode == 2) {
|
||
|
throw VmError{Excno::inv_opcode};
|
||
|
}
|
||
|
Stack& stack = st->get_stack();
|
||
|
VM_LOG(st) << "execute MULDIV/MOD " << (args & 15);
|
||
|
stack.check_underflow(3);
|
||
|
auto z = stack.pop_int();
|
||
|
auto y = stack.pop_int();
|
||
|
auto x = stack.pop_int();
|
||
|
typename td::BigInt256::DoubleInt tmp{0};
|
||
|
tmp.add_mul(*x, *y);
|
||
|
auto q = td::RefInt256{true};
|
||
|
tmp.mod_div(*z, q.unique_write(), round_mode);
|
||
|
switch ((args >> 2) & 3) {
|
||
|
case 1:
|
||
|
q.unique_write().normalize();
|
||
|
stack.push_int_quiet(std::move(q), quiet);
|
||
|
break;
|
||
|
case 3:
|
||
|
q.unique_write().normalize();
|
||
|
stack.push_int_quiet(std::move(q), quiet);
|
||
|
// fallthrough
|
||
|
case 2:
|
||
|
stack.push_int_quiet(td::RefInt256{true, tmp}, quiet);
|
||
|
break;
|
||
|
}
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
std::string dump_muldivmod(CellSlice&, unsigned args, bool quiet) {
|
||
|
int round_mode = (int)(args & 3);
|
||
|
if (!(args & 12) || round_mode == 3) {
|
||
|
return "";
|
||
|
}
|
||
|
std::string s = (args & 4) ? "MULDIV" : "MUL";
|
||
|
if (args & 8) {
|
||
|
s += "MOD";
|
||
|
}
|
||
|
if (quiet) {
|
||
|
s = "Q" + s;
|
||
|
}
|
||
|
return s + "FRC"[round_mode];
|
||
|
}
|
||
|
|
||
|
int exec_mulshrmod(VmState* st, unsigned args, int mode) {
|
||
|
int z = -1;
|
||
|
if (mode & 2) {
|
||
|
z = (args & 0xff) + 1;
|
||
|
args >>= 8;
|
||
|
}
|
||
|
int round_mode = (int)(args & 3) - 1;
|
||
|
if (!(args & 12) || round_mode == 2) {
|
||
|
throw VmError{Excno::inv_opcode};
|
||
|
}
|
||
|
Stack& stack = st->get_stack();
|
||
|
VM_LOG(st) << "execute MULSHR/MOD " << (args & 15) << ',' << z;
|
||
|
if (!(mode & 2)) {
|
||
|
stack.check_underflow(3);
|
||
|
z = stack.pop_smallint_range(256);
|
||
|
} else {
|
||
|
stack.check_underflow(2);
|
||
|
}
|
||
|
if (!z) {
|
||
|
round_mode = -1;
|
||
|
}
|
||
|
auto y = stack.pop_int();
|
||
|
auto x = stack.pop_int();
|
||
|
typename td::BigInt256::DoubleInt tmp{0};
|
||
|
tmp.add_mul(*x, *y);
|
||
|
switch ((args >> 2) & 3) {
|
||
|
case 1:
|
||
|
tmp.rshift(z, round_mode).normalize();
|
||
|
stack.push_int_quiet(td::RefInt256{true, tmp}, mode & 1);
|
||
|
break;
|
||
|
case 3: {
|
||
|
typename td::BigInt256::DoubleInt tmp2{tmp};
|
||
|
tmp2.rshift(z, round_mode).normalize();
|
||
|
stack.push_int_quiet(td::RefInt256{true, tmp2}, mode & 1);
|
||
|
}
|
||
|
// fallthrough
|
||
|
case 2:
|
||
|
tmp.mod_pow2(z, round_mode).normalize();
|
||
|
stack.push_int_quiet(td::RefInt256{true, tmp}, mode & 1);
|
||
|
break;
|
||
|
}
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
std::string dump_mulshrmod(CellSlice&, unsigned args, int mode) {
|
||
|
int y = -1;
|
||
|
if (mode & 2) {
|
||
|
y = (args & 0xff) + 1;
|
||
|
args >>= 8;
|
||
|
}
|
||
|
int round_mode = (int)(args & 3);
|
||
|
if (!(args & 12) || round_mode == 3) {
|
||
|
return "";
|
||
|
}
|
||
|
std::string s;
|
||
|
switch (args & 12) {
|
||
|
case 4:
|
||
|
s = "MULRSHIFT";
|
||
|
break;
|
||
|
case 8:
|
||
|
s = "MULMODPOW2";
|
||
|
break;
|
||
|
case 12:
|
||
|
s = "MULRSHIFTMOD";
|
||
|
break;
|
||
|
}
|
||
|
if (mode & 1) {
|
||
|
s = "Q" + s;
|
||
|
}
|
||
|
s += "FRC"[round_mode];
|
||
|
if (mode & 2) {
|
||
|
char buff[8];
|
||
|
sprintf(buff, " %d", y);
|
||
|
s += buff;
|
||
|
}
|
||
|
return s;
|
||
|
}
|
||
|
|
||
|
int exec_shldivmod(VmState* st, unsigned args, int mode) {
|
||
|
int y = -1;
|
||
|
if (mode & 2) {
|
||
|
y = (args & 0xff) + 1;
|
||
|
args >>= 8;
|
||
|
}
|
||
|
int round_mode = (int)(args & 3) - 1;
|
||
|
if (!(args & 12) || round_mode == 2) {
|
||
|
throw VmError{Excno::inv_opcode};
|
||
|
}
|
||
|
Stack& stack = st->get_stack();
|
||
|
VM_LOG(st) << "execute SHLDIV/MOD " << (args & 15) << ',' << y;
|
||
|
if (!(mode & 2)) {
|
||
|
stack.check_underflow(3);
|
||
|
y = stack.pop_smallint_range(256);
|
||
|
} else {
|
||
|
stack.check_underflow(2);
|
||
|
}
|
||
|
auto z = stack.pop_int();
|
||
|
auto x = stack.pop_int();
|
||
|
typename td::BigInt256::DoubleInt tmp{*x};
|
||
|
tmp <<= y;
|
||
|
switch ((args >> 2) & 3) {
|
||
|
case 1: {
|
||
|
auto q = td::RefInt256{true};
|
||
|
tmp.mod_div(*z, q.unique_write(), round_mode);
|
||
|
q.unique_write().normalize();
|
||
|
stack.push_int_quiet(std::move(q), mode & 1);
|
||
|
break;
|
||
|
}
|
||
|
case 3: {
|
||
|
auto q = td::RefInt256{true};
|
||
|
tmp.mod_div(*z, q.unique_write(), round_mode);
|
||
|
q.unique_write().normalize();
|
||
|
stack.push_int_quiet(std::move(q), mode & 1);
|
||
|
stack.push_int_quiet(td::RefInt256{true, tmp}, mode & 1);
|
||
|
break;
|
||
|
}
|
||
|
case 2: {
|
||
|
typename td::BigInt256::DoubleInt tmp2;
|
||
|
tmp.mod_div(*z, tmp2, round_mode);
|
||
|
stack.push_int_quiet(td::RefInt256{true, tmp}, mode & 1);
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
std::string dump_shldivmod(CellSlice&, unsigned args, bool quiet) {
|
||
|
int round_mode = (int)(args & 3);
|
||
|
if (!(args & 12) || round_mode == 3) {
|
||
|
return "";
|
||
|
}
|
||
|
std::string s = (args & 4) ? "LSHIFTDIV" : "LSHIFT";
|
||
|
if (args & 8) {
|
||
|
s += "MOD";
|
||
|
}
|
||
|
if (quiet) {
|
||
|
s = "Q" + s;
|
||
|
}
|
||
|
return s + "FRC"[round_mode];
|
||
|
}
|
||
|
|
||
|
void register_div_ops(OpcodeTable& cp0) {
|
||
|
using namespace std::placeholders;
|
||
|
cp0.insert(OpcodeInstr::mkfixed(0xa90, 12, 4, std::bind(dump_divmod, _1, _2, false),
|
||
|
std::bind(exec_divmod, _1, _2, false)))
|
||
|
.insert(OpcodeInstr::mkfixed(0xa92, 12, 4, std::bind(dump_shrmod, _1, _2, 0), std::bind(exec_shrmod, _1, _2, 0)))
|
||
|
.insert(OpcodeInstr::mkfixed(0xa93, 12, 12, std::bind(dump_shrmod, _1, _2, 2), std::bind(exec_shrmod, _1, _2, 2)))
|
||
|
.insert(OpcodeInstr::mkfixed(0xa98, 12, 4, std::bind(dump_muldivmod, _1, _2, false),
|
||
|
std::bind(exec_muldivmod, _1, _2, false)))
|
||
|
.insert(OpcodeInstr::mkfixed(0xa9a, 12, 4, std::bind(dump_mulshrmod, _1, _2, 0),
|
||
|
std::bind(exec_mulshrmod, _1, _2, 0)))
|
||
|
.insert(OpcodeInstr::mkfixed(0xa9b, 12, 12, std::bind(dump_mulshrmod, _1, _2, 2),
|
||
|
std::bind(exec_mulshrmod, _1, _2, 2)))
|
||
|
.insert(OpcodeInstr::mkfixed(0xa9c, 12, 4, std::bind(dump_shldivmod, _1, _2, 0),
|
||
|
std::bind(exec_shldivmod, _1, _2, 0)))
|
||
|
.insert(OpcodeInstr::mkfixed(0xa9d, 12, 12, std::bind(dump_shldivmod, _1, _2, 2),
|
||
|
std::bind(exec_shldivmod, _1, _2, 2)));
|
||
|
cp0.insert(OpcodeInstr::mkfixed(0xb7a90, 20, 4, std::bind(dump_divmod, _1, _2, true),
|
||
|
std::bind(exec_divmod, _1, _2, true)))
|
||
|
.insert(
|
||
|
OpcodeInstr::mkfixed(0xb7a92, 20, 4, std::bind(dump_shrmod, _1, _2, 1), std::bind(exec_shrmod, _1, _2, 1)))
|
||
|
// .insert(OpcodeInstr::mkfixed(0xb7a93, 20, 12, std::bind(dump_shrmod, _1, _2, 3), std::bind(exec_shrmod, _1, _2, 3)))
|
||
|
.insert(OpcodeInstr::mkfixed(0xb7a98, 20, 4, std::bind(dump_muldivmod, _1, _2, true),
|
||
|
std::bind(exec_muldivmod, _1, _2, true)))
|
||
|
.insert(OpcodeInstr::mkfixed(0xb7a9a, 20, 4, std::bind(dump_mulshrmod, _1, _2, 1),
|
||
|
std::bind(exec_mulshrmod, _1, _2, 1)))
|
||
|
// .insert(OpcodeInstr::mkfixed(0xb7a9b, 20, 12, std::bind(dump_mulshrmod, _1, _2, 3), std::bind(exec_mulshrmod, _1, _2, 3)))
|
||
|
.insert(OpcodeInstr::mkfixed(0xb7a9c, 20, 4, std::bind(dump_shldivmod, _1, _2, 1),
|
||
|
std::bind(exec_shldivmod, _1, _2, 1)))
|
||
|
// .insert(OpcodeInstr::mkfixed(0xb7a9d, 20, 12, std::bind(dump_shldivmod, _1, _2, 3), std::bind(exec_shldivmod, _1, _2, 3)))
|
||
|
;
|
||
|
}
|
||
|
|
||
|
int exec_lshift_tinyint8(VmState* st, unsigned args, bool quiet) {
|
||
|
int x = (args & 0xff) + 1;
|
||
|
Stack& stack = st->get_stack();
|
||
|
VM_LOG(st) << "execute LSHIFT " << x;
|
||
|
stack.check_underflow(1);
|
||
|
stack.push_int_quiet(stack.pop_int() << x, quiet);
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
int exec_rshift_tinyint8(VmState* st, unsigned args, bool quiet) {
|
||
|
int x = (args & 0xff) + 1;
|
||
|
Stack& stack = st->get_stack();
|
||
|
VM_LOG(st) << "execute RSHIFT " << x;
|
||
|
stack.check_underflow(1);
|
||
|
stack.push_int_quiet(stack.pop_int() >> x, quiet);
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
int exec_lshift(VmState* st, bool quiet) {
|
||
|
Stack& stack = st->get_stack();
|
||
|
VM_LOG(st) << "execute LSHIFT";
|
||
|
stack.check_underflow(2);
|
||
|
int x = stack.pop_smallint_range(1023);
|
||
|
stack.push_int_quiet(stack.pop_int() << x, quiet);
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
int exec_rshift(VmState* st, bool quiet) {
|
||
|
Stack& stack = st->get_stack();
|
||
|
VM_LOG(st) << "execute RSHIFT";
|
||
|
stack.check_underflow(2);
|
||
|
int x = stack.pop_smallint_range(1023);
|
||
|
stack.push_int_quiet(stack.pop_int() >> x, quiet);
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
int exec_pow2(VmState* st, bool quiet) {
|
||
|
Stack& stack = st->get_stack();
|
||
|
VM_LOG(st) << "execute POW2";
|
||
|
stack.check_underflow(1);
|
||
|
int x = stack.pop_smallint_range(1023);
|
||
|
td::RefInt256 r{true};
|
||
|
r.unique_write().set_pow2(x);
|
||
|
stack.push_int_quiet(std::move(r), quiet);
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
int exec_and(VmState* st, bool quiet) {
|
||
|
Stack& stack = st->get_stack();
|
||
|
VM_LOG(st) << "execute AND";
|
||
|
stack.check_underflow(2);
|
||
|
auto y = stack.pop_int();
|
||
|
stack.push_int_quiet(stack.pop_int() & std::move(y), quiet);
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
int exec_or(VmState* st, bool quiet) {
|
||
|
Stack& stack = st->get_stack();
|
||
|
VM_LOG(st) << "execute OR";
|
||
|
stack.check_underflow(2);
|
||
|
auto y = stack.pop_int();
|
||
|
stack.push_int_quiet(stack.pop_int() | std::move(y), quiet);
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
int exec_xor(VmState* st, bool quiet) {
|
||
|
Stack& stack = st->get_stack();
|
||
|
VM_LOG(st) << "execute XOR";
|
||
|
stack.check_underflow(2);
|
||
|
auto y = stack.pop_int();
|
||
|
stack.push_int_quiet(stack.pop_int() ^ std::move(y), quiet);
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
int exec_not(VmState* st, bool quiet) {
|
||
|
Stack& stack = st->get_stack();
|
||
|
VM_LOG(st) << "execute NOT";
|
||
|
stack.check_underflow(1);
|
||
|
stack.push_int_quiet(~stack.pop_int(), quiet);
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
int exec_fits_tinyint8(VmState* st, unsigned args, bool quiet) {
|
||
|
int y = (args & 0xff) + 1;
|
||
|
Stack& stack = st->get_stack();
|
||
|
VM_LOG(st) << "execute FITS " << y;
|
||
|
stack.check_underflow(1);
|
||
|
auto x = stack.pop_int();
|
||
|
if (!x->signed_fits_bits(y)) {
|
||
|
x.write().invalidate();
|
||
|
}
|
||
|
stack.push_int_quiet(std::move(x), quiet);
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
int exec_ufits_tinyint8(VmState* st, unsigned args, bool quiet) {
|
||
|
int y = (args & 0xff) + 1;
|
||
|
Stack& stack = st->get_stack();
|
||
|
VM_LOG(st) << "execute UFITS " << y;
|
||
|
stack.check_underflow(1);
|
||
|
auto x = stack.pop_int();
|
||
|
if (!x->unsigned_fits_bits(y)) {
|
||
|
x.write().invalidate();
|
||
|
}
|
||
|
stack.push_int_quiet(std::move(x), quiet);
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
int exec_fits(VmState* st, bool quiet) {
|
||
|
Stack& stack = st->get_stack();
|
||
|
VM_LOG(st) << "execute FITSX";
|
||
|
stack.check_underflow(2);
|
||
|
int y = stack.pop_smallint_range(1023);
|
||
|
auto x = stack.pop_int();
|
||
|
if (!x->signed_fits_bits(y)) {
|
||
|
x.write().invalidate();
|
||
|
}
|
||
|
stack.push_int_quiet(std::move(x), quiet);
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
int exec_ufits(VmState* st, bool quiet) {
|
||
|
Stack& stack = st->get_stack();
|
||
|
VM_LOG(st) << "execute UFITSX";
|
||
|
stack.check_underflow(2);
|
||
|
int y = stack.pop_smallint_range(1023);
|
||
|
auto x = stack.pop_int();
|
||
|
if (!x->unsigned_fits_bits(y)) {
|
||
|
x.write().invalidate();
|
||
|
}
|
||
|
stack.push_int_quiet(std::move(x), quiet);
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
int exec_bitsize(VmState* st, bool sgnd, bool quiet) {
|
||
|
Stack& stack = st->get_stack();
|
||
|
VM_LOG(st) << "execute " << (sgnd ? "" : "U") << "BITSIZE";
|
||
|
stack.check_underflow(1);
|
||
|
auto x = stack.pop_int();
|
||
|
int y = x->bit_size(sgnd);
|
||
|
if (y < 0x7fffffff) {
|
||
|
stack.push_smallint(y);
|
||
|
} else if (!quiet) {
|
||
|
throw VmError{Excno::range_chk, "CHKSIZE for negative integer"};
|
||
|
} else {
|
||
|
stack.push_int_quiet(td::RefInt256{true}, quiet);
|
||
|
}
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
void register_shift_logic_ops(OpcodeTable& cp0) {
|
||
|
using namespace std::placeholders;
|
||
|
cp0.insert(OpcodeInstr::mkfixed(0xaa, 8, 8, instr::dump_1c_l_add(1, "LSHIFT "),
|
||
|
std::bind(exec_lshift_tinyint8, _1, _2, false)))
|
||
|
.insert(OpcodeInstr::mkfixed(0xab, 8, 8, instr::dump_1c_l_add(1, "RSHIFT "),
|
||
|
std::bind(exec_rshift_tinyint8, _1, _2, false)))
|
||
|
.insert(OpcodeInstr::mksimple(0xac, 8, "LSHIFT", std::bind(exec_lshift, _1, false)))
|
||
|
.insert(OpcodeInstr::mksimple(0xad, 8, "RSHIFT", std::bind(exec_rshift, _1, false)))
|
||
|
.insert(OpcodeInstr::mksimple(0xae, 8, "POW2", std::bind(exec_pow2, _1, false)))
|
||
|
.insert(OpcodeInstr::mksimple(0xb0, 8, "AND", std::bind(exec_and, _1, false)))
|
||
|
.insert(OpcodeInstr::mksimple(0xb1, 8, "OR", std::bind(exec_or, _1, false)))
|
||
|
.insert(OpcodeInstr::mksimple(0xb2, 8, "XOR", std::bind(exec_xor, _1, false)))
|
||
|
.insert(OpcodeInstr::mksimple(0xb3, 8, "NOT", std::bind(exec_not, _1, false)))
|
||
|
.insert(OpcodeInstr::mkfixed(0xb4, 8, 8, instr::dump_1c_l_add(1, "FITS "),
|
||
|
std::bind(exec_fits_tinyint8, _1, _2, false)))
|
||
|
.insert(OpcodeInstr::mkfixed(0xb5, 8, 8, instr::dump_1c_l_add(1, "UFITS "),
|
||
|
std::bind(exec_ufits_tinyint8, _1, _2, false)))
|
||
|
.insert(OpcodeInstr::mksimple(0xb600, 16, "FITSX", std::bind(exec_fits, _1, false)))
|
||
|
.insert(OpcodeInstr::mksimple(0xb601, 16, "UFITSX", std::bind(exec_ufits, _1, false)))
|
||
|
.insert(OpcodeInstr::mksimple(0xb602, 16, "BITSIZE", std::bind(exec_bitsize, _1, true, false)))
|
||
|
.insert(OpcodeInstr::mksimple(0xb603, 16, "UBITSIZE", std::bind(exec_bitsize, _1, false, false)));
|
||
|
cp0.insert(OpcodeInstr::mkfixed(0xb7aa, 16, 8, instr::dump_1c_l_add(1, "QLSHIFT "),
|
||
|
std::bind(exec_lshift_tinyint8, _1, _2, true)))
|
||
|
.insert(OpcodeInstr::mkfixed(0xb7ab, 16, 8, instr::dump_1c_l_add(1, "QRSHIFT "),
|
||
|
std::bind(exec_rshift_tinyint8, _1, _2, true)))
|
||
|
.insert(OpcodeInstr::mksimple(0xb7ac, 16, "QLSHIFT", std::bind(exec_lshift, _1, true)))
|
||
|
.insert(OpcodeInstr::mksimple(0xb7ad, 16, "QRSHIFT", std::bind(exec_rshift, _1, true)))
|
||
|
.insert(OpcodeInstr::mksimple(0xb7ae, 16, "QPOW2", std::bind(exec_pow2, _1, true)))
|
||
|
.insert(OpcodeInstr::mksimple(0xb7b0, 16, "QAND", std::bind(exec_and, _1, true)))
|
||
|
.insert(OpcodeInstr::mksimple(0xb7b1, 16, "QOR", std::bind(exec_or, _1, true)))
|
||
|
.insert(OpcodeInstr::mksimple(0xb7b2, 16, "QXOR", std::bind(exec_xor, _1, true)))
|
||
|
.insert(OpcodeInstr::mksimple(0xb7b3, 16, "QNOT", std::bind(exec_not, _1, true)))
|
||
|
.insert(OpcodeInstr::mkfixed(0xb7b4, 16, 8, instr::dump_1c_l_add(1, "QFITS "),
|
||
|
std::bind(exec_fits_tinyint8, _1, _2, true)))
|
||
|
.insert(OpcodeInstr::mkfixed(0xb7b5, 16, 8, instr::dump_1c_l_add(1, "QUFITS "),
|
||
|
std::bind(exec_ufits_tinyint8, _1, _2, true)))
|
||
|
.insert(OpcodeInstr::mksimple(0xb7b600, 24, "QFITSX", std::bind(exec_fits, _1, true)))
|
||
|
.insert(OpcodeInstr::mksimple(0xb7b601, 24, "QUFITSX", std::bind(exec_ufits, _1, true)))
|
||
|
.insert(OpcodeInstr::mksimple(0xb7b602, 24, "QBITSIZE", std::bind(exec_bitsize, _1, true, true)))
|
||
|
.insert(OpcodeInstr::mksimple(0xb7b603, 24, "QUBITSIZE", std::bind(exec_bitsize, _1, false, true)));
|
||
|
}
|
||
|
|
||
|
int exec_minmax(VmState* st, int mode) {
|
||
|
Stack& stack = st->get_stack();
|
||
|
VM_LOG(st) << "execute MINMAXOP " << mode;
|
||
|
stack.check_underflow(2);
|
||
|
auto x = stack.pop_int();
|
||
|
auto y = stack.pop_int();
|
||
|
if (!x->is_valid()) {
|
||
|
y = x;
|
||
|
} else if (!y->is_valid()) {
|
||
|
x = y;
|
||
|
} else if (cmp(x, y) > 0) {
|
||
|
swap(x, y);
|
||
|
}
|
||
|
if (mode & 2) {
|
||
|
stack.push_int_quiet(std::move(x), mode & 1);
|
||
|
}
|
||
|
if (mode & 4) {
|
||
|
stack.push_int_quiet(std::move(y), mode & 1);
|
||
|
}
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
int exec_abs(VmState* st, bool quiet) {
|
||
|
Stack& stack = st->get_stack();
|
||
|
VM_LOG(st) << "execute ABS";
|
||
|
stack.check_underflow(1);
|
||
|
auto x = stack.pop_int();
|
||
|
if (x->is_valid() && x->sgn() < 0) {
|
||
|
stack.push_int_quiet(-std::move(x), quiet);
|
||
|
} else {
|
||
|
stack.push_int_quiet(std::move(x), quiet);
|
||
|
}
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
void register_other_arith_ops(OpcodeTable& cp0) {
|
||
|
using namespace std::placeholders;
|
||
|
cp0.insert(OpcodeInstr::mksimple(0xb608, 16, "MIN", std::bind(exec_minmax, _1, 2)))
|
||
|
.insert(OpcodeInstr::mksimple(0xb609, 16, "MAX", std::bind(exec_minmax, _1, 4)))
|
||
|
.insert(OpcodeInstr::mksimple(0xb60a, 16, "MINMAX", std::bind(exec_minmax, _1, 6)))
|
||
|
.insert(OpcodeInstr::mksimple(0xb60b, 16, "ABS", std::bind(exec_abs, _1, false)));
|
||
|
cp0.insert(OpcodeInstr::mksimple(0xb7b608, 24, "QMIN", std::bind(exec_minmax, _1, 3)))
|
||
|
.insert(OpcodeInstr::mksimple(0xb7b609, 24, "QMAX", std::bind(exec_minmax, _1, 5)))
|
||
|
.insert(OpcodeInstr::mksimple(0xb7b60a, 24, "QMINMAX", std::bind(exec_minmax, _1, 7)))
|
||
|
.insert(OpcodeInstr::mksimple(0xb7b60b, 24, "QABS", std::bind(exec_abs, _1, true)));
|
||
|
}
|
||
|
|
||
|
int exec_sgn(VmState* st, int mode, bool quiet, const char* name) {
|
||
|
Stack& stack = st->get_stack();
|
||
|
VM_LOG(st) << "execute " << name;
|
||
|
stack.check_underflow(1);
|
||
|
auto x = stack.pop_int();
|
||
|
if (!x->is_valid()) {
|
||
|
stack.push_int_quiet(std::move(x), quiet);
|
||
|
} else {
|
||
|
int y = td::sgn(std::move(x));
|
||
|
stack.push_smallint(((mode >> (4 + y * 4)) & 15) - 8);
|
||
|
}
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
int exec_cmp(VmState* st, int mode, bool quiet, const char* name) {
|
||
|
Stack& stack = st->get_stack();
|
||
|
VM_LOG(st) << "execute " << name;
|
||
|
stack.check_underflow(2);
|
||
|
auto y = stack.pop_int();
|
||
|
auto x = stack.pop_int();
|
||
|
if (!x->is_valid() || !y->is_valid()) {
|
||
|
stack.push_int_quiet(std::move(x), quiet);
|
||
|
} else {
|
||
|
int z = td::cmp(std::move(x), std::move(y));
|
||
|
stack.push_smallint(((mode >> (4 + z * 4)) & 15) - 8);
|
||
|
}
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
int exec_cmp_int(VmState* st, unsigned args, int mode, bool quiet, const char* name) {
|
||
|
int y = (signed char)args;
|
||
|
Stack& stack = st->get_stack();
|
||
|
VM_LOG(st) << "execute " << name << "INT " << y;
|
||
|
stack.check_underflow(1);
|
||
|
auto x = stack.pop_int();
|
||
|
if (!x->is_valid()) {
|
||
|
stack.push_int_quiet(std::move(x), quiet);
|
||
|
} else {
|
||
|
int z = td::cmp(std::move(x), y);
|
||
|
stack.push_smallint(((mode >> (4 + z * 4)) & 15) - 8);
|
||
|
}
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
int exec_is_nan(VmState* st) {
|
||
|
Stack& stack = st->get_stack();
|
||
|
VM_LOG(st) << "execute ISNAN";
|
||
|
stack.check_underflow(1);
|
||
|
auto x = stack.pop_int();
|
||
|
stack.push_smallint(x->is_valid() ? 0 : -1);
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
int exec_chk_nan(VmState* st) {
|
||
|
Stack& stack = st->get_stack();
|
||
|
VM_LOG(st) << "execute CHKNAN";
|
||
|
stack.check_underflow(1);
|
||
|
auto x = stack.pop_int();
|
||
|
stack.push_int(std::move(x));
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
void register_int_cmp_ops(OpcodeTable& cp0) {
|
||
|
using namespace std::placeholders;
|
||
|
cp0.insert(OpcodeInstr::mksimple(0xb8, 8, "SGN", std::bind(exec_sgn, _1, 0x987, false, "SGN")))
|
||
|
.insert(OpcodeInstr::mksimple(0xb9, 8, "LESS", std::bind(exec_cmp, _1, 0x887, false, "LESS")))
|
||
|
.insert(OpcodeInstr::mksimple(0xba, 8, "EQUAL", std::bind(exec_cmp, _1, 0x878, false, "EQUAL")))
|
||
|
.insert(OpcodeInstr::mksimple(0xbb, 8, "LEQ", std::bind(exec_cmp, _1, 0x877, false, "LEQ")))
|
||
|
.insert(OpcodeInstr::mksimple(0xbc, 8, "GREATER", std::bind(exec_cmp, _1, 0x788, false, "GREATER")))
|
||
|
.insert(OpcodeInstr::mksimple(0xbd, 8, "NEQ", std::bind(exec_cmp, _1, 0x787, false, "NEQ")))
|
||
|
.insert(OpcodeInstr::mksimple(0xbe, 8, "GEQ", std::bind(exec_cmp, _1, 0x778, false, "GEQ")))
|
||
|
.insert(OpcodeInstr::mksimple(0xbf, 8, "CMP", std::bind(exec_cmp, _1, 0x987, false, "CMP")))
|
||
|
.insert(OpcodeInstr::mkfixed(0xc0, 8, 8, std::bind(dump_op_tinyint8, "EQINT ", _1, _2),
|
||
|
std::bind(exec_cmp_int, _1, _2, 0x878, false, "EQ")))
|
||
|
.insert(OpcodeInstr::mkfixed(0xc1, 8, 8, std::bind(dump_op_tinyint8, "LESSINT ", _1, _2),
|
||
|
std::bind(exec_cmp_int, _1, _2, 0x887, false, "LESS")))
|
||
|
.insert(OpcodeInstr::mkfixed(0xc2, 8, 8, std::bind(dump_op_tinyint8, "GTINT ", _1, _2),
|
||
|
std::bind(exec_cmp_int, _1, _2, 0x788, false, "GT")))
|
||
|
.insert(OpcodeInstr::mkfixed(0xc3, 8, 8, std::bind(dump_op_tinyint8, "NEQINT ", _1, _2),
|
||
|
std::bind(exec_cmp_int, _1, _2, 0x787, false, "NEQ")))
|
||
|
.insert(OpcodeInstr::mksimple(0xc4, 8, "ISNAN", exec_is_nan))
|
||
|
.insert(OpcodeInstr::mksimple(0xc5, 8, "CHKNAN", exec_chk_nan));
|
||
|
cp0.insert(OpcodeInstr::mksimple(0xb7b8, 16, "QSGN", std::bind(exec_sgn, _1, 0x987, true, "QSGN")))
|
||
|
.insert(OpcodeInstr::mksimple(0xb7b9, 16, "QLESS", std::bind(exec_cmp, _1, 0x887, true, "QLESS")))
|
||
|
.insert(OpcodeInstr::mksimple(0xb7ba, 16, "QEQUAL", std::bind(exec_cmp, _1, 0x878, true, "QEQUAL")))
|
||
|
.insert(OpcodeInstr::mksimple(0xb7bb, 16, "QLEQ", std::bind(exec_cmp, _1, 0x877, true, "QLEQ")))
|
||
|
.insert(OpcodeInstr::mksimple(0xb7bc, 16, "QGREATER", std::bind(exec_cmp, _1, 0x788, true, "QGREATER")))
|
||
|
.insert(OpcodeInstr::mksimple(0xb7bd, 16, "QNEQ", std::bind(exec_cmp, _1, 0x787, true, "QNEQ")))
|
||
|
.insert(OpcodeInstr::mksimple(0xb7be, 16, "QGEQ", std::bind(exec_cmp, _1, 0x778, true, "QGEQ")))
|
||
|
.insert(OpcodeInstr::mksimple(0xb7bf, 16, "QCMP", std::bind(exec_cmp, _1, 0x987, true, "QCMP")))
|
||
|
.insert(OpcodeInstr::mkfixed(0xb7c0, 16, 8, std::bind(dump_op_tinyint8, "QEQINT ", _1, _2),
|
||
|
std::bind(exec_cmp_int, _1, _2, 0x878, true, "QEQ")))
|
||
|
.insert(OpcodeInstr::mkfixed(0xb7c1, 16, 8, std::bind(dump_op_tinyint8, "QLESSINT ", _1, _2),
|
||
|
std::bind(exec_cmp_int, _1, _2, 0x887, true, "QLESS")))
|
||
|
.insert(OpcodeInstr::mkfixed(0xb7c2, 16, 8, std::bind(dump_op_tinyint8, "QGTINT ", _1, _2),
|
||
|
std::bind(exec_cmp_int, _1, _2, 0x788, true, "QGT")))
|
||
|
.insert(OpcodeInstr::mkfixed(0xb7c3, 16, 8, std::bind(dump_op_tinyint8, "QNEQINT ", _1, _2),
|
||
|
std::bind(exec_cmp_int, _1, _2, 0x787, true, "QNEQ")));
|
||
|
}
|
||
|
|
||
|
void register_arith_ops(OpcodeTable& cp0) {
|
||
|
register_int_const_ops(cp0);
|
||
|
register_add_mul_ops(cp0);
|
||
|
register_div_ops(cp0);
|
||
|
register_shift_logic_ops(cp0);
|
||
|
register_other_arith_ops(cp0);
|
||
|
register_int_cmp_ops(cp0);
|
||
|
}
|
||
|
|
||
|
} // namespace vm
|