|
|
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.