Tests for an LLVM-based minimum effort code generator for ABC
This directory contains some preliminary tests to see if a code generator for ABC could be built with minimum effort using LLVM.
The general idea is due to Erin van der Veen, and his repository is located at https://gitlab.com/top-software/llvm-code-generator.
General idea
Non-control-flow ABC instructions are implemented in LLVM IR, in this directory
in rts.ll
. For example:
attributes #0 = { alwaysinline }
define private i64* @addI(i64* %bsp.0) #0 {
%t.0 = load i64, i64* %bsp.0
store i64 undef, i64* %bsp.0
%bsp.1 = getelementptr i64, i64* %bsp.0, i64 1
%t.1 = load i64, i64* %bsp.1
%t.2 = add i64 %t.0, %t.1
store i64 %t.2, i64* %bsp.1
ret i64* %bsp.1
}
ABC code can then be compiled without much effort; we only need to cleverly break the ABC code up into subroutines and handle the control flow instructions correctly. For example:
%bsp.307 = call i64* @pushI(i64 %r.1, i64* %bsp.306.0)
%bsp.308 = call i64* @addI(i64* %bsp.307)
%bsp.309 = call i64* @pushI(i64 1, i64* %bsp.308)
%bsp.310 = call i64* @push_b(i64 1, i64* %bsp.309)
; ...
Because the definitions of the instructions (e.g. addI
) are alwaysinline
,
there is no overhead for these calls, and we can rely on LLVM to optimize
subroutine bodies after the alwaysinline
pass.
This means we need a custom LLVM pipeline, implemented in the Makefile as:
| opt-11 -S -always-inline \
| opt-11 -S -O3 \
| sed 's/noinline nounwind optnone/nounwind/' \
| opt-11 -S -O3 \
Stacks
B-stack arguments are passed as subroutine arguments and return values; there is no global B-stack. Subroutines do have a global A-stack:
define private i64 @s1(i64** %globasp, i64 %arg) #0 {
%astack = alloca i64*, i64 10000
%asp.000 = getelementptr i64*, i64** %astack
%bstack = alloca i64, i64 10000
%bsp.000 = getelementptr i64, i64* %bstack, i64 9999
%bsp.001 = call i64* @pushI(i64 %arg, i64* %bsp.000)
; ...
This ensures that the B-stack is combined with the C-stack, and the alloca
s
for subroutine-local stacks are optimized away by LLVM.
Control flow
Example of a jmp_false else.1
instruction, using peek_b
and pop_b1
from
the RTS:
%t.0 = call i64 @peek_b(i64* %bsp.102)
%bsp.103 = call i64* @pop_b1(i64* %bsp.102)
%t.1 = trunc i64 %t.0 to i1
br i1 %t.1, label %l.0, label %else.1
Example of a jsr s1
instruction with one B-stack argument and one B-stack
return value:
%arg.0 = call i64 @peek_b(i64* %bsp.302)
%bsp.302.0 = call i64* @pop_b1(i64* %bsp.302)
%r.0 = call i64 @s1(i64** %globasp, i64 %arg.0)
%bsp.303 = call i64* @pushI(i64 %r.0, i64* %bsp.302.0)