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