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