blob: 9b9e5a783803ecaa40a8366bf85901f01588af90 [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
78 - 64-bit mode: AbiHints for the stack red zone are only emitted for
79 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
sewardj5ff11dd2006-01-20 14:19:25 +0000105 7C842378 (or 4,4,4) %R3 = guest_NRADDR_GPR2 (64-bit mode only)
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
181// ppc64-linux it isn't (sigh).
182static void* fnptr_to_fnentry( void* f )
183{
184#if defined(__powerpc64__)
185 /* f is a pointer to a 3-word function descriptor, of which
186 the first word is the entry address. */
187 ULong* fdescr = (ULong*)f;
188 return (void*)(fdescr[0]);
189#else
190 return f;
191#endif
192}
193
cerion896a1372005-01-25 12:24:25 +0000194
195/*------------------------------------------------------------*/
196/*--- Debugging output ---*/
197/*------------------------------------------------------------*/
198
199#define DIP(format, args...) \
200 if (vex_traceflags & VEX_TRACE_FE) \
201 vex_printf(format, ## args)
202
203#define DIS(buf, format, args...) \
204 if (vex_traceflags & VEX_TRACE_FE) \
205 vex_sprintf(buf, format, ## args)
206
207
cerion896a1372005-01-25 12:24:25 +0000208/*------------------------------------------------------------*/
ceriond953ebb2005-11-29 13:27:20 +0000209/*--- Offsets of various parts of the ppc32/64 guest state ---*/
cerion896a1372005-01-25 12:24:25 +0000210/*------------------------------------------------------------*/
211
cerion5b2325f2005-12-23 00:55:09 +0000212#define offsetofPPCGuestState(_x) \
213 (mode64 ? offsetof(VexGuestPPC64State, _x) : \
214 offsetof(VexGuestPPC32State, _x))
cerion91ad5362005-01-27 23:02:41 +0000215
cerion5b2325f2005-12-23 00:55:09 +0000216#define OFFB_CIA offsetofPPCGuestState(guest_CIA)
217#define OFFB_LR offsetofPPCGuestState(guest_LR)
218#define OFFB_CTR offsetofPPCGuestState(guest_CTR)
219#define OFFB_XER_SO offsetofPPCGuestState(guest_XER_SO)
220#define OFFB_XER_OV offsetofPPCGuestState(guest_XER_OV)
221#define OFFB_XER_CA offsetofPPCGuestState(guest_XER_CA)
222#define OFFB_XER_BC offsetofPPCGuestState(guest_XER_BC)
223#define OFFB_FPROUND offsetofPPCGuestState(guest_FPROUND)
224#define OFFB_VRSAVE offsetofPPCGuestState(guest_VRSAVE)
225#define OFFB_VSCR offsetofPPCGuestState(guest_VSCR)
226#define OFFB_EMWARN offsetofPPCGuestState(guest_EMWARN)
227#define OFFB_TISTART offsetofPPCGuestState(guest_TISTART)
228#define OFFB_TILEN offsetofPPCGuestState(guest_TILEN)
229#define OFFB_RESVN offsetofPPCGuestState(guest_RESVN)
sewardjce02aa72006-01-12 12:27:58 +0000230#define OFFB_NRADDR offsetofPPCGuestState(guest_NRADDR)
sewardj7787af42005-08-04 18:32:19 +0000231
sewardj5ff11dd2006-01-20 14:19:25 +0000232/* This only exists in the 64-bit guest state */
233#define OFFB64_NRADDR_GPR2 \
234 offsetof(VexGuestPPC64State,guest_NRADDR_GPR2)
235
cerion91ad5362005-01-27 23:02:41 +0000236
cerion38674602005-02-08 02:19:25 +0000237/*------------------------------------------------------------*/
sewardjb51f0f42005-07-18 11:38:02 +0000238/*--- Extract instruction fields --- */
cerion38674602005-02-08 02:19:25 +0000239/*------------------------------------------------------------*/
cerione9d361a2005-03-04 17:35:29 +0000240
cerion76de5cf2005-11-18 18:25:12 +0000241/* Extract field from insn, given idx (zero = lsb) and field length */
242#define IFIELD( insn, idx, len ) ((insn >> idx) & ((1<<len)-1))
243
sewardjb51f0f42005-07-18 11:38:02 +0000244/* Extract primary opcode, instr[31:26] */
cerion76de5cf2005-11-18 18:25:12 +0000245static UChar ifieldOPC( UInt instr ) {
246 return toUChar( IFIELD( instr, 26, 6 ) );
sewardjb51f0f42005-07-18 11:38:02 +0000247}
cerione9d361a2005-03-04 17:35:29 +0000248
cerion76de5cf2005-11-18 18:25:12 +0000249/* Extract 10-bit secondary opcode, instr[10:1] */
sewardjb51f0f42005-07-18 11:38:02 +0000250static UInt ifieldOPClo10 ( UInt instr) {
cerion76de5cf2005-11-18 18:25:12 +0000251 return IFIELD( instr, 1, 10 );
252}
253
254/* Extract 9-bit secondary opcode, instr[9:1] */
255static UInt ifieldOPClo9 ( UInt instr) {
256 return IFIELD( instr, 1, 9 );
257}
258
259/* Extract 5-bit secondary opcode, instr[5:1] */
260static UInt ifieldOPClo5 ( UInt instr) {
261 return IFIELD( instr, 1, 5 );
sewardjb51f0f42005-07-18 11:38:02 +0000262}
cerione9d361a2005-03-04 17:35:29 +0000263
sewardjb51f0f42005-07-18 11:38:02 +0000264/* Extract RD (destination register) field, instr[25:21] */
cerion76de5cf2005-11-18 18:25:12 +0000265static UChar ifieldRegDS( UInt instr ) {
266 return toUChar( IFIELD( instr, 21, 5 ) );
sewardjb51f0f42005-07-18 11:38:02 +0000267}
cerion094d1392005-06-20 13:45:57 +0000268
cerion76de5cf2005-11-18 18:25:12 +0000269/* Extract RA (1st source register) field, instr[20:16] */
270static UChar ifieldRegA ( UInt instr ) {
271 return toUChar( IFIELD( instr, 16, 5 ) );
sewardjb51f0f42005-07-18 11:38:02 +0000272}
273
cerion76de5cf2005-11-18 18:25:12 +0000274/* Extract RB (2nd source register) field, instr[15:11] */
275static UChar ifieldRegB ( UInt instr ) {
276 return toUChar( IFIELD( instr, 11, 5 ) );
sewardjb51f0f42005-07-18 11:38:02 +0000277}
278
cerion76de5cf2005-11-18 18:25:12 +0000279/* Extract RC (3rd source register) field, instr[10:6] */
280static UChar ifieldRegC ( UInt instr ) {
281 return toUChar( IFIELD( instr, 6, 5 ) );
sewardjb51f0f42005-07-18 11:38:02 +0000282}
283
cerion76de5cf2005-11-18 18:25:12 +0000284/* Extract 2nd lowest bit, instr[1] */
285static UChar ifieldBIT10 ( UInt instr ) {
286 return toUChar( IFIELD( instr, 10, 1 ) );
287}
288
289/* Extract 2nd lowest bit, instr[1] */
290static UChar ifieldBIT1 ( UInt instr ) {
291 return toUChar( IFIELD( instr, 1, 1 ) );
292}
293
294/* Extract lowest bit, instr[0] */
295static UChar ifieldBIT0 ( UInt instr ) {
296 return toUChar( instr & 0x1 );
297}
298
299/* Extract unsigned bottom half, instr[15:0] */
300static UInt ifieldUIMM16 ( UInt instr ) {
301 return instr & 0xFFFF;
302}
303
ceriond953ebb2005-11-29 13:27:20 +0000304/* Extract unsigned bottom 26 bits, instr[25:0] */
305static UInt ifieldUIMM26 ( UInt instr ) {
306 return instr & 0x3FFFFFF;
cerion76de5cf2005-11-18 18:25:12 +0000307}
308
sewardjb51f0f42005-07-18 11:38:02 +0000309
cerionedf7fc52005-11-18 20:57:41 +0000310/*------------------------------------------------------------*/
ceriond953ebb2005-11-29 13:27:20 +0000311/*--- Guest-state identifiers ---*/
cerionedf7fc52005-11-18 20:57:41 +0000312/*------------------------------------------------------------*/
sewardje14bb9f2005-07-22 09:39:02 +0000313
cerione9d361a2005-03-04 17:35:29 +0000314typedef enum {
ceriond953ebb2005-11-29 13:27:20 +0000315 PPC_GST_CIA, // Current Instruction Address
316 PPC_GST_LR, // Link Register
317 PPC_GST_CTR, // Count Register
318 PPC_GST_XER, // Overflow, carry flags, byte count
319 PPC_GST_CR, // Condition Register
320 PPC_GST_FPSCR, // Floating Point Status/Control Register
321 PPC_GST_VRSAVE, // Vector Save/Restore Register
322 PPC_GST_VSCR, // Vector Status and Control Register
323 PPC_GST_EMWARN, // Emulation warnings
324 PPC_GST_TISTART,// For icbi: start of area to invalidate
325 PPC_GST_TILEN, // For icbi: length of area to invalidate
326 PPC_GST_RESVN, // For lwarx/stwcx.
327 PPC_GST_MAX
328} PPC_GST;
cerione9d361a2005-03-04 17:35:29 +0000329
cerionedf7fc52005-11-18 20:57:41 +0000330#define MASK_FPSCR_RN 0x3
331#define MASK_VSCR_VALID 0x00010001
sewardje14bb9f2005-07-22 09:39:02 +0000332
cerionedf7fc52005-11-18 20:57:41 +0000333
334/*------------------------------------------------------------*/
335/*--- FP Helpers ---*/
336/*------------------------------------------------------------*/
337
sewardj2ead5222005-11-23 03:53:45 +0000338/* Produce the 32-bit pattern corresponding to the supplied
339 float. */
340static UInt float_to_bits ( Float f )
341{
342 union { UInt i; Float f; } u;
343 vassert(4 == sizeof(UInt));
344 vassert(4 == sizeof(Float));
345 vassert(4 == sizeof(u));
346 u.f = f;
347 return u.i;
348}
349
cerion38674602005-02-08 02:19:25 +0000350
cerion38674602005-02-08 02:19:25 +0000351/*------------------------------------------------------------*/
352/*--- Misc Helpers ---*/
353/*------------------------------------------------------------*/
354
cerionf0de28c2005-12-13 20:21:11 +0000355/* Generate mask with 1's from 'begin' through 'end',
356 wrapping if begin > end.
357 begin->end works from right to left, 0=lsb
358*/
ceriond953ebb2005-11-29 13:27:20 +0000359static UInt MASK32( UInt begin, UInt end )
cerion38674602005-02-08 02:19:25 +0000360{
sewardj63327402006-01-25 03:26:27 +0000361 UInt m1, m2, mask;
ceriond953ebb2005-11-29 13:27:20 +0000362 vassert(begin < 32);
363 vassert(end < 32);
sewardj63327402006-01-25 03:26:27 +0000364 m1 = ((UInt)(-1)) << begin;
365 m2 = ((UInt)(-1)) << end << 1;
366 mask = m1 ^ m2;
cerionb85e8bb2005-02-16 08:54:33 +0000367 if (begin > end) mask = ~mask; // wrap mask
368 return mask;
cerion38674602005-02-08 02:19:25 +0000369}
370
cerion5b2325f2005-12-23 00:55:09 +0000371/* ditto for 64bit mask */
ceriond953ebb2005-11-29 13:27:20 +0000372static ULong MASK64( UInt begin, UInt end )
373{
sewardj63327402006-01-25 03:26:27 +0000374 ULong m1, m2, mask;
ceriond953ebb2005-11-29 13:27:20 +0000375 vassert(begin < 64);
376 vassert(end < 64);
sewardj63327402006-01-25 03:26:27 +0000377 m1 = ((ULong)(-1)) << begin;
378 m2 = ((ULong)(-1)) << end << 1;
379 mask = m1 ^ m2;
ceriond953ebb2005-11-29 13:27:20 +0000380 if (begin > end) mask = ~mask; // wrap mask
381 return mask;
382}
383
cerionf0de28c2005-12-13 20:21:11 +0000384static Addr64 nextInsnAddr( void )
385{
386 return guest_CIA_curr_instr + 4;
387}
ceriond953ebb2005-11-29 13:27:20 +0000388
cerion896a1372005-01-25 12:24:25 +0000389
cerion896a1372005-01-25 12:24:25 +0000390/*------------------------------------------------------------*/
391/*--- Helper bits and pieces for deconstructing the ---*/
ceriond953ebb2005-11-29 13:27:20 +0000392/*--- ppc32/64 insn stream. ---*/
cerion896a1372005-01-25 12:24:25 +0000393/*------------------------------------------------------------*/
394
395/* Add a statement to the list held by "irbb". */
396static void stmt ( IRStmt* st )
397{
398 addStmtToIRBB( irbb, st );
399}
400
cerion896a1372005-01-25 12:24:25 +0000401/* Generate a new temporary of the given type. */
402static IRTemp newTemp ( IRType ty )
403{
sewardj496a58d2005-03-20 18:44:44 +0000404 vassert(isPlausibleIRType(ty));
cerion896a1372005-01-25 12:24:25 +0000405 return newIRTemp( irbb->tyenv, ty );
406}
cerion896a1372005-01-25 12:24:25 +0000407
cerion32aad402005-09-10 12:02:24 +0000408/* Various simple conversions */
409
410static UChar extend_s_5to8 ( UChar x )
411{
412 return toUChar((((Int)x) << 27) >> 27);
413}
414
cerion92d9d872005-09-15 21:58:50 +0000415static UInt extend_s_8to32( UChar x )
416{
417 return (UInt)((((Int)x) << 24) >> 24);
418}
cerion91ad5362005-01-27 23:02:41 +0000419
cerion896a1372005-01-25 12:24:25 +0000420static UInt extend_s_16to32 ( UInt x )
421{
422 return (UInt)((((Int)x) << 16) >> 16);
423}
cerion896a1372005-01-25 12:24:25 +0000424
ceriond953ebb2005-11-29 13:27:20 +0000425static ULong extend_s_16to64 ( UInt x )
426{
427 return (ULong)((((Long)x) << 48) >> 48);
428}
429
430static ULong extend_s_26to64 ( UInt x )
431{
432 return (ULong)((((Long)x) << 38) >> 38);
433}
434
435static ULong extend_s_32to64 ( UInt x )
436{
437 return (ULong)((((Long)x) << 32) >> 32);
438}
439
sewardj684aa952005-01-30 12:52:14 +0000440/* Do a big-endian load of a 32-bit word, regardless of the endianness
441 of the underlying host. */
cerioncf004462005-01-31 15:24:55 +0000442static UInt getUIntBigendianly ( UChar* p )
sewardj684aa952005-01-30 12:52:14 +0000443{
cerioncf004462005-01-31 15:24:55 +0000444 UInt w = 0;
sewardj684aa952005-01-30 12:52:14 +0000445 w = (w << 8) | p[0];
446 w = (w << 8) | p[1];
447 w = (w << 8) | p[2];
448 w = (w << 8) | p[3];
449 return w;
450}
451
cerion896a1372005-01-25 12:24:25 +0000452
453/*------------------------------------------------------------*/
454/*--- Helpers for constructing IR. ---*/
455/*------------------------------------------------------------*/
456
cerion896a1372005-01-25 12:24:25 +0000457static void assign ( IRTemp dst, IRExpr* e )
458{
459 stmt( IRStmt_Tmp(dst, e) );
460}
461
cerionae694622005-01-28 17:52:47 +0000462static void storeBE ( IRExpr* addr, IRExpr* data )
cerion896a1372005-01-25 12:24:25 +0000463{
ceriond953ebb2005-11-29 13:27:20 +0000464 vassert(typeOfIRExpr(irbb->tyenv, addr) == Ity_I32 ||
465 typeOfIRExpr(irbb->tyenv, addr) == Ity_I64);
sewardjaf1ceca2005-06-30 23:31:27 +0000466 stmt( IRStmt_Store(Iend_BE,addr,data) );
cerion896a1372005-01-25 12:24:25 +0000467}
468
469static IRExpr* unop ( IROp op, IRExpr* a )
470{
471 return IRExpr_Unop(op, a);
472}
473
474static IRExpr* binop ( IROp op, IRExpr* a1, IRExpr* a2 )
475{
476 return IRExpr_Binop(op, a1, a2);
477}
478
sewardjb183b852006-02-03 16:08:03 +0000479static IRExpr* triop ( IROp op, IRExpr* a1, IRExpr* a2, IRExpr* a3 )
480{
481 return IRExpr_Triop(op, a1, a2, a3);
482}
483
sewardj40c80262006-02-08 19:30:46 +0000484static IRExpr* qop ( IROp op, IRExpr* a1, IRExpr* a2,
485 IRExpr* a3, IRExpr* a4 )
486{
487 return IRExpr_Qop(op, a1, a2, a3, a4);
488}
489
cerion896a1372005-01-25 12:24:25 +0000490static IRExpr* mkexpr ( IRTemp tmp )
491{
492 return IRExpr_Tmp(tmp);
493}
494
sewardj684c0372005-02-07 02:33:58 +0000495static IRExpr* mkU8 ( UChar i )
cerion896a1372005-01-25 12:24:25 +0000496{
cerion896a1372005-01-25 12:24:25 +0000497 return IRExpr_Const(IRConst_U8(i));
498}
cerion896a1372005-01-25 12:24:25 +0000499
cerion92d9d872005-09-15 21:58:50 +0000500static IRExpr* mkU16 ( UInt i )
501{
502 return IRExpr_Const(IRConst_U16(i));
503}
504
cerion896a1372005-01-25 12:24:25 +0000505static IRExpr* mkU32 ( UInt i )
506{
507 return IRExpr_Const(IRConst_U32(i));
508}
509
cerion4a49b032005-11-08 16:23:07 +0000510static IRExpr* mkU64 ( ULong i )
511{
512 return IRExpr_Const(IRConst_U64(i));
513}
514
cerionae694622005-01-28 17:52:47 +0000515static IRExpr* loadBE ( IRType ty, IRExpr* data )
cerion896a1372005-01-25 12:24:25 +0000516{
sewardjaf1ceca2005-06-30 23:31:27 +0000517 return IRExpr_Load(Iend_BE,ty,data);
cerion896a1372005-01-25 12:24:25 +0000518}
cerion896a1372005-01-25 12:24:25 +0000519
sewardj20ef5472005-07-21 14:48:31 +0000520static IRExpr* mkOR1 ( IRExpr* arg1, IRExpr* arg2 )
521{
522 vassert(typeOfIRExpr(irbb->tyenv, arg1) == Ity_I1);
523 vassert(typeOfIRExpr(irbb->tyenv, arg2) == Ity_I1);
cerion5b2325f2005-12-23 00:55:09 +0000524 return unop(Iop_32to1, binop(Iop_Or32, unop(Iop_1Uto32, arg1),
525 unop(Iop_1Uto32, arg2)));
sewardj20ef5472005-07-21 14:48:31 +0000526}
527
528static IRExpr* mkAND1 ( IRExpr* arg1, IRExpr* arg2 )
529{
530 vassert(typeOfIRExpr(irbb->tyenv, arg1) == Ity_I1);
531 vassert(typeOfIRExpr(irbb->tyenv, arg2) == Ity_I1);
cerion5b2325f2005-12-23 00:55:09 +0000532 return unop(Iop_32to1, binop(Iop_And32, unop(Iop_1Uto32, arg1),
533 unop(Iop_1Uto32, arg2)));
sewardj20ef5472005-07-21 14:48:31 +0000534}
sewardjb51f0f42005-07-18 11:38:02 +0000535
cerion4a49b032005-11-08 16:23:07 +0000536/* expand V128_8Ux16 to 2x V128_16Ux8's */
cerion5b2325f2005-12-23 00:55:09 +0000537static void expand8Ux16( IRExpr* vIn,
538 /*OUTs*/ IRTemp* vEvn, IRTemp* vOdd )
cerion4a49b032005-11-08 16:23:07 +0000539{
540 IRTemp ones8x16 = newTemp(Ity_V128);
541
542 vassert(typeOfIRExpr(irbb->tyenv, vIn) == Ity_V128);
543 vassert(vEvn && *vEvn == IRTemp_INVALID);
544 vassert(vOdd && *vOdd == IRTemp_INVALID);
545 *vEvn = newTemp(Ity_V128);
546 *vOdd = newTemp(Ity_V128);
547
548 assign( ones8x16, unop(Iop_Dup8x16, mkU8(0x1)) );
cerion24d06f12005-11-09 21:34:20 +0000549 assign( *vOdd, binop(Iop_MullEven8Ux16, mkexpr(ones8x16), vIn) );
550 assign( *vEvn, binop(Iop_MullEven8Ux16, mkexpr(ones8x16),
551 binop(Iop_ShrV128, vIn, mkU8(8))) );
cerion4a49b032005-11-08 16:23:07 +0000552}
553
554/* expand V128_8Sx16 to 2x V128_16Sx8's */
cerion5b2325f2005-12-23 00:55:09 +0000555static void expand8Sx16( IRExpr* vIn,
556 /*OUTs*/ IRTemp* vEvn, IRTemp* vOdd )
cerion4a49b032005-11-08 16:23:07 +0000557{
558 IRTemp ones8x16 = newTemp(Ity_V128);
559
560 vassert(typeOfIRExpr(irbb->tyenv, vIn) == Ity_V128);
561 vassert(vEvn && *vEvn == IRTemp_INVALID);
562 vassert(vOdd && *vOdd == IRTemp_INVALID);
563 *vEvn = newTemp(Ity_V128);
564 *vOdd = newTemp(Ity_V128);
565
566 assign( ones8x16, unop(Iop_Dup8x16, mkU8(0x1)) );
cerion24d06f12005-11-09 21:34:20 +0000567 assign( *vOdd, binop(Iop_MullEven8Sx16, mkexpr(ones8x16), vIn) );
568 assign( *vEvn, binop(Iop_MullEven8Sx16, mkexpr(ones8x16),
569 binop(Iop_ShrV128, vIn, mkU8(8))) );
cerion4a49b032005-11-08 16:23:07 +0000570}
571
572/* expand V128_16Uto8 to 2x V128_32Ux4's */
cerion5b2325f2005-12-23 00:55:09 +0000573static void expand16Ux8( IRExpr* vIn,
574 /*OUTs*/ IRTemp* vEvn, IRTemp* vOdd )
cerion4a49b032005-11-08 16:23:07 +0000575{
576 IRTemp ones16x8 = newTemp(Ity_V128);
577
578 vassert(typeOfIRExpr(irbb->tyenv, vIn) == Ity_V128);
579 vassert(vEvn && *vEvn == IRTemp_INVALID);
580 vassert(vOdd && *vOdd == IRTemp_INVALID);
581 *vEvn = newTemp(Ity_V128);
582 *vOdd = newTemp(Ity_V128);
583
584 assign( ones16x8, unop(Iop_Dup16x8, mkU16(0x1)) );
cerion24d06f12005-11-09 21:34:20 +0000585 assign( *vOdd, binop(Iop_MullEven16Ux8, mkexpr(ones16x8), vIn) );
586 assign( *vEvn, binop(Iop_MullEven16Ux8, mkexpr(ones16x8),
587 binop(Iop_ShrV128, vIn, mkU8(16))) );
cerion4a49b032005-11-08 16:23:07 +0000588}
589
590/* expand V128_16Sto8 to 2x V128_32Sx4's */
cerion5b2325f2005-12-23 00:55:09 +0000591static void expand16Sx8( IRExpr* vIn,
592 /*OUTs*/ IRTemp* vEvn, IRTemp* vOdd )
cerion4a49b032005-11-08 16:23:07 +0000593{
594 IRTemp ones16x8 = newTemp(Ity_V128);
595
596 vassert(typeOfIRExpr(irbb->tyenv, vIn) == Ity_V128);
597 vassert(vEvn && *vEvn == IRTemp_INVALID);
598 vassert(vOdd && *vOdd == IRTemp_INVALID);
599 *vEvn = newTemp(Ity_V128);
600 *vOdd = newTemp(Ity_V128);
601
602 assign( ones16x8, unop(Iop_Dup16x8, mkU16(0x1)) );
cerion24d06f12005-11-09 21:34:20 +0000603 assign( *vOdd, binop(Iop_MullEven16Sx8, mkexpr(ones16x8), vIn) );
604 assign( *vEvn, binop(Iop_MullEven16Sx8, mkexpr(ones16x8),
605 binop(Iop_ShrV128, vIn, mkU8(16))) );
cerion4a49b032005-11-08 16:23:07 +0000606}
607
608/* break V128 to 4xI32's, then sign-extend to I64's */
609static void breakV128to4x64S( IRExpr* t128,
610 /*OUTs*/
611 IRTemp* t3, IRTemp* t2,
612 IRTemp* t1, IRTemp* t0 )
613{
614 IRTemp hi64 = newTemp(Ity_I64);
615 IRTemp lo64 = newTemp(Ity_I64);
616
617 vassert(typeOfIRExpr(irbb->tyenv, t128) == Ity_V128);
618 vassert(t0 && *t0 == IRTemp_INVALID);
619 vassert(t1 && *t1 == IRTemp_INVALID);
620 vassert(t2 && *t2 == IRTemp_INVALID);
621 vassert(t3 && *t3 == IRTemp_INVALID);
622 *t0 = newTemp(Ity_I64);
623 *t1 = newTemp(Ity_I64);
624 *t2 = newTemp(Ity_I64);
625 *t3 = newTemp(Ity_I64);
626
627 assign( hi64, unop(Iop_V128HIto64, t128) );
628 assign( lo64, unop(Iop_V128to64, t128) );
629 assign( *t3, unop(Iop_32Sto64, unop(Iop_64HIto32, mkexpr(hi64))) );
630 assign( *t2, unop(Iop_32Sto64, unop(Iop_64to32, mkexpr(hi64))) );
631 assign( *t1, unop(Iop_32Sto64, unop(Iop_64HIto32, mkexpr(lo64))) );
632 assign( *t0, unop(Iop_32Sto64, unop(Iop_64to32, mkexpr(lo64))) );
633}
634
635/* break V128 to 4xI32's, then zero-extend to I64's */
636static void breakV128to4x64U ( IRExpr* t128,
637 /*OUTs*/
638 IRTemp* t3, IRTemp* t2,
639 IRTemp* t1, IRTemp* t0 )
640{
641 IRTemp hi64 = newTemp(Ity_I64);
642 IRTemp lo64 = newTemp(Ity_I64);
643
644 vassert(typeOfIRExpr(irbb->tyenv, t128) == Ity_V128);
645 vassert(t0 && *t0 == IRTemp_INVALID);
646 vassert(t1 && *t1 == IRTemp_INVALID);
647 vassert(t2 && *t2 == IRTemp_INVALID);
648 vassert(t3 && *t3 == IRTemp_INVALID);
649 *t0 = newTemp(Ity_I64);
650 *t1 = newTemp(Ity_I64);
651 *t2 = newTemp(Ity_I64);
652 *t3 = newTemp(Ity_I64);
653
654 assign( hi64, unop(Iop_V128HIto64, t128) );
655 assign( lo64, unop(Iop_V128to64, t128) );
656 assign( *t3, unop(Iop_32Uto64, unop(Iop_64HIto32, mkexpr(hi64))) );
657 assign( *t2, unop(Iop_32Uto64, unop(Iop_64to32, mkexpr(hi64))) );
658 assign( *t1, unop(Iop_32Uto64, unop(Iop_64HIto32, mkexpr(lo64))) );
659 assign( *t0, unop(Iop_32Uto64, unop(Iop_64to32, mkexpr(lo64))) );
660}
661
662/* Signed saturating narrow 64S to 32 */
663static IRExpr* mkQNarrow64Sto32 ( IRExpr* t64 )
664{
665 IRTemp hi32 = newTemp(Ity_I32);
666 IRTemp lo32 = newTemp(Ity_I32);
667
668 vassert(typeOfIRExpr(irbb->tyenv, t64) == Ity_I64);
669
670 assign( hi32, unop(Iop_64HIto32, t64));
671 assign( lo32, unop(Iop_64to32, t64));
672
673 return IRExpr_Mux0X(
674 /* if (hi32 == (lo32 >>s 31)) */
675 unop(Iop_1Uto8,
676 binop(Iop_CmpEQ32, mkexpr(hi32),
677 binop( Iop_Sar32, mkexpr(lo32), mkU8(31)))),
678 /* else: sign dep saturate: 1->0x80000000, 0->0x7FFFFFFF */
679 binop(Iop_Add32, mkU32(0x7FFFFFFF),
680 binop(Iop_Shr32, mkexpr(hi32), mkU8(31))),
681 /* then: within signed-32 range: lo half good enough */
682 mkexpr(lo32) );
683}
684
685/* Unsigned saturating narrow 64S to 32 */
686static IRExpr* mkQNarrow64Uto32 ( IRExpr* t64 )
687{
688 IRTemp hi32 = newTemp(Ity_I32);
689 IRTemp lo32 = newTemp(Ity_I32);
690
691 vassert(typeOfIRExpr(irbb->tyenv, t64) == Ity_I64);
692
693 assign( hi32, unop(Iop_64HIto32, t64));
694 assign( lo32, unop(Iop_64to32, t64));
695
696 return IRExpr_Mux0X(
697 /* if (top 32 bits of t64 are 0) */
698 unop(Iop_1Uto8, binop(Iop_CmpEQ32, mkexpr(hi32), mkU32(0))),
699 /* else: positive saturate -> 0xFFFFFFFF */
700 mkU32(0xFFFFFFFF),
701 /* then: within unsigned-32 range: lo half good enough */
702 mkexpr(lo32) );
703}
704
705/* Signed saturate narrow 64->32, combining to V128 */
706static IRExpr* mkV128from4x64S ( IRExpr* t3, IRExpr* t2,
707 IRExpr* t1, IRExpr* t0 )
708{
709 vassert(typeOfIRExpr(irbb->tyenv, t3) == Ity_I64);
710 vassert(typeOfIRExpr(irbb->tyenv, t2) == Ity_I64);
711 vassert(typeOfIRExpr(irbb->tyenv, t1) == Ity_I64);
712 vassert(typeOfIRExpr(irbb->tyenv, t0) == Ity_I64);
713 return binop(Iop_64HLtoV128,
714 binop(Iop_32HLto64,
715 mkQNarrow64Sto32( t3 ),
716 mkQNarrow64Sto32( t2 )),
717 binop(Iop_32HLto64,
718 mkQNarrow64Sto32( t1 ),
719 mkQNarrow64Sto32( t0 )));
720}
721
722/* Unsigned saturate narrow 64->32, combining to V128 */
723static IRExpr* mkV128from4x64U ( IRExpr* t3, IRExpr* t2,
724 IRExpr* t1, IRExpr* t0 )
725{
726 vassert(typeOfIRExpr(irbb->tyenv, t3) == Ity_I64);
727 vassert(typeOfIRExpr(irbb->tyenv, t2) == Ity_I64);
728 vassert(typeOfIRExpr(irbb->tyenv, t1) == Ity_I64);
729 vassert(typeOfIRExpr(irbb->tyenv, t0) == Ity_I64);
730 return binop(Iop_64HLtoV128,
731 binop(Iop_32HLto64,
732 mkQNarrow64Uto32( t3 ),
733 mkQNarrow64Uto32( t2 )),
734 binop(Iop_32HLto64,
735 mkQNarrow64Uto32( t1 ),
736 mkQNarrow64Uto32( t0 )));
737}
738
cerion24d06f12005-11-09 21:34:20 +0000739/* Simulate irops Iop_MullOdd*, since we don't have them */
740#define MK_Iop_MullOdd8Ux16( expr_vA, expr_vB ) \
741 binop(Iop_MullEven8Ux16, \
742 binop(Iop_ShrV128, expr_vA, mkU8(8)), \
743 binop(Iop_ShrV128, expr_vB, mkU8(8)))
744
745#define MK_Iop_MullOdd8Sx16( expr_vA, expr_vB ) \
746 binop(Iop_MullEven8Sx16, \
747 binop(Iop_ShrV128, expr_vA, mkU8(8)), \
748 binop(Iop_ShrV128, expr_vB, mkU8(8)))
749
750#define MK_Iop_MullOdd16Ux8( expr_vA, expr_vB ) \
751 binop(Iop_MullEven16Ux8, \
752 binop(Iop_ShrV128, expr_vA, mkU8(16)), \
753 binop(Iop_ShrV128, expr_vB, mkU8(16)))
754
755#define MK_Iop_MullOdd16Sx8( expr_vA, expr_vB ) \
756 binop(Iop_MullEven16Sx8, \
757 binop(Iop_ShrV128, expr_vA, mkU8(16)), \
758 binop(Iop_ShrV128, expr_vB, mkU8(16)))
759
cerion59b2c312005-12-17 11:28:53 +0000760static IRExpr* /* :: Ity_I64 */ mk64lo32Sto64 ( IRExpr* src )
ceriond953ebb2005-11-29 13:27:20 +0000761{
762 vassert(typeOfIRExpr(irbb->tyenv, src) == Ity_I64);
763 return unop(Iop_32Sto64, unop(Iop_64to32, src));
764}
765
cerion59b2c312005-12-17 11:28:53 +0000766static IRExpr* /* :: Ity_I64 */ mk64lo32Uto64 ( IRExpr* src )
cerionbb01b7c2005-12-16 13:40:18 +0000767{
768 vassert(typeOfIRExpr(irbb->tyenv, src) == Ity_I64);
769 return unop(Iop_32Uto64, unop(Iop_64to32, src));
770}
771
cerion2831b002005-11-30 19:55:22 +0000772static IROp mkSzOp ( IRType ty, IROp op8 )
ceriond953ebb2005-11-29 13:27:20 +0000773{
774 Int adj;
775 vassert(ty == Ity_I8 || ty == Ity_I16 ||
776 ty == Ity_I32 || ty == Ity_I64);
777 vassert(op8 == Iop_Add8 || op8 == Iop_Sub8 || op8 == Iop_Mul8 ||
778 op8 == Iop_Or8 || op8 == Iop_And8 || op8 == Iop_Xor8 ||
779 op8 == Iop_Shl8 || op8 == Iop_Shr8 || op8 == Iop_Sar8 ||
780 op8 == Iop_CmpEQ8 || op8 == Iop_CmpNE8 ||
781 op8 == Iop_Not8 || op8 == Iop_Neg8 );
782 adj = ty==Ity_I8 ? 0 : (ty==Ity_I16 ? 1 : (ty==Ity_I32 ? 2 : 3));
783 return adj + op8;
784}
785
cerion5b2325f2005-12-23 00:55:09 +0000786/* Make sure we get valid 32 and 64bit addresses */
cerion2831b002005-11-30 19:55:22 +0000787static Addr64 mkSzAddr ( IRType ty, Addr64 addr )
ceriond953ebb2005-11-29 13:27:20 +0000788{
789 vassert(ty == Ity_I32 || ty == Ity_I64);
790 return ( ty == Ity_I64 ?
791 (Addr64)addr :
792 (Addr64)extend_s_32to64( toUInt(addr) ) );
793}
794
795/* sz, ULong -> IRExpr */
cerion2831b002005-11-30 19:55:22 +0000796static IRExpr* mkSzImm ( IRType ty, ULong imm64 )
ceriond953ebb2005-11-29 13:27:20 +0000797{
798 vassert(ty == Ity_I32 || ty == Ity_I64);
799 return ty == Ity_I64 ? mkU64(imm64) : mkU32((UInt)imm64);
800}
801
802/* sz, ULong -> IRConst */
cerion2831b002005-11-30 19:55:22 +0000803static IRConst* mkSzConst ( IRType ty, ULong imm64 )
ceriond953ebb2005-11-29 13:27:20 +0000804{
805 vassert(ty == Ity_I32 || ty == Ity_I64);
806 return ( ty == Ity_I64 ?
807 IRConst_U64(imm64) :
808 IRConst_U32((UInt)imm64) );
809}
810
811/* Sign extend imm16 -> IRExpr* */
cerion2831b002005-11-30 19:55:22 +0000812static IRExpr* mkSzExtendS16 ( IRType ty, UInt imm16 )
ceriond953ebb2005-11-29 13:27:20 +0000813{
814 vassert(ty == Ity_I32 || ty == Ity_I64);
815 return ( ty == Ity_I64 ?
816 mkU64(extend_s_16to64(imm16)) :
817 mkU32(extend_s_16to32(imm16)) );
818}
819
820/* Sign extend imm32 -> IRExpr* */
cerion2831b002005-11-30 19:55:22 +0000821static IRExpr* mkSzExtendS32 ( IRType ty, UInt imm32 )
ceriond953ebb2005-11-29 13:27:20 +0000822{
823 vassert(ty == Ity_I32 || ty == Ity_I64);
824 return ( ty == Ity_I64 ?
825 mkU64(extend_s_32to64(imm32)) :
826 mkU32(imm32) );
827}
828
829/* IR narrows I32/I64 -> I8/I16/I32 */
cerion2831b002005-11-30 19:55:22 +0000830static IRExpr* mkSzNarrow8 ( IRType ty, IRExpr* src )
ceriond953ebb2005-11-29 13:27:20 +0000831{
832 vassert(ty == Ity_I32 || ty == Ity_I64);
833 return ty == Ity_I64 ? unop(Iop_64to8, src) : unop(Iop_32to8, src);
834}
835
cerion2831b002005-11-30 19:55:22 +0000836static IRExpr* mkSzNarrow16 ( IRType ty, IRExpr* src )
ceriond953ebb2005-11-29 13:27:20 +0000837{
838 vassert(ty == Ity_I32 || ty == Ity_I64);
839 return ty == Ity_I64 ? unop(Iop_64to16, src) : unop(Iop_32to16, src);
840}
841
cerion2831b002005-11-30 19:55:22 +0000842static IRExpr* mkSzNarrow32 ( IRType ty, IRExpr* src )
ceriond953ebb2005-11-29 13:27:20 +0000843{
844 vassert(ty == Ity_I32 || ty == Ity_I64);
845 return ty == Ity_I64 ? unop(Iop_64to32, src) : src;
846}
847
848/* Signed/Unsigned IR widens I8/I16/I32 -> I32/I64 */
cerion2831b002005-11-30 19:55:22 +0000849static IRExpr* mkSzWiden8 ( IRType ty, IRExpr* src, Bool sined )
ceriond953ebb2005-11-29 13:27:20 +0000850{
ceriond953ebb2005-11-29 13:27:20 +0000851 IROp op;
sewardj63327402006-01-25 03:26:27 +0000852 vassert(ty == Ity_I32 || ty == Ity_I64);
ceriond953ebb2005-11-29 13:27:20 +0000853 if (sined) op = (ty==Ity_I32) ? Iop_8Sto32 : Iop_8Sto64;
854 else op = (ty==Ity_I32) ? Iop_8Uto32 : Iop_8Uto64;
855 return unop(op, src);
856}
857
cerion2831b002005-11-30 19:55:22 +0000858static IRExpr* mkSzWiden16 ( IRType ty, IRExpr* src, Bool sined )
ceriond953ebb2005-11-29 13:27:20 +0000859{
ceriond953ebb2005-11-29 13:27:20 +0000860 IROp op;
sewardj63327402006-01-25 03:26:27 +0000861 vassert(ty == Ity_I32 || ty == Ity_I64);
ceriond953ebb2005-11-29 13:27:20 +0000862 if (sined) op = (ty==Ity_I32) ? Iop_16Sto32 : Iop_16Sto64;
863 else op = (ty==Ity_I32) ? Iop_16Uto32 : Iop_16Uto64;
864 return unop(op, src);
865}
866
cerion2831b002005-11-30 19:55:22 +0000867static IRExpr* mkSzWiden32 ( IRType ty, IRExpr* src, Bool sined )
ceriond953ebb2005-11-29 13:27:20 +0000868{
869 vassert(ty == Ity_I32 || ty == Ity_I64);
870 if (ty == Ity_I32)
871 return src;
872 return (sined) ? unop(Iop_32Sto64, src) : unop(Iop_32Uto64, src);
873}
cerion24d06f12005-11-09 21:34:20 +0000874
cerion4a49b032005-11-08 16:23:07 +0000875
sewardjb51f0f42005-07-18 11:38:02 +0000876static Int integerGuestRegOffset ( UInt archreg )
cerion45b70ff2005-01-31 17:03:25 +0000877{
sewardjb51f0f42005-07-18 11:38:02 +0000878 vassert(archreg < 32);
879
880 // jrs: probably not necessary; only matters if we reference sub-parts
cerion5b2325f2005-12-23 00:55:09 +0000881 // of the ppc registers, but that isn't the case
sewardjb51f0f42005-07-18 11:38:02 +0000882 // later: this might affect Altivec though?
883 vassert(host_is_bigendian);
884
cerion5b2325f2005-12-23 00:55:09 +0000885 switch (archreg) {
886 case 0: return offsetofPPCGuestState(guest_GPR0);
887 case 1: return offsetofPPCGuestState(guest_GPR1);
888 case 2: return offsetofPPCGuestState(guest_GPR2);
889 case 3: return offsetofPPCGuestState(guest_GPR3);
890 case 4: return offsetofPPCGuestState(guest_GPR4);
891 case 5: return offsetofPPCGuestState(guest_GPR5);
892 case 6: return offsetofPPCGuestState(guest_GPR6);
893 case 7: return offsetofPPCGuestState(guest_GPR7);
894 case 8: return offsetofPPCGuestState(guest_GPR8);
895 case 9: return offsetofPPCGuestState(guest_GPR9);
896 case 10: return offsetofPPCGuestState(guest_GPR10);
897 case 11: return offsetofPPCGuestState(guest_GPR11);
898 case 12: return offsetofPPCGuestState(guest_GPR12);
899 case 13: return offsetofPPCGuestState(guest_GPR13);
900 case 14: return offsetofPPCGuestState(guest_GPR14);
901 case 15: return offsetofPPCGuestState(guest_GPR15);
902 case 16: return offsetofPPCGuestState(guest_GPR16);
903 case 17: return offsetofPPCGuestState(guest_GPR17);
904 case 18: return offsetofPPCGuestState(guest_GPR18);
905 case 19: return offsetofPPCGuestState(guest_GPR19);
906 case 20: return offsetofPPCGuestState(guest_GPR20);
907 case 21: return offsetofPPCGuestState(guest_GPR21);
908 case 22: return offsetofPPCGuestState(guest_GPR22);
909 case 23: return offsetofPPCGuestState(guest_GPR23);
910 case 24: return offsetofPPCGuestState(guest_GPR24);
911 case 25: return offsetofPPCGuestState(guest_GPR25);
912 case 26: return offsetofPPCGuestState(guest_GPR26);
913 case 27: return offsetofPPCGuestState(guest_GPR27);
914 case 28: return offsetofPPCGuestState(guest_GPR28);
915 case 29: return offsetofPPCGuestState(guest_GPR29);
916 case 30: return offsetofPPCGuestState(guest_GPR30);
917 case 31: return offsetofPPCGuestState(guest_GPR31);
918 default: break;
sewardjb51f0f42005-07-18 11:38:02 +0000919 }
cerion5b2325f2005-12-23 00:55:09 +0000920 vpanic("integerGuestRegOffset(ppc,be)"); /*notreached*/
sewardjb51f0f42005-07-18 11:38:02 +0000921}
922
923static IRExpr* getIReg ( UInt archreg )
924{
ceriond953ebb2005-11-29 13:27:20 +0000925 IRType ty = mode64 ? Ity_I64 : Ity_I32;
sewardjb51f0f42005-07-18 11:38:02 +0000926 vassert(archreg < 32);
ceriond953ebb2005-11-29 13:27:20 +0000927 return IRExpr_Get( integerGuestRegOffset(archreg), ty );
sewardjb51f0f42005-07-18 11:38:02 +0000928}
929
930/* Ditto, but write to a reg instead. */
931static void putIReg ( UInt archreg, IRExpr* e )
932{
ceriond953ebb2005-11-29 13:27:20 +0000933 IRType ty = mode64 ? Ity_I64 : Ity_I32;
sewardjb51f0f42005-07-18 11:38:02 +0000934 vassert(archreg < 32);
ceriond953ebb2005-11-29 13:27:20 +0000935 vassert(typeOfIRExpr(irbb->tyenv, e) == ty );
sewardjb51f0f42005-07-18 11:38:02 +0000936 stmt( IRStmt_Put(integerGuestRegOffset(archreg), e) );
937}
938
939
940static Int floatGuestRegOffset ( UInt archreg )
941{
942 vassert(archreg < 32);
943
cerion5b2325f2005-12-23 00:55:09 +0000944 switch (archreg) {
945 case 0: return offsetofPPCGuestState(guest_FPR0);
946 case 1: return offsetofPPCGuestState(guest_FPR1);
947 case 2: return offsetofPPCGuestState(guest_FPR2);
948 case 3: return offsetofPPCGuestState(guest_FPR3);
949 case 4: return offsetofPPCGuestState(guest_FPR4);
950 case 5: return offsetofPPCGuestState(guest_FPR5);
951 case 6: return offsetofPPCGuestState(guest_FPR6);
952 case 7: return offsetofPPCGuestState(guest_FPR7);
953 case 8: return offsetofPPCGuestState(guest_FPR8);
954 case 9: return offsetofPPCGuestState(guest_FPR9);
955 case 10: return offsetofPPCGuestState(guest_FPR10);
956 case 11: return offsetofPPCGuestState(guest_FPR11);
957 case 12: return offsetofPPCGuestState(guest_FPR12);
958 case 13: return offsetofPPCGuestState(guest_FPR13);
959 case 14: return offsetofPPCGuestState(guest_FPR14);
960 case 15: return offsetofPPCGuestState(guest_FPR15);
961 case 16: return offsetofPPCGuestState(guest_FPR16);
962 case 17: return offsetofPPCGuestState(guest_FPR17);
963 case 18: return offsetofPPCGuestState(guest_FPR18);
964 case 19: return offsetofPPCGuestState(guest_FPR19);
965 case 20: return offsetofPPCGuestState(guest_FPR20);
966 case 21: return offsetofPPCGuestState(guest_FPR21);
967 case 22: return offsetofPPCGuestState(guest_FPR22);
968 case 23: return offsetofPPCGuestState(guest_FPR23);
969 case 24: return offsetofPPCGuestState(guest_FPR24);
970 case 25: return offsetofPPCGuestState(guest_FPR25);
971 case 26: return offsetofPPCGuestState(guest_FPR26);
972 case 27: return offsetofPPCGuestState(guest_FPR27);
973 case 28: return offsetofPPCGuestState(guest_FPR28);
974 case 29: return offsetofPPCGuestState(guest_FPR29);
975 case 30: return offsetofPPCGuestState(guest_FPR30);
976 case 31: return offsetofPPCGuestState(guest_FPR31);
977 default: break;
sewardjb51f0f42005-07-18 11:38:02 +0000978 }
cerion5b2325f2005-12-23 00:55:09 +0000979 vpanic("floatGuestRegOffset(ppc)"); /*notreached*/
sewardjb51f0f42005-07-18 11:38:02 +0000980}
981
982static IRExpr* getFReg ( UInt archreg )
983{
984 vassert(archreg < 32);
985 return IRExpr_Get( floatGuestRegOffset(archreg), Ity_F64 );
986}
987
988/* Ditto, but write to a reg instead. */
989static void putFReg ( UInt archreg, IRExpr* e )
990{
991 vassert(archreg < 32);
992 vassert(typeOfIRExpr(irbb->tyenv, e) == Ity_F64);
993 stmt( IRStmt_Put(floatGuestRegOffset(archreg), e) );
994}
995
996
997static Int vectorGuestRegOffset ( UInt archreg )
998{
999 vassert(archreg < 32);
1000
cerion5b2325f2005-12-23 00:55:09 +00001001 switch (archreg) {
1002 case 0: return offsetofPPCGuestState(guest_VR0);
1003 case 1: return offsetofPPCGuestState(guest_VR1);
1004 case 2: return offsetofPPCGuestState(guest_VR2);
1005 case 3: return offsetofPPCGuestState(guest_VR3);
1006 case 4: return offsetofPPCGuestState(guest_VR4);
1007 case 5: return offsetofPPCGuestState(guest_VR5);
1008 case 6: return offsetofPPCGuestState(guest_VR6);
1009 case 7: return offsetofPPCGuestState(guest_VR7);
1010 case 8: return offsetofPPCGuestState(guest_VR8);
1011 case 9: return offsetofPPCGuestState(guest_VR9);
1012 case 10: return offsetofPPCGuestState(guest_VR10);
1013 case 11: return offsetofPPCGuestState(guest_VR11);
1014 case 12: return offsetofPPCGuestState(guest_VR12);
1015 case 13: return offsetofPPCGuestState(guest_VR13);
1016 case 14: return offsetofPPCGuestState(guest_VR14);
1017 case 15: return offsetofPPCGuestState(guest_VR15);
1018 case 16: return offsetofPPCGuestState(guest_VR16);
1019 case 17: return offsetofPPCGuestState(guest_VR17);
1020 case 18: return offsetofPPCGuestState(guest_VR18);
1021 case 19: return offsetofPPCGuestState(guest_VR19);
1022 case 20: return offsetofPPCGuestState(guest_VR20);
1023 case 21: return offsetofPPCGuestState(guest_VR21);
1024 case 22: return offsetofPPCGuestState(guest_VR22);
1025 case 23: return offsetofPPCGuestState(guest_VR23);
1026 case 24: return offsetofPPCGuestState(guest_VR24);
1027 case 25: return offsetofPPCGuestState(guest_VR25);
1028 case 26: return offsetofPPCGuestState(guest_VR26);
1029 case 27: return offsetofPPCGuestState(guest_VR27);
1030 case 28: return offsetofPPCGuestState(guest_VR28);
1031 case 29: return offsetofPPCGuestState(guest_VR29);
1032 case 30: return offsetofPPCGuestState(guest_VR30);
1033 case 31: return offsetofPPCGuestState(guest_VR31);
1034 default: break;
sewardjb51f0f42005-07-18 11:38:02 +00001035 }
cerion5b2325f2005-12-23 00:55:09 +00001036 vpanic("vextorGuestRegOffset(ppc)"); /*notreached*/
sewardjb51f0f42005-07-18 11:38:02 +00001037}
1038
1039static IRExpr* getVReg ( UInt archreg )
1040{
1041 vassert(archreg < 32);
1042 return IRExpr_Get( vectorGuestRegOffset(archreg), Ity_V128 );
1043}
1044
1045/* Ditto, but write to a reg instead. */
1046static void putVReg ( UInt archreg, IRExpr* e )
1047{
1048 vassert(archreg < 32);
1049 vassert(typeOfIRExpr(irbb->tyenv, e) == Ity_V128);
1050 stmt( IRStmt_Put(vectorGuestRegOffset(archreg), e) );
1051}
1052
1053static Int guestCR321offset ( UInt cr )
1054{
cerion5b2325f2005-12-23 00:55:09 +00001055 switch (cr) {
1056 case 0: return offsetofPPCGuestState(guest_CR0_321 );
1057 case 1: return offsetofPPCGuestState(guest_CR1_321 );
1058 case 2: return offsetofPPCGuestState(guest_CR2_321 );
1059 case 3: return offsetofPPCGuestState(guest_CR3_321 );
1060 case 4: return offsetofPPCGuestState(guest_CR4_321 );
1061 case 5: return offsetofPPCGuestState(guest_CR5_321 );
1062 case 6: return offsetofPPCGuestState(guest_CR6_321 );
1063 case 7: return offsetofPPCGuestState(guest_CR7_321 );
1064 default: vpanic("guestCR321offset(ppc)");
sewardjb51f0f42005-07-18 11:38:02 +00001065 }
1066}
1067
1068static Int guestCR0offset ( UInt cr )
1069{
cerion5b2325f2005-12-23 00:55:09 +00001070 switch (cr) {
1071 case 0: return offsetofPPCGuestState(guest_CR0_0 );
1072 case 1: return offsetofPPCGuestState(guest_CR1_0 );
1073 case 2: return offsetofPPCGuestState(guest_CR2_0 );
1074 case 3: return offsetofPPCGuestState(guest_CR3_0 );
1075 case 4: return offsetofPPCGuestState(guest_CR4_0 );
1076 case 5: return offsetofPPCGuestState(guest_CR5_0 );
1077 case 6: return offsetofPPCGuestState(guest_CR6_0 );
1078 case 7: return offsetofPPCGuestState(guest_CR7_0 );
1079 default: vpanic("guestCR3offset(ppc)");
sewardjb51f0f42005-07-18 11:38:02 +00001080 }
sewardjb51f0f42005-07-18 11:38:02 +00001081}
1082
cerion07b07a92005-12-22 14:32:35 +00001083// ROTL(src32/64, rot_amt5/6)
ceriond953ebb2005-11-29 13:27:20 +00001084static IRExpr* /* :: Ity_I32/64 */ ROTL ( IRExpr* src,
1085 IRExpr* rot_amt )
sewardjb51f0f42005-07-18 11:38:02 +00001086{
ceriond953ebb2005-11-29 13:27:20 +00001087 IRExpr *mask, *rot;
cerionf0de28c2005-12-13 20:21:11 +00001088 vassert(typeOfIRExpr(irbb->tyenv,rot_amt) == Ity_I8);
sewardjb51f0f42005-07-18 11:38:02 +00001089
cerionf0de28c2005-12-13 20:21:11 +00001090 if (typeOfIRExpr(irbb->tyenv,src) == Ity_I64) {
ceriond953ebb2005-11-29 13:27:20 +00001091 // rot = (src << rot_amt) | (src >> (64-rot_amt))
1092 mask = binop(Iop_And8, rot_amt, mkU8(63));
1093 rot = binop(Iop_Or64,
1094 binop(Iop_Shl64, src, mask),
1095 binop(Iop_Shr64, src, binop(Iop_Sub8, mkU8(64), mask)));
1096 } else {
ceriond953ebb2005-11-29 13:27:20 +00001097 // rot = (src << rot_amt) | (src >> (32-rot_amt))
cerionf0de28c2005-12-13 20:21:11 +00001098 mask = binop(Iop_And8, rot_amt, mkU8(31));
ceriond953ebb2005-11-29 13:27:20 +00001099 rot = binop(Iop_Or32,
1100 binop(Iop_Shl32, src, mask),
1101 binop(Iop_Shr32, src, binop(Iop_Sub8, mkU8(32), mask)));
cerion2831b002005-11-30 19:55:22 +00001102 }
sewardjc9659532005-07-21 21:33:57 +00001103 /* Note: the MuxOX is not merely an optimisation; it's needed
cerionf0de28c2005-12-13 20:21:11 +00001104 because otherwise the Shr is a shift by the word size when
ceriond953ebb2005-11-29 13:27:20 +00001105 mask denotes zero. For rotates by immediates, a lot of
sewardjc9659532005-07-21 21:33:57 +00001106 this junk gets folded out. */
ceriond953ebb2005-11-29 13:27:20 +00001107 return IRExpr_Mux0X( mask, /* zero rotate */ src,
1108 /* non-zero rotate */ rot );
cerion45b70ff2005-01-31 17:03:25 +00001109}
cerion896a1372005-01-25 12:24:25 +00001110
ceriond953ebb2005-11-29 13:27:20 +00001111#if 0
1112/* ROTL32_64(src64, rot_amt5)
1113 Weirdo 32bit rotl on ppc64:
1114 rot32 = ROTL(src_lo32,y);
1115 return (rot32|rot32);
1116*/
1117static IRExpr* /* :: Ity_I64 */ ROTL32_64 ( IRExpr* src64,
1118 IRExpr* rot_amt )
sewardj87e651f2005-09-09 08:31:18 +00001119{
ceriond953ebb2005-11-29 13:27:20 +00001120 IRExpr *mask, *rot32;
1121 vassert(mode64); // used only in 64bit mode
1122 vassert(typeOfIRExpr(irbb->tyenv,src64) == Ity_I64);
1123 vassert(typeOfIRExpr(irbb->tyenv,rot_amt) == Ity_I8);
1124
1125 mask = binop(Iop_And8, rot_amt, mkU8(31));
1126 rot32 = ROTL( unop(Iop_64to32, src64), rot_amt );
1127
1128 return binop(Iop_Or64,
1129 binop(Iop_Shl64, unop(Iop_32Uto64, rot32), mkU8(32)),
cerion2831b002005-11-30 19:55:22 +00001130 unop(Iop_32Uto64, rot32));
ceriond953ebb2005-11-29 13:27:20 +00001131}
1132#endif
1133
1134
1135/* Standard effective address calc: (rA + rB) */
1136static IRExpr* ea_rA_idxd ( UInt rA, UInt rB )
1137{
1138 IRType ty = mode64 ? Ity_I64 : Ity_I32;
1139 vassert(rA < 32);
1140 vassert(rB < 32);
cerion2831b002005-11-30 19:55:22 +00001141 return binop(mkSzOp(ty, Iop_Add8), getIReg(rA), getIReg(rB));
sewardj87e651f2005-09-09 08:31:18 +00001142}
1143
ceriond953ebb2005-11-29 13:27:20 +00001144/* Standard effective address calc: (rA + simm) */
1145static IRExpr* ea_rA_simm ( UInt rA, UInt simm16 )
sewardj87e651f2005-09-09 08:31:18 +00001146{
ceriond953ebb2005-11-29 13:27:20 +00001147 IRType ty = mode64 ? Ity_I64 : Ity_I32;
1148 vassert(rA < 32);
cerion2831b002005-11-30 19:55:22 +00001149 return binop(mkSzOp(ty, Iop_Add8), getIReg(rA),
1150 mkSzExtendS16(ty, simm16));
ceriond953ebb2005-11-29 13:27:20 +00001151}
1152
1153/* Standard effective address calc: (rA|0) */
1154static IRExpr* ea_rAor0 ( UInt rA )
1155{
1156 IRType ty = mode64 ? Ity_I64 : Ity_I32;
1157 vassert(rA < 32);
sewardj87e651f2005-09-09 08:31:18 +00001158 if (rA == 0) {
cerion2831b002005-11-30 19:55:22 +00001159 return mkSzImm(ty, 0);
sewardj87e651f2005-09-09 08:31:18 +00001160 } else {
1161 return getIReg(rA);
1162 }
1163}
1164
ceriond953ebb2005-11-29 13:27:20 +00001165/* Standard effective address calc: (rA|0) + rB */
1166static IRExpr* ea_rAor0_idxd ( UInt rA, UInt rB )
1167{
1168 vassert(rA < 32);
1169 vassert(rB < 32);
1170 return (rA == 0) ? getIReg(rB) : ea_rA_idxd( rA, rB );
1171}
1172
1173/* Standard effective address calc: (rA|0) + simm16 */
1174static IRExpr* ea_rAor0_simm ( UInt rA, UInt simm16 )
1175{
1176 IRType ty = mode64 ? Ity_I64 : Ity_I32;
1177 vassert(rA < 32);
1178 if (rA == 0) {
cerion2831b002005-11-30 19:55:22 +00001179 return mkSzExtendS16(ty, simm16);
ceriond953ebb2005-11-29 13:27:20 +00001180 } else {
1181 return ea_rA_simm( rA, simm16 );
1182 }
1183}
1184
1185
1186/* Align effective address */
1187static IRExpr* addr_align( IRExpr* addr, UChar align )
1188{
1189 IRType ty = mode64 ? Ity_I64 : Ity_I32;
1190 Long mask;
1191 switch (align) {
cerion2831b002005-11-30 19:55:22 +00001192 case 1: return addr; // byte aligned
ceriond953ebb2005-11-29 13:27:20 +00001193 case 2: mask = ((Long)-1) << 1; break; // half-word aligned
1194 case 4: mask = ((Long)-1) << 2; break; // word aligned
1195 case 16: mask = ((Long)-1) << 4; break; // quad-word aligned
1196 default:
1197 vex_printf("addr_align: align = %u\n", align);
cerion5b2325f2005-12-23 00:55:09 +00001198 vpanic("addr_align(ppc)");
ceriond953ebb2005-11-29 13:27:20 +00001199 }
1200
1201 vassert(typeOfIRExpr(irbb->tyenv,addr) == ty);
cerionfb197c42005-12-24 12:32:10 +00001202 return binop( mkSzOp(ty, Iop_And8), addr, mkSzImm(ty, mask) );
ceriond953ebb2005-11-29 13:27:20 +00001203}
1204
cerion896a1372005-01-25 12:24:25 +00001205
sewardjcf8986c2006-01-18 04:14:52 +00001206/* Generate AbiHints which mark points at which the ELF ppc64 ABI says
1207 that the stack red zone (viz, -288(r1) .. -1(r1)) becomes
1208 undefined. That is at function calls and returns. Only in 64-bit
1209 mode - ELF ppc32 doesn't have this "feature".
1210*/
1211static void make_redzone_AbiHint ( HChar* who )
1212{
1213 if (0) vex_printf("AbiHint: %s\n", who);
1214 vassert(mode64);
1215 stmt( IRStmt_AbiHint(
1216 binop(Iop_Sub64, getIReg(1), mkU64(288)),
1217 288
1218 ));
1219}
1220
1221
cerion896a1372005-01-25 12:24:25 +00001222/*------------------------------------------------------------*/
sewardjb51f0f42005-07-18 11:38:02 +00001223/*--- Helpers for condition codes. ---*/
cerion896a1372005-01-25 12:24:25 +00001224/*------------------------------------------------------------*/
1225
sewardjb51f0f42005-07-18 11:38:02 +00001226/* Condition register layout.
cerion896a1372005-01-25 12:24:25 +00001227
sewardjb51f0f42005-07-18 11:38:02 +00001228 In the hardware, CR is laid out like this. The leftmost end is the
1229 most significant bit in the register; however the IBM documentation
1230 numbers the bits backwards for some reason.
1231
1232 CR0 CR1 .......... CR6 CR7
1233 0 .. 3 ....................... 28 .. 31 (IBM bit numbering)
1234 31 28 3 0 (normal bit numbering)
1235
cerionedf7fc52005-11-18 20:57:41 +00001236 Each CR field is 4 bits: [<,>,==,SO]
sewardjb51f0f42005-07-18 11:38:02 +00001237
cerionedf7fc52005-11-18 20:57:41 +00001238 Hence in IBM's notation, BI=0 is CR7[SO], BI=1 is CR7[==], etc.
sewardjb51f0f42005-07-18 11:38:02 +00001239
1240 Indexing from BI to guest state:
1241
1242 let n = BI / 4
1243 off = BI % 4
1244 this references CR n:
1245
cerionedf7fc52005-11-18 20:57:41 +00001246 off==0 -> guest_CRn_321 >> 3
1247 off==1 -> guest_CRn_321 >> 2
1248 off==2 -> guest_CRn_321 >> 1
sewardjb51f0f42005-07-18 11:38:02 +00001249 off==3 -> guest_CRn_SO
sewardjb51f0f42005-07-18 11:38:02 +00001250
1251 Bear in mind the only significant bit in guest_CRn_SO is bit 0
cerionedf7fc52005-11-18 20:57:41 +00001252 (normal notation) and in guest_CRn_321 the significant bits are
sewardjb51f0f42005-07-18 11:38:02 +00001253 3, 2 and 1 (normal notation).
1254*/
cerionedf7fc52005-11-18 20:57:41 +00001255
1256static void putCR321 ( UInt cr, IRExpr* e )
1257{
1258 vassert(cr < 8);
1259 vassert(typeOfIRExpr(irbb->tyenv, e) == Ity_I8);
1260 stmt( IRStmt_Put(guestCR321offset(cr), e) );
1261}
1262
1263static void putCR0 ( UInt cr, IRExpr* e )
1264{
1265 vassert(cr < 8);
1266 vassert(typeOfIRExpr(irbb->tyenv, e) == Ity_I8);
1267 stmt( IRStmt_Put(guestCR0offset(cr), e) );
1268}
1269
1270static IRExpr* /* :: Ity_I8 */ getCR0 ( UInt cr )
1271{
1272 vassert(cr < 8);
1273 return IRExpr_Get(guestCR0offset(cr), Ity_I8);
1274}
1275
1276static IRExpr* /* :: Ity_I8 */ getCR321 ( UInt cr )
1277{
1278 vassert(cr < 8);
1279 return IRExpr_Get(guestCR321offset(cr), Ity_I8);
1280}
1281
sewardjb51f0f42005-07-18 11:38:02 +00001282/* Fetch the specified CR bit (as per IBM/hardware notation) and
1283 return it at the bottom of an I32; the top 31 bits are guaranteed
1284 to be zero. */
1285static IRExpr* /* :: Ity_I32 */ getCRbit ( UInt bi )
cerion896a1372005-01-25 12:24:25 +00001286{
sewardjb51f0f42005-07-18 11:38:02 +00001287 UInt n = bi / 4;
1288 UInt off = bi % 4;
1289 vassert(bi < 32);
1290 if (off == 3) {
1291 /* Fetch the SO bit for this CR field */
1292 /* Note: And32 is redundant paranoia iff guest state only has 0
1293 or 1 in that slot. */
1294 return binop(Iop_And32, unop(Iop_8Uto32, getCR0(n)), mkU32(1));
1295 } else {
1296 /* Fetch the <, > or == bit for this CR field */
1297 return binop( Iop_And32,
1298 binop( Iop_Shr32,
1299 unop(Iop_8Uto32, getCR321(n)),
sewardjc7cd2142005-09-09 22:31:49 +00001300 mkU8(toUChar(3-off)) ),
sewardjb51f0f42005-07-18 11:38:02 +00001301 mkU32(1) );
1302 }
cerion91ad5362005-01-27 23:02:41 +00001303}
1304
sewardjb51f0f42005-07-18 11:38:02 +00001305/* Dually, write the least significant bit of BIT to the specified CR
1306 bit. Indexing as per getCRbit. */
1307static void putCRbit ( UInt bi, IRExpr* bit )
1308{
sewardj197bd172005-10-12 11:34:33 +00001309 UInt n, off;
sewardjb51f0f42005-07-18 11:38:02 +00001310 IRExpr* safe;
1311 vassert(typeOfIRExpr(irbb->tyenv,bit) == Ity_I32);
1312 safe = binop(Iop_And32, bit, mkU32(1));
sewardj197bd172005-10-12 11:34:33 +00001313 n = bi / 4;
1314 off = bi % 4;
sewardjb51f0f42005-07-18 11:38:02 +00001315 vassert(bi < 32);
1316 if (off == 3) {
1317 /* This is the SO bit for this CR field */
1318 putCR0(n, unop(Iop_32to8, safe));
1319 } else {
1320 off = 3 - off;
1321 vassert(off == 1 || off == 2 || off == 3);
1322 putCR321(
1323 n,
1324 unop( Iop_32to8,
1325 binop( Iop_Or32,
1326 /* old value with field masked out */
1327 binop(Iop_And32, unop(Iop_8Uto32, getCR321(n)),
1328 mkU32(~(1 << off))),
1329 /* new value in the right place */
sewardjc7cd2142005-09-09 22:31:49 +00001330 binop(Iop_Shl32, safe, mkU8(toUChar(off)))
sewardjb51f0f42005-07-18 11:38:02 +00001331 )
1332 )
1333 );
1334 }
1335}
1336
sewardjb51f0f42005-07-18 11:38:02 +00001337/* Fetch the specified CR bit (as per IBM/hardware notation) and
1338 return it somewhere in an I32; it does not matter where, but
1339 whichever bit it is, all other bits are guaranteed to be zero. In
1340 other words, the I32-typed expression will be zero if the bit is
1341 zero and nonzero if the bit is 1. Write into *where the index
1342 of where the bit will be. */
1343
cerion5b2325f2005-12-23 00:55:09 +00001344static
1345IRExpr* /* :: Ity_I32 */ getCRbit_anywhere ( UInt bi, Int* where )
sewardjb51f0f42005-07-18 11:38:02 +00001346{
1347 UInt n = bi / 4;
1348 UInt off = bi % 4;
1349 vassert(bi < 32);
1350 if (off == 3) {
1351 /* Fetch the SO bit for this CR field */
1352 /* Note: And32 is redundant paranoia iff guest state only has 0
1353 or 1 in that slot. */
1354 *where = 0;
1355 return binop(Iop_And32, unop(Iop_8Uto32, getCR0(n)), mkU32(1));
1356 } else {
1357 /* Fetch the <, > or == bit for this CR field */
1358 *where = 3-off;
1359 return binop( Iop_And32,
1360 unop(Iop_8Uto32, getCR321(n)),
1361 mkU32(1 << (3-off)) );
1362 }
1363}
1364
sewardjb51f0f42005-07-18 11:38:02 +00001365/* Set the CR0 flags following an arithmetic operation.
1366 (Condition Register CR0 Field Definition, PPC32 p60)
cerion896a1372005-01-25 12:24:25 +00001367*/
cerionedf7fc52005-11-18 20:57:41 +00001368static IRExpr* getXER_SO ( void );
sewardj20ef5472005-07-21 14:48:31 +00001369static void set_CR0 ( IRExpr* result )
cerion896a1372005-01-25 12:24:25 +00001370{
ceriond953ebb2005-11-29 13:27:20 +00001371 vassert(typeOfIRExpr(irbb->tyenv,result) == Ity_I32 ||
1372 typeOfIRExpr(irbb->tyenv,result) == Ity_I64);
1373 if (mode64) {
ceriond953ebb2005-11-29 13:27:20 +00001374 putCR321( 0, unop(Iop_64to8,
cerion2831b002005-11-30 19:55:22 +00001375 binop(Iop_CmpORD64S, result, mkU64(0))) );
ceriond953ebb2005-11-29 13:27:20 +00001376 } else {
1377 putCR321( 0, unop(Iop_32to8,
1378 binop(Iop_CmpORD32S, result, mkU32(0))) );
1379 }
sewardjb51f0f42005-07-18 11:38:02 +00001380 putCR0( 0, getXER_SO() );
cerion896a1372005-01-25 12:24:25 +00001381}
cerion896a1372005-01-25 12:24:25 +00001382
sewardj20ef5472005-07-21 14:48:31 +00001383
cerionedf7fc52005-11-18 20:57:41 +00001384/* Set the CR6 flags following an AltiVec compare operation. */
1385static void set_AV_CR6 ( IRExpr* result, Bool test_all_ones )
1386{
1387 /* CR6[0:3] = {all_ones, 0, all_zeros, 0}
1388 all_ones = (v[0] && v[1] && v[2] && v[3])
1389 all_zeros = ~(v[0] || v[1] || v[2] || v[3])
1390 */
1391 IRTemp v0 = newTemp(Ity_V128);
1392 IRTemp v1 = newTemp(Ity_V128);
1393 IRTemp v2 = newTemp(Ity_V128);
1394 IRTemp v3 = newTemp(Ity_V128);
1395 IRTemp rOnes = newTemp(Ity_I8);
1396 IRTemp rZeros = newTemp(Ity_I8);
1397
1398 vassert(typeOfIRExpr(irbb->tyenv,result) == Ity_V128);
1399
1400 assign( v0, result );
1401 assign( v1, binop(Iop_ShrV128, result, mkU8(32)) );
1402 assign( v2, binop(Iop_ShrV128, result, mkU8(64)) );
1403 assign( v3, binop(Iop_ShrV128, result, mkU8(96)) );
1404
1405 assign( rZeros, unop(Iop_1Uto8,
1406 binop(Iop_CmpEQ32, mkU32(0xFFFFFFFF),
1407 unop(Iop_Not32,
1408 unop(Iop_V128to32,
1409 binop(Iop_OrV128,
1410 binop(Iop_OrV128, mkexpr(v0), mkexpr(v1)),
1411 binop(Iop_OrV128, mkexpr(v2), mkexpr(v3))))
1412 ))) );
1413
1414 if (test_all_ones) {
1415 assign( rOnes, unop(Iop_1Uto8,
1416 binop(Iop_CmpEQ32, mkU32(0xFFFFFFFF),
1417 unop(Iop_V128to32,
1418 binop(Iop_AndV128,
1419 binop(Iop_AndV128, mkexpr(v0), mkexpr(v1)),
cerion5b2325f2005-12-23 00:55:09 +00001420 binop(Iop_AndV128, mkexpr(v2), mkexpr(v3)))
1421 ))) );
cerionedf7fc52005-11-18 20:57:41 +00001422 putCR321( 6, binop(Iop_Or8,
1423 binop(Iop_Shl8, mkexpr(rOnes), mkU8(3)),
1424 binop(Iop_Shl8, mkexpr(rZeros), mkU8(1))) );
1425 } else {
1426 putCR321( 6, binop(Iop_Shl8, mkexpr(rZeros), mkU8(1)) );
1427 }
1428 putCR0( 6, mkU8(0) );
1429}
1430
1431
1432
1433/*------------------------------------------------------------*/
1434/*--- Helpers for XER flags. ---*/
1435/*------------------------------------------------------------*/
1436
1437static void putXER_SO ( IRExpr* e )
1438{
sewardj63327402006-01-25 03:26:27 +00001439 IRExpr* so;
cerionedf7fc52005-11-18 20:57:41 +00001440 vassert(typeOfIRExpr(irbb->tyenv, e) == Ity_I8);
sewardj63327402006-01-25 03:26:27 +00001441 so = binop(Iop_And8, e, mkU8(1));
cerion5b2325f2005-12-23 00:55:09 +00001442 stmt( IRStmt_Put( OFFB_XER_SO, so ) );
cerionedf7fc52005-11-18 20:57:41 +00001443}
1444
1445static void putXER_OV ( IRExpr* e )
1446{
sewardj63327402006-01-25 03:26:27 +00001447 IRExpr* ov;
cerionedf7fc52005-11-18 20:57:41 +00001448 vassert(typeOfIRExpr(irbb->tyenv, e) == Ity_I8);
sewardj63327402006-01-25 03:26:27 +00001449 ov = binop(Iop_And8, e, mkU8(1));
cerion5b2325f2005-12-23 00:55:09 +00001450 stmt( IRStmt_Put( OFFB_XER_OV, ov ) );
cerionedf7fc52005-11-18 20:57:41 +00001451}
1452
1453static void putXER_CA ( IRExpr* e )
1454{
sewardj63327402006-01-25 03:26:27 +00001455 IRExpr* ca;
cerionedf7fc52005-11-18 20:57:41 +00001456 vassert(typeOfIRExpr(irbb->tyenv, e) == Ity_I8);
sewardj63327402006-01-25 03:26:27 +00001457 ca = binop(Iop_And8, e, mkU8(1));
cerion5b2325f2005-12-23 00:55:09 +00001458 stmt( IRStmt_Put( OFFB_XER_CA, ca ) );
cerionedf7fc52005-11-18 20:57:41 +00001459}
1460
1461static void putXER_BC ( IRExpr* e )
1462{
sewardj63327402006-01-25 03:26:27 +00001463 IRExpr* bc;
cerionedf7fc52005-11-18 20:57:41 +00001464 vassert(typeOfIRExpr(irbb->tyenv, e) == Ity_I8);
sewardj63327402006-01-25 03:26:27 +00001465 bc = binop(Iop_And8, e, mkU8(0x7F));
cerion5b2325f2005-12-23 00:55:09 +00001466 stmt( IRStmt_Put( OFFB_XER_BC, bc ) );
cerionedf7fc52005-11-18 20:57:41 +00001467}
1468
1469static IRExpr* /* :: Ity_I8 */ getXER_SO ( void )
1470{
cerion5b2325f2005-12-23 00:55:09 +00001471 return IRExpr_Get( OFFB_XER_SO, Ity_I8 );
cerionedf7fc52005-11-18 20:57:41 +00001472}
1473
1474static IRExpr* /* :: Ity_I32 */ getXER_SO32 ( void )
1475{
1476 return binop( Iop_And32, unop(Iop_8Uto32, getXER_SO()), mkU32(1) );
1477}
1478
1479static IRExpr* /* :: Ity_I8 */ getXER_OV ( void )
1480{
cerion5b2325f2005-12-23 00:55:09 +00001481 return IRExpr_Get( OFFB_XER_OV, Ity_I8 );
cerionedf7fc52005-11-18 20:57:41 +00001482}
1483
1484static IRExpr* /* :: Ity_I32 */ getXER_OV32 ( void )
1485{
1486 return binop( Iop_And32, unop(Iop_8Uto32, getXER_OV()), mkU32(1) );
1487}
1488
1489static IRExpr* /* :: Ity_I32 */ getXER_CA32 ( void )
1490{
cerion5b2325f2005-12-23 00:55:09 +00001491 IRExpr* ca = IRExpr_Get( OFFB_XER_CA, Ity_I8 );
ceriond953ebb2005-11-29 13:27:20 +00001492 return binop( Iop_And32, unop(Iop_8Uto32, ca ), mkU32(1) );
cerionedf7fc52005-11-18 20:57:41 +00001493}
1494
1495static IRExpr* /* :: Ity_I8 */ getXER_BC ( void )
1496{
cerion5b2325f2005-12-23 00:55:09 +00001497 return IRExpr_Get( OFFB_XER_BC, Ity_I8 );
cerionedf7fc52005-11-18 20:57:41 +00001498}
1499
1500static IRExpr* /* :: Ity_I32 */ getXER_BC32 ( void )
1501{
cerion5b2325f2005-12-23 00:55:09 +00001502 IRExpr* bc = IRExpr_Get( OFFB_XER_BC, Ity_I8 );
ceriond953ebb2005-11-29 13:27:20 +00001503 return binop( Iop_And32, unop(Iop_8Uto32, bc), mkU32(0x7F) );
cerionedf7fc52005-11-18 20:57:41 +00001504}
1505
1506
sewardj20ef5472005-07-21 14:48:31 +00001507/* RES is the result of doing OP on ARGL and ARGR. Set %XER.OV and
1508 %XER.SO accordingly. */
1509
ceriond953ebb2005-11-29 13:27:20 +00001510static void set_XER_OV_32( UInt op, IRExpr* res,
1511 IRExpr* argL, IRExpr* argR )
sewardj20ef5472005-07-21 14:48:31 +00001512{
1513 IRTemp t64;
1514 IRExpr* xer_ov;
cerion5b2325f2005-12-23 00:55:09 +00001515 vassert(op < PPCG_FLAG_OP_NUMBER);
ceriond953ebb2005-11-29 13:27:20 +00001516 vassert(typeOfIRExpr(irbb->tyenv,res) == Ity_I32);
sewardj20ef5472005-07-21 14:48:31 +00001517 vassert(typeOfIRExpr(irbb->tyenv,argL) == Ity_I32);
1518 vassert(typeOfIRExpr(irbb->tyenv,argR) == Ity_I32);
1519
1520# define INT32_MIN 0x80000000
1521
1522# define XOR2(_aa,_bb) \
1523 binop(Iop_Xor32,(_aa),(_bb))
1524
1525# define XOR3(_cc,_dd,_ee) \
1526 binop(Iop_Xor32,binop(Iop_Xor32,(_cc),(_dd)),(_ee))
1527
1528# define AND3(_ff,_gg,_hh) \
1529 binop(Iop_And32,binop(Iop_And32,(_ff),(_gg)),(_hh))
1530
1531#define NOT(_jj) \
1532 unop(Iop_Not32, (_jj))
1533
1534 switch (op) {
cerion5b2325f2005-12-23 00:55:09 +00001535 case /* 0 */ PPCG_FLAG_OP_ADD:
1536 case /* 1 */ PPCG_FLAG_OP_ADDE:
ceriond953ebb2005-11-29 13:27:20 +00001537 /* (argL^argR^-1) & (argL^res) & (1<<31) ?1:0 */
1538 // i.e. ((both_same_sign) & (sign_changed) & (sign_mask))
1539 xer_ov
1540 = AND3( XOR3(argL,argR,mkU32(-1)),
1541 XOR2(argL,res),
1542 mkU32(INT32_MIN) );
1543 /* xer_ov can only be 0 or 1<<31 */
1544 xer_ov
1545 = binop(Iop_Shr32, xer_ov, mkU8(31) );
1546 break;
1547
cerion5b2325f2005-12-23 00:55:09 +00001548 case /* 2 */ PPCG_FLAG_OP_DIVW:
ceriond953ebb2005-11-29 13:27:20 +00001549 /* (argL == INT32_MIN && argR == -1) || argR == 0 */
1550 xer_ov
1551 = mkOR1(
1552 mkAND1(
1553 binop(Iop_CmpEQ32, argL, mkU32(INT32_MIN)),
1554 binop(Iop_CmpEQ32, argR, mkU32(-1))
1555 ),
1556 binop(Iop_CmpEQ32, argR, mkU32(0) )
1557 );
1558 xer_ov
1559 = unop(Iop_1Uto32, xer_ov);
1560 break;
1561
cerion5b2325f2005-12-23 00:55:09 +00001562 case /* 3 */ PPCG_FLAG_OP_DIVWU:
ceriond953ebb2005-11-29 13:27:20 +00001563 /* argR == 0 */
1564 xer_ov
1565 = unop(Iop_1Uto32, binop(Iop_CmpEQ32, argR, mkU32(0)));
1566 break;
1567
cerion5b2325f2005-12-23 00:55:09 +00001568 case /* 4 */ PPCG_FLAG_OP_MULLW:
ceriond953ebb2005-11-29 13:27:20 +00001569 /* OV true if result can't be represented in 32 bits
1570 i.e sHi != sign extension of sLo */
1571 t64 = newTemp(Ity_I64);
1572 assign( t64, binop(Iop_MullS32, argL, argR) );
1573 xer_ov
1574 = binop( Iop_CmpNE32,
1575 unop(Iop_64HIto32, mkexpr(t64)),
1576 binop( Iop_Sar32,
1577 unop(Iop_64to32, mkexpr(t64)),
1578 mkU8(31))
1579 );
1580 xer_ov
1581 = unop(Iop_1Uto32, xer_ov);
1582 break;
1583
cerion5b2325f2005-12-23 00:55:09 +00001584 case /* 5 */ PPCG_FLAG_OP_NEG:
ceriond953ebb2005-11-29 13:27:20 +00001585 /* argL == INT32_MIN */
1586 xer_ov
1587 = unop( Iop_1Uto32,
1588 binop(Iop_CmpEQ32, argL, mkU32(INT32_MIN)) );
1589 break;
1590
cerion5b2325f2005-12-23 00:55:09 +00001591 case /* 6 */ PPCG_FLAG_OP_SUBF:
1592 case /* 7 */ PPCG_FLAG_OP_SUBFC:
1593 case /* 8 */ PPCG_FLAG_OP_SUBFE:
ceriond953ebb2005-11-29 13:27:20 +00001594 /* ((~argL)^argR^-1) & ((~argL)^res) & (1<<31) ?1:0; */
1595 xer_ov
1596 = AND3( XOR3(NOT(argL),argR,mkU32(-1)),
1597 XOR2(NOT(argL),res),
1598 mkU32(INT32_MIN) );
1599 /* xer_ov can only be 0 or 1<<31 */
1600 xer_ov
1601 = binop(Iop_Shr32, xer_ov, mkU8(31) );
1602 break;
1603
1604 default:
1605 vex_printf("set_XER_OV: op = %u\n", op);
cerion5b2325f2005-12-23 00:55:09 +00001606 vpanic("set_XER_OV(ppc)");
sewardj20ef5472005-07-21 14:48:31 +00001607 }
ceriond953ebb2005-11-29 13:27:20 +00001608
sewardj20ef5472005-07-21 14:48:31 +00001609 /* xer_ov MUST denote either 0 or 1, no other value allowed */
ceriond953ebb2005-11-29 13:27:20 +00001610 putXER_OV( unop(Iop_32to8, xer_ov) );
sewardj20ef5472005-07-21 14:48:31 +00001611
1612 /* Update the summary overflow */
cerionedf7fc52005-11-18 20:57:41 +00001613 putXER_SO( binop(Iop_Or8, getXER_SO(), getXER_OV()) );
sewardj20ef5472005-07-21 14:48:31 +00001614
1615# undef INT32_MIN
1616# undef AND3
1617# undef XOR3
1618# undef XOR2
1619# undef NOT
1620}
1621
ceriond953ebb2005-11-29 13:27:20 +00001622static void set_XER_OV_64( UInt op, IRExpr* res,
1623 IRExpr* argL, IRExpr* argR )
1624{
1625 IRExpr* xer_ov;
cerion5b2325f2005-12-23 00:55:09 +00001626 vassert(op < PPCG_FLAG_OP_NUMBER);
ceriond953ebb2005-11-29 13:27:20 +00001627 vassert(typeOfIRExpr(irbb->tyenv,res) == Ity_I64);
1628 vassert(typeOfIRExpr(irbb->tyenv,argL) == Ity_I64);
1629 vassert(typeOfIRExpr(irbb->tyenv,argR) == Ity_I64);
1630
cerion2831b002005-11-30 19:55:22 +00001631# define INT64_MIN 0x8000000000000000ULL
ceriond953ebb2005-11-29 13:27:20 +00001632
1633# define XOR2(_aa,_bb) \
1634 binop(Iop_Xor64,(_aa),(_bb))
1635
1636# define XOR3(_cc,_dd,_ee) \
1637 binop(Iop_Xor64,binop(Iop_Xor64,(_cc),(_dd)),(_ee))
1638
1639# define AND3(_ff,_gg,_hh) \
1640 binop(Iop_And64,binop(Iop_And64,(_ff),(_gg)),(_hh))
1641
1642#define NOT(_jj) \
1643 unop(Iop_Not64, (_jj))
1644
1645 switch (op) {
cerion5b2325f2005-12-23 00:55:09 +00001646 case /* 0 */ PPCG_FLAG_OP_ADD:
1647 case /* 1 */ PPCG_FLAG_OP_ADDE:
ceriond953ebb2005-11-29 13:27:20 +00001648 /* (argL^argR^-1) & (argL^res) & (1<<63) ? 1:0 */
1649 // i.e. ((both_same_sign) & (sign_changed) & (sign_mask))
1650 xer_ov
1651 = AND3( XOR3(argL,argR,mkU64(-1)),
1652 XOR2(argL,res),
1653 mkU64(INT64_MIN) );
1654 /* xer_ov can only be 0 or 1<<63 */
1655 xer_ov
1656 = unop(Iop_64to1, binop(Iop_Shr64, xer_ov, mkU8(63)));
1657 break;
1658
cerion5b2325f2005-12-23 00:55:09 +00001659 case /* 2 */ PPCG_FLAG_OP_DIVW:
ceriond953ebb2005-11-29 13:27:20 +00001660 /* (argL == INT64_MIN && argR == -1) || argR == 0 */
1661 xer_ov
1662 = mkOR1(
1663 mkAND1(
1664 binop(Iop_CmpEQ64, argL, mkU64(INT64_MIN)),
1665 binop(Iop_CmpEQ64, argR, mkU64(-1))
1666 ),
1667 binop(Iop_CmpEQ64, argR, mkU64(0) )
1668 );
1669 break;
1670
cerion5b2325f2005-12-23 00:55:09 +00001671 case /* 3 */ PPCG_FLAG_OP_DIVWU:
ceriond953ebb2005-11-29 13:27:20 +00001672 /* argR == 0 */
1673 xer_ov
1674 = binop(Iop_CmpEQ64, argR, mkU64(0));
1675 break;
1676
cerion5b2325f2005-12-23 00:55:09 +00001677 case /* 4 */ PPCG_FLAG_OP_MULLW: {
ceriond953ebb2005-11-29 13:27:20 +00001678 /* OV true if result can't be represented in 64 bits
1679 i.e sHi != sign extension of sLo */
ceriond953ebb2005-11-29 13:27:20 +00001680 xer_ov
cerionbb01b7c2005-12-16 13:40:18 +00001681 = binop( Iop_CmpNE32,
1682 unop(Iop_64HIto32, res),
1683 binop( Iop_Sar32,
1684 unop(Iop_64to32, res),
1685 mkU8(31))
1686 );
ceriond953ebb2005-11-29 13:27:20 +00001687 break;
1688 }
1689
cerion5b2325f2005-12-23 00:55:09 +00001690 case /* 5 */ PPCG_FLAG_OP_NEG:
ceriond953ebb2005-11-29 13:27:20 +00001691 /* argL == INT64_MIN */
1692 xer_ov
1693 = binop(Iop_CmpEQ64, argL, mkU64(INT64_MIN));
1694 break;
1695
cerion5b2325f2005-12-23 00:55:09 +00001696 case /* 6 */ PPCG_FLAG_OP_SUBF:
1697 case /* 7 */ PPCG_FLAG_OP_SUBFC:
1698 case /* 8 */ PPCG_FLAG_OP_SUBFE:
ceriond953ebb2005-11-29 13:27:20 +00001699 /* ((~argL)^argR^-1) & ((~argL)^res) & (1<<63) ?1:0; */
1700 xer_ov
1701 = AND3( XOR3(NOT(argL),argR,mkU64(-1)),
1702 XOR2(NOT(argL),res),
1703 mkU64(INT64_MIN) );
1704 /* xer_ov can only be 0 or 1<<63 */
1705 xer_ov
1706 = unop(Iop_64to1, binop(Iop_Shr64, xer_ov, mkU8(63)));
1707 break;
1708
1709 default:
1710 vex_printf("set_XER_OV: op = %u\n", op);
cerion5b2325f2005-12-23 00:55:09 +00001711 vpanic("set_XER_OV(ppc64)");
ceriond953ebb2005-11-29 13:27:20 +00001712 }
1713
1714 /* xer_ov MUST denote either 0 or 1, no other value allowed */
1715 putXER_OV( unop(Iop_1Uto8, xer_ov) );
1716
1717 /* Update the summary overflow */
1718 putXER_SO( binop(Iop_Or8, getXER_SO(), getXER_OV()) );
1719
1720# undef INT64_MIN
1721# undef AND3
1722# undef XOR3
1723# undef XOR2
1724# undef NOT
1725}
1726
1727static void set_XER_OV ( IRType ty, UInt op, IRExpr* res,
1728 IRExpr* argL, IRExpr* argR )
1729{
1730 if (ty == Ity_I32)
1731 set_XER_OV_32( op, res, argL, argR );
1732 else
1733 set_XER_OV_64( op, res, argL, argR );
1734}
1735
1736
1737
sewardjb51f0f42005-07-18 11:38:02 +00001738/* RES is the result of doing OP on ARGL and ARGR with the old %XER.CA
1739 value being OLDCA. Set %XER.CA accordingly. */
cerione9d361a2005-03-04 17:35:29 +00001740
cerion2831b002005-11-30 19:55:22 +00001741static void set_XER_CA_32 ( UInt op, IRExpr* res,
1742 IRExpr* argL, IRExpr* argR, IRExpr* oldca )
cerion38674602005-02-08 02:19:25 +00001743{
sewardj9a036bf2005-03-14 18:19:08 +00001744 IRExpr* xer_ca;
cerion5b2325f2005-12-23 00:55:09 +00001745 vassert(op < PPCG_FLAG_OP_NUMBER);
sewardjb51f0f42005-07-18 11:38:02 +00001746 vassert(typeOfIRExpr(irbb->tyenv,res) == Ity_I32);
1747 vassert(typeOfIRExpr(irbb->tyenv,argL) == Ity_I32);
1748 vassert(typeOfIRExpr(irbb->tyenv,argR) == Ity_I32);
1749 vassert(typeOfIRExpr(irbb->tyenv,oldca) == Ity_I32);
cerion70e24122005-03-16 00:27:37 +00001750
sewardj20ef5472005-07-21 14:48:31 +00001751 /* Incoming oldca is assumed to hold the values 0 or 1 only. This
1752 seems reasonable given that it's always generated by
cerionedf7fc52005-11-18 20:57:41 +00001753 getXER_CA32(), which masks it accordingly. In any case it being
cerion75949202005-12-24 13:14:11 +00001754 0 or 1 is an invariant of the ppc guest state representation;
sewardj20ef5472005-07-21 14:48:31 +00001755 if it has any other value, that invariant has been violated. */
cerione9d361a2005-03-04 17:35:29 +00001756
sewardj20ef5472005-07-21 14:48:31 +00001757 switch (op) {
cerion5b2325f2005-12-23 00:55:09 +00001758 case /* 0 */ PPCG_FLAG_OP_ADD:
ceriond953ebb2005-11-29 13:27:20 +00001759 /* res <u argL */
1760 xer_ca
1761 = unop(Iop_1Uto32, binop(Iop_CmpLT32U, res, argL));
1762 break;
1763
cerion5b2325f2005-12-23 00:55:09 +00001764 case /* 1 */ PPCG_FLAG_OP_ADDE:
ceriond953ebb2005-11-29 13:27:20 +00001765 /* res <u argL || (old_ca==1 && res==argL) */
1766 xer_ca
1767 = mkOR1(
1768 binop(Iop_CmpLT32U, res, argL),
1769 mkAND1(
1770 binop(Iop_CmpEQ32, oldca, mkU32(1)),
1771 binop(Iop_CmpEQ32, res, argL)
1772 )
1773 );
1774 xer_ca
1775 = unop(Iop_1Uto32, xer_ca);
1776 break;
1777
cerion5b2325f2005-12-23 00:55:09 +00001778 case /* 8 */ PPCG_FLAG_OP_SUBFE:
ceriond953ebb2005-11-29 13:27:20 +00001779 /* res <u argR || (old_ca==1 && res==argR) */
1780 xer_ca
1781 = mkOR1(
1782 binop(Iop_CmpLT32U, res, argR),
1783 mkAND1(
1784 binop(Iop_CmpEQ32, oldca, mkU32(1)),
1785 binop(Iop_CmpEQ32, res, argR)
1786 )
1787 );
1788 xer_ca
1789 = unop(Iop_1Uto32, xer_ca);
1790 break;
1791
cerion5b2325f2005-12-23 00:55:09 +00001792 case /* 7 */ PPCG_FLAG_OP_SUBFC:
1793 case /* 9 */ PPCG_FLAG_OP_SUBFI:
ceriond953ebb2005-11-29 13:27:20 +00001794 /* res <=u argR */
1795 xer_ca
1796 = unop(Iop_1Uto32, binop(Iop_CmpLE32U, res, argR));
1797 break;
1798
cerion5b2325f2005-12-23 00:55:09 +00001799 case /* 10 */ PPCG_FLAG_OP_SRAW:
ceriond953ebb2005-11-29 13:27:20 +00001800 /* The shift amount is guaranteed to be in 0 .. 63 inclusive.
1801 If it is <= 31, behave like SRAWI; else XER.CA is the sign
1802 bit of argL. */
1803 /* This term valid for shift amount < 32 only */
1804 xer_ca
1805 = binop(
1806 Iop_And32,
1807 binop(Iop_Sar32, argL, mkU8(31)),
1808 binop( Iop_And32,
1809 argL,
1810 binop( Iop_Sub32,
cerion5b2325f2005-12-23 00:55:09 +00001811 binop(Iop_Shl32, mkU32(1),
1812 unop(Iop_32to8,argR)),
ceriond953ebb2005-11-29 13:27:20 +00001813 mkU32(1) )
1814 )
sewardj20ef5472005-07-21 14:48:31 +00001815 );
ceriond953ebb2005-11-29 13:27:20 +00001816 xer_ca
1817 = IRExpr_Mux0X(
1818 /* shift amt > 31 ? */
1819 unop(Iop_1Uto8, binop(Iop_CmpLT32U, mkU32(31), argR)),
1820 /* no -- be like srawi */
1821 unop(Iop_1Uto32, binop(Iop_CmpNE32, xer_ca, mkU32(0))),
1822 /* yes -- get sign bit of argL */
1823 binop(Iop_Shr32, argL, mkU8(31))
1824 );
1825 break;
sewardj20ef5472005-07-21 14:48:31 +00001826
cerion5b2325f2005-12-23 00:55:09 +00001827 case /* 11 */ PPCG_FLAG_OP_SRAWI:
ceriond953ebb2005-11-29 13:27:20 +00001828 /* xer_ca is 1 iff src was negative and bits_shifted_out !=
1829 0. Since the shift amount is known to be in the range
1830 0 .. 31 inclusive the following seems viable:
1831 xer.ca == 1 iff the following is nonzero:
1832 (argL >>s 31) -- either all 0s or all 1s
1833 & (argL & (1<<argR)-1) -- the stuff shifted out */
1834 xer_ca
1835 = binop(
1836 Iop_And32,
1837 binop(Iop_Sar32, argL, mkU8(31)),
1838 binop( Iop_And32,
1839 argL,
1840 binop( Iop_Sub32,
cerion5b2325f2005-12-23 00:55:09 +00001841 binop(Iop_Shl32, mkU32(1),
1842 unop(Iop_32to8,argR)),
ceriond953ebb2005-11-29 13:27:20 +00001843 mkU32(1) )
1844 )
sewardj20ef5472005-07-21 14:48:31 +00001845 );
ceriond953ebb2005-11-29 13:27:20 +00001846 xer_ca
1847 = unop(Iop_1Uto32, binop(Iop_CmpNE32, xer_ca, mkU32(0)));
1848 break;
1849
1850 default:
1851 vex_printf("set_XER_CA: op = %u\n", op);
cerion5b2325f2005-12-23 00:55:09 +00001852 vpanic("set_XER_CA(ppc)");
sewardj20ef5472005-07-21 14:48:31 +00001853 }
1854
1855 /* xer_ca MUST denote either 0 or 1, no other value allowed */
cerionedf7fc52005-11-18 20:57:41 +00001856 putXER_CA( unop(Iop_32to8, xer_ca) );
sewardjb51f0f42005-07-18 11:38:02 +00001857}
1858
cerion2831b002005-11-30 19:55:22 +00001859static void set_XER_CA_64 ( UInt op, IRExpr* res,
1860 IRExpr* argL, IRExpr* argR, IRExpr* oldca )
sewardje14bb9f2005-07-22 09:39:02 +00001861{
ceriond953ebb2005-11-29 13:27:20 +00001862 IRExpr* xer_ca;
cerion5b2325f2005-12-23 00:55:09 +00001863 vassert(op < PPCG_FLAG_OP_NUMBER);
ceriond953ebb2005-11-29 13:27:20 +00001864 vassert(typeOfIRExpr(irbb->tyenv,res) == Ity_I64);
1865 vassert(typeOfIRExpr(irbb->tyenv,argL) == Ity_I64);
1866 vassert(typeOfIRExpr(irbb->tyenv,argR) == Ity_I64);
1867 vassert(typeOfIRExpr(irbb->tyenv,oldca) == Ity_I64);
sewardje14bb9f2005-07-22 09:39:02 +00001868
ceriond953ebb2005-11-29 13:27:20 +00001869 /* Incoming oldca is assumed to hold the values 0 or 1 only. This
1870 seems reasonable given that it's always generated by
1871 getXER_CA32(), which masks it accordingly. In any case it being
cerion75949202005-12-24 13:14:11 +00001872 0 or 1 is an invariant of the ppc guest state representation;
ceriond953ebb2005-11-29 13:27:20 +00001873 if it has any other value, that invariant has been violated. */
1874
1875 switch (op) {
cerion5b2325f2005-12-23 00:55:09 +00001876 case /* 0 */ PPCG_FLAG_OP_ADD:
ceriond953ebb2005-11-29 13:27:20 +00001877 /* res <u argL */
1878 xer_ca
cerionf0de28c2005-12-13 20:21:11 +00001879 = unop(Iop_1Uto32, binop(Iop_CmpLT64U, res, argL));
ceriond953ebb2005-11-29 13:27:20 +00001880 break;
sewardje14bb9f2005-07-22 09:39:02 +00001881
cerion5b2325f2005-12-23 00:55:09 +00001882 case /* 1 */ PPCG_FLAG_OP_ADDE:
ceriond953ebb2005-11-29 13:27:20 +00001883 /* res <u argL || (old_ca==1 && res==argL) */
1884 xer_ca
1885 = mkOR1(
1886 binop(Iop_CmpLT64U, res, argL),
1887 mkAND1(
cerionf0de28c2005-12-13 20:21:11 +00001888 binop(Iop_CmpEQ64, oldca, mkU64(1)),
ceriond953ebb2005-11-29 13:27:20 +00001889 binop(Iop_CmpEQ64, res, argL)
1890 )
1891 );
cerionf0de28c2005-12-13 20:21:11 +00001892 xer_ca
1893 = unop(Iop_1Uto32, xer_ca);
cerionedf7fc52005-11-18 20:57:41 +00001894 break;
ceriond953ebb2005-11-29 13:27:20 +00001895
cerion5b2325f2005-12-23 00:55:09 +00001896 case /* 8 */ PPCG_FLAG_OP_SUBFE:
ceriond953ebb2005-11-29 13:27:20 +00001897 /* res <u argR || (old_ca==1 && res==argR) */
1898 xer_ca
1899 = mkOR1(
1900 binop(Iop_CmpLT64U, res, argR),
1901 mkAND1(
1902 binop(Iop_CmpEQ64, oldca, mkU64(1)),
1903 binop(Iop_CmpEQ64, res, argR)
1904 )
1905 );
cerionf0de28c2005-12-13 20:21:11 +00001906 xer_ca
1907 = unop(Iop_1Uto32, xer_ca);
ceriond953ebb2005-11-29 13:27:20 +00001908 break;
1909
cerion5b2325f2005-12-23 00:55:09 +00001910 case /* 7 */ PPCG_FLAG_OP_SUBFC:
1911 case /* 9 */ PPCG_FLAG_OP_SUBFI:
ceriond953ebb2005-11-29 13:27:20 +00001912 /* res <=u argR */
1913 xer_ca
cerionf0de28c2005-12-13 20:21:11 +00001914 = unop(Iop_1Uto32, binop(Iop_CmpLE64U, res, argR));
ceriond953ebb2005-11-29 13:27:20 +00001915 break;
1916
1917
cerion5b2325f2005-12-23 00:55:09 +00001918 case /* 10 */ PPCG_FLAG_OP_SRAW:
cerionf0de28c2005-12-13 20:21:11 +00001919 /* The shift amount is guaranteed to be in 0 .. 31 inclusive.
ceriond953ebb2005-11-29 13:27:20 +00001920 If it is <= 31, behave like SRAWI; else XER.CA is the sign
1921 bit of argL. */
cerionf0de28c2005-12-13 20:21:11 +00001922 /* This term valid for shift amount < 31 only */
1923
ceriond953ebb2005-11-29 13:27:20 +00001924 xer_ca
1925 = binop(
cerionf0de28c2005-12-13 20:21:11 +00001926 Iop_And64,
1927 binop(Iop_Sar64, argL, mkU8(31)),
1928 binop( Iop_And64,
ceriond953ebb2005-11-29 13:27:20 +00001929 argL,
cerionf0de28c2005-12-13 20:21:11 +00001930 binop( Iop_Sub64,
cerion5b2325f2005-12-23 00:55:09 +00001931 binop(Iop_Shl64, mkU64(1),
1932 unop(Iop_64to8,argR)),
cerionf0de28c2005-12-13 20:21:11 +00001933 mkU64(1) )
ceriond953ebb2005-11-29 13:27:20 +00001934 )
1935 );
1936 xer_ca
1937 = IRExpr_Mux0X(
1938 /* shift amt > 31 ? */
cerionf0de28c2005-12-13 20:21:11 +00001939 unop(Iop_1Uto8, binop(Iop_CmpLT64U, mkU64(31), argR)),
ceriond953ebb2005-11-29 13:27:20 +00001940 /* no -- be like srawi */
cerionf0de28c2005-12-13 20:21:11 +00001941 unop(Iop_1Uto32, binop(Iop_CmpNE64, xer_ca, mkU64(0))),
ceriond953ebb2005-11-29 13:27:20 +00001942 /* yes -- get sign bit of argL */
cerionf0de28c2005-12-13 20:21:11 +00001943 unop(Iop_64to32, binop(Iop_Shr64, argL, mkU8(63)))
ceriond953ebb2005-11-29 13:27:20 +00001944 );
1945 break;
1946
cerion5b2325f2005-12-23 00:55:09 +00001947 case /* 11 */ PPCG_FLAG_OP_SRAWI:
cerionf0de28c2005-12-13 20:21:11 +00001948 /* xer_ca is 1 iff src was negative and bits_shifted_out != 0.
1949 Since the shift amount is known to be in the range 0 .. 31
1950 inclusive the following seems viable:
ceriond953ebb2005-11-29 13:27:20 +00001951 xer.ca == 1 iff the following is nonzero:
1952 (argL >>s 31) -- either all 0s or all 1s
1953 & (argL & (1<<argR)-1) -- the stuff shifted out */
cerionf0de28c2005-12-13 20:21:11 +00001954
ceriond953ebb2005-11-29 13:27:20 +00001955 xer_ca
1956 = binop(
cerionf0de28c2005-12-13 20:21:11 +00001957 Iop_And64,
1958 binop(Iop_Sar64, argL, mkU8(31)),
1959 binop( Iop_And64,
ceriond953ebb2005-11-29 13:27:20 +00001960 argL,
cerionf0de28c2005-12-13 20:21:11 +00001961 binop( Iop_Sub64,
cerion5b2325f2005-12-23 00:55:09 +00001962 binop(Iop_Shl64, mkU64(1),
1963 unop(Iop_64to8,argR)),
cerionf0de28c2005-12-13 20:21:11 +00001964 mkU64(1) )
ceriond953ebb2005-11-29 13:27:20 +00001965 )
1966 );
1967 xer_ca
cerionf0de28c2005-12-13 20:21:11 +00001968 = unop(Iop_1Uto32, binop(Iop_CmpNE64, xer_ca, mkU64(0)));
ceriond953ebb2005-11-29 13:27:20 +00001969 break;
ceriond953ebb2005-11-29 13:27:20 +00001970
cerionf0de28c2005-12-13 20:21:11 +00001971
cerion5b2325f2005-12-23 00:55:09 +00001972 case /* 12 */ PPCG_FLAG_OP_SRAD:
cerionf0de28c2005-12-13 20:21:11 +00001973 /* The shift amount is guaranteed to be in 0 .. 63 inclusive.
1974 If it is <= 63, behave like SRADI; else XER.CA is the sign
1975 bit of argL. */
1976 /* This term valid for shift amount < 63 only */
1977
1978 xer_ca
1979 = binop(
1980 Iop_And64,
1981 binop(Iop_Sar64, argL, mkU8(63)),
1982 binop( Iop_And64,
1983 argL,
1984 binop( Iop_Sub64,
cerion5b2325f2005-12-23 00:55:09 +00001985 binop(Iop_Shl64, mkU64(1),
1986 unop(Iop_64to8,argR)),
cerionf0de28c2005-12-13 20:21:11 +00001987 mkU64(1) )
1988 )
1989 );
1990 xer_ca
1991 = IRExpr_Mux0X(
1992 /* shift amt > 63 ? */
1993 unop(Iop_1Uto8, binop(Iop_CmpLT64U, mkU64(63), argR)),
cerion07b07a92005-12-22 14:32:35 +00001994 /* no -- be like sradi */
1995 unop(Iop_1Uto32, binop(Iop_CmpNE64, xer_ca, mkU64(0))),
cerionf0de28c2005-12-13 20:21:11 +00001996 /* yes -- get sign bit of argL */
1997 unop(Iop_64to32, binop(Iop_Shr64, argL, mkU8(63)))
1998 );
1999 break;
2000
2001
cerion5b2325f2005-12-23 00:55:09 +00002002 case /* 13 */ PPCG_FLAG_OP_SRADI:
cerionf0de28c2005-12-13 20:21:11 +00002003 /* xer_ca is 1 iff src was negative and bits_shifted_out != 0.
2004 Since the shift amount is known to be in the range 0 .. 63
2005 inclusive, the following seems viable:
2006 xer.ca == 1 iff the following is nonzero:
2007 (argL >>s 63) -- either all 0s or all 1s
2008 & (argL & (1<<argR)-1) -- the stuff shifted out */
2009
2010 xer_ca
2011 = binop(
2012 Iop_And64,
2013 binop(Iop_Sar64, argL, mkU8(63)),
2014 binop( Iop_And64,
2015 argL,
2016 binop( Iop_Sub64,
cerion5b2325f2005-12-23 00:55:09 +00002017 binop(Iop_Shl64, mkU64(1),
2018 unop(Iop_64to8,argR)),
cerionf0de28c2005-12-13 20:21:11 +00002019 mkU64(1) )
2020 )
2021 );
2022 xer_ca
2023 = unop(Iop_1Uto32, binop(Iop_CmpNE64, xer_ca, mkU64(0)));
2024 break;
2025
ceriond953ebb2005-11-29 13:27:20 +00002026 default:
2027 vex_printf("set_XER_CA: op = %u\n", op);
cerionf0de28c2005-12-13 20:21:11 +00002028 vpanic("set_XER_CA(ppc64)");
sewardje14bb9f2005-07-22 09:39:02 +00002029 }
2030
ceriond953ebb2005-11-29 13:27:20 +00002031 /* xer_ca MUST denote either 0 or 1, no other value allowed */
cerionf0de28c2005-12-13 20:21:11 +00002032 putXER_CA( unop(Iop_32to8, xer_ca) );
sewardje14bb9f2005-07-22 09:39:02 +00002033}
2034
cerion2831b002005-11-30 19:55:22 +00002035static void set_XER_CA ( IRType ty, UInt op, IRExpr* res,
2036 IRExpr* argL, IRExpr* argR, IRExpr* oldca )
cerionedf7fc52005-11-18 20:57:41 +00002037{
cerion2831b002005-11-30 19:55:22 +00002038 if (ty == Ity_I32)
ceriond953ebb2005-11-29 13:27:20 +00002039 set_XER_CA_32( op, res, argL, argR, oldca );
2040 else
2041 set_XER_CA_64( op, res, argL, argR, oldca );
cerionedf7fc52005-11-18 20:57:41 +00002042}
2043
ceriond953ebb2005-11-29 13:27:20 +00002044
2045
2046/*------------------------------------------------------------*/
2047/*--- Read/write to guest-state --- */
2048/*------------------------------------------------------------*/
2049
cerionf0de28c2005-12-13 20:21:11 +00002050static IRExpr* /* :: Ity_I32/64 */ getGST ( PPC_GST reg )
cerionedf7fc52005-11-18 20:57:41 +00002051{
ceriond953ebb2005-11-29 13:27:20 +00002052 IRType ty = mode64 ? Ity_I64 : Ity_I32;
cerionedf7fc52005-11-18 20:57:41 +00002053 switch (reg) {
ceriond953ebb2005-11-29 13:27:20 +00002054 case PPC_GST_LR:
cerion5b2325f2005-12-23 00:55:09 +00002055 return IRExpr_Get( OFFB_LR, ty );
ceriond953ebb2005-11-29 13:27:20 +00002056
2057 case PPC_GST_CTR:
cerion5b2325f2005-12-23 00:55:09 +00002058 return IRExpr_Get( OFFB_CTR, ty );
ceriond953ebb2005-11-29 13:27:20 +00002059
2060 case PPC_GST_VRSAVE:
cerion5b2325f2005-12-23 00:55:09 +00002061 return IRExpr_Get( OFFB_VRSAVE, Ity_I32 );
ceriond953ebb2005-11-29 13:27:20 +00002062
2063 case PPC_GST_VSCR:
cerion5b2325f2005-12-23 00:55:09 +00002064 return binop(Iop_And32, IRExpr_Get( OFFB_VSCR,Ity_I32 ),
2065 mkU32(MASK_VSCR_VALID));
ceriond953ebb2005-11-29 13:27:20 +00002066
2067 case PPC_GST_CR: {
cerionedf7fc52005-11-18 20:57:41 +00002068 /* Synthesise the entire CR into a single word. Expensive. */
2069# define FIELD(_n) \
2070 binop(Iop_Shl32, \
2071 unop(Iop_8Uto32, \
2072 binop(Iop_Or8, \
2073 binop(Iop_And8, getCR321(_n), mkU8(7<<1)), \
2074 binop(Iop_And8, getCR0(_n), mkU8(1)) \
2075 ) \
2076 ), \
2077 mkU8(4 * (7-(_n))) \
2078 )
2079 return binop(Iop_Or32,
2080 binop(Iop_Or32,
2081 binop(Iop_Or32, FIELD(0), FIELD(1)),
2082 binop(Iop_Or32, FIELD(2), FIELD(3))
2083 ),
2084 binop(Iop_Or32,
2085 binop(Iop_Or32, FIELD(4), FIELD(5)),
2086 binop(Iop_Or32, FIELD(6), FIELD(7))
2087 )
2088 );
2089# undef FIELD
2090 }
ceriond953ebb2005-11-29 13:27:20 +00002091
2092 case PPC_GST_XER:
cerionedf7fc52005-11-18 20:57:41 +00002093 return binop(Iop_Or32,
2094 binop(Iop_Or32,
2095 binop( Iop_Shl32, getXER_SO32(), mkU8(31)),
2096 binop( Iop_Shl32, getXER_OV32(), mkU8(30))),
2097 binop(Iop_Or32,
2098 binop( Iop_Shl32, getXER_CA32(), mkU8(29)),
2099 getXER_BC32()));
ceriond953ebb2005-11-29 13:27:20 +00002100
2101 case PPC_GST_RESVN:
cerion5b2325f2005-12-23 00:55:09 +00002102 return IRExpr_Get( OFFB_RESVN, ty);
ceriond953ebb2005-11-29 13:27:20 +00002103
cerionedf7fc52005-11-18 20:57:41 +00002104 default:
cerion5b2325f2005-12-23 00:55:09 +00002105 vex_printf("getGST(ppc): reg = %u", reg);
2106 vpanic("getGST(ppc)");
ceriond953ebb2005-11-29 13:27:20 +00002107 }
2108}
2109
2110/* Get a masked word from the given reg */
2111static IRExpr* /* ::Ity_I32 */ getGST_masked ( PPC_GST reg, UInt mask )
2112{
2113 IRTemp val = newTemp(Ity_I32);
2114 vassert( reg < PPC_GST_MAX );
2115
2116 switch (reg) {
2117
2118 case PPC_GST_FPSCR: {
cerion5b2325f2005-12-23 00:55:09 +00002119 /* Vex-generated code expects the FPSCR to be set as follows:
ceriond953ebb2005-11-29 13:27:20 +00002120 all exceptions masked, round-to-nearest.
2121 This corresponds to a FPSCR value of 0x0. */
2122
2123 /* We're only keeping track of the rounding mode,
2124 so if the mask isn't asking for this, just return 0x0 */
2125 if (mask & 0x3) {
cerion5b2325f2005-12-23 00:55:09 +00002126 assign( val, IRExpr_Get( OFFB_FPROUND, Ity_I32 ) );
ceriond953ebb2005-11-29 13:27:20 +00002127 } else {
2128 assign( val, mkU32(0x0) );
2129 }
2130 break;
2131 }
2132
2133 default:
cerion5b2325f2005-12-23 00:55:09 +00002134 vex_printf("getGST_masked(ppc): reg = %u", reg);
2135 vpanic("getGST_masked(ppc)");
ceriond953ebb2005-11-29 13:27:20 +00002136 }
2137
2138 if (mask != 0xFFFFFFFF) {
2139 return binop(Iop_And32, mkexpr(val), mkU32(mask));
2140 } else {
2141 return mkexpr(val);
2142 }
2143}
2144
2145/* Fetch the specified REG[FLD] nibble (as per IBM/hardware notation)
2146 and return it at the bottom of an I32; the top 27 bits are
2147 guaranteed to be zero. */
2148static IRExpr* /* ::Ity_I32 */ getGST_field ( PPC_GST reg, UInt fld )
2149{
2150 UInt shft, mask;
2151
2152 vassert( fld < 8 );
2153 vassert( reg < PPC_GST_MAX );
2154
2155 shft = 4*(7-fld);
2156 mask = 0xF<<shft;
2157
2158 switch (reg) {
2159 case PPC_GST_XER:
2160 vassert(fld ==7);
2161 return binop(Iop_Or32,
2162 binop(Iop_Or32,
2163 binop(Iop_Shl32, getXER_SO32(), mkU8(3)),
2164 binop(Iop_Shl32, getXER_OV32(), mkU8(2))),
2165 binop( Iop_Shl32, getXER_CA32(), mkU8(1)));
2166 break;
2167
2168 default:
2169 if (shft == 0)
2170 return getGST_masked( reg, mask );
2171 else
2172 return binop(Iop_Shr32,
2173 getGST_masked( reg, mask ),
2174 mkU8(toUChar( shft )));
2175 }
2176}
2177
2178static void putGST ( PPC_GST reg, IRExpr* src )
2179{
cerion5b2325f2005-12-23 00:55:09 +00002180 IRType ty = mode64 ? Ity_I64 : Ity_I32;
2181 IRType ty_src = typeOfIRExpr(irbb->tyenv,src );
ceriond953ebb2005-11-29 13:27:20 +00002182 vassert( reg < PPC_GST_MAX );
2183 switch (reg) {
2184 case PPC_GST_CIA:
cerion5b2325f2005-12-23 00:55:09 +00002185 vassert( ty_src == ty );
2186 stmt( IRStmt_Put( OFFB_CIA, src ) );
ceriond953ebb2005-11-29 13:27:20 +00002187 break;
2188 case PPC_GST_LR:
cerion5b2325f2005-12-23 00:55:09 +00002189 vassert( ty_src == ty );
2190 stmt( IRStmt_Put( OFFB_LR, src ) );
ceriond953ebb2005-11-29 13:27:20 +00002191 break;
2192 case PPC_GST_CTR:
cerion5b2325f2005-12-23 00:55:09 +00002193 vassert( ty_src == ty );
2194 stmt( IRStmt_Put( OFFB_CTR, src ) );
ceriond953ebb2005-11-29 13:27:20 +00002195 break;
2196 case PPC_GST_VRSAVE:
cerion5b2325f2005-12-23 00:55:09 +00002197 vassert( ty_src == Ity_I32 );
2198 stmt( IRStmt_Put( OFFB_VRSAVE,src));
ceriond953ebb2005-11-29 13:27:20 +00002199 break;
2200 case PPC_GST_VSCR:
cerion5b2325f2005-12-23 00:55:09 +00002201 vassert( ty_src == Ity_I32 );
2202 stmt( IRStmt_Put( OFFB_VSCR,
ceriond953ebb2005-11-29 13:27:20 +00002203 binop(Iop_And32, src,
2204 mkU32(MASK_VSCR_VALID)) ) );
2205 break;
2206 case PPC_GST_XER:
cerion5b2325f2005-12-23 00:55:09 +00002207 vassert( ty_src == Ity_I32 );
ceriond953ebb2005-11-29 13:27:20 +00002208 putXER_SO( unop(Iop_32to8, binop(Iop_Shr32, src, mkU8(31))) );
2209 putXER_OV( unop(Iop_32to8, binop(Iop_Shr32, src, mkU8(30))) );
2210 putXER_CA( unop(Iop_32to8, binop(Iop_Shr32, src, mkU8(29))) );
2211 putXER_BC( unop(Iop_32to8, src) );
2212 break;
2213
2214 case PPC_GST_EMWARN:
cerion5b2325f2005-12-23 00:55:09 +00002215 vassert( ty_src == Ity_I32 );
2216 stmt( IRStmt_Put( OFFB_EMWARN,src) );
ceriond953ebb2005-11-29 13:27:20 +00002217 break;
2218
2219 case PPC_GST_TISTART:
cerion5b2325f2005-12-23 00:55:09 +00002220 vassert( ty_src == ty );
2221 stmt( IRStmt_Put( OFFB_TISTART, src) );
ceriond953ebb2005-11-29 13:27:20 +00002222 break;
2223
2224 case PPC_GST_TILEN:
cerion5b2325f2005-12-23 00:55:09 +00002225 vassert( ty_src == ty );
2226 stmt( IRStmt_Put( OFFB_TILEN, src) );
ceriond953ebb2005-11-29 13:27:20 +00002227 break;
2228
2229 case PPC_GST_RESVN:
cerion5b2325f2005-12-23 00:55:09 +00002230 vassert( ty_src == ty );
2231 stmt( IRStmt_Put( OFFB_RESVN, src) );
ceriond953ebb2005-11-29 13:27:20 +00002232 break;
2233
2234 default:
cerion5b2325f2005-12-23 00:55:09 +00002235 vex_printf("putGST(ppc): reg = %u", reg);
2236 vpanic("putGST(ppc)");
cerionedf7fc52005-11-18 20:57:41 +00002237 }
2238}
sewardje14bb9f2005-07-22 09:39:02 +00002239
2240/* Write masked src to the given reg */
ceriond953ebb2005-11-29 13:27:20 +00002241static void putGST_masked ( PPC_GST reg, IRExpr* src, UInt mask )
sewardje14bb9f2005-07-22 09:39:02 +00002242{
ceriond953ebb2005-11-29 13:27:20 +00002243 IRType ty = mode64 ? Ity_I64 : Ity_I32;
2244 vassert( reg < PPC_GST_MAX );
sewardje14bb9f2005-07-22 09:39:02 +00002245 vassert( typeOfIRExpr(irbb->tyenv,src ) == Ity_I32 );
2246
2247 switch (reg) {
ceriond953ebb2005-11-29 13:27:20 +00002248 case PPC_GST_FPSCR: {
sewardje14bb9f2005-07-22 09:39:02 +00002249 /* Allow writes to Rounding Mode */
2250 if (mask & 0x3) {
sewardjb183b852006-02-03 16:08:03 +00002251 /* construct new fpround from new and old values as per mask:
2252 new fpround = (src & (3 & mask)) | (fpround & (3 & ~mask)) */
2253 stmt(
2254 IRStmt_Put(
2255 OFFB_FPROUND,
2256 binop(
2257 Iop_Or32,
2258 binop(Iop_And32, src, mkU32(3 & mask)),
2259 binop(
2260 Iop_And32,
2261 IRExpr_Get(OFFB_FPROUND,Ity_I32),
2262 mkU32(3 & ~mask)
2263 )
2264 )
2265 )
2266 );
sewardje14bb9f2005-07-22 09:39:02 +00002267 }
2268
cerionedf7fc52005-11-18 20:57:41 +00002269 /* Give EmWarn for attempted writes to:
sewardje14bb9f2005-07-22 09:39:02 +00002270 - Exception Controls
2271 - Non-IEEE Mode
2272 */
2273 if (mask & 0xFC) { // Exception Control, Non-IEE mode
sewardj5ff11dd2006-01-20 14:19:25 +00002274 VexEmWarn ew = EmWarn_PPCexns;
sewardje14bb9f2005-07-22 09:39:02 +00002275
2276 /* If any of the src::exception_control bits are actually set,
2277 side-exit to the next insn, reporting the warning,
2278 so that Valgrind's dispatcher sees the warning. */
ceriond953ebb2005-11-29 13:27:20 +00002279 putGST( PPC_GST_EMWARN, mkU32(ew) );
sewardje14bb9f2005-07-22 09:39:02 +00002280 stmt(
2281 IRStmt_Exit(
2282 binop(Iop_CmpNE32, mkU32(ew), mkU32(EmWarn_NONE)),
2283 Ijk_EmWarn,
cerion2831b002005-11-30 19:55:22 +00002284 mkSzConst( ty, nextInsnAddr()) ));
sewardje14bb9f2005-07-22 09:39:02 +00002285 }
2286
cerionedf7fc52005-11-18 20:57:41 +00002287 /* Ignore all other writes */
sewardje14bb9f2005-07-22 09:39:02 +00002288 break;
cerionedf7fc52005-11-18 20:57:41 +00002289 }
sewardje14bb9f2005-07-22 09:39:02 +00002290
2291 default:
cerion5b2325f2005-12-23 00:55:09 +00002292 vex_printf("putGST_masked(ppc): reg = %u", reg);
2293 vpanic("putGST_masked(ppc)");
sewardje14bb9f2005-07-22 09:39:02 +00002294 }
2295}
2296
cerionedf7fc52005-11-18 20:57:41 +00002297/* Write the least significant nibble of src to the specified
2298 REG[FLD] (as per IBM/hardware notation). */
ceriond953ebb2005-11-29 13:27:20 +00002299static void putGST_field ( PPC_GST reg, IRExpr* src, UInt fld )
cerionedf7fc52005-11-18 20:57:41 +00002300{
sewardj41a7b702005-11-18 22:18:23 +00002301 UInt shft, mask;
2302
cerionedf7fc52005-11-18 20:57:41 +00002303 vassert( typeOfIRExpr(irbb->tyenv,src ) == Ity_I32 );
2304 vassert( fld < 8 );
ceriond953ebb2005-11-29 13:27:20 +00002305 vassert( reg < PPC_GST_MAX );
cerionedf7fc52005-11-18 20:57:41 +00002306
sewardj41a7b702005-11-18 22:18:23 +00002307 shft = 4*(7-fld);
2308 mask = 0xF<<shft;
cerionedf7fc52005-11-18 20:57:41 +00002309
2310 switch (reg) {
ceriond953ebb2005-11-29 13:27:20 +00002311 case PPC_GST_CR:
cerionedf7fc52005-11-18 20:57:41 +00002312 putCR0 (fld, binop(Iop_And8, mkU8(1 ), unop(Iop_32to8, src)));
2313 putCR321(fld, binop(Iop_And8, mkU8(7<<1), unop(Iop_32to8, src)));
2314 break;
2315
2316 default:
2317 if (shft == 0) {
ceriond953ebb2005-11-29 13:27:20 +00002318 putGST_masked( reg, src, mask );
cerionedf7fc52005-11-18 20:57:41 +00002319 } else {
ceriond953ebb2005-11-29 13:27:20 +00002320 putGST_masked( reg,
cerionedf7fc52005-11-18 20:57:41 +00002321 binop(Iop_Shl32, src, mkU8(toUChar(shft))),
2322 mask );
2323 }
2324 }
2325}
cerion62bec572005-02-01 21:29:39 +00002326
cerion76222262005-02-05 13:45:57 +00002327
2328
cerione9d361a2005-03-04 17:35:29 +00002329/*------------------------------------------------------------*/
cerion3d870a32005-03-18 12:23:33 +00002330/*--- Integer Instruction Translation --- */
cerione9d361a2005-03-04 17:35:29 +00002331/*------------------------------------------------------------*/
cerion896a1372005-01-25 12:24:25 +00002332
cerion91ad5362005-01-27 23:02:41 +00002333/*
2334 Integer Arithmetic Instructions
2335*/
cerion645c9302005-01-31 10:09:59 +00002336static Bool dis_int_arith ( UInt theInstr )
cerion91ad5362005-01-27 23:02:41 +00002337{
cerion76de5cf2005-11-18 18:25:12 +00002338 /* D-Form, XO-Form */
2339 UChar opc1 = ifieldOPC(theInstr);
2340 UChar rD_addr = ifieldRegDS(theInstr);
2341 UChar rA_addr = ifieldRegA(theInstr);
ceriond953ebb2005-11-29 13:27:20 +00002342 UInt uimm16 = ifieldUIMM16(theInstr);
cerion76de5cf2005-11-18 18:25:12 +00002343 UChar rB_addr = ifieldRegB(theInstr);
2344 UChar flag_OE = ifieldBIT10(theInstr);
2345 UInt opc2 = ifieldOPClo9(theInstr);
2346 UChar flag_rC = ifieldBIT0(theInstr);
2347
ceriond953ebb2005-11-29 13:27:20 +00002348 Long simm16 = extend_s_16to64(uimm16);
2349 IRType ty = mode64 ? Ity_I64 : Ity_I32;
2350 IRTemp rA = newTemp(ty);
2351 IRTemp rB = newTemp(ty);
2352 IRTemp rD = newTemp(ty);
cerion70e24122005-03-16 00:27:37 +00002353
cerionb85e8bb2005-02-16 08:54:33 +00002354 Bool do_rc = False;
cerion91ad5362005-01-27 23:02:41 +00002355
cerion76de5cf2005-11-18 18:25:12 +00002356 assign( rA, getIReg(rA_addr) );
2357 assign( rB, getIReg(rB_addr) ); // XO-Form: rD, rA, rB
sewardjb51f0f42005-07-18 11:38:02 +00002358
cerionb85e8bb2005-02-16 08:54:33 +00002359 switch (opc1) {
cerionb85e8bb2005-02-16 08:54:33 +00002360 /* D-Form */
cerione9d361a2005-03-04 17:35:29 +00002361 case 0x0C: // addic (Add Immediate Carrying, PPC32 p351
ceriond953ebb2005-11-29 13:27:20 +00002362 DIP("addic r%u,r%u,%d\n", rD_addr, rA_addr, (Int)simm16);
cerion2831b002005-11-30 19:55:22 +00002363 assign( rD, binop( mkSzOp(ty, Iop_Add8), mkexpr(rA),
2364 mkSzExtendS16(ty, uimm16) ) );
cerion5b2325f2005-12-23 00:55:09 +00002365 set_XER_CA( ty, PPCG_FLAG_OP_ADD,
cerion2831b002005-11-30 19:55:22 +00002366 mkexpr(rD), mkexpr(rA), mkSzExtendS16(ty, uimm16),
2367 mkSzImm(ty, 0)/*old xer.ca, which is ignored*/ );
cerion4561acb2005-02-21 14:07:48 +00002368 break;
sewardjb51f0f42005-07-18 11:38:02 +00002369
cerione9d361a2005-03-04 17:35:29 +00002370 case 0x0D: // addic. (Add Immediate Carrying and Record, PPC32 p352)
ceriond953ebb2005-11-29 13:27:20 +00002371 DIP("addic. r%u,r%u,%d\n", rD_addr, rA_addr, (Int)simm16);
cerion2831b002005-11-30 19:55:22 +00002372 assign( rD, binop( mkSzOp(ty, Iop_Add8), mkexpr(rA),
2373 mkSzExtendS16(ty, uimm16) ) );
cerion5b2325f2005-12-23 00:55:09 +00002374 set_XER_CA( ty, PPCG_FLAG_OP_ADD,
cerion2831b002005-11-30 19:55:22 +00002375 mkexpr(rD), mkexpr(rA), mkSzExtendS16(ty, uimm16),
2376 mkSzImm(ty, 0)/*old xer.ca, which is ignored*/ );
cerion70e24122005-03-16 00:27:37 +00002377 do_rc = True; // Always record to CR
cerion76de5cf2005-11-18 18:25:12 +00002378 flag_rC = 1;
cerion4561acb2005-02-21 14:07:48 +00002379 break;
2380
cerione9d361a2005-03-04 17:35:29 +00002381 case 0x0E: // addi (Add Immediate, PPC32 p350)
cerionb85e8bb2005-02-16 08:54:33 +00002382 // li rD,val == addi rD,0,val
2383 // la disp(rA) == addi rD,rA,disp
cerion76de5cf2005-11-18 18:25:12 +00002384 if ( rA_addr == 0 ) {
ceriond953ebb2005-11-29 13:27:20 +00002385 DIP("li r%u,%d\n", rD_addr, (Int)simm16);
cerion2831b002005-11-30 19:55:22 +00002386 assign( rD, mkSzExtendS16(ty, uimm16) );
cerionb85e8bb2005-02-16 08:54:33 +00002387 } else {
ceriond953ebb2005-11-29 13:27:20 +00002388 DIP("addi r%u,r%u,%d\n", rD_addr, rA_addr, (Int)simm16);
cerion2831b002005-11-30 19:55:22 +00002389 assign( rD, binop( mkSzOp(ty, Iop_Add8), mkexpr(rA),
2390 mkSzExtendS16(ty, uimm16) ) );
cerionb85e8bb2005-02-16 08:54:33 +00002391 }
2392 break;
cerion91ad5362005-01-27 23:02:41 +00002393
cerione9d361a2005-03-04 17:35:29 +00002394 case 0x0F: // addis (Add Immediate Shifted, PPC32 p353)
cerionb85e8bb2005-02-16 08:54:33 +00002395 // lis rD,val == addis rD,0,val
cerion76de5cf2005-11-18 18:25:12 +00002396 if ( rA_addr == 0 ) {
ceriond953ebb2005-11-29 13:27:20 +00002397 DIP("lis r%u,%d\n", rD_addr, (Int)simm16);
cerion2831b002005-11-30 19:55:22 +00002398 assign( rD, mkSzExtendS32(ty, uimm16 << 16) );
cerionb85e8bb2005-02-16 08:54:33 +00002399 } else {
ceriond953ebb2005-11-29 13:27:20 +00002400 DIP("addis r%u,r%u,0x%x\n", rD_addr, rA_addr, (Int)simm16);
cerion2831b002005-11-30 19:55:22 +00002401 assign( rD, binop( mkSzOp(ty, Iop_Add8), mkexpr(rA),
2402 mkSzExtendS32(ty, uimm16 << 16) ) );
cerionb85e8bb2005-02-16 08:54:33 +00002403 }
2404 break;
cerion91ad5362005-01-27 23:02:41 +00002405
cerione9d361a2005-03-04 17:35:29 +00002406 case 0x07: // mulli (Multiply Low Immediate, PPC32 p490)
ceriond953ebb2005-11-29 13:27:20 +00002407 DIP("mulli r%u,r%u,%d\n", rD_addr, rA_addr, (Int)simm16);
2408 if (mode64)
2409 assign( rD, unop(Iop_128to64,
2410 binop(Iop_MullS64, mkexpr(rA),
cerion2831b002005-11-30 19:55:22 +00002411 mkSzExtendS16(ty, uimm16))) );
ceriond953ebb2005-11-29 13:27:20 +00002412 else
2413 assign( rD, unop(Iop_64to32,
2414 binop(Iop_MullS32, mkexpr(rA),
cerion2831b002005-11-30 19:55:22 +00002415 mkSzExtendS16(ty, uimm16))) );
cerionb85e8bb2005-02-16 08:54:33 +00002416 break;
cerion38674602005-02-08 02:19:25 +00002417
cerione9d361a2005-03-04 17:35:29 +00002418 case 0x08: // subfic (Subtract from Immediate Carrying, PPC32 p540)
ceriond953ebb2005-11-29 13:27:20 +00002419 DIP("subfic r%u,r%u,%d\n", rD_addr, rA_addr, (Int)simm16);
cerion76de5cf2005-11-18 18:25:12 +00002420 // rD = simm16 - rA
cerion2831b002005-11-30 19:55:22 +00002421 assign( rD, binop( mkSzOp(ty, Iop_Sub8),
2422 mkSzExtendS16(ty, uimm16),
ceriond953ebb2005-11-29 13:27:20 +00002423 mkexpr(rA)) );
cerion5b2325f2005-12-23 00:55:09 +00002424 set_XER_CA( ty, PPCG_FLAG_OP_SUBFI,
cerion2831b002005-11-30 19:55:22 +00002425 mkexpr(rD), mkexpr(rA), mkSzExtendS16(ty, uimm16),
2426 mkSzImm(ty, 0)/*old xer.ca, which is ignored*/ );
cerionb85e8bb2005-02-16 08:54:33 +00002427 break;
cerion38674602005-02-08 02:19:25 +00002428
cerionb85e8bb2005-02-16 08:54:33 +00002429 /* XO-Form */
2430 case 0x1F:
cerionb85e8bb2005-02-16 08:54:33 +00002431 do_rc = True; // All below record to CR
2432
2433 switch (opc2) {
cerione9d361a2005-03-04 17:35:29 +00002434 case 0x10A: // add (Add, PPC32 p347)
ceriond953ebb2005-11-29 13:27:20 +00002435 DIP("add%s%s r%u,r%u,r%u\n",
cerion5b2325f2005-12-23 00:55:09 +00002436 flag_OE ? "o" : "", flag_rC ? ".":"",
cerion76de5cf2005-11-18 18:25:12 +00002437 rD_addr, rA_addr, rB_addr);
cerion2831b002005-11-30 19:55:22 +00002438 assign( rD, binop( mkSzOp(ty, Iop_Add8),
ceriond953ebb2005-11-29 13:27:20 +00002439 mkexpr(rA), mkexpr(rB) ) );
cerion70e24122005-03-16 00:27:37 +00002440 if (flag_OE) {
cerion5b2325f2005-12-23 00:55:09 +00002441 set_XER_OV( ty, PPCG_FLAG_OP_ADD,
cerion76de5cf2005-11-18 18:25:12 +00002442 mkexpr(rD), mkexpr(rA), mkexpr(rB) );
cerion70e24122005-03-16 00:27:37 +00002443 }
cerionb85e8bb2005-02-16 08:54:33 +00002444 break;
cerion91ad5362005-01-27 23:02:41 +00002445
cerione9d361a2005-03-04 17:35:29 +00002446 case 0x00A: // addc (Add Carrying, PPC32 p348)
ceriond953ebb2005-11-29 13:27:20 +00002447 DIP("addc%s%s r%u,r%u,r%u\n",
cerion5b2325f2005-12-23 00:55:09 +00002448 flag_OE ? "o" : "", flag_rC ? ".":"",
cerion76de5cf2005-11-18 18:25:12 +00002449 rD_addr, rA_addr, rB_addr);
cerion2831b002005-11-30 19:55:22 +00002450 assign( rD, binop( mkSzOp(ty, Iop_Add8),
ceriond953ebb2005-11-29 13:27:20 +00002451 mkexpr(rA), mkexpr(rB)) );
cerion5b2325f2005-12-23 00:55:09 +00002452 set_XER_CA( ty, PPCG_FLAG_OP_ADD,
cerion76de5cf2005-11-18 18:25:12 +00002453 mkexpr(rD), mkexpr(rA), mkexpr(rB),
cerion2831b002005-11-30 19:55:22 +00002454 mkSzImm(ty, 0)/*old xer.ca, which is ignored*/ );
cerion70e24122005-03-16 00:27:37 +00002455 if (flag_OE) {
cerion5b2325f2005-12-23 00:55:09 +00002456 set_XER_OV( ty, PPCG_FLAG_OP_ADD,
cerion76de5cf2005-11-18 18:25:12 +00002457 mkexpr(rD), mkexpr(rA), mkexpr(rB) );
cerion70e24122005-03-16 00:27:37 +00002458 }
cerionb85e8bb2005-02-16 08:54:33 +00002459 break;
2460
sewardjb51f0f42005-07-18 11:38:02 +00002461 case 0x08A: { // adde (Add Extended, PPC32 p349)
cerion2831b002005-11-30 19:55:22 +00002462 IRTemp old_xer_ca = newTemp(ty);
ceriond953ebb2005-11-29 13:27:20 +00002463 DIP("adde%s%s r%u,r%u,r%u\n",
cerion5b2325f2005-12-23 00:55:09 +00002464 flag_OE ? "o" : "", flag_rC ? ".":"",
cerion76de5cf2005-11-18 18:25:12 +00002465 rD_addr, rA_addr, rB_addr);
cerionb85e8bb2005-02-16 08:54:33 +00002466 // rD = rA + rB + XER[CA]
cerion2831b002005-11-30 19:55:22 +00002467 assign( old_xer_ca, mkSzWiden32(ty, getXER_CA32(), False) );
2468 assign( rD, binop( mkSzOp(ty, Iop_Add8), mkexpr(rA),
2469 binop( mkSzOp(ty, Iop_Add8),
2470 mkexpr(rB), mkexpr(old_xer_ca))) );
cerion5b2325f2005-12-23 00:55:09 +00002471 set_XER_CA( ty, PPCG_FLAG_OP_ADDE,
cerion76de5cf2005-11-18 18:25:12 +00002472 mkexpr(rD), mkexpr(rA), mkexpr(rB),
cerion2831b002005-11-30 19:55:22 +00002473 mkexpr(old_xer_ca) );
cerion70e24122005-03-16 00:27:37 +00002474 if (flag_OE) {
cerion5b2325f2005-12-23 00:55:09 +00002475 set_XER_OV( ty, PPCG_FLAG_OP_ADDE,
cerion76de5cf2005-11-18 18:25:12 +00002476 mkexpr(rD), mkexpr(rA), mkexpr(rB) );
cerion70e24122005-03-16 00:27:37 +00002477 }
cerionb85e8bb2005-02-16 08:54:33 +00002478 break;
sewardjb51f0f42005-07-18 11:38:02 +00002479 }
2480
cerion5b2325f2005-12-23 00:55:09 +00002481 case 0x0EA: { // addme (Add to Minus One Extended, PPC32 p354)
cerion2831b002005-11-30 19:55:22 +00002482 IRTemp old_xer_ca = newTemp(ty);
2483 IRExpr *min_one;
cerion76de5cf2005-11-18 18:25:12 +00002484 if (rB_addr != 0) {
cerion5b2325f2005-12-23 00:55:09 +00002485 vex_printf("dis_int_arith(ppc)(addme,rB_addr)\n");
cerionb85e8bb2005-02-16 08:54:33 +00002486 return False;
2487 }
ceriond953ebb2005-11-29 13:27:20 +00002488 DIP("addme%s%s r%u,r%u,r%u\n",
cerion5b2325f2005-12-23 00:55:09 +00002489 flag_OE ? "o" : "", flag_rC ? ".":"",
cerion76de5cf2005-11-18 18:25:12 +00002490 rD_addr, rA_addr, rB_addr);
cerion70e24122005-03-16 00:27:37 +00002491 // rD = rA + (-1) + XER[CA]
2492 // => Just another form of adde
cerion2831b002005-11-30 19:55:22 +00002493 assign( old_xer_ca, mkSzWiden32(ty, getXER_CA32(), False) );
2494 min_one = mkSzImm(ty, (Long)-1);
2495 assign( rD, binop( mkSzOp(ty, Iop_Add8), mkexpr(rA),
2496 binop( mkSzOp(ty, Iop_Add8),
2497 min_one, mkexpr(old_xer_ca)) ));
cerion5b2325f2005-12-23 00:55:09 +00002498 set_XER_CA( ty, PPCG_FLAG_OP_ADDE,
ceriond953ebb2005-11-29 13:27:20 +00002499 mkexpr(rD), mkexpr(rA), min_one,
cerion2831b002005-11-30 19:55:22 +00002500 mkexpr(old_xer_ca) );
cerion70e24122005-03-16 00:27:37 +00002501 if (flag_OE) {
cerion5b2325f2005-12-23 00:55:09 +00002502 set_XER_OV( ty, PPCG_FLAG_OP_ADDE,
ceriond953ebb2005-11-29 13:27:20 +00002503 mkexpr(rD), mkexpr(rA), min_one );
cerion70e24122005-03-16 00:27:37 +00002504 }
cerionb85e8bb2005-02-16 08:54:33 +00002505 break;
sewardjb51f0f42005-07-18 11:38:02 +00002506 }
2507
2508 case 0x0CA: { // addze (Add to Zero Extended, PPC32 p355)
cerion2831b002005-11-30 19:55:22 +00002509 IRTemp old_xer_ca = newTemp(ty);
cerion76de5cf2005-11-18 18:25:12 +00002510 if (rB_addr != 0) {
cerion5b2325f2005-12-23 00:55:09 +00002511 vex_printf("dis_int_arith(ppc)(addze,rB_addr)\n");
cerionb85e8bb2005-02-16 08:54:33 +00002512 return False;
2513 }
ceriond953ebb2005-11-29 13:27:20 +00002514 DIP("addze%s%s r%u,r%u,r%u\n",
cerion5b2325f2005-12-23 00:55:09 +00002515 flag_OE ? "o" : "", flag_rC ? ".":"",
cerion76de5cf2005-11-18 18:25:12 +00002516 rD_addr, rA_addr, rB_addr);
cerion70e24122005-03-16 00:27:37 +00002517 // rD = rA + (0) + XER[CA]
2518 // => Just another form of adde
cerion2831b002005-11-30 19:55:22 +00002519 assign( old_xer_ca, mkSzWiden32(ty, getXER_CA32(), False) );
2520 assign( rD, binop( mkSzOp(ty, Iop_Add8),
2521 mkexpr(rA), mkexpr(old_xer_ca)) );
cerion5b2325f2005-12-23 00:55:09 +00002522 set_XER_CA( ty, PPCG_FLAG_OP_ADDE,
cerion2831b002005-11-30 19:55:22 +00002523 mkexpr(rD), mkexpr(rA), mkSzImm(ty, 0),
2524 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,
cerion2831b002005-11-30 19:55:22 +00002527 mkexpr(rD), mkexpr(rA), mkSzImm(ty, 0) );
cerion70e24122005-03-16 00:27:37 +00002528 }
cerionb85e8bb2005-02-16 08:54:33 +00002529 break;
sewardjb51f0f42005-07-18 11:38:02 +00002530 }
cerion91ad5362005-01-27 23:02:41 +00002531
cerione9d361a2005-03-04 17:35:29 +00002532 case 0x1EB: // divw (Divide Word, PPC32 p388)
ceriond953ebb2005-11-29 13:27:20 +00002533 DIP("divw%s%s r%u,r%u,r%u\n",
cerion5b2325f2005-12-23 00:55:09 +00002534 flag_OE ? "o" : "", flag_rC ? ".":"",
cerion76de5cf2005-11-18 18:25:12 +00002535 rD_addr, rA_addr, rB_addr);
ceriond953ebb2005-11-29 13:27:20 +00002536 if (mode64) {
cerion2831b002005-11-30 19:55:22 +00002537 /* Note:
2538 XER settings are mode independent, and reflect the
2539 overflow of the low-order 32bit result
2540 CR0[LT|GT|EQ] are undefined if flag_rC && mode64
2541 */
cerionbb01b7c2005-12-16 13:40:18 +00002542 /* rD[hi32] are undefined: setting them to sign of lo32
2543 - makes set_CR0 happy */
2544 IRExpr* dividend = mk64lo32Sto64( mkexpr(rA) );
2545 IRExpr* divisor = mk64lo32Sto64( mkexpr(rB) );
cerion5b2325f2005-12-23 00:55:09 +00002546 assign( rD, mk64lo32Uto64( binop(Iop_DivS64, dividend,
2547 divisor) ) );
ceriond953ebb2005-11-29 13:27:20 +00002548 if (flag_OE) {
cerion5b2325f2005-12-23 00:55:09 +00002549 set_XER_OV( ty, PPCG_FLAG_OP_DIVW,
cerionf0de28c2005-12-13 20:21:11 +00002550 mkexpr(rD), dividend, divisor );
ceriond953ebb2005-11-29 13:27:20 +00002551 }
2552 } else {
2553 assign( rD, binop(Iop_DivS32, mkexpr(rA), mkexpr(rB)) );
2554 if (flag_OE) {
cerion5b2325f2005-12-23 00:55:09 +00002555 set_XER_OV( ty, PPCG_FLAG_OP_DIVW,
ceriond953ebb2005-11-29 13:27:20 +00002556 mkexpr(rD), mkexpr(rA), mkexpr(rB) );
2557 }
cerion70e24122005-03-16 00:27:37 +00002558 }
cerionb85e8bb2005-02-16 08:54:33 +00002559 /* Note:
2560 if (0x8000_0000 / -1) or (x / 0)
cerion76de5cf2005-11-18 18:25:12 +00002561 => rD=undef, if(flag_rC) CR7=undef, if(flag_OE) XER_OV=1
cerionb85e8bb2005-02-16 08:54:33 +00002562 => But _no_ exception raised. */
2563 break;
cerion91ad5362005-01-27 23:02:41 +00002564
cerione9d361a2005-03-04 17:35:29 +00002565 case 0x1CB: // divwu (Divide Word Unsigned, PPC32 p389)
ceriond953ebb2005-11-29 13:27:20 +00002566 DIP("divwu%s%s r%u,r%u,r%u\n",
cerion5b2325f2005-12-23 00:55:09 +00002567 flag_OE ? "o" : "", flag_rC ? ".":"",
cerion76de5cf2005-11-18 18:25:12 +00002568 rD_addr, rA_addr, rB_addr);
ceriond953ebb2005-11-29 13:27:20 +00002569 if (mode64) {
cerion2831b002005-11-30 19:55:22 +00002570 /* Note:
2571 XER settings are mode independent, and reflect the
2572 overflow of the low-order 32bit result
2573 CR0[LT|GT|EQ] are undefined if flag_rC && mode64
2574 */
cerionbb01b7c2005-12-16 13:40:18 +00002575 IRExpr* dividend = mk64lo32Uto64( mkexpr(rA) );
2576 IRExpr* divisor = mk64lo32Uto64( mkexpr(rB) );
cerion5b2325f2005-12-23 00:55:09 +00002577 assign( rD, mk64lo32Uto64( binop(Iop_DivU64, dividend,
2578 divisor) ) );
ceriond953ebb2005-11-29 13:27:20 +00002579 if (flag_OE) {
cerion5b2325f2005-12-23 00:55:09 +00002580 set_XER_OV( ty, PPCG_FLAG_OP_DIVWU,
cerionf0de28c2005-12-13 20:21:11 +00002581 mkexpr(rD), dividend, divisor );
ceriond953ebb2005-11-29 13:27:20 +00002582 }
cerion2831b002005-11-30 19:55:22 +00002583 } else {
ceriond953ebb2005-11-29 13:27:20 +00002584 assign( rD, binop(Iop_DivU32, mkexpr(rA), mkexpr(rB)) );
2585 if (flag_OE) {
cerion5b2325f2005-12-23 00:55:09 +00002586 set_XER_OV( ty, PPCG_FLAG_OP_DIVWU,
ceriond953ebb2005-11-29 13:27:20 +00002587 mkexpr(rD), mkexpr(rA), mkexpr(rB) );
2588 }
cerion70e24122005-03-16 00:27:37 +00002589 }
cerionb85e8bb2005-02-16 08:54:33 +00002590 /* Note: ditto comment divw, for (x / 0) */
2591 break;
cerion91ad5362005-01-27 23:02:41 +00002592
cerione9d361a2005-03-04 17:35:29 +00002593 case 0x04B: // mulhw (Multiply High Word, PPC32 p488)
cerionb85e8bb2005-02-16 08:54:33 +00002594 if (flag_OE != 0) {
cerion5b2325f2005-12-23 00:55:09 +00002595 vex_printf("dis_int_arith(ppc)(mulhw,flag_OE)\n");
cerionb85e8bb2005-02-16 08:54:33 +00002596 return False;
2597 }
cerion5b2325f2005-12-23 00:55:09 +00002598 DIP("mulhw%s r%u,r%u,r%u\n", flag_rC ? ".":"",
cerion76de5cf2005-11-18 18:25:12 +00002599 rD_addr, rA_addr, rB_addr);
cerion2831b002005-11-30 19:55:22 +00002600 if (mode64) {
cerionbb01b7c2005-12-16 13:40:18 +00002601 /* rD[hi32] are undefined: setting them to sign of lo32
2602 - makes set_CR0 happy */
2603 assign( rD, binop(Iop_Sar64,
2604 binop(Iop_Mul64,
2605 mk64lo32Sto64( mkexpr(rA) ),
2606 mk64lo32Sto64( mkexpr(rB) )),
2607 mkU8(32)) );
cerion2831b002005-11-30 19:55:22 +00002608 } else {
ceriond953ebb2005-11-29 13:27:20 +00002609 assign( rD, unop(Iop_64HIto32,
2610 binop(Iop_MullS32,
2611 mkexpr(rA), mkexpr(rB))) );
cerion2831b002005-11-30 19:55:22 +00002612 }
cerionb85e8bb2005-02-16 08:54:33 +00002613 break;
cerionc19d5e12005-02-01 15:56:25 +00002614
cerion5b2325f2005-12-23 00:55:09 +00002615 case 0x00B: // mulhwu (Multiply High Word Unsigned, PPC32 p489)
cerionb85e8bb2005-02-16 08:54:33 +00002616 if (flag_OE != 0) {
cerion5b2325f2005-12-23 00:55:09 +00002617 vex_printf("dis_int_arith(ppc)(mulhwu,flag_OE)\n");
cerionb85e8bb2005-02-16 08:54:33 +00002618 return False;
2619 }
cerion5b2325f2005-12-23 00:55:09 +00002620 DIP("mulhwu%s r%u,r%u,r%u\n", flag_rC ? ".":"",
cerion76de5cf2005-11-18 18:25:12 +00002621 rD_addr, rA_addr, rB_addr);
cerion2831b002005-11-30 19:55:22 +00002622 if (mode64) {
cerionbb01b7c2005-12-16 13:40:18 +00002623 /* rD[hi32] are undefined: setting them to sign of lo32
2624 - makes set_CR0 happy */
2625 assign( rD, binop(Iop_Sar64,
2626 binop(Iop_Mul64,
2627 mk64lo32Uto64( mkexpr(rA) ),
2628 mk64lo32Uto64( mkexpr(rB) ) ),
2629 mkU8(32)) );
cerion2831b002005-11-30 19:55:22 +00002630 } else {
ceriond953ebb2005-11-29 13:27:20 +00002631 assign( rD, unop(Iop_64HIto32,
2632 binop(Iop_MullU32,
2633 mkexpr(rA), mkexpr(rB))) );
cerion2831b002005-11-30 19:55:22 +00002634 }
cerionb85e8bb2005-02-16 08:54:33 +00002635 break;
2636
cerione9d361a2005-03-04 17:35:29 +00002637 case 0x0EB: // mullw (Multiply Low Word, PPC32 p491)
ceriond953ebb2005-11-29 13:27:20 +00002638 DIP("mullw%s%s r%u,r%u,r%u\n",
cerion5b2325f2005-12-23 00:55:09 +00002639 flag_OE ? "o" : "", flag_rC ? ".":"",
cerion76de5cf2005-11-18 18:25:12 +00002640 rD_addr, rA_addr, rB_addr);
cerion2831b002005-11-30 19:55:22 +00002641 if (mode64) {
cerionbb01b7c2005-12-16 13:40:18 +00002642 /* rD[hi32] are undefined: setting them to sign of lo32
2643 - set_XER_OV() and set_CR0() depend on this */
2644 IRExpr *a = unop(Iop_64to32, mkexpr(rA) );
2645 IRExpr *b = unop(Iop_64to32, mkexpr(rB) );
2646 assign( rD, binop(Iop_MullS32, a, b) );
2647 if (flag_OE) {
cerion5b2325f2005-12-23 00:55:09 +00002648 set_XER_OV( ty, PPCG_FLAG_OP_MULLW,
cerionbb01b7c2005-12-16 13:40:18 +00002649 mkexpr(rD),
2650 unop(Iop_32Uto64, a), unop(Iop_32Uto64, b) );
2651 }
cerion2831b002005-11-30 19:55:22 +00002652 } else {
ceriond953ebb2005-11-29 13:27:20 +00002653 assign( rD, unop(Iop_64to32,
2654 binop(Iop_MullU32,
2655 mkexpr(rA), mkexpr(rB))) );
cerionbb01b7c2005-12-16 13:40:18 +00002656 if (flag_OE) {
cerion5b2325f2005-12-23 00:55:09 +00002657 set_XER_OV( ty, PPCG_FLAG_OP_MULLW,
cerionbb01b7c2005-12-16 13:40:18 +00002658 mkexpr(rD), mkexpr(rA), mkexpr(rB) );
2659 }
cerion70e24122005-03-16 00:27:37 +00002660 }
cerionb85e8bb2005-02-16 08:54:33 +00002661 break;
cerionc19d5e12005-02-01 15:56:25 +00002662
cerione9d361a2005-03-04 17:35:29 +00002663 case 0x068: // neg (Negate, PPC32 p493)
cerion76de5cf2005-11-18 18:25:12 +00002664 if (rB_addr != 0) {
cerion5b2325f2005-12-23 00:55:09 +00002665 vex_printf("dis_int_arith(ppc)(neg,rB_addr)\n");
cerionb85e8bb2005-02-16 08:54:33 +00002666 return False;
2667 }
ceriond953ebb2005-11-29 13:27:20 +00002668 DIP("neg%s%s r%u,r%u\n",
cerion5b2325f2005-12-23 00:55:09 +00002669 flag_OE ? "o" : "", flag_rC ? ".":"",
cerion76de5cf2005-11-18 18:25:12 +00002670 rD_addr, rA_addr);
ceriond953ebb2005-11-29 13:27:20 +00002671 // rD = (~rA) + 1
cerion2831b002005-11-30 19:55:22 +00002672 assign( rD, binop( mkSzOp(ty, Iop_Add8),
2673 unop( mkSzOp(ty, Iop_Not8), mkexpr(rA) ),
2674 mkSzImm(ty, 1)) );
cerion70e24122005-03-16 00:27:37 +00002675 if (flag_OE) {
cerion5b2325f2005-12-23 00:55:09 +00002676 set_XER_OV( ty, PPCG_FLAG_OP_NEG,
cerion76de5cf2005-11-18 18:25:12 +00002677 mkexpr(rD), mkexpr(rA), mkexpr(rB) );
cerion70e24122005-03-16 00:27:37 +00002678 }
cerionb85e8bb2005-02-16 08:54:33 +00002679 break;
cerion91ad5362005-01-27 23:02:41 +00002680
cerione9d361a2005-03-04 17:35:29 +00002681 case 0x028: // subf (Subtract From, PPC32 p537)
ceriond953ebb2005-11-29 13:27:20 +00002682 DIP("subf%s%s r%u,r%u,r%u\n",
cerion5b2325f2005-12-23 00:55:09 +00002683 flag_OE ? "o" : "", flag_rC ? ".":"",
cerion76de5cf2005-11-18 18:25:12 +00002684 rD_addr, rA_addr, rB_addr);
cerion01908472005-02-25 16:43:08 +00002685 // rD = rB - rA
cerion2831b002005-11-30 19:55:22 +00002686 assign( rD, binop( mkSzOp(ty, Iop_Sub8),
ceriond953ebb2005-11-29 13:27:20 +00002687 mkexpr(rB), mkexpr(rA)) );
cerion70e24122005-03-16 00:27:37 +00002688 if (flag_OE) {
cerion5b2325f2005-12-23 00:55:09 +00002689 set_XER_OV( ty, PPCG_FLAG_OP_SUBF,
cerion76de5cf2005-11-18 18:25:12 +00002690 mkexpr(rD), mkexpr(rA), mkexpr(rB) );
cerion70e24122005-03-16 00:27:37 +00002691 }
cerionb85e8bb2005-02-16 08:54:33 +00002692 break;
cerion38674602005-02-08 02:19:25 +00002693
cerione9d361a2005-03-04 17:35:29 +00002694 case 0x008: // subfc (Subtract from Carrying, PPC32 p538)
ceriond953ebb2005-11-29 13:27:20 +00002695 DIP("subfc%s%s r%u,r%u,r%u\n",
cerion5b2325f2005-12-23 00:55:09 +00002696 flag_OE ? "o" : "", flag_rC ? ".":"",
cerion76de5cf2005-11-18 18:25:12 +00002697 rD_addr, rA_addr, rB_addr);
cerion01908472005-02-25 16:43:08 +00002698 // rD = rB - rA
cerion2831b002005-11-30 19:55:22 +00002699 assign( rD, binop( mkSzOp(ty, Iop_Sub8),
ceriond953ebb2005-11-29 13:27:20 +00002700 mkexpr(rB), mkexpr(rA)) );
cerion5b2325f2005-12-23 00:55:09 +00002701 set_XER_CA( ty, PPCG_FLAG_OP_SUBFC,
cerion76de5cf2005-11-18 18:25:12 +00002702 mkexpr(rD), mkexpr(rA), mkexpr(rB),
cerion2831b002005-11-30 19:55:22 +00002703 mkSzImm(ty, 0)/*old xer.ca, which is ignored*/ );
cerion70e24122005-03-16 00:27:37 +00002704 if (flag_OE) {
cerion5b2325f2005-12-23 00:55:09 +00002705 set_XER_OV( ty, PPCG_FLAG_OP_SUBFC,
cerion76de5cf2005-11-18 18:25:12 +00002706 mkexpr(rD), mkexpr(rA), mkexpr(rB) );
cerion70e24122005-03-16 00:27:37 +00002707 }
cerionb85e8bb2005-02-16 08:54:33 +00002708 break;
2709
sewardjb51f0f42005-07-18 11:38:02 +00002710 case 0x088: {// subfe (Subtract from Extended, PPC32 p539)
cerion2831b002005-11-30 19:55:22 +00002711 IRTemp old_xer_ca = newTemp(ty);
ceriond953ebb2005-11-29 13:27:20 +00002712 DIP("subfe%s%s r%u,r%u,r%u\n",
cerion5b2325f2005-12-23 00:55:09 +00002713 flag_OE ? "o" : "", flag_rC ? ".":"",
cerion76de5cf2005-11-18 18:25:12 +00002714 rD_addr, rA_addr, rB_addr);
cerionb85e8bb2005-02-16 08:54:33 +00002715 // rD = (log not)rA + rB + XER[CA]
cerion2831b002005-11-30 19:55:22 +00002716 assign( old_xer_ca, mkSzWiden32(ty, getXER_CA32(), False) );
2717 assign( rD, binop( mkSzOp(ty, Iop_Add8),
2718 unop( mkSzOp(ty, Iop_Not8), mkexpr(rA)),
2719 binop( mkSzOp(ty, Iop_Add8),
2720 mkexpr(rB), mkexpr(old_xer_ca))) );
cerion5b2325f2005-12-23 00:55:09 +00002721 set_XER_CA( ty, PPCG_FLAG_OP_SUBFE,
cerion76de5cf2005-11-18 18:25:12 +00002722 mkexpr(rD), mkexpr(rA), mkexpr(rB),
cerion2831b002005-11-30 19:55:22 +00002723 mkexpr(old_xer_ca) );
cerion70e24122005-03-16 00:27:37 +00002724 if (flag_OE) {
cerion5b2325f2005-12-23 00:55:09 +00002725 set_XER_OV( ty, PPCG_FLAG_OP_SUBFE,
cerion76de5cf2005-11-18 18:25:12 +00002726 mkexpr(rD), mkexpr(rA), mkexpr(rB) );
cerion70e24122005-03-16 00:27:37 +00002727 }
cerionb85e8bb2005-02-16 08:54:33 +00002728 break;
sewardjb51f0f42005-07-18 11:38:02 +00002729 }
2730
cerion5b2325f2005-12-23 00:55:09 +00002731 case 0x0E8: { // subfme (Subtract from -1 Extended, PPC32 p541)
cerion2831b002005-11-30 19:55:22 +00002732 IRTemp old_xer_ca = newTemp(ty);
2733 IRExpr *min_one;
cerion76de5cf2005-11-18 18:25:12 +00002734 if (rB_addr != 0) {
cerion5b2325f2005-12-23 00:55:09 +00002735 vex_printf("dis_int_arith(ppc)(subfme,rB_addr)\n");
sewardj20ef5472005-07-21 14:48:31 +00002736 return False;
2737 }
ceriond953ebb2005-11-29 13:27:20 +00002738 DIP("subfme%s%s r%u,r%u\n",
cerion5b2325f2005-12-23 00:55:09 +00002739 flag_OE ? "o" : "", flag_rC ? ".":"",
cerion76de5cf2005-11-18 18:25:12 +00002740 rD_addr, rA_addr);
sewardj20ef5472005-07-21 14:48:31 +00002741 // rD = (log not)rA + (-1) + XER[CA]
2742 // => Just another form of subfe
cerion2831b002005-11-30 19:55:22 +00002743 assign( old_xer_ca, mkSzWiden32(ty, getXER_CA32(), False) );
2744 min_one = mkSzImm(ty, (Long)-1);
2745 assign( rD, binop( mkSzOp(ty, Iop_Add8),
2746 unop( mkSzOp(ty, Iop_Not8), mkexpr(rA)),
2747 binop( mkSzOp(ty, Iop_Add8),
2748 min_one, mkexpr(old_xer_ca))) );
cerion5b2325f2005-12-23 00:55:09 +00002749 set_XER_CA( ty, PPCG_FLAG_OP_SUBFE,
ceriond953ebb2005-11-29 13:27:20 +00002750 mkexpr(rD), mkexpr(rA), min_one,
cerion2831b002005-11-30 19:55:22 +00002751 mkexpr(old_xer_ca) );
sewardj20ef5472005-07-21 14:48:31 +00002752 if (flag_OE) {
cerion5b2325f2005-12-23 00:55:09 +00002753 set_XER_OV( ty, PPCG_FLAG_OP_SUBFE,
ceriond953ebb2005-11-29 13:27:20 +00002754 mkexpr(rD), mkexpr(rA), min_one );
sewardj20ef5472005-07-21 14:48:31 +00002755 }
2756 break;
2757 }
2758
cerion5b2325f2005-12-23 00:55:09 +00002759 case 0x0C8: { // subfze (Subtract from Zero Extended, PPC32 p542)
cerion2831b002005-11-30 19:55:22 +00002760 IRTemp old_xer_ca = newTemp(ty);
cerion76de5cf2005-11-18 18:25:12 +00002761 if (rB_addr != 0) {
cerion5b2325f2005-12-23 00:55:09 +00002762 vex_printf("dis_int_arith(ppc)(subfze,rB_addr)\n");
cerionb85e8bb2005-02-16 08:54:33 +00002763 return False;
2764 }
ceriond953ebb2005-11-29 13:27:20 +00002765 DIP("subfze%s%s r%u,r%u\n",
cerion5b2325f2005-12-23 00:55:09 +00002766 flag_OE ? "o" : "", flag_rC ? ".":"",
cerion76de5cf2005-11-18 18:25:12 +00002767 rD_addr, rA_addr);
cerion70e24122005-03-16 00:27:37 +00002768 // rD = (log not)rA + (0) + XER[CA]
2769 // => Just another form of subfe
cerion2831b002005-11-30 19:55:22 +00002770 assign( old_xer_ca, mkSzWiden32(ty, getXER_CA32(), False) );
2771 assign( rD, binop( mkSzOp(ty, Iop_Add8),
2772 unop( mkSzOp(ty, Iop_Not8),
2773 mkexpr(rA)), mkexpr(old_xer_ca)) );
cerion5b2325f2005-12-23 00:55:09 +00002774 set_XER_CA( ty, PPCG_FLAG_OP_SUBFE,
cerion2831b002005-11-30 19:55:22 +00002775 mkexpr(rD), mkexpr(rA), mkSzImm(ty, 0),
2776 mkexpr(old_xer_ca) );
cerion70e24122005-03-16 00:27:37 +00002777 if (flag_OE) {
cerion5b2325f2005-12-23 00:55:09 +00002778 set_XER_OV( ty, PPCG_FLAG_OP_SUBFE,
cerion2831b002005-11-30 19:55:22 +00002779 mkexpr(rD), mkexpr(rA), mkSzImm(ty, 0) );
cerion70e24122005-03-16 00:27:37 +00002780 }
cerionb85e8bb2005-02-16 08:54:33 +00002781 break;
sewardjb51f0f42005-07-18 11:38:02 +00002782 }
cerionae694622005-01-28 17:52:47 +00002783
cerionf0de28c2005-12-13 20:21:11 +00002784
2785 /* 64bit Arithmetic */
cerion5b2325f2005-12-23 00:55:09 +00002786 case 0x49: // mulhd (Multiply High DWord, PPC64 p539)
cerionf0de28c2005-12-13 20:21:11 +00002787 if (flag_OE != 0) {
cerion5b2325f2005-12-23 00:55:09 +00002788 vex_printf("dis_int_arith(ppc)(mulhd,flagOE)\n");
cerionf0de28c2005-12-13 20:21:11 +00002789 return False;
2790 }
cerion5b2325f2005-12-23 00:55:09 +00002791 DIP("mulhd%s r%u,r%u,r%u\n", flag_rC ? ".":"",
cerionf0de28c2005-12-13 20:21:11 +00002792 rD_addr, rA_addr, rB_addr);
cerionf0de28c2005-12-13 20:21:11 +00002793 assign( rD, unop(Iop_128HIto64,
cerion07b07a92005-12-22 14:32:35 +00002794 binop(Iop_MullS64,
cerionf0de28c2005-12-13 20:21:11 +00002795 mkexpr(rA), mkexpr(rB))) );
cerion07b07a92005-12-22 14:32:35 +00002796
2797 break;
cerionf0de28c2005-12-13 20:21:11 +00002798
cerion5b2325f2005-12-23 00:55:09 +00002799 case 0x9: // mulhdu (Multiply High DWord Unsigned, PPC64 p540)
cerionf0de28c2005-12-13 20:21:11 +00002800 if (flag_OE != 0) {
cerion5b2325f2005-12-23 00:55:09 +00002801 vex_printf("dis_int_arith(ppc)(mulhdu,flagOE)\n");
cerionf0de28c2005-12-13 20:21:11 +00002802 return False;
2803 }
cerion5b2325f2005-12-23 00:55:09 +00002804 DIP("mulhdu%s r%u,r%u,r%u\n", flag_rC ? ".":"",
cerionf0de28c2005-12-13 20:21:11 +00002805 rD_addr, rA_addr, rB_addr);
cerion07b07a92005-12-22 14:32:35 +00002806 assign( rD, unop(Iop_128HIto64,
2807 binop(Iop_MullU64,
2808 mkexpr(rA), mkexpr(rB))) );
2809 break;
cerionf0de28c2005-12-13 20:21:11 +00002810
cerion5b2325f2005-12-23 00:55:09 +00002811 case 0xE9: // mulld (Multiply Low DWord, PPC64 p543)
cerionf0de28c2005-12-13 20:21:11 +00002812 DIP("mulld%s%s r%u,r%u,r%u\n",
cerion5b2325f2005-12-23 00:55:09 +00002813 flag_OE ? "o" : "", flag_rC ? ".":"",
cerionf0de28c2005-12-13 20:21:11 +00002814 rD_addr, rA_addr, rB_addr);
2815 assign( rD, binop(Iop_Mul64, mkexpr(rA), mkexpr(rB)) );
2816 if (flag_OE) {
cerion5b2325f2005-12-23 00:55:09 +00002817 set_XER_OV( ty, PPCG_FLAG_OP_MULLW,
cerionf0de28c2005-12-13 20:21:11 +00002818 mkexpr(rD), mkexpr(rA), mkexpr(rB) );
2819 }
2820 break;
2821
cerion5b2325f2005-12-23 00:55:09 +00002822 case 0x1E9: // divd (Divide DWord, PPC64 p419)
cerionf0de28c2005-12-13 20:21:11 +00002823 DIP("divd%s%s r%u,r%u,r%u\n",
cerion5b2325f2005-12-23 00:55:09 +00002824 flag_OE ? "o" : "", flag_rC ? ".":"",
cerionf0de28c2005-12-13 20:21:11 +00002825 rD_addr, rA_addr, rB_addr);
cerion07b07a92005-12-22 14:32:35 +00002826 assign( rD, binop(Iop_DivS64, mkexpr(rA), mkexpr(rB)) );
2827 if (flag_OE) {
cerion5b2325f2005-12-23 00:55:09 +00002828 set_XER_OV( ty, PPCG_FLAG_OP_DIVW,
cerion07b07a92005-12-22 14:32:35 +00002829 mkexpr(rD), mkexpr(rA), mkexpr(rB) );
2830 }
2831 break;
cerionf0de28c2005-12-13 20:21:11 +00002832 /* Note:
cerion07b07a92005-12-22 14:32:35 +00002833 if (0x8000_0000_0000_0000 / -1) or (x / 0)
cerionf0de28c2005-12-13 20:21:11 +00002834 => rD=undef, if(flag_rC) CR7=undef, if(flag_OE) XER_OV=1
2835 => But _no_ exception raised. */
2836
cerion5b2325f2005-12-23 00:55:09 +00002837 case 0x1C9: // divdu (Divide DWord Unsigned, PPC64 p420)
cerionf0de28c2005-12-13 20:21:11 +00002838 DIP("divdu%s%s r%u,r%u,r%u\n",
cerion5b2325f2005-12-23 00:55:09 +00002839 flag_OE ? "o" : "", flag_rC ? ".":"",
cerionf0de28c2005-12-13 20:21:11 +00002840 rD_addr, rA_addr, rB_addr);
cerion07b07a92005-12-22 14:32:35 +00002841 assign( rD, binop(Iop_DivU64, mkexpr(rA), mkexpr(rB)) );
2842 if (flag_OE) {
cerion5b2325f2005-12-23 00:55:09 +00002843 set_XER_OV( ty, PPCG_FLAG_OP_DIVWU,
cerion07b07a92005-12-22 14:32:35 +00002844 mkexpr(rD), mkexpr(rA), mkexpr(rB) );
2845 }
2846 break;
cerionf0de28c2005-12-13 20:21:11 +00002847 /* Note: ditto comment divd, for (x / 0) */
2848
cerionb85e8bb2005-02-16 08:54:33 +00002849 default:
cerion5b2325f2005-12-23 00:55:09 +00002850 vex_printf("dis_int_arith(ppc)(opc2)\n");
cerionb85e8bb2005-02-16 08:54:33 +00002851 return False;
2852 }
2853 break;
cerionf0de28c2005-12-13 20:21:11 +00002854
cerionb85e8bb2005-02-16 08:54:33 +00002855 default:
cerion5b2325f2005-12-23 00:55:09 +00002856 vex_printf("dis_int_arith(ppc)(opc1)\n");
cerionb85e8bb2005-02-16 08:54:33 +00002857 return False;
2858 }
cerion91ad5362005-01-27 23:02:41 +00002859
cerion76de5cf2005-11-18 18:25:12 +00002860 putIReg( rD_addr, mkexpr(rD) );
2861
2862 if (do_rc && flag_rC) {
2863 set_CR0( mkexpr(rD) );
cerionb85e8bb2005-02-16 08:54:33 +00002864 }
2865 return True;
cerion91ad5362005-01-27 23:02:41 +00002866}
2867
2868
2869
cerion3d870a32005-03-18 12:23:33 +00002870/*
2871 Integer Compare Instructions
2872*/
cerion7aa4bbc2005-01-29 09:32:07 +00002873static Bool dis_int_cmp ( UInt theInstr )
2874{
cerion76de5cf2005-11-18 18:25:12 +00002875 /* D-Form, X-Form */
2876 UChar opc1 = ifieldOPC(theInstr);
2877 UChar crfD = toUChar( IFIELD( theInstr, 23, 3 ) );
2878 UChar b22 = toUChar( IFIELD( theInstr, 22, 1 ) );
2879 UChar flag_L = toUChar( IFIELD( theInstr, 21, 1 ) );
2880 UChar rA_addr = ifieldRegA(theInstr);
ceriond953ebb2005-11-29 13:27:20 +00002881 UInt uimm16 = ifieldUIMM16(theInstr);
cerion76de5cf2005-11-18 18:25:12 +00002882 UChar rB_addr = ifieldRegB(theInstr);
2883 UInt opc2 = ifieldOPClo10(theInstr);
2884 UChar b0 = ifieldBIT0(theInstr);
cerion7aa4bbc2005-01-29 09:32:07 +00002885
cerionbb01b7c2005-12-16 13:40:18 +00002886 IRType ty = mode64 ? Ity_I64 : Ity_I32;
2887 IRExpr *a = getIReg(rA_addr);
2888 IRExpr *b;
cerion76de5cf2005-11-18 18:25:12 +00002889
ceriond953ebb2005-11-29 13:27:20 +00002890 if (!mode64 && flag_L==1) { // L==1 invalid for 32 bit.
cerion5b2325f2005-12-23 00:55:09 +00002891 vex_printf("dis_int_cmp(ppc)(flag_L)\n");
cerionb85e8bb2005-02-16 08:54:33 +00002892 return False;
2893 }
2894
cerion76de5cf2005-11-18 18:25:12 +00002895 if (b22 != 0) {
cerion5b2325f2005-12-23 00:55:09 +00002896 vex_printf("dis_int_cmp(ppc)(b22)\n");
cerionb85e8bb2005-02-16 08:54:33 +00002897 return False;
2898 }
2899
2900 switch (opc1) {
ceriond953ebb2005-11-29 13:27:20 +00002901 case 0x0B: // cmpi (Compare Immediate, PPC32 p368)
2902 DIP("cmpi cr%u,%u,r%u,%d\n", crfD, flag_L, rA_addr,
2903 (Int)extend_s_16to32(uimm16));
cerion2831b002005-11-30 19:55:22 +00002904 b = mkSzExtendS16( ty, uimm16 );
cerionbb01b7c2005-12-16 13:40:18 +00002905 if (flag_L == 1) {
cerion2831b002005-11-30 19:55:22 +00002906 putCR321(crfD, unop(Iop_64to8, binop(Iop_CmpORD64S, a, b)));
ceriond953ebb2005-11-29 13:27:20 +00002907 } else {
cerionbb01b7c2005-12-16 13:40:18 +00002908 a = mkSzNarrow32( ty, a );
2909 b = mkSzNarrow32( ty, b );
ceriond953ebb2005-11-29 13:27:20 +00002910 putCR321(crfD, unop(Iop_32to8, binop(Iop_CmpORD32S, a, b)));
2911 }
2912 putCR0( crfD, getXER_SO() );
2913 break;
2914
2915 case 0x0A: // cmpli (Compare Logical Immediate, PPC32 p370)
2916 DIP("cmpli cr%u,%u,r%u,0x%x\n", crfD, flag_L, rA_addr, uimm16);
cerion2831b002005-11-30 19:55:22 +00002917 b = mkSzImm( ty, uimm16 );
cerionbb01b7c2005-12-16 13:40:18 +00002918 if (flag_L == 1) {
cerion2831b002005-11-30 19:55:22 +00002919 putCR321(crfD, unop(Iop_64to8, binop(Iop_CmpORD64U, a, b)));
ceriond953ebb2005-11-29 13:27:20 +00002920 } else {
cerionbb01b7c2005-12-16 13:40:18 +00002921 a = mkSzNarrow32( ty, a );
2922 b = mkSzNarrow32( ty, b );
ceriond953ebb2005-11-29 13:27:20 +00002923 putCR321(crfD, unop(Iop_32to8, binop(Iop_CmpORD32U, a, b)));
2924 }
2925 putCR0( crfD, getXER_SO() );
2926 break;
cerionb85e8bb2005-02-16 08:54:33 +00002927
2928 /* X Form */
2929 case 0x1F:
2930 if (b0 != 0) {
cerion5b2325f2005-12-23 00:55:09 +00002931 vex_printf("dis_int_cmp(ppc)(0x1F,b0)\n");
cerionb85e8bb2005-02-16 08:54:33 +00002932 return False;
2933 }
cerionbb01b7c2005-12-16 13:40:18 +00002934 b = getIReg(rB_addr);
cerion7aa4bbc2005-01-29 09:32:07 +00002935
cerionb85e8bb2005-02-16 08:54:33 +00002936 switch (opc2) {
ceriond953ebb2005-11-29 13:27:20 +00002937 case 0x000: // cmp (Compare, PPC32 p367)
2938 DIP("cmp cr%u,%u,r%u,r%u\n", crfD, flag_L, rA_addr, rB_addr);
sewardja9cb67b2006-08-19 18:31:53 +00002939 /* Comparing a reg with itself produces a result which
2940 doesn't depend on the contents of the reg. Therefore
2941 remove the false dependency, which has been known to cause
2942 memcheck to produce false errors. */
sewardj9195aa12006-08-19 22:18:53 +00002943 if (rA_addr == rB_addr)
2944 a = b = typeOfIRExpr(irbb->tyenv,a) == Ity_I64
2945 ? mkU64(0) : mkU32(0);
cerionbb01b7c2005-12-16 13:40:18 +00002946 if (flag_L == 1) {
2947 putCR321(crfD, unop(Iop_64to8, binop(Iop_CmpORD64S, a, b)));
ceriond953ebb2005-11-29 13:27:20 +00002948 } else {
cerionbb01b7c2005-12-16 13:40:18 +00002949 a = mkSzNarrow32( ty, a );
2950 b = mkSzNarrow32( ty, b );
2951 putCR321(crfD, unop(Iop_32to8,binop(Iop_CmpORD32S, a, b)));
ceriond953ebb2005-11-29 13:27:20 +00002952 }
2953 putCR0( crfD, getXER_SO() );
2954 break;
cerionb85e8bb2005-02-16 08:54:33 +00002955
ceriond953ebb2005-11-29 13:27:20 +00002956 case 0x020: // cmpl (Compare Logical, PPC32 p369)
2957 DIP("cmpl cr%u,%u,r%u,r%u\n", crfD, flag_L, rA_addr, rB_addr);
sewardja9cb67b2006-08-19 18:31:53 +00002958 /* Comparing a reg with itself produces a result which
2959 doesn't depend on the contents of the reg. Therefore
2960 remove the false dependency, which has been known to cause
2961 memcheck to produce false errors. */
sewardj9195aa12006-08-19 22:18:53 +00002962 if (rA_addr == rB_addr)
2963 a = b = typeOfIRExpr(irbb->tyenv,a) == Ity_I64
2964 ? mkU64(0) : mkU32(0);
cerionbb01b7c2005-12-16 13:40:18 +00002965 if (flag_L == 1) {
2966 putCR321(crfD, unop(Iop_64to8, binop(Iop_CmpORD64U, a, b)));
ceriond953ebb2005-11-29 13:27:20 +00002967 } else {
cerionbb01b7c2005-12-16 13:40:18 +00002968 a = mkSzNarrow32( ty, a );
2969 b = mkSzNarrow32( ty, b );
2970 putCR321(crfD, unop(Iop_32to8, binop(Iop_CmpORD32U, a, b)));
ceriond953ebb2005-11-29 13:27:20 +00002971 }
2972 putCR0( crfD, getXER_SO() );
2973 break;
cerion7aa4bbc2005-01-29 09:32:07 +00002974
ceriond953ebb2005-11-29 13:27:20 +00002975 default:
cerion5b2325f2005-12-23 00:55:09 +00002976 vex_printf("dis_int_cmp(ppc)(opc2)\n");
ceriond953ebb2005-11-29 13:27:20 +00002977 return False;
cerionb85e8bb2005-02-16 08:54:33 +00002978 }
2979 break;
ceriond953ebb2005-11-29 13:27:20 +00002980
cerionb85e8bb2005-02-16 08:54:33 +00002981 default:
cerion5b2325f2005-12-23 00:55:09 +00002982 vex_printf("dis_int_cmp(ppc)(opc1)\n");
cerionb85e8bb2005-02-16 08:54:33 +00002983 return False;
2984 }
2985
cerionb85e8bb2005-02-16 08:54:33 +00002986 return True;
cerion7aa4bbc2005-01-29 09:32:07 +00002987}
2988
2989
cerion3d870a32005-03-18 12:23:33 +00002990/*
2991 Integer Logical Instructions
2992*/
cerion7aa4bbc2005-01-29 09:32:07 +00002993static Bool dis_int_logic ( UInt theInstr )
2994{
cerion76de5cf2005-11-18 18:25:12 +00002995 /* D-Form, X-Form */
2996 UChar opc1 = ifieldOPC(theInstr);
2997 UChar rS_addr = ifieldRegDS(theInstr);
2998 UChar rA_addr = ifieldRegA(theInstr);
ceriond953ebb2005-11-29 13:27:20 +00002999 UInt uimm16 = ifieldUIMM16(theInstr);
cerion76de5cf2005-11-18 18:25:12 +00003000 UChar rB_addr = ifieldRegB(theInstr);
3001 UInt opc2 = ifieldOPClo10(theInstr);
3002 UChar flag_rC = ifieldBIT0(theInstr);
cerionb85e8bb2005-02-16 08:54:33 +00003003
ceriond953ebb2005-11-29 13:27:20 +00003004 IRType ty = mode64 ? Ity_I64 : Ity_I32;
3005 IRTemp rS = newTemp(ty);
3006 IRTemp rA = newTemp(ty);
3007 IRTemp rB = newTemp(ty);
cerione9d361a2005-03-04 17:35:29 +00003008 IRExpr* irx;
ceriond953ebb2005-11-29 13:27:20 +00003009 Bool do_rc = False;
3010
cerion76de5cf2005-11-18 18:25:12 +00003011 assign( rS, getIReg(rS_addr) );
3012 assign( rB, getIReg(rB_addr) );
cerionb85e8bb2005-02-16 08:54:33 +00003013
3014 switch (opc1) {
cerione9d361a2005-03-04 17:35:29 +00003015 case 0x1C: // andi. (AND Immediate, PPC32 p358)
ceriond953ebb2005-11-29 13:27:20 +00003016 DIP("andi. r%u,r%u,0x%x\n", rA_addr, rS_addr, uimm16);
cerion2831b002005-11-30 19:55:22 +00003017 assign( rA, binop( mkSzOp(ty, Iop_And8), mkexpr(rS),
3018 mkSzImm(ty, uimm16)) );
cerion70e24122005-03-16 00:27:37 +00003019 do_rc = True; // Always record to CR
cerion76de5cf2005-11-18 18:25:12 +00003020 flag_rC = 1;
cerionb85e8bb2005-02-16 08:54:33 +00003021 break;
3022
cerione9d361a2005-03-04 17:35:29 +00003023 case 0x1D: // andis. (AND Immediate Shifted, PPC32 p359)
ceriond953ebb2005-11-29 13:27:20 +00003024 DIP("andis r%u,r%u,0x%x\n", rA_addr, rS_addr, uimm16);
cerion2831b002005-11-30 19:55:22 +00003025 assign( rA, binop( mkSzOp(ty, Iop_And8), mkexpr(rS),
3026 mkSzImm(ty, uimm16 << 16)) );
cerion70e24122005-03-16 00:27:37 +00003027 do_rc = True; // Always record to CR
cerion76de5cf2005-11-18 18:25:12 +00003028 flag_rC = 1;
cerionb85e8bb2005-02-16 08:54:33 +00003029 break;
cerion7aa4bbc2005-01-29 09:32:07 +00003030
cerione9d361a2005-03-04 17:35:29 +00003031 case 0x18: // ori (OR Immediate, PPC32 p497)
ceriond953ebb2005-11-29 13:27:20 +00003032 DIP("ori r%u,r%u,0x%x\n", rA_addr, rS_addr, uimm16);
cerion2831b002005-11-30 19:55:22 +00003033 assign( rA, binop( mkSzOp(ty, Iop_Or8), mkexpr(rS),
3034 mkSzImm(ty, uimm16)) );
cerionb85e8bb2005-02-16 08:54:33 +00003035 break;
cerion7aa4bbc2005-01-29 09:32:07 +00003036
cerione9d361a2005-03-04 17:35:29 +00003037 case 0x19: // oris (OR Immediate Shifted, PPC32 p498)
ceriond953ebb2005-11-29 13:27:20 +00003038 DIP("oris r%u,r%u,0x%x\n", rA_addr, rS_addr, uimm16);
cerion2831b002005-11-30 19:55:22 +00003039 assign( rA, binop( mkSzOp(ty, Iop_Or8), mkexpr(rS),
3040 mkSzImm(ty, uimm16 << 16)) );
cerionb85e8bb2005-02-16 08:54:33 +00003041 break;
cerionaabdfbf2005-01-29 12:56:15 +00003042
cerione9d361a2005-03-04 17:35:29 +00003043 case 0x1A: // xori (XOR Immediate, PPC32 p550)
ceriond953ebb2005-11-29 13:27:20 +00003044 DIP("xori r%u,r%u,0x%x\n", rA_addr, rS_addr, uimm16);
cerion2831b002005-11-30 19:55:22 +00003045 assign( rA, binop( mkSzOp(ty, Iop_Xor8), mkexpr(rS),
3046 mkSzImm(ty, uimm16)) );
cerionb85e8bb2005-02-16 08:54:33 +00003047 break;
cerion38674602005-02-08 02:19:25 +00003048
cerione9d361a2005-03-04 17:35:29 +00003049 case 0x1B: // xoris (XOR Immediate Shifted, PPC32 p551)
ceriond953ebb2005-11-29 13:27:20 +00003050 DIP("xoris r%u,r%u,0x%x\n", rA_addr, rS_addr, uimm16);
cerion2831b002005-11-30 19:55:22 +00003051 assign( rA, binop( mkSzOp(ty, Iop_Xor8), mkexpr(rS),
3052 mkSzImm(ty, uimm16 << 16)) );
cerionb85e8bb2005-02-16 08:54:33 +00003053 break;
cerionaabdfbf2005-01-29 12:56:15 +00003054
cerionb85e8bb2005-02-16 08:54:33 +00003055 /* X Form */
3056 case 0x1F:
cerion70e24122005-03-16 00:27:37 +00003057 do_rc = True; // All below record to CR
3058
cerionb85e8bb2005-02-16 08:54:33 +00003059 switch (opc2) {
ceriond953ebb2005-11-29 13:27:20 +00003060 case 0x01C: // and (AND, PPC32 p356)
3061 DIP("and%s r%u,r%u,r%u\n",
cerion5b2325f2005-12-23 00:55:09 +00003062 flag_rC ? ".":"", rA_addr, rS_addr, rB_addr);
cerion2831b002005-11-30 19:55:22 +00003063 assign(rA, binop( mkSzOp(ty, Iop_And8),
ceriond953ebb2005-11-29 13:27:20 +00003064 mkexpr(rS), mkexpr(rB)));
3065 break;
3066
3067 case 0x03C: // andc (AND with Complement, PPC32 p357)
3068 DIP("andc%s r%u,r%u,r%u\n",
cerion5b2325f2005-12-23 00:55:09 +00003069 flag_rC ? ".":"", rA_addr, rS_addr, rB_addr);
cerion2831b002005-11-30 19:55:22 +00003070 assign(rA, binop( mkSzOp(ty, Iop_And8), mkexpr(rS),
3071 unop( mkSzOp(ty, Iop_Not8),
ceriond953ebb2005-11-29 13:27:20 +00003072 mkexpr(rB))));
3073 break;
3074
3075 case 0x01A: { // cntlzw (Count Leading Zeros Word, PPC32 p371)
3076 IRExpr* lo32;
3077 if (rB_addr!=0) {
cerion5b2325f2005-12-23 00:55:09 +00003078 vex_printf("dis_int_logic(ppc)(cntlzw,rB_addr)\n");
ceriond953ebb2005-11-29 13:27:20 +00003079 return False;
3080 }
3081 DIP("cntlzw%s r%u,r%u\n",
cerion5b2325f2005-12-23 00:55:09 +00003082 flag_rC ? ".":"", rA_addr, rS_addr);
ceriond953ebb2005-11-29 13:27:20 +00003083
3084 // mode64: count in low word only
3085 lo32 = mode64 ? unop(Iop_64to32, mkexpr(rS)) : mkexpr(rS);
3086
3087 // Iop_Clz32 undefined for arg==0, so deal with that case:
3088 irx = binop(Iop_CmpNE32, lo32, mkU32(0));
cerion5b2325f2005-12-23 00:55:09 +00003089 assign(rA, mkSzWiden32(ty,
3090 IRExpr_Mux0X( unop(Iop_1Uto8, irx),
3091 mkU32(32),
3092 unop(Iop_Clz32, lo32)),
3093 False));
3094
ceriond953ebb2005-11-29 13:27:20 +00003095 // TODO: alternatively: assign(rA, verbose_Clz32(rS));
3096 break;
3097 }
3098
3099 case 0x11C: // eqv (Equivalent, PPC32 p396)
3100 DIP("eqv%s r%u,r%u,r%u\n",
cerion5b2325f2005-12-23 00:55:09 +00003101 flag_rC ? ".":"", rA_addr, rS_addr, rB_addr);
cerion2831b002005-11-30 19:55:22 +00003102 assign( rA, unop( mkSzOp(ty, Iop_Not8),
3103 binop( mkSzOp(ty, Iop_Xor8),
ceriond953ebb2005-11-29 13:27:20 +00003104 mkexpr(rS), mkexpr(rB))) );
sewardj20ef5472005-07-21 14:48:31 +00003105 break;
cerion7aa4bbc2005-01-29 09:32:07 +00003106
cerione9d361a2005-03-04 17:35:29 +00003107 case 0x3BA: // extsb (Extend Sign Byte, PPC32 p397
cerion76de5cf2005-11-18 18:25:12 +00003108 if (rB_addr!=0) {
cerion5b2325f2005-12-23 00:55:09 +00003109 vex_printf("dis_int_logic(ppc)(extsb,rB_addr)\n");
cerionb85e8bb2005-02-16 08:54:33 +00003110 return False;
3111 }
ceriond953ebb2005-11-29 13:27:20 +00003112 DIP("extsb%s r%u,r%u\n",
cerion5b2325f2005-12-23 00:55:09 +00003113 flag_rC ? ".":"", rA_addr, rS_addr);
ceriond953ebb2005-11-29 13:27:20 +00003114 if (mode64)
3115 assign( rA, unop(Iop_8Sto64, unop(Iop_64to8, mkexpr(rS))) );
3116 else
3117 assign( rA, unop(Iop_8Sto32, unop(Iop_32to8, mkexpr(rS))) );
cerionb85e8bb2005-02-16 08:54:33 +00003118 break;
cerion7aa4bbc2005-01-29 09:32:07 +00003119
cerione9d361a2005-03-04 17:35:29 +00003120 case 0x39A: // extsh (Extend Sign Half Word, PPC32 p398)
cerion76de5cf2005-11-18 18:25:12 +00003121 if (rB_addr!=0) {
cerion5b2325f2005-12-23 00:55:09 +00003122 vex_printf("dis_int_logic(ppc)(extsh,rB_addr)\n");
cerionb85e8bb2005-02-16 08:54:33 +00003123 return False;
3124 }
ceriond953ebb2005-11-29 13:27:20 +00003125 DIP("extsh%s r%u,r%u\n",
cerion5b2325f2005-12-23 00:55:09 +00003126 flag_rC ? ".":"", rA_addr, rS_addr);
ceriond953ebb2005-11-29 13:27:20 +00003127 if (mode64)
cerion5b2325f2005-12-23 00:55:09 +00003128 assign( rA, unop(Iop_16Sto64,
3129 unop(Iop_64to16, mkexpr(rS))) );
ceriond953ebb2005-11-29 13:27:20 +00003130 else
cerion5b2325f2005-12-23 00:55:09 +00003131 assign( rA, unop(Iop_16Sto32,
3132 unop(Iop_32to16, mkexpr(rS))) );
cerionb85e8bb2005-02-16 08:54:33 +00003133 break;
cerion7aa4bbc2005-01-29 09:32:07 +00003134
cerione9d361a2005-03-04 17:35:29 +00003135 case 0x1DC: // nand (NAND, PPC32 p492)
ceriond953ebb2005-11-29 13:27:20 +00003136 DIP("nand%s r%u,r%u,r%u\n",
cerion5b2325f2005-12-23 00:55:09 +00003137 flag_rC ? ".":"", rA_addr, rS_addr, rB_addr);
cerion2831b002005-11-30 19:55:22 +00003138 assign( rA, unop( mkSzOp(ty, Iop_Not8),
3139 binop( mkSzOp(ty, Iop_And8),
ceriond953ebb2005-11-29 13:27:20 +00003140 mkexpr(rS), mkexpr(rB))) );
cerionb85e8bb2005-02-16 08:54:33 +00003141 break;
3142
cerione9d361a2005-03-04 17:35:29 +00003143 case 0x07C: // nor (NOR, PPC32 p494)
ceriond953ebb2005-11-29 13:27:20 +00003144 DIP("nor%s r%u,r%u,r%u\n",
cerion5b2325f2005-12-23 00:55:09 +00003145 flag_rC ? ".":"", rA_addr, rS_addr, rB_addr);
cerion2831b002005-11-30 19:55:22 +00003146 assign( rA, unop( mkSzOp(ty, Iop_Not8),
3147 binop( mkSzOp(ty, Iop_Or8),
ceriond953ebb2005-11-29 13:27:20 +00003148 mkexpr(rS), mkexpr(rB))) );
cerionb85e8bb2005-02-16 08:54:33 +00003149 break;
cerion7aa4bbc2005-01-29 09:32:07 +00003150
cerione9d361a2005-03-04 17:35:29 +00003151 case 0x1BC: // or (OR, PPC32 p495)
cerion76de5cf2005-11-18 18:25:12 +00003152 if ((!flag_rC) && rS_addr == rB_addr) {
ceriond953ebb2005-11-29 13:27:20 +00003153 DIP("mr r%u,r%u\n", rA_addr, rS_addr);
cerion76de5cf2005-11-18 18:25:12 +00003154 assign( rA, mkexpr(rS) );
sewardjb51f0f42005-07-18 11:38:02 +00003155 } else {
ceriond953ebb2005-11-29 13:27:20 +00003156 DIP("or%s r%u,r%u,r%u\n",
cerion5b2325f2005-12-23 00:55:09 +00003157 flag_rC ? ".":"", rA_addr, rS_addr, rB_addr);
cerion2831b002005-11-30 19:55:22 +00003158 assign( rA, binop( mkSzOp(ty, Iop_Or8),
ceriond953ebb2005-11-29 13:27:20 +00003159 mkexpr(rS), mkexpr(rB)) );
sewardjb51f0f42005-07-18 11:38:02 +00003160 }
cerionb85e8bb2005-02-16 08:54:33 +00003161 break;
cerion7aa4bbc2005-01-29 09:32:07 +00003162
cerione9d361a2005-03-04 17:35:29 +00003163 case 0x19C: // orc (OR with Complement, PPC32 p496)
ceriond953ebb2005-11-29 13:27:20 +00003164 DIP("orc%s r%u,r%u,r%u\n",
cerion5b2325f2005-12-23 00:55:09 +00003165 flag_rC ? ".":"", rA_addr, rS_addr, rB_addr);
cerion2831b002005-11-30 19:55:22 +00003166 assign( rA, binop( mkSzOp(ty, Iop_Or8), mkexpr(rS),
3167 unop(mkSzOp(ty, Iop_Not8), mkexpr(rB))));
cerionb85e8bb2005-02-16 08:54:33 +00003168 break;
3169
cerione9d361a2005-03-04 17:35:29 +00003170 case 0x13C: // xor (XOR, PPC32 p549)
ceriond953ebb2005-11-29 13:27:20 +00003171 DIP("xor%s r%u,r%u,r%u\n",
cerion5b2325f2005-12-23 00:55:09 +00003172 flag_rC ? ".":"", rA_addr, rS_addr, rB_addr);
cerion2831b002005-11-30 19:55:22 +00003173 assign( rA, binop( mkSzOp(ty, Iop_Xor8),
ceriond953ebb2005-11-29 13:27:20 +00003174 mkexpr(rS), mkexpr(rB)) );
cerionb85e8bb2005-02-16 08:54:33 +00003175 break;
cerion7aa4bbc2005-01-29 09:32:07 +00003176
cerionf0de28c2005-12-13 20:21:11 +00003177
3178 /* 64bit Integer Logical Instructions */
3179 case 0x3DA: // extsw (Extend Sign Word, PPC64 p430)
3180 if (rB_addr!=0) {
cerion5b2325f2005-12-23 00:55:09 +00003181 vex_printf("dis_int_logic(ppc)(extsw,rB_addr)\n");
cerionf0de28c2005-12-13 20:21:11 +00003182 return False;
3183 }
cerion5b2325f2005-12-23 00:55:09 +00003184 DIP("extsw%s r%u,r%u\n", flag_rC ? ".":"", rA_addr, rS_addr);
cerionf0de28c2005-12-13 20:21:11 +00003185 assign(rA, unop(Iop_32Sto64, unop(Iop_64to32, mkexpr(rS))));
3186 break;
3187
cerion5b2325f2005-12-23 00:55:09 +00003188 case 0x03A: // cntlzd (Count Leading Zeros DWord, PPC64 p401)
cerionf0de28c2005-12-13 20:21:11 +00003189 if (rB_addr!=0) {
cerion5b2325f2005-12-23 00:55:09 +00003190 vex_printf("dis_int_logic(ppc)(cntlzd,rB_addr)\n");
cerionf0de28c2005-12-13 20:21:11 +00003191 return False;
3192 }
cerion5b2325f2005-12-23 00:55:09 +00003193 DIP("cntlzd%s r%u,r%u\n",
3194 flag_rC ? ".":"", rA_addr, rS_addr);
cerion07b07a92005-12-22 14:32:35 +00003195 // Iop_Clz64 undefined for arg==0, so deal with that case:
3196 irx = binop(Iop_CmpNE64, mkexpr(rS), mkU64(0));
3197 assign(rA, IRExpr_Mux0X( unop(Iop_1Uto8, irx),
3198 mkU64(64),
3199 unop(Iop_Clz64, mkexpr(rS)) ));
cerion5b2325f2005-12-23 00:55:09 +00003200 // TODO: alternatively: assign(rA, verbose_Clz64(rS));
cerion07b07a92005-12-22 14:32:35 +00003201 break;
cerionf0de28c2005-12-13 20:21:11 +00003202
cerionb85e8bb2005-02-16 08:54:33 +00003203 default:
cerion5b2325f2005-12-23 00:55:09 +00003204 vex_printf("dis_int_logic(ppc)(opc2)\n");
cerionb85e8bb2005-02-16 08:54:33 +00003205 return False;
3206 }
cerionb85e8bb2005-02-16 08:54:33 +00003207 break;
3208
3209 default:
cerion5b2325f2005-12-23 00:55:09 +00003210 vex_printf("dis_int_logic(ppc)(opc1)\n");
cerionb85e8bb2005-02-16 08:54:33 +00003211 return False;
3212 }
cerion70e24122005-03-16 00:27:37 +00003213
cerion76de5cf2005-11-18 18:25:12 +00003214 putIReg( rA_addr, mkexpr(rA) );
3215
3216 if (do_rc && flag_rC) {
3217 set_CR0( mkexpr(rA) );
cerionb85e8bb2005-02-16 08:54:33 +00003218 }
3219 return True;
cerion645c9302005-01-31 10:09:59 +00003220}
3221
3222
3223
cerion3d870a32005-03-18 12:23:33 +00003224/*
3225 Integer Rotate Instructions
3226*/
cerion645c9302005-01-31 10:09:59 +00003227static Bool dis_int_rot ( UInt theInstr )
3228{
cerionf0de28c2005-12-13 20:21:11 +00003229 /* M-Form, MDS-Form */
ceriond953ebb2005-11-29 13:27:20 +00003230 UChar opc1 = ifieldOPC(theInstr);
3231 UChar rS_addr = ifieldRegDS(theInstr);
3232 UChar rA_addr = ifieldRegA(theInstr);
3233 UChar rB_addr = ifieldRegB(theInstr);
3234 UChar sh_imm = rB_addr;
3235 UChar MaskBeg = toUChar( IFIELD( theInstr, 6, 5 ) );
3236 UChar MaskEnd = toUChar( IFIELD( theInstr, 1, 5 ) );
cerionf0de28c2005-12-13 20:21:11 +00003237 UChar msk_imm = toUChar( IFIELD( theInstr, 5, 6 ) );
3238 UChar opc2 = toUChar( IFIELD( theInstr, 2, 3 ) );
3239 UChar b1 = ifieldBIT1(theInstr);
ceriond953ebb2005-11-29 13:27:20 +00003240 UChar flag_rC = ifieldBIT0(theInstr);
cerion76de5cf2005-11-18 18:25:12 +00003241
ceriond953ebb2005-11-29 13:27:20 +00003242 IRType ty = mode64 ? Ity_I64 : Ity_I32;
3243 IRTemp rS = newTemp(ty);
3244 IRTemp rA = newTemp(ty);
3245 IRTemp rB = newTemp(ty);
cerionbb01b7c2005-12-16 13:40:18 +00003246 IRTemp rot = newTemp(ty);
ceriond953ebb2005-11-29 13:27:20 +00003247 IRExpr *r;
cerionf0de28c2005-12-13 20:21:11 +00003248 UInt mask32;
3249 ULong mask64;
ceriond953ebb2005-11-29 13:27:20 +00003250
cerion76de5cf2005-11-18 18:25:12 +00003251 assign( rS, getIReg(rS_addr) );
3252 assign( rB, getIReg(rB_addr) );
ceriond953ebb2005-11-29 13:27:20 +00003253
cerionb85e8bb2005-02-16 08:54:33 +00003254 switch (opc1) {
ceriond953ebb2005-11-29 13:27:20 +00003255 case 0x14: {
cerion5b2325f2005-12-23 00:55:09 +00003256 // rlwimi (Rotate Left Word Imm then Mask Insert, PPC32 p500)
3257 DIP("rlwimi%s r%u,r%u,%d,%d,%d\n", flag_rC ? ".":"",
ceriond953ebb2005-11-29 13:27:20 +00003258 rA_addr, rS_addr, sh_imm, MaskBeg, MaskEnd);
3259 if (mode64) {
3260 // tmp32 = (ROTL(rS_Lo32, Imm)
3261 // rA = ((tmp32 || tmp32) & mask64) | (rA & ~mask64)
cerionf0de28c2005-12-13 20:21:11 +00003262 mask64 = MASK64(31-MaskEnd, 31-MaskBeg);
ceriond953ebb2005-11-29 13:27:20 +00003263 r = ROTL( unop(Iop_64to32, mkexpr(rS) ), mkU8(sh_imm) );
3264 r = unop(Iop_32Uto64, r);
cerion5b2325f2005-12-23 00:55:09 +00003265 assign( rot, binop(Iop_Or64, r,
3266 binop(Iop_Shl64, r, mkU8(32))) );
ceriond953ebb2005-11-29 13:27:20 +00003267 assign( rA,
3268 binop(Iop_Or64,
cerionbb01b7c2005-12-16 13:40:18 +00003269 binop(Iop_And64, mkexpr(rot), mkU64(mask64)),
cerionf0de28c2005-12-13 20:21:11 +00003270 binop(Iop_And64, getIReg(rA_addr), mkU64(~mask64))) );
sewardj26b33202005-10-07 09:45:16 +00003271 }
3272 else {
ceriond953ebb2005-11-29 13:27:20 +00003273 // rA = (ROTL(rS, Imm) & mask) | (rA & ~mask);
cerionf0de28c2005-12-13 20:21:11 +00003274 mask32 = MASK32(31-MaskEnd, 31-MaskBeg);
3275 r = ROTL(mkexpr(rS), mkU8(sh_imm));
ceriond953ebb2005-11-29 13:27:20 +00003276 assign( rA,
3277 binop(Iop_Or32,
cerionf0de28c2005-12-13 20:21:11 +00003278 binop(Iop_And32, mkU32(mask32), r),
3279 binop(Iop_And32, getIReg(rA_addr), mkU32(~mask32))) );
sewardj26b33202005-10-07 09:45:16 +00003280 }
cerionb85e8bb2005-02-16 08:54:33 +00003281 break;
ceriond953ebb2005-11-29 13:27:20 +00003282 }
cerion45b70ff2005-01-31 17:03:25 +00003283
ceriond953ebb2005-11-29 13:27:20 +00003284 case 0x15: {
cerion5b2325f2005-12-23 00:55:09 +00003285 // rlwinm (Rotate Left Word Imm then AND with Mask, PPC32 p501)
ceriond953ebb2005-11-29 13:27:20 +00003286 vassert(MaskBeg < 32);
3287 vassert(MaskEnd < 32);
3288 vassert(sh_imm < 32);
3289
3290 if (mode64) {
cerionf0de28c2005-12-13 20:21:11 +00003291 mask64 = MASK64(31-MaskEnd, 31-MaskBeg);
cerion5b2325f2005-12-23 00:55:09 +00003292 DIP("rlwinm%s r%u,r%u,%d,%d,%d\n", flag_rC ? ".":"",
ceriond953ebb2005-11-29 13:27:20 +00003293 rA_addr, rS_addr, sh_imm, MaskBeg, MaskEnd);
3294 // tmp32 = (ROTL(rS_Lo32, Imm)
3295 // rA = ((tmp32 || tmp32) & mask64)
3296 r = ROTL( unop(Iop_64to32, mkexpr(rS) ), mkU8(sh_imm) );
3297 r = unop(Iop_32Uto64, r);
cerion5b2325f2005-12-23 00:55:09 +00003298 assign( rot, binop(Iop_Or64, r,
3299 binop(Iop_Shl64, r, mkU8(32))) );
cerionbb01b7c2005-12-16 13:40:18 +00003300 assign( rA, binop(Iop_And64, mkexpr(rot), mkU64(mask64)) );
ceriond953ebb2005-11-29 13:27:20 +00003301 }
3302 else {
3303 if (MaskBeg == 0 && sh_imm+MaskEnd == 31) {
3304 /* Special-case the ,n,0,31-n form as that is just n-bit
cerion5b2325f2005-12-23 00:55:09 +00003305 shift left, PPC32 p501 */
3306 DIP("slwi%s r%u,r%u,%d\n", flag_rC ? ".":"",
ceriond953ebb2005-11-29 13:27:20 +00003307 rA_addr, rS_addr, sh_imm);
3308 assign( rA, binop(Iop_Shl32, mkexpr(rS), mkU8(sh_imm)) );
3309 }
cerion2831b002005-11-30 19:55:22 +00003310 else if (MaskEnd == 31 && sh_imm+MaskBeg == 32) {
3311 /* Special-case the ,32-n,n,31 form as that is just n-bit
cerion5b2325f2005-12-23 00:55:09 +00003312 unsigned shift right, PPC32 p501 */
3313 DIP("srwi%s r%u,r%u,%d\n", flag_rC ? ".":"",
cerion2831b002005-11-30 19:55:22 +00003314 rA_addr, rS_addr, sh_imm);
3315 assign( rA, binop(Iop_Shr32, mkexpr(rS), mkU8(MaskBeg)) );
3316 }
3317 else {
3318 /* General case. */
cerionf0de28c2005-12-13 20:21:11 +00003319 mask32 = MASK32(31-MaskEnd, 31-MaskBeg);
cerion5b2325f2005-12-23 00:55:09 +00003320 DIP("rlwinm%s r%u,r%u,%d,%d,%d\n", flag_rC ? ".":"",
cerion2831b002005-11-30 19:55:22 +00003321 rA_addr, rS_addr, sh_imm, MaskBeg, MaskEnd);
3322 // rA = ROTL(rS, Imm) & mask
cerion5b2325f2005-12-23 00:55:09 +00003323 assign( rA, binop(Iop_And32,
3324 ROTL(mkexpr(rS), mkU8(sh_imm)),
cerionf0de28c2005-12-13 20:21:11 +00003325 mkU32(mask32)) );
cerion2831b002005-11-30 19:55:22 +00003326 }
ceriond953ebb2005-11-29 13:27:20 +00003327 }
sewardjc9659532005-07-21 21:33:57 +00003328 break;
ceriond953ebb2005-11-29 13:27:20 +00003329 }
3330
3331 case 0x17: {
3332 // rlwnm (Rotate Left Word then AND with Mask, PPC32 p503
cerion5b2325f2005-12-23 00:55:09 +00003333 DIP("rlwnm%s r%u,r%u,r%u,%d,%d\n", flag_rC ? ".":"",
ceriond953ebb2005-11-29 13:27:20 +00003334 rA_addr, rS_addr, rB_addr, MaskBeg, MaskEnd);
3335 if (mode64) {
cerionf0de28c2005-12-13 20:21:11 +00003336 mask64 = MASK64(31-MaskEnd, 31-MaskBeg);
cerionbb01b7c2005-12-16 13:40:18 +00003337 /* weird insn alert!
3338 tmp32 = (ROTL(rS_Lo32, rB[0-4])
3339 rA = ((tmp32 || tmp32) & mask64)
3340 */
ceriond953ebb2005-11-29 13:27:20 +00003341 // note, ROTL does the masking, so we don't do it here
3342 r = ROTL( unop(Iop_64to32, mkexpr(rS)),
cerionbb01b7c2005-12-16 13:40:18 +00003343 unop(Iop_64to8, mkexpr(rB)) );
ceriond953ebb2005-11-29 13:27:20 +00003344 r = unop(Iop_32Uto64, r);
cerionbb01b7c2005-12-16 13:40:18 +00003345 assign(rot, binop(Iop_Or64, r, binop(Iop_Shl64, r, mkU8(32))));
3346 assign( rA, binop(Iop_And64, mkexpr(rot), mkU64(mask64)) );
ceriond953ebb2005-11-29 13:27:20 +00003347 } else {
cerionf0de28c2005-12-13 20:21:11 +00003348 mask32 = MASK32(31-MaskEnd, 31-MaskBeg);
ceriond953ebb2005-11-29 13:27:20 +00003349 // rA = ROTL(rS, rB[0-4]) & mask
3350 // note, ROTL does the masking, so we don't do it here
3351 assign( rA, binop(Iop_And32,
cerion5b2325f2005-12-23 00:55:09 +00003352 ROTL(mkexpr(rS),
3353 unop(Iop_32to8, mkexpr(rB))),
cerionf0de28c2005-12-13 20:21:11 +00003354 mkU32(mask32)) );
ceriond953ebb2005-11-29 13:27:20 +00003355 }
3356 break;
3357 }
cerion45b70ff2005-01-31 17:03:25 +00003358
cerionf0de28c2005-12-13 20:21:11 +00003359
3360 /* 64bit Integer Rotates */
3361 case 0x1E: {
3362 msk_imm = ((msk_imm & 1) << 5) | (msk_imm >> 1);
3363 sh_imm |= b1 << 5;
3364
3365 vassert( msk_imm < 64 );
3366 vassert( sh_imm < 64 );
3367
3368 switch (opc2) {
cerion07b07a92005-12-22 14:32:35 +00003369 case 0x4: {
3370 /* r = ROTL64( rS, rB_lo6) */
3371 r = ROTL( mkexpr(rS), unop(Iop_64to8, mkexpr(rB)) );
3372
cerion5b2325f2005-12-23 00:55:09 +00003373 if (b1 == 0) { // rldcl (Rotl DWord, Clear Left, PPC64 p555)
3374 DIP("rldcl%s r%u,r%u,r%u,%u\n", flag_rC ? ".":"",
cerionf0de28c2005-12-13 20:21:11 +00003375 rA_addr, rS_addr, rB_addr, msk_imm);
cerion07b07a92005-12-22 14:32:35 +00003376 // note, ROTL does the masking, so we don't do it here
cerionf0de28c2005-12-13 20:21:11 +00003377 mask64 = MASK64(0, 63-msk_imm);
3378 assign( rA, binop(Iop_And64, r, mkU64(mask64)) );
3379 break;
cerion5b2325f2005-12-23 00:55:09 +00003380 } else { // rldcr (Rotl DWord, Clear Right, PPC64 p556)
3381 DIP("rldcr%s r%u,r%u,r%u,%u\n", flag_rC ? ".":"",
cerionf0de28c2005-12-13 20:21:11 +00003382 rA_addr, rS_addr, rB_addr, msk_imm);
cerionf0de28c2005-12-13 20:21:11 +00003383 mask64 = MASK64(63-msk_imm, 63);
3384 assign( rA, binop(Iop_And64, r, mkU64(mask64)) );
3385 break;
3386 }
3387 break;
cerion07b07a92005-12-22 14:32:35 +00003388 }
cerion5b2325f2005-12-23 00:55:09 +00003389 case 0x2: // rldic (Rotl DWord Imm, Clear, PPC64 p557)
3390 DIP("rldic%s r%u,r%u,%u,%u\n", flag_rC ? ".":"",
cerionf0de28c2005-12-13 20:21:11 +00003391 rA_addr, rS_addr, sh_imm, msk_imm);
3392 r = ROTL(mkexpr(rS), mkU8(sh_imm));
3393 mask64 = MASK64(sh_imm, 63-msk_imm);
3394 assign( rA, binop(Iop_And64, r, mkU64(mask64)) );
3395 break;
3396 // later: deal with special case: (msk_imm==0) => SHL(sh_imm)
3397 /*
3398 Hmm... looks like this'll do the job more simply:
3399 r = SHL(rS, sh_imm)
3400 m = ~(1 << (63-msk_imm))
3401 assign(rA, r & m);
3402 */
3403
cerion5b2325f2005-12-23 00:55:09 +00003404 case 0x0: // rldicl (Rotl DWord Imm, Clear Left, PPC64 p558)
3405 DIP("rldicl%s r%u,r%u,%u,%u\n", flag_rC ? ".":"",
cerionf0de28c2005-12-13 20:21:11 +00003406 rA_addr, rS_addr, sh_imm, msk_imm);
3407 r = ROTL(mkexpr(rS), mkU8(sh_imm));
3408 mask64 = MASK64(0, 63-msk_imm);
3409 assign( rA, binop(Iop_And64, r, mkU64(mask64)) );
3410 break;
cerion5b2325f2005-12-23 00:55:09 +00003411 /* later: deal with special case:
3412 (msk_imm + sh_imm == 63) => SHR(63 - sh_imm) */
cerionf0de28c2005-12-13 20:21:11 +00003413
cerion5b2325f2005-12-23 00:55:09 +00003414 case 0x1: // rldicr (Rotl DWord Imm, Clear Right, PPC64 p559)
3415 DIP("rldicr%s r%u,r%u,%u,%u\n", flag_rC ? ".":"",
cerionf0de28c2005-12-13 20:21:11 +00003416 rA_addr, rS_addr, sh_imm, msk_imm);
3417 r = ROTL(mkexpr(rS), mkU8(sh_imm));
3418 mask64 = MASK64(63-msk_imm, 63);
3419 assign( rA, binop(Iop_And64, r, mkU64(mask64)) );
3420 break;
cerion5b2325f2005-12-23 00:55:09 +00003421 /* later: deal with special case:
3422 (msk_imm == sh_imm) => SHL(sh_imm) */
cerionf0de28c2005-12-13 20:21:11 +00003423
cerion5b2325f2005-12-23 00:55:09 +00003424 case 0x3: { // rldimi (Rotl DWord Imm, Mask Insert, PPC64 p560)
cerion07b07a92005-12-22 14:32:35 +00003425 IRTemp rA_orig = newTemp(ty);
cerion5b2325f2005-12-23 00:55:09 +00003426 DIP("rldimi%s r%u,r%u,%u,%u\n", flag_rC ? ".":"",
cerionf0de28c2005-12-13 20:21:11 +00003427 rA_addr, rS_addr, sh_imm, msk_imm);
3428 r = ROTL(mkexpr(rS), mkU8(sh_imm));
3429 mask64 = MASK64(sh_imm, 63-msk_imm);
cerion07b07a92005-12-22 14:32:35 +00003430 assign( rA_orig, getIReg(rA_addr) );
cerionf0de28c2005-12-13 20:21:11 +00003431 assign( rA, binop(Iop_Or64,
3432 binop(Iop_And64, mkU64(mask64), r),
cerion5b2325f2005-12-23 00:55:09 +00003433 binop(Iop_And64, mkU64(~mask64),
3434 mkexpr(rA_orig))) );
cerionf0de28c2005-12-13 20:21:11 +00003435 break;
cerion07b07a92005-12-22 14:32:35 +00003436 }
cerionf0de28c2005-12-13 20:21:11 +00003437 default:
cerion5b2325f2005-12-23 00:55:09 +00003438 vex_printf("dis_int_rot(ppc)(opc2)\n");
cerionf0de28c2005-12-13 20:21:11 +00003439 return False;
3440 }
3441 break;
3442 }
3443
cerionb85e8bb2005-02-16 08:54:33 +00003444 default:
cerion5b2325f2005-12-23 00:55:09 +00003445 vex_printf("dis_int_rot(ppc)(opc1)\n");
cerionb85e8bb2005-02-16 08:54:33 +00003446 return False;
3447 }
cerion645c9302005-01-31 10:09:59 +00003448
cerion76de5cf2005-11-18 18:25:12 +00003449 putIReg( rA_addr, mkexpr(rA) );
3450
3451 if (flag_rC) {
3452 set_CR0( mkexpr(rA) );
cerionb85e8bb2005-02-16 08:54:33 +00003453 }
3454 return True;
cerion645c9302005-01-31 10:09:59 +00003455}
3456
3457
cerion3d870a32005-03-18 12:23:33 +00003458/*
3459 Integer Load Instructions
3460*/
cerion645c9302005-01-31 10:09:59 +00003461static Bool dis_int_load ( UInt theInstr )
3462{
cerionf0de28c2005-12-13 20:21:11 +00003463 /* D-Form, X-Form, DS-Form */
cerion76de5cf2005-11-18 18:25:12 +00003464 UChar opc1 = ifieldOPC(theInstr);
3465 UChar rD_addr = ifieldRegDS(theInstr);
3466 UChar rA_addr = ifieldRegA(theInstr);
ceriond953ebb2005-11-29 13:27:20 +00003467 UInt uimm16 = ifieldUIMM16(theInstr);
cerion76de5cf2005-11-18 18:25:12 +00003468 UChar rB_addr = ifieldRegB(theInstr);
3469 UInt opc2 = ifieldOPClo10(theInstr);
cerionf0de28c2005-12-13 20:21:11 +00003470 UChar b1 = ifieldBIT1(theInstr);
cerion76de5cf2005-11-18 18:25:12 +00003471 UChar b0 = ifieldBIT0(theInstr);
3472
ceriond953ebb2005-11-29 13:27:20 +00003473 Int simm16 = extend_s_16to32(uimm16);
3474 IRType ty = mode64 ? Ity_I64 : Ity_I32;
ceriond953ebb2005-11-29 13:27:20 +00003475 IRTemp EA = newTemp(ty);
3476 IRExpr* val;
cerionedf7fc52005-11-18 20:57:41 +00003477
cerionf0de28c2005-12-13 20:21:11 +00003478 switch (opc1) {
3479 case 0x1F: // register offset
ceriond953ebb2005-11-29 13:27:20 +00003480 assign( EA, ea_rAor0_idxd( rA_addr, rB_addr ) );
cerionf0de28c2005-12-13 20:21:11 +00003481 break;
3482 case 0x3A: // immediate offset: 64bit
3483 simm16 = simm16 & 0xFFFFFFFC;
3484 default: // immediate offset
3485 assign( EA, ea_rAor0_simm( rA_addr, simm16 ) );
3486 break;
ceriond953ebb2005-11-29 13:27:20 +00003487 }
cerione9d361a2005-03-04 17:35:29 +00003488
cerionb85e8bb2005-02-16 08:54:33 +00003489 switch (opc1) {
cerione9d361a2005-03-04 17:35:29 +00003490 case 0x22: // lbz (Load B & Zero, PPC32 p433)
ceriond953ebb2005-11-29 13:27:20 +00003491 DIP("lbz r%u,%d(r%u)\n", rD_addr, (Int)simm16, rA_addr);
3492 val = loadBE(Ity_I8, mkexpr(EA));
cerion2831b002005-11-30 19:55:22 +00003493 putIReg( rD_addr, mkSzWiden8(ty, val, False) );
cerionb85e8bb2005-02-16 08:54:33 +00003494 break;
3495
cerion5b2325f2005-12-23 00:55:09 +00003496 case 0x23: // lbzu (Load B & Zero, Update, PPC32 p434)
cerion76de5cf2005-11-18 18:25:12 +00003497 if (rA_addr == 0 || rA_addr == rD_addr) {
cerion5b2325f2005-12-23 00:55:09 +00003498 vex_printf("dis_int_load(ppc)(lbzu,rA_addr|rD_addr)\n");
cerionb85e8bb2005-02-16 08:54:33 +00003499 return False;
3500 }
ceriond953ebb2005-11-29 13:27:20 +00003501 DIP("lbzu r%u,%d(r%u)\n", rD_addr, (Int)simm16, rA_addr);
3502 val = loadBE(Ity_I8, mkexpr(EA));
cerion2831b002005-11-30 19:55:22 +00003503 putIReg( rD_addr, mkSzWiden8(ty, val, False) );
ceriond953ebb2005-11-29 13:27:20 +00003504 putIReg( rA_addr, mkexpr(EA) );
cerionb85e8bb2005-02-16 08:54:33 +00003505 break;
3506
cerion5b2325f2005-12-23 00:55:09 +00003507 case 0x2A: // lha (Load HW Alg, PPC32 p445)
ceriond953ebb2005-11-29 13:27:20 +00003508 DIP("lha r%u,%d(r%u)\n", rD_addr, (Int)simm16, rA_addr);
3509 val = loadBE(Ity_I16, mkexpr(EA));
cerion2831b002005-11-30 19:55:22 +00003510 putIReg( rD_addr, mkSzWiden16(ty, val, True) );
cerionb85e8bb2005-02-16 08:54:33 +00003511 break;
cerion645c9302005-01-31 10:09:59 +00003512
cerion5b2325f2005-12-23 00:55:09 +00003513 case 0x2B: // lhau (Load HW Alg, Update, PPC32 p446)
cerion76de5cf2005-11-18 18:25:12 +00003514 if (rA_addr == 0 || rA_addr == rD_addr) {
cerion5b2325f2005-12-23 00:55:09 +00003515 vex_printf("dis_int_load(ppc)(lhau,rA_addr|rD_addr)\n");
cerioncb14e732005-09-09 16:38:19 +00003516 return False;
3517 }
ceriond953ebb2005-11-29 13:27:20 +00003518 DIP("lhau r%u,%d(r%u)\n", rD_addr, (Int)simm16, rA_addr);
3519 val = loadBE(Ity_I16, mkexpr(EA));
cerion2831b002005-11-30 19:55:22 +00003520 putIReg( rD_addr, mkSzWiden16(ty, val, True) );
ceriond953ebb2005-11-29 13:27:20 +00003521 putIReg( rA_addr, mkexpr(EA) );
cerioncb14e732005-09-09 16:38:19 +00003522 break;
cerionb85e8bb2005-02-16 08:54:33 +00003523
cerione9d361a2005-03-04 17:35:29 +00003524 case 0x28: // lhz (Load HW & Zero, PPC32 p450)
ceriond953ebb2005-11-29 13:27:20 +00003525 DIP("lhz r%u,%d(r%u)\n", rD_addr, (Int)simm16, rA_addr);
3526 val = loadBE(Ity_I16, mkexpr(EA));
cerion2831b002005-11-30 19:55:22 +00003527 putIReg( rD_addr, mkSzWiden16(ty, val, False) );
cerionb85e8bb2005-02-16 08:54:33 +00003528 break;
3529
cerion5b2325f2005-12-23 00:55:09 +00003530 case 0x29: // lhzu (Load HW & and Zero, Update, PPC32 p451)
cerion76de5cf2005-11-18 18:25:12 +00003531 if (rA_addr == 0 || rA_addr == rD_addr) {
cerion5b2325f2005-12-23 00:55:09 +00003532 vex_printf("dis_int_load(ppc)(lhzu,rA_addr|rD_addr)\n");
sewardj0e2cc672005-07-29 21:58:51 +00003533 return False;
3534 }
ceriond953ebb2005-11-29 13:27:20 +00003535 DIP("lhzu r%u,%d(r%u)\n", rD_addr, (Int)simm16, rA_addr);
3536 val = loadBE(Ity_I16, mkexpr(EA));
cerion2831b002005-11-30 19:55:22 +00003537 putIReg( rD_addr, mkSzWiden16(ty, val, False) );
ceriond953ebb2005-11-29 13:27:20 +00003538 putIReg( rA_addr, mkexpr(EA) );
sewardj0e2cc672005-07-29 21:58:51 +00003539 break;
cerion645c9302005-01-31 10:09:59 +00003540
cerione9d361a2005-03-04 17:35:29 +00003541 case 0x20: // lwz (Load W & Zero, PPC32 p460)
ceriond953ebb2005-11-29 13:27:20 +00003542 DIP("lwz r%u,%d(r%u)\n", rD_addr, (Int)simm16, rA_addr);
3543 val = loadBE(Ity_I32, mkexpr(EA));
cerion2831b002005-11-30 19:55:22 +00003544 putIReg( rD_addr, mkSzWiden32(ty, val, False) );
cerionb85e8bb2005-02-16 08:54:33 +00003545 break;
3546
cerion5b2325f2005-12-23 00:55:09 +00003547 case 0x21: // lwzu (Load W & Zero, Update, PPC32 p461))
cerion76de5cf2005-11-18 18:25:12 +00003548 if (rA_addr == 0 || rA_addr == rD_addr) {
cerion5b2325f2005-12-23 00:55:09 +00003549 vex_printf("dis_int_load(ppc)(lwzu,rA_addr|rD_addr)\n");
cerionb85e8bb2005-02-16 08:54:33 +00003550 return False;
3551 }
ceriond953ebb2005-11-29 13:27:20 +00003552 DIP("lwzu r%u,%d(r%u)\n", rD_addr, (Int)simm16, rA_addr);
3553 val = loadBE(Ity_I32, mkexpr(EA));
cerion2831b002005-11-30 19:55:22 +00003554 putIReg( rD_addr, mkSzWiden32(ty, val, False) );
ceriond953ebb2005-11-29 13:27:20 +00003555 putIReg( rA_addr, mkexpr(EA) );
cerionb85e8bb2005-02-16 08:54:33 +00003556 break;
3557
3558 /* X Form */
3559 case 0x1F:
3560 if (b0 != 0) {
cerion5b2325f2005-12-23 00:55:09 +00003561 vex_printf("dis_int_load(ppc)(Ox1F,b0)\n");
cerionb85e8bb2005-02-16 08:54:33 +00003562 return False;
3563 }
cerion645c9302005-01-31 10:09:59 +00003564
cerionb85e8bb2005-02-16 08:54:33 +00003565 switch (opc2) {
cerion5b2325f2005-12-23 00:55:09 +00003566 case 0x077: // lbzux (Load B & Zero, Update Indexed, PPC32 p435)
ceriond953ebb2005-11-29 13:27:20 +00003567 DIP("lbzux r%u,r%u,r%u\n", rD_addr, rA_addr, rB_addr);
cerion76de5cf2005-11-18 18:25:12 +00003568 if (rA_addr == 0 || rA_addr == rD_addr) {
cerion5b2325f2005-12-23 00:55:09 +00003569 vex_printf("dis_int_load(ppc)(lwzux,rA_addr|rD_addr)\n");
cerionb85e8bb2005-02-16 08:54:33 +00003570 return False;
3571 }
ceriond953ebb2005-11-29 13:27:20 +00003572 val = loadBE(Ity_I8, mkexpr(EA));
cerion2831b002005-11-30 19:55:22 +00003573 putIReg( rD_addr, mkSzWiden8(ty, val, False) );
ceriond953ebb2005-11-29 13:27:20 +00003574 putIReg( rA_addr, mkexpr(EA) );
cerionb85e8bb2005-02-16 08:54:33 +00003575 break;
3576
cerion5b2325f2005-12-23 00:55:09 +00003577 case 0x057: // lbzx (Load B & Zero, Indexed, PPC32 p436)
ceriond953ebb2005-11-29 13:27:20 +00003578 DIP("lbzx r%u,r%u,r%u\n", rD_addr, rA_addr, rB_addr);
3579 val = loadBE(Ity_I8, mkexpr(EA));
cerion2831b002005-11-30 19:55:22 +00003580 putIReg( rD_addr, mkSzWiden8(ty, val, False) );
cerionb85e8bb2005-02-16 08:54:33 +00003581 break;
3582
cerion5b2325f2005-12-23 00:55:09 +00003583 case 0x177: // lhaux (Load HW Alg, Update Indexed, PPC32 p447)
cerion76de5cf2005-11-18 18:25:12 +00003584 if (rA_addr == 0 || rA_addr == rD_addr) {
cerion5b2325f2005-12-23 00:55:09 +00003585 vex_printf("dis_int_load(ppc)(lhaux,rA_addr|rD_addr)\n");
cerioncb14e732005-09-09 16:38:19 +00003586 return False;
3587 }
ceriond953ebb2005-11-29 13:27:20 +00003588 DIP("lhaux r%u,r%u,r%u\n", rD_addr, rA_addr, rB_addr);
3589 val = loadBE(Ity_I16, mkexpr(EA));
cerion2831b002005-11-30 19:55:22 +00003590 putIReg( rD_addr, mkSzWiden16(ty, val, True) );
ceriond953ebb2005-11-29 13:27:20 +00003591 putIReg( rA_addr, mkexpr(EA) );
cerioncb14e732005-09-09 16:38:19 +00003592 break;
cerionb85e8bb2005-02-16 08:54:33 +00003593
cerion5b2325f2005-12-23 00:55:09 +00003594 case 0x157: // lhax (Load HW Alg, Indexed, PPC32 p448)
ceriond953ebb2005-11-29 13:27:20 +00003595 DIP("lhax r%u,r%u,r%u\n", rD_addr, rA_addr, rB_addr);
3596 val = loadBE(Ity_I16, mkexpr(EA));
cerion2831b002005-11-30 19:55:22 +00003597 putIReg( rD_addr, mkSzWiden16(ty, val, True) );
cerionb85e8bb2005-02-16 08:54:33 +00003598 break;
3599
cerion5b2325f2005-12-23 00:55:09 +00003600 case 0x137: // lhzux (Load HW & Zero, Update Indexed, PPC32 p452)
cerion76de5cf2005-11-18 18:25:12 +00003601 if (rA_addr == 0 || rA_addr == rD_addr) {
cerion5b2325f2005-12-23 00:55:09 +00003602 vex_printf("dis_int_load(ppc)(lhzux,rA_addr|rD_addr)\n");
cerionb85e8bb2005-02-16 08:54:33 +00003603 return False;
3604 }
ceriond953ebb2005-11-29 13:27:20 +00003605 DIP("lhzux r%u,r%u,r%u\n", rD_addr, rA_addr, rB_addr);
3606 val = loadBE(Ity_I16, mkexpr(EA));
cerion2831b002005-11-30 19:55:22 +00003607 putIReg( rD_addr, mkSzWiden16(ty, val, False) );
ceriond953ebb2005-11-29 13:27:20 +00003608 putIReg( rA_addr, mkexpr(EA) );
cerionb85e8bb2005-02-16 08:54:33 +00003609 break;
3610
cerion5b2325f2005-12-23 00:55:09 +00003611 case 0x117: // lhzx (Load HW & Zero, Indexed, PPC32 p453)
ceriond953ebb2005-11-29 13:27:20 +00003612 DIP("lhzx r%u,r%u,r%u\n", rD_addr, rA_addr, rB_addr);
3613 val = loadBE(Ity_I16, mkexpr(EA));
cerion2831b002005-11-30 19:55:22 +00003614 putIReg( rD_addr, mkSzWiden16(ty, val, False) );
cerionb85e8bb2005-02-16 08:54:33 +00003615 break;
cerion44997f22005-01-31 18:45:59 +00003616
cerion5b2325f2005-12-23 00:55:09 +00003617 case 0x037: // lwzux (Load W & Zero, Update Indexed, PPC32 p462)
cerion76de5cf2005-11-18 18:25:12 +00003618 if (rA_addr == 0 || rA_addr == rD_addr) {
cerion5b2325f2005-12-23 00:55:09 +00003619 vex_printf("dis_int_load(ppc)(lwzux,rA_addr|rD_addr)\n");
sewardj7787af42005-08-04 18:32:19 +00003620 return False;
3621 }
ceriond953ebb2005-11-29 13:27:20 +00003622 DIP("lwzux r%u,r%u,r%u\n", rD_addr, rA_addr, rB_addr);
3623 val = loadBE(Ity_I32, mkexpr(EA));
cerion2831b002005-11-30 19:55:22 +00003624 putIReg( rD_addr, mkSzWiden32(ty, val, False) );
ceriond953ebb2005-11-29 13:27:20 +00003625 putIReg( rA_addr, mkexpr(EA) );
sewardj7787af42005-08-04 18:32:19 +00003626 break;
cerionb85e8bb2005-02-16 08:54:33 +00003627
cerion5b2325f2005-12-23 00:55:09 +00003628 case 0x017: // lwzx (Load W & Zero, Indexed, PPC32 p463)
ceriond953ebb2005-11-29 13:27:20 +00003629 DIP("lwzx r%u,r%u,r%u\n", rD_addr, rA_addr, rB_addr);
3630 val = loadBE(Ity_I32, mkexpr(EA));
cerion2831b002005-11-30 19:55:22 +00003631 putIReg( rD_addr, mkSzWiden32(ty, val, False) );
cerionb85e8bb2005-02-16 08:54:33 +00003632 break;
cerion44997f22005-01-31 18:45:59 +00003633
cerionf0de28c2005-12-13 20:21:11 +00003634
3635 /* 64bit Loads */
cerion5b2325f2005-12-23 00:55:09 +00003636 case 0x035: // ldux (Load DWord, Update Indexed, PPC64 p475)
cerionf0de28c2005-12-13 20:21:11 +00003637 if (rA_addr == 0 || rA_addr == rD_addr) {
cerion5b2325f2005-12-23 00:55:09 +00003638 vex_printf("dis_int_load(ppc)(ldux,rA_addr|rD_addr)\n");
cerionf0de28c2005-12-13 20:21:11 +00003639 return False;
3640 }
3641 DIP("ldux r%u,r%u,r%u\n", rD_addr, rA_addr, rB_addr);
3642 putIReg( rD_addr, loadBE(Ity_I64, mkexpr(EA)) );
3643 putIReg( rA_addr, mkexpr(EA) );
3644 break;
3645
cerion5b2325f2005-12-23 00:55:09 +00003646 case 0x015: // ldx (Load DWord, Indexed, PPC64 p476)
cerionf0de28c2005-12-13 20:21:11 +00003647 DIP("ldx r%u,r%u,r%u\n", rD_addr, rA_addr, rB_addr);
3648 putIReg( rD_addr, loadBE(Ity_I64, mkexpr(EA)) );
3649 break;
3650
cerion5b2325f2005-12-23 00:55:09 +00003651 case 0x175: // lwaux (Load W Alg, Update Indexed, PPC64 p501)
cerionf0de28c2005-12-13 20:21:11 +00003652 if (rA_addr == 0 || rA_addr == rD_addr) {
cerion5b2325f2005-12-23 00:55:09 +00003653 vex_printf("dis_int_load(ppc)(lwaux,rA_addr|rD_addr)\n");
cerionf0de28c2005-12-13 20:21:11 +00003654 return False;
3655 }
3656 DIP("lwaux r%u,r%u,r%u\n", rD_addr, rA_addr, rB_addr);
cerion5b2325f2005-12-23 00:55:09 +00003657 putIReg( rD_addr,
3658 unop(Iop_32Sto64, loadBE(Ity_I32, mkexpr(EA))) );
cerionf0de28c2005-12-13 20:21:11 +00003659 putIReg( rA_addr, mkexpr(EA) );
3660 break;
3661
cerion5b2325f2005-12-23 00:55:09 +00003662 case 0x155: // lwax (Load W Alg, Indexed, PPC64 p502)
cerionf0de28c2005-12-13 20:21:11 +00003663 DIP("lwax r%u,r%u,r%u\n", rD_addr, rA_addr, rB_addr);
cerion5b2325f2005-12-23 00:55:09 +00003664 putIReg( rD_addr,
3665 unop(Iop_32Sto64, loadBE(Ity_I32, mkexpr(EA))) );
cerionf0de28c2005-12-13 20:21:11 +00003666 break;
3667
cerionb85e8bb2005-02-16 08:54:33 +00003668 default:
cerion5b2325f2005-12-23 00:55:09 +00003669 vex_printf("dis_int_load(ppc)(opc2)\n");
cerionb85e8bb2005-02-16 08:54:33 +00003670 return False;
3671 }
3672 break;
cerionf0de28c2005-12-13 20:21:11 +00003673
3674 /* DS Form - 64bit Loads */
3675 case 0x3A:
3676 switch (b1<<1 | b0) {
cerion5b2325f2005-12-23 00:55:09 +00003677 case 0x0: // ld (Load DWord, PPC64 p472)
cerionf0de28c2005-12-13 20:21:11 +00003678 DIP("ld r%u,%d(r%u)\n", rD_addr, simm16, rA_addr);
3679 putIReg( rD_addr, loadBE(Ity_I64, mkexpr(EA)) );
3680 break;
3681
cerion5b2325f2005-12-23 00:55:09 +00003682 case 0x1: // ldu (Load DWord, Update, PPC64 p474)
cerionf0de28c2005-12-13 20:21:11 +00003683 if (rA_addr == 0 || rA_addr == rD_addr) {
cerion5b2325f2005-12-23 00:55:09 +00003684 vex_printf("dis_int_load(ppc)(ldu,rA_addr|rD_addr)\n");
cerionf0de28c2005-12-13 20:21:11 +00003685 return False;
3686 }
3687 DIP("ldu r%u,%d(r%u)\n", rD_addr, simm16, rA_addr);
3688 simm16 = simm16 & ~0x3;
3689 putIReg( rD_addr, loadBE(Ity_I64, mkexpr(EA)) );
3690 putIReg( rA_addr, mkexpr(EA) );
3691 break;
3692
cerion5b2325f2005-12-23 00:55:09 +00003693 case 0x2: // lwa (Load Word Alg, PPC64 p499)
cerionf0de28c2005-12-13 20:21:11 +00003694 DIP("lwa r%u,%d(r%u)\n", rD_addr, simm16, rA_addr);
cerion5b2325f2005-12-23 00:55:09 +00003695 putIReg( rD_addr,
3696 unop(Iop_32Sto64, loadBE(Ity_I32, mkexpr(EA))) );
cerionf0de28c2005-12-13 20:21:11 +00003697 break;
3698
3699 default:
cerion5b2325f2005-12-23 00:55:09 +00003700 vex_printf("dis_int_load(ppc)(0x3A, opc2)\n");
cerionf0de28c2005-12-13 20:21:11 +00003701 return False;
3702 }
3703 break;
3704
cerionb85e8bb2005-02-16 08:54:33 +00003705 default:
cerion5b2325f2005-12-23 00:55:09 +00003706 vex_printf("dis_int_load(ppc)(opc1)\n");
cerionb85e8bb2005-02-16 08:54:33 +00003707 return False;
3708 }
3709 return True;
cerion7aa4bbc2005-01-29 09:32:07 +00003710}
3711
3712
3713
cerion3d870a32005-03-18 12:23:33 +00003714/*
3715 Integer Store Instructions
3716*/
ceriond23be4e2005-01-31 07:23:07 +00003717static Bool dis_int_store ( UInt theInstr )
3718{
cerionf0de28c2005-12-13 20:21:11 +00003719 /* D-Form, X-Form, DS-Form */
cerionedf7fc52005-11-18 20:57:41 +00003720 UChar opc1 = ifieldOPC(theInstr);
cerion76de5cf2005-11-18 18:25:12 +00003721 UInt rS_addr = ifieldRegDS(theInstr);
3722 UInt rA_addr = ifieldRegA(theInstr);
ceriond953ebb2005-11-29 13:27:20 +00003723 UInt uimm16 = ifieldUIMM16(theInstr);
cerion76de5cf2005-11-18 18:25:12 +00003724 UInt rB_addr = ifieldRegB(theInstr);
3725 UInt opc2 = ifieldOPClo10(theInstr);
cerionf0de28c2005-12-13 20:21:11 +00003726 UChar b1 = ifieldBIT1(theInstr);
cerion76de5cf2005-11-18 18:25:12 +00003727 UChar b0 = ifieldBIT0(theInstr);
3728
ceriond953ebb2005-11-29 13:27:20 +00003729 Int simm16 = extend_s_16to32(uimm16);
3730 IRType ty = mode64 ? Ity_I64 : Ity_I32;
3731 IRTemp rS = newTemp(ty);
3732 IRTemp rB = newTemp(ty);
3733 IRTemp EA = newTemp(ty);
cerionb85e8bb2005-02-16 08:54:33 +00003734
cerion76de5cf2005-11-18 18:25:12 +00003735 assign( rB, getIReg(rB_addr) );
3736 assign( rS, getIReg(rS_addr) );
cerionb85e8bb2005-02-16 08:54:33 +00003737
cerionf0de28c2005-12-13 20:21:11 +00003738 switch (opc1) {
3739 case 0x1F: // register offset
ceriond953ebb2005-11-29 13:27:20 +00003740 assign( EA, ea_rAor0_idxd( rA_addr, rB_addr ) );
cerionf0de28c2005-12-13 20:21:11 +00003741 break;
3742 case 0x3E: // immediate offset: 64bit
3743 simm16 = simm16 & 0xFFFFFFFC;
3744 default: // immediate offset
3745 assign( EA, ea_rAor0_simm( rA_addr, simm16 ) );
3746 break;
ceriond953ebb2005-11-29 13:27:20 +00003747 }
3748
cerionb85e8bb2005-02-16 08:54:33 +00003749 switch (opc1) {
sewardjafe85832005-09-09 10:25:39 +00003750 case 0x26: // stb (Store B, PPC32 p509)
cerion76de5cf2005-11-18 18:25:12 +00003751 DIP("stb r%u,%d(r%u)\n", rS_addr, simm16, rA_addr);
cerion2831b002005-11-30 19:55:22 +00003752 storeBE( mkexpr(EA), mkSzNarrow8(ty, mkexpr(rS)) );
sewardjafe85832005-09-09 10:25:39 +00003753 break;
sewardjb51f0f42005-07-18 11:38:02 +00003754
cerion5b2325f2005-12-23 00:55:09 +00003755 case 0x27: // stbu (Store B, Update, PPC32 p510)
cerion76de5cf2005-11-18 18:25:12 +00003756 if (rA_addr == 0 ) {
cerion5b2325f2005-12-23 00:55:09 +00003757 vex_printf("dis_int_store(ppc)(stbu,rA_addr)\n");
cerionb85e8bb2005-02-16 08:54:33 +00003758 return False;
3759 }
cerion76de5cf2005-11-18 18:25:12 +00003760 DIP("stbu r%u,%d(r%u)\n", rS_addr, simm16, rA_addr);
ceriond953ebb2005-11-29 13:27:20 +00003761 putIReg( rA_addr, mkexpr(EA) );
cerion2831b002005-11-30 19:55:22 +00003762 storeBE( mkexpr(EA), mkSzNarrow8(ty, mkexpr(rS)) );
cerionb85e8bb2005-02-16 08:54:33 +00003763 break;
ceriond23be4e2005-01-31 07:23:07 +00003764
cerione9d361a2005-03-04 17:35:29 +00003765 case 0x2C: // sth (Store HW, PPC32 p522)
cerion76de5cf2005-11-18 18:25:12 +00003766 DIP("sth r%u,%d(r%u)\n", rS_addr, simm16, rA_addr);
cerion2831b002005-11-30 19:55:22 +00003767 storeBE( mkexpr(EA), mkSzNarrow16(ty, mkexpr(rS)) );
cerionb85e8bb2005-02-16 08:54:33 +00003768 break;
3769
cerion5b2325f2005-12-23 00:55:09 +00003770 case 0x2D: // sthu (Store HW, Update, PPC32 p524)
cerion76de5cf2005-11-18 18:25:12 +00003771 if (rA_addr == 0) {
cerion5b2325f2005-12-23 00:55:09 +00003772 vex_printf("dis_int_store(ppc)(sthu,rA_addr)\n");
cerionb85e8bb2005-02-16 08:54:33 +00003773 return False;
3774 }
cerion76de5cf2005-11-18 18:25:12 +00003775 DIP("sthu r%u,%d(r%u)\n", rS_addr, simm16, rA_addr);
ceriond953ebb2005-11-29 13:27:20 +00003776 putIReg( rA_addr, mkexpr(EA) );
cerion2831b002005-11-30 19:55:22 +00003777 storeBE( mkexpr(EA), mkSzNarrow16(ty, mkexpr(rS)) );
cerionb85e8bb2005-02-16 08:54:33 +00003778 break;
ceriond23be4e2005-01-31 07:23:07 +00003779
cerione9d361a2005-03-04 17:35:29 +00003780 case 0x24: // stw (Store W, PPC32 p530)
cerion76de5cf2005-11-18 18:25:12 +00003781 DIP("stw r%u,%d(r%u)\n", rS_addr, simm16, rA_addr);
cerion2831b002005-11-30 19:55:22 +00003782 storeBE( mkexpr(EA), mkSzNarrow32(ty, mkexpr(rS)) );
cerionb85e8bb2005-02-16 08:54:33 +00003783 break;
ceriond23be4e2005-01-31 07:23:07 +00003784
cerion5b2325f2005-12-23 00:55:09 +00003785 case 0x25: // stwu (Store W, Update, PPC32 p534)
cerion76de5cf2005-11-18 18:25:12 +00003786 if (rA_addr == 0) {
cerion5b2325f2005-12-23 00:55:09 +00003787 vex_printf("dis_int_store(ppc)(stwu,rA_addr)\n");
cerionb85e8bb2005-02-16 08:54:33 +00003788 return False;
3789 }
cerion76de5cf2005-11-18 18:25:12 +00003790 DIP("stwu r%u,%d(r%u)\n", rS_addr, simm16, rA_addr);
ceriond953ebb2005-11-29 13:27:20 +00003791 putIReg( rA_addr, mkexpr(EA) );
cerion2831b002005-11-30 19:55:22 +00003792 storeBE( mkexpr(EA), mkSzNarrow32(ty, mkexpr(rS)) );
cerionb85e8bb2005-02-16 08:54:33 +00003793 break;
3794
cerionf0de28c2005-12-13 20:21:11 +00003795 /* X Form : all these use EA_indexed */
cerionb85e8bb2005-02-16 08:54:33 +00003796 case 0x1F:
3797 if (b0 != 0) {
cerion5b2325f2005-12-23 00:55:09 +00003798 vex_printf("dis_int_store(ppc)(0x1F,b0)\n");
cerionb85e8bb2005-02-16 08:54:33 +00003799 return False;
3800 }
cerion44997f22005-01-31 18:45:59 +00003801
cerionb85e8bb2005-02-16 08:54:33 +00003802 switch (opc2) {
cerion5b2325f2005-12-23 00:55:09 +00003803 case 0x0F7: // stbux (Store B, Update Indexed, PPC32 p511)
cerion76de5cf2005-11-18 18:25:12 +00003804 if (rA_addr == 0) {
cerion5b2325f2005-12-23 00:55:09 +00003805 vex_printf("dis_int_store(ppc)(stbux,rA_addr)\n");
cerionb85e8bb2005-02-16 08:54:33 +00003806 return False;
3807 }
cerion76de5cf2005-11-18 18:25:12 +00003808 DIP("stbux r%u,r%u,r%u\n", rS_addr, rA_addr, rB_addr);
ceriond953ebb2005-11-29 13:27:20 +00003809 putIReg( rA_addr, mkexpr(EA) );
cerion2831b002005-11-30 19:55:22 +00003810 storeBE( mkexpr(EA), mkSzNarrow8(ty, mkexpr(rS)) );
cerionb85e8bb2005-02-16 08:54:33 +00003811 break;
3812
cerione9d361a2005-03-04 17:35:29 +00003813 case 0x0D7: // stbx (Store B Indexed, PPC32 p512)
cerion76de5cf2005-11-18 18:25:12 +00003814 DIP("stbx r%u,r%u,r%u\n", rS_addr, rA_addr, rB_addr);
cerion2831b002005-11-30 19:55:22 +00003815 storeBE( mkexpr(EA), mkSzNarrow8(ty, mkexpr(rS)) );
cerionb85e8bb2005-02-16 08:54:33 +00003816 break;
3817
cerion5b2325f2005-12-23 00:55:09 +00003818 case 0x1B7: // sthux (Store HW, Update Indexed, PPC32 p525)
cerion76de5cf2005-11-18 18:25:12 +00003819 if (rA_addr == 0) {
cerion5b2325f2005-12-23 00:55:09 +00003820 vex_printf("dis_int_store(ppc)(sthux,rA_addr)\n");
cerioncb14e732005-09-09 16:38:19 +00003821 return False;
3822 }
cerion76de5cf2005-11-18 18:25:12 +00003823 DIP("sthux r%u,r%u,r%u\n", rS_addr, rA_addr, rB_addr);
ceriond953ebb2005-11-29 13:27:20 +00003824 putIReg( rA_addr, mkexpr(EA) );
cerion2831b002005-11-30 19:55:22 +00003825 storeBE( mkexpr(EA), mkSzNarrow16(ty, mkexpr(rS)) );
cerioncb14e732005-09-09 16:38:19 +00003826 break;
cerionb85e8bb2005-02-16 08:54:33 +00003827
cerione9d361a2005-03-04 17:35:29 +00003828 case 0x197: // sthx (Store HW Indexed, PPC32 p526)
cerion76de5cf2005-11-18 18:25:12 +00003829 DIP("sthx r%u,r%u,r%u\n", rS_addr, rA_addr, rB_addr);
cerion2831b002005-11-30 19:55:22 +00003830 storeBE( mkexpr(EA), mkSzNarrow16(ty, mkexpr(rS)) );
cerionb85e8bb2005-02-16 08:54:33 +00003831 break;
3832
cerion5b2325f2005-12-23 00:55:09 +00003833 case 0x0B7: // stwux (Store W, Update Indexed, PPC32 p535)
cerion76de5cf2005-11-18 18:25:12 +00003834 if (rA_addr == 0) {
cerion5b2325f2005-12-23 00:55:09 +00003835 vex_printf("dis_int_store(ppc)(stwux,rA_addr)\n");
cerionb85e8bb2005-02-16 08:54:33 +00003836 return False;
3837 }
cerion76de5cf2005-11-18 18:25:12 +00003838 DIP("stwux r%u,r%u,r%u\n", rS_addr, rA_addr, rB_addr);
ceriond953ebb2005-11-29 13:27:20 +00003839 putIReg( rA_addr, mkexpr(EA) );
cerion2831b002005-11-30 19:55:22 +00003840 storeBE( mkexpr(EA), mkSzNarrow32(ty, mkexpr(rS)) );
cerionb85e8bb2005-02-16 08:54:33 +00003841 break;
cerion44997f22005-01-31 18:45:59 +00003842
cerione9d361a2005-03-04 17:35:29 +00003843 case 0x097: // stwx (Store W Indexed, PPC32 p536)
cerion76de5cf2005-11-18 18:25:12 +00003844 DIP("stwx r%u,r%u,r%u\n", rS_addr, rA_addr, rB_addr);
cerion2831b002005-11-30 19:55:22 +00003845 storeBE( mkexpr(EA), mkSzNarrow32(ty, mkexpr(rS)) );
cerionb85e8bb2005-02-16 08:54:33 +00003846 break;
3847
cerionf0de28c2005-12-13 20:21:11 +00003848
3849 /* 64bit Stores */
cerion5b2325f2005-12-23 00:55:09 +00003850 case 0x0B5: // stdux (Store DWord, Update Indexed, PPC64 p584)
cerionf0de28c2005-12-13 20:21:11 +00003851 if (rA_addr == 0) {
cerion5b2325f2005-12-23 00:55:09 +00003852 vex_printf("dis_int_store(ppc)(stdux,rA_addr)\n");
cerionf0de28c2005-12-13 20:21:11 +00003853 return False;
3854 }
3855 DIP("stdux r%u,r%u,r%u\n", rS_addr, rA_addr, rB_addr);
3856 putIReg( rA_addr, mkexpr(EA) );
3857 storeBE( mkexpr(EA), mkexpr(rS) );
3858 break;
3859
cerion5b2325f2005-12-23 00:55:09 +00003860 case 0x095: // stdx (Store DWord Indexed, PPC64 p585)
cerionf0de28c2005-12-13 20:21:11 +00003861 DIP("stdx r%u,r%u,r%u\n", rS_addr, rA_addr, rB_addr);
3862 storeBE( mkexpr(EA), mkexpr(rS) );
3863 break;
3864
cerionb85e8bb2005-02-16 08:54:33 +00003865 default:
cerion5b2325f2005-12-23 00:55:09 +00003866 vex_printf("dis_int_store(ppc)(opc2)\n");
cerionb85e8bb2005-02-16 08:54:33 +00003867 return False;
3868 }
3869 break;
cerionf0de28c2005-12-13 20:21:11 +00003870
3871 /* DS Form - 64bit Stores */
3872 case 0x3E:
3873 switch (b1<<1 | b0) {
cerion5b2325f2005-12-23 00:55:09 +00003874 case 0x0: // std (Store DWord, PPC64 p580)
cerionf0de28c2005-12-13 20:21:11 +00003875 DIP("std r%u,%d(r%u)\n", rS_addr, simm16, rA_addr);
3876 storeBE( mkexpr(EA), mkexpr(rS) );
3877 break;
3878
cerion5b2325f2005-12-23 00:55:09 +00003879 case 0x1: // stdu (Store DWord, Update, PPC64 p583)
cerionf0de28c2005-12-13 20:21:11 +00003880 DIP("stdu r%u,%d(r%u)\n", rS_addr, simm16, rA_addr);
3881 putIReg( rA_addr, mkexpr(EA) );
3882 storeBE( mkexpr(EA), mkexpr(rS) );
3883 break;
3884
3885 default:
cerion5b2325f2005-12-23 00:55:09 +00003886 vex_printf("dis_int_load(ppc)(0x3A, opc2)\n");
cerionf0de28c2005-12-13 20:21:11 +00003887 return False;
3888 }
3889 break;
3890
cerionb85e8bb2005-02-16 08:54:33 +00003891 default:
cerion5b2325f2005-12-23 00:55:09 +00003892 vex_printf("dis_int_store(ppc)(opc1)\n");
cerionb85e8bb2005-02-16 08:54:33 +00003893 return False;
3894 }
3895 return True;
ceriond23be4e2005-01-31 07:23:07 +00003896}
3897
3898
3899
sewardj7787af42005-08-04 18:32:19 +00003900/*
3901 Integer Load/Store Multiple Instructions
3902*/
3903static Bool dis_int_ldst_mult ( UInt theInstr )
3904{
3905 /* D-Form */
cerion76de5cf2005-11-18 18:25:12 +00003906 UChar opc1 = ifieldOPC(theInstr);
3907 UChar rD_addr = ifieldRegDS(theInstr);
3908 UChar rS_addr = rD_addr;
3909 UChar rA_addr = ifieldRegA(theInstr);
cerion2831b002005-11-30 19:55:22 +00003910 UInt uimm16 = ifieldUIMM16(theInstr);
cerionedf7fc52005-11-18 20:57:41 +00003911
ceriond953ebb2005-11-29 13:27:20 +00003912 Int simm16 = extend_s_16to32(uimm16);
3913 IRType ty = mode64 ? Ity_I64 : Ity_I32;
3914 IRTemp EA = newTemp(ty);
3915 UInt r = 0;
3916 UInt ea_off = 0;
sewardj7787af42005-08-04 18:32:19 +00003917 IRExpr* irx_addr;
cerionedf7fc52005-11-18 20:57:41 +00003918
ceriond953ebb2005-11-29 13:27:20 +00003919 assign( EA, ea_rAor0_simm( rA_addr, simm16 ) );
3920
sewardj7787af42005-08-04 18:32:19 +00003921 switch (opc1) {
ceriond953ebb2005-11-29 13:27:20 +00003922 case 0x2E: // lmw (Load Multiple Word, PPC32 p454)
3923 if (rA_addr >= rD_addr) {
cerion5b2325f2005-12-23 00:55:09 +00003924 vex_printf("dis_int_ldst_mult(ppc)(lmw,rA_addr)\n");
sewardj7787af42005-08-04 18:32:19 +00003925 return False;
ceriond953ebb2005-11-29 13:27:20 +00003926 }
3927 DIP("lmw r%u,%d(r%u)\n", rD_addr, simm16, rA_addr);
3928 for (r = rD_addr; r <= 31; r++) {
3929 irx_addr = binop(Iop_Add32, mkexpr(EA), mkU32(ea_off));
cerion5b2325f2005-12-23 00:55:09 +00003930 putIReg( r, mkSzWiden32(ty, loadBE(Ity_I32, irx_addr ),
3931 False) );
ceriond953ebb2005-11-29 13:27:20 +00003932 ea_off += 4;
3933 }
3934 break;
3935
3936 case 0x2F: // stmw (Store Multiple Word, PPC32 p527)
3937 DIP("stmw r%u,%d(r%u)\n", rS_addr, simm16, rA_addr);
3938 for (r = rS_addr; r <= 31; r++) {
3939 irx_addr = binop(Iop_Add32, mkexpr(EA), mkU32(ea_off));
cerion2831b002005-11-30 19:55:22 +00003940 storeBE( irx_addr, mkSzNarrow32(ty, getIReg(r)) );
ceriond953ebb2005-11-29 13:27:20 +00003941 ea_off += 4;
3942 }
3943 break;
3944
3945 default:
cerion5b2325f2005-12-23 00:55:09 +00003946 vex_printf("dis_int_ldst_mult(ppc)(opc1)\n");
ceriond953ebb2005-11-29 13:27:20 +00003947 return False;
sewardj7787af42005-08-04 18:32:19 +00003948 }
3949 return True;
3950}
3951
3952
3953
sewardj87e651f2005-09-09 08:31:18 +00003954/*
3955 Integer Load/Store String Instructions
3956*/
3957static
3958void generate_lsw_sequence ( IRTemp tNBytes, // # bytes, :: Ity_I32
3959 IRTemp EA, // EA
3960 Int rD, // first dst register
ceriond953ebb2005-11-29 13:27:20 +00003961 Int maxBytes ) // 32 or 128
sewardj87e651f2005-09-09 08:31:18 +00003962{
3963 Int i, shift = 24;
3964 IRExpr* e_nbytes = mkexpr(tNBytes);
ceriond953ebb2005-11-29 13:27:20 +00003965 IRExpr* e_EA = mkexpr(EA);
3966 IRType ty = mode64 ? Ity_I64 : Ity_I32;
sewardj87e651f2005-09-09 08:31:18 +00003967
sewardj5876fa12005-09-09 09:35:29 +00003968 vassert(rD >= 0 && rD < 32);
sewardj87e651f2005-09-09 08:31:18 +00003969 rD--; if (rD < 0) rD = 31;
3970
3971 for (i = 0; i < maxBytes; i++) {
sewardj87e651f2005-09-09 08:31:18 +00003972 /* if (nBytes < (i+1)) goto NIA; */
3973 stmt( IRStmt_Exit( binop(Iop_CmpLT32U, e_nbytes, mkU32(i+1)),
3974 Ijk_Boring,
cerion2831b002005-11-30 19:55:22 +00003975 mkSzConst( ty, nextInsnAddr()) ));
sewardj87e651f2005-09-09 08:31:18 +00003976 /* when crossing into a new dest register, set it to zero. */
3977 if ((i % 4) == 0) {
3978 rD++; if (rD == 32) rD = 0;
cerion2831b002005-11-30 19:55:22 +00003979 putIReg(rD, mkSzImm(ty, 0));
sewardj87e651f2005-09-09 08:31:18 +00003980 shift = 24;
3981 }
3982 /* rD |= (8Uto32(*(EA+i))) << shift */
3983 vassert(shift == 0 || shift == 8 || shift == 16 || shift == 24);
sewardj2ef8a372006-01-28 17:07:19 +00003984 putIReg(
3985 rD,
3986 mkSzWiden32(
3987 ty,
3988 binop(
3989 Iop_Or32,
3990 mkSzNarrow32(ty, getIReg(rD)),
3991 binop(
3992 Iop_Shl32,
3993 unop(
3994 Iop_8Uto32,
3995 loadBE(Ity_I8,
3996 binop(mkSzOp(ty,Iop_Add8), e_EA, mkSzImm(ty,i)))
3997 ),
3998 mkU8(toUChar(shift))
3999 )
4000 ),
4001 /*Signed*/False
4002 )
4003 );
sewardj87e651f2005-09-09 08:31:18 +00004004 shift -= 8;
4005 }
4006}
4007
sewardj5876fa12005-09-09 09:35:29 +00004008static
4009void generate_stsw_sequence ( IRTemp tNBytes, // # bytes, :: Ity_I32
4010 IRTemp EA, // EA
4011 Int rS, // first src register
ceriond953ebb2005-11-29 13:27:20 +00004012 Int maxBytes ) // 32 or 128
sewardj5876fa12005-09-09 09:35:29 +00004013{
4014 Int i, shift = 24;
4015 IRExpr* e_nbytes = mkexpr(tNBytes);
ceriond953ebb2005-11-29 13:27:20 +00004016 IRExpr* e_EA = mkexpr(EA);
4017 IRType ty = mode64 ? Ity_I64 : Ity_I32;
sewardj5876fa12005-09-09 09:35:29 +00004018
4019 vassert(rS >= 0 && rS < 32);
4020 rS--; if (rS < 0) rS = 31;
4021
4022 for (i = 0; i < maxBytes; i++) {
4023 /* if (nBytes < (i+1)) goto NIA; */
4024 stmt( IRStmt_Exit( binop(Iop_CmpLT32U, e_nbytes, mkU32(i+1)),
4025 Ijk_Boring,
cerion2831b002005-11-30 19:55:22 +00004026 mkSzConst( ty, nextInsnAddr() ) ));
sewardj5876fa12005-09-09 09:35:29 +00004027 /* check for crossing into a new src register. */
4028 if ((i % 4) == 0) {
4029 rS++; if (rS == 32) rS = 0;
4030 shift = 24;
4031 }
4032 /* *(EA+i) = 32to8(rS >> shift) */
4033 vassert(shift == 0 || shift == 8 || shift == 16 || shift == 24);
4034 storeBE(
cerion2831b002005-11-30 19:55:22 +00004035 binop(mkSzOp(ty,Iop_Add8), e_EA, mkSzImm(ty,i)),
sewardj5876fa12005-09-09 09:35:29 +00004036 unop(Iop_32to8,
cerion2831b002005-11-30 19:55:22 +00004037 binop(Iop_Shr32,
4038 mkSzNarrow32(ty, getIReg(rS)),
4039 mkU8(toUChar(shift))))
sewardj5876fa12005-09-09 09:35:29 +00004040 );
4041 shift -= 8;
4042 }
4043}
4044
sewardj87e651f2005-09-09 08:31:18 +00004045static Bool dis_int_ldst_str ( UInt theInstr, /*OUT*/Bool* stopHere )
4046{
4047 /* X-Form */
cerion76de5cf2005-11-18 18:25:12 +00004048 UChar opc1 = ifieldOPC(theInstr);
4049 UChar rD_addr = ifieldRegDS(theInstr);
4050 UChar rS_addr = rD_addr;
4051 UChar rA_addr = ifieldRegA(theInstr);
4052 UChar rB_addr = ifieldRegB(theInstr);
4053 UChar NumBytes = rB_addr;
4054 UInt opc2 = ifieldOPClo10(theInstr);
4055 UChar b0 = ifieldBIT0(theInstr);
cerionedf7fc52005-11-18 20:57:41 +00004056
ceriond953ebb2005-11-29 13:27:20 +00004057 IRType ty = mode64 ? Ity_I64 : Ity_I32;
4058 IRTemp t_EA = newTemp(ty);
sewardj87e651f2005-09-09 08:31:18 +00004059 IRTemp t_nbytes = IRTemp_INVALID;
cerionedf7fc52005-11-18 20:57:41 +00004060
sewardj87e651f2005-09-09 08:31:18 +00004061 *stopHere = False;
cerionedf7fc52005-11-18 20:57:41 +00004062
sewardj87e651f2005-09-09 08:31:18 +00004063 if (opc1 != 0x1F || b0 != 0) {
cerion5b2325f2005-12-23 00:55:09 +00004064 vex_printf("dis_int_ldst_str(ppc)(opc1)\n");
sewardj87e651f2005-09-09 08:31:18 +00004065 return False;
4066 }
4067
4068 switch (opc2) {
4069 case 0x255: // lswi (Load String Word Immediate, PPC32 p455)
4070 /* NB: does not reject the case where RA is in the range of
4071 registers to be loaded. It should. */
ceriond953ebb2005-11-29 13:27:20 +00004072 DIP("lswi r%u,r%u,%d\n", rD_addr, rA_addr, NumBytes);
4073 assign( t_EA, ea_rAor0(rA_addr) );
sewardj2ef8a372006-01-28 17:07:19 +00004074 if (NumBytes == 8 && !mode64) {
sewardj87e651f2005-09-09 08:31:18 +00004075 /* Special case hack */
cerion76de5cf2005-11-18 18:25:12 +00004076 /* rD = Mem[EA]; (rD+1)%32 = Mem[EA+4] */
4077 putIReg( rD_addr,
sewardj87e651f2005-09-09 08:31:18 +00004078 loadBE(Ity_I32, mkexpr(t_EA)) );
cerion76de5cf2005-11-18 18:25:12 +00004079 putIReg( (rD_addr+1) % 32,
ceriond953ebb2005-11-29 13:27:20 +00004080 loadBE(Ity_I32,
4081 binop(Iop_Add32, mkexpr(t_EA), mkU32(4))) );
sewardj87e651f2005-09-09 08:31:18 +00004082 } else {
4083 t_nbytes = newTemp(Ity_I32);
cerion76de5cf2005-11-18 18:25:12 +00004084 assign( t_nbytes, mkU32(NumBytes==0 ? 32 : NumBytes) );
ceriond953ebb2005-11-29 13:27:20 +00004085 generate_lsw_sequence( t_nbytes, t_EA, rD_addr, 32 );
sewardj87e651f2005-09-09 08:31:18 +00004086 *stopHere = True;
4087 }
4088 return True;
4089
4090 case 0x215: // lswx (Load String Word Indexed, PPC32 p456)
4091 /* NB: does not reject the case where RA is in the range of
4092 registers to be loaded. It should. Although considering
4093 that that can only be detected at run time, it's not easy to
4094 do so. */
cerion76de5cf2005-11-18 18:25:12 +00004095 if (rD_addr == rA_addr || rD_addr == rB_addr)
sewardj87e651f2005-09-09 08:31:18 +00004096 return False;
cerion76de5cf2005-11-18 18:25:12 +00004097 if (rD_addr == 0 && rA_addr == 0)
sewardj87e651f2005-09-09 08:31:18 +00004098 return False;
ceriond953ebb2005-11-29 13:27:20 +00004099 DIP("lswx r%u,r%u,r%u\n", rD_addr, rA_addr, rB_addr);
sewardj87e651f2005-09-09 08:31:18 +00004100 t_nbytes = newTemp(Ity_I32);
ceriond953ebb2005-11-29 13:27:20 +00004101 assign( t_EA, ea_rAor0_idxd(rA_addr,rB_addr) );
cerionedf7fc52005-11-18 20:57:41 +00004102 assign( t_nbytes, unop( Iop_8Uto32, getXER_BC() ) );
ceriond953ebb2005-11-29 13:27:20 +00004103 generate_lsw_sequence( t_nbytes, t_EA, rD_addr, 128 );
sewardj87e651f2005-09-09 08:31:18 +00004104 *stopHere = True;
4105 return True;
4106
sewardj5876fa12005-09-09 09:35:29 +00004107 case 0x2D5: // stswi (Store String Word Immediate, PPC32 p528)
ceriond953ebb2005-11-29 13:27:20 +00004108 DIP("stswi r%u,r%u,%d\n", rS_addr, rA_addr, NumBytes);
4109 assign( t_EA, ea_rAor0(rA_addr) );
sewardj2ef8a372006-01-28 17:07:19 +00004110 if (NumBytes == 8 && !mode64) {
sewardj5876fa12005-09-09 09:35:29 +00004111 /* Special case hack */
cerion76de5cf2005-11-18 18:25:12 +00004112 /* Mem[EA] = rD; Mem[EA+4] = (rD+1)%32 */
4113 storeBE( mkexpr(t_EA),
4114 getIReg(rD_addr) );
4115 storeBE( binop(Iop_Add32, mkexpr(t_EA), mkU32(4)),
4116 getIReg((rD_addr+1) % 32) );
sewardj5876fa12005-09-09 09:35:29 +00004117 } else {
4118 t_nbytes = newTemp(Ity_I32);
cerion76de5cf2005-11-18 18:25:12 +00004119 assign( t_nbytes, mkU32(NumBytes==0 ? 32 : NumBytes) );
ceriond953ebb2005-11-29 13:27:20 +00004120 generate_stsw_sequence( t_nbytes, t_EA, rD_addr, 32 );
sewardj5876fa12005-09-09 09:35:29 +00004121 *stopHere = True;
4122 }
4123 return True;
4124
sewardj5876fa12005-09-09 09:35:29 +00004125 case 0x295: // stswx (Store String Word Indexed, PPC32 p529)
ceriond953ebb2005-11-29 13:27:20 +00004126 DIP("stswx r%u,r%u,r%u\n", rS_addr, rA_addr, rB_addr);
sewardj5876fa12005-09-09 09:35:29 +00004127 t_nbytes = newTemp(Ity_I32);
ceriond953ebb2005-11-29 13:27:20 +00004128 assign( t_EA, ea_rAor0_idxd(rA_addr,rB_addr) );
cerionedf7fc52005-11-18 20:57:41 +00004129 assign( t_nbytes, unop( Iop_8Uto32, getXER_BC() ) );
ceriond953ebb2005-11-29 13:27:20 +00004130 generate_stsw_sequence( t_nbytes, t_EA, rS_addr, 128 );
sewardj5876fa12005-09-09 09:35:29 +00004131 *stopHere = True;
4132 return True;
sewardj87e651f2005-09-09 08:31:18 +00004133
4134 default:
cerion5b2325f2005-12-23 00:55:09 +00004135 vex_printf("dis_int_ldst_str(ppc)(opc2)\n");
sewardj87e651f2005-09-09 08:31:18 +00004136 return False;
4137 }
4138 return True;
4139}
4140
cerion094d1392005-06-20 13:45:57 +00004141
sewardjb51f0f42005-07-18 11:38:02 +00004142/* ------------------------------------------------------------------
4143 Integer Branch Instructions
4144 ------------------------------------------------------------------ */
cerion645c9302005-01-31 10:09:59 +00004145
cerion45552a92005-02-03 18:20:22 +00004146/*
4147 Branch helper function
4148 ok = BO[2] | ((CTR[0] != 0) ^ BO[1])
sewardjb51f0f42005-07-18 11:38:02 +00004149 Returns an I32 which is 0x00000000 if the ctr condition failed
4150 and 0xFFFFFFFF otherwise.
cerion45552a92005-02-03 18:20:22 +00004151*/
sewardjb51f0f42005-07-18 11:38:02 +00004152static IRExpr* /* :: Ity_I32 */ branch_ctr_ok( UInt BO )
cerion45552a92005-02-03 18:20:22 +00004153{
ceriond953ebb2005-11-29 13:27:20 +00004154 IRType ty = mode64 ? Ity_I64 : Ity_I32;
sewardjb51f0f42005-07-18 11:38:02 +00004155 IRTemp ok = newTemp(Ity_I32);
cerioned623db2005-06-20 12:42:04 +00004156
cerionf0de28c2005-12-13 20:21:11 +00004157 if ((BO >> 2) & 1) { // independent of ctr
sewardjb51f0f42005-07-18 11:38:02 +00004158 assign( ok, mkU32(0xFFFFFFFF) );
cerionb85e8bb2005-02-16 08:54:33 +00004159 } else {
cerionf0de28c2005-12-13 20:21:11 +00004160 if ((BO >> 1) & 1) { // ctr == 0 ?
ceriond953ebb2005-11-29 13:27:20 +00004161 assign( ok, unop( Iop_1Sto32,
cerion4e2c2b32006-01-02 13:35:51 +00004162 binop( mkSzOp(ty, Iop_CmpEQ8),
4163 getGST( PPC_GST_CTR ),
4164 mkSzImm(ty,0))) );
cerionf0de28c2005-12-13 20:21:11 +00004165 } else { // ctr != 0 ?
sewardjb51f0f42005-07-18 11:38:02 +00004166 assign( ok, unop( Iop_1Sto32,
cerion4e2c2b32006-01-02 13:35:51 +00004167 binop( mkSzOp(ty, Iop_CmpNE8),
4168 getGST( PPC_GST_CTR ),
4169 mkSzImm(ty,0))) );
cerionb85e8bb2005-02-16 08:54:33 +00004170 }
4171 }
4172 return mkexpr(ok);
cerion45552a92005-02-03 18:20:22 +00004173}
4174
sewardjb51f0f42005-07-18 11:38:02 +00004175
cerion45552a92005-02-03 18:20:22 +00004176/*
sewardjb51f0f42005-07-18 11:38:02 +00004177 Branch helper function cond_ok = BO[4] | (CR[BI] == BO[3])
4178 Returns an I32 which is either 0 if the condition failed or
4179 some arbitrary nonzero value otherwise. */
4180
4181static IRExpr* /* :: Ity_I32 */ branch_cond_ok( UInt BO, UInt BI )
cerion45552a92005-02-03 18:20:22 +00004182{
sewardjb51f0f42005-07-18 11:38:02 +00004183 Int where;
4184 IRTemp res = newTemp(Ity_I32);
cerionb85e8bb2005-02-16 08:54:33 +00004185 IRTemp cr_bi = newTemp(Ity_I32);
4186
sewardjb51f0f42005-07-18 11:38:02 +00004187 if ((BO >> 4) & 1) {
4188 assign( res, mkU32(1) );
cerionb85e8bb2005-02-16 08:54:33 +00004189 } else {
sewardjb51f0f42005-07-18 11:38:02 +00004190 // ok = (CR[BI] == BO[3]) Note, the following relies on
4191 // getCRbit_anywhere returning a value which
4192 // is either zero or has exactly 1 bit set.
4193 assign( cr_bi, getCRbit_anywhere( BI, &where ) );
cerione9d361a2005-03-04 17:35:29 +00004194
4195 if ((BO >> 3) & 1) {
sewardjb51f0f42005-07-18 11:38:02 +00004196 /* We can use cr_bi as-is. */
4197 assign( res, mkexpr(cr_bi) );
cerione9d361a2005-03-04 17:35:29 +00004198 } else {
sewardjb51f0f42005-07-18 11:38:02 +00004199 /* We have to invert the sense of the information held in
4200 cr_bi. For that we need to know which bit
cerion76de5cf2005-11-18 18:25:12 +00004201 getCRbit_anywhere regards as significant. */
cerion5b2325f2005-12-23 00:55:09 +00004202 assign( res, binop(Iop_Xor32, mkexpr(cr_bi),
4203 mkU32(1<<where)) );
cerionb85e8bb2005-02-16 08:54:33 +00004204 }
4205 }
sewardjb51f0f42005-07-18 11:38:02 +00004206 return mkexpr(res);
cerion45552a92005-02-03 18:20:22 +00004207}
4208
4209
cerion3d870a32005-03-18 12:23:33 +00004210/*
4211 Integer Branch Instructions
4212*/
sewardj9d540e52005-10-08 11:28:16 +00004213static Bool dis_branch ( UInt theInstr,
4214 /*OUT*/DisResult* dres,
sewardjc716aea2006-01-17 01:48:46 +00004215 Bool (*resteerOkFn)(void*,Addr64),
4216 void* callback_opaque )
cerion91ad5362005-01-27 23:02:41 +00004217{
ceriond953ebb2005-11-29 13:27:20 +00004218 UChar opc1 = ifieldOPC(theInstr);
4219 UChar BO = ifieldRegDS(theInstr);
4220 UChar BI = ifieldRegA(theInstr);
4221 UInt BD_u16 = ifieldUIMM16(theInstr) & 0xFFFFFFFC; /* mask off */
4222 UChar b11to15 = ifieldRegB(theInstr);
4223 UInt opc2 = ifieldOPClo10(theInstr);
4224 UInt LI_u26 = ifieldUIMM26(theInstr) & 0xFFFFFFFC; /* mask off */
4225 UChar flag_AA = ifieldBIT1(theInstr);
4226 UChar flag_LK = ifieldBIT0(theInstr);
4227
cerion2831b002005-11-30 19:55:22 +00004228 IRType ty = mode64 ? Ity_I64 : Ity_I32;
ceriond953ebb2005-11-29 13:27:20 +00004229 Addr64 tgt = 0;
4230 Int BD = extend_s_16to32(BD_u16);
cerion2831b002005-11-30 19:55:22 +00004231 IRTemp do_branch = newTemp(Ity_I32);
4232 IRTemp ctr_ok = newTemp(Ity_I32);
4233 IRTemp cond_ok = newTemp(Ity_I32);
4234 IRExpr* e_nia = mkSzImm(ty, nextInsnAddr());
4235 IRConst* c_nia = mkSzConst(ty, nextInsnAddr());
sewardjdf07b882005-11-29 18:19:11 +00004236 IRTemp lr_old = newTemp(ty);
ceriond953ebb2005-11-29 13:27:20 +00004237
cerionb85e8bb2005-02-16 08:54:33 +00004238 /* Hack to pass through code that just wants to read the PC */
4239 if (theInstr == 0x429F0005) {
sewardjb51f0f42005-07-18 11:38:02 +00004240 DIP("bcl 0x%x, 0x%x (a.k.a mr lr,cia+4)\n", BO, BI);
ceriond953ebb2005-11-29 13:27:20 +00004241 putGST( PPC_GST_LR, e_nia );
cerionb85e8bb2005-02-16 08:54:33 +00004242 return True;
sewardjb51f0f42005-07-18 11:38:02 +00004243 }
sewardj9d540e52005-10-08 11:28:16 +00004244
4245 /* The default what-next. Individual cases can override it. */
4246 dres->whatNext = Dis_StopHere;
4247
cerionb85e8bb2005-02-16 08:54:33 +00004248 switch (opc1) {
cerione9d361a2005-03-04 17:35:29 +00004249 case 0x12: // b (Branch, PPC32 p360)
cerion4561acb2005-02-21 14:07:48 +00004250 if (flag_AA) {
cerion2831b002005-11-30 19:55:22 +00004251 tgt = mkSzAddr( ty, extend_s_26to64(LI_u26) );
cerion4561acb2005-02-21 14:07:48 +00004252 } else {
cerion2831b002005-11-30 19:55:22 +00004253 tgt = mkSzAddr( ty, guest_CIA_curr_instr +
4254 (Long)extend_s_26to64(LI_u26) );
cerionb85e8bb2005-02-16 08:54:33 +00004255 }
ceriond953ebb2005-11-29 13:27:20 +00004256 if (mode64) {
4257 DIP("b%s%s 0x%llx\n",
4258 flag_LK ? "l" : "", flag_AA ? "a" : "", tgt);
4259 } else {
4260 DIP("b%s%s 0x%x\n",
4261 flag_LK ? "l" : "", flag_AA ? "a" : "", (Addr32)tgt);
sewardj9d540e52005-10-08 11:28:16 +00004262 }
4263
sewardjcf8986c2006-01-18 04:14:52 +00004264 if (flag_LK) {
ceriond953ebb2005-11-29 13:27:20 +00004265 putGST( PPC_GST_LR, e_nia );
sewardjcf8986c2006-01-18 04:14:52 +00004266 if (mode64)
4267 make_redzone_AbiHint( "branch-and-link (unconditional call)" );
4268 }
ceriond953ebb2005-11-29 13:27:20 +00004269
sewardjc716aea2006-01-17 01:48:46 +00004270 if (resteerOkFn( callback_opaque, tgt )) {
ceriond953ebb2005-11-29 13:27:20 +00004271 dres->whatNext = Dis_Resteer;
4272 dres->continueAt = tgt;
sewardj9d540e52005-10-08 11:28:16 +00004273 } else {
4274 irbb->jumpkind = flag_LK ? Ijk_Call : Ijk_Boring;
cerion2831b002005-11-30 19:55:22 +00004275 irbb->next = mkSzImm(ty, tgt);
sewardj9d540e52005-10-08 11:28:16 +00004276 }
cerionb85e8bb2005-02-16 08:54:33 +00004277 break;
4278
cerione9d361a2005-03-04 17:35:29 +00004279 case 0x10: // bc (Branch Conditional, PPC32 p361)
cerionb85e8bb2005-02-16 08:54:33 +00004280 DIP("bc%s%s 0x%x, 0x%x, 0x%x\n",
ceriond953ebb2005-11-29 13:27:20 +00004281 flag_LK ? "l" : "", flag_AA ? "a" : "", BO, BI, BD);
cerionb85e8bb2005-02-16 08:54:33 +00004282
4283 if (!(BO & 0x4)) {
ceriond953ebb2005-11-29 13:27:20 +00004284 putGST( PPC_GST_CTR,
cerion2831b002005-11-30 19:55:22 +00004285 binop(mkSzOp(ty, Iop_Sub8),
4286 getGST( PPC_GST_CTR ), mkSzImm(ty, 1)) );
cerionb85e8bb2005-02-16 08:54:33 +00004287 }
sewardjb51f0f42005-07-18 11:38:02 +00004288
4289 /* This is a bit subtle. ctr_ok is either all 0s or all 1s.
cerion76de5cf2005-11-18 18:25:12 +00004290 cond_ok is either zero or nonzero, since that's the cheapest
4291 way to compute it. Anding them together gives a value which
4292 is either zero or non zero and so that's what we must test
4293 for in the IRStmt_Exit. */
sewardjb51f0f42005-07-18 11:38:02 +00004294 assign( ctr_ok, branch_ctr_ok( BO ) );
cerionb85e8bb2005-02-16 08:54:33 +00004295 assign( cond_ok, branch_cond_ok( BO, BI ) );
sewardjb51f0f42005-07-18 11:38:02 +00004296 assign( do_branch,
4297 binop(Iop_And32, mkexpr(cond_ok), mkexpr(ctr_ok)) );
4298
cerion4561acb2005-02-21 14:07:48 +00004299 if (flag_AA) {
cerion2831b002005-11-30 19:55:22 +00004300 tgt = mkSzAddr(ty, extend_s_16to64(BD_u16));
cerion4561acb2005-02-21 14:07:48 +00004301 } else {
cerion2831b002005-11-30 19:55:22 +00004302 tgt = mkSzAddr(ty, guest_CIA_curr_instr +
4303 (Long)extend_s_16to64(BD_u16));
cerionb85e8bb2005-02-16 08:54:33 +00004304 }
ceriond953ebb2005-11-29 13:27:20 +00004305 if (flag_LK)
4306 putGST( PPC_GST_LR, e_nia );
cerionb85e8bb2005-02-16 08:54:33 +00004307
ceriond953ebb2005-11-29 13:27:20 +00004308 stmt( IRStmt_Exit(
4309 binop(Iop_CmpNE32, mkexpr(do_branch), mkU32(0)),
4310 flag_LK ? Ijk_Call : Ijk_Boring,
cerion2831b002005-11-30 19:55:22 +00004311 mkSzConst(ty, tgt) ) );
cerionb85e8bb2005-02-16 08:54:33 +00004312
4313 irbb->jumpkind = Ijk_Boring;
ceriond953ebb2005-11-29 13:27:20 +00004314 irbb->next = e_nia;
cerionb85e8bb2005-02-16 08:54:33 +00004315 break;
4316
4317 case 0x13:
sewardj6be67232006-01-24 19:00:05 +00004318 /* For bclr and bcctr, it appears that the lowest two bits of
4319 b11to15 are a branch hint, and so we only need to ensure it's
4320 of the form 000XX. */
4321 if ((b11to15 & ~3) != 0) {
4322 vex_printf("dis_int_branch(ppc)(0x13,b11to15)(%d)\n", (Int)b11to15);
cerionb85e8bb2005-02-16 08:54:33 +00004323 return False;
4324 }
cerion91ad5362005-01-27 23:02:41 +00004325
cerionb85e8bb2005-02-16 08:54:33 +00004326 switch (opc2) {
cerione9d361a2005-03-04 17:35:29 +00004327 case 0x210: // bcctr (Branch Cond. to Count Register, PPC32 p363)
cerion5b2325f2005-12-23 00:55:09 +00004328 if ((BO & 0x4) == 0) { // "decr and test CTR" option invalid
4329 vex_printf("dis_int_branch(ppc)(bcctr,BO)\n");
cerionb85e8bb2005-02-16 08:54:33 +00004330 return False;
4331 }
ceriona31e8b52005-02-21 16:30:45 +00004332 DIP("bcctr%s 0x%x, 0x%x\n", flag_LK ? "l" : "", BO, BI);
cerionb85e8bb2005-02-16 08:54:33 +00004333
4334 assign( cond_ok, branch_cond_ok( BO, BI ) );
ceriond953ebb2005-11-29 13:27:20 +00004335
cerion2831b002005-11-30 19:55:22 +00004336 assign( lr_old, addr_align( getGST( PPC_GST_CTR ), 4 ));
sewardjdf07b882005-11-29 18:19:11 +00004337
ceriond953ebb2005-11-29 13:27:20 +00004338 if (flag_LK)
4339 putGST( PPC_GST_LR, e_nia );
cerionb85e8bb2005-02-16 08:54:33 +00004340
sewardjb51f0f42005-07-18 11:38:02 +00004341 stmt( IRStmt_Exit(
4342 binop(Iop_CmpEQ32, mkexpr(cond_ok), mkU32(0)),
4343 Ijk_Boring,
ceriond953ebb2005-11-29 13:27:20 +00004344 c_nia ));
cerionb85e8bb2005-02-16 08:54:33 +00004345
4346 irbb->jumpkind = flag_LK ? Ijk_Call : Ijk_Boring;
sewardjdf07b882005-11-29 18:19:11 +00004347 irbb->next = mkexpr(lr_old);
cerionb85e8bb2005-02-16 08:54:33 +00004348 break;
4349
sewardjcf8986c2006-01-18 04:14:52 +00004350 case 0x010: { // bclr (Branch Cond. to Link Register, PPC32 p365)
4351 Bool vanilla_return = False;
sewardjb51f0f42005-07-18 11:38:02 +00004352 if ((BO & 0x14 /* 1z1zz */) == 0x14 && flag_LK == 0) {
cerion225a0342005-09-12 20:49:09 +00004353 DIP("blr\n");
sewardjcf8986c2006-01-18 04:14:52 +00004354 vanilla_return = True;
sewardjb51f0f42005-07-18 11:38:02 +00004355 } else {
4356 DIP("bclr%s 0x%x, 0x%x\n", flag_LK ? "l" : "", BO, BI);
4357 }
cerion91ad5362005-01-27 23:02:41 +00004358
cerionb85e8bb2005-02-16 08:54:33 +00004359 if (!(BO & 0x4)) {
ceriond953ebb2005-11-29 13:27:20 +00004360 putGST( PPC_GST_CTR,
cerion2831b002005-11-30 19:55:22 +00004361 binop(mkSzOp(ty, Iop_Sub8),
4362 getGST( PPC_GST_CTR ), mkSzImm(ty, 1)) );
cerionb85e8bb2005-02-16 08:54:33 +00004363 }
4364
sewardjb51f0f42005-07-18 11:38:02 +00004365 /* See comments above for 'bc' about this */
4366 assign( ctr_ok, branch_ctr_ok( BO ) );
4367 assign( cond_ok, branch_cond_ok( BO, BI ) );
4368 assign( do_branch,
4369 binop(Iop_And32, mkexpr(cond_ok), mkexpr(ctr_ok)) );
cerion2831b002005-11-30 19:55:22 +00004370
sewardjdf07b882005-11-29 18:19:11 +00004371 assign( lr_old, addr_align( getGST( PPC_GST_LR ), 4 ));
4372
ceriond953ebb2005-11-29 13:27:20 +00004373 if (flag_LK)
4374 putGST( PPC_GST_LR, e_nia );
sewardjb51f0f42005-07-18 11:38:02 +00004375
4376 stmt( IRStmt_Exit(
4377 binop(Iop_CmpEQ32, mkexpr(do_branch), mkU32(0)),
4378 Ijk_Boring,
ceriond953ebb2005-11-29 13:27:20 +00004379 c_nia ));
sewardjb51f0f42005-07-18 11:38:02 +00004380
sewardjcf8986c2006-01-18 04:14:52 +00004381 if (vanilla_return && mode64)
4382 make_redzone_AbiHint( "branch-to-lr (unconditional return)" );
4383
sewardjd37be032005-11-12 12:56:31 +00004384 /* blrl is pretty strange; it's like a return that sets the
4385 return address of its caller to the insn following this
4386 one. Mark it as a return. */
4387 irbb->jumpkind = Ijk_Ret; /* was flag_LK ? Ijk_Call : Ijk_Ret; */
sewardjdf07b882005-11-29 18:19:11 +00004388 irbb->next = mkexpr(lr_old);
cerionb85e8bb2005-02-16 08:54:33 +00004389 break;
sewardjcf8986c2006-01-18 04:14:52 +00004390 }
cerionb85e8bb2005-02-16 08:54:33 +00004391 default:
cerion5b2325f2005-12-23 00:55:09 +00004392 vex_printf("dis_int_branch(ppc)(opc2)\n");
cerionb85e8bb2005-02-16 08:54:33 +00004393 return False;
4394 }
4395 break;
cerion2831b002005-11-30 19:55:22 +00004396
cerionb85e8bb2005-02-16 08:54:33 +00004397 default:
cerion5b2325f2005-12-23 00:55:09 +00004398 vex_printf("dis_int_branch(ppc)(opc1)\n");
cerionb85e8bb2005-02-16 08:54:33 +00004399 return False;
4400 }
cerion2831b002005-11-30 19:55:22 +00004401
cerionb85e8bb2005-02-16 08:54:33 +00004402 return True;
cerion91ad5362005-01-27 23:02:41 +00004403}
4404
4405
4406
cerion3d870a32005-03-18 12:23:33 +00004407/*
4408 Condition Register Logical Instructions
4409*/
cerion3007c7f2005-02-23 23:13:29 +00004410static Bool dis_cond_logic ( UInt theInstr )
4411{
4412 /* XL-Form */
cerion76de5cf2005-11-18 18:25:12 +00004413 UChar opc1 = ifieldOPC(theInstr);
4414 UChar crbD_addr = ifieldRegDS(theInstr);
4415 UChar crfD_addr = toUChar( IFIELD(theInstr, 23, 3) );
4416 UChar crbA_addr = ifieldRegA(theInstr);
4417 UChar crfS_addr = toUChar( IFIELD(theInstr, 18, 3) );
4418 UChar crbB_addr = ifieldRegB(theInstr);
4419 UInt opc2 = ifieldOPClo10(theInstr);
4420 UChar b0 = ifieldBIT0(theInstr);
cerion3007c7f2005-02-23 23:13:29 +00004421
cerionf0de28c2005-12-13 20:21:11 +00004422 IRTemp crbD = newTemp(Ity_I32);
4423 IRTemp crbA = newTemp(Ity_I32);
4424 IRTemp crbB = newTemp(Ity_I32);
cerion3007c7f2005-02-23 23:13:29 +00004425
4426 if (opc1 != 19 || b0 != 0) {
cerion5b2325f2005-12-23 00:55:09 +00004427 vex_printf("dis_cond_logic(ppc)(opc1)\n");
cerion3007c7f2005-02-23 23:13:29 +00004428 return False;
4429 }
4430
cerione9d361a2005-03-04 17:35:29 +00004431 if (opc2 == 0) { // mcrf (Move Cond Reg Field, PPC32 p464)
cerion3007c7f2005-02-23 23:13:29 +00004432 if (((crbD_addr & 0x3) != 0) ||
cerion76de5cf2005-11-18 18:25:12 +00004433 ((crbA_addr & 0x3) != 0) || (crbB_addr != 0)) {
cerion5b2325f2005-12-23 00:55:09 +00004434 vex_printf("dis_cond_logic(ppc)(crbD|crbA|crbB != 0)\n");
cerion3007c7f2005-02-23 23:13:29 +00004435 return False;
cerion76de5cf2005-11-18 18:25:12 +00004436 }
ceriond953ebb2005-11-29 13:27:20 +00004437 DIP("mcrf cr%u,cr%u\n", crfD_addr, crfS_addr);
sewardjb51f0f42005-07-18 11:38:02 +00004438 putCR0( crfD_addr, getCR0( crfS_addr) );
4439 putCR321( crfD_addr, getCR321(crfS_addr) );
cerion3007c7f2005-02-23 23:13:29 +00004440 } else {
sewardjb51f0f42005-07-18 11:38:02 +00004441 assign( crbA, getCRbit(crbA_addr) );
ceriona50fde52005-07-01 21:16:10 +00004442 if (crbA_addr == crbB_addr)
sewardjb51f0f42005-07-18 11:38:02 +00004443 crbB = crbA;
ceriona50fde52005-07-01 21:16:10 +00004444 else
sewardjb51f0f42005-07-18 11:38:02 +00004445 assign( crbB, getCRbit(crbB_addr) );
cerion3007c7f2005-02-23 23:13:29 +00004446
4447 switch (opc2) {
sewardj7c2dc712005-09-08 17:33:27 +00004448 case 0x101: // crand (Cond Reg AND, PPC32 p372)
4449 DIP("crand crb%d,crb%d,crb%d\n", crbD_addr, crbA_addr, crbB_addr);
4450 assign( crbD, binop(Iop_And32, mkexpr(crbA), mkexpr(crbB)) );
4451 break;
sewardj7787af42005-08-04 18:32:19 +00004452 case 0x081: // crandc (Cond Reg AND w. Complement, PPC32 p373)
4453 DIP("crandc crb%d,crb%d,crb%d\n", crbD_addr, crbA_addr, crbB_addr);
4454 assign( crbD, binop(Iop_And32,
4455 mkexpr(crbA),
4456 unop(Iop_Not32, mkexpr(crbB))) );
4457 break;
sewardje14bb9f2005-07-22 09:39:02 +00004458 case 0x121: // creqv (Cond Reg Equivalent, PPC32 p374)
4459 DIP("creqv crb%d,crb%d,crb%d\n", crbD_addr, crbA_addr, crbB_addr);
4460 assign( crbD, unop(Iop_Not32,
4461 binop(Iop_Xor32, mkexpr(crbA), mkexpr(crbB))) );
4462 break;
sewardj7c2dc712005-09-08 17:33:27 +00004463 case 0x0E1: // crnand (Cond Reg NAND, PPC32 p375)
4464 DIP("crnand crb%d,crb%d,crb%d\n", crbD_addr, crbA_addr, crbB_addr);
4465 assign( crbD, unop(Iop_Not32,
4466 binop(Iop_And32, mkexpr(crbA), mkexpr(crbB))) );
4467 break;
cerione9d361a2005-03-04 17:35:29 +00004468 case 0x021: // crnor (Cond Reg NOR, PPC32 p376)
cerion3007c7f2005-02-23 23:13:29 +00004469 DIP("crnor crb%d,crb%d,crb%d\n", crbD_addr, crbA_addr, crbB_addr);
4470 assign( crbD, unop(Iop_Not32,
4471 binop(Iop_Or32, mkexpr(crbA), mkexpr(crbB))) );
4472 break;
cerione9d361a2005-03-04 17:35:29 +00004473 case 0x1C1: // cror (Cond Reg OR, PPC32 p377)
cerion3007c7f2005-02-23 23:13:29 +00004474 DIP("cror crb%d,crb%d,crb%d\n", crbD_addr, crbA_addr, crbB_addr);
4475 assign( crbD, binop(Iop_Or32, mkexpr(crbA), mkexpr(crbB)) );
4476 break;
sewardj7c2dc712005-09-08 17:33:27 +00004477 case 0x1A1: // crorc (Cond Reg OR w. Complement, PPC32 p378)
4478 DIP("crorc crb%d,crb%d,crb%d\n", crbD_addr, crbA_addr, crbB_addr);
4479 assign( crbD, binop(Iop_Or32,
4480 mkexpr(crbA),
4481 unop(Iop_Not32, mkexpr(crbB))) );
4482 break;
cerione9d361a2005-03-04 17:35:29 +00004483 case 0x0C1: // crxor (Cond Reg XOR, PPC32 p379)
cerion3007c7f2005-02-23 23:13:29 +00004484 DIP("crxor crb%d,crb%d,crb%d\n", crbD_addr, crbA_addr, crbB_addr);
4485 assign( crbD, binop(Iop_Xor32, mkexpr(crbA), mkexpr(crbB)) );
4486 break;
cerion3007c7f2005-02-23 23:13:29 +00004487 default:
cerion5b2325f2005-12-23 00:55:09 +00004488 vex_printf("dis_cond_logic(ppc)(opc2)\n");
cerion3007c7f2005-02-23 23:13:29 +00004489 return False;
4490 }
4491
sewardjb51f0f42005-07-18 11:38:02 +00004492 putCRbit( crbD_addr, mkexpr(crbD) );
cerion3007c7f2005-02-23 23:13:29 +00004493 }
4494 return True;
4495}
4496
4497
sewardj334870d2006-02-07 16:42:39 +00004498/*
4499 Trap instructions
4500*/
4501
4502/* Do the code generation for a trap. Returned Bool is true iff
4503 this is an unconditional trap. */
sewardj2d19fe32006-02-07 20:55:08 +00004504static Bool do_trap ( Bool is_twi, UChar TO,
sewardj334870d2006-02-07 16:42:39 +00004505 IRExpr* argL0, ULong argR0, Addr64 cia )
4506{
4507 IRTemp argL, argR;
4508 IRExpr *argLe, *argRe, *cond, *tmp;
4509
sewardj2d19fe32006-02-07 20:55:08 +00004510 IROp opAND = is_twi ? Iop_And32 : Iop_And64;
4511 IROp opOR = is_twi ? Iop_Or32 : Iop_Or64;
4512 IROp opCMPORDS = is_twi ? Iop_CmpORD32S : Iop_CmpORD64S;
4513 IROp opCMPORDU = is_twi ? Iop_CmpORD32U : Iop_CmpORD64U;
4514 IROp opCMPNE = is_twi ? Iop_CmpNE32 : Iop_CmpNE64;
4515 IROp opCMPEQ = is_twi ? Iop_CmpEQ32 : Iop_CmpEQ64;
4516 IRExpr* const0 = is_twi ? mkU32(0) : mkU64(0);
4517 IRExpr* const2 = is_twi ? mkU32(2) : mkU64(2);
4518 IRExpr* const4 = is_twi ? mkU32(4) : mkU64(4);
4519 IRExpr* const8 = is_twi ? mkU32(8) : mkU64(8);
sewardj334870d2006-02-07 16:42:39 +00004520
4521 const UChar b11100 = 0x1C;
4522 const UChar b00111 = 0x07;
4523
4524 if ((TO & b11100) == b11100 || (TO & b00111) == b00111) {
4525 /* Unconditional trap. Just do the exit without
4526 testing the arguments. */
4527 stmt( IRStmt_Exit(
4528 binop(opCMPEQ, const0, const0),
4529 Ijk_Trap,
4530 mode64 ? IRConst_U64(cia) : IRConst_U32((UInt)cia)
4531 ));
4532 return True; /* unconditional trap */
4533 }
4534
sewardj2d19fe32006-02-07 20:55:08 +00004535 if (is_twi) {
4536 argL = newTemp(Ity_I32);
4537 argR = newTemp(Ity_I32);
4538 assign( argL, mode64 ? mkSzNarrow32(Ity_I64,argL0)
4539 : argL0 );
sewardj334870d2006-02-07 16:42:39 +00004540 assign( argR, mkU32( (UInt)argR0 ));
4541 } else {
4542 vassert(mode64);
sewardj2d19fe32006-02-07 20:55:08 +00004543 argL = newTemp(Ity_I64);
4544 argR = newTemp(Ity_I64);
sewardj334870d2006-02-07 16:42:39 +00004545 assign( argL, argL0 );
4546 assign( argR, mkU64( argR0 ));
4547 }
4548 argLe = mkexpr(argL);
4549 argRe = mkexpr(argR);
4550 cond = const0;
4551 if (TO & 16) { // L <s R
4552 tmp = binop(opAND, binop(opCMPORDS, argLe, argRe), const8);
4553 cond = binop(opOR, tmp, cond);
4554 }
4555 if (TO & 8) { // L >s R
4556 tmp = binop(opAND, binop(opCMPORDS, argLe, argRe), const4);
4557 cond = binop(opOR, tmp, cond);
4558 }
4559 if (TO & 4) { // L == R
4560 tmp = binop(opAND, binop(opCMPORDS, argLe, argRe), const2);
4561 cond = binop(opOR, tmp, cond);
4562 }
4563 if (TO & 2) { // L <u R
4564 tmp = binop(opAND, binop(opCMPORDU, argLe, argRe), const8);
4565 cond = binop(opOR, tmp, cond);
4566 }
4567 if (TO & 1) { // L >u R
4568 tmp = binop(opAND, binop(opCMPORDU, argLe, argRe), const4);
4569 cond = binop(opOR, tmp, cond);
4570 }
4571 stmt( IRStmt_Exit(
4572 binop(opCMPNE, cond, const0),
4573 Ijk_Trap,
4574 mode64 ? IRConst_U64(cia) : IRConst_U32((UInt)cia)
4575 ));
4576 return False; /* not an unconditional trap */
4577}
4578
4579static Bool dis_trapi ( UInt theInstr,
4580 /*OUT*/DisResult* dres )
4581{
4582 /* D-Form */
4583 UChar opc1 = ifieldOPC(theInstr);
4584 UChar TO = ifieldRegDS(theInstr);
4585 UChar rA_addr = ifieldRegA(theInstr);
4586 UInt uimm16 = ifieldUIMM16(theInstr);
4587 ULong simm16 = extend_s_16to64(uimm16);
4588 Addr64 cia = guest_CIA_curr_instr;
4589 IRType ty = mode64 ? Ity_I64 : Ity_I32;
4590 Bool uncond = False;
4591
4592 switch (opc1) {
4593 case 0x03: // twi (Trap Word Immediate, PPC32 p548)
sewardj2d19fe32006-02-07 20:55:08 +00004594 uncond = do_trap( True/*is_twi*/, TO, getIReg(rA_addr), simm16, cia );
sewardj334870d2006-02-07 16:42:39 +00004595 if (TO == 4) {
4596 DIP("tweqi r%u,%d\n", (UInt)rA_addr, (Int)simm16);
4597 } else {
4598 DIP("tw%di r%u,%d\n", (Int)TO, (UInt)rA_addr, (Int)simm16);
4599 }
4600 break;
4601 case 0x02: // tdi
4602 if (!mode64)
4603 return False;
sewardj2d19fe32006-02-07 20:55:08 +00004604 uncond = do_trap( False/*!is_twi*/, TO, getIReg(rA_addr), simm16, cia );
4605 if (TO == 4) {
4606 DIP("tdeqi r%u,%d\n", (UInt)rA_addr, (Int)simm16);
4607 } else {
4608 DIP("td%di r%u,%d\n", (Int)TO, (UInt)rA_addr, (Int)simm16);
4609 }
sewardj334870d2006-02-07 16:42:39 +00004610 break;
4611 default:
4612 return False;
4613 }
4614
4615 if (uncond) {
4616 /* If the trap shows signs of being unconditional, don't
4617 continue decoding past it. */
4618 irbb->next = mkSzImm( ty, nextInsnAddr() );
4619 irbb->jumpkind = Ijk_Boring;
4620 dres->whatNext = Dis_StopHere;
4621 }
4622
4623 return True;
4624}
4625
4626
cerion3d870a32005-03-18 12:23:33 +00004627/*
4628 System Linkage Instructions
4629*/
sewardj9e6491a2005-07-02 19:24:10 +00004630static Bool dis_syslink ( UInt theInstr, DisResult* dres )
cerion8c3adda2005-01-31 11:54:05 +00004631{
ceriond953ebb2005-11-29 13:27:20 +00004632 IRType ty = mode64 ? Ity_I64 : Ity_I32;
4633
cerionb85e8bb2005-02-16 08:54:33 +00004634 if (theInstr != 0x44000002) {
cerion5b2325f2005-12-23 00:55:09 +00004635 vex_printf("dis_syslink(ppc)(theInstr)\n");
cerionb85e8bb2005-02-16 08:54:33 +00004636 return False;
4637 }
cerione1d857b2005-02-04 18:29:05 +00004638
cerione9d361a2005-03-04 17:35:29 +00004639 // sc (System Call, PPC32 p504)
cerionb85e8bb2005-02-16 08:54:33 +00004640 DIP("sc\n");
4641
4642 /* It's important that all ArchRegs carry their up-to-date value
4643 at this point. So we declare an end-of-block here, which
4644 forces any TempRegs caching ArchRegs to be flushed. */
cerion2831b002005-11-30 19:55:22 +00004645 irbb->next = mkSzImm( ty, nextInsnAddr() );
sewardj4fa325a2005-11-03 13:27:24 +00004646 irbb->jumpkind = Ijk_Sys_syscall;
ceriond953ebb2005-11-29 13:27:20 +00004647
sewardj9e6491a2005-07-02 19:24:10 +00004648 dres->whatNext = Dis_StopHere;
cerionb85e8bb2005-02-16 08:54:33 +00004649 return True;
cerion8c3adda2005-01-31 11:54:05 +00004650}
4651
cerion3d870a32005-03-18 12:23:33 +00004652
4653/*
4654 Memory Synchronization Instructions
cerion07b07a92005-12-22 14:32:35 +00004655
4656 Note on Reservations:
4657 We rely on the assumption that V will in fact only allow one thread at
4658 once to run. In effect, a thread can make a reservation, but we don't
4659 check any stores it does. Instead, the reservation is cancelled when
4660 the scheduler switches to another thread (run_thread_for_a_while()).
cerion3d870a32005-03-18 12:23:33 +00004661*/
cerion8c3adda2005-01-31 11:54:05 +00004662static Bool dis_memsync ( UInt theInstr )
4663{
cerionb85e8bb2005-02-16 08:54:33 +00004664 /* X-Form, XL-Form */
ceriond953ebb2005-11-29 13:27:20 +00004665 UChar opc1 = ifieldOPC(theInstr);
4666 UInt b11to25 = IFIELD(theInstr, 11, 15);
cerione43bc882006-01-05 13:11:59 +00004667 UChar flag_L = ifieldRegDS(theInstr);
4668 UInt b11to20 = IFIELD(theInstr, 11, 10);
ceriond953ebb2005-11-29 13:27:20 +00004669 UChar rD_addr = ifieldRegDS(theInstr);
4670 UChar rS_addr = rD_addr;
4671 UChar rA_addr = ifieldRegA(theInstr);
4672 UChar rB_addr = ifieldRegB(theInstr);
4673 UInt opc2 = ifieldOPClo10(theInstr);
4674 UChar b0 = ifieldBIT0(theInstr);
cerionedf7fc52005-11-18 20:57:41 +00004675
ceriond953ebb2005-11-29 13:27:20 +00004676 IRType ty = mode64 ? Ity_I64 : Ity_I32;
4677 IRTemp EA = newTemp(ty);
4678 IRTemp rS = newTemp(ty);
4679
4680 assign( EA, ea_rAor0_idxd( rA_addr, rB_addr ) );
4681
cerionb85e8bb2005-02-16 08:54:33 +00004682 switch (opc1) {
sewardjafe85832005-09-09 10:25:39 +00004683 /* XL-Form */
cerione9d361a2005-03-04 17:35:29 +00004684 case 0x13: // isync (Instruction Synchronize, PPC32 p432)
cerionb85e8bb2005-02-16 08:54:33 +00004685 if (opc2 != 0x096) {
cerion5b2325f2005-12-23 00:55:09 +00004686 vex_printf("dis_memsync(ppc)(0x13,opc2)\n");
cerionb85e8bb2005-02-16 08:54:33 +00004687 return False;
4688 }
4689 if (b11to25 != 0 || b0 != 0) {
cerion5b2325f2005-12-23 00:55:09 +00004690 vex_printf("dis_memsync(ppc)(0x13,b11to25|b0)\n");
cerionb85e8bb2005-02-16 08:54:33 +00004691 return False;
4692 }
4693 DIP("isync\n");
cerionb85e8bb2005-02-16 08:54:33 +00004694 stmt( IRStmt_MFence() );
4695 break;
cerion8c3adda2005-01-31 11:54:05 +00004696
cerionb85e8bb2005-02-16 08:54:33 +00004697 /* X-Form */
4698 case 0x1F:
4699 switch (opc2) {
cerion5b2325f2005-12-23 00:55:09 +00004700 case 0x356: // eieio (Enforce In-Order Exec of I/O, PPC32 p394)
sewardj7787af42005-08-04 18:32:19 +00004701 if (b11to25 != 0 || b0 != 0) {
cerion5b2325f2005-12-23 00:55:09 +00004702 vex_printf("dis_memsync(ppc)(eiei0,b11to25|b0)\n");
sewardj7787af42005-08-04 18:32:19 +00004703 return False;
4704 }
4705 DIP("eieio\n");
4706 /* Insert a memory fence, just to be on the safe side. */
4707 stmt( IRStmt_MFence() );
4708 break;
cerion8c3adda2005-01-31 11:54:05 +00004709
cerione9d361a2005-03-04 17:35:29 +00004710 case 0x014: // lwarx (Load Word and Reserve Indexed, PPC32 p458)
cerionb85e8bb2005-02-16 08:54:33 +00004711 if (b0 != 0) {
cerion5b2325f2005-12-23 00:55:09 +00004712 vex_printf("dis_memsync(ppc)(lwarx,b0)\n");
cerionb85e8bb2005-02-16 08:54:33 +00004713 return False;
4714 }
ceriond953ebb2005-11-29 13:27:20 +00004715 DIP("lwarx r%u,r%u,r%u\n", rD_addr, rA_addr, rB_addr);
cerion5b2325f2005-12-23 00:55:09 +00004716 putIReg( rD_addr, mkSzWiden32(ty, loadBE(Ity_I32, mkexpr(EA)),
4717 False) );
cerion76de5cf2005-11-18 18:25:12 +00004718 /* Take a reservation */
ceriond953ebb2005-11-29 13:27:20 +00004719 putGST( PPC_GST_RESVN, mkexpr(EA) );
cerionb85e8bb2005-02-16 08:54:33 +00004720 break;
4721
sewardj7787af42005-08-04 18:32:19 +00004722 case 0x096: {
4723 // stwcx. (Store Word Conditional Indexed, PPC32 p532)
cerionf0de28c2005-12-13 20:21:11 +00004724 IRTemp resaddr = newTemp(ty);
cerionb85e8bb2005-02-16 08:54:33 +00004725 if (b0 != 1) {
cerion5b2325f2005-12-23 00:55:09 +00004726 vex_printf("dis_memsync(ppc)(stwcx.,b0)\n");
cerionb85e8bb2005-02-16 08:54:33 +00004727 return False;
4728 }
ceriond953ebb2005-11-29 13:27:20 +00004729 DIP("stwcx. r%u,r%u,r%u\n", rS_addr, rA_addr, rB_addr);
cerion76de5cf2005-11-18 18:25:12 +00004730 assign( rS, getIReg(rS_addr) );
sewardjafe85832005-09-09 10:25:39 +00004731
cerion76de5cf2005-11-18 18:25:12 +00004732 /* First set up as if the reservation failed */
sewardj7787af42005-08-04 18:32:19 +00004733 // Set CR0[LT GT EQ S0] = 0b000 || XER[SO]
4734 putCR321(0, mkU8(0<<1));
4735 putCR0(0, getXER_SO());
4736
cerion76de5cf2005-11-18 18:25:12 +00004737 /* Get the reservation address into a temporary, then
4738 clear it. */
ceriond953ebb2005-11-29 13:27:20 +00004739 assign( resaddr, getGST(PPC_GST_RESVN) );
cerion2831b002005-11-30 19:55:22 +00004740 putGST( PPC_GST_RESVN, mkSzImm(ty, 0) );
sewardj7787af42005-08-04 18:32:19 +00004741
cerion76de5cf2005-11-18 18:25:12 +00004742 /* Skip the rest if the reservation really did fail. */
sewardj7787af42005-08-04 18:32:19 +00004743 stmt( IRStmt_Exit(
ceriond953ebb2005-11-29 13:27:20 +00004744 ( mode64 ?
4745 binop(Iop_CmpNE64, mkexpr(resaddr), mkexpr(EA)) :
4746 binop(Iop_CmpNE32, mkexpr(resaddr), mkexpr(EA)) ),
sewardj7787af42005-08-04 18:32:19 +00004747 Ijk_Boring,
cerion2831b002005-11-30 19:55:22 +00004748 mkSzConst( ty, nextInsnAddr()) ));
ceriond953ebb2005-11-29 13:27:20 +00004749
4750 /* Note for mode64:
4751 If resaddr != lwarx_resaddr, CR0[EQ] is undefined, and
4752 whether rS is stored is dependent on that value. */
sewardj7787af42005-08-04 18:32:19 +00004753
cerion4e2c2b32006-01-02 13:35:51 +00004754 /* Success? Do the (32bit) store */
4755 storeBE( mkexpr(EA), mkSzNarrow32(ty, mkexpr(rS)) );
cerionb85e8bb2005-02-16 08:54:33 +00004756
sewardjb51f0f42005-07-18 11:38:02 +00004757 // Set CR0[LT GT EQ S0] = 0b001 || XER[SO]
4758 putCR321(0, mkU8(1<<1));
cerionb85e8bb2005-02-16 08:54:33 +00004759 break;
sewardj7787af42005-08-04 18:32:19 +00004760 }
4761
sewardjb029a612005-12-30 15:04:29 +00004762 case 0x256: // sync (Synchronize, PPC32 p543),
cerione43bc882006-01-05 13:11:59 +00004763 // also lwsync (L==1), ptesync (L==2)
sewardjb029a612005-12-30 15:04:29 +00004764 /* http://sources.redhat.com/ml/binutils/2000-12/msg00311.html
4765
4766 The PowerPC architecture used in IBM chips has expanded
4767 the sync instruction into two variants: lightweight sync
4768 and heavyweight sync. The original sync instruction is
4769 the new heavyweight sync and lightweight sync is a strict
4770 subset of the heavyweight sync functionality. This allows
4771 the programmer to specify a less expensive operation on
4772 high-end systems when the full sync functionality is not
4773 necessary.
4774
4775 The basic "sync" mnemonic now utilizes an operand. "sync"
4776 without an operand now becomes a extended mnemonic for
4777 heavyweight sync. Processors without the lwsync
4778 instruction will not decode the L field and will perform a
4779 heavyweight sync. Everything is backward compatible.
4780
4781 sync = sync 0
4782 lwsync = sync 1
cerione43bc882006-01-05 13:11:59 +00004783 ptesync = sync 2 *** TODO - not implemented ***
cerion4e2c2b32006-01-02 13:35:51 +00004784 */
cerione43bc882006-01-05 13:11:59 +00004785 if (b11to20 != 0 || b0 != 0) {
4786 vex_printf("dis_memsync(ppc)(sync/lwsync,b11to20|b0)\n");
cerionb85e8bb2005-02-16 08:54:33 +00004787 return False;
4788 }
cerione43bc882006-01-05 13:11:59 +00004789 if (flag_L != 0/*sync*/ && flag_L != 1/*lwsync*/) {
4790 vex_printf("dis_memsync(ppc)(sync/lwsync,flag_L)\n");
4791 return False;
4792 }
4793 DIP("%ssync\n", flag_L == 1 ? "lw" : "");
cerionb85e8bb2005-02-16 08:54:33 +00004794 /* Insert a memory fence. It's sometimes important that these
4795 are carried through to the generated code. */
4796 stmt( IRStmt_MFence() );
4797 break;
cerionf0de28c2005-12-13 20:21:11 +00004798
cerionf0de28c2005-12-13 20:21:11 +00004799 /* 64bit Memsync */
cerion5b2325f2005-12-23 00:55:09 +00004800 case 0x054: // ldarx (Load DWord and Reserve Indexed, PPC64 p473)
cerionf0de28c2005-12-13 20:21:11 +00004801 if (b0 != 0) {
cerion5b2325f2005-12-23 00:55:09 +00004802 vex_printf("dis_memsync(ppc)(ldarx,b0)\n");
cerionf0de28c2005-12-13 20:21:11 +00004803 return False;
4804 }
4805 DIP("ldarx r%u,r%u,r%u\n", rD_addr, rA_addr, rB_addr);
cerion07b07a92005-12-22 14:32:35 +00004806 putIReg( rD_addr, loadBE(Ity_I64, mkexpr(EA)) );
4807 // Take a reservation
4808 putGST( PPC_GST_RESVN, mkexpr(EA) );
4809 break;
4810
cerion5b2325f2005-12-23 00:55:09 +00004811 case 0x0D6: { // stdcx. (Store DWord Condition Indexd, PPC64 p581)
cerion07b07a92005-12-22 14:32:35 +00004812 IRTemp resaddr = newTemp(ty);
cerionf0de28c2005-12-13 20:21:11 +00004813 if (b0 != 1) {
cerion5b2325f2005-12-23 00:55:09 +00004814 vex_printf("dis_memsync(ppc)(stdcx.,b0)\n");
cerionf0de28c2005-12-13 20:21:11 +00004815 return False;
4816 }
4817 DIP("stdcx. r%u,r%u,r%u\n", rS_addr, rA_addr, rB_addr);
cerion07b07a92005-12-22 14:32:35 +00004818 assign( rS, getIReg(rS_addr) );
cerionf0de28c2005-12-13 20:21:11 +00004819
cerion07b07a92005-12-22 14:32:35 +00004820 // First set up as if the reservation failed
4821 // Set CR0[LT GT EQ S0] = 0b000 || XER[SO]
4822 putCR321(0, mkU8(0<<1));
4823 putCR0(0, getXER_SO());
cerionf0de28c2005-12-13 20:21:11 +00004824
cerion07b07a92005-12-22 14:32:35 +00004825 // Get the reservation address into a temporary, then clear it.
4826 assign( resaddr, getGST(PPC_GST_RESVN) );
4827 putGST( PPC_GST_RESVN, mkSzImm(ty, 0) );
cerionf0de28c2005-12-13 20:21:11 +00004828
cerion07b07a92005-12-22 14:32:35 +00004829 // Skip the rest if the reservation really did fail.
4830 stmt( IRStmt_Exit( binop(Iop_CmpNE64, mkexpr(resaddr),
4831 mkexpr(EA)),
4832 Ijk_Boring,
4833 IRConst_U64(nextInsnAddr())) );
cerionf0de28c2005-12-13 20:21:11 +00004834
cerion07b07a92005-12-22 14:32:35 +00004835 // Success? Do the store
4836 storeBE( mkexpr(EA), mkexpr(rS) );
cerionb85e8bb2005-02-16 08:54:33 +00004837
cerion07b07a92005-12-22 14:32:35 +00004838 // Set CR0[LT GT EQ S0] = 0b001 || XER[SO]
4839 putCR321(0, mkU8(1<<1));
4840 break;
4841 }
4842
cerionb85e8bb2005-02-16 08:54:33 +00004843 default:
cerion5b2325f2005-12-23 00:55:09 +00004844 vex_printf("dis_memsync(ppc)(opc2)\n");
cerionb85e8bb2005-02-16 08:54:33 +00004845 return False;
4846 }
4847 break;
cerion8c3adda2005-01-31 11:54:05 +00004848
cerionb85e8bb2005-02-16 08:54:33 +00004849 default:
cerion5b2325f2005-12-23 00:55:09 +00004850 vex_printf("dis_memsync(ppc)(opc1)\n");
cerionb85e8bb2005-02-16 08:54:33 +00004851 return False;
4852 }
4853 return True;
cerion8c3adda2005-01-31 11:54:05 +00004854}
4855
4856
4857
cerion3d870a32005-03-18 12:23:33 +00004858/*
4859 Integer Shift Instructions
4860*/
cerion645c9302005-01-31 10:09:59 +00004861static Bool dis_int_shift ( UInt theInstr )
4862{
cerionf0de28c2005-12-13 20:21:11 +00004863 /* X-Form, XS-Form */
cerion76de5cf2005-11-18 18:25:12 +00004864 UChar opc1 = ifieldOPC(theInstr);
4865 UChar rS_addr = ifieldRegDS(theInstr);
4866 UChar rA_addr = ifieldRegA(theInstr);
4867 UChar rB_addr = ifieldRegB(theInstr);
4868 UChar sh_imm = rB_addr;
4869 UInt opc2 = ifieldOPClo10(theInstr);
cerionf0de28c2005-12-13 20:21:11 +00004870 UChar b1 = ifieldBIT1(theInstr);
cerion76de5cf2005-11-18 18:25:12 +00004871 UChar flag_rC = ifieldBIT0(theInstr);
4872
ceriond953ebb2005-11-29 13:27:20 +00004873 IRType ty = mode64 ? Ity_I64 : Ity_I32;
4874 IRTemp rA = newTemp(ty);
cerion07b07a92005-12-22 14:32:35 +00004875 IRTemp rS = newTemp(ty);
4876 IRTemp rB = newTemp(ty);
ceriond953ebb2005-11-29 13:27:20 +00004877 IRTemp outofrange = newTemp(Ity_I8);
ceriond953ebb2005-11-29 13:27:20 +00004878 IRTemp rS_lo32 = newTemp(Ity_I32);
4879 IRTemp rB_lo32 = newTemp(Ity_I32);
4880 IRExpr* e_tmp;
4881
cerion07b07a92005-12-22 14:32:35 +00004882 assign( rS, getIReg(rS_addr) );
4883 assign( rB, getIReg(rB_addr) );
4884 assign( rS_lo32, mkSzNarrow32(ty, mkexpr(rS)) );
4885 assign( rB_lo32, mkSzNarrow32(ty, mkexpr(rB)) );
cerionb85e8bb2005-02-16 08:54:33 +00004886
4887 if (opc1 == 0x1F) {
4888 switch (opc2) {
ceriond953ebb2005-11-29 13:27:20 +00004889 case 0x018: { // slw (Shift Left Word, PPC32 p505)
cerion5b2325f2005-12-23 00:55:09 +00004890 DIP("slw%s r%u,r%u,r%u\n", flag_rC ? ".":"",
cerion76de5cf2005-11-18 18:25:12 +00004891 rA_addr, rS_addr, rB_addr);
4892 /* rA = rS << rB */
4893 /* ppc32 semantics are:
sewardjdfb11442005-10-08 19:58:48 +00004894 slw(x,y) = (x << (y & 31)) -- primary result
4895 & ~((y << 26) >>s 31) -- make result 0
4896 for y in 32 .. 63
4897 */
ceriond953ebb2005-11-29 13:27:20 +00004898 e_tmp =
4899 binop( Iop_And32,
4900 binop( Iop_Shl32,
4901 mkexpr(rS_lo32),
4902 unop( Iop_32to8,
4903 binop(Iop_And32,
4904 mkexpr(rB_lo32), mkU32(31)))),
4905 unop( Iop_Not32,
4906 binop( Iop_Sar32,
4907 binop(Iop_Shl32, mkexpr(rB_lo32), mkU8(26)),
4908 mkU8(31))) );
cerion2831b002005-11-30 19:55:22 +00004909 assign( rA, mkSzWiden32(ty, e_tmp, /* Signed */False) );
cerionb85e8bb2005-02-16 08:54:33 +00004910 break;
ceriond953ebb2005-11-29 13:27:20 +00004911 }
4912
cerion5b2325f2005-12-23 00:55:09 +00004913 case 0x318: { // sraw (Shift Right Alg Word, PPC32 p506)
cerion07b07a92005-12-22 14:32:35 +00004914 IRTemp sh_amt = newTemp(Ity_I32);
cerion5b2325f2005-12-23 00:55:09 +00004915 DIP("sraw%s r%u,r%u,r%u\n", flag_rC ? ".":"",
cerion76de5cf2005-11-18 18:25:12 +00004916 rA_addr, rS_addr, rB_addr);
cerion76de5cf2005-11-18 18:25:12 +00004917 /* JRS: my reading of the (poorly worded) PPC32 doc p506 is:
4918 amt = rB & 63
4919 rA = Sar32( rS, amt > 31 ? 31 : amt )
4920 XER.CA = amt > 31 ? sign-of-rS : (computation as per srawi)
4921 */
cerion5b2325f2005-12-23 00:55:09 +00004922 assign( sh_amt, binop(Iop_And32, mkU32(0x3F),
4923 mkexpr(rB_lo32)) );
cerion76de5cf2005-11-18 18:25:12 +00004924 assign( outofrange,
sewardj20ef5472005-07-21 14:48:31 +00004925 unop( Iop_1Uto8,
cerion5b2325f2005-12-23 00:55:09 +00004926 binop(Iop_CmpLT32U, mkU32(31),
4927 mkexpr(sh_amt)) ));
ceriond953ebb2005-11-29 13:27:20 +00004928 e_tmp = binop( Iop_Sar32,
4929 mkexpr(rS_lo32),
sewardj20ef5472005-07-21 14:48:31 +00004930 unop( Iop_32to8,
4931 IRExpr_Mux0X( mkexpr(outofrange),
cerion07b07a92005-12-22 14:32:35 +00004932 mkexpr(sh_amt),
ceriond953ebb2005-11-29 13:27:20 +00004933 mkU32(31)) ) );
cerion2831b002005-11-30 19:55:22 +00004934 assign( rA, mkSzWiden32(ty, e_tmp, /* Signed */True) );
4935
cerion5b2325f2005-12-23 00:55:09 +00004936 set_XER_CA( ty, PPCG_FLAG_OP_SRAW,
cerionf0de28c2005-12-13 20:21:11 +00004937 mkexpr(rA),
4938 mkSzWiden32(ty, mkexpr(rS_lo32), True),
cerion07b07a92005-12-22 14:32:35 +00004939 mkSzWiden32(ty, mkexpr(sh_amt), True ),
cerionf0de28c2005-12-13 20:21:11 +00004940 mkSzWiden32(ty, getXER_CA32(), True) );
cerionb85e8bb2005-02-16 08:54:33 +00004941 break;
cerion07b07a92005-12-22 14:32:35 +00004942 }
cerionb85e8bb2005-02-16 08:54:33 +00004943
cerion5b2325f2005-12-23 00:55:09 +00004944 case 0x338: // srawi (Shift Right Alg Word Immediate, PPC32 p507)
4945 DIP("srawi%s r%u,r%u,%d\n", flag_rC ? ".":"",
cerion76de5cf2005-11-18 18:25:12 +00004946 rA_addr, rS_addr, sh_imm);
4947 vassert(sh_imm < 32);
cerionf0de28c2005-12-13 20:21:11 +00004948 if (mode64) {
4949 assign( rA, binop(Iop_Sar64,
cerion5b2325f2005-12-23 00:55:09 +00004950 binop(Iop_Shl64, getIReg(rS_addr),
4951 mkU8(32)),
cerionf0de28c2005-12-13 20:21:11 +00004952 mkU8(32 + sh_imm)) );
4953 } else {
cerion5b2325f2005-12-23 00:55:09 +00004954 assign( rA, binop(Iop_Sar32, mkexpr(rS_lo32),
4955 mkU8(sh_imm)) );
cerionf0de28c2005-12-13 20:21:11 +00004956 }
cerion2831b002005-11-30 19:55:22 +00004957
cerion5b2325f2005-12-23 00:55:09 +00004958 set_XER_CA( ty, PPCG_FLAG_OP_SRAWI,
cerionf0de28c2005-12-13 20:21:11 +00004959 mkexpr(rA),
cerion5b2325f2005-12-23 00:55:09 +00004960 mkSzWiden32(ty, mkexpr(rS_lo32), /* Syned */True),
cerionf0de28c2005-12-13 20:21:11 +00004961 mkSzImm(ty, sh_imm),
cerion5b2325f2005-12-23 00:55:09 +00004962 mkSzWiden32(ty, getXER_CA32(), /* Syned */False) );
cerionb85e8bb2005-02-16 08:54:33 +00004963 break;
4964
cerione9d361a2005-03-04 17:35:29 +00004965 case 0x218: // srw (Shift Right Word, PPC32 p508)
cerion5b2325f2005-12-23 00:55:09 +00004966 DIP("srw%s r%u,r%u,r%u\n", flag_rC ? ".":"",
cerion76de5cf2005-11-18 18:25:12 +00004967 rA_addr, rS_addr, rB_addr);
4968 /* rA = rS >>u rB */
4969 /* ppc32 semantics are:
cerionf0de28c2005-12-13 20:21:11 +00004970 srw(x,y) = (x >>u (y & 31)) -- primary result
sewardjdfb11442005-10-08 19:58:48 +00004971 & ~((y << 26) >>s 31) -- make result 0
4972 for y in 32 .. 63
4973 */
ceriond953ebb2005-11-29 13:27:20 +00004974 e_tmp =
sewardjdfb11442005-10-08 19:58:48 +00004975 binop(
4976 Iop_And32,
4977 binop( Iop_Shr32,
ceriond953ebb2005-11-29 13:27:20 +00004978 mkexpr(rS_lo32),
sewardjdfb11442005-10-08 19:58:48 +00004979 unop( Iop_32to8,
cerion5b2325f2005-12-23 00:55:09 +00004980 binop(Iop_And32, mkexpr(rB_lo32),
4981 mkU32(31)))),
sewardjdfb11442005-10-08 19:58:48 +00004982 unop( Iop_Not32,
4983 binop( Iop_Sar32,
cerion5b2325f2005-12-23 00:55:09 +00004984 binop(Iop_Shl32, mkexpr(rB_lo32),
4985 mkU8(26)),
ceriond953ebb2005-11-29 13:27:20 +00004986 mkU8(31))));
cerion2831b002005-11-30 19:55:22 +00004987 assign( rA, mkSzWiden32(ty, e_tmp, /* Signed */False) );
cerionb85e8bb2005-02-16 08:54:33 +00004988 break;
cerionf0de28c2005-12-13 20:21:11 +00004989
4990
4991 /* 64bit Shifts */
cerion5b2325f2005-12-23 00:55:09 +00004992 case 0x01B: // sld (Shift Left DWord, PPC64 p568)
4993 DIP("sld%s r%u,r%u,r%u\n",
4994 flag_rC ? ".":"", rA_addr, rS_addr, rB_addr);
cerionf0de28c2005-12-13 20:21:11 +00004995 /* rA = rS << rB */
cerion07b07a92005-12-22 14:32:35 +00004996 /* ppc64 semantics are:
cerionf0de28c2005-12-13 20:21:11 +00004997 slw(x,y) = (x << (y & 63)) -- primary result
4998 & ~((y << 57) >>s 63) -- make result 0
4999 for y in 64 ..
5000 */
cerionf0de28c2005-12-13 20:21:11 +00005001 assign( rA,
5002 binop(
5003 Iop_And64,
5004 binop( Iop_Shl64,
5005 mkexpr(rS),
5006 unop( Iop_64to8,
5007 binop(Iop_And64, mkexpr(rB), mkU64(63)))),
5008 unop( Iop_Not64,
5009 binop( Iop_Sar64,
5010 binop(Iop_Shl64, mkexpr(rB), mkU8(57)),
5011 mkU8(63)))) );
cerion07b07a92005-12-22 14:32:35 +00005012 break;
cerionf0de28c2005-12-13 20:21:11 +00005013
cerion5b2325f2005-12-23 00:55:09 +00005014 case 0x31A: { // srad (Shift Right Alg DWord, PPC64 p570)
cerion07b07a92005-12-22 14:32:35 +00005015 IRTemp sh_amt = newTemp(Ity_I64);
cerion5b2325f2005-12-23 00:55:09 +00005016 DIP("srad%s r%u,r%u,r%u\n",
5017 flag_rC ? ".":"", rA_addr, rS_addr, rB_addr);
cerionf0de28c2005-12-13 20:21:11 +00005018 /* amt = rB & 127
5019 rA = Sar64( rS, amt > 63 ? 63 : amt )
5020 XER.CA = amt > 63 ? sign-of-rS : (computation as per srawi)
5021 */
cerion07b07a92005-12-22 14:32:35 +00005022 assign( sh_amt, binop(Iop_And64, mkU64(0x7F), mkexpr(rB)) );
cerionf0de28c2005-12-13 20:21:11 +00005023 assign( outofrange,
5024 unop( Iop_1Uto8,
cerion5b2325f2005-12-23 00:55:09 +00005025 binop(Iop_CmpLT64U, mkU64(63),
5026 mkexpr(sh_amt)) ));
cerionf0de28c2005-12-13 20:21:11 +00005027 assign( rA,
5028 binop( Iop_Sar64,
5029 mkexpr(rS),
5030 unop( Iop_64to8,
5031 IRExpr_Mux0X( mkexpr(outofrange),
cerion07b07a92005-12-22 14:32:35 +00005032 mkexpr(sh_amt),
5033 mkU64(63)) ))
cerionf0de28c2005-12-13 20:21:11 +00005034 );
cerion5b2325f2005-12-23 00:55:09 +00005035 set_XER_CA( ty, PPCG_FLAG_OP_SRAD,
cerion07b07a92005-12-22 14:32:35 +00005036 mkexpr(rA), mkexpr(rS), mkexpr(sh_amt),
cerion5b2325f2005-12-23 00:55:09 +00005037 mkSzWiden32(ty, getXER_CA32(), /* Syned */False) );
cerion07b07a92005-12-22 14:32:35 +00005038 break;
5039 }
5040
cerion5b2325f2005-12-23 00:55:09 +00005041 case 0x33A: case 0x33B: // sradi (Shr Alg DWord Imm, PPC64 p571)
cerionf0de28c2005-12-13 20:21:11 +00005042 sh_imm |= b1<<5;
5043 vassert(sh_imm < 64);
cerion5b2325f2005-12-23 00:55:09 +00005044 DIP("sradi%s r%u,r%u,%u\n",
5045 flag_rC ? ".":"", rA_addr, rS_addr, sh_imm);
cerionf0de28c2005-12-13 20:21:11 +00005046 assign( rA, binop(Iop_Sar64, getIReg(rS_addr), mkU8(sh_imm)) );
5047
cerion5b2325f2005-12-23 00:55:09 +00005048 set_XER_CA( ty, PPCG_FLAG_OP_SRADI,
cerionf0de28c2005-12-13 20:21:11 +00005049 mkexpr(rA),
5050 getIReg(rS_addr),
5051 mkU64(sh_imm),
cerion5b2325f2005-12-23 00:55:09 +00005052 mkSzWiden32(ty, getXER_CA32(), /* Syned */False) );
cerionf0de28c2005-12-13 20:21:11 +00005053 break;
5054
cerion5b2325f2005-12-23 00:55:09 +00005055 case 0x21B: // srd (Shift Right DWord, PPC64 p574)
5056 DIP("srd%s r%u,r%u,r%u\n",
5057 flag_rC ? ".":"", rA_addr, rS_addr, rB_addr);
cerionf0de28c2005-12-13 20:21:11 +00005058 /* rA = rS >>u rB */
cerion07b07a92005-12-22 14:32:35 +00005059 /* ppc semantics are:
cerionf0de28c2005-12-13 20:21:11 +00005060 srw(x,y) = (x >>u (y & 63)) -- primary result
5061 & ~((y << 57) >>s 63) -- make result 0
5062 for y in 64 .. 127
5063 */
cerionf0de28c2005-12-13 20:21:11 +00005064 assign( rA,
5065 binop(
5066 Iop_And64,
5067 binop( Iop_Shr64,
5068 mkexpr(rS),
5069 unop( Iop_64to8,
5070 binop(Iop_And64, mkexpr(rB), mkU64(63)))),
5071 unop( Iop_Not64,
5072 binop( Iop_Sar64,
5073 binop(Iop_Shl64, mkexpr(rB), mkU8(57)),
5074 mkU8(63)))) );
cerion07b07a92005-12-22 14:32:35 +00005075 break;
cerionf0de28c2005-12-13 20:21:11 +00005076
cerionb85e8bb2005-02-16 08:54:33 +00005077 default:
cerion5b2325f2005-12-23 00:55:09 +00005078 vex_printf("dis_int_shift(ppc)(opc2)\n");
cerionb85e8bb2005-02-16 08:54:33 +00005079 return False;
5080 }
5081 } else {
cerion5b2325f2005-12-23 00:55:09 +00005082 vex_printf("dis_int_shift(ppc)(opc1)\n");
cerionb85e8bb2005-02-16 08:54:33 +00005083 return False;
5084 }
cerion0d330c52005-02-28 16:43:16 +00005085
cerion76de5cf2005-11-18 18:25:12 +00005086 putIReg( rA_addr, mkexpr(rA) );
cerionb85e8bb2005-02-16 08:54:33 +00005087
cerion76de5cf2005-11-18 18:25:12 +00005088 if (flag_rC) {
5089 set_CR0( mkexpr(rA) );
cerionb85e8bb2005-02-16 08:54:33 +00005090 }
5091 return True;
cerion645c9302005-01-31 10:09:59 +00005092}
5093
5094
5095
sewardj602857d2005-09-06 09:10:09 +00005096/*
5097 Integer Load/Store Reverse Instructions
5098*/
sewardj40d8c092006-05-05 13:26:14 +00005099/* Generates code to swap the byte order in an Ity_I32. */
sewardjfb957972005-09-08 17:53:03 +00005100static IRExpr* /* :: Ity_I32 */ gen_byterev32 ( IRTemp t )
5101{
5102 vassert(typeOfIRTemp(irbb->tyenv, t) == Ity_I32);
5103 return
5104 binop(Iop_Or32,
5105 binop(Iop_Shl32, mkexpr(t), mkU8(24)),
5106 binop(Iop_Or32,
5107 binop(Iop_And32, binop(Iop_Shl32, mkexpr(t), mkU8(8)),
5108 mkU32(0x00FF0000)),
5109 binop(Iop_Or32,
5110 binop(Iop_And32, binop(Iop_Shr32, mkexpr(t), mkU8(8)),
5111 mkU32(0x0000FF00)),
5112 binop(Iop_And32, binop(Iop_Shr32, mkexpr(t), mkU8(24)),
5113 mkU32(0x000000FF) )
5114 )));
5115}
5116
sewardj40d8c092006-05-05 13:26:14 +00005117/* Generates code to swap the byte order in the lower half of an Ity_I32,
5118 and zeroes the upper half. */
5119static IRExpr* /* :: Ity_I32 */ gen_byterev16 ( IRTemp t )
5120{
5121 vassert(typeOfIRTemp(irbb->tyenv, t) == Ity_I32);
5122 return
5123 binop(Iop_Or32,
5124 binop(Iop_And32, binop(Iop_Shl32, mkexpr(t), mkU8(8)),
5125 mkU32(0x0000FF00)),
5126 binop(Iop_And32, binop(Iop_Shr32, mkexpr(t), mkU8(8)),
5127 mkU32(0x000000FF))
5128 );
5129}
5130
sewardj602857d2005-09-06 09:10:09 +00005131static Bool dis_int_ldst_rev ( UInt theInstr )
5132{
5133 /* X-Form */
cerion76de5cf2005-11-18 18:25:12 +00005134 UChar opc1 = ifieldOPC(theInstr);
5135 UChar rD_addr = ifieldRegDS(theInstr);
5136 UChar rS_addr = rD_addr;
5137 UChar rA_addr = ifieldRegA(theInstr);
5138 UChar rB_addr = ifieldRegB(theInstr);
5139 UInt opc2 = ifieldOPClo10(theInstr);
5140 UChar b0 = ifieldBIT0(theInstr);
cerionedf7fc52005-11-18 20:57:41 +00005141
ceriond953ebb2005-11-29 13:27:20 +00005142 IRType ty = mode64 ? Ity_I64 : Ity_I32;
5143 IRTemp EA = newTemp(ty);
cerionedf7fc52005-11-18 20:57:41 +00005144 IRTemp w1 = newTemp(Ity_I32);
5145 IRTemp w2 = newTemp(Ity_I32);
sewardj602857d2005-09-06 09:10:09 +00005146
5147 if (opc1 != 0x1F || b0 != 0) {
cerion5b2325f2005-12-23 00:55:09 +00005148 vex_printf("dis_int_ldst_rev(ppc)(opc1|b0)\n");
sewardj602857d2005-09-06 09:10:09 +00005149 return False;
5150 }
sewardjafe85832005-09-09 10:25:39 +00005151
ceriond953ebb2005-11-29 13:27:20 +00005152 assign( EA, ea_rAor0_idxd( rA_addr, rB_addr ) );
sewardj602857d2005-09-06 09:10:09 +00005153
5154 switch (opc2) {
sewardj40d8c092006-05-05 13:26:14 +00005155
5156 case 0x316: // lhbrx (Load Halfword Byte-Reverse Indexed, PPC32 p449)
5157 DIP("lhbrx r%u,r%u,r%u\n", rD_addr, rA_addr, rB_addr);
5158 assign( w1, unop(Iop_16Uto32, loadBE(Ity_I16, mkexpr(EA))) );
5159 assign( w2, gen_byterev16(w1) );
5160 putIReg( rD_addr, mkSzWiden32(ty, mkexpr(w2),
5161 /* Signed */False) );
5162 break;
5163
sewardjfb957972005-09-08 17:53:03 +00005164 case 0x216: // lwbrx (Load Word Byte-Reverse Indexed, PPC32 p459)
ceriond953ebb2005-11-29 13:27:20 +00005165 DIP("lwbrx r%u,r%u,r%u\n", rD_addr, rA_addr, rB_addr);
sewardjfb957972005-09-08 17:53:03 +00005166 assign( w1, loadBE(Ity_I32, mkexpr(EA)) );
5167 assign( w2, gen_byterev32(w1) );
cerion5b2325f2005-12-23 00:55:09 +00005168 putIReg( rD_addr, mkSzWiden32(ty, mkexpr(w2),
5169 /* Signed */False) );
sewardjfb957972005-09-08 17:53:03 +00005170 break;
sewardj602857d2005-09-06 09:10:09 +00005171
sewardj413a4682006-05-05 13:44:17 +00005172 case 0x396: // sthbrx (Store Half Word Byte-Reverse Indexed, PPC32 p523)
5173 DIP("sthbrx r%u,r%u,r%u\n", rS_addr, rA_addr, rB_addr);
5174 assign( w1, mkSzNarrow32(ty, getIReg(rS_addr)) );
5175 storeBE( mkexpr(EA), unop(Iop_32to16, gen_byterev16(w1)) );
5176 break;
sewardj602857d2005-09-06 09:10:09 +00005177
cerion5b2325f2005-12-23 00:55:09 +00005178 case 0x296: // stwbrx (Store Word Byte-Reverse Indxd, PPC32 p531)
ceriond953ebb2005-11-29 13:27:20 +00005179 DIP("stwbrx r%u,r%u,r%u\n", rS_addr, rA_addr, rB_addr);
cerion2831b002005-11-30 19:55:22 +00005180 assign( w1, mkSzNarrow32(ty, getIReg(rS_addr)) );
sewardjfb957972005-09-08 17:53:03 +00005181 storeBE( mkexpr(EA), gen_byterev32(w1) );
5182 break;
5183
5184 default:
cerion5b2325f2005-12-23 00:55:09 +00005185 vex_printf("dis_int_ldst_rev(ppc)(opc2)\n");
sewardjfb957972005-09-08 17:53:03 +00005186 return False;
sewardj602857d2005-09-06 09:10:09 +00005187 }
5188 return True;
5189}
cerion645c9302005-01-31 10:09:59 +00005190
5191
5192
cerion3d870a32005-03-18 12:23:33 +00005193/*
5194 Processor Control Instructions
5195*/
cerion8c3adda2005-01-31 11:54:05 +00005196static Bool dis_proc_ctl ( UInt theInstr )
5197{
cerion76de5cf2005-11-18 18:25:12 +00005198 UChar opc1 = ifieldOPC(theInstr);
cerionb85e8bb2005-02-16 08:54:33 +00005199
5200 /* X-Form */
cerion76de5cf2005-11-18 18:25:12 +00005201 UChar crfD = toUChar( IFIELD( theInstr, 23, 3 ) );
5202 UChar b21to22 = toUChar( IFIELD( theInstr, 21, 2 ) );
5203 UChar rD_addr = ifieldRegDS(theInstr);
5204 UInt b11to20 = IFIELD( theInstr, 11, 10 );
cerione9d361a2005-03-04 17:35:29 +00005205
cerion76de5cf2005-11-18 18:25:12 +00005206 /* XFX-Form */
5207 UChar rS_addr = rD_addr;
5208 UInt SPR = b11to20;
cerionedf7fc52005-11-18 20:57:41 +00005209 UInt TBR = b11to20;
cerion76de5cf2005-11-18 18:25:12 +00005210 UChar b20 = toUChar( IFIELD( theInstr, 20, 1 ) );
5211 UInt CRM = IFIELD( theInstr, 12, 8 );
5212 UChar b11 = toUChar( IFIELD( theInstr, 11, 1 ) );
5213
5214 UInt opc2 = ifieldOPClo10(theInstr);
5215 UChar b0 = ifieldBIT0(theInstr);
5216
cerion2831b002005-11-30 19:55:22 +00005217 IRType ty = mode64 ? Ity_I64 : Ity_I32;
cerionf0de28c2005-12-13 20:21:11 +00005218 IRTemp rS = newTemp(ty);
ceriond953ebb2005-11-29 13:27:20 +00005219 assign( rS, getIReg(rS_addr) );
sewardj41a7b702005-11-18 22:18:23 +00005220
cerion76de5cf2005-11-18 18:25:12 +00005221 /* Reorder SPR field as per PPC32 p470 */
5222 SPR = ((SPR & 0x1F) << 5) | ((SPR >> 5) & 0x1F);
sewardj73a91972005-09-06 10:25:46 +00005223 /* Reorder TBR field as per PPC32 p475 */
5224 TBR = ((TBR & 31) << 5) | ((TBR >> 5) & 31);
cerionb85e8bb2005-02-16 08:54:33 +00005225
5226 if (opc1 != 0x1F || b0 != 0) {
cerion5b2325f2005-12-23 00:55:09 +00005227 vex_printf("dis_proc_ctl(ppc)(opc1|b0)\n");
cerionb85e8bb2005-02-16 08:54:33 +00005228 return False;
5229 }
5230
5231 switch (opc2) {
cerioncb14e732005-09-09 16:38:19 +00005232 /* X-Form */
cerion5b2325f2005-12-23 00:55:09 +00005233 case 0x200: { // mcrxr (Move to Cond Register from XER, PPC32 p466)
cerioncb14e732005-09-09 16:38:19 +00005234 if (b21to22 != 0 || b11to20 != 0) {
cerion5b2325f2005-12-23 00:55:09 +00005235 vex_printf("dis_proc_ctl(ppc)(mcrxr,b21to22|b11to20)\n");
cerioncb14e732005-09-09 16:38:19 +00005236 return False;
5237 }
5238 DIP("mcrxr crf%d\n", crfD);
cerionedf7fc52005-11-18 20:57:41 +00005239 /* Move XER[0-3] (the top 4 bits of XER) to CR[crfD] */
ceriond953ebb2005-11-29 13:27:20 +00005240 putGST_field( PPC_GST_CR,
5241 getGST_field( PPC_GST_XER, 7 ),
cerionedf7fc52005-11-18 20:57:41 +00005242 crfD );
sewardj55ccc3e2005-09-09 19:45:02 +00005243
5244 // Clear XER[0-3]
cerionedf7fc52005-11-18 20:57:41 +00005245 putXER_SO( mkU8(0) );
5246 putXER_OV( mkU8(0) );
5247 putXER_CA( mkU8(0) );
cerioncb14e732005-09-09 16:38:19 +00005248 break;
sewardj55ccc3e2005-09-09 19:45:02 +00005249 }
cerionb85e8bb2005-02-16 08:54:33 +00005250
sewardj72aefb22006-03-01 18:58:39 +00005251 case 0x013:
5252 // b11to20==0: mfcr (Move from Cond Register, PPC32 p467)
5253 // b20==1 & b11==0: mfocrf (Move from One CR Field)
5254 // However it seems that the 'mfcr' behaviour is an acceptable
5255 // implementation of mfocr (from the 2.02 arch spec)
5256 if (b11to20 == 0) {
5257 DIP("mfcr r%u\n", rD_addr);
5258 putIReg( rD_addr, mkSzWiden32(ty, getGST( PPC_GST_CR ),
5259 /* Signed */False) );
5260 break;
cerionb85e8bb2005-02-16 08:54:33 +00005261 }
sewardj72aefb22006-03-01 18:58:39 +00005262 if (b20 == 1 && b11 == 0) {
5263 DIP("mfocrf r%u,%u\n", rD_addr, CRM);
5264 putIReg( rD_addr, mkSzWiden32(ty, getGST( PPC_GST_CR ),
5265 /* Signed */False) );
5266 break;
5267 }
5268 /* not decodable */
5269 return False;
5270
cerionb85e8bb2005-02-16 08:54:33 +00005271 /* XFX-Form */
cerione9d361a2005-03-04 17:35:29 +00005272 case 0x153: // mfspr (Move from Special-Purpose Register, PPC32 p470)
cerionb85e8bb2005-02-16 08:54:33 +00005273
cerion76de5cf2005-11-18 18:25:12 +00005274 switch (SPR) { // Choose a register...
ceriond953ebb2005-11-29 13:27:20 +00005275 case 0x1:
5276 DIP("mfxer r%u\n", rD_addr);
cerion2831b002005-11-30 19:55:22 +00005277 putIReg( rD_addr, mkSzWiden32(ty, getGST( PPC_GST_XER ),
5278 /* Signed */False) );
ceriond953ebb2005-11-29 13:27:20 +00005279 break;
5280 case 0x8:
5281 DIP("mflr r%u\n", rD_addr);
5282 putIReg( rD_addr, getGST( PPC_GST_LR ) );
5283 break;
5284 case 0x9:
5285 DIP("mfctr r%u\n", rD_addr);
5286 putIReg( rD_addr, getGST( PPC_GST_CTR ) );
5287 break;
5288 case 0x100:
5289 DIP("mfvrsave r%u\n", rD_addr);
cerion2831b002005-11-30 19:55:22 +00005290 putIReg( rD_addr, mkSzWiden32(ty, getGST( PPC_GST_VRSAVE ),
5291 /* Signed */False) );
ceriond953ebb2005-11-29 13:27:20 +00005292 break;
5293
5294 default:
cerion5b2325f2005-12-23 00:55:09 +00005295 vex_printf("dis_proc_ctl(ppc)(mfspr,SPR)(0x%x)\n", SPR);
ceriond953ebb2005-11-29 13:27:20 +00005296 return False;
cerionb85e8bb2005-02-16 08:54:33 +00005297 }
5298 break;
5299
sewardj73a91972005-09-06 10:25:46 +00005300 case 0x173: { // mftb (Move from Time Base, PPC32 p475)
5301 IRTemp val = newTemp(Ity_I64);
5302 IRExpr** args = mkIRExprVec_0();
cerion4c4f5ef2006-01-02 14:41:50 +00005303 IRDirty* d = unsafeIRDirty_1_N(
5304 val,
5305 0/*regparms*/,
5306 "ppcg_dirtyhelper_MFTB",
5307 fnptr_to_fnentry(&ppcg_dirtyhelper_MFTB),
5308 args );
sewardj73a91972005-09-06 10:25:46 +00005309 /* execute the dirty call, dumping the result in val. */
5310 stmt( IRStmt_Dirty(d) );
5311
5312 switch (TBR) {
ceriond953ebb2005-11-29 13:27:20 +00005313 case 269:
ceriond953ebb2005-11-29 13:27:20 +00005314 DIP("mftbu r%u", rD_addr);
cerion2831b002005-11-30 19:55:22 +00005315 putIReg( rD_addr,
5316 mkSzWiden32(ty, unop(Iop_64HIto32, mkexpr(val)),
5317 /* Signed */False) );
ceriond953ebb2005-11-29 13:27:20 +00005318 break;
5319 case 268:
ceriond953ebb2005-11-29 13:27:20 +00005320 DIP("mftb r%u", rD_addr);
cerion2831b002005-11-30 19:55:22 +00005321 putIReg( rD_addr, (mode64) ? mkexpr(val) :
5322 unop(Iop_64to32, mkexpr(val)) );
ceriond953ebb2005-11-29 13:27:20 +00005323 break;
5324 default:
5325 return False; /* illegal instruction */
sewardj73a91972005-09-06 10:25:46 +00005326 }
5327 break;
5328 }
5329
sewardj72aefb22006-03-01 18:58:39 +00005330 case 0x090: {
5331 // b20==0: mtcrf (Move to Cond Register Fields, PPC32 p477)
5332 // b20==1: mtocrf (Move to One Cond Reg Field)
cerionedf7fc52005-11-18 20:57:41 +00005333 Int cr;
5334 UChar shft;
sewardj72aefb22006-03-01 18:58:39 +00005335 if (b11 != 0)
cerionb85e8bb2005-02-16 08:54:33 +00005336 return False;
sewardj72aefb22006-03-01 18:58:39 +00005337 if (b20 == 1) {
5338 /* ppc64 v2.02 spec says mtocrf gives undefined outcome if >
5339 1 field is written. It seems more robust to decline to
5340 decode the insn if so. */
5341 switch (CRM) {
5342 case 0x01: case 0x02: case 0x04: case 0x08:
5343 case 0x10: case 0x20: case 0x40: case 0x80:
5344 break;
5345 default:
5346 return False;
5347 }
cerionb85e8bb2005-02-16 08:54:33 +00005348 }
sewardj72aefb22006-03-01 18:58:39 +00005349 DIP("%s 0x%x,r%u\n", b20==1 ? "mtocrf" : "mtcrf",
5350 CRM, rS_addr);
cerionedf7fc52005-11-18 20:57:41 +00005351 /* Write to each field specified by CRM */
5352 for (cr = 0; cr < 8; cr++) {
5353 if ((CRM & (1 << (7-cr))) == 0)
5354 continue;
5355 shft = 4*(7-cr);
ceriond953ebb2005-11-29 13:27:20 +00005356 putGST_field( PPC_GST_CR,
cerion2831b002005-11-30 19:55:22 +00005357 binop(Iop_Shr32,
5358 mkSzNarrow32(ty, mkexpr(rS)),
5359 mkU8(shft)), cr );
cerionedf7fc52005-11-18 20:57:41 +00005360 }
cerionb85e8bb2005-02-16 08:54:33 +00005361 break;
cerionedf7fc52005-11-18 20:57:41 +00005362 }
cerione9d361a2005-03-04 17:35:29 +00005363
5364 case 0x1D3: // mtspr (Move to Special-Purpose Register, PPC32 p483)
cerionb85e8bb2005-02-16 08:54:33 +00005365
cerion76de5cf2005-11-18 18:25:12 +00005366 switch (SPR) { // Choose a register...
ceriond953ebb2005-11-29 13:27:20 +00005367 case 0x1:
5368 DIP("mtxer r%u\n", rS_addr);
cerion2831b002005-11-30 19:55:22 +00005369 putGST( PPC_GST_XER, mkSzNarrow32(ty, mkexpr(rS)) );
ceriond953ebb2005-11-29 13:27:20 +00005370 break;
5371 case 0x8:
5372 DIP("mtlr r%u\n", rS_addr);
5373 putGST( PPC_GST_LR, mkexpr(rS) );
5374 break;
5375 case 0x9:
5376 DIP("mtctr r%u\n", rS_addr);
5377 putGST( PPC_GST_CTR, mkexpr(rS) );
5378 break;
5379 case 0x100:
5380 DIP("mtvrsave r%u\n", rS_addr);
cerion2831b002005-11-30 19:55:22 +00005381 putGST( PPC_GST_VRSAVE, mkSzNarrow32(ty, mkexpr(rS)) );
ceriond953ebb2005-11-29 13:27:20 +00005382 break;
5383
5384 default:
cerion5b2325f2005-12-23 00:55:09 +00005385 vex_printf("dis_proc_ctl(ppc)(mtspr,SPR)(%u)\n", SPR);
ceriond953ebb2005-11-29 13:27:20 +00005386 return False;
cerionb85e8bb2005-02-16 08:54:33 +00005387 }
5388 break;
5389
5390 default:
cerion5b2325f2005-12-23 00:55:09 +00005391 vex_printf("dis_proc_ctl(ppc)(opc2)\n");
cerionb85e8bb2005-02-16 08:54:33 +00005392 return False;
5393 }
5394 return True;
cerion8c3adda2005-01-31 11:54:05 +00005395}
5396
5397
cerion3d870a32005-03-18 12:23:33 +00005398/*
5399 Cache Management Instructions
5400*/
sewardjd94b73a2005-06-30 12:08:48 +00005401static Bool dis_cache_manage ( UInt theInstr,
sewardj9e6491a2005-07-02 19:24:10 +00005402 DisResult* dres,
sewardjd94b73a2005-06-30 12:08:48 +00005403 VexArchInfo* guest_archinfo )
cerion8c3adda2005-01-31 11:54:05 +00005404{
cerionb85e8bb2005-02-16 08:54:33 +00005405 /* X-Form */
cerion76de5cf2005-11-18 18:25:12 +00005406 UChar opc1 = ifieldOPC(theInstr);
5407 UChar b21to25 = ifieldRegDS(theInstr);
5408 UChar rA_addr = ifieldRegA(theInstr);
5409 UChar rB_addr = ifieldRegB(theInstr);
5410 UInt opc2 = ifieldOPClo10(theInstr);
5411 UChar b0 = ifieldBIT0(theInstr);
cerion5b2325f2005-12-23 00:55:09 +00005412 UInt lineszB = guest_archinfo->ppc_cache_line_szB;
ceriond953ebb2005-11-29 13:27:20 +00005413
5414 IRType ty = mode64 ? Ity_I64 : Ity_I32;
cerion094d1392005-06-20 13:45:57 +00005415
sewardj6be67232006-01-24 19:00:05 +00005416 /* For dcbt, the lowest two bits of b21to25 encode an
5417 access-direction hint (TH field) which we ignore. Well, that's
5418 what the PowerPC documentation says. In fact xlc -O4 on POWER5
5419 seems to generate values of 8 and 10 for b21to25. */
5420 if (opc1 == 0x1F && opc2 == 0x116) {
5421 /* b21to25 &= ~3; */ /* if the docs were true */
5422 b21to25 = 0; /* blunt instrument */
5423 }
5424
cerionb85e8bb2005-02-16 08:54:33 +00005425 if (opc1 != 0x1F || b21to25 != 0 || b0 != 0) {
sewardj6be67232006-01-24 19:00:05 +00005426 if (0) vex_printf("dis_cache_manage %d %d %d\n",
5427 (Int)opc1, (Int)b21to25, (Int)b0);
cerion5b2325f2005-12-23 00:55:09 +00005428 vex_printf("dis_cache_manage(ppc)(opc1|b21to25|b0)\n");
cerionb85e8bb2005-02-16 08:54:33 +00005429 return False;
5430 }
sewardjd94b73a2005-06-30 12:08:48 +00005431
5432 /* stay sane .. */
5433 vassert(lineszB == 32 || lineszB == 128);
cerionb85e8bb2005-02-16 08:54:33 +00005434
5435 switch (opc2) {
sewardjb51f0f42005-07-18 11:38:02 +00005436//zz case 0x2F6: // dcba (Data Cache Block Allocate, PPC32 p380)
5437//zz vassert(0); /* AWAITING TEST CASE */
ceriond953ebb2005-11-29 13:27:20 +00005438//zz DIP("dcba r%u,r%u\n", rA_addr, rB_addr);
cerion5b2325f2005-12-23 00:55:09 +00005439//zz if (0) vex_printf("vex ppc->IR: kludged dcba\n");
sewardjb51f0f42005-07-18 11:38:02 +00005440//zz break;
sewardj20ef5472005-07-21 14:48:31 +00005441
5442 case 0x056: // dcbf (Data Cache Block Flush, PPC32 p382)
ceriond953ebb2005-11-29 13:27:20 +00005443 DIP("dcbf r%u,r%u\n", rA_addr, rB_addr);
sewardj20ef5472005-07-21 14:48:31 +00005444 /* nop as far as vex is concerned */
sewardj20ef5472005-07-21 14:48:31 +00005445 break;
cerionb85e8bb2005-02-16 08:54:33 +00005446
cerione9d361a2005-03-04 17:35:29 +00005447 case 0x036: // dcbst (Data Cache Block Store, PPC32 p384)
ceriond953ebb2005-11-29 13:27:20 +00005448 DIP("dcbst r%u,r%u\n", rA_addr, rB_addr);
sewardjb51f0f42005-07-18 11:38:02 +00005449 /* nop as far as vex is concerned */
cerionb85e8bb2005-02-16 08:54:33 +00005450 break;
cerion8c3adda2005-01-31 11:54:05 +00005451
cerione9d361a2005-03-04 17:35:29 +00005452 case 0x116: // dcbt (Data Cache Block Touch, PPC32 p385)
ceriond953ebb2005-11-29 13:27:20 +00005453 DIP("dcbt r%u,r%u\n", rA_addr, rB_addr);
sewardjb51f0f42005-07-18 11:38:02 +00005454 /* nop as far as vex is concerned */
cerionb85e8bb2005-02-16 08:54:33 +00005455 break;
5456
cerione9d361a2005-03-04 17:35:29 +00005457 case 0x0F6: // dcbtst (Data Cache Block Touch for Store, PPC32 p386)
ceriond953ebb2005-11-29 13:27:20 +00005458 DIP("dcbtst r%u,r%u\n", rA_addr, rB_addr);
sewardjb51f0f42005-07-18 11:38:02 +00005459 /* nop as far as vex is concerned */
cerionb85e8bb2005-02-16 08:54:33 +00005460 break;
5461
cerion094d1392005-06-20 13:45:57 +00005462 case 0x3F6: { // dcbz (Data Cache Block Clear to Zero, PPC32 p387)
sewardjd94b73a2005-06-30 12:08:48 +00005463 /* Clear all bytes in cache block at (rA|0) + rB. */
cerion2831b002005-11-30 19:55:22 +00005464 IRTemp EA = newTemp(ty);
5465 IRTemp addr = newTemp(ty);
cerion094d1392005-06-20 13:45:57 +00005466 IRExpr* irx_addr;
cerion094d1392005-06-20 13:45:57 +00005467 UInt i;
ceriond953ebb2005-11-29 13:27:20 +00005468 DIP("dcbz r%u,r%u\n", rA_addr, rB_addr);
sewardjcb1f68e2005-12-30 03:39:14 +00005469
ceriond953ebb2005-11-29 13:27:20 +00005470 assign( EA, ea_rAor0_idxd(rA_addr, rB_addr) );
cerion094d1392005-06-20 13:45:57 +00005471
cerion2831b002005-11-30 19:55:22 +00005472 if (mode64) {
5473 /* Round EA down to the start of the containing block. */
5474 assign( addr, binop( Iop_And64,
5475 mkexpr(EA),
5476 mkU64( ~((ULong)lineszB-1) )) );
5477
5478 for (i = 0; i < lineszB / 8; i++) {
5479 irx_addr = binop( Iop_Add64, mkexpr(addr), mkU64(i*8) );
5480 storeBE( irx_addr, mkU64(0) );
5481 }
5482 } else {
5483 /* Round EA down to the start of the containing block. */
5484 assign( addr, binop( Iop_And32,
5485 mkexpr(EA),
5486 mkU32( ~(lineszB-1) )) );
5487
5488 for (i = 0; i < lineszB / 4; i++) {
5489 irx_addr = binop( Iop_Add32, mkexpr(addr), mkU32(i*4) );
5490 storeBE( irx_addr, mkU32(0) );
5491 }
cerion094d1392005-06-20 13:45:57 +00005492 }
cerionb85e8bb2005-02-16 08:54:33 +00005493 break;
cerion094d1392005-06-20 13:45:57 +00005494 }
cerion8c3adda2005-01-31 11:54:05 +00005495
sewardj7ce9d152005-03-15 16:54:13 +00005496 case 0x3D6: {
5497 // icbi (Instruction Cache Block Invalidate, PPC32 p431)
5498 /* Invalidate all translations containing code from the cache
sewardjd94b73a2005-06-30 12:08:48 +00005499 block at (rA|0) + rB. */
ceriond953ebb2005-11-29 13:27:20 +00005500 IRTemp EA = newTemp(ty);
5501 IRTemp addr = newTemp(ty);
5502 DIP("icbi r%u,r%u\n", rA_addr, rB_addr);
5503 assign( EA, ea_rAor0_idxd(rA_addr, rB_addr) );
sewardj7ce9d152005-03-15 16:54:13 +00005504
cerion2831b002005-11-30 19:55:22 +00005505 /* Round EA down to the start of the containing block. */
5506 assign( addr, binop( mkSzOp(ty, Iop_And8),
ceriond953ebb2005-11-29 13:27:20 +00005507 mkexpr(EA),
cerion2831b002005-11-30 19:55:22 +00005508 mkSzImm(ty, ~(((ULong)lineszB)-1) )) );
ceriond953ebb2005-11-29 13:27:20 +00005509 putGST( PPC_GST_TISTART, mkexpr(addr) );
cerion2831b002005-11-30 19:55:22 +00005510 putGST( PPC_GST_TILEN, mkSzImm(ty, lineszB) );
sewardj7ce9d152005-03-15 16:54:13 +00005511
sewardja8078f62005-03-15 18:27:40 +00005512 /* be paranoid ... */
5513 stmt( IRStmt_MFence() );
5514
sewardj7ce9d152005-03-15 16:54:13 +00005515 irbb->jumpkind = Ijk_TInval;
cerion2831b002005-11-30 19:55:22 +00005516 irbb->next = mkSzImm(ty, nextInsnAddr());
sewardj9e6491a2005-07-02 19:24:10 +00005517 dres->whatNext = Dis_StopHere;
cerionb85e8bb2005-02-16 08:54:33 +00005518 break;
sewardj7ce9d152005-03-15 16:54:13 +00005519 }
cerion8c3adda2005-01-31 11:54:05 +00005520
cerionb85e8bb2005-02-16 08:54:33 +00005521 default:
cerion5b2325f2005-12-23 00:55:09 +00005522 vex_printf("dis_cache_manage(ppc)(opc2)\n");
cerionb85e8bb2005-02-16 08:54:33 +00005523 return False;
5524 }
5525 return True;
cerion8c3adda2005-01-31 11:54:05 +00005526}
5527
5528
sewardje14bb9f2005-07-22 09:39:02 +00005529/*------------------------------------------------------------*/
5530/*--- Floating Point Helpers ---*/
5531/*------------------------------------------------------------*/
5532
sewardje14bb9f2005-07-22 09:39:02 +00005533/* --------- Synthesise a 2-bit FPU rounding mode. --------- */
5534/* Produces a value in 0 .. 3, which is encoded as per the type
cerion5b2325f2005-12-23 00:55:09 +00005535 IRRoundingMode. PPCRoundingMode encoding is different to
sewardje14bb9f2005-07-22 09:39:02 +00005536 IRRoundingMode, so need to map it.
5537*/
sewardjb183b852006-02-03 16:08:03 +00005538static IRExpr* /* :: Ity_I32 */ get_IR_roundingmode ( void )
sewardje14bb9f2005-07-22 09:39:02 +00005539{
5540/*
5541 rounding mode | PPC | IR
5542 ------------------------
5543 to nearest | 00 | 00
5544 to zero | 01 | 11
5545 to +infinity | 10 | 10
5546 to -infinity | 11 | 01
5547*/
5548 IRTemp rm_PPC32 = newTemp(Ity_I32);
ceriond953ebb2005-11-29 13:27:20 +00005549 assign( rm_PPC32, getGST_masked( PPC_GST_FPSCR, MASK_FPSCR_RN ) );
sewardje14bb9f2005-07-22 09:39:02 +00005550
5551 // rm_IR = XOR( rm_PPC32, (rm_PPC32 << 1) & 2)
sewardjb183b852006-02-03 16:08:03 +00005552 return binop( Iop_Xor32,
5553 mkexpr(rm_PPC32),
5554 binop( Iop_And32,
5555 binop(Iop_Shl32, mkexpr(rm_PPC32), mkU8(1)),
5556 mkU32(2) ));
sewardje14bb9f2005-07-22 09:39:02 +00005557}
cerion094d1392005-06-20 13:45:57 +00005558
5559
cerion3d870a32005-03-18 12:23:33 +00005560/*------------------------------------------------------------*/
5561/*--- Floating Point Instruction Translation ---*/
5562/*------------------------------------------------------------*/
5563
5564/*
5565 Floating Point Load Instructions
5566*/
5567static Bool dis_fp_load ( UInt theInstr )
5568{
cerion76de5cf2005-11-18 18:25:12 +00005569 /* X-Form, D-Form */
5570 UChar opc1 = ifieldOPC(theInstr);
5571 UChar frD_addr = ifieldRegDS(theInstr);
5572 UChar rA_addr = ifieldRegA(theInstr);
5573 UChar rB_addr = ifieldRegB(theInstr);
5574 UInt opc2 = ifieldOPClo10(theInstr);
5575 UChar b0 = ifieldBIT0(theInstr);
cerion2831b002005-11-30 19:55:22 +00005576 UInt uimm16 = ifieldUIMM16(theInstr);
cerion094d1392005-06-20 13:45:57 +00005577
cerion2831b002005-11-30 19:55:22 +00005578 Int simm16 = extend_s_16to32(uimm16);
5579 IRType ty = mode64 ? Ity_I64 : Ity_I32;
5580 IRTemp EA = newTemp(ty);
5581 IRTemp rA = newTemp(ty);
5582 IRTemp rB = newTemp(ty);
cerion094d1392005-06-20 13:45:57 +00005583
5584 assign( rA, getIReg(rA_addr) );
5585 assign( rB, getIReg(rB_addr) );
ceriond953ebb2005-11-29 13:27:20 +00005586
sewardjb183b852006-02-03 16:08:03 +00005587 /* These are completely straightforward from a rounding and status
5588 bits perspective: no rounding involved and no funny status or CR
5589 bits affected. */
cerion3d870a32005-03-18 12:23:33 +00005590
sewardjb183b852006-02-03 16:08:03 +00005591 switch (opc1) {
sewardje14bb9f2005-07-22 09:39:02 +00005592 case 0x30: // lfs (Load Float Single, PPC32 p441)
ceriond953ebb2005-11-29 13:27:20 +00005593 DIP("lfs fr%u,%d(r%u)\n", frD_addr, simm16, rA_addr);
5594 assign( EA, ea_rAor0_simm(rA_addr, simm16) );
cerion5b2325f2005-12-23 00:55:09 +00005595 putFReg( frD_addr,
5596 unop(Iop_F32toF64, loadBE(Ity_F32, mkexpr(EA))) );
sewardje14bb9f2005-07-22 09:39:02 +00005597 break;
5598
cerion5b2325f2005-12-23 00:55:09 +00005599 case 0x31: // lfsu (Load Float Single, Update, PPC32 p442)
sewardjb183b852006-02-03 16:08:03 +00005600 if (rA_addr == 0)
cerion729edb72005-12-02 16:03:46 +00005601 return False;
cerion729edb72005-12-02 16:03:46 +00005602 DIP("lfsu fr%u,%d(r%u)\n", frD_addr, simm16, rA_addr);
5603 assign( EA, ea_rA_simm(rA_addr, simm16) );
cerion5b2325f2005-12-23 00:55:09 +00005604 putFReg( frD_addr,
5605 unop(Iop_F32toF64, loadBE(Ity_F32, mkexpr(EA))) );
cerion729edb72005-12-02 16:03:46 +00005606 putIReg( rA_addr, mkexpr(EA) );
5607 break;
cerion094d1392005-06-20 13:45:57 +00005608
5609 case 0x32: // lfd (Load Float Double, PPC32 p437)
ceriond953ebb2005-11-29 13:27:20 +00005610 DIP("lfd fr%u,%d(r%u)\n", frD_addr, simm16, rA_addr);
5611 assign( EA, ea_rAor0_simm(rA_addr, simm16) );
cerion094d1392005-06-20 13:45:57 +00005612 putFReg( frD_addr, loadBE(Ity_F64, mkexpr(EA)) );
5613 break;
cerion3d870a32005-03-18 12:23:33 +00005614
cerion5b2325f2005-12-23 00:55:09 +00005615 case 0x33: // lfdu (Load Float Double, Update, PPC32 p438)
sewardjb183b852006-02-03 16:08:03 +00005616 if (rA_addr == 0)
sewardj0e2cc672005-07-29 21:58:51 +00005617 return False;
ceriond953ebb2005-11-29 13:27:20 +00005618 DIP("lfdu fr%u,%d(r%u)\n", frD_addr, simm16, rA_addr);
5619 assign( EA, ea_rA_simm(rA_addr, simm16) );
sewardj0e2cc672005-07-29 21:58:51 +00005620 putFReg( frD_addr, loadBE(Ity_F64, mkexpr(EA)) );
5621 putIReg( rA_addr, mkexpr(EA) );
5622 break;
sewardje14bb9f2005-07-22 09:39:02 +00005623
5624 case 0x1F:
5625 if (b0 != 0) {
cerion5b2325f2005-12-23 00:55:09 +00005626 vex_printf("dis_fp_load(ppc)(instr,b0)\n");
sewardje14bb9f2005-07-22 09:39:02 +00005627 return False;
5628 }
5629
5630 switch(opc2) {
ceriond953ebb2005-11-29 13:27:20 +00005631 case 0x217: // lfsx (Load Float Single Indexed, PPC32 p444)
5632 DIP("lfsx fr%u,r%u,r%u\n", frD_addr, rA_addr, rB_addr);
5633 assign( EA, ea_rAor0_idxd(rA_addr, rB_addr) );
5634 putFReg( frD_addr, unop( Iop_F32toF64,
5635 loadBE(Ity_F32, mkexpr(EA))) );
5636 break;
5637
cerion5b2325f2005-12-23 00:55:09 +00005638 case 0x237: // lfsux (Load Float Single, Update Indxd, PPC32 p443)
sewardjb183b852006-02-03 16:08:03 +00005639 if (rA_addr == 0)
sewardje14bb9f2005-07-22 09:39:02 +00005640 return False;
ceriond953ebb2005-11-29 13:27:20 +00005641 DIP("lfsux fr%u,r%u,r%u\n", frD_addr, rA_addr, rB_addr);
5642 assign( EA, ea_rA_idxd(rA_addr, rB_addr) );
cerion5b2325f2005-12-23 00:55:09 +00005643 putFReg( frD_addr,
5644 unop(Iop_F32toF64, loadBE(Ity_F32, mkexpr(EA))) );
ceriond953ebb2005-11-29 13:27:20 +00005645 putIReg( rA_addr, mkexpr(EA) );
5646 break;
5647
5648 case 0x257: // lfdx (Load Float Double Indexed, PPC32 p440)
5649 DIP("lfdx fr%u,r%u,r%u\n", frD_addr, rA_addr, rB_addr);
5650 assign( EA, ea_rAor0_idxd(rA_addr, rB_addr) );
5651 putFReg( frD_addr, loadBE(Ity_F64, mkexpr(EA)) );
5652 break;
5653
cerion5b2325f2005-12-23 00:55:09 +00005654 case 0x277: // lfdux (Load Float Double, Update Indxd, PPC32 p439)
sewardjb183b852006-02-03 16:08:03 +00005655 if (rA_addr == 0)
ceriond953ebb2005-11-29 13:27:20 +00005656 return False;
ceriond953ebb2005-11-29 13:27:20 +00005657 DIP("lfdux fr%u,r%u,r%u\n", frD_addr, rA_addr, rB_addr);
5658 assign( EA, ea_rA_idxd(rA_addr, rB_addr) );
5659 putFReg( frD_addr, loadBE(Ity_F64, mkexpr(EA)) );
5660 putIReg( rA_addr, mkexpr(EA) );
5661 break;
5662
5663 default:
cerion5b2325f2005-12-23 00:55:09 +00005664 vex_printf("dis_fp_load(ppc)(opc2)\n");
ceriond953ebb2005-11-29 13:27:20 +00005665 return False;
sewardje14bb9f2005-07-22 09:39:02 +00005666 }
5667 break;
cerion3d870a32005-03-18 12:23:33 +00005668
5669 default:
cerion5b2325f2005-12-23 00:55:09 +00005670 vex_printf("dis_fp_load(ppc)(opc1)\n");
cerion3d870a32005-03-18 12:23:33 +00005671 return False;
5672 }
5673 return True;
5674}
5675
5676
5677
5678/*
5679 Floating Point Store Instructions
5680*/
5681static Bool dis_fp_store ( UInt theInstr )
5682{
cerion76de5cf2005-11-18 18:25:12 +00005683 /* X-Form, D-Form */
5684 UChar opc1 = ifieldOPC(theInstr);
5685 UChar frS_addr = ifieldRegDS(theInstr);
5686 UChar rA_addr = ifieldRegA(theInstr);
5687 UChar rB_addr = ifieldRegB(theInstr);
5688 UInt opc2 = ifieldOPClo10(theInstr);
5689 UChar b0 = ifieldBIT0(theInstr);
ceriond953ebb2005-11-29 13:27:20 +00005690 Int uimm16 = ifieldUIMM16(theInstr);
cerion094d1392005-06-20 13:45:57 +00005691
cerion2831b002005-11-30 19:55:22 +00005692 Int simm16 = extend_s_16to32(uimm16);
5693 IRTemp frS = newTemp(Ity_F64);
5694 IRType ty = mode64 ? Ity_I64 : Ity_I32;
5695 IRTemp EA = newTemp(ty);
5696 IRTemp rA = newTemp(ty);
5697 IRTemp rB = newTemp(ty);
cerion094d1392005-06-20 13:45:57 +00005698
5699 assign( frS, getFReg(frS_addr) );
cerionedf7fc52005-11-18 20:57:41 +00005700 assign( rA, getIReg(rA_addr) );
5701 assign( rB, getIReg(rB_addr) );
cerion3d870a32005-03-18 12:23:33 +00005702
sewardjb183b852006-02-03 16:08:03 +00005703 /* These are straightforward from a status bits perspective: no
5704 funny status or CR bits affected. For single precision stores,
5705 the values are truncated and denormalised (not rounded) to turn
5706 them into single precision values. */
5707
5708 switch (opc1) {
sewardje14bb9f2005-07-22 09:39:02 +00005709
5710 case 0x34: // stfs (Store Float Single, PPC32 p518)
ceriond953ebb2005-11-29 13:27:20 +00005711 DIP("stfs fr%u,%d(r%u)\n", frS_addr, simm16, rA_addr);
5712 assign( EA, ea_rAor0_simm(rA_addr, simm16) );
sewardjb183b852006-02-03 16:08:03 +00005713 /* Use Iop_TruncF64asF32 to truncate and possible denormalise
5714 the value to be stored in the correct way, without any
5715 rounding. */
sewardje14bb9f2005-07-22 09:39:02 +00005716 storeBE( mkexpr(EA),
sewardjb183b852006-02-03 16:08:03 +00005717 unop(Iop_TruncF64asF32, mkexpr(frS)) );
sewardje14bb9f2005-07-22 09:39:02 +00005718 break;
5719
cerion5b2325f2005-12-23 00:55:09 +00005720 case 0x35: // stfsu (Store Float Single, Update, PPC32 p519)
sewardjb183b852006-02-03 16:08:03 +00005721 if (rA_addr == 0)
cerion729edb72005-12-02 16:03:46 +00005722 return False;
cerion729edb72005-12-02 16:03:46 +00005723 DIP("stfsu fr%u,%d(r%u)\n", frS_addr, simm16, rA_addr);
5724 assign( EA, ea_rA_simm(rA_addr, simm16) );
sewardjb183b852006-02-03 16:08:03 +00005725 /* See comment for stfs */
cerion729edb72005-12-02 16:03:46 +00005726 storeBE( mkexpr(EA),
sewardjb183b852006-02-03 16:08:03 +00005727 unop(Iop_TruncF64asF32, mkexpr(frS)) );
cerion729edb72005-12-02 16:03:46 +00005728 putIReg( rA_addr, mkexpr(EA) );
5729 break;
cerion3d870a32005-03-18 12:23:33 +00005730
cerion094d1392005-06-20 13:45:57 +00005731 case 0x36: // stfd (Store Float Double, PPC32 p513)
ceriond953ebb2005-11-29 13:27:20 +00005732 DIP("stfd fr%u,%d(r%u)\n", frS_addr, simm16, rA_addr);
5733 assign( EA, ea_rAor0_simm(rA_addr, simm16) );
cerion094d1392005-06-20 13:45:57 +00005734 storeBE( mkexpr(EA), mkexpr(frS) );
5735 break;
cerion3d870a32005-03-18 12:23:33 +00005736
cerion5b2325f2005-12-23 00:55:09 +00005737 case 0x37: // stfdu (Store Float Double, Update, PPC32 p514)
sewardjb183b852006-02-03 16:08:03 +00005738 if (rA_addr == 0)
sewardje14bb9f2005-07-22 09:39:02 +00005739 return False;
ceriond953ebb2005-11-29 13:27:20 +00005740 DIP("stfdu fr%u,%d(r%u)\n", frS_addr, simm16, rA_addr);
5741 assign( EA, ea_rA_simm(rA_addr, simm16) );
sewardje14bb9f2005-07-22 09:39:02 +00005742 storeBE( mkexpr(EA), mkexpr(frS) );
5743 putIReg( rA_addr, mkexpr(EA) );
5744 break;
5745
5746 case 0x1F:
5747 if (b0 != 0) {
cerion5b2325f2005-12-23 00:55:09 +00005748 vex_printf("dis_fp_store(ppc)(instr,b0)\n");
sewardje14bb9f2005-07-22 09:39:02 +00005749 return False;
5750 }
sewardje14bb9f2005-07-22 09:39:02 +00005751 switch(opc2) {
ceriond953ebb2005-11-29 13:27:20 +00005752 case 0x297: // stfsx (Store Float Single Indexed, PPC32 p521)
5753 DIP("stfsx fr%u,r%u,r%u\n", frS_addr, rA_addr, rB_addr);
5754 assign( EA, ea_rAor0_idxd(rA_addr, rB_addr) );
sewardjb183b852006-02-03 16:08:03 +00005755 /* See note for stfs */
5756 storeBE( mkexpr(EA),
5757 unop(Iop_TruncF64asF32, mkexpr(frS)) );
ceriond953ebb2005-11-29 13:27:20 +00005758 break;
5759
cerion5b2325f2005-12-23 00:55:09 +00005760 case 0x2B7: // stfsux (Store Float Sgl, Update Indxd, PPC32 p520)
sewardjb183b852006-02-03 16:08:03 +00005761 if (rA_addr == 0)
cerion729edb72005-12-02 16:03:46 +00005762 return False;
cerion729edb72005-12-02 16:03:46 +00005763 DIP("stfsux fr%u,r%u,r%u\n", frS_addr, rA_addr, rB_addr);
5764 assign( EA, ea_rA_idxd(rA_addr, rB_addr) );
sewardjb183b852006-02-03 16:08:03 +00005765 /* See note for stfs */
5766 storeBE( mkexpr(EA),
5767 unop(Iop_TruncF64asF32, mkexpr(frS)) );
cerion729edb72005-12-02 16:03:46 +00005768 putIReg( rA_addr, mkexpr(EA) );
5769 break;
sewardje14bb9f2005-07-22 09:39:02 +00005770
ceriond953ebb2005-11-29 13:27:20 +00005771 case 0x2D7: // stfdx (Store Float Double Indexed, PPC32 p516)
5772 DIP("stfdx fr%u,r%u,r%u\n", frS_addr, rA_addr, rB_addr);
5773 assign( EA, ea_rAor0_idxd(rA_addr, rB_addr) );
5774 storeBE( mkexpr(EA), mkexpr(frS) );
5775 break;
sewardje14bb9f2005-07-22 09:39:02 +00005776
cerion5b2325f2005-12-23 00:55:09 +00005777 case 0x2F7: // stfdux (Store Float Dbl, Update Indxd, PPC32 p515)
sewardjb183b852006-02-03 16:08:03 +00005778 if (rA_addr == 0)
ceriond953ebb2005-11-29 13:27:20 +00005779 return False;
ceriond953ebb2005-11-29 13:27:20 +00005780 DIP("stfdux fr%u,r%u,r%u\n", frS_addr, rA_addr, rB_addr);
5781 assign( EA, ea_rA_idxd(rA_addr, rB_addr) );
5782 storeBE( mkexpr(EA), mkexpr(frS) );
5783 putIReg( rA_addr, mkexpr(EA) );
5784 break;
sewardj5f63c0c2005-09-09 10:36:55 +00005785
sewardj09e88d12006-01-27 16:05:49 +00005786 case 0x3D7: // stfiwx (Store Float as Int, Indexed, PPC32 p517)
sewardj5117ce12006-01-27 21:20:15 +00005787 // NOTE: POWERPC OPTIONAL, "Graphics Group" (PPC32_GX)
sewardj09e88d12006-01-27 16:05:49 +00005788 DIP("stfiwx fr%u,r%u,r%u\n", frS_addr, rA_addr, rB_addr);
5789 assign( EA, ea_rAor0_idxd(rA_addr, rB_addr) );
5790 storeBE( mkexpr(EA),
5791 unop(Iop_64to32, unop(Iop_ReinterpF64asI64, mkexpr(frS))) );
5792 break;
sewardje14bb9f2005-07-22 09:39:02 +00005793
ceriond953ebb2005-11-29 13:27:20 +00005794 default:
cerion5b2325f2005-12-23 00:55:09 +00005795 vex_printf("dis_fp_store(ppc)(opc2)\n");
ceriond953ebb2005-11-29 13:27:20 +00005796 return False;
sewardje14bb9f2005-07-22 09:39:02 +00005797 }
5798 break;
cerion3d870a32005-03-18 12:23:33 +00005799
5800 default:
cerion5b2325f2005-12-23 00:55:09 +00005801 vex_printf("dis_fp_store(ppc)(opc1)\n");
cerion3d870a32005-03-18 12:23:33 +00005802 return False;
5803 }
5804 return True;
5805}
5806
5807
5808
5809/*
5810 Floating Point Arith Instructions
5811*/
5812static Bool dis_fp_arith ( UInt theInstr )
5813{
5814 /* A-Form */
cerion76de5cf2005-11-18 18:25:12 +00005815 UChar opc1 = ifieldOPC(theInstr);
5816 UChar frD_addr = ifieldRegDS(theInstr);
5817 UChar frA_addr = ifieldRegA(theInstr);
5818 UChar frB_addr = ifieldRegB(theInstr);
5819 UChar frC_addr = ifieldRegC(theInstr);
5820 UChar opc2 = ifieldOPClo5(theInstr);
5821 UChar flag_rC = ifieldBIT0(theInstr);
cerion094d1392005-06-20 13:45:57 +00005822
sewardjb183b852006-02-03 16:08:03 +00005823 IRTemp frD = newTemp(Ity_F64);
5824 IRTemp frA = newTemp(Ity_F64);
5825 IRTemp frB = newTemp(Ity_F64);
5826 IRTemp frC = newTemp(Ity_F64);
5827 IRExpr* rm = get_IR_roundingmode();
5828
5829 /* By default, we will examine the results of the operation and set
5830 fpscr[FPRF] accordingly. */
5831 Bool set_FPRF = True;
5832
5833 /* By default, if flag_RC is set, we will clear cr1 after the
5834 operation. In reality we should set cr1 to indicate the
5835 exception status of the operation, but since we're not
5836 simulating exceptions, the exception status will appear to be
5837 zero. Hence cr1 should be cleared if this is a . form insn. */
5838 Bool clear_CR1 = True;
cerion094d1392005-06-20 13:45:57 +00005839
5840 assign( frA, getFReg(frA_addr));
5841 assign( frB, getFReg(frB_addr));
5842 assign( frC, getFReg(frC_addr));
cerion3d870a32005-03-18 12:23:33 +00005843
5844 switch (opc1) {
sewardje14bb9f2005-07-22 09:39:02 +00005845 case 0x3B:
5846 switch (opc2) {
5847 case 0x12: // fdivs (Floating Divide Single, PPC32 p407)
sewardjb183b852006-02-03 16:08:03 +00005848 if (frC_addr != 0)
sewardje14bb9f2005-07-22 09:39:02 +00005849 return False;
cerion5b2325f2005-12-23 00:55:09 +00005850 DIP("fdivs%s fr%u,fr%u,fr%u\n", flag_rC ? ".":"",
sewardje14bb9f2005-07-22 09:39:02 +00005851 frD_addr, frA_addr, frB_addr);
sewardjb183b852006-02-03 16:08:03 +00005852 assign( frD, triop( Iop_DivF64r32,
5853 rm, mkexpr(frA), mkexpr(frB) ));
sewardje14bb9f2005-07-22 09:39:02 +00005854 break;
5855
5856 case 0x14: // fsubs (Floating Subtract Single, PPC32 p430)
sewardjb183b852006-02-03 16:08:03 +00005857 if (frC_addr != 0)
sewardje14bb9f2005-07-22 09:39:02 +00005858 return False;
cerion5b2325f2005-12-23 00:55:09 +00005859 DIP("fsubs%s fr%u,fr%u,fr%u\n", flag_rC ? ".":"",
sewardje14bb9f2005-07-22 09:39:02 +00005860 frD_addr, frA_addr, frB_addr);
sewardjb183b852006-02-03 16:08:03 +00005861 assign( frD, triop( Iop_SubF64r32,
5862 rm, mkexpr(frA), mkexpr(frB) ));
sewardje14bb9f2005-07-22 09:39:02 +00005863 break;
5864
5865 case 0x15: // fadds (Floating Add Single, PPC32 p401)
sewardjb183b852006-02-03 16:08:03 +00005866 if (frC_addr != 0)
sewardje14bb9f2005-07-22 09:39:02 +00005867 return False;
cerion5b2325f2005-12-23 00:55:09 +00005868 DIP("fadds%s fr%u,fr%u,fr%u\n", flag_rC ? ".":"",
sewardje14bb9f2005-07-22 09:39:02 +00005869 frD_addr, frA_addr, frB_addr);
sewardjb183b852006-02-03 16:08:03 +00005870 assign( frD, triop( Iop_AddF64r32,
5871 rm, mkexpr(frA), mkexpr(frB) ));
sewardje14bb9f2005-07-22 09:39:02 +00005872 break;
5873
sewardj870c84b2006-01-24 03:33:43 +00005874 case 0x16: // fsqrts (Floating SqRt (Single-Precision), PPC32 p428)
sewardj5117ce12006-01-27 21:20:15 +00005875 // NOTE: POWERPC OPTIONAL, "General-Purpose Group" (PPC32_FX)
sewardjb183b852006-02-03 16:08:03 +00005876 if (frA_addr != 0 || frC_addr != 0)
sewardj870c84b2006-01-24 03:33:43 +00005877 return False;
sewardj870c84b2006-01-24 03:33:43 +00005878 DIP("fsqrts%s fr%u,fr%u\n", flag_rC ? ".":"",
5879 frD_addr, frB_addr);
sewardj79fd33f2006-01-29 17:07:57 +00005880 // however illogically, on ppc970 this insn behaves identically
sewardjb183b852006-02-03 16:08:03 +00005881 // to fsqrt (double-precision). So use SqrtF64, not SqrtF64r32.
5882 assign( frD, binop( Iop_SqrtF64, rm, mkexpr(frB) ));
sewardj870c84b2006-01-24 03:33:43 +00005883 break;
sewardje14bb9f2005-07-22 09:39:02 +00005884
sewardjbaf971a2006-01-27 15:09:35 +00005885 case 0x18: // fres (Floating Reciprocal Estimate Single, PPC32 p421)
sewardj5117ce12006-01-27 21:20:15 +00005886 // NOTE: POWERPC OPTIONAL, "Graphics Group" (PPC32_GX)
sewardjb183b852006-02-03 16:08:03 +00005887 if (frA_addr != 0 || frC_addr != 0)
sewardjbaf971a2006-01-27 15:09:35 +00005888 return False;
sewardjbaf971a2006-01-27 15:09:35 +00005889 DIP("fres%s fr%u,fr%u\n", flag_rC ? ".":"",
5890 frD_addr, frB_addr);
sewardj157b19b2006-01-31 16:32:25 +00005891 { IRExpr* ieee_one
5892 = IRExpr_Const(IRConst_F64i(0x3ff0000000000000ULL));
sewardjb183b852006-02-03 16:08:03 +00005893 assign( frD, triop( Iop_DivF64r32,
5894 rm,
5895 ieee_one, mkexpr(frB) ));
sewardj157b19b2006-01-31 16:32:25 +00005896 }
sewardjbaf971a2006-01-27 15:09:35 +00005897 break;
sewardje14bb9f2005-07-22 09:39:02 +00005898
5899 case 0x19: // fmuls (Floating Multiply Single, PPC32 p414)
sewardjb183b852006-02-03 16:08:03 +00005900 if (frB_addr != 0)
sewardje14bb9f2005-07-22 09:39:02 +00005901 return False;
cerion5b2325f2005-12-23 00:55:09 +00005902 DIP("fmuls%s fr%u,fr%u,fr%u\n", flag_rC ? ".":"",
sewardje14bb9f2005-07-22 09:39:02 +00005903 frD_addr, frA_addr, frC_addr);
sewardjb183b852006-02-03 16:08:03 +00005904 assign( frD, triop( Iop_MulF64r32,
5905 rm, mkexpr(frA), mkexpr(frC) ));
sewardje14bb9f2005-07-22 09:39:02 +00005906 break;
5907
sewardj79fd33f2006-01-29 17:07:57 +00005908 case 0x1A: // frsqrtes (Floating Recip SqRt Est Single)
5909 // NOTE: POWERPC OPTIONAL, "Graphics Group" (PPC32_GX)
5910 // Undocumented instruction?
sewardjb183b852006-02-03 16:08:03 +00005911 if (frA_addr != 0 || frC_addr != 0)
sewardj79fd33f2006-01-29 17:07:57 +00005912 return False;
sewardj79fd33f2006-01-29 17:07:57 +00005913 DIP("frsqrtes%s fr%u,fr%u\n", flag_rC ? ".":"",
5914 frD_addr, frB_addr);
5915 assign( frD, unop(Iop_Est5FRSqrt, mkexpr(frB)) );
5916 break;
5917
sewardje14bb9f2005-07-22 09:39:02 +00005918 default:
cerion5b2325f2005-12-23 00:55:09 +00005919 vex_printf("dis_fp_arith(ppc)(3B: opc2)\n");
sewardje14bb9f2005-07-22 09:39:02 +00005920 return False;
5921 }
5922 break;
cerion094d1392005-06-20 13:45:57 +00005923
cerion3d870a32005-03-18 12:23:33 +00005924 case 0x3F:
5925 switch (opc2) {
cerion5b2325f2005-12-23 00:55:09 +00005926 case 0x12: // fdiv (Floating Div (Double-Precision), PPC32 p406)
sewardjb183b852006-02-03 16:08:03 +00005927 if (frC_addr != 0)
sewardje14bb9f2005-07-22 09:39:02 +00005928 return False;
cerion5b2325f2005-12-23 00:55:09 +00005929 DIP("fdiv%s fr%u,fr%u,fr%u\n", flag_rC ? ".":"",
sewardje14bb9f2005-07-22 09:39:02 +00005930 frD_addr, frA_addr, frB_addr);
sewardjb183b852006-02-03 16:08:03 +00005931 assign( frD, triop(Iop_DivF64, rm, mkexpr(frA), mkexpr(frB)) );
sewardje14bb9f2005-07-22 09:39:02 +00005932 break;
5933
cerion5b2325f2005-12-23 00:55:09 +00005934 case 0x14: // fsub (Floating Sub (Double-Precision), PPC32 p429)
sewardjb183b852006-02-03 16:08:03 +00005935 if (frC_addr != 0)
sewardje14bb9f2005-07-22 09:39:02 +00005936 return False;
cerion5b2325f2005-12-23 00:55:09 +00005937 DIP("fsub%s fr%u,fr%u,fr%u\n", flag_rC ? ".":"",
sewardje14bb9f2005-07-22 09:39:02 +00005938 frD_addr, frA_addr, frB_addr);
sewardjb183b852006-02-03 16:08:03 +00005939 assign( frD, triop(Iop_SubF64, rm, mkexpr(frA), mkexpr(frB)) );
sewardje14bb9f2005-07-22 09:39:02 +00005940 break;
cerion3d870a32005-03-18 12:23:33 +00005941
5942 case 0x15: // fadd (Floating Add (Double-Precision), PPC32 p400)
sewardjb183b852006-02-03 16:08:03 +00005943 if (frC_addr != 0)
cerion3d870a32005-03-18 12:23:33 +00005944 return False;
cerion5b2325f2005-12-23 00:55:09 +00005945 DIP("fadd%s fr%u,fr%u,fr%u\n", flag_rC ? ".":"",
cerion3d870a32005-03-18 12:23:33 +00005946 frD_addr, frA_addr, frB_addr);
sewardjb183b852006-02-03 16:08:03 +00005947 assign( frD, triop(Iop_AddF64, rm, mkexpr(frA), mkexpr(frB)) );
cerion094d1392005-06-20 13:45:57 +00005948 break;
cerion3d870a32005-03-18 12:23:33 +00005949
cerion876ef412005-12-14 22:00:53 +00005950 case 0x16: // fsqrt (Floating SqRt (Double-Precision), PPC32 p427)
sewardj5117ce12006-01-27 21:20:15 +00005951 // NOTE: POWERPC OPTIONAL, "General-Purpose Group" (PPC32_FX)
sewardjb183b852006-02-03 16:08:03 +00005952 if (frA_addr != 0 || frC_addr != 0)
cerion876ef412005-12-14 22:00:53 +00005953 return False;
cerion5b2325f2005-12-23 00:55:09 +00005954 DIP("fsqrt%s fr%u,fr%u\n", flag_rC ? ".":"",
cerion876ef412005-12-14 22:00:53 +00005955 frD_addr, frB_addr);
sewardjb183b852006-02-03 16:08:03 +00005956 assign( frD, binop(Iop_SqrtF64, rm, mkexpr(frB)) );
cerion876ef412005-12-14 22:00:53 +00005957 break;
sewardje14bb9f2005-07-22 09:39:02 +00005958
5959 case 0x17: { // fsel (Floating Select, PPC32 p426)
sewardj5117ce12006-01-27 21:20:15 +00005960 // NOTE: POWERPC OPTIONAL, "Graphics Group" (PPC32_GX)
sewardje14bb9f2005-07-22 09:39:02 +00005961 IRTemp cc = newTemp(Ity_I32);
5962 IRTemp cc_b0 = newTemp(Ity_I32);
5963
cerion5b2325f2005-12-23 00:55:09 +00005964 DIP("fsel%s fr%u,fr%u,fr%u,fr%u\n", flag_rC ? ".":"",
sewardje14bb9f2005-07-22 09:39:02 +00005965 frD_addr, frA_addr, frC_addr, frB_addr);
5966
5967 // cc: UN == 0x41, LT == 0x01, GT == 0x00, EQ == 0x40
5968 // => GT|EQ == (cc & 0x1 == 0)
cerion5b2325f2005-12-23 00:55:09 +00005969 assign( cc, binop(Iop_CmpF64, mkexpr(frA),
5970 IRExpr_Const(IRConst_F64(0))) );
sewardje14bb9f2005-07-22 09:39:02 +00005971 assign( cc_b0, binop(Iop_And32, mkexpr(cc), mkU32(1)) );
5972
5973 // frD = (frA >= 0.0) ? frC : frB
5974 // = (cc_b0 == 0) ? frC : frB
5975 assign( frD,
5976 IRExpr_Mux0X(
5977 unop(Iop_1Uto8,
5978 binop(Iop_CmpEQ32, mkexpr(cc_b0), mkU32(0))),
5979 mkexpr(frB),
5980 mkexpr(frC) ));
sewardjb183b852006-02-03 16:08:03 +00005981
5982 /* One of the rare ones which don't mess with FPRF */
5983 set_FPRF = False;
sewardje14bb9f2005-07-22 09:39:02 +00005984 break;
5985 }
5986
sewardj79fd33f2006-01-29 17:07:57 +00005987 case 0x18: // fre (Floating Reciprocal Estimate)
5988 // NOTE: POWERPC OPTIONAL, "Graphics Group" (PPC32_GX)
5989 // Note: unclear whether this insn really exists or not
5990 // ppc970 doesn't have it, but POWER5 does
sewardjb183b852006-02-03 16:08:03 +00005991 if (frA_addr != 0 || frC_addr != 0)
sewardj79fd33f2006-01-29 17:07:57 +00005992 return False;
sewardj79fd33f2006-01-29 17:07:57 +00005993 DIP("fre%s fr%u,fr%u\n", flag_rC ? ".":"",
5994 frD_addr, frB_addr);
sewardj157b19b2006-01-31 16:32:25 +00005995 { IRExpr* ieee_one
5996 = IRExpr_Const(IRConst_F64i(0x3ff0000000000000ULL));
sewardjb183b852006-02-03 16:08:03 +00005997 assign( frD, triop( Iop_DivF64,
sewardj56de4212006-02-06 22:19:17 +00005998 rm,
sewardjb183b852006-02-03 16:08:03 +00005999 ieee_one, mkexpr(frB) ));
sewardj157b19b2006-01-31 16:32:25 +00006000 }
sewardj79fd33f2006-01-29 17:07:57 +00006001 break;
6002
cerion5b2325f2005-12-23 00:55:09 +00006003 case 0x19: // fmul (Floating Mult (Double Precision), PPC32 p413)
sewardjb183b852006-02-03 16:08:03 +00006004 if (frB_addr != 0)
cerion5b2325f2005-12-23 00:55:09 +00006005 vex_printf("dis_fp_arith(ppc)(instr,fmul)\n");
cerion5b2325f2005-12-23 00:55:09 +00006006 DIP("fmul%s fr%u,fr%u,fr%u\n", flag_rC ? ".":"",
sewardje14bb9f2005-07-22 09:39:02 +00006007 frD_addr, frA_addr, frC_addr);
sewardjb183b852006-02-03 16:08:03 +00006008 assign( frD, triop(Iop_MulF64, rm, mkexpr(frA), mkexpr(frC)) );
sewardje14bb9f2005-07-22 09:39:02 +00006009 break;
6010
sewardjbaf971a2006-01-27 15:09:35 +00006011 case 0x1A: // frsqrte (Floating Recip SqRt Est., PPC32 p424)
sewardj5117ce12006-01-27 21:20:15 +00006012 // NOTE: POWERPC OPTIONAL, "Graphics Group" (PPC32_GX)
sewardjb183b852006-02-03 16:08:03 +00006013 if (frA_addr != 0 || frC_addr != 0)
sewardjbaf971a2006-01-27 15:09:35 +00006014 return False;
sewardjbaf971a2006-01-27 15:09:35 +00006015 DIP("frsqrte%s fr%u,fr%u\n", flag_rC ? ".":"",
6016 frD_addr, frB_addr);
6017 assign( frD, unop(Iop_Est5FRSqrt, mkexpr(frB)) );
6018 break;
cerion3d870a32005-03-18 12:23:33 +00006019
6020 default:
cerion5b2325f2005-12-23 00:55:09 +00006021 vex_printf("dis_fp_arith(ppc)(3F: opc2)\n");
cerion3d870a32005-03-18 12:23:33 +00006022 return False;
6023 }
cerion094d1392005-06-20 13:45:57 +00006024 break;
6025
cerion3d870a32005-03-18 12:23:33 +00006026 default:
cerion5b2325f2005-12-23 00:55:09 +00006027 vex_printf("dis_fp_arith(ppc)(opc1)\n");
cerion3d870a32005-03-18 12:23:33 +00006028 return False;
6029 }
cerion094d1392005-06-20 13:45:57 +00006030
6031 putFReg( frD_addr, mkexpr(frD) );
sewardjb183b852006-02-03 16:08:03 +00006032
6033 if (set_FPRF) {
6034 // XXX XXX XXX FIXME
6035 // set FPRF from frD
6036 }
6037
6038 if (flag_rC && clear_CR1) {
6039 putCR321( 1, mkU8(0) );
6040 putCR0( 1, mkU8(0) );
6041 }
6042
cerion3d870a32005-03-18 12:23:33 +00006043 return True;
6044}
6045
6046
6047
sewardje14bb9f2005-07-22 09:39:02 +00006048/*
6049 Floating Point Mult-Add Instructions
6050*/
6051static Bool dis_fp_multadd ( UInt theInstr )
6052{
6053 /* A-Form */
cerion76de5cf2005-11-18 18:25:12 +00006054 UChar opc1 = ifieldOPC(theInstr);
6055 UChar frD_addr = ifieldRegDS(theInstr);
6056 UChar frA_addr = ifieldRegA(theInstr);
6057 UChar frB_addr = ifieldRegB(theInstr);
6058 UChar frC_addr = ifieldRegC(theInstr);
6059 UChar opc2 = ifieldOPClo5(theInstr);
6060 UChar flag_rC = ifieldBIT0(theInstr);
sewardje14bb9f2005-07-22 09:39:02 +00006061
sewardjb183b852006-02-03 16:08:03 +00006062 IRTemp frD = newTemp(Ity_F64);
6063 IRTemp frA = newTemp(Ity_F64);
6064 IRTemp frB = newTemp(Ity_F64);
6065 IRTemp frC = newTemp(Ity_F64);
6066 IRTemp rmt = newTemp(Ity_I32);
6067 IRExpr* rm;
6068
6069 /* By default, we will examine the results of the operation and set
6070 fpscr[FPRF] accordingly. */
6071 Bool set_FPRF = True;
6072
6073 /* By default, if flag_RC is set, we will clear cr1 after the
6074 operation. In reality we should set cr1 to indicate the
6075 exception status of the operation, but since we're not
6076 simulating exceptions, the exception status will appear to be
6077 zero. Hence cr1 should be cleared if this is a . form insn. */
6078 Bool clear_CR1 = True;
6079
6080 /* Bind the rounding mode expression to a temp; there's no
6081 point in creating gratuitous CSEs, as we know we'll need
6082 to use it twice. */
6083 assign( rmt, get_IR_roundingmode() );
6084 rm = mkexpr(rmt);
sewardje14bb9f2005-07-22 09:39:02 +00006085
6086 assign( frA, getFReg(frA_addr));
6087 assign( frB, getFReg(frB_addr));
6088 assign( frC, getFReg(frC_addr));
6089
sewardjb183b852006-02-03 16:08:03 +00006090 /* The rounding in this is all a bit dodgy. The idea is to only do
6091 one rounding. That clearly isn't achieveable without dedicated
6092 four-input IR primops, although in the single precision case we
6093 can sort-of simulate it by doing the inner multiply in double
6094 precision.
6095
6096 In the negated cases, the negation happens after rounding. */
6097
sewardje14bb9f2005-07-22 09:39:02 +00006098 switch (opc1) {
6099 case 0x3B:
6100 switch (opc2) {
6101 case 0x1C: // fmsubs (Floating Mult-Subtr Single, PPC32 p412)
cerion5b2325f2005-12-23 00:55:09 +00006102 DIP("fmsubs%s fr%u,fr%u,fr%u,fr%u\n", flag_rC ? ".":"",
sewardje14bb9f2005-07-22 09:39:02 +00006103 frD_addr, frA_addr, frC_addr, frB_addr);
sewardj40c80262006-02-08 19:30:46 +00006104 assign( frD, qop( Iop_MSubF64r32, rm,
6105 mkexpr(frA), mkexpr(frC), mkexpr(frB) ));
sewardjb183b852006-02-03 16:08:03 +00006106 break;
sewardje14bb9f2005-07-22 09:39:02 +00006107
6108 case 0x1D: // fmadds (Floating Mult-Add Single, PPC32 p409)
cerion5b2325f2005-12-23 00:55:09 +00006109 DIP("fmadds%s fr%u,fr%u,fr%u,fr%u\n", flag_rC ? ".":"",
sewardje14bb9f2005-07-22 09:39:02 +00006110 frD_addr, frA_addr, frC_addr, frB_addr);
sewardj40c80262006-02-08 19:30:46 +00006111 assign( frD, qop( Iop_MAddF64r32, rm,
6112 mkexpr(frA), mkexpr(frC), mkexpr(frB) ));
sewardje14bb9f2005-07-22 09:39:02 +00006113 break;
6114
6115 case 0x1E: // fnmsubs (Float Neg Mult-Subtr Single, PPC32 p420)
cerion5b2325f2005-12-23 00:55:09 +00006116 DIP("fnmsubs%s fr%u,fr%u,fr%u,fr%u\n", flag_rC ? ".":"",
sewardje14bb9f2005-07-22 09:39:02 +00006117 frD_addr, frA_addr, frC_addr, frB_addr);
sewardjb183b852006-02-03 16:08:03 +00006118 assign( frD, unop( Iop_NegF64,
sewardj40c80262006-02-08 19:30:46 +00006119 qop( Iop_MSubF64r32, rm,
6120 mkexpr(frA), mkexpr(frC), mkexpr(frB) )));
sewardje14bb9f2005-07-22 09:39:02 +00006121 break;
6122
6123 case 0x1F: // fnmadds (Floating Negative Multiply-Add Single, PPC32 p418)
cerion5b2325f2005-12-23 00:55:09 +00006124 DIP("fnmadds%s fr%u,fr%u,fr%u,fr%u\n", flag_rC ? ".":"",
sewardje14bb9f2005-07-22 09:39:02 +00006125 frD_addr, frA_addr, frC_addr, frB_addr);
sewardjb183b852006-02-03 16:08:03 +00006126 assign( frD, unop( Iop_NegF64,
sewardj40c80262006-02-08 19:30:46 +00006127 qop( Iop_MAddF64r32, rm,
6128 mkexpr(frA), mkexpr(frC), mkexpr(frB) )));
sewardje14bb9f2005-07-22 09:39:02 +00006129 break;
6130
6131 default:
cerion5b2325f2005-12-23 00:55:09 +00006132 vex_printf("dis_fp_multadd(ppc)(3B: opc2)\n");
sewardje14bb9f2005-07-22 09:39:02 +00006133 return False;
6134 }
6135 break;
6136
6137 case 0x3F:
6138 switch (opc2) {
cerion5b2325f2005-12-23 00:55:09 +00006139 case 0x1C: // fmsub (Float Mult-Sub (Dbl Precision), PPC32 p411)
6140 DIP("fmsub%s fr%u,fr%u,fr%u,fr%u\n", flag_rC ? ".":"",
sewardje14bb9f2005-07-22 09:39:02 +00006141 frD_addr, frA_addr, frC_addr, frB_addr);
sewardj40c80262006-02-08 19:30:46 +00006142 assign( frD, qop( Iop_MSubF64, rm,
6143 mkexpr(frA), mkexpr(frC), mkexpr(frB) ));
sewardje14bb9f2005-07-22 09:39:02 +00006144 break;
6145
cerion5b2325f2005-12-23 00:55:09 +00006146 case 0x1D: // fmadd (Float Mult-Add (Dbl Precision), PPC32 p408)
6147 DIP("fmadd%s fr%u,fr%u,fr%u,fr%u\n", flag_rC ? ".":"",
sewardje14bb9f2005-07-22 09:39:02 +00006148 frD_addr, frA_addr, frC_addr, frB_addr);
sewardj40c80262006-02-08 19:30:46 +00006149 assign( frD, qop( Iop_MAddF64, rm,
6150 mkexpr(frA), mkexpr(frC), mkexpr(frB) ));
sewardje14bb9f2005-07-22 09:39:02 +00006151 break;
6152
cerion5b2325f2005-12-23 00:55:09 +00006153 case 0x1E: // fnmsub (Float Neg Mult-Subtr (Dbl Precision), PPC32 p419)
6154 DIP("fnmsub%s fr%u,fr%u,fr%u,fr%u\n", flag_rC ? ".":"",
sewardj0e2cc672005-07-29 21:58:51 +00006155 frD_addr, frA_addr, frC_addr, frB_addr);
6156 assign( frD, unop( Iop_NegF64,
sewardj40c80262006-02-08 19:30:46 +00006157 qop( Iop_MSubF64, rm,
6158 mkexpr(frA), mkexpr(frC), mkexpr(frB) )));
sewardj0e2cc672005-07-29 21:58:51 +00006159 break;
6160
cerion5b2325f2005-12-23 00:55:09 +00006161 case 0x1F: // fnmadd (Float Neg Mult-Add (Dbl Precision), PPC32 p417)
6162 DIP("fnmadd%s fr%u,fr%u,fr%u,fr%u\n", flag_rC ? ".":"",
sewardj0e2cc672005-07-29 21:58:51 +00006163 frD_addr, frA_addr, frC_addr, frB_addr);
6164 assign( frD, unop( Iop_NegF64,
sewardj40c80262006-02-08 19:30:46 +00006165 qop( Iop_MAddF64, rm,
6166 mkexpr(frA), mkexpr(frC), mkexpr(frB) )));
sewardj0e2cc672005-07-29 21:58:51 +00006167 break;
sewardje14bb9f2005-07-22 09:39:02 +00006168
6169 default:
cerion5b2325f2005-12-23 00:55:09 +00006170 vex_printf("dis_fp_multadd(ppc)(3F: opc2)\n");
sewardje14bb9f2005-07-22 09:39:02 +00006171 return False;
6172 }
6173 break;
6174
6175 default:
cerion5b2325f2005-12-23 00:55:09 +00006176 vex_printf("dis_fp_multadd(ppc)(opc1)\n");
sewardje14bb9f2005-07-22 09:39:02 +00006177 return False;
6178 }
6179
6180 putFReg( frD_addr, mkexpr(frD) );
sewardjb183b852006-02-03 16:08:03 +00006181
6182 if (set_FPRF) {
6183 // XXX XXX XXX FIXME
6184 // set FPRF from frD
6185 }
6186
6187 if (flag_rC && clear_CR1) {
6188 putCR321( 1, mkU8(0) );
6189 putCR0( 1, mkU8(0) );
6190 }
6191
sewardje14bb9f2005-07-22 09:39:02 +00006192 return True;
6193}
6194
6195
6196
6197/*
6198 Floating Point Compare Instructions
6199*/
6200static Bool dis_fp_cmp ( UInt theInstr )
6201{
6202 /* X-Form */
cerion76de5cf2005-11-18 18:25:12 +00006203 UChar opc1 = ifieldOPC(theInstr);
6204 UChar crfD = toUChar( IFIELD( theInstr, 23, 3 ) );
6205 UChar b21to22 = toUChar( IFIELD( theInstr, 21, 2 ) );
6206 UChar frA_addr = ifieldRegA(theInstr);
6207 UChar frB_addr = ifieldRegB(theInstr);
6208 UInt opc2 = ifieldOPClo10(theInstr);
6209 UChar b0 = ifieldBIT0(theInstr);
sewardje14bb9f2005-07-22 09:39:02 +00006210
6211 IRTemp ccIR = newTemp(Ity_I32);
6212 IRTemp ccPPC32 = newTemp(Ity_I32);
6213
sewardje14bb9f2005-07-22 09:39:02 +00006214 IRTemp frA = newTemp(Ity_F64);
6215 IRTemp frB = newTemp(Ity_F64);
sewardje14bb9f2005-07-22 09:39:02 +00006216
6217 if (opc1 != 0x3F || b21to22 != 0 || b0 != 0) {
cerion5b2325f2005-12-23 00:55:09 +00006218 vex_printf("dis_fp_cmp(ppc)(instr)\n");
sewardje14bb9f2005-07-22 09:39:02 +00006219 return False;
6220 }
6221
6222 assign( frA, getFReg(frA_addr));
6223 assign( frB, getFReg(frB_addr));
6224
6225 assign( ccIR, binop(Iop_CmpF64, mkexpr(frA), mkexpr(frB)) );
6226
6227 /* Map compare result from IR to PPC32 */
6228 /*
6229 FP cmp result | PPC | IR
6230 --------------------------
6231 UN | 0x1 | 0x45
6232 EQ | 0x2 | 0x40
6233 GT | 0x4 | 0x00
6234 LT | 0x8 | 0x01
6235 */
6236
sewardjb183b852006-02-03 16:08:03 +00006237 // ccPPC32 = Shl(1, (~(ccIR>>5) & 2)
6238 // | ((ccIR ^ (ccIR>>6)) & 1)
sewardje14bb9f2005-07-22 09:39:02 +00006239 assign(
6240 ccPPC32,
sewardjb183b852006-02-03 16:08:03 +00006241 binop(
6242 Iop_Shl32,
6243 mkU32(1),
6244 unop(
6245 Iop_32to8,
6246 binop(
6247 Iop_Or32,
6248 binop(
6249 Iop_And32,
6250 unop(
6251 Iop_Not32,
6252 binop(Iop_Shr32, mkexpr(ccIR), mkU8(5))
6253 ),
6254 mkU32(2)
6255 ),
6256 binop(
6257 Iop_And32,
6258 binop(
6259 Iop_Xor32,
6260 mkexpr(ccIR),
6261 binop(Iop_Shr32, mkexpr(ccIR), mkU8(6))
6262 ),
6263 mkU32(1)
6264 )
6265 )
6266 )
6267 )
sewardje14bb9f2005-07-22 09:39:02 +00006268 );
6269
ceriond953ebb2005-11-29 13:27:20 +00006270 putGST_field( PPC_GST_CR, mkexpr(ccPPC32), crfD );
sewardje14bb9f2005-07-22 09:39:02 +00006271
cerionedf7fc52005-11-18 20:57:41 +00006272 /* CAB: TODO?: Support writing cc to FPSCR->FPCC ?
ceriond953ebb2005-11-29 13:27:20 +00006273 putGST_field( PPC_GST_FPSCR, mkexpr(ccPPC32), 4 );
cerionedf7fc52005-11-18 20:57:41 +00006274 */
sewardjb183b852006-02-03 16:08:03 +00006275 // XXX XXX XXX FIXME
6276 // Also write the result into FPRF (it's not entirely clear how)
sewardje14bb9f2005-07-22 09:39:02 +00006277
cerionedf7fc52005-11-18 20:57:41 +00006278 /* Note: Differences between fcmpu and fcmpo are only in exception
6279 flag settings, which aren't supported anyway. */
sewardje14bb9f2005-07-22 09:39:02 +00006280 switch (opc2) {
ceriond953ebb2005-11-29 13:27:20 +00006281 case 0x000: // fcmpu (Floating Compare Unordered, PPC32 p403)
6282 DIP("fcmpu crf%d,fr%u,fr%u\n", crfD, frA_addr, frB_addr);
6283 break;
6284 case 0x020: // fcmpo (Floating Compare Ordered, PPC32 p402)
6285 DIP("fcmpo crf%d,fr%u,fr%u\n", crfD, frA_addr, frB_addr);
6286 break;
6287 default:
cerion5b2325f2005-12-23 00:55:09 +00006288 vex_printf("dis_fp_cmp(ppc)(opc2)\n");
ceriond953ebb2005-11-29 13:27:20 +00006289 return False;
sewardje14bb9f2005-07-22 09:39:02 +00006290 }
6291 return True;
6292}
6293
6294
6295
6296/*
6297 Floating Point Rounding/Conversion Instructions
6298*/
6299static Bool dis_fp_round ( UInt theInstr )
6300{
6301 /* X-Form */
cerion76de5cf2005-11-18 18:25:12 +00006302 UChar opc1 = ifieldOPC(theInstr);
6303 UChar frD_addr = ifieldRegDS(theInstr);
6304 UChar b16to20 = ifieldRegA(theInstr);
6305 UChar frB_addr = ifieldRegB(theInstr);
6306 UInt opc2 = ifieldOPClo10(theInstr);
6307 UChar flag_rC = ifieldBIT0(theInstr);
sewardje14bb9f2005-07-22 09:39:02 +00006308
sewardjb183b852006-02-03 16:08:03 +00006309 IRTemp frD = newTemp(Ity_F64);
6310 IRTemp frB = newTemp(Ity_F64);
6311 IRTemp r_tmp32 = newTemp(Ity_I32);
6312 IRTemp r_tmp64 = newTemp(Ity_I64);
6313 IRExpr* rm = get_IR_roundingmode();
sewardje14bb9f2005-07-22 09:39:02 +00006314
sewardjb183b852006-02-03 16:08:03 +00006315 /* By default, we will examine the results of the operation and set
6316 fpscr[FPRF] accordingly. */
6317 Bool set_FPRF = True;
6318
6319 /* By default, if flag_RC is set, we will clear cr1 after the
6320 operation. In reality we should set cr1 to indicate the
6321 exception status of the operation, but since we're not
6322 simulating exceptions, the exception status will appear to be
6323 zero. Hence cr1 should be cleared if this is a . form insn. */
6324 Bool clear_CR1 = True;
6325
sewardje14bb9f2005-07-22 09:39:02 +00006326 if (opc1 != 0x3F || b16to20 != 0) {
cerion5b2325f2005-12-23 00:55:09 +00006327 vex_printf("dis_fp_round(ppc)(instr)\n");
sewardje14bb9f2005-07-22 09:39:02 +00006328 return False;
6329 }
6330
6331 assign( frB, getFReg(frB_addr));
6332
sewardje14bb9f2005-07-22 09:39:02 +00006333 switch (opc2) {
cerionf0de28c2005-12-13 20:21:11 +00006334 case 0x00C: // frsp (Float Round to Single, PPC32 p423)
cerion5b2325f2005-12-23 00:55:09 +00006335 DIP("frsp%s fr%u,fr%u\n", flag_rC ? ".":"", frD_addr, frB_addr);
sewardjb183b852006-02-03 16:08:03 +00006336 assign( frD, binop( Iop_RoundF64toF32, rm, mkexpr(frB) ));
ceriond953ebb2005-11-29 13:27:20 +00006337 break;
6338
cerionf0de28c2005-12-13 20:21:11 +00006339 case 0x00E: // fctiw (Float Conv to Int, PPC32 p404)
cerion5b2325f2005-12-23 00:55:09 +00006340 DIP("fctiw%s fr%u,fr%u\n", flag_rC ? ".":"", frD_addr, frB_addr);
6341 assign( r_tmp32,
sewardjb183b852006-02-03 16:08:03 +00006342 binop(Iop_F64toI32, rm, mkexpr(frB)) );
ceriond953ebb2005-11-29 13:27:20 +00006343 assign( frD, unop( Iop_ReinterpI64asF64,
cerion07b07a92005-12-22 14:32:35 +00006344 unop( Iop_32Uto64, mkexpr(r_tmp32))));
sewardjb183b852006-02-03 16:08:03 +00006345 /* FPRF is undefined after fctiw. Leave unchanged. */
6346 set_FPRF = False;
ceriond953ebb2005-11-29 13:27:20 +00006347 break;
6348
cerionf0de28c2005-12-13 20:21:11 +00006349 case 0x00F: // fctiwz (Float Conv to Int, Round to Zero, PPC32 p405)
cerion5b2325f2005-12-23 00:55:09 +00006350 DIP("fctiwz%s fr%u,fr%u\n", flag_rC ? ".":"", frD_addr, frB_addr);
sewardjb183b852006-02-03 16:08:03 +00006351 assign( r_tmp32,
6352 binop(Iop_F64toI32, mkU32(Irrm_ZERO), mkexpr(frB) ));
ceriond953ebb2005-11-29 13:27:20 +00006353 assign( frD, unop( Iop_ReinterpI64asF64,
cerion07b07a92005-12-22 14:32:35 +00006354 unop( Iop_32Uto64, mkexpr(r_tmp32))));
sewardjb183b852006-02-03 16:08:03 +00006355 /* FPRF is undefined after fctiwz. Leave unchanged. */
6356 set_FPRF = False;
ceriond953ebb2005-11-29 13:27:20 +00006357 break;
cerionf0de28c2005-12-13 20:21:11 +00006358
cerion5b2325f2005-12-23 00:55:09 +00006359 case 0x32E: // fctid (Float Conv to Int DWord, PPC64 p437)
6360 DIP("fctid%s fr%u,fr%u\n", flag_rC ? ".":"", frD_addr, frB_addr);
6361 assign( r_tmp64,
sewardjb183b852006-02-03 16:08:03 +00006362 binop(Iop_F64toI64, rm, mkexpr(frB)) );
cerion07b07a92005-12-22 14:32:35 +00006363 assign( frD, unop( Iop_ReinterpI64asF64, mkexpr(r_tmp64)) );
sewardjb183b852006-02-03 16:08:03 +00006364 /* FPRF is undefined after fctid. Leave unchanged. */
6365 set_FPRF = False;
cerion07b07a92005-12-22 14:32:35 +00006366 break;
cerionf0de28c2005-12-13 20:21:11 +00006367
cerion5b2325f2005-12-23 00:55:09 +00006368 case 0x32F: // fctidz (Float Conv to Int DWord, Round to Zero, PPC64 p437)
6369 DIP("fctidz%s fr%u,fr%u\n", flag_rC ? ".":"", frD_addr, frB_addr);
sewardjb183b852006-02-03 16:08:03 +00006370 assign( r_tmp64,
6371 binop(Iop_F64toI64, mkU32(Irrm_ZERO), mkexpr(frB)) );
cerion07b07a92005-12-22 14:32:35 +00006372 assign( frD, unop( Iop_ReinterpI64asF64, mkexpr(r_tmp64)) );
sewardjb183b852006-02-03 16:08:03 +00006373 /* FPRF is undefined after fctidz. Leave unchanged. */
6374 set_FPRF = False;
cerion07b07a92005-12-22 14:32:35 +00006375 break;
cerionf0de28c2005-12-13 20:21:11 +00006376
cerion5b2325f2005-12-23 00:55:09 +00006377 case 0x34E: // fcfid (Float Conv from Int DWord, PPC64 p434)
6378 DIP("fcfid%s fr%u,fr%u\n", flag_rC ? ".":"", frD_addr, frB_addr);
cerion07b07a92005-12-22 14:32:35 +00006379 assign( r_tmp64, unop( Iop_ReinterpF64asI64, mkexpr(frB)) );
sewardjb183b852006-02-03 16:08:03 +00006380 assign( frD,
6381 binop(Iop_I64toF64, rm, mkexpr(r_tmp64)) );
cerion07b07a92005-12-22 14:32:35 +00006382 break;
cerionf0de28c2005-12-13 20:21:11 +00006383
ceriond953ebb2005-11-29 13:27:20 +00006384 default:
cerion5b2325f2005-12-23 00:55:09 +00006385 vex_printf("dis_fp_round(ppc)(opc2)\n");
ceriond953ebb2005-11-29 13:27:20 +00006386 return False;
sewardje14bb9f2005-07-22 09:39:02 +00006387 }
6388
6389 putFReg( frD_addr, mkexpr(frD) );
sewardjb183b852006-02-03 16:08:03 +00006390
6391 if (set_FPRF) {
6392 // XXX XXX XXX FIXME
6393 // set FPRF from frD
6394 }
6395
6396 if (flag_rC && clear_CR1) {
6397 putCR321( 1, mkU8(0) );
6398 putCR0( 1, mkU8(0) );
6399 }
6400
sewardje14bb9f2005-07-22 09:39:02 +00006401 return True;
6402}
6403
6404
6405
6406/*
6407 Floating Point Move Instructions
6408*/
6409static Bool dis_fp_move ( UInt theInstr )
6410{
6411 /* X-Form */
cerion76de5cf2005-11-18 18:25:12 +00006412 UChar opc1 = ifieldOPC(theInstr);
6413 UChar frD_addr = ifieldRegDS(theInstr);
6414 UChar b16to20 = ifieldRegA(theInstr);
6415 UChar frB_addr = ifieldRegB(theInstr);
6416 UInt opc2 = ifieldOPClo10(theInstr);
6417 UChar flag_rC = ifieldBIT0(theInstr);
sewardje14bb9f2005-07-22 09:39:02 +00006418
6419 IRTemp frD = newTemp(Ity_F64);
6420 IRTemp frB = newTemp(Ity_F64);
6421
6422 if (opc1 != 0x3F || b16to20 != 0) {
cerion5b2325f2005-12-23 00:55:09 +00006423 vex_printf("dis_fp_move(ppc)(instr)\n");
sewardje14bb9f2005-07-22 09:39:02 +00006424 return False;
6425 }
6426
6427 assign( frB, getFReg(frB_addr));
6428
sewardje14bb9f2005-07-22 09:39:02 +00006429 switch (opc2) {
ceriond953ebb2005-11-29 13:27:20 +00006430 case 0x028: // fneg (Floating Negate, PPC32 p416)
cerion5b2325f2005-12-23 00:55:09 +00006431 DIP("fneg%s fr%u,fr%u\n", flag_rC ? ".":"", frD_addr, frB_addr);
ceriond953ebb2005-11-29 13:27:20 +00006432 assign( frD, unop( Iop_NegF64, mkexpr(frB) ));
6433 break;
6434
6435 case 0x048: // fmr (Floating Move Register, PPC32 p410)
cerion5b2325f2005-12-23 00:55:09 +00006436 DIP("fmr%s fr%u,fr%u\n", flag_rC ? ".":"", frD_addr, frB_addr);
ceriond953ebb2005-11-29 13:27:20 +00006437 assign( frD, mkexpr(frB) );
6438 break;
6439
6440 case 0x088: // fnabs (Floating Negative Absolute Value, PPC32 p415)
cerion5b2325f2005-12-23 00:55:09 +00006441 DIP("fnabs%s fr%u,fr%u\n", flag_rC ? ".":"", frD_addr, frB_addr);
ceriond953ebb2005-11-29 13:27:20 +00006442 assign( frD, unop( Iop_NegF64, unop( Iop_AbsF64, mkexpr(frB) )));
6443 break;
6444
6445 case 0x108: // fabs (Floating Absolute Value, PPC32 p399)
cerion5b2325f2005-12-23 00:55:09 +00006446 DIP("fabs%s fr%u,fr%u\n", flag_rC ? ".":"", frD_addr, frB_addr);
ceriond953ebb2005-11-29 13:27:20 +00006447 assign( frD, unop( Iop_AbsF64, mkexpr(frB) ));
6448 break;
6449
6450 default:
cerion5b2325f2005-12-23 00:55:09 +00006451 vex_printf("dis_fp_move(ppc)(opc2)\n");
ceriond953ebb2005-11-29 13:27:20 +00006452 return False;
sewardje14bb9f2005-07-22 09:39:02 +00006453 }
6454
6455 putFReg( frD_addr, mkexpr(frD) );
sewardjb183b852006-02-03 16:08:03 +00006456
6457 /* None of these change FPRF. cr1 is set in the usual way though,
6458 if flag_rC is set. */
6459
6460 if (flag_rC) {
6461 putCR321( 1, mkU8(0) );
6462 putCR0( 1, mkU8(0) );
6463 }
6464
sewardje14bb9f2005-07-22 09:39:02 +00006465 return True;
6466}
6467
6468
6469
6470/*
6471 Floating Point Status/Control Register Instructions
6472*/
6473static Bool dis_fp_scr ( UInt theInstr )
6474{
cerion76de5cf2005-11-18 18:25:12 +00006475 /* Many forms - see each switch case */
6476 UChar opc1 = ifieldOPC(theInstr);
6477 UInt opc2 = ifieldOPClo10(theInstr);
6478 UChar flag_rC = ifieldBIT0(theInstr);
sewardje14bb9f2005-07-22 09:39:02 +00006479
6480 if (opc1 != 0x3F) {
cerion5b2325f2005-12-23 00:55:09 +00006481 vex_printf("dis_fp_scr(ppc)(instr)\n");
sewardje14bb9f2005-07-22 09:39:02 +00006482 return False;
6483 }
6484
6485 switch (opc2) {
cerion3ea49ee2006-01-04 10:53:00 +00006486 case 0x026: { // mtfsb1 (Move to FPSCR Bit 1, PPC32 p479)
6487 // Bit crbD of the FPSCR is set.
6488 UChar crbD = ifieldRegDS(theInstr);
6489 UInt b11to20 = IFIELD(theInstr, 11, 10);
6490
6491 if (b11to20 != 0) {
6492 vex_printf("dis_fp_scr(ppc)(instr,mtfsb1)\n");
6493 return False;
6494 }
6495 DIP("mtfsb1%s crb%d \n", flag_rC ? ".":"", crbD);
6496 putGST_masked( PPC_GST_FPSCR, mkU32(1<<(31-crbD)), 1<<(31-crbD) );
6497 break;
6498 }
6499
sewardj496b88f2006-10-04 17:46:11 +00006500 case 0x040: { // mcrfs (Move to Condition Register from FPSCR, PPC32 p465)
6501 UChar crfD = toUChar( IFIELD( theInstr, 23, 3 ) );
6502 UChar b21to22 = toUChar( IFIELD( theInstr, 21, 2 ) );
6503 UChar crfS = toUChar( IFIELD( theInstr, 18, 3 ) );
6504 UChar b11to17 = toUChar( IFIELD( theInstr, 11, 7 ) );
6505 IRTemp tmp = newTemp(Ity_I32);
6506 IRExpr* fpscr_all;
6507 if (b21to22 != 0 || b11to17 != 0 || flag_rC != 0) {
6508 vex_printf("dis_fp_scr(ppc)(instr,mcrfs)\n");
6509 return False;
6510 }
6511 DIP("mcrfs crf%d,crf%d\n", crfD, crfS);
6512 vassert(crfD < 8);
6513 vassert(crfS < 8);
6514 fpscr_all = getGST_masked( PPC_GST_FPSCR, MASK_FPSCR_RN );
6515 assign( tmp, binop(Iop_And32,
6516 binop(Iop_Shr32,fpscr_all,mkU8(4 * (7-crfS))),
6517 mkU32(0xF)) );
6518 putGST_field( PPC_GST_CR, mkexpr(tmp), crfD );
6519 break;
6520 }
sewardj0e2cc672005-07-29 21:58:51 +00006521
6522 case 0x046: { // mtfsb0 (Move to FPSCR Bit 0, PPC32 p478)
6523 // Bit crbD of the FPSCR is cleared.
cerion76de5cf2005-11-18 18:25:12 +00006524 UChar crbD = ifieldRegDS(theInstr);
6525 UInt b11to20 = IFIELD(theInstr, 11, 10);
sewardj0e2cc672005-07-29 21:58:51 +00006526
6527 if (b11to20 != 0) {
cerion5b2325f2005-12-23 00:55:09 +00006528 vex_printf("dis_fp_scr(ppc)(instr,mtfsb0)\n");
sewardj0e2cc672005-07-29 21:58:51 +00006529 return False;
6530 }
cerion5b2325f2005-12-23 00:55:09 +00006531 DIP("mtfsb0%s crb%d\n", flag_rC ? ".":"", crbD);
ceriond953ebb2005-11-29 13:27:20 +00006532 putGST_masked( PPC_GST_FPSCR, mkU32(0), 1<<(31-crbD) );
sewardj0e2cc672005-07-29 21:58:51 +00006533 break;
6534 }
6535
6536 case 0x086: { // mtfsfi (Move to FPSCR Field Immediate, PPC32 p481)
cerion76de5cf2005-11-18 18:25:12 +00006537 UChar crfD = toUChar( IFIELD( theInstr, 23, 3 ) );
6538 UChar b16to22 = toUChar( IFIELD( theInstr, 16, 7 ) );
6539 UChar IMM = toUChar( IFIELD( theInstr, 12, 4 ) );
6540 UChar b11 = toUChar( IFIELD( theInstr, 11, 1 ) );
sewardj0e2cc672005-07-29 21:58:51 +00006541
6542 if (b16to22 != 0 || b11 != 0) {
cerion5b2325f2005-12-23 00:55:09 +00006543 vex_printf("dis_fp_scr(ppc)(instr,mtfsfi)\n");
sewardj0e2cc672005-07-29 21:58:51 +00006544 return False;
6545 }
cerion5b2325f2005-12-23 00:55:09 +00006546 DIP("mtfsfi%s crf%d,%d\n", flag_rC ? ".":"", crfD, IMM);
ceriond953ebb2005-11-29 13:27:20 +00006547 putGST_field( PPC_GST_FPSCR, mkU32(IMM), crfD );
sewardj0e2cc672005-07-29 21:58:51 +00006548 break;
6549 }
sewardje14bb9f2005-07-22 09:39:02 +00006550
6551 case 0x247: { // mffs (Move from FPSCR, PPC32 p468)
sewardj496b88f2006-10-04 17:46:11 +00006552 UChar frD_addr = ifieldRegDS(theInstr);
6553 UInt b11to20 = IFIELD(theInstr, 11, 10);
6554 IRExpr* fpscr_all = getGST_masked( PPC_GST_FPSCR, MASK_FPSCR_RN );
sewardje14bb9f2005-07-22 09:39:02 +00006555
6556 if (b11to20 != 0) {
cerion5b2325f2005-12-23 00:55:09 +00006557 vex_printf("dis_fp_scr(ppc)(instr,mffs)\n");
sewardje14bb9f2005-07-22 09:39:02 +00006558 return False;
6559 }
cerion5b2325f2005-12-23 00:55:09 +00006560 DIP("mffs%s fr%u\n", flag_rC ? ".":"", frD_addr);
6561 putFReg( frD_addr,
6562 unop( Iop_ReinterpI64asF64,
sewardj496b88f2006-10-04 17:46:11 +00006563 unop( Iop_32Uto64, fpscr_all )));
sewardje14bb9f2005-07-22 09:39:02 +00006564 break;
6565 }
6566
6567 case 0x2C7: { // mtfsf (Move to FPSCR Fields, PPC32 p480)
cerion76de5cf2005-11-18 18:25:12 +00006568 UChar b25 = toUChar( IFIELD(theInstr, 25, 1) );
6569 UChar FM = toUChar( IFIELD(theInstr, 17, 8) );
6570 UChar b16 = toUChar( IFIELD(theInstr, 16, 1) );
6571 UChar frB_addr = ifieldRegB(theInstr);
6572 IRTemp frB = newTemp(Ity_F64);
sewardje14bb9f2005-07-22 09:39:02 +00006573 IRTemp rB_32 = newTemp(Ity_I32);
cerion76de5cf2005-11-18 18:25:12 +00006574 Int i, mask;
sewardje14bb9f2005-07-22 09:39:02 +00006575
6576 if (b25 != 0 || b16 != 0) {
cerion5b2325f2005-12-23 00:55:09 +00006577 vex_printf("dis_fp_scr(ppc)(instr,mtfsf)\n");
sewardje14bb9f2005-07-22 09:39:02 +00006578 return False;
6579 }
cerion5b2325f2005-12-23 00:55:09 +00006580 DIP("mtfsf%s %d,fr%u\n", flag_rC ? ".":"", FM, frB_addr);
sewardje14bb9f2005-07-22 09:39:02 +00006581 assign( frB, getFReg(frB_addr));
6582 assign( rB_32, unop( Iop_64to32,
6583 unop( Iop_ReinterpF64asI64, mkexpr(frB) )));
6584 // Build 32bit mask from FM:
cerion76de5cf2005-11-18 18:25:12 +00006585 mask = 0;
sewardje14bb9f2005-07-22 09:39:02 +00006586 for (i=0; i<8; i++) {
6587 if ((FM & (1<<(7-i))) == 1) {
6588 mask |= 0xF << (7-i);
6589 }
6590 }
ceriond953ebb2005-11-29 13:27:20 +00006591 putGST_masked( PPC_GST_FPSCR, mkexpr(rB_32), mask );
sewardje14bb9f2005-07-22 09:39:02 +00006592 break;
6593 }
6594
6595 default:
cerion5b2325f2005-12-23 00:55:09 +00006596 vex_printf("dis_fp_scr(ppc)(opc2)\n");
sewardje14bb9f2005-07-22 09:39:02 +00006597 return False;
6598 }
6599 return True;
6600}
6601
6602
6603
cerion32aad402005-09-10 12:02:24 +00006604/*------------------------------------------------------------*/
6605/*--- AltiVec Instruction Translation ---*/
6606/*------------------------------------------------------------*/
6607
6608/*
6609 Altivec Cache Control Instructions (Data Streams)
6610*/
6611static Bool dis_av_datastream ( UInt theInstr )
6612{
cerion76de5cf2005-11-18 18:25:12 +00006613 /* X-Form */
6614 UChar opc1 = ifieldOPC(theInstr);
6615 UChar flag_T = toUChar( IFIELD( theInstr, 25, 1 ) );
6616 UChar flag_A = flag_T;
6617 UChar b23to24 = toUChar( IFIELD( theInstr, 23, 2 ) );
6618 UChar STRM = toUChar( IFIELD( theInstr, 21, 2 ) );
6619 UChar rA_addr = ifieldRegA(theInstr);
6620 UChar rB_addr = ifieldRegB(theInstr);
6621 UInt opc2 = ifieldOPClo10(theInstr);
6622 UChar b0 = ifieldBIT0(theInstr);
cerion32aad402005-09-10 12:02:24 +00006623
6624 if (opc1 != 0x1F || b23to24 != 0 || b0 != 0) {
cerion5b2325f2005-12-23 00:55:09 +00006625 vex_printf("dis_av_datastream(ppc)(instr)\n");
cerion32aad402005-09-10 12:02:24 +00006626 return False;
6627 }
6628
6629 switch (opc2) {
6630 case 0x156: // dst (Data Stream Touch, AV p115)
cerion5b2325f2005-12-23 00:55:09 +00006631 DIP("dst%s r%u,r%u,%d\n", flag_T ? "t" : "",
6632 rA_addr, rB_addr, STRM);
cerion32aad402005-09-10 12:02:24 +00006633 DIP(" => not implemented\n");
6634 return False;
6635
6636 case 0x176: // dstst (Data Stream Touch for Store, AV p117)
cerion5b2325f2005-12-23 00:55:09 +00006637 DIP("dstst%s r%u,r%u,%d\n", flag_T ? "t" : "",
6638 rA_addr, rB_addr, STRM);
cerion32aad402005-09-10 12:02:24 +00006639 DIP(" => not implemented\n");
6640 return False;
6641
6642 case 0x336: // dss (Data Stream Stop, AV p114)
6643 if (rA_addr != 0 || rB_addr != 0) {
cerion5b2325f2005-12-23 00:55:09 +00006644 vex_printf("dis_av_datastream(ppc)(opc2,dst)\n");
cerion32aad402005-09-10 12:02:24 +00006645 return False;
6646 }
6647 if (flag_A == 0) {
6648 DIP("dss %d\n", STRM);
6649 DIP(" => not implemented\n");
6650 } else {
6651 DIP("dssall\n");
6652 DIP(" => not implemented\n");
6653 }
6654 return False;
6655
6656 default:
cerion5b2325f2005-12-23 00:55:09 +00006657 vex_printf("dis_av_datastream(ppc)(opc2)\n");
cerion32aad402005-09-10 12:02:24 +00006658 return False;
6659 }
6660 return True;
6661}
6662
6663/*
6664 AltiVec Processor Control Instructions
6665*/
6666static Bool dis_av_procctl ( UInt theInstr )
6667{
cerion76de5cf2005-11-18 18:25:12 +00006668 /* VX-Form */
6669 UChar opc1 = ifieldOPC(theInstr);
6670 UChar vD_addr = ifieldRegDS(theInstr);
6671 UChar vA_addr = ifieldRegA(theInstr);
6672 UChar vB_addr = ifieldRegB(theInstr);
6673 UInt opc2 = IFIELD( theInstr, 0, 11 );
cerion32aad402005-09-10 12:02:24 +00006674
6675 if (opc1 != 0x4) {
cerion5b2325f2005-12-23 00:55:09 +00006676 vex_printf("dis_av_procctl(ppc)(instr)\n");
cerion32aad402005-09-10 12:02:24 +00006677 return False;
6678 }
6679
6680 switch (opc2) {
6681 case 0x604: // mfvscr (Move from VSCR, AV p129)
6682 if (vA_addr != 0 || vB_addr != 0) {
cerion5b2325f2005-12-23 00:55:09 +00006683 vex_printf("dis_av_procctl(ppc)(opc2,dst)\n");
cerion32aad402005-09-10 12:02:24 +00006684 return False;
6685 }
6686 DIP("mfvscr v%d\n", vD_addr);
ceriond953ebb2005-11-29 13:27:20 +00006687 putVReg( vD_addr, unop(Iop_32UtoV128, getGST( PPC_GST_VSCR )) );
cerion225a0342005-09-12 20:49:09 +00006688 break;
cerion32aad402005-09-10 12:02:24 +00006689
cerion225a0342005-09-12 20:49:09 +00006690 case 0x644: { // mtvscr (Move to VSCR, AV p130)
sewardj197bd172005-10-12 11:34:33 +00006691 IRTemp vB = newTemp(Ity_V128);
cerion32aad402005-09-10 12:02:24 +00006692 if (vD_addr != 0 || vA_addr != 0) {
cerion5b2325f2005-12-23 00:55:09 +00006693 vex_printf("dis_av_procctl(ppc)(opc2,dst)\n");
cerion32aad402005-09-10 12:02:24 +00006694 return False;
6695 }
6696 DIP("mtvscr v%d\n", vB_addr);
cerion225a0342005-09-12 20:49:09 +00006697 assign( vB, getVReg(vB_addr));
ceriond953ebb2005-11-29 13:27:20 +00006698 putGST( PPC_GST_VSCR, unop(Iop_V128to32, mkexpr(vB)) );
cerion225a0342005-09-12 20:49:09 +00006699 break;
6700 }
cerion32aad402005-09-10 12:02:24 +00006701 default:
cerion5b2325f2005-12-23 00:55:09 +00006702 vex_printf("dis_av_procctl(ppc)(opc2)\n");
cerion32aad402005-09-10 12:02:24 +00006703 return False;
6704 }
6705 return True;
6706}
ceriona982c052005-06-28 17:23:09 +00006707
6708/*
6709 AltiVec Load Instructions
6710*/
6711static Bool dis_av_load ( UInt theInstr )
6712{
cerion76de5cf2005-11-18 18:25:12 +00006713 /* X-Form */
6714 UChar opc1 = ifieldOPC(theInstr);
6715 UChar vD_addr = ifieldRegDS(theInstr);
6716 UChar rA_addr = ifieldRegA(theInstr);
6717 UChar rB_addr = ifieldRegB(theInstr);
6718 UInt opc2 = ifieldOPClo10(theInstr);
6719 UChar b0 = ifieldBIT0(theInstr);
ceriona982c052005-06-28 17:23:09 +00006720
cerionfb197c42005-12-24 12:32:10 +00006721 IRType ty = mode64 ? Ity_I64 : Ity_I32;
6722 IRTemp EA = newTemp(ty);
6723 IRTemp EA_align16 = newTemp(ty);
cerion2831b002005-11-30 19:55:22 +00006724
ceriona982c052005-06-28 17:23:09 +00006725 if (opc1 != 0x1F || b0 != 0) {
cerion5b2325f2005-12-23 00:55:09 +00006726 vex_printf("dis_av_load(ppc)(instr)\n");
ceriona982c052005-06-28 17:23:09 +00006727 return False;
6728 }
6729
cerionfb197c42005-12-24 12:32:10 +00006730 assign( EA, ea_rAor0_idxd(rA_addr, rB_addr) );
6731 assign( EA_align16, addr_align( mkexpr(EA), 16 ) );
ceriona50fde52005-07-01 21:16:10 +00006732
ceriona982c052005-06-28 17:23:09 +00006733 switch (opc2) {
6734
cerion6f6c6a02005-09-13 18:41:09 +00006735 case 0x006: { // lvsl (Load Vector for Shift Left, AV p123)
cerionfb197c42005-12-24 12:32:10 +00006736 IRDirty* d;
sewardjd1470942005-10-22 02:01:16 +00006737 UInt vD_off = vectorGuestRegOffset(vD_addr);
6738 IRExpr** args = mkIRExprVec_3(
6739 mkU32(vD_off),
cerionfb197c42005-12-24 12:32:10 +00006740 binop(Iop_And32, mkSzNarrow32(ty, mkexpr(EA)),
6741 mkU32(0xF)),
sewardjd1470942005-10-22 02:01:16 +00006742 mkU32(0)/*left*/ );
cerion5b2325f2005-12-23 00:55:09 +00006743 if (!mode64) {
cerion4c4f5ef2006-01-02 14:41:50 +00006744 d = unsafeIRDirty_0_N (
6745 0/*regparms*/,
6746 "ppc32g_dirtyhelper_LVS",
6747 fnptr_to_fnentry(&ppc32g_dirtyhelper_LVS),
6748 args );
cerion5b2325f2005-12-23 00:55:09 +00006749 } else {
cerion4c4f5ef2006-01-02 14:41:50 +00006750 d = unsafeIRDirty_0_N (
6751 0/*regparms*/,
6752 "ppc64g_dirtyhelper_LVS",
6753 fnptr_to_fnentry(&ppc64g_dirtyhelper_LVS),
6754 args );
cerion5b2325f2005-12-23 00:55:09 +00006755 }
ceriond953ebb2005-11-29 13:27:20 +00006756 DIP("lvsl v%d,r%u,r%u\n", vD_addr, rA_addr, rB_addr);
cerion6f6c6a02005-09-13 18:41:09 +00006757 /* declare guest state effects */
6758 d->needsBBP = True;
6759 d->nFxState = 1;
6760 d->fxState[0].fx = Ifx_Write;
sewardjd1470942005-10-22 02:01:16 +00006761 d->fxState[0].offset = vD_off;
cerion6f6c6a02005-09-13 18:41:09 +00006762 d->fxState[0].size = sizeof(U128);
cerion32aad402005-09-10 12:02:24 +00006763
cerion6f6c6a02005-09-13 18:41:09 +00006764 /* execute the dirty call, side-effecting guest state */
6765 stmt( IRStmt_Dirty(d) );
6766 break;
6767 }
6768 case 0x026: { // lvsr (Load Vector for Shift Right, AV p125)
cerionfb197c42005-12-24 12:32:10 +00006769 IRDirty* d;
sewardjd1470942005-10-22 02:01:16 +00006770 UInt vD_off = vectorGuestRegOffset(vD_addr);
6771 IRExpr** args = mkIRExprVec_3(
6772 mkU32(vD_off),
cerionfb197c42005-12-24 12:32:10 +00006773 binop(Iop_And32, mkSzNarrow32(ty, mkexpr(EA)),
6774 mkU32(0xF)),
sewardjd1470942005-10-22 02:01:16 +00006775 mkU32(1)/*right*/ );
cerion5b2325f2005-12-23 00:55:09 +00006776 if (!mode64) {
cerion4c4f5ef2006-01-02 14:41:50 +00006777 d = unsafeIRDirty_0_N (
6778 0/*regparms*/,
6779 "ppc32g_dirtyhelper_LVS",
6780 fnptr_to_fnentry(&ppc32g_dirtyhelper_LVS),
6781 args );
cerion5b2325f2005-12-23 00:55:09 +00006782 } else {
cerion4c4f5ef2006-01-02 14:41:50 +00006783 d = unsafeIRDirty_0_N (
6784 0/*regparms*/,
6785 "ppc64g_dirtyhelper_LVS",
6786 fnptr_to_fnentry(&ppc64g_dirtyhelper_LVS),
6787 args );
cerion5b2325f2005-12-23 00:55:09 +00006788 }
ceriond953ebb2005-11-29 13:27:20 +00006789 DIP("lvsr v%d,r%u,r%u\n", vD_addr, rA_addr, rB_addr);
cerion6f6c6a02005-09-13 18:41:09 +00006790 /* declare guest state effects */
6791 d->needsBBP = True;
6792 d->nFxState = 1;
6793 d->fxState[0].fx = Ifx_Write;
sewardjd1470942005-10-22 02:01:16 +00006794 d->fxState[0].offset = vD_off;
cerion6f6c6a02005-09-13 18:41:09 +00006795 d->fxState[0].size = sizeof(U128);
cerion32aad402005-09-10 12:02:24 +00006796
cerion6f6c6a02005-09-13 18:41:09 +00006797 /* execute the dirty call, side-effecting guest state */
6798 stmt( IRStmt_Dirty(d) );
6799 break;
6800 }
cerion32aad402005-09-10 12:02:24 +00006801 case 0x007: // lvebx (Load Vector Element Byte Indexed, AV p119)
ceriond953ebb2005-11-29 13:27:20 +00006802 DIP("lvebx v%d,r%u,r%u\n", vD_addr, rA_addr, rB_addr);
cerion61c92742005-09-14 22:59:26 +00006803 /* loads addressed byte into vector[EA[0:3]
6804 since all other destination bytes are undefined,
6805 can simply load entire vector from 16-aligned EA */
cerionfb197c42005-12-24 12:32:10 +00006806 putVReg( vD_addr, loadBE(Ity_V128, mkexpr(EA_align16)) );
cerion61c92742005-09-14 22:59:26 +00006807 break;
cerion32aad402005-09-10 12:02:24 +00006808
6809 case 0x027: // lvehx (Load Vector Element Half Word Indexed, AV p121)
ceriond953ebb2005-11-29 13:27:20 +00006810 DIP("lvehx v%d,r%u,r%u\n", vD_addr, rA_addr, rB_addr);
cerion61c92742005-09-14 22:59:26 +00006811 /* see note for lvebx */
cerionfb197c42005-12-24 12:32:10 +00006812 putVReg( vD_addr, loadBE(Ity_V128, mkexpr(EA_align16)) );
cerion61c92742005-09-14 22:59:26 +00006813 break;
cerion32aad402005-09-10 12:02:24 +00006814
6815 case 0x047: // lvewx (Load Vector Element Word Indexed, AV p122)
ceriond953ebb2005-11-29 13:27:20 +00006816 DIP("lvewx v%d,r%u,r%u\n", vD_addr, rA_addr, rB_addr);
cerion61c92742005-09-14 22:59:26 +00006817 /* see note for lvebx */
cerionfb197c42005-12-24 12:32:10 +00006818 putVReg( vD_addr, loadBE(Ity_V128, mkexpr(EA_align16)) );
cerion61c92742005-09-14 22:59:26 +00006819 break;
ceriona982c052005-06-28 17:23:09 +00006820
6821 case 0x067: // lvx (Load Vector Indexed, AV p127)
ceriond953ebb2005-11-29 13:27:20 +00006822 DIP("lvx v%d,r%u,r%u\n", vD_addr, rA_addr, rB_addr);
cerionfb197c42005-12-24 12:32:10 +00006823 putVReg( vD_addr, loadBE(Ity_V128, mkexpr(EA_align16)) );
ceriona50fde52005-07-01 21:16:10 +00006824 break;
ceriona982c052005-06-28 17:23:09 +00006825
cerion32aad402005-09-10 12:02:24 +00006826 case 0x167: // lvxl (Load Vector Indexed LRU, AV p128)
6827 // XXX: lvxl gives explicit control over cache block replacement
ceriond953ebb2005-11-29 13:27:20 +00006828 DIP("lvxl v%d,r%u,r%u\n", vD_addr, rA_addr, rB_addr);
cerion32aad402005-09-10 12:02:24 +00006829 DIP(" => not implemented\n");
6830 return False;
ceriona982c052005-06-28 17:23:09 +00006831
6832 default:
cerion5b2325f2005-12-23 00:55:09 +00006833 vex_printf("dis_av_load(ppc)(opc2)\n");
ceriona982c052005-06-28 17:23:09 +00006834 return False;
6835 }
6836 return True;
6837}
6838
cerion2831b002005-11-30 19:55:22 +00006839
ceriona982c052005-06-28 17:23:09 +00006840/*
6841 AltiVec Store Instructions
6842*/
6843static Bool dis_av_store ( UInt theInstr )
6844{
cerion76de5cf2005-11-18 18:25:12 +00006845 /* X-Form */
6846 UChar opc1 = ifieldOPC(theInstr);
6847 UChar vS_addr = ifieldRegDS(theInstr);
6848 UChar rA_addr = ifieldRegA(theInstr);
6849 UChar rB_addr = ifieldRegB(theInstr);
6850 UInt opc2 = ifieldOPClo10(theInstr);
6851 UChar b0 = ifieldBIT0(theInstr);
ceriona982c052005-06-28 17:23:09 +00006852
cerion2831b002005-11-30 19:55:22 +00006853 IRType ty = mode64 ? Ity_I64 : Ity_I32;
6854 IRTemp EA = newTemp(ty);
ceriondba87e22006-01-02 15:15:45 +00006855 IRTemp addr_aligned = newTemp(ty);
cerion2831b002005-11-30 19:55:22 +00006856 IRTemp vS = newTemp(Ity_V128);
6857 IRTemp eb = newTemp(Ity_I8);
6858 IRTemp idx = newTemp(Ity_I8);
ceriona982c052005-06-28 17:23:09 +00006859
6860 if (opc1 != 0x1F || b0 != 0) {
cerion5b2325f2005-12-23 00:55:09 +00006861 vex_printf("dis_av_store(ppc)(instr)\n");
ceriona982c052005-06-28 17:23:09 +00006862 return False;
6863 }
6864
ceriond953ebb2005-11-29 13:27:20 +00006865 assign( vS, getVReg(vS_addr));
cerion2831b002005-11-30 19:55:22 +00006866 assign( EA, ea_rAor0_idxd(rA_addr, rB_addr) );
ceriond953ebb2005-11-29 13:27:20 +00006867
ceriona982c052005-06-28 17:23:09 +00006868 switch (opc2) {
cerion61c92742005-09-14 22:59:26 +00006869 case 0x087: { // stvebx (Store Vector Byte Indexed, AV p131)
ceriond953ebb2005-11-29 13:27:20 +00006870 DIP("stvebx v%d,r%u,r%u\n", vS_addr, rA_addr, rB_addr);
cerion61c92742005-09-14 22:59:26 +00006871 assign( eb, binop(Iop_And8, mkU8(0xF),
cerion2831b002005-11-30 19:55:22 +00006872 unop(Iop_32to8,
6873 mkSzNarrow32(ty, mkexpr(EA)) )) );
cerion5b2325f2005-12-23 00:55:09 +00006874 assign( idx, binop(Iop_Shl8,
6875 binop(Iop_Sub8, mkU8(15), mkexpr(eb)),
cerion61c92742005-09-14 22:59:26 +00006876 mkU8(3)) );
6877 storeBE( mkexpr(EA),
6878 unop(Iop_32to8, unop(Iop_V128to32,
6879 binop(Iop_ShrV128, mkexpr(vS), mkexpr(idx)))) );
6880 break;
6881 }
6882 case 0x0A7: { // stvehx (Store Vector Half Word Indexed, AV p132)
ceriond953ebb2005-11-29 13:27:20 +00006883 DIP("stvehx v%d,r%u,r%u\n", vS_addr, rA_addr, rB_addr);
ceriondba87e22006-01-02 15:15:45 +00006884 assign( addr_aligned, addr_align(mkexpr(EA), 2) );
cerion61c92742005-09-14 22:59:26 +00006885 assign( eb, binop(Iop_And8, mkU8(0xF),
ceriondba87e22006-01-02 15:15:45 +00006886 mkSzNarrow8(ty, mkexpr(addr_aligned) )) );
cerion5b2325f2005-12-23 00:55:09 +00006887 assign( idx, binop(Iop_Shl8,
6888 binop(Iop_Sub8, mkU8(14), mkexpr(eb)),
cerion61c92742005-09-14 22:59:26 +00006889 mkU8(3)) );
ceriond953ebb2005-11-29 13:27:20 +00006890 storeBE( mkexpr(addr_aligned),
cerion61c92742005-09-14 22:59:26 +00006891 unop(Iop_32to16, unop(Iop_V128to32,
6892 binop(Iop_ShrV128, mkexpr(vS), mkexpr(idx)))) );
6893 break;
6894 }
6895 case 0x0C7: { // stvewx (Store Vector Word Indexed, AV p133)
ceriond953ebb2005-11-29 13:27:20 +00006896 DIP("stvewx v%d,r%u,r%u\n", vS_addr, rA_addr, rB_addr);
ceriondba87e22006-01-02 15:15:45 +00006897 assign( addr_aligned, addr_align(mkexpr(EA), 4) );
cerion61c92742005-09-14 22:59:26 +00006898 assign( eb, binop(Iop_And8, mkU8(0xF),
ceriondba87e22006-01-02 15:15:45 +00006899 mkSzNarrow8(ty, mkexpr(addr_aligned) )) );
cerion5b2325f2005-12-23 00:55:09 +00006900 assign( idx, binop(Iop_Shl8,
6901 binop(Iop_Sub8, mkU8(12), mkexpr(eb)),
cerion61c92742005-09-14 22:59:26 +00006902 mkU8(3)) );
ceriond953ebb2005-11-29 13:27:20 +00006903 storeBE( mkexpr(addr_aligned),
cerion61c92742005-09-14 22:59:26 +00006904 unop(Iop_V128to32,
6905 binop(Iop_ShrV128, mkexpr(vS), mkexpr(idx))) );
6906 break;
6907 }
cerion32aad402005-09-10 12:02:24 +00006908
ceriona982c052005-06-28 17:23:09 +00006909 case 0x0E7: // stvx (Store Vector Indexed, AV p134)
ceriond953ebb2005-11-29 13:27:20 +00006910 DIP("stvx v%d,r%u,r%u\n", vS_addr, rA_addr, rB_addr);
6911 storeBE( addr_align( mkexpr(EA), 16 ), mkexpr(vS) );
ceriona982c052005-06-28 17:23:09 +00006912 break;
6913
cerion32aad402005-09-10 12:02:24 +00006914 case 0x1E7: // stvxl (Store Vector Indexed LRU, AV p135)
6915 // XXX: stvxl can give explicit control over cache block replacement
ceriond953ebb2005-11-29 13:27:20 +00006916 DIP("stvxl v%d,r%u,r%u\n", vS_addr, rA_addr, rB_addr);
cerion32aad402005-09-10 12:02:24 +00006917 DIP(" => not implemented\n");
6918 return False;
ceriond953ebb2005-11-29 13:27:20 +00006919// STORE(vS, 16, addr_align( mkexpr(EA), 16 ));
cerion5b2325f2005-12-23 00:55:09 +00006920// break;
ceriona982c052005-06-28 17:23:09 +00006921
6922 default:
cerion5b2325f2005-12-23 00:55:09 +00006923 vex_printf("dis_av_store(ppc)(opc2)\n");
ceriona982c052005-06-28 17:23:09 +00006924 return False;
6925 }
6926 return True;
6927}
6928
cerion32aad402005-09-10 12:02:24 +00006929/*
6930 AltiVec Arithmetic Instructions
6931*/
6932static Bool dis_av_arith ( UInt theInstr )
6933{
cerion76de5cf2005-11-18 18:25:12 +00006934 /* VX-Form */
6935 UChar opc1 = ifieldOPC(theInstr);
6936 UChar vD_addr = ifieldRegDS(theInstr);
6937 UChar vA_addr = ifieldRegA(theInstr);
6938 UChar vB_addr = ifieldRegB(theInstr);
6939 UInt opc2 = IFIELD( theInstr, 0, 11 );
cerion32aad402005-09-10 12:02:24 +00006940
ceriond3e52412005-09-14 21:15:40 +00006941 IRTemp vA = newTemp(Ity_V128);
6942 IRTemp vB = newTemp(Ity_V128);
cerion4a49b032005-11-08 16:23:07 +00006943 IRTemp z3 = newTemp(Ity_I64);
6944 IRTemp z2 = newTemp(Ity_I64);
6945 IRTemp z1 = newTemp(Ity_I64);
6946 IRTemp z0 = newTemp(Ity_I64);
6947 IRTemp aEvn, aOdd;
6948 IRTemp a15, a14, a13, a12, a11, a10, a9, a8;
6949 IRTemp a7, a6, a5, a4, a3, a2, a1, a0;
6950 IRTemp b3, b2, b1, b0;
6951
6952 aEvn = aOdd = IRTemp_INVALID;
6953 a15 = a14 = a13 = a12 = a11 = a10 = a9 = a8 = IRTemp_INVALID;
6954 a7 = a6 = a5 = a4 = a3 = a2 = a1 = a0 = IRTemp_INVALID;
6955 b3 = b2 = b1 = b0 = IRTemp_INVALID;
6956
ceriond3e52412005-09-14 21:15:40 +00006957 assign( vA, getVReg(vA_addr));
6958 assign( vB, getVReg(vB_addr));
6959
cerion32aad402005-09-10 12:02:24 +00006960 if (opc1 != 0x4) {
cerion5b2325f2005-12-23 00:55:09 +00006961 vex_printf("dis_av_arith(ppc)(opc1 != 0x4)\n");
cerion32aad402005-09-10 12:02:24 +00006962 return False;
6963 }
6964
6965 switch (opc2) {
6966 /* Add */
ceriond3e52412005-09-14 21:15:40 +00006967 case 0x180: { // vaddcuw (Add Carryout Unsigned Word, AV p136)
cerion32aad402005-09-10 12:02:24 +00006968 DIP("vaddcuw v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion36991ef2005-09-15 12:42:16 +00006969 /* unsigned_ov(x+y) = (y >u not(x)) */
ceriond3e52412005-09-14 21:15:40 +00006970 putVReg( vD_addr, binop(Iop_ShrN32x4,
cerion36991ef2005-09-15 12:42:16 +00006971 binop(Iop_CmpGT32Ux4, mkexpr(vB),
6972 unop(Iop_NotV128, mkexpr(vA))),
ceriond3e52412005-09-14 21:15:40 +00006973 mkU8(31)) );
6974 break;
6975 }
cerion32aad402005-09-10 12:02:24 +00006976 case 0x000: // vaddubm (Add Unsigned Byte Modulo, AV p141)
6977 DIP("vaddubm v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion36991ef2005-09-15 12:42:16 +00006978 putVReg( vD_addr, binop(Iop_Add8x16, mkexpr(vA), mkexpr(vB)) );
6979 break;
6980
cerion32aad402005-09-10 12:02:24 +00006981 case 0x040: // vadduhm (Add Unsigned Half Word Modulo, AV p143)
6982 DIP("vadduhm v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion36991ef2005-09-15 12:42:16 +00006983 putVReg( vD_addr, binop(Iop_Add16x8, mkexpr(vA), mkexpr(vB)) );
6984 break;
6985
cerion32aad402005-09-10 12:02:24 +00006986 case 0x080: // vadduwm (Add Unsigned Word Modulo, AV p145)
6987 DIP("vadduwm v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion36991ef2005-09-15 12:42:16 +00006988 putVReg( vD_addr, binop(Iop_Add32x4, mkexpr(vA), mkexpr(vB)) );
6989 break;
6990
cerion32aad402005-09-10 12:02:24 +00006991 case 0x200: // vaddubs (Add Unsigned Byte Saturate, AV p142)
6992 DIP("vaddubs v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion36991ef2005-09-15 12:42:16 +00006993 putVReg( vD_addr, binop(Iop_QAdd8Ux16, mkexpr(vA), mkexpr(vB)) );
6994 // TODO: set VSCR[SAT], perhaps via new primop: Iop_SatOfQAdd8Ux16
6995 break;
6996
cerion32aad402005-09-10 12:02:24 +00006997 case 0x240: // vadduhs (Add Unsigned Half Word Saturate, AV p144)
6998 DIP("vadduhs v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion36991ef2005-09-15 12:42:16 +00006999 putVReg( vD_addr, binop(Iop_QAdd16Ux8, mkexpr(vA), mkexpr(vB)) );
7000 // TODO: set VSCR[SAT]
7001 break;
7002
cerion32aad402005-09-10 12:02:24 +00007003 case 0x280: // vadduws (Add Unsigned Word Saturate, AV p146)
7004 DIP("vadduws v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion36991ef2005-09-15 12:42:16 +00007005 putVReg( vD_addr, binop(Iop_QAdd32Ux4, mkexpr(vA), mkexpr(vB)) );
7006 // TODO: set VSCR[SAT]
7007 break;
7008
cerion32aad402005-09-10 12:02:24 +00007009 case 0x300: // vaddsbs (Add Signed Byte Saturate, AV p138)
7010 DIP("vaddsbs v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion36991ef2005-09-15 12:42:16 +00007011 putVReg( vD_addr, binop(Iop_QAdd8Sx16, mkexpr(vA), mkexpr(vB)) );
7012 // TODO: set VSCR[SAT]
7013 break;
7014
cerion32aad402005-09-10 12:02:24 +00007015 case 0x340: // vaddshs (Add Signed Half Word Saturate, AV p139)
7016 DIP("vaddshs v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion36991ef2005-09-15 12:42:16 +00007017 putVReg( vD_addr, binop(Iop_QAdd16Sx8, mkexpr(vA), mkexpr(vB)) );
7018 // TODO: set VSCR[SAT]
7019 break;
7020
cerion32aad402005-09-10 12:02:24 +00007021 case 0x380: // vaddsws (Add Signed Word Saturate, AV p140)
7022 DIP("vaddsws v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion36991ef2005-09-15 12:42:16 +00007023 putVReg( vD_addr, binop(Iop_QAdd32Sx4, mkexpr(vA), mkexpr(vB)) );
7024 // TODO: set VSCR[SAT]
7025 break;
7026
7027
cerion32aad402005-09-10 12:02:24 +00007028 /* Subtract */
cerion36991ef2005-09-15 12:42:16 +00007029 case 0x580: { // vsubcuw (Subtract Carryout Unsigned Word, AV p260)
cerion32aad402005-09-10 12:02:24 +00007030 DIP("vsubcuw v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion36991ef2005-09-15 12:42:16 +00007031 /* unsigned_ov(x-y) = (y >u x) */
7032 putVReg( vD_addr, binop(Iop_ShrN32x4,
7033 unop(Iop_NotV128,
7034 binop(Iop_CmpGT32Ux4, mkexpr(vB),
7035 mkexpr(vA))),
7036 mkU8(31)) );
7037 break;
7038 }
cerion32aad402005-09-10 12:02:24 +00007039 case 0x400: // vsububm (Subtract Unsigned Byte Modulo, AV p265)
7040 DIP("vsububm v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion36991ef2005-09-15 12:42:16 +00007041 putVReg( vD_addr, binop(Iop_Sub8x16, mkexpr(vA), mkexpr(vB)) );
7042 break;
7043
cerion32aad402005-09-10 12:02:24 +00007044 case 0x440: // vsubuhm (Subtract Unsigned Half Word Modulo, AV p267)
7045 DIP("vsubuhm v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion36991ef2005-09-15 12:42:16 +00007046 putVReg( vD_addr, binop(Iop_Sub16x8, mkexpr(vA), mkexpr(vB)) );
7047 break;
7048
cerion32aad402005-09-10 12:02:24 +00007049 case 0x480: // vsubuwm (Subtract Unsigned Word Modulo, AV p269)
7050 DIP("vsubuwm v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion36991ef2005-09-15 12:42:16 +00007051 putVReg( vD_addr, binop(Iop_Sub32x4, mkexpr(vA), mkexpr(vB)) );
7052 break;
7053
cerion32aad402005-09-10 12:02:24 +00007054 case 0x600: // vsububs (Subtract Unsigned Byte Saturate, AV p266)
7055 DIP("vsububs v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion36991ef2005-09-15 12:42:16 +00007056 putVReg( vD_addr, binop(Iop_QSub8Ux16, mkexpr(vA), mkexpr(vB)) );
7057 // TODO: set VSCR[SAT]
7058 break;
7059
cerion5b2325f2005-12-23 00:55:09 +00007060 case 0x640: // vsubuhs (Subtract Unsigned HWord Saturate, AV p268)
cerion32aad402005-09-10 12:02:24 +00007061 DIP("vsubuhs v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion36991ef2005-09-15 12:42:16 +00007062 putVReg( vD_addr, binop(Iop_QSub16Ux8, mkexpr(vA), mkexpr(vB)) );
7063 // TODO: set VSCR[SAT]
7064 break;
7065
cerion32aad402005-09-10 12:02:24 +00007066 case 0x680: // vsubuws (Subtract Unsigned Word Saturate, AV p270)
7067 DIP("vsubuws v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion36991ef2005-09-15 12:42:16 +00007068 putVReg( vD_addr, binop(Iop_QSub32Ux4, mkexpr(vA), mkexpr(vB)) );
7069 // TODO: set VSCR[SAT]
7070 break;
7071
cerion32aad402005-09-10 12:02:24 +00007072 case 0x700: // vsubsbs (Subtract Signed Byte Saturate, AV p262)
7073 DIP("vsubsbs v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion36991ef2005-09-15 12:42:16 +00007074 putVReg( vD_addr, binop(Iop_QSub8Sx16, mkexpr(vA), mkexpr(vB)) );
7075 // TODO: set VSCR[SAT]
7076 break;
7077
cerion32aad402005-09-10 12:02:24 +00007078 case 0x740: // vsubshs (Subtract Signed Half Word Saturate, AV p263)
7079 DIP("vsubshs v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion36991ef2005-09-15 12:42:16 +00007080 putVReg( vD_addr, binop(Iop_QSub16Sx8, mkexpr(vA), mkexpr(vB)) );
7081 // TODO: set VSCR[SAT]
7082 break;
7083
cerion32aad402005-09-10 12:02:24 +00007084 case 0x780: // vsubsws (Subtract Signed Word Saturate, AV p264)
7085 DIP("vsubsws v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion36991ef2005-09-15 12:42:16 +00007086 putVReg( vD_addr, binop(Iop_QSub32Sx4, mkexpr(vA), mkexpr(vB)) );
7087 // TODO: set VSCR[SAT]
7088 break;
cerion32aad402005-09-10 12:02:24 +00007089
7090
7091 /* Maximum */
7092 case 0x002: // vmaxub (Maximum Unsigned Byte, AV p182)
7093 DIP("vmaxub v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion36991ef2005-09-15 12:42:16 +00007094 putVReg( vD_addr, binop(Iop_Max8Ux16, mkexpr(vA), mkexpr(vB)) );
7095 break;
cerion32aad402005-09-10 12:02:24 +00007096
7097 case 0x042: // vmaxuh (Maximum Unsigned Half Word, AV p183)
7098 DIP("vmaxuh v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion36991ef2005-09-15 12:42:16 +00007099 putVReg( vD_addr, binop(Iop_Max16Ux8, mkexpr(vA), mkexpr(vB)) );
7100 break;
cerion32aad402005-09-10 12:02:24 +00007101
7102 case 0x082: // vmaxuw (Maximum Unsigned Word, AV p184)
7103 DIP("vmaxuw v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion36991ef2005-09-15 12:42:16 +00007104 putVReg( vD_addr, binop(Iop_Max32Ux4, mkexpr(vA), mkexpr(vB)) );
7105 break;
cerion32aad402005-09-10 12:02:24 +00007106
7107 case 0x102: // vmaxsb (Maximum Signed Byte, AV p179)
7108 DIP("vmaxsb v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion36991ef2005-09-15 12:42:16 +00007109 putVReg( vD_addr, binop(Iop_Max8Sx16, mkexpr(vA), mkexpr(vB)) );
7110 break;
cerion32aad402005-09-10 12:02:24 +00007111
7112 case 0x142: // vmaxsh (Maximum Signed Half Word, AV p180)
7113 DIP("vmaxsh v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion36991ef2005-09-15 12:42:16 +00007114 putVReg( vD_addr, binop(Iop_Max16Sx8, mkexpr(vA), mkexpr(vB)) );
7115 break;
cerion32aad402005-09-10 12:02:24 +00007116
7117 case 0x182: // vmaxsw (Maximum Signed Word, AV p181)
7118 DIP("vmaxsw v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion36991ef2005-09-15 12:42:16 +00007119 putVReg( vD_addr, binop(Iop_Max32Sx4, mkexpr(vA), mkexpr(vB)) );
7120 break;
cerion32aad402005-09-10 12:02:24 +00007121
7122
7123 /* Minimum */
7124 case 0x202: // vminub (Minimum Unsigned Byte, AV p191)
7125 DIP("vminub v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion36991ef2005-09-15 12:42:16 +00007126 putVReg( vD_addr, binop(Iop_Min8Ux16, mkexpr(vA), mkexpr(vB)) );
7127 break;
cerion32aad402005-09-10 12:02:24 +00007128
7129 case 0x242: // vminuh (Minimum Unsigned Half Word, AV p192)
7130 DIP("vminuh v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion36991ef2005-09-15 12:42:16 +00007131 putVReg( vD_addr, binop(Iop_Min16Ux8, mkexpr(vA), mkexpr(vB)) );
7132 break;
cerion32aad402005-09-10 12:02:24 +00007133
7134 case 0x282: // vminuw (Minimum Unsigned Word, AV p193)
7135 DIP("vminuw v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion36991ef2005-09-15 12:42:16 +00007136 putVReg( vD_addr, binop(Iop_Min32Ux4, mkexpr(vA), mkexpr(vB)) );
7137 break;
cerion32aad402005-09-10 12:02:24 +00007138
7139 case 0x302: // vminsb (Minimum Signed Byte, AV p188)
7140 DIP("vminsb v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion36991ef2005-09-15 12:42:16 +00007141 putVReg( vD_addr, binop(Iop_Min8Sx16, mkexpr(vA), mkexpr(vB)) );
7142 break;
cerion32aad402005-09-10 12:02:24 +00007143
7144 case 0x342: // vminsh (Minimum Signed Half Word, AV p189)
7145 DIP("vminsh 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_Min16Sx8, mkexpr(vA), mkexpr(vB)) );
7147 break;
cerion32aad402005-09-10 12:02:24 +00007148
7149 case 0x382: // vminsw (Minimum Signed Word, AV p190)
7150 DIP("vminsw v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion36991ef2005-09-15 12:42:16 +00007151 putVReg( vD_addr, binop(Iop_Min32Sx4, mkexpr(vA), mkexpr(vB)) );
7152 break;
7153
cerion32aad402005-09-10 12:02:24 +00007154
7155 /* Average */
7156 case 0x402: // vavgub (Average Unsigned Byte, AV p152)
7157 DIP("vavgub v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion36991ef2005-09-15 12:42:16 +00007158 putVReg( vD_addr, binop(Iop_Avg8Ux16, mkexpr(vA), mkexpr(vB)) );
7159 break;
cerion32aad402005-09-10 12:02:24 +00007160
7161 case 0x442: // vavguh (Average Unsigned Half Word, AV p153)
7162 DIP("vavguh v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion36991ef2005-09-15 12:42:16 +00007163 putVReg( vD_addr, binop(Iop_Avg16Ux8, mkexpr(vA), mkexpr(vB)) );
7164 break;
cerion32aad402005-09-10 12:02:24 +00007165
7166 case 0x482: // vavguw (Average Unsigned Word, AV p154)
7167 DIP("vavguw v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion36991ef2005-09-15 12:42:16 +00007168 putVReg( vD_addr, binop(Iop_Avg32Ux4, mkexpr(vA), mkexpr(vB)) );
7169 break;
cerion32aad402005-09-10 12:02:24 +00007170
7171 case 0x502: // vavgsb (Average Signed Byte, AV p149)
7172 DIP("vavgsb v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion36991ef2005-09-15 12:42:16 +00007173 putVReg( vD_addr, binop(Iop_Avg8Sx16, mkexpr(vA), mkexpr(vB)) );
7174 break;
cerion32aad402005-09-10 12:02:24 +00007175
7176 case 0x542: // vavgsh (Average Signed Half Word, AV p150)
7177 DIP("vavgsh v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion36991ef2005-09-15 12:42:16 +00007178 putVReg( vD_addr, binop(Iop_Avg16Sx8, mkexpr(vA), mkexpr(vB)) );
7179 break;
cerion32aad402005-09-10 12:02:24 +00007180
7181 case 0x582: // vavgsw (Average Signed Word, AV p151)
7182 DIP("vavgsw v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion36991ef2005-09-15 12:42:16 +00007183 putVReg( vD_addr, binop(Iop_Avg32Sx4, mkexpr(vA), mkexpr(vB)) );
7184 break;
cerion32aad402005-09-10 12:02:24 +00007185
7186
7187 /* Multiply */
7188 case 0x008: // vmuloub (Multiply Odd Unsigned Byte, AV p213)
7189 DIP("vmuloub v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion5b2325f2005-12-23 00:55:09 +00007190 putVReg( vD_addr,
7191 binop(Iop_MullEven8Ux16, mkexpr(vA), mkexpr(vB)));
cerion36991ef2005-09-15 12:42:16 +00007192 break;
cerion32aad402005-09-10 12:02:24 +00007193
7194 case 0x048: // vmulouh (Multiply Odd Unsigned Half Word, AV p214)
7195 DIP("vmulouh v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion5b2325f2005-12-23 00:55:09 +00007196 putVReg( vD_addr,
7197 binop(Iop_MullEven16Ux8, mkexpr(vA), mkexpr(vB)));
cerion36991ef2005-09-15 12:42:16 +00007198 break;
cerion32aad402005-09-10 12:02:24 +00007199
7200 case 0x108: // vmulosb (Multiply Odd Signed Byte, AV p211)
7201 DIP("vmulosb v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion5b2325f2005-12-23 00:55:09 +00007202 putVReg( vD_addr,
7203 binop(Iop_MullEven8Sx16, mkexpr(vA), mkexpr(vB)));
cerion36991ef2005-09-15 12:42:16 +00007204 break;
cerion32aad402005-09-10 12:02:24 +00007205
7206 case 0x148: // vmulosh (Multiply Odd Signed Half Word, AV p212)
7207 DIP("vmulosh v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion5b2325f2005-12-23 00:55:09 +00007208 putVReg( vD_addr,
7209 binop(Iop_MullEven16Sx8, mkexpr(vA), mkexpr(vB)));
cerion36991ef2005-09-15 12:42:16 +00007210 break;
cerion32aad402005-09-10 12:02:24 +00007211
7212 case 0x208: // vmuleub (Multiply Even Unsigned Byte, AV p209)
7213 DIP("vmuleub v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion24d06f12005-11-09 21:34:20 +00007214 putVReg( vD_addr, MK_Iop_MullOdd8Ux16( mkexpr(vA), mkexpr(vB) ));
cerion36991ef2005-09-15 12:42:16 +00007215 break;
cerion32aad402005-09-10 12:02:24 +00007216
7217 case 0x248: // vmuleuh (Multiply Even Unsigned Half Word, AV p210)
7218 DIP("vmuleuh v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion24d06f12005-11-09 21:34:20 +00007219 putVReg( vD_addr, MK_Iop_MullOdd16Ux8( mkexpr(vA), mkexpr(vB) ));
cerion36991ef2005-09-15 12:42:16 +00007220 break;
cerion32aad402005-09-10 12:02:24 +00007221
7222 case 0x308: // vmulesb (Multiply Even Signed Byte, AV p207)
7223 DIP("vmulesb v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion24d06f12005-11-09 21:34:20 +00007224 putVReg( vD_addr, MK_Iop_MullOdd8Sx16( mkexpr(vA), mkexpr(vB) ));
cerion36991ef2005-09-15 12:42:16 +00007225 break;
cerion32aad402005-09-10 12:02:24 +00007226
7227 case 0x348: // vmulesh (Multiply Even Signed Half Word, AV p208)
7228 DIP("vmulesh v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion24d06f12005-11-09 21:34:20 +00007229 putVReg( vD_addr, MK_Iop_MullOdd16Sx8( mkexpr(vA), mkexpr(vB) ));
cerion36991ef2005-09-15 12:42:16 +00007230 break;
cerion32aad402005-09-10 12:02:24 +00007231
7232
7233 /* Sum Across Partial */
cerion4a49b032005-11-08 16:23:07 +00007234 case 0x608: { // vsum4ubs (Sum Partial (1/4) UB Saturate, AV p275)
7235 IRTemp aEE, aEO, aOE, aOO;
7236 aEE = aEO = aOE = aOO = IRTemp_INVALID;
cerion32aad402005-09-10 12:02:24 +00007237 DIP("vsum4ubs v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion32aad402005-09-10 12:02:24 +00007238
cerion4a49b032005-11-08 16:23:07 +00007239 /* vA: V128_8Ux16 -> 4 x V128_32Ux4, sign-extended */
7240 expand8Ux16( mkexpr(vA), &aEvn, &aOdd ); // (15,13...),(14,12...)
7241 expand16Ux8( mkexpr(aEvn), &aEE, &aEO ); // (15,11...),(13, 9...)
7242 expand16Ux8( mkexpr(aOdd), &aOE, &aOO ); // (14,10...),(12, 8...)
7243
7244 /* break V128 to 4xI32's, zero-extending to I64's */
7245 breakV128to4x64U( mkexpr(aEE), &a15, &a11, &a7, &a3 );
7246 breakV128to4x64U( mkexpr(aOE), &a14, &a10, &a6, &a2 );
7247 breakV128to4x64U( mkexpr(aEO), &a13, &a9, &a5, &a1 );
7248 breakV128to4x64U( mkexpr(aOO), &a12, &a8, &a4, &a0 );
7249 breakV128to4x64U( mkexpr(vB), &b3, &b2, &b1, &b0 );
7250
7251 /* add lanes */
7252 assign( z3, binop(Iop_Add64, mkexpr(b3),
cerion5b2325f2005-12-23 00:55:09 +00007253 binop(Iop_Add64,
7254 binop(Iop_Add64, mkexpr(a15), mkexpr(a14)),
7255 binop(Iop_Add64, mkexpr(a13), mkexpr(a12)))) );
cerion4a49b032005-11-08 16:23:07 +00007256 assign( z2, binop(Iop_Add64, mkexpr(b2),
cerion5b2325f2005-12-23 00:55:09 +00007257 binop(Iop_Add64,
7258 binop(Iop_Add64, mkexpr(a11), mkexpr(a10)),
7259 binop(Iop_Add64, mkexpr(a9), mkexpr(a8)))) );
cerion4a49b032005-11-08 16:23:07 +00007260 assign( z1, binop(Iop_Add64, mkexpr(b1),
cerion5b2325f2005-12-23 00:55:09 +00007261 binop(Iop_Add64,
7262 binop(Iop_Add64, mkexpr(a7), mkexpr(a6)),
7263 binop(Iop_Add64, mkexpr(a5), mkexpr(a4)))) );
cerion4a49b032005-11-08 16:23:07 +00007264 assign( z0, binop(Iop_Add64, mkexpr(b0),
cerion5b2325f2005-12-23 00:55:09 +00007265 binop(Iop_Add64,
7266 binop(Iop_Add64, mkexpr(a3), mkexpr(a2)),
7267 binop(Iop_Add64, mkexpr(a1), mkexpr(a0)))) );
cerion4a49b032005-11-08 16:23:07 +00007268
7269 /* saturate-narrow to 32bit, and combine to V128 */
7270 putVReg( vD_addr, mkV128from4x64U( mkexpr(z3), mkexpr(z2),
7271 mkexpr(z1), mkexpr(z0)) );
7272 break;
7273 }
7274 case 0x708: { // vsum4sbs (Sum Partial (1/4) SB Saturate, AV p273)
7275 IRTemp aEE, aEO, aOE, aOO;
7276 aEE = aEO = aOE = aOO = IRTemp_INVALID;
cerion32aad402005-09-10 12:02:24 +00007277 DIP("vsum4sbs v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion32aad402005-09-10 12:02:24 +00007278
cerion4a49b032005-11-08 16:23:07 +00007279 /* vA: V128_8Sx16 -> 4 x V128_32Sx4, sign-extended */
7280 expand8Sx16( mkexpr(vA), &aEvn, &aOdd ); // (15,13...),(14,12...)
7281 expand16Sx8( mkexpr(aEvn), &aEE, &aEO ); // (15,11...),(13, 9...)
7282 expand16Sx8( mkexpr(aOdd), &aOE, &aOO ); // (14,10...),(12, 8...)
7283
7284 /* break V128 to 4xI32's, sign-extending to I64's */
7285 breakV128to4x64S( mkexpr(aEE), &a15, &a11, &a7, &a3 );
7286 breakV128to4x64S( mkexpr(aOE), &a14, &a10, &a6, &a2 );
7287 breakV128to4x64S( mkexpr(aEO), &a13, &a9, &a5, &a1 );
7288 breakV128to4x64S( mkexpr(aOO), &a12, &a8, &a4, &a0 );
7289 breakV128to4x64S( mkexpr(vB), &b3, &b2, &b1, &b0 );
7290
7291 /* add lanes */
7292 assign( z3, binop(Iop_Add64, mkexpr(b3),
cerion5b2325f2005-12-23 00:55:09 +00007293 binop(Iop_Add64,
7294 binop(Iop_Add64, mkexpr(a15), mkexpr(a14)),
7295 binop(Iop_Add64, mkexpr(a13), mkexpr(a12)))) );
cerion4a49b032005-11-08 16:23:07 +00007296 assign( z2, binop(Iop_Add64, mkexpr(b2),
cerion5b2325f2005-12-23 00:55:09 +00007297 binop(Iop_Add64,
7298 binop(Iop_Add64, mkexpr(a11), mkexpr(a10)),
7299 binop(Iop_Add64, mkexpr(a9), mkexpr(a8)))) );
cerion4a49b032005-11-08 16:23:07 +00007300 assign( z1, binop(Iop_Add64, mkexpr(b1),
cerion5b2325f2005-12-23 00:55:09 +00007301 binop(Iop_Add64,
7302 binop(Iop_Add64, mkexpr(a7), mkexpr(a6)),
7303 binop(Iop_Add64, mkexpr(a5), mkexpr(a4)))) );
cerion4a49b032005-11-08 16:23:07 +00007304 assign( z0, binop(Iop_Add64, mkexpr(b0),
cerion5b2325f2005-12-23 00:55:09 +00007305 binop(Iop_Add64,
7306 binop(Iop_Add64, mkexpr(a3), mkexpr(a2)),
7307 binop(Iop_Add64, mkexpr(a1), mkexpr(a0)))) );
cerion4a49b032005-11-08 16:23:07 +00007308
7309 /* saturate-narrow to 32bit, and combine to V128 */
7310 putVReg( vD_addr, mkV128from4x64S( mkexpr(z3), mkexpr(z2),
7311 mkexpr(z1), mkexpr(z0)) );
7312 break;
7313 }
7314 case 0x648: { // vsum4shs (Sum Partial (1/4) SHW Saturate, AV p274)
cerion32aad402005-09-10 12:02:24 +00007315 DIP("vsum4shs v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion32aad402005-09-10 12:02:24 +00007316
cerion4a49b032005-11-08 16:23:07 +00007317 /* vA: V128_16Sx8 -> 2 x V128_32Sx4, sign-extended */
7318 expand16Sx8( mkexpr(vA), &aEvn, &aOdd ); // (7,5...),(6,4...)
7319
7320 /* break V128 to 4xI32's, sign-extending to I64's */
7321 breakV128to4x64S( mkexpr(aEvn), &a7, &a5, &a3, &a1 );
7322 breakV128to4x64S( mkexpr(aOdd), &a6, &a4, &a2, &a0 );
7323 breakV128to4x64S( mkexpr(vB), &b3, &b2, &b1, &b0 );
7324
7325 /* add lanes */
7326 assign( z3, binop(Iop_Add64, mkexpr(b3),
7327 binop(Iop_Add64, mkexpr(a7), mkexpr(a6))));
7328 assign( z2, binop(Iop_Add64, mkexpr(b2),
7329 binop(Iop_Add64, mkexpr(a5), mkexpr(a4))));
7330 assign( z1, binop(Iop_Add64, mkexpr(b1),
7331 binop(Iop_Add64, mkexpr(a3), mkexpr(a2))));
7332 assign( z0, binop(Iop_Add64, mkexpr(b0),
7333 binop(Iop_Add64, mkexpr(a1), mkexpr(a0))));
7334
7335 /* saturate-narrow to 32bit, and combine to V128 */
7336 putVReg( vD_addr, mkV128from4x64S( mkexpr(z3), mkexpr(z2),
7337 mkexpr(z1), mkexpr(z0)) );
7338 break;
7339 }
7340 case 0x688: { // vsum2sws (Sum Partial (1/2) SW Saturate, AV p272)
cerion32aad402005-09-10 12:02:24 +00007341 DIP("vsum2sws v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion32aad402005-09-10 12:02:24 +00007342
cerion4a49b032005-11-08 16:23:07 +00007343 /* break V128 to 4xI32's, sign-extending to I64's */
7344 breakV128to4x64S( mkexpr(vA), &a3, &a2, &a1, &a0 );
7345 breakV128to4x64S( mkexpr(vB), &b3, &b2, &b1, &b0 );
7346
7347 /* add lanes */
7348 assign( z2, binop(Iop_Add64, mkexpr(b2),
7349 binop(Iop_Add64, mkexpr(a3), mkexpr(a2))) );
7350 assign( z0, binop(Iop_Add64, mkexpr(b0),
7351 binop(Iop_Add64, mkexpr(a1), mkexpr(a0))) );
7352
7353 /* saturate-narrow to 32bit, and combine to V128 */
7354 putVReg( vD_addr, mkV128from4x64S( mkU64(0), mkexpr(z2),
7355 mkU64(0), mkexpr(z0)) );
7356 break;
7357 }
7358 case 0x788: { // vsumsws (Sum SW Saturate, AV p271)
cerion32aad402005-09-10 12:02:24 +00007359 DIP("vsumsws v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion32aad402005-09-10 12:02:24 +00007360
cerion4a49b032005-11-08 16:23:07 +00007361 /* break V128 to 4xI32's, sign-extending to I64's */
7362 breakV128to4x64S( mkexpr(vA), &a3, &a2, &a1, &a0 );
7363 breakV128to4x64S( mkexpr(vB), &b3, &b2, &b1, &b0 );
7364
7365 /* add lanes */
7366 assign( z0, binop(Iop_Add64, mkexpr(b0),
cerion5b2325f2005-12-23 00:55:09 +00007367 binop(Iop_Add64,
7368 binop(Iop_Add64, mkexpr(a3), mkexpr(a2)),
7369 binop(Iop_Add64, mkexpr(a1), mkexpr(a0)))) );
cerion4a49b032005-11-08 16:23:07 +00007370
7371 /* saturate-narrow to 32bit, and combine to V128 */
7372 putVReg( vD_addr, mkV128from4x64S( mkU64(0), mkU64(0),
7373 mkU64(0), mkexpr(z0)) );
7374 break;
7375 }
cerion32aad402005-09-10 12:02:24 +00007376 default:
cerion5b2325f2005-12-23 00:55:09 +00007377 vex_printf("dis_av_arith(ppc)(opc2=0x%x)\n", opc2);
cerion32aad402005-09-10 12:02:24 +00007378 return False;
7379 }
7380 return True;
7381}
7382
7383/*
7384 AltiVec Logic Instructions
7385*/
7386static Bool dis_av_logic ( UInt theInstr )
7387{
cerion76de5cf2005-11-18 18:25:12 +00007388 /* VX-Form */
7389 UChar opc1 = ifieldOPC(theInstr);
7390 UChar vD_addr = ifieldRegDS(theInstr);
7391 UChar vA_addr = ifieldRegA(theInstr);
7392 UChar vB_addr = ifieldRegB(theInstr);
7393 UInt opc2 = IFIELD( theInstr, 0, 11 );
cerion32aad402005-09-10 12:02:24 +00007394
cerion225a0342005-09-12 20:49:09 +00007395 IRTemp vA = newTemp(Ity_V128);
7396 IRTemp vB = newTemp(Ity_V128);
7397 assign( vA, getVReg(vA_addr));
7398 assign( vB, getVReg(vB_addr));
7399
cerion32aad402005-09-10 12:02:24 +00007400 if (opc1 != 0x4) {
cerion5b2325f2005-12-23 00:55:09 +00007401 vex_printf("dis_av_logic(ppc)(opc1 != 0x4)\n");
cerion32aad402005-09-10 12:02:24 +00007402 return False;
7403 }
7404
7405 switch (opc2) {
7406 case 0x404: // vand (And, AV p147)
7407 DIP("vand v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion225a0342005-09-12 20:49:09 +00007408 putVReg( vD_addr, binop(Iop_AndV128, mkexpr(vA), mkexpr(vB)) );
7409 break;
cerion32aad402005-09-10 12:02:24 +00007410
7411 case 0x444: // vandc (And, AV p148)
7412 DIP("vandc v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion6e7a0ea2005-09-13 13:34:09 +00007413 putVReg( vD_addr, binop(Iop_AndV128, mkexpr(vA),
cerion76de5cf2005-11-18 18:25:12 +00007414 unop(Iop_NotV128, mkexpr(vB))) );
cerion6e7a0ea2005-09-13 13:34:09 +00007415 break;
cerion32aad402005-09-10 12:02:24 +00007416
7417 case 0x484: // vor (Or, AV p217)
7418 DIP("vor v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion225a0342005-09-12 20:49:09 +00007419 putVReg( vD_addr, binop(Iop_OrV128, mkexpr(vA), mkexpr(vB)) );
7420 break;
cerion32aad402005-09-10 12:02:24 +00007421
7422 case 0x4C4: // vxor (Xor, AV p282)
7423 DIP("vxor v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion225a0342005-09-12 20:49:09 +00007424 putVReg( vD_addr, binop(Iop_XorV128, mkexpr(vA), mkexpr(vB)) );
7425 break;
cerion32aad402005-09-10 12:02:24 +00007426
7427 case 0x504: // vnor (Nor, AV p216)
7428 DIP("vnor v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion6e7a0ea2005-09-13 13:34:09 +00007429 putVReg( vD_addr,
7430 unop(Iop_NotV128, binop(Iop_OrV128, mkexpr(vA), mkexpr(vB))) );
7431 break;
cerion32aad402005-09-10 12:02:24 +00007432
7433 default:
cerion5b2325f2005-12-23 00:55:09 +00007434 vex_printf("dis_av_logic(ppc)(opc2=0x%x)\n", opc2);
cerion32aad402005-09-10 12:02:24 +00007435 return False;
7436 }
7437 return True;
7438}
7439
7440/*
7441 AltiVec Compare Instructions
7442*/
7443static Bool dis_av_cmp ( UInt theInstr )
7444{
cerion76de5cf2005-11-18 18:25:12 +00007445 /* VXR-Form */
7446 UChar opc1 = ifieldOPC(theInstr);
7447 UChar vD_addr = ifieldRegDS(theInstr);
7448 UChar vA_addr = ifieldRegA(theInstr);
7449 UChar vB_addr = ifieldRegB(theInstr);
7450 UChar flag_rC = ifieldBIT10(theInstr);
7451 UInt opc2 = IFIELD( theInstr, 0, 10 );
cerion32aad402005-09-10 12:02:24 +00007452
cerion0c439222005-09-15 14:22:58 +00007453 IRTemp vA = newTemp(Ity_V128);
7454 IRTemp vB = newTemp(Ity_V128);
7455 IRTemp vD = newTemp(Ity_V128);
7456 assign( vA, getVReg(vA_addr));
7457 assign( vB, getVReg(vB_addr));
7458
cerion32aad402005-09-10 12:02:24 +00007459 if (opc1 != 0x4) {
cerion5b2325f2005-12-23 00:55:09 +00007460 vex_printf("dis_av_cmp(ppc)(instr)\n");
cerion32aad402005-09-10 12:02:24 +00007461 return False;
7462 }
7463
7464 switch (opc2) {
7465 case 0x006: // vcmpequb (Compare Equal-to Unsigned B, AV p160)
cerion5b2325f2005-12-23 00:55:09 +00007466 DIP("vcmpequb%s v%d,v%d,v%d\n", (flag_rC ? ".":""),
7467 vD_addr, vA_addr, vB_addr);
cerion0c439222005-09-15 14:22:58 +00007468 assign( vD, binop(Iop_CmpEQ8x16, mkexpr(vA), mkexpr(vB)) );
7469 break;
cerion32aad402005-09-10 12:02:24 +00007470
7471 case 0x046: // vcmpequh (Compare Equal-to Unsigned HW, AV p161)
cerion5b2325f2005-12-23 00:55:09 +00007472 DIP("vcmpequh%s v%d,v%d,v%d\n", (flag_rC ? ".":""),
7473 vD_addr, vA_addr, vB_addr);
cerion0c439222005-09-15 14:22:58 +00007474 assign( vD, binop(Iop_CmpEQ16x8, mkexpr(vA), mkexpr(vB)) );
7475 break;
cerion32aad402005-09-10 12:02:24 +00007476
7477 case 0x086: // vcmpequw (Compare Equal-to Unsigned W, AV p162)
cerion5b2325f2005-12-23 00:55:09 +00007478 DIP("vcmpequw%s v%d,v%d,v%d\n", (flag_rC ? ".":""),
7479 vD_addr, vA_addr, vB_addr);
cerion0c439222005-09-15 14:22:58 +00007480 assign( vD, binop(Iop_CmpEQ32x4, mkexpr(vA), mkexpr(vB)) );
7481 break;
cerion32aad402005-09-10 12:02:24 +00007482
7483 case 0x206: // vcmpgtub (Compare Greater-than Unsigned B, AV p168)
cerion5b2325f2005-12-23 00:55:09 +00007484 DIP("vcmpgtub%s v%d,v%d,v%d\n", (flag_rC ? ".":""),
7485 vD_addr, vA_addr, vB_addr);
cerion0c439222005-09-15 14:22:58 +00007486 assign( vD, binop(Iop_CmpGT8Ux16, mkexpr(vA), mkexpr(vB)) );
7487 break;
cerion32aad402005-09-10 12:02:24 +00007488
7489 case 0x246: // vcmpgtuh (Compare Greater-than Unsigned HW, AV p169)
cerion5b2325f2005-12-23 00:55:09 +00007490 DIP("vcmpgtuh%s v%d,v%d,v%d\n", (flag_rC ? ".":""),
7491 vD_addr, vA_addr, vB_addr);
cerion0c439222005-09-15 14:22:58 +00007492 assign( vD, binop(Iop_CmpGT16Ux8, mkexpr(vA), mkexpr(vB)) );
7493 break;
cerion32aad402005-09-10 12:02:24 +00007494
7495 case 0x286: // vcmpgtuw (Compare Greater-than Unsigned W, AV p170)
cerion5b2325f2005-12-23 00:55:09 +00007496 DIP("vcmpgtuw%s v%d,v%d,v%d\n", (flag_rC ? ".":""),
7497 vD_addr, vA_addr, vB_addr);
cerion0c439222005-09-15 14:22:58 +00007498 assign( vD, binop(Iop_CmpGT32Ux4, mkexpr(vA), mkexpr(vB)) );
7499 break;
cerion32aad402005-09-10 12:02:24 +00007500
7501 case 0x306: // vcmpgtsb (Compare Greater-than Signed B, AV p165)
cerion5b2325f2005-12-23 00:55:09 +00007502 DIP("vcmpgtsb%s v%d,v%d,v%d\n", (flag_rC ? ".":""),
7503 vD_addr, vA_addr, vB_addr);
cerion0c439222005-09-15 14:22:58 +00007504 assign( vD, binop(Iop_CmpGT8Sx16, mkexpr(vA), mkexpr(vB)) );
7505 break;
cerion32aad402005-09-10 12:02:24 +00007506
7507 case 0x346: // vcmpgtsh (Compare Greater-than Signed HW, AV p166)
cerion5b2325f2005-12-23 00:55:09 +00007508 DIP("vcmpgtsh%s v%d,v%d,v%d\n", (flag_rC ? ".":""),
7509 vD_addr, vA_addr, vB_addr);
cerion0c439222005-09-15 14:22:58 +00007510 assign( vD, binop(Iop_CmpGT16Sx8, mkexpr(vA), mkexpr(vB)) );
7511 break;
cerion32aad402005-09-10 12:02:24 +00007512
7513 case 0x386: // vcmpgtsw (Compare Greater-than Signed W, AV p167)
cerion5b2325f2005-12-23 00:55:09 +00007514 DIP("vcmpgtsw%s v%d,v%d,v%d\n", (flag_rC ? ".":""),
7515 vD_addr, vA_addr, vB_addr);
cerion0c439222005-09-15 14:22:58 +00007516 assign( vD, binop(Iop_CmpGT32Sx4, mkexpr(vA), mkexpr(vB)) );
7517 break;
cerion32aad402005-09-10 12:02:24 +00007518
7519 default:
cerion5b2325f2005-12-23 00:55:09 +00007520 vex_printf("dis_av_cmp(ppc)(opc2)\n");
cerion32aad402005-09-10 12:02:24 +00007521 return False;
7522 }
cerion0c439222005-09-15 14:22:58 +00007523
7524 putVReg( vD_addr, mkexpr(vD) );
7525
cerion76de5cf2005-11-18 18:25:12 +00007526 if (flag_rC) {
cerion8ea0d3e2005-11-14 00:44:47 +00007527 set_AV_CR6( mkexpr(vD), True );
cerion0c439222005-09-15 14:22:58 +00007528 }
cerion32aad402005-09-10 12:02:24 +00007529 return True;
7530}
7531
7532/*
7533 AltiVec Multiply-Sum Instructions
7534*/
7535static Bool dis_av_multarith ( UInt theInstr )
7536{
cerion76de5cf2005-11-18 18:25:12 +00007537 /* VA-Form */
7538 UChar opc1 = ifieldOPC(theInstr);
7539 UChar vD_addr = ifieldRegDS(theInstr);
7540 UChar vA_addr = ifieldRegA(theInstr);
7541 UChar vB_addr = ifieldRegB(theInstr);
7542 UChar vC_addr = ifieldRegC(theInstr);
7543 UChar opc2 = toUChar( IFIELD( theInstr, 0, 6 ) );
cerion32aad402005-09-10 12:02:24 +00007544
cerion4a49b032005-11-08 16:23:07 +00007545 IRTemp vA = newTemp(Ity_V128);
7546 IRTemp vB = newTemp(Ity_V128);
7547 IRTemp vC = newTemp(Ity_V128);
sewardj197bd172005-10-12 11:34:33 +00007548 IRTemp zeros = newTemp(Ity_V128);
cerion4a49b032005-11-08 16:23:07 +00007549 IRTemp aLo = newTemp(Ity_V128);
7550 IRTemp bLo = newTemp(Ity_V128);
7551 IRTemp cLo = newTemp(Ity_V128);
7552 IRTemp zLo = newTemp(Ity_V128);
7553 IRTemp aHi = newTemp(Ity_V128);
7554 IRTemp bHi = newTemp(Ity_V128);
7555 IRTemp cHi = newTemp(Ity_V128);
7556 IRTemp zHi = newTemp(Ity_V128);
7557 IRTemp abEvn = newTemp(Ity_V128);
7558 IRTemp abOdd = newTemp(Ity_V128);
7559 IRTemp z3 = newTemp(Ity_I64);
7560 IRTemp z2 = newTemp(Ity_I64);
7561 IRTemp z1 = newTemp(Ity_I64);
7562 IRTemp z0 = newTemp(Ity_I64);
7563 IRTemp ab7, ab6, ab5, ab4, ab3, ab2, ab1, ab0;
7564 IRTemp c3, c2, c1, c0;
7565
7566 ab7 = ab6 = ab5 = ab4 = ab3 = ab2 = ab1 = ab0 = IRTemp_INVALID;
7567 c3 = c2 = c1 = c0 = IRTemp_INVALID;
7568
cerion6f1cc0f2005-09-16 16:02:11 +00007569 assign( vA, getVReg(vA_addr));
7570 assign( vB, getVReg(vB_addr));
7571 assign( vC, getVReg(vC_addr));
cerion4a49b032005-11-08 16:23:07 +00007572 assign( zeros, unop(Iop_Dup32x4, mkU32(0)) );
cerion6f1cc0f2005-09-16 16:02:11 +00007573
cerion32aad402005-09-10 12:02:24 +00007574 if (opc1 != 0x4) {
cerion5b2325f2005-12-23 00:55:09 +00007575 vex_printf("dis_av_multarith(ppc)(instr)\n");
cerion32aad402005-09-10 12:02:24 +00007576 return False;
7577 }
7578
7579 switch (opc2) {
cerion32aad402005-09-10 12:02:24 +00007580 /* Multiply-Add */
cerion5b2325f2005-12-23 00:55:09 +00007581 case 0x20: { // vmhaddshs (Mult Hi, Add Signed HW Saturate, AV p185)
cerion6f1cc0f2005-09-16 16:02:11 +00007582 IRTemp cSigns = newTemp(Ity_V128);
cerion5b2325f2005-12-23 00:55:09 +00007583 DIP("vmhaddshs v%d,v%d,v%d,v%d\n",
7584 vD_addr, vA_addr, vB_addr, vC_addr);
7585 assign(cSigns, binop(Iop_CmpGT16Sx8, mkexpr(zeros), mkexpr(vC)));
7586 assign(aLo, binop(Iop_InterleaveLO16x8, mkexpr(zeros), mkexpr(vA)));
7587 assign(bLo, binop(Iop_InterleaveLO16x8, mkexpr(zeros), mkexpr(vB)));
7588 assign(cLo, binop(Iop_InterleaveLO16x8, mkexpr(cSigns),mkexpr(vC)));
7589 assign(aHi, binop(Iop_InterleaveHI16x8, mkexpr(zeros), mkexpr(vA)));
7590 assign(bHi, binop(Iop_InterleaveHI16x8, mkexpr(zeros), mkexpr(vB)));
7591 assign(cHi, binop(Iop_InterleaveHI16x8, mkexpr(cSigns),mkexpr(vC)));
cerion32aad402005-09-10 12:02:24 +00007592
cerion24d06f12005-11-09 21:34:20 +00007593 assign( zLo, binop(Iop_Add32x4, mkexpr(cLo),
cerion6f1cc0f2005-09-16 16:02:11 +00007594 binop(Iop_SarN32x4,
cerion1ac656a2005-11-04 19:44:48 +00007595 binop(Iop_MullEven16Sx8,
cerion24d06f12005-11-09 21:34:20 +00007596 mkexpr(aLo), mkexpr(bLo)),
7597 mkU8(15))) );
cerion6f1cc0f2005-09-16 16:02:11 +00007598
cerion24d06f12005-11-09 21:34:20 +00007599 assign( zHi, binop(Iop_Add32x4, mkexpr(cHi),
cerion6f1cc0f2005-09-16 16:02:11 +00007600 binop(Iop_SarN32x4,
cerion1ac656a2005-11-04 19:44:48 +00007601 binop(Iop_MullEven16Sx8,
cerion24d06f12005-11-09 21:34:20 +00007602 mkexpr(aHi), mkexpr(bHi)),
7603 mkU8(15))) );
cerion6f1cc0f2005-09-16 16:02:11 +00007604
cerion5b2325f2005-12-23 00:55:09 +00007605 putVReg( vD_addr,
7606 binop(Iop_QNarrow32Sx4, mkexpr(zHi), mkexpr(zLo)) );
cerion6f1cc0f2005-09-16 16:02:11 +00007607 break;
7608 }
cerion5b2325f2005-12-23 00:55:09 +00007609 case 0x21: { // vmhraddshs (Mult High Round, Add Signed HW Saturate, AV p186)
cerion6f1cc0f2005-09-16 16:02:11 +00007610 IRTemp zKonst = newTemp(Ity_V128);
cerion6f1cc0f2005-09-16 16:02:11 +00007611 IRTemp cSigns = newTemp(Ity_V128);
cerion5b2325f2005-12-23 00:55:09 +00007612 DIP("vmhraddshs v%d,v%d,v%d,v%d\n",
7613 vD_addr, vA_addr, vB_addr, vC_addr);
7614 assign(cSigns, binop(Iop_CmpGT16Sx8, mkexpr(zeros), mkexpr(vC)) );
7615 assign(aLo, binop(Iop_InterleaveLO16x8, mkexpr(zeros), mkexpr(vA)));
7616 assign(bLo, binop(Iop_InterleaveLO16x8, mkexpr(zeros), mkexpr(vB)));
7617 assign(cLo, binop(Iop_InterleaveLO16x8, mkexpr(cSigns),mkexpr(vC)));
7618 assign(aHi, binop(Iop_InterleaveHI16x8, mkexpr(zeros), mkexpr(vA)));
7619 assign(bHi, binop(Iop_InterleaveHI16x8, mkexpr(zeros), mkexpr(vB)));
7620 assign(cHi, binop(Iop_InterleaveHI16x8, mkexpr(cSigns),mkexpr(vC)));
cerion32aad402005-09-10 12:02:24 +00007621
cerion6f1cc0f2005-09-16 16:02:11 +00007622 /* shifting our const avoids store/load version of Dup */
cerion4a49b032005-11-08 16:23:07 +00007623 assign( zKonst, binop(Iop_ShlN32x4, unop(Iop_Dup32x4, mkU32(0x1)),
7624 mkU8(14)) );
cerion6f1cc0f2005-09-16 16:02:11 +00007625
cerion24d06f12005-11-09 21:34:20 +00007626 assign( zLo, binop(Iop_Add32x4, mkexpr(cLo),
cerion6f1cc0f2005-09-16 16:02:11 +00007627 binop(Iop_SarN32x4,
7628 binop(Iop_Add32x4, mkexpr(zKonst),
cerion1ac656a2005-11-04 19:44:48 +00007629 binop(Iop_MullEven16Sx8,
cerion24d06f12005-11-09 21:34:20 +00007630 mkexpr(aLo), mkexpr(bLo))),
7631 mkU8(15))) );
cerion6f1cc0f2005-09-16 16:02:11 +00007632
cerion24d06f12005-11-09 21:34:20 +00007633 assign( zHi, binop(Iop_Add32x4, mkexpr(cHi),
cerion6f1cc0f2005-09-16 16:02:11 +00007634 binop(Iop_SarN32x4,
7635 binop(Iop_Add32x4, mkexpr(zKonst),
cerion1ac656a2005-11-04 19:44:48 +00007636 binop(Iop_MullEven16Sx8,
cerion24d06f12005-11-09 21:34:20 +00007637 mkexpr(aHi), mkexpr(bHi))),
7638 mkU8(15))) );
cerion6f1cc0f2005-09-16 16:02:11 +00007639
7640 putVReg( vD_addr, binop(Iop_QNarrow32Sx4, mkexpr(zHi), mkexpr(zLo)) );
7641 break;
7642 }
cerion5b2325f2005-12-23 00:55:09 +00007643 case 0x22: { // vmladduhm (Mult Low, Add Unsigned HW Modulo, AV p194)
7644 DIP("vmladduhm v%d,v%d,v%d,v%d\n",
7645 vD_addr, vA_addr, vB_addr, vC_addr);
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(zeros), 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(zeros), mkexpr(vC)));
7652 assign(zLo, binop(Iop_Add32x4,
7653 binop(Iop_MullEven16Ux8, mkexpr(aLo), mkexpr(bLo)),
7654 mkexpr(cLo)) );
7655 assign(zHi, binop(Iop_Add32x4,
7656 binop(Iop_MullEven16Ux8, mkexpr(aHi), mkexpr(bHi)),
7657 mkexpr(cHi)));
7658 putVReg(vD_addr, binop(Iop_Narrow32x4, mkexpr(zHi), mkexpr(zLo)));
cerion6f1cc0f2005-09-16 16:02:11 +00007659 break;
7660 }
cerion32aad402005-09-10 12:02:24 +00007661
7662
7663 /* Multiply-Sum */
cerion6f1cc0f2005-09-16 16:02:11 +00007664 case 0x24: { // vmsumubm (Multiply Sum Unsigned B Modulo, AV p204)
cerion4a49b032005-11-08 16:23:07 +00007665 IRTemp abEE, abEO, abOE, abOO;
7666 abEE = abEO = abOE = abOO = IRTemp_INVALID;
cerion5b2325f2005-12-23 00:55:09 +00007667 DIP("vmsumubm v%d,v%d,v%d,v%d\n",
7668 vD_addr, vA_addr, vB_addr, vC_addr);
cerion32aad402005-09-10 12:02:24 +00007669
cerion4a49b032005-11-08 16:23:07 +00007670 /* multiply vA,vB (unsigned, widening) */
cerion24d06f12005-11-09 21:34:20 +00007671 assign( abEvn, MK_Iop_MullOdd8Ux16( mkexpr(vA), mkexpr(vB) ));
7672 assign( abOdd, binop(Iop_MullEven8Ux16, mkexpr(vA), mkexpr(vB)) );
cerion4a49b032005-11-08 16:23:07 +00007673
7674 /* evn,odd: V128_16Ux8 -> 2 x V128_32Ux4, zero-extended */
7675 expand16Ux8( mkexpr(abEvn), &abEE, &abEO );
7676 expand16Ux8( mkexpr(abOdd), &abOE, &abOO );
7677
cerion6f1cc0f2005-09-16 16:02:11 +00007678 putVReg( vD_addr,
cerion5b2325f2005-12-23 00:55:09 +00007679 binop(Iop_Add32x4, mkexpr(vC),
7680 binop(Iop_Add32x4,
7681 binop(Iop_Add32x4, mkexpr(abEE), mkexpr(abEO)),
7682 binop(Iop_Add32x4, mkexpr(abOE), mkexpr(abOO)))) );
cerion6f1cc0f2005-09-16 16:02:11 +00007683 break;
7684 }
cerion4a49b032005-11-08 16:23:07 +00007685 case 0x25: { // vmsummbm (Multiply Sum Mixed-Sign B Modulo, AV p201)
7686 IRTemp aEvn, aOdd, bEvn, bOdd;
7687 IRTemp abEE = newTemp(Ity_V128);
7688 IRTemp abEO = newTemp(Ity_V128);
7689 IRTemp abOE = newTemp(Ity_V128);
7690 IRTemp abOO = newTemp(Ity_V128);
7691 aEvn = aOdd = bEvn = bOdd = IRTemp_INVALID;
cerion5b2325f2005-12-23 00:55:09 +00007692 DIP("vmsummbm v%d,v%d,v%d,v%d\n",
7693 vD_addr, vA_addr, vB_addr, vC_addr);
cerion32aad402005-09-10 12:02:24 +00007694
cerion4a49b032005-11-08 16:23:07 +00007695 /* sign-extend vA, zero-extend vB, for mixed-sign multiply
7696 (separating out adjacent lanes to different vectors) */
7697 expand8Sx16( mkexpr(vA), &aEvn, &aOdd );
7698 expand8Ux16( mkexpr(vB), &bEvn, &bOdd );
7699
7700 /* multiply vA, vB, again separating adjacent lanes */
cerion24d06f12005-11-09 21:34:20 +00007701 assign( abEE, MK_Iop_MullOdd16Sx8( mkexpr(aEvn), mkexpr(bEvn) ));
7702 assign( abEO, binop(Iop_MullEven16Sx8, mkexpr(aEvn), mkexpr(bEvn)) );
7703 assign( abOE, MK_Iop_MullOdd16Sx8( mkexpr(aOdd), mkexpr(bOdd) ));
7704 assign( abOO, binop(Iop_MullEven16Sx8, mkexpr(aOdd), mkexpr(bOdd)) );
cerion4a49b032005-11-08 16:23:07 +00007705
7706 /* add results together, + vC */
7707 putVReg( vD_addr,
cerion5b2325f2005-12-23 00:55:09 +00007708 binop(Iop_QAdd32Sx4, mkexpr(vC),
7709 binop(Iop_QAdd32Sx4,
7710 binop(Iop_QAdd32Sx4, mkexpr(abEE), mkexpr(abEO)),
7711 binop(Iop_QAdd32Sx4, mkexpr(abOE), mkexpr(abOO)))) );
cerion4a49b032005-11-08 16:23:07 +00007712 break;
7713 }
cerion6f1cc0f2005-09-16 16:02:11 +00007714 case 0x26: { // vmsumuhm (Multiply Sum Unsigned HW Modulo, AV p205)
cerion5b2325f2005-12-23 00:55:09 +00007715 DIP("vmsumuhm v%d,v%d,v%d,v%d\n",
7716 vD_addr, vA_addr, vB_addr, vC_addr);
cerion24d06f12005-11-09 21:34:20 +00007717 assign( abEvn, MK_Iop_MullOdd16Ux8( mkexpr(vA), mkexpr(vB) ));
7718 assign( abOdd, binop(Iop_MullEven16Ux8, mkexpr(vA), mkexpr(vB)) );
cerion6f1cc0f2005-09-16 16:02:11 +00007719 putVReg( vD_addr,
cerion5b2325f2005-12-23 00:55:09 +00007720 binop(Iop_Add32x4, mkexpr(vC),
7721 binop(Iop_Add32x4, mkexpr(abEvn), mkexpr(abOdd))) );
cerion6f1cc0f2005-09-16 16:02:11 +00007722 break;
7723 }
cerion4a49b032005-11-08 16:23:07 +00007724 case 0x27: { // vmsumuhs (Multiply Sum Unsigned HW Saturate, AV p206)
cerion5b2325f2005-12-23 00:55:09 +00007725 DIP("vmsumuhs v%d,v%d,v%d,v%d\n",
7726 vD_addr, vA_addr, vB_addr, vC_addr);
cerion4a49b032005-11-08 16:23:07 +00007727 /* widening multiply, separating lanes */
cerion24d06f12005-11-09 21:34:20 +00007728 assign( abEvn, MK_Iop_MullOdd16Ux8(mkexpr(vA), mkexpr(vB) ));
7729 assign( abOdd, binop(Iop_MullEven16Ux8, mkexpr(vA), mkexpr(vB)) );
cerion32aad402005-09-10 12:02:24 +00007730
cerion4a49b032005-11-08 16:23:07 +00007731 /* break V128 to 4xI32's, zero-extending to I64's */
7732 breakV128to4x64U( mkexpr(abEvn), &ab7, &ab5, &ab3, &ab1 );
7733 breakV128to4x64U( mkexpr(abOdd), &ab6, &ab4, &ab2, &ab0 );
7734 breakV128to4x64U( mkexpr(vC), &c3, &c2, &c1, &c0 );
7735
7736 /* add lanes */
7737 assign( z3, binop(Iop_Add64, mkexpr(c3),
7738 binop(Iop_Add64, mkexpr(ab7), mkexpr(ab6))));
7739 assign( z2, binop(Iop_Add64, mkexpr(c2),
7740 binop(Iop_Add64, mkexpr(ab5), mkexpr(ab4))));
7741 assign( z1, binop(Iop_Add64, mkexpr(c1),
7742 binop(Iop_Add64, mkexpr(ab3), mkexpr(ab2))));
7743 assign( z0, binop(Iop_Add64, mkexpr(c0),
7744 binop(Iop_Add64, mkexpr(ab1), mkexpr(ab0))));
7745
7746 /* saturate-narrow to 32bit, and combine to V128 */
7747 putVReg( vD_addr, mkV128from4x64U( mkexpr(z3), mkexpr(z2),
7748 mkexpr(z1), mkexpr(z0)) );
7749
cerion6f1cc0f2005-09-16 16:02:11 +00007750 break;
7751 }
cerion4a49b032005-11-08 16:23:07 +00007752 case 0x28: { // vmsumshm (Multiply Sum Signed HW Modulo, AV p202)
cerion5b2325f2005-12-23 00:55:09 +00007753 DIP("vmsumshm v%d,v%d,v%d,v%d\n",
7754 vD_addr, vA_addr, vB_addr, vC_addr);
cerion24d06f12005-11-09 21:34:20 +00007755 assign( abEvn, MK_Iop_MullOdd16Sx8( mkexpr(vA), mkexpr(vB) ));
7756 assign( abOdd, binop(Iop_MullEven16Sx8, mkexpr(vA), mkexpr(vB)) );
cerion4a49b032005-11-08 16:23:07 +00007757 putVReg( vD_addr,
cerion5b2325f2005-12-23 00:55:09 +00007758 binop(Iop_Add32x4, mkexpr(vC),
7759 binop(Iop_Add32x4, mkexpr(abOdd), mkexpr(abEvn))) );
cerion4a49b032005-11-08 16:23:07 +00007760 break;
7761 }
7762 case 0x29: { // vmsumshs (Multiply Sum Signed HW Saturate, AV p203)
cerion5b2325f2005-12-23 00:55:09 +00007763 DIP("vmsumshs v%d,v%d,v%d,v%d\n",
7764 vD_addr, vA_addr, vB_addr, vC_addr);
cerion4a49b032005-11-08 16:23:07 +00007765 /* widening multiply, separating lanes */
cerion24d06f12005-11-09 21:34:20 +00007766 assign( abEvn, MK_Iop_MullOdd16Sx8( mkexpr(vA), mkexpr(vB) ));
7767 assign( abOdd, binop(Iop_MullEven16Sx8, mkexpr(vA), mkexpr(vB)) );
cerion32aad402005-09-10 12:02:24 +00007768
cerion4a49b032005-11-08 16:23:07 +00007769 /* break V128 to 4xI32's, sign-extending to I64's */
7770 breakV128to4x64S( mkexpr(abEvn), &ab7, &ab5, &ab3, &ab1 );
7771 breakV128to4x64S( mkexpr(abOdd), &ab6, &ab4, &ab2, &ab0 );
7772 breakV128to4x64S( mkexpr(vC), &c3, &c2, &c1, &c0 );
7773
7774 /* add lanes */
7775 assign( z3, binop(Iop_Add64, mkexpr(c3),
7776 binop(Iop_Add64, mkexpr(ab7), mkexpr(ab6))));
7777 assign( z2, binop(Iop_Add64, mkexpr(c2),
7778 binop(Iop_Add64, mkexpr(ab5), mkexpr(ab4))));
7779 assign( z1, binop(Iop_Add64, mkexpr(c1),
7780 binop(Iop_Add64, mkexpr(ab3), mkexpr(ab2))));
7781 assign( z0, binop(Iop_Add64, mkexpr(c0),
7782 binop(Iop_Add64, mkexpr(ab1), mkexpr(ab0))));
7783
7784 /* saturate-narrow to 32bit, and combine to V128 */
7785 putVReg( vD_addr, mkV128from4x64S( mkexpr(z3), mkexpr(z2),
7786 mkexpr(z1), mkexpr(z0)) );
7787 break;
7788 }
cerion32aad402005-09-10 12:02:24 +00007789 default:
cerion5b2325f2005-12-23 00:55:09 +00007790 vex_printf("dis_av_multarith(ppc)(opc2)\n");
cerion32aad402005-09-10 12:02:24 +00007791 return False;
7792 }
7793 return True;
7794}
7795
7796/*
7797 AltiVec Shift/Rotate Instructions
7798*/
7799static Bool dis_av_shift ( UInt theInstr )
7800{
cerion76de5cf2005-11-18 18:25:12 +00007801 /* VX-Form */
7802 UChar opc1 = ifieldOPC(theInstr);
7803 UChar vD_addr = ifieldRegDS(theInstr);
7804 UChar vA_addr = ifieldRegA(theInstr);
7805 UChar vB_addr = ifieldRegB(theInstr);
7806 UInt opc2 = IFIELD( theInstr, 0, 11 );
cerion32aad402005-09-10 12:02:24 +00007807
cerion27b3d7e2005-09-14 20:35:47 +00007808 IRTemp vA = newTemp(Ity_V128);
7809 IRTemp vB = newTemp(Ity_V128);
7810 assign( vA, getVReg(vA_addr));
7811 assign( vB, getVReg(vB_addr));
7812
cerion32aad402005-09-10 12:02:24 +00007813 if (opc1 != 0x4){
cerion5b2325f2005-12-23 00:55:09 +00007814 vex_printf("dis_av_shift(ppc)(instr)\n");
cerion32aad402005-09-10 12:02:24 +00007815 return False;
7816 }
7817
7818 switch (opc2) {
7819 /* Rotate */
7820 case 0x004: // vrlb (Rotate Left Integer B, AV p234)
7821 DIP("vrlb v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
sewardj1bee5612005-11-10 18:10:58 +00007822 putVReg( vD_addr, binop(Iop_Rol8x16, mkexpr(vA), mkexpr(vB)) );
cerion0a7b4f42005-09-16 07:54:40 +00007823 break;
cerion32aad402005-09-10 12:02:24 +00007824
7825 case 0x044: // vrlh (Rotate Left Integer HW, AV p235)
7826 DIP("vrlh v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
sewardj1bee5612005-11-10 18:10:58 +00007827 putVReg( vD_addr, binop(Iop_Rol16x8, mkexpr(vA), mkexpr(vB)) );
cerion0a7b4f42005-09-16 07:54:40 +00007828 break;
cerion32aad402005-09-10 12:02:24 +00007829
7830 case 0x084: // vrlw (Rotate Left Integer W, AV p236)
7831 DIP("vrlw v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
sewardj1bee5612005-11-10 18:10:58 +00007832 putVReg( vD_addr, binop(Iop_Rol32x4, mkexpr(vA), mkexpr(vB)) );
cerion0a7b4f42005-09-16 07:54:40 +00007833 break;
cerion32aad402005-09-10 12:02:24 +00007834
7835
7836 /* Shift Left */
7837 case 0x104: // vslb (Shift Left Integer B, AV p240)
7838 DIP("vslb v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion0a7b4f42005-09-16 07:54:40 +00007839 putVReg( vD_addr, binop(Iop_Shl8x16, mkexpr(vA), mkexpr(vB)) );
7840 break;
cerion32aad402005-09-10 12:02:24 +00007841
7842 case 0x144: // vslh (Shift Left Integer HW, AV p242)
7843 DIP("vslh v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion0a7b4f42005-09-16 07:54:40 +00007844 putVReg( vD_addr, binop(Iop_Shl16x8, mkexpr(vA), mkexpr(vB)) );
7845 break;
cerion32aad402005-09-10 12:02:24 +00007846
7847 case 0x184: // vslw (Shift Left Integer W, AV p244)
7848 DIP("vslw v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion0a7b4f42005-09-16 07:54:40 +00007849 putVReg( vD_addr, binop(Iop_Shl32x4, mkexpr(vA), mkexpr(vB)) );
7850 break;
cerion32aad402005-09-10 12:02:24 +00007851
cerion0a7b4f42005-09-16 07:54:40 +00007852 case 0x1C4: { // vsl (Shift Left, AV p239)
cerion0a7b4f42005-09-16 07:54:40 +00007853 IRTemp sh = newTemp(Ity_I8);
sewardj197bd172005-10-12 11:34:33 +00007854 DIP("vsl v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion0a7b4f42005-09-16 07:54:40 +00007855 assign( sh, binop(Iop_And8, mkU8(0x7),
7856 unop(Iop_32to8,
7857 unop(Iop_V128to32, mkexpr(vB)))) );
7858 putVReg( vD_addr,
7859 binop(Iop_ShlV128, mkexpr(vA), mkexpr(sh)) );
7860 break;
7861 }
7862 case 0x40C: { // vslo (Shift Left by Octet, AV p243)
cerion0a7b4f42005-09-16 07:54:40 +00007863 IRTemp sh = newTemp(Ity_I8);
sewardj197bd172005-10-12 11:34:33 +00007864 DIP("vslo v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion0a7b4f42005-09-16 07:54:40 +00007865 assign( sh, binop(Iop_And8, mkU8(0x78),
7866 unop(Iop_32to8,
7867 unop(Iop_V128to32, mkexpr(vB)))) );
7868 putVReg( vD_addr,
7869 binop(Iop_ShlV128, mkexpr(vA), mkexpr(sh)) );
7870 break;
7871 }
7872
cerion32aad402005-09-10 12:02:24 +00007873
7874 /* Shift Right */
7875 case 0x204: // vsrb (Shift Right B, AV p256)
7876 DIP("vsrb v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion0a7b4f42005-09-16 07:54:40 +00007877 putVReg( vD_addr, binop(Iop_Shr8x16, mkexpr(vA), mkexpr(vB)) );
7878 break;
cerion32aad402005-09-10 12:02:24 +00007879
7880 case 0x244: // vsrh (Shift Right HW, AV p257)
7881 DIP("vsrh v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion0a7b4f42005-09-16 07:54:40 +00007882 putVReg( vD_addr, binop(Iop_Shr16x8, mkexpr(vA), mkexpr(vB)) );
7883 break;
cerion32aad402005-09-10 12:02:24 +00007884
7885 case 0x284: // vsrw (Shift Right W, AV p259)
7886 DIP("vsrw v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion0a7b4f42005-09-16 07:54:40 +00007887 putVReg( vD_addr, binop(Iop_Shr32x4, mkexpr(vA), mkexpr(vB)) );
7888 break;
cerion32aad402005-09-10 12:02:24 +00007889
cerion27b3d7e2005-09-14 20:35:47 +00007890 case 0x2C4: { // vsr (Shift Right, AV p251)
cerion27b3d7e2005-09-14 20:35:47 +00007891 IRTemp sh = newTemp(Ity_I8);
sewardj197bd172005-10-12 11:34:33 +00007892 DIP("vsr v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion27b3d7e2005-09-14 20:35:47 +00007893 assign( sh, binop(Iop_And8, mkU8(0x7),
7894 unop(Iop_32to8,
7895 unop(Iop_V128to32, mkexpr(vB)))) );
7896 putVReg( vD_addr,
7897 binop(Iop_ShrV128, mkexpr(vA), mkexpr(sh)) );
7898 break;
7899 }
cerion5b2325f2005-12-23 00:55:09 +00007900 case 0x304: // vsrab (Shift Right Alg B, AV p253)
cerion32aad402005-09-10 12:02:24 +00007901 DIP("vsrab v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion0a7b4f42005-09-16 07:54:40 +00007902 putVReg( vD_addr, binop(Iop_Sar8x16, mkexpr(vA), mkexpr(vB)) );
7903 break;
cerion32aad402005-09-10 12:02:24 +00007904
cerion5b2325f2005-12-23 00:55:09 +00007905 case 0x344: // vsrah (Shift Right Alg HW, AV p254)
cerion32aad402005-09-10 12:02:24 +00007906 DIP("vsrah v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion0a7b4f42005-09-16 07:54:40 +00007907 putVReg( vD_addr, binop(Iop_Sar16x8, mkexpr(vA), mkexpr(vB)) );
7908 break;
cerion32aad402005-09-10 12:02:24 +00007909
cerion5b2325f2005-12-23 00:55:09 +00007910 case 0x384: // vsraw (Shift Right Alg W, AV p255)
cerion32aad402005-09-10 12:02:24 +00007911 DIP("vsraw v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion0a7b4f42005-09-16 07:54:40 +00007912 putVReg( vD_addr, binop(Iop_Sar32x4, mkexpr(vA), mkexpr(vB)) );
7913 break;
cerion32aad402005-09-10 12:02:24 +00007914
cerion0a7b4f42005-09-16 07:54:40 +00007915 case 0x44C: { // vsro (Shift Right by Octet, AV p258)
cerion0a7b4f42005-09-16 07:54:40 +00007916 IRTemp sh = newTemp(Ity_I8);
sewardj197bd172005-10-12 11:34:33 +00007917 DIP("vsro v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion0a7b4f42005-09-16 07:54:40 +00007918 assign( sh, binop(Iop_And8, mkU8(0x78),
7919 unop(Iop_32to8,
7920 unop(Iop_V128to32, mkexpr(vB)))) );
7921 putVReg( vD_addr,
7922 binop(Iop_ShrV128, mkexpr(vA), mkexpr(sh)) );
7923 break;
7924 }
cerion32aad402005-09-10 12:02:24 +00007925
7926 default:
cerion5b2325f2005-12-23 00:55:09 +00007927 vex_printf("dis_av_shift(ppc)(opc2)\n");
cerion32aad402005-09-10 12:02:24 +00007928 return False;
7929 }
7930 return True;
7931}
7932
7933/*
7934 AltiVec Permute Instructions
7935*/
7936static Bool dis_av_permute ( UInt theInstr )
7937{
cerion76de5cf2005-11-18 18:25:12 +00007938 /* VA-Form, VX-Form */
7939 UChar opc1 = ifieldOPC(theInstr);
7940 UChar vD_addr = ifieldRegDS(theInstr);
7941 UChar vA_addr = ifieldRegA(theInstr);
7942 UChar UIMM_5 = vA_addr;
7943 UChar vB_addr = ifieldRegB(theInstr);
7944 UChar vC_addr = ifieldRegC(theInstr);
7945 UChar b10 = ifieldBIT10(theInstr);
7946 UChar SHB_uimm4 = toUChar( IFIELD( theInstr, 6, 4 ) );
7947 UInt opc2 = toUChar( IFIELD( theInstr, 0, 6 ) );
cerion32aad402005-09-10 12:02:24 +00007948
cerion76de5cf2005-11-18 18:25:12 +00007949 UChar SIMM_8 = extend_s_5to8(UIMM_5);
cerion32aad402005-09-10 12:02:24 +00007950
cerion6e7a0ea2005-09-13 13:34:09 +00007951 IRTemp vA = newTemp(Ity_V128);
7952 IRTemp vB = newTemp(Ity_V128);
7953 IRTemp vC = newTemp(Ity_V128);
7954 assign( vA, getVReg(vA_addr));
7955 assign( vB, getVReg(vB_addr));
7956 assign( vC, getVReg(vC_addr));
7957
cerion32aad402005-09-10 12:02:24 +00007958 if (opc1 != 0x4) {
cerion5b2325f2005-12-23 00:55:09 +00007959 vex_printf("dis_av_permute(ppc)(instr)\n");
cerion32aad402005-09-10 12:02:24 +00007960 return False;
7961 }
7962
7963 switch (opc2) {
7964 case 0x2A: // vsel (Conditional Select, AV p238)
7965 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 +00007966 /* vD = (vA & ~vC) | (vB & vC) */
7967 putVReg( vD_addr, binop(Iop_OrV128,
7968 binop(Iop_AndV128, mkexpr(vA), unop(Iop_NotV128, mkexpr(vC))),
7969 binop(Iop_AndV128, mkexpr(vB), mkexpr(vC))) );
7970 return True;
cerion32aad402005-09-10 12:02:24 +00007971
cerion92d9d872005-09-15 21:58:50 +00007972 case 0x2B: { // vperm (Permute, AV p218)
cerion92d9d872005-09-15 21:58:50 +00007973 /* limited to two args for IR, so have to play games... */
sewardjdc1f9132005-10-22 12:49:49 +00007974 IRTemp a_perm = newTemp(Ity_V128);
7975 IRTemp b_perm = newTemp(Ity_V128);
7976 IRTemp mask = newTemp(Ity_V128);
7977 IRTemp vC_andF = newTemp(Ity_V128);
cerion5b2325f2005-12-23 00:55:09 +00007978 DIP("vperm v%d,v%d,v%d,v%d\n",
7979 vD_addr, vA_addr, vB_addr, vC_addr);
sewardjdc1f9132005-10-22 12:49:49 +00007980 /* Limit the Perm8x16 steering values to 0 .. 15 as that is what
7981 IR specifies, and also to hide irrelevant bits from
7982 memcheck */
cerion5b2325f2005-12-23 00:55:09 +00007983 assign( vC_andF,
7984 binop(Iop_AndV128, mkexpr(vC),
7985 unop(Iop_Dup8x16, mkU8(0xF))) );
7986 assign( a_perm,
7987 binop(Iop_Perm8x16, mkexpr(vA), mkexpr(vC_andF)) );
7988 assign( b_perm,
7989 binop(Iop_Perm8x16, mkexpr(vB), mkexpr(vC_andF)) );
cerion92d9d872005-09-15 21:58:50 +00007990 // mask[i8] = (vC[i8]_4 == 1) ? 0xFF : 0x0
7991 assign( mask, binop(Iop_SarN8x16,
7992 binop(Iop_ShlN8x16, mkexpr(vC), mkU8(3)),
7993 mkU8(7)) );
7994 // dst = (a & ~mask) | (b & mask)
7995 putVReg( vD_addr, binop(Iop_OrV128,
7996 binop(Iop_AndV128, mkexpr(a_perm),
7997 unop(Iop_NotV128, mkexpr(mask))),
7998 binop(Iop_AndV128, mkexpr(b_perm),
7999 mkexpr(mask))) );
8000 return True;
8001 }
cerion32aad402005-09-10 12:02:24 +00008002 case 0x2C: // vsldoi (Shift Left Double by Octet Imm, AV p241)
8003 if (b10 != 0) {
cerion5b2325f2005-12-23 00:55:09 +00008004 vex_printf("dis_av_permute(ppc)(vsldoi)\n");
cerion32aad402005-09-10 12:02:24 +00008005 return False;
8006 }
cerion5b2325f2005-12-23 00:55:09 +00008007 DIP("vsldoi v%d,v%d,v%d,%d\n",
8008 vD_addr, vA_addr, vB_addr, SHB_uimm4);
cerion92d9d872005-09-15 21:58:50 +00008009 if (SHB_uimm4 == 0)
8010 putVReg( vD_addr, mkexpr(vA) );
8011 else
8012 putVReg( vD_addr,
cerion5b2325f2005-12-23 00:55:09 +00008013 binop(Iop_OrV128,
8014 binop(Iop_ShlV128, mkexpr(vA), mkU8(SHB_uimm4*8)),
8015 binop(Iop_ShrV128, mkexpr(vB), mkU8((16-SHB_uimm4)*8))) );
cerion92d9d872005-09-15 21:58:50 +00008016 return True;
cerion32aad402005-09-10 12:02:24 +00008017
8018 default:
8019 break; // Fall through...
8020 }
8021
cerion76de5cf2005-11-18 18:25:12 +00008022 opc2 = IFIELD( theInstr, 0, 11 );
cerion32aad402005-09-10 12:02:24 +00008023 switch (opc2) {
8024
8025 /* Merge */
8026 case 0x00C: // vmrghb (Merge High B, AV p195)
8027 DIP("vmrghb v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion92d9d872005-09-15 21:58:50 +00008028 putVReg( vD_addr,
8029 binop(Iop_InterleaveHI8x16, mkexpr(vA), mkexpr(vB)) );
8030 break;
cerion32aad402005-09-10 12:02:24 +00008031
8032 case 0x04C: // vmrghh (Merge High HW, AV p196)
8033 DIP("vmrghh v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion92d9d872005-09-15 21:58:50 +00008034 putVReg( vD_addr,
8035 binop(Iop_InterleaveHI16x8, mkexpr(vA), mkexpr(vB)) );
8036 break;
cerion32aad402005-09-10 12:02:24 +00008037
8038 case 0x08C: // vmrghw (Merge High W, AV p197)
8039 DIP("vmrghw v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion92d9d872005-09-15 21:58:50 +00008040 putVReg( vD_addr,
8041 binop(Iop_InterleaveHI32x4, mkexpr(vA), mkexpr(vB)) );
8042 break;
cerion32aad402005-09-10 12:02:24 +00008043
8044 case 0x10C: // vmrglb (Merge Low B, AV p198)
8045 DIP("vmrglb v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion92d9d872005-09-15 21:58:50 +00008046 putVReg( vD_addr,
8047 binop(Iop_InterleaveLO8x16, mkexpr(vA), mkexpr(vB)) );
8048 break;
cerion32aad402005-09-10 12:02:24 +00008049
8050 case 0x14C: // vmrglh (Merge Low HW, AV p199)
8051 DIP("vmrglh v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion92d9d872005-09-15 21:58:50 +00008052 putVReg( vD_addr,
8053 binop(Iop_InterleaveLO16x8, mkexpr(vA), mkexpr(vB)) );
8054 break;
cerion32aad402005-09-10 12:02:24 +00008055
8056 case 0x18C: // vmrglw (Merge Low W, AV p200)
8057 DIP("vmrglw v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion92d9d872005-09-15 21:58:50 +00008058 putVReg( vD_addr,
8059 binop(Iop_InterleaveLO32x4, mkexpr(vA), mkexpr(vB)) );
8060 break;
8061
cerion32aad402005-09-10 12:02:24 +00008062
8063 /* Splat */
cerion92d9d872005-09-15 21:58:50 +00008064 case 0x20C: { // vspltb (Splat Byte, AV p245)
cerion92d9d872005-09-15 21:58:50 +00008065 /* vD = Dup8x16( vB[UIMM_5] ) */
sewardjd1470942005-10-22 02:01:16 +00008066 UChar sh_uimm = (15 - (UIMM_5 & 15)) * 8;
sewardj197bd172005-10-12 11:34:33 +00008067 DIP("vspltb v%d,v%d,%d\n", vD_addr, vB_addr, UIMM_5);
cerion92d9d872005-09-15 21:58:50 +00008068 putVReg( vD_addr, unop(Iop_Dup8x16,
8069 unop(Iop_32to8, unop(Iop_V128to32,
8070 binop(Iop_ShrV128, mkexpr(vB), mkU8(sh_uimm))))) );
8071 break;
8072 }
8073 case 0x24C: { // vsplth (Splat Half Word, AV p246)
sewardjd1470942005-10-22 02:01:16 +00008074 UChar sh_uimm = (7 - (UIMM_5 & 7)) * 16;
sewardj197bd172005-10-12 11:34:33 +00008075 DIP("vsplth v%d,v%d,%d\n", vD_addr, vB_addr, UIMM_5);
cerion92d9d872005-09-15 21:58:50 +00008076 putVReg( vD_addr, unop(Iop_Dup16x8,
8077 unop(Iop_32to16, unop(Iop_V128to32,
8078 binop(Iop_ShrV128, mkexpr(vB), mkU8(sh_uimm))))) );
8079 break;
8080 }
cerion27b3d7e2005-09-14 20:35:47 +00008081 case 0x28C: { // vspltw (Splat Word, AV p250)
cerion27b3d7e2005-09-14 20:35:47 +00008082 /* vD = Dup32x4( vB[UIMM_5] ) */
sewardjd1470942005-10-22 02:01:16 +00008083 UChar sh_uimm = (3 - (UIMM_5 & 3)) * 32;
sewardj197bd172005-10-12 11:34:33 +00008084 DIP("vspltw v%d,v%d,%d\n", vD_addr, vB_addr, UIMM_5);
cerion27b3d7e2005-09-14 20:35:47 +00008085 putVReg( vD_addr, unop(Iop_Dup32x4,
8086 unop(Iop_V128to32,
8087 binop(Iop_ShrV128, mkexpr(vB), mkU8(sh_uimm)))) );
8088 break;
8089 }
cerion32aad402005-09-10 12:02:24 +00008090 case 0x30C: // vspltisb (Splat Immediate Signed B, AV p247)
8091 DIP("vspltisb v%d,%d\n", vD_addr, (Char)SIMM_8);
cerion92d9d872005-09-15 21:58:50 +00008092 putVReg( vD_addr, unop(Iop_Dup8x16, mkU8(SIMM_8)) );
8093 break;
cerion32aad402005-09-10 12:02:24 +00008094
8095 case 0x34C: // vspltish (Splat Immediate Signed HW, AV p248)
8096 DIP("vspltish v%d,%d\n", vD_addr, (Char)SIMM_8);
cerion5b2325f2005-12-23 00:55:09 +00008097 putVReg( vD_addr,
8098 unop(Iop_Dup16x8, mkU16(extend_s_8to32(SIMM_8))) );
cerion92d9d872005-09-15 21:58:50 +00008099 break;
cerion32aad402005-09-10 12:02:24 +00008100
8101 case 0x38C: // vspltisw (Splat Immediate Signed W, AV p249)
8102 DIP("vspltisw v%d,%d\n", vD_addr, (Char)SIMM_8);
cerion5b2325f2005-12-23 00:55:09 +00008103 putVReg( vD_addr,
8104 unop(Iop_Dup32x4, mkU32(extend_s_8to32(SIMM_8))) );
cerion92d9d872005-09-15 21:58:50 +00008105 break;
cerion32aad402005-09-10 12:02:24 +00008106
8107 default:
cerion5b2325f2005-12-23 00:55:09 +00008108 vex_printf("dis_av_permute(ppc)(opc2)\n");
cerion32aad402005-09-10 12:02:24 +00008109 return False;
8110 }
8111 return True;
8112}
8113
8114/*
8115 AltiVec Pack/Unpack Instructions
8116*/
8117static Bool dis_av_pack ( UInt theInstr )
8118{
cerion76de5cf2005-11-18 18:25:12 +00008119 /* VX-Form */
8120 UChar opc1 = ifieldOPC(theInstr);
8121 UChar vD_addr = ifieldRegDS(theInstr);
8122 UChar vA_addr = ifieldRegA(theInstr);
8123 UChar vB_addr = ifieldRegB(theInstr);
8124 UInt opc2 = IFIELD( theInstr, 0, 11 );
cerion32aad402005-09-10 12:02:24 +00008125
sewardj197bd172005-10-12 11:34:33 +00008126 IRTemp signs = IRTemp_INVALID;
8127 IRTemp zeros = IRTemp_INVALID;
cerion76de5cf2005-11-18 18:25:12 +00008128 IRTemp vA = newTemp(Ity_V128);
8129 IRTemp vB = newTemp(Ity_V128);
cerion3c052792005-09-16 07:13:44 +00008130 assign( vA, getVReg(vA_addr));
8131 assign( vB, getVReg(vB_addr));
8132
cerion32aad402005-09-10 12:02:24 +00008133 if (opc1 != 0x4) {
cerion5b2325f2005-12-23 00:55:09 +00008134 vex_printf("dis_av_pack(ppc)(instr)\n");
cerion32aad402005-09-10 12:02:24 +00008135 return False;
8136 }
8137
8138 switch (opc2) {
8139 /* Packing */
8140 case 0x00E: // vpkuhum (Pack Unsigned HW Unsigned Modulo, AV p224)
8141 DIP("vpkuhum v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
sewardj1bee5612005-11-10 18:10:58 +00008142 putVReg( vD_addr, binop(Iop_Narrow16x8, mkexpr(vA), mkexpr(vB)) );
cerion3c052792005-09-16 07:13:44 +00008143 return True;
cerion32aad402005-09-10 12:02:24 +00008144
8145 case 0x04E: // vpkuwum (Pack Unsigned W Unsigned Modulo, AV p226)
8146 DIP("vpkuwum v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
sewardj1bee5612005-11-10 18:10:58 +00008147 putVReg( vD_addr, binop(Iop_Narrow32x4, mkexpr(vA), mkexpr(vB)) );
cerion3c052792005-09-16 07:13:44 +00008148 return True;
cerion32aad402005-09-10 12:02:24 +00008149
8150 case 0x08E: // vpkuhus (Pack Unsigned HW Unsigned Saturate, AV p225)
8151 DIP("vpkuhus v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion5b2325f2005-12-23 00:55:09 +00008152 putVReg( vD_addr,
8153 binop(Iop_QNarrow16Ux8, mkexpr(vA), mkexpr(vB)) );
cerion3c052792005-09-16 07:13:44 +00008154 // TODO: set VSCR[SAT]
8155 return True;
cerion32aad402005-09-10 12:02:24 +00008156
8157 case 0x0CE: // vpkuwus (Pack Unsigned W Unsigned Saturate, AV p227)
8158 DIP("vpkuwus v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion5b2325f2005-12-23 00:55:09 +00008159 putVReg( vD_addr,
8160 binop(Iop_QNarrow32Ux4, mkexpr(vA), mkexpr(vB)) );
cerion3c052792005-09-16 07:13:44 +00008161 // TODO: set VSCR[SAT]
8162 return True;
cerion32aad402005-09-10 12:02:24 +00008163
cerion3c052792005-09-16 07:13:44 +00008164 case 0x10E: { // vpkshus (Pack Signed HW Unsigned Saturate, AV p221)
cerion3c052792005-09-16 07:13:44 +00008165 // This insn does a signed->unsigned saturating conversion.
8166 // Conversion done here, then uses unsigned->unsigned vpk insn:
8167 // => UnsignedSaturatingNarrow( x & ~ (x >>s 15) )
8168 IRTemp vA_tmp = newTemp(Ity_V128);
8169 IRTemp vB_tmp = newTemp(Ity_V128);
sewardj197bd172005-10-12 11:34:33 +00008170 DIP("vpkshus v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion3c052792005-09-16 07:13:44 +00008171 assign( vA_tmp, binop(Iop_AndV128, mkexpr(vA),
8172 unop(Iop_NotV128,
8173 binop(Iop_SarN16x8,
8174 mkexpr(vA), mkU8(15)))) );
8175 assign( vB_tmp, binop(Iop_AndV128, mkexpr(vB),
8176 unop(Iop_NotV128,
8177 binop(Iop_SarN16x8,
8178 mkexpr(vB), mkU8(15)))) );
8179 putVReg( vD_addr, binop(Iop_QNarrow16Ux8,
8180 mkexpr(vA_tmp), mkexpr(vB_tmp)) );
8181 // TODO: set VSCR[SAT]
8182 return True;
8183 }
8184 case 0x14E: { // vpkswus (Pack Signed W Unsigned Saturate, AV p223)
cerion3c052792005-09-16 07:13:44 +00008185 // This insn does a signed->unsigned saturating conversion.
8186 // Conversion done here, then uses unsigned->unsigned vpk insn:
8187 // => UnsignedSaturatingNarrow( x & ~ (x >>s 31) )
8188 IRTemp vA_tmp = newTemp(Ity_V128);
8189 IRTemp vB_tmp = newTemp(Ity_V128);
sewardj197bd172005-10-12 11:34:33 +00008190 DIP("vpkswus v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion3c052792005-09-16 07:13:44 +00008191 assign( vA_tmp, binop(Iop_AndV128, mkexpr(vA),
8192 unop(Iop_NotV128,
8193 binop(Iop_SarN32x4,
8194 mkexpr(vA), mkU8(31)))) );
8195 assign( vB_tmp, binop(Iop_AndV128, mkexpr(vB),
8196 unop(Iop_NotV128,
8197 binop(Iop_SarN32x4,
8198 mkexpr(vB), mkU8(31)))) );
8199 putVReg( vD_addr, binop(Iop_QNarrow32Ux4,
8200 mkexpr(vA_tmp), mkexpr(vB_tmp)) );
8201 // TODO: set VSCR[SAT]
8202 return True;
8203 }
cerion32aad402005-09-10 12:02:24 +00008204 case 0x18E: // vpkshss (Pack Signed HW Signed Saturate, AV p220)
8205 DIP("vpkshss v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion5b2325f2005-12-23 00:55:09 +00008206 putVReg( vD_addr,
8207 binop(Iop_QNarrow16Sx8, mkexpr(vA), mkexpr(vB)) );
cerion3c052792005-09-16 07:13:44 +00008208 // TODO: set VSCR[SAT]
8209 return True;
cerion32aad402005-09-10 12:02:24 +00008210
8211 case 0x1CE: // vpkswss (Pack Signed W Signed Saturate, AV p222)
8212 DIP("vpkswss v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion5b2325f2005-12-23 00:55:09 +00008213 putVReg( vD_addr,
8214 binop(Iop_QNarrow32Sx4, mkexpr(vA), mkexpr(vB)) );
cerion3c052792005-09-16 07:13:44 +00008215 // TODO: set VSCR[SAT]
8216 return True;
cerion32aad402005-09-10 12:02:24 +00008217
cerion3c052792005-09-16 07:13:44 +00008218 case 0x30E: { // vpkpx (Pack Pixel, AV p219)
cerion3c052792005-09-16 07:13:44 +00008219 /* CAB: Worth a new primop? */
cerion5b2325f2005-12-23 00:55:09 +00008220 /* Using shifts to compact pixel elements, then packing them */
cerion3c052792005-09-16 07:13:44 +00008221 IRTemp a1 = newTemp(Ity_V128);
8222 IRTemp a2 = newTemp(Ity_V128);
8223 IRTemp a3 = newTemp(Ity_V128);
8224 IRTemp a_tmp = newTemp(Ity_V128);
8225 IRTemp b1 = newTemp(Ity_V128);
8226 IRTemp b2 = newTemp(Ity_V128);
8227 IRTemp b3 = newTemp(Ity_V128);
8228 IRTemp b_tmp = newTemp(Ity_V128);
sewardj197bd172005-10-12 11:34:33 +00008229 DIP("vpkpx v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion3c052792005-09-16 07:13:44 +00008230 assign( a1, binop(Iop_ShlN16x8,
8231 binop(Iop_ShrN32x4, mkexpr(vA), mkU8(19)),
8232 mkU8(10)) );
8233 assign( a2, binop(Iop_ShlN16x8,
8234 binop(Iop_ShrN16x8, mkexpr(vA), mkU8(11)),
8235 mkU8(5)) );
8236 assign( a3, binop(Iop_ShrN16x8,
8237 binop(Iop_ShlN16x8, mkexpr(vA), mkU8(8)),
8238 mkU8(11)) );
8239 assign( a_tmp, binop(Iop_OrV128, mkexpr(a1),
8240 binop(Iop_OrV128, mkexpr(a2), mkexpr(a3))) );
8241
8242 assign( b1, binop(Iop_ShlN16x8,
8243 binop(Iop_ShrN32x4, mkexpr(vB), mkU8(19)),
8244 mkU8(10)) );
8245 assign( b2, binop(Iop_ShlN16x8,
8246 binop(Iop_ShrN16x8, mkexpr(vB), mkU8(11)),
8247 mkU8(5)) );
8248 assign( b3, binop(Iop_ShrN16x8,
8249 binop(Iop_ShlN16x8, mkexpr(vB), mkU8(8)),
8250 mkU8(11)) );
8251 assign( b_tmp, binop(Iop_OrV128, mkexpr(b1),
8252 binop(Iop_OrV128, mkexpr(b2), mkexpr(b3))) );
8253
sewardj1bee5612005-11-10 18:10:58 +00008254 putVReg( vD_addr, binop(Iop_Narrow32x4,
cerion3c052792005-09-16 07:13:44 +00008255 mkexpr(a_tmp), mkexpr(b_tmp)) );
8256 return True;
8257 }
cerion32aad402005-09-10 12:02:24 +00008258
8259 default:
8260 break; // Fall through...
8261 }
8262
8263
8264 if (vA_addr != 0) {
cerion5b2325f2005-12-23 00:55:09 +00008265 vex_printf("dis_av_pack(ppc)(vA_addr)\n");
cerion32aad402005-09-10 12:02:24 +00008266 return False;
8267 }
8268
sewardj197bd172005-10-12 11:34:33 +00008269 signs = newTemp(Ity_V128);
8270 zeros = newTemp(Ity_V128);
cerion3c052792005-09-16 07:13:44 +00008271 assign( zeros, unop(Iop_Dup32x4, mkU32(0)) );
8272
cerion32aad402005-09-10 12:02:24 +00008273 switch (opc2) {
8274 /* Unpacking */
cerion3c052792005-09-16 07:13:44 +00008275 case 0x20E: { // vupkhsb (Unpack High Signed B, AV p277)
cerion32aad402005-09-10 12:02:24 +00008276 DIP("vupkhsb v%d,v%d\n", vD_addr, vB_addr);
cerion3c052792005-09-16 07:13:44 +00008277 assign( signs, binop(Iop_CmpGT8Sx16, mkexpr(zeros), mkexpr(vB)) );
cerion5b2325f2005-12-23 00:55:09 +00008278 putVReg( vD_addr,
8279 binop(Iop_InterleaveHI8x16, mkexpr(signs), mkexpr(vB)) );
cerion3c052792005-09-16 07:13:44 +00008280 break;
8281 }
8282 case 0x24E: { // vupkhsh (Unpack High Signed HW, AV p278)
cerion32aad402005-09-10 12:02:24 +00008283 DIP("vupkhsh v%d,v%d\n", vD_addr, vB_addr);
cerion3c052792005-09-16 07:13:44 +00008284 assign( signs, binop(Iop_CmpGT16Sx8, mkexpr(zeros), mkexpr(vB)) );
cerion5b2325f2005-12-23 00:55:09 +00008285 putVReg( vD_addr,
8286 binop(Iop_InterleaveHI16x8, mkexpr(signs), mkexpr(vB)) );
cerion3c052792005-09-16 07:13:44 +00008287 break;
8288 }
8289 case 0x28E: { // vupklsb (Unpack Low Signed B, AV p280)
cerion32aad402005-09-10 12:02:24 +00008290 DIP("vupklsb v%d,v%d\n", vD_addr, vB_addr);
cerion3c052792005-09-16 07:13:44 +00008291 assign( signs, binop(Iop_CmpGT8Sx16, mkexpr(zeros), mkexpr(vB)) );
cerion5b2325f2005-12-23 00:55:09 +00008292 putVReg( vD_addr,
8293 binop(Iop_InterleaveLO8x16, mkexpr(signs), mkexpr(vB)) );
cerion3c052792005-09-16 07:13:44 +00008294 break;
8295 }
8296 case 0x2CE: { // vupklsh (Unpack Low Signed HW, AV p281)
cerion32aad402005-09-10 12:02:24 +00008297 DIP("vupklsh v%d,v%d\n", vD_addr, vB_addr);
cerion3c052792005-09-16 07:13:44 +00008298 assign( signs, binop(Iop_CmpGT16Sx8, mkexpr(zeros), mkexpr(vB)) );
cerion5b2325f2005-12-23 00:55:09 +00008299 putVReg( vD_addr,
8300 binop(Iop_InterleaveLO16x8, mkexpr(signs), mkexpr(vB)) );
cerion3c052792005-09-16 07:13:44 +00008301 break;
8302 }
8303 case 0x34E: { // vupkhpx (Unpack High Pixel16, AV p276)
cerion3c052792005-09-16 07:13:44 +00008304 /* CAB: Worth a new primop? */
8305 /* Using shifts to isolate pixel elements, then expanding them */
8306 IRTemp z0 = newTemp(Ity_V128);
8307 IRTemp z1 = newTemp(Ity_V128);
8308 IRTemp z01 = newTemp(Ity_V128);
8309 IRTemp z2 = newTemp(Ity_V128);
8310 IRTemp z3 = newTemp(Ity_V128);
8311 IRTemp z23 = newTemp(Ity_V128);
sewardj197bd172005-10-12 11:34:33 +00008312 DIP("vupkhpx v%d,v%d\n", vD_addr, vB_addr);
cerion3c052792005-09-16 07:13:44 +00008313 assign( z0, binop(Iop_ShlN16x8,
8314 binop(Iop_SarN16x8, mkexpr(vB), mkU8(15)),
8315 mkU8(8)) );
8316 assign( z1, binop(Iop_ShrN16x8,
8317 binop(Iop_ShlN16x8, mkexpr(vB), mkU8(1)),
8318 mkU8(11)) );
8319 assign( z01, binop(Iop_InterleaveHI16x8, mkexpr(zeros),
8320 binop(Iop_OrV128, mkexpr(z0), mkexpr(z1))) );
8321 assign( z2, binop(Iop_ShrN16x8,
8322 binop(Iop_ShlN16x8,
8323 binop(Iop_ShrN16x8, mkexpr(vB), mkU8(5)),
8324 mkU8(11)),
8325 mkU8(3)) );
8326 assign( z3, binop(Iop_ShrN16x8,
8327 binop(Iop_ShlN16x8, mkexpr(vB), mkU8(11)),
8328 mkU8(11)) );
8329 assign( z23, binop(Iop_InterleaveHI16x8, mkexpr(zeros),
8330 binop(Iop_OrV128, mkexpr(z2), mkexpr(z3))) );
cerion5b2325f2005-12-23 00:55:09 +00008331 putVReg( vD_addr,
8332 binop(Iop_OrV128,
8333 binop(Iop_ShlN32x4, mkexpr(z01), mkU8(16)),
8334 mkexpr(z23)) );
cerion3c052792005-09-16 07:13:44 +00008335 break;
8336 }
8337 case 0x3CE: { // vupklpx (Unpack Low Pixel16, AV p279)
cerion3c052792005-09-16 07:13:44 +00008338 /* identical to vupkhpx, except interleaving LO */
8339 IRTemp z0 = newTemp(Ity_V128);
8340 IRTemp z1 = newTemp(Ity_V128);
8341 IRTemp z01 = newTemp(Ity_V128);
8342 IRTemp z2 = newTemp(Ity_V128);
8343 IRTemp z3 = newTemp(Ity_V128);
8344 IRTemp z23 = newTemp(Ity_V128);
sewardj197bd172005-10-12 11:34:33 +00008345 DIP("vupklpx v%d,v%d\n", vD_addr, vB_addr);
cerion3c052792005-09-16 07:13:44 +00008346 assign( z0, binop(Iop_ShlN16x8,
8347 binop(Iop_SarN16x8, mkexpr(vB), mkU8(15)),
8348 mkU8(8)) );
8349 assign( z1, binop(Iop_ShrN16x8,
8350 binop(Iop_ShlN16x8, mkexpr(vB), mkU8(1)),
8351 mkU8(11)) );
8352 assign( z01, binop(Iop_InterleaveLO16x8, mkexpr(zeros),
8353 binop(Iop_OrV128, mkexpr(z0), mkexpr(z1))) );
8354 assign( z2, binop(Iop_ShrN16x8,
8355 binop(Iop_ShlN16x8,
8356 binop(Iop_ShrN16x8, mkexpr(vB), mkU8(5)),
8357 mkU8(11)),
8358 mkU8(3)) );
8359 assign( z3, binop(Iop_ShrN16x8,
8360 binop(Iop_ShlN16x8, mkexpr(vB), mkU8(11)),
8361 mkU8(11)) );
8362 assign( z23, binop(Iop_InterleaveLO16x8, mkexpr(zeros),
8363 binop(Iop_OrV128, mkexpr(z2), mkexpr(z3))) );
cerion5b2325f2005-12-23 00:55:09 +00008364 putVReg( vD_addr,
8365 binop(Iop_OrV128,
8366 binop(Iop_ShlN32x4, mkexpr(z01), mkU8(16)),
8367 mkexpr(z23)) );
cerion3c052792005-09-16 07:13:44 +00008368 break;
8369 }
cerion32aad402005-09-10 12:02:24 +00008370 default:
cerion5b2325f2005-12-23 00:55:09 +00008371 vex_printf("dis_av_pack(ppc)(opc2)\n");
cerion32aad402005-09-10 12:02:24 +00008372 return False;
8373 }
8374 return True;
8375}
8376
8377
8378/*
8379 AltiVec Floating Point Arithmetic Instructions
8380*/
8381static Bool dis_av_fp_arith ( UInt theInstr )
8382{
cerion76de5cf2005-11-18 18:25:12 +00008383 /* VA-Form */
8384 UChar opc1 = ifieldOPC(theInstr);
8385 UChar vD_addr = ifieldRegDS(theInstr);
8386 UChar vA_addr = ifieldRegA(theInstr);
8387 UChar vB_addr = ifieldRegB(theInstr);
8388 UChar vC_addr = ifieldRegC(theInstr);
cerion32aad402005-09-10 12:02:24 +00008389 UInt opc2=0;
8390
cerion8ea0d3e2005-11-14 00:44:47 +00008391 IRTemp vA = newTemp(Ity_V128);
8392 IRTemp vB = newTemp(Ity_V128);
8393 IRTemp vC = newTemp(Ity_V128);
8394 assign( vA, getVReg(vA_addr));
8395 assign( vB, getVReg(vB_addr));
8396 assign( vC, getVReg(vC_addr));
8397
cerion32aad402005-09-10 12:02:24 +00008398 if (opc1 != 0x4) {
cerion5b2325f2005-12-23 00:55:09 +00008399 vex_printf("dis_av_fp_arith(ppc)(instr)\n");
cerion32aad402005-09-10 12:02:24 +00008400 return False;
8401 }
8402
cerion76de5cf2005-11-18 18:25:12 +00008403 opc2 = IFIELD( theInstr, 0, 6 );
cerion32aad402005-09-10 12:02:24 +00008404 switch (opc2) {
8405 case 0x2E: // vmaddfp (Multiply Add FP, AV p177)
cerion5b2325f2005-12-23 00:55:09 +00008406 DIP("vmaddfp v%d,v%d,v%d,v%d\n",
8407 vD_addr, vA_addr, vC_addr, vB_addr);
8408 putVReg( vD_addr,
8409 binop(Iop_Add32Fx4, mkexpr(vB),
8410 binop(Iop_Mul32Fx4, mkexpr(vA), mkexpr(vC))) );
cerionf3f173c2005-11-14 02:37:44 +00008411 return True;
cerion32aad402005-09-10 12:02:24 +00008412
cerionf3f173c2005-11-14 02:37:44 +00008413 case 0x2F: { // vnmsubfp (Negative Multiply-Subtract FP, AV p215)
cerion5b2325f2005-12-23 00:55:09 +00008414 DIP("vnmsubfp v%d,v%d,v%d,v%d\n",
8415 vD_addr, vA_addr, vC_addr, vB_addr);
8416 putVReg( vD_addr,
8417 binop(Iop_Sub32Fx4,
8418 mkexpr(vB),
8419 binop(Iop_Mul32Fx4, mkexpr(vA), mkexpr(vC))) );
cerionf3f173c2005-11-14 02:37:44 +00008420 return True;
8421 }
cerion32aad402005-09-10 12:02:24 +00008422
8423 default:
8424 break; // Fall through...
8425 }
8426
cerion76de5cf2005-11-18 18:25:12 +00008427 opc2 = IFIELD( theInstr, 0, 11 );
cerion32aad402005-09-10 12:02:24 +00008428 switch (opc2) {
8429 case 0x00A: // vaddfp (Add FP, AV p137)
8430 DIP("vaddfp v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion8ea0d3e2005-11-14 00:44:47 +00008431 putVReg( vD_addr, binop(Iop_Add32Fx4, mkexpr(vA), mkexpr(vB)) );
8432 return True;
cerion32aad402005-09-10 12:02:24 +00008433
8434 case 0x04A: // vsubfp (Subtract FP, AV p261)
8435 DIP("vsubfp v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion8ea0d3e2005-11-14 00:44:47 +00008436 putVReg( vD_addr, binop(Iop_Sub32Fx4, mkexpr(vA), mkexpr(vB)) );
8437 return True;
cerion32aad402005-09-10 12:02:24 +00008438
8439 case 0x40A: // vmaxfp (Maximum FP, AV p178)
8440 DIP("vmaxfp v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion8ea0d3e2005-11-14 00:44:47 +00008441 putVReg( vD_addr, binop(Iop_Max32Fx4, mkexpr(vA), mkexpr(vB)) );
8442 return True;
cerion32aad402005-09-10 12:02:24 +00008443
8444 case 0x44A: // vminfp (Minimum FP, AV p187)
8445 DIP("vminfp v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr);
cerion8ea0d3e2005-11-14 00:44:47 +00008446 putVReg( vD_addr, binop(Iop_Min32Fx4, mkexpr(vA), mkexpr(vB)) );
8447 return True;
cerion32aad402005-09-10 12:02:24 +00008448
8449 default:
8450 break; // Fall through...
8451 }
8452
8453
8454 if (vA_addr != 0) {
cerion5b2325f2005-12-23 00:55:09 +00008455 vex_printf("dis_av_fp_arith(ppc)(vA_addr)\n");
cerion32aad402005-09-10 12:02:24 +00008456 return False;
8457 }
8458
8459 switch (opc2) {
8460 case 0x10A: // vrefp (Reciprocal Esimate FP, AV p228)
8461 DIP("vrefp v%d,v%d\n", vD_addr, vB_addr);
cerion8ea0d3e2005-11-14 00:44:47 +00008462 putVReg( vD_addr, unop(Iop_Recip32Fx4, mkexpr(vB)) );
8463 return True;
cerion32aad402005-09-10 12:02:24 +00008464
cerion5b2325f2005-12-23 00:55:09 +00008465 case 0x14A: // vrsqrtefp (Reciprocal Sqrt Estimate FP, AV p237)
cerion32aad402005-09-10 12:02:24 +00008466 DIP("vrsqrtefp v%d,v%d\n", vD_addr, vB_addr);
cerion8ea0d3e2005-11-14 00:44:47 +00008467 putVReg( vD_addr, unop(Iop_RSqrt32Fx4, mkexpr(vB)) );
8468 return True;
cerion32aad402005-09-10 12:02:24 +00008469
8470 case 0x18A: // vexptefp (2 Raised to the Exp Est FP, AV p173)
8471 DIP("vexptefp v%d,v%d\n", vD_addr, vB_addr);
8472 DIP(" => not implemented\n");
8473 return False;
8474
8475 case 0x1CA: // vlogefp (Log2 Estimate FP, AV p175)
8476 DIP("vlogefp v%d,v%d\n", vD_addr, vB_addr);
8477 DIP(" => not implemented\n");
8478 return False;
8479
8480 default:
cerion5b2325f2005-12-23 00:55:09 +00008481 vex_printf("dis_av_fp_arith(ppc)(opc2=0x%x)\n",opc2);
cerion32aad402005-09-10 12:02:24 +00008482 return False;
8483 }
8484 return True;
8485}
8486
8487/*
8488 AltiVec Floating Point Compare Instructions
8489*/
8490static Bool dis_av_fp_cmp ( UInt theInstr )
8491{
cerion76de5cf2005-11-18 18:25:12 +00008492 /* VXR-Form */
8493 UChar opc1 = ifieldOPC(theInstr);
8494 UChar vD_addr = ifieldRegDS(theInstr);
8495 UChar vA_addr = ifieldRegA(theInstr);
8496 UChar vB_addr = ifieldRegB(theInstr);
8497 UChar flag_rC = ifieldBIT10(theInstr);
8498 UInt opc2 = IFIELD( theInstr, 0, 10 );
cerion32aad402005-09-10 12:02:24 +00008499
cerion8ea0d3e2005-11-14 00:44:47 +00008500 Bool cmp_bounds = False;
8501
8502 IRTemp vA = newTemp(Ity_V128);
8503 IRTemp vB = newTemp(Ity_V128);
8504 IRTemp vD = newTemp(Ity_V128);
8505 assign( vA, getVReg(vA_addr));
8506 assign( vB, getVReg(vB_addr));
8507
cerion32aad402005-09-10 12:02:24 +00008508 if (opc1 != 0x4) {
cerion5b2325f2005-12-23 00:55:09 +00008509 vex_printf("dis_av_fp_cmp(ppc)(instr)\n");
cerion32aad402005-09-10 12:02:24 +00008510 return False;
8511 }
8512
8513 switch (opc2) {
8514 case 0x0C6: // vcmpeqfp (Compare Equal-to FP, AV p159)
cerion5b2325f2005-12-23 00:55:09 +00008515 DIP("vcmpeqfp%s v%d,v%d,v%d\n", (flag_rC ? ".":""),
8516 vD_addr, vA_addr, vB_addr);
cerion8ea0d3e2005-11-14 00:44:47 +00008517 assign( vD, binop(Iop_CmpEQ32Fx4, mkexpr(vA), mkexpr(vB)) );
8518 break;
cerion32aad402005-09-10 12:02:24 +00008519
cerion5b2325f2005-12-23 00:55:09 +00008520 case 0x1C6: // vcmpgefp (Compare Greater-than-or-Equal-to, AV p163)
8521 DIP("vcmpgefp%s v%d,v%d,v%d\n", (flag_rC ? ".":""),
8522 vD_addr, vA_addr, vB_addr);
cerion8ea0d3e2005-11-14 00:44:47 +00008523 assign( vD, binop(Iop_CmpGE32Fx4, mkexpr(vA), mkexpr(vB)) );
8524 break;
cerion32aad402005-09-10 12:02:24 +00008525
8526 case 0x2C6: // vcmpgtfp (Compare Greater-than FP, AV p164)
cerion5b2325f2005-12-23 00:55:09 +00008527 DIP("vcmpgtfp%s v%d,v%d,v%d\n", (flag_rC ? ".":""),
8528 vD_addr, vA_addr, vB_addr);
cerion8ea0d3e2005-11-14 00:44:47 +00008529 assign( vD, binop(Iop_CmpGT32Fx4, mkexpr(vA), mkexpr(vB)) );
8530 break;
cerion32aad402005-09-10 12:02:24 +00008531
cerion8ea0d3e2005-11-14 00:44:47 +00008532 case 0x3C6: { // vcmpbfp (Compare Bounds FP, AV p157)
8533 IRTemp gt = newTemp(Ity_V128);
8534 IRTemp lt = newTemp(Ity_V128);
8535 IRTemp zeros = newTemp(Ity_V128);
cerion5b2325f2005-12-23 00:55:09 +00008536 DIP("vcmpbfp%s v%d,v%d,v%d\n", (flag_rC ? ".":""),
8537 vD_addr, vA_addr, vB_addr);
cerion8ea0d3e2005-11-14 00:44:47 +00008538 cmp_bounds = True;
8539 assign( zeros, unop(Iop_Dup32x4, mkU32(0)) );
8540
8541 /* Note: making use of fact that the ppc backend for compare insns
cerion5b2325f2005-12-23 00:55:09 +00008542 return zero'd lanes if either of the corresponding arg lanes is
8543 a nan.
cerion8ea0d3e2005-11-14 00:44:47 +00008544
8545 Perhaps better to have an irop Iop_isNan32Fx4, but then we'd
8546 need this for the other compares too (vcmpeqfp etc)...
8547 Better still, tighten down the spec for compare irops.
8548 */
8549 assign( gt, unop(Iop_NotV128,
8550 binop(Iop_CmpLE32Fx4, mkexpr(vA), mkexpr(vB))) );
8551 assign( lt, unop(Iop_NotV128,
8552 binop(Iop_CmpGE32Fx4, mkexpr(vA),
cerion5b2325f2005-12-23 00:55:09 +00008553 binop(Iop_Sub32Fx4, mkexpr(zeros),
8554 mkexpr(vB)))) );
cerion8ea0d3e2005-11-14 00:44:47 +00008555
8556 // finally, just shift gt,lt to correct position
8557 assign( vD, binop(Iop_ShlN32x4,
8558 binop(Iop_OrV128,
8559 binop(Iop_AndV128, mkexpr(gt),
8560 unop(Iop_Dup32x4, mkU32(0x2))),
8561 binop(Iop_AndV128, mkexpr(lt),
8562 unop(Iop_Dup32x4, mkU32(0x1)))),
8563 mkU8(30)) );
8564 break;
8565 }
cerion32aad402005-09-10 12:02:24 +00008566
8567 default:
cerion5b2325f2005-12-23 00:55:09 +00008568 vex_printf("dis_av_fp_cmp(ppc)(opc2)\n");
cerion32aad402005-09-10 12:02:24 +00008569 return False;
8570 }
cerion8ea0d3e2005-11-14 00:44:47 +00008571
8572 putVReg( vD_addr, mkexpr(vD) );
8573
cerion76de5cf2005-11-18 18:25:12 +00008574 if (flag_rC) {
cerion8ea0d3e2005-11-14 00:44:47 +00008575 set_AV_CR6( mkexpr(vD), !cmp_bounds );
8576 }
cerion32aad402005-09-10 12:02:24 +00008577 return True;
8578}
8579
8580/*
8581 AltiVec Floating Point Convert/Round Instructions
8582*/
8583static Bool dis_av_fp_convert ( UInt theInstr )
8584{
cerion76de5cf2005-11-18 18:25:12 +00008585 /* VX-Form */
8586 UChar opc1 = ifieldOPC(theInstr);
8587 UChar vD_addr = ifieldRegDS(theInstr);
8588 UChar UIMM_5 = ifieldRegA(theInstr);
8589 UChar vB_addr = ifieldRegB(theInstr);
8590 UInt opc2 = IFIELD( theInstr, 0, 11 );
cerion32aad402005-09-10 12:02:24 +00008591
cerion76de5cf2005-11-18 18:25:12 +00008592 IRTemp vB = newTemp(Ity_V128);
8593 IRTemp vScale = newTemp(Ity_V128);
ceriond963eb42005-11-16 18:02:58 +00008594 IRTemp vInvScale = newTemp(Ity_V128);
sewardj41a7b702005-11-18 22:18:23 +00008595
8596 float scale, inv_scale;
8597
ceriond963eb42005-11-16 18:02:58 +00008598 assign( vB, getVReg(vB_addr));
8599
8600 /* scale = 2^UIMM, cast to float, reinterpreted as uint */
sewardj41a7b702005-11-18 22:18:23 +00008601 scale = (float)( (unsigned int) 1<<UIMM_5 );
sewardj2ead5222005-11-23 03:53:45 +00008602 assign( vScale, unop(Iop_Dup32x4, mkU32( float_to_bits(scale) )) );
sewardj41a7b702005-11-18 22:18:23 +00008603 inv_scale = 1/scale;
cerion5b2325f2005-12-23 00:55:09 +00008604 assign( vInvScale,
8605 unop(Iop_Dup32x4, mkU32( float_to_bits(inv_scale) )) );
ceriond963eb42005-11-16 18:02:58 +00008606
cerion32aad402005-09-10 12:02:24 +00008607 if (opc1 != 0x4) {
cerion5b2325f2005-12-23 00:55:09 +00008608 vex_printf("dis_av_fp_convert(ppc)(instr)\n");
cerion32aad402005-09-10 12:02:24 +00008609 return False;
8610 }
8611
8612 switch (opc2) {
8613 case 0x30A: // vcfux (Convert from Unsigned Fixed-Point W, AV p156)
8614 DIP("vcfux v%d,v%d,%d\n", vD_addr, vB_addr, UIMM_5);
ceriond963eb42005-11-16 18:02:58 +00008615 putVReg( vD_addr, binop(Iop_Mul32Fx4,
8616 unop(Iop_I32UtoFx4, mkexpr(vB)),
8617 mkexpr(vInvScale)) );
8618 return True;
cerion32aad402005-09-10 12:02:24 +00008619
8620 case 0x34A: // vcfsx (Convert from Signed Fixed-Point W, AV p155)
8621 DIP("vcfsx v%d,v%d,%d\n", vD_addr, vB_addr, UIMM_5);
ceriond963eb42005-11-16 18:02:58 +00008622
8623 putVReg( vD_addr, binop(Iop_Mul32Fx4,
8624 unop(Iop_I32StoFx4, mkexpr(vB)),
8625 mkexpr(vInvScale)) );
8626 return True;
cerion32aad402005-09-10 12:02:24 +00008627
8628 case 0x38A: // vctuxs (Convert to Unsigned Fixed-Point W Saturate, AV p172)
8629 DIP("vctuxs v%d,v%d,%d\n", vD_addr, vB_addr, UIMM_5);
ceriond963eb42005-11-16 18:02:58 +00008630 putVReg( vD_addr,
8631 unop(Iop_QFtoI32Ux4_RZ,
8632 binop(Iop_Mul32Fx4, mkexpr(vB), mkexpr(vScale))) );
8633 return True;
cerion32aad402005-09-10 12:02:24 +00008634
8635 case 0x3CA: // vctsxs (Convert to Signed Fixed-Point W Saturate, AV p171)
8636 DIP("vctsxs v%d,v%d,%d\n", vD_addr, vB_addr, UIMM_5);
ceriond963eb42005-11-16 18:02:58 +00008637 putVReg( vD_addr,
8638 unop(Iop_QFtoI32Sx4_RZ,
8639 binop(Iop_Mul32Fx4, mkexpr(vB), mkexpr(vScale))) );
8640 return True;
cerion32aad402005-09-10 12:02:24 +00008641
8642 default:
8643 break; // Fall through...
8644 }
8645
8646 if (UIMM_5 != 0) {
cerion5b2325f2005-12-23 00:55:09 +00008647 vex_printf("dis_av_fp_convert(ppc)(UIMM_5)\n");
cerion32aad402005-09-10 12:02:24 +00008648 return False;
8649 }
8650
8651 switch (opc2) {
8652 case 0x20A: // vrfin (Round to FP Integer Nearest, AV p231)
8653 DIP("vrfin v%d,v%d\n", vD_addr, vB_addr);
ceriond963eb42005-11-16 18:02:58 +00008654 putVReg( vD_addr, unop(Iop_RoundF32x4_RN, mkexpr(vB)) );
8655 break;
cerion32aad402005-09-10 12:02:24 +00008656
8657 case 0x24A: // vrfiz (Round to FP Integer toward zero, AV p233)
8658 DIP("vrfiz v%d,v%d\n", vD_addr, vB_addr);
ceriond963eb42005-11-16 18:02:58 +00008659 putVReg( vD_addr, unop(Iop_RoundF32x4_RZ, mkexpr(vB)) );
8660 break;
cerion32aad402005-09-10 12:02:24 +00008661
8662 case 0x28A: // vrfip (Round to FP Integer toward +inf, AV p232)
8663 DIP("vrfip v%d,v%d\n", vD_addr, vB_addr);
ceriond963eb42005-11-16 18:02:58 +00008664 putVReg( vD_addr, unop(Iop_RoundF32x4_RP, mkexpr(vB)) );
8665 break;
cerion32aad402005-09-10 12:02:24 +00008666
8667 case 0x2CA: // vrfim (Round to FP Integer toward -inf, AV p230)
8668 DIP("vrfim v%d,v%d\n", vD_addr, vB_addr);
ceriond963eb42005-11-16 18:02:58 +00008669 putVReg( vD_addr, unop(Iop_RoundF32x4_RM, mkexpr(vB)) );
8670 break;
cerion32aad402005-09-10 12:02:24 +00008671
8672 default:
cerion5b2325f2005-12-23 00:55:09 +00008673 vex_printf("dis_av_fp_convert(ppc)(opc2)\n");
cerion32aad402005-09-10 12:02:24 +00008674 return False;
8675 }
8676 return True;
8677}
cerion3d870a32005-03-18 12:23:33 +00008678
8679
cerion91ad5362005-01-27 23:02:41 +00008680
8681
8682
8683
cerion896a1372005-01-25 12:24:25 +00008684/*------------------------------------------------------------*/
8685/*--- Disassemble a single instruction ---*/
8686/*------------------------------------------------------------*/
8687
8688/* Disassemble a single instruction into IR. The instruction
sewardj9e6491a2005-07-02 19:24:10 +00008689 is located in host memory at &guest_code[delta]. */
8690
8691static
cerion5b2325f2005-12-23 00:55:09 +00008692DisResult disInstr_PPC_WRK (
sewardj9e6491a2005-07-02 19:24:10 +00008693 Bool put_IP,
sewardjc716aea2006-01-17 01:48:46 +00008694 Bool (*resteerOkFn) ( /*opaque*/void*, Addr64 ),
8695 void* callback_opaque,
sewardj9e6491a2005-07-02 19:24:10 +00008696 Long delta64,
8697 VexArchInfo* archinfo
8698 )
cerion896a1372005-01-25 12:24:25 +00008699{
sewardj9e6491a2005-07-02 19:24:10 +00008700 UChar opc1;
8701 UInt opc2;
8702 DisResult dres;
cerion896a1372005-01-25 12:24:25 +00008703 UInt theInstr;
ceriond953ebb2005-11-29 13:27:20 +00008704 IRType ty = mode64 ? Ity_I64 : Ity_I32;
sewardj5117ce12006-01-27 21:20:15 +00008705 Bool allow_F = False;
8706 Bool allow_V = False;
8707 Bool allow_FX = False;
8708 Bool allow_GX = False;
8709 UInt hwcaps = archinfo->hwcaps;
8710 Long delta;
cerion896a1372005-01-25 12:24:25 +00008711
sewardj059601a2005-11-13 00:53:05 +00008712 /* What insn variants are we supporting today? */
sewardj5117ce12006-01-27 21:20:15 +00008713 if (mode64) {
8714 allow_F = True;
8715 allow_V = (0 != (hwcaps & VEX_HWCAPS_PPC64_V));
8716 allow_FX = (0 != (hwcaps & VEX_HWCAPS_PPC64_FX));
8717 allow_GX = (0 != (hwcaps & VEX_HWCAPS_PPC64_GX));
8718 } else {
8719 allow_F = (0 != (hwcaps & VEX_HWCAPS_PPC32_F));
8720 allow_V = (0 != (hwcaps & VEX_HWCAPS_PPC32_V));
8721 allow_FX = (0 != (hwcaps & VEX_HWCAPS_PPC32_FX));
8722 allow_GX = (0 != (hwcaps & VEX_HWCAPS_PPC32_GX));
8723 }
sewardj059601a2005-11-13 00:53:05 +00008724
sewardj9e6491a2005-07-02 19:24:10 +00008725 /* The running delta */
sewardj5117ce12006-01-27 21:20:15 +00008726 delta = (Long)mkSzAddr(ty, (ULong)delta64);
sewardj9e6491a2005-07-02 19:24:10 +00008727
8728 /* Set result defaults. */
8729 dres.whatNext = Dis_Continue;
8730 dres.len = 0;
8731 dres.continueAt = 0;
cerion896a1372005-01-25 12:24:25 +00008732
cerion1515db92005-01-25 17:21:23 +00008733 /* At least this is simple on PPC32: insns are all 4 bytes long, and
cerion896a1372005-01-25 12:24:25 +00008734 4-aligned. So just fish the whole thing out of memory right now
8735 and have done. */
cerioncf004462005-01-31 15:24:55 +00008736 theInstr = getUIntBigendianly( (UChar*)(&guest_code[delta]) );
cerion896a1372005-01-25 12:24:25 +00008737
sewardj5117ce12006-01-27 21:20:15 +00008738 if (0) vex_printf("insn: 0x%x\n", theInstr);
cerionf0de28c2005-12-13 20:21:11 +00008739
sewardj1eb7e6b2006-01-12 21:13:14 +00008740 DIP("\t0x%llx: ", (ULong)guest_CIA_curr_instr);
sewardjb51f0f42005-07-18 11:38:02 +00008741
8742 /* We may be asked to update the guest CIA before going further. */
8743 if (put_IP)
cerion2831b002005-11-30 19:55:22 +00008744 putGST( PPC_GST_CIA, mkSzImm(ty, guest_CIA_curr_instr) );
cerion896a1372005-01-25 12:24:25 +00008745
sewardjce02aa72006-01-12 12:27:58 +00008746 /* Spot "Special" instructions (see comment at top of file). */
sewardj1eb7e6b2006-01-12 21:13:14 +00008747 {
sewardjce02aa72006-01-12 12:27:58 +00008748 UChar* code = (UChar*)(guest_code + delta);
sewardj1eb7e6b2006-01-12 21:13:14 +00008749 /* Spot the 16-byte preamble:
8750 32-bit mode:
8751 54001800 rlwinm 0,0,3,0,0
8752 54006800 rlwinm 0,0,13,0,0
8753 5400E800 rlwinm 0,0,29,0,0
8754 54009800 rlwinm 0,0,19,0,0
8755 64-bit mode:
8756 78001800 rotldi 0,0,3
8757 78006800 rotldi 0,0,13
8758 7800E802 rotldi 0,0,61
8759 78009802 rotldi 0,0,51
cerion896a1372005-01-25 12:24:25 +00008760 */
sewardj1eb7e6b2006-01-12 21:13:14 +00008761 UInt word1 = mode64 ? 0x78001800 : 0x54001800;
8762 UInt word2 = mode64 ? 0x78006800 : 0x54006800;
8763 UInt word3 = mode64 ? 0x7800E802 : 0x5400E800;
8764 UInt word4 = mode64 ? 0x78009802 : 0x54009800;
8765 if (getUIntBigendianly(code+ 0) == word1 &&
8766 getUIntBigendianly(code+ 4) == word2 &&
8767 getUIntBigendianly(code+ 8) == word3 &&
8768 getUIntBigendianly(code+12) == word4) {
sewardjce02aa72006-01-12 12:27:58 +00008769 /* Got a "Special" instruction preamble. Which one is it? */
8770 if (getUIntBigendianly(code+16) == 0x7C210B78 /* or 1,1,1 */) {
8771 /* %R3 = client_request ( %R4 ) */
8772 DIP("r3 = client_request ( %%r4 )\n");
8773 delta += 20;
8774 irbb->next = mkSzImm( ty, guest_CIA_bbstart + delta );
8775 irbb->jumpkind = Ijk_ClientReq;
8776 dres.whatNext = Dis_StopHere;
8777 goto decode_success;
8778 }
8779 else
8780 if (getUIntBigendianly(code+16) == 0x7C421378 /* or 2,2,2 */) {
8781 /* %R3 = guest_NRADDR */
8782 DIP("r3 = guest_NRADDR\n");
8783 delta += 20;
8784 dres.len = 20;
8785 putIReg(3, IRExpr_Get( OFFB_NRADDR, ty ));
8786 goto decode_success;
8787 }
8788 else
8789 if (getUIntBigendianly(code+16) == 0x7C631B78 /* or 3,3,3 */) {
8790 /* branch-and-link-to-noredir %R11 */
8791 DIP("branch-and-link-to-noredir r11\n");
8792 delta += 20;
sewardj1eb7e6b2006-01-12 21:13:14 +00008793 putGST( PPC_GST_LR, mkSzImm(ty, guest_CIA_bbstart + (Long)delta) );
sewardjce02aa72006-01-12 12:27:58 +00008794 irbb->next = getIReg(11);
8795 irbb->jumpkind = Ijk_NoRedir;
8796 dres.whatNext = Dis_StopHere;
8797 goto decode_success;
8798 }
sewardj5ff11dd2006-01-20 14:19:25 +00008799 else
8800 if (mode64
8801 && getUIntBigendianly(code+16) == 0x7C842378 /* or 4,4,4 */) {
8802 /* %R3 = guest_NRADDR_GPR2 */
8803 DIP("r3 = guest_NRADDR_GPR2\n");
8804 delta += 20;
8805 dres.len = 20;
8806 vassert(ty == Ity_I64);
8807 putIReg(3, IRExpr_Get( OFFB64_NRADDR_GPR2, ty ));
8808 goto decode_success;
8809 }
sewardjce02aa72006-01-12 12:27:58 +00008810 /* We don't know what it is. Set opc1/opc2 so decode_failure
8811 can print the insn following the Special-insn preamble. */
8812 theInstr = getUIntBigendianly(code+16);
8813 opc1 = ifieldOPC(theInstr);
8814 opc2 = ifieldOPClo10(theInstr);
8815 goto decode_failure;
8816 /*NOTREACHED*/
cerion896a1372005-01-25 12:24:25 +00008817 }
8818 }
8819
cerion76de5cf2005-11-18 18:25:12 +00008820 opc1 = ifieldOPC(theInstr);
sewardjb51f0f42005-07-18 11:38:02 +00008821 opc2 = ifieldOPClo10(theInstr);
cerion932ad942005-01-30 10:18:50 +00008822
cerion91ad5362005-01-27 23:02:41 +00008823 // Note: all 'reserved' bits must be cleared, else invalid
8824 switch (opc1) {
cerion896a1372005-01-25 12:24:25 +00008825
cerione9d361a2005-03-04 17:35:29 +00008826 /* Integer Arithmetic Instructions */
8827 case 0x0C: case 0x0D: case 0x0E: // addic, addic., addi
8828 case 0x0F: case 0x07: case 0x08: // addis, mulli, subfic
8829 if (dis_int_arith( theInstr )) goto decode_success;
cerionb85e8bb2005-02-16 08:54:33 +00008830 goto decode_failure;
cerion7aa4bbc2005-01-29 09:32:07 +00008831
cerione9d361a2005-03-04 17:35:29 +00008832 /* Integer Compare Instructions */
8833 case 0x0B: case 0x0A: // cmpi, cmpli
8834 if (dis_int_cmp( theInstr )) goto decode_success;
cerionb85e8bb2005-02-16 08:54:33 +00008835 goto decode_failure;
cerion7aa4bbc2005-01-29 09:32:07 +00008836
cerione9d361a2005-03-04 17:35:29 +00008837 /* Integer Logical Instructions */
8838 case 0x1C: case 0x1D: case 0x18: // andi., andis., ori
8839 case 0x19: case 0x1A: case 0x1B: // oris, xori, xoris
8840 if (dis_int_logic( theInstr )) goto decode_success;
cerionb85e8bb2005-02-16 08:54:33 +00008841 goto decode_failure;
cerion7aa4bbc2005-01-29 09:32:07 +00008842
cerione9d361a2005-03-04 17:35:29 +00008843 /* Integer Rotate Instructions */
8844 case 0x14: case 0x15: case 0x17: // rlwimi, rlwinm, rlwnm
8845 if (dis_int_rot( theInstr )) goto decode_success;
cerionb85e8bb2005-02-16 08:54:33 +00008846 goto decode_failure;
cerion645c9302005-01-31 10:09:59 +00008847
cerionf0de28c2005-12-13 20:21:11 +00008848 /* 64bit Integer Rotate Instructions */
8849 case 0x1E: // rldcl, rldcr, rldic, rldicl, rldicr, rldimi
8850 if (dis_int_rot( theInstr )) goto decode_success;
8851 goto decode_failure;
8852
cerione9d361a2005-03-04 17:35:29 +00008853 /* Integer Load Instructions */
8854 case 0x22: case 0x23: case 0x2A: // lbz, lbzu, lha
8855 case 0x2B: case 0x28: case 0x29: // lhau, lhz, lhzu
8856 case 0x20: case 0x21: // lwz, lwzu
8857 if (dis_int_load( theInstr )) goto decode_success;
cerionb85e8bb2005-02-16 08:54:33 +00008858 goto decode_failure;
cerion645c9302005-01-31 10:09:59 +00008859
cerione9d361a2005-03-04 17:35:29 +00008860 /* Integer Store Instructions */
8861 case 0x26: case 0x27: case 0x2C: // stb, stbu, sth
8862 case 0x2D: case 0x24: case 0x25: // sthu, stw, stwu
8863 if (dis_int_store( theInstr )) goto decode_success;
cerionb85e8bb2005-02-16 08:54:33 +00008864 goto decode_failure;
ceriond23be4e2005-01-31 07:23:07 +00008865
sewardj7787af42005-08-04 18:32:19 +00008866 /* Integer Load and Store Multiple Instructions */
8867 case 0x2E: case 0x2F: // lmw, stmw
8868 if (dis_int_ldst_mult( theInstr )) goto decode_success;
8869 goto decode_failure;
cerion645c9302005-01-31 10:09:59 +00008870
cerione9d361a2005-03-04 17:35:29 +00008871 /* Branch Instructions */
8872 case 0x12: case 0x10: // b, bc
sewardjc716aea2006-01-17 01:48:46 +00008873 if (dis_branch(theInstr, &dres, resteerOkFn, callback_opaque))
8874 goto decode_success;
cerionb85e8bb2005-02-16 08:54:33 +00008875 goto decode_failure;
cerion896a1372005-01-25 12:24:25 +00008876
cerione9d361a2005-03-04 17:35:29 +00008877 /* System Linkage Instructions */
cerion8c3adda2005-01-31 11:54:05 +00008878 case 0x11: // sc
sewardj9e6491a2005-07-02 19:24:10 +00008879 if (dis_syslink(theInstr, &dres)) goto decode_success;
cerionb85e8bb2005-02-16 08:54:33 +00008880 goto decode_failure;
cerion26d07b22005-02-02 17:13:28 +00008881
sewardj334870d2006-02-07 16:42:39 +00008882 /* Trap Instructions */
8883 case 0x02: case 0x03: // tdi, twi
8884 if (dis_trapi(theInstr, &dres)) goto decode_success;
8885 goto decode_failure;
cerion8c3adda2005-01-31 11:54:05 +00008886
cerion3d870a32005-03-18 12:23:33 +00008887 /* Floating Point Load Instructions */
cerion094d1392005-06-20 13:45:57 +00008888 case 0x30: case 0x31: case 0x32: // lfs, lfsu, lfd
8889 case 0x33: // lfdu
sewardj5117ce12006-01-27 21:20:15 +00008890 if (!allow_F) goto decode_noF;
cerion3d870a32005-03-18 12:23:33 +00008891 if (dis_fp_load( theInstr )) goto decode_success;
cerione9d361a2005-03-04 17:35:29 +00008892 goto decode_failure;
cerion995bc362005-02-03 11:03:31 +00008893
cerion3d870a32005-03-18 12:23:33 +00008894 /* Floating Point Store Instructions */
8895 case 0x34: case 0x35: case 0x36: // stfsx, stfsux, stfdx
8896 case 0x37: // stfdux
sewardj5117ce12006-01-27 21:20:15 +00008897 if (!allow_F) goto decode_noF;
cerion3d870a32005-03-18 12:23:33 +00008898 if (dis_fp_store( theInstr )) goto decode_success;
8899 goto decode_failure;
8900
cerionf0de28c2005-12-13 20:21:11 +00008901 /* 64bit Integer Loads */
8902 case 0x3A: // ld, ldu, lwa
8903 if (!mode64) goto decode_failure;
8904 if (dis_int_load( theInstr )) goto decode_success;
8905 goto decode_failure;
8906
sewardje14bb9f2005-07-22 09:39:02 +00008907 case 0x3B:
sewardj5117ce12006-01-27 21:20:15 +00008908 if (!allow_F) goto decode_noF;
cerion76de5cf2005-11-18 18:25:12 +00008909 opc2 = IFIELD(theInstr, 1, 5);
sewardje14bb9f2005-07-22 09:39:02 +00008910 switch (opc2) {
ceriond953ebb2005-11-29 13:27:20 +00008911 /* Floating Point Arith Instructions */
8912 case 0x12: case 0x14: case 0x15: // fdivs, fsubs, fadds
sewardj5117ce12006-01-27 21:20:15 +00008913 case 0x19: // fmuls
ceriond953ebb2005-11-29 13:27:20 +00008914 if (dis_fp_arith(theInstr)) goto decode_success;
8915 goto decode_failure;
sewardj5117ce12006-01-27 21:20:15 +00008916 case 0x16: // fsqrts
8917 if (!allow_FX) goto decode_noFX;
8918 if (dis_fp_arith(theInstr)) goto decode_success;
8919 goto decode_failure;
8920 case 0x18: // fres
8921 if (!allow_GX) goto decode_noGX;
8922 if (dis_fp_arith(theInstr)) goto decode_success;
8923 goto decode_failure;
8924
ceriond953ebb2005-11-29 13:27:20 +00008925 /* Floating Point Mult-Add Instructions */
8926 case 0x1C: case 0x1D: case 0x1E: // fmsubs, fmadds, fnmsubs
8927 case 0x1F: // fnmadds
8928 if (dis_fp_multadd(theInstr)) goto decode_success;
8929 goto decode_failure;
sewardj79fd33f2006-01-29 17:07:57 +00008930
8931 case 0x1A: // frsqrtes
8932 if (!allow_GX) goto decode_noGX;
8933 if (dis_fp_arith(theInstr)) goto decode_success;
8934 goto decode_failure;
ceriond953ebb2005-11-29 13:27:20 +00008935
8936 default:
8937 goto decode_failure;
sewardje14bb9f2005-07-22 09:39:02 +00008938 }
8939 break;
cerion3d870a32005-03-18 12:23:33 +00008940
cerionf0de28c2005-12-13 20:21:11 +00008941 /* 64bit Integer Stores */
8942 case 0x3E: // std, stdu
8943 if (!mode64) goto decode_failure;
8944 if (dis_int_store( theInstr )) goto decode_success;
8945 goto decode_failure;
8946
cerion3d870a32005-03-18 12:23:33 +00008947 case 0x3F:
sewardj5117ce12006-01-27 21:20:15 +00008948 if (!allow_F) goto decode_noF;
cerion5b2325f2005-12-23 00:55:09 +00008949 /* Instrs using opc[1:5] never overlap instrs using opc[1:10],
cerion3d870a32005-03-18 12:23:33 +00008950 so we can simply fall through the first switch statement */
8951
cerion76de5cf2005-11-18 18:25:12 +00008952 opc2 = IFIELD(theInstr, 1, 5);
cerion3d870a32005-03-18 12:23:33 +00008953 switch (opc2) {
ceriond953ebb2005-11-29 13:27:20 +00008954 /* Floating Point Arith Instructions */
sewardj5117ce12006-01-27 21:20:15 +00008955 case 0x12: case 0x14: case 0x15: // fdiv, fsub, fadd
8956 case 0x19: // fmul
8957 if (dis_fp_arith(theInstr)) goto decode_success;
8958 goto decode_failure;
8959 case 0x16: // fsqrt
8960 if (!allow_FX) goto decode_noFX;
8961 if (dis_fp_arith(theInstr)) goto decode_success;
8962 goto decode_failure;
8963 case 0x17: case 0x1A: // fsel, frsqrte
8964 if (!allow_GX) goto decode_noGX;
ceriond953ebb2005-11-29 13:27:20 +00008965 if (dis_fp_arith(theInstr)) goto decode_success;
8966 goto decode_failure;
sewardje14bb9f2005-07-22 09:39:02 +00008967
ceriond953ebb2005-11-29 13:27:20 +00008968 /* Floating Point Mult-Add Instructions */
8969 case 0x1C: case 0x1D: case 0x1E: // fmsub, fmadd, fnmsub
8970 case 0x1F: // fnmadd
8971 if (dis_fp_multadd(theInstr)) goto decode_success;
8972 goto decode_failure;
sewardj79fd33f2006-01-29 17:07:57 +00008973
8974 case 0x18: // fre
8975 if (!allow_GX) goto decode_noGX;
8976 if (dis_fp_arith(theInstr)) goto decode_success;
8977 goto decode_failure;
ceriond953ebb2005-11-29 13:27:20 +00008978
8979 default:
8980 break; // Fall through
cerion3d870a32005-03-18 12:23:33 +00008981 }
8982
cerion76de5cf2005-11-18 18:25:12 +00008983 opc2 = IFIELD(theInstr, 1, 10);
sewardje14bb9f2005-07-22 09:39:02 +00008984 switch (opc2) {
ceriond953ebb2005-11-29 13:27:20 +00008985 /* Floating Point Compare Instructions */
8986 case 0x000: // fcmpu
8987 case 0x020: // fcmpo
8988 if (dis_fp_cmp(theInstr)) goto decode_success;
8989 goto decode_failure;
cerion2831b002005-11-30 19:55:22 +00008990
ceriond953ebb2005-11-29 13:27:20 +00008991 /* Floating Point Rounding/Conversion Instructions */
8992 case 0x00C: // frsp
8993 case 0x00E: // fctiw
8994 case 0x00F: // fctiwz
sewardj6be67232006-01-24 19:00:05 +00008995 case 0x32E: // fctid
8996 case 0x32F: // fctidz
8997 case 0x34E: // fcfid
ceriond953ebb2005-11-29 13:27:20 +00008998 if (dis_fp_round(theInstr)) goto decode_success;
8999 goto decode_failure;
9000
9001 /* Floating Point Move Instructions */
9002 case 0x028: // fneg
9003 case 0x048: // fmr
9004 case 0x088: // fnabs
9005 case 0x108: // fabs
9006 if (dis_fp_move( theInstr )) goto decode_success;
9007 goto decode_failure;
sewardje14bb9f2005-07-22 09:39:02 +00009008
ceriond953ebb2005-11-29 13:27:20 +00009009 /* Floating Point Status/Control Register Instructions */
cerion3ea49ee2006-01-04 10:53:00 +00009010 case 0x026: // mtfsb1
sewardj496b88f2006-10-04 17:46:11 +00009011 case 0x040: // mcrfs
ceriond953ebb2005-11-29 13:27:20 +00009012 case 0x046: // mtfsb0
9013 case 0x086: // mtfsfi
9014 case 0x247: // mffs
9015 case 0x2C7: // mtfsf
9016 if (dis_fp_scr( theInstr )) goto decode_success;
9017 goto decode_failure;
cerionf0de28c2005-12-13 20:21:11 +00009018
ceriond953ebb2005-11-29 13:27:20 +00009019 default:
9020 goto decode_failure;
sewardje14bb9f2005-07-22 09:39:02 +00009021 }
cerion3d870a32005-03-18 12:23:33 +00009022 break;
ceriond953ebb2005-11-29 13:27:20 +00009023
cerion91ad5362005-01-27 23:02:41 +00009024 case 0x13:
cerionb85e8bb2005-02-16 08:54:33 +00009025 switch (opc2) {
cerion91ad5362005-01-27 23:02:41 +00009026
ceriond953ebb2005-11-29 13:27:20 +00009027 /* Condition Register Logical Instructions */
9028 case 0x101: case 0x081: case 0x121: // crand, crandc, creqv
9029 case 0x0E1: case 0x021: case 0x1C1: // crnand, crnor, cror
9030 case 0x1A1: case 0x0C1: case 0x000: // crorc, crxor, mcrf
9031 if (dis_cond_logic( theInstr )) goto decode_success;
9032 goto decode_failure;
cerionb85e8bb2005-02-16 08:54:33 +00009033
ceriond953ebb2005-11-29 13:27:20 +00009034 /* Branch Instructions */
9035 case 0x210: case 0x010: // bcctr, bclr
sewardjc716aea2006-01-17 01:48:46 +00009036 if (dis_branch(theInstr, &dres, resteerOkFn, callback_opaque))
9037 goto decode_success;
ceriond953ebb2005-11-29 13:27:20 +00009038 goto decode_failure;
9039
9040 /* Memory Synchronization Instructions */
9041 case 0x096: // isync
9042 if (dis_memsync( theInstr )) goto decode_success;
9043 goto decode_failure;
9044
9045 default:
9046 goto decode_failure;
cerionb85e8bb2005-02-16 08:54:33 +00009047 }
9048 break;
cerion91ad5362005-01-27 23:02:41 +00009049
9050
cerionb85e8bb2005-02-16 08:54:33 +00009051 case 0x1F:
cerione9d361a2005-03-04 17:35:29 +00009052
9053 /* For arith instns, bit10 is the OE flag (overflow enable) */
9054
cerion76de5cf2005-11-18 18:25:12 +00009055 opc2 = IFIELD(theInstr, 1, 9);
cerionb85e8bb2005-02-16 08:54:33 +00009056 switch (opc2) {
ceriond953ebb2005-11-29 13:27:20 +00009057 /* Integer Arithmetic Instructions */
9058 case 0x10A: case 0x00A: case 0x08A: // add, addc, adde
9059 case 0x0EA: case 0x0CA: case 0x1EB: // addme, addze, divw
9060 case 0x1CB: case 0x04B: case 0x00B: // divwu, mulhw, mulhwu
9061 case 0x0EB: case 0x068: case 0x028: // mullw, neg, subf
9062 case 0x008: case 0x088: case 0x0E8: // subfc, subfe, subfme
9063 case 0x0C8: // subfze
9064 if (dis_int_arith( theInstr )) goto decode_success;
9065 goto decode_failure;
cerionf0de28c2005-12-13 20:21:11 +00009066
9067 /* 64bit Integer Arithmetic */
9068 case 0x009: case 0x049: case 0x0E9: // mulhdu, mulhd, mulld
9069 case 0x1C9: case 0x1E9: // divdu, divd
9070 if (!mode64) goto decode_failure;
9071 if (dis_int_arith( theInstr )) goto decode_success;
9072 goto decode_failure;
9073
ceriond953ebb2005-11-29 13:27:20 +00009074 default:
9075 break; // Fall through...
cerionb85e8bb2005-02-16 08:54:33 +00009076 }
cerion91ad5362005-01-27 23:02:41 +00009077
cerione9d361a2005-03-04 17:35:29 +00009078 /* All remaining opcodes use full 10 bits. */
9079
cerion76de5cf2005-11-18 18:25:12 +00009080 opc2 = IFIELD(theInstr, 1, 10);
cerionb85e8bb2005-02-16 08:54:33 +00009081 switch (opc2) {
cerione9d361a2005-03-04 17:35:29 +00009082 /* Integer Compare Instructions */
9083 case 0x000: case 0x020: // cmp, cmpl
9084 if (dis_int_cmp( theInstr )) goto decode_success;
cerionb85e8bb2005-02-16 08:54:33 +00009085 goto decode_failure;
cerion7aa4bbc2005-01-29 09:32:07 +00009086
cerione9d361a2005-03-04 17:35:29 +00009087 /* Integer Logical Instructions */
9088 case 0x01C: case 0x03C: case 0x01A: // and, andc, cntlzw
9089 case 0x11C: case 0x3BA: case 0x39A: // eqv, extsb, extsh
9090 case 0x1DC: case 0x07C: case 0x1BC: // nand, nor, or
9091 case 0x19C: case 0x13C: // orc, xor
9092 if (dis_int_logic( theInstr )) goto decode_success;
cerionb85e8bb2005-02-16 08:54:33 +00009093 goto decode_failure;
cerion932ad942005-01-30 10:18:50 +00009094
cerionf0de28c2005-12-13 20:21:11 +00009095 /* 64bit Integer Logical Instructions */
cerion07b07a92005-12-22 14:32:35 +00009096 case 0x3DA: case 0x03A: // extsw, cntlzd
cerionf0de28c2005-12-13 20:21:11 +00009097 if (!mode64) goto decode_failure;
9098 if (dis_int_logic( theInstr )) goto decode_success;
9099 goto decode_failure;
9100
cerione9d361a2005-03-04 17:35:29 +00009101 /* Integer Shift Instructions */
9102 case 0x018: case 0x318: case 0x338: // slw, sraw, srawi
9103 case 0x218: // srw
9104 if (dis_int_shift( theInstr )) goto decode_success;
cerionb85e8bb2005-02-16 08:54:33 +00009105 goto decode_failure;
cerion7aa4bbc2005-01-29 09:32:07 +00009106
cerionf0de28c2005-12-13 20:21:11 +00009107 /* 64bit Integer Shift Instructions */
9108 case 0x01B: case 0x31A: // sld, srad
cerion07b07a92005-12-22 14:32:35 +00009109 case 0x33A: case 0x33B: // sradi
cerionf0de28c2005-12-13 20:21:11 +00009110 case 0x21B: // srd
9111 if (!mode64) goto decode_failure;
9112 if (dis_int_shift( theInstr )) goto decode_success;
9113 goto decode_failure;
9114
cerione9d361a2005-03-04 17:35:29 +00009115 /* Integer Load Instructions */
9116 case 0x057: case 0x077: case 0x157: // lbzx, lbzux, lhax
9117 case 0x177: case 0x117: case 0x137: // lhaux, lhzx, lhzux
9118 case 0x017: case 0x037: // lwzx, lwzux
9119 if (dis_int_load( theInstr )) goto decode_success;
cerionb85e8bb2005-02-16 08:54:33 +00009120 goto decode_failure;
cerion7aa4bbc2005-01-29 09:32:07 +00009121
cerionf0de28c2005-12-13 20:21:11 +00009122 /* 64bit Integer Load Instructions */
9123 case 0x035: case 0x015: // ldux, ldx
9124 case 0x175: case 0x155: // lwaux, lwax
9125 if (!mode64) goto decode_failure;
9126 if (dis_int_load( theInstr )) goto decode_success;
9127 goto decode_failure;
9128
sewardjb51f0f42005-07-18 11:38:02 +00009129 /* Integer Store Instructions */
cerione9d361a2005-03-04 17:35:29 +00009130 case 0x0F7: case 0x0D7: case 0x1B7: // stbux, stbx, sthux
9131 case 0x197: case 0x0B7: case 0x097: // sthx, stwux, stwx
9132 if (dis_int_store( theInstr )) goto decode_success;
cerionb85e8bb2005-02-16 08:54:33 +00009133 goto decode_failure;
cerion91ad5362005-01-27 23:02:41 +00009134
cerionf0de28c2005-12-13 20:21:11 +00009135 /* 64bit Integer Store Instructions */
9136 case 0x0B5: case 0x095: // stdux, stdx
9137 if (!mode64) goto decode_failure;
9138 if (dis_int_store( theInstr )) goto decode_success;
9139 goto decode_failure;
9140
sewardj602857d2005-09-06 09:10:09 +00009141 /* Integer Load and Store with Byte Reverse Instructions */
9142 case 0x316: case 0x216: case 0x396: // lhbrx, lwbrx, sthbrx
9143 case 0x296: // stwbrx
9144 if (dis_int_ldst_rev( theInstr )) goto decode_success;
9145 goto decode_failure;
9146
sewardj87e651f2005-09-09 08:31:18 +00009147 /* Integer Load and Store String Instructions */
9148 case 0x255: case 0x215: case 0x2D5: // lswi, lswx, stswi
9149 case 0x295: { // stswx
9150 Bool stopHere = False;
9151 Bool ok = dis_int_ldst_str( theInstr, &stopHere );
9152 if (!ok) goto decode_failure;
9153 if (stopHere) {
cerion2831b002005-11-30 19:55:22 +00009154 irbb->next = mkSzImm(ty, nextInsnAddr());
sewardj87e651f2005-09-09 08:31:18 +00009155 irbb->jumpkind = Ijk_Boring;
ceriond953ebb2005-11-29 13:27:20 +00009156 dres.whatNext = Dis_StopHere;
sewardj87e651f2005-09-09 08:31:18 +00009157 }
9158 goto decode_success;
9159 }
cerion645c9302005-01-31 10:09:59 +00009160
cerione9d361a2005-03-04 17:35:29 +00009161 /* Memory Synchronization Instructions */
9162 case 0x356: case 0x014: case 0x096: // eieio, lwarx, stwcx.
9163 case 0x256: // sync
9164 if (dis_memsync( theInstr )) goto decode_success;
cerionb85e8bb2005-02-16 08:54:33 +00009165 goto decode_failure;
9166
cerionf0de28c2005-12-13 20:21:11 +00009167 /* 64bit Memory Synchronization Instructions */
9168 case 0x054: case 0x0D6: // ldarx, stdcx.
9169 if (!mode64) goto decode_failure;
9170 if (dis_memsync( theInstr )) goto decode_success;
9171 goto decode_failure;
9172
cerione9d361a2005-03-04 17:35:29 +00009173 /* Processor Control Instructions */
9174 case 0x200: case 0x013: case 0x153: // mcrxr, mfcr, mfspr
9175 case 0x173: case 0x090: case 0x1D3: // mftb, mtcrf, mtspr
9176 if (dis_proc_ctl( theInstr )) goto decode_success;
cerionb85e8bb2005-02-16 08:54:33 +00009177 goto decode_failure;
cerion645c9302005-01-31 10:09:59 +00009178
cerione9d361a2005-03-04 17:35:29 +00009179 /* Cache Management Instructions */
9180 case 0x2F6: case 0x056: case 0x036: // dcba, dcbf, dcbst
9181 case 0x116: case 0x0F6: case 0x3F6: // dcbt, dcbtst, dcbz
9182 case 0x3D6: // icbi
sewardj9e6491a2005-07-02 19:24:10 +00009183 if (dis_cache_manage( theInstr, &dres, archinfo ))
sewardjd94b73a2005-06-30 12:08:48 +00009184 goto decode_success;
cerionb85e8bb2005-02-16 08:54:33 +00009185 goto decode_failure;
ceriond23be4e2005-01-31 07:23:07 +00009186
sewardjb51f0f42005-07-18 11:38:02 +00009187//zz /* External Control Instructions */
9188//zz case 0x136: case 0x1B6: // eciwx, ecowx
9189//zz DIP("external control op => not implemented\n");
9190//zz goto decode_failure;
9191//zz
9192//zz /* Trap Instructions */
9193//zz case 0x004: // tw
9194//zz DIP("trap op (tw) => not implemented\n");
9195//zz goto decode_failure;
cerionf0de28c2005-12-13 20:21:11 +00009196//zz case 0x044: // td
9197//zz DIP("trap op (td) => not implemented\n");
9198//zz goto decode_failure;
sewardje14bb9f2005-07-22 09:39:02 +00009199
9200 /* Floating Point Load Instructions */
9201 case 0x217: case 0x237: case 0x257: // lfsx, lfsux, lfdx
9202 case 0x277: // lfdux
sewardj5117ce12006-01-27 21:20:15 +00009203 if (!allow_F) goto decode_noF;
sewardje14bb9f2005-07-22 09:39:02 +00009204 if (dis_fp_load( theInstr )) goto decode_success;
9205 goto decode_failure;
9206
9207 /* Floating Point Store Instructions */
9208 case 0x297: case 0x2B7: case 0x2D7: // stfs, stfsu, stfd
sewardj5117ce12006-01-27 21:20:15 +00009209 case 0x2F7: // stfdu, stfiwx
9210 if (!allow_F) goto decode_noF;
sewardje14bb9f2005-07-22 09:39:02 +00009211 if (dis_fp_store( theInstr )) goto decode_success;
9212 goto decode_failure;
sewardj5117ce12006-01-27 21:20:15 +00009213 case 0x3D7: // stfiwx
9214 if (!allow_F) goto decode_noF;
9215 if (!allow_GX) goto decode_noGX;
9216 if (dis_fp_store( theInstr )) goto decode_success;
9217 goto decode_failure;
sewardje14bb9f2005-07-22 09:39:02 +00009218
cerion32aad402005-09-10 12:02:24 +00009219 /* AltiVec instructions */
9220
9221 /* AV Cache Control - Data streams */
9222 case 0x156: case 0x176: case 0x336: // dst, dstst, dss
sewardj5117ce12006-01-27 21:20:15 +00009223 if (!allow_V) goto decode_noV;
cerion32aad402005-09-10 12:02:24 +00009224 if (dis_av_datastream( theInstr )) goto decode_success;
9225 goto decode_failure;
ceriona982c052005-06-28 17:23:09 +00009226
9227 /* AV Load */
9228 case 0x006: case 0x026: // lvsl, lvsr
9229 case 0x007: case 0x027: case 0x047: // lvebx, lvehx, lvewx
9230 case 0x067: case 0x167: // lvx, lvxl
sewardj5117ce12006-01-27 21:20:15 +00009231 if (!allow_V) goto decode_noV;
ceriona982c052005-06-28 17:23:09 +00009232 if (dis_av_load( theInstr )) goto decode_success;
9233 goto decode_failure;
9234
9235 /* AV Store */
9236 case 0x087: case 0x0A7: case 0x0C7: // stvebx, stvehx, stvewx
9237 case 0x0E7: case 0x1E7: // stvx, stvxl
sewardj5117ce12006-01-27 21:20:15 +00009238 if (!allow_V) goto decode_noV;
ceriona982c052005-06-28 17:23:09 +00009239 if (dis_av_store( theInstr )) goto decode_success;
9240 goto decode_failure;
9241
9242 default:
9243 goto decode_failure;
9244 }
9245 break;
9246
9247
cerion32aad402005-09-10 12:02:24 +00009248 case 0x04:
9249 /* AltiVec instructions */
9250
cerion76de5cf2005-11-18 18:25:12 +00009251 opc2 = IFIELD(theInstr, 0, 6);
cerion32aad402005-09-10 12:02:24 +00009252 switch (opc2) {
9253 /* AV Mult-Add, Mult-Sum */
9254 case 0x20: case 0x21: case 0x22: // vmhaddshs, vmhraddshs, vmladduhm
9255 case 0x24: case 0x25: case 0x26: // vmsumubm, vmsummbm, vmsumuhm
9256 case 0x27: case 0x28: case 0x29: // vmsumuhs, vmsumshm, vmsumshs
sewardj5117ce12006-01-27 21:20:15 +00009257 if (!allow_V) goto decode_noV;
cerion32aad402005-09-10 12:02:24 +00009258 if (dis_av_multarith( theInstr )) goto decode_success;
9259 goto decode_failure;
9260
9261 /* AV Permutations */
9262 case 0x2A: // vsel
9263 case 0x2B: // vperm
cerion32aad402005-09-10 12:02:24 +00009264 case 0x2C: // vsldoi
sewardj5117ce12006-01-27 21:20:15 +00009265 if (!allow_V) goto decode_noV;
cerion92d9d872005-09-15 21:58:50 +00009266 if (dis_av_permute( theInstr )) goto decode_success;
cerion32aad402005-09-10 12:02:24 +00009267 goto decode_failure;
9268
9269 /* AV Floating Point Mult-Add/Sub */
9270 case 0x2E: case 0x2F: // vmaddfp, vnmsubfp
sewardj5117ce12006-01-27 21:20:15 +00009271 if (!allow_V) goto decode_noV;
cerion32aad402005-09-10 12:02:24 +00009272 if (dis_av_fp_arith( theInstr )) goto decode_success;
9273 goto decode_failure;
9274
9275 default:
9276 break; // Fall through...
9277 }
9278
cerion76de5cf2005-11-18 18:25:12 +00009279 opc2 = IFIELD(theInstr, 0, 11);
cerion32aad402005-09-10 12:02:24 +00009280 switch (opc2) {
9281 /* AV Arithmetic */
9282 case 0x180: // vaddcuw
9283 case 0x000: case 0x040: case 0x080: // vaddubm, vadduhm, vadduwm
9284 case 0x200: case 0x240: case 0x280: // vaddubs, vadduhs, vadduws
9285 case 0x300: case 0x340: case 0x380: // vaddsbs, vaddshs, vaddsws
9286 case 0x580: // vsubcuw
9287 case 0x400: case 0x440: case 0x480: // vsububm, vsubuhm, vsubuwm
9288 case 0x600: case 0x640: case 0x680: // vsububs, vsubuhs, vsubuws
9289 case 0x700: case 0x740: case 0x780: // vsubsbs, vsubshs, vsubsws
9290 case 0x402: case 0x442: case 0x482: // vavgub, vavguh, vavguw
9291 case 0x502: case 0x542: case 0x582: // vavgsb, vavgsh, vavgsw
9292 case 0x002: case 0x042: case 0x082: // vmaxub, vmaxuh, vmaxuw
9293 case 0x102: case 0x142: case 0x182: // vmaxsb, vmaxsh, vmaxsw
9294 case 0x202: case 0x242: case 0x282: // vminub, vminuh, vminuw
9295 case 0x302: case 0x342: case 0x382: // vminsb, vminsh, vminsw
9296 case 0x008: case 0x048: // vmuloub, vmulouh
9297 case 0x108: case 0x148: // vmulosb, vmulosh
9298 case 0x208: case 0x248: // vmuleub, vmuleuh
9299 case 0x308: case 0x348: // vmulesb, vmulesh
9300 case 0x608: case 0x708: case 0x648: // vsum4ubs, vsum4sbs, vsum4shs
9301 case 0x688: case 0x788: // vsum2sws, vsumsws
sewardj5117ce12006-01-27 21:20:15 +00009302 if (!allow_V) goto decode_noV;
cerion32aad402005-09-10 12:02:24 +00009303 if (dis_av_arith( theInstr )) goto decode_success;
9304 goto decode_failure;
9305
9306 /* AV Rotate, Shift */
9307 case 0x004: case 0x044: case 0x084: // vrlb, vrlh, vrlw
9308 case 0x104: case 0x144: case 0x184: // vslb, vslh, vslw
9309 case 0x204: case 0x244: case 0x284: // vsrb, vsrh, vsrw
9310 case 0x304: case 0x344: case 0x384: // vsrab, vsrah, vsraw
9311 case 0x1C4: case 0x2C4: // vsl, vsr
9312 case 0x40C: case 0x44C: // vslo, vsro
sewardj5117ce12006-01-27 21:20:15 +00009313 if (!allow_V) goto decode_noV;
cerion32aad402005-09-10 12:02:24 +00009314 if (dis_av_shift( theInstr )) goto decode_success;
9315 goto decode_failure;
9316
9317 /* AV Logic */
9318 case 0x404: case 0x444: case 0x484: // vand, vandc, vor
9319 case 0x4C4: case 0x504: // vxor, vnor
sewardj5117ce12006-01-27 21:20:15 +00009320 if (!allow_V) goto decode_noV;
cerion32aad402005-09-10 12:02:24 +00009321 if (dis_av_logic( theInstr )) goto decode_success;
9322 goto decode_failure;
9323
9324 /* AV Processor Control */
9325 case 0x604: case 0x644: // mfvscr, mtvscr
sewardj5117ce12006-01-27 21:20:15 +00009326 if (!allow_V) goto decode_noV;
cerion32aad402005-09-10 12:02:24 +00009327 if (dis_av_procctl( theInstr )) goto decode_success;
9328 goto decode_failure;
9329
9330 /* AV Floating Point Arithmetic */
9331 case 0x00A: case 0x04A: // vaddfp, vsubfp
9332 case 0x10A: case 0x14A: case 0x18A: // vrefp, vrsqrtefp, vexptefp
9333 case 0x1CA: // vlogefp
9334 case 0x40A: case 0x44A: // vmaxfp, vminfp
sewardj5117ce12006-01-27 21:20:15 +00009335 if (!allow_V) goto decode_noV;
cerion32aad402005-09-10 12:02:24 +00009336 if (dis_av_fp_arith( theInstr )) goto decode_success;
9337 goto decode_failure;
9338
9339 /* AV Floating Point Round/Convert */
9340 case 0x20A: case 0x24A: case 0x28A: // vrfin, vrfiz, vrfip
9341 case 0x2CA: // vrfim
9342 case 0x30A: case 0x34A: case 0x38A: // vcfux, vcfsx, vctuxs
9343 case 0x3CA: // vctsxs
sewardj5117ce12006-01-27 21:20:15 +00009344 if (!allow_V) goto decode_noV;
cerion32aad402005-09-10 12:02:24 +00009345 if (dis_av_fp_convert( theInstr )) goto decode_success;
9346 goto decode_failure;
9347
9348 /* AV Merge, Splat */
9349 case 0x00C: case 0x04C: case 0x08C: // vmrghb, vmrghh, vmrghw
9350 case 0x10C: case 0x14C: case 0x18C: // vmrglb, vmrglh, vmrglw
9351 case 0x20C: case 0x24C: case 0x28C: // vspltb, vsplth, vspltw
9352 case 0x30C: case 0x34C: case 0x38C: // vspltisb, vspltish, vspltisw
sewardj5117ce12006-01-27 21:20:15 +00009353 if (!allow_V) goto decode_noV;
cerion32aad402005-09-10 12:02:24 +00009354 if (dis_av_permute( theInstr )) goto decode_success;
9355 goto decode_failure;
9356
9357 /* AV Pack, Unpack */
9358 case 0x00E: case 0x04E: case 0x08E: // vpkuhum, vpkuwum, vpkuhus
9359 case 0x0CE: // vpkuwus
9360 case 0x10E: case 0x14E: case 0x18E: // vpkshus, vpkswus, vpkshss
9361 case 0x1CE: // vpkswss
9362 case 0x20E: case 0x24E: case 0x28E: // vupkhsb, vupkhsh, vupklsb
9363 case 0x2CE: // vupklsh
9364 case 0x30E: case 0x34E: case 0x3CE: // vpkpx, vupkhpx, vupklpx
sewardj5117ce12006-01-27 21:20:15 +00009365 if (!allow_V) goto decode_noV;
cerion32aad402005-09-10 12:02:24 +00009366 if (dis_av_pack( theInstr )) goto decode_success;
9367 goto decode_failure;
9368
9369 default:
9370 break; // Fall through...
9371 }
9372
cerion76de5cf2005-11-18 18:25:12 +00009373 opc2 = IFIELD(theInstr, 0, 10);
cerion32aad402005-09-10 12:02:24 +00009374 switch (opc2) {
9375
9376 /* AV Compare */
9377 case 0x006: case 0x046: case 0x086: // vcmpequb, vcmpequh, vcmpequw
9378 case 0x206: case 0x246: case 0x286: // vcmpgtub, vcmpgtuh, vcmpgtuw
9379 case 0x306: case 0x346: case 0x386: // vcmpgtsb, vcmpgtsh, vcmpgtsw
sewardj5117ce12006-01-27 21:20:15 +00009380 if (!allow_V) goto decode_noV;
cerion32aad402005-09-10 12:02:24 +00009381 if (dis_av_cmp( theInstr )) goto decode_success;
9382 goto decode_failure;
9383
9384 /* AV Floating Point Compare */
9385 case 0x0C6: case 0x1C6: case 0x2C6: // vcmpeqfp, vcmpgefp, vcmpgtfp
9386 case 0x3C6: // vcmpbfp
sewardj5117ce12006-01-27 21:20:15 +00009387 if (!allow_V) goto decode_noV;
cerion32aad402005-09-10 12:02:24 +00009388 if (dis_av_fp_cmp( theInstr )) goto decode_success;
9389 goto decode_failure;
9390
9391 default:
9392 goto decode_failure;
9393 }
9394 break;
cerion7aa4bbc2005-01-29 09:32:07 +00009395
cerion896a1372005-01-25 12:24:25 +00009396 default:
cerion5b2325f2005-12-23 00:55:09 +00009397 goto decode_failure;
9398
sewardj5117ce12006-01-27 21:20:15 +00009399 decode_noF:
9400 vassert(!allow_F);
9401 vex_printf("disInstr(ppc): declined to decode an FP insn.\n");
9402 goto decode_failure;
9403 decode_noV:
9404 vassert(!allow_V);
9405 vex_printf("disInstr(ppc): declined to decode an AltiVec insn.\n");
9406 goto decode_failure;
9407 decode_noFX:
sewardj7c545862006-01-27 21:52:19 +00009408 vassert(!allow_FX);
sewardj5117ce12006-01-27 21:20:15 +00009409 vex_printf("disInstr(ppc): "
sewardjb183b852006-02-03 16:08:03 +00009410 "declined to decode a GeneralPurpose-Optional insn.\n");
sewardj5117ce12006-01-27 21:20:15 +00009411 goto decode_failure;
9412 decode_noGX:
sewardj7c545862006-01-27 21:52:19 +00009413 vassert(!allow_GX);
sewardj5117ce12006-01-27 21:20:15 +00009414 vex_printf("disInstr(ppc): "
9415 "declined to decode a Graphics-Optional insn.\n");
cerion5b2325f2005-12-23 00:55:09 +00009416 goto decode_failure;
9417
cerion896a1372005-01-25 12:24:25 +00009418 decode_failure:
9419 /* All decode failures end up here. */
cerion225a0342005-09-12 20:49:09 +00009420 opc2 = (theInstr) & 0x7FF;
cerion5b2325f2005-12-23 00:55:09 +00009421 vex_printf("disInstr(ppc): unhandled instruction: "
cerion896a1372005-01-25 12:24:25 +00009422 "0x%x\n", theInstr);
sewardjc7cd2142005-09-09 22:31:49 +00009423 vex_printf(" primary %d(0x%x), secondary %u(0x%x)\n",
sewardjb51f0f42005-07-18 11:38:02 +00009424 opc1, opc1, opc2, opc2);
cerion995bc362005-02-03 11:03:31 +00009425
sewardj01a9e802005-02-01 20:46:00 +00009426 /* Tell the dispatcher that this insn cannot be decoded, and so has
9427 not been executed, and (is currently) the next to be executed.
9428 CIA should be up-to-date since it made so at the start of each
9429 insn, but nevertheless be paranoid and update it again right
9430 now. */
cerion2831b002005-11-30 19:55:22 +00009431 putGST( PPC_GST_CIA, mkSzImm(ty, guest_CIA_curr_instr) );
9432 irbb->next = mkSzImm(ty, guest_CIA_curr_instr);
sewardj01a9e802005-02-01 20:46:00 +00009433 irbb->jumpkind = Ijk_NoDecode;
ceriond953ebb2005-11-29 13:27:20 +00009434 dres.whatNext = Dis_StopHere;
9435 dres.len = 0;
sewardj9e6491a2005-07-02 19:24:10 +00009436 return dres;
cerion896a1372005-01-25 12:24:25 +00009437
9438 } /* switch (opc) for the main (primary) opcode switch. */
9439
9440 decode_success:
9441 /* All decode successes end up here. */
cerion896a1372005-01-25 12:24:25 +00009442 DIP("\n");
9443
sewardjce02aa72006-01-12 12:27:58 +00009444 if (dres.len == 0) {
9445 dres.len = 4;
9446 } else {
9447 vassert(dres.len == 20);
9448 }
sewardj9e6491a2005-07-02 19:24:10 +00009449 return dres;
cerion896a1372005-01-25 12:24:25 +00009450}
9451
9452#undef DIP
9453#undef DIS
9454
sewardj9e6491a2005-07-02 19:24:10 +00009455
9456/*------------------------------------------------------------*/
9457/*--- Top-level fn ---*/
9458/*------------------------------------------------------------*/
9459
9460/* Disassemble a single instruction into IR. The instruction
9461 is located in host memory at &guest_code[delta]. */
9462
cerion5b2325f2005-12-23 00:55:09 +00009463DisResult disInstr_PPC ( IRBB* irbb_IN,
9464 Bool put_IP,
sewardjc716aea2006-01-17 01:48:46 +00009465 Bool (*resteerOkFn) ( void*, Addr64 ),
9466 void* callback_opaque,
cerion5b2325f2005-12-23 00:55:09 +00009467 UChar* guest_code_IN,
9468 Long delta,
9469 Addr64 guest_IP,
sewardja5f55da2006-04-30 23:37:32 +00009470 VexArch guest_arch,
cerion5b2325f2005-12-23 00:55:09 +00009471 VexArchInfo* archinfo,
9472 Bool host_bigendian_IN )
sewardj9e6491a2005-07-02 19:24:10 +00009473{
sewardj5df65bb2005-11-29 14:47:04 +00009474 IRType ty;
9475 DisResult dres;
sewardj5117ce12006-01-27 21:20:15 +00009476 UInt mask32, mask64;
9477 UInt hwcaps_guest = archinfo->hwcaps;
9478
sewardja5f55da2006-04-30 23:37:32 +00009479 vassert(guest_arch == VexArchPPC32 || guest_arch == VexArchPPC64);
sewardj5df65bb2005-11-29 14:47:04 +00009480
sewardja5f55da2006-04-30 23:37:32 +00009481 /* global -- ick */
9482 mode64 = guest_arch == VexArchPPC64;
9483 ty = mode64 ? Ity_I64 : Ity_I32;
9484
9485 /* do some sanity checks */
sewardj5117ce12006-01-27 21:20:15 +00009486 mask32 = VEX_HWCAPS_PPC32_F | VEX_HWCAPS_PPC32_V
9487 | VEX_HWCAPS_PPC32_FX | VEX_HWCAPS_PPC32_GX;
9488
sewardj5117ce12006-01-27 21:20:15 +00009489 mask64 = VEX_HWCAPS_PPC64_V
9490 | VEX_HWCAPS_PPC64_FX | VEX_HWCAPS_PPC64_GX;
9491
sewardja5f55da2006-04-30 23:37:32 +00009492 if (mode64) {
9493 vassert((hwcaps_guest & mask32) == 0);
9494 } else {
9495 vassert((hwcaps_guest & mask64) == 0);
9496 }
sewardj9e6491a2005-07-02 19:24:10 +00009497
9498 /* Set globals (see top of this file) */
9499 guest_code = guest_code_IN;
9500 irbb = irbb_IN;
9501 host_is_bigendian = host_bigendian_IN;
ceriond953ebb2005-11-29 13:27:20 +00009502
cerion2831b002005-11-30 19:55:22 +00009503 guest_CIA_curr_instr = mkSzAddr(ty, guest_IP);
9504 guest_CIA_bbstart = mkSzAddr(ty, guest_IP - delta);
sewardj9e6491a2005-07-02 19:24:10 +00009505
sewardjc716aea2006-01-17 01:48:46 +00009506 dres = disInstr_PPC_WRK ( put_IP, resteerOkFn, callback_opaque,
cerion5b2325f2005-12-23 00:55:09 +00009507 delta, archinfo );
sewardj9e6491a2005-07-02 19:24:10 +00009508
9509 return dres;
9510}
9511
9512
sewardjc808ef72005-08-18 11:50:43 +00009513/*------------------------------------------------------------*/
9514/*--- Unused stuff ---*/
9515/*------------------------------------------------------------*/
9516
9517///* A potentially more memcheck-friendly implementation of Clz32, with
9518// the boundary case Clz32(0) = 32, which is what ppc requires. */
9519//
9520//static IRExpr* /* :: Ity_I32 */ verbose_Clz32 ( IRTemp arg )
9521//{
9522// /* Welcome ... to SSA R Us. */
9523// IRTemp n1 = newTemp(Ity_I32);
9524// IRTemp n2 = newTemp(Ity_I32);
9525// IRTemp n3 = newTemp(Ity_I32);
9526// IRTemp n4 = newTemp(Ity_I32);
9527// IRTemp n5 = newTemp(Ity_I32);
9528// IRTemp n6 = newTemp(Ity_I32);
9529// IRTemp n7 = newTemp(Ity_I32);
9530// IRTemp n8 = newTemp(Ity_I32);
9531// IRTemp n9 = newTemp(Ity_I32);
9532// IRTemp n10 = newTemp(Ity_I32);
9533// IRTemp n11 = newTemp(Ity_I32);
9534// IRTemp n12 = newTemp(Ity_I32);
9535//
9536// /* First, propagate the most significant 1-bit into all lower
9537// positions in the word. */
9538// /* unsigned int clz ( unsigned int n )
9539// {
9540// n |= (n >> 1);
9541// n |= (n >> 2);
9542// n |= (n >> 4);
9543// n |= (n >> 8);
9544// n |= (n >> 16);
9545// return bitcount(~n);
9546// }
9547// */
9548// assign(n1, mkexpr(arg));
9549// assign(n2, binop(Iop_Or32, mkexpr(n1), binop(Iop_Shr32, mkexpr(n1), mkU8(1))));
9550// assign(n3, binop(Iop_Or32, mkexpr(n2), binop(Iop_Shr32, mkexpr(n2), mkU8(2))));
9551// assign(n4, binop(Iop_Or32, mkexpr(n3), binop(Iop_Shr32, mkexpr(n3), mkU8(4))));
9552// assign(n5, binop(Iop_Or32, mkexpr(n4), binop(Iop_Shr32, mkexpr(n4), mkU8(8))));
9553// assign(n6, binop(Iop_Or32, mkexpr(n5), binop(Iop_Shr32, mkexpr(n5), mkU8(16))));
9554// /* This gives a word of the form 0---01---1. Now invert it, giving
9555// a word of the form 1---10---0, then do a population-count idiom
9556// (to count the 1s, which is the number of leading zeroes, or 32
9557// if the original word was 0. */
9558// assign(n7, unop(Iop_Not32, mkexpr(n6)));
9559//
9560// /* unsigned int bitcount ( unsigned int n )
9561// {
9562// n = n - ((n >> 1) & 0x55555555);
9563// n = (n & 0x33333333) + ((n >> 2) & 0x33333333);
9564// n = (n + (n >> 4)) & 0x0F0F0F0F;
9565// n = n + (n >> 8);
9566// n = (n + (n >> 16)) & 0x3F;
9567// return n;
9568// }
9569// */
9570// assign(n8,
9571// binop(Iop_Sub32,
9572// mkexpr(n7),
9573// binop(Iop_And32,
9574// binop(Iop_Shr32, mkexpr(n7), mkU8(1)),
9575// mkU32(0x55555555))));
9576// assign(n9,
9577// binop(Iop_Add32,
9578// binop(Iop_And32, mkexpr(n8), mkU32(0x33333333)),
9579// binop(Iop_And32,
9580// binop(Iop_Shr32, mkexpr(n8), mkU8(2)),
9581// mkU32(0x33333333))));
9582// assign(n10,
9583// binop(Iop_And32,
9584// binop(Iop_Add32,
9585// mkexpr(n9),
9586// binop(Iop_Shr32, mkexpr(n9), mkU8(4))),
9587// mkU32(0x0F0F0F0F)));
9588// assign(n11,
9589// binop(Iop_Add32,
9590// mkexpr(n10),
9591// binop(Iop_Shr32, mkexpr(n10), mkU8(8))));
9592// assign(n12,
9593// binop(Iop_Add32,
9594// mkexpr(n11),
9595// binop(Iop_Shr32, mkexpr(n11), mkU8(16))));
9596// return
9597// binop(Iop_And32, mkexpr(n12), mkU32(0x3F));
9598//}
9599
cerion896a1372005-01-25 12:24:25 +00009600/*--------------------------------------------------------------------*/
ceriond0eae2d2005-12-23 11:43:01 +00009601/*--- end guest-ppc/toIR.c ---*/
cerion896a1372005-01-25 12:24:25 +00009602/*--------------------------------------------------------------------*/