Repository: tetratelabs/wazero
Pull request #84 (2021-12-23 00:38:11)

Refactor namings around engine and registers
- Renamed `*BaseStack*` -> `*StackBase*`
- Prefixed all the reserved registers with `reservedRegisterFor`

Signed-off-by: Takeshi Yoneda <takeshi@tetrate.io>


Patch:
diff --git a/wasm/jit/RATIONALE.md b/wasm/jit/RATIONALE.md
index 7688a898..8612446f 100644
--- a/wasm/jit/RATIONALE.md
+++ b/wasm/jit/RATIONALE.md
@@ -67,7 +67,7 @@ case jitCallStatusCodeCallWasmFunction:
     // we can resume this caller function frame.
     currentFrame.continuationAddress = currentFrame.f.codeInitialAddress + e.continuationAddressOffset
     currentFrame.continuationStackPointer = e.currentStackPointer + nextFunc.outputNum - nextFunc.inputNum
-    currentFrame.baseStackPointer = e.currentBaseStackPointer
+    currentFrame.baseStackPointer = e.currentStackBasePointer
 ```
 
 and calling into another function in JIT engine's main loop:
@@ -80,7 +80,7 @@ and calling into another function in JIT engine's main loop:
         // Set the caller frame so we can return back to the current frame!
         caller: currentFrame,
         // Set the base pointer to the beginning of the function inputs
-        baseStackPointer: e.currentBaseStackPointer + e.currentStackPointer - nextFunc.inputNum,
+        baseStackPointer: e.currentStackBasePointer + e.currentStackPointer - nextFunc.inputNum,
     }
 ```
 
@@ -92,7 +92,7 @@ case jitStatusReturned:
     // so we just get back to the caller's frame.
     callerFrame := currentFrame.caller
     e.callFrameStack = callerFrame
