I know that one recent innovation is that compilers have adopted a more service-oriented architecture, kind of like the Roslyn compiler. This allows them to not only compile your code, but (for instance) inform your text editor and linter and similar tooling of syntax issues
What are other differences? Is llvm still relevant outside of academia?
Are there any books, papers, or open source projects one could study to learn how compilers are built in this day and age?
Also: does the more abstract "programming language theory" popular in the more formal functional programming world (e.g. denotational semantics, lambda calculus, Floyd-Hoare logic, type theory, etc: this sort of stuff[1]) have any relevance to compiler writers and language/language tooling developers in industry?
I am surprised by the question. In my experience of the industry, LLVM has become dominant to a degree which makes some of the work kind of boring.
I expect MLIR will eventually become similarly universal. In the compiler I am currently working on, we render the AST straight into an MLIR dialect - every node becomes an op - and the rest of the frontend is implemented in terms of MLIR passes.
Writing two backends for two different architectures would be a lot of work and then lots of platform specific optimizations therefore llvm is the present and seems to be the future for the foreseeable future as well.
Besides that, we or at least I studied and was implied that hand written recursive descendant parsers are inferior way of doing things. Real world usage should always start with a grammer handed over to a parser generator (bison etc) that in turn have LR(1) algorithms driven by tables.
Turns out that a of lot of widely used programming languages are moving away from LR(1) and having their parsers as hand written recursively descendent parsers.
Ruby seems to be switching away from generated LR(1) parsers, more details here [0]
EDIT: References.
[0] https://railsatscale.com//2023-06-12-rewriting-the-ruby-pars...
1) Reading the source code of roslyn which can be quite readable 2) Building VSCode extensions to add diagnostics and implement code actions. You can use any open source language server as the reference