Информация об изменениях

Сообщение Re[3]: Почему нет принудительного TCO? от 11.02.2022 9:54

Изменено 11.02.2022 11:36 netch80

Re[3]: Почему нет принудительного TCO?
Здравствуйте, cppguard, Вы писали:

C>Вот за это люблю С++: на одном ресурсе обсуждаешь что-то из стандарта, и тебе говорят про необязательное существование стека (я бы очень хотел взглянуть на эти платформы),


Тут может быть два разных случая:

1. Стека формально нет в архитектуре, но нет проблем его сэмулировать для языка типа C/C++.
Это, например, обыкновеннейшая System/Z, потомок IBM 360.
Стек для C и других подобных эмулируется через регистр 15.

2. Стека нет в архитектуре и ресурсы не позволяют сэмулировать его. Это вообще исключает рекурсию.

Из опыта — такое было в той же IBM 360 на ранних этапах. Области сохранения данных, если нужно, размещались рядом с функциями (подпрограммами). Сейчас это возможно на супермелких платформах (уровня AVR или ниже).


Живой пример на первое. Берём код (что-то такое чтобы не влезало в регистры):

#include <stdio.h>
struct result { int x, y, z; };
struct result moo(int a, int b, int c, int d, int e, int f, int g, int h) {
  struct result r;
  r.x = a*b + c*d + e*f + g*h; r.y = a*c + b*d + e*g + f*h;
  r.z = a*e + b*f + c*g + d*h;
  printf("x=%d y=%d z=%d\n", r.x, r.y, r.z); return r;
}


Просим ассемблер (запускается на убунте на стандартном десктопе): /usr/bin/s390x-linux-gnu-gcc-9 -S -O t01.c

Смотрим выхлоп (урезаю кучу вычислений и комментирую)

moo:
        ; сохранили callee-saved регистры (включая адрес возврата) скопом в стеке
        stmg    %r6,%r15,48(%r15)
        lay     %r15,-160(%r15) ; зарезервировали 160 байт в стеке
        lgr     %r8,%r2 ; адрес структуры возврата - первый скрытый аргумент
        lr      %r9,%r3 ; a
        msr     %r9,%r4 ; b
        lr      %r1,%r5 ; c
        msr     %r1,%r6 ; d
        ar      %r9,%r1
        l       %r1,324(%r15) ; e (уже не влез в первые 6 регистров, лежит в стеке)
        ms      %r1,332(%r15) ; f (аналогично)
...
        ; вызвали printf(), адрес возврата помещён в r14 (не на стек, это вам не x86)
        brasl   %r14,__printf_chk@PLT
...
        lmg     %r6,%r15,208(%r15) ; восстановили регистры скопом
        br      %r14 ; BR 14 - древний мем, все давно забыли


Но это всё ещё позволяет рекурсию, конечно. Просто для иллюстрации, что стека тут в явном виде нет
(ничто не мешало r15 применить под что угодно другое).

Как это влияет на системное программирование — отдельный разговор — могу описать, если интересно.
Re[3]: Почему нет принудительного TCO?
Здравствуйте, cppguard, Вы писали:

C>Вот за это люблю С++: на одном ресурсе обсуждаешь что-то из стандарта, и тебе говорят про необязательное существование стека (я бы очень хотел взглянуть на эти платформы),


Тут может быть два разных случая:

1. Стека формально нет в архитектуре, но нет проблем его сэмулировать для языка типа C/C++.
Это, например, обыкновеннейшая System/Z, потомок IBM 360. Стек для C и других подобных эмулируется через регистр 15.
Это новомодный RISC-V. Тоже, стек существует только в рамках соглашения (x2 — указатель стека, x1 — регистр адреса возврата), ну и сжатые инструкции и хинты реализации заточены под это (хотите другие — не пользуйтесь сжатыми и будете иметь проблемы секьюрити, где они есть).

2. Стека нет в архитектуре и ресурсы не позволяют сэмулировать его. Это вообще исключает рекурсию.

Из опыта — такое было в той же IBM 360 на ранних этапах. Области сохранения данных, если нужно, размещались рядом с функциями (подпрограммами). Сейчас это возможно на супермелких платформах (уровня AVR или ниже).

-----

Живой пример на первое. Берём код (что-то такое чтобы не влезало в регистры):

#include <stdio.h>
struct result { int x, y, z; };
struct result moo(int a, int b, int c, int d, int e, int f, int g, int h) {
  struct result r;
  r.x = a*b + c*d + e*f + g*h; r.y = a*c + b*d + e*g + f*h;
  r.z = a*e + b*f + c*g + d*h;
  printf("x=%d y=%d z=%d\n", r.x, r.y, r.z); return r;
}


Просим ассемблер (запускается на убунте на стандартном десктопе): /usr/bin/s390x-linux-gnu-gcc-9 -S -O t01.c

Смотрим выхлоп (урезаю кучу вычислений и комментирую)

moo:
        ; сохранили callee-saved регистры (включая адрес возврата) скопом в стеке
        stmg    %r6,%r15,48(%r15)
        lay     %r15,-160(%r15) ; зарезервировали 160 байт в стеке
        lgr     %r8,%r2 ; адрес структуры возврата - первый скрытый аргумент
        lr      %r9,%r3 ; a
        msr     %r9,%r4 ; b
        lr      %r1,%r5 ; c
        msr     %r1,%r6 ; d
        ar      %r9,%r1
        l       %r1,324(%r15) ; e (уже не влез в первые 6 регистров, лежит в стеке)
        ms      %r1,332(%r15) ; f (аналогично)
...
        ; вызвали printf(), адрес возврата помещён в r14 (не на стек, это вам не x86)
        brasl   %r14,__printf_chk@PLT
...
        lmg     %r6,%r15,208(%r15) ; восстановили регистры скопом
        br      %r14 ; BR 14 - древний мем, все давно забыли


Но это всё ещё позволяет рекурсию, конечно. Просто для иллюстрации, что стека тут в явном виде нет
(ничто не мешало r15 применить под что угодно другое).

Как это влияет на системное программирование — отдельный разговор — могу описать, если интересно.