/* jit2.c Copyright (C) 2006-2007, Codemist Ltd */ /* and J O'Connell */ /* * Helper functions for just in time compiler. A lot of this * is architecture-independent, but a few places are concerned with * the particular encoding of relative branches etc. */ /* * This code may be used and modified, and redistributed in binary * or source form, subject to the "CCL Public License", which should * accompany it. This license is a variant on the BSD license, and thus * permits use of code derived from this in either open and commercial * projects: but it does require that updates to this code be made * available back to the originators of the package. * Before merging other code in with this or linking this code * with other packages or libraries please check that the license terms * of the other material are compatible with those of this. */ /* Signature: 57e85a33 18-Jan-2007 */ #ifndef WIN32 #include <sys/mman.h> #endif #include "headers.h" #include "bytes.h" #include "jit.h" /* * The following function is provided so that during debugging the JIT can * generate calls to it in the compiled code. It is then a convenient place * to set a debugger break-point. As can be seen, it does not do anything * beyond that! */ extern void break_here(); void break_here() { } reloc_table *labels_table; reloc_table *callers_table; reloc_table *jumpers_table; variables_table *vars_table; reloc_table *disassembler_labels; /* * Having a fixed-size buffer for collecting compiled code in is a temporary * situation! */ unsigned char codebuffer[8192]; unsigned long codep; /*************************************************************************/ reloc_table *add_label_to_table(reloc_table *table, char *label, unsigned long pos) { /* new element added to end of list */ reloc_table *new,*current; current = table; /* set current to head of list */ #ifdef JDEBUG /* check for any duplicate labels before adding. */ /* labels determined by programmer so only needed while coding */ reloc_table *tmp; tmp = table; while (tmp != NULL) { if (strcmp(tmp->label,label)==0) { printf("\nError in reloc table\nDuplicate entry: %s\n",label); return table; } else tmp = tmp->next; } #endif /* Any elements added yet? */ /* * Hmmm - do I like using malloc()??? */ if (current == NULL) { current = malloc(sizeof(reloc_table)); current->next = table; table = current; current->label = (char*)malloc(strlen(label)+1); strcpy(current->label,label); current->pos = pos; } else { /* Already elements in list */ while (current->next != NULL) current = current->next; /* Now at end of linked list */ new = malloc(sizeof(reloc_table)); current->next = new; new->next = NULL; new->label = (char*)malloc(strlen(label)+1); strcpy(new->label,label); new->pos = pos; } return table; } reloc_table* add_caller_to_table(reloc_table *table, char *label, unsigned long pos) { /* new element added to end of list */ reloc_table *new, *current; current = table; /* set current to head of list */ /* Any elements added yet? */ if (current == NULL) { current = malloc(sizeof(reloc_table)); current->next = table; table = current; current->label = (char*)malloc(strlen(label)+1); strcpy(current->label,label); current->pos = pos; } else { /* Already elements in list */ while (current->next != NULL) current = current->next; /* Now at end of linked list */ new = malloc(sizeof(reloc_table)); current->next = new; new->next = NULL; new->label = (char*)malloc(strlen(label)+1); strcpy(new->label,label); new->pos = pos; } return table; } reloc_table* add_dis_label_to_table(reloc_table* table, char* label, unsigned long pos) { /* * New element added to the end of the list. * Allow duplicates */ reloc_table *current,*new; current = table; /* set current to head of list */ /* Any elements added yet? */ if (current == NULL) { current = malloc(sizeof(reloc_table)); current->next = table; table = current; current->label = (char*)malloc(strlen(label)+1); strcpy(current->label,label); current->pos = pos; } else { /* Already elements in list */ while (current->next != NULL) current = current->next; /* Now at end of linked list */ new = malloc(sizeof(reloc_table)); current->next = new; new->next = NULL; new->label = (char*)malloc(strlen(label)+1); strcpy(new->label,label); new->pos = pos; } return table; } variables_table* add_var_to_table(variables_table *table, int addr, unsigned long pos) { /* * New element added to the end of the list. * Allow duplicates */ variables_table *current,*new; current = table; /* set current to head of list */ /* Any elements added yet? */ if (current == NULL) { current = malloc(sizeof(variables_table)); current->next = table; table = current; current->addr = addr; current->pos = pos; } else { /* Already elements in list */ while (current->next != NULL) current = current->next; /* Now at end of linked list */ new = malloc(sizeof(variables_table)); current->next = new; new->next = NULL; new->addr = addr; new->pos = pos; } return table; } void startcode() { codep=0; } void putbyte(int c) { /* check for buffer overflow */ codebuffer[codep++]=c; } /* * For the next few, consider endian-ness of the target machine. */ void put2bytes(int c) { putbyte((c>>8) & 0xff); putbyte(c & 0xff); } void put3bytes(int c) { putbyte(c>>16 & 0xff); putbyte((c>>8) & 0xff); putbyte(c & 0xff); } void put4bytes(int c) { putbyte((c>>24) & 0xff); putbyte((c>>16) & 0xff); putbyte((c>>8) & 0xff); putbyte(c & 0xff); } /* * Do I want to rely on a C compiler having "long long" as a 64-bit type? */ void put5bytes(long long c) { putbyte((c>>32) & 0xff); putbyte((c>>24) & 0xff); putbyte((c>>16) & 0xff); putbyte((c>>8) & 0xff); putbyte(c & 0xff); } void put_little_endian(Lisp_Object c) { putbyte(c & 0xff); putbyte((c>>8) & 0xff); putbyte((c>>16) & 0xff); putbyte((c>>24) & 0xff); } void put_addr(void *c) { /* * Store the variable's address in the reloc table. * Address will be put into the code at linkage. * Doing it at the end allows for peephole optimisations. * If it was added now that would mess that up. * NB the case "void *" to "int" here that is only going to * be valid on 32-bit machines. */ vars_table = add_var_to_table(vars_table, (int)c, codep); put4bytes(0x00000000); } int isreg(int reg) { /* return true if it is a register */ if(0x0 <= reg && reg <= 0x7) return 1; else return 0; /* else it is a memory location */ } void call_rel_fn(char *label) { /* all calls are a relative 32bit signed offset */ putbyte(0xe8); /* call */ callers_table = add_caller_to_table(callers_table, label, codep); put4bytes(0xfcffffff); /* address */ } void jump(char *label) { /* jump must be rel32 for now */ putbyte(0xe9); jumpers_table = add_caller_to_table(jumpers_table, label, codep); put4bytes(0xffffffff); } void cond_jump(char *label, int cond) { /* jump must be rel32 for now */ put2bytes(cond); jumpers_table = add_caller_to_table(jumpers_table, label, codep); put4bytes(0xffffffff); } void add_label(char *label) { labels_table = add_label_to_table(labels_table, label, codep); } void add_disassembler_label(char *label) { disassembler_labels = add_dis_label_to_table(disassembler_labels, label, codep); /* FIXME rename these fns to more generic like dupe, nodupe */ } void Jcall_abs_fn(void *addr_big_endian) { /* overwrites %eax register */ /* Jpush(1,EDI); preserve A_reg */ /* push_A_reg(); */ putbyte(0xb8); put_addr(addr_big_endian); put2bytes(0xffd0); /* pop_A_reg(); */ /* Jpop(1,EDI); */ } void free_labels_table(reloc_table *head) { reloc_table *next; while (head != NULL) { free(head->label); next = head->next; free(head); head = next; } } void free_var_table(variables_table *head) { variables_table *next; while (head != NULL) { next = head->next; free(head); head = next; } } void reloc_calls() { reloc_table *callerlist = callers_table; reloc_table *labellist = labels_table; int found = 0; unsigned long caller = 0; unsigned long offset = 0; unsigned long saved_codep = codep; while (callerlist != NULL) { labellist = labels_table; found = 0; while (labellist != NULL) { if (strcmp(callerlist->label,labellist->label)==0) { /* calculate relative call offset and update code */ caller = callerlist->pos; /* -4 because offset starts at the next instruction following the call instruction */ offset = labellist->pos - callerlist->pos - 4; codep = caller; /* write offset to codebuffer[codep] */ put_little_endian(offset); /* break from while loop */ found = 1; labellist = NULL; } else labellist = labellist->next; } if (!found) printf("\nBUG IN RELOC TABLE: %s %lu\n",callerlist->label, callerlist->pos); callerlist = callerlist->next; } codep = saved_codep; } void reloc_jumps() { reloc_table *jumperlist = jumpers_table; reloc_table *labellist = labels_table; int found = 0; unsigned long jumper = 0; unsigned long offset = 0; unsigned long saved_codep = codep; while (jumperlist != NULL) { labellist = labels_table; found = 0; while (labellist != NULL) { if (strcmp(jumperlist->label,labellist->label)==0) { /* -4 because offset is from next instruction */ /* calculate relative jump offset and update code */ jumper = jumperlist->pos; offset = labellist->pos - jumperlist->pos - 4; codep = jumper; /* write offset to codebuffer[codep] */ /* put4bytes(offset); */ put_little_endian(offset); /* break from while loop */ found = 1; labellist = NULL; } else labellist = labellist->next; } if (!found) printf("\nBUG IN RELOC TABLE: %s %lu\n",jumperlist->label,jumperlist->pos); jumperlist = jumperlist->next; } codep = saved_codep; } void reloc_vars() { variables_table *varlist = vars_table; unsigned long saved_codep = codep; while(varlist != NULL) { codep = varlist->pos; put_little_endian(varlist->addr); varlist = varlist->next; } codep = saved_codep; } /* end of jit2.c */