/* GX target-independent functions for block translation. Copyright (C) 1998 Cygnus Solutions. */ #include "sim-main.h" #include "sim-assert.h" #include "sim-gx.h" #include "config.h" /* shared object functions */ #ifdef HAVE_DLFCN_H #include #else #error "need dlfcn.h" #endif #ifdef HAVE_ERRNO_H #include #else #error "need errno.h" #endif #include #include #include #ifdef HAVE_TIME_H #include #endif #ifdef HAVE_UNISTD_H #include #endif #ifdef HAVE_FCNTL_H #include #endif #ifdef HAVE_SYS_STAT_H #include #endif #include "bfd.h" /* Load the object file with given gx block. Return pointer to GX function or NULL on failure. */ sim_gx_function sim_gx_compiled_block_f(sim_gx_compiled_block* gx) { sim_gx_function f = gx->function_dlhandle; SIM_DESC sd = current_state; static int dlopened_main = 0; if(f == NULL) { /* load object */ if(gx->object_dlhandle == NULL && gx->object_name != NULL) { if(! dlopened_main) { /* dlopen executable itself first to share symbols with shared library */ void* exec_handle = dlopen(NULL, RTLD_LAZY | RTLD_GLOBAL); if(exec_handle == NULL) { sim_io_error(sd, "Load error for executable: %s", dlerror()); } dlopened_main = 1; } /* dlopen the gx block dso itself */ gx->object_dlhandle = dlopen(gx->object_name, RTLD_NOW); if(gx->object_dlhandle == NULL) { sim_io_error(sd, "Load error for GX object %s: %s", gx->object_name, dlerror()); } } /* locate function */ if(gx->function_dlhandle == NULL && gx->object_dlhandle != NULL && gx->symbol_name != NULL) { f = gx->function_dlhandle = dlsym(gx->object_dlhandle, gx->symbol_name); if(f == NULL) { sim_io_error(sd, "Resolve error for GX object %s symbol %s: %s", gx->object_name, gx->symbol_name, dlerror()); } } } /* f == NULL */ return f; } /* Forget about given GX block. Remove its source/object; unload it from memory. */ void sim_gx_compiled_block_dispose(sim_gx_compiled_block* gx) { SIM_DESC sd = current_state; int rc; char compile_command[2000]; char la_name[2000]; /* forget dl information */ gx->function_dlhandle = NULL; /* unload shared library */ if(gx->object_dlhandle != NULL) { rc = dlclose(gx->object_dlhandle); if(rc != 0) { sim_io_error(sd, "dlclose() error for GX object %s: %s", gx->object_name, dlerror()); } gx->object_dlhandle = NULL; } /* uninstall shared object */ strcpy(la_name, gx->object_name); strcpy(strstr(la_name, ".so.0"), ".la"); sprintf(compile_command, "gxtool --silent --mode=uninstall rm -f %s", la_name); rc = system(compile_command); if(rc != 0) { sim_io_error(sd, "Error during finish: `%s' rc %d", compile_command, rc); } /* erase source */ /* sprintf(compile_command, "rm -f %s", block->source_name); */ /* final gasps */ zfree(gx->source_name); zfree(gx->object_name); zfree(gx->symbol_name); zfree(gx); } /* Translate a piece of the code segment around given PC, in given mode. */ sim_gx_block* sim_gx_block_create(sim_cia cia) { sim_gx_block* block; /* allocate emtpy block */ block = zalloc(sizeof(sim_gx_block)); /* initialize block bounds, callback struct etc. */ tgx_block_ctor(block, cia); /* create learning mode translation */ sim_gx_block_translate(block, 0 /* learning mode */); /* add block to block list */ sim_gx_block_add(block); return block; } /* Write the current block list to the state file */ void sim_gx_write_block_list() { int i; SIM_DESC sd = current_state; sim_gx_block_list* blocks = STATE_BLOCKS(sd); FILE* f; char state_file_name[PATH_MAX]; char *exec_name; /* get base of executable name */ exec_name = bfd_get_filename(STATE_PROG_BFD(sd)); if(strrchr(exec_name, '/') != NULL) exec_name = strrchr(exec_name, '/') + 1; /* generate base name */ sprintf(state_file_name, "%s/%s.gx", GX_DIR, exec_name); f = fopen(state_file_name, "w"); if(f == NULL) { sim_io_error(sd, "Error: cannot write to state file %s, errno %d", state_file_name, errno); } fprintf(f, "# This file was automatically generated. Do not edit.\n"); /* write block descriptors into state file */ for(i=0; igx_blocks_used; i++) { sim_gx_block* gx = blocks->gx_blocks[i]; sim_gx_compiled_block* block; int j; int age; age = time(NULL) - gx->learn_last_change; /* store interval */ fprintf(f, "BLOCK 0x%x 0x%x %u %u\n", (unsigned)gx->origin, (unsigned)gx->length, gx->divisor, age); fprintf(f, "FLAGS "); for(j=0; jorigin + gx->length); j++) { fprintf(f, "%2x ", gx->pc_flags[j]); } fprintf(f, "\n"); /* write learning mode names */ block = gx->learning_block; fprintf(f, "LEARNING %s %s %s %lu %u\n", block->source_name, block->object_name, block->symbol_name, gx->compile_time, gx->opt_compile_count); /* write optimized mode names */ block = gx->optimized_block; if(block) fprintf(f, "OPTIMIZED %s %s %s\n", block->source_name, block->object_name, block->symbol_name); /* NB: other fields will be filled in with freshly guessed values */ } (void) fclose(f); } void print_gx_blocks(sim_gx_block_list* blocks, char* where) { printf("print_gx_blocks: %s\n", where); if(blocks == NULL) printf("(null)\n"); else { int i; printf("size: %d, used: %d\n", blocks->gx_blocks_size, blocks->gx_blocks_used); /* linear search */ for(i=0; igx_blocks_used; i++) { sim_gx_block* gx = blocks->gx_blocks[i]; printf("block %d: %p\n", i, (void*) gx); if(gx == NULL) printf("** NULL!\n"); else printf(" begin 0x%08x length 0x%08x [opt %d%s]\n", (unsigned)gx->origin, (unsigned)gx->length, gx->opt_compile_count, (gx->optimized_block ? " loaded" : " discarded")); } } } /* Read the current block list from the cache */ void sim_gx_read_block_list() { SIM_DESC sd = current_state; FILE* f; char state_file_name[PATH_MAX]; const char *exec_name; /* check for block */ if(STATE_PROG_BFD(sd) == NULL) return; /* get base of executable name */ exec_name = bfd_get_filename(STATE_PROG_BFD(sd)); if(strrchr(exec_name, '/') != NULL) exec_name = strrchr(exec_name, '/') + 1; /* generate base name */ sprintf(state_file_name, "%s/%s.gx", GX_DIR, exec_name); f = fopen(state_file_name, "r"); if(f == NULL) { /* XXX: print warning */ return; } fscanf(f, "#%*[^\n]\n"); /* swallow # comment line */ while(1) { unsigned_4 origin, length; unsigned divisor; sim_gx_block* gx; int rc; sim_gx_compiled_block* block; unsigned age; int j; rc = fscanf(f, "BLOCK 0x%0x 0x%x %u %u\n", (unsigned*)& origin, (unsigned*)& length, & divisor, & age); if(rc != 4) /* not all fields matched - assume EOF */ break; gx = zalloc(sizeof(sim_gx_block)); /* initialize block bounds, callback struct etc. */ tgx_block_ctor2(gx, origin, length, divisor); /* read flags */ fscanf(f, "FLAGS"); for(j=0; jorigin + gx->length); j++) { unsigned value; fscanf(f, "%2x ", & value); gx->pc_flags[j] = (unsigned_1) value; } fscanf(f, "\n"); /* read learning mode info */ block = zalloc(sizeof(sim_gx_compiled_block)); gx->learning_block = block; block->source_name = zalloc(PATH_MAX); block->object_name = zalloc(PATH_MAX); block->symbol_name = zalloc(PATH_MAX); fscanf(f, "LEARNING %s %s %s %u %u\n", block->source_name, block->object_name, block->symbol_name, (unsigned*) & gx->compile_time, & gx->opt_compile_count); /* read optimized mode info */ block = zalloc(sizeof(sim_gx_compiled_block)); gx->optimized_block = block; block->source_name = zalloc(PATH_MAX); block->object_name = zalloc(PATH_MAX); block->symbol_name = zalloc(PATH_MAX); rc = fscanf(f, "OPTIMIZED %s %s %s\n", block->source_name, block->object_name, block->symbol_name); if(rc != 3) { /* oops, not an optimized block */ zfree(block->source_name); zfree(block->object_name); zfree(block->symbol_name); zfree(block); gx->optimized_block = NULL; } /* fill in remaining fields */ gx->learn_last_change = time(NULL) - age; /* make absolute */ /* store it away */ sim_gx_block_add(gx); } /* print_gx_blocks(STATE_BLOCKS(sd), "after restoring state"); */ } /* Add a gx block to list */ void sim_gx_block_add(sim_gx_block* block) { SIM_DESC sd = current_state; sim_gx_block_list* blocks = STATE_BLOCKS(sd); int i; /* print_gx_blocks(blocks, "pre add"); */ if(blocks == NULL) blocks = STATE_BLOCKS(sd) = zalloc(sizeof(sim_gx_block_list)); /* need to enlarge block vector? */ if(blocks->gx_blocks_used == blocks->gx_blocks_size) { sim_gx_block** new_blocks; int j; blocks->gx_blocks_size += 20; new_blocks = zalloc(blocks->gx_blocks_size * sizeof(sim_gx_block*)); for(j=0; jgx_blocks_used; j++) new_blocks[j] = blocks->gx_blocks[j]; if(blocks->gx_blocks) zfree(blocks->gx_blocks); blocks->gx_blocks = new_blocks; } /* insert new block */ for(i=0; igx_blocks_used; i++) { ASSERT(blocks->gx_blocks[i] != NULL); /* insertion point reached? */ if(blocks->gx_blocks[i]->origin > block->origin) { int j; for(j=blocks->gx_blocks_used; j>=i; j--) blocks->gx_blocks[j] = blocks->gx_blocks[j-1]; blocks->gx_blocks[i] = block; blocks->gx_blocks_used ++; break; } } /* end of block vector */ if(i == blocks->gx_blocks_used) { blocks->gx_blocks[blocks->gx_blocks_used ++] = block; } /* print_gx_blocks(blocks, "post add"); */ } /* Remove a gx block from list */ void sim_gx_block_remove(sim_gx_block* block) { SIM_DESC sd = current_state; sim_gx_block_list* blocks = STATE_BLOCKS(sd); int i; /* print_gx_blocks(blocks, "pre remove"); */ /* linear search */ for(i=0; igx_blocks_used; i++) { if(blocks->gx_blocks[i] == block) { /* found it */ while(i < blocks->gx_blocks_used - 1) { blocks->gx_blocks[i] = blocks->gx_blocks[i+1]; i++; } blocks->gx_blocks_used --; break; } } /* print_gx_blocks(blocks, "post remove"); */ } /* Find a gx block from list */ sim_gx_block* sim_gx_block_find(sim_cia cia) { SIM_DESC sd = current_state; sim_gx_block_list* blocks = STATE_BLOCKS(sd); int i; if(blocks == NULL) return NULL; /* print_gx_blocks(blocks, "pre find"); */ /* linear search */ for(i=0; igx_blocks_used; i++) { sim_gx_block* gx = blocks->gx_blocks[i]; ASSERT(gx != NULL); if(GX_PC_INCLUDES(gx,cia)) { return gx; } } return NULL; } /* generate */ void sim_gx_block_translate(sim_gx_block* gx, int optimized) { char pwd_name[PATH_MAX]; char dir_name[PATH_MAX]; char base_name[PATH_MAX]; char compile_command[PATH_MAX*4]; const char* exec_name; SIM_DESC sd = current_state; int rc; sim_cia gx_cia; sim_gx_compiled_block* block = zalloc(sizeof(sim_gx_compiled_block)); unsigned time_begin, time_end; time_begin = time(NULL); if(optimized) gx->optimized_block = block; else gx->learning_block = block; /* get base of executable name */ exec_name = bfd_get_filename(STATE_PROG_BFD(sd)); if(strrchr(exec_name, '/') != NULL) exec_name = strrchr(exec_name, '/') + 1; /* generate base name */ sprintf(dir_name, "%s/%s", GX_DIR, exec_name); /* generate base name */ getcwd(pwd_name, sizeof(pwd_name)); /* create work directory */ rc = mkdir(GX_DIR, 0777); if(rc != 0 && errno != EEXIST) { sim_io_error(sd, "Error: cannot create directory %s, errno %d", GX_DIR, errno); } rc = mkdir(dir_name, 0777); if(rc != 0 && errno != EEXIST) { sim_io_error(sd, "Error: cannot create directory %s, errno %d", dir_name, errno); } /* compute base name */ if(optimized) sprintf(base_name, "%08x_opt%d", (unsigned) gx->origin, gx->opt_compile_count); else sprintf(base_name, "%08x", (unsigned) gx->origin); /* generate source/object file names */ block->source_name = zalloc(PATH_MAX); block->object_name = zalloc(PATH_MAX); sprintf(block->source_name, "%s/%s.c", dir_name, base_name); /* generate symbol name for gx function */ block->symbol_name = zalloc(PATH_MAX); sprintf(block->symbol_name, "gx_%s", base_name); /* open source file */ block->source_file = fopen(block->source_name, "w"); if(block->source_file == NULL) { sim_io_error(sd, "Error: cannot open file %s, errno %d", block->source_name, errno); } /* front matter */ fprintf(block->source_file, "/* sim-gx version %d */\n", GX_VERSION); fprintf(block->source_file, "/* gx block date stamp %lu */\n\n", time(NULL)); /* emit head end of source */ tgx_emit_pre_function(gx, optimized); /* emit function header */ fprintf(block->source_file, "\n\n"); fprintf(block->source_file, "extern int\n"); fprintf(block->source_file, "%s", block->symbol_name); fprintf(block->source_file, "(struct tgx_info* info)\n"); fprintf(block->source_file, "{\n"); fprintf(block->source_file, " int rc = 0;\n"); if(! optimized) fprintf(block->source_file, " unsigned int insn_count = 0;\n"); /* emit threaded goto vector for __GNUC__ */ fprintf(block->source_file, "#ifdef __GNUC__\n"); fprintf(block->source_file, " static void* jump_table[] =\n"); fprintf(block->source_file, " {\n"); gx_cia = gx->origin; while(GX_PC_INCLUDES(gx,gx_cia)) { sim_cia next_gx_cia; if((! optimized) || (GX_PC_FLAGS(gx, gx_cia) & GX_PCF_JUMPTARGET)) { fprintf(block->source_file, " && gx_label_%ld,\n", ((gx_cia - gx->origin) / gx->divisor)); } else { fprintf(block->source_file, " && gx_label_default,\n"); } gx_cia = gx_cia + gx->divisor; } fprintf(block->source_file, " };\n"); fprintf(block->source_file, "#endif /*__GNUC__*/\n"); /* pre-block gunk: register load */ tgx_emit_load_block(gx, optimized); /* emit intra-block jump label */ fprintf(block->source_file, "\n"); fprintf(block->source_file, "shortjump:\n"); fprintf(block->source_file, " pc = npc;\n"); /* translate jumptarget table */ if(! optimized) { fprintf(block->source_file, " pc_flags[(pc - 0x%08x) / %u] |= %d;\n", (unsigned)gx->origin, gx->divisor, GX_PCF_JUMPTARGET); } /* enforce learning mode run limit */ if(! optimized) { fprintf(block->source_file, " insn_count++;\n"); fprintf(block->source_file, " if (insn_count > %d)\n", GX_LEARN_RUN_LIMIT); fprintf(block->source_file, " {\n"); fprintf(block->source_file, " rc = %d;\n", GX_F_YIELD); fprintf(block->source_file, " npc = pc;\n"); fprintf(block->source_file, " goto save;\n"); fprintf(block->source_file, " }\n"); } /* emit PC switch, use compressed case numbers */ fprintf(block->source_file, "\n"); fprintf(block->source_file, "#ifdef __GNUC__\n"); fprintf(block->source_file, " goto * jump_table[((pc - 0x%08x) / %u)];\n", (unsigned)gx->origin, gx->divisor); fprintf(block->source_file, "#else /* ! __GNUC__*/\n"); fprintf(block->source_file, " switch((pc - 0x%08x) / %u)\n", (unsigned)gx->origin, gx->divisor); fprintf(block->source_file, "#endif /*__GNUC__*/\n"); fprintf(block->source_file, " {\n"); /* handle bad-PC event */ fprintf(block->source_file, " /* handle unknown jump target */\n"); fprintf(block->source_file, "#ifdef __GNUC__\n"); fprintf(block->source_file, " gx_label_default:\n"); fprintf(block->source_file, "#else /* ! __GNUC__*/\n"); fprintf(block->source_file, " default:\n"); fprintf(block->source_file, "#endif /*__GNUC__*/\n"); fprintf(block->source_file, " pc_flags[%d] |= %d;\n", GX_PC_FLAGS_INDEX(gx, gx_cia), GX_PCF_INSTRUCTION); fprintf(block->source_file, " rc = %d;\n", GX_F_NONPC); fprintf(block->source_file, " npc = pc;\n"); fprintf(block->source_file, " goto save;\n"); /* start translating at the origin */ gx_cia = gx->origin; /* translate instructions in block */ while(GX_PC_INCLUDES(gx,gx_cia)) { sim_cia next_gx_cia; /* translate PC case statement */ fprintf(block->source_file, "\n"); fprintf(block->source_file, " /* PC: 0x%08x, flags %02x */\n", gx_cia, (int) GX_PC_FLAGS(gx, gx_cia)); /* skip over this instruction if it is not executed */ if(optimized && !(GX_PC_FLAGS(gx, gx_cia) & GX_PCF_INSTRUCTION)) { fprintf(block->source_file, " /* (not reached) */\n"); /* prevent fall-through from previous translated insn */ if(gx_cia > gx->origin && GX_PC_FLAGS(gx, (gx_cia - gx->divisor)) & GX_PCF_INSTRUCTION) { fprintf(block->source_file, " /* prevent fall-through */\n"); fprintf(block->source_file, " npc = 0x%08x;\n", gx_cia); fprintf(block->source_file, " pc_flags[%d] |= %d;\n", GX_PC_FLAGS_INDEX(gx, gx_cia), GX_PCF_INSTRUCTION); fprintf(block->source_file, " rc = %d;\n", GX_F_NONPC); fprintf(block->source_file, " goto save;\n"); } next_gx_cia = gx_cia + gx->divisor; goto skip_instruction; } /* translate PC case statement */ if((! optimized) || (GX_PC_FLAGS(gx, gx_cia) & GX_PCF_JUMPTARGET)) { fprintf(block->source_file, " gx_label_%d:\n", ((gx_cia - gx->origin) / gx->divisor)); fprintf(block->source_file, "#ifndef __GNUC__\n"); fprintf(block->source_file, " case %d:\n", ((gx_cia - gx->origin) / gx->divisor)); fprintf(block->source_file, "#endif /* !__GNUC__ */\n"); } /* translate breakpoint check & exit */ if(GX_PC_FLAGS(gx, gx_cia) & GX_PCF_COND_HALT) { fprintf(block->source_file, " if(pc_flags[%d] & %d)\n", GX_PC_FLAGS_INDEX(gx, gx_cia), GX_PCF_HALT); fprintf(block->source_file, " {\n"); fprintf(block->source_file, " rc = %d;\n", GX_F_HALT); fprintf(block->source_file, " npc = pc;\n"); fprintf(block->source_file, " goto save;\n"); fprintf(block->source_file, " }\n"); } /* [don't] emit PC-setting */ /* fprintf(block->source_file, " pc = 0x%08x;\n", gx_cia); */ /* mark traversed instructions */ if(! optimized) { fprintf(block->source_file, " pc_flags[%d] |= %d;\n", GX_PC_FLAGS_INDEX(gx, gx_cia), GX_PCF_INSTRUCTION); } /* translate instruction semantics */ next_gx_cia = tgx_emit_insn(gx, gx_cia, optimized); skip_instruction: /* go to next instruction */ gx_cia = next_gx_cia; } fprintf(block->source_file, " }\n"); /* dropped through last instruction in switch block */ fprintf(block->source_file, "\n"); fprintf(block->source_file, " /* dropped through PC switch */\n"); fprintf(block->source_file, " npc = 0x%08x;\n", gx_cia); fprintf(block->source_file, " rc = %d;\n", GX_F_RANGE); fprintf(block->source_file, " goto save;\n"); /* unknown length jump */ fprintf(block->source_file, "\n"); fprintf(block->source_file, "unknownjump:\n"); fprintf(block->source_file, " if(npc >= 0x%08x && npc < 0x%08x)\n", (unsigned)gx->origin, (unsigned)gx->origin + gx->length); fprintf(block->source_file, " goto shortjump;\n"); /* long jump */ fprintf(block->source_file, "\n"); fprintf(block->source_file, "longjump:\n"); fprintf(block->source_file, " rc = %d;\n", GX_F_RANGE); /* post-block gunk: SAVE etc. */ fprintf(block->source_file, "\n"); fprintf(block->source_file, "save:\n"); tgx_emit_save_block(gx, optimized); /* emit tail end of function */ fprintf(block->source_file, "\n"); fprintf(block->source_file, " return rc;\n"); fprintf(block->source_file, "}\n"); /* emit tail end of source */ tgx_emit_post_function(gx, optimized); /* close source file */ fclose(block->source_file); block->source_file = NULL; /* compile source & produce shared object */ { char* extra_flags = NULL; #ifdef HAVE_GETENV extra_flags = getenv("GX_FLAGS"); #endif if (extra_flags == NULL) extra_flags = ""; sprintf(compile_command, "gxtool --silent --mode=compile gcc %s %s -c %s", (optimized ? "-O9 -fomit-frame-pointer" : "-O"), extra_flags, block->source_name); rc = system(compile_command); if(rc != 0) { sim_io_error(sd, "Error during compiling: `%s' rc %d", compile_command, rc); } /* link source */ sprintf(compile_command, "gxtool --silent --mode=link gcc -export-dynamic -rpath %s %s -o lib%s.la %s.lo", dir_name, extra_flags, base_name, base_name); rc = system(compile_command); if(rc != 0) { sim_io_error(sd, "Error during linking: `%s' rc %d", compile_command, rc); } } /* install */ sprintf(compile_command, "gxtool --silent --mode=install cp lib%s.la %s/%s >/dev/null 2>/dev/null", base_name, pwd_name, dir_name); rc = system(compile_command); if(rc != 0) { sim_io_error(sd, "Error during install: `%s' rc %d", compile_command, rc); } /* finish */ sprintf(compile_command, "gxtool --silent --mode=finish %s >/dev/null 2>/dev/null", dir_name); rc = system(compile_command); if(rc != 0) { sim_io_error(sd, "Error during finish: `%s' rc %d", compile_command, rc); } /* clean up */ sprintf(compile_command, "gxtool --silent --mode=uninstall rm -f lib%s.la %s.lo", base_name, base_name); rc = system(compile_command); if(rc != 0) { sim_io_error(sd, "Error during cleanup: `%s' rc %d", compile_command, rc); } /* XXX: FILL IN block->object_name from .la file */ sprintf(block->object_name, "%s/%s/lib%s.so.0", pwd_name, dir_name, base_name); /* measure compile time */ time_end = time(NULL); if(time_end == time_begin) time_end ++; /* clamp minimum duration to 1 */ gx->compile_time += time_end - time_begin; /* fprintf(stderr, "*** compile time: %d\n", gx->compile_time); */ }