2P by GN⁺ | ★ favorite | 댓글 1개
  • gccrs는 GCC 안에 Rust 컴파일러를 구현하려는 2014년 시작 프로젝트로, 아직 완성은 멀지만 표준 라이브러리 컴파일과 GCC 14 포함을 향해 진전이 있음
  • 프로젝트는 빠르게 변하는 최신 Rust 대신 Rust 1.49를 목표로 삼았지만, 표준 라이브러리 내부 의존성 때문에 const generics 같은 기능도 결국 구현해야 했음
  • corealloc 컴파일은 매크로 이름 해석, 데코레이터 매크로, GCC에 없는 LLVM 컴파일러 intrinsic, borrow checker 부재 때문에 아직 막혀 있음
  • rustc_codegen_gcc는 rustc 일부를 활용하고 GCC를 후단 코드 생성에 쓰는 더 성숙한 접근이며, 2023년 10월 기준 추가 패치 없이 Rust for Linux를 컴파일할 수 있음
  • gccrs는 GCC 보안 플러그인, 정적 분석, LLVM 미지원 아키텍처, Rust for Linux라는 뚜렷한 동기가 있지만 실용성은 아직 rustc_codegen_gcc보다 제한적임

gccrs의 목표와 현재 위치

  • gccrsGNU Compiler Collection(GCC) 안에 Rust 컴파일러를 구현하려는 프로젝트임
  • 2014년에 시작됐고, LWN의 이전 다룸 이후 프로젝트 보고서 기준으로 진전이 있었음
  • 2022년에는 GCC 13 포함을 목표로 했지만 달성하지 못했고, 2023년 11월 월간 보고서 기준으로는 GCC 14 포함을 목표로 함
    • GCC 14는 2024년 중반 출시 가능성이 있는 버전으로 다뤄짐
  • 2023년 10월 EuroRust 2023에서 Arthur Cohen은 “The road to compiling the standard library with gccrs” 발표로 Rust 표준 라이브러리 컴파일 작업과 아직 컴파일하지 못하는 이유를 설명함

Rust 1.49를 목표로 삼은 이유

  • gccrs는 Rust 최신 버전을 계속 따라가기보다 Rust 1.49를 목표로 함
  • Rust 1.49는 2020년 말 릴리스됐고, Rust 1.50에서 일반 사용 가능해진 const generics 지원 이전의 마지막 버전임
  • 프로젝트는 const generics를 피하려 했지만, Rust 1.49의 표준 라이브러리 내부에서도 이미 사용돼 결국 무시할 수 없었음
  • const generics는 이후 완전히 구현됐고, 더 이상 장애물은 아님
  • gccrs는 Rust의 상위 집합이나 별도 “GNU Rust” 언어를 만들지 않으려 함
    • 목표는 rustc의 출력, 버그, 특이 동작까지 재현하는 것임
    • 이를 위해 Rust 테스트 스위트와 GCC 테스트 스위트를 모두 사용함

표준 라이브러리 컴파일에서 막힌 부분

  • Rust 표준 라이브러리는 여러 크레이트(crate) 로 구성됨
  • gccrs는 가장 중요한 두 크레이트인 corealloc 컴파일 지원을 작업 중임
    • core는 원시 타입과 매크로 같은 표준 라이브러리 기반 기능을 구현함
    • alloc은 힙 메모리 할당과 여러 컨테이너 타입을 다룸
  • 현재 gccrs는 아래 기능 부족 때문에 이 크레이트들을 컴파일하지 못함
    • 매크로 이름 해석 동작이 올바르지 않음
    • 데코레이터 매크로 지원이 완전하지 않음
    • borrow checker가 없어 코드 안전성을 제대로 검사할 수 없음
    • GCC에 없는 LLVM 컴파일러 intrinsic을 구현해야 함
  • borrow checker 부재는 컴파일 자체를 막지는 않지만, Rust 코드의 안전성 검사를 제대로 수행하지 못하게 함

