Skip to content

Commit

Permalink
[Tolk] Tolk v0.5.0 as FunC v0.5.0 could have been like
Browse files Browse the repository at this point in the history
All changes from PR "FunC v0.5.0":
#1026

Instead of developing FunC, we decided to fork it.
BTW, the first Tolk release will be v0.6,
a metaphor of FunC v0.5 that missed a chance to occur.
  • Loading branch information
tolk-vm committed Nov 1, 2024
1 parent 82648eb commit ebbab54
Show file tree
Hide file tree
Showing 21 changed files with 1,362 additions and 806 deletions.
16 changes: 8 additions & 8 deletions crypto/smartcont/mathlib.tolk
Original file line number Diff line number Diff line change
Expand Up @@ -81,14 +81,14 @@ int fixed248::acot(int x) inline_ref;

;; random number uniformly distributed in [0..1)
;; fixed248 random();
int fixed248::random() impure inline;
int fixed248::random() inline;
;; random number with standard normal distribution (2100 gas on average)
;; fixed248 nrand();
int fixed248::nrand() impure inline;
int fixed248::nrand() inline;
;; generates a random number approximately distributed according to the standard normal distribution (1200 gas)
;; (fails chi-squared test, but it is shorter and faster than fixed248::nrand())
;; fixed248 nrand_fast();
int fixed248::nrand_fast() impure inline;
int fixed248::nrand_fast() inline;

-} ;; end (declarations)

