mirror of
https://github.com/ton-blockchain/ton
synced 2025-03-09 15:40:10 +00:00
In FunC (and in Tolk before), the assignment > lhs = rhs evaluation order (at IR level) was "rhs first, lhs second". In practice, this did not matter, because lhs could only be a primitive: > (v1, v2) = getValue() Left side of assignment actually has no "evaluation". Since Tolk implemented indexed access, there could be > getTensor().0 = getValue() or (in the future) > getObject().field = getValue() where evaluation order becomes significant. Now evaluation order will be to "lhs first, rhs second" (more expected from user's point of view), which will become significant when building control flow graph.
134 lines
4.7 KiB
C++
134 lines
4.7 KiB
C++
/*
|
|
This file is part of TON Blockchain source code.
|
|
|
|
TON Blockchain is free software; you can redistribute it and/or
|
|
modify it under the terms of the GNU General Public License
|
|
as published by the Free Software Foundation; either version 2
|
|
of the License, or (at your option) any later version.
|
|
|
|
TON Blockchain is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with TON Blockchain. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
#include "tolk.h"
|
|
#include "ast.h"
|
|
#include "ast-replacer.h"
|
|
#include "type-system.h"
|
|
|
|
/*
|
|
* This pipe does some optimizations related to booleans.
|
|
* It happens after type inferring, when we know types of all expressions.
|
|
*
|
|
* Example: `boolVar == true` -> `boolVar`.
|
|
* Example: `!!boolVar` -> `boolVar`.
|
|
*
|
|
* todo some day, replace && || with & | when it's safe (currently, && always produces IFs in Fift)
|
|
* It's tricky to implement whether replacing is safe.
|
|
* For example, safe: `a > 0 && a < 10` / `a != 3 && a != 5`
|
|
* For example, unsafe: `cached && calc()` / `a > 0 && log(a)` / `b != 0 && a / b > 1` / `i >= 0 && arr[idx]` / `f != null && close(f)`
|
|
*/
|
|
|
|
namespace tolk {
|
|
|
|
struct OptimizerBooleanExpressionsReplacer final : ASTReplacerInFunctionBody {
|
|
static V<ast_int_const> create_int_const(SrcLocation loc, td::RefInt256&& intval) {
|
|
auto v_int = createV<ast_int_const>(loc, std::move(intval), {});
|
|
v_int->assign_inferred_type(TypeDataInt::create());
|
|
v_int->assign_rvalue_true();
|
|
return v_int;
|
|
}
|
|
|
|
static V<ast_bool_const> create_bool_const(SrcLocation loc, bool bool_val) {
|
|
auto v_bool = createV<ast_bool_const>(loc, bool_val);
|
|
v_bool->assign_inferred_type(TypeDataInt::create());
|
|
v_bool->assign_rvalue_true();
|
|
return v_bool;
|
|
}
|
|
|
|
static V<ast_unary_operator> create_logical_not_for_bool(SrcLocation loc, AnyExprV rhs) {
|
|
auto v_not = createV<ast_unary_operator>(loc, "!", tok_logical_not, rhs);
|
|
v_not->assign_inferred_type(TypeDataBool::create());
|
|
v_not->assign_rvalue_true();
|
|
v_not->assign_fun_ref(lookup_global_symbol("!b_")->as<FunctionData>());
|
|
return v_not;
|
|
}
|
|
|
|
protected:
|
|
|
|
AnyExprV replace(V<ast_unary_operator> v) override {
|
|
parent::replace(v);
|
|
|
|
if (v->tok == tok_logical_not) {
|
|
if (auto inner_not = v->get_rhs()->try_as<ast_unary_operator>(); inner_not && inner_not->tok == tok_logical_not) {
|
|
AnyExprV cond_not_not = inner_not->get_rhs();
|
|
// `!!boolVar` => `boolVar`
|
|
if (cond_not_not->inferred_type == TypeDataBool::create()) {
|
|
return cond_not_not;
|
|
}
|
|
// `!!intVar` => `intVar != 0`
|
|
if (cond_not_not->inferred_type == TypeDataInt::create()) {
|
|
auto v_zero = create_int_const(v->loc, td::make_refint(0));
|
|
auto v_neq = createV<ast_binary_operator>(v->loc, "!=", tok_neq, cond_not_not, v_zero);
|
|
v_neq->mutate()->assign_rvalue_true();
|
|
v_neq->mutate()->assign_inferred_type(TypeDataBool::create());
|
|
v_neq->mutate()->assign_fun_ref(lookup_global_symbol("_!=_")->as<FunctionData>());
|
|
return v_neq;
|
|
}
|
|
}
|
|
if (auto inner_bool = v->get_rhs()->try_as<ast_bool_const>()) {
|
|
// `!true` / `!false`
|
|
return create_bool_const(v->loc, !inner_bool->bool_val);
|
|
}
|
|
}
|
|
|
|
return v;
|
|
}
|
|
|
|
AnyExprV replace(V<ast_binary_operator> v) override {
|
|
parent::replace(v);
|
|
|
|
if (v->tok == tok_eq || v->tok == tok_neq) {
|
|
AnyExprV lhs = v->get_lhs();
|
|
AnyExprV rhs = v->get_rhs();
|
|
if (lhs->inferred_type == TypeDataBool::create() && rhs->type == ast_bool_const) {
|
|
// `boolVar == true` / `boolVar != false`
|
|
if (rhs->as<ast_bool_const>()->bool_val ^ (v->tok == tok_neq)) {
|
|
return lhs;
|
|
}
|
|
// `boolVar != true` / `boolVar == false`
|
|
return create_logical_not_for_bool(v->loc, lhs);
|
|
}
|
|
}
|
|
|
|
return v;
|
|
}
|
|
|
|
AnyV replace(V<ast_if_statement> v) override {
|
|
parent::replace(v);
|
|
|
|
// `if (!x)` -> ifnot(x)
|
|
while (auto v_cond_unary = v->get_cond()->try_as<ast_unary_operator>()) {
|
|
if (v_cond_unary->tok != tok_logical_not) {
|
|
break;
|
|
}
|
|
v = createV<ast_if_statement>(v->loc, !v->is_ifnot, v_cond_unary->get_rhs(), v->get_if_body(), v->get_else_body());
|
|
}
|
|
|
|
return v;
|
|
}
|
|
|
|
public:
|
|
bool should_visit_function(const FunctionData* fun_ref) override {
|
|
return fun_ref->is_code_function() && !fun_ref->is_generic_function();
|
|
}
|
|
};
|
|
|
|
void pipeline_optimize_boolean_expressions() {
|
|
replace_ast_of_all_functions<OptimizerBooleanExpressionsReplacer>();
|
|
}
|
|
|
|
} // namespace tolk
|