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