1
0
Fork 0
mirror of https://github.com/ton-blockchain/ton synced 2025-02-12 11:12:16 +00:00
ton/tolk/abscode.cpp
tolk-vm 7a1602f591
[Tolk] Support syntax tensorVar.0 and tupleVar.0
It works both for reading and writing:
> var t = (1, 2);
> t.0;      // 1
> t.0 = 5;
> t;        // (5, 2)

It also works for typed/untyped tuples, producing INDEX and SETINDEX.

Global tensors and tuples works. Nesting `t.0.1.2` works. `mutate` works.
Even mixing tuples inside tensors inside a global for writing works.
2025-01-27 15:30:21 +03:00

425 lines
11 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/>.
*/
#include "tolk.h"
#include "compiler-state.h"
#include "type-system.h"
namespace tolk {
/*
*
* ABSTRACT CODE
*
*/
void TmpVar::show_as_stack_comment(std::ostream& os) const {
if (!name.empty()) {
os << name;
} else {
os << '\'' << ir_idx;
}
#ifdef TOLK_DEBUG
// uncomment for detailed stack output, like `'15(binary-op) '16(glob-var)`
// if (desc) os << desc;
#endif
}
void TmpVar::show(std::ostream& os) const {
os << '\'' << ir_idx; // vars are printed out as `'1 '2` (in stack comments, debug info, etc.)
if (!name.empty()) {
os << '_' << name;
}
#ifdef TOLK_DEBUG
if (desc) {
os << ' ' << desc; // "origin" of implicitly created tmp var, like `'15 (binary-op) '16 (glob-var)`
}
#endif
}
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 & _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::make_refint(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;
} else if (s > 0) {
val |= _NonZero | _Pos | _Finite;
} else {
val |= _Zero | _Neg | _Pos | _Finite;
}
if (val & _Finite) {
val |= int_const->get_bit(0) ? _Odd : _Even;
}
}
void VarDescr::set_const(std::string value) {
str_const = value;
val = _Const;
}
void VarDescr::operator|=(const VarDescr& y) {
val &= y.val;
if (is_int_const() && y.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 {
if (unreachable) {
os << "<unreachable> ";
}
os << "[";
for (const auto& v : list) {
os << ' ' << v;
}
os << " ]\n";
}
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 (impure()) {
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 << " := " << (f_sym ? f_sym->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 _Tuple:
os << pfx << dis << "MKTUPLE ";
show_var_list(os, left, vars);
os << " := ";
show_var_list(os, right, vars);
os << std::endl;
break;
case _UnTuple:
os << pfx << dis << "UNTUPLE ";
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 _SliceConst:
os << pfx << dis << "SCONST ";
show_var_list(os, left, vars);
os << " := " << str_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 << " := " << (g_sym ? g_sym->name : "(null)") << std::endl;
break;
case _SetGlob:
os << pfx << dis << "SETGLOB ";
os << (g_sym ? g_sym->name : "(null)") << " := ";
show_var_list(os, right, vars);
os << 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 << "}";
}
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.show(os);
os << " : " << var.v_type << std::endl;
if (var.loc.is_defined() && (flags & 1) != 0) {
var.loc.show(os);
os << " defined here:\n";
var.loc.show_context(os);
}
}
}
os << "------- BEGIN --------\n";
for (const auto& op : ops) {
op.show(os, vars, "", flags);
}
os << "-------- END ---------\n\n";
}
std::vector<var_idx_t> CodeBlob::create_var(TypePtr var_type, SrcLocation loc, std::string name) {
std::vector<var_idx_t> ir_idx;
int stack_w = var_type->calc_width_on_stack();
ir_idx.reserve(stack_w);
if (const TypeDataTensor* t_tensor = var_type->try_as<TypeDataTensor>()) {
for (int i = 0; i < t_tensor->size(); ++i) {
std::string sub_name = name.empty() ? name : name + "." + std::to_string(i);
std::vector<var_idx_t> nested = create_var(t_tensor->items[i], loc, std::move(sub_name));
ir_idx.insert(ir_idx.end(), nested.begin(), nested.end());
}
} else if (var_type != TypeDataVoid::create()) {
#ifdef TOLK_DEBUG
tolk_assert(stack_w == 1);
#endif
vars.emplace_back(var_cnt, var_type, std::move(name), loc);
ir_idx.emplace_back(var_cnt);
var_cnt++;
}
tolk_assert(static_cast<int>(ir_idx.size()) == stack_w);
return ir_idx;
}
} // namespace tolk