-    e.currentBaseStackPointer = callerFrame.baseStackPointer
+    e.currentStackBasePointer = callerFrame.baseStackPointer
     e.currentStackPointer = callerFrame.continuationStackPointer
 ```
 
diff --git a/wasm/jit/engine.go b/wasm/jit/engine.go
index 822e960d..73f36d5e 100644
--- a/wasm/jit/engine.go
+++ b/wasm/jit/engine.go
@@ -17,9 +17,9 @@ type engine struct {
 	// The actual Go-allocated stack.
 	// Note that we NEVER edit len or cap in JITed code so we won't get screwed when GC comes in.
 	stack []uint64
-	// Wasm stack pointer on .stack field which is accessed by currentBaseStackPointer+currentStackPointer
+	// Wasm stack pointer on .stack field which is accessed by currentStackBasePointer+currentStackPointer
 	currentStackPointer     uint64
-	currentBaseStackPointer uint64
+	currentStackBasePointer uint64
 	// Where we store the status code of JIT execution.
 	jitCallStatusCode jitCallStatusCode
 	// Set when statusCode == jitStatusCall{Function,BuiltInFunction,HostFunction}
@@ -50,7 +50,7 @@ type engine struct {
 const (
 	engineStackSliceOffset                = 0
 	engineCurrentStackPointerOffset       = 24
-	engineCurrentBaseStackPointerOffset   = 32
+	engineCurrentStackBasePointerOffset   = 32
 	engineJITCallStatusCodeOffset         = 40
 	engineFunctionCallIndexOffset         = 48
 	engineContinuationAddressOffset       = 56
@@ -218,13 +218,13 @@ func newEngine() *engine {
 }
 
 func (e *engine) pop() (ret uint64) {
-	ret = e.stack[e.currentBaseStackPointer+e.currentStackPointer-1]
+	ret = e.stack[e.currentStackBasePointer+e.currentStackPointer-1]
 	e.currentStackPointer--
 	return
 }
 
 func (e *engine) push(v uint64) {
-	e.stack[e.currentBaseStackPointer+e.currentStackPointer] = v
+	e.stack[e.currentStackBasePointer+e.currentStackPointer] = v
 	e.currentStackPointer++
 }
 
@@ -265,7 +265,7 @@ func (s jitCallStatusCode) String() (ret string) {
 type callFrame struct {
 	continuationAddress      uintptr
 	continuationStackPointer uint64
-	baseStackPointer         uint64
+	stackBasePointer         uint64
 	wasmFunction             *compiledWasmFunction
 	hostFunction             *compiledHostFunction
 	caller                   *callFrame
@@ -273,8 +273,8 @@ type callFrame struct {
 
 func (c *callFrame) String() string {
 	return fmt.Sprintf(
-		"[%s: continuation address=%d, continuation stack pointer=%d, base stack pointer=%d]",
-		c.getFunctionName(), c.continuationAddress, c.continuationStackPointer, c.baseStackPointer,
+		"[%s: continuation address=%d, continuation stack pointer=%d, stack base pointer=%d]",
+		c.getFunctionName(), c.continuationAddress, c.continuationStackPointer, c.stackBasePointer,
 	)
 }
 
@@ -322,13 +322,13 @@ const (
 // for the next function frame execution.
 func (e *engine) maybeGrowStack(maxStackPointer uint64) {
 	currentLen := uint64(len(e.stack))
-	remained := currentLen - e.currentBaseStackPointer
+	remained := currentLen - e.currentStackBasePointer
 	if maxStackPointer > remained {
 		// This case we need to grow the stack as the empty slots
 		// are not able to store all the stack items.
 		// So we grow the stack with the new len = currentLen*2+maxStackPointer.
 		newStack := make([]uint64, currentLen*2+(maxStackPointer))
-		top := e.currentBaseStackPointer + e.currentStackPointer
+		top := e.currentStackBasePointer + e.currentStackPointer
 		copy(newStack[:top], e.stack[:top])
 		e.stack = newStack
 	}
@@ -348,9 +348,9 @@ func (e *engine) exec(f *compiledWasmFunction) {
 	for e.callFrameStack != nil {
 		currentFrame := e.callFrameStack
 		if buildoptions.IsDebugMode {
-			fmt.Printf("callframe=%s, currentBaseStackPointer: %d, currentStackPointer: %d, stack: %v\n",
-				currentFrame.String(), e.currentBaseStackPointer, e.currentStackPointer,
-				e.stack[:e.currentBaseStackPointer+e.currentStackPointer],
+			fmt.Printf("callframe=%s, currentStackBasePointer: %d, currentStackPointer: %d, stack: %v\n",
+				currentFrame.String(), e.currentStackBasePointer, e.currentStackPointer,
+				e.stack[:e.currentStackBasePointer+e.currentStackPointer],
 			)
 		}
 
@@ -369,7 +369,7 @@ func (e *engine) exec(f *compiledWasmFunction) {
 			callerFrame := currentFrame.caller
 			e.callFrameStack = callerFrame
 			if callerFrame != nil {
-				e.currentBaseStackPointer = callerFrame.baseStackPointer
+				e.currentStackBasePointer = callerFrame.stackBasePointer
 				e.currentStackPointer = callerFrame.continuationStackPointer
 			}
 		case jitCallStatusCodeCallWasmFunction:
@@ -380,7 +380,7 @@ func (e *engine) exec(f *compiledWasmFunction) {
 			// we can resume this caller function frame.
 			currentFrame.continuationAddress = currentFrame.wasmFunction.codeInitialAddress + e.continuationAddressOffset
 			currentFrame.continuationStackPointer = e.currentStackPointer + nextFunc.returns - nextFunc.inputs
-			currentFrame.baseStackPointer = e.currentBaseStackPointer
+			currentFrame.stackBasePointer = e.currentStackBasePointer
 			// Create the callee frame.
 			frame := &callFrame{
 				continuationAddress: nextFunc.codeInitialAddress,
@@ -388,13 +388,13 @@ func (e *engine) exec(f *compiledWasmFunction) {
 				// Set the caller frame so we can return back to the current frame!
 				caller: currentFrame,
 				// Set the base pointer to the beginning of the function inputs
-				baseStackPointer: e.currentBaseStackPointer + e.currentStackPointer - nextFunc.inputs,
+				stackBasePointer: e.currentStackBasePointer + e.currentStackPointer - nextFunc.inputs,
 			}
 			// If the Go-allocated stack is running out, we grow it before calling into JITed code.
 			e.maybeGrowStack(nextFunc.maxStackPointer)
 			// Now move onto the callee function.
 			e.callFrameStack = frame
-			e.currentBaseStackPointer = frame.baseStackPointer
+			e.currentStackBasePointer = frame.stackBasePointer
 			// Set the stack pointer so that base+sp would point to the top of function inputs.
 			e.currentStackPointer = nextFunc.inputs
 			e.currentGlobalSliceAddress = nextFunc.globalSliceAddress
diff --git a/wasm/jit/jit_amd64.go b/wasm/jit/jit_amd64.go
index cb19f266..16ebbbbf 100644
--- a/wasm/jit/jit_amd64.go
+++ b/wasm/jit/jit_amd64.go
@@ -24,18 +24,6 @@ import (
 // memory is the pointer to the first byte of memoryInstance.Buffer slice to be used by the target function.
 func jitcall(codeSegment, engine, memory uintptr)
 
-// Reserved registers.
-// Note that we don't use "call" instruction (See wasm/jit/RATIONALE.md#general-limitations-on-pure-go-jit-engines)
-const (
-	// engineInstanceReg R12: pointer to engine instance (i.e. *engine as uintptr)
-	engineInstanceReg = x86.REG_R12
-	// cachedStackBasePointerReg R14: cached stack base pointer (engine.currentStackBase) in the current function call.
-	cachedStackBasePointerReg = x86.REG_R14
-	// TODO: we use memoryReg later when we support the store/load memory operations.
-	// memoryReg R15: pointer to memory space (i.e. *[]byte as uintptr).
-	// memoryReg                 = x86.REG_R15
-)
-
 func (e *engine) compileWasmFunction(f *wasm.FunctionInstance) (*compiledWasmFunction, error) {
 	ir, err := wazeroir.Compile(f)
 	if err != nil {
@@ -417,7 +405,7 @@ func (b *amd64Builder) handleSwap(o *wazeroir.OperationSwap) error {
 const globalInstanceValueOffset = 8
 
 func (b *amd64Builder) handleGlobalGet(o *wazeroir.OperationGlobalGet) error {
-	intReg, err := b.allocateRegister(gpTypeInt)
+	intReg, err := b.allocateRegister(generalPurposeRegisterTypeInt)
 	if err != nil {
 		return err
 	}
@@ -428,7 +416,7 @@ func (b *amd64Builder) handleGlobalGet(o *wazeroir.OperationGlobalGet) error {
 	moveGlobalSlicePointer.To.Type = obj.TYPE_REG
 	moveGlobalSlicePointer.To.Reg = intReg
 	moveGlobalSlicePointer.From.Type = obj.TYPE_MEM
-	moveGlobalSlicePointer.From.Reg = engineInstanceReg
+	moveGlobalSlicePointer.From.Reg = reservedRegisterForEngine
 	moveGlobalSlicePointer.From.Offset = engineCurrentGlobalSliceAddressOffset
 	b.addInstruction(moveGlobalSlicePointer)
 
@@ -455,7 +443,7 @@ func (b *amd64Builder) handleGlobalGet(o *wazeroir.OperationGlobalGet) error {
 	wasmType := b.f.ModuleInstance.Globals[o.Index].Type.ValType
 	switch wasmType {
 	case wasm.ValueTypeF32, wasm.ValueTypeF64:
-		valueReg, err = b.allocateRegister(gpTypeFloat)
+		valueReg, err = b.allocateRegister(generalPurposeRegisterTypeFloat)
 		if err != nil {
 			return err
 		}
@@ -491,7 +479,7 @@ func (b *amd64Builder) handleGlobalSet(o *wazeroir.OperationGlobalSet) error {
 	}
 
 	// Allocate a register to hold the memory location of the target global instance.
-	intReg, err := b.allocateRegister(gpTypeInt)
+	intReg, err := b.allocateRegister(generalPurposeRegisterTypeInt)
 	if err != nil {
 		return err
 	}
@@ -502,7 +490,7 @@ func (b *amd64Builder) handleGlobalSet(o *wazeroir.OperationGlobalSet) error {
 	moveGlobalSlicePointer.To.Type = obj.TYPE_REG
 	moveGlobalSlicePointer.To.Reg = intReg
 	moveGlobalSlicePointer.From.Type = obj.TYPE_MEM
-	moveGlobalSlicePointer.From.Reg = engineInstanceReg
+	moveGlobalSlicePointer.From.Reg = reservedRegisterForEngine
 	moveGlobalSlicePointer.From.Offset = engineCurrentGlobalSliceAddressOffset
 	b.addInstruction(moveGlobalSlicePointer)
 
@@ -899,7 +887,7 @@ func (b *amd64Builder) handlePick(o *wazeroir.OperationPick) error {
 		prog := b.newProg()
 		prog.As = x86.AMOVQ
 		prog.From.Type = obj.TYPE_MEM
-		prog.From.Reg = cachedStackBasePointerReg
+		prog.From.Reg = reservedRegisterForStackBasePointer
 		prog.From.Offset = int64(pickTarget.stackPointer) * 8
 		prog.To.Type = obj.TYPE_REG
 		prog.To.Reg = reg
@@ -923,18 +911,18 @@ func (b *amd64Builder) handleAdd(o *wazeroir.OperationAdd) error {
 	switch o.Type {
 	case wazeroir.SignLessTypeI32:
 		instruction = x86.AADDL
-		tp = gpTypeInt
+		tp = generalPurposeRegisterTypeInt
 		panic("add tests!")
 	case wazeroir.SignLessTypeI64:
 		instruction = x86.AADDQ
-		tp = gpTypeInt
+		tp = generalPurposeRegisterTypeInt
 	case wazeroir.SignLessTypeF32:
 		instruction = x86.AADDSS
-		tp = gpTypeFloat
+		tp = generalPurposeRegisterTypeFloat
 		panic("add tests!")
 	case wazeroir.SignLessTypeF64:
 		instruction = x86.AADDSD
-		tp = gpTypeFloat
+		tp = generalPurposeRegisterTypeFloat
 		panic("add tests!")
 	}
 
@@ -984,18 +972,18 @@ func (b *amd64Builder) handleSub(o *wazeroir.OperationSub) error {
 	switch o.Type {
 	case wazeroir.SignLessTypeI32:
 		instruction = x86.ASUBL
-		tp = gpTypeInt
+		tp = generalPurposeRegisterTypeInt
 		panic("add tests!")
 	case wazeroir.SignLessTypeI64:
 		instruction = x86.ASUBQ
-		tp = gpTypeInt
+		tp = generalPurposeRegisterTypeInt
 	case wazeroir.SignLessTypeF32:
 		instruction = x86.ASUBSS
-		tp = gpTypeFloat
+		tp = generalPurposeRegisterTypeFloat
 		panic("add tests!")
 	case wazeroir.SignLessTypeF64:
 		instruction = x86.ASUBSD
-		tp = gpTypeFloat
+		tp = generalPurposeRegisterTypeFloat
 		panic("add tests!")
 	}
 
@@ -1044,24 +1032,24 @@ func (b *amd64Builder) handleLe(o *wazeroir.OperationLe) error {
 	case wazeroir.SignFulTypeInt32:
 		resultConditionState = conditionalRegisterStateLE
 		instruction = x86.ACMPL
-		tp = gpTypeInt
+		tp = generalPurposeRegisterTypeInt
 	case wazeroir.SignFulTypeUint32:
 		resultConditionState = conditionalRegisterStateBE
 		instruction = x86.ACMPL
-		tp = gpTypeInt
+		tp = generalPurposeRegisterTypeInt
 	case wazeroir.SignFulTypeInt64:
 		resultConditionState = conditionalRegisterStateLE
 		instruction = x86.ACMPQ
-		tp = gpTypeInt
+		tp = generalPurposeRegisterTypeInt
 	case wazeroir.SignFulTypeUint64:
 		resultConditionState = conditionalRegisterStateBE
 		instruction = x86.ACMPQ
-		tp = gpTypeInt
+		tp = generalPurposeRegisterTypeInt
 	case wazeroir.SignFulTypeFloat32:
-		tp = gpTypeFloat
+		tp = generalPurposeRegisterTypeFloat
 		panic("add test!")
 	case wazeroir.SignFulTypeFloat64:
-		tp = gpTypeFloat
+		tp = generalPurposeRegisterTypeFloat
 		panic("add test!")
 	}
 
@@ -1109,7 +1097,7 @@ func (b *amd64Builder) handleLe(o *wazeroir.OperationLe) error {
 }
 
 func (b *amd64Builder) handleConstI32(o *wazeroir.OperationConstI32) error {
-	reg, err := b.allocateRegister(gpTypeInt)
+	reg, err := b.allocateRegister(generalPurposeRegisterTypeInt)
 	if err != nil {
 		return err
 	}
@@ -1127,7 +1115,7 @@ func (b *amd64Builder) handleConstI32(o *wazeroir.OperationConstI32) error {
 }
 
 func (b *amd64Builder) handleConstI64(o *wazeroir.OperationConstI64) error {
-	reg, err := b.allocateRegister(gpTypeInt)
+	reg, err := b.allocateRegister(generalPurposeRegisterTypeInt)
 	if err != nil {
 		return err
 	}
@@ -1145,7 +1133,7 @@ func (b *amd64Builder) handleConstI64(o *wazeroir.OperationConstI64) error {
 }
 
 func (b *amd64Builder) handleConstF32(o *wazeroir.OperationConstF32) error {
-	reg, err := b.allocateRegister(gpTypeFloat)
+	reg, err := b.allocateRegister(generalPurposeRegisterTypeFloat)
 	if err != nil {
 		return err
 	}
@@ -1154,7 +1142,7 @@ func (b *amd64Builder) handleConstF32(o *wazeroir.OperationConstF32) error {
 
 	// We cannot directly load the value from memory to float regs,
 	// so we move it to int reg temporarily.
-	tmpReg, err := b.allocateRegister(gpTypeInt)
+	tmpReg, err := b.allocateRegister(generalPurposeRegisterTypeInt)
 	if err != nil {
 		return err
 	}
@@ -1179,7 +1167,7 @@ func (b *amd64Builder) handleConstF32(o *wazeroir.OperationConstF32) error {
 }
 
 func (b *amd64Builder) handleConstF64(o *wazeroir.OperationConstF64) error {
-	reg, err := b.allocateRegister(gpTypeFloat)
+	reg, err := b.allocateRegister(generalPurposeRegisterTypeFloat)
 	if err != nil {
 		return err
 	}
@@ -1188,7 +1176,7 @@ func (b *amd64Builder) handleConstF64(o *wazeroir.OperationConstF64) error {
 
 	// We cannot directly load the value from memory to float regs,
 	// so we move it to int reg temporarily.
-	tmpReg, err := b.allocateRegister(gpTypeInt)
+	tmpReg, err := b.allocateRegister(generalPurposeRegisterTypeInt)
 	if err != nil {
 		return err
 	}
@@ -1234,7 +1222,7 @@ func (b *amd64Builder) moveStackToRegister(loc *valueLocation) {
 	prog := b.newProg()
 	prog.As = x86.AMOVQ
 	prog.From.Type = obj.TYPE_MEM
-	prog.From.Reg = cachedStackBasePointerReg
+	prog.From.Reg = reservedRegisterForStackBasePointer
 	prog.From.Offset = int64(loc.stackPointer) * 8
 	prog.To.Type = obj.TYPE_REG
 	prog.To.Reg = loc.register
@@ -1243,7 +1231,7 @@ func (b *amd64Builder) moveStackToRegister(loc *valueLocation) {
 
 func (b *amd64Builder) moveConditionalToGPRegister(loc *valueLocation) error {
 	// Get the free register.
-	reg, ok := b.locationStack.takeFreeRegister(gpTypeInt)
+	reg, ok := b.locationStack.takeFreeRegister(generalPurposeRegisterTypeInt)
 	if !ok {
 		// This in theory should never be reached as moveConditionalToGPRegister
 		// is called right after comparison operations, meaning that
@@ -1333,7 +1321,7 @@ func (b *amd64Builder) setJITStatus(status jitCallStatusCode) *obj.Prog {
 	prog.From.Type = obj.TYPE_CONST
 	prog.From.Offset = int64(status)
 	prog.To.Type = obj.TYPE_MEM
-	prog.To.Reg = engineInstanceReg
+	prog.To.Reg = reservedRegisterForEngine
 	prog.To.Offset = engineJITCallStatusCodeOffset
 	b.addInstruction(prog)
 	return prog
@@ -1414,7 +1402,7 @@ func (b *amd64Builder) releaseAllRegistersToStack() {
 func (b *amd64Builder) setContinuationOffsetAtNextInstructionAndReturn() {
 	// setContinuationOffsetAtNextInstructionAndReturn is called after releasing
 	// all the registers, so at this point we always have free registers.
-	tmpReg, _ := b.locationStack.takeFreeRegister(gpTypeInt)
+	tmpReg, _ := b.locationStack.takeFreeRegister(generalPurposeRegisterTypeInt)
 	// Create the instruction for setting offset.
 	// We use tmp register to store the const, not directly movq to memory
 	// as it is not valid to move 64-bit const to memory directly.
@@ -1438,7 +1426,7 @@ func (b *amd64Builder) setContinuationOffsetAtNextInstructionAndReturn() {
 	prog.From.Type = obj.TYPE_REG
 	prog.From.Reg = tmpReg
 	prog.To.Type = obj.TYPE_MEM
-	prog.To.Reg = engineInstanceReg
+	prog.To.Reg = reservedRegisterForEngine
 	prog.To.Offset = engineContinuationAddressOffset
 	b.addInstruction(prog)
 	// Then return temporarily -- giving control to normal Go code.
@@ -1451,7 +1439,7 @@ func (b *amd64Builder) setFunctionCallIndexFromRegister(reg int16) {
 	prog.From.Type = obj.TYPE_REG
 	prog.From.Reg = reg
 	prog.To.Type = obj.TYPE_MEM
-	prog.To.Reg = engineInstanceReg
+	prog.To.Reg = reservedRegisterForEngine
 	prog.To.Offset = engineFunctionCallIndexOffset
 	b.addInstruction(prog)
 }
@@ -1462,7 +1450,7 @@ func (b *amd64Builder) setFunctionCallIndexFromConst(index int64) {
 	prog.From.Type = obj.TYPE_CONST
 	prog.From.Offset = index
 	prog.To.Type = obj.TYPE_MEM
-	prog.To.Reg = engineInstanceReg
+	prog.To.Reg = reservedRegisterForEngine
 	prog.To.Offset = engineFunctionCallIndexOffset
 	b.addInstruction(prog)
 }
@@ -1472,7 +1460,7 @@ func (b *amd64Builder) releaseRegisterToStack(loc *valueLocation) {
 	prog := b.newProg()
 	prog.As = x86.AMOVQ
 	prog.To.Type = obj.TYPE_MEM
-	prog.To.Reg = cachedStackBasePointerReg
+	prog.To.Reg = reservedRegisterForStackBasePointer
 	prog.To.Offset = int64(loc.stackPointer) * 8
 	prog.From.Type = obj.TYPE_REG
 	prog.From.Reg = loc.register
@@ -1487,7 +1475,7 @@ func (b *amd64Builder) assignRegisterToValue(loc *valueLocation, reg int16) {
 	prog := b.newProg()
 	prog.As = x86.AMOVQ
 	prog.From.Type = obj.TYPE_MEM
-	prog.From.Reg = cachedStackBasePointerReg
+	prog.From.Reg = reservedRegisterForStackBasePointer
 	prog.From.Offset = int64(loc.stackPointer) * 8
 	prog.To.Type = obj.TYPE_REG
 	prog.To.Reg = reg
@@ -1505,7 +1493,7 @@ func (b *amd64Builder) returnFunction() {
 	prog.From.Type = obj.TYPE_CONST
 	prog.From.Offset = int64(b.locationStack.sp)
 	prog.To.Type = obj.TYPE_MEM
-	prog.To.Reg = engineInstanceReg
+	prog.To.Reg = reservedRegisterForEngine
 	prog.To.Offset = engineCurrentStackPointerOffset
 	b.addInstruction(prog)
 
@@ -1517,7 +1505,7 @@ func (b *amd64Builder) returnFunction() {
 
 // initializeReservedRegisters must be called at the very beginning and all the
 // after-call continuations of JITed functions.
-// This caches the actual stack base pointer (engine.currentBaseStackPointer*8+[engine.engineStackSliceOffset])
+// This caches the actual stack base pointer (engine.currentStackBasePointer*8+[engine.engineStackSliceOffset])
 // to cachedStackBasePointerReg
 func (b *amd64Builder) initializeReservedRegisters() *obj.Prog {
 	// At first, make cachedStackBasePointerReg point to the beginning of the slice backing array.
@@ -1525,24 +1513,24 @@ func (b *amd64Builder) initializeReservedRegisters() *obj.Prog {
 	prog := b.newProg()
 	prog.As = x86.AMOVQ
 	prog.From.Type = obj.TYPE_MEM
-	prog.From.Reg = engineInstanceReg
+	prog.From.Reg = reservedRegisterForEngine
 	prog.From.Offset = engineStackSliceOffset
 	prog.To.Type = obj.TYPE_REG
-	prog.To.Reg = cachedStackBasePointerReg
+	prog.To.Reg = reservedRegisterForStackBasePointer
 	b.addInstruction(prog)
 
 	// initializeReservedRegisters is called at the beginning of function calls
 	// or right after function returns so at this point we always have free registers.
-	reg, _ := b.locationStack.takeFreeRegister(gpTypeInt)
+	reg, _ := b.locationStack.takeFreeRegister(generalPurposeRegisterTypeInt)
 
-	// Next we move the base pointer (engine.currentBaseStackPointer) to
+	// Next we move the base pointer (engine.currentStackBasePointer) to
 	// a temporary register.
 	// movq [engineInstanceReg+engineCurrentBaseStackPointerOffset] reg
 	prog = b.newProg()
 	prog.As = x86.AMOVQ
 	prog.From.Type = obj.TYPE_MEM
-	prog.From.Reg = engineInstanceReg
-	prog.From.Offset = engineCurrentBaseStackPointerOffset
+	prog.From.Reg = reservedRegisterForEngine
+	prog.From.Offset = engineCurrentStackBasePointerOffset
 	prog.To.Type = obj.TYPE_REG
 	prog.To.Reg = reg
 	b.addInstruction(prog)
@@ -1562,7 +1550,7 @@ func (b *amd64Builder) initializeReservedRegisters() *obj.Prog {
 	prog = b.newProg()
 	prog.As = x86.AADDQ
 	prog.To.Type = obj.TYPE_REG
-	prog.To.Reg = cachedStackBasePointerReg
+	prog.To.Reg = reservedRegisterForStackBasePointer
 	prog.From.Type = obj.TYPE_REG
 	prog.From.Reg = reg
 	b.addInstruction(prog)
diff --git a/wasm/jit/jit_amd64.s b/wasm/jit/jit_amd64.s
index 76bcec97..69d11390 100644
--- a/wasm/jit/jit_amd64.s
+++ b/wasm/jit/jit_amd64.s
@@ -7,6 +7,6 @@
 // jitcall(codeSegment, engine, memory uintptr)
 TEXT ·jitcall(SB),NOSPLIT|NOFRAME,$0-24
         MOVQ codeSegment+0(FP),AX  // Load the address of native code.
-        MOVQ engine+8(FP),R12      // Load the address of engine.
+        MOVQ engine+8(FP),R13      // Load the address of engine.
         MOVQ memory+16(FP),R15     // Load the address of memory instance.
         JMP AX                     // Jump to native code.
diff --git a/wasm/jit/jit_value_location_amd64.go b/wasm/jit/jit_value_location_amd64.go
index 711b5fb9..f48d252c 100644
--- a/wasm/jit/jit_value_location_amd64.go
+++ b/wasm/jit/jit_value_location_amd64.go
@@ -9,8 +9,18 @@ import (
 	"github.com/tetratelabs/wazero/wasm/wazeroir"
 )
 
+// Reserved registers.
+const (
+	// reservedRegisterForEngine R13: pointer to engine instance (i.e. *engine as uintptr)
+	reservedRegisterForEngine = x86.REG_R13
+	// reservedRegisterForStackBasePointer R14: stack base pointer (engine.currentStackBasePointer) in the current function call.
+	reservedRegisterForStackBasePointer = x86.REG_R14
+	// reservedRegisterForMemory R15: pointer to memory space (i.e. *[]byte as uintptr).
+	reservedRegisterForMemory = x86.REG_R15
+)
+
 var (
-	gpFloatRegisters = []int16{
+	generalPurposeFloatRegisters = []int16{
 		x86.REG_X0, x86.REG_X1, x86.REG_X2, x86.REG_X3,
 		x86.REG_X4, x86.REG_X5, x86.REG_X6, x86.REG_X7,
 		x86.REG_X8, x86.REG_X9, x86.REG_X10, x86.REG_X11,
@@ -21,19 +31,19 @@ var (
 	// TODO: Maybe it is safe just save rbp, rsp somewhere
 	// in Go-allocated variables, and reuse these registers
 	// in JITed functions and write them back before returns.
-	gpIntRegisters = []int16{
+	unreservedGeneralPurposeIntRegisters = []int16{
 		x86.REG_AX, x86.REG_CX, x86.REG_DX, x86.REG_BX,
 		x86.REG_SI, x86.REG_DI, x86.REG_R8, x86.REG_R9,
-		x86.REG_R10, x86.REG_R11,
+		x86.REG_R10, x86.REG_R11, x86.REG_R12,
 	}
 )
 
 func isIntRegister(r int16) bool {
-	return gpIntRegisters[0] <= r && r <= gpIntRegisters[len(gpIntRegisters)-1]
+	return unreservedGeneralPurposeIntRegisters[0] <= r && r <= unreservedGeneralPurposeIntRegisters[len(unreservedGeneralPurposeIntRegisters)-1]
 }
 
 func isFloatRegister(r int16) bool {
-	return gpFloatRegisters[0] <= r && r <= gpFloatRegisters[len(gpFloatRegisters)-1]
+	return generalPurposeFloatRegisters[0] <= r && r <= generalPurposeFloatRegisters[len(generalPurposeFloatRegisters)-1]
 }
 
 type conditionalRegisterState byte
@@ -71,9 +81,9 @@ type valueLocation struct {
 func (v *valueLocation) registerType() (t generalPurposeRegisterType) {
 	switch v.valueType {
 	case wazeroir.SignLessTypeI32, wazeroir.SignLessTypeI64:
-		t = gpTypeInt
+		t = generalPurposeRegisterTypeInt
 	case wazeroir.SignLessTypeF32, wazeroir.SignLessTypeF64:
-		t = gpTypeFloat
+		t = generalPurposeRegisterTypeFloat
 	default:
 		panic("unreachable")
 	}
@@ -204,8 +214,8 @@ func (s *valueLocationStack) markRegisterUsed(reg int16) {
 type generalPurposeRegisterType byte
 
 const (
-	gpTypeInt generalPurposeRegisterType = iota
-	gpTypeFloat
+	generalPurposeRegisterTypeInt generalPurposeRegisterType = iota
+	generalPurposeRegisterTypeFloat
 )
 
 // Search for unused registers, and if found, returns the register
@@ -213,10 +223,10 @@ const (
 func (s *valueLocationStack) takeFreeRegister(tp generalPurposeRegisterType) (reg int16, found bool) {
 	var targetRegs []int16
 	switch tp {
-	case gpTypeFloat:
-		targetRegs = gpFloatRegisters
-	case gpTypeInt:
-		targetRegs = gpIntRegisters
+	case generalPurposeRegisterTypeFloat:
+		targetRegs = generalPurposeFloatRegisters
+	case generalPurposeRegisterTypeInt:
+		targetRegs = unreservedGeneralPurposeIntRegisters
 	}
 	for _, candidate := range targetRegs {
 		if _, ok := s.usedRegisters[candidate]; ok {
@@ -234,11 +244,11 @@ func (s *valueLocationStack) takeStealTargetFromUsedRegister(tp generalPurposeRe
 		loc := s.stack[i]
 		if loc.onRegister() {
 			switch tp {
-			case gpTypeFloat:
+			case generalPurposeRegisterTypeFloat:
 				if isFloatRegister(loc.register) {
 					return loc, true
 				}
-			case gpTypeInt:
+			case generalPurposeRegisterTypeInt:
 				if isIntRegister(loc.register) {
 					return loc, true
 				}


Test patch:
diff --git a/wasm/jit/engine_test.go b/wasm/jit/engine_test.go
index 7f62c9e1..dcf1a8d1 100644
--- a/wasm/jit/engine_test.go
+++ b/wasm/jit/engine_test.go
@@ -16,7 +16,7 @@ import (
 func TestEngine_veifyOffsetValue(t *testing.T) {
 	require.Equal(t, int(unsafe.Offsetof((&engine{}).stack)), engineStackSliceOffset)
 	require.Equal(t, int(unsafe.Offsetof((&engine{}).currentStackPointer)), engineCurrentStackPointerOffset)
-	require.Equal(t, int(unsafe.Offsetof((&engine{}).currentBaseStackPointer)), engineCurrentBaseStackPointerOffset)
+	require.Equal(t, int(unsafe.Offsetof((&engine{}).currentStackBasePointer)), engineCurrentStackBasePointerOffset)
 	require.Equal(t, int(unsafe.Offsetof((&engine{}).jitCallStatusCode)), engineJITCallStatusCodeOffset)
 	require.Equal(t, int(unsafe.Offsetof((&engine{}).functionCallIndex)), engineFunctionCallIndexOffset)
 	require.Equal(t, int(unsafe.Offsetof((&engine{}).continuationAddressOffset)), engineContinuationAddressOffset)
@@ -98,10 +98,10 @@ func TestEngine_PreCompile(t *testing.T) {
 func TestEngine_maybeGrowStack(t *testing.T) {
 	t.Run("grow", func(t *testing.T) {
 		eng := &engine{stack: make([]uint64, 10)}
-		eng.currentBaseStackPointer = 5
+		eng.currentStackBasePointer = 5
 		eng.push(10)
 		require.Equal(t, uint64(1), eng.currentStackPointer)
-		require.Equal(t, uint64(10), eng.stack[eng.currentBaseStackPointer+eng.currentStackPointer-1])
+		require.Equal(t, uint64(10), eng.stack[eng.currentStackBasePointer+eng.currentStackPointer-1])
 		eng.maybeGrowStack(100)
 		// Currently we have 9 empty slots (10 - 1(base pointer)) above base pointer for new items,
 		// but we require 100 max stack pointer for the next function,
@@ -110,20 +110,20 @@ func TestEngine_maybeGrowStack(t *testing.T) {
 		// maybeAdjustStack only shrink the stack,
 		// and must not modify neither stack pointer nor the values in the stack.
 		require.Equal(t, uint64(1), eng.currentStackPointer)
-		require.Equal(t, uint64(10), eng.stack[eng.currentBaseStackPointer+eng.currentStackPointer-1])
+		require.Equal(t, uint64(10), eng.stack[eng.currentStackBasePointer+eng.currentStackPointer-1])
 	})
 	t.Run("noop", func(t *testing.T) {
 		eng := &engine{stack: make([]uint64, 10)}
-		eng.currentBaseStackPointer = 1
+		eng.currentStackBasePointer = 1
 		eng.push(10)
 		require.Equal(t, uint64(1), eng.currentStackPointer)
-		require.Equal(t, uint64(10), eng.stack[eng.currentBaseStackPointer+eng.currentStackPointer-1])
+		require.Equal(t, uint64(10), eng.stack[eng.currentStackBasePointer+eng.currentStackPointer-1])
 		eng.maybeGrowStack(6)
 		// Currently we have 9 empty slots (10 - 1(base pointer)) above base pointer for new items,
 		// and we only require 6 max stack pointer for the next function, so we have enough empty slots.
 		// so maybeGrowStack must not modify neither stack pointer, the values in the stack nor stack len.
 		require.Len(t, eng.stack, 10)
 		require.Equal(t, uint64(1), eng.currentStackPointer)
-		require.Equal(t, uint64(10), eng.stack[eng.currentBaseStackPointer+eng.currentStackPointer-1])
+		require.Equal(t, uint64(10), eng.stack[eng.currentStackBasePointer+eng.currentStackPointer-1])
 	})
 }
diff --git a/wasm/jit/jit_amd64_test.go b/wasm/jit/jit_amd64_test.go
index 2738a88e..7414ada4 100644
--- a/wasm/jit/jit_amd64_test.go
+++ b/wasm/jit/jit_amd64_test.go
@@ -538,11 +538,11 @@ func Test_popFromStackToRegister(t *testing.T) {
 
 	// Call in.
 	eng := newEngine()
-	eng.currentBaseStackPointer = 1
-	eng.stack[eng.currentBaseStackPointer+2] = 10000
-	eng.stack[eng.currentBaseStackPointer+1] = 20000
+	eng.currentStackBasePointer = 1
+	eng.stack[eng.currentStackBasePointer+2] = 10000
+	eng.stack[eng.currentStackBasePointer+1] = 20000
 	mem := newMemoryInst()
-	require.Equal(t, []uint64{0, 20000, 10000}, eng.stack[eng.currentBaseStackPointer:eng.currentBaseStackPointer+3])
+	require.Equal(t, []uint64{0, 20000, 10000}, eng.stack[eng.currentStackBasePointer:eng.currentStackBasePointer+3])
 	jitcall(
 		uintptr(unsafe.Pointer(&code[0])),
 		uintptr(unsafe.Pointer(eng)),
@@ -550,7 +550,7 @@ func Test_popFromStackToRegister(t *testing.T) {
 	)
 	// Check the sp and value.
 	require.Equal(t, uint64(2), eng.currentStackPointer)
-	require.Equal(t, []uint64{0, 30000}, eng.stack[eng.currentBaseStackPointer:eng.currentBaseStackPointer+eng.currentStackPointer])
+	require.Equal(t, []uint64{0, 30000}, eng.stack[eng.currentStackBasePointer:eng.currentStackBasePointer+eng.currentStackPointer])
 }
 
 func TestAmd64Builder_initializeReservedRegisters(t *testing.T) {
@@ -575,10 +575,10 @@ func TestAmd64Builder_initializeReservedRegisters(t *testing.T) {
 func TestAmd64Builder_allocateRegister(t *testing.T) {
 	t.Run("free", func(t *testing.T) {
 		builder := requireNewBuilder(t)
-		reg, err := builder.allocateRegister(gpTypeInt)
+		reg, err := builder.allocateRegister(generalPurposeRegisterTypeInt)
 		require.NoError(t, err)
 		require.True(t, isIntRegister(reg))
-		reg, err = builder.allocateRegister(gpTypeFloat)
+		reg, err = builder.allocateRegister(generalPurposeRegisterTypeFloat)
 		require.NoError(t, err)
 		require.True(t, isFloatRegister(reg))
 	})
@@ -587,14 +587,14 @@ func TestAmd64Builder_allocateRegister(t *testing.T) {
 		builder := requireNewBuilder(t)
 		builder.initializeReservedRegisters()
 		// Use up all the Int regs.
-		for _, r := range gpIntRegisters {
+		for _, r := range unreservedGeneralPurposeIntRegisters {
 			builder.locationStack.markRegisterUsed(r)
 		}
 		stealTargetLocation := builder.locationStack.pushValueOnRegister(stealTarget)
 		builder.movIntConstToRegister(int64(50), stealTargetLocation.register)
 		require.Equal(t, int16(stealTarget), stealTargetLocation.register)
 		require.True(t, stealTargetLocation.onRegister())
-		reg, err := builder.allocateRegister(gpTypeInt)
+		reg, err := builder.allocateRegister(generalPurposeRegisterTypeInt)
 		require.NoError(t, err)
 		require.True(t, isIntRegister(reg))
 		require.False(t, stealTargetLocation.onRegister())
@@ -611,7 +611,7 @@ func TestAmd64Builder_allocateRegister(t *testing.T) {
 
 		// Run code.
 		eng := newEngine()
-		eng.currentBaseStackPointer = 10
+		eng.currentStackBasePointer = 10
 		mem := newMemoryInst()
 		jitcall(
 			uintptr(unsafe.Pointer(&code[0])),
@@ -621,7 +621,7 @@ func TestAmd64Builder_allocateRegister(t *testing.T) {
 
 		// Check the sp and value.
 		require.Equal(t, uint64(2), eng.currentStackPointer)
-		require.Equal(t, []uint64{50, 2000}, eng.stack[eng.currentBaseStackPointer:eng.currentBaseStackPointer+eng.currentStackPointer])
+		require.Equal(t, []uint64{50, 2000}, eng.stack[eng.currentStackBasePointer:eng.currentStackBasePointer+eng.currentStackPointer])
 	})
 }
 
@@ -710,8 +710,8 @@ func TestAmd64Builder_handlePick(t *testing.T) {
 		pickTargetLocation := builder.locationStack.pushValueOnStack()
 		builder.locationStack.pushValueOnStack() // Dummy value!
 		eng.currentStackPointer = 5
-		eng.currentBaseStackPointer = 1
-		eng.stack[eng.currentBaseStackPointer+pickTargetLocation.stackPointer] = 100
+		eng.currentStackBasePointer = 1
+		eng.stack[eng.currentStackBasePointer+pickTargetLocation.stackPointer] = 100
 
 		// Now insert pick code.
 		err := builder.handlePick(o)
@@ -741,9 +741,9 @@ func TestAmd64Builder_handlePick(t *testing.T) {
 			uintptr(unsafe.Pointer(&mem.Buffer[0])),
 		)
 		// Check the stack.
-		require.Equal(t, uint64(100), eng.stack[eng.currentBaseStackPointer+pickTargetLocation.stackPointer]) // Original value shouldn't be affected.
+		require.Equal(t, uint64(100), eng.stack[eng.currentStackBasePointer+pickTargetLocation.stackPointer]) // Original value shouldn't be affected.
 		require.Equal(t, uint64(3), eng.currentStackPointer)
-		require.Equal(t, uint64(101), eng.stack[eng.currentBaseStackPointer+eng.currentStackPointer-1])
+		require.Equal(t, uint64(101), eng.stack[eng.currentStackBasePointer+eng.currentStackPointer-1])
 	})
 }
 
diff --git a/wasm/jit/jit_value_location_amd64_test.go b/wasm/jit/jit_value_location_amd64_test.go
index 01eacf6f..4410deb5 100644
--- a/wasm/jit/jit_value_location_amd64_test.go
+++ b/wasm/jit/jit_value_location_amd64_test.go
@@ -11,14 +11,21 @@ import (
 	"github.com/twitchyliquid64/golang-asm/obj/x86"
 )
 
+// TestReservedRegisters ensures that reserved registers are not contained in unreservedGeneralPurposeIntRegisters.
+func TestReservedRegisters(t *testing.T) {
+	require.NotContains(t, unreservedGeneralPurposeIntRegisters, reservedRegisterForEngine)
+	require.NotContains(t, unreservedGeneralPurposeIntRegisters, reservedRegisterForStackBasePointer)
+	require.NotContains(t, unreservedGeneralPurposeIntRegisters, reservedRegisterForMemory)
+}
+
 func Test_isIntRegister(t *testing.T) {
-	for _, r := range gpIntRegisters {
+	for _, r := range unreservedGeneralPurposeIntRegisters {
 		require.True(t, isIntRegister(r))
 	}
 }
 
 func Test_isFloatRegister(t *testing.T) {
-	for _, r := range gpFloatRegisters {
+	for _, r := range generalPurposeFloatRegisters {
 		require.True(t, isFloatRegister(r))
 	}
 }
@@ -64,26 +71,26 @@ func TestValueLocationStack_basic(t *testing.T) {
 func TestValueLocationStack_takeFreeRegister(t *testing.T) {
 	s := newValueLocationStack()
 	// For int registers.
-	r, ok := s.takeFreeRegister(gpTypeInt)
+	r, ok := s.takeFreeRegister(generalPurposeRegisterTypeInt)
 	require.True(t, ok)
 	require.True(t, isIntRegister(r))
 	// Mark all the int registers used.
-	for _, r := range gpIntRegisters {
+	for _, r := range unreservedGeneralPurposeIntRegisters {
 		s.markRegisterUsed(r)
 	}
 	// Now we cannot take free ones for int.
-	_, ok = s.takeFreeRegister(gpTypeInt)
+	_, ok = s.takeFreeRegister(generalPurposeRegisterTypeInt)
 	require.False(t, ok)
 	// But we still should be able to take float regs.
-	r, ok = s.takeFreeRegister(gpTypeFloat)
+	r, ok = s.takeFreeRegister(generalPurposeRegisterTypeFloat)
 	require.True(t, ok)
 	require.True(t, isFloatRegister(r))
 	// Mark all the float registers used.
-	for _, r := range gpFloatRegisters {
+	for _, r := range generalPurposeFloatRegisters {
 		s.markRegisterUsed(r)
 	}
 	// Now we cannot take free ones for floats.
-	_, ok = s.takeFreeRegister(gpTypeFloat)
+	_, ok = s.takeFreeRegister(generalPurposeRegisterTypeFloat)
 	require.False(t, ok)
 }
 
@@ -96,25 +103,25 @@ func TestValueLocationStack_takeStealTargetFromUsedRegister(t *testing.T) {
 	s.push(intLocation)
 	s.push(floatLocation)
 	// Take for float.
-	target, ok := s.takeStealTargetFromUsedRegister(gpTypeFloat)
+	target, ok := s.takeStealTargetFromUsedRegister(generalPurposeRegisterTypeFloat)
 	require.True(t, ok)
 	require.Equal(t, floatLocation, target)
 	// Take for ints.
-	target, ok = s.takeStealTargetFromUsedRegister(gpTypeInt)
+	target, ok = s.takeStealTargetFromUsedRegister(generalPurposeRegisterTypeInt)
 	require.True(t, ok)
 	require.Equal(t, intLocation, target)
 	// Pop float value.
 	popped := s.pop()
 	require.Equal(t, floatLocation, popped)
 	// Now we cannot find the steal target.
-	target, ok = s.takeStealTargetFromUsedRegister(gpTypeFloat)
+	target, ok = s.takeStealTargetFromUsedRegister(generalPurposeRegisterTypeFloat)
 	require.False(t, ok)
 	require.Nil(t, target)
 	// Pop int value.
 	popped = s.pop()
 	require.Equal(t, intLocation, popped)
 	// Now we cannot find the steal target.
-	target, ok = s.takeStealTargetFromUsedRegister(gpTypeInt)
+	target, ok = s.takeStealTargetFromUsedRegister(generalPurposeRegisterTypeInt)
 	require.False(t, ok)
 	require.Nil(t, target)
 }