Update of /cvsroot/psyco/psyco/c/i386 In directory sc8-pr-cvs1:/tmp/cvs-serv2254/c/i386 Added Files: idispatcher.c idispatcher.h iencoding.h iinitialize.h iprocessor.c ipyencoding.c ipyencoding.h itiming.h Log Message: processor dependency isolated in c/i386/ --- NEW FILE: idispatcher.c --- #include "idispatcher.h" #include "../dispatcher.h" #include "../codemanager.h" #include "ipyencoding.h" DEFINEFN void fpo_find_regs_array(vinfo_array_t* source, PsycoObject* po) { int i = source->count; while (i--) { vinfo_t* a = source->items[i]; if (a != NULL) { Source src = a->source; if (is_runtime(src) && !is_reg_none(src)) REG_NUMBER(po, getreg(src)) = a; else if (psyco_vsource_cc(src) != CC_ALWAYS_FALSE) po->ccreg = a; if (a->array != NullArray) fpo_find_regs_array(a->array, po); } } } /***************************************************************/ /*** the hard processor-dependent part of dispatching: ***/ /*** Unification. ***/ struct dmove_s { PsycoObject* po; int original_stack_depth; char* usages; /* buffer: array of vinfo_t*, see ORIGINAL_VINFO() below */ int usages_size; vinfo_t* copy_regs[REG_TOTAL]; code_t* code_origin; code_t* code_limit; code_t* code; /* only used by data_update_stack() */ CodeBufferObject* private_codebuf; }; static code_t* data_new_buffer(code_t* code, struct dmove_s* dm) { /* creates a new buffer containing a copy of the already-written code */ CodeBufferObject* codebuf; int codesize; if (dm->private_codebuf != NULL) { /* overflowing the regular (large) code buffer */ psyco_emergency_enlarge_buffer(&code, &dm->code_limit); return code; } else { /* overflowing the small buffer, start a new (regular) one */ codebuf = psyco_new_code_buffer(NULL, NULL, &dm->code_limit); codebuf->snapshot.fz_stuff.fz_stack_depth = dm->original_stack_depth; /* the new buffer should be at least as large as the old one */ codesize = code - dm->code_origin; if ((code_t*) codebuf->codestart + codesize > dm->code_limit) Py_FatalError("psyco: unexpected unify buffer overflow"); /* copy old code to new buffer */ memcpy(codebuf->codestart, dm->code_origin, codesize); dm->private_codebuf = codebuf; #if PSYCO_DEBUG dm->code_origin = (code_t*) 0xCDCDCDCD; #endif return ((code_t*) codebuf->codestart) + codesize; } } #define ORIGINAL_VINFO(spos) (*(vinfo_t**)(dm->usages + ( \ extra_assert(0 <= (spos) && (spos) < dm->usages_size), \ (spos)))) static void data_original_table(vinfo_t* a, RunTimeSource bsource, struct dmove_s* dm) { /* called on each run-time vinfo_t in the FrozenPsycoObject. Record in the array dm->usages which vinfo_t is found at what position in the stack. Ignore the ones after dm->usages_size: they correspond to stack positions which will soon be deleted (because the stack will shrink). Note: this uses the fact that RUNTIME_STACK_NONE is zero and uses the 0th item of dm->usages_size as general trash. */ if (RUNTIME_STACK(a) < dm->usages_size) ORIGINAL_VINFO(RUNTIME_STACK(a)) = a; } static void data_update_stack(vinfo_t* a, RunTimeSource bsource, struct dmove_s* dm) { PsycoObject* po = dm->po; code_t* code = dm->code; long dststack = getstack(bsource); long srcstack = getstack(a->source); char rg, rgb; vinfo_t* overridden; /* check for values passing from no-reference to reference */ if ((bsource & RunTime_NoRef) == 0) { /* destination has ref */ if ((a->source & RunTime_NoRef) == 0) /* source has ref too */ { /* remove the reference from 'a' because it now belongs to 'b' ('b->source' itself is in the frozen snapshot and must not be modified!) */ a->source = remove_rtref(a->source); } else { /* create a new reference for 'b'. Note that if the same 'a' is copied to several 'b's during data_update_stack() as is allowed by graph quotient detection in psyco_compatible(), then only the first copy will get the original reference owned by 'a' (if any) and for the following copies the following increfing code is executed as well. */ RTVINFO_IN_REG(a); rg = RUNTIME_REG(a); INC_OB_REFCNT_CC(rg); } } /* 'a' must no longer own a reference at this point. The case of 'b' wanting no reference but 'a' having one is forbidden by psyco_compatible() because decrefing 'a' would potentially leave a freed pointer in 'b'. */ extra_assert(!has_rtref(a->source)); /* The operation below is: copy the value currently held by 'a' into the stack position 'dststack'. */ rgb = getreg(bsource); if (rgb != REG_NONE) dm->copy_regs[(int)rgb] = a; if (dststack == RUNTIME_STACK_NONE || dststack == srcstack) ; /* nothing to do */ else { rg = RUNTIME_REG(a); if (rg == REG_NONE) /* load 'a' into a register before it can be */ { /* stored back in the stack */ NEED_FREE_REG(rg); LOAD_REG_FROM_EBP_BASE(rg, srcstack); REG_NUMBER(po, rg) = a; /*SET_RUNTIME_REG_TO(a, rg); ignored*/ } /* is there already a pending value at 'dststack'? */ overridden = ORIGINAL_VINFO(dststack); if (overridden == NULL || RUNTIME_STACK(overridden) != dststack) goto can_save_only; /* no -- just save the new value to 'dststack'. The case RUNTIME_STACK(overridden) != dststack corresponds to a vinfo_t which has been moved elsewhere in the mean time. */ /* yes -- careful! We might have to save the current value of 'dststack' before we can overwrite it. */ SET_RUNTIME_STACK_TO_NONE(overridden); if (!RUNTIME_REG_IS_NONE(overridden)) { /* no need to save the value, it is already in a register too */ can_save_only: /* copy 'a' to 'dststack' */ SAVE_REG_TO_EBP_BASE(rg, dststack); /*if (rgb == REG_NONE) { REG_NUMBER(po, rg) = NULL; rg = REG_NONE; }*/ } else { /* save 'a' to 'dststack' and load the previous value of 'dststack' back into the register 'rg' */ XCHG_REG_AND_EBP_BASE(rg, dststack); SET_RUNTIME_REG_TO(overridden, rg); REG_NUMBER(po, rg) = overridden; rg = REG_NONE; } /* Now 'a' is at 'dststack', but might still be in 'rg' too */ a->source = RunTime_NewStack(dststack, rg, false, false); ORIGINAL_VINFO(dststack) = a; /* 'a' is now there */ if (code > dm->code_limit) /* oops, buffer overflow. Start a new buffer */ code = data_new_buffer(code, dm); } dm->code = code; } static code_t* data_free_unused(code_t* code, struct dmove_s* dm, vinfo_array_t* aa) { /* decref any object that would be present in 'po' but not at all in the snapshot. Note that it is uncommon that this function actually finds any unused object at all. */ int i = aa->count; while (i--) { vinfo_t* a = aa->items[i]; if (a != NULL) { if (has_rtref(a->source)) { PsycoObject* po = dm->po; code_t* saved_code; a->source = remove_rtref(a->source); saved_code = po->code; po->code = code; psyco_decref_rt(po, a); code = po->code; po->code = saved_code; if (code > dm->code_limit) /* oops, buffer overflow. Start a new buffer */ code = data_new_buffer(code, dm); } if (a->array != NullArray) code = data_free_unused(code, dm, a->array); } } return code; } DEFINEFN code_t* psyco_unify(PsycoObject* po, vcompatible_t* lastmatch, CodeBufferObject** target) { /* Update 'po' to match 'lastmatch', then jump to 'lastmatch'. */ int i; struct dmove_s dm; code_t* code = po->code; code_t* backpointer; CodeBufferObject* target_codebuf = lastmatch->matching; int sdepth = get_stack_depth(&target_codebuf->snapshot); int popsdepth; char pops[REG_TOTAL+2]; #if PSYCO_DEBUG bool has_ccreg = (po->ccreg != NULL); #endif extra_assert(lastmatch->diff == NullArray); /* unify with exact match only */ psyco_assert_coherent(po); dm.usages_size = sdepth + sizeof(vinfo_t**); dm.usages = (char*) PyMem_MALLOC(dm.usages_size); if (dm.usages == NULL) OUT_OF_MEMORY(); memset(dm.usages, 0, dm.usages_size); /* set to all NULL */ memset(dm.copy_regs, 0, sizeof(dm.copy_regs)); fz_find_runtimes(&po->vlocals, &target_codebuf->snapshot, (fz_find_fn) &data_original_table, &dm, false); dm.po = po; dm.original_stack_depth = po->stack_depth; dm.code_origin = code; dm.code_limit = po->codelimit == NULL ? code : po->codelimit; dm.private_codebuf = NULL; if (sdepth > po->stack_depth) { /* more items in the target stack (uncommon case). Let the stack grow. */ STACK_CORRECTION(sdepth - po->stack_depth); po->stack_depth = sdepth; } /* update the stack */ dm.code = code; fz_find_runtimes(&po->vlocals, &target_codebuf->snapshot, (fz_find_fn) &data_update_stack, &dm, true); code = dm.code; /* decref any object that would be present in 'po' but not at all in the snapshot (data_update_stack() has removed the 'ref' tag of all vinfo_ts it actually used from 'po') */ code = data_free_unused(code, &dm, &po->vlocals); /* update the registers (1): reg-to-reg moves and exchanges */ popsdepth = po->stack_depth; memset(pops, -1, sizeof(pops)); for (i=0; i<REG_TOTAL; i++) { vinfo_t* a = dm.copy_regs[i]; if (a != NULL) { char rg = RUNTIME_REG(a); if (rg != REG_NONE) { if (rg != i) { /* the value of 'a' is currently in register 'rg' but should go into register 'i'. */ NEED_REGISTER(i); LOAD_REG_FROM_REG(i, rg); /*SET_RUNTIME_REG_TO(a, i); REG_NUMBER(po, rg) = NULL; REG_NUMBER(po, i) = a;*/ } dm.copy_regs[i] = NULL; } else { /* prepare the step (2) below by looking for registers whose source is near the top of the stack */ int from_tos = po->stack_depth - RUNTIME_STACK(a); extra_assert(from_tos >= 0); if (from_tos < REG_TOTAL*sizeof(void*)) { char* ptarget = pops + (from_tos / sizeof(void*)); if (*ptarget == -1) *ptarget = i; else *ptarget = -2; } } } } /* update the registers (2): stack-to-register POPs */ if (popsdepth == po->stack_depth) /* only if no PUSHes have messed things up */ for (i=0; pops[i]>=0 || pops[i+1]>=0; i++) { char reg = pops[i]; if (reg<0) {/* If there is only one 'garbage' stack entry, POP it as well. If there are more, give up and use regular MOVs to load the rest */ po->stack_depth -= 4; reg = pops[++i]; POP_REG(reg); } POP_REG(reg); dm.copy_regs[(int) reg] = NULL; po->stack_depth -= 4; } if (code > dm.code_limit) /* start a new buffer if we wrote past the end */ code = data_new_buffer(code, &dm); /* update the registers (3): stack-to-register loads */ for (i=0; i<REG_TOTAL; i++) { vinfo_t* a = dm.copy_regs[i]; if (a != NULL) LOAD_REG_FROM_EBP_BASE(i, RUNTIME_STACK(a)); } /* done */ STACK_CORRECTION(sdepth - po->stack_depth); if (code > dm.code_limit) /* start a new buffer if we wrote past the end */ code = data_new_buffer(code, &dm); #if PSYCO_DEBUG extra_assert(has_ccreg == (po->ccreg != NULL)); #endif backpointer = code; JUMP_TO((code_t*) target_codebuf->codestart); /* start a new buffer if the last JUMP_TO overflowed, but not if we had no room at all in the first place. */ if (code > dm.code_limit && po->codelimit != NULL) { /* the JMP instruction emitted by JUMP_TO() is not position- independent; we must emit it again at the new position */ code = data_new_buffer(backpointer, &dm); JUMP_TO((code_t*) target_codebuf->codestart); psyco_assert(code <= dm.code_limit); } PyMem_FREE(dm.usages); if (dm.private_codebuf == NULL) { Py_INCREF(target_codebuf); /* no new buffer created */ *target = target_codebuf; } else { SHRINK_CODE_BUFFER(dm.private_codebuf, code, "unify"); *target = dm.private_codebuf; /* add a jump from the original code buffer to the new one */ code = po->code; JUMP_TO((code_t*) dm.private_codebuf->codestart); dump_code_buffers(); } PsycoObject_Delete(po); return code; } --- NEW FILE: idispatcher.h --- /***************************************************************/ /*** Processor-specific routines for dispatcher.c ***/ /***************************************************************/ #ifndef _IDISPATCHER_H #define _IDISPATCHER_H #include "../vcompiler.h" #include "../processor.h" #include "iencoding.h" /***************************************************************/ /*** Freezing ***/ EXTERNFN void fpo_find_regs_array(vinfo_array_t* source, PsycoObject* po); /***************************************************************/ /*** Unification ***/ /* idispatcher.c implements psyco_unify(), whose header is given in dispatcher.h. Conversely, dispatcher.c implements the following function which is declared here because it is really internal: */ typedef void (*fz_find_fn) (vinfo_t* a, RunTimeSource bsource, void* extra); EXTERNFN void fz_find_runtimes(vinfo_array_t* aa, FrozenPsycoObject* fpo, fz_find_fn callback, void* extra, bool clear); /***************************************************************/ /*** Promotion ***/ /* Define to 1 to emit a "compare/jump-if-equal" pair of instructions that checks for the most common case (actually the last seen one). */ #define PROMOTION_FAST_COMMON_CASE 1 #if PROMOTION_FAST_COMMON_CASE /* for FIX_JUMP_IF_EQUAL() */ # define INTERNAL_PROMOTION_FIELDS code_t* jump_if_equal_code; #else # define INTERNAL_PROMOTION_FIELDS /* nothing */ #endif struct ipromotion_s { INTERNAL_PROMOTION_FIELDS }; inline code_t* fix_fast_common_case(void* fs, long value, code_t* codeptr) { #if PROMOTION_FAST_COMMON_CASE FIX_JUMP_IF_EQUAL(((struct ipromotion_s*)fs)->jump_if_equal_code, value, codeptr); #endif return codeptr; } inline void* ipromotion_finish(PsycoObject* po, vinfo_t* fix, void* do_promotion) { long xsource; struct ipromotion_s* fs; #if PROMOTION_FAST_COMMON_CASE code_t* jeqcode; BEGIN_CODE NEED_CC(); RTVINFO_IN_REG(fix); xsource = fix->source; RESERVE_JUMP_IF_EQUAL(RSOURCE_REG(xsource)); jeqcode = code; END_CODE #else xsource = fix->source; #endif if (PROMOTION_FAST_COMMON_CASE || !RSOURCE_REG_IS_NONE(xsource)) { /* remove from 'po->regarray' this value which will soon no longer be RUN_TIME */ REG_NUMBER(po, RSOURCE_REG(xsource)) = NULL; SET_RUNTIME_REG_TO_NONE(fix); } fs = (struct ipromotion_s*) psyco_call_code_builder(po, do_promotion, PROMOTION_FAST_COMMON_CASE, xsource); #if PROMOTION_FAST_COMMON_CASE fs->jump_if_equal_code = jeqcode; #endif return fs; } /***************************************************************/ /*** Misc. ***/ inline void conditional_jump_to(PsycoObject* po, code_t* target, condition_code_t condition) { BEGIN_CODE switch (condition) { case CC_ALWAYS_FALSE: /* never jumps */ break; case CC_ALWAYS_TRUE: JUMP_TO(target); /* always jumps */ break; default: FAR_COND_JUMP_TO(target, condition); } END_CODE } /* reserve a small buffer of code behind po->code in which conditional code can be stored. See make_code_conditional(). */ inline void setup_conditional_code_bounds(PsycoObject* po, PsycoObject* po2) { code_t* code2 = po->code + SIZE_OF_SHORT_CONDITIONAL_JUMP; po2->code = code2; po2->codelimit = code2 + RANGE_OF_SHORT_CONDITIONAL_JUMP; } /* mark a small buffer reserved by setup_conditional_code_bounds() to be only executed if 'condition' holds. */ inline void make_code_conditional(PsycoObject* po, code_t* codeend, condition_code_t condition) { code_t* target; code_t* code2 = po->code + SIZE_OF_SHORT_CONDITIONAL_JUMP; extra_assert(code2 <= codeend && codeend <= code2 + RANGE_OF_SHORT_CONDITIONAL_JUMP); BEGIN_CODE if (IS_A_SINGLE_JUMP(code2, codeend, target)) FAR_COND_JUMP_TO(target, condition); /* replace a jump with a cond jump */ else { /* other cases: write a short cond jump to skip the block if !condition */ SHORT_COND_JUMP_TO(codeend, INVERT_CC(condition)); code = codeend; } END_CODE } #endif /* _IDISPATCHER_H */ --- NEW FILE: iencoding.h --- /***************************************************************/ /*** Processor-specific code-producing macros ***/ /***************************************************************/ #ifndef _IENCODING_H #define _IENCODING_H #include "../psyco.h" /* set to 0 to emit code that runs on 386 and 486 */ #ifndef PENTIUM_INSNS # define PENTIUM_INSNS 1 #endif /* Define to 1 to always write the most compact encoding of instructions. (a quite minor overhead). Set to 0 to disable. No effect on real optimizations. */ #ifndef COMPACT_ENCODING # define COMPACT_ENCODING 1 #endif /* Define to 0 to use EBP as any other register, or to 1 to reserve it */ #ifndef EBP_IS_RESERVED # define EBP_IS_RESERVED 0 #endif typedef enum { REG_386_EAX = 0, REG_386_ECX = 1, REG_386_EDX = 2, REG_386_EBX = 3, REG_386_ESP = 4, REG_386_EBP = 5, REG_386_ESI = 6, REG_386_EDI = 7, #define REG_TOTAL 8 REG_NONE = -1} reg_t; #define REG_FUNCTIONS_RETURN REG_386_EAX #define REG_ANY_CALLER_SAVED REG_386_EAX /* just any "trash" register */ #define REG_ANY_CALLEE_SAVED REG_386_EBX /* saved by C functions */ typedef enum { CC_O = 0, /* overflow */ CC_NO = 1, CC_B = 2, /* below (unsigned) */ CC_NB = 3, CC_E = 4, /* equal */ CC_NE = 5, CC_BE = 6, /* below or equal (unsigned) */ CC_NBE = 7, CC_S = 8, /* sign (i.e. negative) */ CC_NS = 9, CC_L = 12, /* lower than */ CC_NL = 13, CC_LE = 14, /* lower or equal */ CC_NLE = 15, #define CC_TOTAL 16 /* synonyms */ CC_NGE = CC_L, /* not greater or equal */ CC_GE = CC_NL, /* greater or equal */ CC_NG = CC_LE, /* not greater than */ CC_G = CC_NLE, /* greater than */ CC_uL = CC_B, /* unsigned test */ CC_uNL = CC_NB, /* unsigned test */ CC_uLE = CC_BE, /* unsigned test */ CC_uNLE = CC_NBE, /* unsigned test */ CC_uNGE = CC_uL, /* unsigned test */ CC_uGE = CC_uNL, /* unsigned test */ CC_uNG = CC_uLE, /* unsigned test */ CC_uG = CC_uNLE, /* unsigned test */ CC_ALWAYS_FALSE = 16, /* pseudo condition codes for known outcomes */ CC_ALWAYS_TRUE = 17, CC_ERROR = -1 } condition_code_t; #define INVERT_CC(cc) ((condition_code_t)((int)(cc) ^ 1)) /* the registers we want Psyco to use in compiled code, as a circular linked list (see iprocessor.c) */ EXTERNVAR reg_t RegistersLoop[REG_TOTAL]; /* the first register in RegistersLoop that Psyco will use. The best choice is probably the first callee-saved register */ #define REG_LOOP_START REG_386_EBX /* returns the next register that should be used */ #define next_free_reg(po) \ ((po)->last_used_reg = RegistersLoop[(int)((po)->last_used_reg)]) /* processor-depend part of PsycoObject */ #define PROCESSOR_PSYCOOBJECT_FIELDS \ int stack_depth; /* the size of data currently pushed in the stack */ \ vinfo_t* reg_array[REG_TOTAL]; /* the 'vinfo_t' currently stored in regs */ \ vinfo_t* ccreg; /* processor condition codes (aka flags) */ \ reg_t last_used_reg; /* the most recently used register */ /*****************************************************************/ /*** Production of code (common instruction encodings) ***/ /* Most of the following macros implicitely use and update the * local variable 'code'. Some also use 'po'. No macro outside the * present header file must implicitely use or modify 'code'. */ /* Written as a large set of macro. */ #define RSOURCE_REG(src) getreg(src) #define RSOURCE_REG_IS_NONE(src) is_reg_none(src) #define RSOURCE_STACK(src) getstack(src) #define RUNTIME_STACK_MAX RunTime_StackMax #define RUNTIME_STACK_NONE RunTime_StackNone #define RUNTIME_REG(vi) RSOURCE_REG ((vi)->source) #define RUNTIME_REG_IS_NONE(vi) RSOURCE_REG_IS_NONE ((vi)->source) #define RUNTIME_STACK(vi) RSOURCE_STACK ((vi)->source) #define SET_RUNTIME_REG_TO(vi, rg) ((vi)->source = \ set_rtreg_to((vi)->source, rg)) #define SET_RUNTIME_REG_TO_NONE(vi) ((vi)->source = \ set_rtreg_to_none((vi)->source)) #define SET_RUNTIME_STACK_TO(vi, s) ((vi)->source = \ set_rtstack_to((vi)->source, (s))) #define SET_RUNTIME_STACK_TO_NONE(vi) ((vi)->source = \ set_rtstack_to_none((vi)->source)) #define KSOURCE_SOURCE(src) CompileTime_Get(src) #define KNOWN_SOURCE(vi) KSOURCE_SOURCE((vi)->source) #define NEXT_FREE_REG() next_free_reg(po) #define REG_NUMBER(po, rg) ((po)->reg_array[(int)(rg)]) /* release a run-time vinfo_t */ #define RTVINFO_RELEASE(rtsource) do { \ if (!RSOURCE_REG_IS_NONE(rtsource)) \ REG_NUMBER(po, RSOURCE_REG(rtsource)) = NULL; \ } while (0) /* move a run-time vinfo_t */ #define RTVINFO_MOVE(rtsource, vtarget) do { \ if (!RSOURCE_REG_IS_NONE(rtsource)) \ REG_NUMBER(po, RSOURCE_REG(rtsource)) = (vtarget); \ } while (0) /* for PsycoObject_Duplicate() */ #define DUPLICATE_PROCESSOR(result, po) do { \ int i; \ for (i=0; i<REG_TOTAL; i++) \ if (REG_NUMBER(po, i) != NULL) \ REG_NUMBER(result, i) = REG_NUMBER(po, i)->tmp; \ if (po->ccreg != NULL) \ result->ccreg = po->ccreg->tmp; \ \ result->stack_depth = po->stack_depth; \ result->last_used_reg = po->last_used_reg; \ } while (0) #define RTVINFO_CHECK(po, vsource, found) do { \ RunTimeSource _src = (vsource)->source; \ if (!RSOURCE_REG_IS_NONE(_src)) \ { \ extra_assert(REG_NUMBER(po, RSOURCE_REG(_src)) == (vsource)); \ found[(int) RSOURCE_REG(_src)] = 1; \ } \ } while (0) #define RTVINFO_CHECKED(po, found) do { \ int i; \ for (i=0; i<REG_TOTAL; i++) \ if (!found[i]) \ extra_assert(REG_NUMBER(po, i) == NULL); \ } while (0) /*****************************************************************/ #define CODE_FOUR_BYTES(code, b1, b2, b3, b4) /* for constant bytes */ \ (*(long*)(code) = ((unsigned char)(b1)) | (((unsigned char)(b2))<<8) | \ (((unsigned char)(b3))<<16) | (((unsigned char)(b4))<<24)) /* note: the following macro starts writing at code+1 */ #if 0 /* access stack by [EBP-n] where 'n' is fixed for the variable */ #define MODRM_EBP_BASE(middle, stack_pos) do { \ extra_assert(0 < (stack_pos) && (stack_pos) <= RUNTIME_STACK_MAX); \ if (COMPACT_ENCODING && (stack_pos) <= 128) \ { \ code[1] = 0x45 | (middle); \ code[2] = -(stack_pos); \ code += 3; \ } \ else \ { \ code[1] = 0x85 | (middle); \ *(long*)(code+2) = -(stack_pos); \ code += 6; \ } \ } while (0) #else /* access stack by [ESP+n] where 'n' varies depending on the current ESP */ #define MODRM_EBP_BASE(middle, stack_pos) do { \ int _s_p = po->stack_depth - (stack_pos); \ extra_assert(0 < (stack_pos) && (stack_pos) <= RUNTIME_STACK_MAX); \ extra_assert(0 <= _s_p); \ code[2] = 0x24; \ if (COMPACT_ENCODING && _s_p == 0) \ { \ code[1] = 0x04 | (middle); \ code += 3; \ } \ else if (COMPACT_ENCODING && _s_p < 128) \ { \ code[1] = 0x44 | (middle); \ code[3] = _s_p; \ code += 4; \ } \ else \ { \ code[1] = 0x84 | (middle); \ *(long*)(code+3) = _s_p; \ code += 7; \ } \ } while (0) #endif /* Emit instruction 'opcode' having a mod/rm as its second byte. Insert 'middle' in the mod/rm. Let the mod/rm point to the given stack_pos. */ #define INSTR_EBP_BASE(opcode, middle, stack_pos) do { \ code[0] = (opcode); \ MODRM_EBP_BASE(middle, stack_pos); \ } while (0) /* note: the following macro starts writing at code+1 */ #define MODRM_FROM_RT(source, middle) do { \ if (RSOURCE_REG_IS_NONE(source)) \ MODRM_EBP_BASE(middle, RSOURCE_STACK(source)); \ else { /* register source */ \ code[1] = 0xC0 | (middle) | RSOURCE_REG(source); \ code += 2; \ } \ } while (0) /* Same as INSTR_EBP_BASE but reading from the 'source' of a run-time vinfo_t */ #define INSTR_MODRM_FROM_RT(source, opcode, middle) do { \ code[0] = (opcode); \ MODRM_FROM_RT(source, middle); \ } while (0) /* The "common instructions" groups: there are 8 arithmetic instructions whose encodings are identical. Here they are, with their 'group' value: ADD (group 0) OR (group 1) ADC (group 2) SBB (group 3) AND (group 4) SUB (group 5) XOR (group 6) CMP (group 7) */ /* The following macro encodes "INSTR register, immediate" */ #define COMMON_INSTR_IMMED(group, rg, value) do { \ long _v; \ code[1] = 0xC0 | (group<<3) | (rg); \ _v = value; \ if (COMPACT_ENCODING && -128 <= _v && _v < 128) { \ code[2] = (code_t) _v; \ code[0] = 0x83; \ code += 3; \ } \ else { \ *(long*)(code+2) = _v; \ code[0] = 0x81; \ code += 6; \ } \ } while (0) /* Encodes "INSTR register, source" for a run-time or compile-time vinfo_t */ #define COMMON_INSTR_FROM(group, rg, source) do { \ if (((source) & TimeMask) == RunTime) \ COMMON_INSTR_FROM_RT(group, rg, source); \ else \ COMMON_INSTR_IMMED(group, rg, KSOURCE_SOURCE(source)->value); \ } while(0) /* Encodes "INSTR register, source" for a run-time vinfo_t */ #define COMMON_INSTR_FROM_RT(group, rg, source) do { \ code[0] = group*8 + 3; \ MODRM_FROM_RT(source, (rg)<<3); \ } while(0) /* Encodes "INSTR reg" for the following instructions: NOT (group 2) NEG (group 3) IDIV (group 7) */ #define UNARY_INSTR_ON_REG(group, rg) do { \ code[0] = 0xF7; \ code[1] = 0xC0 | (group<<3) | (rg); \ code += 2; \ } while (0) /* Encodes "INSTR source" for the same instructions as above */ #define UNARY_INSTR_FROM_RT(group, source) do { \ INSTR_MODRM_FROM_RT(0xF7, source, (group)<<3); \ } while (0) /* Encodes "INC rg" and "DEC rg" */ #define INCREASE_REG(rg) (*code++ = 0x40 | (rg)) #define DECREASE_REG(rg) (*code++ = 0x48 | (rg)) /* Encodes taking absolute value of the register 'rg' knowing that 'sourcecopy' is a (run-time) copy of 'rg' */ #define INT_ABS(rg, sourcecopy) do { \ /* as you can check the following takes the absolute value of (say) EAX: \ ADD EAX, EAX \ SBB EAX, sourcecopy \ SBB EDX, EDX \ XOR EAX, EDX \ (note: although the idea is not original, the above code might be \ original as it has been found by an exhaustive search on *all* \ short codes :-) \ */ \ reg_t _rg2; \ code[0] = 0x01; \ code[1] = 0xC0 | ((rg)<<3) | (rg); /* ADD rg, rg */ \ code += 2; \ COMMON_INSTR_FROM_RT(3, rg, sourcecopy); /* SBB rg, sourcecopy */ \ DELAY_USE_OF(rg); \ NEED_FREE_REG(_rg2); \ code[0] = 0x19; \ code[1] = 0xC0 | (_rg2<<3) | _rg2; /* SBB _rg2, _rg2 */ \ code[2] = 0x31; \ code[3] = 0xC0 | (_rg2<<3) | (rg); /* XOR rg, _rg2 */ \ code += 4; \ } while (0) #define CHECK_ABS_OVERFLOW CC_S /* Encodes a check (zero/non-zero) on the given 'source' */ #define CHECK_ZERO_CONDITION CC_E #define CHECK_NONZERO_CONDITION INVERT_CC(CHECK_ZERO_CONDITION) #define CHECK_NONZERO_FROM_RT(source) do { \ NEED_CC_SRC(source); \ if (RSOURCE_REG_IS_NONE(source)) \ { \ INSTR_MODRM_FROM_RT(source, 0x83, 7<<3); /* CMP (source), imm8 */ \ *code++ = 0; \ } \ else \ CHECK_NONZERO_REG(RSOURCE_REG(source)); \ } while (0) #define CHECK_NONZERO_REG(rg) ( \ code[0] = 0x85, /* TEST reg, reg */ \ code[1] = 0xC0 | ((rg)*9), \ code += 2) #define COMPARE_IMMED_FROM_RT(source, immed) do { \ long _value = (immed); \ if (COMPACT_ENCODING && -128 <= _value && _value < 128) \ /*if (_value == 0 && !RSOURCE_REG_IS_NONE(source)) \ CHECK_NONZERO_REG(RSOURCE_REG(source)); \ else*/ { \ INSTR_MODRM_FROM_RT(source, 0x83, 7<<3); /* CMP (source), imm8 */ \ *code++ = (code_t) _value; \ } \ else { \ INSTR_MODRM_FROM_RT(source, 0x81, 7<<3); /* CMP (source), imm32 */ \ *(long*)code = _value; \ code += 4; \ } \ } while (0) /* Signed integer multiplications */ #define IMUL_REG_FROM_RT(source, rg) do { \ *code++ = 0x0F; /* IMUL rg, source */ \ INSTR_MODRM_FROM_RT(source, 0xAF, (rg)<<3); \ } while (0) #define IMUL_IMMED_FROM_RT(source, immed, dstrg) do { \ long _value = (immed); \ code_t opcode = (COMPACT_ENCODING && -128 <= _value && _value < 128) \ ? 0x6B : 0x69; /* IMUL dstrg, source, immed */ \ INSTR_MODRM_FROM_RT(source, opcode, (dstrg)<<3); \ if (opcode == 0x69) { \ *(long*)code = _value; \ code += 4; \ } \ else \ *code++ = (code_t) _value; \ } while (0) /* Shitfs. The counters must never be >=32. */ #define SHIFT_GENERIC1(rg, cnt, middle) do { \ code[1] = 0xC0 | ((middle)<<3) | (rg); \ if (COMPACT_ENCODING && cnt==1) { \ code[0] = 0xD1; \ code += 2; \ } \ else { \ code[0] = 0xC1; \ code[2] = (cnt); \ code += 3; \ } \ } while (0) #define SHIFT_COUNTER REG_386_ECX /* must be in range(0,32) */ #define SHIFT_GENERICCL(rg, middle) do { \ code[0] = 0xD3; \ code[1] = 0xC0 | ((middle)<<3) | (rg); \ code += 2; \ } while (0) #define SHIFT_LEFT_BY(rg, cnt) SHIFT_GENERIC1(rg, cnt, 4) #define SHIFT_LEFT_CL(rg) SHIFT_GENERICCL(rg, 4) #define SHIFT_RIGHT_BY(rg, cnt) SHIFT_GENERIC1(rg, cnt, 5) #define SHIFT_RIGHT_CL(rg) SHIFT_GENERICCL(rg, 5) #define SHIFT_SIGNED_RIGHT_BY(rg, cnt) SHIFT_GENERIC1(rg, cnt, 7) #define SHIFT_SIGNED_RIGHT_CL(rg) SHIFT_GENERICCL(rg, 7) /* PUSH the value described in the 'source' of a run-time vinfo_t */ #define PUSH_FROM_RT(source) do { \ if (RSOURCE_REG_IS_NONE(source)) \ PUSH_EBP_BASE(RSOURCE_STACK(source)); \ else \ PUSH_REG(RSOURCE_REG(source)); \ } while (0) /* insert a PUSH_FROM_RT at point 'insert_at' in the given 'code1' */ /* EXTERNFN code_t* insert_push_from_rt(PsycoObject* po, code_t* code1, */ /* long source, code_t* insert_at); */ /* PUSH a run-time or compile-time vinfo_t's value */ #define PUSH_FROM(source) do { \ if (((source) & TimeMask) == RunTime) \ PUSH_FROM_RT(source); \ else \ PUSH_IMMED(KSOURCE_SOURCE(source)->value); \ } while (0) /*****************************************************************/ /*** Some basic management instructions... ***/ /* these two macros do not actually emit the code. They just give you the one-byte instruction encoding. */ #define PUSH_REG_INSTR(reg) (0x50 | (reg)) #define POP_REG_INSTR(reg) (0x58 | (reg)) #define PUSH_REG(reg) (*code++ = PUSH_REG_INSTR(reg)) #define POP_REG(reg) (*code++ = POP_REG_INSTR(reg)) #define PUSH_CC_FLAGS_INSTR 0x9C /* PUSHF */ #define POP_CC_FLAGS_INSTR 0x9D /* POPF */ #define PUSH_CC_FLAGS() (*code++ = PUSH_CC_FLAGS_INSTR) #define POP_CC_FLAGS() (*code++ = POP_CC_FLAGS_INSTR) #define LOAD_REG_FROM_REG(dst, src) ( \ code[0] = 0x89, /* MOV dst, src */ \ code[1] = 0xC0 | ((src) << 3) | (dst), \ code += 2 \ ) #define XCHG_REGS(rg1, rg2) do { \ if (COMPACT_ENCODING && ((rg1) == REG_386_EAX)) \ *code++ = 0x90 | (rg2); \ else if (COMPACT_ENCODING && ((rg2) == REG_386_EAX)) \ *code++ = 0x90 | (rg1); \ else { \ code[0] = 0x87; \ code[1] = 0xC0 | ((rg2)<<3) | (rg1); \ code += 2; \ } \ } while (0) #define LOAD_REG_FROM_IMMED(dst, immed) ( \ code[0] = 0xB8 | (dst), /* MOV dst, immed */ \ *(long*)(code+1) = (immed), \ code += 5 \ ) #define SIZE_OF_LOAD_REG_FROM_IMMED 5 /* loads 0 in a register. The macro name reminds you that this clobbers po->ccreg (use NEED_CC() to save it first). */ /* #define CLEAR_REG_CLOBBER_CC(rg) ( \ */ /* code[0] = 0x33, * XOR rg, rg * \ */ /* code[1] = 0xC0 | ((rg)<<3) | (rg), \ */ /* code += 2 \ */ /* ) */ #define LOAD_REG_FROM_EBP_BASE(dst, stack_pos) \ INSTR_EBP_BASE(0x8B, (dst)<<3, stack_pos) /* MOV dst, [EBP-stack_pos] */ #define SAVE_REG_TO_EBP_BASE(src, stack_pos) \ INSTR_EBP_BASE(0x89, (src)<<3, stack_pos) /* MOV [EBP-stack_pos], src */ #define XCHG_REG_AND_EBP_BASE(src, stack_pos) \ INSTR_EBP_BASE(0x87, (src)<<3, stack_pos) /* XCHG src, [EBP-stack_pos] */ #define SAVE_IMMED_TO_EBP_BASE(immed, stack_pos) do { \ INSTR_EBP_BASE(0xC7, 0<<3, stack_pos); /* MOV [EBP-stack_pos], immed */\ *(long*)code = (immed); \ code += 4; \ } while (0) #define SAVE_IMM8_TO_EBP_BASE(imm8, stack_pos) do { \ INSTR_EBP_BASE(0xC6, 0<<3, stack_pos); /* MOV byte [EBP-stack_pos], imm8 */\ *code++ = (imm8); \ } while (0) #define LOAD_REG_FROM_RT(source, dst) \ INSTR_MODRM_FROM_RT(source, 0x8B, (dst)<<3) /* MOV dst, (...) */ #define SAVE_REG_TO_RT(source, src) \ INSTR_MODRM_FROM_RT(source, 0x89, (src)<<3) /* MOV (...), src */ #define PUSH_EBP_BASE(ofs) \ INSTR_EBP_BASE(0xFF, 0x30, ofs) /* PUSH [EBP-ofs] */ #define POP_EBP_BASE(ofs) \ INSTR_EBP_BASE(0x8F, 0x00, ofs) /* POP [EBP-ofs] */ #define PUSH_IMMED(immed) do { \ if (COMPACT_ENCODING && -128 <= (immed) && (immed) < 128) { \ code[0] = 0x6A; /* PUSH imm8 */ \ code[1] = (code_t) (immed); \ code += 2; \ } \ else { \ code[0] = 0x68; /* PUSH imm32 */ \ *(long*)(code+1) = (immed); \ code += 5; \ } \ } while (0) /* call a function written in C. Use the macros CALL_SET_ARG_xxx() for each argument in reverse order, then use CALL_C_FUNCTION(). */ /* Note: the update of po->stack_depth saves one "ADD ESP, 4*nb_args" at the end of the call. If the stack is to be kept as compact as possible we might as well write the instruction. We have to update po->stack_depth at each CALL_SET_ARG_xxx (instead of just in CALL_C_FUNCTION) because run-time arguments after the first one would be fetched at the wrong place otherwise. */ #define CALL_SET_ARG_IMMED(immed, arg_index, nb_args) do { \ PUSH_IMMED(immed); \ po->stack_depth += 4; \ } while (0) #define CALL_SET_ARG_FROM_RT(source, arg_index, nb_args) do { \ PUSH_FROM_RT(source); \ po->stack_depth += 4; \ } while (0) #define CALL_SET_ARG_FROM(source, arg_index, nb_args) do { \ PUSH_FROM(source); \ po->stack_depth += 4; \ } while (0) #define CALL_C_FUNCTION(target, nb_args) do { \ code[0] = 0xE8; /* CALL */ \ code += 5; \ *(long*)(code-4) = (code_t*)(target) - code; \ } while (0) #define CALL_C_FUNCTION_FROM_RT(source, nb_args) \ INSTR_MODRM_FROM_RT(source, 0xFF, 2<<3) /* CALL [source] */ #define CALL_C_FUNCTION_FROM(source, nb_args) do { \ if (((source) & CompileTime) != 0) \ CALL_C_FUNCTION(KSOURCE_SOURCE(source)->value, nb_args); \ else \ CALL_C_FUNCTION_FROM_RT(source, nb_args); \ } while (0) /* optimization of a CALL followed by a JMP */ #define CALL_C_FUNCTION_AND_JUMP(target, nb_args, jmptarget) do { \ PUSH_IMMED((long)(jmptarget)); \ JUMP_TO((code_t*)(target)); \ } while (0) /* load the 'dst' register with the run-time address of 'source' which must be in the stack */ #define LOAD_ADDRESS_FROM_RT(source, dst) do { \ extra_assert(RSOURCE_STACK(source) != RUNTIME_STACK_NONE); \ INSTR_MODRM_FROM_RT(source, 0x8D, ((dst)<<3)); /* LEA dst, [source] */ \ } while (0) #define LOAD_REG_FROM_REG_PLUS_REG(dst, rg1, rg2) do { \ code[0] = 0x8D; \ code[1] = 0x04 | ((dst)<<3); /* LEA dst, [rg1+rg2] */ \ code[2] = ((rg1)<<3) | (rg2); \ if (!EBP_IS_RESERVED && (rg2) == REG_386_EBP) \ { \ if ((rg1) != REG_386_EBP) \ code[2] = ((rg2)<<3) | (rg1); \ else \ { \ code[1] |= 0x40; \ code[3] = 0; \ code++; \ } \ } \ code += 3; \ } while (0) #define LOAD_REG_FROM_REG_PLUS_IMMED(dst, rg1, immed) do { \ long _value = (immed); \ code[0] = 0x8D; /* LEA dst,[rg1+immed] */ \ if (COMPACT_ENCODING && -128 <= _value && _value < 128) { \ code[1] = 0x40 | (dst)<<3 | (rg1); \ code[2] = (code_t) _value; \ code += 3; \ } \ else { \ code[1] = 0x80 | (dst)<<3 | (rg1); \ *(long*)(code+2) = _value; \ code += 6; \ } \ } while (0) /* saving and restoring the registers currently in use (see also SAVE_REGS_FN_CALLS) */ #define TEMP_SAVE_REGS_FN_CALLS do { \ if (COMPACT_ENCODING) { \ if (REG_NUMBER(po, REG_386_EAX) != NULL) PUSH_REG(REG_386_EAX); \ if (REG_NUMBER(po, REG_386_ECX) != NULL) PUSH_REG(REG_386_ECX); \ if (REG_NUMBER(po, REG_386_EDX) != NULL) PUSH_REG(REG_386_EDX); \ if (po->ccreg != NULL) PUSH_CC_FLAGS(); \ } \ else { \ CODE_FOUR_BYTES(code, \ PUSH_REG_INSTR(REG_386_EAX), \ PUSH_REG_INSTR(REG_386_ECX), \ PUSH_REG_INSTR(REG_386_EDX), \ PUSH_CC_FLAGS_INSTR); \ code += 4; \ } \ } while (0) #define TEMP_RESTORE_REGS_FN_CALLS do { \ if (COMPACT_ENCODING) { \ if (po->ccreg != NULL) POP_CC_FLAGS(); \ if (REG_NUMBER(po, REG_386_EDX) != NULL) POP_REG(REG_386_EDX); \ if (REG_NUMBER(po, REG_386_ECX) != NULL) POP_REG(REG_386_ECX); \ if (REG_NUMBER(po, REG_386_EAX) != NULL) POP_REG(REG_386_EAX); \ } \ else { \ CODE_FOUR_BYTES(code, \ POP_CC_FLAGS_INSTR, \ POP_REG_INSTR(REG_386_EDX), \ POP_REG_INSTR(REG_386_ECX), \ POP_REG_INSTR(REG_386_EAX)); \ code += 4; \ } \ } while (0) /* same as above, but concludes with a JMP *EAX */ #define TEMP_RESTORE_REGS_FN_CALLS_AND_JUMP do { \ if (COMPACT_ENCODING) { \ if (po->ccreg != NULL) POP_CC_FLAGS(); \ if (REG_NUMBER(po, REG_386_EDX) != NULL) POP_REG(REG_386_EDX); \ if (REG_NUMBER(po, REG_386_ECX) != NULL) POP_REG(REG_386_ECX); \ } \ else { \ CODE_FOUR_BYTES(code, \ POP_CC_FLAGS_INSTR, \ POP_REG_INSTR(REG_386_EDX), \ POP_REG_INSTR(REG_386_ECX), \ 0 /* dummy */); \ code += 3; \ } \ if (!COMPACT_ENCODING || REG_NUMBER(po, REG_386_EAX) != NULL) { \ /* must restore EAX, but it contains the jump target... */ \ CODE_FOUR_BYTES(code, \ 0x87, \ 0x04, \ 0x24, /* XCHG EAX, [ESP] */ \ 0xC3); /* RET */ \ code += 4; \ } \ else { \ code[0] = 0xFF; \ code[1] = 0xE0; /* JMP *EAX */ \ code += 2; \ } \ } while (0) /* put an immediate value in memory */ #define SET_REG_ADDR_TO_IMMED(rg, immed) do { \ code[0] = 0xC7; /* MOV [reg], immed */ \ if (EBP_IS_RESERVED || (rg) != REG_386_EBP) \ { \ extra_assert((rg) != REG_386_EBP); \ code[1] = (rg); \ } \ else \ { \ *++code = 0x45; \ code[1] = 0; \ } \ *(long*)(code+2) = (immed); \ code += 6; \ } while (0) /* put an immediate value in memory */ #define SET_IMMED_ADDR_TO_IMMED(addr, immed) do { \ code[0] = 0xC7; /* MOV [addr], immed */ \ code[1] = 0x05; \ *(long*)(code+2) = (addr); \ *(long*)(code+6) = (immed); \ code += 10; \ } while (0) /*****************************************************************/ /*** vinfo_t saving ***/ /* save 'vi', which is currently in register 'rg'. */ #define SAVE_REG_VINFO(vi, rg) do { \ PUSH_REG(rg); \ po->stack_depth += 4; \ SET_RUNTIME_STACK_TO(vi, po->stack_depth); \ } while (0) /* * save 'vi' if needed. * */ /* #define SAVE_VINFO(vi) do { */ /* if (((vi)->source & (TIME_MASK | RUNTIME_STACK_MASK)) == */ /* (RUN_TIME | RUNTIME_STACK_NONE)) */ /* SAVE_REG_VINFO(vi, RUNTIME_REG(vi), 0); */ /* } while (0) */ /* ensure that the register 'rg' is free */ #define NEED_REGISTER(rg) do { \ vinfo_t* _content = REG_NUMBER(po, (rg)); \ if (_content != NULL) { \ if (RUNTIME_STACK(_content) == RUNTIME_STACK_NONE) \ SAVE_REG_VINFO(_content, rg); \ SET_RUNTIME_REG_TO_NONE(_content); \ REG_NUMBER(po, (rg)) = NULL; \ } \ } while (0) /* ensure that the condition code flags of the processor no longer contain any useful information */ #define NEED_CC() NEED_CC_REG(REG_NONE) /* same as NEED_CC() but don't overwrite rg */ #define NEED_CC_REG(rg) do { \ if (po->ccreg != NULL) \ code = psyco_compute_cc(po, code, (rg)); \ } while (0) /* same as NEED_CC() but don't overwrite the given source */ #define NEED_CC_SRC(src) \ NEED_CC_REG(is_runtime(src) ? RSOURCE_REG(src) : REG_NONE) /*internal, see processor.c*/ EXTERNFN code_t* psyco_compute_cc(PsycoObject* po, code_t* code, reg_t reserved); #define LOAD_REG_FROM_CONDITION(rg, cc) do { /* 'rg' is an 8-bit reg */ \ code[0] = 0x0F; /* SETcond rg8 */ \ code[1] = 0x90 | (cc); \ code[2] = 0xC0 | (rg); /* actually an 8-bit register, but the first four \ 32-bit registers have the same number as their \ respective lower-8-bit parts */ \ code[3] = 0x0F; \ code[4] = 0xB6; /* MOVZX rg32, rg8 */ \ code[5] = 0xC0 | ((rg)*9); \ code += 6; \ } while (0) /* save all registers that might be clobbered by a call to a C function */ #define SAVE_REGS_FN_CALLS do { \ NEED_CC(); \ NEED_REGISTER(REG_386_EAX); \ NEED_REGISTER(REG_386_ECX); \ NEED_REGISTER(REG_386_EDX); \ } while (0) #define NEED_FREE_REG_COND(targ, cond) do { \ targ = po->last_used_reg; \ if (!(cond) || REG_NUMBER(po, targ) != NULL) { \ do { \ targ = NEXT_FREE_REG(); \ } while (!(cond)); \ NEED_REGISTER(targ); \ } \ } while (0) /* like NEED_REGISTER but 'targ' is an output argument which will receive the number of a now-free register */ #define NEED_FREE_REG(targ) NEED_FREE_REG_COND(targ, 1) #define IS_BYTE_REG(rg) (REG_386_EAX <= (rg) && (rg) <= REG_386_EBX) #define NEED_FREE_BYTE_REG(targ, resrv1, resrv2) \ NEED_FREE_REG_COND(targ, IS_BYTE_REG(targ) && \ targ!=(resrv1) && targ!=(resrv2)) /* make sure that the register 'reg' will not be returned by the next call to NEED_FREE_REG() */ #define DELAY_USE_OF(reg) do { \ if (RegistersLoop[(int) po->last_used_reg] == (reg)) \ po->last_used_reg = (reg); \ if (po->last_used_reg == (reg)) \ NEXT_FREE_REG(); \ } while (0) /* the same for two registers */ #define DELAY_USE_OF_2(rg1, rg2) do { \ DELAY_USE_OF(rg1); \ DELAY_USE_OF(rg2); \ DELAY_USE_OF(rg1); \ } while (0) /* the same if the given source is run-time and in a register */ #define DONT_OVERWRITE_SOURCE(src) do { \ if (is_runtime_with_reg(src)) \ DELAY_USE_OF(RSOURCE_REG(src)); \ } while (0) /*****************************************************************/ /*** vinfo_t restoring ***/ /* ensure that a run-time vinfo is in a register */ #define RTVINFO_IN_REG(vi) do { \ if (RUNTIME_REG_IS_NONE(vi)) { \ /* reload the vinfo from the stack */ \ reg_t _rg; \ long _stack; \ NEED_FREE_REG(_rg); \ REG_NUMBER(po, _rg) = (vi); \ _stack = RUNTIME_STACK(vi); \ SET_RUNTIME_REG_TO(vi, _rg); \ LOAD_REG_FROM_EBP_BASE(_rg, _stack); \ } \ } while (0) #define RTVINFO_IN_BYTE_REG(vi, resrv1, resrv2) do { \ reg_t _currg = RUNTIME_REG(vi); \ if (!IS_BYTE_REG(_currg)) { \ reg_t _rg; \ NEED_FREE_BYTE_REG(_rg, resrv1, resrv2); \ if (_currg != REG_NONE) \ REG_NUMBER(po, _currg) = NULL; \ REG_NUMBER(po, _rg) = (vi); \ LOAD_REG_FROM_RT((vi)->source, _rg); \ SET_RUNTIME_REG_TO(vi, _rg); \ } \ } while (0) /* load register 'dst' from the given non-virtual source vinfo */ #define LOAD_REG_FROM(source, dst) do { ... [truncated message content] |