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

1.1       root        1: /* $Id: rc-x86-rws.c,v 1.5 2010/06/05 19:14:40 fredette Exp $ */
                      2: 
                      3: /* libtme/host/x86/rc-x86-rws.c - x86 host recode reads and writes 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-rws.c,v 1.5 2010/06/05 19:14:40 fredette Exp $");
                     37: 
                     38: /* this emits instructions to do a byte swap in a host register: */
                     39: static tme_uint8_t *
                     40: _tme_recode_x86_rw_bswap(tme_uint8_t *thunk_bytes,
                     41:                         unsigned int size,
                     42:                         unsigned int reg_x86)
                     43: {
                     44:   unsigned int rex;
                     45: 
                     46:   /* NB: we don't have to worry about zero-truncation on an x86-64
                     47:      host; if this register is supposed to be sign-extended, we do
                     48:      that after all byte swapping: */
                     49: 
                     50:   /* if this is an eight-bit byte swap: */
                     51:   if (size == TME_RECODE_SIZE_8) {
                     52:     /* nothing to do */
                     53:   }
                     54: 
                     55:   /* otherwise, if this is a 16-bit byte swap: */
                     56:   else if (size == TME_RECODE_SIZE_16) {
                     57: 
                     58:     /* if this register has a high 8-bit encoding: */
                     59:     if (reg_x86 < TME_RECODE_X86_REG_SP) {
                     60: 
                     61:       /* emit an xchgb %regh, %regl: */
                     62:       thunk_bytes[0]
                     63:        = (TME_RECODE_X86_OPCODE_BINOP_XCHG
                     64:           + TME_RECODE_X86_OPCODE_BINOP_Gb_Eb);
                     65:       thunk_bytes[1]
                     66:        = TME_RECODE_X86_MOD_OPREG_RM(TME_RECODE_X86_MOD_RM_REG(reg_x86),
                     67:                                      TME_RECODE_X86_REG((TME_RECODE_X86_REG_SP + reg_x86)));
                     68:       thunk_bytes += 2;
                     69:     }
                     70: 
                     71:     /* otherwise, this register doesn't have a high 8-bit encoding: */
                     72:     else {
                     73: 
                     74:       /* emit a rorw $8, %reg: */
                     75:       thunk_bytes[0] = TME_RECODE_X86_PREFIX_OPSIZ;
                     76:       thunk_bytes[1] = TME_RECODE_X86_OPCODE_GRP2_Ib_Ev;
                     77:       thunk_bytes[2]
                     78:        = TME_RECODE_X86_MOD_OPREG_RM(TME_RECODE_X86_MOD_RM_REG(reg_x86),
                     79:                                      TME_RECODE_X86_OPCODE_GRP2_ROR);
                     80:       thunk_bytes[3] = 8;
                     81:       thunk_bytes += 4;
                     82:     }
                     83:   }
                     84: 
                     85:   /* otherwise, this is a 32-bit swap, and/or a host-sized byte
                     86:      swap: */
                     87:   else {
                     88: 
                     89:     /* if the bswap instruction is available: */
                     90:     if (1) {
                     91: 
                     92:       /* emit a bswap %reg: */
                     93:       rex = TME_RECODE_X86_REX_R(size, reg_x86);
                     94:       if (rex != 0) {
                     95:        *(thunk_bytes++) = rex;
                     96:       }
                     97:       thunk_bytes[0] = TME_RECODE_X86_OPCODE_ESC_0F;
                     98:       thunk_bytes[1] = TME_RECODE_X86_OPCODE0F_BSWAP(reg_x86);
                     99:       thunk_bytes += 2;
                    100:     }
                    101: 
                    102:     /* otherwise, the bswap instruction is not available: */
                    103:     else {
                    104: 
                    105:       /* this must be a 32-bit swap: */
                    106:       assert (size == TME_RECODE_SIZE_32);
                    107: 
                    108:       /* emit:
                    109:         rorw $8, %reg
                    110:         rorl $16, %reg
                    111:         rorw $8, %reg
                    112:       */
                    113:       thunk_bytes[0] = TME_RECODE_X86_PREFIX_OPSIZ;
                    114:       thunk_bytes[1] = TME_RECODE_X86_OPCODE_GRP2_Ib_Ev;
                    115:       thunk_bytes[2]
                    116:        = TME_RECODE_X86_MOD_OPREG_RM(TME_RECODE_X86_MOD_RM_REG(reg_x86),
                    117:                                      TME_RECODE_X86_OPCODE_GRP2_ROR);
                    118:       thunk_bytes[3] = 8;
                    119:       thunk_bytes += 4;
                    120:       thunk_bytes[0] = TME_RECODE_X86_OPCODE_GRP2_Ib_Ev;
                    121:       thunk_bytes[1]
                    122:        = TME_RECODE_X86_MOD_OPREG_RM(TME_RECODE_X86_MOD_RM_REG(reg_x86),
                    123:                                      TME_RECODE_X86_OPCODE_GRP2_ROR);
                    124:       thunk_bytes[2] = 16;
                    125:       thunk_bytes += 3;
                    126:       thunk_bytes[0] = TME_RECODE_X86_PREFIX_OPSIZ;
                    127:       thunk_bytes[1] = TME_RECODE_X86_OPCODE_GRP2_Ib_Ev;
                    128:       thunk_bytes[2]
                    129:        = TME_RECODE_X86_MOD_OPREG_RM(TME_RECODE_X86_MOD_RM_REG(reg_x86),
                    130:                                      TME_RECODE_X86_OPCODE_GRP2_ROR);
                    131:       thunk_bytes[3] = 8;
                    132:       thunk_bytes += 4;
                    133:     }
                    134:   }
                    135: 
                    136:   return (thunk_bytes);
                    137: }
                    138: 
                    139: /* this host function returns a new read/write thunk: */
                    140: struct tme_recode_rw_thunk *
                    141: tme_recode_host_rw_thunk_new(struct tme_recode_ic *ic,
                    142:                             const struct tme_recode_rw *rw)
                    143: {
                    144:   struct tme_recode_rw_thunk *rw_thunk;
                    145:   unsigned int max_boundaries_guest;
                    146:   unsigned int max_boundaries_host;
                    147:   struct tme_recode_x86_tlb_type x86_tlb_type;
                    148:   unsigned int reg_x86_address;
                    149:   unsigned int reg_host_value_0;
                    150:   unsigned int reg_host_value_1;
                    151:   struct tme_recode_insn insn_buffer;
                    152:   tme_uint8_t *thunk_bytes;
                    153:   unsigned int rex;
                    154:   unsigned int reg_host_value_orig;
                    155:   int stack_adjust;
                    156:   unsigned int reg_size;
                    157:   int memory_signed;
                    158: 
                    159:   /* start the new read/write thunk: */
                    160:   if (!tme_recode_host_thunk_start(ic)) {
                    161:     abort();
                    162:   }
                    163:   rw_thunk = tme_new(struct tme_recode_rw_thunk, 1);
                    164:   rw_thunk->tme_recode_x86_rw_thunk_subs
                    165:     = tme_recode_build_to_thunk_off(ic, ic->tme_recode_ic_thunk_build_next);
                    166: 
                    167:   /* assume that we will always need to assist: */
                    168:   x86_tlb_type.tme_recode_x86_tlb_type_assist_jmp_address_ok = (tme_uint8_t *) NULL;
                    169:   x86_tlb_type.tme_recode_x86_tlb_type_assist_jmp = (tme_uint8_t *) NULL;
                    170: 
                    171:   /* get the worst-case maximum number of guest bus boundaries this
                    172:      read/write could cross: */
                    173:   max_boundaries_guest
                    174:     = (rw->tme_recode_rw_bus_boundary == 0
                    175:        ? 0
                    176:        : (((TME_MAX(rw->tme_recode_rw_bus_boundary,
                    177:                    rw->tme_recode_rw_address_type.tme_recode_address_type_align_min)
                    178:            - rw->tme_recode_rw_address_type.tme_recode_address_type_align_min)
                    179:           + (TME_BIT(rw->tme_recode_rw_memory_size - TME_RECODE_SIZE_8)
                    180:              - 1))
                    181:          / rw->tme_recode_rw_bus_boundary));
                    182: 
                    183:   /* get the worst-case maximum number of host bus boundaries this
                    184:      read/write could cross: */
                    185:   max_boundaries_host
                    186:     = (((TME_MAX(TME_MEMORY_BUS_BOUNDARY,
                    187:                 rw->tme_recode_rw_address_type.tme_recode_address_type_align_min)
                    188:         - rw->tme_recode_rw_address_type.tme_recode_address_type_align_min)
                    189:        + (TME_BIT(rw->tme_recode_rw_memory_size - TME_RECODE_SIZE_8)
                    190:           - 1))
                    191:        / TME_MEMORY_BUS_BOUNDARY);
                    192: 
                    193:   /* NB: as of 20080906, Intel's "Intel 64 Architecture Memory
                    194:      Ordering White Paper" (Order number 318147-001, from August 2007)
                    195:      only guarantees that size-aligned reads and writes up to 64 bits
                    196:      are atomic; we assume that non-Intel processors are similar.
                    197:      until this guarantee is extended to cover size-aligned 128-bit
                    198:      reads and writes, TME_MEMORY_BUS_BOUNDARY shouldn't be more than
                    199:      sizeof(tme_uint64_t).
                    200: 
                    201:      this means that we will always assist guest 128-bit reads
                    202:      and writes that might be atomic for the guest (since we will
                    203:      detect above that we might cross more bus boundaries than the
                    204:      guest might): */
                    205:       
                    206:   /* if threads are cooperative, or if common atomic operations aren't
                    207:      being done under software lock and we can't cross more boundaries
                    208:      than the guest would: */
                    209:   if (TME_THREADS_COOPERATIVE
                    210:       || (TME_MEMORY_ALIGNMENT_ATOMIC(TME_MEMORY_TYPE_COMMON) != 0
                    211:          && max_boundaries_host <= max_boundaries_guest)) {
                    212: 
                    213:     /* get the TLB type for the address type: */
                    214:     tme_recode_address_type_tlb_type(ic,
                    215:                                     &rw->tme_recode_rw_address_type,
                    216:                                     &x86_tlb_type.tme_recode_tlb_type);
                    217: 
                    218:     /* XXX FIXME - document read/write thunk calling convention, how
                    219:        it differs from normal subs */
                    220: 
                    221:     /* for a double-host-size guest, the guest address is in the a:bp
                    222:        register pair, otherwise it's in the a register.  NB that we
                    223:        primarily deal with only a host-sized part: */
                    224:     reg_x86_address
                    225:       = tme_recode_x86_reg_from_host[_tme_recode_x86_tlb_reg_host_address(ic)];
                    226: 
                    227:     /* for a write, the value to write is in the first host register
                    228:        (pair).  for a read, the value read is returned in the same
                    229:        host register (pair) that was used for the address: */
                    230:     if (rw->tme_recode_rw_write) {
                    231:       reg_host_value_0 = TME_RECODE_REG_HOST(0);
                    232:     }
                    233:     else {
                    234:       reg_host_value_0
                    235:        = _tme_recode_x86_tlb_reg_host_address(ic);
                    236:       assert (tme_recode_x86_reg_from_host[reg_host_value_0] == reg_x86_address);
                    237:     }
                    238:     reg_host_value_1 = reg_host_value_0 + 1;
                    239: 
                    240:     /* find, busy, and check a data TLB entry: */
                    241:     _tme_recode_x86_tlb_busy(ic,
                    242:                             &rw->tme_recode_rw_address_type,
                    243:                             &x86_tlb_type);
                    244: 
                    245:     /* start more instructions: */
                    246:     tme_recode_x86_insns_start(ic, thunk_bytes);
                    247: 
                    248:     /* if this is a write, and we need to byte-swap the value to
                    249:        write: */
                    250:     if (rw->tme_recode_rw_write
                    251:        && rw->tme_recode_rw_memory_size > TME_RECODE_SIZE_8
                    252:        && rw->tme_recode_rw_memory_endian != TME_ENDIAN_NATIVE) {
                    253: 
                    254:       /* we will byte-swap the value to write into (at least) the
                    255:         TLB scratch register: */
                    256:       reg_host_value_orig = reg_host_value_0;
                    257:       assert (reg_host_value_1 == reg_host_value_0 + 1);
                    258:       reg_host_value_0 = TME_RECODE_REG_HOST_UNDEF;
                    259:       assert (tme_recode_x86_reg_from_host[reg_host_value_0] == TME_RECODE_X86_REG_TLB_SCRATCH);
                    260: 
                    261:       /* if this is a double-host-size write: */
                    262:       if (TME_RECODE_SIZE_IS_DOUBLE_HOST(rw->tme_recode_rw_memory_size)) {
                    263: 
                    264:        /* we will use the most-significant half of the guest register
                    265:           that held the address for the higher-in-memory (i.e., the
                    266:           guest least-significant) part of the value.  NB that we are
                    267:           swapping guest register halves here, too: */
                    268:        reg_host_value_1 = TME_RECODE_X86_REG_HOST_SUBS_SRC1 + 1;
                    269:        assert (tme_recode_x86_reg_from_host[reg_host_value_1 - 1] == reg_x86_address);
                    270:        _tme_recode_x86_emit_reg_copy(thunk_bytes,
                    271:                                      tme_recode_x86_reg_from_host[reg_host_value_orig + 0],
                    272:                                      tme_recode_x86_reg_from_host[reg_host_value_1]);
                    273:        thunk_bytes
                    274:          = _tme_recode_x86_rw_bswap(thunk_bytes,
                    275:                                     TME_RECODE_SIZE_HOST,
                    276:                                     tme_recode_x86_reg_from_host[reg_host_value_1]);
                    277:        reg_host_value_orig += 1;
                    278:       }
                    279: 
                    280:       /* copy and swap the lower-in-memory (i.e., the guest
                    281:         most-significant) part of the value: */
                    282:       _tme_recode_x86_emit_reg_copy(thunk_bytes,
                    283:                                    tme_recode_x86_reg_from_host[reg_host_value_orig],
                    284:                                    tme_recode_x86_reg_from_host[reg_host_value_0]);
                    285:       thunk_bytes
                    286:        = _tme_recode_x86_rw_bswap(thunk_bytes,
                    287:                                   TME_MIN(rw->tme_recode_rw_memory_size,
                    288:                                           TME_RECODE_SIZE_HOST),
                    289:                                   tme_recode_x86_reg_from_host[reg_host_value_0]);
                    290:     }
                    291: 
                    292:     /* if threads aren't cooperative, and this is a double-host-size
                    293:        access: */
                    294:     if (!TME_THREADS_COOPERATIVE
                    295:        && TME_RECODE_SIZE_IS_DOUBLE_HOST(rw->tme_recode_rw_memory_size)) {
                    296: 
                    297:       /* NB: in this case, we assume that the host is capable of SSE2
                    298:         instructions.  this seems reasonable: */
                    299: 
                    300:       /* the x86-64 ABI requires that the stack pointer be 16-byte
                    301:         aligned immediately before a call instruction.  inside an
                    302:         insn thunk, the stack pointer is 16-byte aligned immediately
                    303:         before a call to a read/write thunk, which means at the
                    304:         beginning of a read/write thunk it is only 8-byte aligned
                    305:         (because of the return address for the read/write thunk).
                    306: 
                    307:         on x86-64, we want to use at least one movdqa, which requires
                    308:         us to align the stack pointer: */
                    309:       stack_adjust
                    310:        = (TME_RECODE_SIZE_HOST == TME_RECODE_SIZE_32
                    311:           ? 0
                    312:           : TME_BIT(TME_RECODE_SIZE_HOST - TME_RECODE_SIZE_8));
                    313: 
                    314:       /* if this is a write: */
                    315:       if (rw->tme_recode_rw_write) {
                    316: 
                    317:        /* do any stack pointer alignment: */
                    318:        if (stack_adjust) {
                    319:          thunk_bytes = _tme_recode_x86_emit_adjust_sp(thunk_bytes, -stack_adjust);
                    320:        }
                    321: 
                    322:        /* push the double-host-size value to write: */
                    323:        _tme_recode_x86_emit_reg_push(thunk_bytes, tme_recode_x86_reg_from_host[reg_host_value_1]);
                    324:        _tme_recode_x86_emit_reg_push(thunk_bytes, tme_recode_x86_reg_from_host[reg_host_value_0]);
                    325: 
                    326:        /* after the write, we will need to discard the value: */
                    327:        stack_adjust += TME_BIT(rw->tme_recode_rw_memory_size - TME_RECODE_SIZE_8);
                    328:       }
                    329: 
                    330:       /* otherwise, this is a read: */
                    331:       else {
                    332: 
                    333:        /* do any stack pointer alignment, and make space for the
                    334:           double-host-size value to read: */
                    335:        thunk_bytes
                    336:          = _tme_recode_x86_emit_adjust_sp(thunk_bytes, 
                    337:                                           -(stack_adjust
                    338:                                             + TME_BIT(rw->tme_recode_rw_memory_size
                    339:                                                       - TME_RECODE_SIZE_8)));
                    340:       }
                    341: 
                    342:       /* emit one of: 
                    343:         movq (%esp), %xmm0
                    344:         movdqa (%esp), %xmm0
                    345:         movq (%address), %xmm0
                    346:         movdqa (%address), %xmm0
                    347: 
                    348:         to read the value to read or write into %xmm0:
                    349:       */
                    350:       thunk_bytes[0]
                    351:        = ((TME_RECODE_SIZE_HOST == TME_RECODE_SIZE_32
                    352:            || (!rw->tme_recode_rw_write
                    353:                && (rw->tme_recode_rw_address_type.tme_recode_address_type_align_min
                    354:                    < TME_BIT(rw->tme_recode_rw_memory_size - TME_RECODE_SIZE_8))))
                    355:           ? TME_RECODE_X86_PREFIX_REP
                    356:           : TME_RECODE_X86_PREFIX_OPSIZ);
                    357:       thunk_bytes[1] = TME_RECODE_X86_OPCODE_ESC_0F;
                    358:       thunk_bytes[2]
                    359:        = (TME_RECODE_SIZE_HOST == TME_RECODE_SIZE_32
                    360:           ? TME_RECODE_X86_OPCODEF30F_MOVQ_Wq_Vq
                    361:           : thunk_bytes[0] == TME_RECODE_X86_PREFIX_REP
                    362:           ? TME_RECODE_X86_OPCODEF30F_MOVDQU_Wdq_Vdq
                    363:           : TME_RECODE_X86_OPCODE660F_MOVDQA_Wdq_Vdq);
                    364:       if (rw->tme_recode_rw_write) {
                    365:        thunk_bytes[3]
                    366:          = TME_RECODE_X86_MOD_OPREG_RM(TME_RECODE_X86_MOD_RM_EA(TME_RECODE_X86_EA_BASE_SIB),
                    367:                                        TME_RECODE_X86_REG(TME_RECODE_X86_REG_XMM(0)));
                    368:        thunk_bytes[4]
                    369:          = TME_RECODE_X86_SIB(TME_RECODE_X86_REG_SP, TME_RECODE_X86_SIB_INDEX_NONE, 1);
                    370:        thunk_bytes += 5;
                    371:       }
                    372:       else {
                    373:        thunk_bytes[3]
                    374:          = TME_RECODE_X86_MOD_OPREG_RM(TME_RECODE_X86_MOD_RM_EA(reg_x86_address),
                    375:                                        TME_RECODE_X86_REG(TME_RECODE_X86_REG_XMM(0)));
                    376:        thunk_bytes += 4;
                    377:       }
                    378: 
                    379:       /* emit one of:
                    380:         movq %xmm0, (%address)
                    381:         movdqu %xmm0, (%address)
                    382:         movdqa %xmm0, (%address)
                    383:         movq %xmm0, (%esp)
                    384:         movdqa %xmm0, (%esp)
                    385: 
                    386:         to write the value to read or write in %xmm0:
                    387:       */
                    388:       thunk_bytes[0]
                    389:        = ((TME_RECODE_SIZE_HOST == TME_RECODE_SIZE_32
                    390:            || (rw->tme_recode_rw_write
                    391:                && (rw->tme_recode_rw_address_type.tme_recode_address_type_align_min
                    392:                    >= TME_BIT(rw->tme_recode_rw_memory_size - TME_RECODE_SIZE_8))))
                    393:           ? TME_RECODE_X86_PREFIX_OPSIZ
                    394:           : TME_RECODE_X86_PREFIX_REP);
                    395:       thunk_bytes[1] = TME_RECODE_X86_OPCODE_ESC_0F;
                    396:       thunk_bytes[2]
                    397:        = (TME_RECODE_SIZE_HOST == TME_RECODE_SIZE_32
                    398:           ? TME_RECODE_X86_OPCODE660F_MOVQ_Vq_Wq
                    399:           : thunk_bytes[0] == TME_RECODE_X86_PREFIX_REP
                    400:           ? TME_RECODE_X86_OPCODEF30F_MOVDQU_Vdq_Wdq
                    401:           : TME_RECODE_X86_OPCODE660F_MOVDQA_Vdq_Wdq);
                    402:       if (rw->tme_recode_rw_write) {
                    403:        thunk_bytes[3]
                    404:          = TME_RECODE_X86_MOD_OPREG_RM(TME_RECODE_X86_MOD_RM_EA(reg_x86_address),
                    405:                                        TME_RECODE_X86_REG(TME_RECODE_X86_REG_XMM(0)));
                    406:        thunk_bytes += 4;
                    407:       }
                    408:       else {
                    409:        thunk_bytes[3]
                    410:          = TME_RECODE_X86_MOD_OPREG_RM(TME_RECODE_X86_MOD_RM_EA(TME_RECODE_X86_EA_BASE_SIB),
                    411:                                        TME_RECODE_X86_REG(TME_RECODE_X86_REG_XMM(0)));
                    412:        thunk_bytes[4]
                    413:          = TME_RECODE_X86_SIB(TME_RECODE_X86_REG_SP, TME_RECODE_X86_SIB_INDEX_NONE, 1);
                    414:        thunk_bytes += 5;
                    415:       }
                    416: 
                    417:       /* if this was a read: */
                    418:       if (!rw->tme_recode_rw_write) {
                    419: 
                    420:        /* pop the double-host-size value we read: */
                    421:        _tme_recode_x86_emit_reg_pop(thunk_bytes, tme_recode_x86_reg_from_host[reg_host_value_0]);
                    422:        _tme_recode_x86_emit_reg_pop(thunk_bytes, tme_recode_x86_reg_from_host[reg_host_value_1]);
                    423:       }
                    424: 
                    425:       /* discard any double-host-size value we wrote, and any stack
                    426:         pointer alignment: */
                    427:       if (stack_adjust) {
                    428:        thunk_bytes = _tme_recode_x86_emit_adjust_sp(thunk_bytes, -stack_adjust);
                    429:       }
                    430:     }
                    431: 
                    432:     /* otherwise, either threads are cooperative, or this isn't a
                    433:        double-host-size access: */
                    434: 
                    435:     /* if this is a write: */
                    436:     else if (rw->tme_recode_rw_write) {
                    437: 
                    438:       /* if this is an ia32 host, and an 8-bit store of a register
                    439:         that doesn't have an 8-bit encoding: */
                    440:       if (TME_RECODE_SIZE_HOST <= TME_RECODE_SIZE_32
                    441:          && rw->tme_recode_rw_memory_size == TME_RECODE_SIZE_8
                    442:          && tme_recode_x86_reg_from_host[reg_host_value_0] >= TME_RECODE_X86_REG_SP) {
                    443: 
                    444:        /* we will copy the value to write into the TLB scratch
                    445:           register, which has an 8-bit encoding: */
                    446:        reg_host_value_orig = reg_host_value_0;
                    447:        reg_host_value_0 = TME_RECODE_REG_HOST_UNDEF;
                    448:        assert (tme_recode_x86_reg_from_host[reg_host_value_0] < TME_RECODE_X86_REG_SP);
                    449:        assert (tme_recode_x86_reg_from_host[reg_host_value_0] == TME_RECODE_X86_REG_TLB_SCRATCH);
                    450:        _tme_recode_x86_emit_reg_copy(thunk_bytes,
                    451:                                      tme_recode_x86_reg_from_host[reg_host_value_orig],
                    452:                                      tme_recode_x86_reg_from_host[reg_host_value_0]);
                    453:       }
                    454: 
                    455:       /* emit one of:
                    456:         movb %reg, (%address)
                    457:         movw %reg, (%address)
                    458:         movl %reg, (%address)
                    459:         movq %reg, (%address)
                    460:       */
                    461:       if (rw->tme_recode_rw_memory_size == TME_RECODE_SIZE_16) {
                    462:        *(thunk_bytes++) = TME_RECODE_X86_PREFIX_OPSIZ;
                    463:       }
                    464:       rex
                    465:        = (TME_RECODE_X86_REX_R(TME_MIN(rw->tme_recode_rw_memory_size,
                    466:                                        TME_RECODE_SIZE_HOST),
                    467:                                tme_recode_x86_reg_from_host[reg_host_value_0])
                    468:           | TME_RECODE_X86_REX_B(0, reg_x86_address));
                    469:       if (rex != 0) {
                    470:        *(thunk_bytes++) = rex;
                    471:       }
                    472:       thunk_bytes[0]
                    473:        = (rw->tme_recode_rw_memory_size >= TME_RECODE_SIZE_16
                    474:           ? (TME_RECODE_X86_OPCODE_BINOP_MOV + TME_RECODE_X86_OPCODE_BINOP_Gv_Ev)
                    475:           : (TME_RECODE_X86_OPCODE_BINOP_MOV + TME_RECODE_X86_OPCODE_BINOP_Gb_Eb));
                    476:       /* NB: a disp8 EA must be used when the base register is bp or r13: */
                    477:       if (TME_RECODE_X86_REG(reg_x86_address) == TME_RECODE_X86_REG_BP) {
                    478:        thunk_bytes[1]
                    479:          = TME_RECODE_X86_MOD_OPREG_RM(TME_RECODE_X86_MOD_RM_EA_DISP8(reg_x86_address),
                    480:                                        TME_RECODE_X86_REG(tme_recode_x86_reg_from_host[reg_host_value_0]));
                    481:        thunk_bytes[2] = 0;
                    482:        thunk_bytes += 3;
                    483:       }
                    484:       else {
                    485:        thunk_bytes[1]
                    486:          = TME_RECODE_X86_MOD_OPREG_RM(TME_RECODE_X86_MOD_RM_EA(reg_x86_address),
                    487:                                        TME_RECODE_X86_REG(tme_recode_x86_reg_from_host[reg_host_value_0]));
                    488:        thunk_bytes += 2;
                    489:       }
                    490: 
                    491:       /* if this is a double-host-size write: */
                    492:       if (TME_RECODE_SIZE_IS_DOUBLE_HOST(rw->tme_recode_rw_memory_size)) {
                    493: 
                    494:        /* emit one of:
                    495:           movl %reg, 4(%address)
                    496:           movq %reg, 8(%address)
                    497:        */
                    498:        rex
                    499:          = (TME_RECODE_X86_REX_R(TME_RECODE_SIZE_HOST,
                    500:                                  tme_recode_x86_reg_from_host[reg_host_value_1])
                    501:           | TME_RECODE_X86_REX_B(0, reg_x86_address));
                    502:        if (rex != 0) {
                    503:          *(thunk_bytes++) = rex;
                    504:        }
                    505:        thunk_bytes[0] = TME_RECODE_X86_OPCODE_BINOP_MOV + TME_RECODE_X86_OPCODE_BINOP_Gv_Ev;
                    506:        thunk_bytes[1]
                    507:          = TME_RECODE_X86_MOD_OPREG_RM(TME_RECODE_X86_MOD_RM_EA_DISP8(reg_x86_address),
                    508:                                        TME_RECODE_X86_REG(tme_recode_x86_reg_from_host[reg_host_value_1]));
                    509:        thunk_bytes[2] = TME_BIT(TME_RECODE_SIZE_HOST - TME_RECODE_SIZE_8);
                    510:        thunk_bytes += 3;
                    511:       }
                    512:     }
                    513: 
                    514:     /* otherwise, this is a read: */
                    515:     else {
                    516: 
                    517:       /* if this is a double-host-size read: */
                    518:       if (TME_RECODE_SIZE_IS_DOUBLE_HOST(rw->tme_recode_rw_memory_size)) {
                    519: 
                    520:        /* emit one of:
                    521:           movl 4(%address), %reg
                    522:           movq 8(%address), %reg
                    523:        */
                    524:        rex
                    525:          = (TME_RECODE_X86_REX_R(TME_RECODE_SIZE_HOST,
                    526:                                  tme_recode_x86_reg_from_host[reg_host_value_1])
                    527:             | TME_RECODE_X86_REX_B(0, reg_x86_address));
                    528:        if (rex != 0) {
                    529:          *(thunk_bytes++) = rex;
                    530:        }
                    531:        thunk_bytes[0] = TME_RECODE_X86_OPCODE_BINOP_MOV + TME_RECODE_X86_OPCODE_BINOP_Ev_Gv;
                    532:        thunk_bytes[1]
                    533:          = TME_RECODE_X86_MOD_OPREG_RM(TME_RECODE_X86_MOD_RM_EA_DISP8(reg_x86_address),
                    534:                                        TME_RECODE_X86_REG(tme_recode_x86_reg_from_host[reg_host_value_1]));
                    535:        thunk_bytes[2] = TME_BIT(TME_RECODE_SIZE_HOST - TME_RECODE_SIZE_8);
                    536:        thunk_bytes += 3;
                    537:       }
                    538: 
                    539:       /* assume that we we will read into all of a host register: */
                    540:       reg_size = TME_MAX(rw->tme_recode_rw_reg_size, TME_RECODE_SIZE_HOST);
                    541: 
                    542:       /* by default, in the read instruction itself we zero-extend the
                    543:         value into all of a host register.  the only time we will
                    544:         sign-extend in the read instruction itself is when the
                    545:         register size is greater than the memory size, memory is
                    546:         signed, and we're only reading a byte or the guest's byte
                    547:         order matches the host.  if the first two are true, but we're
                    548:         reading more than a byte and the guest's byte order doesn't
                    549:         match the host, we have to wait to do the sign extension
                    550:         after we've byte swapped the value read: */
                    551:       memory_signed
                    552:        = (rw->tme_recode_rw_reg_size > rw->tme_recode_rw_memory_size
                    553:           && rw->tme_recode_rw_memory_signed
                    554:           && (rw->tme_recode_rw_memory_size == TME_RECODE_SIZE_8
                    555:               || rw->tme_recode_rw_memory_endian == TME_ENDIAN_NATIVE));
                    556: 
                    557:       /* if this is an x86-64 host and a 32-bit sign- or zero-extended load: */
                    558:       if (TME_RECODE_SIZE_HOST > TME_RECODE_SIZE_32
                    559:          && rw->tme_recode_rw_memory_size == TME_RECODE_SIZE_32
                    560:          && rw->tme_recode_rw_reg_size > rw->tme_recode_rw_memory_size) {
                    561: 
                    562:        /* if this is a zero-extended load, or if the guest's byte
                    563:           order doesn't match the host: */
                    564:        if (!memory_signed) {
                    565: 
                    566:          /* read into only the least-significant 32 bits of the
                    567:             register.  this will zero-extend the read to all 64
                    568:             bits.  this should prevent a rex prefix: */
                    569:          reg_size = TME_RECODE_SIZE_32;
                    570:        }
                    571:       }
                    572: 
                    573:       /* emit any rex prefix: */
                    574:       rex
                    575:        = (TME_RECODE_X86_REX_B(0, reg_x86_address)
                    576:           | TME_RECODE_X86_REX_R(TME_MIN(reg_size,
                    577:                                          TME_RECODE_SIZE_HOST),
                    578:                                  tme_recode_x86_reg_from_host[reg_host_value_0]));
                    579:       if (rex != 0) {
                    580:        *(thunk_bytes++) = rex;
                    581:       }
                    582: 
                    583:       /* if this is an x86-64 host and a 32-bit sign-extended load: */
                    584:       if (TME_RECODE_SIZE_HOST > TME_RECODE_SIZE_32
                    585:          && rw->tme_recode_rw_memory_size == TME_RECODE_SIZE_32
                    586:          && reg_size > TME_RECODE_SIZE_32) {
                    587: 
                    588:        /* emit the opcode part of a movslq (%address), %reg: */
                    589:        thunk_bytes[0] = TME_RECODE_X86_OPCODE_MOVS_El_Gv;
                    590:       }
                    591: 
                    592:       /* otherwise, if this is an 8- or 16-bit load: */
                    593:       else if (rw->tme_recode_rw_memory_size <= TME_RECODE_SIZE_16) {
                    594: 
                    595:        /* emit the opcode part of one of:
                    596:           movsb (%address), %reg
                    597:           movzb (%address), %reg
                    598:           movsw (%address), %reg
                    599:           movzw (%address), %reg
                    600:        */
                    601:        *(thunk_bytes++) = TME_RECODE_X86_OPCODE_ESC_0F;
                    602:        thunk_bytes[0]
                    603:          = (rw->tme_recode_rw_memory_size == TME_RECODE_SIZE_8
                    604:             ? (memory_signed
                    605:                ? TME_RECODE_X86_OPCODE0F_MOVS_Eb_Gv
                    606:                : TME_RECODE_X86_OPCODE0F_MOVZ_Eb_Gv)
                    607:             : (memory_signed
                    608:                ? TME_RECODE_X86_OPCODE0F_MOVS_Ew_Gv
                    609:                : TME_RECODE_X86_OPCODE0F_MOVZ_Ew_Gv));
                    610:       }
                    611: 
                    612:       /* otherwise, this load is double-host-size, or host-size, or a
                    613:         32-bit zero-extended load on an x86-64 host: */
                    614:       else {
                    615: 
                    616:        /* emit the opcode part of a movl (%address), %reg or a movq (%address), %reg: */
                    617:        thunk_bytes[0]
                    618:          = (TME_RECODE_X86_OPCODE_BINOP_MOV
                    619:             + TME_RECODE_X86_OPCODE_BINOP_Ev_Gv);
                    620:       }
                    621: 
                    622:       /* emit the modR/M byte for this instruction: */
                    623:       /* NB: a disp8 EA must be used when the base register is bp or r13: */
                    624:       if (TME_RECODE_X86_REG(reg_x86_address) == TME_RECODE_X86_REG_BP) {
                    625:        thunk_bytes[1]
                    626:          = TME_RECODE_X86_MOD_OPREG_RM(TME_RECODE_X86_MOD_RM_EA_DISP8(reg_x86_address),
                    627:                                        TME_RECODE_X86_REG(tme_recode_x86_reg_from_host[reg_host_value_0]));
                    628:        thunk_bytes[2] = 0;
                    629:        thunk_bytes += 3;
                    630:       }
                    631:       else {
                    632:        thunk_bytes[1]
                    633:          = TME_RECODE_X86_MOD_OPREG_RM(TME_RECODE_X86_MOD_RM_EA(reg_x86_address),
                    634:                                        TME_RECODE_X86_REG(tme_recode_x86_reg_from_host[reg_host_value_0]));
                    635:        thunk_bytes += 2;
                    636:       }
                    637:     }
                    638: 
                    639:     /* if this is a read: */
                    640:     if (!rw->tme_recode_rw_write) {
                    641: 
                    642:       /* if we need to byte-swap the value read: */
                    643:       if (rw->tme_recode_rw_memory_size > TME_RECODE_SIZE_8
                    644:          && rw->tme_recode_rw_memory_endian != TME_ENDIAN_NATIVE) {
                    645: 
                    646:        /* if this is a double-host-size read: */
                    647:        if (TME_RECODE_SIZE_IS_DOUBLE_HOST(rw->tme_recode_rw_memory_size)) {
                    648: 
                    649:          /* swap the guest register halves: */
                    650:          _tme_recode_x86_emit_reg_binop(thunk_bytes,
                    651:                                         TME_RECODE_X86_OPCODE_BINOP_XCHG,
                    652:                                         tme_recode_x86_reg_from_host[reg_host_value_0],
                    653:                                         tme_recode_x86_reg_from_host[reg_host_value_1]);
                    654: 
                    655:          /* byte-swap the most-significant half of the guest register: */
                    656:          thunk_bytes
                    657:            = _tme_recode_x86_rw_bswap(thunk_bytes,
                    658:                                       TME_RECODE_SIZE_HOST,
                    659:                                       tme_recode_x86_reg_from_host[reg_host_value_1]);
                    660:        }
                    661: 
                    662:        /* byte-swap the (least-significant half of the) guest register: */
                    663:        thunk_bytes
                    664:          = _tme_recode_x86_rw_bswap(thunk_bytes,
                    665:                                     TME_MIN(rw->tme_recode_rw_memory_size,
                    666:                                             TME_RECODE_SIZE_HOST),
                    667:                                     tme_recode_x86_reg_from_host[reg_host_value_0]);
                    668: 
                    669:        /* if the read is sign-extended from smaller than host-sized: */
                    670:        if (rw->tme_recode_rw_reg_size > rw->tme_recode_rw_memory_size
                    671:            && rw->tme_recode_rw_memory_signed
                    672:            && rw->tme_recode_rw_memory_size < TME_RECODE_SIZE_HOST) {
                    673: 
                    674:          /* sign-extend the value read to host size: */
                    675:          tme_recode_x86_insns_finish(ic, thunk_bytes);
                    676:          insn_buffer.tme_recode_insn_opcode = TME_RECODE_OPCODE_EXTS;
                    677:          insn_buffer.tme_recode_insn_operand_src[0] = reg_host_value_0;
                    678:          insn_buffer.tme_recode_insn_operand_src[1] = rw->tme_recode_rw_memory_size;
                    679:          insn_buffer.tme_recode_insn_operand_dst = reg_host_value_0;
                    680:          _tme_recode_x86_insn_ext(ic, &insn_buffer);
                    681:          tme_recode_x86_insns_start(ic, thunk_bytes);
                    682:        }
                    683:       }
                    684: 
                    685:       /* if this is a double-host-size read that needs zero- or sign-extension: */
                    686:       if (TME_RECODE_SIZE_IS_DOUBLE_HOST(rw->tme_recode_rw_reg_size)
                    687:          && rw->tme_recode_rw_reg_size > rw->tme_recode_rw_memory_size) {
                    688: 
                    689:        /* if memory is signed: */
                    690:        if (rw->tme_recode_rw_memory_signed) {
                    691: 
                    692:          /* sign-extend the value read: */
                    693:          _tme_recode_x86_emit_reg_binop(thunk_bytes,
                    694:                                         TME_RECODE_X86_OPCODE_BINOP_MOV,
                    695:                                         tme_recode_x86_reg_from_host[reg_host_value_0],
                    696:                                         tme_recode_x86_reg_from_host[reg_host_value_1]);
                    697:          _tme_recode_x86_emit_reg_binop(thunk_bytes,
                    698:                                         TME_RECODE_X86_OPCODE_BINOP_ADD,
                    699:                                         tme_recode_x86_reg_from_host[reg_host_value_0],
                    700:                                         tme_recode_x86_reg_from_host[reg_host_value_1]);
                    701:          _tme_recode_x86_emit_reg_binop(thunk_bytes,
                    702:                                         TME_RECODE_X86_OPCODE_BINOP_SBB,
                    703:                                         tme_recode_x86_reg_from_host[reg_host_value_1],
                    704:                                         tme_recode_x86_reg_from_host[reg_host_value_1]);
                    705:        }
                    706: 
                    707:        /* otherwise, memory is unsigned: */
                    708:        else {
                    709: 
                    710:          /* zero-extend the value read: */
                    711:          /* NB: we always make this a 32-bit operation, to try to
                    712:             prevent a rex prefix: */
                    713:          rex = (TME_RECODE_X86_REX_R(TME_RECODE_SIZE_32,
                    714:                                      tme_recode_x86_reg_from_host[reg_host_value_1])
                    715:                 | TME_RECODE_X86_REX_B(TME_RECODE_SIZE_32,
                    716:                                        tme_recode_x86_reg_from_host[reg_host_value_1]));
                    717:          if (rex != 0) {
                    718:            *(thunk_bytes++) = rex;
                    719:          }
                    720:          thunk_bytes[0]
                    721:            = (TME_RECODE_X86_OPCODE_BINOP_XOR
                    722:               + TME_RECODE_X86_OPCODE_BINOP_Gv_Ev);
                    723:          thunk_bytes[1]
                    724:            = TME_RECODE_X86_MOD_OPREG_RM(TME_RECODE_X86_MOD_RM_REG(tme_recode_x86_reg_from_host[reg_host_value_1]),
                    725:                                          TME_RECODE_X86_REG(tme_recode_x86_reg_from_host[reg_host_value_1]));
                    726:          thunk_bytes += 2;
                    727:        }
                    728:       }
                    729:     }
                    730: 
                    731:     /* finish these instructions: */
                    732:     tme_recode_x86_insns_finish(ic, thunk_bytes);
                    733: 
                    734:     /* unbusy the TLB entry: */
                    735:     _tme_recode_x86_tlb_unbusy(ic,
                    736:                               x86_tlb_type.tme_recode_tlb_type.tme_recode_tlb_type_offset_token);
                    737: 
                    738:     /* start more instructions: */
                    739:     tme_recode_x86_insns_start(ic, thunk_bytes);
                    740: 
                    741:     /* return to the instructions thunk: */
                    742:     *(thunk_bytes++) = TME_RECODE_X86_OPCODE_RET;
                    743: 
                    744:     /* finish these instructions: */
                    745:     tme_recode_x86_insns_finish(ic, thunk_bytes);
                    746:   }
                    747: 
                    748:   if (x86_tlb_type.tme_recode_x86_tlb_type_assist_jmp != NULL) {
                    749: 
                    750:     /* finish the assist conditional jump above, now
                    751:        that we are at the target: */
                    752:     _tme_recode_x86_fixup_jmp(x86_tlb_type.tme_recode_x86_tlb_type_assist_jmp,
                    753:                              ic->tme_recode_ic_thunk_build_next);
                    754: 
                    755:     /* start more instructions: */
                    756:     tme_recode_x86_insns_start(ic, thunk_bytes);
                    757: 
                    758:     /* exclusive-or the TLB entry page offset with the
                    759:        (least-significant half of the) TLB entry page, to convert the
                    760:        TLB entry page offset back into the (least-significant half of
                    761:        the) guest address: */
                    762:     thunk_bytes
                    763:       = _tme_recode_x86_tlb_ref(thunk_bytes,
                    764:                                TME_RECODE_SIZE_HOST,
                    765:                                (TME_RECODE_X86_OPCODE_BINOP_XOR
                    766:                                 + TME_RECODE_X86_OPCODE_BINOP_Ev_Gv),
                    767:                                TME_RECODE_X86_REG_TLB,
                    768:                                x86_tlb_type.tme_recode_tlb_type.tme_recode_tlb_type_offset_page,
                    769:                                TME_RECODE_X86_REG(reg_x86_address));
                    770: 
                    771:     /* finish these instructions: */
                    772:     tme_recode_x86_insns_finish(ic, thunk_bytes);
                    773:   }
                    774: 
                    775:   /* fix up any double-host-size address assist: */
                    776:   if (x86_tlb_type.tme_recode_x86_tlb_type_assist_jmp_address_ok != NULL) {
                    777:     _tme_recode_x86_fixup_jmp(x86_tlb_type.tme_recode_x86_tlb_type_assist_jmp_address_ok,
                    778:                              ic->tme_recode_ic_thunk_build_next);
                    779:   }
                    780: 
                    781:   if (x86_tlb_type.tme_recode_x86_tlb_type_assist_jmp_address_ok != NULL
                    782:       || x86_tlb_type.tme_recode_x86_tlb_type_assist_jmp != NULL) {
                    783: 
                    784:     /* unbusy the TLB entry: */
                    785:     _tme_recode_x86_tlb_unbusy(ic,
                    786:                               x86_tlb_type.tme_recode_tlb_type.tme_recode_tlb_type_offset_token);
                    787:   }
                    788: 
                    789:   /* start more instructions: */
                    790:   tme_recode_x86_insns_start(ic, thunk_bytes);
                    791: 
                    792:   /* if this is an ia32 host: */
                    793:   if (TME_RECODE_SIZE_HOST == TME_RECODE_SIZE_32) {
                    794: 
                    795:     /* if this is a write: */
                    796:     if (rw->tme_recode_rw_write) {
                    797: 
                    798:       /* push the value argument for the guest function.  NB that if
                    799:         double-host-size guests are supported, but this isn't a
                    800:         double-host-size guest, we use a garbage word on the stack as
                    801:         the most-significant half of this argument (which is okay
                    802:         since the guest functions are supposed to truncate their
                    803:         arguments to the expected size): */
                    804:       if (TME_RECODE_SIZE_GUEST_MAX > TME_RECODE_SIZE_HOST) {
                    805:        _tme_recode_x86_emit_reg_push(thunk_bytes, tme_recode_x86_reg_from_host[TME_RECODE_REG_HOST(0) + 1]);
                    806:       }
                    807:       _tme_recode_x86_emit_reg_push(thunk_bytes, tme_recode_x86_reg_from_host[TME_RECODE_REG_HOST(0)]);
                    808:     }
                    809: 
                    810:     /* push the address argument for the guest function.  NB that if
                    811:        double-host-size guests are supported, but this isn't a
                    812:        double-host-size guest, we use a garbage word on the stack as
                    813:        the most-significant half of this argument (which is okay since
                    814:        the guest functions are supposed to truncate their arguments to
                    815:        the expected size): */
                    816:     if (TME_RECODE_SIZE_GUEST_MAX > TME_RECODE_SIZE_HOST) {
                    817:       _tme_recode_x86_emit_reg_push(thunk_bytes, TME_RECODE_X86_REG_A);
                    818:     }
                    819:     _tme_recode_x86_emit_reg_push(thunk_bytes, reg_x86_address);
                    820: 
                    821:     /* emit the instruction to push the struct tme_ic * argument for
                    822:        the guest function, and then the call instruction to the guest
                    823:        function: */
                    824:     *((tme_uint16_t *) thunk_bytes)
                    825:       = (TME_RECODE_X86_OPCODE_PUSH_Gv(TME_RECODE_X86_REG_IC)
                    826:         + (TME_RECODE_X86_OPCODE_CALL_RELz << 8));
                    827:     thunk_bytes += 2 + sizeof(tme_uint32_t);
                    828:     ((tme_int32_t *) thunk_bytes)[-1]
                    829:       = (tme_recode_function_to_thunk_off(ic, rw->tme_recode_rw_guest_func_read)
                    830:         - tme_recode_build_to_thunk_off(ic, thunk_bytes));
                    831: 
                    832:     /* remove the guest function arguments from the stack: */
                    833:     thunk_bytes
                    834:       = _tme_recode_x86_emit_adjust_sp(thunk_bytes, 
                    835:                                       (sizeof(struct tme_ic *)
                    836:                                        + sizeof(tme_recode_uguest_t)
                    837:                                        + (sizeof(tme_recode_uguest_t)
                    838:                                           * !!rw->tme_recode_rw_write)));
                    839:   }
                    840: 
                    841:   /* otherwise, this is an x86-64 host: */
                    842:   else {
                    843: 
                    844:     /* push the caller-saved registers that aren't normally destroyed
                    845:        by a read/write thunk: */
                    846:     _tme_recode_x86_emit_reg_push(thunk_bytes, TME_RECODE_X86_REG_N(10));
                    847:     _tme_recode_x86_emit_reg_push(thunk_bytes, TME_RECODE_X86_REG_N(11));
                    848:     _tme_recode_x86_emit_reg_push(thunk_bytes, TME_RECODE_X86_REG_DI);
                    849:     _tme_recode_x86_emit_reg_push(thunk_bytes, TME_RECODE_X86_REG_SI);
                    850:     _tme_recode_x86_emit_reg_push(thunk_bytes, TME_RECODE_X86_REG_N(8));
                    851:     _tme_recode_x86_emit_reg_push(thunk_bytes, TME_RECODE_X86_REG_N(9));
                    852: 
                    853:     /* make the struct tme_ic * argument for the guest function: */
                    854:     _tme_recode_x86_emit_reg_copy(thunk_bytes, TME_RECODE_X86_REG_IC, TME_RECODE_X86_REG_DI);
                    855: 
                    856:     /* make the address argument for the guest function.  NB that if
                    857:        double-host-size guests are supported, but this isn't a
                    858:        double-host-size guest, we use a garbage word as the
                    859:        most-significant half of this argument (which is okay since the
                    860:        guest functions are supposed to truncate their arguments to the
                    861:        expected size): */
                    862:     _tme_recode_x86_emit_reg_copy(thunk_bytes, reg_x86_address, TME_RECODE_X86_REG_SI);
                    863:     if (TME_RECODE_SIZE_IS_DOUBLE_HOST(ic->tme_recode_ic_reg_size)) {
                    864:       _tme_recode_x86_emit_reg_copy(thunk_bytes, TME_RECODE_X86_REG_A, TME_RECODE_X86_REG_D);
                    865:     }
                    866: 
                    867:     /* if this is a write: */
                    868:     if (rw->tme_recode_rw_write) {
                    869: 
                    870:       /* make the value argument for the guest function.  NB that if
                    871:         double-host-size guests are supported, but this isn't a
                    872:         double-host-size guest, we use a garbage word as the
                    873:         most-significant half of this argument (which is okay since
                    874:         the guest functions are supposed to truncate their arguments
                    875:         to the expected size): */
                    876:       _tme_recode_x86_emit_reg_copy(thunk_bytes,
                    877:                                    tme_recode_x86_reg_from_host[TME_RECODE_REG_HOST(0)],
                    878:                                    (TME_RECODE_SIZE_GUEST_MAX <= TME_RECODE_SIZE_HOST
                    879:                                     ? TME_RECODE_X86_REG_D
                    880:                                     : TME_RECODE_X86_REG_C));
                    881:       if (TME_RECODE_SIZE_IS_DOUBLE_HOST(ic->tme_recode_ic_reg_size)) {
                    882:        _tme_recode_x86_emit_reg_copy(thunk_bytes, 
                    883:                                      tme_recode_x86_reg_from_host[TME_RECODE_REG_HOST(0)],
                    884:                                      TME_RECODE_X86_REG_N(8));
                    885:       }
                    886:     }
                    887: 
                    888:     /* we must assume that we can't reach the guest function from the
                    889:        instruction thunk with a 32-bit displacement.  emit a direct
                    890:        call to the guest function using %rax: */
                    891:     *((tme_uint16_t *) thunk_bytes)
                    892:       = (TME_RECODE_X86_REX_B(TME_RECODE_SIZE_HOST, TME_RECODE_X86_REG_A)
                    893:         + (TME_RECODE_X86_OPCODE_MOV_Iv_Gv(TME_RECODE_X86_REG_A)
                    894:            << 8));
                    895:     memcpy(thunk_bytes + 2,
                    896:           &rw->tme_recode_rw_guest_func_write,
                    897:           TME_BIT(TME_RECODE_SIZE_HOST - TME_RECODE_SIZE_8));
                    898:     thunk_bytes += 2 + TME_BIT(TME_RECODE_SIZE_HOST - TME_RECODE_SIZE_8);
                    899:     *((tme_uint16_t *) thunk_bytes)
                    900:       = (TME_RECODE_X86_OPCODE_GRP5
                    901:         + (TME_RECODE_X86_MOD_OPREG_RM(TME_RECODE_X86_MOD_RM_REG(TME_RECODE_X86_REG_A),
                    902:                                        TME_RECODE_X86_OPCODE_GRP5_CALL)
                    903:            << 8));
                    904:     thunk_bytes += 2;
                    905: 
                    906:     /* pop the caller-saved registers that aren't normally destroyed
                    907:        by a read/write thunk: */
                    908:     _tme_recode_x86_emit_reg_pop(thunk_bytes, TME_RECODE_X86_REG_N(9));
                    909:     _tme_recode_x86_emit_reg_pop(thunk_bytes, TME_RECODE_X86_REG_N(8));
                    910:     _tme_recode_x86_emit_reg_pop(thunk_bytes, TME_RECODE_X86_REG_SI);
                    911:     _tme_recode_x86_emit_reg_pop(thunk_bytes, TME_RECODE_X86_REG_DI);
                    912:     _tme_recode_x86_emit_reg_pop(thunk_bytes, TME_RECODE_X86_REG_N(11));
                    913:     _tme_recode_x86_emit_reg_pop(thunk_bytes, TME_RECODE_X86_REG_N(10));
                    914:   }
                    915: 
                    916:   /* if this is a read: */
                    917:   if (!rw->tme_recode_rw_write) {
                    918: 
                    919:     /* if this is a double-host-size guest: */
                    920:     if (TME_RECODE_SIZE_IS_DOUBLE_HOST(ic->tme_recode_ic_reg_size)) {
                    921: 
                    922:       /* move the value read into the expected return registers: */
                    923:       _tme_recode_x86_emit_reg_copy(thunk_bytes,
                    924:                                    TME_RECODE_X86_REG_A,
                    925:                                    tme_recode_x86_reg_from_host[reg_host_value_0]);
                    926:       _tme_recode_x86_emit_reg_copy(thunk_bytes,
                    927:                                    TME_RECODE_X86_REG_D,
                    928:                                    tme_recode_x86_reg_from_host[reg_host_value_1]);
                    929:     }
                    930: 
                    931:     /* otherwise, this is not a double-host-size guest: */
                    932:     else {
                    933: 
                    934:       /* the value read should already be in the expected register: */
                    935:       assert (tme_recode_x86_reg_from_host[reg_host_value_0] == TME_RECODE_X86_REG_A);
                    936:     }
                    937:   }
                    938: 
                    939:   /* return to the instructions thunk: */
                    940:   *(thunk_bytes++) = TME_RECODE_X86_OPCODE_RET;
                    941: 
                    942:   /* finish these instructions: */
                    943:   tme_recode_x86_insns_finish(ic, thunk_bytes);
                    944: 
                    945:   /* finish this read/write thunk: */
                    946:   tme_recode_host_thunk_finish(ic);
                    947: 
                    948:   /* no further extension is needed for this read/write thunk: */
                    949:   rw_thunk->tme_recode_x86_rw_thunk_extend_size = 0;
                    950: 
                    951:   return (rw_thunk);
                    952: }
                    953: 
                    954: /* this host function tries to duplicate a read/write thunk: */
                    955: struct tme_recode_rw_thunk *
                    956: tme_recode_host_rw_thunk_dup(struct tme_recode_ic *ic,
                    957:                             const struct tme_recode_rw *rw,
                    958:                             const struct tme_recode_rw *rw_other)
                    959: {
                    960:   tme_uint8_t *thunk_bytes_0;
                    961:   struct tme_recode_insn insn_buffer;
                    962:   tme_uint8_t *thunk_bytes_1;
                    963:   struct tme_recode_rw_thunk *rw_thunk;
                    964: 
                    965:   /* start more instructions, so if we use _tme_recode_x86_insn_ext()
                    966:      to emit an extension instruction, we can discard it from the
                    967:      thunk build memory: */
                    968:   tme_recode_x86_insns_start(ic, thunk_bytes_0);
                    969: 
                    970:   /* if our register size is the same as the memory size, or
                    971:      if the existing read/write thunk will do the extension that
                    972:      we need to at least our register size: */
                    973:   /* NB: a read/write thunk always extends to at least host size, and
                    974:      when no particular extension is explicitly required,
                    975:      zero-extension is the default: */
                    976:   if ((rw->tme_recode_rw_reg_size
                    977:        == rw_other->tme_recode_rw_memory_size)
                    978:       || ((((rw_other->tme_recode_rw_reg_size
                    979:             > rw_other->tme_recode_rw_memory_size)
                    980:            && rw_other->tme_recode_rw_memory_signed)
                    981:           == !!rw->tme_recode_rw_memory_signed)
                    982:          && (TME_MAX(rw_other->tme_recode_rw_reg_size, TME_RECODE_SIZE_HOST)
                    983:              >= rw->tme_recode_rw_reg_size))) {
                    984: 
                    985:     /* we can reuse the existing read/write thunk, and we don't need
                    986:        to do any extension: */
                    987:     /* nothing to do */
                    988:   }
                    989: 
                    990:   /* otherwise, if this is a double-host-size read: */
                    991:   else if (TME_RECODE_SIZE_IS_DOUBLE_HOST(rw->tme_recode_rw_reg_size)) {
                    992: 
                    993:     /* we won't reuse the existing read/write thunk: */
                    994:     return (NULL);
                    995:   }
                    996: 
                    997:   /* otherwise, we will reuse this read/write thunk, with a single
                    998:      zero- or sign-extension instruction after each call: */
                    999:   else {
                   1000: 
                   1001:     /* use _tme_recode_x86_insn_ext() to emit the extension
                   1002:        instruction: */
                   1003:     insn_buffer.tme_recode_insn_opcode
                   1004:       = (rw->tme_recode_rw_memory_signed
                   1005:         ? TME_RECODE_OPCODE_EXTS
                   1006:         : TME_RECODE_OPCODE_EXTZ);
                   1007:     insn_buffer.tme_recode_insn_operand_src[0] = TME_RECODE_X86_REG_HOST_FREE_CALL;
                   1008:     insn_buffer.tme_recode_insn_operand_src[1] = rw->tme_recode_rw_memory_size;
                   1009:     insn_buffer.tme_recode_insn_operand_dst = insn_buffer.tme_recode_insn_operand_src[0];
                   1010:     _tme_recode_x86_insn_ext(ic, &insn_buffer);
                   1011:   }
                   1012: 
                   1013:   /* duplicate the read/write thunk: */
                   1014:   rw_thunk = tme_dup(struct tme_recode_rw_thunk, rw_other->tme_recode_rw_thunk, 1);
                   1015: 
                   1016:   /* get any extension instruction from the thunk build memory, and
                   1017:      then discard it: */
                   1018:   tme_recode_x86_insns_start(ic, thunk_bytes_1);
                   1019:   rw_thunk->tme_recode_x86_rw_thunk_extend = *((tme_uint32_t *) thunk_bytes_0);
                   1020:   rw_thunk->tme_recode_x86_rw_thunk_extend_size = (thunk_bytes_1 - thunk_bytes_0);
                   1021:   assert (rw_thunk->tme_recode_x86_rw_thunk_extend_size <= sizeof(tme_uint32_t));
                   1022:   tme_recode_x86_insns_finish(ic, thunk_bytes_0);
                   1023: 
                   1024:   /* return the duplicated read/write thunk: */
                   1025:   return (rw_thunk);
                   1026: }

unix.superglobalmegacorp.com

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