Live data from Hacker News

Flattening ASTs and other compiler data structures (2023)

cs.cornell.edu

11–20 of 52 posts

Re: Flattening ASTs and other compiler data structures (2023)

#11
post #5

Rust-analyzer uses a similar technique for parsing https://github.com/rust-lang/rust-analyzer/blob/master/crate... which then gets fed into https://github.com/rust-analyzer/rowan (lossless syntax tree)

There is a longer overview of it here: https://github.com/rust-lang/rust-analyzer/blob/master/docs/...

Re: Flattening ASTs and other compiler data structures (2023)

#12
post #3

"Instead of allocating Expr objects willy-nilly on the heap, we’ll pack them into a single, contiguous array." Zig compiler pipeline (AST, Zir, Air, Sema) does exactly this on all layers. Not only contiguous, but instead of array-of-structs it is struct-of-arrays, so walking the tree is even more cache friendly. For AST see: https://github.com/ziglang/zig/blob/master/lib/std/zig/Ast.z...

Makes me wonder if people in APL/J/K community have not been influenced or influencing this kind of technique. IIRC Aaron Hsu does tree processing through arrays (but i'm not skilled enough to analyze his code)

Re: Flattening ASTs and other compiler data structures (2023)

#13
post #10

As the article mentions, this makes it quite similar to a bytecode vm. I think the traditional wisdom is that an AST walker is easy to write, but for speed you'd want a bytecode interpreter. It'd be interesting to see how close the performance gets with this flattened AST. In practice I think there are more differences. E.g. AST interpreters tend to pass environments around while bytecode interpreters often store the…

If you instead flatten the expression tree into RPN, then you can execute it like that, with a stack machine.

I seem to recall that the Red Dragon Book (Compilers: Principles, Techniques and Tools, Aho, Sethi, Ullman [1988]) describes a technique whereby intermediate code is represented in RPN, and transformations are performed by pattern matches on it.

Re: Flattening ASTs and other compiler data structures (2023)

#14
post #10

As the article mentions, this makes it quite similar to a bytecode vm. I think the traditional wisdom is that an AST walker is easy to write, but for speed you'd want a bytecode interpreter. It'd be interesting to see how close the performance gets with this flattened AST. In practice I think there are more differences. E.g. AST interpreters tend to pass environments around while bytecode interpreters often store the…

If you instead flatten the expression tree into RPN, then you can execute it like that, with a stack machine. I seem to recall that the Red Dragon Book ( Compilers: Principles, Techniques and Tools , Aho, Sethi, Ullman [1988]) describes a technique whereby intermediate code is represented in RPN, and transformations are performed by pattern matches on it.

The sample flat program in the post is exactly RPN, no?

Re: Flattening ASTs and other compiler data structures (2023)

#15
post #14

Earlier quoted context omitted.

If you instead flatten the expression tree into RPN, then you can execute it like that, with a stack machine. I seem to recall that the Red Dragon Book ( Compilers: Principles, Techniques and Tools , Aho, Sethi, Ullman [1988]) describes a technique whereby intermediate code is represented in RPN, and transformations are performed by pattern matches on it.

The sample flat program in the post is exactly RPN, no?

I think it would be more like RPN if it used a stack, and operands were specified as relative offsets (i.e., stack offsets). In the version I wrote, operands are still represented as absolute offsets in the expression table.

Re: Flattening ASTs and other compiler data structures (2023)

#18
I thought a reddit comment on this article had an interesting point:

https://www.reddit.com/r/rust/comments/1d3b356/my_new_favori...

[–]Timzhy0 3 points 7 months ago

Btw I think one can go a step further than the author, there is no need to keep two explicit ExprRef baked in a binary node (lhs, rhs). You can exploit locality, basically the AST, seen it the LISP way, is just an arbitrarily nestable list, where elements are atoms or other lists. Hence all you need to know is where each list ends (and if it's an atom you can assume it spans one node) and actually one bit to know if it is the last entry in the list is quite ergonomic as well (because then you can distinguish whether moving next slot in the AST means there is a sibling). Basically it's easier to keep it sync while constructing and takes up less memory per node. I pay 40 bits per node, stored interleaved for best cache locality (some unaligned accesses but I think it's still worthwhile), 8 bits for the tag, 32 for the data, if data is bigger, 32 is an index into some auxiliary segment (basically a ptr).

Re: Flattening ASTs and other compiler data structures (2023)

#19
post #3

"Instead of allocating Expr objects willy-nilly on the heap, we’ll pack them into a single, contiguous array." Zig compiler pipeline (AST, Zir, Air, Sema) does exactly this on all layers. Not only contiguous, but instead of array-of-structs it is struct-of-arrays, so walking the tree is even more cache friendly. For AST see: https://github.com/ziglang/zig/blob/master/lib/std/zig/Ast.z...

Makes me wonder if people in APL/J/K community have not been influenced or influencing this kind of technique. IIRC Aaron Hsu does tree processing through arrays (but i'm not skilled enough to analyze his code)

Do you have a link to such an example of Aaron's code? Thank you in advance!

Re: Flattening ASTs and other compiler data structures (2023)

#20
post #3

"Instead of allocating Expr objects willy-nilly on the heap, we’ll pack them into a single, contiguous array." Zig compiler pipeline (AST, Zir, Air, Sema) does exactly this on all layers. Not only contiguous, but instead of array-of-structs it is struct-of-arrays, so walking the tree is even more cache friendly. For AST see: https://github.com/ziglang/zig/blob/master/lib/std/zig/Ast.z...

I work on a C dialect where everything is flattened. JSON and other trees in particular. Binary heaps are flat, merge sort and iterator heaps are absolutely great, can build LSM databases with that. Stacks, circular buffers, hash maps, etc, all flat. Templated output (PHP like) is done by a flat data structure.

https://github.com/gritzko/librdx/blob/master/abc/B.md

Apart from locality and lifetimes, these flat data structures improve composability. When every data structure is a flat buffer, you can mmap them or zip them or send them by the network, all by the same routine. They are uniform like bricks, in a sense.

Post reply on HN