An optimizing compiler for a lisp dialect targetting the uxn virtual machine.
Read a blog posts:
- https://krzysckh.org/b/Homegrown-closures-for-uxn.html
- https://krzysckh.org/b/Implementing-greener-threads-by-putting-uxn-in-your-uxn.html
The background image is called "uxn.crew.jpeg". As of my knowledge it was drawn by Rek Bell. Distributed by Devine Lu Linvega on wiki.xxiivv.com. Used under the BY-NC-SA4.0 License.
No followers yet
Once you ship this you can't edit the description of the project, but you'll be able to add more devlogs and re-ship it as you add new features!
added typing rules - a way to create types with value constraints,
consider this:
(define-type ZeroOrOne)
(define-typing-rules
(Number n is ZeroOrOne <=> (or
(eq? n 0)
(eq? n 1)))
(Bool is ZeroOrOne)
(ZeroOrOne is Number))
this creates a type ZeroOrOne, which will only be able to hold a value 0 or 1. other values should result in an error thrown by the compiler,
consider this function, right after the code i showed beforehand:
(define-signature f ZeroOrOne -> Number)
(define (f x) x)
it is basically a self or I function, but it does type conversion. since every ZeroOrOne is a Number, it can be freely converted that way, but because only Numbers 0 and 1 are ZeroOrOne, all invocations of f that are not (f 0) or (f 1) will error out. (i mean - they may error out, the typechecker is nowhere near good enough to do symbex to catch every single possible errors - it's a rather uncomplicated type checker)
the typechecker is now much smarter, now it can detect invalid return types of functions based on their body vs what signature was provided.
consider this:
(define-signature f Number -> String)
(define (f x)
x)
(define (main)
(f 10))
the compiler now is able to infer that f called as (f 10) will not return a string, but a number, and will throw a compile-time error
embed
macro, that embeds files as bytes,i used quite a lot of brain power on this
- added deeper typechecking (more on that later)
- added -M to test macroexpand/optimize combo
consider this:
```
(define (g x)
(string-append x x))
(define (f x)
(g x))
(define (main)
(f 1))
```
the typechecker is now able to throw an error at compile time saying it is impossible to call string-append at 1, as the type is Number, while String is required (see the screenshot)
begin
. it's now a builtin handled by the compiler,Added more type checking, it now accepts type signatures and will yield errors on invalid args passed to functions. Still a lot of work needs to be done on that, as right now it only can detect types if they are known in the exact place a function call takes place, so for example:
(define-signature f Number -> Number)
(f abc) ; <- this will error out
(let ((v abc))
(f v)) ; <- this will not error out, yet it should
Started experimenting with type checking. As of right now i can declare a functions' type and make sure that - when called as an argument - it will always return a value, as void functions as arguments cause ub (possibly stack underflows).
added command line arg support, which was tricky, so it works via a callback called by (call-with-command-line ...) which accepts a lambda of 1 arg which is a list of strings. i've fallen into many traps set out by myself from the past which included a shitty allocator that can be un-stable-d by writing a byte out of bounds. but hey, wcyd, at least it was fun---ish
(defvar foo 0 1) → (defvar foo 1), it still maps to alloc!, but with extra steps to en-sugar-ify the syntax
demo:
* added uxn5 as an emulator
* sent patches to uxn5 mailing list regarding invalid obj references
compiler:
* string=?
* cond
* arity checking
* include libraries at compile-time
* 'symbols
* structs, packed structs
* fix malloc (of course; again)
* fix lambda rewriting
* show perf data when compiler is called with -V
* add bunnymark & tests to the web demo
* add thread test (https://krzysckh.org/b/Implementing-greener-threads-by-putting-uxn-in-your-uxn.html)
* add TCE on top of TCO (delete function frame on tail non-recursive calls -- _tailcall! & _tailcall-fast!)
* generate symbol tables for debuggers with -s
* add constant folding for more operations
after fighting with clang --target=wasm32 for literal hours, writing lots of js simulating a real environment i was finally able to create a ghetto demo website demonstrating how the compiler works. i might refine it later. for now it can compile / disassemble files. It's just a web wasm frontend. If you can, please use the normal compiler.
Also i have spent a lot of time going through a weird bug just to realize my js bump malloc is not aligning pointers. big oops
added call-with-lines, uf2 font drawing routines, realloc, memcpy, strlen, varvara file IO (read-file, write-file), {key,button}-pressed, keypress vector, the compiler now alloc!s inline strings (there's no need to do that manually now), fixed dead code elimination for alloc! (it no longer deletes functions that are referenced only in _alloc! expressions). started working on a web demo
well i've worked much more than the counter shows here, just on a machine without hackatime. i've re-written the allocator because i found a fatal flaw that literally made it go boom on certain allocation sequences. i've added some utility functions, signed 16bit arithmetic emulation.
I also got heavily hackernews'd - my blog post was on the main page for some time, and my web server really struggled, as it's a tiny vps with like 300 megs of ram. in 3 hours i got twice as many requests as in the entirety of last year (only from stats from people without adblockers). feels kind of surreal.
what has been up to now:
- basic compilation phases & code generation
- some peephole optimizations
- tail call optimization
- closure generation (https://krzysckh.org/b/Homegrown-closures-for-uxn.html)
- runtime memory allocation (implemented malloc+free)
- variables as 1st class citizens (the compiler can recognise multiple label types)