Introducing a simple experimental Lisp compiler, written in uLisp, that will compile a Lisp function into RISC-V machine code. You can run the compiler on the RISC-V core of a Raspberry Pi Pico 2 (or another RP2350-based board):
For full details see: A Lisp compiler to RISC-V written in Lisp.
Using the compiler
To run the compiler you simply call compile on a Lisp function; for example:
(compile 'fibonacci)
The function will be compiled into a machine code function, replacing the original Lisp code, so that calling fibonacci will now execute the RISC-V machine-code version.
Specification
The compiler understands the following Lisp objects:
Defining variables and functions: defun, setq
Symbols: nil, t
List functions: car, cdr
Arithmetic functions: +, -, *, /, mod, 1+, 1-
Arithmetic comparisons: =, <, <=, >, >=, /=
Conditionals: if, and, or
Tail-call optimisation
Although the compiler doesn’t include any iteration constructs, it does provide tail-call optimisation which can make recursive programs as efficient as iterative ones.
Example
The following example shows the code generated by a simple function, rec, a recursive function related to the factorial function:
(defun rec (n)
(1+ (* n (if (= n 0) 0 (rec (1- n))))))
Compiling this gives the following RISC-V machine code:
> (compiler 'rec)
0000 rec
0000 872a ($mv 'a4 'a0)
0002 853a ($mv 'a0 'a4)
0004 1171 ($addi 'sp 'sp -4)
0006 c02a ($sw 'a0 0 '(sp))
0008 853a ($mv 'a0 'a4)
000a 1171 ($addi 'sp 'sp -4)
000c c02a ($sw 'a0 0 '(sp))
000e 4501 ($li 'a0 0)
0010 4582 ($lw 'a1 0 '(sp))
0012 0111 ($addi 'sp 'sp 4)
0014 8533 ($sub 'a0 'a1 'a0)
0016 40a5
0018 3513 ($seqz 'a0 'a0)
001a 0015
001c c119 ($beqz 'a0 lab1)
001e 4501 ($li 'a0 0)
0020 a819 ($j lab2)
0022 lab1
0022 853a ($mv 'a0 'a4)
0024 157d ($addi 'a0 'a0 -1)
0026 1161 ($addi 'sp 'sp -8)
0028 c006 ($sw 'ra 0 '(sp))
002a c23a ($sw 'a4 4 '(sp))
002c f0ef ($jal rec)
002e fd5f
0030 4712 ($lw 'a4 4 '(sp))
0032 4082 ($lw 'ra 0 '(sp))
0034 0121 ($addi 'sp 'sp 8)
0036 lab2
0036 4582 ($lw 'a1 0 '(sp))
0038 0111 ($addi 'sp 'sp 4)
003a 8533 ($mul 'a0 'a1 'a0)
003c 02a5
003e 0505 ($addi 'a0 'a0 1)
0040 8082 ($ret)
Trying it out:
> (rec 12)
1302061345
This example demonstrates how the RISC-V Lisp assembler takes advantage of 16-bit compact instructions where possible, instead of the equivalent full 32-bit instructions.