Sonntag, 17. Juli 2016

CTFE - My first design sketches

Hi,

I just found my very first design sketches for the CTFE IR

I will post them here verbatim,

for no particular reason other then amusement :)



---

//int bug6498(int x) { int n = 0; while (n < x) ++n; return n; }
// static assert(bug6498(100_000_000)==10_000_000);

i32 bug5498 (i32 $a1) {
//LOCALS
i32 $l1;
START :
str 0, $l1;

lbl while_cond :
cmp $l1 $a1;
jlt after_while; // conditional jump  a< b if less then in prevoius cmp;
inc $l1;
jmp while_cond;
lbl after_while :
ret $l1;
}

>> resolving labels

1 str 0, $l1;
2 cmp $l1 $a1;
4 clt 2; // conditional block less then next 2 instruction are executed if cmp true otherwise execute 2+1 instruction;
3 inc $l1;
5 jmp 2; // jumps use absoulte addresses;
lbl after_while :
6 ret { $l1 } ;


struct Adder {
uint[4] a;
opBinary (string op : "+")(uint c) {
foreach(i;0 .. a.length) {
a[i] += c;
}
}
}
uint[4] fn(uint s) {
Adder adr = Addr([2,3,4,5]);
return addr + s;
}

///
typedef Adder = [i32, i32, i32, i32] ;
//stack is a hardcoded property of the function;
all arguments are written into the reserved bytes before the first instruction
caller passes it's call location to the callee
callee saves results under the call;
callee jumps behinds it's results
[i32, i32, i32, i32] fn1 (i32 $a1) {
// push constant and push varible a diffrent
sts fn2;// set stack
psc 2;
psc 3;
psc 4;
psc 5;
psv $a1;
jmp fn2;
result[1]
result[2]
result[3]
result[4]
ret ;
}
// again caller tells the callee where to write the result;
// one regsiter is needed to hold the current return-address
// and one ip of course;


[i32, i32, i32, i32] fn2 ([i32, i32, i32, i32]$a[1-4], i32 $a5) {
add $a5, $a1;
add $a5, $a2;
add $a5, $a3;
add $a5, $a4;
ret ; //maybe ret is going to be optional
}

// values before the ret are the return value...

--

Donnerstag, 7. Juli 2016

Design new CTFE engine - A preview

Hello,

I have been working on a rewrite of the CTFE interpreter in D.

As D is a complicated language with many constructs and complicated corner-cases that have to be handled correctly, I did not want to rip out the old Interpreter entirely.

Rather I am hooking the engine in at a point where the lowered function is evaluated and see if my engine can evaluate it.
If it can it will do so and return the result.
If it cannot it will give back a null value which signals to the old Interpreter to  take over instead.

This way I can incrementally improve CTFE and still provide a fully functioning compiler.

The AST is already processed into a canonical from when it arrives at the point where I hooked the evaluator in.

This means it is in backend friendlier form.
And much of the syntactic sugar is already removed.

I am translating this canonicalized AST into linear byte-code and hand it off to a byte-code interpreter.

The byte-code itself is almost completely decoupled from the part that processes the AST, thus making it possible to provide different interpretation backends, and to change the byte-code-format at any point without having to rewrite the AST processing.

Thus I keep the byte-code API fairly clean and minimal.

currently there are 15 calls the generator/evaluator backend has to support

Lt3
Gt3
Eq3


Add3
Sub3
Mul3
Div3

And3
Or3
Lsh3
Rsh3

JmpZ
JmpNZ
JmpTrue
JmpFalse


These are enough to represent a large body of code.
And easy implementations should be possible for most of the targets out there.

However I suspect that I will need add at least

Call
and
Return

In order to support function calls.

I will also need to find and document all assumptions I make about the capability  of a backend.

For example it has to support 32bit integer operations. It also has to be able to work with 32bit aligned data.