sewardj | 36ca513 | 2004-07-24 13:12:23 +0000 | [diff] [blame] | 1 | |
| 2 | /*---------------------------------------------------------------*/ |
| 3 | /*--- ---*/ |
sewardj | c0ee2ed | 2004-07-27 10:29:41 +0000 | [diff] [blame] | 4 | /*--- This file (guest-x86/ghelpers.c) is ---*/ |
sewardj | 36ca513 | 2004-07-24 13:12:23 +0000 | [diff] [blame] | 5 | /*--- Copyright (c) 2004 OpenWorks LLP. All rights reserved. ---*/ |
| 6 | /*--- ---*/ |
| 7 | /*---------------------------------------------------------------*/ |
| 8 | |
| 9 | #include "libvex_basictypes.h" |
sewardj | 0c2cb62 | 2004-09-06 23:21:21 +0000 | [diff] [blame] | 10 | #include "libvex_guest_x86.h" |
sewardj | 36ca513 | 2004-07-24 13:12:23 +0000 | [diff] [blame] | 11 | #include "libvex_ir.h" |
sewardj | 49651f4 | 2004-10-28 22:11:04 +0000 | [diff] [blame] | 12 | #include "libvex.h" |
sewardj | c0ee2ed | 2004-07-27 10:29:41 +0000 | [diff] [blame] | 13 | |
| 14 | #include "main/vex_util.h" |
| 15 | #include "guest-x86/gdefs.h" |
sewardj | 36ca513 | 2004-07-24 13:12:23 +0000 | [diff] [blame] | 16 | |
sewardj | c4be80c | 2004-09-10 16:17:45 +0000 | [diff] [blame] | 17 | |
sewardj | 36ca513 | 2004-07-24 13:12:23 +0000 | [diff] [blame] | 18 | /* This file contains helper functions for x86 guest code. |
| 19 | Calls to these functions are generated by the back end. |
| 20 | These calls are of course in the host machine code and |
| 21 | this file will be compiled to host machine code, so that |
| 22 | all makes sense. |
| 23 | |
sewardj | 36ca513 | 2004-07-24 13:12:23 +0000 | [diff] [blame] | 24 | Only change the signatures of these helper functions very |
| 25 | carefully. If you change the signature here, you'll have to change |
| 26 | the parameters passed to it in the IR calls constructed by |
sewardj | 89050e5 | 2004-07-28 23:25:02 +0000 | [diff] [blame] | 27 | x86toIR.c. |
| 28 | |
| 29 | Some of this code/logic is derived from QEMU, which is copyright |
| 30 | Fabrice Bellard, licensed under the LGPL. It is used with |
| 31 | permission. |
sewardj | 36ca513 | 2004-07-24 13:12:23 +0000 | [diff] [blame] | 32 | */ |
| 33 | |
sewardj | 84ff065 | 2004-08-23 16:16:08 +0000 | [diff] [blame] | 34 | /* Set to 1 to get detailed profiling info about use of the flag |
| 35 | machinery. */ |
| 36 | #define PROFILE_EFLAGS 0 |
| 37 | |
| 38 | |
sewardj | d44bc6e | 2004-10-26 12:39:24 +0000 | [diff] [blame] | 39 | static const UChar parity_table[256] = { |
sewardj | 9aebb0c | 2004-10-24 19:20:43 +0000 | [diff] [blame] | 40 | CC_MASK_P, 0, 0, CC_MASK_P, 0, CC_MASK_P, CC_MASK_P, 0, |
| 41 | 0, CC_MASK_P, CC_MASK_P, 0, CC_MASK_P, 0, 0, CC_MASK_P, |
| 42 | 0, CC_MASK_P, CC_MASK_P, 0, CC_MASK_P, 0, 0, CC_MASK_P, |
| 43 | CC_MASK_P, 0, 0, CC_MASK_P, 0, CC_MASK_P, CC_MASK_P, 0, |
| 44 | 0, CC_MASK_P, CC_MASK_P, 0, CC_MASK_P, 0, 0, CC_MASK_P, |
| 45 | CC_MASK_P, 0, 0, CC_MASK_P, 0, CC_MASK_P, CC_MASK_P, 0, |
| 46 | CC_MASK_P, 0, 0, CC_MASK_P, 0, CC_MASK_P, CC_MASK_P, 0, |
| 47 | 0, CC_MASK_P, CC_MASK_P, 0, CC_MASK_P, 0, 0, CC_MASK_P, |
| 48 | 0, CC_MASK_P, CC_MASK_P, 0, CC_MASK_P, 0, 0, CC_MASK_P, |
| 49 | CC_MASK_P, 0, 0, CC_MASK_P, 0, CC_MASK_P, CC_MASK_P, 0, |
| 50 | CC_MASK_P, 0, 0, CC_MASK_P, 0, CC_MASK_P, CC_MASK_P, 0, |
| 51 | 0, CC_MASK_P, CC_MASK_P, 0, CC_MASK_P, 0, 0, CC_MASK_P, |
| 52 | CC_MASK_P, 0, 0, CC_MASK_P, 0, CC_MASK_P, CC_MASK_P, 0, |
| 53 | 0, CC_MASK_P, CC_MASK_P, 0, CC_MASK_P, 0, 0, CC_MASK_P, |
| 54 | 0, CC_MASK_P, CC_MASK_P, 0, CC_MASK_P, 0, 0, CC_MASK_P, |
| 55 | CC_MASK_P, 0, 0, CC_MASK_P, 0, CC_MASK_P, CC_MASK_P, 0, |
| 56 | 0, CC_MASK_P, CC_MASK_P, 0, CC_MASK_P, 0, 0, CC_MASK_P, |
| 57 | CC_MASK_P, 0, 0, CC_MASK_P, 0, CC_MASK_P, CC_MASK_P, 0, |
| 58 | CC_MASK_P, 0, 0, CC_MASK_P, 0, CC_MASK_P, CC_MASK_P, 0, |
| 59 | 0, CC_MASK_P, CC_MASK_P, 0, CC_MASK_P, 0, 0, CC_MASK_P, |
| 60 | CC_MASK_P, 0, 0, CC_MASK_P, 0, CC_MASK_P, CC_MASK_P, 0, |
| 61 | 0, CC_MASK_P, CC_MASK_P, 0, CC_MASK_P, 0, 0, CC_MASK_P, |
| 62 | 0, CC_MASK_P, CC_MASK_P, 0, CC_MASK_P, 0, 0, CC_MASK_P, |
| 63 | CC_MASK_P, 0, 0, CC_MASK_P, 0, CC_MASK_P, CC_MASK_P, 0, |
| 64 | CC_MASK_P, 0, 0, CC_MASK_P, 0, CC_MASK_P, CC_MASK_P, 0, |
| 65 | 0, CC_MASK_P, CC_MASK_P, 0, CC_MASK_P, 0, 0, CC_MASK_P, |
| 66 | 0, CC_MASK_P, CC_MASK_P, 0, CC_MASK_P, 0, 0, CC_MASK_P, |
| 67 | CC_MASK_P, 0, 0, CC_MASK_P, 0, CC_MASK_P, CC_MASK_P, 0, |
| 68 | 0, CC_MASK_P, CC_MASK_P, 0, CC_MASK_P, 0, 0, CC_MASK_P, |
| 69 | CC_MASK_P, 0, 0, CC_MASK_P, 0, CC_MASK_P, CC_MASK_P, 0, |
| 70 | CC_MASK_P, 0, 0, CC_MASK_P, 0, CC_MASK_P, CC_MASK_P, 0, |
| 71 | 0, CC_MASK_P, CC_MASK_P, 0, CC_MASK_P, 0, 0, CC_MASK_P, |
sewardj | 14731f2 | 2004-07-25 01:24:28 +0000 | [diff] [blame] | 72 | }; |
| 73 | |
| 74 | /* n must be a constant to be efficient */ |
sewardj | df22c1c | 2004-10-16 22:59:59 +0000 | [diff] [blame] | 75 | inline static Int lshift ( Int x, Int n ) |
sewardj | 14731f2 | 2004-07-25 01:24:28 +0000 | [diff] [blame] | 76 | { |
sewardj | df22c1c | 2004-10-16 22:59:59 +0000 | [diff] [blame] | 77 | if (n >= 0) |
| 78 | return x << n; |
| 79 | else |
| 80 | return x >> (-n); |
sewardj | 14731f2 | 2004-07-25 01:24:28 +0000 | [diff] [blame] | 81 | } |
| 82 | |
| 83 | |
sewardj | b9c5cf6 | 2004-08-24 15:10:38 +0000 | [diff] [blame] | 84 | #define PREAMBLE(__data_bits) \ |
sewardj | df22c1c | 2004-10-16 22:59:59 +0000 | [diff] [blame] | 85 | /* const */ UInt DATA_MASK \ |
sewardj | b9c5cf6 | 2004-08-24 15:10:38 +0000 | [diff] [blame] | 86 | = __data_bits==8 ? 0xFF \ |
| 87 | : (__data_bits==16 ? 0xFFFF \ |
| 88 | : 0xFFFFFFFF); \ |
sewardj | df22c1c | 2004-10-16 22:59:59 +0000 | [diff] [blame] | 89 | /* const */ UInt SIGN_MASK = 1 << (__data_bits - 1); \ |
sewardj | 2a2ba8b | 2004-11-08 13:14:06 +0000 | [diff] [blame] | 90 | /* const */ UInt CC_DEP1 = cc_dep1_formal; \ |
| 91 | /* const */ UInt CC_DEP2 = cc_dep2_formal; \ |
| 92 | /* const */ UInt CC_NDEP = cc_ndep_formal; \ |
| 93 | /* Four bogus assignments, which hopefully gcc can */ \ |
sewardj | df22c1c | 2004-10-16 22:59:59 +0000 | [diff] [blame] | 94 | /* optimise away, and which stop it complaining about */ \ |
| 95 | /* unused variables. */ \ |
| 96 | SIGN_MASK = SIGN_MASK; \ |
| 97 | DATA_MASK = DATA_MASK; \ |
sewardj | 2a2ba8b | 2004-11-08 13:14:06 +0000 | [diff] [blame] | 98 | CC_DEP2 = CC_DEP2; \ |
| 99 | CC_NDEP = CC_NDEP; |
sewardj | df22c1c | 2004-10-16 22:59:59 +0000 | [diff] [blame] | 100 | |
sewardj | 14731f2 | 2004-07-25 01:24:28 +0000 | [diff] [blame] | 101 | |
sewardj | b9c5cf6 | 2004-08-24 15:10:38 +0000 | [diff] [blame] | 102 | /*-------------------------------------------------------------*/ |
sewardj | 89050e5 | 2004-07-28 23:25:02 +0000 | [diff] [blame] | 103 | |
sewardj | df22c1c | 2004-10-16 22:59:59 +0000 | [diff] [blame] | 104 | #define ACTIONS_ADD(DATA_BITS,DATA_UTYPE) \ |
sewardj | b9c5cf6 | 2004-08-24 15:10:38 +0000 | [diff] [blame] | 105 | { \ |
| 106 | PREAMBLE(DATA_BITS); \ |
sewardj | 948d48b | 2004-11-05 19:49:09 +0000 | [diff] [blame] | 107 | Int cf, pf, af, zf, sf, of; \ |
| 108 | Int argL, argR, res; \ |
sewardj | 2a2ba8b | 2004-11-08 13:14:06 +0000 | [diff] [blame] | 109 | argL = CC_DEP1; \ |
| 110 | argR = CC_DEP2; \ |
| 111 | res = argL + argR; \ |
sewardj | 948d48b | 2004-11-05 19:49:09 +0000 | [diff] [blame] | 112 | cf = (DATA_UTYPE)res < (DATA_UTYPE)argL; \ |
| 113 | pf = parity_table[(UChar)res]; \ |
| 114 | af = (res ^ argL ^ argR) & 0x10; \ |
| 115 | zf = ((DATA_UTYPE)res == 0) << 6; \ |
| 116 | sf = lshift(res, 8 - DATA_BITS) & 0x80; \ |
| 117 | of = lshift((argL ^ argR ^ -1) & (argL ^ res), \ |
sewardj | 9aebb0c | 2004-10-24 19:20:43 +0000 | [diff] [blame] | 118 | 12 - DATA_BITS) & CC_MASK_O; \ |
sewardj | b9c5cf6 | 2004-08-24 15:10:38 +0000 | [diff] [blame] | 119 | return cf | pf | af | zf | sf | of; \ |
sewardj | 89050e5 | 2004-07-28 23:25:02 +0000 | [diff] [blame] | 120 | } |
| 121 | |
sewardj | b9c5cf6 | 2004-08-24 15:10:38 +0000 | [diff] [blame] | 122 | /*-------------------------------------------------------------*/ |
| 123 | |
sewardj | df22c1c | 2004-10-16 22:59:59 +0000 | [diff] [blame] | 124 | #define ACTIONS_SUB(DATA_BITS,DATA_UTYPE) \ |
sewardj | b9c5cf6 | 2004-08-24 15:10:38 +0000 | [diff] [blame] | 125 | { \ |
| 126 | PREAMBLE(DATA_BITS); \ |
sewardj | 948d48b | 2004-11-05 19:49:09 +0000 | [diff] [blame] | 127 | Int cf, pf, af, zf, sf, of; \ |
| 128 | Int argL, argR, res; \ |
sewardj | 2a2ba8b | 2004-11-08 13:14:06 +0000 | [diff] [blame] | 129 | argL = CC_DEP1; \ |
| 130 | argR = CC_DEP2; \ |
| 131 | res = argL - argR; \ |
sewardj | 948d48b | 2004-11-05 19:49:09 +0000 | [diff] [blame] | 132 | cf = (DATA_UTYPE)argL < (DATA_UTYPE)argR; \ |
| 133 | pf = parity_table[(UChar)res]; \ |
| 134 | af = (res ^ argL ^ argR) & 0x10; \ |
| 135 | zf = ((DATA_UTYPE)res == 0) << 6; \ |
| 136 | sf = lshift(res, 8 - DATA_BITS) & 0x80; \ |
| 137 | of = lshift((argL ^ argR) & (argL ^ res), \ |
sewardj | 9aebb0c | 2004-10-24 19:20:43 +0000 | [diff] [blame] | 138 | 12 - DATA_BITS) & CC_MASK_O; \ |
sewardj | b9c5cf6 | 2004-08-24 15:10:38 +0000 | [diff] [blame] | 139 | return cf | pf | af | zf | sf | of; \ |
sewardj | 2ef5f2a | 2004-08-24 01:46:34 +0000 | [diff] [blame] | 140 | } |
| 141 | |
sewardj | b9c5cf6 | 2004-08-24 15:10:38 +0000 | [diff] [blame] | 142 | /*-------------------------------------------------------------*/ |
| 143 | |
sewardj | 2a2ba8b | 2004-11-08 13:14:06 +0000 | [diff] [blame] | 144 | #define ACTIONS_ADC(DATA_BITS,DATA_UTYPE) \ |
| 145 | { \ |
| 146 | PREAMBLE(DATA_BITS); \ |
| 147 | Int cf, pf, af, zf, sf, of; \ |
| 148 | Int argL, argR, oldC, res; \ |
| 149 | oldC = CC_NDEP & CC_MASK_C; \ |
| 150 | argL = CC_DEP1; \ |
| 151 | argR = CC_DEP2 ^ oldC; \ |
| 152 | res = (argL + argR) + oldC; \ |
| 153 | if (oldC) \ |
| 154 | cf = (DATA_UTYPE)res <= (DATA_UTYPE)argL; \ |
| 155 | else \ |
| 156 | cf = (DATA_UTYPE)res < (DATA_UTYPE)argL; \ |
| 157 | pf = parity_table[(UChar)res]; \ |
| 158 | af = (res ^ argL ^ argR) & 0x10; \ |
| 159 | zf = ((DATA_UTYPE)res == 0) << 6; \ |
| 160 | sf = lshift(res, 8 - DATA_BITS) & 0x80; \ |
| 161 | of = lshift((argL ^ argR ^ -1) & (argL ^ res), \ |
| 162 | 12 - DATA_BITS) & CC_MASK_O; \ |
| 163 | return cf | pf | af | zf | sf | of; \ |
| 164 | } |
| 165 | |
| 166 | /*-------------------------------------------------------------*/ |
| 167 | |
sewardj | df22c1c | 2004-10-16 22:59:59 +0000 | [diff] [blame] | 168 | #define ACTIONS_SBB(DATA_BITS,DATA_UTYPE) \ |
sewardj | b9c5cf6 | 2004-08-24 15:10:38 +0000 | [diff] [blame] | 169 | { \ |
| 170 | PREAMBLE(DATA_BITS); \ |
sewardj | 948d48b | 2004-11-05 19:49:09 +0000 | [diff] [blame] | 171 | Int cf, pf, af, zf, sf, of; \ |
sewardj | 2a2ba8b | 2004-11-08 13:14:06 +0000 | [diff] [blame] | 172 | Int argL, argR, oldC, res; \ |
| 173 | oldC = CC_NDEP & CC_MASK_C; \ |
| 174 | argL = CC_DEP1; \ |
| 175 | argR = CC_DEP2 ^ oldC; \ |
| 176 | res = (argL - argR) - oldC; \ |
| 177 | if (oldC) \ |
| 178 | cf = (DATA_UTYPE)argL <= (DATA_UTYPE)argR; \ |
| 179 | else \ |
| 180 | cf = (DATA_UTYPE)argL < (DATA_UTYPE)argR; \ |
sewardj | 948d48b | 2004-11-05 19:49:09 +0000 | [diff] [blame] | 181 | pf = parity_table[(UChar)res]; \ |
| 182 | af = (res ^ argL ^ argR) & 0x10; \ |
| 183 | zf = ((DATA_UTYPE)res == 0) << 6; \ |
| 184 | sf = lshift(res, 8 - DATA_BITS) & 0x80; \ |
| 185 | of = lshift((argL ^ argR) & (argL ^ res), \ |
sewardj | 9aebb0c | 2004-10-24 19:20:43 +0000 | [diff] [blame] | 186 | 12 - DATA_BITS) & CC_MASK_O; \ |
sewardj | b9c5cf6 | 2004-08-24 15:10:38 +0000 | [diff] [blame] | 187 | return cf | pf | af | zf | sf | of; \ |
sewardj | 2ef5f2a | 2004-08-24 01:46:34 +0000 | [diff] [blame] | 188 | } |
| 189 | |
sewardj | b9c5cf6 | 2004-08-24 15:10:38 +0000 | [diff] [blame] | 190 | /*-------------------------------------------------------------*/ |
| 191 | |
sewardj | df22c1c | 2004-10-16 22:59:59 +0000 | [diff] [blame] | 192 | #define ACTIONS_LOGIC(DATA_BITS,DATA_UTYPE) \ |
sewardj | b9c5cf6 | 2004-08-24 15:10:38 +0000 | [diff] [blame] | 193 | { \ |
| 194 | PREAMBLE(DATA_BITS); \ |
sewardj | 948d48b | 2004-11-05 19:49:09 +0000 | [diff] [blame] | 195 | Int cf, pf, af, zf, sf, of; \ |
sewardj | b9c5cf6 | 2004-08-24 15:10:38 +0000 | [diff] [blame] | 196 | cf = 0; \ |
sewardj | 2a2ba8b | 2004-11-08 13:14:06 +0000 | [diff] [blame] | 197 | pf = parity_table[(UChar)CC_DEP1]; \ |
sewardj | b9c5cf6 | 2004-08-24 15:10:38 +0000 | [diff] [blame] | 198 | af = 0; \ |
sewardj | 2a2ba8b | 2004-11-08 13:14:06 +0000 | [diff] [blame] | 199 | zf = ((DATA_UTYPE)CC_DEP1 == 0) << 6; \ |
| 200 | sf = lshift(CC_DEP1, 8 - DATA_BITS) & 0x80; \ |
sewardj | b9c5cf6 | 2004-08-24 15:10:38 +0000 | [diff] [blame] | 201 | of = 0; \ |
| 202 | return cf | pf | af | zf | sf | of; \ |
sewardj | 2ef5f2a | 2004-08-24 01:46:34 +0000 | [diff] [blame] | 203 | } |
| 204 | |
sewardj | b9c5cf6 | 2004-08-24 15:10:38 +0000 | [diff] [blame] | 205 | /*-------------------------------------------------------------*/ |
| 206 | |
sewardj | df22c1c | 2004-10-16 22:59:59 +0000 | [diff] [blame] | 207 | #define ACTIONS_INC(DATA_BITS,DATA_UTYPE) \ |
sewardj | b9c5cf6 | 2004-08-24 15:10:38 +0000 | [diff] [blame] | 208 | { \ |
| 209 | PREAMBLE(DATA_BITS); \ |
sewardj | 948d48b | 2004-11-05 19:49:09 +0000 | [diff] [blame] | 210 | Int cf, pf, af, zf, sf, of; \ |
sewardj | 2a2ba8b | 2004-11-08 13:14:06 +0000 | [diff] [blame] | 211 | Int argL, argR, res; \ |
| 212 | res = CC_DEP1; \ |
| 213 | argL = res - 1; \ |
sewardj | 948d48b | 2004-11-05 19:49:09 +0000 | [diff] [blame] | 214 | argR = 1; \ |
sewardj | 2a2ba8b | 2004-11-08 13:14:06 +0000 | [diff] [blame] | 215 | cf = CC_NDEP & CC_MASK_C; \ |
| 216 | pf = parity_table[(UChar)res]; \ |
| 217 | af = (res ^ argL ^ argR) & 0x10; \ |
| 218 | zf = ((DATA_UTYPE)res == 0) << 6; \ |
| 219 | sf = lshift(res, 8 - DATA_BITS) & 0x80; \ |
| 220 | of = ((res & DATA_MASK) == SIGN_MASK) << 11; \ |
sewardj | b9c5cf6 | 2004-08-24 15:10:38 +0000 | [diff] [blame] | 221 | return cf | pf | af | zf | sf | of; \ |
sewardj | 2ef5f2a | 2004-08-24 01:46:34 +0000 | [diff] [blame] | 222 | } |
| 223 | |
sewardj | b9c5cf6 | 2004-08-24 15:10:38 +0000 | [diff] [blame] | 224 | /*-------------------------------------------------------------*/ |
| 225 | |
sewardj | df22c1c | 2004-10-16 22:59:59 +0000 | [diff] [blame] | 226 | #define ACTIONS_DEC(DATA_BITS,DATA_UTYPE) \ |
sewardj | b9c5cf6 | 2004-08-24 15:10:38 +0000 | [diff] [blame] | 227 | { \ |
| 228 | PREAMBLE(DATA_BITS); \ |
sewardj | 948d48b | 2004-11-05 19:49:09 +0000 | [diff] [blame] | 229 | Int cf, pf, af, zf, sf, of; \ |
sewardj | 2a2ba8b | 2004-11-08 13:14:06 +0000 | [diff] [blame] | 230 | Int argL, argR, res; \ |
| 231 | res = CC_DEP1; \ |
| 232 | argL = res + 1; \ |
sewardj | 948d48b | 2004-11-05 19:49:09 +0000 | [diff] [blame] | 233 | argR = 1; \ |
sewardj | 2a2ba8b | 2004-11-08 13:14:06 +0000 | [diff] [blame] | 234 | cf = CC_NDEP & CC_MASK_C; \ |
| 235 | pf = parity_table[(UChar)res]; \ |
| 236 | af = (res ^ argL ^ argR) & 0x10; \ |
| 237 | zf = ((DATA_UTYPE)res == 0) << 6; \ |
| 238 | sf = lshift(res, 8 - DATA_BITS) & 0x80; \ |
| 239 | of = ((res & DATA_MASK) \ |
sewardj | d44bc6e | 2004-10-26 12:39:24 +0000 | [diff] [blame] | 240 | == ((UInt)SIGN_MASK - 1)) << 11; \ |
sewardj | b9c5cf6 | 2004-08-24 15:10:38 +0000 | [diff] [blame] | 241 | return cf | pf | af | zf | sf | of; \ |
sewardj | 2ef5f2a | 2004-08-24 01:46:34 +0000 | [diff] [blame] | 242 | } |
| 243 | |
sewardj | b9c5cf6 | 2004-08-24 15:10:38 +0000 | [diff] [blame] | 244 | /*-------------------------------------------------------------*/ |
| 245 | |
sewardj | df22c1c | 2004-10-16 22:59:59 +0000 | [diff] [blame] | 246 | #define ACTIONS_SHL(DATA_BITS,DATA_UTYPE) \ |
sewardj | b9c5cf6 | 2004-08-24 15:10:38 +0000 | [diff] [blame] | 247 | { \ |
| 248 | PREAMBLE(DATA_BITS); \ |
sewardj | 948d48b | 2004-11-05 19:49:09 +0000 | [diff] [blame] | 249 | Int cf, pf, af, zf, sf, of; \ |
sewardj | 2a2ba8b | 2004-11-08 13:14:06 +0000 | [diff] [blame] | 250 | cf = (CC_DEP2 >> (DATA_BITS - 1)) & CC_MASK_C; \ |
| 251 | pf = parity_table[(UChar)CC_DEP1]; \ |
sewardj | b9c5cf6 | 2004-08-24 15:10:38 +0000 | [diff] [blame] | 252 | af = 0; /* undefined */ \ |
sewardj | 2a2ba8b | 2004-11-08 13:14:06 +0000 | [diff] [blame] | 253 | zf = ((DATA_UTYPE)CC_DEP1 == 0) << 6; \ |
| 254 | sf = lshift(CC_DEP1, 8 - DATA_BITS) & 0x80; \ |
sewardj | b9c5cf6 | 2004-08-24 15:10:38 +0000 | [diff] [blame] | 255 | /* of is defined if shift count == 1 */ \ |
sewardj | 2a2ba8b | 2004-11-08 13:14:06 +0000 | [diff] [blame] | 256 | of = lshift(CC_DEP2 ^ CC_DEP1, 12 - DATA_BITS) & CC_MASK_O; \ |
sewardj | b9c5cf6 | 2004-08-24 15:10:38 +0000 | [diff] [blame] | 257 | return cf | pf | af | zf | sf | of; \ |
sewardj | 2ef5f2a | 2004-08-24 01:46:34 +0000 | [diff] [blame] | 258 | } |
| 259 | |
sewardj | b9c5cf6 | 2004-08-24 15:10:38 +0000 | [diff] [blame] | 260 | /*-------------------------------------------------------------*/ |
| 261 | |
sewardj | 2a2ba8b | 2004-11-08 13:14:06 +0000 | [diff] [blame] | 262 | #define ACTIONS_SHR(DATA_BITS,DATA_UTYPE) \ |
sewardj | b9c5cf6 | 2004-08-24 15:10:38 +0000 | [diff] [blame] | 263 | { \ |
| 264 | PREAMBLE(DATA_BITS); \ |
sewardj | 948d48b | 2004-11-05 19:49:09 +0000 | [diff] [blame] | 265 | Int cf, pf, af, zf, sf, of; \ |
sewardj | 2a2ba8b | 2004-11-08 13:14:06 +0000 | [diff] [blame] | 266 | cf = CC_DEP2 & 1; \ |
| 267 | pf = parity_table[(UChar)CC_DEP1]; \ |
sewardj | b9c5cf6 | 2004-08-24 15:10:38 +0000 | [diff] [blame] | 268 | af = 0; /* undefined */ \ |
sewardj | 2a2ba8b | 2004-11-08 13:14:06 +0000 | [diff] [blame] | 269 | zf = ((DATA_UTYPE)CC_DEP1 == 0) << 6; \ |
| 270 | sf = lshift(CC_DEP1, 8 - DATA_BITS) & 0x80; \ |
sewardj | b9c5cf6 | 2004-08-24 15:10:38 +0000 | [diff] [blame] | 271 | /* of is defined if shift count == 1 */ \ |
sewardj | 2a2ba8b | 2004-11-08 13:14:06 +0000 | [diff] [blame] | 272 | of = lshift(CC_DEP2 ^ CC_DEP1, 12 - DATA_BITS) & CC_MASK_O; \ |
sewardj | b9c5cf6 | 2004-08-24 15:10:38 +0000 | [diff] [blame] | 273 | return cf | pf | af | zf | sf | of; \ |
sewardj | 5f63035 | 2004-07-25 18:18:03 +0000 | [diff] [blame] | 274 | } |
| 275 | |
sewardj | b9c5cf6 | 2004-08-24 15:10:38 +0000 | [diff] [blame] | 276 | /*-------------------------------------------------------------*/ |
| 277 | |
sewardj | 8ee2de1 | 2004-07-29 22:22:31 +0000 | [diff] [blame] | 278 | /* ROL: cf' = lsb(result). of' = msb(result) ^ lsb(result). */ |
sewardj | 2a2ba8b | 2004-11-08 13:14:06 +0000 | [diff] [blame] | 279 | /* DEP1 = result, NDEP = old flags */ |
sewardj | df22c1c | 2004-10-16 22:59:59 +0000 | [diff] [blame] | 280 | #define ACTIONS_ROL(DATA_BITS,DATA_UTYPE) \ |
sewardj | b9c5cf6 | 2004-08-24 15:10:38 +0000 | [diff] [blame] | 281 | { \ |
| 282 | PREAMBLE(DATA_BITS); \ |
sewardj | 948d48b | 2004-11-05 19:49:09 +0000 | [diff] [blame] | 283 | Int fl \ |
sewardj | 2a2ba8b | 2004-11-08 13:14:06 +0000 | [diff] [blame] | 284 | = (CC_NDEP & ~(CC_MASK_O | CC_MASK_C)) \ |
| 285 | | (CC_MASK_C & CC_DEP1) \ |
| 286 | | (CC_MASK_O & (lshift(CC_DEP1, 11-(DATA_BITS-1)) \ |
| 287 | ^ lshift(CC_DEP1, 11))); \ |
sewardj | b9c5cf6 | 2004-08-24 15:10:38 +0000 | [diff] [blame] | 288 | return fl; \ |
sewardj | 8c7f1ab | 2004-07-29 20:31:09 +0000 | [diff] [blame] | 289 | } |
| 290 | |
sewardj | b9c5cf6 | 2004-08-24 15:10:38 +0000 | [diff] [blame] | 291 | /*-------------------------------------------------------------*/ |
| 292 | |
sewardj | 1813dbe | 2004-07-28 17:09:04 +0000 | [diff] [blame] | 293 | /* ROR: cf' = msb(result). of' = msb(result) ^ msb-1(result). */ |
sewardj | 2a2ba8b | 2004-11-08 13:14:06 +0000 | [diff] [blame] | 294 | /* DEP1 = result, NDEP = old flags */ |
sewardj | df22c1c | 2004-10-16 22:59:59 +0000 | [diff] [blame] | 295 | #define ACTIONS_ROR(DATA_BITS,DATA_UTYPE) \ |
sewardj | b9c5cf6 | 2004-08-24 15:10:38 +0000 | [diff] [blame] | 296 | { \ |
| 297 | PREAMBLE(DATA_BITS); \ |
sewardj | 948d48b | 2004-11-05 19:49:09 +0000 | [diff] [blame] | 298 | Int fl \ |
sewardj | 2a2ba8b | 2004-11-08 13:14:06 +0000 | [diff] [blame] | 299 | = (CC_NDEP & ~(CC_MASK_O | CC_MASK_C)) \ |
| 300 | | (CC_MASK_C & (CC_DEP1 >> (DATA_BITS-1))) \ |
| 301 | | (CC_MASK_O & (lshift(CC_DEP1, 11-(DATA_BITS-1)) \ |
| 302 | ^ lshift(CC_DEP1, 11-(DATA_BITS-1)+1))); \ |
sewardj | b9c5cf6 | 2004-08-24 15:10:38 +0000 | [diff] [blame] | 303 | return fl; \ |
sewardj | 1813dbe | 2004-07-28 17:09:04 +0000 | [diff] [blame] | 304 | } |
| 305 | |
sewardj | b9c5cf6 | 2004-08-24 15:10:38 +0000 | [diff] [blame] | 306 | /*-------------------------------------------------------------*/ |
| 307 | |
sewardj | 2a2ba8b | 2004-11-08 13:14:06 +0000 | [diff] [blame] | 308 | #define ACTIONS_UMUL(DATA_BITS,DATA_UTYPE,DATA_U2TYPE) \ |
| 309 | { \ |
| 310 | PREAMBLE(DATA_BITS); \ |
| 311 | Int cf, pf, af, zf, sf, of; \ |
| 312 | DATA_UTYPE hi; \ |
| 313 | DATA_UTYPE lo = ((DATA_UTYPE)CC_DEP1) \ |
| 314 | * ((DATA_UTYPE)CC_DEP2); \ |
| 315 | DATA_U2TYPE rr = ((DATA_U2TYPE)((DATA_UTYPE)CC_DEP1)) \ |
| 316 | * ((DATA_U2TYPE)((DATA_UTYPE)CC_DEP2)); \ |
| 317 | hi = (DATA_UTYPE)(rr >>/*u*/ DATA_BITS); \ |
| 318 | cf = (hi != 0); \ |
| 319 | pf = parity_table[(UChar)lo]; \ |
| 320 | af = 0; /* undefined */ \ |
| 321 | zf = (lo == 0) << 6; \ |
| 322 | sf = lshift(lo, 8 - DATA_BITS) & 0x80; \ |
| 323 | of = cf << 11; \ |
| 324 | return cf | pf | af | zf | sf | of; \ |
sewardj | 7ed2295 | 2004-07-29 00:09:58 +0000 | [diff] [blame] | 325 | } |
sewardj | 741153c | 2004-07-25 23:39:13 +0000 | [diff] [blame] | 326 | |
sewardj | df22c1c | 2004-10-16 22:59:59 +0000 | [diff] [blame] | 327 | /*-------------------------------------------------------------*/ |
| 328 | |
sewardj | 2a2ba8b | 2004-11-08 13:14:06 +0000 | [diff] [blame] | 329 | #define ACTIONS_SMUL(DATA_BITS,DATA_STYPE,DATA_S2TYPE) \ |
| 330 | { \ |
| 331 | PREAMBLE(DATA_BITS); \ |
| 332 | Int cf, pf, af, zf, sf, of; \ |
| 333 | DATA_STYPE hi; \ |
| 334 | DATA_STYPE lo = ((DATA_STYPE)CC_DEP1) \ |
| 335 | * ((DATA_STYPE)CC_DEP2); \ |
| 336 | DATA_S2TYPE rr = ((DATA_S2TYPE)((DATA_STYPE)CC_DEP1)) \ |
| 337 | * ((DATA_S2TYPE)((DATA_STYPE)CC_DEP2)); \ |
| 338 | hi = (DATA_STYPE)(rr >>/*s*/ DATA_BITS); \ |
| 339 | cf = (hi != (lo >>/*s*/ (DATA_BITS-1))); \ |
| 340 | pf = parity_table[(UChar)lo]; \ |
| 341 | af = 0; /* undefined */ \ |
| 342 | zf = (lo == 0) << 6; \ |
| 343 | sf = lshift(lo, 8 - DATA_BITS) & 0x80; \ |
| 344 | of = cf << 11; \ |
| 345 | return cf | pf | af | zf | sf | of; \ |
sewardj | df22c1c | 2004-10-16 22:59:59 +0000 | [diff] [blame] | 346 | } |
| 347 | |
sewardj | 6e9964d | 2004-07-25 17:25:55 +0000 | [diff] [blame] | 348 | |
sewardj | 84ff065 | 2004-08-23 16:16:08 +0000 | [diff] [blame] | 349 | #if PROFILE_EFLAGS |
| 350 | |
| 351 | static UInt tabc[CC_OP_NUMBER]; |
| 352 | static UInt tab[CC_OP_NUMBER][16]; |
sewardj | 9eab588 | 2004-08-25 16:38:30 +0000 | [diff] [blame] | 353 | static Bool initted = False; |
sewardj | 84ff065 | 2004-08-23 16:16:08 +0000 | [diff] [blame] | 354 | static UInt n_calc_cond = 0; |
| 355 | static UInt n_calc_all = 0; |
| 356 | static UInt n_calc_c = 0; |
| 357 | |
| 358 | static void showCounts ( void ) |
| 359 | { |
| 360 | Int op, co; |
| 361 | Char ch; |
sewardj | 9eab588 | 2004-08-25 16:38:30 +0000 | [diff] [blame] | 362 | vex_printf("\nALL=%d COND=%d C=%d\n", |
sewardj | 84ff065 | 2004-08-23 16:16:08 +0000 | [diff] [blame] | 363 | n_calc_all-n_calc_cond-n_calc_c, n_calc_cond, n_calc_c); |
| 364 | vex_printf(" CARRY O NO B NB Z NZ BE NBE" |
| 365 | " S NS P NP L NL LE NLE\n"); |
| 366 | vex_printf(" ----------------------------------------------" |
| 367 | "----------------------------------------\n"); |
| 368 | for (op = 0; op < CC_OP_NUMBER; op++) { |
| 369 | |
| 370 | ch = ' '; |
sewardj | 93d96e9 | 2004-11-08 17:39:55 +0000 | [diff] [blame] | 371 | if (op > 0 && (op-1) % 3 == 0) |
| 372 | ch = 'B'; |
| 373 | if (op > 0 && (op-1) % 3 == 1) |
| 374 | ch = 'W'; |
sewardj | 84ff065 | 2004-08-23 16:16:08 +0000 | [diff] [blame] | 375 | if (op > 0 && (op-1) % 3 == 2) |
| 376 | ch = 'L'; |
| 377 | |
| 378 | vex_printf("%2d%c: ", op, ch); |
| 379 | vex_printf("%6d ", tabc[op]); |
| 380 | for (co = 0; co < 16; co++) { |
| 381 | Int n = tab[op][co]; |
| 382 | if (n >= 1000) { |
| 383 | vex_printf(" %3dK", n / 1000); |
| 384 | } else |
| 385 | if (n >= 0) { |
| 386 | vex_printf(" %3d ", n ); |
| 387 | } else { |
| 388 | vex_printf(" "); |
| 389 | } |
| 390 | } |
| 391 | vex_printf("\n"); |
| 392 | } |
| 393 | vex_printf("\n"); |
| 394 | } |
| 395 | |
| 396 | static void initCounts ( void ) |
| 397 | { |
| 398 | Int op, co; |
| 399 | initted = True; |
| 400 | for (op = 0; op < CC_OP_NUMBER; op++) { |
| 401 | tabc[op] = 0; |
| 402 | for (co = 0; co < 16; co++) |
| 403 | tab[op][co] = 0; |
| 404 | } |
| 405 | } |
| 406 | |
| 407 | #endif /* PROFILE_EFLAGS */ |
| 408 | |
sewardj | 9aebb0c | 2004-10-24 19:20:43 +0000 | [diff] [blame] | 409 | /* CALLED FROM GENERATED CODE: CLEAN HELPER */ |
sewardj | b9c5cf6 | 2004-08-24 15:10:38 +0000 | [diff] [blame] | 410 | /* Calculate all the 6 flags from the supplied thunk parameters. */ |
sewardj | 2a2ba8b | 2004-11-08 13:14:06 +0000 | [diff] [blame] | 411 | UInt calculate_eflags_all ( UInt cc_op, |
| 412 | UInt cc_dep1_formal, |
| 413 | UInt cc_dep2_formal, |
| 414 | UInt cc_ndep_formal ) |
sewardj | 36ca513 | 2004-07-24 13:12:23 +0000 | [diff] [blame] | 415 | { |
sewardj | 84ff065 | 2004-08-23 16:16:08 +0000 | [diff] [blame] | 416 | # if PROFILE_EFLAGS |
| 417 | n_calc_all++; |
| 418 | # endif |
sewardj | 36ca513 | 2004-07-24 13:12:23 +0000 | [diff] [blame] | 419 | switch (cc_op) { |
sewardj | 1813dbe | 2004-07-28 17:09:04 +0000 | [diff] [blame] | 420 | case CC_OP_COPY: |
sewardj | 2a2ba8b | 2004-11-08 13:14:06 +0000 | [diff] [blame] | 421 | return cc_dep1_formal |
sewardj | df22c1c | 2004-10-16 22:59:59 +0000 | [diff] [blame] | 422 | & (CC_MASK_O | CC_MASK_S | CC_MASK_Z |
| 423 | | CC_MASK_A | CC_MASK_C | CC_MASK_P); |
sewardj | 14731f2 | 2004-07-25 01:24:28 +0000 | [diff] [blame] | 424 | |
sewardj | 8c7f1ab | 2004-07-29 20:31:09 +0000 | [diff] [blame] | 425 | case CC_OP_ADDB: ACTIONS_ADD( 8, UChar ); |
sewardj | a238471 | 2004-07-29 14:36:40 +0000 | [diff] [blame] | 426 | case CC_OP_ADDW: ACTIONS_ADD( 16, UShort ); |
sewardj | 8c7f1ab | 2004-07-29 20:31:09 +0000 | [diff] [blame] | 427 | case CC_OP_ADDL: ACTIONS_ADD( 32, UInt ); |
sewardj | afc5787 | 2004-07-25 18:00:18 +0000 | [diff] [blame] | 428 | |
sewardj | 8c7f1ab | 2004-07-29 20:31:09 +0000 | [diff] [blame] | 429 | case CC_OP_ADCB: ACTIONS_ADC( 8, UChar ); |
sewardj | a238471 | 2004-07-29 14:36:40 +0000 | [diff] [blame] | 430 | case CC_OP_ADCW: ACTIONS_ADC( 16, UShort ); |
sewardj | 8c7f1ab | 2004-07-29 20:31:09 +0000 | [diff] [blame] | 431 | case CC_OP_ADCL: ACTIONS_ADC( 32, UInt ); |
sewardj | 741153c | 2004-07-25 23:39:13 +0000 | [diff] [blame] | 432 | |
sewardj | 8c7f1ab | 2004-07-29 20:31:09 +0000 | [diff] [blame] | 433 | case CC_OP_SUBB: ACTIONS_SUB( 8, UChar ); |
sewardj | a238471 | 2004-07-29 14:36:40 +0000 | [diff] [blame] | 434 | case CC_OP_SUBW: ACTIONS_SUB( 16, UShort ); |
sewardj | 8c7f1ab | 2004-07-29 20:31:09 +0000 | [diff] [blame] | 435 | case CC_OP_SUBL: ACTIONS_SUB( 32, UInt ); |
sewardj | 5f63035 | 2004-07-25 18:18:03 +0000 | [diff] [blame] | 436 | |
sewardj | 8c7f1ab | 2004-07-29 20:31:09 +0000 | [diff] [blame] | 437 | case CC_OP_SBBB: ACTIONS_SBB( 8, UChar ); |
sewardj | a238471 | 2004-07-29 14:36:40 +0000 | [diff] [blame] | 438 | case CC_OP_SBBW: ACTIONS_SBB( 16, UShort ); |
sewardj | 8c7f1ab | 2004-07-29 20:31:09 +0000 | [diff] [blame] | 439 | case CC_OP_SBBL: ACTIONS_SBB( 32, UInt ); |
sewardj | 750f407 | 2004-07-26 22:39:11 +0000 | [diff] [blame] | 440 | |
sewardj | 8c7f1ab | 2004-07-29 20:31:09 +0000 | [diff] [blame] | 441 | case CC_OP_LOGICB: ACTIONS_LOGIC( 8, UChar ); |
sewardj | a238471 | 2004-07-29 14:36:40 +0000 | [diff] [blame] | 442 | case CC_OP_LOGICW: ACTIONS_LOGIC( 16, UShort ); |
sewardj | 8c7f1ab | 2004-07-29 20:31:09 +0000 | [diff] [blame] | 443 | case CC_OP_LOGICL: ACTIONS_LOGIC( 32, UInt ); |
sewardj | 741153c | 2004-07-25 23:39:13 +0000 | [diff] [blame] | 444 | |
sewardj | 8c7f1ab | 2004-07-29 20:31:09 +0000 | [diff] [blame] | 445 | case CC_OP_INCB: ACTIONS_INC( 8, UChar ); |
sewardj | a238471 | 2004-07-29 14:36:40 +0000 | [diff] [blame] | 446 | case CC_OP_INCW: ACTIONS_INC( 16, UShort ); |
sewardj | 8c7f1ab | 2004-07-29 20:31:09 +0000 | [diff] [blame] | 447 | case CC_OP_INCL: ACTIONS_INC( 32, UInt ); |
sewardj | 1813dbe | 2004-07-28 17:09:04 +0000 | [diff] [blame] | 448 | |
sewardj | 8c7f1ab | 2004-07-29 20:31:09 +0000 | [diff] [blame] | 449 | case CC_OP_DECB: ACTIONS_DEC( 8, UChar ); |
sewardj | a238471 | 2004-07-29 14:36:40 +0000 | [diff] [blame] | 450 | case CC_OP_DECW: ACTIONS_DEC( 16, UShort ); |
sewardj | 8c7f1ab | 2004-07-29 20:31:09 +0000 | [diff] [blame] | 451 | case CC_OP_DECL: ACTIONS_DEC( 32, UInt ); |
sewardj | 1813dbe | 2004-07-28 17:09:04 +0000 | [diff] [blame] | 452 | |
sewardj | 8c7f1ab | 2004-07-29 20:31:09 +0000 | [diff] [blame] | 453 | case CC_OP_SHLB: ACTIONS_SHL( 8, UChar ); |
| 454 | case CC_OP_SHLW: ACTIONS_SHL( 16, UShort ); |
| 455 | case CC_OP_SHLL: ACTIONS_SHL( 32, UInt ); |
sewardj | a238471 | 2004-07-29 14:36:40 +0000 | [diff] [blame] | 456 | |
sewardj | 2a2ba8b | 2004-11-08 13:14:06 +0000 | [diff] [blame] | 457 | case CC_OP_SHRB: ACTIONS_SHR( 8, UChar ); |
| 458 | case CC_OP_SHRW: ACTIONS_SHR( 16, UShort ); |
| 459 | case CC_OP_SHRL: ACTIONS_SHR( 32, UInt ); |
sewardj | 8c7f1ab | 2004-07-29 20:31:09 +0000 | [diff] [blame] | 460 | |
| 461 | case CC_OP_ROLB: ACTIONS_ROL( 8, UChar ); |
| 462 | case CC_OP_ROLW: ACTIONS_ROL( 16, UShort ); |
| 463 | case CC_OP_ROLL: ACTIONS_ROL( 32, UInt ); |
| 464 | |
| 465 | case CC_OP_RORB: ACTIONS_ROR( 8, UChar ); |
sewardj | a238471 | 2004-07-29 14:36:40 +0000 | [diff] [blame] | 466 | case CC_OP_RORW: ACTIONS_ROR( 16, UShort ); |
sewardj | 8c7f1ab | 2004-07-29 20:31:09 +0000 | [diff] [blame] | 467 | case CC_OP_RORL: ACTIONS_ROR( 32, UInt ); |
sewardj | a238471 | 2004-07-29 14:36:40 +0000 | [diff] [blame] | 468 | |
sewardj | 2a2ba8b | 2004-11-08 13:14:06 +0000 | [diff] [blame] | 469 | case CC_OP_UMULB: ACTIONS_UMUL( 8, UChar, UShort ); |
| 470 | case CC_OP_UMULW: ACTIONS_UMUL( 16, UShort, UInt ); |
| 471 | case CC_OP_UMULL: ACTIONS_UMUL( 32, UInt, ULong ); |
sewardj | 56296d8 | 2004-07-30 01:52:45 +0000 | [diff] [blame] | 472 | |
sewardj | 2a2ba8b | 2004-11-08 13:14:06 +0000 | [diff] [blame] | 473 | case CC_OP_SMULB: ACTIONS_SMUL( 8, Char, Short ); |
| 474 | case CC_OP_SMULW: ACTIONS_SMUL( 16, Short, Int ); |
| 475 | case CC_OP_SMULL: ACTIONS_SMUL( 32, Int, Long ); |
sewardj | 7ed2295 | 2004-07-29 00:09:58 +0000 | [diff] [blame] | 476 | |
sewardj | 36ca513 | 2004-07-24 13:12:23 +0000 | [diff] [blame] | 477 | default: |
| 478 | /* shouldn't really make these calls from generated code */ |
sewardj | 2a2ba8b | 2004-11-08 13:14:06 +0000 | [diff] [blame] | 479 | vex_printf("calculate_eflags_all( %d, 0x%x, 0x%x, 0x%x )\n", |
| 480 | cc_op, cc_dep1_formal, cc_dep2_formal, cc_ndep_formal ); |
sewardj | 36ca513 | 2004-07-24 13:12:23 +0000 | [diff] [blame] | 481 | vpanic("calculate_eflags_all"); |
| 482 | } |
| 483 | } |
| 484 | |
sewardj | b9c5cf6 | 2004-08-24 15:10:38 +0000 | [diff] [blame] | 485 | |
sewardj | 9aebb0c | 2004-10-24 19:20:43 +0000 | [diff] [blame] | 486 | /* CALLED FROM GENERATED CODE: CLEAN HELPER */ |
sewardj | b9c5cf6 | 2004-08-24 15:10:38 +0000 | [diff] [blame] | 487 | /* Calculate just the carry flag from the supplied thunk parameters. */ |
sewardj | 2a2ba8b | 2004-11-08 13:14:06 +0000 | [diff] [blame] | 488 | UInt calculate_eflags_c ( UInt cc_op, |
| 489 | UInt cc_dep1, |
| 490 | UInt cc_dep2, |
| 491 | UInt cc_ndep ) |
sewardj | 36ca513 | 2004-07-24 13:12:23 +0000 | [diff] [blame] | 492 | { |
sewardj | 9eab588 | 2004-08-25 16:38:30 +0000 | [diff] [blame] | 493 | /* Fast-case some common ones. */ |
sewardj | 43c4695 | 2004-10-23 01:13:57 +0000 | [diff] [blame] | 494 | switch (cc_op) { |
| 495 | case CC_OP_LOGICL: case CC_OP_LOGICW: case CC_OP_LOGICB: |
| 496 | return 0; |
sewardj | 93d96e9 | 2004-11-08 17:39:55 +0000 | [diff] [blame] | 497 | case CC_OP_SUBL: |
| 498 | return ((UInt)cc_dep1) < ((UInt)cc_dep2) |
| 499 | ? CC_MASK_C : 0; |
| 500 | #if 0 |
| 501 | case CC_OP_SUBB: |
| 502 | return ((UInt)(cc_dep1 & 0xFF)) < ((UInt)(cc_dep2 & 0xFF)) |
| 503 | ? CC_MASK_C : 0; |
| 504 | #endif |
| 505 | #if 0 |
sewardj | 43c4695 | 2004-10-23 01:13:57 +0000 | [diff] [blame] | 506 | case CC_OP_DECL: |
| 507 | return cc_src; |
sewardj | 43c4695 | 2004-10-23 01:13:57 +0000 | [diff] [blame] | 508 | case CC_OP_ADDL: |
| 509 | return ( ((UInt)cc_src + (UInt)cc_dst) < ((UInt)cc_src) ) |
| 510 | ? CC_MASK_C : 0; |
| 511 | case CC_OP_SUBB: |
| 512 | return ( ((UInt)(cc_src & 0xFF)) > ((UInt)(cc_dst & 0xFF)) ) |
| 513 | ? CC_MASK_C : 0; |
sewardj | 948d48b | 2004-11-05 19:49:09 +0000 | [diff] [blame] | 514 | #endif |
sewardj | 43c4695 | 2004-10-23 01:13:57 +0000 | [diff] [blame] | 515 | default: |
| 516 | break; |
| 517 | } |
sewardj | 9eab588 | 2004-08-25 16:38:30 +0000 | [diff] [blame] | 518 | |
sewardj | 84ff065 | 2004-08-23 16:16:08 +0000 | [diff] [blame] | 519 | # if PROFILE_EFLAGS |
| 520 | if (!initted) |
| 521 | initCounts(); |
| 522 | tabc[cc_op]++; |
| 523 | |
| 524 | n_calc_c++; |
| 525 | # endif |
sewardj | 2a2ba8b | 2004-11-08 13:14:06 +0000 | [diff] [blame] | 526 | return calculate_eflags_all(cc_op,cc_dep1,cc_dep2,cc_ndep) & CC_MASK_C; |
sewardj | 36ca513 | 2004-07-24 13:12:23 +0000 | [diff] [blame] | 527 | } |
| 528 | |
| 529 | |
sewardj | 9aebb0c | 2004-10-24 19:20:43 +0000 | [diff] [blame] | 530 | /* CALLED FROM GENERATED CODE: CLEAN HELPER */ |
sewardj | 84ff065 | 2004-08-23 16:16:08 +0000 | [diff] [blame] | 531 | /* returns 1 or 0 */ |
| 532 | /*static*/ UInt calculate_condition ( UInt/*Condcode*/ cond, |
sewardj | 2a2ba8b | 2004-11-08 13:14:06 +0000 | [diff] [blame] | 533 | UInt cc_op, |
| 534 | UInt cc_dep1, |
| 535 | UInt cc_dep2, |
| 536 | UInt cc_ndep ) |
sewardj | 84ff065 | 2004-08-23 16:16:08 +0000 | [diff] [blame] | 537 | { |
sewardj | 2a2ba8b | 2004-11-08 13:14:06 +0000 | [diff] [blame] | 538 | UInt eflags = calculate_eflags_all(cc_op, cc_dep1, cc_dep2, cc_ndep); |
sewardj | 84ff065 | 2004-08-23 16:16:08 +0000 | [diff] [blame] | 539 | UInt of,sf,zf,cf,pf; |
| 540 | UInt inv = cond & 1; |
| 541 | |
| 542 | # if PROFILE_EFLAGS |
| 543 | if (!initted) |
| 544 | initCounts(); |
| 545 | |
| 546 | tab[cc_op][cond]++; |
| 547 | n_calc_cond++; |
| 548 | |
sewardj | 93d96e9 | 2004-11-08 17:39:55 +0000 | [diff] [blame] | 549 | if (0 == ((n_calc_all+n_calc_c) & 0x7FFFF)) showCounts(); |
sewardj | 84ff065 | 2004-08-23 16:16:08 +0000 | [diff] [blame] | 550 | # endif |
| 551 | |
| 552 | switch (cond) { |
| 553 | case CondNO: |
| 554 | case CondO: /* OF == 1 */ |
| 555 | of = eflags >> CC_SHIFT_O; |
| 556 | return 1 & (inv ^ of); |
| 557 | |
| 558 | case CondNZ: |
| 559 | case CondZ: /* ZF == 1 */ |
| 560 | zf = eflags >> CC_SHIFT_Z; |
| 561 | return 1 & (inv ^ zf); |
| 562 | |
| 563 | case CondNB: |
| 564 | case CondB: /* CF == 1 */ |
| 565 | cf = eflags >> CC_SHIFT_C; |
| 566 | return 1 & (inv ^ cf); |
| 567 | break; |
| 568 | |
| 569 | case CondNBE: |
| 570 | case CondBE: /* (CF or ZF) == 1 */ |
| 571 | cf = eflags >> CC_SHIFT_C; |
| 572 | zf = eflags >> CC_SHIFT_Z; |
| 573 | return 1 & (inv ^ (cf | zf)); |
| 574 | break; |
| 575 | |
| 576 | case CondNS: |
| 577 | case CondS: /* SF == 1 */ |
| 578 | sf = eflags >> CC_SHIFT_S; |
| 579 | return 1 & (inv ^ sf); |
| 580 | |
| 581 | case CondNP: |
| 582 | case CondP: /* PF == 1 */ |
| 583 | pf = eflags >> CC_SHIFT_P; |
| 584 | return 1 & (inv ^ pf); |
| 585 | |
| 586 | case CondNL: |
| 587 | case CondL: /* (SF xor OF) == 1 */ |
| 588 | sf = eflags >> CC_SHIFT_S; |
| 589 | of = eflags >> CC_SHIFT_O; |
| 590 | return 1 & (inv ^ (sf ^ of)); |
| 591 | break; |
| 592 | |
| 593 | case CondNLE: |
| 594 | case CondLE: /* ((SF xor OF) or ZF) == 1 */ |
| 595 | sf = eflags >> CC_SHIFT_S; |
| 596 | of = eflags >> CC_SHIFT_O; |
| 597 | zf = eflags >> CC_SHIFT_Z; |
| 598 | return 1 & (inv ^ ((sf ^ of) | zf)); |
| 599 | break; |
| 600 | |
| 601 | default: |
| 602 | /* shouldn't really make these calls from generated code */ |
sewardj | 2a2ba8b | 2004-11-08 13:14:06 +0000 | [diff] [blame] | 603 | vex_printf("calculate_condition( %d, %d, 0x%x, 0x%x, 0x%x )\n", |
| 604 | cond, cc_op, cc_dep1, cc_dep2, cc_ndep ); |
sewardj | 84ff065 | 2004-08-23 16:16:08 +0000 | [diff] [blame] | 605 | vpanic("calculate_condition"); |
| 606 | } |
| 607 | } |
| 608 | |
| 609 | |
sewardj | 84ff065 | 2004-08-23 16:16:08 +0000 | [diff] [blame] | 610 | /* Used by the optimiser to try specialisations. Returns an |
| 611 | equivalent expression, or NULL if none. */ |
| 612 | |
| 613 | static Bool isU32 ( IRExpr* e, UInt n ) |
| 614 | { |
| 615 | return e->tag == Iex_Const |
| 616 | && e->Iex.Const.con->tag == Ico_U32 |
| 617 | && e->Iex.Const.con->Ico.U32 == n; |
| 618 | } |
| 619 | |
| 620 | IRExpr* x86guest_spechelper ( Char* function_name, |
| 621 | IRExpr** args ) |
| 622 | { |
| 623 | # define unop(_op,_a1) IRExpr_Unop((_op),(_a1)) |
| 624 | # define binop(_op,_a1,_a2) IRExpr_Binop((_op),(_a1),(_a2)) |
| 625 | # define mkU32(_n) IRExpr_Const(IRConst_U32(_n)) |
| 626 | |
| 627 | Int i, arity = 0; |
| 628 | for (i = 0; args[i]; i++) |
| 629 | arity++; |
| 630 | # if 0 |
| 631 | vex_printf("spec request:\n"); |
| 632 | vex_printf(" %s ", function_name); |
| 633 | for (i = 0; i < arity; i++) { |
| 634 | vex_printf(" "); |
| 635 | ppIRExpr(args[i]); |
| 636 | } |
| 637 | vex_printf("\n"); |
| 638 | # endif |
sewardj | 93d96e9 | 2004-11-08 17:39:55 +0000 | [diff] [blame] | 639 | |
| 640 | /* --------- specialising "calculate_eflags_c" --------- */ |
| 641 | |
sewardj | 84ff065 | 2004-08-23 16:16:08 +0000 | [diff] [blame] | 642 | if (vex_streq(function_name, "calculate_eflags_c")) { |
| 643 | /* specialise calls to above "calculate_eflags_c" function */ |
sewardj | 93d96e9 | 2004-11-08 17:39:55 +0000 | [diff] [blame] | 644 | IRExpr *cc_op, *cc_dep1, *cc_dep2, *cc_ndep; |
| 645 | vassert(arity == 4); |
| 646 | cc_op = args[0]; |
| 647 | cc_dep1 = args[1]; |
| 648 | cc_dep2 = args[2]; |
| 649 | cc_ndep = args[3]; |
sewardj | 84ff065 | 2004-08-23 16:16:08 +0000 | [diff] [blame] | 650 | |
sewardj | 93d96e9 | 2004-11-08 17:39:55 +0000 | [diff] [blame] | 651 | if (isU32(cc_op, CC_OP_SUBL)) { |
| 652 | /* C after sub denotes unsigned less than */ |
| 653 | return unop(Iop_1Uto32, |
| 654 | binop(Iop_CmpLT32U, cc_dep1, cc_dep2)); |
| 655 | } |
sewardj | 84ff065 | 2004-08-23 16:16:08 +0000 | [diff] [blame] | 656 | if (isU32(cc_op, CC_OP_LOGICL)) { |
| 657 | /* cflag after logic is zero */ |
| 658 | return mkU32(0); |
| 659 | } |
| 660 | if (isU32(cc_op, CC_OP_DECL) || isU32(cc_op, CC_OP_INCL)) { |
sewardj | 22419b8 | 2004-11-10 23:27:07 +0000 | [diff] [blame^] | 661 | /* If the thunk is dec or inc, the cflag is supplied as CC_NDEP. */ |
sewardj | e7bd068 | 2004-11-10 23:25:37 +0000 | [diff] [blame] | 662 | return cc_ndep; |
sewardj | 84ff065 | 2004-08-23 16:16:08 +0000 | [diff] [blame] | 663 | } |
sewardj | 84ff065 | 2004-08-23 16:16:08 +0000 | [diff] [blame] | 664 | # if 0 |
| 665 | if (cc_op->tag == Iex_Const) { |
| 666 | vex_printf("CFLAG "); ppIRExpr(cc_op); vex_printf("\n"); |
| 667 | } |
| 668 | # endif |
| 669 | |
| 670 | return NULL; |
| 671 | } |
| 672 | |
sewardj | 93d96e9 | 2004-11-08 17:39:55 +0000 | [diff] [blame] | 673 | /* --------- specialising "calculate_condition" --------- */ |
| 674 | |
sewardj | 84ff065 | 2004-08-23 16:16:08 +0000 | [diff] [blame] | 675 | if (vex_streq(function_name, "calculate_condition")) { |
| 676 | /* specialise calls to above "calculate condition" function */ |
sewardj | 93d96e9 | 2004-11-08 17:39:55 +0000 | [diff] [blame] | 677 | IRExpr *cond, *cc_op, *cc_dep1, *cc_dep2, *cc_ndep; |
| 678 | vassert(arity == 5); |
| 679 | cond = args[0]; |
| 680 | cc_op = args[1]; |
| 681 | cc_dep1 = args[2]; |
| 682 | cc_dep2 = args[3]; |
| 683 | cc_ndep = args[4]; |
sewardj | 84ff065 | 2004-08-23 16:16:08 +0000 | [diff] [blame] | 684 | |
sewardj | 46ccb51 | 2004-08-26 01:25:07 +0000 | [diff] [blame] | 685 | /*---------------- SUBL ----------------*/ |
sewardj | 84ff065 | 2004-08-23 16:16:08 +0000 | [diff] [blame] | 686 | |
| 687 | if (isU32(cc_op, CC_OP_SUBL) && isU32(cond, CondZ)) { |
sewardj | b9c5cf6 | 2004-08-24 15:10:38 +0000 | [diff] [blame] | 688 | /* long sub/cmp, then Z --> test dst==src */ |
| 689 | return unop(Iop_1Uto32, |
sewardj | 93d96e9 | 2004-11-08 17:39:55 +0000 | [diff] [blame] | 690 | binop(Iop_CmpEQ32, cc_dep1, cc_dep2)); |
sewardj | 84ff065 | 2004-08-23 16:16:08 +0000 | [diff] [blame] | 691 | } |
| 692 | |
| 693 | if (isU32(cc_op, CC_OP_SUBL) && isU32(cond, CondL)) { |
sewardj | b9c5cf6 | 2004-08-24 15:10:38 +0000 | [diff] [blame] | 694 | /* long sub/cmp, then L (signed less than) |
| 695 | --> test dst <s src */ |
sewardj | 84ff065 | 2004-08-23 16:16:08 +0000 | [diff] [blame] | 696 | return unop(Iop_1Uto32, |
sewardj | 93d96e9 | 2004-11-08 17:39:55 +0000 | [diff] [blame] | 697 | binop(Iop_CmpLT32S, cc_dep1, cc_dep2)); |
sewardj | 84ff065 | 2004-08-23 16:16:08 +0000 | [diff] [blame] | 698 | } |
| 699 | |
| 700 | if (isU32(cc_op, CC_OP_SUBL) && isU32(cond, CondLE)) { |
sewardj | b9c5cf6 | 2004-08-24 15:10:38 +0000 | [diff] [blame] | 701 | /* long sub/cmp, then LE (signed less than or equal) |
| 702 | --> test dst <=s src */ |
sewardj | 84ff065 | 2004-08-23 16:16:08 +0000 | [diff] [blame] | 703 | return unop(Iop_1Uto32, |
sewardj | 93d96e9 | 2004-11-08 17:39:55 +0000 | [diff] [blame] | 704 | binop(Iop_CmpLE32S, cc_dep1, cc_dep2)); |
sewardj | 84ff065 | 2004-08-23 16:16:08 +0000 | [diff] [blame] | 705 | } |
| 706 | |
| 707 | if (isU32(cc_op, CC_OP_SUBL) && isU32(cond, CondBE)) { |
sewardj | b9c5cf6 | 2004-08-24 15:10:38 +0000 | [diff] [blame] | 708 | /* long sub/cmp, then BE (unsigned less than or equal) |
| 709 | --> test dst <=u src */ |
sewardj | 84ff065 | 2004-08-23 16:16:08 +0000 | [diff] [blame] | 710 | return unop(Iop_1Uto32, |
sewardj | 93d96e9 | 2004-11-08 17:39:55 +0000 | [diff] [blame] | 711 | binop(Iop_CmpLE32U, cc_dep1, cc_dep2)); |
sewardj | 84ff065 | 2004-08-23 16:16:08 +0000 | [diff] [blame] | 712 | } |
sewardj | 93d96e9 | 2004-11-08 17:39:55 +0000 | [diff] [blame] | 713 | #if 0 |
sewardj | af991de | 2004-08-24 23:50:56 +0000 | [diff] [blame] | 714 | if (isU32(cc_op, CC_OP_SUBL) && isU32(cond, CondB)) { |
| 715 | /* long sub/cmp, then B (unsigned less than) |
| 716 | --> test dst <u src */ |
| 717 | return unop(Iop_1Uto32, |
| 718 | binop(Iop_CmpLT32U, cc_dst, cc_src)); |
| 719 | } |
sewardj | 7e6644c | 2004-11-10 12:10:23 +0000 | [diff] [blame] | 720 | #endif |
sewardj | 46ccb51 | 2004-08-26 01:25:07 +0000 | [diff] [blame] | 721 | /*---------------- SUBW ----------------*/ |
| 722 | |
| 723 | if (isU32(cc_op, CC_OP_SUBW) && isU32(cond, CondZ)) { |
| 724 | /* byte sub/cmp, then Z --> test dst==src */ |
| 725 | return unop(Iop_1Uto32, |
| 726 | binop(Iop_CmpEQ16, |
sewardj | 7e6644c | 2004-11-10 12:10:23 +0000 | [diff] [blame] | 727 | unop(Iop_32to16,cc_dep1), |
| 728 | unop(Iop_32to16,cc_dep2))); |
sewardj | 46ccb51 | 2004-08-26 01:25:07 +0000 | [diff] [blame] | 729 | } |
| 730 | |
| 731 | /*---------------- SUBB ----------------*/ |
sewardj | 7e6644c | 2004-11-10 12:10:23 +0000 | [diff] [blame] | 732 | |
sewardj | 46ccb51 | 2004-08-26 01:25:07 +0000 | [diff] [blame] | 733 | if (isU32(cc_op, CC_OP_SUBB) && isU32(cond, CondZ)) { |
| 734 | /* byte sub/cmp, then Z --> test dst==src */ |
| 735 | return unop(Iop_1Uto32, |
| 736 | binop(Iop_CmpEQ8, |
sewardj | 93d96e9 | 2004-11-08 17:39:55 +0000 | [diff] [blame] | 737 | unop(Iop_32to8,cc_dep1), |
| 738 | unop(Iop_32to8,cc_dep2))); |
sewardj | 46ccb51 | 2004-08-26 01:25:07 +0000 | [diff] [blame] | 739 | } |
| 740 | |
| 741 | if (isU32(cc_op, CC_OP_SUBB) && isU32(cond, CondNZ)) { |
sewardj | 7e5b7cd | 2004-11-08 18:20:23 +0000 | [diff] [blame] | 742 | /* byte sub/cmp, then NZ --> test dst!=src */ |
sewardj | 46ccb51 | 2004-08-26 01:25:07 +0000 | [diff] [blame] | 743 | return unop(Iop_1Uto32, |
| 744 | binop(Iop_CmpNE8, |
sewardj | 93d96e9 | 2004-11-08 17:39:55 +0000 | [diff] [blame] | 745 | unop(Iop_32to8,cc_dep1), |
| 746 | unop(Iop_32to8,cc_dep2))); |
sewardj | 46ccb51 | 2004-08-26 01:25:07 +0000 | [diff] [blame] | 747 | } |
sewardj | 22419b8 | 2004-11-10 23:27:07 +0000 | [diff] [blame^] | 748 | |
sewardj | 46ccb51 | 2004-08-26 01:25:07 +0000 | [diff] [blame] | 749 | if (isU32(cc_op, CC_OP_SUBB) && isU32(cond, CondNBE)) { |
| 750 | /* long sub/cmp, then NBE (unsigned greater than) |
| 751 | --> test src <=u dst */ |
sewardj | 7e5b7cd | 2004-11-08 18:20:23 +0000 | [diff] [blame] | 752 | /* Note, args are opposite way round from the usual */ |
sewardj | 46ccb51 | 2004-08-26 01:25:07 +0000 | [diff] [blame] | 753 | return unop(Iop_1Uto32, |
| 754 | binop(Iop_CmpLT32U, |
sewardj | 7e5b7cd | 2004-11-08 18:20:23 +0000 | [diff] [blame] | 755 | binop(Iop_And32,cc_dep2,mkU32(0xFF)), |
| 756 | binop(Iop_And32,cc_dep1,mkU32(0xFF)))); |
sewardj | 46ccb51 | 2004-08-26 01:25:07 +0000 | [diff] [blame] | 757 | } |
| 758 | |
| 759 | /*---------------- LOGICL ----------------*/ |
| 760 | |
| 761 | if (isU32(cc_op, CC_OP_LOGICL) && isU32(cond, CondZ)) { |
| 762 | /* long and/or/xor, then Z --> test dst==0 */ |
sewardj | 93d96e9 | 2004-11-08 17:39:55 +0000 | [diff] [blame] | 763 | return unop(Iop_1Uto32,binop(Iop_CmpEQ32, cc_dep1, mkU32(0))); |
sewardj | 46ccb51 | 2004-08-26 01:25:07 +0000 | [diff] [blame] | 764 | } |
sewardj | 22419b8 | 2004-11-10 23:27:07 +0000 | [diff] [blame^] | 765 | |
sewardj | 46ccb51 | 2004-08-26 01:25:07 +0000 | [diff] [blame] | 766 | if (isU32(cc_op, CC_OP_LOGICL) && isU32(cond, CondS)) { |
| 767 | /* long and/or/xor, then S --> test dst <s 0 */ |
sewardj | 7e6644c | 2004-11-10 12:10:23 +0000 | [diff] [blame] | 768 | return unop(Iop_1Uto32,binop(Iop_CmpLT32S, cc_dep1, mkU32(0))); |
sewardj | 46ccb51 | 2004-08-26 01:25:07 +0000 | [diff] [blame] | 769 | } |
sewardj | 22419b8 | 2004-11-10 23:27:07 +0000 | [diff] [blame^] | 770 | |
sewardj | 46ccb51 | 2004-08-26 01:25:07 +0000 | [diff] [blame] | 771 | if (isU32(cc_op, CC_OP_LOGICL) && isU32(cond, CondLE)) { |
| 772 | /* long and/or/xor, then LE |
| 773 | This is pretty subtle. LOGIC sets SF and ZF according to the |
| 774 | result and makes OF be zero. LE computes (SZ ^ OF) | ZF, but |
| 775 | OF is zero, so this reduces to SZ | ZF -- which will be 1 iff |
| 776 | the result is <=signed 0. Hence ... |
| 777 | */ |
sewardj | 93d96e9 | 2004-11-08 17:39:55 +0000 | [diff] [blame] | 778 | return unop(Iop_1Uto32,binop(Iop_CmpLE32S, cc_dep1, mkU32(0))); |
sewardj | 46ccb51 | 2004-08-26 01:25:07 +0000 | [diff] [blame] | 779 | } |
| 780 | |
| 781 | /*---------------- LOGICB ----------------*/ |
| 782 | |
| 783 | if (isU32(cc_op, CC_OP_LOGICB) && isU32(cond, CondZ)) { |
| 784 | /* byte and/or/xor, then Z --> test dst==0 */ |
| 785 | return unop(Iop_1Uto32, |
sewardj | 93d96e9 | 2004-11-08 17:39:55 +0000 | [diff] [blame] | 786 | binop(Iop_CmpEQ32, binop(Iop_And32,cc_dep1,mkU32(255)), |
sewardj | 46ccb51 | 2004-08-26 01:25:07 +0000 | [diff] [blame] | 787 | mkU32(0))); |
| 788 | } |
| 789 | |
| 790 | /*---------------- DECL ----------------*/ |
| 791 | |
sewardj | 84ff065 | 2004-08-23 16:16:08 +0000 | [diff] [blame] | 792 | if (isU32(cc_op, CC_OP_DECL) && isU32(cond, CondZ)) { |
| 793 | /* dec L, then Z --> test dst == 0 */ |
sewardj | 93d96e9 | 2004-11-08 17:39:55 +0000 | [diff] [blame] | 794 | return unop(Iop_1Uto32,binop(Iop_CmpEQ32, cc_dep1, mkU32(0))); |
sewardj | 84ff065 | 2004-08-23 16:16:08 +0000 | [diff] [blame] | 795 | } |
sewardj | 22419b8 | 2004-11-10 23:27:07 +0000 | [diff] [blame^] | 796 | |
sewardj | fae2ca7 | 2004-08-24 01:16:01 +0000 | [diff] [blame] | 797 | if (isU32(cc_op, CC_OP_DECL) && isU32(cond, CondS)) { |
| 798 | /* dec L, then S --> compare DST <s 0 */ |
sewardj | 7e6644c | 2004-11-10 12:10:23 +0000 | [diff] [blame] | 799 | return unop(Iop_1Uto32,binop(Iop_CmpLT32S, cc_dep1, mkU32(0))); |
sewardj | fae2ca7 | 2004-08-24 01:16:01 +0000 | [diff] [blame] | 800 | } |
| 801 | |
sewardj | 84ff065 | 2004-08-23 16:16:08 +0000 | [diff] [blame] | 802 | return NULL; |
| 803 | } |
| 804 | |
| 805 | # undef unop |
| 806 | # undef binop |
| 807 | # undef mkU32 |
| 808 | |
| 809 | return NULL; |
| 810 | } |
| 811 | |
sewardj | 36ca513 | 2004-07-24 13:12:23 +0000 | [diff] [blame] | 812 | |
sewardj | 0c2cb62 | 2004-09-06 23:21:21 +0000 | [diff] [blame] | 813 | /*-----------------------------------------------------------*/ |
| 814 | /*--- Utility functions for x87 FPU conversions. ---*/ |
| 815 | /*-----------------------------------------------------------*/ |
| 816 | |
| 817 | |
| 818 | /* 80 and 64-bit floating point formats: |
| 819 | |
| 820 | 80-bit: |
| 821 | |
| 822 | S 0 0-------0 zero |
| 823 | S 0 0X------X denormals |
| 824 | S 1-7FFE 1X------X normals (all normals have leading 1) |
| 825 | S 7FFF 10------0 infinity |
| 826 | S 7FFF 10X-----X snan |
| 827 | S 7FFF 11X-----X qnan |
| 828 | |
| 829 | S is the sign bit. For runs X----X, at least one of the Xs must be |
| 830 | nonzero. Exponent is 15 bits, fractional part is 63 bits, and |
| 831 | there is an explicitly represented leading 1, and a sign bit, |
| 832 | giving 80 in total. |
| 833 | |
| 834 | 64-bit avoids the confusion of an explicitly represented leading 1 |
| 835 | and so is simpler: |
| 836 | |
| 837 | S 0 0------0 zero |
| 838 | S 0 X------X denormals |
| 839 | S 1-7FE any normals |
| 840 | S 7FF 0------0 infinity |
| 841 | S 7FF 0X-----X snan |
| 842 | S 7FF 1X-----X qnan |
| 843 | |
| 844 | Exponent is 11 bits, fractional part is 52 bits, and there is a |
| 845 | sign bit, giving 64 in total. |
| 846 | */ |
| 847 | |
sewardj | c4be80c | 2004-09-10 16:17:45 +0000 | [diff] [blame] | 848 | static inline Bool host_is_little_endian ( void ) |
| 849 | { |
| 850 | UInt x = 0x76543210; |
| 851 | UChar* p = (UChar*)(&x); |
| 852 | return (*p == 0x10); |
| 853 | } |
| 854 | |
sewardj | 9aebb0c | 2004-10-24 19:20:43 +0000 | [diff] [blame] | 855 | /* CALLED FROM GENERATED CODE: CLEAN HELPER */ |
sewardj | 8ea867b | 2004-10-30 19:03:02 +0000 | [diff] [blame] | 856 | UInt calculate_FXAM ( UInt tag, ULong dbl ) |
sewardj | c4be80c | 2004-09-10 16:17:45 +0000 | [diff] [blame] | 857 | { |
| 858 | Bool mantissaIsZero; |
| 859 | Int bexp; |
| 860 | UChar sign; |
| 861 | UInt c1; |
| 862 | UChar* f64; |
| 863 | |
sewardj | f5e3667 | 2004-09-21 23:38:53 +0000 | [diff] [blame] | 864 | if (!host_is_little_endian()) { |
sewardj | c4be80c | 2004-09-10 16:17:45 +0000 | [diff] [blame] | 865 | vassert(0); |
| 866 | } |
| 867 | |
| 868 | /* vex_printf("calculate_FXAM ( %d, %llx ) .. ", tag, dbl ); */ |
| 869 | |
| 870 | f64 = (UChar*)(&dbl); |
| 871 | sign = (f64[7] >> 7) & 1; |
| 872 | |
| 873 | /* First off, if the tag indicates the register was empty, |
| 874 | return 1,0,sign,1 */ |
| 875 | if (tag == 0) { |
| 876 | /* vex_printf("Empty\n"); */ |
| 877 | return FC_MASK_C3 | 0 | sign | FC_MASK_C0; |
| 878 | } |
| 879 | |
| 880 | bexp = (f64[7] << 4) | ((f64[6] >> 4) & 0x0F); |
| 881 | bexp &= 0x7FF; |
| 882 | |
| 883 | c1 = ((UInt)sign) << 9; |
| 884 | |
| 885 | mantissaIsZero |
| 886 | = (f64[6] & 0x0F) == 0 |
| 887 | && (f64[5] | f64[4] | f64[3] | f64[2] | f64[1] | f64[0]) == 0; |
| 888 | |
| 889 | /* If both exponent and mantissa are zero, the value is zero. |
| 890 | Return 1,0,sign,0. */ |
| 891 | if (bexp == 0 && mantissaIsZero) { |
| 892 | /* vex_printf("Zero\n"); */ |
| 893 | return FC_MASK_C3 | 0 | sign | 0; |
| 894 | } |
| 895 | |
| 896 | /* If exponent is zero but mantissa isn't, it's a denormal. |
| 897 | Return 1,1,sign,0. */ |
| 898 | if (bexp == 0 && !mantissaIsZero) { |
| 899 | /* vex_printf("Denormal\n"); */ |
| 900 | return FC_MASK_C3 | FC_MASK_C2 | sign | 0; |
| 901 | } |
| 902 | |
| 903 | /* If the exponent is 7FF and the mantissa is zero, this is an infinity. |
| 904 | Return 0,1,sign,1. */ |
| 905 | if (bexp == 0x7FF && mantissaIsZero) { |
| 906 | /* vex_printf("Inf\n"); */ |
| 907 | return 0 | FC_MASK_C2 | sign | FC_MASK_C0; |
| 908 | } |
| 909 | |
| 910 | /* If the exponent is 7FF and the mantissa isn't zero, this is a NaN. |
| 911 | Return 0,0,sign,1. */ |
| 912 | if (bexp == 0x7FF && !mantissaIsZero) { |
| 913 | /* vex_printf("NaN\n"); */ |
| 914 | return 0 | 0 | sign | FC_MASK_C0; |
| 915 | } |
| 916 | |
| 917 | /* Uh, ok, we give up. It must be a normal finite number. |
| 918 | Return 0,1,sign,0. |
| 919 | */ |
| 920 | /* vex_printf("normal\n"); */ |
| 921 | return 0 | FC_MASK_C2 | sign | 0; |
| 922 | } |
| 923 | |
sewardj | 0c2cb62 | 2004-09-06 23:21:21 +0000 | [diff] [blame] | 924 | |
| 925 | /* Convert a IEEE754 double (64-bit) into an x87 extended double |
| 926 | (80-bit), mimicing the hardware fairly closely. Both numbers are |
| 927 | stored little-endian. Limitations, all of which could be fixed, |
| 928 | given some level of hassle: |
| 929 | |
| 930 | * Does not handle double precision denormals. As a result, values |
| 931 | with magnitudes less than 1e-308 are flushed to zero when they |
| 932 | need not be. |
| 933 | |
| 934 | * Identity of NaNs is not preserved. |
| 935 | |
| 936 | See comments in the code for more details. |
| 937 | */ |
| 938 | static void convert_f64le_to_f80le ( /*IN*/UChar* f64, /*OUT*/UChar* f80 ) |
| 939 | { |
| 940 | Bool isInf; |
| 941 | Int bexp; |
| 942 | UChar sign; |
| 943 | |
| 944 | sign = (f64[7] >> 7) & 1; |
| 945 | bexp = (f64[7] << 4) | ((f64[6] >> 4) & 0x0F); |
| 946 | bexp &= 0x7FF; |
| 947 | |
| 948 | /* If the exponent is zero, either we have a zero or a denormal. |
| 949 | Produce a zero. This is a hack in that it forces denormals to |
| 950 | zero. Could do better. */ |
| 951 | if (bexp == 0) { |
| 952 | f80[9] = sign << 7; |
| 953 | f80[8] = f80[7] = f80[6] = f80[5] = f80[4] |
| 954 | = f80[3] = f80[2] = f80[1] = f80[0] = 0; |
| 955 | return; |
| 956 | } |
| 957 | |
| 958 | /* If the exponent is 7FF, this is either an Infinity, a SNaN or |
| 959 | QNaN, as determined by examining bits 51:0, thus: |
| 960 | 0 ... 0 Inf |
| 961 | 0X ... X SNaN |
| 962 | 1X ... X QNaN |
| 963 | where at least one of the Xs is not zero. |
| 964 | */ |
| 965 | if (bexp == 0x7FF) { |
| 966 | isInf = (f64[6] & 0x0F) == 0 |
| 967 | && f64[5] == 0 && f64[4] == 0 && f64[3] == 0 |
| 968 | && f64[2] == 0 && f64[1] == 0 && f64[0] == 0; |
| 969 | if (isInf) { |
| 970 | /* Produce an appropriately signed infinity: |
| 971 | S 1--1 (15) 1 0--0 (63) |
| 972 | */ |
| 973 | f80[9] = (sign << 7) | 0x7F; |
| 974 | f80[8] = 0xFF; |
| 975 | f80[7] = 0x80; |
| 976 | f80[6] = f80[5] = f80[4] = f80[3] |
| 977 | = f80[2] = f80[1] = f80[0] = 0; |
| 978 | return; |
| 979 | } |
| 980 | /* So it's either a QNaN or SNaN. Distinguish by considering |
| 981 | bit 51. Note, this destroys all the trailing bits |
| 982 | (identity?) of the NaN. IEEE754 doesn't require preserving |
| 983 | these (it only requires that there be one QNaN value and one |
| 984 | SNaN value), but x87 does seem to have some ability to |
| 985 | preserve them. Anyway, here, the NaN's identity is |
| 986 | destroyed. Could be improved. */ |
| 987 | if (f64[6] & 8) { |
| 988 | /* QNaN. Make a QNaN: |
| 989 | S 1--1 (15) 1 1--1 (63) |
| 990 | */ |
| 991 | f80[9] = (sign << 7) | 0x7F; |
| 992 | f80[8] = 0xFF; |
| 993 | f80[7] = 0xFF; |
| 994 | f80[6] = f80[5] = f80[4] = f80[3] |
| 995 | = f80[2] = f80[1] = f80[0] = 0xFF; |
| 996 | } else { |
| 997 | /* SNaN. Make a SNaN: |
| 998 | S 1--1 (15) 0 1--1 (63) |
| 999 | */ |
| 1000 | f80[9] = (sign << 7) | 0x7F; |
| 1001 | f80[8] = 0xFF; |
| 1002 | f80[7] = 0x7F; |
| 1003 | f80[6] = f80[5] = f80[4] = f80[3] |
| 1004 | = f80[2] = f80[1] = f80[0] = 0xFF; |
| 1005 | } |
| 1006 | return; |
| 1007 | } |
| 1008 | |
| 1009 | /* It's not a zero, denormal, infinity or nan. So it must be a |
| 1010 | normalised number. Rebias the exponent and build the new |
| 1011 | number. */ |
| 1012 | bexp += (16383 - 1023); |
| 1013 | |
| 1014 | f80[9] = (sign << 7) | ((bexp >> 8) & 0xFF); |
| 1015 | f80[8] = bexp & 0xFF; |
| 1016 | f80[7] = (1 << 7) | ((f64[6] << 3) & 0x78) | ((f64[5] >> 5) & 7); |
| 1017 | f80[6] = ((f64[5] << 3) & 0xF8) | ((f64[4] >> 5) & 7); |
| 1018 | f80[5] = ((f64[4] << 3) & 0xF8) | ((f64[3] >> 5) & 7); |
| 1019 | f80[4] = ((f64[3] << 3) & 0xF8) | ((f64[2] >> 5) & 7); |
| 1020 | f80[3] = ((f64[2] << 3) & 0xF8) | ((f64[1] >> 5) & 7); |
| 1021 | f80[2] = ((f64[1] << 3) & 0xF8) | ((f64[0] >> 5) & 7); |
| 1022 | f80[1] = ((f64[0] << 3) & 0xF8); |
| 1023 | f80[0] = 0; |
| 1024 | } |
| 1025 | |
| 1026 | |
| 1027 | ///////////////////////////////////////////////////////////////// |
| 1028 | |
| 1029 | /* Convert a x87 extended double (80-bit) into an IEEE 754 double |
| 1030 | (64-bit), mimicing the hardware fairly closely. Both numbers are |
| 1031 | stored little-endian. Limitations, all of which could be fixed, |
| 1032 | given some level of hassle: |
| 1033 | |
| 1034 | * Does not create double precision denormals. As a result, values |
| 1035 | with magnitudes less than 1e-308 are flushed to zero when they |
| 1036 | need not be. |
| 1037 | |
| 1038 | * Rounding following truncation could be a bit better. |
| 1039 | |
| 1040 | * Identity of NaNs is not preserved. |
| 1041 | |
| 1042 | See comments in the code for more details. |
| 1043 | */ |
| 1044 | static void convert_f80le_to_f64le ( /*IN*/UChar* f80, /*OUT*/UChar* f64 ) |
| 1045 | { |
| 1046 | Bool isInf; |
| 1047 | Int bexp; |
| 1048 | UChar sign; |
| 1049 | |
| 1050 | sign = (f80[9] >> 7) & 1; |
| 1051 | bexp = (((UInt)f80[9]) << 8) | (UInt)f80[8]; |
| 1052 | bexp &= 0x7FFF; |
| 1053 | |
| 1054 | /* If the exponent is zero, either we have a zero or a denormal. |
| 1055 | But an extended precision denormal becomes a double precision |
| 1056 | zero, so in either case, just produce the appropriately signed |
| 1057 | zero. */ |
| 1058 | if (bexp == 0) { |
| 1059 | f64[7] = sign << 7; |
| 1060 | f64[6] = f64[5] = f64[4] = f64[3] = f64[2] = f64[1] = f64[0] = 0; |
| 1061 | return; |
| 1062 | } |
| 1063 | |
| 1064 | /* If the exponent is 7FFF, this is either an Infinity, a SNaN or |
| 1065 | QNaN, as determined by examining bits 62:0, thus: |
| 1066 | 0 ... 0 Inf |
| 1067 | 0X ... X SNaN |
| 1068 | 1X ... X QNaN |
| 1069 | where at least one of the Xs is not zero. |
| 1070 | */ |
| 1071 | if (bexp == 0x7FFF) { |
| 1072 | isInf = (f80[7] & 0x7F) == 0 |
| 1073 | && f80[6] == 0 && f80[5] == 0 && f80[4] == 0 |
| 1074 | && f80[3] == 0 && f80[2] == 0 && f80[1] == 0 && f80[0] == 0; |
| 1075 | if (isInf) { |
| 1076 | if (0 == (f80[7] & 0x80)) |
| 1077 | goto wierd_NaN; |
| 1078 | /* Produce an appropriately signed infinity: |
| 1079 | S 1--1 (11) 0--0 (52) |
| 1080 | */ |
| 1081 | f64[7] = (sign << 7) | 0x7F; |
| 1082 | f64[6] = 0xF0; |
| 1083 | f64[5] = f64[4] = f64[3] = f64[2] = f64[1] = f64[0] = 0; |
| 1084 | return; |
| 1085 | } |
| 1086 | /* So it's either a QNaN or SNaN. Distinguish by considering |
| 1087 | bit 62. Note, this destroys all the trailing bits |
| 1088 | (identity?) of the NaN. IEEE754 doesn't require preserving |
| 1089 | these (it only requires that there be one QNaN value and one |
| 1090 | SNaN value), but x87 does seem to have some ability to |
| 1091 | preserve them. Anyway, here, the NaN's identity is |
| 1092 | destroyed. Could be improved. */ |
| 1093 | if (f80[8] & 0x40) { |
| 1094 | /* QNaN. Make a QNaN: |
| 1095 | S 1--1 (11) 1 1--1 (51) |
| 1096 | */ |
| 1097 | f64[7] = (sign << 7) | 0x7F; |
| 1098 | f64[6] = 0xFF; |
| 1099 | f64[5] = f64[4] = f64[3] = f64[2] = f64[1] = f64[0] = 0xFF; |
| 1100 | } else { |
| 1101 | /* SNaN. Make a SNaN: |
| 1102 | S 1--1 (11) 0 1--1 (51) |
| 1103 | */ |
| 1104 | f64[7] = (sign << 7) | 0x7F; |
| 1105 | f64[6] = 0xF7; |
| 1106 | f64[5] = f64[4] = f64[3] = f64[2] = f64[1] = f64[0] = 0xFF; |
| 1107 | } |
| 1108 | return; |
| 1109 | } |
| 1110 | |
| 1111 | /* If it's not a Zero, NaN or Inf, and the integer part (bit 62) is |
| 1112 | zero, the x87 FPU appears to consider the number denormalised |
| 1113 | and converts it to a QNaN. */ |
| 1114 | if (0 == (f80[7] & 0x80)) { |
| 1115 | wierd_NaN: |
| 1116 | /* Strange hardware QNaN: |
| 1117 | S 1--1 (11) 1 0--0 (51) |
| 1118 | */ |
| 1119 | /* On a PIII, these QNaNs always appear with sign==1. I have |
| 1120 | no idea why. */ |
| 1121 | f64[7] = (1 /*sign*/ << 7) | 0x7F; |
| 1122 | f64[6] = 0xF8; |
| 1123 | f64[5] = f64[4] = f64[3] = f64[2] = f64[1] = f64[0] = 0; |
| 1124 | return; |
| 1125 | } |
| 1126 | |
| 1127 | /* It's not a zero, denormal, infinity or nan. So it must be a |
| 1128 | normalised number. Rebias the exponent and consider. */ |
| 1129 | bexp -= (16383 - 1023); |
| 1130 | if (bexp >= 0x7FF) { |
| 1131 | /* It's too big for a double. Construct an infinity. */ |
| 1132 | f64[7] = (sign << 7) | 0x7F; |
| 1133 | f64[6] = 0xF0; |
| 1134 | f64[5] = f64[4] = f64[3] = f64[2] = f64[1] = f64[0] = 0; |
| 1135 | return; |
| 1136 | } |
| 1137 | |
| 1138 | if (bexp < 0) { |
| 1139 | /* It's too small for a double. Construct a zero. Note, this |
| 1140 | is a kludge since we could conceivably create a |
| 1141 | denormalised number for bexp in -1 to -51, but we don't |
| 1142 | bother. This means the conversion flushes values |
| 1143 | approximately in the range 1e-309 to 1e-324 ish to zero |
| 1144 | when it doesn't actually need to. This could be |
| 1145 | improved. */ |
| 1146 | f64[7] = sign << 7; |
| 1147 | f64[6] = f64[5] = f64[4] = f64[3] = f64[2] = f64[1] = f64[0] = 0; |
| 1148 | return; |
| 1149 | } |
| 1150 | |
| 1151 | /* Ok, it's a normalised number which is representable as a double. |
| 1152 | Copy the exponent and mantissa into place. */ |
| 1153 | /* |
| 1154 | for (i = 0; i < 52; i++) |
| 1155 | write_bit_array ( f64, |
| 1156 | i, |
| 1157 | read_bit_array ( f80, i+11 ) ); |
| 1158 | */ |
| 1159 | f64[0] = (f80[1] >> 3) | (f80[2] << 5); |
| 1160 | f64[1] = (f80[2] >> 3) | (f80[3] << 5); |
| 1161 | f64[2] = (f80[3] >> 3) | (f80[4] << 5); |
| 1162 | f64[3] = (f80[4] >> 3) | (f80[5] << 5); |
| 1163 | f64[4] = (f80[5] >> 3) | (f80[6] << 5); |
| 1164 | f64[5] = (f80[6] >> 3) | (f80[7] << 5); |
| 1165 | |
| 1166 | f64[6] = ((bexp << 4) & 0xF0) | ((f80[7] >> 3) & 0x0F); |
| 1167 | |
| 1168 | f64[7] = (sign << 7) | ((bexp >> 4) & 0x7F); |
| 1169 | |
| 1170 | /* Now consider any rounding that needs to happen as a result of |
| 1171 | truncating the mantissa. */ |
| 1172 | if (f80[1] & 4) /* read_bit_array(f80, 10) == 1) */ { |
| 1173 | /* Round upwards. This is a kludge. Once in every 64k |
| 1174 | roundings (statistically) the bottom two bytes are both 0xFF |
| 1175 | and so we don't round at all. Could be improved. */ |
| 1176 | if (f64[0] != 0xFF) { |
| 1177 | f64[0]++; |
| 1178 | } |
| 1179 | else |
| 1180 | if (f64[0] == 0xFF && f64[1] != 0xFF) { |
| 1181 | f64[0] = 0; |
| 1182 | f64[1]++; |
| 1183 | } |
| 1184 | /* else we don't round, but we should. */ |
| 1185 | } |
| 1186 | } |
| 1187 | |
sewardj | 17442fe | 2004-09-20 14:54:28 +0000 | [diff] [blame] | 1188 | /* CALLED FROM GENERATED CODE */ |
| 1189 | /* DIRTY HELPER (reads guest memory) */ |
sewardj | 8ea867b | 2004-10-30 19:03:02 +0000 | [diff] [blame] | 1190 | ULong loadF80le ( UInt addrU ) |
sewardj | 17442fe | 2004-09-20 14:54:28 +0000 | [diff] [blame] | 1191 | { |
| 1192 | ULong f64; |
| 1193 | convert_f80le_to_f64le ( (UChar*)addrU, (UChar*)&f64 ); |
| 1194 | return f64; |
| 1195 | } |
| 1196 | |
| 1197 | /* CALLED FROM GENERATED CODE */ |
| 1198 | /* DIRTY HELPER (writes guest memory) */ |
sewardj | 8ea867b | 2004-10-30 19:03:02 +0000 | [diff] [blame] | 1199 | void storeF80le ( UInt addrU, ULong f64 ) |
sewardj | 17442fe | 2004-09-20 14:54:28 +0000 | [diff] [blame] | 1200 | { |
| 1201 | convert_f64le_to_f80le( (UChar*)&f64, (UChar*)addrU ); |
| 1202 | } |
| 1203 | |
sewardj | 0c2cb62 | 2004-09-06 23:21:21 +0000 | [diff] [blame] | 1204 | |
| 1205 | /*----------------------------------------------*/ |
| 1206 | /*--- The exported fns .. ---*/ |
| 1207 | /*----------------------------------------------*/ |
| 1208 | |
| 1209 | /* Layout of the real x87 state. */ |
| 1210 | |
| 1211 | typedef |
| 1212 | struct { |
| 1213 | UShort env[14]; |
| 1214 | UChar reg[80]; |
| 1215 | } |
| 1216 | Fpu_State; |
| 1217 | |
| 1218 | /* Offsets, in 16-bit ints, into the FPU environment (env) area. */ |
| 1219 | #define FP_ENV_CTRL 0 |
| 1220 | #define FP_ENV_STAT 2 |
| 1221 | #define FP_ENV_TAG 4 |
| 1222 | #define FP_ENV_IP 6 /* and 7 */ |
| 1223 | #define FP_ENV_CS 8 |
| 1224 | #define FP_ENV_OPOFF 10 /* and 11 */ |
| 1225 | #define FP_ENV_OPSEL 12 |
| 1226 | #define FP_REG(ii) (10*(7-(ii))) |
| 1227 | |
| 1228 | |
| 1229 | /* VISIBLE TO LIBVEX CLIENT */ |
sewardj | 76bdc80 | 2004-10-25 15:33:26 +0000 | [diff] [blame] | 1230 | void LibVEX_GuestX86_put_x87 ( /*IN*/UChar* x87_state, |
| 1231 | /*OUT*/VexGuestX86State* vex_state ) |
sewardj | 0c2cb62 | 2004-09-06 23:21:21 +0000 | [diff] [blame] | 1232 | { |
| 1233 | Int r; |
| 1234 | UInt tag; |
sewardj | f6dc3ce | 2004-10-19 01:03:46 +0000 | [diff] [blame] | 1235 | Double* vexRegs = (Double*)(&vex_state->guest_FPREG[0]); |
| 1236 | UChar* vexTags = (UChar*)(&vex_state->guest_FPTAG[0]); |
sewardj | 0c2cb62 | 2004-09-06 23:21:21 +0000 | [diff] [blame] | 1237 | Fpu_State* x87 = (Fpu_State*)x87_state; |
| 1238 | UInt ftop = (x87->env[FP_ENV_STAT] >> 11) & 7; |
| 1239 | UInt tagw = x87->env[FP_ENV_TAG]; |
sewardj | 6e0dbda | 2004-09-08 19:06:34 +0000 | [diff] [blame] | 1240 | UInt fpucw = x87->env[FP_ENV_CTRL]; |
sewardj | c4be80c | 2004-09-10 16:17:45 +0000 | [diff] [blame] | 1241 | UInt c3210 = x87->env[FP_ENV_STAT] & 0x4700; |
sewardj | 0c2cb62 | 2004-09-06 23:21:21 +0000 | [diff] [blame] | 1242 | |
| 1243 | /* Copy registers and tags */ |
| 1244 | for (r = 0; r < 8; r++) { |
| 1245 | tag = (tagw >> (2*r)) & 3; |
| 1246 | if (tag == 3) { |
| 1247 | /* register is empty */ |
| 1248 | vexRegs[r] = 0.0; |
| 1249 | vexTags[r] = 0; |
| 1250 | } else { |
| 1251 | /* register is non-empty */ |
| 1252 | convert_f80le_to_f64le( &x87->reg[FP_REG(r)], (UChar*)&vexRegs[r] ); |
| 1253 | vexTags[r] = 1; |
| 1254 | } |
| 1255 | } |
| 1256 | |
| 1257 | /* stack pointer */ |
sewardj | f6dc3ce | 2004-10-19 01:03:46 +0000 | [diff] [blame] | 1258 | vex_state->guest_FTOP = ftop; |
sewardj | 0c2cb62 | 2004-09-06 23:21:21 +0000 | [diff] [blame] | 1259 | |
sewardj | 6e0dbda | 2004-09-08 19:06:34 +0000 | [diff] [blame] | 1260 | /* control word */ |
sewardj | f6dc3ce | 2004-10-19 01:03:46 +0000 | [diff] [blame] | 1261 | vex_state->guest_FPUCW = fpucw; |
sewardj | 3f868e5 | 2004-09-09 00:19:22 +0000 | [diff] [blame] | 1262 | |
| 1263 | /* status word */ |
sewardj | f6dc3ce | 2004-10-19 01:03:46 +0000 | [diff] [blame] | 1264 | vex_state->guest_FC3210 = c3210; |
sewardj | 0c2cb62 | 2004-09-06 23:21:21 +0000 | [diff] [blame] | 1265 | } |
| 1266 | |
sewardj | 6e0dbda | 2004-09-08 19:06:34 +0000 | [diff] [blame] | 1267 | |
sewardj | 0c2cb62 | 2004-09-06 23:21:21 +0000 | [diff] [blame] | 1268 | /* VISIBLE TO LIBVEX CLIENT */ |
sewardj | 76bdc80 | 2004-10-25 15:33:26 +0000 | [diff] [blame] | 1269 | void LibVEX_GuestX86_get_x87 ( /*IN*/VexGuestX86State* vex_state, |
| 1270 | /*OUT*/UChar* x87_state ) |
sewardj | 0c2cb62 | 2004-09-06 23:21:21 +0000 | [diff] [blame] | 1271 | { |
| 1272 | Int i, r; |
| 1273 | UInt tagw; |
sewardj | f6dc3ce | 2004-10-19 01:03:46 +0000 | [diff] [blame] | 1274 | Double* vexRegs = (Double*)(&vex_state->guest_FPREG[0]); |
| 1275 | UChar* vexTags = (UChar*)(&vex_state->guest_FPTAG[0]); |
sewardj | 0c2cb62 | 2004-09-06 23:21:21 +0000 | [diff] [blame] | 1276 | Fpu_State* x87 = (Fpu_State*)x87_state; |
sewardj | f6dc3ce | 2004-10-19 01:03:46 +0000 | [diff] [blame] | 1277 | UInt ftop = vex_state->guest_FTOP; |
| 1278 | UInt c3210 = vex_state->guest_FC3210; |
sewardj | 0c2cb62 | 2004-09-06 23:21:21 +0000 | [diff] [blame] | 1279 | |
| 1280 | for (i = 0; i < 14; i++) |
| 1281 | x87->env[i] = 0; |
| 1282 | |
| 1283 | x87->env[1] = x87->env[3] = x87->env[5] = x87->env[13] = 0xFFFF; |
sewardj | f6dc3ce | 2004-10-19 01:03:46 +0000 | [diff] [blame] | 1284 | x87->env[FP_ENV_CTRL] = (UShort)( vex_state->guest_FPUCW ); |
sewardj | c4be80c | 2004-09-10 16:17:45 +0000 | [diff] [blame] | 1285 | x87->env[FP_ENV_STAT] = ((ftop & 7) << 11) | (c3210 & 0x4700); |
sewardj | 0c2cb62 | 2004-09-06 23:21:21 +0000 | [diff] [blame] | 1286 | |
| 1287 | tagw = 0; |
| 1288 | for (r = 0; r < 8; r++) { |
| 1289 | if (vexTags[r] == 0) { |
| 1290 | /* register is empty */ |
| 1291 | tagw |= (3 << (2*r)); |
| 1292 | convert_f64le_to_f80le( (UChar*)&vexRegs[r], &x87->reg[FP_REG(r)] ); |
| 1293 | } else { |
| 1294 | /* register is full. */ |
| 1295 | tagw |= (0 << (2*r)); |
| 1296 | convert_f64le_to_f80le( (UChar*)&vexRegs[r], &x87->reg[FP_REG(r)] ); |
| 1297 | } |
| 1298 | } |
| 1299 | x87->env[FP_ENV_TAG] = tagw; |
| 1300 | } |
| 1301 | |
| 1302 | |
sewardj | f6dc3ce | 2004-10-19 01:03:46 +0000 | [diff] [blame] | 1303 | /* VISIBLE TO LIBVEX CLIENT */ |
sewardj | 76bdc80 | 2004-10-25 15:33:26 +0000 | [diff] [blame] | 1304 | void LibVEX_GuestX86_put_eflags ( UInt eflags_native, |
| 1305 | /*OUT*/VexGuestX86State* vex_state ) |
sewardj | f6dc3ce | 2004-10-19 01:03:46 +0000 | [diff] [blame] | 1306 | { |
| 1307 | vex_state->guest_DFLAG |
| 1308 | = (eflags_native & (1<<10)) ? 0xFFFFFFFF : 0x00000001; |
sewardj | 006a6a2 | 2004-10-26 00:50:52 +0000 | [diff] [blame] | 1309 | vex_state->guest_IDFLAG |
| 1310 | = (eflags_native & (1<<21)) ? 1 : 0; |
sewardj | f6dc3ce | 2004-10-19 01:03:46 +0000 | [diff] [blame] | 1311 | |
| 1312 | /* Mask out everything except O S Z A C P. */ |
| 1313 | eflags_native |
| 1314 | &= (CC_MASK_C | CC_MASK_P | CC_MASK_A |
| 1315 | | CC_MASK_Z | CC_MASK_S | CC_MASK_O); |
| 1316 | |
sewardj | 2a2ba8b | 2004-11-08 13:14:06 +0000 | [diff] [blame] | 1317 | vex_state->guest_CC_OP = CC_OP_COPY; |
| 1318 | vex_state->guest_CC_DEP1 = eflags_native; |
| 1319 | vex_state->guest_CC_DEP2 = 0; |
| 1320 | vex_state->guest_CC_NDEP = 0; /* unnecessary paranoia */ |
sewardj | f6dc3ce | 2004-10-19 01:03:46 +0000 | [diff] [blame] | 1321 | } |
| 1322 | |
| 1323 | |
| 1324 | /* VISIBLE TO LIBVEX CLIENT */ |
sewardj | 76bdc80 | 2004-10-25 15:33:26 +0000 | [diff] [blame] | 1325 | UInt LibVEX_GuestX86_get_eflags ( /*IN*/VexGuestX86State* vex_state ) |
sewardj | f6dc3ce | 2004-10-19 01:03:46 +0000 | [diff] [blame] | 1326 | { |
| 1327 | UInt eflags = calculate_eflags_all( |
| 1328 | vex_state->guest_CC_OP, |
sewardj | 2a2ba8b | 2004-11-08 13:14:06 +0000 | [diff] [blame] | 1329 | vex_state->guest_CC_DEP1, |
| 1330 | vex_state->guest_CC_DEP2, |
| 1331 | vex_state->guest_CC_NDEP |
sewardj | f6dc3ce | 2004-10-19 01:03:46 +0000 | [diff] [blame] | 1332 | ); |
| 1333 | UInt dflag = vex_state->guest_DFLAG; |
| 1334 | vassert(dflag == 1 || dflag == 0xFFFFFFFF); |
| 1335 | if (dflag == 0xFFFFFFFF) |
| 1336 | eflags |= (1<<10); |
sewardj | 006a6a2 | 2004-10-26 00:50:52 +0000 | [diff] [blame] | 1337 | if (vex_state->guest_IDFLAG == 1) |
| 1338 | eflags |= (1<<21); |
sewardj | f6dc3ce | 2004-10-19 01:03:46 +0000 | [diff] [blame] | 1339 | |
| 1340 | return eflags; |
| 1341 | } |
| 1342 | |
sewardj | dda7a4c | 2004-10-19 23:42:00 +0000 | [diff] [blame] | 1343 | /* VISIBLE TO LIBVEX CLIENT */ |
sewardj | 76bdc80 | 2004-10-25 15:33:26 +0000 | [diff] [blame] | 1344 | void LibVEX_GuestX86_initialise ( /*OUT*/VexGuestX86State* vex_state ) |
sewardj | dda7a4c | 2004-10-19 23:42:00 +0000 | [diff] [blame] | 1345 | { |
| 1346 | Int i; |
sewardj | 76bdc80 | 2004-10-25 15:33:26 +0000 | [diff] [blame] | 1347 | |
| 1348 | vex_state->guest_EAX = 0; |
| 1349 | vex_state->guest_ECX = 0; |
| 1350 | vex_state->guest_EDX = 0; |
| 1351 | vex_state->guest_EBX = 0; |
| 1352 | vex_state->guest_ESP = 0; |
| 1353 | vex_state->guest_EBP = 0; |
| 1354 | vex_state->guest_ESI = 0; |
| 1355 | vex_state->guest_EDI = 0; |
| 1356 | |
sewardj | 2a2ba8b | 2004-11-08 13:14:06 +0000 | [diff] [blame] | 1357 | vex_state->guest_CC_OP = CC_OP_COPY; |
| 1358 | vex_state->guest_CC_DEP1 = 0; |
| 1359 | vex_state->guest_CC_DEP2 = 0; |
| 1360 | vex_state->guest_CC_NDEP = 0; |
| 1361 | vex_state->guest_DFLAG = 1; /* forwards */ |
| 1362 | vex_state->guest_IDFLAG = 0; |
sewardj | 76bdc80 | 2004-10-25 15:33:26 +0000 | [diff] [blame] | 1363 | |
| 1364 | vex_state->guest_EIP = 0; |
| 1365 | |
| 1366 | vex_state->guest_FTOP = 0; |
sewardj | dda7a4c | 2004-10-19 23:42:00 +0000 | [diff] [blame] | 1367 | for (i = 0; i < 8; i++) { |
| 1368 | vex_state->guest_FPTAG[i] = 0; /* empty */ |
| 1369 | vex_state->guest_FPREG[i] = 0; /* IEEE754 64-bit zero */ |
| 1370 | } |
sewardj | dda7a4c | 2004-10-19 23:42:00 +0000 | [diff] [blame] | 1371 | /* The default setting: all fp exceptions masked, rounding to |
| 1372 | nearest, precision to 64 bits */ |
| 1373 | vex_state->guest_FPUCW = 0x03F7; |
sewardj | 76bdc80 | 2004-10-25 15:33:26 +0000 | [diff] [blame] | 1374 | vex_state->guest_FC3210 = 0; |
| 1375 | |
| 1376 | vex_state->guest_CS = 0; |
| 1377 | vex_state->guest_DS = 0; |
| 1378 | vex_state->guest_ES = 0; |
| 1379 | vex_state->guest_FS = 0; |
| 1380 | vex_state->guest_GS = 0; |
| 1381 | vex_state->guest_SS = 0; |
sewardj | dda7a4c | 2004-10-19 23:42:00 +0000 | [diff] [blame] | 1382 | } |
sewardj | f6dc3ce | 2004-10-19 01:03:46 +0000 | [diff] [blame] | 1383 | |
| 1384 | |
sewardj | 9aebb0c | 2004-10-24 19:20:43 +0000 | [diff] [blame] | 1385 | /*----------------------------------------------*/ |
| 1386 | /*--- Misc integer helpers ---*/ |
| 1387 | /*----------------------------------------------*/ |
| 1388 | |
| 1389 | /* CALLED FROM GENERATED CODE: CLEAN HELPER */ |
| 1390 | /* Calculate both flags and value result for rotate right |
| 1391 | through the carry bit. Result in low 32 bits, |
| 1392 | new flags (OSZACP) in high 32 bits. |
| 1393 | */ |
sewardj | 8ea867b | 2004-10-30 19:03:02 +0000 | [diff] [blame] | 1394 | ULong calculate_RCR ( UInt arg, UInt rot_amt, UInt eflags_in, UInt sz ) |
sewardj | 9aebb0c | 2004-10-24 19:20:43 +0000 | [diff] [blame] | 1395 | { |
| 1396 | UInt tempCOUNT = rot_amt & 0x1F, cf=0, of=0, tempcf; |
| 1397 | |
| 1398 | switch (sz) { |
| 1399 | case 4: |
| 1400 | cf = (eflags_in >> CC_SHIFT_C) & 1; |
| 1401 | of = ((arg >> 31) ^ cf) & 1; |
| 1402 | while (tempCOUNT > 0) { |
| 1403 | tempcf = arg & 1; |
| 1404 | arg = (arg >> 1) | (cf << 31); |
| 1405 | cf = tempcf; |
| 1406 | tempCOUNT--; |
| 1407 | } |
| 1408 | break; |
| 1409 | case 2: |
| 1410 | while (tempCOUNT >= 17) tempCOUNT -= 17; |
| 1411 | cf = (eflags_in >> CC_SHIFT_C) & 1; |
| 1412 | of = ((arg >> 15) ^ cf) & 1; |
| 1413 | while (tempCOUNT > 0) { |
| 1414 | tempcf = arg & 1; |
| 1415 | arg = ((arg >> 1) & 0x7FFF) | (cf << 15); |
| 1416 | cf = tempcf; |
| 1417 | tempCOUNT--; |
| 1418 | } |
| 1419 | break; |
| 1420 | case 1: |
| 1421 | while (tempCOUNT >= 9) tempCOUNT -= 9; |
| 1422 | cf = (eflags_in >> CC_SHIFT_C) & 1; |
| 1423 | of = ((arg >> 7) ^ cf) & 1; |
| 1424 | while (tempCOUNT > 0) { |
| 1425 | tempcf = arg & 1; |
| 1426 | arg = ((arg >> 1) & 0x7F) | (cf << 7); |
| 1427 | cf = tempcf; |
| 1428 | tempCOUNT--; |
| 1429 | } |
| 1430 | break; |
| 1431 | default: |
| 1432 | vpanic("calculate_RCR: invalid size"); |
| 1433 | } |
| 1434 | |
| 1435 | cf &= 1; |
| 1436 | of &= 1; |
| 1437 | eflags_in &= ~(CC_MASK_C | CC_MASK_O); |
| 1438 | eflags_in |= (cf << CC_SHIFT_C) | (of << CC_SHIFT_O); |
| 1439 | |
| 1440 | return (((ULong)eflags_in) << 32) | ((ULong)arg); |
| 1441 | } |
| 1442 | |
sewardj | 7cb49d7 | 2004-10-24 22:31:25 +0000 | [diff] [blame] | 1443 | |
| 1444 | /* CALLED FROM GENERATED CODE */ |
| 1445 | /* DIRTY HELPER (modifies guest state) */ |
| 1446 | /* Claim to be a P54C P133 (pre-MMX Pentium) */ |
sewardj | 8ea867b | 2004-10-30 19:03:02 +0000 | [diff] [blame] | 1447 | void dirtyhelper_CPUID ( VexGuestX86State* st ) |
sewardj | 7cb49d7 | 2004-10-24 22:31:25 +0000 | [diff] [blame] | 1448 | { |
| 1449 | if (st->guest_EAX == 0) { |
| 1450 | st->guest_EAX = 0x1; |
| 1451 | st->guest_EBX = 0x756e6547; |
| 1452 | st->guest_ECX = 0x6c65746e; |
| 1453 | st->guest_EDX = 0x49656e69; |
| 1454 | } else { |
| 1455 | st->guest_EAX = 0x52b; |
| 1456 | st->guest_EBX = 0x0; |
| 1457 | st->guest_ECX = 0x0; |
| 1458 | st->guest_EDX = 0x1bf; |
| 1459 | } |
| 1460 | } |
| 1461 | |
sewardj | 8d2291c | 2004-10-25 14:50:21 +0000 | [diff] [blame] | 1462 | /*-----------------------------------------------------------*/ |
| 1463 | /*--- Describing the x86 guest state, for the benefit ---*/ |
| 1464 | /*--- of iropt and instrumenters. ---*/ |
| 1465 | /*-----------------------------------------------------------*/ |
| 1466 | |
| 1467 | /* Figure out if any part of the guest state contained in minoff |
| 1468 | .. maxoff requires precise memory exceptions. If in doubt return |
| 1469 | True (but this is generates significantly slower code). |
| 1470 | |
| 1471 | We enforce precise exns for guest %ESP and %EIP only. |
| 1472 | */ |
| 1473 | Bool guest_x86_state_requires_precise_mem_exns ( Int minoff, |
| 1474 | Int maxoff) |
| 1475 | { |
| 1476 | Int esp_min = offsetof(VexGuestX86State, guest_ESP); |
| 1477 | Int esp_max = esp_min + 4 - 1; |
| 1478 | Int eip_min = offsetof(VexGuestX86State, guest_EIP); |
| 1479 | Int eip_max = eip_min + 4 - 1; |
| 1480 | |
| 1481 | if (maxoff < esp_min || minoff > esp_max) { |
sewardj | eeac841 | 2004-11-02 00:26:55 +0000 | [diff] [blame] | 1482 | /* no overlap with esp */ |
sewardj | 8d2291c | 2004-10-25 14:50:21 +0000 | [diff] [blame] | 1483 | } else { |
sewardj | eeac841 | 2004-11-02 00:26:55 +0000 | [diff] [blame] | 1484 | return True; |
sewardj | 8d2291c | 2004-10-25 14:50:21 +0000 | [diff] [blame] | 1485 | } |
| 1486 | |
| 1487 | if (maxoff < eip_min || minoff > eip_max) { |
sewardj | eeac841 | 2004-11-02 00:26:55 +0000 | [diff] [blame] | 1488 | /* no overlap with eip */ |
sewardj | 8d2291c | 2004-10-25 14:50:21 +0000 | [diff] [blame] | 1489 | } else { |
sewardj | eeac841 | 2004-11-02 00:26:55 +0000 | [diff] [blame] | 1490 | return True; |
sewardj | 8d2291c | 2004-10-25 14:50:21 +0000 | [diff] [blame] | 1491 | } |
| 1492 | |
| 1493 | return False; |
| 1494 | } |
| 1495 | |
| 1496 | |
sewardj | eeac841 | 2004-11-02 00:26:55 +0000 | [diff] [blame] | 1497 | #define ALWAYSDEFD(field) \ |
| 1498 | { offsetof(VexGuestX86State, field), \ |
| 1499 | (sizeof ((VexGuestX86State*)0)->field) } |
| 1500 | |
| 1501 | VexGuestLayout |
sewardj | 49651f4 | 2004-10-28 22:11:04 +0000 | [diff] [blame] | 1502 | x86guest_layout |
sewardj | eeac841 | 2004-11-02 00:26:55 +0000 | [diff] [blame] | 1503 | = { |
| 1504 | /* Total size of the guest state, in bytes. */ |
| 1505 | .total_sizeB = sizeof(VexGuestX86State), |
| 1506 | |
| 1507 | /* Describe the stack pointer. */ |
| 1508 | .offset_SP = offsetof(VexGuestX86State,guest_ESP), |
| 1509 | .sizeof_SP = 4, |
| 1510 | |
sewardj | cf78790 | 2004-11-03 09:08:33 +0000 | [diff] [blame] | 1511 | /* Describe the instruction pointer. */ |
| 1512 | .offset_IP = offsetof(VexGuestX86State,guest_EIP), |
| 1513 | .sizeof_IP = 4, |
sewardj | eeac841 | 2004-11-02 00:26:55 +0000 | [diff] [blame] | 1514 | |
| 1515 | /* Describe any sections to be regarded by Memcheck as |
| 1516 | 'always-defined'. */ |
sewardj | 948d48b | 2004-11-05 19:49:09 +0000 | [diff] [blame] | 1517 | .n_alwaysDefd = 15, |
sewardj | 2a2ba8b | 2004-11-08 13:14:06 +0000 | [diff] [blame] | 1518 | /* flags thunk: OP and NDEP are always defd, whereas DEP1 |
| 1519 | and DEP2 have to be tracked. See detailed comment in |
| 1520 | gdefs.h on meaning of thunk fields. */ |
sewardj | eeac841 | 2004-11-02 00:26:55 +0000 | [diff] [blame] | 1521 | .alwaysDefd[0] = ALWAYSDEFD(guest_CC_OP), |
sewardj | 2a2ba8b | 2004-11-08 13:14:06 +0000 | [diff] [blame] | 1522 | .alwaysDefd[1] = ALWAYSDEFD(guest_CC_NDEP), |
sewardj | b128ebe | 2004-11-06 12:21:23 +0000 | [diff] [blame] | 1523 | |
| 1524 | .alwaysDefd[2] = ALWAYSDEFD(guest_DFLAG), |
| 1525 | .alwaysDefd[3] = ALWAYSDEFD(guest_IDFLAG), |
| 1526 | .alwaysDefd[4] = ALWAYSDEFD(guest_EIP), |
| 1527 | .alwaysDefd[5] = ALWAYSDEFD(guest_FTOP), |
| 1528 | .alwaysDefd[6] = ALWAYSDEFD(guest_FPTAG), |
| 1529 | .alwaysDefd[7] = ALWAYSDEFD(guest_FPUCW), |
| 1530 | .alwaysDefd[8] = ALWAYSDEFD(guest_FC3210), |
| 1531 | .alwaysDefd[9] = ALWAYSDEFD(guest_CS), |
| 1532 | .alwaysDefd[10] = ALWAYSDEFD(guest_DS), |
| 1533 | .alwaysDefd[11] = ALWAYSDEFD(guest_ES), |
| 1534 | .alwaysDefd[12] = ALWAYSDEFD(guest_FS), |
| 1535 | .alwaysDefd[13] = ALWAYSDEFD(guest_GS), |
| 1536 | .alwaysDefd[14] = ALWAYSDEFD(guest_SS) |
sewardj | eeac841 | 2004-11-02 00:26:55 +0000 | [diff] [blame] | 1537 | }; |
sewardj | 49651f4 | 2004-10-28 22:11:04 +0000 | [diff] [blame] | 1538 | |
| 1539 | |
sewardj | 36ca513 | 2004-07-24 13:12:23 +0000 | [diff] [blame] | 1540 | /*---------------------------------------------------------------*/ |
sewardj | c0ee2ed | 2004-07-27 10:29:41 +0000 | [diff] [blame] | 1541 | /*--- end guest-x86/ghelpers.c ---*/ |
sewardj | 36ca513 | 2004-07-24 13:12:23 +0000 | [diff] [blame] | 1542 | /*---------------------------------------------------------------*/ |