Annotation of tme/libtme/host/x86/rc-x86-conds.c, revision 1.1.1.1

1.1       root        1: /* $Id: rc-x86-conds.c,v 1.3 2009/09/07 15:10:10 fredette Exp $ */
                      2: 
                      3: /* libtme/host/x86/rc-x86-conds.c - x86 host recode conditions support: */
                      4: 
                      5: /*
                      6:  * Copyright (c) 2008 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-conds.c,v 1.3 2009/09/07 15:10:10 fredette Exp $");
                     37: 
                     38: /* macros: */
                     39: 
                     40: /* the fields of a conditions simple value: */
                     41: #define TME_RECODE_X86_CONDS_SIMPLE_Z          (1 << 0)
                     42: #define TME_RECODE_X86_CONDS_SIMPLE_ENABLE     (1 << 1)
                     43: #define TME_RECODE_X86_CONDS_SIMPLE_FLAGS_MASK ((tme_uint32_t) 0xfffffffc)
                     44: 
                     45: /* this collects up to one host register's worth of guest flags into
                     46:    the c register: */
                     47: static unsigned int
                     48: _tme_recode_x86_conds_flags_collect(struct tme_recode_ic *ic,
                     49:                                    tme_recode_uguest_t conds_group_flags_mask,
                     50:                                    unsigned long disp,
                     51:                                    unsigned int reg_x86_load)
                     52: {
                     53:   tme_uint8_t *thunk_bytes;
                     54:   unsigned int size;
                     55:   tme_uint8_t opcode;
                     56:   tme_uint8_t rex;
                     57:   unsigned int count_bits_other;
                     58:   unsigned int count_shift;
                     59:   unsigned int count_bits_flags;
                     60: 
                     61:   /* we must be collecting some flags, and they must fit into a host
                     62:      register: */
                     63:   assert (conds_group_flags_mask != 0);
                     64:   assert (TME_RECODE_SIZE_FITS(conds_group_flags_mask, TME_RECODE_SIZE_HOST));
                     65: 
                     66:   /* start more instructions: */
                     67:   tme_recode_x86_insns_start(ic, thunk_bytes);
                     68: 
                     69:   /* we will do the collecting using either host-size instructions or,
                     70:      if the flags fit into 32 bits, 32-bit instructions: */
                     71:   size = TME_RECODE_SIZE_HOST;
                     72:   if (TME_RECODE_SIZE_FITS(conds_group_flags_mask, TME_RECODE_SIZE_32)) {
                     73:     size = TME_RECODE_SIZE_32;
                     74:   }
                     75: 
                     76:   /* assume that we will use a normal mov instruction to load the
                     77:      flags into the load register.  this will load all of that
                     78:      register with variable bits: */
                     79:   opcode = (TME_RECODE_X86_OPCODE_BINOP_MOV + TME_RECODE_X86_OPCODE_BINOP_Ev_Gv);
                     80:   count_bits_other = TME_BIT(size);
                     81: 
                     82:   /* if the flags fit into 16 bits: */
                     83:   if (TME_RECODE_SIZE_FITS(conds_group_flags_mask, TME_RECODE_SIZE_16)) {
                     84: 
                     85:     /* emit the opcode escape for a movzw or a movzb: */
                     86:     *(thunk_bytes++) = TME_RECODE_X86_OPCODE_ESC_0F;
                     87: 
                     88:     /* assume that we will use a movzw instruction to load the flags
                     89:        into the load register: */
                     90:     opcode = TME_RECODE_X86_OPCODE0F_MOVZ_Ew_Gv;
                     91:     count_bits_other = TME_BIT(TME_RECODE_SIZE_16);
                     92: 
                     93:     /* if the flags fit into eight bits: */
                     94:     if (TME_RECODE_SIZE_FITS(conds_group_flags_mask, TME_RECODE_SIZE_8)) {
                     95: 
                     96:       /* we will use a movzb instruction to load the flags into the
                     97:         load register: */
                     98:       opcode = TME_RECODE_X86_OPCODE0F_MOVZ_Eb_Gv;
                     99:       count_bits_other = TME_BIT(TME_RECODE_SIZE_8);
                    100:     }
                    101:   }
                    102: 
                    103:   /* emit the (remainder of the) instruction to load the flags into
                    104:      the load register: */
                    105:   rex = (TME_RECODE_X86_REX_R(size, reg_x86_load)
                    106:         + TME_RECODE_X86_REX_B(0, TME_RECODE_X86_REG_IC));
                    107:   if (rex != 0) {
                    108:     *(thunk_bytes++) = rex;
                    109:   }
                    110:   thunk_bytes[0] = opcode;
                    111:   thunk_bytes
                    112:     = _tme_recode_x86_emit_ic_modrm(thunk_bytes + 1,
                    113:                                    disp,
                    114:                                    reg_x86_load);
                    115: 
                    116:   /* initially, we haven't collected any flags yet, and we don't have
                    117:      any previous flags to shift off: */
                    118:   count_bits_flags = 0;
                    119:   count_shift = 0;
                    120: 
                    121:   /* while we still have flags to collect: */
                    122:   for (; conds_group_flags_mask != 0; ) {
                    123: 
                    124:     /* shift trailing zeroes off of the flags mask, adding them to the
                    125:        shift count: */
                    126:     for (; (conds_group_flags_mask & 1) == 0; ) {
                    127:       count_shift++;
                    128:       conds_group_flags_mask >>= 1;
                    129:     }
                    130: 
                    131:     /* if we have to shift the load register to get to the next flags
                    132:        to collect: */
                    133:     if (count_shift > 0) {
                    134: 
                    135:       /* shift the load register to the right: */
                    136:       rex = TME_RECODE_X86_REX_R(size, reg_x86_load);
                    137:       if (rex != 0) {
                    138:        *(thunk_bytes++) = rex;
                    139:       }
                    140:       thunk_bytes[0] = TME_RECODE_X86_OPCODE_GRP2_Ib_Ev;
                    141:       thunk_bytes[1] = TME_RECODE_X86_MOD_OPREG_RM(TME_RECODE_X86_MOD_RM_REG(reg_x86_load),
                    142:                                                   TME_RECODE_X86_OPCODE_GRP2_SHR);
                    143:       thunk_bytes[2] = count_shift;
                    144:       thunk_bytes += 3;
                    145:     }
                    146: 
                    147:     /* update the count of non-flags bits in the load register: */
                    148:     count_bits_other -= count_shift;
                    149: 
                    150:     /* reset the shift count: */
                    151:     count_shift = 0;
                    152: 
                    153:     /* shift trailing ones off of the flags mask, adding them to the
                    154:        shift count: */
                    155:     for (; (conds_group_flags_mask & 1) == 1; ) {
                    156:       count_shift++;
                    157:       conds_group_flags_mask >>= 1;
                    158:     }
                    159: 
                    160:     /* update the count of non-flags bits in the load register: */
                    161:     count_bits_other -= count_shift;
                    162: 
                    163:     /* update (in advance) the count of flags bits that we have
                    164:        collected in the c register, and make sure it's still within
                    165:        the maximum of 32 bits allowed by recode backends: */
                    166:     count_bits_flags += count_shift;
                    167:     assert (count_bits_flags <= (sizeof(tme_uint32_t) * 8));
                    168: 
                    169:     /* if the load register is not the c register: */
                    170:     if (reg_x86_load != TME_RECODE_X86_REG_C) {
                    171: 
                    172:       /* use the shrd instruction to shift the flags being collected
                    173:         out of the load register and into the c register.  NB we do
                    174:         this with 32-bit instructions because recode backends aren't
                    175:         required to handle any condition that tests more than 32
                    176:         flags: */
                    177:       rex
                    178:        = (TME_RECODE_X86_REX_R(TME_RECODE_SIZE_32, reg_x86_load)
                    179:           + TME_RECODE_X86_REX_B(TME_RECODE_SIZE_32, TME_RECODE_X86_REG_C));
                    180:       if (rex != 0) {
                    181:        *(thunk_bytes++) = rex;
                    182:       }
                    183:       thunk_bytes[0] = TME_RECODE_X86_OPCODE_ESC_0F;
                    184:       thunk_bytes[1] = TME_RECODE_X86_OPCODE0F_SHRD_Ib_Gv_Ev;
                    185:       thunk_bytes[2]
                    186:        = TME_RECODE_X86_MOD_OPREG_RM(TME_RECODE_X86_MOD_RM_REG(TME_RECODE_X86_REG_C),
                    187:                                      reg_x86_load);
                    188:       thunk_bytes[3] = count_shift;
                    189:       thunk_bytes += 4;
                    190:     }
                    191: 
                    192:     /* otherwise, the load register is the c register: */
                    193:     else {
                    194: 
                    195:       /* all of the flags must be contiguous, so we can't have any
                    196:         more to collect: */
                    197:       assert (conds_group_flags_mask == 0);
                    198: 
                    199:       /* if there are any non-flags bits left in %ecx: */
                    200:       if (count_bits_other > 0
                    201:          && count_bits_flags < 32) {
                    202: 
                    203:        /* emit an and $imm8s32, %ecx or an and $imm32, %ecx to clear
                    204:           those non-flags bits: */
                    205:        thunk_bytes[0]
                    206:          = (count_bits_flags < 8
                    207:             ? TME_RECODE_X86_OPCODE_GRP1_Ib_Ev
                    208:             : TME_RECODE_X86_OPCODE_GRP1_Iz_Ev);
                    209:        thunk_bytes[1]
                    210:          = TME_RECODE_X86_MOD_OPREG_RM(TME_RECODE_X86_MOD_RM_REG(TME_RECODE_X86_REG_C),
                    211:                                        TME_RECODE_X86_OPCODE_GRP1_BINOP(TME_RECODE_X86_OPCODE_BINOP_AND));
                    212:        *((tme_uint32_t *) &thunk_bytes[2]) = ((((tme_uint32_t) 1) << count_bits_flags) - 1);
                    213:        thunk_bytes += 2 + (count_bits_flags < 8 ? sizeof(tme_int8_t) : sizeof(tme_uint32_t));
                    214:       }
                    215:     }
                    216:   }
                    217: 
                    218:   /* finish these instructions: */
                    219:   tme_recode_x86_insns_finish(ic, thunk_bytes);
                    220: 
                    221:   /* return the number of flags collected: */
                    222:   return (count_bits_flags);
                    223: }
                    224: 
                    225: /* this host function returns a new conditions thunk: */
                    226: struct tme_recode_conds_thunk *
                    227: tme_recode_host_conds_thunk_new(struct tme_recode_ic *ic,
                    228:                                const struct tme_recode_conds_group *conds_group)
                    229: {
                    230:   struct tme_recode_conds_thunk *conds_thunk;
                    231:   unsigned int size_conds;
                    232:   tme_uint8_t *thunk_bytes;
                    233:   void *conds_array;
                    234:   tme_uint32_t conds_group_flags_index_max;
                    235:   tme_uint32_t conds_group_flags_index;
                    236:   tme_recode_uguest_t conds_group_flags;
                    237:   tme_uint32_t conds;
                    238:   unsigned int cond;
                    239:   tme_recode_uguest_t conds_group_flags_mask;
                    240:   unsigned int count_shift;
                    241:   int noncontiguous;
                    242:   unsigned int reg_x86_load;
                    243:   unsigned int count_bits_flags;
                    244:   tme_uint8_t opcode;
                    245:   tme_uint32_t bitwise_mask;
                    246:   tme_uint32_t simple;
                    247: 
                    248:   /* start the new conditions thunk: */
                    249:   if (!tme_recode_host_thunk_start(ic)) {
                    250:     abort();
                    251:   }
                    252:   assert (conds_group->tme_recode_conds_group_cond_count > 0);
                    253:   assert (conds_group->tme_recode_conds_group_cond_count <= 32);
                    254:   conds_thunk
                    255:     = ((struct tme_recode_conds_thunk *) 
                    256:        tme_malloc0(sizeof(struct tme_recode_conds_thunk)
                    257:                   + (sizeof(conds_thunk->tme_recode_x86_conds_thunk_simple[0])
                    258:                      * conds_group->tme_recode_conds_group_cond_count)));
                    259:   conds_thunk->tme_recode_x86_conds_thunk_subs
                    260:     = tme_recode_build_to_thunk_off(ic, ic->tme_recode_ic_thunk_build_next);
                    261:   conds_thunk->tme_recode_conds_thunk_flags_offset
                    262:     = tme_recode_flags_reg_offset(conds_group->tme_recode_conds_group_flags_reg_size,
                    263:                                  conds_group->tme_recode_conds_group_flags_reg);
                    264:   conds_thunk->tme_recode_conds_thunk_flags
                    265:     = conds_group->tme_recode_conds_group_flags;
                    266: 
                    267:   /* get the size of the word used to hold the conditions bitmap
                    268:      array.  we need one bit for each condition, and one recode
                    269:      conditions thunk may not handle more than 32 conditions: */
                    270:   size_conds
                    271:     = (conds_group->tme_recode_conds_group_cond_count <= 8
                    272:        ? TME_RECODE_SIZE_8
                    273:        : conds_group->tme_recode_conds_group_cond_count <= 16
                    274:        ? TME_RECODE_SIZE_16
                    275:        : TME_RECODE_SIZE_32);
                    276: 
                    277:   /* get the number of flags combinations tested by the conditions in
                    278:      this group: */
                    279:   conds_group_flags_index_max = tme_recode_conds_group_flags_index_max(conds_group);
                    280: 
                    281:   /* allocate the conditions bitmap array: */
                    282:   conds_array
                    283:     = tme_malloc(TME_BIT(size_conds - TME_RECODE_SIZE_8)
                    284:                 * (conds_group_flags_index_max + 1));
                    285: 
                    286:   /* loop over all of the combinations of flags tested by the
                    287:      conditions in this group: */
                    288:   conds_group_flags_index = 0;
                    289:   do {
                    290: 
                    291:     /* get this combination of flags: */
                    292:     conds_group_flags = tme_recode_conds_group_flags_from_index(conds_group, conds_group_flags_index);
                    293: 
                    294:     /* make the bitmap of conditions that are true for this
                    295:        combination of flags: */
                    296:     conds = 0;
                    297:     cond = 0;
                    298:     do {
                    299:       if ((*conds_group->tme_recode_conds_group_guest_func)
                    300:          (conds_group_flags, cond)) {
                    301:        conds += (1 << cond);
                    302:       }
                    303:     } while (++cond < conds_group->tme_recode_conds_group_cond_count);
                    304: 
                    305:     /* store this bitmap: */
                    306:     switch (size_conds) {
                    307:     case TME_RECODE_SIZE_8: ((tme_uint8_t *) conds_array)[conds_group_flags_index] = conds; break;
                    308:     case TME_RECODE_SIZE_16: ((tme_uint16_t *) conds_array)[conds_group_flags_index] = conds; break;
                    309:     default: assert(FALSE);
                    310:     case TME_RECODE_SIZE_32: ((tme_uint32_t *) conds_array)[conds_group_flags_index] = conds; break;
                    311:     }
                    312: 
                    313:   } while (conds_group_flags_index++ != conds_group_flags_index_max);
                    314: 
                    315:   /* get the mask of flags tested by the conditions in this group, and
                    316:      the number of trailing zeroes in the mask: */
                    317:   conds_group_flags_mask = conds_group->tme_recode_conds_group_flags;
                    318:   count_shift = _tme_recode_x86_ffs(conds_group_flags_mask);
                    319: 
                    320:   /* see if the flags tested by the conditions in this group are all
                    321:      contiguous: */
                    322:   noncontiguous
                    323:     = (((conds_group_flags_mask
                    324:         + (((tme_recode_uguest_t) 1) << count_shift))
                    325:        & conds_group_flags_mask) != 0);
                    326: 
                    327:   /* truncate the number of trailing zeroes in the mask to a multiple
                    328:      of eight, shift that many whole bytes off of the flags mask, and
                    329:      set the offset and size of the part of the flags register tested
                    330:      by this thunk: */
                    331:   count_shift &= 0 - (unsigned int) 8;
                    332:   conds_group_flags_mask >>= count_shift;
                    333:   conds_thunk->tme_recode_x86_conds_thunk_flags_offset
                    334:     = (conds_thunk->tme_recode_conds_thunk_flags_offset
                    335:        + (count_shift / 8));
                    336:   conds_thunk->tme_recode_x86_conds_thunk_flags_size
                    337:     = (TME_RECODE_SIZE_FITS(conds_group_flags_mask, TME_RECODE_SIZE_8)
                    338:        ? TME_RECODE_SIZE_8
                    339:        : TME_RECODE_SIZE_FITS(conds_group_flags_mask, TME_RECODE_SIZE_16)
                    340:        ? TME_RECODE_SIZE_16
                    341:        : TME_RECODE_SIZE_FITS(conds_group_flags_mask, TME_RECODE_SIZE_32)
                    342:        ? TME_RECODE_SIZE_32
                    343:        : TME_RECODE_SIZE_FITS(conds_group_flags_mask, TME_RECODE_SIZE_HOST)
                    344:        ? TME_RECODE_SIZE_HOST
                    345:        : TME_RECODE_SIZE_HOST + 1);
                    346: 
                    347:   /* we always collect the flags into the c register, which is always
                    348:      free.  assume that we can also use the c register to load the
                    349:      flags: */
                    350:   reg_x86_load = TME_RECODE_X86_REG_C;
                    351: 
                    352:   /* if the flags tested by the conditions in this group aren't
                    353:      contiguous, or if the flags are double-host-size: */
                    354:   if (noncontiguous
                    355:       || TME_RECODE_SIZE_IS_DOUBLE_HOST(conds_thunk->tme_recode_x86_conds_thunk_flags_size)) {
                    356: 
                    357:     /* push the d register, and use it to load the flags while we
                    358:        collect them into the c register: */
                    359:     reg_x86_load = TME_RECODE_X86_REG_D;
                    360:     tme_recode_x86_insns_start(ic, thunk_bytes);
                    361:     _tme_recode_x86_emit_reg_push(thunk_bytes, reg_x86_load);
                    362:     tme_recode_x86_insns_finish(ic, thunk_bytes);
                    363:   }
                    364: 
                    365:   /* load and collect flags from the (least-significant half of
                    366:      the) flags register tested by this thunk: */
                    367:   count_bits_flags
                    368:     = _tme_recode_x86_conds_flags_collect(ic,
                    369:                                          (conds_group_flags_mask
                    370:                                           & (TME_SHIFT(tme_recode_uguest_t,
                    371:                                                        1,
                    372:                                                        <<,
                    373:                                                        TME_BIT(TME_RECODE_SIZE_HOST))
                    374:                                              - 1)),
                    375:                                          conds_thunk->tme_recode_x86_conds_thunk_flags_offset,
                    376:                                          reg_x86_load);
                    377: 
                    378:   /* if the flags are double-host-size, load and collect flags from
                    379:      the most-significant half of the flags register tested by this
                    380:      thunk: */
                    381:   if (TME_RECODE_SIZE_IS_DOUBLE_HOST(conds_thunk->tme_recode_x86_conds_thunk_flags_size)) {
                    382:     count_bits_flags
                    383:       += _tme_recode_x86_conds_flags_collect(ic,
                    384:                                             TME_SHIFT(tme_recode_uguest_t,
                    385:                                                       conds_group_flags_mask,
                    386:                                                       >>,
                    387:                                                       TME_BIT(TME_RECODE_SIZE_HOST)),
                    388:                                             (conds_thunk->tme_recode_x86_conds_thunk_flags_offset
                    389:                                              + TME_BIT(TME_RECODE_SIZE_HOST
                    390:                                                        - TME_RECODE_SIZE_8)),
                    391:                                             reg_x86_load);
                    392:   }
                    393: 
                    394:   /* start more instructions: */
                    395:   tme_recode_x86_insns_start(ic, thunk_bytes);
                    396: 
                    397:   /* when we don't use the c register for loading, the flags get
                    398:      collected into the c register by shifting them in from the right,
                    399:      so we have to shift them down to start at bit zero: */
                    400:   if (reg_x86_load != TME_RECODE_X86_REG_C) {
                    401: 
                    402:     /* pop the register that we used for loading: */
                    403:     _tme_recode_x86_emit_reg_pop(thunk_bytes, reg_x86_load);
                    404: 
                    405:     /* if we collected less than 32 flags: */
                    406:     if (count_bits_flags < (sizeof(tme_uint32_t) * 8)) {
                    407: 
                    408:       /* shift the collected flags down: */
                    409:       thunk_bytes[0] = TME_RECODE_X86_OPCODE_GRP2_Ib_Ev;
                    410:       thunk_bytes[1] = TME_RECODE_X86_MOD_OPREG_RM(TME_RECODE_X86_MOD_RM_REG(TME_RECODE_X86_REG_C),
                    411:                                                   TME_RECODE_X86_OPCODE_GRP2_SHR);
                    412:       thunk_bytes[2] = (sizeof(tme_uint32_t) * 8) - count_bits_flags;
                    413:       thunk_bytes += 3;
                    414:     }
                    415:   }
                    416: 
                    417:   /* if this is an x86-64 host: */
                    418:   if (TME_RECODE_SIZE_HOST > TME_RECODE_SIZE_32) {
                    419: 
                    420:     /* to keep things simple, we really want to limit conditions subs
                    421:        to the c register except in unusual cases.  on x86-64, this
                    422:        means that we have to add the 64-bit base of the conditions
                    423:        bitmap array to the c register from memory, after scaling the
                    424:        flags index in c by the size of the entries in that array: */
                    425: 
                    426:     /* dispatch on the size of the entries in the array: */
                    427:     switch (size_conds) {
                    428:     default: assert(FALSE);
                    429:     case TME_RECODE_SIZE_8:
                    430: 
                    431:       /* make sure that the most-significant 32 bits of %rcx are
                    432:         zero: */
                    433:       thunk_bytes[0] = (TME_RECODE_X86_OPCODE_BINOP_MOV + TME_RECODE_X86_OPCODE_BINOP_Gv_Ev);
                    434:       thunk_bytes[1] = TME_RECODE_X86_MOD_OPREG_RM(TME_RECODE_X86_MOD_RM_REG(TME_RECODE_X86_REG_C),
                    435:                                                   TME_RECODE_X86_REG_C);
                    436:       thunk_bytes += 2;
                    437:       break;
                    438: 
                    439:     case TME_RECODE_SIZE_16:
                    440: 
                    441:       /* shift %ecx by one: */
                    442:       thunk_bytes[0] = TME_RECODE_X86_OPCODE_GRP2_1_Ev;
                    443:       thunk_bytes[1] = TME_RECODE_X86_MOD_OPREG_RM(TME_RECODE_X86_MOD_RM_REG(TME_RECODE_X86_REG_C),
                    444:                                                   TME_RECODE_X86_OPCODE_GRP2_SHL);
                    445:       thunk_bytes += 2;
                    446:       break;
                    447: 
                    448:     case TME_RECODE_SIZE_32:
                    449: 
                    450:       /* shift %ecx by two: */
                    451:       thunk_bytes[0] = TME_RECODE_X86_OPCODE_GRP2_Ib_Ev;
                    452:       thunk_bytes[1] = TME_RECODE_X86_MOD_OPREG_RM(TME_RECODE_X86_MOD_RM_REG(TME_RECODE_X86_REG_C),
                    453:                                                   TME_RECODE_X86_OPCODE_GRP2_SHL);
                    454:       thunk_bytes[2] = (TME_RECODE_SIZE_32 - TME_RECODE_SIZE_8);
                    455:       thunk_bytes += 3;
                    456:     }
                    457: 
                    458:     /* add the base of the conditions bitmap array to %rcx, from the
                    459:        (%rip-relative) value stored after the next movzbl/movzwl/movl
                    460:        and ret instructions: */
                    461:     thunk_bytes[0] = TME_RECODE_X86_REX_R(TME_RECODE_SIZE_HOST, TME_RECODE_X86_REG_C);
                    462:     thunk_bytes[1] = (TME_RECODE_X86_OPCODE_BINOP_ADD + TME_RECODE_X86_OPCODE_BINOP_Ev_Gv);
                    463:     thunk_bytes[2] = TME_RECODE_X86_MOD_OPREG_RM(TME_RECODE_X86_MOD_RM_EA(TME_RECODE_X86_EA_BASE_NONE),
                    464:                                                 TME_RECODE_X86_REG_C);
                    465:     *((tme_int32_t *) &thunk_bytes[3])
                    466:       = ((size_conds < TME_RECODE_SIZE_32)     /* TME_RECODE_X86_OPCODE_ESC_0F */
                    467:         + 1                                    /* opcode */
                    468:         + 1                                    /* ModR/M */
                    469:         + 1);                                  /* TME_RECODE_X86_OPCODE_RET */
                    470:     thunk_bytes += 3 + sizeof(tme_int32_t);
                    471:   }
                    472: 
                    473:   /* emit the opcode(s) for a movzbl/movzwl/movl instruction,
                    474:      depending on the size of the entries in the conditions bitmap
                    475:      array: */
                    476:   opcode = (TME_RECODE_X86_OPCODE_BINOP_MOV + TME_RECODE_X86_OPCODE_BINOP_Ev_Gv);
                    477:   if (size_conds < TME_RECODE_SIZE_32) {
                    478:     *(thunk_bytes++) = TME_RECODE_X86_OPCODE_ESC_0F;
                    479:     opcode 
                    480:       = (size_conds == TME_RECODE_SIZE_16
                    481:         ? TME_RECODE_X86_OPCODE0F_MOVZ_Ew_Gv
                    482:         : TME_RECODE_X86_OPCODE0F_MOVZ_Eb_Gv);
                    483:   }
                    484:   thunk_bytes[0] = opcode;
                    485: 
                    486:   /* if this is an x86-64 host: */
                    487:   if (TME_RECODE_SIZE_HOST > TME_RECODE_SIZE_32) {
                    488: 
                    489:     /* the complete address of the value we want to load is already in
                    490:        %rcx: */
                    491:     thunk_bytes[1] = TME_RECODE_X86_MOD_OPREG_RM(TME_RECODE_X86_MOD_RM_EA(TME_RECODE_X86_REG_C),
                    492:                                                 TME_RECODE_X86_REG_C);
                    493:     thunk_bytes += 2;
                    494:   }
                    495: 
                    496:   /* otherwise, this is an ia32 host: */
                    497:   else {
                    498: 
                    499:     /* if the conditions bitmap array has eight-bit entries: */
                    500:     if (size_conds == TME_RECODE_SIZE_8) {
                    501: 
                    502:       /* emit a ModR/M byte for %ecx as the base with a 32-bit
                    503:         displacement: */
                    504:       thunk_bytes[1] = TME_RECODE_X86_MOD_OPREG_RM(TME_RECODE_X86_MOD_RM_EA_DISP32(TME_RECODE_X86_REG_C),
                    505:                                                   TME_RECODE_X86_REG_C);
                    506:       thunk_bytes += 2;
                    507:     }
                    508: 
                    509:     /* otherwise, the conditions bitmap array has 16- or 32-bit entries: */
                    510:     else {
                    511: 
                    512:       /* emit a ModR/M byte and a scale-index-base byte for a scaled
                    513:         %ecx index with a 32-bit displacement: */
                    514:       thunk_bytes[1] = TME_RECODE_X86_MOD_OPREG_RM(TME_RECODE_X86_MOD_RM_EA_DISP32(TME_RECODE_X86_EA_BASE_SIB),
                    515:                                                   TME_RECODE_X86_REG_C);
                    516:       thunk_bytes[2] = TME_RECODE_X86_SIB(TME_RECODE_X86_SIB_BASE_NONE,
                    517:                                          TME_RECODE_X86_REG_C,
                    518:                                          TME_BIT(size_conds - TME_RECODE_SIZE_8));
                    519:       thunk_bytes += 3;
                    520:     }
                    521: 
                    522:     /* emit the 32-bit displacement to the conditions bitmap array: */
                    523:     *((tme_uint32_t *) thunk_bytes) = (unsigned long) conds_array;
                    524:     thunk_bytes += sizeof(tme_uint32_t);
                    525:   }
                    526: 
                    527:   /* return to the instruction thunk: */
                    528:   *(thunk_bytes++) = TME_RECODE_X86_OPCODE_RET;
                    529: 
                    530:   /* if this is an x86-64 host: */
                    531:   if (TME_RECODE_SIZE_HOST > TME_RECODE_SIZE_32) {
                    532: 
                    533:     /* store the 64-bit address of the conditions bitmap array at the
                    534:        end of the thunk: */
                    535:     memset(thunk_bytes, 0, TME_BIT(TME_RECODE_SIZE_HOST - TME_RECODE_SIZE_8));
                    536:     memcpy(thunk_bytes, &conds_array, sizeof(conds_array));
                    537:     thunk_bytes += TME_BIT(TME_RECODE_SIZE_HOST - TME_RECODE_SIZE_8);
                    538:   }
                    539: 
                    540:   /* finish these instructions: */
                    541:   tme_recode_x86_insns_finish(ic, thunk_bytes);
                    542: 
                    543:   /* finish this conditions thunk: */
                    544:   tme_recode_host_thunk_finish(ic);
                    545: 
                    546:   /* loop over all of the conditions: */
                    547:   cond = 0;
                    548:   do {
                    549:     
                    550:     /* assume that this condition can't be tested simply: */
                    551:     simple = !TME_RECODE_X86_CONDS_SIMPLE_ENABLE;
                    552: 
                    553:     /* we can test this condition simply if there is a mask of flags
                    554:        that can be tested under OR or NOR to determine the condition's
                    555:        state, and those flags will fit into a simple condition
                    556:        information word: */
                    557:     bitwise_mask
                    558:       = tme_recode_conds_simple_mask(conds_group,
                    559:                                     cond,
                    560:                                     &conds_group_flags_mask);
                    561:     conds_group_flags_mask >>= count_shift;
                    562:     if ((bitwise_mask
                    563:         & (TME_RECODE_BITWISE_OR
                    564:            | TME_RECODE_BITWISE_NOR)) != 0
                    565:        && (conds_group_flags_mask
                    566:            <= TME_FIELD_MASK_EXTRACTU(TME_RECODE_X86_CONDS_SIMPLE_FLAGS_MASK,
                    567:                                       TME_RECODE_X86_CONDS_SIMPLE_FLAGS_MASK))) {
                    568: 
                    569:       /* make the simple condition information word: */
                    570:       simple
                    571:        = (TME_RECODE_X86_CONDS_SIMPLE_ENABLE
                    572:           | ((bitwise_mask & TME_RECODE_BITWISE_NOR)
                    573:              ? TME_RECODE_X86_CONDS_SIMPLE_Z
                    574:              : 0));
                    575:       TME_FIELD_MASK_DEPOSITU(simple,
                    576:                              TME_RECODE_X86_CONDS_SIMPLE_FLAGS_MASK,
                    577:                              conds_group_flags_mask);
                    578:     }
                    579: 
                    580:     /* set the simple condition information word for this condition: */
                    581:     conds_thunk->tme_recode_x86_conds_thunk_simple[cond] = simple;
                    582: 
                    583:   } while (++cond < conds_group->tme_recode_conds_group_cond_count);
                    584: 
                    585:   /* return the conditions thunk: */
                    586:   return (conds_thunk);
                    587: }
                    588: 
                    589: /* this emits instructions to define the recode carry flag: */
                    590: static void
                    591: _tme_recode_x86_conds_defc(struct tme_recode_ic *ic,
                    592:                           const struct tme_recode_insn *insn)
                    593: {
                    594:   const struct tme_recode_conds_thunk *conds_thunk;
                    595:   unsigned int cond;
                    596:   tme_uint32_t simple;
                    597:   unsigned int size;
                    598:   tme_uint8_t *thunk_bytes;
                    599:   unsigned int disp;
                    600: 
                    601:   /* start more instructions: */
                    602:   tme_recode_x86_insns_start(ic, thunk_bytes);
                    603: 
                    604:   /* get the conditions thunk and the condition from the
                    605:      instruction: */
                    606:   conds_thunk = insn->tme_recode_insn_conds_thunk;
                    607:   cond = insn->tme_recode_insn_operand_src[0];
                    608: 
                    609:   /* if this condition is simple: */
                    610:   simple = conds_thunk->tme_recode_x86_conds_thunk_simple[cond];
                    611:   if (__tme_predict_true(simple & TME_RECODE_X86_CONDS_SIMPLE_ENABLE)) {
                    612: 
                    613:     /* emit a testb/testw/testl instruction to test the condition's
                    614:        simple mask in the guest flags register: */
                    615:     size = conds_thunk->tme_recode_x86_conds_thunk_flags_size;
                    616:     assert (size <= TME_RECODE_SIZE_32);
                    617:     if (__tme_predict_false(size == TME_RECODE_SIZE_16)) {
                    618:       *thunk_bytes = TME_RECODE_X86_PREFIX_OPSIZ;
                    619:       thunk_bytes++;
                    620:     }
                    621: #if (TME_RECODE_X86_OPCODE_GRP3_Ev - 1) != TME_RECODE_X86_OPCODE_GRP3_Eb
                    622: #error "TME_RECODE_X86_OPCODE_ values changed"
                    623: #endif
                    624:     thunk_bytes[0]
                    625:       = (TME_RECODE_X86_OPCODE_GRP3_Ev
                    626:         - ((size & 2) >> 1));
                    627:     thunk_bytes
                    628:       = _tme_recode_x86_emit_ic_modrm(thunk_bytes + 1,
                    629:                                      conds_thunk->tme_recode_x86_conds_thunk_flags_offset,
                    630:                                      TME_RECODE_X86_OPCODE_GRP3_TEST);
                    631:     *((tme_uint32_t *) thunk_bytes)
                    632:       = TME_FIELD_MASK_EXTRACTU(simple,
                    633:                                TME_RECODE_X86_CONDS_SIMPLE_FLAGS_MASK);
                    634:     thunk_bytes += TME_BIT(size - TME_RECODE_SIZE_8);
                    635:   }
                    636: 
                    637:   /* otherwise, this condition is not simple: */
                    638:   else {
                    639: 
                    640:     /* the conditions subs destroys the c register: */
                    641:     ic->tme_recode_x86_ic_thunks_reg_guest_window_c = TME_RECODE_REG_GUEST_WINDOW_UNDEF;
                    642: 
                    643:     /* emit a call to the conditions subs: */
                    644:     thunk_bytes[0] = TME_RECODE_X86_OPCODE_CALL_RELz;
                    645:     thunk_bytes += 1 + sizeof(tme_uint32_t);
                    646:     ((tme_uint32_t *) thunk_bytes)[-1]
                    647:       = (conds_thunk->tme_recode_x86_conds_thunk_subs
                    648:         - tme_recode_build_to_thunk_off(ic, thunk_bytes));
                    649: 
                    650:     /* emit a testl instruction to test the condition in the mask
                    651:        returned in %ecx: */
                    652:     *((tme_uint16_t *) thunk_bytes)
                    653:       = (TME_RECODE_X86_OPCODE_GRP3_Ev
                    654:         + (TME_RECODE_X86_MOD_OPREG_RM(TME_RECODE_X86_MOD_RM_REG(TME_RECODE_X86_REG_C),
                    655:                                        TME_RECODE_X86_OPCODE_GRP3_TEST)
                    656:            << 8));
                    657:     *((tme_uint32_t *) &thunk_bytes[2]) = ((tme_uint32_t) 1) << cond;
                    658:     thunk_bytes += 2 + sizeof(tme_uint32_t);
                    659: 
                    660:     /* if the test results in nonzero, the condition is true: */
                    661:     simple = !TME_RECODE_X86_CONDS_SIMPLE_Z;
                    662:   }
                    663: 
                    664:   /* if this instruction is negating the condition: */
                    665:   if (insn->tme_recode_insn_operand_src[1] < 0) {
                    666: 
                    667:     /* flip TME_RECODE_X86_CONDS_SIMPLE_Z: */
                    668:     simple = ~simple;
                    669:   }
                    670: 
                    671:   /* NB that a recode flag byte is zero if that flag is set, and one
                    672:      if it is clear, which seems backwards, but this allows us to set
                    673:      CF correctly by comparing a byte to 1.
                    674: 
                    675:      at this point, TME_RECODE_X86_CONDS_SIMPLE_Z is set if the
                    676:      condition is true if ZF is *set*, otherwise the condition is true
                    677:      if ZF is clear.  so we emit a setnz in the former case, otherwise
                    678:      we emit a setz: */
                    679: #if TME_RECODE_X86_CONDS_SIMPLE_Z != 1
                    680: #error "TME_RECODE_X86_CONDS_SIMPLE_Z value changed"
                    681: #endif
                    682:   thunk_bytes[0] = TME_RECODE_X86_OPCODE_ESC_0F;
                    683:   thunk_bytes[1]
                    684:     = TME_RECODE_X86_OPCODE0F_SETCC(TME_RECODE_X86_COND_Z
                    685:                                    + ((simple
                    686:                                        & TME_RECODE_X86_CONDS_SIMPLE_Z)
                    687:                                       * TME_RECODE_X86_COND_NOT));
                    688:   thunk_bytes += 2;
                    689:   disp = insn->tme_recode_insn_operand_dst;
                    690:   if (disp == 0) {
                    691:     *((tme_uint16_t *) thunk_bytes)
                    692:       = (TME_RECODE_X86_MOD_OPREG_RM(TME_RECODE_X86_MOD_RM_EA(TME_RECODE_X86_EA_BASE_SIB), 0)
                    693:         + (TME_RECODE_X86_SIB(TME_RECODE_X86_REG_SP, TME_RECODE_X86_SIB_INDEX_NONE, 1)
                    694:            << 8));
                    695:     thunk_bytes += 2;
                    696:   }
                    697:   else {
                    698:     *((tme_uint16_t *) thunk_bytes)
                    699:       = (TME_RECODE_X86_MOD_OPREG_RM(TME_RECODE_X86_MOD_RM_EA_DISP8(TME_RECODE_X86_EA_BASE_SIB), 0)
                    700:         + (TME_RECODE_X86_SIB(TME_RECODE_X86_REG_SP, TME_RECODE_X86_SIB_INDEX_NONE, 1)
                    701:            << 8));
                    702:     thunk_bytes[2] = disp;
                    703:     thunk_bytes += 3;
                    704:   }
                    705: 
                    706:   /* finish these instructions: */
                    707:   tme_recode_x86_insns_finish(ic, thunk_bytes);
                    708: }
                    709: 
                    710: /* this emits an instruction that defines CF with the recode carry flag: */
                    711: static void
                    712: _tme_recode_x86_conds_testc(struct tme_recode_ic *ic,
                    713:                            unsigned int disp)
                    714: {
                    715:   tme_uint8_t *thunk_bytes;
                    716: 
                    717:   /* start another instruction: */
                    718:   tme_recode_x86_insns_start(ic, thunk_bytes);
                    719: 
                    720:   /* the recode carry flag byte at disp(%sp) is zero if the recode
                    721:      carry flag is set, and one if it is clear.  comparing this byte
                    722:      to one will set CF appropriately: */
                    723:   if (__tme_predict_true(disp == 0)) {
                    724:     *((tme_uint32_t *) &thunk_bytes[0])
                    725:       = (TME_RECODE_X86_OPCODE_GRP1_Ib_Eb
                    726:         + (TME_RECODE_X86_MOD_OPREG_RM(TME_RECODE_X86_MOD_RM_EA(TME_RECODE_X86_EA_BASE_SIB),
                    727:                                        TME_RECODE_X86_OPCODE_GRP1_BINOP(TME_RECODE_X86_OPCODE_BINOP_CMP))
                    728:            << 8)
                    729:         + (TME_RECODE_X86_SIB(TME_RECODE_X86_REG_SP, TME_RECODE_X86_SIB_INDEX_NONE, 1)
                    730:            << 16)
                    731:         + (1 << 24));
                    732:     thunk_bytes += 4;
                    733:   }
                    734:   else {
                    735:     *((tme_uint32_t *) &thunk_bytes[0])
                    736:       = (TME_RECODE_X86_OPCODE_GRP1_Ib_Eb
                    737:         + (TME_RECODE_X86_MOD_OPREG_RM(TME_RECODE_X86_MOD_RM_EA_DISP8(TME_RECODE_X86_EA_BASE_SIB),
                    738:                                        TME_RECODE_X86_OPCODE_GRP1_BINOP(TME_RECODE_X86_OPCODE_BINOP_CMP))
                    739:            << 8)
                    740:         + (TME_RECODE_X86_SIB(TME_RECODE_X86_REG_SP, TME_RECODE_X86_SIB_INDEX_NONE, 1)
                    741:            << 16));
                    742:     assert (disp < 0x80);
                    743:     thunk_bytes[3] = disp;
                    744:     thunk_bytes[4] = 1;
                    745:     thunk_bytes += 5;
                    746:   }
                    747: 
                    748:   /* finish this instruction: */
                    749:   tme_recode_x86_insns_finish(ic, thunk_bytes);
                    750: }
                    751: 
                    752: #ifdef TME_RECODE_DEBUG
                    753: #include <stdio.h>
                    754: 
                    755: /* this host function dumps a conditions thunk: */
                    756: void
                    757: tme_recode_host_conds_thunk_dump(const struct tme_recode_ic *ic,
                    758:                                 const struct tme_recode_conds_thunk *conds_thunk,
                    759:                                 const char * const *cond_names)
                    760: {
                    761:   unsigned int cond;
                    762:   tme_uint32_t simple;
                    763: 
                    764:   printf("  x86 conds subs: x/10i %p\n",
                    765:         tme_recode_thunk_off_to_pointer(ic,
                    766:                                         conds_thunk->tme_recode_x86_conds_thunk_subs,
                    767:                                         void (*)(void)));
                    768:   printf("  x86 conds flags offset 0x%x bytes %u\n",
                    769:         conds_thunk->tme_recode_x86_conds_thunk_flags_offset,
                    770:         (1 << (conds_thunk->tme_recode_x86_conds_thunk_flags_size - TME_RECODE_SIZE_8)));
                    771:   for (cond = 0; cond_names[cond] != NULL; cond++) {
                    772:     printf("  x86 cond %2u (%s): ", cond, cond_names[cond]);
                    773: 
                    774:     /* if this condition is simple: */
                    775:     simple = conds_thunk->tme_recode_x86_conds_thunk_simple[cond];
                    776:     if (__tme_predict_true(simple & TME_RECODE_X86_CONDS_SIMPLE_ENABLE)) {
                    777:       printf("(flags & 0x%x) %c= 0\n",
                    778:             TME_FIELD_MASK_EXTRACTU(simple,
                    779:                                     TME_RECODE_X86_CONDS_SIMPLE_FLAGS_MASK),
                    780:             (simple & TME_RECODE_X86_CONDS_SIMPLE_Z
                    781:              ? '='
                    782:              : '!'));
                    783:     }
                    784:     else {
                    785:       printf("subs\n");
                    786:     }
                    787:   }
                    788: }
                    789: 
                    790: #endif /* TME_RECODE_DEBUG */

unix.superglobalmegacorp.com

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