1
0
Fork 0
mirror of https://github.com/ton-blockchain/ton synced 2025-03-09 15:40:10 +00:00
This commit is contained in:
Oleksandr 2025-02-07 09:25:56 +08:00 committed by GitHub
commit 083c841b1e
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 164 additions and 94 deletions

View file

@ -293,6 +293,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> ";
}
@ -453,6 +454,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);
}

View file

@ -187,6 +187,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) {
@ -202,19 +213,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;
}
@ -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));
}

View file

@ -436,13 +436,13 @@ AsmOp compile_add(std::vector<VarDescr>& res, std::vector<VarDescr>& 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<VarDescr>& res, std::vector<VarDescr>& 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<VarDescr>& res, std::vector<VarDescr>& 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<VarDescr>& res, std::vector<VarDescr>& 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<VarDescr>& res, std::vector<VarDescr>& 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<VarDescr>& res, std::vector<VarDescr>& 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<VarDescr>& res, std::vector<VarDescr>& 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<VarDescr>& res, std::vector<VarDescr>& 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<VarDescr>& res, std::vector<VarDescr>& 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<VarDescr>& res, std::vector<VarDescr>& 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<VarDescr>& res, std::vector<VarDescr>& 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<VarDescr>& res, std::vector<VarDescr>& 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<VarDescr>& res, std::vector<VarDescr>& 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<VarDescr>& res, std::vector<VarDescr>& 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<VarDescr>& res, std::vector<VarDescr>& 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<VarDescr>& res, std::vector<VarDescr>& 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<VarDescr>& res, std::vector<VarDescr>& 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<VarDescr>& res, std::vector<VarDescr>& 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<VarDescr>& res, std::vector<VarDescr>& 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<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);
@ -938,8 +938,8 @@ AsmOp compile_cmp_int(std::vector<VarDescr>& res, std::vector<VarDescr>& 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<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);
}
@ -974,7 +974,7 @@ AsmOp compile_throw(std::vector<VarDescr>& res, std::vector<VarDescr>& 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<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_op("THROWANY"s + suff, 2, 0);
@ -1006,7 +1006,7 @@ AsmOp compile_throw_arg(std::vector<VarDescr>& res, std::vector<VarDescr>& 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<VarDescr>& res, std::vector<VarDescr>&
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<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);
}
}
@ -1072,7 +1072,7 @@ AsmOp compile_store_int(std::vector<VarDescr>& res, std::vector<VarDescr>& 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<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);
}
}
@ -1097,7 +1097,7 @@ AsmOp compile_tuple_at(std::vector<VarDescr>& res, std::vector<VarDescr>& 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<VarDescr>& res, std::vector<VarDescr>& 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<VarDescr>& 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);

View file

@ -374,10 +374,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]) {
@ -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<SymValFunc*>(fun_ref->value) : nullptr);

View file

@ -49,6 +49,7 @@ void usage(const char* progname) {
"-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"
"-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<version>\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;

View file

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

View file

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