#include <setjmp.h> #include "global.h" #include "lang.h" #include "interpret.h" #include "las.h" #include "instrs.h" #include "array.h" #include "exec.h" #include "object.h" #include "simul_efun.h" #include "incralloc.h" #include "stralloc.h" #include "main.h" #include "operators.h" #include "dynamic_buffer.h" #include "lex.h" #include "mapping.h" #include "simulate.h" #include "save_objectII.h" #define LASDEBUG int lasdebug=0; static node *eval(node *); static int eval_low(node *n); static node *optimize(node *n,int needlval,node *parent); static node *call_optimizer(node *n); static struct vector *current_switch_mapping=0; int *break_stack=0; static int current_break,break_stack_size; int *continue_stack=0; static int current_continue,continue_stack_size; struct program fake_prog; struct object fake_object; struct mem_block mem_block[NUMAREAS]; extern int exact_types; extern int num_parse_error; extern char **local_names; extern unsigned short *type_of_locals; extern int current_number_of_locals; extern int current_line; extern char *get_type_name(int); void nice_print_tree(node *); #define PC mem_block[A_PROGRAM].current_size INLINE void add_to_mem_block(int n,char *data,int size) { struct mem_block *mbp = &mem_block[n]; #ifdef MALLOC_DEBUG check_sfltable(); #endif while (mbp->current_size + size > mbp->max_size) { mbp->max_size <<= 1; mbp->block = realloc((char *)mbp->block, mbp->max_size); } MEMCPY(mbp->block + mbp->current_size, data, size); mbp->current_size += size; } void ins_byte(unsigned char b,int area) { add_to_mem_block(area, (char *)&b, 1); } /* * Store a 2 byte number. It is stored in such a way as to be sure * that correct byte order is used, regardless of machine architecture. * Also beware that some machines can't write a word to odd addresses. */ void ins_short(short l,int area) { add_to_mem_block(area, (char *)&l, sizeof(short)); } #if YYDEBUG extern int yydebug; #endif void upd_short(int offset, short l) { #if YYDEBUG if(yydebug>1) fprintf(stderr,"Upd short %d %d\n",offset,l); #endif #ifdef HANDLES_UNALIGNED_MEMORY_ACCESS *((short *)(mem_block[A_PROGRAM].block+offset))=l; #else mem_block[A_PROGRAM].block[offset + 0] = ((char *)&l)[0]; mem_block[A_PROGRAM].block[offset + 1] = ((char *)&l)[1]; #endif } void upd_branch(int offset, int tmp) { #if YYDEBUG if(yydebug>1) fprintf(stderr,"Upd branch %d %d\n",offset,tmp); #endif tmp-=offset; #ifdef HANDLES_UNALIGNED_MEMORY_ACCESS *((int *)(mem_block[A_PROGRAM].block+offset))=tmp; #else mem_block[A_PROGRAM].block[offset + 0] = ((char *)&tmp)[0]; mem_block[A_PROGRAM].block[offset + 1] = ((char *)&tmp)[1]; mem_block[A_PROGRAM].block[offset + 2] = ((char *)&tmp)[2]; mem_block[A_PROGRAM].block[offset + 3] = ((char *)&tmp)[3]; #endif } short read_short(int offset) { #ifdef HANDLES_UNALIGNED_MEMORY_ACCESS return *((short *)(mem_block[A_PROGRAM].block+offset)); #else short l; ((char *)&l)[0] = mem_block[A_PROGRAM].block[offset + 0]; ((char *)&l)[1] = mem_block[A_PROGRAM].block[offset + 1]; return l; #endif } /* * Store a 4 byte number. It is stored in such a way as to be sure * that correct byte order is used, regardless of machine architecture. */ void ins_long(long l,int area) { add_to_mem_block(area, (char *)&l+0, sizeof(long)); } #if YYDEBUG extern int yydebug; #endif static int store_linenumbers=1; static int last_line,last_pc; void start_line_numbering() { last_pc=last_line=0; } static void insert_small_number(int a,int area) { if(a>-127 && a<=127) { ins_byte(a,area); }else if(a>=-32768 && a<32768){ ins_byte(-127,area); ins_short(a,area); }else{ ins_byte(-128,area); ins_long(a,area); } } static void low_ins_f_byte(unsigned int b) { if(store_linenumbers && last_line!=current_line) { insert_small_number(PC-last_pc,A_LINENUMBERS); insert_small_number(current_line-last_line,A_LINENUMBERS); last_line=current_line; last_pc=PC; } #if YYDEBUG if(yydebug) fprintf(stderr,"Inserting f_byte %s (%d) at %d\n",get_instruction_name(b),b, PC); #endif #if defined(LASDEBUG) if(lasdebug>1) if(store_linenumbers) fprintf(stderr,"Inserting f_byte %s (%d) at %d\n",get_instruction_name(b),b, PC); #endif b-=F_OFFSET; #ifdef OPCPROF if(store_linenumbers) add_compiled(b); #endif if(b>255) { switch(b/256) { case 1: low_ins_f_byte(F_ADD_256); break; case 2: low_ins_f_byte(F_ADD_512); break; case 3: low_ins_f_byte(F_ADD_768); break; case 4: low_ins_f_byte(F_ADD_1024); break; default: low_ins_f_byte(F_ADD_256X); ins_byte(b/256,A_PROGRAM); } b&=255; } ins_byte((unsigned char)b,A_PROGRAM); } static void ins_f_byte(unsigned int b) { extern int T_flag; if(T_flag>2) low_ins_f_byte(F_WRITE_OPCODE); low_ins_f_byte(b); } static void ins_int(int i) { if ( i == 0 ) { ins_f_byte(F_CONST0); }else if ( i == 1 ) { ins_f_byte(F_CONST1); } else if ( i == -1) { ins_f_byte(F_CONST_1); } else if ( i>1 && i<258) { ins_f_byte(F_BYTE); ins_byte(i-2,A_PROGRAM); } else if ( i<-1 && i>-258) { ins_f_byte(F_NEG_BYTE); ins_byte((1024-i-2)&0xff,A_PROGRAM); } else if( i>=-37768 && i<32768){ ins_f_byte(F_SHORT); ins_short(i,A_PROGRAM); }else{ ins_f_byte(F_NUMBER); ins_long(i,A_PROGRAM); } } void ins_float(float f) { ins_f_byte(F_FLOAT); add_to_mem_block(A_PROGRAM,(char *)&f,sizeof(float)); } /* * A mechanism to remember addresses on a stack. The size of the stack is * defined in config.h. */ int comp_stackp; int comp_stack[COMPILER_STACK_SIZE]; void push_address() { if (comp_stackp >= COMPILER_STACK_SIZE) { yyerror("Compiler stack overflow"); comp_stackp++; return; } comp_stack[comp_stackp++] = PC; } void push_explicit(int address) { if (comp_stackp >= COMPILER_STACK_SIZE) { yyerror("Compiler stack overflow"); comp_stackp++; return; } comp_stack[comp_stackp++] = address; } int pop_address() { if (comp_stackp == 0) fatal("Compiler stack underflow.\n"); if (comp_stackp > COMPILER_STACK_SIZE) { --comp_stackp; return 0; } return comp_stack[--comp_stackp]; } short store_prog_string(char *str) { short i; char **p; p = (char **) mem_block[A_STRINGS].block; for (i=mem_block[A_STRINGS].current_size / sizeof str -1; i>=0; --i) if (p[i] == str) return i; str=copy_shared_string(str); add_to_mem_block(A_STRINGS, (char *)&str, sizeof str); return mem_block[A_STRINGS].current_size / sizeof str - 1; } int store_constant(struct svalue *foo) { struct svalue *s,tmp; int size,e; s=(struct svalue *)mem_block[A_CONSTANTS].block; size=mem_block[A_CONSTANTS].current_size / sizeof(struct svalue); for(e=0;e<size;e++) if(is_equal(s+e,foo)) return e; assign_svalue_no_free(&tmp,foo); add_to_mem_block(A_CONSTANTS,(char *)&tmp,sizeof(struct svalue)); return size; } #ifdef LINMALLOC_DEBUG extern void assert_malloced_memory(void *); void assert_node(node *n) { if(!n) return; assert_malloced_memory((char *)n); switch(n->token) { case F_CONSTANT: /* assert_svalue_malloced(&(n->u.sval)); */ break; case F_STRING: /* assert_malloced_memory(n->u.s.a.str); */ break; default: if(n->opt_info & OPT_A_IS_NODE) assert_node(n->u.s.a.node); if(n->opt_info & OPT_B_IS_NODE) assert_node(n->u.s.b.node); } } #endif #define MAX_GLOBAL 256 #define MAX(X,Y) ((X)<(Y)?(Y):(X)) static int max_local_vital=-1; static int max_global_vital=-1; struct nodepair { int used; node *assignment; node *index; struct nodepair *active; }; static struct nodepair *active; static struct nodepair local_vital[MAX_LOCAL]; static struct nodepair global_vital[MAX_GLOBAL]; static void reset_remember(struct nodepair *n) { while(n) { struct nodepair *p; n->assignment=NULL; p=n->active; n->active=NULL; n=p; } } static void clear_remember_local() { while(max_local_vital>=0) reset_remember(local_vital+max_local_vital--); } static void clear_remember_global() { while(max_global_vital>=0) reset_remember(global_vital+max_global_vital--); } static void clear_remember() { clear_remember_local(); clear_remember_global(); } struct nodepair *find_remember(node *a) { switch(a->token) { case F_ASSIGN_AND_POP: case F_ASSIGN: return find_remember(a->u.s.b.node); case F_INC: case F_POST_INC: case F_INC_AND_POP: case F_DEC: case F_POST_DEC: case F_DEC_AND_POP: case F_INDEX: return find_remember(a->u.s.a.node); case F_LOCAL: return local_vital+a->u.s.a.number; case F_GLOBAL: return global_vital+a->u.s.a.number; default: return NULL; } } static void remember_remember(node *b,int used) { node *a; a=b; while(1) { switch(a->token) { case F_ASSIGN_AND_POP: case F_ASSIGN: a=a->u.s.b.node; break; case F_INC: case F_POST_INC: case F_INC_AND_POP: case F_DEC: case F_POST_DEC: case F_DEC_AND_POP: case F_INDEX: a=a->u.s.a.node; break; case F_LOCAL: local_vital[a->u.s.a.number].assignment=b; local_vital[a->u.s.a.number].index=a; local_vital[a->u.s.a.number].used=used; local_vital[a->u.s.a.number].active=active; max_local_vital=MAX(max_local_vital,a->u.s.a.number); return; case F_GLOBAL: global_vital[a->u.s.a.number].assignment=b; global_vital[a->u.s.a.number].index=a; global_vital[a->u.s.a.number].used=used; global_vital[a->u.s.a.number].active=active; max_global_vital=MAX(max_global_vital,a->u.s.a.number); return; default: return; } } } void free_node(node *n) { if(!n) return; #ifdef MALLOC_DEBUG check_sfltable(); #endif switch(n->token) { case F_LOCAL: if(local_vital[n->u.s.a.number].index==n) reset_remember(local_vital+n->u.s.a.number); break; case F_GLOBAL: if(global_vital[n->u.s.a.number].index==n) reset_remember(global_vital+n->u.s.a.number); break; case F_CONSTANT: free_svalue(&(n->u.sval)); break; case F_STRING: free_string(n->u.s.a.str); break; default: if(n->opt_info & OPT_A_IS_NODE) free_node(n->u.s.a.node); if(n->opt_info & OPT_B_IS_NODE) free_node(n->u.s.b.node); } free((char *)n); } node *mknode(short token,node *a,node *b) { node *res; res=(node *)xalloc(sizeof(node)); res->u.s.a.node=a; res->u.s.b.node=b; res->token=token; res->type=0; res->opt_info=OPT_A_IS_NODE | OPT_B_IS_NODE; res->lnum=current_line; return res; } node *mkstrnode(char *str) { node *res; res=(node *)xalloc(sizeof(node)); res->token=F_STRING; res->type=TYPE_STRING; res->opt_info=OPT_CONST | OPT_OPT_COMPUTED | OPT_OPTIMIZED; #ifdef DEBUG if(str!=debug_findstring(str)) fatal("Mkstrnode on nonshared string.\n"); #endif res->u.s.a.str=str; res->lnum=current_line; return res; } node *mkintnode(int nr) { node *res; res=(node *)xalloc(sizeof(node)); res->token=F_NUMBER; if(nr) res->type=TYPE_NUMBER; else res->type=TYPE_ANY; res->opt_info=OPT_CONST | OPT_OPT_COMPUTED | OPT_OPTIMIZED; res->u.s.a.number=nr; res->lnum=current_line; return res; } node *mkfloatnode(float foo) { node *res; res=(node *)xalloc(sizeof(node)); res->token=F_FLOAT; res->type=TYPE_FLOAT; res->opt_info=OPT_CONST | OPT_OPT_COMPUTED | OPT_OPTIMIZED; res->u.s.a.fnum=foo; res->lnum=current_line; return res; } node *mkconstfunnode(int fun) { node *res; res=(node *)xalloc(sizeof(node)); res->token=F_CONSTANT_FUNCTION; res->type=TYPE_FUNCTION; res->opt_info=OPT_CONST; res->u.s.a.number=fun; res->lnum=current_line; return res; } node *mkglobalnode(int var) { node *res; res=(node *)xalloc(sizeof(node)); res->token=F_GLOBAL; res->type=VARIABLE(var)->type & TYPE_MOD_MASK; res->opt_info=OPT_COULD_BE_CONST; res->u.s.a.number=var; res->lnum=current_line; return res; } node *mklocalnode(int var) { node *res; res=(node *)xalloc(sizeof(node)); res->token=F_LOCAL; res->type=type_of_locals[var] & TYPE_MOD_MASK; res->opt_info=OPT_COULD_BE_CONST; res->u.s.a.number=var; res->lnum=current_line; return res; } node *mksimulefunnode(int fun) { node *res; res=(node *)xalloc(sizeof(node)); res->token=F_PUSH_SIMUL_EFUN; res->type=TYPE_FUNCTION; res->opt_info=OPT_CONST; res->u.s.a.number=fun; res->lnum=current_line; return res; } node *mkcastnode(int type,node *n) { node *res; res=(node *)xalloc(sizeof(node)); res->token=F_CAST; res->type=type; res->opt_info=OPT_A_IS_NODE; res->u.s.a.node=n; res->u.s.b.node=NULL; res->lnum=current_line; return res; } node *mkefunnode(int efun,node *args) { node *res; res=(node *)xalloc(sizeof(node)); res->token=F_EFUN_CALL; res->type=instrs[efun-F_OFFSET].ret_type; res->opt_info=OPT_B_IS_NODE; if(instrs[efun-F_OFFSET].ret_type & TYPE_MOD_CONSTANT) { res->opt_info|=OPT_CONST; }else{ res->opt_info|=OPT_SIDE_EFFECT; } res->u.s.a.number=efun; res->u.s.b.node=args; res->lnum=current_line; return res; } static int can_use_constants(struct svalue *s,int svalues) { int e; for(e=0;e<svalues;e++) { switch(s[e].type) { case T_FUNCTION: case T_OBJECT: return 0; case T_LIST: case T_MAPPING: case T_POINTER: case T_ALIST_PART: if(!can_use_constants(s[e].u.vec->item,s[e].u.vec->size)) return 0; } } return 1; } int node_is_eq(node *a,node *b) { if(a==b) return 1; if(!a || !b) return 0; if(a->token!=b->token) return 0; switch(a->token) { case F_STRING: return a->u.s.a.str==b->u.s.a.str; case F_CONSTANT_FUNCTION: case F_PUSH_SIMUL_EFUN: case F_GLOBAL: case F_LOCAL: case F_NUMBER: return a->u.s.a.number==b->u.s.a.number; case F_FLOAT: return a->u.s.a.fnum==b->u.s.a.fnum; case F_CAST: return a->type==b->type && a->opt_info==b->opt_info && node_is_eq(a->u.s.a.node,b->u.s.a.node); case F_EFUN_CALL: return a->u.s.a.number==b->u.s.a.number && a->type==b->type && a->opt_info==b->opt_info && node_is_eq(a->u.s.b.node,b->u.s.b.node); case F_CONSTANT: return is_equal(&(a->u.sval),&(b->u.sval)); default: return a->type==b->type && a->opt_info==b->opt_info && ((a->opt_info & OPT_A_IS_NODE) && node_is_eq(a->u.s.a.node,b->u.s.a.node)) && ((a->opt_info & OPT_B_IS_NODE) && node_is_eq(a->u.s.b.node,b->u.s.b.node)); } } static node *can_use_allocate(struct vector *v) { int e; node *n,*n2; if(!v->size) return mkintnode(0); if(IS_ZERO(v->item)) { for(e=1;e<v->size;e++) if(!IS_ZERO(v->item+e)) return NULL; return mkintnode(v->size); }else if(v->item->type==T_POINTER){ n=can_use_allocate(v->item->u.vec); if(!n) return NULL; for(e=1;e<v->size;e++) { n2=NULL; if(v->item[e].type!=T_POINTER || !node_is_eq(n,n2=can_use_allocate(v->item[e].u.vec))) { free_node(n); free_node(n2); return NULL; } free_node(n2); } return mknode(F_ARG_LIST,mkintnode(v->size),n); } return NULL; } node *mksvaluenode(struct svalue *s) { node *res; int e; switch(s->type) { default: fatal("Unknown type?\n"); break; case T_STRING: return mkstrnode(copy_shared_string(strptr(s))); case T_INT: if(s->subtype) { res=(node *)xalloc(sizeof(node)); res->token=F_CONSTANT; res->type=TYPE_NUMBER; res->opt_info=OPT_CONST; res->lnum=current_line; assign_svalue_no_free(&(res->u.sval),s); return res; } return mkintnode(s->u.number); case T_FLOAT: return mkfloatnode(s->u.fnum); case T_REGEXP: res=(node *)xalloc(sizeof(node)); res->token=F_CONSTANT; res->opt_info=OPT_CONST; res->lnum=current_line; assign_svalue_no_free(&(res->u.sval),s); res->type=TYPE_REGULAR_EXPRESSION; return res; case T_POINTER: if((res=can_use_allocate(s->u.vec))) { res=mkefunnode(F_ALLOCATE,res); res->type=TYPE_ANY | TYPE_MOD_POINTER; return res; } /* FALL THROUGH */ case T_ALIST_PART: case T_LIST: case T_MAPPING: if(can_use_constants(s,1)) { res=(node *)xalloc(sizeof(node)); res->token=F_CONSTANT; res->lnum=current_line; assign_svalue_no_free(&(res->u.sval),s); if(res->u.sval.type==T_ALIST_PART) res->u.sval.type=T_POINTER; res->opt_info=OPT_CONST; if(s->type==T_POINTER || s->type==T_ALIST_PART) res->type=TYPE_ANY | TYPE_MOD_POINTER; else if(s->type==T_LIST) res->type=TYPE_LIST; else res->type=TYPE_MAPPING; return res; }else{ switch(s->type) { case T_POINTER: case T_ALIST_PART: if(!s->u.vec->size) { res=mkefunnode(F_AGGREGATE,NULL); }else{ res=mksvaluenode(s->u.vec->item); for(e=1;e<s->u.vec->size;e++) res=mknode(F_ARG_LIST,res,mksvaluenode(s->u.vec->item+e)); res=mkefunnode(F_AGGREGATE,res); res->type=TYPE_ANY | TYPE_MOD_POINTER; return res; } case T_MAPPING: if(!s->u.vec->item[0].u.vec->size) { res=mkefunnode(F_M_AGGREGATE,NULL); }else{ res=mkefunnode(F_MKMAPPING, mknode(F_ARG_LIST,mksvaluenode(s->u.vec->item), mksvaluenode(s->u.vec->item+1))); res->type=TYPE_MAPPING; } return res; case T_LIST: if(!s->u.vec->item[0].u.vec->size) { res=mkefunnode(F_L_AGGREGATE,NULL); }else{ res=mksvaluenode(s->u.vec->item[0].u.vec->item); for(e=1;e<s->u.vec->size;e++) res=mknode(F_ARG_LIST,res, mksvaluenode(s->u.vec->item[0].u.vec->item+e)); res=mkefunnode(F_L_AGGREGATE,res); res->type=TYPE_LIST; } return res; } } return NULL; case T_OBJECT: if(s->u.ob==&fake_object) { return mkefunnode(F_THIS_OBJECT,0); }else{ res=(node *)xalloc(sizeof(node)); res->lnum=current_line; res->token=F_CONSTANT; assign_svalue_no_free(&(res->u.sval),s); res->opt_info=OPT_CONST; res->type=TYPE_OBJECT; return res; } case T_FUNCTION: if(s->u.ob==&fake_object) { return mkconstfunnode(s->subtype); }else{ int e; for(e=0;e<num_simul_efuns;e++) if(!MEMCMP((char *)s,(char *)&(simul_efuns[e].fun),sizeof(struct svalue))) break; if(e<num_simul_efuns){ return mksimulefunnode(e); }else{ res=(node *)xalloc(sizeof(node)); res->lnum=current_line; res->token=F_CONSTANT; assign_svalue_no_free(&(res->u.sval),s); res->opt_info=OPT_CONST; res->type=TYPE_FUNCTION; return res; } } } return 0; } /* here comes the typechecking */ /* note that the typechecking does certain casts that won't be made */ /* if typechecking is turned off. */ static int cant_check_types; static int typep; static int types[256]; static void get_types(node *n) { if(typep>255 || !n) return; #ifdef MALLOC_DEBUG check_sfltable(); #endif switch(n->token) { case F_ARG_LIST: get_types(n->u.s.a.node); get_types(n->u.s.b.node); break; case F_PUSH_ARRAY: cant_check_types=1; break; default: if(n->type!=TYPE_VOID) types[typep++]=n->type; } #ifdef MALLOC_DEBUG check_sfltable(); #endif } static int match_arg(int *argp,int type) { for(;*argp;argp++) if(TYPE(type,*argp)) return 1; return 0; } static int check_efun_types(int **argp,int offset,int left,node **n,char *name,int *changed) { if(!*n) return offset; if(offset==left) return offset; if((*n)->token==F_ARG_LIST) { offset=check_efun_types(argp,offset,left,&((*n)->u.s.a.node),name,changed); offset=check_efun_types(argp,offset,left,&((*n)->u.s.b.node),name,changed); return offset; } if(!match_arg(*argp,(*n)->type)) { if((*n)->type==TYPE_STRING && match_arg(*argp,TYPE_OBJECT)) { *n=mkcastnode(TYPE_OBJECT,*n); *changed=1; }else if((*n)->type==TYPE_STRING && match_arg(*argp,TYPE_FUNCTION)) { *changed=1; *n=mkcastnode(TYPE_FUNCTION,*n); *n=optimize(*n,0,0); }else if((*n)->type==TYPE_STRING && match_arg(*argp,TYPE_REGULAR_EXPRESSION)) { *n=mkcastnode(TYPE_REGULAR_EXPRESSION,*n); *n=optimize(*n,0,0); *changed=1; }else{ char buf[200]; sprintf(buf, "Bad argument %d type to efun %s()",offset+1,name); yyerror(buf); } } while(**argp) (*argp)++; (*argp)++; return offset+1; } static void check_args_for_summation(node *n) { int e,type; char buf[200]; type=types[0]; if(typep<1) { sprintf(buf,"Too few arguments to sumation."); yyerror(buf); }else{ for(e=0;e<typep;e++) { types[e]&=TYPE_MOD_MASK; if(types[e]==TYPE_FUNCTION || types[0]==TYPE_OBJECT) { sprintf(buf,"Bad argument %d to + (%s).",e+1,get_type_name(types[e])); yyerror(buf); } if(type==TYPE_ANY) continue; if(types[e]==TYPE_ANY) { type=TYPE_ANY; continue; } if(types[e]==type) continue; if(types[e] & TYPE_MOD_POINTER) type=TYPE_MOD_POINTER; if(types[e]&type&TYPE_MOD_POINTER) continue; switch(type) { case TYPE_STRING: if(types[e]==TYPE_NUMBER || types[e]==TYPE_FLOAT) continue; break; case TYPE_NUMBER: case TYPE_FLOAT: if(types[e]==TYPE_STRING) { type=TYPE_STRING; continue; } break; } } if(type!=TYPE_ANY) { for(e=0;e<typep;e++) { switch(type) { case TYPE_STRING: if(types[e]!=TYPE_STRING && types[e]!=TYPE_NUMBER && types[e]!=TYPE_FLOAT) { sprintf(buf,"Bad argument %d to + (%s).",e+1, get_type_name(types[e])); yyerror(buf); } break; case TYPE_MOD_POINTER: if(!(types[e]&TYPE_MOD_POINTER)) { sprintf(buf,"Bad argument %d to + (%s).",e+1, get_type_name(types[e])); yyerror(buf); } break; case TYPE_LIST: case TYPE_MAPPING: case TYPE_NUMBER: case TYPE_FLOAT: if(types[e]!=type) { sprintf(buf,"Bad argument %d to + (%s).",e+1, get_type_name(types[e])); yyerror(buf); } break; } } } } n->type=type; } static int check_types(node *n) { char buf[200]; if(!n) return 0; typep=0; cant_check_types=0; #ifdef LINMALLOC_DEBUG assert_node(n); #endif #ifdef MALLOC_DEBUG check_sfltable(); #endif switch(n->token) { case F_INDEX: get_types(n->u.s.a.node); get_types(n->u.s.b.node); if(typep!=2) { sprintf(buf,"Wrong number of arguments to %s.",get_f_name(n->token)); yyerror(buf); } if(types[0]==TYPE_STRING || (types[0]&TYPE_MOD_POINTER)) { if(!TYPE(types[1],TYPE_NUMBER)) { sprintf(buf,"Index not integer."); yyerror(buf); } if(types[0]==TYPE_STRING) { n->type=TYPE_NUMBER; }else{ n->type=types[0] & ~ TYPE_MOD_POINTER; } }else if(types[0]==TYPE_OBJECT || types[0]==TYPE_FUNCTION || types[0]==TYPE_NUMBER || types[0]==TYPE_FLOAT) { sprintf(buf,"Indexing on illegal type."); yyerror(buf); }else{ n->type=TYPE_ANY; } break; case F_ADD: get_types(n->u.s.a.node); get_types(n->u.s.b.node); check_args_for_summation(n); break; case F_SUBTRACT: case F_AND: case F_XOR: case F_OR: get_types(n->u.s.a.node); get_types(n->u.s.b.node); if(typep!=2) { sprintf(buf,"Wrong number of arguments to %s.",get_f_name(n->token)); yyerror(buf); } if(types[0]==TYPE_FUNCTION || types[0]==TYPE_FUNCTION || (n->token!=F_SUBTRACT && types[0]==TYPE_FLOAT)) { sprintf(buf,"Bad argument 1 to %s.",get_f_name(n->token)); yyerror(buf); } if(!TYPE(types[0],types[1])) { sprintf(buf,"Bad arg 2 to %s.",get_f_name(n->token)); yyerror(buf); } n->type=types[0]; break; case F_MULTIPLY: get_types(n->u.s.a.node); get_types(n->u.s.b.node); if(typep!=2) { sprintf(buf,"Wrong number of arguments to %s.",get_f_name(n->token)); yyerror(buf); } if(types[0]==TYPE_FUNCTION || types[0]==TYPE_OBJECT || types[0]==TYPE_LIST || types[0]==TYPE_MAPPING) { sprintf(buf,"Bad arg 1 to *."); yyerror(buf); } if(types[1]==TYPE_FUNCTION || types[1]==TYPE_OBJECT || types[1]==TYPE_LIST || types[1]==TYPE_MAPPING) { sprintf(buf,"Bad arg 2 to *."); yyerror(buf); } if((types[0]&TYPE_MOD_POINTER) && types[1]==TYPE_STRING) { n->type=TYPE_STRING; }else if(types[0]==TYPE_NUMBER && types[1]==TYPE_NUMBER){ n->type=TYPE_NUMBER; }else if(types[0]==TYPE_FLOAT && types[1]==TYPE_FLOAT){ n->type=TYPE_FLOAT; }else{ n->type=TYPE_ANY; } break; case F_DIVIDE: get_types(n->u.s.a.node); get_types(n->u.s.b.node); if(typep!=2) { sprintf(buf,"Wrong number of arguments to %s.",get_f_name(n->token)); yyerror(buf); } if(types[0]==TYPE_FUNCTION || types[0]==TYPE_OBJECT || types[0]==TYPE_LIST || types[0]==TYPE_MAPPING) { sprintf(buf,"Bad arg 1 to /."); yyerror(buf); } if(types[1]==TYPE_FUNCTION || types[1]==TYPE_OBJECT || types[1]==TYPE_LIST || types[1]==TYPE_MAPPING) { sprintf(buf,"Bad arg 2 to /."); yyerror(buf); } if(types[0]==TYPE_STRING && types[1]==TYPE_STRING) { n->type=TYPE_STRING | TYPE_MOD_POINTER; }else if(types[0]==TYPE_NUMBER && types[1]==TYPE_NUMBER){ n->type=TYPE_NUMBER; }else if(types[0]==TYPE_FLOAT && types[1]==TYPE_FLOAT){ n->type=TYPE_FLOAT; }else{ n->type=TYPE_ANY; } break; case F_NEGATE: get_types(n->u.s.a.node); if(typep!=1) { sprintf(buf,"Wrong number of arguments to %s.",get_f_name(n->token)); yyerror(buf); } if(!BASIC_TYPE(types[0],TYPE_NUMBER) && !BASIC_TYPE(types[0],TYPE_FLOAT)) { sprintf(buf,"Bad 1 arg to subtract."); yyerror(buf); } n->type=types[0]; break; case F_COMPL: get_types(n->u.s.a.node); if(typep!=1) { sprintf(buf,"Wrong number of arguments to %s.",get_f_name(n->token)); yyerror(buf); } if(!BASIC_TYPE(types[0],TYPE_NUMBER)) { sprintf(buf,"Bad 1 arg to ~."); yyerror(buf); } n->type=types[0]; break; case F_EQ: case F_NE: get_types(n->u.s.a.node); get_types(n->u.s.b.node); if(typep!=2) { sprintf(buf,"Wrong number of arguments to %s.",get_f_name(n->token)); yyerror(buf); } n->type=TYPE_NUMBER; break; case F_GT: case F_GE: case F_LT: case F_LE: get_types(n->u.s.a.node); get_types(n->u.s.b.node); if(typep!=2) { sprintf(buf,"Wrong number of arguments to %s.",get_f_name(n->token)); yyerror(buf); } if(!BASIC_TYPE(types[0],TYPE_NUMBER) && !BASIC_TYPE(types[0],TYPE_FLOAT) && !BASIC_TYPE(types[1],TYPE_STRING)) { sprintf(buf,"Bad arg 1 to %s.",get_f_name(n->token)); yyerror(buf); } if(!BASIC_TYPE(types[0],types[1])) { sprintf(buf,"Bad arg 2 to %s.",get_f_name(n->token)); yyerror(buf); } n->type=TYPE_NUMBER; break; case F_NOT: n->type=TYPE_NUMBER; break; case F_MOD: get_types(n->u.s.a.node); get_types(n->u.s.b.node); if(typep!=2) { sprintf(buf,"Wrong number of arguments to %s.",get_f_name(n->token)); yyerror(buf); } if(!BASIC_TYPE(types[0],TYPE_NUMBER) && ! BASIC_TYPE(types[0],TYPE_FLOAT)) { sprintf(buf,"Bad arg 1 to %s.",get_f_name(n->token)); yyerror(buf); } if(!TYPE(types[0],types[1])) { sprintf(buf,"Bad argument 2 to %s.",get_f_name(n->token)); yyerror(buf); } n->type=types[0]; break; case F_RSH: case F_LSH: get_types(n->u.s.a.node); get_types(n->u.s.b.node); if(typep!=2) { sprintf(buf,"Wrong number of arguments to %s.",get_f_name(n->token)); yyerror(buf); } if(!BASIC_TYPE(types[0],TYPE_NUMBER)) { sprintf(buf,"Bad arg 1 to %s.",get_f_name(n->token)); yyerror(buf); } if(!BASIC_TYPE(types[0],TYPE_NUMBER)) { sprintf(buf,"Bad arg 2 to %s.",get_f_name(n->token)); yyerror(buf); } n->type=TYPE_NUMBER; break; case F_SSCANF: get_types(n->u.s.a.node); if(typep!=2) { sprintf(buf,"Wrong number of arguments to sscanf."); yyerror(buf); } if(!BASIC_TYPE(types[0],TYPE_STRING)) { sprintf(buf,"Bad arg 1 to sscanf."); yyerror(buf); } if(!BASIC_TYPE(types[1],TYPE_STRING)) { sprintf(buf,"Bad arg 2 to sscanf."); yyerror(buf); } n->type=TYPE_NUMBER; break; case F_SWAP: n->type=TYPE_VOID; break; case F_LAND: case F_LOR: get_types(n->u.s.a.node); get_types(n->u.s.b.node); if(typep!=2) { sprintf(buf,"Wrong number of arguments to %s.",get_f_name(n->token)); yyerror(buf); } if(types[0]==types[1]) n->type=types[0]; else n->type=TYPE_ANY; break; case F_ASSIGN: case F_ASSIGN_AND_POP: get_types(n->u.s.b.node); get_types(n->u.s.a.node); if(!TYPE(types[0],types[1])) yyerror("Different types to assign."); if(n->token==F_ASSIGN) { n->type=types[1]; }else{ n->type=TYPE_VOID; } break; case F_INC: case F_DEC: case F_POST_INC: case F_POST_DEC: get_types(n->u.s.a.node); if(typep!=1) { sprintf(buf,"Wrong number of arguments to %s.",get_f_name(n->token)); yyerror(buf); } n->type=TYPE_NUMBER; break; case F_EFUN_CALL: { int min,max,def,ret,*argp; extern int efun_arg_types[]; get_types(n->u.s.b.node); #ifdef MALLOC_DEBUG check_sfltable(); #endif if(n->u.s.a.number==F_SUM) { if(cant_check_types) break; check_args_for_summation(n); break; } min=instrs[n->u.s.a.number-F_OFFSET].min_arg; max=instrs[n->u.s.a.number-F_OFFSET].max_arg; def=instrs[n->u.s.a.number-F_OFFSET].Default; ret=instrs[n->u.s.a.number-F_OFFSET].ret_type; if(cant_check_types) { if(min==max) yyerror("Cannot use '@' on efun with fixed number of arguments."); break; } argp = &efun_arg_types[instrs[n->u.s.a.number-F_OFFSET].arg_index]; ret&=TYPE_MOD_MASK; n->type=ret; if(def && typep==min-1) { node *tmp; if(instrs[def-F_OFFSET].efunc) { tmp=mkefunnode(def,0); }else{ switch(def) { case F_CONST0: tmp=mkintnode(0); break; case F_CONST1: tmp=mkintnode(1); break; case F_CONST_1: tmp=mkintnode(-1); break; default: fatal("Illegal 'default' (%d) for opcode %d\n",def,n->u.s.a.number); tmp=mkintnode(0); /* oh well.. */ } } n->u.s.b.node=mknode(F_ARG_LIST,n->u.s.b.node,tmp); n->opt_info&=~(OPT_OPT_COMPUTED | OPT_OPTIMIZED); n->u.s.b.node=optimize(n->u.s.b.node,0,0); typep=0; get_types(n->u.s.b.node); } if(typep<min) { sprintf(buf, "Too few arguments to %s", get_f_name(n->u.s.a.number)); yyerror(buf); } if(typep>max && max!=-1) { sprintf(buf, "Too many arguments to %s", get_f_name(n->u.s.a.number)); yyerror(buf); } #ifdef MALLOC_DEBUG check_sfltable(); #endif if(exact_types) { int foo=0; check_efun_types(&argp,0,max==-1?min:typep,&(n->u.s.b.node),get_f_name(n->u.s.a.number),&foo); return foo; } break; } case '?': get_types(n->u.s.a.node); get_types(n->u.s.b.node->u.s.a.node); get_types(n->u.s.b.node->u.s.b.node); if(types[0]==TYPE_VOID) yyerror("Unexpected void argument to if or ?>\n"); if(typep!=3) n->type=TYPE_VOID; else if(types[1]==types[2]) n->type=types[1]; else n->type=TYPE_ANY; break; default: if(!n->type) n->type=TYPE_ANY; } #ifdef MALLOC_DEBUG check_sfltable(); #endif return 0; } /* these routines operates on parsetrees and are mostly used by the * optimizer */ node *copy_node(node *n) { node *b; #ifdef MALLOC_DEBUG check_sfltable(); #endif if(!n) return n; switch(n->token) { case F_NUMBER: case F_FLOAT: case F_LOCAL: case F_GLOBAL: case F_CONSTANT_FUNCTION: case F_PUSH_SIMUL_EFUN: b=mkintnode(0); *b=*n; return b; case F_STRING: b=mkstrnode(copy_shared_string(n->u.s.a.str)); break; case F_EFUN_CALL: b=mkefunnode(n->u.s.a.number,copy_node(n->u.s.b.node)); break; case F_CAST: b=mkcastnode(n->type,copy_node(n->u.s.a.node)); break; case F_CONSTANT: b=mksvaluenode(&(n->u.sval)); break; default: if((n->opt_info & OPT_A_IS_NODE) && (n->opt_info & OPT_B_IS_NODE)) b=mknode(n->token,copy_node(n->u.s.a.node),copy_node(n->u.s.b.node)); else if(n->opt_info & OPT_A_IS_NODE) b=mknode(n->token,copy_node(n->u.s.a.node),n->u.s.b.node); else if(n->opt_info & OPT_B_IS_NODE) b=mknode(n->token,n->u.s.a.node,copy_node(n->u.s.b.node)); else b=mknode(n->token,n->u.s.a.node,n->u.s.b.node); } b->type=n->type; b->lnum=n->lnum; b->opt_info=n->opt_info; return b; } int is_const(node *n) { return (n->opt_info & OPT_CONST) && !(n->opt_info & (OPT_SIDE_EFFECT | OPT_CASE | OPT_CONT | OPT_BREAK | OPT_LOCAL_ASSIGNMENT | OPT_GLOBAL_ASSIGNMENT | OPT_RESTORE_OBJECT | OPT_RETURN )); } /* this one supposes that the value is optimized */ int node_is_true(node *n) { if(!n) return 0; switch(n->token) { case F_NUMBER: return n->u.s.a.number!=0; case F_FLOAT: /* return n->u.s.a.fnum!=0.0; /wrong */ case F_STRING: return 1; case F_CONSTANT: { switch(n->u.sval.type) { case F_NUMBER: return n->u.sval.u.number!=0; case F_FLOAT: return n->u.sval.u.fnum!=0.0; default: return 1; } } default: return 0; } } /* this one supposes that the value is optimized */ int node_is_false(node *n) { if(!n) return 0; switch(n->token) { case F_NUMBER: return !n->u.s.a.number; case F_FLOAT: return n->u.s.a.fnum==0.0; case F_CONSTANT: { switch(n->u.sval.type) { case F_NUMBER: return n->u.sval.u.number==0; case F_FLOAT: return n->u.sval.u.fnum==0.0; default: return 1; } } default: return 0; } } static node **last_cmd(node **a) { node **n; if(!a || !*a) return (node **)NULL; if((*a)->token==F_CAST) return last_cmd(&(*a)->u.s.a.node); if((*a)->token!='{' && (*a)->token!=F_ARG_LIST) return a; if((*a)->u.s.b.node) { if((*a)->u.s.b.node->token!='{' && (*a)->u.s.b.node->token!=F_CAST && (*a)->u.s.a.node->token!=F_ARG_LIST) return &((*a)->u.s.b.node); if((n=last_cmd(&(*a)->u.s.b.node))) return n; } if((*a)->u.s.a.node) { if((*a)->u.s.a.node->token!='{' && (*a)->u.s.a.node->token!=F_CAST && (*a)->u.s.a.node->token!=F_ARG_LIST) return &((*a)->u.s.a.node); if((n=last_cmd(&(*a)->u.s.a.node))) return n; } return 0; } static node **low_get_arg(node **a,int *nr) { node **n; if(a[0]->token!=F_ARG_LIST) { if(!(*nr)--) return a; else return NULL; } if(a[0]->u.s.a.node) if((n=low_get_arg(&(a[0]->u.s.a.node),nr))) return n; if(a[0]->u.s.b.node) if((n=low_get_arg(&(a[0]->u.s.b.node),nr))) return n; return 0; } static node **my_get_arg(node **a,int n) { return low_get_arg(a,&n); } static node **first_arg(node **a) { return my_get_arg(a,0); } void print_tree(node *foo,int needlval) { if(!foo) return; #ifdef LINMALLOC_DEBUG assert_node(foo); #endif switch(foo->token) { case F_CONSTANT_FUNCTION: printf("%s",FUNC(&fake_prog,foo->u.s.a.number)->name); break; case F_LOCAL: if(needlval) putchar('&'); printf("$%d",foo->u.s.a.number); break; case '?': printf("("); print_tree(foo->u.s.a.node,0); printf(")?("); print_tree(foo->u.s.b.node->u.s.a.node,0); printf("):("); print_tree(foo->u.s.b.node->u.s.b.node,0); printf(")"); break; case F_GLOBAL: if(needlval) putchar('&'); printf("%s",VARIABLE(foo->u.s.a.number)->name); break; case F_NUMBER: printf("%d",foo->u.s.a.number); break; case F_FLOAT: printf("%f",foo->u.s.a.fnum); break; case F_STRING: printf("\"%s\"",foo->u.s.a.str); break; case F_ASSIGN: case F_ASSIGN_AND_POP: print_tree(foo->u.s.b.node,1); printf("="); print_tree(foo->u.s.a.node,0); break; case F_CAST: printf("(%s){",get_type_name(foo->type)); print_tree(foo->u.s.a.node,0); printf("}"); break; case F_ARG_LIST: print_tree(foo->u.s.a.node,0); if(foo->u.s.a.node && foo->u.s.b.node) putchar(','); print_tree(foo->u.s.b.node,needlval); return; case F_LVALUE_LIST: print_tree(foo->u.s.a.node,1); if(foo->u.s.a.node && foo->u.s.b.node) putchar(','); print_tree(foo->u.s.b.node,1); return; case '{': print_tree(foo->u.s.a.node,0); if(foo->u.s.a.node) printf(";\n "); print_tree(foo->u.s.b.node,needlval); return; case F_EFUN_CALL: printf("efun::%s(",get_f_name(foo->u.s.a.number)); print_tree(foo->u.s.b.node,0); printf(")"); return; default: if(!(foo->opt_info & OPT_A_IS_NODE) && !(foo->opt_info & OPT_B_IS_NODE)) { printf("%s",get_f_name(foo->token)); return; } if(foo->token<256) { printf("%c(",foo->token); }else{ printf("%s(",get_f_name(foo->token)); } if(foo->opt_info & OPT_A_IS_NODE) print_tree(foo->u.s.a.node,0); if((foo->opt_info & OPT_A_IS_NODE) && (foo->opt_info & OPT_B_IS_NODE) && foo->u.s.a.node && foo->u.s.b.node) putchar(','); if(foo->opt_info & OPT_B_IS_NODE) print_tree(foo->u.s.b.node,0); printf(")"); return; } } void nice_print_tree(node *n) { print_tree(n,0); printf("\n"); fflush(stdout); } static int depend_p3(node *a,node *b) { if(!a || is_const(a)) return 0; if(!(a->opt_info & OPT_COULD_BE_CONST)) return 2; if(a->token==F_GLOBAL) if(b->opt_info & OPT_RESTORE_OBJECT) return 2; if((a->opt_info & OPT_A_IS_NODE) && depend_p3(a->u.s.a.node,b)) return 1; if((a->opt_info & OPT_B_IS_NODE) && depend_p3(a->u.s.b.node,b)) return 1; return 0; } #if 1 struct used_vars { char locals[MAX_LOCAL]; char globals[MAX_GLOBAL]; }; #define VAR_BLOCKED 0 #define VAR_UNUSED 1 #define VAR_USED 3 #define VAR_SET 4 static void do_and_vars(struct used_vars *a,struct used_vars *b) { int e; for(e=0;e<MAX_LOCAL;e++) a->locals[e]|=b->locals[e]; for(e=0;e<MAX_GLOBAL;e++) a->globals[e]|=b->globals[e]; free((char *)b); } static struct used_vars *copy_vars(struct used_vars *a) { struct used_vars *ret; ret=(struct used_vars *)xalloc(sizeof(struct used_vars)); MEMCPY((char *)ret,(char *)a,sizeof(struct used_vars)); return ret; } static int find_used_variables(node *n,struct used_vars *p,int noblock) { struct used_vars *a; if(!n) return 0; switch(n->token) { case F_LOCAL: if(n->opt_info & OPT_LVALUE_OVERWRITE) { if(p->locals[n->u.s.a.number]==VAR_UNUSED && !noblock) p->locals[n->u.s.a.number]=VAR_BLOCKED; }else{ if(p->locals[n->u.s.a.number]==VAR_UNUSED) p->locals[n->u.s.a.number]=VAR_USED; } break; case F_GLOBAL: if(n->opt_info & OPT_LVALUE_OVERWRITE) { if(p->globals[n->u.s.a.number]==VAR_UNUSED && !noblock) p->globals[n->u.s.a.number]=VAR_BLOCKED; }else{ if(p->globals[n->u.s.a.number]==VAR_UNUSED) p->globals[n->u.s.a.number]=VAR_USED; } break; case '?': find_used_variables(n->u.s.a.node,p,noblock); a=copy_vars(p); find_used_variables(n->u.s.b.node->u.s.a.node,a,noblock); find_used_variables(n->u.s.b.node->u.s.b.node,p,noblock); do_and_vars(p,a); break; case F_INC_NEQ_LOOP: case F_DEC_NEQ_LOOP: case F_INC_LOOP: case F_DEC_LOOP: case F_FOREACH: case F_FOR: find_used_variables(n->u.s.a.node,p,noblock); a=copy_vars(p); find_used_variables(n->u.s.b.node,a,noblock); do_and_vars(p,a); break; case F_SWITCH: find_used_variables(n->u.s.a.node,p,noblock); a=copy_vars(p); find_used_variables(n->u.s.b.node,a,1); do_and_vars(p,a); break; case F_DO: a=copy_vars(p); find_used_variables(n->u.s.a.node,a,noblock); do_and_vars(p,a); find_used_variables(n->u.s.b.node,p,noblock); break; case F_BRANCH: find_used_variables(n->u.s.a.node,p,noblock); find_used_variables(n->u.s.b.node,p,noblock); break; default: if(n->opt_info & OPT_A_IS_NODE) find_used_variables(n->u.s.a.node,p,noblock); if(n->opt_info & OPT_B_IS_NODE) find_used_variables(n->u.s.b.node,p,noblock); } return 0; } /* no subtility needed */ static void find_written_vars(node *n,struct used_vars *p) { if(!n) return; switch(n->token) { case F_LOCAL: if(n->opt_info & (OPT_LVALUE_OVERWRITE | OPT_LVALUE_CHANGE)) p->locals[n->u.s.a.number]=VAR_USED; break; case F_GLOBAL: if(n->opt_info & (OPT_LVALUE_OVERWRITE | OPT_LVALUE_CHANGE)) p->globals[n->u.s.a.number]=VAR_USED; break; default: if(n->opt_info & OPT_A_IS_NODE) find_written_vars(n->u.s.a.node,p); if(n->opt_info & OPT_B_IS_NODE) find_written_vars(n->u.s.b.node,p); } } /* return 1 if A depends on B */ static int depend_p2(node *a,node *b) { struct used_vars aa,bb; int e; if(!a || !b || is_const(a)) return 0; for(e=0;e<MAX_LOCAL;e++) aa.locals[e]=bb.locals[e]=VAR_UNUSED; for(e=0;e<MAX_GLOBAL;e++) aa.globals[e]=bb.globals[e]=VAR_UNUSED; find_used_variables(a,&aa,0); find_written_vars(b,&bb); for(e=0;e<MAX_LOCAL;e++) if(aa.locals[e]==VAR_USED && bb.locals[e]!=VAR_UNUSED) return 1; for(e=0;e<MAX_GLOBAL;e++) if(aa.globals[e]==VAR_USED && bb.globals[e]!=VAR_UNUSED) return 1; return 0; } #else /* return 1 if a depends on b */ static int depend_p2(node *a,node *b) { int foo; if(!a || !b || is_const(a)) return 0; switch(b->token) { case F_INDEX: if(b->opt_info & ( OPT_LVALUE_CHANGE | OPT_LVALUE_OVERWRITE )) { if(a->token==F_INDEX) return 1; switch(a->token) { default: if((a->opt_info & OPT_A_IS_NODE) && (foo=depend_p2(a->u.s.a.node,b))) return foo; if((a->opt_info & OPT_B_IS_NODE) && (foo=depend_p2(a->u.s.b.node,b))) return foo; } } break; case F_GLOBAL: case F_LOCAL: if(b->opt_info & ( OPT_LVALUE_CHANGE | OPT_LVALUE_OVERWRITE )) { if(a->token==b->token && a->u.s.a.number==b->u.s.a.number) { /* if(a->opt_info & OPT_LVALUE_OVERWRITE) return -1; else */ return 1; } switch(a->token) { default: if((a->opt_info & OPT_A_IS_NODE) && (foo=depend_p2(a->u.s.a.node,b))) return foo; if((a->opt_info & OPT_B_IS_NODE) && (foo=depend_p2(a->u.s.b.node,b))) return foo; } } break; } if((b->opt_info & OPT_A_IS_NODE) && (0<depend_p2(a,b->u.s.a.node))) return 1; if((b->opt_info & OPT_B_IS_NODE) && (0<depend_p2(a,b->u.s.b.node))) return 1; return 0; } #endif static int depend_p(node *a,node *b) { if(!b) return 0; if(!(b->opt_info & OPT_SIDE_EFFECT)) { return depend_p2(a,b); }else{ return depend_p3(a,b) || depend_p2(a,b); } } static int find_and_clear_optimize_flag(node *n,node *a) { if(!a || !n) return 0; if(a==n) return 1; if(((n->opt_info & OPT_A_IS_NODE) && find_and_clear_optimize_flag(n->u.s.a.node,a)) || ((n->opt_info & OPT_B_IS_NODE) && find_and_clear_optimize_flag(n->u.s.b.node,a))) { n->opt_info&=~(OPT_OPT_COMPUTED|OPT_OPTIMIZED); return 1; } return 0; } static int cntargs(node *n) { if(!n) return 0; switch(n->token) { case F_ASSIGN_AND_POP: case F_INC_AND_POP: case F_DEC_AND_POP: case '{': return 0; case F_CAST: case F_EFUN_CALL: return n->type!=TYPE_VOID; case F_FOREACH: return 3; case F_INC_NEQ_LOOP: case F_DEC_NEQ_LOOP: case F_INC_LOOP: case F_DEC_LOOP: return 2; case ' ': case F_LVALUE_LIST: case F_ARG_LIST: return cntargs(n->u.s.a.node)+cntargs(n->u.s.b.node); /* this might not be true, but it doesn't matter very much */ default: return 1; } } static node *do_optimize(node *n,int needlval,node *parent); static node *optimize(node *n,int needlval,node *parent) { int save_current_line=current_line; if(!n) return n; #ifdef LINMALLOC_DEBUG assert_node(n); #endif #ifdef MALLOC_DEBUG check_sfltable(); #endif if(n->opt_info & OPT_OPTIMIZED) return n; current_line=n->lnum; n=do_optimize(n,needlval,parent); if(n) n->opt_info|=OPT_OPTIMIZED; current_line=save_current_line; return n; } static node *call_optimizer(node *n) { clear_remember(); return optimize(n,0,NULL); } static node *do_optimize(node *n,int needlval,node *parent) { node *tmp1,*tmp2,*tmp3; int opt_info1,opt_info2,try_optimize; if(!n) return NULL; #ifdef DEBUG if(n->token>=F_MAX_INSTR) { fatal("Internal compiler breakdown.\n"); } #endif #ifdef MALLOC_DEBUG check_sfltable(); #endif try_optimize=OPT_TRY_OPTIMIZE | OPT_COULD_BE_CONST; opt_info1=opt_info2=OPT_CONST; switch(n->token) { case F_NUMBER: case F_STRING: case F_FLOAT: case F_CONSTANT: try_optimize=OPT_COULD_BE_CONST; break; case F_CAST: n->u.s.a.node=optimize(n->u.s.a.node,needlval,n); opt_info1=n->u.s.a.node?n->u.s.a.node->opt_info:OPT_CONST; if(n->type==TYPE_OBJECT) { clear_remember(); if((n->u.s.a.node->type & TYPE_MOD_MASK) == TYPE_STRING || (n->u.s.a.node->type & TYPE_MOD_MASK) == TYPE_ANY) { opt_info2=OPT_SIDE_EFFECT; }else if((n->u.s.a.node->type & TYPE_MOD_MASK) == TYPE_NUMBER){ opt_info2=0; } }else{ } break; case F_CONSTANT_FUNCTION: try_optimize=OPT_COULD_BE_CONST; if(needlval!=-1) { opt_info1=OPT_CONST; }else{ if((FUNCTIONP(n->u.s.a.number)->flags & NAME_PROTOTYPE) || !(FUNCTIONP(n->u.s.a.number)->type & TYPE_MOD_INLINE)) { opt_info1=OPT_SIDE_EFFECT | OPT_RESTORE_OBJECT; /* assume the worst */ }else{ opt_info1=FUNCTIONP(n->u.s.a.number)->type; opt_info1=(opt_info1&TYPE_MOD_CONSTANT?OPT_CONST:0) | (opt_info1&TYPE_MOD_SIDE_EFFECT?OPT_SIDE_EFFECT:0) ; if(!(opt_info1&OPT_CONST)) try_optimize=OPT_TRY_OPTIMIZE; } } break; case F_PUSH_SIMUL_EFUN: try_optimize=OPT_COULD_BE_CONST; if(needlval!=-1) { opt_info1=OPT_CONST; }else{ struct svalue *s; s=&(simul_efuns[n->u.s.a.number].fun); if(s->type==T_FUNCTION) { opt_info1=s->u.ob->prog->function_ptrs[s->subtype].type; opt_info1=(opt_info1&TYPE_MOD_CONSTANT?OPT_CONST:0) | (opt_info1&TYPE_MOD_SIDE_EFFECT?OPT_SIDE_EFFECT:0) ; if(!(opt_info1&OPT_CONST)) try_optimize=OPT_TRY_OPTIMIZE; }else{ fatal("Simul efun destructed unexpectedly.\n"); } } break; case F_EFUN_CALL: switch(n->u.s.a.number) { case F_RESTORE_OBJECT: opt_info1=instrs[n->u.s.a.number-F_OFFSET].ret_type; opt_info1=(opt_info1&TYPE_MOD_CONSTANT?OPT_CONST:0) | (opt_info1&TYPE_MOD_SIDE_EFFECT?OPT_SIDE_EFFECT:0)| OPT_RESTORE_OBJECT; n->u.s.b.node=optimize(n->u.s.b.node,0,n); opt_info2=n->u.s.b.node?n->u.s.b.node->opt_info:OPT_CONST; clear_remember(); try_optimize=0; break; case F_CALL_FUNCTION: n->u.s.b.node=optimize(n->u.s.b.node,-1,n); opt_info1=n->u.s.b.node?n->u.s.b.node->opt_info:OPT_CONST; clear_remember(); if(!(opt_info1&OPT_CONST)) opt_info1|=OPT_SIDE_EFFECT; break; case F_ALLOCATE: opt_info1=instrs[n->u.s.a.number-F_OFFSET].ret_type; opt_info1=(opt_info1&TYPE_MOD_CONSTANT?OPT_CONST:0) | (opt_info1&TYPE_MOD_SIDE_EFFECT?OPT_SIDE_EFFECT:0) ; n->u.s.b.node=optimize(n->u.s.b.node,0,n); opt_info2=n->u.s.b.node?n->u.s.b.node->opt_info:OPT_CONST; if(opt_info1&OPT_SIDE_EFFECT) clear_remember(); default: opt_info1=instrs[n->u.s.a.number-F_OFFSET].ret_type; opt_info1=(opt_info1&TYPE_MOD_CONSTANT?OPT_CONST:0) | (opt_info1&TYPE_MOD_SIDE_EFFECT?OPT_SIDE_EFFECT:0) ; n->u.s.b.node=optimize(n->u.s.b.node,0,n); opt_info2=n->u.s.b.node?n->u.s.b.node->opt_info:OPT_CONST; if(opt_info1&OPT_SIDE_EFFECT) clear_remember(); if(!(opt_info1&OPT_CONST) && n->u.s.a.number!=F_ALLOCATE) try_optimize=OPT_TRY_OPTIMIZE; } break; case F_CONTINUE: opt_info1=OPT_CONT; break; case F_BREAK: opt_info1=OPT_BREAK; break; case F_BRANCH: case F_DO: clear_remember(); n->u.s.a.node=optimize(n->u.s.a.node,0,n); opt_info1=n->u.s.a.node?n->u.s.a.node->opt_info:OPT_CONST; opt_info1&=~OPT_CONT & ~OPT_BREAK; clear_remember(); n->u.s.b.node=optimize(n->u.s.b.node,0,n); opt_info2=n->u.s.b.node?n->u.s.b.node->opt_info:OPT_CONST; clear_remember(); break; case F_INC: case F_INC_AND_POP: case F_POST_INC: case F_DEC: case F_DEC_AND_POP: case F_POST_DEC: { struct nodepair *p; p=active; active=find_remember(n->u.s.a.node); n->u.s.a.node=optimize(n->u.s.a.node,OPT_LVALUE_CHANGE,n); active=p; opt_info1=n->u.s.a.node?n->u.s.a.node->opt_info:OPT_CONST; break; } case F_FOREACH: case F_INC_NEQ_LOOP: case F_DEC_NEQ_LOOP: case F_INC_LOOP: case F_DEC_LOOP: case F_FOR: clear_remember(); n->u.s.a.node=optimize(n->u.s.a.node,0,n); opt_info2=n->u.s.a.node?n->u.s.a.node->opt_info:OPT_CONST; clear_remember(); n->u.s.b.node=optimize(n->u.s.b.node,0,n); opt_info1=n->u.s.b.node?n->u.s.b.node->opt_info:OPT_CONST; opt_info1&= ~OPT_CONT & ~OPT_BREAK; clear_remember(); break; case F_ASSIGN_AND_POP: case F_ASSIGN: { struct nodepair *p; p=active; active=find_remember(n->u.s.b.node); n->u.s.a.node=optimize(n->u.s.a.node,0,n); active=p; opt_info2=n->u.s.a.node?n->u.s.a.node->opt_info:OPT_CONST; n->u.s.b.node=optimize(n->u.s.b.node,OPT_LVALUE_OVERWRITE,n); opt_info1=n->u.s.b.node?n->u.s.b.node->opt_info:OPT_CONST; break; } case F_AND_EQ: case F_OR_EQ: case F_XOR_EQ: case F_LSH_EQ: case F_RSH_EQ: case F_ADD_EQ: case F_SUB_EQ: case F_MULT_EQ: case F_MOD_EQ: case F_DIV_EQ: n->u.s.b.node=optimize(n->u.s.b.node,0,n); opt_info2=n->u.s.b.node?n->u.s.b.node->opt_info:OPT_CONST; n->u.s.a.node=optimize(n->u.s.a.node,OPT_LVALUE_CHANGE,n); opt_info1=n->u.s.a.node?n->u.s.a.node->opt_info:OPT_CONST; break; case ' ': clear_remember(); n->u.s.a.node=optimize(n->u.s.a.node,0,n); opt_info1=n->u.s.a.node?n->u.s.a.node->opt_info:OPT_CONST; clear_remember(); n->u.s.b.node=optimize(n->u.s.b.node,OPT_LVALUE_CHANGE,n); opt_info2=n->u.s.b.node?n->u.s.b.node->opt_info:OPT_CONST; clear_remember(); break; case F_LOCAL: if(needlval) { opt_info1=OPT_LOCAL_ASSIGNMENT; }else{ opt_info1=0; } break; case F_GLOBAL: if(needlval) { opt_info1=OPT_GLOBAL_ASSIGNMENT; }else{ opt_info1=OPT_GLOBAL_DEPEND; } break; case F_INDEX: /* hm */ n->u.s.a.node=optimize(n->u.s.a.node,needlval?OPT_LVALUE_CHANGE:0,n); opt_info1=n->u.s.a.node?n->u.s.a.node->opt_info:OPT_CONST; n->u.s.b.node=optimize(n->u.s.b.node,0,n); opt_info2=n->u.s.b.node?n->u.s.b.node->opt_info:OPT_CONST; needlval=0; break; case '{': case F_ARG_LIST: try_optimize=OPT_COULD_BE_CONST; if(needlval==-1) { n->u.s.a.node=optimize(n->u.s.a.node,needlval,n); opt_info1=n->u.s.a.node?n->u.s.a.node->opt_info:OPT_CONST; n->u.s.b.node=optimize(n->u.s.b.node,0,n); opt_info2=n->u.s.b.node?n->u.s.b.node->opt_info:OPT_CONST; }else{ n->u.s.a.node=optimize(n->u.s.a.node,0,n); opt_info1=n->u.s.a.node?n->u.s.a.node->opt_info:OPT_CONST; n->u.s.b.node=optimize(n->u.s.b.node,needlval,n); opt_info2=n->u.s.b.node?n->u.s.b.node->opt_info:OPT_CONST; } break; case F_LVALUE_LIST: try_optimize=OPT_COULD_BE_CONST; n->u.s.a.node=optimize(n->u.s.a.node,OPT_LVALUE_CHANGE,n); opt_info1=n->u.s.a.node?n->u.s.a.node->opt_info:OPT_CONST; n->u.s.b.node=optimize(n->u.s.b.node,OPT_LVALUE_CHANGE,n); opt_info2=n->u.s.b.node?n->u.s.b.node->opt_info:OPT_CONST; break; case F_CASE: case F_DEFAULT: clear_remember(); n->u.s.a.node=optimize(n->u.s.a.node,0,n); opt_info1=n->u.s.a.node?n->u.s.a.node->opt_info:OPT_CONST; opt_info1|=OPT_CASE; clear_remember(); n->u.s.b.node=optimize(n->u.s.b.node,0,n); opt_info2=n->u.s.b.node?n->u.s.b.node->opt_info:OPT_CONST; opt_info2|=OPT_CASE; clear_remember(); break; case F_RETURN: opt_info1=OPT_RETURN; n->u.s.a.node=optimize(n->u.s.a.node,0,n); opt_info2=n->u.s.a.node?n->u.s.a.node->opt_info:OPT_CONST; clear_remember(); break; case F_SWITCH: n->u.s.a.node=optimize(n->u.s.a.node,0,n); opt_info1=n->u.s.a.node?n->u.s.a.node->opt_info:OPT_CONST; clear_remember(); n->u.s.b.node=optimize(n->u.s.b.node,0,n); opt_info2=n->u.s.b.node?n->u.s.b.node->opt_info:OPT_CONST; opt_info2&= ~OPT_CASE & ~OPT_BREAK; clear_remember(); break; case F_ADD: case F_ADD_INT: case F_SUBTRACT: case F_MULTIPLY: case F_DIVIDE: case F_MOD: case F_NOT: case F_AND: case F_OR: if(n->opt_info & OPT_A_IS_NODE) { n->u.s.a.node=optimize(n->u.s.a.node,needlval,n); opt_info1=n->u.s.a.node?n->u.s.a.node->opt_info:OPT_CONST; }else{ opt_info1=OPT_CONST; } if(n->opt_info & OPT_B_IS_NODE) { n->u.s.b.node=optimize(n->u.s.b.node,needlval,n); opt_info2=n->u.s.b.node?n->u.s.b.node->opt_info:OPT_CONST; }else{ opt_info2=OPT_CONST; } break; default: clear_remember(); if(n->opt_info & OPT_A_IS_NODE) { n->u.s.a.node=optimize(n->u.s.a.node,needlval,n); opt_info1=n->u.s.a.node?n->u.s.a.node->opt_info:OPT_CONST; }else{ opt_info1=OPT_CONST; } clear_remember(); if(n->opt_info & OPT_B_IS_NODE) { n->u.s.b.node=optimize(n->u.s.b.node,needlval,n); opt_info2=n->u.s.b.node?n->u.s.b.node->opt_info:OPT_CONST; }else{ opt_info2=OPT_CONST; } clear_remember(); } #ifdef MALLOC_DEBUG check_sfltable(); #endif n->opt_info= ( (opt_info1 & opt_info2 & OPT_CONST) | ((opt_info1 | opt_info2) & (OPT_SIDE_EFFECT | OPT_GLOBAL_ASSIGNMENT | OPT_LOCAL_ASSIGNMENT | OPT_RESTORE_OBJECT | OPT_TRY_OPTIMIZE | OPT_CONT | OPT_BREAK | OPT_RETURN | OPT_GLOBAL_DEPEND | OPT_CASE)) | (n->opt_info & (OPT_A_IS_NODE | OPT_B_IS_NODE)) | OPT_OPT_COMPUTED | try_optimize | (needlval & (OPT_LVALUE_CHANGE | OPT_LVALUE_OVERWRITE)) ); #ifdef LASDEBUG if(lasdebug>3) { fprintf(stderr,"Computed [%c%c%c%c%c%c%c%c%c%c%c%c%c%c] opt info for:\n", (n->opt_info & OPT_A_IS_NODE)?'A':'-', (n->opt_info & OPT_B_IS_NODE)?'B':'-', (n->opt_info & OPT_CONST)?'C':'-', (n->opt_info & OPT_SIDE_EFFECT)?'X':'-', (n->opt_info & OPT_LOCAL_ASSIGNMENT)?'l':'-', (n->opt_info & OPT_GLOBAL_ASSIGNMENT)?'g':'-', (n->opt_info & OPT_RESTORE_OBJECT)?'r':'-', (n->opt_info & OPT_OPT_COMPUTED)?'O':'-', (n->opt_info & OPT_TRY_OPTIMIZE)?'T':'-', (n->opt_info & OPT_CASE)?'c':'-', (n->opt_info & OPT_CONT)?'c':'-', (n->opt_info & OPT_CONT)?'b':'-', (n->opt_info & OPT_RETURN)?'r':'-', (n->opt_info & OPT_GLOBAL_DEPEND)?'G':'-' ); } #endif #ifdef MALLOC_DEBUG check_sfltable(); #endif if(check_types(n)) return do_optimize(n,needlval,parent); #ifdef MALLOC_DEBUG check_sfltable(); #endif if(lasdebug>2) { fprintf(stderr,"do_optimizing:"); nice_print_tree(n); } #ifdef MALLOC_DEBUG check_sfltable(); #endif switch(n->token) { case ' ': case ':': return n; /* hands of optimizer ! */ case '?': /* (!X) ? (Y) : (Z) -> (X) ? (Z) : (Y)*/ while(n->u.s.a.node->token==F_NOT) { tmp1=n->u.s.b.node->u.s.a.node; n->u.s.b.node->u.s.a.node=n->u.s.b.node->u.s.b.node; n->u.s.b.node->u.s.b.node=tmp1; tmp1=n->u.s.a.node; n->u.s.a.node=tmp1->u.s.a.node; tmp1->u.s.a.node=0; free_node(tmp1); } /* 1 ? X : Y => X * if(1) X; else Y; => X; */ if(node_is_true(n->u.s.a.node)) { tmp1=n->u.s.b.node->u.s.a.node; n->u.s.b.node->u.s.a.node=NULL; free_node(n); return optimize(tmp1,needlval,parent); } /* 0 ? X : Y => Y * if(0) X; else Y; => Y; */ if(node_is_false(n->u.s.a.node)) { tmp1=n->u.s.b.node->u.s.b.node; n->u.s.b.node->u.s.b.node=NULL; free_node(n); return optimize(tmp1,needlval,parent); } /* if(X) Y; else Y; => { X; Y; } */ if(node_is_eq(n->u.s.b.node->u.s.a.node,n->u.s.b.node->u.s.b.node)) { n->token=F_ARG_LIST; n->u.s.a.node=mkcastnode(TYPE_VOID,n->u.s.a.node); tmp1=n->u.s.b.node; n->u.s.b.node=tmp1->u.s.a.node; tmp1->u.s.a.node=NULL; free_node(tmp1); n->opt_info&=~OPT_OPT_COMPUTED; return optimize(n,needlval,parent); } break; case F_LOCAL: case F_GLOBAL: case F_INDEX: { struct nodepair *p; if((p=find_remember(n)) && p->assignment) { if(n->opt_info & (OPT_LVALUE_CHANGE | OPT_LVALUE_OVERWRITE)) { if(n->opt_info & OPT_LVALUE_OVERWRITE) { if(p->assignment && !p->used) { tmp1=p->assignment; free_node(tmp1->u.s.b.node); free_node(tmp1->u.s.a.node); tmp1->u.s.a.node=NULL; tmp1->u.s.b.node=NULL; tmp1->token='{'; }else{ reset_remember(p); } }else{ reset_remember(p); } }else{ if(node_is_eq(n,p->assignment->u.s.a.node)) { if((p->assignment->token==F_ASSIGN || p->assignment->token==F_ASSIGN_AND_POP) && is_const(p->assignment->u.s.a.node)) { free_node(n); return optimize(copy_node(p->assignment->u.s.b.node),needlval,parent); }else{ if(!p->used) { int tmp=0; switch(p->assignment->token) { case F_INC_AND_POP: case F_POST_INC: case F_INC: tmp=F_INC; case F_DEC_AND_POP: case F_POST_DEC: case F_DEC: tmp=F_DEC; break; case F_ASSIGN_AND_POP: case F_ASSIGN: tmp=F_ASSIGN; break; } if(!tmp) fatal("Optimizer fatal error!!\n"); tmp1=mknode(tmp,p->assignment->u.s.a.node,p->assignment->u.s.b.node); p->assignment->u.s.a.node=NULL; p->assignment->u.s.b.node=NULL; p->assignment->token='{'; p->assignment->opt_info&=~OPT_OPT_COMPUTED; reset_remember(p); free_node(n); return optimize(tmp1,needlval,parent); }else{ reset_remember(p); } } }else{ reset_remember(p); } } } break; } case F_ARG_LIST: case '{': /* free useless nodes */ if(!n->u.s.a.node && !n->u.s.b.node) { free_node(n); return NULL; } /* free useless nodes */ if(!n->u.s.a.node) { tmp1=n->u.s.b.node; n->u.s.b.node=0; free_node(n); return optimize(tmp1,needlval,parent); } /* free useless nodes */ if(!n->u.s.b.node) { tmp1=n->u.s.a.node; n->u.s.a.node=0; free_node(n); return optimize(tmp1,needlval,parent); } /* { X; return; Y; } -> { X; return; } * { X; break; Y; } -> { X; return; } * { X; continue; Y; } -> { X; return; } */ if((n->u.s.a.node->token==F_RETURN || n->u.s.a.node->token==F_BREAK || n->u.s.a.node->token==F_CONTINUE || ((n->u.s.a.node->token=='{' || n->u.s.a.node->token==F_ARG_LIST) && n->u.s.a.node->u.s.b.node && (n->u.s.a.node->u.s.b.node->token==F_RETURN || n->u.s.a.node->u.s.b.node->token==F_BREAK || n->u.s.a.node->u.s.b.node->token==F_CONTINUE))) && !(n->u.s.b.node->opt_info & OPT_CASE)) { tmp1=n->u.s.a.node; n->u.s.a.node=0; free_node(n); return optimize(tmp1,needlval,parent); } if(n->token=='{') { node **prev,**curr; /* { (void)a; } => { a; } */ while(n->u.s.a.node->token==F_CAST && n->u.s.a.node->type==TYPE_VOID) { tmp1=n->u.s.a.node; n->u.s.a.node=tmp1->u.s.a.node; tmp1->u.s.a.node=0; free_node(tmp1); } /* { (void)b; } => { b; } */ while(n->u.s.b.node->token==F_CAST && n->u.s.b.node->type==TYPE_VOID) { tmp1=n->u.s.b.node; n->u.s.b.node=tmp1->u.s.a.node; tmp1->u.s.a.node=0; free_node(tmp1); } curr=&(n->u.s.b.node); prev=last_cmd(&(n->u.s.a.node)); if(prev && (*prev) && (*curr)) { if(((*curr)->token==F_INC || (*curr)->token==F_POST_INC || (*curr)->token==F_INC_AND_POP) && ((*prev)->token==F_ASSIGN || (*prev)->token==F_ASSIGN_AND_POP) && node_is_eq((*prev)->u.s.b.node,(*curr)->u.s.a.node)) { free_node(*curr); *curr=0; (*prev)->u.s.a.node=mknode(F_ADD,(*prev)->u.s.a.node,mkintnode(1)); find_and_clear_optimize_flag(n,*prev); return optimize(n,needlval,parent); } if(((*curr)->token==F_DEC || (*curr)->token==F_POST_DEC || (*curr)->token==F_DEC_AND_POP) && ((*prev)->token==F_ASSIGN || (*prev)->token==F_ASSIGN_AND_POP) && node_is_eq((*prev)->u.s.b.node,(*curr)->u.s.a.node)) { free_node(*curr); *curr=0; (*prev)->u.s.a.node=mknode(F_ADD,(*prev)->u.s.a.node,mkintnode(-1)); find_and_clear_optimize_flag(n,*prev); return optimize(n,needlval,parent); } } if(!(n->opt_info & (OPT_CONT | OPT_BREAK | OPT_CASE)) && cntargs(n->u.s.a.node)+cntargs(n->u.s.b.node)<10) { n->token=F_ARG_LIST; return optimize(mkcastnode(TYPE_VOID,n),needlval,parent); } } break; case F_MULTIPLY: if(SAME_TYPE(n->u.s.a.node->type,n->u.s.b.node->type & TYPE_MOD_MASK)) { if(n->u.s.a.node->token==F_NUMBER || n->u.s.b.node->token==F_NUMBER) { int tmp,e; /* x*(X) -> (X)*x */ if(n->u.s.a.node->token==F_NUMBER && n->u.s.b.node->token!=F_NUMBER) { tmp1=n->u.s.a.node; n->u.s.a.node=n->u.s.b.node; n->u.s.b.node=tmp1; } tmp=n->u.s.b.node->u.s.a.number; /* X*1 -> X */ if(tmp==1) { tmp1=n->u.s.a.node; n->u.s.a.node=0; free_node(n); return tmp1; } if(tmp==0) { if(!(n->u.s.a.node->opt_info & OPT_NOT_THROWABLE)) { free_node(n); return mkintnode(0); } } /* X*(1<<x) -> X<<x */ for(e=1;e;e*=2) { if(tmp==e) { n->u.s.b.node->u.s.a.number=0; while(e>1) { n->u.s.b.node->u.s.a.number++; e/=2; } n->token=F_LSH; n->opt_info &=~ (OPT_OPT_COMPUTED | OPT_OPTIMIZED); return optimize(n,needlval,parent); } } } /* 1.0*X => X */ /* X*0.0 => 0.0 */ if((n->u.s.a.node->token==F_FLOAT && n->u.s.a.node->u.s.a.fnum==1.0) || (n->u.s.b.node->token==F_FLOAT && n->u.s.b.node->u.s.a.fnum==0.0 && !(n->u.s.a.node->opt_info & OPT_NOT_THROWABLE))) { tmp1=n->u.s.b.node; n->u.s.b.node=0; free_node(n); return optimize(tmp1,needlval,parent); } /* 1*X => X */ if((n->u.s.b.node->token==F_FLOAT && n->u.s.b.node->u.s.a.fnum==1.0) || (n->u.s.a.node->token==F_FLOAT && n->u.s.a.node->u.s.a.fnum==0.0 && !(n->u.s.b.node->opt_info & OPT_NOT_THROWABLE))) { tmp1=n->u.s.a.node; n->u.s.a.node=0; free_node(n); return optimize(tmp1,needlval,parent); } } break; case F_DIVIDE: if(SAME_TYPE(n->u.s.a.node->type,n->u.s.b.node->type)) { if(n->u.s.b.node->token==F_NUMBER) { int tmp,e; tmp=n->u.s.b.node->u.s.a.number; /* X*1 -> X */ if(tmp==1) { tmp1=n->u.s.a.node; n->u.s.a.node=0; free_node(n); return tmp1; } /* X/(1<<x) -> X>>x */ for(e=1;e;e*=2) { if(tmp==e) { n->u.s.b.node->u.s.a.number=0; while(e>1) { n->u.s.b.node->u.s.a.number++; e/=2; } n->token=F_RSH; n->opt_info &=~ (OPT_OPT_COMPUTED | OPT_OPTIMIZED); return optimize(n,needlval,parent); } } } /* X/1 => X */ /* 0/X => 0 */ if((n->u.s.a.node->token==F_NUMBER && n->u.s.a.node->u.s.a.number==0 && !(n->u.s.b.node->opt_info & OPT_NOT_THROWABLE)) || (n->u.s.a.node->token==F_FLOAT && n->u.s.a.node->u.s.a.fnum==0.0 && !(n->u.s.b.node->opt_info & OPT_NOT_THROWABLE)) || (n->u.s.b.node->token==F_NUMBER && n->u.s.b.node->u.s.a.number==1) || (n->u.s.b.node->token==F_FLOAT && n->u.s.b.node->u.s.a.fnum==1.0)) { tmp1=n->u.s.a.node; n->u.s.a.node=0; free_node(n); return optimize(tmp1,needlval,parent); } } break; case F_ADD: /* X+0 => X */ if(((n->u.s.a.node->type & TYPE_MOD_MASK) == TYPE_NUMBER && n->u.s.b.node->token==F_NUMBER && n->u.s.b.node->u.s.a.number==0) || ((n->u.s.a.node->type & TYPE_MOD_MASK) == TYPE_FLOAT && n->u.s.b.node->token==F_FLOAT && n->u.s.b.node->u.s.a.fnum==0.0) || ((n->u.s.a.node->type & TYPE_MOD_MASK) == TYPE_STRING && n->u.s.b.node->token==F_STRING && !strlen(n->u.s.b.node->u.s.a.str))) { tmp1=n->u.s.a.node; n->u.s.a.node=0; free_node(n); return optimize(tmp1,needlval,parent); } /* 0+X => X */ if(((n->u.s.b.node->type & TYPE_MOD_MASK) == TYPE_NUMBER && n->u.s.a.node->token==F_NUMBER && n->u.s.a.node->u.s.a.number==0) || ((n->u.s.b.node->type & TYPE_MOD_MASK) == TYPE_FLOAT && n->u.s.a.node->token==F_FLOAT && n->u.s.a.node->u.s.a.fnum==0.0) || ((n->u.s.b.node->type & TYPE_MOD_MASK) == TYPE_STRING && n->u.s.a.node->token==F_STRING && !strlen(n->u.s.a.node->u.s.a.str))) { tmp1=n->u.s.b.node; n->u.s.b.node=0; free_node(n); return optimize(tmp1,needlval,parent); } /* (a+b)+c => sum(a,b,c) */ if(n->u.s.a.node->token==F_ADD && SAME_TYPE(n->u.s.a.node->type,n->u.s.b.node->type)) { n->u.s.a.node->token=F_ARG_LIST; n->u.s.a.node->opt_info&=~(OPT_OPT_COMPUTED|OPT_OPTIMIZED); n->token=F_ARG_LIST; n->opt_info&=~OPT_OPT_COMPUTED; return optimize(mkefunnode(F_SUM,n),needlval,parent); } /* a+(b+c) => sum(a,b,c) */ if(n->u.s.b.node->token==F_ADD && SAME_TYPE(n->u.s.a.node->type,n->u.s.b.node->type)) { n->u.s.b.node->token=F_ARG_LIST; n->u.s.b.node->opt_info&=~(OPT_OPT_COMPUTED|OPT_OPTIMIZED); n->token=F_ARG_LIST; n->opt_info&=~OPT_OPT_COMPUTED; return optimize(mkefunnode(F_SUM,n),needlval,parent); } /* sum(X)+c => sum(X,c) */ if(n->u.s.a.node->token==F_EFUN_CALL && n->u.s.a.node->u.s.a.number==F_SUM && SAME_TYPE(n->u.s.a.node->type,n->u.s.b.node->type)) { n->opt_info&=~(OPT_OPT_COMPUTED|OPT_OPTIMIZED); n->u.s.a.node->opt_info&=~(OPT_OPT_COMPUTED|OPT_OPTIMIZED); tmp1=n->u.s.a.node; n->token=F_ARG_LIST; n->u.s.a.node=tmp1->u.s.b.node; tmp1->u.s.b.node=n; return optimize(tmp1,needlval,parent); } /* c+sum(X) => sum(c,X) */ if(n->u.s.b.node->token==F_EFUN_CALL && n->u.s.b.node->u.s.a.number==F_SUM && SAME_TYPE(n->u.s.a.node->type,n->u.s.b.node->type)) { n->opt_info&=~(OPT_OPT_COMPUTED|OPT_OPTIMIZED); n->u.s.b.node->opt_info&=~(OPT_OPT_COMPUTED|OPT_OPTIMIZED); tmp1=n->u.s.b.node; n->token=F_ARG_LIST; n->u.s.b.node=tmp1->u.s.b.node; tmp1->u.s.b.node=n; return optimize(tmp1,needlval,parent); } break; case F_ASSIGN: case F_ASSIGN_AND_POP: /* X=X => {} */ if(node_is_eq(n->u.s.a.node,n->u.s.b.node)) { free_node(n); return NULL; } if(!n->u.s.b.node) break; /* X=X+1 => ++X */ if(n->u.s.a.node->token==F_ADD && n->u.s.b.node->type==TYPE_NUMBER && node_is_eq(n->u.s.b.node,n->u.s.a.node->u.s.a.node) && n->u.s.a.node->u.s.b.node->token==F_NUMBER && n->u.s.a.node->u.s.b.node->u.s.a.number==1) { tmp1=mknode(F_INC,n->u.s.b.node,0); n->u.s.b.node=NULL; free_node(n); return optimize(tmp1,needlval,parent); } /* X=X-1 => --X */ if(n->u.s.a.node->token==F_SUBTRACT && n->u.s.b.node->type==TYPE_NUMBER && node_is_eq(n->u.s.b.node,n->u.s.a.node->u.s.a.node) && n->u.s.a.node->u.s.b.node->token==F_NUMBER && n->u.s.a.node->u.s.b.node->u.s.a.number==1) { tmp1=mknode(F_DEC,n->u.s.b.node,0); n->u.s.b.node=NULL; free_node(n); return optimize(tmp1,needlval,parent); } if(n->token!=F_ASSIGN || !parent || parent->token=='{' || (parent->token==F_CAST && parent->type==TYPE_VOID)) { /* return value probably not used */ remember_remember(n,0); }else{ /* return value probably used */ remember_remember(n,1); } break; case F_EFUN_CALL: switch(n->u.s.a.number) { case F_SIZEOF: /* sizeof(m_indices(X)) => size(X) * sizeof(m_values(X)) => size(X) * sizeof(indices(X)) => size(X) */ if(n->u.s.b.node && n->u.s.b.node->token==F_EFUN_CALL) { switch(n->u.s.b.node->u.s.a.number) { case F_M_INDICES: case F_M_VALUES: case F_INDICES: n->u.s.a.number=F_SIZE; tmp1=n->u.s.b.node; n->u.s.b.node=tmp1->u.s.b.node; tmp1->u.s.b.node=NULL; free_node(tmp1); n->opt_info&=~(OPT_OPT_COMPUTED|OPT_OPTIMIZED); return optimize(n,needlval,parent); } } /* this_object()->X => constant function */ case F_GET_FUNCTION: if(n->u.s.b.node && n->u.s.b.node->token==F_ARG_LIST && n->u.s.b.node->u.s.a.node && n->u.s.b.node->u.s.b.node && n->u.s.b.node->u.s.a.node->token==F_EFUN_CALL && n->u.s.b.node->u.s.a.node->u.s.a.number==F_THIS_OBJECT && n->u.s.b.node->u.s.b.node->token==F_STRING) { int i; if((i=defined_function(n->u.s.b.node->u.s.b.node->u.s.a.str))!=-1) { free_node(n); return optimize(mkconstfunnode(i),needlval,parent); } }else{ node **a; int e; a=my_get_arg(&(n->u.s.b.node),1); if(a && *a && (*a)->token==F_STRING) { for(e=0;e<num_lfuns;e++) { if(lfuns[e]==(*a)->u.s.a.str) /* shared strings */ { free_node(*a); *a=mkintnode(e); n->u.s.a.number=F_GET_LFUN; break; } } } } break; case F_MAP_ARRAY: case F_SEARCH_ARRAY: case F_FILTER_ARRAY: case F_MAP_MAPPING: case F_FILTER_MAPPING: { node **a; int e; a=my_get_arg(&(n->u.s.b.node),1); if(a && *a && (*a)->token==F_STRING) { for(e=0;e<num_lfuns;e++) { if(lfuns[e]==(*a)->u.s.a.str) /* shared strings */ { free_node(*a); *a=mkintnode(e); break; } } } break; } /* sum(a+b,X) => sum(a,b,X) */ case F_SUM: { node **t,**tt; #ifdef MALLOC_DEBUG check_sfltable(); #endif t=first_arg(&n->u.s.b.node); tt=first_arg(&n->u.s.b.node); if(t && tt && (*t)->token==F_ADD && SAME_TYPE((*t)->type,(*tt)->type)) { (*t)->token=F_ARG_LIST; (*t)->opt_info&=~(OPT_OPT_COMPUTED|OPT_OPTIMIZED); return optimize(n,needlval,parent); } #ifdef MALLOC_DEBUG check_sfltable(); #endif break; } } break; case F_XOR: /* X^0 => X */ if(((n->u.s.a.node->type & TYPE_MOD_MASK) == TYPE_NUMBER && n->u.s.b.node->token==F_NUMBER && n->u.s.b.node->u.s.a.number==0)) { tmp1=n->u.s.a.node; n->u.s.a.node=0; free_node(n); return optimize(tmp1,needlval,parent); } /* 0^X => X */ if(((n->u.s.a.node->type & TYPE_MOD_MASK) == TYPE_NUMBER && n->u.s.b.node->token==F_NUMBER && n->u.s.b.node->u.s.a.number==-1)) { tmp1=n->u.s.a.node; n->u.s.a.node=0; free_node(n); return optimize(mknode(F_COMPL,tmp1,0),needlval,parent); } break; case F_AND: /* X&-1 => X; */ if((n->u.s.a.node->type== TYPE_NUMBER && n->u.s.b.node->token==F_NUMBER && n->u.s.b.node->u.s.a.number==-1) || (n->u.s.b.node->type== TYPE_NUMBER && n->u.s.a.node->token==F_NUMBER && n->u.s.a.node->u.s.a.number==0)) { tmp1=n->u.s.a.node; n->u.s.a.node=0; free_node(n); return optimize(tmp1,needlval,parent); } /* -1&X => X; */ if((n->u.s.b.node->type == TYPE_NUMBER && n->u.s.a.node->token==F_NUMBER && n->u.s.a.node->u.s.a.number==-1) || (n->u.s.a.node->type == TYPE_NUMBER && n->u.s.b.node->token==F_NUMBER && n->u.s.b.node->u.s.a.number==0)) { tmp1=n->u.s.b.node; n->u.s.b.node=0; free_node(n); return optimize(tmp1,needlval,parent); } break; case F_LAND: /* !a and !b -> !(a or b) */ if(n->u.s.a.node->token==F_NOT && n->u.s.b.node->token==F_NOT) { tmp1=mknode(F_NOT,mknode(F_LOR,n->u.s.a.node->u.s.a.node, n->u.s.b.node->u.s.a.node),0); n->u.s.a.node->u.s.a.node=NULL; n->u.s.b.node->u.s.a.node=NULL; free_node(n); return optimize(tmp1,needlval,parent); } /* (a && b) && c -> a && (b && c) */ while(n->u.s.a.node->token==F_LAND) { tmp1=n->u.s.a.node; n->u.s.a.node=tmp1->u.s.a.node; tmp1->u.s.a.node=tmp1->u.s.b.node; tmp1->u.s.b.node=n->u.s.b.node; n->u.s.b.node=tmp1; } /* 1 && X => X */ if(node_is_true(n->u.s.a.node)) { tmp1=n->u.s.b.node; n->u.s.b.node=NULL; free_node(n); return optimize(tmp1,needlval,parent); } /* X && 1 -> X */ if(node_is_true(n->u.s.b.node)) { tmp1=n->u.s.a.node; n->u.s.a.node=NULL; free_node(n); return optimize(tmp1,needlval,parent); } /* 0 && X => 0 */ if(node_is_false(n->u.s.a.node)) { free_node(n); return mkintnode(0); } break; case F_LOR: /* !a || !b -> !(a || b) */ if(n->u.s.a.node->token==F_NOT && n->u.s.b.node->token==F_NOT) { tmp1=mknode(F_NOT,mknode(F_LAND,n->u.s.a.node->u.s.a.node, n->u.s.b.node->u.s.a.node),0); n->u.s.a.node->u.s.a.node=NULL; n->u.s.b.node->u.s.a.node=NULL; free_node(n); return optimize(tmp1,needlval,parent); } /* (a && b) && c -> a && (b && c) */ while(n->u.s.a.node->token==F_LOR) { tmp1=n->u.s.a.node; n->u.s.a.node=tmp1->u.s.a.node; tmp1->u.s.a.node=tmp1->u.s.b.node; tmp1->u.s.b.node=n->u.s.b.node; n->u.s.b.node=tmp1; } /* 0 || X => X */ if(node_is_false(n->u.s.a.node)) { tmp1=n->u.s.b.node; n->u.s.b.node=NULL; free_node(n); return optimize(tmp1,needlval,parent); } /* X || 0 => X */ if(node_is_false(n->u.s.b.node)) { tmp1=n->u.s.a.node; n->u.s.a.node=NULL; free_node(n); return optimize(tmp1,needlval,parent); } /* true || X => true */ if(node_is_true(n->u.s.a.node)) { tmp1=n->u.s.a.node; n->u.s.a.node=NULL; free_node(n); return optimize(tmp1,needlval,parent); } break; case F_OR: /* 0 | X => X */ if((n->u.s.a.node->type == TYPE_NUMBER && n->u.s.b.node->token==F_NUMBER && n->u.s.b.node->u.s.a.number==-1) || (n->u.s.b.node->type == TYPE_NUMBER && n->u.s.a.node->token==F_NUMBER && n->u.s.a.node->u.s.a.number==0)) { tmp1=n->u.s.b.node; n->u.s.b.node=0; free_node(n); return optimize(tmp1,needlval,parent); } /* X | 0 => X */ if((n->u.s.b.node->type == TYPE_NUMBER && n->u.s.a.node->token==F_NUMBER && n->u.s.a.node->u.s.a.number==-1) || (n->u.s.a.node->type== TYPE_NUMBER && n->u.s.b.node->token==F_NUMBER && n->u.s.b.node->u.s.a.number==0)) { tmp1=n->u.s.a.node; n->u.s.a.node=0; free_node(n); return optimize(tmp1,needlval,parent); } break; case F_CAST: if(n->type==TYPE_VOID) { if(!(n->u.s.a.node) || !(n->opt_info & (OPT_SIDE_EFFECT | OPT_LOCAL_ASSIGNMENT | OPT_GLOBAL_ASSIGNMENT | OPT_RESTORE_OBJECT | OPT_CASE | OPT_RETURN | OPT_CONT | OPT_BREAK ))) { free_node(n); return NULL; } tmp1=n->u.s.a.node; switch(tmp1->token) { case F_ASSIGN: tmp1->token=F_ASSIGN_AND_POP; tmp1->type=TYPE_VOID; n->u.s.a.node=0; free_node(n); return optimize(tmp1,needlval,parent); case F_INC: case F_POST_INC: tmp1->token=F_INC_AND_POP; tmp1->type=TYPE_VOID; n->u.s.a.node=0; free_node(n); return optimize(tmp1,needlval,parent); case F_DEC: case F_POST_DEC: tmp1->token=F_DEC_AND_POP; tmp1->type=TYPE_VOID; n->u.s.a.node=0; free_node(n); return optimize(tmp1,needlval,parent); } /* if(opt_to_assignments) { remember_all_variables(); eval(n->u.s.b.node); compare_all_variables_and_make_assignments(); } */ } if(!n->u.s.a.node) { switch(n->type) { case TYPE_NUMBER: free_node(n); return mkintnode(0); case TYPE_STRING: free_node(n); return mkstrnode(make_shared_string("")); } } if(n->type==(n->u.s.a.node->type & TYPE_MOD_MASK)) { tmp1=n->u.s.a.node; n->u.s.a.node=0; free_node(n); return optimize(tmp1,needlval,parent); } if(n->type==TYPE_FUNCTION && n->u.s.a.node->token==F_STRING) { int i; if(-1!=(i=defined_function(n->u.s.a.node->u.s.a.str))) { free_node(n); return mkconstfunnode(i); } } break; case F_FOR: { node **last; int inc,less; if(node_is_false(n->u.s.a.node)) { free_node(n); return NULL; } if(n->u.s.a.node && (n->u.s.a.node->token==F_INC || n->u.s.a.node->token==F_DEC || n->u.s.a.node->token==F_POST_INC || n->u.s.a.node->token==F_POST_DEC) && (!n->u.s.b.node->u.s.a.node && !(n->u.s.b.node->u.s.b.node->opt_info & OPT_CONT))) { if(n->u.s.a.node->token==F_DEC || n->u.s.a.node->token==F_POST_DEC) n->token=F_DEC_LOOP; else n->token=F_INC_LOOP; n->u.s.a.node->token=' '; n->u.s.a.node->u.s.b.node=n->u.s.a.node->u.s.a.node; n->u.s.a.node->u.s.a.node=mkintnode(0); n->u.s.a.node->opt_info&=~(OPT_OPT_COMPUTED|OPT_OPTIMIZED); n->u.s.b.node->token='{'; n->u.s.b.node->opt_info&=~(OPT_OPT_COMPUTED|OPT_OPTIMIZED); n->opt_info&=~(OPT_OPT_COMPUTED|OPT_OPTIMIZED); return optimize(n,needlval,parent); } last=&(n->u.s.b.node->u.s.b.node); tmp1=last?*last:(node *)NULL; if(tmp1 && (tmp1->token==F_INC || tmp1->token==F_POST_INC || tmp1->token==F_INC_AND_POP || tmp1->token==F_DEC_AND_POP || tmp1->token==F_DEC || tmp1->token==F_POST_DEC)) { if(tmp1->token==F_INC || tmp1->token==F_POST_INC || tmp1->token==F_INC_AND_POP) inc=1; else inc=0; less=0; if(n->u.s.a.node->token!=F_GT && n->u.s.a.node->token!=F_GE && n->u.s.a.node->token!=F_LE && n->u.s.a.node->token!=F_NE && n->u.s.a.node->token!=F_LT) break; if(!node_is_eq(n->u.s.a.node->u.s.a.node,tmp1->u.s.a.node) || depend_p(n->u.s.a.node->u.s.b.node,n->u.s.b.node->u.s.a.node) || depend_p(n->u.s.a.node->u.s.b.node,n->u.s.a.node->u.s.a.node)) { if(node_is_eq(n->u.s.a.node->u.s.b.node,tmp1->u.s.a.node) && !depend_p(n->u.s.a.node->u.s.a.node,n->u.s.b.node->u.s.a.node) && !depend_p(n->u.s.a.node->u.s.a.node,n->u.s.a.node->u.s.b.node)) { switch(n->u.s.a.node->token) { case F_LT: n->u.s.a.node->token=F_GT; break; case F_LE: n->u.s.a.node->token=F_GT; break; case F_GT: n->u.s.a.node->token=F_LE; break; case F_GE: n->u.s.a.node->token=F_LE; break; } tmp2=n->u.s.a.node->u.s.a.node; n->u.s.a.node->u.s.a.node=n->u.s.a.node->u.s.b.node; n->u.s.a.node->u.s.b.node=tmp2; }else{ break; } } if(inc && n->u.s.a.node->token==F_LE) tmp3=mknode(F_ADD,n->u.s.a.node->u.s.b.node,mkintnode(1)); else if(inc && n->u.s.a.node->token==F_LT) tmp3=n->u.s.a.node->u.s.b.node; else if(!inc && n->u.s.a.node->token==F_GE) tmp3=mknode(F_SUBTRACT,n->u.s.a.node->u.s.b.node,mkintnode(1)); else if(!inc && n->u.s.a.node->token==F_GT) tmp3=n->u.s.a.node->u.s.b.node; else break; *last=0; if(n->u.s.a.node->token==F_NE) { if(inc) { tmp2=mknode(F_INC_NEQ_LOOP,mknode(' ',tmp3,n->u.s.a.node->u.s.a.node),n->u.s.b.node->u.s.a.node); }else{ tmp2=mknode(F_DEC_NEQ_LOOP,mknode(' ',tmp3,n->u.s.a.node->u.s.a.node),n->u.s.b.node->u.s.a.node); } }else{ if(inc) { tmp2=mknode(F_INC_LOOP,mknode(' ',tmp3,n->u.s.a.node->u.s.a.node),n->u.s.b.node->u.s.a.node); }else{ tmp2=mknode(F_DEC_LOOP,mknode(' ',tmp3,n->u.s.a.node->u.s.a.node),n->u.s.b.node->u.s.a.node); } } n->u.s.a.node->u.s.a.node=0; n->u.s.a.node->u.s.b.node=0; n->u.s.b.node->u.s.a.node=0; n->u.s.b.node->u.s.b.node=0; free_node(n); if(inc) { tmp1->token=F_DEC; }else{ tmp1->token=F_INC; } tmp1=mknode('{',mkcastnode(TYPE_VOID,tmp1),tmp2); return optimize(tmp1,needlval,parent); } } break; case F_ADD_EQ: if(n->u.s.a.node->type==TYPE_NUMBER && n->u.s.b.node->token==F_NUMBER && n->u.s.b.node->u.s.a.number==1) { tmp1=mknode(F_INC,n->u.s.a.node,0); n->u.s.a.node=0; free_node(n); return optimize(tmp1,needlval,parent); } if(n->u.s.a.node->type==TYPE_NUMBER && n->u.s.b.node->token==F_NUMBER && n->u.s.b.node->u.s.a.number==-1) { tmp1=mknode(F_DEC,n->u.s.a.node,0); n->u.s.a.node=0; free_node(n); return optimize(tmp1,needlval,parent); } break; case F_SUB_EQ: if(n->u.s.a.node->type==TYPE_NUMBER && n->u.s.b.node->token==F_NUMBER && n->u.s.b.node->u.s.a.number==1) { tmp1=mknode(F_DEC,n->u.s.a.node,0); n->u.s.a.node=0; free_node(n); return optimize(tmp1,needlval,parent); } if(n->u.s.a.node->type==TYPE_NUMBER && n->u.s.b.node->token==F_NUMBER && n->u.s.b.node->u.s.a.number==-1) { tmp1=mknode(F_ADD,n->u.s.a.node,0); n->u.s.a.node=0; free_node(n); return optimize(tmp1,needlval,parent); } break; case F_INC_NEQ_LOOP: case F_DEC_NEQ_LOOP: case F_INC_LOOP: case F_DEC_LOOP: if(!n->u.s.b.node) { n->u.s.a.node->token=F_ASSIGN; n->token=F_CAST; n->type=TYPE_VOID; n->opt_info&=~(OPT_OPT_COMPUTED|OPT_OPTIMIZED); n->u.s.a.node->opt_info&=~(OPT_OPT_COMPUTED|OPT_OPTIMIZED); n->u.s.a.node->type=n->u.s.a.node->u.s.b.node->type; return optimize(n,needlval,parent); } if(!((n->u.s.a.node->opt_info | n->u.s.b.node->opt_info)& (OPT_SIDE_EFFECT | OPT_RETURN | OPT_CONT | OPT_BREAK)) && !depend_p(n->u.s.a.node,n->u.s.b.node) && !depend_p(n->u.s.b.node,n->u.s.b.node) && !depend_p(n->u.s.b.node,n->u.s.a.node)) { n->u.s.a.node->token=F_ASSIGN; n->token='{'; n->type=TYPE_VOID; n->opt_info&=~(OPT_OPT_COMPUTED|OPT_OPTIMIZED); n->u.s.a.node->opt_info&=~(OPT_OPT_COMPUTED|OPT_OPTIMIZED); n->u.s.a.node->type=n->u.s.a.node->u.s.b.node->type; return optimize(n,needlval,parent); } break; case F_SWITCH: { node **tmp; while((tmp=last_cmd(&(n->u.s.b.node))) && (*tmp)->token==F_BREAK) { free_node(*tmp); *tmp=0; } if(!n->u.s.b.node) { n->token=F_CAST; n->type=TYPE_VOID; n->opt_info&=~(OPT_OPT_COMPUTED|OPT_OPTIMIZED); return optimize(n,needlval,parent); } } case F_DO: { if(node_is_true(n->u.s.b.node)) { n->token=F_BRANCH; free_node(n->u.s.b.node); n->u.s.b.node=NULL; } if(node_is_false(n->u.s.b.node)) { tmp1=n->u.s.a.node; n->u.s.a.node=NULL; free_node(n); return optimize(tmp1,needlval,parent); } } } #ifdef MALLOC_DEBUG check_sfltable(); #endif if(is_const(n) && (n->opt_info & OPT_TRY_OPTIMIZE) && parent!=n && /* this is used to avoid doing things a zillion times */ /* (!parent || !is_const(parent)) && I would like to do this * but it is not yet known if parent is const */ !num_parse_error) { tmp1=eval(n); free_node(n); return tmp1; } #ifdef LINMALLOC_DEBUG assert_node(n); #endif return n; } /* depend_optim: nullify a node at a time and see anything depends on it * beeing there */ static int depend_optim(node **a,node *b,int chronologie,int voided,node *tree2) { node *n,*n2; if(!(n=*a)) return 0; #ifdef LINMALLOC_DEBUG assert_node(*a); #endif #ifdef MALLOC_DEBUG check_sfltable(); #endif if(!(n->opt_info & (OPT_SIDE_EFFECT | OPT_CASE | OPT_CONT | OPT_BREAK | OPT_GLOBAL_ASSIGNMENT | OPT_RETURN))) { *a=0; if(!depend_p(b,n) && !depend_p(tree2,n)) { free_node(n); return 1; } *a=n; } switch(n->token) { case F_DO: { int i; tree2=mknode('{',n->u.s.b.node,tree2); i=depend_optim(&(n->u.s.a.node),b,0,0,tree2); n2=tree2->u.s.a.node; tree2->u.s.b.node=tree2->u.s.a.node=NULL; free_node(tree2); tree2=n2; if(i) { n->opt_info&=~OPT_OPTIMIZED; return 1; } return 0; } case F_INC_NEQ_LOOP: case F_DEC_NEQ_LOOP: case F_INC_LOOP: case F_DEC_LOOP: case F_FOREACH: case F_FOR: { int i; tree2=mknode('{',n->u.s.a.node,tree2); i=depend_optim(&(n->u.s.b.node),b,0,0,tree2); n2=tree2->u.s.a.node; tree2->u.s.b.node=tree2->u.s.a.node=NULL; free_node(tree2); tree2=n2; if(i) { n->opt_info&=~OPT_OPTIMIZED; return 1; } return 0; } case F_CAST: if(n->type==TYPE_VOID) { if(depend_optim(&(n->u.s.a.node),b,chronologie,1,tree2)) { n->opt_info&=~OPT_OPTIMIZED; return 1; }else{ return 0; } } return 0; case F_ARG_LIST: case '{': if(n->token == F_ARG_LIST && !voided) return 0; if(chronologie) { int i; /* if chronological dependency is wanted, nullify and ignore * things that happened before this node while testing */ while(depend_optim(&(n->u.s.a.node),b,chronologie,1,tree2)) n->opt_info&=~OPT_OPTIMIZED; n2=n->u.s.a.node; n->u.s.a.node=0; i=depend_optim(&(n->u.s.b.node),b,chronologie,1,tree2); if(i) n->opt_info&=~OPT_OPTIMIZED; n->u.s.a.node=n2; return i; }else{ int i; tree2=mknode('{',n->u.s.b.node,tree2); n->u.s.b.node=NULL; i=depend_optim(&(n->u.s.a.node),b,chronologie,1,tree2); n->u.s.b.node=tree2->u.s.a.node; n2=tree2; tree2=tree2->u.s.b.node; n2->u.s.a.node=n2->u.s.b.node=NULL; free_node(n2); tree2=mknode('{',tree2,n->u.s.a.node); n->u.s.a.node=NULL; i|=depend_optim(&(n->u.s.b.node),b,chronologie,1,tree2); n->u.s.a.node=tree2->u.s.b.node; n2=tree2; tree2=tree2->u.s.a.node; n2->u.s.b.node=n2->u.s.a.node=NULL; free_node(n2); if(i) n->opt_info&=~OPT_OPTIMIZED; return i; } } return 0; } /* we need to dome computations to see if the arguments are pointer arguments * if so, let the function depend on those, so we won't remove code that * changes shared arrays. */ static void do_depend_optim(node **n,int args) { node *tree2; int e; tree2=NULL; for(e=0;e<args;e++) { if(type_of_locals[e]==TYPE_MAPPING || type_of_locals[e]&TYPE_MOD_POINTER) { tree2=mknode(F_ARG_LIST,mklocalnode(e),tree2); } } tree2=call_optimizer(mknode(F_RETURN,mkefunnode(F_AGGREGATE,tree2),0)); while(depend_optim(n,*n,1,0,tree2)); free_node(tree2); } static void do_pop(int nr) { if(!nr) return; if(nr==1) { ins_f_byte(F_POP_VALUE); }else if(nr<256){ ins_f_byte(F_POP_N_ELEMS); ins_byte(nr,A_PROGRAM); }else{ ins_f_byte(F_POP_N_ELEMS); ins_byte(255,A_PROGRAM); do_pop(nr-255); } } #define JUMP_CONDITIONAL 1 #define JUMP_UNSET 2 struct jump { unsigned char flags; unsigned char size; short token; int whereto; int wherefrom; int address; }; static int jump_ptr, max_jumps; static struct jump jumps[256]; static void set_branch(int address,int whereto) { int j,e; if(address<0) return; j=jump_ptr; e=max_jumps; while(e>=0) { if(jumps[j].address==address && (jumps[j].flags & JUMP_UNSET)) break; if(j) j--; else j=max_jumps; e--; } if(e<0) j=-1; /* not found */ for(e=0;e<=max_jumps;e++) { if(jumps[e].flags & JUMP_UNSET) continue; if(jumps[e].flags & JUMP_CONDITIONAL) continue; if(jumps[e].wherefrom!=whereto) continue; #if defined(LASDEBUG) if(lasdebug>1) printf("Optimized Jump to a jump\n"); #endif whereto=jumps[e].whereto; break; } if(e>=0) { if(!(jumps[j].flags & JUMP_CONDITIONAL)) { for(e=0;e<=max_jumps;e++) { if(jumps[e].flags & JUMP_UNSET) continue; if(jumps[e].size!=4) continue; if(jumps[e].whereto==jumps[j].wherefrom) { upd_branch(jumps[e].address,whereto); jumps[e].whereto=whereto; #if defined(LASDEBUG) if(lasdebug>1) printf("Optimized Jump to a jump\n"); #endif } } } jumps[j].whereto=whereto; jumps[j].flags&=~JUMP_UNSET; } upd_branch(address,whereto); } static int do_jump(int token,int whereto) { jump_ptr=(jump_ptr+1)%NELEM(jumps); if(jump_ptr>max_jumps) max_jumps=jump_ptr; jumps[jump_ptr].flags=JUMP_UNSET; jumps[jump_ptr].whereto=-1; jumps[jump_ptr].token=token; jumps[jump_ptr].size=sizeof(int); if(token!=F_BRANCH && token!=F_SHORT_BRANCH) jumps[jump_ptr].flags|=JUMP_CONDITIONAL; if(token==F_SHORT_BRANCH || token==F_SHORT_BRANCH_WHEN_ZERO || token==F_SHORT_BRANCH_WHEN_NON_ZERO) /* not used (yet) */ jumps[jump_ptr].size=1; jumps[jump_ptr].wherefrom=PC; ins_f_byte(token); jumps[jump_ptr].address=PC; ins_long(0,A_PROGRAM); if(whereto!=-1) set_branch(jumps[jump_ptr].address,whereto); return jumps[jump_ptr].address; } static void clean_jumptable() { max_jumps=jump_ptr=-1; } static void push_break_stack() { push_explicit((int)break_stack); push_explicit(current_break); push_explicit(break_stack_size); break_stack_size=10; break_stack=(int *)xalloc(sizeof(int)*break_stack_size); current_break=0; } static void pop_break_stack(int jump) { for(current_break--;current_break>=0;current_break--) set_branch(break_stack[current_break],jump); free((char *)break_stack); break_stack_size=pop_address(); current_break=pop_address(); break_stack=(int *)pop_address(); } static void push_continue_stack() { push_explicit((int)continue_stack); push_explicit(current_continue); push_explicit(continue_stack_size); continue_stack_size=10; continue_stack=(int *)xalloc(sizeof(int)*continue_stack_size); current_continue=0; } static void pop_continue_stack(int jump) { for(current_continue--;current_continue>=0;current_continue--) set_branch(continue_stack[current_continue],jump); free((char *)continue_stack); continue_stack_size=pop_address(); current_continue=pop_address(); continue_stack=(int *)pop_address(); } static int do_docode2(node *n,int needlval); static int do_docode(node *n,int needlval) { int i; int save_current_line=current_line; if(!n) return 0; current_line=n->lnum; i=do_docode2(n,needlval); #ifdef MALLOC_DEBUG check_sfltable(); #endif current_line=save_current_line; return i; } static int docode(node *n) { clean_jumptable(); return do_docode(n,0); } static int do_jump_when_non_zero(node *n,int j) { if(!(n->opt_info & OPT_SIDE_EFFECT)) { if(node_is_true(n)) return do_jump(F_BRANCH,j); if(node_is_false(n)) return -1; } if(do_docode(n,0)!=1) fatal("Infernal compiler skiterror.\n"); return do_jump(F_BRANCH_WHEN_NON_ZERO,j); } static void ins_add(node *a,node *b) { if(a->type==b->type) { if(a->type==TYPE_NUMBER) ins_f_byte(F_ADD_INT); else ins_f_byte(F_ADD); }else{ ins_f_byte(F_ADD); } } static int do_jump_when_zero(node *n,int j) { if(!(n->opt_info & OPT_SIDE_EFFECT)) { if(node_is_true(n)) return -1; if(node_is_false(n)) return do_jump(F_BRANCH,j); } if(do_docode(n,0)!=1) fatal("Infernal compiler skiterror.\n"); return do_jump(F_BRANCH_WHEN_ZERO,j); } static int do_docode2(node *n,int needlval) { int tmp1,tmp2,tmp3; #ifdef MALLOC_DEBUG check_sfltable(); #endif if(!n) return 0; if(needlval & 1) { switch(n->token) { case F_ASSIGN: if(needlval & 2) break; default: yyerror("Illegal lvalue."); ins_int(0); return 1; case F_LVALUE_LIST: case F_LOCAL: case F_GLOBAL: case F_INDEX: case F_ARG_LIST: break; } } switch(n->token) { case F_PUSH_ARRAY: tmp1=do_docode(n->u.s.a.node,2); if(tmp1!=1) { fatal("Internal compiler error, Yikes!\n"); } ins_f_byte(F_PUSH_ARRAY); return -0x7ffffff; case F_STRING: tmp1=store_prog_string(n->u.s.a.str); if(tmp1<256) { ins_f_byte(F_SHORT_STRING); ins_byte(tmp1,A_PROGRAM); }else{ ins_f_byte(F_STRING); ins_short(tmp1,A_PROGRAM); } return 1; case F_NUMBER: ins_int(n->u.s.a.number); return 1; case F_FLOAT: ins_float(n->u.s.a.fnum); return 1; case '?': { int tmp5=0; /* make gcc happy */ tmp1=do_jump_when_zero(n->u.s.a.node,-1); tmp3=do_docode(n->u.s.b.node->u.s.a.node,0); if(tmp3) { ins_f_byte(F_POP_N_ELEMS); tmp5=PC; ins_byte(0,A_PROGRAM); } if(n->u.s.b.node->u.s.b.node) { int tmp4; tmp2=do_jump(F_BRANCH,-1); set_branch(tmp1,PC); tmp4=do_docode(n->u.s.b.node->u.s.b.node,0); if(tmp4>tmp3) { do_pop(tmp4-tmp3); }else if(tmp4<tmp3){ mem_block[A_PROGRAM].block[tmp5]=tmp3-tmp4; tmp3=tmp4; } tmp1=tmp2; }else{ if(tmp3) { mem_block[A_PROGRAM].block[tmp5]=tmp3; tmp3=0; } } set_branch(tmp1,PC); return tmp3; } case F_AND_EQ: case F_OR_EQ: case F_XOR_EQ: case F_LSH_EQ: case F_RSH_EQ: case F_ADD_EQ: case F_SUB_EQ: case F_MULT_EQ: case F_MOD_EQ: case F_DIV_EQ: tmp1=do_docode(n->u.s.a.node,1); if(n->u.s.a.node->type & TYPE_MOD_POINTER) { if(do_docode(n->u.s.b.node,2)!=1) fatal("Internal compiler error, shit happens\n"); ins_f_byte(F_PUSH_LTOSVAL2); }else{ ins_f_byte(F_PUSH_LTOSVAL); if(do_docode(n->u.s.b.node,2)!=1) fatal("Internal compiler error, shit happens (again)\n"); } switch(n->token) { case F_ADD_EQ: ins_add(n->u.s.a.node,n->u.s.b.node); break; case F_AND_EQ: ins_f_byte(F_AND); break; case F_OR_EQ: ins_f_byte(F_OR); break; case F_XOR_EQ: ins_f_byte(F_XOR); break; case F_LSH_EQ: ins_f_byte(F_LSH); break; case F_RSH_EQ: ins_f_byte(F_RSH); break; case F_SUB_EQ: ins_f_byte(F_SUBTRACT); break; case F_MULT_EQ:ins_f_byte(F_MULTIPLY);break; case F_MOD_EQ: ins_f_byte(F_MOD); break; case F_DIV_EQ: ins_f_byte(F_DIVIDE); break; } ins_f_byte(F_ASSIGN); return tmp1; case F_ASSIGN: case F_ASSIGN_AND_POP: switch(n->u.s.a.node->token) { case F_AND: case F_OR: case F_XOR: case F_LSH: case F_RSH: case F_ADD: case F_MOD: case F_SUBTRACT: case F_DIVIDE: case F_MULTIPLY: if(node_is_eq(n->u.s.b.node,n->u.s.a.node->u.s.a.node)) { tmp1=do_docode(n->u.s.b.node,1); if((n->u.s.b.node->type & TYPE_MOD_POINTER) || (n->u.s.b.node->type & TYPE_MOD_MASK)==TYPE_ANY) { if(do_docode(n->u.s.a.node->u.s.b.node,2)!=1) fatal("Infernal compiler error (dumpar core |ver hela mattan).\n"); ins_f_byte(F_PUSH_LTOSVAL2); }else{ ins_f_byte(F_PUSH_LTOSVAL); if(do_docode(n->u.s.a.node->u.s.b.node,2)!=1) fatal("Infernal compiler error (dumpar core).\n"); } if(n->u.s.a.node->token==F_ADD) ins_add(n->u.s.a.node->u.s.a.node,n->u.s.a.node->u.s.b.node); else ins_f_byte(n->u.s.a.node->token); ins_f_byte(n->token); return n->token==F_ASSIGN; } default: if(n->u.s.b.node->token!=F_LOCAL && n->u.s.b.node->token!=F_GLOBAL) tmp1=do_docode(n->u.s.b.node,1); if(do_docode(n->u.s.a.node,0)!=1) fatal("Infernal compiler error *sigh*.\n"); switch(n->u.s.b.node->token) { case F_LOCAL: ins_f_byte(n->token==F_ASSIGN?F_ASSIGN_LOCAL:F_ASSIGN_LOCAL_AND_POP); ins_byte(n->u.s.b.node->u.s.a.number,A_PROGRAM); break; case F_GLOBAL: ins_f_byte(n->token==F_ASSIGN?F_ASSIGN_GLOBAL:F_ASSIGN_GLOBAL_AND_POP); ins_byte(n->u.s.b.node->u.s.a.number,A_PROGRAM); break; default: ins_f_byte(n->token); break; } return n->token==F_ASSIGN; } case F_LAND: case F_LOR: if(do_docode(n->u.s.a.node,0)!=1) fatal("Compiler internal error.\n"); tmp1=do_jump(n->token,-1); if(do_docode(n->u.s.b.node,0)!=1) fatal("Compiler internal error.\n"); set_branch(tmp1,PC); return 1; case F_ADD: tmp1=do_docode(n->u.s.a.node,2); if(do_docode(n->u.s.b.node,2)!=1) fatal("Compiler internal error.\n"); ins_add(n->u.s.a.node,n->u.s.b.node); return tmp1; case F_EQ: case F_NE: tmp1=do_docode(n->u.s.a.node,0); if(do_docode(n->u.s.b.node,0)!=1) fatal("Compiler internal error (gnng!).\n"); ins_f_byte(n->token); return tmp1; case F_LT: case F_LE: case F_GT: case F_GE: case F_SUBTRACT: case F_MULTIPLY: case F_DIVIDE: case F_MOD: case F_LSH: case F_RSH: case F_XOR: case F_OR: case F_AND: tmp1=do_docode(n->u.s.a.node,2); if(do_docode(n->u.s.b.node,2)!=1) fatal("Compiler internal error.\n"); ins_f_byte(n->token); return tmp1; case F_RANGE: tmp1=do_docode(n->u.s.a.node,2); if(do_docode(n->u.s.b.node,2)!=2) fatal("Compiler internal error.\n"); ins_f_byte(n->token); return tmp1; case F_INC: case F_DEC: case F_POST_INC: case F_POST_DEC: tmp1=do_docode(n->u.s.a.node,1); ins_f_byte(n->token); return tmp1; case F_INC_AND_POP: case F_DEC_AND_POP: tmp1=do_docode(n->u.s.a.node,1); ins_f_byte(n->token); return tmp1-1; case F_NOT: case F_COMPL: case F_NEGATE: tmp1=do_docode(n->u.s.a.node,2); ins_f_byte(n->token); return tmp1; case F_FOR: { struct vector *tmp_store; tmp_store=current_switch_mapping; current_switch_mapping=(struct vector *)NULL; push_break_stack(); push_continue_stack(); tmp1=do_jump(F_BRANCH,-1); tmp2=PC; if(n->u.s.b.node) do_pop(do_docode(n->u.s.b.node->u.s.a.node,0)); pop_continue_stack(PC); if(n->u.s.b.node) do_pop(do_docode(n->u.s.b.node->u.s.b.node,0)); set_branch(tmp1,PC); do_jump_when_non_zero(n->u.s.a.node,tmp2); pop_break_stack(PC); current_switch_mapping=tmp_store; return 0; } case ' ': return do_docode(n->u.s.a.node,0)+do_docode(n->u.s.b.node,1); case F_FOREACH: { struct vector *tmp_store; tmp_store=current_switch_mapping; current_switch_mapping=(struct vector *)NULL; tmp2=do_docode(n->u.s.a.node,2); ins_f_byte(F_CONST0); tmp3=do_jump(F_BRANCH,-1); tmp1=PC; push_break_stack(); push_continue_stack(); do_pop(do_docode(n->u.s.b.node,0)); pop_continue_stack(PC); set_branch(tmp3,PC); do_jump(n->token,tmp1); pop_break_stack(PC); current_switch_mapping=tmp_store; return tmp2+1; } case F_INC_NEQ_LOOP: case F_DEC_NEQ_LOOP: case F_INC_LOOP: case F_DEC_LOOP: { struct vector *tmp_store; tmp_store=current_switch_mapping; current_switch_mapping=(struct vector *)NULL; tmp2=do_docode(n->u.s.a.node,0); tmp3=do_jump(F_BRANCH,-1); tmp1=PC; push_break_stack(); push_continue_stack(); do_pop(do_docode(n->u.s.b.node,0)); pop_continue_stack(PC); set_branch(tmp3,PC); do_jump(n->token,tmp1); pop_break_stack(PC); current_switch_mapping=tmp_store; return tmp2; } case F_DO: { struct vector *tmp_store; tmp_store=current_switch_mapping; current_switch_mapping=(struct vector *)NULL; tmp2=PC; push_break_stack(); push_continue_stack(); do_pop(do_docode(n->u.s.a.node,0)); pop_continue_stack(PC); do_jump_when_non_zero(n->u.s.b.node,tmp2); pop_break_stack(PC); current_switch_mapping=tmp_store; return 0; } case F_BRANCH: { struct vector *tmp_store; tmp_store=current_switch_mapping; current_switch_mapping=(struct vector *)NULL; tmp2=PC; push_break_stack(); push_continue_stack(); do_pop(do_docode(n->u.s.a.node,0)); pop_continue_stack(PC); do_jump(F_BRANCH,tmp2); pop_break_stack(PC); current_switch_mapping=tmp_store; return 0; } case F_CAST: tmp1=do_docode(n->u.s.a.node,0); if(n->type==TYPE_VOID) { do_pop(tmp1); return 0; } if(!tmp1) { ins_f_byte(F_CONST0); tmp1=1; } switch(n->type) { case TYPE_STRING: ins_f_byte(F_CAST_TO_STRING); break; case TYPE_NUMBER: ins_f_byte(F_CAST_TO_INT); break; case TYPE_FLOAT: ins_f_byte(F_CAST_TO_FLOAT); break; case TYPE_OBJECT: ins_f_byte(F_CAST_TO_OBJECT); break; case TYPE_FUNCTION: ins_f_byte(F_CAST_TO_FUNCTION); break; case TYPE_REGULAR_EXPRESSION: ins_f_byte(F_CAST_TO_REGEXP); break; } return tmp1; case F_EFUN_CALL: if(n->u.s.a.number==F_CALL_FUNCTION) { node **a,*b; a=first_arg(&(n->u.s.b.node)); if(a && a[0]->token==F_CONSTANT_FUNCTION) { ins_f_byte(F_MARK); b=a[0]; a[0]=NULL; do_docode(n->u.s.b.node,0); a[0]=b; ins_f_byte(a[0]->u.s.a.number+F_MAX_OPCODE); return 1; } } if(instrs[n->u.s.a.number-F_OFFSET].min_arg!=instrs[n->u.s.a.number-F_OFFSET].max_arg) { ins_f_byte(F_MARK); do_docode(n->u.s.b.node,0); ins_f_byte(n->u.s.a.number); }else{ if(n->u.s.b.node && BASIC_TYPE(n->u.s.b.node->type,TYPE_MAPPING)) { tmp1=do_docode(n->u.s.b.node,0); }else{ tmp1=do_docode(n->u.s.b.node,2); } if(tmp1>256) yyerror("To many arguments to function call."); if(tmp1<0) yyerror("Can't use '@' with fixed argument efun.\n"); if(tmp1!=instrs[n->u.s.a.number-F_OFFSET].min_arg) fatal("GAAH, INTERNAL COMPILER ERROR.\n"); ins_f_byte(n->u.s.a.number); } if((n->type & TYPE_MOD_MASK)==TYPE_VOID) return 0; else return 1; case '{': do_pop(do_docode(n->u.s.a.node,2)); do_pop(do_docode(n->u.s.b.node,2)); return 0; case F_ARG_LIST: return do_docode(n->u.s.a.node,needlval & ~1)+ do_docode(n->u.s.b.node,needlval); case F_SWITCH: { int e,range; struct vector *v; struct vector *tmp_store=current_switch_mapping; if(do_docode(n->u.s.a.node,0)!=1) fatal("Internal compiler error, time to panic\n"); current_switch_mapping=allocate_mapping(allocate_array(0), allocate_array(0)); ins_f_byte(F_SWITCH); tmp1=PC; ins_short(0,A_PROGRAM); push_break_stack(); do_pop(do_docode(n->u.s.b.node,2)); v=current_switch_mapping->item[1].u.vec; for(range=e=0;e<v->size;e++) { char *fel,*tmp; switch(v->item[e].subtype) { case 0: if(range) break; continue; case 1: if(range++) break; continue; case 2: range--; continue; default: fatal("Internal error in switch mapping.\n"); } switch(v->item[e].subtype==0) { default: fel="Case inside range"; break; case 1: fel="Ranges overlap"; break; } init_buf(); my_strcat(fel); my_strcat(" at "); save_svalue(current_switch_mapping->item[0].u.vec->item+e); tmp=simple_free_buf(); yyerror(tmp); free(tmp); } if(!current_switch_mapping->item[2].u.number) { current_switch_mapping->item[2].u.number=PC; } pop_break_stack(PC); upd_short(tmp1,mem_block[A_SWITCH_MAPPINGS].current_size / sizeof(struct vector *)); add_to_mem_block(A_SWITCH_MAPPINGS, (char *)¤t_switch_mapping, sizeof(struct vector *)); current_switch_mapping=tmp_store; return 0; } case F_CASE: { extern struct svalue *sp; struct svalue bar; if(!current_switch_mapping) { yyerror("Case outside switch."); }else{ if(!is_const(n->u.s.a.node)) yyerror("Case label isn't constant."); tmp1=eval_low(n->u.s.a.node); if(tmp1<1) { yyerror("Error in case label."); return 0; } if(!n->u.s.b.node) { if(assoc(sp,current_switch_mapping->item[0].u.vec)>=0) { yyerror("Duplicate case"); }else{ bar.type=T_NUMBER; bar.subtype=NUMBER_NUMBER; bar.u.number=PC; insert_alist(sp,&bar,current_switch_mapping); } }else{ if(assoc(sp,current_switch_mapping->item[0].u.vec)>=0) { yyerror("Duplicate case"); }else{ bar.type=T_NUMBER; bar.subtype=1; bar.u.number=PC; insert_alist(sp,&bar,current_switch_mapping); } if(!is_const(n->u.s.b.node)) yyerror("Case label isn't constant."); tmp1+=tmp2=eval_low(n->u.s.b.node); if(tmp2<1) { pop_n_elems(tmp1); yyerror("Error in case label."); return 0; } if(sp->type!=sp[-1].type) yyerror("Mixed types in case range"); if(assoc(sp,current_switch_mapping->item[0].u.vec)>=0) { yyerror("Duplicate case"); }else{ bar.subtype=2; insert_alist(sp,&bar,current_switch_mapping); } } pop_n_elems(tmp1); } } return 0; case F_DEFAULT: if(!current_switch_mapping) { yyerror("Default outside switch."); }else{ if(current_switch_mapping->item[2].u.number) { yyerror("Dual Default in switch."); } current_switch_mapping->item[2].u.number=PC; } return 0; case F_BREAK: if(!break_stack) { yyerror("Break outside loop or switch."); }else{ if(current_break>=break_stack_size) { break_stack_size*=2; break_stack=(int *)realloc((char *)break_stack, sizeof(int)*break_stack_size); } break_stack[current_break++]=do_jump(F_BRANCH,-1); } return 0; case F_CONTINUE: if(!continue_stack) { yyerror("continue outside loop or switch."); }else{ if(current_continue>=continue_stack_size) { continue_stack_size*=2; continue_stack=(int *)realloc((char *)continue_stack, sizeof(int)*continue_stack_size); } continue_stack[current_continue++]=do_jump(F_BRANCH,-1); } return 0; case F_RETURN: if(n->u.s.a.node->token==F_EFUN_CALL && n->u.s.a.node->u.s.a.number==F_CALL_FUNCTION) { ins_f_byte(F_MARK); do_docode(n->u.s.a.node->u.s.b.node,0); ins_f_byte(F_TAILRECURSE); }else{ if(do_docode(n->u.s.a.node,0)!=1) ins_f_byte(F_CONST0); ins_f_byte(F_RETURN); } return 0; case F_SSCANF: tmp1=do_docode(n->u.s.a.node,2); tmp2=do_docode(n->u.s.b.node,2); ins_f_byte(F_SSCANF); ins_byte(tmp2+2,A_PROGRAM); return tmp1-1; case F_SWAP_VARIABLES: tmp1=do_docode(n->u.s.a.node,0); if(tmp1!=2) fatal("Infernal compiler error (skit oxo)\n"); ins_f_byte(F_SWAP_VARIABLES); return 0; case F_CATCH: { struct vector *tmp_store; tmp_store=current_switch_mapping; current_switch_mapping=(struct vector *)NULL; tmp1=do_jump(F_CATCH,-1); push_break_stack(); push_continue_stack(); do_pop(do_docode(n->u.s.a.node,0)); pop_continue_stack(PC); pop_break_stack(PC); ins_f_byte(F_DUMB_RETURN); set_branch(tmp1,PC); current_switch_mapping=tmp_store; return 1; } case F_GAUGE: { struct vector *tmp_store; tmp_store=current_switch_mapping; current_switch_mapping=(struct vector *)NULL; ins_f_byte(F_PUSH_COST); do_pop(do_docode(n->u.s.a.node,2)); ins_f_byte(F_PUSH_COST); ins_f_byte(F_SUBTRACT); ins_f_byte(F_NEGATE); current_switch_mapping=tmp_store; return 1; } case F_LVALUE_LIST: return do_docode(n->u.s.a.node,1)+do_docode(n->u.s.b.node,1); case F_INDEX: tmp1=do_docode(n->u.s.a.node,needlval | 2); if(do_docode(n->u.s.b.node,2)!=1) fatal("Internal compiler error, please report this."); if(needlval & 1) { ins_f_byte(F_PUSH_INDEXED_LVALUE); }else{ ins_f_byte(F_INDEX); if(!(needlval & 2)) { while(n && n->token==F_INDEX) n=n->u.s.a.node; if(n->token==F_CONSTANT) ins_f_byte(F_COPY_VALUE); } } return tmp1; case F_CONSTANT: tmp1=store_constant(&(n->u.sval)); if(tmp1>255) { ins_f_byte(F_CONSTANT); ins_short(tmp1,A_PROGRAM); }else{ ins_f_byte(F_SHORT_CONSTANT); ins_byte(tmp1,A_PROGRAM); } if(!(needlval & 2)) /* copy later */ if(IS_TYPE(n->u.sval,BT_VECTOR)) ins_f_byte(F_COPY_VALUE); return 1; case F_LOCAL: if(needlval & 1) { ins_f_byte(F_PUSH_LOCAL_LVALUE); ins_byte(n->u.s.a.number,A_PROGRAM); }else{ ins_f_byte(F_LOCAL); ins_byte(n->u.s.a.number,A_PROGRAM); } return 1; case F_GLOBAL: if(needlval & 1) { ins_f_byte(F_PUSH_GLOBAL_LVALUE); ins_byte(n->u.s.a.number,A_PROGRAM); }else{ ins_f_byte(F_GLOBAL); ins_byte(n->u.s.a.number,A_PROGRAM); } return 1; case F_CONSTANT_FUNCTION: if(needlval & 1) { yyerror("Function name is not a lvalue."); } if(n->u.s.a.number<256) { ins_f_byte(F_SHORT_CONSTANT_FUNCTION); ins_byte(n->u.s.a.number,A_PROGRAM); }else{ ins_f_byte(F_CONSTANT_FUNCTION); ins_short(n->u.s.a.number,A_PROGRAM); } return 1; case F_PUSH_SIMUL_EFUN: ins_f_byte(F_PUSH_SIMUL_EFUN); ins_short(n->u.s.a.number,A_PROGRAM); return 1; default: fatal("Infernal compiler error (unknown parse-tree-token).\n"); return 0; /* make gcc happy */ } } void setup_fake_program() { fake_prog.next_hash=0; fake_prog.program=mem_block[A_PROGRAM].block; fake_prog.program_size=mem_block[A_PROGRAM].current_size; fake_prog.line_numbers=(char *)mem_block[A_LINENUMBERS].block; fake_prog.num_line_numbers=mem_block[A_LINENUMBERS].current_size; fake_prog.strings=(char **)mem_block[A_STRINGS].block; fake_prog.num_strings= mem_block[A_STRINGS].current_size/sizeof(char **); fake_prog.constants=(struct svalue *)mem_block[A_CONSTANTS].block; fake_prog.num_constants= mem_block[A_CONSTANTS].current_size/sizeof(struct svalue); fake_prog.functions=(struct function *)mem_block[A_FUNCTIONS].block; fake_prog.num_functions= mem_block[A_FUNCTIONS].current_size/sizeof(struct function); fake_prog.function_ptrs= (struct function_p *)mem_block[A_FUNCTION_PTRS].block; fake_prog.num_function_ptrs= mem_block[A_FUNCTION_PTRS].current_size/sizeof(struct function_p); fake_prog.switch_mappings=(struct vector **) mem_block[A_SWITCH_MAPPINGS].block; fake_prog.num_switch_mappings= mem_block[A_SWITCH_MAPPINGS].current_size/sizeof(struct vector **); fake_prog.inherit=(struct inherit *)mem_block[A_INHERITS].block; fake_prog.num_inherited = mem_block[A_INHERITS].current_size / sizeof (struct inherit); fake_prog.inherit[0].prog=&fake_prog; fake_prog.lfuns=NULL; fake_prog.num_lfuns=0; fake_object.prog=&fake_prog; fake_object.ref=1; } static int eval_low(node *n) { extern struct svalue *sp; extern int error_recovery_context_exists; extern jmp_buf error_recovery_context; extern struct object *command_giver,*current_object; struct object *save_command_giver = command_giver; struct object *save_current_object = current_object; extern struct svalue catch_value; extern struct control_stack *csp; extern struct program *current_prog; extern struct svalue *fp; int returnval; VOLATILE int foo,fo2; int e; char **bar; struct svalue *save_sp,*gazonk; int jump=PC; save_sp=sp; if(num_parse_error) return -1; #ifdef MALLOC_DEBUG check_sfltable(); #endif store_linenumbers=0; do_docode(n,0); ins_f_byte(F_DUMB_RETURN); store_linenumbers=1; if(num_parse_error) return -1; foo=mem_block[A_STRINGS].current_size/sizeof(char *); fo2=mem_block[A_CONSTANTS].current_size/sizeof(struct svalue); push_control_stack(0); push_pop_error_context (1); csp->num_local_variables = 0; /* No extra variables */ fp = sp - csp->num_local_variables + 1; setup_fake_program(); current_prog=&fake_prog; current_object=&fake_object; /* signal catch OK - print no err msg */ error_recovery_context_exists = 2; if (setjmp(error_recovery_context)) { push_pop_error_context(-1); pop_control_stack(); if(catch_value.type==T_STRING) { if(strcmp(strptr(&catch_value),"**")) { yyerror(strptr(&catch_value)); } }else{ yyerror("Eval error"); } while(sp>save_sp) pop_stack(); returnval=-1; }else{ eval_instruction(mem_block[A_PROGRAM].block+jump); pop_control_stack(); push_pop_error_context(0); returnval=sp-save_sp; } PC=jump; /* free any strings we no longer need */ bar=(char **)mem_block[A_STRINGS].block; e=mem_block[A_STRINGS].current_size/sizeof(char *)-1; for(;e>=foo;e--) free_string(bar[e]); mem_block[A_STRINGS].current_size=foo*sizeof(char *); /* remove constants not needed */ gazonk=(struct svalue *)mem_block[A_CONSTANTS].block; e=mem_block[A_CONSTANTS].current_size/sizeof(struct svalue)-1; for(;e>=fo2;e--) free_svalue(gazonk+e); mem_block[A_CONSTANTS].current_size=fo2*sizeof(struct svalue); command_giver=save_command_giver; current_object=save_current_object; return returnval; } static node *eval(node *n) { node *new; int args; extern struct svalue *sp; if(!is_const(n)) return copy_node(n); args=eval_low(n); #ifdef MALLOC_DEBUG check_sfltable(); #endif switch(args) { case -1: n=copy_node(n); break; case 0: n=NULL; break; case 1: new=mksvaluenode(sp); pop_stack(); n=optimize(new,0,new); break; default: new=NULL; while(args--) { new=mknode(F_ARG_LIST,mksvaluenode(sp),new); pop_stack(); } n=optimize(new,0,new); } #ifdef MALLOC_DEBUG check_sfltable(); #endif return n; } int last_function_opt_info; void dooptcode(node *n,int args) { last_function_opt_info=OPT_SIDE_EFFECT; #ifdef LASDEBUG if(lasdebug) { fprintf(stderr,"Optimizing:\n"); nice_print_tree(n); } #endif if(!num_parse_error) { n=call_optimizer(n); #ifdef LASDEBUG if(lasdebug>1) { fprintf(stderr,"Result after optimize:\n"); nice_print_tree(n); } #endif do_depend_optim(&n,args); clear_remember(); n=call_optimizer(n); #ifdef LASDEBUG if(lasdebug) { fprintf(stderr,"Result after depend_optim:\n"); nice_print_tree(n); } #endif } if(!num_parse_error) { do_pop(docode(n)); last_function_opt_info=n->opt_info; } free_node(n); } int get_opt_info() { if (last_function_opt_info&(OPT_SIDE_EFFECT | OPT_GLOBAL_ASSIGNMENT)) return TYPE_MOD_SIDE_EFFECT; if(last_function_opt_info & OPT_GLOBAL_DEPEND) return 0; else return TYPE_MOD_CONSTANT; }