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