|
|
1.1 root 1: /* $Id: recode-insns.c,v 1.4 2010/02/07 17:32:01 fredette Exp $ */
2:
3: /* libtme/recode-insns.c - generic recode instruction 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: #include <tme/common.h>
37: _TME_RCSID("$Id: recode-insns.c,v 1.4 2010/02/07 17:32:01 fredette Exp $");
38:
39: #if TME_HAVE_RECODE
40:
41: /* includes: */
42: #include "recode-impl.h"
43:
44: /* macros: */
45:
46: /* the undefined flags offset: */
47: #define TME_RECODE_FLAGS_OFFSET_UNDEF (0 - (tme_uint32_t) 1)
48:
49: /* this returns the thunk offset for a new instructions thunk. it
50: returns less than zero when thunks memory is exhausted and all
51: instructions thunks are flushed: */
52: tme_recode_thunk_off_t
53: tme_recode_insns_thunk(struct tme_recode_ic *ic,
54: const struct tme_recode_insns_group *insns_group)
55: {
56: signed long reg_guest;
57: signed long ruses_record_tmp;
58: signed long ruses_record_right;
59: tme_uint32_t flags_offset;
60: tme_recode_uguest_t flags_needed;
61: tme_uint32_t flags_offset_else;
62: tme_recode_uguest_t flags_needed_else;
63: struct tme_recode_insn *insns;
64: struct tme_recode_insn *insn;
65: tme_uint32_t opcode_mask;
66: signed long operand_i;
67: tme_uint32_t ruses;
68: const struct tme_recode_flags_thunk *flags_thunk;
69: const struct tme_recode_conds_thunk *conds_thunk;
70:
71: /* initialize the mapping from host register to read-uses count for
72: a cached guest register, to all host registers free: */
73: #if TME_RECODE_REG_RUSES_FREE != 0
74: #error "TME_RECODE_REG_RUSES_FREE changed"
75: #endif
76: memset(ic->tme_recode_ic_reg_host_to_ruses,
77: TME_RECODE_REG_RUSES_FREE,
78: sizeof(ic->tme_recode_ic_reg_host_to_ruses));
79:
80: /* there are no reserved registers: */
81: ic->tme_recode_ic_reg_host_reserve_next = 0;
82:
83: /* initialize the read-uses counts for all guest registers. this
84: also marks the guest register tags as invalid: */
85: reg_guest = TME_RECODE_REG_GUEST(ic->tme_recode_ic_reg_count - 1);
86: do {
87: ic->tme_recode_ic_reginfo[reg_guest].tme_recode_reginfo_tags_ruses = (TME_RECODE_REG_RUSES_FREE + 1);
88: } while (--reg_guest >= TME_RECODE_REG_GUEST(0));
89:
90: /* the largest guest register number must fit in
91: [TME_RECODE_REG_RUSES_RECORD_REG_GUEST(0)..TME_RECODE_REG_RUSES_RECORD_UNDEF): */
92: assert (ic->tme_recode_ic_reg_count
93: < (TME_RECODE_REG_RUSES_RECORD_UNDEF
94: - TME_RECODE_REG_RUSES_RECORD_REG_GUEST(0)));
95:
96: /* reset the read-uses records: */
97: ruses_record_tmp = 0;
98: ruses_record_right = ic->tme_recode_ic_reg_guest_ruses_record_count;
99: assert (ic->tme_recode_ic_reg_guest_ruses_records[ruses_record_right]
100: == TME_RECODE_REG_RUSES_RECORD_UNDEF);
101:
102: /* we haven't found any flags register yet: */
103: flags_offset = TME_RECODE_FLAGS_OFFSET_UNDEF;
104: flags_needed = 0;
105: flags_offset_else = TME_RECODE_FLAGS_OFFSET_UNDEF;
106: flags_needed_else = 0;
107:
108: /* loop over the instructions, from last to first: */
109: insns = insns_group->tme_recode_insns_group_insns;
110: insn = insns_group->tme_recode_insns_group_insns_end;
111: do {
112: insn--;
113:
114: /* get the bitmask for this instruction's opcode: */
115: opcode_mask = (1 << insn->tme_recode_insn_opcode);
116:
117: /* if this is an else instruction, or an endif instruction, or a
118: guest instruction with unknown destination registers: */
119: if ((opcode_mask
120: & ((1 << TME_RECODE_OPCODE_ELSE)
121: | (1 << TME_RECODE_OPCODE_ENDIF)))
122: || ((opcode_mask & (1 << TME_RECODE_OPCODE_GUEST))
123: && insn->tme_recode_insn_operand_dst == TME_RECODE_OPERAND_NULL)) {
124:
125: /* if there are no guest register writes between this else,
126: endif, or guest instruction and the next else, endif, or
127: guest instruction: */
128: if (ic->tme_recode_ic_reg_guest_ruses_records[ruses_record_right]
129: >= TME_RECODE_REG_RUSES_RECORD_REG_GUEST(0)) {
130:
131: /* if we can, make a delimiter between any initial read-uses
132: records that we're about to make, and the initial read-uses
133: records that we previously made: */
134: if (ruses_record_right > 0) {
135: ruses_record_right--;
136: ic->tme_recode_ic_reg_guest_ruses_records[ruses_record_right]
137: = TME_RECODE_REG_RUSES_RECORD_UNDEF;
138: }
139: }
140:
141: /* loop over any temporary read-uses records: */
142: for (; ruses_record_tmp > 0; ) {
143:
144: /* if this temporary read-uses record was overwritten by a
145: write read-uses record: */
146: ruses_record_tmp--;
147: if (ruses_record_tmp >= ruses_record_right) {
148:
149: /* skip to the last temporary read-uses record that hasn't
150: been overwritten yet: */
151: ruses_record_tmp = ruses_record_right;
152: continue;
153: }
154:
155: /* get the guest register: */
156: reg_guest = ic->tme_recode_ic_reg_guest_ruses_records[ruses_record_tmp];
157:
158: /* get this guest register's read-uses count: */
159: ruses = ic->tme_recode_ic_reginfo[reg_guest].tme_recode_reginfo_tags_ruses;
160:
161: /* if this guest register has a read use before any first
162: write after this else, endif, or guest instruction: */
163: if (ruses > (TME_RECODE_REG_RUSES_FREE + 1)) {
164:
165: /* reset this guest register's read-uses count: */
166: ic->tme_recode_ic_reginfo[reg_guest].tme_recode_reginfo_tags_ruses = (TME_RECODE_REG_RUSES_FREE + 1);
167:
168: /* if we can't make another initial read-uses record, stop now: */
169: if (ruses_record_right < 2) {
170: break;
171: }
172:
173: /* make the initial read-uses record: */
174: ruses_record_right -= 2;
175: ic->tme_recode_ic_reg_guest_ruses_records[ruses_record_right + 0]
176: = TME_RECODE_REG_RUSES_RECORD_REG_GUEST(reg_guest);
177: ic->tme_recode_ic_reg_guest_ruses_records[ruses_record_right + 1] = ruses;
178: }
179: }
180: }
181:
182: /* if this is an integer, or guest, or read/write instruction: */
183: if (opcode_mask
184: & (((1 << TME_RECODE_OPCODES_INTEGER) - 1)
185: | (1 << TME_RECODE_OPCODE_GUEST)
186: | (1 << TME_RECODE_OPCODE_RW))) {
187:
188: /* if the destination operand is a guest register: */
189: reg_guest = insn->tme_recode_insn_operand_dst;
190: if (reg_guest >= TME_RECODE_REG_GUEST(0)) {
191:
192: /* if this guest register is not fixed: */
193: if ((ic->tme_recode_ic_reginfo[reg_guest].tme_recode_reginfo_all
194: & TME_RECODE_REGINFO_TYPE_FIXED) == 0) {
195:
196: /* get and reset this guest register's read-uses count: */
197: ruses = ic->tme_recode_ic_reginfo[reg_guest].tme_recode_reginfo_tags_ruses;
198: ic->tme_recode_ic_reginfo[reg_guest].tme_recode_reginfo_tags_ruses = (TME_RECODE_REG_RUSES_FREE + 1);
199:
200: /* make a write read-uses record: */
201: if (ruses_record_right > 0) {
202: ruses_record_right--;
203: }
204: ic->tme_recode_ic_reg_guest_ruses_records[ruses_record_right] = ruses;
205: }
206: }
207:
208: /* all integer and guest instructions can have a guest register
209: as the first source operand. all of those instructions can
210: also have a guest register as the second source operand,
211: except for the zero- and sign-extension instructions, which
212: always have a TME_RECODE_SIZE for their second source
213: operand: */
214: operand_i
215: = ((opcode_mask
216: & ((1 << TME_RECODE_OPCODE_EXTZ)
217: | (1 << TME_RECODE_OPCODE_EXTS)))
218: == 0);
219:
220: /* loop over the source operands that can be guest registers: */
221: do {
222:
223: /* if this source operand is a guest register: */
224: reg_guest = insn->tme_recode_insn_operand_src[operand_i];
225: if (reg_guest >= TME_RECODE_REG_GUEST(0)) {
226:
227: /* get this guest register's current read-uses count: */
228: ruses = ic->tme_recode_ic_reginfo[reg_guest].tme_recode_reginfo_tags_ruses;
229:
230: /* if this guest register's current read-uses count is the
231: minimum: */
232: if (ruses == (TME_RECODE_REG_RUSES_FREE + 1)) {
233:
234: /* if we can, make a temporary read-uses record for this
235: guest register: */
236: if (ruses_record_tmp < ruses_record_right) {
237: ic->tme_recode_ic_reg_guest_ruses_records[ruses_record_tmp] = reg_guest;
238: ruses_record_tmp++;
239: }
240: }
241:
242: /* increment the guest register's current read-uses count,
243: unless it would become TME_RECODE_REG_RUSES_RESERVED: */
244: if (__tme_predict_true(ruses < (TME_RECODE_REG_RUSES_RESERVED - 1))) {
245: ruses++;
246: }
247: ic->tme_recode_ic_reginfo[reg_guest].tme_recode_reginfo_tags_ruses = ruses;
248: }
249: } while (--operand_i >= 0);
250: }
251:
252: /* if this is an integer instruction: */
253: if (opcode_mask & ((1 << TME_RECODE_OPCODES_INTEGER) - 1)) {
254:
255: /* if the second source operand is a zero, and the operation is
256: commutative: */
257: if (insn->tme_recode_insn_operand_src[1] == TME_RECODE_OPERAND_ZERO) {
258: if (opcode_mask
259: & ((1 << TME_RECODE_OPCODE_AND)
260: | (1 << TME_RECODE_OPCODE_OR)
261: | (1 << TME_RECODE_OPCODE_XOR)
262: | (1 << TME_RECODE_OPCODE_ADD)
263: | (1 << TME_RECODE_OPCODE_ADDC))) {
264:
265: /* we have the convention of always putting a zero source
266: operand first whenever possible, so swap the first and
267: second source operands: */
268: insn->tme_recode_insn_operand_src[1] = insn->tme_recode_insn_operand_src[0];
269: insn->tme_recode_insn_operand_src[0] = TME_RECODE_OPERAND_ZERO;
270: }
271: }
272:
273: /* if this integer instruction can change flags: */
274: flags_thunk = insn->tme_recode_insn_flags_thunk;
275: if (flags_thunk != NULL) {
276:
277: /* if this integer instruction changes flags in a different
278: flags register: */
279: if (__tme_predict_false(flags_offset != flags_thunk->tme_recode_flags_thunk_flags_offset)) {
280:
281: /* switch to this different flags register, and need all of
282: its flags: */
283: flags_offset = flags_thunk->tme_recode_flags_thunk_flags_offset;
284: flags_needed = 0 - (tme_recode_uguest_t) 1;
285: }
286:
287: /* if this integer instruction doesn't define any of the flags
288: in this flags register needed by later instructions: */
289: if ((flags_needed & flags_thunk->tme_recode_flags_thunk_flags_defined) == 0) {
290:
291: /* this integer instruction doesn't need to define flags any
292: more: */
293: insn->tme_recode_insn_flags_thunk = NULL;
294: }
295:
296: /* any earlier instruction that also defines flags in this
297: flags register, doesn't need to define any of the flags
298: that this instruction changes: */
299: flags_needed &= ~flags_thunk->tme_recode_flags_thunk_flags_changed;
300: }
301: }
302:
303: /* if this is a defc instruction: */
304: else if (opcode_mask == (1 << TME_RECODE_OPCODE_DEFC)) {
305:
306: /* get the conditions thunk for this instruction: */
307: conds_thunk = insn->tme_recode_insn_conds_thunk;
308:
309: /* if this defc instruction tests flags in a different flags register: */
310: if (__tme_predict_false(flags_offset != conds_thunk->tme_recode_conds_thunk_flags_offset)) {
311:
312: /* switch to this different flags register, and need all of
313: its flags: */
314: flags_offset = conds_thunk->tme_recode_conds_thunk_flags_offset;
315: flags_needed = 0 - (tme_recode_uguest_t) 1;
316: }
317:
318: /* any earlier instruction that can define flags tested by this
319: defc instruction, needs to define those flags: */
320: flags_needed |= conds_thunk->tme_recode_conds_thunk_flags;
321: }
322:
323: /* if this is an endif instruction: */
324: else if (opcode_mask == (1 << TME_RECODE_OPCODE_ENDIF)) {
325:
326: /* if we find an earlier else, as an optimization we can restore
327: the flags needed by the instructions after the endif, so save
328: them now: */
329: flags_offset_else = flags_offset;
330: flags_needed_else = flags_needed;
331: }
332:
333: /* if this is an else instruction: */
334: else if (opcode_mask == (1 << TME_RECODE_OPCODE_ELSE)) {
335:
336: /* restore the flags needed by the instructions after the endif: */
337: flags_offset = flags_offset_else;
338: flags_needed = flags_needed_else;
339: }
340:
341: /* otherwise, this must be a guest, read/write, or if instruction: */
342: else {
343: assert (opcode_mask
344: & ((1 << TME_RECODE_OPCODE_GUEST)
345: | (1 << TME_RECODE_OPCODE_RW)
346: | (1 << TME_RECODE_OPCODE_IF)
347: ));
348:
349: /* since a guest function may fault and never return, we have to
350: make sure that all flags are correct in the guest ic state at
351: the time of the fault.
352:
353: since a read/write instruction may fault and never return, we
354: have to make sure that all flags are correct in the guest ic
355: state at the time of the fault.
356:
357: since an if body may not run, we have to make sure that all
358: flags are correct in the guest ic state at the time of the
359: if.
360:
361: since a jump may never return, we have to make sure that all
362: flags are correct in the guest ic state at the time of the
363: jump.
364:
365: in all of these cases, we need any earlier instruction that
366: can define flags to do so: */
367: flags_needed = 0 - (tme_recode_uguest_t) 1;
368: }
369:
370: } while (insn > insns);
371:
372: /* set the next read-uses record: */
373: ic->tme_recode_ic_reg_guest_ruses_record_next = ruses_record_right;
374:
375: /* build the new instructions thunk: */
376: return (tme_recode_host_insns_thunk_new(ic,
377: insns_group));
378: }
379:
380: /* this invalidates all instructions thunks: */
381: void
382: tme_recode_insns_thunk_invalidate_all(struct tme_recode_ic *ic)
383: {
384:
385: /* invalidate all instructions thunks: */
386: tme_recode_host_thunk_invalidate_all(ic, ic->tme_recode_ic_thunk_off_variable);
387: }
388:
389: #endif /* TME_HAVE_RECODE */
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.