Пост-Инкремент возврата: как они реализуются?

Итак, я знаю, что многие языки в стиле C имеют декремент (--) и приращение (++) и позволяют мутации произойти до или после вычисления всего выражения.

Что происходит, когда после возврата происходит постинкремент? Я не спрашиваю с точки зрения поведения, а скорее реализации.

Учитывая виртуальную машину (например, JavaScript / JVM) или физическую машину (например, скомпилированный C ++), сгенерированные коды операций похожи на следующие? (Предполагая основанные на стеке аргументы / возвраты.)

int x = 4, y = 8;
return f(++a) + y++;

Превращается в это: (может быть?)

LOAD 4 A
LOAD 8 B

INC A
PUSH A
CALL F
POP INTO C
ADD C BY B
INC B
RET C

Если так, как такие операции в этих языках решают, куда встраивать приращение, когда выражение становится сложным, возможно, даже немного Lispish?

0

Решение

Как только код был оптимизирован (либо компилятором C ++, либо JIT), я ожидал что-то похожее на следующее:

Источник:

int x = 4, y = 8;
return f(++x) + y++;

Инструкции:

PUSH 5
CALL f
POP INTO A
ADD 8 to A
RET A

Инструкции, которые я перечислил, верны (если я не сделал какую-то глупую ошибку), и они требуют только тех преобразований кода, которые, как я знаю, могут быть выполнены оптимизаторами. В основном, неиспользованные результаты не рассчитываются, а операции с известными результатами вычисляются во время оптимизации.

Вполне возможно, что в конкретной архитектуре есть более эффективный способ сделать это, и в этом случае есть хороший шанс, что оптимизатор узнает об этом и будет его использовать. Оптимизаторы часто превосходят мои ожидания, так как я не опытный программист сборки. В этом случае весьма вероятно, что соглашение о вызовах диктует одно и то же местоположение для возвращаемого значения в случае функции f и этот код. Так что никакой POP может не понадобиться — в регистр возврата / расположение стека можно просто добавить 8. Кроме того, если функция f тогда оптимизация будет применена после встраивание.

Так, например, если f возвращается input * 2 тогда вся функция может быть оптимизирована для:

RET 18
2

Другие решения

Среда выполнения Java далека от вашего исходного кода и даже от байт-кода. Как только метод JIT-скомпилирован, результирующий машинный код агрессивно оптимизируется. Но что еще более важно, детали этой оптимизации выходят далеко за рамки любой спецификации. Если вас это заинтересует, вы можете углубиться в реализацию, подобную HotSpot, но все, что вы узнаете из нее, будет зависеть от платформы, версии, номера сборки, аргументов запуска JVM и даже от отдельного запуска JVM.

3

Вы можете точно увидеть, что генерирует компилятор, используя javap. Например:

int x = 4, y = 8;
return f(++x) + y++;

был скомпилирован в эту последовательность байт-кода:

0:  iconst_4
1:  istore_1
2:  bipush  8
4:  istore_2
5:  aload_0
6:  iinc    1, 1
9:  iload_1
10: invokevirtual   #2; //Method f:(I)I
13: iload_2
14: iinc    2, 1
17: iadd
18: ireturn

Конечно, это зависит от JVM, как превратить это в ассемблер — смотрите Разберите Java JIT скомпилированный собственный байт-код о том, как вы можете увидеть результаты в OpenJDK 7.

1
По вопросам рекламы [email protected]