diff --git a/book/docs/8_0_eval_loop.md b/book/docs/8_0_eval_loop.md
index 0a041dc..e00e439 100644
--- a/book/docs/8_0_eval_loop.md
+++ b/book/docs/8_0_eval_loop.md
@@ -4,14 +4,20 @@
- [8.0 개요](#80-개요)
- [8.1 스레드 상태 생성하기](#81-스레드-상태-생성하기)
- [8.2 프레임 객체 생성하기](#82-프레임-객체-생성하기)
+ - [프레임 객체 초기화 API](#프레임-객체-초기화-api)
- [8.3 프레임 실행](#83-프레임-실행)
+ - [프레임 실행 추적](#프레임-실행-추적)
- [8.4 값 스택](#84-값-스택)
+ - [값 스택 시뮬레이션: 바이트 코드 명령 예제, BINARY_OR](#값-스택-시뮬레이션-바이트코드-명령-예제-binary_or)
+ - [바이트코드 예측](#바이트코드-예측)
- [8.6 요약](#86-요약)
## 8.0 개요
-지금까지는 파이썬으로 작성된 코드들을 파싱하여, 어떻게 구문 분석을 실시하고, 코드 객체로 컴파일 하는지에 대해 알아보았습니다. 컴파일된 코드 객체는 바이트 코드로 표현된 연산 리스트를 포함하게 됩니다.
-이번 장에서는 값 스택 이라는 개념을 소개하는데, 컴파일된 코드 객체의 바이트 코드 연산들은 값 스택에서, 변수를 생성하고 변경하여 사용하게 됩니다.
+지금까지는 파이썬으로 작성된 코드들을 파싱하여, 어떻게 구문 분석을 실시하고, 코드 객체로 컴파일 하는지에 대해 알아보았습니다.
+컴파일된 코드 객체는 바이트 코드로 표현된 연산 리스트를 포함하게 됩니다.
+이번 장에서는 값 스택 이라는 개념을 소개하는데,
+컴파일된 코드 객체의 바이트 코드 연산들은 값 스택에서 변수를 생성하고 변경하여 사용하게 됩니다.
CPython 에서 코드는 **평가 루프** 라는 개념을 통해 루프(Loop)를 돌며 실행되게 됩니다.
@@ -34,23 +40,56 @@ CPython 에서 코드는 **평가 루프** 라는 개념을 통해 루프(Loop)
## 8.1 스레드 상태 생성하기
-프레임을 실행하려면 스레드에 연결해야 합니다.(스레드에서 생성하기 떄문). 인터프리터 상태에서 인터프리터는 스레드들을 연결 리스트의 형태로 스레드들을 관리하게 됩니다.
-
-앞서 설명하였듯이, 스레드는 각자 고유의 스레드 상태를 가지게 되는데, 스레드 상태를 나타내는 PyThreadState 가 그 상태를 나타내게 됩니다.
-
+프레임을 실행하려면 스레드에 연결해야 합니다 (스레드에서 생성하기 떄문).
+인터프리터 상태에서 인터프리터는 스레드들을 연결 리스트의 형태로 스레드들을 관리하게 됩니다.
+앞서 설명하였듯이, 스레드는 각자 고유의 스레드 상태를 가지게 되는데,
+스레드 상태를 나타내는 PyThreadState 가 그 상태를 나타내게 됩니다.
![CST_EXPR](../images/8_eval_loop/02_eval2.JPG)
-위의 PyThreadState는 _ts 의 별칭으로 사용되게 됩니다. 코드를 살펴보게 되면, 고유 식별자, 다른 스레드 상태와 연결된 연결 리스트, 인터프리터의 상태, 재귀 깊이 등을 나타내는 값들로 이루어져 있습니다.
+위의 PyThreadState는 _ts 의 별칭으로 사용되게 됩니다.
+코드를 살펴보게 되면, 고유 식별자, 다른 스레드 상태와 연결된 연결 리스트,
+인터프리터의 상태, 재귀 깊이 등을 나타내는 값들로 이루어져 있습니다.
-
-
+
+조금만 자세히 들여다보자면, 각 스레드는 스레드 상태(`PyThreadState`)를 가지게 되고,
+스레드 상태는 `PyInterpreterState` 내부에서 연결 리스트로 관리한다는 것을 알 수 있습니다.
+
+``` c
+/* Include/pystate.h */
+
+/* struct _ts is defined in cpython/pystate.h */
+typedef struct _ts PyThreadState;
+/* struct _is is defined in internal/pycore_interp.h */
+typedef struct _is PyInterpreterState;
+```
+
+
+
+``` c
+/* Include/internal/pycore_interp.h */
+
+// The PyInterpreterState typedef is in Include/pystate.h.
+struct _is {
+
+ struct _is *next;
+ struct _ts *tstate_head;
+
+ /* skip */
+}
+```
+
+
## 8.2 프레임 객체 생성하기
-AST 를 거쳐 컴파일된 객체는 프레임 객체에 최종적으로 삽입되게 됩니다. 파이썬 타입인 프레임 객체는 C 와 파이썬 코드 양쪽에서 참조할 수 있도록 설계되었습니다. 프레임 객체는 **코드 객체의 명령** 을 실행하는데 필요한 런타임 데이터를 포함하고, 런타임 데이터에는 **전역 변수, 지역 변수, 내장 모듈 등**이 포함됩니다.
+AST 를 거쳐 컴파일된 객체는 프레임 객체에 최종적으로 삽입되게 됩니다.
+파이썬 타입인 프레임 객체는 C 와 파이썬 코드 양쪽에서 참조할 수 있도록 설계되었습니다.
+
+프레임 객체는 **코드 객체의 명령** 을 실행하는데 필요한 런타임 데이터를 포함하고,
+런타임 데이터에는 **전역 변수, 지역 변수, 내장 모듈 등**이 포함됩니다.
@@ -58,40 +97,485 @@ AST 를 거쳐 컴파일된 객체는 프레임 객체에 최종적으로 삽입
![Parser_Tokenizer](../images/8_eval_loop/03_eval3.JPG)
+
+
다음은 프로퍼티를 가진 CPython 의 코드 입니다.
![Parser_Tokenizer](../images/8_eval_loop/04_eval4.JPG)
-인터프리터에 의해 만들어진 PyFrameObject 는 초기화 과정을 거치게 되는데, PyEval_EvalCode( ) 라는 함수 안에서 초기화 과정을 거치게 됩니다.
+인터프리터에 의해 만들어진 `PyFrameObject`는 초기화 과정을 거치게 되는데,
+`PyEval_EvalCode()` 라는 함수 안에서 초기화 과정을 거치게 됩니다.
![Parser_Tokenizer](../images/8_eval_loop/05_eval5.JPG)
-
-
-
+`_PyEval_EvalCode()` 함수를 따라 프레임에 대한 데이터들이 추가되고, 프레임이 해석되며, 코드가 실행되게 됩니다.
+
+
+
+### 프레임 객체 초기화 API
+프레임 객체를 초기화하는 API인 `PyEval_EvalCode()`는 코드 객체를 평가하기 위한 진입점입니다.
+
+`PyEval_EvalCode()`는 내부 함수` _PyEval_EvalCode()` 를 감싸는 Wrapper 함수입니다.
+`PyEval_EvalCode()`는 아래와 같은 호출 스택을 가집니다.
+
+|호출 스택|
+|:---:|
+|`_PyEval_EvalCode`|
+|`_PyEval_EvalCodeWithName`|
+|`PyEval_EvalCodeEx`|
+|`PyEval_EvalCode`|
+
+
+
+`_PyEval_EvalCode()`는 인터프리터 루프와 프레임 객체 동작의 상당 부분을 정의합니다.
+`PyEval_EvalCode()`는 아래와 같으며 3개의 인자를 받습니다.
+
+``` c
+/* Python/ceval.c */
+
+PyObject *
+PyEval_EvalCode(PyObject *co, PyObject *globals, PyObject *locals)
+{
+ return PyEval_EvalCodeEx(co,
+ globals, locals,
+ (PyObject **)NULL, 0,
+ (PyObject **)NULL, 0,
+ (PyObject **)NULL, 0,
+ NULL, NULL);
+}
+```
+- `co` : 코드 객체
+- `globals`, `locals` : 지역 및 전역 변수
+
+
+
+`_PyEval_EvalCode()`는 다양한 인자를 받는다.
+
+```c
+/* Python/ceval.c */
+
+_PyEval_EvalCode(PyThreadState *tstate,
+ PyObject *_co, PyObject *globals, PyObject *locals,
+ PyObject *const *args, Py_ssize_t argcount,
+ PyObject *const *kwnames, PyObject *const *kwargs,
+ Py_ssize_t kwcount, int kwstep,
+ PyObject *const *defs, Py_ssize_t defcount,
+ PyObject *kwdefs, PyObject *closure,
+ PyObject *name, PyObject *qualname)
+```
+
+- `tstate` : 코드를 평가할 스레드의 상태를 나타냄 (PyThreadState*)
+- `_co` : 프레임 객체에 삽입할 코드 객체 (PyCodeObject*)
+- `globals` : 전역 변수, 변수명을 키로 사용 (PyObject*-dict)
+- `locals` : 지역 변수, 변수명을 키로 사용 (PyObject*-dict)
+
+위 4개의 인자를 제외한 인자는 선택 인자로 기본 API에서는 사용하지 않습니다.
+
+
+
+여기까지 주요 흐름을 정리하자면 다음과 같습니다.
+1. 스레드 상태가 유효한지 확인한다.
+2. 프레임 객체를 선언하고 그 반환값을 초기화하는 등 초기화 작업을 진행한다.
+3. 프레임 객체를 생성한다.
+ ``` c
+ /* Python/ceval.c */
+
+ PyObject *
+ _PyEval_EvalCode(PyThreadState *tstate,
+ PyObject *_co, PyObject *globals, PyObject *locals,
+ PyObject *const *args, Py_ssize_t argcount,
+ PyObject *const *kwnames, PyObject *const *kwargs,
+ Py_ssize_t kwcount, int kwstep,
+ PyObject *const *defs, Py_ssize_t defcount,
+ PyObject *kwdefs, PyObject *closure,
+ PyObject *name, PyObject *qualname)
+ {
+ /* skip */
+
+ /* Create the frame */
+ f = _PyFrame_New_NoTrack(tstate, co, globals, locals);
+ if (f == NULL) {
+ return NULL;
+ }
+
+ /* skip */
+ }
+ ```
+4. 인자와 변수를 처리한다.
+5. 제너레이터 객체와 코루틴 객체를 처리한다.
+6. 프레임을 실행하고 결과를 저장한다.
+7. 프레임 객체의 참조 수를 확인하고 관련 객체들을 해제한다.
+8. 결과를 반환한다.
+
+
+
+
+여기서 `_PyFrame_New_NoTrack()`에 조금만 자세히 알아보도록 합시다.
+`_PyFrame_New_NoTrack()`는 `_PyEval_EvalCode()`의 기본 인자 4가지를 받습니다.
+
+```c
+/* Objects/frameobject.c */
+
+PyFrameObject* _Py_HOT_FUNCTION
+_PyFrame_New_NoTrack(PyThreadState *tstate, PyCodeObject *code,
+ PyObject *globals, PyObject *locals)
+```
+
+아래 과정을 통해 새로운 PyFrameObject를 생성하여 반환한다.
+- 프레임의 f_back 프로퍼티를 스레드 상태의 마지막 프레임으로 설정한다.
+- f_builtins 프로퍼티를 설정 → PyModule_GetDict() 로 builtins 모듈에서 내장 함
+수들을 불러온다.
+- f_code 프로퍼티에 평가 중인 코드 객체를 설정한다.
+- f_valuestack 프로퍼티에 빈 값 스택을 설정한다.
+- f_stackpop 에 f_valuestack 을 가리키는 포인터를 설정한다.
+- 전역 이름 공간 프로퍼티인 f_globals 에 인자로 받은 globals 를 설정한다.
+- 지역 이름 공간 프로퍼티인 f_locals 에 새 딕셔너리를 설정한다.
+- TraceBack에 줄 번호를 표시하기 위해 f_lineno 를 코드 객체의 co_firstlineno 로 설
+정한다.
+- 나머지 프로퍼티는 기본값으로 설정한다.
+
+
+
+위 과정을 모두 거쳐 생성된 프레임 객체(`PyFrameObject`)는 다음과 같습니다.
+
+![PyFrameObject Structure](../images/8_eval_loop/06_eval6.JPG)
+
+
+
+|요소|설명|
+|---|---|
+|Built-in NameSpace|`len`, `print`와 같은 내장 함수 및 예외 등을 포함한다.|
+|Global NameSpace|모듈 레벨에서 정의한 변수, 함수, 클래스 등을 포함한다.|
+|Local NameSpace|함수나 메소드 호출에서 생성됨. 함수 내부에서 정의된 지역 변수를 포함한다.|
+|Value Stack|코드의 실행 중 발생하는 연산을 위한 임시 저장소 역할을 한다.
대부분의 연산들은 이 스택을 통해 수행된다.|
+|Code Object|실행 가능한 ByteCode와
이와 관련된 메타데이터(파일 이름, 줄 번호 등)을 포함한다.|
+
+
+
+|요소|설명|
+|---|---|
+|ByteCode Instructions|컴파일 후 생성된 ByteCode, 기계어 명령에 해당한다.|
+|Names|함수, 변수, 클래스 등의 식별자를 포함하는 리스트이다.
코드 실행 중 이름 참조 시 사용된다.|
+|Constants|실행 중 변경되지 않는 값들을 포함함.
코드 내에서 직접 사용되는 Literal 값을 의미한다. (숫자, 문자열, 튜플 등)|
+
+
+
+아래 코드를 예시로 살펴봅시다.
+
+```c
+def sample():
+ x = 5
+ print(x)
+
+ return 4.29
+```
+- `x`, `print`는 Names에 저장됩니다.
+- `4.29`는 Constants에 저장됩니다.
+- 해당 값들은 바이트코드에서 참조될 때, Names와 Constants에 저장된 인덱스를 통해 참조됩니다.
+
+
## 8.3 프레임 실행
-6장 7장에서 살펴보았듯이, 코드 객체는 실행할 바이트 코드를 이진 인코딩한 결과와 심벌 테이블, 변수 목록을 포함하게 됩니다. 변수가 지역인지 전역인지에 따라 함수나 모듈 또는 블록이 호출된 방법에 따라 런타임에 결정되게 됩니다.
+6장 7장에서 살펴보았듯이, 코드 객체는 실행할 바이트 코드를
+이진 인코딩한 결과와 심벌 테이블, 변수 목록을 포함하게 됩니다.
-_PyEval_EvalCode( ) 함수를 따라 프레임에 대한 데이터들이 추가되고, 프레임이 해석되며, 코드가 실행되게 됩니다.
+그리고 변수가 지역인지 전역인지는
+함수나 모듈 또는 블록이 호출된 방법에 따라 런타임에 결정되는데,
+이 정보는 `_PyEval_EvalCode()`에 의해 프레임에 추가됩니다.
+`_PyEval_EvalFrameDefault()`는 **기본 프레임 평가 함수**이며,
+이 함수가 모든 것을 통합하는 역할을 수행합니다.
-
-
-
+
+
+간단히 말해, 파이썬 프로그램이 실행될 때,
+각각의 코드 객체는 실행을 위해 ‘프레임’이라는 단위로 관리되고
+`_PyEval_EvalFrameDefault()`는 이러한 프레임들을 하나씩 받아서
+순차적으로 명령을 실행하게 됩니다.
+
+
+
+
+### 프레임 실행 추적
+Python 3.7 부터는 현재 쓰레드에서 추적 어트리뷰트를 활성화해서
+단계적으로 프레임을 실행할 수 있습니다.
+
+`PyFrameObject` 타입은 `PyObject *` 타입의 `f_trace` 프로퍼티를 가지는데,
+이 프로퍼티 값은 파이썬 함수를 가리키는 포인터입니다.
+
+아래 예제는 전역 추적 함수로 `my_trace()`를 설정해서
+현재 프레임에서 스택을 가져오고, 역어셈블된 명령 코드를 출력하며,
+디버깅을 위한 정보를 추가합니다.
+
+``` python
+import io
+import sys
+import dis
+import traceback
+
+def my_trace(frame, event, args):
+ frame.f_trace_opcodes = True
+ stack = traceback.extract_stack(frame)
+ pad = " "*len(stack) + "|"
+ if event == 'opcode':
+ with io.StringIO() as out:
+ dis.disco(frame.f_code, frame.f_lasti, file=out)
+ lines = out.getvalue().split('\\n')
+ [print(f"{pad}{l}") for l in lines]
+ elif event == 'call':
+ print(f"{pad}Calling {frame.f_code}")
+ elif event == 'return':
+ print(f"{pad}Returning {args}")
+ elif event == 'line':
+ print(f"{pad}Changing line to {frame.f_lineno}")
+ else:
+ print(f"{pad}{frame} ({event} - {args})")
+ print(f"{pad}-----------------------------------")
+ return my_trace
+
+sys.settrace(my_trace)
+
+# 데모용 코드 실행
+eval('"-".join([letter for letter in "hello"])')
+```
+
+![Script](../images/8_eval_loop/07_eval7.JPG)
+
+
+
+`sys.settrace()`는 인자로 전달받은 함수를 현재 스레드 상태의 기본 추적 함수로 설정합니다.
+이 호출 이후 생성된 모든 새 프레임의 `f_trace`가 전달된 함수로 설정됩니다.
+
+이 토막 코드는 각 스택에서 코드를 출력하고 실행 전에 다음 명령을 가리킵니다.
+> 전체 ByteCode 명령 목록은 dis 모듈 문서에서 찾을 수 있다.
+
+``` python
+import dis
+dis.__dict__
+...
+Copyright (c) 1991-1995 Stichting Mathematisch Centrum, Amsterdam.
+All Rights Reserved., 'credits': Thanks to CWI, CNRI, BeOpen.com, Zope Corporation and a cast of thousands
+ for supporting Python development. See www.python.org for more information., 'license': Type license() to see the full license text, 'help': Type help() for interactive help, or help(object) for help about object., '_': None}, 'sys': , 'types': , 'collections': , 'io': , 'cmp_op': ('<', '<=', '==', '!=', '>', '>='), 'hasconst': [100], 'hasname': [90, 91, 95, 96, 97, 98, 101, 106, 108, 109, 116, 160], 'hasjrel': [93, 110, 122, 143, 154], 'hasjabs': [111, 112, 113, 114, 115, 121], 'haslocal': [124, 125, 126], 'hascompare': [107], 'hasfree': [135, 136, 137, 138, 148], 'opname': ['<0>', 'POP_TOP', 'ROT_TWO', 'ROT_THREE', 'DUP_TOP', 'DUP_TOP_TWO', 'ROT_FOUR', '<7>', '<8>', 'NOP', 'UNARY_POSITIVE', 'UNARY_NEGATIVE', 'UNARY_NOT', '<13>', '<14>', 'UNARY_INVERT', 'BINARY_MATRIX_MULTIPLY',
+...
+```
+
+
+
## 8.4 값 스택
-값 스택은 코어 평가 루프 안에서 생성되게 됩니다. 이 스택은 PyObject 인스턴스를 가르키는 포인터가 들어있는 리스트입니다. 값 스택의 포인터는 변수나 함수 참조 등 어떠한 파이썬 객체라도 가르킬 수 있다는 특징이 있습니다.
+값 스택은 코어 평가 루프 안에서 생성되게 됩니다.
+이 스택은 PyObject 인스턴스를 가르키는 포인터가 들어있는 리스트입니다.
+
+값 스택의 포인터는 변수나 함수 참조 등
+어떠한 파이썬 객체라도 가르킬 수 있다는 특징이 있습니다.
+
+> 평가루프에서 바이트코드 명령은 값 스택으로부터 입력을 취합니다.
-값 스택이라는 이름처럼 파이썬의 각 프레임 오브젝트 들은 스택처럼 값을 계산해나가면, PyFrameObject 들을 생성 소멸 해 나가면, 각 명령 코드에 의해 스택 크기의 변화량을 반환합니다.
+값 스택이라는 이름처럼 파이썬의 각 프레임 오브젝트 들은 스택처럼 값을 계산해나가면,
+PyFrameObject 들을 생성 소멸 해 나가면서
+각 명령 코드에 의해 스택 크기의 변화량을 반환합니다.
+
+
+
+
+### 값 스택 시뮬레이션 (바이트코드 명령 예제: BINARY_OR)
+값 스택은 코어 평가 루프 안에서 생성됩니다.
+이 스택은 `PyObject` 인스턴스를 가리키는 포인터가 들어 있는 리스트입니다.
+
+값 스택의 포인터는 변수나 함수 참조 등 어떠한 파이썬 객체라도 가리킬 수 있습니다.
+평가 루프에서 ByteCode 명령은 값 스택으로부터 입력을 취합니다.
+
+
+
+예를 들어 or 문을 사용한 파이썬 코드를 살펴보도록 합시다.
+
+``` python
+if left or right:
+ pass
+```
+컴파일러는 이 `or` 문을 `BINARY_OR` 명령으로 컴파일합니다.
+
+
+
+``` c
+static int
+binop(struct compiler *c, operator_ty op)
+{
+ switch (op) {
+ case Add:
+ return BINARY_ADD;
+ ...
+ case BitOr:
+ return BINARY_OR;
+ }
+}
+```
+평가 루프는 `BINARY_OR`일 경우에 값 스택에서 `left`와 `right` 두 개의 값을 꺼낸 후
+꺼낸 객체들을 인자로 `PyNumber_Or`를 호출합니다.
+
+그 후 연산 결과인 `res`를 스택의 맨 위에 추가합니다.
+
+``` c
+ ...
+ case TARGET(BINARY_OR): {
+ PyObject *right = POP();
+ PyObject *left = TOP();
+ PyObject *res = PyNumber_Or(left, right);
+ Py_DECREF(left);
+ Py_DECREF(right);
+ SET_TOP(res);
+ if (res == NULL)
+ goto error;
+ DISPATCH();
+ }
+
+```
+
+
+
+### 바이트코드 예측
+리스트 객체의 `append()` 메소드를 예로 들어보자.
+
+``` python
+my_list = []
+my_list.append(obj)
+```
+
+앞의 예시에서 `obj`는 리스트 끝에 추가하려는 객체입니다.
+리스트 추가 연산은 다음 두 연산을 포함합니다.
+
+1. `LOAD_FAST`: `obj`를 프레임의 `locals`에서 값 스택의 맨 위로 올린다.
+2. `LIST_APPEND`: 객체를 리스트에 추가한다.
+
+
+
+여기에서 바이트코드 명령인 `LOAD_FAST`은 설명은 다음과 같습니다.
+
+- **목적**: 지역 변수 `obj`를 값 스택의 맨 위로 로드한다.
+- **과정**:
+ 1. `GETLOCAL()`을 사용하여 `obj`의 포인터를 얻는다.
+ 2. 언바운드 지역 변수 에러를 처리한다 (변수가 정의되지 않은 경우).
+ 3. 객체의 레퍼런스 카운터를 증가시킨다.
+ 4. 포인터를 값 스택에 푸시한다.
+ 5. `FAST_DISPATCH` 매크로를 통해 다음 명령으로 빠르게 이동한다.
+
+다음은 `LOAD_FAST`를 처리하는 코드입니다.
+
+``` c
+// Python/ceval.c
+ ...
+ case TARGET(LOAD_FAST): {
+ PyObject *value = GETLOCAL(oparg); // 1.
+ if (value == NULL) {
+ format_exc_check_arg(
+ PyExc_UnboundLocalError,
+ UNBOUNDLOCAL_ERROR_MSG,
+ PyTuple_GetItem(co->co_varnames, oparg));
+ goto error; // 2.
+ }
+ Py_INCREF(value); // 3.
+ PUSH(value); // 4.
+ FAST_DISPATCH(); // 5.
+ }
+ ...
+
+```
+
+
+
+`obj`에 대한 포인터를 값 스택의 맨 위에 추가하면 다음 명령인 `LIST_APPEND`가 실행됩니다.
+`LIST_APPEND`에 대한 설명은 다음과 같습니다.
+
+- **목적**: 값 스택에서 객체를 꺼내 리스트의 끝에 추가한다.
+- **과정**:
+ 1. 값 스택에서 객체의 포인터를 꺼낸다 (`POP()`).
+ 2. 리스트 객체의 포인터를 스택에서 가져온다 (`PEEK(oparg)`).
+ 3. `PyList_Append()` 함수를 호출하여 객체를 리스트에 추가한다.
+ 4. 예외 처리를 수행하고, 에러 발생 시 에러 처리 루틴으로 이동한다.
+ 5. `PREDICT(JUMP_ABSOLUTE)`를 통해 다음 예상 명령으로 빠르게 이동한다.
+
+```c
+...
+ case TARGET(LIST_APPEND): {
+ PyObject *v = POP();
+ PyObject *list = PEEK(oparg);
+ int err;
+ err = PyList_Append(list, v);
+ Py_DECREF(v);
+ if (err != 0)
+ goto error;
+ PREDICT(JUMP_ABSOLUTE); // <-- 예측에 성공한다면 goto 문으로 바뀜
+ DISPATCH();
+ }
+...
+```
+
+`PREDICT`는 다음 명령이 `JUMP_ABNSOULTE`일 것이라고 예측하는 구문입니다.
+이 매크로는 바로 다음에 실행될 것이라고 예측되는
+연산의 `case` 문으로 점프하도록 컴파일러가 생성한 `goto` 문을 포함합니다.
+
+즉, CPU가 루프를 돌지 않고 예측한 명령으로 바로 점프할 수 있습니다.
+
+> 자주 함께 등장하는 명령 코드들에 대해서는 첫 번째 명령을 실행할 때 두 번째 명령을 함께 예측할 수 있습니다.
+예를 들어 COMPARE_OP 실행 후에는 POP_JUMP_IF_FALSE 또는 POP_JUMP_IF_TRUE가 실행되는 경우가 많습니다.
+>
+>
+> 명령 코드에 대한 통계를 수집하려면 2가지 선택지가 있습니다.
+>
+> 1. 예측을 활성화하고 일부 명령 코드가 조합된 것 처럼 결과를 해석한다.
+> 2. 예측을 비활성화하고 각 명령 코드에 대한 실행 빈도 카운터가 독립적으로 갱신되도록 한다.
+>
+> 계산된 `goto`를 사용할 수 있으면 `CPU`가 각 명령어 코드에 대해 별도의 분기 예측 정보를 기록할 수 있기 때문에 CPython 단에서 명령 코드 예측은 비활성화됩니다.
+>
+
+
+
+*※ 계산된 Goto*
+switch 문 내에서 각 case 라벨로 점프하는 대신
+goto문을 이용해서 점프하는 방식
+
+
+
+*※ 명령 코드 예측 관련 코드 (Python/ceval.c)*
+
+``` c
+#define PREDICT_ID(op) PRED_##op
+
+#if defined(DYNAMIC_EXECUTION_PROFILE) || USE_COMPUTED_GOTOS
+#define PREDICT(op) if (0) goto PREDICT_ID(op)
+#else
+#define PREDICT(op) \\
+ do { \\
+ _Py_CODEUNIT word = *next_instr; \\
+ opcode = _Py_OPCODE(word); \\
+ if (opcode == op) { \\
+ oparg = _Py_OPARG(word); \\
+ next_instr++; \\
+ goto PREDICT_ID(op); \\
+ } \\
+ } while(0)
+#endif
+```
+
+- `opcode`: 다음 바이트코드
+- `op`: 예측할 바이트 코드
+
+
+
+다음에 바이트 코드가 예측한 바이트와 동일하다면
+`goto`문을 통해 코드의 실행 흐름을 바꾸는 방식입니다.
## 8.6 요약
-CPython 의 실행 중, 생성되는 파이썬 객체인 PyFrameObject 의 평가 루프에 대해서 알아보았으며, 프레임이 어떻게 생성되고 소멸되는지 알아보는 장이었습니다. 코어 평가 루프는 컴파일 된 파이썬 코드 그리고 그 기반이 되는 C 확장 모듈과 라이브러리, 시스템 호출간의 인터페이스로서, 그 중요성을 잘 설명해주는 챕터였습니다.
\ No newline at end of file
+CPython 의 실행 중, 생성되는 파이썬 객체인 PyFrameObject 의 평가 루프에 대해서 알아보았으며,
+프레임이 어떻게 생성되고 소멸되는지 알아보는 장이었습니다.
+
+코어 평가 루프는 컴파일 된 파이썬 코드 그리고 그 기반이 되는 C 확장 모듈과 라이브러리,
+ 시스템 호출간의 인터페이스로서, 그 중요성을 잘 설명해주는 챕터였습니다.
\ No newline at end of file
diff --git a/book/images/8_eval_loop/06_eval6.JPG b/book/images/8_eval_loop/06_eval6.JPG
new file mode 100644
index 0000000..be805d9
Binary files /dev/null and b/book/images/8_eval_loop/06_eval6.JPG differ
diff --git a/book/images/8_eval_loop/07_eval7.JPG b/book/images/8_eval_loop/07_eval7.JPG
new file mode 100644
index 0000000..ccd24d4
Binary files /dev/null and b/book/images/8_eval_loop/07_eval7.JPG differ