| | 1 | #ifdef RCSID |
| | 2 | static char RCSid[] = |
| | 3 | "$Header: d:/cvsroot/tads/tads3/tct3.cpp,v 1.5 1999/07/11 00:46:58 MJRoberts Exp $"; |
| | 4 | #endif |
| | 5 | |
| | 6 | /* |
| | 7 | * Copyright (c) 1999, 2002 Michael J. Roberts. All Rights Reserved. |
| | 8 | * |
| | 9 | * Please see the accompanying license file, LICENSE.TXT, for information |
| | 10 | * on using and copying this software. |
| | 11 | */ |
| | 12 | /* |
| | 13 | Name |
| | 14 | tct3.cpp - TADS 3 Compiler - T3 VM Code Generator |
| | 15 | Function |
| | 16 | Generate code for the T3 VM |
| | 17 | Notes |
| | 18 | |
| | 19 | Modified |
| | 20 | 05/08/99 MJRoberts - Creation |
| | 21 | */ |
| | 22 | |
| | 23 | #include <stdio.h> |
| | 24 | #include <assert.h> |
| | 25 | |
| | 26 | #include "t3std.h" |
| | 27 | #include "os.h" |
| | 28 | #include "tcprs.h" |
| | 29 | #include "tct3.h" |
| | 30 | #include "tcgen.h" |
| | 31 | #include "vmtype.h" |
| | 32 | #include "vmwrtimg.h" |
| | 33 | #include "vmfile.h" |
| | 34 | #include "tcmain.h" |
| | 35 | #include "tcerr.h" |
| | 36 | #include "vmbignum.h" |
| | 37 | #include "vmrunsym.h" |
| | 38 | #include "tct3unas.h" |
| | 39 | |
| | 40 | |
| | 41 | /* ------------------------------------------------------------------------ */ |
| | 42 | /* |
| | 43 | * T3 Target Code Generator class |
| | 44 | */ |
| | 45 | |
| | 46 | /* |
| | 47 | * initialize the code generator |
| | 48 | */ |
| | 49 | CTcGenTarg::CTcGenTarg() |
| | 50 | { |
| | 51 | int i; |
| | 52 | |
| | 53 | /* |
| | 54 | * we haven't written any instructions yet - fill the pipe with |
| | 55 | * no-op instructions so that we don't think we can combine the |
| | 56 | * first instruction with anything previous |
| | 57 | */ |
| | 58 | last_op_ = OPC_NOP; |
| | 59 | second_last_op_ = OPC_NOP; |
| | 60 | |
| | 61 | /* |
| | 62 | * we haven't seen any strings or lists yet - set the initial |
| | 63 | * maximum lengths to zero |
| | 64 | */ |
| | 65 | max_str_len_ = 0; |
| | 66 | max_list_cnt_ = 0; |
| | 67 | |
| | 68 | /* we haven't generated any code yet */ |
| | 69 | max_bytecode_len_ = 0; |
| | 70 | |
| | 71 | /* there are no metaclasses defined yet */ |
| | 72 | meta_head_ = meta_tail_ = 0; |
| | 73 | meta_cnt_ = 0; |
| | 74 | |
| | 75 | /* no function sets defined yet */ |
| | 76 | fnset_head_ = fnset_tail_ = 0; |
| | 77 | fnset_cnt_ = 0; |
| | 78 | |
| | 79 | /* |
| | 80 | * Add the built-in metaclass entries. The order of these entries |
| | 81 | * is fixed to match the TCT3_METAID_xxx constants - if this order |
| | 82 | * changes, those constants must change to match. |
| | 83 | */ |
| | 84 | add_meta("tads-object"); |
| | 85 | add_meta("list"); |
| | 86 | add_meta("dictionary2/030000"); |
| | 87 | add_meta("grammar-production/030000"); |
| | 88 | add_meta("vector"); |
| | 89 | add_meta("anon-func-ptr"); |
| | 90 | add_meta("int-class-mod/030000"); |
| | 91 | |
| | 92 | /* |
| | 93 | * Set up the initial translation table for translating from our |
| | 94 | * internal metaclass index values - TCT3_METAID_xxx - to the *actual* |
| | 95 | * image file dependency index. When we're compiling a program, these |
| | 96 | * are simply in our own internal order - index N in our scheme is |
| | 97 | * simply index N in the actual image file - because we get to lay out |
| | 98 | * exactly what the dependency table looks like. However, we need the |
| | 99 | * translation table for when we're being used as part of the debugger |
| | 100 | * - in those cases, we don't get to dictate the dependency table |
| | 101 | * layout, because the image file (and thus the dependency table) might |
| | 102 | * have been generated by a different version of the compiler. In |
| | 103 | * those cases, the image file loader will have to set up the |
| | 104 | * translation information for us. |
| | 105 | */ |
| | 106 | for (i = 0 ; i <= TCT3_METAID_LAST ; ++i) |
| | 107 | predef_meta_idx_[i] = i; |
| | 108 | |
| | 109 | /* start at the first valid property ID */ |
| | 110 | next_prop_ = 1; |
| | 111 | |
| | 112 | /* start at the first valid object ID */ |
| | 113 | next_obj_ = 1; |
| | 114 | |
| | 115 | /* allocate an initial sort buffer */ |
| | 116 | sort_buf_size_ = 4096; |
| | 117 | sort_buf_ = (char *)t3malloc(sort_buf_size_); |
| | 118 | |
| | 119 | /* not in a constructor */ |
| | 120 | in_constructor_ = FALSE; |
| | 121 | |
| | 122 | /* no debug line record pointers yet */ |
| | 123 | debug_line_cnt_ = 0; |
| | 124 | debug_line_head_ = debug_line_tail_ = 0; |
| | 125 | |
| | 126 | /* normal (non-debug) evaluation mode */ |
| | 127 | eval_for_debug_ = FALSE; |
| | 128 | speculative_ = FALSE; |
| | 129 | debug_stack_level_ = 0; |
| | 130 | |
| | 131 | /* no multi-method initializer object yet */ |
| | 132 | mminit_obj_ = VM_INVALID_OBJ; |
| | 133 | } |
| | 134 | |
| | 135 | /* |
| | 136 | * delete the code generator |
| | 137 | */ |
| | 138 | CTcGenTarg::~CTcGenTarg() |
| | 139 | { |
| | 140 | /* delete all of the metaclass list entries */ |
| | 141 | while (meta_head_ != 0) |
| | 142 | { |
| | 143 | tc_meta_entry *nxt; |
| | 144 | |
| | 145 | /* remember the next item */ |
| | 146 | nxt = meta_head_->nxt; |
| | 147 | |
| | 148 | /* delete this item */ |
| | 149 | t3free(meta_head_); |
| | 150 | |
| | 151 | /* move on */ |
| | 152 | meta_head_ = nxt; |
| | 153 | } |
| | 154 | |
| | 155 | /* delete all of the function set list entries */ |
| | 156 | while (fnset_head_ != 0) |
| | 157 | { |
| | 158 | tc_fnset_entry *nxt; |
| | 159 | |
| | 160 | /* remember the next item */ |
| | 161 | nxt = fnset_head_->nxt; |
| | 162 | |
| | 163 | /* delete this item */ |
| | 164 | t3free(fnset_head_); |
| | 165 | |
| | 166 | /* move on */ |
| | 167 | fnset_head_ = nxt; |
| | 168 | } |
| | 169 | |
| | 170 | /* delete our sort buffer */ |
| | 171 | t3free(sort_buf_); |
| | 172 | |
| | 173 | /* delete the debug line record pointers */ |
| | 174 | while (debug_line_head_ != 0) |
| | 175 | { |
| | 176 | tct3_debug_line_page *nxt; |
| | 177 | |
| | 178 | /* remember the next one */ |
| | 179 | nxt = debug_line_head_->nxt; |
| | 180 | |
| | 181 | /* delete this one */ |
| | 182 | delete debug_line_head_; |
| | 183 | |
| | 184 | /* move on */ |
| | 185 | debug_line_head_ = nxt; |
| | 186 | } |
| | 187 | } |
| | 188 | |
| | 189 | /* start loading the image file metaclass dependency table */ |
| | 190 | void CTcGenTarg::start_image_file_meta_table() |
| | 191 | { |
| | 192 | int i; |
| | 193 | |
| | 194 | /* |
| | 195 | * clear out all of the entries - set them to -1 to indicate that |
| | 196 | * they're invalid |
| | 197 | */ |
| | 198 | for (i = 0 ; i <= TCT3_METAID_LAST ; ++i) |
| | 199 | predef_meta_idx_[i] = -1; |
| | 200 | } |
| | 201 | |
| | 202 | /* |
| | 203 | * Load an image file metaclass dependency table. When we're being used in |
| | 204 | * a debugger, the image loader must call this with the run-time dependency |
| | 205 | * table to establish the translation from our internal metaclass |
| | 206 | * identifiers (TCT3_METAID_xxx) to the actual run-time index values. |
| | 207 | */ |
| | 208 | void CTcGenTarg::load_image_file_meta_table( |
| | 209 | const char *nm, size_t len, int idx) |
| | 210 | { |
| | 211 | /* |
| | 212 | * Check the name against our known names. If it matches one of the |
| | 213 | * known types, store the actual index in our translation table under |
| | 214 | * our internal index entry. If it's not a known name, ignore it - we |
| | 215 | * only care about the names we actually pre-define, because those are |
| | 216 | * the only ones we need to generate our own code for. |
| | 217 | */ |
| | 218 | if (len == 11 && memcmp(nm, "tads-object", 11) == 0) |
| | 219 | predef_meta_idx_[TCT3_METAID_TADSOBJ] = idx; |
| | 220 | else if (len == 4 && memcmp(nm, "list", 4) == 0) |
| | 221 | predef_meta_idx_[TCT3_METAID_LIST] = idx; |
| | 222 | else if (len == 11 && memcmp(nm, "dictionary2", 11) == 0) |
| | 223 | predef_meta_idx_[TCT3_METAID_DICT] = idx; |
| | 224 | else if (len == 18 && memcmp(nm, "grammar-production", 18) == 0) |
| | 225 | predef_meta_idx_[TCT3_METAID_GRAMPROD] = idx; |
| | 226 | else if (len == 6 && memcmp(nm, "vector", 6) == 0) |
| | 227 | predef_meta_idx_[TCT3_METAID_VECTOR] = idx; |
| | 228 | else if (len == 13 && memcmp(nm, "anon-func-ptr", 13) == 0) |
| | 229 | predef_meta_idx_[TCT3_METAID_ANONFN] = idx; |
| | 230 | else if (len == 13 && memcmp(nm, "int-class-mod", 13) == 0) |
| | 231 | predef_meta_idx_[TCT3_METAID_ICMOD] = idx; |
| | 232 | } |
| | 233 | |
| | 234 | /* |
| | 235 | * End the image file metaclass dependency table. |
| | 236 | */ |
| | 237 | void CTcGenTarg::end_image_file_meta_table() |
| | 238 | { |
| | 239 | int i; |
| | 240 | |
| | 241 | /* |
| | 242 | * Scan the metaclass translation table and make sure they've all been |
| | 243 | * set. If any are unset, it means that these metaclasses aren't |
| | 244 | * available; since we depend upon these metaclasses, this means that |
| | 245 | * we can't generate code interactively, so we can't act as a debugger. |
| | 246 | */ |
| | 247 | for (i = 0 ; i <= TCT3_METAID_LAST ; ++i) |
| | 248 | { |
| | 249 | /* if this entry hasn't been set properly, abort */ |
| | 250 | if (predef_meta_idx_[i] == -1) |
| | 251 | err_throw(VMERR_IMAGE_INCOMPAT_VSN_DBG); |
| | 252 | } |
| | 253 | } |
| | 254 | |
| | 255 | /* |
| | 256 | * Add an entry to the metaclass dependency table |
| | 257 | */ |
| | 258 | int CTcGenTarg::add_meta(const char *nm, size_t len, |
| | 259 | CTcSymMetaclass *sym) |
| | 260 | { |
| | 261 | tc_meta_entry *ent; |
| | 262 | size_t extra_len; |
| | 263 | const char *extra_ptr; |
| | 264 | const char *p; |
| | 265 | size_t rem; |
| | 266 | |
| | 267 | /* |
| | 268 | * if the name string doesn't contain a slash, allocate enough space |
| | 269 | * to add an implied version suffix of "/000000" |
| | 270 | */ |
| | 271 | for (p = nm, rem = len ; rem != 0 && *p != '/' ; ++p, --rem) ; |
| | 272 | if (rem == 0) |
| | 273 | { |
| | 274 | /* we didn't find a version suffix - add space for one */ |
| | 275 | extra_len = 7; |
| | 276 | extra_ptr = "/000000"; |
| | 277 | } |
| | 278 | else |
| | 279 | { |
| | 280 | /* |
| | 281 | * there's already a version suffix - but make sure we have |
| | 282 | * space for a six-character string |
| | 283 | */ |
| | 284 | if (rem < 7) |
| | 285 | { |
| | 286 | /* add zeroes to pad out to a six-place version string */ |
| | 287 | extra_len = 7 - rem; |
| | 288 | extra_ptr = "000000"; |
| | 289 | } |
| | 290 | else |
| | 291 | { |
| | 292 | /* we need nothing extra */ |
| | 293 | extra_len = 0; |
| | 294 | extra_ptr = 0; |
| | 295 | } |
| | 296 | } |
| | 297 | |
| | 298 | /* allocate a new entry for the item */ |
| | 299 | ent = (tc_meta_entry *)t3malloc(sizeof(tc_meta_entry) + len + extra_len); |
| | 300 | if (ent == 0) |
| | 301 | err_throw(TCERR_CODEGEN_NO_MEM); |
| | 302 | |
| | 303 | /* copy the name into the entry */ |
| | 304 | memcpy(ent->nm, nm, len); |
| | 305 | |
| | 306 | /* add any extra version suffix information */ |
| | 307 | if (extra_len != 0) |
| | 308 | memcpy(ent->nm + len, extra_ptr, extra_len); |
| | 309 | |
| | 310 | /* null-terminate the name string in the entry */ |
| | 311 | ent->nm[len + extra_len] = '\0'; |
| | 312 | |
| | 313 | /* remember the symbol */ |
| | 314 | ent->sym = sym; |
| | 315 | |
| | 316 | /* link the entry in at the end of the list */ |
| | 317 | ent->nxt = 0; |
| | 318 | if (meta_tail_ != 0) |
| | 319 | meta_tail_->nxt = ent; |
| | 320 | else |
| | 321 | meta_head_ = ent; |
| | 322 | meta_tail_ = ent; |
| | 323 | |
| | 324 | /* count the entry, returning the index of the entry in the list */ |
| | 325 | return meta_cnt_++; |
| | 326 | } |
| | 327 | |
| | 328 | /* |
| | 329 | * Find a metaclass index given the global identifier. |
| | 330 | */ |
| | 331 | tc_meta_entry *CTcGenTarg::find_meta_entry(const char *nm, size_t len, |
| | 332 | int update_vsn, int *entry_idx) |
| | 333 | { |
| | 334 | tc_meta_entry *ent; |
| | 335 | int idx; |
| | 336 | size_t name_len; |
| | 337 | const char *p; |
| | 338 | const char *vsn; |
| | 339 | size_t vsn_len; |
| | 340 | size_t rem; |
| | 341 | |
| | 342 | /* find the version suffix, if any */ |
| | 343 | for (rem = len, p = nm ; rem != 0 && *p != '/' ; --rem, ++p) ; |
| | 344 | |
| | 345 | /* note the length of the name portion (up to the '/') */ |
| | 346 | name_len = len - rem; |
| | 347 | |
| | 348 | /* note the version string, if there is one */ |
| | 349 | if (rem != 0) |
| | 350 | { |
| | 351 | vsn = p + 1; |
| | 352 | vsn_len = rem - 1; |
| | 353 | } |
| | 354 | else |
| | 355 | { |
| | 356 | vsn = 0; |
| | 357 | vsn_len = 0; |
| | 358 | } |
| | 359 | |
| | 360 | /* search the existing entries */ |
| | 361 | for (idx = 0, ent = meta_head_ ; ent != 0 ; ent = ent->nxt, ++idx) |
| | 362 | { |
| | 363 | size_t ent_name_len; |
| | 364 | char *ent_vsn; |
| | 365 | |
| | 366 | /* find the version suffix in the entry */ |
| | 367 | for (ent_vsn = ent->nm ; *ent_vsn != '\0' && *ent_vsn != '/' ; |
| | 368 | ++ent_vsn) ; |
| | 369 | |
| | 370 | /* the name is the part up to the '/' */ |
| | 371 | ent_name_len = ent_vsn - ent->nm; |
| | 372 | |
| | 373 | /* note the length of the name and the version suffix */ |
| | 374 | if (*ent_vsn == '/') |
| | 375 | { |
| | 376 | /* the version is what follows the '/' */ |
| | 377 | ++ent_vsn; |
| | 378 | } |
| | 379 | else |
| | 380 | { |
| | 381 | /* there is no version suffix */ |
| | 382 | ent_vsn = 0; |
| | 383 | } |
| | 384 | |
| | 385 | /* if this is the one, return it */ |
| | 386 | if (ent_name_len == name_len && memcmp(ent->nm, nm, name_len) == 0) |
| | 387 | { |
| | 388 | /* |
| | 389 | * if this version number is higher than the version number |
| | 390 | * we previously recorded, remember the new, higher version |
| | 391 | * number |
| | 392 | */ |
| | 393 | if (update_vsn && ent_vsn != 0 && strlen(ent_vsn) == vsn_len |
| | 394 | && memcmp(vsn, ent_vsn, vsn_len) > 0) |
| | 395 | { |
| | 396 | /* store the new version string */ |
| | 397 | memcpy(ent_vsn, vsn, vsn_len); |
| | 398 | } |
| | 399 | |
| | 400 | /* tell the caller the index, and return the entry */ |
| | 401 | *entry_idx = idx; |
| | 402 | return ent; |
| | 403 | } |
| | 404 | } |
| | 405 | |
| | 406 | /* we didn't find it */ |
| | 407 | return 0; |
| | 408 | } |
| | 409 | |
| | 410 | |
| | 411 | /* |
| | 412 | * Find a metaclass symbol given the global identifier |
| | 413 | */ |
| | 414 | class CTcSymMetaclass *CTcGenTarg::find_meta_sym(const char *nm, size_t len) |
| | 415 | { |
| | 416 | tc_meta_entry *ent; |
| | 417 | int idx; |
| | 418 | |
| | 419 | /* find the entry */ |
| | 420 | ent = find_meta_entry(nm, len, TRUE, &idx); |
| | 421 | |
| | 422 | /* |
| | 423 | * if we found it, return the associated metaclass symbol; if |
| | 424 | * there's no entry, there's no symbol |
| | 425 | */ |
| | 426 | if (ent != 0) |
| | 427 | return ent->sym; |
| | 428 | else |
| | 429 | return 0; |
| | 430 | } |
| | 431 | |
| | 432 | |
| | 433 | /* |
| | 434 | * Find or add a metaclass entry |
| | 435 | */ |
| | 436 | int CTcGenTarg::find_or_add_meta(const char *nm, size_t len, |
| | 437 | CTcSymMetaclass *sym) |
| | 438 | { |
| | 439 | tc_meta_entry *ent; |
| | 440 | int idx; |
| | 441 | |
| | 442 | /* find the entry */ |
| | 443 | ent = find_meta_entry(nm, len, TRUE, &idx); |
| | 444 | |
| | 445 | /* if we found it, return the index */ |
| | 446 | if (ent != 0) |
| | 447 | { |
| | 448 | /* |
| | 449 | * found it - if it didn't already have a symbol mapping, use |
| | 450 | * the new symbol; if there is a symbol in the table entry |
| | 451 | * already, however, do not change it |
| | 452 | */ |
| | 453 | if (ent->sym == 0) |
| | 454 | ent->sym = sym; |
| | 455 | |
| | 456 | /* return the index */ |
| | 457 | return idx; |
| | 458 | } |
| | 459 | |
| | 460 | /* we didn't find an existing entry - add a new one */ |
| | 461 | return add_meta(nm, len, sym); |
| | 462 | } |
| | 463 | |
| | 464 | /* |
| | 465 | * Get the symbol for a given metaclass dependency table entry |
| | 466 | */ |
| | 467 | CTcSymMetaclass *CTcGenTarg::get_meta_sym(int meta_idx) |
| | 468 | { |
| | 469 | tc_meta_entry *ent; |
| | 470 | |
| | 471 | /* find the list entry at the given index */ |
| | 472 | for (ent = meta_head_ ; ent != 0 && meta_idx != 0 ; |
| | 473 | ent = ent->nxt, --meta_idx) ; |
| | 474 | |
| | 475 | /* if we didn't find the entry, do nothing */ |
| | 476 | if (ent == 0 || meta_idx != 0) |
| | 477 | return 0; |
| | 478 | |
| | 479 | /* return this entry's symbol */ |
| | 480 | return ent->sym; |
| | 481 | } |
| | 482 | |
| | 483 | /* |
| | 484 | * Get the external name for the given metaclass index |
| | 485 | */ |
| | 486 | const char *CTcGenTarg::get_meta_name(int meta_idx) const |
| | 487 | { |
| | 488 | tc_meta_entry *ent; |
| | 489 | |
| | 490 | /* find the list entry at the given index */ |
| | 491 | for (ent = meta_head_ ; ent != 0 && meta_idx != 0 ; |
| | 492 | ent = ent->nxt, --meta_idx) ; |
| | 493 | |
| | 494 | /* if we didn't find the entry, do nothing */ |
| | 495 | if (ent == 0 || meta_idx != 0) |
| | 496 | return 0; |
| | 497 | |
| | 498 | /* return this entry's external name */ |
| | 499 | return ent->nm; |
| | 500 | } |
| | 501 | |
| | 502 | /* |
| | 503 | * Set the symbol for a given metaclass dependency table entry |
| | 504 | */ |
| | 505 | void CTcGenTarg::set_meta_sym(int meta_idx, class CTcSymMetaclass *sym) |
| | 506 | { |
| | 507 | tc_meta_entry *ent; |
| | 508 | |
| | 509 | /* find the list entry at the given index */ |
| | 510 | for (ent = meta_head_ ; ent != 0 && meta_idx != 0 ; |
| | 511 | ent = ent->nxt, --meta_idx) ; |
| | 512 | |
| | 513 | /* if we didn't find the entry, do nothing */ |
| | 514 | if (ent == 0 || meta_idx != 0) |
| | 515 | return; |
| | 516 | |
| | 517 | /* set this entry's symbol */ |
| | 518 | ent->sym = sym; |
| | 519 | } |
| | 520 | |
| | 521 | /* |
| | 522 | * Add an entry to the function set dependency table |
| | 523 | */ |
| | 524 | int CTcGenTarg::add_fnset(const char *nm, size_t len) |
| | 525 | { |
| | 526 | tc_fnset_entry *ent; |
| | 527 | const char *sl; |
| | 528 | size_t sl_len; |
| | 529 | size_t idx; |
| | 530 | |
| | 531 | /* find the version part of the new name, if present */ |
| | 532 | for (sl = nm, sl_len = len ; sl_len != 0 && *sl != '/' ; ++sl, --sl_len) ; |
| | 533 | |
| | 534 | /* look for an existing entry with the same name prefix */ |
| | 535 | for (idx = 0, ent = fnset_head_ ; ent != 0 ; ent = ent->nxt, ++idx) |
| | 536 | { |
| | 537 | char *ent_sl; |
| | 538 | |
| | 539 | /* find the version part of this entry's name, if present */ |
| | 540 | for (ent_sl = ent->nm ; *ent_sl != '\0' && *ent_sl != '/' ; |
| | 541 | ++ent_sl) ; |
| | 542 | |
| | 543 | /* check to see if the prefixes match */ |
| | 544 | if (ent_sl - ent->nm == sl - nm |
| | 545 | && memcmp(ent->nm, nm, sl - nm) == 0) |
| | 546 | { |
| | 547 | /* |
| | 548 | * This one matches. Keep the one with the higher version |
| | 549 | * number. If one has a version number and the other doesn't, |
| | 550 | * keep the one with the version number. |
| | 551 | */ |
| | 552 | if (*ent_sl == '/' && sl_len != 0) |
| | 553 | { |
| | 554 | /* |
| | 555 | * Both have version numbers - keep the higher version. |
| | 556 | * Limit the version length to 6 characters plus the |
| | 557 | * slash. |
| | 558 | */ |
| | 559 | if (sl_len > 7) |
| | 560 | sl_len = 7; |
| | 561 | |
| | 562 | /* check if the new version number is higher */ |
| | 563 | if (memcmp(sl, ent_sl, sl_len) > 0) |
| | 564 | { |
| | 565 | /* the new one is higher - copy it over the old one */ |
| | 566 | memcpy(ent_sl, sl, sl_len); |
| | 567 | } |
| | 568 | |
| | 569 | /* |
| | 570 | * in any case, we're going to keep the existing entry, so |
| | 571 | * we're done - just return the existing entry's index |
| | 572 | */ |
| | 573 | return idx; |
| | 574 | } |
| | 575 | else if (*ent_sl == '/') |
| | 576 | { |
| | 577 | /* |
| | 578 | * only the old entry has a version number, so keep it and |
| | 579 | * ignore the new definition - this means we're done, so |
| | 580 | * just return the existing item's index |
| | 581 | */ |
| | 582 | return idx; |
| | 583 | } |
| | 584 | else |
| | 585 | { |
| | 586 | /* |
| | 587 | * Only the new entry has a version number, so store the |
| | 588 | * new version number. To do this, simply copy the new |
| | 589 | * entry over the old entry, but limit the version number |
| | 590 | * field to 7 characters including the slash. |
| | 591 | */ |
| | 592 | if (sl_len > 7) |
| | 593 | len -= (sl_len - 7); |
| | 594 | |
| | 595 | /* copy the new value */ |
| | 596 | memcpy(ent->nm, nm, len); |
| | 597 | |
| | 598 | /* done - return the existing item's index */ |
| | 599 | return idx; |
| | 600 | } |
| | 601 | } |
| | 602 | } |
| | 603 | |
| | 604 | /* |
| | 605 | * Allocate a new entry for the item. Always allocate space for a |
| | 606 | * version number, even if the entry doesn't have a version number - |
| | 607 | * if the part from the slash on is 7 characters or more, add nothing, |
| | 608 | * else add enough to pad it out to seven characters. |
| | 609 | */ |
| | 610 | ent = (tc_fnset_entry *)t3malloc(sizeof(tc_fnset_entry) + len |
| | 611 | + (sl_len < 7 ? 7 - sl_len : 0)); |
| | 612 | if (ent == 0) |
| | 613 | err_throw(TCERR_CODEGEN_NO_MEM); |
| | 614 | |
| | 615 | /* copy the name into the entry */ |
| | 616 | memcpy(ent->nm, nm, len); |
| | 617 | ent->nm[len] = '\0'; |
| | 618 | |
| | 619 | /* link the entry in at the end of the list */ |
| | 620 | ent->nxt = 0; |
| | 621 | if (fnset_tail_ != 0) |
| | 622 | fnset_tail_->nxt = ent; |
| | 623 | else |
| | 624 | fnset_head_ = ent; |
| | 625 | fnset_tail_ = ent; |
| | 626 | |
| | 627 | /* count the entry, returning the index of the entry in the list */ |
| | 628 | return fnset_cnt_++; |
| | 629 | } |
| | 630 | |
| | 631 | /* |
| | 632 | * get a function set's name given its index |
| | 633 | */ |
| | 634 | const char *CTcGenTarg::get_fnset_name(int idx) const |
| | 635 | { |
| | 636 | tc_fnset_entry *ent; |
| | 637 | |
| | 638 | /* scan the linked list to find the given index */ |
| | 639 | for (ent = fnset_head_ ; idx != 0 && ent != 0 ; ent = ent->nxt, --idx) ; |
| | 640 | |
| | 641 | /* return the one we found */ |
| | 642 | return ent->nm; |
| | 643 | } |
| | 644 | |
| | 645 | /* |
| | 646 | * Determine if we can skip an opcode because it is unreachable from the |
| | 647 | * previous instruction. |
| | 648 | */ |
| | 649 | int CTcGenTarg::can_skip_op() |
| | 650 | { |
| | 651 | /* |
| | 652 | * if the previous instruction was a return or throw of some kind, |
| | 653 | * we can skip any subsequent opcodes until a label is defined |
| | 654 | */ |
| | 655 | switch(last_op_) |
| | 656 | { |
| | 657 | case OPC_RET: |
| | 658 | case OPC_RETVAL: |
| | 659 | case OPC_RETTRUE: |
| | 660 | case OPC_RETNIL: |
| | 661 | case OPC_THROW: |
| | 662 | case OPC_JMP: |
| | 663 | case OPC_LRET: |
| | 664 | /* it's a return, throw, or jump - this new op is unreachable */ |
| | 665 | return TRUE; |
| | 666 | |
| | 667 | default: |
| | 668 | /* this new op is reachable */ |
| | 669 | return FALSE; |
| | 670 | } |
| | 671 | } |
| | 672 | |
| | 673 | /* |
| | 674 | * Remove the last JMP instruction |
| | 675 | */ |
| | 676 | void CTcGenTarg::remove_last_jmp() |
| | 677 | { |
| | 678 | /* a JMP instruction is three bytes long, so back up three bytes */ |
| | 679 | G_cs->dec_ofs(3); |
| | 680 | } |
| | 681 | |
| | 682 | /* |
| | 683 | * Add a line record |
| | 684 | */ |
| | 685 | void CTcGenTarg::add_line_rec(CTcTokFileDesc *file, long linenum) |
| | 686 | { |
| | 687 | /* include line records only in debug mode */ |
| | 688 | if (G_debug) |
| | 689 | { |
| | 690 | /* |
| | 691 | * clear the peephole, to ensure that the line boundary isn't |
| | 692 | * blurred by code optimization |
| | 693 | */ |
| | 694 | clear_peephole(); |
| | 695 | |
| | 696 | /* add the record to the code stream */ |
| | 697 | G_cs->add_line_rec(file, linenum); |
| | 698 | } |
| | 699 | } |
| | 700 | |
| | 701 | /* |
| | 702 | * Write an opcode to the output stream. We'll watch for certain |
| | 703 | * combinations of opcodes being generated, and apply peephole |
| | 704 | * optimization when we see sequences that can be collapsed to more |
| | 705 | * efficient single instructions. |
| | 706 | */ |
| | 707 | void CTcGenTarg::write_op(uchar opc) |
| | 708 | { |
| | 709 | int prv_len; |
| | 710 | int op_len; |
| | 711 | |
| | 712 | /* write the new opcode byte to the output stream */ |
| | 713 | G_cs->write((char)opc); |
| | 714 | |
| | 715 | /* we've only written one byte so far for the current instruction */ |
| | 716 | op_len = 1; |
| | 717 | |
| | 718 | /* presume the previous instruction length is just one byte */ |
| | 719 | prv_len = 1; |
| | 720 | |
| | 721 | /* |
| | 722 | * check for pairs of instructions that we can reduce to more |
| | 723 | * efficient single instructions |
| | 724 | */ |
| | 725 | try_combine: |
| | 726 | switch(opc) |
| | 727 | { |
| | 728 | case OPC_JF: |
| | 729 | /* |
| | 730 | * if the last instruction was a comparison, we can use the |
| | 731 | * opposite compare-and-jump instruction |
| | 732 | */ |
| | 733 | switch(last_op_) |
| | 734 | { |
| | 735 | case OPC_NOT: |
| | 736 | /* invert the sense of the test */ |
| | 737 | opc = OPC_JT; |
| | 738 | goto combine; |
| | 739 | |
| | 740 | combine: |
| | 741 | /* |
| | 742 | * delete the new opcode we wrote, since we're going to combine |
| | 743 | * it with the preceding opcode |
| | 744 | */ |
| | 745 | G_cs->dec_ofs(op_len); |
| | 746 | |
| | 747 | /* overwrite the preceding opcode with the new combined opcode */ |
| | 748 | G_cs->write_at(G_cs->get_ofs() - prv_len, opc); |
| | 749 | |
| | 750 | /* roll back our internal peephole */ |
| | 751 | last_op_ = second_last_op_; |
| | 752 | second_last_op_ = OPC_NOP; |
| | 753 | |
| | 754 | /* |
| | 755 | * we've deleted our own opcode, so the current (most recent) |
| | 756 | * instruction in the output stream has the length of the |
| | 757 | * current opcode |
| | 758 | */ |
| | 759 | op_len = prv_len; |
| | 760 | |
| | 761 | /* presume the previous opcode is one byte again */ |
| | 762 | prv_len = 1; |
| | 763 | |
| | 764 | /* |
| | 765 | * go back for another try, since we may be able to do a |
| | 766 | * three-way combination (for example, GT/NOT/JT would |
| | 767 | * change to GT/JF, which would in turn change to JLE) |
| | 768 | */ |
| | 769 | goto try_combine; |
| | 770 | |
| | 771 | case OPC_EQ: |
| | 772 | opc = OPC_JNE; |
| | 773 | goto combine; |
| | 774 | |
| | 775 | case OPC_NE: |
| | 776 | opc = OPC_JE; |
| | 777 | goto combine; |
| | 778 | |
| | 779 | case OPC_LT: |
| | 780 | opc = OPC_JGE; |
| | 781 | goto combine; |
| | 782 | |
| | 783 | case OPC_LE: |
| | 784 | opc = OPC_JGT; |
| | 785 | goto combine; |
| | 786 | |
| | 787 | case OPC_GT: |
| | 788 | opc = OPC_JLE; |
| | 789 | goto combine; |
| | 790 | |
| | 791 | case OPC_GE: |
| | 792 | opc = OPC_JLT; |
| | 793 | goto combine; |
| | 794 | |
| | 795 | case OPC_GETR0: |
| | 796 | opc = OPC_JR0F; |
| | 797 | goto combine; |
| | 798 | } |
| | 799 | break; |
| | 800 | |
| | 801 | case OPC_JE: |
| | 802 | /* |
| | 803 | * if we just pushed nil, convert the PUSHNIL + JE to JNIL, since |
| | 804 | * we simply want to jump if a value is nil |
| | 805 | */ |
| | 806 | if (last_op_ == OPC_PUSHNIL) |
| | 807 | { |
| | 808 | /* convert it to a jump-if-nil */ |
| | 809 | opc = OPC_JNIL; |
| | 810 | goto combine; |
| | 811 | } |
| | 812 | break; |
| | 813 | |
| | 814 | case OPC_JNE: |
| | 815 | /* if we just pushed nil, convert to JNOTNIL */ |
| | 816 | if (last_op_ == OPC_PUSHNIL) |
| | 817 | { |
| | 818 | /* convert to jump-if-not-nil */ |
| | 819 | opc = OPC_JNOTNIL; |
| | 820 | goto combine; |
| | 821 | } |
| | 822 | break; |
| | 823 | |
| | 824 | case OPC_JT: |
| | 825 | /* |
| | 826 | * if the last instruction was a comparison, we can use a |
| | 827 | * compare-and-jump instruction |
| | 828 | */ |
| | 829 | switch(last_op_) |
| | 830 | { |
| | 831 | case OPC_NOT: |
| | 832 | /* invert the sense of the test */ |
| | 833 | opc = OPC_JF; |
| | 834 | goto combine; |
| | 835 | |
| | 836 | case OPC_EQ: |
| | 837 | opc = OPC_JE; |
| | 838 | goto combine; |
| | 839 | |
| | 840 | case OPC_NE: |
| | 841 | opc = OPC_JNE; |
| | 842 | goto combine; |
| | 843 | |
| | 844 | case OPC_LT: |
| | 845 | opc = OPC_JLT; |
| | 846 | goto combine; |
| | 847 | |
| | 848 | case OPC_LE: |
| | 849 | opc = OPC_JLE; |
| | 850 | goto combine; |
| | 851 | |
| | 852 | case OPC_GT: |
| | 853 | opc = OPC_JGT; |
| | 854 | goto combine; |
| | 855 | |
| | 856 | case OPC_GE: |
| | 857 | opc = OPC_JGE; |
| | 858 | goto combine; |
| | 859 | |
| | 860 | case OPC_GETR0: |
| | 861 | opc = OPC_JR0T; |
| | 862 | goto combine; |
| | 863 | } |
| | 864 | break; |
| | 865 | |
| | 866 | case OPC_NOT: |
| | 867 | /* |
| | 868 | * If the previous instruction was a comparison test of some |
| | 869 | * kind, we can invert the sense of the test. If the previous |
| | 870 | * instruction was a BOOLIZE op, we can eliminate it entirely, |
| | 871 | * because the NOT will perform the same conversion before |
| | 872 | * negating the value. If the previous was a NOT, we're |
| | 873 | * inverting an inversion; we can simply perform a single |
| | 874 | * BOOLIZE to get the same effect. |
| | 875 | */ |
| | 876 | switch(last_op_) |
| | 877 | { |
| | 878 | case OPC_EQ: |
| | 879 | opc = OPC_NE; |
| | 880 | goto combine; |
| | 881 | |
| | 882 | case OPC_NE: |
| | 883 | opc = OPC_EQ; |
| | 884 | goto combine; |
| | 885 | |
| | 886 | case OPC_GT: |
| | 887 | opc = OPC_LE; |
| | 888 | goto combine; |
| | 889 | |
| | 890 | case OPC_GE: |
| | 891 | opc = OPC_LT; |
| | 892 | goto combine; |
| | 893 | |
| | 894 | case OPC_LT: |
| | 895 | opc = OPC_GE; |
| | 896 | goto combine; |
| | 897 | |
| | 898 | case OPC_LE: |
| | 899 | opc = OPC_GT; |
| | 900 | goto combine; |
| | 901 | |
| | 902 | case OPC_BOOLIZE: |
| | 903 | opc = OPC_NOT; |
| | 904 | goto combine; |
| | 905 | |
| | 906 | case OPC_NOT: |
| | 907 | opc = OPC_BOOLIZE; |
| | 908 | goto combine; |
| | 909 | } |
| | 910 | break; |
| | 911 | |
| | 912 | case OPC_RET: |
| | 913 | /* |
| | 914 | * If we're writing a return instruction immediately after |
| | 915 | * another return instruction, we can skip the additional |
| | 916 | * instruction, since it will never be reached. This case |
| | 917 | * typically arises only when we generate the catch-all RET |
| | 918 | * instruction at the end of a function. |
| | 919 | */ |
| | 920 | switch(last_op_) |
| | 921 | { |
| | 922 | case OPC_RET: |
| | 923 | case OPC_RETVAL: |
| | 924 | case OPC_RETNIL: |
| | 925 | case OPC_RETTRUE: |
| | 926 | /* simply suppress this additional RET instruction */ |
| | 927 | return; |
| | 928 | } |
| | 929 | break; |
| | 930 | |
| | 931 | case OPC_RETNIL: |
| | 932 | /* we don't need to write two RETNIL's in a row */ |
| | 933 | if (last_op_ == OPC_RETNIL) |
| | 934 | return; |
| | 935 | break; |
| | 936 | |
| | 937 | case OPC_RETTRUE: |
| | 938 | /* we don't need to write two RETTRUE's in a row */ |
| | 939 | if (last_op_ == OPC_RETTRUE) |
| | 940 | return; |
| | 941 | break; |
| | 942 | |
| | 943 | case OPC_RETVAL: |
| | 944 | /* check the last opcode */ |
| | 945 | switch(last_op_) |
| | 946 | { |
| | 947 | case OPC_GETR0: |
| | 948 | /* |
| | 949 | * if we just pushed R0 onto the stack, we can compress the |
| | 950 | * GETR0 + RETVAL sequence into a simple RET, since RET leaves |
| | 951 | * the R0 value unchanged |
| | 952 | */ |
| | 953 | opc = OPC_RET; |
| | 954 | goto combine; |
| | 955 | |
| | 956 | case OPC_PUSHNIL: |
| | 957 | /* PUSHNIL + RET can be converted to RETNIL */ |
| | 958 | opc = OPC_RETNIL; |
| | 959 | goto combine; |
| | 960 | |
| | 961 | case OPC_PUSHTRUE: |
| | 962 | /* PUSHTRUE + RET can be converted to RETTRUE */ |
| | 963 | opc = OPC_RETTRUE; |
| | 964 | goto combine; |
| | 965 | } |
| | 966 | break; |
| | 967 | |
| | 968 | case OPC_SETLCL1: |
| | 969 | /* we can combine this with a preceding GETR0 */ |
| | 970 | if (last_op_ == OPC_GETR0) |
| | 971 | { |
| | 972 | /* generate a combined SETLCL1R0 */ |
| | 973 | opc = OPC_SETLCL1R0; |
| | 974 | goto combine; |
| | 975 | } |
| | 976 | break; |
| | 977 | |
| | 978 | case OPC_GETPROP: |
| | 979 | /* check the previous instruction for combination possibilities */ |
| | 980 | switch(last_op_) |
| | 981 | { |
| | 982 | case OPC_GETLCL1: |
| | 983 | /* get property of one-byte-addressable local */ |
| | 984 | opc = OPC_GETPROPLCL1; |
| | 985 | |
| | 986 | /* overwrite the preceding two-byte instruction */ |
| | 987 | prv_len = 2; |
| | 988 | goto combine; |
| | 989 | |
| | 990 | case OPC_GETR0: |
| | 991 | /* get property of R0 */ |
| | 992 | opc = OPC_GETPROPR0; |
| | 993 | goto combine; |
| | 994 | } |
| | 995 | break; |
| | 996 | |
| | 997 | case OPC_CALLPROP: |
| | 998 | /* check the previous instruction */ |
| | 999 | switch(last_op_) |
| | 1000 | { |
| | 1001 | case OPC_GETR0: |
| | 1002 | /* call property of R0 */ |
| | 1003 | opc = OPC_CALLPROPR0; |
| | 1004 | goto combine; |
| | 1005 | } |
| | 1006 | break; |
| | 1007 | |
| | 1008 | case OPC_INDEX: |
| | 1009 | /* we can combine small integer constants with INDEX */ |
| | 1010 | switch(last_op_) |
| | 1011 | { |
| | 1012 | case OPC_PUSH_0: |
| | 1013 | case OPC_PUSH_1: |
| | 1014 | /* |
| | 1015 | * We can combine these into IDXINT8, but we must write an |
| | 1016 | * extra byte for the index value. Go back and plug in the |
| | 1017 | * extra index value byte, and add another byte at the end of |
| | 1018 | * the stream to compensate for the insertion. (We're just |
| | 1019 | * going to remove and overwrite everything after the inserted |
| | 1020 | * byte, so don't bother actually fixing up that part with real |
| | 1021 | * data; we merely need to make sure we have the right number |
| | 1022 | * of bytes in the stream.) |
| | 1023 | */ |
| | 1024 | G_cs->write_at(G_cs->get_ofs() - 1, |
| | 1025 | last_op_ == OPC_PUSH_0 ? 0 : 1); |
| | 1026 | G_cs->write(0); |
| | 1027 | |
| | 1028 | /* combine the instructions */ |
| | 1029 | opc = OPC_IDXINT8; |
| | 1030 | prv_len = 2; |
| | 1031 | goto combine; |
| | 1032 | |
| | 1033 | case OPC_PUSHINT8: |
| | 1034 | /* combine the PUSHINT8 + INDEX into IDXINT8 */ |
| | 1035 | opc = OPC_IDXINT8; |
| | 1036 | prv_len = 2; |
| | 1037 | goto combine; |
| | 1038 | } |
| | 1039 | break; |
| | 1040 | |
| | 1041 | case OPC_IDXINT8: |
| | 1042 | /* we can replace GETLCL1 + IDXINT8 with IDXLCL1INT8 */ |
| | 1043 | if (last_op_ == OPC_GETLCL1) |
| | 1044 | { |
| | 1045 | uchar idx; |
| | 1046 | |
| | 1047 | /* rewrite the GETLCL1 to add the index operand */ |
| | 1048 | idx = G_cs->get_byte_at(G_cs->get_ofs() - 1); |
| | 1049 | G_cs->write_at(G_cs->get_ofs() - 2, idx); |
| | 1050 | |
| | 1051 | /* add another byte to compensate for the insertion */ |
| | 1052 | G_cs->write(0); |
| | 1053 | |
| | 1054 | /* go back and combine into what's now a three-byte opcode */ |
| | 1055 | opc = OPC_IDXLCL1INT8; |
| | 1056 | prv_len = 3; |
| | 1057 | goto combine; |
| | 1058 | } |
| | 1059 | break; |
| | 1060 | |
| | 1061 | case OPC_SETIND: |
| | 1062 | /* we can replace SETLCL1 + <small int> + SETIND with SETINDLCL1I8 */ |
| | 1063 | if (second_last_op_ == OPC_SETLCL1) |
| | 1064 | { |
| | 1065 | uchar idx; |
| | 1066 | |
| | 1067 | /* check the middle opcode */ |
| | 1068 | switch(last_op_) |
| | 1069 | { |
| | 1070 | case OPC_PUSHINT8: |
| | 1071 | /* |
| | 1072 | * go back and put the index value in the right spot in the |
| | 1073 | * third instruction back |
| | 1074 | */ |
| | 1075 | idx = G_cs->get_byte_at(G_cs->get_ofs() - 2); |
| | 1076 | G_cs->write_at(G_cs->get_ofs() - 3, idx); |
| | 1077 | |
| | 1078 | /* |
| | 1079 | * Go back and combine into what's now a 3-byte |
| | 1080 | * instruction: we'll remove the SETIND and the |
| | 1081 | * PUSHINT8+val, for three bytes removed, but we're adding |
| | 1082 | * one byte, so we have a net current opcode length (to |
| | 1083 | * remove) of two bytes. Since we're combining three |
| | 1084 | * instructions into one, we're losing our second-to-last |
| | 1085 | * opcode. |
| | 1086 | */ |
| | 1087 | opc = OPC_SETINDLCL1I8; |
| | 1088 | second_last_op_ = OPC_NOP; |
| | 1089 | op_len = 2; |
| | 1090 | prv_len = 3; |
| | 1091 | goto combine; |
| | 1092 | |
| | 1093 | case OPC_PUSH_0: |
| | 1094 | idx = 0; |
| | 1095 | goto combine_setind; |
| | 1096 | |
| | 1097 | case OPC_PUSH_1: |
| | 1098 | idx = 1; |
| | 1099 | |
| | 1100 | combine_setind: |
| | 1101 | /* go back and add the index value */ |
| | 1102 | G_cs->write_at(G_cs->get_ofs() - 2, idx); |
| | 1103 | |
| | 1104 | /* |
| | 1105 | * go back and combine the instructions - we're removing a |
| | 1106 | * net of one byte, since we're removing two one-byte |
| | 1107 | * instructions and extending the old 2-byte instruction |
| | 1108 | * into a 3-byte instruction |
| | 1109 | */ |
| | 1110 | opc = OPC_SETINDLCL1I8; |
| | 1111 | second_last_op_ = OPC_NOP; |
| | 1112 | op_len = 1; |
| | 1113 | prv_len = 3; |
| | 1114 | goto combine; |
| | 1115 | } |
| | 1116 | } |
| | 1117 | break; |
| | 1118 | |
| | 1119 | default: |
| | 1120 | /* write this instruction as-is */ |
| | 1121 | break; |
| | 1122 | } |
| | 1123 | |
| | 1124 | /* remember the last opcode we wrote */ |
| | 1125 | second_last_op_ = last_op_; |
| | 1126 | last_op_ = opc; |
| | 1127 | } |
| | 1128 | |
| | 1129 | /* |
| | 1130 | * Write a CALLPROP instruction, combining with preceding opcodes if |
| | 1131 | * possible. |
| | 1132 | */ |
| | 1133 | void CTcGenTarg::write_callprop(int argc, int varargs, vm_prop_id_t prop) |
| | 1134 | { |
| | 1135 | /* |
| | 1136 | * if the previous instruction was GETLCL1, combine it with the |
| | 1137 | * CALLPROP to form a single CALLPROPLCL1 instruction |
| | 1138 | */ |
| | 1139 | if (last_op_ == OPC_GETLCL1) |
| | 1140 | { |
| | 1141 | uchar lcl; |
| | 1142 | |
| | 1143 | /* get the local variable ID from the GETLCL1 instruction */ |
| | 1144 | lcl = G_cs->get_byte_at(G_cs->get_ofs() - 1); |
| | 1145 | |
| | 1146 | /* back up and delete the GETLCL1 instruction */ |
| | 1147 | G_cs->dec_ofs(2); |
| | 1148 | |
| | 1149 | /* roll back the peephole for the instruction deletion */ |
| | 1150 | last_op_ = second_last_op_; |
| | 1151 | second_last_op_ = OPC_NOP; |
| | 1152 | |
| | 1153 | /* write the varargs modifier if appropriate */ |
| | 1154 | if (varargs) |
| | 1155 | write_op(OPC_VARARGC); |
| | 1156 | |
| | 1157 | /* write the CALLPROPLCL1 */ |
| | 1158 | write_op(OPC_CALLPROPLCL1); |
| | 1159 | G_cs->write((char)argc); |
| | 1160 | G_cs->write(lcl); |
| | 1161 | G_cs->write_prop_id(prop); |
| | 1162 | } |
| | 1163 | else |
| | 1164 | { |
| | 1165 | /* generate the varargs modifier if appropriate */ |
| | 1166 | if (varargs) |
| | 1167 | write_op(OPC_VARARGC); |
| | 1168 | |
| | 1169 | /* we have arguments - generate a CALLPROP */ |
| | 1170 | write_op(OPC_CALLPROP); |
| | 1171 | G_cs->write((char)argc); |
| | 1172 | G_cs->write_prop_id(prop); |
| | 1173 | } |
| | 1174 | |
| | 1175 | /* callprop removes arguments and the object */ |
| | 1176 | note_pop(argc + 1); |
| | 1177 | } |
| | 1178 | |
| | 1179 | /* |
| | 1180 | * Note a string's length |
| | 1181 | */ |
| | 1182 | void CTcGenTarg::note_str(size_t len) |
| | 1183 | { |
| | 1184 | /* if it's the longest so far, remember it */ |
| | 1185 | if (len > max_str_len_) |
| | 1186 | { |
| | 1187 | /* |
| | 1188 | * flag an warning the length plus overhead would exceed 32k |
| | 1189 | * (only do this the first time we cross this limit) |
| | 1190 | */ |
| | 1191 | if (len > (32*1024 - VMB_LEN) |
| | 1192 | && max_str_len_ <= (32*1024 - VMB_LEN)) |
| | 1193 | G_tok->log_warning(TCERR_CONST_POOL_OVER_32K); |
| | 1194 | |
| | 1195 | /* remember the length */ |
| | 1196 | max_str_len_ = len; |
| | 1197 | } |
| | 1198 | } |
| | 1199 | |
| | 1200 | /* |
| | 1201 | * note a list's length |
| | 1202 | */ |
| | 1203 | void CTcGenTarg::note_list(size_t element_count) |
| | 1204 | { |
| | 1205 | /* if it's the longest list so far, remember it */ |
| | 1206 | if (element_count > max_list_cnt_) |
| | 1207 | { |
| | 1208 | /* flag a warning if the stored length would be over 32k */ |
| | 1209 | if (element_count > ((32*1024 - VMB_LEN) / VMB_DATAHOLDER) |
| | 1210 | && max_list_cnt_ <= ((32*1024 - VMB_LEN) / VMB_DATAHOLDER)) |
| | 1211 | G_tok->log_warning(TCERR_CONST_POOL_OVER_32K); |
| | 1212 | |
| | 1213 | /* remember the length */ |
| | 1214 | max_list_cnt_ = element_count; |
| | 1215 | } |
| | 1216 | } |
| | 1217 | |
| | 1218 | /* |
| | 1219 | * Note a bytecode block length |
| | 1220 | */ |
| | 1221 | void CTcGenTarg::note_bytecode(ulong len) |
| | 1222 | { |
| | 1223 | /* if it's the longest bytecode block yet, remember it */ |
| | 1224 | if (len > max_bytecode_len_) |
| | 1225 | { |
| | 1226 | /* flag a warning the first time we go over 32k */ |
| | 1227 | if (len >= 32*1024 && max_bytecode_len_ < 32*1024) |
| | 1228 | G_tok->log_warning(TCERR_CODE_POOL_OVER_32K); |
| | 1229 | |
| | 1230 | /* remember the new length */ |
| | 1231 | max_bytecode_len_ = len; |
| | 1232 | } |
| | 1233 | } |
| | 1234 | |
| | 1235 | /* |
| | 1236 | * Add a string to the constant pool |
| | 1237 | */ |
| | 1238 | void CTcGenTarg::add_const_str(const char *str, size_t len, |
| | 1239 | CTcDataStream *ds, ulong ofs) |
| | 1240 | { |
| | 1241 | CTcStreamAnchor *anchor; |
| | 1242 | |
| | 1243 | /* |
| | 1244 | * Add an anchor for the item, and add a fixup for the reference |
| | 1245 | * from ds@ofs to the item. |
| | 1246 | */ |
| | 1247 | anchor = G_ds->add_anchor(0, 0); |
| | 1248 | CTcAbsFixup::add_abs_fixup(anchor->fixup_list_head_, ds, ofs); |
| | 1249 | |
| | 1250 | /* write the length prefix */ |
| | 1251 | G_ds->write2(len); |
| | 1252 | |
| | 1253 | /* write the string bytes */ |
| | 1254 | G_ds->write(str, len); |
| | 1255 | |
| | 1256 | /* note the length of the string stored */ |
| | 1257 | note_str(len); |
| | 1258 | } |
| | 1259 | |
| | 1260 | /* |
| | 1261 | * Add a list to the constant pool |
| | 1262 | */ |
| | 1263 | void CTcGenTarg::add_const_list(CTPNList *lst, |
| | 1264 | CTcDataStream *ds, ulong ofs) |
| | 1265 | { |
| | 1266 | int i; |
| | 1267 | CTPNListEle *cur; |
| | 1268 | ulong dst; |
| | 1269 | CTcStreamAnchor *anchor; |
| | 1270 | |
| | 1271 | /* |
| | 1272 | * Add an anchor for the item, and add a fixup for the reference |
| | 1273 | * from ds@ofs to the item. |
| | 1274 | */ |
| | 1275 | anchor = G_ds->add_anchor(0, 0); |
| | 1276 | CTcAbsFixup::add_abs_fixup(anchor->fixup_list_head_, ds, ofs); |
| | 1277 | |
| | 1278 | /* |
| | 1279 | * Reserve space for the list. We need to do this first, because |
| | 1280 | * the list might contain elements which themselves must be written |
| | 1281 | * to the data stream; we must therefore reserve space for the |
| | 1282 | * entire list before we start writing its elements. |
| | 1283 | */ |
| | 1284 | dst = G_ds->reserve(2 + lst->get_count()*VMB_DATAHOLDER); |
| | 1285 | |
| | 1286 | /* set the length prefix */ |
| | 1287 | G_ds->write2_at(dst, lst->get_count()); |
| | 1288 | dst += 2; |
| | 1289 | |
| | 1290 | /* store the elements */ |
| | 1291 | for (i = 0, cur = lst->get_head() ; cur != 0 ; |
| | 1292 | ++i, cur = cur->get_next(), dst += VMB_DATAHOLDER) |
| | 1293 | { |
| | 1294 | CTcConstVal *ele; |
| | 1295 | |
| | 1296 | /* get this element */ |
| | 1297 | ele = cur->get_expr()->get_const_val(); |
| | 1298 | |
| | 1299 | /* write it to the element buffer */ |
| | 1300 | write_const_as_dh(G_ds, dst, ele); |
| | 1301 | } |
| | 1302 | |
| | 1303 | /* make sure the list wasn't corrupted */ |
| | 1304 | if (i != lst->get_count()) |
| | 1305 | G_tok->throw_internal_error(TCERR_CORRUPT_LIST); |
| | 1306 | |
| | 1307 | /* note the number of elements in the list */ |
| | 1308 | note_list(lst->get_count()); |
| | 1309 | } |
| | 1310 | |
| | 1311 | /* |
| | 1312 | * Generate a BigNumber object |
| | 1313 | */ |
| | 1314 | vm_obj_id_t CTcGenTarg::gen_bignum_obj(const char *txt, size_t len) |
| | 1315 | { |
| | 1316 | vm_obj_id_t id; |
| | 1317 | long size_ofs; |
| | 1318 | long end_ofs; |
| | 1319 | long num_ofs; |
| | 1320 | CTcDataStream *str = G_bignum_stream; |
| | 1321 | int exp; |
| | 1322 | int decpt; |
| | 1323 | utf8_ptr p; |
| | 1324 | size_t rem; |
| | 1325 | int dig[2]; |
| | 1326 | char dig_idx; |
| | 1327 | int sig; |
| | 1328 | size_t tot_digits; |
| | 1329 | size_t prec; |
| | 1330 | int neg; |
| | 1331 | int val_zero; |
| | 1332 | uchar flags; |
| | 1333 | |
| | 1334 | /* generate a new object ID for the BigNumber */ |
| | 1335 | id = new_obj_id(); |
| | 1336 | |
| | 1337 | /* |
| | 1338 | * add the object ID to the non-symbol object list - this is |
| | 1339 | * necessary to ensure that the object ID is fixed up during linking |
| | 1340 | */ |
| | 1341 | G_prs->add_nonsym_obj(id); |
| | 1342 | |
| | 1343 | /* |
| | 1344 | * generate the object data to the BigNumber data stream |
| | 1345 | */ |
| | 1346 | |
| | 1347 | /* |
| | 1348 | * write the OBJS header - object ID plus byte count for |
| | 1349 | * metaclass-specific data |
| | 1350 | */ |
| | 1351 | str->write_obj_id(id); |
| | 1352 | size_ofs = str->get_ofs(); |
| | 1353 | str->write2(0); |
| | 1354 | |
| | 1355 | /* |
| | 1356 | * write the metaclass-specific data for the BigNumber metaclass |
| | 1357 | */ |
| | 1358 | |
| | 1359 | /* remember where the number starts */ |
| | 1360 | num_ofs = str->get_ofs(); |
| | 1361 | |
| | 1362 | /* write placeholders for the precision, exponent, and flags */ |
| | 1363 | str->write2(0); |
| | 1364 | str->write2(0); |
| | 1365 | str->write(0); |
| | 1366 | |
| | 1367 | /* start at the beginning of the number's text */ |
| | 1368 | p.set((char *)txt); |
| | 1369 | rem = len; |
| | 1370 | |
| | 1371 | /* presume the value won't be zero */ |
| | 1372 | val_zero = FALSE; |
| | 1373 | |
| | 1374 | /* check for leading sign indicators */ |
| | 1375 | for (neg = FALSE ; rem != 0 ; p.inc(&rem)) |
| | 1376 | { |
| | 1377 | /* if it's a sign, note it and keep scanning */ |
| | 1378 | if (p.getch() == '-') |
| | 1379 | { |
| | 1380 | /* negative sign - note it and keep going */ |
| | 1381 | neg = !neg; |
| | 1382 | } |
| | 1383 | else if (p.getch() == '+') |
| | 1384 | { |
| | 1385 | /* positive sign - ignore it and keep going */ |
| | 1386 | } |
| | 1387 | else |
| | 1388 | { |
| | 1389 | /* not a sign character - stop scanning for signs */ |
| | 1390 | break; |
| | 1391 | } |
| | 1392 | } |
| | 1393 | |
| | 1394 | /* scan the digits of the number */ |
| | 1395 | for (exp = 0, sig = FALSE, decpt = FALSE, prec = 0, tot_digits = 0, |
| | 1396 | dig_idx = 0 ; rem != 0 ; p.inc(&rem)) |
| | 1397 | { |
| | 1398 | wchar_t ch; |
| | 1399 | |
| | 1400 | /* get this character */ |
| | 1401 | ch = p.getch(); |
| | 1402 | |
| | 1403 | /* see what we have */ |
| | 1404 | if (is_digit(ch)) |
| | 1405 | { |
| | 1406 | /* |
| | 1407 | * if it's non-zero, it's definitely significant; otherwise, |
| | 1408 | * it's significant only if we've seen a significant digit |
| | 1409 | * already |
| | 1410 | */ |
| | 1411 | if (ch != '0') |
| | 1412 | sig = TRUE; |
| | 1413 | |
| | 1414 | /* count it in the total digits whether or not its significant */ |
| | 1415 | ++tot_digits; |
| | 1416 | |
| | 1417 | /* if the digit is significant, add it to the number */ |
| | 1418 | if (sig) |
| | 1419 | { |
| | 1420 | /* add another digit to our buffer */ |
| | 1421 | dig[dig_idx++] = value_of_digit(ch); |
| | 1422 | |
| | 1423 | /* count the precision */ |
| | 1424 | ++prec; |
| | 1425 | |
| | 1426 | /* |
| | 1427 | * if we haven't found the decimal point yet, count the |
| | 1428 | * exponent change |
| | 1429 | */ |
| | 1430 | if (!decpt) |
| | 1431 | ++exp; |
| | 1432 | |
| | 1433 | /* |
| | 1434 | * if we have two digits now, write out another byte of |
| | 1435 | * the number |
| | 1436 | */ |
| | 1437 | if (dig_idx == 2) |
| | 1438 | { |
| | 1439 | /* write out this digit pair */ |
| | 1440 | str->write((char)((dig[0] << 4) + dig[1])); |
| | 1441 | |
| | 1442 | /* the buffer is now empty */ |
| | 1443 | dig_idx = 0; |
| | 1444 | } |
| | 1445 | } |
| | 1446 | else if (decpt) |
| | 1447 | { |
| | 1448 | /* |
| | 1449 | * we have a leading insignificant zero following the |
| | 1450 | * decimal point - decrease the exponent |
| | 1451 | */ |
| | 1452 | --exp; |
| | 1453 | } |
| | 1454 | } |
| | 1455 | else if (!decpt && ch == '.') |
| | 1456 | { |
| | 1457 | /* we've found the decimal point - note it */ |
| | 1458 | decpt = TRUE; |
| | 1459 | } |
| | 1460 | else if (ch == 'e' || ch == 'E') |
| | 1461 | { |
| | 1462 | int neg_exp = FALSE; |
| | 1463 | long acc; |
| | 1464 | |
| | 1465 | /* we've found our exponent - check for a sign */ |
| | 1466 | p.inc(&rem); |
| | 1467 | if (rem != 0) |
| | 1468 | { |
| | 1469 | if (p.getch() == '-') |
| | 1470 | { |
| | 1471 | /* note the sign and skip the '-' */ |
| | 1472 | neg_exp = TRUE; |
| | 1473 | p.inc(&rem); |
| | 1474 | } |
| | 1475 | else if (p.getch() == '+') |
| | 1476 | { |
| | 1477 | /* skip the '+' */ |
| | 1478 | p.inc(&rem); |
| | 1479 | } |
| | 1480 | } |
| | 1481 | |
| | 1482 | /* scan the digits */ |
| | 1483 | for (acc = 0 ; rem != 0 ; p.inc(&rem)) |
| | 1484 | { |
| | 1485 | long new_acc; |
| | 1486 | |
| | 1487 | /* if this isn't a digit, we're done */ |
| | 1488 | if (!is_digit(p.getch())) |
| | 1489 | break; |
| | 1490 | |
| | 1491 | /* add in this digit */ |
| | 1492 | new_acc = acc*10 + value_of_digit(p.getch()); |
| | 1493 | if ((!neg_exp && new_acc > 32767) |
| | 1494 | || (neg_exp && new_acc > 32768)) |
| | 1495 | break; |
| | 1496 | |
| | 1497 | /* set the new accumulator */ |
| | 1498 | acc = new_acc; |
| | 1499 | } |
| | 1500 | |
| | 1501 | /* set the sign */ |
| | 1502 | if (neg_exp) |
| | 1503 | acc = -acc; |
| | 1504 | |
| | 1505 | /* |
| | 1506 | * add this exponent to the exponent we derived for the |
| | 1507 | * number itself |
| | 1508 | */ |
| | 1509 | exp = (int)(exp + acc); |
| | 1510 | |
| | 1511 | /* |
| | 1512 | * since we scanned the string in our own loop, make sure we |
| | 1513 | * haven't reached the end of the buffer - if we have, we're |
| | 1514 | * done with the outer loop now |
| | 1515 | */ |
| | 1516 | if (rem == 0) |
| | 1517 | break; |
| | 1518 | } |
| | 1519 | else |
| | 1520 | { |
| | 1521 | /* |
| | 1522 | * anything else is invalid, so we've reached the end of the |
| | 1523 | * number - stop scanning |
| | 1524 | */ |
| | 1525 | break; |
| | 1526 | } |
| | 1527 | } |
| | 1528 | |
| | 1529 | /* if we have a pending digit, write it out */ |
| | 1530 | if (dig_idx == 1) |
| | 1531 | str->write((char)(dig[0] << 4)); |
| | 1532 | |
| | 1533 | /* |
| | 1534 | * if we had no significant digits, the number is all zeroes, so in |
| | 1535 | * this special case treat all of the zeroes as significant |
| | 1536 | */ |
| | 1537 | if (prec == 0 && tot_digits != 0) |
| | 1538 | { |
| | 1539 | /* note that the value is zero */ |
| | 1540 | val_zero = TRUE; |
| | 1541 | |
| | 1542 | /* use the zeroes as significant digits */ |
| | 1543 | prec = tot_digits; |
| | 1544 | |
| | 1545 | /* write out the zeroes */ |
| | 1546 | for ( ; tot_digits > 1 ; tot_digits -= 2) |
| | 1547 | str->write(0); |
| | 1548 | if (tot_digits > 0) |
| | 1549 | str->write(0); |
| | 1550 | |
| | 1551 | /* |
| | 1552 | * the exponent for the value zero is always 1 (this is a |
| | 1553 | * normalization rule) |
| | 1554 | */ |
| | 1555 | exp = 1; |
| | 1556 | } |
| | 1557 | |
| | 1558 | /* construct the flags */ |
| | 1559 | flags = 0; |
| | 1560 | if (neg) |
| | 1561 | flags |= VMBN_F_NEG; |
| | 1562 | if (val_zero) |
| | 1563 | flags |= VMBN_F_ZERO; |
| | 1564 | |
| | 1565 | /* go back and fix up the precision, exponent, and flags values */ |
| | 1566 | str->write2_at(num_ofs, prec); |
| | 1567 | str->write2_at(num_ofs + 2, exp); |
| | 1568 | str->write_at(num_ofs + 4, flags); |
| | 1569 | |
| | 1570 | /* fix up the size */ |
| | 1571 | end_ofs = str->get_ofs(); |
| | 1572 | str->write2_at(size_ofs, end_ofs - size_ofs - 2); |
| | 1573 | |
| | 1574 | /* return the new object ID */ |
| | 1575 | return id; |
| | 1576 | } |
| | 1577 | |
| | 1578 | /* |
| | 1579 | * Convert a constant value from a CTcConstVal (compiler internal |
| | 1580 | * representation) to a vm_val_t (interpreter representation). |
| | 1581 | */ |
| | 1582 | void CTcGenTarg::write_const_as_dh(CTcDataStream *ds, ulong ofs, |
| | 1583 | const CTcConstVal *src) |
| | 1584 | { |
| | 1585 | vm_val_t val; |
| | 1586 | char buf[VMB_DATAHOLDER]; |
| | 1587 | |
| | 1588 | /* convert according to the value's type */ |
| | 1589 | switch(src->get_type()) |
| | 1590 | { |
| | 1591 | case TC_CVT_NIL: |
| | 1592 | val.set_nil(); |
| | 1593 | break; |
| | 1594 | |
| | 1595 | case TC_CVT_TRUE: |
| | 1596 | val.set_true(); |
| | 1597 | break; |
| | 1598 | |
| | 1599 | case TC_CVT_INT: |
| | 1600 | val.set_int(src->get_val_int()); |
| | 1601 | break; |
| | 1602 | |
| | 1603 | case TC_CVT_FLOAT: |
| | 1604 | /* generate the BigNumber object */ |
| | 1605 | val.set_obj(gen_bignum_obj(src->get_val_float(), |
| | 1606 | src->get_val_float_len())); |
| | 1607 | |
| | 1608 | /* add a fixup for the object ID */ |
| | 1609 | if (G_keep_objfixups) |
| | 1610 | CTcIdFixup::add_fixup(&G_objfixup, ds, ofs + 1, val.val.obj); |
| | 1611 | break; |
| | 1612 | |
| | 1613 | case TC_CVT_SSTR: |
| | 1614 | /* |
| | 1615 | * Store the string in the constant pool. Note that our fixup |
| | 1616 | * is at the destination stream offset plus one, since the |
| | 1617 | * DATAHOLDER has the type byte followed by the offset value. |
| | 1618 | */ |
| | 1619 | add_const_str(src->get_val_str(), src->get_val_str_len(), |
| | 1620 | ds, ofs + 1); |
| | 1621 | |
| | 1622 | /* |
| | 1623 | * set the offset to zero for now - the fixup that |
| | 1624 | * add_const_str() generates will take care of supplying the |
| | 1625 | * real value |
| | 1626 | */ |
| | 1627 | val.set_sstring(0); |
| | 1628 | break; |
| | 1629 | |
| | 1630 | case TC_CVT_LIST: |
| | 1631 | /* |
| | 1632 | * Store the sublist in the constant pool. Our fixup is at the |
| | 1633 | * destination stream offset plus one, since the DATAHOLDER has |
| | 1634 | * the type byte followed by the offset value. |
| | 1635 | */ |
| | 1636 | add_const_list(src->get_val_list(), ds, ofs + 1); |
| | 1637 | |
| | 1638 | /* |
| | 1639 | * set the offset to zero for now - the fixup that |
| | 1640 | * add_const_list() generates will take care of supplying the |
| | 1641 | * real value |
| | 1642 | */ |
| | 1643 | val.set_list(0); |
| | 1644 | break; |
| | 1645 | |
| | 1646 | case TC_CVT_OBJ: |
| | 1647 | /* set the object ID value */ |
| | 1648 | val.set_obj((vm_obj_id_t)src->get_val_obj()); |
| | 1649 | |
| | 1650 | /* |
| | 1651 | * add a fixup (at the current offset plus one, for the type |
| | 1652 | * byte) if we're keeping object ID fixups |
| | 1653 | */ |
| | 1654 | if (G_keep_objfixups) |
| | 1655 | CTcIdFixup::add_fixup(&G_objfixup, ds, ofs + 1, |
| | 1656 | src->get_val_obj()); |
| | 1657 | break; |
| | 1658 | |
| | 1659 | case TC_CVT_ENUM: |
| | 1660 | /* set the enum value */ |
| | 1661 | val.set_enum(src->get_val_enum()); |
| | 1662 | |
| | 1663 | /* add a fixup */ |
| | 1664 | if (G_keep_enumfixups) |
| | 1665 | CTcIdFixup::add_fixup(&G_enumfixup, ds, ofs + 1, |
| | 1666 | src->get_val_enum()); |
| | 1667 | break; |
| | 1668 | |
| | 1669 | case TC_CVT_PROP: |
| | 1670 | /* set the property ID value */ |
| | 1671 | val.set_propid((vm_prop_id_t)src->get_val_prop()); |
| | 1672 | |
| | 1673 | /* |
| | 1674 | * add a fixup (at the current offset plus one, for the type |
| | 1675 | * byte) if we're keeping property ID fixups |
| | 1676 | */ |
| | 1677 | if (G_keep_propfixups) |
| | 1678 | CTcIdFixup::add_fixup(&G_propfixup, ds, ofs + 1, |
| | 1679 | src->get_val_prop()); |
| | 1680 | break; |
| | 1681 | |
| | 1682 | case TC_CVT_FUNCPTR: |
| | 1683 | /* |
| | 1684 | * use a placeholder value of zero for now - a function's final |
| | 1685 | * address is never known until after all code generation has |
| | 1686 | * been completed (the fixup will take care of supplying the |
| | 1687 | * correct value when the time comes) |
| | 1688 | */ |
| | 1689 | val.set_fnptr(0); |
| | 1690 | |
| | 1691 | /* |
| | 1692 | * Add a fixup. The fixup is at the destination stream offset |
| | 1693 | * plus one, because the DATAHOLDER has a type byte followed by |
| | 1694 | * the function pointer value. |
| | 1695 | */ |
| | 1696 | src->get_val_funcptr_sym()->add_abs_fixup(ds, ofs + 1); |
| | 1697 | break; |
| | 1698 | |
| | 1699 | case TC_CVT_ANONFUNCPTR: |
| | 1700 | /* use a placeholder of zero for now, until we fix up the pointer */ |
| | 1701 | val.set_fnptr(0); |
| | 1702 | |
| | 1703 | /* add a fixup for the code body */ |
| | 1704 | src->get_val_anon_func_ptr()->add_abs_fixup(ds, ofs + 1); |
| | 1705 | break; |
| | 1706 | |
| | 1707 | case TC_CVT_VOCAB_LIST: |
| | 1708 | /* |
| | 1709 | * it's an internal vocabulary list type - this is used as a |
| | 1710 | * placeholder only, and will be replaced during linking with an |
| | 1711 | * actual vocabulary string list |
| | 1712 | */ |
| | 1713 | val.typ = VM_VOCAB_LIST; |
| | 1714 | break; |
| | 1715 | |
| | 1716 | case TC_CVT_UNK: |
| | 1717 | /* unknown - ignore it */ |
| | 1718 | break; |
| | 1719 | } |
| | 1720 | |
| | 1721 | /* write the vm_val_t in DATA_HOLDER format into the stream */ |
| | 1722 | vmb_put_dh(buf, &val); |
| | 1723 | ds->write_at(ofs, buf, VMB_DATAHOLDER); |
| | 1724 | } |
| | 1725 | |
| | 1726 | /* |
| | 1727 | * Write a DATAHOLDER at the current offset in a stream |
| | 1728 | */ |
| | 1729 | void CTcGenTarg::write_const_as_dh(CTcDataStream *ds, |
| | 1730 | const CTcConstVal *src) |
| | 1731 | { |
| | 1732 | /* write to the current stream offset */ |
| | 1733 | write_const_as_dh(ds, ds->get_ofs(), src); |
| | 1734 | } |
| | 1735 | |
| | 1736 | /* |
| | 1737 | * Notify that parsing is finished |
| | 1738 | */ |
| | 1739 | void CTcGenTarg::parsing_done() |
| | 1740 | { |
| | 1741 | /* nothing special to do */ |
| | 1742 | } |
| | 1743 | |
| | 1744 | |
| | 1745 | /* |
| | 1746 | * notify the code generator that we're replacing an object |
| | 1747 | */ |
| | 1748 | void CTcGenTarg::notify_replace_object(ulong stream_ofs) |
| | 1749 | { |
| | 1750 | uint flags; |
| | 1751 | |
| | 1752 | /* set the 'replaced' flag in the flags prefix */ |
| | 1753 | flags = G_os->read2_at(stream_ofs); |
| | 1754 | flags |= TCT3_OBJ_REPLACED; |
| | 1755 | G_os->write2_at(stream_ofs, flags); |
| | 1756 | } |
| | 1757 | |
| | 1758 | |
| | 1759 | /* |
| | 1760 | * Set the starting offset of the current method |
| | 1761 | */ |
| | 1762 | void CTcGenTarg::set_method_ofs(ulong ofs) |
| | 1763 | { |
| | 1764 | /* tell the exception table object about it */ |
| | 1765 | get_exc_table()->set_method_ofs(ofs); |
| | 1766 | |
| | 1767 | /* remember it in the code stream */ |
| | 1768 | G_cs->set_method_ofs(ofs); |
| | 1769 | } |
| | 1770 | |
| | 1771 | /* |
| | 1772 | * Add a debug line table to our list |
| | 1773 | */ |
| | 1774 | void CTcGenTarg::add_debug_line_table(ulong ofs) |
| | 1775 | { |
| | 1776 | size_t idx; |
| | 1777 | uchar *p; |
| | 1778 | |
| | 1779 | /* calculate the index of the next free entry on its page */ |
| | 1780 | idx = (size_t)(debug_line_cnt_ % TCT3_DEBUG_LINE_PAGE_SIZE); |
| | 1781 | |
| | 1782 | /* |
| | 1783 | * if we've completely filled the last page, allocate a new one - we |
| | 1784 | * know we've exhausted the page if we're at the start of a new page |
| | 1785 | * (i.e., the index is zero) |
| | 1786 | */ |
| | 1787 | if (idx == 0) |
| | 1788 | { |
| | 1789 | tct3_debug_line_page *pg; |
| | 1790 | |
| | 1791 | /* allocate the new page */ |
| | 1792 | pg = (tct3_debug_line_page *)t3malloc(sizeof(*pg)); |
| | 1793 | |
| | 1794 | /* link it in at the end of the list */ |
| | 1795 | pg->nxt = 0; |
| | 1796 | if (debug_line_tail_ == 0) |
| | 1797 | debug_line_head_ = pg; |
| | 1798 | else |
| | 1799 | debug_line_tail_->nxt = pg; |
| | 1800 | debug_line_tail_ = pg; |
| | 1801 | } |
| | 1802 | |
| | 1803 | /* get a pointer to the entry */ |
| | 1804 | p = debug_line_tail_->line_ofs + (idx * TCT3_DEBUG_LINE_REC_SIZE); |
| | 1805 | |
| | 1806 | /* |
| | 1807 | * set this entry - one byte for the code stream ID, then a UINT4 |
| | 1808 | * with the offset in the stream |
| | 1809 | */ |
| | 1810 | *p = G_cs->get_stream_id(); |
| | 1811 | oswp4(p + 1, ofs); |
| | 1812 | |
| | 1813 | /* count it */ |
| | 1814 | ++debug_line_cnt_; |
| | 1815 | } |
| | 1816 | |
| | 1817 | /* ------------------------------------------------------------------------ */ |
| | 1818 | /* |
| | 1819 | * Method header generator |
| | 1820 | */ |
| | 1821 | |
| | 1822 | /* |
| | 1823 | * Open a method |
| | 1824 | */ |
| | 1825 | void CTcGenTarg::open_method(CTcCodeStream *stream, |
| | 1826 | CTcSymbol *fixup_owner_sym, |
| | 1827 | CTcAbsFixup **fixup_list_head, |
| | 1828 | CTPNCodeBody *code_body, |
| | 1829 | CTcPrsSymtab *goto_tab, |
| | 1830 | int argc, int varargs, |
| | 1831 | int is_constructor, int is_self_available, |
| | 1832 | tct3_method_gen_ctx *ctx) |
| | 1833 | { |
| | 1834 | /* set the code stream as the current code generator output stream */ |
| | 1835 | G_cs = ctx->stream = stream; |
| | 1836 | |
| | 1837 | /* set the method properties in the code generator */ |
| | 1838 | set_in_constructor(is_constructor); |
| | 1839 | stream->set_self_available(is_self_available); |
| | 1840 | |
| | 1841 | /* we obviously can't combine any past instructions */ |
| | 1842 | clear_peephole(); |
| | 1843 | |
| | 1844 | /* clear the old line records */ |
| | 1845 | stream->clear_line_recs(); |
| | 1846 | |
| | 1847 | /* clear the old frame list */ |
| | 1848 | stream->clear_local_frames(); |
| | 1849 | |
| | 1850 | /* clear the old exception table */ |
| | 1851 | get_exc_table()->clear_table(); |
| | 1852 | |
| | 1853 | /* reset the stack depth counters */ |
| | 1854 | reset_sp_depth(); |
| | 1855 | |
| | 1856 | /* there are no enclosing 'switch' or block statements yet */ |
| | 1857 | stream->set_switch(0); |
| | 1858 | stream->set_enclosing(0); |
| | 1859 | |
| | 1860 | /* |
| | 1861 | * remember where the method header starts - we'll need to come back |
| | 1862 | * and fix up some placeholder entries in "Close" |
| | 1863 | */ |
| | 1864 | ctx->method_ofs = stream->get_ofs(); |
| | 1865 | |
| | 1866 | /* set the method start offset in the code generator */ |
| | 1867 | set_method_ofs(ctx->method_ofs); |
| | 1868 | |
| | 1869 | /* set up a fixup anchor for the new method */ |
| | 1870 | ctx->anchor = stream->add_anchor(fixup_owner_sym, fixup_list_head); |
| | 1871 | |
| | 1872 | /* set the anchor in the associated symbol, if applicable */ |
| | 1873 | if (fixup_owner_sym != 0) |
| | 1874 | fixup_owner_sym->set_anchor(ctx->anchor); |
| | 1875 | |
| | 1876 | /* |
| | 1877 | * Generate the function header. At the moment, we don't know the |
| | 1878 | * stack usage, exception table offset, or debug record offset, since |
| | 1879 | * these all come after the byte code; we won't know how big the byte |
| | 1880 | * code is until after we generate it. For now, write zero bytes as |
| | 1881 | * placeholders for these slots; we'll come back and fix them up to |
| | 1882 | * their real values after we've generated the byte code. |
| | 1883 | */ |
| | 1884 | stream->write(argc | (varargs ? 0x80 : 0)); /* argument count */ |
| | 1885 | stream->write(0); /* reserved zero byte */ |
| | 1886 | stream->write2(0); /* number of locals - won't know until after codegen */ |
| | 1887 | stream->write2(0); /* total stack - won't know until after codegen */ |
| | 1888 | stream->write2(0); /* exception table offset - presume none */ |
| | 1889 | stream->write2(0); /* debug record offset - presume no debug records */ |
| | 1890 | |
| | 1891 | /* remember the starting offset of the code */ |
| | 1892 | ctx->code_start_ofs = stream->get_ofs(); |
| | 1893 | |
| | 1894 | /* set the current code body being generated */ |
| | 1895 | ctx->old_code_body = stream->set_code_body(code_body); |
| | 1896 | |
| | 1897 | /* get the 'goto' symbol table for this function */ |
| | 1898 | stream->set_goto_symtab(goto_tab); |
| | 1899 | } |
| | 1900 | |
| | 1901 | /* |
| | 1902 | * Close a method |
| | 1903 | */ |
| | 1904 | void CTcGenTarg::close_method(int local_cnt, |
| | 1905 | CTcTokFileDesc *end_desc, long end_linenum, |
| | 1906 | tct3_method_gen_ctx *ctx) |
| | 1907 | { |
| | 1908 | /* get the output code stream from the context */ |
| | 1909 | CTcCodeStream *stream = ctx->stream; |
| | 1910 | |
| | 1911 | /* |
| | 1912 | * Generate a 'return' opcode with a default 'nil' return value - this |
| | 1913 | * will ensure that code that reaches the end of the procedure returns |
| | 1914 | * normally. If this is a constructor, return the 'self' object rather |
| | 1915 | * than nil. |
| | 1916 | * |
| | 1917 | * We only need to generate this epilogue if the next instruction would |
| | 1918 | * be reachable. If it's not reachable, then the code explicitly took |
| | 1919 | * care of all types of exits. |
| | 1920 | */ |
| | 1921 | if (!can_skip_op()) |
| | 1922 | { |
| | 1923 | /* |
| | 1924 | * add a line record for the implied return at the last source line |
| | 1925 | * of the code body |
| | 1926 | */ |
| | 1927 | add_line_rec(end_desc, end_linenum); |
| | 1928 | |
| | 1929 | /* write the appropriate return */ |
| | 1930 | if (is_in_constructor()) |
| | 1931 | { |
| | 1932 | /* we're in a constructor - return 'self' */ |
| | 1933 | write_op(OPC_PUSHSELF); |
| | 1934 | write_op(OPC_RETVAL); |
| | 1935 | } |
| | 1936 | else |
| | 1937 | { |
| | 1938 | /* |
| | 1939 | * Normal method/function - return without a value (explicitly |
| | 1940 | * set R0 to nil, though, so we don't return something returned |
| | 1941 | * from a called function). |
| | 1942 | */ |
| | 1943 | write_op(OPC_RETNIL); |
| | 1944 | } |
| | 1945 | } |
| | 1946 | |
| | 1947 | /* |
| | 1948 | * release labels allocated for the code block; this will log an error |
| | 1949 | * if any labels are not defined |
| | 1950 | */ |
| | 1951 | stream->release_labels(); |
| | 1952 | |
| | 1953 | /* |
| | 1954 | * Eliminate jump-to-jump sequences in the generated code. Don't |
| | 1955 | * bother if we've found any errors, as the generated code will not |
| | 1956 | * necessarily be valid if this is the case. |
| | 1957 | */ |
| | 1958 | if (G_tcmain->get_error_count() == 0) |
| | 1959 | remove_jumps_to_jumps(stream, ctx->code_start_ofs); |
| | 1960 | |
| | 1961 | /* note the code block's end point */ |
| | 1962 | ctx->code_end_ofs = stream->get_ofs(); |
| | 1963 | |
| | 1964 | /* |
| | 1965 | * Fix up the local variable count in the function header. We might |
| | 1966 | * allocate extra locals for internal use while generating code, so we |
| | 1967 | * must wait until after generating our code before we know the final |
| | 1968 | * local count. |
| | 1969 | */ |
| | 1970 | stream->write2_at(ctx->method_ofs + 2, local_cnt); |
| | 1971 | |
| | 1972 | /* |
| | 1973 | * Fix up the total stack space indicator in the function header. The |
| | 1974 | * total stack size must include the locals, as well as stack space |
| | 1975 | * needed for intermediate computations. |
| | 1976 | */ |
| | 1977 | stream->write2_at(ctx->method_ofs + 4, get_max_sp_depth() + local_cnt); |
| | 1978 | |
| | 1979 | /* |
| | 1980 | * Generate the exception table, if we have one. If we have no |
| | 1981 | * exception records, leave the exception table offset set to zero to |
| | 1982 | * indicate that there is no exception table for the method. |
| | 1983 | */ |
| | 1984 | if (get_exc_table()->get_entry_count() != 0) |
| | 1985 | { |
| | 1986 | /* |
| | 1987 | * write the exception table offset - it's at the current offset in |
| | 1988 | * the code |
| | 1989 | */ |
| | 1990 | stream->write2_at(ctx->method_ofs + 6, |
| | 1991 | stream->get_ofs() - ctx->method_ofs); |
| | 1992 | |
| | 1993 | /* write the table */ |
| | 1994 | get_exc_table()->write_to_code_stream(); |
| | 1995 | } |
| | 1996 | } |
| | 1997 | |
| | 1998 | /* |
| | 1999 | * Clean up after generating a method |
| | 2000 | */ |
| | 2001 | void CTcGenTarg::close_method_cleanup(tct3_method_gen_ctx *ctx) |
| | 2002 | { |
| | 2003 | /* |
| | 2004 | * Tell the code generator our code block byte length so that it can |
| | 2005 | * keep track of the longest single byte-code block; it will use this |
| | 2006 | * to choose the code pool page size when generating the image file. |
| | 2007 | */ |
| | 2008 | note_bytecode(ctx->anchor->get_len(ctx->stream)); |
| | 2009 | |
| | 2010 | /* we're no longer in a constructor, if we ever were */ |
| | 2011 | set_in_constructor(FALSE); |
| | 2012 | |
| | 2013 | /* clear the current code body */ |
| | 2014 | ctx->stream->set_code_body(ctx->old_code_body); |
| | 2015 | |
| | 2016 | /* always leave the main code stream active by default */ |
| | 2017 | G_cs = G_cs_main; |
| | 2018 | } |
| | 2019 | |
| | 2020 | /* ------------------------------------------------------------------------ */ |
| | 2021 | /* |
| | 2022 | * Run through the generated code stream starting at the given offset (and |
| | 2023 | * running up to the current offset), and eliminate jump-to-jump sequences: |
| | 2024 | * |
| | 2025 | * - whenever we find any jump instruction that points directly to an |
| | 2026 | * unconditional jump, we'll change the first jump so that it points to |
| | 2027 | * the target of the second jump, saving the unnecessary stop at the |
| | 2028 | * intermediate jump |
| | 2029 | * |
| | 2030 | * - whenever we find an unconditional jump to any return or throw |
| | 2031 | * instruction, we'll replace the jump with a copy of the target |
| | 2032 | * return/throw instruction. |
| | 2033 | */ |
| | 2034 | void CTcGenTarg::remove_jumps_to_jumps(CTcCodeStream *str, ulong start_ofs) |
| | 2035 | { |
| | 2036 | ulong ofs; |
| | 2037 | ulong end_ofs; |
| | 2038 | uchar prv_op; |
| | 2039 | static const size_t op_siz[] = |
| | 2040 | { |
| | 2041 | 0, /* 0x00 - unused */ |
| | 2042 | |
| | 2043 | 1, /* 0x01 - OPC_PUSH_0 */ |
| | 2044 | 1, /* 0x02 - OPC_PUSH_1 */ |
| | 2045 | 2, /* 0x03 - OPC_PUSHINT8 */ |
| | 2046 | 5, /* 0x04 - OPC_PUSHINT */ |
| | 2047 | 5, /* 0x05 - OPC_PUSHSTR */ |
| | 2048 | 5, /* 0x06 - OPC_PUSHLST */ |
| | 2049 | 5, /* 0x07 - OPC_PUSHOBJ */ |
| | 2050 | 1, /* 0x08 - OPC_PUSHNIL */ |
| | 2051 | 1, /* 0x09 - OPC_PUSHTRUE */ |
| | 2052 | 3, /* 0x0A - OPC_PUSHPROPID */ |
| | 2053 | 5, /* 0x0B - OPC_PUSHFNPTR */ |
| | 2054 | 0, /* 0x0C - OPC_PUSHSTRI - variable-size instruction */ |
| | 2055 | 2, /* 0x0D - OPC_PUSHPARLST */ |
| | 2056 | 1, /* 0x0E - OPC_MAKELSTPAR */ |
| | 2057 | 5, /* 0x0F - OPC_PUSHENUM */ |
| | 2058 | |
| | 2059 | 1, /* 0x10 - unused */ |
| | 2060 | 1, /* 0x11 - unused */ |
| | 2061 | 1, /* 0x12 - unused */ |
| | 2062 | 1, /* 0x13 - unused */ |
| | 2063 | 1, /* 0x14 - unused */ |
| | 2064 | 1, /* 0x15 - unused */ |
| | 2065 | 1, /* 0x16 - unused */ |
| | 2066 | 1, /* 0x17 - unused */ |
| | 2067 | 1, /* 0x18 - unused */ |
| | 2068 | 1, /* 0x19 - unused */ |
| | 2069 | 1, /* 0x1A - unused */ |
| | 2070 | 1, /* 0x1B - unused */ |
| | 2071 | 1, /* 0x1C - unused */ |
| | 2072 | 1, /* 0x1D - unused */ |
| | 2073 | 1, /* 0x1E - unused */ |
| | 2074 | 1, /* 0x1F - unused */ |
| | 2075 | |
| | 2076 | 1, /* 0x20 - OPC_NEG */ |
| | 2077 | 1, /* 0x21 - OPC_BNOT */ |
| | 2078 | 1, /* 0x22 - OPC_ADD */ |
| | 2079 | 1, /* 0x23 - OPC_SUB */ |
| | 2080 | 1, /* 0x24 - OPC_MUL */ |
| | 2081 | 1, /* 0x25 - OPC_BAND */ |
| | 2082 | 1, /* 0x26 - OPC_BOR */ |
| | 2083 | 1, /* 0x27 - OPC_SHL */ |
| | 2084 | 1, /* 0x28 - OPC_SHR */ |
| | 2085 | 1, /* 0x29 - OPC_XOR */ |
| | 2086 | 1, /* 0x2A - OPC_DIV */ |
| | 2087 | 1, /* 0x2B - OPC_MOD */ |
| | 2088 | 1, /* 0x2C - OPC_NOT */ |
| | 2089 | 1, /* 0x2D - OPC_BOOLIZE */ |
| | 2090 | 1, /* 0x2E - OPC_INC */ |
| | 2091 | 1, /* 0x2F - OPC_DEC */ |
| | 2092 | |
| | 2093 | 1, /* 0x30 - unused */ |
| | 2094 | 1, /* 0x31 - unused */ |
| | 2095 | 1, /* 0x32 - unused */ |
| | 2096 | 1, /* 0x33 - unused */ |
| | 2097 | 1, /* 0x34 - unused */ |
| | 2098 | 1, /* 0x35 - unused */ |
| | 2099 | 1, /* 0x36 - unused */ |
| | 2100 | 1, /* 0x37 - unused */ |
| | 2101 | 1, /* 0x38 - unused */ |
| | 2102 | 1, /* 0x39 - unused */ |
| | 2103 | 1, /* 0x3A - unused */ |
| | 2104 | 1, /* 0x3B - unused */ |
| | 2105 | 1, /* 0x3C - unused */ |
| | 2106 | 1, /* 0x3D - unused */ |
| | 2107 | 1, /* 0x3E - unused */ |
| | 2108 | 1, /* 0x3F - unused */ |
| | 2109 | |
| | 2110 | 1, /* 0x40 - OPC_EQ */ |
| | 2111 | 1, /* 0x41 - OPC_NE */ |
| | 2112 | 1, /* 0x42 - OPC_LT */ |
| | 2113 | 1, /* 0x43 - OPC_LE */ |
| | 2114 | 1, /* 0x44 - OPC_GT */ |
| | 2115 | 1, /* 0x45 - OPC_GE */ |
| | 2116 | |
| | 2117 | 1, /* 0x46 - unused */ |
| | 2118 | 1, /* 0x47 - unused */ |
| | 2119 | 1, /* 0x48 - unused */ |
| | 2120 | 1, /* 0x49 - unused */ |
| | 2121 | 1, /* 0x4A - unused */ |
| | 2122 | 1, /* 0x4B - unused */ |
| | 2123 | 1, /* 0x4C - unused */ |
| | 2124 | 1, /* 0x4D - unused */ |
| | 2125 | 1, /* 0x4E - unused */ |
| | 2126 | 1, /* 0x4F - unused */ |
| | 2127 | |
| | 2128 | 1, /* 0x50 - OPC_RETVAL */ |
| | 2129 | 1, /* 0x51 - OPC_RETNIL */ |
| | 2130 | 1, /* 0x52 - OPC_RETTRUE */ |
| | 2131 | 1, /* 0x53 - unused */ |
| | 2132 | 1, /* 0x54 - OPC_RET */ |
| | 2133 | |
| | 2134 | 1, /* 0x55 - unused */ |
| | 2135 | 1, /* 0x56 - unused */ |
| | 2136 | 1, /* 0x57 - unused */ |
| | 2137 | |
| | 2138 | 6, /* 0x58 - OPC_CALL */ |
| | 2139 | 2, /* 0x59 - OPC_PTRCALL */ |
| | 2140 | |
| | 2141 | 1, /* 0x5A - unused */ |
| | 2142 | 1, /* 0x5B - unused */ |
| | 2143 | 1, /* 0x5C - unused */ |
| | 2144 | 1, /* 0x5D - unused */ |
| | 2145 | 1, /* 0x5E - unused */ |
| | 2146 | 1, /* 0x5F - unused */ |
| | 2147 | |
| | 2148 | 3, /* 0x60 - OPC_GETPROP */ |
| | 2149 | 4, /* 0x61 - OPC_CALLPROP */ |
| | 2150 | 2, /* 0x62 - OPC_PTRCALLPROP */ |
| | 2151 | 3, /* 0x63 - OPC_GETPROPSELF */ |
| | 2152 | 4, /* 0x64 - OPC_CALLPROPSELF */ |
| | 2153 | 2, /* 0x65 - OPC_PTRCALLPROPSELF */ |
| | 2154 | 7, /* 0x66 - OPC_OBJGETPROP */ |
| | 2155 | 8, /* 0x67 - OPC_OBJCALLPROP */ |
| | 2156 | 3, /* 0x68 - OPC_GETPROPDATA */ |
| | 2157 | 1, /* 0x69 - OPC_PTRGETPROPDATA */ |
| | 2158 | 4, /* 0x6A - OPC_GETPROPLCL1 */ |
| | 2159 | 5, /* 0x6B - OPC_CALLPROPLCL1 */ |
| | 2160 | 3, /* 0x6C - OPC_GETPROPR0 */ |
| | 2161 | 4, /* 0x6D - OPC_CALLPROPR0 */ |
| | 2162 | |
| | 2163 | 1, /* 0x6E - unused */ |
| | 2164 | 1, /* 0x6F - unused */ |
| | 2165 | 1, /* 0x70 - unused */ |
| | 2166 | 1, /* 0x71 - unused */ |
| | 2167 | |
| | 2168 | 4, /* 0x72 - OPC_INHERIT */ |
| | 2169 | 2, /* 0x73 - OPC_PTRINHERIT */ |
| | 2170 | 8, /* 0x74 - OPC_EXPINHERIT */ |
| | 2171 | 6, /* 0x75 - OPC_PTREXPINHERIT */ |
| | 2172 | 1, /* 0x76 - OPC_VARARGC */ |
| | 2173 | 4, /* 0x77 - OPC_DELEGATE */ |
| | 2174 | 2, /* 0x78 - OPC_PTRDELEGATE */ |
| | 2175 | |
| | 2176 | 1, /* 0x79 - unused */ |
| | 2177 | 1, /* 0x7A - unused */ |
| | 2178 | 1, /* 0x7B - unused */ |
| | 2179 | 1, /* 0x7C - unused */ |
| | 2180 | 1, /* 0x7D - unused */ |
| | 2181 | 1, /* 0x7E - unused */ |
| | 2182 | 1, /* 0x7F - unused */ |
| | 2183 | |
| | 2184 | 2, /* 0x80 - OPC_GETLCL1 */ |
| | 2185 | 3, /* 0x81 - OPC_GETLCL2 */ |
| | 2186 | 2, /* 0x82 - OPC_GETARG1 */ |
| | 2187 | 3, /* 0x83 - OPC_GETARG2 */ |
| | 2188 | 1, /* 0x84 - OPC_PUSHSELF */ |
| | 2189 | 5, /* 0x85 - OPC_GETDBLCL */ |
| | 2190 | 5, /* 0x86 - OPC_GETDBARG */ |
| | 2191 | 1, /* 0x87 - OPC_GETARGC */ |
| | 2192 | 1, /* 0x88 - OPC_DUP */ |
| | 2193 | 1, /* 0x89 - OPC_DISC */ |
| | 2194 | 2, /* 0x8A - OPC_DISC1 */ |
| | 2195 | 1, /* 0x8B - OPC_GETR0 */ |
| | 2196 | 3, /* 0x8C - OPC_GETDBARGC */ |
| | 2197 | 1, /* 0x8D - OPC_SWAP */ |
| | 2198 | |
| | 2199 | 2, /* 0x8E - OPC_PUSHCTXELE */ |
| | 2200 | |
| | 2201 | 1, /* 0x8F - unused */ |
| | 2202 | |
| | 2203 | 0, /* 0x90 - OPC_SWITCH - variable-size instruction */ |
| | 2204 | 3, /* 0x91 - OPC_JMP */ |
| | 2205 | 3, /* 0x92 - OPC_JT */ |
| | 2206 | 3, /* 0x93 - OPC_JF */ |
| | 2207 | 3, /* 0x94 - OPC_JE */ |
| | 2208 | 3, /* 0x95 - OPC_JNE */ |
| | 2209 | 3, /* 0x96 - OPC_JGT */ |
| | 2210 | 3, /* 0x97 - OPC_JGE */ |
| | 2211 | 3, /* 0x98 - OPC_JLT */ |
| | 2212 | 3, /* 0x99 - OPC_JLE */ |
| | 2213 | 3, /* 0x9A - OPC_JST */ |
| | 2214 | 3, /* 0x9B - OPC_JSF */ |
| | 2215 | 3, /* 0x9C - OPC_LJSR */ |
| | 2216 | 3, /* 0x9D - OPC_LRET */ |
| | 2217 | 3, /* 0x9E - OPC_JNIL */ |
| | 2218 | 3, /* 0x9F - OPC_JNOTNIL */ |
| | 2219 | 3, /* 0xA0 - OPC_JR0T */ |
| | 2220 | 3, /* 0xA1 - OPC_JR0F */ |
| | 2221 | |
| | 2222 | 1, /* 0xA2 - unused */ |
| | 2223 | 1, /* 0xA3 - unused */ |
| | 2224 | 1, /* 0xA4 - unused */ |
| | 2225 | 1, /* 0xA5 - unused */ |
| | 2226 | 1, /* 0xA6 - unused */ |
| | 2227 | 1, /* 0xA7 - unused */ |
| | 2228 | 1, /* 0xA8 - unused */ |
| | 2229 | 1, /* 0xA9 - unused */ |
| | 2230 | 1, /* 0xAA - unused */ |
| | 2231 | 1, /* 0xAB - unused */ |
| | 2232 | 1, /* 0xAC - unused */ |
| | 2233 | 1, /* 0xAD - unused */ |
| | 2234 | 1, /* 0xAE - unused */ |
| | 2235 | 1, /* 0xAF - unused */ |
| | 2236 | |
| | 2237 | 5, /* 0xB0 - OPC_SAY */ |
| | 2238 | 3, /* 0xB1 - OPC_BUILTIN_A */ |
| | 2239 | 3, /* 0xB2 - OPC_BUILTIN_B */ |
| | 2240 | 3, /* 0xB3 - OPC_BUILTIN_C */ |
| | 2241 | 3, /* 0xB4 - OPC_BUILTIN_D */ |
| | 2242 | 3, /* 0xB5 - OPC_BUILTIN1 */ |
| | 2243 | 4, /* 0xB6 - OPC_BUILTIN2 */ |
| | 2244 | 0, /* 0xB7 - OPC_CALLEXT (reserved; not currently implemented) */ |
| | 2245 | 1, /* 0xB8 - OPC_THROW */ |
| | 2246 | 1, /* 0xB9 - OPC_SAYVAL */ |
| | 2247 | |
| | 2248 | 1, /* 0xBA - OPC_INDEX */ |
| | 2249 | 3, /* 0xBB - OPC_IDXLCL1INT8 */ |
| | 2250 | 2, /* 0xBC - OPC_IDXLINT8 */ |
| | 2251 | |
| | 2252 | 1, /* 0xBD - unused */ |
| | 2253 | 1, /* 0xBE - unused */ |
| | 2254 | 1, /* 0xBF - unused */ |
| | 2255 | |
| | 2256 | 3, /* 0xC0 - OPC_NEW1 */ |
| | 2257 | 5, /* 0xC1 - OPC_NEW2 */ |
| | 2258 | 3, /* 0xC2 - OPC_TRNEW1 */ |
| | 2259 | 5, /* 0xC3 - OPC_TRNEW2 */ |
| | 2260 | |
| | 2261 | 1, /* 0xC4 - unused */ |
| | 2262 | 1, /* 0xC5 - unused */ |
| | 2263 | 1, /* 0xC6 - unused */ |
| | 2264 | 1, /* 0xC7 - unused */ |
| | 2265 | 1, /* 0xC8 - unused */ |
| | 2266 | 1, /* 0xC9 - unused */ |
| | 2267 | 1, /* 0xCA - unused */ |
| | 2268 | 1, /* 0xCB - unused */ |
| | 2269 | 1, /* 0xCC - unused */ |
| | 2270 | 1, /* 0xCD - unused */ |
| | 2271 | 1, /* 0xCE - unused */ |
| | 2272 | 1, /* 0xCF - unused */ |
| | 2273 | |
| | 2274 | 3, /* 0xD0 - OPC_INCLCL */ |
| | 2275 | 3, /* 0xD1 - OPC_DECLCL */ |
| | 2276 | 3, /* 0xD2 - OPC_ADDILCL1 */ |
| | 2277 | 7, /* 0xD3 - OPC_ADDILCL4 */ |
| | 2278 | 3, /* 0xD4 - OPC_ADDTOLCL */ |
| | 2279 | 3, /* 0xD5 - OPC_SUBFROMLCL */ |
| | 2280 | 2, /* 0xD6 - OPC_ZEROLCL1 */ |
| | 2281 | 3, /* 0xD7 - OPC_ZEROLCL2 */ |
| | 2282 | 2, /* 0xD8 - OPC_NILLCL1 */ |
| | 2283 | 3, /* 0xD9 - OPC_NILLCL2 */ |
| | 2284 | 2, /* 0xDA - OPC_ONELCL1 */ |
| | 2285 | 3, /* 0xDB - OPC_ONELCL2 */ |
| | 2286 | |
| | 2287 | 1, /* 0xDC - unused */ |
| | 2288 | 1, /* 0xDD - unused */ |
| | 2289 | 1, /* 0xDE - unused */ |
| | 2290 | 1, /* 0xDF - unused */ |
| | 2291 | |
| | 2292 | 2, /* 0xE0 - OPC_SETLCL1 */ |
| | 2293 | 3, /* 0xE1 - OPC_SETLCL2 */ |
| | 2294 | 2, /* 0xE2 - OPC_SETARG1 */ |
| | 2295 | 3, /* 0xE3 - OPC_SETARG2 */ |
| | 2296 | 1, /* 0xE4 - OPC_SETIND */ |
| | 2297 | 3, /* 0xE5 - OPC_SETPROP */ |
| | 2298 | 1, /* 0xE6 - OPC_PTRSETPROP */ |
| | 2299 | 3, /* 0xE7 - OPC_SETPROPSELF */ |
| | 2300 | 7, /* 0xE8 - OPC_OBJSETPROP */ |
| | 2301 | 5, /* 0xE9 - OPC_SETDBLCL */ |
| | 2302 | 5, /* 0xEA - OPC_SETDBARG */ |
| | 2303 | |
| | 2304 | 1, /* 0xEB - OPC_SETSELF */ |
| | 2305 | 1, /* 0xEC - OPC_LOADCTX */ |
| | 2306 | 1, /* 0xED - OPC_STORECTX */ |
| | 2307 | 2, /* 0xEE - OPC_SETLCL1R0 */ |
| | 2308 | 3, /* 0xEF - OPC_SETINDLCL1I8 */ |
| | 2309 | |
| | 2310 | 1, /* 0xF0 - unused */ |
| | 2311 | |
| | 2312 | 1, /* 0xF1 - OPC_BP */ |
| | 2313 | 1, /* 0xF2 - OPC_NOP */ |
| | 2314 | |
| | 2315 | 1, /* 0xF3 - unused */ |
| | 2316 | 1, /* 0xF4 - unused */ |
| | 2317 | 1, /* 0xF5 - unused */ |
| | 2318 | 1, /* 0xF6 - unused */ |
| | 2319 | 1, /* 0xF7 - unused */ |
| | 2320 | 1, /* 0xF8 - unused */ |
| | 2321 | 1, /* 0xF9 - unused */ |
| | 2322 | 1, /* 0xFA - unused */ |
| | 2323 | 1, /* 0xFB - unused */ |
| | 2324 | 1, /* 0xFC - unused */ |
| | 2325 | 1, /* 0xFD - unused */ |
| | 2326 | 1, /* 0xFE - unused */ |
| | 2327 | 255, /* 0xFF - unused */ |
| | 2328 | }; |
| | 2329 | |
| | 2330 | /* |
| | 2331 | * scan the code stream starting at the given offset, continuing |
| | 2332 | * through the current offset |
| | 2333 | */ |
| | 2334 | prv_op = OPC_NOP; |
| | 2335 | for (ofs = start_ofs, end_ofs = str->get_ofs() ; ofs < end_ofs ; ) |
| | 2336 | { |
| | 2337 | uchar op; |
| | 2338 | ulong target_ofs; |
| | 2339 | ulong orig_target_ofs; |
| | 2340 | uchar target_op; |
| | 2341 | int done; |
| | 2342 | int chain_len; |
| | 2343 | |
| | 2344 | /* check the byte code instruction at the current location */ |
| | 2345 | switch(op = str->get_byte_at(ofs)) |
| | 2346 | { |
| | 2347 | case OPC_RETVAL: |
| | 2348 | /* |
| | 2349 | * If our previous opcode was PUSHTRUE or PUSHNIL, we can |
| | 2350 | * replace the previous opcode with RETTRUE or RETNIL. This |
| | 2351 | * sequence can occur when we generate conditional code that |
| | 2352 | * returns a value; in such cases, we sometimes can't elide |
| | 2353 | * the PUSHx/RETVAL sequence during the original code |
| | 2354 | * generation because the RETVAL itself is the target of a |
| | 2355 | * label and thus must be retained as a separate instruction. |
| | 2356 | * Converting the PUSHTRUE or PUSHNIL here won't do any harm, |
| | 2357 | * as we'll still leave the RETVAL as a separate instruction. |
| | 2358 | * Likewise, if the previous instruction was GET_R0, we can |
| | 2359 | * change it to a simple RET. |
| | 2360 | */ |
| | 2361 | switch(prv_op) |
| | 2362 | { |
| | 2363 | case OPC_PUSHTRUE: |
| | 2364 | /* convert the PUSHTRUE to a RETTRUE */ |
| | 2365 | str->write_at(ofs - 1, OPC_RETTRUE); |
| | 2366 | break; |
| | 2367 | |
| | 2368 | case OPC_PUSHNIL: |
| | 2369 | /* convert the PUSHNIL to a RETNIL */ |
| | 2370 | str->write_at(ofs - 1, OPC_RETNIL); |
| | 2371 | break; |
| | 2372 | |
| | 2373 | case OPC_GETR0: |
| | 2374 | /* convert the GETR0 to a RET */ |
| | 2375 | str->write_at(ofs - 1, OPC_RET); |
| | 2376 | break; |
| | 2377 | } |
| | 2378 | |
| | 2379 | /* skip the RETVAL */ |
| | 2380 | ofs += 1; |
| | 2381 | break; |
| | 2382 | |
| | 2383 | case OPC_PUSHSTRI: |
| | 2384 | /* |
| | 2385 | * push in-line string: we have a UINT2 operand giving the |
| | 2386 | * length in bytes of the string, followed by the bytes of the |
| | 2387 | * string, so read the uint2 and then skip that amount plus |
| | 2388 | * three additional bytes (one for the opcode, two for the |
| | 2389 | * uint2 itself) |
| | 2390 | */ |
| | 2391 | ofs += 3 + str->readu2_at(ofs+1); |
| | 2392 | break; |
| | 2393 | |
| | 2394 | case OPC_SWITCH: |
| | 2395 | /* |
| | 2396 | * Switch: we have a UINT2 giving the number of cases, |
| | 2397 | * followed by the cases, followed by an INT2; each case |
| | 2398 | * consists of a DATAHOLDER plus a UINT2, for a total of 7 |
| | 2399 | * bytes. The total is thus 5 bytes (the opcode, the case |
| | 2400 | * count UINT2, the final INT2) plus 7 bytes times the number |
| | 2401 | * of cases. |
| | 2402 | */ |
| | 2403 | ofs += 5 + 7*str->readu2_at(ofs+1); |
| | 2404 | break; |
| | 2405 | |
| | 2406 | case OPC_JMP: |
| | 2407 | /* |
| | 2408 | * Unconditional jump: check for a jump to a RETURN of any |
| | 2409 | * kind or a THROW. If the destination consists of either of |
| | 2410 | * those, replace the JMP with the target instruction. If the |
| | 2411 | * destination is an unconditional JMP, iteratively check its |
| | 2412 | * destination. |
| | 2413 | */ |
| | 2414 | orig_target_ofs = target_ofs = ofs + 1 + str->read2_at(ofs + 1); |
| | 2415 | |
| | 2416 | /* |
| | 2417 | * Iterate through any chain of JMP's we find. Abort if we |
| | 2418 | * try following a chain longer than 20 jumps, in case we |
| | 2419 | * should encounter any circular chains. |
| | 2420 | */ |
| | 2421 | for (done = FALSE, chain_len = 0 ; !done && chain_len < 20 ; |
| | 2422 | ++chain_len) |
| | 2423 | { |
| | 2424 | switch(target_op = str->get_byte_at(target_ofs)) |
| | 2425 | { |
| | 2426 | case OPC_RETVAL: |
| | 2427 | /* |
| | 2428 | * Check for a special sequence that we can optimize |
| | 2429 | * even better than the usual. If we have a GETR0 |
| | 2430 | * followed by a JMP to a RETVAL, then we can |
| | 2431 | * eliminate the JMP *and* the GETR0, and just convert |
| | 2432 | * the GETR0 to a RET. |
| | 2433 | */ |
| | 2434 | if (prv_op == OPC_GETR0) |
| | 2435 | { |
| | 2436 | /* |
| | 2437 | * The GETR0 is the byte before the original JMP: |
| | 2438 | * simply replace it with a RET. Note that we can |
| | 2439 | * leave the original jump intact, in case anyone |
| | 2440 | * else is pointing to it. |
| | 2441 | */ |
| | 2442 | str->write_at(ofs - 1, OPC_RET); |
| | 2443 | |
| | 2444 | /* we're done iterating the chain of jumps-to-jumps */ |
| | 2445 | done = TRUE; |
| | 2446 | } |
| | 2447 | else |
| | 2448 | { |
| | 2449 | /* handle the same as any return instruction */ |
| | 2450 | goto any_RET; |
| | 2451 | } |
| | 2452 | break; |
| | 2453 | |
| | 2454 | case OPC_RETNIL: |
| | 2455 | case OPC_RETTRUE: |
| | 2456 | case OPC_RET: |
| | 2457 | case OPC_THROW: |
| | 2458 | any_RET: |
| | 2459 | /* |
| | 2460 | * it's a THROW or RETURN of some kind - simply copy |
| | 2461 | * it to the current slot; write NOP's over our jump |
| | 2462 | * offset operand, to make sure the code continues to |
| | 2463 | * be deterministically readable |
| | 2464 | */ |
| | 2465 | str->write_at(ofs, target_op); |
| | 2466 | str->write_at(ofs + 1, (uchar)OPC_NOP); |
| | 2467 | str->write_at(ofs + 2, (uchar)OPC_NOP); |
| | 2468 | |
| | 2469 | /* we're done iterating the chain of jumps-to-jumps */ |
| | 2470 | done = TRUE; |
| | 2471 | break; |
| | 2472 | |
| | 2473 | case OPC_JMP: |
| | 2474 | /* |
| | 2475 | * We're jumping to another jump - there's no reason |
| | 2476 | * to stop at the intermediate jump instruction, since |
| | 2477 | * we can simply jump directly to the destination |
| | 2478 | * address. Calculate the new target address, and |
| | 2479 | * continue iterating, in case this jumps to something |
| | 2480 | * we can further optimize away. |
| | 2481 | */ |
| | 2482 | target_ofs = target_ofs + 1 |
| | 2483 | + str->read2_at(target_ofs + 1); |
| | 2484 | |
| | 2485 | /* |
| | 2486 | * if it's a jump to the original location, it must be |
| | 2487 | * some unreachable code that generated a circular |
| | 2488 | * jump; ignore it in this case |
| | 2489 | */ |
| | 2490 | if (target_ofs == ofs) |
| | 2491 | done = TRUE; |
| | 2492 | |
| | 2493 | /* proceed */ |
| | 2494 | break; |
| | 2495 | |
| | 2496 | default: |
| | 2497 | /* |
| | 2498 | * For anything else, we're done with any chain of |
| | 2499 | * jumps to jumps. If we indeed found a new target |
| | 2500 | * address, rewrite the original JMP instruction so |
| | 2501 | * that it jumps directly to the end of the chain |
| | 2502 | * rather than going through the intermediate jumps. |
| | 2503 | */ |
| | 2504 | if (target_ofs != orig_target_ofs) |
| | 2505 | str->write2_at(ofs + 1, target_ofs - (ofs + 1)); |
| | 2506 | |
| | 2507 | /* we're done iterating */ |
| | 2508 | done = TRUE; |
| | 2509 | break; |
| | 2510 | } |
| | 2511 | } |
| | 2512 | |
| | 2513 | /* whatever happened, skip past the jump */ |
| | 2514 | ofs += 3; |
| | 2515 | |
| | 2516 | /* done */ |
| | 2517 | break; |
| | 2518 | |
| | 2519 | case OPC_JT: |
| | 2520 | case OPC_JF: |
| | 2521 | case OPC_JE: |
| | 2522 | case OPC_JNE: |
| | 2523 | case OPC_JGT: |
| | 2524 | case OPC_JGE: |
| | 2525 | case OPC_JLT: |
| | 2526 | case OPC_JLE: |
| | 2527 | case OPC_JST: |
| | 2528 | case OPC_JSF: |
| | 2529 | case OPC_JNIL: |
| | 2530 | case OPC_JNOTNIL: |
| | 2531 | case OPC_JR0T: |
| | 2532 | case OPC_JR0F: |
| | 2533 | /* |
| | 2534 | * We have a jump (conditional or otherwise). Check the |
| | 2535 | * target instruction to see if it's an unconditional jump; if |
| | 2536 | * so, then we can jump straight to the target of the second |
| | 2537 | * jump, since there's no reason to stop at the intermediate |
| | 2538 | * jump instruction on our way to the final destination. Make |
| | 2539 | * this check iteratively, so that we eliminate any chain of |
| | 2540 | * jumps to jumps and land at our final non-jump instruction |
| | 2541 | * in one go. |
| | 2542 | */ |
| | 2543 | orig_target_ofs = target_ofs = ofs + 1 + str->read2_at(ofs + 1); |
| | 2544 | for (done = FALSE, chain_len = 0 ; !done && chain_len < 20 ; |
| | 2545 | ++chain_len) |
| | 2546 | { |
| | 2547 | uchar target_op; |
| | 2548 | |
| | 2549 | /* get the target opcode */ |
| | 2550 | target_op = str->get_byte_at(target_ofs); |
| | 2551 | |
| | 2552 | /* |
| | 2553 | * if the target is an unconditional JMP, we can retarget |
| | 2554 | * the original instruction to jump directly to the target |
| | 2555 | * of the target JMP, bypassing the target JMP entirely and |
| | 2556 | * thus avoiding some unnecessary work at run-time |
| | 2557 | */ |
| | 2558 | if (target_op == OPC_JMP) |
| | 2559 | { |
| | 2560 | /* |
| | 2561 | * retarget the original jump to go directly to the |
| | 2562 | * target of the target JMP |
| | 2563 | */ |
| | 2564 | target_ofs = target_ofs + 1 |
| | 2565 | + str->read2_at(target_ofs + 1); |
| | 2566 | |
| | 2567 | /* |
| | 2568 | * continue scanning for more opportunities, as the new |
| | 2569 | * target could also point to something we can bypass |
| | 2570 | */ |
| | 2571 | continue; |
| | 2572 | } |
| | 2573 | |
| | 2574 | /* |
| | 2575 | * Certain combinations are special. If the original |
| | 2576 | * opcode was a JST or JSF, and the target is a JT or JF, |
| | 2577 | * we can recode the sequence so that the original opcode |
| | 2578 | * turns into a more efficient JT or JF and jumps directly |
| | 2579 | * past the JT or JF. If we have a JST or JSF jumping to a |
| | 2580 | * JST or JSF, we can also recode that sequence to bypass |
| | 2581 | * the second jump. In both cases, we can recode the |
| | 2582 | * sequence because the original jump will unequivocally |
| | 2583 | * determine the behavior at the target jump in such a way |
| | 2584 | * that we can compact the sequence into a single jump. |
| | 2585 | */ |
| | 2586 | switch(op) |
| | 2587 | { |
| | 2588 | case OPC_JSF: |
| | 2589 | /* |
| | 2590 | * the original is a JSF: we can recode a jump to a |
| | 2591 | * JSF, JST, JF, or JT |
| | 2592 | */ |
| | 2593 | switch(target_op) |
| | 2594 | { |
| | 2595 | case OPC_JSF: |
| | 2596 | /* |
| | 2597 | * We're jumping to another JSF. Since the |
| | 2598 | * original jump will only reach the target jump if |
| | 2599 | * the value on the top of the stack is false, and |
| | 2600 | * will then leave this same value on the stack to |
| | 2601 | * be tested again with the target JSF, we know the |
| | 2602 | * target JSF will perform its jump and leave the |
| | 2603 | * stack unchanged again. So, we can simply |
| | 2604 | * retarget the original jump to the target of the |
| | 2605 | * target JSF. |
| | 2606 | */ |
| | 2607 | target_ofs = target_ofs + 1 |
| | 2608 | + str->read2_at(target_ofs + 1); |
| | 2609 | |
| | 2610 | /* keep scanning for additional opportunities */ |
| | 2611 | break; |
| | 2612 | |
| | 2613 | case OPC_JST: |
| | 2614 | case OPC_JT: |
| | 2615 | /* |
| | 2616 | * We're jumping to a JST or a JT. Since the JSF |
| | 2617 | * will only reach the JST/JT on a false value, we |
| | 2618 | * know the JST/JT will NOT jump - we know for a |
| | 2619 | * fact it will pop the non-true stack element and |
| | 2620 | * proceed without jumping. Therefore, we can |
| | 2621 | * avoid saving the value from the original JSF, |
| | 2622 | * which means we can recode the original as the |
| | 2623 | * simpler JF (which doesn't bother saving the |
| | 2624 | * false value), and jump on false directly to the |
| | 2625 | * instruction after the target JST/JT. |
| | 2626 | */ |
| | 2627 | str->write_at(ofs, (uchar)OPC_JF); |
| | 2628 | op = OPC_JF; |
| | 2629 | |
| | 2630 | /* jump to the instruction after the target JST/JT */ |
| | 2631 | target_ofs += 3; |
| | 2632 | |
| | 2633 | /* keep looking for more jumps */ |
| | 2634 | break; |
| | 2635 | |
| | 2636 | case OPC_JF: |
| | 2637 | /* |
| | 2638 | * We're jumping to a JF: we know the JF will jump, |
| | 2639 | * because we had to have - and then save - a false |
| | 2640 | * value for the JSF to reach the JF in the first |
| | 2641 | * place. Since we know for a fact the target JF |
| | 2642 | * will remove the false value and jump to its |
| | 2643 | * target, we can bypass the target JF by recoding |
| | 2644 | * the original instruction as a simpler JF and |
| | 2645 | * jumping directly to the target of the target JF. |
| | 2646 | */ |
| | 2647 | str->write_at(ofs, (uchar)OPC_JF); |
| | 2648 | op = OPC_JF; |
| | 2649 | |
| | 2650 | /* jump to the tartet of the target JF */ |
| | 2651 | target_ofs = target_ofs + 1 |
| | 2652 | + str->read2_at(target_ofs + 1); |
| | 2653 | |
| | 2654 | /* keep scanning for more jumps */ |
| | 2655 | break; |
| | 2656 | |
| | 2657 | default: |
| | 2658 | /* can't make any assumptions about other targets */ |
| | 2659 | done = TRUE; |
| | 2660 | break; |
| | 2661 | } |
| | 2662 | break; |
| | 2663 | |
| | 2664 | case OPC_JST: |
| | 2665 | /* |
| | 2666 | * the original is a JST: recode it if the target is a |
| | 2667 | * JSF, JST, JF, or JT |
| | 2668 | */ |
| | 2669 | switch(target_op) |
| | 2670 | { |
| | 2671 | case OPC_JST: |
| | 2672 | /* JST jumping to JST: jump to the target's target */ |
| | 2673 | target_ofs = target_ofs + 1 |
| | 2674 | + str->read2_at(target_ofs + 1); |
| | 2675 | |
| | 2676 | /* keep looking */ |
| | 2677 | break; |
| | 2678 | |
| | 2679 | case OPC_JSF: |
| | 2680 | case OPC_JF: |
| | 2681 | /* |
| | 2682 | * JST jumping to JSF/JF: the JSF/JF will |
| | 2683 | * definitely pop the stack and not jump (since the |
| | 2684 | * original JST will have left a true value on the |
| | 2685 | * stack), so we can recode the JST as a more |
| | 2686 | * efficient JT and jump to the instruction after |
| | 2687 | * the JSF/JF target |
| | 2688 | */ |
| | 2689 | str->write_at(ofs, (uchar)OPC_JT); |
| | 2690 | op = OPC_JT; |
| | 2691 | |
| | 2692 | /* jump to the instruction after the target */ |
| | 2693 | target_ofs += 3; |
| | 2694 | |
| | 2695 | /* keep looking */ |
| | 2696 | break; |
| | 2697 | |
| | 2698 | case OPC_JT: |
| | 2699 | /* |
| | 2700 | * JST jumping to JT: the JT will definitely pop |
| | 2701 | * and jump, so we can recode the original as a |
| | 2702 | * simpler JT and jump to the target's target |
| | 2703 | */ |
| | 2704 | str->write_at(ofs, (uchar)OPC_JT); |
| | 2705 | op = OPC_JT; |
| | 2706 | |
| | 2707 | /* jump to the target of the target */ |
| | 2708 | target_ofs = target_ofs + 1 |
| | 2709 | + str->read2_at(target_ofs + 1); |
| | 2710 | |
| | 2711 | /* keep scanning */ |
| | 2712 | break; |
| | 2713 | |
| | 2714 | default: |
| | 2715 | /* can't make any assumptions about other targets */ |
| | 2716 | done = TRUE; |
| | 2717 | break; |
| | 2718 | } |
| | 2719 | break; |
| | 2720 | |
| | 2721 | default: |
| | 2722 | /* |
| | 2723 | * we can't make assumptions about anything else, so |
| | 2724 | * we've come to the end of the road - stop scanning |
| | 2725 | */ |
| | 2726 | done = TRUE; |
| | 2727 | break; |
| | 2728 | } |
| | 2729 | } |
| | 2730 | |
| | 2731 | /* |
| | 2732 | * if we found a chain of jumps, replace our original jump |
| | 2733 | * target with the final jump target, bypassing the |
| | 2734 | * intermediate jumps |
| | 2735 | */ |
| | 2736 | if (target_ofs != orig_target_ofs) |
| | 2737 | str->write2_at(ofs + 1, target_ofs - (ofs + 1)); |
| | 2738 | |
| | 2739 | /* skip past the jump */ |
| | 2740 | ofs += 3; |
| | 2741 | |
| | 2742 | /* done */ |
| | 2743 | break; |
| | 2744 | |
| | 2745 | default: |
| | 2746 | /* |
| | 2747 | * everything else is a fixed-size instruction, so simply |
| | 2748 | * consult our table of instruction lengths to determine the |
| | 2749 | * offset of the next instruction |
| | 2750 | */ |
| | 2751 | ofs += op_siz[op]; |
| | 2752 | break; |
| | 2753 | } |
| | 2754 | |
| | 2755 | /* remember the preceding opcode */ |
| | 2756 | prv_op = op; |
| | 2757 | } |
| | 2758 | } |
| | 2759 | |
| | 2760 | /* ------------------------------------------------------------------------ */ |
| | 2761 | /* |
| | 2762 | * Generic T3 node |
| | 2763 | */ |
| | 2764 | |
| | 2765 | /* |
| | 2766 | * generate a jump-ahead instruction, returning a new label which serves |
| | 2767 | * as the jump destination |
| | 2768 | */ |
| | 2769 | CTcCodeLabel *CTcPrsNode::gen_jump_ahead(uchar opc) |
| | 2770 | { |
| | 2771 | CTcCodeLabel *lbl; |
| | 2772 | |
| | 2773 | /* |
| | 2774 | * check to see if we should suppress the jump for peephole |
| | 2775 | * optimization |
| | 2776 | */ |
| | 2777 | if (G_cg->can_skip_op()) |
| | 2778 | return 0; |
| | 2779 | |
| | 2780 | /* emit the opcode */ |
| | 2781 | G_cg->write_op(opc); |
| | 2782 | |
| | 2783 | /* allocate a new label */ |
| | 2784 | lbl = G_cs->new_label_fwd(); |
| | 2785 | |
| | 2786 | /* |
| | 2787 | * write the forward offset to the label (this will generate a fixup |
| | 2788 | * record attached to the label, so that we'll come back and fix it |
| | 2789 | * up when the real offset is known) |
| | 2790 | */ |
| | 2791 | G_cs->write_ofs2(lbl, 0); |
| | 2792 | |
| | 2793 | /* return the forward label */ |
| | 2794 | return lbl; |
| | 2795 | } |
| | 2796 | |
| | 2797 | /* |
| | 2798 | * Allocate a new label at the current write position |
| | 2799 | */ |
| | 2800 | CTcCodeLabel *CTcPrsNode::new_label_here() |
| | 2801 | { |
| | 2802 | /* |
| | 2803 | * suppress any peephole optimizations at this point -- someone |
| | 2804 | * could jump directly to this instruction, so we can't combine an |
| | 2805 | * instruction destined for this point with anything previous |
| | 2806 | */ |
| | 2807 | G_cg->clear_peephole(); |
| | 2808 | |
| | 2809 | /* create and return a label at the current position */ |
| | 2810 | return G_cs->new_label_here(); |
| | 2811 | } |
| | 2812 | |
| | 2813 | /* |
| | 2814 | * define the position of a code label |
| | 2815 | */ |
| | 2816 | void CTcPrsNode::def_label_pos(CTcCodeLabel *lbl) |
| | 2817 | { |
| | 2818 | /* if the label is null, ignore it */ |
| | 2819 | if (lbl == 0) |
| | 2820 | return; |
| | 2821 | |
| | 2822 | /* |
| | 2823 | * try eliminating a jump-to-next-instruction sequence: if the last |
| | 2824 | * opcode was a JMP to this label, remove the last instruction |
| | 2825 | * entirely |
| | 2826 | */ |
| | 2827 | if (G_cg->get_last_op() == OPC_JMP |
| | 2828 | && G_cs->has_fixup_at_ofs(lbl, G_cs->get_ofs() - 2)) |
| | 2829 | { |
| | 2830 | /* remove the fixup pointing to the preceding JMP */ |
| | 2831 | G_cs->remove_fixup_at_ofs(lbl, G_cs->get_ofs() - 2); |
| | 2832 | |
| | 2833 | /* the JMP is unnecessary - remove it */ |
| | 2834 | G_cg->remove_last_jmp(); |
| | 2835 | } |
| | 2836 | |
| | 2837 | /* define the label position and apply the fixup */ |
| | 2838 | G_cs->def_label_pos(lbl); |
| | 2839 | |
| | 2840 | /* |
| | 2841 | * whenever we define a label, we must suppress any peephole |
| | 2842 | * optimizations at this point - someone could jump directly to this |
| | 2843 | * instruction, so we can't combine an instruction destined for this |
| | 2844 | * point with anything previous |
| | 2845 | */ |
| | 2846 | G_cg->clear_peephole(); |
| | 2847 | } |
| | 2848 | |
| | 2849 | /* |
| | 2850 | * Generate code for an if-else conditional test. The default |
| | 2851 | * implementation is to evaluate the expression, and jump to the false |
| | 2852 | * branch if the expression is false (or jump to the true part if the |
| | 2853 | * expression is true and there's no false part). |
| | 2854 | */ |
| | 2855 | void CTcPrsNode::gen_code_cond(CTcCodeLabel *then_label, |
| | 2856 | CTcCodeLabel *else_label) |
| | 2857 | { |
| | 2858 | /* generate our expression code */ |
| | 2859 | gen_code(FALSE, TRUE); |
| | 2860 | |
| | 2861 | /* |
| | 2862 | * if we have a 'then' part, jump to the 'then' part if the condition |
| | 2863 | * is true; otherwise, jump to the 'else' part if the condition is |
| | 2864 | * false |
| | 2865 | */ |
| | 2866 | if (then_label != 0) |
| | 2867 | { |
| | 2868 | /* we have a 'then' part, so jump if true to the 'then' part */ |
| | 2869 | G_cg->write_op(OPC_JT); |
| | 2870 | G_cs->write_ofs2(then_label, 0); |
| | 2871 | } |
| | 2872 | else |
| | 2873 | { |
| | 2874 | /* we have an 'else' part, so jump if false to the 'else' */ |
| | 2875 | G_cg->write_op(OPC_JF); |
| | 2876 | G_cs->write_ofs2(else_label, 0); |
| | 2877 | } |
| | 2878 | |
| | 2879 | /* the JF or JT pops an element off the stack */ |
| | 2880 | G_cg->note_pop(); |
| | 2881 | } |
| | 2882 | |
| | 2883 | /* |
| | 2884 | * generate code for assignment to this node |
| | 2885 | */ |
| | 2886 | int CTcPrsNode::gen_code_asi(int, tc_asitype_t, CTcPrsNode *, |
| | 2887 | int ignore_error) |
| | 2888 | { |
| | 2889 | /* |
| | 2890 | * if ignoring errors, the caller is trying to assign if possible |
| | 2891 | * but doesn't require it to be possible; simply return false to |
| | 2892 | * indicate that nothing happened if this is the case |
| | 2893 | */ |
| | 2894 | if (ignore_error) |
| | 2895 | return FALSE; |
| | 2896 | |
| | 2897 | /* we should never get here - throw an internal error */ |
| | 2898 | G_tok->throw_internal_error(TCERR_GEN_BAD_LVALUE); |
| | 2899 | AFTER_ERR_THROW(return FALSE;) |
| | 2900 | } |
| | 2901 | |
| | 2902 | /* |
| | 2903 | * generate code for taking the address of this node |
| | 2904 | */ |
| | 2905 | void CTcPrsNode::gen_code_addr() |
| | 2906 | { |
| | 2907 | /* we should never get here - throw an internal error */ |
| | 2908 | G_tok->throw_internal_error(TCERR_GEN_BAD_ADDR); |
| | 2909 | } |
| | 2910 | |
| | 2911 | /* |
| | 2912 | * Generate code to call the expression as a function or method. |
| | 2913 | */ |
| | 2914 | void CTcPrsNode::gen_code_call(int discard, int argc, int varargs) |
| | 2915 | { |
| | 2916 | /* function/method calls are never valid in speculative mode */ |
| | 2917 | if (G_cg->is_speculative()) |
| | 2918 | err_throw(VMERR_BAD_SPEC_EVAL); |
| | 2919 | |
| | 2920 | /* |
| | 2921 | * For default nodes, assume that the result of evaluating the |
| | 2922 | * expression contained in the node is a method or function pointer. |
| | 2923 | * First, generate code to evaluate the expression, which should |
| | 2924 | * yield an appropriate pointer value. |
| | 2925 | */ |
| | 2926 | gen_code(FALSE, FALSE); |
| | 2927 | |
| | 2928 | /* |
| | 2929 | * if we have a varargs list, modify the call instruction that |
| | 2930 | * follows to make it a varargs call |
| | 2931 | */ |
| | 2932 | if (varargs) |
| | 2933 | { |
| | 2934 | /* swap the top of the stack to get the arg counter back on top */ |
| | 2935 | G_cg->write_op(OPC_SWAP); |
| | 2936 | |
| | 2937 | /* write the varargs modifier */ |
| | 2938 | G_cg->write_op(OPC_VARARGC); |
| | 2939 | } |
| | 2940 | |
| | 2941 | /* generate an indirect function call through the pointer */ |
| | 2942 | G_cg->write_op(OPC_PTRCALL); |
| | 2943 | G_cs->write((char)argc); |
| | 2944 | |
| | 2945 | /* PTRCALL pops the arguments plus the function pointer */ |
| | 2946 | G_cg->note_pop(argc + 1); |
| | 2947 | |
| | 2948 | /* |
| | 2949 | * if the caller isn't going to discard the return value, push the |
| | 2950 | * result, which is sitting in R0 |
| | 2951 | */ |
| | 2952 | if (!discard) |
| | 2953 | { |
| | 2954 | G_cg->write_op(OPC_GETR0); |
| | 2955 | G_cg->note_push(); |
| | 2956 | } |
| | 2957 | } |
| | 2958 | |
| | 2959 | /* |
| | 2960 | * Generate code for operator 'new' applied to this node |
| | 2961 | */ |
| | 2962 | void CTcPrsNode::gen_code_new(int, int, int, int, int) |
| | 2963 | { |
| | 2964 | /* operator 'new' cannot be applied to a default node */ |
| | 2965 | G_tok->log_error(TCERR_INVAL_NEW_EXPR); |
| | 2966 | } |
| | 2967 | |
| | 2968 | /* |
| | 2969 | * Generate code for a member evaluation |
| | 2970 | */ |
| | 2971 | void CTcPrsNode::gen_code_member(int discard, |
| | 2972 | CTcPrsNode *prop_expr, int prop_is_expr, |
| | 2973 | int argc, int varargs) |
| | 2974 | { |
| | 2975 | /* evaluate my own expression to yield the object value */ |
| | 2976 | gen_code(FALSE, FALSE); |
| | 2977 | |
| | 2978 | /* if we have an argument counter, put it back on top */ |
| | 2979 | if (varargs) |
| | 2980 | G_cg->write_op(OPC_SWAP); |
| | 2981 | |
| | 2982 | /* use the generic code to generate the rest */ |
| | 2983 | s_gen_member_rhs(discard, prop_expr, prop_is_expr, argc, varargs); |
| | 2984 | } |
| | 2985 | |
| | 2986 | /* |
| | 2987 | * Generic code to generate the rest of a member expression after the |
| | 2988 | * left side of the '.' has been generated. This can be used for cases |
| | 2989 | * where the left of the '.' is an arbitrary expression, and hence must |
| | 2990 | * be evaluated at run-time. |
| | 2991 | */ |
| | 2992 | void CTcPrsNode::s_gen_member_rhs(int discard, |
| | 2993 | CTcPrsNode *prop_expr, int prop_is_expr, |
| | 2994 | int argc, int varargs) |
| | 2995 | { |
| | 2996 | vm_prop_id_t prop; |
| | 2997 | |
| | 2998 | /* we can't call methods with argument in speculative mode */ |
| | 2999 | if (argc != 0 && G_cg->is_speculative()) |
| | 3000 | err_throw(VMERR_BAD_SPEC_EVAL); |
| | 3001 | |
| | 3002 | /* get or generate the property ID value */ |
| | 3003 | prop = prop_expr->gen_code_propid(FALSE, prop_is_expr); |
| | 3004 | |
| | 3005 | /* |
| | 3006 | * if we got a property ID, generate a simple GETPROP or CALLPROP |
| | 3007 | * instruction; otherwise, generate a PTRCALLPROP instruction |
| | 3008 | */ |
| | 3009 | if (prop != VM_INVALID_PROP) |
| | 3010 | { |
| | 3011 | /* |
| | 3012 | * we have a constant property ID - generate a GETPROP or |
| | 3013 | * CALLPROP with the property ID as constant data |
| | 3014 | */ |
| | 3015 | if (argc == 0) |
| | 3016 | { |
| | 3017 | /* no arguments - generate a GETPROP */ |
| | 3018 | G_cg->write_op(G_cg->is_speculative() |
| | 3019 | ? OPC_GETPROPDATA : OPC_GETPROP); |
| | 3020 | G_cs->write_prop_id(prop); |
| | 3021 | |
| | 3022 | /* this pops an object element */ |
| | 3023 | G_cg->note_pop(); |
| | 3024 | } |
| | 3025 | else |
| | 3026 | { |
| | 3027 | /* write the CALLPROP instruction */ |
| | 3028 | G_cg->write_callprop(argc, varargs, prop); |
| | 3029 | } |
| | 3030 | } |
| | 3031 | else |
| | 3032 | { |
| | 3033 | if (G_cg->is_speculative()) |
| | 3034 | { |
| | 3035 | /* |
| | 3036 | * speculative - use PTRGETPROPDATA to ensure we don't cause |
| | 3037 | * any side effects |
| | 3038 | */ |
| | 3039 | G_cg->write_op(OPC_PTRGETPROPDATA); |
| | 3040 | } |
| | 3041 | else |
| | 3042 | { |
| | 3043 | /* |
| | 3044 | * if we have a varargs list, modify the call instruction |
| | 3045 | * that follows to make it a varargs call |
| | 3046 | */ |
| | 3047 | if (varargs) |
| | 3048 | { |
| | 3049 | /* swap to get the arg counter back on top */ |
| | 3050 | G_cg->write_op(OPC_SWAP); |
| | 3051 | |
| | 3052 | /* write the varargs modifier */ |
| | 3053 | G_cg->write_op(OPC_VARARGC); |
| | 3054 | } |
| | 3055 | |
| | 3056 | /* a property pointer is on the stack - write a PTRCALLPROP */ |
| | 3057 | G_cg->write_op(OPC_PTRCALLPROP); |
| | 3058 | G_cs->write((int)argc); |
| | 3059 | } |
| | 3060 | |
| | 3061 | /* |
| | 3062 | * ptrcallprop/ptrgetpropdata removes arguments, the object, and |
| | 3063 | * the property |
| | 3064 | */ |
| | 3065 | G_cg->note_pop(argc + 2); |
| | 3066 | } |
| | 3067 | |
| | 3068 | /* if we're not discarding the result, push it from R0 */ |
| | 3069 | if (!discard) |
| | 3070 | { |
| | 3071 | G_cg->write_op(OPC_GETR0); |
| | 3072 | G_cg->note_push(); |
| | 3073 | } |
| | 3074 | } |
| | 3075 | |
| | 3076 | /* |
| | 3077 | * Generate code to get the property ID of the expression. |
| | 3078 | */ |
| | 3079 | vm_prop_id_t CTcPrsNode::gen_code_propid(int check_only, int is_expr) |
| | 3080 | { |
| | 3081 | /* |
| | 3082 | * simply evaluate the expression normally, anticipating that this |
| | 3083 | * will yield a property ID value at run-time |
| | 3084 | */ |
| | 3085 | if (!check_only) |
| | 3086 | gen_code(FALSE, FALSE); |
| | 3087 | |
| | 3088 | /* tell the caller that there's no constant ID available */ |
| | 3089 | return VM_INVALID_PROP; |
| | 3090 | } |
| | 3091 | |
| | 3092 | /* ------------------------------------------------------------------------ */ |
| | 3093 | /* |
| | 3094 | * "self" |
| | 3095 | */ |
| | 3096 | |
| | 3097 | /* |
| | 3098 | * generate code |
| | 3099 | */ |
| | 3100 | void CTPNSelf::gen_code(int discard, int) |
| | 3101 | { |
| | 3102 | /* it's an error if we're not in a method context */ |
| | 3103 | if (!G_cs->is_self_available()) |
| | 3104 | G_tok->log_error(TCERR_SELF_NOT_AVAIL); |
| | 3105 | |
| | 3106 | /* if we're not discarding the result, push the "self" object */ |
| | 3107 | if (!discard) |
| | 3108 | { |
| | 3109 | G_cg->write_op(OPC_PUSHSELF); |
| | 3110 | G_cg->note_push(); |
| | 3111 | } |
| | 3112 | } |
| | 3113 | |
| | 3114 | /* |
| | 3115 | * evaluate a property |
| | 3116 | */ |
| | 3117 | void CTPNSelf::gen_code_member(int discard, |
| | 3118 | CTcPrsNode *prop_expr, int prop_is_expr, |
| | 3119 | int argc, int varargs) |
| | 3120 | { |
| | 3121 | vm_prop_id_t prop; |
| | 3122 | |
| | 3123 | /* make sure "self" is available */ |
| | 3124 | if (!G_cs->is_self_available()) |
| | 3125 | G_tok->log_error(TCERR_SELF_NOT_AVAIL); |
| | 3126 | |
| | 3127 | /* don't allow arguments in speculative eval mode */ |
| | 3128 | if (argc != 0 && G_cg->is_speculative()) |
| | 3129 | err_throw(VMERR_BAD_SPEC_EVAL); |
| | 3130 | |
| | 3131 | /* generate the property value */ |
| | 3132 | prop = prop_expr->gen_code_propid(FALSE, prop_is_expr); |
| | 3133 | |
| | 3134 | /* |
| | 3135 | * if we got a property ID, generate a simple GETPROPSELF or |
| | 3136 | * CALLPROPSELF; otherwise, generate a PTRCALLPROPSELF |
| | 3137 | */ |
| | 3138 | if (prop != VM_INVALID_PROP) |
| | 3139 | { |
| | 3140 | /* |
| | 3141 | * we have a constant property ID - generate a GETPROPDATA, |
| | 3142 | * GETPROPSELF, or CALLPROPSELF with the property ID as |
| | 3143 | * immediate data |
| | 3144 | */ |
| | 3145 | if (G_cg->is_speculative()) |
| | 3146 | { |
| | 3147 | /* speculative - use GETPROPDATA */ |
| | 3148 | G_cg->write_op(OPC_PUSHSELF); |
| | 3149 | G_cg->write_op(OPC_GETPROPDATA); |
| | 3150 | G_cs->write_prop_id(prop); |
| | 3151 | |
| | 3152 | /* we pushed one element (self) and popped it back off */ |
| | 3153 | G_cg->note_push(); |
| | 3154 | G_cg->note_pop(); |
| | 3155 | } |
| | 3156 | else if (argc == 0) |
| | 3157 | { |
| | 3158 | /* no arguments - generate a GETPROPSELF */ |
| | 3159 | G_cg->write_op(OPC_GETPROPSELF); |
| | 3160 | G_cs->write_prop_id(prop); |
| | 3161 | } |
| | 3162 | else |
| | 3163 | { |
| | 3164 | /* add a varargs modifier if appropriate */ |
| | 3165 | if (varargs) |
| | 3166 | G_cg->write_op(OPC_VARARGC); |
| | 3167 | |
| | 3168 | /* we have arguments - generate a CALLPROPSELF */ |
| | 3169 | G_cg->write_op(OPC_CALLPROPSELF); |
| | 3170 | G_cs->write((char)argc); |
| | 3171 | G_cs->write_prop_id(prop); |
| | 3172 | |
| | 3173 | /* this removes arguments */ |
| | 3174 | G_cg->note_pop(argc); |
| | 3175 | } |
| | 3176 | } |
| | 3177 | else |
| | 3178 | { |
| | 3179 | /* |
| | 3180 | * a property pointer is on the stack - use PTRGETPROPDATA or |
| | 3181 | * PTRCALLPROPSELF, depending on the speculative mode |
| | 3182 | */ |
| | 3183 | if (G_cg->is_speculative()) |
| | 3184 | { |
| | 3185 | /* speculative - use PTRGETPROPDATA after pushing self */ |
| | 3186 | G_cg->write_op(OPC_PUSHSELF); |
| | 3187 | G_cg->write_op(OPC_PTRGETPROPDATA); |
| | 3188 | |
| | 3189 | /* we pushed self then removed self and the property ID */ |
| | 3190 | G_cg->note_push(); |
| | 3191 | G_cg->note_pop(2); |
| | 3192 | } |
| | 3193 | else |
| | 3194 | { |
| | 3195 | /* |
| | 3196 | * if we have a varargs list, modify the call instruction |
| | 3197 | * that follows to make it a varargs call |
| | 3198 | */ |
| | 3199 | if (varargs) |
| | 3200 | { |
| | 3201 | /* swap to get the arg counter back on top */ |
| | 3202 | G_cg->write_op(OPC_SWAP); |
| | 3203 | |
| | 3204 | /* write the varargs modifier */ |
| | 3205 | G_cg->write_op(OPC_VARARGC); |
| | 3206 | } |
| | 3207 | |
| | 3208 | /* a prop pointer is on the stack - write a PTRCALLPROPSELF */ |
| | 3209 | G_cg->write_op(OPC_PTRCALLPROPSELF); |
| | 3210 | G_cs->write((int)argc); |
| | 3211 | |
| | 3212 | /* this removes arguments and the property pointer */ |
| | 3213 | G_cg->note_pop(argc + 1); |
| | 3214 | } |
| | 3215 | } |
| | 3216 | |
| | 3217 | /* if the result is needed, push it */ |
| | 3218 | if (!discard) |
| | 3219 | { |
| | 3220 | G_cg->write_op(OPC_GETR0); |
| | 3221 | G_cg->note_push(); |
| | 3222 | } |
| | 3223 | } |
| | 3224 | |
| | 3225 | |
| | 3226 | /* |
| | 3227 | * generate code for an object before a '.' |
| | 3228 | */ |
| | 3229 | vm_obj_id_t CTPNSelf::gen_code_obj_predot(int *is_self) |
| | 3230 | { |
| | 3231 | /* make sure "self" is available */ |
| | 3232 | if (!G_cs->is_self_available()) |
| | 3233 | G_tok->log_error(TCERR_SELF_NOT_AVAIL); |
| | 3234 | |
| | 3235 | /* tell the caller that this is "self" */ |
| | 3236 | *is_self = TRUE; |
| | 3237 | return VM_INVALID_OBJ; |
| | 3238 | } |
| | 3239 | |
| | 3240 | |
| | 3241 | /* ------------------------------------------------------------------------ */ |
| | 3242 | /* |
| | 3243 | * "replaced" |
| | 3244 | */ |
| | 3245 | |
| | 3246 | /* |
| | 3247 | * evaluate 'replaced' on its own - this simply yields a function pointer |
| | 3248 | * to the modified base code |
| | 3249 | */ |
| | 3250 | void CTPNReplaced::gen_code(int discard, int for_condition) |
| | 3251 | { |
| | 3252 | /* get the modified base function symbol */ |
| | 3253 | CTcSymFunc *mod_base = G_cs->get_code_body()->get_replaced_func(); |
| | 3254 | |
| | 3255 | /* make sure we're in a 'modify func()' context */ |
| | 3256 | if (mod_base == 0) |
| | 3257 | G_tok->log_error(TCERR_REPLACED_NOT_AVAIL); |
| | 3258 | |
| | 3259 | /* this expression yields a pointer to the modified base function */ |
| | 3260 | G_cg->write_op(OPC_PUSHFNPTR); |
| | 3261 | |
| | 3262 | /* add a fixup for the current code location */ |
| | 3263 | if (mod_base != 0) |
| | 3264 | mod_base->add_abs_fixup(G_cs); |
| | 3265 | |
| | 3266 | /* write a placeholder offset - arbitrarily use zero */ |
| | 3267 | G_cs->write4(0); |
| | 3268 | |
| | 3269 | /* note the push */ |
| | 3270 | G_cg->note_push(); |
| | 3271 | } |
| | 3272 | |
| | 3273 | /* |
| | 3274 | * 'replaced()' call - this invokes the modified base code |
| | 3275 | */ |
| | 3276 | void CTPNReplaced::gen_code_call(int discard, int argc, int varargs) |
| | 3277 | { |
| | 3278 | /* get the modified base function symbol */ |
| | 3279 | CTcSymFunc *mod_base = G_cs->get_code_body()->get_replaced_func(); |
| | 3280 | |
| | 3281 | /* make sure we're in a 'modify func()' context */ |
| | 3282 | if (mod_base == 0) |
| | 3283 | G_tok->log_error(TCERR_REPLACED_NOT_AVAIL); |
| | 3284 | |
| | 3285 | /* write the varargs modifier if appropriate */ |
| | 3286 | if (varargs) |
| | 3287 | G_cg->write_op(OPC_VARARGC); |
| | 3288 | |
| | 3289 | /* generate the call instruction and argument count */ |
| | 3290 | G_cg->write_op(OPC_CALL); |
| | 3291 | G_cs->write((char)argc); |
| | 3292 | |
| | 3293 | /* generate a fixup for the call to the modified base code */ |
| | 3294 | if (mod_base != 0) |
| | 3295 | mod_base->add_abs_fixup(G_cs); |
| | 3296 | |
| | 3297 | /* add a placeholder for the function address */ |
| | 3298 | G_cs->write4(0); |
| | 3299 | |
| | 3300 | /* call removes arguments */ |
| | 3301 | G_cg->note_pop(argc); |
| | 3302 | |
| | 3303 | /* make sure the argument count is correct */ |
| | 3304 | if (mod_base != 0 |
| | 3305 | && (mod_base->is_varargs() ? argc < mod_base->get_argc() |
| | 3306 | : argc != mod_base->get_argc())) |
| | 3307 | G_tok->log_error(TCERR_WRONG_ARGC_FOR_FUNC, |
| | 3308 | (int)mod_base->get_sym_len(), mod_base->get_sym(), |
| | 3309 | mod_base->get_argc(), argc); |
| | 3310 | |
| | 3311 | /* if we're not discarding, push the return value from R0 */ |
| | 3312 | if (!discard) |
| | 3313 | { |
| | 3314 | G_cg->write_op(OPC_GETR0); |
| | 3315 | G_cg->note_push(); |
| | 3316 | } |
| | 3317 | } |
| | 3318 | |
| | 3319 | /* ------------------------------------------------------------------------ */ |
| | 3320 | /* |
| | 3321 | * "targetprop" |
| | 3322 | */ |
| | 3323 | |
| | 3324 | /* |
| | 3325 | * generate code |
| | 3326 | */ |
| | 3327 | void CTPNTargetprop::gen_code(int discard, int) |
| | 3328 | { |
| | 3329 | /* it's an error if we're not in a method context */ |
| | 3330 | if (!G_cs->is_self_available()) |
| | 3331 | G_tok->log_error(TCERR_TARGETPROP_NOT_AVAIL); |
| | 3332 | |
| | 3333 | /* if we're not discarding the result, push the target property ID */ |
| | 3334 | if (!discard) |
| | 3335 | { |
| | 3336 | G_cg->write_op(OPC_PUSHCTXELE); |
| | 3337 | G_cs->write(PUSHCTXELE_TARGPROP); |
| | 3338 | G_cg->note_push(); |
| | 3339 | } |
| | 3340 | } |
| | 3341 | |
| | 3342 | /* ------------------------------------------------------------------------ */ |
| | 3343 | /* |
| | 3344 | * "targetobj" |
| | 3345 | */ |
| | 3346 | |
| | 3347 | /* |
| | 3348 | * generate code |
| | 3349 | */ |
| | 3350 | void CTPNTargetobj::gen_code(int discard, int) |
| | 3351 | { |
| | 3352 | /* it's an error if we're not in a method context */ |
| | 3353 | if (!G_cs->is_self_available()) |
| | 3354 | G_tok->log_error(TCERR_TARGETOBJ_NOT_AVAIL); |
| | 3355 | |
| | 3356 | /* if we're not discarding the result, push the target object ID */ |
| | 3357 | if (!discard) |
| | 3358 | { |
| | 3359 | G_cg->write_op(OPC_PUSHCTXELE); |
| | 3360 | G_cs->write(PUSHCTXELE_TARGOBJ); |
| | 3361 | G_cg->note_push(); |
| | 3362 | } |
| | 3363 | } |
| | 3364 | |
| | 3365 | /* ------------------------------------------------------------------------ */ |
| | 3366 | /* |
| | 3367 | * "definingobj" |
| | 3368 | */ |
| | 3369 | |
| | 3370 | /* |
| | 3371 | * generate code |
| | 3372 | */ |
| | 3373 | void CTPNDefiningobj::gen_code(int discard, int) |
| | 3374 | { |
| | 3375 | /* it's an error if we're not in a method context */ |
| | 3376 | if (!G_cs->is_self_available()) |
| | 3377 | G_tok->log_error(TCERR_DEFININGOBJ_NOT_AVAIL); |
| | 3378 | |
| | 3379 | /* if we're not discarding the result, push the defining object ID */ |
| | 3380 | if (!discard) |
| | 3381 | { |
| | 3382 | G_cg->write_op(OPC_PUSHCTXELE); |
| | 3383 | G_cs->write(PUSHCTXELE_DEFOBJ); |
| | 3384 | G_cg->note_push(); |
| | 3385 | } |
| | 3386 | } |
| | 3387 | |
| | 3388 | |
| | 3389 | /* ------------------------------------------------------------------------ */ |
| | 3390 | /* |
| | 3391 | * "inherited" |
| | 3392 | */ |
| | 3393 | void CTPNInh::gen_code(int, int) |
| | 3394 | { |
| | 3395 | /* |
| | 3396 | * we should never be asked to generate an "inherited" node |
| | 3397 | * directly; these nodes should always be generated as part of |
| | 3398 | * member evaluation |
| | 3399 | */ |
| | 3400 | G_tok->throw_internal_error(TCERR_GEN_CODE_INH); |
| | 3401 | } |
| | 3402 | |
| | 3403 | /* |
| | 3404 | * evaluate a property |
| | 3405 | */ |
| | 3406 | void CTPNInh::gen_code_member(int discard, |
| | 3407 | CTcPrsNode *prop_expr, int prop_is_expr, |
| | 3408 | int argc, int varargs) |
| | 3409 | { |
| | 3410 | vm_prop_id_t prop; |
| | 3411 | |
| | 3412 | /* don't allow 'inherited' in speculative evaluation mode */ |
| | 3413 | if (G_cg->is_speculative()) |
| | 3414 | err_throw(VMERR_BAD_SPEC_EVAL); |
| | 3415 | |
| | 3416 | /* |
| | 3417 | * If we're in a multi-method context, inherited() has a different |
| | 3418 | * meaning from the ordinary method context meaning. |
| | 3419 | */ |
| | 3420 | for (CTPNCodeBody *cb = G_cs->get_code_body() ; cb != 0 ; |
| | 3421 | cb = cb->get_enclosing()) |
| | 3422 | { |
| | 3423 | /* check for a multi-method context */ |
| | 3424 | CTcSymFunc *f = cb->get_func_sym(); |
| | 3425 | if (f != 0 && f->is_multimethod()) |
| | 3426 | { |
| | 3427 | /* generate the multi-method inherited() code */ |
| | 3428 | gen_code_mminh(f, discard, prop_expr, prop_is_expr, argc, varargs); |
| | 3429 | |
| | 3430 | /* we're done */ |
| | 3431 | return; |
| | 3432 | } |
| | 3433 | } |
| | 3434 | |
| | 3435 | /* |
| | 3436 | * make sure "self" is available - we obviously can't inherit |
| | 3437 | * anything if we're not in an object's method |
| | 3438 | */ |
| | 3439 | if (!G_cs->is_self_available()) |
| | 3440 | G_tok->log_error(TCERR_SELF_NOT_AVAIL); |
| | 3441 | |
| | 3442 | /* a type list ('inherited<>') is invalid for regular method inheritance */ |
| | 3443 | if (typelist_ != 0) |
| | 3444 | G_tok->log_error(TCERR_MMINH_BAD_CONTEXT); |
| | 3445 | |
| | 3446 | /* generate the property value */ |
| | 3447 | prop = prop_expr->gen_code_propid(FALSE, prop_is_expr); |
| | 3448 | |
| | 3449 | /* |
| | 3450 | * if we got a property ID, generate a simple INHERIT; |
| | 3451 | * otherwise, generate a PTRINHERIT |
| | 3452 | */ |
| | 3453 | if (prop != VM_INVALID_PROP) |
| | 3454 | { |
| | 3455 | /* generate a varargs modifier if necessary */ |
| | 3456 | if (varargs) |
| | 3457 | G_cg->write_op(OPC_VARARGC); |
| | 3458 | |
| | 3459 | /* we have a constant property ID - generate a regular INHERIT */ |
| | 3460 | G_cg->write_op(OPC_INHERIT); |
| | 3461 | G_cs->write((char)argc); |
| | 3462 | G_cs->write_prop_id(prop); |
| | 3463 | |
| | 3464 | /* this removes arguments */ |
| | 3465 | G_cg->note_pop(argc); |
| | 3466 | } |
| | 3467 | else |
| | 3468 | { |
| | 3469 | /* |
| | 3470 | * if we have a varargs list, modify the call instruction that |
| | 3471 | * follows to make it a varargs call |
| | 3472 | */ |
| | 3473 | if (varargs) |
| | 3474 | { |
| | 3475 | /* swap the top of the stack to get the arg counter back on top */ |
| | 3476 | G_cg->write_op(OPC_SWAP); |
| | 3477 | |
| | 3478 | /* write the varargs modifier */ |
| | 3479 | G_cg->write_op(OPC_VARARGC); |
| | 3480 | } |
| | 3481 | |
| | 3482 | /* a property pointer is on the stack - write a PTRINHERIT */ |
| | 3483 | G_cg->write_op(OPC_PTRINHERIT); |
| | 3484 | G_cs->write((int)argc); |
| | 3485 | |
| | 3486 | /* this removes arguments and the property pointer */ |
| | 3487 | G_cg->note_pop(argc + 1); |
| | 3488 | } |
| | 3489 | |
| | 3490 | /* if the result is needed, push it */ |
| | 3491 | if (!discard) |
| | 3492 | { |
| | 3493 | G_cg->write_op(OPC_GETR0); |
| | 3494 | G_cg->note_push(); |
| | 3495 | } |
| | 3496 | } |
| | 3497 | |
| | 3498 | void CTPNInh::gen_code_mminh(CTcSymFunc *func, int discard, |
| | 3499 | CTcPrsNode *prop_expr, int prop_is_expr, |
| | 3500 | int argc, int varargs) |
| | 3501 | { |
| | 3502 | /* |
| | 3503 | * There are two forms of inherited() for multi-methods. |
| | 3504 | * |
| | 3505 | * inherited<types>(args) - this form takes an explicit type list, to |
| | 3506 | * invoke a specific overridden version. |
| | 3507 | * |
| | 3508 | * inherited(args) - this form invokes the next inherited version, |
| | 3509 | * determined dynamically at run-time. |
| | 3510 | */ |
| | 3511 | if (typelist_ == 0) |
| | 3512 | { |
| | 3513 | /* |
| | 3514 | * It's the inherited(args) format. |
| | 3515 | * |
| | 3516 | * Call _multiMethodCallInherited(fromFunc, args). We've already |
| | 3517 | * pushed the args list, so just add the source function argument |
| | 3518 | * (which is simply our defining function). |
| | 3519 | */ |
| | 3520 | func->gen_code(FALSE); |
| | 3521 | |
| | 3522 | /* look up _multiMethodCallInherited */ |
| | 3523 | CTcSymFunc *mmci = (CTcSymFunc *)G_prs->get_global_symtab()->find( |
| | 3524 | "_multiMethodCallInherited", 25); |
| | 3525 | |
| | 3526 | if (mmci == 0 || mmci->get_type() != TC_SYM_FUNC) |
| | 3527 | { |
| | 3528 | /* undefined or incorrectly defined - log an error */ |
| | 3529 | G_tok->log_error(TCERR_MMINH_MISSING_SUPPORT_FUNC, |
| | 3530 | 25, "_multiMethodCallInherited"); |
| | 3531 | } |
| | 3532 | else |
| | 3533 | { |
| | 3534 | /* generate the call */ |
| | 3535 | mmci->gen_code_call(discard, argc + 1, varargs); |
| | 3536 | } |
| | 3537 | } |
| | 3538 | else |
| | 3539 | { |
| | 3540 | /* |
| | 3541 | * It's the inherited<types>(args) format. |
| | 3542 | */ |
| | 3543 | |
| | 3544 | /* |
| | 3545 | * Get the base name for the function. 'func' is the decorated |
| | 3546 | * name for the containing function, which is of the form |
| | 3547 | * 'Base*type1;type2...'. The base name is the part up to the |
| | 3548 | * asterisk. |
| | 3549 | */ |
| | 3550 | const char *nm = func->getstr(), *p; |
| | 3551 | size_t rem = func->getlen(); |
| | 3552 | for (p = nm ; rem != 0 && *p != '*' ; ++p, --rem) ; |
| | 3553 | |
| | 3554 | /* make a token for the base name */ |
| | 3555 | CTcToken btok; |
| | 3556 | btok.set_text(nm, p - nm); |
| | 3557 | |
| | 3558 | /* build the decorated name for the target function */ |
| | 3559 | CTcToken dtok; |
| | 3560 | typelist_->decorate_name(&dtok, &btok); |
| | 3561 | |
| | 3562 | /* look up the decorated name */ |
| | 3563 | CTcSymFunc *ifunc = (CTcSymFunc *)G_prs->get_global_symtab()->find( |
| | 3564 | dtok.get_text(), dtok.get_text_len()); |
| | 3565 | |
| | 3566 | /* if we found it, call it */ |
| | 3567 | if (ifunc != 0 && ifunc->get_type() == TC_SYM_FUNC) |
| | 3568 | { |
| | 3569 | /* generate the call */ |
| | 3570 | ifunc->gen_code_call(discard, argc, varargs); |
| | 3571 | } |
| | 3572 | else |
| | 3573 | { |
| | 3574 | /* function not found */ |
| | 3575 | G_tok->log_error(TCERR_MMINH_UNDEF_FUNC, (int)(p - nm), nm); |
| | 3576 | } |
| | 3577 | } |
| | 3578 | } |
| | 3579 | |
| | 3580 | /* ------------------------------------------------------------------------ */ |
| | 3581 | /* |
| | 3582 | * "inherited class" |
| | 3583 | */ |
| | 3584 | void CTPNInhClass::gen_code(int discard, int for_condition) |
| | 3585 | { |
| | 3586 | /* |
| | 3587 | * we should never be asked to generate an "inherited" node |
| | 3588 | * directly; these nodes should always be generated as part of |
| | 3589 | * member evaluation |
| | 3590 | */ |
| | 3591 | G_tok->throw_internal_error(TCERR_GEN_CODE_INH); |
| | 3592 | } |
| | 3593 | |
| | 3594 | /* |
| | 3595 | * evaluate a property |
| | 3596 | */ |
| | 3597 | void CTPNInhClass::gen_code_member(int discard, |
| | 3598 | CTcPrsNode *prop_expr, int prop_is_expr, |
| | 3599 | int argc, int varargs) |
| | 3600 | { |
| | 3601 | vm_prop_id_t prop; |
| | 3602 | CTcSymbol *objsym; |
| | 3603 | vm_obj_id_t obj; |
| | 3604 | |
| | 3605 | /* |
| | 3606 | * make sure "self" is available - we obviously can't inherit |
| | 3607 | * anything if we're not in an object's method |
| | 3608 | */ |
| | 3609 | if (!G_cs->is_self_available()) |
| | 3610 | G_tok->log_error(TCERR_SELF_NOT_AVAIL); |
| | 3611 | |
| | 3612 | /* don't allow 'inherited' in speculative evaluation mode */ |
| | 3613 | if (G_cg->is_speculative()) |
| | 3614 | err_throw(VMERR_BAD_SPEC_EVAL); |
| | 3615 | |
| | 3616 | /* get the superclass name symbol */ |
| | 3617 | objsym = G_cs->get_symtab()->find_or_def_undef(sym_, len_, FALSE); |
| | 3618 | |
| | 3619 | /* if it's not an object, we can't inherit from it */ |
| | 3620 | obj = objsym->get_val_obj(); |
| | 3621 | if (obj == VM_INVALID_OBJ) |
| | 3622 | { |
| | 3623 | G_tok->log_error(TCERR_INH_NOT_OBJ, (int)len_, sym_); |
| | 3624 | return; |
| | 3625 | } |
| | 3626 | |
| | 3627 | /* generate the property value */ |
| | 3628 | prop = prop_expr->gen_code_propid(FALSE, prop_is_expr); |
| | 3629 | |
| | 3630 | /* |
| | 3631 | * if we got a property ID, generate a simple EXPINHERIT; otherwise, |
| | 3632 | * generate a PTREXPINHERIT |
| | 3633 | */ |
| | 3634 | if (prop != VM_INVALID_PROP) |
| | 3635 | { |
| | 3636 | /* add a varargs modifier if needed */ |
| | 3637 | if (varargs) |
| | 3638 | G_cg->write_op(OPC_VARARGC); |
| | 3639 | |
| | 3640 | /* we have a constant property ID - generate a regular EXPINHERIT */ |
| | 3641 | G_cg->write_op(OPC_EXPINHERIT); |
| | 3642 | G_cs->write((char)argc); |
| | 3643 | G_cs->write_prop_id(prop); |
| | 3644 | G_cs->write_obj_id(obj); |
| | 3645 | |
| | 3646 | /* this removes argumnts */ |
| | 3647 | G_cg->note_pop(argc); |
| | 3648 | } |
| | 3649 | else |
| | 3650 | { |
| | 3651 | /* |
| | 3652 | * if we have a varargs list, modify the call instruction that |
| | 3653 | * follows to make it a varargs call |
| | 3654 | */ |
| | 3655 | if (varargs) |
| | 3656 | { |
| | 3657 | /* swap the top of the stack to get the arg counter back on top */ |
| | 3658 | G_cg->write_op(OPC_SWAP); |
| | 3659 | |
| | 3660 | /* write the varargs modifier */ |
| | 3661 | G_cg->write_op(OPC_VARARGC); |
| | 3662 | } |
| | 3663 | |
| | 3664 | /* a property pointer is on the stack - write a PTREXPINHERIT */ |
| | 3665 | G_cg->write_op(OPC_PTREXPINHERIT); |
| | 3666 | G_cs->write((int)argc); |
| | 3667 | G_cs->write_obj_id(obj); |
| | 3668 | |
| | 3669 | /* this removes arguments and the property pointer */ |
| | 3670 | G_cg->note_pop(argc + 1); |
| | 3671 | } |
| | 3672 | |
| | 3673 | /* if the result is needed, push it */ |
| | 3674 | if (!discard) |
| | 3675 | { |
| | 3676 | G_cg->write_op(OPC_GETR0); |
| | 3677 | G_cg->note_push(); |
| | 3678 | } |
| | 3679 | } |
| | 3680 | |
| | 3681 | /* ------------------------------------------------------------------------ */ |
| | 3682 | /* |
| | 3683 | * "delegated" |
| | 3684 | */ |
| | 3685 | void CTPNDelegated::gen_code(int discard, int for_condition) |
| | 3686 | { |
| | 3687 | /* |
| | 3688 | * we should never be asked to generate a "delegated" node directly; |
| | 3689 | * these nodes should always be generated as part of member evaluation |
| | 3690 | */ |
| | 3691 | G_tok->throw_internal_error(TCERR_GEN_CODE_DELEGATED); |
| | 3692 | } |
| | 3693 | |
| | 3694 | /* |
| | 3695 | * evaluate a property |
| | 3696 | */ |
| | 3697 | void CTPNDelegated::gen_code_member(int discard, |
| | 3698 | CTcPrsNode *prop_expr, int prop_is_expr, |
| | 3699 | int argc, int varargs) |
| | 3700 | { |
| | 3701 | vm_prop_id_t prop; |
| | 3702 | |
| | 3703 | /* |
| | 3704 | * make sure "self" is available - we obviously can't delegate |
| | 3705 | * anything if we're not in an object's method |
| | 3706 | */ |
| | 3707 | if (!G_cs->is_self_available()) |
| | 3708 | G_tok->log_error(TCERR_SELF_NOT_AVAIL); |
| | 3709 | |
| | 3710 | /* don't allow 'delegated' in speculative evaluation mode */ |
| | 3711 | if (G_cg->is_speculative()) |
| | 3712 | err_throw(VMERR_BAD_SPEC_EVAL); |
| | 3713 | |
| | 3714 | /* generate the delegatee expression */ |
| | 3715 | delegatee_->gen_code(FALSE, FALSE); |
| | 3716 | |
| | 3717 | /* if we have an argument counter, put it back on top */ |
| | 3718 | if (varargs) |
| | 3719 | G_cg->write_op(OPC_SWAP); |
| | 3720 | |
| | 3721 | /* generate the property value */ |
| | 3722 | prop = prop_expr->gen_code_propid(FALSE, prop_is_expr); |
| | 3723 | |
| | 3724 | /* |
| | 3725 | * if we got a property ID, generate a simple DELEGATE; otherwise, |
| | 3726 | * generate a PTRDELEGATE |
| | 3727 | */ |
| | 3728 | if (prop != VM_INVALID_PROP) |
| | 3729 | { |
| | 3730 | /* add a varargs modifier if needed */ |
| | 3731 | if (varargs) |
| | 3732 | G_cg->write_op(OPC_VARARGC); |
| | 3733 | |
| | 3734 | /* we have a constant property ID - generate a regular DELEGATE */ |
| | 3735 | G_cg->write_op(OPC_DELEGATE); |
| | 3736 | G_cs->write((char)argc); |
| | 3737 | G_cs->write_prop_id(prop); |
| | 3738 | |
| | 3739 | /* this removes arguments and the object value */ |
| | 3740 | G_cg->note_pop(argc + 1); |
| | 3741 | } |
| | 3742 | else |
| | 3743 | { |
| | 3744 | /* |
| | 3745 | * if we have a varargs list, modify the call instruction that |
| | 3746 | * follows to make it a varargs call |
| | 3747 | */ |
| | 3748 | if (varargs) |
| | 3749 | { |
| | 3750 | /* swap the top of the stack to get the arg counter back on top */ |
| | 3751 | G_cg->write_op(OPC_SWAP); |
| | 3752 | |
| | 3753 | /* write the varargs modifier */ |
| | 3754 | G_cg->write_op(OPC_VARARGC); |
| | 3755 | } |
| | 3756 | |
| | 3757 | /* a property pointer is on the stack - write a PTRDELEGATE */ |
| | 3758 | G_cg->write_op(OPC_PTRDELEGATE); |
| | 3759 | G_cs->write((int)argc); |
| | 3760 | |
| | 3761 | /* this removes arguments, the object, and the property pointer */ |
| | 3762 | G_cg->note_pop(argc + 2); |
| | 3763 | } |
| | 3764 | |
| | 3765 | /* if the result is needed, push it */ |
| | 3766 | if (!discard) |
| | 3767 | { |
| | 3768 | G_cg->write_op(OPC_GETR0); |
| | 3769 | G_cg->note_push(); |
| | 3770 | } |
| | 3771 | } |
| | 3772 | |
| | 3773 | |
| | 3774 | |
| | 3775 | /* ------------------------------------------------------------------------ */ |
| | 3776 | /* |
| | 3777 | * "argcount" |
| | 3778 | */ |
| | 3779 | void CTPNArgc::gen_code(int discard, int) |
| | 3780 | { |
| | 3781 | /* generate the argument count, if we're not discarding */ |
| | 3782 | if (!discard) |
| | 3783 | { |
| | 3784 | if (G_cg->is_eval_for_debug()) |
| | 3785 | { |
| | 3786 | /* generate a debug argument count evaluation */ |
| | 3787 | G_cg->write_op(OPC_GETDBARGC); |
| | 3788 | G_cs->write2(G_cg->get_debug_stack_level()); |
| | 3789 | } |
| | 3790 | else |
| | 3791 | { |
| | 3792 | /* generate the normal argument count evaluation */ |
| | 3793 | G_cg->write_op(OPC_GETARGC); |
| | 3794 | } |
| | 3795 | |
| | 3796 | /* we push one element */ |
| | 3797 | G_cg->note_push(); |
| | 3798 | } |
| | 3799 | } |
| | 3800 | |
| | 3801 | |
| | 3802 | /* ------------------------------------------------------------------------ */ |
| | 3803 | /* |
| | 3804 | * constant |
| | 3805 | */ |
| | 3806 | void CTPNConst::gen_code(int discard, int) |
| | 3807 | { |
| | 3808 | /* if we're discarding the value, do nothing */ |
| | 3809 | if (discard) |
| | 3810 | return; |
| | 3811 | |
| | 3812 | /* generate the appropriate type of push for the value */ |
| | 3813 | switch(val_.get_type()) |
| | 3814 | { |
| | 3815 | case TC_CVT_NIL: |
| | 3816 | G_cg->write_op(OPC_PUSHNIL); |
| | 3817 | break; |
| | 3818 | |
| | 3819 | case TC_CVT_TRUE: |
| | 3820 | G_cg->write_op(OPC_PUSHTRUE); |
| | 3821 | break; |
| | 3822 | |
| | 3823 | case TC_CVT_INT: |
| | 3824 | /* write the push-integer instruction */ |
| | 3825 | s_gen_code_int(val_.get_val_int()); |
| | 3826 | |
| | 3827 | /* s_gen_code_int notes a push, which we'll do also, so cancel it */ |
| | 3828 | G_cg->note_pop(); |
| | 3829 | break; |
| | 3830 | |
| | 3831 | case TC_CVT_FLOAT: |
| | 3832 | /* we'll represent it as a BigNumber object */ |
| | 3833 | G_cg->write_op(OPC_PUSHOBJ); |
| | 3834 | |
| | 3835 | /* generate the BigNumber object and write its ID */ |
| | 3836 | G_cs->write_obj_id(G_cg->gen_bignum_obj(val_.get_val_float(), |
| | 3837 | val_.get_val_float_len())); |
| | 3838 | break; |
| | 3839 | |
| | 3840 | case TC_CVT_SSTR: |
| | 3841 | /* write the instruction to push a constant pool string */ |
| | 3842 | G_cg->write_op(OPC_PUSHSTR); |
| | 3843 | |
| | 3844 | /* |
| | 3845 | * add the string to the constant pool, creating a fixup at the |
| | 3846 | * current code stream location |
| | 3847 | */ |
| | 3848 | G_cg->add_const_str(val_.get_val_str(), val_.get_val_str_len(), |
| | 3849 | G_cs, G_cs->get_ofs()); |
| | 3850 | |
| | 3851 | /* |
| | 3852 | * write a placeholder address - this will be corrected by the |
| | 3853 | * fixup that add_const_str() created for us |
| | 3854 | */ |
| | 3855 | G_cs->write4(0); |
| | 3856 | break; |
| | 3857 | |
| | 3858 | case TC_CVT_LIST: |
| | 3859 | /* write the instruction */ |
| | 3860 | G_cg->write_op(OPC_PUSHLST); |
| | 3861 | |
| | 3862 | /* |
| | 3863 | * add the list to the constant pool, creating a fixup at the |
| | 3864 | * current code stream location |
| | 3865 | */ |
| | 3866 | G_cg->add_const_list(val_.get_val_list(), G_cs, G_cs->get_ofs()); |
| | 3867 | |
| | 3868 | /* |
| | 3869 | * write a placeholder address - this will be corrected by the |
| | 3870 | * fixup that add_const_list() created for us |
| | 3871 | */ |
| | 3872 | G_cs->write4(0); |
| | 3873 | break; |
| | 3874 | |
| | 3875 | case TC_CVT_OBJ: |
| | 3876 | /* generate the object ID */ |
| | 3877 | G_cg->write_op(OPC_PUSHOBJ); |
| | 3878 | G_cs->write_obj_id(val_.get_val_obj()); |
| | 3879 | break; |
| | 3880 | |
| | 3881 | case TC_CVT_PROP: |
| | 3882 | /* generate the property address */ |
| | 3883 | G_cg->write_op(OPC_PUSHPROPID); |
| | 3884 | G_cs->write_prop_id(val_.get_val_prop()); |
| | 3885 | break; |
| | 3886 | |
| | 3887 | case TC_CVT_ENUM: |
| | 3888 | /* generate the enum value */ |
| | 3889 | G_cg->write_op(OPC_PUSHENUM); |
| | 3890 | G_cs->write_enum_id(val_.get_val_enum()); |
| | 3891 | break; |
| | 3892 | |
| | 3893 | case TC_CVT_FUNCPTR: |
| | 3894 | /* generate the function pointer instruction */ |
| | 3895 | G_cg->write_op(OPC_PUSHFNPTR); |
| | 3896 | |
| | 3897 | /* add a fixup for the function address */ |
| | 3898 | val_.get_val_funcptr_sym()->add_abs_fixup(G_cs); |
| | 3899 | |
| | 3900 | /* write out a placeholder - arbitrarily use zero */ |
| | 3901 | G_cs->write4(0); |
| | 3902 | break; |
| | 3903 | |
| | 3904 | case TC_CVT_ANONFUNCPTR: |
| | 3905 | /* generate the function pointer instruction */ |
| | 3906 | G_cg->write_op(OPC_PUSHFNPTR); |
| | 3907 | |
| | 3908 | /* add a fixup for the code body address */ |
| | 3909 | val_.get_val_anon_func_ptr()->add_abs_fixup(G_cs); |
| | 3910 | |
| | 3911 | /* write our a placeholder */ |
| | 3912 | G_cs->write4(0); |
| | 3913 | break; |
| | 3914 | |
| | 3915 | default: |
| | 3916 | /* anything else is an internal error */ |
| | 3917 | G_tok->throw_internal_error(TCERR_GEN_UNK_CONST_TYPE); |
| | 3918 | } |
| | 3919 | |
| | 3920 | /* all of these push a value */ |
| | 3921 | G_cg->note_push(); |
| | 3922 | } |
| | 3923 | |
| | 3924 | /* |
| | 3925 | * generate code to push an integer value |
| | 3926 | */ |
| | 3927 | void CTPNConst::s_gen_code_int(long intval) |
| | 3928 | { |
| | 3929 | /* push the smallest format that will fit the value */ |
| | 3930 | if (intval == 0) |
| | 3931 | { |
| | 3932 | /* write the special PUSH_0 instruction */ |
| | 3933 | G_cg->write_op(OPC_PUSH_0); |
| | 3934 | } |
| | 3935 | else if (intval == 1) |
| | 3936 | { |
| | 3937 | /* write the special PUSH_1 instruction */ |
| | 3938 | G_cg->write_op(OPC_PUSH_1); |
| | 3939 | } |
| | 3940 | else if (intval < 127 && intval >= -128) |
| | 3941 | { |
| | 3942 | /* it fits in eight bits */ |
| | 3943 | G_cg->write_op(OPC_PUSHINT8); |
| | 3944 | G_cs->write((char)intval); |
| | 3945 | } |
| | 3946 | else |
| | 3947 | { |
| | 3948 | /* it doesn't fit in 8 bits - use a full 32 bits */ |
| | 3949 | G_cg->write_op(OPC_PUSHINT); |
| | 3950 | G_cs->write4(intval); |
| | 3951 | } |
| | 3952 | |
| | 3953 | /* however we did it, we left one value on the stack */ |
| | 3954 | G_cg->note_push(); |
| | 3955 | } |
| | 3956 | |
| | 3957 | /* |
| | 3958 | * Generate code to apply operator 'new' to the constant. We can apply |
| | 3959 | * 'new' only to constant object values. |
| | 3960 | */ |
| | 3961 | void CTPNConst::gen_code_new(int discard, int argc, int varargs, |
| | 3962 | int /*from_call*/, int is_transient) |
| | 3963 | { |
| | 3964 | /* check the type */ |
| | 3965 | switch(val_.get_type()) |
| | 3966 | { |
| | 3967 | case TC_CVT_OBJ: |
| | 3968 | /* |
| | 3969 | * Treat this the same as any other 'new' call. An object symbol |
| | 3970 | * folded into a constant is guaranteed to be of metaclass |
| | 3971 | * TadsObject - that's the only kind of symbol we'll ever fold this |
| | 3972 | * way. |
| | 3973 | */ |
| | 3974 | CTcSymObj::s_gen_code_new(discard, |
| | 3975 | val_.get_val_obj(), val_.get_val_obj_meta(), |
| | 3976 | argc, varargs, is_transient); |
| | 3977 | break; |
| | 3978 | |
| | 3979 | default: |
| | 3980 | /* can't apply 'new' to other constant values */ |
| | 3981 | G_tok->log_error(TCERR_INVAL_NEW_EXPR); |
| | 3982 | break; |
| | 3983 | } |
| | 3984 | } |
| | 3985 | |
| | 3986 | /* |
| | 3987 | * Generate code to make a function call to this expression. If we're |
| | 3988 | * calling a function, we can generate this directly. |
| | 3989 | */ |
| | 3990 | void CTPNConst::gen_code_call(int discard, int argc, int varargs) |
| | 3991 | { |
| | 3992 | /* check our type */ |
| | 3993 | switch(val_.get_type()) |
| | 3994 | { |
| | 3995 | case TC_CVT_FUNCPTR: |
| | 3996 | /* generate a call to our function symbol */ |
| | 3997 | val_.get_val_funcptr_sym()->gen_code_call(discard, argc, varargs); |
| | 3998 | break; |
| | 3999 | |
| | 4000 | default: |
| | 4001 | /* other types cannot be called */ |
| | 4002 | G_tok->log_error(TCERR_CANNOT_CALL_CONST); |
| | 4003 | break; |
| | 4004 | } |
| | 4005 | } |
| | 4006 | |
| | 4007 | /* |
| | 4008 | * generate a property ID expression |
| | 4009 | */ |
| | 4010 | vm_prop_id_t CTPNConst::gen_code_propid(int check_only, int is_expr) |
| | 4011 | { |
| | 4012 | /* check the type */ |
| | 4013 | switch(val_.get_type()) |
| | 4014 | { |
| | 4015 | case TC_CVT_PROP: |
| | 4016 | /* return the constant property ID */ |
| | 4017 | return (vm_prop_id_t)val_.get_val_prop(); |
| | 4018 | |
| | 4019 | default: |
| | 4020 | /* other values cannot be used as properties */ |
| | 4021 | if (!check_only) |
| | 4022 | G_tok->log_error(TCERR_INVAL_PROP_EXPR); |
| | 4023 | return VM_INVALID_PROP; |
| | 4024 | } |
| | 4025 | } |
| | 4026 | |
| | 4027 | |
| | 4028 | /* |
| | 4029 | * Generate code for a member evaluation |
| | 4030 | */ |
| | 4031 | void CTPNConst::gen_code_member(int discard, |
| | 4032 | CTcPrsNode *prop_expr, int prop_is_expr, |
| | 4033 | int argc, int varargs) |
| | 4034 | { |
| | 4035 | /* check our constant type */ |
| | 4036 | switch(val_.get_type()) |
| | 4037 | { |
| | 4038 | case TC_CVT_OBJ: |
| | 4039 | /* call the object symbol code to do the work */ |
| | 4040 | CTcSymObj::s_gen_code_member(discard, prop_expr, prop_is_expr, |
| | 4041 | argc, val_.get_val_obj(), varargs); |
| | 4042 | break; |
| | 4043 | |
| | 4044 | case TC_CVT_LIST: |
| | 4045 | case TC_CVT_SSTR: |
| | 4046 | case TC_CVT_FLOAT: |
| | 4047 | /* |
| | 4048 | * list/string/BigNumber constant - generate our value as |
| | 4049 | * normal, then use the standard member generation |
| | 4050 | */ |
| | 4051 | gen_code(FALSE, FALSE); |
| | 4052 | |
| | 4053 | /* if we have an argument counter, put it back on top */ |
| | 4054 | if (varargs) |
| | 4055 | G_cg->write_op(OPC_SWAP); |
| | 4056 | |
| | 4057 | /* use standard member generation */ |
| | 4058 | CTcPrsNode::s_gen_member_rhs(discard, prop_expr, prop_is_expr, |
| | 4059 | argc, varargs); |
| | 4060 | break; |
| | 4061 | |
| | 4062 | default: |
| | 4063 | G_tok->log_error(TCERR_INVAL_OBJ_EXPR); |
| | 4064 | break; |
| | 4065 | } |
| | 4066 | } |
| | 4067 | |
| | 4068 | |
| | 4069 | /* |
| | 4070 | * generate code for an object before a '.' |
| | 4071 | */ |
| | 4072 | vm_obj_id_t CTPNConst::gen_code_obj_predot(int *is_self) |
| | 4073 | { |
| | 4074 | /* we're certainly not "self" */ |
| | 4075 | *is_self = FALSE; |
| | 4076 | |
| | 4077 | /* if I don't have an object value, this is illegal */ |
| | 4078 | if (val_.get_type() != TC_CVT_OBJ) |
| | 4079 | { |
| | 4080 | G_tok->log_error(TCERR_INVAL_OBJ_EXPR); |
| | 4081 | return VM_INVALID_OBJ; |
| | 4082 | } |
| | 4083 | |
| | 4084 | /* report our constant object value */ |
| | 4085 | return val_.get_val_obj(); |
| | 4086 | } |
| | 4087 | |
| | 4088 | /* ------------------------------------------------------------------------ */ |
| | 4089 | /* |
| | 4090 | * debugger constant |
| | 4091 | */ |
| | 4092 | void CTPNDebugConst::gen_code(int discard, int for_condition) |
| | 4093 | { |
| | 4094 | /* if we're discarding the value, do nothing */ |
| | 4095 | if (discard) |
| | 4096 | return; |
| | 4097 | |
| | 4098 | /* generate the appropriate type of push for the value */ |
| | 4099 | switch(val_.get_type()) |
| | 4100 | { |
| | 4101 | case TC_CVT_SSTR: |
| | 4102 | /* write the in-line string instruction */ |
| | 4103 | G_cg->write_op(OPC_PUSHSTRI); |
| | 4104 | G_cs->write2(val_.get_val_str_len()); |
| | 4105 | G_cs->write(val_.get_val_str(), val_.get_val_str_len()); |
| | 4106 | |
| | 4107 | /* note the value push */ |
| | 4108 | G_cg->note_push(); |
| | 4109 | break; |
| | 4110 | |
| | 4111 | case TC_CVT_LIST: |
| | 4112 | /* we should never have a constant list when debugging */ |
| | 4113 | assert(FALSE); |
| | 4114 | break; |
| | 4115 | |
| | 4116 | case TC_CVT_FUNCPTR: |
| | 4117 | /* generate the function pointer instruction */ |
| | 4118 | G_cg->write_op(OPC_PUSHFNPTR); |
| | 4119 | |
| | 4120 | /* |
| | 4121 | * write the actual function address - no need for fixups in the |
| | 4122 | * debugger, since everything's fully resolved |
| | 4123 | */ |
| | 4124 | G_cs->write4(val_.get_val_funcptr_sym()->get_code_pool_addr()); |
| | 4125 | |
| | 4126 | /* note the value push */ |
| | 4127 | G_cg->note_push(); |
| | 4128 | break; |
| | 4129 | |
| | 4130 | case TC_CVT_ANONFUNCPTR: |
| | 4131 | /* |
| | 4132 | * we should never see an anonymous function pointer in the |
| | 4133 | * debugger |
| | 4134 | */ |
| | 4135 | assert(FALSE); |
| | 4136 | break; |
| | 4137 | |
| | 4138 | case TC_CVT_FLOAT: |
| | 4139 | { |
| | 4140 | CTcSymMetaclass *sym; |
| | 4141 | |
| | 4142 | /* |
| | 4143 | * find the 'BigNumber' metaclass - if it's not defined, we |
| | 4144 | * can't create BigNumber values |
| | 4145 | */ |
| | 4146 | sym = (CTcSymMetaclass *)G_prs->get_global_symtab() |
| | 4147 | ->find("BigNumber", 9); |
| | 4148 | if (sym == 0 || sym->get_type() != TC_SYM_METACLASS) |
| | 4149 | err_throw(VMERR_INVAL_DBG_EXPR); |
| | 4150 | |
| | 4151 | /* push the floating value as an immediate string */ |
| | 4152 | G_cg->write_op(OPC_PUSHSTRI); |
| | 4153 | G_cs->write2(val_.get_val_str_len()); |
| | 4154 | G_cs->write(val_.get_val_str(), val_.get_val_str_len()); |
| | 4155 | |
| | 4156 | /* create the new BigNumber object from the string */ |
| | 4157 | G_cg->write_op(OPC_NEW2); |
| | 4158 | G_cs->write2(1); |
| | 4159 | G_cs->write2(sym->get_meta_idx()); |
| | 4160 | |
| | 4161 | /* retrieve the value */ |
| | 4162 | G_cg->write_op(OPC_GETR0); |
| | 4163 | |
| | 4164 | /* |
| | 4165 | * note the net push of one value (we pushed the argument, |
| | 4166 | * popped the argument, and pushed the new object) |
| | 4167 | */ |
| | 4168 | G_cg->note_push(); |
| | 4169 | } |
| | 4170 | break; |
| | 4171 | |
| | 4172 | default: |
| | 4173 | /* handle normally for anything else */ |
| | 4174 | CTPNConst::gen_code(discard, for_condition); |
| | 4175 | break; |
| | 4176 | } |
| | 4177 | } |
| | 4178 | |
| | 4179 | |
| | 4180 | /* ------------------------------------------------------------------------ */ |
| | 4181 | /* |
| | 4182 | * Generic Unary Operator |
| | 4183 | */ |
| | 4184 | |
| | 4185 | /* |
| | 4186 | * Generate a unary-operator opcode. We assume that the opcode has no |
| | 4187 | * side effects other than to compute the result, so we do not generate |
| | 4188 | * the opcode at all if 'discard' is true; we do, however, always |
| | 4189 | * generate code for the subexpression to ensure that its side effects |
| | 4190 | * are performed. |
| | 4191 | * |
| | 4192 | * In most cases, the caller simply should pass through its 'discard' |
| | 4193 | * status, since the result of the subexpression is generally needed |
| | 4194 | * only when the result of the enclosing expression is needed. |
| | 4195 | * |
| | 4196 | * In most cases, the caller should pass FALSE for 'for_condition', |
| | 4197 | * because applying an operator to the result generally requires that |
| | 4198 | * the result be properly converted for use as a temporary value. |
| | 4199 | * However, when the caller knows that its own opcode will perform the |
| | 4200 | * same conversions that a conditional opcode would, 'for_condition' |
| | 4201 | * should be TRUE. In most cases, the caller's own 'for_condition' |
| | 4202 | * status is not relevant and should thus not be passed through. |
| | 4203 | */ |
| | 4204 | void CTPNUnary::gen_unary(uchar opc, int discard, int for_condition) |
| | 4205 | { |
| | 4206 | /* |
| | 4207 | * Generate the operand. Pass through the 'discard' status to the |
| | 4208 | * operand - if the result of the parent operator is being |
| | 4209 | * discarded, then so is the result of this subexpression. In |
| | 4210 | * addition, pass through the caller's 'for_condition' disposition. |
| | 4211 | */ |
| | 4212 | sub_->gen_code(discard, for_condition); |
| | 4213 | |
| | 4214 | /* apply the operator if we're not discarding the result */ |
| | 4215 | if (!discard) |
| | 4216 | G_cg->write_op(opc); |
| | 4217 | } |
| | 4218 | |
| | 4219 | /* ------------------------------------------------------------------------ */ |
| | 4220 | /* |
| | 4221 | * Generic Binary Operator |
| | 4222 | */ |
| | 4223 | |
| | 4224 | /* |
| | 4225 | * Generate a binary-operator opcode. |
| | 4226 | * |
| | 4227 | * In most cases, the caller's 'discard' status should be passed |
| | 4228 | * through, since the results of the operands are usually needed if and |
| | 4229 | * only if the results of the enclosing expression are needed. |
| | 4230 | * |
| | 4231 | * In most cases, the caller should pass FALSE for 'for_condition'. |
| | 4232 | * Only when the caller knows that the opcode will perform the same |
| | 4233 | * conversions as a BOOLIZE instruction should it pass TRUE for |
| | 4234 | * 'for_condition'. |
| | 4235 | */ |
| | 4236 | void CTPNBin::gen_binary(uchar opc, int discard, int for_condition) |
| | 4237 | { |
| | 4238 | /* |
| | 4239 | * generate the operands, passing through the discard and |
| | 4240 | * conditional status |
| | 4241 | */ |
| | 4242 | left_->gen_code(discard, for_condition); |
| | 4243 | right_->gen_code(discard, for_condition); |
| | 4244 | |
| | 4245 | /* generate our operand if we're not discarding the result */ |
| | 4246 | if (!discard) |
| | 4247 | { |
| | 4248 | /* apply the operator */ |
| | 4249 | G_cg->write_op(opc); |
| | 4250 | |
| | 4251 | /* |
| | 4252 | * boolean operators all remove two values and push one, so |
| | 4253 | * there's a net pop |
| | 4254 | */ |
| | 4255 | G_cg->note_pop(); |
| | 4256 | } |
| | 4257 | } |
| | 4258 | |
| | 4259 | |
| | 4260 | |
| | 4261 | /* ------------------------------------------------------------------------ */ |
| | 4262 | /* |
| | 4263 | * logical NOT |
| | 4264 | */ |
| | 4265 | void CTPNNot::gen_code(int discard, int) |
| | 4266 | { |
| | 4267 | /* |
| | 4268 | * Generate the subexpression and apply the NOT opcode. Note that |
| | 4269 | * we can compute the subexpression as though we were applying a |
| | 4270 | * condition, because the NOT opcode takes exactly the same kind of |
| | 4271 | * input as any condition opcode; we can thus avoid an extra |
| | 4272 | * conversion in some cases. |
| | 4273 | */ |
| | 4274 | gen_unary(OPC_NOT, discard, TRUE); |
| | 4275 | } |
| | 4276 | |
| | 4277 | /* ------------------------------------------------------------------------ */ |
| | 4278 | /* |
| | 4279 | * Boolean-ize operator |
| | 4280 | */ |
| | 4281 | void CTPNBoolize::gen_code(int discard, int for_condition) |
| | 4282 | { |
| | 4283 | /* |
| | 4284 | * If the result will be used for a conditional, there's no need to |
| | 4285 | * generate an instruction to convert the value to boolean. The opcode |
| | 4286 | * that will be used for the condition will perform exactly the same |
| | 4287 | * conversions that this opcode would apply; avoid the redundant work |
| | 4288 | * in this case, and simply generate the underlying expression |
| | 4289 | * directly. |
| | 4290 | */ |
| | 4291 | if (for_condition) |
| | 4292 | { |
| | 4293 | /* generate the underlying expression without modification */ |
| | 4294 | sub_->gen_code(discard, for_condition); |
| | 4295 | |
| | 4296 | /* done */ |
| | 4297 | return; |
| | 4298 | } |
| | 4299 | |
| | 4300 | /* |
| | 4301 | * Generate the subexpression and apply the BOOLIZE operator. Since |
| | 4302 | * we're explicitly boolean-izing the value, there's no need for the |
| | 4303 | * subexpression to do the same thing, so the subexpression can |
| | 4304 | * pretend it's generating for a conditional. |
| | 4305 | */ |
| | 4306 | gen_unary(OPC_BOOLIZE, discard, TRUE); |
| | 4307 | } |
| | 4308 | |
| | 4309 | |
| | 4310 | /* ------------------------------------------------------------------------ */ |
| | 4311 | /* |
| | 4312 | * bitwise NOT |
| | 4313 | */ |
| | 4314 | void CTPNBNot::gen_code(int discard, int) |
| | 4315 | { |
| | 4316 | gen_unary(OPC_BNOT, discard, FALSE); |
| | 4317 | } |
| | 4318 | |
| | 4319 | |
| | 4320 | /* ------------------------------------------------------------------------ */ |
| | 4321 | /* |
| | 4322 | * arithmetic positive |
| | 4323 | */ |
| | 4324 | void CTPNPos::gen_code(int discard, int) |
| | 4325 | { |
| | 4326 | /* |
| | 4327 | * simply generate our operand, since the operator itself has no |
| | 4328 | * effect |
| | 4329 | */ |
| | 4330 | sub_->gen_code(discard, FALSE); |
| | 4331 | } |
| | 4332 | |
| | 4333 | /* ------------------------------------------------------------------------ */ |
| | 4334 | /* |
| | 4335 | * unary arithmetic negative |
| | 4336 | */ |
| | 4337 | void CTPNNeg::gen_code(int discard, int) |
| | 4338 | { |
| | 4339 | gen_unary(OPC_NEG, discard, FALSE); |
| | 4340 | } |
| | 4341 | |
| | 4342 | /* ------------------------------------------------------------------------ */ |
| | 4343 | /* |
| | 4344 | * pre-increment |
| | 4345 | */ |
| | 4346 | void CTPNPreInc::gen_code(int discard, int) |
| | 4347 | { |
| | 4348 | /* ask the subnode to generate it */ |
| | 4349 | if (!sub_->gen_code_asi(discard, TC_ASI_PREINC, 0, FALSE)) |
| | 4350 | { |
| | 4351 | /* |
| | 4352 | * the subnode didn't handle it - generate code to evaluate the |
| | 4353 | * subnode, increment that value, then assign the result back to |
| | 4354 | * the subnode with a simple assignment |
| | 4355 | */ |
| | 4356 | sub_->gen_code(FALSE, FALSE); |
| | 4357 | |
| | 4358 | /* increment the value at top of stack */ |
| | 4359 | G_cg->write_op(OPC_INC); |
| | 4360 | |
| | 4361 | /* |
| | 4362 | * generate a simple assignment back to the subexpression; if |
| | 4363 | * we're using the value, let the simple assignment leave its |
| | 4364 | * value on the stack, since the result is the value *after* the |
| | 4365 | * increment |
| | 4366 | */ |
| | 4367 | sub_->gen_code_asi(discard, TC_ASI_SIMPLE, 0, FALSE); |
| | 4368 | } |
| | 4369 | } |
| | 4370 | |
| | 4371 | /* ------------------------------------------------------------------------ */ |
| | 4372 | /* |
| | 4373 | * pre-decrement |
| | 4374 | */ |
| | 4375 | void CTPNPreDec::gen_code(int discard, int) |
| | 4376 | { |
| | 4377 | /* ask the subnode to generate it */ |
| | 4378 | if (!sub_->gen_code_asi(discard, TC_ASI_PREDEC, 0, FALSE)) |
| | 4379 | { |
| | 4380 | /* |
| | 4381 | * the subnode didn't handle it - generate code to evaluate the |
| | 4382 | * subnode, decrement that value, then assign the result back to |
| | 4383 | * the subnode with a simple assignment |
| | 4384 | */ |
| | 4385 | sub_->gen_code(FALSE, FALSE); |
| | 4386 | |
| | 4387 | /* decrement the value at top of stack */ |
| | 4388 | G_cg->write_op(OPC_DEC); |
| | 4389 | |
| | 4390 | /* |
| | 4391 | * generate a simple assignment back to the subexpression; if |
| | 4392 | * we're using the value, let the simple assignment leave its |
| | 4393 | * value on the stack, since the result is the value *after* the |
| | 4394 | * decrement |
| | 4395 | */ |
| | 4396 | sub_->gen_code_asi(discard, TC_ASI_SIMPLE, 0, FALSE); |
| | 4397 | } |
| | 4398 | } |
| | 4399 | |
| | 4400 | /* ------------------------------------------------------------------------ */ |
| | 4401 | /* |
| | 4402 | * post-increment |
| | 4403 | */ |
| | 4404 | void CTPNPostInc::gen_code(int discard, int) |
| | 4405 | { |
| | 4406 | /* ask the subnode to generate it */ |
| | 4407 | if (!sub_->gen_code_asi(discard, TC_ASI_POSTINC, 0, FALSE)) |
| | 4408 | { |
| | 4409 | /* |
| | 4410 | * the subnode didn't handle it - generate code to evaluate the |
| | 4411 | * subnode, increment that value, then assign the result back to |
| | 4412 | * the subnode with a simple assignment |
| | 4413 | */ |
| | 4414 | sub_->gen_code(FALSE, FALSE); |
| | 4415 | |
| | 4416 | /* |
| | 4417 | * if we're keeping the result, duplicate the value at top of |
| | 4418 | * stack prior to the increment - since this is a |
| | 4419 | * post-increment, the result is the value *before* the |
| | 4420 | * increment |
| | 4421 | */ |
| | 4422 | if (!discard) |
| | 4423 | { |
| | 4424 | G_cg->write_op(OPC_DUP); |
| | 4425 | G_cg->note_push(); |
| | 4426 | } |
| | 4427 | |
| | 4428 | /* increment the value at top of stack */ |
| | 4429 | G_cg->write_op(OPC_INC); |
| | 4430 | |
| | 4431 | /* |
| | 4432 | * Generate a simple assignment back to the subexpression. |
| | 4433 | * Discard the result of this assignment, regardless of whether |
| | 4434 | * the caller wants the result of the overall expression, |
| | 4435 | * because we've already pushed the actual result, which is the |
| | 4436 | * original value before the increment operation. |
| | 4437 | */ |
| | 4438 | sub_->gen_code_asi(TRUE, TC_ASI_SIMPLE, 0, FALSE); |
| | 4439 | } |
| | 4440 | } |
| | 4441 | |
| | 4442 | /* ------------------------------------------------------------------------ */ |
| | 4443 | /* |
| | 4444 | * post-decrement |
| | 4445 | */ |
| | 4446 | void CTPNPostDec::gen_code(int discard, int) |
| | 4447 | { |
| | 4448 | /* ask the subnode to generate it */ |
| | 4449 | if (!sub_->gen_code_asi(discard, TC_ASI_POSTDEC, 0, FALSE)) |
| | 4450 | { |
| | 4451 | /* |
| | 4452 | * the subnode didn't handle it - generate code to evaluate the |
| | 4453 | * subnode, decrement that value, then assign the result back to |
| | 4454 | * the subnode with a simple assignment |
| | 4455 | */ |
| | 4456 | sub_->gen_code(FALSE, FALSE); |
| | 4457 | |
| | 4458 | /* |
| | 4459 | * if we're keeping the result, duplicate the value at top of |
| | 4460 | * stack prior to the decrement - since this is a |
| | 4461 | * post-decrement, the result is the value *before* the |
| | 4462 | * decrement |
| | 4463 | */ |
| | 4464 | if (!discard) |
| | 4465 | { |
| | 4466 | G_cg->write_op(OPC_DUP); |
| | 4467 | G_cg->note_push(); |
| | 4468 | } |
| | 4469 | |
| | 4470 | /* decrement the value at top of stack */ |
| | 4471 | G_cg->write_op(OPC_DEC); |
| | 4472 | |
| | 4473 | /* |
| | 4474 | * Generate a simple assignment back to the subexpression. |
| | 4475 | * Discard the result of this assignment, regardless of whether |
| | 4476 | * the caller wants the result of the overall expression, |
| | 4477 | * because we've already pushed the actual result, which is the |
| | 4478 | * original value before the decrement operation. |
| | 4479 | */ |
| | 4480 | sub_->gen_code_asi(TRUE, TC_ASI_SIMPLE, 0, FALSE); |
| | 4481 | } |
| | 4482 | } |
| | 4483 | |
| | 4484 | /* ------------------------------------------------------------------------ */ |
| | 4485 | /* |
| | 4486 | * operator 'new' |
| | 4487 | */ |
| | 4488 | void CTPNNew::gen_code(int discard, int /*for condition*/) |
| | 4489 | { |
| | 4490 | /* |
| | 4491 | * ask my subexpression to generate the code - at this point we |
| | 4492 | * don't know the number of arguments, so pass in zero for now |
| | 4493 | */ |
| | 4494 | sub_->gen_code_new(discard, 0, FALSE, FALSE, transient_); |
| | 4495 | } |
| | 4496 | |
| | 4497 | /* ------------------------------------------------------------------------ */ |
| | 4498 | /* |
| | 4499 | * operator 'delete' |
| | 4500 | */ |
| | 4501 | void CTPNDelete::gen_code(int, int) |
| | 4502 | { |
| | 4503 | /* 'delete' generates no code for T3 VM */ |
| | 4504 | } |
| | 4505 | |
| | 4506 | /* ------------------------------------------------------------------------ */ |
| | 4507 | /* |
| | 4508 | * comma operator |
| | 4509 | */ |
| | 4510 | void CTPNComma::gen_code(int discard, int for_condition) |
| | 4511 | { |
| | 4512 | /* |
| | 4513 | * Generate each side's code. Note that the left side is *always* |
| | 4514 | * discarded, regardless of whether the result of the comma operator |
| | 4515 | * will be discarded. After we generate our subexpressions, there's |
| | 4516 | * nothing left to do, since the comma operator itself doesn't |
| | 4517 | * change anything - we simply use the right operand result as our |
| | 4518 | * result. |
| | 4519 | * |
| | 4520 | * Pass through the 'for_condition' status to the right operand, |
| | 4521 | * since we pass through its result to the caller. For the left |
| | 4522 | * operand, treat it as a condition - we don't care about the result |
| | 4523 | * value, so don't bother performing any extra conversions on it. |
| | 4524 | */ |
| | 4525 | left_->gen_code(TRUE, TRUE); |
| | 4526 | right_->gen_code(discard, for_condition); |
| | 4527 | } |
| | 4528 | |
| | 4529 | /* ------------------------------------------------------------------------ */ |
| | 4530 | /* |
| | 4531 | * logical OR (short-circuit logic) |
| | 4532 | */ |
| | 4533 | void CTPNOr::gen_code(int discard, int for_condition) |
| | 4534 | { |
| | 4535 | CTcCodeLabel *lbl; |
| | 4536 | |
| | 4537 | /* |
| | 4538 | * First, evaluate the left-hand side; we need the result even if |
| | 4539 | * we're discarding the overall expression, since we will check the |
| | 4540 | * result to see if we should even evaluate the right-hand side. |
| | 4541 | * We're using the value for a condition, so don't bother |
| | 4542 | * boolean-izing it. |
| | 4543 | */ |
| | 4544 | left_->gen_code(FALSE, TRUE); |
| | 4545 | |
| | 4546 | /* |
| | 4547 | * If the left-hand side is true, there's no need to evaluate the |
| | 4548 | * right-hand side (and, in fact, we're not even allowed to evaluate |
| | 4549 | * the right-hand side because of the short-circuit logic rule). |
| | 4550 | * So, if the lhs is true, we want to jump around the code to |
| | 4551 | * evaluate the rhs, saving the 'true' result if we're not |
| | 4552 | * discarding the overall result. |
| | 4553 | */ |
| | 4554 | lbl = gen_jump_ahead(discard ? OPC_JT : OPC_JST); |
| | 4555 | |
| | 4556 | /* |
| | 4557 | * Evaluate the right-hand side. We don't need to save the result |
| | 4558 | * unless we need the result of the overall expression. Generate |
| | 4559 | * the value as though we were going to booleanize it ourselves, |
| | 4560 | * since we'll do just that (hence pass for_condition = TRUE). |
| | 4561 | */ |
| | 4562 | right_->gen_code(discard, TRUE); |
| | 4563 | |
| | 4564 | /* |
| | 4565 | * If we discarded the result, we generated a JT which explicitly |
| | 4566 | * popped a value. If we didn't discard the result, we generated a |
| | 4567 | * JST; this may or may not pop the value. However, if it doesn't |
| | 4568 | * pop the value (save on true), it will bypass the right side |
| | 4569 | * evaluation, and will thus "pop" that value in the sense that it |
| | 4570 | * will never be pushed. So, note a pop either way. |
| | 4571 | */ |
| | 4572 | G_cg->note_pop(); |
| | 4573 | |
| | 4574 | /* define the label for the jump over the rhs */ |
| | 4575 | def_label_pos(lbl); |
| | 4576 | |
| | 4577 | /* |
| | 4578 | * if the result is not going to be used directly for a condition, |
| | 4579 | * we must boolean-ize the value |
| | 4580 | */ |
| | 4581 | if (!for_condition) |
| | 4582 | G_cg->write_op(OPC_BOOLIZE); |
| | 4583 | } |
| | 4584 | |
| | 4585 | /* |
| | 4586 | * Generate code for the short-circuit OR when used in a condition. We can |
| | 4587 | * use the fact that we're being used conditionally to avoid actually |
| | 4588 | * pushing the result value onto the stack, instead simply branching to the |
| | 4589 | * appropriate point in the enclosing control structure instead. |
| | 4590 | */ |
| | 4591 | void CTPNOr::gen_code_cond(CTcCodeLabel *then_label, |
| | 4592 | CTcCodeLabel *else_label) |
| | 4593 | { |
| | 4594 | CTcCodeLabel *internal_then; |
| | 4595 | |
| | 4596 | /* |
| | 4597 | * First, generate the conditional code for our left operand. If the |
| | 4598 | * condition is true, we can short-circuit the rest of the expression |
| | 4599 | * by jumping directly to the 'then' label. If the caller provided a |
| | 4600 | * 'then' label, we can jump directly to the caller's 'then' label; |
| | 4601 | * otherwise, we must synthesize our own internal label, which we'll |
| | 4602 | * define at the end of our generated code so that we'll fall through |
| | 4603 | * on true to the enclosing code. In any case, we want to fall through |
| | 4604 | * if the condition is false, so that control will flow to the code for |
| | 4605 | * our right operand if the left operand is false. |
| | 4606 | */ |
| | 4607 | internal_then = (then_label == 0 ? G_cs->new_label_fwd() : then_label); |
| | 4608 | left_->gen_code_cond(internal_then, 0); |
| | 4609 | |
| | 4610 | /* |
| | 4611 | * Now, generate code for our right operand. We can generate this code |
| | 4612 | * using the caller's destination labels directly: if we reach this |
| | 4613 | * code at all, it's because the left operand was false, in which case |
| | 4614 | * the result is simply the value of the right operand. |
| | 4615 | */ |
| | 4616 | right_->gen_code_cond(then_label, else_label); |
| | 4617 | |
| | 4618 | /* |
| | 4619 | * If we created an internal 'then' label, it goes at the end of our |
| | 4620 | * generated code: this ensures that we fall off the end of our code |
| | 4621 | * if the left subexpression is true, which is what the caller told us |
| | 4622 | * they wanted when they gave us a null 'then' label. If the caller |
| | 4623 | * gave us an explicit 'then' label, we'll have jumped there directly |
| | 4624 | * if the first subexpression was true. |
| | 4625 | */ |
| | 4626 | if (then_label == 0) |
| | 4627 | def_label_pos(internal_then); |
| | 4628 | } |
| | 4629 | |
| | 4630 | /* ------------------------------------------------------------------------ */ |
| | 4631 | /* |
| | 4632 | * logical AND (short-circuit logic) |
| | 4633 | */ |
| | 4634 | void CTPNAnd::gen_code(int discard, int for_condition) |
| | 4635 | { |
| | 4636 | CTcCodeLabel *lbl; |
| | 4637 | |
| | 4638 | /* |
| | 4639 | * first, evaluate the left-hand side; we need the result even if |
| | 4640 | * we're discarding the overall expression, since we will check the |
| | 4641 | * result to see if we should even evaluate the right-hand side |
| | 4642 | */ |
| | 4643 | left_->gen_code(FALSE, TRUE); |
| | 4644 | |
| | 4645 | /* |
| | 4646 | * If the left-hand side is false, there's no need to evaluate the |
| | 4647 | * right-hand side (and, in fact, we're not even allowed to evaluate |
| | 4648 | * the right-hand side because of the short-circuit logic rule). |
| | 4649 | * So, if the lhs is false, we want to jump around the code to |
| | 4650 | * evaluate the rhs, saving the false result if we're not discarding |
| | 4651 | * the overall result. |
| | 4652 | */ |
| | 4653 | lbl = gen_jump_ahead(discard ? OPC_JF : OPC_JSF); |
| | 4654 | |
| | 4655 | /* |
| | 4656 | * Evaluate the right-hand side. We don't need to save the result |
| | 4657 | * unless we need the result of the overall expression. |
| | 4658 | */ |
| | 4659 | right_->gen_code(discard, TRUE); |
| | 4660 | |
| | 4661 | /* define the label for the jump over the rhs */ |
| | 4662 | def_label_pos(lbl); |
| | 4663 | |
| | 4664 | /* |
| | 4665 | * If we discarded the result, we generated a JF which explicitly |
| | 4666 | * popped a value. If we didn't discard the result, we generated a |
| | 4667 | * JSF; this may or may not pop the value. However, if it doesn't |
| | 4668 | * pop the value (save on false), it will bypass the right side |
| | 4669 | * evaluation, and will thus "pop" that value in the sense that it |
| | 4670 | * will never be pushed. So, note a pop either way. |
| | 4671 | */ |
| | 4672 | G_cg->note_pop(); |
| | 4673 | |
| | 4674 | /* |
| | 4675 | * if the result is not going to be used directly for a condition, |
| | 4676 | * we must boolean-ize the value |
| | 4677 | */ |
| | 4678 | if (!for_condition) |
| | 4679 | G_cg->write_op(OPC_BOOLIZE); |
| | 4680 | } |
| | 4681 | |
| | 4682 | /* |
| | 4683 | * Generate code for the short-circuit AND when used in a condition. We |
| | 4684 | * can use the fact that we're being used conditionally to avoid actually |
| | 4685 | * pushing the result value onto the stack, instead simply branching to the |
| | 4686 | * appropriate point in the enclosing control structure instead. |
| | 4687 | */ |
| | 4688 | void CTPNAnd::gen_code_cond(CTcCodeLabel *then_label, |
| | 4689 | CTcCodeLabel *else_label) |
| | 4690 | { |
| | 4691 | CTcCodeLabel *internal_else; |
| | 4692 | |
| | 4693 | /* |
| | 4694 | * First, generate the conditional code for our left operand. If the |
| | 4695 | * condition is false, we can short-circuit the rest of the expression |
| | 4696 | * by jumping directly to the 'else' label. If the caller provided an |
| | 4697 | * 'else' label, we can jump directly to the caller's 'else' label; |
| | 4698 | * otherwise, we must synthesize our own internal label, which we'll |
| | 4699 | * define at the end of our generated code so that we'll fall through |
| | 4700 | * on false to the enclosing code. In any case, we want to fall |
| | 4701 | * through if the condition is true, so that control will flow to the |
| | 4702 | * code for our right operand if the left operand is true. |
| | 4703 | */ |
| | 4704 | internal_else = (else_label == 0 ? G_cs->new_label_fwd() : else_label); |
| | 4705 | left_->gen_code_cond(0, internal_else); |
| | 4706 | |
| | 4707 | /* |
| | 4708 | * Now, generate code for our right operand. We can generate this code |
| | 4709 | * using the caller's destination labels directly: if we reach this |
| | 4710 | * code at all, it's because the left operand was true, in which case |
| | 4711 | * the result is simply the value of the right operand. |
| | 4712 | */ |
| | 4713 | right_->gen_code_cond(then_label, else_label); |
| | 4714 | |
| | 4715 | /* |
| | 4716 | * If we created an internal 'else' label, it goes at the end of our |
| | 4717 | * generated code: this ensures that we fall off the end of our code |
| | 4718 | * if the left subexpression is false, which is what the caller told |
| | 4719 | * us they wanted when they gave us a null 'else' label. If the |
| | 4720 | * caller gave us an explicit 'else' label, we'll have jumped there |
| | 4721 | * directly if the first subexpression was false. |
| | 4722 | */ |
| | 4723 | if (else_label == 0) |
| | 4724 | def_label_pos(internal_else); |
| | 4725 | } |
| | 4726 | |
| | 4727 | |
| | 4728 | /* ------------------------------------------------------------------------ */ |
| | 4729 | /* |
| | 4730 | * bitwise OR |
| | 4731 | */ |
| | 4732 | void CTPNBOr::gen_code(int discard, int) |
| | 4733 | { |
| | 4734 | gen_binary(OPC_BOR, discard, FALSE); |
| | 4735 | } |
| | 4736 | |
| | 4737 | /* ------------------------------------------------------------------------ */ |
| | 4738 | /* |
| | 4739 | * bitwise AND |
| | 4740 | */ |
| | 4741 | void CTPNBAnd::gen_code(int discard, int) |
| | 4742 | { |
| | 4743 | gen_binary(OPC_BAND, discard, FALSE); |
| | 4744 | } |
| | 4745 | |
| | 4746 | /* ------------------------------------------------------------------------ */ |
| | 4747 | /* |
| | 4748 | * bitwise XOR |
| | 4749 | */ |
| | 4750 | void CTPNBXor::gen_code(int discard, int) |
| | 4751 | { |
| | 4752 | gen_binary(OPC_XOR, discard, FALSE); |
| | 4753 | } |
| | 4754 | |
| | 4755 | /* ------------------------------------------------------------------------ */ |
| | 4756 | /* |
| | 4757 | * greater-than |
| | 4758 | */ |
| | 4759 | void CTPNGt::gen_code(int discard, int) |
| | 4760 | { |
| | 4761 | gen_binary(OPC_GT, discard, FALSE); |
| | 4762 | } |
| | 4763 | |
| | 4764 | /* ------------------------------------------------------------------------ */ |
| | 4765 | /* |
| | 4766 | * greater-or-equal |
| | 4767 | */ |
| | 4768 | void CTPNGe::gen_code(int discard, int) |
| | 4769 | { |
| | 4770 | gen_binary(OPC_GE, discard, FALSE); |
| | 4771 | } |
| | 4772 | |
| | 4773 | /* ------------------------------------------------------------------------ */ |
| | 4774 | /* |
| | 4775 | * less-than |
| | 4776 | */ |
| | 4777 | void CTPNLt::gen_code(int discard, int) |
| | 4778 | { |
| | 4779 | gen_binary(OPC_LT, discard, FALSE); |
| | 4780 | } |
| | 4781 | |
| | 4782 | /* ------------------------------------------------------------------------ */ |
| | 4783 | /* |
| | 4784 | * less-or-equal |
| | 4785 | */ |
| | 4786 | void CTPNLe::gen_code(int discard, int) |
| | 4787 | { |
| | 4788 | gen_binary(OPC_LE, discard, FALSE); |
| | 4789 | } |
| | 4790 | |
| | 4791 | /* ------------------------------------------------------------------------ */ |
| | 4792 | /* |
| | 4793 | * compare for equality |
| | 4794 | */ |
| | 4795 | void CTPNEq::gen_code(int discard, int) |
| | 4796 | { |
| | 4797 | gen_binary(OPC_EQ, discard, FALSE); |
| | 4798 | } |
| | 4799 | |
| | 4800 | /* ------------------------------------------------------------------------ */ |
| | 4801 | /* |
| | 4802 | * compare for inequality |
| | 4803 | */ |
| | 4804 | void CTPNNe::gen_code(int discard, int) |
| | 4805 | { |
| | 4806 | gen_binary(OPC_NE, discard, FALSE); |
| | 4807 | } |
| | 4808 | |
| | 4809 | /* ------------------------------------------------------------------------ */ |
| | 4810 | /* |
| | 4811 | * 'is in' |
| | 4812 | */ |
| | 4813 | void CTPNIsIn::gen_code(int discard, int) |
| | 4814 | { |
| | 4815 | CTPNArglist *lst; |
| | 4816 | CTPNArg *arg; |
| | 4817 | CTcCodeLabel *lbl_found; |
| | 4818 | CTcCodeLabel *lbl_done; |
| | 4819 | |
| | 4820 | /* allocate our 'found' label */ |
| | 4821 | lbl_found = G_cs->new_label_fwd(); |
| | 4822 | |
| | 4823 | /* |
| | 4824 | * allocate our 'done' label - we only need to do this if we don't |
| | 4825 | * have a constant true value and we're not discarding the result |
| | 4826 | */ |
| | 4827 | if (!const_true_ && !discard) |
| | 4828 | lbl_done = G_cs->new_label_fwd(); |
| | 4829 | |
| | 4830 | /* generate my left-side expression */ |
| | 4831 | left_->gen_code(FALSE, FALSE); |
| | 4832 | |
| | 4833 | /* the right side is always an argument list */ |
| | 4834 | lst = (CTPNArglist *)right_; |
| | 4835 | |
| | 4836 | /* compare to each element in the list on the right */ |
| | 4837 | for (arg = lst->get_arg_list_head() ; arg != 0 ; |
| | 4838 | arg = arg->get_next_arg()) |
| | 4839 | { |
| | 4840 | /* |
| | 4841 | * duplicate the left-side value, so we don't have to generate |
| | 4842 | * it again for this comparison |
| | 4843 | */ |
| | 4844 | G_cg->write_op(OPC_DUP); |
| | 4845 | |
| | 4846 | /* generate this list element */ |
| | 4847 | arg->gen_code(FALSE, FALSE); |
| | 4848 | |
| | 4849 | /* if they're equal, jump to the 'found' label */ |
| | 4850 | G_cg->write_op(OPC_JE); |
| | 4851 | G_cs->write_ofs2(lbl_found, 0); |
| | 4852 | |
| | 4853 | /* we pushed one more (DUP) and popped two (JE) */ |
| | 4854 | G_cg->note_push(1); |
| | 4855 | G_cg->note_pop(2); |
| | 4856 | } |
| | 4857 | |
| | 4858 | /* |
| | 4859 | * Generate the code that comes at the end of all of tests when we |
| | 4860 | * fail to find any matches - we simply discard the left-side value |
| | 4861 | * from the stack, push our 'nil' value, and jump to the end label. |
| | 4862 | * |
| | 4863 | * If we have a constant 'true' value, there's no need to do any of |
| | 4864 | * this, because we know that, even after testing all of our |
| | 4865 | * non-constant values, there's a constant value that makes the |
| | 4866 | * entire expression true, and we can thus just fall through to the |
| | 4867 | * 'found' code. |
| | 4868 | * |
| | 4869 | * If we're discarding the result, there's no need to push a |
| | 4870 | * separate value for the result, so we can just fall through to the |
| | 4871 | * common ending code in this case. |
| | 4872 | */ |
| | 4873 | if (!const_true_ && !discard) |
| | 4874 | { |
| | 4875 | G_cg->write_op(OPC_DISC); |
| | 4876 | G_cg->write_op(OPC_PUSHNIL); |
| | 4877 | G_cg->write_op(OPC_JMP); |
| | 4878 | G_cs->write_ofs2(lbl_done, 0); |
| | 4879 | } |
| | 4880 | |
| | 4881 | /* |
| | 4882 | * Generate the 'found' code - this discards the left-side value and |
| | 4883 | * pushes our 'true' result. Note that there's no reason to push |
| | 4884 | * our result if we're discarding it. |
| | 4885 | */ |
| | 4886 | def_label_pos(lbl_found); |
| | 4887 | G_cg->write_op(OPC_DISC); |
| | 4888 | |
| | 4889 | /* |
| | 4890 | * if we're discarding the result, just note the pop of the left |
| | 4891 | * value; otherwise, push our result |
| | 4892 | */ |
| | 4893 | if (discard) |
| | 4894 | G_cg->note_pop(); |
| | 4895 | else |
| | 4896 | G_cg->write_op(OPC_PUSHTRUE); |
| | 4897 | |
| | 4898 | /* our 'done' label is here, if we needed one */ |
| | 4899 | if (!const_true_ && !discard) |
| | 4900 | def_label_pos(lbl_done); |
| | 4901 | } |
| | 4902 | |
| | 4903 | /* ------------------------------------------------------------------------ */ |
| | 4904 | /* |
| | 4905 | * 'not in' |
| | 4906 | */ |
| | 4907 | void CTPNNotIn::gen_code(int discard, int) |
| | 4908 | { |
| | 4909 | CTPNArglist *lst; |
| | 4910 | CTPNArg *arg; |
| | 4911 | CTcCodeLabel *lbl_found; |
| | 4912 | CTcCodeLabel *lbl_done; |
| | 4913 | |
| | 4914 | /* allocate our 'found' label */ |
| | 4915 | lbl_found = G_cs->new_label_fwd(); |
| | 4916 | |
| | 4917 | /* |
| | 4918 | * allocate our 'done' label - we only need to do this if we don't |
| | 4919 | * have a constant false value |
| | 4920 | */ |
| | 4921 | if (!const_false_ && !discard) |
| | 4922 | lbl_done = G_cs->new_label_fwd(); |
| | 4923 | |
| | 4924 | /* generate my left-side expression */ |
| | 4925 | left_->gen_code(FALSE, FALSE); |
| | 4926 | |
| | 4927 | /* the right side is always an argument list */ |
| | 4928 | lst = (CTPNArglist *)right_; |
| | 4929 | |
| | 4930 | /* compare to each element in the list on the right */ |
| | 4931 | for (arg = lst->get_arg_list_head() ; arg != 0 ; |
| | 4932 | arg = arg->get_next_arg()) |
| | 4933 | { |
| | 4934 | /* |
| | 4935 | * duplicate the left-side value, so we don't have to generate |
| | 4936 | * it again for this comparison |
| | 4937 | */ |
| | 4938 | G_cg->write_op(OPC_DUP); |
| | 4939 | |
| | 4940 | /* generate this list element */ |
| | 4941 | arg->gen_code(FALSE, FALSE); |
| | 4942 | |
| | 4943 | /* if they're equal, jump to the 'found' label */ |
| | 4944 | G_cg->write_op(OPC_JE); |
| | 4945 | G_cs->write_ofs2(lbl_found, 0); |
| | 4946 | |
| | 4947 | /* we pushed one more (DUP) and popped two (JE) */ |
| | 4948 | G_cg->note_push(1); |
| | 4949 | G_cg->note_pop(2); |
| | 4950 | } |
| | 4951 | |
| | 4952 | /* |
| | 4953 | * Generate the code that comes at the end of all of tests when we |
| | 4954 | * fail to find any matches - we simply discard the left-side value |
| | 4955 | * from the stack, push our 'true' value, and jump to the end label. |
| | 4956 | * |
| | 4957 | * If we have a constant 'nil' value, however, there's no need to do |
| | 4958 | * any of this, because we know that, even after testing all of our |
| | 4959 | * non-constant values, there's a matching constant value that makes |
| | 4960 | * the entire expression false (because 'not in' is false if we find |
| | 4961 | * a match), and we can thus just fall through to the 'found' code. |
| | 4962 | */ |
| | 4963 | if (!const_false_ && !discard) |
| | 4964 | { |
| | 4965 | G_cg->write_op(OPC_DISC); |
| | 4966 | G_cg->write_op(OPC_PUSHTRUE); |
| | 4967 | G_cg->write_op(OPC_JMP); |
| | 4968 | G_cs->write_ofs2(lbl_done, 0); |
| | 4969 | } |
| | 4970 | |
| | 4971 | /* |
| | 4972 | * generate the 'found' code - this discards the left-side value and |
| | 4973 | * pushes our 'nil' result (because the result of 'not in' is false |
| | 4974 | * if we found the value) |
| | 4975 | */ |
| | 4976 | def_label_pos(lbl_found); |
| | 4977 | G_cg->write_op(OPC_DISC); |
| | 4978 | |
| | 4979 | /* push the result, or note the pop if we're just discarding it */ |
| | 4980 | if (discard) |
| | 4981 | G_cg->note_pop(); |
| | 4982 | else |
| | 4983 | G_cg->write_op(OPC_PUSHNIL); |
| | 4984 | |
| | 4985 | /* our 'done' label is here, if we needed one */ |
| | 4986 | if (!const_false_ && !discard) |
| | 4987 | def_label_pos(lbl_done); |
| | 4988 | } |
| | 4989 | |
| | 4990 | /* ------------------------------------------------------------------------ */ |
| | 4991 | /* |
| | 4992 | * bit-shift left |
| | 4993 | */ |
| | 4994 | void CTPNShl::gen_code(int discard, int) |
| | 4995 | { |
| | 4996 | gen_binary(OPC_SHL, discard, FALSE); |
| | 4997 | } |
| | 4998 | |
| | 4999 | /* ------------------------------------------------------------------------ */ |
| | 5000 | /* |
| | 5001 | * bit-shift right |
| | 5002 | */ |
| | 5003 | void CTPNShr::gen_code(int discard, int) |
| | 5004 | { |
| | 5005 | gen_binary(OPC_SHR, discard, FALSE); |
| | 5006 | } |
| | 5007 | |
| | 5008 | /* ------------------------------------------------------------------------ */ |
| | 5009 | /* |
| | 5010 | * multiply |
| | 5011 | */ |
| | 5012 | void CTPNMul::gen_code(int discard, int) |
| | 5013 | { |
| | 5014 | /* if either side is zero or one, we can apply special handling */ |
| | 5015 | if (left_->is_const_int(0)) |
| | 5016 | { |
| | 5017 | /* evaluate the right for side effects and discard the result */ |
| | 5018 | right_->gen_code(TRUE, TRUE); |
| | 5019 | |
| | 5020 | /* the result is zero */ |
| | 5021 | G_cg->write_op(OPC_PUSH_0); |
| | 5022 | G_cg->note_push(); |
| | 5023 | |
| | 5024 | /* done */ |
| | 5025 | return; |
| | 5026 | } |
| | 5027 | else if (right_->is_const_int(0)) |
| | 5028 | { |
| | 5029 | /* evaluate the left for side effects and discard the result */ |
| | 5030 | left_->gen_code(TRUE, TRUE); |
| | 5031 | |
| | 5032 | /* the result is zero */ |
| | 5033 | G_cg->write_op(OPC_PUSH_0); |
| | 5034 | G_cg->note_push(); |
| | 5035 | |
| | 5036 | /* done */ |
| | 5037 | return; |
| | 5038 | } |
| | 5039 | else if (left_->is_const_int(1)) |
| | 5040 | { |
| | 5041 | /* |
| | 5042 | * evaluate the right side - it's the result; note that, because |
| | 5043 | * of the explicit multiplication, we must compute logical |
| | 5044 | * results using assignment (not 'for condition') rules |
| | 5045 | */ |
| | 5046 | right_->gen_code(discard, FALSE); |
| | 5047 | |
| | 5048 | /* done */ |
| | 5049 | return; |
| | 5050 | } |
| | 5051 | else if (right_->is_const_int(1)) |
| | 5052 | { |
| | 5053 | /* evaluate the right side - it's the result */ |
| | 5054 | left_->gen_code(discard, FALSE); |
| | 5055 | |
| | 5056 | /* done */ |
| | 5057 | return; |
| | 5058 | } |
| | 5059 | |
| | 5060 | /* apply generic handling */ |
| | 5061 | gen_binary(OPC_MUL, discard, FALSE); |
| | 5062 | } |
| | 5063 | |
| | 5064 | /* ------------------------------------------------------------------------ */ |
| | 5065 | /* |
| | 5066 | * divide |
| | 5067 | */ |
| | 5068 | void CTPNDiv::gen_code(int discard, int for_cond) |
| | 5069 | { |
| | 5070 | /* if dividing by 1, we can skip the whole thing (except side effects) */ |
| | 5071 | if (right_->is_const_int(1)) |
| | 5072 | { |
| | 5073 | /* |
| | 5074 | * simply generate the left side for side effects; actually |
| | 5075 | * doing the arithmetic has no effect |
| | 5076 | */ |
| | 5077 | left_->gen_code(discard, for_cond); |
| | 5078 | return; |
| | 5079 | } |
| | 5080 | |
| | 5081 | /* if the left side is zero, the result is always zero */ |
| | 5082 | if (left_->is_const_int(0)) |
| | 5083 | { |
| | 5084 | /* evaluate the right for side effects, but discard the result */ |
| | 5085 | right_->gen_code(TRUE, TRUE); |
| | 5086 | |
| | 5087 | /* the result is zero */ |
| | 5088 | G_cg->write_op(OPC_PUSH_0); |
| | 5089 | G_cg->note_push(); |
| | 5090 | return; |
| | 5091 | } |
| | 5092 | |
| | 5093 | /* use generic code generation */ |
| | 5094 | gen_binary(OPC_DIV, discard, FALSE); |
| | 5095 | } |
| | 5096 | |
| | 5097 | /* ------------------------------------------------------------------------ */ |
| | 5098 | /* |
| | 5099 | * modulo |
| | 5100 | */ |
| | 5101 | void CTPNMod::gen_code(int discard, int for_condition) |
| | 5102 | { |
| | 5103 | /* if dividing by 1, we can skip the whole thing (except side effects) */ |
| | 5104 | if (right_->is_const_int(1)) |
| | 5105 | { |
| | 5106 | /* |
| | 5107 | * simply generate the left side for side effects; actually |
| | 5108 | * doing the arithmetic has no effect |
| | 5109 | */ |
| | 5110 | left_->gen_code(discard, for_condition); |
| | 5111 | |
| | 5112 | /* the result is zero */ |
| | 5113 | G_cg->write_op(OPC_PUSH_0); |
| | 5114 | G_cg->note_push(); |
| | 5115 | return; |
| | 5116 | } |
| | 5117 | |
| | 5118 | /* if the left side is zero, the result is always zero */ |
| | 5119 | if (left_->is_const_int(0)) |
| | 5120 | { |
| | 5121 | /* evaluate the right for side effects, but discard the result */ |
| | 5122 | right_->gen_code(TRUE, TRUE); |
| | 5123 | |
| | 5124 | /* the result is zero */ |
| | 5125 | G_cg->write_op(OPC_PUSH_0); |
| | 5126 | G_cg->note_push(); |
| | 5127 | return; |
| | 5128 | } |
| | 5129 | |
| | 5130 | /* use generic processing */ |
| | 5131 | gen_binary(OPC_MOD, discard, FALSE); |
| | 5132 | } |
| | 5133 | |
| | 5134 | /* ------------------------------------------------------------------------ */ |
| | 5135 | /* |
| | 5136 | * subtract |
| | 5137 | */ |
| | 5138 | void CTPNSub::gen_code(int discard, int for_cond) |
| | 5139 | { |
| | 5140 | /* check for subtracting 1, which we can accomplish more efficiently */ |
| | 5141 | if (right_->is_const_int(1)) |
| | 5142 | { |
| | 5143 | /* |
| | 5144 | * We're subtracting one - use decrement. The decrement |
| | 5145 | * operator itself has no side effects, so we can pass through |
| | 5146 | * the 'discard' status to the subnode. |
| | 5147 | */ |
| | 5148 | left_->gen_code(discard, FALSE); |
| | 5149 | |
| | 5150 | /* apply decrement if we're not discarding the result */ |
| | 5151 | if (!discard) |
| | 5152 | G_cg->write_op(OPC_DEC); |
| | 5153 | } |
| | 5154 | else |
| | 5155 | { |
| | 5156 | /* we can't do anything special - use the general-purpose code */ |
| | 5157 | gen_binary(OPC_SUB, discard, FALSE); |
| | 5158 | } |
| | 5159 | } |
| | 5160 | |
| | 5161 | /* ------------------------------------------------------------------------ */ |
| | 5162 | /* |
| | 5163 | * add |
| | 5164 | */ |
| | 5165 | void CTPNAdd::gen_code(int discard, int) |
| | 5166 | { |
| | 5167 | /* check for adding 1, which we can accomplish more efficiently */ |
| | 5168 | if (right_->is_const_int(1)) |
| | 5169 | { |
| | 5170 | /* |
| | 5171 | * We're adding one - use increment. The increment operator |
| | 5172 | * itself has no side effects, so we can pass through the |
| | 5173 | * 'discard' status to the subnode. |
| | 5174 | */ |
| | 5175 | left_->gen_code(discard, FALSE); |
| | 5176 | |
| | 5177 | /* apply increment if we're not discarding the result */ |
| | 5178 | if (!discard) |
| | 5179 | G_cg->write_op(OPC_INC); |
| | 5180 | } |
| | 5181 | else |
| | 5182 | { |
| | 5183 | /* we can't do anything special - use the general-purpose code */ |
| | 5184 | gen_binary(OPC_ADD, discard, FALSE); |
| | 5185 | } |
| | 5186 | } |
| | 5187 | |
| | 5188 | /* ------------------------------------------------------------------------ */ |
| | 5189 | /* |
| | 5190 | * simple assignment |
| | 5191 | */ |
| | 5192 | void CTPNAsi::gen_code(int discard, int) |
| | 5193 | { |
| | 5194 | /* |
| | 5195 | * Ask the left subnode to generate a simple assignment to the value |
| | 5196 | * on the right. Simple assignments cannot be refused, so we don't |
| | 5197 | * need to try to do any assignment work ourselves. |
| | 5198 | */ |
| | 5199 | left_->gen_code_asi(discard, TC_ASI_SIMPLE, right_, FALSE); |
| | 5200 | } |
| | 5201 | |
| | 5202 | /* ------------------------------------------------------------------------ */ |
| | 5203 | /* |
| | 5204 | * add and assign |
| | 5205 | */ |
| | 5206 | void CTPNAddAsi::gen_code(int discard, int) |
| | 5207 | { |
| | 5208 | /* |
| | 5209 | * ask the left subnode to generate an add-and-assign; if it can't, |
| | 5210 | * handle it generically |
| | 5211 | */ |
| | 5212 | if (!left_->gen_code_asi(discard, TC_ASI_ADD, right_, FALSE)) |
| | 5213 | { |
| | 5214 | /* |
| | 5215 | * there's no special coding for this assignment type -- compute |
| | 5216 | * the result generically, then assign the result as a simple |
| | 5217 | * assignment, which cannot be refused |
| | 5218 | */ |
| | 5219 | gen_binary(OPC_ADD, FALSE, FALSE); |
| | 5220 | left_->gen_code_asi(discard, TC_ASI_SIMPLE, 0, FALSE); |
| | 5221 | } |
| | 5222 | } |
| | 5223 | |
| | 5224 | /* ------------------------------------------------------------------------ */ |
| | 5225 | /* |
| | 5226 | * subtract and assign |
| | 5227 | */ |
| | 5228 | void CTPNSubAsi::gen_code(int discard, int) |
| | 5229 | { |
| | 5230 | /* |
| | 5231 | * ask the left subnode to generate a subtract-and-assign; if it |
| | 5232 | * can't, handle it generically |
| | 5233 | */ |
| | 5234 | if (!left_->gen_code_asi(discard, TC_ASI_SUB, right_, FALSE)) |
| | 5235 | { |
| | 5236 | /* |
| | 5237 | * there's no special coding for this assignment type -- compute |
| | 5238 | * the result generically, then assign the result as a simple |
| | 5239 | * assignment, which cannot be refused |
| | 5240 | */ |
| | 5241 | gen_binary(OPC_SUB, FALSE, FALSE); |
| | 5242 | left_->gen_code_asi(discard, TC_ASI_SIMPLE, 0, FALSE); |
| | 5243 | } |
| | 5244 | } |
| | 5245 | |
| | 5246 | /* ------------------------------------------------------------------------ */ |
| | 5247 | /* |
| | 5248 | * multiply and assign |
| | 5249 | */ |
| | 5250 | void CTPNMulAsi::gen_code(int discard, int) |
| | 5251 | { |
| | 5252 | /* |
| | 5253 | * ask the left subnode to generate a multiply-and-assign; if it |
| | 5254 | * can't, handle it generically |
| | 5255 | */ |
| | 5256 | if (!left_->gen_code_asi(discard, TC_ASI_MUL, right_, FALSE)) |
| | 5257 | { |
| | 5258 | /* |
| | 5259 | * there's no special coding for this assignment type -- compute |
| | 5260 | * the result generically, then assign the result as a simple |
| | 5261 | * assignment, which cannot be refused |
| | 5262 | */ |
| | 5263 | gen_binary(OPC_MUL, FALSE, FALSE); |
| | 5264 | left_->gen_code_asi(discard, TC_ASI_SIMPLE, 0, FALSE); |
| | 5265 | } |
| | 5266 | } |
| | 5267 | |
| | 5268 | /* ------------------------------------------------------------------------ */ |
| | 5269 | /* |
| | 5270 | * divide and assign |
| | 5271 | */ |
| | 5272 | void CTPNDivAsi::gen_code(int discard, int) |
| | 5273 | { |
| | 5274 | /* |
| | 5275 | * ask the left subnode to generate a divide-and-assign; if it |
| | 5276 | * can't, handle it generically |
| | 5277 | */ |
| | 5278 | if (!left_->gen_code_asi(discard, TC_ASI_DIV, right_, FALSE)) |
| | 5279 | { |
| | 5280 | /* |
| | 5281 | * there's no special coding for this assignment type -- compute |
| | 5282 | * the result generically, then assign the result as a simple |
| | 5283 | * assignment, which cannot be refused |
| | 5284 | */ |
| | 5285 | gen_binary(OPC_DIV, FALSE, FALSE); |
| | 5286 | left_->gen_code_asi(discard, TC_ASI_SIMPLE, 0, FALSE); |
| | 5287 | } |
| | 5288 | } |
| | 5289 | |
| | 5290 | /* ------------------------------------------------------------------------ */ |
| | 5291 | /* |
| | 5292 | * modulo and assign |
| | 5293 | */ |
| | 5294 | void CTPNModAsi::gen_code(int discard, int) |
| | 5295 | { |
| | 5296 | /* |
| | 5297 | * ask the left subnode to generate a mod-and-assign; if it can't, |
| | 5298 | * handle it generically |
| | 5299 | */ |
| | 5300 | if (!left_->gen_code_asi(discard, TC_ASI_MOD, right_, FALSE)) |
| | 5301 | { |
| | 5302 | /* |
| | 5303 | * there's no special coding for this assignment type -- compute |
| | 5304 | * the result generically, then assign the result as a simple |
| | 5305 | * assignment, which cannot be refused |
| | 5306 | */ |
| | 5307 | gen_binary(OPC_MOD, FALSE, FALSE); |
| | 5308 | left_->gen_code_asi(discard, TC_ASI_SIMPLE, 0, FALSE); |
| | 5309 | } |
| | 5310 | } |
| | 5311 | |
| | 5312 | /* ------------------------------------------------------------------------ */ |
| | 5313 | /* |
| | 5314 | * bitwise-AND and assign |
| | 5315 | */ |
| | 5316 | void CTPNBAndAsi::gen_code(int discard, int) |
| | 5317 | { |
| | 5318 | /* |
| | 5319 | * ask the left subnode to generate an AND-and-assign; if it can't, |
| | 5320 | * handle it generically |
| | 5321 | */ |
| | 5322 | if (!left_->gen_code_asi(discard, TC_ASI_BAND, right_, FALSE)) |
| | 5323 | { |
| | 5324 | /* |
| | 5325 | * there's no special coding for this assignment type -- compute |
| | 5326 | * the result generically, then assign the result as a simple |
| | 5327 | * assignment, which cannot be refused |
| | 5328 | */ |
| | 5329 | gen_binary(OPC_BAND, FALSE, FALSE); |
| | 5330 | left_->gen_code_asi(discard, TC_ASI_SIMPLE, 0, FALSE); |
| | 5331 | } |
| | 5332 | } |
| | 5333 | |
| | 5334 | /* ------------------------------------------------------------------------ */ |
| | 5335 | /* |
| | 5336 | * bitwise-OR and assign |
| | 5337 | */ |
| | 5338 | void CTPNBOrAsi::gen_code(int discard, int) |
| | 5339 | { |
| | 5340 | /* |
| | 5341 | * ask the left subnode to generate an OR-and-assign; if it can't, |
| | 5342 | * handle it generically |
| | 5343 | */ |
| | 5344 | if (!left_->gen_code_asi(discard, TC_ASI_BOR, right_, FALSE)) |
| | 5345 | { |
| | 5346 | /* |
| | 5347 | * there's no special coding for this assignment type -- compute |
| | 5348 | * the result generically, then assign the result as a simple |
| | 5349 | * assignment, which cannot be refused |
| | 5350 | */ |
| | 5351 | gen_binary(OPC_BOR, FALSE, FALSE); |
| | 5352 | left_->gen_code_asi(discard, TC_ASI_SIMPLE, 0, FALSE); |
| | 5353 | } |
| | 5354 | } |
| | 5355 | |
| | 5356 | /* ------------------------------------------------------------------------ */ |
| | 5357 | /* |
| | 5358 | * bitwise-XOR and assign |
| | 5359 | */ |
| | 5360 | void CTPNBXorAsi::gen_code(int discard, int) |
| | 5361 | { |
| | 5362 | /* |
| | 5363 | * ask the left subnode to generate an XOR-and-assign; if it can't, |
| | 5364 | * handle it generically |
| | 5365 | */ |
| | 5366 | if (!left_->gen_code_asi(discard, TC_ASI_BXOR, right_, FALSE)) |
| | 5367 | { |
| | 5368 | /* |
| | 5369 | * there's no special coding for this assignment type -- compute |
| | 5370 | * the result generically, then assign the result as a simple |
| | 5371 | * assignment, which cannot be refused |
| | 5372 | */ |
| | 5373 | gen_binary(OPC_XOR, FALSE, FALSE); |
| | 5374 | left_->gen_code_asi(discard, TC_ASI_SIMPLE, 0, FALSE); |
| | 5375 | } |
| | 5376 | } |
| | 5377 | |
| | 5378 | /* ------------------------------------------------------------------------ */ |
| | 5379 | /* |
| | 5380 | * bit-shift left and assign |
| | 5381 | */ |
| | 5382 | void CTPNShlAsi::gen_code(int discard, int) |
| | 5383 | { |
| | 5384 | /* |
| | 5385 | * ask the left subnode to generate an shift-left-and-assign; if it |
| | 5386 | * can't, handle it generically |
| | 5387 | */ |
| | 5388 | if (!left_->gen_code_asi(discard, TC_ASI_SHL, right_, FALSE)) |
| | 5389 | { |
| | 5390 | /* |
| | 5391 | * there's no special coding for this assignment type -- compute |
| | 5392 | * the result generically, then assign the result as a simple |
| | 5393 | * assignment, which cannot be refused |
| | 5394 | */ |
| | 5395 | gen_binary(OPC_SHL, FALSE, FALSE); |
| | 5396 | left_->gen_code_asi(discard, TC_ASI_SIMPLE, 0, FALSE); |
| | 5397 | } |
| | 5398 | } |
| | 5399 | |
| | 5400 | /* ------------------------------------------------------------------------ */ |
| | 5401 | /* |
| | 5402 | * bit-shift right and assign |
| | 5403 | */ |
| | 5404 | void CTPNShrAsi::gen_code(int discard, int) |
| | 5405 | { |
| | 5406 | /* |
| | 5407 | * ask the left subnode to generate a shift-right-and-assign; if it |
| | 5408 | * can't, handle it generically |
| | 5409 | */ |
| | 5410 | if (!left_->gen_code_asi(discard, TC_ASI_SHR, right_, FALSE)) |
| | 5411 | { |
| | 5412 | /* |
| | 5413 | * there's no special coding for this assignment type -- compute |
| | 5414 | * the result generically, then assign the result as a simple |
| | 5415 | * assignment, which cannot be refused |
| | 5416 | */ |
| | 5417 | gen_binary(OPC_SHR, FALSE, FALSE); |
| | 5418 | left_->gen_code_asi(discard, TC_ASI_SIMPLE, 0, FALSE); |
| | 5419 | } |
| | 5420 | } |
| | 5421 | |
| | 5422 | /* ------------------------------------------------------------------------ */ |
| | 5423 | /* |
| | 5424 | * subscript a list/array value |
| | 5425 | */ |
| | 5426 | void CTPNSubscript::gen_code(int discard, int) |
| | 5427 | { |
| | 5428 | gen_binary(OPC_INDEX, discard, FALSE); |
| | 5429 | } |
| | 5430 | |
| | 5431 | /* |
| | 5432 | * assign to a subscripted value |
| | 5433 | */ |
| | 5434 | int CTPNSubscript::gen_code_asi(int discard, tc_asitype_t typ, |
| | 5435 | CTcPrsNode *rhs, int) |
| | 5436 | { |
| | 5437 | /* |
| | 5438 | * If this isn't a simple assignment, tell the caller to emit the |
| | 5439 | * generic code to compute the composite result, then call us again |
| | 5440 | * for a simple assignment. We can't add any value with specialized |
| | 5441 | * instructions for composite assignments, so there's no point in |
| | 5442 | * dealing with those here. |
| | 5443 | */ |
| | 5444 | if (typ != TC_ASI_SIMPLE) |
| | 5445 | return FALSE; |
| | 5446 | |
| | 5447 | /* |
| | 5448 | * Generate the value to assign to the element - that's the right |
| | 5449 | * side of the assignment operator. If rhs is null, it means the |
| | 5450 | * caller has already done this. |
| | 5451 | */ |
| | 5452 | if (rhs != 0) |
| | 5453 | rhs->gen_code(FALSE, FALSE); |
| | 5454 | |
| | 5455 | /* |
| | 5456 | * if we're not discarding the result, duplicate the value to be |
| | 5457 | * assigned, so that it's left on the stack after we're finished |
| | 5458 | * (this is necessary because we'll consume one copy with the SETIND |
| | 5459 | * instruction) |
| | 5460 | */ |
| | 5461 | if (!discard) |
| | 5462 | { |
| | 5463 | G_cg->write_op(OPC_DUP); |
| | 5464 | G_cg->note_push(); |
| | 5465 | } |
| | 5466 | |
| | 5467 | /* generate the value to be subscripted - that's my left-hand side */ |
| | 5468 | left_->gen_code(FALSE, FALSE); |
| | 5469 | |
| | 5470 | /* generate the index value - that's my right-hand side */ |
| | 5471 | right_->gen_code(FALSE, FALSE); |
| | 5472 | |
| | 5473 | /* generate the assign-to-indexed-value opcode */ |
| | 5474 | G_cg->write_op(OPC_SETIND); |
| | 5475 | |
| | 5476 | /* setind pops three and pushes one - net of pop 2 */ |
| | 5477 | G_cg->note_pop(2); |
| | 5478 | |
| | 5479 | /* |
| | 5480 | * The top value now on the stack is the new container value. The new |
| | 5481 | * container will be different from the old container in some cases |
| | 5482 | * (with lists, for example, because we must create a new list object |
| | 5483 | * to contain the modified list value). Therefore, if my left-hand |
| | 5484 | * side is an lvalue, we must assign the new container to the left-hand |
| | 5485 | * side - this makes something like "x[1] = 5" actually change the |
| | 5486 | * value in "x" if "x" is a local variable. If my left-hand side isn't |
| | 5487 | * an lvalue, don't bother with this step, and simply discard the new |
| | 5488 | * container value. |
| | 5489 | * |
| | 5490 | * Regardless of whether we're keeping the result of the overall |
| | 5491 | * expression, we're definitely not keeping the result of assigning the |
| | 5492 | * new container - the result of the assignment is the value assigned, |
| | 5493 | * not the container. Thus, discard = true in this call. |
| | 5494 | * |
| | 5495 | * There's a special case that's handled through the peep-hole |
| | 5496 | * optimizer: if we are assigning to a local variable and indexing with |
| | 5497 | * a constant integer value, we will have converted the whole operation |
| | 5498 | * to a SETINDLCL1I8. That instruction takes care of assigning the |
| | 5499 | * value back to the rvalue, so we don't need to generate a separate |
| | 5500 | * rvalue assignment. |
| | 5501 | */ |
| | 5502 | if (G_cg->get_last_op() == OPC_SETINDLCL1I8) |
| | 5503 | { |
| | 5504 | /* |
| | 5505 | * no assignment is necessary - we just need to account for the |
| | 5506 | * difference in the stack arrangement with this form of the |
| | 5507 | * assignment, which is that we don't leave the value on the stack |
| | 5508 | */ |
| | 5509 | G_cg->note_pop(); |
| | 5510 | } |
| | 5511 | else if (!left_->gen_code_asi(TRUE, TC_ASI_SIMPLE, 0, TRUE)) |
| | 5512 | { |
| | 5513 | /* no assignment is possible; discard the new container value */ |
| | 5514 | G_cg->write_op(OPC_DISC); |
| | 5515 | G_cg->note_pop(); |
| | 5516 | } |
| | 5517 | |
| | 5518 | /* handled */ |
| | 5519 | return TRUE; |
| | 5520 | } |
| | 5521 | |
| | 5522 | /* ------------------------------------------------------------------------ */ |
| | 5523 | /* |
| | 5524 | * conditional operator |
| | 5525 | */ |
| | 5526 | void CTPNIf::gen_code(int discard, int for_condition) |
| | 5527 | { |
| | 5528 | CTcCodeLabel *lbl_else; |
| | 5529 | CTcCodeLabel *lbl_end; |
| | 5530 | |
| | 5531 | /* |
| | 5532 | * Generate the condition value - we need the value regardless of |
| | 5533 | * whether the overall result is going to be used, because we need |
| | 5534 | * it to determine which branch to take. Generate the subexpression |
| | 5535 | * for a condition, so that we don't perform any extra unnecessary |
| | 5536 | * conversions on it. |
| | 5537 | */ |
| | 5538 | first_->gen_code(FALSE, TRUE); |
| | 5539 | |
| | 5540 | /* if the condition is false, jump to the 'else' expression part */ |
| | 5541 | lbl_else = gen_jump_ahead(OPC_JF); |
| | 5542 | |
| | 5543 | /* JF pops a value */ |
| | 5544 | G_cg->note_pop(); |
| | 5545 | |
| | 5546 | /* |
| | 5547 | * Generate the 'then' expression part. Only request a return value if |
| | 5548 | * it has one AND we're not discarding it. If it doesn't return a |
| | 5549 | * value, and we actually need one, we'll supply a default 'nil' value |
| | 5550 | * next. This value will be our yielded value (in this branch, |
| | 5551 | * anyway), so pass through the for-condition flag. |
| | 5552 | */ |
| | 5553 | second_->gen_code(discard || !second_->has_return_value(), for_condition); |
| | 5554 | |
| | 5555 | /* |
| | 5556 | * If this expression has no return value, and we need the return |
| | 5557 | * value, supply nil as the result. |
| | 5558 | */ |
| | 5559 | if (!discard && !second_->has_return_value()) |
| | 5560 | { |
| | 5561 | G_cg->write_op(OPC_PUSHNIL); |
| | 5562 | G_cg->note_push(); |
| | 5563 | } |
| | 5564 | |
| | 5565 | /* unconditionally jump over the 'else' part */ |
| | 5566 | lbl_end = gen_jump_ahead(OPC_JMP); |
| | 5567 | |
| | 5568 | /* set the label for the 'else' part */ |
| | 5569 | def_label_pos(lbl_else); |
| | 5570 | |
| | 5571 | /* |
| | 5572 | * Generate the 'else' part. Only request a return value if it has one |
| | 5573 | * AND we're not discarding it. Pass through 'discard' and |
| | 5574 | * 'for_condition', since this result is our result. |
| | 5575 | */ |
| | 5576 | third_->gen_code(discard || !third_->has_return_value(), for_condition); |
| | 5577 | |
| | 5578 | /* |
| | 5579 | * If this expression has no return value, and we need the return |
| | 5580 | * value, supply nil as the result. |
| | 5581 | */ |
| | 5582 | if (!discard && !third_->has_return_value()) |
| | 5583 | { |
| | 5584 | G_cg->write_op(OPC_PUSHNIL); |
| | 5585 | G_cg->note_push(); |
| | 5586 | } |
| | 5587 | |
| | 5588 | /* |
| | 5589 | * Because of the jump, we only evaluate one of the two expressions |
| | 5590 | * we generated, so note an extra pop for the branch we didn't take. |
| | 5591 | * Note that if either one pushes a value, both will, since we'll |
| | 5592 | * explicitly have pushed nil for the one that doesn't generate a |
| | 5593 | * value to keep the stack balanced on both branches. |
| | 5594 | * |
| | 5595 | * If neither of our expressions yields a value, don't pop anything |
| | 5596 | * extra, since we won't think we've pushed two values in the course |
| | 5597 | * of generating the two expressions. |
| | 5598 | */ |
| | 5599 | if (second_->has_return_value() || third_->has_return_value()) |
| | 5600 | G_cg->note_pop(); |
| | 5601 | |
| | 5602 | /* set the label for the end of the expression */ |
| | 5603 | def_label_pos(lbl_end); |
| | 5604 | } |
| | 5605 | |
| | 5606 | /* ------------------------------------------------------------------------ */ |
| | 5607 | /* |
| | 5608 | * symbol |
| | 5609 | */ |
| | 5610 | void CTPNSym::gen_code(int discard, int) |
| | 5611 | { |
| | 5612 | /* |
| | 5613 | * Look up the symbol; if it's undefined, add a default property |
| | 5614 | * symbol entry if possible. Then ask the symbol to generate the |
| | 5615 | * code. |
| | 5616 | */ |
| | 5617 | G_cs->get_symtab() |
| | 5618 | ->find_or_def_prop_implied(get_sym_text(), get_sym_text_len(), |
| | 5619 | FALSE, G_cs->is_self_available()) |
| | 5620 | ->gen_code(discard); |
| | 5621 | } |
| | 5622 | |
| | 5623 | /* |
| | 5624 | * assign to a symbol |
| | 5625 | */ |
| | 5626 | int CTPNSym::gen_code_asi(int discard, tc_asitype_t typ, CTcPrsNode *rhs, |
| | 5627 | int ignore_errors) |
| | 5628 | { |
| | 5629 | /* |
| | 5630 | * Look up the symbol; if it's undefined and there's a "self" object |
| | 5631 | * available, define it as a property by default, since a property |
| | 5632 | * is the only kind of symbol that we could possibly assign to |
| | 5633 | * without having defined anywhere in the program. Once we have the |
| | 5634 | * symbol, tell it to generate the code for assigning to it. |
| | 5635 | */ |
| | 5636 | return G_cs->get_symtab() |
| | 5637 | ->find_or_def_prop_implied(get_sym_text(), get_sym_text_len(), |
| | 5638 | FALSE, G_cs->is_self_available()) |
| | 5639 | ->gen_code_asi(discard, typ, rhs, ignore_errors); |
| | 5640 | } |
| | 5641 | |
| | 5642 | /* |
| | 5643 | * take the address of the symbol |
| | 5644 | */ |
| | 5645 | void CTPNSym::gen_code_addr() |
| | 5646 | { |
| | 5647 | /* |
| | 5648 | * Look up our symbol in the symbol table, then ask the resulting |
| | 5649 | * symbol to generate the appropriate code. If the symbol isn't |
| | 5650 | * defined, and we have a "self" object available (i.e., we're in |
| | 5651 | * method code), define the symbol by default as a property. |
| | 5652 | * |
| | 5653 | * Note that we look only in the global symbol table, because local |
| | 5654 | * symbols have no address value. So, even if the symbol is defined |
| | 5655 | * in the local table, ignore the local definition and look at the |
| | 5656 | * global definition. |
| | 5657 | */ |
| | 5658 | G_prs->get_global_symtab() |
| | 5659 | ->find_or_def_prop_explicit(get_sym_text(), get_sym_text_len(), |
| | 5660 | FALSE) |
| | 5661 | ->gen_code_addr(); |
| | 5662 | } |
| | 5663 | |
| | 5664 | /* |
| | 5665 | * call the symbol |
| | 5666 | */ |
| | 5667 | void CTPNSym::gen_code_call(int discard, int argc, int varargs) |
| | 5668 | { |
| | 5669 | /* |
| | 5670 | * Look up our symbol in the symbol table, then ask the resulting |
| | 5671 | * symbol to generate the appropriate call. The symbol is |
| | 5672 | * implicitly a property (if in a method context), since that's the |
| | 5673 | * only kind of undefined symbol that we could be calling. |
| | 5674 | */ |
| | 5675 | G_cs->get_symtab() |
| | 5676 | ->find_or_def_prop_implied(get_sym_text(), get_sym_text_len(), |
| | 5677 | FALSE, G_cs->is_self_available()) |
| | 5678 | ->gen_code_call(discard, argc, varargs); |
| | 5679 | } |
| | 5680 | |
| | 5681 | /* |
| | 5682 | * generate code for 'new' |
| | 5683 | */ |
| | 5684 | void CTPNSym::gen_code_new(int discard, int argc, int varargs, |
| | 5685 | int /*from_call*/, int is_transient) |
| | 5686 | { |
| | 5687 | /* |
| | 5688 | * Look up our symbol, then ask the resulting symbol to generate the |
| | 5689 | * 'new' code. If the symbol is undefined, add an 'undefined' entry |
| | 5690 | * to the table; we can't implicitly create an object symbol. |
| | 5691 | */ |
| | 5692 | G_cs->get_symtab() |
| | 5693 | ->find_or_def_undef(get_sym_text(), get_sym_text_len(), FALSE) |
| | 5694 | ->gen_code_new(discard, argc, varargs, is_transient); |
| | 5695 | } |
| | 5696 | |
| | 5697 | /* |
| | 5698 | * generate a property ID expression |
| | 5699 | */ |
| | 5700 | vm_prop_id_t CTPNSym::gen_code_propid(int check_only, int is_expr) |
| | 5701 | { |
| | 5702 | CTcSymbol *sym; |
| | 5703 | CTcPrsSymtab *symtab; |
| | 5704 | |
| | 5705 | /* |
| | 5706 | * Figure out where to look for the symbol. If the symbol was given |
| | 5707 | * as an expression (in other words, it was explicitly enclosed in |
| | 5708 | * parentheses), look it up in the local symbol table, since it |
| | 5709 | * could refer to a local. Otherwise, it must refer to a property, |
| | 5710 | * so look only in the global table. |
| | 5711 | * |
| | 5712 | * If the symbol isn't defined already, define it as a property now. |
| | 5713 | * Because the symbol is explicitly on the right side of a member |
| | 5714 | * evaluation, we can define it as a property whether or not there's |
| | 5715 | * a valid "self" in this context. |
| | 5716 | */ |
| | 5717 | if (is_expr) |
| | 5718 | { |
| | 5719 | /* it's an expression - look it up in the local symbol table */ |
| | 5720 | symtab = G_cs->get_symtab(); |
| | 5721 | } |
| | 5722 | else |
| | 5723 | { |
| | 5724 | /* it's a simple symbol - look only in the global symbol table */ |
| | 5725 | symtab = G_prs->get_global_symtab(); |
| | 5726 | } |
| | 5727 | |
| | 5728 | /* |
| | 5729 | * look it up (note that this will always return a valid symbol, |
| | 5730 | * since it will create one if we can't find an existing entry) |
| | 5731 | */ |
| | 5732 | sym = symtab->find_or_def_prop(get_sym_text(), get_sym_text_len(), FALSE); |
| | 5733 | |
| | 5734 | /* ask the symbol to generate the property reference */ |
| | 5735 | return sym->gen_code_propid(check_only, is_expr); |
| | 5736 | } |
| | 5737 | |
| | 5738 | /* |
| | 5739 | * generate code for a member expression |
| | 5740 | */ |
| | 5741 | void CTPNSym::gen_code_member(int discard, CTcPrsNode *prop_expr, |
| | 5742 | int prop_is_expr, int argc, int varargs) |
| | 5743 | { |
| | 5744 | /* |
| | 5745 | * Look up the symbol, and let it do the work. There's no |
| | 5746 | * appropriate default for the symbol, so leave it undefined if we |
| | 5747 | * can't find it. |
| | 5748 | */ |
| | 5749 | G_cs->get_symtab() |
| | 5750 | ->find_or_def_undef(get_sym_text(), get_sym_text_len(), FALSE) |
| | 5751 | ->gen_code_member(discard, prop_expr, prop_is_expr, argc, varargs); |
| | 5752 | } |
| | 5753 | |
| | 5754 | /* |
| | 5755 | * generate code for an object before a '.' |
| | 5756 | */ |
| | 5757 | vm_obj_id_t CTPNSym::gen_code_obj_predot(int *is_self) |
| | 5758 | { |
| | 5759 | /* |
| | 5760 | * Look up the symbol, and let it do the work. There's no default |
| | 5761 | * type for the symbol, so leave it undefined if we don't find it. |
| | 5762 | */ |
| | 5763 | return G_cs->get_symtab() |
| | 5764 | ->find_or_def_undef(get_sym_text(), get_sym_text_len(), FALSE) |
| | 5765 | ->gen_code_obj_predot(is_self); |
| | 5766 | } |
| | 5767 | |
| | 5768 | /* ------------------------------------------------------------------------ */ |
| | 5769 | /* |
| | 5770 | * resolved symbol |
| | 5771 | */ |
| | 5772 | void CTPNSymResolved::gen_code(int discard, int) |
| | 5773 | { |
| | 5774 | /* let the symbol handle it */ |
| | 5775 | sym_->gen_code(discard); |
| | 5776 | } |
| | 5777 | |
| | 5778 | /* |
| | 5779 | * assign to a symbol |
| | 5780 | */ |
| | 5781 | int CTPNSymResolved::gen_code_asi(int discard, tc_asitype_t typ, |
| | 5782 | CTcPrsNode *rhs, |
| | 5783 | int ignore_errors) |
| | 5784 | { |
| | 5785 | /* let the symbol handle it */ |
| | 5786 | return sym_->gen_code_asi(discard, typ, rhs, ignore_errors); |
| | 5787 | } |
| | 5788 | |
| | 5789 | /* |
| | 5790 | * take the address of the symbol |
| | 5791 | */ |
| | 5792 | void CTPNSymResolved::gen_code_addr() |
| | 5793 | { |
| | 5794 | /* let the symbol handle it */ |
| | 5795 | sym_->gen_code_addr(); |
| | 5796 | } |
| | 5797 | |
| | 5798 | /* |
| | 5799 | * call the symbol |
| | 5800 | */ |
| | 5801 | void CTPNSymResolved::gen_code_call(int discard, int argc, int varargs) |
| | 5802 | { |
| | 5803 | /* let the symbol handle it */ |
| | 5804 | sym_->gen_code_call(discard, argc, varargs); |
| | 5805 | } |
| | 5806 | |
| | 5807 | /* |
| | 5808 | * generate code for 'new' |
| | 5809 | */ |
| | 5810 | void CTPNSymResolved::gen_code_new(int discard, int argc, int varargs, |
| | 5811 | int /*from_call*/, int is_transient) |
| | 5812 | { |
| | 5813 | /* let the symbol handle it */ |
| | 5814 | sym_->gen_code_new(discard, argc, varargs, is_transient); |
| | 5815 | } |
| | 5816 | |
| | 5817 | /* |
| | 5818 | * generate a property ID expression |
| | 5819 | */ |
| | 5820 | vm_prop_id_t CTPNSymResolved::gen_code_propid(int check_only, int is_expr) |
| | 5821 | { |
| | 5822 | /* let the symbol handle it */ |
| | 5823 | return sym_->gen_code_propid(check_only, is_expr); |
| | 5824 | } |
| | 5825 | |
| | 5826 | /* |
| | 5827 | * generate code for a member expression |
| | 5828 | */ |
| | 5829 | void CTPNSymResolved::gen_code_member(int discard, |
| | 5830 | CTcPrsNode *prop_expr, int prop_is_expr, |
| | 5831 | int argc, int varargs) |
| | 5832 | { |
| | 5833 | /* let the symbol handle it */ |
| | 5834 | sym_->gen_code_member(discard, prop_expr, prop_is_expr, argc, varargs); |
| | 5835 | } |
| | 5836 | |
| | 5837 | /* |
| | 5838 | * generate code for an object before a '.' |
| | 5839 | */ |
| | 5840 | vm_obj_id_t CTPNSymResolved::gen_code_obj_predot(int *is_self) |
| | 5841 | { |
| | 5842 | /* let the symbol handle it */ |
| | 5843 | return sym_->gen_code_obj_predot(is_self); |
| | 5844 | } |
| | 5845 | |
| | 5846 | /* ------------------------------------------------------------------------ */ |
| | 5847 | /* |
| | 5848 | * Debugger local variable symbol |
| | 5849 | */ |
| | 5850 | |
| | 5851 | /* |
| | 5852 | * generate code to evaluate the variable |
| | 5853 | */ |
| | 5854 | void CTPNSymDebugLocal::gen_code(int discard, int for_condition) |
| | 5855 | { |
| | 5856 | /* if we're not discarding the value, push the local */ |
| | 5857 | if (!discard) |
| | 5858 | { |
| | 5859 | /* generate the debugger local/parameter variable instruction */ |
| | 5860 | G_cg->write_op(is_param_ ? OPC_GETDBARG : OPC_GETDBLCL); |
| | 5861 | G_cs->write2(var_id_); |
| | 5862 | G_cs->write2(frame_idx_); |
| | 5863 | |
| | 5864 | /* note that we pushed the value */ |
| | 5865 | G_cg->note_push(); |
| | 5866 | |
| | 5867 | /* if it's a context local, get the value from the context array */ |
| | 5868 | if (ctx_arr_idx_ != 0) |
| | 5869 | { |
| | 5870 | CTPNConst::s_gen_code_int(ctx_arr_idx_); |
| | 5871 | G_cg->write_op(OPC_INDEX); |
| | 5872 | |
| | 5873 | /* |
| | 5874 | * the 'index' operation pops two values and pushes one, for a |
| | 5875 | * net of one pop |
| | 5876 | */ |
| | 5877 | G_cg->note_pop(); |
| | 5878 | } |
| | 5879 | } |
| | 5880 | } |
| | 5881 | |
| | 5882 | /* |
| | 5883 | * generate code for assigning to this variable |
| | 5884 | */ |
| | 5885 | int CTPNSymDebugLocal::gen_code_asi(int discard, tc_asitype_t typ, |
| | 5886 | CTcPrsNode *rhs, int ignore_error) |
| | 5887 | { |
| | 5888 | /* |
| | 5889 | * if this isn't a simple assignment, use the generic combination |
| | 5890 | * assignment computation |
| | 5891 | */ |
| | 5892 | if (typ != TC_ASI_SIMPLE) |
| | 5893 | return FALSE; |
| | 5894 | |
| | 5895 | /* generate the value to be assigned */ |
| | 5896 | if (rhs != 0) |
| | 5897 | rhs->gen_code(FALSE, FALSE); |
| | 5898 | |
| | 5899 | /* |
| | 5900 | * if we're not discarding the result, duplicate the value so we'll |
| | 5901 | * have a copy after the assignment |
| | 5902 | */ |
| | 5903 | if (!discard) |
| | 5904 | { |
| | 5905 | G_cg->write_op(OPC_DUP); |
| | 5906 | G_cg->note_push(); |
| | 5907 | } |
| | 5908 | |
| | 5909 | /* check for a context property */ |
| | 5910 | if (ctx_arr_idx_ == 0) |
| | 5911 | { |
| | 5912 | /* |
| | 5913 | * generate the debug-local-set instruction - the operands are |
| | 5914 | * the variable number and the stack frame index |
| | 5915 | */ |
| | 5916 | G_cg->write_op(is_param_ ? OPC_SETDBARG : OPC_SETDBLCL); |
| | 5917 | G_cs->write2(var_id_); |
| | 5918 | G_cs->write2(frame_idx_); |
| | 5919 | } |
| | 5920 | else |
| | 5921 | { |
| | 5922 | /* get the local containing our context object */ |
| | 5923 | G_cg->write_op(OPC_GETDBLCL); |
| | 5924 | G_cs->write2(var_id_); |
| | 5925 | G_cs->write2(frame_idx_); |
| | 5926 | |
| | 5927 | /* set the actual variable value in the context object */ |
| | 5928 | CTPNConst::s_gen_code_int(ctx_arr_idx_); |
| | 5929 | G_cg->write_op(OPC_SETIND); |
| | 5930 | G_cg->write_op(OPC_DISC); |
| | 5931 | |
| | 5932 | /* |
| | 5933 | * we did three pops (SETIND), then a push (SETIND), then a pop |
| | 5934 | * (DISC) - this is a net of three extra pops |
| | 5935 | */ |
| | 5936 | G_cg->note_pop(3); |
| | 5937 | } |
| | 5938 | |
| | 5939 | /* the debug-local-set removes the rvalue from the stack */ |
| | 5940 | G_cg->note_pop(); |
| | 5941 | |
| | 5942 | /* handled */ |
| | 5943 | return TRUE; |
| | 5944 | } |
| | 5945 | |
| | 5946 | /* ------------------------------------------------------------------------ */ |
| | 5947 | /* |
| | 5948 | * Double-quoted string. The 'discard' status is irrelevant, because we |
| | 5949 | * evaluate double-quoted strings for their side effects. |
| | 5950 | */ |
| | 5951 | void CTPNDstr::gen_code(int discard, int) |
| | 5952 | { |
| | 5953 | /* if we're not discarding the value, it's an error */ |
| | 5954 | if (!discard) |
| | 5955 | G_tok->log_error(TCERR_DQUOTE_IN_EXPR, (int)len_, str_); |
| | 5956 | |
| | 5957 | /* generate the instruction to display it */ |
| | 5958 | G_cg->write_op(OPC_SAY); |
| | 5959 | |
| | 5960 | /* add the string to the constant pool, creating a fixup here */ |
| | 5961 | G_cg->add_const_str(str_, len_, G_cs, G_cs->get_ofs()); |
| | 5962 | |
| | 5963 | /* write a placeholder value, which will be corrected by the fixup */ |
| | 5964 | G_cs->write4(0); |
| | 5965 | } |
| | 5966 | |
| | 5967 | /* ------------------------------------------------------------------------ */ |
| | 5968 | /* |
| | 5969 | * Double-quoted debug string |
| | 5970 | */ |
| | 5971 | void CTPNDebugDstr::gen_code(int, int) |
| | 5972 | { |
| | 5973 | /* generate code to push the in-line string */ |
| | 5974 | G_cg->write_op(OPC_PUSHSTRI); |
| | 5975 | G_cs->write2(len_); |
| | 5976 | G_cs->write(str_, len_); |
| | 5977 | |
| | 5978 | /* write code to display the value */ |
| | 5979 | G_cg->write_op(OPC_SAYVAL); |
| | 5980 | |
| | 5981 | /* note that we pushed the string and then popped it */ |
| | 5982 | G_cg->note_push(); |
| | 5983 | G_cg->note_pop(); |
| | 5984 | } |
| | 5985 | |
| | 5986 | /* ------------------------------------------------------------------------ */ |
| | 5987 | /* |
| | 5988 | * Double-quoted string embedding |
| | 5989 | */ |
| | 5990 | |
| | 5991 | /* |
| | 5992 | * create an embedding |
| | 5993 | */ |
| | 5994 | CTPNDstrEmbed::CTPNDstrEmbed(CTcPrsNode *sub) |
| | 5995 | : CTPNDstrEmbedBase(sub) |
| | 5996 | { |
| | 5997 | } |
| | 5998 | |
| | 5999 | /* |
| | 6000 | * Generate code for a double-quoted string embedding |
| | 6001 | */ |
| | 6002 | void CTPNDstrEmbed::gen_code(int, int) |
| | 6003 | { |
| | 6004 | int orig_depth; |
| | 6005 | |
| | 6006 | /* note the stack depth before generating the expression */ |
| | 6007 | orig_depth = G_cg->get_sp_depth(); |
| | 6008 | |
| | 6009 | /* |
| | 6010 | * Generate code for the embedded expression. If the expression has a |
| | 6011 | * return value, generate the value so that it can be displayed in the |
| | 6012 | * string; but don't request a value if it doesn't have one, as a |
| | 6013 | * return value is optional in this context. This is a normal value |
| | 6014 | * invocation, not a conditional, so we need any applicable normal |
| | 6015 | * value conversions. |
| | 6016 | */ |
| | 6017 | sub_->gen_code(!sub_->has_return_value(), FALSE); |
| | 6018 | |
| | 6019 | /* |
| | 6020 | * If the code generation left anything on the stack, generate code |
| | 6021 | * to display the value via the default display function. |
| | 6022 | */ |
| | 6023 | if (G_cg->get_sp_depth() > orig_depth) |
| | 6024 | { |
| | 6025 | /* add a SAYVAL instruction */ |
| | 6026 | G_cg->write_op(OPC_SAYVAL); |
| | 6027 | |
| | 6028 | /* SAYVAL pops the argument value */ |
| | 6029 | G_cg->note_pop(); |
| | 6030 | } |
| | 6031 | } |
| | 6032 | |
| | 6033 | |
| | 6034 | /* ------------------------------------------------------------------------ */ |
| | 6035 | /* |
| | 6036 | * Argument list |
| | 6037 | */ |
| | 6038 | void CTPNArglist::gen_code_arglist(int *varargs) |
| | 6039 | { |
| | 6040 | CTPNArg *arg; |
| | 6041 | int i; |
| | 6042 | int fixed_cnt; |
| | 6043 | int pushed_varargs_counter; |
| | 6044 | |
| | 6045 | /* |
| | 6046 | * scan the argument list for varargs - if we have any, we must |
| | 6047 | * treat all of them as varargs |
| | 6048 | */ |
| | 6049 | for (*varargs = FALSE, fixed_cnt = 0, arg = get_arg_list_head() ; |
| | 6050 | arg != 0 ; arg = arg->get_next_arg()) |
| | 6051 | { |
| | 6052 | /* if this is a varargs argument, we have varargs */ |
| | 6053 | if (arg->is_varargs()) |
| | 6054 | { |
| | 6055 | /* note it */ |
| | 6056 | *varargs = TRUE; |
| | 6057 | } |
| | 6058 | else |
| | 6059 | { |
| | 6060 | /* count another fixed argument */ |
| | 6061 | ++fixed_cnt; |
| | 6062 | } |
| | 6063 | } |
| | 6064 | |
| | 6065 | /* |
| | 6066 | * Push each argument in the list - start with the last element and |
| | 6067 | * work backwards through the list to the first element. The parser |
| | 6068 | * builds the list in reverse order, so we must merely follow the |
| | 6069 | * list from head to tail. |
| | 6070 | * |
| | 6071 | * We need each argument value to be pushed (hence discard = false), |
| | 6072 | * and we need the assignable value of each argument expression |
| | 6073 | * (hence for_condition = false). |
| | 6074 | */ |
| | 6075 | for (pushed_varargs_counter = FALSE, i = argc_, |
| | 6076 | arg = get_arg_list_head() ; arg != 0 ; |
| | 6077 | arg = arg->get_next_arg(), --i) |
| | 6078 | { |
| | 6079 | int depth; |
| | 6080 | |
| | 6081 | /* note the stack depth before generating the value */ |
| | 6082 | depth = G_cg->get_sp_depth(); |
| | 6083 | |
| | 6084 | /* |
| | 6085 | * check for varargs - if this is first varargs argument, push |
| | 6086 | * the counter placeholder |
| | 6087 | */ |
| | 6088 | if (arg->is_varargs() && !pushed_varargs_counter) |
| | 6089 | { |
| | 6090 | /* |
| | 6091 | * write code to push the fixed argument count - we can use |
| | 6092 | * this as a starting point, since we always know we have |
| | 6093 | * this many argument to start with; we'll dynamically add |
| | 6094 | * in the variable count at run-time |
| | 6095 | */ |
| | 6096 | CTPNConst::s_gen_code_int(fixed_cnt); |
| | 6097 | |
| | 6098 | /* note that we've pushed the counter */ |
| | 6099 | pushed_varargs_counter = TRUE; |
| | 6100 | |
| | 6101 | /* |
| | 6102 | * we will take the extra value off when we evaluate the |
| | 6103 | * varargs counter, so simply count it as removed now |
| | 6104 | */ |
| | 6105 | G_cg->note_pop(); |
| | 6106 | } |
| | 6107 | |
| | 6108 | /* generate the argument's code */ |
| | 6109 | arg->gen_code(FALSE, FALSE); |
| | 6110 | |
| | 6111 | /* |
| | 6112 | * if we've pushed the variable argument counter value onto the |
| | 6113 | * stack, and this a fixed argument, swap the top two stack |
| | 6114 | * elements to get the argument counter back to the top of the |
| | 6115 | * stack; if this is a varargs argument there's no need, since |
| | 6116 | * it will have taken care of this |
| | 6117 | */ |
| | 6118 | if (pushed_varargs_counter && !arg->is_varargs()) |
| | 6119 | G_cg->write_op(OPC_SWAP); |
| | 6120 | |
| | 6121 | /* ensure that it generated something */ |
| | 6122 | if (G_cg->get_sp_depth() <= depth) |
| | 6123 | G_tok->log_error(TCERR_ARG_EXPR_HAS_NO_VAL, i); |
| | 6124 | } |
| | 6125 | } |
| | 6126 | |
| | 6127 | /* ------------------------------------------------------------------------ */ |
| | 6128 | /* |
| | 6129 | * argument list entry |
| | 6130 | */ |
| | 6131 | void CTPNArg::gen_code(int, int) |
| | 6132 | { |
| | 6133 | /* |
| | 6134 | * Generate the argument expression. We need the value (hence |
| | 6135 | * discard = false), and we need the assignable value (hence |
| | 6136 | * for_condition = false). |
| | 6137 | */ |
| | 6138 | get_arg_expr()->gen_code(FALSE, FALSE); |
| | 6139 | |
| | 6140 | /* |
| | 6141 | * if this is a list-to-varargs conversion, generate the conversion |
| | 6142 | * instruction |
| | 6143 | */ |
| | 6144 | if (is_varargs_) |
| | 6145 | { |
| | 6146 | /* write the opcode */ |
| | 6147 | G_cg->write_op(OPC_MAKELSTPAR); |
| | 6148 | |
| | 6149 | /* note the extra push and pop for the argument count */ |
| | 6150 | G_cg->note_push(); |
| | 6151 | G_cg->note_pop(); |
| | 6152 | } |
| | 6153 | } |
| | 6154 | |
| | 6155 | /* ------------------------------------------------------------------------ */ |
| | 6156 | /* |
| | 6157 | * function/method call |
| | 6158 | */ |
| | 6159 | |
| | 6160 | /* |
| | 6161 | * create |
| | 6162 | */ |
| | 6163 | CTPNCall::CTPNCall(CTcPrsNode *func, class CTPNArglist *arglist) |
| | 6164 | : CTPNCallBase(func, arglist) |
| | 6165 | { |
| | 6166 | /* the T3 instruction set limits calls to 127 arguments */ |
| | 6167 | if (arglist->get_argc() > 127) |
| | 6168 | G_tok->log_error(TCERR_TOO_MANY_CALL_ARGS); |
| | 6169 | } |
| | 6170 | |
| | 6171 | |
| | 6172 | /* |
| | 6173 | * generate code |
| | 6174 | */ |
| | 6175 | void CTPNCall::gen_code(int discard, int) |
| | 6176 | { |
| | 6177 | int varargs; |
| | 6178 | |
| | 6179 | /* push the argument list */ |
| | 6180 | get_arg_list()->gen_code_arglist(&varargs); |
| | 6181 | |
| | 6182 | /* generate an appropriate call instruction */ |
| | 6183 | get_func()->gen_code_call(discard, get_arg_list()->get_argc(), |
| | 6184 | varargs); |
| | 6185 | } |
| | 6186 | |
| | 6187 | /* |
| | 6188 | * Generate code for operator 'new'. A 'new' with an argument list |
| | 6189 | * looks like a function call: NEW(CALL(object-contents, ARGLIST(...))). |
| | 6190 | */ |
| | 6191 | void CTPNCall::gen_code_new(int discard, int argc, int varargs, |
| | 6192 | int from_call, int is_transient) |
| | 6193 | { |
| | 6194 | /* |
| | 6195 | * if this is a recursive call from another 'call' node, it's not |
| | 6196 | * allowed - we'd be trying to use the result of a call as the base |
| | 6197 | * class of the 'new', which is illegal |
| | 6198 | */ |
| | 6199 | if (from_call) |
| | 6200 | { |
| | 6201 | G_tok->log_error(TCERR_INVAL_NEW_EXPR); |
| | 6202 | return; |
| | 6203 | } |
| | 6204 | |
| | 6205 | /* generate the argument list */ |
| | 6206 | get_arg_list()->gen_code_arglist(&varargs); |
| | 6207 | |
| | 6208 | /* generate the code for the 'new' call */ |
| | 6209 | get_func()->gen_code_new(discard, get_arg_list()->get_argc(), varargs, |
| | 6210 | TRUE, is_transient); |
| | 6211 | } |
| | 6212 | |
| | 6213 | |
| | 6214 | /* ------------------------------------------------------------------------ */ |
| | 6215 | /* |
| | 6216 | * member property evaluation |
| | 6217 | */ |
| | 6218 | void CTPNMember::gen_code(int discard, int) |
| | 6219 | { |
| | 6220 | /* ask the object expression to generate the code */ |
| | 6221 | get_obj_expr()->gen_code_member(discard, get_prop_expr(), prop_is_expr_, |
| | 6222 | 0, FALSE); |
| | 6223 | } |
| | 6224 | |
| | 6225 | /* |
| | 6226 | * assign to member expression |
| | 6227 | */ |
| | 6228 | int CTPNMember::gen_code_asi(int discard, tc_asitype_t typ, CTcPrsNode *rhs, |
| | 6229 | int ignore_errors) |
| | 6230 | { |
| | 6231 | int is_self; |
| | 6232 | vm_obj_id_t obj; |
| | 6233 | vm_prop_id_t prop; |
| | 6234 | |
| | 6235 | /* |
| | 6236 | * if it's not a simple assignment, tell the caller to generate the |
| | 6237 | * generic code to compute the composite value, and then call us |
| | 6238 | * again for a simple assignment |
| | 6239 | */ |
| | 6240 | if (typ != TC_ASI_SIMPLE) |
| | 6241 | return FALSE; |
| | 6242 | |
| | 6243 | /* generate the right-hand side, unless the caller has already done so */ |
| | 6244 | if (rhs != 0) |
| | 6245 | rhs->gen_code(FALSE, FALSE); |
| | 6246 | |
| | 6247 | /* |
| | 6248 | * if the caller wants to use the assigned value, push a copy -- |
| | 6249 | * we'll consume one copy in the SETPROP or related instruction, so |
| | 6250 | * we'll need another copy for the caller |
| | 6251 | */ |
| | 6252 | if (!discard) |
| | 6253 | { |
| | 6254 | G_cg->write_op(OPC_DUP); |
| | 6255 | G_cg->note_push(); |
| | 6256 | } |
| | 6257 | |
| | 6258 | /* |
| | 6259 | * Determine what we have on the left: we could have self, a |
| | 6260 | * constant object value, or any other expression. |
| | 6261 | */ |
| | 6262 | obj = get_obj_expr()->gen_code_obj_predot(&is_self); |
| | 6263 | |
| | 6264 | /* |
| | 6265 | * determine what kind of property expression we have - don't |
| | 6266 | * generate any code for now, since we may need to generate some |
| | 6267 | * more code ahead of the property generation |
| | 6268 | */ |
| | 6269 | prop = get_prop_expr()->gen_code_propid(TRUE, prop_is_expr_); |
| | 6270 | |
| | 6271 | /* determine what we need to do based on the operands */ |
| | 6272 | if (prop == VM_INVALID_PROP) |
| | 6273 | { |
| | 6274 | /* |
| | 6275 | * We're assigning through a property pointer -- we must |
| | 6276 | * generate a PTRSETPROP instruction. |
| | 6277 | * |
| | 6278 | * Before we generate the property expression, we must generate |
| | 6279 | * the object expression. If we got a constant object, we must |
| | 6280 | * generate code to push that object value; otherwise, the code |
| | 6281 | * to generate the object value is already generated. |
| | 6282 | */ |
| | 6283 | if (is_self) |
| | 6284 | { |
| | 6285 | /* self - generate code to push the "self" value */ |
| | 6286 | G_cg->write_op(OPC_PUSHSELF); |
| | 6287 | G_cg->note_push(); |
| | 6288 | } |
| | 6289 | else if (obj != VM_INVALID_OBJ) |
| | 6290 | { |
| | 6291 | /* constant object - generate code to push the value */ |
| | 6292 | G_cg->write_op(OPC_PUSHOBJ); |
| | 6293 | G_cs->write_obj_id(obj); |
| | 6294 | G_cg->note_push(); |
| | 6295 | } |
| | 6296 | |
| | 6297 | /* generate the property value expression */ |
| | 6298 | get_prop_expr()->gen_code_propid(FALSE, prop_is_expr_); |
| | 6299 | |
| | 6300 | /* generate the PTRSETPROP instruction */ |
| | 6301 | G_cg->write_op(OPC_PTRSETPROP); |
| | 6302 | |
| | 6303 | /* ptrsetprop removes three elements */ |
| | 6304 | G_cg->note_pop(3); |
| | 6305 | } |
| | 6306 | else |
| | 6307 | { |
| | 6308 | /* |
| | 6309 | * We have a constant property value, so we have several |
| | 6310 | * instructions to choose from. If we're assigning to a |
| | 6311 | * property of "self", use SETPROPSELF. If we're assigning to a |
| | 6312 | * constant object, use OBJSETPROP. Otherwise, use the plain |
| | 6313 | * SETPROP. |
| | 6314 | */ |
| | 6315 | if (is_self) |
| | 6316 | { |
| | 6317 | /* write the SETPROPSELF */ |
| | 6318 | G_cg->write_op(OPC_SETPROPSELF); |
| | 6319 | G_cs->write_prop_id(prop); |
| | 6320 | |
| | 6321 | /* setpropself removes the value */ |
| | 6322 | G_cg->note_pop(); |
| | 6323 | } |
| | 6324 | else if (obj != VM_INVALID_OBJ) |
| | 6325 | { |
| | 6326 | /* write the OBJSETPROP */ |
| | 6327 | G_cg->write_op(OPC_OBJSETPROP); |
| | 6328 | G_cs->write_obj_id(obj); |
| | 6329 | G_cs->write_prop_id(prop); |
| | 6330 | |
| | 6331 | /* objsetprop removes the value */ |
| | 6332 | G_cg->note_pop(); |
| | 6333 | } |
| | 6334 | else |
| | 6335 | { |
| | 6336 | /* |
| | 6337 | * write the normal SETPROP; we already generated the code |
| | 6338 | * to push the object value, so it's where it should be |
| | 6339 | */ |
| | 6340 | G_cg->write_op(OPC_SETPROP); |
| | 6341 | G_cs->write_prop_id(prop); |
| | 6342 | |
| | 6343 | /* setprop removes the value and the object */ |
| | 6344 | G_cg->note_pop(2); |
| | 6345 | } |
| | 6346 | } |
| | 6347 | |
| | 6348 | /* handled */ |
| | 6349 | return TRUE; |
| | 6350 | } |
| | 6351 | |
| | 6352 | /* ------------------------------------------------------------------------ */ |
| | 6353 | /* |
| | 6354 | * member with argument list |
| | 6355 | */ |
| | 6356 | void CTPNMemArg::gen_code(int discard, int) |
| | 6357 | { |
| | 6358 | int varargs; |
| | 6359 | |
| | 6360 | /* push the argument list */ |
| | 6361 | get_arg_list()->gen_code_arglist(&varargs); |
| | 6362 | |
| | 6363 | /* ask the object expression to generate the code */ |
| | 6364 | get_obj_expr()->gen_code_member(discard, get_prop_expr(), prop_is_expr_, |
| | 6365 | get_arg_list()->get_argc(), |
| | 6366 | varargs); |
| | 6367 | } |
| | 6368 | |
| | 6369 | /* ------------------------------------------------------------------------ */ |
| | 6370 | /* |
| | 6371 | * construct a list |
| | 6372 | */ |
| | 6373 | void CTPNList::gen_code(int discard, int for_condition) |
| | 6374 | { |
| | 6375 | CTPNListEle *ele; |
| | 6376 | |
| | 6377 | /* |
| | 6378 | * Before we construct the list dynamically, check to see if the |
| | 6379 | * list is constant. If it is, we need only built the list in the |
| | 6380 | * constant pool, and push its offset. |
| | 6381 | */ |
| | 6382 | if (is_const()) |
| | 6383 | { |
| | 6384 | /* push the value only if we're not discarding it */ |
| | 6385 | if (!discard) |
| | 6386 | { |
| | 6387 | /* write the instruction */ |
| | 6388 | G_cg->write_op(OPC_PUSHLST); |
| | 6389 | |
| | 6390 | /* add the list to the constant pool */ |
| | 6391 | G_cg->add_const_list(this, G_cs, G_cs->get_ofs()); |
| | 6392 | |
| | 6393 | /* |
| | 6394 | * write a placeholder address, which will be corrected by |
| | 6395 | * the fixup that add_const_list() created |
| | 6396 | */ |
| | 6397 | G_cs->write4(0); |
| | 6398 | |
| | 6399 | /* note the push */ |
| | 6400 | G_cg->note_push(); |
| | 6401 | } |
| | 6402 | |
| | 6403 | /* done */ |
| | 6404 | return; |
| | 6405 | } |
| | 6406 | |
| | 6407 | /* |
| | 6408 | * It's not a constant list, so we must generate code to construct a |
| | 6409 | * list dynamically. Push each element of the list. We need each |
| | 6410 | * value (hence discard = false), and we require the assignable |
| | 6411 | * value of each expression (hence for_condition = false). Push the |
| | 6412 | * argument list in reverse order, since the run-time metaclass |
| | 6413 | * requires this ordering. |
| | 6414 | */ |
| | 6415 | for (ele = get_tail() ; ele != 0 ; ele = ele->get_prev()) |
| | 6416 | ele->gen_code(FALSE, FALSE); |
| | 6417 | |
| | 6418 | /* generate a NEW instruction for an object of metaclass LIST */ |
| | 6419 | if (get_count() <= 255) |
| | 6420 | { |
| | 6421 | /* the count will fit in one byte - use the short form */ |
| | 6422 | G_cg->write_op(OPC_NEW1); |
| | 6423 | G_cs->write((char)get_count()); |
| | 6424 | G_cs->write((char)G_cg->get_predef_meta_idx(TCT3_METAID_LIST)); |
| | 6425 | } |
| | 6426 | else |
| | 6427 | { |
| | 6428 | /* count doesn't fit in one byte - use the long form */ |
| | 6429 | G_cg->write_op(OPC_NEW2); |
| | 6430 | G_cs->write2(get_count()); |
| | 6431 | G_cs->write2(G_cg->get_predef_meta_idx(TCT3_METAID_LIST)); |
| | 6432 | } |
| | 6433 | |
| | 6434 | /* new1/new2 remove arguments */ |
| | 6435 | G_cg->note_pop(get_count()); |
| | 6436 | |
| | 6437 | /* if we're not discarding the value, push it */ |
| | 6438 | if (!discard) |
| | 6439 | { |
| | 6440 | G_cg->write_op(OPC_GETR0); |
| | 6441 | G_cg->note_push(); |
| | 6442 | } |
| | 6443 | } |
| | 6444 | |
| | 6445 | /* ------------------------------------------------------------------------ */ |
| | 6446 | /* |
| | 6447 | * list element |
| | 6448 | */ |
| | 6449 | void CTPNListEle::gen_code(int discard, int for_condition) |
| | 6450 | { |
| | 6451 | /* generate the subexpression */ |
| | 6452 | expr_->gen_code(discard, for_condition); |
| | 6453 | } |
| | 6454 | |
| | 6455 | /* ------------------------------------------------------------------------ */ |
| | 6456 | /* |
| | 6457 | * Basic T3-specific symbol class |
| | 6458 | */ |
| | 6459 | |
| | 6460 | /* |
| | 6461 | * generate code to take the address of a symbol - in general, we cannot |
| | 6462 | * take the address of a symbol, so we'll just log an error |
| | 6463 | */ |
| | 6464 | void CTcSymbol::gen_code_addr() |
| | 6465 | { |
| | 6466 | G_tok->log_error(TCERR_NO_ADDR_SYM, (int)get_sym_len(), get_sym()); |
| | 6467 | } |
| | 6468 | |
| | 6469 | /* |
| | 6470 | * generate code to assign to the symbol - in general, we cannot assign |
| | 6471 | * to a symbol, so we'll just log an error |
| | 6472 | */ |
| | 6473 | int CTcSymbol::gen_code_asi(int, tc_asitype_t, class CTcPrsNode *, |
| | 6474 | int ignore_error) |
| | 6475 | { |
| | 6476 | /* |
| | 6477 | * if we're ignoring errors, simply return false to indicate that |
| | 6478 | * nothing happened |
| | 6479 | */ |
| | 6480 | if (ignore_error) |
| | 6481 | return FALSE; |
| | 6482 | |
| | 6483 | /* log the error */ |
| | 6484 | G_tok->log_error(TCERR_CANNOT_ASSIGN_SYM, (int)get_sym_len(), get_sym()); |
| | 6485 | |
| | 6486 | /* |
| | 6487 | * even though we didn't generate anything, this has been fully |
| | 6488 | * handled - the caller shouldn't attempt to generate any additional |
| | 6489 | * code for this |
| | 6490 | */ |
| | 6491 | return TRUE; |
| | 6492 | } |
| | 6493 | |
| | 6494 | /* |
| | 6495 | * Generate code for calling the symbol. By default, we can't call a |
| | 6496 | * symbol. |
| | 6497 | */ |
| | 6498 | void CTcSymbol::gen_code_call(int, int, int) |
| | 6499 | { |
| | 6500 | /* log an error */ |
| | 6501 | G_tok->log_error(TCERR_CANNOT_CALL_SYM, (int)get_sym_len(), get_sym()); |
| | 6502 | } |
| | 6503 | |
| | 6504 | /* |
| | 6505 | * Generate code for operator 'new' |
| | 6506 | */ |
| | 6507 | void CTcSymbol::gen_code_new(int, int, int, int) |
| | 6508 | { |
| | 6509 | G_tok->log_error(TCERR_INVAL_NEW_EXPR); |
| | 6510 | } |
| | 6511 | |
| | 6512 | /* |
| | 6513 | * evaluate a property ID |
| | 6514 | */ |
| | 6515 | vm_prop_id_t CTcSymbol::gen_code_propid(int check_only, int is_expr) |
| | 6516 | { |
| | 6517 | /* by default, a symbol cannot be used as a property ID */ |
| | 6518 | if (!check_only) |
| | 6519 | G_tok->log_error(TCERR_SYM_NOT_PROP, (int)get_sym_len(), get_sym()); |
| | 6520 | |
| | 6521 | /* we can't return a valid property ID */ |
| | 6522 | return VM_INVALID_PROP; |
| | 6523 | } |
| | 6524 | |
| | 6525 | /* |
| | 6526 | * evaluate a member expression |
| | 6527 | */ |
| | 6528 | void CTcSymbol::gen_code_member(int discard, |
| | 6529 | CTcPrsNode *prop_expr, int prop_is_expr, |
| | 6530 | int argc, int varargs) |
| | 6531 | { |
| | 6532 | /* by default, a symbol cannot be used as an object expression */ |
| | 6533 | G_tok->log_error(TCERR_SYM_NOT_OBJ, (int)get_sym_len(), get_sym()); |
| | 6534 | } |
| | 6535 | |
| | 6536 | /* |
| | 6537 | * generate code for an object expression before a '.' |
| | 6538 | */ |
| | 6539 | vm_obj_id_t CTcSymbol::gen_code_obj_predot(int *is_self) |
| | 6540 | { |
| | 6541 | /* by default, a symbol cannot be used as an object expression */ |
| | 6542 | G_tok->log_error(TCERR_SYM_NOT_OBJ, (int)get_sym_len(), get_sym()); |
| | 6543 | |
| | 6544 | /* indicate that we don't have a constant object */ |
| | 6545 | *is_self = FALSE; |
| | 6546 | return VM_INVALID_OBJ; |
| | 6547 | } |
| | 6548 | |
| | 6549 | |
| | 6550 | |
| | 6551 | /* ------------------------------------------------------------------------ */ |
| | 6552 | /* |
| | 6553 | * T3-specific function symbol class |
| | 6554 | */ |
| | 6555 | |
| | 6556 | /* |
| | 6557 | * evaluate the symbol |
| | 6558 | */ |
| | 6559 | void CTcSymFunc::gen_code(int discard) |
| | 6560 | { |
| | 6561 | /* |
| | 6562 | * function address are always unknown during code generation; |
| | 6563 | * generate a placeholder instruction and add a fixup record for it |
| | 6564 | */ |
| | 6565 | G_cg->write_op(OPC_PUSHFNPTR); |
| | 6566 | |
| | 6567 | /* add a fixup for the current code location */ |
| | 6568 | add_abs_fixup(G_cs); |
| | 6569 | |
| | 6570 | /* write a placeholder offset - arbitrarily use zero */ |
| | 6571 | G_cs->write4(0); |
| | 6572 | |
| | 6573 | /* note the push */ |
| | 6574 | G_cg->note_push(); |
| | 6575 | } |
| | 6576 | |
| | 6577 | /* |
| | 6578 | * take the address of the function |
| | 6579 | */ |
| | 6580 | void CTcSymFunc::gen_code_addr() |
| | 6581 | { |
| | 6582 | /* |
| | 6583 | * the address of a function cannot be taken - using the name alone |
| | 6584 | * yields the address |
| | 6585 | */ |
| | 6586 | G_tok->log_error(TCERR_INVAL_FUNC_ADDR, (int)get_sym_len(), get_sym()); |
| | 6587 | } |
| | 6588 | |
| | 6589 | |
| | 6590 | /* |
| | 6591 | * call the symbol |
| | 6592 | */ |
| | 6593 | void CTcSymFunc::gen_code_call(int discard, int argc, int varargs) |
| | 6594 | { |
| | 6595 | /* |
| | 6596 | * If this is a multi-method base function, a call to the function is |
| | 6597 | * actually a call to _multiMethodCall('name', args). |
| | 6598 | */ |
| | 6599 | if (is_multimethod_base_) |
| | 6600 | { |
| | 6601 | /* make a list out of the arguments */ |
| | 6602 | if (varargs) |
| | 6603 | { |
| | 6604 | G_cg->write_op(OPC_VARARGC); |
| | 6605 | G_cs->write((char)argc); |
| | 6606 | } |
| | 6607 | else if (argc <= 255) |
| | 6608 | { |
| | 6609 | G_cg->write_op(OPC_NEW1); |
| | 6610 | G_cs->write((char)argc); |
| | 6611 | } |
| | 6612 | else |
| | 6613 | { |
| | 6614 | G_cg->write_op(OPC_NEW2); |
| | 6615 | G_cs->write2(argc); |
| | 6616 | } |
| | 6617 | G_cs->write((char)G_cg->get_predef_meta_idx(TCT3_METAID_LIST)); |
| | 6618 | G_cg->note_pop(argc); |
| | 6619 | |
| | 6620 | G_cg->write_op(OPC_GETR0); |
| | 6621 | G_cg->note_push(); |
| | 6622 | |
| | 6623 | /* add the base function pointer argument */ |
| | 6624 | CTcConstVal funcval; |
| | 6625 | funcval.set_funcptr(this); |
| | 6626 | CTPNConst func(&funcval); |
| | 6627 | func.gen_code(FALSE, FALSE); |
| | 6628 | |
| | 6629 | /* look up _multiMethodCall */ |
| | 6630 | static const char mmc_name[] = "_multiMethodCall"; |
| | 6631 | static const size_t mmc_len = sizeof(mmc_name) - 1; |
| | 6632 | CTcSymFunc *mmc = (CTcSymFunc *)G_prs->get_global_symtab()->find( |
| | 6633 | mmc_name, mmc_len); |
| | 6634 | if (mmc == 0) |
| | 6635 | { |
| | 6636 | /* it's not defined - add an implied declaration for it */ |
| | 6637 | mmc = new CTcSymFunc(mmc_name, mmc_len, FALSE, 1, TRUE, TRUE, |
| | 6638 | FALSE, FALSE, TRUE); |
| | 6639 | G_prs->get_global_symtab()->add_entry(mmc); |
| | 6640 | } |
| | 6641 | else if(mmc->get_type() != TC_SYM_FUNC) |
| | 6642 | { |
| | 6643 | /* it's defined, but not as a function - this is an error */ |
| | 6644 | G_tok->log_error(TCERR_REDEF_AS_FUNC, (int)mmc_len, mmc_name); |
| | 6645 | return; |
| | 6646 | } |
| | 6647 | |
| | 6648 | /* |
| | 6649 | * Generate the call. Note that there are always two arguments at |
| | 6650 | * this point: the base function pointer, and the argument list. |
| | 6651 | * The argument list is just one argument because we've already |
| | 6652 | * constructed a list out of it. |
| | 6653 | */ |
| | 6654 | mmc->gen_code_call(discard, 2, FALSE); |
| | 6655 | } |
| | 6656 | else |
| | 6657 | { |
| | 6658 | /* write the varargs modifier if appropriate */ |
| | 6659 | if (varargs) |
| | 6660 | G_cg->write_op(OPC_VARARGC); |
| | 6661 | |
| | 6662 | /* generate the call instruction and argument count */ |
| | 6663 | G_cg->write_op(OPC_CALL); |
| | 6664 | G_cs->write((char)argc); |
| | 6665 | |
| | 6666 | /* check the mode */ |
| | 6667 | if (G_cg->is_eval_for_debug()) |
| | 6668 | { |
| | 6669 | /* |
| | 6670 | * debugger expression compilation - we know the absolute |
| | 6671 | * address already, since all symbols are pre-resolved in the |
| | 6672 | * debugger |
| | 6673 | */ |
| | 6674 | G_cs->write4(get_code_pool_addr()); |
| | 6675 | } |
| | 6676 | else |
| | 6677 | { |
| | 6678 | /* |
| | 6679 | * Normal compilation - we won't know the function's address |
| | 6680 | * until after generation is completed, so add a fixup for the |
| | 6681 | * current location, then write a placeholder for the offset |
| | 6682 | * field. |
| | 6683 | */ |
| | 6684 | add_abs_fixup(G_cs); |
| | 6685 | G_cs->write4(0); |
| | 6686 | } |
| | 6687 | |
| | 6688 | /* call removes arguments */ |
| | 6689 | G_cg->note_pop(argc); |
| | 6690 | |
| | 6691 | /* make sure the argument count is correct */ |
| | 6692 | if (varargs_ ? argc < argc_ : argc != argc_) |
| | 6693 | G_tok->log_error(TCERR_WRONG_ARGC_FOR_FUNC, |
| | 6694 | (int)get_sym_len(), get_sym(), argc_, argc); |
| | 6695 | |
| | 6696 | /* if we're not discarding, push the return value from R0 */ |
| | 6697 | if (!discard) |
| | 6698 | { |
| | 6699 | G_cg->write_op(OPC_GETR0); |
| | 6700 | G_cg->note_push(); |
| | 6701 | } |
| | 6702 | } |
| | 6703 | } |
| | 6704 | |
| | 6705 | /* |
| | 6706 | * Get my code pool address. Valid only after linking. |
| | 6707 | */ |
| | 6708 | ulong CTcSymFunc::get_code_pool_addr() const |
| | 6709 | { |
| | 6710 | /* check for an absolute address */ |
| | 6711 | if (abs_addr_valid_) |
| | 6712 | { |
| | 6713 | /* |
| | 6714 | * we have an absolute address - this means the symbol was |
| | 6715 | * loaded from a fully-linked image file (specifically, from the |
| | 6716 | * debug records) |
| | 6717 | */ |
| | 6718 | return abs_addr_; |
| | 6719 | } |
| | 6720 | else |
| | 6721 | { |
| | 6722 | /* |
| | 6723 | * we don't have an absolute address, so our address must have |
| | 6724 | * been determined through a linking step - get the final |
| | 6725 | * address from the anchor |
| | 6726 | */ |
| | 6727 | return anchor_->get_addr(); |
| | 6728 | } |
| | 6729 | } |
| | 6730 | |
| | 6731 | /* |
| | 6732 | * add a runtime symbol table entry |
| | 6733 | */ |
| | 6734 | void CTcSymFunc::add_runtime_symbol(CVmRuntimeSymbols *symtab) |
| | 6735 | { |
| | 6736 | vm_val_t val; |
| | 6737 | |
| | 6738 | /* add an entry for our absolute address */ |
| | 6739 | val.set_fnptr(get_code_pool_addr()); |
| | 6740 | symtab->add_sym(get_sym(), get_sym_len(), &val); |
| | 6741 | } |
| | 6742 | |
| | 6743 | |
| | 6744 | /* ------------------------------------------------------------------------ */ |
| | 6745 | /* |
| | 6746 | * T3-specific object symbol class |
| | 6747 | */ |
| | 6748 | |
| | 6749 | /* |
| | 6750 | * evaluate the symbol |
| | 6751 | */ |
| | 6752 | void CTcSymObj::gen_code(int discard) |
| | 6753 | { |
| | 6754 | /* write code to push the object ID */ |
| | 6755 | if (!discard) |
| | 6756 | { |
| | 6757 | /* push the object */ |
| | 6758 | G_cg->write_op(OPC_PUSHOBJ); |
| | 6759 | G_cs->write_obj_id(obj_id_); |
| | 6760 | |
| | 6761 | /* note the push */ |
| | 6762 | G_cg->note_push(); |
| | 6763 | } |
| | 6764 | } |
| | 6765 | |
| | 6766 | /* |
| | 6767 | * take the address of the object |
| | 6768 | */ |
| | 6769 | void CTcSymObj::gen_code_addr() |
| | 6770 | { |
| | 6771 | /* act as though we were pushing the object ID directly */ |
| | 6772 | gen_code(FALSE); |
| | 6773 | } |
| | 6774 | |
| | 6775 | /* |
| | 6776 | * Generate a 'new' expression |
| | 6777 | */ |
| | 6778 | void CTcSymObj::gen_code_new(int discard, int argc, int varargs, |
| | 6779 | int is_transient) |
| | 6780 | { |
| | 6781 | /* use our static generator */ |
| | 6782 | s_gen_code_new(discard, obj_id_, metaclass_, argc, varargs, is_transient); |
| | 6783 | } |
| | 6784 | |
| | 6785 | /* |
| | 6786 | * Generate a 'new' expression. (This is a static method so that this |
| | 6787 | * code can be used by all of the possible expression types to which |
| | 6788 | * 'new' can be applied.) |
| | 6789 | * |
| | 6790 | * This type of generation applies only to objects of metaclass TADS |
| | 6791 | * Object. |
| | 6792 | */ |
| | 6793 | void CTcSymObj::s_gen_code_new(int discard, vm_obj_id_t obj_id, |
| | 6794 | tc_metaclass_t meta, |
| | 6795 | int argc, int varargs, int is_transient) |
| | 6796 | { |
| | 6797 | /* |
| | 6798 | * push the base class object - this is always the first argument |
| | 6799 | * (hence last pushed) to the metaclass constructor |
| | 6800 | */ |
| | 6801 | G_cg->write_op(OPC_PUSHOBJ); |
| | 6802 | G_cs->write_obj_id(obj_id); |
| | 6803 | |
| | 6804 | /* note the push */ |
| | 6805 | G_cg->note_push(); |
| | 6806 | |
| | 6807 | /* |
| | 6808 | * note that we can only allow 126 arguments to a constructor, |
| | 6809 | * because we must add the implicit superclass argument |
| | 6810 | */ |
| | 6811 | if (argc > 126) |
| | 6812 | G_tok->log_error(TCERR_TOO_MANY_CTOR_ARGS); |
| | 6813 | |
| | 6814 | /* |
| | 6815 | * if we have varargs, swap the top stack elements to get the |
| | 6816 | * argument count back on top, and then generate the varargs |
| | 6817 | * modifier opcode |
| | 6818 | */ |
| | 6819 | if (varargs) |
| | 6820 | { |
| | 6821 | /* swap the top stack elements to get argc back to the top */ |
| | 6822 | G_cg->write_op(OPC_SWAP); |
| | 6823 | |
| | 6824 | /* |
| | 6825 | * increment the argument count to account for the superclass |
| | 6826 | * object argument |
| | 6827 | */ |
| | 6828 | G_cg->write_op(OPC_INC); |
| | 6829 | |
| | 6830 | /* write the varargs modifier opcode */ |
| | 6831 | G_cg->write_op(OPC_VARARGC); |
| | 6832 | } |
| | 6833 | |
| | 6834 | /* figure the metaclass index - the compiler can only generate known |
| | 6835 | |
| | 6836 | /* |
| | 6837 | * write the NEW instruction - since we always add TADS Object to |
| | 6838 | * our metaclass table before we start compiling any code, we know |
| | 6839 | * it always has a small metaclass number and will always fit in the |
| | 6840 | * short form of the instruction |
| | 6841 | * |
| | 6842 | * Note that the actual argument count we generate is one higher |
| | 6843 | * than the source code argument list, because we add the implicit |
| | 6844 | * first argument to the metaclass constructor |
| | 6845 | */ |
| | 6846 | G_cg->write_op(is_transient ? OPC_TRNEW1 : OPC_NEW1); |
| | 6847 | G_cs->write((char)(argc + 1)); |
| | 6848 | |
| | 6849 | /* write out the dependency table index for the metaclass */ |
| | 6850 | switch (meta) |
| | 6851 | { |
| | 6852 | case TC_META_TADSOBJ: |
| | 6853 | G_cs->write((char)G_cg->get_predef_meta_idx(TCT3_METAID_TADSOBJ)); |
| | 6854 | break; |
| | 6855 | |
| | 6856 | default: |
| | 6857 | /* we can't use 'new' on symbols of other metaclasses */ |
| | 6858 | G_tok->log_error(TCERR_BAD_META_FOR_NEW); |
| | 6859 | G_cs->write(0); |
| | 6860 | break; |
| | 6861 | } |
| | 6862 | |
| | 6863 | /* new1 removes the arguments */ |
| | 6864 | G_cg->note_pop(argc + 1); |
| | 6865 | |
| | 6866 | /* |
| | 6867 | * if they're not discarding the value, push the new object |
| | 6868 | * reference, which will be in R0 when the constructor returns |
| | 6869 | */ |
| | 6870 | if (!discard) |
| | 6871 | { |
| | 6872 | G_cg->write_op(OPC_GETR0); |
| | 6873 | G_cg->note_push(); |
| | 6874 | } |
| | 6875 | } |
| | 6876 | |
| | 6877 | /* |
| | 6878 | * Generate code for a member expression |
| | 6879 | */ |
| | 6880 | void CTcSymObj::gen_code_member(int discard, |
| | 6881 | CTcPrsNode *prop_expr, int prop_is_expr, |
| | 6882 | int argc, int varargs) |
| | 6883 | { |
| | 6884 | s_gen_code_member(discard, prop_expr, prop_is_expr, |
| | 6885 | argc, obj_id_, varargs); |
| | 6886 | } |
| | 6887 | |
| | 6888 | /* |
| | 6889 | * Static method to generate code for a member expression. This is |
| | 6890 | * static so that constant object nodes can share it. |
| | 6891 | */ |
| | 6892 | void CTcSymObj::s_gen_code_member(int discard, |
| | 6893 | CTcPrsNode *prop_expr, int prop_is_expr, |
| | 6894 | int argc, vm_obj_id_t obj_id, int varargs) |
| | 6895 | { |
| | 6896 | vm_prop_id_t prop; |
| | 6897 | |
| | 6898 | /* |
| | 6899 | * generate the property expression - don't generate the code right |
| | 6900 | * now even if code generation is necessary, because this isn't the |
| | 6901 | * right place for it; for now, simply check to determine if we're |
| | 6902 | * going to need to generate any code for the property expression |
| | 6903 | */ |
| | 6904 | prop = prop_expr->gen_code_propid(TRUE, prop_is_expr); |
| | 6905 | |
| | 6906 | /* don't allow method calls with arguments in speculative mode */ |
| | 6907 | if (argc != 0 && G_cg->is_speculative()) |
| | 6908 | err_throw(VMERR_BAD_SPEC_EVAL); |
| | 6909 | |
| | 6910 | /* check for a constant property value */ |
| | 6911 | if (prop != VM_INVALID_PROP) |
| | 6912 | { |
| | 6913 | /* generate an OBJGETPROP or OBJCALLPROP as appropriate */ |
| | 6914 | if (G_cg->is_speculative()) |
| | 6915 | { |
| | 6916 | /* speculative evaluation - use GETPROPDATA */ |
| | 6917 | G_cg->write_op(OPC_PUSHOBJ); |
| | 6918 | G_cs->write_obj_id(obj_id); |
| | 6919 | G_cg->write_op(OPC_GETPROPDATA); |
| | 6920 | G_cs->write_prop_id(prop); |
| | 6921 | |
| | 6922 | /* we pushed the object, then popped it */ |
| | 6923 | G_cg->note_push(); |
| | 6924 | G_cg->note_pop(); |
| | 6925 | } |
| | 6926 | else if (argc == 0) |
| | 6927 | { |
| | 6928 | /* no arguments - use OBJGETPROP */ |
| | 6929 | G_cg->write_op(OPC_OBJGETPROP); |
| | 6930 | G_cs->write_obj_id(obj_id); |
| | 6931 | G_cs->write_prop_id(prop); |
| | 6932 | } |
| | 6933 | else |
| | 6934 | { |
| | 6935 | /* generate a varargs modifier if needed */ |
| | 6936 | if (varargs) |
| | 6937 | G_cg->write_op(OPC_VARARGC); |
| | 6938 | |
| | 6939 | /* arguments - use OBJCALLPROP */ |
| | 6940 | G_cg->write_op(OPC_OBJCALLPROP); |
| | 6941 | G_cs->write((char)argc); |
| | 6942 | G_cs->write_obj_id(obj_id); |
| | 6943 | G_cs->write_prop_id(prop); |
| | 6944 | |
| | 6945 | /* objcallprop removes arguments */ |
| | 6946 | G_cg->note_pop(argc); |
| | 6947 | } |
| | 6948 | } |
| | 6949 | else |
| | 6950 | { |
| | 6951 | /* |
| | 6952 | * non-constant property value - we must first push the object |
| | 6953 | * value, then push the property value, then write a PTRCALLPROP |
| | 6954 | * instruction |
| | 6955 | */ |
| | 6956 | |
| | 6957 | /* generate the object push */ |
| | 6958 | G_cg->write_op(OPC_PUSHOBJ); |
| | 6959 | G_cs->write_obj_id(obj_id); |
| | 6960 | |
| | 6961 | /* note the pushes */ |
| | 6962 | G_cg->note_push(); |
| | 6963 | |
| | 6964 | /* keep the argument counter on top if necessary */ |
| | 6965 | if (varargs) |
| | 6966 | G_cg->write_op(OPC_SWAP); |
| | 6967 | |
| | 6968 | /* generate the property push */ |
| | 6969 | prop_expr->gen_code_propid(FALSE, prop_is_expr); |
| | 6970 | |
| | 6971 | /* generate the PTRCALLPROP or PTRGETPROPDATA */ |
| | 6972 | if (G_cg->is_speculative()) |
| | 6973 | { |
| | 6974 | /* speculative - use the data-only property evaluation */ |
| | 6975 | G_cg->write_op(OPC_PTRGETPROPDATA); |
| | 6976 | } |
| | 6977 | else |
| | 6978 | { |
| | 6979 | /* |
| | 6980 | * if we have a varargs list, modify the call instruction |
| | 6981 | * that follows to make it a varargs call |
| | 6982 | */ |
| | 6983 | if (varargs) |
| | 6984 | { |
| | 6985 | /* swap to get the arg counter back on top */ |
| | 6986 | G_cg->write_op(OPC_SWAP); |
| | 6987 | |
| | 6988 | /* write the varargs modifier */ |
| | 6989 | G_cg->write_op(OPC_VARARGC); |
| | 6990 | } |
| | 6991 | |
| | 6992 | /* normal - call the property */ |
| | 6993 | G_cg->write_op(OPC_PTRCALLPROP); |
| | 6994 | G_cs->write((int)argc); |
| | 6995 | } |
| | 6996 | |
| | 6997 | /* ptrcallprop removes the arguments, the object, and the property */ |
| | 6998 | G_cg->note_pop(argc + 2); |
| | 6999 | } |
| | 7000 | |
| | 7001 | /* if they want the result, push it onto the stack */ |
| | 7002 | if (!discard) |
| | 7003 | { |
| | 7004 | G_cg->write_op(OPC_GETR0); |
| | 7005 | G_cg->note_push(); |
| | 7006 | } |
| | 7007 | } |
| | 7008 | |
| | 7009 | /* |
| | 7010 | * generate code for an object before a '.' |
| | 7011 | */ |
| | 7012 | vm_obj_id_t CTcSymObj::gen_code_obj_predot(int *is_self) |
| | 7013 | { |
| | 7014 | /* return our constant object reference */ |
| | 7015 | *is_self = FALSE; |
| | 7016 | return obj_id_; |
| | 7017 | } |
| | 7018 | |
| | 7019 | /* |
| | 7020 | * add a runtime symbol table entry |
| | 7021 | */ |
| | 7022 | void CTcSymObj::add_runtime_symbol(CVmRuntimeSymbols *symtab) |
| | 7023 | { |
| | 7024 | vm_val_t val; |
| | 7025 | |
| | 7026 | /* add our entry */ |
| | 7027 | val.set_obj(obj_id_); |
| | 7028 | symtab->add_sym(get_sym(), get_sym_len(), &val); |
| | 7029 | } |
| | 7030 | |
| | 7031 | /* ------------------------------------------------------------------------ */ |
| | 7032 | /* |
| | 7033 | * T3-specific property symbol class |
| | 7034 | */ |
| | 7035 | |
| | 7036 | /* |
| | 7037 | * evaluate the symbol |
| | 7038 | */ |
| | 7039 | void CTcSymProp::gen_code(int discard) |
| | 7040 | { |
| | 7041 | /* |
| | 7042 | * Evaluating a property is equivalent to calling the property on |
| | 7043 | * the "self" object with no arguments. If there's no "self" |
| | 7044 | * object, an unqualified property evaluation is not possible, so |
| | 7045 | * log an error if this is the case. |
| | 7046 | */ |
| | 7047 | if (!G_cs->is_self_available()) |
| | 7048 | { |
| | 7049 | G_tok->log_error(TCERR_PROP_NEEDS_OBJ, (int)get_sym_len(), get_sym()); |
| | 7050 | return; |
| | 7051 | } |
| | 7052 | |
| | 7053 | if (G_cg->is_speculative()) |
| | 7054 | { |
| | 7055 | /* push 'self', then evaluate the property in data-only mode */ |
| | 7056 | G_cg->write_op(OPC_PUSHSELF); |
| | 7057 | G_cg->write_op(OPC_GETPROPDATA); |
| | 7058 | G_cs->write_prop_id(prop_); |
| | 7059 | |
| | 7060 | /* we pushed the 'self' value then popped it again */ |
| | 7061 | G_cg->note_push(); |
| | 7062 | G_cg->note_pop(); |
| | 7063 | } |
| | 7064 | else |
| | 7065 | { |
| | 7066 | /* generate the call to 'self' */ |
| | 7067 | G_cg->write_op(OPC_GETPROPSELF); |
| | 7068 | G_cs->write_prop_id(prop_); |
| | 7069 | } |
| | 7070 | |
| | 7071 | /* if they're not discarding the value, push the result */ |
| | 7072 | if (!discard) |
| | 7073 | { |
| | 7074 | G_cg->write_op(OPC_GETR0); |
| | 7075 | G_cg->note_push(); |
| | 7076 | } |
| | 7077 | } |
| | 7078 | |
| | 7079 | /* |
| | 7080 | * evaluate a member expression |
| | 7081 | */ |
| | 7082 | void CTcSymProp::gen_code_member(int discard, |
| | 7083 | CTcPrsNode *prop_expr, int prop_is_expr, |
| | 7084 | int argc, int varargs) |
| | 7085 | { |
| | 7086 | /* generate code to evaluate the property */ |
| | 7087 | gen_code(FALSE); |
| | 7088 | |
| | 7089 | /* if we have an argument counter, put it back on top */ |
| | 7090 | if (varargs) |
| | 7091 | G_cg->write_op(OPC_SWAP); |
| | 7092 | |
| | 7093 | /* use the standard member generation */ |
| | 7094 | CTcPrsNode::s_gen_member_rhs(discard, prop_expr, prop_is_expr, |
| | 7095 | argc, varargs); |
| | 7096 | } |
| | 7097 | |
| | 7098 | /* |
| | 7099 | * take the address of the property |
| | 7100 | */ |
| | 7101 | void CTcSymProp::gen_code_addr() |
| | 7102 | { |
| | 7103 | /* write code to push the property ID */ |
| | 7104 | G_cg->write_op(OPC_PUSHPROPID); |
| | 7105 | G_cs->write_prop_id(prop_); |
| | 7106 | |
| | 7107 | /* note the push */ |
| | 7108 | G_cg->note_push(); |
| | 7109 | } |
| | 7110 | |
| | 7111 | /* |
| | 7112 | * assign to a property, implicitly of the "self" object |
| | 7113 | */ |
| | 7114 | int CTcSymProp::gen_code_asi(int discard, tc_asitype_t typ, |
| | 7115 | class CTcPrsNode *rhs, int /*ignore_errors*/) |
| | 7116 | { |
| | 7117 | /* if there's no "self" object, we can't make this assignment */ |
| | 7118 | if (!G_cs->is_self_available()) |
| | 7119 | { |
| | 7120 | /* log an error */ |
| | 7121 | G_tok->log_error(TCERR_SETPROP_NEEDS_OBJ, |
| | 7122 | (int)get_sym_len(), get_sym()); |
| | 7123 | |
| | 7124 | /* |
| | 7125 | * indicate that we're finished, since there's nothing more we |
| | 7126 | * can do here |
| | 7127 | */ |
| | 7128 | return TRUE; |
| | 7129 | } |
| | 7130 | |
| | 7131 | /* |
| | 7132 | * if it's not a simple assignment, tell the caller to do the |
| | 7133 | * composite work and get back to us with the value to store |
| | 7134 | */ |
| | 7135 | if (typ != TC_ASI_SIMPLE) |
| | 7136 | return FALSE; |
| | 7137 | |
| | 7138 | /* |
| | 7139 | * generate the right-hand side's expression for assignment, unless |
| | 7140 | * the caller has already done so |
| | 7141 | */ |
| | 7142 | if (rhs != 0) |
| | 7143 | rhs->gen_code(FALSE, FALSE); |
| | 7144 | |
| | 7145 | /* |
| | 7146 | * if we're not discarding the value, make a copy - we'll consume a |
| | 7147 | * copy in the SETPROP instruction, so we need one more copy to |
| | 7148 | * return to the enclosing expression |
| | 7149 | */ |
| | 7150 | if (!discard) |
| | 7151 | { |
| | 7152 | G_cg->write_op(OPC_DUP); |
| | 7153 | G_cg->note_push(); |
| | 7154 | } |
| | 7155 | |
| | 7156 | /* |
| | 7157 | * write the SETPROP instruction - use the special form to assign to |
| | 7158 | * "self" |
| | 7159 | */ |
| | 7160 | G_cg->write_op(OPC_SETPROPSELF); |
| | 7161 | G_cs->write_prop_id(prop_); |
| | 7162 | |
| | 7163 | /* setpropself removes the value */ |
| | 7164 | G_cg->note_pop(); |
| | 7165 | |
| | 7166 | /* handled */ |
| | 7167 | return TRUE; |
| | 7168 | } |
| | 7169 | |
| | 7170 | /* |
| | 7171 | * call the symbol |
| | 7172 | */ |
| | 7173 | void CTcSymProp::gen_code_call(int discard, int argc, int varargs) |
| | 7174 | { |
| | 7175 | /* |
| | 7176 | * if there's no "self", we can't invoke a property without an |
| | 7177 | * explicit object reference |
| | 7178 | */ |
| | 7179 | if (!G_cs->is_self_available()) |
| | 7180 | { |
| | 7181 | G_tok->log_error(TCERR_PROP_NEEDS_OBJ, (int)get_sym_len(), get_sym()); |
| | 7182 | return; |
| | 7183 | } |
| | 7184 | |
| | 7185 | /* don't allow calling with arguments in speculative mode */ |
| | 7186 | if (argc != 0 && G_cg->is_speculative()) |
| | 7187 | err_throw(VMERR_BAD_SPEC_EVAL); |
| | 7188 | |
| | 7189 | /* generate code to invoke the property of "self" */ |
| | 7190 | if (G_cg->is_speculative()) |
| | 7191 | { |
| | 7192 | /* push 'self', then get the property in data-only mode */ |
| | 7193 | G_cg->write_op(OPC_PUSHSELF); |
| | 7194 | G_cg->write_op(OPC_GETPROPDATA); |
| | 7195 | G_cs->write_prop_id(get_prop()); |
| | 7196 | |
| | 7197 | /* we pushed 'self' then popped it again */ |
| | 7198 | G_cg->note_push(); |
| | 7199 | G_cg->note_pop(); |
| | 7200 | } |
| | 7201 | else if (argc == 0) |
| | 7202 | { |
| | 7203 | /* use the instruction with no arguments */ |
| | 7204 | G_cg->write_op(OPC_GETPROPSELF); |
| | 7205 | G_cs->write_prop_id(get_prop()); |
| | 7206 | } |
| | 7207 | else |
| | 7208 | { |
| | 7209 | /* write the varargs modifier if appropriate */ |
| | 7210 | if (varargs) |
| | 7211 | G_cg->write_op(OPC_VARARGC); |
| | 7212 | |
| | 7213 | /* use the instruction with arguments */ |
| | 7214 | G_cg->write_op(OPC_CALLPROPSELF); |
| | 7215 | G_cs->write((char)argc); |
| | 7216 | G_cs->write_prop_id(get_prop()); |
| | 7217 | |
| | 7218 | /* callpropself removes arguments */ |
| | 7219 | G_cg->note_pop(argc); |
| | 7220 | } |
| | 7221 | |
| | 7222 | /* if we're not discarding, push the return value from R0 */ |
| | 7223 | if (!discard) |
| | 7224 | { |
| | 7225 | G_cg->write_op(OPC_GETR0); |
| | 7226 | G_cg->note_push(); |
| | 7227 | } |
| | 7228 | } |
| | 7229 | |
| | 7230 | /* |
| | 7231 | * generate a property ID expression |
| | 7232 | */ |
| | 7233 | vm_prop_id_t CTcSymProp::gen_code_propid(int check_only, int is_expr) |
| | 7234 | { |
| | 7235 | /* |
| | 7236 | * If I'm to be treated as an expression (which indicates that the |
| | 7237 | * property symbol is explicitly enclosed in parentheses in the |
| | 7238 | * original source code expression), then I must evaluate this |
| | 7239 | * property of self. Otherwise, I yield literally the property ID. |
| | 7240 | */ |
| | 7241 | if (is_expr) |
| | 7242 | { |
| | 7243 | /* generate code unless we're only checking */ |
| | 7244 | if (!check_only) |
| | 7245 | { |
| | 7246 | /* evaluate this property of self */ |
| | 7247 | G_cg->write_op(OPC_GETPROPSELF); |
| | 7248 | G_cs->write_prop_id(get_prop()); |
| | 7249 | |
| | 7250 | /* leave the result on the stack */ |
| | 7251 | G_cg->write_op(OPC_GETR0); |
| | 7252 | G_cg->note_push(); |
| | 7253 | } |
| | 7254 | |
| | 7255 | /* tell the caller to use the stack value */ |
| | 7256 | return VM_INVALID_PROP; |
| | 7257 | } |
| | 7258 | else |
| | 7259 | { |
| | 7260 | /* simple '.prop' - return my property ID as a constant value */ |
| | 7261 | return get_prop(); |
| | 7262 | } |
| | 7263 | } |
| | 7264 | |
| | 7265 | /* |
| | 7266 | * add a runtime symbol table entry |
| | 7267 | */ |
| | 7268 | void CTcSymProp::add_runtime_symbol(CVmRuntimeSymbols *symtab) |
| | 7269 | { |
| | 7270 | vm_val_t val; |
| | 7271 | |
| | 7272 | /* add our entry */ |
| | 7273 | val.set_propid(get_prop()); |
| | 7274 | symtab->add_sym(get_sym(), get_sym_len(), &val); |
| | 7275 | } |
| | 7276 | |
| | 7277 | /* ------------------------------------------------------------------------ */ |
| | 7278 | /* |
| | 7279 | * Enumerator symbol |
| | 7280 | */ |
| | 7281 | |
| | 7282 | /* |
| | 7283 | * evaluate the symbol |
| | 7284 | */ |
| | 7285 | void CTcSymEnum::gen_code(int discard) |
| | 7286 | { |
| | 7287 | if (!discard) |
| | 7288 | { |
| | 7289 | /* generate code to push the enum value */ |
| | 7290 | G_cg->write_op(OPC_PUSHENUM); |
| | 7291 | G_cs->write_enum_id(get_enum_id()); |
| | 7292 | |
| | 7293 | /* note the push */ |
| | 7294 | G_cg->note_push(); |
| | 7295 | } |
| | 7296 | } |
| | 7297 | |
| | 7298 | /* |
| | 7299 | * add a runtime symbol table entry |
| | 7300 | */ |
| | 7301 | void CTcSymEnum::add_runtime_symbol(CVmRuntimeSymbols *symtab) |
| | 7302 | { |
| | 7303 | vm_val_t val; |
| | 7304 | |
| | 7305 | /* add our entry */ |
| | 7306 | val.set_enum(get_enum_id()); |
| | 7307 | symtab->add_sym(get_sym(), get_sym_len(), &val); |
| | 7308 | } |
| | 7309 | |
| | 7310 | |
| | 7311 | /* ------------------------------------------------------------------------ */ |
| | 7312 | /* |
| | 7313 | * T3-specific local variable/parameter symbol class |
| | 7314 | */ |
| | 7315 | |
| | 7316 | /* |
| | 7317 | * generate code to evaluate the symbol |
| | 7318 | */ |
| | 7319 | void CTcSymLocal::gen_code(int discard) |
| | 7320 | { |
| | 7321 | /* generate code to push the local, if we're not discarding it */ |
| | 7322 | if (!discard) |
| | 7323 | { |
| | 7324 | /* |
| | 7325 | * generate as a context local if required, otherwise as an |
| | 7326 | * ordinary local variable |
| | 7327 | */ |
| | 7328 | if (is_ctx_local_) |
| | 7329 | { |
| | 7330 | /* generate the context array lookup */ |
| | 7331 | if (ctx_var_num_ <= 255 && get_ctx_arr_idx() <= 255) |
| | 7332 | { |
| | 7333 | /* we can do this whole operation with one instruction */ |
| | 7334 | G_cg->write_op(OPC_IDXLCL1INT8); |
| | 7335 | G_cs->write((uchar)ctx_var_num_); |
| | 7336 | G_cs->write((uchar)get_ctx_arr_idx()); |
| | 7337 | |
| | 7338 | /* this pushes one value */ |
| | 7339 | G_cg->note_push(); |
| | 7340 | } |
| | 7341 | else |
| | 7342 | { |
| | 7343 | /* get our context array */ |
| | 7344 | s_gen_code_getlcl(ctx_var_num_, FALSE); |
| | 7345 | |
| | 7346 | /* get our value from the context array */ |
| | 7347 | CTPNConst::s_gen_code_int(get_ctx_arr_idx()); |
| | 7348 | G_cg->write_op(OPC_INDEX); |
| | 7349 | |
| | 7350 | /* the INDEX operation removes two values and pushes one */ |
| | 7351 | G_cg->note_pop(); |
| | 7352 | } |
| | 7353 | } |
| | 7354 | else |
| | 7355 | { |
| | 7356 | /* generate as an ordinary local */ |
| | 7357 | s_gen_code_getlcl(get_var_num(), is_param()); |
| | 7358 | } |
| | 7359 | } |
| | 7360 | |
| | 7361 | /* |
| | 7362 | * Mark the value as referenced, whether or not we're generating the |
| | 7363 | * code - the value has been logically referenced in the program |
| | 7364 | * even if the result of evaluating it isn't needed. |
| | 7365 | */ |
| | 7366 | set_val_used(TRUE); |
| | 7367 | } |
| | 7368 | |
| | 7369 | /* |
| | 7370 | * generate code to push a local onto the stack |
| | 7371 | */ |
| | 7372 | void CTcSymLocal::s_gen_code_getlcl(int var_num, int is_param) |
| | 7373 | { |
| | 7374 | /* use the shortest form of the instruction that we can */ |
| | 7375 | if (var_num <= 255) |
| | 7376 | { |
| | 7377 | /* 8-bit local number - use the one-byte form */ |
| | 7378 | G_cg->write_op(is_param ? OPC_GETARG1 : OPC_GETLCL1); |
| | 7379 | G_cs->write((char)var_num); |
| | 7380 | } |
| | 7381 | else |
| | 7382 | { |
| | 7383 | /* local number won't fit in 8 bits - use the two-byte form */ |
| | 7384 | G_cg->write_op(is_param ? OPC_GETARG2 : OPC_GETLCL2); |
| | 7385 | G_cs->write2(var_num); |
| | 7386 | } |
| | 7387 | |
| | 7388 | /* note the push */ |
| | 7389 | G_cg->note_push(); |
| | 7390 | } |
| | 7391 | |
| | 7392 | /* |
| | 7393 | * assign a value |
| | 7394 | */ |
| | 7395 | int CTcSymLocal::gen_code_asi(int discard, tc_asitype_t typ, |
| | 7396 | class CTcPrsNode *rhs, int ignore_errors) |
| | 7397 | { |
| | 7398 | int adding; |
| | 7399 | |
| | 7400 | /* mark the variable as having had a value assigned to it */ |
| | 7401 | set_val_assigned(TRUE); |
| | 7402 | |
| | 7403 | /* |
| | 7404 | * if the assignment is anything but simple, this references the |
| | 7405 | * value as well |
| | 7406 | */ |
| | 7407 | if (typ != TC_ASI_SIMPLE) |
| | 7408 | set_val_used(TRUE); |
| | 7409 | |
| | 7410 | /* |
| | 7411 | * If this is a context variable, use standard assignment (i.e., |
| | 7412 | * generate the result first, then generate a simple assignment to the |
| | 7413 | * variable). Otherwise, we might be able to generate a fancy |
| | 7414 | * combined calculate-and-assign sequence, depending on the type of |
| | 7415 | * assignment calculation we're performing. |
| | 7416 | */ |
| | 7417 | if (is_ctx_local_ && typ != TC_ASI_SIMPLE) |
| | 7418 | { |
| | 7419 | /* |
| | 7420 | * it's a context local and it's not a simple assignment, so we |
| | 7421 | * can't perform any special calculate-and-assign sequence - tell |
| | 7422 | * the caller to calculate the full result first and then try |
| | 7423 | * again using simple assignment |
| | 7424 | */ |
| | 7425 | return FALSE; |
| | 7426 | } |
| | 7427 | |
| | 7428 | /* |
| | 7429 | * check the type of assignment - we can optimize the code |
| | 7430 | * generation to use more compact instruction sequences for certain |
| | 7431 | * types of assignments |
| | 7432 | */ |
| | 7433 | switch(typ) |
| | 7434 | { |
| | 7435 | case TC_ASI_SIMPLE: |
| | 7436 | /* |
| | 7437 | * Simple assignment to local/parameter. Check for some special |
| | 7438 | * cases: when assigning a constant value of 0, 1, or nil to a |
| | 7439 | * local, we can generate a short instruction |
| | 7440 | */ |
| | 7441 | if (!is_param() && !is_ctx_local_ && rhs != 0 && rhs->is_const()) |
| | 7442 | { |
| | 7443 | CTcConstVal *cval; |
| | 7444 | |
| | 7445 | /* get the constant value */ |
| | 7446 | cval = rhs->get_const_val(); |
| | 7447 | |
| | 7448 | /* check for nil and 0 or 1 values */ |
| | 7449 | if (cval->get_type() == TC_CVT_NIL) |
| | 7450 | { |
| | 7451 | /* it's nil - generate NILLCL1 or NILLCL2 */ |
| | 7452 | if (get_var_num() <= 255) |
| | 7453 | { |
| | 7454 | G_cg->write_op(OPC_NILLCL1); |
| | 7455 | G_cs->write((char)get_var_num()); |
| | 7456 | } |
| | 7457 | else |
| | 7458 | { |
| | 7459 | G_cg->write_op(OPC_NILLCL2); |
| | 7460 | G_cs->write2(get_var_num()); |
| | 7461 | } |
| | 7462 | |
| | 7463 | /* if not discarding, leave nil on the stack */ |
| | 7464 | if (!discard) |
| | 7465 | { |
| | 7466 | G_cg->write_op(OPC_PUSHNIL); |
| | 7467 | G_cg->note_push(); |
| | 7468 | } |
| | 7469 | |
| | 7470 | /* handled */ |
| | 7471 | return TRUE; |
| | 7472 | } |
| | 7473 | else if (cval->get_type() == TC_CVT_INT |
| | 7474 | && (cval->get_val_int() == 0 |
| | 7475 | || cval->get_val_int() == 1)) |
| | 7476 | { |
| | 7477 | int ival; |
| | 7478 | |
| | 7479 | /* get the integer value */ |
| | 7480 | ival = cval->get_val_int(); |
| | 7481 | |
| | 7482 | /* 0 or 1 - generate ZEROLCLn or ONELCLn */ |
| | 7483 | if (get_var_num() <= 255) |
| | 7484 | { |
| | 7485 | G_cg->write_op(ival == 0 ? OPC_ZEROLCL1 : OPC_ONELCL1); |
| | 7486 | G_cs->write((char)get_var_num()); |
| | 7487 | } |
| | 7488 | else |
| | 7489 | { |
| | 7490 | G_cg->write_op(ival == 0 ? OPC_ZEROLCL2 : OPC_ONELCL2); |
| | 7491 | G_cs->write2(get_var_num()); |
| | 7492 | } |
| | 7493 | |
| | 7494 | /* if not discarding, leave the value on the stack */ |
| | 7495 | if (!discard) |
| | 7496 | { |
| | 7497 | G_cg->write_op(ival == 0 ? OPC_PUSH_0 : OPC_PUSH_1); |
| | 7498 | G_cg->note_push(); |
| | 7499 | } |
| | 7500 | |
| | 7501 | /* handled */ |
| | 7502 | return TRUE; |
| | 7503 | } |
| | 7504 | } |
| | 7505 | |
| | 7506 | /* |
| | 7507 | * If we got here, we can't generate a specialized constant |
| | 7508 | * assignment - so, first, generate the right-hand side's value |
| | 7509 | * normally. (If no 'rhs' is specified, the value is already on |
| | 7510 | * the stack.) |
| | 7511 | */ |
| | 7512 | if (rhs != 0) |
| | 7513 | rhs->gen_code(FALSE, FALSE); |
| | 7514 | |
| | 7515 | /* leave an extra copy of the value on the stack if not discarding */ |
| | 7516 | if (!discard) |
| | 7517 | { |
| | 7518 | G_cg->write_op(OPC_DUP); |
| | 7519 | G_cg->note_push(); |
| | 7520 | } |
| | 7521 | |
| | 7522 | /* now assign the value at top of stack to the variable */ |
| | 7523 | gen_code_setlcl(); |
| | 7524 | |
| | 7525 | /* handled */ |
| | 7526 | return TRUE; |
| | 7527 | |
| | 7528 | case TC_ASI_ADD: |
| | 7529 | adding = TRUE; |
| | 7530 | goto add_or_sub; |
| | 7531 | |
| | 7532 | case TC_ASI_SUB: |
| | 7533 | adding = FALSE; |
| | 7534 | |
| | 7535 | add_or_sub: |
| | 7536 | /* if this is a parameter, there's nothing special we can do */ |
| | 7537 | if (is_param()) |
| | 7538 | return FALSE; |
| | 7539 | |
| | 7540 | /* |
| | 7541 | * Add/subtract to a local/parameter. If the right-hand side is a |
| | 7542 | * constant integer value, we might be able to generate a special |
| | 7543 | * instruction to add/subtract it. |
| | 7544 | */ |
| | 7545 | if (rhs != 0 |
| | 7546 | && adding |
| | 7547 | && rhs->is_const() |
| | 7548 | && rhs->get_const_val()->get_type() == TC_CVT_INT) |
| | 7549 | { |
| | 7550 | long ival; |
| | 7551 | |
| | 7552 | /* get the integer value to assign */ |
| | 7553 | ival = rhs->get_const_val()->get_val_int(); |
| | 7554 | |
| | 7555 | /* |
| | 7556 | * if the right-hand side's integer value fits in one byte, |
| | 7557 | * generate the short (8-bit) instruction; otherwise, |
| | 7558 | * generate the long (32-bit) format |
| | 7559 | */ |
| | 7560 | if (ival == 1) |
| | 7561 | { |
| | 7562 | /* adding one - increment the local */ |
| | 7563 | G_cg->write_op(OPC_INCLCL); |
| | 7564 | G_cs->write2(get_var_num()); |
| | 7565 | } |
| | 7566 | else if (ival == -1) |
| | 7567 | { |
| | 7568 | /* subtracting one - decrement the local */ |
| | 7569 | G_cg->write_op(OPC_DECLCL); |
| | 7570 | G_cs->write2(get_var_num()); |
| | 7571 | } |
| | 7572 | else if (ival <= 127 && ival >= -128 |
| | 7573 | && get_var_num() <= 255) |
| | 7574 | { |
| | 7575 | /* fits in 8 bits - use the 8-bit format */ |
| | 7576 | G_cg->write_op(OPC_ADDILCL1); |
| | 7577 | G_cs->write((char)get_var_num()); |
| | 7578 | G_cs->write((char)ival); |
| | 7579 | } |
| | 7580 | else |
| | 7581 | { |
| | 7582 | /* |
| | 7583 | * either the value or the variable number doesn't fit |
| | 7584 | * in 8 bits - use the 32-bit format |
| | 7585 | */ |
| | 7586 | G_cg->write_op(OPC_ADDILCL4); |
| | 7587 | G_cs->write2(get_var_num()); |
| | 7588 | G_cs->write4(ival); |
| | 7589 | } |
| | 7590 | } |
| | 7591 | else |
| | 7592 | { |
| | 7593 | /* |
| | 7594 | * We don't have a special instruction for the right side, |
| | 7595 | * so generate it normally and add/subtract the value. (If |
| | 7596 | * there's no 'rhs' value specified, it means that the value |
| | 7597 | * is already on the stack, so there's nothing extra for us |
| | 7598 | * to generate.) |
| | 7599 | */ |
| | 7600 | if (rhs != 0) |
| | 7601 | rhs->gen_code(FALSE, FALSE); |
| | 7602 | |
| | 7603 | /* write the ADDTOLCL instruction */ |
| | 7604 | G_cg->write_op(adding ? OPC_ADDTOLCL : OPC_SUBFROMLCL); |
| | 7605 | G_cs->write2(get_var_num()); |
| | 7606 | |
| | 7607 | /* addtolcl/subfromlcl remove the rvalue */ |
| | 7608 | G_cg->note_pop(); |
| | 7609 | } |
| | 7610 | |
| | 7611 | /* |
| | 7612 | * if not discarding, push the result onto the stack; do this by |
| | 7613 | * simply evaluating the local, which is the simplest and most |
| | 7614 | * efficient way to obtain the result of the computation |
| | 7615 | */ |
| | 7616 | if (!discard) |
| | 7617 | gen_code(FALSE); |
| | 7618 | |
| | 7619 | /* handled */ |
| | 7620 | return TRUE; |
| | 7621 | |
| | 7622 | case TC_ASI_PREINC: |
| | 7623 | /* if this is a parameter, there's nothing special we can do */ |
| | 7624 | if (is_param()) |
| | 7625 | return FALSE; |
| | 7626 | |
| | 7627 | /* generate code to increment the local */ |
| | 7628 | G_cg->write_op(OPC_INCLCL); |
| | 7629 | G_cs->write2(get_var_num()); |
| | 7630 | |
| | 7631 | /* if we're not discarding, push the local's new value */ |
| | 7632 | if (!discard) |
| | 7633 | gen_code(FALSE); |
| | 7634 | |
| | 7635 | /* handled */ |
| | 7636 | return TRUE; |
| | 7637 | |
| | 7638 | case TC_ASI_POSTINC: |
| | 7639 | /* if this is a parameter, there's nothing special we can do */ |
| | 7640 | if (is_param()) |
| | 7641 | return FALSE; |
| | 7642 | |
| | 7643 | /* |
| | 7644 | * if we're not discarding, push the local's value prior to |
| | 7645 | * incrementing it - this will be the result we'll leave on the |
| | 7646 | * stack |
| | 7647 | */ |
| | 7648 | if (!discard) |
| | 7649 | gen_code(FALSE); |
| | 7650 | |
| | 7651 | /* generate code to increment the local */ |
| | 7652 | G_cg->write_op(OPC_INCLCL); |
| | 7653 | G_cs->write2(get_var_num()); |
| | 7654 | |
| | 7655 | /* handled */ |
| | 7656 | return TRUE; |
| | 7657 | |
| | 7658 | case TC_ASI_PREDEC: |
| | 7659 | /* if this is a parameter, there's nothing special we can do */ |
| | 7660 | if (is_param()) |
| | 7661 | return FALSE; |
| | 7662 | |
| | 7663 | /* generate code to decrement the local */ |
| | 7664 | G_cg->write_op(OPC_DECLCL); |
| | 7665 | G_cs->write2(get_var_num()); |
| | 7666 | |
| | 7667 | /* if we're not discarding, push the local's new value */ |
| | 7668 | if (!discard) |
| | 7669 | gen_code(FALSE); |
| | 7670 | |
| | 7671 | /* handled */ |
| | 7672 | return TRUE; |
| | 7673 | |
| | 7674 | case TC_ASI_POSTDEC: |
| | 7675 | /* if this is a parameter, there's nothing special we can do */ |
| | 7676 | if (is_param()) |
| | 7677 | return FALSE; |
| | 7678 | |
| | 7679 | /* |
| | 7680 | * if we're not discarding, push the local's value prior to |
| | 7681 | * decrementing it - this will be the result we'll leave on the |
| | 7682 | * stack |
| | 7683 | */ |
| | 7684 | if (!discard) |
| | 7685 | gen_code(FALSE); |
| | 7686 | |
| | 7687 | /* generate code to decrement the local */ |
| | 7688 | G_cg->write_op(OPC_DECLCL); |
| | 7689 | G_cs->write2(get_var_num()); |
| | 7690 | |
| | 7691 | /* handled */ |
| | 7692 | return TRUE; |
| | 7693 | |
| | 7694 | default: |
| | 7695 | /* we can't do anything special with other assignment types */ |
| | 7696 | return FALSE; |
| | 7697 | } |
| | 7698 | } |
| | 7699 | |
| | 7700 | /* |
| | 7701 | * generate code to assigin the value at top of stack to the local |
| | 7702 | * variable |
| | 7703 | */ |
| | 7704 | void CTcSymLocal::gen_code_setlcl() |
| | 7705 | { |
| | 7706 | /* check to see if we're a context local (as opposed to a stack local) */ |
| | 7707 | if (is_ctx_local_) |
| | 7708 | { |
| | 7709 | /* generate the assignment using the appropriate sequence */ |
| | 7710 | if (ctx_var_num_ <= 255 && get_ctx_arr_idx() <= 255) |
| | 7711 | { |
| | 7712 | /* we can fit this in a single instruction */ |
| | 7713 | G_cg->write_op(OPC_SETINDLCL1I8); |
| | 7714 | G_cs->write((uchar)ctx_var_num_); |
| | 7715 | G_cs->write((uchar)get_ctx_arr_idx()); |
| | 7716 | |
| | 7717 | /* this pops the value being assigned */ |
| | 7718 | G_cg->note_pop(); |
| | 7719 | } |
| | 7720 | else |
| | 7721 | { |
| | 7722 | /* get our context array */ |
| | 7723 | s_gen_code_getlcl(ctx_var_num_, FALSE); |
| | 7724 | |
| | 7725 | /* set our value in the context array */ |
| | 7726 | CTPNConst::s_gen_code_int(get_ctx_arr_idx()); |
| | 7727 | G_cg->write_op(OPC_SETIND); |
| | 7728 | G_cg->write_op(OPC_DISC); |
| | 7729 | |
| | 7730 | /* |
| | 7731 | * the SETIND pops three values and pushes one (for a net two |
| | 7732 | * pops), and the DISC pops one more value, so our total is |
| | 7733 | * three pops |
| | 7734 | */ |
| | 7735 | G_cg->note_pop(3); |
| | 7736 | } |
| | 7737 | } |
| | 7738 | else |
| | 7739 | { |
| | 7740 | /* we're just a plain stack variable */ |
| | 7741 | gen_code_setlcl_stk(); |
| | 7742 | } |
| | 7743 | } |
| | 7744 | |
| | 7745 | /* |
| | 7746 | * Generate code to store the value at the top of the stack into the given |
| | 7747 | * local stack slot. Note that this routine will not work with a context |
| | 7748 | * local - it only works if the variable is known to be a stack variable. |
| | 7749 | */ |
| | 7750 | void CTcSymLocal::s_gen_code_setlcl_stk(int var_num, int is_param) |
| | 7751 | { |
| | 7752 | /* use the shortest form that will fit our variable index */ |
| | 7753 | if (var_num <= 255) |
| | 7754 | { |
| | 7755 | /* use the one-byte instruction */ |
| | 7756 | G_cg->write_op(is_param ? OPC_SETARG1 : OPC_SETLCL1); |
| | 7757 | G_cs->write((char)var_num); |
| | 7758 | } |
| | 7759 | else |
| | 7760 | { |
| | 7761 | /* big number - use the two-byte instruction */ |
| | 7762 | G_cg->write_op(is_param ? OPC_SETARG2 : OPC_SETLCL2); |
| | 7763 | G_cs->write2(var_num); |
| | 7764 | } |
| | 7765 | |
| | 7766 | /* the setarg/setlcl ops remove the rvalue */ |
| | 7767 | G_cg->note_pop(); |
| | 7768 | } |
| | 7769 | |
| | 7770 | /* |
| | 7771 | * call the symbol |
| | 7772 | */ |
| | 7773 | void CTcSymLocal::gen_code_call(int discard, int argc, int varargs) |
| | 7774 | { |
| | 7775 | /* |
| | 7776 | * to call a local, we'll simply evaluate the local normally, then |
| | 7777 | * call through the resulting (presumed) property or function |
| | 7778 | * pointer value |
| | 7779 | */ |
| | 7780 | gen_code(FALSE); |
| | 7781 | |
| | 7782 | /* |
| | 7783 | * if we have a varargs list, modify the call instruction that |
| | 7784 | * follows to make it a varargs call |
| | 7785 | */ |
| | 7786 | if (varargs) |
| | 7787 | { |
| | 7788 | /* swap the top of the stack to get the arg counter back on top */ |
| | 7789 | G_cg->write_op(OPC_SWAP); |
| | 7790 | |
| | 7791 | /* write the varargs modifier */ |
| | 7792 | G_cg->write_op(OPC_VARARGC); |
| | 7793 | } |
| | 7794 | |
| | 7795 | /* don't allow this at all in speculative mode */ |
| | 7796 | if (G_cg->is_speculative()) |
| | 7797 | err_throw(VMERR_BAD_SPEC_EVAL); |
| | 7798 | |
| | 7799 | /* call the result as a function or method pointer */ |
| | 7800 | G_cg->write_op(OPC_PTRCALL); |
| | 7801 | G_cs->write((char)argc); |
| | 7802 | |
| | 7803 | /* ptrcall removes the arguments and the function pointer */ |
| | 7804 | G_cg->note_pop(argc + 1); |
| | 7805 | |
| | 7806 | /* if we're not discarding the value, push the result */ |
| | 7807 | if (!discard) |
| | 7808 | { |
| | 7809 | G_cg->write_op(OPC_GETR0); |
| | 7810 | G_cg->note_push(); |
| | 7811 | } |
| | 7812 | } |
| | 7813 | |
| | 7814 | /* |
| | 7815 | * generate a property ID expression |
| | 7816 | */ |
| | 7817 | vm_prop_id_t CTcSymLocal::gen_code_propid(int check_only, int /*is_expr*/) |
| | 7818 | { |
| | 7819 | /* |
| | 7820 | * treat the local as a property-valued expression; generate the |
| | 7821 | * code for the local, then tell the caller that no constant value |
| | 7822 | * is available, since the local's property ID value should be on |
| | 7823 | * the stack |
| | 7824 | */ |
| | 7825 | if (!check_only) |
| | 7826 | gen_code(FALSE); |
| | 7827 | |
| | 7828 | /* tell the caller to use the stack value */ |
| | 7829 | return VM_INVALID_PROP; |
| | 7830 | } |
| | 7831 | |
| | 7832 | /* |
| | 7833 | * evaluate a member expression |
| | 7834 | */ |
| | 7835 | void CTcSymLocal::gen_code_member(int discard, |
| | 7836 | CTcPrsNode *prop_expr, int prop_is_expr, |
| | 7837 | int argc, int varargs) |
| | 7838 | { |
| | 7839 | /* generate code to evaluate the local */ |
| | 7840 | gen_code(FALSE); |
| | 7841 | |
| | 7842 | /* if we have an argument counter, put it back on top */ |
| | 7843 | if (varargs) |
| | 7844 | G_cg->write_op(OPC_SWAP); |
| | 7845 | |
| | 7846 | /* use the standard member generation */ |
| | 7847 | CTcPrsNode::s_gen_member_rhs(discard, prop_expr, prop_is_expr, |
| | 7848 | argc, varargs); |
| | 7849 | } |
| | 7850 | |
| | 7851 | /* |
| | 7852 | * write to a debug record |
| | 7853 | */ |
| | 7854 | int CTcSymLocal::write_to_debug_frame() |
| | 7855 | { |
| | 7856 | int flags; |
| | 7857 | |
| | 7858 | /* |
| | 7859 | * write my ID - if we're a context variable, we want to write the |
| | 7860 | * context variable ID; otherwise write our stack location as normal |
| | 7861 | */ |
| | 7862 | if (is_ctx_local_) |
| | 7863 | G_cs->write2(ctx_var_num_); |
| | 7864 | else |
| | 7865 | G_cs->write2(var_num_); |
| | 7866 | |
| | 7867 | /* compute my flags */ |
| | 7868 | flags = 0; |
| | 7869 | if (is_param_) |
| | 7870 | flags |= 1; |
| | 7871 | if (is_ctx_local_) |
| | 7872 | flags |= 2; |
| | 7873 | |
| | 7874 | /* write my flags */ |
| | 7875 | G_cs->write2(flags); |
| | 7876 | |
| | 7877 | /* write my local context array index */ |
| | 7878 | G_cs->write2(get_ctx_arr_idx()); |
| | 7879 | |
| | 7880 | /* write the length of my symbol name */ |
| | 7881 | G_cs->write2(len_); |
| | 7882 | G_cs->write(str_, len_); |
| | 7883 | |
| | 7884 | /* we did write this symbol */ |
| | 7885 | return TRUE; |
| | 7886 | } |
| | 7887 | |
| | 7888 | /* ------------------------------------------------------------------------ */ |
| | 7889 | /* |
| | 7890 | * Built-in function symbol |
| | 7891 | */ |
| | 7892 | |
| | 7893 | /* |
| | 7894 | * Evaluate the symbol. Invoking a built-in function without an |
| | 7895 | * argument list is simply a call to the built-in function with no |
| | 7896 | * arguments. |
| | 7897 | */ |
| | 7898 | void CTcSymBif::gen_code(int discard) |
| | 7899 | { |
| | 7900 | /* generate a call */ |
| | 7901 | gen_code_call(discard, 0, FALSE); |
| | 7902 | } |
| | 7903 | |
| | 7904 | /* |
| | 7905 | * Generate code to call the built-in function |
| | 7906 | */ |
| | 7907 | void CTcSymBif::gen_code_call(int discard, int argc, int varargs) |
| | 7908 | { |
| | 7909 | /* don't allow calling built-in functions in speculative mode */ |
| | 7910 | if (G_cg->is_speculative()) |
| | 7911 | err_throw(VMERR_BAD_SPEC_EVAL); |
| | 7912 | |
| | 7913 | /* check for minimum and maximum arguments */ |
| | 7914 | if (argc < min_argc_) |
| | 7915 | { |
| | 7916 | G_tok->log_error(TCERR_TOO_FEW_FUNC_ARGS, |
| | 7917 | (int)get_sym_len(), get_sym()); |
| | 7918 | } |
| | 7919 | else if (!varargs_ && argc > max_argc_) |
| | 7920 | { |
| | 7921 | G_tok->log_error(TCERR_TOO_MANY_FUNC_ARGS, |
| | 7922 | (int)get_sym_len(), get_sym()); |
| | 7923 | } |
| | 7924 | |
| | 7925 | /* write the varargs modifier if appropriate */ |
| | 7926 | if (varargs) |
| | 7927 | G_cg->write_op(OPC_VARARGC); |
| | 7928 | |
| | 7929 | /* generate the call */ |
| | 7930 | if (get_func_set_id() < 4 && get_func_idx() < 256) |
| | 7931 | { |
| | 7932 | uchar short_ops[] = |
| | 7933 | { OPC_BUILTIN_A, OPC_BUILTIN_B, OPC_BUILTIN_C, OPC_BUILTIN_D }; |
| | 7934 | |
| | 7935 | /* |
| | 7936 | * it's one of the first 256 functions in one of the first four |
| | 7937 | * function sets - we can generate a short instruction |
| | 7938 | */ |
| | 7939 | G_cg->write_op(short_ops[get_func_set_id()]); |
| | 7940 | G_cs->write((char)argc); |
| | 7941 | G_cs->write((char)get_func_idx()); |
| | 7942 | } |
| | 7943 | else |
| | 7944 | { |
| | 7945 | /* it's not in the default set - use the longer instruction */ |
| | 7946 | if (get_func_idx() < 256) |
| | 7947 | { |
| | 7948 | /* low function index - write the short form */ |
| | 7949 | G_cg->write_op(OPC_BUILTIN1); |
| | 7950 | G_cs->write((char)argc); |
| | 7951 | G_cs->write((char)get_func_idx()); |
| | 7952 | } |
| | 7953 | else |
| | 7954 | { |
| | 7955 | /* big function index - write the long form */ |
| | 7956 | G_cg->write_op(OPC_BUILTIN2); |
| | 7957 | G_cs->write((char)argc); |
| | 7958 | G_cs->write2(get_func_idx()); |
| | 7959 | } |
| | 7960 | |
| | 7961 | /* write the function set ID */ |
| | 7962 | G_cs->write((char)get_func_set_id()); |
| | 7963 | } |
| | 7964 | |
| | 7965 | /* the built-in functions always remove arguments */ |
| | 7966 | G_cg->note_pop(argc); |
| | 7967 | |
| | 7968 | /* |
| | 7969 | * if they're not discarding the value, push it - the value is |
| | 7970 | * sitting in R0 after the call returns |
| | 7971 | */ |
| | 7972 | if (!discard) |
| | 7973 | { |
| | 7974 | G_cg->write_op(OPC_GETR0); |
| | 7975 | G_cg->note_push(); |
| | 7976 | } |
| | 7977 | } |
| | 7978 | |
| | 7979 | |
| | 7980 | /* ------------------------------------------------------------------------ */ |
| | 7981 | /* |
| | 7982 | * External function symbol |
| | 7983 | */ |
| | 7984 | |
| | 7985 | /* |
| | 7986 | * evaluate the symbol |
| | 7987 | */ |
| | 7988 | void CTcSymExtfn::gen_code(int /*discard*/) |
| | 7989 | { |
| | 7990 | //$$$ to be implemented |
| | 7991 | assert(FALSE); |
| | 7992 | } |
| | 7993 | |
| | 7994 | /* |
| | 7995 | * generate a call to the symbol |
| | 7996 | */ |
| | 7997 | void CTcSymExtfn::gen_code_call(int /*discard*/, int /*argc*/, int /*varargs*/) |
| | 7998 | { |
| | 7999 | //$$$ to be implemented |
| | 8000 | assert(FALSE); |
| | 8001 | } |
| | 8002 | |
| | 8003 | |
| | 8004 | /* ------------------------------------------------------------------------ */ |
| | 8005 | /* |
| | 8006 | * Code Label symbol |
| | 8007 | */ |
| | 8008 | |
| | 8009 | /* |
| | 8010 | * evaluate the symbol |
| | 8011 | */ |
| | 8012 | void CTcSymLabel::gen_code(int discard) |
| | 8013 | { |
| | 8014 | /* it's not legal to evaluate a code label; log an error */ |
| | 8015 | G_tok->log_error(TCERR_CANNOT_EVAL_LABEL, |
| | 8016 | (int)get_sym_len(), get_sym()); |
| | 8017 | } |
| | 8018 | |
| | 8019 | /* ------------------------------------------------------------------------ */ |
| | 8020 | /* |
| | 8021 | * Metaclass symbol |
| | 8022 | */ |
| | 8023 | |
| | 8024 | /* |
| | 8025 | * generate code for evaluating the symbol |
| | 8026 | */ |
| | 8027 | void CTcSymMetaclass::gen_code(int discard) |
| | 8028 | { |
| | 8029 | /* |
| | 8030 | * the metaclass name refers to the IntrinsicClass instance |
| | 8031 | * associated with the metaclass |
| | 8032 | */ |
| | 8033 | G_cg->write_op(OPC_PUSHOBJ); |
| | 8034 | G_cs->write_obj_id(class_obj_); |
| | 8035 | |
| | 8036 | /* note the push */ |
| | 8037 | G_cg->note_push(); |
| | 8038 | } |
| | 8039 | |
| | 8040 | /* |
| | 8041 | * generate code for operator 'new' applied to the metaclass |
| | 8042 | */ |
| | 8043 | void CTcSymMetaclass::gen_code_new(int discard, int argc, int varargs, |
| | 8044 | int is_transient) |
| | 8045 | { |
| | 8046 | /* if we have varargs, write the modifier */ |
| | 8047 | if (varargs) |
| | 8048 | G_cg->write_op(OPC_VARARGC); |
| | 8049 | |
| | 8050 | if (meta_idx_ <= 255 && argc <= 255) |
| | 8051 | { |
| | 8052 | G_cg->write_op(is_transient ? OPC_TRNEW1 : OPC_NEW1); |
| | 8053 | G_cs->write((char)argc); |
| | 8054 | G_cs->write((char)meta_idx_); |
| | 8055 | } |
| | 8056 | else |
| | 8057 | { |
| | 8058 | G_cg->write_op(is_transient ? OPC_TRNEW1 : OPC_NEW2); |
| | 8059 | G_cs->write2(argc); |
| | 8060 | G_cs->write2(meta_idx_); |
| | 8061 | } |
| | 8062 | |
| | 8063 | /* new1/new2 remove arguments */ |
| | 8064 | G_cg->note_pop(argc); |
| | 8065 | |
| | 8066 | /* if we're not discarding the value, push it */ |
| | 8067 | if (!discard) |
| | 8068 | { |
| | 8069 | G_cg->write_op(OPC_GETR0); |
| | 8070 | G_cg->note_push(); |
| | 8071 | } |
| | 8072 | } |
| | 8073 | |
| | 8074 | /* |
| | 8075 | * generate a member expression |
| | 8076 | */ |
| | 8077 | void CTcSymMetaclass::gen_code_member(int discard, CTcPrsNode *prop_expr, |
| | 8078 | int prop_is_expr, |
| | 8079 | int argc, int varargs) |
| | 8080 | { |
| | 8081 | /* generate code to push our class object onto the stack */ |
| | 8082 | gen_code(FALSE); |
| | 8083 | |
| | 8084 | /* if we have an argument counter, put it back on top */ |
| | 8085 | if (varargs) |
| | 8086 | G_cg->write_op(OPC_SWAP); |
| | 8087 | |
| | 8088 | /* use the standard member generation */ |
| | 8089 | CTcPrsNode::s_gen_member_rhs(discard, prop_expr, prop_is_expr, |
| | 8090 | argc, varargs); |
| | 8091 | } |
| | 8092 | |
| | 8093 | /* |
| | 8094 | * add a runtime symbol table entry |
| | 8095 | */ |
| | 8096 | void CTcSymMetaclass::add_runtime_symbol(CVmRuntimeSymbols *symtab) |
| | 8097 | { |
| | 8098 | vm_val_t val; |
| | 8099 | |
| | 8100 | /* add our entry */ |
| | 8101 | val.set_obj(get_class_obj()); |
| | 8102 | symtab->add_sym(get_sym(), get_sym_len(), &val); |
| | 8103 | } |
| | 8104 | |
| | 8105 | |
| | 8106 | /* ------------------------------------------------------------------------ */ |
| | 8107 | /* |
| | 8108 | * Exception Table |
| | 8109 | */ |
| | 8110 | |
| | 8111 | /* |
| | 8112 | * create |
| | 8113 | */ |
| | 8114 | CTcT3ExcTable::CTcT3ExcTable() |
| | 8115 | { |
| | 8116 | /* allocate an initial table */ |
| | 8117 | exc_alloced_ = 1024; |
| | 8118 | table_ = (CTcT3ExcEntry *)t3malloc(exc_alloced_ * sizeof(table_[0])); |
| | 8119 | |
| | 8120 | /* no entries are in use yet */ |
| | 8121 | exc_used_ = 0; |
| | 8122 | |
| | 8123 | /* method offset is not yet known */ |
| | 8124 | method_ofs_ = 0; |
| | 8125 | } |
| | 8126 | |
| | 8127 | |
| | 8128 | /* |
| | 8129 | * add an entry to our table |
| | 8130 | */ |
| | 8131 | void CTcT3ExcTable::add_catch(ulong protected_start_ofs, |
| | 8132 | ulong protected_end_ofs, |
| | 8133 | ulong exc_obj_id, ulong catch_block_ofs) |
| | 8134 | { |
| | 8135 | CTcT3ExcEntry *entry; |
| | 8136 | |
| | 8137 | /* if necessary, expand our table */ |
| | 8138 | if (exc_used_ == exc_alloced_) |
| | 8139 | { |
| | 8140 | /* expand the table a bit */ |
| | 8141 | exc_alloced_ += 1024; |
| | 8142 | |
| | 8143 | /* reallocate the table at the larger size */ |
| | 8144 | table_ = (CTcT3ExcEntry *) |
| | 8145 | t3realloc(table_, exc_alloced_ * sizeof(table_[0])); |
| | 8146 | } |
| | 8147 | |
| | 8148 | /* |
| | 8149 | * set up the new entry - store the offsets relative to the method |
| | 8150 | * header start address |
| | 8151 | */ |
| | 8152 | entry = table_ + exc_used_; |
| | 8153 | entry->start_ofs = protected_start_ofs - method_ofs_; |
| | 8154 | entry->end_ofs = protected_end_ofs - method_ofs_; |
| | 8155 | entry->exc_obj_id = exc_obj_id; |
| | 8156 | entry->catch_ofs = catch_block_ofs - method_ofs_; |
| | 8157 | |
| | 8158 | /* consume the new entry */ |
| | 8159 | ++exc_used_; |
| | 8160 | } |
| | 8161 | |
| | 8162 | /* |
| | 8163 | * write our exception table to the code stream |
| | 8164 | */ |
| | 8165 | void CTcT3ExcTable::write_to_code_stream() |
| | 8166 | { |
| | 8167 | CTcT3ExcEntry *entry; |
| | 8168 | size_t i; |
| | 8169 | |
| | 8170 | /* write the number of entries as a UINT2 */ |
| | 8171 | G_cs->write2(exc_used_); |
| | 8172 | |
| | 8173 | /* write the entries */ |
| | 8174 | for (i = 0, entry = table_ ; i < exc_used_ ; ++i, ++entry) |
| | 8175 | { |
| | 8176 | /* write this entry */ |
| | 8177 | G_cs->write2(entry->start_ofs); |
| | 8178 | G_cs->write2(entry->end_ofs); |
| | 8179 | G_cs->write_obj_id(entry->exc_obj_id); |
| | 8180 | G_cs->write2(entry->catch_ofs); |
| | 8181 | } |
| | 8182 | } |
| | 8183 | |
| | 8184 | |
| | 8185 | /* ------------------------------------------------------------------------ */ |
| | 8186 | /* |
| | 8187 | * Code body |
| | 8188 | */ |
| | 8189 | |
| | 8190 | /* |
| | 8191 | * generate code |
| | 8192 | */ |
| | 8193 | void CTPNCodeBody::gen_code(int, int) |
| | 8194 | { |
| | 8195 | CTcCodeBodyCtx *cur_ctx; |
| | 8196 | int ctx_idx; |
| | 8197 | tct3_method_gen_ctx gen_ctx; |
| | 8198 | |
| | 8199 | /* if I've been replaced, don't bother generating any code */ |
| | 8200 | if (replaced_) |
| | 8201 | return; |
| | 8202 | |
| | 8203 | /* |
| | 8204 | * Open the method header. |
| | 8205 | * |
| | 8206 | * Generate to the static stream if this is a static initializer |
| | 8207 | * method, otherwise to the main stream. |
| | 8208 | * |
| | 8209 | * Anchor the fixups in the associated symbol table entry, if any. We |
| | 8210 | * maintain our own fixup list if we don't have a symbol, otherwise we |
| | 8211 | * use the one from our symbol table entry - in either case, we have to |
| | 8212 | * keep track of it ourselves, because a code body might be reachable |
| | 8213 | * through multiple references (a function, for example, has a global |
| | 8214 | * symbol table entry - fixups referencing us might already have been |
| | 8215 | * created by the time we generate our code). |
| | 8216 | */ |
| | 8217 | G_cg->open_method(is_static_ ? G_cs_static : G_cs_main, |
| | 8218 | fixup_owner_sym_, fixup_list_anchor_, |
| | 8219 | this, gototab_, |
| | 8220 | argc_, varargs_, is_constructor_, self_valid_, |
| | 8221 | &gen_ctx); |
| | 8222 | |
| | 8223 | /* |
| | 8224 | * Add each local symbol table enclosing the code body's primary |
| | 8225 | * local symbol table to the frame list. The outermost code body |
| | 8226 | * table can be outside the primary code body table for situations |
| | 8227 | * such as anonymous functions. Since these tables are outside of |
| | 8228 | * any statements, we must explicitly add them to ensure that they |
| | 8229 | * are assigned debugging frame ID's and are written to the debug |
| | 8230 | * data. |
| | 8231 | */ |
| | 8232 | if (lcltab_ != 0) |
| | 8233 | { |
| | 8234 | CTcPrsSymtab *tab; |
| | 8235 | |
| | 8236 | /* add each frame outside the primary frame to the code gen list */ |
| | 8237 | for (tab = lcltab_->get_parent() ; tab != 0 ; tab = tab->get_parent()) |
| | 8238 | G_cs->set_local_frame(tab); |
| | 8239 | } |
| | 8240 | |
| | 8241 | /* the method's local symbol table is now the active symbol table */ |
| | 8242 | G_cs->set_local_frame(lcltab_); |
| | 8243 | |
| | 8244 | /* if we have a local context, initialize it */ |
| | 8245 | if (has_local_ctx_) |
| | 8246 | { |
| | 8247 | /* write code to create the new Vector to store the context locals */ |
| | 8248 | CTPNConst::s_gen_code_int(local_ctx_arr_size_); |
| | 8249 | G_cg->write_op(OPC_DUP); |
| | 8250 | G_cg->write_op(OPC_NEW1); |
| | 8251 | G_cs->write(2); |
| | 8252 | G_cs->write((char)G_cg->get_predef_meta_idx(TCT3_METAID_VECTOR)); |
| | 8253 | |
| | 8254 | /* retrieve the object value */ |
| | 8255 | G_cg->write_op(OPC_GETR0); |
| | 8256 | |
| | 8257 | /* |
| | 8258 | * we duplicated the vector size argument, then we popped it and |
| | 8259 | * pushed the object; so we have a maximum of one extra push and a |
| | 8260 | * net of zero |
| | 8261 | */ |
| | 8262 | G_cg->note_push(); |
| | 8263 | G_cg->note_pop(); |
| | 8264 | |
| | 8265 | /* store the new object in the context local variable */ |
| | 8266 | CTcSymLocal::s_gen_code_setlcl_stk(local_ctx_var_, FALSE); |
| | 8267 | |
| | 8268 | /* |
| | 8269 | * go through our symbol table, and copy each parameter that's |
| | 8270 | * also a context local into its context local slot |
| | 8271 | */ |
| | 8272 | if (lcltab_ != 0) |
| | 8273 | lcltab_->enum_entries(&enum_for_param_ctx, this); |
| | 8274 | } |
| | 8275 | |
| | 8276 | /* |
| | 8277 | * If we have a varargs-list parameter, generate the code to set up |
| | 8278 | * the list value from the actual parameters. Note that we must do |
| | 8279 | * this after we set up the local context, in case the varargs list |
| | 8280 | * parameter variable is a context local, in which case it will need |
| | 8281 | * to be stored in the context, in which case we need the context to |
| | 8282 | * be initialized first. |
| | 8283 | */ |
| | 8284 | if (varargs_list_) |
| | 8285 | { |
| | 8286 | /* generate the PUSHPARLST instruction to create the list */ |
| | 8287 | G_cg->write_op(OPC_PUSHPARLST); |
| | 8288 | G_cs->write((uchar)argc_); |
| | 8289 | |
| | 8290 | /* |
| | 8291 | * we pushed at least one value (the list); we don't know how many |
| | 8292 | * others we might have pushed, but it doesn't matter because the |
| | 8293 | * interpreter is responsible for checking for stack space |
| | 8294 | */ |
| | 8295 | G_cg->note_push(); |
| | 8296 | |
| | 8297 | /* store the list in our varargs parameter list local */ |
| | 8298 | varargs_list_local_->gen_code_setlcl(); |
| | 8299 | } |
| | 8300 | |
| | 8301 | /* |
| | 8302 | * Generate code to initialize each enclosing-context-pointer local - |
| | 8303 | * these variables allow us to find the context objects while we're |
| | 8304 | * running inside this function. |
| | 8305 | * |
| | 8306 | * We *have to* generate context level 1 last. Context level 1 does a |
| | 8307 | * set-self to re-establish the method context (if there is one), and |
| | 8308 | * once we've changed to the method context 'self', we can no longer |
| | 8309 | * access the anonymous function pointer context, which is in 'self' |
| | 8310 | * until we change it. So, we have to wait and do level 1 last, so |
| | 8311 | * that we're completely done with the anonymous function context |
| | 8312 | * before we lose it. |
| | 8313 | * |
| | 8314 | * To ensure we generate level 1 last, make two passes: in the first |
| | 8315 | * pass, generate everything except level 1; on the second pass, |
| | 8316 | * generate only level 1. This two-pass approach guarantees that level |
| | 8317 | * 1 will be the last one generated, regardless of where it appears in |
| | 8318 | * the list. (We can't just rearrange the list - not easily, at least |
| | 8319 | * - because the list is in order of function object ('self') context |
| | 8320 | * slot index.) |
| | 8321 | */ |
| | 8322 | for (int ctx_pass = 1 ; ctx_pass <= 2 ; ++ctx_pass) |
| | 8323 | { |
| | 8324 | /* loop over each context entry */ |
| | 8325 | for (ctx_idx = 0, cur_ctx = ctx_head_ ; cur_ctx != 0 ; |
| | 8326 | cur_ctx = cur_ctx->nxt_, ++ctx_idx) |
| | 8327 | { |
| | 8328 | /* |
| | 8329 | * Context level 1 *must* be generated last. If we're on pass |
| | 8330 | * 1 and this is level 1, skip it for now; if we're on pass 2, |
| | 8331 | * skip everything *except* level 1. |
| | 8332 | */ |
| | 8333 | if ((ctx_pass == 1 && cur_ctx->level_ == 1) |
| | 8334 | || (ctx_pass == 2 && cur_ctx->level_ != 1)) |
| | 8335 | continue; |
| | 8336 | |
| | 8337 | /* |
| | 8338 | * Get this context value, stored in the function object |
| | 8339 | * ('self') at index value 2+n (n=0,1,...). Note that the |
| | 8340 | * context object indices start at 2 because the code pointer |
| | 8341 | * for the function is at index 1. |
| | 8342 | */ |
| | 8343 | G_cg->write_op(OPC_PUSHSELF); |
| | 8344 | CTPNConst::s_gen_code_int(ctx_idx + 2); |
| | 8345 | G_cg->write_op(OPC_INDEX); |
| | 8346 | |
| | 8347 | /* |
| | 8348 | * we pushed the object, then popped the object and index and |
| | 8349 | * pushed the indexed value - this is a net of no change with |
| | 8350 | * one maximum push |
| | 8351 | */ |
| | 8352 | G_cg->note_push(); |
| | 8353 | G_cg->note_pop(); |
| | 8354 | |
| | 8355 | /* |
| | 8356 | * If this is context level 1, and this context has a 'self', |
| | 8357 | * and we need either 'self' or the full method context from |
| | 8358 | * the lexically enclosing scope, generate code to load the |
| | 8359 | * self or the full method context (as appropriate) from our |
| | 8360 | * local context. |
| | 8361 | * |
| | 8362 | * The enclosing method context is always stored in the context |
| | 8363 | * at level 1, because this is inherently shared context for |
| | 8364 | * all enclosed lexical scopes. We thus only have to worry |
| | 8365 | * about this for context level 1. |
| | 8366 | */ |
| | 8367 | if (cur_ctx->level_ == 1 |
| | 8368 | && self_valid_ |
| | 8369 | && (self_referenced_ || full_method_ctx_referenced_)) |
| | 8370 | { |
| | 8371 | CTPNCodeBody *outer; |
| | 8372 | |
| | 8373 | /* |
| | 8374 | * we just put our context object on the stack in |
| | 8375 | * preparation for storing it - make a duplicate copy of it |
| | 8376 | * for our own purposes |
| | 8377 | */ |
| | 8378 | G_cg->write_op(OPC_DUP); |
| | 8379 | G_cg->note_push(); |
| | 8380 | |
| | 8381 | /* get the saved method context from the context object */ |
| | 8382 | CTPNConst::s_gen_code_int(TCPRS_LOCAL_CTX_METHODCTX); |
| | 8383 | G_cg->write_op(OPC_INDEX); |
| | 8384 | |
| | 8385 | /* |
| | 8386 | * Load the context. We must check the outermost context |
| | 8387 | * to determine what it stored, because we must load |
| | 8388 | * whatever it stored. |
| | 8389 | */ |
| | 8390 | if ((outer = get_outermost_enclosing()) != 0 |
| | 8391 | && outer->local_ctx_needs_full_method_ctx()) |
| | 8392 | { |
| | 8393 | /* load the full method context */ |
| | 8394 | G_cg->write_op(OPC_LOADCTX); |
| | 8395 | } |
| | 8396 | else |
| | 8397 | { |
| | 8398 | /* load the 'self' object */ |
| | 8399 | G_cg->write_op(OPC_SETSELF); |
| | 8400 | } |
| | 8401 | |
| | 8402 | /* |
| | 8403 | * we popped two values and pushed one in the INDEX, then |
| | 8404 | * popped a value in the LOADCTX or SETSELF: the net is |
| | 8405 | * removal of two elements and no additional maximum depth |
| | 8406 | */ |
| | 8407 | G_cg->note_pop(2); |
| | 8408 | } |
| | 8409 | |
| | 8410 | /* store the context value in the appropriate local variable */ |
| | 8411 | CTcSymLocal::s_gen_code_setlcl_stk(cur_ctx->var_num_, FALSE); |
| | 8412 | |
| | 8413 | /* |
| | 8414 | * if we just did context level 1, and this is pass 2, we're |
| | 8415 | * done - pass 2's only function is to do level 1, so once we |
| | 8416 | * reach it, there's nothing left to do |
| | 8417 | */ |
| | 8418 | if (ctx_pass == 2 && cur_ctx->level_ == 1) |
| | 8419 | break; |
| | 8420 | } |
| | 8421 | } |
| | 8422 | |
| | 8423 | /* |
| | 8424 | * if we created our own local context, and we have a 'self' object, |
| | 8425 | * and we need access to the 'self' object or the full method context |
| | 8426 | * from anonymous functions that refer to the local context, generate |
| | 8427 | * code to store the appropriate data in the local context |
| | 8428 | */ |
| | 8429 | if (has_local_ctx_ && self_valid_ |
| | 8430 | && (local_ctx_needs_self_ || local_ctx_needs_full_method_ctx_)) |
| | 8431 | { |
| | 8432 | /* check to see what we need */ |
| | 8433 | if (local_ctx_needs_full_method_ctx_) |
| | 8434 | { |
| | 8435 | /* |
| | 8436 | * we need the full method context - generate code to store it |
| | 8437 | * and push a reference to it onto the stack |
| | 8438 | */ |
| | 8439 | G_cg->write_op(OPC_STORECTX); |
| | 8440 | } |
| | 8441 | else |
| | 8442 | { |
| | 8443 | /* we only need 'self' - push it */ |
| | 8444 | G_cg->write_op(OPC_PUSHSELF); |
| | 8445 | } |
| | 8446 | |
| | 8447 | /* we just pushed one value */ |
| | 8448 | G_cg->note_push(); |
| | 8449 | |
| | 8450 | /* assign the value to the context variable */ |
| | 8451 | if (local_ctx_var_ <= 255 && TCPRS_LOCAL_CTX_METHODCTX <= 255) |
| | 8452 | { |
| | 8453 | /* we can make the assignment with a single instruction */ |
| | 8454 | G_cg->write_op(OPC_SETINDLCL1I8); |
| | 8455 | G_cs->write((uchar)local_ctx_var_); |
| | 8456 | G_cs->write(TCPRS_LOCAL_CTX_METHODCTX); |
| | 8457 | |
| | 8458 | /* that pops one value */ |
| | 8459 | G_cg->note_pop(); |
| | 8460 | } |
| | 8461 | else |
| | 8462 | { |
| | 8463 | /* get the context object */ |
| | 8464 | CTcSymLocal::s_gen_code_getlcl(local_ctx_var_, FALSE); |
| | 8465 | |
| | 8466 | /* store the data in the local context object */ |
| | 8467 | CTPNConst::s_gen_code_int(TCPRS_LOCAL_CTX_METHODCTX); |
| | 8468 | G_cg->write_op(OPC_SETIND); |
| | 8469 | |
| | 8470 | /* discard the indexed result */ |
| | 8471 | G_cg->write_op(OPC_DISC); |
| | 8472 | |
| | 8473 | /* |
| | 8474 | * the SETIND pops three values and pushes one, then we pop one |
| | 8475 | * more with the DISC - this is a net three pops with no extra |
| | 8476 | * maximum depth |
| | 8477 | */ |
| | 8478 | G_cg->note_pop(3); |
| | 8479 | } |
| | 8480 | } |
| | 8481 | |
| | 8482 | /* generate the compound statement, if we have one */ |
| | 8483 | if (stm_ != 0) |
| | 8484 | stm_->gen_code(TRUE, TRUE); |
| | 8485 | |
| | 8486 | #ifdef T3_DEBUG |
| | 8487 | if (G_cg->get_sp_depth() != 0) |
| | 8488 | { |
| | 8489 | printf("---> stack depth is %d after block codegen!\n", |
| | 8490 | G_cg->get_sp_depth()); |
| | 8491 | if (fixup_owner_sym_ != 0) |
| | 8492 | printf("---> code block for %.*s\n", |
| | 8493 | (int)fixup_owner_sym_->get_sym_len(), |
| | 8494 | fixup_owner_sym_->get_sym()); |
| | 8495 | } |
| | 8496 | #endif |
| | 8497 | |
| | 8498 | /* close the method */ |
| | 8499 | G_cg->close_method(local_cnt_, end_desc_, end_linenum_, &gen_ctx); |
| | 8500 | |
| | 8501 | /* remember the head of the nested symbol table list */ |
| | 8502 | first_nested_symtab_ = G_cs->get_first_frame(); |
| | 8503 | |
| | 8504 | /* generate debug records if appropriate */ |
| | 8505 | if (G_debug) |
| | 8506 | build_debug_table(gen_ctx.method_ofs); |
| | 8507 | |
| | 8508 | /* check for unreferenced labels and issue warnings */ |
| | 8509 | check_unreferenced_labels(); |
| | 8510 | |
| | 8511 | /* show the disassembly of the code block if desired */ |
| | 8512 | if (G_disasm_out != 0) |
| | 8513 | show_disassembly(gen_ctx.method_ofs, |
| | 8514 | gen_ctx.code_start_ofs, gen_ctx.code_end_ofs); |
| | 8515 | |
| | 8516 | /* clean up globals for the end of the method */ |
| | 8517 | G_cg->close_method_cleanup(&gen_ctx); |
| | 8518 | } |
| | 8519 | |
| | 8520 | /* |
| | 8521 | * disassembly stream source implementation |
| | 8522 | */ |
| | 8523 | class CTcUnasSrcCodeBody: public CTcUnasSrc |
| | 8524 | { |
| | 8525 | public: |
| | 8526 | CTcUnasSrcCodeBody(CTcCodeStream *str, |
| | 8527 | unsigned long code_start_ofs, |
| | 8528 | unsigned long code_end_ofs) |
| | 8529 | { |
| | 8530 | /* remember the stream */ |
| | 8531 | str_ = str; |
| | 8532 | |
| | 8533 | /* start at the starting offset */ |
| | 8534 | cur_ofs_ = code_start_ofs; |
| | 8535 | |
| | 8536 | /* remember the ending offset */ |
| | 8537 | end_ofs_ = code_end_ofs; |
| | 8538 | } |
| | 8539 | |
| | 8540 | /* read the next byte */ |
| | 8541 | int next_byte(char *ch) |
| | 8542 | { |
| | 8543 | /* if there's anything left, return it */ |
| | 8544 | if (cur_ofs_ < end_ofs_) |
| | 8545 | { |
| | 8546 | /* return the next byte */ |
| | 8547 | *ch = str_->get_byte_at(cur_ofs_); |
| | 8548 | ++cur_ofs_; |
| | 8549 | return 0; |
| | 8550 | } |
| | 8551 | else |
| | 8552 | { |
| | 8553 | /* indicate end of file */ |
| | 8554 | return 1; |
| | 8555 | } |
| | 8556 | } |
| | 8557 | |
| | 8558 | /* get the current offset */ |
| | 8559 | ulong get_ofs() const { return cur_ofs_; } |
| | 8560 | |
| | 8561 | protected: |
| | 8562 | /* code stream */ |
| | 8563 | CTcCodeStream *str_; |
| | 8564 | |
| | 8565 | /* current offset */ |
| | 8566 | unsigned long cur_ofs_; |
| | 8567 | |
| | 8568 | /* offset of end of code stream */ |
| | 8569 | unsigned long end_ofs_; |
| | 8570 | }; |
| | 8571 | |
| | 8572 | /* |
| | 8573 | * Show the disassembly of this code block |
| | 8574 | */ |
| | 8575 | void CTPNCodeBody::show_disassembly(unsigned long start_ofs, |
| | 8576 | unsigned long code_start_ofs, |
| | 8577 | unsigned long code_end_ofs) |
| | 8578 | { |
| | 8579 | int argc; |
| | 8580 | int locals; |
| | 8581 | int total_stk; |
| | 8582 | unsigned exc_rel; |
| | 8583 | unsigned dbg_rel; |
| | 8584 | |
| | 8585 | /* first, dump the header */ |
| | 8586 | argc = (unsigned char)G_cs->get_byte_at(start_ofs); |
| | 8587 | locals = G_cs->readu2_at(start_ofs + 2); |
| | 8588 | total_stk = G_cs->readu2_at(start_ofs + 4); |
| | 8589 | exc_rel = G_cs->readu2_at(start_ofs + 6); |
| | 8590 | dbg_rel = G_cs->readu2_at(start_ofs + 8); |
| | 8591 | G_disasm_out->print("%8lx .code\n", start_ofs); |
| | 8592 | G_disasm_out->print(" .argcount %d%s\n", |
| | 8593 | (argc & 0x7f), |
| | 8594 | (argc & 0x80) != 0 ? "+" : ""); |
| | 8595 | G_disasm_out->print(" .locals %d\n", locals); |
| | 8596 | G_disasm_out->print(" .maxstack %d\n", total_stk); |
| | 8597 | |
| | 8598 | /* set up a code stream reader and dump the code stream */ |
| | 8599 | CTcUnasSrcCodeBody src(G_cs, code_start_ofs, code_end_ofs); |
| | 8600 | CTcT3Unasm::disasm(&src, G_disasm_out); |
| | 8601 | |
| | 8602 | /* show the exception table, if there is one */ |
| | 8603 | if (exc_rel != 0) |
| | 8604 | { |
| | 8605 | unsigned long exc_ofs; |
| | 8606 | unsigned long exc_end_ofs; |
| | 8607 | |
| | 8608 | /* get the starting address */ |
| | 8609 | exc_ofs = start_ofs + exc_rel; |
| | 8610 | |
| | 8611 | /* |
| | 8612 | * get the length - it's the part up to the debug records, or the |
| | 8613 | * part up to the current code offset if there are no debug records |
| | 8614 | */ |
| | 8615 | exc_end_ofs = (dbg_rel != 0 ? start_ofs + dbg_rel : G_cs->get_ofs()); |
| | 8616 | |
| | 8617 | /* show the table */ |
| | 8618 | G_disasm_out->print(".exceptions\n"); |
| | 8619 | CTcUnasSrcCodeBody exc_src(G_cs, exc_ofs, exc_end_ofs); |
| | 8620 | CTcT3Unasm::show_exc_table(&exc_src, G_disasm_out, start_ofs); |
| | 8621 | } |
| | 8622 | |
| | 8623 | /* add a blank line at the end */ |
| | 8624 | G_disasm_out->print("\n"); |
| | 8625 | } |
| | 8626 | |
| | 8627 | /* |
| | 8628 | * Check for unreferenced local variables |
| | 8629 | */ |
| | 8630 | void CTPNCodeBody::check_locals() |
| | 8631 | { |
| | 8632 | CTcPrsSymtab *tab; |
| | 8633 | |
| | 8634 | /* check for unreferenced locals in each nested scope */ |
| | 8635 | for (tab = first_nested_symtab_ ; tab != 0 ; tab = tab->get_list_next()) |
| | 8636 | { |
| | 8637 | /* check this table */ |
| | 8638 | tab->check_unreferenced_locals(); |
| | 8639 | } |
| | 8640 | } |
| | 8641 | |
| | 8642 | /* |
| | 8643 | * local symbol table enumerator for checking for parameter symbols that |
| | 8644 | * belong in the local context |
| | 8645 | */ |
| | 8646 | void CTPNCodeBody::enum_for_param_ctx(void *, class CTcSymbol *sym) |
| | 8647 | { |
| | 8648 | /* if this is a local, check it further */ |
| | 8649 | if (sym->get_type() == TC_SYM_LOCAL || sym->get_type() == TC_SYM_PARAM) |
| | 8650 | { |
| | 8651 | CTcSymLocal *lcl = (CTcSymLocal *)sym; |
| | 8652 | |
| | 8653 | /* |
| | 8654 | * if it's a parameter, and it's also a context variable, its |
| | 8655 | * value needs to be moved into the context |
| | 8656 | */ |
| | 8657 | if (lcl->is_param() && lcl->is_ctx_local()) |
| | 8658 | { |
| | 8659 | /* get the actual parameter value from the stack */ |
| | 8660 | CTcSymLocal::s_gen_code_getlcl(lcl->get_var_num(), TRUE); |
| | 8661 | |
| | 8662 | /* store the value in the context variable */ |
| | 8663 | lcl->gen_code_asi(TRUE, TC_ASI_SIMPLE, 0, TRUE); |
| | 8664 | } |
| | 8665 | } |
| | 8666 | } |
| | 8667 | |
| | 8668 | |
| | 8669 | /* ------------------------------------------------------------------------ */ |
| | 8670 | /* |
| | 8671 | * 'return' statement |
| | 8672 | */ |
| | 8673 | |
| | 8674 | /* |
| | 8675 | * generate code |
| | 8676 | */ |
| | 8677 | void CTPNStmReturn::gen_code(int, int) |
| | 8678 | { |
| | 8679 | int val_on_stack; |
| | 8680 | int need_gen; |
| | 8681 | |
| | 8682 | /* add a line record */ |
| | 8683 | add_debug_line_rec(); |
| | 8684 | |
| | 8685 | /* presume we'll generate a value */ |
| | 8686 | need_gen = TRUE; |
| | 8687 | val_on_stack = FALSE; |
| | 8688 | |
| | 8689 | /* generate the return value expression, if appropriate */ |
| | 8690 | if (expr_ != 0) |
| | 8691 | { |
| | 8692 | /* |
| | 8693 | * it's an error if we're in a constructor, because a |
| | 8694 | * constructor implicitly always returns 'self' |
| | 8695 | */ |
| | 8696 | if (G_cg->is_in_constructor()) |
| | 8697 | log_error(TCERR_CONSTRUCT_CANNOT_RET_VAL); |
| | 8698 | |
| | 8699 | /* check for a constant expression */ |
| | 8700 | if (expr_->is_const()) |
| | 8701 | { |
| | 8702 | switch(expr_->get_const_val()->get_type()) |
| | 8703 | { |
| | 8704 | case TC_CVT_NIL: |
| | 8705 | case TC_CVT_TRUE: |
| | 8706 | /* |
| | 8707 | * we can use special constant return instructions for |
| | 8708 | * these, so there's no need to generate the value |
| | 8709 | */ |
| | 8710 | need_gen = FALSE; |
| | 8711 | break; |
| | 8712 | |
| | 8713 | default: |
| | 8714 | /* |
| | 8715 | * other types don't have constant-return opcodes, so we |
| | 8716 | * must generate the expression code |
| | 8717 | */ |
| | 8718 | need_gen = TRUE; |
| | 8719 | break; |
| | 8720 | } |
| | 8721 | } |
| | 8722 | |
| | 8723 | /* if necessary, generate the value */ |
| | 8724 | if (need_gen) |
| | 8725 | { |
| | 8726 | int depth; |
| | 8727 | |
| | 8728 | /* note the initial stack depth */ |
| | 8729 | depth = G_cg->get_sp_depth(); |
| | 8730 | |
| | 8731 | /* |
| | 8732 | * Generate the value. We are obviously not discarding the |
| | 8733 | * value, and since returning a value is equivalent to |
| | 8734 | * assigning the value, we must use the stricter assignment |
| | 8735 | * (not 'for condition') rules for logical expressions |
| | 8736 | */ |
| | 8737 | expr_->gen_code(FALSE, FALSE); |
| | 8738 | |
| | 8739 | /* note whether we actually left a value on the stack */ |
| | 8740 | val_on_stack = (G_cg->get_sp_depth() > depth); |
| | 8741 | } |
| | 8742 | else |
| | 8743 | { |
| | 8744 | /* |
| | 8745 | * we obviously aren't leaving a value on the stack if we |
| | 8746 | * don't generate anything |
| | 8747 | */ |
| | 8748 | val_on_stack = FALSE; |
| | 8749 | } |
| | 8750 | } |
| | 8751 | |
| | 8752 | /* |
| | 8753 | * Before we return, let any enclosing statements generate any code |
| | 8754 | * necessary to leave their scope (in particular, we must invoke |
| | 8755 | * 'finally' handlers in any enclosing 'try' blocks). |
| | 8756 | * |
| | 8757 | * Note that we generated the expression BEFORE we call any |
| | 8758 | * 'finally' handlers. This is necessary because something we call |
| | 8759 | * in the course of evaluating the return value could have thrown an |
| | 8760 | * exception; if we were to call the 'finally' clauses before |
| | 8761 | * generating the return value, we could invoke the 'finally' clause |
| | 8762 | * twice (once explicitly, once in the handling of the thrown |
| | 8763 | * exception), which would be incorrect. By generating the |
| | 8764 | * 'finally' calls after the return expression, we're sure that the |
| | 8765 | * 'finally' blocks are invoked only once - either through the |
| | 8766 | * throw, or else now, after there's no more possibility of a |
| | 8767 | * 'throw' before the return. |
| | 8768 | */ |
| | 8769 | if (G_cs->get_enclosing() != 0) |
| | 8770 | { |
| | 8771 | int did_save_retval; |
| | 8772 | uint fin_ret_lcl; |
| | 8773 | |
| | 8774 | /* |
| | 8775 | * if we're going to generate any subroutine calls, and we have |
| | 8776 | * a return value on the stack, we need to save the return value |
| | 8777 | * in a local to make sure the calculated value isn't affected |
| | 8778 | * by the subroutine call |
| | 8779 | */ |
| | 8780 | if (val_on_stack |
| | 8781 | && G_cs->get_enclosing()->will_gen_code_unwind_for_return() |
| | 8782 | && G_cs->get_code_body() != 0) |
| | 8783 | { |
| | 8784 | /* allocate a local variable to save the return value */ |
| | 8785 | fin_ret_lcl = G_cs->get_code_body()->alloc_fin_ret_lcl(); |
| | 8786 | |
| | 8787 | /* save the return value in a stack temporary for a moment */ |
| | 8788 | CTcSymLocal::s_gen_code_setlcl_stk(fin_ret_lcl, FALSE); |
| | 8789 | |
| | 8790 | /* |
| | 8791 | * note that we saved the return value, so we can retrieve |
| | 8792 | * it later |
| | 8793 | */ |
| | 8794 | did_save_retval = TRUE; |
| | 8795 | } |
| | 8796 | else |
| | 8797 | { |
| | 8798 | /* note that we didn't save the return value */ |
| | 8799 | did_save_retval = FALSE; |
| | 8800 | } |
| | 8801 | |
| | 8802 | /* generate the unwind */ |
| | 8803 | G_cs->get_enclosing()->gen_code_unwind_for_return(); |
| | 8804 | |
| | 8805 | /* if we saved the return value, retrieve it */ |
| | 8806 | if (did_save_retval) |
| | 8807 | CTcSymLocal::s_gen_code_getlcl(fin_ret_lcl, FALSE); |
| | 8808 | } |
| | 8809 | |
| | 8810 | /* check for an expression to return */ |
| | 8811 | if (G_cg->is_in_constructor()) |
| | 8812 | { |
| | 8813 | /* we're in a constructor - return 'self' */ |
| | 8814 | G_cg->write_op(OPC_PUSHSELF); |
| | 8815 | G_cg->write_op(OPC_RETVAL); |
| | 8816 | } |
| | 8817 | else if (expr_ == 0) |
| | 8818 | { |
| | 8819 | /* |
| | 8820 | * there's no expression - generate a simple void return (but |
| | 8821 | * explicitly return nil, so we don't return something left in |
| | 8822 | * R0 from a previous function call we made) |
| | 8823 | */ |
| | 8824 | G_cg->write_op(OPC_RETNIL); |
| | 8825 | } |
| | 8826 | else |
| | 8827 | { |
| | 8828 | /* check for a constant expression */ |
| | 8829 | if (expr_->is_const()) |
| | 8830 | { |
| | 8831 | switch(expr_->get_const_val()->get_type()) |
| | 8832 | { |
| | 8833 | case TC_CVT_NIL: |
| | 8834 | /* generate a RETNIL instruction */ |
| | 8835 | G_cg->write_op(OPC_RETNIL); |
| | 8836 | break; |
| | 8837 | |
| | 8838 | case TC_CVT_TRUE: |
| | 8839 | /* generate a RETTRUE instruction */ |
| | 8840 | G_cg->write_op(OPC_RETTRUE); |
| | 8841 | break; |
| | 8842 | |
| | 8843 | default: |
| | 8844 | break; |
| | 8845 | } |
| | 8846 | } |
| | 8847 | |
| | 8848 | /* |
| | 8849 | * if we needed code generation to evaluate the return value, we |
| | 8850 | * now need to return the value |
| | 8851 | */ |
| | 8852 | if (need_gen) |
| | 8853 | { |
| | 8854 | /* |
| | 8855 | * Other types don't have constant-return opcodes. We |
| | 8856 | * already generated the expression value (before invoking |
| | 8857 | * the enclosing 'finally' handlers, if any), so the value |
| | 8858 | * is on the stack, and all we need to do is return it. |
| | 8859 | * |
| | 8860 | * If we didn't actually leave a value on the stack, we'll |
| | 8861 | * just return nil. |
| | 8862 | */ |
| | 8863 | if (val_on_stack) |
| | 8864 | { |
| | 8865 | /* generate the return-value opcode */ |
| | 8866 | G_cg->write_op(OPC_RETVAL); |
| | 8867 | |
| | 8868 | /* RETVAL removes an element from the stack */ |
| | 8869 | G_cg->note_pop(); |
| | 8870 | } |
| | 8871 | else |
| | 8872 | { |
| | 8873 | /* |
| | 8874 | * The depth didn't change - they must have evaluated an |
| | 8875 | * expression involving a dstring or void function. |
| | 8876 | * Return nil instead of the non-existent value. |
| | 8877 | */ |
| | 8878 | G_cg->write_op(OPC_RETNIL); |
| | 8879 | } |
| | 8880 | } |
| | 8881 | } |
| | 8882 | } |
| | 8883 | |
| | 8884 | /* ------------------------------------------------------------------------ */ |
| | 8885 | /* |
| | 8886 | * Static property initializer statement |
| | 8887 | */ |
| | 8888 | void CTPNStmStaticPropInit::gen_code(int, int) |
| | 8889 | { |
| | 8890 | int depth; |
| | 8891 | |
| | 8892 | /* add a line record */ |
| | 8893 | add_debug_line_rec(); |
| | 8894 | |
| | 8895 | /* note the initial stack depth */ |
| | 8896 | depth = G_cg->get_sp_depth(); |
| | 8897 | |
| | 8898 | /* generate the expression, keeping the generated value */ |
| | 8899 | expr_->gen_code(FALSE, FALSE); |
| | 8900 | |
| | 8901 | /* ensure that we generated a value; if we didn't, push nil by default */ |
| | 8902 | if (G_cg->get_sp_depth() <= depth) |
| | 8903 | { |
| | 8904 | /* push a default nil value */ |
| | 8905 | G_cg->write_op(OPC_PUSHNIL); |
| | 8906 | G_cg->note_push(); |
| | 8907 | } |
| | 8908 | |
| | 8909 | /* |
| | 8910 | * duplicate the value on the stack, so we can assign it to |
| | 8911 | * initialize the property and also return it |
| | 8912 | */ |
| | 8913 | G_cg->write_op(OPC_DUP); |
| | 8914 | G_cg->note_push(); |
| | 8915 | |
| | 8916 | /* write the SETPROPSELF to initialize the property */ |
| | 8917 | G_cg->write_op(OPC_SETPROPSELF); |
| | 8918 | G_cs->write_prop_id(prop_); |
| | 8919 | |
| | 8920 | /* SETPROPSELF removes the value */ |
| | 8921 | G_cg->note_pop(); |
| | 8922 | |
| | 8923 | /* return the value (which we duplicated on the stack) */ |
| | 8924 | G_cg->write_op(OPC_RETVAL); |
| | 8925 | |
| | 8926 | /* RETVAL removes the value */ |
| | 8927 | G_cg->note_pop(); |
| | 8928 | } |
| | 8929 | |
| | 8930 | |
| | 8931 | /* ------------------------------------------------------------------------ */ |
| | 8932 | /* |
| | 8933 | * Object Definition Statement |
| | 8934 | */ |
| | 8935 | |
| | 8936 | /* |
| | 8937 | * generate code |
| | 8938 | */ |
| | 8939 | void CTPNStmObject::gen_code(int, int) |
| | 8940 | { |
| | 8941 | CTPNSuperclass *sc; |
| | 8942 | CTPNObjProp *prop; |
| | 8943 | int sc_cnt; |
| | 8944 | ulong start_ofs; |
| | 8945 | uint internal_flags; |
| | 8946 | uint obj_flags; |
| | 8947 | CTcDataStream *str; |
| | 8948 | int bad_sc; |
| | 8949 | |
| | 8950 | /* if this object has been replaced, don't generate any code for it */ |
| | 8951 | if (replaced_) |
| | 8952 | return; |
| | 8953 | |
| | 8954 | /* add an implicit constructor if necessary */ |
| | 8955 | add_implicit_constructor(); |
| | 8956 | |
| | 8957 | /* get the appropriate stream for generating the data */ |
| | 8958 | str = obj_sym_->get_stream(); |
| | 8959 | |
| | 8960 | /* clear the internal flags */ |
| | 8961 | internal_flags = 0; |
| | 8962 | |
| | 8963 | /* |
| | 8964 | * if we're a modified object, set the 'modified' flag in the object |
| | 8965 | * header |
| | 8966 | */ |
| | 8967 | if (modified_) |
| | 8968 | internal_flags |= TCT3_OBJ_MODIFIED; |
| | 8969 | |
| | 8970 | /* set the 'transient' flag if appropriate */ |
| | 8971 | if (transient_) |
| | 8972 | internal_flags |= TCT3_OBJ_TRANSIENT; |
| | 8973 | |
| | 8974 | /* clear the object flags */ |
| | 8975 | obj_flags = 0; |
| | 8976 | |
| | 8977 | /* |
| | 8978 | * If we're specifically marked as a 'class' object, or we're a |
| | 8979 | * modified object, set the 'class' flag in the object flags. |
| | 8980 | */ |
| | 8981 | if (is_class_ || modified_) |
| | 8982 | obj_flags |= TCT3_OBJFLG_CLASS; |
| | 8983 | |
| | 8984 | /* remember our starting offset in the object stream */ |
| | 8985 | start_ofs = str->get_ofs(); |
| | 8986 | |
| | 8987 | /* |
| | 8988 | * store our stream offset in our defining symbol, for storage in |
| | 8989 | * the object file |
| | 8990 | */ |
| | 8991 | obj_sym_->set_stream_ofs(start_ofs); |
| | 8992 | |
| | 8993 | /* write our internal flags */ |
| | 8994 | str->write2(internal_flags); |
| | 8995 | |
| | 8996 | /* |
| | 8997 | * First, write the per-object image file "OBJS" header - each |
| | 8998 | * object starts with its object ID and the number of bytes in the |
| | 8999 | * object's metaclass-specific data. For now, write zero as a |
| | 9000 | * placeholder for our data size. Note that this is a |
| | 9001 | * self-reference: it must be modified if the object is renumbered. |
| | 9002 | */ |
| | 9003 | str->write_obj_id_selfref(obj_sym_); |
| | 9004 | str->write2(0); |
| | 9005 | |
| | 9006 | /* write a placeholder for the superclass count */ |
| | 9007 | str->write2(0); |
| | 9008 | |
| | 9009 | /* write the fixed property count */ |
| | 9010 | str->write2(prop_cnt_); |
| | 9011 | |
| | 9012 | /* write the object flags */ |
| | 9013 | str->write2(obj_flags); |
| | 9014 | |
| | 9015 | /* |
| | 9016 | * First, go through the superclass list and verify that each |
| | 9017 | * superclass is actually an object. |
| | 9018 | */ |
| | 9019 | for (bad_sc = FALSE, sc_cnt = 0, sc = first_sc_ ; sc != 0 ; sc = sc->nxt_) |
| | 9020 | { |
| | 9021 | CTcSymObj *sc_sym; |
| | 9022 | |
| | 9023 | /* look up the superclass in the global symbol table */ |
| | 9024 | sc_sym = (CTcSymObj *)sc->get_sym(); |
| | 9025 | |
| | 9026 | /* make sure it's defined, and that it's really an object */ |
| | 9027 | if (sc_sym == 0) |
| | 9028 | { |
| | 9029 | /* not defined */ |
| | 9030 | log_error(TCERR_UNDEF_SYM_SC, |
| | 9031 | (int)sc->get_sym_len(), sc->get_sym_txt(), |
| | 9032 | (int)obj_sym_->get_sym_len(), obj_sym_->get_sym()); |
| | 9033 | |
| | 9034 | /* note that we have an invalid superclass */ |
| | 9035 | bad_sc = TRUE; |
| | 9036 | } |
| | 9037 | else if (sc_sym->get_type() != TC_SYM_OBJ) |
| | 9038 | { |
| | 9039 | /* log an error */ |
| | 9040 | log_error(TCERR_SC_NOT_OBJECT, |
| | 9041 | (int)sc_sym->get_sym_len(), sc_sym->get_sym()); |
| | 9042 | |
| | 9043 | /* note that we have an invalid superclass */ |
| | 9044 | bad_sc = TRUE; |
| | 9045 | } |
| | 9046 | else |
| | 9047 | { |
| | 9048 | /* count the superclass */ |
| | 9049 | ++sc_cnt; |
| | 9050 | |
| | 9051 | /* write the superclass to the object header */ |
| | 9052 | str->write_obj_id(sc_sym->get_obj_id()); |
| | 9053 | } |
| | 9054 | } |
| | 9055 | |
| | 9056 | /* |
| | 9057 | * If we detected a 'bad template' error when we were parsing the |
| | 9058 | * object definition, and all of our superclasses are valid, report the |
| | 9059 | * template error. |
| | 9060 | * |
| | 9061 | * Do not report this error if we have any undefined or invalid |
| | 9062 | * superclasses, because (1) we've already reported one error for this |
| | 9063 | * object definition (the bad superclass error), and (2) the missing |
| | 9064 | * template is likely just a consequence of the bad superclass, since |
| | 9065 | * we can't have scanned the proper superclass's list of templates if |
| | 9066 | * they didn't tell us the correct superclass to start with. When they |
| | 9067 | * fix the superclass list and re-compile the code, it's likely that |
| | 9068 | * this will fix the template problem as well, since we'll probably be |
| | 9069 | * able to find the template give the corrected superclass list. |
| | 9070 | * |
| | 9071 | * If we found an undescribed class anywhere in our hierarchy, a |
| | 9072 | * template simply cannot be used with this object; otherwise, the |
| | 9073 | * error is that we failed to find a suitable template |
| | 9074 | */ |
| | 9075 | if (has_bad_template() && !bad_sc) |
| | 9076 | log_error(has_undesc_sc() |
| | 9077 | ? TCERR_OBJ_DEF_CANNOT_USE_TEMPLATE |
| | 9078 | : TCERR_OBJ_DEF_NO_TEMPLATE); |
| | 9079 | |
| | 9080 | /* go back and write the superclass count to the header */ |
| | 9081 | str->write2_at(start_ofs + TCT3_TADSOBJ_HEADER_OFS, sc_cnt); |
| | 9082 | |
| | 9083 | /* |
| | 9084 | * Write the properties. We're required to write the properties in |
| | 9085 | * sorted order of property ID, but we can't do that yet, because |
| | 9086 | * the property ID's aren't finalized until after linking. For now, |
| | 9087 | * just write them out in the order in which they were defined. |
| | 9088 | */ |
| | 9089 | for (prop = first_prop_ ; prop != 0 ; prop = prop->nxt_) |
| | 9090 | { |
| | 9091 | /* make sure we have a valid property symbol */ |
| | 9092 | if (prop->get_prop_sym() != 0) |
| | 9093 | { |
| | 9094 | /* write the property ID */ |
| | 9095 | str->write_prop_id(prop->get_prop_sym()->get_prop()); |
| | 9096 | |
| | 9097 | /* generate code for the property */ |
| | 9098 | prop->gen_code(FALSE, FALSE); |
| | 9099 | } |
| | 9100 | } |
| | 9101 | |
| | 9102 | /* |
| | 9103 | * go back and write the size of our metaclass-specific data - this |
| | 9104 | * goes at offset 4 in the T3 generic metaclass header |
| | 9105 | */ |
| | 9106 | str->write2_at(start_ofs + TCT3_META_HEADER_OFS + 4, |
| | 9107 | str->get_ofs() - (start_ofs + TCT3_META_HEADER_OFS + 6)); |
| | 9108 | } |
| | 9109 | |
| | 9110 | /* |
| | 9111 | * Check for unreferenced local variables |
| | 9112 | */ |
| | 9113 | void CTPNStmObject::check_locals() |
| | 9114 | { |
| | 9115 | CTPNObjProp *prop; |
| | 9116 | |
| | 9117 | /* check for unreferenced locals for each property */ |
| | 9118 | for (prop = first_prop_ ; prop != 0 ; prop = prop->nxt_) |
| | 9119 | prop->check_locals(); |
| | 9120 | } |