Opcode Quick Reference

The instruction set on one page. The normative version — with stack effects, errors, and the validator rules — is the opcode reference.

[..., a, b] means b is on top. k = constant index, s = slot, u = upvalue index, t = absolute jump target. Result and argument counts are biased: 0 means MULTRET, n means n-1 values (Lab 17).


Constants and Literals

OpOperandsEffect
LOAD_CONSTk:u16push constants[k]
LOAD_INTn:i32push Integer(n) — inline, no pool slot
LOAD_NILpush Nil
LOAD_TRUE / LOAD_FALSEpush the boolean

Locals and Stack

OpOperandsEffect
GET_LOCALs:u8push stack[base+s] — one add, one load
SET_LOCALs:u8pop into stack[base+s]
POPn:u8drop n — how a scope closes

Globals

OpOperandsEffect
GET_GLOBALk:u16push globals[constants[k]]; missing → Nil
SET_GLOBALk:u16pop into the globals table

Upvalues

OpOperandsEffect
GET_UPVALu:u8push the upvalue's value (open → stack, closed → heap)
SET_UPVALu:u8pop into the upvalue
CLOSE_UPVALSs:u8close every open upvalue at or above base+s, then truncate

Arithmetic and Unary

OpStackErrors
ADD SUB MUL[a,b] → [c]non-number operand
DIV POW[a,b] → [Float]non-number operand
IDIV MOD[a,b] → [c]non-number; integer divisor 0
CONCAT[a,b] → [Str]operand not string/number
NEG[a] → [b]non-number
NOT[a] → [Bool]never errors
LEN[a] → [Int]not a string or table

Comparison

OpStackErrors
EQ NE[a,b] → [Bool]never error
LT LE GT GE[a,b] → [Bool]incomparable types

GT/GE exist because Ember guarantees left-to-right operand evaluation and therefore cannot use Lua's operand-swap trick.

Jumps

OpOperandsEffect
JUMPt:u32ip = t
JUMP_IF_FALSEt:u32pop; jump if falsy
JUMP_IF_FALSE_KEEPt:u32peek; jump if falsy; does not pop — for and
JUMP_IF_TRUE_KEEPt:u32peek; jump if truthy; does not pop — for or

Calls, Returns, Closures

OpOperandsEffect
CALLargc:u8, nres:u8callee sits below its args; nres = 0 → all results
RETURNn:u8n = 0 → all; closes upvalues before truncating
CLOSUREp:u16build a closure from protos[p], capturing per its descriptors

Tables

OpOperandsStack
NEW_TABLE[] → [Table]
GET_INDEX[t,k] → [v]
SET_INDEX[t,k,v] → []
GET_FIELDk:u16[t] → [v] — constant key; the inline-cache site
SET_FIELDk:u16[t,v] → []
SET_LISTn:u16, off:u32[t,v₁..vₙ] → [t]
SELF_FIELDk:u16[t] → [t.k, t] — evaluates t once

Numeric for

OpOperandsEffect
FOR_PREPt:u32validate; convert; compute the iteration count; jump to t if zero
FOR_LOOPt:u32decrement the count; advance; jump back to t if positive

Counting down is what makes for i = math.maxinteger - 1, math.maxinteger do terminate.

Varargs

OpOperandsEffect
VARARGn:u8push n-1 varargs; 0 → all

Validator Rules

  1. code.len() == lines.len()
  2. Constant indices in range
  3. Name operands are string constants
  4. Jump targets ≤ code.len(), and none is u32::MAX (unpatched)
  5. Slot numbers < max_stack
  6. Upvalue indices < upvals.len()
  7. Proto indices in range
  8. The last instruction is a RETURN
  9. Stack-depth abstraction: never negative, agrees at every join, ends at 0

Rule 9 is only tractable because Ember is a stack machine — the same reason the JVM and WebAssembly chose one.


Comparison

EmberLua 5.4CPython 3.12JVMWasm 1.0
Modelstackregisterstackstackstack
Opcodes~4483~120202~180
Instruction size8 B enum4 B packed2 B + caches1–n BLEB128
Jump targetsabsoluterelativerelativerelativestructured
Verificationvalidatornonenonefullfull

Next: ADR Index.