3
$\begingroup$

A register-based virtual machine necessarily contains at least some operations to manipulate registers. Like PUSH <register> <value>.

What are some of the most common register-manipulation operations?

$\endgroup$
1
  • 2
    $\begingroup$ Why does it need to be a virtual machine? Wouldn't the same answer apply to real hardware too? $\endgroup$ Commented Jun 11, 2023 at 0:59

2 Answers 2

2
$\begingroup$

Here are some of the most common register-manipulation operations:

  • Load: This operation loads a value from memory into a register. It typically takes an address as an argument and retrieves the value stored at that address.

  • Store: This operation stores a value from a register into memory. It usually takes an address and a register as arguments, storing the value from the register at the specified address in memory.

  • Move: The move operation copies the contents of one register into another register. It is often used for transferring data between registers or for temporary storage.

  • Increment/Decrement: These operations increase or decrease the value in a register by a constant amount, typically 1. They are often used for loop counters or array indexing.

  • Bitwise Operations: Bitwise operations manipulate the individual bits of register values. They include logical AND, OR, XOR, and bit shifting operations (left shift, right shift).

  • Compare: This operation compares the values in two registers and sets flags or status bits based on the result. It is commonly used for branching and conditional execution.

  • Jump/Branch: These operations modify the program counter (PC) to transfer control flow to a different part of the program. They can be used for unconditional jumps or conditional branching based on the result of a previous comparison.

  • Stack Operations: Stack operations manipulate a stack data structure using registers. Common stack operations include push (add a value to the top of the stack), pop (remove and retrieve the top value from the stack), and swap (exchange the top two values on the stack).

$\endgroup$
3
$\begingroup$

The list of instructions is similar to what you'd find in a stack-based VM. The main difference is that in a register-based VM the instructions can operate over any position of the stack, while in a stack-VM the instructions always work with the top of the stack.


One interesting case study is Lua. Up to Lua 4.0 it used a stack-based model and starting from Lua 5.0 it switched to a register-based model. Let's compare them!

Lua 4.0 (stack based)

END RETURN
CALL TAILCALL
POP
PUSHNIL PUSHINT PUSHSTRING PUSHNUM PUSHNEGNUM PUSHUPVALUE
GETLOCAL GETGLOBAL GETTABLE GETDOTTED GETINDEXED
PUSHSELF CREATETABLE
SETLOCAL SETGLOBAL SETTABLE SETLIST SETMAP
ADD ADDI SUB MULT DIV POW CONCAT MINUS NOT
JMPNE JMPEQ JMPLT JMPLE JMPGT JMPGE
JMPT JMPF JMPONT JMPONF JMP
PUSHNILJMP
FORPREP FORLOOP
LFORPREP LFORLOOP
CLOSURE
Lua 5.0 (register based)
MOVE
LOADK LOADBOOL LOADNIL
GETUPVAL GETGLOBAL GETTABLE
SETGLOBAL SETUPVAL SETTABLE
SELF NEWTABLE
ADD SUB MUL DIV POW UNM NOT 
CONCAT
JMP EQ LT LE TEST
CALL TAILCALL RETURN
FORLOOP TFORLOOP  TFORPREP
SETLIST SETLISTO
CLOSE
CLOSURE
  • The stack VM had GETLOCAL and SETLOCAL instructions to get and set local variables, which live in the bottom of the stack. In the register VM both of these use-cases are covered by MOVE, which can copy a value from any source stack slot to any destination stack slot.

  • PUSH instructions were renamed. For example, PUSHBOOL became LOADBOOl because in the register VM the result can go in any stack slot, not just the top.

  • The POP instruction was removed. In the register VM, the stack is treated as a flat array.

  • The register VM can get away with fewer comparison operators. It can implement GT using LE because x>y is equal to y<x. This is harder to do in a stack VM if you want to keep the original order of evaluation (x before y)

Some other changes in the opcode list were unrelated to the stack->register switch. END became a special case of RETURN. PUSHSTRING and PUSHNUM got united in the more general LOADK (load constant). Lua 5.0 introduced new bytecodes for its lambda-function feature (GET/SETUPVAL, CLOSE). GETDOTTED became a special case of GETTABLE (but would be re-introduced in future versions of Lua, under the name GET/SETFIELD). Code that used SETMAP now uses a sequence of SETTABLE instructions.

$\endgroup$

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .