V8の最適化されていない機械語を理解する


# はじめに

今更ですが、V8のソースコード読解とデバッグをはじめてしばらく経過したので、ちょっとつまずいた部分などをメモしていきます。なお、V8(d8)のバージョンは、13.3.0をソースコードからビルドしました。

# 仮想マシンの実装方法

Ignitionで生成されたバイトコードがどのように実行されているかを確認しようとしたのですが、 https://github.com/v8/v8/tree/main/src/interpreter
のフォルダにあるファイルにブレークポイントを設定しても、実行されている形跡がありません。

調べているうちに、どうやら、このコードは、V８がコンパイルされたときに、すぐに実行され、ビルドした環境の機械語のみが直接、V8に組み込まれるようでした。

組み込まれたソースコードの一覧は、d8 --log-code などと打つと、v8.logファイルに組み込まれた機械語の関数一覧が表示されます。

該当行は、ログファイルの行先頭にcode-creationと書かれていて、その次に、Builtinまたは、BytecodeHandlerと書かれています。

```v8.log
...
new,MemoryChunk,0xbdd00000000,262144
code-creation,Builtin,2,142921,0x7fff7f2c0000,1028,DeoptimizationEntry_Eager
...
code-creation,BytecodeHandler,0,191851,0x7fff7fd90d40,304,Wide
...

```


BytecodeHandlerですが、これは、Ignitionが生成したバイトコードの命令と対応しているようです。バイトコードが読み取られた後に、BytecodeHandlerのアドレスが、ある関数テーブルにあり、バイトコードの命令を指す最初の１バイトが直接BytecodeHandlerのアドレスを指し示すようになっているようです。

```v8.log
code-creation,BytecodeHandler,0,191851,0x7fff7fd90d40,304,Wide
code-creation,BytecodeHandler,0,191874,0x7fff7fd90e80,304,ExtraWide
code-creation,BytecodeHandler,0,191897,0x7fff7fd90fc0,560,DebugBreakWide
code-creation,BytecodeHandler,0,191919,0x7fff7fd91200,560,DebugBreakExtraWide
code-creation,BytecodeHandler,0,191942,0x7fff7fd91440,664,DebugBreak0
```

以下は、デバッガによる関数テーブル先頭からのメモリの内容です。

```
-exec x/500x 0x55555572d820
0x55555572d820:	0x7fd90d40	0x00007fff	0x7fd90e80	0x00007fff
0x55555572d830:	0x7fd90fc0	0x00007fff	0x7fd91200	0x00007fff
0x55555572d840:	0x7fd91440	0x00007fff	0x7fd91700	0x00007fff
0x55555572d850:	0x7fd91940	0x00007fff	0x7fd91b80	0x00007fff
0x55555572d860:	0x7fd91dc0	0x00007fff	0x7fd92000	0x00007fff
0x55555572d870:	0x7fd92240	0x00007fff	0x7fd92480	0x00007fff
0x55555572d880:	0x7fd925c0	0x00007fff	0x7fd92780	0x00007fff
0x55555572d890:	0x7fd92980	0x00007fff	0x7fd92b40	0x00007fff
...
```

BytecodeHandlerの置かれたアドレスと関数テーブルのアドレスが一致していることが確認できました。

## InterpreterEntryTrampolineの実装

次に、Builtinですが、これは、個々の関数毎色々役割があります。BytecodeHandlerから呼ばれるものが多いですが、個々のBytecodeHandlerを呼び出すCallerとしての役割である、InterpreterEntryTrampolineの機械語について見ておくとよいでしょう。

<details><summary>InterpreterEntryTrampoline</summary>

```
   # 0xf may be JSFunction::kSharedFunctionInfoOffset
   # r11 is shared_function_info
   0x7fff7f30cec0:	mov    r11d,DWORD PTR [rdi+0xf]
   0x7fff7f30cec4:	add    r11,r14
   # ResetSharedFunctionInfoAge
   # 0x2b is SharedFunctionInfo::kAgeOffset
   0x7fff7f30cec7:	mov    WORD PTR [r11+0x2b],0x0
   # GetSharedFunctionInfoBytecodeOrBaseline
   0x7fff7f30cece:	mov    r10d,DWORD PTR [r11+0x3]
   ## CheckSharedFunctionInfoBytecodeOrBaseline
   0x7fff7f30ced2:	test   r10b,0x1
   0x7fff7f30ced6:	je     0x7fff7f30cef2
   0x7fff7f30ced8:	mov    r12,QWORD PTR [r13+0x26e8]
   0x7fff7f30cedf:	shr    r10d,0x9
   0x7fff7f30cee3:	shl    r10d,0x4
   0x7fff7f30cee7:	mov    r12,QWORD PTR [r12+r10*1+0x8]
   0x7fff7f30ceec:	or     r12,0x1
   0x7fff7f30cef0:	jmp    0x7fff7f30cf0e
   0x7fff7f30cef2:	shr    r10d,0x9
   0x7fff7f30cef6:	mov    r12,QWORD PTR [r13+0x218]
   0x7fff7f30cefd:	mov    r12,QWORD PTR [r12+r10*8]
   0x7fff7f30cf01:	movabs r10,0x80ffffffffffff
   0x7fff7f30cf0b:	and    r12,r10
   0x7fff7f30cf0e:	mov    r10d,DWORD PTR [r12-0x1]
   0x7fff7f30cf13:	cmp    r10d,0xd59
   0x7fff7f30cf1a:	jne    0x7fff7f30cf42
   0x7fff7f30cf20:	mov    r10d,DWORD PTR [r12+0x1b]
   0x7fff7f30cf25:	and    r10,0xf
   0x7fff7f30cf29:	cmp    r10d,0xa
   0x7fff7f30cf2d:	je     0x7fff7f30cf3c
   0x7fff7f30cf2f:	mov    edx,0x16
   0x7fff7f30cf34:	call   QWORD PTR [r13+0x57b0] # Abort
   0x7fff7f30cf3b:	int3   
   0x7fff7f30cf3c:	je     0x7fff7f30d185
   0x7fff7f30cf42:	cmp    r10d,0x20a5
   0x7fff7f30cf49:	jne    0x7fff7f30cf57
   0x7fff7f30cf4b:	mov    r12d,DWORD PTR [r12+0x7]
   0x7fff7f30cf50:	or     r12,QWORD PTR [r13+0x210]
   0x7fff7f30cf57:	mov    r10d,DWORD PTR [r12-0x1]
   0x7fff7f30cf5c:	cmp    r10d,0x941
   0x7fff7f30cf63:	jne    0x7fff7f30d10b
   ############################################################
   # LoadParameterCountFromJSDispatchTable
   # r13+0x26f8 is ExternalReference::js_dispatch_table_address
   0x7fff7f30cf69:	mov    r10,QWORD PTR [r13+0x26f8]
   0x7fff7f30cf70:	mov    r8,r15
   0x7fff7f30cf73:	shr    r8d,0x9
   0x7fff7f30cf77:	shl    r8d,0x4
   0x7fff7f30cf7b:	movzx  r8d,WORD PTR [r10+r8*1+0x8]
   # 0x1f is BytecodeArray::kParameterSizeOffset
   0x7fff7f30cf81:	cmp    r8w,WORD PTR [r12+0x1f]
   0x7fff7f30cf87:	je     0x7fff7f30cf96
   0x7fff7f30cf89:	mov    edx,0x9a
   0x7fff7f30cf8e:	call   QWORD PTR [r13+0x57b0] # Abort
   0x7fff7f30cf95:	int3   
   # LoadFeedBackVector ???
   0x7fff7f30cf96:	mov    ebx,DWORD PTR [rdi+0x17]
   0x7fff7f30cf99:	mov    ebx,DWORD PTR [r14+rbx*1+0x3]
   0x7fff7f30cf9e:	add    rbx,r14
   0x7fff7f30cfa1:	mov    ecx,DWORD PTR [rbx-0x1]
   0x7fff7f30cfa4:	cmp    ecx,0x801
   0x7fff7f30cfaa:	je     0x7fff7f30cfb2
   0x7fff7f30cfac:	lea    rbx,[r14+0x11]
   0x7fff7f30cfb0:	jmp    0x7fff7f30cfc1
   # ResetFeedbackVectorOsrUrgency
   # 0xc is FeedbackVector::kOsrStateOffset
   0x7fff7f30cfb2:	mov    r10b,BYTE PTR [rbx+0xc]
   # 0x8 is Immediate(~FeedbackVector::OsrUrgencyBits::kMask)
   0x7fff7f30cfb6:	and    r10b,0xf8
   0x7fff7f30cfba:	mov    BYTE PTR [rbx+0xc],r10b
   # 0x7 may be FeedbackVector::kInvocationCountOffset
   0x7fff7f30cfbe:	inc    DWORD PTR [rbx+0x7]
   0x7fff7f30cfc1:	push   rbp
   0x7fff7f30cfc2:	mov    rbp,rsp
   0x7fff7f30cfc5:	push   rsi # kContextRegister
   0x7fff7f30cfc6:	push   rdi # kJavaScriptCallTargetRegister
   0x7fff7f30cfc7:	push   rax # kJavaScriptCallArgCountRegister
   # 0x27 is BytecodeArray::kHeaderSize(0x28) - kHeapObjectTag(1)
   # r9 is kInterpreterBytecodeOffsetRegister
   0x7fff7f30cfc8:	mov    r9d,0x27 
   # r12 is kInterpreterBytecodeArrayRegister
   0x7fff7f30cfce:	push   r12
   0x7fff7f30cfd0:	mov    ecx,r9d
   0x7fff7f30cfd3:	add    ecx,ecx
   0x7fff7f30cfd5:	rol    rcx,0x20
   0x7fff7f30cfd9:	xor    rcx,0x515151
   0x7fff7f30cfe0:	ror    rcx,0x20
   # 
   0x7fff7f30cfe4:	push   rcx
   # rbx is FeedbackVector
   0x7fff7f30cfe5:	push   rbx
   0x7fff7f30cfe6:	mov    ecx,DWORD PTR [r12+0x1b]
   0x7fff7f30cfeb:	mov    rax,rsp
   0x7fff7f30cfee:	sub    rax,rcx
   0x7fff7f30cff1:	cmp    rax,QWORD PTR [r13-0x70]
   0x7fff7f30cff5:	jb     0x7fff7f30d1fe
   0x7fff7f30cffb:	lea    rax,[r14+0x11]
   0x7fff7f30cfff:	jmp    0x7fff7f30d002
   0x7fff7f30d001:	push   rax
   0x7fff7f30d002:	sub    rcx,0x8
   0x7fff7f30d006:	jge    0x7fff7f30d001
   0x7fff7f30d008:	movsxd rcx,DWORD PTR [r12+0x23]
   0x7fff7f30d00d:	test   ecx,ecx
   0x7fff7f30d00f:	je     0x7fff7f30d016
   0x7fff7f30d011:	mov    QWORD PTR [rbp+rcx*8+0x0],rdx
   0x7fff7f30d016:	cmp    rsp,QWORD PTR [r13-0x60]
   0x7fff7f30d01a:	jb     0x7fff7f30d0c8
   # load the head address of byte code handler table
   0x7fff7f30d020:	mov    r15,QWORD PTR [r13+0x4f78]
   # r12 + r9 will be the offset to the bytecode. 
   # read the bytecode to jump to bytecode handler.
   0x7fff7f30d027:	movzx  r10d,BYTE PTR [r12+r9*1]
   # r15 is a head of function pointer
   # r10 is an offset to the bytecode handler to be called. 
   0x7fff7f30d02c:	mov    rcx,QWORD PTR [r15+r10*8]
   ############################################################
=> 0x7fff7f30d030:	call   rcx
   ############################################################
   0x7fff7f30d032:	mov    r12,QWORD PTR [rbp-0x20]
   ## if it is 0, will abort.
   0x7fff7f30d036:	mov    r9d,DWORD PTR [rbp-0x28]
   
   0x7fff7f30d03a:	test   r9d,0x10000000
   0x7fff7f30d041:	je     0x7fff7f30d050
   0x7fff7f30d043:	mov    edx,0x4
   # Abort
   0x7fff7f30d048:	call   QWORD PTR [r13+0x57b0]
   0x7fff7f30d04f:	int3   
   ##########
   0x7fff7f30d050:	shr    r9d,1
   # r12 + r9d should be the next bytecode to be executed. 
   0x7fff7f30d053:	movzx  ebx,BYTE PTR [r12+r9*1]
   0x7fff7f30d058:	mov    r8,r9
   0x7fff7f30d05b:	mov    rcx,QWORD PTR [r13+0x1e90]
   0x7fff7f30d062:	cmp    bl,0x3
   0x7fff7f30d065:	ja     0x7fff7f30d084
   0x7fff7f30d067:	inc    r9d
   0x7fff7f30d06a:	test   bl,0x1
   0x7fff7f30d06d:	movzx  ebx,BYTE PTR [r12+r9*1]
   0x7fff7f30d072:	jne    0x7fff7f30d07d
   0x7fff7f30d074:	add    rcx,0xd0
   0x7fff7f30d07b:	jmp    0x7fff7f30d084
   0x7fff7f30d07d:	add    rcx,0x1a0
   0x7fff7f30d084:	cmp    bl,0xb3
   0x7fff7f30d087:	je     0x7fff7f30d0ad
   0x7fff7f30d08d:	cmp    bl,0xb9
   0x7fff7f30d090:	je     0x7fff7f30d0ad
   0x7fff7f30d096:	cmp    bl,0x92
   0x7fff7f30d099:	jne    0x7fff7f30d0a0
   0x7fff7f30d09b:	mov    r9,r8
   0x7fff7f30d09e:	jmp    0x7fff7f30d0a8
   0x7fff7f30d0a0:	movzx  r10d,BYTE PTR [rcx+rbx*1]
   0x7fff7f30d0a5:	add    r9d,r10d
   0x7fff7f30d0a8:	jmp    0x7fff7f30d020
   0x7fff7f30d0ad:	mov    rbx,QWORD PTR [rbp-0x20]
   0x7fff7f30d0b1:	movzx  ebx,WORD PTR [rbx+0x1f]
   0x7fff7f30d0b5:	mov    rcx,QWORD PTR [rbp-0x18]
   0x7fff7f30d0b9:	cmp    rbx,rcx
   0x7fff7f30d0bc:	cmovl  rbx,rcx
   0x7fff7f30d0c0:	leave  
   0x7fff7f30d0c1:	pop    rcx
   0x7fff7f30d0c2:	lea    rsp,[rsp+rbx*8]
   0x7fff7f30d0c6:	push   rcx
   0x7fff7f30d0c7:	ret    
   ############################################################
   0x7fff7f30d0c8:	mov    r10d,0x4c
   0x7fff7f30d0ce:	mov    QWORD PTR [rbp-0x28],r10
   0x7fff7f30d0d2:	xor    eax,eax
   0x7fff7f30d0d4:	mov    rbx,QWORD PTR [r13+0x40f8] # rbx=0x7ffff4c6
   0x7fff7f30d0db:	call   0x7fff7f744540 # CEntry_Return1_ArgvOnStack_NoBuiltinExit
   # _ZN2v88internal18Runtime_StackGuardEiPmPNS0_7IsolateE
   0x7fff7f30d0e0:	mov    r12,QWORD PTR [rbp-0x20]
   0x7fff7f30d0e4:	mov    r9d,0x27
   0x7fff7f30d0ea:	lea    rax,[r14+0x11]
   0x7fff7f30d0ee:	mov    ecx,r12d
   0x7fff7f30d0f1:	add    ecx,ecx
   0x7fff7f30d0f3:	rol    rcx,0x20
   0x7fff7f30d0f7:	xor    rcx,0x515151
   0x7fff7f30d0fe:	ror    rcx,0x20
   0x7fff7f30d102:	mov    QWORD PTR [rbp-0x28],rcx
   0x7fff7f30d106:	jmp    0x7fff7f30d020
   0x7fff7f30d10b:	push   rbp # // Caller's frame pointer.
   0x7fff7f30d10c:	mov    rbp,rsp
   0x7fff7f30d10f:	push   0x2e
   0x7fff7f30d111:	push   rdi
   0x7fff7f30d112:	push   rdx
   0x7fff7f30d113:	add    eax,eax
   0x7fff7f30d115:	rol    rax,0x20
   0x7fff7f30d119:	xor    rax,0x515151
   0x7fff7f30d11f:	ror    rax,0x20
   0x7fff7f30d123:	push   rax
   0x7fff7f30d124:	push   r15
   0x7fff7f30d126:	push   rdi
   0x7fff7f30d127:	mov    eax,0x1
   0x7fff7f30d12c:	mov    rbx,QWORD PTR [r13+0x3c78] # 0x7ffff4c3d720:CompileLazy
   0x7fff7f30d133:	call   0x7fff7f744540
   0x7fff7f30d138:	mov    rcx,rax
   0x7fff7f30d13b:	pop    r15
   0x7fff7f30d13d:	pop    rax
   0x7fff7f30d13e:	test   eax,0x10000000
   0x7fff7f30d143:	je     0x7fff7f30d152
   0x7fff7f30d145:	mov    edx,0x4
   0x7fff7f30d14a:	call   QWORD PTR [r13+0x57b0]
   0x7fff7f30d151:	int3   
   0x7fff7f30d152:	shr    eax,1
   0x7fff7f30d154:	pop    rdx
   0x7fff7f30d155:	pop    rdi
   0x7fff7f30d156:	cmp    QWORD PTR [rbp-0x8],0x2e
   0x7fff7f30d15b:	je     0x7fff7f30d16a
   0x7fff7f30d15d:	mov    edx,0x60
   0x7fff7f30d162:	call   QWORD PTR [r13+0x57b0]
   0x7fff7f30d169:	int3   
   0x7fff7f30d16a:	mov    rsp,rbp
   0x7fff7f30d16d:	pop    rbp
   0x7fff7f30d16e:	mov    r10,QWORD PTR [r13+0x26e8]
   0x7fff7f30d175:	mov    ecx,DWORD PTR [rcx+0x3]
   0x7fff7f30d178:	shr    ecx,0x9
   0x7fff7f30d17b:	shl    ecx,0x4
   0x7fff7f30d17e:	mov    rcx,QWORD PTR [r10+rcx*1]
   0x7fff7f30d182:	jmp    rcx
   0x7fff7f30d184:	int3  
   ###################################################
   0x7fff7f30d185:	push   rbp
   0x7fff7f30d186:	mov    rbp,rsp
   0x7fff7f30d189:	push   0x2e
   0x7fff7f30d18b:	push   rdi
   0x7fff7f30d18c:	push   rdx
   0x7fff7f30d18d:	add    eax,eax
   0x7fff7f30d18f:	rol    rax,0x20
   0x7fff7f30d193:	xor    rax,0x515151
   0x7fff7f30d199:	ror    rax,0x20
   0x7fff7f30d19d:	push   rax
   0x7fff7f30d19e:	push   r15
   0x7fff7f30d1a0:	push   rdi
   0x7fff7f30d1a1:	mov    eax,0x1


```

</details>

マーク「=>」のある0x7fff7f30d030の`call rcx`から各BytecodeHandlerが呼ばれます。このrcxは前の命令を見ると、`[r15+r10*8]`のメモリを読み取っています。この`r15`が関数テーブルの先頭を指しています。`r10`は、その前の命令を見ると、`[r12+r9*1]`を読み取っていて、このアドレスは実は、現在読み込もうとしているバイトコードの命令の１バイト目を指しています。

この機械語を生成するソースコードはこちら(https://github.com/v8/v8/blob/main/src/builtins/x64/builtins-x64.cc#L1106) にありますが、ソースコードだけを読んでもなかなか分かりにくいところです。



なお、このTrampolineから全てのHandlerが呼ばれるわけではなく、BasicBlockの先頭の命令のみが呼ばれます。それ以外の命令に関しては、各命令列のHandlerから直接jmp命令を経由して鎖のように呼ばれます。

# LdaZeroの実装

幾つかのHandlerの機械語を確認して、BytecodeHandlerがどのように実装されているかを見てみましょう。

LdaZero (0x0c)の命令は、アキュミュレータレジスタに０をロードする命令です。この命令の解釈には以下のような機械語が実装されていました。

<details><summary>LdaZero</summary>

```
   0x7fff7fd925c0:	lea    rbx,[rip+0xfffffffffffffff9]        # 0x7fff7fd925c0
   0x7fff7fd925c7:	cmp    rbx,rcx
   0x7fff7fd925ca:	je     0x7fff7fd925d9
   0x7fff7fd925cc:	mov    edx,0x88
   0x7fff7fd925d1:	call   QWORD PTR [r13+0x57b0]
   0x7fff7fd925d8:	int3   
   0x7fff7fd925d9:	push   rbp
   0x7fff7fd925da:	mov    rbp,rsp
   0x7fff7fd925dd:	push   0x24
   0x7fff7fd925df:	sub    rsp,0x20
   0x7fff7fd925e3:	mov    r10,rsp
   0x7fff7fd925e6:	sub    rsp,0x8
   0x7fff7fd925ea:	and    rsp,0xfffffffffffffff0
   0x7fff7fd925ee:	mov    QWORD PTR [rsp],r10
   #################################################
   0x7fff7fd925f2:	mov    QWORD PTR [rbp-0x10],r12
   0x7fff7fd925f6:	mov    QWORD PTR [rbp-0x18],r15
   0x7fff7fd925fa:	mov    QWORD PTR [rbp-0x28],rax
   0x7fff7fd925fe:	mov    QWORD PTR [rbp-0x20],r9
   #################################################
   0x7fff7fd92602:	mov    esi,0x20e
   0x7fff7fd92607:	mov    rdx,QWORD PTR [r13+0x1b98]
   0x7fff7fd9260e:	mov    edx,DWORD PTR [rdx+0x6627]
   0x7fff7fd92614:	add    rdx,r14
   0x7fff7fd92617:	mov    rdi,r12
   0x7fff7fd9261a:	mov    rax,QWORD PTR [r13+0x1e98]
   0x7fff7fd92621:	test   spl,0xf
   0x7fff7fd92625:	je     0x7fff7fd92628
   0x7fff7fd92627:	int3   
   0x7fff7fd92628:	lea    r10,[rip+0x10]        # 0x7fff7fd9263f
   0x7fff7fd9262f:	mov    QWORD PTR [r13+0xa8],r10
   0x7fff7fd92636:	mov    QWORD PTR [r13+0xa0],rbp
   0x7fff7fd9263d:	call   rax
   0x7fff7fd9263f:	mov    QWORD PTR [r13+0xa0],0x0
   0x7fff7fd9264a:	mov    rsp,QWORD PTR [rsp]
   0x7fff7fd9264e:	mov    r10,rsp
   0x7fff7fd92651:	sub    rsp,0x8
   0x7fff7fd92655:	and    rsp,0xfffffffffffffff0
   0x7fff7fd92659:	mov    QWORD PTR [rsp],r10
   0x7fff7fd9265d:	xor    esi,esi
   0x7fff7fd9265f:	mov    rdx,QWORD PTR [r13+0x1b98]
   0x7fff7fd92666:	mov    edx,DWORD PTR [rdx+0x662b]
   0x7fff7fd9266c:	add    rdx,r14
   0x7fff7fd9266f:	mov    rdi,QWORD PTR [rbp-0x28]
   0x7fff7fd92673:	mov    rax,QWORD PTR [r13+0x1e98]
   0x7fff7fd9267a:	test   spl,0xf
   0x7fff7fd9267e:	je     0x7fff7fd92681
   0x7fff7fd92680:	int3   
   0x7fff7fd92681:	lea    r10,[rip+0x10]        # 0x7fff7fd92698
   0x7fff7fd92688:	mov    QWORD PTR [r13+0xa8],r10
   0x7fff7fd9268f:	mov    QWORD PTR [r13+0xa0],rbp
   0x7fff7fd92696:	call   rax
   0x7fff7fd92698:	mov    QWORD PTR [r13+0xa0],0x0
   0x7fff7fd926a3:	mov    rsp,QWORD PTR [rsp]
   #################################################
   0x7fff7fd926a7:	mov    rbx,QWORD PTR [rbp-0x20]
   0x7fff7fd926ab:	lea    r9,[rbx+0x1]
   0x7fff7fd926af:	mov    r12,QWORD PTR [rbp-0x10]
   0x7fff7fd926b3:	movzx  r8d,BYTE PTR [r12+rbx*1+0x1]
   #################################################
   0x7fff7fd926b9:	mov    r10d,0xffffffff
   0x7fff7fd926bf:	cmp    r8,r10
   0x7fff7fd926c2:	jbe    0x7fff7fd926d1
   0x7fff7fd926c4:	mov    edx,0x2
   0x7fff7fd926c9:	call   QWORD PTR [r13+0x57b0]
   0x7fff7fd926d0:	int3   
   0x7fff7fd926d1:	cmp    r8b,0xbf
   0x7fff7fd926d5:	jae    0x7fff7fd926eb
   0x7fff7fd926d7:	mov    r15,QWORD PTR [rbp-0x18]
   0x7fff7fd926db:	mov    rcx,QWORD PTR [r15+r8*8]
   0x7fff7fd926df:	mov    rbp,QWORD PTR [rbp+0x0]
   0x7fff7fd926e3:	xor    eax,eax
   0x7fff7fd926e5:	add    rsp,0x30
   0x7fff7fd926e9:	jmp    rcx
   0x7fff7fd926eb:	cmp    r8b,0xce
   0x7fff7fd926ef:	ja     0x7fff7fd92737
   0x7fff7fd926f1:	mov    r11,QWORD PTR [rbp+0x0]
   0x7fff7fd926f5:	mov    QWORD PTR [r11+r8*8-0x6a8],0x0
   0x7fff7fd92701:	add    r9,0x1
   #################################################
   0x7fff7fd92705:	movzx  ebx,BYTE PTR [r12+rbx*1+0x2]
   0x7fff7fd9270b:	mov    r10d,0xffffffff
   0x7fff7fd92711:	cmp    rbx,r10
   0x7fff7fd92714:	jbe    0x7fff7fd92723
   0x7fff7fd92716:	mov    edx,0x2
   0x7fff7fd9271b:	call   QWORD PTR [r13+0x57b0]
   0x7fff7fd92722:	int3   
   0x7fff7fd92723:	mov    r15,QWORD PTR [rbp-0x18]
   0x7fff7fd92727:	mov    rcx,QWORD PTR [r15+rbx*8]
   0x7fff7fd9272b:	mov    rbp,QWORD PTR [rbp+0x0]
   0x7fff7fd9272f:	xor    eax,eax
   0x7fff7fd92731:	add    rsp,0x30
   0x7fff7fd92735:	jmp    rcx
   0x7fff7fd92737:	mov    rdx,QWORD PTR [r13+0x1b98]
   0x7fff7fd9273e:	mov    edx,DWORD PTR [rdx+0x663b]
   0x7fff7fd92744:	add    rdx,r14
   0x7fff7fd92747:	call   0x7fff7f46c780
   0x7fff7fd9274c:	int3   
   0x7fff7fd9274d:	int3   
   0x7fff7fd9274e:	int3   

```

</details>

先ず、`0x7fff7fd925f2`から始まる機械語に着目してみましょう。`r9`、`r12`がスタックに格納されています。この関数が`InterpreterEntryTrampoline`から呼ばれることがあることに着目すると、`r9`と`r12`を足し合わせたメモリに現在のバイトコード(0x0c)が格納されています。

この`r9`と`r12`が、`0x7fff7fd926a7`から始まる命令列で再びレジスタに読み込まれています。そして、`0x7fff7fd926b3`で、`movzx  r8d,BYTE PTR [r12+rbx*1+0x1]`しているということは、これは、`LdaZero`の次の命令列を読みだそうとしていることを意味します。なお、LdaZeroは、１バイト命令のため、これはオペランドの読み込みでは無く、次の命令のロードとなります。この後、この値が、`0x7fff7fd926d1`と`0x7fff7fd926eb`で、0xbfと0xceの間にある値かどうか確認しています。先ず、0xbfより小さい場合、`0x7fff7fd926e9`の`jmp rcx`で次のBytecodeHandlerに遷移します。そして、肝心のLdaZeroの処理ですが、`0x7fff7fd926e3`で、`eax`を0にしています。実は、`rax`はアキュミュレータレジスタを格納するために使用されており、これが実質的に、`LdaZero`の処理を指します。

次の命令列が、0xbfと0xceの間にある場合はどうでしょうか。実は、この範囲の命令は、全てstor0,stor1などのアキュミュレータレジスタからX番目レジスタに値を移す命令です。以下の部分の処理ですが、これはX番目のレジスタに０を格納するということを意味します。
```
0x7fff7fd926f1:	mov    r11,QWORD PTR [rbp+0x0]
0x7fff7fd926f5:	mov    QWORD PTR [r11+r8*8-0x6a8],0x0
```
アキュミュレータ以外のレジスタ値は、[rbp+0x0]の指すメモリ上に列を成して格納されているということです。

つまり、LdaZeroのBytecodeHandlerの処理の中で、次の命令がStor系の命令の場合は、アキュミュレータに０を格納するという点を省略して、直接それ以外のレジスタに値を格納しています。

こんな感じで、BytecodeHandlerの中で、レジスタが何を指すか理解することにより、仮想マシンについて理解を進めることができます。