- non-leaf procedure
- 함수가 다른 함수를 또 호출하는 함수! (그게 꼭 자기 자신이 아니더라도!)
- 대표적인 예로는 재귀함수가 있음! (얘는 자기 자신을 호출하는 함수)
- 이런 함수 콜이 연속되는 함수의 경우 caller 함수가 다음의 것들을 스택에 저장하고 있어야 함!
- RA 라고 불리는, 돌아갈 주소의 값 (호출 한 칸 아래)
- 콜이 된 이후에 필요한 인자라든지 변수들!! (리턴값도 여기에 포함이 되는 것인지..?ㅇㅇ 당연 그래야 레지스터로 가져가서 연산하지! x10 번 레지스터에 그 값이 저장된다는 것도 잊지 말자 여기는 파라미터와 리턴 값 둘 다 저장할 수 잇는 공간임!!)
- 모든 과정이 끝이 나면, 스택에서 지워지게 됨!!
- 재귀함수 호출 과정
- 문자열 데이터의 저장 과정!
- c 에서 봤듯이 1바이트 = 8 비트이고 총 256개의 데이터를 저장할 수 있음. 아스키코드가 대표적
- 반면 유니코드는 32비트 이므로 2^32 개의 데이터를 저장할 수 있음.
- 이 두 개는 서로 혼용이 안됨. 가끔 문서 파일 깨지는 이유가, 받은 파일은 유니코드인데 컴퓨터가 아스키코드로 읽으면 쓰레기 값이 읽혀서 그럼!!
- RISC-V 의 레지스터의 명령어는 32비트! 즉 메모리에는 32비트의 기계어가 명령어가 되어서 그게 ALU 에 전해지면 ALU 가 연산을 하는 거!! 그리고 레지스터 한 줄에는 32비트의 기계어를 저장할 수가 있음! 근데 만약에 만약에 immediate 값으로 훨씬 큰 수를 주고 싶다거나 레지스터에 훨씬 큰 수를 저장하고 싶다면 어떻게 해야 할까??
- LUI 를 사용하면 된다! load upper immediate 라고 하는 것!
- 총 20비트!! 의 상수를 레지스터의 12번에서 31번 자리에 넣음으로써 로드할 수 있게 된다!
- 이 더하는 과정을 잠시 설명해 보자면
- 먼저 addi 는 뒷 12 비트가 immediate 를 나타내기 때문에, 32비트에 몽땅 어떤 수를 넣고 싶다면, lui 가 필요함
- lui 는 32비트에서 왼쪽 20개의 비트를 가져와서 레지스터의 앞쪽 20비트에 채워넣는다. 그 다음에 addi 를 써주면 32비트에 완전히 다 값만 들어가게 된다!
- 브랜치에서의 주소!
- 브랜치는 주소에다가 immediate 값을 더해서 피융 하고 이동시키는 명령어!
- 그래서 immediate 에는 늘 주소값이 저장됨!
- 주소값은 워드 단위 즉 4바이트 단위이기 때문에, 모든 주소의 뒷 두 자리는 항상 00이 되게 됨! (2진수로 바꾸면)
- 따라서 00은 굳이 저장하지 않아도 된다!
- 근데 우리는 또 친절하게 두 번째 0은 저장하고, 맨 마지막 0만 안 저장함.
- 그래서 결국 우리가 가게 되는 주소는 PC + 2 * immediate 가 되는 것임!
- 무조건적 jump and link instruction jal
- 얘는 묻지도 따지지도 않고 그냥 jump! 무조건 jump 하는 애!
- 얘도 마찬가지로 맨 오른쪽 0은 버려버림!
- 여기서는 기존 레지스터가 담고 있던 32비트 즉 메모리 주소를 더하는 것이 아니라!! 레지스터 번호를 알려주면 pc 가 그 레지스터 번호에 있는 값에 더하게 된다!!
- 이렇듯 PC relative Addressing 에서는 32비트를 다 가리킬 수가 없어 한계가 있다!
- branch 에서는 상하 2^12 바이트, jal 에서는 2^20 바이트가 최대이다. (물론 위, 아래가 다 있기 때문에 가리킬 수 있는 것의 총 개수는 *2 를 해준 2^13, 2^21 이다.)
- R type, I type, S Type 등 타입에 해당하는 명령어가 무엇인지 알아야 하고, 외울 필요는 없다.
- 컴파일러 최적화의 효과
- 좋아진다. 성능이 모든 면에서!
- 오해
- 강한 인스트럭션 이를테면 곱하기를 쓰면 성능이 무조건 좋아지는 건 아니다. 필요한 인스트럭션의 수는 줄 수 있지만 곱하기 자체를 구현하는 데 시간이 만ㅇ히 걸린다.
- 꼭 어셈블리 코드를 써야 하는 건 아니다. 모던 프로세서에 더 잘 적응하고 있다. 현재 컴파일러는!
- Backward compatibility 는 무슨 말인지 모르겠다.. 질문!