/* * build.c - Fragment builder. * * Copyright (c) 2000-2003 - J. Kevin Scott and Jack W. Davidson * * This file is part of the Strata dynamic code modification infrastructure. * This file may be copied, modified, and redistributed for non-commercial * use as long as all copyright, permission, and nonwarranty notices are * preserved, and that the distributor grants the recipient permission for * further redistribution as permitted by this notice. * * Please contact the authors for restrictions applying to commercial use. * * THIS SOURCE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF * MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. * * Author: J. Kevin Scott * e-mail: jks6b@cs.virginia.edu * URL : http://www.cs.virginia.edu/~jks6b * */ #ident "$Id: build.c 1063 2006-03-14 16:52:47Z williadw $" #include "all.h" /* Private data structures and function declarations. */ /* these macros are to the head and tail of the fragment queue * replacing the head and tail globals with thread specific * pointers */ #define HEAD ((hashtable_get((*TI.thread_id)()))->fq_head) #define TAIL ((hashtable_get((*TI.thread_id)()))->fq_tail) #define PATCHTABSIZE 1087 static strata_patch *patch_tab[PATCHTABSIZE]; static jmp_buf strata_build_env; int builder_opt_no_frag_linking; int strata_entrance_count; int strata_stop_after; static char *frag_ty_map[] = { "CBRANCH", "IBRANCH", "CALL", "RET", "SWITCH", "SPECIAL", "INVALID", "ICALL" }; strata_fragment* strata_install_fragment_group(); strata_fragment* strata_build_single_fragment(app_iaddr_t next_PC); void strata_add_pc_mapping(app_iaddr_t PC, fcache_iaddr_t fPC); void strata_create_fragment_trampolines(strata_fragment *frag); void strata_remove_create_tramp_entry(strata_patch* cur); void targ_create_trampoline(strata_fragment *frag, strata_patch *tramplist); /* * strata_enter_builder - Perform operations necessary when entering the builder, * including locking mutexes, and logging. */ void strata_enter_builder(app_iaddr_t to_PC, strata_fragment *from_frag) { /* Thread locking */ STRATA_LOG("lock", "builder lock.\n"); (*TI.mutex_lock)(); } /* * strata_leave_builder - Perform any operation that's necessary each time we * exit the builder and return execution to the fragment cache, including * dumping tracing info, updating the control flow graph, and unlocking mutexs. */ void strata_leave_builder(strata_fragment* frag, strata_fragment* from_frag) { /* emit information about ibtc traces, if requested */ if(from_frag && form_ibtc_traces_fd) { fwrite((const void *)(&(from_frag->ty)), sizeof(int), 1, form_ibtc_traces_fd); fwrite((const void *)(&(from_frag->indPC)), sizeof(unsigned), 1, form_ibtc_traces_fd); fwrite((const void *)(&(from_frag->indfPC)), sizeof(unsigned), 1, form_ibtc_traces_fd); fwrite((const void *)(&(frag->PC)), sizeof(unsigned), 1, form_ibtc_traces_fd); } /* handle control flow edges */ strata_handle_control_flow_graph(from_frag, frag); STRATA_TRACE("Leaving builder to execute 0x%08x:0x%08x\n", frag->fPC, frag->PC); (*TI.mutex_unlock)(); } /* * strata_build_main - * Build a fragment and returns the starting fragment address. * * Inputs: * to_PC - Beginning of new fragment. * from_frag - Pointer to the fragment calling strata_build_main * * Output: * The (fragment) address to start executing. */ fcache_iaddr_t strata_build_main (app_iaddr_t to_PC, strata_fragment *from_frag) { strata_fragment *frag; insn_t insn; fcache_iaddr_t to_fPC, previous_last_fPC; app_iaddr_t next_PC, previous_next_PC; int insn_class, first_frag; int sieve_needs_fill=0; strata_enter_builder(to_PC, from_frag); /* Mark our place in case we need to restart the builder. If the fragment cache fills, we need to create a new fragment and then, restart the builder. (consider moving past the point where we know we're building a fragment) */ if(!targ_opt_fast_return) setjmp(strata_build_env); /* this is for flushing the f$, we're only doing that w/o fast returns */ #ifdef STATS /* "strata iso" functionality*/ strata_entrance_count++; if ( strata_entrance_count == strata_stop_after ) { return to_PC; } stat_builder_enters++; #endif STRATA_TRACE("Builder entered from fragment %08x:%08x [%s] to execute %08x (%d entrances)\n", from_frag, from_frag?from_frag->PC:0, from_frag?frag_ty_map[from_frag->ty]:"NONE", to_PC, strata_entrance_count ); /* !!! TEMP SOLUTNION check for fast return setjmp error */ if ( strata_fragment_address(to_PC) ) { STRATA_LOG("fast_returns", "returning to to_PC!"); (*TI.mutex_unlock)(); return to_PC; } /* lookup the fragment in the F$ */ frag = strata_lookup_built_fragment(to_PC) ; /* if it's not there, build it */ if (!frag) { /* We're adding at least one new fragment to the fragment cache. */ /* (consider moving setjump here) */ STRATA_TRACE("Target not in F$!\n"); /* Add it to the work list, and build the group. */ strata_enqueue_address(to_PC); frag = strata_install_fragment_group(); } /* Execute the fragment. */ STRATA_TRACE("Executing %08x mapped to %08x type=%s\n", to_PC, frag->fPC, frag_ty_map[frag->ty] ); strata_sieve_update(frag); /* leave builder and return the fragment address */ strata_leave_builder(frag, from_frag); return frag->fPC; } /* * strata_handle_control_flow_graph - update the (non-working) CFG for strata */ void strata_handle_control_flow_graph(strata_fragment *from_frag, strata_fragment *frag) { app_iaddr_t to_PC; assert(frag); to_PC=frag->PC; /* Target is already in the fragment cache. */ STRATA_TRACE("Target in F$!\n"); /* Count the execution of the fragment we're going to. */ /* consider making into macro */ strata_profiler_count_insn(frag->prof,1); if (from_frag != NULL) { /* Update control flow information. */ if (!(from_frag->ty == STRATA_FRAG_CBRANCH || from_frag->ty == STRATA_FRAG_CALL)) { strata_frag_control_edge(from_frag,frag); } switch(from_frag->ty) { case STRATA_FRAG_RET: #ifdef STATS stat_num_rets++; #endif strata_indirect_branch_miss(from_frag,frag); break; case STRATA_FRAG_ICALL: case STRATA_FRAG_IBRANCH: #ifdef STATS stat_num_ibranches++; #endif strata_indirect_branch_miss(from_frag,frag); break; case STRATA_FRAG_CBRANCH: #ifdef STATS stat_num_branches++; #endif break; case STRATA_FRAG_SWITCH: break; case STRATA_FRAG_CALL: break; case STRATA_FRAG_SPECIAL: break; default: strata_fatal("Unknown branch type for fragment"); } } else { /* All fragments must set from_frag! */ /*strata_fatal("Re-entering fragment did not set from_frag");*/ /* I'm not sure above statement is true, warn about it for now */ /* thread -- reason for not using strata fatal */ STRATA_LOG("warn", "From Frag not set!"); } } /* * strata_install_fragment_group - * This function builds and installs an group of * dependent fragments into the F$. These should be * fragments that aren't ready to be used until all of them are * built. (ie, a fragment with a call + its return fragment if * fast returns are on.) */ strata_fragment* strata_install_fragment_group() { strata_fragment* frag, *first_frag; app_iaddr_t next_PC; fragment_queue* cur; /* initializations */ first_frag = NULL; cur = HEAD; /* While we have fragments on the work list */ while ( cur != NULL ) { next_PC = cur->PC; frag = strata_build_single_fragment(next_PC); if ( !(frag->flags & STRATA_FRAG_DONT_LINK) ) { /* Do chaining. */ strata_apply_patches(frag); } cur = cur->next; } /* don't set the flags to "ready" unil all of the fragments are built */ while( next_PC = strata_dequeue_address() ) { frag = strata_lookup_fragment(next_PC); assert(frag!=NULL); if ( first_frag == NULL ) { first_frag = frag; } frag->flags |= STRATA_FRAG_READY; } return first_frag; } /* * strata_build_single_fragment - * builds a single fragment from the give application address, and * returns a pointer to the strata_fragment struct. Note, this function * does not set the STRATA_FRAG_READY flag, which is handled in the common * case by strata_install_fragment_group */ strata_fragment* strata_build_single_fragment(app_iaddr_t next_PC) { strata_fragment *frag=NULL; insn_t insn; int insn_class=0; int instrs_in_this_frag = 0; fcache_iaddr_t to_fPC=0, previous_last_fPC=0; app_iaddr_t previous_next_PC=0; /* initalize the new fragment */ STRATA_TRACE("Building new fragment for :>0x%08x\n",next_PC); frag = strata_create_fragment(next_PC); strata_begin_fragment(frag); /* set linking flag if necessary */ if ( builder_opt_no_frag_linking ) { frag->flags |= STRATA_FRAG_DONT_LINK; } STRATA_TRACE("New fragment at: 0x%08x\n", frag->fPC); /* Remember the frag. cache address of the first fragment. */ if (to_fPC == 0) to_fPC = frag->fPC; /* Fetch/decode/translate loop for fragment formation. */ do { /* reset vars keeping last PC */ previous_next_PC=next_PC; previous_last_fPC=frag->last_fPC; /* fetch and classify */ insn = (*TI.fetch)(next_PC); insn_class = (*TI.classify)(insn); if(next_PC==strata_stop_address) { targ_emit_shutdown_code(frag,previous_next_PC, insn); frag->ty=STRATA_FRAG_SPECIAL; next_PC=0; break; } /* consider making into a indirect function call off the enumeration */ switch(insn_class) { case STRATA_CALL: next_PC = (*TI.xlate_call)(frag,next_PC,insn); strata_add_pc_mapping(next_PC, frag->last_fPC); STRATA_TRACE("call translation returned: 0x%08x\n", next_PC); break; case STRATA_PC_RELATIVE_BRANCH: next_PC = (*TI.xlate_pcrel_branch)(frag,next_PC,insn); strata_add_pc_mapping(next_PC, frag->last_fPC); break; case STRATA_INDIRECT_BRANCH: next_PC = (*TI.xlate_ind_branch)(frag,next_PC,insn); break; case STRATA_RETURN: next_PC = (*TI.xlate_return)(frag,next_PC,insn); break; case STRATA_SPECIAL: next_PC = (*TI.xlate_special)(frag,next_PC,insn); strata_add_pc_mapping(next_PC, frag->last_fPC); break; case STRATA_NORMAL: next_PC = (*TI.xlate_normal)(frag,next_PC,insn); break; default: assert(0); } instrs_in_this_frag++; if(instrs_in_this_frag >= strata_max_insts_per_frag && next_PC) { (*TI.end_fragment_early)(frag,next_PC); break; } frag->indPC = previous_next_PC; frag->indfPC = previous_last_fPC; } while(next_PC); /* Create all the trampolines that need to be added */ strata_create_fragment_trampolines(frag); /* Mark end of fragment. */ strata_end_fragment(frag); /* assert that the fragment type was set to non-zero */ assert(frag->ty!=STRATA_FRAG_INVALID); return frag; } /* The next two functions manage the builder's work list of fragments. Items * added to the work list will get added to the fragment cache in FIFO order. * We use a linked list to represent the queue. The static globals head & * tail point to the head and tail of the queue respectively. We enqueue at * the tail and dequeue at the head. The queue is empty when head points to * NULL. */ /* * strata_enqueue_address - Place a fragment's starting address onto the builder's work list. */ void strata_enqueue_address (app_iaddr_t PC) { fragment_queue *p; STRATA_TRACE("Enqueue'ing 0x%08x to process later\n",PC); /* Allocate a new queue element and initialize its fields. */ NEW(p,BUILDER); p->PC = PC; p->next = NULL; /* Link p into the queue at the tail. */ if (TAIL != NULL) { TAIL->next = p; } TAIL = p; /* Check for previously empty queue. */ if (HEAD == NULL) HEAD = TAIL; } /* * strata_dequeue_address - Get the next fragment starting address off of the builder's work list. */ app_iaddr_t strata_dequeue_address (void) { fragment_queue *p; p = HEAD; if (p != NULL) { HEAD = HEAD->next; return p->PC; } else { TAIL = NULL; return 0; } } /* * strata_reset_builder - Reset the builder. */ void strata_reset_builder (void) { int i; for(i=0;i 0x%08x\n",frag->PC,frag->fPC); h = frag->PC % PATCHTABSIZE; cur = patch_tab[h]; while(cur != NULL) { if (!cur->patched && cur->PC == frag->PC) { strata_frag_control_edge(cur->frag,frag); strata_remove_create_tramp_entry(cur); (*TI.patch)(cur,frag->fPC); cur->patched = 1; } cur = cur->next; } } /* * This is a list of patches that needs to be applied for the current fragment. */ strata_patch* tramplist=NULL; /* * strata_create_trampoline_later - Record that the branch in patch->u.loc needs to temporarily go * to a trampoline. However, we can't emit that trampoline now * because we're not anywhere near done with emitting this fragment. * Consequently, we'll fix up this branch later (in end_fragment) * and emit a trampoline. */ void strata_create_trampoline_later(strata_patch *patch, app_iaddr_t to_PC) { app_iaddr_t h; assert(patch); /* tracing help */ if (patch->ty == PATCH_SWITCH) { assert(0); } else { STRATA_TRACE("Location %08x will have a tramp added after this fragment build\n", patch->u.loc,to_PC); } patch->PC=to_PC; /* Install the patch into add trampoline list */ patch->next=tramplist; tramplist=patch; } /* * Create all the trampolines that this fragments needs */ void strata_create_fragment_trampolines(strata_fragment *frag) { /* loop over the tramplist, creating trampolines as we go */ while(tramplist) { /* create this trampoline, we'll need the target's help */ targ_create_trampoline(frag,tramplist); /* next trampoline */ tramplist=tramplist->next; } return; } /* * strata_remove_create_tramp_entry - Remove an entry from the create * trampoline list. This is sometimes necessary when we patch before * we finish the fragment (sometimes this happens when we create PC mappings). */ void strata_remove_create_tramp_entry(strata_patch* cur) { strata_patch* ttramp=tramplist; strata_patch* prev=NULL; /* examine each entry on the tramp lines */ while(ttramp) { /* match: remove entry */ if(ttramp->PC==cur->PC) { /* note: freeing of list entries isn't necessary, as they're arena allocated. * We could put them into a list of free entries, if we had a mechanism for it */ if(!prev) { /* head of list, easy removal */ tramplist=tramplist->next; } else { /* make prev entry's next ptr point to cur entry next */ prev->next=ttramp->next; } } /* keep track of the previous one for removal purposes, and move to the next one */ prev=ttramp; ttramp=ttramp->next; } return; } /* * strata_patch_later - Add patch as a list of fragment addresses that need to be fixed * when targPC is translated. */ void strata_patch_later (strata_patch *patch, app_iaddr_t targPC) { app_iaddr_t h; assert(patch); if (patch->ty == PATCH_SWITCH) { STRATA_TRACE("Table %08x, element %08x will be patched when %08x arrives\n", patch->u.sw.table_base,patch->u.sw.table_entry,targPC); } else { STRATA_TRACE("Location %08x will be patched when %08x arrives\n", patch->u.loc,targPC); } /* Initialize the patch. */ patch->PC = targPC; patch->patched = 0; /* Install the patch into the patch table. */ h = patch->PC % PATCHTABSIZE; patch->next = patch_tab[h]; patch_tab[h] = patch; } /* * strata_create_patch_with_type - Create a simple patch with a f$ address and a type, which may be * a target defined type. */ strata_patch *strata_create_patch_with_type(strata_fragment *frag, fcache_iaddr_t loc, strata_patch_type_t ty) { strata_patch *patch; NEW(patch,FCACHE); patch->ty = ty; patch->frag = frag; patch->u.loc = loc; return patch; } /* Allocate a trampoline patch. */ strata_patch *strata_patch_tramp (strata_fragment *frag, fcache_iaddr_t loc) { return strata_create_patch_with_type(frag,loc,PATCH_TRAMP); } /* Allocate for a return address. */ strata_patch *strata_patch_alt_retaddr (strata_fragment *frag, fcache_iaddr_t loc) { return strata_create_patch_with_type(frag,loc,PATCH_ALT_RETADDR); } /* Allocate for a callsite address; used when partial inlining is off */ strata_patch *strata_patch_callsite(strata_fragment *frag, app_iaddr_t loc) { return strata_create_patch_with_type(frag,loc,PATCH_CALLSITE); } /* Allocate for a return address. */ strata_patch *strata_patch_retaddr (strata_fragment *frag, fcache_iaddr_t loc) { return strata_create_patch_with_type(frag,loc,PATCH_RETADDR); } /* Allocate a patch for a switch trampoline entry. */ strata_patch *strata_patch_switch (strata_fragment *frag, unsigned table_base, unsigned table_entry) { strata_patch *patch; NEW(patch,FCACHE); patch->ty = PATCH_SWITCH; patch->frag = frag; patch->u.sw.table_base = table_base; patch->u.sw.table_entry = table_entry; return patch; } /* * strata_add_pc_mapping - create a fake fragment which is a mapping of an frag_PC to an app_PC. */ void strata_add_pc_mapping(app_iaddr_t PC, fcache_iaddr_t fPC) { strata_fragment *frag=NULL; #define strata_opt_add_pc_mappings 1 if(!strata_opt_add_pc_mappings) return; if((void*)PC==(void*)NULL) { return; } frag = strata_create_fragment(PC); frag->fPC=fPC; frag->last_fPC=fPC; frag->ty=STRATA_FRAG_PC_MAPPING; strata_apply_patches(frag); }