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"; os << " ]\n";
} }
std::string dis = disabled() ? "<disabled> " : ""; std::string dis = disabled() ? "<disabled> " : "";
if (replaced()) dis = "<replaced> "; // not just disabled
if (noreturn()) { if (noreturn()) {
dis += "<noret> "; 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()) { if (list[i].is_unused()) {
os << '?'; os << '?';
} }
if (list[i].is_replaced()) {
os << "#";
}
os << vars.at(list[i].idx) << ':'; os << vars.at(list[i].idx) << ':';
list[i].show_value(os); 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; 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) { VarDescrList& VarDescrList::operator-=(var_idx_t idx) {
auto it = std::lower_bound(list.begin(), list.end(), idx); auto it = std::lower_bound(list.begin(), list.end(), idx);
if (it != list.end() && it->idx == 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; 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); auto it = std::lower_bound(list.begin(), list.end(), idx);
if (it == list.end() || it->idx != 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) { } else if (it->is_unused() && !unused) {
it->clear_unused(); it->clear_unused();
} else if (it->is_replaced() && !replaced) {
it->clear_replaced();
} }
return *this; 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) { for (var_idx_t idx : idx_list) {
add_var(idx, unused); add_var(idx, unused, replaced);
} }
return *this; return *this;
} }
@ -330,7 +344,7 @@ VarDescrList& VarDescrList::import_values(const VarDescrList& values) {
return *this; return *this;
} }
bool Op::std_compute_used_vars(bool disabled) { bool Op::std_compute_used_vars(bool disabled, bool replaced) {
// left = OP right // left = OP right
// var_info := (var_info - left) + right // var_info := (var_info - left) + right
VarDescrList new_var_info{next->var_info}; VarDescrList new_var_info{next->var_info};
@ -338,10 +352,10 @@ bool Op::std_compute_used_vars(bool disabled) {
new_var_info.clear_last(); new_var_info.clear_last();
if (args.size() == right.size() && !disabled) { if (args.size() == right.size() && !disabled) {
for (const VarDescr& arg : args) { 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 { } 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)); 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; // left = EXEC right;
if (!next_var_info.count_used(left) && is_pure()) { if (!next_var_info.count_used(left) && is_pure()) {
// all variables in `left` are not needed // all variables in `left` are not needed
bool repl = left.size() && !next_var_info.count_unreplaced(left);
if (edit) { if (edit) {
disable(); 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(); return std_compute_used_vars();
} }
@ -380,6 +397,7 @@ bool Op::compute_used_vars(const CodeBlob& code, bool edit) {
case _Let: { case _Let: {
// left = right // left = right
std::size_t cnt = next_var_info.count_used(left); 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()); func_assert(left.size() == right.size());
auto l_it = left.cbegin(), r_it = right.cbegin(); auto l_it = left.cbegin(), r_it = right.cbegin();
VarDescrList new_var_info{next_var_info}; 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) { for (; l_it < left.cend(); ++l_it, ++r_it) {
if (std::find(l_it + 1, left.cend(), *l_it) == left.cend()) { if (std::find(l_it + 1, left.cend(), *l_it) == left.cend()) {
auto p = next_var_info[*l_it]; 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_left.push_back(*l_it);
new_right.push_back(*r_it); new_right.push_back(*r_it);
} }
@ -401,6 +419,8 @@ bool Op::compute_used_vars(const CodeBlob& code, bool edit) {
if (!cnt && edit) { if (!cnt && edit) {
// all variables in `left` are not needed // all variables in `left` are not needed
disable(); disable();
if (left.size() && !unr)
replaced();
} }
return set_var_info(std::move(new_var_info)); 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()) { if (!r.int_const->is_valid()) {
throw src::ParseError(where, "integer overflow"); throw src::ParseError(where, "integer overflow");
} }
x.unused(); x.replaced();
y.unused(); y.replaced();
return push_const(r.int_const); return push_const(r.int_const);
} }
r.val = emulate_add(x.val, y.val); r.val = emulate_add(x.val, y.val);
if (y.is_int_const() && y.int_const->signed_fits_bits(8)) { if (y.is_int_const() && y.int_const->signed_fits_bits(8)) {
y.unused(); y.replaced();
if (y.always_zero()) { if (y.always_zero()) {
return AsmOp::Nop(); 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); return exec_arg_op("ADDCONST", y.int_const, 1);
} }
if (x.is_int_const() && x.int_const->signed_fits_bits(8)) { if (x.is_int_const() && x.int_const->signed_fits_bits(8)) {
x.unused(); x.replaced();
if (x.always_zero()) { if (x.always_zero()) {
return AsmOp::Nop(); 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()) { if (!r.int_const->is_valid()) {
throw src::ParseError(where, "integer overflow"); throw src::ParseError(where, "integer overflow");
} }
x.unused(); x.replaced();
y.unused(); y.replaced();
return push_const(r.int_const); return push_const(r.int_const);
} }
r.val = emulate_sub(x.val, y.val); r.val = emulate_sub(x.val, y.val);
if (y.is_int_const() && (-y.int_const)->signed_fits_bits(8)) { if (y.is_int_const() && (-y.int_const)->signed_fits_bits(8)) {
y.unused(); y.replaced();
if (y.always_zero()) { if (y.always_zero()) {
return {}; 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); return exec_arg_op("ADDCONST", -y.int_const, 1);
} }
if (x.always_zero()) { if (x.always_zero()) {
x.unused(); x.replaced();
return exec_op("NEGATE", 1); return exec_op("NEGATE", 1);
} }
return exec_op("SUB", 2); 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()) { if (!r.int_const->is_valid()) {
throw src::ParseError(where, "integer overflow"); throw src::ParseError(where, "integer overflow");
} }
x.unused(); x.replaced();
return push_const(r.int_const); return push_const(r.int_const);
} }
r.val = emulate_negate(x.val); 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]; VarDescr &r = res[0], &x = args[0], &y = args[1];
if (x.is_int_const() && y.is_int_const()) { if (x.is_int_const() && y.is_int_const()) {
r.set_const(x.int_const & y.int_const); r.set_const(x.int_const & y.int_const);
x.unused(); x.replaced();
y.unused(); y.replaced();
return push_const(r.int_const); return push_const(r.int_const);
} }
r.val = emulate_and(x.val, y.val); 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]; VarDescr &r = res[0], &x = args[0], &y = args[1];
if (x.is_int_const() && y.is_int_const()) { if (x.is_int_const() && y.is_int_const()) {
r.set_const(x.int_const | y.int_const); r.set_const(x.int_const | y.int_const);
x.unused(); x.replaced();
y.unused(); y.replaced();
return push_const(r.int_const); return push_const(r.int_const);
} }
r.val = emulate_or(x.val, y.val); 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]; VarDescr &r = res[0], &x = args[0], &y = args[1];
if (x.is_int_const() && y.is_int_const()) { if (x.is_int_const() && y.is_int_const()) {
r.set_const(x.int_const ^ y.int_const); r.set_const(x.int_const ^ y.int_const);
x.unused(); x.replaced();
y.unused(); y.replaced();
return push_const(r.int_const); return push_const(r.int_const);
} }
r.val = emulate_xor(x.val, y.val); 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]; VarDescr &r = res[0], &x = args[0];
if (x.is_int_const()) { if (x.is_int_const()) {
r.set_const(~x.int_const); r.set_const(~x.int_const);
x.unused(); x.replaced();
return push_const(r.int_const); return push_const(r.int_const);
} }
r.val = emulate_not(x.val); 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()) { if (!r.int_const->is_valid()) {
throw src::ParseError(where, "integer overflow"); throw src::ParseError(where, "integer overflow");
} }
x.unused(); x.replaced();
y.unused(); y.replaced();
return push_const(r.int_const); return push_const(r.int_const);
} }
r.val = emulate_mul(x.val, y.val); r.val = emulate_mul(x.val, y.val);
if (y.is_int_const()) { if (y.is_int_const()) {
int k = is_pos_pow2(y.int_const); int k = is_pos_pow2(y.int_const);
if (y.int_const->signed_fits_bits(8) && k < 0) { if (y.int_const->signed_fits_bits(8) && k < 0) {
y.unused(); y.replaced();
if (y.always_zero() && x.always_finite()) { if (y.always_zero() && x.always_finite()) {
// dubious optimization: NaN * 0 = ? // dubious optimization: NaN * 0 = ?
r.set_const(y.int_const); r.set_const(y.int_const);
x.unused(); x.replaced();
return push_const(r.int_const); return push_const(r.int_const);
} }
if (*y.int_const == 1 && x.always_finite()) { 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); return exec_arg_op("MULCONST", y.int_const, 1);
} }
if (k > 0) { if (k > 0) {
y.unused(); y.replaced();
return exec_arg_op("LSHIFT#", k, 1); return exec_arg_op("LSHIFT#", k, 1);
} }
if (k == 0) { if (k == 0) {
y.unused(); y.replaced();
return AsmOp::Nop(); return AsmOp::Nop();
} }
} }
if (x.is_int_const()) { if (x.is_int_const()) {
int k = is_pos_pow2(x.int_const); int k = is_pos_pow2(x.int_const);
if (x.int_const->signed_fits_bits(8) && k < 0) { if (x.int_const->signed_fits_bits(8) && k < 0) {
x.unused(); x.replaced();
if (x.always_zero() && y.always_finite()) { if (x.always_zero() && y.always_finite()) {
// dubious optimization: NaN * 0 = ? // dubious optimization: NaN * 0 = ?
r.set_const(x.int_const); r.set_const(x.int_const);
y.unused(); y.replaced();
return push_const(r.int_const); return push_const(r.int_const);
} }
if (*x.int_const == 1 && y.always_finite()) { 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); return exec_arg_op("MULCONST", x.int_const, 1);
} }
if (k > 0) { if (k > 0) {
x.unused(); x.replaced();
return exec_arg_op("LSHIFT#", k, 1); return exec_arg_op("LSHIFT#", k, 1);
} }
if (k == 0) { if (k == 0) {
x.unused(); x.replaced();
return AsmOp::Nop(); 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()) { if (!r.int_const->is_valid()) {
throw src::ParseError(where, "integer overflow"); throw src::ParseError(where, "integer overflow");
} }
x.unused(); x.replaced();
y.unused(); y.replaced();
return push_const(r.int_const); 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()); int k = (int)(y.int_const->to_long());
if (!k /* && x.always_finite() */) { if (!k /* && x.always_finite() */) {
// dubious optimization: what if x=NaN ? // dubious optimization: what if x=NaN ?
y.unused(); y.replaced();
return AsmOp::Nop(); return AsmOp::Nop();
} }
y.unused(); y.replaced();
return exec_arg_op("LSHIFT#", k, 1); return exec_arg_op("LSHIFT#", k, 1);
} }
if (x.is_int_const()) { if (x.is_int_const()) {
auto xv = x.int_const->to_long(); auto xv = x.int_const->to_long();
if (xv == 1) { if (xv == 1) {
x.unused(); x.replaced();
return exec_op("POW2", 1); return exec_op("POW2", 1);
} }
if (xv == -1) { if (xv == -1) {
x.unused(); x.replaced();
return exec_op("-1 PUSHINT SWAP LSHIFT", 1); 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"); throw src::ParseError(where, "rshift argument is out of range");
} else if (x.is_int_const()) { } else if (x.is_int_const()) {
r.set_const(td::rshift(x.int_const, (int)yv, round_mode)); r.set_const(td::rshift(x.int_const, (int)yv, round_mode));
x.unused(); x.replaced();
y.unused(); y.replaced();
return push_const(r.int_const); 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()); int k = (int)(y.int_const->to_long());
if (!k /* && x.always_finite() */) { if (!k /* && x.always_finite() */) {
// dubious optimization: what if x=NaN ? // dubious optimization: what if x=NaN ?
y.unused(); y.replaced();
return AsmOp::Nop(); return AsmOp::Nop();
} }
y.unused(); y.replaced();
return exec_arg_op(rshift + "#", k, 1); return exec_arg_op(rshift + "#", k, 1);
} }
return exec_op(rshift, 2); 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()) { if (!r.int_const->is_valid()) {
throw src::ParseError(where, *y.int_const == 0 ? "division by zero" : "integer overflow"); throw src::ParseError(where, *y.int_const == 0 ? "division by zero" : "integer overflow");
} }
x.unused(); x.replaced();
y.unused(); y.replaced();
return push_const(r.int_const); return push_const(r.int_const);
} }
r.val = emulate_div(x.val, y.val); 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"); throw src::ParseError(where, "division by zero");
} }
if (*y.int_const == 1 && x.always_finite()) { if (*y.int_const == 1 && x.always_finite()) {
y.unused(); y.replaced();
return AsmOp::Nop(); return AsmOp::Nop();
} }
if (*y.int_const == -1) { if (*y.int_const == -1) {
y.unused(); y.replaced();
return exec_op("NEGATE", 1); return exec_op("NEGATE", 1);
} }
int k = is_pos_pow2(y.int_const); int k = is_pos_pow2(y.int_const);
if (k > 0) { if (k > 0) {
y.unused(); y.replaced();
std::string op = "RSHIFT"; std::string op = "RSHIFT";
if (round_mode >= 0) { if (round_mode >= 0) {
op += (round_mode > 0 ? 'C' : 'R'); 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()) { if (!r.int_const->is_valid()) {
throw src::ParseError(where, *y.int_const == 0 ? "division by zero" : "integer overflow"); throw src::ParseError(where, *y.int_const == 0 ? "division by zero" : "integer overflow");
} }
x.unused(); x.replaced();
y.unused(); y.replaced();
return push_const(r.int_const); return push_const(r.int_const);
} }
r.val = emulate_mod(x.val, y.val); 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"); throw src::ParseError(where, "division by zero");
} }
if ((*y.int_const == 1 || *y.int_const == -1) && x.always_finite()) { if ((*y.int_const == 1 || *y.int_const == -1) && x.always_finite()) {
x.unused(); x.replaced();
y.unused(); y.replaced();
r.set_const(td::zero_refint()); r.set_const(td::zero_refint());
return push_const(r.int_const); return push_const(r.int_const);
} }
int k = is_pos_pow2(y.int_const); int k = is_pos_pow2(y.int_const);
if (k > 0) { if (k > 0) {
y.unused(); y.replaced();
std::string op = "MODPOW2"; std::string op = "MODPOW2";
if (round_mode >= 0) { if (round_mode >= 0) {
op += (round_mode > 0 ? 'C' : 'R'); 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()) { if (!r.int_const->is_valid()) {
throw src::ParseError(where, *z.int_const == 0 ? "division by zero" : "integer overflow"); throw src::ParseError(where, *z.int_const == 0 ? "division by zero" : "integer overflow");
} }
x.unused(); x.replaced();
y.unused(); y.replaced();
z.unused(); z.replaced();
return push_const(r.int_const); return push_const(r.int_const);
} }
if (x.always_zero() || y.always_zero()) { if (x.always_zero() || y.always_zero()) {
// dubious optimization for z=0... // dubious optimization for z=0...
x.unused(); x.replaced();
y.unused(); y.replaced();
z.unused(); z.replaced();
r.set_const(td::make_refint(0)); r.set_const(td::make_refint(0));
return push_const(r.int_const); 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) { if (y.is_int_const() && *y.int_const == 1) {
y.unused(); y.replaced();
return compile_div_internal(r, x, z, where, round_mode); return compile_div_internal(r, x, z, where, round_mode);
} }
if (x.is_int_const() && *x.int_const == 1) { if (x.is_int_const() && *x.int_const == 1) {
x.unused(); x.replaced();
return compile_div_internal(r, y, z, where, round_mode); return compile_div_internal(r, y, z, where, round_mode);
} }
if (z.is_int_const()) { if (z.is_int_const()) {
int k = is_pos_pow2(z.int_const); int k = is_pos_pow2(z.int_const);
if (k > 0) { if (k > 0) {
z.unused(); z.replaced();
std::string op = "MULRSHIFT"; std::string op = "MULRSHIFT";
if (c) { if (c) {
op += c; op += c;
@ -855,7 +855,7 @@ AsmOp compile_muldiv(std::vector<VarDescr>& res, std::vector<VarDescr>& args, co
if (y.is_int_const()) { if (y.is_int_const()) {
int k = is_pos_pow2(y.int_const); int k = is_pos_pow2(y.int_const);
if (k > 0) { if (k > 0) {
y.unused(); y.replaced();
std::string op = "LSHIFT#DIV"; std::string op = "LSHIFT#DIV";
if (c) { if (c) {
op += c; op += c;
@ -866,7 +866,7 @@ AsmOp compile_muldiv(std::vector<VarDescr>& res, std::vector<VarDescr>& args, co
if (x.is_int_const()) { if (x.is_int_const()) {
int k = is_pos_pow2(x.int_const); int k = is_pos_pow2(x.int_const);
if (k > 0) { if (k > 0) {
x.unused(); x.replaced();
std::string op = "LSHIFT#DIV"; std::string op = "LSHIFT#DIV";
if (c) { if (c) {
op += 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()) { if (x.is_int_const() && y.is_int_const()) {
int v = compute_compare(x.int_const, y.int_const, mode); int v = compute_compare(x.int_const, y.int_const, mode);
r.set_const(v); r.set_const(v);
x.unused(); x.replaced();
y.unused(); y.replaced();
return mode == 7 ? push_const(r.int_const) : AsmOp::BoolConst(v != 0); return mode == 7 ? push_const(r.int_const) : AsmOp::BoolConst(v != 0);
} }
int v = compute_compare(x, y, mode); 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); func_assert(v);
if (!(v & (v - 1))) { if (!(v & (v - 1))) {
r.set_const(v - (v >> 2) - 2); r.set_const(v - (v >> 2) - 2);
x.unused(); x.replaced();
y.unused(); y.replaced();
return mode == 7 ? push_const(r.int_const) : AsmOp::BoolConst(v & 1); return mode == 7 ? push_const(r.int_const) : AsmOp::BoolConst(v & 1);
} }
r.val = ~0; 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}; static int cmp_int_delta[] = {0, 0, 0, -1, 0, 0, 1};
if (mode != 7) { if (mode != 7) {
if (y.is_int_const() && y.int_const >= -128 && y.int_const <= 127) { 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); 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) { 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); 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); 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); func_assert(res.empty() && args.size() == 1);
VarDescr& x = args[0]; VarDescr& x = args[0];
if (x.is_int_const() && x.int_const->unsigned_fits_bits(11)) { 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); return exec_arg_op("THROW", x.int_const, 0, 0);
} else { } else {
return exec_op("THROWANY", 1, 0); 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"); std::string suff = (mode ? "IF" : "IFNOT");
bool skip_cond = false; bool skip_cond = false;
if (y.always_true() || y.always_false()) { if (y.always_true() || y.always_false()) {
y.unused(); y.replaced();
skip_cond = true; skip_cond = true;
if (y.always_true() != mode) { if (y.always_true() != mode) {
x.unused(); x.replaced();
return AsmOp::Nop(); return AsmOp::Nop();
} }
} }
if (x.is_int_const() && x.int_const->unsigned_fits_bits(11)) { 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); return skip_cond ? exec_arg_op("THROW", x.int_const, 0, 0) : exec_arg_op("THROW"s + suff, x.int_const, 1, 0);
} else { } else {
return skip_cond ? exec_op("THROWANY", 1, 0) : exec_op("THROWANY"s + suff, 2, 0); 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); func_assert(res.empty() && args.size() == 2);
VarDescr &x = args[1]; VarDescr &x = args[1];
if (x.is_int_const() && x.int_const->unsigned_fits_bits(11)) { 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); return exec_arg_op("THROWARG", x.int_const, 1, 0);
} else { } else {
return exec_op("THROWARGANY", 2, 0); 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"); std::string suff = (mode ? "IF" : "IFNOT");
bool skip_cond = false; bool skip_cond = false;
if (y.always_true() || y.always_false()) { if (y.always_true() || y.always_false()) {
y.unused(); y.replaced();
skip_cond = true; skip_cond = true;
if (y.always_true() != mode) { if (y.always_true() != mode) {
x.unused(); x.replaced();
return AsmOp::Nop(); return AsmOp::Nop();
} }
} }
if (x.is_int_const() && x.int_const->unsigned_fits_bits(11)) { 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); return skip_cond ? exec_arg_op("THROWARG", x.int_const, 1, 0) : exec_arg_op("THROWARG"s + suff, x.int_const, 2, 0);
} else { } else {
return skip_cond ? exec_op("THROWARGANY", 2, 0) : exec_op("THROWARGANY"s + suff, 3, 0); 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); r.val = (sgnd ? VarDescr::ValBool : VarDescr::ValBit);
} }
if (v > 0) { if (v > 0) {
y.unused(); y.replaced();
return exec_arg_op((fetch ? "LD"s : "PLD"s) + (sgnd ? 'I' : 'U'), v, 1, 1 + (unsigned)fetch); 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); func_assert(args.size() == 3 && res.size() == 1);
auto& z = args[2]; auto& z = args[2];
if (z.is_int_const() && z.int_const > 0 && z.int_const <= 256) { 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_arg_op("ST"s + (sgnd ? 'I' : 'U'), z.int_const, 2, 1);
} }
return exec_op("ST"s + (sgnd ? "IX" : "UX"), 3, 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) { if (y.is_int_const() && y.int_const > 0 && y.int_const <= 256) {
v = (int)y.int_const->to_long(); v = (int)y.int_const->to_long();
if (v > 0) { if (v > 0) {
y.unused(); y.replaced();
return exec_arg_op(fetch ? "LDSLICE" : "PLDSLICE", v, 1, 1 + (unsigned)fetch); 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); func_assert(args.size() == 2 && res.size() == 1);
auto& y = args[1]; auto& y = args[1];
if (y.is_int_const() && y.int_const >= 0 && y.int_const < 16) { 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_arg_op("INDEX", y.int_const, 1, 1);
} }
return exec_op("INDEXVAR", 2, 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); func_assert(args.size() == 1 && res.size() == 1);
auto &x = args[0], &r = res[0]; auto &x = args[0], &r = res[0];
if (x.always_null() || x.always_not_null()) { if (x.always_null() || x.always_not_null()) {
x.unused(); x.replaced();
r.set_const(x.always_null() ? -1 : 0); r.set_const(x.always_null() ? -1 : 0);
return push_const(r.int_const); 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); func_assert(args.size() == (unsigned)n + 1 && res.size() == (unsigned)has_value);
auto& x = args[0]; auto& x = args[0];
if (x.is_int_const() && x.int_const->unsigned_fits_bits(14)) { 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); code << exec_arg_op("PREPAREDICT", x.int_const, 0, 2);
} else { } else {
code << exec_op("c3 PUSH", 0, 1); code << exec_op("c3 PUSH", 0, 1);

View file

@ -374,10 +374,13 @@ bool Op::generate_code_step(Stack& stack) {
int i = 0; int i = 0;
std::vector<bool> active; std::vector<bool> active;
active.reserve(left.size()); active.reserve(left.size());
int unused = 0;
for (std::size_t k = 0; k < left.size(); k++) { for (std::size_t k = 0; k < left.size(); k++) {
var_idx_t y = left[k]; // "y" = "x" var_idx_t y = left[k]; // "y" = "x"
auto p = next->var_info[y]; auto p = next->var_info[y];
active.push_back(p && !p->is_unused()); 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++) { for (std::size_t k = 0; k < left.size(); k++) {
if (!active[k]) { if (!active[k]) {
@ -406,6 +409,16 @@ bool Op::generate_code_step(Stack& stack) {
stack.assign_var(left[k], --i); 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; return true;
} }
case _Tuple: case _Tuple:
@ -437,6 +450,16 @@ bool Op::generate_code_step(Stack& stack) {
case _Call: case _Call:
case _CallInd: { case _CallInd: {
if (disabled()) { 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; return true;
} }
SymValFunc* func = (fun_ref ? dynamic_cast<SymValFunc*>(fun_ref->value) : nullptr); 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" "-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 " "-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"
"\t-s\tOutput semantic version of FunC and exit\n" "\t-s\tOutput semantic version of FunC and exit\n"
"\t-V<version>\tShow func build information\n"; "\t-V<version>\tShow func build information\n";
std::exit(2); std::exit(2);
@ -57,7 +58,7 @@ void usage(const char* progname) {
int main(int argc, char* const argv[]) { int main(int argc, char* const argv[]) {
int i; int i;
std::string output_filename; 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) { switch (i) {
case 'A': case 'A':
funC::asm_preamble = true; funC::asm_preamble = true;
@ -83,6 +84,9 @@ int main(int argc, char* const argv[]) {
case 'S': case 'S':
funC::stack_layout_comments = true; funC::stack_layout_comments = true;
break; break;
case 'u':
++funC::warn_unused;
break;
case 'v': case 'v':
++funC::verbosity; ++funC::verbosity;
break; break;

View file

@ -35,7 +35,7 @@
namespace funC { 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 stack_layout_comments, op_rewrite_comments, program_envelope, asm_preamble;
bool interactive = false; bool interactive = false;
GlobalPragma pragma_allow_post_modification{"allow-post-modification"}; GlobalPragma pragma_allow_post_modification{"allow-post-modification"};

View file

@ -39,7 +39,7 @@
namespace funC { namespace funC {
extern int verbosity; extern int verbosity, warn_unused;
extern bool op_rewrite_comments; extern bool op_rewrite_comments;
extern std::string generated_from; extern std::string generated_from;
@ -328,7 +328,7 @@ struct TmpVar {
struct VarDescr { struct VarDescr {
var_idx_t idx; var_idx_t idx;
enum { _Last = 1, _Unused = 2 }; enum { _Last = 1, _Unused = 2, _Replaced = 4 };
int flags; int flags;
enum { enum {
_Const = 16, _Const = 16,
@ -365,6 +365,9 @@ struct VarDescr {
bool is_unused() const { bool is_unused() const {
return flags & _Unused; return flags & _Unused;
} }
bool is_replaced() const {
return flags & _Replaced;
}
bool is_last() const { bool is_last() const {
return flags & _Last; return flags & _Last;
} }
@ -422,8 +425,16 @@ struct VarDescr {
void unused() { void unused() {
flags |= _Unused; flags |= _Unused;
} }
void replaced() {
unused(); // replaced var is always unused
flags |= _Replaced;
}
void clear_unused() { void clear_unused() {
flags &= ~_Unused; flags &= ~_Unused;
clear_replaced();
}
void clear_replaced() {
flags &= ~_Replaced;
} }
void set_const(long long value); void set_const(long long value);
void set_const(td::RefInt256 value); void set_const(td::RefInt256 value);
@ -474,12 +485,13 @@ struct VarDescrList {
VarDescrList& operator+=(const std::vector<var_idx_t>& idx_list) { VarDescrList& operator+=(const std::vector<var_idx_t>& idx_list) {
return add_vars(idx_list); return add_vars(idx_list);
} }
VarDescrList& add_var(var_idx_t idx, 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); 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-=(const std::vector<var_idx_t>& idx_list);
VarDescrList& operator-=(var_idx_t idx); VarDescrList& operator-=(var_idx_t idx);
std::size_t count(const std::vector<var_idx_t> idx_list) const; 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_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(var_idx_t idx);
VarDescr& add_newval(var_idx_t idx); VarDescr& add_newval(var_idx_t idx);
VarDescrList& operator&=(const VarDescrList& values); VarDescrList& operator&=(const VarDescrList& values);
@ -557,7 +569,7 @@ struct Op {
_SliceConst _SliceConst
}; };
int cl; 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; int flags;
std::unique_ptr<Op> next; std::unique_ptr<Op> next;
SymDef* fun_ref; SymDef* fun_ref;
@ -593,12 +605,19 @@ struct Op {
bool disabled() const { bool disabled() const {
return flags & _Disabled; return flags & _Disabled;
} }
bool replaced() const {
return flags & _Replaced;
}
bool enabled() const { bool enabled() const {
return !disabled(); return !disabled();
} }
void disable() { void disable() {
flags |= _Disabled; flags |= _Disabled;
} }
void replace() {
disable();
flags |= _Replaced;
}
bool unreachable() { bool unreachable() {
return !(flags & _Reachable); return !(flags & _Reachable);
} }
@ -611,7 +630,7 @@ struct Op {
void split_vars(const std::vector<TmpVar>& vars); 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); 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 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(const VarDescrList& new_var_info);
bool set_var_info(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); bool set_var_info_except(const VarDescrList& new_var_info, const std::vector<var_idx_t>& var_list);