Annotation of tme/libtme/host/x86/rc-x86-insns.c, revision 1.1

1.1     ! root        1: /* $Id: rc-x86-insns.c,v 1.7 2010/06/05 19:11:51 fredette Exp $ */
        !             2: 
        !             3: /* libtme/host/x86/rc-x86-insns.c - x86 host recode instructions support: */
        !             4: 
        !             5: /*
        !             6:  * Copyright (c) 2007 Matt Fredette
        !             7:  * All rights reserved.
        !             8:  *
        !             9:  * Redistribution and use in source and binary forms, with or without
        !            10:  * modification, are permitted provided that the following conditions
        !            11:  * are met:
        !            12:  * 1. Redistributions of source code must retain the above copyright
        !            13:  *    notice, this list of conditions and the following disclaimer.
        !            14:  * 2. Redistributions in binary form must reproduce the above copyright
        !            15:  *    notice, this list of conditions and the following disclaimer in the
        !            16:  *    documentation and/or other materials provided with the distribution.
        !            17:  * 3. All advertising materials mentioning features or use of this software
        !            18:  *    must display the following acknowledgement:
        !            19:  *      This product includes software developed by Matt Fredette.
        !            20:  * 4. The name of the author may not be used to endorse or promote products
        !            21:  *    derived from this software without specific prior written permission.
        !            22:  *
        !            23:  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
        !            24:  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
        !            25:  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
        !            26:  * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
        !            27:  * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
        !            28:  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
        !            29:  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
        !            30:  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
        !            31:  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
        !            32:  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
        !            33:  * POSSIBILITY OF SUCH DAMAGE.
        !            34:  */
        !            35: 
        !            36: _TME_RCSID("$Id: rc-x86-insns.c,v 1.7 2010/06/05 19:11:51 fredette Exp $");
        !            37: 
        !            38: /* this maps from recode opcode to x86 binary operation: */
        !            39: #define TME_RECODE_X86_OPCODE_TO_BINOP(opcode) _tme_recode_x86_opcode_to_binop[(opcode) - TME_RECODE_OPCODE_SUB]
        !            40: static const tme_uint8_t _tme_recode_x86_opcode_to_binop[] = {
        !            41: #if TME_RECODE_OPCODE_SUB != 3
        !            42: #error "TME_RECODE_OPCODE_ values changed, need to fix _tme_recode_x86_opcode_to_binop"
        !            43: #endif
        !            44:   TME_RECODE_X86_OPCODE_BINOP_SUB, /* TME_RECODE_OPCODE_SUB */
        !            45: #if TME_RECODE_OPCODE_SUBC != 4
        !            46: #error "TME_RECODE_OPCODE_ values changed, need to fix _tme_recode_x86_opcode_to_binop"
        !            47: #endif
        !            48:   TME_RECODE_X86_OPCODE_BINOP_SBB, /* TME_RECODE_OPCODE_SUBC */
        !            49: #if TME_RECODE_OPCODE_AND != 5
        !            50: #error "TME_RECODE_OPCODE_ values changed, need to fix _tme_recode_x86_opcode_to_binop"
        !            51: #endif
        !            52:   TME_RECODE_X86_OPCODE_BINOP_AND, /* TME_RECODE_OPCODE_AND */
        !            53: #if TME_RECODE_OPCODE_OR != 6
        !            54: #error "TME_RECODE_OPCODE_ values changed, need to fix _tme_recode_x86_opcode_to_binop"
        !            55: #endif
        !            56:   TME_RECODE_X86_OPCODE_BINOP_OR, /* TME_RECODE_OPCODE_OR */
        !            57: #if TME_RECODE_OPCODE_XOR != 7
        !            58: #error "TME_RECODE_OPCODE_ values changed, need to fix _tme_recode_x86_opcode_to_binop"
        !            59: #endif
        !            60:   TME_RECODE_X86_OPCODE_BINOP_XOR, /* TME_RECODE_OPCODE_XOR */
        !            61: #if TME_RECODE_OPCODE_ADD != 8
        !            62: #error "TME_RECODE_OPCODE_ values changed, need to fix _tme_recode_x86_opcode_to_binop"
        !            63: #endif
        !            64:   TME_RECODE_X86_OPCODE_BINOP_ADD, /* TME_RECODE_OPCODE_ADD */
        !            65: #if TME_RECODE_OPCODE_ADDC != 9
        !            66: #error "TME_RECODE_OPCODE_ values changed, need to fix _tme_recode_x86_opcode_to_binop"
        !            67: #endif
        !            68:   TME_RECODE_X86_OPCODE_BINOP_ADC, /* TME_RECODE_OPCODE_ADDC */
        !            69: };
        !            70: 
        !            71: /* this maps from recode opcode to x86 shift group2 opcode: */
        !            72: #define TME_RECODE_X86_OPCODE_TO_GRP2(opcode)  \
        !            73:   ((((opcode)                                  \
        !            74:      - TME_RECODE_OPCODE_SHLL)                 \
        !            75:     + TME_RECODE_X86_OPCODE_GRP2_SHL)          \
        !            76:    + (((opcode)                                        \
        !            77:        - TME_RECODE_OPCODE_SHLL)               \
        !            78:       / 2))
        !            79: #if TME_RECODE_X86_OPCODE_TO_GRP2(TME_RECODE_OPCODE_SHLL) != TME_RECODE_X86_OPCODE_GRP2_SHL
        !            80: #error "TME_RECODE_OPCODE_ values changed"
        !            81: #endif
        !            82: #if TME_RECODE_X86_OPCODE_TO_GRP2(TME_RECODE_OPCODE_SHRL) != TME_RECODE_X86_OPCODE_GRP2_SHR
        !            83: #error "TME_RECODE_OPCODE_ values changed"
        !            84: #endif
        !            85: #if TME_RECODE_X86_OPCODE_TO_GRP2(TME_RECODE_OPCODE_SHRA) != TME_RECODE_X86_OPCODE_GRP2_SAR
        !            86: #error "TME_RECODE_OPCODE_ values changed"
        !            87: #endif
        !            88: 
        !            89: /* this emits a call to a subs: */
        !            90: static void
        !            91: _tme_recode_x86_insn_subs(struct tme_recode_ic *ic,
        !            92:                          const struct tme_recode_insn *insn)
        !            93: {
        !            94:   tme_uint8_t *thunk_bytes;
        !            95: 
        !            96:   /* force the destination operand to be in the same host register as
        !            97:      the first source operand: */
        !            98:   tme_recode_regs_dst_specific(ic,
        !            99:                               insn,
        !           100:                               0);
        !           101: 
        !           102:   /* a subs is allowed to destroy the a register, which must be the
        !           103:      most-significant of the two host registers used for the subs
        !           104:      second source operand.  if this is a double-host-size guest, we
        !           105:      must free both the a register and the other least-significant
        !           106:      host register, otherwise we only need to free the a register: */
        !           107: #if (TME_RECODE_X86_REG_HOST_SUBS_SRC1 & 1) != 0
        !           108: #error "TME_RECODE_X86_REG_HOST_ values changed"
        !           109: #endif
        !           110:   assert (tme_recode_x86_reg_from_host[TME_RECODE_X86_REG_HOST_SUBS_SRC1 + 1] == TME_RECODE_X86_REG_A);
        !           111:   tme_recode_regs_host_free_specific(ic,
        !           112:                                     (TME_RECODE_SIZE_IS_DOUBLE_HOST(ic->tme_recode_ic_reg_size)
        !           113:                                      ? TME_RECODE_X86_REG_HOST_SUBS_SRC1
        !           114:                                      : (TME_RECODE_X86_REG_HOST_SUBS_SRC1 + 1)));
        !           115: 
        !           116:   /* a subs is allowed to destroy the d and c registers, which must be
        !           117:      the two host registers used for the subs first source operand.
        !           118:      the d register must be a true host register, and the c register
        !           119:      must not be a true host register.  if this isn't a
        !           120:      double-host-size guest, we must free the d register.  we must
        !           121:      always mark the c register as destroyed: */
        !           122: #if (TME_RECODE_X86_REG_HOST_SUBS_SRC0 + 1) != TME_RECODE_REG_HOST_UNDEF
        !           123: #error "TME_RECODE_X86_REG_HOST_ values changed"
        !           124: #endif
        !           125:   assert (tme_recode_x86_reg_from_host[TME_RECODE_X86_REG_HOST_SUBS_SRC0 + 0] == TME_RECODE_X86_REG_D);
        !           126:   assert (tme_recode_x86_reg_from_host[TME_RECODE_X86_REG_HOST_SUBS_SRC0 + 1] == TME_RECODE_X86_REG_C);
        !           127:   if (!TME_RECODE_SIZE_IS_DOUBLE_HOST(ic->tme_recode_ic_reg_size)) {
        !           128:     tme_recode_regs_host_free_specific(ic,
        !           129:                                       TME_RECODE_X86_REG_HOST_SUBS_SRC0);
        !           130:   }
        !           131:   ic->tme_recode_x86_ic_thunks_reg_guest_window_c = TME_RECODE_REG_GUEST_WINDOW_UNDEF;
        !           132: 
        !           133:   /* start another instruction: */
        !           134:   tme_recode_x86_insns_start(ic, thunk_bytes);
        !           135: 
        !           136:   /* emit the call to the instruction's subs: */
        !           137:   thunk_bytes[0] = TME_RECODE_X86_OPCODE_CALL_RELz;
        !           138:   thunk_bytes += 1 + sizeof(tme_uint32_t);
        !           139:   ((tme_uint32_t *) thunk_bytes)[-1]
        !           140:     = (insn->tme_recode_x86_insn_subs_thunk_off
        !           141:        - tme_recode_build_to_thunk_off(ic, thunk_bytes));
        !           142: 
        !           143:   /* finish this instruction: */
        !           144:   tme_recode_x86_insns_finish(ic, thunk_bytes);
        !           145: }
        !           146: 
        !           147: /* this does a one's- or two's-complement negation of the destination
        !           148:    operand: */
        !           149: static void
        !           150: _tme_recode_x86_insn_negate(struct tme_recode_ic *ic,
        !           151:                            const struct tme_recode_insn *insn)
        !           152: {
        !           153:   tme_uint8_t *thunk_bytes;
        !           154:   unsigned int reg_x86;
        !           155:   unsigned int opcode;
        !           156: 
        !           157:   /* start more instructions: */
        !           158:   tme_recode_x86_insns_start(ic, thunk_bytes);
        !           159: 
        !           160:   /* get the opcode for this instruction: */
        !           161:   opcode = insn->tme_recode_insn_opcode;
        !           162:   assert (opcode == TME_RECODE_OPCODE_SUB
        !           163:          || opcode == TME_RECODE_OPCODE_ANDN
        !           164:          || opcode == TME_RECODE_OPCODE_ORN
        !           165:          || opcode == TME_RECODE_OPCODE_XORN);
        !           166: 
        !           167:   /* get the (first) x86 register to negate: */
        !           168:   reg_x86 = tme_recode_x86_reg_from_host[insn->tme_recode_insn_operand_dst];
        !           169: 
        !           170:   /* emit a not %reg or neg %reg: */
        !           171:   if (TME_RECODE_SIZE_HOST > TME_RECODE_SIZE_32) {
        !           172:     thunk_bytes[0] = TME_RECODE_X86_REX_B(TME_RECODE_SIZE_HOST, reg_x86);
        !           173:   }
        !           174:   thunk_bytes[(TME_RECODE_SIZE_HOST > TME_RECODE_SIZE_32) + 0] = TME_RECODE_X86_OPCODE_GRP3_Ev;
        !           175: #if TME_RECODE_OPCODE_SUB <= TME_RECODE_OPCODE_ANDN || TME_RECODE_OPCODE_SUB <= TME_RECODE_OPCODE_ORN || TME_RECODE_OPCODE_SUB <= TME_RECODE_OPCODE_XORN
        !           176: #error "TME_RECODE_OPCODE_ values changed"
        !           177: #endif
        !           178:   thunk_bytes[(TME_RECODE_SIZE_HOST > TME_RECODE_SIZE_32) + 1]
        !           179:     = (TME_RECODE_X86_MOD_OPREG_RM(TME_RECODE_X86_MOD_RM_REG(reg_x86), 0)
        !           180:        + (opcode < TME_RECODE_OPCODE_SUB
        !           181:          ? TME_RECODE_X86_MOD_OPREG_RM(0, TME_RECODE_X86_OPCODE_GRP3_NOT)
        !           182:          : TME_RECODE_X86_MOD_OPREG_RM(0, TME_RECODE_X86_OPCODE_GRP3_NEG)));
        !           183:   thunk_bytes += (TME_RECODE_SIZE_HOST > TME_RECODE_SIZE_32) + 2;
        !           184: 
        !           185:   /* if this is a double-host-size operation: */
        !           186:   if (TME_RECODE_SIZE_IS_DOUBLE_HOST(insn->tme_recode_insn_size)) {
        !           187: 
        !           188:     /* get the second x86 register to negate: */
        !           189:     reg_x86 = tme_recode_x86_reg_from_host[insn->tme_recode_insn_operand_dst + 1];
        !           190: 
        !           191:     /* if this is a two's complement negation: */
        !           192:     if (opcode == TME_RECODE_OPCODE_SUB) {
        !           193: 
        !           194:       /* emit an adc $0, %reg: */
        !           195:       if (TME_RECODE_SIZE_HOST > TME_RECODE_SIZE_32) {
        !           196:        thunk_bytes[0] = TME_RECODE_X86_REX_B(TME_RECODE_SIZE_HOST, reg_x86);
        !           197:       }
        !           198:       thunk_bytes[(TME_RECODE_SIZE_HOST > TME_RECODE_SIZE_32) + 0] = TME_RECODE_X86_OPCODE_GRP1_Ib_Ev;
        !           199:       thunk_bytes[(TME_RECODE_SIZE_HOST > TME_RECODE_SIZE_32) + 1]
        !           200:        = TME_RECODE_X86_MOD_OPREG_RM(TME_RECODE_X86_MOD_RM_REG(reg_x86),
        !           201:                                      TME_RECODE_X86_OPCODE_GRP1_BINOP(TME_RECODE_X86_OPCODE_BINOP_ADC));
        !           202:       thunk_bytes[(TME_RECODE_SIZE_HOST > TME_RECODE_SIZE_32) + 2] = 0;
        !           203:       thunk_bytes += (TME_RECODE_SIZE_HOST > TME_RECODE_SIZE_32) + 3;
        !           204:     }
        !           205: 
        !           206:     /* emit a not %reg or a neg %reg: */
        !           207:     if (TME_RECODE_SIZE_HOST > TME_RECODE_SIZE_32) {
        !           208:       thunk_bytes[0] = TME_RECODE_X86_REX_B(TME_RECODE_SIZE_HOST, reg_x86);
        !           209:     }
        !           210:     thunk_bytes[(TME_RECODE_SIZE_HOST > TME_RECODE_SIZE_32) + 0] = TME_RECODE_X86_OPCODE_GRP3_Ev;
        !           211: #if TME_RECODE_OPCODE_SUB <= TME_RECODE_OPCODE_ORN || TME_RECODE_OPCODE_SUB <= TME_RECODE_OPCODE_XORN
        !           212: #error "TME_RECODE_OPCODE_ values changed"
        !           213: #endif
        !           214:     thunk_bytes[(TME_RECODE_SIZE_HOST > TME_RECODE_SIZE_32) + 1]
        !           215:       = (TME_RECODE_X86_MOD_OPREG_RM(TME_RECODE_X86_MOD_RM_REG(reg_x86), 0)
        !           216:         + (opcode < TME_RECODE_OPCODE_SUB
        !           217:            ? TME_RECODE_X86_MOD_OPREG_RM(0, TME_RECODE_X86_OPCODE_GRP3_NOT)
        !           218:            : TME_RECODE_X86_MOD_OPREG_RM(0, TME_RECODE_X86_OPCODE_GRP3_NEG)));
        !           219:     thunk_bytes += (TME_RECODE_SIZE_HOST > TME_RECODE_SIZE_32) + 2;
        !           220:   }
        !           221: 
        !           222:   /* finish these instructions: */
        !           223:   tme_recode_x86_insns_finish(ic, thunk_bytes);
        !           224: }
        !           225: 
        !           226: /* this emits a host-sized binary operation instruction with an
        !           227:    immediate right source operand: */
        !           228: static void
        !           229: _tme_recode_x86_insn_binop_imm(struct tme_recode_ic *ic,
        !           230:                               const struct tme_recode_insn *insn)
        !           231: {
        !           232:   tme_uint8_t *thunk_bytes;
        !           233:   unsigned int reg_x86_dst;
        !           234:   tme_int32_t imm_32;
        !           235:   unsigned int imm_size;
        !           236: 
        !           237:   /* start another instruction: */
        !           238:   tme_recode_x86_insns_start(ic, thunk_bytes);
        !           239: 
        !           240:   /* get the x86 destination register: */
        !           241:   reg_x86_dst = tme_recode_x86_reg_from_host[insn->tme_recode_insn_operand_dst];
        !           242: 
        !           243:   /* emit any rex prefix: */
        !           244:   if (TME_RECODE_SIZE_HOST > TME_RECODE_SIZE_32) {
        !           245:     thunk_bytes[0] = TME_RECODE_X86_REX_B(TME_RECODE_SIZE_HOST, reg_x86_dst);
        !           246:   }
        !           247: 
        !           248:   /* emit the modR/M byte for this instruction: */
        !           249:   thunk_bytes[(TME_RECODE_SIZE_HOST > TME_RECODE_SIZE_32) + 1]
        !           250:     = TME_RECODE_X86_MOD_OPREG_RM(TME_RECODE_X86_MOD_RM_REG(reg_x86_dst),
        !           251:                                  TME_RECODE_X86_OPCODE_GRP1_BINOP(TME_RECODE_X86_OPCODE_TO_BINOP(insn->tme_recode_insn_opcode)));
        !           252: 
        !           253:   /* emit the immediate for this instruction as a sign-extended 32 bits: */
        !           254:   imm_32 = insn->tme_recode_insn_imm_guest;
        !           255:   *((tme_int32_t *) &thunk_bytes[(TME_RECODE_SIZE_HOST > TME_RECODE_SIZE_32) + 2]) = imm_32;
        !           256: 
        !           257:   /* see if this sign-extended 32-bit immediate can be a sign-extended
        !           258:      eight bits instead: */
        !           259:   imm_size
        !           260:     = (imm_32 == (tme_int8_t) imm_32
        !           261:        ? sizeof(tme_int8_t)
        !           262:        : sizeof(tme_int32_t));
        !           263: 
        !           264:   /* emit the opcode for this instruction: */
        !           265:   /* NB: we take advantage of the fact that the group one opcodes for
        !           266:      an eight-bit immediate and a 32-bit immediate are separated by a
        !           267:      distance of two, which is sizeof(tme_int32_t) / 2: */
        !           268: #if (TME_RECODE_X86_OPCODE_GRP1_Ib_Ev - TME_RECODE_X86_OPCODE_GRP1_Iz_Ev) != 2
        !           269: #error "TME_RECODE_X86_OPCODE_GRP1_ opcodes changed"
        !           270: #endif
        !           271:   thunk_bytes[(TME_RECODE_SIZE_HOST > TME_RECODE_SIZE_32) + 0]
        !           272:     = (TME_RECODE_X86_OPCODE_GRP1_Ib_Ev
        !           273:        - (imm_size / 2));
        !           274: 
        !           275:   /* finish this instruction: */
        !           276:   thunk_bytes += (TME_RECODE_SIZE_HOST > TME_RECODE_SIZE_32) + 2 + imm_size;
        !           277:   tme_recode_x86_insns_finish(ic, thunk_bytes);
        !           278: }
        !           279: 
        !           280: /* this emits a host-sized binary operation instruction with a
        !           281:    register right source operand: */
        !           282: static void
        !           283: _tme_recode_x86_insn_binop(struct tme_recode_ic *ic,
        !           284:                           const struct tme_recode_insn *insn)
        !           285: {
        !           286:   tme_uint8_t *thunk_bytes;
        !           287:   unsigned int reg_x86_src;
        !           288:   unsigned int reg_x86_dst;
        !           289: 
        !           290:   /* start another instruction: */
        !           291:   tme_recode_x86_insns_start(ic, thunk_bytes);
        !           292: 
        !           293:   /* get the x86 source and destination registers: */
        !           294:   reg_x86_src = tme_recode_x86_reg_from_host[insn->tme_recode_insn_operand_src[1]];
        !           295:   reg_x86_dst = tme_recode_x86_reg_from_host[insn->tme_recode_insn_operand_dst];
        !           296: 
        !           297:   /* emit any rex prefix: */
        !           298:   if (TME_RECODE_SIZE_HOST > TME_RECODE_SIZE_32) {
        !           299:     thunk_bytes[0]
        !           300:       = (TME_RECODE_X86_REX_R(TME_RECODE_SIZE_HOST, reg_x86_src)
        !           301:         | TME_RECODE_X86_REX_B(TME_RECODE_SIZE_HOST, reg_x86_dst));
        !           302:   }
        !           303: 
        !           304:   /* emit the opcode for this instruction: */
        !           305:   thunk_bytes[(TME_RECODE_SIZE_HOST > TME_RECODE_SIZE_32) + 0]
        !           306:     = (TME_RECODE_X86_OPCODE_TO_BINOP(insn->tme_recode_insn_opcode)
        !           307:        + TME_RECODE_X86_OPCODE_BINOP_Gv_Ev);
        !           308: 
        !           309:   /* emit the modR/M byte for this instruction: */
        !           310:   thunk_bytes[(TME_RECODE_SIZE_HOST > TME_RECODE_SIZE_32) + 1]
        !           311:     = TME_RECODE_X86_MOD_OPREG_RM(TME_RECODE_X86_MOD_RM_REG(reg_x86_dst),
        !           312:                                  TME_RECODE_X86_REG(reg_x86_src));
        !           313: 
        !           314:   /* finish this instruction: */
        !           315:   thunk_bytes += (TME_RECODE_SIZE_HOST > TME_RECODE_SIZE_32) + 2;
        !           316:   tme_recode_x86_insns_finish(ic, thunk_bytes);
        !           317: }
        !           318: 
        !           319: /* this emits a host-sized zero-extend or sign-extend instruction: */
        !           320: static void
        !           321: _tme_recode_x86_insn_ext(struct tme_recode_ic *ic,
        !           322:                         const struct tme_recode_insn *insn)
        !           323: {
        !           324:   tme_uint8_t *thunk_bytes;
        !           325:   unsigned int reg_x86_src;
        !           326:   unsigned int reg_x86_dst;
        !           327:   unsigned int extend;
        !           328:   unsigned int rex;
        !           329:   unsigned int mod_reg_rm;
        !           330: 
        !           331:   /* start another instruction: */
        !           332:   tme_recode_x86_insns_start(ic, thunk_bytes);
        !           333: 
        !           334:   /* get the x86 source and destination registers: */
        !           335:   reg_x86_src = tme_recode_x86_reg_from_host[insn->tme_recode_insn_operand_src[0]];
        !           336:   reg_x86_dst = tme_recode_x86_reg_from_host[insn->tme_recode_insn_operand_dst];
        !           337: 
        !           338:   /* get the size of the source operand: */
        !           339:   extend = insn->tme_recode_insn_operand_src[1];
        !           340:   assert (extend >= TME_RECODE_SIZE_8
        !           341:          && extend < ic->tme_recode_ic_reg_size);
        !           342: 
        !           343:   /* assume that this is not an extension from eight bits, and start
        !           344:      any rex prefix with the source register: */
        !           345:   rex = TME_RECODE_X86_REX_B(0, reg_x86_src);
        !           346: 
        !           347:   /* if this is an extension from eight bits: */
        !           348:   if (extend == TME_RECODE_SIZE_8) {
        !           349: 
        !           350:     /* if this is an ia32 host, and the source register has no
        !           351:        eight-bit register encoding: */
        !           352:     if (TME_RECODE_SIZE_HOST == TME_RECODE_SIZE_32
        !           353:        && reg_x86_src >= TME_RECODE_X86_REG_SP) {
        !           354: 
        !           355:       /* we destroy the c register: */
        !           356:       ic->tme_recode_x86_ic_thunks_reg_guest_window_c = TME_RECODE_REG_GUEST_WINDOW_UNDEF;
        !           357: 
        !           358:       /* copy the source register into the scratch c register: */
        !           359:       _tme_recode_x86_emit_reg_copy(thunk_bytes, reg_x86_src, TME_RECODE_X86_REG_C);
        !           360: 
        !           361:       /* the source register is now the scratch c register: */
        !           362:       reg_x86_src = TME_RECODE_X86_REG_C;
        !           363:     }
        !           364: 
        !           365:     /* restart any rex prefix: */
        !           366:     rex = TME_RECODE_X86_REX_B(TME_RECODE_SIZE_8, reg_x86_src);
        !           367:   }
        !           368: 
        !           369:   /* add the destination register to any rex prefix: */
        !           370:   rex |= TME_RECODE_X86_REX_R(0, reg_x86_dst);
        !           371: 
        !           372:   /* make the modR/M byte for this instruction, shifted up to the
        !           373:      second byte to make room for the opcode: */
        !           374:   mod_reg_rm
        !           375:     = (TME_RECODE_X86_MOD_OPREG_RM(TME_RECODE_X86_MOD_RM_REG(reg_x86_src),
        !           376:                                   TME_RECODE_X86_REG(reg_x86_dst))
        !           377:        << 8);
        !           378: 
        !           379:   /* if this is an x86-64 host: */
        !           380:   if (TME_RECODE_SIZE_HOST > TME_RECODE_SIZE_32) {
        !           381: 
        !           382:     /* if this is a sign-extension: */
        !           383:     if (insn->tme_recode_insn_opcode == TME_RECODE_OPCODE_EXTS) {
        !           384: 
        !           385:       /* sign extension is a 64-bit operation: */
        !           386:       rex |= TME_RECODE_X86_REX_W(TME_RECODE_SIZE_HOST);
        !           387:     }
        !           388: 
        !           389:     /* emit any rex prefix: */
        !           390:     thunk_bytes[0] = rex;
        !           391:     thunk_bytes += (rex != 0);
        !           392:   }
        !           393: 
        !           394:   /* if this is an x86-64 host and this is an extension from 32
        !           395:      bits: */
        !           396:   if (TME_RECODE_SIZE_HOST > TME_RECODE_SIZE_32
        !           397:       && extend == TME_RECODE_SIZE_32) {
        !           398: 
        !           399:     /* emit either a movl %src, %dst or a movslq %src, %dst: */
        !           400:     *((tme_uint16_t *) &thunk_bytes[0])
        !           401:       = ((insn->tme_recode_insn_opcode == TME_RECODE_OPCODE_EXTS
        !           402:          ? TME_RECODE_X86_OPCODE_MOVS_El_Gv
        !           403:          : (TME_RECODE_X86_OPCODE_BINOP_MOV
        !           404:             + TME_RECODE_X86_OPCODE_BINOP_Ev_Gv))
        !           405:         + mod_reg_rm);
        !           406:     thunk_bytes += 2;
        !           407:   }
        !           408: 
        !           409:   /* otherwise, either this is not an x86-64 host or this is an
        !           410:      extension from 16 or 8 bits: */
        !           411:   else {
        !           412:     assert (extend == TME_RECODE_SIZE_16
        !           413:            || extend == TME_RECODE_SIZE_8);
        !           414: 
        !           415:     /* emit the two-byte opcode escape: */
        !           416:     thunk_bytes[0] = TME_RECODE_X86_OPCODE_ESC_0F;
        !           417: 
        !           418:     /* emit either a movzb %src, %dst, or a movzw %src, %dst, or a
        !           419:        movsb %src %dst, or a movsw %src, %dst: */
        !           420: #if (TME_RECODE_X86_OPCODE0F_MOVS_Eb_Gv ^ TME_RECODE_X86_OPCODE0F_MOVZ_Eb_Gv) != (TME_RECODE_X86_OPCODE0F_MOVS_Ew_Gv ^ TME_RECODE_X86_OPCODE0F_MOVZ_Ew_Gv)
        !           421: #error "TME_RECODE_X86_OPCODE0F_MOV values changed"
        !           422: #endif
        !           423: #if (TME_RECODE_OPCODE_EXTS - TME_RECODE_OPCODE_EXTZ) != 1
        !           424: #error "TME_RECODE_OPCODE_ values changed"
        !           425: #endif
        !           426: #if (TME_RECODE_X86_OPCODE0F_MOVZ_Ew_Gv ^ TME_RECODE_X86_OPCODE0F_MOVZ_Eb_Gv) != (TME_RECODE_X86_OPCODE0F_MOVS_Ew_Gv ^ TME_RECODE_X86_OPCODE0F_MOVS_Eb_Gv)
        !           427: #error "TME_RECODE_X86_OPCODE0F_MOV values changed"
        !           428: #endif
        !           429: #if (TME_RECODE_SIZE_16 - TME_RECODE_SIZE_8) != 1
        !           430: #error "TME_RECODE_SIZE_ values changed"
        !           431: #endif
        !           432:     *((tme_uint16_t *) &thunk_bytes[1])
        !           433:       = ((TME_RECODE_X86_OPCODE0F_MOVZ_Eb_Gv
        !           434:          + ((extend
        !           435:              - TME_RECODE_SIZE_8)
        !           436:             * (TME_RECODE_X86_OPCODE0F_MOVZ_Ew_Gv
        !           437:                - TME_RECODE_X86_OPCODE0F_MOVZ_Eb_Gv))
        !           438:          + ((insn->tme_recode_insn_opcode
        !           439:              - TME_RECODE_OPCODE_EXTZ)
        !           440:             * (TME_RECODE_X86_OPCODE0F_MOVS_Eb_Gv
        !           441:                - TME_RECODE_X86_OPCODE0F_MOVZ_Eb_Gv)))
        !           442:         + mod_reg_rm);
        !           443:     thunk_bytes += 3;
        !           444:   }
        !           445: 
        !           446:   /* finish this instruction: */
        !           447:   tme_recode_x86_insns_finish(ic, thunk_bytes);
        !           448: }
        !           449: 
        !           450: /* this emits a host-sized shift-by-immediate instruction: */
        !           451: static void
        !           452: _tme_recode_x86_insn_shift_imm(struct tme_recode_ic *ic,
        !           453:                               const struct tme_recode_insn *insn)
        !           454: {
        !           455:   tme_uint8_t *thunk_bytes;
        !           456:   unsigned int reg_x86_dst;
        !           457: 
        !           458:   /* start another instruction: */
        !           459:   tme_recode_x86_insns_start(ic, thunk_bytes);
        !           460: 
        !           461:   /* get the x86 destination register: */
        !           462:   reg_x86_dst = tme_recode_x86_reg_from_host[insn->tme_recode_insn_operand_dst];
        !           463: 
        !           464:   /* emit any rex prefix: */
        !           465:   if (TME_RECODE_SIZE_HOST > TME_RECODE_SIZE_32) {
        !           466:     thunk_bytes[0] = TME_RECODE_X86_REX_B(TME_RECODE_SIZE_HOST, reg_x86_dst);
        !           467:   }
        !           468: 
        !           469:   /* emit the opcode for this instruction: */
        !           470:   thunk_bytes[(TME_RECODE_SIZE_HOST > TME_RECODE_SIZE_32) + 0] = TME_RECODE_X86_OPCODE_GRP2_Ib_Ev;
        !           471: 
        !           472:   /* emit the modR/M byte for this instruction: */
        !           473:   thunk_bytes[(TME_RECODE_SIZE_HOST > TME_RECODE_SIZE_32) + 1]
        !           474:     = TME_RECODE_X86_MOD_OPREG_RM(TME_RECODE_X86_MOD_RM_REG(reg_x86_dst),
        !           475:                                  TME_RECODE_X86_OPCODE_TO_GRP2(insn->tme_recode_insn_opcode));
        !           476: 
        !           477:   /* emit the immediate shift count for this instruction: */
        !           478:   thunk_bytes[(TME_RECODE_SIZE_HOST > TME_RECODE_SIZE_32) + 2] = insn->tme_recode_insn_imm_uguest;
        !           479: 
        !           480:   /* finish this instruction: */
        !           481:   thunk_bytes += (TME_RECODE_SIZE_HOST > TME_RECODE_SIZE_32) + 3;
        !           482:   tme_recode_x86_insns_finish(ic, thunk_bytes);
        !           483: }
        !           484: 
        !           485: /* this emits a guest instruction: */
        !           486: static void
        !           487: _tme_recode_x86_insn_guest(struct tme_recode_ic *ic,
        !           488:                           struct tme_recode_insn *insn)
        !           489: {
        !           490:   tme_uint8_t *thunk_bytes;
        !           491:   unsigned long reg_host_free;
        !           492: 
        !           493:   /* since a guest function may fault and never return, we have to
        !           494:      clean all dirty host registers so that all guest registers are
        !           495:      correct in the guest ic state at the time of the fault: */
        !           496:   tme_recode_regs_host_clean_all(ic);
        !           497: 
        !           498:   /* a guest function that takes an undefined first source operand,
        !           499:      must take an undefined second source operand: */
        !           500:   assert (insn->tme_recode_insn_operand_src[0] != TME_RECODE_OPERAND_UNDEF
        !           501:          || insn->tme_recode_insn_operand_src[1] == TME_RECODE_OPERAND_UNDEF);
        !           502: 
        !           503:   /* if this is an ia32 host: */
        !           504:   if (TME_RECODE_SIZE_HOST == TME_RECODE_SIZE_32) {
        !           505: 
        !           506:     /* if this guest function takes an undefined second source operand: */
        !           507:     if (insn->tme_recode_insn_operand_src[1] == TME_RECODE_OPERAND_UNDEF) {
        !           508: 
        !           509:       /* if this guest function returns a register value, we need to
        !           510:         reserve the host register(s) for the return value, since we
        !           511:         won't reserve by loading the second source operand: */
        !           512:       if (insn->tme_recode_insn_operand_dst != TME_RECODE_OPERAND_NULL) {
        !           513:        tme_recode_regs_host_reserve(ic,
        !           514:                                     (TME_RECODE_SIZE_IS_DOUBLE_HOST(ic->tme_recode_ic_reg_size)
        !           515:                                      ? (TME_RECODE_X86_REG_HOST_FREE_CALL - 1)
        !           516:                                      : TME_RECODE_X86_REG_HOST_FREE_CALL));
        !           517:       }
        !           518:     }
        !           519: 
        !           520:     /* otherwise, this guest function takes a defined second source operand: */
        !           521:     else {
        !           522: 
        !           523:       /* if this is a double-host-size guest, load the second source
        !           524:         operand into %eax:%ebp, otherwise load it into %eax: */
        !           525: #if (TME_RECODE_X86_REG_HOST_FREE_CALL & 1) != 1
        !           526: #error "TME_RECODE_X86_REG_HOST_ values changed"
        !           527: #endif
        !           528:       tme_recode_regs_src_specific(ic,
        !           529:                                   insn,
        !           530:                                   insn->tme_recode_insn_operand_src[1],
        !           531:                                   (TME_RECODE_SIZE_IS_DOUBLE_HOST(ic->tme_recode_ic_reg_size)
        !           532:                                    ? (TME_RECODE_X86_REG_HOST_FREE_CALL - 1)
        !           533:                                    : TME_RECODE_X86_REG_HOST_FREE_CALL));
        !           534:     }
        !           535: 
        !           536:     /* if this guest function takes a defined first source operand: */
        !           537:     if (insn->tme_recode_insn_operand_src[0] != TME_RECODE_OPERAND_UNDEF) {
        !           538: 
        !           539:       /* load the first source operand into any host register: */
        !           540:       insn->tme_recode_insn_operand_src[0]
        !           541:        = tme_recode_regs_src_any(ic,
        !           542:                                  insn,
        !           543:                                  insn->tme_recode_insn_operand_src[0]);
        !           544:     }
        !           545: 
        !           546:     /* if this guest function returns a register value: */
        !           547:     if (insn->tme_recode_insn_operand_dst != TME_RECODE_OPERAND_NULL) {
        !           548: 
        !           549:       /* if this is a double-host-size guest, force the destination
        !           550:         operand to be in %eax:%ebp, otherwise force it to be directly
        !           551:         in the return value register (%eax): */
        !           552:       tme_recode_regs_dst_specific(ic,
        !           553:                                   insn,
        !           554:                                   0);
        !           555: 
        !           556:       /* free all host registers that be will destroyed by the call,
        !           557:         except for the first one, which we are using for the
        !           558:         destination operand: */
        !           559:       reg_host_free = TME_RECODE_X86_REG_HOST_FREE_CALL + 1;
        !           560:     }
        !           561: 
        !           562:     /* otherwise, this guest function does not return a register value: */
        !           563:     else {
        !           564: 
        !           565:       /* we must assume that this guest function modifies all guest
        !           566:         registers: */
        !           567: 
        !           568:       /* unreserve all registers: */
        !           569:       tme_recode_regs_host_unreserve_all(ic);
        !           570: 
        !           571:       /* free all host registers: */
        !           572:       reg_host_free = TME_RECODE_REG_HOST(0);
        !           573:     }
        !           574: 
        !           575:     /* free host registers: */
        !           576:     tme_recode_regs_host_free_many(ic, reg_host_free);
        !           577: 
        !           578:     /* the guest function call destroys the c register: */
        !           579:     ic->tme_recode_x86_ic_thunks_reg_guest_window_c = TME_RECODE_REG_GUEST_WINDOW_UNDEF;
        !           580: 
        !           581:     /* start more instructions: */
        !           582:     tme_recode_x86_insns_start(ic, thunk_bytes);
        !           583: 
        !           584:     /* if this guest function takes two undefined source operands: */
        !           585:     if (insn->tme_recode_insn_operand_src[0] == TME_RECODE_OPERAND_UNDEF) {
        !           586: 
        !           587:       /* push garbage source operands for the guest function: */
        !           588:       thunk_bytes = _tme_recode_x86_emit_adjust_sp(thunk_bytes, - (int) (sizeof(tme_recode_uguest_t) * 2));
        !           589:     }
        !           590: 
        !           591:     /* otherwise, this guest function takes at least one defined
        !           592:        source operand: */
        !           593:     else {
        !           594: 
        !           595:       /* push the second source operand for the guest function.  NB that
        !           596:         if double-host-size guests are supported, but this isn't a
        !           597:         double-host-size guest, we use a garbage word on the stack as
        !           598:         the most-significant half of that argument (which is okay since
        !           599:         the guest functions are supposed to truncate their arguments to
        !           600:         the expected size): */
        !           601:       if (TME_RECODE_SIZE_IS_DOUBLE_HOST(ic->tme_recode_ic_reg_size)) {
        !           602:        _tme_recode_x86_emit_reg_push(thunk_bytes, TME_RECODE_X86_REG_A);
        !           603:        _tme_recode_x86_emit_reg_push(thunk_bytes, TME_RECODE_X86_REG_BP);
        !           604:       }
        !           605:       else {
        !           606:        if (TME_RECODE_SIZE_GUEST_MAX > TME_RECODE_SIZE_HOST) {
        !           607:          thunk_bytes = _tme_recode_x86_emit_adjust_sp(thunk_bytes, - (int) (sizeof(tme_recode_uguest_t) / 2));
        !           608:        }
        !           609:        _tme_recode_x86_emit_reg_push(thunk_bytes, TME_RECODE_X86_REG_A);
        !           610:       }
        !           611: 
        !           612:       /* push the first source operand for the guest function.  if
        !           613:         double-host size guests are supported, assume that this is a
        !           614:         double-host-size guest and push the most-significant half of
        !           615:         the argument first.  if this isn't a double-host-size guest,
        !           616:         this will push some unknown register (which is okay since the
        !           617:         guest functions are supposed to truncate their arguments to the
        !           618:         expected size): */
        !           619:       if (TME_RECODE_SIZE_GUEST_MAX > TME_RECODE_SIZE_HOST) {
        !           620:        _tme_recode_x86_emit_reg_push(thunk_bytes,
        !           621:                                      tme_recode_x86_reg_from_host[insn->tme_recode_insn_operand_src[0] + 1]);
        !           622:       }
        !           623:       _tme_recode_x86_emit_reg_push(thunk_bytes,
        !           624:                                    tme_recode_x86_reg_from_host[insn->tme_recode_insn_operand_src[0]]);
        !           625:     }
        !           626: 
        !           627:     /* emit the instruction to push the struct tme_ic * argument for
        !           628:        the guest function, and then the call instruction to the guest
        !           629:        function: */
        !           630:     *((tme_uint16_t *) thunk_bytes)
        !           631:       = (TME_RECODE_X86_OPCODE_PUSH_Gv(TME_RECODE_X86_REG_IC)
        !           632:         + (TME_RECODE_X86_OPCODE_CALL_RELz << 8));
        !           633:     thunk_bytes += 2 + sizeof(tme_uint32_t);
        !           634:     ((tme_int32_t *) thunk_bytes)[-1]
        !           635:       = (tme_recode_function_to_thunk_off(ic, insn->tme_recode_insn_guest_func)
        !           636:         - tme_recode_build_to_thunk_off(ic, thunk_bytes));
        !           637: 
        !           638:     /* remove the guest function arguments from the stack: */
        !           639:     thunk_bytes 
        !           640:       = _tme_recode_x86_emit_adjust_sp(thunk_bytes,
        !           641:                                       (sizeof(struct tme_ic *)
        !           642:                                        + sizeof(tme_recode_uguest_t)
        !           643:                                        + sizeof(tme_recode_uguest_t)));
        !           644:   }
        !           645: 
        !           646:   /* otherwise, this is an x86-64 host: */
        !           647:   else {
        !           648: 
        !           649:     /* if this is a double-host-size guest: */
        !           650:     if (TME_RECODE_SIZE_IS_DOUBLE_HOST(ic->tme_recode_ic_reg_size)) {
        !           651: 
        !           652:       /* if this guest function takes an undefined first source operand: */
        !           653:       if (insn->tme_recode_insn_operand_src[0] == TME_RECODE_OPERAND_UNDEF) {
        !           654: 
        !           655:        /* if this guest function returns a register value, we need to
        !           656:           reserve the host register(s) for the return value, since we
        !           657:           won't reserve it by loading the first source operand: */
        !           658:        if (insn->tme_recode_insn_operand_dst != TME_RECODE_OPERAND_NULL) {
        !           659:          tme_recode_regs_host_reserve(ic,
        !           660:                                       TME_RECODE_X86_REG_HOST_FREE_CALL - 1);
        !           661:        }
        !           662:       }
        !           663: 
        !           664:       /* otherwise, this guest function takes a defined first source operand: */
        !           665:       else {
        !           666: 
        !           667:        /* load the first source operand into the first host register
        !           668:           pair that is destroyed by the call: */
        !           669: #if (TME_RECODE_X86_REG_HOST_FREE_CALL & 1) != 1
        !           670: #error "TME_RECODE_X86_REG_HOST_ values changed"
        !           671: #endif
        !           672:        tme_recode_regs_src_specific(ic,
        !           673:                                     insn,
        !           674:                                     insn->tme_recode_insn_operand_src[0],
        !           675:                                     TME_RECODE_X86_REG_HOST_FREE_CALL - 1);
        !           676:       }
        !           677: 
        !           678:       /* if this guest function takes a defined second source operand: */
        !           679:       if (insn->tme_recode_insn_operand_src[1] != TME_RECODE_OPERAND_UNDEF) {
        !           680: 
        !           681:        /* load the second source operand into the second host register
        !           682:           pair that is destroyed by the call: */
        !           683:        tme_recode_regs_src_specific(ic,
        !           684:                                     insn,
        !           685:                                     insn->tme_recode_insn_operand_src[1],
        !           686:                                     TME_RECODE_X86_REG_HOST_FREE_CALL + 1);
        !           687:       }
        !           688: 
        !           689:       /* if this guest function returns a register value: */
        !           690:       if (insn->tme_recode_insn_operand_dst != TME_RECODE_OPERAND_NULL) {
        !           691: 
        !           692:        /* force the destination operand to be in %rax:%rbp: */
        !           693:        tme_recode_regs_dst_specific(ic,
        !           694:                                     insn,
        !           695:                                     0);
        !           696: 
        !           697:        /* free all host registers that be will destroyed by the call,
        !           698:           except for the first one, which we are using for the
        !           699:           destination operand: */
        !           700:        reg_host_free = TME_RECODE_X86_REG_HOST_FREE_CALL + 1;
        !           701:       }
        !           702: 
        !           703:       /* otherwise, this guest function does not return a register value: */
        !           704:       else {
        !           705: 
        !           706:        /* unreserve all registers: */
        !           707:        tme_recode_regs_host_unreserve_all(ic);
        !           708: 
        !           709:        /* free all host registers: */
        !           710:        reg_host_free = TME_RECODE_REG_HOST(0);
        !           711:       }
        !           712: 
        !           713:       /* free host registers: */
        !           714:       tme_recode_regs_host_free_many(ic, reg_host_free);
        !           715: 
        !           716:       /* the guest function call destroys the c register: */
        !           717:       ic->tme_recode_x86_ic_thunks_reg_guest_window_c = TME_RECODE_REG_GUEST_WINDOW_UNDEF;
        !           718: 
        !           719:       /* start more instructions: */
        !           720:       tme_recode_x86_insns_start(ic, thunk_bytes);
        !           721: 
        !           722:       /* move the first and second source operands into the function
        !           723:         argument registers: */
        !           724:       if (insn->tme_recode_insn_operand_src[0] != TME_RECODE_OPERAND_UNDEF) {
        !           725:        _tme_recode_x86_emit_reg_copy(thunk_bytes, TME_RECODE_X86_REG_BP, TME_RECODE_X86_REG_SI);
        !           726:        _tme_recode_x86_emit_reg_copy(thunk_bytes, TME_RECODE_X86_REG_A, TME_RECODE_X86_REG_D);
        !           727:       }
        !           728:       if (insn->tme_recode_insn_operand_src[1] != TME_RECODE_OPERAND_UNDEF) {
        !           729:        _tme_recode_x86_emit_reg_copy(thunk_bytes, TME_RECODE_X86_REG_N(10), TME_RECODE_X86_REG_C);
        !           730:        _tme_recode_x86_emit_reg_copy(thunk_bytes, TME_RECODE_X86_REG_N(11), TME_RECODE_X86_REG_N(8));
        !           731:       }
        !           732:     }
        !           733: 
        !           734:     /* otherwise, this is not a double-host-size guest: */
        !           735:     else {
        !           736: 
        !           737:       /* if this guest function takes a defined first source operand: */
        !           738:       if (insn->tme_recode_insn_operand_src[0] != TME_RECODE_OPERAND_UNDEF) {
        !           739: 
        !           740:        /* load the first source operand directly into its (first) argument
        !           741:           register (%rsi): */
        !           742:        tme_recode_regs_src_specific(ic,
        !           743:                                     insn,
        !           744:                                     insn->tme_recode_insn_operand_src[0],
        !           745:                                     TME_RECODE_X86_REG_HOST_ARG(1));
        !           746:       }
        !           747: 
        !           748:       /* if this guest function takes a defined second source operand: */
        !           749:       if (insn->tme_recode_insn_operand_src[1] != TME_RECODE_OPERAND_UNDEF) {
        !           750: 
        !           751:        /* load the second source operand directly into %rdx.  if
        !           752:           double-host-size guests are not supported, this is the
        !           753:           correct argument register for the second source operand.
        !           754:           otherwise, double-host-size guests are supported, and %rdx is
        !           755:           the most-significant of the two first source operand argument
        !           756:           registers.  in that case, below we'll copy %rdx into the
        !           757:           first argument register for the second source operand, but
        !           758:           otherwise we'll leave %rdx loaded, which is okay since the
        !           759:           guest functions are supposed to truncate their arguments to
        !           760:           the expected size: */
        !           761:        tme_recode_regs_src_specific(ic,
        !           762:                                     insn,
        !           763:                                     insn->tme_recode_insn_operand_src[1],
        !           764:                                     TME_RECODE_X86_REG_HOST_ARG(2));
        !           765:       }
        !           766: 
        !           767:       /* if this guest function returns a register value: */
        !           768:       if (insn->tme_recode_insn_operand_dst != TME_RECODE_OPERAND_NULL) {
        !           769: 
        !           770:        /* force the destination operand to be directly in the return
        !           771:           value register (%rax): */
        !           772:        tme_recode_regs_host_reserve(ic,
        !           773:                                     TME_RECODE_X86_REG_HOST_FREE_CALL);
        !           774:        tme_recode_regs_dst_specific(ic,
        !           775:                                     insn,
        !           776:                                     2);
        !           777: 
        !           778:        /* free all host registers that will be destroyed by the call,
        !           779:           except for the first one, which we are using for the
        !           780:           destination operand: */
        !           781:        reg_host_free = TME_RECODE_X86_REG_HOST_FREE_CALL + 1;
        !           782:       }
        !           783: 
        !           784:       /* otherwise, this guest function does not return a register value: */
        !           785:       else {
        !           786: 
        !           787:        /* unreserve all registers: */
        !           788:        tme_recode_regs_host_unreserve_all(ic);
        !           789: 
        !           790:        /* free all host registers: */
        !           791:        reg_host_free = TME_RECODE_REG_HOST(0);
        !           792:       }
        !           793: 
        !           794:       /* free host registers: */
        !           795:       tme_recode_regs_host_free_many(ic, reg_host_free);
        !           796: 
        !           797:       /* the guest function call destroys the c register: */
        !           798:       ic->tme_recode_x86_ic_thunks_reg_guest_window_c = TME_RECODE_REG_GUEST_WINDOW_UNDEF;
        !           799: 
        !           800:       /* start more instructions: */
        !           801:       tme_recode_x86_insns_start(ic, thunk_bytes);
        !           802: 
        !           803:       /* if double-host-size guests are supported: */
        !           804:       if (TME_RECODE_SIZE_GUEST_MAX > TME_RECODE_SIZE_HOST) {
        !           805: 
        !           806:        /* if this guest function takes a defined second source operand: */
        !           807:        if (insn->tme_recode_insn_operand_src[1] != TME_RECODE_OPERAND_UNDEF) {
        !           808: 
        !           809:          /* copy the second source operand from %rdx into the second
        !           810:             source operand function argument registers.  NB that this
        !           811:             leaves the most-significant half of the argument (%r8)
        !           812:             undefined, which is okay since the guest functions are
        !           813:             supposed to truncate their arguments to the expected
        !           814:             size: */
        !           815:          _tme_recode_x86_emit_reg_copy(thunk_bytes, TME_RECODE_X86_REG_D, TME_RECODE_X86_REG_C);
        !           816:        }
        !           817:       }
        !           818:     }
        !           819: 
        !           820:     /* emit a movq %ic, %rdi: */
        !           821:     _tme_recode_x86_emit_reg_copy(thunk_bytes, TME_RECODE_X86_REG_IC, TME_RECODE_X86_REG_DI);
        !           822: 
        !           823:     /* we must assume that we can't reach the guest function from the
        !           824:        instruction thunk with a 32-bit displacement.  emit a direct
        !           825:        call to the guest function using %rax: */
        !           826:     *((tme_uint16_t *) thunk_bytes)
        !           827:       = (TME_RECODE_X86_REX_B(TME_RECODE_SIZE_HOST, TME_RECODE_X86_REG_A)
        !           828:         + (TME_RECODE_X86_OPCODE_MOV_Iv_Gv(TME_RECODE_X86_REG_A)
        !           829:            << 8));
        !           830:     memcpy(thunk_bytes + 2,
        !           831:           &insn->tme_recode_insn_guest_func,
        !           832:           TME_BIT(TME_RECODE_SIZE_HOST - TME_RECODE_SIZE_8));
        !           833:     thunk_bytes += 2 + TME_BIT(TME_RECODE_SIZE_HOST - TME_RECODE_SIZE_8);
        !           834:     *((tme_uint16_t *) thunk_bytes)
        !           835:       = (TME_RECODE_X86_OPCODE_GRP5
        !           836:         + (TME_RECODE_X86_MOD_OPREG_RM(TME_RECODE_X86_MOD_RM_REG(TME_RECODE_X86_REG_A),
        !           837:                                        TME_RECODE_X86_OPCODE_GRP5_CALL)
        !           838:            << 8));
        !           839:     thunk_bytes += 2;
        !           840:   }
        !           841: 
        !           842:   /* if the guest function returned a register value, and this is a
        !           843:      double-host-size guest: */
        !           844:   if (insn->tme_recode_insn_operand_dst != TME_RECODE_OPERAND_NULL
        !           845:       && TME_RECODE_SIZE_IS_DOUBLE_HOST(ic->tme_recode_ic_reg_size)) {
        !           846: 
        !           847:     /* move the guest function return value from d:a into ax:bp: */
        !           848:     _tme_recode_x86_emit_reg_copy(thunk_bytes, TME_RECODE_X86_REG_A, TME_RECODE_X86_REG_BP);
        !           849:     _tme_recode_x86_emit_reg_copy(thunk_bytes, TME_RECODE_X86_REG_D, TME_RECODE_X86_REG_A);
        !           850:   }
        !           851: 
        !           852:   /* finish these instructions: */
        !           853:   tme_recode_x86_insns_finish(ic, thunk_bytes);
        !           854: }
        !           855: 
        !           856: /* this emits a read or a write instruction: */
        !           857: static void
        !           858: _tme_recode_x86_insn_rw(struct tme_recode_ic *ic,
        !           859:                        struct tme_recode_insn *insn)
        !           860: {
        !           861:   const struct tme_recode_rw_thunk *rw_thunk;
        !           862:   unsigned int reg_host_address;
        !           863:   tme_uint8_t *thunk_bytes;
        !           864: 
        !           865:   /* get the read/write thunk: */
        !           866:   rw_thunk = insn->tme_recode_insn_rw_thunk;
        !           867: 
        !           868:   /* set the instruction size to the address size: */
        !           869:   insn->tme_recode_insn_size = rw_thunk->tme_recode_rw_thunk_address_size;
        !           870: 
        !           871:   /* the guest address is often a value that has been calculated in a
        !           872:      guest temporary register.  before we clean all dirty host
        !           873:      registers, notify that the guest address will be the first source
        !           874:      operand we load, to hopefully avoid a store of a dirty guest
        !           875:      temporary register: */
        !           876:   tme_recode_regs_src_notify(ic,
        !           877:                             insn,
        !           878:                             insn->tme_recode_insn_operand_src[0]);
        !           879: 
        !           880:   /* since a read or a write instruction may fault and never return,
        !           881:      we have to clean all dirty host registers so that all guest
        !           882:      registers are correct in the guest ic state at the time of the
        !           883:      fault: */
        !           884:   tme_recode_regs_host_clean_all(ic);
        !           885: 
        !           886:   /* if this is a double-host-size guest, load the guest address into
        !           887:      the a:bp register pair, otherwise load it into the a register: */
        !           888: #if (TME_RECODE_X86_REG_HOST_FREE_CALL & 1) != 1
        !           889: #error "TME_RECODE_X86_REG_HOST_ values changed"
        !           890: #endif
        !           891:   reg_host_address
        !           892:     = (TME_RECODE_SIZE_IS_DOUBLE_HOST(ic->tme_recode_ic_reg_size)
        !           893:        ? (TME_RECODE_X86_REG_HOST_FREE_CALL - 1)
        !           894:        : TME_RECODE_X86_REG_HOST_FREE_CALL);
        !           895:   tme_recode_regs_src_specific(ic,
        !           896:                               insn,
        !           897:                               insn->tme_recode_insn_operand_src[0],
        !           898:                               reg_host_address);
        !           899: 
        !           900:   /* set the instruction size to the register size: */
        !           901:   insn->tme_recode_insn_size = rw_thunk->tme_recode_rw_thunk_reg_size;
        !           902: 
        !           903:   /* if this is a write: */
        !           904:   if (rw_thunk->tme_recode_rw_thunk_write) {
        !           905: 
        !           906:     /* load the value to write into the first host register (pair): */
        !           907:     tme_recode_regs_src_specific(ic,
        !           908:                                 insn,
        !           909:                                 insn->tme_recode_insn_operand_src[1],
        !           910:                                 TME_RECODE_REG_HOST(0));
        !           911: 
        !           912:     /* unreserve all registers: */
        !           913:     tme_recode_regs_host_unreserve_all(ic);
        !           914: 
        !           915:     /* a write thunk destroys the guest address: */
        !           916:     tme_recode_regs_host_free_specific(ic,
        !           917:                                       reg_host_address);
        !           918:   }
        !           919: 
        !           920:   else {
        !           921: 
        !           922:     /* force the destination register into the same register(s)
        !           923:        holding the guest address: */
        !           924:     tme_recode_regs_dst_specific(ic,
        !           925:                                 insn,
        !           926:                                 0);
        !           927:   }
        !           928: 
        !           929:   /* a read/write thunk is allowed to destroy the d and c registers,
        !           930:      which must be the two host registers used for the subs first
        !           931:      source operand.  the d register must be a true host register, and
        !           932:      the c register must not be a true host register.  if this isn't a
        !           933:      double-host-size guest, we must free the d register.  we must
        !           934:      always mark the c register as destroyed: */
        !           935: #if (TME_RECODE_X86_REG_HOST_SUBS_SRC0 + 1) != TME_RECODE_REG_HOST_UNDEF
        !           936: #error "TME_RECODE_X86_REG_HOST_ values changed"
        !           937: #endif
        !           938:   assert (tme_recode_x86_reg_from_host[TME_RECODE_X86_REG_HOST_SUBS_SRC0 + 0] == TME_RECODE_X86_REG_D);
        !           939:   assert (tme_recode_x86_reg_from_host[TME_RECODE_X86_REG_HOST_SUBS_SRC0 + 1] == TME_RECODE_X86_REG_C);
        !           940:   if (!TME_RECODE_SIZE_IS_DOUBLE_HOST(ic->tme_recode_ic_reg_size)) {
        !           941:     tme_recode_regs_host_free_specific(ic,
        !           942:                                       TME_RECODE_X86_REG_HOST_SUBS_SRC0);
        !           943:   }
        !           944:   ic->tme_recode_x86_ic_thunks_reg_guest_window_c = TME_RECODE_REG_GUEST_WINDOW_UNDEF;
        !           945:     
        !           946:   /* start another instruction: */
        !           947:   tme_recode_x86_insns_start(ic, thunk_bytes);
        !           948: 
        !           949:   /* emit the call to the instruction's subs: */
        !           950:   thunk_bytes[0] = TME_RECODE_X86_OPCODE_CALL_RELz;
        !           951:   thunk_bytes += 1 + sizeof(tme_uint32_t);
        !           952:   ((tme_uint32_t *) thunk_bytes)[-1]
        !           953:     = (rw_thunk->tme_recode_x86_rw_thunk_subs
        !           954:        - tme_recode_build_to_thunk_off(ic, thunk_bytes));
        !           955: 
        !           956:   /* add any zero- or sign-extension instruction: */
        !           957:   *((tme_uint32_t *) thunk_bytes) = rw_thunk->tme_recode_x86_rw_thunk_extend;
        !           958:   thunk_bytes += rw_thunk->tme_recode_x86_rw_thunk_extend_size;
        !           959: 
        !           960:   /* finish this instruction: */
        !           961:   tme_recode_x86_insns_finish(ic, thunk_bytes);
        !           962: }
        !           963: 
        !           964: /* this recodes one instruction: */
        !           965: static void
        !           966: _tme_recode_x86_insn_emit(struct tme_recode_ic *ic,
        !           967:                          struct tme_recode_insn *insn)
        !           968: {
        !           969:   tme_uint32_t opcode_mask;
        !           970:   const struct tme_recode_flags_thunk *flags_thunk;
        !           971:   unsigned int size_saved;
        !           972:   signed int operand_src;
        !           973:   tme_uint8_t *thunk_bytes;
        !           974:   unsigned int reg_x86_src;
        !           975:   unsigned int reg_x86_dst;
        !           976:   unsigned int rex;
        !           977:   unsigned int mod_reg_rm;
        !           978:   tme_uint8_t *thunk_bytes_other;
        !           979:   unsigned int window_i;
        !           980:   unsigned long relv;
        !           981: 
        !           982:   /* get the mask for this opcode: */
        !           983:   opcode_mask = (1 << insn->tme_recode_insn_opcode);
        !           984: 
        !           985:   /* if this is an integer opcode: */
        !           986:   if (__tme_predict_true(opcode_mask
        !           987:                         & ((1 << TME_RECODE_OPCODES_INTEGER) - 1))) {
        !           988: 
        !           989:     /* if this instruction has a flags thunk: */
        !           990:     flags_thunk = insn->tme_recode_insn_flags_thunk;
        !           991:     if (__tme_predict_false(flags_thunk != NULL)) {
        !           992: 
        !           993:       /* if this is a zero- or sign-extension: */
        !           994:       if (__tme_predict_false(opcode_mask
        !           995:                              & ((1 << TME_RECODE_OPCODE_EXTZ)
        !           996:                                 | (1 << TME_RECODE_OPCODE_EXTS)))) {
        !           997: 
        !           998:        /* save the instruction size: */
        !           999:        size_saved = insn->tme_recode_insn_size;
        !          1000: 
        !          1001:        /* temporarily set the instruction size to the size of the
        !          1002:           source operand: */
        !          1003:        insn->tme_recode_insn_size = insn->tme_recode_insn_operand_src[1];
        !          1004: 
        !          1005:        /* set the condition code subs for this opcode at this size and
        !          1006:           source operand size: */
        !          1007: #if (TME_RECODE_OPCODE_EXTS - TME_RECODE_OPCODE_EXTZ) != 1
        !          1008: #error "TME_RECODE_OPCODE_ values changed"
        !          1009: #endif
        !          1010:        insn->tme_recode_x86_insn_subs_thunk_off
        !          1011:          = (flags_thunk->tme_recode_x86_flags_thunk_sizes
        !          1012:             [size_saved - TME_RECODE_SIZE_8]
        !          1013:             .tme_recode_x86_flags_thunk_size_subs_ext
        !          1014:             [insn->tme_recode_insn_size - TME_RECODE_SIZE_8]
        !          1015:             [insn->tme_recode_insn_opcode - TME_RECODE_OPCODE_EXTZ]);
        !          1016: 
        !          1017:        /* load the first source operand into the host register for a
        !          1018:           subs first source operand and destination: */
        !          1019:        tme_recode_regs_src_specific(ic,
        !          1020:                                     insn,
        !          1021:                                     insn->tme_recode_insn_operand_src[0],
        !          1022:                                     TME_RECODE_X86_REG_HOST_SUBS_DST);
        !          1023: 
        !          1024:        /* restore the instruction size: */
        !          1025:        insn->tme_recode_insn_size = size_saved;
        !          1026:       }
        !          1027: 
        !          1028:       /* otherwise, this is not a zero- or sign-extension: */
        !          1029:       else {
        !          1030: 
        !          1031:        /* set the condition code subs for this opcode at this size: */
        !          1032:        insn->tme_recode_x86_insn_subs_thunk_off
        !          1033:          = (flags_thunk->tme_recode_x86_flags_thunk_sizes
        !          1034:             [insn->tme_recode_insn_size - TME_RECODE_SIZE_8]
        !          1035:             .tme_recode_x86_flags_thunk_size_subs[insn->tme_recode_insn_opcode]);
        !          1036: 
        !          1037:        /* load the first source operand into the host register for a
        !          1038:           subs first source operand and destination: */
        !          1039:        tme_recode_regs_src_specific(ic,
        !          1040:                                     insn,
        !          1041:                                     insn->tme_recode_insn_operand_src[0],
        !          1042:                                     TME_RECODE_X86_REG_HOST_SUBS_DST);
        !          1043: 
        !          1044:        /* load the second source operand into the host register for a
        !          1045:           subs second source operand: */
        !          1046:        tme_recode_regs_src_specific(ic,
        !          1047:                                     insn,
        !          1048:                                     insn->tme_recode_insn_operand_src[1],
        !          1049:                                     TME_RECODE_X86_REG_HOST_SUBS_SRC1);
        !          1050:       }
        !          1051: 
        !          1052:       /* if this is an x86-64 host, and this condition code thunk calls
        !          1053:         a guest function: */
        !          1054:       if (TME_RECODE_SIZE_HOST > TME_RECODE_SIZE_32
        !          1055:          && flags_thunk->tme_recode_x86_flags_thunk_has_guest_func) {
        !          1056: 
        !          1057:        /* we need to free all host registers that can be destroyed by
        !          1058:           a C function.  the first such register must be the a
        !          1059:           register, and this is freed by _tme_recode_x86_insn_subs(),
        !          1060:           so we only need to free the others: */
        !          1061:        assert (tme_recode_x86_reg_from_host[TME_RECODE_X86_REG_HOST_FREE_CALL] == TME_RECODE_X86_REG_A);
        !          1062: #if (TME_RECODE_X86_REG_HOST_FREE_CALL & 1) != 1
        !          1063: #error "TME_RECODE_X86_REG_HOST_ values changed"
        !          1064: #endif
        !          1065:        tme_recode_regs_host_free_many(ic, TME_RECODE_X86_REG_HOST_FREE_CALL + 1);
        !          1066:       }
        !          1067: 
        !          1068:       /* emit a call to the condition code subs: */
        !          1069:       _tme_recode_x86_insn_subs(ic, insn);
        !          1070:       return;
        !          1071:     }
        !          1072:   }
        !          1073: 
        !          1074:   /* if the first source operand is zero: */
        !          1075:   operand_src = insn->tme_recode_insn_operand_src[0];
        !          1076:   if (operand_src == TME_RECODE_OPERAND_ZERO) {
        !          1077: 
        !          1078:     /* if this opcode is a copy or a negation when the first source
        !          1079:        operand is zero: */
        !          1080:     if (opcode_mask
        !          1081:        & ((1 << TME_RECODE_OPCODE_ORN)
        !          1082:           | (1 << TME_RECODE_OPCODE_XORN)
        !          1083:           | (1 << TME_RECODE_OPCODE_SUB)
        !          1084:           | (1 << TME_RECODE_OPCODE_OR)
        !          1085:           | (1 << TME_RECODE_OPCODE_XOR)
        !          1086:           | (1 << TME_RECODE_OPCODE_ADD))) {
        !          1087: 
        !          1088:       /* load the second source operand and copy it into the
        !          1089:         destination operand: */
        !          1090:       tme_recode_regs_src_any(ic,
        !          1091:                              insn,
        !          1092:                              insn->tme_recode_insn_operand_src[1]);
        !          1093:       insn->tme_recode_insn_operand_dst
        !          1094:         = tme_recode_regs_dst_specific(ic,
        !          1095:                                       insn,
        !          1096:                                       0);
        !          1097: 
        !          1098:       /* if this opcode is a negation: */
        !          1099:       if (opcode_mask
        !          1100:          & ((1 << TME_RECODE_OPCODE_ORN)
        !          1101:             | (1 << TME_RECODE_OPCODE_XORN)
        !          1102:             | (1 << TME_RECODE_OPCODE_SUB))) {
        !          1103: 
        !          1104:        /* negate the destination operand: */
        !          1105:        _tme_recode_x86_insn_negate(ic, insn);
        !          1106:       }
        !          1107: 
        !          1108:       /* done: */
        !          1109:       return;
        !          1110:     }
        !          1111:   }
        !          1112: 
        !          1113:   /* otherwise, the first source operand is not zero: */
        !          1114: 
        !          1115:   /* if the first source operand is a guest register: */
        !          1116:   else if (operand_src >= TME_RECODE_REG_GUEST(0)) {
        !          1117: 
        !          1118:     /* if this opcode makes a zero when the two source operands are
        !          1119:        the same guest register, and the two source operands are the
        !          1120:        same guest register: */
        !          1121:     if ((opcode_mask
        !          1122:         & ((1 << TME_RECODE_OPCODE_SUB)
        !          1123:            | (1 << TME_RECODE_OPCODE_XOR)))
        !          1124:        && (operand_src
        !          1125:            == insn->tme_recode_insn_operand_src[1])) {
        !          1126:       
        !          1127:       /* zero the destination operand: */
        !          1128:       tme_recode_host_reg_zero(ic,
        !          1129:                               NULL,
        !          1130:                               tme_recode_regs_dst_any(ic,
        !          1131:                                                       insn));
        !          1132:       return;
        !          1133:     }
        !          1134:   }
        !          1135: 
        !          1136:   /* if this is a binary operation: */
        !          1137:   if (opcode_mask
        !          1138:       & ((1 << TME_RECODE_OPCODE_ANDN)
        !          1139:         | (1 << TME_RECODE_OPCODE_ORN)
        !          1140:         | (1 << TME_RECODE_OPCODE_XORN)
        !          1141:         | (1 << TME_RECODE_OPCODE_SUB)
        !          1142:         | (1 << TME_RECODE_OPCODE_SUBC)
        !          1143:         | (1 << TME_RECODE_OPCODE_AND)
        !          1144:         | (1 << TME_RECODE_OPCODE_OR)
        !          1145:         | (1 << TME_RECODE_OPCODE_XOR)
        !          1146:         | (1 << TME_RECODE_OPCODE_ADD)
        !          1147:         | (1 << TME_RECODE_OPCODE_ADDC))) {
        !          1148: 
        !          1149:     /* if the right source operand is an immediate that we can emit
        !          1150:        directly in the instruction(s): */
        !          1151:     /* NB: on an ia32 host, we can emit all immediates directly in the
        !          1152:        instruction(s), even for a double-host-size instruction.  on an
        !          1153:        x86-64 host, we can only emit an immediate whose one or two
        !          1154:        host-sized parts can be sign-extended from 32-bit parts: */
        !          1155:     if (insn->tme_recode_insn_operand_src[1] == TME_RECODE_OPERAND_IMM
        !          1156:        && (TME_RECODE_SIZE_HOST == TME_RECODE_SIZE_32
        !          1157:            || ((((signed long) insn->tme_recode_insn_imm_guest)
        !          1158:                 == (tme_int32_t) insn->tme_recode_insn_imm_guest)
        !          1159:                && (!TME_RECODE_SIZE_IS_DOUBLE_HOST(insn->tme_recode_insn_size)
        !          1160:                    || (((signed long)
        !          1161:                         (insn->tme_recode_insn_imm_guest
        !          1162:                          >> ((sizeof(insn->tme_recode_insn_imm_guest) * 8) / 2)))
        !          1163:                        == ((tme_int32_t)
        !          1164:                            (insn->tme_recode_insn_imm_guest
        !          1165:                             >> ((sizeof(insn->tme_recode_insn_imm_guest) * 8) / 2)))))))) {
        !          1166: 
        !          1167:       /* if this is a logical noncommutative instruction: */
        !          1168:       if (opcode_mask
        !          1169:          & ((1 << TME_RECODE_OPCODE_ANDN)
        !          1170:             | (1 << TME_RECODE_OPCODE_ORN)
        !          1171:             | (1 << TME_RECODE_OPCODE_XORN))) {
        !          1172: 
        !          1173:        /* invert the immediate: */
        !          1174:        insn->tme_recode_insn_imm_uguest = ~insn->tme_recode_insn_imm_uguest;
        !          1175: 
        !          1176:        /* convert the opcode into its commutative version: */
        !          1177:        /* NB: we don't need to change opcode_mask, since the
        !          1178:           only remaining tests are for additive operations: */
        !          1179:        insn->tme_recode_insn_opcode = TME_RECODE_OPCODE_LOGICALN_TO_PLAIN(insn->tme_recode_insn_opcode);
        !          1180:       }
        !          1181: 
        !          1182:       /* load the left source operand: */
        !          1183:       insn->tme_recode_insn_operand_src[0]
        !          1184:        = tme_recode_regs_src_any(ic,
        !          1185:                                  insn,
        !          1186:                                  insn->tme_recode_insn_operand_src[0]);
        !          1187: 
        !          1188:       /* copy the left source operand into the destination operand: */
        !          1189:       insn->tme_recode_insn_operand_dst
        !          1190:        = tme_recode_regs_dst_specific(ic,
        !          1191:                                       insn,
        !          1192:                                       0);
        !          1193: 
        !          1194:       /* if this is an additive operation with the carry: */
        !          1195:       if (opcode_mask
        !          1196:          & ((1 << TME_RECODE_OPCODE_ADDC)
        !          1197:             | (1 << TME_RECODE_OPCODE_SUBC))) {
        !          1198: 
        !          1199:        /* emit the instruction to define CF with the recode carry flag: */
        !          1200:        _tme_recode_x86_conds_testc(ic, 0);
        !          1201:       }
        !          1202: 
        !          1203:       /* if this is a double-host-size instruction: */
        !          1204:       if (TME_RECODE_SIZE_IS_DOUBLE_HOST(insn->tme_recode_insn_size)) {
        !          1205: 
        !          1206:        /* emit the low-half host-size instruction: */
        !          1207:        _tme_recode_x86_insn_binop_imm(ic, insn);
        !          1208: 
        !          1209:        /* advance to the high halves of the immediate and
        !          1210:           destination: */
        !          1211:        insn->tme_recode_insn_imm_uguest >>= (sizeof(insn->tme_recode_insn_imm_uguest) * 8) / 2;
        !          1212:        insn->tme_recode_insn_operand_dst++;
        !          1213: 
        !          1214:        /* a high-half additive operation must use the carry: */
        !          1215:        if (opcode_mask
        !          1216:            & ((1 << TME_RECODE_OPCODE_ADD)
        !          1217:               | (1 << TME_RECODE_OPCODE_SUB))) {
        !          1218: #if (TME_RECODE_OPCODE_SUBC - TME_RECODE_OPCODE_SUB) != (TME_RECODE_OPCODE_ADDC - TME_RECODE_OPCODE_ADD)
        !          1219: #error "TME_RECODE_OPCODE_ values changed"
        !          1220: #endif
        !          1221:          insn->tme_recode_insn_opcode += (TME_RECODE_OPCODE_SUBC - TME_RECODE_OPCODE_SUB);
        !          1222:        }
        !          1223:       }        
        !          1224: 
        !          1225:       /* emit the host-size instruction: */
        !          1226:       _tme_recode_x86_insn_binop_imm(ic, insn);
        !          1227:       return;
        !          1228:     }
        !          1229: 
        !          1230:     /* if the second source operand is the same as the destination operand,
        !          1231:        and this operation is commutative: */
        !          1232:     operand_src = insn->tme_recode_insn_operand_src[1];
        !          1233:     if (operand_src == insn->tme_recode_insn_operand_dst
        !          1234:        && (opcode_mask
        !          1235:            & ((1 << TME_RECODE_OPCODE_AND)
        !          1236:               | (1 << TME_RECODE_OPCODE_OR)
        !          1237:               | (1 << TME_RECODE_OPCODE_XOR)
        !          1238:               | (1 << TME_RECODE_OPCODE_ADD)
        !          1239:               | (1 << TME_RECODE_OPCODE_ADDC)))) {
        !          1240: 
        !          1241:       /* switch the first and second source operands: */
        !          1242:       insn->tme_recode_insn_operand_src[1] = insn->tme_recode_insn_operand_src[0];
        !          1243:       insn->tme_recode_insn_operand_src[0] = operand_src;
        !          1244:     }
        !          1245: 
        !          1246:     /* load the left and right source operands: */
        !          1247:     insn->tme_recode_insn_operand_src[0]
        !          1248:       = tme_recode_regs_src_any(ic,
        !          1249:                                insn,
        !          1250:                                insn->tme_recode_insn_operand_src[0]);
        !          1251:     insn->tme_recode_insn_operand_src[1]
        !          1252:       = tme_recode_regs_src_any(ic,
        !          1253:                                insn,
        !          1254:                                insn->tme_recode_insn_operand_src[1]);
        !          1255: 
        !          1256:     /* if this is a logical noncommutative instruction: */
        !          1257:     if (opcode_mask
        !          1258:        & ((1 << TME_RECODE_OPCODE_ANDN)
        !          1259:           | (1 << TME_RECODE_OPCODE_ORN)
        !          1260:           | (1 << TME_RECODE_OPCODE_XORN))) {
        !          1261: 
        !          1262:       /* copy the right source operand into the destination operand: */
        !          1263:       insn->tme_recode_insn_operand_dst
        !          1264:        = tme_recode_regs_dst_specific(ic,
        !          1265:                                       insn,
        !          1266:                                       1);
        !          1267: 
        !          1268:       /* negate the destination operand: */
        !          1269:       _tme_recode_x86_insn_negate(ic, insn);
        !          1270: 
        !          1271:       /* move the left source operand into the right source operand: */
        !          1272:       insn->tme_recode_insn_operand_src[1] = insn->tme_recode_insn_operand_src[0];
        !          1273: 
        !          1274:       /* convert the opcode into its commutative version: */
        !          1275:       /* NB: we don't need to change opcode_mask, since the
        !          1276:         only remaining tests are for additive operations: */
        !          1277:       insn->tme_recode_insn_opcode = TME_RECODE_OPCODE_LOGICALN_TO_PLAIN(insn->tme_recode_insn_opcode);
        !          1278:     }
        !          1279: 
        !          1280:     /* otherwise, this is not a logical noncommutative instruction: */
        !          1281:     else {
        !          1282: 
        !          1283:       /* copy the left source operand into the destination operand: */
        !          1284:       insn->tme_recode_insn_operand_dst
        !          1285:        = tme_recode_regs_dst_specific(ic,
        !          1286:                                       insn,
        !          1287:                                       0);
        !          1288: 
        !          1289:       /* if this is an additive operation with the carry: */
        !          1290:       if (opcode_mask
        !          1291:          & ((1 << TME_RECODE_OPCODE_ADDC)
        !          1292:             | (1 << TME_RECODE_OPCODE_SUBC))) {
        !          1293: 
        !          1294:        /* emit the instruction to define CF with the recode carry flag: */
        !          1295:        _tme_recode_x86_conds_testc(ic, 0);
        !          1296:       }
        !          1297:     }
        !          1298: 
        !          1299:     /* if this is a double-host-size instruction: */
        !          1300:     if (TME_RECODE_SIZE_IS_DOUBLE_HOST(insn->tme_recode_insn_size)) {
        !          1301: 
        !          1302:       /* emit the low-half host-size instruction: */
        !          1303:       _tme_recode_x86_insn_binop(ic, insn);
        !          1304: 
        !          1305:       /* advance to the high halves of the source and destination: */
        !          1306:       insn->tme_recode_insn_operand_src[1]++;
        !          1307:       insn->tme_recode_insn_operand_dst++;
        !          1308: 
        !          1309:       /* a high-half additive operation must use the carry: */
        !          1310:       if (opcode_mask
        !          1311:          & ((1 << TME_RECODE_OPCODE_ADD)
        !          1312:             | (1 << TME_RECODE_OPCODE_SUB))) {
        !          1313: #if (TME_RECODE_OPCODE_SUBC - TME_RECODE_OPCODE_SUB) != (TME_RECODE_OPCODE_ADDC - TME_RECODE_OPCODE_ADD)
        !          1314: #error "TME_RECODE_OPCODE_ values changed"
        !          1315: #endif
        !          1316:        insn->tme_recode_insn_opcode += (TME_RECODE_OPCODE_SUBC - TME_RECODE_OPCODE_SUB);
        !          1317:       }
        !          1318:     }
        !          1319: 
        !          1320:     /* emit the host-size instruction: */
        !          1321:     _tme_recode_x86_insn_binop(ic, insn);
        !          1322:     return;
        !          1323:   }
        !          1324: 
        !          1325:   /* if this is a shift: */
        !          1326:   if (opcode_mask
        !          1327:       & ((1 << TME_RECODE_OPCODE_SHRA)
        !          1328:         | (1 << TME_RECODE_OPCODE_SHRL)
        !          1329:         | (1 << TME_RECODE_OPCODE_SHLL))) {
        !          1330: 
        !          1331:     /* if the shift count is an immediate, and this isn't a
        !          1332:        double-host-size instruction, and the shift count is less than
        !          1333:        the instruction size: */
        !          1334:     if (insn->tme_recode_insn_operand_src[1] == TME_RECODE_OPERAND_IMM
        !          1335:        && !TME_RECODE_SIZE_IS_DOUBLE_HOST(insn->tme_recode_insn_size)
        !          1336:        && insn->tme_recode_insn_imm_uguest < TME_BIT(insn->tme_recode_insn_size)) {
        !          1337: 
        !          1338:       /* load the source operand: */
        !          1339:       tme_recode_regs_src_any(ic,
        !          1340:                              insn,
        !          1341:                              insn->tme_recode_insn_operand_src[0]);
        !          1342: 
        !          1343:       /* copy the source operand into the destination operand: */
        !          1344:       insn->tme_recode_insn_operand_dst
        !          1345:        = tme_recode_regs_dst_specific(ic,
        !          1346:                                       insn,
        !          1347:                                       0);
        !          1348: 
        !          1349:       /* if this is less than a host-size instruction: */
        !          1350:       if (__tme_predict_false(insn->tme_recode_insn_size < TME_RECODE_SIZE_HOST)) {
        !          1351: 
        !          1352:        /* if this is a right shift: */
        !          1353:        if (opcode_mask != (1 << TME_RECODE_OPCODE_SHLL)) {
        !          1354: 
        !          1355:          /* for a right shift, we have to make sure that the correct
        !          1356:             bits get shifted in on the right, so we have to first
        !          1357:             extend the value in the destination register to host
        !          1358:             size.  for an arithmetic shift, we have to do a sign
        !          1359:             extension, and for a logical shift we have to do a zero
        !          1360:             extension: */
        !          1361: 
        !          1362:          /* map TME_RECODE_OPCODE_SHRL to TME_RECODE_OPCODE_EXTZ, and
        !          1363:             TME_RECODE_OPCODE_SHRA to TME_RECODE_OPCODE_EXTS: */
        !          1364: #if (TME_RECODE_OPCODE_SHRA - TME_RECODE_OPCODE_SHRL) != (TME_RECODE_OPCODE_EXTS - TME_RECODE_OPCODE_EXTZ)
        !          1365: #error "TME_RECODE_OPCODE_ values changed"
        !          1366: #endif
        !          1367:          insn->tme_recode_insn_opcode
        !          1368:            = ((insn->tme_recode_insn_opcode
        !          1369:                - TME_RECODE_OPCODE_SHRL)
        !          1370:               + TME_RECODE_OPCODE_EXTZ);
        !          1371: 
        !          1372:          /* zero- or sign-extend the destination operand: */
        !          1373:          insn->tme_recode_insn_operand_src[0] = insn->tme_recode_insn_operand_dst;
        !          1374:          insn->tme_recode_insn_operand_src[1] = insn->tme_recode_insn_size;
        !          1375:          _tme_recode_x86_insn_ext(ic, insn);
        !          1376: 
        !          1377:          /* restore the opcode: */
        !          1378: #if (TME_RECODE_OPCODE_SHRA - TME_RECODE_OPCODE_SHRL) != (TME_RECODE_OPCODE_EXTS - TME_RECODE_OPCODE_EXTZ)
        !          1379: #error "TME_RECODE_OPCODE_ values changed"
        !          1380: #endif
        !          1381:          insn->tme_recode_insn_opcode
        !          1382:            = ((insn->tme_recode_insn_opcode
        !          1383:                - TME_RECODE_OPCODE_EXTZ)
        !          1384:               + TME_RECODE_OPCODE_SHRL);
        !          1385:        }
        !          1386:       }
        !          1387: 
        !          1388:       /* emit the host-size instruction: */
        !          1389:       _tme_recode_x86_insn_shift_imm(ic, insn);
        !          1390:       return;
        !          1391:     }
        !          1392: 
        !          1393:     /* load the source operand into the host register for a subs first
        !          1394:        source operand and destination: */
        !          1395:     tme_recode_regs_src_specific(ic,
        !          1396:                                 insn,
        !          1397:                                 insn->tme_recode_insn_operand_src[0],
        !          1398:                                 TME_RECODE_X86_REG_HOST_SUBS_DST);
        !          1399: 
        !          1400:     /* load the shift count into the host register for a subs second
        !          1401:        source operand: */
        !          1402:     tme_recode_regs_src_specific(ic,
        !          1403:                                 insn,
        !          1404:                                 insn->tme_recode_insn_operand_src[1],
        !          1405:                                 TME_RECODE_X86_REG_HOST_SUBS_SRC1);
        !          1406: 
        !          1407:     /* emit a call to the subs for this particular shift: */
        !          1408: #if (TME_RECODE_OPCODE_SHLL + 1) != TME_RECODE_OPCODE_SHRL || (TME_RECODE_OPCODE_SHRL + 1) != TME_RECODE_OPCODE_SHRA
        !          1409: #error "TME_RECODE_OPCODE_ values changed"
        !          1410: #endif
        !          1411:     insn->tme_recode_x86_insn_subs_thunk_off
        !          1412:       = (ic->tme_recode_x86_ic_subs_shift
        !          1413:         [insn->tme_recode_insn_size - TME_RECODE_SIZE_8]
        !          1414:         [insn->tme_recode_insn_opcode - TME_RECODE_OPCODE_SHLL]);
        !          1415:     _tme_recode_x86_insn_subs(ic, insn);
        !          1416:     return;
        !          1417:   }
        !          1418: 
        !          1419:   /* if this is a zero- or sign-extension: */
        !          1420:   else if (opcode_mask
        !          1421:           & ((1 << TME_RECODE_OPCODE_EXTZ)
        !          1422:              | (1 << TME_RECODE_OPCODE_EXTS))) {
        !          1423: 
        !          1424:     /* save the instruction size: */
        !          1425:     size_saved = insn->tme_recode_insn_size;
        !          1426: 
        !          1427:     /* temporarily set the instruction size to the size of the
        !          1428:        source operand: */
        !          1429:     insn->tme_recode_insn_size = insn->tme_recode_insn_operand_src[1];
        !          1430: 
        !          1431:     /* load the source operand: */
        !          1432:     insn->tme_recode_insn_operand_src[0]
        !          1433:       = tme_recode_regs_src_any(ic,
        !          1434:                                insn,
        !          1435:                                insn->tme_recode_insn_operand_src[0]);
        !          1436: 
        !          1437:     /* restore the instruction size: */
        !          1438:     insn->tme_recode_insn_size = size_saved;
        !          1439: 
        !          1440:     /* load the destination operand: */
        !          1441:     insn->tme_recode_insn_operand_dst
        !          1442:       = tme_recode_regs_dst_any(ic,
        !          1443:                                insn);
        !          1444: 
        !          1445:     /* if this can't be a double-host-size instruction, or if this is
        !          1446:        not an extension from the host size: */
        !          1447:     if (TME_RECODE_SIZE_GUEST_MAX <= TME_RECODE_SIZE_HOST
        !          1448:        || insn->tme_recode_insn_operand_src[1] < TME_RECODE_SIZE_HOST) {
        !          1449: 
        !          1450:       /* emit the extension to host size: */
        !          1451:       _tme_recode_x86_insn_ext(ic, insn);
        !          1452:     }
        !          1453: 
        !          1454:     /* if this is a double-host-size instruction: */
        !          1455:     if (TME_RECODE_SIZE_IS_DOUBLE_HOST(insn->tme_recode_insn_size)) {
        !          1456: 
        !          1457:       /* start more instructions: */
        !          1458:       tme_recode_x86_insns_start(ic, thunk_bytes);
        !          1459: 
        !          1460:       /* get the x86 source register: */
        !          1461:       reg_x86_src = tme_recode_x86_reg_from_host[insn->tme_recode_insn_operand_src[0] + 0];
        !          1462: 
        !          1463:       /* if this is an extension from host size: */
        !          1464:       if (insn->tme_recode_insn_operand_src[1] == TME_RECODE_SIZE_HOST) {
        !          1465: 
        !          1466:        /* get the x86 destination register: */
        !          1467:        reg_x86_dst = tme_recode_x86_reg_from_host[insn->tme_recode_insn_operand_dst + 0];
        !          1468: 
        !          1469:        /* unless the host registers are the same, copy the least
        !          1470:           significant half of the source operand into the least
        !          1471:           significant half of the destination operand: */
        !          1472:        if (reg_x86_src != reg_x86_dst) {
        !          1473:          _tme_recode_x86_emit_reg_copy(thunk_bytes, reg_x86_src, reg_x86_dst);
        !          1474:        }
        !          1475:       }          
        !          1476: 
        !          1477:       /* get the x86 destination register: */
        !          1478:       reg_x86_dst = tme_recode_x86_reg_from_host[insn->tme_recode_insn_operand_dst + 1];
        !          1479: 
        !          1480:       /* if this is a sign-extension: */
        !          1481:       if (opcode_mask == (1 << TME_RECODE_OPCODE_EXTS)) {
        !          1482: 
        !          1483:        /* emit a mov %src, %dst; add %src, %dst: */
        !          1484:        if (TME_RECODE_SIZE_HOST > TME_RECODE_SIZE_32) {
        !          1485:          rex = (TME_RECODE_X86_REX_B(TME_RECODE_SIZE_HOST, reg_x86_src)
        !          1486:                 | TME_RECODE_X86_REX_R(TME_RECODE_SIZE_HOST, reg_x86_dst));
        !          1487:          thunk_bytes[0] = rex;
        !          1488:          thunk_bytes[3] = rex;
        !          1489:        }
        !          1490:        thunk_bytes[(TME_RECODE_SIZE_HOST > TME_RECODE_SIZE_32) + 0]
        !          1491:          = (TME_RECODE_X86_OPCODE_BINOP_MOV
        !          1492:             + TME_RECODE_X86_OPCODE_BINOP_Ev_Gv);
        !          1493:        thunk_bytes[(TME_RECODE_SIZE_HOST > TME_RECODE_SIZE_32) + 2
        !          1494:                    + (TME_RECODE_SIZE_HOST > TME_RECODE_SIZE_32) + 0]
        !          1495:          = (TME_RECODE_X86_OPCODE_BINOP_ADD
        !          1496:             + TME_RECODE_X86_OPCODE_BINOP_Ev_Gv);
        !          1497:        mod_reg_rm
        !          1498:          = TME_RECODE_X86_MOD_OPREG_RM(TME_RECODE_X86_MOD_RM_REG(reg_x86_src),
        !          1499:                                        TME_RECODE_X86_REG(reg_x86_dst));
        !          1500:        thunk_bytes[(TME_RECODE_SIZE_HOST > TME_RECODE_SIZE_32) + 1] = mod_reg_rm;
        !          1501:        thunk_bytes[(TME_RECODE_SIZE_HOST > TME_RECODE_SIZE_32) + 2
        !          1502:                    + (TME_RECODE_SIZE_HOST > TME_RECODE_SIZE_32) + 1] = mod_reg_rm;
        !          1503:        thunk_bytes += ((TME_RECODE_SIZE_HOST > TME_RECODE_SIZE_32) + 2) * 2;
        !          1504:       }
        !          1505: 
        !          1506:       /* emit a sbb %dst, %dst if this is a sign-extension,
        !          1507:         or a sub %dst, %dst if this is a zero-extension: */
        !          1508:       if (TME_RECODE_SIZE_HOST > TME_RECODE_SIZE_32) {
        !          1509:        thunk_bytes[0]
        !          1510:          = (TME_RECODE_X86_REX_B(TME_RECODE_SIZE_HOST, reg_x86_dst)
        !          1511:             | TME_RECODE_X86_REX_R(TME_RECODE_SIZE_HOST, reg_x86_dst));
        !          1512:       }
        !          1513: #if (TME_RECODE_OPCODE_EXTS - TME_RECODE_OPCODE_EXTZ) != 1
        !          1514: #error "TME_RECODE_OPCODE_ values changed"
        !          1515: #endif
        !          1516:       thunk_bytes[(TME_RECODE_SIZE_HOST > TME_RECODE_SIZE_32) + 0]
        !          1517:        = (TME_RECODE_X86_OPCODE_BINOP_SUB
        !          1518:           + TME_RECODE_X86_OPCODE_BINOP_Ev_Gv
        !          1519:           - ((insn->tme_recode_insn_opcode
        !          1520:               - TME_RECODE_OPCODE_EXTZ)
        !          1521:              * (TME_RECODE_X86_OPCODE_BINOP_SUB
        !          1522:                 - TME_RECODE_X86_OPCODE_BINOP_SBB)));
        !          1523:       thunk_bytes[(TME_RECODE_SIZE_HOST > TME_RECODE_SIZE_32) + 1]
        !          1524:        = TME_RECODE_X86_MOD_OPREG_RM(TME_RECODE_X86_MOD_RM_REG(reg_x86_dst),
        !          1525:                                      TME_RECODE_X86_REG(reg_x86_dst));
        !          1526:       thunk_bytes += (TME_RECODE_SIZE_HOST > TME_RECODE_SIZE_32) + 2;
        !          1527: 
        !          1528:       /* finish these instructions: */
        !          1529:       tme_recode_x86_insns_finish(ic, thunk_bytes);
        !          1530:     }
        !          1531:     return;
        !          1532:   }
        !          1533: 
        !          1534:   /* if this is a guest opcode: */
        !          1535:   else if (opcode_mask == (1 << TME_RECODE_OPCODE_GUEST)) {
        !          1536: 
        !          1537:     /* emit the call to the guest function: */
        !          1538:     _tme_recode_x86_insn_guest(ic, insn);
        !          1539:     return;
        !          1540:   }
        !          1541: 
        !          1542:   /* if this is a define-recode-carry opcode: */
        !          1543:   else if (opcode_mask == (1 << TME_RECODE_OPCODE_DEFC)) {
        !          1544: 
        !          1545:     /* emit the carry define: */
        !          1546:     _tme_recode_x86_conds_defc(ic, insn);
        !          1547:     return;
        !          1548:   }
        !          1549: 
        !          1550:   /* if this is an if opcode: */
        !          1551:   else if (opcode_mask == (1 << TME_RECODE_OPCODE_IF)) {
        !          1552: 
        !          1553:     /* clean all dirty host registers before we begin the if: */
        !          1554:     tme_recode_regs_host_clean_all(ic);
        !          1555: 
        !          1556:     /* emit the instruction to define CF with the desired recode flag: */
        !          1557:     _tme_recode_x86_conds_testc(ic, insn->tme_recode_insn_operand_src[0]);
        !          1558: 
        !          1559:     /* start more instructions: */
        !          1560:     tme_recode_x86_insns_start(ic, thunk_bytes);
        !          1561: 
        !          1562:     /* ifs can't be nested: */
        !          1563:     assert (ic->tme_recode_x86_ic_thunks_build_if == NULL
        !          1564:            && ic->tme_recode_x86_ic_thunks_build_else == NULL);
        !          1565: 
        !          1566:     /* save the address of this if: */
        !          1567:     ic->tme_recode_x86_ic_thunks_build_if = thunk_bytes;
        !          1568:     
        !          1569:     /* a 32-bit displacement jnc instruction requires six bytes: */
        !          1570:     thunk_bytes += 2 + sizeof(tme_uint32_t);
        !          1571: 
        !          1572:     /* save any c register window index at the if jnc: */
        !          1573:     ic->tme_recode_x86_ic_thunks_reg_guest_window_c_if_jmp
        !          1574:       = ic->tme_recode_x86_ic_thunks_reg_guest_window_c;
        !          1575: 
        !          1576:     /* finish these instructions: */
        !          1577:     tme_recode_x86_insns_finish(ic, thunk_bytes);
        !          1578:   }
        !          1579: 
        !          1580:   /* if this is an else or an endif opcode: */
        !          1581:   else if (opcode_mask
        !          1582:           & ((1 << TME_RECODE_OPCODE_ELSE)
        !          1583:              | (1 << TME_RECODE_OPCODE_ENDIF))) {
        !          1584: 
        !          1585:     /* free all host registers: */
        !          1586:     tme_recode_regs_host_free_many(ic, TME_RECODE_REG_HOST(0));
        !          1587: 
        !          1588:     /* start more instructions: */
        !          1589:     tme_recode_x86_insns_start(ic, thunk_bytes);
        !          1590: 
        !          1591:     /* if we are in an if: */
        !          1592:     thunk_bytes_other = ic->tme_recode_x86_ic_thunks_build_if;
        !          1593:     if (thunk_bytes_other != NULL) {
        !          1594: 
        !          1595:       /* if this is an else: */
        !          1596:       if (opcode_mask == (1 << TME_RECODE_OPCODE_ELSE)) {
        !          1597: 
        !          1598:        /* save the address of this else: */
        !          1599:        ic->tme_recode_x86_ic_thunks_build_else = thunk_bytes;
        !          1600:     
        !          1601:        /* a 32-bit displacement jmp instruction requires five bytes: */
        !          1602:        thunk_bytes += 1 + sizeof(tme_uint32_t);
        !          1603: 
        !          1604:        /* switch any c register window index at the if jnc with any
        !          1605:           window index at the end of the if body: */
        !          1606:        window_i = ic->tme_recode_x86_ic_thunks_reg_guest_window_c;
        !          1607:        ic->tme_recode_x86_ic_thunks_reg_guest_window_c
        !          1608:          = ic->tme_recode_x86_ic_thunks_reg_guest_window_c_if_jmp;
        !          1609:        ic->tme_recode_x86_ic_thunks_reg_guest_window_c_if_jmp = window_i;
        !          1610: 
        !          1611:        /* finish these instructions: */
        !          1612:        tme_recode_x86_insns_finish(ic, thunk_bytes);
        !          1613:       }
        !          1614: 
        !          1615:       /* emit the jnc instruction at the if: */
        !          1616:       relv = thunk_bytes - (thunk_bytes_other + 1 + sizeof(tme_int8_t));
        !          1617:       if (relv < 0x7f) {
        !          1618:        thunk_bytes_other[0] = TME_RECODE_X86_OPCODE_JCC(TME_RECODE_X86_COND_NOT | TME_RECODE_X86_COND_C);
        !          1619:        thunk_bytes_other[1] = relv;
        !          1620:        *((tme_uint32_t *) &thunk_bytes_other[2]) = TME_RECODE_X86_NOP4;
        !          1621:       }
        !          1622:       else {
        !          1623:        relv = thunk_bytes - (thunk_bytes_other + 2 + sizeof(tme_int32_t));
        !          1624:        *((tme_uint16_t *) &thunk_bytes_other[0])
        !          1625:          = (TME_RECODE_X86_OPCODE_ESC_0F
        !          1626:             + (TME_RECODE_X86_OPCODE0F_JCC(TME_RECODE_X86_COND_NOT | TME_RECODE_X86_COND_C)
        !          1627:                << 8));
        !          1628:        *((tme_uint32_t *) &thunk_bytes_other[2]) = relv;
        !          1629:       }
        !          1630: 
        !          1631:       /* clear the if: */
        !          1632:       ic->tme_recode_x86_ic_thunks_build_if = NULL;
        !          1633:     }
        !          1634: 
        !          1635:     /* otherwise, we must be in an else and this must be an endif: */
        !          1636:     else {
        !          1637:       thunk_bytes_other = ic->tme_recode_x86_ic_thunks_build_else;
        !          1638:       assert (thunk_bytes_other != NULL
        !          1639:              && opcode_mask == (1 << TME_RECODE_OPCODE_ENDIF));
        !          1640: 
        !          1641:       /* emit the jmp instruction at the else: */
        !          1642:       relv = thunk_bytes - (thunk_bytes_other + 1 + sizeof(tme_int8_t));
        !          1643:       if (relv < 0x7f) {
        !          1644:        thunk_bytes_other[0] = TME_RECODE_X86_OPCODE_JMP_RELb;
        !          1645:        relv += (TME_RECODE_X86_NOP3 << 8);
        !          1646:       }
        !          1647:       else {
        !          1648:        relv = thunk_bytes - (thunk_bytes_other + 1 + sizeof(tme_int32_t));
        !          1649:        thunk_bytes_other[0] = TME_RECODE_X86_OPCODE_JMP_RELz;
        !          1650:       }
        !          1651:       *((tme_uint32_t *) &thunk_bytes_other[1]) = relv;
        !          1652:     }
        !          1653: 
        !          1654:     /* clear the else: */
        !          1655:     ic->tme_recode_x86_ic_thunks_build_else = NULL;
        !          1656: 
        !          1657:     /* if this is an endif: */
        !          1658:     if (opcode_mask == (1 << TME_RECODE_OPCODE_ENDIF)) {
        !          1659:  
        !          1660:       /* if there is no else body, and any c register window index at
        !          1661:         the if jnc disagrees with the window index at the end of the
        !          1662:         if body, or if there is an else body, and any c register
        !          1663:         window index at the end of the if body disagrees with any c
        !          1664:         register window index at the end of the else body: */
        !          1665:       if (ic->tme_recode_x86_ic_thunks_reg_guest_window_c_if_jmp
        !          1666:          != ic->tme_recode_x86_ic_thunks_reg_guest_window_c) {
        !          1667: 
        !          1668:        /* at this point, there isn't a known window index in the c register: */
        !          1669:        ic->tme_recode_x86_ic_thunks_reg_guest_window_c = TME_RECODE_REG_GUEST_WINDOW_UNDEF;
        !          1670:       }
        !          1671:     }
        !          1672:   }
        !          1673: 
        !          1674:   /* if this is a read or a write instruction: */
        !          1675:   else if (opcode_mask == (1 << TME_RECODE_OPCODE_RW)) {
        !          1676: 
        !          1677:     /* emit the read or write instruction: */
        !          1678:     _tme_recode_x86_insn_rw(ic, insn);
        !          1679:   }
        !          1680: 
        !          1681:   /* otherwise, this is some unknown opcode: */
        !          1682:   else {
        !          1683:     assert(FALSE);
        !          1684:   }
        !          1685: }
        !          1686: 
        !          1687: /* this invalidates an instructions thunk: */
        !          1688: void
        !          1689: tme_recode_insns_thunk_invalidate(struct tme_recode_ic *ic,
        !          1690:                                  tme_recode_thunk_off_t insns_thunk)
        !          1691: {
        !          1692:   tme_uint8_t opcode_jmp_relz;
        !          1693:   tme_int32_t disp32;
        !          1694: 
        !          1695:   /* overwrite the first instruction of the thunk with a jmp to the
        !          1696:      chain epilogue: */
        !          1697:   opcode_jmp_relz = TME_RECODE_X86_OPCODE_JMP_RELz;
        !          1698:   disp32
        !          1699:     = (ic->tme_recode_x86_ic_chain_epilogue
        !          1700:        - (insns_thunk
        !          1701:          + sizeof(opcode_jmp_relz)
        !          1702:          + sizeof(disp32)));
        !          1703:   tme_recode_thunk_off_write(ic,
        !          1704:                             insns_thunk,
        !          1705:                             tme_uint8_t,
        !          1706:                             opcode_jmp_relz);
        !          1707:   tme_recode_thunk_off_write(ic,
        !          1708:                             (insns_thunk
        !          1709:                              + sizeof(opcode_jmp_relz)),
        !          1710:                             tme_int32_t,
        !          1711:                             disp32);
        !          1712: }
        !          1713: 
        !          1714: /* this checks the value of TME_RECODE_HOST_INSN_SIZE_MAX: */
        !          1715: static void
        !          1716: _tme_recode_x86_insn_size_max_check(void)
        !          1717: {
        !          1718:   tme_uint32_t insn_size_window_offset;
        !          1719:   tme_uint32_t insn_size_host_store;
        !          1720:   tme_uint32_t insn_size_host_copy;
        !          1721:   tme_uint32_t insn_size_host_load;
        !          1722:   tme_uint32_t insn_size;
        !          1723: 
        !          1724:   /* the largest insn recode for any guest should be that guest's
        !          1725:      worst-case TME_RECODE_OPCODE_GUEST insn, but exactly what that
        !          1726:      means depends on a lot of variables that we don't want to
        !          1727:      consider here.
        !          1728: 
        !          1729:      instead, this calculates an upper-bound for that worst-case
        !          1730:      TME_RECODE_OPCODE_GUEST insn, by including all possible x86
        !          1731:      instructions and x86 instruction features in a
        !          1732:      TME_RECODE_OPCODE_GUEST insn recode, even though it's impossible
        !          1733:      for all instructions and all features to be present at the same
        !          1734:      time: */
        !          1735: 
        !          1736:   /* size the x86 instruction to load one window offset: */
        !          1737:   insn_size_window_offset
        !          1738:     = ((TME_RECODE_SIZE_HOST > TME_RECODE_SIZE_32) /* TME_RECODE_X86_REX */
        !          1739:        + 1             /* TME_RECODE_X86_OPCODE_MOVS_El_Gv or TME_RECODE_X86_OPCODE_BINOP_MOV */
        !          1740:        + 1             /* TME_RECODE_X86_MOD_OPREG_RM(TME_RECODE_X86_MOD_RM_EA_DISP32) */
        !          1741:        + sizeof(tme_int32_t));
        !          1742: 
        !          1743:   /* size the x86 instruction(s) to store one host register at the
        !          1744:      worst size to a guest register window.  on an x86-64 host, the
        !          1745:      worst size is the 16-bit size, and we simply assume that all host
        !          1746:      registers need a REX prefix.  on an ia32, the worst size is the
        !          1747:      8-bit size, and we simply assume that all host registers don't
        !          1748:      have an 8-bit encoding: */
        !          1749:   insn_size_host_store
        !          1750:     = ((TME_RECODE_SIZE_HOST > TME_RECODE_SIZE_32)
        !          1751:        ? (1            /* TME_RECODE_X86_PREFIX_OPSIZ */
        !          1752:          + 1           /* TME_RECODE_X86_REX */
        !          1753:          + 1           /* TME_RECODE_X86_OPCODE_BINOP_MOV */
        !          1754:          + 1           /* TME_RECODE_X86_MOD_OPREG_RM(TME_RECODE_X86_MOD_RM_EA_DISP32) */
        !          1755:          + 1           /* TME_RECODE_X86_SIB */
        !          1756:          + sizeof(tme_uint32_t))
        !          1757:        : (1            /* TME_RECODE_X86_OPCODE_BINOP_XCHG */
        !          1758:          + 1           /* TME_RECODE_X86_MOD_OPREG_RM(TME_RECODE_X86_MOD_RM_REG) */
        !          1759:          + 1           /* TME_RECODE_X86_OPCODE_BINOP_MOV */
        !          1760:          + 1           /* TME_RECODE_X86_MOD_OPREG_RM(TME_RECODE_X86_MOD_RM_EA_DISP32) */
        !          1761:          + 1           /* TME_RECODE_X86_SIB */
        !          1762:          + sizeof(tme_uint32_t)
        !          1763:          + 1           /* TME_RECODE_X86_OPCODE_BINOP_XCHG */
        !          1764:          + 1));        /* TME_RECODE_X86_MOD_OPREG_RM(TME_RECODE_X86_MOD_RM_REG) */
        !          1765: 
        !          1766:   /* size the x86 instruction to copy one host register to another: */
        !          1767:   insn_size_host_copy
        !          1768:     = ((TME_RECODE_SIZE_HOST > TME_RECODE_SIZE_32)     /* TME_RECODE_X86_REX */
        !          1769:        + 1             /* TME_RECODE_X86_OPCODE_BINOP_MOV */
        !          1770:        + 1);           /* TME_RECODE_X86_MOD_OPREG_RM(TME_RECODE_X86_MOD_RM_REG) */
        !          1771: 
        !          1772:   /* size the x86 instruction to load one host register from a guest
        !          1773:      register window: */
        !          1774:   insn_size_host_load
        !          1775:     = ((TME_RECODE_SIZE_HOST > TME_RECODE_SIZE_32)     /* TME_RECODE_X86_REX */
        !          1776:        + 1             /* TME_RECODE_X86_OPCODE_BINOP_MOV */
        !          1777:        + 1             /* TME_RECODE_X86_MOD_OPREG_RM(TME_RECODE_X86_MOD_RM_EA_DISP32) */
        !          1778:        + 1             /* TME_RECODE_X86_SIB */
        !          1779:        + sizeof(tme_uint32_t));
        !          1780: 
        !          1781:   /* start with no instructions: */
        !          1782:   insn_size = 0;
        !          1783: 
        !          1784:   /* add in cleaning all TME_RECODE_REG_HOST_UNDEF host registers at
        !          1785:      the worst size, storing them to alternating guest register
        !          1786:      windows (requiring the c register to be loaded with a window
        !          1787:      offset before each store): */
        !          1788:   insn_size
        !          1789:     += ((insn_size_window_offset
        !          1790:         + insn_size_host_store)
        !          1791:        * TME_RECODE_REG_HOST_UNDEF);
        !          1792: 
        !          1793:   /* add in the x86 instructions to load two double-host-size guest
        !          1794:      windowed registers for the two source operands, moving older
        !          1795:      guest register values to other host registers, and loading the
        !          1796:      source operands from alternating guest register windows: */
        !          1797:   insn_size
        !          1798:     += ((insn_size_host_copy
        !          1799:         + insn_size_window_offset
        !          1800:         + insn_size_host_load)
        !          1801:        * (2            /* double-host-size */
        !          1802:           * 2));       /* two source operands */
        !          1803: 
        !          1804:   /* add in the x86 instructions to move an older guest register
        !          1805:      to other host registers, for the destination operand: */
        !          1806:   insn_size += (insn_size_host_copy * 2);      /* double-host-size */
        !          1807: 
        !          1808:   /* if this is an ia32 host: */
        !          1809:   if (TME_RECODE_SIZE_HOST == TME_RECODE_SIZE_32) {
        !          1810: 
        !          1811:     /* add in the ia32 instructions for pushing the arguments for the
        !          1812:        guest function: */
        !          1813:     insn_size
        !          1814:       += (3            /* sub $imm8, %esp (double-host-size) */
        !          1815:          + 1           /* TME_RECODE_X86_OPCODE_PUSH_Gv */
        !          1816:          + 1           /* TME_RECODE_X86_OPCODE_PUSH_Gv (double-host-size) */
        !          1817:          + 1           /* TME_RECODE_X86_OPCODE_PUSH_Gv */
        !          1818:          + 1);         /* TME_RECODE_X86_OPCODE_PUSH_Gv (struct tme_ic *) */
        !          1819: 
        !          1820:     /* add in the ia32 instructions for calling the guest function: */
        !          1821:     insn_size
        !          1822:       += (1            /* TME_RECODE_X86_OPCODE_CALL_RELz */
        !          1823:          + sizeof(tme_int32_t));
        !          1824: 
        !          1825:     /* add in the ia32 instructions for removing the guest function
        !          1826:        arguments from the stack: */
        !          1827:     insn_size += 3;    /* sub $imm8, %esp (double-host-size) */
        !          1828:   }
        !          1829: 
        !          1830:   /* otherwise, this is an x86-64 host: */
        !          1831:   else {
        !          1832: 
        !          1833:     /* add in the x86-64 instructions for moving host registers into
        !          1834:        arguments for the guest function: */
        !          1835:     insn_size
        !          1836:       += (insn_size_host_copy
        !          1837:          * ((2         /* double-host-size */
        !          1838:              * 2)      /* two source operands */
        !          1839:             + 1));     /* struct tme_ic */
        !          1840: 
        !          1841:     /* add in the x86-64 instructions for calling the guest function: */
        !          1842:     insn_size
        !          1843:       += (1            /* TME_RECODE_X86_REX */
        !          1844:          + 1           /* TME_RECODE_X86_OPCODE_MOV_Iv_Gv */
        !          1845:          + TME_BIT(TME_RECODE_SIZE_HOST - TME_RECODE_SIZE_8)
        !          1846:          + 1           /* TME_RECODE_X86_OPCODE_GRP5 */
        !          1847:          + 1);         /* TME_RECODE_X86_MOD_OPREG_RM */         
        !          1848:   }
        !          1849: 
        !          1850:   /* add in the x86 instructions to move a double-host-size guest
        !          1851:      function return value from d:a into ax:bp: */
        !          1852:   insn_size += (insn_size_host_copy * 2); /* double-host-size */
        !          1853: 
        !          1854:   /* since the worst-case TME_RECODE_OPCODE_GUEST insn is also the
        !          1855:      last insn in a thunk, add in the x86 instructions for cleaning
        !          1856:      the host register(s) holding the destination operand to a guest
        !          1857:      register window, and for jumping to the instructions thunk
        !          1858:      epilogue: */
        !          1859:   insn_size
        !          1860:     += (insn_size_window_offset
        !          1861:        + (insn_size_host_store
        !          1862:           * 2)                 /* double-host-size */
        !          1863:        + 1                     /* TME_RECODE_X86_OPCODE_JMP_RELz */
        !          1864:        + sizeof(tme_int32_t));
        !          1865: 
        !          1866:   /* check the value of TME_RECODE_HOST_INSN_SIZE_MAX: */
        !          1867:   if (insn_size > TME_RECODE_HOST_INSN_SIZE_MAX) {
        !          1868:     abort();
        !          1869:   }
        !          1870: }
        !          1871: 
        !          1872: /* this host function returns the thunk offset for a new instructions
        !          1873:    thunk.  it returns less than zero when thunks memory is exhausted
        !          1874:    and all instructions thunks are flushed: */
        !          1875: tme_recode_thunk_off_t
        !          1876: tme_recode_host_insns_thunk_new(struct tme_recode_ic *ic,
        !          1877:                                const struct tme_recode_insns_group *insns_group)
        !          1878: {
        !          1879:   tme_uint8_t *thunk_bytes;
        !          1880:   tme_recode_thunk_off_t thunk_off;
        !          1881:   struct tme_recode_insn *insn;
        !          1882:   struct tme_recode_insn *insns_end;
        !          1883: 
        !          1884:   /* if we don't have enough room to make a maximum-sized thunk: */
        !          1885:   if (!tme_recode_host_thunk_start(ic)) {
        !          1886: 
        !          1887:     /* invalidate all instructions thunks: */
        !          1888:     tme_recode_insns_thunk_invalidate_all(ic);
        !          1889:     return (-1);
        !          1890:   }
        !          1891: 
        !          1892:   /* start more instructions: */
        !          1893:   tme_recode_x86_insns_start(ic, thunk_bytes);
        !          1894:   thunk_off = tme_recode_build_to_thunk_off(ic, thunk_bytes);
        !          1895: 
        !          1896:   /* make the chain in: */
        !          1897:   _tme_recode_x86_chain_in(ic, insns_group);
        !          1898: 
        !          1899:   /* the c register doesn't hold the base offset of any guest register
        !          1900:      window: */
        !          1901:   ic->tme_recode_x86_ic_thunks_reg_guest_window_c = TME_RECODE_REG_GUEST_WINDOW_UNDEF;
        !          1902: 
        !          1903:   /* make sure there isn't any leftover if/else insn state: */
        !          1904:   assert (ic->tme_recode_x86_ic_thunks_build_if == NULL
        !          1905:          && ic->tme_recode_x86_ic_thunks_build_else == NULL);
        !          1906: 
        !          1907:   /* emit all of the instructions: */
        !          1908:   insn = insns_group->tme_recode_insns_group_insns;
        !          1909:   insns_end = insns_group->tme_recode_insns_group_insns_end;
        !          1910:   do {
        !          1911:     _tme_recode_x86_insn_emit(ic, insn);
        !          1912:   } while (++insn < insns_end);
        !          1913: 
        !          1914:   /* make sure there isn't any leftover if/else insn state: */
        !          1915:   assert (ic->tme_recode_x86_ic_thunks_build_if == NULL
        !          1916:          && ic->tme_recode_x86_ic_thunks_build_else == NULL);
        !          1917: 
        !          1918:   /* free all host registers: */
        !          1919:   tme_recode_regs_host_free_many(ic, TME_RECODE_REG_HOST(0));
        !          1920: 
        !          1921:   /* make the chain out: */
        !          1922:   _tme_recode_x86_chain_out(ic, insns_group);
        !          1923: 
        !          1924:   /* finish and return this thunk: */
        !          1925:   tme_recode_host_thunk_finish(ic);
        !          1926:   return (thunk_off);
        !          1927: }

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.