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