29
StackLayout Stack::vars() const {
31
res.reserve(s.size());
33
res.push_back(x.first);
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) {
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) {
58
int Stack::find_outside(var_idx_t var, int from, int to) const {
59
from = std::max(from, 0);
63
int t = find(var, 0, from);
64
return t >= 0 ? t : find(var, to);
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) {
77
void Stack::forget_const() {
79
if (vc.second != not_const) {
80
vc.second = not_const;
85
void Stack::issue_pop(int i) {
87
if (output_enabled()) {
95
void Stack::issue_push(int i) {
97
if (output_enabled()) {
104
void Stack::issue_xchg(int i, int j) {
107
if (i != j && get(i) != get(j)) {
108
if (output_enabled()) {
109
o << AsmOp::Xchg(i, j);
111
std::swap(at(i), at(j));
116
int Stack::drop_vars_except(const VarDescrList& var_info, int excl_var) {
117
int dropped = 0, changes;
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) {
135
void Stack::show(int flags) {
136
std::ostringstream os;
139
o.show_var_ext(os, i);
141
o << AsmOp::Comment(os.str());
145
void Stack::forget_var(var_idx_t idx) {
147
if (x.first == idx) {
148
x = std::make_pair(_Garbage, not_const);
154
void Stack::push_new_var(var_idx_t idx) {
156
s.emplace_back(idx, not_const);
160
void Stack::push_new_const(var_idx_t idx, const_idx_t cidx) {
162
s.emplace_back(idx, cidx);
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;
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) {
180
func_assert(at(0).first == old_idx);
182
assign_var(new_idx, old_idx);
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) {
192
while (depth() > 0 && std::find(req_stack.cbegin(), req_stack.cend(), get(0).first) == req_stack.cend()) {
197
if (j >= depth() - i) {
201
issue_xchg(j, depth() - i - 1);
202
func_assert(s[i].first == x);
204
while (depth() > k) {
207
func_assert(depth() == k);
208
for (int i = 0; i < k; i++) {
209
func_assert(s[i].first == req_stack[i]);
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;
223
void Stack::merge_state(const Stack& req_stack) {
224
enforce_state(req_stack.vars());
225
merge_const(req_stack);
228
void Stack::rearrange_top(const StackLayout& top, std::vector<bool> last) {
229
while (last.size() < top.size()) {
230
last.push_back(false);
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]) {
242
for (int i = 0; i < k; i++) {
247
for (int i = 0; i < k; i++) {
248
var_idx_t x = top[i];
250
int j = find_outside(x, ss, ss + i);
254
func_assert(get(ss).first == x);
259
func_assert(get(ss).first == x);
265
void Stack::rearrange_top(var_idx_t top, bool last) {
272
func_assert(get(0).first == top);
275
bool Op::generate_code_step(Stack& stack) {
277
stack.drop_vars_except(var_info);
279
bool inline_func = stack.mode & Stack::_InlineFunc;
285
stack.enforce_state(left);
286
if (stack.o.retalt_ && (stack.mode & Stack::_NeedRetAlt)) {
293
auto p = next->var_info[left[0]];
294
if (!p || p->is_unused()) {
297
auto cidx = stack.o.register_const(int_const);
298
int i = stack.find_const(cidx);
300
stack.o << push_const(int_const);
301
stack.push_new_const(left[0], cidx);
303
func_assert(stack.at(i).second == cidx);
304
stack.do_copy_var(left[0], stack[i]);
309
auto p = next->var_info[left[0]];
310
if (!p || p->is_unused()) {
313
stack.o << AsmOp::Const("x{" + str_const + "} PUSHSLICE");
314
stack.push_new_var(left[0]);
318
if (dynamic_cast<const SymValGlobVar*>(fun_ref->value)) {
320
for (auto i : left) {
321
auto p = next->var_info[i];
322
if (p && !p->is_unused()) {
326
if (!used || disabled()) {
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());
335
for (auto i : left) {
336
stack.push_new_var(i);
340
func_assert(left.size() == 1);
341
auto p = next->var_info[left[0]];
342
if (!p || p->is_unused() || disabled()) {
345
stack.o << "CONT:<{";
347
auto func = dynamic_cast<SymValAsmFunc*>(fun_ref->value);
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++) {
359
for (int i = 0; i < wr; i++) {
360
args0.emplace_back(0);
362
func->compile(stack.o, res, args0, where);
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());
369
stack.push_new_var(left.at(0));
373
func_assert(left.size() == right.size());
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];
379
auto p = next->var_info[y];
380
active.push_back(p && !p->is_unused());
382
for (std::size_t k = 0; k < left.size(); k++) {
386
var_idx_t x = right[k];
388
for (std::size_t l = k + 1; l < right.size(); l++) {
389
if (right[l] == x && active[l]) {
394
auto info = var_info[x];
395
is_last = (info && info->is_last());
398
stack.assign_var(--i, x);
400
stack.do_copy_var(--i, x);
404
for (std::size_t k = 0; k < left.size(); k++) {
406
stack.assign_var(left[k], --i);
416
std::vector<bool> last;
417
for (var_idx_t x : right) {
418
last.push_back(var_info[x] && var_info[x]->is_last());
420
stack.rearrange_top(right, std::move(last));
422
int k = (int)stack.depth() - (int)right.size();
425
stack.o << AsmOp::Tuple((int)right.size());
426
func_assert(left.size() == 1);
428
stack.o << AsmOp::UnTuple((int)left.size());
429
func_assert(right.size() == 1);
432
for (int i = 0; i < (int)left.size(); i++) {
433
stack.push_new_var(left.at(i));
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;
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);
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)));
465
std::vector<bool> last;
466
for (var_idx_t x : right1) {
467
last.push_back(var_info[x] && var_info[x]->is_last());
469
stack.rearrange_top(right1, std::move(last));
471
int k = (int)stack.depth() - (int)right1.size();
473
for (int i = 0; i < (int)right1.size(); i++) {
474
if (stack.s[k + i].first != right1[i]) {
475
std::cerr << stack.o;
477
func_assert(stack.s[k + i].first == right1[i]);
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);
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);
489
if (cl == _CallInd) {
490
exec_callxargs((int)right.size() - 1, (int)left.size());
492
auto func = dynamic_cast<const SymValAsmFunc*>(fun_ref->value);
494
std::vector<VarDescr> res;
495
res.reserve(left.size());
496
for (var_idx_t i : left) {
499
func->compile(stack.o, res, args, where);
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));
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());
510
stack.o << AsmOp::Custom(name + " CALLDICT", (int)right.size(), (int)left.size());
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));
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());
527
stack.rearrange_top(right, std::move(last));
529
int k = (int)stack.depth() - (int)right.size();
531
for (int i = 0; i < (int)right.size(); i++) {
532
if (stack.s[k + i].first != right[i]) {
533
std::cerr << stack.o;
535
func_assert(stack.s[k + i].first == right[i]);
537
if (right.size() > 1) {
538
stack.o << AsmOp::Tuple((int)right.size());
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);
548
if (block0->is_empty() && block1->is_empty()) {
551
if (!next->noreturn() && (block0->noreturn() != block1->noreturn())) {
552
stack.o.retalt_ = true;
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);
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:<{");
567
Stack stack_copy{stack};
568
block_noreturn->generate_code_all(stack_copy);
570
stack.o << "}>ELSE<{";
572
block_other->generate_code_all(stack);
573
if (!block_other->noreturn()) {
574
next->generate_code_all(stack);
580
if (block1->is_empty() || block0->is_empty()) {
581
bool is0 = block1->is_empty();
582
Op* block = is0 ? block0.get() : block1.get();
585
if (block->noreturn()) {
586
stack.o << (is0 ? "IFJMP:<{" : "IFNOTJMP:<{");
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);
596
stack.o << (is0 ? "IF:<{" : "IFNOT:<{");
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())) {
609
stack.merge_const(stack_copy);
614
stack_copy.enforce_state(stack_target.vars());
615
stack_copy.opt_show();
616
if (stack_copy.vars() == stack.vars()) {
619
stack.merge_const(stack_copy);
623
stack.o << "}>ELSE<{";
625
stack.merge_state(stack_copy);
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:<{");
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);
643
block_other->generate_code_all(stack);
644
return !block_other->noreturn();
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();
654
stack.o << "}>ELSE<{";
656
stack.mode &= ~Stack::_InlineFunc;
657
block1->generate_code_all(stack);
658
stack.merge_state(stack_copy);
665
var_idx_t x = left[0];
667
stack.rearrange_top(x, var_info[x] && var_info[x]->is_last());
668
func_assert(stack[0] == x);
672
if (block0->noreturn()) {
673
stack.o.retalt_ = true;
675
if (true || !next->is_empty()) {
676
stack.o << "REPEAT:<{";
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);
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));
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));
707
stack.drop_vars_except(block0->var_info);
709
if (block0->noreturn()) {
710
stack.o.retalt_ = true;
712
if (!next->is_empty() || inline_func) {
713
stack.o << "AGAIN:<{";
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));
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));
738
if (block0->noreturn()) {
739
stack.o.retalt_ = true;
741
if (true || !next->is_empty()) {
742
stack.o << "UNTIL:<{";
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));
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));
770
var_idx_t x = left[0];
771
stack.drop_vars_except(block0->var_info);
773
StackLayout layout1 = stack.vars();
774
bool next_empty = false && next->is_empty();
775
if (block0->noreturn()) {
776
stack.o.retalt_ = true;
778
stack.o << "WHILE:<{";
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]);
789
Stack stack_copy{stack};
790
stack.o << (next_empty ? "}>DO:" : "}>DO<{");
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();
807
if (block0->is_empty() && block1->is_empty()) {
810
if (block0->noreturn() || block1->noreturn()) {
811
stack.o.retalt_ = true;
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]);
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]);
829
catch_stack.push_new_var(left[0]);
830
catch_stack.push_new_var(left[1]);
831
stack.rearrange_top(catch_vars, catch_last);
833
stack.o << "c4 PUSH";
834
stack.o << "c5 PUSH";
835
stack.o << "c7 PUSH";
838
if (block1->noreturn()) {
839
catch_stack.mode |= Stack::_NeedRetAlt;
841
block1->generate_code_all(catch_stack);
842
catch_stack.drop_vars_except(next->var_info);
843
catch_stack.opt_show();
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";
855
stack.s.erase(stack.s.end() - catch_vars.size(), stack.s.end());
859
if (block0->noreturn()) {
860
stack.mode |= Stack::_NeedRetAlt;
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);
871
stack.o << "c1 PUSH";
872
stack.o << "COMPOSALT";
878
std::cerr << "fatal: unknown operation <??" << cl << ">\n";
879
throw src::ParseError{where, "unknown operation in generate_code()"};
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);
888
next->generate_code_all(stack);
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);
899
ops->generate_code_all(stack);
900
stack.apply_wrappers(require_callxargs && (mode & Stack::_InlineAny) ? args : -1);
901
if (!(mode & Stack::_DisableOpt)) {
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);