Expand Down Expand Up @@ -880,7 +880,7 @@ int fixed248::acot(int x) inline_ref {
;; generated by Kinderman--Monahan ratio method modified by J.Leva
;; spends ~ 2k..3k gas on average
;; fixed252 nrand();
int nrand_f252() impure inline_ref {
int nrand_f252() inline_ref {
var (x, s, t, A, B, r0) = (nan(), touch(29483) << 236, touch(-3167) << 239, 12845, 16693, 9043);
;; 4/sqrt(e*Pi) = 1.369 loop iterations on average
do {
Expand Down Expand Up @@ -910,7 +910,7 @@ int nrand_f252() impure inline_ref {
;; generates a random number approximately distributed according to the standard normal distribution
;; much faster than nrand_f252(), should be suitable for most purposes when only several random numbers are needed
;; fixed252 nrand_fast();
int nrand_fast_f252() impure inline_ref {
int nrand_fast_f252() inline_ref {
int t = touch(-3) << 253; ;; -6. as fixed252
repeat (12) {
t += random() / 16; ;; add together 12 uniformly random numbers
Expand All @@ -920,18 +920,18 @@ int nrand_fast_f252() impure inline_ref {

;; random number uniformly distributed in [0..1)
;; fixed248 random();
int fixed248::random() impure inline {
int fixed248::random() inline {
return random() >> 8;
}

;; random number with standard normal distribution
;; fixed248 nrand();
int fixed248::nrand() impure inline {
int fixed248::nrand() inline {
return nrand_f252() ~>> 4;
}

;; generates a random number approximately distributed according to the standard normal distribution
;; fixed248 nrand_fast();
int fixed248::nrand_fast() impure inline {
int fixed248::nrand_fast() inline {
return nrand_fast_f252() ~>> 4;
}
939 changes: 496 additions & 443 deletions crypto/smartcont/stdlib.tolk

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion lite-client/lite-client.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -926,7 +926,7 @@ bool TestNode::show_help(std::string command) {
"saveaccount[code|data] <filename> <addr> [<block-id-ext>]\tSaves into specified file the most recent state "
"(StateInit) or just the code or data of specified account; <addr> is in "
"[<workchain>:]<hex-or-base64-addr> format\n"
"runmethod[full] <addr> [<block-id-ext>] <method-id> <params>...\tRuns GET method <method-id> of account "
"runmethod[full] <addr> [<block-id-ext>] <name> <params>...\tRuns GET method <name> of account "
"<addr> "
"with specified parameters\n"
"dnsresolve [<block-id-ext>] <domain> [<category>]\tResolves a domain starting from root dns smart contract\n"
Expand Down
4 changes: 4 additions & 0 deletions tolk/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,10 @@ target_link_libraries(tolk PUBLIC git ton_crypto) # todo replace with ton_crypt
if (WINGETOPT_FOUND)
target_link_libraries_system(tolk wingetopt)
endif ()
if (${TOLK_DEBUG}) # -DTOLK_DEBUG=1 in CMake options => #define TOLK_DEBUG (for development purposes)
message(STATUS "TOLK_DEBUG is ON")
target_compile_definitions(tolk PRIVATE TOLK_DEBUG=1)
endif()

if (USE_EMSCRIPTEN)
add_executable(tolkfiftlib tolk-wasm.cpp ${TOLK_SOURCE})
Expand Down
17 changes: 1 addition & 16 deletions tolk/abscode.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -221,15 +221,6 @@ void VarDescrList::show(std::ostream& os) const {
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);
Expand Down Expand Up @@ -294,7 +285,7 @@ void Op::show(std::ostream& os, const std::vector<TmpVar>& vars, std::string pfx
if (noreturn()) {
dis += "<noret> ";
}
if (!is_pure()) {
if (impure()) {
dis += "<impure> ";
}
switch (cl) {
Expand Down Expand Up @@ -467,12 +458,6 @@ void Op::show_block(std::ostream& os, const Op* block, const std::vector<TmpVar>
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;
Expand Down
49 changes: 40 additions & 9 deletions tolk/analyzer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -360,10 +360,10 @@ bool Op::compute_used_vars(const CodeBlob& code, bool edit) {
case _Tuple:
case _UnTuple: {
// left = EXEC right;
if (!next_var_info.count_used(left) && is_pure()) {
if (!next_var_info.count_used(left) && !impure()) {
// all variables in `left` are not needed
if (edit) {
disable();
set_disabled();
}
return std_compute_used_vars(true);
}
Expand All @@ -372,7 +372,7 @@ bool Op::compute_used_vars(const CodeBlob& code, bool edit) {
case _SetGlob: {
// GLOB = right
if (right.empty() && edit) {
disable();
set_disabled();
}
return std_compute_used_vars(right.empty());
}
Expand All @@ -399,7 +399,7 @@ bool Op::compute_used_vars(const CodeBlob& code, bool edit) {
}
if (!cnt && edit) {
// all variables in `left` are not needed
disable();
set_disabled();
}
return set_var_info(std::move(new_var_info));
}
Expand Down Expand Up @@ -860,15 +860,45 @@ VarDescrList Op::fwd_analyze(VarDescrList values) {
}
}

bool Op::set_noreturn(bool nr) {
if (nr) {
void Op::set_disabled(bool flag) {
if (flag) {
flags |= _Disabled;
} else {
flags &= ~_Disabled;
}
}


bool Op::set_noreturn(bool flag) {
if (flag) {
flags |= _NoReturn;
} else {
flags &= ~_NoReturn;
}
return nr;
return flag;
}

void Op::set_impure(const CodeBlob &code) {
// todo calling this function with `code` is a bad design (flags are assigned after Op is constructed)
// later it's better to check this somewhere in code.emplace_back()
if (code.flags & CodeBlob::_ForbidImpure) {
throw ParseError(where, "An impure operation in a pure function");
}
flags |= _Impure;
}

void Op::set_impure(const CodeBlob &code, bool flag) {
if (flag) {
if (code.flags & CodeBlob::_ForbidImpure) {
throw ParseError(where, "An impure operation in a pure function");
}
flags |= _Impure;
} else {
flags &= ~_Impure;
}
}


bool Op::mark_noreturn() {
switch (cl) {
case _Nop:
Expand All @@ -888,13 +918,14 @@ bool Op::mark_noreturn() {
case _Call:
return set_noreturn(next->mark_noreturn());
case _Return:
return set_noreturn(true);
return set_noreturn();
case _If:
case _TryCatch:
// note, that & | (not && ||) here and below is mandatory to invoke both left and right calls
return set_noreturn((static_cast<int>(block0->mark_noreturn()) & static_cast<int>(block1 && block1->mark_noreturn())) | static_cast<int>(next->mark_noreturn()));
case _Again:
block0->mark_noreturn();
return set_noreturn(true);
return set_noreturn();
case _Until:
return set_noreturn(static_cast<int>(block0->mark_noreturn()) | static_cast<int>(next->mark_noreturn()));
case _While:
Expand Down
3 changes: 3 additions & 0 deletions tolk/asmops.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -317,6 +317,9 @@ void AsmOpList::show_var_ext(std::ostream& os, std::pair<var_idx_t, const_idx_t>
os << '_' << i;
} else {
var_names_->at(i).show(os, 2);
// if (!var_names_->at(i).v_type->is_int()) {
// os << '<'; var_names_->at(i).v_type->print(os); os << '>';
// }
}
if ((unsigned)j < constants_.size() && constants_[j].not_null()) {
os << '=' << constants_[j];
Expand Down
52 changes: 31 additions & 21 deletions tolk/builtins.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,10 @@ using namespace std::literals::string_literals;
*/

int glob_func_cnt, undef_func_cnt, glob_var_cnt, const_cnt;
std::vector<SymDef*> glob_func, glob_vars;
std::vector<SymDef*> glob_func, glob_vars, glob_get_methods;
std::set<std::string> prohibited_var_names;

SymDef* predefine_builtin_func(std::string name, TypeExpr* func_type) {
SymDef* define_builtin_func_impl(const std::string& name, SymValAsmFunc* func_val) {
if (name.back() == '_') {
prohibited_var_names.insert(name);
}
Expand All @@ -42,30 +42,40 @@ SymDef* predefine_builtin_func(std::string name, TypeExpr* func_type) {
std::cerr << "fatal: global function `" << name << "` already defined" << std::endl;
std::exit(1);
}
func_val->flags |= SymValFunc::flagBuiltinFunction;
def->value = func_val;
#ifdef TOLK_DEBUG
dynamic_cast<SymValAsmFunc*>(def->value)->name = name;
#endif
return def;
}

template <typename T>
SymDef* define_builtin_func(std::string name, TypeExpr* func_type, const T& func, bool impure = false) {
SymDef* def = predefine_builtin_func(name, func_type);
def->value = new SymValAsmFunc{func_type, func, impure};
return def;
SymDef* define_builtin_func(const std::string& name, TypeExpr* func_type, const simple_compile_func_t& func, bool impure = false) {
return define_builtin_func_impl(name, new SymValAsmFunc{func_type, func, !impure});
}

SymDef* define_builtin_func(const std::string& name, TypeExpr* func_type, const compile_func_t& func, bool impure = false) {
return define_builtin_func_impl(name, new SymValAsmFunc{func_type, func, !impure});
}

SymDef* define_builtin_func(const std::string& name, TypeExpr* func_type, const AsmOp& macro, bool impure = false) {
return define_builtin_func_impl(name, new SymValAsmFunc{func_type, make_simple_compile(macro), !impure});
}

template <typename T>
SymDef* define_builtin_func(std::string name, TypeExpr* func_type, const T& func, std::initializer_list<int> arg_order,
SymDef* define_builtin_func(const std::string& name, TypeExpr* func_type, const simple_compile_func_t& func, std::initializer_list<int> arg_order,
std::initializer_list<int> ret_order = {}, bool impure = false) {
SymDef* def = predefine_builtin_func(name, func_type);
def->value = new SymValAsmFunc{func_type, func, arg_order, ret_order, impure};
return def;
return define_builtin_func_impl(name, new SymValAsmFunc{func_type, func, arg_order, ret_order, !impure});
}

SymDef* define_builtin_func(std::string name, TypeExpr* func_type, const AsmOp& macro,
SymDef* define_builtin_func(const std::string& name, TypeExpr* func_type, const compile_func_t& func, std::initializer_list<int> arg_order,
std::initializer_list<int> ret_order = {}, bool impure = false) {
return define_builtin_func_impl(name, new SymValAsmFunc{func_type, func, arg_order, ret_order, !impure});
}

SymDef* define_builtin_func(const std::string& name, TypeExpr* func_type, const AsmOp& macro,
std::initializer_list<int> arg_order, std::initializer_list<int> ret_order = {},
bool impure = false) {
SymDef* def = predefine_builtin_func(name, func_type);
def->value = new SymValAsmFunc{func_type, make_simple_compile(macro), arg_order, ret_order, impure};
return def;
return define_builtin_func_impl(name, new SymValAsmFunc{func_type, make_simple_compile(macro), arg_order, ret_order, !impure});
}

SymDef* force_autoapply(SymDef* def) {
Expand Down Expand Up @@ -262,7 +272,7 @@ int emulate_lshift(int a, int b) {
}
int t = ((b & VarDescr::_NonZero) ? VarDescr::_Even : 0);
t |= b & VarDescr::_Finite;
return emulate_mul(a, VarDescr::_Int | VarDescr::_Pos | VarDescr::_NonZero | VarDescr::_Even | t);
return emulate_mul(a, VarDescr::_Int | VarDescr::_Pos | VarDescr::_NonZero | t);
}

int emulate_div(int a, int b) {
Expand Down Expand Up @@ -308,7 +318,7 @@ int emulate_rshift(int a, int b) {
}
int t = ((b & VarDescr::_NonZero) ? VarDescr::_Even : 0);
t |= b & VarDescr::_Finite;
return emulate_div(a, VarDescr::_Int | VarDescr::_Pos | VarDescr::_NonZero | VarDescr::_Even | t);
return emulate_div(a, VarDescr::_Int | VarDescr::_Pos | VarDescr::_NonZero | t);
}

int emulate_mod(int a, int b, int round_mode = -1) {
Expand Down Expand Up @@ -1128,9 +1138,9 @@ void define_builtins() {
auto Int3 = TypeExpr::new_tensor({Int, Int, Int});
auto TupleInt = TypeExpr::new_tensor({Tuple, Int});
auto SliceInt = TypeExpr::new_tensor({Slice, Int});
auto X = TypeExpr::new_var();
auto Y = TypeExpr::new_var();
auto Z = TypeExpr::new_var();
auto X = TypeExpr::new_var(0);
auto Y = TypeExpr::new_var(1);
auto Z = TypeExpr::new_var(2);
auto XY = TypeExpr::new_tensor({X, Y});
auto arith_bin_op = TypeExpr::new_map(Int2, Int);
auto arith_un_op = TypeExpr::new_map(Int, Int);
Expand Down
36 changes: 17 additions & 19 deletions tolk/codegen.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -437,6 +437,7 @@ bool Op::generate_code_step(Stack& stack) {
if (disabled()) {
return true;
}
// fun_ref can be nullptr for Op::_CallInd (invoke a variable, not a function)
SymValFunc* func = (fun_ref ? dynamic_cast<SymValFunc*>(fun_ref->value) : nullptr);
auto arg_order = (func ? func->get_arg_order() : nullptr);
auto ret_order = (func ? func->get_ret_order() : nullptr);
Expand Down Expand Up @@ -486,27 +487,24 @@ bool Op::generate_code_step(Stack& stack) {
};
if (cl == _CallInd) {
exec_callxargs((int)right.size() - 1, (int)left.size());
} else if (auto asm_fv = dynamic_cast<const SymValAsmFunc*>(fun_ref->value)) {
std::vector<VarDescr> res;
res.reserve(left.size());
for (var_idx_t i : left) {
res.emplace_back(i);
}
asm_fv->compile(stack.o, res, args, where); // compile res := f (args)
} else {
auto func = dynamic_cast<const SymValAsmFunc*>(fun_ref->value);
if (func) {
std::vector<VarDescr> res;
res.reserve(left.size());
for (var_idx_t i : left) {
res.emplace_back(i);
}
func->compile(stack.o, res, args, where); // compile res := f (args)
auto fv = dynamic_cast<const SymValCodeFunc*>(fun_ref->value);
// todo can be fv == nullptr?
std::string name = symbols.get_name(fun_ref->sym_idx);
if (fv && (fv->is_inline() || fv->is_inline_ref())) {
stack.o << AsmOp::Custom(name + " INLINECALLDICT", (int)right.size(), (int)left.size());
} else if (fv && fv->code && fv->code->require_callxargs) {
stack.o << AsmOp::Custom(name + (" PREPAREDICT"), 0, 2);
exec_callxargs((int)right.size() + 1, (int)left.size());
} else {
auto fv = dynamic_cast<const SymValCodeFunc*>(fun_ref->value);
std::string name = symbols.get_name(fun_ref->sym_idx);
bool is_inline = (fv && (fv->flags & 3));
if (is_inline) {
stack.o << AsmOp::Custom(name + " INLINECALLDICT", (int)right.size(), (int)left.size());
} else if (fv && fv->code && fv->code->require_callxargs) {
stack.o << AsmOp::Custom(name + (" PREPAREDICT"), 0, 2);
exec_callxargs((int)right.size() + 1, (int)left.size());
} else {
stack.o << AsmOp::Custom(name + " CALLDICT", (int)right.size(), (int)left.size());
}
stack.o << AsmOp::Custom(name + " CALLDICT", (int)right.size(), (int)left.size());
}
}
stack.s.resize(k);
Expand Down
Loading

0 comments on commit ebbab54

Please sign in to comment.