Hello, JIT World from D
This post is inspired by Hello, JIT World: The Joy of Simple JITs, especially the first short program that he used to demonstrate that JIT programming can be simple. In this post, I will convert the program he wrote in C to [D][wiki] to show some capabilities and convenience of D.
What follows is the whole source of the program in D. You can view the original source in C in the blog post linked above.
import std.c.string;
import std.c.linux.linux;
import std.conv;
import std.stdio;
int main(string[] args)
{
// Machine code for:
// mov eax, 0
// ret
ubyte code[] = [0xb8, 0x00, 0x00, 0x00, 0x00, 0xc3];
if (args.length < 2)
{
stderr.writeln("Usage: jit1 <integer>");
return 1;
}
// Overwrite immediate value "0" in the instruction
// with the user's value. This will make our code:
// mov eax, <user's value>
// ret
int num = to!int(args[1]);
memcpy(&code[1], &num, 4);
// Allocate writable/executable memory.
// Note: real programs should not map memory both writable
// and executable because it is a security risk.
void* mem = mmap(null, code.sizeof, PROT_WRITE | PROT_EXEC,
MAP_ANON | MAP_PRIVATE, -1, 0);
memcpy(mem, code, code.sizeof);
// The function will return the user's value.
int function() func = cast(int function())mem;
return func();
}
Let’s go through some first impressions.
D programming language is in the same family of languages that have the C flavor, so you might already find the code familiar and quite easy to read. Some familiarities are
- double forward slashes starts a line comment
- curly braces around a block of code
- every statement ends in a semicolon
- type goes before the identifier in a declaration
- all executions start from the
main
function which can have one of the following signatures
- and unlike C (or C++ for that matter), the D standard actually have
void main(...)
functions return 0 (which means success) to the shell. - indexing operator is
[]
- address-of operator is
&
- from the above example you can also see that there are pointer types in D and the syntax for declaring one is just like C’s
- there’s actually a
null
value for pointers, not just a cast from zero as in C - bitwise operators in D are the same as in C, e.g.
|
,&
and~
- you can cast from one time to another, which is not recommended unless you know exactly what you are doing as the safe subset of D is powerful enough for most applications, by doing
- syntax for returning from a function is
So D is quite similar to C/C++. But the differences that set D apart is even more interesting.
- there is a concept of modules that can
import
each other, so that a source file does not have to be processed so many times in compilation (well, there’s more to this but the reduction in compilation time that comes with this is, to me, the most significant benefit) - string is actually supported, not something that feels bolted on like in C (or C++)
- there’s no
unsigned
keyword. Most types have their unsigned counterparts named with au
prepended. - arrays know their own lengths, which can be accessed by doing
int[200] a;
writeln(a.length); // assuming you have `import std.stdio;` before this.
// Prints out 200.
to
into!int
is a generic function (and you specialize it using the bang-type syntax). The syntax, though unfamiliar, but greatly simplifies parsing, both for compilers and for humans.- C code can be called directly from D after creating declarations for it. The function
memcpy
andmmap
are C functions whose declarations are defined in packagesstring
andlinux
. - the size of some variable in bytes can be obtained from doing
variable.sizeof
- any pointer type is automatically convertible to
void*
, not so for the converse. - there is a
function
keyword so that the type of a function pointer or complex types can be declared without hurting your brain
// fp is a pointer to a function that takes a function taking a char
// returning an int and returns a typeless pointer
// (try saying that five times fast)
void* function(int function(char)) fp;
// the equivalent for that in C is
// void* (*fp)(int (*)(char));
// let's look at something more interesting
// signal is a function that takes an int and a pointer to a function
// taking an int returning nothing and returns a pointer to a function
// taking an int returning nothing
void function(int) signal(int sig, void function(int) func));
// the equivalent for that in C is
// void (*signal (int sig, void (*func)(int)) )(int);
// try reading that five times fast :)
Those are the differences that are present in this code snippet, you can check out more about D at the D home page or a summary at the wikipedia page.