diff --git a/crypto/func/abscode.cpp b/crypto/func/abscode.cpp index f1ffcfa4..63ef3b90 100644 --- a/crypto/func/abscode.cpp +++ b/crypto/func/abscode.cpp @@ -293,6 +293,7 @@ void Op::show(std::ostream& os, const std::vector& vars, std::string pfx os << " ]\n"; } std::string dis = disabled() ? " " : ""; + if (replaced()) dis = " "; // not just disabled if (noreturn()) { dis += " "; } @@ -453,6 +454,9 @@ void Op::show_var_list(std::ostream& os, const std::vector& list, cons if (list[i].is_unused()) { os << '?'; } + if (list[i].is_replaced()) { + os << "#"; + } os << vars.at(list[i].idx) << ':'; list[i].show_value(os); } diff --git a/crypto/func/analyzer.cpp b/crypto/func/analyzer.cpp index 9d59cf9a..31e767ba 100644 --- a/crypto/func/analyzer.cpp +++ b/crypto/func/analyzer.cpp @@ -187,6 +187,17 @@ std::size_t VarDescrList::count_used(const std::vector idx_list) cons return res; } +std::size_t VarDescrList::count_unreplaced(const std::vector idx_list) const { + std::size_t res = 0; + for (var_idx_t idx : idx_list) { + auto v = operator[](idx); + if (!v || !v->is_replaced()) { + ++res; + } + } + return res; +} + VarDescrList& VarDescrList::operator-=(var_idx_t idx) { auto it = std::lower_bound(list.begin(), list.end(), idx); if (it != list.end() && it->idx == idx) { @@ -202,19 +213,22 @@ VarDescrList& VarDescrList::operator-=(const std::vector& idx_list) { return *this; } -VarDescrList& VarDescrList::add_var(var_idx_t idx, bool unused) { +VarDescrList& VarDescrList::add_var(var_idx_t idx, bool unused, bool replaced) { auto it = std::lower_bound(list.begin(), list.end(), idx); if (it == list.end() || it->idx != idx) { - list.emplace(it, idx, VarDescr::_Last | (unused ? VarDescr::_Unused : 0)); + list.emplace(it, idx, VarDescr::_Last | (unused ? VarDescr::_Unused : 0) + | (replaced ? (VarDescr::_Unused | VarDescr::_Replaced) : 0)); } else if (it->is_unused() && !unused) { it->clear_unused(); + } else if (it->is_replaced() && !replaced) { + it->clear_replaced(); } return *this; } -VarDescrList& VarDescrList::add_vars(const std::vector& idx_list, bool unused) { +VarDescrList& VarDescrList::add_vars(const std::vector& idx_list, bool unused, bool replaced) { for (var_idx_t idx : idx_list) { - add_var(idx, unused); + add_var(idx, unused, replaced); } return *this; } @@ -330,7 +344,7 @@ VarDescrList& VarDescrList::import_values(const VarDescrList& values) { return *this; } -bool Op::std_compute_used_vars(bool disabled) { +bool Op::std_compute_used_vars(bool disabled, bool replaced) { // left = OP right // var_info := (var_info - left) + right VarDescrList new_var_info{next->var_info}; @@ -338,10 +352,10 @@ bool Op::std_compute_used_vars(bool disabled) { new_var_info.clear_last(); if (args.size() == right.size() && !disabled) { for (const VarDescr& arg : args) { - new_var_info.add_var(arg.idx, arg.is_unused()); + new_var_info.add_var(arg.idx, arg.is_unused(), arg.is_replaced()); } } else { - new_var_info.add_vars(right, disabled); + new_var_info.add_vars(right, disabled, replaced); } return set_var_info(std::move(new_var_info)); } @@ -363,10 +377,13 @@ bool Op::compute_used_vars(const CodeBlob& code, bool edit) { // left = EXEC right; if (!next_var_info.count_used(left) && is_pure()) { // all variables in `left` are not needed + bool repl = left.size() && !next_var_info.count_unreplaced(left); if (edit) { disable(); + if (repl) + replace(); // mark as replaced } - return std_compute_used_vars(true); + return std_compute_used_vars(true, repl); } return std_compute_used_vars(); } @@ -380,6 +397,7 @@ bool Op::compute_used_vars(const CodeBlob& code, bool edit) { case _Let: { // left = right std::size_t cnt = next_var_info.count_used(left); + std::size_t unr = next_var_info.count_unreplaced(left); func_assert(left.size() == right.size()); auto l_it = left.cbegin(), r_it = right.cbegin(); VarDescrList new_var_info{next_var_info}; @@ -389,7 +407,7 @@ bool Op::compute_used_vars(const CodeBlob& code, bool edit) { for (; l_it < left.cend(); ++l_it, ++r_it) { if (std::find(l_it + 1, left.cend(), *l_it) == left.cend()) { auto p = next_var_info[*l_it]; - new_var_info.add_var(*r_it, edit && (!p || p->is_unused())); + new_var_info.add_var(*r_it, edit && (!p || p->is_unused()), p && p->is_replaced()); new_left.push_back(*l_it); new_right.push_back(*r_it); } @@ -401,6 +419,8 @@ bool Op::compute_used_vars(const CodeBlob& code, bool edit) { if (!cnt && edit) { // all variables in `left` are not needed disable(); + if (left.size() && !unr) + replaced(); } return set_var_info(std::move(new_var_info)); } diff --git a/crypto/func/builtins.cpp b/crypto/func/builtins.cpp index cf3adf41..740e438a 100644 --- a/crypto/func/builtins.cpp +++ b/crypto/func/builtins.cpp @@ -436,13 +436,13 @@ AsmOp compile_add(std::vector& res, std::vector& args, const if (!r.int_const->is_valid()) { throw src::ParseError(where, "integer overflow"); } - x.unused(); - y.unused(); + x.replaced(); + y.replaced(); return push_const(r.int_const); } r.val = emulate_add(x.val, y.val); if (y.is_int_const() && y.int_const->signed_fits_bits(8)) { - y.unused(); + y.replaced(); if (y.always_zero()) { return AsmOp::Nop(); } @@ -455,7 +455,7 @@ AsmOp compile_add(std::vector& res, std::vector& args, const return exec_arg_op("ADDCONST", y.int_const, 1); } if (x.is_int_const() && x.int_const->signed_fits_bits(8)) { - x.unused(); + x.replaced(); if (x.always_zero()) { return AsmOp::Nop(); } @@ -478,13 +478,13 @@ AsmOp compile_sub(std::vector& res, std::vector& args, const if (!r.int_const->is_valid()) { throw src::ParseError(where, "integer overflow"); } - x.unused(); - y.unused(); + x.replaced(); + y.replaced(); return push_const(r.int_const); } r.val = emulate_sub(x.val, y.val); if (y.is_int_const() && (-y.int_const)->signed_fits_bits(8)) { - y.unused(); + y.replaced(); if (y.always_zero()) { return {}; } @@ -497,7 +497,7 @@ AsmOp compile_sub(std::vector& res, std::vector& args, const return exec_arg_op("ADDCONST", -y.int_const, 1); } if (x.always_zero()) { - x.unused(); + x.replaced(); return exec_op("NEGATE", 1); } return exec_op("SUB", 2); @@ -511,7 +511,7 @@ AsmOp compile_negate(std::vector& res, std::vector& args, co if (!r.int_const->is_valid()) { throw src::ParseError(where, "integer overflow"); } - x.unused(); + x.replaced(); return push_const(r.int_const); } r.val = emulate_negate(x.val); @@ -523,8 +523,8 @@ AsmOp compile_and(std::vector& res, std::vector& args, const VarDescr &r = res[0], &x = args[0], &y = args[1]; if (x.is_int_const() && y.is_int_const()) { r.set_const(x.int_const & y.int_const); - x.unused(); - y.unused(); + x.replaced(); + y.replaced(); return push_const(r.int_const); } r.val = emulate_and(x.val, y.val); @@ -536,8 +536,8 @@ AsmOp compile_or(std::vector& res, std::vector& args, const VarDescr &r = res[0], &x = args[0], &y = args[1]; if (x.is_int_const() && y.is_int_const()) { r.set_const(x.int_const | y.int_const); - x.unused(); - y.unused(); + x.replaced(); + y.replaced(); return push_const(r.int_const); } r.val = emulate_or(x.val, y.val); @@ -549,8 +549,8 @@ AsmOp compile_xor(std::vector& res, std::vector& args, const VarDescr &r = res[0], &x = args[0], &y = args[1]; if (x.is_int_const() && y.is_int_const()) { r.set_const(x.int_const ^ y.int_const); - x.unused(); - y.unused(); + x.replaced(); + y.replaced(); return push_const(r.int_const); } r.val = emulate_xor(x.val, y.val); @@ -562,7 +562,7 @@ AsmOp compile_not(std::vector& res, std::vector& args, const VarDescr &r = res[0], &x = args[0]; if (x.is_int_const()) { r.set_const(~x.int_const); - x.unused(); + x.replaced(); return push_const(r.int_const); } r.val = emulate_not(x.val); @@ -575,19 +575,19 @@ AsmOp compile_mul_internal(VarDescr& r, VarDescr& x, VarDescr& y, const SrcLocat if (!r.int_const->is_valid()) { throw src::ParseError(where, "integer overflow"); } - x.unused(); - y.unused(); + x.replaced(); + y.replaced(); return push_const(r.int_const); } r.val = emulate_mul(x.val, y.val); if (y.is_int_const()) { int k = is_pos_pow2(y.int_const); if (y.int_const->signed_fits_bits(8) && k < 0) { - y.unused(); + y.replaced(); if (y.always_zero() && x.always_finite()) { // dubious optimization: NaN * 0 = ? r.set_const(y.int_const); - x.unused(); + x.replaced(); return push_const(r.int_const); } if (*y.int_const == 1 && x.always_finite()) { @@ -599,22 +599,22 @@ AsmOp compile_mul_internal(VarDescr& r, VarDescr& x, VarDescr& y, const SrcLocat return exec_arg_op("MULCONST", y.int_const, 1); } if (k > 0) { - y.unused(); + y.replaced(); return exec_arg_op("LSHIFT#", k, 1); } if (k == 0) { - y.unused(); + y.replaced(); return AsmOp::Nop(); } } if (x.is_int_const()) { int k = is_pos_pow2(x.int_const); if (x.int_const->signed_fits_bits(8) && k < 0) { - x.unused(); + x.replaced(); if (x.always_zero() && y.always_finite()) { // dubious optimization: NaN * 0 = ? r.set_const(x.int_const); - y.unused(); + y.replaced(); return push_const(r.int_const); } if (*x.int_const == 1 && y.always_finite()) { @@ -626,11 +626,11 @@ AsmOp compile_mul_internal(VarDescr& r, VarDescr& x, VarDescr& y, const SrcLocat return exec_arg_op("MULCONST", x.int_const, 1); } if (k > 0) { - x.unused(); + x.replaced(); return exec_arg_op("LSHIFT#", k, 1); } if (k == 0) { - x.unused(); + x.replaced(); return AsmOp::Nop(); } } @@ -654,8 +654,8 @@ AsmOp compile_lshift(std::vector& res, std::vector& args, co if (!r.int_const->is_valid()) { throw src::ParseError(where, "integer overflow"); } - x.unused(); - y.unused(); + x.replaced(); + y.replaced(); return push_const(r.int_const); } } @@ -664,20 +664,20 @@ AsmOp compile_lshift(std::vector& res, std::vector& args, co int k = (int)(y.int_const->to_long()); if (!k /* && x.always_finite() */) { // dubious optimization: what if x=NaN ? - y.unused(); + y.replaced(); return AsmOp::Nop(); } - y.unused(); + y.replaced(); return exec_arg_op("LSHIFT#", k, 1); } if (x.is_int_const()) { auto xv = x.int_const->to_long(); if (xv == 1) { - x.unused(); + x.replaced(); return exec_op("POW2", 1); } if (xv == -1) { - x.unused(); + x.replaced(); return exec_op("-1 PUSHINT SWAP LSHIFT", 1); } } @@ -694,8 +694,8 @@ AsmOp compile_rshift(std::vector& res, std::vector& args, co throw src::ParseError(where, "rshift argument is out of range"); } else if (x.is_int_const()) { r.set_const(td::rshift(x.int_const, (int)yv, round_mode)); - x.unused(); - y.unused(); + x.replaced(); + y.replaced(); return push_const(r.int_const); } } @@ -705,10 +705,10 @@ AsmOp compile_rshift(std::vector& res, std::vector& args, co int k = (int)(y.int_const->to_long()); if (!k /* && x.always_finite() */) { // dubious optimization: what if x=NaN ? - y.unused(); + y.replaced(); return AsmOp::Nop(); } - y.unused(); + y.replaced(); return exec_arg_op(rshift + "#", k, 1); } return exec_op(rshift, 2); @@ -720,8 +720,8 @@ AsmOp compile_div_internal(VarDescr& r, VarDescr& x, VarDescr& y, const SrcLocat if (!r.int_const->is_valid()) { throw src::ParseError(where, *y.int_const == 0 ? "division by zero" : "integer overflow"); } - x.unused(); - y.unused(); + x.replaced(); + y.replaced(); return push_const(r.int_const); } r.val = emulate_div(x.val, y.val); @@ -730,16 +730,16 @@ AsmOp compile_div_internal(VarDescr& r, VarDescr& x, VarDescr& y, const SrcLocat throw src::ParseError(where, "division by zero"); } if (*y.int_const == 1 && x.always_finite()) { - y.unused(); + y.replaced(); return AsmOp::Nop(); } if (*y.int_const == -1) { - y.unused(); + y.replaced(); return exec_op("NEGATE", 1); } int k = is_pos_pow2(y.int_const); if (k > 0) { - y.unused(); + y.replaced(); std::string op = "RSHIFT"; if (round_mode >= 0) { op += (round_mode > 0 ? 'C' : 'R'); @@ -768,8 +768,8 @@ AsmOp compile_mod(std::vector& res, std::vector& args, const if (!r.int_const->is_valid()) { throw src::ParseError(where, *y.int_const == 0 ? "division by zero" : "integer overflow"); } - x.unused(); - y.unused(); + x.replaced(); + y.replaced(); return push_const(r.int_const); } r.val = emulate_mod(x.val, y.val); @@ -778,14 +778,14 @@ AsmOp compile_mod(std::vector& res, std::vector& args, const throw src::ParseError(where, "division by zero"); } if ((*y.int_const == 1 || *y.int_const == -1) && x.always_finite()) { - x.unused(); - y.unused(); + x.replaced(); + y.replaced(); r.set_const(td::zero_refint()); return push_const(r.int_const); } int k = is_pos_pow2(y.int_const); if (k > 0) { - y.unused(); + y.replaced(); std::string op = "MODPOW2"; if (round_mode >= 0) { op += (round_mode > 0 ? 'C' : 'R'); @@ -809,16 +809,16 @@ AsmOp compile_muldiv(std::vector& res, std::vector& args, co if (!r.int_const->is_valid()) { throw src::ParseError(where, *z.int_const == 0 ? "division by zero" : "integer overflow"); } - x.unused(); - y.unused(); - z.unused(); + x.replaced(); + y.replaced(); + z.replaced(); return push_const(r.int_const); } if (x.always_zero() || y.always_zero()) { // dubious optimization for z=0... - x.unused(); - y.unused(); - z.unused(); + x.replaced(); + y.replaced(); + z.replaced(); r.set_const(td::make_refint(0)); return push_const(r.int_const); } @@ -834,17 +834,17 @@ AsmOp compile_muldiv(std::vector& res, std::vector& args, co } } if (y.is_int_const() && *y.int_const == 1) { - y.unused(); + y.replaced(); return compile_div_internal(r, x, z, where, round_mode); } if (x.is_int_const() && *x.int_const == 1) { - x.unused(); + x.replaced(); return compile_div_internal(r, y, z, where, round_mode); } if (z.is_int_const()) { int k = is_pos_pow2(z.int_const); if (k > 0) { - z.unused(); + z.replaced(); std::string op = "MULRSHIFT"; if (c) { op += c; @@ -855,7 +855,7 @@ AsmOp compile_muldiv(std::vector& res, std::vector& args, co if (y.is_int_const()) { int k = is_pos_pow2(y.int_const); if (k > 0) { - y.unused(); + y.replaced(); std::string op = "LSHIFT#DIV"; if (c) { op += c; @@ -866,7 +866,7 @@ AsmOp compile_muldiv(std::vector& res, std::vector& args, co if (x.is_int_const()) { int k = is_pos_pow2(x.int_const); if (k > 0) { - x.unused(); + x.replaced(); std::string op = "LSHIFT#DIV"; if (c) { op += c; @@ -929,8 +929,8 @@ AsmOp compile_cmp_int(std::vector& res, std::vector& args, i if (x.is_int_const() && y.is_int_const()) { int v = compute_compare(x.int_const, y.int_const, mode); r.set_const(v); - x.unused(); - y.unused(); + x.replaced(); + y.replaced(); return mode == 7 ? push_const(r.int_const) : AsmOp::BoolConst(v != 0); } int v = compute_compare(x, y, mode); @@ -938,8 +938,8 @@ AsmOp compile_cmp_int(std::vector& res, std::vector& args, i func_assert(v); if (!(v & (v - 1))) { r.set_const(v - (v >> 2) - 2); - x.unused(); - y.unused(); + x.replaced(); + y.replaced(); return mode == 7 ? push_const(r.int_const) : AsmOp::BoolConst(v & 1); } r.val = ~0; @@ -958,11 +958,11 @@ AsmOp compile_cmp_int(std::vector& res, std::vector& args, i static int cmp_int_delta[] = {0, 0, 0, -1, 0, 0, 1}; if (mode != 7) { if (y.is_int_const() && y.int_const >= -128 && y.int_const <= 127) { - y.unused(); + y.replaced(); return exec_arg_op(cmp_int_names[mode], y.int_const + cmp_int_delta[mode], 1); } if (x.is_int_const() && x.int_const >= -128 && x.int_const <= 127) { - x.unused(); + x.replaced(); mode = ((mode & 4) >> 2) | (mode & 2) | ((mode & 1) << 2); return exec_arg_op(cmp_int_names[mode], x.int_const + cmp_int_delta[mode], 1); } @@ -974,7 +974,7 @@ AsmOp compile_throw(std::vector& res, std::vector& args, con func_assert(res.empty() && args.size() == 1); VarDescr& x = args[0]; if (x.is_int_const() && x.int_const->unsigned_fits_bits(11)) { - x.unused(); + x.replaced(); return exec_arg_op("THROW", x.int_const, 0, 0); } else { return exec_op("THROWANY", 1, 0); @@ -987,15 +987,15 @@ AsmOp compile_cond_throw(std::vector& res, std::vector& args std::string suff = (mode ? "IF" : "IFNOT"); bool skip_cond = false; if (y.always_true() || y.always_false()) { - y.unused(); + y.replaced(); skip_cond = true; if (y.always_true() != mode) { - x.unused(); + x.replaced(); return AsmOp::Nop(); } } if (x.is_int_const() && x.int_const->unsigned_fits_bits(11)) { - x.unused(); + x.replaced(); return skip_cond ? exec_arg_op("THROW", x.int_const, 0, 0) : exec_arg_op("THROW"s + suff, x.int_const, 1, 0); } else { return skip_cond ? exec_op("THROWANY", 1, 0) : exec_op("THROWANY"s + suff, 2, 0); @@ -1006,7 +1006,7 @@ AsmOp compile_throw_arg(std::vector& res, std::vector& args, func_assert(res.empty() && args.size() == 2); VarDescr &x = args[1]; if (x.is_int_const() && x.int_const->unsigned_fits_bits(11)) { - x.unused(); + x.replaced(); return exec_arg_op("THROWARG", x.int_const, 1, 0); } else { return exec_op("THROWARGANY", 2, 0); @@ -1019,15 +1019,15 @@ AsmOp compile_cond_throw_arg(std::vector& res, std::vector& std::string suff = (mode ? "IF" : "IFNOT"); bool skip_cond = false; if (y.always_true() || y.always_false()) { - y.unused(); + y.replaced(); skip_cond = true; if (y.always_true() != mode) { - x.unused(); + x.replaced(); return AsmOp::Nop(); } } if (x.is_int_const() && x.int_const->unsigned_fits_bits(11)) { - x.unused(); + x.replaced(); return skip_cond ? exec_arg_op("THROWARG", x.int_const, 1, 0) : exec_arg_op("THROWARG"s + suff, x.int_const, 2, 0); } else { return skip_cond ? exec_op("THROWARGANY", 2, 0) : exec_op("THROWARGANY"s + suff, 3, 0); @@ -1059,7 +1059,7 @@ AsmOp compile_fetch_int(std::vector& res, std::vector& args, r.val = (sgnd ? VarDescr::ValBool : VarDescr::ValBit); } if (v > 0) { - y.unused(); + y.replaced(); return exec_arg_op((fetch ? "LD"s : "PLD"s) + (sgnd ? 'I' : 'U'), v, 1, 1 + (unsigned)fetch); } } @@ -1072,7 +1072,7 @@ AsmOp compile_store_int(std::vector& res, std::vector& args, func_assert(args.size() == 3 && res.size() == 1); auto& z = args[2]; if (z.is_int_const() && z.int_const > 0 && z.int_const <= 256) { - z.unused(); + z.replaced(); return exec_arg_op("ST"s + (sgnd ? 'I' : 'U'), z.int_const, 2, 1); } return exec_op("ST"s + (sgnd ? "IX" : "UX"), 3, 1); @@ -1085,7 +1085,7 @@ AsmOp compile_fetch_slice(std::vector& res, std::vector& arg if (y.is_int_const() && y.int_const > 0 && y.int_const <= 256) { v = (int)y.int_const->to_long(); if (v > 0) { - y.unused(); + y.replaced(); return exec_arg_op(fetch ? "LDSLICE" : "PLDSLICE", v, 1, 1 + (unsigned)fetch); } } @@ -1097,7 +1097,7 @@ AsmOp compile_tuple_at(std::vector& res, std::vector& args, func_assert(args.size() == 2 && res.size() == 1); auto& y = args[1]; if (y.is_int_const() && y.int_const >= 0 && y.int_const < 16) { - y.unused(); + y.replaced(); return exec_arg_op("INDEX", y.int_const, 1, 1); } return exec_op("INDEXVAR", 2, 1); @@ -1108,7 +1108,7 @@ AsmOp compile_is_null(std::vector& res, std::vector& args, c func_assert(args.size() == 1 && res.size() == 1); auto &x = args[0], &r = res[0]; if (x.always_null() || x.always_not_null()) { - x.unused(); + x.replaced(); r.set_const(x.always_null() ? -1 : 0); return push_const(r.int_const); } @@ -1121,7 +1121,7 @@ bool compile_run_method(AsmOpList& code, std::vector& res, std::vector func_assert(args.size() == (unsigned)n + 1 && res.size() == (unsigned)has_value); auto& x = args[0]; if (x.is_int_const() && x.int_const->unsigned_fits_bits(14)) { - x.unused(); + x.replaced(); code << exec_arg_op("PREPAREDICT", x.int_const, 0, 2); } else { code << exec_op("c3 PUSH", 0, 1); diff --git a/crypto/func/codegen.cpp b/crypto/func/codegen.cpp index aa2972c2..3b496895 100644 --- a/crypto/func/codegen.cpp +++ b/crypto/func/codegen.cpp @@ -374,10 +374,13 @@ bool Op::generate_code_step(Stack& stack) { int i = 0; std::vector active; active.reserve(left.size()); + int unused = 0; for (std::size_t k = 0; k < left.size(); k++) { var_idx_t y = left[k]; // "y" = "x" auto p = next->var_info[y]; active.push_back(p && !p->is_unused()); + if (p && p->is_unused() && !p->is_replaced()) + ++unused; } for (std::size_t k = 0; k < left.size(); k++) { if (!active[k]) { @@ -406,6 +409,16 @@ bool Op::generate_code_step(Stack& stack) { stack.assign_var(left[k], --i); } } + if (funC::warn_unused >= 1 && unused > 0) { + where.show(std::cerr); + if (left.size() != 1) { + std::cerr << "\tWarning: unused " << unused + << " out of " << left.size() << " assigned variables" << std::endl; + } else { + std::cerr << "\tWarning: unused variable assignment" << std::endl; + } + where.show_context(std::cerr); + } return true; } case _Tuple: @@ -437,6 +450,16 @@ bool Op::generate_code_step(Stack& stack) { case _Call: case _CallInd: { if (disabled()) { + if (funC::warn_unused >= 2 && !replaced()) { + where.show(std::cerr); + std::cerr << "\tWarning: unused "; + if (cl == _Call) + std::cerr << "call"; + else + std::cerr << "indirect call"; + std::cerr << " to " << fun_ref->name() << "\n"; + where.show_context(std::cerr); + } return true; } SymValFunc* func = (fun_ref ? dynamic_cast(fun_ref->value) : nullptr); diff --git a/crypto/func/func-main.cpp b/crypto/func/func-main.cpp index c8208eee..7ba807d9 100644 --- a/crypto/func/func-main.cpp +++ b/crypto/func/func-main.cpp @@ -49,6 +49,7 @@ void usage(const char* progname) { "-R\tInclude operation rewrite comments in the output code\n" "-W\tInclude Fift code to serialize and save generated code into specified BoC file. Enables " "-A and -P.\n" + "-u\tEnable warnings about unused calls and variables (once for assigns, twice also for calls)\n" "\t-s\tOutput semantic version of FunC and exit\n" "\t-V\tShow func build information\n"; std::exit(2); @@ -57,7 +58,7 @@ void usage(const char* progname) { int main(int argc, char* const argv[]) { int i; std::string output_filename; - while ((i = getopt(argc, argv, "Ahi:Io:O:PRsSvW:V")) != -1) { + while ((i = getopt(argc, argv, "Ahi:Io:O:PRsSuvW:V")) != -1) { switch (i) { case 'A': funC::asm_preamble = true; @@ -83,6 +84,9 @@ int main(int argc, char* const argv[]) { case 'S': funC::stack_layout_comments = true; break; + case 'u': + ++funC::warn_unused; + break; case 'v': ++funC::verbosity; break; diff --git a/crypto/func/func.cpp b/crypto/func/func.cpp index 39648c05..749cbefa 100644 --- a/crypto/func/func.cpp +++ b/crypto/func/func.cpp @@ -35,7 +35,7 @@ namespace funC { -int verbosity, indent, opt_level = 2; +int verbosity, indent, opt_level = 2, warn_unused; bool stack_layout_comments, op_rewrite_comments, program_envelope, asm_preamble; bool interactive = false; GlobalPragma pragma_allow_post_modification{"allow-post-modification"}; diff --git a/crypto/func/func.h b/crypto/func/func.h index 25711db8..23198863 100644 --- a/crypto/func/func.h +++ b/crypto/func/func.h @@ -39,7 +39,7 @@ namespace funC { -extern int verbosity; +extern int verbosity, warn_unused; extern bool op_rewrite_comments; extern std::string generated_from; @@ -328,7 +328,7 @@ struct TmpVar { struct VarDescr { var_idx_t idx; - enum { _Last = 1, _Unused = 2 }; + enum { _Last = 1, _Unused = 2, _Replaced = 4 }; int flags; enum { _Const = 16, @@ -365,6 +365,9 @@ struct VarDescr { bool is_unused() const { return flags & _Unused; } + bool is_replaced() const { + return flags & _Replaced; + } bool is_last() const { return flags & _Last; } @@ -422,8 +425,16 @@ struct VarDescr { void unused() { flags |= _Unused; } + void replaced() { + unused(); // replaced var is always unused + flags |= _Replaced; + } void clear_unused() { flags &= ~_Unused; + clear_replaced(); + } + void clear_replaced() { + flags &= ~_Replaced; } void set_const(long long value); void set_const(td::RefInt256 value); @@ -474,12 +485,13 @@ struct VarDescrList { VarDescrList& operator+=(const std::vector& idx_list) { return add_vars(idx_list); } - VarDescrList& add_var(var_idx_t idx, bool unused = false); - VarDescrList& add_vars(const std::vector& idx_list, bool unused = false); + VarDescrList& add_var(var_idx_t idx, bool unused = false, bool replaced = false); + VarDescrList& add_vars(const std::vector& idx_list, bool unused = false, bool replaced = false); VarDescrList& operator-=(const std::vector& idx_list); VarDescrList& operator-=(var_idx_t idx); std::size_t count(const std::vector idx_list) const; std::size_t count_used(const std::vector idx_list) const; + std::size_t count_unreplaced(const std::vector idx_list) const; VarDescr& add(var_idx_t idx); VarDescr& add_newval(var_idx_t idx); VarDescrList& operator&=(const VarDescrList& values); @@ -557,7 +569,7 @@ struct Op { _SliceConst }; int cl; - enum { _Disabled = 1, _Reachable = 2, _NoReturn = 4, _ImpureR = 8, _ImpureW = 16, _Impure = 24 }; + enum { _Disabled = 1, _Reachable = 2, _NoReturn = 4, _ImpureR = 8, _ImpureW = 16, _Impure = 24, _Replaced = 32 }; int flags; std::unique_ptr next; SymDef* fun_ref; @@ -593,12 +605,19 @@ struct Op { bool disabled() const { return flags & _Disabled; } + bool replaced() const { + return flags & _Replaced; + } bool enabled() const { return !disabled(); } void disable() { flags |= _Disabled; } + void replace() { + disable(); + flags |= _Replaced; + } bool unreachable() { return !(flags & _Reachable); } @@ -611,7 +630,7 @@ struct Op { void split_vars(const std::vector& vars); static void split_var_list(std::vector& var_list, const std::vector& vars); bool compute_used_vars(const CodeBlob& code, bool edit); - bool std_compute_used_vars(bool disabled = false); + bool std_compute_used_vars(bool disabled = false, bool replaced = false); bool set_var_info(const VarDescrList& new_var_info); bool set_var_info(VarDescrList&& new_var_info); bool set_var_info_except(const VarDescrList& new_var_info, const std::vector& var_list);