The pushl
Y86 instruction both decrements the stack pointer by 4 and writes a register value to memory. So it's not clear what the processor should do when it executes the instruction pushl %esp
, since the register being pushed is being changed by the same instruction. Two possible events can occur:
(1) push the original value of %esp
, or (2) push the decremented value of %esp
.
In light of this, how could we modify this code-equivalent of pushl REG
to account for, and accomdate, these ambiguities (being that REG can be %esp as well as any other register)?:
subl $4,%esp Decrement stack pointer
movl REG,(%esp) Store REG on stack
Similarly, the instruction popl %esp
could either set %esp
to the value read from memory or to the incremented stack pointer. How could this code be changed to accommodate for these ambiguities?:
movl (%esp),REG Read REG from stack
addl $4,%esp Increment stack pointer
y86 is based on x86. The x86 instruction-set reference manual entry for push
says (correctly):
The PUSH ESP instruction pushes the value of the ESP register as it existed before the instruction was executed.
And pop
:
The POP ESP instruction increments the stack pointer (ESP) before data at the old top of stack is written into the destination.
So in the pop %esp
case, the increment is lost. This sequence has the same effect, although most real CPUs probably load into temporary internal storage instead of actually using an updated ESP value in the addressing mode.
add $4, %esp
movl -4(%esp), %esp
But pop %esp
does that without updating FLAGS, and without a possibility of an interrupt or signal-handler between the add and mov. (The separate add/mov sequence isn't safe in contexts where anything below the current %esp
can be asynchronously overwritten by an interrupt handler.)
Presumably y86 does the same thing as x86. You can easily (and should) check with a debugger to see how your favourite y86 simulator handles this corner case. push %esp
is easy to test by looking at memory (or adding a pop %eax
after it).
Testing both at once would get confusing, and if the value you pop is the same as the old stack pointer, you can't tell the difference. Probably push a 0
(or store a 0
to (%esp)
), then pop %esp
and see what value is in the register with a debugger. (It doesn't matter if your code crashes afterwards, you're just using a debugger.)
I didn't check if y86 supports push $0
or movl $0, (%esp)
like x86. I guess it would be immovl $0, (%esp)
if it's supported (immediate to memory). If not, then zero a register and push that.