1
0
Fork 0
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:
ton 2019-12-29 12:14:12 +03:00
parent d41ce55305
commit acf16718e6
45 changed files with 1360 additions and 185 deletions

View file

@ -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);

View file

@ -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:

View file

@ -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);

View file

@ -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();

View file

@ -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 {

View file

@ -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();

View file

@ -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: {

View file

@ -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)

View file

@ -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))) ||

View file

@ -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;
}

View file

@ -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
View 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
View 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
View 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
View 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
View 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);
}