mirror of
https://github.com/ton-blockchain/ton
synced 2025-03-09 15:40:10 +00:00
Highly refined algorithm of detection of builtins and unused vars (-u, -uu)
Now builtin replacements are marked with special _Replaced flag Unused calls and variables only have the _Unused flag This way, it is possbile to separate builtin-precalculated from really unused vars Added -u and -uu flags that print out optimized out (disabled) calls and assigns -u prints only unused assigns while -uu prints also all unused calls (to stderr) Such option allows to find out all sorts of different bugs: copy-paste, incorrect pure/impure markings, mistypes or accidental variable redefinitions, etc.
This commit is contained in:
parent
acf16718e6
commit
2008feb907
6 changed files with 149 additions and 79 deletions
|
@ -285,6 +285,7 @@ void Op::show(std::ostream& os, const std::vector<TmpVar>& vars, std::string pfx
|
|||
os << " ]\n";
|
||||
}
|
||||
std::string dis = disabled() ? "<disabled> " : "";
|
||||
if (replaced()) dis = "<replaced> "; // not just disabled
|
||||
if (noreturn()) {
|
||||
dis += "<noret> ";
|
||||
}
|
||||
|
@ -426,6 +427,9 @@ void Op::show_var_list(std::ostream& os, const std::vector<VarDescr>& 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);
|
||||
}
|
||||
|
|
|
@ -185,6 +185,17 @@ std::size_t VarDescrList::count_used(const std::vector<var_idx_t> idx_list) cons
|
|||
return res;
|
||||
}
|
||||
|
||||
std::size_t VarDescrList::count_unreplaced(const std::vector<var_idx_t> 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) {
|
||||
|
@ -200,19 +211,22 @@ VarDescrList& VarDescrList::operator-=(const std::vector<var_idx_t>& 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<var_idx_t>& idx_list, bool unused) {
|
||||
VarDescrList& VarDescrList::add_vars(const std::vector<var_idx_t>& idx_list, bool unused, bool replaced) {
|
||||
for (var_idx_t idx : idx_list) {
|
||||
add_var(idx, unused);
|
||||
add_var(idx, unused, replaced);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
@ -328,7 +342,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};
|
||||
|
@ -336,10 +350,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));
|
||||
}
|
||||
|
@ -358,10 +372,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();
|
||||
}
|
||||
|
@ -375,6 +392,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);
|
||||
assert(left.size() == right.size());
|
||||
auto l_it = left.cbegin(), r_it = right.cbegin();
|
||||
VarDescrList new_var_info{next_var_info};
|
||||
|
@ -384,7 +402,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, !p || p->is_unused());
|
||||
new_var_info.add_var(*r_it, !p || p->is_unused(), p && p->is_replaced());
|
||||
new_left.push_back(*l_it);
|
||||
new_right.push_back(*r_it);
|
||||
}
|
||||
|
@ -396,6 +414,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));
|
||||
}
|
||||
|
|
|
@ -351,13 +351,13 @@ AsmOp compile_add(std::vector<VarDescr>& res, std::vector<VarDescr>& args) {
|
|||
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_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();
|
||||
}
|
||||
|
@ -370,7 +370,7 @@ AsmOp compile_add(std::vector<VarDescr>& res, std::vector<VarDescr>& args) {
|
|||
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();
|
||||
}
|
||||
|
@ -390,13 +390,13 @@ AsmOp compile_sub(std::vector<VarDescr>& res, std::vector<VarDescr>& args) {
|
|||
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_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 {};
|
||||
}
|
||||
|
@ -409,7 +409,7 @@ AsmOp compile_sub(std::vector<VarDescr>& res, std::vector<VarDescr>& args) {
|
|||
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);
|
||||
|
@ -420,7 +420,7 @@ AsmOp compile_negate(std::vector<VarDescr>& res, std::vector<VarDescr>& args) {
|
|||
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_negate(x.val);
|
||||
|
@ -432,15 +432,15 @@ AsmOp compile_mul(std::vector<VarDescr>& res, std::vector<VarDescr>& args) {
|
|||
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_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);
|
||||
|
@ -455,18 +455,18 @@ AsmOp compile_mul(std::vector<VarDescr>& res, std::vector<VarDescr>& args) {
|
|||
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);
|
||||
|
@ -481,11 +481,11 @@ AsmOp compile_mul(std::vector<VarDescr>& res, std::vector<VarDescr>& args) {
|
|||
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();
|
||||
}
|
||||
}
|
||||
|
@ -499,13 +499,13 @@ AsmOp compile_lshift(std::vector<VarDescr>& res, std::vector<VarDescr>& args) {
|
|||
auto yv = y.int_const->to_long();
|
||||
if (yv < 0 || yv > 256) {
|
||||
r.set_const_nan();
|
||||
x.unused();
|
||||
y.unused();
|
||||
x.replaced();
|
||||
y.replaced();
|
||||
return push_const(r.int_const);
|
||||
} else if (x.is_int_const()) {
|
||||
r.set_const(x.int_const << (int)yv);
|
||||
x.unused();
|
||||
y.unused();
|
||||
x.replaced();
|
||||
y.replaced();
|
||||
return push_const(r.int_const);
|
||||
}
|
||||
}
|
||||
|
@ -514,20 +514,20 @@ AsmOp compile_lshift(std::vector<VarDescr>& res, std::vector<VarDescr>& args) {
|
|||
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("NEGPOW2", 1);
|
||||
}
|
||||
}
|
||||
|
@ -541,13 +541,13 @@ AsmOp compile_rshift(std::vector<VarDescr>& res, std::vector<VarDescr>& args, in
|
|||
auto yv = y.int_const->to_long();
|
||||
if (yv < 0 || yv > 256) {
|
||||
r.set_const_nan();
|
||||
x.unused();
|
||||
y.unused();
|
||||
x.replaced();
|
||||
y.replaced();
|
||||
return push_const(r.int_const);
|
||||
} 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);
|
||||
}
|
||||
}
|
||||
|
@ -557,10 +557,10 @@ AsmOp compile_rshift(std::vector<VarDescr>& res, std::vector<VarDescr>& args, in
|
|||
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);
|
||||
|
@ -571,29 +571,29 @@ AsmOp compile_div(std::vector<VarDescr>& res, std::vector<VarDescr>& args, int r
|
|||
VarDescr &r = res[0], &x = args[0], &y = args[1];
|
||||
if (x.is_int_const() && y.is_int_const()) {
|
||||
r.set_const(div(x.int_const, y.int_const, round_mode));
|
||||
x.unused();
|
||||
y.unused();
|
||||
x.replaced();
|
||||
y.replaced();
|
||||
return push_const(r.int_const);
|
||||
}
|
||||
r.val = emulate_div(x.val, y.val);
|
||||
if (y.is_int_const()) {
|
||||
if (*y.int_const == 0) {
|
||||
x.unused();
|
||||
y.unused();
|
||||
x.replaced();
|
||||
y.replaced();
|
||||
r.set_const(div(y.int_const, y.int_const));
|
||||
return push_const(r.int_const);
|
||||
}
|
||||
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');
|
||||
|
@ -613,27 +613,27 @@ AsmOp compile_mod(std::vector<VarDescr>& res, std::vector<VarDescr>& args, int r
|
|||
VarDescr &r = res[0], &x = args[0], &y = args[1];
|
||||
if (x.is_int_const() && y.is_int_const()) {
|
||||
r.set_const(mod(x.int_const, y.int_const, round_mode));
|
||||
x.unused();
|
||||
y.unused();
|
||||
x.replaced();
|
||||
y.replaced();
|
||||
return push_const(r.int_const);
|
||||
}
|
||||
r.val = emulate_mod(x.val, y.val);
|
||||
if (y.is_int_const()) {
|
||||
if (*y.int_const == 0) {
|
||||
x.unused();
|
||||
y.unused();
|
||||
x.replaced();
|
||||
y.replaced();
|
||||
r.set_const(mod(y.int_const, y.int_const));
|
||||
return push_const(r.int_const);
|
||||
}
|
||||
if ((*y.int_const == 1 || *y.int_const == -1) && x.always_finite()) {
|
||||
x.unused();
|
||||
y.unused();
|
||||
x.replaced();
|
||||
y.replaced();
|
||||
r.set_const(td::RefInt256{true, 0});
|
||||
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');
|
||||
|
@ -696,8 +696,8 @@ AsmOp compile_cmp_int(std::vector<VarDescr>& res, std::vector<VarDescr>& 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);
|
||||
|
@ -705,8 +705,8 @@ AsmOp compile_cmp_int(std::vector<VarDescr>& res, std::vector<VarDescr>& args, i
|
|||
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;
|
||||
|
@ -725,11 +725,11 @@ AsmOp compile_cmp_int(std::vector<VarDescr>& res, std::vector<VarDescr>& 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);
|
||||
}
|
||||
|
@ -741,7 +741,7 @@ AsmOp compile_throw(std::vector<VarDescr>& res, std::vector<VarDescr>& args) {
|
|||
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);
|
||||
|
@ -754,15 +754,15 @@ AsmOp compile_cond_throw(std::vector<VarDescr>& res, std::vector<VarDescr>& 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_arg_op("THROWANY"s + suff, 2, 0);
|
||||
|
@ -794,7 +794,7 @@ AsmOp compile_fetch_int(std::vector<VarDescr>& res, std::vector<VarDescr>& 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);
|
||||
}
|
||||
}
|
||||
|
@ -807,7 +807,7 @@ AsmOp compile_store_int(std::vector<VarDescr>& res, std::vector<VarDescr>& args,
|
|||
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);
|
||||
|
@ -820,7 +820,7 @@ AsmOp compile_fetch_slice(std::vector<VarDescr>& res, std::vector<VarDescr>& 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);
|
||||
}
|
||||
}
|
||||
|
@ -832,7 +832,7 @@ AsmOp compile_tuple_at(std::vector<VarDescr>& res, std::vector<VarDescr>& args)
|
|||
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);
|
||||
|
@ -843,7 +843,7 @@ AsmOp compile_is_null(std::vector<VarDescr>& res, std::vector<VarDescr>& args) {
|
|||
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);
|
||||
}
|
||||
|
@ -856,7 +856,7 @@ bool compile_run_method(AsmOpList& code, std::vector<VarDescr>& res, std::vector
|
|||
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);
|
||||
|
|
|
@ -362,10 +362,13 @@ bool Op::generate_code_step(Stack& stack) {
|
|||
int i = 0;
|
||||
std::vector<bool> 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]) {
|
||||
|
@ -394,11 +397,31 @@ 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 _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<SymValFunc*>(fun_ref->value) : nullptr);
|
||||
|
|
|
@ -34,7 +34,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;
|
||||
std::ostream* outs = &std::cout;
|
||||
std::string generated_from, boc_output_filename;
|
||||
|
@ -171,7 +171,8 @@ void usage(const char* progname) {
|
|||
"-S\tInclude stack layout comments in the output code\n"
|
||||
"-R\tInclude operation rewrite comments in the output code\n"
|
||||
"-W<output-boc-file>\tInclude Fift code to serialize and save generated code into specified BoC file. Enables "
|
||||
"-A and -P.\n";
|
||||
"-A and -P.\n"
|
||||
"-u\tEnable warnings about unused calls and variables (once for assigns, twice also for calls)\n";
|
||||
std::exit(2);
|
||||
}
|
||||
|
||||
|
@ -180,7 +181,7 @@ std::string output_filename;
|
|||
int main(int argc, char* const argv[]) {
|
||||
int i;
|
||||
bool interactive = false;
|
||||
while ((i = getopt(argc, argv, "Ahi:Io:O:PRSvW:")) != -1) {
|
||||
while ((i = getopt(argc, argv, "Ahi:Io:O:PRSuvW:")) != -1) {
|
||||
switch (i) {
|
||||
case 'A':
|
||||
funC::asm_preamble = true;
|
||||
|
@ -206,6 +207,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;
|
||||
|
|
|
@ -33,7 +33,7 @@
|
|||
|
||||
namespace funC {
|
||||
|
||||
extern int verbosity;
|
||||
extern int verbosity, warn_unused;
|
||||
extern bool op_rewrite_comments;
|
||||
|
||||
constexpr int optimize_depth = 12;
|
||||
|
@ -290,7 +290,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,
|
||||
|
@ -325,6 +325,9 @@ struct VarDescr {
|
|||
bool is_unused() const {
|
||||
return flags & _Unused;
|
||||
}
|
||||
bool is_replaced() const {
|
||||
return flags & _Replaced;
|
||||
}
|
||||
bool is_last() const {
|
||||
return flags & _Last;
|
||||
}
|
||||
|
@ -382,8 +385,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);
|
||||
|
@ -433,12 +444,13 @@ struct VarDescrList {
|
|||
VarDescrList& operator+=(const std::vector<var_idx_t>& idx_list) {
|
||||
return add_vars(idx_list);
|
||||
}
|
||||
VarDescrList& add_var(var_idx_t idx, bool unused = false);
|
||||
VarDescrList& add_vars(const std::vector<var_idx_t>& idx_list, bool unused = false);
|
||||
VarDescrList& add_var(var_idx_t idx, bool unused = false, bool replaced = false);
|
||||
VarDescrList& add_vars(const std::vector<var_idx_t>& idx_list, bool unused = false, bool replaced = false);
|
||||
VarDescrList& operator-=(const std::vector<var_idx_t>& idx_list);
|
||||
VarDescrList& operator-=(var_idx_t idx);
|
||||
std::size_t count(const std::vector<var_idx_t> idx_list) const;
|
||||
std::size_t count_used(const std::vector<var_idx_t> idx_list) const;
|
||||
std::size_t count_unreplaced(const std::vector<var_idx_t> idx_list) const;
|
||||
VarDescr& add(var_idx_t idx);
|
||||
VarDescr& add_newval(var_idx_t idx);
|
||||
VarDescrList& operator&=(const VarDescrList& values);
|
||||
|
@ -512,7 +524,7 @@ struct Op {
|
|||
_Again
|
||||
};
|
||||
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<Op> next;
|
||||
SymDef* fun_ref;
|
||||
|
@ -544,12 +556,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);
|
||||
}
|
||||
|
@ -562,7 +581,7 @@ struct Op {
|
|||
void split_vars(const std::vector<TmpVar>& vars);
|
||||
static void split_var_list(std::vector<var_idx_t>& var_list, const std::vector<TmpVar>& 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_idx_t>& var_list);
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue