Script Machine Registers

Seeking some thoughts/feedback on this idea. This may or may not be already implemented in other coins, it is not exactly a novel idea.
I was looking at some cashscript stuff and it occurred to me that the variable abstraction would be a lot easier to work with if the script machine had some direct access data storage instead of relying on purely sequential access.

I named this script machine registers because the naming convention for the opcodes matches the LOAD and STORE assembley instructions that load/store data to a register.

This is not meant to be a formal chip.

Title: Script Machine Registers
Maintainer: Griffith
Status: Draft
Initial Publication Date: 2024-07-13
Latest Revision Date: 2024-07-13
Version: 0.1

Script Machine Registers

How would they work

By using two opcodes LOAD and STORE we can save up to X variables into script machine registers for easier use later in the script as a more performant alternative to ROLL and PICK. Initially allow for up to 8 (arbitrary value) registers to be used

script machine registers implemented as std::array<StackItem, 8> vRegisters; in the script machine.

OP_STORE implementation

const StackItem &register_value = stackItemAt(-2);
int64_t register_num CScriptNum(stacktop(-1), fRequireMinimal, maxIntegerSize).getint64();
// in the script machine there are only 8 registers (0-7)
// a register number higher than this a failure
if register_num > 7:
    script failure

// store the value in the register
vRegisters[register_num] = register_value
PopStack(); // pop the register number off the stack
PopStack(); // pop the stored value off the stack

Example:

push 10
push 5
OP_STORE # stores the value 10 into register #5

OP_LOAD implementation

int64_t register_num CScriptNum(stacktop(-1), fRequireMinimal, maxIntegerSize).getint64();
// in the script machine there are only 8 registers (0-7)
// a register number higher than this a failure
if register_num > 7:
    script failure

// load the value in the register
const StackItem register_value& = vRegisters[register_num];
PopStack(); // pop the register number off the stack
PushStack(value) // push the loaded value on to the stack

Example:

push 5
OP_LOAD # pops the top stack item (5) and replaces it with the contents of that register

TODO - consider clearing the register after the value is pushed on to the stack?

Final script state

It is not required that all registers be empty for the script to complete successfully. If values remain when the script is finished, they are cleared/ignored

Changelog

3 Likes

Nice! It would make it easier for anyone coding contracts in raw Script, as stack juggling is a big mental overhead :slight_smile:. It would also reduce script sizes as you’d likely need less opcodes for equivalent operation using current set of opcodes.

I saw a proposal for OP_UNROLL too, or just tweak OP_ROLL so negative index would move the current stack item down the stack.

I wonder, what would the efficiency gains be between status quo and unroll/negroll, and then between unroll/negroll and load/unload.

1 Like

It’s worth noting that this feature is being added to the Nexa blockchain (and being activated March 15th 2025) :grinning_face_with_smiling_eyes:

In the Nexa 1.4.1.0 changelog on Gitlab you find the following:

Implement script machine registers: OP_STORE , OP_LOAD (/doc/script_registers.md)
Implement negative indexes for OP_ROLL/OP_PICK (/doc/negativeRollAndPick.md)

So they are also adding @bitcoincashautist’s mentioned alternative of negative roll/pick indexes.

1 Like

Yes. by me. i specced and wrote both of those.

I was also unaware BCHA mentioned negative indexes somewhere. Where was that?

Two posts above yours.

oh. woops. XD
face palm