| /* command line interpretation, image loading etc. for Gforth |
/* command line interpretation, image loading etc. for Gforth |
| |
|
| |
|
| Copyright (C) 1995,1996,1997,1998,2000,2003,2004,2005,2006,2007,2008 Free Software Foundation, Inc. |
Copyright (C) 1995,1996,1997,1998,2000,2003,2004,2005,2006,2007,2008,2009,2010,2011 Free Software Foundation, Inc. |
| |
|
| This file is part of Gforth. |
This file is part of Gforth. |
| |
|
| #include <string.h> |
#include <string.h> |
| #include <math.h> |
#include <math.h> |
| #include <sys/types.h> |
#include <sys/types.h> |
| |
#ifdef HAVE_ALLOCA_H |
| |
#include <alloca.h> |
| |
#endif |
| #ifndef STANDALONE |
#ifndef STANDALONE |
| #include <sys/stat.h> |
#include <sys/stat.h> |
| #endif |
#endif |
| #include <fcntl.h> |
#include <fcntl.h> |
| #include <assert.h> |
#include <assert.h> |
| #include <stdlib.h> |
#include <stdlib.h> |
| #include <stdbool.h> |
|
| #include <signal.h> |
#include <signal.h> |
| |
|
| #ifndef STANDALONE |
#ifndef STANDALONE |
| #if HAVE_SYS_MMAN_H |
#if HAVE_SYS_MMAN_H |
| #include <sys/mman.h> |
#include <sys/mman.h> |
| #endif |
#endif |
| #include "io.h" |
#include "io.h" |
| #include "getopt.h" |
#include "getopt.h" |
| #ifdef STANDALONE |
#ifndef STANDALONE |
| /* #include <systypes.h> */ |
#include <locale.h> |
| #endif |
#endif |
| |
|
| /* output rules etc. for burg with --debug and --print-sequences */ |
/* output rules etc. for burg with --debug and --print-sequences */ |
| /* global variables for engine.c |
/* global variables for engine.c |
| We put them here because engine.c is compiled several times in |
We put them here because engine.c is compiled several times in |
| different ways for the same engine. */ |
different ways for the same engine. */ |
| Cell *gforth_SP; |
PER_THREAD Cell *gforth_SP; |
| Float *gforth_FP; |
PER_THREAD Float *gforth_FP; |
| Address gforth_UP=NULL; |
PER_THREAD user_area* gforth_UP=NULL; |
| Cell *gforth_RP; |
PER_THREAD Cell *gforth_RP; |
| Address gforth_LP; |
PER_THREAD Address gforth_LP; |
| |
|
| #ifdef HAS_FFCALL |
#ifdef HAS_FFCALL |
| |
|
| #include <callback.h> |
#include <callback.h> |
| |
|
| va_alist gforth_clist; |
PER_THREAD va_alist gforth_clist; |
| |
|
| void gforth_callback(Xt* fcall, void * alist) |
void gforth_callback(Xt* fcall, void * alist) |
| { |
{ |
| |
|
| gforth_clist = (va_alist)alist; |
gforth_clist = (va_alist)alist; |
| |
|
| gforth_engine(fcall, sp, rp, fp, lp sr_call); |
gforth_engine(fcall sr_call); |
| |
|
| /* restore global variables */ |
/* restore global variables */ |
| gforth_RP = rp; |
gforth_RP = rp; |
| GNU C manual) */ |
GNU C manual) */ |
| #if defined(GLOBALS_NONRELOC) |
#if defined(GLOBALS_NONRELOC) |
| saved_regs saved_regs_v; |
saved_regs saved_regs_v; |
| saved_regs *saved_regs_p = &saved_regs_v; |
PER_THREAD saved_regs *saved_regs_p = &saved_regs_v; |
| #else /* !defined(GLOBALS_NONRELOC) */ |
#else /* !defined(GLOBALS_NONRELOC) */ |
| Xt *saved_ip; |
PER_THREAD Xt *saved_ip; |
| Cell *rp; |
PER_THREAD Cell *rp; |
| #endif /* !defined(GLOBALS_NONRELOC) */ |
#endif /* !defined(GLOBALS_NONRELOC) */ |
| #endif /* !defined(GFORTH_DEBUGGING) */ |
#endif /* !defined(GFORTH_DEBUGGING) */ |
| |
|
| #endif |
#endif |
| |
|
| #ifdef MSDOS |
#ifdef MSDOS |
| jmp_buf throw_jmp_buf; |
jmp_buf throw_jmp_handler; |
| #endif |
#endif |
| |
|
| #if defined(DOUBLY_INDIRECT) |
#if defined(DOUBLY_INDIRECT) |
| #define CODE_BLOCK_SIZE (512*1024) /* !! overflow handling for -native */ |
#define CODE_BLOCK_SIZE (512*1024) /* !! overflow handling for -native */ |
| Address code_area=0; |
Address code_area=0; |
| Cell code_area_size = CODE_BLOCK_SIZE; |
Cell code_area_size = CODE_BLOCK_SIZE; |
| Address code_here=NULL+CODE_BLOCK_SIZE; /* does for code-area what HERE |
Address code_here; /* does for code-area what HERE does for the dictionary */ |
| does for the dictionary */ |
|
| Address start_flush=NULL; /* start of unflushed code */ |
Address start_flush=NULL; /* start of unflushed code */ |
| Cell last_jump=0; /* if the last prim was compiled without jump, this |
Cell last_jump=0; /* if the last prim was compiled without jump, this |
| is it's number, otherwise this contains 0 */ |
is it's number, otherwise this contains 0 */ |
| #define MAX_STATE 9 /* maximum number of states */ |
#define MAX_STATE 9 /* maximum number of states */ |
| static int maxstates = MAX_STATE; /* number of states for stack caching */ |
static int maxstates = MAX_STATE; /* number of states for stack caching */ |
| static int ss_greedy = 0; /* if true: use greedy, not optimal ss selection */ |
static int ss_greedy = 0; /* if true: use greedy, not optimal ss selection */ |
| static int diag = 0; /* if true: print diagnostic informations */ |
|
| static int tpa_noequiv = 0; /* if true: no state equivalence checking */ |
static int tpa_noequiv = 0; /* if true: no state equivalence checking */ |
| static int tpa_noautomaton = 0; /* if true: no tree parsing automaton */ |
static int tpa_noautomaton = 0; /* if true: no tree parsing automaton */ |
| static int tpa_trace = 0; /* if true: data for line graph of new states etc. */ |
static int tpa_trace = 0; /* if true: data for line graph of new states etc. */ |
| { |
{ |
| return memcmp(s1, s2, n); |
return memcmp(s1, s2, n); |
| } |
} |
| |
|
| |
Char *gforth_memmove(Char * dest, const Char* src, Cell n) |
| |
{ |
| |
return memmove(dest, src, n); |
| |
} |
| |
|
| |
Char *gforth_memset(Char * s, Cell c, UCell n) |
| |
{ |
| |
return memset(s, c, n); |
| |
} |
| |
|
| |
Char *gforth_memcpy(Char * dest, const Char* src, Cell n) |
| |
{ |
| |
return memcpy(dest, src, n); |
| |
} |
| #endif |
#endif |
| |
|
| static Cell max(Cell a, Cell b) |
static Cell max(Cell a, Cell b) |
| /* image file format: |
/* image file format: |
| * "#! binary-path -i\n" (e.g., "#! /usr/local/bin/gforth-0.4.0 -i\n") |
* "#! binary-path -i\n" (e.g., "#! /usr/local/bin/gforth-0.4.0 -i\n") |
| * padding to a multiple of 8 |
* padding to a multiple of 8 |
| * magic: "Gforth3x" means format 0.6, |
* magic: "Gforth4x" means format 0.8, |
| * where x is a byte with |
* where x is a byte with |
| * bit 7: reserved = 0 |
* bit 7: reserved = 0 |
| * bit 6:5: address unit size 2^n octets |
* bit 6:5: address unit size 2^n octets |
| * If the word =-1 (CF_NIL), the address is NIL, |
* If the word =-1 (CF_NIL), the address is NIL, |
| * If the word is <CF_NIL and >CF(DODOES), it's a CFA (:, Create, ...) |
* If the word is <CF_NIL and >CF(DODOES), it's a CFA (:, Create, ...) |
| * If the word =CF(DODOES), it's a DOES> CFA |
* If the word =CF(DODOES), it's a DOES> CFA |
| * If the word =CF(DOESJUMP), it's a DOES JUMP (2 Cells after DOES>, |
* !! ABI-CODE and ;ABI-CODE |
| * possibly containing a jump to dodoes) |
* If the word is <CF(DOER_MAX) and bit 14 is set, it's the xt of a primitive |
| * If the word is <CF(DOESJUMP) and bit 14 is set, it's the xt of a primitive |
* If the word is <CF(DOER_MAX) and bit 14 is clear, |
| * If the word is <CF(DOESJUMP) and bit 14 is clear, |
|
| * it's the threaded code of a primitive |
* it's the threaded code of a primitive |
| * bits 13..9 of a primitive token state which group the primitive belongs to, |
* bits 13..9 of a primitive token state which group the primitive belongs to, |
| * bits 8..0 of a primitive token index into the group |
* bits 8..0 of a primitive token index into the group |
| case CF(DOVAL) : |
case CF(DOVAL) : |
| case CF(DOUSER) : |
case CF(DOUSER) : |
| case CF(DODEFER) : |
case CF(DODEFER) : |
| case CF(DOFIELD) : MAKE_CF(image+i,symbols[CF(token)]); break; |
case CF(DOFIELD) : |
| case CF(DOESJUMP): image[i]=0; break; |
|
| #endif /* !defined(DOUBLY_INDIRECT) */ |
|
| case CF(DODOES) : |
case CF(DODOES) : |
| MAKE_DOES_CF(image+i,(Xt *)(image[i+1]+((Cell)start))); |
case CF(DOABICODE) : |
| break; |
case CF(DOSEMIABICODE): |
| |
MAKE_CF(image+i,symbols[CF(token)]); break; |
| |
#endif /* !defined(DOUBLY_INDIRECT) */ |
| default : /* backward compatibility */ |
default : /* backward compatibility */ |
| /* printf("Code field generation image[%x]:=CFA(%x)\n", |
/* printf("Code field generation image[%x]:=CFA(%x)\n", |
| i, CF(image[i])); */ |
i, CF(image[i])); */ |
| } |
} |
| #endif |
#endif |
| } else |
} else |
| fprintf(stderr,"Primitive %ld used in this image at $%lx (offset $%x) is not implemented by this\n engine (%s); executing this code will crash.\n",(long)CF(token),(long)&image[i], i, PACKAGE_VERSION); |
fprintf(stderr,"Primitive %ld used in this image at %p (offset $%x) is not implemented by this\n engine (%s); executing this code will crash.\n",(long)CF(token), &image[i], i, PACKAGE_VERSION); |
| } |
} |
| } else { |
} else { |
| int tok = -token & 0x1FF; |
int tok = -token & 0x1FF; |
| } |
} |
| #endif |
#endif |
| } else |
} else |
| fprintf(stderr,"Primitive %lx, %d of group %d used in this image at $%lx (offset $%x) is not implemented by this\n engine (%s); executing this code will crash.\n", (long)-token, tok, group, (long)&image[i],i,PACKAGE_VERSION); |
fprintf(stderr,"Primitive %lx, %d of group %d used in this image at %p (offset $%x) is not implemented by this\n engine (%s); executing this code will crash.\n", (long)-token, tok, group, &image[i],i,PACKAGE_VERSION); |
| } |
} |
| } else { |
} else { |
| /* if base is > 0: 0 is a null reference so don't adjust*/ |
/* if base is > 0: 0 is a null reference so don't adjust*/ |
| UCell r=PRIM_VERSION; |
UCell r=PRIM_VERSION; |
| Cell i; |
Cell i; |
| |
|
| for (i=DOCOL; i<=DOESJUMP; i++) { |
for (i=DOCOL; i<=DOER_MAX; i++) { |
| r ^= (UCell)(symbols[i]); |
r ^= (UCell)(symbols[i]); |
| r = (r << 5) | (r >> (8*sizeof(Cell)-5)); |
r = (r << 5) | (r >> (8*sizeof(Cell)-5)); |
| } |
} |
| exit(1); |
exit(1); |
| } |
} |
| r = (Address)((((Cell)r)+(sizeof(Float)-1))&(-sizeof(Float))); |
r = (Address)((((Cell)r)+(sizeof(Float)-1))&(-sizeof(Float))); |
| debugp(stderr, "malloc succeeds, address=$%lx\n", (long)r); |
debugp(stderr, "malloc succeeds, address=%p\n", r); |
| return r; |
return r; |
| } |
} |
| |
|
| static Address next_address=0; |
static void *next_address=0; |
| static void after_alloc(Address r, Cell size) |
static void after_alloc(Address r, Cell size) |
| { |
{ |
| if (r != (Address)-1) { |
if (r != (Address)-1) { |
| debugp(stderr, "success, address=$%lx\n", (long) r); |
debugp(stderr, "success, address=%p\n", r); |
| #if 0 |
#if 0 |
| /* not needed now that we protect the stacks with mprotect */ |
/* not needed now that we protect the stacks with mprotect */ |
| if (pagesize != 1) |
if (pagesize != 1) |
| #ifndef MAP_PRIVATE |
#ifndef MAP_PRIVATE |
| # define MAP_PRIVATE 0 |
# define MAP_PRIVATE 0 |
| #endif |
#endif |
| |
#ifndef PROT_NONE |
| |
# define PROT_NONE 0 |
| |
#endif |
| #if !defined(MAP_ANON) && defined(MAP_ANONYMOUS) |
#if !defined(MAP_ANON) && defined(MAP_ANONYMOUS) |
| # define MAP_ANON MAP_ANONYMOUS |
# define MAP_ANON MAP_ANONYMOUS |
| #endif |
#endif |
| #if defined(HAVE_MMAP) |
#if defined(HAVE_MMAP) |
| static Address alloc_mmap(Cell size) |
static Address alloc_mmap(Cell size) |
| { |
{ |
| Address r; |
void *r; |
| |
|
| #if defined(MAP_ANON) |
#if defined(MAP_ANON) |
| debugp(stderr,"try mmap($%lx, $%lx, ..., MAP_ANON, ...); ", (long)next_address, (long)size); |
debugp(stderr,"try mmap(%p, $%lx, ..., MAP_ANON, ...); ", next_address, size); |
| r = mmap(next_address, size, PROT_EXEC|PROT_READ|PROT_WRITE, MAP_ANON|MAP_PRIVATE|map_noreserve, -1, 0); |
r = mmap(next_address, size, PROT_EXEC|PROT_READ|PROT_WRITE, MAP_ANON|MAP_PRIVATE|map_noreserve, -1, 0); |
| #else /* !defined(MAP_ANON) */ |
#else /* !defined(MAP_ANON) */ |
| /* Ultrix (at least) does not define MAP_FILE and MAP_PRIVATE (both are |
/* Ultrix (at least) does not define MAP_FILE and MAP_PRIVATE (both are |
| debugp(stderr, "open(\"/dev/zero\"...) failed (%s), no mmap; ", |
debugp(stderr, "open(\"/dev/zero\"...) failed (%s), no mmap; ", |
| strerror(errno)); |
strerror(errno)); |
| } else { |
} else { |
| debugp(stderr,"try mmap($%lx, $%lx, ..., MAP_FILE, dev_zero, ...); ", (long)next_address, (long)size); |
debugp(stderr,"try mmap(%p, $%lx, ..., MAP_FILE, dev_zero, ...); ", next_address, size); |
| r=mmap(next_address, size, PROT_EXEC|PROT_READ|PROT_WRITE, MAP_FILE|MAP_PRIVATE|map_noreserve, dev_zero, 0); |
r=mmap(next_address, size, PROT_EXEC|PROT_READ|PROT_WRITE, MAP_FILE|MAP_PRIVATE|map_noreserve, dev_zero, 0); |
| } |
} |
| #endif /* !defined(MAP_ANON) */ |
#endif /* !defined(MAP_ANON) */ |
| return r; |
return r; |
| } |
} |
| |
|
| static void page_noaccess(Address a) |
static void page_noaccess(void *a) |
| { |
{ |
| /* try mprotect first; with munmap the page might be allocated later */ |
/* try mprotect first; with munmap the page might be allocated later */ |
| debugp(stderr, "try mprotect(%p,%ld,PROT_NONE); ", a, (long)pagesize); |
debugp(stderr, "try mprotect(%p,$%lx,PROT_NONE); ", a, (long)pagesize); |
| if (mprotect(a, pagesize, PROT_NONE)==0) { |
if (mprotect(a, pagesize, PROT_NONE)==0) { |
| debugp(stderr, "ok\n"); |
debugp(stderr, "ok\n"); |
| return; |
return; |
| } |
} |
| debugp(stderr, "failed: %s\n", strerror(errno)); |
debugp(stderr, "failed: %s\n", strerror(errno)); |
| debugp(stderr, "try munmap(%p,%ld); ", a, (long)pagesize); |
debugp(stderr, "try munmap(%p,$%lx); ", a, (long)pagesize); |
| if (munmap(a,pagesize)==0) { |
if (munmap(a,pagesize)==0) { |
| debugp(stderr, "ok\n"); |
debugp(stderr, "ok\n"); |
| return; |
return; |
| return verbose_malloc(size); |
return verbose_malloc(size); |
| } |
} |
| |
|
| static Address dict_alloc_read(FILE *file, Cell imagesize, Cell dictsize, Cell offset) |
static void *dict_alloc_read(FILE *file, Cell imagesize, Cell dictsize, Cell offset) |
| { |
{ |
| Address image = MAP_FAILED; |
void *image = MAP_FAILED; |
| |
|
| #if defined(HAVE_MMAP) |
#if defined(HAVE_MMAP) |
| if (offset==0) { |
if (offset==0) { |
| image=alloc_mmap(dictsize); |
image=alloc_mmap(dictsize); |
| if (image != (Address)MAP_FAILED) { |
if (image != (void *)MAP_FAILED) { |
| Address image1; |
void *image1; |
| debugp(stderr,"try mmap($%lx, $%lx, ..., MAP_FIXED|MAP_FILE, imagefile, 0); ", (long)image, (long)imagesize); |
debugp(stderr,"try mmap(%p, $%lx, ..., MAP_FIXED|MAP_FILE, imagefile, 0); ", image, imagesize); |
| image1 = mmap(image, imagesize, PROT_EXEC|PROT_READ|PROT_WRITE, MAP_FIXED|MAP_FILE|MAP_PRIVATE|map_noreserve, fileno(file), 0); |
image1 = mmap(image, imagesize, PROT_EXEC|PROT_READ|PROT_WRITE, MAP_FIXED|MAP_FILE|MAP_PRIVATE|map_noreserve, fileno(file), 0); |
| after_alloc(image1,dictsize); |
after_alloc(image1,dictsize); |
| if (image1 == (Address)MAP_FAILED) |
if (image1 == (void *)MAP_FAILED) |
| goto read_image; |
goto read_image; |
| } |
} |
| } |
} |
| #endif /* defined(HAVE_MMAP) */ |
#endif /* defined(HAVE_MMAP) */ |
| if (image == (Address)MAP_FAILED) { |
if (image == (void *)MAP_FAILED) { |
| image = gforth_alloc(dictsize+offset)+offset; |
image = gforth_alloc(dictsize+offset)+offset; |
| read_image: |
read_image: |
| rewind(file); /* fseek(imagefile,0L,SEEK_SET); */ |
rewind(file); /* fseek(imagefile,0L,SEEK_SET); */ |
| rsize=maxaligned(rsize); |
rsize=maxaligned(rsize); |
| lsize=maxaligned(lsize); |
lsize=maxaligned(lsize); |
| fsize=maxaligned(fsize); |
fsize=maxaligned(fsize); |
| } |
|
| |
|
| #ifdef STANDALONE |
header->dict_size=dictsize; |
| void alloc_stacks(ImageHeader * h) |
header->data_stack_size=dsize; |
| { |
header->fp_stack_size=fsize; |
| #define SSTACKSIZE 0x200 |
header->return_stack_size=rsize; |
| static Cell dstack[SSTACKSIZE+1]; |
header->locals_stack_size=lsize; |
| static Cell rstack[SSTACKSIZE+1]; |
|
| |
|
| h->dict_size=dictsize; |
|
| h->data_stack_size=dsize; |
|
| h->fp_stack_size=fsize; |
|
| h->return_stack_size=rsize; |
|
| h->locals_stack_size=lsize; |
|
| |
|
| h->data_stack_base=dstack+SSTACKSIZE; |
|
| // h->fp_stack_base=gforth_alloc(fsize); |
|
| h->return_stack_base=rstack+SSTACKSIZE; |
|
| // h->locals_stack_base=gforth_alloc(lsize); |
|
| } |
|
| #else |
|
| void alloc_stacks(ImageHeader * h) |
|
| { |
|
| h->dict_size=dictsize; |
|
| h->data_stack_size=dsize; |
|
| h->fp_stack_size=fsize; |
|
| h->return_stack_size=rsize; |
|
| h->locals_stack_size=lsize; |
|
| |
|
| #if defined(HAVE_MMAP) && !defined(STANDALONE) |
|
| if (pagesize > 1) { |
|
| size_t p = pagesize; |
|
| size_t totalsize = |
|
| wholepage(dsize)+wholepage(fsize)+wholepage(rsize)+wholepage(lsize)+5*p; |
|
| Address a = alloc_mmap(totalsize); |
|
| if (a != (Address)MAP_FAILED) { |
|
| page_noaccess(a); a+=p; h-> data_stack_base=a; a+=wholepage(dsize); |
|
| page_noaccess(a); a+=p; h-> fp_stack_base=a; a+=wholepage(fsize); |
|
| page_noaccess(a); a+=p; h->return_stack_base=a; a+=wholepage(rsize); |
|
| page_noaccess(a); a+=p; h->locals_stack_base=a; a+=wholepage(lsize); |
|
| page_noaccess(a); |
|
| debugp(stderr,"stack addresses: d=%p f=%p r=%p l=%p\n", |
|
| h->data_stack_base, |
|
| h->fp_stack_base, |
|
| h->return_stack_base, |
|
| h->locals_stack_base); |
|
| return; |
|
| } |
} |
| } |
|
| #endif |
|
| h->data_stack_base=gforth_alloc(dsize); |
|
| h->fp_stack_base=gforth_alloc(fsize); |
|
| h->return_stack_base=gforth_alloc(rsize); |
|
| h->locals_stack_base=gforth_alloc(lsize); |
|
| } |
|
| #endif |
|
| |
|
| #warning You can ignore the warnings about clobbered variables in gforth_go |
#warning You can ignore the warnings about clobbered variables in gforth_go |
| int gforth_go(Address image, int stack, Cell *entries) |
|
| |
#define NEXTPAGE(addr) ((Address)((((UCell)(addr)-1)&-pagesize)+pagesize)) |
| |
#define NEXTPAGE2(addr) ((Address)((((UCell)(addr)-1)&-pagesize)+2*pagesize)) |
| |
|
| |
Cell gforth_go(Xt* ip0) |
| { |
{ |
| volatile ImageHeader *image_header = (ImageHeader *)image; |
|
| Cell *sp0=(Cell*)(image_header->data_stack_base + dsize); |
|
| Cell *rp0=(Cell *)(image_header->return_stack_base + rsize); |
|
| Float *fp0=(Float *)(image_header->fp_stack_base + fsize); |
|
| #ifdef GFORTH_DEBUGGING |
|
| volatile Cell *orig_rp0=rp0; |
|
| #endif |
|
| Address lp0=image_header->locals_stack_base + lsize; |
|
| Xt *ip0=(Xt *)(image_header->boot_entry); |
|
| #ifdef SYSSIGNALS |
#ifdef SYSSIGNALS |
| int throw_code; |
int throw_code; |
| |
jmp_buf throw_jmp_buf; |
| #endif |
#endif |
| |
Cell signal_data_stack[24]; |
| /* ensure that the cached elements (if any) are accessible */ |
Cell signal_return_stack[16]; |
| #if !(defined(GFORTH_DEBUGGING) || defined(INDIRECT_THREADED) || defined(DOUBLY_INDIRECT) || defined(VM_PROFILING)) |
Float signal_fp_stack[1]; |
| sp0 -= 8; /* make stuff below bottom accessible for stack caching */ |
|
| fp0--; |
|
| #endif |
|
| |
|
| for(;stack>0;stack--) |
|
| *--sp0=entries[stack-1]; |
|
| |
|
| #if defined(SYSSIGNALS) && !defined(STANDALONE) |
#if defined(SYSSIGNALS) && !defined(STANDALONE) |
| get_winsize(); |
throw_jmp_handler = &throw_jmp_buf; |
| |
|
| install_signal_handlers(); /* right place? */ |
|
| |
|
| if ((throw_code=setjmp(throw_jmp_buf))) { |
|
| static Cell signal_data_stack[24]; |
|
| static Cell signal_return_stack[16]; |
|
| static Float signal_fp_stack[1]; |
|
| |
|
| |
debugp(stderr, "setjmp(%p)\n", *throw_jmp_handler); |
| |
while((throw_code=setjmp(*throw_jmp_handler))) { |
| signal_data_stack[15]=throw_code; |
signal_data_stack[15]=throw_code; |
| |
|
| #ifdef GFORTH_DEBUGGING |
#ifdef GFORTH_DEBUGGING |
| debugp(stderr,"\ncaught signal, throwing exception %d, ip=%p rp=%p\n", |
debugp(stderr,"\ncaught signal, throwing exception %d, ip=%p rp=%p\n", |
| throw_code, saved_ip, rp); |
throw_code, saved_ip, rp); |
| if (rp <= orig_rp0 && rp > (Cell *)(image_header->return_stack_base+5)) { |
if ((rp > NEXTPAGE2(gforth_UP->sp0)) && |
| |
(rp < NEXTPAGE(gforth_UP->rp0))) { |
| /* no rstack overflow or underflow */ |
/* no rstack overflow or underflow */ |
| rp0 = rp; |
gforth_RP = rp; |
| *--rp0 = (Cell)saved_ip; |
*--gforth_RP = (Cell)saved_ip; |
| } |
} |
| else /* I love non-syntactic ifdefs :-) */ |
else /* I love non-syntactic ifdefs :-) */ |
| rp0 = signal_return_stack+16; |
gforth_RP = signal_return_stack+16; |
| #else /* !defined(GFORTH_DEBUGGING) */ |
#else /* !defined(GFORTH_DEBUGGING) */ |
| debugp(stderr,"\ncaught signal, throwing exception %d\n", throw_code); |
debugp(stderr,"\ncaught signal, throwing exception %d\n", throw_code); |
| rp0 = signal_return_stack+16; |
gforth_RP = signal_return_stack+16; |
| #endif /* !defined(GFORTH_DEBUGGING) */ |
#endif /* !defined(GFORTH_DEBUGGING) */ |
| /* fprintf(stderr, "rp=$%x\n",rp0);*/ |
/* fprintf(stderr, "rp=$%x\n",rp0);*/ |
| |
|
| return((int)(Cell)gforth_engine(image_header->throw_entry, signal_data_stack+15, |
ip0=gforth_header->throw_entry; |
| rp0, signal_fp_stack, 0 sr_call)); |
gforth_SP=signal_data_stack+15; |
| |
gforth_FP=signal_fp_stack; |
| } |
} |
| #endif |
#endif |
| |
|
| return((int)(Cell)gforth_engine(ip0,sp0,rp0,fp0,lp0 sr_call)); |
return((Cell)gforth_engine(ip0 sr_call)); |
| } |
} |
| |
|
| #if !defined(INCLUDE_IMAGE) && !defined(STANDALONE) |
#if !defined(INCLUDE_IMAGE) && !defined(STANDALONE) |
| significant space so we only do it if the user explicitly |
significant space so we only do it if the user explicitly |
| disables state equivalence. */ |
disables state equivalence. */ |
| debugp(stderr, "Disabling tpa-automaton, because nsupers>0 and state equivalence is enabled.\n"); |
debugp(stderr, "Disabling tpa-automaton, because nsupers>0 and state equivalence is enabled.\n"); |
| tpa_noautomaton = true; |
tpa_noautomaton = 1; |
| } |
} |
| } |
} |
| |
|
| #ifndef NO_DYNAMIC |
#ifndef NO_DYNAMIC |
| if (no_dynamic) |
if (no_dynamic) |
| return; |
return; |
| symbols2=gforth_engine2(0,0,0,0,0 sr_call); |
symbols2=gforth_engine2(0 sr_call); |
| #if NO_IP |
#if NO_IP |
| symbols3=gforth_engine3(0,0,0,0,0 sr_call); |
symbols3=gforth_engine3(0 sr_call); |
| #else |
#else |
| symbols3=symbols1; |
symbols3=symbols1; |
| #endif |
#endif |
| nonrelocs++; |
nonrelocs++; |
| continue; |
continue; |
| } |
} |
| |
if (CHECK_PRIM(s1, prim_len)) { |
| |
#ifndef BURG_FORMAT |
| |
debugp(stderr,"\n non_reloc: architecture specific check failed\n"); |
| |
#endif |
| |
pi->start = NULL; /* not relocatable */ |
| |
relocs--; |
| |
nonrelocs++; |
| |
continue; |
| |
} |
| assert(pi->length>=0); |
assert(pi->length>=0); |
| assert(pi->restlength >=0); |
assert(pi->restlength >=0); |
| while (j<(pi->length+pi->restlength)) { |
while (j<(pi->length+pi->restlength)) { |
| ia->rel=0; |
ia->rel=0; |
| debugp(stderr,"\n absolute immarg: offset %3d",j); |
debugp(stderr,"\n absolute immarg: offset %3d",j); |
| } else if ((&(s1[j]))+(*(Cell *)&(s1[j]))+4 == |
} else if ((&(s1[j]))+(*(Cell *)&(s1[j]))+4 == |
| symbols1[DOESJUMP+1]) { |
symbols1[DOER_MAX+1]) { |
| ia->rel=1; |
ia->rel=1; |
| debugp(stderr,"\n relative immarg: offset %3d",j); |
debugp(stderr,"\n relative immarg: offset %3d",j); |
| } else { |
} else { |
| debugp(stderr,"\n"); |
debugp(stderr,"\n"); |
| } |
} |
| decomp_prims = calloc(i,sizeof(PrimInfo *)); |
decomp_prims = calloc(i,sizeof(PrimInfo *)); |
| for (i=DOESJUMP+1; i<npriminfos; i++) |
for (i=DOER_MAX+1; i<npriminfos; i++) |
| decomp_prims[i] = &(priminfos[i]); |
decomp_prims[i] = &(priminfos[i]); |
| qsort(decomp_prims+DOESJUMP+1, npriminfos-DOESJUMP-1, sizeof(PrimInfo *), |
qsort(decomp_prims+DOER_MAX+1, npriminfos-DOER_MAX-1, sizeof(PrimInfo *), |
| compare_priminfo_length); |
compare_priminfo_length); |
| #endif |
#endif |
| } |
} |
| { |
{ |
| #ifndef NO_DYNAMIC |
#ifndef NO_DYNAMIC |
| if (start_flush) |
if (start_flush) |
| FLUSH_ICACHE(start_flush, code_here-start_flush); |
FLUSH_ICACHE((caddr_t)start_flush, code_here-start_flush); |
| start_flush=code_here; |
start_flush=code_here; |
| #endif |
#endif |
| } |
} |
| Cell size; |
Cell size; |
| } *code_block_list=NULL, **next_code_blockp=&code_block_list; |
} *code_block_list=NULL, **next_code_blockp=&code_block_list; |
| |
|
| static Address append_prim(Cell p) |
static void reserve_code_space(UCell size) |
| { |
{ |
| PrimInfo *pi = &priminfos[p]; |
if (code_area+code_area_size < code_here+size) { |
| Address old_code_here = code_here; |
|
| |
|
| if (code_area+code_area_size < code_here+pi->length+pi->restlength+goto_len+CODE_ALIGNMENT) { |
|
| struct code_block_list *p; |
struct code_block_list *p; |
| append_jump(); |
append_jump(); |
| |
debugp(stderr,"Did not use %ld bytes in code block\n", |
| |
(long)(code_area+code_area_size-code_here)); |
| flush_to_here(); |
flush_to_here(); |
| if (*next_code_blockp == NULL) { |
if (*next_code_blockp == NULL) { |
| code_here = start_flush = code_area = gforth_alloc(code_area_size); |
code_here = start_flush = code_area = gforth_alloc(code_area_size); |
| p = *next_code_blockp; |
p = *next_code_blockp; |
| code_here = start_flush = code_area = p->block; |
code_here = start_flush = code_area = p->block; |
| } |
} |
| old_code_here = code_here; |
|
| next_code_blockp = &(p->next); |
next_code_blockp = &(p->next); |
| } |
} |
| |
} |
| |
|
| |
static Address append_prim(Cell p) |
| |
{ |
| |
PrimInfo *pi = &priminfos[p]; |
| |
Address old_code_here; |
| |
reserve_code_space(pi->length+pi->restlength+goto_len+CODE_ALIGNMENT-1); |
| memcpy(code_here, pi->start, pi->length); |
memcpy(code_here, pi->start, pi->length); |
| |
old_code_here = code_here; |
| code_here += pi->length; |
code_here += pi->length; |
| return old_code_here; |
return old_code_here; |
| } |
} |
| |
|
| |
static void reserve_code_super(PrimNum origs[], int ninsts) |
| |
{ |
| |
int i; |
| |
UCell size = CODE_ALIGNMENT-1; /* alignment may happen first */ |
| |
if (no_dynamic) |
| |
return; |
| |
/* use size of the original primitives as an upper bound for the |
| |
size of the superinstruction. !! This is only safe if we |
| |
optimize for code size (the default) */ |
| |
for (i=0; i<ninsts; i++) { |
| |
PrimNum p = origs[i]; |
| |
PrimInfo *pi = &priminfos[p]; |
| |
if (is_relocatable(p)) |
| |
size += pi->length; |
| |
else |
| |
if (i>0) |
| |
size += priminfos[origs[i-1]].restlength+goto_len+CODE_ALIGNMENT-1; |
| |
} |
| |
size += priminfos[origs[i-1]].restlength+goto_len; |
| |
reserve_code_space(size); |
| |
} |
| #endif |
#endif |
| |
|
| int forget_dyncode(Address code) |
int forget_dyncode(Address code) |
| break; |
break; |
| } |
} |
| /* reverse order because NOOP might match other prims */ |
/* reverse order because NOOP might match other prims */ |
| for (i=npriminfos-1; i>DOESJUMP; i--) { |
for (i=npriminfos-1; i>DOER_MAX; i--) { |
| PrimInfo *pi=decomp_prims[i]; |
PrimInfo *pi=decomp_prims[i]; |
| if (pi->start==code || (pi->start && memcmp(code,pi->start,pi->length)==0)) |
if (pi->start==code || (pi->start && memcmp(code,pi->start,pi->length)==0)) |
| return vm_prims[super2[super_costs[pi-priminfos].offset]]; |
return vm_prims[super2[super_costs[pi-priminfos].offset]]; |
| static void tpa_state_normalize(struct tpa_state *t) |
static void tpa_state_normalize(struct tpa_state *t) |
| { |
{ |
| /* normalize so cost of canonical state=0; this may result in |
/* normalize so cost of canonical state=0; this may result in |
| negative states for some states */ |
negative costs for some states */ |
| int d = t->inst[CANONICAL_STATE].cost; |
int d = t->inst[CANONICAL_STATE].cost; |
| int i; |
int i; |
| |
|
| int i,j; |
int i,j; |
| struct tpa_state *ts[ninsts+1]; |
struct tpa_state *ts[ninsts+1]; |
| int nextdyn, nextstate, no_transition; |
int nextdyn, nextstate, no_transition; |
| |
Address old_code_area; |
| |
|
| lb_basic_blocks++; |
lb_basic_blocks++; |
| ts[ninsts] = termstate; |
ts[ninsts] = termstate; |
| } |
} |
| } |
} |
| /* now rewrite the instructions */ |
/* now rewrite the instructions */ |
| |
reserve_code_super(origs,ninsts); |
| |
old_code_area = code_area; |
| nextdyn=0; |
nextdyn=0; |
| nextstate=CANONICAL_STATE; |
nextstate=CANONICAL_STATE; |
| no_transition = ((!ts[0]->trans[nextstate].relocatable) |
no_transition = ((!ts[0]->trans[nextstate].relocatable) |
| nextstate = c->state_out; |
nextstate = c->state_out; |
| } |
} |
| assert(nextstate==CANONICAL_STATE); |
assert(nextstate==CANONICAL_STATE); |
| |
assert(code_area==old_code_area); /* does reserve_code_super() work? */ |
| } |
} |
| #endif |
#endif |
| |
|
| if (start==NULL) |
if (start==NULL) |
| return; |
return; |
| prim = (Label)*start; |
prim = (Label)*start; |
| if (prim<((Label)(xts+DOESJUMP)) || prim>((Label)(xts+npriminfos))) { |
if (prim<((Label)(xts+DOER_MAX)) || prim>((Label)(xts+npriminfos))) { |
| fprintf(stderr,"compile_prim encountered xt %p\n", prim); |
fprintf(stderr,"compile_prim encountered xt %p\n", prim); |
| *start=(Cell)prim; |
*start=(Cell)prim; |
| return; |
return; |
| #elif defined(INDIRECT_THREADED) |
#elif defined(INDIRECT_THREADED) |
| return; |
return; |
| #else /* !(defined(DOUBLY_INDIRECT) || defined(INDIRECT_THREADED)) */ |
#else /* !(defined(DOUBLY_INDIRECT) || defined(INDIRECT_THREADED)) */ |
| /* !! does not work, for unknown reasons; but something like this is |
|
| probably needed to ensure that we don't call compile_prim_dyn |
|
| before the inline arguments are there */ |
|
| static Cell *instps[MAX_BB]; |
static Cell *instps[MAX_BB]; |
| static PrimNum origs[MAX_BB]; |
static PrimNum origs[MAX_BB]; |
| static int ninsts=0; |
static int ninsts=0; |
| } |
} |
| prim_num = ((Xt)*start)-vm_prims; |
prim_num = ((Xt)*start)-vm_prims; |
| if(prim_num >= npriminfos) { |
if(prim_num >= npriminfos) { |
| |
/* code word */ |
| optimize_rewrite(instps,origs,ninsts); |
optimize_rewrite(instps,origs,ninsts); |
| /* fprintf(stderr,"optimize_rewrite(...,%d)\n",ninsts);*/ |
/* fprintf(stderr,"optimize_rewrite(...,%d)\n",ninsts);*/ |
| ninsts=0; |
ninsts=0; |
| |
append_jump(); |
| |
*start = *(Cell *)*start; |
| return; |
return; |
| } |
} |
| assert(ninsts<MAX_BB); |
assert(ninsts<MAX_BB); |
| #endif /* !(defined(DOUBLY_INDIRECT) || defined(INDIRECT_THREADED)) */ |
#endif /* !(defined(DOUBLY_INDIRECT) || defined(INDIRECT_THREADED)) */ |
| } |
} |
| |
|
| |
void gforth_init() |
| |
{ |
| |
#if 0 && defined(__i386) |
| |
/* disabled because the drawbacks may be worse than the benefits */ |
| |
/* set 387 precision control to use 53-bit mantissae to avoid most |
| |
cases of double rounding */ |
| |
short fpu_control = 0x027f ; |
| |
asm("fldcw %0" : : "m"(fpu_control)); |
| |
#endif /* defined(__i386) */ |
| |
|
| |
#ifdef MACOSX_DEPLOYMENT_TARGET |
| |
setenv("MACOSX_DEPLOYMENT_TARGET", MACOSX_DEPLOYMENT_TARGET, 0); |
| |
#endif |
| |
#ifdef LTDL_LIBRARY_PATH |
| |
setenv("LTDL_LIBRARY_PATH", LTDL_LIBRARY_PATH, 0); |
| |
#endif |
| |
#ifndef STANDALONE |
| |
/* buffering of the user output device */ |
| |
#ifdef _IONBF |
| |
if (isatty(fileno(stdout))) { |
| |
fflush(stdout); |
| |
setvbuf(stdout,NULL,_IONBF,0); |
| |
} |
| |
#endif |
| |
setlocale(LC_ALL, ""); |
| |
setlocale(LC_NUMERIC, "C"); |
| |
#else |
| |
prep_terminal(); |
| |
#endif |
| |
|
| #ifndef STANDALONE |
#ifndef STANDALONE |
| Address gforth_loader(FILE *imagefile, char* filename) |
#ifdef HAVE_LIBLTDL |
| |
if (lt_dlinit()!=0) { |
| |
fprintf(stderr,"%s: lt_dlinit failed", progname); |
| |
exit(1); |
| |
} |
| |
#endif |
| |
#ifdef HAS_OS |
| |
#ifndef NO_DYNAMIC |
| |
init_ss_cost(); |
| |
#endif /* !defined(NO_DYNAMIC) */ |
| |
#endif /* defined(HAS_OS) */ |
| |
#endif |
| |
code_here = ((void *)0)+code_area_size; |
| |
|
| |
get_winsize(); |
| |
|
| |
install_signal_handlers(); /* right place? */ |
| |
} |
| |
|
| |
/* pointer to last '/' or '\' in file, 0 if there is none. */ |
| |
static char *onlypath(char *filename) |
| |
{ |
| |
return strrchr(filename, DIRSEP); |
| |
} |
| |
|
| |
static FILE *openimage(char *fullfilename) |
| |
{ |
| |
FILE *image_file; |
| |
char * expfilename = tilde_cstr((Char *)fullfilename, strlen(fullfilename)); |
| |
|
| |
image_file=fopen(expfilename,"rb"); |
| |
if (image_file!=NULL && debug) |
| |
fprintf(stderr, "Opened image file: %s\n", expfilename); |
| |
free(expfilename); |
| |
return image_file; |
| |
} |
| |
|
| |
/* try to open image file concat(path[0:len],imagename) */ |
| |
static FILE *checkimage(char *path, int len, char *imagename) |
| |
{ |
| |
int dirlen=len; |
| |
char fullfilename[dirlen+strlen((char *)imagename)+2]; |
| |
|
| |
memcpy(fullfilename, path, dirlen); |
| |
if (fullfilename[dirlen-1]!=DIRSEP) |
| |
fullfilename[dirlen++]=DIRSEP; |
| |
strcpy(fullfilename+dirlen,imagename); |
| |
return openimage(fullfilename); |
| |
} |
| |
|
| |
static FILE * open_image_file(char * imagename, char * path) |
| |
{ |
| |
FILE * image_file=NULL; |
| |
char *origpath=path; |
| |
|
| |
if(strchr(imagename, DIRSEP)==NULL) { |
| |
/* first check the directory where the exe file is in !! 01may97jaw */ |
| |
if (onlypath(progname)) |
| |
image_file=checkimage(progname, onlypath(progname)-progname, imagename); |
| |
if (!image_file) |
| |
do { |
| |
char *pend=strchr(path, PATHSEP); |
| |
if (pend==NULL) |
| |
pend=path+strlen(path); |
| |
if (strlen(path)==0) break; |
| |
image_file=checkimage(path, pend-path, imagename); |
| |
path=pend+(*pend==PATHSEP); |
| |
} while (image_file==NULL); |
| |
} else { |
| |
image_file=openimage(imagename); |
| |
} |
| |
|
| |
if (!image_file) { |
| |
fprintf(stderr,"%s: cannot open image file %s in path %s for reading\n", |
| |
progname, imagename, origpath); |
| |
exit(1); |
| |
} |
| |
|
| |
return image_file; |
| |
} |
| |
|
| |
#ifdef STANDALONE |
| |
Address gforth_loader(char* imagename, char* path) |
| |
{ |
| |
gforth_init(); |
| |
return gforth_engine(0 sr_call); |
| |
} |
| |
#else |
| |
Address gforth_loader(char* imagename, char* path) |
| /* returns the address of the image proper (after the preamble) */ |
/* returns the address of the image proper (after the preamble) */ |
| { |
{ |
| ImageHeader header; |
ImageHeader header; |
| 1 |
1 |
| #endif |
#endif |
| ; |
; |
| |
FILE* imagefile=open_image_file(imagename, path); |
| |
|
| |
gforth_init(); |
| |
|
| vm_prims = gforth_engine(0,0,0,0,0 sr_call); |
vm_prims = gforth_engine(0 sr_call); |
| check_prims(vm_prims); |
check_prims(vm_prims); |
| prepare_super_table(); |
prepare_super_table(); |
| #ifndef DOUBLY_INDIRECT |
#ifndef DOUBLY_INDIRECT |
| |
|
| do { |
do { |
| if(fread(magic,sizeof(Char),8,imagefile) < 8) { |
if(fread(magic,sizeof(Char),8,imagefile) < 8) { |
| fprintf(stderr,"%s: image %s doesn't seem to be a Gforth (>=0.6) image.\n", |
fprintf(stderr,"%s: image %s doesn't seem to be a Gforth (>=0.8) image.\n", |
| progname, filename); |
progname, imagename); |
| exit(1); |
exit(1); |
| } |
} |
| preamblesize+=8; |
preamblesize+=8; |
| } while(memcmp(magic,"Gforth3",7)); |
} while(memcmp(magic,"Gforth4",7)); |
| magic7 = magic[7]; |
magic7 = magic[7]; |
| if (debug) { |
if (debug) { |
| magic[7]='\0'; |
magic[7]='\0'; |
| debugp(stderr,"pagesize=%ld\n",(unsigned long) pagesize); |
debugp(stderr,"pagesize=%ld\n",(unsigned long) pagesize); |
| |
|
| image = dict_alloc_read(imagefile, preamblesize+header.image_size, |
image = dict_alloc_read(imagefile, preamblesize+header.image_size, |
| preamblesize+dictsize, data_offset); |
dictsize, data_offset); |
| imp=image+preamblesize; |
imp=image+preamblesize; |
| |
|
| alloc_stacks((ImageHeader *)imp); |
|
| if (clear_dictionary) |
if (clear_dictionary) |
| memset(imp+header.image_size, 0, dictsize-header.image_size); |
memset(imp+header.image_size, 0, dictsize-header.image_size-preamblesize); |
| if(header.base==0 || header.base == (Address)0x100) { |
if(header.base==0 || header.base == (Address)0x100) { |
| Cell reloc_size=((header.image_size-1)/sizeof(Cell))/8+1; |
Cell reloc_size=((header.image_size-1)/sizeof(Cell))/8+1; |
| Char reloc_bits[reloc_size]; |
Char reloc_bits[reloc_size]; |
| #endif |
#endif |
| } |
} |
| else if(header.base!=imp) { |
else if(header.base!=imp) { |
| fprintf(stderr,"%s: Cannot load nonrelocatable image (compiled for address $%lx) at address $%lx\n", |
fprintf(stderr,"%s: Cannot load nonrelocatable image (compiled for address %p) at address %p\n", |
| progname, (unsigned long)header.base, (unsigned long)imp); |
progname, header.base, imp); |
| exit(1); |
exit(1); |
| } |
} |
| if (header.checksum==0) |
if (header.checksum==0) |
| ((ImageHeader *)imp)->checksum=check_sum; |
((ImageHeader *)imp)->checksum=check_sum; |
| else if (header.checksum != check_sum) { |
else if (header.checksum != check_sum) { |
| fprintf(stderr,"%s: Checksum of image ($%lx) does not match the executable ($%lx)\n", |
fprintf(stderr,"%s: Checksum of image ($%lx) does not match the executable ($%lx)\n", |
| progname, (unsigned long)(header.checksum),(unsigned long)check_sum); |
progname, header.checksum, check_sum); |
| exit(1); |
exit(1); |
| } |
} |
| #ifdef DOUBLY_INDIRECT |
#ifdef DOUBLY_INDIRECT |
| return imp; |
return imp; |
| } |
} |
| #endif |
#endif |
| |
|
| /* pointer to last '/' or '\' in file, 0 if there is none. */ |
|
| static char *onlypath(char *filename) |
|
| { |
|
| return strrchr(filename, DIRSEP); |
|
| } |
|
| |
|
| static FILE *openimage(char *fullfilename) |
|
| { |
|
| FILE *image_file; |
|
| char * expfilename = tilde_cstr((Char *)fullfilename, strlen(fullfilename), 1); |
|
| |
|
| image_file=fopen(expfilename,"rb"); |
|
| if (image_file!=NULL && debug) |
|
| fprintf(stderr, "Opened image file: %s\n", expfilename); |
|
| return image_file; |
|
| } |
|
| |
|
| /* try to open image file concat(path[0:len],imagename) */ |
|
| static FILE *checkimage(char *path, int len, char *imagename) |
|
| { |
|
| int dirlen=len; |
|
| char fullfilename[dirlen+strlen((char *)imagename)+2]; |
|
| |
|
| memcpy(fullfilename, path, dirlen); |
|
| if (fullfilename[dirlen-1]!=DIRSEP) |
|
| fullfilename[dirlen++]=DIRSEP; |
|
| strcpy(fullfilename+dirlen,imagename); |
|
| return openimage(fullfilename); |
|
| } |
|
| |
|
| static FILE * open_image_file(char * imagename, char * path) |
|
| { |
|
| FILE * image_file=NULL; |
|
| char *origpath=path; |
|
| |
|
| if(strchr(imagename, DIRSEP)==NULL) { |
|
| /* first check the directory where the exe file is in !! 01may97jaw */ |
|
| if (onlypath(progname)) |
|
| image_file=checkimage(progname, onlypath(progname)-progname, imagename); |
|
| if (!image_file) |
|
| do { |
|
| char *pend=strchr(path, PATHSEP); |
|
| if (pend==NULL) |
|
| pend=path+strlen(path); |
|
| if (strlen(path)==0) break; |
|
| image_file=checkimage(path, pend-path, imagename); |
|
| path=pend+(*pend==PATHSEP); |
|
| } while (image_file==NULL); |
|
| } else { |
|
| image_file=openimage(imagename); |
|
| } |
|
| |
|
| if (!image_file) { |
|
| fprintf(stderr,"%s: cannot open image file %s in path %s for reading\n", |
|
| progname, imagename, origpath); |
|
| exit(1); |
|
| } |
|
| |
|
| return image_file; |
|
| } |
|
| #endif |
#endif |
| |
|
| #ifdef STANDALONE_ALLOC |
#ifdef STANDALONE_ALLOC |
| exit(1); |
exit(1); |
| } |
} |
| r = (Address)((((Cell)r)+(sizeof(Float)-1))&(-sizeof(Float))); |
r = (Address)((((Cell)r)+(sizeof(Float)-1))&(-sizeof(Float))); |
| debugp(stderr, "malloc succeeds, address=$%lx\n", (long)r); |
debugp(stderr, "malloc succeeds, address=%p\n", r); |
| return r; |
return r; |
| } |
} |
| #endif |
#endif |
| ss_min_ls, |
ss_min_ls, |
| ss_min_lsu, |
ss_min_lsu, |
| ss_min_nexts, |
ss_min_nexts, |
| |
opt_code_block_size, |
| }; |
}; |
| |
|
| #ifndef STANDALONE |
static void print_diag() |
| |
{ |
| |
|
| |
#if !defined(HAVE_GETRUSAGE) |
| |
fprintf(stderr, "*** missing functionality ***\n" |
| |
#ifndef HAVE_GETRUSAGE |
| |
" no getrusage -> CPUTIME broken\n" |
| |
#endif |
| |
); |
| |
#endif |
| |
if((relocs < nonrelocs) || |
| |
#if defined(BUGGY_LL_CMP) || defined(BUGGY_LL_MUL) || defined(BUGGY_LL_DIV) || defined(BUGGY_LL_ADD) || defined(BUGGY_LL_SHIFT) || defined(BUGGY_LL_D2F) || defined(BUGGY_LL_F2D) |
| |
1 |
| |
#else |
| |
0 |
| |
#endif |
| |
) |
| |
debugp(stderr, "relocs: %d:%d\n", relocs, nonrelocs); |
| |
fprintf(stderr, "*** %sperformance problems ***\n%s%s", |
| |
#if defined(BUGGY_LL_CMP) || defined(BUGGY_LL_MUL) || defined(BUGGY_LL_DIV) || defined(BUGGY_LL_ADD) || defined(BUGGY_LL_SHIFT) || defined(BUGGY_LL_D2F) || defined(BUGGY_LL_F2D) || !(defined(FORCE_REG) || defined(FORCE_REG_UNNECESSARY)) || defined(BUGGY_LONG_LONG) |
| |
"", |
| |
#else |
| |
"no ", |
| |
#endif |
| |
#if defined(BUGGY_LL_CMP) || defined(BUGGY_LL_MUL) || defined(BUGGY_LL_DIV) || defined(BUGGY_LL_ADD) || defined(BUGGY_LL_SHIFT) || defined(BUGGY_LL_D2F) || defined(BUGGY_LL_F2D) |
| |
" double-cell integer type buggy ->\n " |
| |
#ifdef BUGGY_LL_CMP |
| |
"double comparisons, " |
| |
#endif |
| |
#ifdef BUGGY_LL_MUL |
| |
"*/MOD */ M* UM* " |
| |
#endif |
| |
#ifdef BUGGY_LL_DIV |
| |
/* currently nothing is affected */ |
| |
#endif |
| |
#ifdef BUGGY_LL_ADD |
| |
"M+ D+ D- DNEGATE " |
| |
#endif |
| |
#ifdef BUGGY_LL_SHIFT |
| |
"D2/ " |
| |
#endif |
| |
#ifdef BUGGY_LL_D2F |
| |
"D>F " |
| |
#endif |
| |
#ifdef BUGGY_LL_F2D |
| |
"F>D " |
| |
#endif |
| |
"\b\b slow\n" |
| |
#endif |
| |
#if !(defined(FORCE_REG) || defined(FORCE_REG_UNNECESSARY)) |
| |
" automatic register allocation: performance degradation possible\n" |
| |
#endif |
| |
"", |
| |
(relocs < nonrelocs) ? "no dynamic code generation (--debug for details) -> factor 2 slowdown\n" : ""); |
| |
} |
| |
|
| |
#ifdef STANDALONE |
| |
void gforth_args(int argc, char ** argv, char ** path, char ** imagename) |
| |
{ |
| |
#ifdef HAS_OS |
| |
*path = getenv("GFORTHPATH") ? : DEFAULTPATH; |
| |
#else |
| |
*path = DEFAULTPATH; |
| |
#endif |
| |
*imagename="gforth.fi"; |
| |
} |
| |
#else |
| void gforth_args(int argc, char ** argv, char ** path, char ** imagename) |
void gforth_args(int argc, char ** argv, char ** path, char ** imagename) |
| { |
{ |
| int c; |
int c; |
| |
#ifdef HAS_OS |
| |
*path = getenv("GFORTHPATH") ? : DEFAULTPATH; |
| |
#else |
| |
*path = DEFAULTPATH; |
| |
#endif |
| |
*imagename="gforth.fi"; |
| |
progname = argv[0]; |
| |
|
| opterr=0; |
opterr=0; |
| while (1) { |
while (1) { |
| {"no-offset-im", no_argument, &offset_image, 0}, |
{"no-offset-im", no_argument, &offset_image, 0}, |
| {"clear-dictionary", no_argument, &clear_dictionary, 1}, |
{"clear-dictionary", no_argument, &clear_dictionary, 1}, |
| {"debug", no_argument, &debug, 1}, |
{"debug", no_argument, &debug, 1}, |
| {"diag", no_argument, &diag, 1}, |
{"diag", no_argument, NULL, 'D'}, |
| {"die-on-signal", no_argument, &die_on_signal, 1}, |
{"die-on-signal", no_argument, &die_on_signal, 1}, |
| {"ignore-async-signals", no_argument, &ignore_async_signals, 1}, |
{"ignore-async-signals", no_argument, &ignore_async_signals, 1}, |
| {"no-super", no_argument, &no_super, 1}, |
{"no-super", no_argument, &no_super, 1}, |
| {"no-dynamic", no_argument, &no_dynamic, 1}, |
{"no-dynamic", no_argument, &no_dynamic, 1}, |
| {"dynamic", no_argument, &no_dynamic, 0}, |
{"dynamic", no_argument, &no_dynamic, 0}, |
| |
{"code-block-size", required_argument, NULL, opt_code_block_size}, |
| {"print-metrics", no_argument, &print_metrics, 1}, |
{"print-metrics", no_argument, &print_metrics, 1}, |
| {"print-sequences", no_argument, &print_sequences, 1}, |
{"print-sequences", no_argument, &print_sequences, 1}, |
| {"ss-number", required_argument, NULL, ss_number}, |
{"ss-number", required_argument, NULL, ss_number}, |
| /* no-init-file, no-rc? */ |
/* no-init-file, no-rc? */ |
| }; |
}; |
| |
|
| c = getopt_long(argc, argv, "+i:m:d:r:f:l:p:vhoncsx", opts, &option_index); |
c = getopt_long(argc, argv, "+i:m:d:r:f:l:p:vhoncsxD", opts, &option_index); |
| |
|
| switch (c) { |
switch (c) { |
| case EOF: return; |
case EOF: return; |
| case 'c': clear_dictionary = 1; break; |
case 'c': clear_dictionary = 1; break; |
| case 's': die_on_signal = 1; break; |
case 's': die_on_signal = 1; break; |
| case 'x': debug = 1; break; |
case 'x': debug = 1; break; |
| |
case 'D': print_diag(); break; |
| case 'v': fputs(PACKAGE_STRING"\n", stderr); exit(0); |
case 'v': fputs(PACKAGE_STRING"\n", stderr); exit(0); |
| |
case opt_code_block_size: code_area_size = atoi(optarg); break; |
| case ss_number: static_super_number = atoi(optarg); break; |
case ss_number: static_super_number = atoi(optarg); break; |
| case ss_states: maxstates = max(min(atoi(optarg),MAX_STATE),1); break; |
case ss_states: maxstates = max(min(atoi(optarg),MAX_STATE),1); break; |
| #ifndef NO_DYNAMIC |
#ifndef NO_DYNAMIC |
| Engine Options:\n\ |
Engine Options:\n\ |
| --appl-image FILE Equivalent to '--image-file=FILE --'\n\ |
--appl-image FILE Equivalent to '--image-file=FILE --'\n\ |
| --clear-dictionary Initialize the dictionary with 0 bytes\n\ |
--clear-dictionary Initialize the dictionary with 0 bytes\n\ |
| |
--code-block-size=SIZE size of native code blocks [512KB]\n\ |
| -d SIZE, --data-stack-size=SIZE Specify data stack size\n\ |
-d SIZE, --data-stack-size=SIZE Specify data stack size\n\ |
| --debug Print debugging information during startup\n\ |
--debug Print debugging information during startup\n\ |
| --diag Print diagnostic information during startup\n\ |
-D, --diag Print diagnostic information during startup\n\ |
| --die-on-signal Exit instead of THROWing some signals\n\ |
--die-on-signal Exit instead of THROWing some signals\n\ |
| --dynamic Use dynamic native code\n\ |
--dynamic Use dynamic native code\n\ |
| -f SIZE, --fp-stack-size=SIZE Specify floating point stack size\n\ |
-f SIZE, --fp-stack-size=SIZE Specify floating point stack size\n\ |
| #endif |
#endif |
| #endif |
#endif |
| |
|
| static void print_diag() |
|
| { |
|
| |
|
| #if !defined(HAVE_GETRUSAGE) |
|
| fprintf(stderr, "*** missing functionality ***\n" |
|
| #ifndef HAVE_GETRUSAGE |
|
| " no getrusage -> CPUTIME broken\n" |
|
| #endif |
|
| ); |
|
| #endif |
|
| if((relocs < nonrelocs) || |
|
| #if defined(BUGGY_LL_CMP) || defined(BUGGY_LL_MUL) || defined(BUGGY_LL_DIV) || defined(BUGGY_LL_ADD) || defined(BUGGY_LL_SHIFT) || defined(BUGGY_LL_D2F) || defined(BUGGY_LL_F2D) |
|
| 1 |
|
| #else |
|
| 0 |
|
| #endif |
|
| ) |
|
| debugp(stderr, "relocs: %d:%d\n", relocs, nonrelocs); |
|
| fprintf(stderr, "*** %sperformance problems ***\n%s%s", |
|
| #if defined(BUGGY_LL_CMP) || defined(BUGGY_LL_MUL) || defined(BUGGY_LL_DIV) || defined(BUGGY_LL_ADD) || defined(BUGGY_LL_SHIFT) || defined(BUGGY_LL_D2F) || defined(BUGGY_LL_F2D) || !(defined(FORCE_REG) || defined(FORCE_REG_UNNECESSARY)) || defined(BUGGY_LONG_LONG) |
|
| "", |
|
| #else |
|
| "no ", |
|
| #endif |
|
| #if defined(BUGGY_LL_CMP) || defined(BUGGY_LL_MUL) || defined(BUGGY_LL_DIV) || defined(BUGGY_LL_ADD) || defined(BUGGY_LL_SHIFT) || defined(BUGGY_LL_D2F) || defined(BUGGY_LL_F2D) |
|
| " double-cell integer type buggy ->\n " |
|
| #ifdef BUGGY_LL_CMP |
|
| "CMP, " |
|
| #endif |
|
| #ifdef BUGGY_LL_MUL |
|
| "MUL, " |
|
| #endif |
|
| #ifdef BUGGY_LL_DIV |
|
| "DIV, " |
|
| #endif |
|
| #ifdef BUGGY_LL_ADD |
|
| "ADD, " |
|
| #endif |
|
| #ifdef BUGGY_LL_SHIFT |
|
| "SHIFT, " |
|
| #endif |
|
| #ifdef BUGGY_LL_D2F |
|
| "D2F, " |
|
| #endif |
|
| #ifdef BUGGY_LL_F2D |
|
| "F2D, " |
|
| #endif |
|
| "\b\b slow\n" |
|
| #endif |
|
| #if !(defined(FORCE_REG) || defined(FORCE_REG_UNNECESSARY)) |
|
| " automatic register allocation: performance degradation possible\n" |
|
| #endif |
|
| "", |
|
| (relocs < nonrelocs) ? "no dynamic code generation (--debug for details) -> factor 2 slowdown\n" : ""); |
|
| } |
|
| |
|
| #ifdef STANDALONE |
#ifdef STANDALONE |
| Cell data_abort_pc; |
Cell data_abort_pc; |
| |
|
| } |
} |
| #endif |
#endif |
| |
|
| int main(int argc, char **argv, char **env) |
void* gforth_pointers(Cell n) |
| { |
{ |
| #ifdef HAS_OS |
switch(n) { |
| char *path = getenv("GFORTHPATH") ? : DEFAULTPATH; |
case 0: return (void*)&gforth_SP; |
| #else |
case 1: return (void*)&gforth_FP; |
| char *path = DEFAULTPATH; |
case 2: return (void*)&gforth_LP; |
| #endif |
case 3: return (void*)&gforth_RP; |
| #ifndef INCLUDE_IMAGE |
case 4: return (void*)&gforth_UP; |
| char *imagename="gforth.fi"; |
case 5: return (void*)&gforth_engine; |
| FILE *image_file; |
#ifdef HAS_FILE |
| Address image; |
case 6: return (void*)&cstr; |
| |
case 7: return (void*)&tilde_cstr; |
| #endif |
#endif |
| int retvalue; |
case 8: return (void*)&throw_jmp_handler; |
| |
case 9: return (void*)&gforth_stacks; |
| #ifndef STANDALONE |
default: return NULL; |
| /* buffering of the user output device */ |
|
| #ifdef _IONBF |
|
| if (isatty(fileno(stdout))) { |
|
| fflush(stdout); |
|
| setvbuf(stdout,NULL,_IONBF,0); |
|
| } |
} |
| #endif |
|
| #else |
|
| prep_terminal(); |
|
| #endif |
|
| |
|
| progname = argv[0]; |
|
| |
|
| #ifndef STANDALONE |
|
| if (lt_dlinit()!=0) { |
|
| fprintf(stderr,"%s: lt_dlinit failed", progname); |
|
| exit(1); |
|
| } |
} |
| |
|
| #ifdef HAS_OS |
void gforth_printmetrics() |
| gforth_args(argc, argv, &path, &imagename); |
|
| #ifndef NO_DYNAMIC |
|
| init_ss_cost(); |
|
| #endif /* !defined(NO_DYNAMIC) */ |
|
| #endif /* defined(HAS_OS) */ |
|
| #endif |
|
| |
|
| #ifdef STANDALONE |
|
| image = gforth_engine(0, 0, 0, 0, 0 sr_call); |
|
| alloc_stacks((ImageHeader *)image); |
|
| #else |
|
| image_file = open_image_file(imagename, path); |
|
| image = gforth_loader(image_file, imagename); |
|
| #endif |
|
| gforth_header=(ImageHeader *)image; /* used in SIGSEGV handler */ |
|
| |
|
| if (diag) |
|
| print_diag(); |
|
| { |
{ |
| char path2[strlen(path)+1]; |
|
| char *p1, *p2; |
|
| Cell environ[]= { |
|
| (Cell)argc-(optind-1), |
|
| (Cell)(argv+(optind-1)), |
|
| (Cell)strlen(path), |
|
| (Cell)path2}; |
|
| argv[optind-1] = progname; |
|
| /* |
|
| for (i=0; i<environ[0]; i++) |
|
| printf("%s\n", ((char **)(environ[1]))[i]); |
|
| */ |
|
| /* make path OS-independent by replacing path separators with NUL */ |
|
| for (p1=path, p2=path2; *p1!='\0'; p1++, p2++) |
|
| if (*p1==PATHSEP) |
|
| *p2 = '\0'; |
|
| else |
|
| *p2 = *p1; |
|
| *p2='\0'; |
|
| retvalue = gforth_go(image, 4, environ); |
|
| #if defined(SIGPIPE) && !defined(STANDALONE) |
|
| bsd_signal(SIGPIPE, SIG_IGN); |
|
| #endif |
|
| #ifdef VM_PROFILING |
|
| vm_print_profile(stderr); |
|
| #endif |
|
| deprep_terminal(); |
|
| #ifndef STANDALONE |
|
| if (lt_dlexit()!=0) |
|
| fprintf(stderr,"%s: lt_dlexit failed", progname); |
|
| #endif |
|
| } |
|
| if (print_metrics) { |
if (print_metrics) { |
| int i; |
int i; |
| fprintf(stderr, "code size = %8ld\n", dyncodesize()); |
fprintf(stderr, "code size = %8ld\n", dyncodesize()); |
| fprintf(stderr, "%ld %ld lb_states\n", lb_labeler_steps, lb_newstate_new); |
fprintf(stderr, "%ld %ld lb_states\n", lb_labeler_steps, lb_newstate_new); |
| fprintf(stderr, "%ld %ld lb_table_entries\n", lb_labeler_steps, lb_labeler_dynprog); |
fprintf(stderr, "%ld %ld lb_table_entries\n", lb_labeler_steps, lb_labeler_dynprog); |
| } |
} |
| |
} |
| |
|
| |
void gforth_cleanup() |
| |
{ |
| |
#if defined(SIGPIPE) && !defined(STANDALONE) |
| |
bsd_signal(SIGPIPE, SIG_IGN); |
| |
#endif |
| |
#ifdef VM_PROFILING |
| |
vm_print_profile(stderr); |
| |
#endif |
| |
deprep_terminal(); |
| |
#ifndef STANDALONE |
| |
#ifdef HAVE_LIBLTDL |
| |
if (lt_dlexit()!=0) |
| |
fprintf(stderr,"%s: lt_dlexit failed", progname); |
| |
#endif |
| |
#endif |
| |
} |
| |
|
| |
user_area* gforth_stacks(Cell dsize, Cell rsize, Cell fsize, Cell lsize) |
| |
{ |
| |
#ifdef SIGSTKSZ |
| |
stack_t sigstack; |
| |
int sas_retval=-1; |
| |
#endif |
| |
size_t totalsize; |
| |
Cell a; |
| |
user_area * up0; |
| |
Cell dsizep = wholepage(dsize); |
| |
Cell rsizep = wholepage(rsize); |
| |
Cell fsizep = wholepage(fsize); |
| |
Cell lsizep = wholepage(lsize); |
| |
totalsize = dsizep+fsizep+rsizep+lsizep+6*pagesize; |
| |
#ifdef SIGSTKSZ |
| |
totalsize += 2*SIGSTKSZ; |
| |
#endif |
| |
a = (Cell)alloc_mmap(totalsize); |
| |
if (a != (Cell)MAP_FAILED) { |
| |
up0=(user_area*)a; a+=pagesize; |
| |
page_noaccess((void*)a); a+=pagesize; up0->sp0=a+dsize; a+=dsizep; |
| |
page_noaccess((void*)a); a+=pagesize; up0->rp0=a+rsize; a+=rsizep; |
| |
page_noaccess((void*)a); a+=pagesize; up0->fp0=a+fsize; a+=fsizep; |
| |
page_noaccess((void*)a); a+=pagesize; up0->lp0=a+lsize; a+=lsizep; |
| |
page_noaccess((void*)a); a+=pagesize; |
| |
#ifdef SIGSTKSZ |
| |
sigstack.ss_sp=(void*)a+SIGSTKSZ; |
| |
sigstack.ss_size=SIGSTKSZ; |
| |
sas_retval=sigaltstack(&sigstack,(stack_t *)0); |
| |
#if defined(HAS_FILE) || !defined(STANDALONE) |
| |
debugp(stderr,"sigaltstack: %s\n",strerror(sas_retval)); |
| |
#endif |
| |
#endif |
| |
return up0; |
| |
} |
| |
return 0; |
| |
} |
| |
|
| |
void gforth_setstacks() |
| |
{ |
| |
gforth_UP->next_task = NULL; /* mark user area as need-to-be-set */ |
| |
|
| |
/* ensure that the cached elements (if any) are accessible */ |
| |
#if !(defined(GFORTH_DEBUGGING) || defined(INDIRECT_THREADED) || defined(DOUBLY_INDIRECT) || defined(VM_PROFILING)) |
| |
gforth_UP->sp0 -= 8; /* make stuff below bottom accessible for stack caching */ |
| |
gforth_UP->fp0--; |
| |
#endif |
| |
|
| |
gforth_SP = gforth_UP->sp0; |
| |
gforth_RP = gforth_UP->rp0; |
| |
gforth_FP = gforth_UP->fp0; |
| |
gforth_LP = gforth_UP->lp0; |
| |
} |
| |
|
| |
int gforth_boot(int argc, char** argv, char* path) |
| |
{ |
| |
char *path2=malloc(strlen(path)+1); |
| |
char *p1, *p2; |
| |
|
| |
argv[optind-1] = progname; |
| |
|
| |
/* make path OS-independent by replacing path separators with NUL */ |
| |
for (p1=path, p2=path2; *p1!='\0'; p1++, p2++) |
| |
if (*p1==PATHSEP) |
| |
*p2 = '\0'; |
| |
else |
| |
*p2 = *p1; |
| |
*p2='\0'; |
| |
|
| |
*--gforth_SP=(Cell)path2; |
| |
*--gforth_SP=(Cell)strlen(path); |
| |
*--gforth_SP=(Cell)(argv+(optind-1)); |
| |
*--gforth_SP=(Cell)(argc-(optind-1)); |
| |
|
| |
debugp(stderr, "Booting Gforth: %p\n", gforth_header->boot_entry); |
| |
return gforth_go(gforth_header->boot_entry); |
| |
} |
| |
|
| |
int gforth_quit() |
| |
{ |
| |
debugp(stderr, "Quit into Gforth: %p\n", gforth_header->quit_entry); |
| |
return gforth_go(gforth_header->quit_entry); |
| |
} |
| |
|
| |
int gforth_execute(Xt xt) |
| |
{ |
| |
debugp(stderr, "Execute Gforth xt %p: %p\n", xt, gforth_header->execute_entry); |
| |
|
| |
*--gforth_SP = (Cell)xt; |
| |
|
| |
return gforth_go(gforth_header->execute_entry); |
| |
} |
| |
|
| |
Xt gforth_find(Char * name) |
| |
{ |
| |
Xt xt; |
| |
debugp(stderr, "Find '%s' in Gforth: %p\n", name, gforth_header->find_entry); |
| |
|
| |
*--gforth_SP = (Cell)name; |
| |
*--gforth_SP = strlen(name); |
| |
|
| |
xt = (Xt)gforth_go(gforth_header->find_entry); |
| |
debugp(stderr, "Found %p\n", xt); |
| |
return xt; |
| |
} |
| |
|
| |
int gforth_start(int argc, char ** argv) |
| |
{ |
| |
char *path, *imagename; |
| |
|
| |
gforth_args(argc, argv, &path, &imagename); |
| |
gforth_header = gforth_loader(imagename, path); |
| |
gforth_UP = gforth_stacks(dsize, rsize, fsize, lsize); |
| |
gforth_setstacks(); |
| |
return gforth_boot(argc, argv, path); |
| |
} |
| |
|
| |
int gforth_main(int argc, char **argv, char **env) |
| |
{ |
| |
int retvalue=gforth_start(argc, argv); |
| |
|
| |
if(retvalue > 0) { |
| |
gforth_execute(gforth_find("bootmessage")); |
| |
retvalue = gforth_quit(); |
| |
} |
| |
gforth_cleanup(); |
| |
gforth_printmetrics(); |
| |
|
| return retvalue; |
return retvalue; |
| } |
} |