한시간 전쯤에 7장 가상 머신의 동작 정리를 마치고

좀 피곤하니까 8장을 빨리 마무리 하고 자야지 싶었는데, 8장 ppt 자료만 182페이지나 된다

7장께 120페이지 정도만 해도 장난아니게 많았는데;;

 

 

아무래도 오늘 8장을 정리 다하려고하면 잠은 좀 늦게 자야할거같긴한데 피곤해서 할수있는데까지는 해야겠다. 빠르게 진행하기 위해서 책 내용보다는 PPT 자료를 가지고 잘못 된 부분이 있더라도 그냥 넘어가야될거같다.

 

 

 

 

 지난 장에서 가상머신의 기본적인 내용과 추상화와 구현에 대해서 봤다면 이번 장에서는 위의 ppt와 같이 분기와 함수, 그리고 함수 호출과 반환 마지막으로 HACK 플랫폼에서 가상 머신 구현 등의 내용을 다루고 있다.

 

 아까 스택가지고 산술 논리 연산을 어떻게 하는지 봤지만 이번에는 이걸 가지고 어떻게 중첩 함수 호출, 파라미터 전달, 재귀, 메모리 할당, 테스크 재활용 등 프로그램 동작에 필요한 복잡한 작업들이 이뤄지는 지볼 건데, 그동안 당연한 것이라고 생각했던것들이 어떻게 동작하고 있는지 알아보자.

 

 

 

 

 아 PPT 자료를 추가해놓자면 이번 장에서는 고급 언어를 컴파일 하면 나오는 VM 코드에서 나오는 분기와 함수 관련 명령어들을 다루는거같다. 

 

 

 

 런타임 시스템

 모든 컴퓨터 시스템은 런타임 시스템 보델을 따르는데, 어떻게 프로그램을 실행하고, 프로그램을 언제 종료해야하고, 어떻게 함수에서 다른 함수를 호출할때 매개변수를 넘길지, 함수를 돌릴때 어떻게 매모리를 관리해야할지, 그리고 더이상 필요없을때 어떻게 메모리 자원을 해재해야하는지 전부 구체화 할수 있어야 한다.

 

 이 책 nand 2 tetris에선 이 문제들을 HACK 플랫폼의 표준 맵핑을 이용해서 VM 언어 상세화 vm language specification를 다루고자 한다. VM 번역기가 이 규칙을 따르도록 만든다면, 런타임 시스템에서 동작할수 있게 되며 push, pop, add 같은 단순한 VM 명령어를 어셈블리어로 번역할 뿐만 아니라 프로그램이 동작하면서 감싸진 내용들에 대한 어셈블리 코드 전체를 만들어 낼수 있개 된다.  이 내용들이 어떻게 프로그램을 시작하고, 함수 호출과 반환을 처리할지에 대한 내용은 이 동작을 하는 완전한 어셈블리 코드를 만들어 내면 이해할수 있을거다. 

(완전히 이해하기가 힘들어 편하게 번역체로 썻다.)

 

 

고급 언어의 효과(?) magic 

 고급 언어를 이용하면 아래와 같은 분모가 1인 근의 공식같이 루트나 제곱이 들어간 복잡한 수식도 sqrt(), power()같은 함수를 이용해서 프로그램으로 쓸수가 있다. 이와 같이 함수를 이용해서 무한히 늘릴수 있는게 고급 언어의 중요한 특징이라 할수 있는데, 더 나아가 sqrt, power 같은 이름으로 구현하면 이게 어떻게 만들었는지랑은 상관없이 응용 어플리캐이션 개발자들은 이게 뭘하는지 알고 사용할수 있을거다.

 

 그리고 분기의 효과도 큰데, 분기를 이용하면 복잡한 로직/알고리즘도 코드로 표현할수가 있다. 예를들어 a== 0이 아닐때는 위의 이차식을 a가 0일때는 아래의 1차식의 근을 구하도록 구현이 가능하다.

 

 근대적인 프로그래밍 언어들은 개발자들이 쓰기 쉽게 만들어져있어서 쉽고 강력한 추상화를 쓸수가 있지만 결국에는 고급 언어가 얼마나 고급스럽던지간에/ 사람이 쓰기 쉽던지간에 결국에는 어느 하드웨어 플랫폼에 동작할수 있는 기계어로 되어있다보니까 결국에는 컴파일러와 VM 개발자들이 분기와 함수 호출, 반환 명령어들을 구현해서 저수준 언어로 변활 할수 있도록 만들어야한다.

 

 함수는 함수 하나 하나가 각자의 동작을 가지고 있는 독립적인 기능 단위라고 할수 있다. solve라는 계산 하는 함수가 있는경우 이 함수는 sqrt() 함수를 호출하고, 또 호출하고, power() 함수를 호출 할수도 있을 것이고, 어떤 경우에는 재귀적으로 아주 깊이 들어갈수도 있다. 이런 호출하는 함수를 콜러 caller라 부르며, 호출 받는 함수를 콜리 callee라고 한다.

 

 콜러는 콜리를 호출하면 콜리가 작업을 끝낼때 까지 중지하고, 콜리는 매개변수가 있거나 없을수도 있지만 매개변수를 이용해서 연산을 하고 계산 결과가 있을지 없을지는 모르지만 콜러에게 반환해주면서 콜러가 다시 실행하게 된다.

 

 

