This happens naturally if you bump-allocate them in a garbage-collected run-time, particularly under a copying collector. Free lists also tend to co-locate because they are produced during sweep phases of GC which run through heaps in order of address.
Don't make me bring out the L word for the billionth time.
> A flat array of Exprs can make it fun and easy to implement hash consing
OK, it's not a case of L-ignorance, just willful neglect.
> A sufficiently smart memory allocator might achieve the same thing, especially if you allocate the whole AST up front and never add to it
> Again, a really fast malloc might be hard to compete with—but you basically can’t beat bump allocation on sheer simplicity.
- Finding and understanding bugs in C compilers. Xuejun Yang, Yang Chen, Eric Eide, and John Regehr. PLDI 2011. https://dl.acm.org/citation.cfm?id=1993532 - Compiler validation via equivalence modulo inputs. Vu Le, Mehrdad Afshari, and Zhendong Su. PLDI 2014. https://dl.acm.org/citation.cfm?id=2594334
Front end is lexing, parsing, building the AST (in whole or just keeping pieces of it around), semantic analysis. When that is done, you can say "Yup, valid program. It is possible to generate code." So, to your question, yes. Those techniques from the course are 100% in play.
Back-end is turning the AST into something you can optimized and generate code from. (Sometimes there is a "middle-end" in there....) High-level optimizations, low level optimizations, memory allocation, register allocation.
Tooling is everything that keeps your life from being miserable as a user -- starting with debug symbols and these days people expect library and package management, etc, etc.
So if you are interested in exploring the Next Great Syntax or some semantic problems that can be re-written into C, then doing a C-front is a great way to do that. Let the C compiler handle all the really-really-really-hard back-end issues for you, and avoid all that distraction. But.... expect that getting debug symbols from your spiffy language into the ready-to-link object file is going to be, as they say, "non-trivial". So... great for experiments and a great learning exercise, but it's hard to do a production compiler that way.
> The Vanilla-5 cores are a 5-stage in-order pipeline RV32IM cores so they support the integer and multiply extensions
So, roughly comparable to a high-speed Cortex M.
> instead of using caches, the entire memory address space is mapped across all the nodes in the network using a 32-bit address scheme. This approach, which also means no virtualization or translation, simplifies the design a great deal.
The diagram shows each core has icache and dcache; what they've ditched is cache coherency. That certainly makes it simpler to implement but now the cores have to be responsible for their own coherency. Also, none of your protected mode operating system nonsense - this is designed to run a single program and get everything out of the way. Every core can potentially overwrite any other core's memory, and if it does so you won't know until you have a cache miss. Good luck figuring that one out in the debugger.
This is very clearly intended for the sort of AI or image processing workload where you can clearly partition it two-dimensionally across the array to identical nodes, and then have those nodes collaborate locally by passing messages across the edges.
This is not quite true: the local data memories are not caches, i.e., they do not implicitly move memory in from a more distant tier in the memory hierarchy. They are just plain explicitly managed local memories (sometimes called "scratchpads" to distinguish them from caches).
To this day I’m grateful I stumbled across the Hotline software and the server.