절차적 매크로와 GCC 통합 작업

  • 2023년 9월 GNU Tools Cauldron에서 Pierre-Emmanuel Patry는 GCC 14 포함 진행 상황과 매크로 작업을 중심으로 발표함
  • 절차적 매크로(procedural macro) 구현 방식은 GCC 빌드 시스템 변경을 필요로 함
  • 절차적 매크로는 C나 C++ 매크로처럼 단순 소스 텍스트를 내보내지 않고 토큰 스트림을 내보내는 함수형 매크로임
  • Rust에서는 내장 크레이트 proc_macro로 구현됨
  • 절차적 매크로는 구현이 까다롭지만 강력한 기능을 가능하게 함
    • #[attribute] 데코레이터
    • #[derive()] 데코레이터
    • 컴파일 시간 평가 기반 도메인 특화 언어 생성
  • GNU Cauldron 발표 기준 gccrs에는 GCC로 upstream해야 할 커밋이 800개 이상 있었음

GCC 생태계를 Rust에 활용하려는 이유

  • gccrs의 주요 동기 중 하나는 GCC의 보안 플러그인을 Rust 코드에도 활용하는 것임
  • GCC에는 디버깅, 정적 분석, 하드닝을 돕는 플러그인이 다양하며, 이들은 GCC 중간 표현에서 동작함
  • gccrs는 Rust 개발자가 기존 GCC 플러그인을 재사용하는 워크플로를 지원하려 함
  • Cohen은 C 프로그래머가 파일 디스크립터를 닫지 않는 실수를 오랫동안 해왔기 때문에 이를 잡는 플러그인이 많다고 예를 들었음
  • Rust의 unsafe 코드에서 버그를 잡는 데 기존 GCC 플러그인과 정적 분석기를 활용할 수 있게 하는 것이 목표임

이미 일부 쓰이는 영역

  • Cohen에 따르면 Sega Dreamcast 홈브루 커뮤니티는 gccrs로 Dreamcast 게임 콘솔용 새 게임을 만들고 있음
  • Dreamcast 커뮤니티가 관심을 갖는 이유는 rustc의 LLVM 백엔드가 콘솔의 Hitachi SH-4 아키텍처를 지원하지 않지만 GCC는 지원하기 때문임
  • gccrs는 아직 미완성이지만, 이런 임베디드 사용 사례에서는 도움이 됨
  • GCC 플러그인을 이용한 unsafe Rust 코드 정적 분석도 이미 가능함
  • gccrs 작업 과정에서 Deref와 매크로 이름 해석처럼 명세가 충분히 정해지지 않은 언어 기능이 드러났고, 프로젝트는 Rust 명세 추가에도 기여할 수 있었음
    • Rust에는 현재 공식 명세가 없지만, RFC 3355에 따라 명세 작업이 진행 중임

아직 개발 중인 핵심 기능

  • gccrs에는 여전히 Rust 컴파일러의 핵심 기능이 많이 빠져 있음
  • 주요 미구현 또는 개발 중 기능은 다음과 같음
    • async/await
    • GCC에 없는 LLVM intrinsic
    • println!() 같은 출력 매크로가 쓰는 format_args!() 매크로
    • Rust 참조 규칙을 강제하는 borrow checker
  • borrow checker의 유력한 해결책은 별도 프로젝트 Polonius
  • Cohen은 gccrs가 몇 달 뒤 Polonius를 통합할 가능성이 높다고 말함
  • Jakub Dupak은 최근 몇 달 동안 이 작업에서 진전을 냄
  • Polonius는 현재 rustc의 borrow checker와 의미적으로 동등한 borrow checker를 구현하는 라이브러리임
    • 참조 수명 계산에 다른 알고리듬을 사용함
    • 장기적으로 rustc의 현재 borrow checker가 가진 부족한 점과 코너 케이스 해결을 목표로 함
    • 성숙해지면 rustc도 미래에 Polonius를 채택할 가능성이 있음

