| |
| /*---------------------------------------------------------------*/ |
| /*--- ---*/ |
| /*--- This file (guest-x86/ghelpers.c) is ---*/ |
| /*--- Copyright (c) 2004 OpenWorks LLP. All rights reserved. ---*/ |
| /*--- ---*/ |
| /*---------------------------------------------------------------*/ |
| |
| /* |
| This file is part of LibVEX, a library for dynamic binary |
| instrumentation and translation. |
| |
| Copyright (C) 2004 OpenWorks, LLP. |
| |
| This program is free software; you can redistribute it and/or modify |
| it under the terms of the GNU General Public License as published by |
| the Free Software Foundation; Version 2 dated June 1991 of the |
| license. |
| |
| This program is distributed in the hope that it will be useful, |
| but WITHOUT ANY WARRANTY; without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, or liability |
| for damages. See the GNU General Public License for more details. |
| |
| Neither the names of the U.S. Department of Energy nor the |
| University of California nor the names of its contributors may be |
| used to endorse or promote products derived from this software |
| without prior written permission. |
| |
| You should have received a copy of the GNU General Public License |
| along with this program; if not, write to the Free Software |
| Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 |
| USA. |
| */ |
| |
| #include "libvex_basictypes.h" |
| #include "libvex_emwarn.h" |
| #include "libvex_guest_x86.h" |
| #include "libvex_ir.h" |
| #include "libvex.h" |
| |
| #include "main/vex_util.h" |
| #include "guest-x86/gdefs.h" |
| |
| |
| /* This file contains helper functions for x86 guest code. |
| Calls to these functions are generated by the back end. |
| These calls are of course in the host machine code and |
| this file will be compiled to host machine code, so that |
| all makes sense. |
| |
| Only change the signatures of these helper functions very |
| carefully. If you change the signature here, you'll have to change |
| the parameters passed to it in the IR calls constructed by |
| guest-x86/toIR.c. |
| |
| Some of this code/logic is derived from QEMU, which is copyright |
| Fabrice Bellard, licensed under the LGPL. It is used with |
| permission. |
| */ |
| |
| /* Set to 1 to get detailed profiling info about use of the flag |
| machinery. */ |
| #define PROFILE_EFLAGS 0 |
| |
| |
| static const UChar parity_table[256] = { |
| X86G_CC_MASK_P, 0, 0, X86G_CC_MASK_P, 0, X86G_CC_MASK_P, X86G_CC_MASK_P, 0, |
| 0, X86G_CC_MASK_P, X86G_CC_MASK_P, 0, X86G_CC_MASK_P, 0, 0, X86G_CC_MASK_P, |
| 0, X86G_CC_MASK_P, X86G_CC_MASK_P, 0, X86G_CC_MASK_P, 0, 0, X86G_CC_MASK_P, |
| X86G_CC_MASK_P, 0, 0, X86G_CC_MASK_P, 0, X86G_CC_MASK_P, X86G_CC_MASK_P, 0, |
| 0, X86G_CC_MASK_P, X86G_CC_MASK_P, 0, X86G_CC_MASK_P, 0, 0, X86G_CC_MASK_P, |
| X86G_CC_MASK_P, 0, 0, X86G_CC_MASK_P, 0, X86G_CC_MASK_P, X86G_CC_MASK_P, 0, |
| X86G_CC_MASK_P, 0, 0, X86G_CC_MASK_P, 0, X86G_CC_MASK_P, X86G_CC_MASK_P, 0, |
| 0, X86G_CC_MASK_P, X86G_CC_MASK_P, 0, X86G_CC_MASK_P, 0, 0, X86G_CC_MASK_P, |
| 0, X86G_CC_MASK_P, X86G_CC_MASK_P, 0, X86G_CC_MASK_P, 0, 0, X86G_CC_MASK_P, |
| X86G_CC_MASK_P, 0, 0, X86G_CC_MASK_P, 0, X86G_CC_MASK_P, X86G_CC_MASK_P, 0, |
| X86G_CC_MASK_P, 0, 0, X86G_CC_MASK_P, 0, X86G_CC_MASK_P, X86G_CC_MASK_P, 0, |
| 0, X86G_CC_MASK_P, X86G_CC_MASK_P, 0, X86G_CC_MASK_P, 0, 0, X86G_CC_MASK_P, |
| X86G_CC_MASK_P, 0, 0, X86G_CC_MASK_P, 0, X86G_CC_MASK_P, X86G_CC_MASK_P, 0, |
| 0, X86G_CC_MASK_P, X86G_CC_MASK_P, 0, X86G_CC_MASK_P, 0, 0, X86G_CC_MASK_P, |
| 0, X86G_CC_MASK_P, X86G_CC_MASK_P, 0, X86G_CC_MASK_P, 0, 0, X86G_CC_MASK_P, |
| X86G_CC_MASK_P, 0, 0, X86G_CC_MASK_P, 0, X86G_CC_MASK_P, X86G_CC_MASK_P, 0, |
| 0, X86G_CC_MASK_P, X86G_CC_MASK_P, 0, X86G_CC_MASK_P, 0, 0, X86G_CC_MASK_P, |
| X86G_CC_MASK_P, 0, 0, X86G_CC_MASK_P, 0, X86G_CC_MASK_P, X86G_CC_MASK_P, 0, |
| X86G_CC_MASK_P, 0, 0, X86G_CC_MASK_P, 0, X86G_CC_MASK_P, X86G_CC_MASK_P, 0, |
| 0, X86G_CC_MASK_P, X86G_CC_MASK_P, 0, X86G_CC_MASK_P, 0, 0, X86G_CC_MASK_P, |
| X86G_CC_MASK_P, 0, 0, X86G_CC_MASK_P, 0, X86G_CC_MASK_P, X86G_CC_MASK_P, 0, |
| 0, X86G_CC_MASK_P, X86G_CC_MASK_P, 0, X86G_CC_MASK_P, 0, 0, X86G_CC_MASK_P, |
| 0, X86G_CC_MASK_P, X86G_CC_MASK_P, 0, X86G_CC_MASK_P, 0, 0, X86G_CC_MASK_P, |
| X86G_CC_MASK_P, 0, 0, X86G_CC_MASK_P, 0, X86G_CC_MASK_P, X86G_CC_MASK_P, 0, |
| X86G_CC_MASK_P, 0, 0, X86G_CC_MASK_P, 0, X86G_CC_MASK_P, X86G_CC_MASK_P, 0, |
| 0, X86G_CC_MASK_P, X86G_CC_MASK_P, 0, X86G_CC_MASK_P, 0, 0, X86G_CC_MASK_P, |
| 0, X86G_CC_MASK_P, X86G_CC_MASK_P, 0, X86G_CC_MASK_P, 0, 0, X86G_CC_MASK_P, |
| X86G_CC_MASK_P, 0, 0, X86G_CC_MASK_P, 0, X86G_CC_MASK_P, X86G_CC_MASK_P, 0, |
| 0, X86G_CC_MASK_P, X86G_CC_MASK_P, 0, X86G_CC_MASK_P, 0, 0, X86G_CC_MASK_P, |
| X86G_CC_MASK_P, 0, 0, X86G_CC_MASK_P, 0, X86G_CC_MASK_P, X86G_CC_MASK_P, 0, |
| X86G_CC_MASK_P, 0, 0, X86G_CC_MASK_P, 0, X86G_CC_MASK_P, X86G_CC_MASK_P, 0, |
| 0, X86G_CC_MASK_P, X86G_CC_MASK_P, 0, X86G_CC_MASK_P, 0, 0, X86G_CC_MASK_P, |
| }; |
| |
| /* n must be a constant to be efficient */ |
| inline static Int lshift ( Int x, Int n ) |
| { |
| if (n >= 0) |
| return x << n; |
| else |
| return x >> (-n); |
| } |
| |
| |
| #define PREAMBLE(__data_bits) \ |
| /* const */ UInt DATA_MASK \ |
| = __data_bits==8 ? 0xFF \ |
| : (__data_bits==16 ? 0xFFFF \ |
| : 0xFFFFFFFF); \ |
| /* const */ UInt SIGN_MASK = 1 << (__data_bits - 1); \ |
| /* const */ UInt CC_DEP1 = cc_dep1_formal; \ |
| /* const */ UInt CC_DEP2 = cc_dep2_formal; \ |
| /* const */ UInt CC_NDEP = cc_ndep_formal; \ |
| /* Four bogus assignments, which hopefully gcc can */ \ |
| /* optimise away, and which stop it complaining about */ \ |
| /* unused variables. */ \ |
| SIGN_MASK = SIGN_MASK; \ |
| DATA_MASK = DATA_MASK; \ |
| CC_DEP2 = CC_DEP2; \ |
| CC_NDEP = CC_NDEP; |
| |
| |
| /*-------------------------------------------------------------*/ |
| |
| #define ACTIONS_ADD(DATA_BITS,DATA_UTYPE) \ |
| { \ |
| PREAMBLE(DATA_BITS); \ |
| { Int cf, pf, af, zf, sf, of; \ |
| Int argL, argR, res; \ |
| argL = CC_DEP1; \ |
| argR = CC_DEP2; \ |
| res = argL + argR; \ |
| cf = (DATA_UTYPE)res < (DATA_UTYPE)argL; \ |
| pf = parity_table[(UChar)res]; \ |
| af = (res ^ argL ^ argR) & 0x10; \ |
| zf = ((DATA_UTYPE)res == 0) << 6; \ |
| sf = lshift(res, 8 - DATA_BITS) & 0x80; \ |
| of = lshift((argL ^ argR ^ -1) & (argL ^ res), \ |
| 12 - DATA_BITS) & X86G_CC_MASK_O; \ |
| return cf | pf | af | zf | sf | of; \ |
| } \ |
| } |
| |
| /*-------------------------------------------------------------*/ |
| |
| #define ACTIONS_SUB(DATA_BITS,DATA_UTYPE) \ |
| { \ |
| PREAMBLE(DATA_BITS); \ |
| { Int cf, pf, af, zf, sf, of; \ |
| Int argL, argR, res; \ |
| argL = CC_DEP1; \ |
| argR = CC_DEP2; \ |
| res = argL - argR; \ |
| cf = (DATA_UTYPE)argL < (DATA_UTYPE)argR; \ |
| pf = parity_table[(UChar)res]; \ |
| af = (res ^ argL ^ argR) & 0x10; \ |
| zf = ((DATA_UTYPE)res == 0) << 6; \ |
| sf = lshift(res, 8 - DATA_BITS) & 0x80; \ |
| of = lshift((argL ^ argR) & (argL ^ res), \ |
| 12 - DATA_BITS) & X86G_CC_MASK_O; \ |
| return cf | pf | af | zf | sf | of; \ |
| } \ |
| } |
| |
| /*-------------------------------------------------------------*/ |
| |
| #define ACTIONS_ADC(DATA_BITS,DATA_UTYPE) \ |
| { \ |
| PREAMBLE(DATA_BITS); \ |
| { Int cf, pf, af, zf, sf, of; \ |
| Int argL, argR, oldC, res; \ |
| oldC = CC_NDEP & X86G_CC_MASK_C; \ |
| argL = CC_DEP1; \ |
| argR = CC_DEP2 ^ oldC; \ |
| res = (argL + argR) + oldC; \ |
| if (oldC) \ |
| cf = (DATA_UTYPE)res <= (DATA_UTYPE)argL; \ |
| else \ |
| cf = (DATA_UTYPE)res < (DATA_UTYPE)argL; \ |
| pf = parity_table[(UChar)res]; \ |
| af = (res ^ argL ^ argR) & 0x10; \ |
| zf = ((DATA_UTYPE)res == 0) << 6; \ |
| sf = lshift(res, 8 - DATA_BITS) & 0x80; \ |
| of = lshift((argL ^ argR ^ -1) & (argL ^ res), \ |
| 12 - DATA_BITS) & X86G_CC_MASK_O; \ |
| return cf | pf | af | zf | sf | of; \ |
| } \ |
| } |
| |
| /*-------------------------------------------------------------*/ |
| |
| #define ACTIONS_SBB(DATA_BITS,DATA_UTYPE) \ |
| { \ |
| PREAMBLE(DATA_BITS); \ |
| { Int cf, pf, af, zf, sf, of; \ |
| Int argL, argR, oldC, res; \ |
| oldC = CC_NDEP & X86G_CC_MASK_C; \ |
| argL = CC_DEP1; \ |
| argR = CC_DEP2 ^ oldC; \ |
| res = (argL - argR) - oldC; \ |
| if (oldC) \ |
| cf = (DATA_UTYPE)argL <= (DATA_UTYPE)argR; \ |
| else \ |
| cf = (DATA_UTYPE)argL < (DATA_UTYPE)argR; \ |
| pf = parity_table[(UChar)res]; \ |
| af = (res ^ argL ^ argR) & 0x10; \ |
| zf = ((DATA_UTYPE)res == 0) << 6; \ |
| sf = lshift(res, 8 - DATA_BITS) & 0x80; \ |
| of = lshift((argL ^ argR) & (argL ^ res), \ |
| 12 - DATA_BITS) & X86G_CC_MASK_O; \ |
| return cf | pf | af | zf | sf | of; \ |
| } \ |
| } |
| |
| /*-------------------------------------------------------------*/ |
| |
| #define ACTIONS_LOGIC(DATA_BITS,DATA_UTYPE) \ |
| { \ |
| PREAMBLE(DATA_BITS); \ |
| { Int cf, pf, af, zf, sf, of; \ |
| cf = 0; \ |
| pf = parity_table[(UChar)CC_DEP1]; \ |
| af = 0; \ |
| zf = ((DATA_UTYPE)CC_DEP1 == 0) << 6; \ |
| sf = lshift(CC_DEP1, 8 - DATA_BITS) & 0x80; \ |
| of = 0; \ |
| return cf | pf | af | zf | sf | of; \ |
| } \ |
| } |
| |
| /*-------------------------------------------------------------*/ |
| |
| #define ACTIONS_INC(DATA_BITS,DATA_UTYPE) \ |
| { \ |
| PREAMBLE(DATA_BITS); \ |
| { Int cf, pf, af, zf, sf, of; \ |
| Int argL, argR, res; \ |
| res = CC_DEP1; \ |
| argL = res - 1; \ |
| argR = 1; \ |
| cf = CC_NDEP & X86G_CC_MASK_C; \ |
| pf = parity_table[(UChar)res]; \ |
| af = (res ^ argL ^ argR) & 0x10; \ |
| zf = ((DATA_UTYPE)res == 0) << 6; \ |
| sf = lshift(res, 8 - DATA_BITS) & 0x80; \ |
| of = ((res & DATA_MASK) == SIGN_MASK) << 11; \ |
| return cf | pf | af | zf | sf | of; \ |
| } \ |
| } |
| |
| /*-------------------------------------------------------------*/ |
| |
| #define ACTIONS_DEC(DATA_BITS,DATA_UTYPE) \ |
| { \ |
| PREAMBLE(DATA_BITS); \ |
| { Int cf, pf, af, zf, sf, of; \ |
| Int argL, argR, res; \ |
| res = CC_DEP1; \ |
| argL = res + 1; \ |
| argR = 1; \ |
| cf = CC_NDEP & X86G_CC_MASK_C; \ |
| pf = parity_table[(UChar)res]; \ |
| af = (res ^ argL ^ argR) & 0x10; \ |
| zf = ((DATA_UTYPE)res == 0) << 6; \ |
| sf = lshift(res, 8 - DATA_BITS) & 0x80; \ |
| of = ((res & DATA_MASK) \ |
| == ((UInt)SIGN_MASK - 1)) << 11; \ |
| return cf | pf | af | zf | sf | of; \ |
| } \ |
| } |
| |
| /*-------------------------------------------------------------*/ |
| |
| #define ACTIONS_SHL(DATA_BITS,DATA_UTYPE) \ |
| { \ |
| PREAMBLE(DATA_BITS); \ |
| { Int cf, pf, af, zf, sf, of; \ |
| cf = (CC_DEP2 >> (DATA_BITS - 1)) & X86G_CC_MASK_C; \ |
| pf = parity_table[(UChar)CC_DEP1]; \ |
| af = 0; /* undefined */ \ |
| zf = ((DATA_UTYPE)CC_DEP1 == 0) << 6; \ |
| sf = lshift(CC_DEP1, 8 - DATA_BITS) & 0x80; \ |
| /* of is defined if shift count == 1 */ \ |
| of = lshift(CC_DEP2 ^ CC_DEP1, 12 - DATA_BITS) \ |
| & X86G_CC_MASK_O; \ |
| return cf | pf | af | zf | sf | of; \ |
| } \ |
| } |
| |
| /*-------------------------------------------------------------*/ |
| |
| #define ACTIONS_SHR(DATA_BITS,DATA_UTYPE) \ |
| { \ |
| PREAMBLE(DATA_BITS); \ |
| { Int cf, pf, af, zf, sf, of; \ |
| cf = CC_DEP2 & 1; \ |
| pf = parity_table[(UChar)CC_DEP1]; \ |
| af = 0; /* undefined */ \ |
| zf = ((DATA_UTYPE)CC_DEP1 == 0) << 6; \ |
| sf = lshift(CC_DEP1, 8 - DATA_BITS) & 0x80; \ |
| /* of is defined if shift count == 1 */ \ |
| of = lshift(CC_DEP2 ^ CC_DEP1, 12 - DATA_BITS) \ |
| & X86G_CC_MASK_O; \ |
| return cf | pf | af | zf | sf | of; \ |
| } \ |
| } |
| |
| /*-------------------------------------------------------------*/ |
| |
| /* ROL: cf' = lsb(result). of' = msb(result) ^ lsb(result). */ |
| /* DEP1 = result, NDEP = old flags */ |
| #define ACTIONS_ROL(DATA_BITS,DATA_UTYPE) \ |
| { \ |
| PREAMBLE(DATA_BITS); \ |
| { Int fl \ |
| = (CC_NDEP & ~(X86G_CC_MASK_O | X86G_CC_MASK_C)) \ |
| | (X86G_CC_MASK_C & CC_DEP1) \ |
| | (X86G_CC_MASK_O & (lshift(CC_DEP1, \ |
| 11-(DATA_BITS-1)) \ |
| ^ lshift(CC_DEP1, 11))); \ |
| return fl; \ |
| } \ |
| } |
| |
| /*-------------------------------------------------------------*/ |
| |
| /* ROR: cf' = msb(result). of' = msb(result) ^ msb-1(result). */ |
| /* DEP1 = result, NDEP = old flags */ |
| #define ACTIONS_ROR(DATA_BITS,DATA_UTYPE) \ |
| { \ |
| PREAMBLE(DATA_BITS); \ |
| { Int fl \ |
| = (CC_NDEP & ~(X86G_CC_MASK_O | X86G_CC_MASK_C)) \ |
| | (X86G_CC_MASK_C & (CC_DEP1 >> (DATA_BITS-1))) \ |
| | (X86G_CC_MASK_O & (lshift(CC_DEP1, \ |
| 11-(DATA_BITS-1)) \ |
| ^ lshift(CC_DEP1, 11-(DATA_BITS-1)+1))); \ |
| return fl; \ |
| } \ |
| } |
| |
| /*-------------------------------------------------------------*/ |
| |
| #define ACTIONS_UMUL(DATA_BITS,DATA_UTYPE,DATA_U2TYPE) \ |
| { \ |
| PREAMBLE(DATA_BITS); \ |
| { Int cf, pf, af, zf, sf, of; \ |
| DATA_UTYPE hi; \ |
| DATA_UTYPE lo = ((DATA_UTYPE)CC_DEP1) \ |
| * ((DATA_UTYPE)CC_DEP2); \ |
| DATA_U2TYPE rr = ((DATA_U2TYPE)((DATA_UTYPE)CC_DEP1)) \ |
| * ((DATA_U2TYPE)((DATA_UTYPE)CC_DEP2)); \ |
| hi = (DATA_UTYPE)(rr >>/*u*/ DATA_BITS); \ |
| cf = (hi != 0); \ |
| pf = parity_table[(UChar)lo]; \ |
| af = 0; /* undefined */ \ |
| zf = (lo == 0) << 6; \ |
| sf = lshift(lo, 8 - DATA_BITS) & 0x80; \ |
| of = cf << 11; \ |
| return cf | pf | af | zf | sf | of; \ |
| } \ |
| } |
| |
| /*-------------------------------------------------------------*/ |
| |
| #define ACTIONS_SMUL(DATA_BITS,DATA_STYPE,DATA_S2TYPE) \ |
| { \ |
| PREAMBLE(DATA_BITS); \ |
| { Int cf, pf, af, zf, sf, of; \ |
| DATA_STYPE hi; \ |
| DATA_STYPE lo = ((DATA_STYPE)CC_DEP1) \ |
| * ((DATA_STYPE)CC_DEP2); \ |
| DATA_S2TYPE rr = ((DATA_S2TYPE)((DATA_STYPE)CC_DEP1)) \ |
| * ((DATA_S2TYPE)((DATA_STYPE)CC_DEP2)); \ |
| hi = (DATA_STYPE)(rr >>/*s*/ DATA_BITS); \ |
| cf = (hi != (lo >>/*s*/ (DATA_BITS-1))); \ |
| pf = parity_table[(UChar)lo]; \ |
| af = 0; /* undefined */ \ |
| zf = (lo == 0) << 6; \ |
| sf = lshift(lo, 8 - DATA_BITS) & 0x80; \ |
| of = cf << 11; \ |
| return cf | pf | af | zf | sf | of; \ |
| } \ |
| } |
| |
| |
| #if PROFILE_EFLAGS |
| |
| static UInt tabc[X86G_CC_OP_NUMBER]; |
| static UInt tab[X86G_CC_OP_NUMBER][16]; |
| static Bool initted = False; |
| static UInt n_calc_cond = 0; |
| static UInt n_calc_all = 0; |
| static UInt n_calc_c = 0; |
| |
| static void showCounts ( void ) |
| { |
| Int op, co; |
| Char ch; |
| vex_printf("\nALL=%d COND=%d C=%d\n", |
| n_calc_all-n_calc_cond-n_calc_c, n_calc_cond, n_calc_c); |
| vex_printf(" CARRY O NO B NB Z NZ BE NBE" |
| " S NS P NP L NL LE NLE\n"); |
| vex_printf(" ----------------------------------------------" |
| "----------------------------------------\n"); |
| for (op = 0; op < X86G_CC_OP_NUMBER; op++) { |
| |
| ch = ' '; |
| if (op > 0 && (op-1) % 3 == 0) |
| ch = 'B'; |
| if (op > 0 && (op-1) % 3 == 1) |
| ch = 'W'; |
| if (op > 0 && (op-1) % 3 == 2) |
| ch = 'L'; |
| |
| vex_printf("%2d%c: ", op, ch); |
| vex_printf("%6d ", tabc[op]); |
| for (co = 0; co < 16; co++) { |
| Int n = tab[op][co]; |
| if (n >= 1000) { |
| vex_printf(" %3dK", n / 1000); |
| } else |
| if (n >= 0) { |
| vex_printf(" %3d ", n ); |
| } else { |
| vex_printf(" "); |
| } |
| } |
| vex_printf("\n"); |
| } |
| vex_printf("\n"); |
| } |
| |
| static void initCounts ( void ) |
| { |
| Int op, co; |
| initted = True; |
| for (op = 0; op < X86G_CC_OP_NUMBER; op++) { |
| tabc[op] = 0; |
| for (co = 0; co < 16; co++) |
| tab[op][co] = 0; |
| } |
| } |
| |
| #endif /* PROFILE_EFLAGS */ |
| |
| /* CALLED FROM GENERATED CODE: CLEAN HELPER */ |
| /* Calculate all the 6 flags from the supplied thunk parameters. */ |
| UInt x86g_calculate_eflags_all ( UInt cc_op, |
| UInt cc_dep1_formal, |
| UInt cc_dep2_formal, |
| UInt cc_ndep_formal ) |
| { |
| # if PROFILE_EFLAGS |
| n_calc_all++; |
| # endif |
| switch (cc_op) { |
| case X86G_CC_OP_COPY: |
| return cc_dep1_formal |
| & (X86G_CC_MASK_O | X86G_CC_MASK_S | X86G_CC_MASK_Z |
| | X86G_CC_MASK_A | X86G_CC_MASK_C | X86G_CC_MASK_P); |
| |
| case X86G_CC_OP_ADDB: ACTIONS_ADD( 8, UChar ); |
| case X86G_CC_OP_ADDW: ACTIONS_ADD( 16, UShort ); |
| case X86G_CC_OP_ADDL: ACTIONS_ADD( 32, UInt ); |
| |
| case X86G_CC_OP_ADCB: ACTIONS_ADC( 8, UChar ); |
| case X86G_CC_OP_ADCW: ACTIONS_ADC( 16, UShort ); |
| case X86G_CC_OP_ADCL: ACTIONS_ADC( 32, UInt ); |
| |
| case X86G_CC_OP_SUBB: ACTIONS_SUB( 8, UChar ); |
| case X86G_CC_OP_SUBW: ACTIONS_SUB( 16, UShort ); |
| case X86G_CC_OP_SUBL: ACTIONS_SUB( 32, UInt ); |
| |
| case X86G_CC_OP_SBBB: ACTIONS_SBB( 8, UChar ); |
| case X86G_CC_OP_SBBW: ACTIONS_SBB( 16, UShort ); |
| case X86G_CC_OP_SBBL: ACTIONS_SBB( 32, UInt ); |
| |
| case X86G_CC_OP_LOGICB: ACTIONS_LOGIC( 8, UChar ); |
| case X86G_CC_OP_LOGICW: ACTIONS_LOGIC( 16, UShort ); |
| case X86G_CC_OP_LOGICL: ACTIONS_LOGIC( 32, UInt ); |
| |
| case X86G_CC_OP_INCB: ACTIONS_INC( 8, UChar ); |
| case X86G_CC_OP_INCW: ACTIONS_INC( 16, UShort ); |
| case X86G_CC_OP_INCL: ACTIONS_INC( 32, UInt ); |
| |
| case X86G_CC_OP_DECB: ACTIONS_DEC( 8, UChar ); |
| case X86G_CC_OP_DECW: ACTIONS_DEC( 16, UShort ); |
| case X86G_CC_OP_DECL: ACTIONS_DEC( 32, UInt ); |
| |
| case X86G_CC_OP_SHLB: ACTIONS_SHL( 8, UChar ); |
| case X86G_CC_OP_SHLW: ACTIONS_SHL( 16, UShort ); |
| case X86G_CC_OP_SHLL: ACTIONS_SHL( 32, UInt ); |
| |
| case X86G_CC_OP_SHRB: ACTIONS_SHR( 8, UChar ); |
| case X86G_CC_OP_SHRW: ACTIONS_SHR( 16, UShort ); |
| case X86G_CC_OP_SHRL: ACTIONS_SHR( 32, UInt ); |
| |
| case X86G_CC_OP_ROLB: ACTIONS_ROL( 8, UChar ); |
| case X86G_CC_OP_ROLW: ACTIONS_ROL( 16, UShort ); |
| case X86G_CC_OP_ROLL: ACTIONS_ROL( 32, UInt ); |
| |
| case X86G_CC_OP_RORB: ACTIONS_ROR( 8, UChar ); |
| case X86G_CC_OP_RORW: ACTIONS_ROR( 16, UShort ); |
| case X86G_CC_OP_RORL: ACTIONS_ROR( 32, UInt ); |
| |
| case X86G_CC_OP_UMULB: ACTIONS_UMUL( 8, UChar, UShort ); |
| case X86G_CC_OP_UMULW: ACTIONS_UMUL( 16, UShort, UInt ); |
| case X86G_CC_OP_UMULL: ACTIONS_UMUL( 32, UInt, ULong ); |
| |
| case X86G_CC_OP_SMULB: ACTIONS_SMUL( 8, Char, Short ); |
| case X86G_CC_OP_SMULW: ACTIONS_SMUL( 16, Short, Int ); |
| case X86G_CC_OP_SMULL: ACTIONS_SMUL( 32, Int, Long ); |
| |
| default: |
| /* shouldn't really make these calls from generated code */ |
| vex_printf("calculate_eflags_all(X86)( %d, 0x%x, 0x%x, 0x%x )\n", |
| cc_op, cc_dep1_formal, cc_dep2_formal, cc_ndep_formal ); |
| vpanic("calculate_eflags_all(X86)"); |
| } |
| } |
| |
| |
| /* CALLED FROM GENERATED CODE: CLEAN HELPER */ |
| /* Calculate just the carry flag from the supplied thunk parameters. */ |
| UInt x86g_calculate_eflags_c ( UInt cc_op, |
| UInt cc_dep1, |
| UInt cc_dep2, |
| UInt cc_ndep ) |
| { |
| /* Fast-case some common ones. */ |
| switch (cc_op) { |
| case X86G_CC_OP_LOGICL: |
| case X86G_CC_OP_LOGICW: |
| case X86G_CC_OP_LOGICB: |
| return 0; |
| case X86G_CC_OP_SUBL: |
| return ((UInt)cc_dep1) < ((UInt)cc_dep2) |
| ? X86G_CC_MASK_C : 0; |
| #if 0 |
| case X86G_CC_OP_SUBB: |
| return ((UInt)(cc_dep1 & 0xFF)) < ((UInt)(cc_dep2 & 0xFF)) |
| ? X86G_CC_MASK_C : 0; |
| #endif |
| #if 0 |
| case X86G_CC_OP_DECL: |
| return cc_src; |
| case X86G_CC_OP_ADDL: |
| return ( ((UInt)cc_src + (UInt)cc_dst) < ((UInt)cc_src) ) |
| ? X86G_CC_MASK_C : 0; |
| case X86G_CC_OP_SUBB: |
| return ( ((UInt)(cc_src & 0xFF)) > ((UInt)(cc_dst & 0xFF)) ) |
| ? X86G_CC_MASK_C : 0; |
| #endif |
| default: |
| break; |
| } |
| |
| # if PROFILE_EFLAGS |
| if (!initted) |
| initCounts(); |
| tabc[cc_op]++; |
| |
| n_calc_c++; |
| # endif |
| return x86g_calculate_eflags_all(cc_op,cc_dep1,cc_dep2,cc_ndep) |
| & X86G_CC_MASK_C; |
| } |
| |
| |
| /* CALLED FROM GENERATED CODE: CLEAN HELPER */ |
| /* returns 1 or 0 */ |
| UInt x86g_calculate_condition ( UInt/*X86Condcode*/ cond, |
| UInt cc_op, |
| UInt cc_dep1, |
| UInt cc_dep2, |
| UInt cc_ndep ) |
| { |
| UInt eflags = x86g_calculate_eflags_all(cc_op, cc_dep1, |
| cc_dep2, cc_ndep); |
| UInt of,sf,zf,cf,pf; |
| UInt inv = cond & 1; |
| |
| # if PROFILE_EFLAGS |
| if (!initted) |
| initCounts(); |
| |
| tab[cc_op][cond]++; |
| n_calc_cond++; |
| |
| if (0 == ((n_calc_all+n_calc_c) & 0x7FFFF)) showCounts(); |
| # endif |
| |
| switch (cond) { |
| case X86CondNO: |
| case X86CondO: /* OF == 1 */ |
| of = eflags >> X86G_CC_SHIFT_O; |
| return 1 & (inv ^ of); |
| |
| case X86CondNZ: |
| case X86CondZ: /* ZF == 1 */ |
| zf = eflags >> X86G_CC_SHIFT_Z; |
| return 1 & (inv ^ zf); |
| |
| case X86CondNB: |
| case X86CondB: /* CF == 1 */ |
| cf = eflags >> X86G_CC_SHIFT_C; |
| return 1 & (inv ^ cf); |
| break; |
| |
| case X86CondNBE: |
| case X86CondBE: /* (CF or ZF) == 1 */ |
| cf = eflags >> X86G_CC_SHIFT_C; |
| zf = eflags >> X86G_CC_SHIFT_Z; |
| return 1 & (inv ^ (cf | zf)); |
| break; |
| |
| case X86CondNS: |
| case X86CondS: /* SF == 1 */ |
| sf = eflags >> X86G_CC_SHIFT_S; |
| return 1 & (inv ^ sf); |
| |
| case X86CondNP: |
| case X86CondP: /* PF == 1 */ |
| pf = eflags >> X86G_CC_SHIFT_P; |
| return 1 & (inv ^ pf); |
| |
| case X86CondNL: |
| case X86CondL: /* (SF xor OF) == 1 */ |
| sf = eflags >> X86G_CC_SHIFT_S; |
| of = eflags >> X86G_CC_SHIFT_O; |
| return 1 & (inv ^ (sf ^ of)); |
| break; |
| |
| case X86CondNLE: |
| case X86CondLE: /* ((SF xor OF) or ZF) == 1 */ |
| sf = eflags >> X86G_CC_SHIFT_S; |
| of = eflags >> X86G_CC_SHIFT_O; |
| zf = eflags >> X86G_CC_SHIFT_Z; |
| return 1 & (inv ^ ((sf ^ of) | zf)); |
| break; |
| |
| default: |
| /* shouldn't really make these calls from generated code */ |
| vex_printf("calculate_condition( %d, %d, 0x%x, 0x%x, 0x%x )\n", |
| cond, cc_op, cc_dep1, cc_dep2, cc_ndep ); |
| vpanic("calculate_condition"); |
| } |
| } |
| |
| |
| /* Used by the optimiser to try specialisations. Returns an |
| equivalent expression, or NULL if none. */ |
| |
| static Bool isU32 ( IRExpr* e, UInt n ) |
| { |
| return e->tag == Iex_Const |
| && e->Iex.Const.con->tag == Ico_U32 |
| && e->Iex.Const.con->Ico.U32 == n; |
| } |
| |
| IRExpr* guest_x86_spechelper ( Char* function_name, |
| IRExpr** args ) |
| { |
| # define unop(_op,_a1) IRExpr_Unop((_op),(_a1)) |
| # define binop(_op,_a1,_a2) IRExpr_Binop((_op),(_a1),(_a2)) |
| # define mkU32(_n) IRExpr_Const(IRConst_U32(_n)) |
| # define mkU8(_n) IRExpr_Const(IRConst_U8(_n)) |
| |
| Int i, arity = 0; |
| for (i = 0; args[i]; i++) |
| arity++; |
| # if 0 |
| vex_printf("spec request:\n"); |
| vex_printf(" %s ", function_name); |
| for (i = 0; i < arity; i++) { |
| vex_printf(" "); |
| ppIRExpr(args[i]); |
| } |
| vex_printf("\n"); |
| # endif |
| |
| /* --------- specialising "x86g_calculate_eflags_c" --------- */ |
| |
| if (vex_streq(function_name, "x86g_calculate_eflags_c")) { |
| /* specialise calls to above "calculate_eflags_c" function */ |
| IRExpr *cc_op, *cc_dep1, *cc_dep2, *cc_ndep; |
| vassert(arity == 4); |
| cc_op = args[0]; |
| cc_dep1 = args[1]; |
| cc_dep2 = args[2]; |
| cc_ndep = args[3]; |
| |
| if (isU32(cc_op, X86G_CC_OP_SUBL)) { |
| /* C after sub denotes unsigned less than */ |
| return unop(Iop_1Uto32, |
| binop(Iop_CmpLT32U, cc_dep1, cc_dep2)); |
| } |
| if (isU32(cc_op, X86G_CC_OP_LOGICL)) { |
| /* cflag after logic is zero */ |
| return mkU32(0); |
| } |
| if (isU32(cc_op, X86G_CC_OP_DECL) || isU32(cc_op, X86G_CC_OP_INCL)) { |
| /* If the thunk is dec or inc, the cflag is supplied as CC_NDEP. */ |
| return cc_ndep; |
| } |
| if (isU32(cc_op, X86G_CC_OP_COPY)) { |
| /* cflag after COPY is stored in DEP1. */ |
| return |
| binop( |
| Iop_And32, |
| binop(Iop_Shr32, cc_dep1, mkU8(X86G_CC_SHIFT_C)), |
| mkU32(1) |
| ); |
| } |
| # if 0 |
| if (cc_op->tag == Iex_Const) { |
| vex_printf("CFLAG "); ppIRExpr(cc_op); vex_printf("\n"); |
| } |
| # endif |
| |
| return NULL; |
| } |
| |
| /* --------- specialising "x86g_calculate_condition" --------- */ |
| |
| if (vex_streq(function_name, "x86g_calculate_condition")) { |
| /* specialise calls to above "calculate condition" function */ |
| IRExpr *cond, *cc_op, *cc_dep1, *cc_dep2, *cc_ndep; |
| vassert(arity == 5); |
| cond = args[0]; |
| cc_op = args[1]; |
| cc_dep1 = args[2]; |
| cc_dep2 = args[3]; |
| cc_ndep = args[4]; |
| |
| /*---------------- ADDL ----------------*/ |
| |
| if (isU32(cc_op, X86G_CC_OP_ADDL) && isU32(cond, X86CondZ)) { |
| /* long add, then Z --> test (dst+src == 0) */ |
| return unop(Iop_1Uto32, |
| binop(Iop_CmpEQ32, |
| binop(Iop_Add32, cc_dep1, cc_dep2), |
| mkU32(0))); |
| } |
| |
| /*---------------- SUBL ----------------*/ |
| |
| if (isU32(cc_op, X86G_CC_OP_SUBL) && isU32(cond, X86CondZ)) { |
| /* long sub/cmp, then Z --> test dst==src */ |
| return unop(Iop_1Uto32, |
| binop(Iop_CmpEQ32, cc_dep1, cc_dep2)); |
| } |
| |
| if (isU32(cc_op, X86G_CC_OP_SUBL) && isU32(cond, X86CondL)) { |
| /* long sub/cmp, then L (signed less than) |
| --> test dst <s src */ |
| return unop(Iop_1Uto32, |
| binop(Iop_CmpLT32S, cc_dep1, cc_dep2)); |
| } |
| |
| if (isU32(cc_op, X86G_CC_OP_SUBL) && isU32(cond, X86CondLE)) { |
| /* long sub/cmp, then LE (signed less than or equal) |
| --> test dst <=s src */ |
| return unop(Iop_1Uto32, |
| binop(Iop_CmpLE32S, cc_dep1, cc_dep2)); |
| } |
| |
| if (isU32(cc_op, X86G_CC_OP_SUBL) && isU32(cond, X86CondBE)) { |
| /* long sub/cmp, then BE (unsigned less than or equal) |
| --> test dst <=u src */ |
| return unop(Iop_1Uto32, |
| binop(Iop_CmpLE32U, cc_dep1, cc_dep2)); |
| } |
| #if 0 |
| if (isU32(cc_op, X86G_CC_OP_SUBL) && isU32(cond, X86CondB)) { |
| /* long sub/cmp, then B (unsigned less than) |
| --> test dst <u src */ |
| return unop(Iop_1Uto32, |
| binop(Iop_CmpLT32U, cc_dst, cc_src)); |
| } |
| #endif |
| /*---------------- SUBW ----------------*/ |
| |
| if (isU32(cc_op, X86G_CC_OP_SUBW) && isU32(cond, X86CondZ)) { |
| /* byte sub/cmp, then Z --> test dst==src */ |
| return unop(Iop_1Uto32, |
| binop(Iop_CmpEQ16, |
| unop(Iop_32to16,cc_dep1), |
| unop(Iop_32to16,cc_dep2))); |
| } |
| |
| /*---------------- SUBB ----------------*/ |
| |
| if (isU32(cc_op, X86G_CC_OP_SUBB) && isU32(cond, X86CondZ)) { |
| /* byte sub/cmp, then Z --> test dst==src */ |
| return unop(Iop_1Uto32, |
| binop(Iop_CmpEQ8, |
| unop(Iop_32to8,cc_dep1), |
| unop(Iop_32to8,cc_dep2))); |
| } |
| |
| if (isU32(cc_op, X86G_CC_OP_SUBB) && isU32(cond, X86CondNZ)) { |
| /* byte sub/cmp, then NZ --> test dst!=src */ |
| return unop(Iop_1Uto32, |
| binop(Iop_CmpNE8, |
| unop(Iop_32to8,cc_dep1), |
| unop(Iop_32to8,cc_dep2))); |
| } |
| |
| if (isU32(cc_op, X86G_CC_OP_SUBB) && isU32(cond, X86CondNBE)) { |
| /* long sub/cmp, then NBE (unsigned greater than) |
| --> test src <=u dst */ |
| /* Note, args are opposite way round from the usual */ |
| return unop(Iop_1Uto32, |
| binop(Iop_CmpLT32U, |
| binop(Iop_And32,cc_dep2,mkU32(0xFF)), |
| binop(Iop_And32,cc_dep1,mkU32(0xFF)))); |
| } |
| |
| /*---------------- LOGICL ----------------*/ |
| |
| if (isU32(cc_op, X86G_CC_OP_LOGICL) && isU32(cond, X86CondZ)) { |
| /* long and/or/xor, then Z --> test dst==0 */ |
| return unop(Iop_1Uto32,binop(Iop_CmpEQ32, cc_dep1, mkU32(0))); |
| } |
| |
| if (isU32(cc_op, X86G_CC_OP_LOGICL) && isU32(cond, X86CondS)) { |
| /* long and/or/xor, then S --> test dst <s 0 */ |
| return unop(Iop_1Uto32,binop(Iop_CmpLT32S, cc_dep1, mkU32(0))); |
| } |
| |
| if (isU32(cc_op, X86G_CC_OP_LOGICL) && isU32(cond, X86CondLE)) { |
| /* long and/or/xor, then LE |
| This is pretty subtle. LOGIC sets SF and ZF according to the |
| result and makes OF be zero. LE computes (SZ ^ OF) | ZF, but |
| OF is zero, so this reduces to SZ | ZF -- which will be 1 iff |
| the result is <=signed 0. Hence ... |
| */ |
| return unop(Iop_1Uto32,binop(Iop_CmpLE32S, cc_dep1, mkU32(0))); |
| } |
| |
| /*---------------- LOGICW ----------------*/ |
| |
| if (isU32(cc_op, X86G_CC_OP_LOGICW) && isU32(cond, X86CondZ)) { |
| /* byte and/or/xor, then Z --> test dst==0 */ |
| return unop(Iop_1Uto32, |
| binop(Iop_CmpEQ32, binop(Iop_And32,cc_dep1,mkU32(0xFFFF)), |
| mkU32(0))); |
| } |
| |
| /*---------------- LOGICB ----------------*/ |
| |
| if (isU32(cc_op, X86G_CC_OP_LOGICB) && isU32(cond, X86CondZ)) { |
| /* byte and/or/xor, then Z --> test dst==0 */ |
| return unop(Iop_1Uto32, |
| binop(Iop_CmpEQ32, binop(Iop_And32,cc_dep1,mkU32(255)), |
| mkU32(0))); |
| } |
| |
| /*---------------- DECL ----------------*/ |
| |
| if (isU32(cc_op, X86G_CC_OP_DECL) && isU32(cond, X86CondZ)) { |
| /* dec L, then Z --> test dst == 0 */ |
| return unop(Iop_1Uto32,binop(Iop_CmpEQ32, cc_dep1, mkU32(0))); |
| } |
| |
| if (isU32(cc_op, X86G_CC_OP_DECL) && isU32(cond, X86CondS)) { |
| /* dec L, then S --> compare DST <s 0 */ |
| return unop(Iop_1Uto32,binop(Iop_CmpLT32S, cc_dep1, mkU32(0))); |
| } |
| |
| /*---------------- SHRL ----------------*/ |
| |
| if (isU32(cc_op, X86G_CC_OP_SHRL) && isU32(cond, X86CondZ)) { |
| /* SHRL, then Z --> test dep1 == 0 */ |
| return unop(Iop_1Uto32,binop(Iop_CmpEQ32, cc_dep1, mkU32(0))); |
| } |
| |
| /*---------------- COPY ----------------*/ |
| /* This can happen, as a result of x87 FP compares: "fcom ... ; |
| fnstsw %ax ; sahf ; jbe" for example. */ |
| |
| if (isU32(cc_op, X86G_CC_OP_COPY) && isU32(cond, X86CondBE)) { |
| /* COPY, then BE --> extract C and Z from dep1, and test (C |
| or Z == 1). */ |
| return |
| unop( |
| Iop_1Uto32, |
| binop( |
| Iop_CmpNE32, |
| binop( |
| Iop_And32, |
| binop( |
| Iop_Or32, |
| binop(Iop_Shr32, cc_dep1, mkU8(X86G_CC_SHIFT_C)), |
| binop(Iop_Shr32, cc_dep1, mkU8(X86G_CC_SHIFT_Z)) |
| ), |
| mkU32(1) |
| ), |
| mkU32(0) |
| ) |
| ); |
| } |
| |
| if (isU32(cc_op, X86G_CC_OP_COPY) && isU32(cond, X86CondB)) { |
| /* COPY, then B --> extract C dep1, and test (C == 1). */ |
| return |
| unop( |
| Iop_1Uto32, |
| binop( |
| Iop_CmpNE32, |
| binop( |
| Iop_And32, |
| binop(Iop_Shr32, cc_dep1, mkU8(X86G_CC_SHIFT_C)), |
| mkU32(1) |
| ), |
| mkU32(0) |
| ) |
| ); |
| } |
| |
| return NULL; |
| } |
| |
| # undef unop |
| # undef binop |
| # undef mkU32 |
| # undef mkU8 |
| |
| return NULL; |
| } |
| |
| |
| /*-----------------------------------------------------------*/ |
| /*--- Utility functions for x87 FPU conversions. ---*/ |
| /*-----------------------------------------------------------*/ |
| |
| |
| /* 80 and 64-bit floating point formats: |
| |
| 80-bit: |
| |
| S 0 0-------0 zero |
| S 0 0X------X denormals |
| S 1-7FFE 1X------X normals (all normals have leading 1) |
| S 7FFF 10------0 infinity |
| S 7FFF 10X-----X snan |
| S 7FFF 11X-----X qnan |
| |
| S is the sign bit. For runs X----X, at least one of the Xs must be |
| nonzero. Exponent is 15 bits, fractional part is 63 bits, and |
| there is an explicitly represented leading 1, and a sign bit, |
| giving 80 in total. |
| |
| 64-bit avoids the confusion of an explicitly represented leading 1 |
| and so is simpler: |
| |
| S 0 0------0 zero |
| S 0 X------X denormals |
| S 1-7FE any normals |
| S 7FF 0------0 infinity |
| S 7FF 0X-----X snan |
| S 7FF 1X-----X qnan |
| |
| Exponent is 11 bits, fractional part is 52 bits, and there is a |
| sign bit, giving 64 in total. |
| */ |
| |
| static inline Bool host_is_little_endian ( void ) |
| { |
| UInt x = 0x76543210; |
| UChar* p = (UChar*)(&x); |
| return (*p == 0x10); |
| } |
| |
| /* CALLED FROM GENERATED CODE: CLEAN HELPER */ |
| UInt x86g_calculate_FXAM ( UInt tag, ULong dbl ) |
| { |
| Bool mantissaIsZero; |
| Int bexp; |
| UChar sign; |
| UInt c1; |
| UChar* f64; |
| |
| vassert(host_is_little_endian()); |
| |
| /* vex_printf("calculate_FXAM ( %d, %llx ) .. ", tag, dbl ); */ |
| |
| f64 = (UChar*)(&dbl); |
| sign = (f64[7] >> 7) & 1; |
| |
| /* First off, if the tag indicates the register was empty, |
| return 1,0,sign,1 */ |
| if (tag == 0) { |
| /* vex_printf("Empty\n"); */ |
| return X86G_FC_MASK_C3 | 0 | sign | X86G_FC_MASK_C0; |
| } |
| |
| bexp = (f64[7] << 4) | ((f64[6] >> 4) & 0x0F); |
| bexp &= 0x7FF; |
| |
| c1 = ((UInt)sign) << 9; |
| |
| mantissaIsZero |
| = (f64[6] & 0x0F) == 0 |
| && (f64[5] | f64[4] | f64[3] | f64[2] | f64[1] | f64[0]) == 0; |
| |
| /* If both exponent and mantissa are zero, the value is zero. |
| Return 1,0,sign,0. */ |
| if (bexp == 0 && mantissaIsZero) { |
| /* vex_printf("Zero\n"); */ |
| return X86G_FC_MASK_C3 | 0 | sign | 0; |
| } |
| |
| /* If exponent is zero but mantissa isn't, it's a denormal. |
| Return 1,1,sign,0. */ |
| if (bexp == 0 && !mantissaIsZero) { |
| /* vex_printf("Denormal\n"); */ |
| return X86G_FC_MASK_C3 | X86G_FC_MASK_C2 | sign | 0; |
| } |
| |
| /* If the exponent is 7FF and the mantissa is zero, this is an infinity. |
| Return 0,1,sign,1. */ |
| if (bexp == 0x7FF && mantissaIsZero) { |
| /* vex_printf("Inf\n"); */ |
| return 0 | X86G_FC_MASK_C2 | sign | X86G_FC_MASK_C0; |
| } |
| |
| /* If the exponent is 7FF and the mantissa isn't zero, this is a NaN. |
| Return 0,0,sign,1. */ |
| if (bexp == 0x7FF && !mantissaIsZero) { |
| /* vex_printf("NaN\n"); */ |
| return 0 | 0 | sign | X86G_FC_MASK_C0; |
| } |
| |
| /* Uh, ok, we give up. It must be a normal finite number. |
| Return 0,1,sign,0. |
| */ |
| /* vex_printf("normal\n"); */ |
| return 0 | X86G_FC_MASK_C2 | sign | 0; |
| } |
| |
| |
| ///////////////////////////////////////////////////////////////// |
| |
| static inline |
| UInt read_bit_array ( UChar* arr, UInt n ) |
| { |
| UChar c = arr[n >> 3]; |
| c >>= (n&7); |
| return c & 1; |
| } |
| |
| static inline |
| void write_bit_array ( UChar* arr, UInt n, UInt b ) |
| { |
| UChar c = arr[n >> 3]; |
| c &= ~(1 << (n&7)); |
| c |= ((b&1) << (n&7)); |
| arr[n >> 3] = c; |
| } |
| |
| |
| /* Convert a IEEE754 double (64-bit) into an x87 extended double |
| (80-bit), mimicing the hardware fairly closely. Both numbers are |
| stored little-endian. Limitations, all of which could be fixed, |
| given some level of hassle: |
| |
| * Identity of NaNs is not preserved. |
| |
| See comments in the code for more details. |
| */ |
| static void convert_f64le_to_f80le ( /*IN*/UChar* f64, /*OUT*/UChar* f80 ) |
| { |
| Bool mantissaIsZero; |
| Int bexp, i, j, shift; |
| UChar sign; |
| |
| sign = (f64[7] >> 7) & 1; |
| bexp = (f64[7] << 4) | ((f64[6] >> 4) & 0x0F); |
| bexp &= 0x7FF; |
| |
| mantissaIsZero = False; |
| if (bexp == 0 || bexp == 0x7FF) { |
| /* We'll need to know whether or not the mantissa (bits 51:0) is |
| all zeroes in order to handle these cases. So figure it |
| out. */ |
| mantissaIsZero |
| = (f64[6] & 0x0F) == 0 |
| && f64[5] == 0 && f64[4] == 0 && f64[3] == 0 |
| && f64[2] == 0 && f64[1] == 0 && f64[0] == 0; |
| } |
| |
| /* If the exponent is zero, either we have a zero or a denormal. |
| Produce a zero. This is a hack in that it forces denormals to |
| zero. Could do better. */ |
| if (bexp == 0) { |
| f80[9] = sign << 7; |
| f80[8] = f80[7] = f80[6] = f80[5] = f80[4] |
| = f80[3] = f80[2] = f80[1] = f80[0] = 0; |
| |
| if (mantissaIsZero) |
| /* It really is zero, so that's all we can do. */ |
| return; |
| |
| /* There is at least one 1-bit in the mantissa. So it's a |
| potentially denormalised double -- but we can produce a |
| normalised long double. Count the leading zeroes in the |
| mantissa so as to decide how much to bump the exponent down |
| by. Note, this is SLOW. */ |
| shift = 0; |
| for (i = 51; i >= 0; i--) { |
| if (read_bit_array(f64, i)) |
| break; |
| shift++; |
| } |
| |
| /* and copy into place as many bits as we can get our hands on. */ |
| j = 63; |
| for (i = 51 - shift; i >= 0; i--) { |
| write_bit_array( f80, j, |
| read_bit_array( f64, i ) ); |
| j--; |
| } |
| |
| /* Set the exponent appropriately, and we're done. */ |
| bexp -= shift; |
| bexp += (16383 - 1023); |
| f80[9] = (sign << 7) | ((bexp >> 8) & 0xFF); |
| f80[8] = bexp & 0xFF; |
| return; |
| } |
| |
| /* If the exponent is 7FF, this is either an Infinity, a SNaN or |
| QNaN, as determined by examining bits 51:0, thus: |
| 0 ... 0 Inf |
| 0X ... X SNaN |
| 1X ... X QNaN |
| where at least one of the Xs is not zero. |
| */ |
| if (bexp == 0x7FF) { |
| if (mantissaIsZero) { |
| /* Produce an appropriately signed infinity: |
| S 1--1 (15) 1 0--0 (63) |
| */ |
| f80[9] = (sign << 7) | 0x7F; |
| f80[8] = 0xFF; |
| f80[7] = 0x80; |
| f80[6] = f80[5] = f80[4] = f80[3] |
| = f80[2] = f80[1] = f80[0] = 0; |
| return; |
| } |
| /* So it's either a QNaN or SNaN. Distinguish by considering |
| bit 51. Note, this destroys all the trailing bits |
| (identity?) of the NaN. IEEE754 doesn't require preserving |
| these (it only requires that there be one QNaN value and one |
| SNaN value), but x87 does seem to have some ability to |
| preserve them. Anyway, here, the NaN's identity is |
| destroyed. Could be improved. */ |
| if (f64[6] & 8) { |
| /* QNaN. Make a QNaN: |
| S 1--1 (15) 1 1--1 (63) |
| */ |
| f80[9] = (sign << 7) | 0x7F; |
| f80[8] = 0xFF; |
| f80[7] = 0xFF; |
| f80[6] = f80[5] = f80[4] = f80[3] |
| = f80[2] = f80[1] = f80[0] = 0xFF; |
| } else { |
| /* SNaN. Make a SNaN: |
| S 1--1 (15) 0 1--1 (63) |
| */ |
| f80[9] = (sign << 7) | 0x7F; |
| f80[8] = 0xFF; |
| f80[7] = 0x7F; |
| f80[6] = f80[5] = f80[4] = f80[3] |
| = f80[2] = f80[1] = f80[0] = 0xFF; |
| } |
| return; |
| } |
| |
| /* It's not a zero, denormal, infinity or nan. So it must be a |
| normalised number. Rebias the exponent and build the new |
| number. */ |
| bexp += (16383 - 1023); |
| |
| f80[9] = (sign << 7) | ((bexp >> 8) & 0xFF); |
| f80[8] = bexp & 0xFF; |
| f80[7] = (1 << 7) | ((f64[6] << 3) & 0x78) | ((f64[5] >> 5) & 7); |
| f80[6] = ((f64[5] << 3) & 0xF8) | ((f64[4] >> 5) & 7); |
| f80[5] = ((f64[4] << 3) & 0xF8) | ((f64[3] >> 5) & 7); |
| f80[4] = ((f64[3] << 3) & 0xF8) | ((f64[2] >> 5) & 7); |
| f80[3] = ((f64[2] << 3) & 0xF8) | ((f64[1] >> 5) & 7); |
| f80[2] = ((f64[1] << 3) & 0xF8) | ((f64[0] >> 5) & 7); |
| f80[1] = ((f64[0] << 3) & 0xF8); |
| f80[0] = 0; |
| } |
| |
| |
| ///////////////////////////////////////////////////////////////// |
| |
| /* Convert a x87 extended double (80-bit) into an IEEE 754 double |
| (64-bit), mimicking the hardware fairly closely. Both numbers are |
| stored little-endian. Limitations, both of which could be fixed, |
| given some level of hassle: |
| |
| * Rounding following truncation could be a bit better. |
| |
| * Identity of NaNs is not preserved. |
| |
| See comments in the code for more details. |
| */ |
| static void convert_f80le_to_f64le ( /*IN*/UChar* f80, /*OUT*/UChar* f64 ) |
| { |
| Bool isInf; |
| Int bexp, i, j; |
| UChar sign; |
| |
| sign = (f80[9] >> 7) & 1; |
| bexp = (((UInt)f80[9]) << 8) | (UInt)f80[8]; |
| bexp &= 0x7FFF; |
| |
| /* If the exponent is zero, either we have a zero or a denormal. |
| But an extended precision denormal becomes a double precision |
| zero, so in either case, just produce the appropriately signed |
| zero. */ |
| if (bexp == 0) { |
| f64[7] = sign << 7; |
| f64[6] = f64[5] = f64[4] = f64[3] = f64[2] = f64[1] = f64[0] = 0; |
| return; |
| } |
| |
| /* If the exponent is 7FFF, this is either an Infinity, a SNaN or |
| QNaN, as determined by examining bits 62:0, thus: |
| 0 ... 0 Inf |
| 0X ... X SNaN |
| 1X ... X QNaN |
| where at least one of the Xs is not zero. |
| */ |
| if (bexp == 0x7FFF) { |
| isInf = (f80[7] & 0x7F) == 0 |
| && f80[6] == 0 && f80[5] == 0 && f80[4] == 0 |
| && f80[3] == 0 && f80[2] == 0 && f80[1] == 0 && f80[0] == 0; |
| if (isInf) { |
| if (0 == (f80[7] & 0x80)) |
| goto wierd_NaN; |
| /* Produce an appropriately signed infinity: |
| S 1--1 (11) 0--0 (52) |
| */ |
| f64[7] = (sign << 7) | 0x7F; |
| f64[6] = 0xF0; |
| f64[5] = f64[4] = f64[3] = f64[2] = f64[1] = f64[0] = 0; |
| return; |
| } |
| /* So it's either a QNaN or SNaN. Distinguish by considering |
| bit 62. Note, this destroys all the trailing bits |
| (identity?) of the NaN. IEEE754 doesn't require preserving |
| these (it only requires that there be one QNaN value and one |
| SNaN value), but x87 does seem to have some ability to |
| preserve them. Anyway, here, the NaN's identity is |
| destroyed. Could be improved. */ |
| if (f80[8] & 0x40) { |
| /* QNaN. Make a QNaN: |
| S 1--1 (11) 1 1--1 (51) |
| */ |
| f64[7] = (sign << 7) | 0x7F; |
| f64[6] = 0xFF; |
| f64[5] = f64[4] = f64[3] = f64[2] = f64[1] = f64[0] = 0xFF; |
| } else { |
| /* SNaN. Make a SNaN: |
| S 1--1 (11) 0 1--1 (51) |
| */ |
| f64[7] = (sign << 7) | 0x7F; |
| f64[6] = 0xF7; |
| f64[5] = f64[4] = f64[3] = f64[2] = f64[1] = f64[0] = 0xFF; |
| } |
| return; |
| } |
| |
| /* If it's not a Zero, NaN or Inf, and the integer part (bit 62) is |
| zero, the x87 FPU appears to consider the number denormalised |
| and converts it to a QNaN. */ |
| if (0 == (f80[7] & 0x80)) { |
| wierd_NaN: |
| /* Strange hardware QNaN: |
| S 1--1 (11) 1 0--0 (51) |
| */ |
| /* On a PIII, these QNaNs always appear with sign==1. I have |
| no idea why. */ |
| f64[7] = (1 /*sign*/ << 7) | 0x7F; |
| f64[6] = 0xF8; |
| f64[5] = f64[4] = f64[3] = f64[2] = f64[1] = f64[0] = 0; |
| return; |
| } |
| |
| /* It's not a zero, denormal, infinity or nan. So it must be a |
| normalised number. Rebias the exponent and consider. */ |
| bexp -= (16383 - 1023); |
| if (bexp >= 0x7FF) { |
| /* It's too big for a double. Construct an infinity. */ |
| f64[7] = (sign << 7) | 0x7F; |
| f64[6] = 0xF0; |
| f64[5] = f64[4] = f64[3] = f64[2] = f64[1] = f64[0] = 0; |
| return; |
| } |
| |
| if (bexp <= 0) { |
| /* It's too small for a normalised double. First construct a |
| zero and then see if it can be improved into a denormal. */ |
| f64[7] = sign << 7; |
| f64[6] = f64[5] = f64[4] = f64[3] = f64[2] = f64[1] = f64[0] = 0; |
| |
| if (bexp < -52) |
| /* Too small even for a denormal. */ |
| return; |
| |
| /* Ok, let's make a denormal. Note, this is SLOW. */ |
| /* Copy bits 63, 62, 61, etc of the src mantissa into the dst, |
| indexes 52+bexp, 51+bexp, etc, until k+bexp < 0. */ |
| /* bexp is in range -52 .. 0 inclusive */ |
| for (i = 63; i >= 0; i--) { |
| j = i - 12 + bexp; |
| if (j < 0) break; |
| /* We shouldn't really call vassert from generated code. */ |
| vassert(j >= 0 && j < 52); |
| write_bit_array ( f64, |
| j, |
| read_bit_array ( f80, i ) ); |
| } |
| /* and now we might have to round ... */ |
| if (read_bit_array(f80, 10+1 - bexp) == 1) |
| goto do_rounding; |
| |
| return; |
| } |
| |
| /* Ok, it's a normalised number which is representable as a double. |
| Copy the exponent and mantissa into place. */ |
| /* |
| for (i = 0; i < 52; i++) |
| write_bit_array ( f64, |
| i, |
| read_bit_array ( f80, i+11 ) ); |
| */ |
| f64[0] = (f80[1] >> 3) | (f80[2] << 5); |
| f64[1] = (f80[2] >> 3) | (f80[3] << 5); |
| f64[2] = (f80[3] >> 3) | (f80[4] << 5); |
| f64[3] = (f80[4] >> 3) | (f80[5] << 5); |
| f64[4] = (f80[5] >> 3) | (f80[6] << 5); |
| f64[5] = (f80[6] >> 3) | (f80[7] << 5); |
| |
| f64[6] = ((bexp << 4) & 0xF0) | ((f80[7] >> 3) & 0x0F); |
| |
| f64[7] = (sign << 7) | ((bexp >> 4) & 0x7F); |
| |
| /* Now consider any rounding that needs to happen as a result of |
| truncating the mantissa. */ |
| if (f80[1] & 4) /* read_bit_array(f80, 10) == 1) */ { |
| |
| /* If the bottom bits of f80 are "100 0000 0000", then the |
| infinitely precise value is deemed to be mid-way between the |
| two closest representable values. Since we're doing |
| round-to-nearest (the default mode), in that case it is the |
| bit immediately above which indicates whether we should round |
| upwards or not -- if 0, we don't. All that is encapsulated |
| in the following simple test. */ |
| if ((f80[1] & 0xF) == 4/*0100b*/ && f80[0] == 0) |
| return; |
| |
| do_rounding: |
| /* Round upwards. This is a kludge. Once in every 2^24 |
| roundings (statistically) the bottom three bytes are all 0xFF |
| and so we don't round at all. Could be improved. */ |
| if (f64[0] != 0xFF) { |
| f64[0]++; |
| } |
| else |
| if (f64[0] == 0xFF && f64[1] != 0xFF) { |
| f64[0] = 0; |
| f64[1]++; |
| } |
| else |
| if (f64[0] == 0xFF && f64[1] == 0xFF && f64[2] != 0xFF) { |
| f64[0] = 0; |
| f64[1] = 0; |
| f64[2]++; |
| } |
| /* else we don't round, but we should. */ |
| } |
| } |
| |
| |
| /* CALLED FROM GENERATED CODE */ |
| /* DIRTY HELPER (reads guest memory) */ |
| ULong x86g_loadF80le ( UInt addrU ) |
| { |
| ULong f64; |
| convert_f80le_to_f64le ( (UChar*)addrU, (UChar*)&f64 ); |
| return f64; |
| } |
| |
| /* CALLED FROM GENERATED CODE */ |
| /* DIRTY HELPER (writes guest memory) */ |
| void x86g_storeF80le ( UInt addrU, ULong f64 ) |
| { |
| convert_f64le_to_f80le( (UChar*)&f64, (UChar*)addrU ); |
| } |
| |
| |
| /*----------------------------------------------*/ |
| /*--- The exported fns .. ---*/ |
| /*----------------------------------------------*/ |
| |
| /* Layout of the real x87 state. */ |
| |
| typedef |
| struct { |
| UShort env[14]; |
| UChar reg[80]; |
| } |
| Fpu_State; |
| |
| /* Offsets, in 16-bit ints, into the FPU environment (env) area. */ |
| #define FP_ENV_CTRL 0 |
| #define FP_ENV_STAT 2 |
| #define FP_ENV_TAG 4 |
| #define FP_ENV_IP 6 /* and 7 */ |
| #define FP_ENV_CS 8 |
| #define FP_ENV_OPOFF 10 /* and 11 */ |
| #define FP_ENV_OPSEL 12 |
| #define FP_REG(ii) (10*(7-(ii))) |
| |
| |
| /* CLEAN HELPER */ |
| /* fpucw[15:0] contains a x87 native format FPU control word. |
| Extract from it the required FPROUND value and any resulting |
| emulation warning, and return (warn << 32) | fpround value. |
| */ |
| ULong x86g_check_fldcw ( UInt fpucw ) |
| { |
| /* Decide on a rounding mode. fpucw[11:10] holds it. */ |
| /* NOTE, encoded exactly as per enum IRRoundingMode. */ |
| UInt rmode = (fpucw >> 10) & 3; |
| |
| /* Detect any required emulation warnings. */ |
| VexEmWarn ew = EmWarn_NONE; |
| |
| if ((fpucw & 0x3F) != 0x3F) { |
| /* unmasked exceptions! */ |
| ew = EmWarn_X86_x87exns; |
| } |
| else |
| if (((fpucw >> 8) & 3) != 3) { |
| /* unsupported precision */ |
| ew = EmWarn_X86_x87precision; |
| } |
| |
| return (((ULong)ew) << 32) | ((ULong)rmode); |
| } |
| |
| /* CLEAN HELPER */ |
| /* Given fpround as an IRRoundingMode value, create a suitable x87 |
| native format FPU control word. */ |
| UInt x86g_create_fpucw ( UInt fpround ) |
| { |
| fpround &= 3; |
| return 0x037F | (fpround << 10); |
| } |
| |
| |
| /* CLEAN HELPER */ |
| /* mxcsr[15:0] contains a SSE native format MXCSR value. |
| Extract from it the required SSEROUND value and any resulting |
| emulation warning, and return (warn << 32) | sseround value. |
| */ |
| ULong x86g_check_ldmxcsr ( UInt mxcsr ) |
| { |
| /* Decide on a rounding mode. mxcsr[14:13] holds it. */ |
| /* NOTE, encoded exactly as per enum IRRoundingMode. */ |
| UInt rmode = (mxcsr >> 13) & 3; |
| |
| /* Detect any required emulation warnings. */ |
| VexEmWarn ew = EmWarn_NONE; |
| |
| if ((mxcsr & 0x1F80) != 0x1F80) { |
| /* unmasked exceptions! */ |
| ew = EmWarn_X86_sseExns; |
| } |
| else |
| if (mxcsr & (1<<15)) { |
| /* FZ is set */ |
| ew = EmWarn_X86_fz; |
| } |
| else |
| if (mxcsr & (1<<6)) { |
| /* DAZ is set */ |
| ew = EmWarn_X86_daz; |
| } |
| |
| return (((ULong)ew) << 32) | ((ULong)rmode); |
| } |
| |
| |
| /* CLEAN HELPER */ |
| /* Given sseround as an IRRoundingMode value, create a suitable SSE |
| native format MXCSR value. */ |
| UInt x86g_create_mxcsr ( UInt sseround ) |
| { |
| sseround &= 3; |
| return 0x1F80 | (sseround << 13); |
| } |
| |
| |
| /* CALLED FROM GENERATED CODE */ |
| /* DIRTY HELPER (writes guest state) */ |
| /* Initialise the x87 FPU state as per 'finit'. */ |
| void x86g_dirtyhelper_FINIT ( VexGuestX86State* gst ) |
| { |
| Int i; |
| gst->guest_FTOP = 0; |
| for (i = 0; i < 8; i++) { |
| gst->guest_FPTAG[i] = 0; /* empty */ |
| gst->guest_FPREG[i] = 0; /* IEEE754 64-bit zero */ |
| } |
| gst->guest_FPROUND = (UInt)Irrm_NEAREST; |
| gst->guest_FC3210 = 0; |
| } |
| |
| |
| /* This is used to implement both 'frstor' and 'fldenv'. The latter |
| appears to differ from the former only in that the 8 FP registers |
| themselves are not transferred into the guest state. */ |
| static |
| VexEmWarn do_put_x87 ( Bool moveRegs, |
| /*IN*/UChar* x87_state, |
| /*OUT*/VexGuestX86State* vex_state ) |
| { |
| Int stno, preg; |
| UInt tag; |
| Double* vexRegs = (Double*)(&vex_state->guest_FPREG[0]); |
| UChar* vexTags = (UChar*)(&vex_state->guest_FPTAG[0]); |
| Fpu_State* x87 = (Fpu_State*)x87_state; |
| UInt ftop = (x87->env[FP_ENV_STAT] >> 11) & 7; |
| UInt tagw = x87->env[FP_ENV_TAG]; |
| UInt fpucw = x87->env[FP_ENV_CTRL]; |
| UInt c3210 = x87->env[FP_ENV_STAT] & 0x4700; |
| VexEmWarn ew; |
| UInt fpround; |
| ULong pair; |
| |
| /* Copy registers and tags */ |
| for (stno = 0; stno < 8; stno++) { |
| preg = (stno + ftop) & 7; |
| tag = (tagw >> (2*preg)) & 3; |
| if (tag == 3) { |
| /* register is empty */ |
| /* hmm, if it's empty, does it still get written? Probably |
| safer to say it does. If we don't, memcheck could get out |
| of sync, in that it thinks all FP registers are defined by |
| this helper, but in reality some have not been updated. */ |
| if (True || moveRegs) |
| vexRegs[preg] = 0.0; |
| vexTags[preg] = 0; |
| } else { |
| /* register is non-empty */ |
| if (moveRegs) |
| convert_f80le_to_f64le( &x87->reg[10*stno], |
| (UChar*)&vexRegs[preg] ); |
| vexTags[preg] = 1; |
| } |
| } |
| |
| /* stack pointer */ |
| vex_state->guest_FTOP = ftop; |
| |
| /* status word */ |
| vex_state->guest_FC3210 = c3210; |
| |
| /* handle the control word, setting FPROUND and detecting any |
| emulation warnings. */ |
| pair = x86g_check_fldcw ( (UInt)fpucw ); |
| fpround = (UInt)pair; |
| ew = (VexEmWarn)(pair >> 32); |
| |
| vex_state->guest_FPROUND = fpround & 3; |
| |
| /* emulation warnings --> caller */ |
| return ew; |
| } |
| |
| |
| /* Create an x87 FPU state from the guest state, as close as |
| we can approximate it. */ |
| static |
| void do_get_x87 ( /*IN*/VexGuestX86State* vex_state, |
| /*OUT*/UChar* x87_state ) |
| { |
| Int i, stno, preg; |
| UInt tagw; |
| Double* vexRegs = (Double*)(&vex_state->guest_FPREG[0]); |
| UChar* vexTags = (UChar*)(&vex_state->guest_FPTAG[0]); |
| Fpu_State* x87 = (Fpu_State*)x87_state; |
| UInt ftop = vex_state->guest_FTOP; |
| UInt c3210 = vex_state->guest_FC3210; |
| |
| for (i = 0; i < 14; i++) |
| x87->env[i] = 0; |
| |
| x87->env[1] = x87->env[3] = x87->env[5] = x87->env[13] = 0xFFFF; |
| x87->env[FP_ENV_STAT] = ((ftop & 7) << 11) | (c3210 & 0x4700); |
| x87->env[FP_ENV_CTRL] |
| = (UShort)x86g_create_fpucw( vex_state->guest_FPROUND ); |
| |
| /* Dump the register stack in ST order. */ |
| tagw = 0; |
| for (stno = 0; stno < 8; stno++) { |
| preg = (stno + ftop) & 7; |
| if (vexTags[preg] == 0) { |
| /* register is empty */ |
| tagw |= (3 << (2*preg)); |
| convert_f64le_to_f80le( (UChar*)&vexRegs[preg], |
| &x87->reg[10*stno] ); |
| } else { |
| /* register is full. */ |
| tagw |= (0 << (2*preg)); |
| convert_f64le_to_f80le( (UChar*)&vexRegs[preg], |
| &x87->reg[10*stno] ); |
| } |
| } |
| x87->env[FP_ENV_TAG] = tagw; |
| } |
| |
| |
| /* VISIBLE TO LIBVEX CLIENT */ |
| UInt LibVEX_GuestX86_get_eflags ( /*IN*/VexGuestX86State* vex_state ) |
| { |
| UInt eflags = x86g_calculate_eflags_all( |
| vex_state->guest_CC_OP, |
| vex_state->guest_CC_DEP1, |
| vex_state->guest_CC_DEP2, |
| vex_state->guest_CC_NDEP |
| ); |
| UInt dflag = vex_state->guest_DFLAG; |
| vassert(dflag == 1 || dflag == 0xFFFFFFFF); |
| if (dflag == 0xFFFFFFFF) |
| eflags |= (1<<10); |
| if (vex_state->guest_IDFLAG == 1) |
| eflags |= (1<<21); |
| |
| return eflags; |
| } |
| |
| /* VISIBLE TO LIBVEX CLIENT */ |
| void LibVEX_GuestX86_initialise ( /*OUT*/VexGuestX86State* vex_state ) |
| { |
| vex_state->guest_EAX = 0; |
| vex_state->guest_ECX = 0; |
| vex_state->guest_EDX = 0; |
| vex_state->guest_EBX = 0; |
| vex_state->guest_ESP = 0; |
| vex_state->guest_EBP = 0; |
| vex_state->guest_ESI = 0; |
| vex_state->guest_EDI = 0; |
| |
| vex_state->guest_CC_OP = X86G_CC_OP_COPY; |
| vex_state->guest_CC_DEP1 = 0; |
| vex_state->guest_CC_DEP2 = 0; |
| vex_state->guest_CC_NDEP = 0; |
| vex_state->guest_DFLAG = 1; /* forwards */ |
| vex_state->guest_IDFLAG = 0; |
| |
| vex_state->guest_EIP = 0; |
| |
| /* Initialise the simulated FPU */ |
| x86g_dirtyhelper_FINIT( vex_state ); |
| |
| /* Initialse the SSE state. */ |
| # define SSEZERO(_xmm) _xmm[0]=_xmm[1]=_xmm[2]=_xmm[3] = 0; |
| |
| vex_state->guest_SSEROUND = (UInt)Irrm_NEAREST; |
| SSEZERO(vex_state->guest_XMM0); |
| SSEZERO(vex_state->guest_XMM1); |
| SSEZERO(vex_state->guest_XMM2); |
| SSEZERO(vex_state->guest_XMM3); |
| SSEZERO(vex_state->guest_XMM4); |
| SSEZERO(vex_state->guest_XMM5); |
| SSEZERO(vex_state->guest_XMM6); |
| SSEZERO(vex_state->guest_XMM7); |
| |
| # undef SSEZERO |
| |
| vex_state->guest_CS = 0; |
| vex_state->guest_DS = 0; |
| vex_state->guest_ES = 0; |
| vex_state->guest_FS = 0; |
| vex_state->guest_GS = 0; |
| vex_state->guest_SS = 0; |
| vex_state->guest_LDT = 0; |
| vex_state->guest_GDT = 0; |
| |
| vex_state->guest_EMWARN = EmWarn_NONE; |
| } |
| |
| |
| /*----------------------------------------------*/ |
| /*--- Misc integer/fp helpers ---*/ |
| /*----------------------------------------------*/ |
| |
| /* CALLED FROM GENERATED CODE: CLEAN HELPER */ |
| /* Calculate both flags and value result for rotate right |
| through the carry bit. Result in low 32 bits, |
| new flags (OSZACP) in high 32 bits. |
| */ |
| ULong x86g_calculate_RCR ( UInt arg, UInt rot_amt, UInt eflags_in, UInt sz ) |
| { |
| UInt tempCOUNT = rot_amt & 0x1F, cf=0, of=0, tempcf; |
| |
| switch (sz) { |
| case 4: |
| cf = (eflags_in >> X86G_CC_SHIFT_C) & 1; |
| of = ((arg >> 31) ^ cf) & 1; |
| while (tempCOUNT > 0) { |
| tempcf = arg & 1; |
| arg = (arg >> 1) | (cf << 31); |
| cf = tempcf; |
| tempCOUNT--; |
| } |
| break; |
| case 2: |
| while (tempCOUNT >= 17) tempCOUNT -= 17; |
| cf = (eflags_in >> X86G_CC_SHIFT_C) & 1; |
| of = ((arg >> 15) ^ cf) & 1; |
| while (tempCOUNT > 0) { |
| tempcf = arg & 1; |
| arg = ((arg >> 1) & 0x7FFF) | (cf << 15); |
| cf = tempcf; |
| tempCOUNT--; |
| } |
| break; |
| case 1: |
| while (tempCOUNT >= 9) tempCOUNT -= 9; |
| cf = (eflags_in >> X86G_CC_SHIFT_C) & 1; |
| of = ((arg >> 7) ^ cf) & 1; |
| while (tempCOUNT > 0) { |
| tempcf = arg & 1; |
| arg = ((arg >> 1) & 0x7F) | (cf << 7); |
| cf = tempcf; |
| tempCOUNT--; |
| } |
| break; |
| default: |
| vpanic("calculate_RCR: invalid size"); |
| } |
| |
| cf &= 1; |
| of &= 1; |
| eflags_in &= ~(X86G_CC_MASK_C | X86G_CC_MASK_O); |
| eflags_in |= (cf << X86G_CC_SHIFT_C) | (of << X86G_CC_SHIFT_O); |
| |
| return (((ULong)eflags_in) << 32) | ((ULong)arg); |
| } |
| |
| |
| /* CALLED FROM GENERATED CODE */ |
| /* DIRTY HELPER (modifies guest state) */ |
| /* Claim to be a P55C (Intel Pentium/MMX) */ |
| void x86g_dirtyhelper_CPUID_sse0 ( VexGuestX86State* st ) |
| { |
| switch (st->guest_EAX) { |
| case 0: |
| st->guest_EAX = 0x1; |
| st->guest_EBX = 0x756e6547; |
| st->guest_ECX = 0x6c65746e; |
| st->guest_EDX = 0x49656e69; |
| break; |
| default: |
| st->guest_EAX = 0x543; |
| st->guest_EBX = 0x0; |
| st->guest_ECX = 0x0; |
| st->guest_EDX = 0x8001bf; |
| break; |
| } |
| } |
| |
| /* CALLED FROM GENERATED CODE */ |
| /* DIRTY HELPER (modifies guest state) */ |
| /* Claim to be the following SSE1-capable CPU: |
| vendor_id : GenuineIntel |
| cpu family : 6 |
| model : 11 |
| model name : Intel(R) Pentium(R) III CPU family 1133MHz |
| stepping : 1 |
| cpu MHz : 1131.013 |
| cache size : 512 KB |
| */ |
| void x86g_dirtyhelper_CPUID_sse1 ( VexGuestX86State* st ) |
| { |
| switch (st->guest_EAX) { |
| case 0: |
| st->guest_EAX = 0x00000002; |
| st->guest_EBX = 0x756e6547; |
| st->guest_ECX = 0x6c65746e; |
| st->guest_EDX = 0x49656e69; |
| break; |
| case 1: |
| st->guest_EAX = 0x000006b1; |
| st->guest_EBX = 0x00000004; |
| st->guest_ECX = 0x00000000; |
| st->guest_EDX = 0x0383fbff; |
| break; |
| default: |
| st->guest_EAX = 0x03020101; |
| st->guest_EBX = 0x00000000; |
| st->guest_ECX = 0x00000000; |
| st->guest_EDX = 0x0c040883; |
| break; |
| } |
| } |
| |
| /* Claim to be the following SSE2-capable CPU: |
| vendor_id : GenuineIntel |
| cpu family : 15 |
| model : 2 |
| model name : Intel(R) Pentium(R) 4 CPU 2.40GHz |
| stepping : 7 |
| cpu MHz : 2394.234 |
| cache size : 512 KB |
| */ |
| void x86g_dirtyhelper_CPUID_sse2 ( VexGuestX86State* st ) |
| { |
| switch (st->guest_EAX) { |
| case 0: |
| st->guest_EAX = 0x00000002; |
| st->guest_EBX = 0x756e6547; |
| st->guest_ECX = 0x6c65746e; |
| st->guest_EDX = 0x49656e69; |
| break; |
| case 1: |
| st->guest_EAX = 0x00000f27; |
| st->guest_EBX = 0x00010809; |
| st->guest_ECX = 0x00004400; |
| st->guest_EDX = 0xbfebfbff; |
| break; |
| default: |
| st->guest_EAX = 0x665b5101; |
| st->guest_EBX = 0x00000000; |
| st->guest_ECX = 0x00000000; |
| st->guest_EDX = 0x007b7040; |
| break; |
| } |
| } |
| |
| |
| /* CALLED FROM GENERATED CODE */ |
| /* DIRTY HELPER (reads guest state, writes guest mem) */ |
| void x86g_dirtyhelper_FXSAVE ( VexGuestX86State* gst, HWord addr ) |
| { |
| /* Somewhat roundabout, but at least it's simple. */ |
| Fpu_State tmp; |
| UShort* addrS = (UShort*)addr; |
| UChar* addrC = (UChar*)addr; |
| U128* xmm = (U128*)(addr + 160); |
| UInt mxcsr; |
| UShort fp_tags; |
| UChar summary_tags; |
| Int r, stno; |
| UShort *srcS, *dstS; |
| |
| do_get_x87( gst, (UChar*)&tmp ); |
| mxcsr = x86g_create_mxcsr( gst->guest_SSEROUND ); |
| |
| /* Now build the proper fxsave image from the x87 image we just |
| made. */ |
| |
| addrS[0] = tmp.env[FP_ENV_CTRL]; /* FCW: fpu control word */ |
| addrS[1] = tmp.env[FP_ENV_STAT]; /* FCW: fpu status word */ |
| |
| /* set addrS[2] in an endian-independent way */ |
| summary_tags = 0; |
| fp_tags = tmp.env[FP_ENV_TAG]; |
| for (r = 0; r < 8; r++) { |
| if ( ((fp_tags >> (2*r)) & 3) != 3 ) |
| summary_tags |= (1 << r); |
| } |
| addrC[4] = summary_tags; /* FTW: tag summary byte */ |
| addrC[5] = 0; /* pad */ |
| |
| addrS[3] = 0; /* FOP: fpu opcode (bogus) */ |
| addrS[4] = 0; |
| addrS[5] = 0; /* FPU IP (bogus) */ |
| addrS[6] = 0; /* FPU IP's segment selector (bogus) (although we |
| could conceivably dump %CS here) */ |
| |
| addrS[7] = 0; /* Intel reserved */ |
| |
| addrS[8] = 0; /* FPU DP (operand pointer) (bogus) */ |
| addrS[9] = 0; /* FPU DP (operand pointer) (bogus) */ |
| addrS[10] = 0; /* segment selector for above operand pointer; %DS |
| perhaps? */ |
| addrS[11] = 0; /* Intel reserved */ |
| |
| addrS[12] = (UShort)mxcsr; /* MXCSR */ |
| addrS[13] = (UShort)(mxcsr >> 16); |
| |
| addrS[14] = 0xFFFF; /* MXCSR mask (lo16); who knows what for */ |
| addrS[15] = 0xFFFF; /* MXCSR mask (hi16); who knows what for */ |
| |
| /* Copy in the FP registers, in ST order. */ |
| for (stno = 0; stno < 8; stno++) { |
| srcS = (UShort*)(&tmp.reg[10*stno]); |
| dstS = (UShort*)(&addrS[16 + 8*stno]); |
| dstS[0] = srcS[0]; |
| dstS[1] = srcS[1]; |
| dstS[2] = srcS[2]; |
| dstS[3] = srcS[3]; |
| dstS[4] = srcS[4]; |
| dstS[5] = 0; |
| dstS[6] = 0; |
| dstS[7] = 0; |
| } |
| |
| /* That's the first 160 bytes of the image done. Now only %xmm0 |
| .. %xmm7 remain to be copied. If the host is big-endian, these |
| need to be byte-swapped. */ |
| vassert(host_is_little_endian()); |
| |
| # define COPY_U128(_dst,_src) \ |
| do { _dst[0] = _src[0]; _dst[1] = _src[1]; \ |
| _dst[2] = _src[2]; _dst[3] = _src[3]; } \ |
| while (0) |
| |
| COPY_U128( xmm[0], gst->guest_XMM0 ); |
| COPY_U128( xmm[1], gst->guest_XMM1 ); |
| COPY_U128( xmm[2], gst->guest_XMM2 ); |
| COPY_U128( xmm[3], gst->guest_XMM3 ); |
| COPY_U128( xmm[4], gst->guest_XMM4 ); |
| COPY_U128( xmm[5], gst->guest_XMM5 ); |
| COPY_U128( xmm[6], gst->guest_XMM6 ); |
| COPY_U128( xmm[7], gst->guest_XMM7 ); |
| |
| # undef COPY_U128 |
| } |
| |
| |
| /* CALLED FROM GENERATED CODE */ |
| /* DIRTY HELPER (reads guest state, writes guest mem) */ |
| void x86g_dirtyhelper_FSAVE ( VexGuestX86State* gst, HWord addr ) |
| { |
| do_get_x87( gst, (UChar*)addr ); |
| } |
| |
| /* CALLED FROM GENERATED CODE */ |
| /* DIRTY HELPER (writes guest state, reads guest mem) */ |
| VexEmWarn x86g_dirtyhelper_FRSTOR ( VexGuestX86State* gst, HWord addr ) |
| { |
| return do_put_x87( True/*regs too*/, (UChar*)addr, gst ); |
| } |
| |
| /* CALLED FROM GENERATED CODE */ |
| /* DIRTY HELPER (reads guest state, writes guest mem) */ |
| void x86g_dirtyhelper_FSTENV ( VexGuestX86State* gst, HWord addr ) |
| { |
| /* Somewhat roundabout, but at least it's simple. */ |
| Int i; |
| UShort* addrP = (UShort*)addr; |
| Fpu_State tmp; |
| do_get_x87( gst, (UChar*)&tmp ); |
| for (i = 0; i < 14; i++) |
| addrP[i] = tmp.env[i]; |
| } |
| |
| /* CALLED FROM GENERATED CODE */ |
| /* DIRTY HELPER (writes guest state, reads guest mem) */ |
| VexEmWarn x86g_dirtyhelper_FLDENV ( VexGuestX86State* gst, HWord addr ) |
| { |
| return do_put_x87( False/*don't move regs*/, (UChar*)addr, gst); |
| } |
| |
| |
| /*----------------------------------------------*/ |
| /*--- Helpers for MMX/SSE ---*/ |
| /*----------------------------------------------*/ |
| |
| static inline UChar abdU8 ( UChar xx, UChar yy ) { |
| return xx>yy ? xx-yy : yy-xx; |
| } |
| |
| static inline ULong mk32x2 ( UInt w1, UInt w0 ) { |
| return (((ULong)w1) << 32) | ((ULong)w0); |
| } |
| |
| static inline UShort sel16x4_3 ( ULong w64 ) { |
| UInt hi32 = (UInt)(w64 >> 32); |
| return 0xFFFF & (UShort)(hi32 >> 16); |
| } |
| static inline UShort sel16x4_2 ( ULong w64 ) { |
| UInt hi32 = (UInt)(w64 >> 32); |
| return 0xFFFF & (UShort)hi32; |
| } |
| static inline UShort sel16x4_1 ( ULong w64 ) { |
| UInt lo32 = (UInt)w64; |
| return 0xFFFF & (UShort)(lo32 >> 16); |
| } |
| static inline UShort sel16x4_0 ( ULong w64 ) { |
| UInt lo32 = (UInt)w64; |
| return 0xFFFF & (UShort)lo32; |
| } |
| |
| static inline UChar sel8x8_7 ( ULong w64 ) { |
| UInt hi32 = (UInt)(w64 >> 32); |
| return 0xFF & (UChar)(hi32 >> 24); |
| } |
| static inline UChar sel8x8_6 ( ULong w64 ) { |
| UInt hi32 = (UInt)(w64 >> 32); |
| return 0xFF & (UChar)(hi32 >> 16); |
| } |
| static inline UChar sel8x8_5 ( ULong w64 ) { |
| UInt hi32 = (UInt)(w64 >> 32); |
| return 0xFF & (UChar)(hi32 >> 8); |
| } |
| static inline UChar sel8x8_4 ( ULong w64 ) { |
| UInt hi32 = (UInt)(w64 >> 32); |
| return 0xFF & (UChar)(hi32 >> 0); |
| } |
| static inline UChar sel8x8_3 ( ULong w64 ) { |
| UInt lo32 = (UInt)w64; |
| return 0xFF & (UChar)(lo32 >> 24); |
| } |
| static inline UChar sel8x8_2 ( ULong w64 ) { |
| UInt lo32 = (UInt)w64; |
| return 0xFF & (UChar)(lo32 >> 16); |
| } |
| static inline UChar sel8x8_1 ( ULong w64 ) { |
| UInt lo32 = (UInt)w64; |
| return 0xFF & (UChar)(lo32 >> 8); |
| } |
| static inline UChar sel8x8_0 ( ULong w64 ) { |
| UInt lo32 = (UInt)w64; |
| return 0xFF & (UChar)(lo32 >> 0); |
| } |
| |
| /* CALLED FROM GENERATED CODE: CLEAN HELPER */ |
| ULong x86g_calculate_mmx_pmaddwd ( ULong xx, ULong yy ) |
| { |
| return |
| mk32x2( |
| (((Int)(Short)sel16x4_3(xx)) * ((Int)(Short)sel16x4_3(yy))) |
| + (((Int)(Short)sel16x4_2(xx)) * ((Int)(Short)sel16x4_2(yy))), |
| (((Int)(Short)sel16x4_1(xx)) * ((Int)(Short)sel16x4_1(yy))) |
| + (((Int)(Short)sel16x4_0(xx)) * ((Int)(Short)sel16x4_0(yy))) |
| ); |
| } |
| |
| /* CALLED FROM GENERATED CODE: CLEAN HELPER */ |
| UInt x86g_calculate_mmx_pmovmskb ( ULong xx ) |
| { |
| UInt r = 0; |
| if (xx & (1ULL << (64-1))) r |= (1<<7); |
| if (xx & (1ULL << (56-1))) r |= (1<<6); |
| if (xx & (1ULL << (48-1))) r |= (1<<5); |
| if (xx & (1ULL << (40-1))) r |= (1<<4); |
| if (xx & (1ULL << (32-1))) r |= (1<<3); |
| if (xx & (1ULL << (24-1))) r |= (1<<2); |
| if (xx & (1ULL << (16-1))) r |= (1<<1); |
| if (xx & (1ULL << ( 8-1))) r |= (1<<0); |
| return r; |
| } |
| |
| /* CALLED FROM GENERATED CODE: CLEAN HELPER */ |
| ULong x86g_calculate_mmx_psadbw ( ULong xx, ULong yy ) |
| { |
| UInt t = 0; |
| t += (UInt)abdU8( sel8x8_7(xx), sel8x8_7(yy) ); |
| t += (UInt)abdU8( sel8x8_6(xx), sel8x8_6(yy) ); |
| t += (UInt)abdU8( sel8x8_5(xx), sel8x8_5(yy) ); |
| t += (UInt)abdU8( sel8x8_4(xx), sel8x8_4(yy) ); |
| t += (UInt)abdU8( sel8x8_3(xx), sel8x8_3(yy) ); |
| t += (UInt)abdU8( sel8x8_2(xx), sel8x8_2(yy) ); |
| t += (UInt)abdU8( sel8x8_1(xx), sel8x8_1(yy) ); |
| t += (UInt)abdU8( sel8x8_0(xx), sel8x8_0(yy) ); |
| t &= 0xFFFF; |
| return (ULong)t; |
| } |
| |
| /* CALLED FROM GENERATED CODE: CLEAN HELPER */ |
| UInt x86g_calculate_sse_pmovmskb ( ULong w64hi, ULong w64lo ) |
| { |
| UInt rHi8 = x86g_calculate_mmx_pmovmskb ( w64hi ); |
| UInt rLo8 = x86g_calculate_mmx_pmovmskb ( w64lo ); |
| return ((rHi8 & 0xFF) << 8) | (rLo8 & 0xFF); |
| } |
| |
| |
| /*----------------------------------------------*/ |
| /*--- Helpers for segment overrides ---*/ |
| /*----------------------------------------------*/ |
| |
| static inline |
| UInt get_segdescr_base ( VexGuestX86SegDescr* ent ) |
| { |
| UInt lo = 0xFFFF & (UInt)ent->LdtEnt.Bits.BaseLow; |
| UInt mid = 0xFF & (UInt)ent->LdtEnt.Bits.BaseMid; |
| UInt hi = 0xFF & (UInt)ent->LdtEnt.Bits.BaseHi; |
| return (hi << 24) | (mid << 16) | lo; |
| } |
| |
| static inline |
| UInt get_segdescr_limit ( VexGuestX86SegDescr* ent ) |
| { |
| UInt lo = 0xFFFF & (UInt)ent->LdtEnt.Bits.LimitLow; |
| UInt hi = 0xF & (UInt)ent->LdtEnt.Bits.LimitHi; |
| UInt limit = (hi << 16) | lo; |
| if (ent->LdtEnt.Bits.Granularity) |
| limit = (limit << 12) | 0xFFF; |
| return limit; |
| } |
| |
| /* CALLED FROM GENERATED CODE: CLEAN HELPER */ |
| ULong x86g_use_seg_selector ( HWord ldt, HWord gdt, |
| UInt seg_selector, UInt virtual_addr ) |
| { |
| UInt tiBit, base, limit; |
| VexGuestX86SegDescr* the_descrs; |
| |
| Bool verboze = False; |
| |
| /* If this isn't true, we're in Big Trouble. */ |
| vassert(8 == sizeof(VexGuestX86SegDescr)); |
| |
| if (verboze) |
| vex_printf("x86h_use_seg_selector: " |
| "seg_selector = 0x%x, vaddr = 0x%x\n", |
| seg_selector, virtual_addr); |
| |
| /* Check for wildly invalid selector. */ |
| if (seg_selector & ~0xFFFF) |
| goto bad; |
| |
| seg_selector &= 0x0000FFFF; |
| |
| /* Sanity check the segment selector. Ensure that RPL=11b (least |
| privilege). This forms the bottom 2 bits of the selector. */ |
| if ((seg_selector & 3) != 3) |
| goto bad; |
| |
| /* Extract the TI bit (0 means GDT, 1 means LDT) */ |
| tiBit = (seg_selector >> 2) & 1; |
| |
| /* Convert the segment selector onto a table index */ |
| seg_selector >>= 3; |
| vassert(seg_selector >= 0 && seg_selector < 8192); |
| |
| if (tiBit == 0) { |
| |
| /* GDT access. */ |
| /* Do we actually have a GDT to look at? */ |
| if (gdt == 0) |
| goto bad; |
| |
| /* Check for access to non-existent entry. */ |
| if (seg_selector >= VEX_GUEST_X86_GDT_NENT) |
| goto bad; |
| |
| the_descrs = (VexGuestX86SegDescr*)gdt; |
| base = get_segdescr_base (&the_descrs[seg_selector]); |
| limit = get_segdescr_limit(&the_descrs[seg_selector]); |
| |
| } else { |
| |
| /* All the same stuff, except for the LDT. */ |
| if (ldt == 0) |
| goto bad; |
| |
| if (seg_selector >= VEX_GUEST_X86_LDT_NENT) |
| goto bad; |
| |
| the_descrs = (VexGuestX86SegDescr*)ldt; |
| base = get_segdescr_base (&the_descrs[seg_selector]); |
| limit = get_segdescr_limit(&the_descrs[seg_selector]); |
| |
| } |
| |
| /* Do the limit check. Note, this check is just slightly too |
| slack. Really it should be "if (virtual_addr + size - 1 >= |
| limit)," but we don't have the size info to hand. Getting it |
| could be significantly complex. */ |
| if (virtual_addr >= limit) |
| goto bad; |
| |
| if (verboze) |
| vex_printf("x86h_use_seg_selector: " |
| "base = 0x%x, addr = 0x%x\n", |
| base, base + virtual_addr); |
| |
| /* High 32 bits are zero, indicating success. */ |
| return (ULong)( ((UInt)virtual_addr) + base ); |
| |
| bad: |
| return 1ULL << 32; |
| } |
| |
| |
| /*-----------------------------------------------------------*/ |
| /*--- Describing the x86 guest state, for the benefit ---*/ |
| /*--- of iropt and instrumenters. ---*/ |
| /*-----------------------------------------------------------*/ |
| |
| /* Figure out if any part of the guest state contained in minoff |
| .. maxoff requires precise memory exceptions. If in doubt return |
| True (but this is generates significantly slower code). |
| |
| We enforce precise exns for guest %ESP and %EIP only. |
| */ |
| Bool guest_x86_state_requires_precise_mem_exns ( Int minoff, |
| Int maxoff) |
| { |
| Int esp_min = offsetof(VexGuestX86State, guest_ESP); |
| Int esp_max = esp_min + 4 - 1; |
| Int eip_min = offsetof(VexGuestX86State, guest_EIP); |
| Int eip_max = eip_min + 4 - 1; |
| |
| if (maxoff < esp_min || minoff > esp_max) { |
| /* no overlap with esp */ |
| } else { |
| return True; |
| } |
| |
| if (maxoff < eip_min || minoff > eip_max) { |
| /* no overlap with eip */ |
| } else { |
| return True; |
| } |
| |
| return False; |
| } |
| |
| |
| #define ALWAYSDEFD(field) \ |
| { offsetof(VexGuestX86State, field), \ |
| (sizeof ((VexGuestX86State*)0)->field) } |
| |
| VexGuestLayout |
| x86guest_layout |
| = { |
| /* Total size of the guest state, in bytes. */ |
| .total_sizeB = sizeof(VexGuestX86State), |
| |
| /* Describe the stack pointer. */ |
| .offset_SP = offsetof(VexGuestX86State,guest_ESP), |
| .sizeof_SP = 4, |
| |
| /* Describe the instruction pointer. */ |
| .offset_IP = offsetof(VexGuestX86State,guest_EIP), |
| .sizeof_IP = 4, |
| |
| /* Describe any sections to be regarded by Memcheck as |
| 'always-defined'. */ |
| .n_alwaysDefd = 18, |
| |
| /* flags thunk: OP and NDEP are always defd, whereas DEP1 |
| and DEP2 have to be tracked. See detailed comment in |
| gdefs.h on meaning of thunk fields. */ |
| .alwaysDefd |
| = { /* 0 */ ALWAYSDEFD(guest_CC_OP), |
| /* 1 */ ALWAYSDEFD(guest_CC_NDEP), |
| /* 2 */ ALWAYSDEFD(guest_DFLAG), |
| /* 3 */ ALWAYSDEFD(guest_IDFLAG), |
| /* 4 */ ALWAYSDEFD(guest_EIP), |
| /* 5 */ ALWAYSDEFD(guest_FTOP), |
| /* 6 */ ALWAYSDEFD(guest_FPTAG), |
| /* 7 */ ALWAYSDEFD(guest_FPROUND), |
| /* 8 */ ALWAYSDEFD(guest_FC3210), |
| /* 9 */ ALWAYSDEFD(guest_CS), |
| /* 10 */ ALWAYSDEFD(guest_DS), |
| /* 11 */ ALWAYSDEFD(guest_ES), |
| /* 12 */ ALWAYSDEFD(guest_FS), |
| /* 13 */ ALWAYSDEFD(guest_GS), |
| /* 14 */ ALWAYSDEFD(guest_SS), |
| /* 15 */ ALWAYSDEFD(guest_LDT), |
| /* 16 */ ALWAYSDEFD(guest_GDT), |
| /* 17 */ ALWAYSDEFD(guest_EMWARN) |
| } |
| }; |
| |
| |
| /*---------------------------------------------------------------*/ |
| /*--- end guest-x86/ghelpers.c ---*/ |
| /*---------------------------------------------------------------*/ |