blob: 5808a63ac2483d70677a0cb33eb538c3286b74e5 [file] [log] [blame]
cerion896a1372005-01-25 12:24:25 +00001
2/*--------------------------------------------------------------------*/
3/*--- ---*/
ceriond0eae2d2005-12-23 11:43:01 +00004/*--- This file (guest-ppc/toIR.c) is ---*/
sewardjdbcfae72005-08-02 11:14:04 +00005/*--- Copyright (C) OpenWorks LLP. All rights reserved. ---*/
cerion896a1372005-01-25 12:24:25 +00006/*--- ---*/
7/*--------------------------------------------------------------------*/
8
9/*
10 This file is part of LibVEX, a library for dynamic binary
11 instrumentation and translation.
12
sewardja33e9a42006-06-05 23:13:19 +000013 Copyright (C) 2004-2006 OpenWorks LLP. All rights reserved.
cerion896a1372005-01-25 12:24:25 +000014
sewardj7bd6ffe2005-08-03 16:07:36 +000015 This library is made available under a dual licensing scheme.
cerion896a1372005-01-25 12:24:25 +000016
sewardj7bd6ffe2005-08-03 16:07:36 +000017 If you link LibVEX against other code all of which is itself
18 licensed under the GNU General Public License, version 2 dated June
19 1991 ("GPL v2"), then you may use LibVEX under the terms of the GPL
20 v2, as appearing in the file LICENSE.GPL. If the file LICENSE.GPL
21 is missing, you can obtain a copy of the GPL v2 from the Free
22 Software Foundation Inc., 51 Franklin St, Fifth Floor, Boston, MA
23 02110-1301, USA.
24
25 For any other uses of LibVEX, you must first obtain a commercial
26 license from OpenWorks LLP. Please contact info@open-works.co.uk
27 for information about commercial licensing.
28
29 This software is provided by OpenWorks LLP "as is" and any express
30 or implied warranties, including, but not limited to, the implied
31 warranties of merchantability and fitness for a particular purpose
32 are disclaimed. In no event shall OpenWorks LLP be liable for any
33 direct, indirect, incidental, special, exemplary, or consequential
34 damages (including, but not limited to, procurement of substitute
35 goods or services; loss of use, data, or profits; or business
36 interruption) however caused and on any theory of liability,
37 whether in contract, strict liability, or tort (including
38 negligence or otherwise) arising in any way out of the use of this
39 software, even if advised of the possibility of such damage.
cerion896a1372005-01-25 12:24:25 +000040
41 Neither the names of the U.S. Department of Energy nor the
42 University of California nor the names of its contributors may be
43 used to endorse or promote products derived from this software
44 without prior written permission.
cerion896a1372005-01-25 12:24:25 +000045*/
46
cerionedf7fc52005-11-18 20:57:41 +000047/* TODO 18/Nov/05:
sewardjb51f0f42005-07-18 11:38:02 +000048
sewardjcf8986c2006-01-18 04:14:52 +000049 Spot rld... cases which are simply left/right shifts and emit
50 Shl64/Shr64 accordingly.
sewardje14bb9f2005-07-22 09:39:02 +000051
cerionedf7fc52005-11-18 20:57:41 +000052 Altivec
53 - datastream insns
54 - lvxl,stvxl: load/store with 'least recently used' hint
55 - vexptefp, vlogefp
sewardj87e651f2005-09-09 08:31:18 +000056
57 LIMITATIONS:
58
59 Various, including:
60
61 - Some invalid forms of lswi and lswx are accepted when they should
62 not be.
63
cerionedf7fc52005-11-18 20:57:41 +000064 - Floating Point:
65 - All exceptions disabled in FPSCR
66 - condition codes not set in FPSCR
cerion76de5cf2005-11-18 18:25:12 +000067
cerionedf7fc52005-11-18 20:57:41 +000068 - Altivec floating point:
69 - vmaddfp, vnmsubfp
70 Because we're using Java/IEEE mode (FPSCR[NJ]), rather than the
71 system default of Non-Java mode, we get some small errors
72 (lowest bit only).
73 This is because Non-Java mode brutally hacks denormalised results
74 to zero, whereas we keep maximum accuracy. However, using
75 Non-Java mode would give us more inaccuracy, as our intermediate
76 results would then be zeroed, too.
sewardjcf8986c2006-01-18 04:14:52 +000077
sewardjaca070a2006-10-17 00:28:22 +000078 - AbiHints for the stack red zone are only emitted for
sewardjcf8986c2006-01-18 04:14:52 +000079 unconditional calls and returns (bl, blr). They should also be
80 emitted for conditional calls and returns, but we don't have a
81 way to express that right now. Ah well.
sewardjb51f0f42005-07-18 11:38:02 +000082*/
83
sewardjce02aa72006-01-12 12:27:58 +000084/* "Special" instructions.
85
sewardj09e88d12006-01-27 16:05:49 +000086 This instruction decoder can decode four special instructions
sewardjce02aa72006-01-12 12:27:58 +000087 which mean nothing natively (are no-ops as far as regs/mem are
88 concerned) but have meaning for supporting Valgrind. A special
sewardj1eb7e6b2006-01-12 21:13:14 +000089 instruction is flagged by a 16-byte preamble:
90
91 32-bit mode: 54001800 54006800 5400E800 54009800
92 (rlwinm 0,0,3,0,0; rlwinm 0,0,13,0,0;
93 rlwinm 0,0,29,0,0; rlwinm 0,0,19,0,0)
94
95 64-bit mode: 78001800 78006800 7800E802 78009802
96 (rotldi 0,0,3; rotldi 0,0,13;
97 rotldi 0,0,61; rotldi 0,0,51)
98
99 Following that, one of the following 3 are allowed
sewardjce02aa72006-01-12 12:27:58 +0000100 (standard interpretation in parentheses):
101
102 7C210B78 (or 1,1,1) %R3 = client_request ( %R4 )
103 7C421378 (or 2,2,2) %R3 = guest_NRADDR
104 7C631B78 (or 3,3,3) branch-and-link-to-noredir %R11
sewardjaca070a2006-10-17 00:28:22 +0000105 7C842378 (or 4,4,4) %R3 = guest_NRADDR_GPR2
sewardjce02aa72006-01-12 12:27:58 +0000106
107 Any other bytes following the 16-byte preamble are illegal and
108 constitute a failure in instruction decoding. This all assumes
109 that the preamble will never occur except in specific code
110 fragments designed for Valgrind to catch.
111*/
112
sewardjb51f0f42005-07-18 11:38:02 +0000113
cerion5b2325f2005-12-23 00:55:09 +0000114/* Translates PPC32/64 code to IR. */
cerion896a1372005-01-25 12:24:25 +0000115
cerion645c9302005-01-31 10:09:59 +0000116/* References
ceriona982c052005-06-28 17:23:09 +0000117
118#define PPC32
cerion645c9302005-01-31 10:09:59 +0000119 "PowerPC Microprocessor Family:
ceriond953ebb2005-11-29 13:27:20 +0000120 The Programming Environments Manual for 32-Bit Microprocessors"
cerione9d361a2005-03-04 17:35:29 +0000121 02/21/2000
122 http://www-3.ibm.com/chips/techlib/techlib.nsf/techdocs/852569B20050FF778525699600719DF2
123
ceriond953ebb2005-11-29 13:27:20 +0000124#define PPC64
125 "PowerPC Microprocessor Family:
126 Programming Environments Manual for 64-Bit Microprocessors"
127 06/10/2003
128 http://www-3.ibm.com/chips/techlib/techlib.nsf/techdocs/F7E732FF811F783187256FDD004D3797
129
ceriona982c052005-06-28 17:23:09 +0000130#define AV
131 "PowerPC Microprocessor Family:
132 AltiVec(TM) Technology Programming Environments Manual"
133 07/10/2003
134 http://www-3.ibm.com/chips/techlib/techlib.nsf/techdocs/FBFA164F824370F987256D6A006F424D
cerion645c9302005-01-31 10:09:59 +0000135*/
136
cerion896a1372005-01-25 12:24:25 +0000137#include "libvex_basictypes.h"
138#include "libvex_ir.h"
139#include "libvex.h"
cerion1515db92005-01-25 17:21:23 +0000140#include "libvex_guest_ppc32.h"
ceriond953ebb2005-11-29 13:27:20 +0000141#include "libvex_guest_ppc64.h"
cerion896a1372005-01-25 12:24:25 +0000142
143#include "main/vex_util.h"
144#include "main/vex_globals.h"
sewardj9e6491a2005-07-02 19:24:10 +0000145#include "guest-generic/bb_to_IR.h"
ceriond0eae2d2005-12-23 11:43:01 +0000146#include "guest-ppc/gdefs.h"
cerion896a1372005-01-25 12:24:25 +0000147
148
149/*------------------------------------------------------------*/
150/*--- Globals ---*/
151/*------------------------------------------------------------*/
152
sewardj9e6491a2005-07-02 19:24:10 +0000153/* These are set at the start of the translation of an insn, right
cerion5b2325f2005-12-23 00:55:09 +0000154 down in disInstr_PPC, so that we don't have to pass them around
sewardj9e6491a2005-07-02 19:24:10 +0000155 endlessly. They are all constant during the translation of any
156 given insn. */
cerion896a1372005-01-25 12:24:25 +0000157
cerioned623db2005-06-20 12:42:04 +0000158/* We need to know this to do sub-register accesses correctly. */
cerioned623db2005-06-20 12:42:04 +0000159static Bool host_is_bigendian;
160
cerion896a1372005-01-25 12:24:25 +0000161/* Pointer to the guest code area. */
cerion896a1372005-01-25 12:24:25 +0000162static UChar* guest_code;
163
164/* The guest address corresponding to guest_code[0]. */
ceriond953ebb2005-11-29 13:27:20 +0000165static Addr64 guest_CIA_bbstart;
cerion896a1372005-01-25 12:24:25 +0000166
sewardj01a9e802005-02-01 20:46:00 +0000167/* The guest address for the instruction currently being
168 translated. */
ceriond953ebb2005-11-29 13:27:20 +0000169static Addr64 guest_CIA_curr_instr;
sewardj01a9e802005-02-01 20:46:00 +0000170
cerion896a1372005-01-25 12:24:25 +0000171/* The IRBB* into which we're generating code. */
172static IRBB* irbb;
173
sewardj5df65bb2005-11-29 14:47:04 +0000174/* Is our guest binary 32 or 64bit? Set at each call to
cerion5b2325f2005-12-23 00:55:09 +0000175 disInstr_PPC below. */
sewardj5df65bb2005-11-29 14:47:04 +0000176static Bool mode64 = False;
ceriond953ebb2005-11-29 13:27:20 +0000177
cerion4c4f5ef2006-01-02 14:41:50 +0000178// Given a pointer to a function as obtained by "& functionname" in C,
179// produce a pointer to the actual entry point for the function. For
180// most platforms it's the identity function. Unfortunately, on
sewardjaca070a2006-10-17 00:28:22 +0000181// ppc64-linux it isn't (sigh) and ditto for ppc32-aix5 and
182// ppc64-aix5.
183static void* fnptr_to_fnentry( VexMiscInfo* vmi, void* f )
cerion4c4f5ef2006-01-02 14:41:50 +0000184{
sewardjaca070a2006-10-17 00:28:22 +0000185 if (vmi->host_ppc_calls_use_fndescrs) {
186 /* f is a pointer to a 3-word function descriptor, of which the
187 first word is the entry address. */
188 /* note, this is correct even with cross-jitting, since this is
189 purely a host issue, not a guest one. */
190 HWord* fdescr = (HWord*)f;
191 return (void*)(fdescr[0]);
192 } else {
193 /* Simple; "& f" points directly at the code for f. */
194 return f;
195 }
cerion4c4f5ef2006-01-02 14:41:50 +0000196}
197
cerion896a1372005-01-25 12:24:25 +0000198
199/*------------------------------------------------------------*/
200/*--- Debugging output ---*/
201/*------------------------------------------------------------*/
202
203#define DIP(format, args...) \
204 if (vex_traceflags & VEX_TRACE_FE) \
205 vex_printf(format, ## args)
206
207#define DIS(buf, format, args...) \
208 if (vex_traceflags & VEX_TRACE_FE) \
209 vex_sprintf(buf, format, ## args)
210
211
cerion896a1372005-01-25 12:24:25 +0000212/*------------------------------------------------------------*/
ceriond953ebb2005-11-29 13:27:20 +0000213/*--- Offsets of various parts of the ppc32/64 guest state ---*/
cerion896a1372005-01-25 12:24:25 +0000214/*------------------------------------------------------------*/
215
cerion5b2325f2005-12-23 00:55:09 +0000216#define offsetofPPCGuestState(_x) \
217 (mode64 ? offsetof(VexGuestPPC64State, _x) : \
218 offsetof(VexGuestPPC32State, _x))
cerion91ad5362005-01-27 23:02:41 +0000219
sewardjaca070a2006-10-17 00:28:22 +0000220#define OFFB_CIA offsetofPPCGuestState(guest_CIA)
221#define OFFB_CIA_AT_SC offsetofPPCGuestState(guest_CIA_AT_SC)
222#define OFFB_SPRG3_RO offsetofPPCGuestState(guest_SPRG3_RO)
223#define OFFB_LR offsetofPPCGuestState(guest_LR)
224#define OFFB_CTR offsetofPPCGuestState(guest_CTR)
225#define OFFB_XER_SO offsetofPPCGuestState(guest_XER_SO)
226#define OFFB_XER_OV offsetofPPCGuestState(guest_XER_OV)
227#define OFFB_XER_CA offsetofPPCGuestState(guest_XER_CA)
228#define OFFB_XER_BC offsetofPPCGuestState(guest_XER_BC)
229#define OFFB_FPROUND offsetofPPCGuestState(guest_FPROUND)
230#define OFFB_VRSAVE offsetofPPCGuestState(guest_VRSAVE)
231#define OFFB_VSCR offsetofPPCGuestState(guest_VSCR)
232#define OFFB_EMWARN offsetofPPCGuestState(guest_EMWARN)
233#define OFFB_TISTART offsetofPPCGuestState(guest_TISTART)
234#define OFFB_TILEN offsetofPPCGuestState(guest_TILEN)
235#define OFFB_RESVN offsetofPPCGuestState(guest_RESVN)
236#define OFFB_NRADDR offsetofPPCGuestState(guest_NRADDR)
237#define OFFB_NRADDR_GPR2 offsetofPPCGuestState(guest_NRADDR_GPR2)
sewardj5ff11dd2006-01-20 14:19:25 +0000238
cerion91ad5362005-01-27 23:02:41 +0000239
cerion38674602005-02-08 02:19:25 +0000240/*------------------------------------------------------------*/
sewardjb51f0f42005-07-18 11:38:02 +0000241/*--- Extract instruction fields --- */
cerion38674602005-02-08 02:19:25 +0000242/*------------------------------------------------------------*/
cerione9d361a2005-03-04 17:35:29 +0000243
cerion76de5cf2005-11-18 18:25:12 +0000244/* Extract field from insn, given idx (zero = lsb) and field length */
245#define IFIELD( insn, idx, len ) ((insn >> idx) & ((1<<len)-1))
246
sewardjb51f0f42005-07-18 11:38:02 +0000247/* Extract primary opcode, instr[31:26] */
cerion76de5cf2005-11-18 18:25:12 +0000248static UChar ifieldOPC( UInt instr ) {
249 return toUChar( IFIELD( instr, 26, 6 ) );
sewardjb51f0f42005-07-18 11:38:02 +0000250}
cerione9d361a2005-03-04 17:35:29 +0000251
cerion76de5cf2005-11-18 18:25:12 +0000252/* Extract 10-bit secondary opcode, instr[10:1] */
sewardjb51f0f42005-07-18 11:38:02 +0000253static UInt ifieldOPClo10 ( UInt instr) {
cerion76de5cf2005-11-18 18:25:12 +0000254 return IFIELD( instr, 1, 10 );
255}
256
257/* Extract 9-bit secondary opcode, instr[9:1] */
258static UInt ifieldOPClo9 ( UInt instr) {
259 return IFIELD( instr, 1, 9 );
260}
261
262/* Extract 5-bit secondary opcode, instr[5:1] */
263static UInt ifieldOPClo5 ( UInt instr) {
264 return IFIELD( instr, 1, 5 );
sewardjb51f0f42005-07-18 11:38:02 +0000265}
cerione9d361a2005-03-04 17:35:29 +0000266
sewardjb51f0f42005-07-18 11:38:02 +0000267/* Extract RD (destination register) field, instr[25:21] */
cerion76de5cf2005-11-18 18:25:12 +0000268static UChar ifieldRegDS( UInt instr ) {
269 return toUChar( IFIELD( instr, 21, 5 ) );
sewardjb51f0f42005-07-18 11:38:02 +0000270}
cerion094d1392005-06-20 13:45:57 +0000271
cerion76de5cf2005-11-18 18:25:12 +0000272/* Extract RA (1st source register) field, instr[20:16] */
273static UChar ifieldRegA ( UInt instr ) {
274 return toUChar( IFIELD( instr, 16, 5 ) );
sewardjb51f0f42005-07-18 11:38:02 +0000275}
276
cerion76de5cf2005-11-18 18:25:12 +0000277/* Extract RB (2nd source register) field, instr[15:11] */
278static UChar ifieldRegB ( UInt instr ) {
279 return toUChar( IFIELD( instr, 11, 5 ) );
sewardjb51f0f42005-07-18 11:38:02 +0000280}
281
cerion76de5cf2005-11-18 18:25:12 +0000282/* Extract RC (3rd source register) field, instr[10:6] */
283static UChar ifieldRegC ( UInt instr ) {
284 return toUChar( IFIELD( instr, 6, 5 ) );
sewardjb51f0f42005-07-18 11:38:02 +0000285}
286
cerion76de5cf2005-11-18 18:25:12 +0000287/* Extract 2nd lowest bit, instr[1] */
288static UChar ifieldBIT10 ( UInt instr ) {
289 return toUChar( IFIELD( instr, 10, 1 ) );
290}
291
292/* Extract 2nd lowest bit, instr[1] */
293static UChar ifieldBIT1 ( UInt instr ) {
294 return toUChar( IFIELD( instr, 1, 1 ) );
295}
296
297/* Extract lowest bit, instr[0] */
298static UChar ifieldBIT0 ( UInt instr ) {
299 return toUChar( instr & 0x1 );
300}
301
302/* Extract unsigned bottom half, instr[15:0] */
303static UInt ifieldUIMM16 ( UInt instr ) {
304 return instr & 0xFFFF;
305}
306
ceriond953ebb2005-11-29 13:27:20 +0000307/* Extract unsigned bottom 26 bits, instr[25:0] */
308static UInt ifieldUIMM26 ( UInt instr ) {
309 return instr & 0x3FFFFFF;
cerion76de5cf2005-11-18 18:25:12 +0000310}
311
sewardjb51f0f42005-07-18 11:38:02 +0000312
cerionedf7fc52005-11-18 20:57:41 +0000313/*------------------------------------------------------------*/
ceriond953ebb2005-11-29 13:27:20 +0000314/*--- Guest-state identifiers ---*/
cerionedf7fc52005-11-18 20:57:41 +0000315/*------------------------------------------------------------*/
sewardje14bb9f2005-07-22 09:39:02 +0000316
cerione9d361a2005-03-04 17:35:29 +0000317typedef enum {
ceriond953ebb2005-11-29 13:27:20 +0000318 PPC_GST_CIA, // Current Instruction Address
319 PPC_GST_LR, // Link Register
320 PPC_GST_CTR, // Count Register
321 PPC_GST_XER, // Overflow, carry flags, byte count
322 PPC_GST_CR, // Condition Register
323 PPC_GST_FPSCR, // Floating Point Status/Control Register
324 PPC_GST_VRSAVE, // Vector Save/Restore Register
325 PPC_GST_VSCR, // Vector Status and Control Register
326 PPC_GST_EMWARN, // Emulation warnings
327 PPC_GST_TISTART,// For icbi: start of area to invalidate
328 PPC_GST_TILEN, // For icbi: length of area to invalidate
329 PPC_GST_RESVN, // For lwarx/stwcx.
sewardjaca070a2006-10-17 00:28:22 +0000330 PPC_GST_CIA_AT_SC, // the CIA of the most recently executed SC insn
331 PPC_GST_SPRG3_RO, // SPRG3
ceriond953ebb2005-11-29 13:27:20 +0000332 PPC_GST_MAX
333} PPC_GST;
cerione9d361a2005-03-04 17:35:29 +0000334
cerionedf7fc52005-11-18 20:57:41 +0000335#define MASK_FPSCR_RN 0x3
336#define MASK_VSCR_VALID 0x00010001
sewardje14bb9f2005-07-22 09:39:02 +0000337
cerionedf7fc52005-11-18 20:57:41 +0000338
339/*------------------------------------------------------------*/
340/*--- FP Helpers ---*/
341/*------------------------------------------------------------*/
342
sewardj2ead5222005-11-23 03:53:45 +0000343/* Produce the 32-bit pattern corresponding to the supplied
344 float. */
345static UInt float_to_bits ( Float f )
346{
347 union { UInt i; Float f; } u;
348 vassert(4 == sizeof(UInt));
349 vassert(4 == sizeof(Float));
350 vassert(4 == sizeof(u));
351 u.f = f;
352 return u.i;
353}
354
cerion38674602005-02-08 02:19:25 +0000355
cerion38674602005-02-08 02:19:25 +0000356/*------------------------------------------------------------*/
357/*--- Misc Helpers ---*/
358/*------------------------------------------------------------*/
359
cerionf0de28c2005-12-13 20:21:11 +0000360/* Generate mask with 1's from 'begin' through 'end',
361 wrapping if begin > end.
362 begin->end works from right to left, 0=lsb
363*/
ceriond953ebb2005-11-29 13:27:20 +0000364static UInt MASK32( UInt begin, UInt end )
cerion38674602005-02-08 02:19:25 +0000365{
sewardj63327402006-01-25 03:26:27 +0000366 UInt m1, m2, mask;
ceriond953ebb2005-11-29 13:27:20 +0000367 vassert(begin < 32);
368 vassert(end < 32);
sewardj63327402006-01-25 03:26:27 +0000369 m1 = ((UInt)(-1)) << begin;
370 m2 = ((UInt)(-1)) << end << 1;
371 mask = m1 ^ m2;
cerionb85e8bb2005-02-16 08:54:33 +0000372 if (begin > end) mask = ~mask; // wrap mask
373 return mask;
cerion38674602005-02-08 02:19:25 +0000374}
375
cerion5b2325f2005-12-23 00:55:09 +0000376/* ditto for 64bit mask */
ceriond953ebb2005-11-29 13:27:20 +0000377static ULong MASK64( UInt begin, UInt end )
378{
sewardj63327402006-01-25 03:26:27 +0000379 ULong m1, m2, mask;
ceriond953ebb2005-11-29 13:27:20 +0000380 vassert(begin < 64);
381 vassert(end < 64);
sewardj63327402006-01-25 03:26:27 +0000382 m1 = ((ULong)(-1)) << begin;
383 m2 = ((ULong)(-1)) << end << 1;
384 mask = m1 ^ m2;
ceriond953ebb2005-11-29 13:27:20 +0000385 if (begin > end) mask = ~mask; // wrap mask
386 return mask;
387}
388
cerionf0de28c2005-12-13 20:21:11 +0000389static Addr64 nextInsnAddr( void )
390{
391 return guest_CIA_curr_instr + 4;
392}
ceriond953ebb2005-11-29 13:27:20 +0000393
cerion896a1372005-01-25 12:24:25 +0000394
cerion896a1372005-01-25 12:24:25 +0000395/*------------------------------------------------------------*/
396/*--- Helper bits and pieces for deconstructing the ---*/
ceriond953ebb2005-11-29 13:27:20 +0000397/*--- ppc32/64 insn stream. ---*/
cerion896a1372005-01-25 12:24:25 +0000398/*------------------------------------------------------------*/
399
400/* Add a statement to the list held by "irbb". */
401static void stmt ( IRStmt* st )
402{
403 addStmtToIRBB( irbb, st );
404}
405
cerion896a1372005-01-25 12:24:25 +0000406/* Generate a new temporary of the given type. */
407static IRTemp newTemp ( IRType ty )
408{
sewardj496a58d2005-03-20 18:44:44 +0000409 vassert(isPlausibleIRType(ty));
cerion896a1372005-01-25 12:24:25 +0000410 return newIRTemp( irbb->tyenv, ty );
411}
cerion896a1372005-01-25 12:24:25 +0000412
cerion32aad402005-09-10 12:02:24 +0000413/* Various simple conversions */
414
415static UChar extend_s_5to8 ( UChar x )
416{
417 return toUChar((((Int)x) << 27) >> 27);
418}
419
cerion92d9d872005-09-15 21:58:50 +0000420static UInt extend_s_8to32( UChar x )
421{
422 return (UInt)((((Int)x) << 24) >> 24);
423}
cerion91ad5362005-01-27 23:02:41 +0000424
cerion896a1372005-01-25 12:24:25 +0000425static UInt extend_s_16to32 ( UInt x )
426{
427 return (UInt)((((Int)x) << 16) >> 16);
428}
cerion896a1372005-01-25 12:24:25 +0000429
ceriond953ebb2005-11-29 13:27:20 +0000430static ULong extend_s_16to64 ( UInt x )
431{
432 return (ULong)((((Long)x) << 48) >> 48);
433}
434
435static ULong extend_s_26to64 ( UInt x )
436{
437 return (ULong)((((Long)x) << 38) >> 38);
438}
439
440static ULong extend_s_32to64 ( UInt x )
441{
442 return (ULong)((((Long)x) << 32) >> 32);
443}
444
sewardj684aa952005-01-30 12:52:14 +0000445/* Do a big-endian load of a 32-bit word, regardless of the endianness
446 of the underlying host. */
cerioncf004462005-01-31 15:24:55 +0000447static UInt getUIntBigendianly ( UChar* p )
sewardj684aa952005-01-30 12:52:14 +0000448{
cerioncf004462005-01-31 15:24:55 +0000449 UInt w = 0;
sewardj684aa952005-01-30 12:52:14 +0000450 w = (w << 8) | p[0];
451 w = (w << 8) | p[1];
452 w = (w << 8) | p[2];
453 w = (w << 8) | p[3];
454 return w;
455}
456
cerion896a1372005-01-25 12:24:25 +0000457
458/*------------------------------------------------------------*/
459/*--- Helpers for constructing IR. ---*/
460/*------------------------------------------------------------*/
461
cerion896a1372005-01-25 12:24:25 +0000462static void assign ( IRTemp dst, IRExpr* e )
463{
464 stmt( IRStmt_Tmp(dst, e) );
465}
466
cerionae694622005-01-28 17:52:47 +0000467static void storeBE ( IRExpr* addr, IRExpr* data )
cerion896a1372005-01-25 12:24:25 +0000468{
ceriond953ebb2005-11-29 13:27:20 +0000469 vassert(typeOfIRExpr(irbb->tyenv, addr) == Ity_I32 ||
470 typeOfIRExpr(irbb->tyenv, addr) == Ity_I64);
sewardjaf1ceca2005-06-30 23:31:27 +0000471 stmt( IRStmt_Store(Iend_BE,addr,data) );
cerion896a1372005-01-25 12:24:25 +0000472}
473
474static IRExpr* unop ( IROp op, IRExpr* a )
475{
476 return IRExpr_Unop(op, a);
477}
478
479static IRExpr* binop ( IROp op, IRExpr* a1, IRExpr* a2 )
480{
481 return IRExpr_Binop(op, a1, a2);
482}
483
sewardjb183b852006-02-03 16:08:03 +0000484static IRExpr* triop ( IROp op, IRExpr* a1, IRExpr* a2, IRExpr* a3 )
485{
486 return IRExpr_Triop(op, a1, a2, a3);
487}
488
sewardj40c80262006-02-08 19:30:46 +0000489static IRExpr* qop ( IROp op, IRExpr* a1, IRExpr* a2,
490 IRExpr* a3, IRExpr* a4 )
491{
492 return IRExpr_Qop(op, a1, a2, a3, a4);
493}
494
cerion896a1372005-01-25 12:24:25 +0000495static IRExpr* mkexpr ( IRTemp tmp )
496{
497 return IRExpr_Tmp(tmp);
498}
499
sewardj684c0372005-02-07 02:33:58 +0000500static IRExpr* mkU8 ( UChar i )
cerion896a1372005-01-25 12:24:25 +0000501{
cerion896a1372005-01-25 12:24:25 +0000502 return IRExpr_Const(IRConst_U8(i));
503}
cerion896a1372005-01-25 12:24:25 +0000504
cerion92d9d872005-09-15 21:58:50 +0000505static IRExpr* mkU16 ( UInt i )
506{
507 return IRExpr_Const(IRConst_U16(i));
508}
509
cerion896a1372005-01-25 12:24:25 +0000510static IRExpr* mkU32 ( UInt i )
511{
512 return IRExpr_Const(IRConst_U32(i));
513}
514
cerion4a49b032005-11-08 16:23:07 +0000515static IRExpr* mkU64 ( ULong i )
516{
517 return IRExpr_Const(IRConst_U64(i));
518}
519
cerionae694622005-01-28 17:52:47 +0000520static IRExpr* loadBE ( IRType ty, IRExpr* data )
cerion896a1372005-01-25 12:24:25 +0000521{
sewardjaf1ceca2005-06-30 23:31:27 +0000522 return IRExpr_Load(Iend_BE,ty,data);
cerion896a1372005-01-25 12:24:25 +0000523}
cerion896a1372005-01-25 12:24:25 +0000524
sewardj20ef5472005-07-21 14:48:31 +0000525static IRExpr* mkOR1 ( IRExpr* arg1, IRExpr* arg2 )
526{
527 vassert(typeOfIRExpr(irbb->tyenv, arg1) == Ity_I1);
528 vassert(typeOfIRExpr(irbb->tyenv, arg2) == Ity_I1);
cerion5b2325f2005-12-23 00:55:09 +0000529 return unop(Iop_32to1, binop(Iop_Or32, unop(Iop_1Uto32, arg1),
530 unop(Iop_1Uto32, arg2)));
sewardj20ef5472005-07-21 14:48:31 +0000531}
532
533static IRExpr* mkAND1 ( IRExpr* arg1, IRExpr* arg2 )
534{
535 vassert(typeOfIRExpr(irbb->tyenv, arg1) == Ity_I1);
536 vassert(typeOfIRExpr(irbb->tyenv, arg2) == Ity_I1);
cerion5b2325f2005-12-23 00:55:09 +0000537 return unop(Iop_32to1, binop(Iop_And32, unop(Iop_1Uto32, arg1),
538 unop(Iop_1Uto32, arg2)));
sewardj20ef5472005-07-21 14:48:31 +0000539}
sewardjb51f0f42005-07-18 11:38:02 +0000540
cerion4a49b032005-11-08 16:23:07 +0000541/* expand V128_8Ux16 to 2x V128_16Ux8's */
cerion5b2325f2005-12-23 00:55:09 +0000542static void expand8Ux16( IRExpr* vIn,
543 /*OUTs*/ IRTemp* vEvn, IRTemp* vOdd )
cerion4a49b032005-11-08 16:23:07 +0000544{
545 IRTemp ones8x16 = newTemp(Ity_V128);
546
547 vassert(typeOfIRExpr(irbb->tyenv, vIn) == Ity_V128);
548 vassert(vEvn && *vEvn == IRTemp_INVALID);
549 vassert(vOdd && *vOdd == IRTemp_INVALID);
550 *vEvn = newTemp(Ity_V128);
551 *vOdd = newTemp(Ity_V128);
552
553 assign( ones8x16, unop(Iop_Dup8x16, mkU8(0x1)) );
cerion24d06f12005-11-09 21:34:20 +0000554 assign( *vOdd, binop(Iop_MullEven8Ux16, mkexpr(ones8x16), vIn) );
555 assign( *vEvn, binop(Iop_MullEven8Ux16, mkexpr(ones8x16),
556 binop(Iop_ShrV128, vIn, mkU8(8))) );
cerion4a49b032005-11-08 16:23:07 +0000557}
558
559/* expand V128_8Sx16 to 2x V128_16Sx8's */
cerion5b2325f2005-12-23 00:55:09 +0000560static void expand8Sx16( IRExpr* vIn,
561 /*OUTs*/ IRTemp* vEvn, IRTemp* vOdd )
cerion4a49b032005-11-08 16:23:07 +0000562{
563 IRTemp ones8x16 = newTemp(Ity_V128);
564
565 vassert(typeOfIRExpr(irbb->tyenv, vIn) == Ity_V128);
566 vassert(vEvn && *vEvn == IRTemp_INVALID);
567 vassert(vOdd && *vOdd == IRTemp_INVALID);
568 *vEvn = newTemp(Ity_V128);
569 *vOdd = newTemp(Ity_V128);
570
571 assign( ones8x16, unop(Iop_Dup8x16, mkU8(0x1)) );
cerion24d06f12005-11-09 21:34:20 +0000572 assign( *vOdd, binop(Iop_MullEven8Sx16, mkexpr(ones8x16), vIn) );
573 assign( *vEvn, binop(Iop_MullEven8Sx16, mkexpr(ones8x16),
574 binop(Iop_ShrV128, vIn, mkU8(8))) );
cerion4a49b032005-11-08 16:23:07 +0000575}
576
577/* expand V128_16Uto8 to 2x V128_32Ux4's */
cerion5b2325f2005-12-23 00:55:09 +0000578static void expand16Ux8( IRExpr* vIn,
579 /*OUTs*/ IRTemp* vEvn, IRTemp* vOdd )
cerion4a49b032005-11-08 16:23:07 +0000580{
581 IRTemp ones16x8 = newTemp(Ity_V128);
582
583 vassert(typeOfIRExpr(irbb->tyenv, vIn) == Ity_V128);
584 vassert(vEvn && *vEvn == IRTemp_INVALID);
585 vassert(vOdd && *vOdd == IRTemp_INVALID);
586 *vEvn = newTemp(Ity_V128);
587 *vOdd = newTemp(Ity_V128);
588
589 assign( ones16x8, unop(Iop_Dup16x8, mkU16(0x1)) );
cerion24d06f12005-11-09 21:34:20 +0000590 assign( *vOdd, binop(Iop_MullEven16Ux8, mkexpr(ones16x8), vIn) );
591 assign( *vEvn, binop(Iop_MullEven16Ux8, mkexpr(ones16x8),
592 binop(Iop_ShrV128, vIn, mkU8(16))) );
cerion4a49b032005-11-08 16:23:07 +0000593}
594
595/* expand V128_16Sto8 to 2x V128_32Sx4's */
cerion5b2325f2005-12-23 00:55:09 +0000596static void expand16Sx8( IRExpr* vIn,
597 /*OUTs*/ IRTemp* vEvn, IRTemp* vOdd )
cerion4a49b032005-11-08 16:23:07 +0000598{
599 IRTemp ones16x8 = newTemp(Ity_V128);
600
601 vassert(typeOfIRExpr(irbb->tyenv, vIn) == Ity_V128);
602 vassert(vEvn && *vEvn == IRTemp_INVALID);
603 vassert(vOdd && *vOdd == IRTemp_INVALID);
604 *vEvn = newTemp(Ity_V128);
605 *vOdd = newTemp(Ity_V128);
606
607 assign( ones16x8, unop(Iop_Dup16x8, mkU16(0x1)) );
cerion24d06f12005-11-09 21:34:20 +0000608 assign( *vOdd, binop(Iop_MullEven16Sx8, mkexpr(ones16x8), vIn) );
609 assign( *vEvn, binop(Iop_MullEven16Sx8, mkexpr(ones16x8),
610 binop(Iop_ShrV128, vIn, mkU8(16))) );
cerion4a49b032005-11-08 16:23:07 +0000611}
612
613/* break V128 to 4xI32's, then sign-extend to I64's */
614static void breakV128to4x64S( IRExpr* t128,
615 /*OUTs*/
616 IRTemp* t3, IRTemp* t2,
617 IRTemp* t1, IRTemp* t0 )
618{
619 IRTemp hi64 = newTemp(Ity_I64);
620 IRTemp lo64 = newTemp(Ity_I64);
621
622 vassert(typeOfIRExpr(irbb->tyenv, t128) == Ity_V128);
623 vassert(t0 && *t0 == IRTemp_INVALID);
624 vassert(t1 && *t1 == IRTemp_INVALID);
625 vassert(t2 && *t2 == IRTemp_INVALID);
626 vassert(t3 && *t3 == IRTemp_INVALID);
627 *t0 = newTemp(Ity_I64);
628 *t1 = newTemp(Ity_I64);
629 *t2 = newTemp(Ity_I64);
630 *t3 = newTemp(Ity_I64);
631
632 assign( hi64, unop(Iop_V128HIto64, t128) );
633 assign( lo64, unop(Iop_V128to64, t128) );
634 assign( *t3, unop(Iop_32Sto64, unop(Iop_64HIto32, mkexpr(hi64))) );
635 assign( *t2, unop(Iop_32Sto64, unop(Iop_64to32, mkexpr(hi64))) );
636 assign( *t1, unop(Iop_32Sto64, unop(Iop_64HIto32, mkexpr(lo64))) );
637 assign( *t0, unop(Iop_32Sto64, unop(Iop_64to32, mkexpr(lo64))) );
638}
639
640/* break V128 to 4xI32's, then zero-extend to I64's */
641static void breakV128to4x64U ( IRExpr* t128,
642 /*OUTs*/
643 IRTemp* t3, IRTemp* t2,
644 IRTemp* t1, IRTemp* t0 )
645{
646 IRTemp hi64 = newTemp(Ity_I64);
647 IRTemp lo64 = newTemp(Ity_I64);
648
649 vassert(typeOfIRExpr(irbb->tyenv, t128) == Ity_V128);
650 vassert(t0 && *t0 == IRTemp_INVALID);
651 vassert(t1 && *t1 == IRTemp_INVALID);
652 vassert(t2 && *t2 == IRTemp_INVALID);
653 vassert(t3 && *t3 == IRTemp_INVALID);
654 *t0 = newTemp(Ity_I64);
655 *t1 = newTemp(Ity_I64);
656 *t2 = newTemp(Ity_I64);
657 *t3 = newTemp(Ity_I64);
658
659 assign( hi64, unop(Iop_V128HIto64, t128) );
660 assign( lo64, unop(Iop_V128to64, t128) );
661 assign( *t3, unop(Iop_32Uto64, unop(Iop_64HIto32, mkexpr(hi64))) );
662 assign( *t2, unop(Iop_32Uto64, unop(Iop_64to32, mkexpr(hi64))) );
663 assign( *t1, unop(Iop_32Uto64, unop(Iop_64HIto32, mkexpr(lo64))) );
664 assign( *t0, unop(Iop_32Uto64, unop(Iop_64to32, mkexpr(lo64))) );
665}
666
667/* Signed saturating narrow 64S to 32 */
668static IRExpr* mkQNarrow64Sto32 ( IRExpr* t64 )
669{
670 IRTemp hi32 = newTemp(Ity_I32);
671 IRTemp lo32 = newTemp(Ity_I32);
672
673 vassert(typeOfIRExpr(irbb->tyenv, t64) == Ity_I64);
674
675 assign( hi32, unop(Iop_64HIto32, t64));
676 assign( lo32, unop(Iop_64to32, t64));
677
678 return IRExpr_Mux0X(
679 /* if (hi32 == (lo32 >>s 31)) */
680 unop(Iop_1Uto8,
681 binop(Iop_CmpEQ32, mkexpr(hi32),
682 binop( Iop_Sar32, mkexpr(lo32), mkU8(31)))),
683 /* else: sign dep saturate: 1->0x80000000, 0->0x7FFFFFFF */
684 binop(Iop_Add32, mkU32(0x7FFFFFFF),
685 binop(Iop_Shr32, mkexpr(hi32), mkU8(31))),
686 /* then: within signed-32 range: lo half good enough */
687 mkexpr(lo32) );
688}
689
690/* Unsigned saturating narrow 64S to 32 */
691static IRExpr* mkQNarrow64Uto32 ( IRExpr* t64 )
692{
693 IRTemp hi32 = newTemp(Ity_I32);
694 IRTemp lo32 = newTemp(Ity_I32);
695
696 vassert(typeOfIRExpr(irbb->tyenv, t64) == Ity_I64);
697
698 assign( hi32, unop(Iop_64HIto32, t64));
699 assign( lo32, unop(Iop_64to32, t64));
700
701 return IRExpr_Mux0X(
702 /* if (top 32 bits of t64 are 0) */
703 unop(Iop_1Uto8, binop(Iop_CmpEQ32, mkexpr(hi32), mkU32(0))),
704 /* else: positive saturate -> 0xFFFFFFFF */
705 mkU32(0xFFFFFFFF),
706 /* then: within unsigned-32 range: lo half good enough */
707 mkexpr(lo32) );
708}
709
710/* Signed saturate narrow 64->32, combining to V128 */
711static IRExpr* mkV128from4x64S ( IRExpr* t3, IRExpr* t2,
712 IRExpr* t1, IRExpr* t0 )
713{
714 vassert(typeOfIRExpr(irbb->tyenv, t3) == Ity_I64);
715 vassert(typeOfIRExpr(irbb->tyenv, t2) == Ity_I64);
716 vassert(typeOfIRExpr(irbb->tyenv, t1) == Ity_I64);
717 vassert(typeOfIRExpr(irbb->tyenv, t0) == Ity_I64);
718 return binop(Iop_64HLtoV128,
719 binop(Iop_32HLto64,
720 mkQNarrow64Sto32( t3 ),
721 mkQNarrow64Sto32( t2 )),
722 binop(Iop_32HLto64,
723 mkQNarrow64Sto32( t1 ),
724 mkQNarrow64Sto32( t0 )));
725}
726
727/* Unsigned saturate narrow 64->32, combining to V128 */
728static IRExpr* mkV128from4x64U ( IRExpr* t3, IRExpr* t2,
729 IRExpr* t1, IRExpr* t0 )
730{
731 vassert(typeOfIRExpr(irbb->tyenv, t3) == Ity_I64);
732 vassert(typeOfIRExpr(irbb->tyenv, t2) == Ity_I64);
733 vassert(typeOfIRExpr(irbb->tyenv, t1) == Ity_I64);
734 vassert(typeOfIRExpr(irbb->tyenv, t0) == Ity_I64);
735 return binop(Iop_64HLtoV128,
736 binop(Iop_32HLto64,
737 mkQNarrow64Uto32( t3 ),
738 mkQNarrow64Uto32( t2 )),
739 binop(Iop_32HLto64,
740 mkQNarrow64Uto32( t1 ),
741 mkQNarrow64Uto32( t0 )));
742}
743
cerion24d06f12005-11-09 21:34:20 +0000744/* Simulate irops Iop_MullOdd*, since we don't have them */
745#define MK_Iop_MullOdd8Ux16( expr_vA, expr_vB ) \
746 binop(Iop_MullEven8Ux16, \
747 binop(Iop_ShrV128, expr_vA, mkU8(8)), \
748 binop(Iop_ShrV128, expr_vB, mkU8(8)))
749
750#define MK_Iop_MullOdd8Sx16( expr_vA, expr_vB ) \
751 binop(Iop_MullEven8Sx16, \
752 binop(Iop_ShrV128, expr_vA, mkU8(8)), \
753 binop(Iop_ShrV128, expr_vB, mkU8(8)))
754
755#define MK_Iop_MullOdd16Ux8( expr_vA, expr_vB ) \
756 binop(Iop_MullEven16Ux8, \
757 binop(Iop_ShrV128, expr_vA, mkU8(16)), \
758 binop(Iop_ShrV128, expr_vB, mkU8(16)))
759
760#define MK_Iop_MullOdd16Sx8( expr_vA, expr_vB ) \
761 binop(Iop_MullEven16Sx8, \
762 binop(Iop_ShrV128, expr_vA, mkU8(16)), \
763 binop(Iop_ShrV128, expr_vB, mkU8(16)))
764
cerion59b2c312005-12-17 11:28:53 +0000765static IRExpr* /* :: Ity_I64 */ mk64lo32Sto64 ( IRExpr* src )
ceriond953ebb2005-11-29 13:27:20 +0000766{
767 vassert(typeOfIRExpr(irbb->tyenv, src) == Ity_I64);
768 return unop(Iop_32Sto64, unop(Iop_64to32, src));
769}
770
cerion59b2c312005-12-17 11:28:53 +0000771static IRExpr* /* :: Ity_I64 */ mk64lo32Uto64 ( IRExpr* src )
cerionbb01b7c2005-12-16 13:40:18 +0000772{
773 vassert(typeOfIRExpr(irbb->tyenv, src) == Ity_I64);
774 return unop(Iop_32Uto64, unop(Iop_64to32, src));
775}
776
cerion2831b002005-11-30 19:55:22 +0000777static IROp mkSzOp ( IRType ty, IROp op8 )
ceriond953ebb2005-11-29 13:27:20 +0000778{
779 Int adj;
780 vassert(ty == Ity_I8 || ty == Ity_I16 ||
781 ty == Ity_I32 || ty == Ity_I64);
782 vassert(op8 == Iop_Add8 || op8 == Iop_Sub8 || op8 == Iop_Mul8 ||
783 op8 == Iop_Or8 || op8 == Iop_And8 || op8 == Iop_Xor8 ||
784 op8 == Iop_Shl8 || op8 == Iop_Shr8 || op8 == Iop_Sar8 ||
785 op8 == Iop_CmpEQ8 || op8 == Iop_CmpNE8 ||
786 op8 == Iop_Not8 || op8 == Iop_Neg8 );
787 adj = ty==Ity_I8 ? 0 : (ty==Ity_I16 ? 1 : (ty==Ity_I32 ? 2 : 3));
788 return adj + op8;
789}
790
cerion5b2325f2005-12-23 00:55:09 +0000791/* Make sure we get valid 32 and 64bit addresses */
cerion2831b002005-11-30 19:55:22 +0000792static Addr64 mkSzAddr ( IRType ty, Addr64 addr )
ceriond953ebb2005-11-29 13:27:20 +0000793{
794 vassert(ty == Ity_I32 || ty == Ity_I64);
795 return ( ty == Ity_I64 ?
796 (Addr64)addr :
797 (Addr64)extend_s_32to64( toUInt(addr) ) );
798}
799
800/* sz, ULong -> IRExpr */
cerion2831b002005-11-30 19:55:22 +0000801static IRExpr* mkSzImm ( IRType ty, ULong imm64 )
ceriond953ebb2005-11-29 13:27:20 +0000802{
803 vassert(ty == Ity_I32 || ty == Ity_I64);
804 return ty == Ity_I64 ? mkU64(imm64) : mkU32((UInt)imm64);
805}
806
807/* sz, ULong -> IRConst */
cerion2831b002005-11-30 19:55:22 +0000808static IRConst* mkSzConst ( IRType ty, ULong imm64 )
ceriond953ebb2005-11-29 13:27:20 +0000809{
810 vassert(ty == Ity_I32 || ty == Ity_I64);
811 return ( ty == Ity_I64 ?
812 IRConst_U64(imm64) :
813 IRConst_U32((UInt)imm64) );
814}
815
816/* Sign extend imm16 -> IRExpr* */
cerion2831b002005-11-30 19:55:22 +0000817static IRExpr* mkSzExtendS16 ( IRType ty, UInt imm16 )
ceriond953ebb2005-11-29 13:27:20 +0000818{
819 vassert(ty == Ity_I32 || ty == Ity_I64);
820 return ( ty == Ity_I64 ?
821 mkU64(extend_s_16to64(imm16)) :
822 mkU32(extend_s_16to32(imm16)) );
823}
824
825/* Sign extend imm32 -> IRExpr* */
cerion2831b002005-11-30 19:55:22 +0000826static IRExpr* mkSzExtendS32 ( IRType ty, UInt imm32 )
ceriond953ebb2005-11-29 13:27:20 +0000827{
828 vassert(ty == Ity_I32 || ty == Ity_I64);
829 return ( ty == Ity_I64 ?
830 mkU64(extend_s_32to64(imm32)) :
831 mkU32(imm32) );
832}
833
834/* IR narrows I32/I64 -> I8/I16/I32 */
cerion2831b002005-11-30 19:55:22 +0000835static IRExpr* mkSzNarrow8 ( IRType ty, IRExpr* src )
ceriond953ebb2005-11-29 13:27:20 +0000836{
837 vassert(ty == Ity_I32 || ty == Ity_I64);
838 return ty == Ity_I64 ? unop(Iop_64to8, src) : unop(Iop_32to8, src);
839}
840
cerion2831b002005-11-30 19:55:22 +0000841static IRExpr* mkSzNarrow16 ( IRType ty, IRExpr* src )
ceriond953ebb2005-11-29 13:27:20 +0000842{
843 vassert(ty == Ity_I32 || ty == Ity_I64);
844 return ty == Ity_I64 ? unop(Iop_64to16, src) : unop(Iop_32to16, src);
845}
846
cerion2831b002005-11-30 19:55:22 +0000847static IRExpr* mkSzNarrow32 ( IRType ty, IRExpr* src )
ceriond953ebb2005-11-29 13:27:20 +0000848{
849 vassert(ty == Ity_I32 || ty == Ity_I64);
850 return ty == Ity_I64 ? unop(Iop_64to32, src) : src;
851}
852
853/* Signed/Unsigned IR widens I8/I16/I32 -> I32/I64 */
cerion2831b002005-11-30 19:55:22 +0000854static IRExpr* mkSzWiden8 ( IRType ty, IRExpr* src, Bool sined )
ceriond953ebb2005-11-29 13:27:20 +0000855{
ceriond953ebb2005-11-29 13:27:20 +0000856 IROp op;
sewardj63327402006-01-25 03:26:27 +0000857 vassert(ty == Ity_I32 || ty == Ity_I64);
ceriond953ebb2005-11-29 13:27:20 +0000858 if (sined) op = (ty==Ity_I32) ? Iop_8Sto32 : Iop_8Sto64;
859 else op = (ty==Ity_I32) ? Iop_8Uto32 : Iop_8Uto64;
860 return unop(op, src);
861}
862
cerion2831b002005-11-30 19:55:22 +0000863static IRExpr* mkSzWiden16 ( IRType ty, IRExpr* src, Bool sined )
ceriond953ebb2005-11-29 13:27:20 +0000864{
ceriond953ebb2005-11-29 13:27:20 +0000865 IROp op;
sewardj63327402006-01-25 03:26:27 +0000866 vassert(ty == Ity_I32 || ty == Ity_I64);
ceriond953ebb2005-11-29 13:27:20 +0000867 if (sined) op = (ty==Ity_I32) ? Iop_16Sto32 : Iop_16Sto64;
868 else op = (ty==Ity_I32) ? Iop_16Uto32 : Iop_16Uto64;
869 return unop(op, src);
870}
871
cerion2831b002005-11-30 19:55:22 +0000872static IRExpr* mkSzWiden32 ( IRType ty, IRExpr* src, Bool sined )
ceriond953ebb2005-11-29 13:27:20 +0000873{
874 vassert(ty == Ity_I32 || ty == Ity_I64);
875 if (ty == Ity_I32)
876 return src;
877 return (sined) ? unop(Iop_32Sto64, src) : unop(Iop_32Uto64, src);
878}
cerion24d06f12005-11-09 21:34:20 +0000879
cerion4a49b032005-11-08 16:23:07 +0000880
sewardjb51f0f42005-07-18 11:38:02 +0000881static Int integerGuestRegOffset ( UInt archreg )
cerion45b70ff2005-01-31 17:03:25 +0000882{
sewardjb51f0f42005-07-18 11:38:02 +0000883 vassert(archreg < 32);
884
885 // jrs: probably not necessary; only matters if we reference sub-parts
cerion5b2325f2005-12-23 00:55:09 +0000886 // of the ppc registers, but that isn't the case
sewardjb51f0f42005-07-18 11:38:02 +0000887 // later: this might affect Altivec though?
888 vassert(host_is_bigendian);
889
cerion5b2325f2005-12-23 00:55:09 +0000890 switch (archreg) {
891 case 0: return offsetofPPCGuestState(guest_GPR0);
892 case 1: return offsetofPPCGuestState(guest_GPR1);
893 case 2: return offsetofPPCGuestState(guest_GPR2);
894 case 3: return offsetofPPCGuestState(guest_GPR3);
895 case 4: return offsetofPPCGuestState(guest_GPR4);
896 case 5: return offsetofPPCGuestState(guest_GPR5);
897 case 6: return offsetofPPCGuestState(guest_GPR6);
898 case 7: return offsetofPPCGuestState(guest_GPR7);
899 case 8: return offsetofPPCGuestState(guest_GPR8);
900 case 9: return offsetofPPCGuestState(guest_GPR9);
901 case 10: return offsetofPPCGuestState(guest_GPR10);
902 case 11: return offsetofPPCGuestState(guest_GPR11);
903 case 12: return offsetofPPCGuestState(guest_GPR12);
904 case 13: return offsetofPPCGuestState(guest_GPR13);
905 case 14: return offsetofPPCGuestState(guest_GPR14);
906 case 15: return offsetofPPCGuestState(guest_GPR15);
907 case 16: return offsetofPPCGuestState(guest_GPR16);
908 case 17: return offsetofPPCGuestState(guest_GPR17);
909 case 18: return offsetofPPCGuestState(guest_GPR18);
910 case 19: return offsetofPPCGuestState(guest_GPR19);
911 case 20: return offsetofPPCGuestState(guest_GPR20);
912 case 21: return offsetofPPCGuestState(guest_GPR21);
913 case 22: return offsetofPPCGuestState(guest_GPR22);
914 case 23: return offsetofPPCGuestState(guest_GPR23);
915 case 24: return offsetofPPCGuestState(guest_GPR24);
916 case 25: return offsetofPPCGuestState(guest_GPR25);
917 case 26: return offsetofPPCGuestState(guest_GPR26);
918 case 27: return offsetofPPCGuestState(guest_GPR27);
919 case 28: return offsetofPPCGuestState(guest_GPR28);
920 case 29: return offsetofPPCGuestState(guest_GPR29);
921 case 30: return offsetofPPCGuestState(guest_GPR30);
922 case 31: return offsetofPPCGuestState(guest_GPR31);
923 default: break;
sewardjb51f0f42005-07-18 11:38:02 +0000924 }
cerion5b2325f2005-12-23 00:55:09 +0000925 vpanic("integerGuestRegOffset(ppc,be)"); /*notreached*/
sewardjb51f0f42005-07-18 11:38:02 +0000926}
927
928static IRExpr* getIReg ( UInt archreg )
929{
ceriond953ebb2005-11-29 13:27:20 +0000930 IRType ty = mode64 ? Ity_I64 : Ity_I32;
sewardjb51f0f42005-07-18 11:38:02 +0000931 vassert(archreg < 32);
ceriond953ebb2005-11-29 13:27:20 +0000932 return IRExpr_Get( integerGuestRegOffset(archreg), ty );
sewardjb51f0f42005-07-18 11:38:02 +0000933}
934
935/* Ditto, but write to a reg instead. */
936static void putIReg ( UInt archreg, IRExpr* e )
937{
ceriond953ebb2005-11-29 13:27:20 +0000938 IRType ty = mode64 ? Ity_I64 : Ity_I32;
sewardjb51f0f42005-07-18 11:38:02 +0000939 vassert(archreg < 32);
ceriond953ebb2005-11-29 13:27:20 +0000940 vassert(typeOfIRExpr(irbb->tyenv, e) == ty );
sewardjb51f0f42005-07-18 11:38:02 +0000941 stmt( IRStmt_Put(integerGuestRegOffset(archreg), e) );
942}
943
944
945static Int floatGuestRegOffset ( UInt archreg )
946{
947 vassert(archreg < 32);
948
cerion5b2325f2005-12-23 00:55:09 +0000949 switch (archreg) {
950 case 0: return offsetofPPCGuestState(guest_FPR0);
951 case 1: return offsetofPPCGuestState(guest_FPR1);
952 case 2: return offsetofPPCGuestState(guest_FPR2);
953 case 3: return offsetofPPCGuestState(guest_FPR3);
954 case 4: return offsetofPPCGuestState(guest_FPR4);
955 case 5: return offsetofPPCGuestState(guest_FPR5);
956 case 6: return offsetofPPCGuestState(guest_FPR6);
957 case 7: return offsetofPPCGuestState(guest_FPR7);
958 case 8: return offsetofPPCGuestState(guest_FPR8);
959 case 9: return offsetofPPCGuestState(guest_FPR9);
960 case 10: return offsetofPPCGuestState(guest_FPR10);
961 case 11: return offsetofPPCGuestState(guest_FPR11);
962 case 12: return offsetofPPCGuestState(guest_FPR12);
963 case 13: return offsetofPPCGuestState(guest_FPR13);
964 case 14: return offsetofPPCGuestState(guest_FPR14);
965 case 15: return offsetofPPCGuestState(guest_FPR15);
966 case 16: return offsetofPPCGuestState(guest_FPR16);
967 case 17: return offsetofPPCGuestState(guest_FPR17);
968 case 18: return offsetofPPCGuestState(guest_FPR18);
969 case 19: return offsetofPPCGuestState(guest_FPR19);
970 case 20: return offsetofPPCGuestState(guest_FPR20);
971 case 21: return offsetofPPCGuestState(guest_FPR21);
972 case 22: return offsetofPPCGuestState(guest_FPR22);
973 case 23: return offsetofPPCGuestState(guest_FPR23);
974 case 24: return offsetofPPCGuestState(guest_FPR24);
975 case 25: return offsetofPPCGuestState(guest_FPR25);
976 case 26: return offsetofPPCGuestState(guest_FPR26);
977 case 27: return offsetofPPCGuestState(guest_FPR27);
978 case 28: return offsetofPPCGuestState(guest_FPR28);
979 case 29: return offsetofPPCGuestState(guest_FPR29);
980 case 30: return offsetofPPCGuestState(guest_FPR30);
981 case 31: return offsetofPPCGuestState(guest_FPR31);
982 default: break;
sewardjb51f0f42005-07-18 11:38:02 +0000983 }
cerion5b2325f2005-12-23 00:55:09 +0000984 vpanic("floatGuestRegOffset(ppc)"); /*notreached*/
sewardjb51f0f42005-07-18 11:38:02 +0000985}
986
987static IRExpr* getFReg ( UInt archreg )
988{
989 vassert(archreg < 32);
990 return IRExpr_Get( floatGuestRegOffset(archreg), Ity_F64 );
991}
992
993/* Ditto, but write to a reg instead. */
994static void putFReg ( UInt archreg, IRExpr* e )
995{
996 vassert(archreg < 32);
997 vassert(typeOfIRExpr(irbb->tyenv, e) == Ity_F64);
998 stmt( IRStmt_Put(floatGuestRegOffset(archreg), e) );
999}
1000
1001
1002static Int vectorGuestRegOffset ( UInt archreg )
1003{
1004 vassert(archreg < 32);
1005
cerion5b2325f2005-12-23 00:55:09 +00001006 switch (archreg) {
1007 case 0: return offsetofPPCGuestState(guest_VR0);
1008 case 1: return offsetofPPCGuestState(guest_VR1);
1009 case 2: return offsetofPPCGuestState(guest_VR2);
1010 case 3: return offsetofPPCGuestState(guest_VR3);
1011 case 4: return offsetofPPCGuestState(guest_VR4);
1012 case 5: return offsetofPPCGuestState(guest_VR5);
1013 case 6: return offsetofPPCGuestState(guest_VR6);
1014 case 7: return offsetofPPCGuestState(guest_VR7);
1015 case 8: return offsetofPPCGuestState(guest_VR8);
1016 case 9: return offsetofPPCGuestState(guest_VR9);
1017 case 10: return offsetofPPCGuestState(guest_VR10);
1018 case 11: return offsetofPPCGuestState(guest_VR11);
1019 case 12: return offsetofPPCGuestState(guest_VR12);
1020 case 13: return offsetofPPCGuestState(guest_VR13);
1021 case 14: return offsetofPPCGuestState(guest_VR14);
1022 case 15: return offsetofPPCGuestState(guest_VR15);
1023 case 16: return offsetofPPCGuestState(guest_VR16);
1024 case 17: return offsetofPPCGuestState(guest_VR17);
1025 case 18: return offsetofPPCGuestState(guest_VR18);
1026 case 19: return offsetofPPCGuestState(guest_VR19);
1027 case 20: return offsetofPPCGuestState(guest_VR20);
1028 case 21: return offsetofPPCGuestState(guest_VR21);
1029 case 22: return offsetofPPCGuestState(guest_VR22);
1030 case 23: return offsetofPPCGuestState(guest_VR23);
1031 case 24: return offsetofPPCGuestState(guest_VR24);
1032 case 25: return offsetofPPCGuestState(guest_VR25);
1033 case 26: return offsetofPPCGuestState(guest_VR26);
1034 case 27: return offsetofPPCGuestState(guest_VR27);
1035 case 28: return offsetofPPCGuestState(guest_VR28);
1036 case 29: return offsetofPPCGuestState(guest_VR29);
1037 case 30: return offsetofPPCGuestState(guest_VR30);
1038 case 31: return offsetofPPCGuestState(guest_VR31);
1039 default: break;
sewardjb51f0f42005-07-18 11:38:02 +00001040 }
cerion5b2325f2005-12-23 00:55:09 +00001041 vpanic("vextorGuestRegOffset(ppc)"); /*notreached*/
sewardjb51f0f42005-07-18 11:38:02 +00001042}
1043
1044static IRExpr* getVReg ( UInt archreg )
1045{
1046 vassert(archreg < 32);
1047 return IRExpr_Get( vectorGuestRegOffset(archreg), Ity_V128 );
1048}
1049
1050/* Ditto, but write to a reg instead. */
1051static void putVReg ( UInt archreg, IRExpr* e )
1052{
1053 vassert(archreg < 32);
1054 vassert(typeOfIRExpr(irbb->tyenv, e) == Ity_V128);
1055 stmt( IRStmt_Put(vectorGuestRegOffset(archreg), e) );
1056}
1057
1058static Int guestCR321offset ( UInt cr )
1059{
cerion5b2325f2005-12-23 00:55:09 +00001060 switch (cr) {
1061 case 0: return offsetofPPCGuestState(guest_CR0_321 );
1062 case 1: return offsetofPPCGuestState(guest_CR1_321 );
1063 case 2: return offsetofPPCGuestState(guest_CR2_321 );
1064 case 3: return offsetofPPCGuestState(guest_CR3_321 );
1065 case 4: return offsetofPPCGuestState(guest_CR4_321 );
1066 case 5: return offsetofPPCGuestState(guest_CR5_321 );
1067 case 6: return offsetofPPCGuestState(guest_CR6_321 );
1068 case 7: return offsetofPPCGuestState(guest_CR7_321 );
1069 default: vpanic("guestCR321offset(ppc)");
sewardjb51f0f42005-07-18 11:38:02 +00001070 }
1071}
1072
1073static Int guestCR0offset ( UInt cr )
1074{
cerion5b2325f2005-12-23 00:55:09 +00001075 switch (cr) {
1076 case 0: return offsetofPPCGuestState(guest_CR0_0 );
1077 case 1: return offsetofPPCGuestState(guest_CR1_0 );
1078 case 2: return offsetofPPCGuestState(guest_CR2_0 );
1079 case 3: return offsetofPPCGuestState(guest_CR3_0 );
1080 case 4: return offsetofPPCGuestState(guest_CR4_0 );
1081 case 5: return offsetofPPCGuestState(guest_CR5_0 );
1082 case 6: return offsetofPPCGuestState(guest_CR6_0 );
1083 case 7: return offsetofPPCGuestState(guest_CR7_0 );
1084 default: vpanic("guestCR3offset(ppc)");
sewardjb51f0f42005-07-18 11:38:02 +00001085 }
sewardjb51f0f42005-07-18 11:38:02 +00001086}
1087
cerion07b07a92005-12-22 14:32:35 +00001088// ROTL(src32/64, rot_amt5/6)
ceriond953ebb2005-11-29 13:27:20 +00001089static IRExpr* /* :: Ity_I32/64 */ ROTL ( IRExpr* src,
1090 IRExpr* rot_amt )
sewardjb51f0f42005-07-18 11:38:02 +00001091{
ceriond953ebb2005-11-29 13:27:20 +00001092 IRExpr *mask, *rot;
cerionf0de28c2005-12-13 20:21:11 +00001093 vassert(typeOfIRExpr(irbb->tyenv,rot_amt) == Ity_I8);
sewardjb51f0f42005-07-18 11:38:02 +00001094
cerionf0de28c2005-12-13 20:21:11 +00001095 if (typeOfIRExpr(irbb->tyenv,src) == Ity_I64) {
ceriond953ebb2005-11-29 13:27:20 +00001096 // rot = (src << rot_amt) | (src >> (64-rot_amt))
1097 mask = binop(Iop_And8, rot_amt, mkU8(63));
1098 rot = binop(Iop_Or64,
1099 binop(Iop_Shl64, src, mask),
1100 binop(Iop_Shr64, src, binop(Iop_Sub8, mkU8(64), mask)));
1101 } else {
ceriond953ebb2005-11-29 13:27:20 +00001102 // rot = (src << rot_amt) | (src >> (32-rot_amt))
cerionf0de28c2005-12-13 20:21:11 +00001103 mask = binop(Iop_And8, rot_amt, mkU8(31));
ceriond953ebb2005-11-29 13:27:20 +00001104 rot = binop(Iop_Or32,
1105 binop(Iop_Shl32, src, mask),
1106 binop(Iop_Shr32, src, binop(Iop_Sub8, mkU8(32), mask)));
cerion2831b002005-11-30 19:55:22 +00001107 }
sewardjc9659532005-07-21 21:33:57 +00001108 /* Note: the MuxOX is not merely an optimisation; it's needed
cerionf0de28c2005-12-13 20:21:11 +00001109 because otherwise the Shr is a shift by the word size when
ceriond953ebb2005-11-29 13:27:20 +00001110 mask denotes zero. For rotates by immediates, a lot of
sewardjc9659532005-07-21 21:33:57 +00001111 this junk gets folded out. */
ceriond953ebb2005-11-29 13:27:20 +00001112 return IRExpr_Mux0X( mask, /* zero rotate */ src,
1113 /* non-zero rotate */ rot );
cerion45b70ff2005-01-31 17:03:25 +00001114}
cerion896a1372005-01-25 12:24:25 +00001115
ceriond953ebb2005-11-29 13:27:20 +00001116#if 0
1117/* ROTL32_64(src64, rot_amt5)
1118 Weirdo 32bit rotl on ppc64:
1119 rot32 = ROTL(src_lo32,y);
1120 return (rot32|rot32);
1121*/
1122static IRExpr* /* :: Ity_I64 */ ROTL32_64 ( IRExpr* src64,
1123 IRExpr* rot_amt )
sewardj87e651f2005-09-09 08:31:18 +00001124{
ceriond953ebb2005-11-29 13:27:20 +00001125 IRExpr *mask, *rot32;
1126 vassert(mode64); // used only in 64bit mode
1127 vassert(typeOfIRExpr(irbb->tyenv,src64) == Ity_I64);
1128 vassert(typeOfIRExpr(irbb->tyenv,rot_amt) == Ity_I8);
1129
1130 mask = binop(Iop_And8, rot_amt, mkU8(31));
1131 rot32 = ROTL( unop(Iop_64to32, src64), rot_amt );
1132
1133 return binop(Iop_Or64,
1134 binop(Iop_Shl64, unop(Iop_32Uto64, rot32), mkU8(32)),
cerion2831b002005-11-30 19:55:22 +00001135 unop(Iop_32Uto64, rot32));
ceriond953ebb2005-11-29 13:27:20 +00001136}
1137#endif
1138
1139
1140/* Standard effective address calc: (rA + rB) */
1141static IRExpr* ea_rA_idxd ( UInt rA, UInt rB )
1142{
1143 IRType ty = mode64 ? Ity_I64 : Ity_I32;
1144 vassert(rA < 32);
1145 vassert(rB < 32);
cerion2831b002005-11-30 19:55:22 +00001146 return binop(mkSzOp(ty, Iop_Add8), getIReg(rA), getIReg(rB));
sewardj87e651f2005-09-09 08:31:18 +00001147}
1148
ceriond953ebb2005-11-29 13:27:20 +00001149/* Standard effective address calc: (rA + simm) */
1150static IRExpr* ea_rA_simm ( UInt rA, UInt simm16 )
sewardj87e651f2005-09-09 08:31:18 +00001151{
ceriond953ebb2005-11-29 13:27:20 +00001152 IRType ty = mode64 ? Ity_I64 : Ity_I32;
1153 vassert(rA < 32);
cerion2831b002005-11-30 19:55:22 +00001154 return binop(mkSzOp(ty, Iop_Add8), getIReg(rA),
1155 mkSzExtendS16(ty, simm16));
ceriond953ebb2005-11-29 13:27:20 +00001156}
1157
1158/* Standard effective address calc: (rA|0) */
1159static IRExpr* ea_rAor0 ( UInt rA )
1160{
1161 IRType ty = mode64 ? Ity_I64 : Ity_I32;
1162 vassert(rA < 32);
sewardj87e651f2005-09-09 08:31:18 +00001163 if (rA == 0) {
cerion2831b002005-11-30 19:55:22 +00001164 return mkSzImm(ty, 0);
sewardj87e651f2005-09-09 08:31:18 +00001165 } else {
1166 return getIReg(rA);
1167 }
1168}
1169
ceriond953ebb2005-11-29 13:27:20 +00001170/* Standard effective address calc: (rA|0) + rB */
1171static IRExpr* ea_rAor0_idxd ( UInt rA, UInt rB )
1172{
1173 vassert(rA < 32);
1174 vassert(rB < 32);
1175 return (rA == 0) ? getIReg(rB) : ea_rA_idxd( rA, rB );
1176}
1177
1178/* Standard effective address calc: (rA|0) + simm16 */
1179static IRExpr* ea_rAor0_simm ( UInt rA, UInt simm16 )
1180{
1181 IRType ty = mode64 ? Ity_I64 : Ity_I32;
1182 vassert(rA < 32);
1183 if (rA == 0) {
cerion2831b002005-11-30 19:55:22 +00001184 return mkSzExtendS16(ty, simm16);
ceriond953ebb2005-11-29 13:27:20 +00001185 } else {
1186 return ea_rA_simm( rA, simm16 );
1187 }
1188}
1189
1190
1191/* Align effective address */
1192static IRExpr* addr_align( IRExpr* addr, UChar align )
1193{
1194 IRType ty = mode64 ? Ity_I64 : Ity_I32;
1195 Long mask;
1196 switch (align) {
cerion2831b002005-11-30 19:55:22 +00001197 case 1: return addr; // byte aligned
ceriond953ebb2005-11-29 13:27:20 +00001198 case 2: mask = ((Long)-1) << 1; break; // half-word aligned
1199 case 4: mask = ((Long)-1) << 2; break; // word aligned
1200 case 16: mask = ((Long)-1) << 4; break; // quad-word aligned
1201 default:
1202 vex_printf("addr_align: align = %u\n", align);
cerion5b2325f2005-12-23 00:55:09 +00001203 vpanic("addr_align(ppc)");
ceriond953ebb2005-11-29 13:27:20 +00001204 }
1205
1206 vassert(typeOfIRExpr(irbb->tyenv,addr) == ty);
cerionfb197c42005-12-24 12:32:10 +00001207 return binop( mkSzOp(ty, Iop_And8), addr, mkSzImm(ty, mask) );
ceriond953ebb2005-11-29 13:27:20 +00001208}
1209
cerion896a1372005-01-25 12:24:25 +00001210
sewardjaca070a2006-10-17 00:28:22 +00001211/* Generate AbiHints which mark points at which the ELF or PowerOpen
1212 ABIs say that the stack red zone (viz, -N(r1) .. -1(r1), for some
1213 N) becomes undefined. That is at function calls and returns. ELF
1214 ppc32 doesn't have this "feature" (how fortunate for it).
sewardjcf8986c2006-01-18 04:14:52 +00001215*/
sewardjaca070a2006-10-17 00:28:22 +00001216static void make_redzone_AbiHint ( VexMiscInfo* vmi, HChar* who )
sewardjcf8986c2006-01-18 04:14:52 +00001217{
sewardjaca070a2006-10-17 00:28:22 +00001218 Int szB = vmi->guest_stack_redzone_size;
sewardjcf8986c2006-01-18 04:14:52 +00001219 if (0) vex_printf("AbiHint: %s\n", who);
sewardjaca070a2006-10-17 00:28:22 +00001220 vassert(szB >= 0);
1221 if (szB > 0) {
1222 if (mode64)
1223 stmt( IRStmt_AbiHint(
1224 binop(Iop_Sub64, getIReg(1), mkU64(szB)),
1225 szB
1226 ));
1227 else
1228 stmt( IRStmt_AbiHint(
1229 binop(Iop_Sub32, getIReg(1), mkU32(szB)),
1230 szB
1231 ));
1232 }
sewardjcf8986c2006-01-18 04:14:52 +00001233}
1234
1235
cerion896a1372005-01-25 12:24:25 +00001236/*------------------------------------------------------------*/
sewardjb51f0f42005-07-18 11:38:02 +00001237/*--- Helpers for condition codes. ---*/
cerion896a1372005-01-25 12:24:25 +00001238/*------------------------------------------------------------*/
1239
sewardjb51f0f42005-07-18 11:38:02 +00001240/* Condition register layout.
cerion896a1372005-01-25 12:24:25 +00001241
sewardjb51f0f42005-07-18 11:38:02 +00001242 In the hardware, CR is laid out like this. The leftmost end is the
1243 most significant bit in the register; however the IBM documentation
1244 numbers the bits backwards for some reason.
1245
1246 CR0 CR1 .......... CR6 CR7
1247 0 .. 3 ....................... 28 .. 31 (IBM bit numbering)
1248 31 28 3 0 (normal bit numbering)
1249
cerionedf7fc52005-11-18 20:57:41 +00001250 Each CR field is 4 bits: [<,>,==,SO]
sewardjb51f0f42005-07-18 11:38:02 +00001251
cerionedf7fc52005-11-18 20:57:41 +00001252 Hence in IBM's notation, BI=0 is CR7[SO], BI=1 is CR7[==], etc.
sewardjb51f0f42005-07-18 11:38:02 +00001253
1254 Indexing from BI to guest state:
1255
1256 let n = BI / 4
1257 off = BI % 4
1258 this references CR n:
1259
cerionedf7fc52005-11-18 20:57:41 +00001260 off==0 -> guest_CRn_321 >> 3
1261 off==1 -> guest_CRn_321 >> 2
1262 off==2 -> guest_CRn_321 >> 1
sewardjb51f0f42005-07-18 11:38:02 +00001263 off==3 -> guest_CRn_SO
sewardjb51f0f42005-07-18 11:38:02 +00001264
1265 Bear in mind the only significant bit in guest_CRn_SO is bit 0
cerionedf7fc52005-11-18 20:57:41 +00001266 (normal notation) and in guest_CRn_321 the significant bits are
sewardjb51f0f42005-07-18 11:38:02 +00001267 3, 2 and 1 (normal notation).
1268*/
cerionedf7fc52005-11-18 20:57:41 +00001269
1270static void putCR321 ( UInt cr, IRExpr* e )
1271{
1272 vassert(cr < 8);
1273 vassert(typeOfIRExpr(irbb->tyenv, e) == Ity_I8);
1274 stmt( IRStmt_Put(guestCR321offset(cr), e) );
1275}
1276
1277static void putCR0 ( UInt cr, IRExpr* e )
1278{
1279 vassert(cr < 8);
1280 vassert(typeOfIRExpr(irbb->tyenv, e) == Ity_I8);
1281 stmt( IRStmt_Put(guestCR0offset(cr), e) );
1282}
1283
1284static IRExpr* /* :: Ity_I8 */ getCR0 ( UInt cr )
1285{
1286 vassert(cr < 8);
1287 return IRExpr_Get(guestCR0offset(cr), Ity_I8);
1288}
1289
1290static IRExpr* /* :: Ity_I8 */ getCR321 ( UInt cr )
1291{
1292 vassert(cr < 8);
1293 return IRExpr_Get(guestCR321offset(cr), Ity_I8);
1294}
1295
sewardjb51f0f42005-07-18 11:38:02 +00001296/* Fetch the specified CR bit (as per IBM/hardware notation) and
1297 return it at the bottom of an I32; the top 31 bits are guaranteed
1298 to be zero. */
1299static IRExpr* /* :: Ity_I32 */ getCRbit ( UInt bi )
cerion896a1372005-01-25 12:24:25 +00001300{
sewardjb51f0f42005-07-18 11:38:02 +00001301 UInt n = bi / 4;
1302 UInt off = bi % 4;
1303 vassert(bi < 32);
1304 if (off == 3) {
1305 /* Fetch the SO bit for this CR field */
1306 /* Note: And32 is redundant paranoia iff guest state only has 0
1307 or 1 in that slot. */
1308 return binop(Iop_And32, unop(Iop_8Uto32, getCR0(n)), mkU32(1));
1309 } else {
1310 /* Fetch the <, > or == bit for this CR field */
1311 return binop( Iop_And32,
1312 binop( Iop_Shr32,
1313 unop(Iop_8Uto32, getCR321(n)),
sewardjc7cd2142005-09-09 22:31:49 +00001314 mkU8(toUChar(3-off)) ),
sewardjb51f0f42005-07-18 11:38:02 +00001315 mkU32(1) );
1316 }
cerion91ad5362005-01-27 23:02:41 +00001317}
1318
sewardjb51f0f42005-07-18 11:38:02 +00001319/* Dually, write the least significant bit of BIT to the specified CR
1320 bit. Indexing as per getCRbit. */
1321static void putCRbit ( UInt bi, IRExpr* bit )
1322{
sewardj197bd172005-10-12 11:34:33 +00001323 UInt n, off;
sewardjb51f0f42005-07-18 11:38:02 +00001324 IRExpr* safe;
1325 vassert(typeOfIRExpr(irbb->tyenv,bit) == Ity_I32);
1326 safe = binop(Iop_And32, bit, mkU32(1));
sewardj197bd172005-10-12 11:34:33 +00001327 n = bi / 4;
1328 off = bi % 4;
sewardjb51f0f42005-07-18 11:38:02 +00001329 vassert(bi < 32);
1330 if (off == 3) {
1331 /* This is the SO bit for this CR field */
1332 putCR0(n, unop(Iop_32to8, safe));
1333 } else {
1334 off = 3 - off;
1335 vassert(off == 1 || off == 2 || off == 3);
1336 putCR321(
1337 n,
1338 unop( Iop_32to8,
1339 binop( Iop_Or32,
1340 /* old value with field masked out */
1341 binop(Iop_And32, unop(Iop_8Uto32, getCR321(n)),
1342 mkU32(~(1 << off))),
1343 /* new value in the right place */
sewardjc7cd2142005-09-09 22:31:49 +00001344 binop(Iop_Shl32, safe, mkU8(toUChar(off)))
sewardjb51f0f42005-07-18 11:38:02 +00001345 )
1346 )
1347 );
1348 }
1349}
1350
sewardjb51f0f42005-07-18 11:38:02 +00001351/* Fetch the specified CR bit (as per IBM/hardware notation) and
1352 return it somewhere in an I32; it does not matter where, but
1353 whichever bit it is, all other bits are guaranteed to be zero. In
1354 other words, the I32-typed expression will be zero if the bit is
1355 zero and nonzero if the bit is 1. Write into *where the index
1356 of where the bit will be. */
1357
cerion5b2325f2005-12-23 00:55:09 +00001358static
1359IRExpr* /* :: Ity_I32 */ getCRbit_anywhere ( UInt bi, Int* where )
sewardjb51f0f42005-07-18 11:38:02 +00001360{
1361 UInt n = bi / 4;
1362 UInt off = bi % 4;
1363 vassert(bi < 32);
1364 if (off == 3) {
1365 /* Fetch the SO bit for this CR field */
1366 /* Note: And32 is redundant paranoia iff guest state only has 0
1367 or 1 in that slot. */
1368 *where = 0;
1369 return binop(Iop_And32, unop(Iop_8Uto32, getCR0(n)), mkU32(1));
1370 } else {
1371 /* Fetch the <, > or == bit for this CR field */
1372 *where = 3-off;
1373 return binop( Iop_And32,
1374 unop(Iop_8Uto32, getCR321(n)),
1375 mkU32(1 << (3-off)) );
1376 }
1377}
1378
sewardjb51f0f42005-07-18 11:38:02 +00001379/* Set the CR0 flags following an arithmetic operation.
1380 (Condition Register CR0 Field Definition, PPC32 p60)
cerion896a1372005-01-25 12:24:25 +00001381*/
cerionedf7fc52005-11-18 20:57:41 +00001382static IRExpr* getXER_SO ( void );
sewardj20ef5472005-07-21 14:48:31 +00001383static void set_CR0 ( IRExpr* result )
cerion896a1372005-01-25 12:24:25 +00001384{
ceriond953ebb2005-11-29 13:27:20 +00001385 vassert(typeOfIRExpr(irbb->tyenv,result) == Ity_I32 ||
1386 typeOfIRExpr(irbb->tyenv,result) == Ity_I64);
1387 if (mode64) {
ceriond953ebb2005-11-29 13:27:20 +00001388 putCR321( 0, unop(Iop_64to8,
cerion2831b002005-11-30 19:55:22 +00001389 binop(Iop_CmpORD64S, result, mkU64(0))) );
ceriond953ebb2005-11-29 13:27:20 +00001390 } else {
1391 putCR321( 0, unop(Iop_32to8,
1392 binop(Iop_CmpORD32S, result, mkU32(0))) );
1393 }
sewardjb51f0f42005-07-18 11:38:02 +00001394 putCR0( 0, getXER_SO() );
cerion896a1372005-01-25 12:24:25 +00001395}
cerion896a1372005-01-25 12:24:25 +00001396
sewardj20ef5472005-07-21 14:48:31 +00001397
cerionedf7fc52005-11-18 20:57:41 +00001398/* Set the CR6 flags following an AltiVec compare operation. */
1399static void set_AV_CR6 ( IRExpr* result, Bool test_all_ones )
1400{
1401 /* CR6[0:3] = {all_ones, 0, all_zeros, 0}
1402 all_ones = (v[0] && v[1] && v[2] && v[3])
1403 all_zeros = ~(v[0] || v[1] || v[2] || v[3])
1404 */
1405 IRTemp v0 = newTemp(Ity_V128);
1406 IRTemp v1 = newTemp(Ity_V128);
1407 IRTemp v2 = newTemp(Ity_V128);
1408 IRTemp v3 = newTemp(Ity_V128);
1409 IRTemp rOnes = newTemp(Ity_I8);
1410 IRTemp rZeros = newTemp(Ity_I8);
1411
1412 vassert(typeOfIRExpr(irbb->tyenv,result) == Ity_V128);
1413
1414 assign( v0, result );
1415 assign( v1, binop(Iop_ShrV128, result, mkU8(32)) );
1416 assign( v2, binop(Iop_ShrV128, result, mkU8(64)) );
1417 assign( v3, binop(Iop_ShrV128, result, mkU8(96)) );
1418
1419 assign( rZeros, unop(Iop_1Uto8,
1420 binop(Iop_CmpEQ32, mkU32(0xFFFFFFFF),
1421 unop(Iop_Not32,
1422 unop(Iop_V128to32,
1423 binop(Iop_OrV128,
1424 binop(Iop_OrV128, mkexpr(v0), mkexpr(v1)),
1425 binop(Iop_OrV128, mkexpr(v2), mkexpr(v3))))
1426 ))) );
1427
1428 if (test_all_ones) {
1429 assign( rOnes, unop(Iop_1Uto8,
1430 binop(Iop_CmpEQ32, mkU32(0xFFFFFFFF),
1431 unop(Iop_V128to32,
1432 binop(Iop_AndV128,
1433 binop(Iop_AndV128, mkexpr(v0), mkexpr(v1)),
cerion5b2325f2005-12-23 00:55:09 +00001434 binop(Iop_AndV128, mkexpr(v2), mkexpr(v3)))
1435 ))) );
cerionedf7fc52005-11-18 20:57:41 +00001436 putCR321( 6, binop(Iop_Or8,
1437 binop(Iop_Shl8, mkexpr(rOnes), mkU8(3)),
1438 binop(Iop_Shl8, mkexpr(rZeros), mkU8(1))) );
1439 } else {
1440 putCR321( 6, binop(Iop_Shl8, mkexpr(rZeros), mkU8(1)) );
1441 }
1442 putCR0( 6, mkU8(0) );
1443}
1444
1445
1446
1447/*------------------------------------------------------------*/
1448/*--- Helpers for XER flags. ---*/
1449/*------------------------------------------------------------*/
1450
1451static void putXER_SO ( IRExpr* e )
1452{
sewardj63327402006-01-25 03:26:27 +00001453 IRExpr* so;
cerionedf7fc52005-11-18 20:57:41 +00001454 vassert(typeOfIRExpr(irbb->tyenv, e) == Ity_I8);
sewardj63327402006-01-25 03:26:27 +00001455 so = binop(Iop_And8, e, mkU8(1));
cerion5b2325f2005-12-23 00:55:09 +00001456 stmt( IRStmt_Put( OFFB_XER_SO, so ) );
cerionedf7fc52005-11-18 20:57:41 +00001457}
1458
1459static void putXER_OV ( IRExpr* e )
1460{
sewardj63327402006-01-25 03:26:27 +00001461 IRExpr* ov;
cerionedf7fc52005-11-18 20:57:41 +00001462 vassert(typeOfIRExpr(irbb->tyenv, e) == Ity_I8);
sewardj63327402006-01-25 03:26:27 +00001463 ov = binop(Iop_And8, e, mkU8(1));
cerion5b2325f2005-12-23 00:55:09 +00001464 stmt( IRStmt_Put( OFFB_XER_OV, ov ) );
cerionedf7fc52005-11-18 20:57:41 +00001465}
1466
1467static void putXER_CA ( IRExpr* e )
1468{
sewardj63327402006-01-25 03:26:27 +00001469 IRExpr* ca;
cerionedf7fc52005-11-18 20:57:41 +00001470 vassert(typeOfIRExpr(irbb->tyenv, e) == Ity_I8);
sewardj63327402006-01-25 03:26:27 +00001471 ca = binop(Iop_And8, e, mkU8(1));
cerion5b2325f2005-12-23 00:55:09 +00001472 stmt( IRStmt_Put( OFFB_XER_CA, ca ) );
cerionedf7fc52005-11-18 20:57:41 +00001473}
1474
1475static void putXER_BC ( IRExpr* e )
1476{
sewardj63327402006-01-25 03:26:27 +00001477 IRExpr* bc;
cerionedf7fc52005-11-18 20:57:41 +00001478 vassert(typeOfIRExpr(irbb->tyenv, e) == Ity_I8);
sewardj63327402006-01-25 03:26:27 +00001479 bc = binop(Iop_And8, e, mkU8(0x7F));
cerion5b2325f2005-12-23 00:55:09 +00001480 stmt( IRStmt_Put( OFFB_XER_BC, bc ) );
cerionedf7fc52005-11-18 20:57:41 +00001481}
1482
1483static IRExpr* /* :: Ity_I8 */ getXER_SO ( void )
1484{
cerion5b2325f2005-12-23 00:55:09 +00001485 return IRExpr_Get( OFFB_XER_SO, Ity_I8 );
cerionedf7fc52005-11-18 20:57:41 +00001486}
1487
1488static IRExpr* /* :: Ity_I32 */ getXER_SO32 ( void )
1489{
1490 return binop( Iop_And32, unop(Iop_8Uto32, getXER_SO()), mkU32(1) );
1491}
1492
1493static IRExpr* /* :: Ity_I8 */ getXER_OV ( void )
1494{
cerion5b2325f2005-12-23 00:55:09 +00001495 return IRExpr_Get( OFFB_XER_OV, Ity_I8 );
cerionedf7fc52005-11-18 20:57:41 +00001496}
1497
1498static IRExpr* /* :: Ity_I32 */ getXER_OV32 ( void )
1499{
1500 return binop( Iop_And32, unop(Iop_8Uto32, getXER_OV()), mkU32(1) );
1501}
1502
1503static IRExpr* /* :: Ity_I32 */ getXER_CA32 ( void )
1504{
cerion5b2325f2005-12-23 00:55:09 +00001505 IRExpr* ca = IRExpr_Get( OFFB_XER_CA, Ity_I8 );
ceriond953ebb2005-11-29 13:27:20 +00001506 return binop( Iop_And32, unop(Iop_8Uto32, ca ), mkU32(1) );
cerionedf7fc52005-11-18 20:57:41 +00001507}
1508
1509static IRExpr* /* :: Ity_I8 */ getXER_BC ( void )
1510{
cerion5b2325f2005-12-23 00:55:09 +00001511 return IRExpr_Get( OFFB_XER_BC, Ity_I8 );
cerionedf7fc52005-11-18 20:57:41 +00001512}
1513
1514static IRExpr* /* :: Ity_I32 */ getXER_BC32 ( void )
1515{
cerion5b2325f2005-12-23 00:55:09 +00001516 IRExpr* bc = IRExpr_Get( OFFB_XER_BC, Ity_I8 );
ceriond953ebb2005-11-29 13:27:20 +00001517 return binop( Iop_And32, unop(Iop_8Uto32, bc), mkU32(0x7F) );
cerionedf7fc52005-11-18 20:57:41 +00001518}
1519
1520
sewardj20ef5472005-07-21 14:48:31 +00001521/* RES is the result of doing OP on ARGL and ARGR. Set %XER.OV and
1522 %XER.SO accordingly. */
1523
ceriond953ebb2005-11-29 13:27:20 +00001524static void set_XER_OV_32( UInt op, IRExpr* res,
1525 IRExpr* argL, IRExpr* argR )
sewardj20ef5472005-07-21 14:48:31 +00001526{
1527 IRTemp t64;
1528 IRExpr* xer_ov;
cerion5b2325f2005-12-23 00:55:09 +00001529 vassert(op < PPCG_FLAG_OP_NUMBER);
ceriond953ebb2005-11-29 13:27:20 +00001530 vassert(typeOfIRExpr(irbb->tyenv,res) == Ity_I32);
sewardj20ef5472005-07-21 14:48:31 +00001531 vassert(typeOfIRExpr(irbb->tyenv,argL) == Ity_I32);
1532 vassert(typeOfIRExpr(irbb->tyenv,argR) == Ity_I32);
1533
1534# define INT32_MIN 0x80000000
1535
1536# define XOR2(_aa,_bb) \
1537 binop(Iop_Xor32,(_aa),(_bb))
1538
1539# define XOR3(_cc,_dd,_ee) \
1540 binop(Iop_Xor32,binop(Iop_Xor32,(_cc),(_dd)),(_ee))
1541
1542# define AND3(_ff,_gg,_hh) \
1543 binop(Iop_And32,binop(Iop_And32,(_ff),(_gg)),(_hh))
1544
1545#define NOT(_jj) \
1546 unop(Iop_Not32, (_jj))
1547
1548 switch (op) {
cerion5b2325f2005-12-23 00:55:09 +00001549 case /* 0 */ PPCG_FLAG_OP_ADD:
1550 case /* 1 */ PPCG_FLAG_OP_ADDE:
ceriond953ebb2005-11-29 13:27:20 +00001551 /* (argL^argR^-1) & (argL^res) & (1<<31) ?1:0 */
1552 // i.e. ((both_same_sign) & (sign_changed) & (sign_mask))
1553 xer_ov
1554 = AND3( XOR3(argL,argR,mkU32(-1)),
1555 XOR2(argL,res),
1556 mkU32(INT32_MIN) );
1557 /* xer_ov can only be 0 or 1<<31 */
1558 xer_ov
1559 = binop(Iop_Shr32, xer_ov, mkU8(31) );
1560 break;
1561
cerion5b2325f2005-12-23 00:55:09 +00001562 case /* 2 */ PPCG_FLAG_OP_DIVW:
ceriond953ebb2005-11-29 13:27:20 +00001563 /* (argL == INT32_MIN && argR == -1) || argR == 0 */
1564 xer_ov
1565 = mkOR1(
1566 mkAND1(
1567 binop(Iop_CmpEQ32, argL, mkU32(INT32_MIN)),
1568 binop(Iop_CmpEQ32, argR, mkU32(-1))
1569 ),
1570 binop(Iop_CmpEQ32, argR, mkU32(0) )
1571 );
1572 xer_ov
1573 = unop(Iop_1Uto32, xer_ov);
1574 break;
1575
cerion5b2325f2005-12-23 00:55:09 +00001576 case /* 3 */ PPCG_FLAG_OP_DIVWU:
ceriond953ebb2005-11-29 13:27:20 +00001577 /* argR == 0 */
1578 xer_ov
1579 = unop(Iop_1Uto32, binop(Iop_CmpEQ32, argR, mkU32(0)));
1580 break;
1581
cerion5b2325f2005-12-23 00:55:09 +00001582 case /* 4 */ PPCG_FLAG_OP_MULLW:
ceriond953ebb2005-11-29 13:27:20 +00001583 /* OV true if result can't be represented in 32 bits
1584 i.e sHi != sign extension of sLo */
1585 t64 = newTemp(Ity_I64);
1586 assign( t64, binop(Iop_MullS32, argL, argR) );
1587 xer_ov
1588 = binop( Iop_CmpNE32,
1589 unop(Iop_64HIto32, mkexpr(t64)),
1590 binop( Iop_Sar32,
1591 unop(Iop_64to32, mkexpr(t64)),
1592 mkU8(31))
1593 );
1594 xer_ov
1595 = unop(Iop_1Uto32, xer_ov);
1596 break;
1597
cerion5b2325f2005-12-23 00:55:09 +00001598 case /* 5 */ PPCG_FLAG_OP_NEG:
ceriond953ebb2005-11-29 13:27:20 +00001599 /* argL == INT32_MIN */
1600 xer_ov
1601 = unop( Iop_1Uto32,
1602 binop(Iop_CmpEQ32, argL, mkU32(INT32_MIN)) );
1603 break;
1604
cerion5b2325f2005-12-23 00:55:09 +00001605 case /* 6 */ PPCG_FLAG_OP_SUBF:
1606 case /* 7 */ PPCG_FLAG_OP_SUBFC:
1607 case /* 8 */ PPCG_FLAG_OP_SUBFE:
ceriond953ebb2005-11-29 13:27:20 +00001608 /* ((~argL)^argR^-1) & ((~argL)^res) & (1<<31) ?1:0; */
1609 xer_ov
1610 = AND3( XOR3(NOT(argL),argR,mkU32(-1)),
1611 XOR2(NOT(argL),res),
1612 mkU32(INT32_MIN) );
1613 /* xer_ov can only be 0 or 1<<31 */
1614 xer_ov
1615 = binop(Iop_Shr32, xer_ov, mkU8(31) );
1616 break;
1617
1618 default:
1619 vex_printf("set_XER_OV: op = %u\n", op);
cerion5b2325f2005-12-23 00:55:09 +00001620 vpanic("set_XER_OV(ppc)");
sewardj20ef5472005-07-21 14:48:31 +00001621 }
ceriond953ebb2005-11-29 13:27:20 +00001622
sewardj20ef5472005-07-21 14:48:31 +00001623 /* xer_ov MUST denote either 0 or 1, no other value allowed */
ceriond953ebb2005-11-29 13:27:20 +00001624 putXER_OV( unop(Iop_32to8, xer_ov) );
sewardj20ef5472005-07-21 14:48:31 +00001625
1626 /* Update the summary overflow */
cerionedf7fc52005-11-18 20:57:41 +00001627 putXER_SO( binop(Iop_Or8, getXER_SO(), getXER_OV()) );
sewardj20ef5472005-07-21 14:48:31 +00001628
1629# undef INT32_MIN
1630# undef AND3
1631# undef XOR3
1632# undef XOR2
1633# undef NOT
1634}
1635
ceriond953ebb2005-11-29 13:27:20 +00001636static void set_XER_OV_64( UInt op, IRExpr* res,
1637 IRExpr* argL, IRExpr* argR )
1638{
1639 IRExpr* xer_ov;
cerion5b2325f2005-12-23 00:55:09 +00001640 vassert(op < PPCG_FLAG_OP_NUMBER);
ceriond953ebb2005-11-29 13:27:20 +00001641 vassert(typeOfIRExpr(irbb->tyenv,res) == Ity_I64);
1642 vassert(typeOfIRExpr(irbb->tyenv,argL) == Ity_I64);
1643 vassert(typeOfIRExpr(irbb->tyenv,argR) == Ity_I64);
1644
cerion2831b002005-11-30 19:55:22 +00001645# define INT64_MIN 0x8000000000000000ULL
ceriond953ebb2005-11-29 13:27:20 +00001646
1647# define XOR2(_aa,_bb) \
1648 binop(Iop_Xor64,(_aa),(_bb))
1649
1650# define XOR3(_cc,_dd,_ee) \
1651 binop(Iop_Xor64,binop(Iop_Xor64,(_cc),(_dd)),(_ee))
1652
1653# define AND3(_ff,_gg,_hh) \
1654 binop(Iop_And64,binop(Iop_And64,(_ff),(_gg)),(_hh))
1655
1656#define NOT(_jj) \
1657 unop(Iop_Not64, (_jj))
1658
1659 switch (op) {
cerion5b2325f2005-12-23 00:55:09 +00001660 case /* 0 */ PPCG_FLAG_OP_ADD:
1661 case /* 1 */ PPCG_FLAG_OP_ADDE:
ceriond953ebb2005-11-29 13:27:20 +00001662 /* (argL^argR^-1) & (argL^res) & (1<<63) ? 1:0 */
1663 // i.e. ((both_same_sign) & (sign_changed) & (sign_mask))
1664 xer_ov
1665 = AND3( XOR3(argL,argR,mkU64(-1)),
1666 XOR2(argL,res),
1667 mkU64(INT64_MIN) );
1668 /* xer_ov can only be 0 or 1<<63 */
1669 xer_ov
1670 = unop(Iop_64to1, binop(Iop_Shr64, xer_ov, mkU8(63)));
1671 break;
1672
cerion5b2325f2005-12-23 00:55:09 +00001673 case /* 2 */ PPCG_FLAG_OP_DIVW:
ceriond953ebb2005-11-29 13:27:20 +00001674 /* (argL == INT64_MIN && argR == -1) || argR == 0 */
1675 xer_ov
1676 = mkOR1(
1677 mkAND1(
1678 binop(Iop_CmpEQ64, argL, mkU64(INT64_MIN)),
1679 binop(Iop_CmpEQ64, argR, mkU64(-1))
1680 ),
1681 binop(Iop_CmpEQ64, argR, mkU64(0) )
1682 );
1683 break;
1684
cerion5b2325f2005-12-23 00:55:09 +00001685 case /* 3 */ PPCG_FLAG_OP_DIVWU:
ceriond953ebb2005-11-29 13:27:20 +00001686 /* argR == 0 */
1687 xer_ov
1688 = binop(Iop_CmpEQ64, argR, mkU64(0));
1689 break;
1690
cerion5b2325f2005-12-23 00:55:09 +00001691 case /* 4 */ PPCG_FLAG_OP_MULLW: {
ceriond953ebb2005-11-29 13:27:20 +00001692 /* OV true if result can't be represented in 64 bits
1693 i.e sHi != sign extension of sLo */
ceriond953ebb2005-11-29 13:27:20 +00001694 xer_ov
cerionbb01b7c2005-12-16 13:40:18 +00001695 = binop( Iop_CmpNE32,
1696 unop(Iop_64HIto32, res),
1697 binop( Iop_Sar32,
1698 unop(Iop_64to32, res),
1699 mkU8(31))
1700 );
ceriond953ebb2005-11-29 13:27:20 +00001701 break;
1702 }
1703
cerion5b2325f2005-12-23 00:55:09 +00001704 case /* 5 */ PPCG_FLAG_OP_NEG:
ceriond953ebb2005-11-29 13:27:20 +00001705 /* argL == INT64_MIN */
1706 xer_ov
1707 = binop(Iop_CmpEQ64, argL, mkU64(INT64_MIN));
1708 break;
1709
cerion5b2325f2005-12-23 00:55:09 +00001710 case /* 6 */ PPCG_FLAG_OP_SUBF:
1711 case /* 7 */ PPCG_FLAG_OP_SUBFC:
1712 case /* 8 */ PPCG_FLAG_OP_SUBFE:
ceriond953ebb2005-11-29 13:27:20 +00001713 /* ((~argL)^argR^-1) & ((~argL)^res) & (1<<63) ?1:0; */
1714 xer_ov
1715 = AND3( XOR3(NOT(argL),argR,mkU64(-1)),
1716 XOR2(NOT(argL),res),
1717 mkU64(INT64_MIN) );
1718 /* xer_ov can only be 0 or 1<<63 */
1719 xer_ov
1720 = unop(Iop_64to1, binop(Iop_Shr64, xer_ov, mkU8(63)));
1721 break;
1722
1723 default:
1724 vex_printf("set_XER_OV: op = %u\n", op);
cerion5b2325f2005-12-23 00:55:09 +00001725 vpanic("set_XER_OV(ppc64)");
ceriond953ebb2005-11-29 13:27:20 +00001726 }
1727
1728 /* xer_ov MUST denote either 0 or 1, no other value allowed */
1729 putXER_OV( unop(Iop_1Uto8, xer_ov) );
1730
1731 /* Update the summary overflow */
1732 putXER_SO( binop(Iop_Or8, getXER_SO(), getXER_OV()) );
1733
1734# undef INT64_MIN
1735# undef AND3
1736# undef XOR3
1737# undef XOR2
1738# undef NOT
1739}
1740
1741static void set_XER_OV ( IRType ty, UInt op, IRExpr* res,
1742 IRExpr* argL, IRExpr* argR )
1743{
1744 if (ty == Ity_I32)
1745 set_XER_OV_32( op, res, argL, argR );
1746 else
1747 set_XER_OV_64( op, res, argL, argR );
1748}
1749
1750
1751
sewardjb51f0f42005-07-18 11:38:02 +00001752/* RES is the result of doing OP on ARGL and ARGR with the old %XER.CA
1753 value being OLDCA. Set %XER.CA accordingly. */
cerione9d361a2005-03-04 17:35:29 +00001754
cerion2831b002005-11-30 19:55:22 +00001755static void set_XER_CA_32 ( UInt op, IRExpr* res,
1756 IRExpr* argL, IRExpr* argR, IRExpr* oldca )
cerion38674602005-02-08 02:19:25 +00001757{
sewardj9a036bf2005-03-14 18:19:08 +00001758 IRExpr* xer_ca;
cerion5b2325f2005-12-23 00:55:09 +00001759 vassert(op < PPCG_FLAG_OP_NUMBER);
sewardjb51f0f42005-07-18 11:38:02 +00001760 vassert(typeOfIRExpr(irbb->tyenv,res) == Ity_I32);
1761 vassert(typeOfIRExpr(irbb->tyenv,argL) == Ity_I32);
1762 vassert(typeOfIRExpr(irbb->tyenv,argR) == Ity_I32);
1763 vassert(typeOfIRExpr(irbb->tyenv,oldca) == Ity_I32);
cerion70e24122005-03-16 00:27:37 +00001764
sewardj20ef5472005-07-21 14:48:31 +00001765 /* Incoming oldca is assumed to hold the values 0 or 1 only. This
1766 seems reasonable given that it's always generated by
cerionedf7fc52005-11-18 20:57:41 +00001767 getXER_CA32(), which masks it accordingly. In any case it being
cerion75949202005-12-24 13:14:11 +00001768 0 or 1 is an invariant of the ppc guest state representation;
sewardj20ef5472005-07-21 14:48:31 +00001769 if it has any other value, that invariant has been violated. */
cerione9d361a2005-03-04 17:35:29 +00001770
sewardj20ef5472005-07-21 14:48:31 +00001771 switch (op) {
cerion5b2325f2005-12-23 00:55:09 +00001772 case /* 0 */ PPCG_FLAG_OP_ADD:
ceriond953ebb2005-11-29 13:27:20 +00001773 /* res <u argL */
1774 xer_ca
1775 = unop(Iop_1Uto32, binop(Iop_CmpLT32U, res, argL));
1776 break;
1777
cerion5b2325f2005-12-23 00:55:09 +00001778 case /* 1 */ PPCG_FLAG_OP_ADDE:
ceriond953ebb2005-11-29 13:27:20 +00001779 /* res <u argL || (old_ca==1 && res==argL) */
1780 xer_ca
1781 = mkOR1(
1782 binop(Iop_CmpLT32U, res, argL),
1783 mkAND1(
1784 binop(Iop_CmpEQ32, oldca, mkU32(1)),
1785 binop(Iop_CmpEQ32, res, argL)
1786 )
1787 );
1788 xer_ca
1789 = unop(Iop_1Uto32, xer_ca);
1790 break;
1791
cerion5b2325f2005-12-23 00:55:09 +00001792 case /* 8 */ PPCG_FLAG_OP_SUBFE:
ceriond953ebb2005-11-29 13:27:20 +00001793 /* res <u argR || (old_ca==1 && res==argR) */
1794 xer_ca
1795 = mkOR1(
1796 binop(Iop_CmpLT32U, res, argR),
1797 mkAND1(
1798 binop(Iop_CmpEQ32, oldca, mkU32(1)),
1799 binop(Iop_CmpEQ32, res, argR)
1800 )
1801 );
1802 xer_ca
1803 = unop(Iop_1Uto32, xer_ca);
1804 break;
1805
cerion5b2325f2005-12-23 00:55:09 +00001806 case /* 7 */ PPCG_FLAG_OP_SUBFC:
1807 case /* 9 */ PPCG_FLAG_OP_SUBFI:
ceriond953ebb2005-11-29 13:27:20 +00001808 /* res <=u argR */
1809 xer_ca
1810 = unop(Iop_1Uto32, binop(Iop_CmpLE32U, res, argR));
1811 break;
1812
cerion5b2325f2005-12-23 00:55:09 +00001813 case /* 10 */ PPCG_FLAG_OP_SRAW:
ceriond953ebb2005-11-29 13:27:20 +00001814 /* The shift amount is guaranteed to be in 0 .. 63 inclusive.
1815 If it is <= 31, behave like SRAWI; else XER.CA is the sign
1816 bit of argL. */
1817 /* This term valid for shift amount < 32 only */
1818 xer_ca
1819 = binop(
1820 Iop_And32,
1821 binop(Iop_Sar32, argL, mkU8(31)),
1822 binop( Iop_And32,
1823 argL,
1824 binop( Iop_Sub32,
cerion5b2325f2005-12-23 00:55:09 +00001825 binop(Iop_Shl32, mkU32(1),
1826 unop(Iop_32to8,argR)),
ceriond953ebb2005-11-29 13:27:20 +00001827 mkU32(1) )
1828 )
sewardj20ef5472005-07-21 14:48:31 +00001829 );
ceriond953ebb2005-11-29 13:27:20 +00001830 xer_ca
1831 = IRExpr_Mux0X(
1832 /* shift amt > 31 ? */
1833 unop(Iop_1Uto8, binop(Iop_CmpLT32U, mkU32(31), argR)),
1834 /* no -- be like srawi */
1835 unop(Iop_1Uto32, binop(Iop_CmpNE32, xer_ca, mkU32(0))),
1836 /* yes -- get sign bit of argL */
1837 binop(Iop_Shr32, argL, mkU8(31))
1838 );
1839 break;
sewardj20ef5472005-07-21 14:48:31 +00001840
cerion5b2325f2005-12-23 00:55:09 +00001841 case /* 11 */ PPCG_FLAG_OP_SRAWI:
ceriond953ebb2005-11-29 13:27:20 +00001842 /* xer_ca is 1 iff src was negative and bits_shifted_out !=
1843 0. Since the shift amount is known to be in the range
1844 0 .. 31 inclusive the following seems viable:
1845 xer.ca == 1 iff the following is nonzero:
1846 (argL >>s 31) -- either all 0s or all 1s
1847 & (argL & (1<<argR)-1) -- the stuff shifted out */
1848 xer_ca
1849 = binop(
1850 Iop_And32,
1851 binop(Iop_Sar32, argL, mkU8(31)),
1852 binop( Iop_And32,
1853 argL,
1854 binop( Iop_Sub32,
cerion5b2325f2005-12-23 00:55:09 +00001855 binop(Iop_Shl32, mkU32(1),
1856 unop(Iop_32to8,argR)),
ceriond953ebb2005-11-29 13:27:20 +00001857 mkU32(1) )
1858 )
sewardj20ef5472005-07-21 14:48:31 +00001859 );
ceriond953ebb2005-11-29 13:27:20 +00001860 xer_ca
1861 = unop(Iop_1Uto32, binop(Iop_CmpNE32, xer_ca, mkU32(0)));
1862 break;
1863
1864 default:
1865 vex_printf("set_XER_CA: op = %u\n", op);
cerion5b2325f2005-12-23 00:55:09 +00001866 vpanic("set_XER_CA(ppc)");
sewardj20ef5472005-07-21 14:48:31 +00001867 }
1868
1869 /* xer_ca MUST denote either 0 or 1, no other value allowed */
cerionedf7fc52005-11-18 20:57:41 +00001870 putXER_CA( unop(Iop_32to8, xer_ca) );
sewardjb51f0f42005-07-18 11:38:02 +00001871}
1872
cerion2831b002005-11-30 19:55:22 +00001873static void set_XER_CA_64 ( UInt op, IRExpr* res,
1874 IRExpr* argL, IRExpr* argR, IRExpr* oldca )
sewardje14bb9f2005-07-22 09:39:02 +00001875{
ceriond953ebb2005-11-29 13:27:20 +00001876 IRExpr* xer_ca;
cerion5b2325f2005-12-23 00:55:09 +00001877 vassert(op < PPCG_FLAG_OP_NUMBER);
ceriond953ebb2005-11-29 13:27:20 +00001878 vassert(typeOfIRExpr(irbb->tyenv,res) == Ity_I64);
1879 vassert(typeOfIRExpr(irbb->tyenv,argL) == Ity_I64);
1880 vassert(typeOfIRExpr(irbb->tyenv,argR) == Ity_I64);
1881 vassert(typeOfIRExpr(irbb->tyenv,oldca) == Ity_I64);
sewardje14bb9f2005-07-22 09:39:02 +00001882
ceriond953ebb2005-11-29 13:27:20 +00001883 /* Incoming oldca is assumed to hold the values 0 or 1 only. This
1884 seems reasonable given that it's always generated by
1885 getXER_CA32(), which masks it accordingly. In any case it being
cerion75949202005-12-24 13:14:11 +00001886 0 or 1 is an invariant of the ppc guest state representation;
ceriond953ebb2005-11-29 13:27:20 +00001887 if it has any other value, that invariant has been violated. */
1888
1889 switch (op) {
cerion5b2325f2005-12-23 00:55:09 +00001890 case /* 0 */ PPCG_FLAG_OP_ADD:
ceriond953ebb2005-11-29 13:27:20 +00001891 /* res <u argL */
1892 xer_ca
cerionf0de28c2005-12-13 20:21:11 +00001893 = unop(Iop_1Uto32, binop(Iop_CmpLT64U, res, argL));
ceriond953ebb2005-11-29 13:27:20 +00001894 break;
sewardje14bb9f2005-07-22 09:39:02 +00001895
cerion5b2325f2005-12-23 00:55:09 +00001896 case /* 1 */ PPCG_FLAG_OP_ADDE:
ceriond953ebb2005-11-29 13:27:20 +00001897 /* res <u argL || (old_ca==1 && res==argL) */
1898 xer_ca
1899 = mkOR1(
1900 binop(Iop_CmpLT64U, res, argL),
1901 mkAND1(
cerionf0de28c2005-12-13 20:21:11 +00001902 binop(Iop_CmpEQ64, oldca, mkU64(1)),
ceriond953ebb2005-11-29 13:27:20 +00001903 binop(Iop_CmpEQ64, res, argL)
1904 )
1905 );
cerionf0de28c2005-12-13 20:21:11 +00001906 xer_ca
1907 = unop(Iop_1Uto32, xer_ca);
cerionedf7fc52005-11-18 20:57:41 +00001908 break;
ceriond953ebb2005-11-29 13:27:20 +00001909
cerion5b2325f2005-12-23 00:55:09 +00001910 case /* 8 */ PPCG_FLAG_OP_SUBFE:
ceriond953ebb2005-11-29 13:27:20 +00001911 /* res <u argR || (old_ca==1 && res==argR) */
1912 xer_ca
1913 = mkOR1(
1914 binop(Iop_CmpLT64U, res, argR),
1915 mkAND1(
1916 binop(Iop_CmpEQ64, oldca, mkU64(1)),
1917 binop(Iop_CmpEQ64, res, argR)
1918 )
1919 );
cerionf0de28c2005-12-13 20:21:11 +00001920 xer_ca
1921 = unop(Iop_1Uto32, xer_ca);
ceriond953ebb2005-11-29 13:27:20 +00001922 break;
1923
cerion5b2325f2005-12-23 00:55:09 +00001924 case /* 7 */ PPCG_FLAG_OP_SUBFC:
1925 case /* 9 */ PPCG_FLAG_OP_SUBFI:
ceriond953ebb2005-11-29 13:27:20 +00001926 /* res <=u argR */
1927 xer_ca
cerionf0de28c2005-12-13 20:21:11 +00001928 = unop(Iop_1Uto32, binop(Iop_CmpLE64U, res, argR));
ceriond953ebb2005-11-29 13:27:20 +00001929 break;
1930
1931
cerion5b2325f2005-12-23 00:55:09 +00001932 case /* 10 */ PPCG_FLAG_OP_SRAW:
cerionf0de28c2005-12-13 20:21:11 +00001933 /* The shift amount is guaranteed to be in 0 .. 31 inclusive.
ceriond953ebb2005-11-29 13:27:20 +00001934 If it is <= 31, behave like SRAWI; else XER.CA is the sign
1935 bit of argL. */
cerionf0de28c2005-12-13 20:21:11 +00001936 /* This term valid for shift amount < 31 only */
1937
ceriond953ebb2005-11-29 13:27:20 +00001938 xer_ca
1939 = binop(
cerionf0de28c2005-12-13 20:21:11 +00001940 Iop_And64,
1941 binop(Iop_Sar64, argL, mkU8(31)),
1942 binop( Iop_And64,
ceriond953ebb2005-11-29 13:27:20 +00001943 argL,
cerionf0de28c2005-12-13 20:21:11 +00001944 binop( Iop_Sub64,
cerion5b2325f2005-12-23 00:55:09 +00001945 binop(Iop_Shl64, mkU64(1),
1946 unop(Iop_64to8,argR)),
cerionf0de28c2005-12-13 20:21:11 +00001947 mkU64(1) )
ceriond953ebb2005-11-29 13:27:20 +00001948 )
1949 );
1950 xer_ca
1951 = IRExpr_Mux0X(
1952 /* shift amt > 31 ? */
cerionf0de28c2005-12-13 20:21:11 +00001953 unop(Iop_1Uto8, binop(Iop_CmpLT64U, mkU64(31), argR)),
ceriond953ebb2005-11-29 13:27:20 +00001954 /* no -- be like srawi */
cerionf0de28c2005-12-13 20:21:11 +00001955 unop(Iop_1Uto32, binop(Iop_CmpNE64, xer_ca, mkU64(0))),
ceriond953ebb2005-11-29 13:27:20 +00001956 /* yes -- get sign bit of argL */
cerionf0de28c2005-12-13 20:21:11 +00001957 unop(Iop_64to32, binop(Iop_Shr64, argL, mkU8(63)))
ceriond953ebb2005-11-29 13:27:20 +00001958 );
1959 break;
1960
cerion5b2325f2005-12-23 00:55:09 +00001961 case /* 11 */ PPCG_FLAG_OP_SRAWI:
cerionf0de28c2005-12-13 20:21:11 +00001962 /* xer_ca is 1 iff src was negative and bits_shifted_out != 0.
1963 Since the shift amount is known to be in the range 0 .. 31
1964 inclusive the following seems viable:
ceriond953ebb2005-11-29 13:27:20 +00001965 xer.ca == 1 iff the following is nonzero:
1966 (argL >>s 31) -- either all 0s or all 1s
1967 & (argL & (1<<argR)-1) -- the stuff shifted out */
cerionf0de28c2005-12-13 20:21:11 +00001968
ceriond953ebb2005-11-29 13:27:20 +00001969 xer_ca
1970 = binop(
cerionf0de28c2005-12-13 20:21:11 +00001971 Iop_And64,
1972 binop(Iop_Sar64, argL, mkU8(31)),
1973 binop( Iop_And64,
ceriond953ebb2005-11-29 13:27:20 +00001974 argL,
cerionf0de28c2005-12-13 20:21:11 +00001975 binop( Iop_Sub64,
cerion5b2325f2005-12-23 00:55:09 +00001976 binop(Iop_Shl64, mkU64(1),
1977 unop(Iop_64to8,argR)),
cerionf0de28c2005-12-13 20:21:11 +00001978 mkU64(1) )
ceriond953ebb2005-11-29 13:27:20 +00001979 )
1980 );
1981 xer_ca
cerionf0de28c2005-12-13 20:21:11 +00001982 = unop(Iop_1Uto32, binop(Iop_CmpNE64, xer_ca, mkU64(0)));
ceriond953ebb2005-11-29 13:27:20 +00001983 break;
ceriond953ebb2005-11-29 13:27:20 +00001984
cerionf0de28c2005-12-13 20:21:11 +00001985
cerion5b2325f2005-12-23 00:55:09 +00001986 case /* 12 */ PPCG_FLAG_OP_SRAD:
cerionf0de28c2005-12-13 20:21:11 +00001987 /* The shift amount is guaranteed to be in 0 .. 63 inclusive.
1988 If it is <= 63, behave like SRADI; else XER.CA is the sign
1989 bit of argL. */
1990 /* This term valid for shift amount < 63 only */
1991
1992 xer_ca
1993 = binop(
1994 Iop_And64,
1995 binop(Iop_Sar64, argL, mkU8(63)),
1996 binop( Iop_And64,
1997 argL,
1998 binop( Iop_Sub64,
cerion5b2325f2005-12-23 00:55:09 +00001999 binop(Iop_Shl64, mkU64(1),
2000 unop(Iop_64to8,argR)),
cerionf0de28c2005-12-13 20:21:11 +00002001 mkU64(1) )
2002 )
2003 );
2004 xer_ca
2005 = IRExpr_Mux0X(
2006 /* shift amt > 63 ? */
2007 unop(Iop_1Uto8, binop(Iop_CmpLT64U, mkU64(63), argR)),
cerion07b07a92005-12-22 14:32:35 +00002008 /* no -- be like sradi */
2009 unop(Iop_1Uto32, binop(Iop_CmpNE64, xer_ca, mkU64(0))),
cerionf0de28c2005-12-13 20:21:11 +00002010 /* yes -- get sign bit of argL */
2011 unop(Iop_64to32, binop(Iop_Shr64, argL, mkU8(63)))
2012 );
2013 break;
2014
2015
cerion5b2325f2005-12-23 00:55:09 +00002016 case /* 13 */ PPCG_FLAG_OP_SRADI:
cerionf0de28c2005-12-13 20:21:11 +00002017 /* xer_ca is 1 iff src was negative and bits_shifted_out != 0.
2018 Since the shift amount is known to be in the range 0 .. 63
2019 inclusive, the following seems viable:
2020 xer.ca == 1 iff the following is nonzero:
2021 (argL >>s 63) -- either all 0s or all 1s
2022 & (argL & (1<<argR)-1) -- the stuff shifted out */
2023
2024 xer_ca
2025 = binop(
2026 Iop_And64,
2027 binop(Iop_Sar64, argL, mkU8(63)),
2028 binop( Iop_And64,
2029 argL,
2030 binop( Iop_Sub64,
cerion5b2325f2005-12-23 00:55:09 +00002031 binop(Iop_Shl64, mkU64(1),
2032 unop(Iop_64to8,argR)),
cerionf0de28c2005-12-13 20:21:11 +00002033 mkU64(1) )
2034 )
2035 );
2036 xer_ca
2037 = unop(Iop_1Uto32, binop(Iop_CmpNE64, xer_ca, mkU64(0)));
2038 break;
2039
ceriond953ebb2005-11-29 13:27:20 +00002040 default:
2041 vex_printf("set_XER_CA: op = %u\n", op);
cerionf0de28c2005-12-13 20:21:11 +00002042 vpanic("set_XER_CA(ppc64)");
sewardje14bb9f2005-07-22 09:39:02 +00002043 }
2044
ceriond953ebb2005-11-29 13:27:20 +00002045 /* xer_ca MUST denote either 0 or 1, no other value allowed */
cerionf0de28c2005-12-13 20:21:11 +00002046 putXER_CA( unop(Iop_32to8, xer_ca) );
sewardje14bb9f2005-07-22 09:39:02 +00002047}
2048
cerion2831b002005-11-30 19:55:22 +00002049static void set_XER_CA ( IRType ty, UInt op, IRExpr* res,
2050 IRExpr* argL, IRExpr* argR, IRExpr* oldca )
cerionedf7fc52005-11-18 20:57:41 +00002051{
cerion2831b002005-11-30 19:55:22 +00002052 if (ty == Ity_I32)
ceriond953ebb2005-11-29 13:27:20 +00002053 set_XER_CA_32( op, res, argL, argR, oldca );
2054 else
2055 set_XER_CA_64( op, res, argL, argR, oldca );
cerionedf7fc52005-11-18 20:57:41 +00002056}
2057
ceriond953ebb2005-11-29 13:27:20 +00002058
2059
2060/*------------------------------------------------------------*/
2061/*--- Read/write to guest-state --- */
2062/*------------------------------------------------------------*/
2063
cerionf0de28c2005-12-13 20:21:11 +00002064static IRExpr* /* :: Ity_I32/64 */ getGST ( PPC_GST reg )
cerionedf7fc52005-11-18 20:57:41 +00002065{
ceriond953ebb2005-11-29 13:27:20 +00002066 IRType ty = mode64 ? Ity_I64 : Ity_I32;
cerionedf7fc52005-11-18 20:57:41 +00002067 switch (reg) {
sewardjaca070a2006-10-17 00:28:22 +00002068 case PPC_GST_SPRG3_RO:
2069 return IRExpr_Get( OFFB_SPRG3_RO, ty );
2070
2071 case PPC_GST_CIA:
2072 return IRExpr_Get( OFFB_CIA, ty );
2073
ceriond953ebb2005-11-29 13:27:20 +00002074 case PPC_GST_LR:
cerion5b2325f2005-12-23 00:55:09 +00002075 return IRExpr_Get( OFFB_LR, ty );
ceriond953ebb2005-11-29 13:27:20 +00002076
2077 case PPC_GST_CTR:
cerion5b2325f2005-12-23 00:55:09 +00002078 return IRExpr_Get( OFFB_CTR, ty );
ceriond953ebb2005-11-29 13:27:20 +00002079
2080 case PPC_GST_VRSAVE:
cerion5b2325f2005-12-23 00:55:09 +00002081 return IRExpr_Get( OFFB_VRSAVE, Ity_I32 );
ceriond953ebb2005-11-29 13:27:20 +00002082
2083 case PPC_GST_VSCR:
cerion5b2325f2005-12-23 00:55:09 +00002084 return binop(Iop_And32, IRExpr_Get( OFFB_VSCR,Ity_I32 ),
2085 mkU32(MASK_VSCR_VALID));
ceriond953ebb2005-11-29 13:27:20 +00002086
2087 case PPC_GST_CR: {
cerionedf7fc52005-11-18 20:57:41 +00002088 /* Synthesise the entire CR into a single word. Expensive. */
2089# define FIELD(_n) \
2090 binop(Iop_Shl32, \
2091 unop(Iop_8Uto32, \
2092 binop(Iop_Or8, \
2093 binop(Iop_And8, getCR321(_n), mkU8(7<<1)), \
2094 binop(Iop_And8, getCR0(_n), mkU8(1)) \
2095 ) \
2096 ), \
2097 mkU8(4 * (7-(_n))) \
2098 )
2099 return binop(Iop_Or32,
2100 binop(Iop_Or32,
2101 binop(Iop_Or32, FIELD(0), FIELD(1)),
2102 binop(Iop_Or32, FIELD(2), FIELD(3))
2103 ),
2104 binop(Iop_Or32,
2105 binop(Iop_Or32, FIELD(4), FIELD(5)),
2106 binop(Iop_Or32, FIELD(6), FIELD(7))
2107 )
2108 );
2109# undef FIELD
2110 }
ceriond953ebb2005-11-29 13:27:20 +00002111
2112 case PPC_GST_XER:
cerionedf7fc52005-11-18 20:57:41 +00002113 return binop(Iop_Or32,
2114 binop(Iop_Or32,
2115 binop( Iop_Shl32, getXER_SO32(), mkU8(31)),
2116 binop( Iop_Shl32, getXER_OV32(), mkU8(30))),
2117 binop(Iop_Or32,
2118 binop( Iop_Shl32, getXER_CA32(), mkU8(29)),
2119 getXER_BC32()));
ceriond953ebb2005-11-29 13:27:20 +00002120
2121 case PPC_GST_RESVN:
cerion5b2325f2005-12-23 00:55:09 +00002122 return IRExpr_Get( OFFB_RESVN, ty);
ceriond953ebb2005-11-29 13:27:20 +00002123
cerionedf7fc52005-11-18 20:57:41 +00002124 default:
cerion5b2325f2005-12-23 00:55:09 +00002125 vex_printf("getGST(ppc): reg = %u", reg);
2126 vpanic("getGST(ppc)");
ceriond953ebb2005-11-29 13:27:20 +00002127 }
2128}
2129
2130/* Get a masked word from the given reg */
2131static IRExpr* /* ::Ity_I32 */ getGST_masked ( PPC_GST reg, UInt mask )
2132{
2133 IRTemp val = newTemp(Ity_I32);
2134 vassert( reg < PPC_GST_MAX );
2135
2136 switch (reg) {
2137
2138 case PPC_GST_FPSCR: {
cerion5b2325f2005-12-23 00:55:09 +00002139 /* Vex-generated code expects the FPSCR to be set as follows:
ceriond953ebb2005-11-29 13:27:20 +00002140 all exceptions masked, round-to-nearest.
2141 This corresponds to a FPSCR value of 0x0. */
2142
2143 /* We're only keeping track of the rounding mode,
2144 so if the mask isn't asking for this, just return 0x0 */
2145 if (mask & 0x3) {
cerion5b2325f2005-12-23 00:55:09 +00002146 assign( val, IRExpr_Get( OFFB_FPROUND, Ity_I32 ) );
ceriond953ebb2005-11-29 13:27:20 +00002147 } else {
2148 assign( val, mkU32(0x0) );
2149 }
2150 break;
2151 }
2152
2153 default:
cerion5b2325f2005-12-23 00:55:09 +00002154 vex_printf("getGST_masked(ppc): reg = %u", reg);
2155 vpanic("getGST_masked(ppc)");
ceriond953ebb2005-11-29 13:27:20 +00002156 }
2157
2158 if (mask != 0xFFFFFFFF) {
2159 return binop(Iop_And32, mkexpr(val), mkU32(mask));
2160 } else {
2161 return mkexpr(val);
2162 }
2163}
2164
2165/* Fetch the specified REG[FLD] nibble (as per IBM/hardware notation)
2166 and return it at the bottom of an I32; the top 27 bits are
2167 guaranteed to be zero. */
2168static IRExpr* /* ::Ity_I32 */ getGST_field ( PPC_GST reg, UInt fld )
2169{
2170 UInt shft, mask;
2171
2172 vassert( fld < 8 );
2173 vassert( reg < PPC_GST_MAX );
2174
2175 shft = 4*(7-fld);
2176 mask = 0xF<<shft;
2177
2178 switch (reg) {
2179 case PPC_GST_XER:
2180 vassert(fld ==7);
2181 return binop(Iop_Or32,
2182 binop(Iop_Or32,
2183 binop(Iop_Shl32, getXER_SO32(), mkU8(3)),
2184 binop(Iop_Shl32, getXER_OV32(), mkU8(2))),
2185 binop( Iop_Shl32, getXER_CA32(), mkU8(1)));
2186 break;
2187
2188 default:
2189 if (shft == 0)
2190 return getGST_masked( reg, mask );
2191 else
2192 return binop(Iop_Shr32,
2193 getGST_masked( reg, mask ),
2194 mkU8(toUChar( shft )));
2195 }
2196}
2197
2198static void putGST ( PPC_GST reg, IRExpr* src )
2199{
cerion5b2325f2005-12-23 00:55:09 +00002200 IRType ty = mode64 ? Ity_I64 : Ity_I32;
2201 IRType ty_src = typeOfIRExpr(irbb->tyenv,src );
ceriond953ebb2005-11-29 13:27:20 +00002202 vassert( reg < PPC_GST_MAX );
2203 switch (reg) {
sewardjaca070a2006-10-17 00:28:22 +00002204 case PPC_GST_CIA_AT_SC:
2205 vassert( ty_src == ty );
2206 stmt( IRStmt_Put( OFFB_CIA_AT_SC, src ) );
2207 break;
ceriond953ebb2005-11-29 13:27:20 +00002208 case PPC_GST_CIA:
cerion5b2325f2005-12-23 00:55:09 +00002209 vassert( ty_src == ty );
2210 stmt( IRStmt_Put( OFFB_CIA, src ) );
ceriond953ebb2005-11-29 13:27:20 +00002211 break;
2212 case PPC_GST_LR:
cerion5b2325f2005-12-23 00:55:09 +00002213 vassert( ty_src == ty );
2214 stmt( IRStmt_Put( OFFB_LR, src ) );
ceriond953ebb2005-11-29 13:27:20 +00002215 break;
2216 case PPC_GST_CTR:
cerion5b2325f2005-12-23 00:55:09 +00002217 vassert( ty_src == ty );
2218 stmt( IRStmt_Put( OFFB_CTR, src ) );
ceriond953ebb2005-11-29 13:27:20 +00002219 break;
2220 case PPC_GST_VRSAVE:
cerion5b2325f2005-12-23 00:55:09 +00002221 vassert( ty_src == Ity_I32 );
2222 stmt( IRStmt_Put( OFFB_VRSAVE,src));
ceriond953ebb2005-11-29 13:27:20 +00002223 break;
2224 case PPC_GST_VSCR:
cerion5b2325f2005-12-23 00:55:09 +00002225 vassert( ty_src == Ity_I32 );
2226 stmt( IRStmt_Put( OFFB_VSCR,
ceriond953ebb2005-11-29 13:27:20 +00002227 binop(Iop_And32, src,
2228 mkU32(MASK_VSCR_VALID)) ) );
2229 break;
2230 case PPC_GST_XER:
cerion5b2325f2005-12-23 00:55:09 +00002231 vassert( ty_src == Ity_I32 );
ceriond953ebb2005-11-29 13:27:20 +00002232 putXER_SO( unop(Iop_32to8, binop(Iop_Shr32, src, mkU8(31))) );
2233 putXER_OV( unop(Iop_32to8, binop(Iop_Shr32, src, mkU8(30))) );
2234 putXER_CA( unop(Iop_32to8, binop(Iop_Shr32, src, mkU8(29))) );
2235 putXER_BC( unop(Iop_32to8, src) );
2236 break;
2237
2238 case PPC_GST_EMWARN:
cerion5b2325f2005-12-23 00:55:09 +00002239 vassert( ty_src == Ity_I32 );
2240 stmt( IRStmt_Put( OFFB_EMWARN,src) );
ceriond953ebb2005-11-29 13:27:20 +00002241 break;
2242
2243 case PPC_GST_TISTART:
cerion5b2325f2005-12-23 00:55:09 +00002244 vassert( ty_src == ty );
2245 stmt( IRStmt_Put( OFFB_TISTART, src) );
ceriond953ebb2005-11-29 13:27:20 +00002246 break;
2247
2248 case PPC_GST_TILEN:
cerion5b2325f2005-12-23 00:55:09 +00002249 vassert( ty_src == ty );
2250 stmt( IRStmt_Put( OFFB_TILEN, src) );
ceriond953ebb2005-11-29 13:27:20 +00002251 break;
2252
2253 case PPC_GST_RESVN:
cerion5b2325f2005-12-23 00:55:09 +00002254 vassert( ty_src == ty );
2255 stmt( IRStmt_Put( OFFB_RESVN, src) );
ceriond953ebb2005-11-29 13:27:20 +00002256 break;
2257
2258 default:
cerion5b2325f2005-12-23 00:55:09 +00002259 vex_printf("putGST(ppc): reg = %u", reg);
2260 vpanic("putGST(ppc)");
cerionedf7fc52005-11-18 20:57:41 +00002261 }
2262}
sewardje14bb9f2005-07-22 09:39:02 +00002263
2264/* Write masked src to the given reg */
ceriond953ebb2005-11-29 13:27:20 +00002265static void putGST_masked ( PPC_GST reg, IRExpr* src, UInt mask )
sewardje14bb9f2005-07-22 09:39:02 +00002266{
ceriond953ebb2005-11-29 13:27:20 +00002267 IRType ty = mode64 ? Ity_I64 : Ity_I32;
2268 vassert( reg < PPC_GST_MAX );
sewardje14bb9f2005-07-22 09:39:02 +00002269 vassert( typeOfIRExpr(irbb->tyenv,src ) == Ity_I32 );
2270
2271 switch (reg) {
ceriond953ebb2005-11-29 13:27:20 +00002272 case PPC_GST_FPSCR: {
sewardje14bb9f2005-07-22 09:39:02 +00002273 /* Allow writes to Rounding Mode */
2274 if (mask & 0x3) {
sewardjb183b852006-02-03 16:08:03 +00002275 /* construct new fpround from new and old values as per mask:
2276 new fpround = (src & (3 & mask)) | (fpround & (3 & ~mask)) */
2277 stmt(
2278 IRStmt_Put(
2279 OFFB_FPROUND,
2280 binop(
2281 Iop_Or32,
2282 binop(Iop_And32, src, mkU32(3 & mask)),
2283 binop(
2284 Iop_And32,
2285 IRExpr_Get(OFFB_FPROUND,Ity_I32),
2286 mkU32(3 & ~mask)
2287 )
2288 )
2289 )
2290 );
sewardje14bb9f2005-07-22 09:39:02 +00002291 }
2292
cerionedf7fc52005-11-18 20:57:41 +00002293 /* Give EmWarn for attempted writes to:
sewardje14bb9f2005-07-22 09:39:02 +00002294 - Exception Controls
2295 - Non-IEEE Mode
2296 */
2297 if (mask & 0xFC) { // Exception Control, Non-IEE mode
sewardj5ff11dd2006-01-20 14:19:25 +00002298 VexEmWarn ew = EmWarn_PPCexns;
sewardje14bb9f2005-07-22 09:39:02 +00002299
2300 /* If any of the src::exception_control bits are actually set,
2301 side-exit to the next insn, reporting the warning,
2302 so that Valgrind's dispatcher sees the warning. */
ceriond953ebb2005-11-29 13:27:20 +00002303 putGST( PPC_GST_EMWARN, mkU32(ew) );
sewardje14bb9f2005-07-22 09:39:02 +00002304 stmt(
2305 IRStmt_Exit(
2306 binop(Iop_CmpNE32, mkU32(ew), mkU32(EmWarn_NONE)),
2307 Ijk_EmWarn,
cerion2831b002005-11-30 19:55:22 +00002308 mkSzConst( ty, nextInsnAddr()) ));
sewardje14bb9f2005-07-22 09:39:02 +00002309 }
2310
cerionedf7fc52005-11-18 20:57:41 +00002311 /* Ignore all other writes */
sewardje14bb9f2005-07-22 09:39:02 +00002312 break;
cerionedf7fc52005-11-18 20:57:41 +00002313 }
sewardje14bb9f2005-07-22 09:39:02 +00002314
2315 default:
cerion5b2325f2005-12-23 00:55:09 +00002316 vex_printf("putGST_masked(ppc): reg = %u", reg);
2317 vpanic("putGST_masked(ppc)");
sewardje14bb9f2005-07-22 09:39:02 +00002318 }
2319}
2320
cerionedf7fc52005-11-18 20:57:41 +00002321/* Write the least significant nibble of src to the specified
2322 REG[FLD] (as per IBM/hardware notation). */
ceriond953ebb2005-11-29 13:27:20 +00002323static void putGST_field ( PPC_GST reg, IRExpr* src, UInt fld )
cerionedf7fc52005-11-18 20:57:41 +00002324{
sewardj41a7b702005-11-18 22:18:23 +00002325 UInt shft, mask;
2326
cerionedf7fc52005-11-18 20:57:41 +00002327 vassert( typeOfIRExpr(irbb->tyenv,src ) == Ity_I32 );
2328 vassert( fld < 8 );
ceriond953ebb2005-11-29 13:27:20 +00002329 vassert( reg < PPC_GST_MAX );
cerionedf7fc52005-11-18 20:57:41 +00002330
sewardj41a7b702005-11-18 22:18:23 +00002331 shft = 4*(7-fld);
2332 mask = 0xF<<shft;
cerionedf7fc52005-11-18 20:57:41 +00002333
2334 switch (reg) {
ceriond953ebb2005-11-29 13:27:20 +00002335 case PPC_GST_CR:
cerionedf7fc52005-11-18 20:57:41 +00002336 putCR0 (fld, binop(Iop_And8, mkU8(1 ), unop(Iop_32to8, src)));
2337 putCR321(fld, binop(Iop_And8, mkU8(7<<1), unop(Iop_32to8, src)));
2338 break;
2339
2340 default:
2341 if (shft == 0) {
ceriond953ebb2005-11-29 13:27:20 +00002342 putGST_masked( reg, src, mask );
cerionedf7fc52005-11-18 20:57:41 +00002343 } else {
ceriond953ebb2005-11-29 13:27:20 +00002344 putGST_masked( reg,
cerionedf7fc52005-11-18 20:57:41 +00002345 binop(Iop_Shl32, src, mkU8(toUChar(shft))),
2346 mask );
2347 }
2348 }
2349}
cerion62bec572005-02-01 21:29:39 +00002350
cerion76222262005-02-05 13:45:57 +00002351
2352
cerione9d361a2005-03-04 17:35:29 +00002353/*------------------------------------------------------------*/
cerion3d870a32005-03-18 12:23:33 +00002354/*--- Integer Instruction Translation --- */
cerione9d361a2005-03-04 17:35:29 +00002355/*------------------------------------------------------------*/
cerion896a1372005-01-25 12:24:25 +00002356
cerion91ad5362005-01-27 23:02:41 +00002357/*
2358 Integer Arithmetic Instructions
2359*/
cerion645c9302005-01-31 10:09:59 +00002360static Bool dis_int_arith ( UInt theInstr )
cerion91ad5362005-01-27 23:02:41 +00002361{
cerion76de5cf2005-11-18 18:25:12 +00002362 /* D-Form, XO-Form */
2363 UChar opc1 = ifieldOPC(theInstr);
2364 UChar rD_addr = ifieldRegDS(theInstr);
2365 UChar rA_addr = ifieldRegA(theInstr);
ceriond953ebb2005-11-29 13:27:20 +00002366 UInt uimm16 = ifieldUIMM16(theInstr);
cerion76de5cf2005-11-18 18:25:12 +00002367 UChar rB_addr = ifieldRegB(theInstr);
2368 UChar flag_OE = ifieldBIT10(theInstr);
2369 UInt opc2 = ifieldOPClo9(theInstr);
2370 UChar flag_rC = ifieldBIT0(theInstr);
2371
ceriond953ebb2005-11-29 13:27:20 +00002372 Long simm16 = extend_s_16to64(uimm16);
2373 IRType ty = mode64 ? Ity_I64 : Ity_I32;
2374 IRTemp rA = newTemp(ty);
2375 IRTemp rB = newTemp(ty);
2376 IRTemp rD = newTemp(ty);
cerion70e24122005-03-16 00:27:37 +00002377
cerionb85e8bb2005-02-16 08:54:33 +00002378 Bool do_rc = False;
cerion91ad5362005-01-27 23:02:41 +00002379
cerion76de5cf2005-11-18 18:25:12 +00002380 assign( rA, getIReg(rA_addr) );
2381 assign( rB, getIReg(rB_addr) ); // XO-Form: rD, rA, rB
sewardjb51f0f42005-07-18 11:38:02 +00002382
cerionb85e8bb2005-02-16 08:54:33 +00002383 switch (opc1) {
cerionb85e8bb2005-02-16 08:54:33 +00002384 /* D-Form */
cerione9d361a2005-03-04 17:35:29 +00002385 case 0x0C: // addic (Add Immediate Carrying, PPC32 p351
ceriond953ebb2005-11-29 13:27:20 +00002386 DIP("addic r%u,r%u,%d\n", rD_addr, rA_addr, (Int)simm16);
cerion2831b002005-11-30 19:55:22 +00002387 assign( rD, binop( mkSzOp(ty, Iop_Add8), mkexpr(rA),
2388 mkSzExtendS16(ty, uimm16) ) );
cerion5b2325f2005-12-23 00:55:09 +00002389 set_XER_CA( ty, PPCG_FLAG_OP_ADD,
cerion2831b002005-11-30 19:55:22 +00002390 mkexpr(rD), mkexpr(rA), mkSzExtendS16(ty, uimm16),
2391 mkSzImm(ty, 0)/*old xer.ca, which is ignored*/ );
cerion4561acb2005-02-21 14:07:48 +00002392 break;
sewardjb51f0f42005-07-18 11:38:02 +00002393
cerione9d361a2005-03-04 17:35:29 +00002394 case 0x0D: // addic. (Add Immediate Carrying and Record, PPC32 p352)
ceriond953ebb2005-11-29 13:27:20 +00002395 DIP("addic. r%u,r%u,%d\n", rD_addr, rA_addr, (Int)simm16);
cerion2831b002005-11-30 19:55:22 +00002396 assign( rD, binop( mkSzOp(ty, Iop_Add8), mkexpr(rA),
2397 mkSzExtendS16(ty, uimm16) ) );
cerion5b2325f2005-12-23 00:55:09 +00002398 set_XER_CA( ty, PPCG_FLAG_OP_ADD,
cerion2831b002005-11-30 19:55:22 +00002399 mkexpr(rD), mkexpr(rA), mkSzExtendS16(ty, uimm16),
2400 mkSzImm(ty, 0)/*old xer.ca, which is ignored*/ );
cerion70e24122005-03-16 00:27:37 +00002401 do_rc = True; // Always record to CR
cerion76de5cf2005-11-18 18:25:12 +00002402 flag_rC = 1;
cerion4561acb2005-02-21 14:07:48 +00002403 break;
2404
cerione9d361a2005-03-04 17:35:29 +00002405 case 0x0E: // addi (Add Immediate, PPC32 p350)
cerionb85e8bb2005-02-16 08:54:33 +00002406 // li rD,val == addi rD,0,val
2407 // la disp(rA) == addi rD,rA,disp
cerion76de5cf2005-11-18 18:25:12 +00002408 if ( rA_addr == 0 ) {
ceriond953ebb2005-11-29 13:27:20 +00002409 DIP("li r%u,%d\n", rD_addr, (Int)simm16);
cerion2831b002005-11-30 19:55:22 +00002410 assign( rD, mkSzExtendS16(ty, uimm16) );
cerionb85e8bb2005-02-16 08:54:33 +00002411 } else {
ceriond953ebb2005-11-29 13:27:20 +00002412 DIP("addi r%u,r%u,%d\n", rD_addr, rA_addr, (Int)simm16);
cerion2831b002005-11-30 19:55:22 +00002413 assign( rD, binop( mkSzOp(ty, Iop_Add8), mkexpr(rA),
2414 mkSzExtendS16(ty, uimm16) ) );
cerionb85e8bb2005-02-16 08:54:33 +00002415 }
2416 break;
cerion91ad5362005-01-27 23:02:41 +00002417
cerione9d361a2005-03-04 17:35:29 +00002418 case 0x0F: // addis (Add Immediate Shifted, PPC32 p353)
cerionb85e8bb2005-02-16 08:54:33 +00002419 // lis rD,val == addis rD,0,val
cerion76de5cf2005-11-18 18:25:12 +00002420 if ( rA_addr == 0 ) {
ceriond953ebb2005-11-29 13:27:20 +00002421 DIP("lis r%u,%d\n", rD_addr, (Int)simm16);
cerion2831b002005-11-30 19:55:22 +00002422 assign( rD, mkSzExtendS32(ty, uimm16 << 16) );
cerionb85e8bb2005-02-16 08:54:33 +00002423 } else {
ceriond953ebb2005-11-29 13:27:20 +00002424 DIP("addis r%u,r%u,0x%x\n", rD_addr, rA_addr, (Int)simm16);
cerion2831b002005-11-30 19:55:22 +00002425 assign( rD, binop( mkSzOp(ty, Iop_Add8), mkexpr(rA),
2426 mkSzExtendS32(ty, uimm16 << 16) ) );
cerionb85e8bb2005-02-16 08:54:33 +00002427 }
2428 break;
cerion91ad5362005-01-27 23:02:41 +00002429
cerione9d361a2005-03-04 17:35:29 +00002430 case 0x07: // mulli (Multiply Low Immediate, PPC32 p490)
ceriond953ebb2005-11-29 13:27:20 +00002431 DIP("mulli r%u,r%u,%d\n", rD_addr, rA_addr, (Int)simm16);
2432 if (mode64)
2433 assign( rD, unop(Iop_128to64,
2434 binop(Iop_MullS64, mkexpr(rA),
cerion2831b002005-11-30 19:55:22 +00002435 mkSzExtendS16(ty, uimm16))) );
ceriond953ebb2005-11-29 13:27:20 +00002436 else
2437 assign( rD, unop(Iop_64to32,
2438 binop(Iop_MullS32, mkexpr(rA),
cerion2831b002005-11-30 19:55:22 +00002439 mkSzExtendS16(ty, uimm16))) );
cerionb85e8bb2005-02-16 08:54:33 +00002440 break;
cerion38674602005-02-08 02:19:25 +00002441
cerione9d361a2005-03-04 17:35:29 +00002442 case 0x08: // subfic (Subtract from Immediate Carrying, PPC32 p540)
ceriond953ebb2005-11-29 13:27:20 +00002443 DIP("subfic r%u,r%u,%d\n", rD_addr, rA_addr, (Int)simm16);
cerion76de5cf2005-11-18 18:25:12 +00002444 // rD = simm16 - rA
cerion2831b002005-11-30 19:55:22 +00002445 assign( rD, binop( mkSzOp(ty, Iop_Sub8),
2446 mkSzExtendS16(ty, uimm16),
ceriond953ebb2005-11-29 13:27:20 +00002447 mkexpr(rA)) );
cerion5b2325f2005-12-23 00:55:09 +00002448 set_XER_CA( ty, PPCG_FLAG_OP_SUBFI,
cerion2831b002005-11-30 19:55:22 +00002449 mkexpr(rD), mkexpr(rA), mkSzExtendS16(ty, uimm16),
2450 mkSzImm(ty, 0)/*old xer.ca, which is ignored*/ );
cerionb85e8bb2005-02-16 08:54:33 +00002451 break;
cerion38674602005-02-08 02:19:25 +00002452
cerionb85e8bb2005-02-16 08:54:33 +00002453 /* XO-Form */
2454 case 0x1F:
cerionb85e8bb2005-02-16 08:54:33 +00002455 do_rc = True; // All below record to CR
2456
2457 switch (opc2) {
cerione9d361a2005-03-04 17:35:29 +00002458 case 0x10A: // add (Add, PPC32 p347)
ceriond953ebb2005-11-29 13:27:20 +00002459 DIP("add%s%s r%u,r%u,r%u\n",
cerion5b2325f2005-12-23 00:55:09 +00002460 flag_OE ? "o" : "", flag_rC ? ".":"",
cerion76de5cf2005-11-18 18:25:12 +00002461 rD_addr, rA_addr, rB_addr);
cerion2831b002005-11-30 19:55:22 +00002462 assign( rD, binop( mkSzOp(ty, Iop_Add8),
ceriond953ebb2005-11-29 13:27:20 +00002463 mkexpr(rA), mkexpr(rB) ) );
cerion70e24122005-03-16 00:27:37 +00002464 if (flag_OE) {
cerion5b2325f2005-12-23 00:55:09 +00002465 set_XER_OV( ty, PPCG_FLAG_OP_ADD,
cerion76de5cf2005-11-18 18:25:12 +00002466 mkexpr(rD), mkexpr(rA), mkexpr(rB) );
cerion70e24122005-03-16 00:27:37 +00002467 }
cerionb85e8bb2005-02-16 08:54:33 +00002468 break;
cerion91ad5362005-01-27 23:02:41 +00002469
cerione9d361a2005-03-04 17:35:29 +00002470 case 0x00A: // addc (Add Carrying, PPC32 p348)
ceriond953ebb2005-11-29 13:27:20 +00002471 DIP("addc%s%s r%u,r%u,r%u\n",
cerion5b2325f2005-12-23 00:55:09 +00002472 flag_OE ? "o" : "", flag_rC ? ".":"",
cerion76de5cf2005-11-18 18:25:12 +00002473 rD_addr, rA_addr, rB_addr);
cerion2831b002005-11-30 19:55:22 +00002474 assign( rD, binop( mkSzOp(ty, Iop_Add8),
ceriond953ebb2005-11-29 13:27:20 +00002475 mkexpr(rA), mkexpr(rB)) );
cerion5b2325f2005-12-23 00:55:09 +00002476 set_XER_CA( ty, PPCG_FLAG_OP_ADD,
cerion76de5cf2005-11-18 18:25:12 +00002477 mkexpr(rD), mkexpr(rA), mkexpr(rB),
cerion2831b002005-11-30 19:55:22 +00002478 mkSzImm(ty, 0)/*old xer.ca, which is ignored*/ );
cerion70e24122005-03-16 00:27:37 +00002479 if (flag_OE) {
cerion5b2325f2005-12-23 00:55:09 +00002480 set_XER_OV( ty, PPCG_FLAG_OP_ADD,
cerion76de5cf2005-11-18 18:25:12 +00002481 mkexpr(rD), mkexpr(rA), mkexpr(rB) );
cerion70e24122005-03-16 00:27:37 +00002482 }
cerionb85e8bb2005-02-16 08:54:33 +00002483 break;
2484
sewardjb51f0f42005-07-18 11:38:02 +00002485 case 0x08A: { // adde (Add Extended, PPC32 p349)
cerion2831b002005-11-30 19:55:22 +00002486 IRTemp old_xer_ca = newTemp(ty);
ceriond953ebb2005-11-29 13:27:20 +00002487 DIP("adde%s%s r%u,r%u,r%u\n",
cerion5b2325f2005-12-23 00:55:09 +00002488 flag_OE ? "o" : "", flag_rC ? ".":"",
cerion76de5cf2005-11-18 18:25:12 +00002489 rD_addr, rA_addr, rB_addr);
cerionb85e8bb2005-02-16 08:54:33 +00002490 // rD = rA + rB + XER[CA]
cerion2831b002005-11-30 19:55:22 +00002491 assign( old_xer_ca, mkSzWiden32(ty, getXER_CA32(), False) );
2492 assign( rD, binop( mkSzOp(ty, Iop_Add8), mkexpr(rA),
2493 binop( mkSzOp(ty, Iop_Add8),
2494 mkexpr(rB), mkexpr(old_xer_ca))) );
cerion5b2325f2005-12-23 00:55:09 +00002495 set_XER_CA( ty, PPCG_FLAG_OP_ADDE,
cerion76de5cf2005-11-18 18:25:12 +00002496 mkexpr(rD), mkexpr(rA), mkexpr(rB),
cerion2831b002005-11-30 19:55:22 +00002497 mkexpr(old_xer_ca) );
cerion70e24122005-03-16 00:27:37 +00002498 if (flag_OE) {
cerion5b2325f2005-12-23 00:55:09 +00002499 set_XER_OV( ty, PPCG_FLAG_OP_ADDE,
cerion76de5cf2005-11-18 18:25:12 +00002500 mkexpr(rD), mkexpr(rA), mkexpr(rB) );
cerion70e24122005-03-16 00:27:37 +00002501 }
cerionb85e8bb2005-02-16 08:54:33 +00002502 break;
sewardjb51f0f42005-07-18 11:38:02 +00002503 }
2504
cerion5b2325f2005-12-23 00:55:09 +00002505 case 0x0EA: { // addme (Add to Minus One Extended, PPC32 p354)
cerion2831b002005-11-30 19:55:22 +00002506 IRTemp old_xer_ca = newTemp(ty);
2507 IRExpr *min_one;
cerion76de5cf2005-11-18 18:25:12 +00002508 if (rB_addr != 0) {
cerion5b2325f2005-12-23 00:55:09 +00002509 vex_printf("dis_int_arith(ppc)(addme,rB_addr)\n");
cerionb85e8bb2005-02-16 08:54:33 +00002510 return False;
2511 }
ceriond953ebb2005-11-29 13:27:20 +00002512 DIP("addme%s%s r%u,r%u,r%u\n",
cerion5b2325f2005-12-23 00:55:09 +00002513 flag_OE ? "o" : "", flag_rC ? ".":"",
cerion76de5cf2005-11-18 18:25:12 +00002514 rD_addr, rA_addr, rB_addr);
cerion70e24122005-03-16 00:27:37 +00002515 // rD = rA + (-1) + XER[CA]
2516 // => Just another form of adde
cerion2831b002005-11-30 19:55:22 +00002517 assign( old_xer_ca, mkSzWiden32(ty, getXER_CA32(), False) );
2518 min_one = mkSzImm(ty, (Long)-1);
2519 assign( rD, binop( mkSzOp(ty, Iop_Add8), mkexpr(rA),
2520 binop( mkSzOp(ty, Iop_Add8),
2521 min_one, mkexpr(old_xer_ca)) ));
cerion5b2325f2005-12-23 00:55:09 +00002522 set_XER_CA( ty, PPCG_FLAG_OP_ADDE,
ceriond953ebb2005-11-29 13:27:20 +00002523 mkexpr(rD), mkexpr(rA), min_one,
cerion2831b002005-11-30 19:55:22 +00002524 mkexpr(old_xer_ca) );
cerion70e24122005-03-16 00:27:37 +00002525 if (flag_OE) {
cerion5b2325f2005-12-23 00:55:09 +00002526 set_XER_OV( ty, PPCG_FLAG_OP_ADDE,
ceriond953ebb2005-11-29 13:27:20 +00002527 mkexpr(rD), mkexpr(rA), min_one );
cerion70e24122005-03-16 00:27:37 +00002528 }
cerionb85e8bb2005-02-16 08:54:33 +00002529 break;
sewardjb51f0f42005-07-18 11:38:02 +00002530 }
2531
2532 case 0x0CA: { // addze (Add to Zero Extended, PPC32 p355)
cerion2831b002005-11-30 19:55:22 +00002533 IRTemp old_xer_ca = newTemp(ty);
cerion76de5cf2005-11-18 18:25:12 +00002534 if (rB_addr != 0) {
cerion5b2325f2005-12-23 00:55:09 +00002535 vex_printf("dis_int_arith(ppc)(addze,rB_addr)\n");
cerionb85e8bb2005-02-16 08:54:33 +00002536 return False;
2537 }
ceriond953ebb2005-11-29 13:27:20 +00002538 DIP("addze%s%s r%u,r%u,r%u\n",
cerion5b2325f2005-12-23 00:55:09 +00002539 flag_OE ? "o" : "", flag_rC ? ".":"",
cerion76de5cf2005-11-18 18:25:12 +00002540 rD_addr, rA_addr, rB_addr);
cerion70e24122005-03-16 00:27:37 +00002541 // rD = rA + (0) + XER[CA]
2542 // => Just another form of adde
cerion2831b002005-11-30 19:55:22 +00002543 assign( old_xer_ca, mkSzWiden32(ty, getXER_CA32(), False) );
2544 assign( rD, binop( mkSzOp(ty, Iop_Add8),
2545 mkexpr(rA), mkexpr(old_xer_ca)) );
cerion5b2325f2005-12-23 00:55:09 +00002546 set_XER_CA( ty, PPCG_FLAG_OP_ADDE,
cerion2831b002005-11-30 19:55:22 +00002547 mkexpr(rD), mkexpr(rA), mkSzImm(ty, 0),
2548 mkexpr(old_xer_ca) );
cerion70e24122005-03-16 00:27:37 +00002549 if (flag_OE) {
cerion5b2325f2005-12-23 00:55:09 +00002550 set_XER_OV( ty, PPCG_FLAG_OP_ADDE,
cerion2831b002005-11-30 19:55:22 +00002551 mkexpr(rD), mkexpr(rA), mkSzImm(ty, 0) );
cerion70e24122005-03-16 00:27:37 +00002552 }
cerionb85e8bb2005-02-16 08:54:33 +00002553 break;
sewardjb51f0f42005-07-18 11:38:02 +00002554 }
cerion91ad5362005-01-27 23:02:41 +00002555
cerione9d361a2005-03-04 17:35:29 +00002556 case 0x1EB: // divw (Divide Word, PPC32 p388)
ceriond953ebb2005-11-29 13:27:20 +00002557 DIP("divw%s%s r%u,r%u,r%u\n",
cerion5b2325f2005-12-23 00:55:09 +00002558 flag_OE ? "o" : "", flag_rC ? ".":"",
cerion76de5cf2005-11-18 18:25:12 +00002559 rD_addr, rA_addr, rB_addr);
ceriond953ebb2005-11-29 13:27:20 +00002560 if (mode64) {
cerion2831b002005-11-30 19:55:22 +00002561 /* Note:
2562 XER settings are mode independent, and reflect the
2563 overflow of the low-order 32bit result
2564 CR0[LT|GT|EQ] are undefined if flag_rC && mode64
2565 */
cerionbb01b7c2005-12-16 13:40:18 +00002566 /* rD[hi32] are undefined: setting them to sign of lo32
2567 - makes set_CR0 happy */
2568 IRExpr* dividend = mk64lo32Sto64( mkexpr(rA) );
2569 IRExpr* divisor = mk64lo32Sto64( mkexpr(rB) );
cerion5b2325f2005-12-23 00:55:09 +00002570 assign( rD, mk64lo32Uto64( binop(Iop_DivS64, dividend,
2571 divisor) ) );
ceriond953ebb2005-11-29 13:27:20 +00002572 if (flag_OE) {
cerion5b2325f2005-12-23 00:55:09 +00002573 set_XER_OV( ty, PPCG_FLAG_OP_DIVW,
cerionf0de28c2005-12-13 20:21:11 +00002574 mkexpr(rD), dividend, divisor );
ceriond953ebb2005-11-29 13:27:20 +00002575 }
2576 } else {
2577 assign( rD, binop(Iop_DivS32, mkexpr(rA), mkexpr(rB)) );
2578 if (flag_OE) {
cerion5b2325f2005-12-23 00:55:09 +00002579 set_XER_OV( ty, PPCG_FLAG_OP_DIVW,
ceriond953ebb2005-11-29 13:27:20 +00002580 mkexpr(rD), mkexpr(rA), mkexpr(rB) );
2581 }
cerion70e24122005-03-16 00:27:37 +00002582 }
cerionb85e8bb2005-02-16 08:54:33 +00002583 /* Note:
2584 if (0x8000_0000 / -1) or (x / 0)
cerion76de5cf2005-11-18 18:25:12 +00002585 => rD=undef, if(flag_rC) CR7=undef, if(flag_OE) XER_OV=1
cerionb85e8bb2005-02-16 08:54:33 +00002586 => But _no_ exception raised. */
2587 break;
cerion91ad5362005-01-27 23:02:41 +00002588
cerione9d361a2005-03-04 17:35:29 +00002589 case 0x1CB: // divwu (Divide Word Unsigned, PPC32 p389)
ceriond953ebb2005-11-29 13:27:20 +00002590 DIP("divwu%s%s r%u,r%u,r%u\n",
cerion5b2325f2005-12-23 00:55:09 +00002591 flag_OE ? "o" : "", flag_rC ? ".":"",
cerion76de5cf2005-11-18 18:25:12 +00002592 rD_addr, rA_addr, rB_addr);
ceriond953ebb2005-11-29 13:27:20 +00002593 if (mode64) {
cerion2831b002005-11-30 19:55:22 +00002594 /* Note:
2595 XER settings are mode independent, and reflect the
2596 overflow of the low-order 32bit result
2597 CR0[LT|GT|EQ] are undefined if flag_rC && mode64
2598 */
cerionbb01b7c2005-12-16 13:40:18 +00002599 IRExpr* dividend = mk64lo32Uto64( mkexpr(rA) );
2600 IRExpr* divisor = mk64lo32Uto64( mkexpr(rB) );
cerion5b2325f2005-12-23 00:55:09 +00002601 assign( rD, mk64lo32Uto64( binop(Iop_DivU64, dividend,
2602 divisor) ) );
ceriond953ebb2005-11-29 13:27:20 +00002603 if (flag_OE) {
cerion5b2325f2005-12-23 00:55:09 +00002604 set_XER_OV( ty, PPCG_FLAG_OP_DIVWU,
cerionf0de28c2005-12-13 20:21:11 +00002605 mkexpr(rD), dividend, divisor );
ceriond953ebb2005-11-29 13:27:20 +00002606 }
cerion2831b002005-11-30 19:55:22 +00002607 } else {
ceriond953ebb2005-11-29 13:27:20 +00002608 assign( rD, binop(Iop_DivU32, mkexpr(rA), mkexpr(rB)) );
2609 if (flag_OE) {
cerion5b2325f2005-12-23 00:55:09 +00002610 set_XER_OV( ty, PPCG_FLAG_OP_DIVWU,
ceriond953ebb2005-11-29 13:27:20 +00002611 mkexpr(rD), mkexpr(rA), mkexpr(rB) );
2612 }
cerion70e24122005-03-16 00:27:37 +00002613 }
cerionb85e8bb2005-02-16 08:54:33 +00002614 /* Note: ditto comment divw, for (x / 0) */
2615 break;
cerion91ad5362005-01-27 23:02:41 +00002616
cerione9d361a2005-03-04 17:35:29 +00002617 case 0x04B: // mulhw (Multiply High Word, PPC32 p488)
cerionb85e8bb2005-02-16 08:54:33 +00002618 if (flag_OE != 0) {
cerion5b2325f2005-12-23 00:55:09 +00002619 vex_printf("dis_int_arith(ppc)(mulhw,flag_OE)\n");
cerionb85e8bb2005-02-16 08:54:33 +00002620 return False;
2621 }
cerion5b2325f2005-12-23 00:55:09 +00002622 DIP("mulhw%s r%u,r%u,r%u\n", flag_rC ? ".":"",
cerion76de5cf2005-11-18 18:25:12 +00002623 rD_addr, rA_addr, rB_addr);
cerion2831b002005-11-30 19:55:22 +00002624 if (mode64) {
cerionbb01b7c2005-12-16 13:40:18 +00002625 /* rD[hi32] are undefined: setting them to sign of lo32
2626 - makes set_CR0 happy */
2627 assign( rD, binop(Iop_Sar64,
2628 binop(Iop_Mul64,
2629 mk64lo32Sto64( mkexpr(rA) ),
2630 mk64lo32Sto64( mkexpr(rB) )),
2631 mkU8(32)) );
cerion2831b002005-11-30 19:55:22 +00002632 } else {
ceriond953ebb2005-11-29 13:27:20 +00002633 assign( rD, unop(Iop_64HIto32,
2634 binop(Iop_MullS32,
2635 mkexpr(rA), mkexpr(rB))) );
cerion2831b002005-11-30 19:55:22 +00002636 }
cerionb85e8bb2005-02-16 08:54:33 +00002637 break;
cerionc19d5e12005-02-01 15:56:25 +00002638
cerion5b2325f2005-12-23 00:55:09 +00002639 case 0x00B: // mulhwu (Multiply High Word Unsigned, PPC32 p489)
cerionb85e8bb2005-02-16 08:54:33 +00002640 if (flag_OE != 0) {
cerion5b2325f2005-12-23 00:55:09 +00002641 vex_printf("dis_int_arith(ppc)(mulhwu,flag_OE)\n");
cerionb85e8bb2005-02-16 08:54:33 +00002642 return False;
2643 }
cerion5b2325f2005-12-23 00:55:09 +00002644 DIP("mulhwu%s r%u,r%u,r%u\n", flag_rC ? ".":"",
cerion76de5cf2005-11-18 18:25:12 +00002645 rD_addr, rA_addr, rB_addr);
cerion2831b002005-11-30 19:55:22 +00002646 if (mode64) {
cerionbb01b7c2005-12-16 13:40:18 +00002647 /* rD[hi32] are undefined: setting them to sign of lo32
2648 - makes set_CR0 happy */
2649 assign( rD, binop(Iop_Sar64,
2650 binop(Iop_Mul64,
2651 mk64lo32Uto64( mkexpr(rA) ),
2652 mk64lo32Uto64( mkexpr(rB) ) ),
2653 mkU8(32)) );
cerion2831b002005-11-30 19:55:22 +00002654 } else {
ceriond953ebb2005-11-29 13:27:20 +00002655 assign( rD, unop(Iop_64HIto32,
2656 binop(Iop_MullU32,
2657 mkexpr(rA), mkexpr(rB))) );
cerion2831b002005-11-30 19:55:22 +00002658 }
cerionb85e8bb2005-02-16 08:54:33 +00002659 break;
2660
cerione9d361a2005-03-04 17:35:29 +00002661 case 0x0EB: // mullw (Multiply Low Word, PPC32 p491)
ceriond953ebb2005-11-29 13:27:20 +00002662 DIP("mullw%s%s r%u,r%u,r%u\n",
cerion5b2325f2005-12-23 00:55:09 +00002663 flag_OE ? "o" : "", flag_rC ? ".":"",
cerion76de5cf2005-11-18 18:25:12 +00002664 rD_addr, rA_addr, rB_addr);
cerion2831b002005-11-30 19:55:22 +00002665 if (mode64) {
cerionbb01b7c2005-12-16 13:40:18 +00002666 /* rD[hi32] are undefined: setting them to sign of lo32
2667 - set_XER_OV() and set_CR0() depend on this */
2668 IRExpr *a = unop(Iop_64to32, mkexpr(rA) );
2669 IRExpr *b = unop(Iop_64to32, mkexpr(rB) );
2670 assign( rD, binop(Iop_MullS32, a, b) );
2671 if (flag_OE) {
cerion5b2325f2005-12-23 00:55:09 +00002672 set_XER_OV( ty, PPCG_FLAG_OP_MULLW,
cerionbb01b7c2005-12-16 13:40:18 +00002673 mkexpr(rD),
2674 unop(Iop_32Uto64, a), unop(Iop_32Uto64, b) );
2675 }
cerion2831b002005-11-30 19:55:22 +00002676 } else {
ceriond953ebb2005-11-29 13:27:20 +00002677 assign( rD, unop(Iop_64to32,
2678 binop(Iop_MullU32,
2679 mkexpr(rA), mkexpr(rB))) );
cerionbb01b7c2005-12-16 13:40:18 +00002680 if (flag_OE) {
cerion5b2325f2005-12-23 00:55:09 +00002681 set_XER_OV( ty, PPCG_FLAG_OP_MULLW,
cerionbb01b7c2005-12-16 13:40:18 +00002682 mkexpr(rD), mkexpr(rA), mkexpr(rB) );
2683 }
cerion70e24122005-03-16 00:27:37 +00002684 }
cerionb85e8bb2005-02-16 08:54:33 +00002685 break;
cerionc19d5e12005-02-01 15:56:25 +00002686
cerione9d361a2005-03-04 17:35:29 +00002687 case 0x068: // neg (Negate, PPC32 p493)
cerion76de5cf2005-11-18 18:25:12 +00002688 if (rB_addr != 0) {
cerion5b2325f2005-12-23 00:55:09 +00002689 vex_printf("dis_int_arith(ppc)(neg,rB_addr)\n");
cerionb85e8bb2005-02-16 08:54:33 +00002690 return False;
2691 }
ceriond953ebb2005-11-29 13:27:20 +00002692 DIP("neg%s%s r%u,r%u\n",
cerion5b2325f2005-12-23 00:55:09 +00002693 flag_OE ? "o" : "", flag_rC ? ".":"",
cerion76de5cf2005-11-18 18:25:12 +00002694 rD_addr, rA_addr);
ceriond953ebb2005-11-29 13:27:20 +00002695 // rD = (~rA) + 1
cerion2831b002005-11-30 19:55:22 +00002696 assign( rD, binop( mkSzOp(ty, Iop_Add8),
2697 unop( mkSzOp(ty, Iop_Not8), mkexpr(rA) ),
2698 mkSzImm(ty, 1)) );
cerion70e24122005-03-16 00:27:37 +00002699 if (flag_OE) {
cerion5b2325f2005-12-23 00:55:09 +00002700 set_XER_OV( ty, PPCG_FLAG_OP_NEG,
cerion76de5cf2005-11-18 18:25:12 +00002701 mkexpr(rD), mkexpr(rA), mkexpr(rB) );
cerion70e24122005-03-16 00:27:37 +00002702 }
cerionb85e8bb2005-02-16 08:54:33 +00002703 break;
cerion91ad5362005-01-27 23:02:41 +00002704
cerione9d361a2005-03-04 17:35:29 +00002705 case 0x028: // subf (Subtract From, PPC32 p537)
ceriond953ebb2005-11-29 13:27:20 +00002706 DIP("subf%s%s r%u,r%u,r%u\n",
cerion5b2325f2005-12-23 00:55:09 +00002707 flag_OE ? "o" : "", flag_rC ? ".":"",
cerion76de5cf2005-11-18 18:25:12 +00002708 rD_addr, rA_addr, rB_addr);
cerion01908472005-02-25 16:43:08 +00002709 // rD = rB - rA
cerion2831b002005-11-30 19:55:22 +00002710 assign( rD, binop( mkSzOp(ty, Iop_Sub8),
ceriond953ebb2005-11-29 13:27:20 +00002711 mkexpr(rB), mkexpr(rA)) );
cerion70e24122005-03-16 00:27:37 +00002712 if (flag_OE) {
cerion5b2325f2005-12-23 00:55:09 +00002713 set_XER_OV( ty, PPCG_FLAG_OP_SUBF,
cerion76de5cf2005-11-18 18:25:12 +00002714 mkexpr(rD), mkexpr(rA), mkexpr(rB) );
cerion70e24122005-03-16 00:27:37 +00002715 }
cerionb85e8bb2005-02-16 08:54:33 +00002716 break;
cerion38674602005-02-08 02:19:25 +00002717
cerione9d361a2005-03-04 17:35:29 +00002718 case 0x008: // subfc (Subtract from Carrying, PPC32 p538)
ceriond953ebb2005-11-29 13:27:20 +00002719 DIP("subfc%s%s r%u,r%u,r%u\n",
cerion5b2325f2005-12-23 00:55:09 +00002720 flag_OE ? "o" : "", flag_rC ? ".":"",
cerion76de5cf2005-11-18 18:25:12 +00002721 rD_addr, rA_addr, rB_addr);
cerion01908472005-02-25 16:43:08 +00002722 // rD = rB - rA
cerion2831b002005-11-30 19:55:22 +00002723 assign( rD, binop( mkSzOp(ty, Iop_Sub8),
ceriond953ebb2005-11-29 13:27:20 +00002724 mkexpr(rB), mkexpr(rA)) );
cerion5b2325f2005-12-23 00:55:09 +00002725 set_XER_CA( ty, PPCG_FLAG_OP_SUBFC,
cerion76de5cf2005-11-18 18:25:12 +00002726 mkexpr(rD), mkexpr(rA), mkexpr(rB),
cerion2831b002005-11-30 19:55:22 +00002727 mkSzImm(ty, 0)/*old xer.ca, which is ignored*/ );
cerion70e24122005-03-16 00:27:37 +00002728 if (flag_OE) {
cerion5b2325f2005-12-23 00:55:09 +00002729 set_XER_OV( ty, PPCG_FLAG_OP_SUBFC,
cerion76de5cf2005-11-18 18:25:12 +00002730 mkexpr(rD), mkexpr(rA), mkexpr(rB) );
cerion70e24122005-03-16 00:27:37 +00002731 }
cerionb85e8bb2005-02-16 08:54:33 +00002732 break;
2733
sewardjb51f0f42005-07-18 11:38:02 +00002734 case 0x088: {// subfe (Subtract from Extended, PPC32 p539)
cerion2831b002005-11-30 19:55:22 +00002735 IRTemp old_xer_ca = newTemp(ty);
ceriond953ebb2005-11-29 13:27:20 +00002736 DIP("subfe%s%s r%u,r%u,r%u\n",
cerion5b2325f2005-12-23 00:55:09 +00002737 flag_OE ? "o" : "", flag_rC ? ".":"",
cerion76de5cf2005-11-18 18:25:12 +00002738 rD_addr, rA_addr, rB_addr);
cerionb85e8bb2005-02-16 08:54:33 +00002739 // rD = (log not)rA + rB + XER[CA]
cerion2831b002005-11-30 19:55:22 +00002740 assign( old_xer_ca, mkSzWiden32(ty, getXER_CA32(), False) );
2741 assign( rD, binop( mkSzOp(ty, Iop_Add8),
2742 unop( mkSzOp(ty, Iop_Not8), mkexpr(rA)),
2743 binop( mkSzOp(ty, Iop_Add8),
2744 mkexpr(rB), mkexpr(old_xer_ca))) );
cerion5b2325f2005-12-23 00:55:09 +00002745 set_XER_CA( ty, PPCG_FLAG_OP_SUBFE,
cerion76de5cf2005-11-18 18:25:12 +00002746 mkexpr(rD), mkexpr(rA), mkexpr(rB),
cerion2831b002005-11-30 19:55:22 +00002747 mkexpr(old_xer_ca) );
cerion70e24122005-03-16 00:27:37 +00002748 if (flag_OE) {
cerion5b2325f2005-12-23 00:55:09 +00002749 set_XER_OV( ty, PPCG_FLAG_OP_SUBFE,
cerion76de5cf2005-11-18 18:25:12 +00002750 mkexpr(rD), mkexpr(rA), mkexpr(rB) );
cerion70e24122005-03-16 00:27:37 +00002751 }
cerionb85e8bb2005-02-16 08:54:33 +00002752 break;
sewardjb51f0f42005-07-18 11:38:02 +00002753 }
2754
cerion5b2325f2005-12-23 00:55:09 +00002755 case 0x0E8: { // subfme (Subtract from -1 Extended, PPC32 p541)
cerion2831b002005-11-30 19:55:22 +00002756 IRTemp old_xer_ca = newTemp(ty);
2757 IRExpr *min_one;
cerion76de5cf2005-11-18 18:25:12 +00002758 if (rB_addr != 0) {
cerion5b2325f2005-12-23 00:55:09 +00002759 vex_printf("dis_int_arith(ppc)(subfme,rB_addr)\n");
sewardj20ef5472005-07-21 14:48:31 +00002760 return False;
2761 }
ceriond953ebb2005-11-29 13:27:20 +00002762 DIP("subfme%s%s r%u,r%u\n",
cerion5b2325f2005-12-23 00:55:09 +00002763 flag_OE ? "o" : "", flag_rC ? ".":"",
cerion76de5cf2005-11-18 18:25:12 +00002764 rD_addr, rA_addr);
sewardj20ef5472005-07-21 14:48:31 +00002765 // rD = (log not)rA + (-1) + XER[CA]
2766 // => Just another form of subfe
cerion2831b002005-11-30 19:55:22 +00002767 assign( old_xer_ca, mkSzWiden32(ty, getXER_CA32(), False) );
2768 min_one = mkSzImm(ty, (Long)-1);
2769 assign( rD, binop( mkSzOp(ty, Iop_Add8),
2770 unop( mkSzOp(ty, Iop_Not8), mkexpr(rA)),
2771 binop( mkSzOp(ty, Iop_Add8),
2772 min_one, mkexpr(old_xer_ca))) );
cerion5b2325f2005-12-23 00:55:09 +00002773 set_XER_CA( ty, PPCG_FLAG_OP_SUBFE,
ceriond953ebb2005-11-29 13:27:20 +00002774 mkexpr(rD), mkexpr(rA), min_one,
cerion2831b002005-11-30 19:55:22 +00002775 mkexpr(old_xer_ca) );
sewardj20ef5472005-07-21 14:48:31 +00002776 if (flag_OE) {
cerion5b2325f2005-12-23 00:55:09 +00002777 set_XER_OV( ty, PPCG_FLAG_OP_SUBFE,
ceriond953ebb2005-11-29 13:27:20 +00002778 mkexpr(rD), mkexpr(rA), min_one );
sewardj20ef5472005-07-21 14:48:31 +00002779 }
2780 break;
2781 }
2782
cerion5b2325f2005-12-23 00:55:09 +00002783 case 0x0C8: { // subfze (Subtract from Zero Extended, PPC32 p542)
cerion2831b002005-11-30 19:55:22 +00002784 IRTemp old_xer_ca = newTemp(ty);
cerion76de5cf2005-11-18 18:25:12 +00002785 if (rB_addr != 0) {
cerion5b2325f2005-12-23 00:55:09 +00002786 vex_printf("dis_int_arith(ppc)(subfze,rB_addr)\n");
cerionb85e8bb2005-02-16 08:54:33 +00002787 return False;
2788 }
ceriond953ebb2005-11-29 13:27:20 +00002789 DIP("subfze%s%s r%u,r%u\n",
cerion5b2325f2005-12-23 00:55:09 +00002790 flag_OE ? "o" : "", flag_rC ? ".":"",
cerion76de5cf2005-11-18 18:25:12 +00002791 rD_addr, rA_addr);
cerion70e24122005-03-16 00:27:37 +00002792 // rD = (log not)rA + (0) + XER[CA]
2793 // => Just another form of subfe
cerion2831b002005-11-30 19:55:22 +00002794 assign( old_xer_ca, mkSzWiden32(ty, getXER_CA32(), False) );
2795 assign( rD, binop( mkSzOp(ty, Iop_Add8),
2796 unop( mkSzOp(ty, Iop_Not8),
2797 mkexpr(rA)), mkexpr(old_xer_ca)) );
cerion5b2325f2005-12-23 00:55:09 +00002798 set_XER_CA( ty, PPCG_FLAG_OP_SUBFE,
cerion2831b002005-11-30 19:55:22 +00002799 mkexpr(rD), mkexpr(rA), mkSzImm(ty, 0),
2800 mkexpr(old_xer_ca) );
cerion70e24122005-03-16 00:27:37 +00002801 if (flag_OE) {
cerion5b2325f2005-12-23 00:55:09 +00002802 set_XER_OV( ty, PPCG_FLAG_OP_SUBFE,
cerion2831b002005-11-30 19:55:22 +00002803 mkexpr(rD), mkexpr(rA), mkSzImm(ty, 0) );
cerion70e24122005-03-16 00:27:37 +00002804 }
cerionb85e8bb2005-02-16 08:54:33 +00002805 break;
sewardjb51f0f42005-07-18 11:38:02 +00002806 }
cerionae694622005-01-28 17:52:47 +00002807
cerionf0de28c2005-12-13 20:21:11 +00002808
2809 /* 64bit Arithmetic */
cerion5b2325f2005-12-23 00:55:09 +00002810 case 0x49: // mulhd (Multiply High DWord, PPC64 p539)
cerionf0de28c2005-12-13 20:21:11 +00002811 if (flag_OE != 0) {
cerion5b2325f2005-12-23 00:55:09 +00002812 vex_printf("dis_int_arith(ppc)(mulhd,flagOE)\n");
cerionf0de28c2005-12-13 20:21:11 +00002813 return False;
2814 }
cerion5b2325f2005-12-23 00:55:09 +00002815 DIP("mulhd%s r%u,r%u,r%u\n", flag_rC ? ".":"",
cerionf0de28c2005-12-13 20:21:11 +00002816 rD_addr, rA_addr, rB_addr);
cerionf0de28c2005-12-13 20:21:11 +00002817 assign( rD, unop(Iop_128HIto64,
cerion07b07a92005-12-22 14:32:35 +00002818 binop(Iop_MullS64,
cerionf0de28c2005-12-13 20:21:11 +00002819 mkexpr(rA), mkexpr(rB))) );
cerion07b07a92005-12-22 14:32:35 +00002820
2821 break;
cerionf0de28c2005-12-13 20:21:11 +00002822
cerion5b2325f2005-12-23 00:55:09 +00002823 case 0x9: // mulhdu (Multiply High DWord Unsigned, PPC64 p540)
cerionf0de28c2005-12-13 20:21:11 +00002824 if (flag_OE != 0) {
cerion5b2325f2005-12-23 00:55:09 +00002825 vex_printf("dis_int_arith(ppc)(mulhdu,flagOE)\n");
cerionf0de28c2005-12-13 20:21:11 +00002826 return False;
2827 }
cerion5b2325f2005-12-23 00:55:09 +00002828 DIP("mulhdu%s r%u,r%u,r%u\n", flag_rC ? ".":"",
cerionf0de28c2005-12-13 20:21:11 +00002829 rD_addr, rA_addr, rB_addr);
cerion07b07a92005-12-22 14:32:35 +00002830 assign( rD, unop(Iop_128HIto64,
2831 binop(Iop_MullU64,
2832 mkexpr(rA), mkexpr(rB))) );
2833 break;
cerionf0de28c2005-12-13 20:21:11 +00002834
cerion5b2325f2005-12-23 00:55:09 +00002835 case 0xE9: // mulld (Multiply Low DWord, PPC64 p543)
cerionf0de28c2005-12-13 20:21:11 +00002836 DIP("mulld%s%s r%u,r%u,r%u\n",
cerion5b2325f2005-12-23 00:55:09 +00002837 flag_OE ? "o" : "", flag_rC ? ".":"",
cerionf0de28c2005-12-13 20:21:11 +00002838 rD_addr, rA_addr, rB_addr);
2839 assign( rD, binop(Iop_Mul64, mkexpr(rA), mkexpr(rB)) );
2840 if (flag_OE) {
cerion5b2325f2005-12-23 00:55:09 +00002841 set_XER_OV( ty, PPCG_FLAG_OP_MULLW,
cerionf0de28c2005-12-13 20:21:11 +00002842 mkexpr(rD), mkexpr(rA), mkexpr(rB) );
2843 }
2844 break;
2845
cerion5b2325f2005-12-23 00:55:09 +00002846 case 0x1E9: // divd (Divide DWord, PPC64 p419)
cerionf0de28c2005-12-13 20:21:11 +00002847 DIP("divd%s%s r%u,r%u,r%u\n",
cerion5b2325f2005-12-23 00:55:09 +00002848 flag_OE ? "o" : "", flag_rC ? ".":"",
cerionf0de28c2005-12-13 20:21:11 +00002849 rD_addr, rA_addr, rB_addr);
cerion07b07a92005-12-22 14:32:35 +00002850 assign( rD, binop(Iop_DivS64, mkexpr(rA), mkexpr(rB)) );
2851 if (flag_OE) {
cerion5b2325f2005-12-23 00:55:09 +00002852 set_XER_OV( ty, PPCG_FLAG_OP_DIVW,
cerion07b07a92005-12-22 14:32:35 +00002853 mkexpr(rD), mkexpr(rA), mkexpr(rB) );
2854 }
2855 break;
cerionf0de28c2005-12-13 20:21:11 +00002856 /* Note:
cerion07b07a92005-12-22 14:32:35 +00002857 if (0x8000_0000_0000_0000 / -1) or (x / 0)
cerionf0de28c2005-12-13 20:21:11 +00002858 => rD=undef, if(flag_rC) CR7=undef, if(flag_OE) XER_OV=1
2859 => But _no_ exception raised. */
2860
cerion5b2325f2005-12-23 00:55:09 +00002861 case 0x1C9: // divdu (Divide DWord Unsigned, PPC64 p420)
cerionf0de28c2005-12-13 20:21:11 +00002862 DIP("divdu%s%s r%u,r%u,r%u\n",
cerion5b2325f2005-12-23 00:55:09 +00002863 flag_OE ? "o" : "", flag_rC ? ".":"",
cerionf0de28c2005-12-13 20:21:11 +00002864 rD_addr, rA_addr, rB_addr);
cerion07b07a92005-12-22 14:32:35 +00002865 assign( rD, binop(Iop_DivU64, mkexpr(rA), mkexpr(rB)) );
2866 if (flag_OE) {
cerion5b2325f2005-12-23 00:55:09 +00002867 set_XER_OV( ty, PPCG_FLAG_OP_DIVWU,
cerion07b07a92005-12-22 14:32:35 +00002868 mkexpr(rD), mkexpr(rA), mkexpr(rB) );
2869 }
2870 break;
cerionf0de28c2005-12-13 20:21:11 +00002871 /* Note: ditto comment divd, for (x / 0) */
2872
cerionb85e8bb2005-02-16 08:54:33 +00002873 default:
cerion5b2325f2005-12-23 00:55:09 +00002874 vex_printf("dis_int_arith(ppc)(opc2)\n");
cerionb85e8bb2005-02-16 08:54:33 +00002875 return False;
2876 }
2877 break;
cerionf0de28c2005-12-13 20:21:11 +00002878
cerionb85e8bb2005-02-16 08:54:33 +00002879 default:
cerion5b2325f2005-12-23 00:55:09 +00002880 vex_printf("dis_int_arith(ppc)(opc1)\n");
cerionb85e8bb2005-02-16 08:54:33 +00002881 return False;
2882 }
cerion91ad5362005-01-27 23:02:41 +00002883
cerion76de5cf2005-11-18 18:25:12 +00002884 putIReg( rD_addr, mkexpr(rD) );
2885
2886 if (do_rc && flag_rC) {
2887 set_CR0( mkexpr(rD) );
cerionb85e8bb2005-02-16 08:54:33 +00002888 }
2889 return True;
cerion91ad5362005-01-27 23:02:41 +00002890}
2891
2892
2893
cerion3d870a32005-03-18 12:23:33 +00002894/*
2895 Integer Compare Instructions
2896*/
cerion7aa4bbc2005-01-29 09:32:07 +00002897static Bool dis_int_cmp ( UInt theInstr )
2898{
cerion76de5cf2005-11-18 18:25:12 +00002899 /* D-Form, X-Form */
2900 UChar opc1 = ifieldOPC(theInstr);
2901 UChar crfD = toUChar( IFIELD( theInstr, 23, 3 ) );
2902 UChar b22 = toUChar( IFIELD( theInstr, 22, 1 ) );
2903 UChar flag_L = toUChar( IFIELD( theInstr, 21, 1 ) );
2904 UChar rA_addr = ifieldRegA(theInstr);
ceriond953ebb2005-11-29 13:27:20 +00002905 UInt uimm16 = ifieldUIMM16(theInstr);
cerion76de5cf2005-11-18 18:25:12 +00002906 UChar rB_addr = ifieldRegB(theInstr);
2907 UInt opc2 = ifieldOPClo10(theInstr);
2908 UChar b0 = ifieldBIT0(theInstr);
cerion7aa4bbc2005-01-29 09:32:07 +00002909
cerionbb01b7c2005-12-16 13:40:18 +00002910 IRType ty = mode64 ? Ity_I64 : Ity_I32;
2911 IRExpr *a = getIReg(rA_addr);
2912 IRExpr *b;
cerion76de5cf2005-11-18 18:25:12 +00002913
ceriond953ebb2005-11-29 13:27:20 +00002914 if (!mode64 && flag_L==1) { // L==1 invalid for 32 bit.
cerion5b2325f2005-12-23 00:55:09 +00002915 vex_printf("dis_int_cmp(ppc)(flag_L)\n");
cerionb85e8bb2005-02-16 08:54:33 +00002916 return False;
2917 }
2918
cerion76de5cf2005-11-18 18:25:12 +00002919 if (b22 != 0) {
cerion5b2325f2005-12-23 00:55:09 +00002920 vex_printf("dis_int_cmp(ppc)(b22)\n");
cerionb85e8bb2005-02-16 08:54:33 +00002921 return False;
2922 }
2923
2924 switch (opc1) {
ceriond953ebb2005-11-29 13:27:20 +00002925 case 0x0B: // cmpi (Compare Immediate, PPC32 p368)
2926 DIP("cmpi cr%u,%u,r%u,%d\n", crfD, flag_L, rA_addr,
2927 (Int)extend_s_16to32(uimm16));
cerion2831b002005-11-30 19:55:22 +00002928 b = mkSzExtendS16( ty, uimm16 );
cerionbb01b7c2005-12-16 13:40:18 +00002929 if (flag_L == 1) {
cerion2831b002005-11-30 19:55:22 +00002930 putCR321(crfD, unop(Iop_64to8, binop(Iop_CmpORD64S, a, b)));
ceriond953ebb2005-11-29 13:27:20 +00002931 } else {
cerionbb01b7c2005-12-16 13:40:18 +00002932 a = mkSzNarrow32( ty, a );
2933 b = mkSzNarrow32( ty, b );
ceriond953ebb2005-11-29 13:27:20 +00002934 putCR321(crfD, unop(Iop_32to8, binop(Iop_CmpORD32S, a, b)));
2935 }
2936 putCR0( crfD, getXER_SO() );
2937 break;
2938
2939 case 0x0A: // cmpli (Compare Logical Immediate, PPC32 p370)
2940 DIP("cmpli cr%u,%u,r%u,0x%x\n", crfD, flag_L, rA_addr, uimm16);
cerion2831b002005-11-30 19:55:22 +00002941 b = mkSzImm( ty, uimm16 );
cerionbb01b7c2005-12-16 13:40:18 +00002942 if (flag_L == 1) {
cerion2831b002005-11-30 19:55:22 +00002943 putCR321(crfD, unop(Iop_64to8, binop(Iop_CmpORD64U, a, b)));
ceriond953ebb2005-11-29 13:27:20 +00002944 } else {
cerionbb01b7c2005-12-16 13:40:18 +00002945 a = mkSzNarrow32( ty, a );
2946 b = mkSzNarrow32( ty, b );
ceriond953ebb2005-11-29 13:27:20 +00002947 putCR321(crfD, unop(Iop_32to8, binop(Iop_CmpORD32U, a, b)));
2948 }
2949 putCR0( crfD, getXER_SO() );
2950 break;
cerionb85e8bb2005-02-16 08:54:33 +00002951
2952 /* X Form */
2953 case 0x1F:
2954 if (b0 != 0) {
cerion5b2325f2005-12-23 00:55:09 +00002955 vex_printf("dis_int_cmp(ppc)(0x1F,b0)\n");
cerionb85e8bb2005-02-16 08:54:33 +00002956 return False;
2957 }
cerionbb01b7c2005-12-16 13:40:18 +00002958 b = getIReg(rB_addr);
cerion7aa4bbc2005-01-29 09:32:07 +00002959
cerionb85e8bb2005-02-16 08:54:33 +00002960 switch (opc2) {
ceriond953ebb2005-11-29 13:27:20 +00002961 case 0x000: // cmp (Compare, PPC32 p367)
2962 DIP("cmp cr%u,%u,r%u,r%u\n", crfD, flag_L, rA_addr, rB_addr);
sewardja9cb67b2006-08-19 18:31:53 +00002963 /* Comparing a reg with itself produces a result which
2964 doesn't depend on the contents of the reg. Therefore
2965 remove the false dependency, which has been known to cause
2966 memcheck to produce false errors. */
sewardj9195aa12006-08-19 22:18:53 +00002967 if (rA_addr == rB_addr)
2968 a = b = typeOfIRExpr(irbb->tyenv,a) == Ity_I64
2969 ? mkU64(0) : mkU32(0);
cerionbb01b7c2005-12-16 13:40:18 +00002970 if (flag_L == 1) {
2971 putCR321(crfD, unop(Iop_64to8, binop(Iop_CmpORD64S, a, b)));
ceriond953ebb2005-11-29 13:27:20 +00002972 } else {
cerionbb01b7c2005-12-16 13:40:18 +00002973 a = mkSzNarrow32( ty, a );
2974 b = mkSzNarrow32( ty, b );
2975 putCR321(crfD, unop(Iop_32to8,binop(Iop_CmpORD32S, a, b)));
ceriond953ebb2005-11-29 13:27:20 +00002976 }
2977 putCR0( crfD, getXER_SO() );
2978 break;
cerionb85e8bb2005-02-16 08:54:33 +00002979
ceriond953ebb2005-11-29 13:27:20 +00002980 case 0x020: // cmpl (Compare Logical, PPC32 p369)
2981 DIP("cmpl cr%u,%u,r%u,r%u\n", crfD, flag_L, rA_addr, rB_addr);
sewardja9cb67b2006-08-19 18:31:53 +00002982 /* Comparing a reg with itself produces a result which
2983 doesn't depend on the contents of the reg. Therefore
2984 remove the false dependency, which has been known to cause
2985 memcheck to produce false errors. */
sewardj9195aa12006-08-19 22:18:53 +00002986 if (rA_addr == rB_addr)
2987 a = b = typeOfIRExpr(irbb->tyenv,a) == Ity_I64
2988 ? mkU64(0) : mkU32(0);
cerionbb01b7c2005-12-16 13:40:18 +00002989 if (flag_L == 1) {
2990 putCR321(crfD, unop(Iop_64to8, binop(Iop_CmpORD64U, a, b)));
ceriond953ebb2005-11-29 13:27:20 +00002991 } else {
cerionbb01b7c2005-12-16 13:40:18 +00002992 a = mkSzNarrow32( ty, a );
2993 b = mkSzNarrow32( ty, b );
2994 putCR321(crfD, unop(Iop_32to8, binop(Iop_CmpORD32U, a, b)));
ceriond953ebb2005-11-29 13:27:20 +00002995 }
2996 putCR0( crfD, getXER_SO() );
2997 break;
cerion7aa4bbc2005-01-29 09:32:07 +00002998
ceriond953ebb2005-11-29 13:27:20 +00002999 default:
cerion5b2325f2005-12-23 00:55:09 +00003000 vex_printf("dis_int_cmp(ppc)(opc2)\n");
ceriond953ebb2005-11-29 13:27:20 +00003001 return False;
cerionb85e8bb2005-02-16 08:54:33 +00003002 }
3003 break;
ceriond953ebb2005-11-29 13:27:20 +00003004
cerionb85e8bb2005-02-16 08:54:33 +00003005 default:
cerion5b2325f2005-12-23 00:55:09 +00003006 vex_printf("dis_int_cmp(ppc)(opc1)\n");
cerionb85e8bb2005-02-16 08:54:33 +00003007 return False;
3008 }
3009
cerionb85e8bb2005-02-16 08:54:33 +00003010 return True;
cerion7aa4bbc2005-01-29 09:32:07 +00003011}
3012
3013
cerion3d870a32005-03-18 12:23:33 +00003014/*
3015 Integer Logical Instructions
3016*/
cerion7aa4bbc2005-01-29 09:32:07 +00003017static Bool dis_int_logic ( UInt theInstr )
3018{
cerion76de5cf2005-11-18 18:25:12 +00003019 /* D-Form, X-Form */
3020 UChar opc1 = ifieldOPC(theInstr);
3021 UChar rS_addr = ifieldRegDS(theInstr);
3022 UChar rA_addr = ifieldRegA(theInstr);
ceriond953ebb2005-11-29 13:27:20 +00003023 UInt uimm16 = ifieldUIMM16(theInstr);
cerion76de5cf2005-11-18 18:25:12 +00003024 UChar rB_addr = ifieldRegB(theInstr);
3025 UInt opc2 = ifieldOPClo10(theInstr);
3026 UChar flag_rC = ifieldBIT0(theInstr);
cerionb85e8bb2005-02-16 08:54:33 +00003027
ceriond953ebb2005-11-29 13:27:20 +00003028 IRType ty = mode64 ? Ity_I64 : Ity_I32;
3029 IRTemp rS = newTemp(ty);
3030 IRTemp rA = newTemp(ty);
3031 IRTemp rB = newTemp(ty);
cerione9d361a2005-03-04 17:35:29 +00003032 IRExpr* irx;
ceriond953ebb2005-11-29 13:27:20 +00003033 Bool do_rc = False;
3034
cerion76de5cf2005-11-18 18:25:12 +00003035 assign( rS, getIReg(rS_addr) );
3036 assign( rB, getIReg(rB_addr) );
cerionb85e8bb2005-02-16 08:54:33 +00003037
3038 switch (opc1) {
cerione9d361a2005-03-04 17:35:29 +00003039 case 0x1C: // andi. (AND Immediate, PPC32 p358)
ceriond953ebb2005-11-29 13:27:20 +00003040 DIP("andi. r%u,r%u,0x%x\n", rA_addr, rS_addr, uimm16);
cerion2831b002005-11-30 19:55:22 +00003041 assign( rA, binop( mkSzOp(ty, Iop_And8), mkexpr(rS),
3042 mkSzImm(ty, uimm16)) );
cerion70e24122005-03-16 00:27:37 +00003043 do_rc = True; // Always record to CR
cerion76de5cf2005-11-18 18:25:12 +00003044 flag_rC = 1;
cerionb85e8bb2005-02-16 08:54:33 +00003045 break;
3046
cerione9d361a2005-03-04 17:35:29 +00003047 case 0x1D: // andis. (AND Immediate Shifted, PPC32 p359)
ceriond953ebb2005-11-29 13:27:20 +00003048 DIP("andis r%u,r%u,0x%x\n", rA_addr, rS_addr, uimm16);
cerion2831b002005-11-30 19:55:22 +00003049 assign( rA, binop( mkSzOp(ty, Iop_And8), mkexpr(rS),
3050 mkSzImm(ty, uimm16 << 16)) );
cerion70e24122005-03-16 00:27:37 +00003051 do_rc = True; // Always record to CR
cerion76de5cf2005-11-18 18:25:12 +00003052 flag_rC = 1;
cerionb85e8bb2005-02-16 08:54:33 +00003053 break;
cerion7aa4bbc2005-01-29 09:32:07 +00003054
cerione9d361a2005-03-04 17:35:29 +00003055 case 0x18: // ori (OR Immediate, PPC32 p497)
ceriond953ebb2005-11-29 13:27:20 +00003056 DIP("ori r%u,r%u,0x%x\n", rA_addr, rS_addr, uimm16);
cerion2831b002005-11-30 19:55:22 +00003057 assign( rA, binop( mkSzOp(ty, Iop_Or8), mkexpr(rS),
3058 mkSzImm(ty, uimm16)) );
cerionb85e8bb2005-02-16 08:54:33 +00003059 break;
cerion7aa4bbc2005-01-29 09:32:07 +00003060
cerione9d361a2005-03-04 17:35:29 +00003061 case 0x19: // oris (OR Immediate Shifted, PPC32 p498)
ceriond953ebb2005-11-29 13:27:20 +00003062 DIP("oris r%u,r%u,0x%x\n", rA_addr, rS_addr, uimm16);
cerion2831b002005-11-30 19:55:22 +00003063 assign( rA, binop( mkSzOp(ty, Iop_Or8), mkexpr(rS),
3064 mkSzImm(ty, uimm16 << 16)) );
cerionb85e8bb2005-02-16 08:54:33 +00003065 break;
cerionaabdfbf2005-01-29 12:56:15 +00003066
cerione9d361a2005-03-04 17:35:29 +00003067 case 0x1A: // xori (XOR Immediate, PPC32 p550)
ceriond953ebb2005-11-29 13:27:20 +00003068 DIP("xori r%u,r%u,0x%x\n", rA_addr, rS_addr, uimm16);
cerion2831b002005-11-30 19:55:22 +00003069 assign( rA, binop( mkSzOp(ty, Iop_Xor8), mkexpr(rS),
3070 mkSzImm(ty, uimm16)) );
cerionb85e8bb2005-02-16 08:54:33 +00003071 break;
cerion38674602005-02-08 02:19:25 +00003072
cerione9d361a2005-03-04 17:35:29 +00003073 case 0x1B: // xoris (XOR Immediate Shifted, PPC32 p551)
ceriond953ebb2005-11-29 13:27:20 +00003074 DIP("xoris r%u,r%u,0x%x\n", rA_addr, rS_addr, uimm16);
cerion2831b002005-11-30 19:55:22 +00003075 assign( rA, binop( mkSzOp(ty, Iop_Xor8), mkexpr(rS),
3076 mkSzImm(ty, uimm16 << 16)) );
cerionb85e8bb2005-02-16 08:54:33 +00003077 break;
cerionaabdfbf2005-01-29 12:56:15 +00003078
cerionb85e8bb2005-02-16 08:54:33 +00003079 /* X Form */
3080 case 0x1F:
cerion70e24122005-03-16 00:27:37 +00003081 do_rc = True; // All below record to CR
3082
cerionb85e8bb2005-02-16 08:54:33 +00003083 switch (opc2) {
ceriond953ebb2005-11-29 13:27:20 +00003084 case 0x01C: // and (AND, PPC32 p356)
3085 DIP("and%s r%u,r%u,r%u\n",
cerion5b2325f2005-12-23 00:55:09 +00003086 flag_rC ? ".":"", rA_addr, rS_addr, rB_addr);
cerion2831b002005-11-30 19:55:22 +00003087 assign(rA, binop( mkSzOp(ty, Iop_And8),
ceriond953ebb2005-11-29 13:27:20 +00003088 mkexpr(rS), mkexpr(rB)));
3089 break;
3090
3091 case 0x03C: // andc (AND with Complement, PPC32 p357)
3092 DIP("andc%s r%u,r%u,r%u\n",
cerion5b2325f2005-12-23 00:55:09 +00003093 flag_rC ? ".":"", rA_addr, rS_addr, rB_addr);
cerion2831b002005-11-30 19:55:22 +00003094 assign(rA, binop( mkSzOp(ty, Iop_And8), mkexpr(rS),
3095 unop( mkSzOp(ty, Iop_Not8),
ceriond953ebb2005-11-29 13:27:20 +00003096 mkexpr(rB))));
3097 break;
3098
3099 case 0x01A: { // cntlzw (Count Leading Zeros Word, PPC32 p371)
3100 IRExpr* lo32;
3101 if (rB_addr!=0) {
cerion5b2325f2005-12-23 00:55:09 +00003102 vex_printf("dis_int_logic(ppc)(cntlzw,rB_addr)\n");
ceriond953ebb2005-11-29 13:27:20 +00003103 return False;
3104 }
3105 DIP("cntlzw%s r%u,r%u\n",
cerion5b2325f2005-12-23 00:55:09 +00003106 flag_rC ? ".":"", rA_addr, rS_addr);
ceriond953ebb2005-11-29 13:27:20 +00003107
3108 // mode64: count in low word only
3109 lo32 = mode64 ? unop(Iop_64to32, mkexpr(rS)) : mkexpr(rS);
3110
3111 // Iop_Clz32 undefined for arg==0, so deal with that case:
3112 irx = binop(Iop_CmpNE32, lo32, mkU32(0));
cerion5b2325f2005-12-23 00:55:09 +00003113 assign(rA, mkSzWiden32(ty,
3114 IRExpr_Mux0X( unop(Iop_1Uto8, irx),
3115 mkU32(32),
3116 unop(Iop_Clz32, lo32)),
3117 False));
3118
ceriond953ebb2005-11-29 13:27:20 +00003119 // TODO: alternatively: assign(rA, verbose_Clz32(rS));
3120 break;
3121 }
3122
3123 case 0x11C: // eqv (Equivalent, PPC32 p396)
3124 DIP("eqv%s r%u,r%u,r%u\n",
cerion5b2325f2005-12-23 00:55:09 +00003125 flag_rC ? ".":"", rA_addr, rS_addr, rB_addr);
cerion2831b002005-11-30 19:55:22 +00003126 assign( rA, unop( mkSzOp(ty, Iop_Not8),
3127 binop( mkSzOp(ty, Iop_Xor8),
ceriond953ebb2005-11-29 13:27:20 +00003128 mkexpr(rS), mkexpr(rB))) );
sewardj20ef5472005-07-21 14:48:31 +00003129 break;
cerion7aa4bbc2005-01-29 09:32:07 +00003130
cerione9d361a2005-03-04 17:35:29 +00003131 case 0x3BA: // extsb (Extend Sign Byte, PPC32 p397
cerion76de5cf2005-11-18 18:25:12 +00003132 if (rB_addr!=0) {
cerion5b2325f2005-12-23 00:55:09 +00003133 vex_printf("dis_int_logic(ppc)(extsb,rB_addr)\n");
cerionb85e8bb2005-02-16 08:54:33 +00003134 return False;
3135 }
ceriond953ebb2005-11-29 13:27:20 +00003136 DIP("extsb%s r%u,r%u\n",
cerion5b2325f2005-12-23 00:55:09 +00003137 flag_rC ? ".":"", rA_addr, rS_addr);
ceriond953ebb2005-11-29 13:27:20 +00003138 if (mode64)
3139 assign( rA, unop(Iop_8Sto64, unop(Iop_64to8, mkexpr(rS))) );
3140 else
3141 assign( rA, unop(Iop_8Sto32, unop(Iop_32to8, mkexpr(rS))) );
cerionb85e8bb2005-02-16 08:54:33 +00003142 break;
cerion7aa4bbc2005-01-29 09:32:07 +00003143
cerione9d361a2005-03-04 17:35:29 +00003144 case 0x39A: // extsh (Extend Sign Half Word, PPC32 p398)
cerion76de5cf2005-11-18 18:25:12 +00003145 if (rB_addr!=0) {
cerion5b2325f2005-12-23 00:55:09 +00003146 vex_printf("dis_int_logic(ppc)(extsh,rB_addr)\n");
cerionb85e8bb2005-02-16 08:54:33 +00003147 return False;
3148 }
ceriond953ebb2005-11-29 13:27:20 +00003149 DIP("extsh%s r%u,r%u\n",
cerion5b2325f2005-12-23 00:55:09 +00003150 flag_rC ? ".":"", rA_addr, rS_addr);
ceriond953ebb2005-11-29 13:27:20 +00003151 if (mode64)
cerion5b2325f2005-12-23 00:55:09 +00003152 assign( rA, unop(Iop_16Sto64,
3153 unop(Iop_64to16, mkexpr(rS))) );
ceriond953ebb2005-11-29 13:27:20 +00003154 else
cerion5b2325f2005-12-23 00:55:09 +00003155 assign( rA, unop(Iop_16Sto32,
3156 unop(Iop_32to16, mkexpr(rS))) );
cerionb85e8bb2005-02-16 08:54:33 +00003157 break;
cerion7aa4bbc2005-01-29 09:32:07 +00003158
cerione9d361a2005-03-04 17:35:29 +00003159 case 0x1DC: // nand (NAND, PPC32 p492)
ceriond953ebb2005-11-29 13:27:20 +00003160 DIP("nand%s r%u,r%u,r%u\n",
cerion5b2325f2005-12-23 00:55:09 +00003161 flag_rC ? ".":"", rA_addr, rS_addr, rB_addr);
cerion2831b002005-11-30 19:55:22 +00003162 assign( rA, unop( mkSzOp(ty, Iop_Not8),
3163 binop( mkSzOp(ty, Iop_And8),
ceriond953ebb2005-11-29 13:27:20 +00003164 mkexpr(rS), mkexpr(rB))) );
cerionb85e8bb2005-02-16 08:54:33 +00003165 break;
3166
cerione9d361a2005-03-04 17:35:29 +00003167 case 0x07C: // nor (NOR, PPC32 p494)
ceriond953ebb2005-11-29 13:27:20 +00003168 DIP("nor%s r%u,r%u,r%u\n",
cerion5b2325f2005-12-23 00:55:09 +00003169 flag_rC ? ".":"", rA_addr, rS_addr, rB_addr);
cerion2831b002005-11-30 19:55:22 +00003170 assign( rA, unop( mkSzOp(ty, Iop_Not8),
3171 binop( mkSzOp(ty, Iop_Or8),
ceriond953ebb2005-11-29 13:27:20 +00003172 mkexpr(rS), mkexpr(rB))) );
cerionb85e8bb2005-02-16 08:54:33 +00003173 break;
cerion7aa4bbc2005-01-29 09:32:07 +00003174
cerione9d361a2005-03-04 17:35:29 +00003175 case 0x1BC: // or (OR, PPC32 p495)
cerion76de5cf2005-11-18 18:25:12 +00003176 if ((!flag_rC) && rS_addr == rB_addr) {
ceriond953ebb2005-11-29 13:27:20 +00003177 DIP("mr r%u,r%u\n", rA_addr, rS_addr);
cerion76de5cf2005-11-18 18:25:12 +00003178 assign( rA, mkexpr(rS) );
sewardjb51f0f42005-07-18 11:38:02 +00003179 } else {
ceriond953ebb2005-11-29 13:27:20 +00003180 DIP("or%s r%u,r%u,r%u\n",
cerion5b2325f2005-12-23 00:55:09 +00003181 flag_rC ? ".":"", rA_addr, rS_addr, rB_addr);
cerion2831b002005-11-30 19:55:22 +00003182 assign( rA, binop( mkSzOp(ty, Iop_Or8),
ceriond953ebb2005-11-29 13:27:20 +00003183 mkexpr(rS), mkexpr(rB)) );
sewardjb51f0f42005-07-18 11:38:02 +00003184 }
cerionb85e8bb2005-02-16 08:54:33 +00003185 break;
cerion7aa4bbc2005-01-29 09:32:07 +00003186
cerione9d361a2005-03-04 17:35:29 +00003187 case 0x19C: // orc (OR with Complement, PPC32 p496)
ceriond953ebb2005-11-29 13:27:20 +00003188 DIP("orc%s r%u,r%u,r%u\n",
cerion5b2325f2005-12-23 00:55:09 +00003189 flag_rC ? ".":"", rA_addr, rS_addr, rB_addr);
cerion2831b002005-11-30 19:55:22 +00003190 assign( rA, binop( mkSzOp(ty, Iop_Or8), mkexpr(rS),
3191 unop(mkSzOp(ty, Iop_Not8), mkexpr(rB))));
cerionb85e8bb2005-02-16 08:54:33 +00003192 break;
3193
cerione9d361a2005-03-04 17:35:29 +00003194 case 0x13C: // xor (XOR, PPC32 p549)
ceriond953ebb2005-11-29 13:27:20 +00003195 DIP("xor%s r%u,r%u,r%u\n",
cerion5b2325f2005-12-23 00:55:09 +00003196 flag_rC ? ".":"", rA_addr, rS_addr, rB_addr);
cerion2831b002005-11-30 19:55:22 +00003197 assign( rA, binop( mkSzOp(ty, Iop_Xor8),
ceriond953ebb2005-11-29 13:27:20 +00003198 mkexpr(rS), mkexpr(rB)) );
cerionb85e8bb2005-02-16 08:54:33 +00003199 break;
cerion7aa4bbc2005-01-29 09:32:07 +00003200
cerionf0de28c2005-12-13 20:21:11 +00003201
3202 /* 64bit Integer Logical Instructions */
3203 case 0x3DA: // extsw (Extend Sign Word, PPC64 p430)
3204 if (rB_addr!=0) {
cerion5b2325f2005-12-23 00:55:09 +00003205 vex_printf("dis_int_logic(ppc)(extsw,rB_addr)\n");
cerionf0de28c2005-12-13 20:21:11 +00003206 return False;
3207 }
cerion5b2325f2005-12-23 00:55:09 +00003208 DIP("extsw%s r%u,r%u\n", flag_rC ? ".":"", rA_addr, rS_addr);
cerionf0de28c2005-12-13 20:21:11 +00003209 assign(rA, unop(Iop_32Sto64, unop(Iop_64to32, mkexpr(rS))));
3210 break;
3211
cerion5b2325f2005-12-23 00:55:09 +00003212 case 0x03A: // cntlzd (Count Leading Zeros DWord, PPC64 p401)
cerionf0de28c2005-12-13 20:21:11 +00003213 if (rB_addr!=0) {
cerion5b2325f2005-12-23 00:55:09 +00003214 vex_printf("dis_int_logic(ppc)(cntlzd,rB_addr)\n");
cerionf0de28c2005-12-13 20:21:11 +00003215 return False;
3216 }
cerion5b2325f2005-12-23 00:55:09 +00003217 DIP("cntlzd%s r%u,r%u\n",
3218 flag_rC ? ".":"", rA_addr, rS_addr);
cerion07b07a92005-12-22 14:32:35 +00003219 // Iop_Clz64 undefined for arg==0, so deal with that case:
3220 irx = binop(Iop_CmpNE64, mkexpr(rS), mkU64(0));
3221 assign(rA, IRExpr_Mux0X( unop(Iop_1Uto8, irx),
3222 mkU64(64),
3223 unop(Iop_Clz64, mkexpr(rS)) ));
cerion5b2325f2005-12-23 00:55:09 +00003224 // TODO: alternatively: assign(rA, verbose_Clz64(rS));
cerion07b07a92005-12-22 14:32:35 +00003225 break;
cerionf0de28c2005-12-13 20:21:11 +00003226
cerionb85e8bb2005-02-16 08:54:33 +00003227 default:
cerion5b2325f2005-12-23 00:55:09 +00003228 vex_printf("dis_int_logic(ppc)(opc2)\n");
cerionb85e8bb2005-02-16 08:54:33 +00003229 return False;
3230 }
cerionb85e8bb2005-02-16 08:54:33 +00003231 break;
3232
3233 default:
cerion5b2325f2005-12-23 00:55:09 +00003234 vex_printf("dis_int_logic(ppc)(opc1)\n");
cerionb85e8bb2005-02-16 08:54:33 +00003235 return False;
3236 }
cerion70e24122005-03-16 00:27:37 +00003237
cerion76de5cf2005-11-18 18:25:12 +00003238 putIReg( rA_addr, mkexpr(rA) );
3239
3240 if (do_rc && flag_rC) {
3241 set_CR0( mkexpr(rA) );
cerionb85e8bb2005-02-16 08:54:33 +00003242 }
3243 return True;
cerion645c9302005-01-31 10:09:59 +00003244}
3245
3246
3247
cerion3d870a32005-03-18 12:23:33 +00003248/*
3249 Integer Rotate Instructions
3250*/
cerion645c9302005-01-31 10:09:59 +00003251static Bool dis_int_rot ( UInt theInstr )
3252{
cerionf0de28c2005-12-13 20:21:11 +00003253 /* M-Form, MDS-Form */
ceriond953ebb2005-11-29 13:27:20 +00003254 UChar opc1 = ifieldOPC(theInstr);
3255 UChar rS_addr = ifieldRegDS(theInstr);
3256 UChar rA_addr = ifieldRegA(theInstr);
3257 UChar rB_addr = ifieldRegB(theInstr);
3258 UChar sh_imm = rB_addr;
3259 UChar MaskBeg = toUChar( IFIELD( theInstr, 6, 5 ) );
3260 UChar MaskEnd = toUChar( IFIELD( theInstr, 1, 5 ) );
cerionf0de28c2005-12-13 20:21:11 +00003261 UChar msk_imm = toUChar( IFIELD( theInstr, 5, 6 ) );
3262 UChar opc2 = toUChar( IFIELD( theInstr, 2, 3 ) );
3263 UChar b1 = ifieldBIT1(theInstr);
ceriond953ebb2005-11-29 13:27:20 +00003264 UChar flag_rC = ifieldBIT0(theInstr);
cerion76de5cf2005-11-18 18:25:12 +00003265
ceriond953ebb2005-11-29 13:27:20 +00003266 IRType ty = mode64 ? Ity_I64 : Ity_I32;
3267 IRTemp rS = newTemp(ty);
3268 IRTemp rA = newTemp(ty);
3269 IRTemp rB = newTemp(ty);
cerionbb01b7c2005-12-16 13:40:18 +00003270 IRTemp rot = newTemp(ty);
ceriond953ebb2005-11-29 13:27:20 +00003271 IRExpr *r;
cerionf0de28c2005-12-13 20:21:11 +00003272 UInt mask32;
3273 ULong mask64;
ceriond953ebb2005-11-29 13:27:20 +00003274
cerion76de5cf2005-11-18 18:25:12 +00003275 assign( rS, getIReg(rS_addr) );
3276 assign( rB, getIReg(rB_addr) );
ceriond953ebb2005-11-29 13:27:20 +00003277
cerionb85e8bb2005-02-16 08:54:33 +00003278 switch (opc1) {
ceriond953ebb2005-11-29 13:27:20 +00003279 case 0x14: {
cerion5b2325f2005-12-23 00:55:09 +00003280 // rlwimi (Rotate Left Word Imm then Mask Insert, PPC32 p500)
3281 DIP("rlwimi%s r%u,r%u,%d,%d,%d\n", flag_rC ? ".":"",
ceriond953ebb2005-11-29 13:27:20 +00003282 rA_addr, rS_addr, sh_imm, MaskBeg, MaskEnd);
3283 if (mode64) {
3284 // tmp32 = (ROTL(rS_Lo32, Imm)
3285 // rA = ((tmp32 || tmp32) & mask64) | (rA & ~mask64)
cerionf0de28c2005-12-13 20:21:11 +00003286 mask64 = MASK64(31-MaskEnd, 31-MaskBeg);
ceriond953ebb2005-11-29 13:27:20 +00003287 r = ROTL( unop(Iop_64to32, mkexpr(rS) ), mkU8(sh_imm) );
3288 r = unop(Iop_32Uto64, r);
cerion5b2325f2005-12-23 00:55:09 +00003289 assign( rot, binop(Iop_Or64, r,
3290 binop(Iop_Shl64, r, mkU8(32))) );
ceriond953ebb2005-11-29 13:27:20 +00003291 assign( rA,
3292 binop(Iop_Or64,
cerionbb01b7c2005-12-16 13:40:18 +00003293 binop(Iop_And64, mkexpr(rot), mkU64(mask64)),
cerionf0de28c2005-12-13 20:21:11 +00003294 binop(Iop_And64, getIReg(rA_addr), mkU64(~mask64))) );
sewardj26b33202005-10-07 09:45:16 +00003295 }
3296 else {
ceriond953ebb2005-11-29 13:27:20 +00003297 // rA = (ROTL(rS, Imm) & mask) | (rA & ~mask);
cerionf0de28c2005-12-13 20:21:11 +00003298 mask32 = MASK32(31-MaskEnd, 31-MaskBeg);
3299 r = ROTL(mkexpr(rS), mkU8(sh_imm));
ceriond953ebb2005-11-29 13:27:20 +00003300 assign( rA,
3301 binop(Iop_Or32,
cerionf0de28c2005-12-13 20:21:11 +00003302 binop(Iop_And32, mkU32(mask32), r),
3303 binop(Iop_And32, getIReg(rA_addr), mkU32(~mask32))) );
sewardj26b33202005-10-07 09:45:16 +00003304 }
cerionb85e8bb2005-02-16 08:54:33 +00003305 break;
ceriond953ebb2005-11-29 13:27:20 +00003306 }
cerion45b70ff2005-01-31 17:03:25 +00003307
ceriond953ebb2005-11-29 13:27:20 +00003308 case 0x15: {
cerion5b2325f2005-12-23 00:55:09 +00003309 // rlwinm (Rotate Left Word Imm then AND with Mask, PPC32 p501)
ceriond953ebb2005-11-29 13:27:20 +00003310 vassert(MaskBeg < 32);
3311 vassert(MaskEnd < 32);
3312 vassert(sh_imm < 32);
3313
3314 if (mode64) {
sewardjef4433b2006-10-19 03:01:09 +00003315 IRTemp rTmp = newTemp(Ity_I64);
cerionf0de28c2005-12-13 20:21:11 +00003316 mask64 = MASK64(31-MaskEnd, 31-MaskBeg);
cerion5b2325f2005-12-23 00:55:09 +00003317 DIP("rlwinm%s r%u,r%u,%d,%d,%d\n", flag_rC ? ".":"",
ceriond953ebb2005-11-29 13:27:20 +00003318 rA_addr, rS_addr, sh_imm, MaskBeg, MaskEnd);
3319 // tmp32 = (ROTL(rS_Lo32, Imm)
3320 // rA = ((tmp32 || tmp32) & mask64)
3321 r = ROTL( unop(Iop_64to32, mkexpr(rS) ), mkU8(sh_imm) );
3322 r = unop(Iop_32Uto64, r);
sewardjef4433b2006-10-19 03:01:09 +00003323 assign( rTmp, r );
3324 r = NULL;
3325 assign( rot, binop(Iop_Or64, mkexpr(rTmp),
3326 binop(Iop_Shl64, mkexpr(rTmp), mkU8(32))) );
cerionbb01b7c2005-12-16 13:40:18 +00003327 assign( rA, binop(Iop_And64, mkexpr(rot), mkU64(mask64)) );
ceriond953ebb2005-11-29 13:27:20 +00003328 }
3329 else {
3330 if (MaskBeg == 0 && sh_imm+MaskEnd == 31) {
3331 /* Special-case the ,n,0,31-n form as that is just n-bit
cerion5b2325f2005-12-23 00:55:09 +00003332 shift left, PPC32 p501 */
3333 DIP("slwi%s r%u,r%u,%d\n", flag_rC ? ".":"",
ceriond953ebb2005-11-29 13:27:20 +00003334 rA_addr, rS_addr, sh_imm);
3335 assign( rA, binop(Iop_Shl32, mkexpr(rS), mkU8(sh_imm)) );
3336 }
cerion2831b002005-11-30 19:55:22 +00003337 else if (MaskEnd == 31 && sh_imm+MaskBeg == 32) {
3338 /* Special-case the ,32-n,n,31 form as that is just n-bit
cerion5b2325f2005-12-23 00:55:09 +00003339 unsigned shift right, PPC32 p501 */
3340 DIP("srwi%s r%u,r%u,%d\n", flag_rC ? ".":"",
sewardjee4a8592006-10-19 00:15:25 +00003341 rA_addr, rS_addr, MaskBeg);
cerion2831b002005-11-30 19:55:22 +00003342 assign( rA, binop(Iop_Shr32, mkexpr(rS), mkU8(MaskBeg)) );
3343 }
3344 else {
3345 /* General case. */
cerionf0de28c2005-12-13 20:21:11 +00003346 mask32 = MASK32(31-MaskEnd, 31-MaskBeg);
cerion5b2325f2005-12-23 00:55:09 +00003347 DIP("rlwinm%s r%u,r%u,%d,%d,%d\n", flag_rC ? ".":"",
cerion2831b002005-11-30 19:55:22 +00003348 rA_addr, rS_addr, sh_imm, MaskBeg, MaskEnd);
3349 // rA = ROTL(rS, Imm) & mask
cerion5b2325f2005-12-23 00:55:09 +00003350 assign( rA, binop(Iop_And32,
3351 ROTL(mkexpr(rS), mkU8(sh_imm)),
cerionf0de28c2005-12-13 20:21:11 +00003352 mkU32(mask32)) );
cerion2831b002005-11-30 19:55:22 +00003353 }
ceriond953ebb2005-11-29 13:27:20 +00003354 }
sewardjc9659532005-07-21 21:33:57 +00003355 break;
ceriond953ebb2005-11-29 13:27:20 +00003356 }
3357
3358 case 0x17: {
3359 // rlwnm (Rotate Left Word then AND with Mask, PPC32 p503
cerion5b2325f2005-12-23 00:55:09 +00003360 DIP("rlwnm%s r%u,r%u,r%u,%d,%d\n", flag_rC ? ".":"",
ceriond953ebb2005-11-29 13:27:20 +00003361 rA_addr, rS_addr, rB_addr, MaskBeg, MaskEnd);
3362 if (mode64) {
cerionf0de28c2005-12-13 20:21:11 +00003363 mask64 = MASK64(31-MaskEnd, 31-MaskBeg);
cerionbb01b7c2005-12-16 13:40:18 +00003364 /* weird insn alert!
3365 tmp32 = (ROTL(rS_Lo32, rB[0-4])
3366 rA = ((tmp32 || tmp32) & mask64)
3367 */
ceriond953ebb2005-11-29 13:27:20 +00003368 // note, ROTL does the masking, so we don't do it here
3369 r = ROTL( unop(Iop_64to32, mkexpr(rS)),
cerionbb01b7c2005-12-16 13:40:18 +00003370 unop(Iop_64to8, mkexpr(rB)) );
ceriond953ebb2005-11-29 13:27:20 +00003371 r = unop(Iop_32Uto64, r);
cerionbb01b7c2005-12-16 13:40:18 +00003372 assign(rot, binop(Iop_Or64, r, binop(Iop_Shl64, r, mkU8(32))));
3373 assign( rA, binop(Iop_And64, mkexpr(rot), mkU64(mask64)) );
ceriond953ebb2005-11-29 13:27:20 +00003374 } else {
cerionf0de28c2005-12-13 20:21:11 +00003375 mask32 = MASK32(31-MaskEnd, 31-MaskBeg);
ceriond953ebb2005-11-29 13:27:20 +00003376 // rA = ROTL(rS, rB[0-4]) & mask
3377 // note, ROTL does the masking, so we don't do it here
3378 assign( rA, binop(Iop_And32,
cerion5b2325f2005-12-23 00:55:09 +00003379 ROTL(mkexpr(rS),
3380 unop(Iop_32to8, mkexpr(rB))),
cerionf0de28c2005-12-13 20:21:11 +00003381 mkU32(mask32)) );
ceriond953ebb2005-11-29 13:27:20 +00003382 }
3383 break;
3384 }
cerion45b70ff2005-01-31 17:03:25 +00003385
cerionf0de28c2005-12-13 20:21:11 +00003386 /* 64bit Integer Rotates */
3387 case 0x1E: {
3388 msk_imm = ((msk_imm & 1) << 5) | (msk_imm >> 1);
3389 sh_imm |= b1 << 5;
3390
3391 vassert( msk_imm < 64 );
3392 vassert( sh_imm < 64 );
3393
3394 switch (opc2) {
cerion07b07a92005-12-22 14:32:35 +00003395 case 0x4: {
3396 /* r = ROTL64( rS, rB_lo6) */
3397 r = ROTL( mkexpr(rS), unop(Iop_64to8, mkexpr(rB)) );
3398
cerion5b2325f2005-12-23 00:55:09 +00003399 if (b1 == 0) { // rldcl (Rotl DWord, Clear Left, PPC64 p555)
3400 DIP("rldcl%s r%u,r%u,r%u,%u\n", flag_rC ? ".":"",
cerionf0de28c2005-12-13 20:21:11 +00003401 rA_addr, rS_addr, rB_addr, msk_imm);
cerion07b07a92005-12-22 14:32:35 +00003402 // note, ROTL does the masking, so we don't do it here
cerionf0de28c2005-12-13 20:21:11 +00003403 mask64 = MASK64(0, 63-msk_imm);
3404 assign( rA, binop(Iop_And64, r, mkU64(mask64)) );
3405 break;
cerion5b2325f2005-12-23 00:55:09 +00003406 } else { // rldcr (Rotl DWord, Clear Right, PPC64 p556)
3407 DIP("rldcr%s r%u,r%u,r%u,%u\n", flag_rC ? ".":"",
cerionf0de28c2005-12-13 20:21:11 +00003408 rA_addr, rS_addr, rB_addr, msk_imm);
cerionf0de28c2005-12-13 20:21:11 +00003409 mask64 = MASK64(63-msk_imm, 63);
3410 assign( rA, binop(Iop_And64, r, mkU64(mask64)) );
3411 break;
3412 }
3413 break;
cerion07b07a92005-12-22 14:32:35 +00003414 }
cerion5b2325f2005-12-23 00:55:09 +00003415 case 0x2: // rldic (Rotl DWord Imm, Clear, PPC64 p557)
3416 DIP("rldic%s r%u,r%u,%u,%u\n", flag_rC ? ".":"",
cerionf0de28c2005-12-13 20:21:11 +00003417 rA_addr, rS_addr, sh_imm, msk_imm);
3418 r = ROTL(mkexpr(rS), mkU8(sh_imm));
3419 mask64 = MASK64(sh_imm, 63-msk_imm);
3420 assign( rA, binop(Iop_And64, r, mkU64(mask64)) );
3421 break;
3422 // later: deal with special case: (msk_imm==0) => SHL(sh_imm)
3423 /*
3424 Hmm... looks like this'll do the job more simply:
3425 r = SHL(rS, sh_imm)
3426 m = ~(1 << (63-msk_imm))
3427 assign(rA, r & m);
3428 */
3429
cerion5b2325f2005-12-23 00:55:09 +00003430 case 0x0: // rldicl (Rotl DWord Imm, Clear Left, PPC64 p558)
sewardjee4a8592006-10-19 00:15:25 +00003431 if (mode64
3432 && sh_imm + msk_imm == 64 && msk_imm >= 1 && msk_imm <= 63) {
3433 /* special-case the ,64-n,n form as that is just
3434 unsigned shift-right by n */
3435 DIP("srdi%s r%u,r%u,%u\n",
3436 flag_rC ? ".":"", rA_addr, rS_addr, msk_imm);
3437 assign( rA, binop(Iop_Shr64, mkexpr(rS), mkU8(msk_imm)) );
3438 } else {
3439 DIP("rldicl%s r%u,r%u,%u,%u\n", flag_rC ? ".":"",
3440 rA_addr, rS_addr, sh_imm, msk_imm);
3441 r = ROTL(mkexpr(rS), mkU8(sh_imm));
3442 mask64 = MASK64(0, 63-msk_imm);
3443 assign( rA, binop(Iop_And64, r, mkU64(mask64)) );
3444 }
cerionf0de28c2005-12-13 20:21:11 +00003445 break;
cerionf0de28c2005-12-13 20:21:11 +00003446
cerion5b2325f2005-12-23 00:55:09 +00003447 case 0x1: // rldicr (Rotl DWord Imm, Clear Right, PPC64 p559)
sewardjee4a8592006-10-19 00:15:25 +00003448 if (mode64
3449 && sh_imm + msk_imm == 63 && sh_imm >= 1 && sh_imm <= 63) {
3450 /* special-case the ,n,63-n form as that is just
3451 shift-left by n */
3452 DIP("sldi%s r%u,r%u,%u\n",
3453 flag_rC ? ".":"", rA_addr, rS_addr, sh_imm);
3454 assign( rA, binop(Iop_Shl64, mkexpr(rS), mkU8(sh_imm)) );
3455 } else {
3456 DIP("rldicr%s r%u,r%u,%u,%u\n", flag_rC ? ".":"",
3457 rA_addr, rS_addr, sh_imm, msk_imm);
3458 r = ROTL(mkexpr(rS), mkU8(sh_imm));
3459 mask64 = MASK64(63-msk_imm, 63);
3460 assign( rA, binop(Iop_And64, r, mkU64(mask64)) );
3461 }
cerionf0de28c2005-12-13 20:21:11 +00003462 break;
cerionf0de28c2005-12-13 20:21:11 +00003463
cerion5b2325f2005-12-23 00:55:09 +00003464 case 0x3: { // rldimi (Rotl DWord Imm, Mask Insert, PPC64 p560)
cerion07b07a92005-12-22 14:32:35 +00003465 IRTemp rA_orig = newTemp(ty);
cerion5b2325f2005-12-23 00:55:09 +00003466 DIP("rldimi%s r%u,r%u,%u,%u\n", flag_rC ? ".":"",
cerionf0de28c2005-12-13 20:21:11 +00003467 rA_addr, rS_addr, sh_imm, msk_imm);
3468 r = ROTL(mkexpr(rS), mkU8(sh_imm));
3469 mask64 = MASK64(sh_imm, 63-msk_imm);
cerion07b07a92005-12-22 14:32:35 +00003470 assign( rA_orig, getIReg(rA_addr) );
cerionf0de28c2005-12-13 20:21:11 +00003471 assign( rA, binop(Iop_Or64,
3472 binop(Iop_And64, mkU64(mask64), r),
cerion5b2325f2005-12-23 00:55:09 +00003473 binop(Iop_And64, mkU64(~mask64),
3474 mkexpr(rA_orig))) );
cerionf0de28c2005-12-13 20:21:11 +00003475 break;
cerion07b07a92005-12-22 14:32:35 +00003476 }
cerionf0de28c2005-12-13 20:21:11 +00003477 default:
cerion5b2325f2005-12-23 00:55:09 +00003478 vex_printf("dis_int_rot(ppc)(opc2)\n");
cerionf0de28c2005-12-13 20:21:11 +00003479 return False;
3480 }
3481 break;
3482 }
3483
cerionb85e8bb2005-02-16 08:54:33 +00003484 default:
cerion5b2325f2005-12-23 00:55:09 +00003485 vex_printf("dis_int_rot(ppc)(opc1)\n");
cerionb85e8bb2005-02-16 08:54:33 +00003486 return False;
3487 }
cerion645c9302005-01-31 10:09:59 +00003488
cerion76de5cf2005-11-18 18:25:12 +00003489 putIReg( rA_addr, mkexpr(rA) );
3490
3491 if (flag_rC) {
3492 set_CR0( mkexpr(rA) );
cerionb85e8bb2005-02-16 08:54:33 +00003493 }
3494 return True;
cerion645c9302005-01-31 10:09:59 +00003495}
3496
3497
cerion3d870a32005-03-18 12:23:33 +00003498/*
3499 Integer Load Instructions
3500*/
cerion645c9302005-01-31 10:09:59 +00003501static Bool dis_int_load ( UInt theInstr )
3502{
cerionf0de28c2005-12-13 20:21:11 +00003503 /* D-Form, X-Form, DS-Form */
cerion76de5cf2005-11-18 18:25:12 +00003504 UChar opc1 = ifieldOPC(theInstr);
3505 UChar rD_addr = ifieldRegDS(theInstr);
3506 UChar rA_addr = ifieldRegA(theInstr);
ceriond953ebb2005-11-29 13:27:20 +00003507 UInt uimm16 = ifieldUIMM16(theInstr);
cerion76de5cf2005-11-18 18:25:12 +00003508 UChar rB_addr = ifieldRegB(theInstr);
3509 UInt opc2 = ifieldOPClo10(theInstr);
cerionf0de28c2005-12-13 20:21:11 +00003510 UChar b1 = ifieldBIT1(theInstr);
cerion76de5cf2005-11-18 18:25:12 +00003511 UChar b0 = ifieldBIT0(theInstr);
3512
ceriond953ebb2005-11-29 13:27:20 +00003513 Int simm16 = extend_s_16to32(uimm16);
3514 IRType ty = mode64 ? Ity_I64 : Ity_I32;
ceriond953ebb2005-11-29 13:27:20 +00003515 IRTemp EA = newTemp(ty);
3516 IRExpr* val;
cerionedf7fc52005-11-18 20:57:41 +00003517
cerionf0de28c2005-12-13 20:21:11 +00003518 switch (opc1) {
3519 case 0x1F: // register offset
ceriond953ebb2005-11-29 13:27:20 +00003520 assign( EA, ea_rAor0_idxd( rA_addr, rB_addr ) );
cerionf0de28c2005-12-13 20:21:11 +00003521 break;
3522 case 0x3A: // immediate offset: 64bit
3523 simm16 = simm16 & 0xFFFFFFFC;
3524 default: // immediate offset
3525 assign( EA, ea_rAor0_simm( rA_addr, simm16 ) );
3526 break;
ceriond953ebb2005-11-29 13:27:20 +00003527 }
cerione9d361a2005-03-04 17:35:29 +00003528
cerionb85e8bb2005-02-16 08:54:33 +00003529 switch (opc1) {
cerione9d361a2005-03-04 17:35:29 +00003530 case 0x22: // lbz (Load B & Zero, PPC32 p433)
ceriond953ebb2005-11-29 13:27:20 +00003531 DIP("lbz r%u,%d(r%u)\n", rD_addr, (Int)simm16, rA_addr);
3532 val = loadBE(Ity_I8, mkexpr(EA));
cerion2831b002005-11-30 19:55:22 +00003533 putIReg( rD_addr, mkSzWiden8(ty, val, False) );
cerionb85e8bb2005-02-16 08:54:33 +00003534 break;
3535
cerion5b2325f2005-12-23 00:55:09 +00003536 case 0x23: // lbzu (Load B & Zero, Update, PPC32 p434)
cerion76de5cf2005-11-18 18:25:12 +00003537 if (rA_addr == 0 || rA_addr == rD_addr) {
cerion5b2325f2005-12-23 00:55:09 +00003538 vex_printf("dis_int_load(ppc)(lbzu,rA_addr|rD_addr)\n");
cerionb85e8bb2005-02-16 08:54:33 +00003539 return False;
3540 }
ceriond953ebb2005-11-29 13:27:20 +00003541 DIP("lbzu r%u,%d(r%u)\n", rD_addr, (Int)simm16, rA_addr);
3542 val = loadBE(Ity_I8, mkexpr(EA));
cerion2831b002005-11-30 19:55:22 +00003543 putIReg( rD_addr, mkSzWiden8(ty, val, False) );
ceriond953ebb2005-11-29 13:27:20 +00003544 putIReg( rA_addr, mkexpr(EA) );
cerionb85e8bb2005-02-16 08:54:33 +00003545 break;
3546
cerion5b2325f2005-12-23 00:55:09 +00003547 case 0x2A: // lha (Load HW Alg, PPC32 p445)
ceriond953ebb2005-11-29 13:27:20 +00003548 DIP("lha r%u,%d(r%u)\n", rD_addr, (Int)simm16, rA_addr);
3549 val = loadBE(Ity_I16, mkexpr(EA));
cerion2831b002005-11-30 19:55:22 +00003550 putIReg( rD_addr, mkSzWiden16(ty, val, True) );
cerionb85e8bb2005-02-16 08:54:33 +00003551 break;
cerion645c9302005-01-31 10:09:59 +00003552
cerion5b2325f2005-12-23 00:55:09 +00003553 case 0x2B: // lhau (Load HW Alg, Update, PPC32 p446)
cerion76de5cf2005-11-18 18:25:12 +00003554 if (rA_addr == 0 || rA_addr == rD_addr) {
cerion5b2325f2005-12-23 00:55:09 +00003555 vex_printf("dis_int_load(ppc)(lhau,rA_addr|rD_addr)\n");
cerioncb14e732005-09-09 16:38:19 +00003556 return False;
3557 }
ceriond953ebb2005-11-29 13:27:20 +00003558 DIP("lhau r%u,%d(r%u)\n", rD_addr, (Int)simm16, rA_addr);
3559 val = loadBE(Ity_I16, mkexpr(EA));
cerion2831b002005-11-30 19:55:22 +00003560 putIReg( rD_addr, mkSzWiden16(ty, val, True) );
ceriond953ebb2005-11-29 13:27:20 +00003561 putIReg( rA_addr, mkexpr(EA) );
cerioncb14e732005-09-09 16:38:19 +00003562 break;
cerionb85e8bb2005-02-16 08:54:33 +00003563
cerione9d361a2005-03-04 17:35:29 +00003564 case 0x28: // lhz (Load HW & Zero, PPC32 p450)
ceriond953ebb2005-11-29 13:27:20 +00003565 DIP("lhz r%u,%d(r%u)\n", rD_addr, (Int)simm16, rA_addr);
3566 val = loadBE(Ity_I16, mkexpr(EA));
cerion2831b002005-11-30 19:55:22 +00003567 putIReg( rD_addr, mkSzWiden16(ty, val, False) );
cerionb85e8bb2005-02-16 08:54:33 +00003568 break;
3569
cerion5b2325f2005-12-23 00:55:09 +00003570 case 0x29: // lhzu (Load HW & and Zero, Update, PPC32 p451)
cerion76de5cf2005-11-18 18:25:12 +00003571 if (rA_addr == 0 || rA_addr == rD_addr) {
cerion5b2325f2005-12-23 00:55:09 +00003572 vex_printf("dis_int_load(ppc)(lhzu,rA_addr|rD_addr)\n");
sewardj0e2cc672005-07-29 21:58:51 +00003573 return False;
3574 }
ceriond953ebb2005-11-29 13:27:20 +00003575 DIP("lhzu r%u,%d(r%u)\n", rD_addr, (Int)simm16, rA_addr);
3576 val = loadBE(Ity_I16, mkexpr(EA));
cerion2831b002005-11-30 19:55:22 +00003577 putIReg( rD_addr, mkSzWiden16(ty, val, False) );
ceriond953ebb2005-11-29 13:27:20 +00003578 putIReg( rA_addr, mkexpr(EA) );
sewardj0e2cc672005-07-29 21:58:51 +00003579 break;
cerion645c9302005-01-31 10:09:59 +00003580
cerione9d361a2005-03-04 17:35:29 +00003581 case 0x20: // lwz (Load W & Zero, PPC32 p460)
ceriond953ebb2005-11-29 13:27:20 +00003582 DIP("lwz r%u,%d(r%u)\n", rD_addr, (Int)simm16, rA_addr);
3583 val = loadBE(Ity_I32, mkexpr(EA));
cerion2831b002005-11-30 19:55:22 +00003584 putIReg( rD_addr, mkSzWiden32(ty, val, False) );
cerionb85e8bb2005-02-16 08:54:33 +00003585 break;
3586
cerion5b2325f2005-12-23 00:55:09 +00003587 case 0x21: // lwzu (Load W & Zero, Update, PPC32 p461))
cerion76de5cf2005-11-18 18:25:12 +00003588 if (rA_addr == 0 || rA_addr == rD_addr) {
cerion5b2325f2005-12-23 00:55:09 +00003589 vex_printf("dis_int_load(ppc)(lwzu,rA_addr|rD_addr)\n");
cerionb85e8bb2005-02-16 08:54:33 +00003590 return False;
3591 }
ceriond953ebb2005-11-29 13:27:20 +00003592 DIP("lwzu r%u,%d(r%u)\n", rD_addr, (Int)simm16, rA_addr);
3593 val = loadBE(Ity_I32, mkexpr(EA));
cerion2831b002005-11-30 19:55:22 +00003594 putIReg( rD_addr, mkSzWiden32(ty, val, False) );
ceriond953ebb2005-11-29 13:27:20 +00003595 putIReg( rA_addr, mkexpr(EA) );
cerionb85e8bb2005-02-16 08:54:33 +00003596 break;
3597
3598 /* X Form */
3599 case 0x1F:
3600 if (b0 != 0) {
cerion5b2325f2005-12-23 00:55:09 +00003601 vex_printf("dis_int_load(ppc)(Ox1F,b0)\n");
cerionb85e8bb2005-02-16 08:54:33 +00003602 return False;
3603 }
cerion645c9302005-01-31 10:09:59 +00003604
cerionb85e8bb2005-02-16 08:54:33 +00003605 switch (opc2) {
cerion5b2325f2005-12-23 00:55:09 +00003606 case 0x077: // lbzux (Load B & Zero, Update Indexed, PPC32 p435)
ceriond953ebb2005-11-29 13:27:20 +00003607 DIP("lbzux r%u,r%u,r%u\n", rD_addr, rA_addr, rB_addr);
cerion76de5cf2005-11-18 18:25:12 +00003608 if (rA_addr == 0 || rA_addr == rD_addr) {
cerion5b2325f2005-12-23 00:55:09 +00003609 vex_printf("dis_int_load(ppc)(lwzux,rA_addr|rD_addr)\n");
cerionb85e8bb2005-02-16 08:54:33 +00003610 return False;
3611 }
ceriond953ebb2005-11-29 13:27:20 +00003612 val = loadBE(Ity_I8, mkexpr(EA));
cerion2831b002005-11-30 19:55:22 +00003613 putIReg( rD_addr, mkSzWiden8(ty, val, False) );
ceriond953ebb2005-11-29 13:27:20 +00003614 putIReg( rA_addr, mkexpr(EA) );
cerionb85e8bb2005-02-16 08:54:33 +00003615 break;
3616
cerion5b2325f2005-12-23 00:55:09 +00003617 case 0x057: // lbzx (Load B & Zero, Indexed, PPC32 p436)
ceriond953ebb2005-11-29 13:27:20 +00003618 DIP("lbzx r%u,r%u,r%u\n", rD_addr, rA_addr, rB_addr);
3619 val = loadBE(Ity_I8, mkexpr(EA));
cerion2831b002005-11-30 19:55:22 +00003620 putIReg( rD_addr, mkSzWiden8(ty, val, False) );
cerionb85e8bb2005-02-16 08:54:33 +00003621 break;
3622
cerion5b2325f2005-12-23 00:55:09 +00003623 case 0x177: // lhaux (Load HW Alg, Update Indexed, PPC32 p447)
cerion76de5cf2005-11-18 18:25:12 +00003624 if (rA_addr == 0 || rA_addr == rD_addr) {
cerion5b2325f2005-12-23 00:55:09 +00003625 vex_printf("dis_int_load(ppc)(lhaux,rA_addr|rD_addr)\n");
cerioncb14e732005-09-09 16:38:19 +00003626 return False;
3627 }
ceriond953ebb2005-11-29 13:27:20 +00003628 DIP("lhaux r%u,r%u,r%u\n", rD_addr, rA_addr, rB_addr);
3629 val = loadBE(Ity_I16, mkexpr(EA));
cerion2831b002005-11-30 19:55:22 +00003630 putIReg( rD_addr, mkSzWiden16(ty, val, True) );
ceriond953ebb2005-11-29 13:27:20 +00003631 putIReg( rA_addr, mkexpr(EA) );
cerioncb14e732005-09-09 16:38:19 +00003632 break;
cerionb85e8bb2005-02-16 08:54:33 +00003633
cerion5b2325f2005-12-23 00:55:09 +00003634 case 0x157: // lhax (Load HW Alg, Indexed, PPC32 p448)
ceriond953ebb2005-11-29 13:27:20 +00003635 DIP("lhax r%u,r%u,r%u\n", rD_addr, rA_addr, rB_addr);
3636 val = loadBE(Ity_I16, mkexpr(EA));
cerion2831b002005-11-30 19:55:22 +00003637 putIReg( rD_addr, mkSzWiden16(ty, val, True) );
cerionb85e8bb2005-02-16 08:54:33 +00003638 break;
3639
cerion5b2325f2005-12-23 00:55:09 +00003640 case 0x137: // lhzux (Load HW & Zero, Update Indexed, PPC32 p452)
cerion76de5cf2005-11-18 18:25:12 +00003641 if (rA_addr == 0 || rA_addr == rD_addr) {
cerion5b2325f2005-12-23 00:55:09 +00003642 vex_printf("dis_int_load(ppc)(lhzux,rA_addr|rD_addr)\n");
cerionb85e8bb2005-02-16 08:54:33 +00003643 return False;
3644 }
ceriond953ebb2005-11-29 13:27:20 +00003645 DIP("lhzux r%u,r%u,r%u\n", rD_addr, rA_addr, rB_addr);
3646 val = loadBE(Ity_I16, mkexpr(EA));
cerion2831b002005-11-30 19:55:22 +00003647 putIReg( rD_addr, mkSzWiden16(ty, val, False) );
ceriond953ebb2005-11-29 13:27:20 +00003648 putIReg( rA_addr, mkexpr(EA) );
cerionb85e8bb2005-02-16 08:54:33 +00003649 break;
3650
cerion5b2325f2005-12-23 00:55:09 +00003651 case 0x117: // lhzx (Load HW & Zero, Indexed, PPC32 p453)
ceriond953ebb2005-11-29 13:27:20 +00003652 DIP("lhzx r%u,r%u,r%u\n", rD_addr, rA_addr, rB_addr);
3653 val = loadBE(Ity_I16, mkexpr(EA));
cerion2831b002005-11-30 19:55:22 +00003654 putIReg( rD_addr, mkSzWiden16(ty, val, False) );
cerionb85e8bb2005-02-16 08:54:33 +00003655 break;
cerion44997f22005-01-31 18:45:59 +00003656
cerion5b2325f2005-12-23 00:55:09 +00003657 case 0x037: // lwzux (Load W & Zero, Update Indexed, PPC32 p462)
cerion76de5cf2005-11-18 18:25:12 +00003658 if (rA_addr == 0 || rA_addr == rD_addr) {
cerion5b2325f2005-12-23 00:55:09 +00003659 vex_printf("dis_int_load(ppc)(lwzux,rA_addr|rD_addr)\n");
sewardj7787af42005-08-04 18:32:19 +00003660 return False;
3661 }
ceriond953ebb2005-11-29 13:27:20 +00003662 DIP("lwzux r%u,r%u,r%u\n", rD_addr, rA_addr, rB_addr);
3663 val = loadBE(Ity_I32, mkexpr(EA));
cerion2831b002005-11-30 19:55:22 +00003664 putIReg( rD_addr, mkSzWiden32(ty, val, False) );
ceriond953ebb2005-11-29 13:27:20 +00003665 putIReg( rA_addr, mkexpr(EA) );
sewardj7787af42005-08-04 18:32:19 +00003666 break;
cerionb85e8bb2005-02-16 08:54:33 +00003667
cerion5b2325f2005-12-23 00:55:09 +00003668 case 0x017: // lwzx (Load W & Zero, Indexed, PPC32 p463)
ceriond953ebb2005-11-29 13:27:20 +00003669 DIP("lwzx r%u,r%u,r%u\n", rD_addr, rA_addr, rB_addr);
3670 val = loadBE(Ity_I32, mkexpr(EA));
cerion2831b002005-11-30 19:55:22 +00003671 putIReg( rD_addr, mkSzWiden32(ty, val, False) );
cerionb85e8bb2005-02-16 08:54:33 +00003672 break;
cerion44997f22005-01-31 18:45:59 +00003673
cerionf0de28c2005-12-13 20:21:11 +00003674
3675 /* 64bit Loads */
cerion5b2325f2005-12-23 00:55:09 +00003676 case 0x035: // ldux (Load DWord, Update Indexed, PPC64 p475)
cerionf0de28c2005-12-13 20:21:11 +00003677 if (rA_addr == 0 || rA_addr == rD_addr) {
cerion5b2325f2005-12-23 00:55:09 +00003678 vex_printf("dis_int_load(ppc)(ldux,rA_addr|rD_addr)\n");
cerionf0de28c2005-12-13 20:21:11 +00003679 return False;
3680 }
3681 DIP("ldux r%u,r%u,r%u\n", rD_addr, rA_addr, rB_addr);
3682 putIReg( rD_addr, loadBE(Ity_I64, mkexpr(EA)) );
3683 putIReg( rA_addr, mkexpr(EA) );
3684 break;
3685
cerion5b2325f2005-12-23 00:55:09 +00003686 case 0x015: // ldx (Load DWord, Indexed, PPC64 p476)
cerionf0de28c2005-12-13 20:21:11 +00003687 DIP("ldx r%u,r%u,r%u\n", rD_addr, rA_addr, rB_addr);
3688 putIReg( rD_addr, loadBE(Ity_I64, mkexpr(EA)) );
3689 break;
3690
cerion5b2325f2005-12-23 00:55:09 +00003691 case 0x175: // lwaux (Load W Alg, Update Indexed, PPC64 p501)
cerionf0de28c2005-12-13 20:21:11 +00003692 if (rA_addr == 0 || rA_addr == rD_addr) {
cerion5b2325f2005-12-23 00:55:09 +00003693 vex_printf("dis_int_load(ppc)(lwaux,rA_addr|rD_addr)\n");
cerionf0de28c2005-12-13 20:21:11 +00003694 return False;
3695 }
3696 DIP("lwaux r%u,r%u,r%u\n", rD_addr, rA_addr, rB_addr);
cerion5b2325f2005-12-23 00:55:09 +00003697 putIReg( rD_addr,
3698 unop(Iop_32Sto64, loadBE(Ity_I32, mkexpr(EA))) );
cerionf0de28c2005-12-13 20:21:11 +00003699 putIReg( rA_addr, mkexpr(EA) );
3700 break;
3701
cerion5b2325f2005-12-23 00:55:09 +00003702 case 0x155: // lwax (Load W Alg, Indexed, PPC64 p502)
cerionf0de28c2005-12-13 20:21:11 +00003703 DIP("lwax r%u,r%u,r%u\n", rD_addr, rA_addr, rB_addr);
cerion5b2325f2005-12-23 00:55:09 +00003704 putIReg( rD_addr,
3705 unop(Iop_32Sto64, loadBE(Ity_I32, mkexpr(EA))) );
cerionf0de28c2005-12-13 20:21:11 +00003706 break;
3707
cerionb85e8bb2005-02-16 08:54:33 +00003708 default:
cerion5b2325f2005-12-23 00:55:09 +00003709 vex_printf("dis_int_load(ppc)(opc2)\n");
cerionb85e8bb2005-02-16 08:54:33 +00003710 return False;
3711 }
3712 break;
cerionf0de28c2005-12-13 20:21:11 +00003713
3714 /* DS Form - 64bit Loads */
3715 case 0x3A:
3716 switch (b1<<1 | b0) {
cerion5b2325f2005-12-23 00:55:09 +00003717 case 0x0: // ld (Load DWord, PPC64 p472)
cerionf0de28c2005-12-13 20:21:11 +00003718 DIP("ld r%u,%d(r%u)\n", rD_addr, simm16, rA_addr);
3719 putIReg( rD_addr, loadBE(Ity_I64, mkexpr(EA)) );
3720 break;
3721
cerion5b2325f2005-12-23 00:55:09 +00003722 case 0x1: // ldu (Load DWord, Update, PPC64 p474)
cerionf0de28c2005-12-13 20:21:11 +00003723 if (rA_addr == 0 || rA_addr == rD_addr) {
cerion5b2325f2005-12-23 00:55:09 +00003724 vex_printf("dis_int_load(ppc)(ldu,rA_addr|rD_addr)\n");
cerionf0de28c2005-12-13 20:21:11 +00003725 return False;
3726 }
3727 DIP("ldu r%u,%d(r%u)\n", rD_addr, simm16, rA_addr);
3728 simm16 = simm16 & ~0x3;
3729 putIReg( rD_addr, loadBE(Ity_I64, mkexpr(EA)) );
3730 putIReg( rA_addr, mkexpr(EA) );
3731 break;
3732
cerion5b2325f2005-12-23 00:55:09 +00003733 case 0x2: // lwa (Load Word Alg, PPC64 p499)
cerionf0de28c2005-12-13 20:21:11 +00003734 DIP("lwa r%u,%d(r%u)\n", rD_addr, simm16, rA_addr);
cerion5b2325f2005-12-23 00:55:09 +00003735 putIReg( rD_addr,
3736 unop(Iop_32Sto64, loadBE(Ity_I32, mkexpr(EA))) );
cerionf0de28c2005-12-13 20:21:11 +00003737 break;
3738
3739 default:
cerion5b2325f2005-12-23 00:55:09 +00003740 vex_printf("dis_int_load(ppc)(0x3A, opc2)\n");
cerionf0de28c2005-12-13 20:21:11 +00003741 return False;
3742 }
3743 break;
3744
cerionb85e8bb2005-02-16 08:54:33 +00003745 default:
cerion5b2325f2005-12-23 00:55:09 +00003746 vex_printf("dis_int_load(ppc)(opc1)\n");
cerionb85e8bb2005-02-16 08:54:33 +00003747 return False;
3748 }
3749 return True;
cerion7aa4bbc2005-01-29 09:32:07 +00003750}
3751
3752
3753
cerion3d870a32005-03-18 12:23:33 +00003754/*
3755 Integer Store Instructions
3756*/
sewardjaca070a2006-10-17 00:28:22 +00003757static Bool dis_int_store ( UInt theInstr, VexMiscInfo* vmi )
ceriond23be4e2005-01-31 07:23:07 +00003758{
cerionf0de28c2005-12-13 20:21:11 +00003759 /* D-Form, X-Form, DS-Form */
cerionedf7fc52005-11-18 20:57:41 +00003760 UChar opc1 = ifieldOPC(theInstr);
cerion76de5cf2005-11-18 18:25:12 +00003761 UInt rS_addr = ifieldRegDS(theInstr);
3762 UInt rA_addr = ifieldRegA(theInstr);
ceriond953ebb2005-11-29 13:27:20 +00003763 UInt uimm16 = ifieldUIMM16(theInstr);
cerion76de5cf2005-11-18 18:25:12 +00003764 UInt rB_addr = ifieldRegB(theInstr);
3765 UInt opc2 = ifieldOPClo10(theInstr);
cerionf0de28c2005-12-13 20:21:11 +00003766 UChar b1 = ifieldBIT1(theInstr);
cerion76de5cf2005-11-18 18:25:12 +00003767 UChar b0 = ifieldBIT0(theInstr);
3768
ceriond953ebb2005-11-29 13:27:20 +00003769 Int simm16 = extend_s_16to32(uimm16);
3770 IRType ty = mode64 ? Ity_I64 : Ity_I32;
3771 IRTemp rS = newTemp(ty);
3772 IRTemp rB = newTemp(ty);
3773 IRTemp EA = newTemp(ty);
cerionb85e8bb2005-02-16 08:54:33 +00003774
cerion76de5cf2005-11-18 18:25:12 +00003775 assign( rB, getIReg(rB_addr) );
3776 assign( rS, getIReg(rS_addr) );
cerionb85e8bb2005-02-16 08:54:33 +00003777
cerionf0de28c2005-12-13 20:21:11 +00003778 switch (opc1) {
3779 case 0x1F: // register offset
ceriond953ebb2005-11-29 13:27:20 +00003780 assign( EA, ea_rAor0_idxd( rA_addr, rB_addr ) );
cerionf0de28c2005-12-13 20:21:11 +00003781 break;
3782 case 0x3E: // immediate offset: 64bit
3783 simm16 = simm16 & 0xFFFFFFFC;
3784 default: // immediate offset
3785 assign( EA, ea_rAor0_simm( rA_addr, simm16 ) );
3786 break;
ceriond953ebb2005-11-29 13:27:20 +00003787 }
3788
cerionb85e8bb2005-02-16 08:54:33 +00003789 switch (opc1) {
sewardjafe85832005-09-09 10:25:39 +00003790 case 0x26: // stb (Store B, PPC32 p509)
cerion76de5cf2005-11-18 18:25:12 +00003791 DIP("stb r%u,%d(r%u)\n", rS_addr, simm16, rA_addr);
cerion2831b002005-11-30 19:55:22 +00003792 storeBE( mkexpr(EA), mkSzNarrow8(ty, mkexpr(rS)) );
sewardjafe85832005-09-09 10:25:39 +00003793 break;
sewardjb51f0f42005-07-18 11:38:02 +00003794
cerion5b2325f2005-12-23 00:55:09 +00003795 case 0x27: // stbu (Store B, Update, PPC32 p510)
cerion76de5cf2005-11-18 18:25:12 +00003796 if (rA_addr == 0 ) {
cerion5b2325f2005-12-23 00:55:09 +00003797 vex_printf("dis_int_store(ppc)(stbu,rA_addr)\n");
cerionb85e8bb2005-02-16 08:54:33 +00003798 return False;
3799 }
cerion76de5cf2005-11-18 18:25:12 +00003800 DIP("stbu r%u,%d(r%u)\n", rS_addr, simm16, rA_addr);
ceriond953ebb2005-11-29 13:27:20 +00003801 putIReg( rA_addr, mkexpr(EA) );
cerion2831b002005-11-30 19:55:22 +00003802 storeBE( mkexpr(EA), mkSzNarrow8(ty, mkexpr(rS)) );
cerionb85e8bb2005-02-16 08:54:33 +00003803 break;
ceriond23be4e2005-01-31 07:23:07 +00003804
cerione9d361a2005-03-04 17:35:29 +00003805 case 0x2C: // sth (Store HW, PPC32 p522)
cerion76de5cf2005-11-18 18:25:12 +00003806 DIP("sth r%u,%d(r%u)\n", rS_addr, simm16, rA_addr);
cerion2831b002005-11-30 19:55:22 +00003807 storeBE( mkexpr(EA), mkSzNarrow16(ty, mkexpr(rS)) );
cerionb85e8bb2005-02-16 08:54:33 +00003808 break;
3809
cerion5b2325f2005-12-23 00:55:09 +00003810 case 0x2D: // sthu (Store HW, Update, PPC32 p524)
cerion76de5cf2005-11-18 18:25:12 +00003811 if (rA_addr == 0) {
cerion5b2325f2005-12-23 00:55:09 +00003812 vex_printf("dis_int_store(ppc)(sthu,rA_addr)\n");
cerionb85e8bb2005-02-16 08:54:33 +00003813 return False;
3814 }
cerion76de5cf2005-11-18 18:25:12 +00003815 DIP("sthu r%u,%d(r%u)\n", rS_addr, simm16, rA_addr);
ceriond953ebb2005-11-29 13:27:20 +00003816 putIReg( rA_addr, mkexpr(EA) );
cerion2831b002005-11-30 19:55:22 +00003817 storeBE( mkexpr(EA), mkSzNarrow16(ty, mkexpr(rS)) );
cerionb85e8bb2005-02-16 08:54:33 +00003818 break;
ceriond23be4e2005-01-31 07:23:07 +00003819
cerione9d361a2005-03-04 17:35:29 +00003820 case 0x24: // stw (Store W, PPC32 p530)
cerion76de5cf2005-11-18 18:25:12 +00003821 DIP("stw r%u,%d(r%u)\n", rS_addr, simm16, rA_addr);
cerion2831b002005-11-30 19:55:22 +00003822 storeBE( mkexpr(EA), mkSzNarrow32(ty, mkexpr(rS)) );
cerionb85e8bb2005-02-16 08:54:33 +00003823 break;
ceriond23be4e2005-01-31 07:23:07 +00003824
cerion5b2325f2005-12-23 00:55:09 +00003825 case 0x25: // stwu (Store W, Update, PPC32 p534)
cerion76de5cf2005-11-18 18:25:12 +00003826 if (rA_addr == 0) {
cerion5b2325f2005-12-23 00:55:09 +00003827 vex_printf("dis_int_store(ppc)(stwu,rA_addr)\n");
cerionb85e8bb2005-02-16 08:54:33 +00003828 return False;
3829 }
cerion76de5cf2005-11-18 18:25:12 +00003830 DIP("stwu r%u,%d(r%u)\n", rS_addr, simm16, rA_addr);
ceriond953ebb2005-11-29 13:27:20 +00003831 putIReg( rA_addr, mkexpr(EA) );
cerion2831b002005-11-30 19:55:22 +00003832 storeBE( mkexpr(EA), mkSzNarrow32(ty, mkexpr(rS)) );
cerionb85e8bb2005-02-16 08:54:33 +00003833 break;
3834
cerionf0de28c2005-12-13 20:21:11 +00003835 /* X Form : all these use EA_indexed */
cerionb85e8bb2005-02-16 08:54:33 +00003836 case 0x1F:
3837 if (b0 != 0) {
cerion5b2325f2005-12-23 00:55:09 +00003838 vex_printf("dis_int_store(ppc)(0x1F,b0)\n");
cerionb85e8bb2005-02-16 08:54:33 +00003839 return False;
3840 }
cerion44997f22005-01-31 18:45:59 +00003841
cerionb85e8bb2005-02-16 08:54:33 +00003842 switch (opc2) {
cerion5b2325f2005-12-23 00:55:09 +00003843 case 0x0F7: // stbux (Store B, Update Indexed, PPC32 p511)
cerion76de5cf2005-11-18 18:25:12 +00003844 if (rA_addr == 0) {
cerion5b2325f2005-12-23 00:55:09 +00003845 vex_printf("dis_int_store(ppc)(stbux,rA_addr)\n");
cerionb85e8bb2005-02-16 08:54:33 +00003846 return False;
3847 }
cerion76de5cf2005-11-18 18:25:12 +00003848 DIP("stbux r%u,r%u,r%u\n", rS_addr, rA_addr, rB_addr);
ceriond953ebb2005-11-29 13:27:20 +00003849 putIReg( rA_addr, mkexpr(EA) );
cerion2831b002005-11-30 19:55:22 +00003850 storeBE( mkexpr(EA), mkSzNarrow8(ty, mkexpr(rS)) );
cerionb85e8bb2005-02-16 08:54:33 +00003851 break;
3852
cerione9d361a2005-03-04 17:35:29 +00003853 case 0x0D7: // stbx (Store B Indexed, PPC32 p512)
cerion76de5cf2005-11-18 18:25:12 +00003854 DIP("stbx r%u,r%u,r%u\n", rS_addr, rA_addr, rB_addr);
cerion2831b002005-11-30 19:55:22 +00003855 storeBE( mkexpr(EA), mkSzNarrow8(ty, mkexpr(rS)) );
cerionb85e8bb2005-02-16 08:54:33 +00003856 break;
3857
cerion5b2325f2005-12-23 00:55:09 +00003858 case 0x1B7: // sthux (Store HW, Update Indexed, PPC32 p525)
cerion76de5cf2005-11-18 18:25:12 +00003859 if (rA_addr == 0) {
cerion5b2325f2005-12-23 00:55:09 +00003860 vex_printf("dis_int_store(ppc)(sthux,rA_addr)\n");
cerioncb14e732005-09-09 16:38:19 +00003861 return False;
3862 }
cerion76de5cf2005-11-18 18:25:12 +00003863 DIP("sthux r%u,r%u,r%u\n", rS_addr, rA_addr, rB_addr);
ceriond953ebb2005-11-29 13:27:20 +00003864 putIReg( rA_addr, mkexpr(EA) );
cerion2831b002005-11-30 19:55:22 +00003865 storeBE( mkexpr(EA), mkSzNarrow16(ty, mkexpr(rS)) );
cerioncb14e732005-09-09 16:38:19 +00003866 break;
cerionb85e8bb2005-02-16 08:54:33 +00003867
cerione9d361a2005-03-04 17:35:29 +00003868 case 0x197: // sthx (Store HW Indexed, PPC32 p526)
cerion76de5cf2005-11-18 18:25:12 +00003869 DIP("sthx r%u,r%u,r%u\n", rS_addr, rA_addr, rB_addr);
cerion2831b002005-11-30 19:55:22 +00003870 storeBE( mkexpr(EA), mkSzNarrow16(ty, mkexpr(rS)) );
cerionb85e8bb2005-02-16 08:54:33 +00003871 break;
3872
cerion5b2325f2005-12-23 00:55:09 +00003873 case 0x0B7: // stwux (Store W, Update Indexed, PPC32 p535)
cerion76de5cf2005-11-18 18:25:12 +00003874 if (rA_addr == 0) {
cerion5b2325f2005-12-23 00:55:09 +00003875 vex_printf("dis_int_store(ppc)(stwux,rA_addr)\n");
cerionb85e8bb2005-02-16 08:54:33 +00003876 return False;
3877 }
cerion76de5cf2005-11-18 18:25:12 +00003878 DIP("stwux r%u,r%u,r%u\n", rS_addr, rA_addr, rB_addr);
ceriond953ebb2005-11-29 13:27:20 +00003879 putIReg( rA_addr, mkexpr(EA) );
cerion2831b002005-11-30 19:55:22 +00003880 storeBE( mkexpr(EA), mkSzNarrow32(ty, mkexpr(rS)) );
cerionb85e8bb2005-02-16 08:54:33 +00003881 break;
cerion44997f22005-01-31 18:45:59 +00003882
cerione9d361a2005-03-04 17:35:29 +00003883 case 0x097: // stwx (Store W Indexed, PPC32 p536)
cerion76de5cf2005-11-18 18:25:12 +00003884 DIP("stwx r%u,r%u,r%u\n", rS_addr, rA_addr, rB_addr);
cerion2831b002005-11-30 19:55:22 +00003885 storeBE( mkexpr(EA), mkSzNarrow32(ty, mkexpr(rS)) );
cerionb85e8bb2005-02-16 08:54:33 +00003886 break;
3887
cerionf0de28c2005-12-13 20:21:11 +00003888
3889 /* 64bit Stores */
cerion5b2325f2005-12-23 00:55:09 +00003890 case 0x0B5: // stdux (Store DWord, Update Indexed, PPC64 p584)
cerionf0de28c2005-12-13 20:21:11 +00003891 if (rA_addr == 0) {
cerion5b2325f2005-12-23 00:55:09 +00003892 vex_printf("dis_int_store(ppc)(stdux,rA_addr)\n");
cerionf0de28c2005-12-13 20:21:11 +00003893 return False;
3894 }
3895 DIP("stdux r%u,r%u,r%u\n", rS_addr, rA_addr, rB_addr);
3896 putIReg( rA_addr, mkexpr(EA) );
3897 storeBE( mkexpr(EA), mkexpr(rS) );
3898 break;
3899
cerion5b2325f2005-12-23 00:55:09 +00003900 case 0x095: // stdx (Store DWord Indexed, PPC64 p585)
cerionf0de28c2005-12-13 20:21:11 +00003901 DIP("stdx r%u,r%u,r%u\n", rS_addr, rA_addr, rB_addr);
3902 storeBE( mkexpr(EA), mkexpr(rS) );
3903 break;
3904
cerionb85e8bb2005-02-16 08:54:33 +00003905 default:
cerion5b2325f2005-12-23 00:55:09 +00003906 vex_printf("dis_int_store(ppc)(opc2)\n");
cerionb85e8bb2005-02-16 08:54:33 +00003907 return False;
3908 }
3909 break;
cerionf0de28c2005-12-13 20:21:11 +00003910
3911 /* DS Form - 64bit Stores */
3912 case 0x3E:
3913 switch (b1<<1 | b0) {
cerion5b2325f2005-12-23 00:55:09 +00003914 case 0x0: // std (Store DWord, PPC64 p580)
cerionf0de28c2005-12-13 20:21:11 +00003915 DIP("std r%u,%d(r%u)\n", rS_addr, simm16, rA_addr);
3916 storeBE( mkexpr(EA), mkexpr(rS) );
3917 break;
3918
cerion5b2325f2005-12-23 00:55:09 +00003919 case 0x1: // stdu (Store DWord, Update, PPC64 p583)
cerionf0de28c2005-12-13 20:21:11 +00003920 DIP("stdu r%u,%d(r%u)\n", rS_addr, simm16, rA_addr);
3921 putIReg( rA_addr, mkexpr(EA) );
3922 storeBE( mkexpr(EA), mkexpr(rS) );
3923 break;
3924
3925 default:
cerion5b2325f2005-12-23 00:55:09 +00003926 vex_printf("dis_int_load(ppc)(0x3A, opc2)\n");
cerionf0de28c2005-12-13 20:21:11 +00003927 return False;
3928 }
3929 break;
3930
cerionb85e8bb2005-02-16 08:54:33 +00003931 default:
cerion5b2325f2005-12-23 00:55:09 +00003932 vex_printf("dis_int_store(ppc)(opc1)\n");
cerionb85e8bb2005-02-16 08:54:33 +00003933 return False;
3934 }
3935 return True;
ceriond23be4e2005-01-31 07:23:07 +00003936}
3937
3938
3939
sewardj7787af42005-08-04 18:32:19 +00003940/*
3941 Integer Load/Store Multiple Instructions
3942*/
3943static Bool dis_int_ldst_mult ( UInt theInstr )
3944{
3945 /* D-Form */
cerion76de5cf2005-11-18 18:25:12 +00003946 UChar opc1 = ifieldOPC(theInstr);
3947 UChar rD_addr = ifieldRegDS(theInstr);
3948 UChar rS_addr = rD_addr;
3949 UChar rA_addr = ifieldRegA(theInstr);
cerion2831b002005-11-30 19:55:22 +00003950 UInt uimm16 = ifieldUIMM16(theInstr);
cerionedf7fc52005-11-18 20:57:41 +00003951
ceriond953ebb2005-11-29 13:27:20 +00003952 Int simm16 = extend_s_16to32(uimm16);
3953 IRType ty = mode64 ? Ity_I64 : Ity_I32;
3954 IRTemp EA = newTemp(ty);
3955 UInt r = 0;
3956 UInt ea_off = 0;
sewardj7787af42005-08-04 18:32:19 +00003957 IRExpr* irx_addr;
cerionedf7fc52005-11-18 20:57:41 +00003958
ceriond953ebb2005-11-29 13:27:20 +00003959 assign( EA, ea_rAor0_simm( rA_addr, simm16 ) );
3960
sewardj7787af42005-08-04 18:32:19 +00003961 switch (opc1) {
ceriond953ebb2005-11-29 13:27:20 +00003962 case 0x2E: // lmw (Load Multiple Word, PPC32 p454)
3963 if (rA_addr >= rD_addr) {
cerion5b2325f2005-12-23 00:55:09 +00003964 vex_printf("dis_int_ldst_mult(ppc)(lmw,rA_addr)\n");
sewardj7787af42005-08-04 18:32:19 +00003965 return False;
ceriond953ebb2005-11-29 13:27:20 +00003966 }
3967 DIP("lmw r%u,%d(r%u)\n", rD_addr, simm16, rA_addr);
3968 for (r = rD_addr; r <= 31; r++) {
3969 irx_addr = binop(Iop_Add32, mkexpr(EA), mkU32(ea_off));
cerion5b2325f2005-12-23 00:55:09 +00003970 putIReg( r, mkSzWiden32(ty, loadBE(Ity_I32, irx_addr ),
3971 False) );
ceriond953ebb2005-11-29 13:27:20 +00003972 ea_off += 4;
3973 }
3974 break;
3975
3976 case 0x2F: // stmw (Store Multiple Word, PPC32 p527)
3977 DIP("stmw r%u,%d(r%u)\n", rS_addr, simm16, rA_addr);
3978 for (r = rS_addr; r <= 31; r++) {
3979 irx_addr = binop(Iop_Add32, mkexpr(EA), mkU32(ea_off));
cerion2831b002005-11-30 19:55:22 +00003980 storeBE( irx_addr, mkSzNarrow32(ty, getIReg(r)) );
ceriond953ebb2005-11-29 13:27:20 +00003981 ea_off += 4;
3982 }
3983 break;
3984
3985 default:
cerion5b2325f2005-12-23 00:55:09 +00003986 vex_printf("dis_int_ldst_mult(ppc)(opc1)\n");
ceriond953ebb2005-11-29 13:27:20 +00003987 return False;
sewardj7787af42005-08-04 18:32:19 +00003988 }
3989 return True;
3990}
3991
3992
3993
sewardj87e651f2005-09-09 08:31:18 +00003994/*
3995 Integer Load/Store String Instructions
3996*/
3997static
3998void generate_lsw_sequence ( IRTemp tNBytes, // # bytes, :: Ity_I32
3999 IRTemp EA, // EA
4000 Int rD, // first dst register
ceriond953ebb2005-11-29 13:27:20 +00004001 Int maxBytes ) // 32 or 128
sewardj87e651f2005-09-09 08:31:18 +00004002{
4003 Int i, shift = 24;
4004 IRExpr* e_nbytes = mkexpr(tNBytes);
ceriond953ebb2005-11-29 13:27:20 +00004005 IRExpr* e_EA = mkexpr(EA);
4006 IRType ty = mode64 ? Ity_I64 : Ity_I32;
sewardj87e651f2005-09-09 08:31:18 +00004007
sewardj5876fa12005-09-09 09:35:29 +00004008 vassert(rD >= 0 && rD < 32);
sewardj87e651f2005-09-09 08:31:18 +00004009 rD--; if (rD < 0) rD = 31;
4010
4011 for (i = 0; i < maxBytes; i++) {
sewardj87e651f2005-09-09 08:31:18 +00004012 /* if (nBytes < (i+1)) goto NIA; */
4013 stmt( IRStmt_Exit( binop(Iop_CmpLT32U, e_nbytes, mkU32(i+1)),
4014 Ijk_Boring,
cerion2831b002005-11-30 19:55:22 +00004015 mkSzConst( ty, nextInsnAddr()) ));
sewardj87e651f2005-09-09 08:31:18 +00004016 /* when crossing into a new dest register, set it to zero. */
4017 if ((i % 4) == 0) {
4018 rD++; if (rD == 32) rD = 0;
cerion2831b002005-11-30 19:55:22 +00004019 putIReg(rD, mkSzImm(ty, 0));
sewardj87e651f2005-09-09 08:31:18 +00004020 shift = 24;
4021 }
4022 /* rD |= (8Uto32(*(EA+i))) << shift */
4023 vassert(shift == 0 || shift == 8 || shift == 16 || shift == 24);
sewardj2ef8a372006-01-28 17:07:19 +00004024 putIReg(
4025 rD,
4026 mkSzWiden32(
4027 ty,
4028 binop(
4029 Iop_Or32,
4030 mkSzNarrow32(ty, getIReg(rD)),
4031 binop(
4032 Iop_Shl32,
4033 unop(
4034 Iop_8Uto32,
4035 loadBE(Ity_I8,
4036 binop(mkSzOp(ty,Iop_Add8), e_EA, mkSzImm(ty,i)))
4037 ),
4038 mkU8(toUChar(shift))
4039 )
4040 ),
4041 /*Signed*/False
4042 )
4043 );
sewardj87e651f2005-09-09 08:31:18 +00004044 shift -= 8;
4045 }
4046}
4047
sewardj5876fa12005-09-09 09:35:29 +00004048static
4049void generate_stsw_sequence ( IRTemp tNBytes, // # bytes, :: Ity_I32
4050 IRTemp EA, // EA
4051 Int rS, // first src register
ceriond953ebb2005-11-29 13:27:20 +00004052 Int maxBytes ) // 32 or 128
sewardj5876fa12005-09-09 09:35:29 +00004053{
4054 Int i, shift = 24;
4055 IRExpr* e_nbytes = mkexpr(tNBytes);
ceriond953ebb2005-11-29 13:27:20 +00004056 IRExpr* e_EA = mkexpr(EA);
4057 IRType ty = mode64 ? Ity_I64 : Ity_I32;
sewardj5876fa12005-09-09 09:35:29 +00004058
4059 vassert(rS >= 0 && rS < 32);
4060 rS--; if (rS < 0) rS = 31;
4061
4062 for (i = 0; i < maxBytes; i++) {
4063 /* if (nBytes < (i+1)) goto NIA; */
4064 stmt( IRStmt_Exit( binop(Iop_CmpLT32U, e_nbytes, mkU32(i+1)),
4065 Ijk_Boring,
cerion2831b002005-11-30 19:55:22 +00004066 mkSzConst( ty, nextInsnAddr() ) ));
sewardj5876fa12005-09-09 09:35:29 +00004067 /* check for crossing into a new src register. */
4068 if ((i % 4) == 0) {
4069 rS++; if (rS == 32) rS = 0;
4070 shift = 24;
4071 }
4072 /* *(EA+i) = 32to8(rS >> shift) */
4073 vassert(shift == 0 || shift == 8 || shift == 16 || shift == 24);
4074 storeBE(
cerion2831b002005-11-30 19:55:22 +00004075 binop(mkSzOp(ty,Iop_Add8), e_EA, mkSzImm(ty,i)),
sewardj5876fa12005-09-09 09:35:29 +00004076 unop(Iop_32to8,
cerion2831b002005-11-30 19:55:22 +00004077 binop(Iop_Shr32,
4078 mkSzNarrow32(ty, getIReg(rS)),
4079 mkU8(toUChar(shift))))
sewardj5876fa12005-09-09 09:35:29 +00004080 );
4081 shift -= 8;
4082 }
4083}
4084
sewardj87e651f2005-09-09 08:31:18 +00004085static Bool dis_int_ldst_str ( UInt theInstr, /*OUT*/Bool* stopHere )
4086{
4087 /* X-Form */
cerion76de5cf2005-11-18 18:25:12 +00004088 UChar opc1 = ifieldOPC(theInstr);
4089 UChar rD_addr = ifieldRegDS(theInstr);
4090 UChar rS_addr = rD_addr;
4091 UChar rA_addr = ifieldRegA(theInstr);
4092 UChar rB_addr = ifieldRegB(theInstr);
4093 UChar NumBytes = rB_addr;
4094 UInt opc2 = ifieldOPClo10(theInstr);
4095 UChar b0 = ifieldBIT0(theInstr);
cerionedf7fc52005-11-18 20:57:41 +00004096
ceriond953ebb2005-11-29 13:27:20 +00004097 IRType ty = mode64 ? Ity_I64 : Ity_I32;
4098 IRTemp t_EA = newTemp(ty);
sewardj87e651f2005-09-09 08:31:18 +00004099 IRTemp t_nbytes = IRTemp_INVALID;
cerionedf7fc52005-11-18 20:57:41 +00004100
sewardj87e651f2005-09-09 08:31:18 +00004101 *stopHere = False;
cerionedf7fc52005-11-18 20:57:41 +00004102
sewardj87e651f2005-09-09 08:31:18 +00004103 if (opc1 != 0x1F || b0 != 0) {
cerion5b2325f2005-12-23 00:55:09 +00004104 vex_printf("dis_int_ldst_str(ppc)(opc1)\n");
sewardj87e651f2005-09-09 08:31:18 +00004105 return False;
4106 }
4107
4108 switch (opc2) {
4109 case 0x255: // lswi (Load String Word Immediate, PPC32 p455)
4110 /* NB: does not reject the case where RA is in the range of
4111 registers to be loaded. It should. */
ceriond953ebb2005-11-29 13:27:20 +00004112 DIP("lswi r%u,r%u,%d\n", rD_addr, rA_addr, NumBytes);
4113 assign( t_EA, ea_rAor0(rA_addr) );
sewardj2ef8a372006-01-28 17:07:19 +00004114 if (NumBytes == 8 && !mode64) {
sewardj87e651f2005-09-09 08:31:18 +00004115 /* Special case hack */
cerion76de5cf2005-11-18 18:25:12 +00004116 /* rD = Mem[EA]; (rD+1)%32 = Mem[EA+4] */
4117 putIReg( rD_addr,
sewardj87e651f2005-09-09 08:31:18 +00004118 loadBE(Ity_I32, mkexpr(t_EA)) );
cerion76de5cf2005-11-18 18:25:12 +00004119 putIReg( (rD_addr+1) % 32,
ceriond953ebb2005-11-29 13:27:20 +00004120 loadBE(Ity_I32,
4121 binop(Iop_Add32, mkexpr(t_EA), mkU32(4))) );
sewardj87e651f2005-09-09 08:31:18 +00004122 } else {
4123 t_nbytes = newTemp(Ity_I32);
cerion76de5cf2005-11-18 18:25:12 +00004124 assign( t_nbytes, mkU32(NumBytes==0 ? 32 : NumBytes) );
ceriond953ebb2005-11-29 13:27:20 +00004125 generate_lsw_sequence( t_nbytes, t_EA, rD_addr, 32 );
sewardj87e651f2005-09-09 08:31:18 +00004126 *stopHere = True;
4127 }
4128 return True;
4129
4130 case 0x215: // lswx (Load String Word Indexed, PPC32 p456)
4131 /* NB: does not reject the case where RA is in the range of
4132 registers to be loaded. It should. Although considering
4133 that that can only be detected at run time, it's not easy to
4134 do so. */
cerion76de5cf2005-11-18 18:25:12 +00004135 if (rD_addr == rA_addr || rD_addr == rB_addr)
sewardj87e651f2005-09-09 08:31:18 +00004136 return False;
cerion76de5cf2005-11-18 18:25:12 +00004137 if (rD_addr == 0 && rA_addr == 0)
sewardj87e651f2005-09-09 08:31:18 +00004138 return False;
ceriond953ebb2005-11-29 13:27:20 +00004139 DIP("lswx r%u,r%u,r%u\n", rD_addr, rA_addr, rB_addr);
sewardj87e651f2005-09-09 08:31:18 +00004140 t_nbytes = newTemp(Ity_I32);
ceriond953ebb2005-11-29 13:27:20 +00004141 assign( t_EA, ea_rAor0_idxd(rA_addr,rB_addr) );
cerionedf7fc52005-11-18 20:57:41 +00004142 assign( t_nbytes, unop( Iop_8Uto32, getXER_BC() ) );
ceriond953ebb2005-11-29 13:27:20 +00004143 generate_lsw_sequence( t_nbytes, t_EA, rD_addr, 128 );
sewardj87e651f2005-09-09 08:31:18 +00004144 *stopHere = True;
4145 return True;
4146
sewardj5876fa12005-09-09 09:35:29 +00004147 case 0x2D5: // stswi (Store String Word Immediate, PPC32 p528)
ceriond953ebb2005-11-29 13:27:20 +00004148 DIP("stswi r%u,r%u,%d\n", rS_addr, rA_addr, NumBytes);
4149 assign( t_EA, ea_rAor0(rA_addr) );
sewardj2ef8a372006-01-28 17:07:19 +00004150 if (NumBytes == 8 && !mode64) {
sewardj5876fa12005-09-09 09:35:29 +00004151 /* Special case hack */
cerion76de5cf2005-11-18 18:25:12 +00004152 /* Mem[EA] = rD; Mem[EA+4] = (rD+1)%32 */
4153 storeBE( mkexpr(t_EA),
4154 getIReg(rD_addr) );
4155 storeBE( binop(Iop_Add32, mkexpr(t_EA), mkU32(4)),
4156 getIReg((rD_addr+1) % 32) );
sewardj5876fa12005-09-09 09:35:29 +00004157 } else {
4158 t_nbytes = newTemp(Ity_I32);
cerion76de5cf2005-11-18 18:25:12 +00004159 assign( t_nbytes, mkU32(NumBytes==0 ? 32 : NumBytes) );
ceriond953ebb2005-11-29 13:27:20 +00004160 generate_stsw_sequence( t_nbytes, t_EA, rD_addr, 32 );
sewardj5876fa12005-09-09 09:35:29 +00004161 *stopHere = True;
4162 }
4163 return True;
4164
sewardj5876fa12005-09-09 09:35:29 +00004165 case 0x295: // stswx (Store String Word Indexed, PPC32 p529)
ceriond953ebb2005-11-29 13:27:20 +00004166 DIP("stswx r%u,r%u,r%u\n", rS_addr, rA_addr, rB_addr);
sewardj5876fa12005-09-09 09:35:29 +00004167 t_nbytes = newTemp(Ity_I32);
ceriond953ebb2005-11-29 13:27:20 +00004168 assign( t_EA, ea_rAor0_idxd(rA_addr,rB_addr) );
cerionedf7fc52005-11-18 20:57:41 +00004169 assign( t_nbytes, unop( Iop_8Uto32, getXER_BC() ) );
ceriond953ebb2005-11-29 13:27:20 +00004170 generate_stsw_sequence( t_nbytes, t_EA, rS_addr, 128 );
sewardj5876fa12005-09-09 09:35:29 +00004171 *stopHere = True;
4172 return True;
sewardj87e651f2005-09-09 08:31:18 +00004173
4174 default:
cerion5b2325f2005-12-23 00:55:09 +00004175 vex_printf("dis_int_ldst_str(ppc)(opc2)\n");
sewardj87e651f2005-09-09 08:31:18 +00004176 return False;
4177 }
4178 return True;
4179}
4180
cerion094d1392005-06-20 13:45:57 +00004181
sewardjb51f0f42005-07-18 11:38:02 +00004182/* ------------------------------------------------------------------
4183 Integer Branch Instructions
4184 ------------------------------------------------------------------ */
cerion645c9302005-01-31 10:09:59 +00004185
cerion45552a92005-02-03 18:20:22 +00004186/*
4187 Branch helper function
4188 ok = BO[2] | ((CTR[0] != 0) ^ BO[1])
sewardjb51f0f42005-07-18 11:38:02 +00004189 Returns an I32 which is 0x00000000 if the ctr condition failed
4190 and 0xFFFFFFFF otherwise.
cerion45552a92005-02-03 18:20:22 +00004191*/
sewardjb51f0f42005-07-18 11:38:02 +00004192static IRExpr* /* :: Ity_I32 */ branch_ctr_ok( UInt BO )
cerion45552a92005-02-03 18:20:22 +00004193{
ceriond953ebb2005-11-29 13:27:20 +00004194 IRType ty = mode64 ? Ity_I64 : Ity_I32;
sewardjb51f0f42005-07-18 11:38:02 +00004195 IRTemp ok = newTemp(Ity_I32);
cerioned623db2005-06-20 12:42:04 +00004196
cerionf0de28c2005-12-13 20:21:11 +00004197 if ((BO >> 2) & 1) { // independent of ctr
sewardjb51f0f42005-07-18 11:38:02 +00004198 assign( ok, mkU32(0xFFFFFFFF) );
cerionb85e8bb2005-02-16 08:54:33 +00004199 } else {
cerionf0de28c2005-12-13 20:21:11 +00004200 if ((BO >> 1) & 1) { // ctr == 0 ?
ceriond953ebb2005-11-29 13:27:20 +00004201 assign( ok, unop( Iop_1Sto32,
cerion4e2c2b32006-01-02 13:35:51 +00004202 binop( mkSzOp(ty, Iop_CmpEQ8),
4203 getGST( PPC_GST_CTR ),
4204 mkSzImm(ty,0))) );
cerionf0de28c2005-12-13 20:21:11 +00004205 } else { // ctr != 0 ?
sewardjb51f0f42005-07-18 11:38:02 +00004206 assign( ok, unop( Iop_1Sto32,
cerion4e2c2b32006-01-02 13:35:51 +00004207 binop( mkSzOp(ty, Iop_CmpNE8),
4208 getGST( PPC_GST_CTR ),
4209 mkSzImm(ty,0))) );
cerionb85e8bb2005-02-16 08:54:33 +00004210 }
4211 }
4212 return mkexpr(ok);
cerion45552a92005-02-03 18:20:22 +00004213}
4214
sewardjb51f0f42005-07-18 11:38:02 +00004215
cerion45552a92005-02-03 18:20:22 +00004216/*
sewardjb51f0f42005-07-18 11:38:02 +00004217 Branch helper function cond_ok = BO[4] | (CR[BI] == BO[3])
4218 Returns an I32 which is either 0 if the condition failed or
4219 some arbitrary nonzero value otherwise. */
4220
4221static IRExpr* /* :: Ity_I32 */ branch_cond_ok( UInt BO, UInt BI )
cerion45552a92005-02-03 18:20:22 +00004222{
sewardjb51f0f42005-07-18 11:38:02 +00004223 Int where;
4224 IRTemp res = newTemp(Ity_I32);
cerionb85e8bb2005-02-16 08:54:33 +00004225 IRTemp cr_bi = newTemp(Ity_I32);
4226
sewardjb51f0f42005-07-18 11:38:02 +00004227 if ((BO >> 4) & 1) {
4228 assign( res, mkU32(1) );
cerionb85e8bb2005-02-16 08:54:33 +00004229 } else {
sewardjb51f0f42005-07-18 11:38:02 +00004230 // ok = (CR[BI] == BO[3]) Note, the following relies on
4231 // getCRbit_anywhere returning a value which
4232 // is either zero or has exactly 1 bit set.
4233 assign( cr_bi, getCRbit_anywhere( BI, &where ) );
cerione9d361a2005-03-04 17:35:29 +00004234
4235 if ((BO >> 3) & 1) {
sewardjb51f0f42005-07-18 11:38:02 +00004236 /* We can use cr_bi as-is. */
4237 assign( res, mkexpr(cr_bi) );
cerione9d361a2005-03-04 17:35:29 +00004238 } else {
sewardjb51f0f42005-07-18 11:38:02 +00004239 /* We have to invert the sense of the information held in
4240 cr_bi. For that we need to know which bit
cerion76de5cf2005-11-18 18:25:12 +00004241 getCRbit_anywhere regards as significant. */
cerion5b2325f2005-12-23 00:55:09 +00004242 assign( res, binop(Iop_Xor32, mkexpr(cr_bi),
4243 mkU32(1<<where)) );
cerionb85e8bb2005-02-16 08:54:33 +00004244 }
4245 }
sewardjb51f0f42005-07-18 11:38:02 +00004246 return mkexpr(res);
cerion45552a92005-02-03 18:20:22 +00004247}
4248
4249
cerion3d870a32005-03-18 12:23:33 +00004250/*
4251 Integer Branch Instructions
4252*/
sewardj9d540e52005-10-08 11:28:16 +00004253static Bool dis_branch ( UInt theInstr,
sewardjaca070a2006-10-17 00:28:22 +00004254 VexMiscInfo* vmi,
sewardj9d540e52005-10-08 11:28:16 +00004255 /*OUT*/DisResult* dres,
sewardjc716aea2006-01-17 01:48:46 +00004256 Bool (*resteerOkFn)(void*,Addr64),
4257 void* callback_opaque )
cerion91ad5362005-01-27 23:02:41 +00004258{
ceriond953ebb2005-11-29 13:27:20 +00004259 UChar opc1 = ifieldOPC(theInstr);
4260 UChar BO = ifieldRegDS(theInstr);
4261 UChar BI = ifieldRegA(theInstr);
4262 UInt BD_u16 = ifieldUIMM16(theInstr) & 0xFFFFFFFC; /* mask off */
4263 UChar b11to15 = ifieldRegB(theInstr);
4264 UInt opc2 = ifieldOPClo10(theInstr);
4265 UInt LI_u26 = ifieldUIMM26(theInstr) & 0xFFFFFFFC; /* mask off */
4266 UChar flag_AA = ifieldBIT1(theInstr);
4267 UChar flag_LK = ifieldBIT0(theInstr);
4268
cerion2831b002005-11-30 19:55:22 +00004269 IRType ty = mode64 ? Ity_I64 : Ity_I32;
ceriond953ebb2005-11-29 13:27:20 +00004270 Addr64 tgt = 0;
4271 Int BD = extend_s_16to32(BD_u16);
cerion2831b002005-11-30 19:55:22 +00004272 IRTemp do_branch = newTemp(Ity_I32);
4273 IRTemp ctr_ok = newTemp(Ity_I32);
4274 IRTemp cond_ok = newTemp(Ity_I32);
4275 IRExpr* e_nia = mkSzImm(ty, nextInsnAddr());
4276 IRConst* c_nia = mkSzConst(ty, nextInsnAddr());
sewardjdf07b882005-11-29 18:19:11 +00004277 IRTemp lr_old = newTemp(ty);
ceriond953ebb2005-11-29 13:27:20 +00004278
cerionb85e8bb2005-02-16 08:54:33 +00004279 /* Hack to pass through code that just wants to read the PC */
4280 if (theInstr == 0x429F0005) {
sewardjb51f0f42005-07-18 11:38:02 +00004281 DIP("bcl 0x%x, 0x%x (a.k.a mr lr,cia+4)\n", BO, BI);
ceriond953ebb2005-11-29 13:27:20 +00004282 putGST( PPC_GST_LR, e_nia );
cerionb85e8bb2005-02-16 08:54:33 +00004283 return True;
sewardjb51f0f42005-07-18 11:38:02 +00004284 }
sewardj9d540e52005-10-08 11:28:16 +00004285
4286 /* The default what-next. Individual cases can override it. */
4287 dres->whatNext = Dis_StopHere;
4288
cerionb85e8bb2005-02-16 08:54:33 +00004289 switch (opc1) {
cerione9d361a2005-03-04 17:35:29 +00004290 case 0x12: // b (Branch, PPC32 p360)
cerion4561acb2005-02-21 14:07:48 +00004291 if (flag_AA) {
cerion2831b002005-11-30 19:55:22 +00004292 tgt = mkSzAddr( ty, extend_s_26to64(LI_u26) );
cerion4561acb2005-02-21 14:07:48 +00004293 } else {
cerion2831b002005-11-30 19:55:22 +00004294 tgt = mkSzAddr( ty, guest_CIA_curr_instr +
4295 (Long)extend_s_26to64(LI_u26) );
cerionb85e8bb2005-02-16 08:54:33 +00004296 }
ceriond953ebb2005-11-29 13:27:20 +00004297 if (mode64) {
4298 DIP("b%s%s 0x%llx\n",
4299 flag_LK ? "l" : "", flag_AA ? "a" : "", tgt);
4300 } else {
4301 DIP("b%s%s 0x%x\n",
4302 flag_LK ? "l" : "", flag_AA ? "a" : "", (Addr32)tgt);
sewardj9d540e52005-10-08 11:28:16 +00004303 }
4304
sewardjcf8986c2006-01-18 04:14:52 +00004305 if (flag_LK) {
ceriond953ebb2005-11-29 13:27:20 +00004306 putGST( PPC_GST_LR, e_nia );
sewardjaca070a2006-10-17 00:28:22 +00004307 if (vmi->guest_ppc_zap_RZ_at_bl
4308 && vmi->guest_ppc_zap_RZ_at_bl( (ULong)tgt) )
4309 make_redzone_AbiHint( vmi,
4310 "branch-and-link (unconditional call)" );
sewardjcf8986c2006-01-18 04:14:52 +00004311 }
ceriond953ebb2005-11-29 13:27:20 +00004312
sewardjc716aea2006-01-17 01:48:46 +00004313 if (resteerOkFn( callback_opaque, tgt )) {
ceriond953ebb2005-11-29 13:27:20 +00004314 dres->whatNext = Dis_Resteer;
4315 dres->continueAt = tgt;
sewardj9d540e52005-10-08 11:28:16 +00004316 } else {
4317 irbb->jumpkind = flag_LK ? Ijk_Call : Ijk_Boring;
cerion2831b002005-11-30 19:55:22 +00004318 irbb->next = mkSzImm(ty, tgt);
sewardj9d540e52005-10-08 11:28:16 +00004319 }
cerionb85e8bb2005-02-16 08:54:33 +00004320 break;
4321
cerione9d361a2005-03-04 17:35:29 +00004322 case 0x10: // bc (Branch Conditional, PPC32 p361)
cerionb85e8bb2005-02-16 08:54:33 +00004323 DIP("bc%s%s 0x%x, 0x%x, 0x%x\n",
ceriond953ebb2005-11-29 13:27:20 +00004324 flag_LK ? "l" : "", flag_AA ? "a" : "", BO, BI, BD);
cerionb85e8bb2005-02-16 08:54:33 +00004325
4326 if (!(BO & 0x4)) {
ceriond953ebb2005-11-29 13:27:20 +00004327 putGST( PPC_GST_CTR,
cerion2831b002005-11-30 19:55:22 +00004328 binop(mkSzOp(ty, Iop_Sub8),
4329 getGST( PPC_GST_CTR ), mkSzImm(ty, 1)) );
cerionb85e8bb2005-02-16 08:54:33 +00004330 }
sewardjb51f0f42005-07-18 11:38:02 +00004331
4332 /* This is a bit subtle. ctr_ok is either all 0s or all 1s.
cerion76de5cf2005-11-18 18:25:12 +00004333 cond_ok is either zero or nonzero, since that's the cheapest
4334 way to compute it. Anding them together gives a value which
4335 is either zero or non zero and so that's what we must test
4336 for in the IRStmt_Exit. */
sewardjb51f0f42005-07-18 11:38:02 +00004337 assign( ctr_ok, branch_ctr_ok( BO ) );
cerionb85e8bb2005-02-16 08:54:33 +00004338 assign( cond_ok, branch_cond_ok( BO, BI ) );
sewardjb51f0f42005-07-18 11:38:02 +00004339 assign( do_branch,
4340 binop(Iop_And32, mkexpr(cond_ok), mkexpr(ctr_ok)) );
4341
cerion4561acb2005-02-21 14:07:48 +00004342 if (flag_AA) {
cerion2831b002005-11-30 19:55:22 +00004343 tgt = mkSzAddr(ty, extend_s_16to64(BD_u16));
cerion4561acb2005-02-21 14:07:48 +00004344 } else {
cerion2831b002005-11-30 19:55:22 +00004345 tgt = mkSzAddr(ty, guest_CIA_curr_instr +
4346 (Long)extend_s_16to64(BD_u16));
cerionb85e8bb2005-02-16 08:54:33 +00004347 }
ceriond953ebb2005-11-29 13:27:20 +00004348 if (flag_LK)
4349 putGST( PPC_GST_LR, e_nia );
cerionb85e8bb2005-02-16 08:54:33 +00004350
ceriond953ebb2005-11-29 13:27:20 +00004351 stmt( IRStmt_Exit(
4352 binop(Iop_CmpNE32, mkexpr(do_branch), mkU32(0)),
4353 flag_LK ? Ijk_Call : Ijk_Boring,
cerion2831b002005-11-30 19:55:22 +00004354 mkSzConst(ty, tgt) ) );
cerionb85e8bb2005-02-16 08:54:33 +00004355
4356 irbb->jumpkind = Ijk_Boring;
ceriond953ebb2005-11-29 13:27:20 +00004357 irbb->next = e_nia;
cerionb85e8bb2005-02-16 08:54:33 +00004358 break;
4359
4360 case 0x13:
sewardj6be67232006-01-24 19:00:05 +00004361 /* For bclr and bcctr, it appears that the lowest two bits of
4362 b11to15 are a branch hint, and so we only need to ensure it's
4363 of the form 000XX. */
4364 if ((b11to15 & ~3) != 0) {
4365 vex_printf("dis_int_branch(ppc)(0x13,b11to15)(%d)\n", (Int)b11to15);
cerionb85e8bb2005-02-16 08:54:33 +00004366 return False;
4367 }
cerion91ad5362005-01-27 23:02:41 +00004368
cerionb85e8bb2005-02-16 08:54:33 +00004369 switch (opc2) {
cerione9d361a2005-03-04 17:35:29 +00004370 case 0x210: // bcctr (Branch Cond. to Count Register, PPC32 p363)
cerion5b2325f2005-12-23 00:55:09 +00004371 if ((BO & 0x4) == 0) { // "decr and test CTR" option invalid
4372 vex_printf("dis_int_branch(ppc)(bcctr,BO)\n");
cerionb85e8bb2005-02-16 08:54:33 +00004373 return False;
4374 }
ceriona31e8b52005-02-21 16:30:45 +00004375 DIP("bcctr%s 0x%x, 0x%x\n", flag_LK ? "l" : "", BO, BI);
cerionb85e8bb2005-02-16 08:54:33 +00004376
4377 assign( cond_ok, branch_cond_ok( BO, BI ) );
ceriond953ebb2005-11-29 13:27:20 +00004378
cerion2831b002005-11-30 19:55:22 +00004379 assign( lr_old, addr_align( getGST( PPC_GST_CTR ), 4 ));
sewardjdf07b882005-11-29 18:19:11 +00004380
ceriond953ebb2005-11-29 13:27:20 +00004381 if (flag_LK)
4382 putGST( PPC_GST_LR, e_nia );
cerionb85e8bb2005-02-16 08:54:33 +00004383
sewardjb51f0f42005-07-18 11:38:02 +00004384 stmt( IRStmt_Exit(
4385 binop(Iop_CmpEQ32, mkexpr(cond_ok), mkU32(0)),
4386 Ijk_Boring,
ceriond953ebb2005-11-29 13:27:20 +00004387 c_nia ));
cerionb85e8bb2005-02-16 08:54:33 +00004388
4389 irbb->jumpkind = flag_LK ? Ijk_Call : Ijk_Boring;
sewardjdf07b882005-11-29 18:19:11 +00004390 irbb->next = mkexpr(lr_old);
cerionb85e8bb2005-02-16 08:54:33 +00004391 break;
4392
sewardjcf8986c2006-01-18 04:14:52 +00004393 case 0x010: { // bclr (Branch Cond. to Link Register, PPC32 p365)
4394 Bool vanilla_return = False;
sewardjb51f0f42005-07-18 11:38:02 +00004395 if ((BO & 0x14 /* 1z1zz */) == 0x14 && flag_LK == 0) {
cerion225a0342005-09-12 20:49:09 +00004396 DIP("blr\n");
sewardjcf8986c2006-01-18 04:14:52 +00004397 vanilla_return = True;
sewardjb51f0f42005-07-18 11:38:02 +00004398 } else {
4399 DIP("bclr%s 0x%x, 0x%x\n", flag_LK ? "l" : "", BO, BI);
4400 }
cerion91ad5362005-01-27 23:02:41 +00004401
cerionb85e8bb2005-02-16 08:54:33 +00004402 if (!(BO & 0x4)) {
ceriond953ebb2005-11-29 13:27:20 +00004403 putGST( PPC_GST_CTR,
cerion2831b002005-11-30 19:55:22 +00004404 binop(mkSzOp(ty, Iop_Sub8),
4405 getGST( PPC_GST_CTR ), mkSzImm(ty, 1)) );
cerionb85e8bb2005-02-16 08:54:33 +00004406 }
4407
sewardjb51f0f42005-07-18 11:38:02 +00004408 /* See comments above for 'bc' about this */
4409 assign( ctr_ok, branch_ctr_ok( BO ) );
4410 assign( cond_ok, branch_cond_ok( BO, BI ) );
4411 assign( do_branch,
4412 binop(Iop_And32, mkexpr(cond_ok), mkexpr(ctr_ok)) );
cerion2831b002005-11-30 19:55:22 +00004413
sewardjdf07b882005-11-29 18:19:11 +00004414 assign( lr_old, addr_align( getGST( PPC_GST_LR ), 4 ));
4415
ceriond953ebb2005-11-29 13:27:20 +00004416 if (flag_LK)
4417 putGST( PPC_GST_LR, e_nia );
sewardjb51f0f42005-07-18 11:38:02 +00004418
4419 stmt( IRStmt_Exit(
4420 binop(Iop_CmpEQ32, mkexpr(do_branch), mkU32(0)),
4421 Ijk_Boring,
ceriond953ebb2005-11-29 13:27:20 +00004422 c_nia ));
sewardjb51f0f42005-07-18 11:38:02 +00004423
sewardjaca070a2006-10-17 00:28:22 +00004424 if (vanilla_return && vmi->guest_ppc_zap_RZ_at_blr)
4425 make_redzone_AbiHint( vmi, "branch-to-lr (unconditional return)" );
sewardjcf8986c2006-01-18 04:14:52 +00004426
sewardjd37be032005-11-12 12:56:31 +00004427 /* blrl is pretty strange; it's like a return that sets the
4428 return address of its caller to the insn following this
4429 one. Mark it as a return. */
4430 irbb->jumpkind = Ijk_Ret; /* was flag_LK ? Ijk_Call : Ijk_Ret; */
sewardjdf07b882005-11-29 18:19:11 +00004431 irbb->next = mkexpr(lr_old);
cerionb85e8bb2005-02-16 08:54:33 +00004432 break;
sewardjcf8986c2006-01-18 04:14:52 +00004433 }
cerionb85e8bb2005-02-16 08:54:33 +00004434 default:
cerion5b2325f2005-12-23 00:55:09 +00004435 vex_printf("dis_int_branch(ppc)(opc2)\n");
cerionb85e8bb2005-02-16 08:54:33 +00004436 return False;
4437 }
4438 break;
cerion2831b002005-11-30 19:55:22 +00004439
cerionb85e8bb2005-02-16 08:54:33 +00004440 default:
cerion5b2325f2005-12-23 00:55:09 +00004441 vex_printf("dis_int_branch(ppc)(opc1)\n");
cerionb85e8bb2005-02-16 08:54:33 +00004442 return False;
4443 }
cerion2831b002005-11-30 19:55:22 +00004444
cerionb85e8bb2005-02-16 08:54:33 +00004445 return True;
cerion91ad5362005-01-27 23:02:41 +00004446}
4447
4448
4449
cerion3d870a32005-03-18 12:23:33 +00004450/*
4451 Condition Register Logical Instructions
4452*/
cerion3007c7f2005-02-23 23:13:29 +00004453static Bool dis_cond_logic ( UInt theInstr )
4454{
4455 /* XL-Form */
cerion76de5cf2005-11-18 18:25:12 +00004456 UChar opc1 = ifieldOPC(theInstr);
4457 UChar crbD_addr = ifieldRegDS(theInstr);
4458 UChar crfD_addr = toUChar( IFIELD(theInstr, 23, 3) );
4459 UChar crbA_addr = ifieldRegA(theInstr);
4460 UChar crfS_addr = toUChar( IFIELD(theInstr, 18, 3) );
4461 UChar crbB_addr = ifieldRegB(theInstr);
4462 UInt opc2 = ifieldOPClo10(theInstr);
4463 UChar b0 = ifieldBIT0(theInstr);
cerion3007c7f2005-02-23 23:13:29 +00004464
cerionf0de28c2005-12-13 20:21:11 +00004465 IRTemp crbD = newTemp(Ity_I32);
4466 IRTemp crbA = newTemp(Ity_I32);
4467 IRTemp crbB = newTemp(Ity_I32);
cerion3007c7f2005-02-23 23:13:29 +00004468
4469 if (opc1 != 19 || b0 != 0) {
cerion5b2325f2005-12-23 00:55:09 +00004470 vex_printf("dis_cond_logic(ppc)(opc1)\n");
cerion3007c7f2005-02-23 23:13:29 +00004471 return False;
4472 }
4473
cerione9d361a2005-03-04 17:35:29 +00004474 if (opc2 == 0) { // mcrf (Move Cond Reg Field, PPC32 p464)
cerion3007c7f2005-02-23 23:13:29 +00004475 if (((crbD_addr & 0x3) != 0) ||
cerion76de5cf2005-11-18 18:25:12 +00004476 ((crbA_addr & 0x3) != 0) || (crbB_addr != 0)) {
cerion5b2325f2005-12-23 00:55:09 +00004477 vex_printf("dis_cond_logic(ppc)(crbD|crbA|crbB != 0)\n");
cerion3007c7f2005-02-23 23:13:29 +00004478 return False;
cerion76de5cf2005-11-18 18:25:12 +00004479 }
ceriond953ebb2005-11-29 13:27:20 +00004480 DIP("mcrf cr%u,cr%u\n", crfD_addr, crfS_addr);
sewardjb51f0f42005-07-18 11:38:02 +00004481 putCR0( crfD_addr, getCR0( crfS_addr) );
4482 putCR321( crfD_addr, getCR321(crfS_addr) );
cerion3007c7f2005-02-23 23:13:29 +00004483 } else {
sewardjb51f0f42005-07-18 11:38:02 +00004484 assign( crbA, getCRbit(crbA_addr) );
ceriona50fde52005-07-01 21:16:10 +00004485 if (crbA_addr == crbB_addr)
sewardjb51f0f42005-07-18 11:38:02 +00004486 crbB = crbA;
ceriona50fde52005-07-01 21:16:10 +00004487 else
sewardjb51f0f42005-07-18 11:38:02 +00004488 assign( crbB, getCRbit(crbB_addr) );
cerion3007c7f2005-02-23 23:13:29 +00004489
4490 switch (opc2) {
sewardj7c2dc712005-09-08 17:33:27 +00004491 case 0x101: // crand (Cond Reg AND, PPC32 p372)
4492 DIP("crand crb%d,crb%d,crb%d\n", crbD_addr, crbA_addr, crbB_addr);
4493 assign( crbD, binop(Iop_And32, mkexpr(crbA), mkexpr(crbB)) );
4494 break;
sewardj7787af42005-08-04 18:32:19 +00004495 case 0x081: // crandc (Cond Reg AND w. Complement, PPC32 p373)
4496 DIP("crandc crb%d,crb%d,crb%d\n", crbD_addr, crbA_addr, crbB_addr);
4497 assign( crbD, binop(Iop_And32,
4498 mkexpr(crbA),
4499 unop(Iop_Not32, mkexpr(crbB))) );
4500 break;
sewardje14bb9f2005-07-22 09:39:02 +00004501 case 0x121: // creqv (Cond Reg Equivalent, PPC32 p374)
4502 DIP("creqv crb%d,crb%d,crb%d\n", crbD_addr, crbA_addr, crbB_addr);
4503 assign( crbD, unop(Iop_Not32,
4504 binop(Iop_Xor32, mkexpr(crbA), mkexpr(crbB))) );
4505 break;
sewardj7c2dc712005-09-08 17:33:27 +00004506 case 0x0E1: // crnand (Cond Reg NAND, PPC32 p375)
4507 DIP("crnand crb%d,crb%d,crb%d\n", crbD_addr, crbA_addr, crbB_addr);
4508 assign( crbD, unop(Iop_Not32,
4509 binop(Iop_And32, mkexpr(crbA), mkexpr(crbB))) );
4510 break;
cerione9d361a2005-03-04 17:35:29 +00004511 case 0x021: // crnor (Cond Reg NOR, PPC32 p376)
cerion3007c7f2005-02-23 23:13:29 +00004512 DIP("crnor crb%d,crb%d,crb%d\n", crbD_addr, crbA_addr, crbB_addr);
4513 assign( crbD, unop(Iop_Not32,
4514 binop(Iop_Or32, mkexpr(crbA), mkexpr(crbB))) );
4515 break;
cerione9d361a2005-03-04 17:35:29 +00004516 case 0x1C1: // cror (Cond Reg OR, PPC32 p377)
cerion3007c7f2005-02-23 23:13:29 +00004517 DIP("cror crb%d,crb%d,crb%d\n", crbD_addr, crbA_addr, crbB_addr);
4518 assign( crbD, binop(Iop_Or32, mkexpr(crbA), mkexpr(crbB)) );
4519 break;
sewardj7c2dc712005-09-08 17:33:27 +00004520 case 0x1A1: // crorc (Cond Reg OR w. Complement, PPC32 p378)
4521 DIP("crorc crb%d,crb%d,crb%d\n", crbD_addr, crbA_addr, crbB_addr);
4522 assign( crbD, binop(Iop_Or32,
4523 mkexpr(crbA),
4524 unop(Iop_Not32, mkexpr(crbB))) );
4525 break;
cerione9d361a2005-03-04 17:35:29 +00004526 case 0x0C1: // crxor (Cond Reg XOR, PPC32 p379)
cerion3007c7f2005-02-23 23:13:29 +00004527 DIP("crxor crb%d,crb%d,crb%d\n", crbD_addr, crbA_addr, crbB_addr);
4528 assign( crbD, binop(Iop_Xor32, mkexpr(crbA), mkexpr(crbB)) );
4529 break;
cerion3007c7f2005-02-23 23:13:29 +00004530 default:
cerion5b2325f2005-12-23 00:55:09 +00004531 vex_printf("dis_cond_logic(ppc)(opc2)\n");
cerion3007c7f2005-02-23 23:13:29 +00004532 return False;
4533 }
4534
sewardjb51f0f42005-07-18 11:38:02 +00004535 putCRbit( crbD_addr, mkexpr(crbD) );
cerion3007c7f2005-02-23 23:13:29 +00004536 }
4537 return True;
4538}
4539
4540
sewardj334870d2006-02-07 16:42:39 +00004541/*
4542 Trap instructions
4543*/
4544
4545/* Do the code generation for a trap. Returned Bool is true iff
4546 this is an unconditional trap. */
sewardj2d19fe32006-02-07 20:55:08 +00004547static Bool do_trap ( Bool is_twi, UChar TO,
sewardj334870d2006-02-07 16:42:39 +00004548 IRExpr* argL0, ULong argR0, Addr64 cia )
4549{
4550 IRTemp argL, argR;
4551 IRExpr *argLe, *argRe, *cond, *tmp;
4552
sewardj2d19fe32006-02-07 20:55:08 +00004553 IROp opAND = is_twi ? Iop_And32 : Iop_And64;
4554 IROp opOR = is_twi ? Iop_Or32 : Iop_Or64;
4555 IROp opCMPORDS = is_twi ? Iop_CmpORD32S : Iop_CmpORD64S;
4556 IROp opCMPORDU = is_twi ? Iop_CmpORD32U : Iop_CmpORD64U;
4557 IROp opCMPNE = is_twi ? Iop_CmpNE32 : Iop_CmpNE64;
4558 IROp opCMPEQ = is_twi ? Iop_CmpEQ32 : Iop_CmpEQ64;
4559 IRExpr* const0 = is_twi ? mkU32(0) : mkU64(0);
4560 IRExpr* const2 = is_twi ? mkU32(2) : mkU64(2);
4561 IRExpr* const4 = is_twi ? mkU32(4) : mkU64(4);
4562 IRExpr* const8 = is_twi ? mkU32(8) : mkU64(8);
sewardj334870d2006-02-07 16:42:39 +00004563
4564 const UChar b11100 = 0x1C;
4565 const UChar b00111 = 0x07;
4566
4567 if ((TO & b11100) == b11100 || (TO & b00111) == b00111) {
4568 /* Unconditional trap. Just do the exit without
4569 testing the arguments. */
4570 stmt( IRStmt_Exit(
4571 binop(opCMPEQ, const0, const0),
4572 Ijk_Trap,
4573 mode64 ? IRConst_U64(cia) : IRConst_U32((UInt)cia)
4574 ));
4575 return True; /* unconditional trap */
4576 }
4577
sewardj2d19fe32006-02-07 20:55:08 +00004578 if (is_twi) {
4579 argL = newTemp(Ity_I32);
4580 argR = newTemp(Ity_I32);
4581 assign( argL, mode64 ? mkSzNarrow32(Ity_I64,argL0)
4582 : argL0 );
sewardj334870d2006-02-07 16:42:39 +00004583 assign( argR, mkU32( (UInt)argR0 ));
4584 } else {
4585 vassert(mode64);
sewardj2d19fe32006-02-07 20:55:08 +00004586 argL = newTemp(Ity_I64);
4587 argR = newTemp(Ity_I64);
sewardj334870d2006-02-07 16:42:39 +00004588 assign( argL, argL0 );
4589 assign( argR, mkU64( argR0 ));
4590 }
4591 argLe = mkexpr(argL);
4592 argRe = mkexpr(argR);
4593 cond = const0;
4594 if (TO & 16) { // L <s R
4595 tmp = binop(opAND, binop(opCMPORDS, argLe, argRe), const8);
4596 cond = binop(opOR, tmp, cond);
4597 }
4598 if (TO & 8) { // L >s R
4599 tmp = binop(opAND, binop(opCMPORDS, argLe, argRe), const4);
4600 cond = binop(opOR, tmp, cond);
4601 }
4602 if (TO & 4) { // L == R
4603 tmp = binop(opAND, binop(opCMPORDS, argLe, argRe), const2);
4604 cond = binop(opOR, tmp, cond);
4605 }
4606 if (TO & 2) { // L <u R
4607 tmp = binop(opAND, binop(opCMPORDU, argLe, argRe), const8);
4608 cond = binop(opOR, tmp, cond);
4609 }
4610 if (TO & 1) { // L >u R
4611 tmp = binop(opAND, binop(opCMPORDU, argLe, argRe), const4);
4612 cond = binop(opOR, tmp, cond);
4613 }
4614 stmt( IRStmt_Exit(
4615 binop(opCMPNE, cond, const0),
4616 Ijk_Trap,
4617 mode64 ? IRConst_U64(cia) : IRConst_U32((UInt)cia)
4618 ));
4619 return False; /* not an unconditional trap */
4620}
4621
4622static Bool dis_trapi ( UInt theInstr,
4623 /*OUT*/DisResult* dres )
4624{
4625 /* D-Form */
4626 UChar opc1 = ifieldOPC(theInstr);
4627 UChar TO = ifieldRegDS(theInstr);
4628 UChar rA_addr = ifieldRegA(theInstr);
4629 UInt uimm16 = ifieldUIMM16(theInstr);
4630 ULong simm16 = extend_s_16to64(uimm16);
4631 Addr64 cia = guest_CIA_curr_instr;
4632 IRType ty = mode64 ? Ity_I64 : Ity_I32;
4633 Bool uncond = False;
4634
4635 switch (opc1) {
4636 case 0x03: // twi (Trap Word Immediate, PPC32 p548)
sewardj2d19fe32006-02-07 20:55:08 +00004637 uncond = do_trap( True/*is_twi*/, TO, getIReg(rA_addr), simm16, cia );
sewardj334870d2006-02-07 16:42:39 +00004638 if (TO == 4) {
4639 DIP("tweqi r%u,%d\n", (UInt)rA_addr, (Int)simm16);
4640 } else {
4641 DIP("tw%di r%u,%d\n", (Int)TO, (UInt)rA_addr, (Int)simm16);
4642 }
4643 break;
4644 case 0x02: // tdi
4645 if (!mode64)
4646 return False;
sewardj2d19fe32006-02-07 20:55:08 +00004647 uncond = do_trap( False/*!is_twi*/, TO, getIReg(rA_addr), simm16, cia );
4648 if (TO == 4) {
4649 DIP("tdeqi r%u,%d\n", (UInt)rA_addr, (Int)simm16);
4650 } else {
4651 DIP("td%di r%u,%d\n", (Int)TO, (UInt)rA_addr, (Int)simm16);
4652 }
sewardj334870d2006-02-07 16:42:39 +00004653 break;
4654 default:
4655 return False;
4656 }
4657
4658 if (uncond) {
4659 /* If the trap shows signs of being unconditional, don't
4660 continue decoding past it. */
4661 irbb->next = mkSzImm( ty, nextInsnAddr() );
4662 irbb->jumpkind = Ijk_Boring;
4663 dres->whatNext = Dis_StopHere;
4664 }
4665
4666 return True;
4667}
4668
4669
cerion3d870a32005-03-18 12:23:33 +00004670/*
4671 System Linkage Instructions
4672*/
sewardjaca070a2006-10-17 00:28:22 +00004673static Bool dis_syslink ( UInt theInstr,
4674 VexMiscInfo* miscinfo, DisResult* dres )
cerion8c3adda2005-01-31 11:54:05 +00004675{
ceriond953ebb2005-11-29 13:27:20 +00004676 IRType ty = mode64 ? Ity_I64 : Ity_I32;
4677
cerionb85e8bb2005-02-16 08:54:33 +00004678 if (theInstr != 0x44000002) {
cerion5b2325f2005-12-23 00:55:09 +00004679 vex_printf("dis_syslink(ppc)(theInstr)\n");
cerionb85e8bb2005-02-16 08:54:33 +00004680 return False;
4681 }
cerione1d857b2005-02-04 18:29:05 +00004682
cerione9d361a2005-03-04 17:35:29 +00004683 // sc (System Call, PPC32 p504)
cerionb85e8bb2005-02-16 08:54:33 +00004684 DIP("sc\n");
sewardjaca070a2006-10-17 00:28:22 +00004685
4686 /* Copy CIA into the CIA_AT_SC pseudo-register, so that on AIX
4687 Valgrind can back the guest up to this instruction if it needs
4688 to restart the syscall. */
4689 putGST( PPC_GST_CIA_AT_SC, getGST( PPC_GST_CIA ) );
4690
cerionb85e8bb2005-02-16 08:54:33 +00004691 /* It's important that all ArchRegs carry their up-to-date value
4692 at this point. So we declare an end-of-block here, which
4693 forces any TempRegs caching ArchRegs to be flushed. */
sewardjaca070a2006-10-17 00:28:22 +00004694 /* At this point, AIX's behaviour differs from Linux's: AIX resumes
4695 after the syscall at %lr, whereas Linux does the obvious thing
4696 and resumes at the next instruction. Hence we need to encode
4697 that into the generated IR. */
4698 irbb->next = miscinfo->guest_ppc_sc_continues_at_LR
4699 ? /*AIXishly*/getGST( PPC_GST_LR )
4700 : /*Linuxfully*/mkSzImm( ty, nextInsnAddr() );
sewardj4fa325a2005-11-03 13:27:24 +00004701 irbb->jumpkind = Ijk_Sys_syscall;
ceriond953ebb2005-11-29 13:27:20 +00004702
sewardj9e6491a2005-07-02 19:24:10 +00004703 dres->whatNext = Dis_StopHere;
cerionb85e8bb2005-02-16 08:54:33 +00004704 return True;
cerion8c3adda2005-01-31 11:54:05 +00004705}
4706
cerion3d870a32005-03-18 12:23:33 +00004707
4708/*
4709 Memory Synchronization Instructions
cerion07b07a92005-12-22 14:32:35 +00004710
4711 Note on Reservations:
4712 We rely on the assumption that V will in fact only allow one thread at
4713 once to run. In effect, a thread can make a reservation, but we don't
4714 check any stores it does. Instead, the reservation is cancelled when
4715 the scheduler switches to another thread (run_thread_for_a_while()).
cerion3d870a32005-03-18 12:23:33 +00004716*/
cerion8c3adda2005-01-31 11:54:05 +00004717static Bool dis_memsync ( UInt theInstr )
4718{
cerionb85e8bb2005-02-16 08:54:33 +00004719 /* X-Form, XL-Form */
ceriond953ebb2005-11-29 13:27:20 +00004720 UChar opc1 = ifieldOPC(theInstr);
4721 UInt b11to25 = IFIELD(theInstr, 11, 15);
cerione43bc882006-01-05 13:11:59 +00004722 UChar flag_L = ifieldRegDS(theInstr);
4723 UInt b11to20 = IFIELD(theInstr, 11, 10);
ceriond953ebb2005-11-29 13:27:20 +00004724 UChar rD_addr = ifieldRegDS(theInstr);
4725 UChar rS_addr = rD_addr;
4726 UChar rA_addr = ifieldRegA(theInstr);
4727 UChar rB_addr = ifieldRegB(theInstr);
4728 UInt opc2 = ifieldOPClo10(theInstr);
4729 UChar b0 = ifieldBIT0(theInstr);
cerionedf7fc52005-11-18 20:57:41 +00004730
ceriond953ebb2005-11-29 13:27:20 +00004731 IRType ty = mode64 ? Ity_I64 : Ity_I32;
4732 IRTemp EA = newTemp(ty);
4733 IRTemp rS = newTemp(ty);
4734
4735 assign( EA, ea_rAor0_idxd( rA_addr, rB_addr ) );
4736
cerionb85e8bb2005-02-16 08:54:33 +00004737 switch (opc1) {
sewardjafe85832005-09-09 10:25:39 +00004738 /* XL-Form */
cerione9d361a2005-03-04 17:35:29 +00004739 case 0x13: // isync (Instruction Synchronize, PPC32 p432)
cerionb85e8bb2005-02-16 08:54:33 +00004740 if (opc2 != 0x096) {
cerion5b2325f2005-12-23 00:55:09 +00004741 vex_printf("dis_memsync(ppc)(0x13,opc2)\n");
cerionb85e8bb2005-02-16 08:54:33 +00004742 return False;
4743 }
4744 if (b11to25 != 0 || b0 != 0) {
cerion5b2325f2005-12-23 00:55:09 +00004745 vex_printf("dis_memsync(ppc)(0x13,b11to25|b0)\n");
cerionb85e8bb2005-02-16 08:54:33 +00004746 return False;
4747 }
4748 DIP("isync\n");
cerionb85e8bb2005-02-16 08:54:33 +00004749 stmt( IRStmt_MFence() );
4750 break;
cerion8c3adda2005-01-31 11:54:05 +00004751
cerionb85e8bb2005-02-16 08:54:33 +00004752 /* X-Form */
4753 case 0x1F:
4754 switch (opc2) {
cerion5b2325f2005-12-23 00:55:09 +00004755 case 0x356: // eieio (Enforce In-Order Exec of I/O, PPC32 p394)
sewardj7787af42005-08-04 18:32:19 +00004756 if (b11to25 != 0 || b0 != 0) {
cerion5b2325f2005-12-23 00:55:09 +00004757 vex_printf("dis_memsync(ppc)(eiei0,b11to25|b0)\n");
sewardj7787af42005-08-04 18:32:19 +00004758 return False;
4759 }
4760 DIP("eieio\n");
4761 /* Insert a memory fence, just to be on the safe side. */
4762 stmt( IRStmt_MFence() );
4763 break;
cerion8c3adda2005-01-31 11:54:05 +00004764
cerione9d361a2005-03-04 17:35:29 +00004765 case 0x014: // lwarx (Load Word and Reserve Indexed, PPC32 p458)
cerionb85e8bb2005-02-16 08:54:33 +00004766 if (b0 != 0) {
cerion5b2325f2005-12-23 00:55:09 +00004767 vex_printf("dis_memsync(ppc)(lwarx,b0)\n");
cerionb85e8bb2005-02-16 08:54:33 +00004768 return False;
4769 }
ceriond953ebb2005-11-29 13:27:20 +00004770 DIP("lwarx r%u,r%u,r%u\n", rD_addr, rA_addr, rB_addr);
cerion5b2325f2005-12-23 00:55:09 +00004771 putIReg( rD_addr, mkSzWiden32(ty, loadBE(Ity_I32, mkexpr(EA)),
4772 False) );
cerion76de5cf2005-11-18 18:25:12 +00004773 /* Take a reservation */
ceriond953ebb2005-11-29 13:27:20 +00004774 putGST( PPC_GST_RESVN, mkexpr(EA) );
cerionb85e8bb2005-02-16 08:54:33 +00004775 break;
4776
sewardj7787af42005-08-04 18:32:19 +00004777 case 0x096: {
4778 // stwcx. (Store Word Conditional Indexed, PPC32 p532)
cerionf0de28c2005-12-13 20:21:11 +00004779 IRTemp resaddr = newTemp(ty);
cerionb85e8bb2005-02-16 08:54:33 +00004780 if (b0 != 1) {
cerion5b2325f2005-12-23 00:55:09 +00004781 vex_printf("dis_memsync(ppc)(stwcx.,b0)\n");
cerionb85e8bb2005-02-16 08:54:33 +00004782 return False;
4783 }
ceriond953ebb2005-11-29 13:27:20 +00004784 DIP("stwcx. r%u,r%u,r%u\n", rS_addr, rA_addr, rB_addr);
cerion76de5cf2005-11-18 18:25:12 +00004785 assign( rS, getIReg(rS_addr) );
sewardjafe85832005-09-09 10:25:39 +00004786
cerion76de5cf2005-11-18 18:25:12 +00004787 /* First set up as if the reservation failed */
sewardj7787af42005-08-04 18:32:19 +00004788 // Set CR0[LT GT EQ S0] = 0b000 || XER[SO]
4789 putCR321(0, mkU8(0<<1));
4790 putCR0(0, getXER_SO());
4791
cerion76de5cf2005-11-18 18:25:12 +00004792 /* Get the reservation address into a temporary, then
4793 clear it. */
ceriond953ebb2005-11-29 13:27:20 +00004794 assign( resaddr, getGST(PPC_GST_RESVN) );
cerion2831b002005-11-30 19:55:22 +00004795 putGST( PPC_GST_RESVN, mkSzImm(ty, 0) );
sewardj7787af42005-08-04 18:32:19 +00004796
cerion76de5cf2005-11-18 18:25:12 +00004797 /* Skip the rest if the reservation really did fail. */
sewardj7787af42005-08-04 18:32:19 +00004798 stmt( IRStmt_Exit(
ceriond953ebb2005-11-29 13:27:20 +00004799 ( mode64 ?
4800 binop(Iop_CmpNE64, mkexpr(resaddr), mkexpr(EA)) :
4801 binop(Iop_CmpNE32, mkexpr(resaddr), mkexpr(EA)) ),
sewardj7787af42005-08-04 18:32:19 +00004802 Ijk_Boring,
cerion2831b002005-11-30 19:55:22 +00004803 mkSzConst( ty, nextInsnAddr()) ));
ceriond953ebb2005-11-29 13:27:20 +00004804
4805 /* Note for mode64:
4806 If resaddr != lwarx_resaddr, CR0[EQ] is undefined, and
4807 whether rS is stored is dependent on that value. */
sewardj7787af42005-08-04 18:32:19 +00004808
cerion4e2c2b32006-01-02 13:35:51 +00004809 /* Success? Do the (32bit) store */
4810 storeBE( mkexpr(EA), mkSzNarrow32(ty, mkexpr(rS)) );
cerionb85e8bb2005-02-16 08:54:33 +00004811
sewardjb51f0f42005-07-18 11:38:02 +00004812 // Set CR0[LT GT EQ S0] = 0b001 || XER[SO]
4813 putCR321(0, mkU8(1<<1));
cerionb85e8bb2005-02-16 08:54:33 +00004814 break;
sewardj7787af42005-08-04 18:32:19 +00004815 }
4816
sewardjb029a612005-12-30 15:04:29 +00004817 case 0x256: // sync (Synchronize, PPC32 p543),
cerione43bc882006-01-05 13:11:59 +00004818 // also lwsync (L==1), ptesync (L==2)
sewardjb029a612005-12-30 15:04:29 +00004819 /* http://sources.redhat.com/ml/binutils/2000-12/msg00311.html
4820
4821 The PowerPC architecture used in IBM chips has expanded
4822 the sync instruction into two variants: lightweight sync
4823 and heavyweight sync. The original sync instruction is
4824 the new heavyweight sync and lightweight sync is a strict
4825 subset of the heavyweight sync functionality. This allows
4826 the programmer to specify a less expensive operation on
4827 high-end systems when the full sync functionality is not
4828 necessary.
4829
4830 The basic "sync" mnemonic now utilizes an operand. "sync"
4831 without an operand now becomes a extended mnemonic for
4832 heavyweight sync. Processors without the lwsync
4833 instruction will not decode the L field and will perform a
4834 heavyweight sync. Everything is backward compatible.
4835
4836 sync = sync 0
4837 lwsync = sync 1
cerione43bc882006-01-05 13:11:59 +00004838 ptesync = sync 2 *** TODO - not implemented ***
cerion4e2c2b32006-01-02 13:35:51 +00004839 */
cerione43bc882006-01-05 13:11:59 +00004840 if (b11to20 != 0 || b0 != 0) {
4841 vex_printf("dis_memsync(ppc)(sync/lwsync,b11to20|b0)\n");
cerionb85e8bb2005-02-16 08:54:33 +00004842 return False;
4843 }
cerione43bc882006-01-05 13:11:59 +00004844 if (flag_L != 0/*sync*/ && flag_L != 1/*lwsync*/) {
4845 vex_printf("dis_memsync(ppc)(sync/lwsync,flag_L)\n");
4846 return False;
4847 }
4848 DIP("%ssync\n", flag_L == 1 ? "lw" : "");
cerionb85e8bb2005-02-16 08:54:33 +00004849 /* Insert a memory fence. It's sometimes important that these
4850 are carried through to the generated code. */
4851 stmt( IRStmt_MFence() );
4852 break;
cerionf0de28c2005-12-13 20:21:11 +00004853
cerionf0de28c2005-12-13 20:21:11 +00004854 /* 64bit Memsync */
cerion5b2325f2005-12-23 00:55:09 +00004855 case 0x054: // ldarx (Load DWord and Reserve Indexed, PPC64 p473)
cerionf0de28c2005-12-13 20:21:11 +00004856 if (b0 != 0) {
cerion5b2325f2005-12-23 00:55:09 +00004857 vex_printf("dis_memsync(ppc)(ldarx,b0)\n");
cerionf0de28c2005-12-13 20:21:11 +00004858 return False;
4859 }
4860 DIP("ldarx r%u,r%u,r%u\n", rD_addr, rA_addr, rB_addr);
cerion07b07a92005-12-22 14:32:35 +00004861 putIReg( rD_addr, loadBE(Ity_I64, mkexpr(EA)) );
4862 // Take a reservation
4863 putGST( PPC_GST_RESVN, mkexpr(EA) );
4864 break;
4865
cerion5b2325f2005-12-23 00:55:09 +00004866 case 0x0D6: { // stdcx. (Store DWord Condition Indexd, PPC64 p581)
cerion07b07a92005-12-22 14:32:35 +00004867 IRTemp resaddr = newTemp(ty);
cerionf0de28c2005-12-13 20:21:11 +00004868 if (b0 != 1) {
cerion5b2325f2005-12-23 00:55:09 +00004869 vex_printf("dis_memsync(ppc)(stdcx.,b0)\n");
cerionf0de28c2005-12-13 20:21:11 +00004870 return False;
4871 }
4872 DIP("stdcx. r%u,r%u,r%u\n", rS_addr, rA_addr, rB_addr);
cerion07b07a92005-12-22 14:32:35 +00004873 assign( rS, getIReg(rS_addr) );
cerionf0de28c2005-12-13 20:21:11 +00004874
cerion07b07a92005-12-22 14:32:35 +00004875 // First set up as if the reservation failed
4876 // Set CR0[LT GT EQ S0] = 0b000 || XER[SO]
4877 putCR321(0, mkU8(0<<1));
4878 putCR0(0, getXER_SO());
cerionf0de28c2005-12-13 20:21:11 +00004879
cerion07b07a92005-12-22 14:32:35 +00004880 // Get the reservation address into a temporary, then clear it.
4881 assign( resaddr, getGST(PPC_GST_RESVN) );
4882 putGST( PPC_GST_RESVN, mkSzImm(ty, 0) );
cerionf0de28c2005-12-13 20:21:11 +00004883
cerion07b07a92005-12-22 14:32:35 +00004884 // Skip the rest if the reservation really did fail.
4885 stmt( IRStmt_Exit( binop(Iop_CmpNE64, mkexpr(resaddr),
4886 mkexpr(EA)),
4887 Ijk_Boring,
4888 IRConst_U64(nextInsnAddr())) );
cerionf0de28c2005-12-13 20:21:11 +00004889
cerion07b07a92005-12-22 14:32:35 +00004890 // Success? Do the store
4891 storeBE( mkexpr(EA), mkexpr(rS) );
cerionb85e8bb2005-02-16 08:54:33 +00004892
cerion07b07a92005-12-22 14:32:35 +00004893 // Set CR0[LT GT EQ S0] = 0b001 || XER[SO]
4894 putCR321(0, mkU8(1<<1));
4895 break;
4896 }
4897
cerionb85e8bb2005-02-16 08:54:33 +00004898 default:
cerion5b2325f2005-12-23 00:55:09 +00004899 vex_printf("dis_memsync(ppc)(opc2)\n");
cerionb85e8bb2005-02-16 08:54:33 +00004900 return False;
4901 }
4902 break;
cerion8c3adda2005-01-31 11:54:05 +00004903
cerionb85e8bb2005-02-16 08:54:33 +00004904 default:
cerion5b2325f2005-12-23 00:55:09 +00004905 vex_printf("dis_memsync(ppc)(opc1)\n");
cerionb85e8bb2005-02-16 08:54:33 +00004906 return False;
4907 }
4908 return True;
cerion8c3adda2005-01-31 11:54:05 +00004909}
4910
4911
4912
cerion3d870a32005-03-18 12:23:33 +00004913/*
4914 Integer Shift Instructions
4915*/
cerion645c9302005-01-31 10:09:59 +00004916static Bool dis_int_shift ( UInt theInstr )
4917{
cerionf0de28c2005-12-13 20:21:11 +00004918 /* X-Form, XS-Form */
cerion76de5cf2005-11-18 18:25:12 +00004919 UChar opc1 = ifieldOPC(theInstr);
4920 UChar rS_addr = ifieldRegDS(theInstr);
4921 UChar rA_addr = ifieldRegA(theInstr);
4922 UChar rB_addr = ifieldRegB(theInstr);
4923 UChar sh_imm = rB_addr;
4924 UInt opc2 = ifieldOPClo10(theInstr);
cerionf0de28c2005-12-13 20:21:11 +00004925 UChar b1 = ifieldBIT1(theInstr);
cerion76de5cf2005-11-18 18:25:12 +00004926 UChar flag_rC = ifieldBIT0(theInstr);
4927
ceriond953ebb2005-11-29 13:27:20 +00004928 IRType ty = mode64 ? Ity_I64 : Ity_I32;
4929 IRTemp rA = newTemp(ty);
cerion07b07a92005-12-22 14:32:35 +00004930 IRTemp rS = newTemp(ty);
4931 IRTemp rB = newTemp(ty);
ceriond953ebb2005-11-29 13:27:20 +00004932 IRTemp outofrange = newTemp(Ity_I8);
ceriond953ebb2005-11-29 13:27:20 +00004933 IRTemp rS_lo32 = newTemp(Ity_I32);
4934 IRTemp rB_lo32 = newTemp(Ity_I32);
4935 IRExpr* e_tmp;
4936
cerion07b07a92005-12-22 14:32:35 +00004937 assign( rS, getIReg(rS_addr) );
4938 assign( rB, getIReg(rB_addr) );
4939 assign( rS_lo32, mkSzNarrow32(ty, mkexpr(rS)) );
4940 assign( rB_lo32, mkSzNarrow32(ty, mkexpr(rB)) );
cerionb85e8bb2005-02-16 08:54:33 +00004941
4942 if (opc1 == 0x1F) {
4943 switch (opc2) {
ceriond953ebb2005-11-29 13:27:20 +00004944 case 0x018: { // slw (Shift Left Word, PPC32 p505)
cerion5b2325f2005-12-23 00:55:09 +00004945 DIP("slw%s r%u,r%u,r%u\n", flag_rC ? ".":"",
cerion76de5cf2005-11-18 18:25:12 +00004946 rA_addr, rS_addr, rB_addr);
4947 /* rA = rS << rB */
4948 /* ppc32 semantics are:
sewardjdfb11442005-10-08 19:58:48 +00004949 slw(x,y) = (x << (y & 31)) -- primary result
4950 & ~((y << 26) >>s 31) -- make result 0
4951 for y in 32 .. 63
4952 */
ceriond953ebb2005-11-29 13:27:20 +00004953 e_tmp =
4954 binop( Iop_And32,
4955 binop( Iop_Shl32,
4956 mkexpr(rS_lo32),
4957 unop( Iop_32to8,
4958 binop(Iop_And32,
4959 mkexpr(rB_lo32), mkU32(31)))),
4960 unop( Iop_Not32,
4961 binop( Iop_Sar32,
4962 binop(Iop_Shl32, mkexpr(rB_lo32), mkU8(26)),
4963 mkU8(31))) );
cerion2831b002005-11-30 19:55:22 +00004964 assign( rA, mkSzWiden32(ty, e_tmp, /* Signed */False) );
cerionb85e8bb2005-02-16 08:54:33 +00004965 break;
ceriond953ebb2005-11-29 13:27:20 +00004966 }
4967
cerion5b2325f2005-12-23 00:55:09 +00004968 case 0x318: { // sraw (Shift Right Alg Word, PPC32 p506)
cerion07b07a92005-12-22 14:32:35 +00004969 IRTemp sh_amt = newTemp(Ity_I32);
cerion5b2325f2005-12-23 00:55:09 +00004970 DIP("sraw%s r%u,r%u,r%u\n", flag_rC ? ".":"",
cerion76de5cf2005-11-18 18:25:12 +00004971 rA_addr, rS_addr, rB_addr);
cerion76de5cf2005-11-18 18:25:12 +00004972 /* JRS: my reading of the (poorly worded) PPC32 doc p506 is:
4973 amt = rB & 63
4974 rA = Sar32( rS, amt > 31 ? 31 : amt )
4975 XER.CA = amt > 31 ? sign-of-rS : (computation as per srawi)
4976 */
cerion5b2325f2005-12-23 00:55:09 +00004977 assign( sh_amt, binop(Iop_And32, mkU32(0x3F),
4978 mkexpr(rB_lo32)) );
cerion76de5cf2005-11-18 18:25:12 +00004979 assign( outofrange,
sewardj20ef5472005-07-21 14:48:31 +00004980 unop( Iop_1Uto8,
cerion5b2325f2005-12-23 00:55:09 +00004981 binop(Iop_CmpLT32U, mkU32(31),
4982 mkexpr(sh_amt)) ));
ceriond953ebb2005-11-29 13:27:20 +00004983 e_tmp = binop( Iop_Sar32,
4984 mkexpr(rS_lo32),
sewardj20ef5472005-07-21 14:48:31 +00004985 unop( Iop_32to8,
4986 IRExpr_Mux0X( mkexpr(outofrange),
cerion07b07a92005-12-22 14:32:35 +00004987 mkexpr(sh_amt),
ceriond953ebb2005-11-29 13:27:20 +00004988 mkU32(31)) ) );
cerion2831b002005-11-30 19:55:22 +00004989 assign( rA, mkSzWiden32(ty, e_tmp, /* Signed */True) );
4990
cerion5b2325f2005-12-23 00:55:09 +00004991 set_XER_CA( ty, PPCG_FLAG_OP_SRAW,
cerionf0de28c2005-12-13 20:21:11 +00004992 mkexpr(rA),
4993 mkSzWiden32(ty, mkexpr(rS_lo32), True),
cerion07b07a92005-12-22 14:32:35 +00004994 mkSzWiden32(ty, mkexpr(sh_amt), True ),
cerionf0de28c2005-12-13 20:21:11 +00004995 mkSzWiden32(ty, getXER_CA32(), True) );
cerionb85e8bb2005-02-16 08:54:33 +00004996 break;
cerion07b07a92005-12-22 14:32:35 +00004997 }
cerionb85e8bb2005-02-16 08:54:33 +00004998
cerion5b2325f2005-12-23 00:55:09 +00004999 case 0x338: // srawi (Shift Right Alg Word Immediate, PPC32 p507)
5000 DIP("srawi%s r%u,r%u,%d\n", flag_rC ? ".":"",
cerion76de5cf2005-11-18 18:25:12 +00005001 rA_addr, rS_addr, sh_imm);
5002 vassert(sh_imm < 32);
cerionf0de28c2005-12-13 20:21:11 +00005003 if (mode64) {
5004 assign( rA, binop(Iop_Sar64,
cerion5b2325f2005-12-23 00:55:09 +00005005 binop(Iop_Shl64, getIReg(rS_addr),
5006 mkU8(32)),
cerionf0de28c2005-12-13 20:21:11 +00005007 mkU8(32 + sh_imm)) );
5008 } else {
cerion5b2325f2005-12-23 00:55:09 +00005009 assign( rA, binop(Iop_Sar32, mkexpr(rS_lo32),
5010 mkU8(sh_imm)) );
cerionf0de28c2005-12-13 20:21:11 +00005011 }
cerion2831b002005-11-30 19:55:22 +00005012
cerion5b2325f2005-12-23 00:55:09 +00005013 set_XER_CA( ty, PPCG_FLAG_OP_SRAWI,
cerionf0de28c2005-12-13 20:21:11 +00005014 mkexpr(rA),
cerion5b2325f2005-12-23 00:55:09 +00005015 mkSzWiden32(ty, mkexpr(rS_lo32), /* Syned */True),
cerionf0de28c2005-12-13 20:21:11 +00005016 mkSzImm(ty, sh_imm),
cerion5b2325f2005-12-23 00:55:09 +00005017 mkSzWiden32(ty, getXER_CA32(), /* Syned */False) );
cerionb85e8bb2005-02-16 08:54:33 +00005018 break;
5019
cerione9d361a2005-03-04 17:35:29 +00005020 case 0x218: // srw (Shift Right Word, PPC32 p508)
cerion5b2325f2005-12-23 00:55:09 +00005021 DIP("srw%s r%u,r%u,r%u\n", flag_rC ? ".":"",
cerion76de5cf2005-11-18 18:25:12 +00005022 rA_addr, rS_addr, rB_addr);
5023 /* rA = rS >>u rB */
5024 /* ppc32 semantics are:
cerionf0de28c2005-12-13 20:21:11 +00005025 srw(x,y) = (x >>u (y & 31)) -- primary result
sewardjdfb11442005-10-08 19:58:48 +00005026 & ~((y << 26) >>s 31) -- make result 0
5027 for y in 32 .. 63
5028 */
ceriond953ebb2005-11-29 13:27:20 +00005029 e_tmp =
sewardjdfb11442005-10-08 19:58:48 +00005030 binop(
5031 Iop_And32,
5032 binop( Iop_Shr32,
ceriond953ebb2005-11-29 13:27:20 +00005033 mkexpr(rS_lo32),
sewardjdfb11442005-10-08 19:58:48 +00005034 unop( Iop_32to8,
cerion5b2325f2005-12-23 00:55:09 +00005035 binop(Iop_And32, mkexpr(rB_lo32),
5036 mkU32(31)))),
sewardjdfb11442005-10-08 19:58:48 +00005037 unop( Iop_Not32,
5038 binop( Iop_Sar32,
cerion5b2325f2005-12-23 00:55:09 +00005039 binop(Iop_Shl32, mkexpr(rB_lo32),
5040 mkU8(26)),
ceriond953ebb2005-11-29 13:27:20 +00005041 mkU8(31))));
cerion2831b002005-11-30 19:55:22 +00005042 assign( rA, mkSzWiden32(ty, e_tmp, /* Signed */False) );
cerionb85e8bb2005-02-16 08:54:33 +00005043 break;
cerionf0de28c2005-12-13 20:21:11 +00005044
5045
5046 /* 64bit Shifts */
cerion5b2325f2005-12-23 00:55:09 +00005047 case 0x01B: // sld (Shift Left DWord, PPC64 p568)
5048 DIP("sld%s r%u,r%u,r%u\n",
5049 flag_rC ? ".":"", rA_addr, rS_addr, rB_addr);
cerionf0de28c2005-12-13 20:21:11 +00005050 /* rA = rS << rB */
cerion07b07a92005-12-22 14:32:35 +00005051 /* ppc64 semantics are:
cerionf0de28c2005-12-13 20:21:11 +00005052 slw(x,y) = (x << (y & 63)) -- primary result
5053 & ~((y << 57) >>s 63) -- make result 0
5054 for y in 64 ..
5055 */
cerionf0de28c2005-12-13 20:21:11 +00005056 assign( rA,
5057 binop(
5058 Iop_And64,
5059 binop( Iop_Shl64,
5060 mkexpr(rS),
5061 unop( Iop_64to8,
5062 binop(Iop_And64, mkexpr(rB), mkU64(63)))),
5063 unop( Iop_Not64,
5064 binop( Iop_Sar64,
5065 binop(Iop_Shl64, mkexpr(rB), mkU8(57)),
5066 mkU8(63)))) );
cerion07b07a92005-12-22 14:32:35 +00005067 break;
cerionf0de28c2005-12-13 20:21:11 +00005068
cerion5b2325f2005-12-23 00:55:09 +00005069 case 0x31A: { // srad (Shift Right Alg DWord, PPC64 p570)
cerion07b07a92005-12-22 14:32:35 +00005070 IRTemp sh_amt = newTemp(Ity_I64);
cerion5b2325f2005-12-23 00:55:09 +00005071 DIP("srad%s r%u,r%u,r%u\n",
5072 flag_rC ? ".":"", rA_addr, rS_addr, rB_addr);
cerionf0de28c2005-12-13 20:21:11 +00005073 /* amt = rB & 127
5074 rA = Sar64( rS, amt > 63 ? 63 : amt )
5075 XER.CA = amt > 63 ? sign-of-rS : (computation as per srawi)
5076 */
cerion07b07a92005-12-22 14:32:35 +00005077 assign( sh_amt, binop(Iop_And64, mkU64(0x7F), mkexpr(rB)) );
cerionf0de28c2005-12-13 20:21:11 +00005078 assign( outofrange,
5079 unop( Iop_1Uto8,
cerion5b2325f2005-12-23 00:55:09 +00005080 binop(Iop_CmpLT64U, mkU64(63),
5081 mkexpr(sh_amt)) ));
cerionf0de28c2005-12-13 20:21:11 +00005082 assign( rA,
5083 binop( Iop_Sar64,
5084 mkexpr(rS),
5085 unop( Iop_64to8,
5086 IRExpr_Mux0X( mkexpr(outofrange),
cerion07b07a92005-12-22 14:32:35 +00005087 mkexpr(sh_amt),
5088 mkU64(63)) ))
cerionf0de28c2005-12-13 20:21:11 +00005089 );
cerion5b2325f2005-12-23 00:55:09 +00005090 set_XER_CA( ty, PPCG_FLAG_OP_SRAD,
cerion07b07a92005-12-22 14:32:35 +00005091 mkexpr(rA), mkexpr(rS), mkexpr(sh_amt),
cerion5b2325f2005-12-23 00:55:09 +00005092 mkSzWiden32(ty, getXER_CA32(), /* Syned */False) );
cerion07b07a92005-12-22 14:32:35 +00005093 break;
5094 }
5095
cerion5b2325f2005-12-23 00:55:09 +00005096 case 0x33A: case 0x33B: // sradi (Shr Alg DWord Imm, PPC64 p571)
cerionf0de28c2005-12-13 20:21:11 +00005097 sh_imm |= b1<<5;
5098 vassert(sh_imm < 64);
cerion5b2325f2005-12-23 00:55:09 +00005099 DIP("sradi%s r%u,r%u,%u\n",
5100 flag_rC ? ".":"", rA_addr, rS_addr, sh_imm);
cerionf0de28c2005-12-13 20:21:11 +00005101 assign( rA, binop(Iop_Sar64, getIReg(rS_addr), mkU8(sh_imm)) );
5102
cerion5b2325f2005-12-23 00:55:09 +00005103 set_XER_CA( ty, PPCG_FLAG_OP_SRADI,
cerionf0de28c2005-12-13 20:21:11 +00005104 mkexpr(rA),
5105 getIReg(rS_addr),
5106 mkU64(sh_imm),
cerion5b2325f2005-12-23 00:55:09 +00005107 mkSzWiden32(ty, getXER_CA32(), /* Syned */False) );
cerionf0de28c2005-12-13 20:21:11 +00005108 break;
5109
cerion5b2325f2005-12-23 00:55:09 +00005110 case 0x21B: // srd (Shift Right DWord, PPC64 p574)
5111 DIP("srd%s r%u,r%u,r%u\n",
5112 flag_rC ? ".":"", rA_addr, rS_addr, rB_addr);
cerionf0de28c2005-12-13 20:21:11 +00005113 /* rA = rS >>u rB */
cerion07b07a92005-12-22 14:32:35 +00005114 /* ppc semantics are:
cerionf0de28c2005-12-13 20:21:11 +00005115 srw(x,y) = (x >>u (y & 63)) -- primary result
5116 & ~((y << 57) >>s 63) -- make result 0
5117 for y in 64 .. 127
5118 */
cerionf0de28c2005-12-13 20:21:11 +00005119 assign( rA,
5120 binop(
5121 Iop_And64,
5122 binop( Iop_Shr64,
5123 mkexpr(rS),
5124 unop( Iop_64to8,
5125 binop(Iop_And64, mkexpr(rB), mkU64(63)))),
5126 unop( Iop_Not64,
5127 binop( Iop_Sar64,
5128 binop(Iop_Shl64, mkexpr(rB), mkU8(57)),
5129 mkU8(63)))) );
cerion07b07a92005-12-22 14:32:35 +00005130 break;
cerionf0de28c2005-12-13 20:21:11 +00005131
cerionb85e8bb2005-02-16 08:54:33 +00005132 default:
cerion5b2325f2005-12-23 00:55:09 +00005133 vex_printf("dis_int_shift(ppc)(opc2)\n");
cerionb85e8bb2005-02-16 08:54:33 +00005134 return False;
5135 }
5136 } else {
cerion5b2325f2005-12-23 00:55:09 +00005137 vex_printf("dis_int_shift(ppc)(opc1)\n");
cerionb85e8bb2005-02-16 08:54:33 +00005138 return False;
5139 }
cerion0d330c52005-02-28 16:43:16 +00005140
cerion76de5cf2005-11-18 18:25:12 +00005141 putIReg( rA_addr, mkexpr(rA) );
cerionb85e8bb2005-02-16 08:54:33 +00005142
cerion76de5cf2005-11-18 18:25:12 +00005143 if (flag_rC) {
5144 set_CR0( mkexpr(rA) );
cerionb85e8bb2005-02-16 08:54:33 +00005145 }
5146 return True;
cerion645c9302005-01-31 10:09:59 +00005147}
5148
5149
5150
sewardj602857d2005-09-06 09:10:09 +00005151/*
5152 Integer Load/Store Reverse Instructions
5153*/
sewardj40d8c092006-05-05 13:26:14 +00005154/* Generates code to swap the byte order in an Ity_I32. */
sewardjfb957972005-09-08 17:53:03 +00005155static IRExpr* /* :: Ity_I32 */ gen_byterev32 ( IRTemp t )
5156{
5157 vassert(typeOfIRTemp(irbb->tyenv, t) == Ity_I32);
5158 return
5159 binop(Iop_Or32,
5160 binop(Iop_Shl32, mkexpr(t), mkU8(24)),
5161 binop(Iop_Or32,
5162 binop(Iop_And32, binop(Iop_Shl32, mkexpr(t), mkU8(8)),
5163 mkU32(0x00FF0000)),
5164 binop(Iop_Or32,
5165 binop(Iop_And32, binop(Iop_Shr32, mkexpr(t), mkU8(8)),
5166 mkU32(0x0000FF00)),
5167 binop(Iop_And32, binop(Iop_Shr32, mkexpr(t), mkU8(24)),
5168 mkU32(0x000000FF) )
5169 )));
5170}
5171
sewardj40d8c092006-05-05 13:26:14 +00005172/* Generates code to swap the byte order in the lower half of an Ity_I32,
5173 and zeroes the upper half. */
5174static IRExpr* /* :: Ity_I32 */ gen_byterev16 ( IRTemp t )
5175{
5176 vassert(typeOfIRTemp(irbb->tyenv, t) == Ity_I32);
5177 return
5178 binop(Iop_Or32,
5179 binop(Iop_And32, binop(Iop_Shl32, mkexpr(t), mkU8(8)),
5180 mkU32(0x0000FF00)),
5181 binop(Iop_And32, binop(Iop_Shr32, mkexpr(t), mkU8(8)),
5182 mkU32(0x000000FF))
5183 );
5184}
5185
sewardj602857d2005-09-06 09:10:09 +00005186static Bool dis_int_ldst_rev ( UInt theInstr )
5187{
5188 /* X-Form */
cerion76de5cf2005-11-18 18:25:12 +00005189 UChar opc1 = ifieldOPC(theInstr);
5190 UChar rD_addr = ifieldRegDS(theInstr);
5191 UChar rS_addr = rD_addr;
5192 UChar rA_addr = ifieldRegA(theInstr);
5193 UChar rB_addr = ifieldRegB(theInstr);
5194 UInt opc2 = ifieldOPClo10(theInstr);
5195 UChar b0 = ifieldBIT0(theInstr);
cerionedf7fc52005-11-18 20:57:41 +00005196
ceriond953ebb2005-11-29 13:27:20 +00005197 IRType ty = mode64 ? Ity_I64 : Ity_I32;
5198 IRTemp EA = newTemp(ty);
cerionedf7fc52005-11-18 20:57:41 +00005199 IRTemp w1 = newTemp(Ity_I32);
5200 IRTemp w2 = newTemp(Ity_I32);
sewardj602857d2005-09-06 09:10:09 +00005201
5202 if (opc1 != 0x1F || b0 != 0) {
cerion5b2325f2005-12-23 00:55:09 +00005203 vex_printf("dis_int_ldst_rev(ppc)(opc1|b0)\n");
sewardj602857d2005-09-06 09:10:09 +00005204 return False;
5205 }
sewardjafe85832005-09-09 10:25:39 +00005206
ceriond953ebb2005-11-29 13:27:20 +00005207 assign( EA, ea_rAor0_idxd( rA_addr, rB_addr ) );
sewardj602857d2005-09-06 09:10:09 +00005208
5209 switch (opc2) {
sewardj40d8c092006-05-05 13:26:14 +00005210
5211 case 0x316: // lhbrx (Load Halfword Byte-Reverse Indexed, PPC32 p449)
5212 DIP("lhbrx r%u,r%u,r%u\n", rD_addr, rA_addr, rB_addr);
5213 assign( w1, unop(Iop_16Uto32, loadBE(Ity_I16, mkexpr(EA))) );
5214 assign( w2, gen_byterev16(w1) );
5215 putIReg( rD_addr, mkSzWiden32(ty, mkexpr(w2),
5216 /* Signed */False) );
5217 break;
5218
sewardjfb957972005-09-08 17:53:03 +00005219 case 0x216: // lwbrx (Load Word Byte-Reverse Indexed, PPC32 p459)
ceriond953ebb2005-11-29 13:27:20 +00005220 DIP("lwbrx r%u,r%u,r%u\n", rD_addr, rA_addr, rB_addr);
sewardjfb957972005-09-08 17:53:03 +00005221 assign( w1, loadBE(Ity_I32, mkexpr(EA)) );
5222 assign( w2, gen_byterev32(w1) );
cerion5b2325f2005-12-23 00:55:09 +00005223 putIReg( rD_addr, mkSzWiden32(ty, mkexpr(w2),
5224 /* Signed */False) );
sewardjfb957972005-09-08 17:53:03 +00005225 break;
sewardj602857d2005-09-06 09:10:09 +00005226
sewardj413a4682006-05-05 13:44:17 +00005227 case 0x396: // sthbrx (Store Half Word Byte-Reverse Indexed, PPC32 p523)
5228 DIP("sthbrx r%u,r%u,r%u\n", rS_addr, rA_addr, rB_addr);
5229 assign( w1, mkSzNarrow32(ty, getIReg(rS_addr)) );
5230 storeBE( mkexpr(EA), unop(Iop_32to16, gen_byterev16(w1)) );
5231 break;
sewardj602857d2005-09-06 09:10:09 +00005232
cerion5b2325f2005-12-23 00:55:09 +00005233 case 0x296: // stwbrx (Store Word Byte-Reverse Indxd, PPC32 p531)
ceriond953ebb2005-11-29 13:27:20 +00005234 DIP("stwbrx r%u,r%u,r%u\n", rS_addr, rA_addr, rB_addr);
cerion2831b002005-11-30 19:55:22 +00005235 assign( w1, mkSzNarrow32(ty, getIReg(rS_addr)) );
sewardjfb957972005-09-08 17:53:03 +00005236 storeBE( mkexpr(EA), gen_byterev32(w1) );
5237 break;
5238
5239 default:
cerion5b2325f2005-12-23 00:55:09 +00005240 vex_printf("dis_int_ldst_rev(ppc)(opc2)\n");
sewardjfb957972005-09-08 17:53:03 +00005241 return False;
sewardj602857d2005-09-06 09:10:09 +00005242 }
5243 return True;
5244}
cerion645c9302005-01-31 10:09:59 +00005245
5246
5247
cerion3d870a32005-03-18 12:23:33 +00005248/*
5249 Processor Control Instructions
5250*/
sewardjaca070a2006-10-17 00:28:22 +00005251static Bool dis_proc_ctl ( VexMiscInfo* vmi, UInt theInstr )
cerion8c3adda2005-01-31 11:54:05 +00005252{
cerion76de5cf2005-11-18 18:25:12 +00005253 UChar opc1 = ifieldOPC(theInstr);
cerionb85e8bb2005-02-16 08:54:33 +00005254
5255 /* X-Form */
cerion76de5cf2005-11-18 18:25:12 +00005256 UChar crfD = toUChar( IFIELD( theInstr, 23, 3 ) );
5257 UChar b21to22 = toUChar( IFIELD( theInstr, 21, 2 ) );
5258 UChar rD_addr = ifieldRegDS(theInstr);
5259 UInt b11to20 = IFIELD( theInstr, 11, 10 );
cerione9d361a2005-03-04 17:35:29 +00005260
cerion76de5cf2005-11-18 18:25:12 +00005261 /* XFX-Form */
5262 UChar rS_addr = rD_addr;
5263 UInt SPR = b11to20;
cerionedf7fc52005-11-18 20:57:41 +00005264 UInt TBR = b11to20;
cerion76de5cf2005-11-18 18:25:12 +00005265 UChar b20 = toUChar( IFIELD( theInstr, 20, 1 ) );
5266 UInt CRM = IFIELD( theInstr, 12, 8 );
5267 UChar b11 = toUChar( IFIELD( theInstr, 11, 1 ) );
5268
5269 UInt opc2 = ifieldOPClo10(theInstr);
5270 UChar b0 = ifieldBIT0(theInstr);
5271
cerion2831b002005-11-30 19:55:22 +00005272 IRType ty = mode64 ? Ity_I64 : Ity_I32;
cerionf0de28c2005-12-13 20:21:11 +00005273 IRTemp rS = newTemp(ty);
ceriond953ebb2005-11-29 13:27:20 +00005274 assign( rS, getIReg(rS_addr) );
sewardj41a7b702005-11-18 22:18:23 +00005275
cerion76de5cf2005-11-18 18:25:12 +00005276 /* Reorder SPR field as per PPC32 p470 */
5277 SPR = ((SPR & 0x1F) << 5) | ((SPR >> 5) & 0x1F);
sewardj73a91972005-09-06 10:25:46 +00005278 /* Reorder TBR field as per PPC32 p475 */
5279 TBR = ((TBR & 31) << 5) | ((TBR >> 5) & 31);
cerionb85e8bb2005-02-16 08:54:33 +00005280
5281 if (opc1 != 0x1F || b0 != 0) {
cerion5b2325f2005-12-23 00:55:09 +00005282 vex_printf("dis_proc_ctl(ppc)(opc1|b0)\n");
cerionb85e8bb2005-02-16 08:54:33 +00005283 return False;
5284 }
5285
5286 switch (opc2) {
cerioncb14e732005-09-09 16:38:19 +00005287 /* X-Form */
cerion5b2325f2005-12-23 00:55:09 +00005288 case 0x200: { // mcrxr (Move to Cond Register from XER, PPC32 p466)
cerioncb14e732005-09-09 16:38:19 +00005289 if (b21to22 != 0 || b11to20 != 0) {
cerion5b2325f2005-12-23 00:55:09 +00005290 vex_printf("dis_proc_ctl(ppc)(mcrxr,b21to22|b11to20)\n");
cerioncb14e732005-09-09 16:38:19 +00005291 return False;
5292 }
5293 DIP("mcrxr crf%d\n", crfD);
cerionedf7fc52005-11-18 20:57:41 +00005294 /* Move XER[0-3] (the top 4 bits of XER) to CR[crfD] */
ceriond953ebb2005-11-29 13:27:20 +00005295 putGST_field( PPC_GST_CR,
5296 getGST_field( PPC_GST_XER, 7 ),
cerionedf7fc52005-11-18 20:57:41 +00005297 crfD );
sewardj55ccc3e2005-09-09 19:45:02 +00005298
5299 // Clear XER[0-3]
cerionedf7fc52005-11-18 20:57:41 +00005300 putXER_SO( mkU8(0) );
5301 putXER_OV( mkU8(0) );
5302 putXER_CA( mkU8(0) );
cerioncb14e732005-09-09 16:38:19 +00005303 break;
sewardj55ccc3e2005-09-09 19:45:02 +00005304 }
cerionb85e8bb2005-02-16 08:54:33 +00005305
sewardj72aefb22006-03-01 18:58:39 +00005306 case 0x013:
5307 // b11to20==0: mfcr (Move from Cond Register, PPC32 p467)
5308 // b20==1 & b11==0: mfocrf (Move from One CR Field)
5309 // However it seems that the 'mfcr' behaviour is an acceptable
5310 // implementation of mfocr (from the 2.02 arch spec)
5311 if (b11to20 == 0) {
5312 DIP("mfcr r%u\n", rD_addr);
5313 putIReg( rD_addr, mkSzWiden32(ty, getGST( PPC_GST_CR ),
5314 /* Signed */False) );
5315 break;
cerionb85e8bb2005-02-16 08:54:33 +00005316 }
sewardj72aefb22006-03-01 18:58:39 +00005317 if (b20 == 1 && b11 == 0) {
5318 DIP("mfocrf r%u,%u\n", rD_addr, CRM);
5319 putIReg( rD_addr, mkSzWiden32(ty, getGST( PPC_GST_CR ),
5320 /* Signed */False) );
5321 break;
5322 }
5323 /* not decodable */
5324 return False;
5325
cerionb85e8bb2005-02-16 08:54:33 +00005326 /* XFX-Form */
cerione9d361a2005-03-04 17:35:29 +00005327 case 0x153: // mfspr (Move from Special-Purpose Register, PPC32 p470)
cerionb85e8bb2005-02-16 08:54:33 +00005328
cerion76de5cf2005-11-18 18:25:12 +00005329 switch (SPR) { // Choose a register...
ceriond953ebb2005-11-29 13:27:20 +00005330 case 0x1:
5331 DIP("mfxer r%u\n", rD_addr);
cerion2831b002005-11-30 19:55:22 +00005332 putIReg( rD_addr, mkSzWiden32(ty, getGST( PPC_GST_XER ),
5333 /* Signed */False) );
ceriond953ebb2005-11-29 13:27:20 +00005334 break;
5335 case 0x8:
5336 DIP("mflr r%u\n", rD_addr);
5337 putIReg( rD_addr, getGST( PPC_GST_LR ) );
5338 break;
5339 case 0x9:
5340 DIP("mfctr r%u\n", rD_addr);
5341 putIReg( rD_addr, getGST( PPC_GST_CTR ) );
5342 break;
5343 case 0x100:
5344 DIP("mfvrsave r%u\n", rD_addr);
cerion2831b002005-11-30 19:55:22 +00005345 putIReg( rD_addr, mkSzWiden32(ty, getGST( PPC_GST_VRSAVE ),
5346 /* Signed */False) );
ceriond953ebb2005-11-29 13:27:20 +00005347 break;
sewardjaca070a2006-10-17 00:28:22 +00005348
5349 case 0x103:
5350 DIP("mfspr r%u, SPRG3(readonly)\n", rD_addr);
5351 putIReg( rD_addr, getGST( PPC_GST_SPRG3_RO ) );
5352 break;
5353
ceriond953ebb2005-11-29 13:27:20 +00005354 default:
cerion5b2325f2005-12-23 00:55:09 +00005355 vex_printf("dis_proc_ctl(ppc)(mfspr,SPR)(0x%x)\n", SPR);
ceriond953ebb2005-11-29 13:27:20 +00005356 return False;
cerionb85e8bb2005-02-16 08:54:33 +00005357 }
5358 break;
5359
sewardj73a91972005-09-06 10:25:46 +00005360 case 0x173: { // mftb (Move from Time Base, PPC32 p475)
5361 IRTemp val = newTemp(Ity_I64);
5362 IRExpr** args = mkIRExprVec_0();
cerion4c4f5ef2006-01-02 14:41:50 +00005363 IRDirty* d = unsafeIRDirty_1_N(
5364 val,
5365 0/*regparms*/,
5366 "ppcg_dirtyhelper_MFTB",
sewardjaca070a2006-10-17 00:28:22 +00005367 fnptr_to_fnentry(vmi, &ppcg_dirtyhelper_MFTB),
cerion4c4f5ef2006-01-02 14:41:50 +00005368 args );
sewardj73a91972005-09-06 10:25:46 +00005369 /* execute the dirty call, dumping the result in val. */
5370 stmt( IRStmt_Dirty(d) );
5371
5372 switch (TBR) {
ceriond953ebb2005-11-29 13:27:20 +00005373 case 269:
ceriond953ebb2005-11-29 13:27:20 +00005374 DIP("mftbu r%u", rD_addr);
cerion2831b002005-11-30 19:55:22 +00005375 putIReg( rD_addr,
5376 mkSzWiden32(ty, unop(Iop_64HIto32, mkexpr(val)),
5377 /* Signed */False) );
ceriond953ebb2005-11-29 13:27:20 +00005378 break;
5379 case 268:
ceriond953ebb2005-11-29 13:27:20 +00005380 DIP("mftb r%u", rD_addr);
cerion2831b002005-11-30 19:55:22 +00005381 putIReg( rD_addr, (mode64) ? mkexpr(val) :
5382 unop(Iop_64to32, mkexpr(val)) );
ceriond953ebb2005-11-29 13:27:20 +00005383 break;
5384 default:
5385 return False; /* illegal instruction */
sewardj73a91972005-09-06 10:25:46 +00005386 }
5387 break;
5388 }
5389
sewardj72aefb22006-03-01 18:58:39 +00005390 case 0x090: {
5391 // b20==0: mtcrf (Move to Cond Register Fields, PPC32 p477)
5392 // b20==1: mtocrf (Move to One Cond Reg Field)
cerionedf7fc52005-11-18 20:57:41 +00005393 Int cr;
5394 UChar shft;
sewardj72aefb22006-03-01 18:58:39 +00005395 if (b11 != 0)
cerionb85e8bb2005-02-16 08:54:33 +00005396 return False;
sewardj72aefb22006-03-01 18:58:39 +00005397 if (b20 == 1) {
5398 /* ppc64 v2.02 spec says mtocrf gives undefined outcome if >
5399 1 field is written. It seems more robust to decline to
5400 decode the insn if so. */
5401 switch (CRM) {
5402 case 0x01: case 0x02: case 0x04: case 0x08:
5403 case 0x10: case 0x20: case 0x40: case 0x80:
5404 break;
5405 default:
5406 return False;
5407 }
cerionb85e8bb2005-02-16 08:54:33 +00005408 }
sewardj72aefb22006-03-01 18:58:39 +00005409 DIP("%s 0x%x,r%u\n", b20==1 ? "mtocrf" : "mtcrf",
5410 CRM, rS_addr);
cerionedf7fc52005-11-18 20:57:41 +00005411 /* Write to each field specified by CRM */
5412 for (cr = 0; cr < 8; cr++) {
5413 if ((CRM & (1 << (7-cr))) == 0)
5414 continue;
5415 shft = 4*(7-cr);
ceriond953ebb2005-11-29 13:27:20 +00005416 putGST_field( PPC_GST_CR,
cerion2831b002005-11-30 19:55:22 +00005417 binop(Iop_Shr32,
5418 mkSzNarrow32(ty, mkexpr(rS)),
5419 mkU8(shft)), cr );
cerionedf7fc52005-11-18 20:57:41 +00005420 }
cerionb85e8bb2005-02-16 08:54:33 +00005421 break;
cerionedf7fc52005-11-18 20:57:41 +00005422 }
cerione9d361a2005-03-04 17:35:29 +00005423
5424 case 0x1D3: // mtspr (Move to Special-Purpose Register, PPC32 p483)
cerionb85e8bb2005-02-16 08:54:33 +00005425
cerion76de5cf2005-11-18 18:25:12 +00005426 switch (SPR) { // Choose a register...
ceriond953ebb2005-11-29 13:27:20 +00005427 case 0x1:
5428 DIP("mtxer r%u\n", rS_addr);
cerion2831b002005-11-30 19:55:22 +00005429 putGST( PPC_GST_XER, mkSzNarrow32(ty, mkexpr(rS)) );
ceriond953ebb2005-11-29 13:27:20 +00005430 break;
5431 case 0x8:
5432 DIP("mtlr r%u\n", rS_addr);
5433 putGST( PPC_GST_LR, mkexpr(rS) );
5434 break;
5435 case 0x9:
5436 DIP("mtctr r%u\n", rS_addr);
5437 putGST( PPC_GST_CTR, mkexpr(rS) );
5438 break;
5439 case 0x100:
5440 DIP("mtvrsave r%u\n", rS_addr);
cerion2831b002005-11-30 19:55:22 +00005441 putGST( PPC_GST_VRSAVE, mkSzNarrow32(ty, mkexpr(rS)) );
ceriond953ebb2005-11-29 13:27:20 +00005442 break;
5443
5444 default:
cerion5b2325f2005-12-23 00:55:09 +00005445 vex_printf("dis_proc_ctl(ppc)(mtspr,SPR)(%u)\n", SPR);
ceriond953ebb2005-11-29 13:27:20 +00005446 return False;
cerionb85e8bb2005-02-16 08:54:33 +00005447 }
5448 break;
5449
5450 default:
cerion5b2325f2005-12-23 00:55:09 +00005451 vex_printf("dis_proc_ctl(ppc)(opc2)\n");
cerionb85e8bb2005-02-16 08:54:33 +00005452 return False;
5453 }
5454 return True;
cerion8c3adda2005-01-31 11:54:05 +00005455}
5456
5457
cerion3d870a32005-03-18 12:23:33 +00005458/*
5459 Cache Management Instructions
5460*/
sewardjd94b73a2005-06-30 12:08:48 +00005461static Bool dis_cache_manage ( UInt theInstr,
sewardj9e6491a2005-07-02 19:24:10 +00005462 DisResult* dres,
sewardjd94b73a2005-06-30 12:08:48 +00005463 VexArchInfo* guest_archinfo )
cerion8c3adda2005-01-31 11:54:05 +00005464{
cerionb85e8bb2005-02-16 08:54:33 +00005465 /* X-Form */
cerion76de5cf2005-11-18 18:25:12 +00005466 UChar opc1 = ifieldOPC(theInstr);
5467 UChar b21to25 = ifieldRegDS(theInstr);
5468 UChar rA_addr = ifieldRegA(theInstr);
5469 UChar rB_addr = ifieldRegB(theInstr);
5470 UInt opc2 = ifieldOPClo10(theInstr);
5471 UChar b0 = ifieldBIT0(theInstr);
cerion5b2325f2005-12-23 00:55:09 +00005472 UInt lineszB = guest_archinfo->ppc_cache_line_szB;
ceriond953ebb2005-11-29 13:27:20 +00005473
5474 IRType ty = mode64 ? Ity_I64 : Ity_I32;
cerion094d1392005-06-20 13:45:57 +00005475
sewardj6be67232006-01-24 19:00:05 +00005476 /* For dcbt, the lowest two bits of b21to25 encode an
5477 access-direction hint (TH field) which we ignore. Well, that's
5478 what the PowerPC documentation says. In fact xlc -O4 on POWER5
5479 seems to generate values of 8 and 10 for b21to25. */
5480 if (opc1 == 0x1F && opc2 == 0x116) {
5481 /* b21to25 &= ~3; */ /* if the docs were true */
5482 b21to25 = 0; /* blunt instrument */
5483 }
5484
cerionb85e8bb2005-02-16 08:54:33 +00005485 if (opc1 != 0x1F || b21to25 != 0 || b0 != 0) {
sewardj6be67232006-01-24 19:00:05 +00005486 if (0) vex_printf("dis_cache_manage %d %d %d\n",
5487 (Int)opc1, (Int)b21to25, (Int)b0);
cerion5b2325f2005-12-23 00:55:09 +00005488 vex_printf("dis_cache_manage(ppc)(opc1|b21to25|b0)\n");
cerionb85e8bb2005-02-16 08:54:33 +00005489 return False;
5490 }
sewardjd94b73a2005-06-30 12:08:48 +00005491
5492 /* stay sane .. */
5493 vassert(lineszB == 32 || lineszB == 128);
cerionb85e8bb2005-02-16 08:54:33 +00005494
5495 switch (opc2) {
sewardjb51f0f42005-07-18 11:38:02 +00005496//zz case 0x2F6: // dcba (Data Cache Block Allocate, PPC32 p380)
5497//zz vassert(0); /* AWAITING TEST CASE */
ceriond953ebb2005-11-29 13:27:20 +00005498//zz DIP("dcba r%u,r%u\n", rA_addr, rB_addr);
cerion5b2325f2005-12-23 00:55:09 +00005499//zz if (0) vex_printf("vex ppc->IR: kludged dcba\n");
sewardjb51f0f42005-07-18 11:38:02 +00005500//zz break;
sewardj20ef5472005-07-21 14:48:31 +00005501
5502 case 0x056: // dcbf (Data Cache Block Flush, PPC32 p382)
ceriond953ebb2005-11-29 13:27:20 +00005503 DIP("dcbf r%u,r%u\n", rA_addr, rB_addr);
sewardj20ef5472005-07-21 14:48:31 +00005504 /* nop as far as vex is concerned */
sewardj20ef5472005-07-21 14:48:31 +00005505 break;
cerionb85e8bb2005-02-16 08:54:33 +00005506
cerione9d361a2005-03-04 17:35:29 +00005507 case 0x036: // dcbst (Data Cache Block Store, PPC32 p384)
ceriond953ebb2005-11-29 13:27:20 +00005508 DIP("dcbst r%u,r%u\n", rA_addr, rB_addr);
sewardjb51f0f42005-07-18 11:38:02 +00005509 /* nop as far as vex is concerned */
cerionb85e8bb2005-02-16 08:54:33 +00005510 break;
cerion8c3adda2005-01-31 11:54:05 +00005511
cerione9d361a2005-03-04 17:35:29 +00005512 case 0x116: // dcbt (Data Cache Block Touch, PPC32 p385)
ceriond953ebb2005-11-29 13:27:20 +00005513 DIP("dcbt r%u,r%u\n", rA_addr, rB_addr);
sewardjb51f0f42005-07-18 11:38:02 +00005514 /* nop as far as vex is concerned */
cerionb85e8bb2005-02-16 08:54:33 +00005515 break;
5516
cerione9d361a2005-03-04 17:35:29 +00005517 case 0x0F6: // dcbtst (Data Cache Block Touch for Store, PPC32 p386)
ceriond953ebb2005-11-29 13:27:20 +00005518 DIP("dcbtst r%u,r%u\n", rA_addr, rB_addr);
sewardjb51f0f42005-07-18 11:38:02 +00005519 /* nop as far as vex is concerned */
cerionb85e8bb2005-02-16 08:54:33 +00005520 break;
5521
cerion094d1392005-06-20 13:45:57 +00005522 case 0x3F6: { // dcbz (Data Cache Block Clear to Zero, PPC32 p387)
sewardjd94b73a2005-06-30 12:08:48 +00005523 /* Clear all bytes in cache block at (rA|0) + rB. */
cerion2831b002005-11-30 19:55:22 +00005524 IRTemp EA = newTemp(ty);
5525 IRTemp addr = newTemp(ty);
cerion094d1392005-06-20 13:45:57 +00005526 IRExpr* irx_addr;
cerion094d1392005-06-20 13:45:57 +00005527 UInt i;
ceriond953ebb2005-11-29 13:27:20 +00005528 DIP("dcbz r%u,r%u\n", rA_addr, rB_addr);
sewardjcb1f68e2005-12-30 03:39:14 +00005529
ceriond953ebb2005-11-29 13:27:20 +00005530 assign( EA, ea_rAor0_idxd(rA_addr, rB_addr) );
cerion094d1392005-06-20 13:45:57 +00005531
cerion2831b002005-11-30 19:55:22 +00005532 if (mode64) {
5533 /* Round EA down to the start of the containing block. */
5534 assign( addr, binop( Iop_And64,
5535 mkexpr(EA),
5536 mkU64( ~((ULong)lineszB-1) )) );
5537
5538 for (i = 0; i < lineszB / 8; i++) {
5539 irx_addr = binop( Iop_Add64, mkexpr(addr), mkU64(i*8) );
5540 storeBE( irx_addr, mkU64(0) );
5541 }
5542 } else {
5543 /* Round EA down to the start of the containing block. */
5544 assign( addr, binop( Iop_And32,
5545 mkexpr(EA),
5546 mkU32( ~(lineszB-1) )) );
5547
5548 for (i = 0; i < lineszB / 4; i++) {
5549 irx_addr = binop( Iop_Add32, mkexpr(addr), mkU32(i*4) );
5550 storeBE( irx_addr, mkU32(0) );
5551 }
cerion094d1392005-06-20 13:45:57 +00005552 }
cerionb85e8bb2005-02-16 08:54:33 +00005553 break;
cerion094d1392005-06-20 13:45:57 +00005554 }
cerion8c3adda2005-01-31 11:54:05 +00005555
sewardj7ce9d152005-03-15 16:54:13 +00005556 case 0x3D6: {
5557 // icbi (Instruction Cache Block Invalidate, PPC32 p431)
5558 /* Invalidate all translations containing code from the cache
sewardjd94b73a2005-06-30 12:08:48 +00005559 block at (rA|0) + rB. */
ceriond953ebb2005-11-29 13:27:20 +00005560 IRTemp EA = newTemp(ty);
5561 IRTemp addr = newTemp(ty);
5562 DIP("icbi r%u,r%u\n", rA_addr, rB_addr);
5563 assign( EA, ea_rAor0_idxd(rA_addr, rB_addr) );
sewardj7ce9d152005-03-15 16:54:13 +00005564
cerion2831b002005-11-30 19:55:22 +00005565 /* Round EA down to the start of the containing block. */
5566 assign( addr, binop( mkSzOp(ty, Iop_And8),
ceriond953ebb2005-11-29 13:27:20 +00005567 mkexpr(EA),
cerion2831b002005-11-30 19:55:22 +00005568 mkSzImm(ty, ~(((ULong)lineszB)-1) )) );
ceriond953ebb2005-11-29 13:27:20 +00005569 putGST( PPC_GST_TISTART, mkexpr(addr) );
cerion2831b002005-11-30 19:55:22 +00005570 putGST( PPC_GST_TILEN, mkSzImm(ty, lineszB) );
sewardj7ce9d152005-03-15 16:54:13 +00005571
sewardja8078f62005-03-15 18:27:40 +00005572 /* be paranoid ... */
5573 stmt( IRStmt_MFence() );
5574
sewardj7ce9d152005-03-15 16:54:13 +00005575 irbb->jumpkind = Ijk_TInval;
cerion2831b002005-11-30 19:55:22 +00005576 irbb->next = mkSzImm(ty, nextInsnAddr());
sewardj9e6491a2005-07-02 19:24:10 +00005577 dres->whatNext = Dis_StopHere;
cerionb85e8bb2005-02-16 08:54:33 +00005578 break;
sewardj7ce9d152005-03-15 16:54:13 +00005579 }
cerion8c3adda2005-01-31 11:54:05 +00005580
cerionb85e8bb2005-02-16 08:54:33 +00005581 default:
cerion5b2325f2005-12-23 00:55:09 +00005582 vex_printf("dis_cache_manage(ppc)(opc2)\n");
cerionb85e8bb2005-02-16 08:54:33 +00005583 return False;
5584 }
5585 return True;
cerion8c3adda2005-01-31 11:54:05 +00005586}
5587
5588
sewardje14bb9f2005-07-22 09:39:02 +00005589/*------------------------------------------------------------*/
5590/*--- Floating Point Helpers ---*/
5591/*------------------------------------------------------------*/
5592
sewardje14bb9f2005-07-22 09:39:02 +00005593/* --------- Synthesise a 2-bit FPU rounding mode. --------- */
5594/* Produces a value in 0 .. 3, which is encoded as per the type
cerion5b2325f2005-12-23 00:55:09 +00005595 IRRoundingMode. PPCRoundingMode encoding is different to
sewardje14bb9f2005-07-22 09:39:02 +00005596 IRRoundingMode, so need to map it.
5597*/
sewardjb183b852006-02-03 16:08:03 +00005598static IRExpr* /* :: Ity_I32 */ get_IR_roundingmode ( void )
sewardje14bb9f2005-07-22 09:39:02 +00005599{
5600/*
5601 rounding mode | PPC | IR
5602 ------------------------
5603 to nearest | 00 | 00
5604 to zero | 01 | 11
5605 to +infinity | 10 | 10
5606 to -infinity | 11 | 01
5607*/
5608 IRTemp rm_PPC32 = newTemp(Ity_I32);
ceriond953ebb2005-11-29 13:27:20 +00005609 assign( rm_PPC32, getGST_masked( PPC_GST_FPSCR, MASK_FPSCR_RN ) );
sewardje14bb9f2005-07-22 09:39:02 +00005610
5611 // rm_IR = XOR( rm_PPC32, (rm_PPC32 << 1) & 2)
sewardjb183b852006-02-03 16:08:03 +00005612 return binop( Iop_Xor32,
5613 mkexpr(rm_PPC32),
5614 binop( Iop_And32,
5615 binop(Iop_Shl32, mkexpr(rm_PPC32), mkU8(1)),
5616 mkU32(2) ));
sewardje14bb9f2005-07-22 09:39:02 +00005617}
cerion094d1392005-06-20 13:45:57 +00005618
5619
cerion3d870a32005-03-18 12:23:33 +00005620/*------------------------------------------------------------*/
5621/*--- Floating Point Instruction Translation ---*/
5622/*------------------------------------------------------------*/
5623
5624/*
5625 Floating Point Load Instructions
5626*/
5627static Bool dis_fp_load ( UInt theInstr )
5628{
cerion76de5cf2005-11-18 18:25:12 +00005629 /* X-Form, D-Form */
5630 UChar opc1 = ifieldOPC(theInstr);
5631 UChar frD_addr = ifieldRegDS(theInstr);
5632 UChar rA_addr = ifieldRegA(theInstr);
5633 UChar rB_addr = ifieldRegB(theInstr);
5634 UInt opc2 = ifieldOPClo10(theInstr);
5635 UChar b0 = ifieldBIT0(theInstr);
cerion2831b002005-11-30 19:55:22 +00005636 UInt uimm16 = ifieldUIMM16(theInstr);
cerion094d1392005-06-20 13:45:57 +00005637
cerion2831b002005-11-30 19:55:22 +00005638 Int simm16 = extend_s_16to32(uimm16);
5639 IRType ty = mode64 ? Ity_I64 : Ity_I32;
5640 IRTemp EA = newTemp(ty);
5641 IRTemp rA = newTemp(ty);
5642 IRTemp rB = newTemp(ty);
cerion094d1392005-06-20 13:45:57 +00005643
5644 assign( rA, getIReg(rA_addr) );
5645 assign( rB, getIReg(rB_addr) );
ceriond953ebb2005-11-29 13:27:20 +00005646
sewardjb183b852006-02-03 16:08:03 +00005647 /* These are completely straightforward from a rounding and status
5648 bits perspective: no rounding involved and no funny status or CR
5649 bits affected. */
cerion3d870a32005-03-18 12:23:33 +00005650
sewardjb183b852006-02-03 16:08:03 +00005651 switch (opc1) {
sewardje14bb9f2005-07-22 09:39:02 +00005652 case 0x30: // lfs (Load Float Single, PPC32 p441)
ceriond953ebb2005-11-29 13:27:20 +00005653 DIP("lfs fr%u,%d(r%u)\n", frD_addr, simm16, rA_addr);
5654 assign( EA, ea_rAor0_simm(rA_addr, simm16) );
cerion5b2325f2005-12-23 00:55:09 +00005655 putFReg( frD_addr,
5656 unop(Iop_F32toF64, loadBE(Ity_F32, mkexpr(EA))) );
sewardje14bb9f2005-07-22 09:39:02 +00005657 break;
5658
cerion5b2325f2005-12-23 00:55:09 +00005659 case 0x31: // lfsu (Load Float Single, Update, PPC32 p442)
sewardjb183b852006-02-03 16:08:03 +00005660 if (rA_addr == 0)
cerion729edb72005-12-02 16:03:46 +00005661 return False;
cerion729edb72005-12-02 16:03:46 +00005662 DIP("lfsu fr%u,%d(r%u)\n", frD_addr, simm16, rA_addr);
5663 assign( EA, ea_rA_simm(rA_addr, simm16) );
cerion5b2325f2005-12-23 00:55:09 +00005664 putFReg( frD_addr,
5665 unop(Iop_F32toF64, loadBE(Ity_F32, mkexpr(EA))) );
cerion729edb72005-12-02 16:03:46 +00005666 putIReg( rA_addr, mkexpr(EA) );
5667 break;
cerion094d1392005-06-20 13:45:57 +00005668
5669 case 0x32: // lfd (Load Float Double, PPC32 p437)
ceriond953ebb2005-11-29 13:27:20 +00005670 DIP("lfd fr%u,%d(r%u)\n", frD_addr, simm16, rA_addr);
5671 assign( EA, ea_rAor0_simm(rA_addr, simm16) );
cerion094d1392005-06-20 13:45:57 +00005672 putFReg( frD_addr, loadBE(Ity_F64, mkexpr(EA)) );
5673 break;
cerion3d870a32005-03-18 12:23:33 +00005674
cerion5b2325f2005-12-23 00:55:09 +00005675 case 0x33: // lfdu (Load Float Double, Update, PPC32 p438)
sewardjb183b852006-02-03 16:08:03 +00005676 if (rA_addr == 0)
sewardj0e2cc672005-07-29 21:58:51 +00005677 return False;
ceriond953ebb2005-11-29 13:27:20 +00005678 DIP("lfdu fr%u,%d(r%u)\n", frD_addr, simm16, rA_addr);
5679 assign( EA, ea_rA_simm(rA_addr, simm16) );
sewardj0e2cc672005-07-29 21:58:51 +00005680 putFReg( frD_addr, loadBE(Ity_F64, mkexpr(EA)) );
5681 putIReg( rA_addr, mkexpr(EA) );
5682 break;
sewardje14bb9f2005-07-22 09:39:02 +00005683
5684 case 0x1F:
5685 if (b0 != 0) {
cerion5b2325f2005-12-23 00:55:09 +00005686 vex_printf("dis_fp_load(ppc)(instr,b0)\n");
sewardje14bb9f2005-07-22 09:39:02 +00005687 return False;
5688 }
5689
5690 switch(opc2) {
ceriond953ebb2005-11-29 13:27:20 +00005691 case 0x217: // lfsx (Load Float Single Indexed, PPC32 p444)
5692 DIP("lfsx fr%u,r%u,r%u\n", frD_addr, rA_addr, rB_addr);
5693 assign( EA, ea_rAor0_idxd(rA_addr, rB_addr) );
5694 putFReg( frD_addr, unop( Iop_F32toF64,
5695 loadBE(Ity_F32, mkexpr(EA))) );
5696 break;
5697
cerion5b2325f2005-12-23 00:55:09 +00005698 case 0x237: // lfsux (Load Float Single, Update Indxd, PPC32 p443)
sewardjb183b852006-02-03 16:08:03 +00005699 if (rA_addr == 0)
sewardje14bb9f2005-07-22 09:39:02 +00005700 return False;
ceriond953ebb2005-11-29 13:27:20 +00005701 DIP("lfsux fr%u,r%u,r%u\n", frD_addr, rA_addr, rB_addr);
5702 assign( EA, ea_rA_idxd(rA_addr, rB_addr) );
cerion5b2325f2005-12-23 00:55:09 +00005703 putFReg( frD_addr,
5704 unop(Iop_F32toF64, loadBE(Ity_F32, mkexpr(EA))) );
ceriond953ebb2005-11-29 13:27:20 +00005705 putIReg( rA_addr, mkexpr(EA) );
5706 break;
5707
5708 case 0x257: // lfdx (Load Float Double Indexed, PPC32 p440)
5709 DIP("lfdx fr%u,r%u,r%u\n", frD_addr, rA_addr, rB_addr);
5710 assign( EA, ea_rAor0_idxd(rA_addr, rB_addr) );
5711 putFReg( frD_addr, loadBE(Ity_F64, mkexpr(EA)) );
5712 break;
5713
cerion5b2325f2005-12-23 00:55:09 +00005714 case 0x277: // lfdux (Load Float Double, Update Indxd, PPC32 p439)
sewardjb183b852006-02-03 16:08:03 +00005715 if (rA_addr == 0)
ceriond953ebb2005-11-29 13:27:20 +00005716 return False;
ceriond953ebb2005-11-29 13:27:20 +00005717 DIP("lfdux fr%u,r%u,r%u\n", frD_addr, rA_addr, rB_addr);
5718 assign( EA, ea_rA_idxd(rA_addr, rB_addr) );
5719 putFReg( frD_addr, loadBE(Ity_F64, mkexpr(EA)) );
5720 putIReg( rA_addr, mkexpr(EA) );
5721 break;
5722
5723 default:
cerion5b2325f2005-12-23 00:55:09 +00005724 vex_printf("dis_fp_load(ppc)(opc2)\n");
ceriond953ebb2005-11-29 13:27:20 +00005725 return False;
sewardje14bb9f2005-07-22 09:39:02 +00005726 }
5727 break;
cerion3d870a32005-03-18 12:23:33 +00005728
5729 default:
cerion5b2325f2005-12-23 00:55:09 +00005730 vex_printf("dis_fp_load(ppc)(opc1)\n");
cerion3d870a32005-03-18 12:23:33 +00005731 return False;
5732 }
5733 return True;
5734}
5735
5736
5737
5738/*
5739 Floating Point Store Instructions
5740*/
5741static Bool dis_fp_store ( UInt theInstr )
5742{
cerion76de5cf2005-11-18 18:25:12 +00005743 /* X-Form, D-Form */
5744 UChar opc1 = ifieldOPC(theInstr);
5745 UChar frS_addr = ifieldRegDS(theInstr);
5746 UChar rA_addr = ifieldRegA(theInstr);
5747 UChar rB_addr = ifieldRegB(theInstr);
5748 UInt opc2 = ifieldOPClo10(theInstr);
5749 UChar b0 = ifieldBIT0(theInstr);
ceriond953ebb2005-11-29 13:27:20 +00005750 Int uimm16 = ifieldUIMM16(theInstr);
cerion094d1392005-06-20 13:45:57 +00005751
cerion2831b002005-11-30 19:55:22 +00005752 Int simm16 = extend_s_16to32(uimm16);
5753 IRTemp frS = newTemp(Ity_F64);
5754 IRType ty = mode64 ? Ity_I64 : Ity_I32;
5755 IRTemp EA = newTemp(ty);
5756 IRTemp rA = newTemp(ty);
5757 IRTemp rB = newTemp(ty);
cerion094d1392005-06-20 13:45:57 +00005758
5759 assign( frS, getFReg(frS_addr) );
cerionedf7fc52005-11-18 20:57:41 +00005760 assign( rA, getIReg(rA_addr) );
5761 assign( rB, getIReg(rB_addr) );
cerion3d870a32005-03-18 12:23:33 +00005762
sewardjb183b852006-02-03 16:08:03 +00005763 /* These are straightforward from a status bits perspective: no
5764 funny status or CR bits affected. For single precision stores,
5765 the values are truncated and denormalised (not rounded) to turn
5766 them into single precision values. */
5767
5768 switch (opc1) {
sewardje14bb9f2005-07-22 09:39:02 +00005769
5770 case 0x34: // stfs (Store Float Single, PPC32 p518)
ceriond953ebb2005-11-29 13:27:20 +00005771 DIP("stfs fr%u,%d(r%u)\n", frS_addr, simm16, rA_addr);
5772 assign( EA, ea_rAor0_simm(rA_addr, simm16) );
sewardjb183b852006-02-03 16:08:03 +00005773 /* Use Iop_TruncF64asF32 to truncate and possible denormalise
5774 the value to be stored in the correct way, without any
5775 rounding. */
sewardje14bb9f2005-07-22 09:39:02 +00005776 storeBE( mkexpr(EA),
sewardjb183b852006-02-03 16:08:03 +00005777 unop(Iop_TruncF64asF32, mkexpr(frS)) );
sewardje14bb9f2005-07-22 09:39:02 +00005778 break;
5779
cerion5b2325f2005-12-23 00:55:09 +00005780 case 0x35: // stfsu (Store Float Single, Update, PPC32 p519)
sewardjb183b852006-02-03 16:08:03 +00005781 if (rA_addr == 0)
cerion729edb72005-12-02 16:03:46 +00005782 return False;
cerion729edb72005-12-02 16:03:46 +00005783 DIP("stfsu fr%u,%d(r%u)\n", frS_addr, simm16, rA_addr);
5784 assign( EA, ea_rA_simm(rA_addr, simm16) );
sewardjb183b852006-02-03 16:08:03 +00005785 /* See comment for stfs */
cerion729edb72005-12-02 16:03:46 +00005786 storeBE( mkexpr(EA),
sewardjb183b852006-02-03 16:08:03 +00005787 unop(Iop_TruncF64asF32, mkexpr(frS)) );
cerion729edb72005-12-02 16:03:46 +00005788 putIReg( rA_addr, mkexpr(EA) );
5789 break;
cerion3d870a32005-03-18 12:23:33 +00005790
cerion094d1392005-06-20 13:45:57 +00005791 case 0x36: // stfd (Store Float Double, PPC32 p513)
ceriond953ebb2005-11-29 13:27:20 +00005792 DIP("stfd fr%u,%d(r%u)\n", frS_addr, simm16, rA_addr);
5793 assign( EA, ea_rAor0_simm(rA_addr, simm16) );
cerion094d1392005-06-20 13:45:57 +00005794 storeBE( mkexpr(EA), mkexpr(frS) );
5795 break;
cerion3d870a32005-03-18 12:23:33 +00005796
cerion5b2325f2005-12-23 00:55:09 +00005797 case 0x37: // stfdu (Store Float Double, Update, PPC32 p514)
sewardjb183b852006-02-03 16:08:03 +00005798 if (rA_addr == 0)
sewardje14bb9f2005-07-22 09:39:02 +00005799 return False;
ceriond953ebb2005-11-29 13:27:20 +00005800 DIP("stfdu fr%u,%d(r%u)\n", frS_addr, simm16, rA_addr);
5801 assign( EA, ea_rA_simm(rA_addr, simm16) );
sewardje14bb9f2005-07-22 09:39:02 +00005802 storeBE( mkexpr(EA), mkexpr(frS) );
5803 putIReg( rA_addr, mkexpr(EA) );
5804 break;
5805
5806 case 0x1F:
5807 if (b0 != 0) {
cerion5b2325f2005-12-23 00:55:09 +00005808 vex_printf("dis_fp_store(ppc)(instr,b0)\n");
sewardje14bb9f2005-07-22 09:39:02 +00005809 return False;
5810 }
sewardje14bb9f2005-07-22 09:39:02 +00005811 switch(opc2) {
ceriond953ebb2005-11-29 13:27:20 +00005812 case 0x297: // stfsx (Store Float Single Indexed, PPC32 p521)
5813 DIP("stfsx fr%u,r%u,r%u\n", frS_addr, rA_addr, rB_addr);
5814 assign( EA, ea_rAor0_idxd(rA_addr, rB_addr) );
sewardjb183b852006-02-03 16:08:03 +00005815 /* See note for stfs */
5816 storeBE( mkexpr(EA),
5817 unop(Iop_TruncF64asF32, mkexpr(frS)) );
ceriond953ebb2005-11-29 13:27:20 +00005818 break;
5819
cerion5b2325f2005-12-23 00:55:09 +00005820 case 0x2B7: // stfsux (Store Float Sgl, Update Indxd, PPC32 p520)
sewardjb183b852006-02-03 16:08:03 +00005821 if (rA_addr == 0)
cerion729edb72005-12-02 16:03:46 +00005822 return False;
cerion729edb72005-12-02 16:03:46 +00005823 DIP("stfsux fr%u,r%u,r%u\n", frS_addr, rA_addr, rB_addr);
5824 assign( EA, ea_rA_idxd(rA_addr, rB_addr) );
sewardjb183b852006-02-03 16:08:03 +00005825 /* See note for stfs */
5826 storeBE( mkexpr(EA),
5827 unop(Iop_TruncF64asF32, mkexpr(frS)) );
cerion729edb72005-12-02 16:03:46 +00005828 putIReg( rA_addr, mkexpr(EA) );
5829 break;
sewardje14bb9f2005-07-22 09:39:02 +00005830
ceriond953ebb2005-11-29 13:27:20 +00005831 case 0x2D7: // stfdx (Store Float Double Indexed, PPC32 p516)
5832 DIP("stfdx fr%u,r%u,r%u\n", frS_addr, rA_addr, rB_addr);
5833 assign( EA, ea_rAor0_idxd(rA_addr, rB_addr) );
5834 storeBE( mkexpr(EA), mkexpr(frS) );
5835 break;
sewardje14bb9f2005-07-22 09:39:02 +00005836
cerion5b2325f2005-12-23 00:55:09 +00005837 case 0x2F7: // stfdux (Store Float Dbl, Update Indxd, PPC32 p515)
sewardjb183b852006-02-03 16:08:03 +00005838 if (rA_addr == 0)
ceriond953ebb2005-11-29 13:27:20 +00005839 return False;
ceriond953ebb2005-11-29 13:27:20 +00005840 DIP("stfdux fr%u,r%u,r%u\n", frS_addr, rA_addr, rB_addr);
5841 assign( EA, ea_rA_idxd(rA_addr, rB_addr) );
5842 storeBE( mkexpr(EA), mkexpr(frS) );
5843 putIReg( rA_addr, mkexpr(EA) );
5844 break;
sewardj5f63c0c2005-09-09 10:36:55 +00005845
sewardj09e88d12006-01-27 16:05:49 +00005846 case 0x3D7: // stfiwx (Store Float as Int, Indexed, PPC32 p517)
sewardj5117ce12006-01-27 21:20:15 +00005847 // NOTE: POWERPC OPTIONAL, "Graphics Group" (PPC32_GX)
sewardj09e88d12006-01-27 16:05:49 +00005848 DIP("stfiwx fr%u,r%u,r%u\n", frS_addr, rA_addr, rB_addr);
5849 assign( EA, ea_rAor0_idxd(rA_addr, rB_addr) );
5850 storeBE( mkexpr(EA),
5851 unop(Iop_64to32, unop(Iop_ReinterpF64asI64, mkexpr(frS))) );
5852 break;
sewardje14bb9f2005-07-22 09:39:02 +00005853
ceriond953ebb2005-11-29 13:27:20 +00005854 default:
cerion5b2325f2005-12-23 00:55:09 +00005855 vex_printf("dis_fp_store(ppc)(opc2)\n");
ceriond953ebb2005-11-29 13:27:20 +00005856 return False;
sewardje14bb9f2005-07-22 09:39:02 +00005857 }
5858 break;
cerion3d870a32005-03-18 12:23:33 +00005859
5860 default:
cerion5b2325f2005-12-23 00:55:09 +00005861 vex_printf("dis_fp_store(ppc)(opc1)\n");
cerion3d870a32005-03-18 12:23:33 +00005862 return False;
5863 }
5864 return True;
5865}
5866
5867
5868
5869/*
5870 Floating Point Arith Instructions
5871*/
5872static Bool dis_fp_arith ( UInt theInstr )
5873{
5874 /* A-Form */
cerion76de5cf2005-11-18 18:25:12 +00005875 UChar opc1 = ifieldOPC(theInstr);
5876 UChar frD_addr = ifieldRegDS(theInstr);
5877 UChar frA_addr = ifieldRegA(theInstr);
5878 UChar frB_addr = ifieldRegB(theInstr);
5879 UChar frC_addr = ifieldRegC(theInstr);
5880 UChar opc2 = ifieldOPClo5(theInstr);
5881 UChar flag_rC = ifieldBIT0(theInstr);
cerion094d1392005-06-20 13:45:57 +00005882
sewardjb183b852006-02-03 16:08:03 +00005883 IRTemp frD = newTemp(Ity_F64);
5884 IRTemp frA = newTemp(Ity_F64);
5885 IRTemp frB = newTemp(Ity_F64);
5886 IRTemp frC = newTemp(Ity_F64);
5887 IRExpr* rm = get_IR_roundingmode();
5888
5889 /* By default, we will examine the results of the operation and set
5890 fpscr[FPRF] accordingly. */
5891 Bool set_FPRF = True;
5892
5893 /* By default, if flag_RC is set, we will clear cr1 after the
5894 operation. In reality we should set cr1 to indicate the
5895 exception status of the operation, but since we're not
5896 simulating exceptions, the exception status will appear to be
5897 zero. Hence cr1 should be cleared if this is a . form insn. */
5898 Bool clear_CR1 = True;
cerion094d1392005-06-20 13:45:57 +00005899
5900 assign( frA, getFReg(frA_addr));
5901 assign( frB, getFReg(frB_addr));
5902 assign( frC, getFReg(frC_addr));
cerion3d870a32005-03-18 12:23:33 +00005903
5904 switch (opc1) {
sewardje14bb9f2005-07-22 09:39:02 +00005905 case 0x3B:
5906 switch (opc2) {
5907 case 0x12: // fdivs (Floating Divide Single, PPC32 p407)
sewardjb183b852006-02-03 16:08:03 +00005908 if (frC_addr != 0)
sewardje14bb9f2005-07-22 09:39:02 +00005909 return False;
cerion5b2325f2005-12-23 00:55:09 +00005910 DIP("fdivs%s fr%u,fr%u,fr%u\n", flag_rC ? ".":"",
sewardje14bb9f2005-07-22 09:39:02 +00005911 frD_addr, frA_addr, frB_addr);
sewardjb183b852006-02-03 16:08:03 +00005912 assign( frD, triop( Iop_DivF64r32,
5913 rm, mkexpr(frA), mkexpr(frB) ));
sewardje14bb9f2005-07-22 09:39:02 +00005914 break;
5915
5916 case 0x14: // fsubs (Floating Subtract Single, PPC32 p430)
sewardjb183b852006-02-03 16:08:03 +00005917 if (frC_addr != 0)
sewardje14bb9f2005-07-22 09:39:02 +00005918 return False;
cerion5b2325f2005-12-23 00:55:09 +00005919 DIP("fsubs%s fr%u,fr%u,fr%u\n", flag_rC ? ".":"",
sewardje14bb9f2005-07-22 09:39:02 +00005920 frD_addr, frA_addr, frB_addr);
sewardjb183b852006-02-03 16:08:03 +00005921 assign( frD, triop( Iop_SubF64r32,
5922 rm, mkexpr(frA), mkexpr(frB) ));
sewardje14bb9f2005-07-22 09:39:02 +00005923 break;
5924
5925 case 0x15: // fadds (Floating Add Single, PPC32 p401)
sewardjb183b852006-02-03 16:08:03 +00005926 if (frC_addr != 0)
sewardje14bb9f2005-07-22 09:39:02 +00005927 return False;
cerion5b2325f2005-12-23 00:55:09 +00005928 DIP("fadds%s fr%u,fr%u,fr%u\n", flag_rC ? ".":"",
sewardje14bb9f2005-07-22 09:39:02 +00005929 frD_addr, frA_addr, frB_addr);
sewardjb183b852006-02-03 16:08:03 +00005930 assign( frD, triop( Iop_AddF64r32,
5931 rm, mkexpr(frA), mkexpr(frB) ));
sewardje14bb9f2005-07-22 09:39:02 +00005932 break;
5933
sewardj870c84b2006-01-24 03:33:43 +00005934 case 0x16: // fsqrts (Floating SqRt (Single-Precision), PPC32 p428)
sewardj5117ce12006-01-27 21:20:15 +00005935 // NOTE: POWERPC OPTIONAL, "General-Purpose Group" (PPC32_FX)
sewardjb183b852006-02-03 16:08:03 +00005936 if (frA_addr != 0 || frC_addr != 0)
sewardj870c84b2006-01-24 03:33:43 +00005937 return False;
sewardj870c84b2006-01-24 03:33:43 +00005938 DIP("fsqrts%s fr%u,fr%u\n", flag_rC ? ".":"",
5939 frD_addr, frB_addr);
sewardj79fd33f2006-01-29 17:07:57 +00005940 // however illogically, on ppc970 this insn behaves identically
sewardjb183b852006-02-03 16:08:03 +00005941 // to fsqrt (double-precision). So use SqrtF64, not SqrtF64r32.
5942 assign( frD, binop( Iop_SqrtF64, rm, mkexpr(frB) ));
sewardj870c84b2006-01-24 03:33:43 +00005943 break;
sewardje14bb9f2005-07-22 09:39:02 +00005944
sewardjbaf971a2006-01-27 15:09:35 +00005945 case 0x18: // fres (Floating Reciprocal Estimate Single, PPC32 p421)
sewardj5117ce12006-01-27 21:20:15 +00005946 // NOTE: POWERPC OPTIONAL, "Graphics Group" (PPC32_GX)
sewardjb183b852006-02-03 16:08:03 +00005947 if (frA_addr != 0 || frC_addr != 0)
sewardjbaf971a2006-01-27 15:09:35 +00005948 return False;
sewardjbaf971a2006-01-27 15:09:35 +00005949 DIP("fres%s fr%u,fr%u\n", flag_rC ? ".":"",
5950 frD_addr, frB_addr);
sewardj157b19b2006-01-31 16:32:25 +00005951 { IRExpr* ieee_one
5952 = IRExpr_Const(IRConst_F64i(0x3ff0000000000000ULL));
sewardjb183b852006-02-03 16:08:03 +00005953 assign( frD, triop( Iop_DivF64r32,
5954 rm,
5955 ieee_one, mkexpr(frB) ));
sewardj157b19b2006-01-31 16:32:25 +00005956 }
sewardjbaf971a2006-01-27 15:09:35 +00005957 break;
sewardje14bb9f2005-07-22 09:39:02 +00005958
5959 case 0x19: // fmuls (Floating Multiply Single, PPC32 p414)
sewardjb183b852006-02-03 16:08:03 +00005960 if (frB_addr != 0)
sewardje14bb9f2005-07-22 09:39:02 +00005961 return False;
cerion5b2325f2005-12-23 00:55:09 +00005962 DIP("fmuls%s fr%u,fr%u,fr%u\n", flag_rC ? ".":"",
sewardje14bb9f2005-07-22 09:39:02 +00005963 frD_addr, frA_addr, frC_addr);
sewardjb183b852006-02-03 16:08:03 +00005964 assign( frD, triop( Iop_MulF64r32,
5965 rm, mkexpr(frA), mkexpr(frC) ));
sewardje14bb9f2005-07-22 09:39:02 +00005966 break;
5967
sewardj79fd33f2006-01-29 17:07:57 +00005968 case 0x1A: // frsqrtes (Floating Recip SqRt Est Single)
5969 // NOTE: POWERPC OPTIONAL, "Graphics Group" (PPC32_GX)
5970 // Undocumented instruction?
sewardjb183b852006-02-03 16:08:03 +00005971 if (frA_addr != 0 || frC_addr != 0)
sewardj79fd33f2006-01-29 17:07:57 +00005972 return False;
sewardj79fd33f2006-01-29 17:07:57 +00005973 DIP("frsqrtes%s fr%u,fr%u\n", flag_rC ? ".":"",
5974 frD_addr, frB_addr);
5975 assign( frD, unop(Iop_Est5FRSqrt, mkexpr(frB)) );
5976 break;
5977
sewardje14bb9f2005-07-22 09:39:02 +00005978 default:
cerion5b2325f2005-12-23 00:55:09 +00005979 vex_printf("dis_fp_arith(ppc)(3B: opc2)\n");
sewardje14bb9f2005-07-22 09:39:02 +00005980 return False;
5981 }
5982 break;
cerion094d1392005-06-20 13:45:57 +00005983
cerion3d870a32005-03-18 12:23:33 +00005984 case 0x3F:
5985 switch (opc2) {
cerion5b2325f2005-12-23 00:55:09 +00005986 case 0x12: // fdiv (Floating Div (Double-Precision), PPC32 p406)
sewardjb183b852006-02-03 16:08:03 +00005987 if (frC_addr != 0)
sewardje14bb9f2005-07-22 09:39:02 +00005988 return False;
cerion5b2325f2005-12-23 00:55:09 +00005989 DIP("fdiv%s fr%u,fr%u,fr%u\n", flag_rC ? ".":"",
sewardje14bb9f2005-07-22 09:39:02 +00005990 frD_addr, frA_addr, frB_addr);
sewardjb183b852006-02-03 16:08:03 +00005991 assign( frD, triop(Iop_DivF64, rm, mkexpr(frA), mkexpr(frB)) );
sewardje14bb9f2005-07-22 09:39:02 +00005992 break;
5993
cerion5b2325f2005-12-23 00:55:09 +00005994 case 0x14: // fsub (Floating Sub (Double-Precision), PPC32 p429)
sewardjb183b852006-02-03 16:08:03 +00005995 if (frC_addr != 0)
sewardje14bb9f2005-07-22 09:39:02 +00005996 return False;
cerion5b2325f2005-12-23 00:55:09 +00005997 DIP("fsub%s fr%u,fr%u,fr%u\n", flag_rC ? ".":"",
sewardje14bb9f2005-07-22 09:39:02 +00005998 frD_addr, frA_addr, frB_addr);
sewardjb183b852006-02-03 16:08:03 +00005999 assign( frD, triop(Iop_SubF64, rm, mkexpr(frA), mkexpr(frB)) );
sewardje14bb9f2005-07-22 09:39:02 +00006000 break;
cerion3d870a32005-03-18 12:23:33 +00006001
6002 case 0x15: // fadd (Floating Add (Double-Precision), PPC32 p400)
sewardjb183b852006-02-03 16:08:03 +00006003 if (frC_addr != 0)
cerion3d870a32005-03-18 12:23:33 +00006004 return False;
cerion5b2325f2005-12-23 00:55:09 +00006005 DIP("fadd%s fr%u,fr%u,fr%u\n", flag_rC ? ".":"",
cerion3d870a32005-03-18 12:23:33 +00006006 frD_addr, frA_addr, frB_addr);
sewardjb183b852006-02-03 16:08:03 +00006007 assign( frD, triop(Iop_AddF64, rm, mkexpr(frA), mkexpr(frB)) );
cerion094d1392005-06-20 13:45:57 +00006008 break;
cerion3d870a32005-03-18 12:23:33 +00006009
cerion876ef412005-12-14 22:00:53 +00006010 case 0x16: // fsqrt (Floating SqRt (Double-Precision), PPC32 p427)
sewardj5117ce12006-01-27 21:20:15 +00006011 // NOTE: POWERPC OPTIONAL, "General-Purpose Group" (PPC32_FX)
sewardjb183b852006-02-03 16:08:03 +00006012 if (frA_addr != 0 || frC_addr != 0)
cerion876ef412005-12-14 22:00:53 +00006013 return False;
cerion5b2325f2005-12-23 00:55:09 +00006014 DIP("fsqrt%s fr%u,fr%u\n", flag_rC ? ".":"",
cerion876ef412005-12-14 22:00:53 +00006015 frD_addr, frB_addr);
sewardjb183b852006-02-03 16:08:03 +00006016 assign( frD, binop(Iop_SqrtF64, rm, mkexpr(frB)) );
cerion876ef412005-12-14 22:00:53 +00006017 break;
sewardje14bb9f2005-07-22 09:39:02 +00006018
6019 case 0x17: { // fsel (Floating Select, PPC32 p426)
sewardj5117ce12006-01-27 21:20:15 +00006020 // NOTE: POWERPC OPTIONAL, "Graphics Group" (PPC32_GX)
sewardje14bb9f2005-07-22 09:39:02 +00006021 IRTemp cc = newTemp(Ity_I32);
6022 IRTemp cc_b0 = newTemp(Ity_I32);
6023
cerion5b2325f2005-12-23 00:55:09 +00006024 DIP("fsel%s fr%u,fr%u,fr%u,fr%u\n", flag_rC ? ".":"",
sewardje14bb9f2005-07-22 09:39:02 +00006025 frD_addr, frA_addr, frC_addr, frB_addr);
6026
6027 // cc: UN == 0x41, LT == 0x01, GT == 0x00, EQ == 0x40
6028 // => GT|EQ == (cc & 0x1 == 0)
cerion5b2325f2005-12-23 00:55:09 +00006029 assign( cc, binop(Iop_CmpF64, mkexpr(frA),
6030 IRExpr_Const(IRConst_F64(0))) );
sewardje14bb9f2005-07-22 09:39:02 +00006031 assign( cc_b0, binop(Iop_And32, mkexpr(cc), mkU32(1)) );
6032
6033 // frD = (frA >= 0.0) ? frC : frB
6034 // = (cc_b0 == 0) ? frC : frB
6035 assign( frD,
6036 IRExpr_Mux0X(
6037 unop(Iop_1Uto8,
6038 binop(Iop_CmpEQ32, mkexpr(cc_b0), mkU32(0))),
6039 mkexpr(frB),
6040 mkexpr(frC) ));
sewardjb183b852006-02-03 16:08:03 +00006041
6042 /* One of the rare ones which don't mess with FPRF */
6043 set_FPRF = False;
sewardje14bb9f2005-07-22 09:39:02 +00006044 break;
6045 }
6046
sewardj79fd33f2006-01-29 17:07:57 +00006047 case 0x18: // fre (Floating Reciprocal Estimate)
6048 // NOTE: POWERPC OPTIONAL, "Graphics Group" (PPC32_GX)
6049 // Note: unclear whether this insn really exists or not
6050 // ppc970 doesn't have it, but POWER5 does
sewardjb183b852006-02-03 16:08:03 +00006051 if (frA_addr != 0 || frC_addr != 0)
sewardj79fd33f2006-01-29 17:07:57 +00006052 return False;
sewardj79fd33f2006-01-29 17:07:57 +00006053 DIP("fre%s fr%u,fr%u\n", flag_rC ? ".":"",
6054 frD_addr, frB_addr);
sewardj157b19b2006-01-31 16:32:25 +00006055 { IRExpr* ieee_one
6056 = IRExpr_Const(IRConst_F64i(0x3ff0000000000000ULL));
sewardjb183b852006-02-03 16:08:03 +00006057 assign( frD, triop( Iop_DivF64,
sewardj56de4212006-02-06 22:19:17 +00006058 rm,
sewardjb183b852006-02-03 16:08:03 +00006059 ieee_one, mkexpr(frB) ));
sewardj157b19b2006-01-31 16:32:25 +00006060 }
sewardj79fd33f2006-01-29 17:07:57 +00006061 break;
6062
cerion5b2325f2005-12-23 00:55:09 +00006063 case 0x19: // fmul (Floating Mult (Double Precision), PPC32 p413)
sewardjb183b852006-02-03 16:08:03 +00006064 if (frB_addr != 0)
cerion5b2325f2005-12-23 00:55:09 +00006065 vex_printf("dis_fp_arith(ppc)(instr,fmul)\n");
cerion5b2325f2005-12-23 00:55:09 +00006066 DIP("fmul%s fr%u,fr%u,fr%u\n", flag_rC ? ".":"",
sewardje14bb9f2005-07-22 09:39:02 +00006067 frD_addr, frA_addr, frC_addr);
sewardjb183b852006-02-03 16:08:03 +00006068 assign( frD, triop(Iop_MulF64, rm, mkexpr(frA), mkexpr(frC)) );
sewardje14bb9f2005-07-22 09:39:02 +00006069 break;
6070
sewardjbaf971a2006-01-27 15:09:35 +00006071 case 0x1A: // frsqrte (Floating Recip SqRt Est., PPC32 p424)
sewardj5117ce12006-01-27 21:20:15 +00006072 // NOTE: POWERPC OPTIONAL, "Graphics Group" (PPC32_GX)
sewardjb183b852006-02-03 16:08:03 +00006073 if (frA_addr != 0 || frC_addr != 0)
sewardjbaf971a2006-01-27 15:09:35 +00006074 return False;
sewardjbaf971a2006-01-27 15:09:35 +00006075 DIP("frsqrte%s fr%u,fr%u\n", flag_rC ? ".":"",
6076 frD_addr, frB_addr);
6077 assign( frD, unop(Iop_Est5FRSqrt, mkexpr(frB)) );
6078 break;
cerion3d870a32005-03-18 12:23:33 +00006079
6080 default:
cerion5b2325f2005-12-23 00:55:09 +00006081 vex_printf("dis_fp_arith(ppc)(3F: opc2)\n");
cerion3d870a32005-03-18 12:23:33 +00006082 return False;
6083 }
cerion094d1392005-06-20 13:45:57 +00006084 break;
6085
cerion3d870a32005-03-18 12:23:33 +00006086 default:
cerion5b2325f2005-12-23 00:55:09 +00006087 vex_printf("dis_fp_arith(ppc)(opc1)\n");
cerion3d870a32005-03-18 12:23:33 +00006088 return False;
6089 }
cerion094d1392005-06-20 13:45:57 +00006090
6091 putFReg( frD_addr, mkexpr(frD) );
sewardjb183b852006-02-03 16:08:03 +00006092
6093 if (set_FPRF) {
6094 // XXX XXX XXX FIXME
6095 // set FPRF from frD
6096 }
6097
6098 if (flag_rC && clear_CR1) {
6099 putCR321( 1, mkU8(0) );
6100 putCR0( 1, mkU8(0) );
6101 }
6102
cerion3d870a32005-03-18 12:23:33 +00006103 return True;
6104}
6105
6106
6107
sewardje14bb9f2005-07-22 09:39:02 +00006108/*
6109 Floating Point Mult-Add Instructions
6110*/
6111static Bool dis_fp_multadd ( UInt theInstr )
6112{
6113 /* A-Form */
cerion76de5cf2005-11-18 18:25:12 +00006114 UChar opc1 = ifieldOPC(theInstr);
6115 UChar frD_addr = ifieldRegDS(theInstr);
6116 UChar frA_addr = ifieldRegA(theInstr);
6117 UChar frB_addr = ifieldRegB(theInstr);
6118 UChar frC_addr = ifieldRegC(theInstr);
6119 UChar opc2 = ifieldOPClo5(theInstr);
6120 UChar flag_rC = ifieldBIT0(theInstr);
sewardje14bb9f2005-07-22 09:39:02 +00006121
sewardjb183b852006-02-03 16:08:03 +00006122 IRTemp frD = newTemp(Ity_F64);
6123 IRTemp frA = newTemp(Ity_F64);
6124 IRTemp frB = newTemp(Ity_F64);
6125 IRTemp frC = newTemp(Ity_F64);
6126 IRTemp rmt = newTemp(Ity_I32);
6127 IRExpr* rm;
6128
6129 /* By default, we will examine the results of the operation and set
6130 fpscr[FPRF] accordingly. */
6131 Bool set_FPRF = True;
6132
6133 /* By default, if flag_RC is set, we will clear cr1 after the
6134 operation. In reality we should set cr1 to indicate the
6135 exception status of the operation, but since we're not
6136 simulating exceptions, the exception status will appear to be
6137 zero. Hence cr1 should be cleared if this is a . form insn. */
6138 Bool clear_CR1 = True;
6139
6140 /* Bind the rounding mode expression to a temp; there's no
6141 point in creating gratuitous CSEs, as we know we'll need
6142 to use it twice. */
6143 assign( rmt, get_IR_roundingmode() );
6144 rm = mkexpr(rmt);
sewardje14bb9f2005-07-22 09:39:02 +00006145
6146 assign( frA, getFReg(frA_addr));
6147 assign( frB, getFReg(frB_addr));
6148 assign( frC, getFReg(frC_addr));
6149
sewardjb183b852006-02-03 16:08:03 +00006150 /* The rounding in this is all a bit dodgy. The idea is to only do
6151 one rounding. That clearly isn't achieveable without dedicated
6152 four-input IR primops, although in the single precision case we
6153 can sort-of simulate it by doing the inner multiply in double
6154 precision.
6155
6156 In the negated cases, the negation happens after rounding. */
6157
sewardje14bb9f2005-07-22 09:39:02 +00006158 switch (opc1) {
6159 case 0x3B:
6160 switch (opc2) {
6161 case 0x1C: // fmsubs (Floating Mult-Subtr Single, PPC32 p412)
cerion5b2325f2005-12-23 00:55:09 +00006162 DIP("fmsubs%s fr%u,fr%u,fr%u,fr%u\n", flag_rC ? ".":"",
sewardje14bb9f2005-07-22 09:39:02 +00006163 frD_addr, frA_addr, frC_addr, frB_addr);
sewardj40c80262006-02-08 19:30:46 +00006164 assign( frD, qop( Iop_MSubF64r32, rm,
6165 mkexpr(frA), mkexpr(frC), mkexpr(frB) ));
sewardjb183b852006-02-03 16:08:03 +00006166 break;
sewardje14bb9f2005-07-22 09:39:02 +00006167
6168 case 0x1D: // fmadds (Floating Mult-Add Single, PPC32 p409)
cerion5b2325f2005-12-23 00:55:09 +00006169 DIP("fmadds%s fr%u,fr%u,fr%u,fr%u\n", flag_rC ? ".":"",
sewardje14bb9f2005-07-22 09:39:02 +00006170 frD_addr, frA_addr, frC_addr, frB_addr);
sewardj40c80262006-02-08 19:30:46 +00006171 assign( frD, qop( Iop_MAddF64r32, rm,
6172 mkexpr(frA), mkexpr(frC), mkexpr(frB) ));
sewardje14bb9f2005-07-22 09:39:02 +00006173 break;
6174
6175 case 0x1E: // fnmsubs (Float Neg Mult-Subtr Single, PPC32 p420)
cerion5b2325f2005-12-23 00:55:09 +00006176 DIP("fnmsubs%s fr%u,fr%u,fr%u,fr%u\n", flag_rC ? ".":"",
sewardje14bb9f2005-07-22 09:39:02 +00006177 frD_addr, frA_addr, frC_addr, frB_addr);
sewardjb183b852006-02-03 16:08:03 +00006178 assign( frD, unop( Iop_NegF64,
sewardj40c80262006-02-08 19:30:46 +00006179 qop( Iop_MSubF64r32, rm,
6180 mkexpr(frA), mkexpr(frC), mkexpr(frB) )));
sewardje14bb9f2005-07-22 09:39:02 +00006181 break;
6182
6183 case 0x1F: // fnmadds (Floating Negative Multiply-Add Single, PPC32 p418)
cerion5b2325f2005-12-23 00:55:09 +00006184 DIP("fnmadds%s fr%u,fr%u,fr%u,fr%u\n", flag_rC ? ".":"",
sewardje14bb9f2005-07-22 09:39:02 +00006185 frD_addr, frA_addr, frC_addr, frB_addr);
sewardjb183b852006-02-03 16:08:03 +00006186 assign( frD, unop( Iop_NegF64,
sewardj40c80262006-02-08 19:30:46 +00006187 qop( Iop_MAddF64r32, rm,
6188 mkexpr(frA), mkexpr(frC), mkexpr(frB) )));
sewardje14bb9f2005-07-22 09:39:02 +00006189 break;
6190
6191 default:
cerion5b2325f2005-12-23 00:55:09 +00006192 vex_printf("dis_fp_multadd(ppc)(3B: opc2)\n");
sewardje14bb9f2005-07-22 09:39:02 +00006193 return False;
6194 }
6195 break;
6196
6197 case 0x3F:
6198 switch (opc2) {
cerion5b2325f2005-12-23 00:55:09 +00006199 case 0x1C: // fmsub (Float Mult-Sub (Dbl Precision), PPC32 p411)
6200 DIP("fmsub%s fr%u,fr%u,fr%u,fr%u\n", flag_rC ? ".":"",
sewardje14bb9f2005-07-22 09:39:02 +00006201 frD_addr, frA_addr, frC_addr, frB_addr);
sewardj40c80262006-02-08 19:30:46 +00006202 assign( frD, qop( Iop_MSubF64, rm,
6203 mkexpr(frA), mkexpr(frC), mkexpr(frB) ));
sewardje14bb9f2005-07-22 09:39:02 +00006204 break;
6205
cerion5b2325f2005-12-23 00:55:09 +00006206 case 0x1D: // fmadd (Float Mult-Add (Dbl Precision), PPC32 p408)
6207 DIP("fmadd%s fr%u,fr%u,fr%u,fr%u\n", flag_rC ? ".":"",
sewardje14bb9f2005-07-22 09:39:02 +00006208 frD_addr, frA_addr, frC_addr, frB_addr);
sewardj40c80262006-02-08 19:30:46 +00006209 assign( frD, qop( Iop_MAddF64, rm,
6210 mkexpr(frA), mkexpr(frC), mkexpr(frB) ));
sewardje14bb9f2005-07-22 09:39:02 +00006211 break;
6212
cerion5b2325f2005-12-23 00:55:09 +00006213 case 0x1E: // fnmsub (Float Neg Mult-Subtr (Dbl Precision), PPC32 p419)
6214 DIP("fnmsub%s fr%u,fr%u,fr%u,fr%u\n", flag_rC ? ".":"",
sewardj0e2cc672005-07-29 21:58:51 +00006215 frD_addr, frA_addr, frC_addr, frB_addr);
6216 assign( frD, unop( Iop_NegF64,
sewardj40c80262006-02-08 19:30:46 +00006217 qop( Iop_MSubF64, rm,
6218 mkexpr(frA), mkexpr(frC), mkexpr(frB) )));
sewardj0e2cc672005-07-29 21:58:51 +00006219 break;
6220
cerion5b2325f2005-12-23 00:55:09 +00006221 case 0x1F: // fnmadd (Float Neg Mult-Add (Dbl Precision), PPC32 p417)
6222 DIP("fnmadd%s fr%u,fr%u,fr%u,fr%u\n", flag_rC ? ".":"",
sewardj0e2cc672005-07-29 21:58:51 +00006223 frD_addr, frA_addr, frC_addr, frB_addr);
6224 assign( frD, unop( Iop_NegF64,
sewardj40c80262006-02-08 19:30:46 +00006225 qop( Iop_MAddF64, rm,
6226 mkexpr(frA), mkexpr(frC), mkexpr(frB) )));
sewardj0e2cc672005-07-29 21:58:51 +00006227 break;
sewardje14bb9f2005-07-22 09:39:02 +00006228
6229 default:
cerion5b2325f2005-12-23 00:55:09 +00006230 vex_printf("dis_fp_multadd(ppc)(3F: opc2)\n");
sewardje14bb9f2005-07-22 09:39:02 +00006231 return False;
6232 }
6233 break;
6234
6235 default:
cerion5b2325f2005-12-23 00:55:09 +00006236 vex_printf("dis_fp_multadd(ppc)(opc1)\n");
sewardje14bb9f2005-07-22 09:39:02 +00006237 return False;
6238 }
6239
6240 putFReg( frD_addr, mkexpr(frD) );
sewardjb183b852006-02-03 16:08:03 +00006241
6242 if (set_FPRF) {
6243 // XXX XXX XXX FIXME
6244 // set FPRF from frD
6245 }
6246
6247 if (flag_rC && clear_CR1) {
6248 putCR321( 1, mkU8(0) );
6249 putCR0( 1, mkU8(0) );
6250 }
6251
sewardje14bb9f2005-07-22 09:39:02 +00006252 return True;
6253}
6254
6255
6256
6257/*
6258 Floating Point Compare Instructions
6259*/
6260static Bool dis_fp_cmp ( UInt theInstr )
6261{
6262 /* X-Form */
cerion76de5cf2005-11-18 18:25:12 +00006263 UChar opc1 = ifieldOPC(theInstr);
6264 UChar crfD = toUChar( IFIELD( theInstr, 23, 3 ) );
6265 UChar b21to22 = toUChar( IFIELD( theInstr, 21, 2 ) );
6266 UChar frA_addr = ifieldRegA(theInstr);
6267 UChar frB_addr = ifieldRegB(theInstr);
6268 UInt opc2 = ifieldOPClo10(theInstr);
6269 UChar b0 = ifieldBIT0(theInstr);
sewardje14bb9f2005-07-22 09:39:02 +00006270
6271 IRTemp ccIR = newTemp(Ity_I32);
6272 IRTemp ccPPC32 = newTemp(Ity_I32);
6273
sewardje14bb9f2005-07-22 09:39:02 +00006274 IRTemp frA = newTemp(Ity_F64);
6275 IRTemp frB = newTemp(Ity_F64);
sewardje14bb9f2005-07-22 09:39:02 +00006276
6277 if (opc1 != 0x3F || b21to22 != 0 || b0 != 0) {
cerion5b2325f2005-12-23 00:55:09 +00006278 vex_printf("dis_fp_cmp(ppc)(instr)\n");
sewardje14bb9f2005-07-22 09:39:02 +00006279 return False;
6280 }
6281
6282 assign( frA, getFReg(frA_addr));
6283 assign( frB, getFReg(frB_addr));
6284
6285 assign( ccIR, binop(Iop_CmpF64, mkexpr(frA), mkexpr(frB)) );
6286
6287 /* Map compare result from IR to PPC32 */
6288 /*
6289 FP cmp result | PPC | IR
6290 --------------------------
6291 UN | 0x1 | 0x45
6292 EQ | 0x2 | 0x40
6293 GT | 0x4 | 0x00
6294 LT | 0x8 | 0x01
6295 */
6296
sewardjb183b852006-02-03 16:08:03 +00006297 // ccPPC32 = Shl(1, (~(ccIR>>5) & 2)
6298 // | ((ccIR ^ (ccIR>>6)) & 1)
sewardje14bb9f2005-07-22 09:39:02 +00006299 assign(
6300 ccPPC32,
sewardjb183b852006-02-03 16:08:03 +00006301 binop(
6302 Iop_Shl32,
6303 mkU32(1),
6304 unop(
6305 Iop_32to8,
6306 binop(
6307 Iop_Or32,
6308 binop(
6309 Iop_And32,
6310 unop(
6311 Iop_Not32,
6312 binop(Iop_Shr32, mkexpr(ccIR), mkU8(5))
6313 ),
6314 mkU32(2)
6315 ),
6316 binop(
6317 Iop_And32,
6318 binop(
6319 Iop_Xor32,
6320 mkexpr(ccIR),
6321 binop(Iop_Shr32, mkexpr(ccIR), mkU8(6))
6322 ),
6323 mkU32(1)
6324 )
6325 )
6326 )
6327 )
sewardje14bb9f2005-07-22 09:39:02 +00006328 );
6329
ceriond953ebb2005-11-29 13:27:20 +00006330 putGST_field( PPC_GST_CR, mkexpr(ccPPC32), crfD );
sewardje14bb9f2005-07-22 09:39:02 +00006331
cerionedf7fc52005-11-18 20:57:41 +00006332 /* CAB: TODO?: Support writing cc to FPSCR->FPCC ?
ceriond953ebb2005-11-29 13:27:20 +00006333 putGST_field( PPC_GST_FPSCR, mkexpr(ccPPC32), 4 );
cerionedf7fc52005-11-18 20:57:41 +00006334 */
sewardjb183b852006-02-03 16:08:03 +00006335 // XXX XXX XXX FIXME
6336 // Also write the result into FPRF (it's not entirely clear how)
sewardje14bb9f2005-07-22 09:39:02 +00006337
cerionedf7fc52005-11-18 20:57:41 +00006338 /* Note: Differences between fcmpu and fcmpo are only in exception
6339 flag settings, which aren't supported anyway. */
sewardje14bb9f2005-07-22 09:39:02 +00006340 switch (opc2) {
ceriond953ebb2005-11-29 13:27:20 +00006341 case 0x000: // fcmpu (Floating Compare Unordered, PPC32 p403)
6342 DIP("fcmpu crf%d,fr%u,fr%u\n", crfD, frA_addr, frB_addr);
6343 break;
6344 case 0x020: // fcmpo (Floating Compare Ordered, PPC32 p402)
6345 DIP("fcmpo crf%d,fr%u,fr%u\n", crfD, frA_addr, frB_addr);
6346 break;
6347 default:
cerion5b2325f2005-12-23 00:55:09 +00006348 vex_printf("dis_fp_cmp(ppc)(opc2)\n");
ceriond953ebb2005-11-29 13:27:20 +00006349 return False;
sewardje14bb9f2005-07-22 09:39:02 +00006350 }
6351 return True;
6352}
6353
6354
6355
6356/*
6357 Floating Point Rounding/Conversion Instructions
6358*/
6359static Bool dis_fp_round ( UInt theInstr )
6360{
6361 /* X-Form */
cerion76de5cf2005-11-18 18:25:12 +00006362 UChar opc1 = ifieldOPC(theInstr);
6363 UChar frD_addr = ifieldRegDS(theInstr);
6364 UChar b16to20 = ifieldRegA(theInstr);
6365 UChar frB_addr = ifieldRegB(theInstr);
6366 UInt opc2 = ifieldOPClo10(theInstr);
6367 UChar flag_rC = ifieldBIT0(theInstr);
sewardje14bb9f2005-07-22 09:39:02 +00006368
sewardjb183b852006-02-03 16:08:03 +00006369 IRTemp frD = newTemp(Ity_F64);
6370 IRTemp frB = newTemp(Ity_F64);
6371 IRTemp r_tmp32 = newTemp(Ity_I32);
6372 IRTemp r_tmp64 = newTemp(Ity_I64);
6373 IRExpr* rm = get_IR_roundingmode();
sewardje14bb9f2005-07-22 09:39:02 +00006374
sewardjb183b852006-02-03 16:08:03 +00006375 /* By default, we will examine the results of the operation and set
6376 fpscr[FPRF] accordingly. */
6377 Bool set_FPRF = True;
6378
6379 /* By default, if flag_RC is set, we will clear cr1 after the
6380 operation. In reality we should set cr1 to indicate the
6381 exception status of the operation, but since we're not
6382 simulating exceptions, the exception status will appear to be
6383 zero. Hence cr1 should be cleared if this is a . form insn. */
6384 Bool clear_CR1 = True;
6385
sewardje14bb9f2005-07-22 09:39:02 +00006386 if (opc1 != 0x3F || b16to20 != 0) {
cerion5b2325f2005-12-23 00:55:09 +00006387 vex_printf("dis_fp_round(ppc)(instr)\n");
sewardje14bb9f2005-07-22 09:39:02 +00006388 return False;
6389 }
6390
6391 assign( frB, getFReg(frB_addr));
6392
sewardje14bb9f2005-07-22 09:39:02 +00006393 switch (opc2) {
cerionf0de28c2005-12-13 20:21:11 +00006394 case 0x00C: // frsp (Float Round to Single, PPC32 p423)
cerion5b2325f2005-12-23 00:55:09 +00006395 DIP("frsp%s fr%u,fr%u\n", flag_rC ? ".":"", frD_addr, frB_addr);
sewardjb183b852006-02-03 16:08:03 +00006396 assign( frD, binop( Iop_RoundF64toF32, rm, mkexpr(frB) ));
ceriond953ebb2005-11-29 13:27:20 +00006397 break;
6398
cerionf0de28c2005-12-13 20:21:11 +00006399 case 0x00E: // fctiw (Float Conv to Int, PPC32 p404)
cerion5b2325f2005-12-23 00:55:09 +00006400 DIP("fctiw%s fr%u,fr%u\n", flag_rC ? ".":"", frD_addr, frB_addr);
6401 assign( r_tmp32,
sewardjb183b852006-02-03 16:08:03 +00006402 binop(Iop_F64toI32, rm, mkexpr(frB)) );
ceriond953ebb2005-11-29 13:27:20 +00006403 assign( frD, unop( Iop_ReinterpI64asF64,
cerion07b07a92005-12-22 14:32:35 +00006404 unop( Iop_32Uto64, mkexpr(r_tmp32))));
sewardjb183b852006-02-03 16:08:03 +00006405 /* FPRF is undefined after fctiw. Leave unchanged. */
6406 set_FPRF = False;
ceriond953ebb2005-11-29 13:27:20 +00006407 break;
6408
cerionf0de28c2005-12-13 20:21:11 +00006409 case 0x00F: // fctiwz (Float Conv to Int, Round to Zero, PPC32 p405)
cerion5b2325f2005-12-23 00:55:09 +00006410 DIP("fctiwz%s fr%u,fr%u\n", flag_rC ? ".":"", frD_addr, frB_addr);
sewardjb183b852006-02-03 16:08:03 +00006411 assign( r_tmp32,
6412 binop(Iop_F64toI32, mkU32(Irrm_ZERO), mkexpr(frB) ));
ceriond953ebb2005-11-29 13:27:20 +00006413 assign( frD, unop( Iop_ReinterpI64asF64,
cerion07b07a92005-12-22 14:32:35 +00006414 unop( Iop_32Uto64, mkexpr(r_tmp32))));
sewardjb183b852006-02-03 16:08:03 +00006415 /* FPRF is undefined after fctiwz. Leave unchanged. */
6416 set_FPRF = False;
ceriond953ebb2005-11-29 13:27:20 +00006417 break;
cerionf0de28c2005-12-13 20:21:11 +00006418
cerion5b2325f2005-12-23 00:55:09 +00006419 case 0x32E: // fctid (Float Conv to Int DWord, PPC64 p437)
6420 DIP("fctid%s fr%u,fr%u\n", flag_rC ? ".":"", frD_addr, frB_addr);
6421 assign( r_tmp64,
sewardjb183b852006-02-03 16:08:03 +00006422 binop(Iop_F64toI64, rm, mkexpr(frB)) );
cerion07b07a92005-12-22 14:32:35 +00006423 assign( frD, unop( Iop_ReinterpI64asF64, mkexpr(r_tmp64)) );
sewardjb183b852006-02-03 16:08:03 +00006424 /* FPRF is undefined after fctid. Leave unchanged. */
6425 set_FPRF = False;
cerion07b07a92005-12-22 14:32:35 +00006426 break;
cerionf0de28c2005-12-13 20:21:11 +00006427
cerion5b2325f2005-12-23 00:55:09 +00006428 case 0x32F: // fctidz (Float Conv to Int DWord, Round to Zero, PPC64 p437)
6429 DIP("fctidz%s fr%u,fr%u\n", flag_rC ? ".":"", frD_addr, frB_addr);
sewardjb183b852006-02-03 16:08:03 +00006430 assign( r_tmp64,
6431 binop(Iop_F64toI64, mkU32(Irrm_ZERO), mkexpr(frB)) );
cerion07b07a92005-12-22 14:32:35 +00006432 assign( frD, unop( Iop_ReinterpI64asF64, mkexpr(r_tmp64)) );
sewardjb183b852006-02-03 16:08:03 +00006433 /* FPRF is undefined after fctidz. Leave unchanged. */
6434 set_FPRF = False;
cerion07b07a92005-12-22 14:32:35 +00006435 break;
cerionf0de28c2005-12-13 20:21:11 +00006436
cerion5b2325f2005-12-23 00:55:09 +00006437 case 0x34E: // fcfid (Float Conv from Int DWord, PPC64 p434)
6438 DIP("fcfid%s fr%u,fr%u\n", flag_rC ? ".":"", frD_addr, frB_addr);
cerion07b07a92005-12-22 14:32:35 +00006439 assign( r_tmp64, unop( Iop_ReinterpF64asI64, mkexpr(frB)) );
sewardjb183b852006-02-03 16:08:03 +00006440 assign( frD,
6441 binop(Iop_I64toF64, rm, mkexpr(r_tmp64)) );
cerion07b07a92005-12-22 14:32:35 +00006442 break;
cerionf0de28c2005-12-13 20:21:11 +00006443
ceriond953ebb2005-11-29 13:27:20 +00006444 default:
cerion5b2325f2005-12-23 00:55:09 +00006445 vex_printf("dis_fp_round(ppc)(opc2)\n");
ceriond953ebb2005-11-29 13:27:20 +00006446 return False;
sewardje14bb9f2005-07-22 09:39:02 +00006447 }
6448
6449 putFReg( frD_addr, mkexpr(frD) );
sewardjb183b852006-02-03 16:08:03 +00006450
6451 if (set_FPRF) {
6452 // XXX XXX XXX FIXME
6453 // set FPRF from frD
6454 }
6455
6456 if (flag_rC && clear_CR1) {
6457 putCR321( 1, mkU8(0) );
6458 putCR0( 1, mkU8(0) );
6459 }
6460
sewardje14bb9f2005-07-22 09:39:02 +00006461 return True;
6462}
6463
6464
6465
6466/*
6467 Floating Point Move Instructions
6468*/
6469static Bool dis_fp_move ( UInt theInstr )
6470{
6471 /* X-Form */
cerion76de5cf2005-11-18 18:25:12 +00006472 UChar opc1 = ifieldOPC(theInstr);
6473 UChar frD_addr = ifieldRegDS(theInstr);
6474 UChar b16to20 = ifieldRegA(theInstr);
6475 UChar frB_addr = ifieldRegB(theInstr);
6476 UInt opc2 = ifieldOPClo10(theInstr);
6477 UChar flag_rC = ifieldBIT0(theInstr);
sewardje14bb9f2005-07-22 09:39:02 +00006478
6479 IRTemp frD = newTemp(Ity_F64);
6480 IRTemp frB = newTemp(Ity_F64);
6481
6482 if (opc1 != 0x3F || b16to20 != 0) {
cerion5b2325f2005-12-23 00:55:09 +00006483 vex_printf("dis_fp_move(ppc)(instr)\n");
sewardje14bb9f2005-07-22 09:39:02 +00006484 return False;
6485 }
6486
6487 assign( frB, getFReg(frB_addr));
6488
sewardje14bb9f2005-07-22 09:39:02 +00006489 switch (opc2) {
ceriond953ebb2005-11-29 13:27:20 +00006490 case 0x028: // fneg (Floating Negate, PPC32 p416)
cerion5b2325f2005-12-23 00:55:09 +00006491 DIP("fneg%s fr%u,fr%u\n", flag_rC ? ".":"", frD_addr, frB_addr);
ceriond953ebb2005-11-29 13:27:20 +00006492 assign( frD, unop( Iop_NegF64, mkexpr(frB) ));
6493 break;
6494
6495 case 0x048: // fmr (Floating Move Register, PPC32 p410)
cerion5b2325f2005-12-23 00:55:09 +00006496 DIP("fmr%s fr%u,fr%u\n", flag_rC ? ".":"", frD_addr, frB_addr);
ceriond953ebb2005-11-29 13:27:20 +00006497 assign( frD, mkexpr(frB) );
6498 break;
6499
6500 case 0x088: // fnabs (Floating Negative Absolute Value, PPC32 p415)
cerion5b2325f2005-12-23 00:55:09 +00006501 DIP("fnabs%s fr%u,fr%u\n", flag_rC ? ".":"", frD_addr, frB_addr);
ceriond953ebb2005-11-29 13:27:20 +00006502 assign( frD, unop( Iop_NegF64, unop( Iop_AbsF64, mkexpr(frB) )));
6503 break;
6504
6505 case 0x108: // fabs (Floating Absolute Value, PPC32 p399)
cerion5b2325f2005-12-23 00:55:09 +00006506 DIP("fabs%s fr%u,fr%u\n", flag_rC ? ".":"", frD_addr, frB_addr);
ceriond953ebb2005-11-29 13:27:20 +00006507 assign( frD, unop( Iop_AbsF64, mkexpr(frB) ));
6508 break;
6509
6510 default:
cerion5b2325f2005-12-23 00:55:09 +00006511 vex_printf("dis_fp_move(ppc)(opc2)\n");
ceriond953ebb2005-11-29 13:27:20 +00006512 return False;
sewardje14bb9f2005-07-22 09:39:02 +00006513 }
6514
6515 putFReg( frD_addr, mkexpr(frD) );
sewardjb183b852006-02-03 16:08:03 +00006516
6517 /* None of these change FPRF. cr1 is set in the usual way though,
6518 if flag_rC is set. */
6519
6520 if (flag_rC) {
6521 putCR321( 1, mkU8(0) );
6522 putCR0( 1, mkU8(0) );
6523 }
6524
sewardje14bb9f2005-07-22 09:39:02 +00006525 return True;
6526}
6527
6528
6529
6530/*
6531 Floating Point Status/Control Register Instructions
6532*/
6533static Bool dis_fp_scr ( UInt theInstr )
6534{
cerion76de5cf2005-11-18 18:25:12 +00006535 /* Many forms - see each switch case */
6536 UChar opc1 = ifieldOPC(theInstr);
6537 UInt opc2 = ifieldOPClo10(theInstr);
6538 UChar flag_rC = ifieldBIT0(theInstr);
sewardje14bb9f2005-07-22 09:39:02 +00006539
6540 if (opc1 != 0x3F) {
cerion5b2325f2005-12-23 00:55:09 +00006541 vex_printf("dis_fp_scr(ppc)(instr)\n");
sewardje14bb9f2005-07-22 09:39:02 +00006542 return False;
6543 }
6544
6545 switch (opc2) {
cerion3ea49ee2006-01-04 10:53:00 +00006546 case 0x026: { // mtfsb1 (Move to FPSCR Bit 1, PPC32 p479)
6547 // Bit crbD of the FPSCR is set.
6548 UChar crbD = ifieldRegDS(theInstr);
6549 UInt b11to20 = IFIELD(theInstr, 11, 10);
6550
6551 if (b11to20 != 0) {
6552 vex_printf("dis_fp_scr(ppc)(instr,mtfsb1)\n");
6553 return False;
6554 }
6555 DIP("mtfsb1%s crb%d \n", flag_rC ? ".":"", crbD);
6556 putGST_masked( PPC_GST_FPSCR, mkU32(1<<(31-crbD)), 1<<(31-crbD) );
6557 break;
6558 }
6559
sewardj496b88f2006-10-04 17:46:11 +00006560 case 0x040: { // mcrfs (Move to Condition Register from FPSCR, PPC32 p465)
6561 UChar crfD = toUChar( IFIELD( theInstr, 23, 3 ) );
6562 UChar b21to22 = toUChar( IFIELD( theInstr, 21, 2 ) );
6563 UChar crfS = toUChar( IFIELD( theInstr, 18, 3 ) );
6564 UChar b11to17 = toUChar( IFIELD( theInstr, 11, 7 ) );
6565 IRTemp tmp = newTemp(Ity_I32);
6566 IRExpr* fpscr_all;
6567 if (b21to22 != 0 || b11to17 != 0 || flag_rC != 0) {
6568 vex_printf("dis_fp_scr(ppc)(instr,mcrfs)\n");
6569 return False;
6570 }
6571 DIP("mcrfs crf%d,crf%d\n", crfD, crfS);
6572 vassert(crfD < 8);
6573 vassert(crfS < 8);
6574 fpscr_all = getGST_masked( PPC_GST_FPSCR, MASK_FPSCR_RN );
6575 assign( tmp, binop(Iop_And32,
6576 binop(Iop_Shr32,fpscr_all,mkU8(4 * (7-crfS))),
6577 mkU32(0xF)) );
6578 putGST_field( PPC_GST_CR, mkexpr(tmp), crfD );
6579 break;
6580 }
sewardj0e2cc672005-07-29 21:58:51 +00006581
6582 case 0x046: { // mtfsb0 (Move to FPSCR Bit 0, PPC32 p478)
6583 // Bit crbD of the FPSCR is cleared.
cerion76de5cf2005-11-18 18:25:12 +00006584 UChar crbD = ifieldRegDS(theInstr);
6585 UInt b11to20 = IFIELD(theInstr, 11, 10);
sewardj0e2cc672005-07-29 21:58:51 +00006586
6587 if (b11to20 != 0) {
cerion5b2325f2005-12-23 00:55:09 +00006588 vex_printf("dis_fp_scr(ppc)(instr,mtfsb0)\n");
sewardj0e2cc672005-07-29 21:58:51 +00006589 return False;
6590 }
cerion5b2325f2005-12-23 00:55:09 +00006591 DIP("mtfsb0%s crb%d\n", flag_rC ? ".":"", crbD);
ceriond953ebb2005-11-29 13:27:20 +00006592 putGST_masked( PPC_GST_FPSCR, mkU32(0), 1<<(31-crbD) );
sewardj0e2cc672005-07-29 21:58:51 +00006593 break;
6594 }
6595
6596 case 0x086: { // mtfsfi (Move to FPSCR Field Immediate, PPC32 p481)
cerion76de5cf2005-11-18 18:25:12 +00006597 UChar crfD = toUChar( IFIELD( theInstr, 23, 3 ) );
6598 UChar b16to22 = toUChar( IFIELD( theInstr, 16, 7 ) );
6599 UChar IMM = toUChar( IFIELD( theInstr, 12, 4 ) );
6600 UChar b11 = toUChar( IFIELD( theInstr, 11, 1 ) );
sewardj0e2cc672005-07-29 21:58:51 +00006601
6602 if (b16to22 != 0 || b11 != 0) {
cerion5b2325f2005-12-23 00:55:09 +00006603 vex_printf("dis_fp_scr(ppc)(instr,mtfsfi)\n");
sewardj0e2cc672005-07-29 21:58:51 +00006604 return False;
6605 }
cerion5b2325f2005-12-23 00:55:09 +00006606 DIP("mtfsfi%s crf%d,%d\n", flag_rC ? ".":"", crfD, IMM);
ceriond953ebb2005-11-29 13:27:20 +00006607 putGST_field( PPC_GST_FPSCR, mkU32(IMM), crfD );
sewardj0e2cc672005-07-29 21:58:51 +00006608 break;
6609 }
sewardje14bb9f2005-07-22 09:39:02 +00006610
6611 case 0x247: { // mffs (Move from FPSCR, PPC32 p468)
sewardj496b88f2006-10-04 17:46:11 +00006612 UChar frD_addr = ifieldRegDS(theInstr);
6613 UInt b11to20 = IFIELD(theInstr, 11, 10);
6614 IRExpr* fpscr_all = getGST_masked( PPC_GST_FPSCR, MASK_FPSCR_RN );
sewardje14bb9f2005-07-22 09:39:02 +00006615
6616 if (b11to20 != 0) {
cerion5b2325f2005-12-23 00:55:09 +00006617 vex_printf("dis_fp_scr(ppc)(instr,mffs)\n");
sewardje14bb9f2005-07-22 09:39:02 +00006618 return False;
6619 }
cerion5b2325f2005-12-23 00:55:09 +00006620 DIP("mffs%s fr%u\n", flag_rC ? ".":"", frD_addr);
6621 putFReg( frD_addr,
6622 unop( Iop_ReinterpI64asF64,
sewardj496b88f2006-10-04 17:46:11 +00006623 unop( Iop_32Uto64, fpscr_all )));
sewardje14bb9f2005-07-22 09:39:02 +00006624 break;
6625 }
6626
6627 case 0x2C7: { // mtfsf (Move to FPSCR Fields, PPC32 p480)
cerion76de5cf2005-11-18 18:25:12 +00006628 UChar b25 = toUChar( IFIELD(theInstr, 25, 1) );
6629 UChar FM = toUChar( IFIELD(theInstr, 17, 8) );
6630 UChar b16 = toUChar( IFIELD(theInstr, 16, 1) );
6631 UChar frB_addr = ifieldRegB(theInstr);
6632 IRTemp frB = newTemp(Ity_F64);
sewardje14bb9f2005-07-22 09:39:02 +00006633 IRTemp rB_32 = newTemp(Ity_I32);
cerion76de5cf2005-11-18 18:25:12 +00006634 Int i, mask;
sewardje14bb9f2005-07-22 09:39:02 +00006635
6636 if (b25 != 0 || b16 != 0) {
cerion5b2325f2005-12-23 00:55:09 +00006637 vex_printf("dis_fp_scr(ppc)(instr,mtfsf)\n");
sewardje14bb9f2005-07-22 09:39:02 +00006638 return False;
6639 }
cerion5b2325f2005-12-23 00:55:09 +00006640 DIP("mtfsf%s %d,fr%u\n", flag_rC ? ".":"", FM, frB_addr);
sewardje14bb9f2005-07-22 09:39:02 +00006641 assign( frB, getFReg(frB_addr));
6642 assign( rB_32, unop( Iop_64to32,
6643 unop( Iop_ReinterpF64asI64, mkexpr(frB) )));
6644 // Build 32bit mask from FM:
cerion76de5cf2005-11-18 18:25:12 +00006645 mask = 0;
sewardje14bb9f2005-07-22 09:39:02 +00006646 for (i=0; i<8; i++) {
6647 if ((FM & (1<<(7-i))) == 1) {
6648 mask |= 0xF << (7-i);
6649 }
6650 }
ceriond953ebb2005-11-29 13:27:20 +00006651 putGST_masked( PPC_GST_FPSCR, mkexpr(rB_32), mask );
sewardje14bb9f2005-07-22 09:39:02 +00006652 break;
6653 }
6654
6655 default:
cerion5b2325f2005-12-23 00:55:09 +00006656 vex_printf("dis_fp_scr(ppc)(opc2)\n");
sewardje14bb9f2005-07-22 09:39:02 +00006657 return False;
6658 }
6659 return True;
6660}
6661
6662
6663
cerion32aad402005-09-10 12:02:24 +00006664/*------------------------------------------------------------*/
6665/*--- AltiVec Instruction Translation ---*/
6666/*------------------------------------------------------------*/
6667
6668/*
6669 Altivec Cache Control Instructions (Data Streams)
6670*/
6671static Bool dis_av_datastream ( UInt theInstr )
6672{
cerion76de5cf2005-11-18 18:25:12 +00006673 /* X-Form */
6674 UChar opc1 = ifieldOPC(theInstr);
6675 UChar flag_T = toUChar( IFIELD( theInstr, 25, 1 ) );
6676 UChar flag_A = flag_T;
6677 UChar b23to24 = toUChar( IFIELD( theInstr, 23, 2 ) );
6678 UChar STRM = toUChar( IFIELD( theInstr, 21, 2 ) );
6679 UChar rA_addr = ifieldRegA(theInstr);
6680 UChar rB_addr = ifieldRegB(theInstr);
6681 UInt opc2 = ifieldOPClo10(theInstr);
6682 UChar b0 = ifieldBIT0(theInstr);
cerion32aad402005-09-10 12:02:24 +00006683
6684 if (opc1 != 0x1F || b23to24 != 0 || b0 != 0) {
cerion5b2325f2005-12-23 00:55:09 +00006685 vex_printf("dis_av_datastream(ppc)(instr)\n");
cerion32aad402005-09-10 12:02:24 +00006686 return False;
6687 }
6688
6689 switch (opc2) {
6690 case 0x156: // dst (Data Stream Touch, AV p115)
cerion5b2325f2005-12-23 00:55:09 +00006691 DIP("dst%s r%u,r%u,%d\n", flag_T ? "t" : "",
6692 rA_addr, rB_addr, STRM);
cerion32aad402005-09-10 12:02:24 +00006693 DIP(" => not implemented\n");
6694 return False;
6695
6696 case 0x176: // dstst (Data Stream Touch for Store, AV p117)
cerion5b2325f2005-12-23 00:55:09 +00006697 DIP("dstst%s r%u,r%u,%d\n", flag_T ? "t" : "",
6698 rA_addr, rB_addr, STRM);
cerion32aad402005-09-10 12:02:24 +00006699 DIP(" => not implemented\n");
6700 return False;
6701
6702 case 0x336: // dss (Data Stream Stop, AV p114)
6703 if (rA_addr != 0 || rB_addr != 0) {
cerion5b2325f2005-12-23 00:55:09 +00006704 vex_printf("dis_av_datastream(ppc)(opc2,dst)\n");
cerion32aad402005-09-10 12:02:24 +00006705 return False;
6706 }
6707 if (flag_A == 0) {
6708 DIP("dss %d\n", STRM);
6709 DIP(" => not implemented\n");
6710 } else {
6711 DIP("dssall\n");
6712 DIP(" => not implemented\n");
6713 }
6714 return False;
6715
6716 default:
cerion5b2325f2005-12-23 00:55:09 +00006717 vex_printf("dis_av_datastream(ppc)(opc2)\n");
cerion32aad402005-09-10 12:02:24 +00006718 return False;
6719 }
6720 return True;
6721}
6722
6723/*
6724 AltiVec Processor Control Instructions
6725*/
6726static Bool dis_av_procctl ( UInt theInstr )
6727{
cerion76de5cf2005-11-18 18:25:12 +00006728 /* VX-Form */
6729 UChar opc1 = ifieldOPC(theInstr);
6730 UChar vD_addr = ifieldRegDS(theInstr);
6731 UChar vA_addr = ifieldRegA(theInstr);
6732 UChar vB_addr = ifieldRegB(theInstr);
6733 UInt opc2 = IFIELD( theInstr, 0, 11 );
cerion32aad402005-09-10 12:02:24 +00006734
6735 if (opc1 != 0x4) {
cerion5b2325f2005-12-23 00:55:09 +00006736 vex_printf("dis_av_procctl(ppc)(instr)\n");
cerion32aad402005-09-10 12:02:24 +00006737 return False;
6738 }
6739
6740 switch (opc2) {
6741 case 0x604: // mfvscr (Move from VSCR, AV p129)
6742 if (vA_addr != 0 || vB_addr != 0) {
cerion5b2325f2005-12-23 00:55:09 +00006743 vex_printf("dis_av_procctl(ppc)(opc2,dst)\n");
cerion32aad402005-09-10 12:02:24 +00006744 return False;
6745 }
6746 DIP("mfvscr v%d\n", vD_addr);
ceriond953ebb2005-11-29 13:27:20 +00006747 putVReg( vD_addr, unop(Iop_32UtoV128, getGST( PPC_GST_VSCR )) );
cerion225a0342005-09-12 20:49:09 +00006748 break;
cerion32aad402005-09-10 12:02:24 +00006749
cerion225a0342005-09-12 20:49:09 +00006750 case 0x644: { // mtvscr (Move to VSCR, AV p130)
sewardj197bd172005-10-12 11:34:33 +00006751 IRTemp vB = newTemp(Ity_V128);
cerion32aad402005-09-10 12:02:24 +00006752 if (vD_addr != 0 || vA_addr != 0) {
cerion5b2325f2005-12-23 00:55:09 +00006753 vex_printf("dis_av_procctl(ppc)(opc2,dst)\n");
cerion32aad402005-09-10 12:02:24 +00006754 return False;
6755 }
6756 DIP("mtvscr v%d\n", vB_addr);
cerion225a0342005-09-12 20:49:09 +00006757 assign( vB, getVReg(vB_addr));
ceriond953ebb2005-11-29 13:27:20 +00006758 putGST( PPC_GST_VSCR, unop(Iop_V128to32, mkexpr(vB)) );
cerion225a0342005-09-12 20:49:09 +00006759 break;
6760 }
cerion32aad402005-09-10 12:02:24 +00006761 default:
cerion5b2325f2005-12-23 00:55:09 +00006762 vex_printf("dis_av_procctl(ppc)(opc2)\n");
cerion32aad402005-09-10 12:02:24 +00006763 return False;
6764 }
6765 return True;
6766}
ceriona982c052005-06-28 17:23:09 +00006767
6768/*
6769 AltiVec Load Instructions
6770*/
sewardjaca070a2006-10-17 00:28:22 +00006771static Bool dis_av_load ( VexMiscInfo* vmi, UInt theInstr )
ceriona982c052005-06-28 17:23:09 +00006772{
cerion76de5cf2005-11-18 18:25:12 +00006773 /* X-Form */
6774 UChar opc1 = ifieldOPC(theInstr);
6775 UChar vD_addr = ifieldRegDS(theInstr);
6776 UChar rA_addr = ifieldRegA(theInstr);
6777 UChar rB_addr = ifieldRegB(theInstr);
6778 UInt opc2 = ifieldOPClo10(theInstr);
6779 UChar b0 = ifieldBIT0(theInstr);
ceriona982c052005-06-28 17:23:09 +00006780
cerionfb197c42005-12-24 12:32:10 +00006781 IRType ty = mode64 ? Ity_I64 : Ity_I32;
6782 IRTemp EA = newTemp(ty);
6783 IRTemp EA_align16 = newTemp(ty);
cerion2831b002005-11-30 19:55:22 +00006784
ceriona982c052005-06-28 17:23:09 +00006785 if (opc1 != 0x1F || b0 != 0) {
cerion5b2325f2005-12-23 00:55:09 +00006786 vex_printf("dis_av_load(ppc)(instr)\n");
ceriona982c052005-06-28 17:23:09 +00006787 return False;
6788 }
6789
cerionfb197c42005-12-24 12:32:10 +00006790 assign( EA, ea_rAor0_idxd(rA_addr, rB_addr) );
6791 assign( EA_align16, addr_align( mkexpr(EA), 16 ) );
ceriona50fde52005-07-01 21:16:10 +00006792
ceriona982c052005-06-28 17:23:09 +00006793 switch (opc2) {
6794
cerion6f6c6a02005-09-13 18:41:09 +00006795 case 0x006: { // lvsl (Load Vector for Shift Left, AV p123)
cerionfb197c42005-12-24 12:32:10 +00006796 IRDirty* d;
sewardjd1470942005-10-22 02:01:16 +00006797 UInt vD_off = vectorGuestRegOffset(vD_addr);
6798 IRExpr** args = mkIRExprVec_3(
6799 mkU32(vD_off),
cerionfb197c42005-12-24 12:32:10 +00006800 binop(Iop_And32, mkSzNarrow32(ty, mkexpr(EA)),
6801 mkU32(0xF)),
sewardjd1470942005-10-22 02:01:16 +00006802 mkU32(0)/*left*/ );
cerion5b2325f2005-12-23 00:55:09 +00006803 if (!mode64) {
cerion4c4f5ef2006-01-02 14:41:50 +00006804 d = unsafeIRDirty_0_N (
6805 0/*regparms*/,
6806 "ppc32g_dirtyhelper_LVS",
sewardjaca070a2006-10-17 00:28:22 +00006807 fnptr_to_fnentry(vmi, &ppc32g_dirtyhelper_LVS),
cerion4c4f5ef2006-01-02 14:41:50 +00006808 args );
cerion5b2325f2005-12-23 00:55:09 +00006809 } else {
cerion4c4f5ef2006-01-02 14:41:50 +00006810 d = unsafeIRDirty_0_N (
6811 0/*regparms*/,
6812 "ppc64g_dirtyhelper_LVS",
sewardjaca070a2006-10-17 00:28:22 +00006813 fnptr_to_fnentry(vmi, &ppc64g_dirtyhelper_LVS),
cerion4c4f5ef2006-01-02 14:41:50 +00006814 args );
cerion5b2325f2005-12-23 00:55:09 +00006815 }
ceriond953ebb2005-11-29 13:27:20 +00006816 DIP("lvsl v%d,r%u,r%u\n", vD_addr, rA_addr, rB_addr);
cerion6f6c6a02005-09-13 18:41:09 +00006817 /* declare guest state effects */
6818 d->needsBBP = True;
6819 d->nFxState = 1;
6820 d->fxState[0].fx = Ifx_Write;
sewardjd1470942005-10-22 02:01:16 +00006821 d->fxState[0].offset = vD_off;
cerion6f6c6a02005-09-13 18:41:09 +00006822 d->fxState[0].size = sizeof(U128);
cerion32aad402005-09-10 12:02:24 +00006823
cerion6f6c6a02005-09-13 18:41:09 +00006824 /* execute the dirty call, side-effecting guest state */
6825 stmt( IRStmt_Dirty(d) );
6826 break;
6827 }
6828 case 0x026: { // lvsr (Load Vector for Shift Right, AV p125)
cerionfb197c42005-12-24 12:32:10 +00006829 IRDirty* d;
sewardjd1470942005-10-22 02:01:16 +00006830 UInt vD_off = vectorGuestRegOffset(vD_addr);
6831 IRExpr** args = mkIRExprVec_3(
6832 mkU32(vD_off),
cerionfb197c42005-12-24 12:32:10 +00006833 binop(Iop_And32, mkSzNarrow32(ty, mkexpr(EA)),
6834 mkU32(0xF)),
sewardjd1470942005-10-22 02:01:16 +00006835 mkU32(1)/*right*/ );
cerion5b2325f2005-12-23 00:55:09 +00006836 if (!mode64) {
cerion4c4f5ef2006-01-02 14:41:50 +00006837 d = unsafeIRDirty_0_N (
6838 0/*regparms*/,
6839 "ppc32g_dirtyhelper_LVS",
sewardjaca070a2006-10-17 00:28:22 +00006840 fnptr_to_fnentry(vmi, &ppc32g_dirtyhelper_LVS),
cerion4c4f5ef2006-01-02 14:41:50 +00006841 args );
cerion5b2325f2005-12-23 00:55:09 +00006842 } else {
cerion4c4f5ef2006-01-02 14:41:50 +00006843 d = unsafeIRDirty_0_N (
6844 0/*regparms*/,
6845 "ppc64g_dirtyhelper_LVS",
sewardjaca070a2006-10-17 00:28:22 +00006846 fnptr_to_fnentry(vmi, &ppc64g_dirtyhelper_LVS),
cerion4c4f5ef2006-01-02 14:41:50 +00006847 args );
cerion5b2325f2005-12-23 00:55:09 +00006848 }
ceriond953ebb2005-11-29 13:27:20 +00006849 DIP("lvsr v%d,r%u,r%u\n", vD_addr, rA_addr, rB_addr);
cerion6f6c6a02005-09-13 18:41:09 +00006850 /* declare guest state effects */
6851 d->needsBBP = True;
6852 d->nFxState = 1;
6853 d->fxState[0].fx = Ifx_Write;
sewardjd1470942005-10-22 02:01:16 +00006854 d->fxState[0].offset = vD_off;
cerion6f6c6a02005-09-13 18:41:09 +00006855 d->fxState[0].size = sizeof(U128);
cerion32aad402005-09-10 12:02:24 +00006856
cerion6f6c6a02005-09-13 18:41:09 +00006857 /* execute the dirty call, side-effecting guest state */
6858 stmt( IRStmt_Dirty(d) );
6859 break;
6860 }
cerion32aad402005-09-10 12:02:24 +00006861 case 0x007: // lvebx (Load Vector Element Byte Indexed, AV p119)
ceriond953ebb2005-11-29 13:27:20 +00006862 DIP("lvebx v%d,r%u,r%u\n", vD_addr, rA_addr, rB_addr);
cerion61c92742005-09-14 22:59:26 +00006863 /* loads addressed byte into vector[EA[0:3]
6864 since all other destination bytes are undefined,
6865 can simply load entire vector from 16-aligned EA */
cerionfb197c42005-12-24 12:32:10 +00006866 putVReg( vD_addr, loadBE(Ity_V128, mkexpr(EA_align16)) );
cerion61c92742005-09-14 22:59:26 +00006867 break;
cerion32aad402005-09-10 12:02:24 +00006868
6869 case 0x027: // lvehx (Load Vector Element Half Word Indexed, AV p121)
ceriond953ebb2005-11-29 13:27:20 +00006870 DIP("lvehx v%d,r%u,r%u\n", vD_addr, rA_addr, rB_addr);
cerion61c92742005-09-14 22:59:26 +00006871 /* see note for lvebx */
cerionfb197c42005-12-24 12:32:10 +00006872 putVReg( vD_addr, loadBE(Ity_V128, mkexpr(EA_align16)) );
cerion61c92742005-09-14 22:59:26 +00006873 break;
cerion32aad402005-09-10 12:02:24 +00006874
6875 case 0x047: // lvewx (Load Vector Element Word Indexed, AV p122)
ceriond953ebb2005-11-29 13:27:20 +00006876 DIP("lvewx v%d,r%u,r%u\n", vD_addr, rA_addr, rB_addr);
cerion61c92742005-09-14 22:59:26 +00006877 /* see note for lvebx */
cerionfb197c42005-12-24 12:32:10 +00006878 putVReg( vD_addr, loadBE(Ity_V128, mkexpr(EA_align16)) );
cerion61c92742005-09-14 22:59:26 +00006879 break;
ceriona982c052005-06-28 17:23:09 +00006880
6881 case 0x067: // lvx (Load Vector Indexed, AV p127)
ceriond953ebb2005-11-29 13:27:20 +00006882 DIP("lvx v%d,r%u,r%u\n", vD_addr, rA_addr, rB_addr);
cerionfb197c42005-12-24 12:32:10 +00006883 putVReg( vD_addr, loadBE(Ity_V128, mkexpr(EA_align16)) );
ceriona50fde52005-07-01 21:16:10 +00006884 break;
ceriona982c052005-06-28 17:23:09 +00006885
cerion32aad402005-09-10 12:02:24 +00006886 case 0x167: // lvxl (Load Vector Indexed LRU, AV p128)
6887 // XXX: lvxl gives explicit control over cache block replacement
ceriond953ebb2005-11-29 13:27:20 +00006888 DIP("lvxl v%d,r%u,r%u\n", vD_addr, rA_addr, rB_addr);
cerion32aad402005-09-10 12:02:24 +00006889 DIP(" => not implemented\n");
6890 return False;
ceriona982c052005-06-28 17:23:09 +00006891
6892 default:
cerion5b2325f2005-12-23 00:55:09 +00006893 vex_printf("dis_av_load(ppc)(opc2)\n");
ceriona982c052005-06-28 17:23:09 +00006894 return False;
6895 }
6896 return True;
6897}
6898
cerion2831b002005-11-30 19:55:22 +00006899
ceriona982c052005-06-28 17:23:09 +00006900/*
6901 AltiVec Store Instructions
6902*/
6903static Bool dis_av_store ( UInt theInstr )
6904{
cerion76de5cf2005-11-18 18:25:12 +00006905 /* X-Form */
6906 UChar opc1 = ifieldOPC(theInstr);
6907 UChar vS_addr = ifieldRegDS(theInstr);
6908 UChar rA_addr = ifieldRegA(theInstr);
6909 UChar rB_addr = ifieldRegB(theInstr);
6910 UInt opc2 = ifieldOPClo10(theInstr);
6911 UChar b0 = ifieldBIT0(theInstr);
ceriona982c052005-06-28 17:23:09 +00006912
cerion2831b002005-11-30 19:55:22 +00006913 IRType ty = mode64 ? Ity_I64 : Ity_I32;
6914 IRTemp EA = newTemp(ty);
ceriondba87e22006-01-02 15:15:45 +00006915 IRTemp addr_aligned = newTemp(ty);
cerion2831b002005-11-30 19:55:22 +00006916 IRTemp vS = newTemp(Ity_V128);
6917 IRTemp eb = newTemp(Ity_I8);
6918 IRTemp idx = newTemp(Ity_I8);
ceriona982c052005-06-28 17:23:09 +00006919
6920 if (opc1 != 0x1F || b0 != 0) {
cerion5b2325f2005-12-23 00:55:09 +00006921 vex_printf("dis_av_store(ppc)(instr)\n");
ceriona982c052005-06-28 17:23:09 +00006922 return False;
6923 }
6924
ceriond953ebb2005-11-29 13:27:20 +00006925 assign( vS, getVReg(vS_addr));
cerion2831b002005-11-30 19:55:22 +00006926 assign( EA, ea_rAor0_idxd(rA_addr, rB_addr) );
ceriond953ebb2005-11-29 13:27:20 +00006927
ceriona982c052005-06-28 17:23:09 +00006928 switch (opc2) {
cerion61c92742005-09-14 22:59:26 +00006929 case 0x087: { // stvebx (Store Vector Byte Indexed, AV p131)
ceriond953ebb2005-11-29 13:27:20 +00006930 DIP("stvebx v%d,r%u,r%u\n", vS_addr, rA_addr, rB_addr);
cerion61c92742005-09-14 22:59:26 +00006931 assign( eb, binop(Iop_And8, mkU8(0xF),
cerion2831b002005-11-30 19:55:22 +00006932 unop(Iop_32to8,
6933 mkSzNarrow32(ty, mkexpr(EA)) )) );
cerion5b2325f2005-12-23 00:55:09 +00006934 assign( idx, binop(Iop_Shl8,
6935 binop(Iop_Sub8, mkU8(15), mkexpr(eb)),
cerion61c92742005-09-14 22:59:26 +00006936 mkU8(3)) );
6937 storeBE( mkexpr(EA),
6938 unop(Iop_32to8, unop(Iop_V128to32,
6939 binop(Iop_ShrV128, mkexpr(vS), mkexpr(idx)))) );
6940 break;
6941 }
6942 case 0x0A7: { // stvehx (Store Vector Half Word Indexed, AV p132)
ceriond953ebb2005-11-29 13:27:20 +00006943 DIP("stvehx v%d,r%u,r%u\n", vS_addr, rA_addr, rB_addr);
ceriondba87e22006-01-02 15:15:45 +00006944 assign( addr_aligned, addr_align(mkexpr(EA), 2) );
cerion61c92742005-09-14 22:59:26 +00006945 assign( eb, binop(Iop_And8, mkU8(0xF),
ceriondba87e22006-01-02 15:15:45 +00006946 mkSzNarrow8(ty, mkexpr(addr_aligned) )) );
cerion5b2325f2005-12-23 00:55:09 +00006947 assign( idx, binop(Iop_Shl8,
6948 binop(Iop_Sub8, mkU8(14), mkexpr(eb)),
cerion61c92742005-09-14 22:59:26 +00006949 mkU8(3)) );
ceriond953ebb2005-11-29 13:27:20 +00006950 storeBE( mkexpr(addr_aligned),
cerion61c92742005-09-14 22:59:26 +00006951 unop(Iop_32to16, unop(Iop_V128to32,
6952 binop(Iop_ShrV128, mkexpr(vS), mkexpr(idx)))) );
6953 break;
6954 }
6955 case 0x0C7: { // stvewx (Store Vector Word Indexed, AV p133)
ceriond953ebb2005-11-29 13:27:20 +00006956 DIP("stvewx v%d,r%u,r%u\n", vS_addr, rA_addr, rB_addr);
ceriondba87e22006-01-02 15:15:45 +00006957 assign( addr_aligned, addr_align(mkexpr(EA), 4) );
cerion61c92742005-09-14 22:59:26 +00006958 assign( eb, binop(Iop_And8, mkU8(0xF),
ceriondba87e22006-01-02 15:15:45 +00006959 mkSzNarrow8(ty, mkexpr(addr_aligned) )) );
cerion5b2325f2005-12-23 00:55:09 +00006960 assign( idx, binop(Iop_Shl8,
6961 binop(Iop_Sub8, mkU8(12), mkexpr(eb)),
cerion61c92742005-09-14 22:59:26 +00006962 mkU8(3)) );
ceriond953ebb2005-11-29 13:27:20 +00006963 storeBE( mkexpr(addr_aligned),
cerion61c92742005-09-14 22:59:26 +00006964 unop(Iop_V128to32,
6965 binop(Iop_ShrV128, mkexpr(vS), mkexpr(idx))) );
6966 break;
6967 }
cerion32aad402005-09-10 12:02:24 +00006968
ceriona982c052005-06-28 17:23:09 +00006969 case 0x0E7: // stvx (Store Vector Indexed, AV p134)
ceriond953ebb2005-11-29 13:27:20 +00006970 DIP("stvx v%d,r%u,r%u\n", vS_addr, rA_addr, rB_addr);
6971 storeBE( addr_align( mkexpr(EA), 16 ), mkexpr(vS) );
ceriona982c052005-06-28 17:23:09 +00006972 break;
6973
cerion32aad402005-09-10 12:02:24 +00006974 case 0x1E7: // stvxl (Store Vector Indexed LRU, AV p135)
6975 // XXX: stvxl can give explicit control over cache block replacement
ceriond953ebb2005-11-29 13:27:20 +00006976 DIP("stvxl v%d,r%u,r%u\n", vS_addr, rA_addr, rB_addr);
cerion32aad402005-09-10 12:02:24 +00006977 DIP(" => not implemented\n");
6978 return False;
ceriond953ebb2005-11-29 13:27:20 +00006979// STORE(vS, 16, addr_align( mkexpr(EA), 16 ));
cerion5b2325f2005-12-23 00:55:09 +00006980// break;
ceriona982c052005-06-28 17:23:09 +00006981
6982 default:
cerion5b2325f2005-12-23 00:55:09 +00006983 vex_printf("dis_av_store(ppc)(opc2)\n");
ceriona982c052005-06-28 17:23:09 +00006984 return False;
6985 }
6986 return True;
6987}
6988
cerion32aad402005-09-10 12:02:24 +00006989/*
6990 AltiVec Arithmetic Instructions
6991*/
6992static Bool dis_av_arith ( UInt theInstr )
6993{
cerion76de5cf2005-11-18 18:25:12 +00006994 /* VX-Form */
6995 UChar opc1 = ifieldOPC(theInstr);
6996 UChar vD_addr = ifieldRegDS(theInstr);
6997 UChar vA_addr = ifieldRegA(theInstr);
6998 UChar vB_addr = ifieldRegB(theInstr);
6999 UInt opc2 = IFIELD( theInstr, 0, 11 );
cerion32aad402005-09-10 12:02:24 +00007000
ceriond3e52412005-09-14 21:15:40 +00007001 IRTemp vA = newTemp(Ity_V128);
7002 IRTemp vB = newTemp(Ity_V128);
cerion4a49b032005-11-08 16:23:07 +00007003 IRTemp z3 = newTemp(Ity_I64);
7004 IRTemp z2 = newTemp(Ity_I64);
7005 IRTemp z1 = newTemp(Ity_I64);
7006 IRTemp z0 = newTemp(Ity_I64);
7007 IRTemp aEvn, aOdd;
7008 IRTemp a15, a14, a13, a12, a11, a10, a9, a8;
7009 IRTemp a7, a6, a5, a4, a3, a2, a1, a0;
7010 IRTemp b3, b2, b1, b0;
7011
7012 aEvn = aOdd = IRTemp_INVALID;
7013 a15 = a14 = a13 = a12 = a11 = a10 = a9 = a8 = IRTemp_INVALID;
7014 a7 = a6 = a5 = a4 = a3 = a2 = a1 = a0 = IRTemp_INVALID;
7015 b3 = b2 = b1 = b0 = IRTemp_INVALID;
7016
ceriond3e52412005-09-14 21:15:40 +00007017 assign( vA, getVReg(vA_addr));
7018 assign( vB, getVReg(vB_addr));
7019
cerion32aad402005-09-10 12:02:24 +00007020 if (opc1 != 0x4) {
cerion5b2325f2005-12-23 00:55:09 +00007021 vex_printf("dis_av_arith(ppc)(opc1 != 0x4)\n");
cerion32aad402005-09-10 12:02:24 +00007022 return False;
7023 }
7024
7025 switch (opc2) {
7026 /* Add */
ceriond3e52412005-09-14 21:15:40 +00007027 case 0x180: { // vaddcuw (Add Carryout Unsigned Word, AV p136)
cerion32aad402005-09-10 12:02:24 +00007028 DIP("vaddcuw v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion36991ef2005-09-15 12:42:16 +00007029 /* unsigned_ov(x+y) = (y >u not(x)) */
ceriond3e52412005-09-14 21:15:40 +00007030 putVReg( vD_addr, binop(Iop_ShrN32x4,
cerion36991ef2005-09-15 12:42:16 +00007031 binop(Iop_CmpGT32Ux4, mkexpr(vB),
7032 unop(Iop_NotV128, mkexpr(vA))),
ceriond3e52412005-09-14 21:15:40 +00007033 mkU8(31)) );
7034 break;
7035 }
cerion32aad402005-09-10 12:02:24 +00007036 case 0x000: // vaddubm (Add Unsigned Byte Modulo, AV p141)
7037 DIP("vaddubm v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion36991ef2005-09-15 12:42:16 +00007038 putVReg( vD_addr, binop(Iop_Add8x16, mkexpr(vA), mkexpr(vB)) );
7039 break;
7040
cerion32aad402005-09-10 12:02:24 +00007041 case 0x040: // vadduhm (Add Unsigned Half Word Modulo, AV p143)
7042 DIP("vadduhm v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion36991ef2005-09-15 12:42:16 +00007043 putVReg( vD_addr, binop(Iop_Add16x8, mkexpr(vA), mkexpr(vB)) );
7044 break;
7045
cerion32aad402005-09-10 12:02:24 +00007046 case 0x080: // vadduwm (Add Unsigned Word Modulo, AV p145)
7047 DIP("vadduwm v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion36991ef2005-09-15 12:42:16 +00007048 putVReg( vD_addr, binop(Iop_Add32x4, mkexpr(vA), mkexpr(vB)) );
7049 break;
7050
cerion32aad402005-09-10 12:02:24 +00007051 case 0x200: // vaddubs (Add Unsigned Byte Saturate, AV p142)
7052 DIP("vaddubs v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion36991ef2005-09-15 12:42:16 +00007053 putVReg( vD_addr, binop(Iop_QAdd8Ux16, mkexpr(vA), mkexpr(vB)) );
7054 // TODO: set VSCR[SAT], perhaps via new primop: Iop_SatOfQAdd8Ux16
7055 break;
7056
cerion32aad402005-09-10 12:02:24 +00007057 case 0x240: // vadduhs (Add Unsigned Half Word Saturate, AV p144)
7058 DIP("vadduhs v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion36991ef2005-09-15 12:42:16 +00007059 putVReg( vD_addr, binop(Iop_QAdd16Ux8, mkexpr(vA), mkexpr(vB)) );
7060 // TODO: set VSCR[SAT]
7061 break;
7062
cerion32aad402005-09-10 12:02:24 +00007063 case 0x280: // vadduws (Add Unsigned Word Saturate, AV p146)
7064 DIP("vadduws v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion36991ef2005-09-15 12:42:16 +00007065 putVReg( vD_addr, binop(Iop_QAdd32Ux4, mkexpr(vA), mkexpr(vB)) );
7066 // TODO: set VSCR[SAT]
7067 break;
7068
cerion32aad402005-09-10 12:02:24 +00007069 case 0x300: // vaddsbs (Add Signed Byte Saturate, AV p138)
7070 DIP("vaddsbs v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion36991ef2005-09-15 12:42:16 +00007071 putVReg( vD_addr, binop(Iop_QAdd8Sx16, mkexpr(vA), mkexpr(vB)) );
7072 // TODO: set VSCR[SAT]
7073 break;
7074
cerion32aad402005-09-10 12:02:24 +00007075 case 0x340: // vaddshs (Add Signed Half Word Saturate, AV p139)
7076 DIP("vaddshs v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion36991ef2005-09-15 12:42:16 +00007077 putVReg( vD_addr, binop(Iop_QAdd16Sx8, mkexpr(vA), mkexpr(vB)) );
7078 // TODO: set VSCR[SAT]
7079 break;
7080
cerion32aad402005-09-10 12:02:24 +00007081 case 0x380: // vaddsws (Add Signed Word Saturate, AV p140)
7082 DIP("vaddsws v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion36991ef2005-09-15 12:42:16 +00007083 putVReg( vD_addr, binop(Iop_QAdd32Sx4, mkexpr(vA), mkexpr(vB)) );
7084 // TODO: set VSCR[SAT]
7085 break;
7086
7087
cerion32aad402005-09-10 12:02:24 +00007088 /* Subtract */
cerion36991ef2005-09-15 12:42:16 +00007089 case 0x580: { // vsubcuw (Subtract Carryout Unsigned Word, AV p260)
cerion32aad402005-09-10 12:02:24 +00007090 DIP("vsubcuw v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion36991ef2005-09-15 12:42:16 +00007091 /* unsigned_ov(x-y) = (y >u x) */
7092 putVReg( vD_addr, binop(Iop_ShrN32x4,
7093 unop(Iop_NotV128,
7094 binop(Iop_CmpGT32Ux4, mkexpr(vB),
7095 mkexpr(vA))),
7096 mkU8(31)) );
7097 break;
7098 }
cerion32aad402005-09-10 12:02:24 +00007099 case 0x400: // vsububm (Subtract Unsigned Byte Modulo, AV p265)
7100 DIP("vsububm v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion36991ef2005-09-15 12:42:16 +00007101 putVReg( vD_addr, binop(Iop_Sub8x16, mkexpr(vA), mkexpr(vB)) );
7102 break;
7103
cerion32aad402005-09-10 12:02:24 +00007104 case 0x440: // vsubuhm (Subtract Unsigned Half Word Modulo, AV p267)
7105 DIP("vsubuhm v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion36991ef2005-09-15 12:42:16 +00007106 putVReg( vD_addr, binop(Iop_Sub16x8, mkexpr(vA), mkexpr(vB)) );
7107 break;
7108
cerion32aad402005-09-10 12:02:24 +00007109 case 0x480: // vsubuwm (Subtract Unsigned Word Modulo, AV p269)
7110 DIP("vsubuwm v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion36991ef2005-09-15 12:42:16 +00007111 putVReg( vD_addr, binop(Iop_Sub32x4, mkexpr(vA), mkexpr(vB)) );
7112 break;
7113
cerion32aad402005-09-10 12:02:24 +00007114 case 0x600: // vsububs (Subtract Unsigned Byte Saturate, AV p266)
7115 DIP("vsububs v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion36991ef2005-09-15 12:42:16 +00007116 putVReg( vD_addr, binop(Iop_QSub8Ux16, mkexpr(vA), mkexpr(vB)) );
7117 // TODO: set VSCR[SAT]
7118 break;
7119
cerion5b2325f2005-12-23 00:55:09 +00007120 case 0x640: // vsubuhs (Subtract Unsigned HWord Saturate, AV p268)
cerion32aad402005-09-10 12:02:24 +00007121 DIP("vsubuhs v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion36991ef2005-09-15 12:42:16 +00007122 putVReg( vD_addr, binop(Iop_QSub16Ux8, mkexpr(vA), mkexpr(vB)) );
7123 // TODO: set VSCR[SAT]
7124 break;
7125
cerion32aad402005-09-10 12:02:24 +00007126 case 0x680: // vsubuws (Subtract Unsigned Word Saturate, AV p270)
7127 DIP("vsubuws v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion36991ef2005-09-15 12:42:16 +00007128 putVReg( vD_addr, binop(Iop_QSub32Ux4, mkexpr(vA), mkexpr(vB)) );
7129 // TODO: set VSCR[SAT]
7130 break;
7131
cerion32aad402005-09-10 12:02:24 +00007132 case 0x700: // vsubsbs (Subtract Signed Byte Saturate, AV p262)
7133 DIP("vsubsbs v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion36991ef2005-09-15 12:42:16 +00007134 putVReg( vD_addr, binop(Iop_QSub8Sx16, mkexpr(vA), mkexpr(vB)) );
7135 // TODO: set VSCR[SAT]
7136 break;
7137
cerion32aad402005-09-10 12:02:24 +00007138 case 0x740: // vsubshs (Subtract Signed Half Word Saturate, AV p263)
7139 DIP("vsubshs v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion36991ef2005-09-15 12:42:16 +00007140 putVReg( vD_addr, binop(Iop_QSub16Sx8, mkexpr(vA), mkexpr(vB)) );
7141 // TODO: set VSCR[SAT]
7142 break;
7143
cerion32aad402005-09-10 12:02:24 +00007144 case 0x780: // vsubsws (Subtract Signed Word Saturate, AV p264)
7145 DIP("vsubsws v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion36991ef2005-09-15 12:42:16 +00007146 putVReg( vD_addr, binop(Iop_QSub32Sx4, mkexpr(vA), mkexpr(vB)) );
7147 // TODO: set VSCR[SAT]
7148 break;
cerion32aad402005-09-10 12:02:24 +00007149
7150
7151 /* Maximum */
7152 case 0x002: // vmaxub (Maximum Unsigned Byte, AV p182)
7153 DIP("vmaxub v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion36991ef2005-09-15 12:42:16 +00007154 putVReg( vD_addr, binop(Iop_Max8Ux16, mkexpr(vA), mkexpr(vB)) );
7155 break;
cerion32aad402005-09-10 12:02:24 +00007156
7157 case 0x042: // vmaxuh (Maximum Unsigned Half Word, AV p183)
7158 DIP("vmaxuh v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion36991ef2005-09-15 12:42:16 +00007159 putVReg( vD_addr, binop(Iop_Max16Ux8, mkexpr(vA), mkexpr(vB)) );
7160 break;
cerion32aad402005-09-10 12:02:24 +00007161
7162 case 0x082: // vmaxuw (Maximum Unsigned Word, AV p184)
7163 DIP("vmaxuw v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion36991ef2005-09-15 12:42:16 +00007164 putVReg( vD_addr, binop(Iop_Max32Ux4, mkexpr(vA), mkexpr(vB)) );
7165 break;
cerion32aad402005-09-10 12:02:24 +00007166
7167 case 0x102: // vmaxsb (Maximum Signed Byte, AV p179)
7168 DIP("vmaxsb v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion36991ef2005-09-15 12:42:16 +00007169 putVReg( vD_addr, binop(Iop_Max8Sx16, mkexpr(vA), mkexpr(vB)) );
7170 break;
cerion32aad402005-09-10 12:02:24 +00007171
7172 case 0x142: // vmaxsh (Maximum Signed Half Word, AV p180)
7173 DIP("vmaxsh v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion36991ef2005-09-15 12:42:16 +00007174 putVReg( vD_addr, binop(Iop_Max16Sx8, mkexpr(vA), mkexpr(vB)) );
7175 break;
cerion32aad402005-09-10 12:02:24 +00007176
7177 case 0x182: // vmaxsw (Maximum Signed Word, AV p181)
7178 DIP("vmaxsw v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion36991ef2005-09-15 12:42:16 +00007179 putVReg( vD_addr, binop(Iop_Max32Sx4, mkexpr(vA), mkexpr(vB)) );
7180 break;
cerion32aad402005-09-10 12:02:24 +00007181
7182
7183 /* Minimum */
7184 case 0x202: // vminub (Minimum Unsigned Byte, AV p191)
7185 DIP("vminub v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion36991ef2005-09-15 12:42:16 +00007186 putVReg( vD_addr, binop(Iop_Min8Ux16, mkexpr(vA), mkexpr(vB)) );
7187 break;
cerion32aad402005-09-10 12:02:24 +00007188
7189 case 0x242: // vminuh (Minimum Unsigned Half Word, AV p192)
7190 DIP("vminuh v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion36991ef2005-09-15 12:42:16 +00007191 putVReg( vD_addr, binop(Iop_Min16Ux8, mkexpr(vA), mkexpr(vB)) );
7192 break;
cerion32aad402005-09-10 12:02:24 +00007193
7194 case 0x282: // vminuw (Minimum Unsigned Word, AV p193)
7195 DIP("vminuw v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion36991ef2005-09-15 12:42:16 +00007196 putVReg( vD_addr, binop(Iop_Min32Ux4, mkexpr(vA), mkexpr(vB)) );
7197 break;
cerion32aad402005-09-10 12:02:24 +00007198
7199 case 0x302: // vminsb (Minimum Signed Byte, AV p188)
7200 DIP("vminsb v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion36991ef2005-09-15 12:42:16 +00007201 putVReg( vD_addr, binop(Iop_Min8Sx16, mkexpr(vA), mkexpr(vB)) );
7202 break;
cerion32aad402005-09-10 12:02:24 +00007203
7204 case 0x342: // vminsh (Minimum Signed Half Word, AV p189)
7205 DIP("vminsh v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion36991ef2005-09-15 12:42:16 +00007206 putVReg( vD_addr, binop(Iop_Min16Sx8, mkexpr(vA), mkexpr(vB)) );
7207 break;
cerion32aad402005-09-10 12:02:24 +00007208
7209 case 0x382: // vminsw (Minimum Signed Word, AV p190)
7210 DIP("vminsw v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion36991ef2005-09-15 12:42:16 +00007211 putVReg( vD_addr, binop(Iop_Min32Sx4, mkexpr(vA), mkexpr(vB)) );
7212 break;
7213
cerion32aad402005-09-10 12:02:24 +00007214
7215 /* Average */
7216 case 0x402: // vavgub (Average Unsigned Byte, AV p152)
7217 DIP("vavgub v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion36991ef2005-09-15 12:42:16 +00007218 putVReg( vD_addr, binop(Iop_Avg8Ux16, mkexpr(vA), mkexpr(vB)) );
7219 break;
cerion32aad402005-09-10 12:02:24 +00007220
7221 case 0x442: // vavguh (Average Unsigned Half Word, AV p153)
7222 DIP("vavguh v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion36991ef2005-09-15 12:42:16 +00007223 putVReg( vD_addr, binop(Iop_Avg16Ux8, mkexpr(vA), mkexpr(vB)) );
7224 break;
cerion32aad402005-09-10 12:02:24 +00007225
7226 case 0x482: // vavguw (Average Unsigned Word, AV p154)
7227 DIP("vavguw v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion36991ef2005-09-15 12:42:16 +00007228 putVReg( vD_addr, binop(Iop_Avg32Ux4, mkexpr(vA), mkexpr(vB)) );
7229 break;
cerion32aad402005-09-10 12:02:24 +00007230
7231 case 0x502: // vavgsb (Average Signed Byte, AV p149)
7232 DIP("vavgsb v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion36991ef2005-09-15 12:42:16 +00007233 putVReg( vD_addr, binop(Iop_Avg8Sx16, mkexpr(vA), mkexpr(vB)) );
7234 break;
cerion32aad402005-09-10 12:02:24 +00007235
7236 case 0x542: // vavgsh (Average Signed Half Word, AV p150)
7237 DIP("vavgsh v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion36991ef2005-09-15 12:42:16 +00007238 putVReg( vD_addr, binop(Iop_Avg16Sx8, mkexpr(vA), mkexpr(vB)) );
7239 break;
cerion32aad402005-09-10 12:02:24 +00007240
7241 case 0x582: // vavgsw (Average Signed Word, AV p151)
7242 DIP("vavgsw v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion36991ef2005-09-15 12:42:16 +00007243 putVReg( vD_addr, binop(Iop_Avg32Sx4, mkexpr(vA), mkexpr(vB)) );
7244 break;
cerion32aad402005-09-10 12:02:24 +00007245
7246
7247 /* Multiply */
7248 case 0x008: // vmuloub (Multiply Odd Unsigned Byte, AV p213)
7249 DIP("vmuloub v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion5b2325f2005-12-23 00:55:09 +00007250 putVReg( vD_addr,
7251 binop(Iop_MullEven8Ux16, mkexpr(vA), mkexpr(vB)));
cerion36991ef2005-09-15 12:42:16 +00007252 break;
cerion32aad402005-09-10 12:02:24 +00007253
7254 case 0x048: // vmulouh (Multiply Odd Unsigned Half Word, AV p214)
7255 DIP("vmulouh v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion5b2325f2005-12-23 00:55:09 +00007256 putVReg( vD_addr,
7257 binop(Iop_MullEven16Ux8, mkexpr(vA), mkexpr(vB)));
cerion36991ef2005-09-15 12:42:16 +00007258 break;
cerion32aad402005-09-10 12:02:24 +00007259
7260 case 0x108: // vmulosb (Multiply Odd Signed Byte, AV p211)
7261 DIP("vmulosb v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion5b2325f2005-12-23 00:55:09 +00007262 putVReg( vD_addr,
7263 binop(Iop_MullEven8Sx16, mkexpr(vA), mkexpr(vB)));
cerion36991ef2005-09-15 12:42:16 +00007264 break;
cerion32aad402005-09-10 12:02:24 +00007265
7266 case 0x148: // vmulosh (Multiply Odd Signed Half Word, AV p212)
7267 DIP("vmulosh v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion5b2325f2005-12-23 00:55:09 +00007268 putVReg( vD_addr,
7269 binop(Iop_MullEven16Sx8, mkexpr(vA), mkexpr(vB)));
cerion36991ef2005-09-15 12:42:16 +00007270 break;
cerion32aad402005-09-10 12:02:24 +00007271
7272 case 0x208: // vmuleub (Multiply Even Unsigned Byte, AV p209)
7273 DIP("vmuleub v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion24d06f12005-11-09 21:34:20 +00007274 putVReg( vD_addr, MK_Iop_MullOdd8Ux16( mkexpr(vA), mkexpr(vB) ));
cerion36991ef2005-09-15 12:42:16 +00007275 break;
cerion32aad402005-09-10 12:02:24 +00007276
7277 case 0x248: // vmuleuh (Multiply Even Unsigned Half Word, AV p210)
7278 DIP("vmuleuh v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion24d06f12005-11-09 21:34:20 +00007279 putVReg( vD_addr, MK_Iop_MullOdd16Ux8( mkexpr(vA), mkexpr(vB) ));
cerion36991ef2005-09-15 12:42:16 +00007280 break;
cerion32aad402005-09-10 12:02:24 +00007281
7282 case 0x308: // vmulesb (Multiply Even Signed Byte, AV p207)
7283 DIP("vmulesb v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion24d06f12005-11-09 21:34:20 +00007284 putVReg( vD_addr, MK_Iop_MullOdd8Sx16( mkexpr(vA), mkexpr(vB) ));
cerion36991ef2005-09-15 12:42:16 +00007285 break;
cerion32aad402005-09-10 12:02:24 +00007286
7287 case 0x348: // vmulesh (Multiply Even Signed Half Word, AV p208)
7288 DIP("vmulesh v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion24d06f12005-11-09 21:34:20 +00007289 putVReg( vD_addr, MK_Iop_MullOdd16Sx8( mkexpr(vA), mkexpr(vB) ));
cerion36991ef2005-09-15 12:42:16 +00007290 break;
cerion32aad402005-09-10 12:02:24 +00007291
7292
7293 /* Sum Across Partial */
cerion4a49b032005-11-08 16:23:07 +00007294 case 0x608: { // vsum4ubs (Sum Partial (1/4) UB Saturate, AV p275)
7295 IRTemp aEE, aEO, aOE, aOO;
7296 aEE = aEO = aOE = aOO = IRTemp_INVALID;
cerion32aad402005-09-10 12:02:24 +00007297 DIP("vsum4ubs v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion32aad402005-09-10 12:02:24 +00007298
cerion4a49b032005-11-08 16:23:07 +00007299 /* vA: V128_8Ux16 -> 4 x V128_32Ux4, sign-extended */
7300 expand8Ux16( mkexpr(vA), &aEvn, &aOdd ); // (15,13...),(14,12...)
7301 expand16Ux8( mkexpr(aEvn), &aEE, &aEO ); // (15,11...),(13, 9...)
7302 expand16Ux8( mkexpr(aOdd), &aOE, &aOO ); // (14,10...),(12, 8...)
7303
7304 /* break V128 to 4xI32's, zero-extending to I64's */
7305 breakV128to4x64U( mkexpr(aEE), &a15, &a11, &a7, &a3 );
7306 breakV128to4x64U( mkexpr(aOE), &a14, &a10, &a6, &a2 );
7307 breakV128to4x64U( mkexpr(aEO), &a13, &a9, &a5, &a1 );
7308 breakV128to4x64U( mkexpr(aOO), &a12, &a8, &a4, &a0 );
7309 breakV128to4x64U( mkexpr(vB), &b3, &b2, &b1, &b0 );
7310
7311 /* add lanes */
7312 assign( z3, binop(Iop_Add64, mkexpr(b3),
cerion5b2325f2005-12-23 00:55:09 +00007313 binop(Iop_Add64,
7314 binop(Iop_Add64, mkexpr(a15), mkexpr(a14)),
7315 binop(Iop_Add64, mkexpr(a13), mkexpr(a12)))) );
cerion4a49b032005-11-08 16:23:07 +00007316 assign( z2, binop(Iop_Add64, mkexpr(b2),
cerion5b2325f2005-12-23 00:55:09 +00007317 binop(Iop_Add64,
7318 binop(Iop_Add64, mkexpr(a11), mkexpr(a10)),
7319 binop(Iop_Add64, mkexpr(a9), mkexpr(a8)))) );
cerion4a49b032005-11-08 16:23:07 +00007320 assign( z1, binop(Iop_Add64, mkexpr(b1),
cerion5b2325f2005-12-23 00:55:09 +00007321 binop(Iop_Add64,
7322 binop(Iop_Add64, mkexpr(a7), mkexpr(a6)),
7323 binop(Iop_Add64, mkexpr(a5), mkexpr(a4)))) );
cerion4a49b032005-11-08 16:23:07 +00007324 assign( z0, binop(Iop_Add64, mkexpr(b0),
cerion5b2325f2005-12-23 00:55:09 +00007325 binop(Iop_Add64,
7326 binop(Iop_Add64, mkexpr(a3), mkexpr(a2)),
7327 binop(Iop_Add64, mkexpr(a1), mkexpr(a0)))) );
cerion4a49b032005-11-08 16:23:07 +00007328
7329 /* saturate-narrow to 32bit, and combine to V128 */
7330 putVReg( vD_addr, mkV128from4x64U( mkexpr(z3), mkexpr(z2),
7331 mkexpr(z1), mkexpr(z0)) );
7332 break;
7333 }
7334 case 0x708: { // vsum4sbs (Sum Partial (1/4) SB Saturate, AV p273)
7335 IRTemp aEE, aEO, aOE, aOO;
7336 aEE = aEO = aOE = aOO = IRTemp_INVALID;
cerion32aad402005-09-10 12:02:24 +00007337 DIP("vsum4sbs v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion32aad402005-09-10 12:02:24 +00007338
cerion4a49b032005-11-08 16:23:07 +00007339 /* vA: V128_8Sx16 -> 4 x V128_32Sx4, sign-extended */
7340 expand8Sx16( mkexpr(vA), &aEvn, &aOdd ); // (15,13...),(14,12...)
7341 expand16Sx8( mkexpr(aEvn), &aEE, &aEO ); // (15,11...),(13, 9...)
7342 expand16Sx8( mkexpr(aOdd), &aOE, &aOO ); // (14,10...),(12, 8...)
7343
7344 /* break V128 to 4xI32's, sign-extending to I64's */
7345 breakV128to4x64S( mkexpr(aEE), &a15, &a11, &a7, &a3 );
7346 breakV128to4x64S( mkexpr(aOE), &a14, &a10, &a6, &a2 );
7347 breakV128to4x64S( mkexpr(aEO), &a13, &a9, &a5, &a1 );
7348 breakV128to4x64S( mkexpr(aOO), &a12, &a8, &a4, &a0 );
7349 breakV128to4x64S( mkexpr(vB), &b3, &b2, &b1, &b0 );
7350
7351 /* add lanes */
7352 assign( z3, binop(Iop_Add64, mkexpr(b3),
cerion5b2325f2005-12-23 00:55:09 +00007353 binop(Iop_Add64,
7354 binop(Iop_Add64, mkexpr(a15), mkexpr(a14)),
7355 binop(Iop_Add64, mkexpr(a13), mkexpr(a12)))) );
cerion4a49b032005-11-08 16:23:07 +00007356 assign( z2, binop(Iop_Add64, mkexpr(b2),
cerion5b2325f2005-12-23 00:55:09 +00007357 binop(Iop_Add64,
7358 binop(Iop_Add64, mkexpr(a11), mkexpr(a10)),
7359 binop(Iop_Add64, mkexpr(a9), mkexpr(a8)))) );
cerion4a49b032005-11-08 16:23:07 +00007360 assign( z1, binop(Iop_Add64, mkexpr(b1),
cerion5b2325f2005-12-23 00:55:09 +00007361 binop(Iop_Add64,
7362 binop(Iop_Add64, mkexpr(a7), mkexpr(a6)),
7363 binop(Iop_Add64, mkexpr(a5), mkexpr(a4)))) );
cerion4a49b032005-11-08 16:23:07 +00007364 assign( z0, binop(Iop_Add64, mkexpr(b0),
cerion5b2325f2005-12-23 00:55:09 +00007365 binop(Iop_Add64,
7366 binop(Iop_Add64, mkexpr(a3), mkexpr(a2)),
7367 binop(Iop_Add64, mkexpr(a1), mkexpr(a0)))) );
cerion4a49b032005-11-08 16:23:07 +00007368
7369 /* saturate-narrow to 32bit, and combine to V128 */
7370 putVReg( vD_addr, mkV128from4x64S( mkexpr(z3), mkexpr(z2),
7371 mkexpr(z1), mkexpr(z0)) );
7372 break;
7373 }
7374 case 0x648: { // vsum4shs (Sum Partial (1/4) SHW Saturate, AV p274)
cerion32aad402005-09-10 12:02:24 +00007375 DIP("vsum4shs v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion32aad402005-09-10 12:02:24 +00007376
cerion4a49b032005-11-08 16:23:07 +00007377 /* vA: V128_16Sx8 -> 2 x V128_32Sx4, sign-extended */
7378 expand16Sx8( mkexpr(vA), &aEvn, &aOdd ); // (7,5...),(6,4...)
7379
7380 /* break V128 to 4xI32's, sign-extending to I64's */
7381 breakV128to4x64S( mkexpr(aEvn), &a7, &a5, &a3, &a1 );
7382 breakV128to4x64S( mkexpr(aOdd), &a6, &a4, &a2, &a0 );
7383 breakV128to4x64S( mkexpr(vB), &b3, &b2, &b1, &b0 );
7384
7385 /* add lanes */
7386 assign( z3, binop(Iop_Add64, mkexpr(b3),
7387 binop(Iop_Add64, mkexpr(a7), mkexpr(a6))));
7388 assign( z2, binop(Iop_Add64, mkexpr(b2),
7389 binop(Iop_Add64, mkexpr(a5), mkexpr(a4))));
7390 assign( z1, binop(Iop_Add64, mkexpr(b1),
7391 binop(Iop_Add64, mkexpr(a3), mkexpr(a2))));
7392 assign( z0, binop(Iop_Add64, mkexpr(b0),
7393 binop(Iop_Add64, mkexpr(a1), mkexpr(a0))));
7394
7395 /* saturate-narrow to 32bit, and combine to V128 */
7396 putVReg( vD_addr, mkV128from4x64S( mkexpr(z3), mkexpr(z2),
7397 mkexpr(z1), mkexpr(z0)) );
7398 break;
7399 }
7400 case 0x688: { // vsum2sws (Sum Partial (1/2) SW Saturate, AV p272)
cerion32aad402005-09-10 12:02:24 +00007401 DIP("vsum2sws v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion32aad402005-09-10 12:02:24 +00007402
cerion4a49b032005-11-08 16:23:07 +00007403 /* break V128 to 4xI32's, sign-extending to I64's */
7404 breakV128to4x64S( mkexpr(vA), &a3, &a2, &a1, &a0 );
7405 breakV128to4x64S( mkexpr(vB), &b3, &b2, &b1, &b0 );
7406
7407 /* add lanes */
7408 assign( z2, binop(Iop_Add64, mkexpr(b2),
7409 binop(Iop_Add64, mkexpr(a3), mkexpr(a2))) );
7410 assign( z0, binop(Iop_Add64, mkexpr(b0),
7411 binop(Iop_Add64, mkexpr(a1), mkexpr(a0))) );
7412
7413 /* saturate-narrow to 32bit, and combine to V128 */
7414 putVReg( vD_addr, mkV128from4x64S( mkU64(0), mkexpr(z2),
7415 mkU64(0), mkexpr(z0)) );
7416 break;
7417 }
7418 case 0x788: { // vsumsws (Sum SW Saturate, AV p271)
cerion32aad402005-09-10 12:02:24 +00007419 DIP("vsumsws v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion32aad402005-09-10 12:02:24 +00007420
cerion4a49b032005-11-08 16:23:07 +00007421 /* break V128 to 4xI32's, sign-extending to I64's */
7422 breakV128to4x64S( mkexpr(vA), &a3, &a2, &a1, &a0 );
7423 breakV128to4x64S( mkexpr(vB), &b3, &b2, &b1, &b0 );
7424
7425 /* add lanes */
7426 assign( z0, binop(Iop_Add64, mkexpr(b0),
cerion5b2325f2005-12-23 00:55:09 +00007427 binop(Iop_Add64,
7428 binop(Iop_Add64, mkexpr(a3), mkexpr(a2)),
7429 binop(Iop_Add64, mkexpr(a1), mkexpr(a0)))) );
cerion4a49b032005-11-08 16:23:07 +00007430
7431 /* saturate-narrow to 32bit, and combine to V128 */
7432 putVReg( vD_addr, mkV128from4x64S( mkU64(0), mkU64(0),
7433 mkU64(0), mkexpr(z0)) );
7434 break;
7435 }
cerion32aad402005-09-10 12:02:24 +00007436 default:
cerion5b2325f2005-12-23 00:55:09 +00007437 vex_printf("dis_av_arith(ppc)(opc2=0x%x)\n", opc2);
cerion32aad402005-09-10 12:02:24 +00007438 return False;
7439 }
7440 return True;
7441}
7442
7443/*
7444 AltiVec Logic Instructions
7445*/
7446static Bool dis_av_logic ( UInt theInstr )
7447{
cerion76de5cf2005-11-18 18:25:12 +00007448 /* VX-Form */
7449 UChar opc1 = ifieldOPC(theInstr);
7450 UChar vD_addr = ifieldRegDS(theInstr);
7451 UChar vA_addr = ifieldRegA(theInstr);
7452 UChar vB_addr = ifieldRegB(theInstr);
7453 UInt opc2 = IFIELD( theInstr, 0, 11 );
cerion32aad402005-09-10 12:02:24 +00007454
cerion225a0342005-09-12 20:49:09 +00007455 IRTemp vA = newTemp(Ity_V128);
7456 IRTemp vB = newTemp(Ity_V128);
7457 assign( vA, getVReg(vA_addr));
7458 assign( vB, getVReg(vB_addr));
7459
cerion32aad402005-09-10 12:02:24 +00007460 if (opc1 != 0x4) {
cerion5b2325f2005-12-23 00:55:09 +00007461 vex_printf("dis_av_logic(ppc)(opc1 != 0x4)\n");
cerion32aad402005-09-10 12:02:24 +00007462 return False;
7463 }
7464
7465 switch (opc2) {
7466 case 0x404: // vand (And, AV p147)
7467 DIP("vand v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion225a0342005-09-12 20:49:09 +00007468 putVReg( vD_addr, binop(Iop_AndV128, mkexpr(vA), mkexpr(vB)) );
7469 break;
cerion32aad402005-09-10 12:02:24 +00007470
7471 case 0x444: // vandc (And, AV p148)
7472 DIP("vandc v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion6e7a0ea2005-09-13 13:34:09 +00007473 putVReg( vD_addr, binop(Iop_AndV128, mkexpr(vA),
cerion76de5cf2005-11-18 18:25:12 +00007474 unop(Iop_NotV128, mkexpr(vB))) );
cerion6e7a0ea2005-09-13 13:34:09 +00007475 break;
cerion32aad402005-09-10 12:02:24 +00007476
7477 case 0x484: // vor (Or, AV p217)
7478 DIP("vor v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion225a0342005-09-12 20:49:09 +00007479 putVReg( vD_addr, binop(Iop_OrV128, mkexpr(vA), mkexpr(vB)) );
7480 break;
cerion32aad402005-09-10 12:02:24 +00007481
7482 case 0x4C4: // vxor (Xor, AV p282)
7483 DIP("vxor v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion225a0342005-09-12 20:49:09 +00007484 putVReg( vD_addr, binop(Iop_XorV128, mkexpr(vA), mkexpr(vB)) );
7485 break;
cerion32aad402005-09-10 12:02:24 +00007486
7487 case 0x504: // vnor (Nor, AV p216)
7488 DIP("vnor v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion6e7a0ea2005-09-13 13:34:09 +00007489 putVReg( vD_addr,
7490 unop(Iop_NotV128, binop(Iop_OrV128, mkexpr(vA), mkexpr(vB))) );
7491 break;
cerion32aad402005-09-10 12:02:24 +00007492
7493 default:
cerion5b2325f2005-12-23 00:55:09 +00007494 vex_printf("dis_av_logic(ppc)(opc2=0x%x)\n", opc2);
cerion32aad402005-09-10 12:02:24 +00007495 return False;
7496 }
7497 return True;
7498}
7499
7500/*
7501 AltiVec Compare Instructions
7502*/
7503static Bool dis_av_cmp ( UInt theInstr )
7504{
cerion76de5cf2005-11-18 18:25:12 +00007505 /* VXR-Form */
7506 UChar opc1 = ifieldOPC(theInstr);
7507 UChar vD_addr = ifieldRegDS(theInstr);
7508 UChar vA_addr = ifieldRegA(theInstr);
7509 UChar vB_addr = ifieldRegB(theInstr);
7510 UChar flag_rC = ifieldBIT10(theInstr);
7511 UInt opc2 = IFIELD( theInstr, 0, 10 );
cerion32aad402005-09-10 12:02:24 +00007512
cerion0c439222005-09-15 14:22:58 +00007513 IRTemp vA = newTemp(Ity_V128);
7514 IRTemp vB = newTemp(Ity_V128);
7515 IRTemp vD = newTemp(Ity_V128);
7516 assign( vA, getVReg(vA_addr));
7517 assign( vB, getVReg(vB_addr));
7518
cerion32aad402005-09-10 12:02:24 +00007519 if (opc1 != 0x4) {
cerion5b2325f2005-12-23 00:55:09 +00007520 vex_printf("dis_av_cmp(ppc)(instr)\n");
cerion32aad402005-09-10 12:02:24 +00007521 return False;
7522 }
7523
7524 switch (opc2) {
7525 case 0x006: // vcmpequb (Compare Equal-to Unsigned B, AV p160)
cerion5b2325f2005-12-23 00:55:09 +00007526 DIP("vcmpequb%s v%d,v%d,v%d\n", (flag_rC ? ".":""),
7527 vD_addr, vA_addr, vB_addr);
cerion0c439222005-09-15 14:22:58 +00007528 assign( vD, binop(Iop_CmpEQ8x16, mkexpr(vA), mkexpr(vB)) );
7529 break;
cerion32aad402005-09-10 12:02:24 +00007530
7531 case 0x046: // vcmpequh (Compare Equal-to Unsigned HW, AV p161)
cerion5b2325f2005-12-23 00:55:09 +00007532 DIP("vcmpequh%s v%d,v%d,v%d\n", (flag_rC ? ".":""),
7533 vD_addr, vA_addr, vB_addr);
cerion0c439222005-09-15 14:22:58 +00007534 assign( vD, binop(Iop_CmpEQ16x8, mkexpr(vA), mkexpr(vB)) );
7535 break;
cerion32aad402005-09-10 12:02:24 +00007536
7537 case 0x086: // vcmpequw (Compare Equal-to Unsigned W, AV p162)
cerion5b2325f2005-12-23 00:55:09 +00007538 DIP("vcmpequw%s v%d,v%d,v%d\n", (flag_rC ? ".":""),
7539 vD_addr, vA_addr, vB_addr);
cerion0c439222005-09-15 14:22:58 +00007540 assign( vD, binop(Iop_CmpEQ32x4, mkexpr(vA), mkexpr(vB)) );
7541 break;
cerion32aad402005-09-10 12:02:24 +00007542
7543 case 0x206: // vcmpgtub (Compare Greater-than Unsigned B, AV p168)
cerion5b2325f2005-12-23 00:55:09 +00007544 DIP("vcmpgtub%s v%d,v%d,v%d\n", (flag_rC ? ".":""),
7545 vD_addr, vA_addr, vB_addr);
cerion0c439222005-09-15 14:22:58 +00007546 assign( vD, binop(Iop_CmpGT8Ux16, mkexpr(vA), mkexpr(vB)) );
7547 break;
cerion32aad402005-09-10 12:02:24 +00007548
7549 case 0x246: // vcmpgtuh (Compare Greater-than Unsigned HW, AV p169)
cerion5b2325f2005-12-23 00:55:09 +00007550 DIP("vcmpgtuh%s v%d,v%d,v%d\n", (flag_rC ? ".":""),
7551 vD_addr, vA_addr, vB_addr);
cerion0c439222005-09-15 14:22:58 +00007552 assign( vD, binop(Iop_CmpGT16Ux8, mkexpr(vA), mkexpr(vB)) );
7553 break;
cerion32aad402005-09-10 12:02:24 +00007554
7555 case 0x286: // vcmpgtuw (Compare Greater-than Unsigned W, AV p170)
cerion5b2325f2005-12-23 00:55:09 +00007556 DIP("vcmpgtuw%s v%d,v%d,v%d\n", (flag_rC ? ".":""),
7557 vD_addr, vA_addr, vB_addr);
cerion0c439222005-09-15 14:22:58 +00007558 assign( vD, binop(Iop_CmpGT32Ux4, mkexpr(vA), mkexpr(vB)) );
7559 break;
cerion32aad402005-09-10 12:02:24 +00007560
7561 case 0x306: // vcmpgtsb (Compare Greater-than Signed B, AV p165)
cerion5b2325f2005-12-23 00:55:09 +00007562 DIP("vcmpgtsb%s v%d,v%d,v%d\n", (flag_rC ? ".":""),
7563 vD_addr, vA_addr, vB_addr);
cerion0c439222005-09-15 14:22:58 +00007564 assign( vD, binop(Iop_CmpGT8Sx16, mkexpr(vA), mkexpr(vB)) );
7565 break;
cerion32aad402005-09-10 12:02:24 +00007566
7567 case 0x346: // vcmpgtsh (Compare Greater-than Signed HW, AV p166)
cerion5b2325f2005-12-23 00:55:09 +00007568 DIP("vcmpgtsh%s v%d,v%d,v%d\n", (flag_rC ? ".":""),
7569 vD_addr, vA_addr, vB_addr);
cerion0c439222005-09-15 14:22:58 +00007570 assign( vD, binop(Iop_CmpGT16Sx8, mkexpr(vA), mkexpr(vB)) );
7571 break;
cerion32aad402005-09-10 12:02:24 +00007572
7573 case 0x386: // vcmpgtsw (Compare Greater-than Signed W, AV p167)
cerion5b2325f2005-12-23 00:55:09 +00007574 DIP("vcmpgtsw%s v%d,v%d,v%d\n", (flag_rC ? ".":""),
7575 vD_addr, vA_addr, vB_addr);
cerion0c439222005-09-15 14:22:58 +00007576 assign( vD, binop(Iop_CmpGT32Sx4, mkexpr(vA), mkexpr(vB)) );
7577 break;
cerion32aad402005-09-10 12:02:24 +00007578
7579 default:
cerion5b2325f2005-12-23 00:55:09 +00007580 vex_printf("dis_av_cmp(ppc)(opc2)\n");
cerion32aad402005-09-10 12:02:24 +00007581 return False;
7582 }
cerion0c439222005-09-15 14:22:58 +00007583
7584 putVReg( vD_addr, mkexpr(vD) );
7585
cerion76de5cf2005-11-18 18:25:12 +00007586 if (flag_rC) {
cerion8ea0d3e2005-11-14 00:44:47 +00007587 set_AV_CR6( mkexpr(vD), True );
cerion0c439222005-09-15 14:22:58 +00007588 }
cerion32aad402005-09-10 12:02:24 +00007589 return True;
7590}
7591
7592/*
7593 AltiVec Multiply-Sum Instructions
7594*/
7595static Bool dis_av_multarith ( UInt theInstr )
7596{
cerion76de5cf2005-11-18 18:25:12 +00007597 /* VA-Form */
7598 UChar opc1 = ifieldOPC(theInstr);
7599 UChar vD_addr = ifieldRegDS(theInstr);
7600 UChar vA_addr = ifieldRegA(theInstr);
7601 UChar vB_addr = ifieldRegB(theInstr);
7602 UChar vC_addr = ifieldRegC(theInstr);
7603 UChar opc2 = toUChar( IFIELD( theInstr, 0, 6 ) );
cerion32aad402005-09-10 12:02:24 +00007604
cerion4a49b032005-11-08 16:23:07 +00007605 IRTemp vA = newTemp(Ity_V128);
7606 IRTemp vB = newTemp(Ity_V128);
7607 IRTemp vC = newTemp(Ity_V128);
sewardj197bd172005-10-12 11:34:33 +00007608 IRTemp zeros = newTemp(Ity_V128);
cerion4a49b032005-11-08 16:23:07 +00007609 IRTemp aLo = newTemp(Ity_V128);
7610 IRTemp bLo = newTemp(Ity_V128);
7611 IRTemp cLo = newTemp(Ity_V128);
7612 IRTemp zLo = newTemp(Ity_V128);
7613 IRTemp aHi = newTemp(Ity_V128);
7614 IRTemp bHi = newTemp(Ity_V128);
7615 IRTemp cHi = newTemp(Ity_V128);
7616 IRTemp zHi = newTemp(Ity_V128);
7617 IRTemp abEvn = newTemp(Ity_V128);
7618 IRTemp abOdd = newTemp(Ity_V128);
7619 IRTemp z3 = newTemp(Ity_I64);
7620 IRTemp z2 = newTemp(Ity_I64);
7621 IRTemp z1 = newTemp(Ity_I64);
7622 IRTemp z0 = newTemp(Ity_I64);
7623 IRTemp ab7, ab6, ab5, ab4, ab3, ab2, ab1, ab0;
7624 IRTemp c3, c2, c1, c0;
7625
7626 ab7 = ab6 = ab5 = ab4 = ab3 = ab2 = ab1 = ab0 = IRTemp_INVALID;
7627 c3 = c2 = c1 = c0 = IRTemp_INVALID;
7628
cerion6f1cc0f2005-09-16 16:02:11 +00007629 assign( vA, getVReg(vA_addr));
7630 assign( vB, getVReg(vB_addr));
7631 assign( vC, getVReg(vC_addr));
cerion4a49b032005-11-08 16:23:07 +00007632 assign( zeros, unop(Iop_Dup32x4, mkU32(0)) );
cerion6f1cc0f2005-09-16 16:02:11 +00007633
cerion32aad402005-09-10 12:02:24 +00007634 if (opc1 != 0x4) {
cerion5b2325f2005-12-23 00:55:09 +00007635 vex_printf("dis_av_multarith(ppc)(instr)\n");
cerion32aad402005-09-10 12:02:24 +00007636 return False;
7637 }
7638
7639 switch (opc2) {
cerion32aad402005-09-10 12:02:24 +00007640 /* Multiply-Add */
cerion5b2325f2005-12-23 00:55:09 +00007641 case 0x20: { // vmhaddshs (Mult Hi, Add Signed HW Saturate, AV p185)
cerion6f1cc0f2005-09-16 16:02:11 +00007642 IRTemp cSigns = newTemp(Ity_V128);
cerion5b2325f2005-12-23 00:55:09 +00007643 DIP("vmhaddshs v%d,v%d,v%d,v%d\n",
7644 vD_addr, vA_addr, vB_addr, vC_addr);
7645 assign(cSigns, binop(Iop_CmpGT16Sx8, mkexpr(zeros), mkexpr(vC)));
7646 assign(aLo, binop(Iop_InterleaveLO16x8, mkexpr(zeros), mkexpr(vA)));
7647 assign(bLo, binop(Iop_InterleaveLO16x8, mkexpr(zeros), mkexpr(vB)));
7648 assign(cLo, binop(Iop_InterleaveLO16x8, mkexpr(cSigns),mkexpr(vC)));
7649 assign(aHi, binop(Iop_InterleaveHI16x8, mkexpr(zeros), mkexpr(vA)));
7650 assign(bHi, binop(Iop_InterleaveHI16x8, mkexpr(zeros), mkexpr(vB)));
7651 assign(cHi, binop(Iop_InterleaveHI16x8, mkexpr(cSigns),mkexpr(vC)));
cerion32aad402005-09-10 12:02:24 +00007652
cerion24d06f12005-11-09 21:34:20 +00007653 assign( zLo, binop(Iop_Add32x4, mkexpr(cLo),
cerion6f1cc0f2005-09-16 16:02:11 +00007654 binop(Iop_SarN32x4,
cerion1ac656a2005-11-04 19:44:48 +00007655 binop(Iop_MullEven16Sx8,
cerion24d06f12005-11-09 21:34:20 +00007656 mkexpr(aLo), mkexpr(bLo)),
7657 mkU8(15))) );
cerion6f1cc0f2005-09-16 16:02:11 +00007658
cerion24d06f12005-11-09 21:34:20 +00007659 assign( zHi, binop(Iop_Add32x4, mkexpr(cHi),
cerion6f1cc0f2005-09-16 16:02:11 +00007660 binop(Iop_SarN32x4,
cerion1ac656a2005-11-04 19:44:48 +00007661 binop(Iop_MullEven16Sx8,
cerion24d06f12005-11-09 21:34:20 +00007662 mkexpr(aHi), mkexpr(bHi)),
7663 mkU8(15))) );
cerion6f1cc0f2005-09-16 16:02:11 +00007664
cerion5b2325f2005-12-23 00:55:09 +00007665 putVReg( vD_addr,
7666 binop(Iop_QNarrow32Sx4, mkexpr(zHi), mkexpr(zLo)) );
cerion6f1cc0f2005-09-16 16:02:11 +00007667 break;
7668 }
cerion5b2325f2005-12-23 00:55:09 +00007669 case 0x21: { // vmhraddshs (Mult High Round, Add Signed HW Saturate, AV p186)
cerion6f1cc0f2005-09-16 16:02:11 +00007670 IRTemp zKonst = newTemp(Ity_V128);
cerion6f1cc0f2005-09-16 16:02:11 +00007671 IRTemp cSigns = newTemp(Ity_V128);
cerion5b2325f2005-12-23 00:55:09 +00007672 DIP("vmhraddshs v%d,v%d,v%d,v%d\n",
7673 vD_addr, vA_addr, vB_addr, vC_addr);
7674 assign(cSigns, binop(Iop_CmpGT16Sx8, mkexpr(zeros), mkexpr(vC)) );
7675 assign(aLo, binop(Iop_InterleaveLO16x8, mkexpr(zeros), mkexpr(vA)));
7676 assign(bLo, binop(Iop_InterleaveLO16x8, mkexpr(zeros), mkexpr(vB)));
7677 assign(cLo, binop(Iop_InterleaveLO16x8, mkexpr(cSigns),mkexpr(vC)));
7678 assign(aHi, binop(Iop_InterleaveHI16x8, mkexpr(zeros), mkexpr(vA)));
7679 assign(bHi, binop(Iop_InterleaveHI16x8, mkexpr(zeros), mkexpr(vB)));
7680 assign(cHi, binop(Iop_InterleaveHI16x8, mkexpr(cSigns),mkexpr(vC)));
cerion32aad402005-09-10 12:02:24 +00007681
cerion6f1cc0f2005-09-16 16:02:11 +00007682 /* shifting our const avoids store/load version of Dup */
cerion4a49b032005-11-08 16:23:07 +00007683 assign( zKonst, binop(Iop_ShlN32x4, unop(Iop_Dup32x4, mkU32(0x1)),
7684 mkU8(14)) );
cerion6f1cc0f2005-09-16 16:02:11 +00007685
cerion24d06f12005-11-09 21:34:20 +00007686 assign( zLo, binop(Iop_Add32x4, mkexpr(cLo),
cerion6f1cc0f2005-09-16 16:02:11 +00007687 binop(Iop_SarN32x4,
7688 binop(Iop_Add32x4, mkexpr(zKonst),
cerion1ac656a2005-11-04 19:44:48 +00007689 binop(Iop_MullEven16Sx8,
cerion24d06f12005-11-09 21:34:20 +00007690 mkexpr(aLo), mkexpr(bLo))),
7691 mkU8(15))) );
cerion6f1cc0f2005-09-16 16:02:11 +00007692
cerion24d06f12005-11-09 21:34:20 +00007693 assign( zHi, binop(Iop_Add32x4, mkexpr(cHi),
cerion6f1cc0f2005-09-16 16:02:11 +00007694 binop(Iop_SarN32x4,
7695 binop(Iop_Add32x4, mkexpr(zKonst),
cerion1ac656a2005-11-04 19:44:48 +00007696 binop(Iop_MullEven16Sx8,
cerion24d06f12005-11-09 21:34:20 +00007697 mkexpr(aHi), mkexpr(bHi))),
7698 mkU8(15))) );
cerion6f1cc0f2005-09-16 16:02:11 +00007699
7700 putVReg( vD_addr, binop(Iop_QNarrow32Sx4, mkexpr(zHi), mkexpr(zLo)) );
7701 break;
7702 }
cerion5b2325f2005-12-23 00:55:09 +00007703 case 0x22: { // vmladduhm (Mult Low, Add Unsigned HW Modulo, AV p194)
7704 DIP("vmladduhm v%d,v%d,v%d,v%d\n",
7705 vD_addr, vA_addr, vB_addr, vC_addr);
7706 assign(aLo, binop(Iop_InterleaveLO16x8, mkexpr(zeros), mkexpr(vA)));
7707 assign(bLo, binop(Iop_InterleaveLO16x8, mkexpr(zeros), mkexpr(vB)));
7708 assign(cLo, binop(Iop_InterleaveLO16x8, mkexpr(zeros), mkexpr(vC)));
7709 assign(aHi, binop(Iop_InterleaveHI16x8, mkexpr(zeros), mkexpr(vA)));
7710 assign(bHi, binop(Iop_InterleaveHI16x8, mkexpr(zeros), mkexpr(vB)));
7711 assign(cHi, binop(Iop_InterleaveHI16x8, mkexpr(zeros), mkexpr(vC)));
7712 assign(zLo, binop(Iop_Add32x4,
7713 binop(Iop_MullEven16Ux8, mkexpr(aLo), mkexpr(bLo)),
7714 mkexpr(cLo)) );
7715 assign(zHi, binop(Iop_Add32x4,
7716 binop(Iop_MullEven16Ux8, mkexpr(aHi), mkexpr(bHi)),
7717 mkexpr(cHi)));
7718 putVReg(vD_addr, binop(Iop_Narrow32x4, mkexpr(zHi), mkexpr(zLo)));
cerion6f1cc0f2005-09-16 16:02:11 +00007719 break;
7720 }
cerion32aad402005-09-10 12:02:24 +00007721
7722
7723 /* Multiply-Sum */
cerion6f1cc0f2005-09-16 16:02:11 +00007724 case 0x24: { // vmsumubm (Multiply Sum Unsigned B Modulo, AV p204)
cerion4a49b032005-11-08 16:23:07 +00007725 IRTemp abEE, abEO, abOE, abOO;
7726 abEE = abEO = abOE = abOO = IRTemp_INVALID;
cerion5b2325f2005-12-23 00:55:09 +00007727 DIP("vmsumubm v%d,v%d,v%d,v%d\n",
7728 vD_addr, vA_addr, vB_addr, vC_addr);
cerion32aad402005-09-10 12:02:24 +00007729
cerion4a49b032005-11-08 16:23:07 +00007730 /* multiply vA,vB (unsigned, widening) */
cerion24d06f12005-11-09 21:34:20 +00007731 assign( abEvn, MK_Iop_MullOdd8Ux16( mkexpr(vA), mkexpr(vB) ));
7732 assign( abOdd, binop(Iop_MullEven8Ux16, mkexpr(vA), mkexpr(vB)) );
cerion4a49b032005-11-08 16:23:07 +00007733
7734 /* evn,odd: V128_16Ux8 -> 2 x V128_32Ux4, zero-extended */
7735 expand16Ux8( mkexpr(abEvn), &abEE, &abEO );
7736 expand16Ux8( mkexpr(abOdd), &abOE, &abOO );
7737
cerion6f1cc0f2005-09-16 16:02:11 +00007738 putVReg( vD_addr,
cerion5b2325f2005-12-23 00:55:09 +00007739 binop(Iop_Add32x4, mkexpr(vC),
7740 binop(Iop_Add32x4,
7741 binop(Iop_Add32x4, mkexpr(abEE), mkexpr(abEO)),
7742 binop(Iop_Add32x4, mkexpr(abOE), mkexpr(abOO)))) );
cerion6f1cc0f2005-09-16 16:02:11 +00007743 break;
7744 }
cerion4a49b032005-11-08 16:23:07 +00007745 case 0x25: { // vmsummbm (Multiply Sum Mixed-Sign B Modulo, AV p201)
7746 IRTemp aEvn, aOdd, bEvn, bOdd;
7747 IRTemp abEE = newTemp(Ity_V128);
7748 IRTemp abEO = newTemp(Ity_V128);
7749 IRTemp abOE = newTemp(Ity_V128);
7750 IRTemp abOO = newTemp(Ity_V128);
7751 aEvn = aOdd = bEvn = bOdd = IRTemp_INVALID;
cerion5b2325f2005-12-23 00:55:09 +00007752 DIP("vmsummbm v%d,v%d,v%d,v%d\n",
7753 vD_addr, vA_addr, vB_addr, vC_addr);
cerion32aad402005-09-10 12:02:24 +00007754
cerion4a49b032005-11-08 16:23:07 +00007755 /* sign-extend vA, zero-extend vB, for mixed-sign multiply
7756 (separating out adjacent lanes to different vectors) */
7757 expand8Sx16( mkexpr(vA), &aEvn, &aOdd );
7758 expand8Ux16( mkexpr(vB), &bEvn, &bOdd );
7759
7760 /* multiply vA, vB, again separating adjacent lanes */
cerion24d06f12005-11-09 21:34:20 +00007761 assign( abEE, MK_Iop_MullOdd16Sx8( mkexpr(aEvn), mkexpr(bEvn) ));
7762 assign( abEO, binop(Iop_MullEven16Sx8, mkexpr(aEvn), mkexpr(bEvn)) );
7763 assign( abOE, MK_Iop_MullOdd16Sx8( mkexpr(aOdd), mkexpr(bOdd) ));
7764 assign( abOO, binop(Iop_MullEven16Sx8, mkexpr(aOdd), mkexpr(bOdd)) );
cerion4a49b032005-11-08 16:23:07 +00007765
7766 /* add results together, + vC */
7767 putVReg( vD_addr,
cerion5b2325f2005-12-23 00:55:09 +00007768 binop(Iop_QAdd32Sx4, mkexpr(vC),
7769 binop(Iop_QAdd32Sx4,
7770 binop(Iop_QAdd32Sx4, mkexpr(abEE), mkexpr(abEO)),
7771 binop(Iop_QAdd32Sx4, mkexpr(abOE), mkexpr(abOO)))) );
cerion4a49b032005-11-08 16:23:07 +00007772 break;
7773 }
cerion6f1cc0f2005-09-16 16:02:11 +00007774 case 0x26: { // vmsumuhm (Multiply Sum Unsigned HW Modulo, AV p205)
cerion5b2325f2005-12-23 00:55:09 +00007775 DIP("vmsumuhm v%d,v%d,v%d,v%d\n",
7776 vD_addr, vA_addr, vB_addr, vC_addr);
cerion24d06f12005-11-09 21:34:20 +00007777 assign( abEvn, MK_Iop_MullOdd16Ux8( mkexpr(vA), mkexpr(vB) ));
7778 assign( abOdd, binop(Iop_MullEven16Ux8, mkexpr(vA), mkexpr(vB)) );
cerion6f1cc0f2005-09-16 16:02:11 +00007779 putVReg( vD_addr,
cerion5b2325f2005-12-23 00:55:09 +00007780 binop(Iop_Add32x4, mkexpr(vC),
7781 binop(Iop_Add32x4, mkexpr(abEvn), mkexpr(abOdd))) );
cerion6f1cc0f2005-09-16 16:02:11 +00007782 break;
7783 }
cerion4a49b032005-11-08 16:23:07 +00007784 case 0x27: { // vmsumuhs (Multiply Sum Unsigned HW Saturate, AV p206)
cerion5b2325f2005-12-23 00:55:09 +00007785 DIP("vmsumuhs v%d,v%d,v%d,v%d\n",
7786 vD_addr, vA_addr, vB_addr, vC_addr);
cerion4a49b032005-11-08 16:23:07 +00007787 /* widening multiply, separating lanes */
cerion24d06f12005-11-09 21:34:20 +00007788 assign( abEvn, MK_Iop_MullOdd16Ux8(mkexpr(vA), mkexpr(vB) ));
7789 assign( abOdd, binop(Iop_MullEven16Ux8, mkexpr(vA), mkexpr(vB)) );
cerion32aad402005-09-10 12:02:24 +00007790
cerion4a49b032005-11-08 16:23:07 +00007791 /* break V128 to 4xI32's, zero-extending to I64's */
7792 breakV128to4x64U( mkexpr(abEvn), &ab7, &ab5, &ab3, &ab1 );
7793 breakV128to4x64U( mkexpr(abOdd), &ab6, &ab4, &ab2, &ab0 );
7794 breakV128to4x64U( mkexpr(vC), &c3, &c2, &c1, &c0 );
7795
7796 /* add lanes */
7797 assign( z3, binop(Iop_Add64, mkexpr(c3),
7798 binop(Iop_Add64, mkexpr(ab7), mkexpr(ab6))));
7799 assign( z2, binop(Iop_Add64, mkexpr(c2),
7800 binop(Iop_Add64, mkexpr(ab5), mkexpr(ab4))));
7801 assign( z1, binop(Iop_Add64, mkexpr(c1),
7802 binop(Iop_Add64, mkexpr(ab3), mkexpr(ab2))));
7803 assign( z0, binop(Iop_Add64, mkexpr(c0),
7804 binop(Iop_Add64, mkexpr(ab1), mkexpr(ab0))));
7805
7806 /* saturate-narrow to 32bit, and combine to V128 */
7807 putVReg( vD_addr, mkV128from4x64U( mkexpr(z3), mkexpr(z2),
7808 mkexpr(z1), mkexpr(z0)) );
7809
cerion6f1cc0f2005-09-16 16:02:11 +00007810 break;
7811 }
cerion4a49b032005-11-08 16:23:07 +00007812 case 0x28: { // vmsumshm (Multiply Sum Signed HW Modulo, AV p202)
cerion5b2325f2005-12-23 00:55:09 +00007813 DIP("vmsumshm v%d,v%d,v%d,v%d\n",
7814 vD_addr, vA_addr, vB_addr, vC_addr);
cerion24d06f12005-11-09 21:34:20 +00007815 assign( abEvn, MK_Iop_MullOdd16Sx8( mkexpr(vA), mkexpr(vB) ));
7816 assign( abOdd, binop(Iop_MullEven16Sx8, mkexpr(vA), mkexpr(vB)) );
cerion4a49b032005-11-08 16:23:07 +00007817 putVReg( vD_addr,
cerion5b2325f2005-12-23 00:55:09 +00007818 binop(Iop_Add32x4, mkexpr(vC),
7819 binop(Iop_Add32x4, mkexpr(abOdd), mkexpr(abEvn))) );
cerion4a49b032005-11-08 16:23:07 +00007820 break;
7821 }
7822 case 0x29: { // vmsumshs (Multiply Sum Signed HW Saturate, AV p203)
cerion5b2325f2005-12-23 00:55:09 +00007823 DIP("vmsumshs v%d,v%d,v%d,v%d\n",
7824 vD_addr, vA_addr, vB_addr, vC_addr);
cerion4a49b032005-11-08 16:23:07 +00007825 /* widening multiply, separating lanes */
cerion24d06f12005-11-09 21:34:20 +00007826 assign( abEvn, MK_Iop_MullOdd16Sx8( mkexpr(vA), mkexpr(vB) ));
7827 assign( abOdd, binop(Iop_MullEven16Sx8, mkexpr(vA), mkexpr(vB)) );
cerion32aad402005-09-10 12:02:24 +00007828
cerion4a49b032005-11-08 16:23:07 +00007829 /* break V128 to 4xI32's, sign-extending to I64's */
7830 breakV128to4x64S( mkexpr(abEvn), &ab7, &ab5, &ab3, &ab1 );
7831 breakV128to4x64S( mkexpr(abOdd), &ab6, &ab4, &ab2, &ab0 );
7832 breakV128to4x64S( mkexpr(vC), &c3, &c2, &c1, &c0 );
7833
7834 /* add lanes */
7835 assign( z3, binop(Iop_Add64, mkexpr(c3),
7836 binop(Iop_Add64, mkexpr(ab7), mkexpr(ab6))));
7837 assign( z2, binop(Iop_Add64, mkexpr(c2),
7838 binop(Iop_Add64, mkexpr(ab5), mkexpr(ab4))));
7839 assign( z1, binop(Iop_Add64, mkexpr(c1),
7840 binop(Iop_Add64, mkexpr(ab3), mkexpr(ab2))));
7841 assign( z0, binop(Iop_Add64, mkexpr(c0),
7842 binop(Iop_Add64, mkexpr(ab1), mkexpr(ab0))));
7843
7844 /* saturate-narrow to 32bit, and combine to V128 */
7845 putVReg( vD_addr, mkV128from4x64S( mkexpr(z3), mkexpr(z2),
7846 mkexpr(z1), mkexpr(z0)) );
7847 break;
7848 }
cerion32aad402005-09-10 12:02:24 +00007849 default:
cerion5b2325f2005-12-23 00:55:09 +00007850 vex_printf("dis_av_multarith(ppc)(opc2)\n");
cerion32aad402005-09-10 12:02:24 +00007851 return False;
7852 }
7853 return True;
7854}
7855
7856/*
7857 AltiVec Shift/Rotate Instructions
7858*/
7859static Bool dis_av_shift ( UInt theInstr )
7860{
cerion76de5cf2005-11-18 18:25:12 +00007861 /* VX-Form */
7862 UChar opc1 = ifieldOPC(theInstr);
7863 UChar vD_addr = ifieldRegDS(theInstr);
7864 UChar vA_addr = ifieldRegA(theInstr);
7865 UChar vB_addr = ifieldRegB(theInstr);
7866 UInt opc2 = IFIELD( theInstr, 0, 11 );
cerion32aad402005-09-10 12:02:24 +00007867
cerion27b3d7e2005-09-14 20:35:47 +00007868 IRTemp vA = newTemp(Ity_V128);
7869 IRTemp vB = newTemp(Ity_V128);
7870 assign( vA, getVReg(vA_addr));
7871 assign( vB, getVReg(vB_addr));
7872
cerion32aad402005-09-10 12:02:24 +00007873 if (opc1 != 0x4){
cerion5b2325f2005-12-23 00:55:09 +00007874 vex_printf("dis_av_shift(ppc)(instr)\n");
cerion32aad402005-09-10 12:02:24 +00007875 return False;
7876 }
7877
7878 switch (opc2) {
7879 /* Rotate */
7880 case 0x004: // vrlb (Rotate Left Integer B, AV p234)
7881 DIP("vrlb v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
sewardj1bee5612005-11-10 18:10:58 +00007882 putVReg( vD_addr, binop(Iop_Rol8x16, mkexpr(vA), mkexpr(vB)) );
cerion0a7b4f42005-09-16 07:54:40 +00007883 break;
cerion32aad402005-09-10 12:02:24 +00007884
7885 case 0x044: // vrlh (Rotate Left Integer HW, AV p235)
7886 DIP("vrlh v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
sewardj1bee5612005-11-10 18:10:58 +00007887 putVReg( vD_addr, binop(Iop_Rol16x8, mkexpr(vA), mkexpr(vB)) );
cerion0a7b4f42005-09-16 07:54:40 +00007888 break;
cerion32aad402005-09-10 12:02:24 +00007889
7890 case 0x084: // vrlw (Rotate Left Integer W, AV p236)
7891 DIP("vrlw v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
sewardj1bee5612005-11-10 18:10:58 +00007892 putVReg( vD_addr, binop(Iop_Rol32x4, mkexpr(vA), mkexpr(vB)) );
cerion0a7b4f42005-09-16 07:54:40 +00007893 break;
cerion32aad402005-09-10 12:02:24 +00007894
7895
7896 /* Shift Left */
7897 case 0x104: // vslb (Shift Left Integer B, AV p240)
7898 DIP("vslb v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion0a7b4f42005-09-16 07:54:40 +00007899 putVReg( vD_addr, binop(Iop_Shl8x16, mkexpr(vA), mkexpr(vB)) );
7900 break;
cerion32aad402005-09-10 12:02:24 +00007901
7902 case 0x144: // vslh (Shift Left Integer HW, AV p242)
7903 DIP("vslh v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion0a7b4f42005-09-16 07:54:40 +00007904 putVReg( vD_addr, binop(Iop_Shl16x8, mkexpr(vA), mkexpr(vB)) );
7905 break;
cerion32aad402005-09-10 12:02:24 +00007906
7907 case 0x184: // vslw (Shift Left Integer W, AV p244)
7908 DIP("vslw v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion0a7b4f42005-09-16 07:54:40 +00007909 putVReg( vD_addr, binop(Iop_Shl32x4, mkexpr(vA), mkexpr(vB)) );
7910 break;
cerion32aad402005-09-10 12:02:24 +00007911
cerion0a7b4f42005-09-16 07:54:40 +00007912 case 0x1C4: { // vsl (Shift Left, AV p239)
cerion0a7b4f42005-09-16 07:54:40 +00007913 IRTemp sh = newTemp(Ity_I8);
sewardj197bd172005-10-12 11:34:33 +00007914 DIP("vsl v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion0a7b4f42005-09-16 07:54:40 +00007915 assign( sh, binop(Iop_And8, mkU8(0x7),
7916 unop(Iop_32to8,
7917 unop(Iop_V128to32, mkexpr(vB)))) );
7918 putVReg( vD_addr,
7919 binop(Iop_ShlV128, mkexpr(vA), mkexpr(sh)) );
7920 break;
7921 }
7922 case 0x40C: { // vslo (Shift Left by Octet, AV p243)
cerion0a7b4f42005-09-16 07:54:40 +00007923 IRTemp sh = newTemp(Ity_I8);
sewardj197bd172005-10-12 11:34:33 +00007924 DIP("vslo v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion0a7b4f42005-09-16 07:54:40 +00007925 assign( sh, binop(Iop_And8, mkU8(0x78),
7926 unop(Iop_32to8,
7927 unop(Iop_V128to32, mkexpr(vB)))) );
7928 putVReg( vD_addr,
7929 binop(Iop_ShlV128, mkexpr(vA), mkexpr(sh)) );
7930 break;
7931 }
7932
cerion32aad402005-09-10 12:02:24 +00007933
7934 /* Shift Right */
7935 case 0x204: // vsrb (Shift Right B, AV p256)
7936 DIP("vsrb v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion0a7b4f42005-09-16 07:54:40 +00007937 putVReg( vD_addr, binop(Iop_Shr8x16, mkexpr(vA), mkexpr(vB)) );
7938 break;
cerion32aad402005-09-10 12:02:24 +00007939
7940 case 0x244: // vsrh (Shift Right HW, AV p257)
7941 DIP("vsrh v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion0a7b4f42005-09-16 07:54:40 +00007942 putVReg( vD_addr, binop(Iop_Shr16x8, mkexpr(vA), mkexpr(vB)) );
7943 break;
cerion32aad402005-09-10 12:02:24 +00007944
7945 case 0x284: // vsrw (Shift Right W, AV p259)
7946 DIP("vsrw v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion0a7b4f42005-09-16 07:54:40 +00007947 putVReg( vD_addr, binop(Iop_Shr32x4, mkexpr(vA), mkexpr(vB)) );
7948 break;
cerion32aad402005-09-10 12:02:24 +00007949
cerion27b3d7e2005-09-14 20:35:47 +00007950 case 0x2C4: { // vsr (Shift Right, AV p251)
cerion27b3d7e2005-09-14 20:35:47 +00007951 IRTemp sh = newTemp(Ity_I8);
sewardj197bd172005-10-12 11:34:33 +00007952 DIP("vsr v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion27b3d7e2005-09-14 20:35:47 +00007953 assign( sh, binop(Iop_And8, mkU8(0x7),
7954 unop(Iop_32to8,
7955 unop(Iop_V128to32, mkexpr(vB)))) );
7956 putVReg( vD_addr,
7957 binop(Iop_ShrV128, mkexpr(vA), mkexpr(sh)) );
7958 break;
7959 }
cerion5b2325f2005-12-23 00:55:09 +00007960 case 0x304: // vsrab (Shift Right Alg B, AV p253)
cerion32aad402005-09-10 12:02:24 +00007961 DIP("vsrab v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion0a7b4f42005-09-16 07:54:40 +00007962 putVReg( vD_addr, binop(Iop_Sar8x16, mkexpr(vA), mkexpr(vB)) );
7963 break;
cerion32aad402005-09-10 12:02:24 +00007964
cerion5b2325f2005-12-23 00:55:09 +00007965 case 0x344: // vsrah (Shift Right Alg HW, AV p254)
cerion32aad402005-09-10 12:02:24 +00007966 DIP("vsrah v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion0a7b4f42005-09-16 07:54:40 +00007967 putVReg( vD_addr, binop(Iop_Sar16x8, mkexpr(vA), mkexpr(vB)) );
7968 break;
cerion32aad402005-09-10 12:02:24 +00007969
cerion5b2325f2005-12-23 00:55:09 +00007970 case 0x384: // vsraw (Shift Right Alg W, AV p255)
cerion32aad402005-09-10 12:02:24 +00007971 DIP("vsraw v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion0a7b4f42005-09-16 07:54:40 +00007972 putVReg( vD_addr, binop(Iop_Sar32x4, mkexpr(vA), mkexpr(vB)) );
7973 break;
cerion32aad402005-09-10 12:02:24 +00007974
cerion0a7b4f42005-09-16 07:54:40 +00007975 case 0x44C: { // vsro (Shift Right by Octet, AV p258)
cerion0a7b4f42005-09-16 07:54:40 +00007976 IRTemp sh = newTemp(Ity_I8);
sewardj197bd172005-10-12 11:34:33 +00007977 DIP("vsro v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion0a7b4f42005-09-16 07:54:40 +00007978 assign( sh, binop(Iop_And8, mkU8(0x78),
7979 unop(Iop_32to8,
7980 unop(Iop_V128to32, mkexpr(vB)))) );
7981 putVReg( vD_addr,
7982 binop(Iop_ShrV128, mkexpr(vA), mkexpr(sh)) );
7983 break;
7984 }
cerion32aad402005-09-10 12:02:24 +00007985
7986 default:
cerion5b2325f2005-12-23 00:55:09 +00007987 vex_printf("dis_av_shift(ppc)(opc2)\n");
cerion32aad402005-09-10 12:02:24 +00007988 return False;
7989 }
7990 return True;
7991}
7992
7993/*
7994 AltiVec Permute Instructions
7995*/
7996static Bool dis_av_permute ( UInt theInstr )
7997{
cerion76de5cf2005-11-18 18:25:12 +00007998 /* VA-Form, VX-Form */
7999 UChar opc1 = ifieldOPC(theInstr);
8000 UChar vD_addr = ifieldRegDS(theInstr);
8001 UChar vA_addr = ifieldRegA(theInstr);
8002 UChar UIMM_5 = vA_addr;
8003 UChar vB_addr = ifieldRegB(theInstr);
8004 UChar vC_addr = ifieldRegC(theInstr);
8005 UChar b10 = ifieldBIT10(theInstr);
8006 UChar SHB_uimm4 = toUChar( IFIELD( theInstr, 6, 4 ) );
8007 UInt opc2 = toUChar( IFIELD( theInstr, 0, 6 ) );
cerion32aad402005-09-10 12:02:24 +00008008
cerion76de5cf2005-11-18 18:25:12 +00008009 UChar SIMM_8 = extend_s_5to8(UIMM_5);
cerion32aad402005-09-10 12:02:24 +00008010
cerion6e7a0ea2005-09-13 13:34:09 +00008011 IRTemp vA = newTemp(Ity_V128);
8012 IRTemp vB = newTemp(Ity_V128);
8013 IRTemp vC = newTemp(Ity_V128);
8014 assign( vA, getVReg(vA_addr));
8015 assign( vB, getVReg(vB_addr));
8016 assign( vC, getVReg(vC_addr));
8017
cerion32aad402005-09-10 12:02:24 +00008018 if (opc1 != 0x4) {
cerion5b2325f2005-12-23 00:55:09 +00008019 vex_printf("dis_av_permute(ppc)(instr)\n");
cerion32aad402005-09-10 12:02:24 +00008020 return False;
8021 }
8022
8023 switch (opc2) {
8024 case 0x2A: // vsel (Conditional Select, AV p238)
8025 DIP("vsel v%d,v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr, vC_addr);
cerion6e7a0ea2005-09-13 13:34:09 +00008026 /* vD = (vA & ~vC) | (vB & vC) */
8027 putVReg( vD_addr, binop(Iop_OrV128,
8028 binop(Iop_AndV128, mkexpr(vA), unop(Iop_NotV128, mkexpr(vC))),
8029 binop(Iop_AndV128, mkexpr(vB), mkexpr(vC))) );
8030 return True;
cerion32aad402005-09-10 12:02:24 +00008031
cerion92d9d872005-09-15 21:58:50 +00008032 case 0x2B: { // vperm (Permute, AV p218)
cerion92d9d872005-09-15 21:58:50 +00008033 /* limited to two args for IR, so have to play games... */
sewardjdc1f9132005-10-22 12:49:49 +00008034 IRTemp a_perm = newTemp(Ity_V128);
8035 IRTemp b_perm = newTemp(Ity_V128);
8036 IRTemp mask = newTemp(Ity_V128);
8037 IRTemp vC_andF = newTemp(Ity_V128);
cerion5b2325f2005-12-23 00:55:09 +00008038 DIP("vperm v%d,v%d,v%d,v%d\n",
8039 vD_addr, vA_addr, vB_addr, vC_addr);
sewardjdc1f9132005-10-22 12:49:49 +00008040 /* Limit the Perm8x16 steering values to 0 .. 15 as that is what
8041 IR specifies, and also to hide irrelevant bits from
8042 memcheck */
cerion5b2325f2005-12-23 00:55:09 +00008043 assign( vC_andF,
8044 binop(Iop_AndV128, mkexpr(vC),
8045 unop(Iop_Dup8x16, mkU8(0xF))) );
8046 assign( a_perm,
8047 binop(Iop_Perm8x16, mkexpr(vA), mkexpr(vC_andF)) );
8048 assign( b_perm,
8049 binop(Iop_Perm8x16, mkexpr(vB), mkexpr(vC_andF)) );
cerion92d9d872005-09-15 21:58:50 +00008050 // mask[i8] = (vC[i8]_4 == 1) ? 0xFF : 0x0
8051 assign( mask, binop(Iop_SarN8x16,
8052 binop(Iop_ShlN8x16, mkexpr(vC), mkU8(3)),
8053 mkU8(7)) );
8054 // dst = (a & ~mask) | (b & mask)
8055 putVReg( vD_addr, binop(Iop_OrV128,
8056 binop(Iop_AndV128, mkexpr(a_perm),
8057 unop(Iop_NotV128, mkexpr(mask))),
8058 binop(Iop_AndV128, mkexpr(b_perm),
8059 mkexpr(mask))) );
8060 return True;
8061 }
cerion32aad402005-09-10 12:02:24 +00008062 case 0x2C: // vsldoi (Shift Left Double by Octet Imm, AV p241)
8063 if (b10 != 0) {
cerion5b2325f2005-12-23 00:55:09 +00008064 vex_printf("dis_av_permute(ppc)(vsldoi)\n");
cerion32aad402005-09-10 12:02:24 +00008065 return False;
8066 }
cerion5b2325f2005-12-23 00:55:09 +00008067 DIP("vsldoi v%d,v%d,v%d,%d\n",
8068 vD_addr, vA_addr, vB_addr, SHB_uimm4);
cerion92d9d872005-09-15 21:58:50 +00008069 if (SHB_uimm4 == 0)
8070 putVReg( vD_addr, mkexpr(vA) );
8071 else
8072 putVReg( vD_addr,
cerion5b2325f2005-12-23 00:55:09 +00008073 binop(Iop_OrV128,
8074 binop(Iop_ShlV128, mkexpr(vA), mkU8(SHB_uimm4*8)),
8075 binop(Iop_ShrV128, mkexpr(vB), mkU8((16-SHB_uimm4)*8))) );
cerion92d9d872005-09-15 21:58:50 +00008076 return True;
cerion32aad402005-09-10 12:02:24 +00008077
8078 default:
8079 break; // Fall through...
8080 }
8081
cerion76de5cf2005-11-18 18:25:12 +00008082 opc2 = IFIELD( theInstr, 0, 11 );
cerion32aad402005-09-10 12:02:24 +00008083 switch (opc2) {
8084
8085 /* Merge */
8086 case 0x00C: // vmrghb (Merge High B, AV p195)
8087 DIP("vmrghb v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion92d9d872005-09-15 21:58:50 +00008088 putVReg( vD_addr,
8089 binop(Iop_InterleaveHI8x16, mkexpr(vA), mkexpr(vB)) );
8090 break;
cerion32aad402005-09-10 12:02:24 +00008091
8092 case 0x04C: // vmrghh (Merge High HW, AV p196)
8093 DIP("vmrghh v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion92d9d872005-09-15 21:58:50 +00008094 putVReg( vD_addr,
8095 binop(Iop_InterleaveHI16x8, mkexpr(vA), mkexpr(vB)) );
8096 break;
cerion32aad402005-09-10 12:02:24 +00008097
8098 case 0x08C: // vmrghw (Merge High W, AV p197)
8099 DIP("vmrghw v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion92d9d872005-09-15 21:58:50 +00008100 putVReg( vD_addr,
8101 binop(Iop_InterleaveHI32x4, mkexpr(vA), mkexpr(vB)) );
8102 break;
cerion32aad402005-09-10 12:02:24 +00008103
8104 case 0x10C: // vmrglb (Merge Low B, AV p198)
8105 DIP("vmrglb v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion92d9d872005-09-15 21:58:50 +00008106 putVReg( vD_addr,
8107 binop(Iop_InterleaveLO8x16, mkexpr(vA), mkexpr(vB)) );
8108 break;
cerion32aad402005-09-10 12:02:24 +00008109
8110 case 0x14C: // vmrglh (Merge Low HW, AV p199)
8111 DIP("vmrglh v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion92d9d872005-09-15 21:58:50 +00008112 putVReg( vD_addr,
8113 binop(Iop_InterleaveLO16x8, mkexpr(vA), mkexpr(vB)) );
8114 break;
cerion32aad402005-09-10 12:02:24 +00008115
8116 case 0x18C: // vmrglw (Merge Low W, AV p200)
8117 DIP("vmrglw v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion92d9d872005-09-15 21:58:50 +00008118 putVReg( vD_addr,
8119 binop(Iop_InterleaveLO32x4, mkexpr(vA), mkexpr(vB)) );
8120 break;
8121
cerion32aad402005-09-10 12:02:24 +00008122
8123 /* Splat */
cerion92d9d872005-09-15 21:58:50 +00008124 case 0x20C: { // vspltb (Splat Byte, AV p245)
cerion92d9d872005-09-15 21:58:50 +00008125 /* vD = Dup8x16( vB[UIMM_5] ) */
sewardjd1470942005-10-22 02:01:16 +00008126 UChar sh_uimm = (15 - (UIMM_5 & 15)) * 8;
sewardj197bd172005-10-12 11:34:33 +00008127 DIP("vspltb v%d,v%d,%d\n", vD_addr, vB_addr, UIMM_5);
cerion92d9d872005-09-15 21:58:50 +00008128 putVReg( vD_addr, unop(Iop_Dup8x16,
8129 unop(Iop_32to8, unop(Iop_V128to32,
8130 binop(Iop_ShrV128, mkexpr(vB), mkU8(sh_uimm))))) );
8131 break;
8132 }
8133 case 0x24C: { // vsplth (Splat Half Word, AV p246)
sewardjd1470942005-10-22 02:01:16 +00008134 UChar sh_uimm = (7 - (UIMM_5 & 7)) * 16;
sewardj197bd172005-10-12 11:34:33 +00008135 DIP("vsplth v%d,v%d,%d\n", vD_addr, vB_addr, UIMM_5);
cerion92d9d872005-09-15 21:58:50 +00008136 putVReg( vD_addr, unop(Iop_Dup16x8,
8137 unop(Iop_32to16, unop(Iop_V128to32,
8138 binop(Iop_ShrV128, mkexpr(vB), mkU8(sh_uimm))))) );
8139 break;
8140 }
cerion27b3d7e2005-09-14 20:35:47 +00008141 case 0x28C: { // vspltw (Splat Word, AV p250)
cerion27b3d7e2005-09-14 20:35:47 +00008142 /* vD = Dup32x4( vB[UIMM_5] ) */
sewardjd1470942005-10-22 02:01:16 +00008143 UChar sh_uimm = (3 - (UIMM_5 & 3)) * 32;
sewardj197bd172005-10-12 11:34:33 +00008144 DIP("vspltw v%d,v%d,%d\n", vD_addr, vB_addr, UIMM_5);
cerion27b3d7e2005-09-14 20:35:47 +00008145 putVReg( vD_addr, unop(Iop_Dup32x4,
8146 unop(Iop_V128to32,
8147 binop(Iop_ShrV128, mkexpr(vB), mkU8(sh_uimm)))) );
8148 break;
8149 }
cerion32aad402005-09-10 12:02:24 +00008150 case 0x30C: // vspltisb (Splat Immediate Signed B, AV p247)
8151 DIP("vspltisb v%d,%d\n", vD_addr, (Char)SIMM_8);
cerion92d9d872005-09-15 21:58:50 +00008152 putVReg( vD_addr, unop(Iop_Dup8x16, mkU8(SIMM_8)) );
8153 break;
cerion32aad402005-09-10 12:02:24 +00008154
8155 case 0x34C: // vspltish (Splat Immediate Signed HW, AV p248)
8156 DIP("vspltish v%d,%d\n", vD_addr, (Char)SIMM_8);
cerion5b2325f2005-12-23 00:55:09 +00008157 putVReg( vD_addr,
8158 unop(Iop_Dup16x8, mkU16(extend_s_8to32(SIMM_8))) );
cerion92d9d872005-09-15 21:58:50 +00008159 break;
cerion32aad402005-09-10 12:02:24 +00008160
8161 case 0x38C: // vspltisw (Splat Immediate Signed W, AV p249)
8162 DIP("vspltisw v%d,%d\n", vD_addr, (Char)SIMM_8);
cerion5b2325f2005-12-23 00:55:09 +00008163 putVReg( vD_addr,
8164 unop(Iop_Dup32x4, mkU32(extend_s_8to32(SIMM_8))) );
cerion92d9d872005-09-15 21:58:50 +00008165 break;
cerion32aad402005-09-10 12:02:24 +00008166
8167 default:
cerion5b2325f2005-12-23 00:55:09 +00008168 vex_printf("dis_av_permute(ppc)(opc2)\n");
cerion32aad402005-09-10 12:02:24 +00008169 return False;
8170 }
8171 return True;
8172}
8173
8174/*
8175 AltiVec Pack/Unpack Instructions
8176*/
8177static Bool dis_av_pack ( UInt theInstr )
8178{
cerion76de5cf2005-11-18 18:25:12 +00008179 /* VX-Form */
8180 UChar opc1 = ifieldOPC(theInstr);
8181 UChar vD_addr = ifieldRegDS(theInstr);
8182 UChar vA_addr = ifieldRegA(theInstr);
8183 UChar vB_addr = ifieldRegB(theInstr);
8184 UInt opc2 = IFIELD( theInstr, 0, 11 );
cerion32aad402005-09-10 12:02:24 +00008185
sewardj197bd172005-10-12 11:34:33 +00008186 IRTemp signs = IRTemp_INVALID;
8187 IRTemp zeros = IRTemp_INVALID;
cerion76de5cf2005-11-18 18:25:12 +00008188 IRTemp vA = newTemp(Ity_V128);
8189 IRTemp vB = newTemp(Ity_V128);
cerion3c052792005-09-16 07:13:44 +00008190 assign( vA, getVReg(vA_addr));
8191 assign( vB, getVReg(vB_addr));
8192
cerion32aad402005-09-10 12:02:24 +00008193 if (opc1 != 0x4) {
cerion5b2325f2005-12-23 00:55:09 +00008194 vex_printf("dis_av_pack(ppc)(instr)\n");
cerion32aad402005-09-10 12:02:24 +00008195 return False;
8196 }
8197
8198 switch (opc2) {
8199 /* Packing */
8200 case 0x00E: // vpkuhum (Pack Unsigned HW Unsigned Modulo, AV p224)
8201 DIP("vpkuhum v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
sewardj1bee5612005-11-10 18:10:58 +00008202 putVReg( vD_addr, binop(Iop_Narrow16x8, mkexpr(vA), mkexpr(vB)) );
cerion3c052792005-09-16 07:13:44 +00008203 return True;
cerion32aad402005-09-10 12:02:24 +00008204
8205 case 0x04E: // vpkuwum (Pack Unsigned W Unsigned Modulo, AV p226)
8206 DIP("vpkuwum v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
sewardj1bee5612005-11-10 18:10:58 +00008207 putVReg( vD_addr, binop(Iop_Narrow32x4, mkexpr(vA), mkexpr(vB)) );
cerion3c052792005-09-16 07:13:44 +00008208 return True;
cerion32aad402005-09-10 12:02:24 +00008209
8210 case 0x08E: // vpkuhus (Pack Unsigned HW Unsigned Saturate, AV p225)
8211 DIP("vpkuhus v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion5b2325f2005-12-23 00:55:09 +00008212 putVReg( vD_addr,
8213 binop(Iop_QNarrow16Ux8, mkexpr(vA), mkexpr(vB)) );
cerion3c052792005-09-16 07:13:44 +00008214 // TODO: set VSCR[SAT]
8215 return True;
cerion32aad402005-09-10 12:02:24 +00008216
8217 case 0x0CE: // vpkuwus (Pack Unsigned W Unsigned Saturate, AV p227)
8218 DIP("vpkuwus v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion5b2325f2005-12-23 00:55:09 +00008219 putVReg( vD_addr,
8220 binop(Iop_QNarrow32Ux4, mkexpr(vA), mkexpr(vB)) );
cerion3c052792005-09-16 07:13:44 +00008221 // TODO: set VSCR[SAT]
8222 return True;
cerion32aad402005-09-10 12:02:24 +00008223
cerion3c052792005-09-16 07:13:44 +00008224 case 0x10E: { // vpkshus (Pack Signed HW Unsigned Saturate, AV p221)
cerion3c052792005-09-16 07:13:44 +00008225 // This insn does a signed->unsigned saturating conversion.
8226 // Conversion done here, then uses unsigned->unsigned vpk insn:
8227 // => UnsignedSaturatingNarrow( x & ~ (x >>s 15) )
8228 IRTemp vA_tmp = newTemp(Ity_V128);
8229 IRTemp vB_tmp = newTemp(Ity_V128);
sewardj197bd172005-10-12 11:34:33 +00008230 DIP("vpkshus v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion3c052792005-09-16 07:13:44 +00008231 assign( vA_tmp, binop(Iop_AndV128, mkexpr(vA),
8232 unop(Iop_NotV128,
8233 binop(Iop_SarN16x8,
8234 mkexpr(vA), mkU8(15)))) );
8235 assign( vB_tmp, binop(Iop_AndV128, mkexpr(vB),
8236 unop(Iop_NotV128,
8237 binop(Iop_SarN16x8,
8238 mkexpr(vB), mkU8(15)))) );
8239 putVReg( vD_addr, binop(Iop_QNarrow16Ux8,
8240 mkexpr(vA_tmp), mkexpr(vB_tmp)) );
8241 // TODO: set VSCR[SAT]
8242 return True;
8243 }
8244 case 0x14E: { // vpkswus (Pack Signed W Unsigned Saturate, AV p223)
cerion3c052792005-09-16 07:13:44 +00008245 // This insn does a signed->unsigned saturating conversion.
8246 // Conversion done here, then uses unsigned->unsigned vpk insn:
8247 // => UnsignedSaturatingNarrow( x & ~ (x >>s 31) )
8248 IRTemp vA_tmp = newTemp(Ity_V128);
8249 IRTemp vB_tmp = newTemp(Ity_V128);
sewardj197bd172005-10-12 11:34:33 +00008250 DIP("vpkswus v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion3c052792005-09-16 07:13:44 +00008251 assign( vA_tmp, binop(Iop_AndV128, mkexpr(vA),
8252 unop(Iop_NotV128,
8253 binop(Iop_SarN32x4,
8254 mkexpr(vA), mkU8(31)))) );
8255 assign( vB_tmp, binop(Iop_AndV128, mkexpr(vB),
8256 unop(Iop_NotV128,
8257 binop(Iop_SarN32x4,
8258 mkexpr(vB), mkU8(31)))) );
8259 putVReg( vD_addr, binop(Iop_QNarrow32Ux4,
8260 mkexpr(vA_tmp), mkexpr(vB_tmp)) );
8261 // TODO: set VSCR[SAT]
8262 return True;
8263 }
cerion32aad402005-09-10 12:02:24 +00008264 case 0x18E: // vpkshss (Pack Signed HW Signed Saturate, AV p220)
8265 DIP("vpkshss v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion5b2325f2005-12-23 00:55:09 +00008266 putVReg( vD_addr,
8267 binop(Iop_QNarrow16Sx8, mkexpr(vA), mkexpr(vB)) );
cerion3c052792005-09-16 07:13:44 +00008268 // TODO: set VSCR[SAT]
8269 return True;
cerion32aad402005-09-10 12:02:24 +00008270
8271 case 0x1CE: // vpkswss (Pack Signed W Signed Saturate, AV p222)
8272 DIP("vpkswss v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion5b2325f2005-12-23 00:55:09 +00008273 putVReg( vD_addr,
8274 binop(Iop_QNarrow32Sx4, mkexpr(vA), mkexpr(vB)) );
cerion3c052792005-09-16 07:13:44 +00008275 // TODO: set VSCR[SAT]
8276 return True;
cerion32aad402005-09-10 12:02:24 +00008277
cerion3c052792005-09-16 07:13:44 +00008278 case 0x30E: { // vpkpx (Pack Pixel, AV p219)
cerion3c052792005-09-16 07:13:44 +00008279 /* CAB: Worth a new primop? */
cerion5b2325f2005-12-23 00:55:09 +00008280 /* Using shifts to compact pixel elements, then packing them */
cerion3c052792005-09-16 07:13:44 +00008281 IRTemp a1 = newTemp(Ity_V128);
8282 IRTemp a2 = newTemp(Ity_V128);
8283 IRTemp a3 = newTemp(Ity_V128);
8284 IRTemp a_tmp = newTemp(Ity_V128);
8285 IRTemp b1 = newTemp(Ity_V128);
8286 IRTemp b2 = newTemp(Ity_V128);
8287 IRTemp b3 = newTemp(Ity_V128);
8288 IRTemp b_tmp = newTemp(Ity_V128);
sewardj197bd172005-10-12 11:34:33 +00008289 DIP("vpkpx v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion3c052792005-09-16 07:13:44 +00008290 assign( a1, binop(Iop_ShlN16x8,
8291 binop(Iop_ShrN32x4, mkexpr(vA), mkU8(19)),
8292 mkU8(10)) );
8293 assign( a2, binop(Iop_ShlN16x8,
8294 binop(Iop_ShrN16x8, mkexpr(vA), mkU8(11)),
8295 mkU8(5)) );
8296 assign( a3, binop(Iop_ShrN16x8,
8297 binop(Iop_ShlN16x8, mkexpr(vA), mkU8(8)),
8298 mkU8(11)) );
8299 assign( a_tmp, binop(Iop_OrV128, mkexpr(a1),
8300 binop(Iop_OrV128, mkexpr(a2), mkexpr(a3))) );
8301
8302 assign( b1, binop(Iop_ShlN16x8,
8303 binop(Iop_ShrN32x4, mkexpr(vB), mkU8(19)),
8304 mkU8(10)) );
8305 assign( b2, binop(Iop_ShlN16x8,
8306 binop(Iop_ShrN16x8, mkexpr(vB), mkU8(11)),
8307 mkU8(5)) );
8308 assign( b3, binop(Iop_ShrN16x8,
8309 binop(Iop_ShlN16x8, mkexpr(vB), mkU8(8)),
8310 mkU8(11)) );
8311 assign( b_tmp, binop(Iop_OrV128, mkexpr(b1),
8312 binop(Iop_OrV128, mkexpr(b2), mkexpr(b3))) );
8313
sewardj1bee5612005-11-10 18:10:58 +00008314 putVReg( vD_addr, binop(Iop_Narrow32x4,
cerion3c052792005-09-16 07:13:44 +00008315 mkexpr(a_tmp), mkexpr(b_tmp)) );
8316 return True;
8317 }
cerion32aad402005-09-10 12:02:24 +00008318
8319 default:
8320 break; // Fall through...
8321 }
8322
8323
8324 if (vA_addr != 0) {
cerion5b2325f2005-12-23 00:55:09 +00008325 vex_printf("dis_av_pack(ppc)(vA_addr)\n");
cerion32aad402005-09-10 12:02:24 +00008326 return False;
8327 }
8328
sewardj197bd172005-10-12 11:34:33 +00008329 signs = newTemp(Ity_V128);
8330 zeros = newTemp(Ity_V128);
cerion3c052792005-09-16 07:13:44 +00008331 assign( zeros, unop(Iop_Dup32x4, mkU32(0)) );
8332
cerion32aad402005-09-10 12:02:24 +00008333 switch (opc2) {
8334 /* Unpacking */
cerion3c052792005-09-16 07:13:44 +00008335 case 0x20E: { // vupkhsb (Unpack High Signed B, AV p277)
cerion32aad402005-09-10 12:02:24 +00008336 DIP("vupkhsb v%d,v%d\n", vD_addr, vB_addr);
cerion3c052792005-09-16 07:13:44 +00008337 assign( signs, binop(Iop_CmpGT8Sx16, mkexpr(zeros), mkexpr(vB)) );
cerion5b2325f2005-12-23 00:55:09 +00008338 putVReg( vD_addr,
8339 binop(Iop_InterleaveHI8x16, mkexpr(signs), mkexpr(vB)) );
cerion3c052792005-09-16 07:13:44 +00008340 break;
8341 }
8342 case 0x24E: { // vupkhsh (Unpack High Signed HW, AV p278)
cerion32aad402005-09-10 12:02:24 +00008343 DIP("vupkhsh v%d,v%d\n", vD_addr, vB_addr);
cerion3c052792005-09-16 07:13:44 +00008344 assign( signs, binop(Iop_CmpGT16Sx8, mkexpr(zeros), mkexpr(vB)) );
cerion5b2325f2005-12-23 00:55:09 +00008345 putVReg( vD_addr,
8346 binop(Iop_InterleaveHI16x8, mkexpr(signs), mkexpr(vB)) );
cerion3c052792005-09-16 07:13:44 +00008347 break;
8348 }
8349 case 0x28E: { // vupklsb (Unpack Low Signed B, AV p280)
cerion32aad402005-09-10 12:02:24 +00008350 DIP("vupklsb v%d,v%d\n", vD_addr, vB_addr);
cerion3c052792005-09-16 07:13:44 +00008351 assign( signs, binop(Iop_CmpGT8Sx16, mkexpr(zeros), mkexpr(vB)) );
cerion5b2325f2005-12-23 00:55:09 +00008352 putVReg( vD_addr,
8353 binop(Iop_InterleaveLO8x16, mkexpr(signs), mkexpr(vB)) );
cerion3c052792005-09-16 07:13:44 +00008354 break;
8355 }
8356 case 0x2CE: { // vupklsh (Unpack Low Signed HW, AV p281)
cerion32aad402005-09-10 12:02:24 +00008357 DIP("vupklsh v%d,v%d\n", vD_addr, vB_addr);
cerion3c052792005-09-16 07:13:44 +00008358 assign( signs, binop(Iop_CmpGT16Sx8, mkexpr(zeros), mkexpr(vB)) );
cerion5b2325f2005-12-23 00:55:09 +00008359 putVReg( vD_addr,
8360 binop(Iop_InterleaveLO16x8, mkexpr(signs), mkexpr(vB)) );
cerion3c052792005-09-16 07:13:44 +00008361 break;
8362 }
8363 case 0x34E: { // vupkhpx (Unpack High Pixel16, AV p276)
cerion3c052792005-09-16 07:13:44 +00008364 /* CAB: Worth a new primop? */
8365 /* Using shifts to isolate pixel elements, then expanding them */
8366 IRTemp z0 = newTemp(Ity_V128);
8367 IRTemp z1 = newTemp(Ity_V128);
8368 IRTemp z01 = newTemp(Ity_V128);
8369 IRTemp z2 = newTemp(Ity_V128);
8370 IRTemp z3 = newTemp(Ity_V128);
8371 IRTemp z23 = newTemp(Ity_V128);
sewardj197bd172005-10-12 11:34:33 +00008372 DIP("vupkhpx v%d,v%d\n", vD_addr, vB_addr);
cerion3c052792005-09-16 07:13:44 +00008373 assign( z0, binop(Iop_ShlN16x8,
8374 binop(Iop_SarN16x8, mkexpr(vB), mkU8(15)),
8375 mkU8(8)) );
8376 assign( z1, binop(Iop_ShrN16x8,
8377 binop(Iop_ShlN16x8, mkexpr(vB), mkU8(1)),
8378 mkU8(11)) );
8379 assign( z01, binop(Iop_InterleaveHI16x8, mkexpr(zeros),
8380 binop(Iop_OrV128, mkexpr(z0), mkexpr(z1))) );
8381 assign( z2, binop(Iop_ShrN16x8,
8382 binop(Iop_ShlN16x8,
8383 binop(Iop_ShrN16x8, mkexpr(vB), mkU8(5)),
8384 mkU8(11)),
8385 mkU8(3)) );
8386 assign( z3, binop(Iop_ShrN16x8,
8387 binop(Iop_ShlN16x8, mkexpr(vB), mkU8(11)),
8388 mkU8(11)) );
8389 assign( z23, binop(Iop_InterleaveHI16x8, mkexpr(zeros),
8390 binop(Iop_OrV128, mkexpr(z2), mkexpr(z3))) );
cerion5b2325f2005-12-23 00:55:09 +00008391 putVReg( vD_addr,
8392 binop(Iop_OrV128,
8393 binop(Iop_ShlN32x4, mkexpr(z01), mkU8(16)),
8394 mkexpr(z23)) );
cerion3c052792005-09-16 07:13:44 +00008395 break;
8396 }
8397 case 0x3CE: { // vupklpx (Unpack Low Pixel16, AV p279)
cerion3c052792005-09-16 07:13:44 +00008398 /* identical to vupkhpx, except interleaving LO */
8399 IRTemp z0 = newTemp(Ity_V128);
8400 IRTemp z1 = newTemp(Ity_V128);
8401 IRTemp z01 = newTemp(Ity_V128);
8402 IRTemp z2 = newTemp(Ity_V128);
8403 IRTemp z3 = newTemp(Ity_V128);
8404 IRTemp z23 = newTemp(Ity_V128);
sewardj197bd172005-10-12 11:34:33 +00008405 DIP("vupklpx v%d,v%d\n", vD_addr, vB_addr);
cerion3c052792005-09-16 07:13:44 +00008406 assign( z0, binop(Iop_ShlN16x8,
8407 binop(Iop_SarN16x8, mkexpr(vB), mkU8(15)),
8408 mkU8(8)) );
8409 assign( z1, binop(Iop_ShrN16x8,
8410 binop(Iop_ShlN16x8, mkexpr(vB), mkU8(1)),
8411 mkU8(11)) );
8412 assign( z01, binop(Iop_InterleaveLO16x8, mkexpr(zeros),
8413 binop(Iop_OrV128, mkexpr(z0), mkexpr(z1))) );
8414 assign( z2, binop(Iop_ShrN16x8,
8415 binop(Iop_ShlN16x8,
8416 binop(Iop_ShrN16x8, mkexpr(vB), mkU8(5)),
8417 mkU8(11)),
8418 mkU8(3)) );
8419 assign( z3, binop(Iop_ShrN16x8,
8420 binop(Iop_ShlN16x8, mkexpr(vB), mkU8(11)),
8421 mkU8(11)) );
8422 assign( z23, binop(Iop_InterleaveLO16x8, mkexpr(zeros),
8423 binop(Iop_OrV128, mkexpr(z2), mkexpr(z3))) );
cerion5b2325f2005-12-23 00:55:09 +00008424 putVReg( vD_addr,
8425 binop(Iop_OrV128,
8426 binop(Iop_ShlN32x4, mkexpr(z01), mkU8(16)),
8427 mkexpr(z23)) );
cerion3c052792005-09-16 07:13:44 +00008428 break;
8429 }
cerion32aad402005-09-10 12:02:24 +00008430 default:
cerion5b2325f2005-12-23 00:55:09 +00008431 vex_printf("dis_av_pack(ppc)(opc2)\n");
cerion32aad402005-09-10 12:02:24 +00008432 return False;
8433 }
8434 return True;
8435}
8436
8437
8438/*
8439 AltiVec Floating Point Arithmetic Instructions
8440*/
8441static Bool dis_av_fp_arith ( UInt theInstr )
8442{
cerion76de5cf2005-11-18 18:25:12 +00008443 /* VA-Form */
8444 UChar opc1 = ifieldOPC(theInstr);
8445 UChar vD_addr = ifieldRegDS(theInstr);
8446 UChar vA_addr = ifieldRegA(theInstr);
8447 UChar vB_addr = ifieldRegB(theInstr);
8448 UChar vC_addr = ifieldRegC(theInstr);
cerion32aad402005-09-10 12:02:24 +00008449 UInt opc2=0;
8450
cerion8ea0d3e2005-11-14 00:44:47 +00008451 IRTemp vA = newTemp(Ity_V128);
8452 IRTemp vB = newTemp(Ity_V128);
8453 IRTemp vC = newTemp(Ity_V128);
8454 assign( vA, getVReg(vA_addr));
8455 assign( vB, getVReg(vB_addr));
8456 assign( vC, getVReg(vC_addr));
8457
cerion32aad402005-09-10 12:02:24 +00008458 if (opc1 != 0x4) {
cerion5b2325f2005-12-23 00:55:09 +00008459 vex_printf("dis_av_fp_arith(ppc)(instr)\n");
cerion32aad402005-09-10 12:02:24 +00008460 return False;
8461 }
8462
cerion76de5cf2005-11-18 18:25:12 +00008463 opc2 = IFIELD( theInstr, 0, 6 );
cerion32aad402005-09-10 12:02:24 +00008464 switch (opc2) {
8465 case 0x2E: // vmaddfp (Multiply Add FP, AV p177)
cerion5b2325f2005-12-23 00:55:09 +00008466 DIP("vmaddfp v%d,v%d,v%d,v%d\n",
8467 vD_addr, vA_addr, vC_addr, vB_addr);
8468 putVReg( vD_addr,
8469 binop(Iop_Add32Fx4, mkexpr(vB),
8470 binop(Iop_Mul32Fx4, mkexpr(vA), mkexpr(vC))) );
cerionf3f173c2005-11-14 02:37:44 +00008471 return True;
cerion32aad402005-09-10 12:02:24 +00008472
cerionf3f173c2005-11-14 02:37:44 +00008473 case 0x2F: { // vnmsubfp (Negative Multiply-Subtract FP, AV p215)
cerion5b2325f2005-12-23 00:55:09 +00008474 DIP("vnmsubfp v%d,v%d,v%d,v%d\n",
8475 vD_addr, vA_addr, vC_addr, vB_addr);
8476 putVReg( vD_addr,
8477 binop(Iop_Sub32Fx4,
8478 mkexpr(vB),
8479 binop(Iop_Mul32Fx4, mkexpr(vA), mkexpr(vC))) );
cerionf3f173c2005-11-14 02:37:44 +00008480 return True;
8481 }
cerion32aad402005-09-10 12:02:24 +00008482
8483 default:
8484 break; // Fall through...
8485 }
8486
cerion76de5cf2005-11-18 18:25:12 +00008487 opc2 = IFIELD( theInstr, 0, 11 );
cerion32aad402005-09-10 12:02:24 +00008488 switch (opc2) {
8489 case 0x00A: // vaddfp (Add FP, AV p137)
8490 DIP("vaddfp v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion8ea0d3e2005-11-14 00:44:47 +00008491 putVReg( vD_addr, binop(Iop_Add32Fx4, mkexpr(vA), mkexpr(vB)) );
8492 return True;
cerion32aad402005-09-10 12:02:24 +00008493
8494 case 0x04A: // vsubfp (Subtract FP, AV p261)
8495 DIP("vsubfp v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion8ea0d3e2005-11-14 00:44:47 +00008496 putVReg( vD_addr, binop(Iop_Sub32Fx4, mkexpr(vA), mkexpr(vB)) );
8497 return True;
cerion32aad402005-09-10 12:02:24 +00008498
8499 case 0x40A: // vmaxfp (Maximum FP, AV p178)
8500 DIP("vmaxfp v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion8ea0d3e2005-11-14 00:44:47 +00008501 putVReg( vD_addr, binop(Iop_Max32Fx4, mkexpr(vA), mkexpr(vB)) );
8502 return True;
cerion32aad402005-09-10 12:02:24 +00008503
8504 case 0x44A: // vminfp (Minimum FP, AV p187)
8505 DIP("vminfp v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion8ea0d3e2005-11-14 00:44:47 +00008506 putVReg( vD_addr, binop(Iop_Min32Fx4, mkexpr(vA), mkexpr(vB)) );
8507 return True;
cerion32aad402005-09-10 12:02:24 +00008508
8509 default:
8510 break; // Fall through...
8511 }
8512
8513
8514 if (vA_addr != 0) {
cerion5b2325f2005-12-23 00:55:09 +00008515 vex_printf("dis_av_fp_arith(ppc)(vA_addr)\n");
cerion32aad402005-09-10 12:02:24 +00008516 return False;
8517 }
8518
8519 switch (opc2) {
8520 case 0x10A: // vrefp (Reciprocal Esimate FP, AV p228)
8521 DIP("vrefp v%d,v%d\n", vD_addr, vB_addr);
cerion8ea0d3e2005-11-14 00:44:47 +00008522 putVReg( vD_addr, unop(Iop_Recip32Fx4, mkexpr(vB)) );
8523 return True;
cerion32aad402005-09-10 12:02:24 +00008524
cerion5b2325f2005-12-23 00:55:09 +00008525 case 0x14A: // vrsqrtefp (Reciprocal Sqrt Estimate FP, AV p237)
cerion32aad402005-09-10 12:02:24 +00008526 DIP("vrsqrtefp v%d,v%d\n", vD_addr, vB_addr);
cerion8ea0d3e2005-11-14 00:44:47 +00008527 putVReg( vD_addr, unop(Iop_RSqrt32Fx4, mkexpr(vB)) );
8528 return True;
cerion32aad402005-09-10 12:02:24 +00008529
8530 case 0x18A: // vexptefp (2 Raised to the Exp Est FP, AV p173)
8531 DIP("vexptefp v%d,v%d\n", vD_addr, vB_addr);
8532 DIP(" => not implemented\n");
8533 return False;
8534
8535 case 0x1CA: // vlogefp (Log2 Estimate FP, AV p175)
8536 DIP("vlogefp v%d,v%d\n", vD_addr, vB_addr);
8537 DIP(" => not implemented\n");
8538 return False;
8539
8540 default:
cerion5b2325f2005-12-23 00:55:09 +00008541 vex_printf("dis_av_fp_arith(ppc)(opc2=0x%x)\n",opc2);
cerion32aad402005-09-10 12:02:24 +00008542 return False;
8543 }
8544 return True;
8545}
8546
8547/*
8548 AltiVec Floating Point Compare Instructions
8549*/
8550static Bool dis_av_fp_cmp ( UInt theInstr )
8551{
cerion76de5cf2005-11-18 18:25:12 +00008552 /* VXR-Form */
8553 UChar opc1 = ifieldOPC(theInstr);
8554 UChar vD_addr = ifieldRegDS(theInstr);
8555 UChar vA_addr = ifieldRegA(theInstr);
8556 UChar vB_addr = ifieldRegB(theInstr);
8557 UChar flag_rC = ifieldBIT10(theInstr);
8558 UInt opc2 = IFIELD( theInstr, 0, 10 );
cerion32aad402005-09-10 12:02:24 +00008559
cerion8ea0d3e2005-11-14 00:44:47 +00008560 Bool cmp_bounds = False;
8561
8562 IRTemp vA = newTemp(Ity_V128);
8563 IRTemp vB = newTemp(Ity_V128);
8564 IRTemp vD = newTemp(Ity_V128);
8565 assign( vA, getVReg(vA_addr));
8566 assign( vB, getVReg(vB_addr));
8567
cerion32aad402005-09-10 12:02:24 +00008568 if (opc1 != 0x4) {
cerion5b2325f2005-12-23 00:55:09 +00008569 vex_printf("dis_av_fp_cmp(ppc)(instr)\n");
cerion32aad402005-09-10 12:02:24 +00008570 return False;
8571 }
8572
8573 switch (opc2) {
8574 case 0x0C6: // vcmpeqfp (Compare Equal-to FP, AV p159)
cerion5b2325f2005-12-23 00:55:09 +00008575 DIP("vcmpeqfp%s v%d,v%d,v%d\n", (flag_rC ? ".":""),
8576 vD_addr, vA_addr, vB_addr);
cerion8ea0d3e2005-11-14 00:44:47 +00008577 assign( vD, binop(Iop_CmpEQ32Fx4, mkexpr(vA), mkexpr(vB)) );
8578 break;
cerion32aad402005-09-10 12:02:24 +00008579
cerion5b2325f2005-12-23 00:55:09 +00008580 case 0x1C6: // vcmpgefp (Compare Greater-than-or-Equal-to, AV p163)
8581 DIP("vcmpgefp%s v%d,v%d,v%d\n", (flag_rC ? ".":""),
8582 vD_addr, vA_addr, vB_addr);
cerion8ea0d3e2005-11-14 00:44:47 +00008583 assign( vD, binop(Iop_CmpGE32Fx4, mkexpr(vA), mkexpr(vB)) );
8584 break;
cerion32aad402005-09-10 12:02:24 +00008585
8586 case 0x2C6: // vcmpgtfp (Compare Greater-than FP, AV p164)
cerion5b2325f2005-12-23 00:55:09 +00008587 DIP("vcmpgtfp%s v%d,v%d,v%d\n", (flag_rC ? ".":""),
8588 vD_addr, vA_addr, vB_addr);
cerion8ea0d3e2005-11-14 00:44:47 +00008589 assign( vD, binop(Iop_CmpGT32Fx4, mkexpr(vA), mkexpr(vB)) );
8590 break;
cerion32aad402005-09-10 12:02:24 +00008591
cerion8ea0d3e2005-11-14 00:44:47 +00008592 case 0x3C6: { // vcmpbfp (Compare Bounds FP, AV p157)
8593 IRTemp gt = newTemp(Ity_V128);
8594 IRTemp lt = newTemp(Ity_V128);
8595 IRTemp zeros = newTemp(Ity_V128);
cerion5b2325f2005-12-23 00:55:09 +00008596 DIP("vcmpbfp%s v%d,v%d,v%d\n", (flag_rC ? ".":""),
8597 vD_addr, vA_addr, vB_addr);
cerion8ea0d3e2005-11-14 00:44:47 +00008598 cmp_bounds = True;
8599 assign( zeros, unop(Iop_Dup32x4, mkU32(0)) );
8600
8601 /* Note: making use of fact that the ppc backend for compare insns
cerion5b2325f2005-12-23 00:55:09 +00008602 return zero'd lanes if either of the corresponding arg lanes is
8603 a nan.
cerion8ea0d3e2005-11-14 00:44:47 +00008604
8605 Perhaps better to have an irop Iop_isNan32Fx4, but then we'd
8606 need this for the other compares too (vcmpeqfp etc)...
8607 Better still, tighten down the spec for compare irops.
8608 */
8609 assign( gt, unop(Iop_NotV128,
8610 binop(Iop_CmpLE32Fx4, mkexpr(vA), mkexpr(vB))) );
8611 assign( lt, unop(Iop_NotV128,
8612 binop(Iop_CmpGE32Fx4, mkexpr(vA),
cerion5b2325f2005-12-23 00:55:09 +00008613 binop(Iop_Sub32Fx4, mkexpr(zeros),
8614 mkexpr(vB)))) );
cerion8ea0d3e2005-11-14 00:44:47 +00008615
8616 // finally, just shift gt,lt to correct position
8617 assign( vD, binop(Iop_ShlN32x4,
8618 binop(Iop_OrV128,
8619 binop(Iop_AndV128, mkexpr(gt),
8620 unop(Iop_Dup32x4, mkU32(0x2))),
8621 binop(Iop_AndV128, mkexpr(lt),
8622 unop(Iop_Dup32x4, mkU32(0x1)))),
8623 mkU8(30)) );
8624 break;
8625 }
cerion32aad402005-09-10 12:02:24 +00008626
8627 default:
cerion5b2325f2005-12-23 00:55:09 +00008628 vex_printf("dis_av_fp_cmp(ppc)(opc2)\n");
cerion32aad402005-09-10 12:02:24 +00008629 return False;
8630 }
cerion8ea0d3e2005-11-14 00:44:47 +00008631
8632 putVReg( vD_addr, mkexpr(vD) );
8633
cerion76de5cf2005-11-18 18:25:12 +00008634 if (flag_rC) {
cerion8ea0d3e2005-11-14 00:44:47 +00008635 set_AV_CR6( mkexpr(vD), !cmp_bounds );
8636 }
cerion32aad402005-09-10 12:02:24 +00008637 return True;
8638}
8639
8640/*
8641 AltiVec Floating Point Convert/Round Instructions
8642*/
8643static Bool dis_av_fp_convert ( UInt theInstr )
8644{
cerion76de5cf2005-11-18 18:25:12 +00008645 /* VX-Form */
8646 UChar opc1 = ifieldOPC(theInstr);
8647 UChar vD_addr = ifieldRegDS(theInstr);
8648 UChar UIMM_5 = ifieldRegA(theInstr);
8649 UChar vB_addr = ifieldRegB(theInstr);
8650 UInt opc2 = IFIELD( theInstr, 0, 11 );
cerion32aad402005-09-10 12:02:24 +00008651
cerion76de5cf2005-11-18 18:25:12 +00008652 IRTemp vB = newTemp(Ity_V128);
8653 IRTemp vScale = newTemp(Ity_V128);
ceriond963eb42005-11-16 18:02:58 +00008654 IRTemp vInvScale = newTemp(Ity_V128);
sewardj41a7b702005-11-18 22:18:23 +00008655
8656 float scale, inv_scale;
8657
ceriond963eb42005-11-16 18:02:58 +00008658 assign( vB, getVReg(vB_addr));
8659
8660 /* scale = 2^UIMM, cast to float, reinterpreted as uint */
sewardj41a7b702005-11-18 22:18:23 +00008661 scale = (float)( (unsigned int) 1<<UIMM_5 );
sewardj2ead5222005-11-23 03:53:45 +00008662 assign( vScale, unop(Iop_Dup32x4, mkU32( float_to_bits(scale) )) );
sewardj41a7b702005-11-18 22:18:23 +00008663 inv_scale = 1/scale;
cerion5b2325f2005-12-23 00:55:09 +00008664 assign( vInvScale,
8665 unop(Iop_Dup32x4, mkU32( float_to_bits(inv_scale) )) );
ceriond963eb42005-11-16 18:02:58 +00008666
cerion32aad402005-09-10 12:02:24 +00008667 if (opc1 != 0x4) {
cerion5b2325f2005-12-23 00:55:09 +00008668 vex_printf("dis_av_fp_convert(ppc)(instr)\n");
cerion32aad402005-09-10 12:02:24 +00008669 return False;
8670 }
8671
8672 switch (opc2) {
8673 case 0x30A: // vcfux (Convert from Unsigned Fixed-Point W, AV p156)
8674 DIP("vcfux v%d,v%d,%d\n", vD_addr, vB_addr, UIMM_5);
ceriond963eb42005-11-16 18:02:58 +00008675 putVReg( vD_addr, binop(Iop_Mul32Fx4,
8676 unop(Iop_I32UtoFx4, mkexpr(vB)),
8677 mkexpr(vInvScale)) );
8678 return True;
cerion32aad402005-09-10 12:02:24 +00008679
8680 case 0x34A: // vcfsx (Convert from Signed Fixed-Point W, AV p155)
8681 DIP("vcfsx v%d,v%d,%d\n", vD_addr, vB_addr, UIMM_5);
ceriond963eb42005-11-16 18:02:58 +00008682
8683 putVReg( vD_addr, binop(Iop_Mul32Fx4,
8684 unop(Iop_I32StoFx4, mkexpr(vB)),
8685 mkexpr(vInvScale)) );
8686 return True;
cerion32aad402005-09-10 12:02:24 +00008687
8688 case 0x38A: // vctuxs (Convert to Unsigned Fixed-Point W Saturate, AV p172)
8689 DIP("vctuxs v%d,v%d,%d\n", vD_addr, vB_addr, UIMM_5);
ceriond963eb42005-11-16 18:02:58 +00008690 putVReg( vD_addr,
8691 unop(Iop_QFtoI32Ux4_RZ,
8692 binop(Iop_Mul32Fx4, mkexpr(vB), mkexpr(vScale))) );
8693 return True;
cerion32aad402005-09-10 12:02:24 +00008694
8695 case 0x3CA: // vctsxs (Convert to Signed Fixed-Point W Saturate, AV p171)
8696 DIP("vctsxs v%d,v%d,%d\n", vD_addr, vB_addr, UIMM_5);
ceriond963eb42005-11-16 18:02:58 +00008697 putVReg( vD_addr,
8698 unop(Iop_QFtoI32Sx4_RZ,
8699 binop(Iop_Mul32Fx4, mkexpr(vB), mkexpr(vScale))) );
8700 return True;
cerion32aad402005-09-10 12:02:24 +00008701
8702 default:
8703 break; // Fall through...
8704 }
8705
8706 if (UIMM_5 != 0) {
cerion5b2325f2005-12-23 00:55:09 +00008707 vex_printf("dis_av_fp_convert(ppc)(UIMM_5)\n");
cerion32aad402005-09-10 12:02:24 +00008708 return False;
8709 }
8710
8711 switch (opc2) {
8712 case 0x20A: // vrfin (Round to FP Integer Nearest, AV p231)
8713 DIP("vrfin v%d,v%d\n", vD_addr, vB_addr);
ceriond963eb42005-11-16 18:02:58 +00008714 putVReg( vD_addr, unop(Iop_RoundF32x4_RN, mkexpr(vB)) );
8715 break;
cerion32aad402005-09-10 12:02:24 +00008716
8717 case 0x24A: // vrfiz (Round to FP Integer toward zero, AV p233)
8718 DIP("vrfiz v%d,v%d\n", vD_addr, vB_addr);
ceriond963eb42005-11-16 18:02:58 +00008719 putVReg( vD_addr, unop(Iop_RoundF32x4_RZ, mkexpr(vB)) );
8720 break;
cerion32aad402005-09-10 12:02:24 +00008721
8722 case 0x28A: // vrfip (Round to FP Integer toward +inf, AV p232)
8723 DIP("vrfip v%d,v%d\n", vD_addr, vB_addr);
ceriond963eb42005-11-16 18:02:58 +00008724 putVReg( vD_addr, unop(Iop_RoundF32x4_RP, mkexpr(vB)) );
8725 break;
cerion32aad402005-09-10 12:02:24 +00008726
8727 case 0x2CA: // vrfim (Round to FP Integer toward -inf, AV p230)
8728 DIP("vrfim v%d,v%d\n", vD_addr, vB_addr);
ceriond963eb42005-11-16 18:02:58 +00008729 putVReg( vD_addr, unop(Iop_RoundF32x4_RM, mkexpr(vB)) );
8730 break;
cerion32aad402005-09-10 12:02:24 +00008731
8732 default:
cerion5b2325f2005-12-23 00:55:09 +00008733 vex_printf("dis_av_fp_convert(ppc)(opc2)\n");
cerion32aad402005-09-10 12:02:24 +00008734 return False;
8735 }
8736 return True;
8737}
cerion3d870a32005-03-18 12:23:33 +00008738
8739
cerion91ad5362005-01-27 23:02:41 +00008740
8741
8742
8743
cerion896a1372005-01-25 12:24:25 +00008744/*------------------------------------------------------------*/
8745/*--- Disassemble a single instruction ---*/
8746/*------------------------------------------------------------*/
8747
8748/* Disassemble a single instruction into IR. The instruction
sewardj9e6491a2005-07-02 19:24:10 +00008749 is located in host memory at &guest_code[delta]. */
8750
8751static
cerion5b2325f2005-12-23 00:55:09 +00008752DisResult disInstr_PPC_WRK (
sewardj9e6491a2005-07-02 19:24:10 +00008753 Bool put_IP,
sewardjc716aea2006-01-17 01:48:46 +00008754 Bool (*resteerOkFn) ( /*opaque*/void*, Addr64 ),
8755 void* callback_opaque,
sewardj9e6491a2005-07-02 19:24:10 +00008756 Long delta64,
sewardjaca070a2006-10-17 00:28:22 +00008757 VexArchInfo* archinfo,
8758 VexMiscInfo* miscinfo
sewardj9e6491a2005-07-02 19:24:10 +00008759 )
cerion896a1372005-01-25 12:24:25 +00008760{
sewardj9e6491a2005-07-02 19:24:10 +00008761 UChar opc1;
8762 UInt opc2;
8763 DisResult dres;
cerion896a1372005-01-25 12:24:25 +00008764 UInt theInstr;
ceriond953ebb2005-11-29 13:27:20 +00008765 IRType ty = mode64 ? Ity_I64 : Ity_I32;
sewardj5117ce12006-01-27 21:20:15 +00008766 Bool allow_F = False;
8767 Bool allow_V = False;
8768 Bool allow_FX = False;
8769 Bool allow_GX = False;
8770 UInt hwcaps = archinfo->hwcaps;
8771 Long delta;
cerion896a1372005-01-25 12:24:25 +00008772
sewardj059601a2005-11-13 00:53:05 +00008773 /* What insn variants are we supporting today? */
sewardj5117ce12006-01-27 21:20:15 +00008774 if (mode64) {
8775 allow_F = True;
8776 allow_V = (0 != (hwcaps & VEX_HWCAPS_PPC64_V));
8777 allow_FX = (0 != (hwcaps & VEX_HWCAPS_PPC64_FX));
8778 allow_GX = (0 != (hwcaps & VEX_HWCAPS_PPC64_GX));
8779 } else {
8780 allow_F = (0 != (hwcaps & VEX_HWCAPS_PPC32_F));
8781 allow_V = (0 != (hwcaps & VEX_HWCAPS_PPC32_V));
8782 allow_FX = (0 != (hwcaps & VEX_HWCAPS_PPC32_FX));
8783 allow_GX = (0 != (hwcaps & VEX_HWCAPS_PPC32_GX));
8784 }
sewardj059601a2005-11-13 00:53:05 +00008785
sewardj9e6491a2005-07-02 19:24:10 +00008786 /* The running delta */
sewardj5117ce12006-01-27 21:20:15 +00008787 delta = (Long)mkSzAddr(ty, (ULong)delta64);
sewardj9e6491a2005-07-02 19:24:10 +00008788
8789 /* Set result defaults. */
8790 dres.whatNext = Dis_Continue;
8791 dres.len = 0;
8792 dres.continueAt = 0;
cerion896a1372005-01-25 12:24:25 +00008793
cerion1515db92005-01-25 17:21:23 +00008794 /* At least this is simple on PPC32: insns are all 4 bytes long, and
cerion896a1372005-01-25 12:24:25 +00008795 4-aligned. So just fish the whole thing out of memory right now
8796 and have done. */
cerioncf004462005-01-31 15:24:55 +00008797 theInstr = getUIntBigendianly( (UChar*)(&guest_code[delta]) );
cerion896a1372005-01-25 12:24:25 +00008798
sewardj5117ce12006-01-27 21:20:15 +00008799 if (0) vex_printf("insn: 0x%x\n", theInstr);
cerionf0de28c2005-12-13 20:21:11 +00008800
sewardj1eb7e6b2006-01-12 21:13:14 +00008801 DIP("\t0x%llx: ", (ULong)guest_CIA_curr_instr);
sewardjb51f0f42005-07-18 11:38:02 +00008802
8803 /* We may be asked to update the guest CIA before going further. */
8804 if (put_IP)
cerion2831b002005-11-30 19:55:22 +00008805 putGST( PPC_GST_CIA, mkSzImm(ty, guest_CIA_curr_instr) );
cerion896a1372005-01-25 12:24:25 +00008806
sewardjce02aa72006-01-12 12:27:58 +00008807 /* Spot "Special" instructions (see comment at top of file). */
sewardj1eb7e6b2006-01-12 21:13:14 +00008808 {
sewardjce02aa72006-01-12 12:27:58 +00008809 UChar* code = (UChar*)(guest_code + delta);
sewardj1eb7e6b2006-01-12 21:13:14 +00008810 /* Spot the 16-byte preamble:
8811 32-bit mode:
8812 54001800 rlwinm 0,0,3,0,0
8813 54006800 rlwinm 0,0,13,0,0
8814 5400E800 rlwinm 0,0,29,0,0
8815 54009800 rlwinm 0,0,19,0,0
8816 64-bit mode:
8817 78001800 rotldi 0,0,3
8818 78006800 rotldi 0,0,13
8819 7800E802 rotldi 0,0,61
8820 78009802 rotldi 0,0,51
cerion896a1372005-01-25 12:24:25 +00008821 */
sewardj1eb7e6b2006-01-12 21:13:14 +00008822 UInt word1 = mode64 ? 0x78001800 : 0x54001800;
8823 UInt word2 = mode64 ? 0x78006800 : 0x54006800;
8824 UInt word3 = mode64 ? 0x7800E802 : 0x5400E800;
8825 UInt word4 = mode64 ? 0x78009802 : 0x54009800;
8826 if (getUIntBigendianly(code+ 0) == word1 &&
8827 getUIntBigendianly(code+ 4) == word2 &&
8828 getUIntBigendianly(code+ 8) == word3 &&
8829 getUIntBigendianly(code+12) == word4) {
sewardjce02aa72006-01-12 12:27:58 +00008830 /* Got a "Special" instruction preamble. Which one is it? */
8831 if (getUIntBigendianly(code+16) == 0x7C210B78 /* or 1,1,1 */) {
8832 /* %R3 = client_request ( %R4 ) */
8833 DIP("r3 = client_request ( %%r4 )\n");
8834 delta += 20;
8835 irbb->next = mkSzImm( ty, guest_CIA_bbstart + delta );
8836 irbb->jumpkind = Ijk_ClientReq;
8837 dres.whatNext = Dis_StopHere;
8838 goto decode_success;
8839 }
8840 else
8841 if (getUIntBigendianly(code+16) == 0x7C421378 /* or 2,2,2 */) {
8842 /* %R3 = guest_NRADDR */
8843 DIP("r3 = guest_NRADDR\n");
8844 delta += 20;
8845 dres.len = 20;
8846 putIReg(3, IRExpr_Get( OFFB_NRADDR, ty ));
8847 goto decode_success;
8848 }
8849 else
8850 if (getUIntBigendianly(code+16) == 0x7C631B78 /* or 3,3,3 */) {
8851 /* branch-and-link-to-noredir %R11 */
8852 DIP("branch-and-link-to-noredir r11\n");
8853 delta += 20;
sewardj1eb7e6b2006-01-12 21:13:14 +00008854 putGST( PPC_GST_LR, mkSzImm(ty, guest_CIA_bbstart + (Long)delta) );
sewardjce02aa72006-01-12 12:27:58 +00008855 irbb->next = getIReg(11);
8856 irbb->jumpkind = Ijk_NoRedir;
8857 dres.whatNext = Dis_StopHere;
8858 goto decode_success;
8859 }
sewardj5ff11dd2006-01-20 14:19:25 +00008860 else
sewardjaca070a2006-10-17 00:28:22 +00008861 if (getUIntBigendianly(code+16) == 0x7C842378 /* or 4,4,4 */) {
sewardj5ff11dd2006-01-20 14:19:25 +00008862 /* %R3 = guest_NRADDR_GPR2 */
8863 DIP("r3 = guest_NRADDR_GPR2\n");
8864 delta += 20;
8865 dres.len = 20;
sewardjaca070a2006-10-17 00:28:22 +00008866 putIReg(3, IRExpr_Get( OFFB_NRADDR_GPR2, ty ));
sewardj5ff11dd2006-01-20 14:19:25 +00008867 goto decode_success;
8868 }
sewardjce02aa72006-01-12 12:27:58 +00008869 /* We don't know what it is. Set opc1/opc2 so decode_failure
8870 can print the insn following the Special-insn preamble. */
8871 theInstr = getUIntBigendianly(code+16);
8872 opc1 = ifieldOPC(theInstr);
8873 opc2 = ifieldOPClo10(theInstr);
8874 goto decode_failure;
8875 /*NOTREACHED*/
cerion896a1372005-01-25 12:24:25 +00008876 }
8877 }
8878
cerion76de5cf2005-11-18 18:25:12 +00008879 opc1 = ifieldOPC(theInstr);
sewardjb51f0f42005-07-18 11:38:02 +00008880 opc2 = ifieldOPClo10(theInstr);
cerion932ad942005-01-30 10:18:50 +00008881
cerion91ad5362005-01-27 23:02:41 +00008882 // Note: all 'reserved' bits must be cleared, else invalid
8883 switch (opc1) {
cerion896a1372005-01-25 12:24:25 +00008884
cerione9d361a2005-03-04 17:35:29 +00008885 /* Integer Arithmetic Instructions */
8886 case 0x0C: case 0x0D: case 0x0E: // addic, addic., addi
8887 case 0x0F: case 0x07: case 0x08: // addis, mulli, subfic
8888 if (dis_int_arith( theInstr )) goto decode_success;
cerionb85e8bb2005-02-16 08:54:33 +00008889 goto decode_failure;
cerion7aa4bbc2005-01-29 09:32:07 +00008890
cerione9d361a2005-03-04 17:35:29 +00008891 /* Integer Compare Instructions */
8892 case 0x0B: case 0x0A: // cmpi, cmpli
8893 if (dis_int_cmp( theInstr )) goto decode_success;
cerionb85e8bb2005-02-16 08:54:33 +00008894 goto decode_failure;
cerion7aa4bbc2005-01-29 09:32:07 +00008895
cerione9d361a2005-03-04 17:35:29 +00008896 /* Integer Logical Instructions */
8897 case 0x1C: case 0x1D: case 0x18: // andi., andis., ori
8898 case 0x19: case 0x1A: case 0x1B: // oris, xori, xoris
8899 if (dis_int_logic( theInstr )) goto decode_success;
cerionb85e8bb2005-02-16 08:54:33 +00008900 goto decode_failure;
cerion7aa4bbc2005-01-29 09:32:07 +00008901
cerione9d361a2005-03-04 17:35:29 +00008902 /* Integer Rotate Instructions */
8903 case 0x14: case 0x15: case 0x17: // rlwimi, rlwinm, rlwnm
8904 if (dis_int_rot( theInstr )) goto decode_success;
cerionb85e8bb2005-02-16 08:54:33 +00008905 goto decode_failure;
cerion645c9302005-01-31 10:09:59 +00008906
cerionf0de28c2005-12-13 20:21:11 +00008907 /* 64bit Integer Rotate Instructions */
8908 case 0x1E: // rldcl, rldcr, rldic, rldicl, rldicr, rldimi
8909 if (dis_int_rot( theInstr )) goto decode_success;
8910 goto decode_failure;
8911
cerione9d361a2005-03-04 17:35:29 +00008912 /* Integer Load Instructions */
8913 case 0x22: case 0x23: case 0x2A: // lbz, lbzu, lha
8914 case 0x2B: case 0x28: case 0x29: // lhau, lhz, lhzu
8915 case 0x20: case 0x21: // lwz, lwzu
8916 if (dis_int_load( theInstr )) goto decode_success;
cerionb85e8bb2005-02-16 08:54:33 +00008917 goto decode_failure;
cerion645c9302005-01-31 10:09:59 +00008918
cerione9d361a2005-03-04 17:35:29 +00008919 /* Integer Store Instructions */
8920 case 0x26: case 0x27: case 0x2C: // stb, stbu, sth
8921 case 0x2D: case 0x24: case 0x25: // sthu, stw, stwu
sewardjaca070a2006-10-17 00:28:22 +00008922 if (dis_int_store( theInstr, miscinfo )) goto decode_success;
cerionb85e8bb2005-02-16 08:54:33 +00008923 goto decode_failure;
ceriond23be4e2005-01-31 07:23:07 +00008924
sewardj7787af42005-08-04 18:32:19 +00008925 /* Integer Load and Store Multiple Instructions */
8926 case 0x2E: case 0x2F: // lmw, stmw
8927 if (dis_int_ldst_mult( theInstr )) goto decode_success;
8928 goto decode_failure;
cerion645c9302005-01-31 10:09:59 +00008929
cerione9d361a2005-03-04 17:35:29 +00008930 /* Branch Instructions */
8931 case 0x12: case 0x10: // b, bc
sewardjaca070a2006-10-17 00:28:22 +00008932 if (dis_branch(theInstr, miscinfo, &dres,
8933 resteerOkFn, callback_opaque))
sewardjc716aea2006-01-17 01:48:46 +00008934 goto decode_success;
cerionb85e8bb2005-02-16 08:54:33 +00008935 goto decode_failure;
cerion896a1372005-01-25 12:24:25 +00008936
cerione9d361a2005-03-04 17:35:29 +00008937 /* System Linkage Instructions */
cerion8c3adda2005-01-31 11:54:05 +00008938 case 0x11: // sc
sewardjaca070a2006-10-17 00:28:22 +00008939 if (dis_syslink(theInstr, miscinfo, &dres)) goto decode_success;
cerionb85e8bb2005-02-16 08:54:33 +00008940 goto decode_failure;
cerion26d07b22005-02-02 17:13:28 +00008941
sewardj334870d2006-02-07 16:42:39 +00008942 /* Trap Instructions */
8943 case 0x02: case 0x03: // tdi, twi
8944 if (dis_trapi(theInstr, &dres)) goto decode_success;
8945 goto decode_failure;
cerion8c3adda2005-01-31 11:54:05 +00008946
cerion3d870a32005-03-18 12:23:33 +00008947 /* Floating Point Load Instructions */
cerion094d1392005-06-20 13:45:57 +00008948 case 0x30: case 0x31: case 0x32: // lfs, lfsu, lfd
8949 case 0x33: // lfdu
sewardj5117ce12006-01-27 21:20:15 +00008950 if (!allow_F) goto decode_noF;
cerion3d870a32005-03-18 12:23:33 +00008951 if (dis_fp_load( theInstr )) goto decode_success;
cerione9d361a2005-03-04 17:35:29 +00008952 goto decode_failure;
cerion995bc362005-02-03 11:03:31 +00008953
cerion3d870a32005-03-18 12:23:33 +00008954 /* Floating Point Store Instructions */
8955 case 0x34: case 0x35: case 0x36: // stfsx, stfsux, stfdx
8956 case 0x37: // stfdux
sewardj5117ce12006-01-27 21:20:15 +00008957 if (!allow_F) goto decode_noF;
cerion3d870a32005-03-18 12:23:33 +00008958 if (dis_fp_store( theInstr )) goto decode_success;
8959 goto decode_failure;
8960
cerionf0de28c2005-12-13 20:21:11 +00008961 /* 64bit Integer Loads */
8962 case 0x3A: // ld, ldu, lwa
8963 if (!mode64) goto decode_failure;
8964 if (dis_int_load( theInstr )) goto decode_success;
8965 goto decode_failure;
8966
sewardje14bb9f2005-07-22 09:39:02 +00008967 case 0x3B:
sewardj5117ce12006-01-27 21:20:15 +00008968 if (!allow_F) goto decode_noF;
cerion76de5cf2005-11-18 18:25:12 +00008969 opc2 = IFIELD(theInstr, 1, 5);
sewardje14bb9f2005-07-22 09:39:02 +00008970 switch (opc2) {
ceriond953ebb2005-11-29 13:27:20 +00008971 /* Floating Point Arith Instructions */
8972 case 0x12: case 0x14: case 0x15: // fdivs, fsubs, fadds
sewardj5117ce12006-01-27 21:20:15 +00008973 case 0x19: // fmuls
ceriond953ebb2005-11-29 13:27:20 +00008974 if (dis_fp_arith(theInstr)) goto decode_success;
8975 goto decode_failure;
sewardj5117ce12006-01-27 21:20:15 +00008976 case 0x16: // fsqrts
8977 if (!allow_FX) goto decode_noFX;
8978 if (dis_fp_arith(theInstr)) goto decode_success;
8979 goto decode_failure;
8980 case 0x18: // fres
8981 if (!allow_GX) goto decode_noGX;
8982 if (dis_fp_arith(theInstr)) goto decode_success;
8983 goto decode_failure;
8984
ceriond953ebb2005-11-29 13:27:20 +00008985 /* Floating Point Mult-Add Instructions */
8986 case 0x1C: case 0x1D: case 0x1E: // fmsubs, fmadds, fnmsubs
8987 case 0x1F: // fnmadds
8988 if (dis_fp_multadd(theInstr)) goto decode_success;
8989 goto decode_failure;
sewardj79fd33f2006-01-29 17:07:57 +00008990
8991 case 0x1A: // frsqrtes
8992 if (!allow_GX) goto decode_noGX;
8993 if (dis_fp_arith(theInstr)) goto decode_success;
8994 goto decode_failure;
ceriond953ebb2005-11-29 13:27:20 +00008995
8996 default:
8997 goto decode_failure;
sewardje14bb9f2005-07-22 09:39:02 +00008998 }
8999 break;
cerion3d870a32005-03-18 12:23:33 +00009000
cerionf0de28c2005-12-13 20:21:11 +00009001 /* 64bit Integer Stores */
9002 case 0x3E: // std, stdu
9003 if (!mode64) goto decode_failure;
sewardjaca070a2006-10-17 00:28:22 +00009004 if (dis_int_store( theInstr, miscinfo )) goto decode_success;
cerionf0de28c2005-12-13 20:21:11 +00009005 goto decode_failure;
9006
cerion3d870a32005-03-18 12:23:33 +00009007 case 0x3F:
sewardj5117ce12006-01-27 21:20:15 +00009008 if (!allow_F) goto decode_noF;
cerion5b2325f2005-12-23 00:55:09 +00009009 /* Instrs using opc[1:5] never overlap instrs using opc[1:10],
cerion3d870a32005-03-18 12:23:33 +00009010 so we can simply fall through the first switch statement */
9011
cerion76de5cf2005-11-18 18:25:12 +00009012 opc2 = IFIELD(theInstr, 1, 5);
cerion3d870a32005-03-18 12:23:33 +00009013 switch (opc2) {
ceriond953ebb2005-11-29 13:27:20 +00009014 /* Floating Point Arith Instructions */
sewardj5117ce12006-01-27 21:20:15 +00009015 case 0x12: case 0x14: case 0x15: // fdiv, fsub, fadd
9016 case 0x19: // fmul
9017 if (dis_fp_arith(theInstr)) goto decode_success;
9018 goto decode_failure;
9019 case 0x16: // fsqrt
9020 if (!allow_FX) goto decode_noFX;
9021 if (dis_fp_arith(theInstr)) goto decode_success;
9022 goto decode_failure;
9023 case 0x17: case 0x1A: // fsel, frsqrte
9024 if (!allow_GX) goto decode_noGX;
ceriond953ebb2005-11-29 13:27:20 +00009025 if (dis_fp_arith(theInstr)) goto decode_success;
9026 goto decode_failure;
sewardje14bb9f2005-07-22 09:39:02 +00009027
ceriond953ebb2005-11-29 13:27:20 +00009028 /* Floating Point Mult-Add Instructions */
9029 case 0x1C: case 0x1D: case 0x1E: // fmsub, fmadd, fnmsub
9030 case 0x1F: // fnmadd
9031 if (dis_fp_multadd(theInstr)) goto decode_success;
9032 goto decode_failure;
sewardj79fd33f2006-01-29 17:07:57 +00009033
9034 case 0x18: // fre
9035 if (!allow_GX) goto decode_noGX;
9036 if (dis_fp_arith(theInstr)) goto decode_success;
9037 goto decode_failure;
ceriond953ebb2005-11-29 13:27:20 +00009038
9039 default:
9040 break; // Fall through
cerion3d870a32005-03-18 12:23:33 +00009041 }
9042
cerion76de5cf2005-11-18 18:25:12 +00009043 opc2 = IFIELD(theInstr, 1, 10);
sewardje14bb9f2005-07-22 09:39:02 +00009044 switch (opc2) {
ceriond953ebb2005-11-29 13:27:20 +00009045 /* Floating Point Compare Instructions */
9046 case 0x000: // fcmpu
9047 case 0x020: // fcmpo
9048 if (dis_fp_cmp(theInstr)) goto decode_success;
9049 goto decode_failure;
cerion2831b002005-11-30 19:55:22 +00009050
ceriond953ebb2005-11-29 13:27:20 +00009051 /* Floating Point Rounding/Conversion Instructions */
9052 case 0x00C: // frsp
9053 case 0x00E: // fctiw
9054 case 0x00F: // fctiwz
sewardj6be67232006-01-24 19:00:05 +00009055 case 0x32E: // fctid
9056 case 0x32F: // fctidz
9057 case 0x34E: // fcfid
ceriond953ebb2005-11-29 13:27:20 +00009058 if (dis_fp_round(theInstr)) goto decode_success;
9059 goto decode_failure;
9060
9061 /* Floating Point Move Instructions */
9062 case 0x028: // fneg
9063 case 0x048: // fmr
9064 case 0x088: // fnabs
9065 case 0x108: // fabs
9066 if (dis_fp_move( theInstr )) goto decode_success;
9067 goto decode_failure;
sewardje14bb9f2005-07-22 09:39:02 +00009068
ceriond953ebb2005-11-29 13:27:20 +00009069 /* Floating Point Status/Control Register Instructions */
cerion3ea49ee2006-01-04 10:53:00 +00009070 case 0x026: // mtfsb1
sewardj496b88f2006-10-04 17:46:11 +00009071 case 0x040: // mcrfs
ceriond953ebb2005-11-29 13:27:20 +00009072 case 0x046: // mtfsb0
9073 case 0x086: // mtfsfi
9074 case 0x247: // mffs
9075 case 0x2C7: // mtfsf
9076 if (dis_fp_scr( theInstr )) goto decode_success;
9077 goto decode_failure;
cerionf0de28c2005-12-13 20:21:11 +00009078
ceriond953ebb2005-11-29 13:27:20 +00009079 default:
9080 goto decode_failure;
sewardje14bb9f2005-07-22 09:39:02 +00009081 }
cerion3d870a32005-03-18 12:23:33 +00009082 break;
ceriond953ebb2005-11-29 13:27:20 +00009083
cerion91ad5362005-01-27 23:02:41 +00009084 case 0x13:
cerionb85e8bb2005-02-16 08:54:33 +00009085 switch (opc2) {
cerion91ad5362005-01-27 23:02:41 +00009086
ceriond953ebb2005-11-29 13:27:20 +00009087 /* Condition Register Logical Instructions */
9088 case 0x101: case 0x081: case 0x121: // crand, crandc, creqv
9089 case 0x0E1: case 0x021: case 0x1C1: // crnand, crnor, cror
9090 case 0x1A1: case 0x0C1: case 0x000: // crorc, crxor, mcrf
9091 if (dis_cond_logic( theInstr )) goto decode_success;
9092 goto decode_failure;
cerionb85e8bb2005-02-16 08:54:33 +00009093
ceriond953ebb2005-11-29 13:27:20 +00009094 /* Branch Instructions */
9095 case 0x210: case 0x010: // bcctr, bclr
sewardjaca070a2006-10-17 00:28:22 +00009096 if (dis_branch(theInstr, miscinfo, &dres,
9097 resteerOkFn, callback_opaque))
sewardjc716aea2006-01-17 01:48:46 +00009098 goto decode_success;
ceriond953ebb2005-11-29 13:27:20 +00009099 goto decode_failure;
9100
9101 /* Memory Synchronization Instructions */
9102 case 0x096: // isync
9103 if (dis_memsync( theInstr )) goto decode_success;
9104 goto decode_failure;
9105
9106 default:
9107 goto decode_failure;
cerionb85e8bb2005-02-16 08:54:33 +00009108 }
9109 break;
cerion91ad5362005-01-27 23:02:41 +00009110
9111
cerionb85e8bb2005-02-16 08:54:33 +00009112 case 0x1F:
cerione9d361a2005-03-04 17:35:29 +00009113
9114 /* For arith instns, bit10 is the OE flag (overflow enable) */
9115
cerion76de5cf2005-11-18 18:25:12 +00009116 opc2 = IFIELD(theInstr, 1, 9);
cerionb85e8bb2005-02-16 08:54:33 +00009117 switch (opc2) {
ceriond953ebb2005-11-29 13:27:20 +00009118 /* Integer Arithmetic Instructions */
9119 case 0x10A: case 0x00A: case 0x08A: // add, addc, adde
9120 case 0x0EA: case 0x0CA: case 0x1EB: // addme, addze, divw
9121 case 0x1CB: case 0x04B: case 0x00B: // divwu, mulhw, mulhwu
9122 case 0x0EB: case 0x068: case 0x028: // mullw, neg, subf
9123 case 0x008: case 0x088: case 0x0E8: // subfc, subfe, subfme
9124 case 0x0C8: // subfze
9125 if (dis_int_arith( theInstr )) goto decode_success;
9126 goto decode_failure;
cerionf0de28c2005-12-13 20:21:11 +00009127
9128 /* 64bit Integer Arithmetic */
9129 case 0x009: case 0x049: case 0x0E9: // mulhdu, mulhd, mulld
9130 case 0x1C9: case 0x1E9: // divdu, divd
9131 if (!mode64) goto decode_failure;
9132 if (dis_int_arith( theInstr )) goto decode_success;
9133 goto decode_failure;
9134
ceriond953ebb2005-11-29 13:27:20 +00009135 default:
9136 break; // Fall through...
cerionb85e8bb2005-02-16 08:54:33 +00009137 }
cerion91ad5362005-01-27 23:02:41 +00009138
cerione9d361a2005-03-04 17:35:29 +00009139 /* All remaining opcodes use full 10 bits. */
9140
cerion76de5cf2005-11-18 18:25:12 +00009141 opc2 = IFIELD(theInstr, 1, 10);
cerionb85e8bb2005-02-16 08:54:33 +00009142 switch (opc2) {
cerione9d361a2005-03-04 17:35:29 +00009143 /* Integer Compare Instructions */
9144 case 0x000: case 0x020: // cmp, cmpl
9145 if (dis_int_cmp( theInstr )) goto decode_success;
cerionb85e8bb2005-02-16 08:54:33 +00009146 goto decode_failure;
cerion7aa4bbc2005-01-29 09:32:07 +00009147
cerione9d361a2005-03-04 17:35:29 +00009148 /* Integer Logical Instructions */
9149 case 0x01C: case 0x03C: case 0x01A: // and, andc, cntlzw
9150 case 0x11C: case 0x3BA: case 0x39A: // eqv, extsb, extsh
9151 case 0x1DC: case 0x07C: case 0x1BC: // nand, nor, or
9152 case 0x19C: case 0x13C: // orc, xor
9153 if (dis_int_logic( theInstr )) goto decode_success;
cerionb85e8bb2005-02-16 08:54:33 +00009154 goto decode_failure;
cerion932ad942005-01-30 10:18:50 +00009155
cerionf0de28c2005-12-13 20:21:11 +00009156 /* 64bit Integer Logical Instructions */
cerion07b07a92005-12-22 14:32:35 +00009157 case 0x3DA: case 0x03A: // extsw, cntlzd
cerionf0de28c2005-12-13 20:21:11 +00009158 if (!mode64) goto decode_failure;
9159 if (dis_int_logic( theInstr )) goto decode_success;
9160 goto decode_failure;
9161
cerione9d361a2005-03-04 17:35:29 +00009162 /* Integer Shift Instructions */
9163 case 0x018: case 0x318: case 0x338: // slw, sraw, srawi
9164 case 0x218: // srw
9165 if (dis_int_shift( theInstr )) goto decode_success;
cerionb85e8bb2005-02-16 08:54:33 +00009166 goto decode_failure;
cerion7aa4bbc2005-01-29 09:32:07 +00009167
cerionf0de28c2005-12-13 20:21:11 +00009168 /* 64bit Integer Shift Instructions */
9169 case 0x01B: case 0x31A: // sld, srad
cerion07b07a92005-12-22 14:32:35 +00009170 case 0x33A: case 0x33B: // sradi
cerionf0de28c2005-12-13 20:21:11 +00009171 case 0x21B: // srd
9172 if (!mode64) goto decode_failure;
9173 if (dis_int_shift( theInstr )) goto decode_success;
9174 goto decode_failure;
9175
cerione9d361a2005-03-04 17:35:29 +00009176 /* Integer Load Instructions */
9177 case 0x057: case 0x077: case 0x157: // lbzx, lbzux, lhax
9178 case 0x177: case 0x117: case 0x137: // lhaux, lhzx, lhzux
9179 case 0x017: case 0x037: // lwzx, lwzux
9180 if (dis_int_load( theInstr )) goto decode_success;
cerionb85e8bb2005-02-16 08:54:33 +00009181 goto decode_failure;
cerion7aa4bbc2005-01-29 09:32:07 +00009182
cerionf0de28c2005-12-13 20:21:11 +00009183 /* 64bit Integer Load Instructions */
9184 case 0x035: case 0x015: // ldux, ldx
9185 case 0x175: case 0x155: // lwaux, lwax
9186 if (!mode64) goto decode_failure;
9187 if (dis_int_load( theInstr )) goto decode_success;
9188 goto decode_failure;
9189
sewardjb51f0f42005-07-18 11:38:02 +00009190 /* Integer Store Instructions */
cerione9d361a2005-03-04 17:35:29 +00009191 case 0x0F7: case 0x0D7: case 0x1B7: // stbux, stbx, sthux
9192 case 0x197: case 0x0B7: case 0x097: // sthx, stwux, stwx
sewardjaca070a2006-10-17 00:28:22 +00009193 if (dis_int_store( theInstr, miscinfo )) goto decode_success;
cerionb85e8bb2005-02-16 08:54:33 +00009194 goto decode_failure;
cerion91ad5362005-01-27 23:02:41 +00009195
cerionf0de28c2005-12-13 20:21:11 +00009196 /* 64bit Integer Store Instructions */
9197 case 0x0B5: case 0x095: // stdux, stdx
9198 if (!mode64) goto decode_failure;
sewardjaca070a2006-10-17 00:28:22 +00009199 if (dis_int_store( theInstr, miscinfo )) goto decode_success;
cerionf0de28c2005-12-13 20:21:11 +00009200 goto decode_failure;
9201
sewardj602857d2005-09-06 09:10:09 +00009202 /* Integer Load and Store with Byte Reverse Instructions */
9203 case 0x316: case 0x216: case 0x396: // lhbrx, lwbrx, sthbrx
9204 case 0x296: // stwbrx
9205 if (dis_int_ldst_rev( theInstr )) goto decode_success;
9206 goto decode_failure;
9207
sewardj87e651f2005-09-09 08:31:18 +00009208 /* Integer Load and Store String Instructions */
9209 case 0x255: case 0x215: case 0x2D5: // lswi, lswx, stswi
9210 case 0x295: { // stswx
9211 Bool stopHere = False;
9212 Bool ok = dis_int_ldst_str( theInstr, &stopHere );
9213 if (!ok) goto decode_failure;
9214 if (stopHere) {
cerion2831b002005-11-30 19:55:22 +00009215 irbb->next = mkSzImm(ty, nextInsnAddr());
sewardj87e651f2005-09-09 08:31:18 +00009216 irbb->jumpkind = Ijk_Boring;
ceriond953ebb2005-11-29 13:27:20 +00009217 dres.whatNext = Dis_StopHere;
sewardj87e651f2005-09-09 08:31:18 +00009218 }
9219 goto decode_success;
9220 }
cerion645c9302005-01-31 10:09:59 +00009221
cerione9d361a2005-03-04 17:35:29 +00009222 /* Memory Synchronization Instructions */
9223 case 0x356: case 0x014: case 0x096: // eieio, lwarx, stwcx.
9224 case 0x256: // sync
9225 if (dis_memsync( theInstr )) goto decode_success;
cerionb85e8bb2005-02-16 08:54:33 +00009226 goto decode_failure;
9227
cerionf0de28c2005-12-13 20:21:11 +00009228 /* 64bit Memory Synchronization Instructions */
9229 case 0x054: case 0x0D6: // ldarx, stdcx.
9230 if (!mode64) goto decode_failure;
9231 if (dis_memsync( theInstr )) goto decode_success;
9232 goto decode_failure;
9233
cerione9d361a2005-03-04 17:35:29 +00009234 /* Processor Control Instructions */
9235 case 0x200: case 0x013: case 0x153: // mcrxr, mfcr, mfspr
9236 case 0x173: case 0x090: case 0x1D3: // mftb, mtcrf, mtspr
sewardjaca070a2006-10-17 00:28:22 +00009237 if (dis_proc_ctl( miscinfo, theInstr )) goto decode_success;
cerionb85e8bb2005-02-16 08:54:33 +00009238 goto decode_failure;
cerion645c9302005-01-31 10:09:59 +00009239
cerione9d361a2005-03-04 17:35:29 +00009240 /* Cache Management Instructions */
9241 case 0x2F6: case 0x056: case 0x036: // dcba, dcbf, dcbst
9242 case 0x116: case 0x0F6: case 0x3F6: // dcbt, dcbtst, dcbz
9243 case 0x3D6: // icbi
sewardj9e6491a2005-07-02 19:24:10 +00009244 if (dis_cache_manage( theInstr, &dres, archinfo ))
sewardjd94b73a2005-06-30 12:08:48 +00009245 goto decode_success;
cerionb85e8bb2005-02-16 08:54:33 +00009246 goto decode_failure;
ceriond23be4e2005-01-31 07:23:07 +00009247
sewardjb51f0f42005-07-18 11:38:02 +00009248//zz /* External Control Instructions */
9249//zz case 0x136: case 0x1B6: // eciwx, ecowx
9250//zz DIP("external control op => not implemented\n");
9251//zz goto decode_failure;
9252//zz
9253//zz /* Trap Instructions */
9254//zz case 0x004: // tw
9255//zz DIP("trap op (tw) => not implemented\n");
9256//zz goto decode_failure;
cerionf0de28c2005-12-13 20:21:11 +00009257//zz case 0x044: // td
9258//zz DIP("trap op (td) => not implemented\n");
9259//zz goto decode_failure;
sewardje14bb9f2005-07-22 09:39:02 +00009260
9261 /* Floating Point Load Instructions */
9262 case 0x217: case 0x237: case 0x257: // lfsx, lfsux, lfdx
9263 case 0x277: // lfdux
sewardj5117ce12006-01-27 21:20:15 +00009264 if (!allow_F) goto decode_noF;
sewardje14bb9f2005-07-22 09:39:02 +00009265 if (dis_fp_load( theInstr )) goto decode_success;
9266 goto decode_failure;
9267
9268 /* Floating Point Store Instructions */
9269 case 0x297: case 0x2B7: case 0x2D7: // stfs, stfsu, stfd
sewardj5117ce12006-01-27 21:20:15 +00009270 case 0x2F7: // stfdu, stfiwx
9271 if (!allow_F) goto decode_noF;
sewardje14bb9f2005-07-22 09:39:02 +00009272 if (dis_fp_store( theInstr )) goto decode_success;
9273 goto decode_failure;
sewardj5117ce12006-01-27 21:20:15 +00009274 case 0x3D7: // stfiwx
9275 if (!allow_F) goto decode_noF;
9276 if (!allow_GX) goto decode_noGX;
9277 if (dis_fp_store( theInstr )) goto decode_success;
9278 goto decode_failure;
sewardje14bb9f2005-07-22 09:39:02 +00009279
cerion32aad402005-09-10 12:02:24 +00009280 /* AltiVec instructions */
9281
9282 /* AV Cache Control - Data streams */
9283 case 0x156: case 0x176: case 0x336: // dst, dstst, dss
sewardj5117ce12006-01-27 21:20:15 +00009284 if (!allow_V) goto decode_noV;
cerion32aad402005-09-10 12:02:24 +00009285 if (dis_av_datastream( theInstr )) goto decode_success;
9286 goto decode_failure;
ceriona982c052005-06-28 17:23:09 +00009287
9288 /* AV Load */
9289 case 0x006: case 0x026: // lvsl, lvsr
9290 case 0x007: case 0x027: case 0x047: // lvebx, lvehx, lvewx
9291 case 0x067: case 0x167: // lvx, lvxl
sewardj5117ce12006-01-27 21:20:15 +00009292 if (!allow_V) goto decode_noV;
sewardjaca070a2006-10-17 00:28:22 +00009293 if (dis_av_load( miscinfo, theInstr )) goto decode_success;
ceriona982c052005-06-28 17:23:09 +00009294 goto decode_failure;
9295
9296 /* AV Store */
9297 case 0x087: case 0x0A7: case 0x0C7: // stvebx, stvehx, stvewx
9298 case 0x0E7: case 0x1E7: // stvx, stvxl
sewardj5117ce12006-01-27 21:20:15 +00009299 if (!allow_V) goto decode_noV;
ceriona982c052005-06-28 17:23:09 +00009300 if (dis_av_store( theInstr )) goto decode_success;
9301 goto decode_failure;
9302
9303 default:
9304 goto decode_failure;
9305 }
9306 break;
9307
9308
cerion32aad402005-09-10 12:02:24 +00009309 case 0x04:
9310 /* AltiVec instructions */
9311
cerion76de5cf2005-11-18 18:25:12 +00009312 opc2 = IFIELD(theInstr, 0, 6);
cerion32aad402005-09-10 12:02:24 +00009313 switch (opc2) {
9314 /* AV Mult-Add, Mult-Sum */
9315 case 0x20: case 0x21: case 0x22: // vmhaddshs, vmhraddshs, vmladduhm
9316 case 0x24: case 0x25: case 0x26: // vmsumubm, vmsummbm, vmsumuhm
9317 case 0x27: case 0x28: case 0x29: // vmsumuhs, vmsumshm, vmsumshs
sewardj5117ce12006-01-27 21:20:15 +00009318 if (!allow_V) goto decode_noV;
cerion32aad402005-09-10 12:02:24 +00009319 if (dis_av_multarith( theInstr )) goto decode_success;
9320 goto decode_failure;
9321
9322 /* AV Permutations */
9323 case 0x2A: // vsel
9324 case 0x2B: // vperm
cerion32aad402005-09-10 12:02:24 +00009325 case 0x2C: // vsldoi
sewardj5117ce12006-01-27 21:20:15 +00009326 if (!allow_V) goto decode_noV;
cerion92d9d872005-09-15 21:58:50 +00009327 if (dis_av_permute( theInstr )) goto decode_success;
cerion32aad402005-09-10 12:02:24 +00009328 goto decode_failure;
9329
9330 /* AV Floating Point Mult-Add/Sub */
9331 case 0x2E: case 0x2F: // vmaddfp, vnmsubfp
sewardj5117ce12006-01-27 21:20:15 +00009332 if (!allow_V) goto decode_noV;
cerion32aad402005-09-10 12:02:24 +00009333 if (dis_av_fp_arith( theInstr )) goto decode_success;
9334 goto decode_failure;
9335
9336 default:
9337 break; // Fall through...
9338 }
9339
cerion76de5cf2005-11-18 18:25:12 +00009340 opc2 = IFIELD(theInstr, 0, 11);
cerion32aad402005-09-10 12:02:24 +00009341 switch (opc2) {
9342 /* AV Arithmetic */
9343 case 0x180: // vaddcuw
9344 case 0x000: case 0x040: case 0x080: // vaddubm, vadduhm, vadduwm
9345 case 0x200: case 0x240: case 0x280: // vaddubs, vadduhs, vadduws
9346 case 0x300: case 0x340: case 0x380: // vaddsbs, vaddshs, vaddsws
9347 case 0x580: // vsubcuw
9348 case 0x400: case 0x440: case 0x480: // vsububm, vsubuhm, vsubuwm
9349 case 0x600: case 0x640: case 0x680: // vsububs, vsubuhs, vsubuws
9350 case 0x700: case 0x740: case 0x780: // vsubsbs, vsubshs, vsubsws
9351 case 0x402: case 0x442: case 0x482: // vavgub, vavguh, vavguw
9352 case 0x502: case 0x542: case 0x582: // vavgsb, vavgsh, vavgsw
9353 case 0x002: case 0x042: case 0x082: // vmaxub, vmaxuh, vmaxuw
9354 case 0x102: case 0x142: case 0x182: // vmaxsb, vmaxsh, vmaxsw
9355 case 0x202: case 0x242: case 0x282: // vminub, vminuh, vminuw
9356 case 0x302: case 0x342: case 0x382: // vminsb, vminsh, vminsw
9357 case 0x008: case 0x048: // vmuloub, vmulouh
9358 case 0x108: case 0x148: // vmulosb, vmulosh
9359 case 0x208: case 0x248: // vmuleub, vmuleuh
9360 case 0x308: case 0x348: // vmulesb, vmulesh
9361 case 0x608: case 0x708: case 0x648: // vsum4ubs, vsum4sbs, vsum4shs
9362 case 0x688: case 0x788: // vsum2sws, vsumsws
sewardj5117ce12006-01-27 21:20:15 +00009363 if (!allow_V) goto decode_noV;
cerion32aad402005-09-10 12:02:24 +00009364 if (dis_av_arith( theInstr )) goto decode_success;
9365 goto decode_failure;
9366
9367 /* AV Rotate, Shift */
9368 case 0x004: case 0x044: case 0x084: // vrlb, vrlh, vrlw
9369 case 0x104: case 0x144: case 0x184: // vslb, vslh, vslw
9370 case 0x204: case 0x244: case 0x284: // vsrb, vsrh, vsrw
9371 case 0x304: case 0x344: case 0x384: // vsrab, vsrah, vsraw
9372 case 0x1C4: case 0x2C4: // vsl, vsr
9373 case 0x40C: case 0x44C: // vslo, vsro
sewardj5117ce12006-01-27 21:20:15 +00009374 if (!allow_V) goto decode_noV;
cerion32aad402005-09-10 12:02:24 +00009375 if (dis_av_shift( theInstr )) goto decode_success;
9376 goto decode_failure;
9377
9378 /* AV Logic */
9379 case 0x404: case 0x444: case 0x484: // vand, vandc, vor
9380 case 0x4C4: case 0x504: // vxor, vnor
sewardj5117ce12006-01-27 21:20:15 +00009381 if (!allow_V) goto decode_noV;
cerion32aad402005-09-10 12:02:24 +00009382 if (dis_av_logic( theInstr )) goto decode_success;
9383 goto decode_failure;
9384
9385 /* AV Processor Control */
9386 case 0x604: case 0x644: // mfvscr, mtvscr
sewardj5117ce12006-01-27 21:20:15 +00009387 if (!allow_V) goto decode_noV;
cerion32aad402005-09-10 12:02:24 +00009388 if (dis_av_procctl( theInstr )) goto decode_success;
9389 goto decode_failure;
9390
9391 /* AV Floating Point Arithmetic */
9392 case 0x00A: case 0x04A: // vaddfp, vsubfp
9393 case 0x10A: case 0x14A: case 0x18A: // vrefp, vrsqrtefp, vexptefp
9394 case 0x1CA: // vlogefp
9395 case 0x40A: case 0x44A: // vmaxfp, vminfp
sewardj5117ce12006-01-27 21:20:15 +00009396 if (!allow_V) goto decode_noV;
cerion32aad402005-09-10 12:02:24 +00009397 if (dis_av_fp_arith( theInstr )) goto decode_success;
9398 goto decode_failure;
9399
9400 /* AV Floating Point Round/Convert */
9401 case 0x20A: case 0x24A: case 0x28A: // vrfin, vrfiz, vrfip
9402 case 0x2CA: // vrfim
9403 case 0x30A: case 0x34A: case 0x38A: // vcfux, vcfsx, vctuxs
9404 case 0x3CA: // vctsxs
sewardj5117ce12006-01-27 21:20:15 +00009405 if (!allow_V) goto decode_noV;
cerion32aad402005-09-10 12:02:24 +00009406 if (dis_av_fp_convert( theInstr )) goto decode_success;
9407 goto decode_failure;
9408
9409 /* AV Merge, Splat */
9410 case 0x00C: case 0x04C: case 0x08C: // vmrghb, vmrghh, vmrghw
9411 case 0x10C: case 0x14C: case 0x18C: // vmrglb, vmrglh, vmrglw
9412 case 0x20C: case 0x24C: case 0x28C: // vspltb, vsplth, vspltw
9413 case 0x30C: case 0x34C: case 0x38C: // vspltisb, vspltish, vspltisw
sewardj5117ce12006-01-27 21:20:15 +00009414 if (!allow_V) goto decode_noV;
cerion32aad402005-09-10 12:02:24 +00009415 if (dis_av_permute( theInstr )) goto decode_success;
9416 goto decode_failure;
9417
9418 /* AV Pack, Unpack */
9419 case 0x00E: case 0x04E: case 0x08E: // vpkuhum, vpkuwum, vpkuhus
9420 case 0x0CE: // vpkuwus
9421 case 0x10E: case 0x14E: case 0x18E: // vpkshus, vpkswus, vpkshss
9422 case 0x1CE: // vpkswss
9423 case 0x20E: case 0x24E: case 0x28E: // vupkhsb, vupkhsh, vupklsb
9424 case 0x2CE: // vupklsh
9425 case 0x30E: case 0x34E: case 0x3CE: // vpkpx, vupkhpx, vupklpx
sewardj5117ce12006-01-27 21:20:15 +00009426 if (!allow_V) goto decode_noV;
cerion32aad402005-09-10 12:02:24 +00009427 if (dis_av_pack( theInstr )) goto decode_success;
9428 goto decode_failure;
9429
9430 default:
9431 break; // Fall through...
9432 }
9433
cerion76de5cf2005-11-18 18:25:12 +00009434 opc2 = IFIELD(theInstr, 0, 10);
cerion32aad402005-09-10 12:02:24 +00009435 switch (opc2) {
9436
9437 /* AV Compare */
9438 case 0x006: case 0x046: case 0x086: // vcmpequb, vcmpequh, vcmpequw
9439 case 0x206: case 0x246: case 0x286: // vcmpgtub, vcmpgtuh, vcmpgtuw
9440 case 0x306: case 0x346: case 0x386: // vcmpgtsb, vcmpgtsh, vcmpgtsw
sewardj5117ce12006-01-27 21:20:15 +00009441 if (!allow_V) goto decode_noV;
cerion32aad402005-09-10 12:02:24 +00009442 if (dis_av_cmp( theInstr )) goto decode_success;
9443 goto decode_failure;
9444
9445 /* AV Floating Point Compare */
9446 case 0x0C6: case 0x1C6: case 0x2C6: // vcmpeqfp, vcmpgefp, vcmpgtfp
9447 case 0x3C6: // vcmpbfp
sewardj5117ce12006-01-27 21:20:15 +00009448 if (!allow_V) goto decode_noV;
cerion32aad402005-09-10 12:02:24 +00009449 if (dis_av_fp_cmp( theInstr )) goto decode_success;
9450 goto decode_failure;
9451
9452 default:
9453 goto decode_failure;
9454 }
9455 break;
cerion7aa4bbc2005-01-29 09:32:07 +00009456
cerion896a1372005-01-25 12:24:25 +00009457 default:
cerion5b2325f2005-12-23 00:55:09 +00009458 goto decode_failure;
9459
sewardj5117ce12006-01-27 21:20:15 +00009460 decode_noF:
9461 vassert(!allow_F);
9462 vex_printf("disInstr(ppc): declined to decode an FP insn.\n");
9463 goto decode_failure;
9464 decode_noV:
9465 vassert(!allow_V);
9466 vex_printf("disInstr(ppc): declined to decode an AltiVec insn.\n");
9467 goto decode_failure;
9468 decode_noFX:
sewardj7c545862006-01-27 21:52:19 +00009469 vassert(!allow_FX);
sewardj5117ce12006-01-27 21:20:15 +00009470 vex_printf("disInstr(ppc): "
sewardjb183b852006-02-03 16:08:03 +00009471 "declined to decode a GeneralPurpose-Optional insn.\n");
sewardj5117ce12006-01-27 21:20:15 +00009472 goto decode_failure;
9473 decode_noGX:
sewardj7c545862006-01-27 21:52:19 +00009474 vassert(!allow_GX);
sewardj5117ce12006-01-27 21:20:15 +00009475 vex_printf("disInstr(ppc): "
9476 "declined to decode a Graphics-Optional insn.\n");
cerion5b2325f2005-12-23 00:55:09 +00009477 goto decode_failure;
9478
cerion896a1372005-01-25 12:24:25 +00009479 decode_failure:
9480 /* All decode failures end up here. */
cerion225a0342005-09-12 20:49:09 +00009481 opc2 = (theInstr) & 0x7FF;
cerion5b2325f2005-12-23 00:55:09 +00009482 vex_printf("disInstr(ppc): unhandled instruction: "
cerion896a1372005-01-25 12:24:25 +00009483 "0x%x\n", theInstr);
sewardjc7cd2142005-09-09 22:31:49 +00009484 vex_printf(" primary %d(0x%x), secondary %u(0x%x)\n",
sewardjb51f0f42005-07-18 11:38:02 +00009485 opc1, opc1, opc2, opc2);
cerion995bc362005-02-03 11:03:31 +00009486
sewardj01a9e802005-02-01 20:46:00 +00009487 /* Tell the dispatcher that this insn cannot be decoded, and so has
9488 not been executed, and (is currently) the next to be executed.
9489 CIA should be up-to-date since it made so at the start of each
9490 insn, but nevertheless be paranoid and update it again right
9491 now. */
cerion2831b002005-11-30 19:55:22 +00009492 putGST( PPC_GST_CIA, mkSzImm(ty, guest_CIA_curr_instr) );
9493 irbb->next = mkSzImm(ty, guest_CIA_curr_instr);
sewardj01a9e802005-02-01 20:46:00 +00009494 irbb->jumpkind = Ijk_NoDecode;
ceriond953ebb2005-11-29 13:27:20 +00009495 dres.whatNext = Dis_StopHere;
9496 dres.len = 0;
sewardj9e6491a2005-07-02 19:24:10 +00009497 return dres;
cerion896a1372005-01-25 12:24:25 +00009498
9499 } /* switch (opc) for the main (primary) opcode switch. */
9500
9501 decode_success:
9502 /* All decode successes end up here. */
cerion896a1372005-01-25 12:24:25 +00009503 DIP("\n");
9504
sewardjce02aa72006-01-12 12:27:58 +00009505 if (dres.len == 0) {
9506 dres.len = 4;
9507 } else {
9508 vassert(dres.len == 20);
9509 }
sewardj9e6491a2005-07-02 19:24:10 +00009510 return dres;
cerion896a1372005-01-25 12:24:25 +00009511}
9512
9513#undef DIP
9514#undef DIS
9515
sewardj9e6491a2005-07-02 19:24:10 +00009516
9517/*------------------------------------------------------------*/
9518/*--- Top-level fn ---*/
9519/*------------------------------------------------------------*/
9520
9521/* Disassemble a single instruction into IR. The instruction
9522 is located in host memory at &guest_code[delta]. */
9523
cerion5b2325f2005-12-23 00:55:09 +00009524DisResult disInstr_PPC ( IRBB* irbb_IN,
9525 Bool put_IP,
sewardjc716aea2006-01-17 01:48:46 +00009526 Bool (*resteerOkFn) ( void*, Addr64 ),
9527 void* callback_opaque,
cerion5b2325f2005-12-23 00:55:09 +00009528 UChar* guest_code_IN,
9529 Long delta,
9530 Addr64 guest_IP,
sewardja5f55da2006-04-30 23:37:32 +00009531 VexArch guest_arch,
cerion5b2325f2005-12-23 00:55:09 +00009532 VexArchInfo* archinfo,
sewardjaca070a2006-10-17 00:28:22 +00009533 VexMiscInfo* miscinfo,
cerion5b2325f2005-12-23 00:55:09 +00009534 Bool host_bigendian_IN )
sewardj9e6491a2005-07-02 19:24:10 +00009535{
sewardj5df65bb2005-11-29 14:47:04 +00009536 IRType ty;
9537 DisResult dres;
sewardj5117ce12006-01-27 21:20:15 +00009538 UInt mask32, mask64;
9539 UInt hwcaps_guest = archinfo->hwcaps;
9540
sewardja5f55da2006-04-30 23:37:32 +00009541 vassert(guest_arch == VexArchPPC32 || guest_arch == VexArchPPC64);
sewardj5df65bb2005-11-29 14:47:04 +00009542
sewardja5f55da2006-04-30 23:37:32 +00009543 /* global -- ick */
9544 mode64 = guest_arch == VexArchPPC64;
9545 ty = mode64 ? Ity_I64 : Ity_I32;
9546
9547 /* do some sanity checks */
sewardj5117ce12006-01-27 21:20:15 +00009548 mask32 = VEX_HWCAPS_PPC32_F | VEX_HWCAPS_PPC32_V
9549 | VEX_HWCAPS_PPC32_FX | VEX_HWCAPS_PPC32_GX;
9550
sewardj5117ce12006-01-27 21:20:15 +00009551 mask64 = VEX_HWCAPS_PPC64_V
9552 | VEX_HWCAPS_PPC64_FX | VEX_HWCAPS_PPC64_GX;
9553
sewardja5f55da2006-04-30 23:37:32 +00009554 if (mode64) {
9555 vassert((hwcaps_guest & mask32) == 0);
9556 } else {
9557 vassert((hwcaps_guest & mask64) == 0);
9558 }
sewardj9e6491a2005-07-02 19:24:10 +00009559
9560 /* Set globals (see top of this file) */
9561 guest_code = guest_code_IN;
9562 irbb = irbb_IN;
9563 host_is_bigendian = host_bigendian_IN;
ceriond953ebb2005-11-29 13:27:20 +00009564
cerion2831b002005-11-30 19:55:22 +00009565 guest_CIA_curr_instr = mkSzAddr(ty, guest_IP);
9566 guest_CIA_bbstart = mkSzAddr(ty, guest_IP - delta);
sewardj9e6491a2005-07-02 19:24:10 +00009567
sewardjc716aea2006-01-17 01:48:46 +00009568 dres = disInstr_PPC_WRK ( put_IP, resteerOkFn, callback_opaque,
sewardjaca070a2006-10-17 00:28:22 +00009569 delta, archinfo, miscinfo );
sewardj9e6491a2005-07-02 19:24:10 +00009570
9571 return dres;
9572}
9573
9574
sewardjc808ef72005-08-18 11:50:43 +00009575/*------------------------------------------------------------*/
9576/*--- Unused stuff ---*/
9577/*------------------------------------------------------------*/
9578
9579///* A potentially more memcheck-friendly implementation of Clz32, with
9580// the boundary case Clz32(0) = 32, which is what ppc requires. */
9581//
9582//static IRExpr* /* :: Ity_I32 */ verbose_Clz32 ( IRTemp arg )
9583//{
9584// /* Welcome ... to SSA R Us. */
9585// IRTemp n1 = newTemp(Ity_I32);
9586// IRTemp n2 = newTemp(Ity_I32);
9587// IRTemp n3 = newTemp(Ity_I32);
9588// IRTemp n4 = newTemp(Ity_I32);
9589// IRTemp n5 = newTemp(Ity_I32);
9590// IRTemp n6 = newTemp(Ity_I32);
9591// IRTemp n7 = newTemp(Ity_I32);
9592// IRTemp n8 = newTemp(Ity_I32);
9593// IRTemp n9 = newTemp(Ity_I32);
9594// IRTemp n10 = newTemp(Ity_I32);
9595// IRTemp n11 = newTemp(Ity_I32);
9596// IRTemp n12 = newTemp(Ity_I32);
9597//
9598// /* First, propagate the most significant 1-bit into all lower
9599// positions in the word. */
9600// /* unsigned int clz ( unsigned int n )
9601// {
9602// n |= (n >> 1);
9603// n |= (n >> 2);
9604// n |= (n >> 4);
9605// n |= (n >> 8);
9606// n |= (n >> 16);
9607// return bitcount(~n);
9608// }
9609// */
9610// assign(n1, mkexpr(arg));
9611// assign(n2, binop(Iop_Or32, mkexpr(n1), binop(Iop_Shr32, mkexpr(n1), mkU8(1))));
9612// assign(n3, binop(Iop_Or32, mkexpr(n2), binop(Iop_Shr32, mkexpr(n2), mkU8(2))));
9613// assign(n4, binop(Iop_Or32, mkexpr(n3), binop(Iop_Shr32, mkexpr(n3), mkU8(4))));
9614// assign(n5, binop(Iop_Or32, mkexpr(n4), binop(Iop_Shr32, mkexpr(n4), mkU8(8))));
9615// assign(n6, binop(Iop_Or32, mkexpr(n5), binop(Iop_Shr32, mkexpr(n5), mkU8(16))));
9616// /* This gives a word of the form 0---01---1. Now invert it, giving
9617// a word of the form 1---10---0, then do a population-count idiom
9618// (to count the 1s, which is the number of leading zeroes, or 32
9619// if the original word was 0. */
9620// assign(n7, unop(Iop_Not32, mkexpr(n6)));
9621//
9622// /* unsigned int bitcount ( unsigned int n )
9623// {
9624// n = n - ((n >> 1) & 0x55555555);
9625// n = (n & 0x33333333) + ((n >> 2) & 0x33333333);
9626// n = (n + (n >> 4)) & 0x0F0F0F0F;
9627// n = n + (n >> 8);
9628// n = (n + (n >> 16)) & 0x3F;
9629// return n;
9630// }
9631// */
9632// assign(n8,
9633// binop(Iop_Sub32,
9634// mkexpr(n7),
9635// binop(Iop_And32,
9636// binop(Iop_Shr32, mkexpr(n7), mkU8(1)),
9637// mkU32(0x55555555))));
9638// assign(n9,
9639// binop(Iop_Add32,
9640// binop(Iop_And32, mkexpr(n8), mkU32(0x33333333)),
9641// binop(Iop_And32,
9642// binop(Iop_Shr32, mkexpr(n8), mkU8(2)),
9643// mkU32(0x33333333))));
9644// assign(n10,
9645// binop(Iop_And32,
9646// binop(Iop_Add32,
9647// mkexpr(n9),
9648// binop(Iop_Shr32, mkexpr(n9), mkU8(4))),
9649// mkU32(0x0F0F0F0F)));
9650// assign(n11,
9651// binop(Iop_Add32,
9652// mkexpr(n10),
9653// binop(Iop_Shr32, mkexpr(n10), mkU8(8))));
9654// assign(n12,
9655// binop(Iop_Add32,
9656// mkexpr(n11),
9657// binop(Iop_Shr32, mkexpr(n11), mkU8(16))));
9658// return
9659// binop(Iop_And32, mkexpr(n12), mkU32(0x3F));
9660//}
9661
cerion896a1372005-01-25 12:24:25 +00009662/*--------------------------------------------------------------------*/
ceriond0eae2d2005-12-23 11:43:01 +00009663/*--- end guest-ppc/toIR.c ---*/
cerion896a1372005-01-25 12:24:25 +00009664/*--------------------------------------------------------------------*/