Ton

Форк
0
/
codegen.cpp 
912 строк · 26.9 Кб
1
/*
2
    This file is part of TON Blockchain Library.
3

4
    TON Blockchain Library is free software: you can redistribute it and/or modify
5
    it under the terms of the GNU Lesser General Public License as published by
6
    the Free Software Foundation, either version 2 of the License, or
7
    (at your option) any later version.
8

9
    TON Blockchain Library is distributed in the hope that it will be useful,
10
    but WITHOUT ANY WARRANTY; without even the implied warranty of
11
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
    GNU Lesser General Public License for more details.
13

14
    You should have received a copy of the GNU Lesser General Public License
15
    along with TON Blockchain Library.  If not, see <http://www.gnu.org/licenses/>.
16

17
    Copyright 2017-2020 Telegram Systems LLP
18
*/
19
#include "func.h"
20

21
namespace funC {
22

23
/*
24
 * 
25
 *   GENERATE TVM STACK CODE
26
 * 
27
 */
28

29
StackLayout Stack::vars() const {
30
  StackLayout res;
31
  res.reserve(s.size());
32
  for (auto x : s) {
33
    res.push_back(x.first);
34
  }
35
  return res;
36
}
37

38
int Stack::find(var_idx_t var, int from) const {
39
  for (int i = from; i < depth(); i++) {
40
    if (at(i).first == var) {
41
      return i;
42
    }
43
  }
44
  return -1;
45
}
46

47
// finds var in [from .. to)
48
int Stack::find(var_idx_t var, int from, int to) const {
49
  for (int i = from; i < depth() && i < to; i++) {
50
    if (at(i).first == var) {
51
      return i;
52
    }
53
  }
54
  return -1;
55
}
56

57
// finds var outside [from .. to)
58
int Stack::find_outside(var_idx_t var, int from, int to) const {
59
  from = std::max(from, 0);
60
  if (from >= to) {
61
    return find(var);
62
  } else {
63
    int t = find(var, 0, from);
64
    return t >= 0 ? t : find(var, to);
65
  }
66
}
67

68
int Stack::find_const(const_idx_t cst, int from) const {
69
  for (int i = from; i < depth(); i++) {
70
    if (at(i).second == cst) {
71
      return i;
72
    }
73
  }
74
  return -1;
75
}
76

77
void Stack::forget_const() {
78
  for (auto& vc : s) {
79
    if (vc.second != not_const) {
80
      vc.second = not_const;
81
    }
82
  }
83
}
84

85
void Stack::issue_pop(int i) {
86
  validate(i);
87
  if (output_enabled()) {
88
    o << AsmOp::Pop(i);
89
  }
90
  at(i) = get(0);
91
  s.pop_back();
92
  modified();
93
}
94

95
void Stack::issue_push(int i) {
96
  validate(i);
97
  if (output_enabled()) {
98
    o << AsmOp::Push(i);
99
  }
100
  s.push_back(get(i));
101
  modified();
102
}
103

104
void Stack::issue_xchg(int i, int j) {
105
  validate(i);
106
  validate(j);
107
  if (i != j && get(i) != get(j)) {
108
    if (output_enabled()) {
109
      o << AsmOp::Xchg(i, j);
110
    }
111
    std::swap(at(i), at(j));
112
    modified();
113
  }
114
}
115

116
int Stack::drop_vars_except(const VarDescrList& var_info, int excl_var) {
117
  int dropped = 0, changes;
118
  do {
119
    changes = 0;
120
    int n = depth();
121
    for (int i = 0; i < n; i++) {
122
      var_idx_t idx = at(i).first;
123
      if (((!var_info[idx] || var_info[idx]->is_unused()) && idx != excl_var) || find(idx, 0, i - 1) >= 0) {
124
        // unneeded
125
        issue_pop(i);
126
        changes = 1;
127
        break;
128
      }
129
    }
130
    dropped += changes;
131
  } while (changes);
132
  return dropped;
133
}
134

135
void Stack::show(int flags) {
136
  std::ostringstream os;
137
  for (auto i : s) {
138
    os << ' ';
139
    o.show_var_ext(os, i);
140
  }
141
  o << AsmOp::Comment(os.str());
142
  mode |= _Shown;
143
}
144

145
void Stack::forget_var(var_idx_t idx) {
146
  for (auto& x : s) {
147
    if (x.first == idx) {
148
      x = std::make_pair(_Garbage, not_const);
149
      modified();
150
    }
151
  }
152
}
153

154
void Stack::push_new_var(var_idx_t idx) {
155
  forget_var(idx);
156
  s.emplace_back(idx, not_const);
157
  modified();
158
}
159

160
void Stack::push_new_const(var_idx_t idx, const_idx_t cidx) {
161
  forget_var(idx);
162
  s.emplace_back(idx, cidx);
163
  modified();
164
}
165

166
void Stack::assign_var(var_idx_t new_idx, var_idx_t old_idx) {
167
  int i = find(old_idx);
168
  func_assert(i >= 0 && "variable not found in stack");
169
  if (new_idx != old_idx) {
170
    at(i).first = new_idx;
171
    modified();
172
  }
173
}
174

175
void Stack::do_copy_var(var_idx_t new_idx, var_idx_t old_idx) {
176
  int i = find(old_idx);
177
  func_assert(i >= 0 && "variable not found in stack");
178
  if (find(old_idx, i + 1) < 0) {
179
    issue_push(i);
180
    func_assert(at(0).first == old_idx);
181
  }
182
  assign_var(new_idx, old_idx);
183
}
184

185
void Stack::enforce_state(const StackLayout& req_stack) {
186
  int k = (int)req_stack.size();
187
  for (int i = 0; i < k; i++) {
188
    var_idx_t x = req_stack[i];
189
    if (i < depth() && s[i].first == x) {
190
      continue;
191
    }
192
    while (depth() > 0 && std::find(req_stack.cbegin(), req_stack.cend(), get(0).first) == req_stack.cend()) {
193
      // current TOS entry is unused in req_stack, drop it
194
      issue_pop(0);
195
    }
196
    int j = find(x);
197
    if (j >= depth() - i) {
198
      issue_push(j);
199
      j = 0;
200
    }
201
    issue_xchg(j, depth() - i - 1);
202
    func_assert(s[i].first == x);
203
  }
204
  while (depth() > k) {
205
    issue_pop(0);
206
  }
207
  func_assert(depth() == k);
208
  for (int i = 0; i < k; i++) {
209
    func_assert(s[i].first == req_stack[i]);
210
  }
211
}
212

213
void Stack::merge_const(const Stack& req_stack) {
214
  func_assert(s.size() == req_stack.s.size());
215
  for (std::size_t i = 0; i < s.size(); i++) {
216
    func_assert(s[i].first == req_stack.s[i].first);
217
    if (s[i].second != req_stack.s[i].second) {
218
      s[i].second = not_const;
219
    }
220
  }
221
}
222

223
void Stack::merge_state(const Stack& req_stack) {
224
  enforce_state(req_stack.vars());
225
  merge_const(req_stack);
226
}
227

228
void Stack::rearrange_top(const StackLayout& top, std::vector<bool> last) {
229
  while (last.size() < top.size()) {
230
    last.push_back(false);
231
  }
232
  int k = (int)top.size();
233
  for (int i = 0; i < k; i++) {
234
    for (int j = i + 1; j < k; j++) {
235
      if (top[i] == top[j]) {
236
        last[i] = false;
237
        break;
238
      }
239
    }
240
  }
241
  int ss = 0;
242
  for (int i = 0; i < k; i++) {
243
    if (last[i]) {
244
      ++ss;
245
    }
246
  }
247
  for (int i = 0; i < k; i++) {
248
    var_idx_t x = top[i];
249
    // find s(j) containing x with j not in [ss, ss+i)
250
    int j = find_outside(x, ss, ss + i);
251
    if (last[i]) {
252
      // rearrange x to be at s(ss-1)
253
      issue_xchg(--ss, j);
254
      func_assert(get(ss).first == x);
255
    } else {
256
      // create a new copy of x
257
      issue_push(j);
258
      issue_xchg(0, ss);
259
      func_assert(get(ss).first == x);
260
    }
261
  }
262
  func_assert(!ss);
263
}
264

265
void Stack::rearrange_top(var_idx_t top, bool last) {
266
  int i = find(top);
267
  if (last) {
268
    issue_xchg(0, i);
269
  } else {
270
    issue_push(i);
271
  }
272
  func_assert(get(0).first == top);
273
}
274

275
bool Op::generate_code_step(Stack& stack) {
276
  stack.opt_show();
277
  stack.drop_vars_except(var_info);
278
  stack.opt_show();
279
  bool inline_func = stack.mode & Stack::_InlineFunc;
280
  switch (cl) {
281
    case _Nop:
282
    case _Import:
283
      return true;
284
    case _Return: {
285
      stack.enforce_state(left);
286
      if (stack.o.retalt_ && (stack.mode & Stack::_NeedRetAlt)) {
287
        stack.o << "RETALT";
288
      }
289
      stack.opt_show();
290
      return false;
291
    }
292
    case _IntConst: {
293
      auto p = next->var_info[left[0]];
294
      if (!p || p->is_unused()) {
295
        return true;
296
      }
297
      auto cidx = stack.o.register_const(int_const);
298
      int i = stack.find_const(cidx);
299
      if (i < 0) {
300
        stack.o << push_const(int_const);
301
        stack.push_new_const(left[0], cidx);
302
      } else {
303
        func_assert(stack.at(i).second == cidx);
304
        stack.do_copy_var(left[0], stack[i]);
305
      }
306
      return true;
307
    }
308
    case _SliceConst: {
309
      auto p = next->var_info[left[0]];
310
      if (!p || p->is_unused()) {
311
        return true;
312
      }
313
      stack.o << AsmOp::Const("x{" + str_const + "} PUSHSLICE");
314
      stack.push_new_var(left[0]);
315
      return true;
316
    }
317
    case _GlobVar:
318
      if (dynamic_cast<const SymValGlobVar*>(fun_ref->value)) {
319
        bool used = false;
320
        for (auto i : left) {
321
          auto p = next->var_info[i];
322
          if (p && !p->is_unused()) {
323
            used = true;
324
          }
325
        }
326
        if (!used || disabled()) {
327
          return true;
328
        }
329
        std::string name = sym::symbols.get_name(fun_ref->sym_idx);
330
        stack.o << AsmOp::Custom(name + " GETGLOB", 0, 1);
331
        if (left.size() != 1) {
332
          func_assert(left.size() <= 15);
333
          stack.o << AsmOp::UnTuple((int)left.size());
334
        }
335
        for (auto i : left) {
336
          stack.push_new_var(i);
337
        }
338
        return true;
339
      } else {
340
        func_assert(left.size() == 1);
341
        auto p = next->var_info[left[0]];
342
        if (!p || p->is_unused() || disabled()) {
343
          return true;
344
        }
345
        stack.o << "CONT:<{";
346
        stack.o.indent();
347
        auto func = dynamic_cast<SymValAsmFunc*>(fun_ref->value);
348
        if (func) {
349
          // TODO: create and compile a true lambda instead of this (so that arg_order and ret_order would work correctly)
350
          std::vector<VarDescr> args0, res;
351
          TypeExpr::remove_indirect(func->sym_type);
352
          func_assert(func->get_type()->is_map());
353
          auto wr = func->get_type()->args.at(0)->get_width();
354
          auto wl = func->get_type()->args.at(1)->get_width();
355
          func_assert(wl >= 0 && wr >= 0);
356
          for (int i = 0; i < wl; i++) {
357
            res.emplace_back(0);
358
          }
359
          for (int i = 0; i < wr; i++) {
360
            args0.emplace_back(0);
361
          }
362
          func->compile(stack.o, res, args0, where);  // compile res := f (args0)
363
        } else {
364
          std::string name = sym::symbols.get_name(fun_ref->sym_idx);
365
          stack.o << AsmOp::Custom(name + " CALLDICT", (int)right.size(), (int)left.size());
366
        }
367
        stack.o.undent();
368
        stack.o << "}>";
369
        stack.push_new_var(left.at(0));
370
        return true;
371
      }
372
    case _Let: {
373
      func_assert(left.size() == right.size());
374
      int i = 0;
375
      std::vector<bool> active;
376
      active.reserve(left.size());
377
      for (std::size_t k = 0; k < left.size(); k++) {
378
        var_idx_t y = left[k];  // "y" = "x"
379
        auto p = next->var_info[y];
380
        active.push_back(p && !p->is_unused());
381
      }
382
      for (std::size_t k = 0; k < left.size(); k++) {
383
        if (!active[k]) {
384
          continue;
385
        }
386
        var_idx_t x = right[k];  // "y" = "x"
387
        bool is_last = true;
388
        for (std::size_t l = k + 1; l < right.size(); l++) {
389
          if (right[l] == x && active[l]) {
390
            is_last = false;
391
          }
392
        }
393
        if (is_last) {
394
          auto info = var_info[x];
395
          is_last = (info && info->is_last());
396
        }
397
        if (is_last) {
398
          stack.assign_var(--i, x);
399
        } else {
400
          stack.do_copy_var(--i, x);
401
        }
402
      }
403
      i = 0;
404
      for (std::size_t k = 0; k < left.size(); k++) {
405
        if (active[k]) {
406
          stack.assign_var(left[k], --i);
407
        }
408
      }
409
      return true;
410
    }
411
    case _Tuple:
412
    case _UnTuple: {
413
      if (disabled()) {
414
        return true;
415
      }
416
      std::vector<bool> last;
417
      for (var_idx_t x : right) {
418
        last.push_back(var_info[x] && var_info[x]->is_last());
419
      }
420
      stack.rearrange_top(right, std::move(last));
421
      stack.opt_show();
422
      int k = (int)stack.depth() - (int)right.size();
423
      func_assert(k >= 0);
424
      if (cl == _Tuple) {
425
        stack.o << AsmOp::Tuple((int)right.size());
426
        func_assert(left.size() == 1);
427
      } else {
428
        stack.o << AsmOp::UnTuple((int)left.size());
429
        func_assert(right.size() == 1);
430
      }
431
      stack.s.resize(k);
432
      for (int i = 0; i < (int)left.size(); i++) {
433
        stack.push_new_var(left.at(i));
434
      }
435
      return true;
436
    }
437
    case _Call:
438
    case _CallInd: {
439
      if (disabled()) {
440
        return true;
441
      }
442
      SymValFunc* func = (fun_ref ? dynamic_cast<SymValFunc*>(fun_ref->value) : nullptr);
443
      auto arg_order = (func ? func->get_arg_order() : nullptr);
444
      auto ret_order = (func ? func->get_ret_order() : nullptr);
445
      func_assert(!arg_order || arg_order->size() == right.size());
446
      func_assert(!ret_order || ret_order->size() == left.size());
447
      std::vector<var_idx_t> right1;
448
      if (args.size()) {
449
        func_assert(args.size() == right.size());
450
        for (int i = 0; i < (int)right.size(); i++) {
451
          int j = arg_order ? arg_order->at(i) : i;
452
          const VarDescr& arg = args.at(j);
453
          if (!arg.is_unused()) {
454
            func_assert(var_info[arg.idx] && !var_info[arg.idx]->is_unused());
455
            right1.push_back(arg.idx);
456
          }
457
        }
458
      } else if (arg_order) {
459
        for (int i = 0; i < (int)right.size(); i++) {
460
          right1.push_back(right.at(arg_order->at(i)));
461
        }
462
      } else {
463
        right1 = right;
464
      }
465
      std::vector<bool> last;
466
      for (var_idx_t x : right1) {
467
        last.push_back(var_info[x] && var_info[x]->is_last());
468
      }
469
      stack.rearrange_top(right1, std::move(last));
470
      stack.opt_show();
471
      int k = (int)stack.depth() - (int)right1.size();
472
      func_assert(k >= 0);
473
      for (int i = 0; i < (int)right1.size(); i++) {
474
        if (stack.s[k + i].first != right1[i]) {
475
          std::cerr << stack.o;
476
        }
477
        func_assert(stack.s[k + i].first == right1[i]);
478
      }
479
      auto exec_callxargs = [&](int args, int ret) {
480
        if (args <= 15 && ret <= 15) {
481
          stack.o << exec_arg2_op("CALLXARGS", args, ret, args + 1, ret);
482
        } else {
483
          func_assert(args <= 254 && ret <= 254);
484
          stack.o << AsmOp::Const(PSTRING() << args << " PUSHINT");
485
          stack.o << AsmOp::Const(PSTRING() << ret << " PUSHINT");
486
          stack.o << AsmOp::Custom("CALLXVARARGS", args + 3, ret);
487
        }
488
      };
489
      if (cl == _CallInd) {
490
        exec_callxargs((int)right.size() - 1, (int)left.size());
491
      } else {
492
        auto func = dynamic_cast<const SymValAsmFunc*>(fun_ref->value);
493
        if (func) {
494
          std::vector<VarDescr> res;
495
          res.reserve(left.size());
496
          for (var_idx_t i : left) {
497
            res.emplace_back(i);
498
          }
499
          func->compile(stack.o, res, args, where);  // compile res := f (args)
500
        } else {
501
          auto fv = dynamic_cast<const SymValCodeFunc*>(fun_ref->value);
502
          std::string name = sym::symbols.get_name(fun_ref->sym_idx);
503
          bool is_inline = (fv && (fv->flags & 3));
504
          if (is_inline) {
505
            stack.o << AsmOp::Custom(name + " INLINECALLDICT", (int)right.size(), (int)left.size());
506
          } else if (fv && fv->code && fv->code->require_callxargs) {
507
            stack.o << AsmOp::Custom(name + (" PREPAREDICT"), 0, 2);
508
            exec_callxargs((int)right.size() + 1, (int)left.size());
509
          } else {
510
            stack.o << AsmOp::Custom(name + " CALLDICT", (int)right.size(), (int)left.size());
511
          }
512
        }
513
      }
514
      stack.s.resize(k);
515
      for (int i = 0; i < (int)left.size(); i++) {
516
        int j = ret_order ? ret_order->at(i) : i;
517
        stack.push_new_var(left.at(j));
518
      }
519
      return true;
520
    }
521
    case _SetGlob: {
522
      func_assert(fun_ref && dynamic_cast<const SymValGlobVar*>(fun_ref->value));
523
      std::vector<bool> last;
524
      for (var_idx_t x : right) {
525
        last.push_back(var_info[x] && var_info[x]->is_last());
526
      }
527
      stack.rearrange_top(right, std::move(last));
528
      stack.opt_show();
529
      int k = (int)stack.depth() - (int)right.size();
530
      func_assert(k >= 0);
531
      for (int i = 0; i < (int)right.size(); i++) {
532
        if (stack.s[k + i].first != right[i]) {
533
          std::cerr << stack.o;
534
        }
535
        func_assert(stack.s[k + i].first == right[i]);
536
      }
537
      if (right.size() > 1) {
538
        stack.o << AsmOp::Tuple((int)right.size());
539
      }
540
      if (!right.empty()) {
541
        std::string name = sym::symbols.get_name(fun_ref->sym_idx);
542
        stack.o << AsmOp::Custom(name + " SETGLOB", 1, 0);
543
      }
544
      stack.s.resize(k);
545
      return true;
546
    }
547
    case _If: {
548
      if (block0->is_empty() && block1->is_empty()) {
549
        return true;
550
      }
551
      if (!next->noreturn() && (block0->noreturn() != block1->noreturn())) {
552
        stack.o.retalt_ = true;
553
      }
554
      var_idx_t x = left[0];
555
      stack.rearrange_top(x, var_info[x] && var_info[x]->is_last());
556
      func_assert(stack[0] == x);
557
      stack.opt_show();
558
      stack.s.pop_back();
559
      stack.modified();
560
      if (inline_func && (block0->noreturn() || block1->noreturn())) {
561
        bool is0 = block0->noreturn();
562
        Op* block_noreturn = is0 ? block0.get() : block1.get();
563
        Op* block_other = is0 ? block1.get() : block0.get();
564
        stack.mode &= ~Stack::_InlineFunc;
565
        stack.o << (is0 ? "IF:<{" : "IFNOT:<{");
566
        stack.o.indent();
567
        Stack stack_copy{stack};
568
        block_noreturn->generate_code_all(stack_copy);
569
        stack.o.undent();
570
        stack.o << "}>ELSE<{";
571
        stack.o.indent();
572
        block_other->generate_code_all(stack);
573
        if (!block_other->noreturn()) {
574
          next->generate_code_all(stack);
575
        }
576
        stack.o.undent();
577
        stack.o << "}>";
578
        return false;
579
      }
580
      if (block1->is_empty() || block0->is_empty()) {
581
        bool is0 = block1->is_empty();
582
        Op* block = is0 ? block0.get() : block1.get();
583
        // if (left) block0; ...
584
        // if (!left) block1; ...
585
        if (block->noreturn()) {
586
          stack.o << (is0 ? "IFJMP:<{" : "IFNOTJMP:<{");
587
          stack.o.indent();
588
          Stack stack_copy{stack};
589
          stack_copy.mode &= ~Stack::_InlineFunc;
590
          stack_copy.mode |= next->noreturn() ? 0 : Stack::_NeedRetAlt;
591
          block->generate_code_all(stack_copy);
592
          stack.o.undent();
593
          stack.o << "}>";
594
          return true;
595
        }
596
        stack.o << (is0 ? "IF:<{" : "IFNOT:<{");
597
        stack.o.indent();
598
        Stack stack_copy{stack}, stack_target{stack};
599
        stack_target.disable_output();
600
        stack_target.drop_vars_except(next->var_info);
601
        stack_copy.mode &= ~Stack::_InlineFunc;
602
        block->generate_code_all(stack_copy);
603
        stack_copy.drop_vars_except(var_info);
604
        stack_copy.opt_show();
605
        if ((is0 && stack_copy == stack) || (!is0 && stack_copy.vars() == stack.vars())) {
606
          stack.o.undent();
607
          stack.o << "}>";
608
          if (!is0) {
609
            stack.merge_const(stack_copy);
610
          }
611
          return true;
612
        }
613
        // stack_copy.drop_vars_except(next->var_info);
614
        stack_copy.enforce_state(stack_target.vars());
615
        stack_copy.opt_show();
616
        if (stack_copy.vars() == stack.vars()) {
617
          stack.o.undent();
618
          stack.o << "}>";
619
          stack.merge_const(stack_copy);
620
          return true;
621
        }
622
        stack.o.undent();
623
        stack.o << "}>ELSE<{";
624
        stack.o.indent();
625
        stack.merge_state(stack_copy);
626
        stack.opt_show();
627
        stack.o.undent();
628
        stack.o << "}>";
629
        return true;
630
      }
631
      if (block0->noreturn() || block1->noreturn()) {
632
        bool is0 = block0->noreturn();
633
        Op* block_noreturn = is0 ? block0.get() : block1.get();
634
        Op* block_other = is0 ? block1.get() : block0.get();
635
        stack.o << (is0 ? "IFJMP:<{" : "IFNOTJMP:<{");
636
        stack.o.indent();
637
        Stack stack_copy{stack};
638
        stack_copy.mode &= ~Stack::_InlineFunc;
639
        stack_copy.mode |= (block_other->noreturn() || next->noreturn()) ? 0 : Stack::_NeedRetAlt;
640
        block_noreturn->generate_code_all(stack_copy);
641
        stack.o.undent();
642
        stack.o << "}>";
643
        block_other->generate_code_all(stack);
644
        return !block_other->noreturn();
645
      }
646
      stack.o << "IF:<{";
647
      stack.o.indent();
648
      Stack stack_copy{stack};
649
      stack_copy.mode &= ~Stack::_InlineFunc;
650
      block0->generate_code_all(stack_copy);
651
      stack_copy.drop_vars_except(next->var_info);
652
      stack_copy.opt_show();
653
      stack.o.undent();
654
      stack.o << "}>ELSE<{";
655
      stack.o.indent();
656
      stack.mode &= ~Stack::_InlineFunc;
657
      block1->generate_code_all(stack);
658
      stack.merge_state(stack_copy);
659
      stack.opt_show();
660
      stack.o.undent();
661
      stack.o << "}>";
662
      return true;
663
    }
664
    case _Repeat: {
665
      var_idx_t x = left[0];
666
      //stack.drop_vars_except(block0->var_info, x);
667
      stack.rearrange_top(x, var_info[x] && var_info[x]->is_last());
668
      func_assert(stack[0] == x);
669
      stack.opt_show();
670
      stack.s.pop_back();
671
      stack.modified();
672
      if (block0->noreturn()) {
673
        stack.o.retalt_ = true;
674
      }
675
      if (true || !next->is_empty()) {
676
        stack.o << "REPEAT:<{";
677
        stack.o.indent();
678
        stack.forget_const();
679
        if (block0->noreturn()) {
680
          Stack stack_copy{stack};
681
          StackLayout layout1 = stack.vars();
682
          stack_copy.mode &= ~Stack::_InlineFunc;
683
          stack_copy.mode |= Stack::_NeedRetAlt;
684
          block0->generate_code_all(stack_copy);
685
        } else {
686
          StackLayout layout1 = stack.vars();
687
          stack.mode &= ~Stack::_InlineFunc;
688
          stack.mode |= Stack::_NeedRetAlt;
689
          block0->generate_code_all(stack);
690
          stack.enforce_state(std::move(layout1));
691
          stack.opt_show();
692
        }
693
        stack.o.undent();
694
        stack.o << "}>";
695
        return true;
696
      } else {
697
        stack.o << "REPEATEND";
698
        stack.forget_const();
699
        StackLayout layout1 = stack.vars();
700
        block0->generate_code_all(stack);
701
        stack.enforce_state(std::move(layout1));
702
        stack.opt_show();
703
        return false;
704
      }
705
    }
706
    case _Again: {
707
      stack.drop_vars_except(block0->var_info);
708
      stack.opt_show();
709
      if (block0->noreturn()) {
710
        stack.o.retalt_ = true;
711
      }
712
      if (!next->is_empty() || inline_func) {
713
        stack.o << "AGAIN:<{";
714
        stack.o.indent();
715
        stack.forget_const();
716
        StackLayout layout1 = stack.vars();
717
        stack.mode &= ~Stack::_InlineFunc;
718
        stack.mode |= Stack::_NeedRetAlt;
719
        block0->generate_code_all(stack);
720
        stack.enforce_state(std::move(layout1));
721
        stack.opt_show();
722
        stack.o.undent();
723
        stack.o << "}>";
724
        return true;
725
      } else {
726
        stack.o << "AGAINEND";
727
        stack.forget_const();
728
        StackLayout layout1 = stack.vars();
729
        block0->generate_code_all(stack);
730
        stack.enforce_state(std::move(layout1));
731
        stack.opt_show();
732
        return false;
733
      }
734
    }
735
    case _Until: {
736
      // stack.drop_vars_except(block0->var_info);
737
      // stack.opt_show();
738
      if (block0->noreturn()) {
739
        stack.o.retalt_ = true;
740
      }
741
      if (true || !next->is_empty()) {
742
        stack.o << "UNTIL:<{";
743
        stack.o.indent();
744
        stack.forget_const();
745
        auto layout1 = stack.vars();
746
        stack.mode &= ~Stack::_InlineFunc;
747
        stack.mode |= Stack::_NeedRetAlt;
748
        block0->generate_code_all(stack);
749
        layout1.push_back(left[0]);
750
        stack.enforce_state(std::move(layout1));
751
        stack.opt_show();
752
        stack.o.undent();
753
        stack.o << "}>";
754
        stack.s.pop_back();
755
        stack.modified();
756
        return true;
757
      } else {
758
        stack.o << "UNTILEND";
759
        stack.forget_const();
760
        StackLayout layout1 = stack.vars();
761
        block0->generate_code_all(stack);
762
        layout1.push_back(left[0]);
763
        stack.enforce_state(std::move(layout1));
764
        stack.opt_show();
765
        return false;
766
      }
767
    }
768
    case _While: {
769
      // while (block0 | left) block1; ...next
770
      var_idx_t x = left[0];
771
      stack.drop_vars_except(block0->var_info);
772
      stack.opt_show();
773
      StackLayout layout1 = stack.vars();
774
      bool next_empty = false && next->is_empty();
775
      if (block0->noreturn()) {
776
        stack.o.retalt_ = true;
777
      }
778
      stack.o << "WHILE:<{";
779
      stack.o.indent();
780
      stack.forget_const();
781
      stack.mode &= ~Stack::_InlineFunc;
782
      stack.mode |= Stack::_NeedRetAlt;
783
      block0->generate_code_all(stack);
784
      stack.rearrange_top(x, !next->var_info[x] && !block1->var_info[x]);
785
      stack.opt_show();
786
      stack.s.pop_back();
787
      stack.modified();
788
      stack.o.undent();
789
      Stack stack_copy{stack};
790
      stack.o << (next_empty ? "}>DO:" : "}>DO<{");
791
      if (!next_empty) {
792
        stack.o.indent();
793
      }
794
      stack_copy.opt_show();
795
      block1->generate_code_all(stack_copy);
796
      stack_copy.enforce_state(std::move(layout1));
797
      stack_copy.opt_show();
798
      if (!next_empty) {
799
        stack.o.undent();
800
        stack.o << "}>";
801
        return true;
802
      } else {
803
        return false;
804
      }
805
    }
806
    case _TryCatch: {
807
      if (block0->is_empty() && block1->is_empty()) {
808
        return true;
809
      }
810
      if (block0->noreturn() || block1->noreturn()) {
811
        stack.o.retalt_ = true;
812
      }
813
      Stack catch_stack{stack.o};
814
      std::vector<var_idx_t> catch_vars;
815
      std::vector<bool> catch_last;
816
      for (const VarDescr& var : block1->var_info.list) {
817
        if (stack.find(var.idx) >= 0) {
818
          catch_vars.push_back(var.idx);
819
          catch_last.push_back(!block0->var_info[var.idx]);
820
        }
821
      }
822
      const size_t block_size = 255;
823
      for (size_t begin = catch_vars.size(), end = begin; end > 0; end = begin) {
824
        begin = end >= block_size ? end - block_size : 0;
825
        for (size_t i = begin; i < end; ++i) {
826
          catch_stack.push_new_var(catch_vars[i]);
827
        }
828
      }
829
      catch_stack.push_new_var(left[0]);
830
      catch_stack.push_new_var(left[1]);
831
      stack.rearrange_top(catch_vars, catch_last);
832
      stack.opt_show();
833
      stack.o << "c4 PUSH";
834
      stack.o << "c5 PUSH";
835
      stack.o << "c7 PUSH";
836
      stack.o << "<{";
837
      stack.o.indent();
838
      if (block1->noreturn()) {
839
        catch_stack.mode |= Stack::_NeedRetAlt;
840
      }
841
      block1->generate_code_all(catch_stack);
842
      catch_stack.drop_vars_except(next->var_info);
843
      catch_stack.opt_show();
844
      stack.o.undent();
845
      stack.o << "}>CONT";
846
      stack.o << "c7 SETCONT";
847
      stack.o << "c5 SETCONT";
848
      stack.o << "c4 SETCONT";
849
      for (size_t begin = catch_vars.size(), end = begin; end > 0; end = begin) {
850
        begin = end >= block_size ? end - block_size : 0;
851
        stack.o << std::to_string(end - begin) + " PUSHINT";
852
        stack.o << "-1 PUSHINT";
853
        stack.o << "SETCONTVARARGS";
854
      }
855
      stack.s.erase(stack.s.end() - catch_vars.size(), stack.s.end());
856
      stack.modified();
857
      stack.o << "<{";
858
      stack.o.indent();
859
      if (block0->noreturn()) {
860
        stack.mode |= Stack::_NeedRetAlt;
861
      }
862
      block0->generate_code_all(stack);
863
      if (block0->noreturn()) {
864
        stack.s = std::move(catch_stack.s);
865
      } else if (!block1->noreturn()) {
866
        stack.merge_state(catch_stack);
867
      }
868
      stack.opt_show();
869
      stack.o.undent();
870
      stack.o << "}>CONT";
871
      stack.o << "c1 PUSH";
872
      stack.o << "COMPOSALT";
873
      stack.o << "SWAP";
874
      stack.o << "TRY";
875
      return true;
876
    }
877
    default:
878
      std::cerr << "fatal: unknown operation <??" << cl << ">\n";
879
      throw src::ParseError{where, "unknown operation in generate_code()"};
880
  }
881
}
882

883
void Op::generate_code_all(Stack& stack) {
884
  int saved_mode = stack.mode;
885
  auto cont = generate_code_step(stack);
886
  stack.mode = (stack.mode & ~Stack::_ModeSave) | (saved_mode & Stack::_ModeSave);
887
  if (cont && next) {
888
    next->generate_code_all(stack);
889
  }
890
}
891

892
void CodeBlob::generate_code(AsmOpList& out, int mode) {
893
  Stack stack{out, mode};
894
  func_assert(ops && ops->cl == Op::_Import);
895
  auto args = (int)ops->left.size();
896
  for (var_idx_t x : ops->left) {
897
    stack.push_new_var(x);
898
  }
899
  ops->generate_code_all(stack);
900
  stack.apply_wrappers(require_callxargs && (mode & Stack::_InlineAny) ? args : -1);
901
  if (!(mode & Stack::_DisableOpt)) {
902
    optimize_code(out);
903
  }
904
}
905

906
void CodeBlob::generate_code(std::ostream& os, int mode, int indent) {
907
  AsmOpList out_list(indent, &vars);
908
  generate_code(out_list, mode);
909
  out_list.out(os, mode);
910
}
911

912
}  // namespace funC
913

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.