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