diff options
author | Frank Ch. Eigler <fche@redhat.com> | 1998-11-14 04:35:47 +0000 |
---|---|---|
committer | Frank Ch. Eigler <fche@redhat.com> | 1998-11-14 04:35:47 +0000 |
commit | 92fa45795dc1b8427de0c1e310bbe1bd52d4bd05 (patch) | |
tree | b9e8e3d84758508a6d59901dca64974cecafdda7 /sim/common | |
parent | fca5abc13a6d7df76b3ecef15c31a77973fb35e2 (diff) | |
download | gdb-92fa45795dc1b8427de0c1e310bbe1bd52d4bd05.zip gdb-92fa45795dc1b8427de0c1e310bbe1bd52d4bd05.tar.gz gdb-92fa45795dc1b8427de0c1e310bbe1bd52d4bd05.tar.bz2 |
* Personal prototype "gx" translation-based JIT engine for M32R.
[ChangeLog]
start-sanitize-gxsim
1998-11-13 Frank Ch. Eigler <fche@elastic.org>
* configure.in: Added "--enable-sim-gx" option.
* configure: Regenerated.
end-sanitize-gxsim
[common/ChangeLog]
1998-11-13 Frank Ch. Eigler <fche@elastic.org>
start-sanitize-gxsim
* Make-common.im: Build sim-gx.o and sim-gx-run.o.
* sim-gx.c: New file: target-independent gx routines.
* sim-gx.h: Declarations for gx structs and routines.
* sim-gx-run.c: New file: target-independent gx driver.
* sim-base.h: Add gx block vector to state struct.
end-sanitize-gxsim
* aclocal.m4: Add tests for dlopen family.
Diffstat (limited to 'sim/common')
-rw-r--r-- | sim/common/.Sanitize | 35 | ||||
-rw-r--r-- | sim/common/ChangeLog | 12 | ||||
-rw-r--r-- | sim/common/Make-common.in | 15 | ||||
-rw-r--r-- | sim/common/sim-gx-run.c | 148 | ||||
-rw-r--r-- | sim/common/sim-gx.c | 807 | ||||
-rw-r--r-- | sim/common/sim-gx.h | 142 |
6 files changed, 1159 insertions, 0 deletions
diff --git a/sim/common/.Sanitize b/sim/common/.Sanitize index b468d63..e6b50e3 100644 --- a/sim/common/.Sanitize +++ b/sim/common/.Sanitize @@ -22,6 +22,13 @@ else lose_these_too="${cygnus_files} ${lose_these_too}" fi +gxsim_files="sim-gx-run.c sim-gx.c sim-gx.h" +if ( echo $* | grep keep\-gxsim > /dev/null ) ; then + keep_these_too="${gxsim_files} ${keep_these_too}" +else + lose_these_too="${gxsim_files} ${lose_these_too}" +fi + # All files listed between the "Things-to-keep:" line and the # "Files-to-sed:" line will be kept. All other files will be removed. # Directories listed in this section will have their own Sanitize @@ -179,4 +186,32 @@ else done fi +gxsim_files="ChangeLog Make-common.in sim-base.h" +if ( echo $* | grep keep\-gxsim > /dev/null ) ; then + for i in $gxsim_files ; do + if test ! -d $i && (grep sanitize-gxsim $i > /dev/null) ; then + if [ -n "${verbose}" ] ; then + echo Keeping gxsim stuff in $i + fi + fi + done +else + for i in $gxsim_files ; do + if test ! -d $i && (grep sanitize-gxsim $i > /dev/null) ; then + if [ -n "${verbose}" ] ; then + echo Removing traces of \"gxsim\" from $i... + fi + cp $i new + sed '/start\-sanitize\-gxsim/,/end-\sanitize\-gxsim/d' < $i > new + if [ -n "${safe}" -a ! -f .Recover/$i ] ; then + if [ -n "${verbose}" ] ; then + echo Caching $i in .Recover... + fi + mv $i .Recover + fi + mv new $i + fi + done +fi + # End of file. diff --git a/sim/common/ChangeLog b/sim/common/ChangeLog index b7e6f2e..8a334a2 100644 --- a/sim/common/ChangeLog +++ b/sim/common/ChangeLog @@ -1,3 +1,15 @@ +1998-11-13 Frank Ch. Eigler <fche@elastic.org> + +start-sanitize-gxsim + * Make-common.im: Build sim-gx.o and sim-gx-run.o. + * sim-gx.c: New file: target-independent gx routines. + * sim-gx.h: Declarations for gx structs and routines. + * sim-gx-run.c: New file: target-independent gx driver. + * sim-base.h: Add gx block vector to state struct. +end-sanitize-gxsim + * aclocal.m4: Add tests for dlopen family. + * config.in: Regenerated. + Wed Nov 11 14:02:25 1998 Doug Evans <devans@canuck.cygnus.com> * sim-hload.c (sim_load): Pass `prog_name' to sim_load_file, not NULL. diff --git a/sim/common/Make-common.in b/sim/common/Make-common.in index 809160b..3622bec 100644 --- a/sim/common/Make-common.in +++ b/sim/common/Make-common.in @@ -318,6 +318,9 @@ sim-n-core_h = $(srccom)/sim-n-core.h sim-engine_h = $(srccom)/sim-engine.h sim-events_h = $(srccom)/sim-events.h sim-fpu_h = $(srccom)/sim-fpu.h +# start-sanitize-gxsim +sim-gx_h = $(srccom)/sim-gx.h +# end-sanitize-gxsim sim-io_h = $(srccom)/sim-io.h sim-options_h = $(srccom)/sim-options.h sim-break_h = $(srccom)/sim-break.h @@ -383,6 +386,18 @@ sim-fpu.o: $(srccom)/sim-fpu.c $(sim-fpu_h) \ $(SIM_EXTRA_DEPS) $(CC) -c $(srccom)/sim-fpu.c $(ALL_CFLAGS) +# start-sanitize-gxsim +sim-gx.o: $(srccom)/sim-gx.c $(sim_main_headers) \ + $(sim-gx_h) \ + $(SIM_EXTRA_DEPS) + $(CC) -c $(srccom)/sim-gx.c $(ALL_CFLAGS) + +sim-gx-run.o: $(srccom)/sim-gx-run.c $(sim_main_headers) \ + $(sim-gx_h) \ + $(SIM_EXTRA_DEPS) + $(CC) -c $(srccom)/sim-gx-run.c $(ALL_CFLAGS) +# end-sanitize-gxsim + sim-hload.o: $(srccom)/sim-hload.c $(sim-assert_h) \ $(srcroot)/include/remote-sim.h \ $(SIM_EXTRA_DEPS) diff --git a/sim/common/sim-gx-run.c b/sim/common/sim-gx-run.c new file mode 100644 index 0000000..a4d8865 --- /dev/null +++ b/sim/common/sim-gx-run.c @@ -0,0 +1,148 @@ +/* GX generic simulator run. + Copyright (C) 1998 Cygnus Solutions. +*/ + +#include "sim-main.h" +#include "sim-assert.h" +#include "sim-gx.h" + +#ifdef HAVE_TIME_H +#include <time.h> +#endif + + +/* GX implementation of sim_engine_run that works within the + sim_engine setjmp/longjmp framework. */ + + +void +sim_engine_run (SIM_DESC sd, + int next_cpu_nr, + int nr_cpus, /* ignore */ + int siggnal) /* ignore */ +{ + sim_cpu* cpu; + int cont = 1; + int rc; + + SIM_ASSERT (STATE_MAGIC (sd) == SIM_MAGIC_NUMBER); + cpu = STATE_CPU (sd, next_cpu_nr); + + while(cont) + { + sim_gx_block* block; + sim_gx_compiled_block* compiled_block; + sim_gx_function f; + sim_cia cia = CIA_GET(cpu); + int optimized; + int pre_checksum, post_checksum; + + /* find optimized gx block that includes this PC */ + block = sim_gx_block_find(cia); + if(block == NULL) + { + /* start new learning block */ + block = sim_gx_block_create(cia); + } + ASSERT(block != NULL); + + /* pick preferred compiled block */ + if(block->optimized_block != NULL) + { + compiled_block = block->optimized_block; + /* no stats */ + } + else + { + /* test for optimization policy */ + if(tgx_optimize_test(block)) + { + block->opt_compile_count ++; + sim_gx_block_translate(block, 1 /* optimized */); + compiled_block = block->optimized_block; + optimized = 1; + } + else + { + compiled_block = block->learning_block; + optimized = 0; + } + } + ASSERT(compiled_block != NULL); + + /* load & resolve gx function */ + f = sim_gx_compiled_block_f(compiled_block); + + /* XXX: debug + printf("calling into gx function %p, pc=%08lx, opt %d\n", + (void*) f, (unsigned long) cpu->regs.h_pc, optimized); + */ + + /* compute pc_flags checksum */ + if(! optimized) + { + int i; + pre_checksum = 0; + for(i=0; i < block->length / block->divisor; i++) + pre_checksum += block->pc_flags[i]; + } + + /* call into gx function */ + rc = (*f)(& cpu->regs, block->pc_flags, block->callbacks); + + /* compute pc_flags checksum */ + if(! optimized) + { + int i; + post_checksum = 0; + for(i=0; i < block->length / block->divisor; i++) + post_checksum += block->pc_flags[i]; + + if(post_checksum != pre_checksum) /* system changing */ + { + block->learn_last_change = time(NULL); + } + } + + /* XXX: debug + printf("returned from gx function %p, rc=%d, pc=%08lx\n", + (void*) f, rc, (unsigned long) cpu->regs.h_pc); + */ + + switch(rc) + { + case GX_F_YIELD: /* gx block voluntarily gave up control */ + case GX_F_RANGE: /* PC travelled outside this block */ + ; /* continue block dispatch loop */ + break; + + case GX_F_NONPC: /* non-instruction PC in this block */ + if(compiled_block == block->optimized_block) + { + /* sim_io_printf(sd, "NOTE: cancelling premature optimization, GX block %p, PC %08lx\n", + block, (long) cpu->regs.h_pc); */ + sim_gx_compiled_block_dispose(compiled_block); + block->learn_last_change = time(NULL); + block->optimized_block = NULL; + } + else + { + /* learning-mode gx block should not fail this way */ + sim_io_error(sd, "Internal error - GX block cia %08lx NONPC\n", (long) cia); + } + break; + + case GX_F_HALT: /* gx function returning control */ + cont = 0; /* merely exit loop */ + break; + + /* should not happen */ + default: + sim_io_error(sd, "Translation error (bad rc 0x%d in gx block)", rc); + /* NOTREACHED */ + } + + if(sim_events_tick(sd)) + sim_events_process(sd); + } +} diff --git a/sim/common/sim-gx.c b/sim/common/sim-gx.c new file mode 100644 index 0000000..413739d --- /dev/null +++ b/sim/common/sim-gx.c @@ -0,0 +1,807 @@ +/* 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" +#include "cconfig.h" + +/* shared object functions */ +#ifdef HAVE_DLFCN_H +#include <dlfcn.h> +#else +#error "need dlfcn.h" +#endif + +#ifdef HAVE_ERRNO_H +#include <errno.h> +#else +#error "need errno.h" +#endif + +#include <stdlib.h> +#include <string.h> +#include <limits.h> +#ifdef HAVE_TIME_H +#include <time.h> +#endif +#ifdef HAVE_UNISTD_H +#include <unistd.h> +#endif +#ifdef HAVE_FCNTL_H +#include <fcntl.h> +#endif +#ifdef HAVE_SYS_STAT_H +#include <sys/stat.h> +#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; + int rc; + + if(f == NULL) + { + /* compile object */ + if(gx->object_name == NULL && gx->source_name != NULL) + { + char compile_command[2000]; + + gx->object_name = strdup(gx->source_name); + /* turn *.c into *.o */ + gx->object_name[strlen(gx->object_name)]='o'; + + /* compute command string to compile object */ + sprintf(compile_command, + "make -f %s OBJ=%s SRC=%s gx", +#define GX_MAKEFILE "--no-makefile-yet--" + GX_MAKEFILE, + gx->object_name, + gx->source_name); + + rc = system(compile_command); + if(rc != 0) + { + sim_io_error(sd, "Compile error rc %d for GX source %s: %s", + rc, + gx->source_name, + strerror(errno)); + } + } + + /* load object */ + if(gx->object_dlhandle == NULL && gx->object_name != NULL) + { + 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; + + /* 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; + } + + /* 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; i<blocks->gx_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%lx 0x%lx %u %u\n", gx->origin, gx->length, gx->divisor, age); + fprintf(f, "FLAGS "); + for(j=0; j<GX_PC_FLAGS_INDEX(gx, gx->origin + 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; i<blocks->gx_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]; + 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%0lx 0x%lx %u %u\n", & origin, & 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; j<GX_PC_FLAGS_INDEX(gx, gx->origin + 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 %lu %u\n", + block->source_name, block->object_name, block->symbol_name, + & 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; j<blocks->gx_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; i<blocks->gx_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; i<blocks->gx_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; i<blocks->gx_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]; + 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, "%08lx_opt%d", gx->origin, gx->opt_compile_count); + else + sprintf(base_name, "%08lx", 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_cpu_regs* regs, char* pc_flags, struct tgx_callbacks* callbacks)\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"); + + /* 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, " switch((pc - 0x%08x) / %u)\n", + (unsigned)gx->origin, gx->divisor); + fprintf(block->source_file, " {\n"); + + /* handle bad-PC event */ + fprintf(block->source_file, " /* handle unknown jump target */\n"); + fprintf(block->source_file, " default:\n"); + 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, " 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, " case %ld:\n", + ((gx_cia - gx->origin) / gx->divisor)); + } + + /* translate breakpoint check & exit */ + if(GX_PC_FLAGS(gx, gx_cia) & GX_PCF_COND_HALT) + { + fprintf(block->source_file, " if(pc_flags[%ld] & %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[%ld] |= %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%08lx && npc < 0x%08lx)\n", + gx->origin, 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 */ + + sprintf(compile_command, + "gxtool --silent --mode=compile gcc -c -g %s %s", + (optimized ? "-O3" : "-O"), 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 -g -o lib%s.la %s.lo", + dir_name, 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, "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); */ +} + diff --git a/sim/common/sim-gx.h b/sim/common/sim-gx.h new file mode 100644 index 0000000..b0d0673 --- /dev/null +++ b/sim/common/sim-gx.h @@ -0,0 +1,142 @@ +/* GX generic simulator structs. + Copyright (C) 1998 Cygnus Solutions. +*/ + +#ifndef SIM_GX_H +#define SIM_GX_H + +#include <stdio.h> +#include "sim-base.h" +#include "sim-core.h" + + +/* +#ifndef SIM_GX +#error "Compile this file only if configured SIM_GX" +#endif +*/ + + +/* configuration */ + +#define GX_DIR ".gx" +#define GX_VERSION 1 + + +struct sim_gx_compiled_block; + + +/* record for a particular GX block */ +typedef struct sim_gx_block +{ + /* ---- BLOCK EXTENT ---- */ + address_word origin; /* first code-segment address translated */ + unsigned_4 length; /* length of translated code-segment */ + +#define GX_PC_INCLUDES(gx,pc) ((gx)->origin <= (pc) && (pc) < (gx)->origin + (gx)->length) + + short divisor; /* minimum instruction word size; address_word -> index divisor */ + char* pc_flags; /* see GX_PC_* below */ + +#define GX_PCF_INSTRUCTION 0x01 /* learned */ +#define GX_PCF_JUMPTARGET 0x02 /* learned */ +#define GX_PCF_COND_HALT 0x10 /* translate-time input */ +#define GX_PCF_HALT 0x20 /* run-time input */ +#define GX_PC_FLAGS_INDEX(gx,pc) ((((pc) - ((gx)->origin)) / (gx)->divisor)) +#define GX_PC_FLAGS(gx,pc) ((gx)->pc_flags[GX_PC_FLAGS_INDEX((gx),(pc))]) + + /* GX callbacks */ + struct tgx_callbacks* callbacks; + + /* compilation statistics */ + unsigned_4 compile_time; /* time to compile [s] */ + + /* ---- LEARNING MODE STATE ---- */ + unsigned_4 learn_last_change; /* time of last flag change */ + struct sim_gx_compiled_block* learning_block; + + /* ---- OPTIMIZED MODE STATE ---- */ + unsigned opt_compile_count; /* number of optimized compile attempts */ + struct sim_gx_compiled_block* optimized_block; +} sim_gx_block; + + + + +typedef struct sim_gx_compiled_block +{ + /* ---- TRANSLATION OBJECTS ---- */ + char* source_name; /* source file for translated object */ + char* object_name; /* file name of translated object */ + char* symbol_name; /* symbol name of function */ + /* (all above pointers are zalloc()'d buffers, to be zfree()'d. */ + + FILE* source_file; /* working file pointer during translation */ + + /* ---- LOADED TRANSLATIONS ---- */ + void* object_dlhandle; /* dlopen() handle to loaded object (if open) */ + void* function_dlhandle; /* dlsym() pointer to function (if found) */ +} sim_gx_compiled_block; + + + +/* GX block vector: for quick search of translated blocks */ +typedef struct sim_gx_block_list +{ + /* ---- BLOCK VECTOR ---- */ + sim_gx_block** gx_blocks; /* vector of GX blocks, sorted by origin field */ + unsigned gx_blocks_size; /* vector length */ + unsigned gx_blocks_used; /* number of elements used in vector */ + address_word gx_first, gx_last; /* first & last addresses translated by any gx block */ +} sim_gx_block_list; + + +/* actual gx function pointer type */ +struct tgx_cpu_regs; +typedef int (*sim_gx_function)(struct tgx_cpu_regs* cpu, char* pc_flags, struct tgx_callbacks* callbacks); + + +/* return values from gx function */ +#define GX_F_HALT 0 +#define GX_F_NONPC 1 +#define GX_F_RANGE 2 +#define GX_F_YIELD 3 + + +/* Limit on loop cycles within a learning mode gx block */ +#define GX_LEARN_RUN_LIMIT 10000 + +/* Operations */ +sim_gx_block* sim_gx_block_create(sim_cia cia); + +sim_gx_block* sim_gx_block_find(sim_cia cia); +void sim_gx_block_add(sim_gx_block* block); +void sim_gx_block_remove(sim_gx_block* block); + +/* State save/restore */ +void sim_gx_write_block_list(); +void sim_gx_read_block_list(); + +sim_gx_function sim_gx_compiled_block_f(sim_gx_compiled_block* gx); +void sim_gx_compiled_block_dispose(sim_gx_compiled_block* gx); +void sim_gx_block_translate(sim_gx_block* gx, int optimized); + + + +/* Target-specific translation operations */ + +int tgx_optimize_test(sim_gx_block* block); + +void tgx_block_ctor(sim_gx_block* block, sim_cia cia); +void tgx_block_ctor2(sim_gx_block* block, unsigned_4 origin, + unsigned_4 length, unsigned_4 divisor); +void tgx_block_dtor(sim_gx_block* block); + +void tgx_emit_pre_function(sim_gx_block* block, int optimized); +void tgx_emit_load_block(sim_gx_block* block, int optimized); +sim_cia tgx_emit_insn(sim_gx_block* block, sim_cia cia, int optimized); +void tgx_emit_save_block(sim_gx_block* block, int optimized); +void tgx_emit_post_function(sim_gx_block* block, int optimized); + + +#endif /* SIM_GX_H */ |