From a1d8a5e4f32bd5ba36d9596a97980d82763314d8 Mon Sep 17 00:00:00 2001 From: EmelyanenkoK Date: Thu, 29 Dec 2022 18:06:13 +0300 Subject: [PATCH] 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 Co-authored-by: SpyCheese --- crypto/func/analyzer.cpp | 3 +- crypto/func/auto-tests/stress_tester.py | 11 ++++- crypto/func/func.h | 26 ++++++++++- crypto/func/gen-abscode.cpp | 62 +++++++++++++++++++------ 4 files changed, 83 insertions(+), 19 deletions(-) diff --git a/crypto/func/analyzer.cpp b/crypto/func/analyzer.cpp index 006d119e..c001e3e8 100644 --- a/crypto/func/analyzer.cpp +++ b/crypto/func/analyzer.cpp @@ -810,6 +810,7 @@ VarDescrList Op::fwd_analyze(VarDescrList values) { break; } case _While: { + auto values0 = values; values = block0->fwd_analyze(values); if (values[left[0]] && values[left[0]]->always_false()) { // block1 never executed @@ -817,7 +818,7 @@ VarDescrList Op::fwd_analyze(VarDescrList values) { break; } 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)) { break; } diff --git a/crypto/func/auto-tests/stress_tester.py b/crypto/func/auto-tests/stress_tester.py index c7b63934..e48127b1 100644 --- a/crypto/func/auto-tests/stress_tester.py +++ b/crypto/func/auto-tests/stress_tester.py @@ -137,13 +137,20 @@ class CodeRepeat(Code): self.c.write(f, indent + 1) print(" " * (indent + 1) + "%s += 1;" % var, file=f) print(" " * indent + "}", file=f) - else: + elif self.loop_type == 2: var = gen_var_name() print(" " * indent + "int %s = 0;" % var, file=f) print(" " * indent + "do {", file=f) self.c.write(f, indent + 1) print(" " * (indent + 1) + "%s += 1;" % var, 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): 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)): if random.randint(0, 3) == 0 and loop_depth < 3: 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: xmid = random.randrange(xl + 1, xr) ret = random.choice((0, 0, 0, 0, 0, 1, 2)) diff --git a/crypto/func/func.h b/crypto/func/func.h index 0f4182bf..81911ade 100644 --- a/crypto/func/func.h +++ b/crypto/func/func.h @@ -39,7 +39,7 @@ extern std::string generated_from; constexpr int optimize_depth = 20; -const std::string func_version{"0.3.0"}; +const std::string func_version{"0.4.0"}; enum Keyword { _Eof = -1, @@ -306,10 +306,16 @@ struct TmpVar { sym_idx_t name; int coord; std::unique_ptr where; + size_t modify_forbidden = 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 dump(std::ostream& os) const; void set_location(const SrcLocation& loc); + std::string to_string() const { + std::ostringstream s; + show(s, 2); + return s.str(); + } }; struct VarDescr { @@ -722,6 +728,22 @@ struct CodeBlob { void mark_noreturn(); void generate_code(AsmOpList& out_list, int mode = 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 predefine_vars(); - std::vector pre_compile(CodeBlob& code, bool lval = false) const; + std::vector pre_compile(CodeBlob& code, std::vector>* lval_globs = nullptr) const; static std::vector pre_compile_let(CodeBlob& code, Expr* lhs, Expr* rhs, const SrcLocation& here); var_idx_t new_tmp(CodeBlob& code) const; std::vector new_tmp_vect(CodeBlob& code) const { diff --git a/crypto/func/gen-abscode.cpp b/crypto/func/gen-abscode.cpp index 21dce714..6fcb1599 100644 --- a/crypto/func/gen-abscode.cpp +++ b/crypto/func/gen-abscode.cpp @@ -221,6 +221,13 @@ var_idx_t Expr::new_tmp(CodeBlob& code) const { return code.create_tmp_var(e_type, &here); } +void add_set_globs(CodeBlob& code, std::vector>& globs, const SrcLocation& here) { + for (const auto& p : globs) { + auto& op = code.emplace_back(here, Op::_SetGlob, std::vector{}, std::vector{ p.second }, p.first); + op.flags |= Op::_Impure; + } +} + std::vector Expr::pre_compile_let(CodeBlob& code, Expr* lhs, Expr* rhs, const SrcLocation& here) { while (lhs->is_type_apply()) { lhs = lhs->args.at(0); @@ -245,19 +252,18 @@ std::vector Expr::pre_compile_let(CodeBlob& code, Expr* lhs, Expr* rh return tmp; } auto right = rhs->pre_compile(code); - if (lhs->cls == Expr::_GlobVar) { - assert(lhs->sym); - auto& op = code.emplace_back(here, Op::_SetGlob, std::vector{}, right, lhs->sym); - op.flags |= Op::_Impure; - } else { - auto left = lhs->pre_compile(code, true); - code.emplace_back(here, Op::_Let, std::move(left), right); + std::vector> globs; + auto left = lhs->pre_compile(code, &globs); + for (var_idx_t v : left) { + code.check_modify_forbidden(v, here); } + code.emplace_back(here, Op::_Let, std::move(left), right); + add_set_globs(code, globs, here); return right; } -std::vector Expr::pre_compile(CodeBlob& code, bool lval) const { - if (lval && !(cls == _Tensor || cls == _Var || cls == _Hole || cls == _TypeApply)) { +std::vector Expr::pre_compile(CodeBlob& code, std::vector>* lval_globs) const { + if (lval_globs && !(cls == _Tensor || cls == _Var || cls == _Hole || cls == _TypeApply || cls == _GlobVar)) { std::cerr << "lvalue expression constructor is " << cls << std::endl; throw src::Fatal{"cannot compile lvalue expression with unknown constructor"}; } @@ -265,9 +271,15 @@ std::vector Expr::pre_compile(CodeBlob& code, bool lval) const { case _Tensor: { std::vector res; 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()); } + for (var_idx_t v : res) { + code.unmark_modify_forbidden(v); + } return res; } case _Apply: { @@ -279,6 +291,9 @@ std::vector Expr::pre_compile(CodeBlob& code, bool lval) const { std::vector> add_list(args.size()); for (int i : func->arg_order) { 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) { res.insert(res.end(), add.cbegin(), add.cend()); @@ -286,9 +301,15 @@ std::vector Expr::pre_compile(CodeBlob& code, bool lval) const { } else { for (const auto& x : args) { 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()); } } + for (var_idx_t v : res) { + code.unmark_modify_forbidden(v); + } auto rvect = new_tmp_vect(code); auto& op = code.emplace_back(here, Op::_Call, rvect, std::move(res), sym); if (flags & _IsImpure) { @@ -297,7 +318,7 @@ std::vector Expr::pre_compile(CodeBlob& code, bool lval) const { return rvect; } case _TypeApply: - return args[0]->pre_compile(code, lval); + return args[0]->pre_compile(code, lval_globs); case _Var: case _Hole: return {val}; @@ -329,8 +350,13 @@ std::vector Expr::pre_compile(CodeBlob& code, bool lval) const { case _Glob: case _GlobVar: { auto rvect = new_tmp_vect(code); - code.emplace_back(here, Op::_GlobVar, rvect, std::vector{}, sym); - return rvect; + if (lval_globs) { + lval_globs->push_back({ sym, rvect[0] }); + return rvect; + } else { + code.emplace_back(here, Op::_GlobVar, rvect, std::vector{}, sym); + return rvect; + } } case _Letop: { return pre_compile_let(code, args.at(0), args.at(1), here); @@ -338,9 +364,17 @@ std::vector Expr::pre_compile(CodeBlob& code, bool lval) const { case _LetFirst: { auto rvect = new_tmp_vect(code); auto right = args[1]->pre_compile(code); - auto left = args[0]->pre_compile(code, true); + std::vector> local_globs; + if (!lval_globs) { + lval_globs = &local_globs; + } + auto left = args[0]->pre_compile(code, lval_globs); 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)); + add_set_globs(code, local_globs, here); return rvect; } case _MkTuple: {