호출 규약은 레지스터 표를 외울 때보다 실제 컴파일러 출력에서 읽어 낼 때 더 잘 남는다. 좋은 연습은 int 매개변수 세 개를 받아 합을 돌려주는 C 함수를 RV32용으로 -O0에서 한 번, -O2에서 한 번 컴파일해 보는 것이다.
-O0 리스트는 시끄럽다. 함수가 프레임을 만들고, a0, a1, a2를 스택에 저장했다가 다시 읽고, 더한 뒤 결과를 a0에 두고 ret 한다. -O2에서는 add 명령 두 개와 ret으로 줄어든다. 규약은 같고 리스트는 전혀 다르며, 그 비교가 핵심이다. 인자는 어느 쪽이든 인자 레지스터로 들어오고 결과는 어느 쪽이든 a0로 나간다. 나머지는 전부 컴파일러 자신의 장부 정리였다.
다음에는 함수가 다른 함수를 호출하게 바꾸고 왜 이제 ra를 저장해야 하는지 묻는다. ret은 그냥 jalr x0, 0(ra)이고 안쪽 call이 ra를 덮어쓴다.
최적화하지 않은 단계를 건너뛰면 스택 왕래가 규약의 일부라고 믿기 쉽다. 그렇지 않다. 확신이 없는 건 순서다. -O2부터 시작하면 깔끔하지만 프레임 설정이 나중에 뜻밖의 일로 나타난다.