7 ms·
Show HN: A small virtual machine written in C
- tekknolagi 12y agoI have been working on this project on and off for about 6 months. I would love feedback on the code, design - whatever!
- dsturnbull2049 12y agoVery concise implementation. I keep thinking there's missing files or at least I'm missing something in my understanding. :) I wrote a 0-operand VM in C a few years ago that used a lot of the same concepts but considerably more code, like 10x at least. I will learn a lot from this.
- tekknolagi 12y agoWhat kinds of files look missing? I'm curious. What is a 0 operand VM? I'd love to see it!
- chewxy 12y agoUsing only PUSH and POP I think. i.e a stack machine. also, OP, your code looks great.
- tekknolagi 12y agoOh interesting. That sounds... complicated. Thanks! It's my first big C project that isn't hideous :D
- dsturnbull2049 12y agoYeah, a stack machine. https://github.com/dsturnbull/stack_cpu https://github.com/dsturnbull/stack_cpu What I mean by missing files is that it's so clean that at first glance it didn't look like the whole deal :)
- tekknolagi 12y agoOh well thanks — that's quite a compliment. This is pretty cool!
- Hytosys 12y agoJust for anyone looking for more information: http://en.wikipedia.org/wiki/Instruction_set#Number_of_operands http://en.wikipedia.org/wiki/Instruction_set#Number_of_opera...
- tekknolagi 12y agoAwesome!
- listic 12y agoUnfortunately, with the ever-increasing processor-memory gap (the growing discrepancy between processor and memory speeds, also called Memory Wall, if I'm not mistaken), 0-operand, or stack VM's are seeing ever-increasing performance penalties.
- ghkbrew 12y agoIs there a reason for the combination of register-based (logical ops) and stack-based (arithmetic ops) operations? Otherwise fun project.
- tekknolagi 12y agoProbably just my sloppiness. Things like OR were implemented while looking at other projects like tinyvm, so it's not perfectly consistent.
- zura 12y agoNice. Any particular tutorial/article/book you're following?
- tekknolagi 12y agoNope :) Lots of small web resources.
- wsc981 12y agoPerhaps, if you're interested in learning to write well-written C code, you might be interested in the book "C Programming: A Modern Approach"[0] by King. Coding style is an important topic in his book. [0]: http://www.amazon.com/Programming-Modern-Approach-2nd-Edition/dp/0393979504 http://www.amazon.com/Programming-Modern-Approach-2nd-Editio...
- zura 12y agoThank you, seems very interesting. Usually, I only write pure C code when I really have to. Otherwise, I see no reason why someone should restrict "shimself" from using C++.
- papaf 12y agoThe code looks really clean to me. If you feel the urge to convert the VM to a JIT, rather than calling function pointers for each instruction, you might find this blog post useful: http://blog.reverberate.org/2012/12/hello-jit-world-joy-of-simple-jits.html http://blog.reverberate.org/2012/12/hello-jit-world-joy-of-s...
- tekknolagi 12y agoThanks! The goal was readability and ease of use, so that's probably why it's slow :P Not sure I understand. What would it do differently? Edit: Hang on, so I should generate C or asm? or something?
- TheHydroImpulse 12y agoYou'd essentially be generating machine code on the fly and letting the cpu execute it, instead of interpreting anything yourself.
- tekknolagi 12y agoInteresting. Is that significantly harder?
- TheHydroImpulse 12y agoIt does make it more complex since you now have another layer to deal with. I wouldn't roll by own JIT compiler though, otherwise it would definitely be a crap ton harder. Using something like libjit or llvm's JIT compiler are two good options. It's nice having a normal, interpreted VM then building a JIT compiler later on for extra speed. A recent Rust project called `js.rs`[0] started out with a simple interpreter and swapped it for a JIT compiler using libjit. The biggest difference being you now have to interface with the actual CPU and such. [0] https://github.com/TomBebbington/js.rs https://github.com/TomBebbington/js.rs
- chewxy 12y agoif you're already writing in C, no. Just write some functions to convert those instructions into asm, and write an assembler (or use one of the many out there) to output them to bytes. Then use mprotect(), tadah!
- akkartik 12y agoCan you give some directions for how to run it? $ git clone https://github.com/tekknolagi/carp $ make $ ./carp $ ./carp --help # after peeking in carp.c help msg There's a tantalizing target called 'tests' in the Makefile, but it doesn't work. There's some examples, but no makefile to build them. How do you run them? I see you used to have some .carp files but you just deleted them: https://github.com/tekknolagi/carp/commit/fa16eeb443 https://github.com/tekknolagi/carp/commit/fa16eeb443 I tried restoring one, but it doesn't work: $ git checkout fa16eeb443~1 examples/reg.carp $ ./carp -f examples/reg.carp Unknown label <add> So at this point I'm ready to give up..
- tekknolagi 12y agoHi there! Sorry for this. I actually have a help message but for some reason I forgot to commit it. It's coming soon! You run the examples like so: carp -f examples/carp/call.carp You can compile C files that use the Carp API, but I have not written any documentation yet.
- tekknolagi 12y agoOh that's weird. the carp folder from examples disappeared. Gimme a sec.
- tekknolagi 12y agoAlright, try now. Also — thanks for the pull request!
- akkartik 12y agoYou're welcome. I pulled the new commits, but I'm still having trouble running the examples: $ ./carp -f examples/carp/call.carp Unknown label <add> (I am very interested in building easy-to-understand codebases: http://akkartik.name/post/wart http://akkartik.name/post/wart)
- tekknolagi 12y ago
- axman6 12y agoSeems like [1] could benefit from use of the X macro [2], should make adding new instructions much easier and you can avoid the hassle of having to keep two separate tables in sync. There's probably quite a few places where the code could be made clearer by using it. Also in the implementation of your functions, there's a hell of a lot of repetition in all the binary operations, another macro which you pass in the operation and the function name would make life earier: #define binop(NAME, OP) definstr (NAME) { \ long long b, a; \ if (carp_stack_pop(&m->stack, &b) == -1)\ carp_vm_err(m, CARP_STACK_EMPTY);\ if (carp_stack_pop(&m->stack, &a) == -1)\ carp_vm_err(m, CARP_STACK_EMPTY);\ carp_stack_push(&m->stack, a OP b);} binop(ADD,+) binop(MUL,*) ... The repetition of if (carp_stack_pop(&m->stack, &a) == -1) carp_vm_err(m, CARP_STACK_EMPTY); seems like a good place to just use a function to encapsulate all the error checking and handling. [1] https://github.com/tekknolagi/carp/blob/master/src/carp_instructions.h https://github.com/tekknolagi/carp/blob/master/src/carp_inst... [2] http://www.embedded.com/design/programming-languages-and-tools/4403953/C-language-coding-errors-with-X-macros-Part-1 http://www.embedded.com/design/programming-languages-and-too... and the following parts
- tekknolagi 12y agoDefinitely interesting — I shall take a look! Feel free to make a pull request if you're game.
- axman6 12y agoIf I had time and a job where I got to code I would, but it'll be a few days before I have time.
- tekknolagi 12y agoAh, I feel you — no problem! I do really love the binop macro.
- tekknolagi 12y agoQuick question. Is that code you've given ok to use in the project? I just realized that I added and pushed without asking. GPL?
- panic 12y agoThe implementation is really clean! The mixing of general-purpose registers, special-purpose registers and the stack makes the instruction set a bit weird, though. For example, instead of using registers, why not have OR pick values off the stack like ADD, or vice versa? Why use EAX for the conditional jump instructions when you could look at the top of the stack? Why have REM when you can just MOV from ERX?
- tekknolagi 12y agoYou know, I was thinking about that this morning actually. All great suggestions! If you could make an issue, that would be fantastic.
- tekknolagi 12y agoI just changed OR/XOR/AND to be stack based, and good idea for the jump instructions.
- deleted 12y ago[deleted]
- tekknolagi 12y agoPardon?
- ourjs 12y agoI mean it's a big story, if you can do part of job of VMWare
- tekknolagi 12y agoThis is a totally different type of VM than VMWare makes. This is what's called a process virtual machine, and they make application virtual machines (I think). Basically, this acts as a small CPU-type-thing and interprets instructions. Theirs emulate whole operating systems.
- ourjs 12y agoSound more like container
- tekknolagi 12y agoMmm nope. Nothing about kernels in here.
- bronson 12y agoThink VM as in JVM.
- jsmeaton 12y agoIt's similar to the Java Virtual Machine (JVM) or the .NET CLR VM if you're familiar with either of those.
- deleted 12y ago[deleted]
- cobookman 12y agoVery cool! Georgia Tech offers a class where you code a VM for the LC3b instruction set (http://users.ece.gatech.edu/~moin/s13a/hw.html http://users.ece.gatech.edu/~moin/s13a/hw.html). It was a great learning experience, and pretty fun too.
- tekknolagi 12y agoAh, that's neat! I am not headed off to Georgia Tech though :)
- n1ghtmare_ 12y ago"NOP (): Does nothing. Seriously." - I love this :)
- tekknolagi 12y agoMe too :D
- n1ghtmare_ 12y agoGood job man, code looks tight, wish I had some more time to jump on this. Keep it up. EDIT: Btw, do you happen to have a book or some study material on this subject ? Anything you can recommend ?
- tekknolagi 12y agoWhenever you get a chance just send a stray pull request :) I don't - I wasn't really following anything unified. I just pulled together a bunch of crappy web resources :)
- manish_gill 12y agoCan anyone link me to some literature on VMs? Sure, I can analyse the code, but from a design perspective, I would love to learn more about how VMs work, where they can be used, etc etc.
- tekknolagi 12y agoSo I started off following this (http://en.wikibooks.org/wiki/Creating_a_Virtual_Machine/Register_VM_in_C http://en.wikibooks.org/wiki/Creating_a_Virtual_Machine/Regi...) which was interesting but clearly never super influential in my design (except in earlier stages, but that died). Then I read this: http://stackoverflow.com/questions/2034422/tutorial-resource-for-implementing-vm http://stackoverflow.com/questions/2034422/tutorial-resource... and this: http://courses.cms.caltech.edu/cs11/material/c/mike/lab8/lab8.html http://courses.cms.caltech.edu/cs11/material/c/mike/lab8/lab... and took a look at the MSP430's instruction set. As far as where they can be used... anywhere? I don't know the answer to your question - sorry :)
- manish_gill 12y agoThanks for all those links!
- VLM 12y ago"and took a look at the MSP430's instruction set." I wonder in a general sense if there are many VMs that explicitly copy good older CPUs. I've always thought the 6809 would make an awesome VM. After all, it was pretty awesome to code on in the real world. Or a VM based on the classic 1802, 6502, or Z80 with some minor mods. If you really want to warp peoples minds give them a VM based on IBM HAL assembler, basically turn Hercules and VM into a hypervisor rather than an application. A PDP-11 inspired/based virtual machine. Hmm.
- astrobe_ 12y agoCISC would be better than RISC because one should keep in mind that for many operation most of the execution time is VM overhead. But if you want performance you'd rather tailor your instruction set to the language it will execute (assuming you're not making a general purpose VM). CPU makers like Intel do actually keep a close look on what programs are doing in order to evolve their instruction set.
- lindig 12y agoAs inspiration, I would suggest to look at the virtual machine (byte code interpreter) of the Lua language. You can find several papers describing the design at http://www.lua.org/docs.html http://www.lua.org/docs.html. The code base is also very small and clean.
- tekknolagi 12y agoAwesome!
- zencoder 12y ago>The goal is to try and build a small (and decently reliable) VM from the ground up, learning more and more C as I go. The author explicitly mentions, he is in the process of learning C while he's building something using it. This is interesting because the folks at [osdev.org](http://wiki.osdev.org http://wiki.osdev.org) keep stressing that you must be a god-level expert in C, before you even think of getting into systems programming.
- TheSoftwareGuy 12y agoI think the guys at osdev mean you need to be good at C before doing systems programming at any form of a professional level. If you don't intend for your code to actually do anything (I mean this is just a side project or something, not for somebody to use in production) than you can really do whatever you want.
- tekknolagi 12y agoDefinitely a side project.
- wtetzner 12y agoI don't know about other people, but there's no way I'd be able to become an expert in a language without building things with it.
- jheriko 12y agogood stuff. the power of function pointers for stuff like this is quite enormous. making a vm like this is great practice for a real compiler too... :) i'm curious though, why it isn't a direct copy of x86? other than the obvious 'thats really complicated' it would save a lot of wheel reinventing...
- tekknolagi 12y agoI started off following MSP430 but just kind of wanted to make my own thing.
- abhorrence 12y agoThis is neat. For others interested in this sort of thing, https://challenge.synacor.com/ https://challenge.synacor.com/ specifies a virtual machine, and comes with a binary that performs a self-test -- it is perhaps a neat way to get started.
- tekknolagi 12y agoCool!
- ravyoli 12y agoOut of curiosity - How are you testing your VM? meaning, how do you know it actually works correctly? I didn't see any tests in the github repository
- tekknolagi 12y agoI am not testing — and I know this is horrifying. I need to write some tests. D:
- tekknolagi 12y agoI am now starting to write some tests with libtap. If you are game, come help out! :D