컴파일러, VM 설계자가 고려해야하는 오버해드들

* 네이버에 검색해보니 개발에서 오버해드란 결과를 얻는데 추가적인 자원, 요소들이라고 한다.

- 콜리가 연산을 마치고 결과를 반환해야하는 주소의 저장

- 콜러의 자원 저장

- 콜리에서 필요한 메모리 할당하기

- 콜러의 매개변수를 콜리로 전달하기

- 콜리 코드를 시작하기

 

 콜리가 연산이 끝나고 값을 반환시에는 다음의 오버해드들도 다뤄야한다.

- 콜리의 반환 결과를 콜러에서 사용할 수 있도록 하기

- 콜리에서 썻던 메모리 공간을 재활용하기

- 콜리 연산을 하기전에 콜러를 멈추면서 저장했던 메모리 자원들을 다시 사용하기

- 이전에 저장해둔 반환 주소를 찾아내기

- 콜러를 다시 실행하기

 

 

 그리고 보통 함수를 쓰면서 위와 같은 오버해드가 발생하고는 하는데, 보통의 응용 어플리케이션 개발자들은 어셈블리어 코드야 컴파일러가 만들어주고, 2단계 컴파일 모델에선 컴파일러의 백앤드 단, VM 번역기가 알아서 다뤄주니 알필요가 없다. 하지만 지금 VM에대해서 배우면서 이것들이 어떻게  VM 상에서 동작하고 처리되는지를 알아보자.

 

 

 

 

 

 

분기 branching

 

 어셈블리어를 봤다시피 프로그램은 한줄 한줄 읽어가면서 순서대로 진행하는데 어느 과정을 반복하도록 루프를 사용하기도 하고, 조건을 주기위해 분기 명령을 주기도 했었다. 이전에 사용한 goto 그러니까 조건에 따라서 JUMP한게 분기의 예시라고 할수 있을거 같은데 이전 장에서는 라벨 심볼을 이용해서 어셈블리어를 만들었다가 이게 실제 주소로 바뀐걸 봤었다.

 

 

 

 VM에서도 이런 라벨 심볼로 분기를 처리할 것이고, 무조건적인 분기는 goto를 조건부 분기의 경우는 if goto 심볼 명령을 사용할 거고 이때 스택의 맨 꼭대기에 있는 값이 참인지 거짓인지를 보고 판단한다. 위의 장은 고급 프로그램에서 컴파일한 결과 VM code 상에서 어떻게 표현되는지가 나온다. 이건 컴파일러의 영역이니까 지금 다루는건 아니고, 이번 장에서 구현해야하는 VM 번역기로 위의 분기 명령을 어떻게 타겟 플랫폼에서 어셈블리 명령어로 바꿀지를 고민해야한다.

 

 

 

 지금까지 VM 언어로 산술 논리연산 명령어와 메모리 세그먼트 명령어 그리고 분기 명령어에 대해서 까지 알아봤고, 이제 함수 명령어가 남았다. 

 

 잠깐 분기 파트를 넘어가기전에 책의 내용을 조금 더 짚고 넘어가면, 위의 VM 코드로 표현한 곱셈 연산의 경우 VM 프로그램인 만큼 컴파일러가 만들어내고 그 다음에 VM 번역기로 어셈블리어로 만들어 동작시키고, 저 코드는 비트 단위를 이용한 곱셈 나눗셈 알고리즘을 쓴게 아니라서 비효율 적이다. 이 알고리즘은 OS 장에서 Math 클래스의 함수를 구현하면서 다룰 건데 그 코드를 JACK 컴파일러에 돌리면 Math.vm이 나온다.

 

 이 외에도 OS에서는 수학 연산 뿐만이 아니라 메모리 관리를 위한 Memory.vm, 문자열 처리를 위한 String.vm, 배열 처리를 위한 Output.vm, 화면 처리를 위한 Screen.vm, 키보드 출력을 위한 Keyboard.vm, OS API를 제공하는 Sys.vm까지 8개의 파일로 컴파일된다.

 

 

 

 

 욕심 같아서는 오늘 가상머신을 끝내고는 싶었는데, 생각보다 VM 2장 내용이 장난아니게 많아서 좀 일찍 자고, 내일 일찍 일어나서 해야되겠다.

+ Recent posts