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
tme_recode_insns_thunk(struct tme_recode_ic * ic,const struct tme_recode_insns_group * insns_group)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
tme_recode_insns_thunk_invalidate_all(struct tme_recode_ic * ic)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 */
390