| 1 | /* GX target-independent functions for block translation. |
| 2 | Copyright (C) 1998 Cygnus Solutions. */ |
| 3 | |
| 4 | |
| 5 | #include "sim-main.h" |
| 6 | #include "sim-assert.h" |
| 7 | #include "sim-gx.h" |
| 8 | |
| 9 | #include "config.h" |
| 10 | |
| 11 | /* shared object functions */ |
| 12 | #ifdef HAVE_DLFCN_H |
| 13 | #include <dlfcn.h> |
| 14 | #else |
| 15 | #error "need dlfcn.h" |
| 16 | #endif |
| 17 | |
| 18 | #ifdef HAVE_ERRNO_H |
| 19 | #include <errno.h> |
| 20 | #else |
| 21 | #error "need errno.h" |
| 22 | #endif |
| 23 | |
| 24 | #include <stdlib.h> |
| 25 | #include <string.h> |
| 26 | #include <limits.h> |
| 27 | #ifdef HAVE_TIME_H |
| 28 | #include <time.h> |
| 29 | #endif |
| 30 | #ifdef HAVE_UNISTD_H |
| 31 | #include <unistd.h> |
| 32 | #endif |
| 33 | #ifdef HAVE_FCNTL_H |
| 34 | #include <fcntl.h> |
| 35 | #endif |
| 36 | #ifdef HAVE_SYS_STAT_H |
| 37 | #include <sys/stat.h> |
| 38 | #endif |
| 39 | |
| 40 | #include "bfd.h" |
| 41 | |
| 42 | |
| 43 | |
| 44 | |
| 45 | /* Load the object file with given gx block. Return pointer to GX |
| 46 | function or NULL on failure. */ |
| 47 | |
| 48 | sim_gx_function |
| 49 | sim_gx_compiled_block_f(sim_gx_compiled_block* gx) |
| 50 | { |
| 51 | sim_gx_function f = gx->function_dlhandle; |
| 52 | SIM_DESC sd = current_state; |
| 53 | static int dlopened_main = 0; |
| 54 | |
| 55 | if(f == NULL) |
| 56 | { |
| 57 | /* load object */ |
| 58 | if(gx->object_dlhandle == NULL && gx->object_name != NULL) |
| 59 | { |
| 60 | if(! dlopened_main) |
| 61 | { |
| 62 | /* dlopen executable itself first to share symbols with shared library */ |
| 63 | void* exec_handle = dlopen(NULL, RTLD_LAZY | RTLD_GLOBAL); |
| 64 | if(exec_handle == NULL) |
| 65 | { |
| 66 | sim_io_error(sd, "Load error for executable: %s", |
| 67 | dlerror()); |
| 68 | } |
| 69 | |
| 70 | dlopened_main = 1; |
| 71 | } |
| 72 | |
| 73 | /* dlopen the gx block dso itself */ |
| 74 | gx->object_dlhandle = dlopen(gx->object_name, RTLD_NOW); |
| 75 | if(gx->object_dlhandle == NULL) |
| 76 | { |
| 77 | sim_io_error(sd, "Load error for GX object %s: %s", |
| 78 | gx->object_name, |
| 79 | dlerror()); |
| 80 | } |
| 81 | } |
| 82 | |
| 83 | /* locate function */ |
| 84 | if(gx->function_dlhandle == NULL && gx->object_dlhandle != NULL && gx->symbol_name != NULL) |
| 85 | { |
| 86 | f = gx->function_dlhandle = dlsym(gx->object_dlhandle, gx->symbol_name); |
| 87 | if(f == NULL) |
| 88 | { |
| 89 | sim_io_error(sd, "Resolve error for GX object %s symbol %s: %s", |
| 90 | gx->object_name, |
| 91 | gx->symbol_name, |
| 92 | dlerror()); |
| 93 | } |
| 94 | } |
| 95 | } /* f == NULL */ |
| 96 | |
| 97 | return f; |
| 98 | } |
| 99 | |
| 100 | |
| 101 | |
| 102 | /* Forget about given GX block. Remove its source/object; unload it |
| 103 | from memory. */ |
| 104 | void |
| 105 | sim_gx_compiled_block_dispose(sim_gx_compiled_block* gx) |
| 106 | { |
| 107 | SIM_DESC sd = current_state; |
| 108 | int rc; |
| 109 | char compile_command[2000]; |
| 110 | char la_name[2000]; |
| 111 | |
| 112 | /* forget dl information */ |
| 113 | gx->function_dlhandle = NULL; |
| 114 | |
| 115 | /* unload shared library */ |
| 116 | if(gx->object_dlhandle != NULL) |
| 117 | { |
| 118 | rc = dlclose(gx->object_dlhandle); |
| 119 | if(rc != 0) |
| 120 | { |
| 121 | sim_io_error(sd, "dlclose() error for GX object %s: %s", |
| 122 | gx->object_name, |
| 123 | dlerror()); |
| 124 | } |
| 125 | gx->object_dlhandle = NULL; |
| 126 | } |
| 127 | |
| 128 | /* uninstall shared object */ |
| 129 | |
| 130 | strcpy(la_name, gx->object_name); |
| 131 | strcpy(strstr(la_name, ".so.0"), ".la"); |
| 132 | sprintf(compile_command, "gxtool --silent --mode=uninstall rm -f %s", la_name); |
| 133 | |
| 134 | rc = system(compile_command); |
| 135 | if(rc != 0) |
| 136 | { |
| 137 | sim_io_error(sd, "Error during finish: `%s' rc %d", |
| 138 | compile_command, rc); |
| 139 | } |
| 140 | |
| 141 | |
| 142 | /* erase source */ |
| 143 | /* sprintf(compile_command, "rm -f %s", block->source_name); */ |
| 144 | |
| 145 | /* final gasps */ |
| 146 | zfree(gx->source_name); |
| 147 | zfree(gx->object_name); |
| 148 | zfree(gx->symbol_name); |
| 149 | zfree(gx); |
| 150 | } |
| 151 | |
| 152 | |
| 153 | |
| 154 | /* Translate a piece of the code segment around given PC, in given mode. */ |
| 155 | sim_gx_block* |
| 156 | sim_gx_block_create(sim_cia cia) |
| 157 | { |
| 158 | sim_gx_block* block; |
| 159 | |
| 160 | /* allocate emtpy block */ |
| 161 | block = zalloc(sizeof(sim_gx_block)); |
| 162 | |
| 163 | /* initialize block bounds, callback struct etc. */ |
| 164 | tgx_block_ctor(block, cia); |
| 165 | |
| 166 | /* create learning mode translation */ |
| 167 | sim_gx_block_translate(block, 0 /* learning mode */); |
| 168 | |
| 169 | /* add block to block list */ |
| 170 | sim_gx_block_add(block); |
| 171 | |
| 172 | return block; |
| 173 | } |
| 174 | |
| 175 | |
| 176 | |
| 177 | /* Write the current block list to the state file */ |
| 178 | void |
| 179 | sim_gx_write_block_list() |
| 180 | { |
| 181 | int i; |
| 182 | SIM_DESC sd = current_state; |
| 183 | sim_gx_block_list* blocks = STATE_BLOCKS(sd); |
| 184 | FILE* f; |
| 185 | char state_file_name[PATH_MAX]; |
| 186 | char *exec_name; |
| 187 | |
| 188 | /* get base of executable name */ |
| 189 | exec_name = bfd_get_filename(STATE_PROG_BFD(sd)); |
| 190 | if(strrchr(exec_name, '/') != NULL) |
| 191 | exec_name = strrchr(exec_name, '/') + 1; |
| 192 | |
| 193 | /* generate base name */ |
| 194 | sprintf(state_file_name, "%s/%s.gx", |
| 195 | GX_DIR, |
| 196 | exec_name); |
| 197 | |
| 198 | f = fopen(state_file_name, "w"); |
| 199 | if(f == NULL) |
| 200 | { |
| 201 | sim_io_error(sd, "Error: cannot write to state file %s, errno %d", |
| 202 | state_file_name, errno); |
| 203 | } |
| 204 | |
| 205 | fprintf(f, "# This file was automatically generated. Do not edit.\n"); |
| 206 | |
| 207 | /* write block descriptors into state file */ |
| 208 | for(i=0; i<blocks->gx_blocks_used; i++) |
| 209 | { |
| 210 | sim_gx_block* gx = blocks->gx_blocks[i]; |
| 211 | sim_gx_compiled_block* block; |
| 212 | int j; |
| 213 | int age; |
| 214 | |
| 215 | age = time(NULL) - gx->learn_last_change; /* store interval */ |
| 216 | fprintf(f, "BLOCK 0x%x 0x%x %u %u\n", (unsigned)gx->origin, (unsigned)gx->length, gx->divisor, age); |
| 217 | fprintf(f, "FLAGS "); |
| 218 | for(j=0; j<GX_PC_FLAGS_INDEX(gx, gx->origin + gx->length); j++) |
| 219 | { |
| 220 | fprintf(f, "%2x ", gx->pc_flags[j]); |
| 221 | } |
| 222 | fprintf(f, "\n"); |
| 223 | |
| 224 | /* write learning mode names */ |
| 225 | block = gx->learning_block; |
| 226 | fprintf(f, "LEARNING %s %s %s %lu %u\n", |
| 227 | block->source_name, block->object_name, block->symbol_name, |
| 228 | gx->compile_time, gx->opt_compile_count); |
| 229 | |
| 230 | /* write optimized mode names */ |
| 231 | block = gx->optimized_block; |
| 232 | if(block) |
| 233 | fprintf(f, "OPTIMIZED %s %s %s\n", |
| 234 | block->source_name, block->object_name, block->symbol_name); |
| 235 | |
| 236 | /* NB: other fields will be filled in with freshly guessed values */ |
| 237 | } |
| 238 | |
| 239 | (void) fclose(f); |
| 240 | } |
| 241 | |
| 242 | |
| 243 | |
| 244 | void |
| 245 | print_gx_blocks(sim_gx_block_list* blocks, char* where) |
| 246 | { |
| 247 | printf("print_gx_blocks: %s\n", where); |
| 248 | |
| 249 | if(blocks == NULL) |
| 250 | printf("(null)\n"); |
| 251 | else |
| 252 | { |
| 253 | int i; |
| 254 | printf("size: %d, used: %d\n", |
| 255 | blocks->gx_blocks_size, blocks->gx_blocks_used); |
| 256 | |
| 257 | /* linear search */ |
| 258 | for(i=0; i<blocks->gx_blocks_used; i++) |
| 259 | { |
| 260 | sim_gx_block* gx = blocks->gx_blocks[i]; |
| 261 | printf("block %d: %p\n", i, (void*) gx); |
| 262 | if(gx == NULL) |
| 263 | printf("** NULL!\n"); |
| 264 | else |
| 265 | printf(" begin 0x%08x length 0x%08x [opt %d%s]\n", |
| 266 | (unsigned)gx->origin, (unsigned)gx->length, |
| 267 | gx->opt_compile_count, |
| 268 | (gx->optimized_block ? " loaded" : " discarded")); |
| 269 | } |
| 270 | |
| 271 | } |
| 272 | } |
| 273 | |
| 274 | |
| 275 | |
| 276 | /* Read the current block list from the cache */ |
| 277 | void |
| 278 | sim_gx_read_block_list() |
| 279 | { |
| 280 | SIM_DESC sd = current_state; |
| 281 | FILE* f; |
| 282 | char state_file_name[PATH_MAX]; |
| 283 | const char *exec_name; |
| 284 | |
| 285 | /* check for block */ |
| 286 | if(STATE_PROG_BFD(sd) == NULL) |
| 287 | return; |
| 288 | |
| 289 | /* get base of executable name */ |
| 290 | exec_name = bfd_get_filename(STATE_PROG_BFD(sd)); |
| 291 | if(strrchr(exec_name, '/') != NULL) |
| 292 | exec_name = strrchr(exec_name, '/') + 1; |
| 293 | |
| 294 | /* generate base name */ |
| 295 | sprintf(state_file_name, "%s/%s.gx", |
| 296 | GX_DIR, |
| 297 | exec_name); |
| 298 | |
| 299 | f = fopen(state_file_name, "r"); |
| 300 | if(f == NULL) |
| 301 | { |
| 302 | /* XXX: print warning */ |
| 303 | return; |
| 304 | } |
| 305 | |
| 306 | fscanf(f, "#%*[^\n]\n"); /* swallow # comment line */ |
| 307 | |
| 308 | while(1) |
| 309 | { |
| 310 | unsigned_4 origin, length; |
| 311 | unsigned divisor; |
| 312 | sim_gx_block* gx; |
| 313 | int rc; |
| 314 | sim_gx_compiled_block* block; |
| 315 | unsigned age; |
| 316 | int j; |
| 317 | |
| 318 | rc = fscanf(f, "BLOCK 0x%0x 0x%x %u %u\n", (unsigned*)& origin, (unsigned*)& length, & divisor, & age); |
| 319 | if(rc != 4) /* not all fields matched - assume EOF */ |
| 320 | break; |
| 321 | |
| 322 | gx = zalloc(sizeof(sim_gx_block)); |
| 323 | |
| 324 | /* initialize block bounds, callback struct etc. */ |
| 325 | tgx_block_ctor2(gx, origin, length, divisor); |
| 326 | |
| 327 | /* read flags */ |
| 328 | fscanf(f, "FLAGS"); |
| 329 | for(j=0; j<GX_PC_FLAGS_INDEX(gx, gx->origin + gx->length); j++) |
| 330 | { |
| 331 | unsigned value; |
| 332 | fscanf(f, "%2x ", & value); |
| 333 | gx->pc_flags[j] = (unsigned_1) value; |
| 334 | } |
| 335 | fscanf(f, "\n"); |
| 336 | |
| 337 | /* read learning mode info */ |
| 338 | block = zalloc(sizeof(sim_gx_compiled_block)); |
| 339 | gx->learning_block = block; |
| 340 | block->source_name = zalloc(PATH_MAX); |
| 341 | block->object_name = zalloc(PATH_MAX); |
| 342 | block->symbol_name = zalloc(PATH_MAX); |
| 343 | fscanf(f, "LEARNING %s %s %s %u %u\n", |
| 344 | block->source_name, block->object_name, block->symbol_name, |
| 345 | (unsigned*) & gx->compile_time, & gx->opt_compile_count); |
| 346 | |
| 347 | /* read optimized mode info */ |
| 348 | block = zalloc(sizeof(sim_gx_compiled_block)); |
| 349 | gx->optimized_block = block; |
| 350 | block->source_name = zalloc(PATH_MAX); |
| 351 | block->object_name = zalloc(PATH_MAX); |
| 352 | block->symbol_name = zalloc(PATH_MAX); |
| 353 | rc = fscanf(f, "OPTIMIZED %s %s %s\n", |
| 354 | block->source_name, block->object_name, block->symbol_name); |
| 355 | if(rc != 3) |
| 356 | { |
| 357 | /* oops, not an optimized block */ |
| 358 | zfree(block->source_name); |
| 359 | zfree(block->object_name); |
| 360 | zfree(block->symbol_name); |
| 361 | zfree(block); |
| 362 | gx->optimized_block = NULL; |
| 363 | } |
| 364 | |
| 365 | /* fill in remaining fields */ |
| 366 | gx->learn_last_change = time(NULL) - age; /* make absolute */ |
| 367 | |
| 368 | /* store it away */ |
| 369 | sim_gx_block_add(gx); |
| 370 | } |
| 371 | |
| 372 | /* print_gx_blocks(STATE_BLOCKS(sd), "after restoring state"); */ |
| 373 | } |
| 374 | |
| 375 | |
| 376 | |
| 377 | |
| 378 | |
| 379 | |
| 380 | /* Add a gx block to list */ |
| 381 | void |
| 382 | sim_gx_block_add(sim_gx_block* block) |
| 383 | { |
| 384 | SIM_DESC sd = current_state; |
| 385 | sim_gx_block_list* blocks = STATE_BLOCKS(sd); |
| 386 | int i; |
| 387 | |
| 388 | /* print_gx_blocks(blocks, "pre add"); */ |
| 389 | |
| 390 | if(blocks == NULL) |
| 391 | blocks = STATE_BLOCKS(sd) = zalloc(sizeof(sim_gx_block_list)); |
| 392 | |
| 393 | /* need to enlarge block vector? */ |
| 394 | if(blocks->gx_blocks_used == blocks->gx_blocks_size) |
| 395 | { |
| 396 | sim_gx_block** new_blocks; |
| 397 | int j; |
| 398 | |
| 399 | blocks->gx_blocks_size += 20; |
| 400 | new_blocks = zalloc(blocks->gx_blocks_size * sizeof(sim_gx_block*)); |
| 401 | for(j=0; j<blocks->gx_blocks_used; j++) |
| 402 | new_blocks[j] = blocks->gx_blocks[j]; |
| 403 | if(blocks->gx_blocks) zfree(blocks->gx_blocks); |
| 404 | blocks->gx_blocks = new_blocks; |
| 405 | } |
| 406 | |
| 407 | /* insert new block */ |
| 408 | for(i=0; i<blocks->gx_blocks_used; i++) |
| 409 | { |
| 410 | ASSERT(blocks->gx_blocks[i] != NULL); |
| 411 | |
| 412 | /* insertion point reached? */ |
| 413 | if(blocks->gx_blocks[i]->origin > block->origin) |
| 414 | { |
| 415 | int j; |
| 416 | for(j=blocks->gx_blocks_used; j>=i; j--) |
| 417 | blocks->gx_blocks[j] = blocks->gx_blocks[j-1]; |
| 418 | blocks->gx_blocks[i] = block; |
| 419 | blocks->gx_blocks_used ++; |
| 420 | break; |
| 421 | } |
| 422 | } |
| 423 | |
| 424 | /* end of block vector */ |
| 425 | if(i == blocks->gx_blocks_used) |
| 426 | { |
| 427 | blocks->gx_blocks[blocks->gx_blocks_used ++] = block; |
| 428 | } |
| 429 | |
| 430 | /* print_gx_blocks(blocks, "post add"); */ |
| 431 | } |
| 432 | |
| 433 | |
| 434 | |
| 435 | /* Remove a gx block from list */ |
| 436 | void |
| 437 | sim_gx_block_remove(sim_gx_block* block) |
| 438 | { |
| 439 | SIM_DESC sd = current_state; |
| 440 | sim_gx_block_list* blocks = STATE_BLOCKS(sd); |
| 441 | int i; |
| 442 | |
| 443 | /* print_gx_blocks(blocks, "pre remove"); */ |
| 444 | |
| 445 | /* linear search */ |
| 446 | for(i=0; i<blocks->gx_blocks_used; i++) |
| 447 | { |
| 448 | if(blocks->gx_blocks[i] == block) |
| 449 | { |
| 450 | /* found it */ |
| 451 | while(i < blocks->gx_blocks_used - 1) |
| 452 | { |
| 453 | blocks->gx_blocks[i] = blocks->gx_blocks[i+1]; |
| 454 | i++; |
| 455 | } |
| 456 | blocks->gx_blocks_used --; |
| 457 | break; |
| 458 | } |
| 459 | } |
| 460 | |
| 461 | /* print_gx_blocks(blocks, "post remove"); */ |
| 462 | } |
| 463 | |
| 464 | |
| 465 | /* Find a gx block from list */ |
| 466 | sim_gx_block* |
| 467 | sim_gx_block_find(sim_cia cia) |
| 468 | { |
| 469 | SIM_DESC sd = current_state; |
| 470 | sim_gx_block_list* blocks = STATE_BLOCKS(sd); |
| 471 | int i; |
| 472 | |
| 473 | if(blocks == NULL) return NULL; |
| 474 | |
| 475 | /* print_gx_blocks(blocks, "pre find"); */ |
| 476 | |
| 477 | /* linear search */ |
| 478 | for(i=0; i<blocks->gx_blocks_used; i++) |
| 479 | { |
| 480 | sim_gx_block* gx = blocks->gx_blocks[i]; |
| 481 | ASSERT(gx != NULL); |
| 482 | |
| 483 | if(GX_PC_INCLUDES(gx,cia)) |
| 484 | { |
| 485 | return gx; |
| 486 | } |
| 487 | } |
| 488 | |
| 489 | return NULL; |
| 490 | } |
| 491 | |
| 492 | |
| 493 | |
| 494 | /* generate */ |
| 495 | void |
| 496 | sim_gx_block_translate(sim_gx_block* gx, int optimized) |
| 497 | { |
| 498 | char pwd_name[PATH_MAX]; |
| 499 | char dir_name[PATH_MAX]; |
| 500 | char base_name[PATH_MAX]; |
| 501 | char compile_command[PATH_MAX*4]; |
| 502 | const char* exec_name; |
| 503 | SIM_DESC sd = current_state; |
| 504 | int rc; |
| 505 | sim_cia gx_cia; |
| 506 | sim_gx_compiled_block* block = zalloc(sizeof(sim_gx_compiled_block)); |
| 507 | unsigned time_begin, time_end; |
| 508 | |
| 509 | time_begin = time(NULL); |
| 510 | |
| 511 | if(optimized) gx->optimized_block = block; |
| 512 | else gx->learning_block = block; |
| 513 | |
| 514 | /* get base of executable name */ |
| 515 | exec_name = bfd_get_filename(STATE_PROG_BFD(sd)); |
| 516 | if(strrchr(exec_name, '/') != NULL) |
| 517 | exec_name = strrchr(exec_name, '/') + 1; |
| 518 | |
| 519 | /* generate base name */ |
| 520 | sprintf(dir_name, "%s/%s", |
| 521 | GX_DIR, |
| 522 | exec_name); |
| 523 | |
| 524 | /* generate base name */ |
| 525 | getcwd(pwd_name, sizeof(pwd_name)); |
| 526 | |
| 527 | /* create work directory */ |
| 528 | rc = mkdir(GX_DIR, 0777); |
| 529 | if(rc != 0 && |
| 530 | errno != EEXIST) |
| 531 | { |
| 532 | sim_io_error(sd, "Error: cannot create directory %s, errno %d", |
| 533 | GX_DIR, errno); |
| 534 | } |
| 535 | |
| 536 | rc = mkdir(dir_name, 0777); |
| 537 | if(rc != 0 && |
| 538 | errno != EEXIST) |
| 539 | { |
| 540 | sim_io_error(sd, "Error: cannot create directory %s, errno %d", |
| 541 | dir_name, errno); |
| 542 | } |
| 543 | |
| 544 | /* compute base name */ |
| 545 | if(optimized) |
| 546 | sprintf(base_name, "%08x_opt%d", (unsigned) gx->origin, gx->opt_compile_count); |
| 547 | else |
| 548 | sprintf(base_name, "%08x", (unsigned) gx->origin); |
| 549 | |
| 550 | /* generate source/object file names */ |
| 551 | block->source_name = zalloc(PATH_MAX); |
| 552 | block->object_name = zalloc(PATH_MAX); |
| 553 | sprintf(block->source_name, "%s/%s.c", dir_name, base_name); |
| 554 | |
| 555 | /* generate symbol name for gx function */ |
| 556 | block->symbol_name = zalloc(PATH_MAX); |
| 557 | sprintf(block->symbol_name, "gx_%s", base_name); |
| 558 | |
| 559 | /* open source file */ |
| 560 | block->source_file = fopen(block->source_name, "w"); |
| 561 | if(block->source_file == NULL) |
| 562 | { |
| 563 | sim_io_error(sd, "Error: cannot open file %s, errno %d", |
| 564 | block->source_name, errno); |
| 565 | } |
| 566 | |
| 567 | /* front matter */ |
| 568 | fprintf(block->source_file, "/* sim-gx version %d */\n", GX_VERSION); |
| 569 | fprintf(block->source_file, "/* gx block date stamp %lu */\n\n", time(NULL)); |
| 570 | |
| 571 | /* emit head end of source */ |
| 572 | tgx_emit_pre_function(gx, optimized); |
| 573 | |
| 574 | /* emit function header */ |
| 575 | fprintf(block->source_file, "\n\n"); |
| 576 | fprintf(block->source_file, "extern int\n"); |
| 577 | fprintf(block->source_file, "%s", block->symbol_name); |
| 578 | fprintf(block->source_file, "(struct tgx_info* info)\n"); |
| 579 | fprintf(block->source_file, "{\n"); |
| 580 | fprintf(block->source_file, " int rc = 0;\n"); |
| 581 | if(! optimized) |
| 582 | fprintf(block->source_file, " unsigned int insn_count = 0;\n"); |
| 583 | |
| 584 | /* emit threaded goto vector for __GNUC__ */ |
| 585 | fprintf(block->source_file, "#ifdef __GNUC__\n"); |
| 586 | fprintf(block->source_file, " static void* jump_table[] =\n"); |
| 587 | fprintf(block->source_file, " {\n"); |
| 588 | gx_cia = gx->origin; |
| 589 | while(GX_PC_INCLUDES(gx,gx_cia)) |
| 590 | { |
| 591 | sim_cia next_gx_cia; |
| 592 | if((! optimized) || |
| 593 | (GX_PC_FLAGS(gx, gx_cia) & GX_PCF_JUMPTARGET)) |
| 594 | { |
| 595 | fprintf(block->source_file, " && gx_label_%ld,\n", |
| 596 | ((gx_cia - gx->origin) / gx->divisor)); |
| 597 | } |
| 598 | else |
| 599 | { |
| 600 | fprintf(block->source_file, " && gx_label_default,\n"); |
| 601 | } |
| 602 | gx_cia = gx_cia + gx->divisor; |
| 603 | } |
| 604 | fprintf(block->source_file, " };\n"); |
| 605 | fprintf(block->source_file, "#endif /*__GNUC__*/\n"); |
| 606 | |
| 607 | /* pre-block gunk: register load */ |
| 608 | tgx_emit_load_block(gx, optimized); |
| 609 | |
| 610 | /* emit intra-block jump label */ |
| 611 | fprintf(block->source_file, "\n"); |
| 612 | fprintf(block->source_file, "shortjump:\n"); |
| 613 | fprintf(block->source_file, " pc = npc;\n"); |
| 614 | |
| 615 | /* translate jumptarget table */ |
| 616 | if(! optimized) |
| 617 | { |
| 618 | fprintf(block->source_file, " pc_flags[(pc - 0x%08x) / %u] |= %d;\n", |
| 619 | (unsigned)gx->origin, gx->divisor, GX_PCF_JUMPTARGET); |
| 620 | } |
| 621 | |
| 622 | /* enforce learning mode run limit */ |
| 623 | if(! optimized) |
| 624 | { |
| 625 | fprintf(block->source_file, " insn_count++;\n"); |
| 626 | fprintf(block->source_file, " if (insn_count > %d)\n", GX_LEARN_RUN_LIMIT); |
| 627 | fprintf(block->source_file, " {\n"); |
| 628 | fprintf(block->source_file, " rc = %d;\n", GX_F_YIELD); |
| 629 | fprintf(block->source_file, " npc = pc;\n"); |
| 630 | fprintf(block->source_file, " goto save;\n"); |
| 631 | fprintf(block->source_file, " }\n"); |
| 632 | } |
| 633 | |
| 634 | /* emit PC switch, use compressed case numbers */ |
| 635 | fprintf(block->source_file, "\n"); |
| 636 | fprintf(block->source_file, "#ifdef __GNUC__\n"); |
| 637 | fprintf(block->source_file, " goto * jump_table[((pc - 0x%08x) / %u)];\n", |
| 638 | (unsigned)gx->origin, gx->divisor); |
| 639 | fprintf(block->source_file, "#else /* ! __GNUC__*/\n"); |
| 640 | fprintf(block->source_file, " switch((pc - 0x%08x) / %u)\n", |
| 641 | (unsigned)gx->origin, gx->divisor); |
| 642 | fprintf(block->source_file, "#endif /*__GNUC__*/\n"); |
| 643 | fprintf(block->source_file, " {\n"); |
| 644 | |
| 645 | /* handle bad-PC event */ |
| 646 | fprintf(block->source_file, " /* handle unknown jump target */\n"); |
| 647 | fprintf(block->source_file, "#ifdef __GNUC__\n"); |
| 648 | fprintf(block->source_file, " gx_label_default:\n"); |
| 649 | fprintf(block->source_file, "#else /* ! __GNUC__*/\n"); |
| 650 | fprintf(block->source_file, " default:\n"); |
| 651 | fprintf(block->source_file, "#endif /*__GNUC__*/\n"); |
| 652 | fprintf(block->source_file, " pc_flags[%d] |= %d;\n", |
| 653 | GX_PC_FLAGS_INDEX(gx, gx_cia), GX_PCF_INSTRUCTION); |
| 654 | fprintf(block->source_file, " rc = %d;\n", GX_F_NONPC); |
| 655 | fprintf(block->source_file, " npc = pc;\n"); |
| 656 | fprintf(block->source_file, " goto save;\n"); |
| 657 | |
| 658 | /* start translating at the origin */ |
| 659 | gx_cia = gx->origin; |
| 660 | |
| 661 | /* translate instructions in block */ |
| 662 | while(GX_PC_INCLUDES(gx,gx_cia)) |
| 663 | { |
| 664 | sim_cia next_gx_cia; |
| 665 | |
| 666 | /* translate PC case statement */ |
| 667 | fprintf(block->source_file, "\n"); |
| 668 | fprintf(block->source_file, " /* PC: 0x%08x, flags %02x */\n", |
| 669 | gx_cia, (int) GX_PC_FLAGS(gx, gx_cia)); |
| 670 | |
| 671 | |
| 672 | /* skip over this instruction if it is not executed */ |
| 673 | if(optimized && !(GX_PC_FLAGS(gx, gx_cia) & GX_PCF_INSTRUCTION)) |
| 674 | { |
| 675 | fprintf(block->source_file, " /* (not reached) */\n"); |
| 676 | |
| 677 | /* prevent fall-through from previous translated insn */ |
| 678 | if(gx_cia > gx->origin && |
| 679 | GX_PC_FLAGS(gx, (gx_cia - gx->divisor)) & GX_PCF_INSTRUCTION) |
| 680 | { |
| 681 | fprintf(block->source_file, " /* prevent fall-through */\n"); |
| 682 | fprintf(block->source_file, " npc = 0x%08x;\n", gx_cia); |
| 683 | fprintf(block->source_file, " pc_flags[%d] |= %d;\n", |
| 684 | GX_PC_FLAGS_INDEX(gx, gx_cia), GX_PCF_INSTRUCTION); |
| 685 | fprintf(block->source_file, " rc = %d;\n", GX_F_NONPC); |
| 686 | fprintf(block->source_file, " goto save;\n"); |
| 687 | } |
| 688 | |
| 689 | next_gx_cia = gx_cia + gx->divisor; |
| 690 | goto skip_instruction; |
| 691 | } |
| 692 | |
| 693 | /* translate PC case statement */ |
| 694 | if((! optimized) || |
| 695 | (GX_PC_FLAGS(gx, gx_cia) & GX_PCF_JUMPTARGET)) |
| 696 | { |
| 697 | fprintf(block->source_file, " gx_label_%d:\n", |
| 698 | ((gx_cia - gx->origin) / gx->divisor)); |
| 699 | fprintf(block->source_file, "#ifndef __GNUC__\n"); |
| 700 | fprintf(block->source_file, " case %d:\n", |
| 701 | ((gx_cia - gx->origin) / gx->divisor)); |
| 702 | fprintf(block->source_file, "#endif /* !__GNUC__ */\n"); |
| 703 | } |
| 704 | |
| 705 | /* translate breakpoint check & exit */ |
| 706 | if(GX_PC_FLAGS(gx, gx_cia) & GX_PCF_COND_HALT) |
| 707 | { |
| 708 | fprintf(block->source_file, " if(pc_flags[%d] & %d)\n", |
| 709 | GX_PC_FLAGS_INDEX(gx, gx_cia), |
| 710 | GX_PCF_HALT); |
| 711 | fprintf(block->source_file, " {\n"); |
| 712 | fprintf(block->source_file, " rc = %d;\n", GX_F_HALT); |
| 713 | fprintf(block->source_file, " npc = pc;\n"); |
| 714 | fprintf(block->source_file, " goto save;\n"); |
| 715 | fprintf(block->source_file, " }\n"); |
| 716 | } |
| 717 | |
| 718 | /* [don't] emit PC-setting */ |
| 719 | /* fprintf(block->source_file, " pc = 0x%08x;\n", gx_cia); */ |
| 720 | |
| 721 | /* mark traversed instructions */ |
| 722 | if(! optimized) |
| 723 | { |
| 724 | fprintf(block->source_file, " pc_flags[%d] |= %d;\n", |
| 725 | GX_PC_FLAGS_INDEX(gx, gx_cia), |
| 726 | GX_PCF_INSTRUCTION); |
| 727 | } |
| 728 | |
| 729 | |
| 730 | /* translate instruction semantics */ |
| 731 | next_gx_cia = tgx_emit_insn(gx, gx_cia, optimized); |
| 732 | |
| 733 | skip_instruction: |
| 734 | |
| 735 | /* go to next instruction */ |
| 736 | gx_cia = next_gx_cia; |
| 737 | } |
| 738 | fprintf(block->source_file, " }\n"); |
| 739 | |
| 740 | /* dropped through last instruction in switch block */ |
| 741 | fprintf(block->source_file, "\n"); |
| 742 | fprintf(block->source_file, " /* dropped through PC switch */\n"); |
| 743 | fprintf(block->source_file, " npc = 0x%08x;\n", gx_cia); |
| 744 | fprintf(block->source_file, " rc = %d;\n", GX_F_RANGE); |
| 745 | fprintf(block->source_file, " goto save;\n"); |
| 746 | |
| 747 | /* unknown length jump */ |
| 748 | fprintf(block->source_file, "\n"); |
| 749 | fprintf(block->source_file, "unknownjump:\n"); |
| 750 | fprintf(block->source_file, " if(npc >= 0x%08x && npc < 0x%08x)\n", |
| 751 | (unsigned)gx->origin, (unsigned)gx->origin + gx->length); |
| 752 | fprintf(block->source_file, " goto shortjump;\n"); |
| 753 | |
| 754 | /* long jump */ |
| 755 | fprintf(block->source_file, "\n"); |
| 756 | fprintf(block->source_file, "longjump:\n"); |
| 757 | fprintf(block->source_file, " rc = %d;\n", GX_F_RANGE); |
| 758 | |
| 759 | /* post-block gunk: SAVE etc. */ |
| 760 | fprintf(block->source_file, "\n"); |
| 761 | fprintf(block->source_file, "save:\n"); |
| 762 | |
| 763 | tgx_emit_save_block(gx, optimized); |
| 764 | |
| 765 | /* emit tail end of function */ |
| 766 | fprintf(block->source_file, "\n"); |
| 767 | fprintf(block->source_file, " return rc;\n"); |
| 768 | fprintf(block->source_file, "}\n"); |
| 769 | |
| 770 | /* emit tail end of source */ |
| 771 | tgx_emit_post_function(gx, optimized); |
| 772 | |
| 773 | /* close source file */ |
| 774 | fclose(block->source_file); |
| 775 | block->source_file = NULL; |
| 776 | |
| 777 | /* compile source & produce shared object */ |
| 778 | |
| 779 | { |
| 780 | char* extra_flags = NULL; |
| 781 | #ifdef HAVE_GETENV |
| 782 | extra_flags = getenv("GX_FLAGS"); |
| 783 | #endif |
| 784 | if (extra_flags == NULL) extra_flags = ""; |
| 785 | |
| 786 | sprintf(compile_command, |
| 787 | "gxtool --silent --mode=compile gcc %s %s -c %s", |
| 788 | (optimized ? "-O9 -fomit-frame-pointer" : "-O"), |
| 789 | extra_flags, |
| 790 | block->source_name); |
| 791 | |
| 792 | rc = system(compile_command); |
| 793 | if(rc != 0) |
| 794 | { |
| 795 | sim_io_error(sd, "Error during compiling: `%s' rc %d", |
| 796 | compile_command, rc); |
| 797 | } |
| 798 | |
| 799 | /* link source */ |
| 800 | |
| 801 | sprintf(compile_command, |
| 802 | "gxtool --silent --mode=link gcc -export-dynamic -rpath %s %s -o lib%s.la %s.lo", |
| 803 | dir_name, extra_flags, base_name, base_name); |
| 804 | |
| 805 | rc = system(compile_command); |
| 806 | if(rc != 0) |
| 807 | { |
| 808 | sim_io_error(sd, "Error during linking: `%s' rc %d", |
| 809 | compile_command, rc); |
| 810 | } |
| 811 | } |
| 812 | |
| 813 | /* install */ |
| 814 | |
| 815 | sprintf(compile_command, |
| 816 | "gxtool --silent --mode=install cp lib%s.la %s/%s >/dev/null 2>/dev/null", |
| 817 | base_name, pwd_name, dir_name); |
| 818 | |
| 819 | rc = system(compile_command); |
| 820 | if(rc != 0) |
| 821 | { |
| 822 | sim_io_error(sd, "Error during install: `%s' rc %d", |
| 823 | compile_command, rc); |
| 824 | } |
| 825 | |
| 826 | |
| 827 | /* finish */ |
| 828 | |
| 829 | sprintf(compile_command, |
| 830 | "gxtool --silent --mode=finish %s >/dev/null 2>/dev/null", |
| 831 | dir_name); |
| 832 | |
| 833 | rc = system(compile_command); |
| 834 | if(rc != 0) |
| 835 | { |
| 836 | sim_io_error(sd, "Error during finish: `%s' rc %d", |
| 837 | compile_command, rc); |
| 838 | } |
| 839 | |
| 840 | /* clean up */ |
| 841 | |
| 842 | sprintf(compile_command, "gxtool --silent --mode=uninstall rm -f lib%s.la %s.lo", base_name, base_name); |
| 843 | rc = system(compile_command); |
| 844 | if(rc != 0) |
| 845 | { |
| 846 | sim_io_error(sd, "Error during cleanup: `%s' rc %d", |
| 847 | compile_command, rc); |
| 848 | } |
| 849 | |
| 850 | /* XXX: FILL IN block->object_name from .la file */ |
| 851 | sprintf(block->object_name, "%s/%s/lib%s.so.0", |
| 852 | pwd_name, dir_name, base_name); |
| 853 | |
| 854 | /* measure compile time */ |
| 855 | time_end = time(NULL); |
| 856 | |
| 857 | if(time_end == time_begin) time_end ++; /* clamp minimum duration to 1 */ |
| 858 | gx->compile_time += time_end - time_begin; |
| 859 | /* fprintf(stderr, "*** compile time: %d\n", gx->compile_time); */ |
| 860 | } |
| 861 | |