1
0
Fork 0
mirror of https://github.com/ton-blockchain/ton synced 2025-02-15 04:32:21 +00:00
ton/crypto/vm/arithops.cpp
EmelyanenkoK 9f93888cf4
TVM Upgrade (#686)
* New TVM instructions

* Remove PREVBLOCKS

* Separate target ton_crypto into TVM-related and -unrelared code

* Add fine for failed "send message"; rework SENDMSG

* Fix include

* Fix bugs, improve action fines

* Disable fines for special accounts

* Handle msg_balance_remaining.grams == null in transaction.cpp

* Bugfixes in SENDMSG

* Fix fee calculation in SENDMSG

* Fix CellStorageStat and transaction.cpp after merge

* SETBOUNCEONACTIONPHASEFAIL instruction

* ADDDIVMOD instructions

* RUNVM, RUNVMX instructions

* Changes in RUNVM

* Tests for adddiv and runvm

* HASHEXT instruction

* Improve opcode-timing

More iterations
Don't measure preliminary run
Remove logs and other excessive operations
Add "error" to output

* Increase RUNVM gas price

* Optimize HASHEXT, adjust gas price

* Add "bounce of action fail" flag to actions

* Stack operations with unlimited arguments

* Ristretto255 instructions

* Adjust gas consumption

* Optional fixed number of return values in RUNVM, fix exception handling

* Adjust gas consumption

* Simplify gas consumption logic

* Support of secp256k1 and sodium libraries in builds (#11)

* add support of secp256k1 library to the builds (linux, win)

* add support of secp256k1 library to the builds (linux, win)

* install secp256k1 via brew

* install libsodium via brew;
change sodium to upper case in FindSodium.cmake

* install libsodium via brew;
change sodium to upper case in FindSodium.cmake

* simplify FindSodium.cmake

* bug fixing

* bug fixing

* bug fixing

* add macro SODIUM_STATIC

* adjust build command for windows

* put back original FindSodium.cmake

* put back original FindSodium.cmake

* fix sodium unzipped path for windows;
add ninja

* fix sodium unzipped path for windows;
add ninja

* fix sodium unzipped path for windows;
add ninja

* Win32 github build for secp256k1

* x64 architecture github build for secp256k1

* fix sodium linking on linux

* enable docker buildx arm64 builds from forked repos

* enable docker buildx arm64 builds from forked repos

* enable docker buildx arm64 builds from forked repos

* adjust mac builds for secp2561k and sodium

* fix tonlib jni generation

* minor fix

* sync fixes across platforms

* add libsodium build script for android and precompiled static libraries

* build tonlib for android (fails)

* FindSodium uppercase

* remove system libsodium for android, use precompiled instead;
specify SECP256K1_INCLUDE_DIR fir mac 12.6

* uppercase sodium

* simplify FindSodium

* fix windows build sodium path;
use ninja for windows

* simplify sodium 2

* adjust windows sodium paths;
add paths to android jni

* add ninja build windows

* add ninja build windows

* add ninja build windows 2

* remove win ninja

* fix 1

* fix 2

* fix win 3

* fix linux compile 3

* fix jni 1

* fix jni 2 and mac

* fix jni 3

* fix jni 4

* fix jni 5

* fix mac 6

* fix mac 7 and jni paths

* fix jni 8

* rework sodium for android

* rework sodium for android

* rework sodium for android 2

* fixed sodium for android 2

* fixed sodium for android 3

* static secp256k1 for android

* add precompiled arm secp256k1

* add precompiled arm secp256k1

* build native-lib with secp256k1 x86-64 (non arm)

* update precompiled with NDK libsecp256k1.a

* update precompiled with NDK libsecp256k1.a

* update precompiled with NDK libsecp256k1.a

* refactor llvm-strip location

* refactor llvm-strip location

* add native-lib.so for armv7a, armv8a

* add native-lib.so for armv7a, armv8a

* test  armv7a, armv8a

* armv7a - fails linking on sodium, test -> armv8a

* works x86-64, armv7a - fails linking on sodium, armv8a - fails linking secp256k1 (incompatible with aarch64linux)

* update libpsec256k1, sodium static libs

* test x86 android native-lib

* test armv7 android native-lib

* test armv8 android native-lib

* x86_64 and arm64 android native-lib works

* x86_64 and arm64 android native-lib works

* x86_64 and arm64 android native-lib works

* test armv7 android native-lib

* test all android native-libs

* test all android native-libs

* test all android native-libs

* test all android native-libs - without SodiumAndroid

* test all android native-libs - with FindSodiumAndroid.cmake

* win, with Sodium via SODIUM_DIR

* win, with Sodium via SODIUM_DIR env

* win, with Sodium via SODIUM_DIR env

* win, with Sodium via SODIUM_DIR env and SODIUM_USE_STATIC_LIBS

* win, with Sodium via SODIUM_DIR, SODIUM_USE_STATIC_LIBS and SODIUM_INCLUDE_DIR

* android, with FindSodium

* android, with FindSodium with SODIUM_USE_STATIC_LIBS

* remove if not apple

* target_link_libraries(ton_crypto_core PUBLIC secp256k1)

* android SECP256K1_INCLUDE_DIRS

* android SECP256K1_INCLUDE_DIR

* add libsecp256k1.a/so pre-compiled with ubuntu 22 x86-64

* add libsecp256k1.a/so pre-compiled with ubuntu 22 x86-64

* sodium dirs

* sodium dirs

* sodium dirs

* remove NOT APPLE and SodiumAndroid

* add NOT APPLE and remove SodiumAndroid

* add NOT APPLE and remove SodiumAndroid

* remove build scripts for 18.04, reduce CMakeLists.txt

* remove build scripts for 18.04, reduce CMakeLists.txt

* Fix cas consumption during library load

* Fix fetch_config_params after merge

* Add all ADDDIVMOD ops to Asm.fif

* Save unpaid storage fee to due_payment

* Add "set prev blocks info" to emulator

* Adjusted builds  (#13)

* Update flake.nix

Add libsodium

* add libsecp256k1-dev and libsodium-dev into wasm build

* make back emulator a shared library;
put emulator to artifacts;
compile wasm artifacts with sodium and secp256k1.

* add secp256k1 to nix

* compile emulator statically with nix

* compile emulator statically with nix

* compile emulator lib statically with nix

* compile emulator lib statically with nix

* add libemulator to artifacts

* add shared libemulator library to artifacts

* minor release fix

* update set-output commands;
add recent_changelog.md

* releases fixes

* releases fixes, multiline

* releases fixes, multiline

* releases fixes, multiline

* put back multiline changelog

* put back multiline changelog

* ConfigParam 19 (global-id) and GLOBALID instruction

* Fix gas consumption in HASHEXT

* Add blst library

* Add bls instructions

* Allow passing long code to opcode-timing

* Add bls testcase

* More BLS instructions

* Fix tests, add bls tests

* Add more bls tests

* Improve some bls operations

* Adjust some BLS gas prices

* Adjust BLS gas prices

* Enable __BLST_PORTABLE__ flag only if PORTABLE flag is set

* Add tests for BLS_PAIRING

* GASCONSUMED instruction

* Fix compilation against docker with blst library; (#14)

* fix compilation against docker with blst library;
add precompiled libblst.a to android builds

* minor fix

* Adjust BLKSWX gas

* Fix comparison with NAN

* Allow arbitrary integers for scalars in ristretto multiplication, fix test

* Adjust nix builds according to PR 694 (#15)

* integrate and test PR-694

* integrate and test PR-694, test 2

* Add P256_CHKSIGN (secp256r1)

---------

Co-authored-by: SpyCheese <mikle98@yandex.ru>
Co-authored-by: neodiX42 <namlem@gmail.com>
2023-05-24 21:14:13 +03:00

1052 lines
35 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-2020 Telegram Systems LLP
*/
#include <functional>
#include "vm/arithops.h"
#include "vm/log.h"
#include "vm/opctable.h"
#include "vm/stack.hpp"
#include "vm/excno.hpp"
#include "vm/vm.h"
#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;
os << "PUSHINT " << 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;
os << op_prefix << 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;
os << "PUSHINT " << 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;
os << "PUSHINT " << 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;
unsigned d = (args >> 2) & 3;
bool add = false;
if (d == 0 && st->get_global_version() >= 4) {
d = 3;
add = true;
}
if (d == 0 || round_mode == 2) {
throw VmError{Excno::inv_opcode};
}
Stack& stack = st->get_stack();
VM_LOG(st) << "execute DIV/MOD " << (args & 15);
stack.check_underflow(add ? 3 : 2);
auto y = stack.pop_int();
auto w = add ? stack.pop_int() : td::RefInt256{};
auto x = stack.pop_int();
if (add) {
CHECK(d == 3);
typename td::BigInt256::DoubleInt tmp{*x}, quot;
tmp += *w;
tmp.mod_div(*y, quot, round_mode);
stack.push_int_quiet(td::make_refint(quot), quiet);
stack.push_int_quiet(td::make_refint(tmp), quiet);
} else {
switch (d) {
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);
unsigned d = (args >> 2) & 3;
bool add = false;
if (d == 0) {
d = 3;
add = true;
}
if (round_mode == 3) {
return "";
}
std::string s = add ? "ADD" : "";
if (d & 1) {
s += "DIV";
}
if (d & 2) {
s += "MOD";
}
if (quiet) {
s = "Q" + s;
}
return round_mode ? s + "FRC"[round_mode] : s;
}
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;
unsigned d = (args >> 2) & 3;
bool add = false;
if (d == 0 && st->get_global_version() >= 4) {
d = 3;
add = true;
}
if (d == 0 || 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(add ? 3 : 2);
y = stack.pop_smallint_range(256);
} else {
stack.check_underflow(add ? 2 : 1);
}
if (!y) {
round_mode = -1;
}
auto w = add ? stack.pop_int() : td::RefInt256{};
auto x = stack.pop_int();
if (add) {
CHECK(d == 3);
typename td::BigInt256::DoubleInt tmp{*x}, quot;
tmp += *w;
typename td::BigInt256::DoubleInt tmp2{tmp};
tmp2.rshift(y, round_mode).normalize();
stack.push_int_quiet(td::make_refint(tmp2), mode & 1);
tmp.normalize().mod_pow2(y, round_mode).normalize();
stack.push_int_quiet(td::make_refint(tmp), mode & 1);
} else {
switch (d) {
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 (round_mode == 3) {
return "";
}
std::ostringstream os;
if (mode & 1) {
os << 'Q';
}
switch (args & 12) {
case 4:
os << "RSHIFT";
break;
case 8:
os << "MODPOW2";
break;
case 12:
os << "RSHIFTMOD";
break;
case 0:
os << "ADDRSHIFTMOD";
break;
}
if (round_mode) {
os << "FRC"[round_mode];
}
if (mode & 2) {
os << ' ' << y;
}
return os.str();
}
int exec_muldivmod(VmState* st, unsigned args, int quiet) {
int round_mode = (int)(args & 3) - 1;
unsigned d = (args >> 2) & 3;
bool add = false;
if (d == 0 && st->get_global_version() >= 4) {
d = 3;
add = true;
}
if (d == 0 || round_mode == 2) {
throw VmError{Excno::inv_opcode};
}
Stack& stack = st->get_stack();
VM_LOG(st) << "execute MULDIV/MOD " << (args & 15);
stack.check_underflow(add ? 4 : 3);
auto z = stack.pop_int();
auto w = add ? stack.pop_int() : td::RefInt256{};
auto y = stack.pop_int();
auto x = stack.pop_int();
typename td::BigInt256::DoubleInt tmp{0}, quot;
if (add) {
tmp = *w;
}
tmp.add_mul(*x, *y);
auto q = td::make_refint();
tmp.mod_div(*z, quot, round_mode);
switch (d) {
case 1:
stack.push_int_quiet(td::make_refint(quot.normalize()), quiet);
break;
case 3:
stack.push_int_quiet(td::make_refint(quot.normalize()), quiet);
// fallthrough
case 2:
stack.push_int_quiet(td::make_refint(tmp), quiet);
break;
}
return 0;
}
std::string dump_muldivmod(CellSlice&, unsigned args, bool quiet) {
int round_mode = (int)(args & 3);
unsigned d = (args >> 2) & 3;
bool add = false;
if (d == 0) {
d = 3;
add = true;
}
if (round_mode == 3) {
return "";
}
std::string s = add ? "MULADD" : "MUL";
if (d & 1) {
s += "DIV";
}
if (d & 2) {
s += "MOD";
}
if (quiet) {
s = "Q" + s;
}
return round_mode ? s + "FRC"[round_mode] : s;
}
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;
unsigned d = (args >> 2) & 3;
bool add = false;
if (d == 0 && st->get_global_version() >= 4) {
d = 3;
add = true;
}
if (d == 0 || 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(add ? 4 : 3);
z = stack.pop_smallint_range(256);
} else {
stack.check_underflow(add ? 3 : 2);
}
if (!z) {
round_mode = -1;
}
auto w = add ? stack.pop_int() : td::RefInt256{};
auto y = stack.pop_int();
auto x = stack.pop_int();
typename td::BigInt256::DoubleInt tmp{0};
if (add) {
tmp = *w;
}
tmp.add_mul(*x, *y);
switch (d) {
case 1:
tmp.rshift(z, round_mode).normalize();
stack.push_int_quiet(td::make_refint(tmp), mode & 1);
break;
case 3: {
typename td::BigInt256::DoubleInt tmp2{tmp};
tmp2.rshift(z, round_mode).normalize();
stack.push_int_quiet(td::make_refint(tmp2), mode & 1);
}
// fallthrough
case 2:
tmp.normalize().mod_pow2(z, round_mode).normalize();
stack.push_int_quiet(td::make_refint(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 (round_mode == 3) {
return "";
}
std::ostringstream os;
if (mode & 1) {
os << 'Q';
}
switch (args & 12) {
case 4:
os << "MULRSHIFT";
break;
case 8:
os << "MULMODPOW2";
break;
case 12:
os << "MULRSHIFTMOD";
break;
case 0:
os << "MULADDRSHIFTMOD";
break;
}
if (round_mode) {
os << "FRC"[round_mode];
}
if (mode & 2) {
os << ' ' << y;
}
return os.str();
}
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;
unsigned d = (args >> 2) & 3;
bool add = false;
if (d == 0 && st->get_global_version() >= 4) {
d = 3;
add = true;
}
if (d == 0 || 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(add ? 4 : 3);
y = stack.pop_smallint_range(256);
} else {
stack.check_underflow(add ? 3 : 2);
}
auto z = stack.pop_int();
auto w = add ? stack.pop_int() : td::RefInt256{};
auto x = stack.pop_int();
typename td::BigInt256::DoubleInt tmp{*x}, quot;
tmp <<= y;
if (add) {
tmp += *w;
}
switch (d) {
case 1: {
tmp.mod_div(*z, quot, round_mode);
stack.push_int_quiet(td::make_refint(quot.normalize()), mode & 1);
break;
}
case 3: {
tmp.mod_div(*z, quot, round_mode);
stack.push_int_quiet(td::make_refint(quot.normalize()), mode & 1);
stack.push_int_quiet(td::make_refint(tmp), mode & 1);
break;
}
case 2: {
typename td::BigInt256::DoubleInt tmp2;
tmp.mod_div(*z, tmp2, round_mode);
stack.push_int_quiet(td::make_refint(tmp), mode & 1);
break;
}
}
return 0;
}
std::string dump_shldivmod(CellSlice&, unsigned args, int mode) {
int y = -1;
if (mode & 2) {
y = (args & 0xff) + 1;
args >>= 8;
}
int round_mode = (int)(args & 3);
if (round_mode == 3) {
return "";
}
std::ostringstream os;
if (mode & 1) {
os << "Q";
}
switch (args & 12) {
case 4:
os << "LSHIFTDIV";
break;
case 8:
os << "LSHIFTMOD";
break;
case 12:
os << "LSHIFTDIVMOD";
break;
case 0:
os << "LSHIFTADDDIVMOD";
break;
}
if (round_mode) {
os << "FRC"[round_mode];
}
if (y >= 0) {
os << ' ' << y;
}
return os.str();
}
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::make_refint(), 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 " << (mode & 1 ? "Q" : "") << (mode & 2 ? "MIN" : "") << (mode & 4 ? "MAX" : "");
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 " << (quiet ? "QABS" : "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()) {
td::RefInt256 r{true};
r.unique_write().invalidate();
stack.push_int_quiet(std::move(r), 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