[HN Gopher] Building the Rust Compiler with GCC
       ___________________________________________________________________
        
       Building the Rust Compiler with GCC
        
       Author : todsacerdoti
       Score  : 212 points
       Date   : 2025-07-06 21:46 UTC (1 days ago)
        
 (HTM) web link (fractalfir.github.io)
 (TXT) w3m dump (fractalfir.github.io)
        
       | dwheeler wrote:
       | It may not seem like it, but this is impressive progress. Getting
       | a compiler to bootstrap at all is an accomplishment, especially
       | for Rust since that depends on so _many_ things working. Once it
       | can reliably bootstrap, a lot of performance-improving steps can
       | begin. Congrats!
        
         | ramon156 wrote:
         | I'm not that deep into gcc. Would it really add a lot of
         | performance gain?
        
           | mijoharas wrote:
           | I don't think the goal is performance (and I don't think a
           | well optimised gcc implementation will improve performance).
           | I think the comment you are replying to is speaking about
           | performance because of things like this line from the
           | article:
           | 
           | > ...with the emphasis on limp. At some points, we are using
           | over 100 GB of RAM... not ideal.
           | 
           | (so performance will be the next thing to be worked on to
           | make this useable).
           | 
           | I think the goals of using gcc for rust is that it can
           | provide a parallel implementation, which can help uncover
           | bugs and provide more stability to the entire language, and
           | because if there is a large gcc project already, they may be
           | reticent to introduce LLVM as a dependency as well.
        
             | tucnak wrote:
             | > reticent
             | 
             | That is a $200 word right there, and you're using it wrong.
        
               | mijoharas wrote:
               | https://en.wiktionary.org/wiki/reticent (2)
               | 
               | https://www.merriam-webster.com/grammar/can-reticent-
               | mean-re...
               | 
               | You're correct that some people recommend against it, and
               | it's only been in use since the second world war.
        
               | echelon wrote:
               | I only knew of the "reluctant" meaning.
               | 
               | Languages mutate quickly.
        
               | mijoharas wrote:
               | Yep, it's what I've heard most often too (I only just
               | learnt the other meaning from the kind person prompting
               | me look up the definition). I also don't think of it as
               | that much of an unusual word, but hey.
        
               | trelane wrote:
               | It's also Merriam-Webster, a descriptivist dictionary.
               | Others are of a prescriptionist bent. For example,
               | Cambridge: https://dictionary.cambridge.org/us/dictionary
               | /english/retic...
               | 
               | More on prescriptive vs descriptive:
               | https://twominenglish.com/prescriptivist-vs-
               | descriptivist/
               | 
               | I personally am more descriptivist when it comes to
               | English. It is also a lot of fun to bring some M-W
               | citations in order to "Well, actually" someone who is
               | "Well, actually"-ing.
        
               | nartho wrote:
               | Reticent is a valid synonym of reluctant.
        
             | johnklos wrote:
             | And let's not forget that bootstrapping rust using
             | something other than rust is quite useful.
        
               | pornel wrote:
               | You can't use this implementation to bootstrap Rust (in
               | the sense of bootstrapping from non-Rust language or a
               | compiler that isn't the rustc).
               | 
               | This GCC support here is only a backend in the existing
               | Rust compiler written in Rust. The existing Rust compiler
               | is using GCC as a language-agnostic assembler and
               | optimizer, not as a Rust compiler. The GCC part doesn't
               | even know what Rust code looks like.
               | 
               | There is a different project meant to reimplement Rust
               | (front end) from scratch in C++ in GCC itself, but that
               | implementation is far behind and can't compile non-toy
               | programs yet.
        
       | aswanson wrote:
       | I just started playing with rust again today. Godspeed.
        
       | Cogito wrote:
       | Really great read.
       | 
       | Someone mentioned recently that the slowness of rustc is in large
       | part due to llvm. I know that is probably orthogonal to the work
       | here, but I do like the idea of building the compiler with
       | different toolchains, and that there may be follow on effects
       | down the line.
        
         | torstenvl wrote:
         | It's slow because the borrow checker is NP complete. LLVM may
         | or may not generate slower code than GCC would for rustc, but I
         | doubt it's anywhere close to the primary cause of the lack of
         | snappy.
        
           | almostgotcaught wrote:
           | You're wrong it's been debunked that the borrow checker is
           | any appreciable part of the compile time - Steve Klabnik
           | actually verified it on here somewhere.
           | 
           | Edit: found it
           | 
           | https://news.ycombinator.com/item?id=44391240
        
             | Cogito wrote:
             | Thanks, I'm pretty sure I was thinking of this whole
             | discussion when I made my original comment :)
        
             | resurrectedcyb wrote:
             | I don't see that debunking it. Instead, it says "usually".
             | That means that it depends on the project.
             | 
             | There is definitely Rust code that takes exponential time
             | to compile, borrow checker or not.
             | 
             | https://play.rust-
             | lang.org/?version=stable&mode=release&edit...
             | 
             | https://github.com/rust-lang/rust/issues/75992
             | 
             | Some people used async in ways that surfaced these
             | problems. Upgraded rustc, then the project took forever to
             | compile.
        
               | steveklabnik wrote:
               | I say "usually" because of course sometimes bugs happen
               | and of course you can conduct degenerate stress tests.
               | But outside of those edge cases, it's not an issue. If it
               | were, blog posts that talk about lowering compile times
               | would be discussing avoiding the borrow checker to get
               | better times, but they never do. It's always other
               | things.
        
               | resurrectedcyb wrote:
               | Is there any tool for Rust that does profiling that
               | detects what part of compilation time is caused by what?
               | Like, a tool that reports:
               | 
               | - Parsing: x ms
               | 
               | - Type checking: y ms
               | 
               | - LLVM IR generation: z ms
               | 
               | And have there been any statistics done on that across
               | open-source projects, like mean, median, percentiles and
               | so on?
               | 
               | I am asking because it should depend a lot on each
               | project what is costly in compile time, making it more
               | difficult to analyse. And I am also curious about how
               | many projects are covered by "edge cases", if it is 1%,
               | 0.1%, 0.01%, and so on.
        
               | steveklabnik wrote:
               | The post my original comment is on discusses doing this
               | at length.
               | 
               | > And have there been any statistics done on that across
               | open-source projects, like mean, median, percentiles and
               | so on?
               | 
               | I am not aware of any. But in all the posts on this topic
               | over the years, codegen always ends up being half the
               | time. It's why cargo check is built the way it is, and
               | why it's always faster than a full build. If non-codegen
               | factors were significant with any regularity, you'd be
               | seeing reports of check being super slow compared to
               | build.
        
               | resurrectedcyb wrote:
               | I have actually seen a few posts here and there of 'cargo
               | check' being slow. I have also heard of complaints of
               | rust-analyzer being slow, though rust-analyzer may be
               | doing more than just 'cargo check'.
               | 
               | https://www.reddit.com/r/rust/comments/1daip72/rust_check
               | run...
               | 
               | May not be indicative, not sure what crate the author was
               | using.
        
               | steveklabnik wrote:
               | cargo check can be slow but what I mean is relative to a
               | full build. Large projects are going to be slow to build
               | by virtue of being large.
        
               | resurrectedcyb wrote:
               | That project had 'cargo check' take 15-20 minutes, though
               | it might not have been indicative, the submitter posted
               | an update about how it was fixed.
               | 
               | This may be a better example.
               | 
               | https://github.com/rust-lang/rust/issues/132064
               | 
               | >cargo check with 1.82: 6m 19s
               | 
               | >cargo check with 1.81: 1m 22s
               | 
               | It may be difficult to fix.
               | 
               | https://github.com/rust-
               | lang/rust/issues/132064#issuecomment...
               | 
               | >Triage notes (AFAIUI): #132625 is merged, but the
               | compile time is not fully clawed back as #132625 is a
               | compromise between (full) soundness and performance in
               | favor of a full revert (full revert would bring back more
               | soundness problems AFAICT)
               | 
               | Update: They fixed it, but another issue surfaced,
               | possibly related, as I read the comments.
        
           | resurrectedcyb wrote:
           | I do not know if the borrow checker and Rust's type system
           | have been formalized. There are stacked borrows and tree
           | borrows, and other languages experimenting with features
           | similar to borrow checking, but without formal algorithms
           | like Algorithm W or J for Hindley-Milner, or formalizations
           | like of Hindley-Milner and problems like typability for them,
           | I am not sure how one can prove the complexity class of the
           | borrow checking problem nor a specific algorithm, like
           | https://link.springer.com/chapter/10.1007/3-540-52590-4_50
           | does for ML.
           | 
           | I could imagine you being correct about the borrow checking
           | typability problem being NP-complete. Or an even worse
           | complexity class. Typability in ML is EXPTIME-complete, a
           | larger set than NP-complete
           | https://en.wikipedia.org/wiki/EXPTIME
           | https://dl.acm.org/doi/10.1145/96709.96748 .
           | 
           | I also am not sure how to figure out if the complexity class
           | of some kind of borrow checking has something to do with the
           | exponential compile times of some practical Rust projects
           | after they upgraded compiler version, for instance in
           | https://github.com/rust-lang/rust/issues/75992 .
           | 
           | It would be good if there was a formal description of at
           | least one borrow checking algorithm as well as the borrow
           | checking "problem", and maybe also analysis of the complexity
           | class of the problem.
        
             | genrilz wrote:
             | There isn't a formal definition of how the borrow checking
             | algorithm works, but if anyone is interested, [0] is a
             | fairly detailed if not mathematically rigorous description
             | of how the current non-lexical lifetime algorithm works.
             | 
             | The upcoming Polonius borrow checking algorithm was
             | prototyped using Datalog, which is a logical programming
             | language. So the source code of the prototype [1]
             | effectively is a formal definition. However, I don't think
             | that the version which is in the compiler now exactly
             | matches this early prototype.
             | 
             | EDIT: to be clear, there is a polonius implementation in
             | the rust compiler, but you need to use '-Zpolonius=next'
             | flag on a nightly rust compiler to access it.
             | 
             | [0]: https://rust-lang.github.io/rfcs/2094-nll.html
             | 
             | [1]: https://github.com/rust-lang/polonius/tree/master
        
               | resurrectedcyb wrote:
               | Interesting. The change to sets of loans is interesting.
               | Datalog, related to Prolog, is not a language family I
               | have a lot of experience with, only a smidgen. They use
               | some kind of solving as I recall, and are great at
               | certain types of problems and explorative programming.
               | Analyzing the performance of them is not always easy, but
               | they are also often used for problems that already are
               | exponential.
               | 
               | I read something curious.
               | 
               | https://users.rust-lang.org/t/polonius-is-more-ergonomic-
               | tha...
               | 
               | >I recommend watching the video @nerditation linked. I
               | believe Amanda mentioned somewhere that Polonius is 5000x
               | slower than the existing borrow-checker; IIRC the plan
               | isn't to use Polonius instead of NLL, but rather use NLL
               | and kick off Polonius for certain failure cases.
               | 
               | That slowdown might be temporary, as it is optimized over
               | time, if I had to guess, since otherwise there might then
               | be two solvers in compilers for Rust. It would be line
               | with some other languages if the worst-case complexity
               | class is something exponential.
        
               | genrilz wrote:
               | > IRC the plan isn't to use Polonius instead of NLL, but
               | rather use NLL and kick off Polonius for certain failure
               | cases.
               | 
               | Indeed. Based on the last comment on the tracking issue
               | [0], it looks like they have not figured out whether they
               | will be able to optimize Polonius enough before
               | stabilization, or if they will try non-lexical lifetimes
               | first.
               | 
               | [0]: https://github.com/rust-lang/rust-project-
               | goals/issues/118
        
         | JoshTriplett wrote:
         | Depends on the workload, but yes, codegen is a huge part of the
         | total compilation time.
         | 
         | That said, that doesn't mean LLVM is always where the fixes
         | need to be. For instance, one reason rustc spends a lot of time
         | in LLVM is that rustc feeds more code to LLVM than it should,
         | and relies on the LLVM optimizer to improve it. Over time,
         | we're getting better about how much code we throw at LLVM, and
         | that's providing performance improvements.
        
           | Cogito wrote:
           | I'm completely ignorant so forgive me if this is obvious: in
           | the effort of the parent article - to compile rustc with gcc
           | - will rustc _still_ be feeding lots of code to LLVM, or
           | would that code now be fed to gcc?
        
             | heftig wrote:
             | Which codegen backend the building compiler uses is
             | independent of which codegen backend(s) the built compiler
             | uses.
             | 
             | Similarly, you can build Clang using itself or using GCC.
             | The resulting compiler should behave the same and produce
             | the same machine code, even if its own machine code is
             | somewhat different.
             | 
             | The produced binaries could still have artifacts from the
             | original compiler in them, e.g. if "compiler built-in"
             | libraries or standard libraries were compiled with the
             | original compiler.
             | 
             | Both GCC and rustc use a multi-stage build process where
             | the new compiler builds itself again, so you reach an
             | idempotent state where no artifacts from the original
             | compiler are left.
        
         | feznyng wrote:
         | There's an experimental compiler backend [1] using cranelift
         | [1] that's supposed to improve debug build times. I never see
         | it mentioned often in threads about Rust's long compilation
         | time so I'm not sure if I'm missing something.
         | 
         | [1] https://github.com/rust-lang/rustc_codegen_cranelift/ [2]
         | https://cranelift.dev/
        
       | saagarjha wrote:
       | > Normally, debuing the compiler is fairly straightforward: it is
       | more or less a run of the mill executable.
       | 
       | > In the bootstrap process, the entire thing becomes way more
       | complex. You see, rustc is not invoked directly. The bootstrap
       | script calls a wrapper around the compiler.
       | 
       | > Running that wrapped rustc is not easy to run either: it
       | requires a whole lot of complex, environment flags to be set.
       | 
       | > All that is to say: I don't know how to debug the Rust
       | compiler. I am 99.9 % sure there is an easy way to do this,
       | documented somewhere I did not think to look. After I post this,
       | somebody will tell me "oh, you just need to do X".
       | 
       | > Still, at the time of writing, I did not know how to do this.
       | 
       | > So, can we attach gdb to the running process? Nope, it crashes
       | way to quickly for that.
       | 
       | It's kind of funny how often this problem crops up and the
       | variety of tricks I have in my back to deal with it. Sometimes I
       | patch the script to invoke gdb --args [the original command]
       | instead, but this is only really worthwhile if it's a simple
       | shell script and also I can track where stdin/stdout are going.
       | Otherwise I might patch the code to sleep a bit before actually
       | running anything to give me a chance to attach GDB. On some
       | platforms you can get notified of process execs and sometimes
       | even intercept that (e.g. as an EDR solution) and sometimes I
       | will use that to suspend the process before it gets a chance to
       | launch. But I kind of wish there was a better way to do this in
       | general...LLDB has a "wait for launch" flag but it just spins in
       | a loop waiting for new processes and it can't catch anything that
       | dies too early.
        
         | o11c wrote:
         | Other ideas:
         | 
         | * Run the whole tree of processes under `gdb` with `set detach-
         | on-fork off`.
         | 
         | * LD_PRELOAD a library that inserts the sleeps for you, maybe
         | on startup or maybe on signal/exit.
         | 
         | Ideally, we'd have some kind of infrastructure to name and
         | identify _particular_ processes recursively.
        
         | jcranmer wrote:
         | I have a LD_PRELOAD library that hooks SIGSEGV into spawning
         | gdb on the process using the best guess for the process's
         | terminal (which currently isn't very smart because I haven't
         | yet needed to debug processes that do a lot of stdio
         | redirection).
        
           | dezgeg wrote:
           | Do you have this publicly available? Sounds useful!
        
             | theMMaI wrote:
             | https://github.com/stass/libsegfault does something similar
             | if not the same
        
         | CJefferson wrote:
         | I agree, recently I was working with a large Java program and
         | after spending about 90 minutes (far too long, I was getting
         | obsessed), I just gave up trying to get it into a debugger.
         | 
         | This is one area where rust disappoints me, there isn't a
         | "cargo debug" built in (there is an external program but it
         | doesn't work well), and when I just manually attach gdb most of
         | the symbols are usually missing.
         | 
         | I would seriously consider a language billed as "debugger-
         | first", just to see what the experience was like.
        
           | dmitrygr wrote:
           | > I would seriously consider a language billed as "debugger-
           | first", just to see what the experience was like.
           | 
           | Use C, tell gdb "-O0 -g -ggdb3"
        
             | CJefferson wrote:
             | You are probably fairly accurate there, but that would
             | require me going back and writing more C, which I've try to
             | avoid nowadays :)
             | 
             | I still sometimes get caught out getting debug information
             | all in the right place, particularly when someone is using
             | ninja or some such, I've even ended up wrapping gcc just to
             | strip optimisation options and add -g, rather than figure
             | out how to fight some very complex build system mess.
        
           | gavinray wrote:
           | For Java programs, you run the JAR with a flag telling it to
           | launch a remote-debug server listening on some port
           | (traditionally 5005):                 java -jar -agentlib:jdw
           | p=transport=dt_socket,server=y,suspend=n,address=*:5005
           | 
           | If your app is a gradle app, the flag is "--debug-jvm"
           | 
           | Then you connect to it with "jdb"
        
             | CJefferson wrote:
             | Thanks, I'll try that if I'm ever Java debugging again!
             | 
             | I don't think I want to do it myself, but I'd love a "101
             | ways to get into a debugger" webpage.
        
         | touisteur wrote:
         | Now wondering whether the author might be able to force a core
         | dump. With recent snapshot abilities, on Modern Intel
         | processors one can get a Processor Trace that can be helpful
         | even without getting an actual interactive debugging session
         | (haven't done one of those in a while as snapshots seem enough
         | for my needs these days).
        
         | timhh wrote:
         | I have a C library (I've also done a Python one in the past)
         | that you load into the executable you want to debug. It
         | activated based on an environment variable so normally I just
         | permanently link it.
         | 
         | When it is loaded it will automatically talk to VSCode and tell
         | it to start a debugger and attach to it & it waits for the
         | debugger to attach.
         | 
         | End result is you just have to run your script with an
         | environment variable set and it will automatically attach a
         | nice GUI debugger to the process no matter how deeply buried in
         | scripts and Makefiles it is.
         | 
         | https://github.com/Timmmm/autodebug
         | 
         | I currently use this for debugging C++ libraries that are
         | dynamically loaded into Questa (a commercial SystemVerilog
         | simulator) that is started by a Python script running in some
         | custom build system.
         | 
         | In the past I used it to debug Python code running in an
         | interpreter launched by a C library loaded by Questa started by
         | a Makefile started by a _different_ Python interpreter that was
         | launched by _another_ Makefile. Yeah. It wasn 't the only
         | reason by a long shot but that company did not survive...
        
         | izacus wrote:
         | I find it outright incredible how much software is built in a
         | way that outright prevents debugging and observabiliy of what's
         | going on in it (no hooks, no logging, no error messages, etc.).
         | I have no idea how people fix bugs there outside vibing.
        
           | mystified5016 wrote:
           | The old fashioned way: elbow grease and lots of squinting and
           | swearing at your computer
        
         | AndrewDucker wrote:
         | This is where I really appreciate the .Net command
         | 
         | System.Diagnostics.Debugger.Launch();
         | 
         | Which pops up a window asking you to select what debugger you
         | want to use, and then opens your application in it.
        
         | mark_undoio wrote:
         | Process recording by time travel debug seems like a good fit
         | for this problem - then you can capture 100% of process
         | execution and then go back and investigate further.
         | 
         | We (Undo.io) came up with a technique for following a tree of
         | processes and initiating process recording based on a glob of
         | program name. It's the `--record-on` flag in
         | https://docs.undo.io/UsingTheLiveRecorderTool.html. You can
         | grab a free trial from our website.
         | 
         | For open source, with rr (https://rr-project.org/) I think
         | you'd just `rr record` the initial process and you'll end up
         | capturing the whole process tree - then you can look at the one
         | you're interested in.
         | 
         | As others have said you could also do some smart things with
         | GDB's follow-fork settings but I think process recording is
         | ideal for capturing complicated situations like this as you can
         | go and review what happened later on.
        
       | dtgriscom wrote:
       | I love vicarious engineering.
        
       | Rogach wrote:
       | Sounds like attempting to always inline a recursive function
       | should be an error instead. But it's probably undesirable to make
       | that change because it would likely break existing crates and
       | thus backwards compatibility as well?
        
         | dwattttt wrote:
         | "inline(always)" I expect matches Clang's "always_inline", and
         | Clang's documentation makes what it does clearer:
         | 
         | > Inlining heuristics are disabled and inlining is always
         | attempted regardless of optimization level.
         | 
         | So it should be interpreted as "always attempt to inline", as
         | opposed to "this must be inlined", or other attributes that
         | instead influence the "should this be inlined" heuristic.
         | 
         | EDIT: as curious an attribute as it might be, I didn't mean to
         | be talking about inclines
        
         | j16sdiz wrote:
         | recursive function can be inline when unrolled. This is a valid
         | optimization.
         | 
         | Google "llvm inline recursion". It exists. It should works.
         | Fibonacci is the standard test case.
        
           | 1718627440 wrote:
           | But not every recursive function can be inlined (without a
           | secondary stack).
        
       | 1718627440 wrote:
       | Wouldn't the best way to check whether something is inlineable to
       | just try it? Why can't GCC be let to try to inline it and if it
       | encounters that it isn't possible it can be instructed to just
       | not inline it?
        
         | pornel wrote:
         | My guess is that the public library interface of GCC doesn't
         | support it this way.
         | 
         | This back-end uses the confusingly-named libgccjit (not as
         | JIT), which gives access only to a subset of GCC's
         | functionality.
         | 
         | If something isn't already exposed, it might take a while to
         | get patches to GCC and libgccjit accepted and merged.
        
       | juxhindb wrote:
       | > Really, think about it. How often do you see somebody
       | inexperienced use ifs instead of a switch? This pattern is common
       | enough for most compilers to recognize, and more importantly,
       | optimize it.
       | 
       | This made me chuckle. Playing dumb so that gcc optimizes you away
       | is both hilarious and genius
        
         | 1718627440 wrote:
         | Actually the compiler converts the switch to ifs not the other
         | way around.
        
       ___________________________________________________________________
       (page generated 2025-07-07 23:01 UTC)