ChubakPDP11+TakeWithGrainOfSalt

  • 58 Posts
  • 101 Comments
Joined 5 months ago
cake
Cake day: February 10th, 2024

help-circle





























  • I think by ‘GADTs’ you mean an AST (could be mistaken). In that case, it would not be a bytecode interpreter, it would be a tree walker. In most languages, an AST is translated down to bytecode, and passed to a VM execution engine. How this engine deals with closures is highly compliant on the architecture of the AST.

    First, keep this in mind: A closure is just a closure at the higher-level. Job of the AST is to translate it down to ta more abstract form, thus, a closure would most probably be inlined. Otherwise, it will be a global subroutine defined somewhere on the stack. It never makes sense not to inline closures, unless that closure is a ‘hotzone’, e.g. frequently used, in whcih case you should most obviously define as a subroutine, and if possible, JIT.

    A VM lke NekoVM has a higher-order, terse IR with which you can handle closures like you do in JS.

    Don’t conflate higher-order constructs with intermediate representations. If you are interested to see a VM development in progress, star my RuppVM. Still early though. But i tend to work on it actively.

    Thanks.


  • Are you specifying everything beforehand? If not, I’d recommend locking in on an ISA with stack effect pre-determined. Also, minimize as much as you can.

    First off: Read Xia-Feng Li’s excellent book if you have not.

    Then.

    Here are some minimization tips I am doing for my RuppVM. I just began working on it less that 24 hours ago. But the tips are based on over 6 months of off-and-on trying to make a VM, and failing. I am sure this one will take, because of these minimization efforts:

    • Everything is a word: When you are reading the bytecode stream, read word-by-word, and by word I mean machine word. Don’t waste your time on floats, you can implement IEEE-745 floats as a module, it will be good for practice. There’s a good book for it here.
    • No complex datatypes, BLESS!: So I said everything is a word, what about arrays? Structs? Basically, you need to ‘bless’ chunks. Blessing means taking a routine and turning it into a structure, see: Perl.
    • No OS-level threads, BIND!: Just make ‘green’ threads, as I am doing in RuppVM. You can use the FFI to bind them to OS threads.
    • Stop the World GC + Arena Allocation: Don’t waste time on intricate GC, Just do stop-the-world mark and sweep, on arena-allocated memory (see my code).
    • Super Basic FFI: Take a look at my ISA.txt file, look at what I am doing for FFI. You don’t need intricate type mappings for the FFI. Just register a ‘hook’ to an address in memory (or ELF).
    • Avoid JiT/AoT for now: Don’t focus on it at the beginning.

    These variables are not exactly portable, but you can use them, abuse them, etc:

    • extern etext -> First address past the text segment;
    • extern edata -> First address past the initialized data segment;
    • extern end -> end of bss

    I think there are portable libraries for handling ELFs and PEs. You could also write your own Assembly code for it. For loading functions from files, for the FFI, these help a lot.

    Another aspect you should implement is a ‘signal trampoline’. This trampoline will help handle signals from the OS, and hook it to your green threads.

    Now, don’t take these as gospel. I am sure you know most of these already. But in case there’s new info, feel free to adapt it.

    Star RuppVM, I will be adding a lot of stuff to it soon.

    EDIT: I looked, there does not seem to be any ‘portable’ libraries for handling PE and ELF with the same interface. I guess ther can’t be, two different beasts.

    EDIT 2: The FFI could prove to be much more complex than I thought? There’s libffi though.