fuel-specs icon indicating copy to clipboard operation
fuel-specs copied to clipboard

Consider adding a `cfe` instruction to the vm.

Open nfurfaro opened this issue 3 years ago • 2 comments

Motivation

While working to implement a load_contract() function for the stdlib, I need to wrap the ldc opcode. ldc requires that the stack be empty when used, or the VM will panic. As there is no way to ensure this function will always be called first, we need to clear the stack manually.

This entails stashing the current stack values on the heap temporarily, and then restoring them later. When we try to restore n bytes from the heap to the stack, we need to allocate n bytes. Currently, the only way to allocate stack space is with the cfei opcode, but we don't know the value of n yet so we can't use an immediate.

Proposal

Add a cfe instruction, and possibly the corresponding cfs instruction as well.

nfurfaro avatar Jun 10 '22 13:06 nfurfaro

It could be possible to allocate dynamic sizes now using a logarithmic expansion algorithm in a loop.

Pseudocode:

while sp - ssp < size {
    let remaining = size - (sp - ssp)
    if remaining > 256 then CFEI 256 else
    if remaining > 128 then CFEI 128 else
    if remaining > 64 then CFEI 64 else
    ....
    if remaining > 1 then CFEI 1
}

Now that I've spelled it out it looks like a waste of gas (though it could be impoved by updating remaining as you go meaning a small enough allocation could happen in a single iteration).

TBH, the need to allocate an unknown amount of stack space is a pretty rare case -- otherwise you'd use the heap. In your case you're explicitly manipulating the stack which is particularly rare.

It might be safer to introduce instructions for creating a new stack. To save $ssp on the current stack and then change it to be at the top of free space, creating a fresh stack. Then it could be wiped away by restoring $sp and $ssp from that saved value quite easily. No copying to or from the heap required.

If we had regular push and pop it would be the equivalent to:

# new stack
push ssp
move ssp sp

# restore stack
move sp ssp
pop ssp

Moving to $ssp is disallowed, so these two operations could be new specific instructions.

otrho avatar Jun 11 '22 00:06 otrho

It's been a while since this was opened, so I want to resurface it as it will be needed for supporting upgradeable contract patterns.

nfurfaro avatar Sep 26 '22 12:09 nfurfaro

Recent IRL chats should be mentioned here:

  • Perhaps we should relax the requirement that $sp == $ssp and instead just clobber the stack with the loaded code and set $sp to the new $ssp.
  • Position independent code will be needed, most easily realised via relative jumps. #430
  • load_contract() could provide an API which puts a persistent context on the heap, relieving the need to copy back to the new stack or a CFE. It could also provide ways to call the loaded code. It really depends on the use cases.

Clobbering the stack is pretty hardcore and would require library support for a context switch. It would require a special handler, a bit like an interrupt, which would ensure that clobbering the stack is not harmful and the heap based context is preserved (or at least the pointer to it is). I imagine it'd be a bit like EXECV(3) from unix.

otrho avatar Nov 23 '22 03:11 otrho

It would require a special handler, a bit like an interrupt, which would ensure that clobbering the stack is not harmful and the heap based context is preserved (or at least the pointer to it is). I imagine it'd be a bit like EXECV(3) from unix.

Would this be at the runtime / library level? Or would the VM need to do anything to support this?

Voxelot avatar Feb 27 '23 20:02 Voxelot

No, this would be all compiler/library stuff.

otrho avatar Feb 28 '23 01:02 otrho