aboutsummaryrefslogtreecommitdiff

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 allocas 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)