format_args!()가 필요한 이유

  • 2023년 11월 gccrs 월간 보고서에 따르면 format_args!() 매크로 작업이 시작됨
  • format_args!()는 문자열 포맷팅 매크로에 전달할 인자를 구성하는 보조 매크로임
  • 이 기능은 DisplayDebug 트레이트와 관련됨
  • format!()println!() 같은 매크로에 넘길 인자를 준비하는 데 필요함
  • format_args!()가 없으면 Rust 프로그램은 포맷된 출력을 만들 수 없음
  • 따라서 gccrs가 “Hello, World” 프로그램을 컴파일하기 전에도 필요한 기능임
  • format_args!() 심층 설명으로 Mara Bos의 블로그 글이 함께 다뤄짐

rustc_codegen_gcc와의 차이

  • rustc_codegen_gcc는 gccrs와 다른 GCC 기반 Rust 프로젝트임
  • gccrs보다 성숙했지만 범위는 더 제한적임
  • Rust 컴파일러를 처음부터 완전히 구현하는 방식은 아님
  • libgccjit 라이브러리를 사용해 rustc의 LLVM 백엔드 API에 연결함
  • 컴파일의 많은 부분은 rustc가 수행하고, 이후 단계에서 GCC를 사용함
  • libgccjit 이름에 JIT가 들어가지만, rustc_codegen_gcc는 ahead-of-time 컴파일을 목적으로 함
  • 주요 목표는 LLVM이 지원하지 않는 플랫폼에서 Rust 코드 생성을 가능하게 하는 것임
  • 2023년 10월 기준 rustc_codegen_gcc는 추가 패치 없이 Rust for Linux를 컴파일할 수 있음
  • 지난 1년 동안 SIMD와 링크 타임 최적화 지원을 추가함
    • 두 기능은 이전에 테스트 실패 원인으로 지목됐음
  • Cohen은 EuroRust 발표에서 여러 차례 당분간 gccrs 대신 rustc_codegen_gcc를 사용하라고 권함
  • rustc_codegen_gcc는 이미 Rust 언어 저장소 upstream에 들어가 있음

Rust for Linux와 버전 격차

  • Rust for Linux는 Linux 커널에 Rust 지원을 추가하려는 이니셔티브임
  • Cohen은 Linux 커널을 gccrs 프로젝트의 핵심 동기로 들었음
    • 많은 커널 관계자가 커널이 GNU 툴체인만으로 컴파일되기를 선호하기 때문임
  • 현재 Rust for Linux 프로젝트는 커널 Rust 코드를 빌드하는 방법으로 rustc 또는 rustc_codegen_gcc를 문서화함
  • 커널에는 여러 빌드 도구의 최소 지원 버전 문서도 있음
    • rustc는 최소 버전이 아니라 정확히 일치해야 하는 버전으로 취급됨
    • 현재 지원되는 rustc 버전은 2023년 10월 릴리스된 1.73.0
  • gccrs가 목표로 하는 Rust 1.49와 Rust for Linux가 요구하는 Rust 1.73.0 사이에는 큰 차이가 있음
  • Rust for Linux 지원은 gccrs의 명시된 목표지만, 이 버전 격차 때문에 아직 상당히 멀리 있음

전체 평가

  • gccrs 저장소에는 2023년 1월 1일 이후 3,000개가 넘는 커밋이 있음
  • 지난 1년 동안 프로젝트는 상당한 진전을 보였음
  • 하지만 처음부터 완전한 Rust 컴파일러를 구현하는 범위가 매우 크기 때문에, 거의 모든 실용적 목적에서는 아직 사용 가능한 상태가 아님
  • rustc_codegen_gcc는 upstream Rust 저장소에 병합됐고 Rust for Linux에서 실제 사용되고 있음
  • Rust 언어에 여러 독립 컴파일러 구현이 존재하는 단계에는 아직 도달하지 않았지만, 그 방향으로 가까워지고 있음

댓글과 토론

