3.9 KiB
Goals
Be an efficent compilation target for typed lambda calculi
Be substantially easier to translate to than machine code.
Run cross-platform on as many operating systems and architectures as possible. (bare minimum 32-bit).
Allow for code completely independent of the host machine.
Leave memory managment out of the question for the compiler writer.
Provide significant IO functionality.
Provide efficient native methods for manipulating commonly used types
- Strings (don’t represent as a list of characters, god)
- Characters
- Integers
- Floating point numbers
- Raw bytes
- Lists (cons linked list), efficient map, foldr, and filter instructions would be nice
- Arrays (constant lookup continous memory)
Have methods for representing algebraic data types built into the machine.
- Sums and products of arbitrary size, and methods for (de)constructing them.
- Sums as tagged pairs since the machine is untyped
Instruction for tail recursion which takes the new parameters of the stack and resets the C pointer to where it last entered a closure.
Recursion instruction which functions as dup : app
GMP arithmetic
Some sort of pattern matching facility for matching on the first element of a pair, (the constructor).
Non-Goals
Safely error out or leave catchable exceptions when instruction is called with wrongful operands.
Dynamic linking, creating object files with polymorphic types.
Lazy evaluation.
Nicities (plausible)
Provide a compiler from a simply typed lambda calculus, coupled with a small standard library which would confirm type safety of all used instructions. Maybe go even further? System F?
Maybe some vector operations? You could reasonably operate on chunks of the stack.
Nicities (dreaming)
Provide more complete IO functionality, such as networking, etc. (this is not likely to happen)
Provide some sort of FFI (this is not likely to happen)
It’s possible one might be able to JIT compile some stuff?
Encoding stuff
Common instructions are 1 byte and a lookup table is used to run a function executing said instruction. Less common instructions will have the first byte take them to a function which reads the next byte, allowing for a tree of instructions.
All words realistically have to be the same size for the stack to make sense. Which size should be used? This feels like it could easily become machine dependant.
- Possibly go for the highest you have, 8 bytes? Would mean that performance would be god awful on 32 bit machines. I might be ok with that? Am I?
- What about byte types, etc? Should we really allow that? It becomes
a pain. Perhaps it is easiest to pretend computers do not exist and only
deal with numbers? Only use GMP arithmetic then?
- Possible set of builtin types:
- Int64 - 64-bit signed integer
- Word64 - 64-bit unsigned integer
- Float - 64-bit double precision floating point number
- Integer - GMP
- Natural - GMP
- Float - GMP
- Rational - GMP
- String - Like Data.Text
- Char - 64-bit unicode codepoint.
- List - Pointer to linked list
- Array - Pointer to length tagged constant memory
- Pair - Pointer to constant memory of length 2
- Possible set of builtin types:
File Format stuff
Much like ELF, start of with header containing basic info, reserve some bytes for future additions, have field specifing version. Then have sections.
Code section, the initial C stack.
Global section, the initial G env.
Linkable and Executable. Linkables have some sort of symbol table for wanted symbols, as well as a list of files it wishes to link with. The linker, or perhaps the VM, when loading, will then lookup these files in a global search path, perform substitution with the symbols to create an Executable file with a complete global table.
Must realise an encoding for closures, products and GMP numbers to be used in globals.