mirror of
				https://github.com/ton-blockchain/ton
				synced 2025-03-09 15:40:10 +00:00 
			
		
		
		
	* FunC: enable asserts in Release * FunC: Fix analyzing infinite loops * FunC: Allow catch with one tensor argument * FunC: Fix try/catch stack corruption --------- Co-authored-by: SpyCheese <mikle98@yandex.ru>
		
			
				
	
	
		
			528 lines
		
	
	
	
		
			12 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			528 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-2020 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();
 | |
|   }
 | |
|   if (cls == _Named) {
 | |
|     undefined = true;
 | |
|   }
 | |
| }
 | |
| 
 | |
| 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::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;
 | |
|     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(std::string value) {
 | |
|   str_const = value;
 | |
|   val = _Const;
 | |
| }
 | |
| 
 | |
| void VarDescr::set_const_nan() {
 | |
|   set_const(td::make_refint());
 | |
| }
 | |
| 
 | |
| 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::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 _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 << " := " << (fun_ref ? fun_ref->name() : "(null)") << std::endl;
 | |
|       break;
 | |
|     case _SetGlob:
 | |
|       os << pfx << dis << "SETGLOB ";
 | |
|       os << (fun_ref ? fun_ref->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 << "}";
 | |
| }
 | |
| 
 | |
| 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
 |