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 }