Odin, 타입 검사와 레지스터 바인딩을 담은 인라인 어셈블리 설계 공개
- Odin은 인라인 어셈블리를 문자열로 넘기지 않고, 입력과 출력이 있는 asm 템플릿을 프로시저처럼 선언하고 호출하게 설계함
- 템플릿 바인딩은 clobber, 고정 레지스터, tied 레지스터, scratch 레지스터를 지정해 어셈블리와 컴파일러의 레지스터 사용을 연결함
- 작성자는 명령어마다 피연산자 종류, 레지스터 클래스, 비트 폭, 즉값 범위, 암묵적 변경 레지스터가 정해져 있으므로 어셈블리도 타입을 가짐이라고 주장함
- Odin은 core:rexcode 인코딩 테이블로 명령어 형식을 검사하고, 어셈블러가 생성한 문자열 오류 대신 의미 단위 진단을 내도록 구현함
- 문법은 ISA별 표기를 하나로 맞추고 Odin 타입을 피연산자 폭에 사용하며, 작성자는 이 인라인 어셈블러를 약 7일 만에 만들었다고 밝힘
Hacker News opinions
GCC 확장 asm도 피연산자에 이름을 붙일 수 있음. %0, %1을 손으로 세는 방식만 있는 건 아닌데, 문서의 asmSymbolicName을 보면 됨.
Odin 쪽은 add_three :: asm(my_in: u64) -> (my_out: u64)처럼 프로시저 서명으로 입력과 출력을 드러냄. 기호나 클로버 문자열 없이 읽히는 점이 훨씬 낫다고 봄.
이 논의는 이미 Typed Assembly Language 연구가 다룬 분야임. TAL 논문과 Cornell TALT 자료를 봐야 함.
내가 말하는 건 TAL이 아님. 어셈블리는 별도 주석을 붙여야 타입이 생기는 게 아니라, 명령어 자체가 이미 타입 조건을 갖는다는 주장임.
Turbo Pascal 6.0과 7.0에서는 db $66 접두사를 붙여 32비트 인라인 어셈블리를 많이 썼음. 내장 어셈블러가 80286까지만 알아도 이 방법으로 넘길 수 있었음.
통일 문법에는 회의적임. 보통은 ISA 매뉴얼 문법을 써야 하고, 검사기가 Fil-C만큼 강해야 하는데 글의 CPUID 예시는 ECX 입력 바인딩을 빼먹은 듯함.
그 예시는 CPUID 전에 ECX에 pin 바인딩을 걸고 있음. 문서에서 pin도 바인딩 종류라고 설명함.
벤더 매뉴얼 문법은 너무 낡았음. 어셈블리 도구가 지난 세기 방식에 묶일 논리적 이유는 없음.
단일 ISA만 다루면 벤더 문법도 쓸 만하지만, x86-64와 AArch64를 오가면 문법 차이 자체가 버그 원인이 됨. 고급 언어 코드 안에 섞어 쓸 때도 통일 문법이 낫다고 봄.
타입이라는 주장 자체에는 큰 관심 없지만, GCC asm의 마찰을 크게 줄인 어셈블러 설계로 보임. 특정 프로젝트에서는 Odin을 고를 이유가 될 수 있음.
GAS는 mnemonic의 폭 접미사를 생략할 수 있고 Intel 계열도 피연산자에서 메모리 크기를 추론하는 경우가 많음. 128비트 값을 두 레지스터로 넘기는 식의 이상한 경우가 나오면 이 문법도 결국 복잡해질 텐데, 그런 모서리 사례 때문에 기존 inline asm 문법이 복잡한 것임.
Watcom C의 완전한 인라인 어셈블러를 빠뜨렸고, Zortech의 1980년대 인라인 어셈블러는 지금 D 컴파일러에도 남아 있음.
x64, ARM64, RISC-V 64에는 128비트 정수 레지스터가 없음. 상하위 64비트를 두 레지스터에 담는 연산이고, Odin에서도 u64 두 입력과 두 출력을 선언해 처리할 수 있음.
어셈블리에서 조립 시점이나 실행 시점의 타입 오류를 실제로 낼 수 있는지 궁금함. 그게 없다면 제목은 낚시에 가까움.
명령어 형식은 레지스터, 메모리, 즉값, 레이블 종류와 레지스터 클래스, 폭, 즉값 범위, 클로버를 정함. 예를 들어 mulps는 128비트 벡터 레지스터를 요구하고 div는 RDX와 RAX를 읽고 쓰므로, 그런 의미에서는 타입 조건이 있음.
사람들이 어셈블리를 무타입이라고 할 때는 정수, 부동소수점, 벡터 레지스터 구분이 없다는 뜻이 아님. 글은 다른 의미의 주장을 허수아비로 세워 반박하는 것 같음.