1
0
mirror of https://github.com/danog/ton.git synced 2024-12-02 09:28:02 +01:00
ton/crypto/func/abscode.cpp
2019-09-07 14:33:36 +04:00

493 lines
12 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 "func.h"
namespace funC {
/*
*
* ABSTRACT CODE
*
*/
TmpVar::TmpVar(var_idx_t _idx, int _cls, TypeExpr* _type, SymDef* sym, const SrcLocation* loc)
: v_type(_type), idx(_idx), cls(_cls), coord(0) {
if (sym) {
name = sym->sym_idx;
sym->value->idx = _idx;
}
if (loc) {
where = std::make_unique<SrcLocation>(*loc);
}
if (!_type) {
v_type = TypeExpr::new_hole();
}
}
void TmpVar::set_location(const SrcLocation& loc) {
if (where) {
*where = loc;
} else {
where = std::make_unique<SrcLocation>(loc);
}
}
void TmpVar::dump(std::ostream& os) const {
show(os);
os << " : " << v_type << " (width ";
v_type->show_width(os);
os << ")";
if (coord > 0) {
os << " = _" << (coord >> 8) << '.' << (coord & 255);
} else if (coord < 0) {
int n = (~coord >> 8), k = (~coord & 0xff);
if (k) {
os << " = (_" << n << ".._" << (n + k - 1) << ")";
} else {
os << " = ()";
}
}
os << std::endl;
}
void TmpVar::show(std::ostream& os, int omit_idx) const {
if (cls & _Named) {
os << sym::symbols.get_name(name);
if (omit_idx && (omit_idx >= 2 || (cls & _UniqueName))) {
return;
}
}
os << '_' << idx;
}
std::ostream& operator<<(std::ostream& os, const TmpVar& var) {
var.show(os);
return os;
}
void VarDescr::show_value(std::ostream& os) const {
if (val & _Int) {
os << 'i';
}
if (val & _Const) {
os << 'c';
}
if (val & _Zero) {
os << '0';
}
if (val & _NonZero) {
os << '!';
}
if (val & _Pos) {
os << '>';
}
if (val & _Neg) {
os << '<';
}
if (val & _Bool) {
os << 'B';
}
if (val & _Bit) {
os << 'b';
}
if (val & _Even) {
os << 'E';
}
if (val & _Odd) {
os << 'O';
}
if (val & _Finite) {
os << 'f';
}
if (val & _Nan) {
os << 'N';
}
if (int_const.not_null()) {
os << '=' << int_const;
}
}
void VarDescr::show(std::ostream& os, const char* name) const {
if (flags & _Last) {
os << '*';
}
if (flags & _Unused) {
os << '?';
}
if (name) {
os << name;
}
os << '_' << idx;
show_value(os);
}
void VarDescr::set_const(long long value) {
return set_const(td::RefInt256{true, value});
}
void VarDescr::set_const(td::RefInt256 value) {
int_const = std::move(value);
if (!int_const->signed_fits_bits(257)) {
int_const.write().invalidate();
}
val = _Const | _Int;
int s = sgn(int_const);
if (s < -1) {
val |= _Nan | _NonZero;
} else if (s < 0) {
val |= _NonZero | _Neg | _Finite;
if (*int_const == -1) {
val |= _Bool;
}
} else if (s > 0) {
val |= _NonZero | _Pos | _Finite;
} else if (!s) {
if (*int_const == 1) {
val |= _Bit;
}
val |= _Zero | _Neg | _Pos | _Finite | _Bool | _Bit;
}
if (val & _Finite) {
val |= int_const->get_bit(0) ? _Odd : _Even;
}
}
void VarDescr::set_const_nan() {
set_const(td::RefInt256{true});
}
void VarDescr::operator|=(const VarDescr& y) {
val &= y.val;
if (is_int_const() && cmp(int_const, y.int_const) != 0) {
val &= ~_Const;
}
if (!(val & _Const)) {
int_const.clear();
}
}
void VarDescr::operator&=(const VarDescr& y) {
val |= y.val;
if (y.int_const.not_null() && int_const.is_null()) {
int_const = y.int_const;
}
}
void VarDescr::set_value(const VarDescr& y) {
val = y.val;
int_const = y.int_const;
}
void VarDescr::set_value(VarDescr&& y) {
val = y.val;
int_const = std::move(y.int_const);
}
void VarDescr::clear_value() {
val = 0;
int_const.clear();
}
void VarDescrList::show(std::ostream& os) const {
os << "[";
for (const auto& v : list) {
os << ' ' << v;
}
os << " ]\n";
}
void Op::flags_set_clear(int set, int clear) {
flags = (flags | set) & ~clear;
for (auto& op : block0) {
op.flags_set_clear(set, clear);
}
for (auto& op : block1) {
op.flags_set_clear(set, clear);
}
}
void Op::split_vars(const std::vector<TmpVar>& vars) {
split_var_list(left, vars);
split_var_list(right, vars);
for (auto& op : block0) {
op.split_vars(vars);
}
for (auto& op : block1) {
op.split_vars(vars);
}
}
void Op::split_var_list(std::vector<var_idx_t>& var_list, const std::vector<TmpVar>& vars) {
int new_size = 0, changes = 0;
for (var_idx_t v : var_list) {
int c = vars.at(v).coord;
if (c < 0) {
++changes;
new_size += (~c & 0xff);
} else {
++new_size;
}
}
if (!changes) {
return;
}
std::vector<var_idx_t> new_var_list;
new_var_list.reserve(new_size);
for (var_idx_t v : var_list) {
int c = vars.at(v).coord;
if (c < 0) {
int n = (~c >> 8), k = (~c & 0xff);
while (k-- > 0) {
new_var_list.push_back(n++);
}
} else {
new_var_list.push_back(v);
}
}
var_list = std::move(new_var_list);
}
void Op::show(std::ostream& os, const std::vector<TmpVar>& vars, std::string pfx, int mode) const {
if (mode & 2) {
os << pfx << " [";
for (const auto& v : var_info.list) {
os << ' ';
if (v.flags & VarDescr::_Last) {
os << '*';
}
if (v.flags & VarDescr::_Unused) {
os << '?';
}
os << vars[v.idx];
if (mode & 4) {
os << ':';
v.show_value(os);
}
}
os << " ]\n";
}
std::string dis = disabled() ? "<disabled> " : "";
if (noreturn()) {
dis += "<noret> ";
}
if (!is_pure()) {
dis += "<impure> ";
}
switch (cl) {
case _Undef:
os << pfx << dis << "???\n";
break;
case _Nop:
os << pfx << dis << "NOP\n";
break;
case _Call:
os << pfx << dis << "CALL: ";
show_var_list(os, left, vars);
os << " := " << (fun_ref ? fun_ref->name() : "(null)") << " ";
if ((mode & 4) && args.size() == right.size()) {
show_var_list(os, args, vars);
} else {
show_var_list(os, right, vars);
}
os << std::endl;
break;
case _CallInd:
os << pfx << dis << "CALLIND: ";
show_var_list(os, left, vars);
os << " := EXEC ";
show_var_list(os, right, vars);
os << std::endl;
break;
case _Let:
os << pfx << dis << "LET ";
show_var_list(os, left, vars);
os << " := ";
show_var_list(os, right, vars);
os << std::endl;
break;
case _IntConst:
os << pfx << dis << "CONST ";
show_var_list(os, left, vars);
os << " := " << int_const << std::endl;
break;
case _Import:
os << pfx << dis << "IMPORT ";
show_var_list(os, left, vars);
os << std::endl;
break;
case _Return:
os << pfx << dis << "RETURN ";
show_var_list(os, left, vars);
os << std::endl;
break;
case _GlobVar:
os << pfx << dis << "GLOBVAR ";
show_var_list(os, left, vars);
os << " := " << (fun_ref ? fun_ref->name() : "(null)") << std::endl;
break;
case _Repeat:
os << pfx << dis << "REPEAT ";
show_var_list(os, left, vars);
os << ' ';
show_block(os, block0.get(), vars, pfx, mode);
os << std::endl;
break;
case _If:
os << pfx << dis << "IF ";
show_var_list(os, left, vars);
os << ' ';
show_block(os, block0.get(), vars, pfx, mode);
os << " ELSE ";
show_block(os, block1.get(), vars, pfx, mode);
os << std::endl;
break;
case _While:
os << pfx << dis << "WHILE ";
show_var_list(os, left, vars);
os << ' ';
show_block(os, block0.get(), vars, pfx, mode);
os << " DO ";
show_block(os, block1.get(), vars, pfx, mode);
os << std::endl;
break;
case _Until:
os << pfx << dis << "UNTIL ";
show_var_list(os, left, vars);
os << ' ';
show_block(os, block0.get(), vars, pfx, mode);
os << std::endl;
break;
case _Again:
os << pfx << dis << "AGAIN ";
show_var_list(os, left, vars);
os << ' ';
show_block(os, block0.get(), vars, pfx, mode);
os << std::endl;
break;
default:
os << pfx << dis << "<???" << cl << "> ";
show_var_list(os, left, vars);
os << " -- ";
show_var_list(os, right, vars);
os << std::endl;
break;
}
}
void Op::show_var_list(std::ostream& os, const std::vector<var_idx_t>& idx_list,
const std::vector<TmpVar>& vars) const {
if (!idx_list.size()) {
os << "()";
} else if (idx_list.size() == 1) {
os << vars.at(idx_list[0]);
} else {
os << "(" << vars.at(idx_list[0]);
for (std::size_t i = 1; i < idx_list.size(); i++) {
os << "," << vars.at(idx_list[i]);
}
os << ")";
}
}
void Op::show_var_list(std::ostream& os, const std::vector<VarDescr>& list, const std::vector<TmpVar>& vars) const {
auto n = list.size();
if (!n) {
os << "()";
} else {
os << "( ";
for (std::size_t i = 0; i < list.size(); i++) {
if (i) {
os << ", ";
}
if (list[i].is_unused()) {
os << '?';
}
os << vars.at(list[i].idx) << ':';
list[i].show_value(os);
}
os << " )";
}
}
void Op::show_block(std::ostream& os, const Op* block, const std::vector<TmpVar>& vars, std::string pfx, int mode) {
os << "{" << std::endl;
std::string pfx2 = pfx + " ";
for (const Op& op : block) {
op.show(os, vars, pfx2, mode);
}
os << pfx << "}";
}
void CodeBlob::flags_set_clear(int set, int clear) {
for (auto& op : ops) {
op.flags_set_clear(set, clear);
}
}
std::ostream& operator<<(std::ostream& os, const CodeBlob& code) {
code.print(os);
return os;
}
// flags: +1 = show variable definition locations; +2 = show vars after each op; +4 = show var abstract value info after each op; +8 = show all variables at start
void CodeBlob::print(std::ostream& os, int flags) const {
os << "CODE BLOB: " << var_cnt << " variables, " << in_var_cnt << " input\n";
if ((flags & 8) != 0) {
for (const auto& var : vars) {
var.dump(os);
if (var.where && (flags & 1) != 0) {
var.where->show(os);
os << " defined here:\n";
var.where->show_context(os);
}
}
}
os << "------- BEGIN --------\n";
for (const auto& op : ops) {
op.show(os, vars, "", flags);
}
os << "-------- END ---------\n\n";
}
var_idx_t CodeBlob::create_var(int cls, TypeExpr* var_type, SymDef* sym, const SrcLocation* location) {
vars.emplace_back(var_cnt, cls, var_type, sym, location);
if (sym) {
sym->value->idx = var_cnt;
}
return var_cnt++;
}
bool CodeBlob::import_params(FormalArgList arg_list) {
if (var_cnt || in_var_cnt || op_cnt) {
return false;
}
std::vector<var_idx_t> list;
for (const auto& par : arg_list) {
TypeExpr* arg_type;
SymDef* arg_sym;
SrcLocation arg_loc;
std::tie(arg_type, arg_sym, arg_loc) = par;
list.push_back(create_var(arg_sym ? (TmpVar::_In | TmpVar::_Named) : TmpVar::_In, arg_type, arg_sym, &arg_loc));
}
emplace_back(loc, Op::_Import, list);
in_var_cnt = var_cnt;
return true;
}
} // namespace funC