|
|
1.1 ! root 1: /* $Id: rc-x86-insns.c,v 1.7 2010/06/05 19:11:51 fredette Exp $ */ ! 2: ! 3: /* libtme/host/x86/rc-x86-insns.c - x86 host recode instructions 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-insns.c,v 1.7 2010/06/05 19:11:51 fredette Exp $"); ! 37: ! 38: /* this maps from recode opcode to x86 binary operation: */ ! 39: #define TME_RECODE_X86_OPCODE_TO_BINOP(opcode) _tme_recode_x86_opcode_to_binop[(opcode) - TME_RECODE_OPCODE_SUB] ! 40: static const tme_uint8_t _tme_recode_x86_opcode_to_binop[] = { ! 41: #if TME_RECODE_OPCODE_SUB != 3 ! 42: #error "TME_RECODE_OPCODE_ values changed, need to fix _tme_recode_x86_opcode_to_binop" ! 43: #endif ! 44: TME_RECODE_X86_OPCODE_BINOP_SUB, /* TME_RECODE_OPCODE_SUB */ ! 45: #if TME_RECODE_OPCODE_SUBC != 4 ! 46: #error "TME_RECODE_OPCODE_ values changed, need to fix _tme_recode_x86_opcode_to_binop" ! 47: #endif ! 48: TME_RECODE_X86_OPCODE_BINOP_SBB, /* TME_RECODE_OPCODE_SUBC */ ! 49: #if TME_RECODE_OPCODE_AND != 5 ! 50: #error "TME_RECODE_OPCODE_ values changed, need to fix _tme_recode_x86_opcode_to_binop" ! 51: #endif ! 52: TME_RECODE_X86_OPCODE_BINOP_AND, /* TME_RECODE_OPCODE_AND */ ! 53: #if TME_RECODE_OPCODE_OR != 6 ! 54: #error "TME_RECODE_OPCODE_ values changed, need to fix _tme_recode_x86_opcode_to_binop" ! 55: #endif ! 56: TME_RECODE_X86_OPCODE_BINOP_OR, /* TME_RECODE_OPCODE_OR */ ! 57: #if TME_RECODE_OPCODE_XOR != 7 ! 58: #error "TME_RECODE_OPCODE_ values changed, need to fix _tme_recode_x86_opcode_to_binop" ! 59: #endif ! 60: TME_RECODE_X86_OPCODE_BINOP_XOR, /* TME_RECODE_OPCODE_XOR */ ! 61: #if TME_RECODE_OPCODE_ADD != 8 ! 62: #error "TME_RECODE_OPCODE_ values changed, need to fix _tme_recode_x86_opcode_to_binop" ! 63: #endif ! 64: TME_RECODE_X86_OPCODE_BINOP_ADD, /* TME_RECODE_OPCODE_ADD */ ! 65: #if TME_RECODE_OPCODE_ADDC != 9 ! 66: #error "TME_RECODE_OPCODE_ values changed, need to fix _tme_recode_x86_opcode_to_binop" ! 67: #endif ! 68: TME_RECODE_X86_OPCODE_BINOP_ADC, /* TME_RECODE_OPCODE_ADDC */ ! 69: }; ! 70: ! 71: /* this maps from recode opcode to x86 shift group2 opcode: */ ! 72: #define TME_RECODE_X86_OPCODE_TO_GRP2(opcode) \ ! 73: ((((opcode) \ ! 74: - TME_RECODE_OPCODE_SHLL) \ ! 75: + TME_RECODE_X86_OPCODE_GRP2_SHL) \ ! 76: + (((opcode) \ ! 77: - TME_RECODE_OPCODE_SHLL) \ ! 78: / 2)) ! 79: #if TME_RECODE_X86_OPCODE_TO_GRP2(TME_RECODE_OPCODE_SHLL) != TME_RECODE_X86_OPCODE_GRP2_SHL ! 80: #error "TME_RECODE_OPCODE_ values changed" ! 81: #endif ! 82: #if TME_RECODE_X86_OPCODE_TO_GRP2(TME_RECODE_OPCODE_SHRL) != TME_RECODE_X86_OPCODE_GRP2_SHR ! 83: #error "TME_RECODE_OPCODE_ values changed" ! 84: #endif ! 85: #if TME_RECODE_X86_OPCODE_TO_GRP2(TME_RECODE_OPCODE_SHRA) != TME_RECODE_X86_OPCODE_GRP2_SAR ! 86: #error "TME_RECODE_OPCODE_ values changed" ! 87: #endif ! 88: ! 89: /* this emits a call to a subs: */ ! 90: static void ! 91: _tme_recode_x86_insn_subs(struct tme_recode_ic *ic, ! 92: const struct tme_recode_insn *insn) ! 93: { ! 94: tme_uint8_t *thunk_bytes; ! 95: ! 96: /* force the destination operand to be in the same host register as ! 97: the first source operand: */ ! 98: tme_recode_regs_dst_specific(ic, ! 99: insn, ! 100: 0); ! 101: ! 102: /* a subs is allowed to destroy the a register, which must be the ! 103: most-significant of the two host registers used for the subs ! 104: second source operand. if this is a double-host-size guest, we ! 105: must free both the a register and the other least-significant ! 106: host register, otherwise we only need to free the a register: */ ! 107: #if (TME_RECODE_X86_REG_HOST_SUBS_SRC1 & 1) != 0 ! 108: #error "TME_RECODE_X86_REG_HOST_ values changed" ! 109: #endif ! 110: assert (tme_recode_x86_reg_from_host[TME_RECODE_X86_REG_HOST_SUBS_SRC1 + 1] == TME_RECODE_X86_REG_A); ! 111: tme_recode_regs_host_free_specific(ic, ! 112: (TME_RECODE_SIZE_IS_DOUBLE_HOST(ic->tme_recode_ic_reg_size) ! 113: ? TME_RECODE_X86_REG_HOST_SUBS_SRC1 ! 114: : (TME_RECODE_X86_REG_HOST_SUBS_SRC1 + 1))); ! 115: ! 116: /* a subs is allowed to destroy the d and c registers, which must be ! 117: the two host registers used for the subs first source operand. ! 118: the d register must be a true host register, and the c register ! 119: must not be a true host register. if this isn't a ! 120: double-host-size guest, we must free the d register. we must ! 121: always mark the c register as destroyed: */ ! 122: #if (TME_RECODE_X86_REG_HOST_SUBS_SRC0 + 1) != TME_RECODE_REG_HOST_UNDEF ! 123: #error "TME_RECODE_X86_REG_HOST_ values changed" ! 124: #endif ! 125: assert (tme_recode_x86_reg_from_host[TME_RECODE_X86_REG_HOST_SUBS_SRC0 + 0] == TME_RECODE_X86_REG_D); ! 126: assert (tme_recode_x86_reg_from_host[TME_RECODE_X86_REG_HOST_SUBS_SRC0 + 1] == TME_RECODE_X86_REG_C); ! 127: if (!TME_RECODE_SIZE_IS_DOUBLE_HOST(ic->tme_recode_ic_reg_size)) { ! 128: tme_recode_regs_host_free_specific(ic, ! 129: TME_RECODE_X86_REG_HOST_SUBS_SRC0); ! 130: } ! 131: ic->tme_recode_x86_ic_thunks_reg_guest_window_c = TME_RECODE_REG_GUEST_WINDOW_UNDEF; ! 132: ! 133: /* start another instruction: */ ! 134: tme_recode_x86_insns_start(ic, thunk_bytes); ! 135: ! 136: /* emit the call to the instruction's subs: */ ! 137: thunk_bytes[0] = TME_RECODE_X86_OPCODE_CALL_RELz; ! 138: thunk_bytes += 1 + sizeof(tme_uint32_t); ! 139: ((tme_uint32_t *) thunk_bytes)[-1] ! 140: = (insn->tme_recode_x86_insn_subs_thunk_off ! 141: - tme_recode_build_to_thunk_off(ic, thunk_bytes)); ! 142: ! 143: /* finish this instruction: */ ! 144: tme_recode_x86_insns_finish(ic, thunk_bytes); ! 145: } ! 146: ! 147: /* this does a one's- or two's-complement negation of the destination ! 148: operand: */ ! 149: static void ! 150: _tme_recode_x86_insn_negate(struct tme_recode_ic *ic, ! 151: const struct tme_recode_insn *insn) ! 152: { ! 153: tme_uint8_t *thunk_bytes; ! 154: unsigned int reg_x86; ! 155: unsigned int opcode; ! 156: ! 157: /* start more instructions: */ ! 158: tme_recode_x86_insns_start(ic, thunk_bytes); ! 159: ! 160: /* get the opcode for this instruction: */ ! 161: opcode = insn->tme_recode_insn_opcode; ! 162: assert (opcode == TME_RECODE_OPCODE_SUB ! 163: || opcode == TME_RECODE_OPCODE_ANDN ! 164: || opcode == TME_RECODE_OPCODE_ORN ! 165: || opcode == TME_RECODE_OPCODE_XORN); ! 166: ! 167: /* get the (first) x86 register to negate: */ ! 168: reg_x86 = tme_recode_x86_reg_from_host[insn->tme_recode_insn_operand_dst]; ! 169: ! 170: /* emit a not %reg or neg %reg: */ ! 171: if (TME_RECODE_SIZE_HOST > TME_RECODE_SIZE_32) { ! 172: thunk_bytes[0] = TME_RECODE_X86_REX_B(TME_RECODE_SIZE_HOST, reg_x86); ! 173: } ! 174: thunk_bytes[(TME_RECODE_SIZE_HOST > TME_RECODE_SIZE_32) + 0] = TME_RECODE_X86_OPCODE_GRP3_Ev; ! 175: #if TME_RECODE_OPCODE_SUB <= TME_RECODE_OPCODE_ANDN || TME_RECODE_OPCODE_SUB <= TME_RECODE_OPCODE_ORN || TME_RECODE_OPCODE_SUB <= TME_RECODE_OPCODE_XORN ! 176: #error "TME_RECODE_OPCODE_ values changed" ! 177: #endif ! 178: thunk_bytes[(TME_RECODE_SIZE_HOST > TME_RECODE_SIZE_32) + 1] ! 179: = (TME_RECODE_X86_MOD_OPREG_RM(TME_RECODE_X86_MOD_RM_REG(reg_x86), 0) ! 180: + (opcode < TME_RECODE_OPCODE_SUB ! 181: ? TME_RECODE_X86_MOD_OPREG_RM(0, TME_RECODE_X86_OPCODE_GRP3_NOT) ! 182: : TME_RECODE_X86_MOD_OPREG_RM(0, TME_RECODE_X86_OPCODE_GRP3_NEG))); ! 183: thunk_bytes += (TME_RECODE_SIZE_HOST > TME_RECODE_SIZE_32) + 2; ! 184: ! 185: /* if this is a double-host-size operation: */ ! 186: if (TME_RECODE_SIZE_IS_DOUBLE_HOST(insn->tme_recode_insn_size)) { ! 187: ! 188: /* get the second x86 register to negate: */ ! 189: reg_x86 = tme_recode_x86_reg_from_host[insn->tme_recode_insn_operand_dst + 1]; ! 190: ! 191: /* if this is a two's complement negation: */ ! 192: if (opcode == TME_RECODE_OPCODE_SUB) { ! 193: ! 194: /* emit an adc $0, %reg: */ ! 195: if (TME_RECODE_SIZE_HOST > TME_RECODE_SIZE_32) { ! 196: thunk_bytes[0] = TME_RECODE_X86_REX_B(TME_RECODE_SIZE_HOST, reg_x86); ! 197: } ! 198: thunk_bytes[(TME_RECODE_SIZE_HOST > TME_RECODE_SIZE_32) + 0] = TME_RECODE_X86_OPCODE_GRP1_Ib_Ev; ! 199: thunk_bytes[(TME_RECODE_SIZE_HOST > TME_RECODE_SIZE_32) + 1] ! 200: = TME_RECODE_X86_MOD_OPREG_RM(TME_RECODE_X86_MOD_RM_REG(reg_x86), ! 201: TME_RECODE_X86_OPCODE_GRP1_BINOP(TME_RECODE_X86_OPCODE_BINOP_ADC)); ! 202: thunk_bytes[(TME_RECODE_SIZE_HOST > TME_RECODE_SIZE_32) + 2] = 0; ! 203: thunk_bytes += (TME_RECODE_SIZE_HOST > TME_RECODE_SIZE_32) + 3; ! 204: } ! 205: ! 206: /* emit a not %reg or a neg %reg: */ ! 207: if (TME_RECODE_SIZE_HOST > TME_RECODE_SIZE_32) { ! 208: thunk_bytes[0] = TME_RECODE_X86_REX_B(TME_RECODE_SIZE_HOST, reg_x86); ! 209: } ! 210: thunk_bytes[(TME_RECODE_SIZE_HOST > TME_RECODE_SIZE_32) + 0] = TME_RECODE_X86_OPCODE_GRP3_Ev; ! 211: #if TME_RECODE_OPCODE_SUB <= TME_RECODE_OPCODE_ORN || TME_RECODE_OPCODE_SUB <= TME_RECODE_OPCODE_XORN ! 212: #error "TME_RECODE_OPCODE_ values changed" ! 213: #endif ! 214: thunk_bytes[(TME_RECODE_SIZE_HOST > TME_RECODE_SIZE_32) + 1] ! 215: = (TME_RECODE_X86_MOD_OPREG_RM(TME_RECODE_X86_MOD_RM_REG(reg_x86), 0) ! 216: + (opcode < TME_RECODE_OPCODE_SUB ! 217: ? TME_RECODE_X86_MOD_OPREG_RM(0, TME_RECODE_X86_OPCODE_GRP3_NOT) ! 218: : TME_RECODE_X86_MOD_OPREG_RM(0, TME_RECODE_X86_OPCODE_GRP3_NEG))); ! 219: thunk_bytes += (TME_RECODE_SIZE_HOST > TME_RECODE_SIZE_32) + 2; ! 220: } ! 221: ! 222: /* finish these instructions: */ ! 223: tme_recode_x86_insns_finish(ic, thunk_bytes); ! 224: } ! 225: ! 226: /* this emits a host-sized binary operation instruction with an ! 227: immediate right source operand: */ ! 228: static void ! 229: _tme_recode_x86_insn_binop_imm(struct tme_recode_ic *ic, ! 230: const struct tme_recode_insn *insn) ! 231: { ! 232: tme_uint8_t *thunk_bytes; ! 233: unsigned int reg_x86_dst; ! 234: tme_int32_t imm_32; ! 235: unsigned int imm_size; ! 236: ! 237: /* start another instruction: */ ! 238: tme_recode_x86_insns_start(ic, thunk_bytes); ! 239: ! 240: /* get the x86 destination register: */ ! 241: reg_x86_dst = tme_recode_x86_reg_from_host[insn->tme_recode_insn_operand_dst]; ! 242: ! 243: /* emit any rex prefix: */ ! 244: if (TME_RECODE_SIZE_HOST > TME_RECODE_SIZE_32) { ! 245: thunk_bytes[0] = TME_RECODE_X86_REX_B(TME_RECODE_SIZE_HOST, reg_x86_dst); ! 246: } ! 247: ! 248: /* emit the modR/M byte for this instruction: */ ! 249: thunk_bytes[(TME_RECODE_SIZE_HOST > TME_RECODE_SIZE_32) + 1] ! 250: = TME_RECODE_X86_MOD_OPREG_RM(TME_RECODE_X86_MOD_RM_REG(reg_x86_dst), ! 251: TME_RECODE_X86_OPCODE_GRP1_BINOP(TME_RECODE_X86_OPCODE_TO_BINOP(insn->tme_recode_insn_opcode))); ! 252: ! 253: /* emit the immediate for this instruction as a sign-extended 32 bits: */ ! 254: imm_32 = insn->tme_recode_insn_imm_guest; ! 255: *((tme_int32_t *) &thunk_bytes[(TME_RECODE_SIZE_HOST > TME_RECODE_SIZE_32) + 2]) = imm_32; ! 256: ! 257: /* see if this sign-extended 32-bit immediate can be a sign-extended ! 258: eight bits instead: */ ! 259: imm_size ! 260: = (imm_32 == (tme_int8_t) imm_32 ! 261: ? sizeof(tme_int8_t) ! 262: : sizeof(tme_int32_t)); ! 263: ! 264: /* emit the opcode for this instruction: */ ! 265: /* NB: we take advantage of the fact that the group one opcodes for ! 266: an eight-bit immediate and a 32-bit immediate are separated by a ! 267: distance of two, which is sizeof(tme_int32_t) / 2: */ ! 268: #if (TME_RECODE_X86_OPCODE_GRP1_Ib_Ev - TME_RECODE_X86_OPCODE_GRP1_Iz_Ev) != 2 ! 269: #error "TME_RECODE_X86_OPCODE_GRP1_ opcodes changed" ! 270: #endif ! 271: thunk_bytes[(TME_RECODE_SIZE_HOST > TME_RECODE_SIZE_32) + 0] ! 272: = (TME_RECODE_X86_OPCODE_GRP1_Ib_Ev ! 273: - (imm_size / 2)); ! 274: ! 275: /* finish this instruction: */ ! 276: thunk_bytes += (TME_RECODE_SIZE_HOST > TME_RECODE_SIZE_32) + 2 + imm_size; ! 277: tme_recode_x86_insns_finish(ic, thunk_bytes); ! 278: } ! 279: ! 280: /* this emits a host-sized binary operation instruction with a ! 281: register right source operand: */ ! 282: static void ! 283: _tme_recode_x86_insn_binop(struct tme_recode_ic *ic, ! 284: const struct tme_recode_insn *insn) ! 285: { ! 286: tme_uint8_t *thunk_bytes; ! 287: unsigned int reg_x86_src; ! 288: unsigned int reg_x86_dst; ! 289: ! 290: /* start another instruction: */ ! 291: tme_recode_x86_insns_start(ic, thunk_bytes); ! 292: ! 293: /* get the x86 source and destination registers: */ ! 294: reg_x86_src = tme_recode_x86_reg_from_host[insn->tme_recode_insn_operand_src[1]]; ! 295: reg_x86_dst = tme_recode_x86_reg_from_host[insn->tme_recode_insn_operand_dst]; ! 296: ! 297: /* emit any rex prefix: */ ! 298: if (TME_RECODE_SIZE_HOST > TME_RECODE_SIZE_32) { ! 299: thunk_bytes[0] ! 300: = (TME_RECODE_X86_REX_R(TME_RECODE_SIZE_HOST, reg_x86_src) ! 301: | TME_RECODE_X86_REX_B(TME_RECODE_SIZE_HOST, reg_x86_dst)); ! 302: } ! 303: ! 304: /* emit the opcode for this instruction: */ ! 305: thunk_bytes[(TME_RECODE_SIZE_HOST > TME_RECODE_SIZE_32) + 0] ! 306: = (TME_RECODE_X86_OPCODE_TO_BINOP(insn->tme_recode_insn_opcode) ! 307: + TME_RECODE_X86_OPCODE_BINOP_Gv_Ev); ! 308: ! 309: /* emit the modR/M byte for this instruction: */ ! 310: thunk_bytes[(TME_RECODE_SIZE_HOST > TME_RECODE_SIZE_32) + 1] ! 311: = TME_RECODE_X86_MOD_OPREG_RM(TME_RECODE_X86_MOD_RM_REG(reg_x86_dst), ! 312: TME_RECODE_X86_REG(reg_x86_src)); ! 313: ! 314: /* finish this instruction: */ ! 315: thunk_bytes += (TME_RECODE_SIZE_HOST > TME_RECODE_SIZE_32) + 2; ! 316: tme_recode_x86_insns_finish(ic, thunk_bytes); ! 317: } ! 318: ! 319: /* this emits a host-sized zero-extend or sign-extend instruction: */ ! 320: static void ! 321: _tme_recode_x86_insn_ext(struct tme_recode_ic *ic, ! 322: const struct tme_recode_insn *insn) ! 323: { ! 324: tme_uint8_t *thunk_bytes; ! 325: unsigned int reg_x86_src; ! 326: unsigned int reg_x86_dst; ! 327: unsigned int extend; ! 328: unsigned int rex; ! 329: unsigned int mod_reg_rm; ! 330: ! 331: /* start another instruction: */ ! 332: tme_recode_x86_insns_start(ic, thunk_bytes); ! 333: ! 334: /* get the x86 source and destination registers: */ ! 335: reg_x86_src = tme_recode_x86_reg_from_host[insn->tme_recode_insn_operand_src[0]]; ! 336: reg_x86_dst = tme_recode_x86_reg_from_host[insn->tme_recode_insn_operand_dst]; ! 337: ! 338: /* get the size of the source operand: */ ! 339: extend = insn->tme_recode_insn_operand_src[1]; ! 340: assert (extend >= TME_RECODE_SIZE_8 ! 341: && extend < ic->tme_recode_ic_reg_size); ! 342: ! 343: /* assume that this is not an extension from eight bits, and start ! 344: any rex prefix with the source register: */ ! 345: rex = TME_RECODE_X86_REX_B(0, reg_x86_src); ! 346: ! 347: /* if this is an extension from eight bits: */ ! 348: if (extend == TME_RECODE_SIZE_8) { ! 349: ! 350: /* if this is an ia32 host, and the source register has no ! 351: eight-bit register encoding: */ ! 352: if (TME_RECODE_SIZE_HOST == TME_RECODE_SIZE_32 ! 353: && reg_x86_src >= TME_RECODE_X86_REG_SP) { ! 354: ! 355: /* we destroy the c register: */ ! 356: ic->tme_recode_x86_ic_thunks_reg_guest_window_c = TME_RECODE_REG_GUEST_WINDOW_UNDEF; ! 357: ! 358: /* copy the source register into the scratch c register: */ ! 359: _tme_recode_x86_emit_reg_copy(thunk_bytes, reg_x86_src, TME_RECODE_X86_REG_C); ! 360: ! 361: /* the source register is now the scratch c register: */ ! 362: reg_x86_src = TME_RECODE_X86_REG_C; ! 363: } ! 364: ! 365: /* restart any rex prefix: */ ! 366: rex = TME_RECODE_X86_REX_B(TME_RECODE_SIZE_8, reg_x86_src); ! 367: } ! 368: ! 369: /* add the destination register to any rex prefix: */ ! 370: rex |= TME_RECODE_X86_REX_R(0, reg_x86_dst); ! 371: ! 372: /* make the modR/M byte for this instruction, shifted up to the ! 373: second byte to make room for the opcode: */ ! 374: mod_reg_rm ! 375: = (TME_RECODE_X86_MOD_OPREG_RM(TME_RECODE_X86_MOD_RM_REG(reg_x86_src), ! 376: TME_RECODE_X86_REG(reg_x86_dst)) ! 377: << 8); ! 378: ! 379: /* if this is an x86-64 host: */ ! 380: if (TME_RECODE_SIZE_HOST > TME_RECODE_SIZE_32) { ! 381: ! 382: /* if this is a sign-extension: */ ! 383: if (insn->tme_recode_insn_opcode == TME_RECODE_OPCODE_EXTS) { ! 384: ! 385: /* sign extension is a 64-bit operation: */ ! 386: rex |= TME_RECODE_X86_REX_W(TME_RECODE_SIZE_HOST); ! 387: } ! 388: ! 389: /* emit any rex prefix: */ ! 390: thunk_bytes[0] = rex; ! 391: thunk_bytes += (rex != 0); ! 392: } ! 393: ! 394: /* if this is an x86-64 host and this is an extension from 32 ! 395: bits: */ ! 396: if (TME_RECODE_SIZE_HOST > TME_RECODE_SIZE_32 ! 397: && extend == TME_RECODE_SIZE_32) { ! 398: ! 399: /* emit either a movl %src, %dst or a movslq %src, %dst: */ ! 400: *((tme_uint16_t *) &thunk_bytes[0]) ! 401: = ((insn->tme_recode_insn_opcode == TME_RECODE_OPCODE_EXTS ! 402: ? TME_RECODE_X86_OPCODE_MOVS_El_Gv ! 403: : (TME_RECODE_X86_OPCODE_BINOP_MOV ! 404: + TME_RECODE_X86_OPCODE_BINOP_Ev_Gv)) ! 405: + mod_reg_rm); ! 406: thunk_bytes += 2; ! 407: } ! 408: ! 409: /* otherwise, either this is not an x86-64 host or this is an ! 410: extension from 16 or 8 bits: */ ! 411: else { ! 412: assert (extend == TME_RECODE_SIZE_16 ! 413: || extend == TME_RECODE_SIZE_8); ! 414: ! 415: /* emit the two-byte opcode escape: */ ! 416: thunk_bytes[0] = TME_RECODE_X86_OPCODE_ESC_0F; ! 417: ! 418: /* emit either a movzb %src, %dst, or a movzw %src, %dst, or a ! 419: movsb %src %dst, or a movsw %src, %dst: */ ! 420: #if (TME_RECODE_X86_OPCODE0F_MOVS_Eb_Gv ^ TME_RECODE_X86_OPCODE0F_MOVZ_Eb_Gv) != (TME_RECODE_X86_OPCODE0F_MOVS_Ew_Gv ^ TME_RECODE_X86_OPCODE0F_MOVZ_Ew_Gv) ! 421: #error "TME_RECODE_X86_OPCODE0F_MOV values changed" ! 422: #endif ! 423: #if (TME_RECODE_OPCODE_EXTS - TME_RECODE_OPCODE_EXTZ) != 1 ! 424: #error "TME_RECODE_OPCODE_ values changed" ! 425: #endif ! 426: #if (TME_RECODE_X86_OPCODE0F_MOVZ_Ew_Gv ^ TME_RECODE_X86_OPCODE0F_MOVZ_Eb_Gv) != (TME_RECODE_X86_OPCODE0F_MOVS_Ew_Gv ^ TME_RECODE_X86_OPCODE0F_MOVS_Eb_Gv) ! 427: #error "TME_RECODE_X86_OPCODE0F_MOV values changed" ! 428: #endif ! 429: #if (TME_RECODE_SIZE_16 - TME_RECODE_SIZE_8) != 1 ! 430: #error "TME_RECODE_SIZE_ values changed" ! 431: #endif ! 432: *((tme_uint16_t *) &thunk_bytes[1]) ! 433: = ((TME_RECODE_X86_OPCODE0F_MOVZ_Eb_Gv ! 434: + ((extend ! 435: - TME_RECODE_SIZE_8) ! 436: * (TME_RECODE_X86_OPCODE0F_MOVZ_Ew_Gv ! 437: - TME_RECODE_X86_OPCODE0F_MOVZ_Eb_Gv)) ! 438: + ((insn->tme_recode_insn_opcode ! 439: - TME_RECODE_OPCODE_EXTZ) ! 440: * (TME_RECODE_X86_OPCODE0F_MOVS_Eb_Gv ! 441: - TME_RECODE_X86_OPCODE0F_MOVZ_Eb_Gv))) ! 442: + mod_reg_rm); ! 443: thunk_bytes += 3; ! 444: } ! 445: ! 446: /* finish this instruction: */ ! 447: tme_recode_x86_insns_finish(ic, thunk_bytes); ! 448: } ! 449: ! 450: /* this emits a host-sized shift-by-immediate instruction: */ ! 451: static void ! 452: _tme_recode_x86_insn_shift_imm(struct tme_recode_ic *ic, ! 453: const struct tme_recode_insn *insn) ! 454: { ! 455: tme_uint8_t *thunk_bytes; ! 456: unsigned int reg_x86_dst; ! 457: ! 458: /* start another instruction: */ ! 459: tme_recode_x86_insns_start(ic, thunk_bytes); ! 460: ! 461: /* get the x86 destination register: */ ! 462: reg_x86_dst = tme_recode_x86_reg_from_host[insn->tme_recode_insn_operand_dst]; ! 463: ! 464: /* emit any rex prefix: */ ! 465: if (TME_RECODE_SIZE_HOST > TME_RECODE_SIZE_32) { ! 466: thunk_bytes[0] = TME_RECODE_X86_REX_B(TME_RECODE_SIZE_HOST, reg_x86_dst); ! 467: } ! 468: ! 469: /* emit the opcode for this instruction: */ ! 470: thunk_bytes[(TME_RECODE_SIZE_HOST > TME_RECODE_SIZE_32) + 0] = TME_RECODE_X86_OPCODE_GRP2_Ib_Ev; ! 471: ! 472: /* emit the modR/M byte for this instruction: */ ! 473: thunk_bytes[(TME_RECODE_SIZE_HOST > TME_RECODE_SIZE_32) + 1] ! 474: = TME_RECODE_X86_MOD_OPREG_RM(TME_RECODE_X86_MOD_RM_REG(reg_x86_dst), ! 475: TME_RECODE_X86_OPCODE_TO_GRP2(insn->tme_recode_insn_opcode)); ! 476: ! 477: /* emit the immediate shift count for this instruction: */ ! 478: thunk_bytes[(TME_RECODE_SIZE_HOST > TME_RECODE_SIZE_32) + 2] = insn->tme_recode_insn_imm_uguest; ! 479: ! 480: /* finish this instruction: */ ! 481: thunk_bytes += (TME_RECODE_SIZE_HOST > TME_RECODE_SIZE_32) + 3; ! 482: tme_recode_x86_insns_finish(ic, thunk_bytes); ! 483: } ! 484: ! 485: /* this emits a guest instruction: */ ! 486: static void ! 487: _tme_recode_x86_insn_guest(struct tme_recode_ic *ic, ! 488: struct tme_recode_insn *insn) ! 489: { ! 490: tme_uint8_t *thunk_bytes; ! 491: unsigned long reg_host_free; ! 492: ! 493: /* since a guest function may fault and never return, we have to ! 494: clean all dirty host registers so that all guest registers are ! 495: correct in the guest ic state at the time of the fault: */ ! 496: tme_recode_regs_host_clean_all(ic); ! 497: ! 498: /* a guest function that takes an undefined first source operand, ! 499: must take an undefined second source operand: */ ! 500: assert (insn->tme_recode_insn_operand_src[0] != TME_RECODE_OPERAND_UNDEF ! 501: || insn->tme_recode_insn_operand_src[1] == TME_RECODE_OPERAND_UNDEF); ! 502: ! 503: /* if this is an ia32 host: */ ! 504: if (TME_RECODE_SIZE_HOST == TME_RECODE_SIZE_32) { ! 505: ! 506: /* if this guest function takes an undefined second source operand: */ ! 507: if (insn->tme_recode_insn_operand_src[1] == TME_RECODE_OPERAND_UNDEF) { ! 508: ! 509: /* if this guest function returns a register value, we need to ! 510: reserve the host register(s) for the return value, since we ! 511: won't reserve by loading the second source operand: */ ! 512: if (insn->tme_recode_insn_operand_dst != TME_RECODE_OPERAND_NULL) { ! 513: tme_recode_regs_host_reserve(ic, ! 514: (TME_RECODE_SIZE_IS_DOUBLE_HOST(ic->tme_recode_ic_reg_size) ! 515: ? (TME_RECODE_X86_REG_HOST_FREE_CALL - 1) ! 516: : TME_RECODE_X86_REG_HOST_FREE_CALL)); ! 517: } ! 518: } ! 519: ! 520: /* otherwise, this guest function takes a defined second source operand: */ ! 521: else { ! 522: ! 523: /* if this is a double-host-size guest, load the second source ! 524: operand into %eax:%ebp, otherwise load it into %eax: */ ! 525: #if (TME_RECODE_X86_REG_HOST_FREE_CALL & 1) != 1 ! 526: #error "TME_RECODE_X86_REG_HOST_ values changed" ! 527: #endif ! 528: tme_recode_regs_src_specific(ic, ! 529: insn, ! 530: insn->tme_recode_insn_operand_src[1], ! 531: (TME_RECODE_SIZE_IS_DOUBLE_HOST(ic->tme_recode_ic_reg_size) ! 532: ? (TME_RECODE_X86_REG_HOST_FREE_CALL - 1) ! 533: : TME_RECODE_X86_REG_HOST_FREE_CALL)); ! 534: } ! 535: ! 536: /* if this guest function takes a defined first source operand: */ ! 537: if (insn->tme_recode_insn_operand_src[0] != TME_RECODE_OPERAND_UNDEF) { ! 538: ! 539: /* load the first source operand into any host register: */ ! 540: insn->tme_recode_insn_operand_src[0] ! 541: = tme_recode_regs_src_any(ic, ! 542: insn, ! 543: insn->tme_recode_insn_operand_src[0]); ! 544: } ! 545: ! 546: /* if this guest function returns a register value: */ ! 547: if (insn->tme_recode_insn_operand_dst != TME_RECODE_OPERAND_NULL) { ! 548: ! 549: /* if this is a double-host-size guest, force the destination ! 550: operand to be in %eax:%ebp, otherwise force it to be directly ! 551: in the return value register (%eax): */ ! 552: tme_recode_regs_dst_specific(ic, ! 553: insn, ! 554: 0); ! 555: ! 556: /* free all host registers that be will destroyed by the call, ! 557: except for the first one, which we are using for the ! 558: destination operand: */ ! 559: reg_host_free = TME_RECODE_X86_REG_HOST_FREE_CALL + 1; ! 560: } ! 561: ! 562: /* otherwise, this guest function does not return a register value: */ ! 563: else { ! 564: ! 565: /* we must assume that this guest function modifies all guest ! 566: registers: */ ! 567: ! 568: /* unreserve all registers: */ ! 569: tme_recode_regs_host_unreserve_all(ic); ! 570: ! 571: /* free all host registers: */ ! 572: reg_host_free = TME_RECODE_REG_HOST(0); ! 573: } ! 574: ! 575: /* free host registers: */ ! 576: tme_recode_regs_host_free_many(ic, reg_host_free); ! 577: ! 578: /* the guest function call destroys the c register: */ ! 579: ic->tme_recode_x86_ic_thunks_reg_guest_window_c = TME_RECODE_REG_GUEST_WINDOW_UNDEF; ! 580: ! 581: /* start more instructions: */ ! 582: tme_recode_x86_insns_start(ic, thunk_bytes); ! 583: ! 584: /* if this guest function takes two undefined source operands: */ ! 585: if (insn->tme_recode_insn_operand_src[0] == TME_RECODE_OPERAND_UNDEF) { ! 586: ! 587: /* push garbage source operands for the guest function: */ ! 588: thunk_bytes = _tme_recode_x86_emit_adjust_sp(thunk_bytes, - (int) (sizeof(tme_recode_uguest_t) * 2)); ! 589: } ! 590: ! 591: /* otherwise, this guest function takes at least one defined ! 592: source operand: */ ! 593: else { ! 594: ! 595: /* push the second source operand for the guest function. NB that ! 596: if double-host-size guests are supported, but this isn't a ! 597: double-host-size guest, we use a garbage word on the stack as ! 598: the most-significant half of that argument (which is okay since ! 599: the guest functions are supposed to truncate their arguments to ! 600: the expected size): */ ! 601: if (TME_RECODE_SIZE_IS_DOUBLE_HOST(ic->tme_recode_ic_reg_size)) { ! 602: _tme_recode_x86_emit_reg_push(thunk_bytes, TME_RECODE_X86_REG_A); ! 603: _tme_recode_x86_emit_reg_push(thunk_bytes, TME_RECODE_X86_REG_BP); ! 604: } ! 605: else { ! 606: if (TME_RECODE_SIZE_GUEST_MAX > TME_RECODE_SIZE_HOST) { ! 607: thunk_bytes = _tme_recode_x86_emit_adjust_sp(thunk_bytes, - (int) (sizeof(tme_recode_uguest_t) / 2)); ! 608: } ! 609: _tme_recode_x86_emit_reg_push(thunk_bytes, TME_RECODE_X86_REG_A); ! 610: } ! 611: ! 612: /* push the first source operand for the guest function. if ! 613: double-host size guests are supported, assume that this is a ! 614: double-host-size guest and push the most-significant half of ! 615: the argument first. if this isn't a double-host-size guest, ! 616: this will push some unknown register (which is okay since the ! 617: guest functions are supposed to truncate their arguments to the ! 618: expected size): */ ! 619: if (TME_RECODE_SIZE_GUEST_MAX > TME_RECODE_SIZE_HOST) { ! 620: _tme_recode_x86_emit_reg_push(thunk_bytes, ! 621: tme_recode_x86_reg_from_host[insn->tme_recode_insn_operand_src[0] + 1]); ! 622: } ! 623: _tme_recode_x86_emit_reg_push(thunk_bytes, ! 624: tme_recode_x86_reg_from_host[insn->tme_recode_insn_operand_src[0]]); ! 625: } ! 626: ! 627: /* emit the instruction to push the struct tme_ic * argument for ! 628: the guest function, and then the call instruction to the guest ! 629: function: */ ! 630: *((tme_uint16_t *) thunk_bytes) ! 631: = (TME_RECODE_X86_OPCODE_PUSH_Gv(TME_RECODE_X86_REG_IC) ! 632: + (TME_RECODE_X86_OPCODE_CALL_RELz << 8)); ! 633: thunk_bytes += 2 + sizeof(tme_uint32_t); ! 634: ((tme_int32_t *) thunk_bytes)[-1] ! 635: = (tme_recode_function_to_thunk_off(ic, insn->tme_recode_insn_guest_func) ! 636: - tme_recode_build_to_thunk_off(ic, thunk_bytes)); ! 637: ! 638: /* remove the guest function arguments from the stack: */ ! 639: thunk_bytes ! 640: = _tme_recode_x86_emit_adjust_sp(thunk_bytes, ! 641: (sizeof(struct tme_ic *) ! 642: + sizeof(tme_recode_uguest_t) ! 643: + sizeof(tme_recode_uguest_t))); ! 644: } ! 645: ! 646: /* otherwise, this is an x86-64 host: */ ! 647: else { ! 648: ! 649: /* if this is a double-host-size guest: */ ! 650: if (TME_RECODE_SIZE_IS_DOUBLE_HOST(ic->tme_recode_ic_reg_size)) { ! 651: ! 652: /* if this guest function takes an undefined first source operand: */ ! 653: if (insn->tme_recode_insn_operand_src[0] == TME_RECODE_OPERAND_UNDEF) { ! 654: ! 655: /* if this guest function returns a register value, we need to ! 656: reserve the host register(s) for the return value, since we ! 657: won't reserve it by loading the first source operand: */ ! 658: if (insn->tme_recode_insn_operand_dst != TME_RECODE_OPERAND_NULL) { ! 659: tme_recode_regs_host_reserve(ic, ! 660: TME_RECODE_X86_REG_HOST_FREE_CALL - 1); ! 661: } ! 662: } ! 663: ! 664: /* otherwise, this guest function takes a defined first source operand: */ ! 665: else { ! 666: ! 667: /* load the first source operand into the first host register ! 668: pair that is destroyed by the call: */ ! 669: #if (TME_RECODE_X86_REG_HOST_FREE_CALL & 1) != 1 ! 670: #error "TME_RECODE_X86_REG_HOST_ values changed" ! 671: #endif ! 672: tme_recode_regs_src_specific(ic, ! 673: insn, ! 674: insn->tme_recode_insn_operand_src[0], ! 675: TME_RECODE_X86_REG_HOST_FREE_CALL - 1); ! 676: } ! 677: ! 678: /* if this guest function takes a defined second source operand: */ ! 679: if (insn->tme_recode_insn_operand_src[1] != TME_RECODE_OPERAND_UNDEF) { ! 680: ! 681: /* load the second source operand into the second host register ! 682: pair that is destroyed by the call: */ ! 683: tme_recode_regs_src_specific(ic, ! 684: insn, ! 685: insn->tme_recode_insn_operand_src[1], ! 686: TME_RECODE_X86_REG_HOST_FREE_CALL + 1); ! 687: } ! 688: ! 689: /* if this guest function returns a register value: */ ! 690: if (insn->tme_recode_insn_operand_dst != TME_RECODE_OPERAND_NULL) { ! 691: ! 692: /* force the destination operand to be in %rax:%rbp: */ ! 693: tme_recode_regs_dst_specific(ic, ! 694: insn, ! 695: 0); ! 696: ! 697: /* free all host registers that be will destroyed by the call, ! 698: except for the first one, which we are using for the ! 699: destination operand: */ ! 700: reg_host_free = TME_RECODE_X86_REG_HOST_FREE_CALL + 1; ! 701: } ! 702: ! 703: /* otherwise, this guest function does not return a register value: */ ! 704: else { ! 705: ! 706: /* unreserve all registers: */ ! 707: tme_recode_regs_host_unreserve_all(ic); ! 708: ! 709: /* free all host registers: */ ! 710: reg_host_free = TME_RECODE_REG_HOST(0); ! 711: } ! 712: ! 713: /* free host registers: */ ! 714: tme_recode_regs_host_free_many(ic, reg_host_free); ! 715: ! 716: /* the guest function call destroys the c register: */ ! 717: ic->tme_recode_x86_ic_thunks_reg_guest_window_c = TME_RECODE_REG_GUEST_WINDOW_UNDEF; ! 718: ! 719: /* start more instructions: */ ! 720: tme_recode_x86_insns_start(ic, thunk_bytes); ! 721: ! 722: /* move the first and second source operands into the function ! 723: argument registers: */ ! 724: if (insn->tme_recode_insn_operand_src[0] != TME_RECODE_OPERAND_UNDEF) { ! 725: _tme_recode_x86_emit_reg_copy(thunk_bytes, TME_RECODE_X86_REG_BP, TME_RECODE_X86_REG_SI); ! 726: _tme_recode_x86_emit_reg_copy(thunk_bytes, TME_RECODE_X86_REG_A, TME_RECODE_X86_REG_D); ! 727: } ! 728: if (insn->tme_recode_insn_operand_src[1] != TME_RECODE_OPERAND_UNDEF) { ! 729: _tme_recode_x86_emit_reg_copy(thunk_bytes, TME_RECODE_X86_REG_N(10), TME_RECODE_X86_REG_C); ! 730: _tme_recode_x86_emit_reg_copy(thunk_bytes, TME_RECODE_X86_REG_N(11), TME_RECODE_X86_REG_N(8)); ! 731: } ! 732: } ! 733: ! 734: /* otherwise, this is not a double-host-size guest: */ ! 735: else { ! 736: ! 737: /* if this guest function takes a defined first source operand: */ ! 738: if (insn->tme_recode_insn_operand_src[0] != TME_RECODE_OPERAND_UNDEF) { ! 739: ! 740: /* load the first source operand directly into its (first) argument ! 741: register (%rsi): */ ! 742: tme_recode_regs_src_specific(ic, ! 743: insn, ! 744: insn->tme_recode_insn_operand_src[0], ! 745: TME_RECODE_X86_REG_HOST_ARG(1)); ! 746: } ! 747: ! 748: /* if this guest function takes a defined second source operand: */ ! 749: if (insn->tme_recode_insn_operand_src[1] != TME_RECODE_OPERAND_UNDEF) { ! 750: ! 751: /* load the second source operand directly into %rdx. if ! 752: double-host-size guests are not supported, this is the ! 753: correct argument register for the second source operand. ! 754: otherwise, double-host-size guests are supported, and %rdx is ! 755: the most-significant of the two first source operand argument ! 756: registers. in that case, below we'll copy %rdx into the ! 757: first argument register for the second source operand, but ! 758: otherwise we'll leave %rdx loaded, which is okay since the ! 759: guest functions are supposed to truncate their arguments to ! 760: the expected size: */ ! 761: tme_recode_regs_src_specific(ic, ! 762: insn, ! 763: insn->tme_recode_insn_operand_src[1], ! 764: TME_RECODE_X86_REG_HOST_ARG(2)); ! 765: } ! 766: ! 767: /* if this guest function returns a register value: */ ! 768: if (insn->tme_recode_insn_operand_dst != TME_RECODE_OPERAND_NULL) { ! 769: ! 770: /* force the destination operand to be directly in the return ! 771: value register (%rax): */ ! 772: tme_recode_regs_host_reserve(ic, ! 773: TME_RECODE_X86_REG_HOST_FREE_CALL); ! 774: tme_recode_regs_dst_specific(ic, ! 775: insn, ! 776: 2); ! 777: ! 778: /* free all host registers that will be destroyed by the call, ! 779: except for the first one, which we are using for the ! 780: destination operand: */ ! 781: reg_host_free = TME_RECODE_X86_REG_HOST_FREE_CALL + 1; ! 782: } ! 783: ! 784: /* otherwise, this guest function does not return a register value: */ ! 785: else { ! 786: ! 787: /* unreserve all registers: */ ! 788: tme_recode_regs_host_unreserve_all(ic); ! 789: ! 790: /* free all host registers: */ ! 791: reg_host_free = TME_RECODE_REG_HOST(0); ! 792: } ! 793: ! 794: /* free host registers: */ ! 795: tme_recode_regs_host_free_many(ic, reg_host_free); ! 796: ! 797: /* the guest function call destroys the c register: */ ! 798: ic->tme_recode_x86_ic_thunks_reg_guest_window_c = TME_RECODE_REG_GUEST_WINDOW_UNDEF; ! 799: ! 800: /* start more instructions: */ ! 801: tme_recode_x86_insns_start(ic, thunk_bytes); ! 802: ! 803: /* if double-host-size guests are supported: */ ! 804: if (TME_RECODE_SIZE_GUEST_MAX > TME_RECODE_SIZE_HOST) { ! 805: ! 806: /* if this guest function takes a defined second source operand: */ ! 807: if (insn->tme_recode_insn_operand_src[1] != TME_RECODE_OPERAND_UNDEF) { ! 808: ! 809: /* copy the second source operand from %rdx into the second ! 810: source operand function argument registers. NB that this ! 811: leaves the most-significant half of the argument (%r8) ! 812: undefined, which is okay since the guest functions are ! 813: supposed to truncate their arguments to the expected ! 814: size: */ ! 815: _tme_recode_x86_emit_reg_copy(thunk_bytes, TME_RECODE_X86_REG_D, TME_RECODE_X86_REG_C); ! 816: } ! 817: } ! 818: } ! 819: ! 820: /* emit a movq %ic, %rdi: */ ! 821: _tme_recode_x86_emit_reg_copy(thunk_bytes, TME_RECODE_X86_REG_IC, TME_RECODE_X86_REG_DI); ! 822: ! 823: /* we must assume that we can't reach the guest function from the ! 824: instruction thunk with a 32-bit displacement. emit a direct ! 825: call to the guest function using %rax: */ ! 826: *((tme_uint16_t *) thunk_bytes) ! 827: = (TME_RECODE_X86_REX_B(TME_RECODE_SIZE_HOST, TME_RECODE_X86_REG_A) ! 828: + (TME_RECODE_X86_OPCODE_MOV_Iv_Gv(TME_RECODE_X86_REG_A) ! 829: << 8)); ! 830: memcpy(thunk_bytes + 2, ! 831: &insn->tme_recode_insn_guest_func, ! 832: TME_BIT(TME_RECODE_SIZE_HOST - TME_RECODE_SIZE_8)); ! 833: thunk_bytes += 2 + TME_BIT(TME_RECODE_SIZE_HOST - TME_RECODE_SIZE_8); ! 834: *((tme_uint16_t *) thunk_bytes) ! 835: = (TME_RECODE_X86_OPCODE_GRP5 ! 836: + (TME_RECODE_X86_MOD_OPREG_RM(TME_RECODE_X86_MOD_RM_REG(TME_RECODE_X86_REG_A), ! 837: TME_RECODE_X86_OPCODE_GRP5_CALL) ! 838: << 8)); ! 839: thunk_bytes += 2; ! 840: } ! 841: ! 842: /* if the guest function returned a register value, and this is a ! 843: double-host-size guest: */ ! 844: if (insn->tme_recode_insn_operand_dst != TME_RECODE_OPERAND_NULL ! 845: && TME_RECODE_SIZE_IS_DOUBLE_HOST(ic->tme_recode_ic_reg_size)) { ! 846: ! 847: /* move the guest function return value from d:a into ax:bp: */ ! 848: _tme_recode_x86_emit_reg_copy(thunk_bytes, TME_RECODE_X86_REG_A, TME_RECODE_X86_REG_BP); ! 849: _tme_recode_x86_emit_reg_copy(thunk_bytes, TME_RECODE_X86_REG_D, TME_RECODE_X86_REG_A); ! 850: } ! 851: ! 852: /* finish these instructions: */ ! 853: tme_recode_x86_insns_finish(ic, thunk_bytes); ! 854: } ! 855: ! 856: /* this emits a read or a write instruction: */ ! 857: static void ! 858: _tme_recode_x86_insn_rw(struct tme_recode_ic *ic, ! 859: struct tme_recode_insn *insn) ! 860: { ! 861: const struct tme_recode_rw_thunk *rw_thunk; ! 862: unsigned int reg_host_address; ! 863: tme_uint8_t *thunk_bytes; ! 864: ! 865: /* get the read/write thunk: */ ! 866: rw_thunk = insn->tme_recode_insn_rw_thunk; ! 867: ! 868: /* set the instruction size to the address size: */ ! 869: insn->tme_recode_insn_size = rw_thunk->tme_recode_rw_thunk_address_size; ! 870: ! 871: /* the guest address is often a value that has been calculated in a ! 872: guest temporary register. before we clean all dirty host ! 873: registers, notify that the guest address will be the first source ! 874: operand we load, to hopefully avoid a store of a dirty guest ! 875: temporary register: */ ! 876: tme_recode_regs_src_notify(ic, ! 877: insn, ! 878: insn->tme_recode_insn_operand_src[0]); ! 879: ! 880: /* since a read or a write instruction may fault and never return, ! 881: we have to clean all dirty host registers so that all guest ! 882: registers are correct in the guest ic state at the time of the ! 883: fault: */ ! 884: tme_recode_regs_host_clean_all(ic); ! 885: ! 886: /* if this is a double-host-size guest, load the guest address into ! 887: the a:bp register pair, otherwise load it into the a register: */ ! 888: #if (TME_RECODE_X86_REG_HOST_FREE_CALL & 1) != 1 ! 889: #error "TME_RECODE_X86_REG_HOST_ values changed" ! 890: #endif ! 891: reg_host_address ! 892: = (TME_RECODE_SIZE_IS_DOUBLE_HOST(ic->tme_recode_ic_reg_size) ! 893: ? (TME_RECODE_X86_REG_HOST_FREE_CALL - 1) ! 894: : TME_RECODE_X86_REG_HOST_FREE_CALL); ! 895: tme_recode_regs_src_specific(ic, ! 896: insn, ! 897: insn->tme_recode_insn_operand_src[0], ! 898: reg_host_address); ! 899: ! 900: /* set the instruction size to the register size: */ ! 901: insn->tme_recode_insn_size = rw_thunk->tme_recode_rw_thunk_reg_size; ! 902: ! 903: /* if this is a write: */ ! 904: if (rw_thunk->tme_recode_rw_thunk_write) { ! 905: ! 906: /* load the value to write into the first host register (pair): */ ! 907: tme_recode_regs_src_specific(ic, ! 908: insn, ! 909: insn->tme_recode_insn_operand_src[1], ! 910: TME_RECODE_REG_HOST(0)); ! 911: ! 912: /* unreserve all registers: */ ! 913: tme_recode_regs_host_unreserve_all(ic); ! 914: ! 915: /* a write thunk destroys the guest address: */ ! 916: tme_recode_regs_host_free_specific(ic, ! 917: reg_host_address); ! 918: } ! 919: ! 920: else { ! 921: ! 922: /* force the destination register into the same register(s) ! 923: holding the guest address: */ ! 924: tme_recode_regs_dst_specific(ic, ! 925: insn, ! 926: 0); ! 927: } ! 928: ! 929: /* a read/write thunk is allowed to destroy the d and c registers, ! 930: which must be the two host registers used for the subs first ! 931: source operand. the d register must be a true host register, and ! 932: the c register must not be a true host register. if this isn't a ! 933: double-host-size guest, we must free the d register. we must ! 934: always mark the c register as destroyed: */ ! 935: #if (TME_RECODE_X86_REG_HOST_SUBS_SRC0 + 1) != TME_RECODE_REG_HOST_UNDEF ! 936: #error "TME_RECODE_X86_REG_HOST_ values changed" ! 937: #endif ! 938: assert (tme_recode_x86_reg_from_host[TME_RECODE_X86_REG_HOST_SUBS_SRC0 + 0] == TME_RECODE_X86_REG_D); ! 939: assert (tme_recode_x86_reg_from_host[TME_RECODE_X86_REG_HOST_SUBS_SRC0 + 1] == TME_RECODE_X86_REG_C); ! 940: if (!TME_RECODE_SIZE_IS_DOUBLE_HOST(ic->tme_recode_ic_reg_size)) { ! 941: tme_recode_regs_host_free_specific(ic, ! 942: TME_RECODE_X86_REG_HOST_SUBS_SRC0); ! 943: } ! 944: ic->tme_recode_x86_ic_thunks_reg_guest_window_c = TME_RECODE_REG_GUEST_WINDOW_UNDEF; ! 945: ! 946: /* start another instruction: */ ! 947: tme_recode_x86_insns_start(ic, thunk_bytes); ! 948: ! 949: /* emit the call to the instruction's subs: */ ! 950: thunk_bytes[0] = TME_RECODE_X86_OPCODE_CALL_RELz; ! 951: thunk_bytes += 1 + sizeof(tme_uint32_t); ! 952: ((tme_uint32_t *) thunk_bytes)[-1] ! 953: = (rw_thunk->tme_recode_x86_rw_thunk_subs ! 954: - tme_recode_build_to_thunk_off(ic, thunk_bytes)); ! 955: ! 956: /* add any zero- or sign-extension instruction: */ ! 957: *((tme_uint32_t *) thunk_bytes) = rw_thunk->tme_recode_x86_rw_thunk_extend; ! 958: thunk_bytes += rw_thunk->tme_recode_x86_rw_thunk_extend_size; ! 959: ! 960: /* finish this instruction: */ ! 961: tme_recode_x86_insns_finish(ic, thunk_bytes); ! 962: } ! 963: ! 964: /* this recodes one instruction: */ ! 965: static void ! 966: _tme_recode_x86_insn_emit(struct tme_recode_ic *ic, ! 967: struct tme_recode_insn *insn) ! 968: { ! 969: tme_uint32_t opcode_mask; ! 970: const struct tme_recode_flags_thunk *flags_thunk; ! 971: unsigned int size_saved; ! 972: signed int operand_src; ! 973: tme_uint8_t *thunk_bytes; ! 974: unsigned int reg_x86_src; ! 975: unsigned int reg_x86_dst; ! 976: unsigned int rex; ! 977: unsigned int mod_reg_rm; ! 978: tme_uint8_t *thunk_bytes_other; ! 979: unsigned int window_i; ! 980: unsigned long relv; ! 981: ! 982: /* get the mask for this opcode: */ ! 983: opcode_mask = (1 << insn->tme_recode_insn_opcode); ! 984: ! 985: /* if this is an integer opcode: */ ! 986: if (__tme_predict_true(opcode_mask ! 987: & ((1 << TME_RECODE_OPCODES_INTEGER) - 1))) { ! 988: ! 989: /* if this instruction has a flags thunk: */ ! 990: flags_thunk = insn->tme_recode_insn_flags_thunk; ! 991: if (__tme_predict_false(flags_thunk != NULL)) { ! 992: ! 993: /* if this is a zero- or sign-extension: */ ! 994: if (__tme_predict_false(opcode_mask ! 995: & ((1 << TME_RECODE_OPCODE_EXTZ) ! 996: | (1 << TME_RECODE_OPCODE_EXTS)))) { ! 997: ! 998: /* save the instruction size: */ ! 999: size_saved = insn->tme_recode_insn_size; ! 1000: ! 1001: /* temporarily set the instruction size to the size of the ! 1002: source operand: */ ! 1003: insn->tme_recode_insn_size = insn->tme_recode_insn_operand_src[1]; ! 1004: ! 1005: /* set the condition code subs for this opcode at this size and ! 1006: source operand size: */ ! 1007: #if (TME_RECODE_OPCODE_EXTS - TME_RECODE_OPCODE_EXTZ) != 1 ! 1008: #error "TME_RECODE_OPCODE_ values changed" ! 1009: #endif ! 1010: insn->tme_recode_x86_insn_subs_thunk_off ! 1011: = (flags_thunk->tme_recode_x86_flags_thunk_sizes ! 1012: [size_saved - TME_RECODE_SIZE_8] ! 1013: .tme_recode_x86_flags_thunk_size_subs_ext ! 1014: [insn->tme_recode_insn_size - TME_RECODE_SIZE_8] ! 1015: [insn->tme_recode_insn_opcode - TME_RECODE_OPCODE_EXTZ]); ! 1016: ! 1017: /* load the first source operand into the host register for a ! 1018: subs first source operand and destination: */ ! 1019: tme_recode_regs_src_specific(ic, ! 1020: insn, ! 1021: insn->tme_recode_insn_operand_src[0], ! 1022: TME_RECODE_X86_REG_HOST_SUBS_DST); ! 1023: ! 1024: /* restore the instruction size: */ ! 1025: insn->tme_recode_insn_size = size_saved; ! 1026: } ! 1027: ! 1028: /* otherwise, this is not a zero- or sign-extension: */ ! 1029: else { ! 1030: ! 1031: /* set the condition code subs for this opcode at this size: */ ! 1032: insn->tme_recode_x86_insn_subs_thunk_off ! 1033: = (flags_thunk->tme_recode_x86_flags_thunk_sizes ! 1034: [insn->tme_recode_insn_size - TME_RECODE_SIZE_8] ! 1035: .tme_recode_x86_flags_thunk_size_subs[insn->tme_recode_insn_opcode]); ! 1036: ! 1037: /* load the first source operand into the host register for a ! 1038: subs first source operand and destination: */ ! 1039: tme_recode_regs_src_specific(ic, ! 1040: insn, ! 1041: insn->tme_recode_insn_operand_src[0], ! 1042: TME_RECODE_X86_REG_HOST_SUBS_DST); ! 1043: ! 1044: /* load the second source operand into the host register for a ! 1045: subs second source operand: */ ! 1046: tme_recode_regs_src_specific(ic, ! 1047: insn, ! 1048: insn->tme_recode_insn_operand_src[1], ! 1049: TME_RECODE_X86_REG_HOST_SUBS_SRC1); ! 1050: } ! 1051: ! 1052: /* if this is an x86-64 host, and this condition code thunk calls ! 1053: a guest function: */ ! 1054: if (TME_RECODE_SIZE_HOST > TME_RECODE_SIZE_32 ! 1055: && flags_thunk->tme_recode_x86_flags_thunk_has_guest_func) { ! 1056: ! 1057: /* we need to free all host registers that can be destroyed by ! 1058: a C function. the first such register must be the a ! 1059: register, and this is freed by _tme_recode_x86_insn_subs(), ! 1060: so we only need to free the others: */ ! 1061: assert (tme_recode_x86_reg_from_host[TME_RECODE_X86_REG_HOST_FREE_CALL] == TME_RECODE_X86_REG_A); ! 1062: #if (TME_RECODE_X86_REG_HOST_FREE_CALL & 1) != 1 ! 1063: #error "TME_RECODE_X86_REG_HOST_ values changed" ! 1064: #endif ! 1065: tme_recode_regs_host_free_many(ic, TME_RECODE_X86_REG_HOST_FREE_CALL + 1); ! 1066: } ! 1067: ! 1068: /* emit a call to the condition code subs: */ ! 1069: _tme_recode_x86_insn_subs(ic, insn); ! 1070: return; ! 1071: } ! 1072: } ! 1073: ! 1074: /* if the first source operand is zero: */ ! 1075: operand_src = insn->tme_recode_insn_operand_src[0]; ! 1076: if (operand_src == TME_RECODE_OPERAND_ZERO) { ! 1077: ! 1078: /* if this opcode is a copy or a negation when the first source ! 1079: operand is zero: */ ! 1080: if (opcode_mask ! 1081: & ((1 << TME_RECODE_OPCODE_ORN) ! 1082: | (1 << TME_RECODE_OPCODE_XORN) ! 1083: | (1 << TME_RECODE_OPCODE_SUB) ! 1084: | (1 << TME_RECODE_OPCODE_OR) ! 1085: | (1 << TME_RECODE_OPCODE_XOR) ! 1086: | (1 << TME_RECODE_OPCODE_ADD))) { ! 1087: ! 1088: /* load the second source operand and copy it into the ! 1089: destination operand: */ ! 1090: tme_recode_regs_src_any(ic, ! 1091: insn, ! 1092: insn->tme_recode_insn_operand_src[1]); ! 1093: insn->tme_recode_insn_operand_dst ! 1094: = tme_recode_regs_dst_specific(ic, ! 1095: insn, ! 1096: 0); ! 1097: ! 1098: /* if this opcode is a negation: */ ! 1099: if (opcode_mask ! 1100: & ((1 << TME_RECODE_OPCODE_ORN) ! 1101: | (1 << TME_RECODE_OPCODE_XORN) ! 1102: | (1 << TME_RECODE_OPCODE_SUB))) { ! 1103: ! 1104: /* negate the destination operand: */ ! 1105: _tme_recode_x86_insn_negate(ic, insn); ! 1106: } ! 1107: ! 1108: /* done: */ ! 1109: return; ! 1110: } ! 1111: } ! 1112: ! 1113: /* otherwise, the first source operand is not zero: */ ! 1114: ! 1115: /* if the first source operand is a guest register: */ ! 1116: else if (operand_src >= TME_RECODE_REG_GUEST(0)) { ! 1117: ! 1118: /* if this opcode makes a zero when the two source operands are ! 1119: the same guest register, and the two source operands are the ! 1120: same guest register: */ ! 1121: if ((opcode_mask ! 1122: & ((1 << TME_RECODE_OPCODE_SUB) ! 1123: | (1 << TME_RECODE_OPCODE_XOR))) ! 1124: && (operand_src ! 1125: == insn->tme_recode_insn_operand_src[1])) { ! 1126: ! 1127: /* zero the destination operand: */ ! 1128: tme_recode_host_reg_zero(ic, ! 1129: NULL, ! 1130: tme_recode_regs_dst_any(ic, ! 1131: insn)); ! 1132: return; ! 1133: } ! 1134: } ! 1135: ! 1136: /* if this is a binary operation: */ ! 1137: if (opcode_mask ! 1138: & ((1 << TME_RECODE_OPCODE_ANDN) ! 1139: | (1 << TME_RECODE_OPCODE_ORN) ! 1140: | (1 << TME_RECODE_OPCODE_XORN) ! 1141: | (1 << TME_RECODE_OPCODE_SUB) ! 1142: | (1 << TME_RECODE_OPCODE_SUBC) ! 1143: | (1 << TME_RECODE_OPCODE_AND) ! 1144: | (1 << TME_RECODE_OPCODE_OR) ! 1145: | (1 << TME_RECODE_OPCODE_XOR) ! 1146: | (1 << TME_RECODE_OPCODE_ADD) ! 1147: | (1 << TME_RECODE_OPCODE_ADDC))) { ! 1148: ! 1149: /* if the right source operand is an immediate that we can emit ! 1150: directly in the instruction(s): */ ! 1151: /* NB: on an ia32 host, we can emit all immediates directly in the ! 1152: instruction(s), even for a double-host-size instruction. on an ! 1153: x86-64 host, we can only emit an immediate whose one or two ! 1154: host-sized parts can be sign-extended from 32-bit parts: */ ! 1155: if (insn->tme_recode_insn_operand_src[1] == TME_RECODE_OPERAND_IMM ! 1156: && (TME_RECODE_SIZE_HOST == TME_RECODE_SIZE_32 ! 1157: || ((((signed long) insn->tme_recode_insn_imm_guest) ! 1158: == (tme_int32_t) insn->tme_recode_insn_imm_guest) ! 1159: && (!TME_RECODE_SIZE_IS_DOUBLE_HOST(insn->tme_recode_insn_size) ! 1160: || (((signed long) ! 1161: (insn->tme_recode_insn_imm_guest ! 1162: >> ((sizeof(insn->tme_recode_insn_imm_guest) * 8) / 2))) ! 1163: == ((tme_int32_t) ! 1164: (insn->tme_recode_insn_imm_guest ! 1165: >> ((sizeof(insn->tme_recode_insn_imm_guest) * 8) / 2)))))))) { ! 1166: ! 1167: /* if this is a logical noncommutative instruction: */ ! 1168: if (opcode_mask ! 1169: & ((1 << TME_RECODE_OPCODE_ANDN) ! 1170: | (1 << TME_RECODE_OPCODE_ORN) ! 1171: | (1 << TME_RECODE_OPCODE_XORN))) { ! 1172: ! 1173: /* invert the immediate: */ ! 1174: insn->tme_recode_insn_imm_uguest = ~insn->tme_recode_insn_imm_uguest; ! 1175: ! 1176: /* convert the opcode into its commutative version: */ ! 1177: /* NB: we don't need to change opcode_mask, since the ! 1178: only remaining tests are for additive operations: */ ! 1179: insn->tme_recode_insn_opcode = TME_RECODE_OPCODE_LOGICALN_TO_PLAIN(insn->tme_recode_insn_opcode); ! 1180: } ! 1181: ! 1182: /* load the left source operand: */ ! 1183: insn->tme_recode_insn_operand_src[0] ! 1184: = tme_recode_regs_src_any(ic, ! 1185: insn, ! 1186: insn->tme_recode_insn_operand_src[0]); ! 1187: ! 1188: /* copy the left source operand into the destination operand: */ ! 1189: insn->tme_recode_insn_operand_dst ! 1190: = tme_recode_regs_dst_specific(ic, ! 1191: insn, ! 1192: 0); ! 1193: ! 1194: /* if this is an additive operation with the carry: */ ! 1195: if (opcode_mask ! 1196: & ((1 << TME_RECODE_OPCODE_ADDC) ! 1197: | (1 << TME_RECODE_OPCODE_SUBC))) { ! 1198: ! 1199: /* emit the instruction to define CF with the recode carry flag: */ ! 1200: _tme_recode_x86_conds_testc(ic, 0); ! 1201: } ! 1202: ! 1203: /* if this is a double-host-size instruction: */ ! 1204: if (TME_RECODE_SIZE_IS_DOUBLE_HOST(insn->tme_recode_insn_size)) { ! 1205: ! 1206: /* emit the low-half host-size instruction: */ ! 1207: _tme_recode_x86_insn_binop_imm(ic, insn); ! 1208: ! 1209: /* advance to the high halves of the immediate and ! 1210: destination: */ ! 1211: insn->tme_recode_insn_imm_uguest >>= (sizeof(insn->tme_recode_insn_imm_uguest) * 8) / 2; ! 1212: insn->tme_recode_insn_operand_dst++; ! 1213: ! 1214: /* a high-half additive operation must use the carry: */ ! 1215: if (opcode_mask ! 1216: & ((1 << TME_RECODE_OPCODE_ADD) ! 1217: | (1 << TME_RECODE_OPCODE_SUB))) { ! 1218: #if (TME_RECODE_OPCODE_SUBC - TME_RECODE_OPCODE_SUB) != (TME_RECODE_OPCODE_ADDC - TME_RECODE_OPCODE_ADD) ! 1219: #error "TME_RECODE_OPCODE_ values changed" ! 1220: #endif ! 1221: insn->tme_recode_insn_opcode += (TME_RECODE_OPCODE_SUBC - TME_RECODE_OPCODE_SUB); ! 1222: } ! 1223: } ! 1224: ! 1225: /* emit the host-size instruction: */ ! 1226: _tme_recode_x86_insn_binop_imm(ic, insn); ! 1227: return; ! 1228: } ! 1229: ! 1230: /* if the second source operand is the same as the destination operand, ! 1231: and this operation is commutative: */ ! 1232: operand_src = insn->tme_recode_insn_operand_src[1]; ! 1233: if (operand_src == insn->tme_recode_insn_operand_dst ! 1234: && (opcode_mask ! 1235: & ((1 << TME_RECODE_OPCODE_AND) ! 1236: | (1 << TME_RECODE_OPCODE_OR) ! 1237: | (1 << TME_RECODE_OPCODE_XOR) ! 1238: | (1 << TME_RECODE_OPCODE_ADD) ! 1239: | (1 << TME_RECODE_OPCODE_ADDC)))) { ! 1240: ! 1241: /* switch the first and second source operands: */ ! 1242: insn->tme_recode_insn_operand_src[1] = insn->tme_recode_insn_operand_src[0]; ! 1243: insn->tme_recode_insn_operand_src[0] = operand_src; ! 1244: } ! 1245: ! 1246: /* load the left and right source operands: */ ! 1247: insn->tme_recode_insn_operand_src[0] ! 1248: = tme_recode_regs_src_any(ic, ! 1249: insn, ! 1250: insn->tme_recode_insn_operand_src[0]); ! 1251: insn->tme_recode_insn_operand_src[1] ! 1252: = tme_recode_regs_src_any(ic, ! 1253: insn, ! 1254: insn->tme_recode_insn_operand_src[1]); ! 1255: ! 1256: /* if this is a logical noncommutative instruction: */ ! 1257: if (opcode_mask ! 1258: & ((1 << TME_RECODE_OPCODE_ANDN) ! 1259: | (1 << TME_RECODE_OPCODE_ORN) ! 1260: | (1 << TME_RECODE_OPCODE_XORN))) { ! 1261: ! 1262: /* copy the right source operand into the destination operand: */ ! 1263: insn->tme_recode_insn_operand_dst ! 1264: = tme_recode_regs_dst_specific(ic, ! 1265: insn, ! 1266: 1); ! 1267: ! 1268: /* negate the destination operand: */ ! 1269: _tme_recode_x86_insn_negate(ic, insn); ! 1270: ! 1271: /* move the left source operand into the right source operand: */ ! 1272: insn->tme_recode_insn_operand_src[1] = insn->tme_recode_insn_operand_src[0]; ! 1273: ! 1274: /* convert the opcode into its commutative version: */ ! 1275: /* NB: we don't need to change opcode_mask, since the ! 1276: only remaining tests are for additive operations: */ ! 1277: insn->tme_recode_insn_opcode = TME_RECODE_OPCODE_LOGICALN_TO_PLAIN(insn->tme_recode_insn_opcode); ! 1278: } ! 1279: ! 1280: /* otherwise, this is not a logical noncommutative instruction: */ ! 1281: else { ! 1282: ! 1283: /* copy the left source operand into the destination operand: */ ! 1284: insn->tme_recode_insn_operand_dst ! 1285: = tme_recode_regs_dst_specific(ic, ! 1286: insn, ! 1287: 0); ! 1288: ! 1289: /* if this is an additive operation with the carry: */ ! 1290: if (opcode_mask ! 1291: & ((1 << TME_RECODE_OPCODE_ADDC) ! 1292: | (1 << TME_RECODE_OPCODE_SUBC))) { ! 1293: ! 1294: /* emit the instruction to define CF with the recode carry flag: */ ! 1295: _tme_recode_x86_conds_testc(ic, 0); ! 1296: } ! 1297: } ! 1298: ! 1299: /* if this is a double-host-size instruction: */ ! 1300: if (TME_RECODE_SIZE_IS_DOUBLE_HOST(insn->tme_recode_insn_size)) { ! 1301: ! 1302: /* emit the low-half host-size instruction: */ ! 1303: _tme_recode_x86_insn_binop(ic, insn); ! 1304: ! 1305: /* advance to the high halves of the source and destination: */ ! 1306: insn->tme_recode_insn_operand_src[1]++; ! 1307: insn->tme_recode_insn_operand_dst++; ! 1308: ! 1309: /* a high-half additive operation must use the carry: */ ! 1310: if (opcode_mask ! 1311: & ((1 << TME_RECODE_OPCODE_ADD) ! 1312: | (1 << TME_RECODE_OPCODE_SUB))) { ! 1313: #if (TME_RECODE_OPCODE_SUBC - TME_RECODE_OPCODE_SUB) != (TME_RECODE_OPCODE_ADDC - TME_RECODE_OPCODE_ADD) ! 1314: #error "TME_RECODE_OPCODE_ values changed" ! 1315: #endif ! 1316: insn->tme_recode_insn_opcode += (TME_RECODE_OPCODE_SUBC - TME_RECODE_OPCODE_SUB); ! 1317: } ! 1318: } ! 1319: ! 1320: /* emit the host-size instruction: */ ! 1321: _tme_recode_x86_insn_binop(ic, insn); ! 1322: return; ! 1323: } ! 1324: ! 1325: /* if this is a shift: */ ! 1326: if (opcode_mask ! 1327: & ((1 << TME_RECODE_OPCODE_SHRA) ! 1328: | (1 << TME_RECODE_OPCODE_SHRL) ! 1329: | (1 << TME_RECODE_OPCODE_SHLL))) { ! 1330: ! 1331: /* if the shift count is an immediate, and this isn't a ! 1332: double-host-size instruction, and the shift count is less than ! 1333: the instruction size: */ ! 1334: if (insn->tme_recode_insn_operand_src[1] == TME_RECODE_OPERAND_IMM ! 1335: && !TME_RECODE_SIZE_IS_DOUBLE_HOST(insn->tme_recode_insn_size) ! 1336: && insn->tme_recode_insn_imm_uguest < TME_BIT(insn->tme_recode_insn_size)) { ! 1337: ! 1338: /* load the source operand: */ ! 1339: tme_recode_regs_src_any(ic, ! 1340: insn, ! 1341: insn->tme_recode_insn_operand_src[0]); ! 1342: ! 1343: /* copy the source operand into the destination operand: */ ! 1344: insn->tme_recode_insn_operand_dst ! 1345: = tme_recode_regs_dst_specific(ic, ! 1346: insn, ! 1347: 0); ! 1348: ! 1349: /* if this is less than a host-size instruction: */ ! 1350: if (__tme_predict_false(insn->tme_recode_insn_size < TME_RECODE_SIZE_HOST)) { ! 1351: ! 1352: /* if this is a right shift: */ ! 1353: if (opcode_mask != (1 << TME_RECODE_OPCODE_SHLL)) { ! 1354: ! 1355: /* for a right shift, we have to make sure that the correct ! 1356: bits get shifted in on the right, so we have to first ! 1357: extend the value in the destination register to host ! 1358: size. for an arithmetic shift, we have to do a sign ! 1359: extension, and for a logical shift we have to do a zero ! 1360: extension: */ ! 1361: ! 1362: /* map TME_RECODE_OPCODE_SHRL to TME_RECODE_OPCODE_EXTZ, and ! 1363: TME_RECODE_OPCODE_SHRA to TME_RECODE_OPCODE_EXTS: */ ! 1364: #if (TME_RECODE_OPCODE_SHRA - TME_RECODE_OPCODE_SHRL) != (TME_RECODE_OPCODE_EXTS - TME_RECODE_OPCODE_EXTZ) ! 1365: #error "TME_RECODE_OPCODE_ values changed" ! 1366: #endif ! 1367: insn->tme_recode_insn_opcode ! 1368: = ((insn->tme_recode_insn_opcode ! 1369: - TME_RECODE_OPCODE_SHRL) ! 1370: + TME_RECODE_OPCODE_EXTZ); ! 1371: ! 1372: /* zero- or sign-extend the destination operand: */ ! 1373: insn->tme_recode_insn_operand_src[0] = insn->tme_recode_insn_operand_dst; ! 1374: insn->tme_recode_insn_operand_src[1] = insn->tme_recode_insn_size; ! 1375: _tme_recode_x86_insn_ext(ic, insn); ! 1376: ! 1377: /* restore the opcode: */ ! 1378: #if (TME_RECODE_OPCODE_SHRA - TME_RECODE_OPCODE_SHRL) != (TME_RECODE_OPCODE_EXTS - TME_RECODE_OPCODE_EXTZ) ! 1379: #error "TME_RECODE_OPCODE_ values changed" ! 1380: #endif ! 1381: insn->tme_recode_insn_opcode ! 1382: = ((insn->tme_recode_insn_opcode ! 1383: - TME_RECODE_OPCODE_EXTZ) ! 1384: + TME_RECODE_OPCODE_SHRL); ! 1385: } ! 1386: } ! 1387: ! 1388: /* emit the host-size instruction: */ ! 1389: _tme_recode_x86_insn_shift_imm(ic, insn); ! 1390: return; ! 1391: } ! 1392: ! 1393: /* load the source operand into the host register for a subs first ! 1394: source operand and destination: */ ! 1395: tme_recode_regs_src_specific(ic, ! 1396: insn, ! 1397: insn->tme_recode_insn_operand_src[0], ! 1398: TME_RECODE_X86_REG_HOST_SUBS_DST); ! 1399: ! 1400: /* load the shift count into the host register for a subs second ! 1401: source operand: */ ! 1402: tme_recode_regs_src_specific(ic, ! 1403: insn, ! 1404: insn->tme_recode_insn_operand_src[1], ! 1405: TME_RECODE_X86_REG_HOST_SUBS_SRC1); ! 1406: ! 1407: /* emit a call to the subs for this particular shift: */ ! 1408: #if (TME_RECODE_OPCODE_SHLL + 1) != TME_RECODE_OPCODE_SHRL || (TME_RECODE_OPCODE_SHRL + 1) != TME_RECODE_OPCODE_SHRA ! 1409: #error "TME_RECODE_OPCODE_ values changed" ! 1410: #endif ! 1411: insn->tme_recode_x86_insn_subs_thunk_off ! 1412: = (ic->tme_recode_x86_ic_subs_shift ! 1413: [insn->tme_recode_insn_size - TME_RECODE_SIZE_8] ! 1414: [insn->tme_recode_insn_opcode - TME_RECODE_OPCODE_SHLL]); ! 1415: _tme_recode_x86_insn_subs(ic, insn); ! 1416: return; ! 1417: } ! 1418: ! 1419: /* if this is a zero- or sign-extension: */ ! 1420: else if (opcode_mask ! 1421: & ((1 << TME_RECODE_OPCODE_EXTZ) ! 1422: | (1 << TME_RECODE_OPCODE_EXTS))) { ! 1423: ! 1424: /* save the instruction size: */ ! 1425: size_saved = insn->tme_recode_insn_size; ! 1426: ! 1427: /* temporarily set the instruction size to the size of the ! 1428: source operand: */ ! 1429: insn->tme_recode_insn_size = insn->tme_recode_insn_operand_src[1]; ! 1430: ! 1431: /* load the source operand: */ ! 1432: insn->tme_recode_insn_operand_src[0] ! 1433: = tme_recode_regs_src_any(ic, ! 1434: insn, ! 1435: insn->tme_recode_insn_operand_src[0]); ! 1436: ! 1437: /* restore the instruction size: */ ! 1438: insn->tme_recode_insn_size = size_saved; ! 1439: ! 1440: /* load the destination operand: */ ! 1441: insn->tme_recode_insn_operand_dst ! 1442: = tme_recode_regs_dst_any(ic, ! 1443: insn); ! 1444: ! 1445: /* if this can't be a double-host-size instruction, or if this is ! 1446: not an extension from the host size: */ ! 1447: if (TME_RECODE_SIZE_GUEST_MAX <= TME_RECODE_SIZE_HOST ! 1448: || insn->tme_recode_insn_operand_src[1] < TME_RECODE_SIZE_HOST) { ! 1449: ! 1450: /* emit the extension to host size: */ ! 1451: _tme_recode_x86_insn_ext(ic, insn); ! 1452: } ! 1453: ! 1454: /* if this is a double-host-size instruction: */ ! 1455: if (TME_RECODE_SIZE_IS_DOUBLE_HOST(insn->tme_recode_insn_size)) { ! 1456: ! 1457: /* start more instructions: */ ! 1458: tme_recode_x86_insns_start(ic, thunk_bytes); ! 1459: ! 1460: /* get the x86 source register: */ ! 1461: reg_x86_src = tme_recode_x86_reg_from_host[insn->tme_recode_insn_operand_src[0] + 0]; ! 1462: ! 1463: /* if this is an extension from host size: */ ! 1464: if (insn->tme_recode_insn_operand_src[1] == TME_RECODE_SIZE_HOST) { ! 1465: ! 1466: /* get the x86 destination register: */ ! 1467: reg_x86_dst = tme_recode_x86_reg_from_host[insn->tme_recode_insn_operand_dst + 0]; ! 1468: ! 1469: /* unless the host registers are the same, copy the least ! 1470: significant half of the source operand into the least ! 1471: significant half of the destination operand: */ ! 1472: if (reg_x86_src != reg_x86_dst) { ! 1473: _tme_recode_x86_emit_reg_copy(thunk_bytes, reg_x86_src, reg_x86_dst); ! 1474: } ! 1475: } ! 1476: ! 1477: /* get the x86 destination register: */ ! 1478: reg_x86_dst = tme_recode_x86_reg_from_host[insn->tme_recode_insn_operand_dst + 1]; ! 1479: ! 1480: /* if this is a sign-extension: */ ! 1481: if (opcode_mask == (1 << TME_RECODE_OPCODE_EXTS)) { ! 1482: ! 1483: /* emit a mov %src, %dst; add %src, %dst: */ ! 1484: if (TME_RECODE_SIZE_HOST > TME_RECODE_SIZE_32) { ! 1485: rex = (TME_RECODE_X86_REX_B(TME_RECODE_SIZE_HOST, reg_x86_src) ! 1486: | TME_RECODE_X86_REX_R(TME_RECODE_SIZE_HOST, reg_x86_dst)); ! 1487: thunk_bytes[0] = rex; ! 1488: thunk_bytes[3] = rex; ! 1489: } ! 1490: thunk_bytes[(TME_RECODE_SIZE_HOST > TME_RECODE_SIZE_32) + 0] ! 1491: = (TME_RECODE_X86_OPCODE_BINOP_MOV ! 1492: + TME_RECODE_X86_OPCODE_BINOP_Ev_Gv); ! 1493: thunk_bytes[(TME_RECODE_SIZE_HOST > TME_RECODE_SIZE_32) + 2 ! 1494: + (TME_RECODE_SIZE_HOST > TME_RECODE_SIZE_32) + 0] ! 1495: = (TME_RECODE_X86_OPCODE_BINOP_ADD ! 1496: + TME_RECODE_X86_OPCODE_BINOP_Ev_Gv); ! 1497: mod_reg_rm ! 1498: = TME_RECODE_X86_MOD_OPREG_RM(TME_RECODE_X86_MOD_RM_REG(reg_x86_src), ! 1499: TME_RECODE_X86_REG(reg_x86_dst)); ! 1500: thunk_bytes[(TME_RECODE_SIZE_HOST > TME_RECODE_SIZE_32) + 1] = mod_reg_rm; ! 1501: thunk_bytes[(TME_RECODE_SIZE_HOST > TME_RECODE_SIZE_32) + 2 ! 1502: + (TME_RECODE_SIZE_HOST > TME_RECODE_SIZE_32) + 1] = mod_reg_rm; ! 1503: thunk_bytes += ((TME_RECODE_SIZE_HOST > TME_RECODE_SIZE_32) + 2) * 2; ! 1504: } ! 1505: ! 1506: /* emit a sbb %dst, %dst if this is a sign-extension, ! 1507: or a sub %dst, %dst if this is a zero-extension: */ ! 1508: if (TME_RECODE_SIZE_HOST > TME_RECODE_SIZE_32) { ! 1509: thunk_bytes[0] ! 1510: = (TME_RECODE_X86_REX_B(TME_RECODE_SIZE_HOST, reg_x86_dst) ! 1511: | TME_RECODE_X86_REX_R(TME_RECODE_SIZE_HOST, reg_x86_dst)); ! 1512: } ! 1513: #if (TME_RECODE_OPCODE_EXTS - TME_RECODE_OPCODE_EXTZ) != 1 ! 1514: #error "TME_RECODE_OPCODE_ values changed" ! 1515: #endif ! 1516: thunk_bytes[(TME_RECODE_SIZE_HOST > TME_RECODE_SIZE_32) + 0] ! 1517: = (TME_RECODE_X86_OPCODE_BINOP_SUB ! 1518: + TME_RECODE_X86_OPCODE_BINOP_Ev_Gv ! 1519: - ((insn->tme_recode_insn_opcode ! 1520: - TME_RECODE_OPCODE_EXTZ) ! 1521: * (TME_RECODE_X86_OPCODE_BINOP_SUB ! 1522: - TME_RECODE_X86_OPCODE_BINOP_SBB))); ! 1523: thunk_bytes[(TME_RECODE_SIZE_HOST > TME_RECODE_SIZE_32) + 1] ! 1524: = TME_RECODE_X86_MOD_OPREG_RM(TME_RECODE_X86_MOD_RM_REG(reg_x86_dst), ! 1525: TME_RECODE_X86_REG(reg_x86_dst)); ! 1526: thunk_bytes += (TME_RECODE_SIZE_HOST > TME_RECODE_SIZE_32) + 2; ! 1527: ! 1528: /* finish these instructions: */ ! 1529: tme_recode_x86_insns_finish(ic, thunk_bytes); ! 1530: } ! 1531: return; ! 1532: } ! 1533: ! 1534: /* if this is a guest opcode: */ ! 1535: else if (opcode_mask == (1 << TME_RECODE_OPCODE_GUEST)) { ! 1536: ! 1537: /* emit the call to the guest function: */ ! 1538: _tme_recode_x86_insn_guest(ic, insn); ! 1539: return; ! 1540: } ! 1541: ! 1542: /* if this is a define-recode-carry opcode: */ ! 1543: else if (opcode_mask == (1 << TME_RECODE_OPCODE_DEFC)) { ! 1544: ! 1545: /* emit the carry define: */ ! 1546: _tme_recode_x86_conds_defc(ic, insn); ! 1547: return; ! 1548: } ! 1549: ! 1550: /* if this is an if opcode: */ ! 1551: else if (opcode_mask == (1 << TME_RECODE_OPCODE_IF)) { ! 1552: ! 1553: /* clean all dirty host registers before we begin the if: */ ! 1554: tme_recode_regs_host_clean_all(ic); ! 1555: ! 1556: /* emit the instruction to define CF with the desired recode flag: */ ! 1557: _tme_recode_x86_conds_testc(ic, insn->tme_recode_insn_operand_src[0]); ! 1558: ! 1559: /* start more instructions: */ ! 1560: tme_recode_x86_insns_start(ic, thunk_bytes); ! 1561: ! 1562: /* ifs can't be nested: */ ! 1563: assert (ic->tme_recode_x86_ic_thunks_build_if == NULL ! 1564: && ic->tme_recode_x86_ic_thunks_build_else == NULL); ! 1565: ! 1566: /* save the address of this if: */ ! 1567: ic->tme_recode_x86_ic_thunks_build_if = thunk_bytes; ! 1568: ! 1569: /* a 32-bit displacement jnc instruction requires six bytes: */ ! 1570: thunk_bytes += 2 + sizeof(tme_uint32_t); ! 1571: ! 1572: /* save any c register window index at the if jnc: */ ! 1573: ic->tme_recode_x86_ic_thunks_reg_guest_window_c_if_jmp ! 1574: = ic->tme_recode_x86_ic_thunks_reg_guest_window_c; ! 1575: ! 1576: /* finish these instructions: */ ! 1577: tme_recode_x86_insns_finish(ic, thunk_bytes); ! 1578: } ! 1579: ! 1580: /* if this is an else or an endif opcode: */ ! 1581: else if (opcode_mask ! 1582: & ((1 << TME_RECODE_OPCODE_ELSE) ! 1583: | (1 << TME_RECODE_OPCODE_ENDIF))) { ! 1584: ! 1585: /* free all host registers: */ ! 1586: tme_recode_regs_host_free_many(ic, TME_RECODE_REG_HOST(0)); ! 1587: ! 1588: /* start more instructions: */ ! 1589: tme_recode_x86_insns_start(ic, thunk_bytes); ! 1590: ! 1591: /* if we are in an if: */ ! 1592: thunk_bytes_other = ic->tme_recode_x86_ic_thunks_build_if; ! 1593: if (thunk_bytes_other != NULL) { ! 1594: ! 1595: /* if this is an else: */ ! 1596: if (opcode_mask == (1 << TME_RECODE_OPCODE_ELSE)) { ! 1597: ! 1598: /* save the address of this else: */ ! 1599: ic->tme_recode_x86_ic_thunks_build_else = thunk_bytes; ! 1600: ! 1601: /* a 32-bit displacement jmp instruction requires five bytes: */ ! 1602: thunk_bytes += 1 + sizeof(tme_uint32_t); ! 1603: ! 1604: /* switch any c register window index at the if jnc with any ! 1605: window index at the end of the if body: */ ! 1606: window_i = ic->tme_recode_x86_ic_thunks_reg_guest_window_c; ! 1607: ic->tme_recode_x86_ic_thunks_reg_guest_window_c ! 1608: = ic->tme_recode_x86_ic_thunks_reg_guest_window_c_if_jmp; ! 1609: ic->tme_recode_x86_ic_thunks_reg_guest_window_c_if_jmp = window_i; ! 1610: ! 1611: /* finish these instructions: */ ! 1612: tme_recode_x86_insns_finish(ic, thunk_bytes); ! 1613: } ! 1614: ! 1615: /* emit the jnc instruction at the if: */ ! 1616: relv = thunk_bytes - (thunk_bytes_other + 1 + sizeof(tme_int8_t)); ! 1617: if (relv < 0x7f) { ! 1618: thunk_bytes_other[0] = TME_RECODE_X86_OPCODE_JCC(TME_RECODE_X86_COND_NOT | TME_RECODE_X86_COND_C); ! 1619: thunk_bytes_other[1] = relv; ! 1620: *((tme_uint32_t *) &thunk_bytes_other[2]) = TME_RECODE_X86_NOP4; ! 1621: } ! 1622: else { ! 1623: relv = thunk_bytes - (thunk_bytes_other + 2 + sizeof(tme_int32_t)); ! 1624: *((tme_uint16_t *) &thunk_bytes_other[0]) ! 1625: = (TME_RECODE_X86_OPCODE_ESC_0F ! 1626: + (TME_RECODE_X86_OPCODE0F_JCC(TME_RECODE_X86_COND_NOT | TME_RECODE_X86_COND_C) ! 1627: << 8)); ! 1628: *((tme_uint32_t *) &thunk_bytes_other[2]) = relv; ! 1629: } ! 1630: ! 1631: /* clear the if: */ ! 1632: ic->tme_recode_x86_ic_thunks_build_if = NULL; ! 1633: } ! 1634: ! 1635: /* otherwise, we must be in an else and this must be an endif: */ ! 1636: else { ! 1637: thunk_bytes_other = ic->tme_recode_x86_ic_thunks_build_else; ! 1638: assert (thunk_bytes_other != NULL ! 1639: && opcode_mask == (1 << TME_RECODE_OPCODE_ENDIF)); ! 1640: ! 1641: /* emit the jmp instruction at the else: */ ! 1642: relv = thunk_bytes - (thunk_bytes_other + 1 + sizeof(tme_int8_t)); ! 1643: if (relv < 0x7f) { ! 1644: thunk_bytes_other[0] = TME_RECODE_X86_OPCODE_JMP_RELb; ! 1645: relv += (TME_RECODE_X86_NOP3 << 8); ! 1646: } ! 1647: else { ! 1648: relv = thunk_bytes - (thunk_bytes_other + 1 + sizeof(tme_int32_t)); ! 1649: thunk_bytes_other[0] = TME_RECODE_X86_OPCODE_JMP_RELz; ! 1650: } ! 1651: *((tme_uint32_t *) &thunk_bytes_other[1]) = relv; ! 1652: } ! 1653: ! 1654: /* clear the else: */ ! 1655: ic->tme_recode_x86_ic_thunks_build_else = NULL; ! 1656: ! 1657: /* if this is an endif: */ ! 1658: if (opcode_mask == (1 << TME_RECODE_OPCODE_ENDIF)) { ! 1659: ! 1660: /* if there is no else body, and any c register window index at ! 1661: the if jnc disagrees with the window index at the end of the ! 1662: if body, or if there is an else body, and any c register ! 1663: window index at the end of the if body disagrees with any c ! 1664: register window index at the end of the else body: */ ! 1665: if (ic->tme_recode_x86_ic_thunks_reg_guest_window_c_if_jmp ! 1666: != ic->tme_recode_x86_ic_thunks_reg_guest_window_c) { ! 1667: ! 1668: /* at this point, there isn't a known window index in the c register: */ ! 1669: ic->tme_recode_x86_ic_thunks_reg_guest_window_c = TME_RECODE_REG_GUEST_WINDOW_UNDEF; ! 1670: } ! 1671: } ! 1672: } ! 1673: ! 1674: /* if this is a read or a write instruction: */ ! 1675: else if (opcode_mask == (1 << TME_RECODE_OPCODE_RW)) { ! 1676: ! 1677: /* emit the read or write instruction: */ ! 1678: _tme_recode_x86_insn_rw(ic, insn); ! 1679: } ! 1680: ! 1681: /* otherwise, this is some unknown opcode: */ ! 1682: else { ! 1683: assert(FALSE); ! 1684: } ! 1685: } ! 1686: ! 1687: /* this invalidates an instructions thunk: */ ! 1688: void ! 1689: tme_recode_insns_thunk_invalidate(struct tme_recode_ic *ic, ! 1690: tme_recode_thunk_off_t insns_thunk) ! 1691: { ! 1692: tme_uint8_t opcode_jmp_relz; ! 1693: tme_int32_t disp32; ! 1694: ! 1695: /* overwrite the first instruction of the thunk with a jmp to the ! 1696: chain epilogue: */ ! 1697: opcode_jmp_relz = TME_RECODE_X86_OPCODE_JMP_RELz; ! 1698: disp32 ! 1699: = (ic->tme_recode_x86_ic_chain_epilogue ! 1700: - (insns_thunk ! 1701: + sizeof(opcode_jmp_relz) ! 1702: + sizeof(disp32))); ! 1703: tme_recode_thunk_off_write(ic, ! 1704: insns_thunk, ! 1705: tme_uint8_t, ! 1706: opcode_jmp_relz); ! 1707: tme_recode_thunk_off_write(ic, ! 1708: (insns_thunk ! 1709: + sizeof(opcode_jmp_relz)), ! 1710: tme_int32_t, ! 1711: disp32); ! 1712: } ! 1713: ! 1714: /* this checks the value of TME_RECODE_HOST_INSN_SIZE_MAX: */ ! 1715: static void ! 1716: _tme_recode_x86_insn_size_max_check(void) ! 1717: { ! 1718: tme_uint32_t insn_size_window_offset; ! 1719: tme_uint32_t insn_size_host_store; ! 1720: tme_uint32_t insn_size_host_copy; ! 1721: tme_uint32_t insn_size_host_load; ! 1722: tme_uint32_t insn_size; ! 1723: ! 1724: /* the largest insn recode for any guest should be that guest's ! 1725: worst-case TME_RECODE_OPCODE_GUEST insn, but exactly what that ! 1726: means depends on a lot of variables that we don't want to ! 1727: consider here. ! 1728: ! 1729: instead, this calculates an upper-bound for that worst-case ! 1730: TME_RECODE_OPCODE_GUEST insn, by including all possible x86 ! 1731: instructions and x86 instruction features in a ! 1732: TME_RECODE_OPCODE_GUEST insn recode, even though it's impossible ! 1733: for all instructions and all features to be present at the same ! 1734: time: */ ! 1735: ! 1736: /* size the x86 instruction to load one window offset: */ ! 1737: insn_size_window_offset ! 1738: = ((TME_RECODE_SIZE_HOST > TME_RECODE_SIZE_32) /* TME_RECODE_X86_REX */ ! 1739: + 1 /* TME_RECODE_X86_OPCODE_MOVS_El_Gv or TME_RECODE_X86_OPCODE_BINOP_MOV */ ! 1740: + 1 /* TME_RECODE_X86_MOD_OPREG_RM(TME_RECODE_X86_MOD_RM_EA_DISP32) */ ! 1741: + sizeof(tme_int32_t)); ! 1742: ! 1743: /* size the x86 instruction(s) to store one host register at the ! 1744: worst size to a guest register window. on an x86-64 host, the ! 1745: worst size is the 16-bit size, and we simply assume that all host ! 1746: registers need a REX prefix. on an ia32, the worst size is the ! 1747: 8-bit size, and we simply assume that all host registers don't ! 1748: have an 8-bit encoding: */ ! 1749: insn_size_host_store ! 1750: = ((TME_RECODE_SIZE_HOST > TME_RECODE_SIZE_32) ! 1751: ? (1 /* TME_RECODE_X86_PREFIX_OPSIZ */ ! 1752: + 1 /* TME_RECODE_X86_REX */ ! 1753: + 1 /* TME_RECODE_X86_OPCODE_BINOP_MOV */ ! 1754: + 1 /* TME_RECODE_X86_MOD_OPREG_RM(TME_RECODE_X86_MOD_RM_EA_DISP32) */ ! 1755: + 1 /* TME_RECODE_X86_SIB */ ! 1756: + sizeof(tme_uint32_t)) ! 1757: : (1 /* TME_RECODE_X86_OPCODE_BINOP_XCHG */ ! 1758: + 1 /* TME_RECODE_X86_MOD_OPREG_RM(TME_RECODE_X86_MOD_RM_REG) */ ! 1759: + 1 /* TME_RECODE_X86_OPCODE_BINOP_MOV */ ! 1760: + 1 /* TME_RECODE_X86_MOD_OPREG_RM(TME_RECODE_X86_MOD_RM_EA_DISP32) */ ! 1761: + 1 /* TME_RECODE_X86_SIB */ ! 1762: + sizeof(tme_uint32_t) ! 1763: + 1 /* TME_RECODE_X86_OPCODE_BINOP_XCHG */ ! 1764: + 1)); /* TME_RECODE_X86_MOD_OPREG_RM(TME_RECODE_X86_MOD_RM_REG) */ ! 1765: ! 1766: /* size the x86 instruction to copy one host register to another: */ ! 1767: insn_size_host_copy ! 1768: = ((TME_RECODE_SIZE_HOST > TME_RECODE_SIZE_32) /* TME_RECODE_X86_REX */ ! 1769: + 1 /* TME_RECODE_X86_OPCODE_BINOP_MOV */ ! 1770: + 1); /* TME_RECODE_X86_MOD_OPREG_RM(TME_RECODE_X86_MOD_RM_REG) */ ! 1771: ! 1772: /* size the x86 instruction to load one host register from a guest ! 1773: register window: */ ! 1774: insn_size_host_load ! 1775: = ((TME_RECODE_SIZE_HOST > TME_RECODE_SIZE_32) /* TME_RECODE_X86_REX */ ! 1776: + 1 /* TME_RECODE_X86_OPCODE_BINOP_MOV */ ! 1777: + 1 /* TME_RECODE_X86_MOD_OPREG_RM(TME_RECODE_X86_MOD_RM_EA_DISP32) */ ! 1778: + 1 /* TME_RECODE_X86_SIB */ ! 1779: + sizeof(tme_uint32_t)); ! 1780: ! 1781: /* start with no instructions: */ ! 1782: insn_size = 0; ! 1783: ! 1784: /* add in cleaning all TME_RECODE_REG_HOST_UNDEF host registers at ! 1785: the worst size, storing them to alternating guest register ! 1786: windows (requiring the c register to be loaded with a window ! 1787: offset before each store): */ ! 1788: insn_size ! 1789: += ((insn_size_window_offset ! 1790: + insn_size_host_store) ! 1791: * TME_RECODE_REG_HOST_UNDEF); ! 1792: ! 1793: /* add in the x86 instructions to load two double-host-size guest ! 1794: windowed registers for the two source operands, moving older ! 1795: guest register values to other host registers, and loading the ! 1796: source operands from alternating guest register windows: */ ! 1797: insn_size ! 1798: += ((insn_size_host_copy ! 1799: + insn_size_window_offset ! 1800: + insn_size_host_load) ! 1801: * (2 /* double-host-size */ ! 1802: * 2)); /* two source operands */ ! 1803: ! 1804: /* add in the x86 instructions to move an older guest register ! 1805: to other host registers, for the destination operand: */ ! 1806: insn_size += (insn_size_host_copy * 2); /* double-host-size */ ! 1807: ! 1808: /* if this is an ia32 host: */ ! 1809: if (TME_RECODE_SIZE_HOST == TME_RECODE_SIZE_32) { ! 1810: ! 1811: /* add in the ia32 instructions for pushing the arguments for the ! 1812: guest function: */ ! 1813: insn_size ! 1814: += (3 /* sub $imm8, %esp (double-host-size) */ ! 1815: + 1 /* TME_RECODE_X86_OPCODE_PUSH_Gv */ ! 1816: + 1 /* TME_RECODE_X86_OPCODE_PUSH_Gv (double-host-size) */ ! 1817: + 1 /* TME_RECODE_X86_OPCODE_PUSH_Gv */ ! 1818: + 1); /* TME_RECODE_X86_OPCODE_PUSH_Gv (struct tme_ic *) */ ! 1819: ! 1820: /* add in the ia32 instructions for calling the guest function: */ ! 1821: insn_size ! 1822: += (1 /* TME_RECODE_X86_OPCODE_CALL_RELz */ ! 1823: + sizeof(tme_int32_t)); ! 1824: ! 1825: /* add in the ia32 instructions for removing the guest function ! 1826: arguments from the stack: */ ! 1827: insn_size += 3; /* sub $imm8, %esp (double-host-size) */ ! 1828: } ! 1829: ! 1830: /* otherwise, this is an x86-64 host: */ ! 1831: else { ! 1832: ! 1833: /* add in the x86-64 instructions for moving host registers into ! 1834: arguments for the guest function: */ ! 1835: insn_size ! 1836: += (insn_size_host_copy ! 1837: * ((2 /* double-host-size */ ! 1838: * 2) /* two source operands */ ! 1839: + 1)); /* struct tme_ic */ ! 1840: ! 1841: /* add in the x86-64 instructions for calling the guest function: */ ! 1842: insn_size ! 1843: += (1 /* TME_RECODE_X86_REX */ ! 1844: + 1 /* TME_RECODE_X86_OPCODE_MOV_Iv_Gv */ ! 1845: + TME_BIT(TME_RECODE_SIZE_HOST - TME_RECODE_SIZE_8) ! 1846: + 1 /* TME_RECODE_X86_OPCODE_GRP5 */ ! 1847: + 1); /* TME_RECODE_X86_MOD_OPREG_RM */ ! 1848: } ! 1849: ! 1850: /* add in the x86 instructions to move a double-host-size guest ! 1851: function return value from d:a into ax:bp: */ ! 1852: insn_size += (insn_size_host_copy * 2); /* double-host-size */ ! 1853: ! 1854: /* since the worst-case TME_RECODE_OPCODE_GUEST insn is also the ! 1855: last insn in a thunk, add in the x86 instructions for cleaning ! 1856: the host register(s) holding the destination operand to a guest ! 1857: register window, and for jumping to the instructions thunk ! 1858: epilogue: */ ! 1859: insn_size ! 1860: += (insn_size_window_offset ! 1861: + (insn_size_host_store ! 1862: * 2) /* double-host-size */ ! 1863: + 1 /* TME_RECODE_X86_OPCODE_JMP_RELz */ ! 1864: + sizeof(tme_int32_t)); ! 1865: ! 1866: /* check the value of TME_RECODE_HOST_INSN_SIZE_MAX: */ ! 1867: if (insn_size > TME_RECODE_HOST_INSN_SIZE_MAX) { ! 1868: abort(); ! 1869: } ! 1870: } ! 1871: ! 1872: /* this host function returns the thunk offset for a new instructions ! 1873: thunk. it returns less than zero when thunks memory is exhausted ! 1874: and all instructions thunks are flushed: */ ! 1875: tme_recode_thunk_off_t ! 1876: tme_recode_host_insns_thunk_new(struct tme_recode_ic *ic, ! 1877: const struct tme_recode_insns_group *insns_group) ! 1878: { ! 1879: tme_uint8_t *thunk_bytes; ! 1880: tme_recode_thunk_off_t thunk_off; ! 1881: struct tme_recode_insn *insn; ! 1882: struct tme_recode_insn *insns_end; ! 1883: ! 1884: /* if we don't have enough room to make a maximum-sized thunk: */ ! 1885: if (!tme_recode_host_thunk_start(ic)) { ! 1886: ! 1887: /* invalidate all instructions thunks: */ ! 1888: tme_recode_insns_thunk_invalidate_all(ic); ! 1889: return (-1); ! 1890: } ! 1891: ! 1892: /* start more instructions: */ ! 1893: tme_recode_x86_insns_start(ic, thunk_bytes); ! 1894: thunk_off = tme_recode_build_to_thunk_off(ic, thunk_bytes); ! 1895: ! 1896: /* make the chain in: */ ! 1897: _tme_recode_x86_chain_in(ic, insns_group); ! 1898: ! 1899: /* the c register doesn't hold the base offset of any guest register ! 1900: window: */ ! 1901: ic->tme_recode_x86_ic_thunks_reg_guest_window_c = TME_RECODE_REG_GUEST_WINDOW_UNDEF; ! 1902: ! 1903: /* make sure there isn't any leftover if/else insn state: */ ! 1904: assert (ic->tme_recode_x86_ic_thunks_build_if == NULL ! 1905: && ic->tme_recode_x86_ic_thunks_build_else == NULL); ! 1906: ! 1907: /* emit all of the instructions: */ ! 1908: insn = insns_group->tme_recode_insns_group_insns; ! 1909: insns_end = insns_group->tme_recode_insns_group_insns_end; ! 1910: do { ! 1911: _tme_recode_x86_insn_emit(ic, insn); ! 1912: } while (++insn < insns_end); ! 1913: ! 1914: /* make sure there isn't any leftover if/else insn state: */ ! 1915: assert (ic->tme_recode_x86_ic_thunks_build_if == NULL ! 1916: && ic->tme_recode_x86_ic_thunks_build_else == NULL); ! 1917: ! 1918: /* free all host registers: */ ! 1919: tme_recode_regs_host_free_many(ic, TME_RECODE_REG_HOST(0)); ! 1920: ! 1921: /* make the chain out: */ ! 1922: _tme_recode_x86_chain_out(ic, insns_group); ! 1923: ! 1924: /* finish and return this thunk: */ ! 1925: tme_recode_host_thunk_finish(ic); ! 1926: return (thunk_off); ! 1927: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.