|
|
1.1 ! root 1: /* $Id: rc-x86-flags.c,v 1.5 2010/02/15 22:21:48 fredette Exp $ */ ! 2: ! 3: /* libtme/host/x86/rc-x86-flags.c - x86 host recode flags support: */ ! 4: ! 5: /* ! 6: * Copyright (c) 2007 Matt Fredette ! 7: * All rights reserved. ! 8: * ! 9: * Redistribution and use in source and binary forms, with or without ! 10: * modification, are permitted provided that the following conditions ! 11: * are met: ! 12: * 1. Redistributions of source code must retain the above copyright ! 13: * notice, this list of conditions and the following disclaimer. ! 14: * 2. Redistributions in binary form must reproduce the above copyright ! 15: * notice, this list of conditions and the following disclaimer in the ! 16: * documentation and/or other materials provided with the distribution. ! 17: * 3. All advertising materials mentioning features or use of this software ! 18: * must display the following acknowledgement: ! 19: * This product includes software developed by Matt Fredette. ! 20: * 4. The name of the author may not be used to endorse or promote products ! 21: * derived from this software without specific prior written permission. ! 22: * ! 23: * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR ! 24: * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED ! 25: * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE ! 26: * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, ! 27: * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES ! 28: * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR ! 29: * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) ! 30: * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, ! 31: * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ! 32: * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE ! 33: * POSSIBILITY OF SUCH DAMAGE. ! 34: */ ! 35: ! 36: _TME_RCSID("$Id: rc-x86-flags.c,v 1.5 2010/02/15 22:21:48 fredette Exp $"); ! 37: ! 38: /* this gives one of the x86 registers that can be used by an x86 ! 39: flags thunk: */ ! 40: /* an x86-64 flags thunk that needs to call a guest function must use ! 41: the 9, 10, and 11 registers. on ia32 and on x86-64 otherwise, we ! 42: must use the a, c, and d registers: */ ! 43: #if (TME_RECODE_X86_REG_A + 1) != TME_RECODE_X86_REG_C || (TME_RECODE_X86_REG_C + 1) != TME_RECODE_X86_REG_D ! 44: #error "TME_RECODE_X86_REG_ values changed" ! 45: #endif ! 46: #define _TME_RECODE_X86_FLAGS_REG(flags_group, flags_reg_index) \ ! 47: (((TME_RECODE_SIZE_HOST == TME_RECODE_SIZE_32 \ ! 48: || (flags_group)->tme_recode_flags_group_guest_func == NULL) \ ! 49: ? TME_RECODE_X86_REG_A \ ! 50: : TME_RECODE_X86_REG_N(9)) \ ! 51: + (flags_reg_index)) ! 52: #define _TME_RECODE_X86_FLAGS_REG_COUNT (3) ! 53: ! 54: /* this adds a one-byte-opcode ModRM instruction to a thunk, that ! 55: addresses the stacked host flags of a particular size: */ ! 56: static tme_uint8_t * ! 57: _tme_recode_x86_flags_host_flags(const struct tme_recode_flags_group *flags_group, ! 58: tme_uint8_t *thunk_bytes, ! 59: tme_uint32_t larger_sizes, ! 60: tme_uint8_t rex, ! 61: tme_uint16_t opcode_opreg) ! 62: { ! 63: unsigned long disp; ! 64: ! 65: /* the stacked host flags are always pushed in size order, largest ! 66: last. the least significant bit of larger_sizes must be set, and ! 67: corresponds to the size of the flags that we want. the remaining ! 68: set bits are for the larger flag sizes that we must skip over. ! 69: calculate the displacement to the flags we want, starting from ! 70: the largest: */ ! 71: assert (larger_sizes & 1); ! 72: larger_sizes -= 1; ! 73: disp = 0; ! 74: for (; larger_sizes != 0; larger_sizes &= (larger_sizes - 1)) { ! 75: disp += TME_BIT(TME_RECODE_SIZE_HOST - TME_RECODE_SIZE_8); ! 76: } ! 77: ! 78: /* if there is a guest function: */ ! 79: if (flags_group->tme_recode_flags_group_guest_func != NULL) { ! 80: ! 81: /* there may be arguments for the guest function stacked before ! 82: the host flags. on ia32, all of the guest function arguments ! 83: are stacked. on x86-64, the destination operand argument is ! 84: stacked, but only when double-host-size guests are ! 85: supported: */ ! 86: disp ! 87: += (TME_RECODE_SIZE_HOST == TME_RECODE_SIZE_32 ! 88: ? (sizeof(struct tme_ic *) ! 89: + sizeof(tme_recode_uguest_t) ! 90: + sizeof(tme_recode_uguest_t) ! 91: + sizeof(tme_recode_uguest_t)) ! 92: : TME_RECODE_SIZE_GUEST_MAX > TME_RECODE_SIZE_HOST ! 93: ? sizeof(tme_recode_uguest_t) ! 94: : 0); ! 95: } ! 96: ! 97: /* emit the %sp-relative reference: */ ! 98: if (rex != 0) { ! 99: *(thunk_bytes++) = rex; ! 100: } ! 101: *((tme_uint16_t *) &thunk_bytes[0]) ! 102: = (opcode_opreg ! 103: | (TME_RECODE_X86_MOD_OPREG_RM((disp == 0 ! 104: ? TME_RECODE_X86_MOD_RM_EA(TME_RECODE_X86_EA_BASE_SIB) ! 105: : TME_RECODE_X86_MOD_RM_EA_DISP8(TME_RECODE_X86_EA_BASE_SIB)), ! 106: 0) << 8)); ! 107: thunk_bytes[2] = TME_RECODE_X86_SIB(TME_RECODE_X86_REG_SP, TME_RECODE_X86_SIB_INDEX_NONE, 1); ! 108: thunk_bytes += 3; ! 109: if (disp != 0) { ! 110: assert (disp < 0x80); ! 111: *(thunk_bytes++) = disp; ! 112: } ! 113: ! 114: /* done: */ ! 115: return (thunk_bytes); ! 116: } ! 117: ! 118: /* this emits instructions to shift a destination register by some ! 119: count, and then add in a register and a constant. all of these ! 120: three operations are optional: */ ! 121: static tme_uint8_t * ! 122: _tme_recode_x86_flags_shift_add_add(const struct tme_recode_flags_group *flags_group, ! 123: tme_uint8_t *thunk_bytes, ! 124: unsigned int size, ! 125: unsigned int flags_reg_index_dest, ! 126: unsigned int dest_shift, ! 127: unsigned int flags_reg_index_addend, ! 128: tme_recode_uguest_t constant) ! 129: { ! 130: unsigned int reg_x86_dst; ! 131: tme_uint8_t rex; ! 132: unsigned int reg_x86_base; ! 133: unsigned int mod_rm; ! 134: unsigned int size_constant; ! 135: ! 136: /* to keep things simple and instruction size down, we promote all ! 137: operation sizes to at least 32 bits: */ ! 138: size = TME_MAX(size, TME_RECODE_SIZE_32); ! 139: ! 140: /* get the destination register: */ ! 141: reg_x86_dst = _TME_RECODE_X86_FLAGS_REG(flags_group, flags_reg_index_dest); ! 142: ! 143: /* if there is a destination shift, and it's more than three (more ! 144: than a factor of eight) or if there is no constant and no addend ! 145: register: */ ! 146: if (dest_shift != 0 ! 147: && (dest_shift > TME_RECODE_SIZE_8 ! 148: || (constant == 0 ! 149: && flags_reg_index_addend >= _TME_RECODE_X86_FLAGS_REG_COUNT))) { ! 150: ! 151: /* do all of the shifting with an shl instruction: */ ! 152: rex = TME_RECODE_X86_REX_R(size, reg_x86_dst); ! 153: if (rex != 0) { ! 154: *(thunk_bytes++) = rex; ! 155: } ! 156: thunk_bytes[0] = TME_RECODE_X86_OPCODE_GRP2_Ib_Ev; ! 157: thunk_bytes[1] ! 158: = TME_RECODE_X86_MOD_OPREG_RM(TME_RECODE_X86_MOD_RM_REG(reg_x86_dst), ! 159: TME_RECODE_X86_OPCODE_GRP2_SHL); ! 160: thunk_bytes[2] = dest_shift; ! 161: thunk_bytes += 3; ! 162: ! 163: /* we don't need to shift the destination any more: */ ! 164: dest_shift = 0; ! 165: } ! 166: ! 167: /* make the rex prefix for the lea. if there is an addend register, ! 168: that is the base register, otherwise if there is no destination ! 169: shift then the destination register is also the base register, ! 170: otherwise there is no base register. if there is a destination ! 171: shift, the destination register is also the index register, ! 172: otherwise there is no index register: */ ! 173: rex ! 174: = (TME_RECODE_X86_REX_R(size, reg_x86_dst) ! 175: | (flags_reg_index_addend < _TME_RECODE_X86_FLAGS_REG_COUNT ! 176: ? TME_RECODE_X86_REX_B(0, _TME_RECODE_X86_FLAGS_REG(flags_group, flags_reg_index_addend)) ! 177: : dest_shift == 0 ! 178: ? TME_RECODE_X86_REX_B(0, reg_x86_dst) ! 179: : 0) ! 180: | (dest_shift != 0 ! 181: ? TME_RECODE_X86_REX_X(reg_x86_dst) ! 182: : 0)); ! 183: ! 184: /* if there is an addend register or a destination shift, the R/M ! 185: base register indicates that there is a scale-index-base byte, ! 186: otherwise the destination register is the R/M base register: */ ! 187: reg_x86_base ! 188: = ((flags_reg_index_addend < _TME_RECODE_X86_FLAGS_REG_COUNT ! 189: || dest_shift != 0) ! 190: ? TME_RECODE_X86_EA_BASE_SIB ! 191: : reg_x86_dst); ! 192: ! 193: /* if there is no constant, we don't need a displacement: */ ! 194: if (constant == 0) { ! 195: mod_rm = TME_RECODE_X86_MOD_RM_EA(reg_x86_base); ! 196: size_constant = 0; ! 197: ! 198: /* if there is no scale-index-base byte, we don't need to emit the ! 199: lea instruction at all: */ ! 200: if (reg_x86_base != TME_RECODE_X86_EA_BASE_SIB) { ! 201: return (thunk_bytes); ! 202: } ! 203: } ! 204: ! 205: /* otherwise, if the constant fits into a sign-extended eight bits, ! 206: we can use that displacement form: */ ! 207: else if (constant < 0x7f) { ! 208: mod_rm = TME_RECODE_X86_MOD_RM_EA_DISP8(reg_x86_base); ! 209: size_constant = sizeof(tme_uint8_t); ! 210: } ! 211: ! 212: /* otherwise, we must use the 32-bit displacement form: */ ! 213: else { ! 214: ! 215: /* the constant must fit into 32 bits, unless the operation size is ! 216: 64 bits, in which case the constant must fit into 31 bits because ! 217: the 32-bit displacement will be sign-extended to 64 bits: */ ! 218: assert ((constant >> 31) < (1 + (size <= TME_RECODE_SIZE_32))); ! 219: mod_rm = TME_RECODE_X86_MOD_RM_EA_DISP32(reg_x86_base); ! 220: size_constant = sizeof(tme_uint32_t); ! 221: } ! 222: ! 223: /* emit the lea instruction: */ ! 224: if (rex != 0) { ! 225: *(thunk_bytes++) = rex; ! 226: } ! 227: thunk_bytes[0] = TME_RECODE_X86_OPCODE_LEA; ! 228: thunk_bytes[1] = TME_RECODE_X86_MOD_OPREG_RM(mod_rm, TME_RECODE_X86_REG(reg_x86_dst)); ! 229: thunk_bytes += 2; ! 230: if (reg_x86_base == TME_RECODE_X86_EA_BASE_SIB) { ! 231: *(thunk_bytes++) ! 232: = TME_RECODE_X86_SIB((flags_reg_index_addend < _TME_RECODE_X86_FLAGS_REG_COUNT ! 233: ? _TME_RECODE_X86_FLAGS_REG(flags_group, flags_reg_index_addend) ! 234: : TME_RECODE_X86_SIB_BASE_NONE), ! 235: reg_x86_dst, ! 236: (1 << dest_shift)); ! 237: } ! 238: memcpy(thunk_bytes, &constant, size_constant); ! 239: thunk_bytes += size_constant; ! 240: ! 241: /* done: */ ! 242: return (thunk_bytes); ! 243: } ! 244: ! 245: /* this updates one host-sized part of the guest flags: */ ! 246: static tme_uint8_t * ! 247: _tme_recode_x86_flags_update(const struct tme_recode_flags_group *flags_group, ! 248: tme_uint8_t *thunk_bytes, ! 249: tme_recode_uguest_t flags_mask_update, ! 250: unsigned int flags_reg_index_accum, ! 251: tme_recode_uguest_t flag_accum_last, ! 252: unsigned int flags_reg_index_tmp) ! 253: { ! 254: tme_recode_uguest_t flags_defined_host; ! 255: tme_recode_uguest_t flags_set; ! 256: tme_recode_uguest_t flags_clear; ! 257: tme_recode_uguest_t flags_constant; ! 258: unsigned int byte_shift; ! 259: unsigned long disp; ! 260: unsigned int size_flags_defined; ! 261: unsigned int size_constant; ! 262: unsigned int binop; ! 263: unsigned int reg_x86; ! 264: tme_uint8_t rex; ! 265: ! 266: /* get the mask of all defined flags that we are providing, that we ! 267: are updating now. if this is zero, return now: */ ! 268: flags_defined_host = tme_recode_flags_group_flags_defined_host(flags_group, TME_RECODE_COND_UNDEF); ! 269: flags_defined_host &= flags_mask_update; ! 270: if (flags_defined_host == 0) { ! 271: return (thunk_bytes); ! 272: } ! 273: ! 274: /* get the masks of flags that are always cleared or set, that we ! 275: are updating now. either or both of these may be zero: */ ! 276: flags_clear = tme_recode_flags_group_flags_defined_host(flags_group, TME_RECODE_COND_FALSE); ! 277: flags_clear &= flags_mask_update; ! 278: flags_set = tme_recode_flags_group_flags_defined_host(flags_group, TME_RECODE_COND_MOD_NOT | TME_RECODE_COND_FALSE); ! 279: flags_set &= flags_mask_update; ! 280: ! 281: /* we only update whole bytes. shift whole bytes of zero bits off ! 282: of all guest flags masks, and get the displacement within the ! 283: guest struct tme_ic of the first byte we are updating: */ ! 284: byte_shift = _tme_recode_x86_ffs(flags_defined_host) & (((unsigned int) 0) - 8); ! 285: flags_defined_host >>= byte_shift; ! 286: flags_clear >>= byte_shift; ! 287: flags_set >>= byte_shift; ! 288: flag_accum_last >>= byte_shift; ! 289: disp ! 290: = (tme_recode_flags_reg_offset(flags_group->tme_recode_flags_group_flags_reg_size, ! 291: flags_group->tme_recode_flags_group_flags_reg) ! 292: + (((flags_mask_update & 1) == 0 ! 293: ? TME_BIT(TME_RECODE_SIZE_HOST - TME_RECODE_SIZE_8) ! 294: : 0) ! 295: + (byte_shift / 8))); ! 296: ! 297: /* get the log base two of the number of bytes in the guest flags ! 298: register that we are updating: */ ! 299: size_flags_defined ! 300: = ((flags_defined_host <= 0xff) ! 301: ? TME_RECODE_SIZE_8 ! 302: : (flags_defined_host <= 0xffff) ! 303: ? TME_RECODE_SIZE_16 ! 304: : (TME_RECODE_SIZE_32 ! 305: + ((flags_defined_host >> 31) > 1))); ! 306: ! 307: /* to keep things simple (and to avoid updating eight bit register ! 308: parts, which may decrease performance), we always use at least ! 309: 32-bit immediates with register operations: */ ! 310: size_constant = TME_MAX(size_flags_defined, TME_RECODE_SIZE_32); ! 311: ! 312: /* if the mask of all defined guest flags that we are providing, ! 313: that we are updating now, is all flags that are always cleared, ! 314: or all flags that are always set: */ ! 315: if (flags_defined_host == flags_clear ! 316: || flags_defined_host == flags_set) { ! 317: ! 318: /* get the constant and binop for clearing or setting flags: */ ! 319: if (flags_defined_host == flags_clear) { ! 320: flags_constant = ~flags_clear; ! 321: binop = TME_RECODE_X86_OPCODE_BINOP_AND; ! 322: } ! 323: else { ! 324: flags_constant = flags_set; ! 325: binop = TME_RECODE_X86_OPCODE_BINOP_OR; ! 326: } ! 327: ! 328: /* if this is a 64-bit operation, we have to load the constant ! 329: into the temporary register and then do the binop from the ! 330: temporary register to memory, since there aren't binop ! 331: instructions with 64-bit immediates: */ ! 332: if (size_flags_defined > TME_RECODE_SIZE_32) { ! 333: ! 334: /* load the temporary register with the 64-bit immediate: */ ! 335: reg_x86 = _TME_RECODE_X86_FLAGS_REG(flags_group, flags_reg_index_tmp); ! 336: thunk_bytes[0] = TME_RECODE_X86_REX_B(TME_RECODE_SIZE_HOST, reg_x86); ! 337: thunk_bytes[1] = TME_RECODE_X86_OPCODE_MOV_Iv_Gv(reg_x86); ! 338: memcpy(&thunk_bytes[2], &flags_constant, TME_BIT(TME_RECODE_SIZE_HOST - TME_RECODE_SIZE_8)); ! 339: thunk_bytes += 1 + 1 + TME_BIT(TME_RECODE_SIZE_HOST - TME_RECODE_SIZE_8); ! 340: ! 341: /* update the guest flags: */ ! 342: thunk_bytes[0] ! 343: = (TME_RECODE_X86_REX_B(0, TME_RECODE_X86_REG_IC) ! 344: | TME_RECODE_X86_REX_R(TME_RECODE_SIZE_HOST, reg_x86)); ! 345: thunk_bytes[1] = (binop + TME_RECODE_X86_OPCODE_BINOP_Gv_Ev); ! 346: thunk_bytes ! 347: = _tme_recode_x86_emit_ic_modrm(thunk_bytes + 2, ! 348: disp, ! 349: reg_x86); ! 350: ! 351: /* done: */ ! 352: return (thunk_bytes); ! 353: } ! 354: ! 355: /* otherwise, emit the binop from the constant to memory: */ ! 356: if (size_flags_defined == TME_RECODE_SIZE_16) { ! 357: *(thunk_bytes++) = TME_RECODE_X86_PREFIX_OPSIZ; ! 358: } ! 359: #if TME_RECODE_X86_REG_IC >= 8 ! 360: #error "TME_RECODE_X86_REG_IC changed" ! 361: #endif ! 362: *(thunk_bytes++) ! 363: = (size_flags_defined == TME_RECODE_SIZE_8 ! 364: ? TME_RECODE_X86_OPCODE_GRP1_Ib_Eb ! 365: : TME_RECODE_X86_OPCODE_GRP1_Iz_Ev); ! 366: thunk_bytes ! 367: = _tme_recode_x86_emit_ic_modrm(thunk_bytes, ! 368: disp, ! 369: TME_RECODE_X86_OPCODE_GRP1_BINOP(binop)); ! 370: memcpy(thunk_bytes, ! 371: &flags_constant, ! 372: TME_BIT(size_flags_defined - TME_RECODE_SIZE_8)); ! 373: thunk_bytes += TME_BIT(size_flags_defined - TME_RECODE_SIZE_8); ! 374: ! 375: /* done: */ ! 376: return (thunk_bytes); ! 377: } ! 378: ! 379: /* if we're updating all of the bits in a tme_uint8_t, tme_uint16_t, ! 380: tme_uint32_t, or tme_uint64_t and we're not providing any ! 381: variable guest flags, or if this is a 64-bit operation and the ! 382: flags we're setting don't fit into 31 bits, we have to get the ! 383: always-set flags into the register that (would have held any / ! 384: does hold the) variable guest flags. in the first case, this is ! 385: an optimization, since the guest flags don't have to be read at ! 386: all, and in the second case this is essential since an lea ! 387: displacement is a sign-extended 32 bits: */ ! 388: if ((flags_defined_host == ((((unsigned long) 2) << (TME_BIT(size_flags_defined) - 1)) - 1) ! 389: && flag_accum_last == 0) ! 390: || (size_flags_defined > TME_RECODE_SIZE_32 ! 391: && (flags_set >> 30) > 1)) { ! 392: ! 393: /* if there are any variable guest flags, load the always-set ! 394: flags into the temporary register, otherwise load them directly ! 395: into the register that would have held any variable guest ! 396: flags: */ ! 397: reg_x86 ! 398: = _TME_RECODE_X86_FLAGS_REG(flags_group, ! 399: (flag_accum_last != 0 ! 400: ? flags_reg_index_tmp ! 401: : flags_reg_index_accum)); ! 402: rex = TME_RECODE_X86_REX_B(size_constant, reg_x86); ! 403: if (rex != 0) { ! 404: *(thunk_bytes++) = rex; ! 405: } ! 406: thunk_bytes[0] = TME_RECODE_X86_OPCODE_MOV_Iv_Gv(reg_x86); ! 407: memcpy(&thunk_bytes[1], &flags_set, TME_BIT(size_constant - TME_RECODE_SIZE_8)); ! 408: thunk_bytes += 1 + TME_BIT(size_constant - TME_RECODE_SIZE_8); ! 409: ! 410: /* if there are any variable guest flags, shift the accumulation ! 411: register as needed and add in the always-set flags in the ! 412: temporary register: */ ! 413: if (flag_accum_last != 0) { ! 414: thunk_bytes ! 415: = _tme_recode_x86_flags_shift_add_add(flags_group, ! 416: thunk_bytes, ! 417: size_constant, ! 418: flags_reg_index_accum, ! 419: _tme_recode_x86_ffs(flag_accum_last), ! 420: flags_reg_index_tmp, ! 421: 0); ! 422: } ! 423: ! 424: /* we just combined the always-set flags with any variable guest ! 425: flags. from now on, there are no always-set flags and act like ! 426: there are variable guest flags: */ ! 427: flag_accum_last = 1; ! 428: flags_set = 0; ! 429: } ! 430: ! 431: /* if we're updating all of the bits in a tme_uint8_t, tme_uint16_t, ! 432: tme_uint32_t, or tme_uint64_t: */ ! 433: if (flags_defined_host == ((((unsigned long) 2) << (TME_BIT(size_flags_defined) - 1)) - 1)) { ! 434: ! 435: /* there must be some variable guest flags: */ ! 436: assert (flag_accum_last != 0); ! 437: ! 438: /* we don't have to read the guest flags at all to preserve any ! 439: bits, so we don't have to add the temporary register to the ! 440: variable guest flags below: */ ! 441: flags_reg_index_tmp = _TME_RECODE_X86_FLAGS_REG_COUNT; ! 442: } ! 443: ! 444: /* otherwise, we're not updating all of the bits in a tme_uint8_t, ! 445: tme_uint16_t, tme_uint32_t, or tme_uint64_t. we have to read the ! 446: guest flags, mask off the flags that we are updating, and ! 447: preserve the rest: */ ! 448: else { ! 449: ! 450: /* if there are any variable guest flags, load the inverse of the ! 451: mask of the guest flags that we are updating into the temporary ! 452: register, otherwise load it directly into the register that ! 453: would have held any variable guest flags: */ ! 454: flags_clear = ~flags_defined_host; ! 455: reg_x86 ! 456: = _TME_RECODE_X86_FLAGS_REG(flags_group, ! 457: (flag_accum_last != 0 ! 458: ? flags_reg_index_tmp ! 459: : flags_reg_index_accum)); ! 460: rex = TME_RECODE_X86_REX_B(size_constant, reg_x86); ! 461: if (rex != 0) { ! 462: *(thunk_bytes++) = rex; ! 463: } ! 464: thunk_bytes[0] = TME_RECODE_X86_OPCODE_MOV_Iv_Gv(reg_x86); ! 465: memcpy(&thunk_bytes[1], &flags_clear, TME_BIT(size_constant - TME_RECODE_SIZE_8)); ! 466: thunk_bytes += 1 + TME_BIT(size_constant - TME_RECODE_SIZE_8); ! 467: ! 468: /* binary-and the part of the guest flags register that we are ! 469: updating into that same register: */ ! 470: if (size_flags_defined == TME_RECODE_SIZE_16) { ! 471: *(thunk_bytes++) = TME_RECODE_X86_PREFIX_OPSIZ; ! 472: } ! 473: rex = TME_RECODE_X86_REX_B(size_flags_defined, reg_x86); ! 474: if (rex != 0) { ! 475: *(thunk_bytes++) = rex; ! 476: } ! 477: *(thunk_bytes++) ! 478: = (TME_RECODE_X86_OPCODE_BINOP_AND ! 479: + (size_flags_defined == TME_RECODE_SIZE_8 ! 480: ? TME_RECODE_X86_OPCODE_BINOP_Eb_Gb ! 481: : TME_RECODE_X86_OPCODE_BINOP_Ev_Gv)); ! 482: thunk_bytes ! 483: = _tme_recode_x86_emit_ic_modrm(thunk_bytes, ! 484: disp, ! 485: reg_x86); ! 486: ! 487: /* if there are no variable guest flags, we're only providing ! 488: always-set and always-clear flags, which we now have in the ! 489: register that would have held variable guest flags. we don't ! 490: have to add in the temporary register below: */ ! 491: if (flag_accum_last == 0) { ! 492: flags_reg_index_tmp = _TME_RECODE_X86_FLAGS_REG_COUNT; ! 493: } ! 494: } ! 495: ! 496: /* shift the accumulation register as needed and add in any ! 497: temporary register and any always-set guest flags: */ ! 498: thunk_bytes ! 499: = _tme_recode_x86_flags_shift_add_add(flags_group, ! 500: thunk_bytes, ! 501: size_flags_defined, ! 502: flags_reg_index_accum, ! 503: _tme_recode_x86_ffs(flag_accum_last), ! 504: flags_reg_index_tmp, ! 505: flags_set); ! 506: ! 507: /* update the guest flags register: */ ! 508: reg_x86 = _TME_RECODE_X86_FLAGS_REG(flags_group, flags_reg_index_accum); ! 509: if (size_flags_defined == TME_RECODE_SIZE_16) { ! 510: *(thunk_bytes++) = TME_RECODE_X86_PREFIX_OPSIZ; ! 511: } ! 512: rex = (TME_RECODE_X86_REX_R(size_flags_defined, reg_x86) ! 513: + TME_RECODE_X86_REX_B(0, TME_RECODE_X86_REG_IC)); ! 514: if (rex != 0) { ! 515: *(thunk_bytes++) = rex; ! 516: } ! 517: *(thunk_bytes++) ! 518: = (TME_RECODE_X86_OPCODE_BINOP_MOV ! 519: + (size_flags_defined == TME_RECODE_SIZE_8 ! 520: ? TME_RECODE_X86_OPCODE_BINOP_Gb_Eb ! 521: : TME_RECODE_X86_OPCODE_BINOP_Gv_Ev)); ! 522: thunk_bytes ! 523: = _tme_recode_x86_emit_ic_modrm(thunk_bytes, ! 524: disp, ! 525: reg_x86); ! 526: ! 527: /* done: */ ! 528: return (thunk_bytes); ! 529: } ! 530: ! 531: /* this returns a new host flags thunk: */ ! 532: struct tme_recode_flags_thunk * ! 533: tme_recode_host_flags_thunk_new(struct tme_recode_ic *ic, ! 534: const struct tme_recode_flags_thunk *flags_thunk_template, ! 535: const struct tme_recode_flags_group *flags_group) ! 536: { ! 537: struct tme_recode_flags_thunk *flags_thunk; ! 538: tme_uint8_t *thunk_bytes; ! 539: tme_recode_uguest_t flags_defined_host; ! 540: tme_recode_uguest_t flags_variable_host; ! 541: tme_uint32_t host_sizes; ! 542: int stack_adjust; ! 543: tme_uint32_t host_sizes_other; ! 544: tme_recode_uguest_t flags_mask_0; ! 545: tme_recode_uguest_t flags_mask_1; ! 546: unsigned int size_accum; ! 547: const struct tme_recode_flag *flag; ! 548: tme_uint32_t cond; ! 549: tme_uint32_t cond_mods; ! 550: tme_recode_uguest_t factor; ! 551: unsigned int subfactor; ! 552: unsigned int flags_reg_index_next; ! 553: unsigned int flags_reg_index_accum; ! 554: unsigned int flags_reg_drain; ! 555: tme_uint8_t rex; ! 556: unsigned int x86_reg_0; ! 557: unsigned int x86_reg_1; ! 558: tme_recode_uguest_t flags_reg_index_to_flag[_TME_RECODE_X86_FLAGS_REG_COUNT]; ! 559: tme_uint32_t host_size_loaded; ! 560: ! 561: /* start the new flags thunk: */ ! 562: if (!tme_recode_host_thunk_start(ic)) { ! 563: abort(); ! 564: } ! 565: flags_thunk = tme_new(struct tme_recode_flags_thunk, 1); ! 566: *flags_thunk = *flags_thunk_template; ! 567: flags_thunk->tme_recode_x86_flags_thunk_subs_main ! 568: = tme_recode_build_to_thunk_off(ic, ic->tme_recode_ic_thunk_build_next); ! 569: ! 570: /* start instructions: */ ! 571: tme_recode_x86_insns_start(ic, thunk_bytes); ! 572: ! 573: /* get the mask of all guest flags that we are providing. this may ! 574: be zero: */ ! 575: flags_defined_host = tme_recode_flags_group_flags_defined_host(flags_group, TME_RECODE_COND_UNDEF); ! 576: ! 577: /* get the mask of variable guest flags that we are providing. this ! 578: may be zero: */ ! 579: flags_variable_host ! 580: = (flags_defined_host ! 581: & ~(tme_recode_flags_group_flags_defined_host(flags_group, TME_RECODE_COND_FALSE) ! 582: | tme_recode_flags_group_flags_defined_host(flags_group, TME_RECODE_COND_MOD_NOT | TME_RECODE_COND_FALSE))); ! 583: ! 584: /* get the mask of sizes for which we need to provide at least one ! 585: variable flag in the given flags group. this may be ! 586: zero: */ ! 587: host_sizes = tme_recode_flags_group_sizes(flags_group, flags_variable_host); ! 588: ! 589: /* it's easiest to assume that we always need the instruction-size ! 590: flags when we need any flags at all, since the instruction-size ! 591: operation always happens last no matter what, and leaves its ! 592: flags in the flags register: */ ! 593: if (host_sizes != 0) { ! 594: host_sizes |= TME_BIT(flags_group->tme_recode_flags_group_insn_size); ! 595: } ! 596: ! 597: /* when we need the double-host-size flags, we also need the ! 598: host-size flags, because we need the host-size Z flag to complete ! 599: the double-host-size Z flag: */ ! 600: if (host_sizes & TME_BIT(TME_RECODE_SIZE_HOST + 1)) { ! 601: host_sizes |= TME_BIT(TME_RECODE_SIZE_HOST); ! 602: } ! 603: ! 604: /* assume that we won't need to remove anything from the stack ! 605: before returning to the insn thunk: */ ! 606: stack_adjust = 0; ! 607: ! 608: /* if there are flags from multiple sizes: */ ! 609: if (host_sizes & (host_sizes - 1)) { ! 610: ! 611: /* the flags for the instruction size are in the flags register on ! 612: entry into the thunk, and the others are stacked in size order, ! 613: with the flags for the smallest size farthest away from the ! 614: stack pointer. emit a pushf to save the instruction-size flags ! 615: on the stack: */ ! 616: *(thunk_bytes++) = TME_RECODE_X86_OPCODE_PUSHF; ! 617: ! 618: /* before returning to the insn thunk, we will need to remove all ! 619: of these flags from the stack: */ ! 620: for (host_sizes_other = host_sizes; ! 621: host_sizes_other != 0; ! 622: host_sizes_other &= (host_sizes_other - 1)) { ! 623: stack_adjust += TME_BIT(TME_RECODE_SIZE_HOST - TME_RECODE_SIZE_8); ! 624: } ! 625: } ! 626: ! 627: /* assume that the opcode-specific flags subs won't need to ! 628: do any stack padding: */ ! 629: flags_thunk->tme_recode_x86_flags_thunk_stack_padding = 0; ! 630: ! 631: /* if there is a guest function: */ ! 632: flags_thunk->tme_recode_x86_flags_thunk_has_guest_func ! 633: = (flags_group->tme_recode_flags_group_guest_func != NULL); ! 634: if (flags_thunk->tme_recode_x86_flags_thunk_has_guest_func) { ! 635: ! 636: /* if this is an ia32 host: */ ! 637: if (TME_RECODE_SIZE_HOST == TME_RECODE_SIZE_32) { ! 638: ! 639: /* push the destination operand for the guest function: */ ! 640: if (TME_RECODE_SIZE_GUEST_MAX > TME_RECODE_SIZE_HOST) { ! 641: _tme_recode_x86_emit_reg_push(thunk_bytes, tme_recode_x86_reg_from_host[TME_RECODE_X86_REG_HOST_SUBS_DST + 1]); ! 642: } ! 643: _tme_recode_x86_emit_reg_push(thunk_bytes, tme_recode_x86_reg_from_host[TME_RECODE_X86_REG_HOST_SUBS_DST]); ! 644: ! 645: /* push the second source operand for the guest function: */ ! 646: if (TME_RECODE_SIZE_GUEST_MAX > TME_RECODE_SIZE_HOST) { ! 647: _tme_recode_x86_emit_reg_push(thunk_bytes, tme_recode_x86_reg_from_host[TME_RECODE_X86_REG_HOST_SUBS_SRC1 + 1]); ! 648: } ! 649: _tme_recode_x86_emit_reg_push(thunk_bytes, tme_recode_x86_reg_from_host[TME_RECODE_X86_REG_HOST_SUBS_SRC1]); ! 650: ! 651: /* push the first source operand for the guest function: */ ! 652: if (TME_RECODE_SIZE_GUEST_MAX > TME_RECODE_SIZE_HOST) { ! 653: _tme_recode_x86_emit_reg_push(thunk_bytes, tme_recode_x86_reg_from_host[TME_RECODE_X86_REG_HOST_SUBS_SRC0 + 1]); ! 654: } ! 655: _tme_recode_x86_emit_reg_push(thunk_bytes, tme_recode_x86_reg_from_host[TME_RECODE_X86_REG_HOST_SUBS_SRC0]); ! 656: ! 657: /* push the struct tme_ic * for the guest function: */ ! 658: _tme_recode_x86_emit_reg_push(thunk_bytes, TME_RECODE_X86_REG_IC); ! 659: ! 660: /* before returning to the insn thunk, we will need to remove ! 661: all of these guest function arguments from the stack: */ ! 662: stack_adjust ! 663: += (sizeof(struct tme_ic *) ! 664: + sizeof(tme_recode_uguest_t) ! 665: + sizeof(tme_recode_uguest_t) ! 666: + sizeof(tme_recode_uguest_t)); ! 667: } ! 668: ! 669: /* otherwise, this is an x86-64 host: */ ! 670: else { ! 671: ! 672: /* copy the struct tme_ic * for the guest function: */ ! 673: _tme_recode_x86_emit_reg_copy(thunk_bytes, ! 674: TME_RECODE_X86_REG_IC, ! 675: TME_RECODE_X86_REG_DI); ! 676: ! 677: /* copy the (least-significant half of) the first source operand ! 678: for the guest function: */ ! 679: _tme_recode_x86_emit_reg_copy(thunk_bytes, ! 680: tme_recode_x86_reg_from_host[TME_RECODE_X86_REG_HOST_SUBS_SRC0 + 0], ! 681: TME_RECODE_X86_REG_SI); ! 682: ! 683: /* if double-host-size guests are supported: */ ! 684: if (TME_RECODE_SIZE_GUEST_MAX > TME_RECODE_SIZE_HOST) { ! 685: ! 686: /* copy the most-significant half of the first source operand ! 687: for the guest function: */ ! 688: _tme_recode_x86_emit_reg_copy(thunk_bytes, ! 689: tme_recode_x86_reg_from_host[TME_RECODE_X86_REG_HOST_SUBS_SRC0 + 1], ! 690: TME_RECODE_X86_REG_D); ! 691: ! 692: /* copy the second source operand for the guest function: */ ! 693: _tme_recode_x86_emit_reg_copy(thunk_bytes, ! 694: tme_recode_x86_reg_from_host[TME_RECODE_X86_REG_HOST_SUBS_SRC1 + 0], ! 695: TME_RECODE_X86_REG_C); ! 696: _tme_recode_x86_emit_reg_copy(thunk_bytes, ! 697: tme_recode_x86_reg_from_host[TME_RECODE_X86_REG_HOST_SUBS_SRC1 + 1], ! 698: TME_RECODE_X86_REG_N(8)); ! 699: ! 700: /* push the destination operand for the guest function: */ ! 701: _tme_recode_x86_emit_reg_push(thunk_bytes, tme_recode_x86_reg_from_host[TME_RECODE_X86_REG_HOST_SUBS_DST + 1]); ! 702: _tme_recode_x86_emit_reg_push(thunk_bytes, tme_recode_x86_reg_from_host[TME_RECODE_X86_REG_HOST_SUBS_DST + 0]); ! 703: ! 704: /* before returning to the insn thunk, we will need to remove ! 705: the destination operand argument from the stack: */ ! 706: stack_adjust += sizeof(tme_recode_uguest_t); ! 707: ! 708: /* the x86-64 ABI requires that the stack pointer be 16-byte ! 709: aligned immediately before a call instruction. inside an ! 710: insn thunk, the stack pointer is 16-byte aligned ! 711: immediately before the call to a subs, which means at the ! 712: beginning of the subs it is only 8-byte aligned (because of ! 713: the return address for the subs). ! 714: ! 715: if our stack won't be 16-byte aligned before we call the ! 716: guest function, the subs that chained to us will need to ! 717: pad the stack with one extra word to get it aligned: */ ! 718: if ((TME_BIT(TME_RECODE_SIZE_HOST - TME_RECODE_SIZE_8) /* the subs return address */ ! 719: + stack_adjust) ! 720: % (TME_BIT(TME_RECODE_SIZE_HOST - TME_RECODE_SIZE_8) * 2)) { ! 721: flags_thunk->tme_recode_x86_flags_thunk_stack_padding = TME_BIT(TME_RECODE_SIZE_HOST - TME_RECODE_SIZE_8); ! 722: } ! 723: } ! 724: ! 725: /* otherwise, double-host-size guests are not supported: */ ! 726: else { ! 727: ! 728: /* copy the second source operand for the guest function: */ ! 729: _tme_recode_x86_emit_reg_copy(thunk_bytes, ! 730: tme_recode_x86_reg_from_host[TME_RECODE_X86_REG_HOST_SUBS_SRC1], ! 731: TME_RECODE_X86_REG_D); ! 732: ! 733: /* copy the destination operand for the guest function: */ ! 734: _tme_recode_x86_emit_reg_copy(thunk_bytes, ! 735: tme_recode_x86_reg_from_host[TME_RECODE_X86_REG_HOST_SUBS_DST], ! 736: TME_RECODE_X86_REG_C); ! 737: ! 738: /* NB that when double-host-size guests are not supported, we ! 739: will always chain to the guest function, so we don't have ! 740: to worry about the ABI stack alignment like we do above: */ ! 741: } ! 742: } ! 743: ! 744: /* we will also have to remove any stack padding: */ ! 745: stack_adjust += flags_thunk->tme_recode_x86_flags_thunk_stack_padding; ! 746: } ! 747: ! 748: /* if any double-host-size flag is needed: */ ! 749: if (host_sizes & TME_BIT(TME_RECODE_SIZE_HOST + 1)) { ! 750: ! 751: /* the instruction-size flags we pushed above are incomplete ! 752: double-host-size flags, and there are also host-size flags on ! 753: the stack. load the least-significant 32 bits of the host-size ! 754: flags from the stack into %eax: */ ! 755: thunk_bytes ! 756: = _tme_recode_x86_flags_host_flags(flags_group, ! 757: thunk_bytes, ! 758: (host_sizes >> TME_RECODE_SIZE_HOST), ! 759: TME_RECODE_X86_REX_R(TME_RECODE_SIZE_32, TME_RECODE_X86_REG_A), ! 760: ((TME_RECODE_X86_OPCODE_BINOP_MOV ! 761: + TME_RECODE_X86_OPCODE_BINOP_Ev_Gv) ! 762: + (TME_RECODE_X86_MOD_OPREG_RM(0, ! 763: TME_RECODE_X86_REG(TME_RECODE_X86_REG_A)) ! 764: << 8))); ! 765: ! 766: /* or a mask of all-bits-one, minus the Z flag, into %eax: */ ! 767: thunk_bytes[0] = TME_RECODE_X86_OPCODE_GRP1_Ib_Ev; ! 768: thunk_bytes[1] = TME_RECODE_X86_MOD_OPREG_RM(TME_RECODE_X86_MOD_RM_REG(TME_RECODE_X86_REG_A), TME_RECODE_X86_OPCODE_GRP1_BINOP(TME_RECODE_X86_OPCODE_BINOP_OR)); ! 769: #if TME_RECODE_X86_FLAG_Z >= 0x80 ! 770: #error "TME_RECODE_X86_FLAG_Z value changed" ! 771: #endif ! 772: thunk_bytes[2] = ~TME_RECODE_X86_FLAG_Z; ! 773: thunk_bytes += 3; ! 774: ! 775: /* and %eax into the least-significant 32 bits of the ! 776: double-host-size flags on the stack: */ ! 777: thunk_bytes ! 778: = _tme_recode_x86_flags_host_flags(flags_group, ! 779: thunk_bytes, ! 780: (host_sizes >> (TME_RECODE_SIZE_HOST + 1)), ! 781: TME_RECODE_X86_REX_R(TME_RECODE_SIZE_32, TME_RECODE_X86_REG_A), ! 782: ((TME_RECODE_X86_OPCODE_BINOP_AND ! 783: + TME_RECODE_X86_OPCODE_BINOP_Gv_Ev) ! 784: + (TME_RECODE_X86_MOD_OPREG_RM(0, ! 785: TME_RECODE_X86_REG(TME_RECODE_X86_REG_A)) ! 786: << 8))); ! 787: ! 788: /* initially, no host flags are loaded: */ ! 789: host_size_loaded = TME_RECODE_SIZE_1; ! 790: } ! 791: ! 792: /* otherwise, no double-host-size flag is needed: */ ! 793: else { ! 794: ! 795: /* the flags from the instruction size are always in the flags ! 796: register on entry into the thunk: */ ! 797: host_size_loaded = flags_group->tme_recode_flags_group_insn_size; ! 798: } ! 799: ! 800: /* to keep things simple, only whole bytes are updated in the guest ! 801: flags register. with only host-size or smaller guests, this ! 802: isn't so important since one host register will always cover the ! 803: entire guest flags register, and most double-host-size guests ! 804: will have their flags concentrated in their flags register so ! 805: that one host flags register will also cover all of them, even when ! 806: only updating whole bytes. rarely, a double-host-size guest may ! 807: have flags in so many bytes of its flags register that we have to ! 808: split it in two halves, updated separately. when we do this, ! 809: flags_mask_1 is all-bits-one covering the most significant half ! 810: of the guest flags register, otherwise flags_mask_1 is zero: */ ! 811: flags_mask_1 ! 812: = ((TME_SHIFT(tme_recode_uguest_t, ! 813: _tme_recode_x86_ffs_byte_shift(flags_defined_host), ! 814: >>, ! 815: TME_BIT(TME_RECODE_SIZE_HOST) - 1) ! 816: > 1) ! 817: * (0 - TME_SHIFT(tme_recode_uguest_t, 1, <<, TME_BIT(TME_RECODE_SIZE_HOST)))); ! 818: flags_mask_0 = ~flags_mask_1; ! 819: ! 820: /* if we aren't providing any variable guest flags: */ ! 821: if (flags_variable_host == 0) { ! 822: ! 823: /* we need to clear flags register zero, which will be used as the ! 824: accumulated register for one or two ! 825: _tme_recode_x86_flags_update() calls: */ ! 826: flags_reg_index_next = 1; ! 827: } ! 828: ! 829: /* otherwise, if the whole-bytes span of defined flags in any ! 830: host-sized part of the guest flags register is bigger than eight ! 831: bits: */ ! 832: else if ((_tme_recode_x86_ffs_byte_shift(flags_defined_host & flags_mask_0) ! 833: | _tme_recode_x86_ffs_byte_shift(flags_defined_host & flags_mask_1)) ! 834: > 0xff) { ! 835: ! 836: /* we need to clear all flags registers because setcc only sets ! 837: the least significant byte of a register. otherwise, the ! 838: garbage in the other bytes would get accumulated into the ! 839: results: */ ! 840: flags_reg_index_next = _TME_RECODE_X86_FLAGS_REG_COUNT; ! 841: } ! 842: ! 843: /* otherwise, we are providing variable guest flags, and the ! 844: whole-bytes span of defined guest flags in the one or two ! 845: host-sized parts of the guest flags register fits into eight ! 846: bits: */ ! 847: else { ! 848: ! 849: /* in this case, we don't need to clear any flags registers at ! 850: all. instead, we rely on setcc to clear the least significant ! 851: eight bits of flags registers: */ ! 852: flags_reg_index_next = 0; ! 853: } ! 854: ! 855: /* if there are any flags registers to clear: */ ! 856: if (flags_reg_index_next > 0) { ! 857: ! 858: /* if there are host flags initially loaded: */ ! 859: if (host_size_loaded != TME_RECODE_SIZE_1) { ! 860: ! 861: /* use only flags-preserving mov instructions: */ ! 862: ! 863: /* mov an immediate zero into the first flags register: */ ! 864: x86_reg_0 = _TME_RECODE_X86_FLAGS_REG(flags_group, 0); ! 865: rex = TME_RECODE_X86_REX_B(TME_RECODE_SIZE_32, x86_reg_0); ! 866: if (rex != 0) { ! 867: *(thunk_bytes++) = rex; ! 868: } ! 869: thunk_bytes[0] = TME_RECODE_X86_OPCODE_MOV_Iv_Gv(x86_reg_0); ! 870: *((tme_uint32_t *) &thunk_bytes[1]) = 0; ! 871: thunk_bytes += 1 + sizeof(tme_uint32_t); ! 872: ! 873: /* use register-to-register moves to copy that zero into any ! 874: other flags registers: */ ! 875: for (flags_reg_index_accum = 1; ! 876: flags_reg_index_accum < flags_reg_index_next; ! 877: flags_reg_index_accum++) { ! 878: x86_reg_1 = _TME_RECODE_X86_FLAGS_REG(flags_group, flags_reg_index_accum); ! 879: rex ! 880: = (TME_RECODE_X86_REX_B(TME_RECODE_SIZE_32, x86_reg_0) ! 881: | TME_RECODE_X86_REX_R(TME_RECODE_SIZE_32, x86_reg_1)); ! 882: if (rex != 0) { ! 883: *(thunk_bytes++) = rex; ! 884: } ! 885: thunk_bytes[0] = (TME_RECODE_X86_OPCODE_BINOP_MOV + TME_RECODE_X86_OPCODE_BINOP_Ev_Gv); ! 886: thunk_bytes[1] = TME_RECODE_X86_MOD_OPREG_RM(TME_RECODE_X86_MOD_RM_REG(x86_reg_0), TME_RECODE_X86_REG(x86_reg_1)); ! 887: thunk_bytes += 1 + 1; ! 888: } ! 889: } ! 890: ! 891: /* otherwise, no host flags are initially loaded: */ ! 892: else { ! 893: ! 894: /* use xor instructions to clear all of the registers: */ ! 895: for (flags_reg_index_accum = 0; ! 896: flags_reg_index_accum < flags_reg_index_next; ! 897: flags_reg_index_accum++) { ! 898: x86_reg_0 = _TME_RECODE_X86_FLAGS_REG(flags_group, flags_reg_index_accum); ! 899: rex ! 900: = (TME_RECODE_X86_REX_B(TME_RECODE_SIZE_32, x86_reg_0) ! 901: | TME_RECODE_X86_REX_R(TME_RECODE_SIZE_32, x86_reg_0)); ! 902: if (rex != 0) { ! 903: *(thunk_bytes++) = rex; ! 904: } ! 905: thunk_bytes[0] = (TME_RECODE_X86_OPCODE_BINOP_XOR + TME_RECODE_X86_OPCODE_BINOP_Ev_Gv); ! 906: thunk_bytes[1] = TME_RECODE_X86_MOD_OPREG_RM(TME_RECODE_X86_MOD_RM_REG(x86_reg_0), TME_RECODE_X86_REG(x86_reg_0)); ! 907: thunk_bytes += 2; ! 908: } ! 909: } ! 910: } ! 911: ! 912: /* if the whole-bytes span of variable flags in any host-sized part ! 913: of the guest flags register is bigger than 32 bits, we must ! 914: accumulate using 64-bit math, otherwise we can use 32-bit ! 915: math: */ ! 916: size_accum ! 917: = (TME_RECODE_SIZE_32 ! 918: + (((_tme_recode_x86_ffs_byte_shift(flags_variable_host & flags_mask_0) ! 919: | _tme_recode_x86_ffs_byte_shift(flags_variable_host & flags_mask_1)) ! 920: >> 31) ! 921: > 1)); ! 922: ! 923: /* start accumulating the guest flags into the first flags register: */ ! 924: flags_reg_index_accum = 0; ! 925: flags_reg_index_next = flags_reg_index_accum; ! 926: memset(flags_reg_index_to_flag, 0, sizeof(flags_reg_index_to_flag)); ! 927: flags_reg_drain = _TME_RECODE_X86_FLAGS_REG_COUNT; ! 928: ! 929: /* loop over the flags: */ ! 930: flag = flags_group->tme_recode_flags_group_flags; ! 931: for (;;) { ! 932: ! 933: /* if we're not at the end of the list of flags, but ! 934: this is not a variable flag we're providing, skip ! 935: it: */ ! 936: if (flag->tme_recode_flag_flag != 0 ! 937: && (flag->tme_recode_flag_flag & flags_variable_host) == 0) { ! 938: flag++; ! 939: continue; ! 940: } ! 941: ! 942: /* unless we haven't accumulated any flags yet: */ ! 943: if (flags_reg_index_to_flag[flags_reg_index_accum] != 0) { ! 944: ! 945: /* advance to the next register: */ ! 946: flags_reg_index_next++; ! 947: if (flags_reg_index_next >= _TME_RECODE_X86_FLAGS_REG_COUNT) { ! 948: flags_reg_index_next = 0; ! 949: } ! 950: ! 951: /* always skip flags register zero, and the flags register we're ! 952: accumulating into (which can be nonzero when we need to ! 953: accumulate in two registers): */ ! 954: if (flags_reg_index_next == 0 ! 955: || flags_reg_index_next == flags_reg_index_accum) { ! 956: continue; ! 957: } ! 958: } ! 959: ! 960: /* if this next register is busy with a previous flag: */ ! 961: if (flags_reg_index_to_flag[flags_reg_index_next]) { ! 962: ! 963: /* if flags register zero has accumulated one or more flags from ! 964: the most significant half of a double-host-size guest flags ! 965: register, and the flag that is busy in the next register is ! 966: from the least significant half: */ ! 967: if ((flags_reg_index_to_flag[flags_reg_index_accum] & flags_mask_1) ! 968: && (flags_reg_index_to_flag[flags_reg_index_next] & flags_mask_0)) { ! 969: ! 970: /* this next register, that we originally wanted to store ! 971: another single flag into, is now actually the second flags ! 972: register that we will accumulate flags into, and already ! 973: has its first flag in it. we loop to select another next ! 974: register for the next flag: */ ! 975: flags_reg_index_accum = flags_reg_index_next; ! 976: continue; ! 977: } ! 978: ! 979: /* otherwise, we need to shift the accumulating register up ! 980: and OR in the previous flag in this next register. if the ! 981: factor between the last flag in the accumulating register ! 982: and this previous flag is less than or equal to eight, we ! 983: can do the shift and or in one lea instruction, otherwise ! 984: we need to shift the accumulating register by itself with ! 985: leas first: */ ! 986: for (factor = flags_reg_index_to_flag[flags_reg_index_accum] / flags_reg_index_to_flag[flags_reg_index_next]; ! 987: factor > 1; ! 988: factor /= 8) { ! 989: ! 990: /* get the factor to multiply the index register (the ! 991: accumulating register) by, and the base to add to that, ! 992: which is none if this won't be our last shift: */ ! 993: if (factor > 8) { ! 994: subfactor = 8; ! 995: x86_reg_0 = TME_RECODE_X86_SIB_BASE_NONE; ! 996: } ! 997: else { ! 998: subfactor = factor; ! 999: x86_reg_0 = _TME_RECODE_X86_FLAGS_REG(flags_group, flags_reg_index_next); ! 1000: } ! 1001: ! 1002: /* emit the lea instruction to do the shift and add: */ ! 1003: x86_reg_1 = _TME_RECODE_X86_FLAGS_REG(flags_group, flags_reg_index_accum); ! 1004: rex ! 1005: = (TME_RECODE_X86_REX_W(size_accum) ! 1006: | TME_RECODE_X86_REX_R(size_accum, x86_reg_1) ! 1007: | TME_RECODE_X86_REX_X(x86_reg_1) ! 1008: | TME_RECODE_X86_REX_B(size_accum, x86_reg_0)); ! 1009: if (rex != 0) { ! 1010: *(thunk_bytes++) = rex; ! 1011: } ! 1012: thunk_bytes[0] = TME_RECODE_X86_OPCODE_LEA; ! 1013: thunk_bytes[1] = TME_RECODE_X86_MOD_OPREG_RM(TME_RECODE_X86_EA_BASE_SIB, TME_RECODE_X86_REG(x86_reg_1)); ! 1014: thunk_bytes[2] = TME_RECODE_X86_SIB(x86_reg_0, x86_reg_1, subfactor); ! 1015: thunk_bytes += 3; ! 1016: } ! 1017: ! 1018: /* update the last flag accumulated: */ ! 1019: flags_reg_index_to_flag[flags_reg_index_accum] = flags_reg_index_to_flag[flags_reg_index_next]; ! 1020: ! 1021: /* this next register is no longer busy: */ ! 1022: flags_reg_index_to_flag[flags_reg_index_next] = 0; ! 1023: } ! 1024: ! 1025: /* if there are no more flags: */ ! 1026: if (flag->tme_recode_flag_flag == 0) { ! 1027: ! 1028: /* keep looping until we've accumulated all of the flags still ! 1029: in flags registers: */ ! 1030: if (flags_reg_drain--) { ! 1031: continue; ! 1032: } ! 1033: ! 1034: /* otherwise, stop now: */ ! 1035: break; ! 1036: } ! 1037: ! 1038: /* get the condition and remove any modifiers: */ ! 1039: cond = flag->tme_recode_flag_cond; ! 1040: cond_mods = (cond & TME_RECODE_COND_MOD_NOT); ! 1041: cond -= cond_mods; ! 1042: ! 1043: /* if the x86 flags for this size aren't loaded: */ ! 1044: assert (flag->tme_recode_flag_size >= TME_RECODE_SIZE_8); ! 1045: if (host_size_loaded == TME_RECODE_SIZE_1 ! 1046: || (cond != TME_RECODE_COND_PE ! 1047: && host_size_loaded != flag->tme_recode_flag_size)) { ! 1048: ! 1049: /* push the flags for this size: */ ! 1050: thunk_bytes ! 1051: = _tme_recode_x86_flags_host_flags(flags_group, ! 1052: thunk_bytes, ! 1053: (host_sizes >> flag->tme_recode_flag_size), ! 1054: 0, ! 1055: (TME_RECODE_X86_OPCODE_GRP5 ! 1056: + (TME_RECODE_X86_MOD_OPREG_RM(0, ! 1057: TME_RECODE_X86_OPCODE_GRP5_PUSH) ! 1058: << 8))); ! 1059: ! 1060: /* popf the flags for this size: */ ! 1061: *(thunk_bytes++) = TME_RECODE_X86_OPCODE_POPF; ! 1062: ! 1063: /* the x86 flags for this size are now loaded: */ ! 1064: host_size_loaded = flag->tme_recode_flag_size; ! 1065: } ! 1066: ! 1067: /* emit the setcc instruction for this condition: */ ! 1068: switch (cond) { ! 1069: default: assert(FALSE); ! 1070: case TME_RECODE_COND_N: cond = TME_RECODE_X86_COND_S; break; ! 1071: case TME_RECODE_COND_C: cond = TME_RECODE_X86_COND_C; break; ! 1072: case TME_RECODE_COND_V: cond = TME_RECODE_X86_COND_O; break; ! 1073: case TME_RECODE_COND_Z: cond = TME_RECODE_X86_COND_Z; break; ! 1074: case TME_RECODE_COND_PE: cond = TME_RECODE_X86_COND_PE; break; ! 1075: case TME_RECODE_COND_BE: cond = TME_RECODE_X86_COND_BE; break; ! 1076: case TME_RECODE_COND_LE: cond = TME_RECODE_X86_COND_LE; break; ! 1077: case TME_RECODE_COND_L: cond = TME_RECODE_X86_COND_L; break; ! 1078: } ! 1079: if (cond_mods & TME_RECODE_COND_MOD_NOT) { ! 1080: cond += TME_RECODE_X86_COND_NOT; ! 1081: } ! 1082: x86_reg_0 = _TME_RECODE_X86_FLAGS_REG(flags_group, flags_reg_index_next); ! 1083: rex = TME_RECODE_X86_REX_B(TME_RECODE_SIZE_8, x86_reg_0); ! 1084: if (rex != 0) { ! 1085: *(thunk_bytes++) = rex; ! 1086: } ! 1087: thunk_bytes[0] = TME_RECODE_X86_OPCODE_ESC_0F; ! 1088: thunk_bytes[1] = TME_RECODE_X86_OPCODE0F_SETCC(cond); ! 1089: thunk_bytes[2] = TME_RECODE_X86_MOD_OPREG_RM(TME_RECODE_X86_MOD_RM_REG(x86_reg_0), 0); ! 1090: thunk_bytes += 3; ! 1091: ! 1092: /* this register now contains this flag: */ ! 1093: flags_reg_index_to_flag[flags_reg_index_next] = flag->tme_recode_flag_flag; ! 1094: ! 1095: /* continue with the next flag: */ ! 1096: flag++; ! 1097: } ! 1098: ! 1099: /* we always try to update at least one part of the guest flags ! 1100: register. if we are providing variable flags, and they're all in ! 1101: the most-significant host-sized half of a double-host-size guest ! 1102: flags register, update that half of the guest flags register now, ! 1103: otherwise update the least-significant half (which may mean the ! 1104: whole guest flags register): */ ! 1105: thunk_bytes ! 1106: = _tme_recode_x86_flags_update(flags_group, ! 1107: thunk_bytes, ! 1108: ((flags_reg_index_to_flag[flags_reg_index_accum] ! 1109: & flags_mask_1) ! 1110: ? flags_mask_1 ! 1111: : flags_mask_0), ! 1112: flags_reg_index_accum, ! 1113: flags_reg_index_to_flag[flags_reg_index_accum], ! 1114: flags_reg_index_next); ! 1115: ! 1116: /* if we are providing variable flags and this is a double-host-size ! 1117: guest flags register and there are variable flags in both halves, ! 1118: we've only updated the least significant half above. or, if all ! 1119: of the variable flags are in the most-significant half, we've ! 1120: only updated the most-significant half above. we need to update ! 1121: the other half of the guest flags register: */ ! 1122: if (flags_reg_index_accum != 0 ! 1123: || (flags_reg_index_to_flag[flags_reg_index_accum] ! 1124: & flags_mask_1)) { ! 1125: ! 1126: /* we've already updated the half of the guest flags register that ! 1127: was being accumulated in flags_reg_index_accum, so we can now free ! 1128: it up: */ ! 1129: flags_reg_index_to_flag[flags_reg_index_accum] = 0; ! 1130: ! 1131: /* if we haven't already updated the half of the guest flags ! 1132: variable that was being accumulated in flags register zero, we'll ! 1133: do it now: */ ! 1134: flags_reg_index_accum = 0; ! 1135: ! 1136: /* update the other half of the guest flags register: */ ! 1137: thunk_bytes ! 1138: = _tme_recode_x86_flags_update(flags_group, ! 1139: thunk_bytes, ! 1140: ((flags_reg_index_to_flag[flags_reg_index_accum] ! 1141: & flags_mask_1) ! 1142: ? flags_mask_1 ! 1143: : flags_mask_0), ! 1144: flags_reg_index_accum, ! 1145: flags_reg_index_to_flag[flags_reg_index_accum], ! 1146: flags_reg_index_next); ! 1147: } ! 1148: ! 1149: /* if there is a guest function: */ ! 1150: if (flags_thunk->tme_recode_x86_flags_thunk_has_guest_func) { ! 1151: ! 1152: /* if this is an x86-64 host, and double-host-size guests are not ! 1153: supported, and we have things to remove from the stack: */ ! 1154: if (TME_RECODE_SIZE_HOST > TME_RECODE_SIZE_32 ! 1155: && TME_RECODE_SIZE_GUEST_MAX <= TME_RECODE_SIZE_HOST ! 1156: && stack_adjust != 0) { ! 1157: ! 1158: /* we can chain to the guest function, so adjust the stack ! 1159: pointer now: */ ! 1160: thunk_bytes = _tme_recode_x86_emit_adjust_sp(thunk_bytes, stack_adjust); ! 1161: stack_adjust = 0; ! 1162: } ! 1163: ! 1164: /* jmp or call the guest function: */ ! 1165: tme_recode_x86_insns_finish(ic, thunk_bytes); ! 1166: _tme_recode_x86_emit_transfer_func(ic, ! 1167: (stack_adjust == 0 ! 1168: ? TME_RECODE_X86_OPCODE_JMP_RELz ! 1169: : TME_RECODE_X86_OPCODE_CALL_RELz), ! 1170: ((void (*) _TME_P((void))) ! 1171: flags_group->tme_recode_flags_group_guest_func)); ! 1172: tme_recode_x86_insns_start(ic, thunk_bytes); ! 1173: } ! 1174: ! 1175: /* if we still have to adjust the stack, or if there is no guest ! 1176: function: */ ! 1177: if (stack_adjust != 0 ! 1178: || !flags_thunk->tme_recode_x86_flags_thunk_has_guest_func) { ! 1179: ! 1180: /* do any stack adjust: */ ! 1181: if (stack_adjust != 0) { ! 1182: thunk_bytes = _tme_recode_x86_emit_adjust_sp(thunk_bytes, stack_adjust); ! 1183: } ! 1184: ! 1185: /* return to the instruction thunk: */ ! 1186: *(thunk_bytes++) = TME_RECODE_X86_OPCODE_RET; ! 1187: } ! 1188: ! 1189: /* finish these instructions: */ ! 1190: tme_recode_x86_insns_finish(ic, thunk_bytes); ! 1191: ! 1192: /* finish this main flags subs thunk: */ ! 1193: tme_recode_host_thunk_finish(ic); ! 1194: ! 1195: /* add this flags group to this new flags thunk: */ ! 1196: return (tme_recode_host_flags_thunk_add(ic, flags_thunk, flags_group)); ! 1197: } ! 1198: ! 1199: /* this emits a jmp to chain to a subs, and finishes a thunk: */ ! 1200: static void ! 1201: _tme_recode_x86_flags_thunk_chain(struct tme_recode_ic *ic, ! 1202: tme_recode_thunk_off_t subs_next) ! 1203: { ! 1204: tme_uint8_t *thunk_bytes; ! 1205: ! 1206: /* start another instruction: */ ! 1207: tme_recode_x86_insns_start(ic, thunk_bytes); ! 1208: ! 1209: /* emit a jmp to the subs: */ ! 1210: thunk_bytes[0] = TME_RECODE_X86_OPCODE_JMP_RELz; ! 1211: thunk_bytes += 1 + sizeof(tme_int32_t); ! 1212: ((tme_int32_t *) thunk_bytes)[-1] ! 1213: = (subs_next ! 1214: - tme_recode_build_to_thunk_off(ic, thunk_bytes)); ! 1215: ! 1216: /* finish this instruction: */ ! 1217: tme_recode_x86_insns_finish(ic, thunk_bytes); ! 1218: ! 1219: /* finish this code thunk: */ ! 1220: tme_recode_host_thunk_finish(ic); ! 1221: } ! 1222: ! 1223: /* this emits instructions to do a binop at multiple sizes: */ ! 1224: static void ! 1225: _tme_recode_x86_flags_thunk_ops(struct tme_recode_flags_thunk *flags_thunk, ! 1226: unsigned int binop, ! 1227: unsigned int reg_host_src, ! 1228: tme_uint32_t host_sizes, ! 1229: struct tme_recode_ic *ic) ! 1230: { ! 1231: tme_uint8_t *thunk_bytes; ! 1232: int stack_adjust; ! 1233: unsigned int size; ! 1234: unsigned int reg_x86_src; ! 1235: unsigned int reg_x86_dst; ! 1236: unsigned int reg_x86_op_src; ! 1237: unsigned int reg_x86_op_dst; ! 1238: tme_uint8_t rex; ! 1239: ! 1240: /* start more instructions: */ ! 1241: tme_recode_x86_insns_start(ic, thunk_bytes); ! 1242: ! 1243: /* do any stack padding needed for the host ABI: */ ! 1244: stack_adjust = flags_thunk->tme_recode_x86_flags_thunk_stack_padding; ! 1245: if (stack_adjust != 0) { ! 1246: thunk_bytes = _tme_recode_x86_emit_adjust_sp(thunk_bytes, stack_adjust); ! 1247: } ! 1248: ! 1249: /* we need to track the stack adjust here for binops that test the ! 1250: carry. include the subs return address in the stack adjust: */ ! 1251: stack_adjust += TME_BIT(TME_RECODE_SIZE_HOST - TME_RECODE_SIZE_8); ! 1252: ! 1253: /* loop over all of the sizes: */ ! 1254: for (; host_sizes != 0; ) { ! 1255: ! 1256: /* get this size, and remove it from the mask: */ ! 1257: size = _tme_recode_x86_ffs(host_sizes); ! 1258: host_sizes &= (host_sizes - 1); ! 1259: ! 1260: /* if this is a double-host size operation: */ ! 1261: if (TME_RECODE_SIZE_IS_DOUBLE_HOST(size)) { ! 1262: ! 1263: /* force a host-size operation on the most-significant halves of ! 1264: the host registers: */ ! 1265: size = TME_RECODE_SIZE_HOST; ! 1266: reg_x86_src = tme_recode_x86_reg_from_host[reg_host_src + 1]; ! 1267: reg_x86_dst = tme_recode_x86_reg_from_host[TME_RECODE_X86_REG_HOST_SUBS_DST + 1]; ! 1268: ! 1269: /* the most-significant half of a double-host-size additive ! 1270: operation uses the carry from the least-significant half: */ ! 1271: if (binop == TME_RECODE_X86_OPCODE_BINOP_ADD) { ! 1272: binop = TME_RECODE_X86_OPCODE_BINOP_ADC; ! 1273: } ! 1274: else if (binop == TME_RECODE_X86_OPCODE_BINOP_SUB) { ! 1275: binop = TME_RECODE_X86_OPCODE_BINOP_SBB; ! 1276: } ! 1277: } ! 1278: ! 1279: /* otherwise, this is not a double-host-size operation: */ ! 1280: else { ! 1281: ! 1282: /* operate on the (least-significant halves of) the host ! 1283: registers: */ ! 1284: reg_x86_src = tme_recode_x86_reg_from_host[reg_host_src]; ! 1285: reg_x86_dst = tme_recode_x86_reg_from_host[TME_RECODE_X86_REG_HOST_SUBS_DST]; ! 1286: ! 1287: /* if this is an additive instruction that needs the carry: */ ! 1288: if (binop == TME_RECODE_X86_OPCODE_BINOP_ADC ! 1289: || binop == TME_RECODE_X86_OPCODE_BINOP_SBB) { ! 1290: ! 1291: /* emit the instruction to define CF with the recode carry flag: */ ! 1292: tme_recode_x86_insns_finish(ic, thunk_bytes); ! 1293: _tme_recode_x86_conds_testc(ic, stack_adjust); ! 1294: tme_recode_x86_insns_start(ic, thunk_bytes); ! 1295: } ! 1296: } ! 1297: ! 1298: /* if this is a test instruction, the source register is the same ! 1299: as the destination: */ ! 1300: if (binop == TME_RECODE_X86_OPCODE_BINOP_TEST) { ! 1301: reg_x86_src = reg_x86_dst; ! 1302: } ! 1303: ! 1304: /* assume that we will be able to do the operation on the x86 ! 1305: registers as loaded: */ ! 1306: reg_x86_op_src = reg_x86_src; ! 1307: reg_x86_op_dst = reg_x86_dst; ! 1308: ! 1309: /* if this is an ia32 host, and this is an eight-bit operation: */ ! 1310: if (TME_RECODE_SIZE_HOST == TME_RECODE_SIZE_32 ! 1311: && size == TME_RECODE_SIZE_8) { ! 1312: ! 1313: /* eight-bit operations aren't possible with the normal source ! 1314: and destination x86 registers, since they don't have ! 1315: eight-bit encodings: */ ! 1316: ! 1317: /* if this is a test instruction: */ ! 1318: if (binop == TME_RECODE_X86_OPCODE_BINOP_TEST) { ! 1319: ! 1320: /* emit a testl %reg, $0xff: */ ! 1321: thunk_bytes[0] = TME_RECODE_X86_OPCODE_GRP3_Ev; ! 1322: thunk_bytes[1] = TME_RECODE_X86_MOD_OPREG_RM(TME_RECODE_X86_MOD_RM_REG(reg_x86_op_dst), ! 1323: TME_RECODE_X86_OPCODE_GRP3_TEST); ! 1324: *((tme_uint32_t *) &thunk_bytes[2]) = 0xff; ! 1325: thunk_bytes += 2 + sizeof(tme_uint32_t); ! 1326: ! 1327: /* suppress the binop instruction below: */ ! 1328: size = TME_RECODE_SIZE_1; ! 1329: } ! 1330: ! 1331: /* otherwise, this is not a test instruction: */ ! 1332: else { ! 1333: ! 1334: /* we need to get the (least-significant halves of the) ! 1335: destination and second source operands into the two halves ! 1336: of the first source operand host register: */ ! 1337: reg_x86_op_src = tme_recode_x86_reg_from_host[TME_RECODE_X86_REG_HOST_SUBS_SRC0 + 1]; ! 1338: reg_x86_op_dst = tme_recode_x86_reg_from_host[TME_RECODE_X86_REG_HOST_SUBS_SRC0 + 0]; ! 1339: ! 1340: /* mov the (least-significant half of the) second source ! 1341: operand into place: */ ! 1342: _tme_recode_x86_emit_reg_copy(thunk_bytes, ! 1343: reg_x86_src, ! 1344: reg_x86_op_src); ! 1345: ! 1346: /* the (least significant half of the) destination operand has ! 1347: already been copied to the (least significant half of the) ! 1348: first source operand host register, unless there is no ! 1349: guest function and this is the only operation size: */ ! 1350: if (!flags_thunk->tme_recode_x86_flags_thunk_has_guest_func ! 1351: && host_sizes == 0) { ! 1352: ! 1353: /* otherwise, mov the (least-significant half of the) ! 1354: destination operand into place: */ ! 1355: _tme_recode_x86_emit_reg_copy(thunk_bytes, ! 1356: reg_x86_dst, ! 1357: reg_x86_op_dst); ! 1358: } ! 1359: } ! 1360: } ! 1361: ! 1362: /* unless the binop has been suppressed: */ ! 1363: if (size != TME_RECODE_SIZE_1) { ! 1364: ! 1365: /* if this is a 16-bit operation: */ ! 1366: if (size == TME_RECODE_SIZE_16) { ! 1367: ! 1368: /* emit the OPSIZ prefix: */ ! 1369: thunk_bytes[0] = TME_RECODE_X86_PREFIX_OPSIZ; ! 1370: thunk_bytes++; ! 1371: } ! 1372: ! 1373: /* emit any rex prefix: */ ! 1374: /* NB: we must use the Gb_Eb/Gv_Ev forms here since those are the ! 1375: only ones that the test instruction supports: */ ! 1376: rex ! 1377: = (TME_RECODE_X86_REX_R(size, reg_x86_op_src) ! 1378: | TME_RECODE_X86_REX_B(size, reg_x86_op_dst)); ! 1379: if (rex != 0) { ! 1380: thunk_bytes[0] = rex; ! 1381: thunk_bytes++; ! 1382: } ! 1383: ! 1384: /* emit the opcode: */ ! 1385: thunk_bytes[0] ! 1386: = (binop ! 1387: + (size == TME_RECODE_SIZE_8 ! 1388: ? TME_RECODE_X86_OPCODE_BINOP_Gb_Eb ! 1389: : TME_RECODE_X86_OPCODE_BINOP_Gv_Ev)); ! 1390: thunk_bytes++; ! 1391: ! 1392: /* emit the mod R/M byte: */ ! 1393: thunk_bytes[0] = TME_RECODE_X86_MOD_OPREG_RM(TME_RECODE_X86_MOD_RM_REG(reg_x86_op_dst), ! 1394: TME_RECODE_X86_REG(reg_x86_op_src)); ! 1395: thunk_bytes++; ! 1396: } ! 1397: ! 1398: /* if we have more sizes to do: */ ! 1399: if (host_sizes != 0) { ! 1400: ! 1401: /* push these flags: */ ! 1402: thunk_bytes[0] = TME_RECODE_X86_OPCODE_PUSHF; ! 1403: thunk_bytes++; ! 1404: stack_adjust += TME_BIT(TME_RECODE_SIZE_HOST - TME_RECODE_SIZE_8); ! 1405: } ! 1406: ! 1407: /* if this is an ia32 host, and this was an eight-bit operation: */ ! 1408: if (TME_RECODE_SIZE_HOST == TME_RECODE_SIZE_32 ! 1409: && size == TME_RECODE_SIZE_8) { ! 1410: ! 1411: /* if double-host-size guests are supported, and the last size ! 1412: is double-host-size, and there is a guest function: */ ! 1413: if (TME_RECODE_SIZE_GUEST_MAX > TME_RECODE_SIZE_HOST ! 1414: && (host_sizes & TME_BIT(TME_RECODE_SIZE_GUEST_MAX)) ! 1415: && flags_thunk->tme_recode_x86_flags_thunk_has_guest_func) { ! 1416: ! 1417: /* copy the most-significant half of the destination operand ! 1418: back into the most-significant half of the first source ! 1419: operand, to be passed to the guest function: */ ! 1420: _tme_recode_x86_emit_reg_copy(thunk_bytes, ! 1421: tme_recode_x86_reg_from_host[TME_RECODE_X86_REG_HOST_SUBS_DST + 1], ! 1422: reg_x86_op_src); ! 1423: } ! 1424: ! 1425: /* if we have a guest function, or if we have at least two more ! 1426: sizes to do: */ ! 1427: if (flags_thunk->tme_recode_x86_flags_thunk_has_guest_func ! 1428: || (host_sizes & (host_sizes - 1))) { ! 1429: ! 1430: /* copy the (least-significant half of) the destination ! 1431: operand back into the (least-significant half of) the first ! 1432: source operand, so it can be passed to the guest function ! 1433: and/or used to reload the destination operand later. if we ! 1434: have no more sizes to do, this must be an xchg to put the ! 1435: destination value in the destination operand: */ ! 1436: thunk_bytes[0] ! 1437: = ((host_sizes == 0 ! 1438: ? TME_RECODE_X86_OPCODE_BINOP_XCHG ! 1439: : TME_RECODE_X86_OPCODE_BINOP_MOV) ! 1440: + TME_RECODE_X86_OPCODE_BINOP_Gv_Ev); ! 1441: thunk_bytes[1] ! 1442: = TME_RECODE_X86_MOD_OPREG_RM(TME_RECODE_X86_MOD_RM_REG(reg_x86_op_dst), ! 1443: reg_x86_dst); ! 1444: thunk_bytes += 2; ! 1445: } ! 1446: ! 1447: /* otherwise, if we have no more sizes to do: */ ! 1448: else if (host_sizes == 0) { ! 1449: ! 1450: /* copy the destination operand back into place: */ ! 1451: _tme_recode_x86_emit_reg_copy(thunk_bytes, ! 1452: reg_x86_op_dst, ! 1453: reg_x86_dst); ! 1454: } ! 1455: } ! 1456: ! 1457: /* otherwise, this is not an ia32 host or this was not ! 1458: an eight-bit operation: */ ! 1459: else { ! 1460: ! 1461: /* if this was not a test instruction, and we have at least one ! 1462: more size to do, and it isn't the double-host-size size: */ ! 1463: if (binop != TME_RECODE_X86_OPCODE_BINOP_TEST ! 1464: && host_sizes != 0 ! 1465: && (TME_RECODE_SIZE_GUEST_MAX == TME_RECODE_SIZE_HOST ! 1466: || host_sizes != TME_BIT(TME_RECODE_SIZE_GUEST_MAX))) { ! 1467: ! 1468: /* reload the (least-significant half of the) destination ! 1469: operand: */ ! 1470: _tme_recode_x86_emit_reg_copy(thunk_bytes, ! 1471: tme_recode_x86_reg_from_host[TME_RECODE_X86_REG_HOST_SUBS_SRC0 + 0], ! 1472: reg_x86_dst); ! 1473: } ! 1474: } ! 1475: } ! 1476: ! 1477: /* finish these instructions: */ ! 1478: tme_recode_x86_insns_finish(ic, thunk_bytes); ! 1479: } ! 1480: ! 1481: /* this adds another flags group to an existing flags thunk: */ ! 1482: struct tme_recode_flags_thunk * ! 1483: tme_recode_host_flags_thunk_add(struct tme_recode_ic *ic, ! 1484: struct tme_recode_flags_thunk *flags_thunk, ! 1485: const struct tme_recode_flags_group *flags_group) ! 1486: { ! 1487: tme_recode_uguest_t flags_variable_host; ! 1488: tme_uint32_t host_sizes; ! 1489: int multiple_ops; ! 1490: tme_recode_thunk_off_t *_subs; ! 1491: tme_recode_thunk_off_t subs_test; ! 1492: unsigned int size_other; ! 1493: unsigned int opcode; ! 1494: struct tme_recode_insn insn; ! 1495: struct tme_recode_insn insn_not; ! 1496: unsigned int class; ! 1497: tme_uint8_t *thunk_bytes; ! 1498: ! 1499: /* get the mask of variable guest flags that we are providing. this ! 1500: may be zero: */ ! 1501: flags_variable_host ! 1502: = (tme_recode_flags_group_flags_defined_host(flags_group, TME_RECODE_COND_UNDEF) ! 1503: & ~(tme_recode_flags_group_flags_defined_host(flags_group, TME_RECODE_COND_FALSE) ! 1504: | tme_recode_flags_group_flags_defined_host(flags_group, TME_RECODE_COND_MOD_NOT | TME_RECODE_COND_FALSE))); ! 1505: ! 1506: /* get the mask of sizes for which we need to provide at least one ! 1507: variable flag in the given flags group. this may ! 1508: be zero: */ ! 1509: host_sizes = tme_recode_flags_group_sizes(flags_group, flags_variable_host); ! 1510: ! 1511: /* it's easiest to assume that we always need the instruction-size ! 1512: flags when we need any flags at all, since the instruction-size ! 1513: operation always happens last no matter what, and leaves its flags ! 1514: in the flags register: */ ! 1515: if (host_sizes != 0) { ! 1516: host_sizes |= TME_BIT(flags_group->tme_recode_flags_group_insn_size); ! 1517: } ! 1518: ! 1519: /* when we need the double-host-size flags, we also need ! 1520: the host-size flags, because we need the host-size Z ! 1521: flag to complete the double-host-size Z flag: */ ! 1522: if (host_sizes & TME_BIT(TME_RECODE_SIZE_HOST + 1)) { ! 1523: host_sizes |= TME_BIT(TME_RECODE_SIZE_HOST); ! 1524: } ! 1525: ! 1526: /* we have to do multiple operations if we need flags for multiple ! 1527: sizes, not counting the double-host-size: */ ! 1528: multiple_ops ! 1529: = (((host_sizes ! 1530: & (TME_BIT(TME_RECODE_SIZE_HOST + 1) - 1)) ! 1531: & ((host_sizes ! 1532: & (TME_BIT(TME_RECODE_SIZE_HOST + 1) - 1)) ! 1533: - 1)) ! 1534: != 0); ! 1535: ! 1536: /* dispatch on the flags group instruction class: */ ! 1537: switch (flags_group->tme_recode_flags_group_insn_class) { ! 1538: default: abort(); ! 1539: ! 1540: /* these instruction classes never chain to the test subs: */ ! 1541: case TME_RECODE_INSN_CLASS_ADDITIVE: ! 1542: subs_test = 0; ! 1543: break; ! 1544: ! 1545: /* these instruction classes always chain to the test subs: */ ! 1546: case TME_RECODE_INSN_CLASS_SHIFT: ! 1547: case TME_RECODE_INSN_CLASS_EXT: ! 1548: subs_test = 1; ! 1549: break; ! 1550: ! 1551: /* these instruction classes chain to the test subs only if flags ! 1552: for multiple sizes are needed: */ ! 1553: case TME_RECODE_INSN_CLASS_LOGICAL: ! 1554: subs_test = multiple_ops; ! 1555: break; ! 1556: } ! 1557: ! 1558: /* if this flags group chains to the test subs: */ ! 1559: if (subs_test != 0) { ! 1560: ! 1561: /* if we haven't already created the test subs: */ ! 1562: _subs ! 1563: = &(flags_thunk->tme_recode_x86_flags_thunk_sizes ! 1564: [flags_group->tme_recode_flags_group_insn_size - TME_RECODE_SIZE_8] ! 1565: .tme_recode_x86_flags_thunk_size_subs_test); ! 1566: subs_test = *_subs; ! 1567: if (subs_test == 0) { ! 1568: ! 1569: /* make the test subs, which chains to the main flags subs: */ ! 1570: subs_test = tme_recode_build_to_thunk_off(ic, ic->tme_recode_ic_thunk_build_next); ! 1571: assert (subs_test != 0); ! 1572: *_subs = subs_test; ! 1573: _tme_recode_x86_flags_thunk_ops(flags_thunk, ! 1574: TME_RECODE_X86_OPCODE_BINOP_TEST, ! 1575: TME_RECODE_X86_REG_HOST_SUBS_SRC1, ! 1576: host_sizes, ! 1577: ic); ! 1578: _tme_recode_x86_flags_thunk_chain(ic, ! 1579: flags_thunk->tme_recode_x86_flags_thunk_subs_main); ! 1580: } ! 1581: } ! 1582: ! 1583: /* loop over all sizes starting from eight bits: */ ! 1584: for (size_other = TME_RECODE_SIZE_8; ! 1585: size_other <= TME_RECODE_SIZE_GUEST_MAX; ! 1586: size_other++) { ! 1587: ! 1588: /* loop over all integer opcodes: */ ! 1589: for (opcode = 0; ! 1590: opcode < TME_RECODE_OPCODES_INTEGER; ! 1591: opcode++) { ! 1592: ! 1593: /* initialize the machine-independent parts of the recode IC ! 1594: such that guest registers zero and one are loaded in the x86 ! 1595: subs destination operand and second source operand host ! 1596: registers, respectively: */ ! 1597: ic->tme_recode_ic_reg_host_reserve_next = 0; ! 1598: ic->tme_recode_ic_reg_host_to_ruses ! 1599: [TME_RECODE_X86_REG_HOST_SUBS_DST] ! 1600: = TME_RECODE_REGINFO_RUSES(1); ! 1601: ic->tme_recode_ic_reg_host_to_ruses ! 1602: [TME_RECODE_X86_REG_HOST_SUBS_SRC1] ! 1603: = TME_RECODE_REGINFO_RUSES(1); ! 1604: ic->tme_recode_ic_reg_host_to_reg_guest ! 1605: [TME_RECODE_X86_REG_HOST_SUBS_DST] = TME_RECODE_REG_GUEST(0); ! 1606: ic->tme_recode_ic_reg_host_to_reg_guest ! 1607: [TME_RECODE_X86_REG_HOST_SUBS_SRC1] = TME_RECODE_REG_GUEST(1); ! 1608: (ic->tme_recode_ic_reginfo ! 1609: + TME_RECODE_REG_GUEST(0))->tme_recode_reginfo_tags_ruses ! 1610: = (TME_RECODE_REGINFO_TAGS_VALID ! 1611: | TME_RECODE_REGINFO_TAGS_VALID_SIZE(TME_RECODE_SIZE_GUEST_MAX) ! 1612: | TME_RECODE_REGINFO_TAGS_CLEAN ! 1613: | TME_RECODE_REGINFO_TAGS_REG_HOST(TME_RECODE_X86_REG_HOST_SUBS_DST)); ! 1614: (ic->tme_recode_ic_reginfo ! 1615: + TME_RECODE_REG_GUEST(1))->tme_recode_reginfo_tags_ruses ! 1616: = (TME_RECODE_REGINFO_TAGS_VALID ! 1617: | TME_RECODE_REGINFO_TAGS_VALID_SIZE(TME_RECODE_SIZE_GUEST_MAX) ! 1618: | TME_RECODE_REGINFO_TAGS_CLEAN ! 1619: | TME_RECODE_REGINFO_TAGS_REG_HOST(TME_RECODE_X86_REG_HOST_SUBS_SRC1)); ! 1620: ! 1621: /* if this isn't a double-host-size guest, initialize the ! 1622: machine-independent parts of the recode ic such that guest ! 1623: register two is loaded in the a register, which can be ! 1624: destroyed by a subs: */ ! 1625: if (!TME_RECODE_SIZE_IS_DOUBLE_HOST(ic->tme_recode_ic_reg_size)) { ! 1626: #if (TME_RECODE_X86_REG_HOST_SUBS_SRC1 + 1) != TME_RECODE_X86_REG_HOST_FREE_CALL ! 1627: #error "TME_RECODE_X86_REG_HOST_ values changed" ! 1628: #endif ! 1629: assert (tme_recode_x86_reg_from_host[TME_RECODE_X86_REG_HOST_SUBS_SRC1 + 1] == TME_RECODE_X86_REG_A); ! 1630: ic->tme_recode_ic_reg_host_to_ruses ! 1631: [TME_RECODE_X86_REG_HOST_SUBS_SRC1 + 1] ! 1632: = TME_RECODE_REGINFO_RUSES(1); ! 1633: ic->tme_recode_ic_reg_host_to_reg_guest ! 1634: [TME_RECODE_X86_REG_HOST_SUBS_SRC1 + 1] = TME_RECODE_REG_GUEST(2); ! 1635: (ic->tme_recode_ic_reginfo ! 1636: + TME_RECODE_REG_GUEST(2))->tme_recode_reginfo_tags_ruses ! 1637: = (TME_RECODE_REGINFO_TAGS_VALID ! 1638: | TME_RECODE_REGINFO_TAGS_VALID_SIZE(TME_RECODE_SIZE_GUEST_MAX) ! 1639: | TME_RECODE_REGINFO_TAGS_CLEAN ! 1640: | TME_RECODE_REGINFO_TAGS_REG_HOST(TME_RECODE_X86_REG_HOST_SUBS_SRC1 + 1)); ! 1641: } ! 1642: ! 1643: /* initialize the instruction, assuming that we'll emit for only ! 1644: one size (double-host-size if this is a double-host-size flags ! 1645: group, otherwise host-size), and assuming that this ! 1646: instruction will operate with guest register zero on the left ! 1647: and guest register one on the right, with the result going ! 1648: back into guest register zero: */ ! 1649: insn.tme_recode_insn_opcode = opcode; ! 1650: insn.tme_recode_insn_size ! 1651: = (TME_RECODE_SIZE_HOST ! 1652: + (TME_RECODE_SIZE_IS_DOUBLE_HOST(flags_group->tme_recode_flags_group_insn_size) != 0)); ! 1653: insn.tme_recode_insn_operand_src[0] = TME_RECODE_REG_GUEST(0); ! 1654: insn.tme_recode_insn_operand_src[1] = TME_RECODE_REG_GUEST(1); ! 1655: insn.tme_recode_insn_operand_dst = TME_RECODE_REG_GUEST(0); ! 1656: insn.tme_recode_insn_flags_thunk = NULL; ! 1657: ! 1658: /* assume that this opcode, for this flags group ! 1659: instruction size, only has one subs: */ ! 1660: _subs ! 1661: = &(flags_thunk->tme_recode_x86_flags_thunk_sizes ! 1662: [flags_group->tme_recode_flags_group_insn_size - TME_RECODE_SIZE_8] ! 1663: .tme_recode_x86_flags_thunk_size_subs[opcode]); ! 1664: ! 1665: /* dispatch on the opcode to get its instruction class and to ! 1666: override any defaults: */ ! 1667: switch (opcode) { ! 1668: default: assert(FALSE); ! 1669: case TME_RECODE_OPCODE_ANDN: ! 1670: case TME_RECODE_OPCODE_ORN: ! 1671: case TME_RECODE_OPCODE_XORN: ! 1672: case TME_RECODE_OPCODE_AND: ! 1673: case TME_RECODE_OPCODE_OR: ! 1674: case TME_RECODE_OPCODE_XOR: ! 1675: class = TME_RECODE_INSN_CLASS_LOGICAL; ! 1676: break; ! 1677: case TME_RECODE_OPCODE_SUB: ! 1678: case TME_RECODE_OPCODE_SUBC: ! 1679: case TME_RECODE_OPCODE_ADD: ! 1680: case TME_RECODE_OPCODE_ADDC: ! 1681: class = TME_RECODE_INSN_CLASS_ADDITIVE; ! 1682: break; ! 1683: case TME_RECODE_OPCODE_SHRL: ! 1684: case TME_RECODE_OPCODE_SHLL: ! 1685: case TME_RECODE_OPCODE_SHRA: ! 1686: class = TME_RECODE_INSN_CLASS_SHIFT; ! 1687: break; ! 1688: case TME_RECODE_OPCODE_EXTZ: ! 1689: case TME_RECODE_OPCODE_EXTS: ! 1690: class = TME_RECODE_INSN_CLASS_EXT; ! 1691: #if (TME_RECODE_OPCODE_EXTS - TME_RECODE_OPCODE_EXTZ) != 1 ! 1692: #error "TME_RECODE_OPCODE_ values changed" ! 1693: #endif ! 1694: _subs ! 1695: = &(flags_thunk->tme_recode_x86_flags_thunk_sizes ! 1696: [flags_group->tme_recode_flags_group_insn_size - TME_RECODE_SIZE_8] ! 1697: .tme_recode_x86_flags_thunk_size_subs_ext ! 1698: [size_other - TME_RECODE_SIZE_8] ! 1699: [opcode - TME_RECODE_OPCODE_EXTZ]); ! 1700: insn.tme_recode_insn_imm_uguest = size_other; ! 1701: break; ! 1702: } ! 1703: ! 1704: /* skip this opcode if it's in the wrong class: */ ! 1705: if (class != flags_group->tme_recode_flags_group_insn_class) { ! 1706: continue; ! 1707: } ! 1708: ! 1709: /* skip the other size if it's not the result size of the ! 1710: instructions in this flags group, unless this is the ! 1711: extension class, in which case we skip other sizes that are ! 1712: greater than or equal to the result size: */ ! 1713: if (class == TME_RECODE_INSN_CLASS_EXT ! 1714: ? size_other >= flags_group->tme_recode_flags_group_insn_size ! 1715: : size_other != flags_group->tme_recode_flags_group_insn_size) { ! 1716: continue; ! 1717: } ! 1718: ! 1719: /* skip this opcode if we've already created its subs: */ ! 1720: if (*_subs != 0) { ! 1721: continue; ! 1722: } ! 1723: ! 1724: /* set the thunk offset of this subs: */ ! 1725: *_subs = tme_recode_build_to_thunk_off(ic, ic->tme_recode_ic_thunk_build_next); ! 1726: assert (*_subs != 0); ! 1727: ! 1728: /* if there is a guest function, or if this class doesn't chain ! 1729: to the test subs and we have do to its instructions at ! 1730: multiple sizes, not counting the double-host-size: */ ! 1731: if (flags_thunk->tme_recode_x86_flags_thunk_has_guest_func ! 1732: || (subs_test == 0 ! 1733: && multiple_ops)) { ! 1734: ! 1735: /* start more instructions: */ ! 1736: tme_recode_x86_insns_start(ic, thunk_bytes); ! 1737: ! 1738: /* copy the first source operand, currently in the destination ! 1739: operand host register, to the first source operand host ! 1740: register: */ ! 1741: if (TME_RECODE_SIZE_IS_DOUBLE_HOST(flags_group->tme_recode_flags_group_insn_size)) { ! 1742: _tme_recode_x86_emit_reg_copy(thunk_bytes, ! 1743: tme_recode_x86_reg_from_host[TME_RECODE_X86_REG_HOST_SUBS_DST + 1], ! 1744: tme_recode_x86_reg_from_host[TME_RECODE_X86_REG_HOST_SUBS_SRC0 + 1]); ! 1745: } ! 1746: _tme_recode_x86_emit_reg_copy(thunk_bytes, ! 1747: tme_recode_x86_reg_from_host[TME_RECODE_X86_REG_HOST_SUBS_DST], ! 1748: tme_recode_x86_reg_from_host[TME_RECODE_X86_REG_HOST_SUBS_SRC0]); ! 1749: ! 1750: /* finish these instructions: */ ! 1751: tme_recode_x86_insns_finish(ic, thunk_bytes); ! 1752: } ! 1753: ! 1754: /* if this is a logical noncommutative instruction: */ ! 1755: if ((1 << opcode) ! 1756: & ((1 << TME_RECODE_OPCODE_ANDN) ! 1757: | (1 << TME_RECODE_OPCODE_ORN) ! 1758: | (1 << TME_RECODE_OPCODE_XORN))) { ! 1759: ! 1760: /* we have to invert the second source operand and convert ! 1761: this instruction into its commutative version. for speed, ! 1762: we do at least a 32-bit not: */ ! 1763: insn_not.tme_recode_insn_opcode = opcode; ! 1764: insn_not.tme_recode_insn_size = TME_MAX(insn.tme_recode_insn_size, TME_RECODE_SIZE_32); ! 1765: ! 1766: /* NB that the a register can't be allocated to any guest ! 1767: register at this point, since it can be destroyed by a ! 1768: subs. since the a register is the second of a ! 1769: double-host-size pair, if this is a double-host-size guest, ! 1770: the first register of the pair is also free: */ ! 1771: #if (TME_RECODE_X86_REG_HOST_SUBS_SRC1 + 1) < TME_RECODE_X86_REG_HOST_FREE_CALL ! 1772: #error "TME_RECODE_X86_REG_HOST_ values changed" ! 1773: #endif ! 1774: assert (tme_recode_x86_reg_from_host[TME_RECODE_X86_REG_HOST_SUBS_SRC1 + 1] == TME_RECODE_X86_REG_A); ! 1775: ! 1776: /* if this is a double-host-size instruction: */ ! 1777: if (TME_RECODE_SIZE_IS_DOUBLE_HOST(flags_group->tme_recode_flags_group_insn_size)) { ! 1778: ! 1779: /* we invert the double-host-size second source operand in ! 1780: place, since both host registers can't be allocated to ! 1781: any guest register: */ ! 1782: insn_not.tme_recode_insn_operand_dst = TME_RECODE_X86_REG_HOST_SUBS_SRC1; ! 1783: } ! 1784: ! 1785: /* otherwise, this is not a double-host-size instruction: */ ! 1786: else { ! 1787: ! 1788: /* copy the second source operand into the free a register: */ ! 1789: tme_recode_x86_insns_start(ic, thunk_bytes); ! 1790: _tme_recode_x86_emit_reg_copy(thunk_bytes, ! 1791: tme_recode_x86_reg_from_host[TME_RECODE_X86_REG_HOST_SUBS_SRC1], ! 1792: tme_recode_x86_reg_from_host[TME_RECODE_X86_REG_HOST_SUBS_SRC1 + 1]); ! 1793: tme_recode_x86_insns_finish(ic, thunk_bytes); ! 1794: ! 1795: /* we invert the host-size or smaller second source operand in ! 1796: the a register: */ ! 1797: insn_not.tme_recode_insn_operand_dst = TME_RECODE_X86_REG_HOST_SUBS_SRC1 + 1; ! 1798: ! 1799: /* rewrite the second source operand to be the guest ! 1800: register we "allocated" to the a register: */ ! 1801: insn.tme_recode_insn_operand_src[1] = TME_RECODE_REG_GUEST(2); ! 1802: } ! 1803: ! 1804: /* invert the second source operand: */ ! 1805: _tme_recode_x86_insn_negate(ic, &insn_not); ! 1806: ! 1807: /* rewrite the opcode to a plain logical: */ ! 1808: insn.tme_recode_insn_opcode = TME_RECODE_OPCODE_LOGICALN_TO_PLAIN(opcode); ! 1809: } ! 1810: ! 1811: if (subs_test == 0) { ! 1812: ! 1813: /* make the binops subs. NB that we have to load the second ! 1814: source operand, since this is not necessarily ! 1815: TME_RECODE_X86_REG_HOST_SUBS_SRC1: */ ! 1816: _tme_recode_x86_flags_thunk_ops(flags_thunk, ! 1817: TME_RECODE_X86_OPCODE_TO_BINOP(insn.tme_recode_insn_opcode), ! 1818: tme_recode_regs_src_any(ic, ! 1819: &insn, ! 1820: insn.tme_recode_insn_operand_src[1]), ! 1821: host_sizes, ! 1822: ic); ! 1823: } ! 1824: ! 1825: else { ! 1826: ! 1827: /* emit an instruction normally: */ ! 1828: _tme_recode_x86_insn_emit(ic, &insn); ! 1829: } ! 1830: ! 1831: /* if this is a double-host-size logical noncommutative ! 1832: instruction and there is a guest function: */ ! 1833: if (((1 << opcode) ! 1834: & ((1 << TME_RECODE_OPCODE_ANDN) ! 1835: | (1 << TME_RECODE_OPCODE_ORN) ! 1836: | (1 << TME_RECODE_OPCODE_XORN))) ! 1837: && TME_RECODE_SIZE_IS_DOUBLE_HOST(flags_group->tme_recode_flags_group_insn_size) ! 1838: && flags_thunk->tme_recode_x86_flags_thunk_has_guest_func) { ! 1839: ! 1840: /* we have to undo the inversion of the double-host-size ! 1841: second source operand in place, because we have to pass it ! 1842: to the guest function: */ ! 1843: ! 1844: /* if there isn't a test subs: */ ! 1845: if (subs_test == 0) { ! 1846: ! 1847: /* push the double-host-size flags: */ ! 1848: tme_recode_x86_insns_start(ic, thunk_bytes); ! 1849: thunk_bytes[0] = TME_RECODE_X86_OPCODE_PUSHF; ! 1850: thunk_bytes++; ! 1851: tme_recode_x86_insns_finish(ic, thunk_bytes); ! 1852: } ! 1853: ! 1854: /* undo the inversion by redoing it: */ ! 1855: _tme_recode_x86_insn_negate(ic, &insn_not); ! 1856: ! 1857: /* if there isn't a test subs: */ ! 1858: if (subs_test == 0) { ! 1859: ! 1860: /* pop the double-host-size flags: */ ! 1861: tme_recode_x86_insns_start(ic, thunk_bytes); ! 1862: thunk_bytes[0] = TME_RECODE_X86_OPCODE_PUSHF; ! 1863: thunk_bytes++; ! 1864: tme_recode_x86_insns_finish(ic, thunk_bytes); ! 1865: } ! 1866: } ! 1867: ! 1868: /* chain to either the test subs or the main flags subs: */ ! 1869: _tme_recode_x86_flags_thunk_chain(ic, ! 1870: (subs_test != 0 ! 1871: ? subs_test ! 1872: : flags_thunk->tme_recode_x86_flags_thunk_subs_main)); ! 1873: } ! 1874: } ! 1875: ! 1876: /* return the flags thunk: */ ! 1877: return (flags_thunk); ! 1878: } ! 1879: ! 1880: #ifdef TME_RECODE_DEBUG ! 1881: #include <stdio.h> ! 1882: ! 1883: /* this host function dumps a flags thunk: */ ! 1884: void ! 1885: tme_recode_host_flags_thunk_dump(const struct tme_recode_ic *ic, ! 1886: const struct tme_recode_flags_thunk *flags_thunk) ! 1887: { ! 1888: unsigned int insn_size; ! 1889: unsigned int opcode; ! 1890: tme_recode_thunk_off_t thunk_off; ! 1891: unsigned int insn_size_other; ! 1892: ! 1893: printf(" x86 stack padding: %d\n", flags_thunk->tme_recode_x86_flags_thunk_stack_padding); ! 1894: printf(" x86 has guest func: %s\n", ! 1895: (flags_thunk->tme_recode_x86_flags_thunk_has_guest_func ! 1896: ? "yes" ! 1897: : "no")); ! 1898: printf(" x86 main flags subs: x/15i %p\n", ! 1899: tme_recode_thunk_off_to_pointer(ic, ! 1900: flags_thunk->tme_recode_x86_flags_thunk_subs_main, ! 1901: void (*)(void))); ! 1902: ! 1903: for (insn_size = TME_RECODE_SIZE_8; ! 1904: insn_size <= TME_RECODE_SIZE_GUEST_MAX; ! 1905: insn_size++) { ! 1906: for (opcode = 0; ! 1907: opcode < TME_RECODE_OPCODES_INTEGER; ! 1908: opcode++) { ! 1909: thunk_off = flags_thunk ! 1910: ->tme_recode_x86_flags_thunk_sizes[insn_size - TME_RECODE_SIZE_8] ! 1911: .tme_recode_x86_flags_thunk_size_subs[opcode]; ! 1912: if (thunk_off != 0) { ! 1913: printf(" x86 %s%u flags subs: x/4i %p\n", ! 1914: tme_recode_opcode_dump(opcode), ! 1915: (1 << insn_size), ! 1916: tme_recode_thunk_off_to_pointer(ic, ! 1917: thunk_off, ! 1918: void (*)(void))); ! 1919: } ! 1920: } ! 1921: #if (TME_RECODE_OPCODE_EXTZ + 1) != TME_RECODE_OPCODE_EXTS ! 1922: #error "TME_RECODE_OPCODE_EXTS or TME_RECODE_OPCODE_EXTZ changed" ! 1923: #endif ! 1924: for (opcode = TME_RECODE_OPCODE_EXTZ; ! 1925: opcode <= TME_RECODE_OPCODE_EXTS; ! 1926: opcode++) { ! 1927: for (insn_size_other = TME_RECODE_SIZE_8; ! 1928: insn_size_other < insn_size; ! 1929: insn_size_other++) { ! 1930: thunk_off = flags_thunk ! 1931: ->tme_recode_x86_flags_thunk_sizes[insn_size - TME_RECODE_SIZE_8] ! 1932: .tme_recode_x86_flags_thunk_size_subs_ext[insn_size_other - TME_RECODE_SIZE_8] ! 1933: [opcode - TME_RECODE_OPCODE_EXTZ]; ! 1934: if (thunk_off != 0) { ! 1935: printf(" x86 ext%u%c%u flags subs: x/4i %p\n", ! 1936: (1 << insn_size_other), ! 1937: (opcode == TME_RECODE_OPCODE_EXTZ ? 'z' : 's'), ! 1938: (1 << insn_size), ! 1939: tme_recode_thunk_off_to_pointer(ic, ! 1940: thunk_off, ! 1941: void (*)(void))); ! 1942: } ! 1943: } ! 1944: } ! 1945: } ! 1946: } ! 1947: ! 1948: #endif /* TME_RECODE_DEBUG */
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.