mirror of
https://github.com/ton-blockchain/ton
synced 2025-03-09 15:40:10 +00:00
updated func/fift
- updated func/fift - updated liteclient/liteserver - bugfixes
This commit is contained in:
parent
d41ce55305
commit
acf16718e6
45 changed files with 1360 additions and 185 deletions
|
@ -343,6 +343,12 @@ void Op::show(std::ostream& os, const std::vector<TmpVar>& vars, std::string pfx
|
|||
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);
|
||||
|
|
|
@ -365,6 +365,13 @@ bool Op::compute_used_vars(const CodeBlob& code, bool edit) {
|
|||
}
|
||||
return std_compute_used_vars();
|
||||
}
|
||||
case _SetGlob: {
|
||||
// GLOB = right
|
||||
if (right.empty() && edit) {
|
||||
disable();
|
||||
}
|
||||
return std_compute_used_vars(right.empty());
|
||||
}
|
||||
case _Let: {
|
||||
// left = right
|
||||
std::size_t cnt = next_var_info.count_used(left);
|
||||
|
@ -531,6 +538,7 @@ bool prune_unreachable(std::unique_ptr<Op>& ops) {
|
|||
switch (op.cl) {
|
||||
case Op::_IntConst:
|
||||
case Op::_GlobVar:
|
||||
case Op::_SetGlob:
|
||||
case Op::_Call:
|
||||
case Op::_CallInd:
|
||||
case Op::_Import:
|
||||
|
@ -694,7 +702,6 @@ VarDescrList Op::fwd_analyze(VarDescrList values) {
|
|||
values.add_newval(left[0]).set_const(int_const);
|
||||
break;
|
||||
}
|
||||
case _GlobVar:
|
||||
case _Call: {
|
||||
prepare_args(values);
|
||||
auto func = dynamic_cast<const SymValAsmFunc*>(fun_ref->value);
|
||||
|
@ -717,12 +724,15 @@ VarDescrList Op::fwd_analyze(VarDescrList values) {
|
|||
}
|
||||
break;
|
||||
}
|
||||
case _GlobVar:
|
||||
case _CallInd: {
|
||||
for (var_idx_t i : left) {
|
||||
values.add_newval(i);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case _SetGlob:
|
||||
break;
|
||||
case _Let: {
|
||||
std::vector<VarDescr> old_val;
|
||||
assert(left.size() == right.size());
|
||||
|
@ -832,6 +842,7 @@ bool Op::mark_noreturn() {
|
|||
case _Import:
|
||||
case _IntConst:
|
||||
case _Let:
|
||||
case _SetGlob:
|
||||
case _GlobVar:
|
||||
case _CallInd:
|
||||
case _Call:
|
||||
|
|
|
@ -27,8 +27,8 @@ using namespace std::literals::string_literals;
|
|||
*
|
||||
*/
|
||||
|
||||
int glob_func_cnt, undef_func_cnt;
|
||||
std::vector<SymDef*> glob_func;
|
||||
int glob_func_cnt, undef_func_cnt, glob_var_cnt;
|
||||
std::vector<SymDef*> glob_func, glob_vars;
|
||||
|
||||
SymDef* predefine_builtin_func(std::string name, TypeExpr* func_type) {
|
||||
sym_idx_t name_idx = sym::symbols.lookup(name, 1);
|
||||
|
@ -44,30 +44,49 @@ SymDef* predefine_builtin_func(std::string name, TypeExpr* func_type) {
|
|||
}
|
||||
|
||||
template <typename T>
|
||||
void define_builtin_func(std::string name, TypeExpr* func_type, const T& func, bool impure = false) {
|
||||
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;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void define_builtin_func(std::string name, TypeExpr* func_type, const T& func, std::initializer_list<int> arg_order,
|
||||
std::initializer_list<int> ret_order = {}, bool impure = false) {
|
||||
SymDef* define_builtin_func(std::string name, TypeExpr* func_type, const 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;
|
||||
}
|
||||
|
||||
void define_builtin_func(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* define_builtin_func(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;
|
||||
}
|
||||
|
||||
bool SymValAsmFunc::compile(AsmOpList& dest, std::vector<VarDescr>& in, std::vector<VarDescr>& out) const {
|
||||
SymDef* force_autoapply(SymDef* def) {
|
||||
if (def) {
|
||||
auto val = dynamic_cast<SymVal*>(def->value);
|
||||
if (val) {
|
||||
val->auto_apply = true;
|
||||
}
|
||||
}
|
||||
return def;
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
SymDef* define_builtin_const(std::string name, TypeExpr* const_type, Args&&... args) {
|
||||
return force_autoapply(
|
||||
define_builtin_func(name, TypeExpr::new_map(TypeExpr::new_unit(), const_type), std::forward<Args>(args)...));
|
||||
}
|
||||
|
||||
bool SymValAsmFunc::compile(AsmOpList& dest, std::vector<VarDescr>& out, std::vector<VarDescr>& in) const {
|
||||
if (simple_compile) {
|
||||
return dest.append(simple_compile(in, out));
|
||||
return dest.append(simple_compile(out, in));
|
||||
} else if (ext_compile) {
|
||||
return ext_compile(dest, in, out);
|
||||
return ext_compile(dest, out, in);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
@ -317,6 +336,12 @@ AsmOp exec_arg_op(std::string op, td::RefInt256 arg, int args, int retv) {
|
|||
return AsmOp::Custom(os.str(), args, retv);
|
||||
}
|
||||
|
||||
AsmOp exec_arg2_op(std::string op, long long imm1, long long imm2, int args, int retv) {
|
||||
std::ostringstream os;
|
||||
os << imm1 << ' ' << imm2 << ' ' << op;
|
||||
return AsmOp::Custom(os.str(), args, retv);
|
||||
}
|
||||
|
||||
AsmOp push_const(td::RefInt256 x) {
|
||||
return AsmOp::IntConst(std::move(x));
|
||||
}
|
||||
|
@ -918,11 +943,11 @@ void define_builtins() {
|
|||
define_builtin_func("_<=_", arith_bin_op, std::bind(compile_cmp_int, _1, _2, 6));
|
||||
define_builtin_func("_>=_", arith_bin_op, std::bind(compile_cmp_int, _1, _2, 3));
|
||||
define_builtin_func("_<=>_", arith_bin_op, std::bind(compile_cmp_int, _1, _2, 7));
|
||||
define_builtin_func("true", Int, /* AsmOp::Const("TRUE") */ std::bind(compile_bool_const, _1, _2, true));
|
||||
define_builtin_func("false", Int, /* AsmOp::Const("FALSE") */ std::bind(compile_bool_const, _1, _2, false));
|
||||
define_builtin_const("true", Int, /* AsmOp::Const("TRUE") */ std::bind(compile_bool_const, _1, _2, true));
|
||||
define_builtin_const("false", Int, /* AsmOp::Const("FALSE") */ std::bind(compile_bool_const, _1, _2, false));
|
||||
// define_builtin_func("null", Null, AsmOp::Const("PUSHNULL"));
|
||||
define_builtin_func("nil", Tuple, AsmOp::Const("PUSHNULL"));
|
||||
define_builtin_func("Nil", Tuple, AsmOp::Const("NIL"));
|
||||
define_builtin_const("nil", Tuple, AsmOp::Const("PUSHNULL"));
|
||||
define_builtin_const("Nil", Tuple, AsmOp::Const("NIL"));
|
||||
define_builtin_func("null?", TypeExpr::new_forall({X}, TypeExpr::new_map(X, Int)), compile_is_null);
|
||||
define_builtin_func("throw", impure_un_op, compile_throw, true);
|
||||
define_builtin_func("throw_if", impure_bin_op, std::bind(compile_cond_throw, _1, _2, true), true);
|
||||
|
|
|
@ -84,7 +84,9 @@ void Stack::forget_const() {
|
|||
|
||||
void Stack::issue_pop(int i) {
|
||||
validate(i);
|
||||
o << AsmOp::Pop(i);
|
||||
if (output_enabled()) {
|
||||
o << AsmOp::Pop(i);
|
||||
}
|
||||
at(i) = get(0);
|
||||
s.pop_back();
|
||||
modified();
|
||||
|
@ -92,7 +94,9 @@ void Stack::issue_pop(int i) {
|
|||
|
||||
void Stack::issue_push(int i) {
|
||||
validate(i);
|
||||
o << AsmOp::Push(i);
|
||||
if (output_enabled()) {
|
||||
o << AsmOp::Push(i);
|
||||
}
|
||||
s.push_back(get(i));
|
||||
modified();
|
||||
}
|
||||
|
@ -101,7 +105,9 @@ void Stack::issue_xchg(int i, int j) {
|
|||
validate(i);
|
||||
validate(j);
|
||||
if (i != j && get(i) != get(j)) {
|
||||
o << AsmOp::Xchg(i, j);
|
||||
if (output_enabled()) {
|
||||
o << AsmOp::Xchg(i, j);
|
||||
}
|
||||
std::swap(at(i), at(j));
|
||||
modified();
|
||||
}
|
||||
|
@ -183,6 +189,10 @@ void Stack::enforce_state(const StackLayout& req_stack) {
|
|||
if (i < depth() && s[i].first == x) {
|
||||
continue;
|
||||
}
|
||||
while (depth() > 0 && std::find(req_stack.cbegin(), req_stack.cend(), get(0).first) == req_stack.cend()) {
|
||||
// current TOS entry is unused in req_stack, drop it
|
||||
issue_pop(0);
|
||||
}
|
||||
int j = find(x);
|
||||
if (j >= depth() - i) {
|
||||
issue_push(j);
|
||||
|
@ -292,27 +302,61 @@ bool Op::generate_code_step(Stack& stack) {
|
|||
}
|
||||
return true;
|
||||
}
|
||||
case _GlobVar: {
|
||||
assert(left.size() == 1);
|
||||
auto p = next_var_info[left[0]];
|
||||
if (!p || p->is_unused() || disabled()) {
|
||||
case _GlobVar:
|
||||
if (dynamic_cast<const SymValGlobVar*>(fun_ref->value)) {
|
||||
bool used = false;
|
||||
for (auto i : left) {
|
||||
auto p = next_var_info[i];
|
||||
if (p && !p->is_unused()) {
|
||||
used = true;
|
||||
}
|
||||
}
|
||||
if (!used || disabled()) {
|
||||
return true;
|
||||
}
|
||||
std::string name = sym::symbols.get_name(fun_ref->sym_idx);
|
||||
stack.o << AsmOp::Custom(name + " GETGLOB", 0, 1);
|
||||
if (left.size() != 1) {
|
||||
assert(left.size() <= 15);
|
||||
stack.o << exec_arg_op("UNTUPLE", (int)left.size(), 1, (int)left.size());
|
||||
}
|
||||
for (auto i : left) {
|
||||
stack.push_new_var(i);
|
||||
}
|
||||
return true;
|
||||
} else {
|
||||
assert(left.size() == 1);
|
||||
auto p = next_var_info[left[0]];
|
||||
if (!p || p->is_unused() || disabled()) {
|
||||
return true;
|
||||
}
|
||||
stack.o << "CONT:<{";
|
||||
stack.o.indent();
|
||||
auto func = dynamic_cast<SymValAsmFunc*>(fun_ref->value);
|
||||
if (func) {
|
||||
// TODO: create and compile a true lambda instead of this (so that arg_order and ret_order would work correctly)
|
||||
std::vector<VarDescr> args0, res;
|
||||
TypeExpr::remove_indirect(func->sym_type);
|
||||
assert(func->get_type()->is_map());
|
||||
auto wr = func->get_type()->args.at(0)->get_width();
|
||||
auto wl = func->get_type()->args.at(1)->get_width();
|
||||
assert(wl >= 0 && wr >= 0);
|
||||
for (int i = 0; i < wl; i++) {
|
||||
res.emplace_back(0);
|
||||
}
|
||||
for (int i = 0; i < wr; i++) {
|
||||
args0.emplace_back(0);
|
||||
}
|
||||
func->compile(stack.o, res, args0); // compile res := f (args0)
|
||||
} else {
|
||||
std::string name = sym::symbols.get_name(fun_ref->sym_idx);
|
||||
stack.o << AsmOp::Custom(name + " CALLDICT", (int)right.size(), (int)left.size());
|
||||
}
|
||||
stack.o.undent();
|
||||
stack.o << "}>";
|
||||
stack.push_new_var(left.at(0));
|
||||
return true;
|
||||
}
|
||||
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); // compile res := f (args)
|
||||
} else {
|
||||
std::string name = sym::symbols.get_name(fun_ref->sym_idx);
|
||||
stack.o << AsmOp::Custom(name + " CALLDICT", (int)right.size(), (int)left.size());
|
||||
}
|
||||
stack.push_new_var(left[0]);
|
||||
return true;
|
||||
}
|
||||
case _Let: {
|
||||
assert(left.size() == right.size());
|
||||
int i = 0;
|
||||
|
@ -395,7 +439,9 @@ bool Op::generate_code_step(Stack& stack) {
|
|||
assert(stack.s[k + i].first == right1[i]);
|
||||
}
|
||||
if (cl == _CallInd) {
|
||||
stack.o << exec_arg_op("CALLARGS", (int)right.size() - 1, (int)right.size(), (int)left.size());
|
||||
// TODO: replace with exec_arg2_op()
|
||||
stack.o << exec_arg2_op("CALLXARGS", (int)right.size() - 1, (int)left.size(), (int)right.size(),
|
||||
(int)left.size());
|
||||
} else {
|
||||
auto func = dynamic_cast<const SymValAsmFunc*>(fun_ref->value);
|
||||
if (func) {
|
||||
|
@ -420,6 +466,32 @@ bool Op::generate_code_step(Stack& stack) {
|
|||
}
|
||||
return true;
|
||||
}
|
||||
case _SetGlob: {
|
||||
assert(fun_ref && dynamic_cast<const SymValGlobVar*>(fun_ref->value));
|
||||
std::vector<bool> last;
|
||||
for (var_idx_t x : right) {
|
||||
last.push_back(var_info[x] && var_info[x]->is_last());
|
||||
}
|
||||
stack.rearrange_top(right, std::move(last));
|
||||
stack.opt_show();
|
||||
int k = (int)stack.depth() - (int)right.size();
|
||||
assert(k >= 0);
|
||||
for (int i = 0; i < (int)right.size(); i++) {
|
||||
if (stack.s[k + i].first != right[i]) {
|
||||
std::cerr << stack.o;
|
||||
}
|
||||
assert(stack.s[k + i].first == right[i]);
|
||||
}
|
||||
if (right.size() > 1) {
|
||||
stack.o << exec_arg_op("TUPLE", (int)right.size(), (int)right.size(), 1);
|
||||
}
|
||||
if (!right.empty()) {
|
||||
std::string name = sym::symbols.get_name(fun_ref->sym_idx);
|
||||
stack.o << AsmOp::Custom(name + " SETGLOB", 1, 0);
|
||||
}
|
||||
stack.s.resize(k);
|
||||
return true;
|
||||
}
|
||||
case _If: {
|
||||
if (block0->is_empty() && block1->is_empty()) {
|
||||
return true;
|
||||
|
@ -448,7 +520,9 @@ bool Op::generate_code_step(Stack& stack) {
|
|||
}
|
||||
stack.o << "IF:<{";
|
||||
stack.o.indent();
|
||||
Stack stack_copy{stack};
|
||||
Stack stack_copy{stack}, stack_target{stack};
|
||||
stack_target.disable_output();
|
||||
stack_target.drop_vars_except(next->var_info);
|
||||
block0->generate_code_all(stack_copy);
|
||||
stack_copy.drop_vars_except(var_info);
|
||||
stack_copy.opt_show();
|
||||
|
@ -457,7 +531,8 @@ bool Op::generate_code_step(Stack& stack) {
|
|||
stack.o << "}>";
|
||||
return true;
|
||||
}
|
||||
stack_copy.drop_vars_except(next->var_info);
|
||||
// stack_copy.drop_vars_except(next->var_info);
|
||||
stack_copy.enforce_state(stack_target.vars());
|
||||
stack_copy.opt_show();
|
||||
if (stack_copy.vars() == stack.vars()) {
|
||||
stack.o.undent();
|
||||
|
@ -487,7 +562,9 @@ bool Op::generate_code_step(Stack& stack) {
|
|||
}
|
||||
stack.o << "IFNOT:<{";
|
||||
stack.o.indent();
|
||||
Stack stack_copy{stack};
|
||||
Stack stack_copy{stack}, stack_target{stack};
|
||||
stack_target.disable_output();
|
||||
stack_target.drop_vars_except(next->var_info);
|
||||
block1->generate_code_all(stack_copy);
|
||||
stack_copy.drop_vars_except(var_info);
|
||||
stack_copy.opt_show();
|
||||
|
@ -497,7 +574,8 @@ bool Op::generate_code_step(Stack& stack) {
|
|||
stack.merge_const(stack_copy);
|
||||
return true;
|
||||
}
|
||||
stack_copy.drop_vars_except(next->var_info);
|
||||
// stack_copy.drop_vars_except(next->var_info);
|
||||
stack_copy.enforce_state(stack_target.vars());
|
||||
stack_copy.opt_show();
|
||||
if (stack_copy.vars() == stack.vars()) {
|
||||
stack.o.undent();
|
||||
|
|
|
@ -130,6 +130,11 @@ int generate_output() {
|
|||
*outs << func_val->method_id << " DECLMETHOD " << name << "\n";
|
||||
}
|
||||
}
|
||||
for (SymDef* gvar_sym : glob_vars) {
|
||||
assert(dynamic_cast<SymValGlobVar*>(gvar_sym->value));
|
||||
std::string name = sym::symbols.get_name(gvar_sym->sym_idx);
|
||||
*outs << std::string(indent * 2, ' ') << "DECLGLOBVAR " << name << "\n";
|
||||
}
|
||||
int errors = 0;
|
||||
for (SymDef* func_sym : glob_func) {
|
||||
try {
|
||||
|
|
|
@ -97,6 +97,7 @@ enum Keyword {
|
|||
_Forall,
|
||||
_Asm,
|
||||
_Impure,
|
||||
_Global,
|
||||
_Extern,
|
||||
_Inline,
|
||||
_InlineRef,
|
||||
|
@ -181,6 +182,9 @@ struct TypeExpr {
|
|||
bool is_var() const {
|
||||
return constr == te_Var;
|
||||
}
|
||||
bool is_map() const {
|
||||
return constr == te_Map;
|
||||
}
|
||||
bool has_fixed_width() const {
|
||||
return minw == maxw;
|
||||
}
|
||||
|
@ -498,6 +502,7 @@ struct Op {
|
|||
_Let,
|
||||
_IntConst,
|
||||
_GlobVar,
|
||||
_SetGlob,
|
||||
_Import,
|
||||
_Return,
|
||||
_If,
|
||||
|
@ -694,6 +699,7 @@ struct SymVal : sym::SymValBase {
|
|||
TypeExpr* sym_type;
|
||||
td::RefInt256 method_id;
|
||||
bool impure;
|
||||
bool auto_apply{false};
|
||||
short flags; // +1 = inline, +2 = inline_ref
|
||||
SymVal(int _type, int _idx, TypeExpr* _stype = nullptr, bool _impure = false)
|
||||
: sym::SymValBase(_type, _idx), sym_type(_stype), impure(_impure), flags(0) {
|
||||
|
@ -745,8 +751,20 @@ struct SymValType : sym::SymValBase {
|
|||
}
|
||||
};
|
||||
|
||||
extern int glob_func_cnt, undef_func_cnt;
|
||||
extern std::vector<SymDef*> glob_func;
|
||||
struct SymValGlobVar : sym::SymValBase {
|
||||
TypeExpr* sym_type;
|
||||
int out_idx{0};
|
||||
SymValGlobVar(int val, TypeExpr* gvtype, int oidx = 0)
|
||||
: sym::SymValBase(_GlobVar, val), sym_type(gvtype), out_idx(oidx) {
|
||||
}
|
||||
~SymValGlobVar() override = default;
|
||||
TypeExpr* get_type() const {
|
||||
return sym_type;
|
||||
}
|
||||
};
|
||||
|
||||
extern int glob_func_cnt, undef_func_cnt, glob_var_cnt;
|
||||
extern std::vector<SymDef*> glob_func, glob_vars;
|
||||
|
||||
/*
|
||||
*
|
||||
|
@ -775,6 +793,7 @@ struct Expr {
|
|||
_Const,
|
||||
_Var,
|
||||
_Glob,
|
||||
_GlobVar,
|
||||
_Letop,
|
||||
_LetFirst,
|
||||
_Hole,
|
||||
|
@ -1155,6 +1174,7 @@ struct StackTransform {
|
|||
bool apply_push(int i);
|
||||
bool apply_pop(int i = 0);
|
||||
bool apply_push_newconst();
|
||||
bool apply_blkpop(int k);
|
||||
bool apply(const StackTransform& other); // this = this * other
|
||||
bool preapply(const StackTransform& other); // this = other * this
|
||||
// c := a * b
|
||||
|
@ -1246,6 +1266,11 @@ struct StackTransform {
|
|||
bool is_nip_seq(int i, int j = 0) const;
|
||||
bool is_nip_seq(int* i) const;
|
||||
bool is_nip_seq(int* i, int* j) const;
|
||||
bool is_pop_blkdrop(int i, int k) const;
|
||||
bool is_pop_blkdrop(int* i, int* k) const;
|
||||
bool is_2pop_blkdrop(int i, int j, int k) const;
|
||||
bool is_2pop_blkdrop(int* i, int* j, int* k) const;
|
||||
bool is_const_rot() const;
|
||||
|
||||
void show(std::ostream& os, int mode = 0) const;
|
||||
|
||||
|
@ -1306,14 +1331,20 @@ struct Optimizer {
|
|||
bool rewrite_const_push_swap();
|
||||
bool is_const_push_xchgs();
|
||||
bool rewrite_const_push_xchgs();
|
||||
bool is_const_rot() const;
|
||||
bool rewrite_const_rot();
|
||||
bool simple_rewrite(int p, AsmOp&& new_op);
|
||||
bool simple_rewrite(int p, AsmOp&& new_op1, AsmOp&& new_op2);
|
||||
bool simple_rewrite(int p, AsmOp&& new_op1, AsmOp&& new_op2, AsmOp&& new_op3);
|
||||
bool simple_rewrite(AsmOp&& new_op) {
|
||||
return simple_rewrite(p_, std::move(new_op));
|
||||
}
|
||||
bool simple_rewrite(AsmOp&& new_op1, AsmOp&& new_op2) {
|
||||
return simple_rewrite(p_, std::move(new_op1), std::move(new_op2));
|
||||
}
|
||||
bool simple_rewrite(AsmOp&& new_op1, AsmOp&& new_op2, AsmOp&& new_op3) {
|
||||
return simple_rewrite(p_, std::move(new_op1), std::move(new_op2), std::move(new_op3));
|
||||
}
|
||||
bool simple_rewrite_nop();
|
||||
bool is_pred(const std::function<bool(const StackTransform&)>& pred, int min_p = 2);
|
||||
bool is_same_as(const StackTransform& trans, int min_p = 2);
|
||||
|
@ -1345,6 +1376,8 @@ struct Optimizer {
|
|||
bool is_blkdrop(int* i);
|
||||
bool is_reverse(int* i, int* j);
|
||||
bool is_nip_seq(int* i, int* j);
|
||||
bool is_pop_blkdrop(int* i, int* k);
|
||||
bool is_2pop_blkdrop(int* i, int* j, int* k);
|
||||
AsmOpConsList extract_code();
|
||||
};
|
||||
|
||||
|
@ -1355,7 +1388,7 @@ void optimize_code(AsmOpList& ops);
|
|||
struct Stack {
|
||||
StackLayoutExt s;
|
||||
AsmOpList& o;
|
||||
enum { _StkCmt = 1, _CptStkCmt = 2, _DisableOpt = 4, _Shown = 256, _Garbage = -0x10000 };
|
||||
enum { _StkCmt = 1, _CptStkCmt = 2, _DisableOpt = 4, _DisableOut = 128, _Shown = 256, _Garbage = -0x10000 };
|
||||
int mode;
|
||||
Stack(AsmOpList& _o, int _mode = 0) : o(_o), mode(_mode) {
|
||||
}
|
||||
|
@ -1381,6 +1414,15 @@ struct Stack {
|
|||
var_const_idx_t get(int i) const {
|
||||
return at(i);
|
||||
}
|
||||
bool output_disabled() const {
|
||||
return mode & _DisableOut;
|
||||
}
|
||||
bool output_enabled() const {
|
||||
return !output_disabled();
|
||||
}
|
||||
void disable_output() {
|
||||
mode |= _DisableOut;
|
||||
}
|
||||
StackLayout vars() const;
|
||||
int find(var_idx_t var, int from = 0) const;
|
||||
int find(var_idx_t var, int from, int to) const;
|
||||
|
@ -1470,7 +1512,7 @@ struct SymValAsmFunc : SymValFunc {
|
|||
std::initializer_list<int> ret_order = {}, bool impure = false)
|
||||
: SymValFunc(-1, ft, arg_order, ret_order, impure), ext_compile(std::move(_compile)) {
|
||||
}
|
||||
bool compile(AsmOpList& dest, std::vector<VarDescr>& in, std::vector<VarDescr>& out) const;
|
||||
bool compile(AsmOpList& dest, std::vector<VarDescr>& out, std::vector<VarDescr>& in) const;
|
||||
};
|
||||
|
||||
// defined in builtins.cpp
|
||||
|
@ -1478,6 +1520,7 @@ AsmOp exec_arg_op(std::string op, long long arg);
|
|||
AsmOp exec_arg_op(std::string op, long long arg, int args, int retv = 1);
|
||||
AsmOp exec_arg_op(std::string op, td::RefInt256 arg);
|
||||
AsmOp exec_arg_op(std::string op, td::RefInt256 arg, int args, int retv = 1);
|
||||
AsmOp exec_arg2_op(std::string op, long long imm1, long long imm2, int args, int retv = 1);
|
||||
AsmOp push_const(td::RefInt256 x);
|
||||
|
||||
void define_builtins();
|
||||
|
|
|
@ -282,15 +282,22 @@ std::vector<var_idx_t> Expr::pre_compile(CodeBlob& code) const {
|
|||
code.emplace_back(here, Op::_IntConst, rvect, intval);
|
||||
return rvect;
|
||||
}
|
||||
case _Glob: {
|
||||
case _Glob:
|
||||
case _GlobVar: {
|
||||
auto rvect = new_tmp_vect(code);
|
||||
code.emplace_back(here, Op::_GlobVar, rvect, std::vector<var_idx_t>{}, sym);
|
||||
return rvect;
|
||||
}
|
||||
case _Letop: {
|
||||
auto right = args[1]->pre_compile(code);
|
||||
auto left = args[0]->pre_compile(code);
|
||||
code.emplace_back(here, Op::_Let, std::move(left), right);
|
||||
if (args[0]->cls == Expr::_GlobVar) {
|
||||
assert(args[0]->sym);
|
||||
auto& op = code.emplace_back(here, Op::_SetGlob, std::vector<var_idx_t>{}, right, args[0]->sym);
|
||||
op.flags |= Op::_Impure;
|
||||
} else {
|
||||
auto left = args[0]->pre_compile(code);
|
||||
code.emplace_back(here, Op::_Let, std::move(left), right);
|
||||
}
|
||||
return right;
|
||||
}
|
||||
case _LetFirst: {
|
||||
|
|
|
@ -113,6 +113,7 @@ void define_keywords() {
|
|||
.add_keyword("forall", Kw::_Forall);
|
||||
|
||||
sym::symbols.add_keyword("extern", Kw::_Extern)
|
||||
.add_keyword("global", Kw::_Global)
|
||||
.add_keyword("asm", Kw::_Asm)
|
||||
.add_keyword("impure", Kw::_Impure)
|
||||
.add_keyword("inline", Kw::_Inline)
|
||||
|
|
|
@ -150,6 +150,21 @@ bool Optimizer::rewrite_const_push_swap() {
|
|||
return true;
|
||||
}
|
||||
|
||||
bool Optimizer::is_const_rot() const {
|
||||
return pb_ >= 3 && pb_ <= l2_ && op_[0]->is_gconst() && tr_[pb_ - 1].is_const_rot();
|
||||
}
|
||||
|
||||
bool Optimizer::rewrite_const_rot() {
|
||||
p_ = pb_;
|
||||
q_ = 2;
|
||||
show_left();
|
||||
oq_[0] = std::move(op_[0]);
|
||||
oq_[1] = std::move(op_[1]);
|
||||
*oq_[1] = AsmOp::Custom("ROT", 3, 3);
|
||||
show_right();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Optimizer::is_const_push_xchgs() {
|
||||
if (!(pb_ >= 2 && pb_ <= l2_ && op_[0]->is_gconst())) {
|
||||
return false;
|
||||
|
@ -260,6 +275,21 @@ bool Optimizer::simple_rewrite(int p, AsmOp&& new_op1, AsmOp&& new_op2) {
|
|||
return true;
|
||||
}
|
||||
|
||||
bool Optimizer::simple_rewrite(int p, AsmOp&& new_op1, AsmOp&& new_op2, AsmOp&& new_op3) {
|
||||
assert(p > 2 && p <= l_);
|
||||
p_ = p;
|
||||
q_ = 3;
|
||||
show_left();
|
||||
oq_[0] = std::move(op_[0]);
|
||||
*oq_[0] = new_op1;
|
||||
oq_[1] = std::move(op_[1]);
|
||||
*oq_[1] = new_op2;
|
||||
oq_[2] = std::move(op_[2]);
|
||||
*oq_[2] = new_op3;
|
||||
show_right();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Optimizer::simple_rewrite_nop() {
|
||||
assert(p_ > 0 && p_ <= l_);
|
||||
q_ = 0;
|
||||
|
@ -402,6 +432,16 @@ bool Optimizer::is_nip_seq(int* i, int* j) {
|
|||
return is_pred([i, j](const auto& t) { return t.is_nip_seq(i, j) && *i >= 3 && *i <= 15; });
|
||||
}
|
||||
|
||||
bool Optimizer::is_pop_blkdrop(int* i, int* k) {
|
||||
return is_pred([i, k](const auto& t) { return t.is_pop_blkdrop(i, k) && *i >= *k && *k >= 2 && *k <= 15; }, 3);
|
||||
}
|
||||
|
||||
bool Optimizer::is_2pop_blkdrop(int* i, int* j, int* k) {
|
||||
return is_pred(
|
||||
[i, j, k](const auto& t) { return t.is_2pop_blkdrop(i, j, k) && *i >= *k && *j >= *k && *k >= 2 && *k <= 15; },
|
||||
3);
|
||||
}
|
||||
|
||||
bool Optimizer::compute_stack_transforms() {
|
||||
StackTransform trans;
|
||||
for (int i = 0; i < l_; i++) {
|
||||
|
@ -450,7 +490,7 @@ bool Optimizer::find_at_least(int pb) {
|
|||
// show_stack_transforms();
|
||||
int i = -100, j = -100, k = -100;
|
||||
return (is_const_push_swap() && 3 >= pb && rewrite_const_push_swap()) || (is_nop() && simple_rewrite_nop()) ||
|
||||
(is_const_push_xchgs() && rewrite_const_push_xchgs()) ||
|
||||
(is_const_rot() && rewrite_const_rot()) || (is_const_push_xchgs() && rewrite_const_push_xchgs()) ||
|
||||
(is_xchg(&i, &j) && simple_rewrite(AsmOp::Xchg(i, j))) || (is_push(&i) && simple_rewrite(AsmOp::Push(i))) ||
|
||||
(is_pop(&i) && simple_rewrite(AsmOp::Pop(i))) || (is_rot() && simple_rewrite(AsmOp::Custom("ROT", 3, 3))) ||
|
||||
(is_rotrev() && simple_rewrite(AsmOp::Custom("-ROT", 3, 3))) ||
|
||||
|
@ -468,6 +508,10 @@ bool Optimizer::find_at_least(int pb) {
|
|||
(is_blkdrop(&i) && simple_rewrite(AsmOp::BlkDrop(i))) ||
|
||||
(is_reverse(&i, &j) && simple_rewrite(AsmOp::BlkReverse(i, j))) ||
|
||||
(is_nip_seq(&i, &j) && simple_rewrite(AsmOp::Xchg(i, j), AsmOp::BlkDrop(i))) ||
|
||||
(is_pop_blkdrop(&i, &k) && simple_rewrite(AsmOp::Pop(i), AsmOp::BlkDrop(k))) ||
|
||||
(is_2pop_blkdrop(&i, &j, &k) && (k >= 3 && k <= 13 && i != j + 1 && i <= 15 && j <= 14
|
||||
? simple_rewrite(AsmOp::Xchg2(j + 1, i), AsmOp::BlkDrop(k + 2))
|
||||
: simple_rewrite(AsmOp::Pop(i), AsmOp::Pop(j), AsmOp::BlkDrop(k)))) ||
|
||||
(is_xchg3(&i, &j, &k) && simple_rewrite(AsmOp::Xchg3(i, j, k))) ||
|
||||
(is_xc2pu(&i, &j, &k) && simple_rewrite(AsmOp::Xc2Pu(i, j, k))) ||
|
||||
(is_xcpuxc(&i, &j, &k) && simple_rewrite(AsmOp::XcPuXc(i, j, k))) ||
|
||||
|
|
|
@ -174,6 +174,53 @@ FormalArg parse_formal_arg(Lexer& lex, int fa_idx) {
|
|||
return std::make_tuple(arg_type, new_sym_def, loc);
|
||||
}
|
||||
|
||||
void parse_global_var_decl(Lexer& lex) {
|
||||
TypeExpr* var_type = 0;
|
||||
SrcLocation loc = lex.cur().loc;
|
||||
if (lex.tp() == '_') {
|
||||
lex.next();
|
||||
var_type = TypeExpr::new_hole();
|
||||
loc = lex.cur().loc;
|
||||
} else if (lex.tp() != _Ident) {
|
||||
var_type = parse_type(lex);
|
||||
} else {
|
||||
auto sym = sym::lookup_symbol(lex.cur().val);
|
||||
if (sym && dynamic_cast<SymValType*>(sym->value)) {
|
||||
auto val = dynamic_cast<SymValType*>(sym->value);
|
||||
lex.next();
|
||||
var_type = val->get_type();
|
||||
} else {
|
||||
var_type = TypeExpr::new_hole();
|
||||
}
|
||||
}
|
||||
if (lex.tp() != _Ident) {
|
||||
lex.expect(_Ident, "global variable name");
|
||||
}
|
||||
loc = lex.cur().loc;
|
||||
SymDef* sym_def = sym::define_global_symbol(lex.cur().val, false, loc);
|
||||
if (!sym_def) {
|
||||
lex.cur().error_at("cannot define global symbol `", "`");
|
||||
}
|
||||
if (sym_def->value) {
|
||||
auto val = dynamic_cast<SymValGlobVar*>(sym_def->value);
|
||||
if (!val) {
|
||||
lex.cur().error_at("symbol `", "` cannot be redefined as a global variable");
|
||||
}
|
||||
try {
|
||||
unify(var_type, val->sym_type);
|
||||
} catch (UnifyError& ue) {
|
||||
std::ostringstream os;
|
||||
os << "cannot unify new type " << var_type << " of global variable `" << sym_def->name()
|
||||
<< "` with its previous type " << val->sym_type << ": " << ue;
|
||||
lex.cur().error(os.str());
|
||||
}
|
||||
} else {
|
||||
sym_def->value = new SymValGlobVar{glob_var_cnt++, var_type};
|
||||
glob_vars.push_back(sym_def);
|
||||
}
|
||||
lex.next();
|
||||
}
|
||||
|
||||
FormalArgList parse_formal_args(Lexer& lex) {
|
||||
FormalArgList args;
|
||||
lex.expect('(', "formal argument list");
|
||||
|
@ -205,6 +252,18 @@ TypeExpr* extract_total_arg_type(const FormalArgList& arg_list) {
|
|||
return TypeExpr::new_tensor(std::move(type_list));
|
||||
}
|
||||
|
||||
void parse_global_var_decls(Lexer& lex) {
|
||||
lex.expect(_Global);
|
||||
while (true) {
|
||||
parse_global_var_decl(lex);
|
||||
if (lex.tp() != ',') {
|
||||
break;
|
||||
}
|
||||
lex.expect(',');
|
||||
}
|
||||
lex.expect(';');
|
||||
}
|
||||
|
||||
SymValCodeFunc* make_new_glob_func(SymDef* func_sym, TypeExpr* func_type, bool impure = false) {
|
||||
SymValCodeFunc* res = new SymValCodeFunc{glob_func_cnt, func_type, impure};
|
||||
func_sym->value = res;
|
||||
|
@ -239,6 +298,22 @@ bool check_global_func(const Lexem& cur, sym_idx_t func_name = 0) {
|
|||
}
|
||||
}
|
||||
|
||||
Expr* make_func_apply(Expr* fun, Expr* x) {
|
||||
Expr* res;
|
||||
if (fun->cls == Expr::_Glob) {
|
||||
if (x->cls == Expr::_Tuple) {
|
||||
res = new Expr{Expr::_Apply, fun->sym, x->args};
|
||||
} else {
|
||||
res = new Expr{Expr::_Apply, fun->sym, {x}};
|
||||
}
|
||||
res->flags = Expr::_IsRvalue | (fun->flags & Expr::_IsImpure);
|
||||
} else {
|
||||
res = new Expr{Expr::_VarApply, {fun, x}};
|
||||
res->flags = Expr::_IsRvalue;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
Expr* parse_expr(Lexer& lex, CodeBlob& code, bool nv = false);
|
||||
|
||||
// parse ( E { , E } ) | () | id | num | _
|
||||
|
@ -323,6 +398,16 @@ Expr* parse_expr100(Lexer& lex, CodeBlob& code, bool nv) {
|
|||
lex.next();
|
||||
return res;
|
||||
}
|
||||
if (sym && dynamic_cast<SymValGlobVar*>(sym->value)) {
|
||||
auto val = dynamic_cast<SymValGlobVar*>(sym->value);
|
||||
Expr* res = new Expr{Expr::_GlobVar, lex.cur().loc};
|
||||
res->e_type = val->get_type();
|
||||
res->sym = sym;
|
||||
res->flags = Expr::_IsLvalue | Expr::_IsRvalue | Expr::_IsImpure;
|
||||
lex.next();
|
||||
return res;
|
||||
}
|
||||
bool auto_apply = false;
|
||||
Expr* res = new Expr{Expr::_Var, lex.cur().loc};
|
||||
if (nv) {
|
||||
res->val = ~lex.cur().val;
|
||||
|
@ -344,6 +429,7 @@ Expr* parse_expr100(Lexer& lex, CodeBlob& code, bool nv) {
|
|||
} else if (val->type == SymVal::_Func) {
|
||||
res->e_type = val->get_type();
|
||||
res->cls = Expr::_Glob;
|
||||
auto_apply = val->auto_apply;
|
||||
} else if (val->idx < 0) {
|
||||
lex.cur().error_at("accessing variable `", "` being defined");
|
||||
} else {
|
||||
|
@ -354,6 +440,12 @@ Expr* parse_expr100(Lexer& lex, CodeBlob& code, bool nv) {
|
|||
// std::cerr << "accessing symbol " << lex.cur().str << " : " << res->e_type << (val->impure ? " (impure)" : " (pure)") << std::endl;
|
||||
res->flags = Expr::_IsLvalue | Expr::_IsRvalue | (val->impure ? Expr::_IsImpure : 0);
|
||||
}
|
||||
if (auto_apply) {
|
||||
int impure = res->flags & Expr::_IsImpure;
|
||||
delete res;
|
||||
res = new Expr{Expr::_Apply, sym, {}};
|
||||
res->flags = Expr::_IsRvalue | impure;
|
||||
}
|
||||
res->deduce_type(lex.cur());
|
||||
lex.next();
|
||||
return res;
|
||||
|
@ -362,22 +454,6 @@ Expr* parse_expr100(Lexer& lex, CodeBlob& code, bool nv) {
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
Expr* make_func_apply(Expr* fun, Expr* x) {
|
||||
Expr* res;
|
||||
if (fun->cls == Expr::_Glob) {
|
||||
if (x->cls == Expr::_Tuple) {
|
||||
res = new Expr{Expr::_Apply, fun->sym, x->args};
|
||||
} else {
|
||||
res = new Expr{Expr::_Apply, fun->sym, {x}};
|
||||
}
|
||||
res->flags = Expr::_IsRvalue | (fun->flags & Expr::_IsImpure);
|
||||
} else {
|
||||
res = new Expr{Expr::_VarApply, {fun, x}};
|
||||
res->flags = Expr::_IsRvalue;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
// parse E { E }
|
||||
Expr* parse_expr90(Lexer& lex, CodeBlob& code, bool nv) {
|
||||
Expr* res = parse_expr100(lex, code, nv);
|
||||
|
@ -1193,7 +1269,11 @@ bool parse_source(std::istream* is, const src::FileDescr* fdescr) {
|
|||
src::SourceReader reader{is, fdescr};
|
||||
Lexer lex{reader, true};
|
||||
while (lex.tp() != _Eof) {
|
||||
parse_func_def(lex);
|
||||
if (lex.tp() == _Global) {
|
||||
parse_global_var_decls(lex);
|
||||
} else {
|
||||
parse_func_def(lex);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -334,6 +334,13 @@ bool StackTransform::apply_pop(int i) {
|
|||
}
|
||||
}
|
||||
|
||||
bool StackTransform::apply_blkpop(int k) {
|
||||
if (!is_valid() || k < 0) {
|
||||
return invalidate();
|
||||
}
|
||||
return !k || (touch(k - 1) && shift(k));
|
||||
}
|
||||
|
||||
bool StackTransform::equal(const StackTransform &other, bool relaxed) const {
|
||||
if (!is_valid() || !other.is_valid()) {
|
||||
return false;
|
||||
|
@ -800,6 +807,49 @@ bool StackTransform::is_nip_seq(int *i, int *j) const {
|
|||
}
|
||||
}
|
||||
|
||||
// POP s(i); BLKDROP k (usually for i >= k >= 0)
|
||||
bool StackTransform::is_pop_blkdrop(int i, int k) const {
|
||||
StackTransform t;
|
||||
return is_valid() && d == k + 1 && t.apply_pop(i) && t.apply_blkpop(k) && t <= *this;
|
||||
}
|
||||
|
||||
// POP s(i); BLKDROP k == XCHG s0,s(i); BLKDROP k+1 for i >= k >= 0
|
||||
// k+1 k+2 .. i-1 0 i+1 ..
|
||||
bool StackTransform::is_pop_blkdrop(int *i, int *k) const {
|
||||
if (is_valid() && n == 1 && d > 0 && !A[0].second) {
|
||||
*k = d - 1;
|
||||
*i = A[0].first;
|
||||
return is_pop_blkdrop(*i, *k);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// POP s(i); POP s(j); BLKDROP k (usually for i<>j >= k >= 0)
|
||||
bool StackTransform::is_2pop_blkdrop(int i, int j, int k) const {
|
||||
StackTransform t;
|
||||
return is_valid() && d == k + 2 && t.apply_pop(i) && t.apply_pop(j) && t.apply_blkpop(k) && t <= *this;
|
||||
}
|
||||
|
||||
// POP s(i); POP s(j); BLKDROP k == XCHG s0,s(i); XCHG s1,s(j+1); BLKDROP k+2 (usually for i<>j >= k >= 2)
|
||||
// k+2 k+3 .. i-1 0 i+1 ... j 1 j+2 ...
|
||||
bool StackTransform::is_2pop_blkdrop(int *i, int *j, int *k) const {
|
||||
if (is_valid() && n == 2 && d >= 2 && A[0].second + A[1].second == 1) {
|
||||
*k = d - 2;
|
||||
int t = (A[0].second > 0);
|
||||
*i = A[t].first;
|
||||
*j = A[1 - t].first - 1;
|
||||
return is_2pop_blkdrop(*i, *j, *k);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// PUSHCONST c ; ROT == 1 -1000 0 2 3
|
||||
bool StackTransform::is_const_rot() const {
|
||||
return is_valid() && d == -1 && is_trivial_after(3) && get(0) == 1 && get(1) <= c_start && get(2) == 0;
|
||||
}
|
||||
|
||||
void StackTransform::show(std::ostream &os, int mode) const {
|
||||
if (!is_valid()) {
|
||||
os << "<invalid>";
|
||||
|
|
28
crypto/func/test/a11.fc
Normal file
28
crypto/func/test/a11.fc
Normal file
|
@ -0,0 +1,28 @@
|
|||
_ f(slice ds, int total_weight, int elect_at, cell frozen, int total_stakes, int cnt, cell credits, cell vdict, int elect) {
|
||||
return (ds, elect, credits);
|
||||
}
|
||||
|
||||
_ g(slice ds, int total_weight, int elect_at, cell frozen, int total_stakes, int cnt, cell credits, int elect) {
|
||||
return (ds, elect, credits);
|
||||
}
|
||||
|
||||
_ h(slice ds, int total_weight, int elect_at, cell frozen, int total_stakes, cell credits, int elect) {
|
||||
return (ds, elect, credits);
|
||||
}
|
||||
|
||||
_ h2(slice ds, int total_weight, int elect_at, cell frozen, int total_stakes, cell credits, int elect) {
|
||||
return (ds, credits, elect);
|
||||
}
|
||||
|
||||
_ h3(int pubkey, int adnl_addr, int stake, int tot_weight, tuple vinfo) {
|
||||
return (pubkey, tot_weight, stake, vinfo);
|
||||
}
|
||||
|
||||
_ z(int a, int b) {
|
||||
return (b, 0, a);
|
||||
}
|
||||
|
||||
_ z2(int a, int b) {
|
||||
return (0, a, b);
|
||||
}
|
||||
|
20
crypto/func/test/a6_5.fc
Normal file
20
crypto/func/test/a6_5.fc
Normal file
|
@ -0,0 +1,20 @@
|
|||
var twice(f, x) {
|
||||
return f (f x);
|
||||
}
|
||||
|
||||
_ sqr(x) {
|
||||
return x * x;
|
||||
}
|
||||
|
||||
var main(x) {
|
||||
var f = sqr;
|
||||
return twice(f, x) * f(x);
|
||||
}
|
||||
|
||||
var pow6(x) {
|
||||
return twice(sqr, x) * sqr(x);
|
||||
}
|
||||
|
||||
_ q() {
|
||||
return false;
|
||||
}
|
37
crypto/func/test/c1.fc
Normal file
37
crypto/func/test/c1.fc
Normal file
|
@ -0,0 +1,37 @@
|
|||
global int x, y, z;
|
||||
global (cell, slice) y;
|
||||
global ((int, int) -> int) op;
|
||||
|
||||
_ get() {
|
||||
var t = z + 1;
|
||||
return x;
|
||||
}
|
||||
|
||||
_ pre_next() {
|
||||
return x + 1;
|
||||
}
|
||||
|
||||
() init() impure {
|
||||
;; global x;
|
||||
x = 0;
|
||||
}
|
||||
|
||||
int next() impure {
|
||||
;; global x;
|
||||
return x += 1;
|
||||
}
|
||||
|
||||
_ set_y(x, w) {
|
||||
y = (w, x);
|
||||
}
|
||||
|
||||
_ get_y() impure {
|
||||
return y;
|
||||
}
|
||||
|
||||
int main(int c) {
|
||||
init();
|
||||
c += next();
|
||||
return c + pre_next();
|
||||
}
|
||||
|
10
crypto/func/test/c2.fc
Normal file
10
crypto/func/test/c2.fc
Normal file
|
@ -0,0 +1,10 @@
|
|||
global ((int, int) -> int) op;
|
||||
|
||||
int check_assoc(int a, int b, int c) {
|
||||
return op(op(a, b), c) == op(a, op(b, c));
|
||||
}
|
||||
|
||||
int main() {
|
||||
op = _+_;
|
||||
return check_assoc(2, 3, 9);
|
||||
}
|
7
crypto/func/test/c2_1.fc
Normal file
7
crypto/func/test/c2_1.fc
Normal file
|
@ -0,0 +1,7 @@
|
|||
_ check_assoc(op, a, b, c) {
|
||||
return op(op(a, b), c) == op(a, op(b, c));
|
||||
}
|
||||
|
||||
int main() {
|
||||
return check_assoc(_+_, 2, 3, 9);
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue