sewardj | c2c8716 | 2004-11-25 13:07:02 +0000 | [diff] [blame] | 1 | |
| 2 | /*--------------------------------------------------------------------*/ |
| 3 | /*--- ---*/ |
| 4 | /*--- This file (guest-arm/toIR.c) is ---*/ |
| 5 | /*--- Copyright (c) 2004 OpenWorks LLP. All rights reserved. ---*/ |
| 6 | /*--- ---*/ |
| 7 | /*--------------------------------------------------------------------*/ |
| 8 | |
| 9 | /* |
| 10 | This file is part of LibVEX, a library for dynamic binary |
| 11 | instrumentation and translation. |
| 12 | |
| 13 | Copyright (C) 2004 OpenWorks, LLP. |
| 14 | |
| 15 | This program is free software; you can redistribute it and/or modify |
| 16 | it under the terms of the GNU General Public License as published by |
| 17 | the Free Software Foundation; Version 2 dated June 1991 of the |
| 18 | license. |
| 19 | |
| 20 | This program is distributed in the hope that it will be useful, |
| 21 | but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 22 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, or liability |
| 23 | for damages. See the GNU General Public License for more details. |
| 24 | |
| 25 | Neither the names of the U.S. Department of Energy nor the |
| 26 | University of California nor the names of its contributors may be |
| 27 | used to endorse or promote products derived from this software |
| 28 | without prior written permission. |
| 29 | |
| 30 | You should have received a copy of the GNU General Public License |
| 31 | along with this program; if not, write to the Free Software |
| 32 | Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 |
| 33 | USA. |
| 34 | */ |
| 35 | |
| 36 | /* Translates ARM(v4) code to IR. */ |
| 37 | |
| 38 | #include "libvex_basictypes.h" |
| 39 | #include "libvex_ir.h" |
| 40 | #include "libvex.h" |
| 41 | #include "libvex_guest_arm.h" |
| 42 | |
| 43 | #include "main/vex_util.h" |
| 44 | #include "main/vex_globals.h" |
| 45 | #include "guest-arm/gdefs.h" |
| 46 | |
| 47 | |
| 48 | /*------------------------------------------------------------*/ |
| 49 | /*--- Globals ---*/ |
| 50 | /*------------------------------------------------------------*/ |
| 51 | |
| 52 | /* These are set at the start of the translation of a BB, so that we |
| 53 | don't have to pass them around endlessly. CONST means does not |
| 54 | change during translation of a bb. |
| 55 | */ |
| 56 | |
| 57 | /* We need to know this to do sub-register accesses correctly. */ |
| 58 | /* CONST */ |
| 59 | static Bool host_is_bigendian; |
| 60 | |
| 61 | /* Pointer to the guest code area. */ |
| 62 | /* CONST */ |
| 63 | static UChar* guest_code; |
| 64 | |
| 65 | /* The guest address corresponding to guest_code[0]. */ |
| 66 | /* CONST */ |
| 67 | static Addr32 guest_pc_bbstart; |
| 68 | |
| 69 | /* The IRBB* into which we're generating code. */ |
| 70 | static IRBB* irbb; |
| 71 | |
| 72 | |
| 73 | /*------------------------------------------------------------*/ |
| 74 | /*--- Debugging output ---*/ |
| 75 | /*------------------------------------------------------------*/ |
| 76 | |
| 77 | #define DIP(format, args...) \ |
| 78 | if (vex_traceflags & VEX_TRACE_FE) \ |
| 79 | vex_printf(format, ## args) |
| 80 | |
| 81 | #define DIS(buf, format, args...) \ |
| 82 | if (vex_traceflags & VEX_TRACE_FE) \ |
| 83 | vex_sprintf(buf, format, ## args) |
| 84 | |
| 85 | |
cerion | a70a37b | 2004-12-03 18:54:08 +0000 | [diff] [blame] | 86 | |
| 87 | |
sewardj | c2c8716 | 2004-11-25 13:07:02 +0000 | [diff] [blame] | 88 | /*------------------------------------------------------------*/ |
| 89 | /*--- Offsets of various parts of the arm guest state. ---*/ |
| 90 | /*------------------------------------------------------------*/ |
| 91 | |
| 92 | #define OFFB_R0 offsetof(VexGuestARMState,guest_R0) |
cerion | c60c01e | 2004-12-02 20:19:22 +0000 | [diff] [blame] | 93 | #define OFFB_R1 offsetof(VexGuestARMState,guest_R1) |
| 94 | #define OFFB_R2 offsetof(VexGuestARMState,guest_R2) |
| 95 | #define OFFB_R3 offsetof(VexGuestARMState,guest_R3) |
| 96 | #define OFFB_R4 offsetof(VexGuestARMState,guest_R4) |
| 97 | #define OFFB_R5 offsetof(VexGuestARMState,guest_R5) |
| 98 | #define OFFB_R6 offsetof(VexGuestARMState,guest_R6) |
| 99 | #define OFFB_R7 offsetof(VexGuestARMState,guest_R7) |
| 100 | #define OFFB_R8 offsetof(VexGuestARMState,guest_R8) |
| 101 | #define OFFB_R9 offsetof(VexGuestARMState,guest_R9) |
| 102 | #define OFFB_R10 offsetof(VexGuestARMState,guest_R10) |
| 103 | #define OFFB_R11 offsetof(VexGuestARMState,guest_R11) |
| 104 | #define OFFB_R12 offsetof(VexGuestARMState,guest_R12) |
| 105 | #define OFFB_R13 offsetof(VexGuestARMState,guest_R13) |
| 106 | #define OFFB_R14 offsetof(VexGuestARMState,guest_R14) |
sewardj | c2c8716 | 2004-11-25 13:07:02 +0000 | [diff] [blame] | 107 | #define OFFB_R15 offsetof(VexGuestARMState,guest_R15) |
| 108 | |
cerion | c60c01e | 2004-12-02 20:19:22 +0000 | [diff] [blame] | 109 | // CAB: ? guest_SYSCALLNO; |
| 110 | |
| 111 | #define OFFB_CC_OP offsetof(VexGuestARMState,guest_CC_OP) |
| 112 | #define OFFB_CC_DEP1 offsetof(VexGuestARMState,guest_CC_DEP1) |
| 113 | #define OFFB_CC_DEP2 offsetof(VexGuestARMState,guest_CC_DEP2) |
| 114 | |
| 115 | // CAB: ? guest_EMWARN; |
| 116 | |
sewardj | c2c8716 | 2004-11-25 13:07:02 +0000 | [diff] [blame] | 117 | |
| 118 | /*------------------------------------------------------------*/ |
| 119 | /*--- Disassemble an entire basic block ---*/ |
| 120 | /*------------------------------------------------------------*/ |
| 121 | |
| 122 | /* The results of disassembling an instruction. There are three |
| 123 | possible outcomes. For Dis_Resteer, the disassembler _must_ |
| 124 | continue at the specified address. For Dis_StopHere, the |
| 125 | disassembler _must_ terminate the BB. For Dis_Continue, we may at |
| 126 | our option either disassemble the next insn, or terminate the BB; |
| 127 | but in the latter case we must set the bb's ->next field to point |
| 128 | to the next instruction. */ |
| 129 | |
| 130 | typedef |
| 131 | enum { |
| 132 | Dis_StopHere, /* this insn terminates the BB; we must stop. */ |
| 133 | Dis_Continue, /* we can optionally continue into the next insn */ |
| 134 | Dis_Resteer /* followed a branch; continue at the spec'd addr */ |
| 135 | } |
| 136 | DisResult; |
| 137 | |
| 138 | |
| 139 | /* forward decls .. */ |
| 140 | static IRExpr* mkU32 ( UInt i ); |
| 141 | static void stmt ( IRStmt* st ); |
| 142 | |
| 143 | |
| 144 | /* disInstr disassembles an instruction located at &guest_code[delta], |
| 145 | and sets *size to its size. If the returned value is Dis_Resteer, |
| 146 | the next guest address is assigned to *whereNext. disInstr is not |
| 147 | permitted to return Dis_Resteer if either (1) resteerOK is False, |
| 148 | or (2) resteerOkFn, when applied to the address which it wishes to |
| 149 | resteer into, returns False. */ |
| 150 | |
| 151 | static DisResult disInstr ( /*IN*/ Bool resteerOK, |
| 152 | /*IN*/ Bool (*resteerOkFn) ( Addr64 ), |
| 153 | /*IN*/ UInt delta, |
| 154 | /*OUT*/ UInt* size, |
| 155 | /*OUT*/ Addr64* whereNext ); |
| 156 | |
| 157 | |
| 158 | /* This is the main (only, in fact) entry point for this module. */ |
| 159 | |
| 160 | /* Disassemble a complete basic block, starting at guest_pc_start, and |
| 161 | dumping the IR into global irbb. Returns the size, in bytes, of |
| 162 | the basic block. |
| 163 | */ |
| 164 | IRBB* bbToIR_ARM ( UChar* armCode, |
| 165 | Addr64 guest_pc_start, |
| 166 | Int* guest_bytes_read, |
| 167 | Bool (*byte_accessible)(Addr64), |
| 168 | Bool (*chase_into_ok)(Addr64), |
| 169 | Bool host_bigendian ) |
| 170 | { |
| 171 | UInt delta; |
| 172 | Int i, n_instrs, size, first_stmt_idx; |
| 173 | Addr64 guest_next; |
| 174 | Bool resteerOK; |
| 175 | DisResult dres; |
| 176 | static Int n_resteers = 0; |
| 177 | Int d_resteers = 0; |
| 178 | |
| 179 | /* check sanity .. */ |
| 180 | vassert(vex_control.guest_max_insns >= 1); |
| 181 | vassert(vex_control.guest_max_insns < 1000); |
| 182 | vassert(vex_control.guest_chase_thresh >= 0); |
| 183 | vassert(vex_control.guest_chase_thresh < vex_control.guest_max_insns); |
| 184 | |
| 185 | /* Set up globals. */ |
| 186 | host_is_bigendian = host_bigendian; |
| 187 | guest_code = armCode; |
| 188 | guest_pc_bbstart = (Addr32)guest_pc_start; |
| 189 | irbb = emptyIRBB(); |
| 190 | |
| 191 | vassert((guest_pc_start >> 32) == 0); |
| 192 | |
| 193 | /* Delta keeps track of how far along the x86code array we |
| 194 | have so far gone. */ |
| 195 | delta = 0; |
| 196 | n_instrs = 0; |
| 197 | *guest_bytes_read = 0; |
| 198 | |
| 199 | while (True) { |
| 200 | vassert(n_instrs < vex_control.guest_max_insns); |
| 201 | |
| 202 | guest_next = 0; |
| 203 | resteerOK = n_instrs < vex_control.guest_chase_thresh; |
| 204 | first_stmt_idx = irbb->stmts_used; |
| 205 | |
| 206 | if (n_instrs > 0) { |
| 207 | /* for the first insn, the dispatch loop will have set |
| 208 | R15, but for all the others we have to do it ourselves. */ |
| 209 | stmt( IRStmt_Put( OFFB_R15, mkU32(guest_pc_bbstart + delta)) ); |
| 210 | } |
| 211 | |
| 212 | dres = disInstr( resteerOK, chase_into_ok, |
| 213 | delta, &size, &guest_next ); |
| 214 | |
| 215 | /* Print the resulting IR, if needed. */ |
| 216 | if (vex_traceflags & VEX_TRACE_FE) { |
| 217 | for (i = first_stmt_idx; i < irbb->stmts_used; i++) { |
| 218 | vex_printf(" "); |
| 219 | ppIRStmt(irbb->stmts[i]); |
| 220 | vex_printf("\n"); |
| 221 | } |
| 222 | } |
| 223 | |
| 224 | if (dres == Dis_StopHere) { |
| 225 | vassert(irbb->next != NULL); |
| 226 | if (vex_traceflags & VEX_TRACE_FE) { |
| 227 | vex_printf(" "); |
| 228 | vex_printf( "goto {"); |
| 229 | ppIRJumpKind(irbb->jumpkind); |
| 230 | vex_printf( "} "); |
| 231 | ppIRExpr( irbb->next ); |
| 232 | vex_printf( "\n"); |
| 233 | } |
| 234 | } |
| 235 | |
| 236 | delta += size; |
| 237 | *guest_bytes_read += size; |
| 238 | n_instrs++; |
| 239 | DIP("\n"); |
| 240 | |
| 241 | vassert(size > 0 && size <= 18); |
| 242 | if (!resteerOK) |
| 243 | vassert(dres != Dis_Resteer); |
| 244 | if (dres != Dis_Resteer) |
| 245 | vassert(guest_next == 0); |
| 246 | |
| 247 | switch (dres) { |
| 248 | case Dis_Continue: |
| 249 | vassert(irbb->next == NULL); |
| 250 | if (n_instrs < vex_control.guest_max_insns) { |
| 251 | /* keep going */ |
| 252 | } else { |
| 253 | irbb->next = mkU32(((Addr32)guest_pc_start)+delta); |
| 254 | return irbb; |
| 255 | } |
| 256 | break; |
| 257 | case Dis_StopHere: |
| 258 | vassert(irbb->next != NULL); |
| 259 | return irbb; |
| 260 | case Dis_Resteer: |
| 261 | vassert(irbb->next == NULL); |
| 262 | /* figure out a new delta to continue at. */ |
| 263 | vassert(chase_into_ok(guest_next)); |
| 264 | delta = (UInt)(guest_next - guest_pc_start); |
| 265 | n_resteers++; |
| 266 | d_resteers++; |
| 267 | if (0 && (n_resteers & 0xFF) == 0) |
| 268 | vex_printf("resteer[%d,%d] to %p (delta = %d)\n", |
| 269 | n_resteers, d_resteers, |
| 270 | (void*)(UInt)(guest_next), delta); |
| 271 | break; |
| 272 | } |
| 273 | } |
| 274 | } |
| 275 | |
| 276 | |
| 277 | /*------------------------------------------------------------*/ |
| 278 | /*--- Helper bits and pieces for deconstructing the ---*/ |
| 279 | /*--- x86 insn stream. ---*/ |
| 280 | /*------------------------------------------------------------*/ |
| 281 | |
| 282 | /* Add a statement to the list held by "irbb". */ |
| 283 | static void stmt ( IRStmt* st ) |
| 284 | { |
| 285 | addStmtToIRBB( irbb, st ); |
| 286 | } |
| 287 | |
| 288 | /* Generate a new temporary of the given type. */ |
| 289 | static IRTemp newTemp ( IRType ty ) |
| 290 | { |
| 291 | vassert(isPlausibleType(ty)); |
| 292 | return newIRTemp( irbb->tyenv, ty ); |
| 293 | } |
| 294 | |
sewardj | fa89fdc | 2004-12-15 12:21:28 +0000 | [diff] [blame] | 295 | #if 0 |
sewardj | c2c8716 | 2004-11-25 13:07:02 +0000 | [diff] [blame] | 296 | /* Bomb out if we can't handle something. */ |
| 297 | __attribute__ ((noreturn)) |
| 298 | static void unimplemented ( Char* str ) |
| 299 | { |
| 300 | vex_printf("armToIR: unimplemented feature\n"); |
| 301 | vpanic(str); |
| 302 | } |
sewardj | fa89fdc | 2004-12-15 12:21:28 +0000 | [diff] [blame] | 303 | #endif |
sewardj | c2c8716 | 2004-11-25 13:07:02 +0000 | [diff] [blame] | 304 | |
| 305 | /* Various simple conversions */ |
| 306 | |
sewardj | fa89fdc | 2004-12-15 12:21:28 +0000 | [diff] [blame] | 307 | #if 0 |
sewardj | c2c8716 | 2004-11-25 13:07:02 +0000 | [diff] [blame] | 308 | static UInt extend_s_8to32( UInt x ) |
| 309 | { |
| 310 | return (UInt)((((Int)x) << 24) >> 24); |
| 311 | } |
| 312 | |
| 313 | static UInt extend_s_16to32 ( UInt x ) |
| 314 | { |
| 315 | return (UInt)((((Int)x) << 16) >> 16); |
| 316 | } |
sewardj | fa89fdc | 2004-12-15 12:21:28 +0000 | [diff] [blame] | 317 | #endif |
sewardj | c2c8716 | 2004-11-25 13:07:02 +0000 | [diff] [blame] | 318 | |
cerion | 19e8a61 | 2004-12-10 10:18:58 +0000 | [diff] [blame] | 319 | static UInt extend_s_24to32 ( UInt x ) |
| 320 | { |
| 321 | return (UInt)((((Int)x) << 8) >> 8); |
| 322 | } |
| 323 | |
sewardj | fa89fdc | 2004-12-15 12:21:28 +0000 | [diff] [blame] | 324 | #if 0 |
sewardj | c2c8716 | 2004-11-25 13:07:02 +0000 | [diff] [blame] | 325 | /* Fetch a byte from the guest insn stream. */ |
| 326 | static UChar getIByte ( UInt delta ) |
| 327 | { |
| 328 | return guest_code[delta]; |
| 329 | } |
sewardj | fa89fdc | 2004-12-15 12:21:28 +0000 | [diff] [blame] | 330 | #endif |
sewardj | c2c8716 | 2004-11-25 13:07:02 +0000 | [diff] [blame] | 331 | |
| 332 | /* Get a 8/16/32-bit unsigned value out of the insn stream. */ |
| 333 | |
sewardj | fa89fdc | 2004-12-15 12:21:28 +0000 | [diff] [blame] | 334 | #if 0 |
sewardj | c2c8716 | 2004-11-25 13:07:02 +0000 | [diff] [blame] | 335 | static UInt getUChar ( UInt delta ) |
| 336 | { |
| 337 | UInt v = guest_code[delta+0]; |
| 338 | return v & 0xFF; |
| 339 | } |
sewardj | fa89fdc | 2004-12-15 12:21:28 +0000 | [diff] [blame] | 340 | #endif |
sewardj | c2c8716 | 2004-11-25 13:07:02 +0000 | [diff] [blame] | 341 | |
sewardj | fa89fdc | 2004-12-15 12:21:28 +0000 | [diff] [blame] | 342 | #if 0 |
sewardj | c2c8716 | 2004-11-25 13:07:02 +0000 | [diff] [blame] | 343 | static UInt getUDisp16 ( UInt delta ) |
| 344 | { |
| 345 | UInt v = guest_code[delta+1]; v <<= 8; |
| 346 | v |= guest_code[delta+0]; |
| 347 | return v & 0xFFFF; |
| 348 | } |
sewardj | fa89fdc | 2004-12-15 12:21:28 +0000 | [diff] [blame] | 349 | #endif |
sewardj | c2c8716 | 2004-11-25 13:07:02 +0000 | [diff] [blame] | 350 | |
sewardj | fa89fdc | 2004-12-15 12:21:28 +0000 | [diff] [blame] | 351 | #if 0 |
sewardj | c2c8716 | 2004-11-25 13:07:02 +0000 | [diff] [blame] | 352 | static UInt getUDisp32 ( UInt delta ) |
| 353 | { |
| 354 | UInt v = guest_code[delta+3]; v <<= 8; |
| 355 | v |= guest_code[delta+2]; v <<= 8; |
| 356 | v |= guest_code[delta+1]; v <<= 8; |
| 357 | v |= guest_code[delta+0]; |
| 358 | return v; |
| 359 | } |
sewardj | fa89fdc | 2004-12-15 12:21:28 +0000 | [diff] [blame] | 360 | #endif |
sewardj | c2c8716 | 2004-11-25 13:07:02 +0000 | [diff] [blame] | 361 | |
sewardj | fa89fdc | 2004-12-15 12:21:28 +0000 | [diff] [blame] | 362 | #if 0 |
sewardj | c2c8716 | 2004-11-25 13:07:02 +0000 | [diff] [blame] | 363 | static UInt getUDisp ( Int size, UInt delta ) |
| 364 | { |
| 365 | switch (size) { |
| 366 | case 4: return getUDisp32(delta); |
| 367 | case 2: return getUDisp16(delta); |
| 368 | case 1: return getUChar(delta); |
cerion | c60c01e | 2004-12-02 20:19:22 +0000 | [diff] [blame] | 369 | default: vpanic("getUDisp(ARM)"); |
sewardj | c2c8716 | 2004-11-25 13:07:02 +0000 | [diff] [blame] | 370 | } |
| 371 | return 0; /*notreached*/ |
| 372 | } |
sewardj | fa89fdc | 2004-12-15 12:21:28 +0000 | [diff] [blame] | 373 | #endif |
sewardj | c2c8716 | 2004-11-25 13:07:02 +0000 | [diff] [blame] | 374 | |
sewardj | fa89fdc | 2004-12-15 12:21:28 +0000 | [diff] [blame] | 375 | #if 0 |
sewardj | c2c8716 | 2004-11-25 13:07:02 +0000 | [diff] [blame] | 376 | /* Get a byte value out of the insn stream and sign-extend to 32 |
| 377 | bits. */ |
| 378 | static UInt getSDisp8 ( UInt delta ) |
| 379 | { |
| 380 | return extend_s_8to32( (UInt) (guest_code[delta]) ); |
| 381 | } |
sewardj | fa89fdc | 2004-12-15 12:21:28 +0000 | [diff] [blame] | 382 | #endif |
sewardj | c2c8716 | 2004-11-25 13:07:02 +0000 | [diff] [blame] | 383 | |
sewardj | fa89fdc | 2004-12-15 12:21:28 +0000 | [diff] [blame] | 384 | #if 0 |
sewardj | c2c8716 | 2004-11-25 13:07:02 +0000 | [diff] [blame] | 385 | static UInt getSDisp16 ( UInt delta0 ) |
| 386 | { |
| 387 | UChar* eip = (UChar*)(&guest_code[delta0]); |
| 388 | UInt d = *eip++; |
| 389 | d |= ((*eip++) << 8); |
| 390 | return extend_s_16to32(d); |
| 391 | } |
sewardj | fa89fdc | 2004-12-15 12:21:28 +0000 | [diff] [blame] | 392 | #endif |
sewardj | c2c8716 | 2004-11-25 13:07:02 +0000 | [diff] [blame] | 393 | |
sewardj | fa89fdc | 2004-12-15 12:21:28 +0000 | [diff] [blame] | 394 | #if 0 |
sewardj | c2c8716 | 2004-11-25 13:07:02 +0000 | [diff] [blame] | 395 | static UInt getSDisp ( Int size, UInt delta ) |
| 396 | { |
| 397 | switch (size) { |
| 398 | case 4: return getUDisp32(delta); |
| 399 | case 2: return getSDisp16(delta); |
| 400 | case 1: return getSDisp8(delta); |
cerion | c60c01e | 2004-12-02 20:19:22 +0000 | [diff] [blame] | 401 | default: vpanic("getSDisp(ARM)"); |
sewardj | c2c8716 | 2004-11-25 13:07:02 +0000 | [diff] [blame] | 402 | } |
| 403 | return 0; /*notreached*/ |
| 404 | } |
sewardj | fa89fdc | 2004-12-15 12:21:28 +0000 | [diff] [blame] | 405 | #endif |
sewardj | c2c8716 | 2004-11-25 13:07:02 +0000 | [diff] [blame] | 406 | |
| 407 | |
| 408 | /*------------------------------------------------------------*/ |
| 409 | /*--- Helpers for constructing IR. ---*/ |
| 410 | /*------------------------------------------------------------*/ |
| 411 | |
| 412 | /* Create a 1/2/4 byte read of an x86 integer registers. For 16/8 bit |
| 413 | register references, we need to take the host endianness into |
| 414 | account. Supplied value is 0 .. 7 and in the Intel instruction |
| 415 | encoding. */ |
| 416 | |
sewardj | fa89fdc | 2004-12-15 12:21:28 +0000 | [diff] [blame] | 417 | #if 0 |
sewardj | c2c8716 | 2004-11-25 13:07:02 +0000 | [diff] [blame] | 418 | static IRType szToITy ( Int n ) |
| 419 | { |
| 420 | switch (n) { |
| 421 | case 1: return Ity_I8; |
| 422 | case 2: return Ity_I16; |
| 423 | case 4: return Ity_I32; |
cerion | c60c01e | 2004-12-02 20:19:22 +0000 | [diff] [blame] | 424 | default: vpanic("szToITy(ARM)"); |
sewardj | c2c8716 | 2004-11-25 13:07:02 +0000 | [diff] [blame] | 425 | } |
| 426 | } |
sewardj | fa89fdc | 2004-12-15 12:21:28 +0000 | [diff] [blame] | 427 | #endif |
sewardj | c2c8716 | 2004-11-25 13:07:02 +0000 | [diff] [blame] | 428 | |
| 429 | static Int integerGuestRegOffset ( UInt archreg ) |
| 430 | { |
| 431 | vassert(archreg < 16); |
| 432 | |
| 433 | vassert(!host_is_bigendian); //TODO: is this necessary? |
sewardj | cca7194 | 2004-12-02 23:35:18 +0000 | [diff] [blame] | 434 | // jrs: probably not; only matters if we reference sub-parts |
| 435 | // of the arm registers, but that isn't the case |
sewardj | c2c8716 | 2004-11-25 13:07:02 +0000 | [diff] [blame] | 436 | switch (archreg) { |
cerion | c60c01e | 2004-12-02 20:19:22 +0000 | [diff] [blame] | 437 | case 0: return offsetof(VexGuestARMState, guest_R0); |
| 438 | case 1: return offsetof(VexGuestARMState, guest_R1); |
| 439 | case 2: return offsetof(VexGuestARMState, guest_R2); |
| 440 | case 3: return offsetof(VexGuestARMState, guest_R3); |
| 441 | case 4: return offsetof(VexGuestARMState, guest_R4); |
| 442 | case 5: return offsetof(VexGuestARMState, guest_R5); |
| 443 | case 6: return offsetof(VexGuestARMState, guest_R6); |
| 444 | case 7: return offsetof(VexGuestARMState, guest_R7); |
| 445 | case 8: return offsetof(VexGuestARMState, guest_R8); |
| 446 | case 9: return offsetof(VexGuestARMState, guest_R9); |
| 447 | case 10: return offsetof(VexGuestARMState,guest_R10); |
| 448 | case 11: return offsetof(VexGuestARMState,guest_R11); |
| 449 | case 12: return offsetof(VexGuestARMState,guest_R12); |
| 450 | case 13: return offsetof(VexGuestARMState,guest_R13); |
| 451 | case 14: return offsetof(VexGuestARMState,guest_R14); |
sewardj | c2c8716 | 2004-11-25 13:07:02 +0000 | [diff] [blame] | 452 | case 15: return offsetof(VexGuestARMState,guest_R15); |
| 453 | } |
| 454 | |
cerion | c60c01e | 2004-12-02 20:19:22 +0000 | [diff] [blame] | 455 | vpanic("integerGuestRegOffset(arm,le)"); /*notreached*/ |
sewardj | c2c8716 | 2004-11-25 13:07:02 +0000 | [diff] [blame] | 456 | } |
| 457 | |
| 458 | static IRExpr* getIReg ( UInt archreg ) |
| 459 | { |
| 460 | vassert(archreg < 16); |
| 461 | return IRExpr_Get( integerGuestRegOffset(archreg), Ity_I32 ); |
| 462 | } |
| 463 | |
| 464 | /* Ditto, but write to a reg instead. */ |
| 465 | static void putIReg ( UInt archreg, IRExpr* e ) |
| 466 | { |
| 467 | vassert(archreg < 16); |
| 468 | stmt( IRStmt_Put(integerGuestRegOffset(archreg), e) ); |
| 469 | } |
| 470 | |
| 471 | static void assign ( IRTemp dst, IRExpr* e ) |
| 472 | { |
| 473 | stmt( IRStmt_Tmp(dst, e) ); |
| 474 | } |
| 475 | |
| 476 | static void storeLE ( IRExpr* addr, IRExpr* data ) |
| 477 | { |
| 478 | stmt( IRStmt_STle(addr,data) ); |
| 479 | } |
| 480 | |
| 481 | static IRExpr* unop ( IROp op, IRExpr* a ) |
| 482 | { |
| 483 | return IRExpr_Unop(op, a); |
| 484 | } |
| 485 | |
| 486 | static IRExpr* binop ( IROp op, IRExpr* a1, IRExpr* a2 ) |
| 487 | { |
| 488 | return IRExpr_Binop(op, a1, a2); |
| 489 | } |
| 490 | |
| 491 | static IRExpr* mkexpr ( IRTemp tmp ) |
| 492 | { |
| 493 | return IRExpr_Tmp(tmp); |
| 494 | } |
| 495 | |
| 496 | static IRExpr* mkU8 ( UInt i ) |
| 497 | { |
| 498 | vassert(i < 256); |
| 499 | return IRExpr_Const(IRConst_U8(i)); |
| 500 | } |
| 501 | |
sewardj | fa89fdc | 2004-12-15 12:21:28 +0000 | [diff] [blame] | 502 | #if 0 |
sewardj | c2c8716 | 2004-11-25 13:07:02 +0000 | [diff] [blame] | 503 | static IRExpr* mkU16 ( UInt i ) |
| 504 | { |
| 505 | vassert(i < 65536); |
| 506 | return IRExpr_Const(IRConst_U16(i)); |
| 507 | } |
sewardj | fa89fdc | 2004-12-15 12:21:28 +0000 | [diff] [blame] | 508 | #endif |
sewardj | c2c8716 | 2004-11-25 13:07:02 +0000 | [diff] [blame] | 509 | |
| 510 | static IRExpr* mkU32 ( UInt i ) |
| 511 | { |
| 512 | return IRExpr_Const(IRConst_U32(i)); |
| 513 | } |
| 514 | |
sewardj | fa89fdc | 2004-12-15 12:21:28 +0000 | [diff] [blame] | 515 | #if 0 |
sewardj | c2c8716 | 2004-11-25 13:07:02 +0000 | [diff] [blame] | 516 | static IRExpr* mkU ( IRType ty, UInt i ) |
| 517 | { |
| 518 | if (ty == Ity_I8) return mkU8(i); |
| 519 | if (ty == Ity_I16) return mkU16(i); |
| 520 | if (ty == Ity_I32) return mkU32(i); |
| 521 | /* If this panics, it usually means you passed a size (1,2,4) |
| 522 | value as the IRType, rather than a real IRType. */ |
cerion | c60c01e | 2004-12-02 20:19:22 +0000 | [diff] [blame] | 523 | vpanic("mkU(ARM)"); |
sewardj | c2c8716 | 2004-11-25 13:07:02 +0000 | [diff] [blame] | 524 | } |
sewardj | fa89fdc | 2004-12-15 12:21:28 +0000 | [diff] [blame] | 525 | #endif |
sewardj | c2c8716 | 2004-11-25 13:07:02 +0000 | [diff] [blame] | 526 | |
| 527 | static IRExpr* loadLE ( IRType ty, IRExpr* data ) |
| 528 | { |
| 529 | return IRExpr_LDle(ty,data); |
| 530 | } |
| 531 | |
sewardj | fa89fdc | 2004-12-15 12:21:28 +0000 | [diff] [blame] | 532 | #if 0 |
sewardj | c2c8716 | 2004-11-25 13:07:02 +0000 | [diff] [blame] | 533 | static IROp mkSizedOp ( IRType ty, IROp op8 ) |
| 534 | { |
| 535 | Int adj; |
| 536 | vassert(ty == Ity_I8 || ty == Ity_I16 || ty == Ity_I32); |
| 537 | vassert(op8 == Iop_Add8 || op8 == Iop_Sub8 |
| 538 | || op8 == Iop_Mul8 |
| 539 | || op8 == Iop_Or8 || op8 == Iop_And8 || op8 == Iop_Xor8 |
| 540 | || op8 == Iop_Shl8 || op8 == Iop_Shr8 || op8 == Iop_Sar8 |
| 541 | || op8 == Iop_CmpEQ8 || op8 == Iop_CmpNE8 |
| 542 | || op8 == Iop_Not8 ); |
| 543 | adj = ty==Ity_I8 ? 0 : (ty==Ity_I16 ? 1 : 2); |
| 544 | return adj + op8; |
| 545 | } |
sewardj | fa89fdc | 2004-12-15 12:21:28 +0000 | [diff] [blame] | 546 | #endif |
sewardj | c2c8716 | 2004-11-25 13:07:02 +0000 | [diff] [blame] | 547 | |
sewardj | fa89fdc | 2004-12-15 12:21:28 +0000 | [diff] [blame] | 548 | #if 0 |
sewardj | c2c8716 | 2004-11-25 13:07:02 +0000 | [diff] [blame] | 549 | static IROp mkWidenOp ( Int szSmall, Int szBig, Bool signd ) |
| 550 | { |
| 551 | if (szSmall == 1 && szBig == 4) { |
| 552 | return signd ? Iop_8Sto32 : Iop_8Uto32; |
| 553 | } |
| 554 | if (szSmall == 1 && szBig == 2) { |
| 555 | return signd ? Iop_8Sto16 : Iop_8Uto16; |
| 556 | } |
| 557 | if (szSmall == 2 && szBig == 4) { |
| 558 | return signd ? Iop_16Sto32 : Iop_16Uto32; |
| 559 | } |
cerion | c60c01e | 2004-12-02 20:19:22 +0000 | [diff] [blame] | 560 | vpanic("mkWidenOp(ARM,guest)"); |
sewardj | c2c8716 | 2004-11-25 13:07:02 +0000 | [diff] [blame] | 561 | } |
sewardj | fa89fdc | 2004-12-15 12:21:28 +0000 | [diff] [blame] | 562 | #endif |
sewardj | c2c8716 | 2004-11-25 13:07:02 +0000 | [diff] [blame] | 563 | |
cerion | c60c01e | 2004-12-02 20:19:22 +0000 | [diff] [blame] | 564 | |
| 565 | |
| 566 | |
| 567 | |
| 568 | |
| 569 | |
| 570 | |
| 571 | |
| 572 | |
| 573 | |
| 574 | |
| 575 | |
| 576 | |
sewardj | c2c8716 | 2004-11-25 13:07:02 +0000 | [diff] [blame] | 577 | /*------------------------------------------------------------*/ |
cerion | f7da63d | 2004-12-09 19:04:57 +0000 | [diff] [blame] | 578 | /*--- Helpers for %flags. ---*/ |
sewardj | c2c8716 | 2004-11-25 13:07:02 +0000 | [diff] [blame] | 579 | /*------------------------------------------------------------*/ |
cerion | c60c01e | 2004-12-02 20:19:22 +0000 | [diff] [blame] | 580 | |
sewardj | c2c8716 | 2004-11-25 13:07:02 +0000 | [diff] [blame] | 581 | /* -------------- Evaluating the flags-thunk. -------------- */ |
| 582 | |
sewardj | fa89fdc | 2004-12-15 12:21:28 +0000 | [diff] [blame] | 583 | #if 0 |
cerion | f7da63d | 2004-12-09 19:04:57 +0000 | [diff] [blame] | 584 | /* Build IR to calculate all the flags from stored |
cerion | c60c01e | 2004-12-02 20:19:22 +0000 | [diff] [blame] | 585 | CC_OP/CC_DEP1/CC_DEP2/CC_NDEP. |
| 586 | Returns an expression :: Ity_I32. */ |
| 587 | static IRExpr* mk_armg_calculate_flags_all ( void ) |
sewardj | c2c8716 | 2004-11-25 13:07:02 +0000 | [diff] [blame] | 588 | { |
| 589 | IRExpr** args |
cerion | c60c01e | 2004-12-02 20:19:22 +0000 | [diff] [blame] | 590 | = mkIRExprVec_3( IRExpr_Get(OFFB_CC_OP, Ity_I32), |
sewardj | c2c8716 | 2004-11-25 13:07:02 +0000 | [diff] [blame] | 591 | IRExpr_Get(OFFB_CC_DEP1, Ity_I32), |
cerion | c60c01e | 2004-12-02 20:19:22 +0000 | [diff] [blame] | 592 | IRExpr_Get(OFFB_CC_DEP2, Ity_I32) ); |
sewardj | c2c8716 | 2004-11-25 13:07:02 +0000 | [diff] [blame] | 593 | IRExpr* call |
| 594 | = mkIRExprCCall( |
| 595 | Ity_I32, |
| 596 | 0/*regparm*/, |
cerion | c60c01e | 2004-12-02 20:19:22 +0000 | [diff] [blame] | 597 | "armg_calculate_flags_all", &armg_calculate_flags_all, |
sewardj | c2c8716 | 2004-11-25 13:07:02 +0000 | [diff] [blame] | 598 | args |
| 599 | ); |
cerion | c60c01e | 2004-12-02 20:19:22 +0000 | [diff] [blame] | 600 | |
| 601 | /* Exclude OP from definedness checking. We're only |
sewardj | c2c8716 | 2004-11-25 13:07:02 +0000 | [diff] [blame] | 602 | interested in DEP1 and DEP2. */ |
cerion | c60c01e | 2004-12-02 20:19:22 +0000 | [diff] [blame] | 603 | call->Iex.CCall.cee->mcx_mask = 1; |
sewardj | c2c8716 | 2004-11-25 13:07:02 +0000 | [diff] [blame] | 604 | return call; |
| 605 | } |
sewardj | fa89fdc | 2004-12-15 12:21:28 +0000 | [diff] [blame] | 606 | #endif |
cerion | c60c01e | 2004-12-02 20:19:22 +0000 | [diff] [blame] | 607 | |
cerion | f7da63d | 2004-12-09 19:04:57 +0000 | [diff] [blame] | 608 | /* Build IR to calculate just the carry flag from stored |
| 609 | CC_OP/CC_DEP1/CC_DEP2/CC_NDEP. Returns an expression :: Ity_I32. */ |
| 610 | static IRExpr* mk_armg_calculate_flags_c ( void ) |
| 611 | { |
| 612 | IRExpr** args |
| 613 | = mkIRExprVec_3( IRExpr_Get(OFFB_CC_OP, Ity_I32), |
| 614 | IRExpr_Get(OFFB_CC_DEP1, Ity_I32), |
| 615 | IRExpr_Get(OFFB_CC_DEP2, Ity_I32) ); |
| 616 | IRExpr* call |
| 617 | = mkIRExprCCall( |
| 618 | Ity_I32, |
| 619 | 0/*regparm*/, |
| 620 | "armg_calculate_flags_c", &armg_calculate_flags_c, |
| 621 | args |
| 622 | ); |
| 623 | /* Exclude OP from definedness checking. We're only |
| 624 | interested in DEP1 and DEP2. */ |
| 625 | call->Iex.CCall.cee->mcx_mask = 1; |
| 626 | return call; |
| 627 | } |
| 628 | |
cerion | c60c01e | 2004-12-02 20:19:22 +0000 | [diff] [blame] | 629 | |
sewardj | c2c8716 | 2004-11-25 13:07:02 +0000 | [diff] [blame] | 630 | /* Build IR to calculate some particular condition from stored |
cerion | c60c01e | 2004-12-02 20:19:22 +0000 | [diff] [blame] | 631 | CC_OP/CC_DEP1/CC_DEP2. Returns an expression |
sewardj | cca7194 | 2004-12-02 23:35:18 +0000 | [diff] [blame] | 632 | of type Ity_I1. |
cerion | c60c01e | 2004-12-02 20:19:22 +0000 | [diff] [blame] | 633 | */ |
| 634 | static IRExpr* mk_armg_calculate_condition ( ARMCondcode cond ) |
sewardj | c2c8716 | 2004-11-25 13:07:02 +0000 | [diff] [blame] | 635 | { |
| 636 | IRExpr** args |
cerion | c60c01e | 2004-12-02 20:19:22 +0000 | [diff] [blame] | 637 | = mkIRExprVec_4( mkU32(cond), |
sewardj | c2c8716 | 2004-11-25 13:07:02 +0000 | [diff] [blame] | 638 | IRExpr_Get(OFFB_CC_OP, Ity_I32), |
| 639 | IRExpr_Get(OFFB_CC_DEP1, Ity_I32), |
cerion | c60c01e | 2004-12-02 20:19:22 +0000 | [diff] [blame] | 640 | IRExpr_Get(OFFB_CC_DEP2, Ity_I32) ); |
sewardj | c2c8716 | 2004-11-25 13:07:02 +0000 | [diff] [blame] | 641 | IRExpr* call |
| 642 | = mkIRExprCCall( |
| 643 | Ity_I32, |
| 644 | 0/*regparm*/, |
cerion | c60c01e | 2004-12-02 20:19:22 +0000 | [diff] [blame] | 645 | "armg_calculate_condition", &armg_calculate_condition, |
sewardj | c2c8716 | 2004-11-25 13:07:02 +0000 | [diff] [blame] | 646 | args |
| 647 | ); |
cerion | c60c01e | 2004-12-02 20:19:22 +0000 | [diff] [blame] | 648 | |
| 649 | /* Exclude the requested condition and OP from definedness |
sewardj | c2c8716 | 2004-11-25 13:07:02 +0000 | [diff] [blame] | 650 | checking. We're only interested in DEP1 and DEP2. */ |
cerion | c60c01e | 2004-12-02 20:19:22 +0000 | [diff] [blame] | 651 | call->Iex.CCall.cee->mcx_mask = (1<<0) | (1<<1); |
sewardj | c2c8716 | 2004-11-25 13:07:02 +0000 | [diff] [blame] | 652 | return unop(Iop_32to1, call); |
| 653 | } |
| 654 | |
cerion | c60c01e | 2004-12-02 20:19:22 +0000 | [diff] [blame] | 655 | |
| 656 | |
cerion | c60c01e | 2004-12-02 20:19:22 +0000 | [diff] [blame] | 657 | |
| 658 | |
sewardj | c2c8716 | 2004-11-25 13:07:02 +0000 | [diff] [blame] | 659 | |
| 660 | |
| 661 | /* -------------- Building the flags-thunk. -------------- */ |
| 662 | |
| 663 | /* The machinery in this section builds the flag-thunk following a |
| 664 | flag-setting operation. Hence the various setFlags_* functions. |
| 665 | */ |
| 666 | |
sewardj | fa89fdc | 2004-12-15 12:21:28 +0000 | [diff] [blame] | 667 | #if 0 |
sewardj | c2c8716 | 2004-11-25 13:07:02 +0000 | [diff] [blame] | 668 | static Bool isAddSub ( IROp op8 ) |
| 669 | { |
| 670 | return op8 == Iop_Add8 || op8 == Iop_Sub8; |
| 671 | } |
sewardj | fa89fdc | 2004-12-15 12:21:28 +0000 | [diff] [blame] | 672 | #endif |
sewardj | c2c8716 | 2004-11-25 13:07:02 +0000 | [diff] [blame] | 673 | |
sewardj | fa89fdc | 2004-12-15 12:21:28 +0000 | [diff] [blame] | 674 | #if 0 |
sewardj | c2c8716 | 2004-11-25 13:07:02 +0000 | [diff] [blame] | 675 | static Bool isLogic ( IROp op8 ) |
| 676 | { |
| 677 | return op8 == Iop_And8 || op8 == Iop_Or8 || op8 == Iop_Xor8; |
| 678 | } |
sewardj | fa89fdc | 2004-12-15 12:21:28 +0000 | [diff] [blame] | 679 | #endif |
sewardj | c2c8716 | 2004-11-25 13:07:02 +0000 | [diff] [blame] | 680 | |
| 681 | /* U-widen 8/16/32 bit int expr to 32. */ |
| 682 | static IRExpr* widenUto32 ( IRExpr* e ) |
| 683 | { |
| 684 | switch (typeOfIRExpr(irbb->tyenv,e)) { |
| 685 | case Ity_I32: return e; |
| 686 | case Ity_I16: return unop(Iop_16Uto32,e); |
| 687 | case Ity_I8: return unop(Iop_8Uto32,e); |
| 688 | default: vpanic("widenUto32"); |
| 689 | } |
| 690 | } |
| 691 | |
sewardj | fa89fdc | 2004-12-15 12:21:28 +0000 | [diff] [blame] | 692 | #if 0 |
sewardj | c2c8716 | 2004-11-25 13:07:02 +0000 | [diff] [blame] | 693 | /* S-widen 8/16/32 bit int expr to 32. */ |
| 694 | static IRExpr* widenSto32 ( IRExpr* e ) |
| 695 | { |
| 696 | switch (typeOfIRExpr(irbb->tyenv,e)) { |
| 697 | case Ity_I32: return e; |
| 698 | case Ity_I16: return unop(Iop_16Sto32,e); |
| 699 | case Ity_I8: return unop(Iop_8Sto32,e); |
| 700 | default: vpanic("widenSto32"); |
| 701 | } |
| 702 | } |
sewardj | fa89fdc | 2004-12-15 12:21:28 +0000 | [diff] [blame] | 703 | #endif |
sewardj | c2c8716 | 2004-11-25 13:07:02 +0000 | [diff] [blame] | 704 | |
| 705 | /* Narrow 8/16/32 bit int expr to 8/16/32. Clearly only some |
| 706 | of these combinations make sense. */ |
| 707 | static IRExpr* narrowTo ( IRType dst_ty, IRExpr* e ) |
| 708 | { |
| 709 | IRType src_ty = typeOfIRExpr(irbb->tyenv,e); |
| 710 | if (src_ty == dst_ty) |
| 711 | return e; |
| 712 | if (src_ty == Ity_I32 && dst_ty == Ity_I16) |
| 713 | return unop(Iop_32to16, e); |
| 714 | if (src_ty == Ity_I32 && dst_ty == Ity_I8) |
| 715 | return unop(Iop_32to8, e); |
| 716 | |
| 717 | vex_printf("\nsrc, dst tys are: "); |
| 718 | ppIRType(src_ty); |
| 719 | vex_printf(", "); |
| 720 | ppIRType(dst_ty); |
| 721 | vex_printf("\n"); |
cerion | c60c01e | 2004-12-02 20:19:22 +0000 | [diff] [blame] | 722 | vpanic("narrowTo(ARM)"); |
sewardj | c2c8716 | 2004-11-25 13:07:02 +0000 | [diff] [blame] | 723 | } |
| 724 | |
| 725 | |
| 726 | /* Set the flags thunk OP, DEP1 and DEP2 fields. The supplied op is |
| 727 | auto-sized up to the real op. */ |
| 728 | |
| 729 | static |
cerion | fd7474a | 2004-12-03 11:16:42 +0000 | [diff] [blame] | 730 | void setFlags_DEP1_DEP2 ( IROp op, IRTemp dep1, IRTemp dep2 ) |
sewardj | c2c8716 | 2004-11-25 13:07:02 +0000 | [diff] [blame] | 731 | { |
cerion | c60c01e | 2004-12-02 20:19:22 +0000 | [diff] [blame] | 732 | stmt( IRStmt_Put( OFFB_CC_OP, mkU32(op)) ); |
sewardj | c2c8716 | 2004-11-25 13:07:02 +0000 | [diff] [blame] | 733 | stmt( IRStmt_Put( OFFB_CC_DEP1, widenUto32(mkexpr(dep1))) ); |
| 734 | stmt( IRStmt_Put( OFFB_CC_DEP2, widenUto32(mkexpr(dep2))) ); |
| 735 | } |
| 736 | |
| 737 | |
| 738 | /* Set the OP and DEP1 fields only, and write zero to DEP2. */ |
| 739 | |
sewardj | fa89fdc | 2004-12-15 12:21:28 +0000 | [diff] [blame] | 740 | #if 0 |
sewardj | c2c8716 | 2004-11-25 13:07:02 +0000 | [diff] [blame] | 741 | static |
cerion | fd7474a | 2004-12-03 11:16:42 +0000 | [diff] [blame] | 742 | void setFlags_DEP1 ( IROp op, IRTemp dep1 ) |
sewardj | c2c8716 | 2004-11-25 13:07:02 +0000 | [diff] [blame] | 743 | { |
cerion | c60c01e | 2004-12-02 20:19:22 +0000 | [diff] [blame] | 744 | stmt( IRStmt_Put( OFFB_CC_OP, mkU32(op)) ); |
sewardj | c2c8716 | 2004-11-25 13:07:02 +0000 | [diff] [blame] | 745 | stmt( IRStmt_Put( OFFB_CC_DEP1, widenUto32(mkexpr(dep1))) ); |
| 746 | stmt( IRStmt_Put( OFFB_CC_DEP2, mkU32(0)) ); |
| 747 | } |
sewardj | fa89fdc | 2004-12-15 12:21:28 +0000 | [diff] [blame] | 748 | #endif |
sewardj | c2c8716 | 2004-11-25 13:07:02 +0000 | [diff] [blame] | 749 | |
sewardj | fa89fdc | 2004-12-15 12:21:28 +0000 | [diff] [blame] | 750 | #if 0 |
sewardj | c2c8716 | 2004-11-25 13:07:02 +0000 | [diff] [blame] | 751 | /* For shift operations, we put in the result and the undershifted |
| 752 | result. Except if the shift amount is zero, the thunk is left |
| 753 | unchanged. */ |
| 754 | |
cerion | c60c01e | 2004-12-02 20:19:22 +0000 | [diff] [blame] | 755 | static void setFlags_DEP1_DEP2_shift ( IROp op, |
sewardj | c2c8716 | 2004-11-25 13:07:02 +0000 | [diff] [blame] | 756 | IRTemp res, |
| 757 | IRTemp resUS, |
sewardj | c2c8716 | 2004-11-25 13:07:02 +0000 | [diff] [blame] | 758 | IRTemp guard ) |
| 759 | { |
cerion | c60c01e | 2004-12-02 20:19:22 +0000 | [diff] [blame] | 760 | vassert(guard); |
sewardj | c2c8716 | 2004-11-25 13:07:02 +0000 | [diff] [blame] | 761 | |
| 762 | /* DEP1 contains the result, DEP2 contains the undershifted value. */ |
| 763 | stmt( IRStmt_Put( OFFB_CC_OP, |
| 764 | IRExpr_Mux0X( mkexpr(guard), |
| 765 | IRExpr_Get(OFFB_CC_OP,Ity_I32), |
cerion | c60c01e | 2004-12-02 20:19:22 +0000 | [diff] [blame] | 766 | mkU32(op))) ); |
sewardj | c2c8716 | 2004-11-25 13:07:02 +0000 | [diff] [blame] | 767 | stmt( IRStmt_Put( OFFB_CC_DEP1, |
| 768 | IRExpr_Mux0X( mkexpr(guard), |
| 769 | IRExpr_Get(OFFB_CC_DEP1,Ity_I32), |
| 770 | widenUto32(mkexpr(res)))) ); |
| 771 | stmt( IRStmt_Put( OFFB_CC_DEP2, |
| 772 | IRExpr_Mux0X( mkexpr(guard), |
| 773 | IRExpr_Get(OFFB_CC_DEP2,Ity_I32), |
| 774 | widenUto32(mkexpr(resUS)))) ); |
| 775 | } |
sewardj | fa89fdc | 2004-12-15 12:21:28 +0000 | [diff] [blame] | 776 | #endif |
sewardj | c2c8716 | 2004-11-25 13:07:02 +0000 | [diff] [blame] | 777 | |
| 778 | |
cerion | c60c01e | 2004-12-02 20:19:22 +0000 | [diff] [blame] | 779 | |
| 780 | |
cerion | c60c01e | 2004-12-02 20:19:22 +0000 | [diff] [blame] | 781 | |
sewardj | fa89fdc | 2004-12-15 12:21:28 +0000 | [diff] [blame] | 782 | #if 0 |
sewardj | c2c8716 | 2004-11-25 13:07:02 +0000 | [diff] [blame] | 783 | /* Multiplies are pretty much like add and sub: DEP1 and DEP2 hold the |
| 784 | two arguments. */ |
| 785 | |
| 786 | static |
cerion | c60c01e | 2004-12-02 20:19:22 +0000 | [diff] [blame] | 787 | void setFlags_MUL ( IRTemp arg1, IRTemp arg2, UInt op ) |
sewardj | c2c8716 | 2004-11-25 13:07:02 +0000 | [diff] [blame] | 788 | { |
cerion | c60c01e | 2004-12-02 20:19:22 +0000 | [diff] [blame] | 789 | stmt( IRStmt_Put( OFFB_CC_OP, mkU32(op) ) ); |
| 790 | stmt( IRStmt_Put( OFFB_CC_DEP1, widenUto32(mkexpr(arg1)) )); |
| 791 | stmt( IRStmt_Put( OFFB_CC_DEP2, widenUto32(mkexpr(arg2)) )); |
sewardj | c2c8716 | 2004-11-25 13:07:02 +0000 | [diff] [blame] | 792 | } |
sewardj | fa89fdc | 2004-12-15 12:21:28 +0000 | [diff] [blame] | 793 | #endif |
cerion | c60c01e | 2004-12-02 20:19:22 +0000 | [diff] [blame] | 794 | |
| 795 | |
| 796 | |
| 797 | |
| 798 | |
| 799 | |
| 800 | |
sewardj | c2c8716 | 2004-11-25 13:07:02 +0000 | [diff] [blame] | 801 | |
| 802 | |
| 803 | /* -------------- Condition codes. -------------- */ |
| 804 | |
cerion | c60c01e | 2004-12-02 20:19:22 +0000 | [diff] [blame] | 805 | /* Condition codes, using the ARM encoding. */ |
sewardj | c2c8716 | 2004-11-25 13:07:02 +0000 | [diff] [blame] | 806 | |
sewardj | cca7194 | 2004-12-02 23:35:18 +0000 | [diff] [blame] | 807 | static HChar* name_ARMCondcode ( ARMCondcode cond ) |
sewardj | c2c8716 | 2004-11-25 13:07:02 +0000 | [diff] [blame] | 808 | { |
| 809 | switch (cond) { |
cerion | 397f9e5 | 2004-12-15 13:04:06 +0000 | [diff] [blame^] | 810 | case ARMCondEQ: return "{eq}"; |
| 811 | case ARMCondNE: return "{ne}"; |
| 812 | case ARMCondHS: return "{hs}"; // or 'cs' |
| 813 | case ARMCondLO: return "{lo}"; // or 'cc' |
| 814 | case ARMCondMI: return "{mi}"; |
| 815 | case ARMCondPL: return "{pl}"; |
| 816 | case ARMCondVS: return "{vs}"; |
| 817 | case ARMCondVC: return "{vc}"; |
| 818 | case ARMCondHI: return "{hi}"; |
| 819 | case ARMCondLS: return "{ls}"; |
| 820 | case ARMCondGE: return "{ge}"; |
| 821 | case ARMCondLT: return "{lt}"; |
| 822 | case ARMCondGT: return "{gt}"; |
| 823 | case ARMCondLE: return "{le}"; |
| 824 | case ARMCondAL: return ""; // {al}: default, doesn't need specifying |
| 825 | case ARMCondNV: return "{nv}"; |
cerion | c60c01e | 2004-12-02 20:19:22 +0000 | [diff] [blame] | 826 | default: vpanic("name_ARMCondcode"); |
sewardj | c2c8716 | 2004-11-25 13:07:02 +0000 | [diff] [blame] | 827 | } |
| 828 | } |
| 829 | |
sewardj | fa89fdc | 2004-12-15 12:21:28 +0000 | [diff] [blame] | 830 | #if 0 |
sewardj | c2c8716 | 2004-11-25 13:07:02 +0000 | [diff] [blame] | 831 | static |
cerion | c60c01e | 2004-12-02 20:19:22 +0000 | [diff] [blame] | 832 | ARMCondcode positiveIse_ARMCondcode ( ARMCondcode cond, |
sewardj | c2c8716 | 2004-11-25 13:07:02 +0000 | [diff] [blame] | 833 | Bool* needInvert ) |
| 834 | { |
cerion | c60c01e | 2004-12-02 20:19:22 +0000 | [diff] [blame] | 835 | vassert(cond >= ARMCondEQ && cond <= ARMCondNV); |
sewardj | c2c8716 | 2004-11-25 13:07:02 +0000 | [diff] [blame] | 836 | if (cond & 1) { |
| 837 | *needInvert = True; |
| 838 | return cond-1; |
| 839 | } else { |
| 840 | *needInvert = False; |
| 841 | return cond; |
| 842 | } |
| 843 | } |
sewardj | fa89fdc | 2004-12-15 12:21:28 +0000 | [diff] [blame] | 844 | #endif |
cerion | c60c01e | 2004-12-02 20:19:22 +0000 | [diff] [blame] | 845 | |
| 846 | |
cerion | 397f9e5 | 2004-12-15 13:04:06 +0000 | [diff] [blame^] | 847 | /* Addressing Mode 1 - DP ops |
| 848 | Addressing Mode 2 - Load/Store word/ubyte (scaled) |
| 849 | */ |
| 850 | static HChar* name_ARMShiftOp ( UChar shift_op, UChar imm_val ) |
| 851 | { |
| 852 | switch (shift_op) { |
| 853 | case 0x0: case 0x1: case 0x8: return "lsl"; |
| 854 | case 0x2: case 0x3: case 0xA: return "lsr"; |
| 855 | case 0x4: case 0x5: case 0xC: return "asr"; |
| 856 | case 0x6: return (imm_val==0) ? "rrx" : "ror"; |
| 857 | case 0x7: case 0xE: return "ror"; |
| 858 | default: vpanic("name_ARMShiftcode"); |
| 859 | } |
| 860 | } |
| 861 | |
| 862 | |
| 863 | /* Addressing Mode 4 - Load/Store Multiple */ |
| 864 | static HChar* name_ARMAddrMode4 ( UChar mode ) |
| 865 | { |
| 866 | /* See ARM ARM A5-55 for alternative names for stack operations |
| 867 | ldmfa (full ascending), etc. */ |
| 868 | switch (mode) { |
| 869 | case 0x0: return "da"; // Decrement after |
| 870 | case 0x1: return "ia"; // Increment after |
| 871 | case 0x2: return "db"; // Decrement before |
| 872 | case 0x3: return "ib"; // Increment before |
| 873 | default: vpanic("name_ARMAddrMode4"); |
| 874 | } |
| 875 | } |
| 876 | |
| 877 | /* Data Processing ops */ |
| 878 | static HChar* name_ARMDataProcOp ( UChar opc ) |
| 879 | { |
| 880 | switch (opc) { |
| 881 | case 0x0: return "and"; |
| 882 | case 0x1: return "eor"; |
| 883 | case 0x2: return "sub"; |
| 884 | case 0x3: return "rsb"; |
| 885 | case 0x4: return "add"; |
| 886 | case 0x5: return "adc"; |
| 887 | case 0x6: return "sbc"; |
| 888 | case 0x7: return "rsc"; |
| 889 | case 0x8: return "tst"; |
| 890 | case 0x9: return "teq"; |
| 891 | case 0xA: return "cmp"; |
| 892 | case 0xB: return "cmn"; |
| 893 | case 0xC: return "orr"; |
| 894 | case 0xD: return "mov"; |
| 895 | case 0xE: return "bic"; |
| 896 | case 0xF: return "mvn"; |
| 897 | default: vpanic("name_ARMDataProcOp"); |
| 898 | } |
| 899 | } |
cerion | c60c01e | 2004-12-02 20:19:22 +0000 | [diff] [blame] | 900 | |
| 901 | |
| 902 | |
cerion | f7da63d | 2004-12-09 19:04:57 +0000 | [diff] [blame] | 903 | /* |
| 904 | Addressing mode 4 - LOAD/STORE multiple, LDM|STM |
| 905 | ARM ARM A5-48 |
| 906 | */ |
| 907 | static |
cerion | a512a65 | 2004-12-10 11:43:10 +0000 | [diff] [blame] | 908 | Bool dis_loadstore_mult ( theInstr ) |
cerion | f7da63d | 2004-12-09 19:04:57 +0000 | [diff] [blame] | 909 | { |
| 910 | UChar flags = (theInstr >> 20) & 0x1F; // theInstr[24:20] |
| 911 | UChar Rn_addr = (theInstr >> 16) & 0xF; |
| 912 | IRTemp Rn = newTemp(Ity_I32); |
| 913 | IRTemp Rn_orig = newTemp(Ity_I32); |
cerion | 060c542 | 2004-12-10 15:28:43 +0000 | [diff] [blame] | 914 | UInt reg_list = theInstr & 0xFFFF; // each bit addresses a register: R15 to R0 |
| 915 | |
cerion | f7da63d | 2004-12-09 19:04:57 +0000 | [diff] [blame] | 916 | UChar L = (flags >> 0) & 1; // Load(1) | Store(0) |
| 917 | UChar W = (flags >> 1) & 1; // (W)riteback Rn (incr(U=1) | decr(U=0) by n_bytes) |
cerion | 397f9e5 | 2004-12-15 13:04:06 +0000 | [diff] [blame^] | 918 | UChar S = (flags >> 2) & 1; // Priviledged mode flag - *** CAB TODO *** |
cerion | f7da63d | 2004-12-09 19:04:57 +0000 | [diff] [blame] | 919 | UChar U = (flags >> 3) & 1; // Txfr ctl: Direction = upwards(1) | downwards(0) |
| 920 | UChar PU = (flags >> 3) & 3; // Txfr ctl: Rn within(P=1) | outside(P=0) accessed mem |
| 921 | |
cerion | 19e8a61 | 2004-12-10 10:18:58 +0000 | [diff] [blame] | 922 | IRTemp start_addr = newTemp(Ity_I32); |
| 923 | IRTemp end_addr = newTemp(Ity_I32); |
cerion | f7da63d | 2004-12-09 19:04:57 +0000 | [diff] [blame] | 924 | IRTemp data=0; |
| 925 | UInt n_bytes=0; |
| 926 | UInt tmp_reg = reg_list; |
cerion | 19e8a61 | 2004-12-10 10:18:58 +0000 | [diff] [blame] | 927 | UInt reg_idx, offset; |
cerion | a512a65 | 2004-12-10 11:43:10 +0000 | [diff] [blame] | 928 | Bool decode_ok = True; |
cerion | f7da63d | 2004-12-09 19:04:57 +0000 | [diff] [blame] | 929 | |
cerion | 397f9e5 | 2004-12-15 13:04:06 +0000 | [diff] [blame^] | 930 | HChar* cond_name = name_ARMCondcode( (theInstr >> 28) & 0xF ); |
| 931 | HChar reg_names[70]; |
| 932 | UInt buf_offset; |
| 933 | |
cerion | f7da63d | 2004-12-09 19:04:57 +0000 | [diff] [blame] | 934 | while (tmp_reg > 0) { // Count num bits in reg_list => num_bytes |
| 935 | if (tmp_reg & 1) { n_bytes += 4; } |
| 936 | tmp_reg = tmp_reg >> 1; |
| 937 | } |
| 938 | |
| 939 | assign( Rn, getIReg(Rn_addr) ); |
| 940 | assign( Rn_orig, mkexpr(Rn) ); |
| 941 | |
cerion | 19e8a61 | 2004-12-10 10:18:58 +0000 | [diff] [blame] | 942 | switch (PU) { // <addressing_mode> |
cerion | f7da63d | 2004-12-09 19:04:57 +0000 | [diff] [blame] | 943 | case 0x0: // Decrement after (DA) |
| 944 | assign( start_addr, binop( Iop_Add32, mkexpr(Rn), mkU32(n_bytes + 4) ) ); |
| 945 | assign( end_addr, mkexpr(Rn) ); |
| 946 | break; |
| 947 | |
| 948 | case 0x1: // Increment after (IA) |
| 949 | assign( start_addr, mkexpr(Rn) ); |
| 950 | assign( end_addr, binop( Iop_Add32, mkexpr(Rn), mkU32(n_bytes - 4) ) ); |
| 951 | break; |
| 952 | |
| 953 | case 0x2: // Decrement before (DB) |
| 954 | assign( start_addr, binop( Iop_Sub32, mkexpr(Rn), mkU32(n_bytes) ) ); |
| 955 | assign( end_addr, binop( Iop_Sub32, mkexpr(Rn), mkU32(4) ) ); |
| 956 | break; |
| 957 | |
| 958 | case 0x3: // Increment before (IB) |
| 959 | assign( start_addr, binop( Iop_Add32, mkexpr(Rn), mkU32(4) ) ); |
| 960 | assign( end_addr, binop( Iop_Add32, mkexpr(Rn), mkU32(n_bytes) ) ); |
| 961 | break; |
| 962 | |
| 963 | default: |
cerion | a512a65 | 2004-12-10 11:43:10 +0000 | [diff] [blame] | 964 | vex_printf("dis_loadstore_mult(ARM): No such case: 0x%x", PU); |
| 965 | return False; |
cerion | f7da63d | 2004-12-09 19:04:57 +0000 | [diff] [blame] | 966 | } |
| 967 | |
| 968 | if (W==1) { |
| 969 | if (U==1) { // upwards |
cerion | 19e8a61 | 2004-12-10 10:18:58 +0000 | [diff] [blame] | 970 | putIReg( Rn_addr, binop( Iop_Add32, mkexpr(Rn), mkU32(n_bytes) ) ); |
cerion | f7da63d | 2004-12-09 19:04:57 +0000 | [diff] [blame] | 971 | } else { // downwards |
cerion | 19e8a61 | 2004-12-10 10:18:58 +0000 | [diff] [blame] | 972 | putIReg( Rn_addr, binop( Iop_Sub32, mkexpr(Rn), mkU32(n_bytes) ) ); |
cerion | f7da63d | 2004-12-09 19:04:57 +0000 | [diff] [blame] | 973 | } |
| 974 | } |
| 975 | |
| 976 | |
| 977 | /* |
| 978 | Loop through register list, LOAD/STORE indicated registers |
cerion | 060c542 | 2004-12-10 15:28:43 +0000 | [diff] [blame] | 979 | Lowest numbered reg -> lowest address, so start with lowest register |
| 980 | reg_idx: guest register address |
| 981 | offset : current mem offset from start_addr |
cerion | f7da63d | 2004-12-09 19:04:57 +0000 | [diff] [blame] | 982 | */ |
cerion | 397f9e5 | 2004-12-15 13:04:06 +0000 | [diff] [blame^] | 983 | reg_names[0] = '\0'; |
| 984 | buf_offset=0; |
cerion | 19e8a61 | 2004-12-10 10:18:58 +0000 | [diff] [blame] | 985 | offset=0; |
cerion | f7da63d | 2004-12-09 19:04:57 +0000 | [diff] [blame] | 986 | for (reg_idx=0; reg_idx < 16; reg_idx++) { |
| 987 | if (( reg_list >> reg_idx ) & 1) { // reg_list[i] == 1? |
cerion | 19e8a61 | 2004-12-10 10:18:58 +0000 | [diff] [blame] | 988 | |
| 989 | if (L==1) { // LOAD Ri, (start_addr + offset) |
cerion | f7da63d | 2004-12-09 19:04:57 +0000 | [diff] [blame] | 990 | |
cerion | a512a65 | 2004-12-10 11:43:10 +0000 | [diff] [blame] | 991 | if (Rn_addr == reg_idx && W==1) { // Undefined - ARM ARM A4-31 |
| 992 | decode_ok=False; |
| 993 | break; |
| 994 | } |
cerion | f7da63d | 2004-12-09 19:04:57 +0000 | [diff] [blame] | 995 | |
cerion | 19e8a61 | 2004-12-10 10:18:58 +0000 | [diff] [blame] | 996 | assign( data, loadLE(Ity_I32, binop(Iop_Add32, |
cerion | 060c542 | 2004-12-10 15:28:43 +0000 | [diff] [blame] | 997 | mkexpr(start_addr), |
| 998 | mkU32(offset))) ); |
cerion | f7da63d | 2004-12-09 19:04:57 +0000 | [diff] [blame] | 999 | if (reg_idx == 15) { |
| 1000 | // assuming architecture < 5: See ARM ARM A4-31 |
| 1001 | putIReg( reg_idx, binop(Iop_And32, mkexpr(data), mkU32(0xFFFFFFFC)) ); |
| 1002 | } else { |
| 1003 | putIReg( reg_idx, mkexpr(data) ); |
| 1004 | } |
cerion | 19e8a61 | 2004-12-10 10:18:58 +0000 | [diff] [blame] | 1005 | } else { // STORE Ri, (start_addr + offset) |
cerion | f7da63d | 2004-12-09 19:04:57 +0000 | [diff] [blame] | 1006 | |
| 1007 | // ARM ARM A4-85 (Operand restrictions) |
cerion | a512a65 | 2004-12-10 11:43:10 +0000 | [diff] [blame] | 1008 | if (reg_idx == Rn_addr && W==1) { // Rn in reg_list && writeback |
| 1009 | if (offset != 0) { // Undefined - See ARM ARM A4-85 |
| 1010 | decode_ok=False; |
| 1011 | break; |
cerion | f7da63d | 2004-12-09 19:04:57 +0000 | [diff] [blame] | 1012 | } |
cerion | a512a65 | 2004-12-10 11:43:10 +0000 | [diff] [blame] | 1013 | // is lowest reg in reg_list: store Rn_orig |
| 1014 | storeLE( mkexpr(start_addr), mkexpr(Rn_orig) ); |
cerion | f7da63d | 2004-12-09 19:04:57 +0000 | [diff] [blame] | 1015 | } else { |
cerion | 19e8a61 | 2004-12-10 10:18:58 +0000 | [diff] [blame] | 1016 | storeLE( binop(Iop_Add32, mkexpr(start_addr), mkU32(offset) ), |
| 1017 | getIReg(reg_idx) ); |
cerion | f7da63d | 2004-12-09 19:04:57 +0000 | [diff] [blame] | 1018 | } |
| 1019 | } |
cerion | 19e8a61 | 2004-12-10 10:18:58 +0000 | [diff] [blame] | 1020 | offset += 4; |
cerion | 397f9e5 | 2004-12-15 13:04:06 +0000 | [diff] [blame^] | 1021 | |
| 1022 | reg_names[buf_offset++] = 'R'; |
| 1023 | if (reg_idx > 9) { |
| 1024 | reg_names[buf_offset++] = '1'; |
| 1025 | reg_names[buf_offset++] = 38 + reg_idx; |
| 1026 | } else { |
| 1027 | reg_names[buf_offset++] = 48 + reg_idx; |
| 1028 | } |
| 1029 | reg_names[buf_offset++] = ','; |
| 1030 | // CAB: Eugh! Where's strcpy?! |
cerion | f7da63d | 2004-12-09 19:04:57 +0000 | [diff] [blame] | 1031 | } |
| 1032 | } |
cerion | 397f9e5 | 2004-12-15 13:04:06 +0000 | [diff] [blame^] | 1033 | if (buf_offset > 0) { |
| 1034 | reg_names[buf_offset-1] = '\0'; |
| 1035 | } |
| 1036 | DIP("%s%s%s R%d%s, {%s}%s\n", (L==1) ? "ldm":"stm", cond_name, |
| 1037 | name_ARMAddrMode4( PU ), Rn_addr, (W==1) ? "!" : "", |
| 1038 | reg_names, (S==1) ? "^" : ""); |
| 1039 | |
cerion | f7da63d | 2004-12-09 19:04:57 +0000 | [diff] [blame] | 1040 | // CAB TODO: |
cerion | 19e8a61 | 2004-12-10 10:18:58 +0000 | [diff] [blame] | 1041 | // IR assert( end_addr == (start_addr + offset) - 8 ) |
cerion | f7da63d | 2004-12-09 19:04:57 +0000 | [diff] [blame] | 1042 | |
cerion | 060c542 | 2004-12-10 15:28:43 +0000 | [diff] [blame] | 1043 | if (offset == 0) { // Unpredictable - ARM ARM A5-21 |
| 1044 | vex_printf("dis_loadstore_mult(arm): Unpredictable - offset==0\n"); |
| 1045 | decode_ok = False; |
| 1046 | } |
| 1047 | |
cerion | a512a65 | 2004-12-10 11:43:10 +0000 | [diff] [blame] | 1048 | return decode_ok; |
cerion | f7da63d | 2004-12-09 19:04:57 +0000 | [diff] [blame] | 1049 | } |
| 1050 | |
| 1051 | |
| 1052 | |
| 1053 | |
cerion | 060c542 | 2004-12-10 15:28:43 +0000 | [diff] [blame] | 1054 | |
cerion | f7da63d | 2004-12-09 19:04:57 +0000 | [diff] [blame] | 1055 | static |
cerion | 397f9e5 | 2004-12-15 13:04:06 +0000 | [diff] [blame^] | 1056 | Bool dis_loadstore_w_ub_address ( UInt theInstr, IRTemp* address, HChar* buf ) |
cerion | f7da63d | 2004-12-09 19:04:57 +0000 | [diff] [blame] | 1057 | { |
| 1058 | UChar is_reg = (theInstr >> 25) & 0x1; // immediate | register offset/index |
| 1059 | UInt flags = (theInstr >> 20) & 0x3F; // theInstr[25:20] |
| 1060 | UChar Rn_addr = (theInstr >> 16) & 0xF; |
cerion | f7da63d | 2004-12-09 19:04:57 +0000 | [diff] [blame] | 1061 | UChar Rm_addr = (theInstr >> 00) & 0xF; |
| 1062 | UChar shift_op = (theInstr >> 04) & 0xFF; |
| 1063 | UInt offset_12 = (theInstr >> 00) & 0xFFF; |
| 1064 | IRTemp Rn = newTemp(Ity_I32); |
| 1065 | IRTemp Rm = newTemp(Ity_I32); |
| 1066 | UChar shift_imm, shift; |
| 1067 | |
cerion | 060c542 | 2004-12-10 15:28:43 +0000 | [diff] [blame] | 1068 | UChar W = (flags >> 1) & 1; // base register writeback flag - See *Note |
cerion | f7da63d | 2004-12-09 19:04:57 +0000 | [diff] [blame] | 1069 | UChar U = (flags >> 3) & 1; // offset is added(1)|subtracted(0) from the base |
cerion | 060c542 | 2004-12-10 15:28:43 +0000 | [diff] [blame] | 1070 | UChar P = (flags >> 4) & 1; // addressing mode flag - See *Note |
| 1071 | /* *Note |
| 1072 | P==0: post-indexed addressing: addr -> Rn |
| 1073 | W==0: normal mem access |
| 1074 | W==1: unprivileged mem access |
| 1075 | P==1: W==0: offset addressing: Rn not updated - ARM ARM A5-20 |
| 1076 | W==1: pre-indexed addressing: addr -> Rn |
| 1077 | */ |
cerion | f7da63d | 2004-12-09 19:04:57 +0000 | [diff] [blame] | 1078 | |
cerion | 060c542 | 2004-12-10 15:28:43 +0000 | [diff] [blame] | 1079 | IRTemp scaled_index = newTemp(Ity_I32); |
| 1080 | IRTemp reg_offset = newTemp(Ity_I32); |
cerion | f7da63d | 2004-12-09 19:04:57 +0000 | [diff] [blame] | 1081 | |
| 1082 | IRTemp oldFlagC = newTemp(Ity_I32); |
| 1083 | |
cerion | 397f9e5 | 2004-12-15 13:04:06 +0000 | [diff] [blame^] | 1084 | HChar buf2[30]; |
| 1085 | HChar buf3[20]; |
| 1086 | buf3[0] = '\0'; |
| 1087 | |
cerion | f7da63d | 2004-12-09 19:04:57 +0000 | [diff] [blame] | 1088 | if (Rn_addr == 15) { |
cerion | 060c542 | 2004-12-10 15:28:43 +0000 | [diff] [blame] | 1089 | if (P==1 && W==0) { // offset addressing |
cerion | a512a65 | 2004-12-10 11:43:10 +0000 | [diff] [blame] | 1090 | // CAB: This right? |
cerion | 060c542 | 2004-12-10 15:28:43 +0000 | [diff] [blame] | 1091 | assign( Rn, binop(Iop_And32, getIReg(15), mkU32(8)) ); |
| 1092 | } else { // Unpredictable - ARM ARM A5-25,29... |
| 1093 | vex_printf("dis_loadstore_w_ub_address(arm): Unpredictable - Rn_addr==15\n"); |
cerion | a512a65 | 2004-12-10 11:43:10 +0000 | [diff] [blame] | 1094 | return False; |
cerion | f7da63d | 2004-12-09 19:04:57 +0000 | [diff] [blame] | 1095 | } |
cerion | 060c542 | 2004-12-10 15:28:43 +0000 | [diff] [blame] | 1096 | } else { |
| 1097 | assign( Rn, getIReg(Rn_addr) ); |
cerion | f7da63d | 2004-12-09 19:04:57 +0000 | [diff] [blame] | 1098 | } |
| 1099 | |
| 1100 | /* |
cerion | 060c542 | 2004-12-10 15:28:43 +0000 | [diff] [blame] | 1101 | Retrieve / Calculate reg_offset |
cerion | f7da63d | 2004-12-09 19:04:57 +0000 | [diff] [blame] | 1102 | */ |
| 1103 | if (is_reg) { |
cerion | 060c542 | 2004-12-10 15:28:43 +0000 | [diff] [blame] | 1104 | if (Rm_addr == 15) { // Unpredictable - ARM ARM A5-21 |
| 1105 | vex_printf("dis_loadstore_w_ub_address(arm): Unpredictable - Rm_addr==15\n"); |
cerion | a512a65 | 2004-12-10 11:43:10 +0000 | [diff] [blame] | 1106 | return False; |
| 1107 | } |
cerion | 060c542 | 2004-12-10 15:28:43 +0000 | [diff] [blame] | 1108 | if (P==0 || W==1) { // pre|post-indexed addressing |
| 1109 | if (Rm_addr == Rn_addr) { // Unpredictable - ARM ARM A5-25 |
| 1110 | vex_printf("dis_loadstore_w_ub_address(arm): Unpredictable - Rm_addr==Rn_addr\n"); |
| 1111 | return False; |
| 1112 | } |
| 1113 | } |
cerion | f7da63d | 2004-12-09 19:04:57 +0000 | [diff] [blame] | 1114 | assign( Rm, getIReg(Rm_addr) ); |
| 1115 | |
cerion | 060c542 | 2004-12-10 15:28:43 +0000 | [diff] [blame] | 1116 | if (shift_op == 0) { // Register addressing |
| 1117 | assign( reg_offset, mkexpr(Rm) ); |
| 1118 | } else { // Scaled Register addressing |
cerion | f7da63d | 2004-12-09 19:04:57 +0000 | [diff] [blame] | 1119 | shift_imm = (shift_op >> 3) & 0x1F; |
| 1120 | shift = (shift_op >> 1) & 0x3; |
| 1121 | |
| 1122 | switch (shift) { |
| 1123 | case 0x0: // LSL |
cerion | 060c542 | 2004-12-10 15:28:43 +0000 | [diff] [blame] | 1124 | assign( scaled_index, binop(Iop_Shl32, mkexpr(Rm), mkU8(shift_imm)) ); |
cerion | f7da63d | 2004-12-09 19:04:57 +0000 | [diff] [blame] | 1125 | break; |
| 1126 | |
| 1127 | case 0x1: // LSR |
| 1128 | if (shift_imm) { |
cerion | 060c542 | 2004-12-10 15:28:43 +0000 | [diff] [blame] | 1129 | assign( scaled_index, binop(Iop_Shr32, mkexpr(Rm), mkU8(shift_imm)) ); |
cerion | f7da63d | 2004-12-09 19:04:57 +0000 | [diff] [blame] | 1130 | } else { |
cerion | 060c542 | 2004-12-10 15:28:43 +0000 | [diff] [blame] | 1131 | assign( scaled_index, mkU32(0) ); |
cerion | f7da63d | 2004-12-09 19:04:57 +0000 | [diff] [blame] | 1132 | } |
| 1133 | break; |
| 1134 | |
| 1135 | case 0x2: // ASR |
| 1136 | if (shift_imm) { |
cerion | 060c542 | 2004-12-10 15:28:43 +0000 | [diff] [blame] | 1137 | assign( scaled_index, binop(Iop_Sar32, mkexpr(Rm), mkU32(shift_imm)) ); |
cerion | f7da63d | 2004-12-09 19:04:57 +0000 | [diff] [blame] | 1138 | } else { |
cerion | 060c542 | 2004-12-10 15:28:43 +0000 | [diff] [blame] | 1139 | assign( scaled_index, // Rm[31] ? 0xFFFFFFFF : 0x0 |
| 1140 | IRExpr_Mux0X(binop(Iop_And32, mkexpr(Rm), mkU32(0x8FFFFFFF)), |
| 1141 | mkexpr(0x0), mkexpr(0xFFFFFFFF)) ); |
cerion | f7da63d | 2004-12-09 19:04:57 +0000 | [diff] [blame] | 1142 | } |
| 1143 | break; |
| 1144 | |
| 1145 | case 0x3: // ROR|RRX |
cerion | dee0867 | 2004-12-14 12:08:09 +0000 | [diff] [blame] | 1146 | assign( oldFlagC, binop(Iop_Shr32, |
| 1147 | mk_armg_calculate_flags_c(), |
cerion | 397f9e5 | 2004-12-15 13:04:06 +0000 | [diff] [blame^] | 1148 | mkU8(ARMG_CC_SHIFT_C)) ); |
cerion | f7da63d | 2004-12-09 19:04:57 +0000 | [diff] [blame] | 1149 | |
| 1150 | if (shift_imm == 0) { // RRX (ARM ARM A5-17) |
| 1151 | // 33 bit ROR using carry flag as the 33rd bit |
| 1152 | // op = Rm >> 1, carry flag replacing vacated bit position. |
cerion | dee0867 | 2004-12-14 12:08:09 +0000 | [diff] [blame] | 1153 | // scaled_index = (c_flag << 31) | (Rm >> 1) |
cerion | 060c542 | 2004-12-10 15:28:43 +0000 | [diff] [blame] | 1154 | assign( scaled_index, binop(Iop_Or32, |
| 1155 | binop(Iop_Shl32, mkexpr(oldFlagC), mkU32(31)), |
| 1156 | binop(Iop_Shr32, mkexpr(Rm), mkU8(1))) ); |
cerion | f7da63d | 2004-12-09 19:04:57 +0000 | [diff] [blame] | 1157 | |
| 1158 | } else { // ROR |
cerion | 060c542 | 2004-12-10 15:28:43 +0000 | [diff] [blame] | 1159 | // scaled_index = Rm ROR shift_imm |
| 1160 | // = (Rm >> shift_imm) | (Rm << (32-shift_imm)) |
| 1161 | assign( scaled_index, |
| 1162 | binop(Iop_Or32, |
| 1163 | binop(Iop_Shr32, mkexpr(Rm), mkU8(shift_imm)), |
| 1164 | binop(Iop_Shl32, mkexpr(Rm), |
| 1165 | binop(Iop_Sub8, mkU8(32), mkU32(shift_imm)))) ); |
cerion | f7da63d | 2004-12-09 19:04:57 +0000 | [diff] [blame] | 1166 | } |
| 1167 | break; |
| 1168 | |
cerion | 060c542 | 2004-12-10 15:28:43 +0000 | [diff] [blame] | 1169 | default: |
| 1170 | vex_printf("dis_loadstore_w_ub(ARM): No such case: 0x%x", shift); |
| 1171 | return False; |
cerion | f7da63d | 2004-12-09 19:04:57 +0000 | [diff] [blame] | 1172 | } |
cerion | 060c542 | 2004-12-10 15:28:43 +0000 | [diff] [blame] | 1173 | assign( reg_offset, mkexpr(scaled_index) ); |
cerion | 397f9e5 | 2004-12-15 13:04:06 +0000 | [diff] [blame^] | 1174 | |
| 1175 | if (shift == 0x3 && shift_imm == 0) { |
| 1176 | DIS(buf3, ", %s", name_ARMShiftOp(shift_op * 2, shift_imm)); |
| 1177 | } else { |
| 1178 | DIS(buf3, ", %s #%d", name_ARMShiftOp(shift_op * 2, shift_imm), shift_imm); |
| 1179 | } |
cerion | f7da63d | 2004-12-09 19:04:57 +0000 | [diff] [blame] | 1180 | } |
cerion | 397f9e5 | 2004-12-15 13:04:06 +0000 | [diff] [blame^] | 1181 | DIS(buf2, "%cR%d%s", (U==1) ? '+' : '-', Rm_addr, buf3); |
cerion | 060c542 | 2004-12-10 15:28:43 +0000 | [diff] [blame] | 1182 | } else { // immediate |
| 1183 | assign( reg_offset, mkU32(offset_12) ); |
cerion | 397f9e5 | 2004-12-15 13:04:06 +0000 | [diff] [blame^] | 1184 | |
| 1185 | DIS(buf2, "#%c%d", (U==1) ? '+' : '-', offset_12); |
cerion | f7da63d | 2004-12-09 19:04:57 +0000 | [diff] [blame] | 1186 | } |
cerion | 397f9e5 | 2004-12-15 13:04:06 +0000 | [diff] [blame^] | 1187 | DIS(buf, "[R%d%s, %s%s", Rn_addr, |
| 1188 | (P==0) ? "]" : "", buf2, |
| 1189 | (P==1) ? ((W==1) ? "]!" : "]") : ""); |
| 1190 | |
cerion | f7da63d | 2004-12-09 19:04:57 +0000 | [diff] [blame] | 1191 | /* |
cerion | 060c542 | 2004-12-10 15:28:43 +0000 | [diff] [blame] | 1192 | Depending on P,U,W, write to Rn and set address to load/store |
cerion | f7da63d | 2004-12-09 19:04:57 +0000 | [diff] [blame] | 1193 | */ |
cerion | 060c542 | 2004-12-10 15:28:43 +0000 | [diff] [blame] | 1194 | if (P==1) { // offset | pre-indexed addressing |
| 1195 | if (U == 1) { // - increment |
| 1196 | assign( *address, binop(Iop_Add32, mkexpr(Rn), mkexpr(reg_offset)) ); |
| 1197 | } else { // - decrement |
| 1198 | assign( *address, binop(Iop_Sub32, mkexpr(Rn), mkexpr(reg_offset)) ); |
cerion | f7da63d | 2004-12-09 19:04:57 +0000 | [diff] [blame] | 1199 | } |
cerion | 060c542 | 2004-12-10 15:28:43 +0000 | [diff] [blame] | 1200 | if (W == 1) { // pre-indexed addressing, base register writeback |
| 1201 | putIReg( Rn_addr, mkexpr(*address) ); |
cerion | f7da63d | 2004-12-09 19:04:57 +0000 | [diff] [blame] | 1202 | } |
| 1203 | } else { // post-indexed addressing |
cerion | 060c542 | 2004-12-10 15:28:43 +0000 | [diff] [blame] | 1204 | assign( *address, mkexpr(Rn) ); |
| 1205 | if (U == 1) { // - increment |
| 1206 | putIReg( Rn_addr, binop( Iop_Add32, mkexpr(Rn), mkexpr(reg_offset) ) ); |
| 1207 | } else { // - decrement |
| 1208 | putIReg( Rn_addr, binop( Iop_Sub32, mkexpr(Rn), mkexpr(reg_offset) ) ); |
cerion | f7da63d | 2004-12-09 19:04:57 +0000 | [diff] [blame] | 1209 | } |
| 1210 | } |
cerion | 060c542 | 2004-12-10 15:28:43 +0000 | [diff] [blame] | 1211 | return True; |
| 1212 | } |
cerion | f7da63d | 2004-12-09 19:04:57 +0000 | [diff] [blame] | 1213 | |
| 1214 | |
| 1215 | |
cerion | 060c542 | 2004-12-10 15:28:43 +0000 | [diff] [blame] | 1216 | |
| 1217 | /* |
| 1218 | Addressing mode 2 - LOAD/STORE word or unsigned byte |
| 1219 | ARM ARM A5-18 |
| 1220 | */ |
| 1221 | static |
| 1222 | Bool dis_loadstore_w_ub ( UInt theInstr ) |
| 1223 | { |
| 1224 | UInt flags = (theInstr >> 20) & 0x3F; // theInstr[25:20] |
| 1225 | UChar Rn_addr = (theInstr >> 16) & 0xF; |
| 1226 | UChar Rd_addr = (theInstr >> 12) & 0xF; |
| 1227 | IRTemp address = newTemp(Ity_I32); |
| 1228 | |
| 1229 | UChar L = (flags >> 0) & 1; // Load(1) | Store(0) |
| 1230 | UChar W = (flags >> 1) & 1; // base register writeback |
| 1231 | UChar B = (flags >> 2) & 1; // access = unsigned byte(1) | word(0) |
| 1232 | |
| 1233 | IRTemp value = newTemp(Ity_I32); |
| 1234 | IRTemp data = newTemp(Ity_I32); |
| 1235 | IRTemp data_ror8 = newTemp(Ity_I32); |
| 1236 | IRTemp data_ror16 = newTemp(Ity_I32); |
| 1237 | IRTemp data_ror24 = newTemp(Ity_I32); |
| 1238 | IRExpr* expr_addr_10; |
cerion | 397f9e5 | 2004-12-15 13:04:06 +0000 | [diff] [blame^] | 1239 | HChar* cond_name = name_ARMCondcode( (theInstr >> 28) & 0xF ); |
| 1240 | HChar dis_buf[50]; |
cerion | 060c542 | 2004-12-10 15:28:43 +0000 | [diff] [blame] | 1241 | |
| 1242 | |
| 1243 | vassert(((theInstr >> 26) & 0x3) == 0x1); |
| 1244 | |
| 1245 | // Get the address to load/store |
cerion | 397f9e5 | 2004-12-15 13:04:06 +0000 | [diff] [blame^] | 1246 | if (!dis_loadstore_w_ub_address(theInstr, &address, dis_buf)) { return False; } |
cerion | 060c542 | 2004-12-10 15:28:43 +0000 | [diff] [blame] | 1247 | |
cerion | 397f9e5 | 2004-12-15 13:04:06 +0000 | [diff] [blame^] | 1248 | DIP("%s%s%s R%d, %s\n", (L==1) ? "ldr" : "str", cond_name, |
| 1249 | (B==1) ? "b" : "", Rd_addr, dis_buf); |
cerion | 060c542 | 2004-12-10 15:28:43 +0000 | [diff] [blame] | 1250 | |
| 1251 | if (Rd_addr == Rn_addr && W==1) { // Unpredictable - ARM ARM A4-39,41,89,91 |
| 1252 | vex_printf("dis_loadstore_w_ub(arm): Unpredictable - Rd_addr==Rn_addr\n"); |
| 1253 | return False; |
| 1254 | } |
| 1255 | |
cerion | f7da63d | 2004-12-09 19:04:57 +0000 | [diff] [blame] | 1256 | /* |
| 1257 | LOAD/STORE Rd, address |
| 1258 | */ |
| 1259 | if (L==1) { // LOAD |
| 1260 | if (B==1) { // unsigned byte (LDRB): ARM ARM A4-40 |
cerion | 060c542 | 2004-12-10 15:28:43 +0000 | [diff] [blame] | 1261 | if (Rd_addr == 15) { // Unpredictable - ARM ARM A4-40 |
| 1262 | vex_printf("dis_loadstore_w_ub(arm): Unpredictable - Rd_addr==15\n"); |
| 1263 | return False; |
| 1264 | } |
| 1265 | putIReg( Rd_addr, loadLE( Ity_I8, mkexpr( address ) ) ); |
cerion | f7da63d | 2004-12-09 19:04:57 +0000 | [diff] [blame] | 1266 | } |
| 1267 | else { // word (LDR): ARM ARM A4-38 |
cerion | 060c542 | 2004-12-10 15:28:43 +0000 | [diff] [blame] | 1268 | expr_addr_10 = binop(Iop_And32, mkexpr(address), mkU32(0x3)); |
| 1269 | |
| 1270 | /* |
| 1271 | CAB TODO |
| 1272 | if (Rd_addr == 15 && address[1:0] == 0) => Unpredictable |
| 1273 | How to bomb out using IR? |
| 1274 | */ |
cerion | f7da63d | 2004-12-09 19:04:57 +0000 | [diff] [blame] | 1275 | |
| 1276 | /* LOAD memory data (4 bytes) */ |
cerion | 060c542 | 2004-12-10 15:28:43 +0000 | [diff] [blame] | 1277 | assign( data, loadLE( Ity_I32, mkexpr( address ) ) ); |
cerion | f7da63d | 2004-12-09 19:04:57 +0000 | [diff] [blame] | 1278 | |
| 1279 | // data ROR 8 |
cerion | 060c542 | 2004-12-10 15:28:43 +0000 | [diff] [blame] | 1280 | assign( data_ror8, binop(Iop_Sub8, mkU8(32), mkU32(8)) ); |
| 1281 | assign( data_ror8, binop( Iop_Or32, |
| 1282 | binop( Iop_Shr32, mkexpr(data), mkU8(8) ), |
| 1283 | binop( Iop_Shl32, mkexpr(data), mkexpr(data_ror8) ) ) ); |
cerion | f7da63d | 2004-12-09 19:04:57 +0000 | [diff] [blame] | 1284 | // data ROR 16 |
cerion | 060c542 | 2004-12-10 15:28:43 +0000 | [diff] [blame] | 1285 | assign( data_ror16, binop(Iop_Sub8, mkU8(32), mkU32(16)) ); |
| 1286 | assign( data_ror16, binop( Iop_Or32, |
| 1287 | binop( Iop_Shr32, mkexpr(data), mkU8(16) ), |
| 1288 | binop( Iop_Shl32, mkexpr(data), mkexpr(data_ror16) ) ) ); |
cerion | f7da63d | 2004-12-09 19:04:57 +0000 | [diff] [blame] | 1289 | |
| 1290 | // data ROR 24 |
cerion | 060c542 | 2004-12-10 15:28:43 +0000 | [diff] [blame] | 1291 | assign( data_ror24, binop(Iop_Sub8, mkU8(32), mkU32(24)) ); |
| 1292 | assign( data_ror24, binop( Iop_Or32, |
| 1293 | binop( Iop_Shr32, mkexpr(data), mkU8(24) ), |
| 1294 | binop( Iop_Shl32, mkexpr(data), mkexpr(data_ror24) ) ) ); |
cerion | f7da63d | 2004-12-09 19:04:57 +0000 | [diff] [blame] | 1295 | |
cerion | 060c542 | 2004-12-10 15:28:43 +0000 | [diff] [blame] | 1296 | /* switch (address[1:0]) { |
| 1297 | 0x0: value = data; |
| 1298 | 0x1: value = data ROR 8; |
| 1299 | 0x2: value = data ROR 16; |
| 1300 | 0x3: value = data ROR 24; } */ |
| 1301 | assign( value, IRExpr_Mux0X( |
| 1302 | binop(Iop_CmpEQ32, expr_addr_10, mkU32(0x0)), |
cerion | f7da63d | 2004-12-09 19:04:57 +0000 | [diff] [blame] | 1303 | IRExpr_Mux0X( |
cerion | 060c542 | 2004-12-10 15:28:43 +0000 | [diff] [blame] | 1304 | binop(Iop_CmpEQ32, expr_addr_10, mkU32(0x1)), |
cerion | f7da63d | 2004-12-09 19:04:57 +0000 | [diff] [blame] | 1305 | IRExpr_Mux0X( |
cerion | 060c542 | 2004-12-10 15:28:43 +0000 | [diff] [blame] | 1306 | binop(Iop_CmpEQ32, expr_addr_10, mkU32(0x2)), |
| 1307 | mkexpr(data_ror24), |
| 1308 | mkexpr(data_ror16) ), |
| 1309 | mkexpr(data_ror8) ), |
| 1310 | mkexpr(data) ) ); |
cerion | f7da63d | 2004-12-09 19:04:57 +0000 | [diff] [blame] | 1311 | |
cerion | 060c542 | 2004-12-10 15:28:43 +0000 | [diff] [blame] | 1312 | if (Rd_addr == 15) { |
cerion | f7da63d | 2004-12-09 19:04:57 +0000 | [diff] [blame] | 1313 | // assuming architecture < 5: See ARM ARM A4-28 |
cerion | 060c542 | 2004-12-10 15:28:43 +0000 | [diff] [blame] | 1314 | putIReg( Rd_addr, binop(Iop_And32, mkexpr(value), mkU32(0xFFFFFFFC)) ); |
| 1315 | |
| 1316 | // CAB: Need to tell vex we're doing a jump here? |
| 1317 | // irbb->jumpkind = Ijk_Boring; |
| 1318 | // irbb->next = mkexpr(value); |
cerion | f7da63d | 2004-12-09 19:04:57 +0000 | [diff] [blame] | 1319 | } else { |
cerion | 060c542 | 2004-12-10 15:28:43 +0000 | [diff] [blame] | 1320 | putIReg( Rd_addr, mkexpr(value) ); |
cerion | f7da63d | 2004-12-09 19:04:57 +0000 | [diff] [blame] | 1321 | } |
| 1322 | |
| 1323 | } |
| 1324 | } else { // STORE: ARM ARM A4-88 |
cerion | f7da63d | 2004-12-09 19:04:57 +0000 | [diff] [blame] | 1325 | if (B==1) { // unsigned byte |
cerion | 060c542 | 2004-12-10 15:28:43 +0000 | [diff] [blame] | 1326 | if (Rd_addr == 15) { // Unpredictable - ARM ARM A4-90 |
| 1327 | vex_printf("dis_loadstore_w_ub(arm): Unpredictable - Rd_addr==15\n"); |
| 1328 | return False; |
| 1329 | } |
| 1330 | storeLE( mkexpr(address), unop(Iop_32to8, getIReg(Rd_addr)) ); // Rd[7:0] |
cerion | f7da63d | 2004-12-09 19:04:57 +0000 | [diff] [blame] | 1331 | } else { // word |
cerion | 060c542 | 2004-12-10 15:28:43 +0000 | [diff] [blame] | 1332 | |
| 1333 | if (Rd_addr == 15) { // Implementation Defined - ARM ARM A4-88 |
| 1334 | vex_printf("dis_loadstore_w_ub(arm): Implementation Defined - Rd_addr==15\n"); |
| 1335 | return False; |
| 1336 | // CAB TODO: What to do here? |
| 1337 | } |
| 1338 | storeLE( mkexpr(address), getIReg(Rd_addr) ); |
cerion | f7da63d | 2004-12-09 19:04:57 +0000 | [diff] [blame] | 1339 | } |
| 1340 | } |
cerion | a512a65 | 2004-12-10 11:43:10 +0000 | [diff] [blame] | 1341 | return True; |
cerion | f7da63d | 2004-12-09 19:04:57 +0000 | [diff] [blame] | 1342 | } |
cerion | c60c01e | 2004-12-02 20:19:22 +0000 | [diff] [blame] | 1343 | |
cerion | c60c01e | 2004-12-02 20:19:22 +0000 | [diff] [blame] | 1344 | |
cerion | fd7474a | 2004-12-03 11:16:42 +0000 | [diff] [blame] | 1345 | |
| 1346 | |
| 1347 | |
| 1348 | |
| 1349 | /* |
cerion | f7da63d | 2004-12-09 19:04:57 +0000 | [diff] [blame] | 1350 | ARMG_CC_OP_LSL, ARMG_CC_OP_LSR, ARMG_CC_OP_ASR |
| 1351 | ARM ARM A5-9... |
| 1352 | |
| 1353 | carry = carry_out[0] |
cerion | fd7474a | 2004-12-03 11:16:42 +0000 | [diff] [blame] | 1354 | */ |
cerion | a70a37b | 2004-12-03 18:54:08 +0000 | [diff] [blame] | 1355 | static |
cerion | 397f9e5 | 2004-12-15 13:04:06 +0000 | [diff] [blame^] | 1356 | IRExpr* dis_shift( Bool* decode_ok, UInt theInstr, IRTemp* carry_out, HChar* buf ) |
cerion | a70a37b | 2004-12-03 18:54:08 +0000 | [diff] [blame] | 1357 | { |
cerion | 060c542 | 2004-12-10 15:28:43 +0000 | [diff] [blame] | 1358 | UChar Rn_addr = (theInstr >> 16) & 0xF; |
| 1359 | UChar Rd_addr = (theInstr >> 12) & 0xF; |
| 1360 | UChar Rs_addr = (theInstr >> 8) & 0xF; |
| 1361 | UChar Rm_addr = (theInstr >> 0) & 0xF; |
| 1362 | UChar by_reg = (theInstr >> 4) & 0x1; // instr[4] |
| 1363 | UChar shift_imm = (theInstr >> 7) & 0x1F; // instr[11:7] |
| 1364 | UChar shift_op = (theInstr >> 4) & 0xF; // instr[7:4] |
cerion | f7da63d | 2004-12-09 19:04:57 +0000 | [diff] [blame] | 1365 | IRTemp Rm = newTemp(Ity_I32); |
| 1366 | IRTemp Rs = newTemp(Ity_I32); |
cerion | 19e8a61 | 2004-12-10 10:18:58 +0000 | [diff] [blame] | 1367 | IRTemp shift_amt = newTemp(Ity_I8); |
| 1368 | IRTemp carry_shift = newTemp(Ity_I8); |
cerion | f7da63d | 2004-12-09 19:04:57 +0000 | [diff] [blame] | 1369 | IRTemp oldFlagC = newTemp(Ity_I32); |
| 1370 | IRTemp mux_false = newTemp(Ity_I32); |
cerion | a70a37b | 2004-12-03 18:54:08 +0000 | [diff] [blame] | 1371 | IRExpr* expr; |
cerion | f7da63d | 2004-12-09 19:04:57 +0000 | [diff] [blame] | 1372 | IROp op; |
cerion | fd7474a | 2004-12-03 11:16:42 +0000 | [diff] [blame] | 1373 | |
cerion | dee0867 | 2004-12-14 12:08:09 +0000 | [diff] [blame] | 1374 | assign( oldFlagC, binop(Iop_Shr32, |
| 1375 | mk_armg_calculate_flags_c(), |
| 1376 | mkU8(ARMG_CC_SHIFT_C)) ); |
cerion | 060c542 | 2004-12-10 15:28:43 +0000 | [diff] [blame] | 1377 | |
cerion | f7da63d | 2004-12-09 19:04:57 +0000 | [diff] [blame] | 1378 | switch (shift_op) { |
cerion | a512a65 | 2004-12-10 11:43:10 +0000 | [diff] [blame] | 1379 | case 0x0: case 0x8: case 0x1: op = Iop_Shl32; break; |
| 1380 | case 0x2: case 0xA: case 0x3: op = Iop_Shr32; break; |
| 1381 | case 0x4: case 0xC: case 0x5: op = Iop_Sar32; break; |
| 1382 | default: |
| 1383 | vex_printf("dis_shift(arm): No such case: 0x%x\n", shift_op); |
| 1384 | *decode_ok = False; |
| 1385 | return mkU32(0); |
cerion | f7da63d | 2004-12-09 19:04:57 +0000 | [diff] [blame] | 1386 | } |
cerion | fd7474a | 2004-12-03 11:16:42 +0000 | [diff] [blame] | 1387 | |
cerion | f7da63d | 2004-12-09 19:04:57 +0000 | [diff] [blame] | 1388 | |
| 1389 | if (by_reg) { // Register Shift |
cerion | 060c542 | 2004-12-10 15:28:43 +0000 | [diff] [blame] | 1390 | assign( Rm, getIReg(Rm_addr) ); |
| 1391 | |
| 1392 | if (Rd_addr == 15 || Rm_addr == 15 || |
| 1393 | Rn_addr == 15 || Rs_addr == 15) { // Unpredictable (ARM ARM A5-10) |
| 1394 | vex_printf("dis_shift(arm): Unpredictable - Rd|Rm|Rn|Rs == R15\n"); |
cerion | a512a65 | 2004-12-10 11:43:10 +0000 | [diff] [blame] | 1395 | *decode_ok = False; |
| 1396 | return mkU32(0); |
cerion | f7da63d | 2004-12-09 19:04:57 +0000 | [diff] [blame] | 1397 | } |
| 1398 | |
cerion | 060c542 | 2004-12-10 15:28:43 +0000 | [diff] [blame] | 1399 | assign( Rs, getIReg((theInstr >> 8) & 0xF) ); // instr[11:8] |
cerion | f7da63d | 2004-12-09 19:04:57 +0000 | [diff] [blame] | 1400 | |
| 1401 | // shift_amt = shift_expr & 31 => Rs[5:0] |
cerion | 19e8a61 | 2004-12-10 10:18:58 +0000 | [diff] [blame] | 1402 | assign( shift_amt, |
| 1403 | narrowTo(Ity_I8, binop( Iop_And32, mkexpr(Rs), mkU32(0x1F)) ) ); |
cerion | 060c542 | 2004-12-10 15:28:43 +0000 | [diff] [blame] | 1404 | |
cerion | 19e8a61 | 2004-12-10 10:18:58 +0000 | [diff] [blame] | 1405 | // CAB TODO: support for >31 shift ? (Rs[7:0]) |
cerion | f7da63d | 2004-12-09 19:04:57 +0000 | [diff] [blame] | 1406 | |
| 1407 | switch (shift_op) { |
| 1408 | case 0x1: // LSL(reg) |
| 1409 | assign( mux_false, mkU32(0) ); |
cerion | 19e8a61 | 2004-12-10 10:18:58 +0000 | [diff] [blame] | 1410 | assign( carry_shift, binop(Iop_Add8, mkU8(32), mkexpr(shift_amt)) ); |
cerion | f7da63d | 2004-12-09 19:04:57 +0000 | [diff] [blame] | 1411 | break; |
cerion | 060c542 | 2004-12-10 15:28:43 +0000 | [diff] [blame] | 1412 | |
cerion | f7da63d | 2004-12-09 19:04:57 +0000 | [diff] [blame] | 1413 | case 0x3: // LSR(reg) |
| 1414 | assign( mux_false, mkU32(0) ); |
cerion | 19e8a61 | 2004-12-10 10:18:58 +0000 | [diff] [blame] | 1415 | assign( carry_shift, binop(Iop_Sub8, mkexpr(shift_amt), mkU8(1)) ); |
cerion | f7da63d | 2004-12-09 19:04:57 +0000 | [diff] [blame] | 1416 | break; |
cerion | 060c542 | 2004-12-10 15:28:43 +0000 | [diff] [blame] | 1417 | |
cerion | f7da63d | 2004-12-09 19:04:57 +0000 | [diff] [blame] | 1418 | case 0x5: // ASR(reg) |
| 1419 | // Rs[31] == 0 ? 0x0 : 0xFFFFFFFF |
| 1420 | assign( mux_false, |
| 1421 | IRExpr_Mux0X( |
| 1422 | binop(Iop_CmpLT32U, mkexpr(Rs), mkU32(0x80000000)), |
| 1423 | mkU32(0xFFFFFFFF), mkU32(0) ) ); |
| 1424 | assign( carry_shift, |
cerion | 19e8a61 | 2004-12-10 10:18:58 +0000 | [diff] [blame] | 1425 | binop(Iop_Sub8, mkexpr(shift_amt), mkU8(1)) ); |
cerion | f7da63d | 2004-12-09 19:04:57 +0000 | [diff] [blame] | 1426 | break; |
cerion | 060c542 | 2004-12-10 15:28:43 +0000 | [diff] [blame] | 1427 | |
cerion | 19e8a61 | 2004-12-10 10:18:58 +0000 | [diff] [blame] | 1428 | default: |
| 1429 | vex_printf("dis_shift(arm): Reg shift: No such case: 0x%x\n", shift_op); |
cerion | a512a65 | 2004-12-10 11:43:10 +0000 | [diff] [blame] | 1430 | *decode_ok = False; |
| 1431 | return mkU32(0); |
cerion | f7da63d | 2004-12-09 19:04:57 +0000 | [diff] [blame] | 1432 | } |
cerion | 19e8a61 | 2004-12-10 10:18:58 +0000 | [diff] [blame] | 1433 | |
| 1434 | expr = IRExpr_Mux0X( |
| 1435 | binop(Iop_CmpLT32U, widenUto32(mkexpr(shift_amt)), mkU32(32)), |
| 1436 | mkexpr(mux_false), |
| 1437 | binop(op, mkexpr(Rm), mkexpr(shift_amt)) ); |
cerion | f7da63d | 2004-12-09 19:04:57 +0000 | [diff] [blame] | 1438 | |
| 1439 | // shift_amt == 0 ? old_flag_c : Rm >> x |
| 1440 | assign( *carry_out, |
| 1441 | IRExpr_Mux0X( |
cerion | 19e8a61 | 2004-12-10 10:18:58 +0000 | [diff] [blame] | 1442 | binop(Iop_CmpEQ8, mkexpr(shift_amt), mkU8(0)), |
cerion | f7da63d | 2004-12-09 19:04:57 +0000 | [diff] [blame] | 1443 | binop(Iop_Shr32, mkexpr(Rm), mkexpr(carry_shift)), |
cerion | 19e8a61 | 2004-12-10 10:18:58 +0000 | [diff] [blame] | 1444 | mkexpr(oldFlagC) ) ); |
cerion | 397f9e5 | 2004-12-15 13:04:06 +0000 | [diff] [blame^] | 1445 | |
| 1446 | DIS(buf, "R%d, %s R%d", Rm_addr, name_ARMShiftOp(shift_op, 0), Rs_addr); |
cerion | a70a37b | 2004-12-03 18:54:08 +0000 | [diff] [blame] | 1447 | } |
| 1448 | else { // Immediate shift |
cerion | 397f9e5 | 2004-12-15 13:04:06 +0000 | [diff] [blame^] | 1449 | |
cerion | 060c542 | 2004-12-10 15:28:43 +0000 | [diff] [blame] | 1450 | // CAB: This right? |
| 1451 | // "the value used is the address of the current intruction plus 8" |
| 1452 | if (Rm_addr == 15 || Rn_addr == 15) { // ARM ARM A5-9 |
cerion | f7da63d | 2004-12-09 19:04:57 +0000 | [diff] [blame] | 1453 | assign( Rm, binop(Iop_Add32, getIReg(15), mkU32(8)) ); |
cerion | 060c542 | 2004-12-10 15:28:43 +0000 | [diff] [blame] | 1454 | } else { |
| 1455 | assign( Rm, getIReg(Rm_addr) ); |
cerion | f7da63d | 2004-12-09 19:04:57 +0000 | [diff] [blame] | 1456 | } |
cerion | c60c01e | 2004-12-02 20:19:22 +0000 | [diff] [blame] | 1457 | |
cerion | f7da63d | 2004-12-09 19:04:57 +0000 | [diff] [blame] | 1458 | if (shift_imm == 0) { |
| 1459 | switch (shift_op) { |
| 1460 | case 0x0: case 0x8: // LSL(imm) |
cerion | 19e8a61 | 2004-12-10 10:18:58 +0000 | [diff] [blame] | 1461 | expr = mkexpr(Rm); |
| 1462 | assign( *carry_out, mkexpr(oldFlagC) ); |
cerion | f7da63d | 2004-12-09 19:04:57 +0000 | [diff] [blame] | 1463 | break; |
cerion | 060c542 | 2004-12-10 15:28:43 +0000 | [diff] [blame] | 1464 | |
cerion | f7da63d | 2004-12-09 19:04:57 +0000 | [diff] [blame] | 1465 | case 0x2: case 0xA: // LSR(imm) |
| 1466 | expr = mkexpr(0); |
| 1467 | // Rm >> 31: carry = R[0] |
cerion | 19e8a61 | 2004-12-10 10:18:58 +0000 | [diff] [blame] | 1468 | assign( *carry_out, binop(Iop_Shr32, mkexpr(Rm), mkU8(31)) ); |
cerion | f7da63d | 2004-12-09 19:04:57 +0000 | [diff] [blame] | 1469 | break; |
cerion | 060c542 | 2004-12-10 15:28:43 +0000 | [diff] [blame] | 1470 | |
cerion | f7da63d | 2004-12-09 19:04:57 +0000 | [diff] [blame] | 1471 | case 0x4: case 0xC: // ASR(imm) |
| 1472 | // Rs[31] == 0 ? 0x0 : 0xFFFFFFFF |
| 1473 | expr = IRExpr_Mux0X( |
| 1474 | binop(Iop_CmpLT32U, mkexpr(Rs), mkU32(0x80000000)), |
| 1475 | mkU32(0xFFFFFFFF), mkU32(0) ); |
| 1476 | // Rm >> 31: carry = R[0] |
cerion | 19e8a61 | 2004-12-10 10:18:58 +0000 | [diff] [blame] | 1477 | assign( *carry_out, binop(Iop_Shr32, mkexpr(Rm), mkU8(31)) ); |
cerion | f7da63d | 2004-12-09 19:04:57 +0000 | [diff] [blame] | 1478 | break; |
cerion | 060c542 | 2004-12-10 15:28:43 +0000 | [diff] [blame] | 1479 | |
cerion | 19e8a61 | 2004-12-10 10:18:58 +0000 | [diff] [blame] | 1480 | default: |
| 1481 | vex_printf("dis_shift(arm): Imm shift: No such case: 0x%x\n", shift_op); |
cerion | a512a65 | 2004-12-10 11:43:10 +0000 | [diff] [blame] | 1482 | *decode_ok = False; |
| 1483 | return mkU32(0); |
cerion | f7da63d | 2004-12-09 19:04:57 +0000 | [diff] [blame] | 1484 | } |
cerion | 397f9e5 | 2004-12-15 13:04:06 +0000 | [diff] [blame^] | 1485 | DIS(buf, "R%d", Rm_addr); |
cerion | 19e8a61 | 2004-12-10 10:18:58 +0000 | [diff] [blame] | 1486 | } else { |
| 1487 | expr = binop(op, mkexpr(Rm), mkU8(shift_imm)); |
| 1488 | assign( *carry_out, binop(op, mkexpr(Rm), |
| 1489 | binop(Iop_Sub32, mkU32(shift_imm), mkU32(1)) ) ); |
cerion | 397f9e5 | 2004-12-15 13:04:06 +0000 | [diff] [blame^] | 1490 | |
| 1491 | DIS(buf, "R%d, %s #%d", Rm_addr, name_ARMShiftOp(shift_op, 0), shift_imm); |
cerion | f7da63d | 2004-12-09 19:04:57 +0000 | [diff] [blame] | 1492 | } |
cerion | a70a37b | 2004-12-03 18:54:08 +0000 | [diff] [blame] | 1493 | } |
cerion | a70a37b | 2004-12-03 18:54:08 +0000 | [diff] [blame] | 1494 | return expr; |
cerion | c60c01e | 2004-12-02 20:19:22 +0000 | [diff] [blame] | 1495 | } |
| 1496 | |
| 1497 | |
| 1498 | |
cerion | a70a37b | 2004-12-03 18:54:08 +0000 | [diff] [blame] | 1499 | |
cerion | c60c01e | 2004-12-02 20:19:22 +0000 | [diff] [blame] | 1500 | /* |
cerion | f7da63d | 2004-12-09 19:04:57 +0000 | [diff] [blame] | 1501 | ARMG_CC_OP_ROR |
| 1502 | ARM ARM A5-15,16,17 |
cerion | c60c01e | 2004-12-02 20:19:22 +0000 | [diff] [blame] | 1503 | */ |
cerion | f7da63d | 2004-12-09 19:04:57 +0000 | [diff] [blame] | 1504 | static |
cerion | 397f9e5 | 2004-12-15 13:04:06 +0000 | [diff] [blame^] | 1505 | IRExpr* dis_rotate ( Bool* decode_ok, UInt theInstr, IRTemp* carry_out, HChar* buf ) |
cerion | f7da63d | 2004-12-09 19:04:57 +0000 | [diff] [blame] | 1506 | { |
| 1507 | UChar Rn_addr = (theInstr >> 16) & 0xF; |
| 1508 | UChar Rd_addr = (theInstr >> 12) & 0xF; |
| 1509 | UChar Rs_addr = (theInstr >> 8) & 0xF; |
| 1510 | UChar Rm_addr = (theInstr >> 0) & 0xF; |
| 1511 | UChar by_reg = (theInstr >> 4) & 0x1; // instr[4] |
| 1512 | UInt rot_imm = (theInstr >> 7) & 0x1F; // instr[11:7] |
| 1513 | IRTemp Rm = newTemp(Ity_I32); |
| 1514 | IRTemp Rs = newTemp(Ity_I32); |
cerion | 19e8a61 | 2004-12-10 10:18:58 +0000 | [diff] [blame] | 1515 | IRTemp rot_amt = newTemp(Ity_I8); // Rs[7:0] |
cerion | f7da63d | 2004-12-09 19:04:57 +0000 | [diff] [blame] | 1516 | IRTemp oldFlagC = newTemp(Ity_I32); |
| 1517 | IRExpr* expr=0; |
| 1518 | |
cerion | dee0867 | 2004-12-14 12:08:09 +0000 | [diff] [blame] | 1519 | assign( oldFlagC, binop(Iop_Shr32, |
| 1520 | mk_armg_calculate_flags_c(), |
| 1521 | mkU8(ARMG_CC_SHIFT_C)) ); |
cerion | f7da63d | 2004-12-09 19:04:57 +0000 | [diff] [blame] | 1522 | |
| 1523 | if (by_reg) { // Register rotate |
cerion | 060c542 | 2004-12-10 15:28:43 +0000 | [diff] [blame] | 1524 | assign( Rm, getIReg(Rm_addr) ); |
| 1525 | |
| 1526 | if (Rd_addr == 15 || Rm_addr == 15 || |
| 1527 | Rn_addr == 15 || Rs_addr == 15) { // Unpredictable (ARM ARM A5-10) |
| 1528 | vex_printf("dis_rotate(arm): Unpredictable - Rd|Rm|Rn|Rs == R15\n"); |
cerion | a512a65 | 2004-12-10 11:43:10 +0000 | [diff] [blame] | 1529 | *decode_ok = False; |
| 1530 | return mkU32(0); |
cerion | f7da63d | 2004-12-09 19:04:57 +0000 | [diff] [blame] | 1531 | } |
| 1532 | |
| 1533 | assign( Rs, getIReg((theInstr >> 8) & 0xF) ); // instr[11:8] |
cerion | 19e8a61 | 2004-12-10 10:18:58 +0000 | [diff] [blame] | 1534 | // Rs[4:0] |
| 1535 | assign( rot_amt, narrowTo(Ity_I8, |
| 1536 | binop(Iop_And32, mkexpr(Rs), mkU32(0x1F))) ); |
cerion | f7da63d | 2004-12-09 19:04:57 +0000 | [diff] [blame] | 1537 | |
| 1538 | // CAB: This right? |
cerion | f7da63d | 2004-12-09 19:04:57 +0000 | [diff] [blame] | 1539 | // Rs[7:0] == 0 ? oldFlagC : (Rs[4:0] == 0 ? Rm >> 31 : Rm >> rot-1 ) |
cerion | f7da63d | 2004-12-09 19:04:57 +0000 | [diff] [blame] | 1540 | assign( *carry_out, |
| 1541 | IRExpr_Mux0X( |
| 1542 | binop(Iop_CmpNE32, mkU32(0), |
| 1543 | binop(Iop_And32, mkexpr(Rs), mkU32(0xFF))), |
cerion | dee0867 | 2004-12-14 12:08:09 +0000 | [diff] [blame] | 1544 | mkexpr(oldFlagC), |
cerion | f7da63d | 2004-12-09 19:04:57 +0000 | [diff] [blame] | 1545 | IRExpr_Mux0X( |
cerion | 19e8a61 | 2004-12-10 10:18:58 +0000 | [diff] [blame] | 1546 | binop(Iop_CmpEQ8, mkexpr(rot_amt), mkU8(0)), |
cerion | f7da63d | 2004-12-09 19:04:57 +0000 | [diff] [blame] | 1547 | binop(Iop_Shr32, mkexpr(Rm), |
cerion | 19e8a61 | 2004-12-10 10:18:58 +0000 | [diff] [blame] | 1548 | binop(Iop_Sub8, mkexpr(rot_amt), mkU8(1))), |
cerion | f7da63d | 2004-12-09 19:04:57 +0000 | [diff] [blame] | 1549 | binop(Iop_Shr32, mkexpr(Rm), |
cerion | 19e8a61 | 2004-12-10 10:18:58 +0000 | [diff] [blame] | 1550 | binop(Iop_Shr32, mkexpr(Rm), mkU8(31))) ) ) ); |
cerion | f7da63d | 2004-12-09 19:04:57 +0000 | [diff] [blame] | 1551 | |
| 1552 | |
| 1553 | /* expr = (dst0 >> rot_amt) | (dst0 << (wordsize-rot_amt)) */ |
cerion | dee0867 | 2004-12-14 12:08:09 +0000 | [diff] [blame] | 1554 | expr = binop(Iop_Or32, |
| 1555 | binop(Iop_Shr32, mkexpr(Rm), mkexpr(rot_amt)), |
| 1556 | binop(Iop_Shl32, mkexpr(Rm), |
| 1557 | binop(Iop_Sub8, mkU8(32), mkexpr(rot_amt)))); |
cerion | 397f9e5 | 2004-12-15 13:04:06 +0000 | [diff] [blame^] | 1558 | |
| 1559 | DIS(buf, "R%d, ror R%d", Rm_addr, Rs_addr); |
cerion | c60c01e | 2004-12-02 20:19:22 +0000 | [diff] [blame] | 1560 | } |
cerion | f7da63d | 2004-12-09 19:04:57 +0000 | [diff] [blame] | 1561 | else { // Immediate rotate |
| 1562 | |
cerion | 060c542 | 2004-12-10 15:28:43 +0000 | [diff] [blame] | 1563 | // CAB: This right? |
| 1564 | // "the value used is the address of the current intruction plus 8" |
| 1565 | if (Rm_addr == 15 || Rn_addr == 15) { // ARM ARM A5-9 |
| 1566 | assign( Rm, binop(Iop_Add32, getIReg(15), mkU32(8)) ); |
| 1567 | } else { |
| 1568 | assign( Rm, getIReg(Rm_addr) ); |
cerion | f7da63d | 2004-12-09 19:04:57 +0000 | [diff] [blame] | 1569 | } |
| 1570 | |
| 1571 | // Rm >> rot-1: carry = R[0] |
| 1572 | assign( *carry_out, binop(Iop_Shr32, mkexpr(Rm), |
cerion | 19e8a61 | 2004-12-10 10:18:58 +0000 | [diff] [blame] | 1573 | binop(Iop_Sub8, mkU8(rot_imm), mkU8(1)) ) ); |
cerion | f7da63d | 2004-12-09 19:04:57 +0000 | [diff] [blame] | 1574 | |
cerion | 060c542 | 2004-12-10 15:28:43 +0000 | [diff] [blame] | 1575 | if (rot_imm == 0) { // RRX (ARM ARM A5-17) |
cerion | f7da63d | 2004-12-09 19:04:57 +0000 | [diff] [blame] | 1576 | // 33 bit ROR using carry flag as the 33rd bit |
| 1577 | // op = Rm >> 1, carry flag replacing vacated bit position. |
| 1578 | |
| 1579 | // CAB: This right? |
cerion | f7da63d | 2004-12-09 19:04:57 +0000 | [diff] [blame] | 1580 | expr = binop(Iop_Or32, |
cerion | dee0867 | 2004-12-14 12:08:09 +0000 | [diff] [blame] | 1581 | binop(Iop_Shl32, mkexpr(oldFlagC), mkU8(31)), |
| 1582 | binop(Iop_Shr32, mkexpr(Rm), mkU8(1))); |
cerion | 397f9e5 | 2004-12-15 13:04:06 +0000 | [diff] [blame^] | 1583 | DIS(buf, "R%d, rrx", Rm_addr); |
cerion | f7da63d | 2004-12-09 19:04:57 +0000 | [diff] [blame] | 1584 | } else { |
cerion | f7da63d | 2004-12-09 19:04:57 +0000 | [diff] [blame] | 1585 | expr = binop(Iop_Or32, |
cerion | dee0867 | 2004-12-14 12:08:09 +0000 | [diff] [blame] | 1586 | binop(Iop_Shr32, mkexpr(Rm), mkU8(rot_imm)), |
| 1587 | binop(Iop_Shl32, mkexpr(Rm), |
| 1588 | binop(Iop_Sub8, mkU8(32), mkU8(rot_imm)))); |
cerion | 397f9e5 | 2004-12-15 13:04:06 +0000 | [diff] [blame^] | 1589 | |
| 1590 | DIS(buf, "R%d, ror #%d", Rm_addr, rot_imm); |
cerion | f7da63d | 2004-12-09 19:04:57 +0000 | [diff] [blame] | 1591 | } |
| 1592 | } |
| 1593 | return expr; |
cerion | c60c01e | 2004-12-02 20:19:22 +0000 | [diff] [blame] | 1594 | } |
| 1595 | |
| 1596 | |
| 1597 | |
| 1598 | |
cerion | f7da63d | 2004-12-09 19:04:57 +0000 | [diff] [blame] | 1599 | /* |
| 1600 | CAB TODO: |
| 1601 | - Not all shifts by 0 leave c_flag unchanged, so guard_expr is more difficult... |
| 1602 | assign( flags_guard, binop( Iop_CmpEQ32, mkexpr(shift_amt), mkU32(0) ) ); |
| 1603 | setFlags_DEP1_DEP2_shift( ARMG_CC_OP_LSL, Rm, shift_op, flags_guard ); |
| 1604 | */ |
cerion | c60c01e | 2004-12-02 20:19:22 +0000 | [diff] [blame] | 1605 | |
cerion | c60c01e | 2004-12-02 20:19:22 +0000 | [diff] [blame] | 1606 | |
cerion | f7da63d | 2004-12-09 19:04:57 +0000 | [diff] [blame] | 1607 | |
| 1608 | |
| 1609 | /* Addressing mode 1 - Data Processing ops |
| 1610 | General syntax: <opcode>{<cond>}{S} <Rd>, <Rn>, <shifter_operand> |
| 1611 | Returns <shifter_operand> expression |
cerion | c60c01e | 2004-12-02 20:19:22 +0000 | [diff] [blame] | 1612 | */ |
| 1613 | static |
cerion | 397f9e5 | 2004-12-15 13:04:06 +0000 | [diff] [blame^] | 1614 | IRExpr* dis_shifter_op ( Bool *decode_ok, UInt theInstr, IRTemp* carry_out, HChar* buf ) |
cerion | c60c01e | 2004-12-02 20:19:22 +0000 | [diff] [blame] | 1615 | { |
cerion | f7da63d | 2004-12-09 19:04:57 +0000 | [diff] [blame] | 1616 | UChar is_immed = (theInstr >> 25) & 1; // immediate / register shift |
| 1617 | UChar shift_op = (theInstr >> 4) & 0xF; // second byte |
cerion | a70a37b | 2004-12-03 18:54:08 +0000 | [diff] [blame] | 1618 | UInt immed_8, rot_imm; |
| 1619 | UInt imm; |
cerion | f7da63d | 2004-12-09 19:04:57 +0000 | [diff] [blame] | 1620 | IRTemp oldFlagC = newTemp(Ity_I32); |
cerion | c60c01e | 2004-12-02 20:19:22 +0000 | [diff] [blame] | 1621 | |
cerion | a70a37b | 2004-12-03 18:54:08 +0000 | [diff] [blame] | 1622 | if (is_immed) { // ARM ARM A5-2 |
| 1623 | immed_8 = theInstr & 0xFF; |
| 1624 | rot_imm = (theInstr >> 8) & 0xF; |
| 1625 | imm = immed_8 << (rot_imm << 1); |
cerion | f7da63d | 2004-12-09 19:04:57 +0000 | [diff] [blame] | 1626 | |
cerion | f7da63d | 2004-12-09 19:04:57 +0000 | [diff] [blame] | 1627 | if (rot_imm == 0) { |
cerion | dee0867 | 2004-12-14 12:08:09 +0000 | [diff] [blame] | 1628 | assign( oldFlagC, binop(Iop_Shr32, |
| 1629 | mk_armg_calculate_flags_c(), |
| 1630 | mkU8(ARMG_CC_SHIFT_C)) ); |
cerion | 19e8a61 | 2004-12-10 10:18:58 +0000 | [diff] [blame] | 1631 | assign( *carry_out, mkexpr(oldFlagC) ); |
cerion | f7da63d | 2004-12-09 19:04:57 +0000 | [diff] [blame] | 1632 | } else { |
cerion | 19e8a61 | 2004-12-10 10:18:58 +0000 | [diff] [blame] | 1633 | assign( *carry_out, binop(Iop_Shr32, mkU32(imm), mkU8(31)) ); |
cerion | f7da63d | 2004-12-09 19:04:57 +0000 | [diff] [blame] | 1634 | } |
cerion | 397f9e5 | 2004-12-15 13:04:06 +0000 | [diff] [blame^] | 1635 | DIS(buf, "#%d", imm); |
cerion | 19e8a61 | 2004-12-10 10:18:58 +0000 | [diff] [blame] | 1636 | return mkU32(imm); |
cerion | a70a37b | 2004-12-03 18:54:08 +0000 | [diff] [blame] | 1637 | } else { |
cerion | 19e8a61 | 2004-12-10 10:18:58 +0000 | [diff] [blame] | 1638 | |
cerion | f7da63d | 2004-12-09 19:04:57 +0000 | [diff] [blame] | 1639 | // We shouldn't have any 'op' with bits 4=1 and 7=1 : 1xx1 |
| 1640 | switch (shift_op) { |
| 1641 | case 0x0: case 0x8: case 0x1: |
| 1642 | case 0x2: case 0xA: case 0x3: |
cerion | a512a65 | 2004-12-10 11:43:10 +0000 | [diff] [blame] | 1643 | case 0x4: case 0xC: case 0x5: |
cerion | 397f9e5 | 2004-12-15 13:04:06 +0000 | [diff] [blame^] | 1644 | return dis_shift( decode_ok, theInstr, carry_out, buf ); |
cerion | a512a65 | 2004-12-10 11:43:10 +0000 | [diff] [blame] | 1645 | |
| 1646 | case 0x6: case 0xE: case 0x7: |
cerion | 397f9e5 | 2004-12-15 13:04:06 +0000 | [diff] [blame^] | 1647 | return dis_rotate( decode_ok, theInstr, carry_out, buf ); |
cerion | a512a65 | 2004-12-10 11:43:10 +0000 | [diff] [blame] | 1648 | |
cerion | f7da63d | 2004-12-09 19:04:57 +0000 | [diff] [blame] | 1649 | default: // Error: Any other value shouldn't be here. |
cerion | a512a65 | 2004-12-10 11:43:10 +0000 | [diff] [blame] | 1650 | *decode_ok = False; |
| 1651 | vex_printf("dis_shifter_op(arm): shift: No such case: 0x%x\n", shift_op); |
| 1652 | return mkU32(0); |
cerion | f7da63d | 2004-12-09 19:04:57 +0000 | [diff] [blame] | 1653 | } |
cerion | a70a37b | 2004-12-03 18:54:08 +0000 | [diff] [blame] | 1654 | } |
cerion | c60c01e | 2004-12-02 20:19:22 +0000 | [diff] [blame] | 1655 | } |
| 1656 | |
| 1657 | |
| 1658 | |
| 1659 | |
| 1660 | |
cerion | f7da63d | 2004-12-09 19:04:57 +0000 | [diff] [blame] | 1661 | /* -------------- Helper for DPI's. -------------- |
cerion | a70a37b | 2004-12-03 18:54:08 +0000 | [diff] [blame] | 1662 | */ |
cerion | a70a37b | 2004-12-03 18:54:08 +0000 | [diff] [blame] | 1663 | static |
cerion | a512a65 | 2004-12-10 11:43:10 +0000 | [diff] [blame] | 1664 | Bool dis_dataproc ( UInt theInstr ) |
cerion | a70a37b | 2004-12-03 18:54:08 +0000 | [diff] [blame] | 1665 | { |
cerion | f7da63d | 2004-12-09 19:04:57 +0000 | [diff] [blame] | 1666 | UChar opc = (theInstr >> 21) & 0xF; |
| 1667 | UChar set_flags = (theInstr >> 20) & 1; |
| 1668 | UChar Rn_addr = (theInstr >> 16) & 0xF; |
| 1669 | UChar Rd_addr = (theInstr >> 12) & 0xF; |
| 1670 | IRTemp Rn = newTemp(Ity_I32); |
| 1671 | IRTemp Rd = newTemp(Ity_I32); |
cerion | 060c542 | 2004-12-10 15:28:43 +0000 | [diff] [blame] | 1672 | IRTemp alu_out = newTemp(Ity_I32); |
cerion | f7da63d | 2004-12-09 19:04:57 +0000 | [diff] [blame] | 1673 | IRTemp shifter_op = newTemp(Ity_I32); |
| 1674 | IRTemp carry_out = newTemp(Ity_I32); |
cerion | 397f9e5 | 2004-12-15 13:04:06 +0000 | [diff] [blame^] | 1675 | IROp op_set_flags = ARMG_CC_OP_LOGIC; |
cerion | 060c542 | 2004-12-10 15:28:43 +0000 | [diff] [blame] | 1676 | Bool testing_instr = False; |
cerion | a512a65 | 2004-12-10 11:43:10 +0000 | [diff] [blame] | 1677 | Bool decode_ok = True; |
cerion | 397f9e5 | 2004-12-15 13:04:06 +0000 | [diff] [blame^] | 1678 | HChar* cond_name = name_ARMCondcode( (theInstr >> 28) & 0xF ); |
| 1679 | HChar* ch_set_flags = (set_flags == 1) ? "S" : ""; |
| 1680 | HChar dis_buf[50]; |
cerion | a70a37b | 2004-12-03 18:54:08 +0000 | [diff] [blame] | 1681 | |
cerion | 397f9e5 | 2004-12-15 13:04:06 +0000 | [diff] [blame^] | 1682 | assign( shifter_op, dis_shifter_op( &decode_ok, theInstr, &carry_out, dis_buf ) ); |
cerion | a512a65 | 2004-12-10 11:43:10 +0000 | [diff] [blame] | 1683 | if (!decode_ok) return False; |
| 1684 | |
cerion | f7da63d | 2004-12-09 19:04:57 +0000 | [diff] [blame] | 1685 | assign( Rd, getIReg(Rd_addr) ); |
| 1686 | assign( Rn, getIReg(Rn_addr) ); |
cerion | a70a37b | 2004-12-03 18:54:08 +0000 | [diff] [blame] | 1687 | |
cerion | 397f9e5 | 2004-12-15 13:04:06 +0000 | [diff] [blame^] | 1688 | |
| 1689 | switch (opc) { |
| 1690 | case 0x0: case 0x1: case 0x2: case 0x3: case 0x4: |
| 1691 | case 0xC: case 0xE: |
| 1692 | DIP("%s%s%s R%d, R%d, %s\n", name_ARMDataProcOp(opc), |
| 1693 | cond_name, ch_set_flags, Rd_addr, Rn_addr, dis_buf); |
| 1694 | break; |
| 1695 | case 0x5: case 0x6: case 0x7: |
| 1696 | // CAB: Unimplemented |
| 1697 | break; |
| 1698 | case 0x8: case 0x9: case 0xA: case 0xB: |
| 1699 | DIP("%s%s R%d, %s\n", name_ARMDataProcOp(opc), |
| 1700 | cond_name, Rn_addr, dis_buf); |
| 1701 | break; |
| 1702 | case 0xD: case 0xF: |
| 1703 | DIP("%s%s%s R%d, %s\n", name_ARMDataProcOp(opc), |
| 1704 | cond_name, ch_set_flags, Rd_addr, dis_buf); |
| 1705 | break; |
| 1706 | default:break; |
| 1707 | } |
| 1708 | |
| 1709 | |
cerion | f7da63d | 2004-12-09 19:04:57 +0000 | [diff] [blame] | 1710 | switch (opc) { |
| 1711 | case 0x0: // AND |
cerion | 060c542 | 2004-12-10 15:28:43 +0000 | [diff] [blame] | 1712 | assign( alu_out, binop(Iop_And32, getIReg(Rn_addr), mkexpr(shifter_op)) ); |
cerion | a70a37b | 2004-12-03 18:54:08 +0000 | [diff] [blame] | 1713 | break; |
| 1714 | |
cerion | f7da63d | 2004-12-09 19:04:57 +0000 | [diff] [blame] | 1715 | case 0x1: // EOR |
cerion | 060c542 | 2004-12-10 15:28:43 +0000 | [diff] [blame] | 1716 | assign( alu_out, binop(Iop_Xor32, getIReg(Rn_addr), mkexpr(shifter_op)) ); |
cerion | a70a37b | 2004-12-03 18:54:08 +0000 | [diff] [blame] | 1717 | break; |
| 1718 | |
cerion | f7da63d | 2004-12-09 19:04:57 +0000 | [diff] [blame] | 1719 | case 0x2: // SUB |
cerion | 060c542 | 2004-12-10 15:28:43 +0000 | [diff] [blame] | 1720 | assign( alu_out, binop( Iop_Sub32, getIReg(Rn_addr), mkexpr(shifter_op) ) ); |
cerion | 397f9e5 | 2004-12-15 13:04:06 +0000 | [diff] [blame^] | 1721 | op_set_flags = ARMG_CC_OP_SUB; |
cerion | a70a37b | 2004-12-03 18:54:08 +0000 | [diff] [blame] | 1722 | break; |
| 1723 | |
cerion | f7da63d | 2004-12-09 19:04:57 +0000 | [diff] [blame] | 1724 | case 0x3: // RSB |
cerion | 060c542 | 2004-12-10 15:28:43 +0000 | [diff] [blame] | 1725 | assign( alu_out, binop( Iop_Sub32, mkexpr(shifter_op), getIReg(Rn_addr) ) ); |
cerion | 397f9e5 | 2004-12-15 13:04:06 +0000 | [diff] [blame^] | 1726 | op_set_flags = ARMG_CC_OP_SUB; |
| 1727 | /* set_flags(), below, switches the args for this case */ |
cerion | f7da63d | 2004-12-09 19:04:57 +0000 | [diff] [blame] | 1728 | break; |
| 1729 | |
| 1730 | case 0x4: // ADD |
cerion | 060c542 | 2004-12-10 15:28:43 +0000 | [diff] [blame] | 1731 | assign( alu_out, binop( Iop_Add32, getIReg(Rn_addr), mkexpr(shifter_op) ) ); |
cerion | 397f9e5 | 2004-12-15 13:04:06 +0000 | [diff] [blame^] | 1732 | op_set_flags = ARMG_CC_OP_ADD; |
cerion | f7da63d | 2004-12-09 19:04:57 +0000 | [diff] [blame] | 1733 | break; |
| 1734 | |
cerion | 397f9e5 | 2004-12-15 13:04:06 +0000 | [diff] [blame^] | 1735 | case 0x5: // ADC // CAB: Unimplemented |
| 1736 | case 0x6: // SBC // CAB: Unimplemented |
| 1737 | case 0x7: // RSC // CAB: Unimplemented |
cerion | f7da63d | 2004-12-09 19:04:57 +0000 | [diff] [blame] | 1738 | goto decode_failure; |
| 1739 | |
| 1740 | case 0x8: // TST |
| 1741 | vassert(set_flags==1); |
cerion | 060c542 | 2004-12-10 15:28:43 +0000 | [diff] [blame] | 1742 | assign( alu_out, binop(Iop_And32, getIReg(Rn_addr), mkexpr(shifter_op)) ); |
| 1743 | testing_instr = True; |
cerion | f7da63d | 2004-12-09 19:04:57 +0000 | [diff] [blame] | 1744 | break; |
| 1745 | |
| 1746 | case 0x9: // TEQ |
| 1747 | vassert(set_flags==1); |
cerion | 060c542 | 2004-12-10 15:28:43 +0000 | [diff] [blame] | 1748 | assign( alu_out, binop(Iop_Xor32, getIReg(Rn_addr), mkexpr(shifter_op)) ); |
| 1749 | testing_instr = True; |
cerion | f7da63d | 2004-12-09 19:04:57 +0000 | [diff] [blame] | 1750 | break; |
| 1751 | |
| 1752 | case 0xA: // CMP |
| 1753 | vassert(set_flags==1); |
cerion | 397f9e5 | 2004-12-15 13:04:06 +0000 | [diff] [blame^] | 1754 | op_set_flags = ARMG_CC_OP_SUB; |
cerion | 060c542 | 2004-12-10 15:28:43 +0000 | [diff] [blame] | 1755 | testing_instr = True; |
cerion | f7da63d | 2004-12-09 19:04:57 +0000 | [diff] [blame] | 1756 | break; |
| 1757 | |
| 1758 | case 0xB: // CMN |
| 1759 | vassert(set_flags==1); |
cerion | 397f9e5 | 2004-12-15 13:04:06 +0000 | [diff] [blame^] | 1760 | op_set_flags = ARMG_CC_OP_ADD; |
cerion | 060c542 | 2004-12-10 15:28:43 +0000 | [diff] [blame] | 1761 | testing_instr = True; |
cerion | f7da63d | 2004-12-09 19:04:57 +0000 | [diff] [blame] | 1762 | break; |
| 1763 | |
| 1764 | case 0xC: // ORR |
cerion | 060c542 | 2004-12-10 15:28:43 +0000 | [diff] [blame] | 1765 | assign( alu_out, binop(Iop_Or32, getIReg(Rn_addr), mkexpr(shifter_op)) ); |
cerion | f7da63d | 2004-12-09 19:04:57 +0000 | [diff] [blame] | 1766 | break; |
| 1767 | |
| 1768 | case 0xD: // MOV |
cerion | 060c542 | 2004-12-10 15:28:43 +0000 | [diff] [blame] | 1769 | assign( alu_out, mkexpr(shifter_op) ); |
cerion | f7da63d | 2004-12-09 19:04:57 +0000 | [diff] [blame] | 1770 | break; |
| 1771 | |
| 1772 | case 0xE: // BIC |
cerion | 060c542 | 2004-12-10 15:28:43 +0000 | [diff] [blame] | 1773 | assign( alu_out, binop(Iop_And32, getIReg(Rn_addr), |
| 1774 | unop( Iop_Not32, mkexpr(shifter_op))) ); |
cerion | f7da63d | 2004-12-09 19:04:57 +0000 | [diff] [blame] | 1775 | break; |
| 1776 | |
| 1777 | case 0xF: // MVN |
cerion | 060c542 | 2004-12-10 15:28:43 +0000 | [diff] [blame] | 1778 | assign( alu_out, unop(Iop_Not32, mkexpr(shifter_op)) ); |
cerion | a70a37b | 2004-12-03 18:54:08 +0000 | [diff] [blame] | 1779 | break; |
| 1780 | |
| 1781 | default: |
cerion | f7da63d | 2004-12-09 19:04:57 +0000 | [diff] [blame] | 1782 | decode_failure: |
cerion | a512a65 | 2004-12-10 11:43:10 +0000 | [diff] [blame] | 1783 | vex_printf("dis_dataproc(arm): unhandled opcode: 0x%x\n", opc); |
| 1784 | return False; |
cerion | a70a37b | 2004-12-03 18:54:08 +0000 | [diff] [blame] | 1785 | } |
| 1786 | |
cerion | 060c542 | 2004-12-10 15:28:43 +0000 | [diff] [blame] | 1787 | if (!testing_instr) { |
| 1788 | if ( Rd_addr == 15) { // dest reg == PC |
cerion | f7da63d | 2004-12-09 19:04:57 +0000 | [diff] [blame] | 1789 | // CPSR = SPSR: Unpredictable in User | System mode (no SPSR!) |
cerion | 060c542 | 2004-12-10 15:28:43 +0000 | [diff] [blame] | 1790 | // Unpredictable - We're only supporting user mode... |
| 1791 | vex_printf("dis_dataproc(arm): Unpredictable - Rd_addr==15\n"); |
| 1792 | return False; |
| 1793 | } |
| 1794 | putIReg( Rd_addr, mkexpr(alu_out) ); |
| 1795 | } |
| 1796 | |
| 1797 | if (set_flags) { |
cerion | 397f9e5 | 2004-12-15 13:04:06 +0000 | [diff] [blame^] | 1798 | if (op_set_flags == ARMG_CC_OP_LOGIC) { |
| 1799 | setFlags_DEP1_DEP2( op_set_flags, alu_out, carry_out ); |
cerion | a70a37b | 2004-12-03 18:54:08 +0000 | [diff] [blame] | 1800 | } else { |
cerion | 397f9e5 | 2004-12-15 13:04:06 +0000 | [diff] [blame^] | 1801 | if (opc == 0x3) { |
| 1802 | setFlags_DEP1_DEP2( op_set_flags, shifter_op, Rn ); |
| 1803 | } else { |
| 1804 | setFlags_DEP1_DEP2( op_set_flags, Rn, shifter_op ); |
| 1805 | } |
cerion | a70a37b | 2004-12-03 18:54:08 +0000 | [diff] [blame] | 1806 | } |
| 1807 | } |
cerion | a512a65 | 2004-12-10 11:43:10 +0000 | [diff] [blame] | 1808 | return decode_ok; |
cerion | a70a37b | 2004-12-03 18:54:08 +0000 | [diff] [blame] | 1809 | } |
| 1810 | |
| 1811 | |
| 1812 | |
cerion | f7da63d | 2004-12-09 19:04:57 +0000 | [diff] [blame] | 1813 | |
| 1814 | /* -------------- Helper for Branch. -------------- |
| 1815 | */ |
| 1816 | static |
| 1817 | void dis_branch ( UInt theInstr ) |
| 1818 | { |
| 1819 | UChar link = (theInstr >> 24) & 1; |
cerion | 19e8a61 | 2004-12-10 10:18:58 +0000 | [diff] [blame] | 1820 | UInt signed_immed_24 = theInstr & 0xFFFFFF; |
| 1821 | UInt branch_offset; |
cerion | f7da63d | 2004-12-09 19:04:57 +0000 | [diff] [blame] | 1822 | IRTemp addr = newTemp(Ity_I32); |
| 1823 | |
cerion | 397f9e5 | 2004-12-15 13:04:06 +0000 | [diff] [blame^] | 1824 | DIP("b%c%s 0x%x\n", link ? 'l' : ' ', |
| 1825 | name_ARMCondcode( (theInstr >> 28) & 0xF ), |
| 1826 | signed_immed_24); |
| 1827 | |
cerion | f7da63d | 2004-12-09 19:04:57 +0000 | [diff] [blame] | 1828 | if (link) { // LR (R14) = addr of instr after branch instr |
cerion | 19e8a61 | 2004-12-10 10:18:58 +0000 | [diff] [blame] | 1829 | assign( addr, binop(Iop_Add32, getIReg(15), mkU32(4)) ); |
| 1830 | putIReg( 14, mkexpr(addr) ); |
cerion | f7da63d | 2004-12-09 19:04:57 +0000 | [diff] [blame] | 1831 | } |
| 1832 | |
| 1833 | // PC = PC + (SignExtend(signed_immed_24) << 2) |
cerion | 19e8a61 | 2004-12-10 10:18:58 +0000 | [diff] [blame] | 1834 | branch_offset = extend_s_24to32( signed_immed_24 ) << 2; |
cerion | f7da63d | 2004-12-09 19:04:57 +0000 | [diff] [blame] | 1835 | putIReg( 15, binop(Iop_Add32, getIReg(15), mkU32(branch_offset)) ); |
cerion | 19e8a61 | 2004-12-10 10:18:58 +0000 | [diff] [blame] | 1836 | |
| 1837 | irbb->jumpkind = link ? Ijk_Call : Ijk_Boring; |
| 1838 | irbb->next = mkU32(branch_offset); |
cerion | f7da63d | 2004-12-09 19:04:57 +0000 | [diff] [blame] | 1839 | } |
| 1840 | |
| 1841 | |
cerion | f7da63d | 2004-12-09 19:04:57 +0000 | [diff] [blame] | 1842 | |
| 1843 | |
cerion | a70a37b | 2004-12-03 18:54:08 +0000 | [diff] [blame] | 1844 | |
| 1845 | |
| 1846 | |
| 1847 | |
| 1848 | |
| 1849 | |
| 1850 | |
cerion | c60c01e | 2004-12-02 20:19:22 +0000 | [diff] [blame] | 1851 | |
| 1852 | |
| 1853 | |
sewardj | c2c8716 | 2004-11-25 13:07:02 +0000 | [diff] [blame] | 1854 | |
| 1855 | |
| 1856 | /*------------------------------------------------------------*/ |
| 1857 | /*--- Disassemble a single instruction ---*/ |
| 1858 | /*------------------------------------------------------------*/ |
| 1859 | |
| 1860 | /* Disassemble a single instruction into IR. The instruction |
| 1861 | is located in host memory at &guest_code[delta]. |
| 1862 | Set *size to be the size of the instruction. |
| 1863 | If the returned value is Dis_Resteer, |
| 1864 | the next guest address is assigned to *whereNext. If resteerOK |
| 1865 | is False, disInstr may not return Dis_Resteer. */ |
| 1866 | |
| 1867 | static DisResult disInstr ( /*IN*/ Bool resteerOK, |
| 1868 | /*IN*/ Bool (*resteerOkFn) ( Addr64 ), |
| 1869 | /*IN*/ UInt delta, |
| 1870 | /*OUT*/ UInt* size, |
| 1871 | /*OUT*/ Addr64* whereNext ) |
| 1872 | { |
sewardj | fb183d2 | 2004-12-03 11:55:29 +0000 | [diff] [blame] | 1873 | // IRType ty; |
| 1874 | // IRTemp addr, t1, t2; |
| 1875 | // Int alen; |
cerion | f7da63d | 2004-12-09 19:04:57 +0000 | [diff] [blame] | 1876 | UChar opc1, opc2, opc_tmp; //, modrm, abyte; |
cerion | c60c01e | 2004-12-02 20:19:22 +0000 | [diff] [blame] | 1877 | ARMCondcode cond; |
sewardj | fb183d2 | 2004-12-03 11:55:29 +0000 | [diff] [blame] | 1878 | // UInt d32; |
| 1879 | // UChar dis_buf[50]; |
| 1880 | // Int am_sz, d_sz; |
sewardj | c2c8716 | 2004-11-25 13:07:02 +0000 | [diff] [blame] | 1881 | DisResult whatNext = Dis_Continue; |
| 1882 | UInt theInstr; |
cerion | a512a65 | 2004-12-10 11:43:10 +0000 | [diff] [blame] | 1883 | |
sewardj | c2c8716 | 2004-11-25 13:07:02 +0000 | [diff] [blame] | 1884 | |
sewardj | c2c8716 | 2004-11-25 13:07:02 +0000 | [diff] [blame] | 1885 | /* At least this is simple on ARM: insns are all 4 bytes long, and |
| 1886 | 4-aligned. So just fish the whole thing out of memory right now |
| 1887 | and have done. */ |
| 1888 | |
| 1889 | /* We will set *size to 4 if the insn is successfully decoded. |
| 1890 | Setting it to 0 by default makes bbToIR_ARM abort if we fail the |
| 1891 | decode. */ |
| 1892 | *size = 0; |
| 1893 | |
| 1894 | theInstr = *(UInt*)(&guest_code[delta]); |
| 1895 | |
cerion | 397f9e5 | 2004-12-15 13:04:06 +0000 | [diff] [blame^] | 1896 | // vex_printf("START: 0x%x, %,b\n", theInstr, theInstr ); |
cerion | c60c01e | 2004-12-02 20:19:22 +0000 | [diff] [blame] | 1897 | |
sewardj | c2c8716 | 2004-11-25 13:07:02 +0000 | [diff] [blame] | 1898 | DIP("\t0x%x: ", guest_pc_bbstart+delta); |
| 1899 | |
cerion | c60c01e | 2004-12-02 20:19:22 +0000 | [diff] [blame] | 1900 | |
| 1901 | |
sewardj | c2c8716 | 2004-11-25 13:07:02 +0000 | [diff] [blame] | 1902 | // TODO: fix the client-request stuff, else nothing will work |
cerion | c60c01e | 2004-12-02 20:19:22 +0000 | [diff] [blame] | 1903 | |
sewardj | c2c8716 | 2004-11-25 13:07:02 +0000 | [diff] [blame] | 1904 | /* Spot the client-request magic sequence. */ |
cerion | c60c01e | 2004-12-02 20:19:22 +0000 | [diff] [blame] | 1905 | // Essentially a v. unlikely sequence of noops that we can catch |
sewardj | c2c8716 | 2004-11-25 13:07:02 +0000 | [diff] [blame] | 1906 | { |
sewardj | cca7194 | 2004-12-02 23:35:18 +0000 | [diff] [blame] | 1907 | UInt* code = (UInt*)(guest_code + delta); |
cerion | f7da63d | 2004-12-09 19:04:57 +0000 | [diff] [blame] | 1908 | |
| 1909 | // CAB: easy way to rotate left? |
| 1910 | |
| 1911 | /* Spot this: |
cerion | c60c01e | 2004-12-02 20:19:22 +0000 | [diff] [blame] | 1912 | E1A00EE0 mov r0, r0, ror #29 |
| 1913 | E1A001E0 mov r0, r0, ror #3 |
| 1914 | E1A00DE0 mov r0, r0, ror #27 |
| 1915 | E1A002E0 mov r0, r0, ror #5 |
| 1916 | E1A006E0 mov r0, r0, ror #13 |
| 1917 | E1A009E0 mov r0, r0, ror #19 |
sewardj | c2c8716 | 2004-11-25 13:07:02 +0000 | [diff] [blame] | 1918 | */ |
sewardj | 6cd9163 | 2004-12-02 23:36:20 +0000 | [diff] [blame] | 1919 | /* I suspect these will have to be turned the other way round to |
| 1920 | work on little-endian arm. */ |
sewardj | cca7194 | 2004-12-02 23:35:18 +0000 | [diff] [blame] | 1921 | if (code[0] == 0xE1A00EE0 && |
| 1922 | code[1] == 0xE1A001E0 && |
| 1923 | code[2] == 0xE1A00DE0 && |
| 1924 | code[3] == 0xE1A002E0 && |
| 1925 | code[4] == 0xE1A006E0 && |
| 1926 | code[5] == 0xE1A009E0) { |
| 1927 | |
| 1928 | // uh ... I'll figure this out later. possibly r0 = client_request(r0) */ |
cerion | c60c01e | 2004-12-02 20:19:22 +0000 | [diff] [blame] | 1929 | DIP("?CAB? = client_request ( ?CAB? )\n"); |
| 1930 | |
sewardj | cca7194 | 2004-12-02 23:35:18 +0000 | [diff] [blame] | 1931 | *size = 24; |
cerion | c60c01e | 2004-12-02 20:19:22 +0000 | [diff] [blame] | 1932 | |
cerion | c60c01e | 2004-12-02 20:19:22 +0000 | [diff] [blame] | 1933 | irbb->next = mkU32(guest_pc_bbstart+delta); |
| 1934 | irbb->jumpkind = Ijk_ClientReq; |
| 1935 | |
sewardj | c2c8716 | 2004-11-25 13:07:02 +0000 | [diff] [blame] | 1936 | whatNext = Dis_StopHere; |
| 1937 | goto decode_success; |
| 1938 | } |
| 1939 | } |
cerion | c60c01e | 2004-12-02 20:19:22 +0000 | [diff] [blame] | 1940 | |
| 1941 | |
| 1942 | |
| 1943 | |
cerion | f7da63d | 2004-12-09 19:04:57 +0000 | [diff] [blame] | 1944 | |
cerion | c60c01e | 2004-12-02 20:19:22 +0000 | [diff] [blame] | 1945 | /* |
| 1946 | Deal with condition first |
| 1947 | */ |
| 1948 | cond = (theInstr >> 28) & 0xF; /* opcode: bits 31:28 */ |
cerion | 397f9e5 | 2004-12-15 13:04:06 +0000 | [diff] [blame^] | 1949 | // vex_printf("\ndisInstr(arm): cond: 0x%x, %b\n", cond, cond ); |
cerion | fd7474a | 2004-12-03 11:16:42 +0000 | [diff] [blame] | 1950 | |
cerion | c60c01e | 2004-12-02 20:19:22 +0000 | [diff] [blame] | 1951 | switch (cond) { |
| 1952 | case 0xF: // => Illegal instruction prior to v5 (see ARM ARM A3-5) |
| 1953 | vex_printf("disInstr(arm): illegal condition\n"); |
| 1954 | goto decode_failure; |
| 1955 | |
| 1956 | case 0xE: // => Unconditional: go translate the instruction |
| 1957 | break; |
| 1958 | |
cerion | f7da63d | 2004-12-09 19:04:57 +0000 | [diff] [blame] | 1959 | default: |
| 1960 | // => Valid condition: translate the condition test first |
cerion | c60c01e | 2004-12-02 20:19:22 +0000 | [diff] [blame] | 1961 | stmt( IRStmt_Exit( mk_armg_calculate_condition(cond), |
| 1962 | Ijk_Boring, |
| 1963 | IRConst_U32(guest_pc_bbstart+delta+4) ) ); |
cerion | f7da63d | 2004-12-09 19:04:57 +0000 | [diff] [blame] | 1964 | //irbb->next = mkU32(guest_pc_bbstart+delta+4); |
| 1965 | //irbb->jumpkind = Ijk_Boring; |
cerion | c60c01e | 2004-12-02 20:19:22 +0000 | [diff] [blame] | 1966 | } |
| 1967 | |
| 1968 | |
| 1969 | |
cerion | f7da63d | 2004-12-09 19:04:57 +0000 | [diff] [blame] | 1970 | /* Primary opcode is roughly bits 27:20 (ARM ARM(v2) A3-2) |
| 1971 | secondary opcode is bits 4:0 */ |
| 1972 | opc1 = (theInstr >> 20) & 0xFF; /* opcode1: bits 27:20 */ |
| 1973 | opc2 = (theInstr >> 4 ) & 0xF; /* opcode2: bits 7:4 */ |
cerion | 397f9e5 | 2004-12-15 13:04:06 +0000 | [diff] [blame^] | 1974 | // vex_printf("disInstr(arm): opcode1: 0x%2x, %,09b\n", opc1, opc1 ); |
| 1975 | // vex_printf("disInstr(arm): opcode2: 0x%02x, %,04b\n", opc2, opc2 ); |
cerion | c60c01e | 2004-12-02 20:19:22 +0000 | [diff] [blame] | 1976 | |
cerion | f7da63d | 2004-12-09 19:04:57 +0000 | [diff] [blame] | 1977 | switch (opc1 >> 4) { // instr[27:24] |
| 1978 | case 0x0: |
| 1979 | case 0x1: |
| 1980 | /* |
| 1981 | Multiplies, extra load/store instructions: ARM ARM A3-3 |
cerion | c60c01e | 2004-12-02 20:19:22 +0000 | [diff] [blame] | 1982 | */ |
cerion | f7da63d | 2004-12-09 19:04:57 +0000 | [diff] [blame] | 1983 | if ( (opc1 & 0xE0) == 0x0 && (opc2 & 0x9) == 0x9 ) { // 000xxxxx && 1xx1 |
| 1984 | if (opc2 == 0x9) { |
| 1985 | if ((opc1 & 0x1C) == 0x00) { // multiply (accumulate) |
| 1986 | goto decode_failure; |
| 1987 | } |
| 1988 | if ((opc1 & 0x18) == 0x08) { // multiply (accumulate) long |
| 1989 | goto decode_failure; |
| 1990 | } |
| 1991 | if ((opc1 & 0x1B) == 0x10) { // swap/swap byte |
| 1992 | goto decode_failure; |
| 1993 | } |
| 1994 | } |
| 1995 | if ( opc2 == 0xB ) { |
| 1996 | if ((opc1 & 0x04) == 0x00) { // load/store 1/2word reg offset |
| 1997 | goto decode_failure; |
| 1998 | } else { // load/store 1/2word imm offset |
| 1999 | goto decode_failure; |
| 2000 | } |
| 2001 | } |
| 2002 | if ((opc2 & 0xD) == 0xD) { |
| 2003 | if ((opc1 & 0x05) == 0x00) { // load/store 2 words reg offset |
| 2004 | goto decode_failure; |
| 2005 | } |
| 2006 | if ((opc1 & 0x05) == 0x04) { // load/store 2 words imm offset |
| 2007 | goto decode_failure; |
| 2008 | } |
| 2009 | if ((opc1 & 0x05) == 0x01) { // load/store signed 1/2word/byte reg offset |
| 2010 | goto decode_failure; |
| 2011 | } |
| 2012 | if ((opc1 & 0x05) == 0x05) { // load/store signed 1/2word/byte imm offset |
| 2013 | goto decode_failure; |
| 2014 | } |
| 2015 | } |
| 2016 | } /* endif: Multiplies, extra load/store... */ |
cerion | c60c01e | 2004-12-02 20:19:22 +0000 | [diff] [blame] | 2017 | |
cerion | f7da63d | 2004-12-09 19:04:57 +0000 | [diff] [blame] | 2018 | /* |
| 2019 | 'Misc' Instructions: ARM ARM A3-4 |
| 2020 | */ |
| 2021 | if ((opc1 & 0xF9) == 0x10) { // 0001 0xx0 |
| 2022 | opc_tmp = (opc1 >> 1) & 0x3; |
| 2023 | switch (opc2) { |
| 2024 | case 0x0: |
| 2025 | if ((opc_tmp & 0x1) == 0x0) { // move stat reg -> reg |
| 2026 | goto decode_failure; |
| 2027 | } else { // move reg -> stat reg |
| 2028 | goto decode_failure; |
| 2029 | } |
cerion | c60c01e | 2004-12-02 20:19:22 +0000 | [diff] [blame] | 2030 | |
cerion | f7da63d | 2004-12-09 19:04:57 +0000 | [diff] [blame] | 2031 | case 0x1: |
| 2032 | if (opc_tmp == 0x1) { // branch/exchange instr set |
| 2033 | goto decode_failure; |
| 2034 | } |
| 2035 | if (opc_tmp == 0x3) { // count leading zeros |
| 2036 | goto decode_failure; |
| 2037 | } |
| 2038 | break; |
| 2039 | |
| 2040 | case 0x3: |
| 2041 | if (opc_tmp == 0x1) { // branch & link/exchange instr set |
| 2042 | goto decode_failure; |
| 2043 | } |
| 2044 | break; |
| 2045 | |
| 2046 | case 0x5: // enhanced dsp add/subtracts |
| 2047 | goto decode_failure; |
| 2048 | |
| 2049 | case 0x7: |
| 2050 | if (opc_tmp == 0x1) { // software breakpoint |
cerion | 060c542 | 2004-12-10 15:28:43 +0000 | [diff] [blame] | 2051 | if (cond != 0xE) { // Unpredictable - ARM ARM A3-4 |
| 2052 | vex_printf("disInstr(arm): Unpredictable instruction\n"); |
| 2053 | goto decode_failure; |
| 2054 | } |
cerion | f7da63d | 2004-12-09 19:04:57 +0000 | [diff] [blame] | 2055 | goto decode_failure; |
| 2056 | } |
| 2057 | break; |
| 2058 | |
| 2059 | case 0x8: case 0x9: case 0xA: // enhanced dsp multiplies |
| 2060 | case 0xB: case 0xC: case 0xD: case 0xE: |
| 2061 | goto decode_failure; |
| 2062 | |
| 2063 | default: break; |
| 2064 | } |
| 2065 | } /* endif: 'Misc' Instructions... */ |
| 2066 | // fall through... |
| 2067 | |
| 2068 | case 0x2: |
| 2069 | case 0x3: |
cerion | a512a65 | 2004-12-10 11:43:10 +0000 | [diff] [blame] | 2070 | if ((opc1 & 0xFB) == 0x30) goto decode_failure; // Undefined - ARM ARM A3-2 |
cerion | f7da63d | 2004-12-09 19:04:57 +0000 | [diff] [blame] | 2071 | |
| 2072 | /* |
| 2073 | A lonely 'MOV imm to status reg': |
cerion | c60c01e | 2004-12-02 20:19:22 +0000 | [diff] [blame] | 2074 | */ |
cerion | f7da63d | 2004-12-09 19:04:57 +0000 | [diff] [blame] | 2075 | if ((opc1 & 0xFB) == 0x32) { // 0011 0x10 |
| 2076 | goto decode_failure; |
| 2077 | } |
cerion | c60c01e | 2004-12-02 20:19:22 +0000 | [diff] [blame] | 2078 | |
cerion | f7da63d | 2004-12-09 19:04:57 +0000 | [diff] [blame] | 2079 | /* |
| 2080 | Data Processing Instructions |
cerion | 060c542 | 2004-12-10 15:28:43 +0000 | [diff] [blame] | 2081 | (if we get here, it's a dpi) |
cerion | c60c01e | 2004-12-02 20:19:22 +0000 | [diff] [blame] | 2082 | */ |
cerion | a512a65 | 2004-12-10 11:43:10 +0000 | [diff] [blame] | 2083 | if (!dis_dataproc( theInstr )) { goto decode_failure; } |
cerion | f7da63d | 2004-12-09 19:04:57 +0000 | [diff] [blame] | 2084 | break; |
| 2085 | |
| 2086 | |
| 2087 | /* |
| 2088 | Load/Store word | unsigned byte |
| 2089 | */ |
| 2090 | case 0x6: case 0x7: // LOAD/STORE reg offset |
cerion | a512a65 | 2004-12-10 11:43:10 +0000 | [diff] [blame] | 2091 | if ((opc2 & 0x1) == 0x1) goto decode_failure; // Undefined - ARM ARM A3-2 |
| 2092 | |
cerion | f7da63d | 2004-12-09 19:04:57 +0000 | [diff] [blame] | 2093 | case 0x4: case 0x5: // LOAD/STORE imm offset |
cerion | a512a65 | 2004-12-10 11:43:10 +0000 | [diff] [blame] | 2094 | if (!dis_loadstore_w_ub(theInstr)) { goto decode_failure; } |
cerion | f7da63d | 2004-12-09 19:04:57 +0000 | [diff] [blame] | 2095 | break; |
| 2096 | |
| 2097 | /* |
| 2098 | Load/Store multiple |
| 2099 | */ |
| 2100 | case 0x8: case 0x9: |
cerion | a512a65 | 2004-12-10 11:43:10 +0000 | [diff] [blame] | 2101 | if (!dis_loadstore_mult(theInstr)) { goto decode_failure; } |
cerion | f7da63d | 2004-12-09 19:04:57 +0000 | [diff] [blame] | 2102 | break; |
| 2103 | |
| 2104 | |
| 2105 | /* |
| 2106 | Branch, Branch and Link |
| 2107 | */ |
| 2108 | case 0xA: case 0xB: // B, BL |
| 2109 | // B(L): L=1 => return address stored in link register (R14) |
cerion | f7da63d | 2004-12-09 19:04:57 +0000 | [diff] [blame] | 2110 | dis_branch(theInstr); |
cerion | 19e8a61 | 2004-12-10 10:18:58 +0000 | [diff] [blame] | 2111 | whatNext = Dis_StopHere; |
cerion | f7da63d | 2004-12-09 19:04:57 +0000 | [diff] [blame] | 2112 | break; |
| 2113 | |
| 2114 | |
| 2115 | /* |
| 2116 | Co-processor instructions |
| 2117 | */ |
| 2118 | case 0xC: case 0xD: // co-pro load/store & double reg trxfrs |
cerion | c60c01e | 2004-12-02 20:19:22 +0000 | [diff] [blame] | 2119 | goto decode_failure; |
cerion | f7da63d | 2004-12-09 19:04:57 +0000 | [diff] [blame] | 2120 | |
| 2121 | case 0xE: |
| 2122 | if ((opc2 & 0x1) == 0x0) { // co-pro data processing |
| 2123 | goto decode_failure; |
| 2124 | } else { // co-pro register transfers |
| 2125 | goto decode_failure; |
| 2126 | } |
cerion | c60c01e | 2004-12-02 20:19:22 +0000 | [diff] [blame] | 2127 | |
cerion | a70a37b | 2004-12-03 18:54:08 +0000 | [diff] [blame] | 2128 | |
cerion | f7da63d | 2004-12-09 19:04:57 +0000 | [diff] [blame] | 2129 | /* |
| 2130 | Software Interrupt |
| 2131 | */ |
| 2132 | case 0xF: // swi |
| 2133 | goto decode_failure; |
cerion | a70a37b | 2004-12-03 18:54:08 +0000 | [diff] [blame] | 2134 | |
sewardj | c2c8716 | 2004-11-25 13:07:02 +0000 | [diff] [blame] | 2135 | default: |
| 2136 | decode_failure: |
| 2137 | /* All decode failures end up here. */ |
| 2138 | vex_printf("disInstr(arm): unhandled instruction: " |
cerion | fd7474a | 2004-12-03 11:16:42 +0000 | [diff] [blame] | 2139 | "0x%x\n", theInstr); |
sewardj | c2c8716 | 2004-11-25 13:07:02 +0000 | [diff] [blame] | 2140 | vpanic("armToIR: unimplemented insn"); |
| 2141 | |
| 2142 | } /* switch (opc) for the main (primary) opcode switch. */ |
| 2143 | |
| 2144 | decode_success: |
| 2145 | /* All decode successes end up here. */ |
cerion | 397f9e5 | 2004-12-15 13:04:06 +0000 | [diff] [blame^] | 2146 | // vex_printf("disInstr(arm): success"); |
sewardj | c2c8716 | 2004-11-25 13:07:02 +0000 | [diff] [blame] | 2147 | DIP("\n"); |
| 2148 | |
| 2149 | *size = 4; |
| 2150 | return whatNext; |
| 2151 | } |
| 2152 | |
| 2153 | #undef DIP |
| 2154 | #undef DIS |
| 2155 | |
| 2156 | /*--------------------------------------------------------------------*/ |
| 2157 | /*--- end guest-arm/toIR.c ---*/ |
| 2158 | /*--------------------------------------------------------------------*/ |