mirror of
https://github.com/ton-blockchain/ton
synced 2025-03-09 15:40:10 +00:00
Merge fa2aa90dfc
into 2a68c8610b
This commit is contained in:
commit
083c841b1e
7 changed files with 164 additions and 94 deletions
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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"};
|
||||
|
|
|
@ -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);
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue