¿El tamaño de la pila de operandos de EVM (en cualquier instrucción) es independiente de la ruta de ejecución?

Estoy tratando de analizar un código de bytes de EVM, comparándolo con el código de bytes de Java.

La especificación Java establece lo siguiente en la especificación JVM §4.9.2. Restricciones estructurales :

Si una instrucción se puede ejecutar a lo largo de varias rutas de ejecución diferentes, la pila de operandos debe tener la misma profundidad (§2.6.2) antes de la ejecución de la instrucción, independientemente de la ruta tomada.

Entonces, para cualquier instrucción dada, la pila de operandos tiene un tamaño bien definido.

Ahora me pregunto si lo mismo es cierto para el código de bytes EVM.

Respuestas (1)

No , esto no es válido para el EVM.

Un contraejemplo simple es una función recursiva:

function f(int a) {
    f(a);
}

Eso se compilaría a:

tag 5
  JUMPDEST          ; method entry
  PUSH [tag] 7      ; push return address
  DUP2              ; push argument `a`
  PUSH [tag] 5      ; push method address
  JUMP [in]         ; call method
tag 7
  JUMPDEST          ; method return

Por cada llamada recursiva, la pila obtiene dos elementos más. Entonces, el tamaño de la pila es diferente para cada llamada.


Ahora, ¿cómo maneja Java la recursividad y mantiene un tamaño fijo de pila de operandos? La diferencia entre JVM y EVM es que JVM crea un nuevo marco con una nueva pila de operandos para cada llamada de método, mientras que EVM solo tiene una pila de operandos global. La EVM ni siquiera conoce el concepto de métodos, aunque se han propuesto subrutinas .


Sin embargo, esto deja una pregunta abierta ... ¿Se mantiene para el tamaño de la pila para el código al lado de las llamadas recursivas? Por ejemplo, para un bloque if-else, ¿el tamaño de la pila sería el mismo después, independientemente de la rama ejecutada?