Hacker News 의견들
  • 글의 주장만으로는 gccrs의 동기가 다소 약해 보임
    GCC 보안 플러그인이나 Linux 커널 쪽 GNU 도구체인 선호는 GCC를 백엔드로 쓰고 싶은 이유는 설명하지만, 왜 중복 프런트엔드가 필요한지는 설명하지 못함
    C++처럼 여러 컴파일러의 스위치, 언어 지원 수준 차이, 플랫폼별 버그 때문에 크로스 플랫폼 개발이 어려워지는 실수를 Rust가 반복하지 않았으면 함
    그래서 gccrs가 rustc_codegen_gcc보다 왜 더 나은 접근인지 설명해 주면 좋겠고, 후자는 훨씬 적은 노력과 위험으로 같은 목표를 달성할 수 있어 보임

    • Rust 구현이 하나 더 있으면 Rust 명세를 검증하고 미정의 동작을 줄이는 감사 역할을 할 수 있음
      MSVC에서 컴파일러 버그를 만나면 보고한 뒤 GCC로 바꿔 작업을 계속할 수 있지만, Rust에서는 지금 그런 선택지가 없음
    • Rust는 이미 C++의 실수에서 배우고 있음
      인용한 것처럼 Rust의 상위 집합이 되지 않으려는 주의가 그 예임
      C/C++의 문제는 컴파일러 업체들이 서로 “더 낫다”고 경쟁하면서 생겼고, 여러 프런트엔드는 보통 버그와 잘못된 구현을 많이 드러내는 장점이 있음
    • 왜 계속 이런 질문이 나오는지 이해가 안 감
      Rust가 어떤 의미에서 신성해서 프런트엔드 재작성이 금지된 것도 아니고, 새 아키텍처에서 Rust를 부트스트랩하는 건 여전히 고통스러움
      LLVM이 지원하지 않는 아키텍처에는 동작하는 Rust 컴파일러도 없음
      codegen_rust_gcc도 기존 Rust 컴파일러와 같은 부트스트랩 문제가 있고, Rust 여러 부분에 아키텍처 지원을 넣어야 하는데 Rust 유지관리자들이 그걸 꺼려 왔음
      그래서 가까운 미래에 바로 쓸 수 있는 Rust 컴파일러가 생겨 Alpha 같은 아키텍처에서도 다시 Rust화된 라이브러리를 큰 고통 없이 빌드할 수 있게 된다면 매우 반가움
    • 그 논리라면 LLVM 커뮤니티는 왜 DragonEgg를 계속 쓰지 않고 Clang, Clang++, libc++ 등을 만들었는지 묻게 됨
      이미 GCC, G++, libstdc++, EDG C++ 프런트엔드가 있었음
      GCC, Clang, MSVC와 다른 컴파일러들은 서로 보완하고, 다른 목적과 시장을 담당하며, 언어가 단일 구현의 우연한 특성이 아니라 명세에 맞게 견고해지도록 해줌
      GNU Toolchain Project, LLVM Project, Rust 프로젝트 모두 문제를 겪어 왔으니 단일 장애점에 의존하지 않는 편이 좋고, 중복성과 반취약성은 친구임
    • Rust가 C++와 C의 실수에서 배워야 한다는 건, 역사상 가장 오래 지속되고 영향력이 크며 널리 배포된 언어들의 실수에서 배우라는 말인가 싶음
      언어 표준을 나쁘게 보고 “이 코드는 C99/C++11”이라고 하지 않고 “이 코드는 SHA256 해시 e49d560cd008344edf745b8052ef714b07595808898c835f17f962a10012f964인 rustc 바이너리/소스에서 동작한다”고 말하길 선호하는 건 혼란스러움
  • Rust에는 언어 표준이 필요함
    https://blog.m-ou.se/rust-standard/
    https://rust-lang.github.io/rfcs/3355-rust-spec.html
    https://github.com/rust-lang/rfcs/pull/3355
    표준이 생기기 전까지 Rust를 도입하지 않을 조직과 산업이 많음
    C, C++, C#, 심지어 JavaScript(ECMAScript)에도 언어 표준이 있는데 Rust라고 없어야 할 이유가 없음
    C: https://www.iso.org/standard/74528.html
    C++: https://isocpp.org/std/the-standard
    C#: https://learn.microsoft.com/en-us/dotnet/csharp/language-ref...
    JavaScript / ECMAScript: https://ecma-international.org/publications-and-standards/st...

    • 그 RFC는 이미 승인됐고, 실제로 진행되기 시작했음
      진척이 실망스러울 만큼 느리긴 하지만 프로젝트는 살아 있고, 내년에는 속도가 붙을 가능성이 있음
      https://blog.rust-lang.org/inside-rust/2023/11/15/spec-visio...
    • Mara의 블로그 글, 즉 첫 번째 링크는 Rust가 이미 기능 추가와 호환성 유지 수단을 갖고 있으므로 본질적으로 표준이 필요 없다는 쪽에 가까움
    • Ferrocene 명세 덕분에 그런 산업에서도 Rust를 사용할 수 있음
    • Go는 정말 잘 된 명세와 여러 구현을 갖고 있음
      https://go.dev/ref/spec
    • 표준이 없으면 도입하지 않을 조직과 산업이 많다는 말에 대한 반론으로, Rust는 그 조직과 산업 없이도 잘하고 있음
      잘 작동하는 방식을 왜 바꿔야 하는지 모르겠음
  • GCC-RS에 부정적인 반응이 많은 게 놀라움
    언어에 여러 구현이 없다면 꽤 초라한 언어라고 봄

    • 예전에는 특히 C/C++ 때문에 그게 통념이었지만, 요즘은 훨씬 더 논쟁적임
      Rust 커뮤니티의 합의는 현재 방식, 즉 정의상 표준인 컴파일러 하나, 많은 문서, 안전 필수 산업용 최소 명세, 일부 모듈 하위 영역 명세가 여러 구현의 장점을 대부분 가져오면서 단점은 피한다는 쪽임
    • 장기적으로는 문법 안정성과 확장/속성에 따른 기능 팽창 방지가 문제임
      C에서 실제로 겪고 있고, C++는 터무니없고 괴상한 복잡성 때문에 이미 구제 불능에 가까움
      이게 없으면 현실적인 대안 구현은 나오기 어려움
    • 개인적으로 여러 구현의 가치는 이해하지만, 문제는 GCC 위에 쌓는다는 데 있음
      GNU 도구체인은 엉망이고, 실제로 그 위에서 어떻게 개발하는지 모르겠음
      이념적인 얘기가 아니라 문자 그대로 GCC 자체의 개발 환경을 어떻게 잡는지 이해가 안 됨
      몇 번 부트스트랩해 본 불운이 있는데, 지금까지 본 소프트웨어 중 가장 나쁘게 동작하는 물건이었음
  • Sega Dreamcast 홈브루 커뮤니티가 gccrs로 새 게임을 만들고, GCC 플러그인으로 unsafe Rust 코드의 정적 분석을 할 수 있다는 대목이 즐거움
    Dreamcast의 Hitachi SH-4 아키텍처를 rustc의 LLVM 백엔드는 지원하지 않지만 GCC는 지원하므로, 미완성 상태의 gccrs도 이런 임베디드 활용에는 도움이 됨

    • 이건 조금 오해의 소지가 있음
      이를 위해 GCC 프런트엔드가 필요한 게 아니라 GCC 백엔드만 있으면 됨
  • 이제 LLVM이 지원하지 않고 GCC가 지원하는 Alpha, SuperH, VAX 같은 아키텍처에서도 Rust 지원을 볼 수 있겠음

    • rustc가 최근 Loongson에서 자금/자원 지원을 끌어내려다 실패한 뒤 지원을 버린 mips64도 해당됨
      https://github.com/rust-lang/compiler-team/issues/648
      LLVM식 사고의 가장 큰 문제는 아키텍처 지원을 하드웨어 회사에서 지원, 즉 유급 개발자 자리를 끌어내는 수단으로 쓴다는 점임
      GNU는 변경 병합 기준이 성가실 만큼 높을 수 있지만, 일단 들어가 지원되면 장기적으로 유지해 줌
      사는 것과 빌리는 것의 차이 같고, GCC에 지원을 넣는 데 개발 시간이 훨씬 더 들지만 한 번 들어가면 남아 있음
    • 이미 지원되는 아키텍처에도 추가 구성 옵션이 생길 것 같음
      예를 들어 최근 RISC-V에서 GCC는 RV32E 대상을 지원하지만 LLVM은 지원하지 않는다는 걸 알게 됨
    • Rust에서 나온 PDP-11 기계어를 빨리 보고 싶음
      마지막으로 확인했을 때 GCC에서는 독립 실행형 C 컴파일이 아직 동작했음
  • gccrs가 “GNU Rust”라는 특별한 언어를 만들지 않고 rustc의 출력, 버그와 특이점까지 복제하려 한다는 부분은 경험상 큰 실수라고 봄
    Rust에는 명세가 없고, 참고 문서는 있지만 명시적으로 규범적이지 않음
    단일 참조 구현 말고는 문서화되지 않은 언어는 장기적인 약점이 있음
    기존 코드가 두 구현 모두에서 동작하도록 보장하려는 목표는 합리적이지만, 버그 호환성까지 약속하면 잘못된 결정과 버그를 화석화하게 됨
    Microsoft는 오래된 프로그램을 계속 실행되게 하면서 보안과 신뢰성 버그를 고치기 위해 많은 인력을 쓰는데, Rust가 수명 초기에 그런 부담을 질 필요는 없음
    언어를 진화시키고 싶다면 품질 보증과 품질 관리를 받아들여야 함
    품질은 테스트로 나중에 주입할 수 없고, 아키텍처와 설계, 설계·코드 리뷰 같은 과정으로 제대로 동작하게 만들고 실패하더라도 올바른 방향으로 실패하게 해야 함
    Common Lisp, C++, FORTRAN 같은 강한 표준은 이 믿음을 받아들였고, Python처럼 사실상 표준에 가까운 약한 언어도 인기는 얻을 수 있지만 변화가 어렵다는 점은 Python 2에서 3으로의 긴 전환과 적은 구현 수에서 드러남

    • 큰 버그를 찾으면 업스트림에 보고하고 두 구현 모두 바꾸면 될 것 같음
  • 이 스레드 후반에 늦게 달지만, 이게 꼭 좋은 일은 아닐 수 있음
    배포판은 몇 달마다 새 버전이 나오는 언어들을 따라가지 못해서 이미 rustc나 Go를 배포판 패키지로 쓰기 어려움
    지금은 놀랍게도 GCC는 필요 없어져 제거하고, 기존 소프트웨어 업데이트를 위해 업스트림 Go와 Rust만 유지하는 시스템이 있음
    몇 달 전 CVE 때문에 Go를 업데이트했을 때 Go 기반 앱들이 자기만의 Go 환경을 네 군데나 저장해 둔 걸 보고 악몽 같았음

  • Linux는 원하면 이미 Clang으로 컴파일해서 전부 LLVM 기반 도구체인을 쓸 수 있음
    GNU “순수성”을 위해 이걸 개발하고 유지하는 중복 노력이 그만한 가치가 있어 보이지 않음

    • 순수성 문제가 아니라 선택지의 문제임
      ClangBuiltLinux 커뮤니티는 Linux가 단일 컴파일러에 의존하면 안 된다고 주장했는데, Rust가 오자 같은 사람들 중 상당수가 갑자기 단일 컴파일러도 괜찮다고 보기 시작했음
    • 여기서 오해가 있는 듯함
      커널을 GNU 전용으로 묶어두려는 게 아니라, 순수 GNU 도구체인을 선택할 수 있게 하려는 것뿐임