mirror of
https://github.com/ton-blockchain/ton
synced 2025-02-13 03:32:22 +00:00
Add complex assigns to FunC and fix UB (#574)
* Fixed complex funC setglob cases * Forbid modifying local variables after using them in the same tensor * Fix analyzing "while" in func * Update funC version (#9) * Update stress tester * Fix using variable after move Co-authored-by: krigga <krigga7@gmail.com> Co-authored-by: SpyCheese <mikle98@yandex.ru>
This commit is contained in:
parent
7347ec0b3b
commit
a1d8a5e4f3
4 changed files with 83 additions and 19 deletions
|
@ -810,6 +810,7 @@ VarDescrList Op::fwd_analyze(VarDescrList values) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case _While: {
|
case _While: {
|
||||||
|
auto values0 = values;
|
||||||
values = block0->fwd_analyze(values);
|
values = block0->fwd_analyze(values);
|
||||||
if (values[left[0]] && values[left[0]]->always_false()) {
|
if (values[left[0]] && values[left[0]]->always_false()) {
|
||||||
// block1 never executed
|
// block1 never executed
|
||||||
|
@ -817,7 +818,7 @@ VarDescrList Op::fwd_analyze(VarDescrList values) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
while (true) {
|
while (true) {
|
||||||
VarDescrList next_values = values | block0->fwd_analyze(block1->fwd_analyze(values));
|
VarDescrList next_values = values | block0->fwd_analyze(values0 | block1->fwd_analyze(values));
|
||||||
if (same_values(next_values, values)) {
|
if (same_values(next_values, values)) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
|
@ -137,13 +137,20 @@ class CodeRepeat(Code):
|
||||||
self.c.write(f, indent + 1)
|
self.c.write(f, indent + 1)
|
||||||
print(" " * (indent + 1) + "%s += 1;" % var, file=f)
|
print(" " * (indent + 1) + "%s += 1;" % var, file=f)
|
||||||
print(" " * indent + "}", file=f)
|
print(" " * indent + "}", file=f)
|
||||||
else:
|
elif self.loop_type == 2:
|
||||||
var = gen_var_name()
|
var = gen_var_name()
|
||||||
print(" " * indent + "int %s = 0;" % var, file=f)
|
print(" " * indent + "int %s = 0;" % var, file=f)
|
||||||
print(" " * indent + "do {", file=f)
|
print(" " * indent + "do {", file=f)
|
||||||
self.c.write(f, indent + 1)
|
self.c.write(f, indent + 1)
|
||||||
print(" " * (indent + 1) + "%s += 1;" % var, file=f)
|
print(" " * (indent + 1) + "%s += 1;" % var, file=f)
|
||||||
print(" " * indent + "} until (%s >= %d);" % (var, self.n), file=f)
|
print(" " * indent + "} until (%s >= %d);" % (var, self.n), file=f)
|
||||||
|
else:
|
||||||
|
var = gen_var_name()
|
||||||
|
print(" " * indent + "int %s = %d;" % (var, self.n - 1), file=f)
|
||||||
|
print(" " * indent + "while (%s >= 0) {" % var, file=f)
|
||||||
|
self.c.write(f, indent + 1)
|
||||||
|
print(" " * (indent + 1) + "%s -= 1;" % var, file=f)
|
||||||
|
print(" " * indent + "}", file=f)
|
||||||
|
|
||||||
class CodeThrow(Code):
|
class CodeThrow(Code):
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
|
@ -199,7 +206,7 @@ def gen_code(xl, xr, with_return, loop_depth=0, try_catch_depth=0, can_throw=Fal
|
||||||
for _ in range(random.randint(0, 2)):
|
for _ in range(random.randint(0, 2)):
|
||||||
if random.randint(0, 3) == 0 and loop_depth < 3:
|
if random.randint(0, 3) == 0 and loop_depth < 3:
|
||||||
c = gen_code(xl, xr, False, loop_depth + 1, try_catch_depth, can_throw)
|
c = gen_code(xl, xr, False, loop_depth + 1, try_catch_depth, can_throw)
|
||||||
code.append(CodeRepeat(random.randint(0, 3), c, random.randint(0, 2)))
|
code.append(CodeRepeat(random.randint(0, 3), c, random.randint(0, 3)))
|
||||||
elif xr - xl > 1:
|
elif xr - xl > 1:
|
||||||
xmid = random.randrange(xl + 1, xr)
|
xmid = random.randrange(xl + 1, xr)
|
||||||
ret = random.choice((0, 0, 0, 0, 0, 1, 2))
|
ret = random.choice((0, 0, 0, 0, 0, 1, 2))
|
||||||
|
|
|
@ -39,7 +39,7 @@ extern std::string generated_from;
|
||||||
|
|
||||||
constexpr int optimize_depth = 20;
|
constexpr int optimize_depth = 20;
|
||||||
|
|
||||||
const std::string func_version{"0.3.0"};
|
const std::string func_version{"0.4.0"};
|
||||||
|
|
||||||
enum Keyword {
|
enum Keyword {
|
||||||
_Eof = -1,
|
_Eof = -1,
|
||||||
|
@ -306,10 +306,16 @@ struct TmpVar {
|
||||||
sym_idx_t name;
|
sym_idx_t name;
|
||||||
int coord;
|
int coord;
|
||||||
std::unique_ptr<SrcLocation> where;
|
std::unique_ptr<SrcLocation> where;
|
||||||
|
size_t modify_forbidden = 0;
|
||||||
TmpVar(var_idx_t _idx, int _cls, TypeExpr* _type = 0, SymDef* sym = 0, const SrcLocation* loc = 0);
|
TmpVar(var_idx_t _idx, int _cls, TypeExpr* _type = 0, SymDef* sym = 0, const SrcLocation* loc = 0);
|
||||||
void show(std::ostream& os, int omit_idx = 0) const;
|
void show(std::ostream& os, int omit_idx = 0) const;
|
||||||
void dump(std::ostream& os) const;
|
void dump(std::ostream& os) const;
|
||||||
void set_location(const SrcLocation& loc);
|
void set_location(const SrcLocation& loc);
|
||||||
|
std::string to_string() const {
|
||||||
|
std::ostringstream s;
|
||||||
|
show(s, 2);
|
||||||
|
return s.str();
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
struct VarDescr {
|
struct VarDescr {
|
||||||
|
@ -722,6 +728,22 @@ struct CodeBlob {
|
||||||
void mark_noreturn();
|
void mark_noreturn();
|
||||||
void generate_code(AsmOpList& out_list, int mode = 0);
|
void generate_code(AsmOpList& out_list, int mode = 0);
|
||||||
void generate_code(std::ostream& os, int mode = 0, int indent = 0);
|
void generate_code(std::ostream& os, int mode = 0, int indent = 0);
|
||||||
|
|
||||||
|
void mark_modify_forbidden(var_idx_t idx) {
|
||||||
|
++vars.at(idx).modify_forbidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
void unmark_modify_forbidden(var_idx_t idx) {
|
||||||
|
assert(vars.at(idx).modify_forbidden > 0);
|
||||||
|
--vars.at(idx).modify_forbidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
void check_modify_forbidden(var_idx_t idx, const SrcLocation& here) const {
|
||||||
|
if (vars.at(idx).modify_forbidden) {
|
||||||
|
throw src::ParseError{here, PSTRING() << "Modifying local variable " << vars[idx].to_string()
|
||||||
|
<< " after using it in the same expression"};
|
||||||
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -925,7 +947,7 @@ struct Expr {
|
||||||
}
|
}
|
||||||
int define_new_vars(CodeBlob& code);
|
int define_new_vars(CodeBlob& code);
|
||||||
int predefine_vars();
|
int predefine_vars();
|
||||||
std::vector<var_idx_t> pre_compile(CodeBlob& code, bool lval = false) const;
|
std::vector<var_idx_t> pre_compile(CodeBlob& code, std::vector<std::pair<SymDef*, var_idx_t>>* lval_globs = nullptr) const;
|
||||||
static std::vector<var_idx_t> pre_compile_let(CodeBlob& code, Expr* lhs, Expr* rhs, const SrcLocation& here);
|
static std::vector<var_idx_t> pre_compile_let(CodeBlob& code, Expr* lhs, Expr* rhs, const SrcLocation& here);
|
||||||
var_idx_t new_tmp(CodeBlob& code) const;
|
var_idx_t new_tmp(CodeBlob& code) const;
|
||||||
std::vector<var_idx_t> new_tmp_vect(CodeBlob& code) const {
|
std::vector<var_idx_t> new_tmp_vect(CodeBlob& code) const {
|
||||||
|
|
|
@ -221,6 +221,13 @@ var_idx_t Expr::new_tmp(CodeBlob& code) const {
|
||||||
return code.create_tmp_var(e_type, &here);
|
return code.create_tmp_var(e_type, &here);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void add_set_globs(CodeBlob& code, std::vector<std::pair<SymDef*, var_idx_t>>& globs, const SrcLocation& here) {
|
||||||
|
for (const auto& p : globs) {
|
||||||
|
auto& op = code.emplace_back(here, Op::_SetGlob, std::vector<var_idx_t>{}, std::vector<var_idx_t>{ p.second }, p.first);
|
||||||
|
op.flags |= Op::_Impure;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
std::vector<var_idx_t> Expr::pre_compile_let(CodeBlob& code, Expr* lhs, Expr* rhs, const SrcLocation& here) {
|
std::vector<var_idx_t> Expr::pre_compile_let(CodeBlob& code, Expr* lhs, Expr* rhs, const SrcLocation& here) {
|
||||||
while (lhs->is_type_apply()) {
|
while (lhs->is_type_apply()) {
|
||||||
lhs = lhs->args.at(0);
|
lhs = lhs->args.at(0);
|
||||||
|
@ -245,19 +252,18 @@ std::vector<var_idx_t> Expr::pre_compile_let(CodeBlob& code, Expr* lhs, Expr* rh
|
||||||
return tmp;
|
return tmp;
|
||||||
}
|
}
|
||||||
auto right = rhs->pre_compile(code);
|
auto right = rhs->pre_compile(code);
|
||||||
if (lhs->cls == Expr::_GlobVar) {
|
std::vector<std::pair<SymDef*, var_idx_t>> globs;
|
||||||
assert(lhs->sym);
|
auto left = lhs->pre_compile(code, &globs);
|
||||||
auto& op = code.emplace_back(here, Op::_SetGlob, std::vector<var_idx_t>{}, right, lhs->sym);
|
for (var_idx_t v : left) {
|
||||||
op.flags |= Op::_Impure;
|
code.check_modify_forbidden(v, here);
|
||||||
} else {
|
|
||||||
auto left = lhs->pre_compile(code, true);
|
|
||||||
code.emplace_back(here, Op::_Let, std::move(left), right);
|
|
||||||
}
|
}
|
||||||
|
code.emplace_back(here, Op::_Let, std::move(left), right);
|
||||||
|
add_set_globs(code, globs, here);
|
||||||
return right;
|
return right;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<var_idx_t> Expr::pre_compile(CodeBlob& code, bool lval) const {
|
std::vector<var_idx_t> Expr::pre_compile(CodeBlob& code, std::vector<std::pair<SymDef*, var_idx_t>>* lval_globs) const {
|
||||||
if (lval && !(cls == _Tensor || cls == _Var || cls == _Hole || cls == _TypeApply)) {
|
if (lval_globs && !(cls == _Tensor || cls == _Var || cls == _Hole || cls == _TypeApply || cls == _GlobVar)) {
|
||||||
std::cerr << "lvalue expression constructor is " << cls << std::endl;
|
std::cerr << "lvalue expression constructor is " << cls << std::endl;
|
||||||
throw src::Fatal{"cannot compile lvalue expression with unknown constructor"};
|
throw src::Fatal{"cannot compile lvalue expression with unknown constructor"};
|
||||||
}
|
}
|
||||||
|
@ -265,9 +271,15 @@ std::vector<var_idx_t> Expr::pre_compile(CodeBlob& code, bool lval) const {
|
||||||
case _Tensor: {
|
case _Tensor: {
|
||||||
std::vector<var_idx_t> res;
|
std::vector<var_idx_t> res;
|
||||||
for (const auto& x : args) {
|
for (const auto& x : args) {
|
||||||
auto add = x->pre_compile(code, lval);
|
auto add = x->pre_compile(code, lval_globs);
|
||||||
|
for (var_idx_t v : add) {
|
||||||
|
code.mark_modify_forbidden(v);
|
||||||
|
}
|
||||||
res.insert(res.end(), add.cbegin(), add.cend());
|
res.insert(res.end(), add.cbegin(), add.cend());
|
||||||
}
|
}
|
||||||
|
for (var_idx_t v : res) {
|
||||||
|
code.unmark_modify_forbidden(v);
|
||||||
|
}
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
case _Apply: {
|
case _Apply: {
|
||||||
|
@ -279,6 +291,9 @@ std::vector<var_idx_t> Expr::pre_compile(CodeBlob& code, bool lval) const {
|
||||||
std::vector<std::vector<var_idx_t>> add_list(args.size());
|
std::vector<std::vector<var_idx_t>> add_list(args.size());
|
||||||
for (int i : func->arg_order) {
|
for (int i : func->arg_order) {
|
||||||
add_list[i] = args[i]->pre_compile(code);
|
add_list[i] = args[i]->pre_compile(code);
|
||||||
|
for (var_idx_t v : add_list[i]) {
|
||||||
|
code.mark_modify_forbidden(v);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
for (const auto& add : add_list) {
|
for (const auto& add : add_list) {
|
||||||
res.insert(res.end(), add.cbegin(), add.cend());
|
res.insert(res.end(), add.cbegin(), add.cend());
|
||||||
|
@ -286,9 +301,15 @@ std::vector<var_idx_t> Expr::pre_compile(CodeBlob& code, bool lval) const {
|
||||||
} else {
|
} else {
|
||||||
for (const auto& x : args) {
|
for (const auto& x : args) {
|
||||||
auto add = x->pre_compile(code);
|
auto add = x->pre_compile(code);
|
||||||
|
for (var_idx_t v : add) {
|
||||||
|
code.mark_modify_forbidden(v);
|
||||||
|
}
|
||||||
res.insert(res.end(), add.cbegin(), add.cend());
|
res.insert(res.end(), add.cbegin(), add.cend());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
for (var_idx_t v : res) {
|
||||||
|
code.unmark_modify_forbidden(v);
|
||||||
|
}
|
||||||
auto rvect = new_tmp_vect(code);
|
auto rvect = new_tmp_vect(code);
|
||||||
auto& op = code.emplace_back(here, Op::_Call, rvect, std::move(res), sym);
|
auto& op = code.emplace_back(here, Op::_Call, rvect, std::move(res), sym);
|
||||||
if (flags & _IsImpure) {
|
if (flags & _IsImpure) {
|
||||||
|
@ -297,7 +318,7 @@ std::vector<var_idx_t> Expr::pre_compile(CodeBlob& code, bool lval) const {
|
||||||
return rvect;
|
return rvect;
|
||||||
}
|
}
|
||||||
case _TypeApply:
|
case _TypeApply:
|
||||||
return args[0]->pre_compile(code, lval);
|
return args[0]->pre_compile(code, lval_globs);
|
||||||
case _Var:
|
case _Var:
|
||||||
case _Hole:
|
case _Hole:
|
||||||
return {val};
|
return {val};
|
||||||
|
@ -329,18 +350,31 @@ std::vector<var_idx_t> Expr::pre_compile(CodeBlob& code, bool lval) const {
|
||||||
case _Glob:
|
case _Glob:
|
||||||
case _GlobVar: {
|
case _GlobVar: {
|
||||||
auto rvect = new_tmp_vect(code);
|
auto rvect = new_tmp_vect(code);
|
||||||
|
if (lval_globs) {
|
||||||
|
lval_globs->push_back({ sym, rvect[0] });
|
||||||
|
return rvect;
|
||||||
|
} else {
|
||||||
code.emplace_back(here, Op::_GlobVar, rvect, std::vector<var_idx_t>{}, sym);
|
code.emplace_back(here, Op::_GlobVar, rvect, std::vector<var_idx_t>{}, sym);
|
||||||
return rvect;
|
return rvect;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
case _Letop: {
|
case _Letop: {
|
||||||
return pre_compile_let(code, args.at(0), args.at(1), here);
|
return pre_compile_let(code, args.at(0), args.at(1), here);
|
||||||
}
|
}
|
||||||
case _LetFirst: {
|
case _LetFirst: {
|
||||||
auto rvect = new_tmp_vect(code);
|
auto rvect = new_tmp_vect(code);
|
||||||
auto right = args[1]->pre_compile(code);
|
auto right = args[1]->pre_compile(code);
|
||||||
auto left = args[0]->pre_compile(code, true);
|
std::vector<std::pair<SymDef*, var_idx_t>> local_globs;
|
||||||
|
if (!lval_globs) {
|
||||||
|
lval_globs = &local_globs;
|
||||||
|
}
|
||||||
|
auto left = args[0]->pre_compile(code, lval_globs);
|
||||||
left.push_back(rvect[0]);
|
left.push_back(rvect[0]);
|
||||||
|
for (var_idx_t v : left) {
|
||||||
|
code.check_modify_forbidden(v, here);
|
||||||
|
}
|
||||||
code.emplace_back(here, Op::_Let, std::move(left), std::move(right));
|
code.emplace_back(here, Op::_Let, std::move(left), std::move(right));
|
||||||
|
add_set_globs(code, local_globs, here);
|
||||||
return rvect;
|
return rvect;
|
||||||
}
|
}
|
||||||
case _MkTuple: {
|
case _MkTuple: {
|
||||||
|
|
Loading…
Reference in a new issue