|
|
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.