#include #include #include #include #include #include #include "scheme.h" typedef struct root_t{ int count; char** start; struct root_t* next; } root_t; void S_add_roots(pcb_t* pcb, int* f){ int n = *f; if(n == 0) return; root_t* t = malloc(sizeof(root_t)); if(t == NULL){ fprintf(stderr, "Error mallocing\n"); exit(-1); } t->count = n; t->start = (char**)(f+1); t->next = (root_t*) pcb->roots; pcb->roots = (char*) t; int i; for(i=1; i<=n; i++){ assert(f[i] == 0); } } void S_check_roots(pcb_t* pcb, int* f){ int n = *f; int i; for(i=1; i<=n; i++){ assert(f[i] != 0); } } /* S_collect is called from scheme under the following conditions: * 1. An attempt is made to allocate a small object and the ap is above * the red line. * 2. The current frame of the call is dead, so, upon return from S_collect, * the caller returns to its caller. * 3. The frame-pointer of the caller to S_collect is saved at * pcb->stack_extent. No variables are live at that frame except for * the return point (at *(pcb->stack_extent)). * 4. S_collect must return a new ap (in pcb->allocation_pointer) that has * at least 2 pages of memory free. * 5. S_collect must also update pcb->allocaton_red_line to be 2 pages below * the real end of heap. * 6. S_collect should not move the stack. */ #define pagesize 4096 #define pageshift 12 #define minimum_heap_size (pagesize * 1024 * 4) #define maximum_heap_size (pagesize * 1024 * 8) #define minimum_stack_size (pagesize * 128) #define align_to_page(x) (((x)/pagesize)*pagesize) #define align_to_next_page(x) \ (((pagesize - 1 + (unsigned int)(x)) >> pageshift) << pageshift) #define align_to_prev_page(x) \ ((((unsigned int)(x)) >> pageshift) << pageshift) static char* allocate_unprotected_space(int size); static void deallocate_unprotected_space(char* p, int size); static void deallocate_string_pages(char*); static void copy_roots(pcb_t* pcb); static char* move_object(char* x, pcb_t* pcb); pcb_t* S_collect(int req, pcb_t* pcb){ #if 0 fprintf(stderr, "S_collect entry %d (pcb=0x%08x)\n", req, (int)pcb); #endif char* heap_base = pcb->heap_base; #if 0 int heap_size = (int)pcb->heap_size; fprintf(stderr, "heapsize=0x%08x (0x%08x .. 0x%08x)\n", heap_size, (int) heap_base, (int) (heap_base + heap_size - 1)); #endif int used_space = (int)(pcb->allocation_pointer - heap_base); { int bytes = (int) pcb->allocated_bytes + (used_space & 0xFFFFF); pcb->allocated_megs += (bytes >> 20); pcb->allocated_bytes = (char*) (bytes & 0xFFFFF); #if 0 fprintf(stderr, "allocated %d megs and %d bytes so far\n", (int) pcb->allocated_megs, (int) pcb->allocated_bytes); #endif } int required_space = align_to_page(used_space + 2 * req + 2 * pagesize); if(required_space < minimum_heap_size){ required_space = minimum_heap_size; } if(required_space > maximum_heap_size){ fprintf(stderr, "Maximum heapsize exceeded\n"); exit(-1); } char* old_heap = pcb->heap_base; int old_size = (int)pcb->heap_size; char* old_string_pages = pcb->string_pages; pcb->string_pages = 0; char* new_heap = allocate_unprotected_space(maximum_heap_size); pcb->allocation_pointer = new_heap; pcb->allocation_redline = new_heap + maximum_heap_size - 2 * pagesize; pcb->heap_base = new_heap; pcb->heap_size = (char*) maximum_heap_size; copy_roots(pcb); char** p = (char**) new_heap; while(p != (char**) pcb->allocation_pointer){ *p = move_object(*p, pcb); p++; } deallocate_unprotected_space(old_heap, old_size); deallocate_string_pages(old_string_pages); { int free_space = (int)pcb->allocation_redline - (int)pcb->allocation_pointer; int diff = align_to_page(free_space - minimum_heap_size); if(diff > 0){ deallocate_unprotected_space( pcb->heap_base + (int)pcb->heap_size - diff, diff); pcb->allocation_redline -= diff; pcb->heap_size -= diff; } } #if 0 fprintf(stderr, "ap=0x%08x limit=0x%08x\n", (int)pcb->allocation_pointer, (int)pcb->heap_base+(int)pcb->heap_size-wordsize); #endif return pcb; } #define fixnump(x) ((((int)(x)) & fx_mask) == fx_tag) #define closurep(x) ((((int)(x)) & closure_mask) == closure_tag) #define immediatep(x) ((((int)(x)) & 7) == 7) #define tagof(x) (((int) (x)) & 7) #define ref(x,t) (*((char**)(((char*)(x))+((int)(t))))) #define align(x) ((((x)+object_alignment-1)>>align_shift)<base, p->end - p->base); p=p->next; } p = (page_t*) old_string_pages; while(p){ page_t* n = p->next; free(p); p = n; } } #if 0 static char* extend_pointer_ap(pcb_t* pcb, int size){ if(pcb->pointer_base){ page_t* p = make_page_t(); p->base = pcb->pointer_base; p->end = pcb->pointer_ap; p->next = (page_t*) pcb->pointer_pages; pcb->pointer_pages = (char*) p; } char* ap = allocate_unprotected_space(size); pcb->pointer_base = ap; pcb->pointer_ap = ap; pcb->pointer_eap = ap + size; return ap; } #endif static char* alloc_large_string(pcb_t* pcb, int size){ char* ap = allocate_unprotected_space(size); page_t* p = make_page_t(); p->base = ap; p->end = ap+size; p->next = (page_t*) pcb->string_pages; pcb->string_pages = (char*) p; return ap; } static char* extend_string_ap(pcb_t* pcb, int size){ if(pcb->string_base){ page_t* p = make_page_t(); p->base = pcb->string_base; p->end = pcb->string_ap; p->next = (page_t*) pcb->string_pages; pcb->string_pages = (char*) p; } char* ap = allocate_unprotected_space(size); pcb->string_base = ap; pcb->string_ap = ap; pcb->string_eap = ap + size; return ap; } static char* move_string(char* s, pcb_t* pcb){ int len = (int) ref(s, -string_tag); int sz = align((len>>fx_shift)+disp_string_data+1); if(sz < pagesize){ char* ap = pcb->string_ap; char* nap = ap + sz; if(nap > pcb->string_eap){ ap = extend_string_ap(pcb, pagesize); pcb->string_eap = ap + pagesize; nap = ap + sz; } pcb->string_ap = nap; memcpy(ap, s-string_tag, sz); ref(s,-string_tag) = (char*)-1; ref(s,wordsize-string_tag) = ap+string_tag; return ap + string_tag; } else { char* ap = alloc_large_string(pcb, sz); memcpy(ap, s-string_tag, sz); ref(s,-string_tag) = (char*)-1; ref(s,wordsize-string_tag) = ap+string_tag; return ap + string_tag; } } static void munch_stack(char* fp, pcb_t* pcb, char* frame_base){ while(fp != frame_base){ assert(fp < frame_base); #if 0 fprintf(stderr, "copying frame at 0x%08x of 0x%08x\n", (int)fp, (int)stack_base); #endif char* rp = ref(fp, 0); #if 0 fprintf(stderr, "return-point = 0x%08x\n", (int)rp); #endif int framesize = (int) ref(rp, disp_frame_size); /* UGLY */ assert(fixnump(framesize)); assert(framesize >= 0); if(framesize > 0){ int bytes_in_mask = ((framesize>>fx_shift)+7)>>3; char* mask = rp + disp_frame_size - bytes_in_mask; fp = fp + framesize; char** fpp = (char**) fp; int i; for(i=0; i 0); #if 0 /* move cp */ { char* cp = ref(fp, 2*wordsize); assert(closurep(cp)); ref(fp, 2*wordsize) = move_object(cp, pcb); } #endif fp += framesize; int i; for(i=wordsize; i<(framesize); i+=wordsize){ ref(fp, -i) = move_object(ref(fp,-i), pcb); } } else { fprintf(stderr, "Error: framesize is %d\n", framesize); exit(-10); } } } static char* move_stack(char* s, pcb_t* pcb, int sz){ char* ns; int asz = align(sz); if(asz < pagesize){ char* ap = pcb->string_ap; char* nap = ap + asz; if(nap > pcb->string_eap){ ap = extend_string_ap(pcb, pagesize); pcb->string_eap = ap + pagesize; nap = ap + asz; } pcb->string_ap = nap; ns = ap; } else { ns = alloc_large_string(pcb, asz); } memcpy(ns, s, sz); munch_stack(ns, pcb, ns+sz); return ns; } static char* move_pointers(char* x, pcb_t* pcb, int size, int tag){ int sz = align(size); char* ap = pcb->allocation_pointer; char* nap = ap + sz; pcb->allocation_pointer = nap; ref(nap, -wordsize) = 0; memcpy(ap, x, size); ref(x,0) = (char*)-1; ref(x,wordsize) = ap + tag; return ap + tag; } static char* move_continuation(char* x, pcb_t* pcb){ int sz = (int) ref(x, disp_continuation_size); char* top = ref(x, disp_continuation_top); char* r = move_pointers(x, pcb, continuation_size, vector_tag); ref(r, disp_continuation_top - vector_tag) = move_stack(top, pcb, sz); return r; } static char* move_code(char* x, pcb_t* pcb){ int instrsize = (int) ref(x, disp_code_instrsize); if(instrsize == 0){ return (x + vector_tag); } int relocsize = (int) ref(x, disp_code_relocsize); int reqspace = instrsize + relocsize + disp_code_data; char* nx = allocate_unprotected_space(reqspace); { page_t* p = malloc(sizeof(page_t)); if(p == NULL){ fprintf(stderr, "failed to alloc a page_t\n"); exit(-1); } p->next = (page_t*) pcb->string_pages; pcb->string_pages = (char*) p; p->base = nx; p->end = nx + reqspace; } memcpy(nx, x, reqspace); ref(x, 0) = (char*)-1; ref(x, wordsize) = nx + vector_tag; { char* p = nx + disp_code_data + instrsize; char* pe = p + relocsize; while(p < pe){ int r = (int) ref(p,0); if(r == 0){ p = pe; } else { int rtag = r & 3; if(rtag == 0){ /* undisplaced pointer */ int code_offset = r >> 2; char* old_object = ref(nx, disp_code_data + code_offset); char* new_object = move_object(old_object, pcb); ref(nx, disp_code_data + code_offset) = new_object; p += wordsize; } else if(rtag == 1){ /* displaced pointer */ int code_offset = r >> 2; int object_offset = (int) ref(p, wordsize); char* old_displaced_object = ref(nx, disp_code_data + code_offset); char* old_object = old_displaced_object - object_offset; char* new_object = move_object(old_object, pcb); char* new_displaced_object = new_object + object_offset; ref(nx, disp_code_data + code_offset) = new_displaced_object; p += (2 * wordsize); } else if(rtag == 2){ /* displaced relative pointer */ int code_offset = r >> 2; int relative_offset = (int) ref(p, wordsize); char* old_relative_pointer = ref(nx, disp_code_data + code_offset); char* old_relative_object = old_relative_pointer - relative_offset; char* old_addr = x + disp_code_data + code_offset + wordsize; char* old_object = old_relative_object + (unsigned int) old_addr; char* new_object = move_object(old_object, pcb); char* new_disp_object = new_object + relative_offset; char* next_word = nx + disp_code_data + code_offset + wordsize; char* new_relative_pointer = new_disp_object - (unsigned int) next_word; ref(next_word, -wordsize) = new_relative_pointer; p += (2 * wordsize); } else { fprintf(stderr, "invalid rtag %d in 0x%08x\n", rtag, r); exit(-1); } } } } int err = mprotect(nx, align_to_next_page(reqspace), PROT_READ | PROT_WRITE | PROT_EXEC); if(err == -1){ perror("Cannot set code executable"); exit(-1); } return nx + vector_tag; } static char* move_object(char* x, pcb_t* pcb){ if(fixnump(x)){ return x; } else if(immediatep(x)){ return x; } else { int tag = tagof(x); char* fst = ref(x, -tag); if(fst == (char*)-1){ return ref(x,wordsize-tag); } else if(tag == pair_tag){ return(move_pointers(x-tag, pcb, pair_size, tag)); } else if(tag == closure_tag){ //assert(ref(fst, -2*wordsize) == 0); int size = (int) ref(fst, -wordsize); assert(fixnump(size)); assert(size > 0); char* new_closure = move_pointers(x-tag, pcb, size, tag); char* code_entry = ref(new_closure, -closure_tag); char* code_object = code_entry - disp_code_data + vector_tag; char* new_code_object = move_object(code_object, pcb); char* new_code_entry = new_code_object + disp_code_data - vector_tag; ref(new_closure, -closure_tag) = new_code_entry; return new_closure; } else if(tag == symbol_tag){ return (move_pointers(x-tag, pcb, symbol_size, tag)); } else if(tag == vector_tag){ if(fixnump(fst)){ return (move_pointers(x-tag, pcb, disp_vector_data + (int)fst, tag)); } else if(fst == (char*) continuation_tag){ return (move_continuation(x-tag, pcb)); } else if(fst == (char*) code_tag){ return (move_code(x-tag, pcb)); } else if(((int)fst & record_pmask) == record_ptag){ int len; { char* rtd = fst; char* rtd_fst = ref(rtd, -record_ptag); if(rtd_fst == (char*) -1){ rtd = ref(rtd, wordsize-record_ptag); } len = (int) ref(rtd, disp_record_data - record_ptag); } return (move_pointers(x-tag, pcb, disp_record_data + len, tag)); } else { fprintf(stderr, "nonvec 0x%08x 0x%08x\n", (int)x, (int)fst); exit(-1); } } else if(tag == string_tag){ return (move_string(x, pcb)); } else { fprintf(stderr, "here tag=%d\n", tag); exit(-1); } } } static void copy_roots(pcb_t* pcb){ /* first, the constants */ root_t* r = (root_t*)pcb->roots; while(r){ int n = r->count; char** f = r->start; int i; for(i=0; inext; } /* next, the pcb-primitives */ char** fst = &pcb->scheme_objects; char** end = &pcb->scheme_objects_end; fst++; while(fst < end){ *fst = move_object(*fst, pcb); fst++; } /* next, the stack */ char* fp = pcb->frame_pointer; char* frame_base = pcb->frame_base; munch_stack(fp, pcb, frame_base); } static char* allocate_unprotected_space(int size){ int aligned_size = ((size + pagesize - 1) / pagesize) * pagesize; char* p = mmap(0, aligned_size, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE, 0, 0); if(p == MAP_FAILED){ perror("allocate_unprotected_space failed to mmap"); exit(-10); } return p; } static void deallocate_unprotected_space(char* p, int size){ int status; int aligned_size = ((size + pagesize - 1) / pagesize) * pagesize; status = munmap(p, aligned_size); if(status != 0){ perror("deallocate_unprotected_space failed to unmap"); exit(-10); } } void S_stack_overflow(pcb_t* pcb){ // fprintf(stderr, "stack overflow detected\n"); char* stack_top = pcb->stack_top; int stack_size = (int) pcb->stack_size; char* fp = pcb->frame_pointer; char* frame_base = pcb->frame_base; assert(fp != frame_base); char* rp = ref(fp, 0); int framesize = (int) ref(rp, disp_frame_size); /* UGLY */ assert(fixnump(framesize)); assert(framesize >= 0); if(framesize == 0){ framesize = (int)ref(fp, wordsize); assert(fixnump(framesize)); } // fprintf(stderr, "framesize = %d bytes\n", framesize); { /* capture continuation */ char* next_frame_top = fp + framesize; if(next_frame_top == frame_base){ fprintf(stderr, "continuation already captured\n"); } else { //fprintf(stderr, "capturing continuation ... "); char* cont = pcb->allocation_pointer; pcb->allocation_pointer += continuation_size; ref(cont, 0) = (char*) continuation_tag; ref(cont, disp_continuation_top) = next_frame_top; ref(cont, disp_continuation_next) = pcb->next_continuation; ref(cont, disp_continuation_size) = frame_base - (int)next_frame_top; pcb->next_continuation = cont + vector_tag; //fprintf(stderr, "done (sz=0x%08x)\n", // (int) ref(cont, disp_continuation_size)); } } int req_stack_size = align_to_page(framesize * 4 + 2 * pagesize); if(req_stack_size < minimum_stack_size){ req_stack_size = minimum_stack_size; } char* new_stack = allocate_unprotected_space(req_stack_size); char* new_frame_redline = new_stack + 2 * pagesize; char* new_frame_base = new_stack + req_stack_size - wordsize; ref(new_frame_base, 0) = ref(frame_base, 0); /* underflow handler */ memcpy(new_frame_base - framesize, fp, framesize); pcb->stack_top = new_stack; pcb->stack_size = (char*)req_stack_size; pcb->frame_base = new_frame_base; pcb->frame_pointer = new_frame_base - framesize; pcb->frame_redline = new_frame_redline; /* fprintf(stderr, "stack=0x%08x .. 0x%08x (redline=0x%08x) fp=0x%08x\n", (int) pcb->frame_base, (int) pcb->stack_top, (int) pcb->frame_redline, (int) pcb->frame_pointer); fprintf(stderr, "returning ... \n"); */ page_t* p = malloc(sizeof(page_t)); if(p == NULL){ fprintf(stderr, "cannot malloc page_t\n"); exit(-1); } p->base = stack_top; p->end = stack_top + stack_size; p->next = (page_t*) pcb->string_pages; pcb->string_pages = (char*) p; //fprintf(stderr, "done\n"); return; } /* On overflow: +--------------+ | unused | | area | | | +--------------+ | rp | <-- frame pointer on overflow +--------------+ | frame | | when | | overflow | | occured | +--------------+ | rp_next | <-- capture next conitnuation here +--------------+ (unless we're at base already) | ... | | ... | | ... | +--------------+ | underflow | +--------------+ New stack: +--------------+ | unused | | area | | | | | | | | | | | | | | | | | +--------------+ | rp | <-- frame pointer on return +--------------+ | frame | | when | | overflow | | occured | +--------------+ | underflow | +--------------+ */ char* S_make_code(int fxcsize, int fxrsize, int fxclsize, pcb_t* pcb){ int csize = fxcsize >> fx_shift; csize = (((csize + (1 << fx_shift) - 1) >> fx_shift) << fx_shift); int reqspace = csize + fxrsize + disp_code_data; char* code = allocate_unprotected_space(reqspace); { page_t* p = malloc(sizeof(page_t)); if(p == NULL){ fprintf(stderr, "failed to allocate a page\n"); exit(-1); } p->base = code; p->end = code + reqspace; p->next = (page_t*) pcb->string_pages; pcb->string_pages = (char*) p; } memset(code, 0, reqspace); ref(code, 0) = (char*)code_tag; ref(code, disp_code_instrsize) = (char*) csize; ref(code, disp_code_relocsize) = (char*) fxrsize; ref(code, disp_code_closuresize) = (char*) fxclsize; return(code + vector_tag); } char* S_make_code_executable(char* x, pcb_t* pcb){ int instrsize = (int) ref(x, disp_code_instrsize - vector_tag); char* code_start = x + disp_code_data - vector_tag; char* code_end = code_start + instrsize; char* page_start = (char*) align_to_prev_page(code_start); char* page_end = (char*) align_to_next_page(code_end); int err = mprotect(page_start, (int) (page_end - page_start), PROT_READ | PROT_WRITE | PROT_EXEC); if(err == -1){ perror("Cannot set code executable"); exit(-1); } return bool_t; } #if 0 SUPER FAST HASH Taken from http://www.azillionmonkeys.com/qed/hash.html #endif #undef get16bits #if (defined(__GNUC__) && defined(__i386__)) || defined(__WATCOMC__) \ || defined(_MSC_VER) || defined (__BORLANDC__) || defined (__TURBOC__) #define get16bits(d) (*((const uint16_t *) (d))) #endif #if !defined (get16bits) #define get16bits(d) ((((const uint8_t *)(d))[1] << UINT32_C(8))\ +((const uint8_t *)(d))[0]) #endif char* SuperFastHash (char* str) { char* data = str + disp_string_data - string_tag; int len = (int) ref(str, disp_string_length - string_tag); len = len >> fx_shift; uint32_t hash = len, tmp; int rem; if (len <= 0 || data == NULL) return 0; rem = len & 3; len >>= 2; /* Main loop */ for (;len > 0; len--) { hash += get16bits (data); tmp = (get16bits (data+2) << 11) ^ hash; hash = (hash << 16) ^ tmp; data += 2*sizeof (uint16_t); hash += hash >> 11; } /* Handle end cases */ switch (rem) { case 3: hash += get16bits (data); hash ^= hash << 16; hash ^= data[sizeof (uint16_t)] << 18; hash += hash >> 11; break; case 2: hash += get16bits (data); hash ^= hash << 11; hash += hash >> 17; break; case 1: hash += *data; hash ^= hash << 10; hash += hash >> 1; } /* Force "avalanching" of final 127 bits */ hash ^= hash << 3; hash += hash >> 5; hash ^= hash << 4; hash += hash >> 17; hash ^= hash << 25; hash += hash >> 6; return (char*)(hash<