1 /* Digital Mars DMDScript source code.
2  * Copyright (c) 2000-2002 by Chromium Communications
3  * D version Copyright (c) 2004-2010 by Digital Mars
4  * Distributed under the Boost Software License, Version 1.0.
5  * (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
6  * written by Walter Bright
7  * http://www.digitalmars.com
8  *
9  * D2 port by Dmitry Olshansky 
10  *
11  * DMDScript is implemented in the D Programming Language,
12  * http://www.digitalmars.com/d/
13  *
14  * For a C++ implementation of DMDScript, including COM support, see
15  * http://www.digitalmars.com/dscript/cppscript.html
16  */
17 
18 
19 module dmdscript.irstate;
20 
21 import core.stdc.stdarg;
22 import core.stdc.stdlib;
23 import core.stdc.string;
24 import dmdscript.outbuffer;
25 import core.memory;
26 import core.stdc.stdio;
27 
28 import std.stdio;
29 
30 import dmdscript.script;
31 import dmdscript.statement;
32 import dmdscript.opcodes;
33 import dmdscript.ir;
34 import dmdscript.identifier;
35 
36 // The state of the interpreter machine as seen by the code generator, not
37 // the interpreter.
38 
39 struct IRstate
40 {
41     import core.stdc.stdint : uintptr_t;
42     alias Op = uintptr_t;
43     static assert(Op.sizeof == IR.sizeof);
44 
45     OutBuffer      codebuf;             // accumulate code here
46     Statement      breakTarget;         // current statement that 'break' applies to
47     Statement      continueTarget;      // current statement that 'continue' applies to
48     ScopeStatement scopeContext;        // current ScopeStatement we're inside
49     uint[]         fixups;
50 
51     //void next();	// close out current Block, and start a new one
52 
53     uint locali = 1;            // leave location 0 as our "null"
54     uint nlocals = 1;
55 
56     void ctor()
57     {
58         codebuf = new OutBuffer();
59     }
60 
61     void validate()
62     {
63         assert(codebuf.offset <= codebuf.data.length);
64         if(codebuf.data.length > codebuf.data.capacity)
65             printf("ptr %p, length %d, capacity %d\n", codebuf.data.ptr, cast(uint)codebuf.data.length, cast(uint)core.memory.GC.sizeOf(codebuf.data.ptr));
66         assert(codebuf.data.length <= codebuf.data.capacity);
67         for(uint u = 0; u < codebuf.offset; )
68         {
69             IR* code = cast(IR*)(codebuf.data.ptr + u);
70             assert(code.opcode < IRMAX);
71             u += IR.size(code.opcode) * 4;
72         }
73     }
74 
75     /**********************************
76      * Allocate a block of local variables, and return an
77      * index to them.
78      */
79 
80     uint alloc(size_t nlocals)
81     {
82         uint n;
83 
84         assert(nlocals < uint.max);
85 
86         n = locali;
87         locali += nlocals;
88         if(locali > this.nlocals)
89             this.nlocals = locali;
90         assert(n);
91         return n * INDEX_FACTOR;
92     }
93 
94     /****************************************
95      * Release this block of n locals starting at local.
96      */
97 
98     void release(uint local, uint n)
99     {
100         /+
101             local /= INDEX_FACTOR;
102             if (local + n == locali)
103                 locali = local;
104          +/
105     }
106 
107     uint mark()
108     {
109         return locali;
110     }
111 
112     void release(uint i)
113     {
114         //locali = i;
115     }
116 
117     static Op combine(uint loc, uint opcode)
118     {
119         static if (Op.sizeof == uint.sizeof)
120             return (loc << 16) | opcode;
121         else return cast(ulong)loc << 32 | opcode;
122     }
123 
124     /***************************************
125      * Generate code.
126      */
127 
128     void genX(Args...)(Loc loc, uint opcode, Args args)
129     {
130         import std.algorithm.comparison : max;
131 
132         foreach (i, A; Args)
133             static assert(A.sizeof == 4 || A.sizeof == 8);
134 
135         enum s = (1 + opCount!Args) * Op.sizeof;
136         codebuf.reserve(s);
137 
138         auto data = cast(Op *)(codebuf.data.ptr + codebuf.offset);
139         codebuf.offset += s;
140         data[0] = combine(loc, opcode);
141         foreach (i, A; Args) {
142             enum off = 1 + opCount!(Args[0 .. i]);
143             static if (A.sizeof == 4) {
144                 data[off] = *(cast(uint*)&args[i]);
145             } else static if (A.sizeof == Op.sizeof) {
146                 data[off] = *(cast(Op*)&args[i]);
147             } else {
148                 data[off] = (cast(Op*)&args[i])[0];
149                 data[off+1] = (cast(Op*)&args[i])[1];
150             }
151         }
152     }
153 
154     private static template opCount(T...) {
155         static if (T.length == 0) enum opCount = 0;
156         else enum opCount = (T[0].sizeof <= Op.sizeof ? 1 : T[0].sizeof/Op.sizeof) + opCount!(T[1 .. $]);
157     }
158 
159     static assert(opCount!() == 0);
160     static assert(opCount!(uint) == 1);
161     static assert(opCount!(ushort) == 1);
162     static assert(opCount!(void*) == 1);
163     static assert(opCount!(uint, uint, double) == 2 + double.sizeof/Op.sizeof);
164 
165     void gen0(Loc loc, uint opcode)
166     {
167         codebuf.write(combine(loc, opcode));
168     }
169 
170     void gen1(Loc loc, uint opcode, Op arg)
171     {
172         codebuf.reserve(2 * Op.sizeof);
173         version(all)
174         {
175             // Inline ourselves for speed (compiler doesn't do a good job)
176             auto data = cast(Op *)(codebuf.data.ptr + codebuf.offset);
177             codebuf.offset += 2 * Op.sizeof;
178             data[0] = combine(loc, opcode);
179             data[1] = arg;
180         }
181         else
182         {
183             codebuf.write4n(combine(loc, opcode));
184             codebuf.write4n(arg);
185         }
186     }
187 
188     void gen2(Loc loc, uint opcode, Op arg1, Op arg2)
189     {
190         codebuf.reserve(3 * Op.sizeof);
191         version(all)
192         {
193             // Inline ourselves for speed (compiler doesn't do a good job)
194             auto data = cast(Op *)(codebuf.data.ptr + codebuf.offset);
195             codebuf.offset += 3 * Op.sizeof;
196             data[0] = combine(loc, opcode);
197             data[1] = arg1;
198             data[2] = arg2;
199         }
200         else
201         {
202             codebuf.write4n(combine(loc, opcode));
203             codebuf.write4n(arg1);
204             codebuf.write4n(arg2);
205         }
206     }
207 
208     void gen3(Loc loc, uint opcode, Op arg1, Op arg2, Op arg3)
209     {
210         codebuf.reserve(4 * Op.sizeof);
211         version(all)
212         {
213             // Inline ourselves for speed (compiler doesn't do a good job)
214             auto data = cast(Op *)(codebuf.data.ptr + codebuf.offset);
215             codebuf.offset += 4 * Op.sizeof;
216             data[0] = combine(loc, opcode);
217             data[1] = arg1;
218             data[2] = arg2;
219             data[3] = arg3;
220         }
221         else
222         {
223             codebuf.write4n(combine(loc, opcode));
224             codebuf.write4n(arg1);
225             codebuf.write4n(arg2);
226             codebuf.write4n(arg3);
227         }
228     }
229 
230     void gen4(Loc loc, uint opcode, Op arg1, Op arg2, Op arg3, Op arg4)
231     {
232         codebuf.reserve(5 * Op.sizeof);
233         version(all)
234         {
235             // Inline ourselves for speed (compiler doesn't do a good job)
236             auto data = cast(Op *)(codebuf.data.ptr + codebuf.offset);
237             codebuf.offset += 5 * Op.sizeof;
238             data[0] = combine(loc, opcode);
239             data[1] = arg1;
240             data[2] = arg2;
241             data[3] = arg3;
242             data[4] = arg4;
243         }
244         else
245         {
246             codebuf.write4n(combine(loc, opcode));
247             codebuf.write4n(arg1);
248             codebuf.write4n(arg2);
249             codebuf.write4n(arg3);
250             codebuf.write4n(arg4);
251         }
252     }
253 
254     void pops(uint npops)
255     {
256         while(npops--)
257             gen0(0, IRpop);
258     }
259 
260     /******************************
261      * Get the current "instruction pointer"
262      */
263 
264     uint getIP()
265     {
266         if(!codebuf)
267             return 0;
268         return codebuf.offset / Op.sizeof;
269     }
270 
271     /******************************
272      * Patch a value into the existing codebuf.
273      */
274 
275     void patchJmp(uint index, uint value)
276     {
277         assert((index + 1) * Op.sizeof < codebuf.offset);
278         (cast(Op*)(codebuf.data))[index + 1] = value - index;
279     }
280 
281     /*******************************
282      * Add this IP to list of jump instructions to patch.
283      */
284 
285     void addFixup(uint index)
286     {
287         fixups ~= index;
288     }
289 
290     /*******************************
291      * Go through the list of fixups and patch them.
292      */
293 
294     void doFixups()
295     {
296         uint i;
297         uint index;
298         uint value;
299         Statement s;
300 
301         for(i = 0; i < fixups.length; i++)
302         {
303             index = fixups[i];
304             assert((index + 1) * Op.sizeof < codebuf.offset);
305             s = (cast(Statement *)codebuf.data)[index + 1];
306             value = s.getTarget();
307             patchJmp(index, value);
308         }
309     }
310 
311 
312     void optimize()
313     {
314         // Determine the length of the code array
315         IR *c;
316         IR *c2;
317         IR *code;
318         size_t length;
319         uint i;
320 
321         code = cast(IR *)codebuf.data;
322         for(c = code; c.opcode != IRend; c += IR.size(c.opcode))
323         {
324         }
325         length = c - code + 1;
326 
327         // Allocate a bit vector for the array
328         byte[] b = new byte[length]; //TODO: that was a bit array, maybe should use std.container
329 
330         // Set bit for each target of a jump
331         for(c = code; c.opcode != IRend; c += IR.size(c.opcode))
332         {
333             switch(c.opcode)
334             {
335             case IRjf:
336             case IRjt:
337             case IRjfb:
338             case IRjtb:
339             case IRjmp:
340             case IRjlt:
341             case IRjle:
342             case IRjltc:
343             case IRjlec:
344             case IRtrycatch:
345             case IRtryfinally:
346             case IRnextscope:
347             case IRnext:
348             case IRnexts:
349                 //writefln("set %d", (c - code) + (c + 1).offset);
350                 b[(c - code) + (c + 1).offset] = true;
351                 break;
352             default:
353                 break;
354             }
355         }
356 
357         // Allocate array of IR contents for locals.
358         IR*[] local;
359         IR*[] p1 = null;
360 
361         // Allocate on stack for smaller arrays
362         IR** plocals;
363         if(nlocals < 128)
364             plocals = cast(IR * *)alloca(nlocals * local[0].sizeof);
365 
366         if(plocals)
367         {
368             local = plocals[0 .. nlocals];
369             local[] = null;
370         }
371         else
372         {
373             p1 = new IR *[nlocals];
374             local = p1;
375         }
376 
377         // Optimize
378         for(c = code; c.opcode != IRend; c += IR.size(c.opcode))
379         {
380             size_t offset = (c - code);
381 
382             if(b[offset])       // if target of jump
383             {
384                 // Reset contents of locals
385                 local[] = null;
386             }
387 
388             switch(c.opcode)
389             {
390             case IRnop:
391                 break;
392 
393             case IRnumber:
394             case IRstring:
395             case IRboolean:
396                 local[(c + 1).index / INDEX_FACTOR] = c;
397                 break;
398 
399             case IRadd:
400             case IRsub:
401             case IRcle:
402                 local[(c + 1).index / INDEX_FACTOR] = c;
403                 break;
404 
405             case IRputthis:
406                 local[(c + 1).index / INDEX_FACTOR] = c;
407                 goto Lreset;
408 
409             case IRputscope:
410                 local[(c + 1).index / INDEX_FACTOR] = c;
411                 break;
412 
413             case IRgetscope:
414             {
415                 Identifier* cs = (c + 2).id;
416                 IR *cimax = null;
417                 for(i = nlocals; i--; )
418                 {
419                     IR *ci = local[i];
420                     if(ci &&
421                        (ci.opcode == IRgetscope || ci.opcode == IRputscope) &&
422                        (ci + 2).id.value..string == cs.value..string
423                        )
424                     {
425                         if(cimax)
426                         {
427                             if(cimax < ci)
428                                 cimax = ci;     // select most recent instruction
429                         }
430                         else
431                             cimax = ci;
432                     }
433                 }
434                 if(1 && cimax)
435                 {
436                     //writef("IRgetscope . IRmov %d, %d\n", (c + 1).index, (cimax + 1).index);
437                     c.opcode = IRmov;
438                     (c + 2).index = (cimax + 1).index;
439                     local[(c + 1).index / INDEX_FACTOR] = cimax;
440                 }
441                 else
442                     local[(c + 1).index / INDEX_FACTOR] = c;
443                 break;
444             }
445 
446             case IRnew:
447                 local[(c + 1).index / INDEX_FACTOR] = c;
448                 goto Lreset;
449 
450             case IRcallscope:
451             case IRputcall:
452             case IRputcalls:
453             case IRputcallscope:
454             case IRputcallv:
455             case IRcallv:
456                 local[(c + 1).index / INDEX_FACTOR] = c;
457                 goto Lreset;
458 
459             case IRmov:
460                 local[(c + 1).index / INDEX_FACTOR] = local[(c + 2).index / INDEX_FACTOR];
461                 break;
462 
463             case IRput:
464             case IRpostincscope:
465             case IRaddassscope:
466                 goto Lreset;
467 
468             case IRjf:
469             case IRjfb:
470             case IRjtb:
471             case IRjmp:
472             case IRjt:
473             case IRret:
474             case IRjlt:
475             case IRjle:
476             case IRjltc:
477             case IRjlec:
478                 break;
479 
480             default:
481                 Lreset:
482                 // Reset contents of locals
483                 local[] = null;
484                 break;
485             }
486         }
487 
488         destroy(p1);
489 
490         //return;
491         // Remove all IRnop's
492         for(c = code; c.opcode != IRend; )
493         {
494             size_t offset;
495             size_t o;
496             size_t c2off;
497 
498             if(c.opcode == IRnop)
499             {
500                 offset = (c - code);
501                 for(c2 = code; c2.opcode != IRend; c2 += IR.size(c2.opcode))
502                 {
503                     switch(c2.opcode)
504                     {
505                     case IRjf:
506                     case IRjt:
507                     case IRjfb:
508                     case IRjtb:
509                     case IRjmp:
510                     case IRjlt:
511                     case IRjle:
512                     case IRjltc:
513                     case IRjlec:
514                     case IRnextscope:
515                     case IRtryfinally:
516                     case IRtrycatch:
517                         c2off = c2 - code;
518                         o = c2off + (c2 + 1).offset;
519                         if(c2off <= offset && offset < o)
520                             (c2 + 1).offset--;
521                         else if(c2off > offset && o <= offset)
522                             (c2 + 1).offset++;
523                         break;
524                     /+
525                                         case IRtrycatch:
526                                             o = (c2 + 1).offset;
527                                             if (offset < o)
528                                                 (c2 + 1).offset--;
529                                             break;
530                      +/
531                     default:
532                         continue;
533                     }
534                 }
535 
536                 length--;
537                 memmove(c, c + 1, (length - offset) * uint.sizeof);
538             }
539             else
540                 c += IR.size(c.opcode);
541         }
542     }
543 }