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