/* vm interpreter wrapper Copyright (C) 2001,2002,2003 Free Software Foundation, Inc. This file is part of Gforth. Gforth is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA. */ #include "mini.h" #define USE_spTOS 1 #ifdef USE_spTOS #define IF_spTOS(x) x #else #define IF_spTOS(x) #endif #ifdef VM_DEBUG #define NAME(_x) if (vm_debug) {fprintf(vm_out, "%p: %-20s, ", ip-1, _x); fprintf(vm_out,"fp=%p, sp=%p", fp, sp);} #else #define NAME(_x) #endif /* different threading schemes for different architectures; the sparse numbering is there for historical reasons */ /* here you select the threading scheme; I have only set this up for 386 and generic, because I don't know what preprocessor macros to test for (Gforth uses config.guess instead). Anyway, it's probably best to build them all and select the fastest instead of hardwiring a specific scheme for an architecture. E.g., scheme 8 is fastest for Gforth "make bench" on a 486, whereas scheme 5 is fastest for "mini fib.mini" on an Athlon */ #ifndef THREADING_SCHEME #define THREADING_SCHEME 5 #endif /* defined(THREADING_SCHEME) */ #ifdef __GNUC__ #if THREADING_SCHEME==1 /* direct threading scheme 1: autoinc, long latency (HPPA, Sharc) */ # define NEXT_P0 ({cfa=*ip++;}) # define IP (ip-1) # define SET_IP(p) ({ip=(p); NEXT_P0;}) # define NEXT_INST (cfa) # define INC_IP(const_inc) ({cfa=IP[const_inc]; ip+=(const_inc);}) # define DEF_CA # define NEXT_P1 # define NEXT_P2 ({goto *(cfa.inst);}) #endif #if THREADING_SCHEME==3 /* direct threading scheme 3: autoinc, low latency (68K) */ # define NEXT_P0 # define IP (ip) # define SET_IP(p) ({ip=(p); NEXT_P0;}) # define NEXT_INST (*ip) # define INC_IP(const_inc) ({ip+=(const_inc);}) # define DEF_CA # define NEXT_P1 ({cfa=*ip++;}) # define NEXT_P2 ({goto *(cfa.inst);}) #endif #if THREADING_SCHEME==5 /* direct threading scheme 5: early fetching (Alpha, MIPS) */ # define CFA_NEXT # define NEXT_P0 ({cfa=*ip;}) # define IP (ip) # define SET_IP(p) ({ip=(Inst *)(p); NEXT_P0;}) # define NEXT_INST (cfa) # define INC_IP(const_inc) ({cfa=ip[const_inc]; ip+=(const_inc);}) # define DEF_CA # define NEXT_P1 (ip++) # define NEXT_P2 ({goto *(cfa.inst);}) #endif #if THREADING_SCHEME==8 /* direct threading scheme 8: i386 hack */ # define NEXT_P0 # define IP (ip) # define SET_IP(p) ({ip=(p); NEXT_P0;}) # define NEXT_INST (*IP) # define INC_IP(const_inc) ({ ip+=(const_inc);}) # define DEF_CA # define NEXT_P1 (ip++) # define NEXT_P2 ({goto *((*(ip-1)).inst);}) #endif #if THREADING_SCHEME==9 /* direct threading scheme 9: prefetching (for PowerPC) */ /* note that the "cfa=next_cfa;" occurs only in NEXT_P1, because this works out better with the capabilities of gcc to introduce and schedule the mtctr instruction. */ # define NEXT_P0 # define IP ip # define SET_IP(p) ({ip=(p); next_cfa=*ip; NEXT_P0;}) # define NEXT_INST (next_cfa) # define INC_IP(const_inc) ({next_cfa=IP[const_inc]; ip+=(const_inc);}) # define DEF_CA # define NEXT_P1 ({cfa=next_cfa; ip++; next_cfa=*ip;}) # define NEXT_P2 ({goto *(cfa.inst);}) # define MORE_VARS Cell next_cfa; #endif #if THREADING_SCHEME==10 /* direct threading scheme 10: plain (no attempt at scheduling) */ # define NEXT_P0 # define IP (ip) # define SET_IP(p) ({ip=(p); NEXT_P0;}) # define NEXT_INST (*ip) # define INC_IP(const_inc) ({ip+=(const_inc);}) # define DEF_CA # define NEXT_P1 # define NEXT_P2 ({cfa=*ip++; goto *(cfa.inst);}) #endif #define NEXT ({DEF_CA NEXT_P1; NEXT_P2;}) #define IPTOS ((NEXT_INST)) #define INST_ADDR(name) (Label)&&I_##name #define LABEL(name) I_##name: #else /* !defined(__GNUC__) */ /* use switch dispatch */ #define DEF_CA #define NEXT_P0 #define NEXT_P1 #define NEXT_P2 goto next_inst; #define SET_IP(p) (ip=(p)) #define IP ip #define NEXT_INST (*ip) #define INC_IP(const_inc) (ip+=(const_inc)) #define IPTOS NEXT_INST #define INST_ADDR(name) I_##name #define LABEL(name) case I_##name: #endif /* !defined(__GNUC__) */ #define LABEL2(x) #ifdef VM_PROFILING #define SUPER_END vm_count_block(IP) #else #define SUPER_END #endif #ifndef __GNUC__ enum { #include "mini-labels.i" }; #endif #if defined(__GNUC__) && ((__GNUC__==2 && defined(__GNUC_MINOR__) && __GNUC_MINOR__>=7)||(__GNUC__>2)) #define MAYBE_UNUSED __attribute__((unused)) #else #define MAYBE_UNUSED #endif /* the return type can be anything you want it to */ long engine(Cell *ip0, Cell *sp, char *fp) { /* VM registers (you may want to use gcc's "Explicit Reg Vars" here) */ Cell * ip; Cell cfa; #ifdef USE_spTOS Cell spTOS; #else #define spTOS (sp[0]) #endif static Label labels[] = { #include "mini-labels.i" }; #ifdef MORE_VARS MORE_VARS #endif if (vm_debug) fprintf(vm_out,"entering engine(%p,%p,%p)\n",ip0,sp,fp); if (ip0 == NULL) { vm_prim = labels; return 0; } /* I don't have a clue where these things come from, but I've put them in macros.h for the moment */ IF_spTOS(spTOS = sp[0]); SET_IP(ip0); SUPER_END; /* count the BB starting at ip0 */ #ifdef __GNUC__ NEXT; #include "mini-vm.i" #else next_inst: switch((*ip++).inst) { #include "mini-vm.i" default: fprintf(stderr,"unknown instruction %d at %p\n", ip[-1], ip-1); exit(1); } #endif }