Annotation of tme/libtme/host/x86/rc-x86-insns.c, revision 1.1.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.