1
0
Fork 0
mirror of https://github.com/ton-blockchain/ton synced 2025-03-09 15:40:10 +00:00

[FunC] Refactor allow-post-modification, stop producing disabled Op::_Let

Before, #pragma allow-post-modification produced Op::_Let for every
tensor entity (which became non-disabled if modification really happened).
Although they are stripped off by the compiler and don't affect fif output,
they pollute intermediate "AST" representation (ops).
Now, Op::_Let is added only if var modification actually happens
(which is very uncommon for real-wise code)
This commit is contained in:
Aleksandr Kirsanov 2024-05-10 16:34:15 +03:00
parent 1e4b20a061
commit aee51731ce
No known key found for this signature in database
GPG key ID: B758BBAA01FFB3D3
2 changed files with 24 additions and 22 deletions

View file

@ -269,24 +269,29 @@ std::vector<var_idx_t> pre_compile_let(CodeBlob& code, Expr* lhs, Expr* rhs, con
std::vector<var_idx_t> pre_compile_tensor(const std::vector<Expr *>& args, CodeBlob &code,
std::vector<std::pair<SymDef*, var_idx_t>> *lval_globs) {
std::vector<std::vector<var_idx_t>> res_lists(args.size());
const size_t n = args.size();
if (n == 0) { // just `()`
return {};
}
if (n == 1) { // just `(x)`: even if x is modified (e.g. `f(x=x+2)`), there are no next arguments
return args[0]->pre_compile(code, lval_globs);
}
std::vector<std::vector<var_idx_t>> res_lists(n);
struct ModifiedVar {
size_t i, j;
Op* op;
std::unique_ptr<Op>* cur_ops; // `LET tmp = v_ij` will be inserted before this
};
auto modified_vars = std::make_shared<std::vector<ModifiedVar>>();
for (size_t i = 0; i < args.size(); ++i) {
std::vector<ModifiedVar> modified_vars;
for (size_t i = 0; i < n; ++i) {
res_lists[i] = args[i]->pre_compile(code, lval_globs);
for (size_t j = 0; j < res_lists[i].size(); ++j) {
TmpVar& var = code.vars.at(res_lists[i][j]);
if (!lval_globs && (var.cls & TmpVar::_Named)) {
Op *op = &code.emplace_back(nullptr, Op::_Let, std::vector<var_idx_t>(), std::vector<var_idx_t>());
op->set_disabled();
var.on_modification.push_back([modified_vars, i, j, op, done = false](const SrcLocation &here) mutable {
var.on_modification.push_back([&modified_vars, i, j, cur_ops = code.cur_ops, done = false](const SrcLocation &here) mutable {
if (!done) {
done = true;
modified_vars->push_back({i, j, op});
modified_vars.push_back({i, j, cur_ops});
}
});
} else {
@ -301,13 +306,16 @@ std::vector<var_idx_t> pre_compile_tensor(const std::vector<Expr *>& args, CodeB
code.vars.at(v).on_modification.pop_back();
}
}
for (const ModifiedVar &m : *modified_vars) {
var_idx_t& v = res_lists[m.i][m.j];
var_idx_t v2 = code.create_tmp_var(code.vars[v].v_type, code.vars[v].where.get());
m.op->left = {v2};
m.op->right = {v};
m.op->set_disabled(false);
v = v2;
for (size_t idx = modified_vars.size(); idx--; ) {
const ModifiedVar &m = modified_vars[idx];
var_idx_t orig_v = res_lists[m.i][m.j];
var_idx_t tmp_v = code.create_tmp_var(code.vars[orig_v].v_type, code.vars[orig_v].where.get());
std::unique_ptr<Op> op = std::make_unique<Op>(*code.vars[orig_v].where, Op::_Let);
op->left = {tmp_v};
op->right = {orig_v};
op->next = std::move((*m.cur_ops));
*m.cur_ops = std::move(op);
res_lists[m.i][m.j] = tmp_v;
}
std::vector<var_idx_t> res;
for (const auto& list : res_lists) {
@ -333,10 +341,7 @@ std::vector<var_idx_t> Expr::pre_compile(CodeBlob& code, std::vector<std::pair<S
// replace `beginCell()` with `begin_cell()`
if (func && func->is_just_wrapper_for_another_f()) {
// body is { Op::_Import; Op::_Call; Op::_Return; }
const Op *op_call = dynamic_cast<SymValCodeFunc*>(func)->code->ops.get();
while (op_call->cl != Op::_Call) {
op_call = op_call->next.get();
}
const std::unique_ptr<Op>& op_call = dynamic_cast<SymValCodeFunc*>(func)->code->ops->next;
applied_sym = op_call->fun_ref;
// a function may call anotherF with shuffled arguments: f(x,y) { return anotherF(y,x) }
// then op_call looks like (_1,_0), so use op_call->right for correct positions in Op::_Call below

View file

@ -1431,10 +1431,7 @@ void detect_if_function_just_wraps_another(SymValCodeFunc* v_current, const td::
func_assert(op_import && op_import->cl == Op::_Import);
// then Op::_Call (anotherF)
// when pragma allow-post-modification, it might be prepended with empty Op::_Let todo I don't like this
const Op* op_call = op_import->next.get();
while (op_call && op_call->cl == Op::_Let && op_call->disabled())
op_call = op_call->next.get();
if (!op_call || op_call->cl != Op::_Call)
return;
func_assert(op_call->left.size() == 1);